commit 7f1c0e5f71ef096a5c8ca19389600dea2b889fb3 Author: LumaOps release export Date: Thu Sep 3 01:18:23 2026 +0200 Publish LumaOps source diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..957cae9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +.git +.gitignore +.venv* +**/__pycache__ +**/*.pyc +build +release +debug +lumaops/frontend/node_modules +lumaops/frontend/dist +lumaops/frontend/coverage +lumaops/backend/.mypy_cache +lumaops/backend/.pytest_cache +docs/screenshots + diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a90b0a0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 + +# We recommend you to keep these unchanged +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false +indent_size = 4 \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c8f8f7c --- /dev/null +++ b/.env.example @@ -0,0 +1,47 @@ +# LumaOps web application +APP_HOST=0.0.0.0 +# Externe webpoort in bridge-modus; LumaOps is standaard bereikbaar op :1223. +WEB_PORT=1223 +# Interne containerpoort. Laat deze op 8080 staan bij bridge networking. +APP_PORT=8080 +LOG_LEVEL=INFO +TZ=Europe/Brussels +LUMAOPS_ENV=production + +# Persistent paths inside the container +OPENRGB_CONFIG_DIR=/config/openrgb +DATABASE_URL=sqlite:////data/lumaops.db +CONFIG_DIR=/config/lumaops +DATA_DIR=/data +LOGS_DIR=/logs + +# OpenRGB SDK — never publish port 6742 +OPENRGB_HOST=127.0.0.1 +OPENRGB_PORT=6742 + +# Optional connectors and discovery +ENABLE_NETWORK_DISCOVERY=true +ENABLE_HOME_ASSISTANT=false +ENABLE_WLED=false + +# Runtime identity (Unraid defaults: nobody/users) +PUID=99 +PGID=100 + +# Authentication is mandatory for the default LAN bind. +AUTH_ENABLED=true +EXTERNAL_ACCESS=false +# Generate a unique value, for example: openssl rand -base64 32 +LUMAOPS_ADMIN_TOKEN=replace-with-a-long-random-token +# Keep false for plain HTTP on a trusted LAN; set true behind HTTPS. +SECURE_COOKIES=false +TRUSTED_PROXIES= +CORS_ORIGINS= + +# Prefer a Docker secret or keep the generated /config/lumaops/secret.key. +# LUMAOPS_SECRET_KEY= + +# Command safety +COMMAND_RATE_PER_SECOND=10 +REALTIME_RATE_PER_SECOND=30 +COMMAND_TIMEOUT_SECONDS=10 diff --git a/.gitea/workflows/managed-validation.yml b/.gitea/workflows/managed-validation.yml new file mode 100644 index 0000000..40fd8ba --- /dev/null +++ b/.gitea/workflows/managed-validation.yml @@ -0,0 +1,115 @@ +name: Managed validation + +on: + pull_request: + workflow_dispatch: + inputs: + profile: + description: Allowlisted validation profile + required: true + default: full + type: choice + options: [test, lint, typecheck, build, security, full] + +permissions: + contents: read + +concurrency: + group: managed-validation-${{ gitea.repository }}-${{ gitea.ref }} + cancel-in-progress: true + +jobs: + full: + name: full + # Public fork code must never execute automatically on the private runner. + if: ${{ gitea.event_name != 'pull_request' || gitea.event.pull_request.head.repo.full_name == gitea.repository }} + runs-on: ubuntu-latest + timeout-minutes: 40 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + - name: Select validation profile + shell: bash + env: + REQUESTED_PROFILE: ${{ inputs.profile }} + run: | + set -euo pipefail + profile="${REQUESTED_PROFILE:-full}" + case "$profile" in + test|lint|typecheck|build|security|full) ;; + *) echo "Profile is not allowlisted" >&2; exit 2 ;; + esac + echo "PROFILE=$profile" >> "$GITHUB_ENV" + - name: Repository boundaries + shell: bash + run: | + set -euo pipefail + git diff --check + if git grep -nE '^(<<<<<<< |=======$|>>>>>>> )' -- . ':!*.lock' ':!*.patch'; then + echo "Unresolved merge markers detected" >&2 + exit 1 + fi + # Checkout recreates only `origin`; restore the documented upstream + # identity required by the fork-boundary guard. The pinned baseline + # tag is already part of this repository, so no upstream fetch occurs. + git remote add upstream https://gitlab.com/CalcProgrammer1/OpenRGB.git + bash scripts/openrgb-upstream-guard.sh + - name: Backend environment + if: "env.PROFILE != 'security'" + shell: bash + run: | + set -euo pipefail + python3 -m venv "$RUNNER_TEMP/lumaops-python" + "$RUNNER_TEMP/lumaops-python/bin/python" -m pip install --disable-pip-version-check -e 'lumaops/backend[dev]' + - name: Backend tests + if: "env.PROFILE == 'test' || env.PROFILE == 'full'" + run: | + "$RUNNER_TEMP/lumaops-python/bin/python" -m pytest lumaops/backend/tests + - name: Backend lint + if: "env.PROFILE == 'lint' || env.PROFILE == 'full'" + run: | + "$RUNNER_TEMP/lumaops-python/bin/python" -m ruff check lumaops/backend + - name: Backend typecheck + if: "env.PROFILE == 'typecheck' || env.PROFILE == 'full'" + working-directory: lumaops/backend + run: | + "$RUNNER_TEMP/lumaops-python/bin/python" -m mypy src + - name: Frontend dependencies + if: "env.PROFILE != 'security'" + working-directory: lumaops/frontend + run: npm ci --ignore-scripts + - name: Frontend tests + if: "env.PROFILE == 'test' || env.PROFILE == 'full'" + working-directory: lumaops/frontend + run: npm test + - name: Frontend lint + if: "env.PROFILE == 'lint' || env.PROFILE == 'full'" + working-directory: lumaops/frontend + run: npm run lint + - name: Frontend build + if: "env.PROFILE == 'build' || env.PROFILE == 'typecheck' || env.PROFILE == 'full'" + working-directory: lumaops/frontend + run: npm run build + - name: Dependency audit + if: "env.PROFILE == 'security' || env.PROFILE == 'full'" + working-directory: lumaops/frontend + run: npm audit --omit=dev --audit-level=moderate + - name: Secret scan + if: "env.PROFILE == 'security' || env.PROFILE == 'full'" + shell: bash + run: | + set -euo pipefail + [[ "$(uname -m)" == "x86_64" ]] || { echo "Unsupported Gitleaks runner architecture" >&2; exit 1; } + gitleaks_version="8.30.0" + gitleaks_archive="gitleaks_${gitleaks_version}_linux_x64.tar.gz" + gitleaks_sha256="79a3ab579b53f71efd634f3aaf7e04a0fa0cf206b7ed434638d1547a2470a66e" + gitleaks_dir="${RUNNER_TEMP}/gitleaks-${gitleaks_version}" + mkdir -p "${gitleaks_dir}" + curl --proto '=https' --tlsv1.2 --fail --location --silent --show-error \ + --retry 3 --output "${RUNNER_TEMP}/${gitleaks_archive}" \ + "https://github.com/gitleaks/gitleaks/releases/download/v${gitleaks_version}/${gitleaks_archive}" + echo "${gitleaks_sha256} ${RUNNER_TEMP}/${gitleaks_archive}" | sha256sum --check --strict + tar -xzf "${RUNNER_TEMP}/${gitleaks_archive}" -C "${gitleaks_dir}" gitleaks + "${gitleaks_dir}/gitleaks" version + "${gitleaks_dir}/gitleaks" git . --config .gitleaks.toml --redact --no-banner diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..fe933ee --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: CalcProgrammer1 +patreon: CalcProgrammer1 +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/workflows/issue_opened.yml b/.github/workflows/issue_opened.yml new file mode 100644 index 0000000..77c3553 --- /dev/null +++ b/.github/workflows/issue_opened.yml @@ -0,0 +1,21 @@ +name: Issue Opened + +on: + issues: + types: [opened,reopened] + +jobs: + issue_greeting: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Greeting + run: gh issue comment "$NUMBER" --body "$BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.issue.number }} + BODY: > + This Github repo is a mirror of the main repository on Gitlab. + For any new issue please refer to https://gitlab.com/CalcProgrammer1/OpenRGB/-/issues diff --git a/.github/workflows/openrgb_upstream_compat_guard.yml b/.github/workflows/openrgb_upstream_compat_guard.yml new file mode 100644 index 0000000..63b3897 --- /dev/null +++ b/.github/workflows/openrgb_upstream_compat_guard.yml @@ -0,0 +1,24 @@ +name: OpenRGB Upstream Compatibility Guard + +on: + pull_request: + branches: [ "main", "master" ] + push: + branches: [ "main", "master" ] + +jobs: + guard: + name: Guard OpenRGB-core diff boundary + runs-on: ubuntu-latest + steps: + - name: Checkout full history + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Verify fork boundary + env: + OPENRGB_BASELINE_TAG: upstream-openrgb-1.0rc3 + OPENRGB_UPSTREAM_REMOTE: upstream + OPENRGB_FETCH_UPSTREAM: 1 + run: bash scripts/openrgb-upstream-guard.sh diff --git a/.github/workflows/pr_opened.yml b/.github/workflows/pr_opened.yml new file mode 100644 index 0000000..3a97cef --- /dev/null +++ b/.github/workflows/pr_opened.yml @@ -0,0 +1,21 @@ +name: PR Opened + +on: + pull_request: + types: [opened,reopened] + +jobs: + pr_greeting: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Greeting + run: gh pr comment "$NUMBER" --body "$BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} + BODY: > + This Github repo is a mirror of the main repository on Gitlab. + Please fork https://gitlab.com/CalcProgrammer1/OpenRGB/ and raise the appropriate merge request on Gitlab. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd627ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,144 @@ +# This file is used to ignore files that should not be pushed to the repo. +# ---------------------------------------------------------------------------- + +# OpenRGB Specific +OpenRGB +openrgb +!lumaops/backend/src/lumaops_backend/connectors/openrgb/ +!lumaops/backend/src/lumaops_backend/connectors/openrgb/** +OpenRGB-x86_64.AppImage +60-openrgb.rules +debian/changelog +fedora/OpenRGB.spec +OpenRGB_resource.rc +OpenRGB Windows 32-bit/ +OpenRGB Windows 64-bit/ + +# Directories +.build/ +.cache/ +.moc/ +.obj/ +.pch/ +.rcc/ +.uic/ +.clangd/ +*_debug/ +*_release/ +debug/ +release/ +build/ +Build/ +.qmake.cache +.qmake.stash +.vscode +.vs +.idea + +# Generic Files +.DS_Store +Thumbs.db + +# Binaries +*.exe +!dependencies/**/*.exe +*.bat +!dependencies/**/*.bat + +# C++ objects and libs +*.a +*.dll +*.dylib +*.ii +*.la +*.lai +*.lo +*.o +*.s +*.slo +*.so +*.so.* +!dependencies/**/*.dll + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* +*.qm +*.prl + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database +compile_commands.json + +# QtCreator local machine specific files for imported projects +*creator.user* +*.qrc + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.ncb +*.opensdf +*.pdb +*.sdf +*.sln +*.suo +*.vcproj +*.vcxproj +*vcproj.*.*.user +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Clang tooling files +compile_commands.json + +# Generated i18n files +*.qm + +# LumaOps local development and runtime state +.venv-lumaops/ +lumaops/frontend/node_modules/ +lumaops/frontend/dist/ +__pycache__/ +*.py[cod] +*.egg-info/ +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +.coverage +htmlcov/ +.env +data/ +logs/ +config/lumaops/ +config/openrgb/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..e9f4b4e --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,603 @@ +#-----------------------------------------------------------# +# .gitlab-ci.yml # +# # +# OpenRGB GitLab CI Configuration # +# # +# This file is part of the OpenRGB project # +# SPDX-License-Identifier: GPL-2.0-or-later # +#-----------------------------------------------------------# + +#-----------------------------------------------------------# +# GitLab CI Rules # +# # +# * For downstream forks (not on CalcProgrammer1/OpenRGB), # +# run only if manually started to save CI minutes if # +# GitLab hosted runners # +# * For commits to the CalcProgrammer1/OpenRGB repository, # +# automatically run all jobs on the default branch, # +# otherwise run them if the source is part of a merge # +# request # +#-----------------------------------------------------------# +.downstream_rules: + rules: + - if: $CI_PROJECT_PATH != "CalcProgrammer1/OpenRGB" && $CI_PIPELINE_SOURCE == "push" + when: manual + allow_failure: true + +.upstream_rules: + rules: + - if: '$CI_PROJECT_PATH == "CalcProgrammer1/OpenRGB" && ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_PIPELINE_SOURCE == "merge_request_event")' + when: on_success + - !reference [.downstream_rules, rules] + +.shared_windows_runners: + tags: + - shared-windows + - windows + - windows-1809 + +stages: + - build + - test + - deploy + +variables: + GIT_DEPTH: 0 + +before_script: + - echo "started by ${GITLAB_USER_NAME}" + +#-----------------------------------------------------------# +# Supported Devices Build Target # +#-----------------------------------------------------------# +"Supported Devices": + image: registry.gitlab.com/openrgbdevelopers/openrgb-linux-ci-deb-builder:bookworm-amd64 + tags: + - linux + - amd64 + stage: build + script: + - qmake + - make -j$(nproc) + - ./scripts/build-supported-devices-md.sh $CI_PROJECT_DIR $CI_COMMIT_SHORT_SHA + + artifacts: + name: "${CI_PROJECT_NAME}_Supported_Devices_${CI_COMMIT_SHORT_SHA}" + paths: + - Supported Devices.csv + expire_in: 30 days + + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# OpenRGB Common Appimage Build Steps # +#-----------------------------------------------------------# +.hidden_appimage_script: &appimage_script_steps + image: registry.gitlab.com/openrgbdevelopers/openrgb-linux-ci-deb-builder:bookworm-${TGT_ARCH} + tags: + - linux + - ${TGT_ARCH} + stage: build + script: + - export $(dpkg-architecture) + - ./scripts/build-appimage.sh ${TGT_QT} + + artifacts: + name: "${CI_PROJECT_NAME}_Linux_${TGT_NAME}_${CI_COMMIT_SHORT_SHA}" + paths: + - OpenRGB-${TGT_PATH}.AppImage + - 60-openrgb.rules + - README.md + expire_in: 30 days + + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# Linux (AppImage) i386 Build Target # +#-----------------------------------------------------------# +"Linux i386 AppImage": + variables: + TGT_ARCH: "i386" + TGT_QT: "" + TGT_NAME: "${TGT_ARCH}" + TGT_PATH: "${TGT_ARCH}" + <<: *appimage_script_steps + +#-----------------------------------------------------------# +# Linux (AppImage) Qt6 i386 Build Target # +#-----------------------------------------------------------# +"Linux i386 AppImage Qt6": + variables: + TGT_ARCH: "i386" + TGT_QT: "qt6" + TGT_NAME: "${TGT_ARCH}_${TGT_QT^}" + TGT_PATH: "${TGT_ARCH}" + <<: *appimage_script_steps + +#-----------------------------------------------------------# +# Linux (AppImage) amd64 Build Target # +#-----------------------------------------------------------# +"Linux amd64 AppImage": + variables: + TGT_ARCH: "amd64" + TGT_QT: "" + TGT_NAME: "${TGT_ARCH}" + TGT_PATH: "x86_64" + <<: *appimage_script_steps + rules: + - !reference [.upstream_rules, rules] + +"Linux amd64 AppImage (Downstream)": + extends: "Linux amd64 AppImage" + rules: + - !reference [.downstream_rules, rules] + tags: + - "saas-linux-small-amd64" + +#-----------------------------------------------------------# +# Linux (AppImage) Qt6 amd64 Build Target # +#-----------------------------------------------------------# +"Linux amd64 AppImage Qt6": + variables: + TGT_ARCH: "amd64" + TGT_QT: "qt6" + TGT_NAME: "${TGT_ARCH}_${TGT_QT^}" + TGT_PATH: "x86_64" + <<: *appimage_script_steps + +#-----------------------------------------------------------# +# Linux (AppImage) armhf Build Target # +#-----------------------------------------------------------# +"Linux armhf AppImage": + variables: + TGT_ARCH: "armhf" + TGT_QT: "" + TGT_NAME: "${TGT_ARCH}" + TGT_PATH: "${TGT_ARCH}" + <<: *appimage_script_steps + +#-----------------------------------------------------------# +# Linux (AppImage) Qt6 armhf Build Target # +#-----------------------------------------------------------# +"Linux armhf AppImage Qt6": + variables: + TGT_ARCH: "armhf" + TGT_QT: "qt6" + TGT_NAME: "${TGT_ARCH}_${TGT_QT^}" + TGT_PATH: "${TGT_ARCH}" + <<: *appimage_script_steps + +#-----------------------------------------------------------# +# Linux (AppImage) arm64 Build Target # +#-----------------------------------------------------------# +"Linux arm64 AppImage": + variables: + TGT_ARCH: "arm64" + TGT_QT: "" + TGT_NAME: "${TGT_ARCH}" + TGT_PATH: "${TGT_ARCH}" + <<: *appimage_script_steps + +#-----------------------------------------------------------# +# Linux (AppImage) Qt6 arm64 Build Target # +#-----------------------------------------------------------# +"Linux arm64 AppImage Qt6": + variables: + TGT_ARCH: "arm64" + TGT_QT: "qt6" + TGT_NAME: "${TGT_ARCH}_${TGT_QT^}" + TGT_PATH: "${TGT_ARCH}" + <<: *appimage_script_steps + +#-----------------------------------------------------------# +# OpenRGB Common Debian Build Steps # +#-----------------------------------------------------------# +.hidden_deb_script: &debian_script_steps + stage: build + image: registry.gitlab.com/openrgbdevelopers/openrgb-linux-ci-deb-builder:$TGT_DEB-$TGT_ARCH + tags: + - linux + - $TGT_ARCH + script: + - ./scripts/build-package-files.sh debian/changelog + - dpkg-architecture -l + - dpkg-buildpackage -us -B + - rm -v ../openrgb-dbgsym*.deb + - mv -v ../openrgb*.deb ./ + + artifacts: + name: "${CI_PROJECT_NAME}_Linux_${TGT_ARCH}_deb_${CI_COMMIT_SHORT_SHA}" + paths: + - openrgb*.deb + exclude: + - openrgb-dbgsym*.deb + expire_in: 30 days + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# Linux (.deb) Debian Bookworm i386 Build Target # +#-----------------------------------------------------------# +"Linux i386 .deb (Debian Bookworm)": + variables: + TGT_ARCH: "i386" + TGT_DEB: "bookworm" + <<: *debian_script_steps + +#-----------------------------------------------------------# +# Linux (.deb) Debian Trixie i386 Build Target # +#-----------------------------------------------------------# +"Linux i386 .deb (Debian Trixie)": + variables: + TGT_ARCH: "i386" + TGT_DEB: "trixie" + <<: *debian_script_steps + +#-----------------------------------------------------------# +# Linux (.deb) Debian Bookworm amd64 Build Target # +#-----------------------------------------------------------# +"Linux amd64 .deb (Debian Bookworm)": + variables: + TGT_ARCH: "amd64" + TGT_DEB: "bookworm" + <<: *debian_script_steps + +#-----------------------------------------------------------# +# Linux (.deb) Debian Trixie amd64 Build Target # +#-----------------------------------------------------------# +"Linux amd64 .deb (Debian Trixie)": + variables: + TGT_ARCH: "amd64" + TGT_DEB: "trixie" + <<: *debian_script_steps + +#-----------------------------------------------------------# +# Linux (.deb) Debian Bookworm armhf Build Target # +#-----------------------------------------------------------# +"Linux armhf .deb (Debian Bookworm)": + variables: + TGT_ARCH: "armhf" + TGT_DEB: "bookworm" + <<: *debian_script_steps + +#-----------------------------------------------------------# +# Linux (.deb) Debian Trixie armhf Build Target # +#-----------------------------------------------------------# +"Linux armhf .deb (Debian Trixie)": + variables: + TGT_ARCH: "armhf" + TGT_DEB: "trixie" + <<: *debian_script_steps + +#-----------------------------------------------------------# +# Linux (.deb) Debian Bookworm arm64 Build Target # +#-----------------------------------------------------------# +"Linux arm64 .deb (Debian Bookworm)": + variables: + TGT_ARCH: "arm64" + TGT_DEB: "bookworm" + <<: *debian_script_steps + +#-----------------------------------------------------------# +# Linux (.deb) Debian Trixie arm64 Build Target # +#-----------------------------------------------------------# +"Linux arm64 .deb (Debian Trixie)": + variables: + TGT_ARCH: "arm64" + TGT_DEB: "trixie" + <<: *debian_script_steps + +#-----------------------------------------------------------# +# Linux (.rpm, F43) 64-bit Build Target # +#-----------------------------------------------------------# +"Linux 64 F43 rpm": + image: fedora:43 + stage: build + script: + - dnf install rpmdevtools dnf-plugins-core libcurl-devel qt5-qtbase-devel git -y + - rpmdev-setuptree + - ./scripts/build-package-files.sh fedora/OpenRGB.spec + - cp fedora/OpenRGB.spec /root/rpmbuild/SPECS + - cp -rp . /root/rpmbuild/SOURCES/OpenRGB + - cd /root/rpmbuild + - dnf builddep SPECS/OpenRGB.spec -y + - rpmbuild -ba SPECS/OpenRGB.spec + - cd RPMS/x86_64/ + - mv openrgb*.rpm ${CI_PROJECT_DIR}/ + - cd ${CI_PROJECT_DIR} + + artifacts: + name: "${CI_PROJECT_NAME}_Linux_64_rpm_${CI_COMMIT_SHORT_SHA}" + paths: + - openrgb*.rpm + expire_in: 30 days + + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# Debian i386 Bookworm test # +#-----------------------------------------------------------# +"Debian i386 Bookworm": + image: i386/debian:bookworm + stage: test + script: + - apt update + - DEBIAN_FRONTEND=noninteractive apt install -yq --no-install-recommends ./openrgb*i386.deb + - openrgb --version + - apt remove -y openrgb + dependencies: + - "Linux i386 .deb (Debian Bookworm)" + needs: + - "Linux i386 .deb (Debian Bookworm)" + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# Debian amd64 Bookworm test # +#-----------------------------------------------------------# +"Debian amd64 Bookworm": + image: amd64/debian:bookworm + stage: test + script: + - apt update + - DEBIAN_FRONTEND=noninteractive apt install -yq --no-install-recommends ./openrgb*amd64.deb + - openrgb --version + - apt remove -y openrgb + dependencies: + - "Linux amd64 .deb (Debian Bookworm)" + needs: + - "Linux amd64 .deb (Debian Bookworm)" + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# Ubuntu amd64 24.04 test # +#-----------------------------------------------------------# +"Ubuntu amd64 24.04LTS": + image: ubuntu:noble + stage: test + script: + - apt update + - DEBIAN_FRONTEND=noninteractive apt install -yq --no-install-recommends ./openrgb*amd64.deb + - openrgb --version + - apt remove -y openrgb + dependencies: + - "Linux amd64 .deb (Debian Bookworm)" + needs: + - "Linux amd64 .deb (Debian Bookworm)" + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# Fedora 64 v43 test # +#-----------------------------------------------------------# +"Fedora 64 v43": + image: fedora:43 + stage: test + script: + - dnf5 -y install ./openrgb*64.rpm + - openrgb --version + - dnf5 -y remove openrgb + dependencies: + - "Linux 64 F43 rpm" + needs: + - "Linux 64 F43 rpm" + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# Windows (32-bit) Build Target # +#-----------------------------------------------------------# +"Windows 32": + extends: + - .shared_windows_runners + stage: build + script: + - $ErrorActionPreference = "SilentlyContinue" ; Set-MpPreference -DisableRealtimeMonitoring $true ; $ErrorActionPreference = "Stop" + - scripts\build-windows.bat 5.15.0 2019 32 + artifacts: + name: "${CI_PROJECT_NAME}_Windows_32_${CI_COMMIT_SHORT_SHA}" + paths: + - 'OpenRGB Windows 32-bit' + exclude: + - 'OpenRGB Windows 32-bit\*.qm' + expire_in: 30 days + + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# Windows (32-bit) Qt6 Build Target # +#-----------------------------------------------------------# +"Windows 32 Qt6": + extends: + - .shared_windows_runners + stage: build + script: + - $ErrorActionPreference = "SilentlyContinue" ; Set-MpPreference -DisableRealtimeMonitoring $true ; $ErrorActionPreference = "Stop" + - scripts\build-windows.bat 6.8.3 2022 32 + artifacts: + name: "${CI_PROJECT_NAME}_Windows_32_Qt6_${CI_COMMIT_SHORT_SHA}" + paths: + - 'OpenRGB Windows 32-bit' + exclude: + - 'OpenRGB Windows 32-bit\*.qm' + expire_in: 30 days + + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# Windows (64-bit) Build Target # +#-----------------------------------------------------------# +"Windows 64 Base": + extends: + - .shared_windows_runners + stage: build + script: + - $ErrorActionPreference = "SilentlyContinue" ; Set-MpPreference -DisableRealtimeMonitoring $true ; $ErrorActionPreference = "Stop" + - scripts\build-windows.bat 5.15.0 2019 64 + artifacts: + name: "${CI_PROJECT_NAME}_Windows_64_${CI_COMMIT_SHORT_SHA}" + paths: + - 'OpenRGB Windows 64-bit' + exclude: + - 'OpenRGB Windows 64-bit\*.qm' + expire_in: 30 days + + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_PIPELINE_SOURCE == "push" + when: never + +"Windows 64": + extends: "Windows 64 Base" + rules: + - !reference [.upstream_rules, rules] + +"Windows 64 (Downstream)": + extends: "Windows 64 Base" + before_script: + - git clone https://gitlab.com/OpenRGBDevelopers/OpenRGB-Qt-Packages + - cd OpenRGB-Qt-Packages + - .\install-chocolatey.bat + - cd .. + rules: + - !reference [.downstream_rules, rules] + tags: + - "saas-windows-medium-amd64" + +#-----------------------------------------------------------# +# Windows (64-bit) Qt6 Build Target # +#-----------------------------------------------------------# +"Windows 64 Qt6 Base": + extends: + - .shared_windows_runners + stage: build + script: + - $ErrorActionPreference = "SilentlyContinue" ; Set-MpPreference -DisableRealtimeMonitoring $true ; $ErrorActionPreference = "Stop" + - scripts\build-windows.bat 6.8.3 2022 64 + artifacts: + name: "${CI_PROJECT_NAME}_Windows_64_Qt6_${CI_COMMIT_SHORT_SHA}" + paths: + - 'OpenRGB Windows 64-bit' + exclude: + - 'OpenRGB Windows 64-bit\*.qm' + expire_in: 30 days + + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_PIPELINE_SOURCE == "push" + when: never + +"Windows 64 Qt6": + extends: "Windows 64 Qt6 Base" + rules: + - !reference [.upstream_rules, rules] + +"Windows 64 Qt6 (Downstream)": + extends: "Windows 64 Qt6 Base" + before_script: + - git clone https://gitlab.com/OpenRGBDevelopers/OpenRGB-Qt-Packages + - cd OpenRGB-Qt-Packages + - .\install-chocolatey.bat + - cd .. + rules: + - !reference [.downstream_rules, rules] + tags: + - "saas-windows-medium-amd64" + +#-----------------------------------------------------------# +# MacOS Build Target # +#-----------------------------------------------------------# +"MacOS ARM64": + tags: + - macos + stage: build + script: + - ./scripts/build-macos.sh qt5 arm + + artifacts: + name: "${CI_PROJECT_NAME}_MacOS_ARM64_${CI_COMMIT_SHORT_SHA}" + paths: + - OpenRGB.app + expire_in: 30 days + + rules: + - !reference [.upstream_rules, rules] + +"MacOS ARM64 Qt6": + tags: + - macos + stage: build + script: + - ./scripts/build-macos.sh qt6 arm + + artifacts: + name: "${CI_PROJECT_NAME}_MacOS_ARM64_Qt6_${CI_COMMIT_SHORT_SHA}" + paths: + - OpenRGB.app + expire_in: 30 days + + rules: + - !reference [.upstream_rules, rules] + +"MacOS Intel": + tags: + - macos + stage: build + script: + - ./scripts/build-macos.sh qt5 intel + + artifacts: + name: "${CI_PROJECT_NAME}_MacOS_Intel_${CI_COMMIT_SHORT_SHA}" + paths: + - OpenRGB.app + expire_in: 30 days + + rules: + - !reference [.upstream_rules, rules] + +"MacOS Intel Qt6": + tags: + - macos + stage: build + script: + - ./scripts/build-macos.sh qt6 intel + + artifacts: + name: "${CI_PROJECT_NAME}_MacOS_Intel_Qt6_${CI_COMMIT_SHORT_SHA}" + paths: + - OpenRGB.app + expire_in: 30 days + + rules: + - !reference [.upstream_rules, rules] + +#-----------------------------------------------------------# +# Windows (64-bit) MSI Target # +#-----------------------------------------------------------# +"Windows 64 MSI": + image: registry.gitlab.com/openrgbdevelopers/openrgb-linux-ci-deb-builder:bookworm-i386 + stage: deploy + tags: + - linux + - i386 + script: + - ls -la + - ls -la "OpenRGB Windows 64-bit/" + - ./scripts/build-msi.sh + dependencies: + - "Windows 64" + needs: + - "Windows 64" + + artifacts: + name: "${CI_PROJECT_NAME}_Windows_64_msi_${CI_COMMIT_SHORT_SHA}" + paths: + - OpenRGB_Windows_64.msi + expire_in: 30 days + + rules: + - !reference [.upstream_rules, rules] diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS new file mode 100644 index 0000000..b7e7b88 --- /dev/null +++ b/.gitlab/CODEOWNERS @@ -0,0 +1,107 @@ +# OpenRGB CODEOWNERS + +# Default Code Owner +* @Calcprogrammer1 + +CODEOWNERS @Calcprogrammer1 + +#-----------------------------------------------------------------------------# +# Controllers - Directories # +#-----------------------------------------------------------------------------# +[Controllers] +/Controllers/AMDWraithPrismController/ +/Controllers/ASRockPolychromeUSBController/ +/Controllers/ASRockSMBusController/ +/Controllers/AlienwareController/ +/Controllers/AlienwareKeyboardController/ +/Controllers/AnnePro2Controller/ +/Controllers/AsusAuraCoreController/ +/Controllers/AsusAuraGPUController/ +/Controllers/AsusAuraUSBController/ +/Controllers/AsusTUFLaptopController/ +/Controllers/BlinkyTapeController/ +/Controllers/CoolerMasterController/ @Dr_No +/Controllers/CorsairCommanderCoreController/ +/Controllers/CorsairDRAMController/ +/Controllers/CorsairHydroController/ +/Controllers/CorsairHydroPlatinumController/ +/Controllers/CorsairLightingNodeController/ +/Controllers/CorsairPeripheralController/ +/Controllers/CorsairVengeanceController/ +/Controllers/CorsairWirelessController/ +/Controllers/CreativeController/ +/Controllers/CrucialController/ +/Controllers/DasKeyboardController/ +/Controllers/DebugController/ +/Controllers/DuckyKeyboardController/ +/Controllers/DygmaRaiseController/ +/Controllers/E131Controller/ +/Controllers/EKController/ @Dr_No +/Controllers/ENESMBusController/ +/Controllers/EVGAAmpereGPUController/ @TheRogueZeta +/Controllers/EVGAGP102GPUController/ +/Controllers/EVGAPascalGPUController/ +/Controllers/EVGATuringGPUController/ @TheRogueZeta +/Controllers/EVGASMBusController/ @balika011 +/Controllers/EVGAUSBController/ @Dr_No +/Controllers/EVisionKeyboardController/ +/Controllers/EspurnaController/ +/Controllers/FanBusController/ +/Controllers/FaustusController/ +/Controllers/GainwardGPUController/ +/Controllers/GalaxGPUController/ +/Controllers/GigabyteAorusCPUCoolerController/ +/Controllers/GigabyteRGBFusion2DRAMController/ +/Controllers/GigabyteRGBFusion2GPUController/ +/Controllers/GigabyteRGBFusion2SMBusController/ @Dr_No +/Controllers/GigabyteRGBFusion2USBController/ @Dr_No +/Controllers/GigabyteRGBFusionController/ +/Controllers/GigabyteRGBFusionGPUController/ +/Controllers/HPOmen30LController/ +/Controllers/HoltekController/ +/Controllers/HyperXDRAMController/ +/Controllers/HyperXKeyboardController/ +/Controllers/HyperXMouseController/ +/Controllers/HyperXMousematController/ +/Controllers/LEDStripController/ +/Controllers/LianLiController/ +/Controllers/LinuxLEDController/ +/Controllers/LogitechController/ @Dr_No +/Controllers/MSI3ZoneController/ +/Controllers/MSIGPUController/ +/Controllers/MSIMysticLightController/ +/Controllers/MSIRGBController/ +/Controllers/NZXTHue2Controller/ +/Controllers/NZXTHuePlusController/ +/Controllers/NZXTKrakenController/ +/Controllers/PNYGPUController/ +/Controllers/PatriotViperController/ +/Controllers/PhilipsHueController/ +/Controllers/PhilipsWizController/ +/Controllers/QMKOpenRGBController/ +/Controllers/RazerController/ +/Controllers/RedragonController/ +/Controllers/RoccatController/ +/Controllers/SapphireGPUController/ +/Controllers/SinowealthController/ +/Controllers/SonyDS4Controller/ +/Controllers/SteelSeriesController/ +/Controllers/TecknetController/ +/Controllers/ThermaltakePoseidonZRGBController/ +/Controllers/ThermaltakeRiingController/ +/Controllers/ThingMController/ +/Controllers/WootingKeyboardController/ @Dr_No +/Controllers/YeelightController/ +/Controllers/ZalmanZSyncController/ + +#-----------------------------------------------------------------------------# +# Controllers - File exceptions # +# # +# As sections get combined and the last rule applies any specific file # +# that is an exception to the above should be explicitly named here # +#-----------------------------------------------------------------------------# +[Controllers] +/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController.cpp @TheRogueZeta +/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController.h @TheRogueZeta +/Controllers/ASRockSMBusController/RGBController_ASRockPolychromeV1SMBus.cpp @TheRogueZeta +/Controllers/ASRockSMBusController/RGBController_ASRockPolychromeV1SMBus.h @TheRogueZeta diff --git a/.gitlab/issue_templates/Bug Report.md b/.gitlab/issue_templates/Bug Report.md new file mode 100644 index 0000000..f279b07 --- /dev/null +++ b/.gitlab/issue_templates/Bug Report.md @@ -0,0 +1,33 @@ + + +### Description of Bug + + +### Attached Log + + +### Operating System + +~"OS - Linux" +~"OS - MacOS" +~"OS - Windows" +### Hardware Configuration + diff --git a/.gitlab/issue_templates/Feature Request.md b/.gitlab/issue_templates/Feature Request.md new file mode 100644 index 0000000..b58bc79 --- /dev/null +++ b/.gitlab/issue_templates/Feature Request.md @@ -0,0 +1,11 @@ + + +### Feature Request + diff --git a/.gitlab/issue_templates/New Device.md b/.gitlab/issue_templates/New Device.md new file mode 100644 index 0000000..2862ec9 --- /dev/null +++ b/.gitlab/issue_templates/New Device.md @@ -0,0 +1,86 @@ + + +### Name of device: + + + +### Link to manufacturer's product page: + + + +### Please select what type of device/interface the device uses: + + +~"DeviceType::IDK" +~"DeviceType::USB" +~"DeviceType::GPU::AMD" +~"DeviceType::GPU::NVidia" +~"DeviceType::SMBus" +~"DeviceType::WMI" + + + +### ID information: + + + + + +### Please attach screenshots of the device's official control application here: + + + +### Please attach device captures here: + + + + + + + + + + + +/label ~"Issue Type - New Device" +/label ~"NewDevice::Step0 - Unconfirmed" + +# Checklist for Step2 +- [ ] Name of device +- [ ] A link to the vendors product page has been included +- [ ] The transport bus has been identified and the appropriate label added to the issue. +- [ ] The device ID's have been included for [USB](https://gitlab.com/Dr_No/OpenRGB/-/wikis/USB-Vendor-Identification-and-Product-Identification) or PCI +- [ ] Screenshots of the OEM Application are included +- [ ] There is either, appropriate code examples linked or suitable device captures attached + diff --git a/.gitlab/merge_request_templates/New Device.md b/.gitlab/merge_request_templates/New Device.md new file mode 100644 index 0000000..2e9941d --- /dev/null +++ b/.gitlab/merge_request_templates/New Device.md @@ -0,0 +1,25 @@ + + + + + + + + +# Checklist for Accepting a Merge Request for a New Device +- [ ] The source branch of the merge request is not protected (`master` is protected by default when creating a fork, so it is recommended to not use it as your source). +- [ ] The `New Device` issue raised for this device is linked to this MR with a keyword `Closes / Resolves / Implements` +- [ ] There is a device protocol page in the [Developer Wiki](https://gitlab.com/OpenRGBDevelopers/OpenRGB-Wiki) or there is enough information / captures in the `New Device` issue to provide ongoing support. +- [ ] The code to be merged follows the style guide and change requirements as [documented in the contributing guide](https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/master/CONTRIBUTING.md). + +- [ ] Meta data for the device is included in `RGBController_*` file +- [ ] This device is detected and is working on Windows 10 and / or 11 +- [ ] This device is detected and is working on Linux (Please specify distribution and releases tested) +- [ ] Logging for Info, Warnings and Errors has been added for troubleshooting purposes + diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..7dc4dcb --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,14 @@ +[extend] +useDefault = true + +[[allowlists]] +description = "Public vendor/protocol constants in the pristine OpenRGB 1.0rc3 import" +commits = ["a15d49986ddada23b60a2dae5a7885919c28ac6f"] + +[[allowlists]] +description = "Exact OpenRGB identifiers that resemble generic API keys after a parentless export" +regexTarget = "match" +regexes = [ + '''caps\.idx_perkey_v2\s*:\s*caps\.idx_perkey_v1''', + '''NVAPI_I2C_SPEED_200KHZ,\s*NVAPI_I2C_SPEED_400KHZ''', +] diff --git a/AutoStart/AutoStart-FreeBSD.cpp b/AutoStart/AutoStart-FreeBSD.cpp new file mode 100644 index 0000000..d76fbbf --- /dev/null +++ b/AutoStart/AutoStart-FreeBSD.cpp @@ -0,0 +1,177 @@ +/*---------------------------------------------------------*\ +| AutoStart-FreeBSD.cpp | +| | +| Autostart implementation for FreeBSD | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "AutoStart-FreeBSD.h" +#include "LogManager.h" +#include "filesystem.h" + +AutoStart::AutoStart(std::string name) +{ + InitAutoStart(name); +} + +bool AutoStart::DisableAutoStart() +{ + std::error_code autostart_file_remove_errcode; + bool success = false; + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + /*-------------------------------------------------*\ + | If file doesn't exist, disable is successful | + \*-------------------------------------------------*/ + if(!filesystem::exists(autostart_file)) + { + success = true; + } + /*-------------------------------------------------*\ + | Otherwise, delete the file | + \*-------------------------------------------------*/ + else + { + success = filesystem::remove(autostart_file, autostart_file_remove_errcode); + + if(!success) + { + LOG_ERROR("[AutoStart] An error occurred removing the auto start file."); + } + } + } + else + { + LOG_ERROR("[AutoStart] Could not establish correct autostart file path."); + } + + return(success); +} + +bool AutoStart::EnableAutoStart(AutoStartInfo autostart_info) +{ + bool success = false; + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + std::ofstream autostart_file_stream(autostart_file, std::ios::out | std::ios::trunc); + + /*-------------------------------------------------*\ + | Error out if the file could not be opened | + \*-------------------------------------------------*/ + if(!autostart_file_stream) + { + LOG_ERROR("[AutoStart] Could not open %s for writing.", autostart_file.c_str()); + success = false; + } + /*-------------------------------------------------*\ + | Otherwise, write the file | + \*-------------------------------------------------*/ + else + { + autostart_file_stream.close(); + success = !autostart_file_stream.fail(); + + if (!success) + { + LOG_ERROR("[AutoStart] An error occurred writing the auto start file."); + } + } + } + else + { + LOG_ERROR("[AutoStart] Could not establish correct autostart file path."); + } + + return(success); +} + +bool AutoStart::IsAutoStartEnabled() +{ + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + return(filesystem::exists(autostart_file)); + } + else + { + return(false); + } +} + +std::string AutoStart::GetExePath() +{ + /*-----------------------------------------------------*\ + | Create the OpenRGB executable path | + \*-----------------------------------------------------*/ + char exepath[ PATH_MAX ]; + + ssize_t count = readlink("/proc/self/exe", exepath, PATH_MAX); + + return(std::string(exepath, (count > 0) ? count : 0)); +} + +void AutoStart::InitAutoStart(std::string name) +{ + std::string autostart_dir; + + autostart_name = name; + + /*-----------------------------------------------------*\ + | Get home and config paths | + \*-----------------------------------------------------*/ + const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + const char *home = getenv("HOME"); + + /*-----------------------------------------------------*\ + | Determine where the autostart .desktop files are | + | kept | + \*-----------------------------------------------------*/ + if(xdg_config_home != NULL) + { + autostart_dir = xdg_config_home; + autostart_dir = autostart_dir + "/autostart/"; + } + else if(home != NULL) + { + autostart_dir = home; + autostart_dir = autostart_dir + "/.config/autostart/"; + } + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_dir != "") + { + std::error_code ec; + + bool success = true; + + if(!filesystem::exists(autostart_dir)) + { + success = filesystem::create_directories(autostart_dir, ec); + } + + if(success) + { + autostart_file = autostart_dir + autostart_name + ".desktop"; + } + } +} + diff --git a/AutoStart/AutoStart-FreeBSD.h b/AutoStart/AutoStart-FreeBSD.h new file mode 100644 index 0000000..4266d06 --- /dev/null +++ b/AutoStart/AutoStart-FreeBSD.h @@ -0,0 +1,28 @@ +/*---------------------------------------------------------*\ +| AutoStart-FreeBSD.h | +| | +| Autostart implementation for FreeBSD | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "AutoStart.h" + +class AutoStart: public AutoStartInterface +{ +public: + AutoStart(std::string name); + + bool DisableAutoStart(); + bool EnableAutoStart(AutoStartInfo autostart_info); + bool IsAutoStartEnabled(); + std::string GetExePath(); + +private: + void InitAutoStart(std::string name); + std::string GenerateLaunchAgentFile(AutoStartInfo autostart_info); +}; diff --git a/AutoStart/AutoStart-Linux.cpp b/AutoStart/AutoStart-Linux.cpp new file mode 100644 index 0000000..64bac05 --- /dev/null +++ b/AutoStart/AutoStart-Linux.cpp @@ -0,0 +1,216 @@ +/*---------------------------------------------------------*\ +| AutoStart-Linux.cpp | +| | +| Autostart implementation for Linux | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "AutoStart-Linux.h" +#include "LogManager.h" +#include "filesystem.h" + +AutoStart::AutoStart(std::string name) +{ + InitAutoStart(name); +} + +bool AutoStart::DisableAutoStart() +{ + std::error_code autostart_file_remove_errcode; + bool success = false; + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + /*-------------------------------------------------*\ + | If file doesn't exist, disable is successful | + \*-------------------------------------------------*/ + if(!filesystem::exists(autostart_file)) + { + success = true; + } + /*-------------------------------------------------*\ + | Otherwise, delete the file | + \*-------------------------------------------------*/ + else + { + success = filesystem::remove(autostart_file, autostart_file_remove_errcode); + + if(!success) + { + LOG_ERROR("[AutoStart] An error occurred removing the auto start file."); + } + } + } + else + { + LOG_ERROR("[AutoStart] Could not establish correct autostart file path."); + } + + return(success); +} + +bool AutoStart::EnableAutoStart(AutoStartInfo autostart_info) +{ + bool success = false; + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + std::string desktop_file = GenerateDesktopFile(autostart_info); + std::ofstream autostart_file_stream(autostart_file, std::ios::out | std::ios::trunc); + + /*-------------------------------------------------*\ + | Error out if the file could not be opened | + \*-------------------------------------------------*/ + if(!autostart_file_stream) + { + LOG_ERROR("[AutoStart] Could not open %s for writing.", autostart_file.c_str()); + success = false; + } + /*-------------------------------------------------*\ + | Otherwise, write the file | + \*-------------------------------------------------*/ + else + { + autostart_file_stream << desktop_file; + autostart_file_stream.close(); + success = !autostart_file_stream.fail(); + + if (!success) + { + LOG_ERROR("[AutoStart] An error occurred writing the auto start file."); + } + } + } + else + { + LOG_ERROR("[AutoStart] Could not establish correct autostart file path."); + } + + return(success); +} + +bool AutoStart::IsAutoStartEnabled() +{ + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + return(filesystem::exists(autostart_file)); + } + else + { + return(false); + } +} + +std::string AutoStart::GetExePath() +{ + /*-----------------------------------------------------*\ + | Create the OpenRGB executable path | + \*-----------------------------------------------------*/ + char exepath[ PATH_MAX ]; + + ssize_t count = readlink("/proc/self/exe", exepath, PATH_MAX); + + return(std::string(exepath, (count > 0) ? count : 0)); +} + +/*---------------------------------------------------------*\ +| Linux AutoStart Implementation | +| Private Methods | +\*---------------------------------------------------------*/ + +std::string AutoStart::GenerateDesktopFile(AutoStartInfo autostart_info) +{ + /*-----------------------------------------------------*\ + | Generate a .desktop file from the AutoStart | + | parameters | + \*-----------------------------------------------------*/ + std::stringstream fileContents; + + fileContents << "[Desktop Entry]" << std::endl; + fileContents << "Categories=" << autostart_info.category << std::endl; + fileContents << "Comment=" << autostart_info.desc << std::endl; + fileContents << "Icon=" << autostart_info.icon << std::endl; + fileContents << "Name=" << GetAutoStartName() << std::endl; + fileContents << "StartupNotify=true" << std::endl; + fileContents << "Terminal=false" << std::endl; + fileContents << "Type=Application" << std::endl; + + /*-----------------------------------------------------*\ + | Add the executable path and arguments | + \*-----------------------------------------------------*/ + fileContents << "Exec=" << autostart_info.path; + + if (autostart_info.args != "") + { + fileContents << " " << autostart_info.args; + } + + fileContents << std::endl; + + return(fileContents.str()); +} + +void AutoStart::InitAutoStart(std::string name) +{ + std::string autostart_dir; + + autostart_name = name; + + /*-----------------------------------------------------*\ + | Get home and config paths | + \*-----------------------------------------------------*/ + const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + const char *home = getenv("HOME"); + + /*-----------------------------------------------------*\ + | Determine where the autostart .desktop files are | + | kept | + \*-----------------------------------------------------*/ + if(xdg_config_home != NULL) + { + autostart_dir = xdg_config_home; + autostart_dir = autostart_dir + "/autostart/"; + } + else if(home != NULL) + { + autostart_dir = home; + autostart_dir = autostart_dir + "/.config/autostart/"; + } + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_dir != "") + { + std::error_code ec; + + bool success = true; + + if(!filesystem::exists(autostart_dir)) + { + success = filesystem::create_directories(autostart_dir, ec); + } + + if(success) + { + autostart_file = autostart_dir + autostart_name + ".desktop"; + } + } +} + diff --git a/AutoStart/AutoStart-Linux.h b/AutoStart/AutoStart-Linux.h new file mode 100644 index 0000000..89a0708 --- /dev/null +++ b/AutoStart/AutoStart-Linux.h @@ -0,0 +1,28 @@ +/*---------------------------------------------------------*\ +| AutoStart-Linux.h | +| | +| Autostart implementation for Linux | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "AutoStart.h" + +class AutoStart: public AutoStartInterface +{ +public: + AutoStart(std::string name); + + bool DisableAutoStart(); + bool EnableAutoStart(AutoStartInfo autostart_info); + bool IsAutoStartEnabled(); + std::string GetExePath(); + +private: + void InitAutoStart(std::string name); + std::string GenerateDesktopFile(AutoStartInfo autostart_info); +}; diff --git a/AutoStart/AutoStart-MacOS.cpp b/AutoStart/AutoStart-MacOS.cpp new file mode 100644 index 0000000..152217e --- /dev/null +++ b/AutoStart/AutoStart-MacOS.cpp @@ -0,0 +1,210 @@ +/*---------------------------------------------------------*\ +| AutoStart-MacOS.cpp | +| | +| Autostart implementation for MacOS | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "AutoStart-MacOS.h" +#include "LogManager.h" +#include "filesystem.h" + +AutoStart::AutoStart(std::string name) +{ + InitAutoStart(name); +} + +bool AutoStart::DisableAutoStart() +{ + std::error_code autostart_file_remove_errcode; + bool success = false; + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + /*-------------------------------------------------*\ + | If file doesn't exist, disable is successful | + \*-------------------------------------------------*/ + if(!filesystem::exists(autostart_file)) + { + success = true; + } + /*-------------------------------------------------*\ + | Otherwise, delete the file | + \*-------------------------------------------------*/ + else + { + success = filesystem::remove(autostart_file, autostart_file_remove_errcode); + + if(!success) + { + LOG_ERROR("[AutoStart] An error occurred removing the auto start file."); + } + } + } + else + { + LOG_ERROR("[AutoStart] Could not establish correct autostart file path."); + } + + return(success); +} + +bool AutoStart::EnableAutoStart(AutoStartInfo autostart_info) +{ + bool success = false; + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + std::string desktop_file = GenerateLaunchAgentFile(autostart_info); + std::ofstream autostart_file_stream(autostart_file, std::ios::out | std::ios::trunc); + + /*-------------------------------------------------*\ + | Error out if the file could not be opened | + \*-------------------------------------------------*/ + if(!autostart_file_stream) + { + LOG_ERROR("[AutoStart] Could not open %s for writing.", autostart_file.c_str()); + success = false; + } + /*-------------------------------------------------*\ + | Otherwise, write the file | + \*-------------------------------------------------*/ + else + { + autostart_file_stream << desktop_file; + autostart_file_stream.close(); + success = !autostart_file_stream.fail(); + + if (!success) + { + LOG_ERROR("[AutoStart] An error occurred writing the auto start file."); + } + } + } + else + { + LOG_ERROR("[AutoStart] Could not establish correct autostart file path."); + } + + return(success); +} + +bool AutoStart::IsAutoStartEnabled() +{ + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + return(filesystem::exists(autostart_file)); + } + else + { + return(false); + } +} + +std::string AutoStart::GetExePath() +{ + /*-----------------------------------------------------*\ + | Create the OpenRGB executable path | + \*-----------------------------------------------------*/ + char exepath[ PATH_MAX ]; + uint32_t exesize = PATH_MAX; + + int ret_val = _NSGetExecutablePath(exepath, &exesize); + + return(std::string(exepath, (ret_val == 0) ? strlen(exepath) : 0)); + + return(""); +} + +/*---------------------------------------------------------*\ +| MacOS AutoStart Implementation | +| Private Methods | +\*---------------------------------------------------------*/ + +std::string AutoStart::GenerateLaunchAgentFile(AutoStartInfo autostart_info) +{ + /*-----------------------------------------------------*\ + | Generate a .plist file from the AutoStart | + | parameters | + \*-----------------------------------------------------*/ + std::stringstream fileContents; + + fileContents << "" << std::endl; + fileContents << "" << std::endl; + fileContents << "" << std::endl; + fileContents << "" << std::endl; + fileContents << " Label" << std::endl; + fileContents << " org.openrgb" << std::endl; + fileContents << " ProgramArguments" << std::endl; + fileContents << " " << std::endl; + fileContents << " " << autostart_info.path << "" << std::endl; + + if(autostart_info.args != "") + { + std::istringstream arg_parser(autostart_info.args); + std::string arg; + + while(arg_parser >> arg) + { + fileContents << " " << arg << "" << std::endl; + } + } + + fileContents << " " << std::endl; + fileContents << " RunAtLoad" << std::endl; + fileContents << "" << std::endl; + fileContents << "" << std::endl; + + return(fileContents.str()); +} + +void AutoStart::InitAutoStart(std::string name) +{ + std::string autostart_dir; + + autostart_name = name; + + /*-----------------------------------------------------*\ + | Determine where the autostart .desktop files are | + | kept | + \*-----------------------------------------------------*/ + autostart_dir = getenv("HOME"); + autostart_dir = autostart_dir + "/Library/LaunchAgents/"; + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_dir != "") + { + std::error_code ec; + + bool success = true; + + if(!filesystem::exists(autostart_dir)) + { + success = filesystem::create_directories(autostart_dir, ec); + } + + if(success) + { + autostart_file = autostart_dir + autostart_name + ".plist"; + } + } +} + diff --git a/AutoStart/AutoStart-MacOS.h b/AutoStart/AutoStart-MacOS.h new file mode 100644 index 0000000..b97068f --- /dev/null +++ b/AutoStart/AutoStart-MacOS.h @@ -0,0 +1,28 @@ +/*---------------------------------------------------------*\ +| AutoStart-MacOS.h | +| | +| Autostart implementation for MacOS | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "AutoStart.h" + +class AutoStart: public AutoStartInterface +{ +public: + AutoStart(std::string name); + + bool DisableAutoStart(); + bool EnableAutoStart(AutoStartInfo autostart_info); + bool IsAutoStartEnabled(); + std::string GetExePath(); + +private: + void InitAutoStart(std::string name); + std::string GenerateLaunchAgentFile(AutoStartInfo autostart_info); +}; diff --git a/AutoStart/AutoStart-Windows.cpp b/AutoStart/AutoStart-Windows.cpp new file mode 100644 index 0000000..54036a8 --- /dev/null +++ b/AutoStart/AutoStart-Windows.cpp @@ -0,0 +1,206 @@ +/*---------------------------------------------------------*\ +| AutoStart-Windows.cpp | +| | +| Autostart implementation for Windows | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "AutoStart-Windows.h" +#include "LogManager.h" +#include "filesystem.h" +#include "windows.h" + +AutoStart::AutoStart(std::string name) +{ + InitAutoStart(name); +} + +bool AutoStart::DisableAutoStart() +{ + std::error_code autostart_file_remove_errcode; + bool success = false; + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + /*-------------------------------------------------*\ + | If file doesn't exist, disable is successful | + \*-------------------------------------------------*/ + if(!filesystem::exists(autostart_file)) + { + success = true; + } + /*-------------------------------------------------*\ + | Otherwise, delete the file | + \*-------------------------------------------------*/ + else + { + success = filesystem::remove(autostart_file, autostart_file_remove_errcode); + + if(!success) + { + LOG_ERROR("[AutoStart] An error occurred removing the auto start file."); + } + } + } + else + { + LOG_ERROR("[AutoStart] Could not establish correct autostart file path."); + } + + return(success); +} + +bool AutoStart::EnableAutoStart(AutoStartInfo autostart_info) +{ + bool success = false; + + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + bool weInitialised = false; + HRESULT result; + IShellLinkW* shellLink = NULL; + + std::wstring exepathw = utf8_decode(autostart_info.path); + std::wstring argumentsw = utf8_decode(autostart_info.args); + std::wstring startupfilepathw = utf8_decode(autostart_file); + std::wstring descriptionw = utf8_decode(autostart_info.desc); + std::wstring iconw = utf8_decode(autostart_info.path); + + result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&shellLink); + + /*-------------------------------------------------*\ + | If not initialized, initialize | + \*-------------------------------------------------*/ + if(result == CO_E_NOTINITIALIZED) + { + weInitialised = true; + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&shellLink); + } + + /*-------------------------------------------------*\ + | If successfully initialized, save a shortcut | + | from the AutoStart parameters | + \*-------------------------------------------------*/ + if(SUCCEEDED(result)) + { + shellLink->SetPath(exepathw.c_str()); + shellLink->SetArguments(argumentsw.c_str()); + shellLink->SetDescription(descriptionw.c_str()); + shellLink->SetIconLocation(iconw.c_str(), 0); + + IPersistFile* persistFile; + + result = shellLink->QueryInterface(IID_IPersistFile, (void**)&persistFile); + + if(SUCCEEDED(result)) + { + result = persistFile->Save(startupfilepathw.c_str(), TRUE); + success = SUCCEEDED(result); + persistFile->Release(); + } + + shellLink->Release(); + } + + /*-------------------------------------------------*\ + | Uninitialize when done | + \*-------------------------------------------------*/ + if(weInitialised) + { + CoUninitialize(); + } + } + else + { + LOG_ERROR("[AutoStart] Could not establish correct autostart file path."); + } + + return success; +} + +bool AutoStart::IsAutoStartEnabled() +{ + /*-----------------------------------------------------*\ + | Check if the filename is valid | + \*-----------------------------------------------------*/ + if(autostart_file != "") + { + return(filesystem::exists(autostart_file)); + } + else + { + return(false); + } +} + +std::string AutoStart::GetExePath() +{ + /*-----------------------------------------------------*\ + | Create the OpenRGB executable path | + \*-----------------------------------------------------*/ + char exepath[MAX_PATH] = ""; + + DWORD count = GetModuleFileNameA(NULL, exepath, MAX_PATH); + + return(std::string(exepath, (count > 0) ? count : 0)); +} + +/*---------------------------------------------------------*\ +| Windows AutoStart Implementation | +| Private Methods | +\*---------------------------------------------------------*/ + +void AutoStart::InitAutoStart(std::string name) +{ + char startMenuPath[MAX_PATH]; + + autostart_name = name; + + /*-----------------------------------------------------*\ + | Get startup applications path | + \*-----------------------------------------------------*/ + HRESULT result = SHGetFolderPathA(NULL, CSIDL_PROGRAMS, NULL, 0, startMenuPath); + + if(SUCCEEDED(result)) + { + autostart_file = std::string(startMenuPath); + + autostart_file += "\\Startup\\" + autostart_name + ".lnk"; + } + else + { + autostart_file.clear(); + } +} + +/*---------------------------------------------------------*\ +| Convert an UTF8 string to a wide Unicode String | +| (from wmi.cpp) | +\*---------------------------------------------------------*/ +std::wstring AutoStart::utf8_decode(const std::string& str) +{ + if(str.empty()) + { + return std::wstring(); + } + + int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int) str.size(), nullptr, 0); + + std::wstring wstrTo(size_needed, 0); + + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int) str.size(), &wstrTo[0], size_needed); + + return(wstrTo); +} diff --git a/AutoStart/AutoStart-Windows.h b/AutoStart/AutoStart-Windows.h new file mode 100644 index 0000000..bb89fb4 --- /dev/null +++ b/AutoStart/AutoStart-Windows.h @@ -0,0 +1,28 @@ +/*---------------------------------------------------------*\ +| AutoStart-Windows.h | +| | +| Autostart implementation for Windows | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "AutoStart.h" + +class AutoStart: public AutoStartInterface +{ +public: + AutoStart(std::string name); + + bool DisableAutoStart(); + bool EnableAutoStart(AutoStartInfo autostart_info); + bool IsAutoStartEnabled(); + std::string GetExePath(); + +private: + void InitAutoStart(std::string name); + std::wstring utf8_decode(const std::string& str); +}; diff --git a/AutoStart/AutoStart.cpp b/AutoStart/AutoStart.cpp new file mode 100644 index 0000000..9c0f87c --- /dev/null +++ b/AutoStart/AutoStart.cpp @@ -0,0 +1,20 @@ +/*---------------------------------------------------------*\ +| AutoStart.cpp | +| | +| Autostart common implementation | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "AutoStart.h" + +std::string AutoStartInterface::GetAutoStartFile() +{ + return(autostart_file); +} + +std::string AutoStartInterface::GetAutoStartName() +{ + return(autostart_name); +} diff --git a/AutoStart/AutoStart.h b/AutoStart/AutoStart.h new file mode 100644 index 0000000..7366724 --- /dev/null +++ b/AutoStart/AutoStart.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| AutoStart.h | +| | +| Autostart common implementation | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +struct AutoStartInfo +{ + std::string path; + std::string args; + std::string desc; + std::string icon; + std::string category; +}; + +class AutoStartInterface +{ +public: + virtual bool DisableAutoStart() = 0; + virtual bool EnableAutoStart(AutoStartInfo autostart_info) = 0; + virtual bool IsAutoStartEnabled() = 0; + virtual std::string GetExePath() = 0; + + std::string GetAutoStartFile(); + std::string GetAutoStartName(); + +protected: + std::string autostart_file; + std::string autostart_name; +}; + +#ifdef _WIN32 +#include "AutoStart-Windows.h" +#endif + +#ifdef __linux__ +#include "AutoStart-Linux.h" +#endif + +#ifdef __APPLE__ +#include "AutoStart-MacOS.h" +#endif + +#ifdef __FreeBSD__ +#include "AutoStart-FreeBSD.h" +#endif diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..19720d0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,75 @@ +# Contributing to OpenRGB + +The OpenRGB project welcomes contributions from the community. The project would not support the number of devices it does today without the amazing contributions from community developers. If you want to add a new device, fix a bug, or add a feature, feel free to open a merge request on the OpenRGB GitLab (https://gitlab.com/CalcProgrammer1/OpenRGB). + +## Creating a Merge Request + +To create a merge request, log into GitLab and fork the OpenRGB project. Push your changes to your fork and then use the Create Merge Request option. Before opening a merge request, please review the following best practices to help your merge request get merged without excessive delay. + +* Please keep the scope of each merge request limited to one functional area. If you wish to add a new device controller, for instance, don't also do code cleanup in another controller as part of the same merge request. +* Explain what your merge request is changing, why you feel the change is necessary, and add any additional information you feel is needed to best understand your change in the merge request description. +* Mark your merge request as a Draft (start title with "Draft:") until it has been fully developed and ideally tested and verified working. Remove Draft status when you are ready for the code to be reviewed and merged. I typically do not look at or review Draft merge requests. +* Follow the Style Guidelines below when making your code changes. +* Avoid using `git merge` when updating your fork. The OpenRGB project uses a linear git history, meaning all changes are rebased onto the tip of master. By using `git rebase` to update your fork, you will make it easier to accept merge requests. I also recommend squashing your commits, though GitLab will do this as part of the merge process if you don't. +* Do not submit a merge request using your fork's `master` branch as the source. The `master` branch is protected by default when creating a fork and I cannot manually rebase a protected branch before merging. If you submit a merge request with a protected `master` branch as the source, it may not get merged until you unprotect it. + +### Pipelines, Shared Runners, Quotas, and Accepting Merge Requests + +Since mid 2022, GitLab has severely limited the usage of shared runners to all free users. With that in mind, most pipeline jobs are skipped on forked copies of the project. Only the Windows 64bit and Linux AppImage amd64 jobs are run on forks, and only if you have permission and available CI minutes on your account. Unfortunately, with the ever tightening restrictions GitLab has on free CI usage, even to public projects with an open source license, you likely will not be able to utilize the CI builds for your own fork unless you set up your own runners. Because of this, I no longer require a successful CI run in order to accept a merge request as it is an unreasonable ask given the current CI situation. If your merge request requires additional verification, I may choose to merge it into a branch instead of into `master` so that my own runners can run the full set of jobs and then I will pull it into master after any verification or changes. This also means that any build breakages inadvertently caused by merging a merge request may result in the merge being backed out, in which case I will either fix it myself or leave a message on the merge request to rework and resubmit a fixed version. + +Please refer to the following link for [further information relating to minutes and quotas](https://docs.gitlab.com/ee/ci/pipelines/cicd_minutes.html). + +## Style Guidelines + +OpenRGB is written in C++, uses the Qt framework for UI, and uses the QMake build system. While OpenRGB does use C++, I am primarily a C programmer and prefer doing things "C Style" vs. C++ style. C++'s object oriented programming features fit the needs of a program such as OpenRGB where there are many implementations of a single interface so I chose to write it in C++ over C. Still, however, I prefer using C-style code where possible. When making changes to existing code files, try to follow the existing style. Merge requests that go out of their way to restyle code will not be accepted and will be sent back for rework. + +### Functional Style + +* Limit use of C++ std library data types + * For fixed-length containers, use C arrays + * For list constants, use const C arrays + * For fixed-length strings, use C char buffers ("C strings"), though using std::string is acceptable if the value will be placed in a std::string + * For basic types, prefer using the non-typedef'd basic types (char, short, int, etc) over typedef'd (uint8_t, uint16_t, uint32_t, etc) + * Unless defined in an OpenRGB API or the file is implementing an existing API using these types (such as i2c_smbus) + * For hard-coded values, use `#define`s over const variables unless you need a pointer to the value + * For variable-length containers, use std::vector + * For variable-length strings, use std::string + * `struct` should not include any functions, only variables + * `class` should only be used for objects containing functions + * Other std types may be used sparingly if necessary +* Only use Qt libraries, types, and functionality in user interface files + * Anything under the qt/ folder basically + * Do not use QDebug, use our LogManager instead. If you use QDebug during development, please remove it before submitting your merge request +* Use C-style indexed for loops when iterating through an array or vector +* Use C-style casts unless necessary +* Avoid the `auto` type. It makes code more difficult to read +* Do not use `printf` or `cout` for debug messages, use LogManager instead +* Don't define namespaces unless necessary + +### Non-functional Style + +* Set your editor's tab settings to insert spaces, using 4 spaces per tab stop +* Always put opening and closing braces `{`, `}` on their own lines +* Indent code inside the braces, do not indent the braces themselves +* Capitalize hexadecimal literals (`0xFF` not `0xff`) except in the udev rules file (which must be lowercase to work) +* No space between keyword and open parenthesis (`if(TRUE)` not `if (TRUE)`). There are some instances of this that haven't been cleaned up in the code. +* Generally, `snake_case` is used for variable names and `CamelCase` is used for function and class names +* Add a header comment to new files with the filename, description, original author, date, and SPDX license identifier. You can copy one from an existing file to preserve formatting. +* Line up `=` on a tab stop when doing a lot of assignments together +* Use this comment box style when adding comments: + * Try to start the comment box on the same column as the code directly beneath it and end on column 61 if possible. See the example below: + +``` +/*---------------------------------------------------------*\ +| This is a comment | +\*---------------------------------------------------------*/ +``` + +## Translating + +Translation files are located in [`OpenRGB/qt/i18n/`](https://gitlab.com/CalcProgrammer1/OpenRGB/-/tree/master/qt/i18n), where languages are formatted using ISO 639-1 format: `OpenRGB_xx_XX.ts` — `xx_XX` representing the language code. +In order to translate a file, you need to [fork](https://gitlab.com/CalcProgrammer1/OpenRGB/-/forks/new) the project, create a new file for your language with `lupdate` (or edit an exisiting one), edit the file with `qtlinguist`, commit, push, and create a merge request. + +## AI Guidelines + +OpenRGB is an open source project developed by humans for humans. As a general rule, AI generated submissions are not permitted. The licensing behind AI generated code is problematic. If you choose to use AI for assistance in your development process, we can't stop you, but do not credit the AI in commits (no AI authorship/co-authorship). Ultimately, you as a human developer are responsible for the code you submit and it is expected that any code you submit you fully understand and have manually vetted before submission. Merge requests that appear to be straight from an AI output, commits with AI tool authorship or co-authorship tags, or otherwise AI generated submissions are subject to closure. diff --git a/Colors.h b/Colors.h new file mode 100644 index 0000000..a7942f8 --- /dev/null +++ b/Colors.h @@ -0,0 +1,162 @@ +/*---------------------------------------------------------*\ +| Colors.h | +| | +| List of named color constants | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#ifndef COLORS_H +#define COLORS_H + +#define COLOR_BLACK 0x000000 +#define COLOR_NAVY 0x000080 +#define COLOR_DARKBLUE 0x00008b +#define COLOR_MEDIUMBLUE 0x0000cd +#define COLOR_BLUE 0x0000ff +#define COLOR_DARKGREEN 0x006400 +#define COLOR_GREEN 0x008000 +#define COLOR_TEAL 0x008080 +#define COLOR_DARKCYAN 0x008b8b +#define COLOR_DEEPSKYBLUE 0x00bfff +#define COLOR_DARKTURQUOISE 0x00ced1 +#define COLOR_MEDIUMSPRINGGREEN 0x00fa9a +#define COLOR_LIME 0x00ff00 +#define COLOR_SPRINGGREEN 0x00ff7f +#define COLOR_AQUA 0x00ffff +#define COLOR_CYAN 0x00ffff +#define COLOR_MIDNIGHTBLUE 0x191970 +#define COLOR_DODGERBLUE 0x1e90ff +#define COLOR_LIGHTSEAGREEN 0x20b2aa +#define COLOR_FORESTGREEN 0x228b22 +#define COLOR_SEAGREEN 0x2e8b57 +#define COLOR_DARKSLATEGRAY 0x2f4f4f +#define COLOR_DARKSLATEGREY 0x2f4f4f +#define COLOR_LIMEGREEN 0x32cd32 +#define COLOR_MEDIUMSEAGREEN 0x3cb371 +#define COLOR_TURQUOISE 0x40e0d0 +#define COLOR_ROYALBLUE 0x4169e1 +#define COLOR_STEELBLUE 0x4682b4 +#define COLOR_DARKSLATEBLUE 0x483d8b +#define COLOR_MEDIUMTURQUOISE 0x48d1cc +#define COLOR_INDIGO 0x4b0082 +#define COLOR_DARKOLIVEGREEN 0x556b2f +#define COLOR_CADETBLUE 0x5f9ea0 +#define COLOR_CORNFLOWERBLUE 0x6495ed +#define COLOR_MEDIUMAQUAMARINE 0x66cdaa +#define COLOR_DIMGRAY 0x696969 +#define COLOR_DIMGREY 0x696969 +#define COLOR_SLATEBLUE 0x6a5acd +#define COLOR_OLIVEDRAB 0x6b8e23 +#define COLOR_SLATEGRAY 0x708090 +#define COLOR_SLATEGREY 0x708090 +#define COLOR_LIGHTSLATEGRAY 0x778899 +#define COLOR_LIGHTSLATEGREY 0x778899 +#define COLOR_MEDIUMSLATEBLUE 0x7b68ee +#define COLOR_LAWNGREEN 0x7cfc00 +#define COLOR_CHARTREUSE 0x7fff00 +#define COLOR_AQUAMARINE 0x7fffd4 +#define COLOR_MAROON 0x800000 +#define COLOR_PURPLE 0x800080 +#define COLOR_ELECTRIC_ULTRAMARINE 0x4000FF +#define COLOR_OLIVE 0x808000 +#define COLOR_GRAY 0x808080 +#define COLOR_GREY 0x808080 +#define COLOR_SKYBLUE 0x87ceeb +#define COLOR_LIGHTSKYBLUE 0x87cefa +#define COLOR_BLUEVIOLET 0x8a2be2 +#define COLOR_DARKRED 0x8b0000 +#define COLOR_DARKMAGENTA 0x8b008b +#define COLOR_SADDLEBROWN 0x8b4513 +#define COLOR_DARKSEAGREEN 0x8fbc8f +#define COLOR_LIGHTGREEN 0x90ee90 +#define COLOR_MEDIUMPURPLE 0x9370db +#define COLOR_DARKVIOLET 0x9400d3 +#define COLOR_PALEGREEN 0x98fb98 +#define COLOR_DARKORCHID 0x9932cc +#define COLOR_YELLOWGREEN 0x9acd32 +#define COLOR_SIENNA 0xa0522d +#define COLOR_BROWN 0xa52a2a +#define COLOR_DARKGRAY 0xa9a9a9 +#define COLOR_DARKGREY 0xa9a9a9 +#define COLOR_LIGHTBLUE 0xadd8e6 +#define COLOR_GREENYELLOW 0xadff2f +#define COLOR_PALETURQUOISE 0xafeeee +#define COLOR_LIGHTSTEELBLUE 0xb0c4de +#define COLOR_POWDERBLUE 0xb0e0e6 +#define COLOR_FIREBRICK 0xb22222 +#define COLOR_DARKGOLDENROD 0xb8860b +#define COLOR_MEDIUMORCHID 0xba55d3 +#define COLOR_ROSYBROWN 0xbc8f8f +#define COLOR_DARKKHAKI 0xbdb76b +#define COLOR_SILVER 0xc0c0c0 +#define COLOR_MEDIUMVIOLETRED 0xc71585 +#define COLOR_INDIANRED 0xcd5c5c +#define COLOR_PERU 0xcd853f +#define COLOR_CHOCOLATE 0xd2691e +#define COLOR_TAN 0xd2b48c +#define COLOR_LIGHTGRAY 0xd3d3d3 +#define COLOR_LIGHTGREY 0xd3d3d3 +#define COLOR_THISTLE 0xd8bfd8 +#define COLOR_ORCHID 0xda70d6 +#define COLOR_GOLDENROD 0xdaa520 +#define COLOR_PALEVIOLETRED 0xdb7093 +#define COLOR_CRIMSON 0xdc143c +#define COLOR_GAINSBORO 0xdcdcdc +#define COLOR_PLUM 0xdda0dd +#define COLOR_BURLYWOOD 0xdeb887 +#define COLOR_LIGHTCYAN 0xe0ffff +#define COLOR_LAVENDER 0xe6e6fa +#define COLOR_DARKSALMON 0xe9967a +#define COLOR_VIOLET 0xee82ee +#define COLOR_PALEGOLDENROD 0xeee8aa +#define COLOR_LIGHTCORAL 0xf08080 +#define COLOR_KHAKI 0xf0e68c +#define COLOR_ALICEBLUE 0xf0f8ff +#define COLOR_HONEYDEW 0xf0fff0 +#define COLOR_AZURE 0xf0ffff +#define COLOR_SANDYBROWN 0xf4a460 +#define COLOR_WHEAT 0xf5deb3 +#define COLOR_BEIGE 0xf5f5dc +#define COLOR_WHITESMOKE 0xf5f5f5 +#define COLOR_MINTCREAM 0xf5fffa +#define COLOR_GHOSTWHITE 0xf8f8ff +#define COLOR_SALMON 0xfa8072 +#define COLOR_ANTIQUEWHITE 0xfaebd7 +#define COLOR_LINEN 0xfaf0e6 +#define COLOR_LIGHTGOLDENRODYELLOW 0xfafad2 +#define COLOR_OLDLACE 0xfdf5e6 +#define COLOR_RED 0xff0000 +#define COLOR_FUCHSIA 0xff00ff +#define COLOR_MAGENTA 0xff00ff +#define COLOR_DEEPPINK 0xff1493 +#define COLOR_ORANGERED 0xff4500 +#define COLOR_TOMATO 0xff6347 +#define COLOR_HOTPINK 0xff69b4 +#define COLOR_CORAL 0xff7f50 +#define COLOR_DARKORANGE 0xff8c00 +#define COLOR_LIGHTSALMON 0xffa07a +#define COLOR_ORANGE 0xffa500 +#define COLOR_LIGHTPINK 0xffb6c1 +#define COLOR_PINK 0xffc0cb +#define COLOR_GOLD 0xffd700 +#define COLOR_PEACHPUFF 0xffdab9 +#define COLOR_NAVAJOWHITE 0xffdead +#define COLOR_MOCCASIN 0xffe4b5 +#define COLOR_BISQUE 0xffe4c4 +#define COLOR_MISTYROSE 0xffe4e1 +#define COLOR_BLANCHEDALMOND 0xffebcd +#define COLOR_PAPAYAWHIP 0xffefd5 +#define COLOR_LAVENDERBLUSH 0xfff0f5 +#define COLOR_SEASHELL 0xfff5ee +#define COLOR_CORNSILK 0xfff8dc +#define COLOR_LEMONCHIFFON 0xfffacd +#define COLOR_FLORALWHITE 0xfffaf0 +#define COLOR_SNOW 0xfffafa +#define COLOR_YELLOW 0xffff00 +#define COLOR_LIGHTYELLOW 0xffffe0 +#define COLOR_IVORY 0xfffff0 +#define COLOR_WHITE 0xffffff + +#endif // COLORS_H diff --git a/Controllers/A4TechController/A4Tech_Detector.cpp b/Controllers/A4TechController/A4Tech_Detector.cpp new file mode 100644 index 0000000..ec74a0e --- /dev/null +++ b/Controllers/A4TechController/A4Tech_Detector.cpp @@ -0,0 +1,61 @@ +/*---------------------------------------------------------*\ +| A4TechDetector.cpp | +| | +| Detector for A4Tech Devices | +| | +| Chris M (Dr_No) 30 Jun 2022 | +| Mohammed Julfikar Ali Mahbub (o-julfikar) 01 Apr 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +/*-----------------------------------------------------*\ +| OpenRGB includes | +\*-----------------------------------------------------*/ +#include +#include "Detector.h" + +/*-----------------------------------------------------*\ +| A4 Tech specific includes | +\*-----------------------------------------------------*/ +#include "RGBController_BloodyMouse.h" +#include "RGBController_BloodyB820R.h" + +/*-----------------------------------------------------*\ +| A4 Tech USB vendor ID | +\*-----------------------------------------------------*/ +#define A4_TECH_VID 0x09DA + +void DetectA4TechMouseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + BloodyMouseController* controller = new BloodyMouseController(dev, info->path, info->product_id, name); + RGBController_BloodyMouse* rgb_controller = new RGBController_BloodyMouse(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectBloodyB820R(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + BloodyB820RController* controller = new BloodyB820RController(dev, info->path, name); + RGBController_BloodyB820R* rgb_controller = new RGBController_BloodyB820R(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Bloody W60 Pro", DetectA4TechMouseControllers, A4_TECH_VID, BLOODY_W60_PRO_PID, 2, 0xFF33, 0x0529); +REGISTER_HID_DETECTOR_IPU("Bloody W70 Max", DetectA4TechMouseControllers, A4_TECH_VID, BLOODY_W70_MAX_PID, 2, 0xFF33, 0x0518); +REGISTER_HID_DETECTOR_IPU("Bloody W90 Max", DetectA4TechMouseControllers, A4_TECH_VID, BLOODY_W90_MAX_PID, 2, 0xFF33, 0x053D); +REGISTER_HID_DETECTOR_IPU("Bloody W90 Pro", DetectA4TechMouseControllers, A4_TECH_VID, BLOODY_W90_PRO_PID, 2, 0xFF33, 0x054D); +REGISTER_HID_DETECTOR_IPU("Bloody MP 50RS", DetectA4TechMouseControllers, A4_TECH_VID, BLOODY_MP_50RS_PID, 2, 0xFFF2, 0x6009); +REGISTER_HID_DETECTOR_IPU("Bloody B820R", DetectBloodyB820R, A4_TECH_VID, BLOODY_B820R_PID, 2, 0xFF52, 0x0210); diff --git a/Controllers/A4TechController/BloodyB820RController/BloodyB820RController.cpp b/Controllers/A4TechController/BloodyB820RController/BloodyB820RController.cpp new file mode 100644 index 0000000..811e1cd --- /dev/null +++ b/Controllers/A4TechController/BloodyB820RController/BloodyB820RController.cpp @@ -0,0 +1,110 @@ +/*---------------------------------------------------------*\ +| BloodyB820RController.cpp | +| | +| Driver for A4Tech Bloody B820R Keyboard | +| | +| Mohammed Julfikar Ali Mahbub (o-julfikar) 01 Apr 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "BloodyB820RController.h" +#include "StringUtils.h" + +/*-------------------------------------------------------------------------------------*\ +| The controller for this device should pass a packet of 64 bytes where the subsequent | +| two packets are for RED, GREEN, and BLUE respectively. The first 6 bytes are control | +| or information bytes and colors bytes starts from index 6. Moreover, the first pack- | +| et of each RGB contains color for 58 keys and the rest (104 - 58 == 46) are send on | +| the second packet. | +\*-------------------------------------------------------------------------------------*/ + +BloodyB820RController::BloodyB820RController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + SendControlPacket(BLOODY_B820R_GAIN_CONTROL); +} + +BloodyB820RController::~BloodyB820RController() +{ + SendControlPacket(BLOODY_B820R_RELEASE_CONTROL); + hid_close(dev); +} + +std::string BloodyB820RController::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string BloodyB820RController::GetLocation() +{ + return("HID: " + location); +} + +std::string BloodyB820RController::GetName() +{ + return(name); +} + +void BloodyB820RController::SendControlPacket(uint8_t data) +{ + uint8_t buffer[BLOODY_B820R_PACKET_SIZE] = { 0x07, 0x03, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 }; + + hid_send_feature_report(dev, buffer, BLOODY_B820R_PACKET_SIZE); + + buffer[BLOODY_B820R_MODE_BYTE] = 0; + buffer[BLOODY_B820R_DATA_BYTE] = data; + + hid_send_feature_report(dev, buffer, BLOODY_B820R_PACKET_SIZE); +} + +void BloodyB820RController::SetLEDDirect(std::vector colors) +{ + + uint8_t r1_buffer[BLOODY_B820R_PACKET_SIZE] = { 0x07, 0x03, 0x06, 0x07, 0x00, 0x00 }; + uint8_t r2_buffer[BLOODY_B820R_PACKET_SIZE] = { 0x07, 0x03, 0x06, 0x08, 0x00, 0x00 }; + uint8_t g1_buffer[BLOODY_B820R_PACKET_SIZE] = { 0x07, 0x03, 0x06, 0x09, 0x00, 0x00 }; + uint8_t g2_buffer[BLOODY_B820R_PACKET_SIZE] = { 0x07, 0x03, 0x06, 0x0A, 0x00, 0x00 }; + uint8_t b1_buffer[BLOODY_B820R_PACKET_SIZE] = { 0x07, 0x03, 0x06, 0x0B, 0x00, 0x00 }; + uint8_t b2_buffer[BLOODY_B820R_PACKET_SIZE] = { 0x07, 0x03, 0x06, 0x0C, 0x00, 0x00 }; + + /*-----------------------------------------------------------------*\ + | Set up Direct packet | + | packet_map is the index of the Key from full_matrix_map and | + | the value is the position in the direct packet buffer | + \*-----------------------------------------------------------------*/ + for(size_t i = 0; i < colors.size(); i++) + { + RGBColor color = colors[i]; + uint8_t offset = BLOODY_B820R_RGB_OFFSET; + uint8_t buffer_idx = offset + i % BLOODY_B820R_RGB_BUFFER_SIZE; + uint8_t* buffers[2][3] = + { + {r1_buffer, g1_buffer, b1_buffer}, + {r2_buffer, g2_buffer, b2_buffer} + }; + + buffers[i >= BLOODY_B820R_RGB_BUFFER_SIZE][0][buffer_idx] = RGBGetRValue(color); + buffers[i >= BLOODY_B820R_RGB_BUFFER_SIZE][1][buffer_idx] = RGBGetGValue(color); + buffers[i >= BLOODY_B820R_RGB_BUFFER_SIZE][2][buffer_idx] = RGBGetBValue(color); + } + + hid_send_feature_report(dev, r1_buffer, BLOODY_B820R_PACKET_SIZE); + hid_send_feature_report(dev, r2_buffer, BLOODY_B820R_PACKET_SIZE); + hid_send_feature_report(dev, g1_buffer, BLOODY_B820R_PACKET_SIZE); + hid_send_feature_report(dev, g2_buffer, BLOODY_B820R_PACKET_SIZE); + hid_send_feature_report(dev, b1_buffer, BLOODY_B820R_PACKET_SIZE); + hid_send_feature_report(dev, b2_buffer, BLOODY_B820R_PACKET_SIZE); +} diff --git a/Controllers/A4TechController/BloodyB820RController/BloodyB820RController.h b/Controllers/A4TechController/BloodyB820RController/BloodyB820RController.h new file mode 100644 index 0000000..f971d39 --- /dev/null +++ b/Controllers/A4TechController/BloodyB820RController/BloodyB820RController.h @@ -0,0 +1,55 @@ +/*---------------------------------------------------------*\ +| BloodyB820RController.h | +| | +| Driver for A4Tech Bloody B820R Keyboard | +| | +| Mohammed Julfikar Ali Mahbub (o-julfikar) 01 Apr 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define HID_MAX_STR 255 + +#define BLOODY_B820R_RGB_BUFFER_SIZE 58 +#define BLOODY_B820R_RGB_OFFSET 6 +#define BLOODY_B820R_PACKET_SIZE 64 +#define BLOODY_B820R_KEYCOUNT 104 +#define BLOODY_B820R_MODE_BYTE 3 +#define BLOODY_B820R_DATA_BYTE 8 +#define BLOODY_B820R_GAIN_CONTROL 0x01 +#define BLOODY_B820R_RELEASE_CONTROL 0x00 + +/*---------------------------------------------------------*\ +| Bloody B820R product ID | +\*---------------------------------------------------------*/ +#define BLOODY_B820R_PID 0xFA10 + +enum +{ + BLOODY_B820R_MODE_DIRECT = 0x01, // Direct LED control - Independently set LEDs in zone +}; + +class BloodyB820RController +{ +public: + BloodyB820RController(hid_device* dev_handle, const char* path, std::string dev_name); + ~BloodyB820RController(); + + std::string GetSerial(); + std::string GetLocation(); + std::string GetName(); + + void SetLEDDirect(std::vector colors); + void SendControlPacket(uint8_t data); +private: + std::string location; + std::string name; + hid_device* dev; +}; diff --git a/Controllers/A4TechController/BloodyB820RController/RGBController_BloodyB820R.cpp b/Controllers/A4TechController/BloodyB820RController/RGBController_BloodyB820R.cpp new file mode 100644 index 0000000..58ffacc --- /dev/null +++ b/Controllers/A4TechController/BloodyB820RController/RGBController_BloodyB820R.cpp @@ -0,0 +1,253 @@ +/*---------------------------------------------------------*\ +| RGBController_BloodyB820R.cpp | +| | +| RGBController for A4Tech Bloody B820R Keyboard | +| | +| Mohammed Julfikar Ali Mahbub (o-julfikar) 01 Apr 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_BloodyB820R.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[6][21] = + { + {0, NA, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, NA, NA, NA, NA, 13, 14, 15}, + {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36}, + {37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57}, + {58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, NA, NA, NA, NA, 71, 72, 73, NA}, + {74, NA, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, NA, NA, 86, NA, 87, 88, 89, 90}, + {91, 92, 93, 94, NA, NA, NA, NA, NA, NA, 95, 96, 97, 98, 99, 100, 101, 102, NA, 103, NA}, + }; + +static const char *led_names[] = + { + KEY_EN_ESCAPE, //00 + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAUSE_BREAK, + + KEY_EN_BACK_TICK, //10 + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_HOME, + KEY_EN_PAGE_UP, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, + + KEY_EN_TAB, //20 + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_PLUS, + + KEY_EN_CAPS_LOCK, //30 + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + + KEY_EN_LEFT_SHIFT, //40 + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_ENTER, + + KEY_EN_LEFT_CONTROL, //50 + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_MENU, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD + }; + +/**------------------------------------------------------------------*\ + @name A4Tech Bloody B820R + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectBloodyB820R + @comment The A4Tech Bloody B820R keyboard controller currently + supports the full size (ANSI layout). +\*-------------------------------------------------------------------*/ + +RGBController_BloodyB820R::RGBController_BloodyB820R(BloodyB820RController *controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "A4Tech"; + type = DEVICE_TYPE_KEYBOARD; + description = "A4Tech Bloody Keyboard"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = BLOODY_B820R_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_BloodyB820R::~RGBController_BloodyB820R() +{ + delete controller; +} + +void RGBController_BloodyB820R::SetupZones() +{ + /*-------------------------------------------------*\ + | Create the Keyboard zone and add the matrix map | + \*-------------------------------------------------*/ + zone KB_zone; + KB_zone.name = ZONE_EN_KEYBOARD; + KB_zone.type = ZONE_TYPE_MATRIX; + KB_zone.leds_min = BLOODY_B820R_KEYCOUNT; + KB_zone.leds_max = BLOODY_B820R_KEYCOUNT; + KB_zone.leds_count = BLOODY_B820R_KEYCOUNT; + + KB_zone.matrix_map = new matrix_map_type; + KB_zone.matrix_map->height = 6; + KB_zone.matrix_map->width = 21; + KB_zone.matrix_map->map = (unsigned int *)&matrix_map; + zones.push_back(KB_zone); + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(unsigned int led_index = 0; led_index < BLOODY_B820R_KEYCOUNT; led_index++) + { + led new_led; + new_led.name = led_names[led_index]; + new_led.value = led_index; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_BloodyB820R::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + + +void RGBController_BloodyB820R::DeviceUpdateLEDs() +{ + controller->SetLEDDirect(colors); +} + +void RGBController_BloodyB820R::UpdateZoneLEDs(int zone) +{ + std::vector colour; + + for(size_t i = 0; i < zones[zone].leds_count; i++) + { + colour.push_back(zones[zone].colors[i]); + } + + controller->SetLEDDirect(colour); +} + +void RGBController_BloodyB820R::UpdateSingleLED(int led) +{ + std::vector colour; + colour.push_back(colors[led]); + + controller->SetLEDDirect(colour); +} + +void RGBController_BloodyB820R::DeviceUpdateMode() +{ + /* This device does not support modes yet */ +} diff --git a/Controllers/A4TechController/BloodyB820RController/RGBController_BloodyB820R.h b/Controllers/A4TechController/BloodyB820RController/RGBController_BloodyB820R.h new file mode 100644 index 0000000..293c128 --- /dev/null +++ b/Controllers/A4TechController/BloodyB820RController/RGBController_BloodyB820R.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_BloodyB820R.h | +| | +| RGBController for A4Tech Bloody B820R Keyboard | +| | +| Mohammed Julfikar Ali Mahbub (o-julfikar) 01 Apr 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "BloodyB820RController.h" + +class RGBController_BloodyB820R : public RGBController +{ +public: + RGBController_BloodyB820R(BloodyB820RController* controller_ptr); + ~RGBController_BloodyB820R(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + BloodyB820RController* controller; +}; diff --git a/Controllers/A4TechController/BloodyMouseController/BloodyMouseController.cpp b/Controllers/A4TechController/BloodyMouseController/BloodyMouseController.cpp new file mode 100644 index 0000000..e0f56fb --- /dev/null +++ b/Controllers/A4TechController/BloodyMouseController/BloodyMouseController.cpp @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| BloodyMouseController.cpp | +| | +| Driver for A4Tech Bloody Mouse | +| | +| Chris M (Dr_No) 30 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "BloodyMouseController.h" +#include "StringUtils.h" + +BloodyMouseController::BloodyMouseController(hid_device* dev_handle, const char* path, uint16_t product_id, std::string dev_name) +{ + dev = dev_handle; + location = path; + pid = product_id; + name = dev_name; + + InitDevice(); +} + +BloodyMouseController::~BloodyMouseController() +{ + hid_close(dev); +} + +uint16_t BloodyMouseController::GetPid() +{ + return(pid); +} + +std::string BloodyMouseController::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string BloodyMouseController::GetLocation() +{ + return("HID: " + location); +} + +std::string BloodyMouseController::GetName() +{ + return(name); +} + +void BloodyMouseController::InitDevice() +{ + uint8_t buffer[BLOODYMOUSE_WRITE_PACKET_SIZE] = { 0x07, 0x03, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 }; + + hid_send_feature_report(dev, buffer, BLOODYMOUSE_WRITE_PACKET_SIZE); + + buffer[BLOODYMOUSE_MODE_BYTE] = 0; + buffer[BLOODYMOUSE_DATA_BYTE] = 1; + + hid_send_feature_report(dev, buffer, BLOODYMOUSE_WRITE_PACKET_SIZE); +} + +void BloodyMouseController::SetLedsDirect(std::vector colors) +{ + uint8_t buffer[BLOODYMOUSE_WRITE_PACKET_SIZE] = { 0x07, 0x03, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00 }; + + for(uint8_t i = 0; i < colors.size(); i++) + { + uint8_t offset = 3 * (colors[i] >> 24) + BLOODYMOUSE_DATA_BYTE; + + buffer[offset] = RGBGetRValue(colors[i]); + buffer[offset + 1] = RGBGetGValue(colors[i]); + buffer[offset + 2] = RGBGetBValue(colors[i]); + } + + hid_send_feature_report(dev, buffer, BLOODYMOUSE_WRITE_PACKET_SIZE); +} diff --git a/Controllers/A4TechController/BloodyMouseController/BloodyMouseController.h b/Controllers/A4TechController/BloodyMouseController/BloodyMouseController.h new file mode 100644 index 0000000..a3b6e7a --- /dev/null +++ b/Controllers/A4TechController/BloodyMouseController/BloodyMouseController.h @@ -0,0 +1,70 @@ +/*---------------------------------------------------------*\ +| BloodyMouseController.h | +| | +| Driver for A4Tech Bloody Mouse | +| | +| Chris M (Dr_No) 30 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*-----------------------------------------------------*\ +| Mouse product IDs | +\*-----------------------------------------------------*/ +#define BLOODY_W60_PRO_PID 0x37EA +#define BLOODY_W70_MAX_PID 0x79EF +#define BLOODY_W90_MAX_PID 0x3666 +#define BLOODY_W90_PRO_PID 0x39B6 + +/*-----------------------------------------------------*\ +| Mousemat product IDs | +\*-----------------------------------------------------*/ +#define BLOODY_MP_50RS_PID 0xFA60 + +#define HID_MAX_STR 255 +#define BLOODYMOUSE_WRITE_PACKET_SIZE 64 + +#define BLOODYMOUSE_BRIGHTNESS_MIN 0 +#define BLOODYMOUSE_BRIGHTNESS_MAX 255 + +enum +{ + BLOODYMOUSE_MODE_DIRECT = 0x01, //Direct Led Control - Independently set LEDs in zone +}; + +enum +{ + BLOODYMOUSE_REPORT_BYTE = 1, + BLOODYMOUSE_COMMAND_BYTE = 2, + BLOODYMOUSE_MODE_BYTE = 3, + BLOODYMOUSE_DATA_BYTE = 8, +}; + +class BloodyMouseController +{ +public: + BloodyMouseController(hid_device* dev_handle, const char* path, uint16_t product_id, std::string dev_name); + ~BloodyMouseController(); + + uint16_t GetPid(); + std::string GetSerial(); + std::string GetLocation(); + std::string GetName(); + + void SetLedsDirect(std::vector colors); + +private: + uint16_t pid; + std::string location; + std::string name; + hid_device* dev; + + void InitDevice(); +}; diff --git a/Controllers/A4TechController/BloodyMouseController/RGBController_BloodyMouse.cpp b/Controllers/A4TechController/BloodyMouseController/RGBController_BloodyMouse.cpp new file mode 100644 index 0000000..a739c8f --- /dev/null +++ b/Controllers/A4TechController/BloodyMouseController/RGBController_BloodyMouse.cpp @@ -0,0 +1,197 @@ +/*---------------------------------------------------------*\ +| RGBController_BloodyMouse.cpp | +| | +| RGBController for A4Tech Bloody Mouse | +| | +| Chris M (Dr_No) 30 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_BloodyMouse.h" + +static const mouse_layout w60_pro +{ + { + "Scroll Wheel", { 14 } + }, + { + "Mid Line", { 0, 1, 2, 9, 3, 4, 5, 6 } + }, + { + "Logo", { 10 } + }, + { + "Rear", { 8, 7, 13, 12, 11 } + } +}; + +static const mouse_layout w90_max +{ + { + "Scroll Wheel", { 14 } + }, + { + "Logo", { 7 } + }, + { + "Rear", { 13, 12, 11, 10, 9, 8, 6, 5, 4, 3, 2, 1, 0 } + } +}; + +static const mouse_layout mp_50rs +{ + { + "Mouse Pad", { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + } +}; + +/**------------------------------------------------------------------*\ + @name BloodyMouse + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectA4TechMouseControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_BloodyMouse::RGBController_BloodyMouse(BloodyMouseController *controller_ptr) +{ + controller = controller_ptr; + + switch(controller->GetPid()) + { + case BLOODY_MP_50RS_PID: + type = DEVICE_TYPE_MOUSEMAT; + break; + default: + type = DEVICE_TYPE_MOUSE; + } + + name = controller->GetName(); + vendor = "Bloody"; + description = "Controller compatible with the Bloody W60 Pro and MP 50RS"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = BLOODYMOUSE_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_BloodyMouse::~RGBController_BloodyMouse() +{ + delete controller; +} + +void RGBController_BloodyMouse::SetupZones() +{ + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*-------------------------------------------------*\ + | Select layout from PID | + \*-------------------------------------------------*/ + mouse_layout layout; + + switch(controller->GetPid()) + { + case BLOODY_W60_PRO_PID: + layout = w60_pro; + break; + case BLOODY_W70_MAX_PID: + case BLOODY_W90_MAX_PID: + case BLOODY_W90_PRO_PID: + layout = w90_max; + break; + case BLOODY_MP_50RS_PID: + layout = mp_50rs; + break; + } + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(uint8_t zone_idx = 0; zone_idx < layout.size(); zone_idx++) + { + mouse_zone mz = layout[zone_idx]; + bool bool_single = mz.zone_leds.size() == 1; + + zone new_zone; + new_zone.name = mz.name; + new_zone.leds_min = (unsigned int)mz.zone_leds.size(); + new_zone.leds_max = new_zone.leds_min; + new_zone.leds_count = new_zone.leds_min; + new_zone.type = bool_single ? ZONE_TYPE_SINGLE : ZONE_TYPE_LINEAR; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + for(unsigned int lp_idx = 0; lp_idx < zones[zone_idx].leds_count; lp_idx++) + { + led new_led; + + new_led.value = mz.zone_leds[lp_idx]; + + if(bool_single) + { + new_led.name = mz.name + " LED"; + } + else + { + new_led.name = mz.name; + new_led.name.append(" LED " + std::to_string(lp_idx)); + } + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_BloodyMouse::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_BloodyMouse::DeviceUpdateLEDs() +{ + std::vector colour; + for(size_t i = 0; i < colors.size(); i++) + { + RGBColor c = colors[i] | (leds[i].value << 24); + colour.push_back(c); + } + + controller->SetLedsDirect(colour); +} + +void RGBController_BloodyMouse::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_BloodyMouse::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_BloodyMouse::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | This device only supports Direct mode | + \*---------------------------------------------------------*/ +} diff --git a/Controllers/A4TechController/BloodyMouseController/RGBController_BloodyMouse.h b/Controllers/A4TechController/BloodyMouseController/RGBController_BloodyMouse.h new file mode 100644 index 0000000..58cd6c0 --- /dev/null +++ b/Controllers/A4TechController/BloodyMouseController/RGBController_BloodyMouse.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_BloodyMouse.h | +| | +| RGBController for A4Tech Bloody Mouse | +| | +| Chris M (Dr_No) 30 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "BloodyMouseController.h" + +struct mouse_zone +{ + std::string name; + std::vector zone_leds; +}; + +typedef std::vector mouse_layout; + +class RGBController_BloodyMouse : public RGBController +{ +public: + RGBController_BloodyMouse(BloodyMouseController* controller_ptr); + ~RGBController_BloodyMouse(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + BloodyMouseController* controller; +}; diff --git a/Controllers/AMBXController/AMBXController.cpp b/Controllers/AMBXController/AMBXController.cpp new file mode 100644 index 0000000..e630583 --- /dev/null +++ b/Controllers/AMBXController/AMBXController.cpp @@ -0,0 +1,195 @@ +/*---------------------------------------------------------*\ +| AMBXController.cpp | +| | +| Driver for Philips amBX Gaming lights | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "AMBXController.h" +#include "LogManager.h" +#include "StringUtils.h" +#include +#include +#include + +AMBXController::AMBXController(const char* path) +{ + initialized = false; + usb_context = nullptr; + dev_handle = nullptr; + + location = "USB: "; + location += path; + + // Initialize libusb in this instance + if(libusb_init(&usb_context) < 0) + { + return; + } + + // Get the device list + libusb_device** device_list; + ssize_t device_count = libusb_get_device_list(usb_context, &device_list); + + if(device_count < 0) + { + return; + } + + for(ssize_t i = 0; i < device_count; i++) + { + libusb_device* device = device_list[i]; + struct libusb_device_descriptor desc; + + if(libusb_get_device_descriptor(device, &desc) != LIBUSB_SUCCESS) + { + continue; + } + + if(desc.idVendor == AMBX_VID && desc.idProduct == AMBX_PID) + { + uint8_t bus = libusb_get_bus_number(device); + uint8_t address = libusb_get_device_address(device); + + char current_path[32]; + snprintf(current_path, sizeof(current_path), "%d-%d", bus, address); + + if(strcmp(path, current_path) == 0) + { + // Try to open this device + if(libusb_open(device, &dev_handle) != LIBUSB_SUCCESS) + { + continue; + } + + // Try to detach the kernel driver if attached + if(libusb_kernel_driver_active(dev_handle, 0)) + { + libusb_detach_kernel_driver(dev_handle, 0); + } + + // Set auto-detach for Windows compatibility + libusb_set_auto_detach_kernel_driver(dev_handle, 1); + + // Claim the interface + if(libusb_claim_interface(dev_handle, 0) != LIBUSB_SUCCESS) + { + libusb_close(dev_handle); + dev_handle = nullptr; + continue; + } + + // Get string descriptor for serial number if available + if(desc.iSerialNumber != 0) + { + unsigned char serial_str[256]; + int serial_result = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, + serial_str, sizeof(serial_str)); + if(serial_result > 0) + { + serial = std::string(reinterpret_cast(serial_str), serial_result); + } + } + + // Successfully opened and claimed the device + initialized = true; + break; + } + } + } + + libusb_free_device_list(device_list, 1); +} + +AMBXController::~AMBXController() +{ + if(initialized) + { + // Turn off all lights before closing + unsigned int led_ids[5] = + { + AMBX_LIGHT_LEFT, + AMBX_LIGHT_RIGHT, + AMBX_LIGHT_WALL_LEFT, + AMBX_LIGHT_WALL_CENTER, + AMBX_LIGHT_WALL_RIGHT + }; + + RGBColor colors[5] = { 0, 0, 0, 0, 0 }; + SetLEDColors(led_ids, colors, 5); + } + + if(dev_handle != nullptr) + { + // Release the interface + libusb_release_interface(dev_handle, 0); + + // Close the device + libusb_close(dev_handle); + dev_handle = nullptr; + } + + if(usb_context != nullptr) + { + libusb_exit(usb_context); + usb_context = nullptr; + } +} + +std::string AMBXController::GetDeviceLocation() +{ + return location; +} + +std::string AMBXController::GetSerialString() +{ + return serial; +} + +bool AMBXController::IsInitialized() +{ + return initialized; +} + +void AMBXController::SendPacket(unsigned char* packet, unsigned int size) +{ + if(!initialized || dev_handle == nullptr) + { + return; + } + + int actual_length = 0; + libusb_interrupt_transfer(dev_handle, AMBX_ENDPOINT_OUT, packet, size, &actual_length, 100); +} + +void AMBXController::SetLEDColor(unsigned int led, RGBColor color) +{ + if(!initialized) + { + return; + } + + unsigned char color_buf[6] = + { + AMBX_PACKET_HEADER, + static_cast(led), + AMBX_SET_COLOR, + (unsigned char)RGBGetRValue(color), + (unsigned char)RGBGetGValue(color), + (unsigned char)RGBGetBValue(color) + }; + + SendPacket(color_buf, 6); + + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void AMBXController::SetLEDColors(unsigned int* leds, RGBColor* colors, unsigned int count) +{ + for(unsigned int i = 0; i < count; i++) + { + SetLEDColor(leds[i], colors[i]); + } +} diff --git a/Controllers/AMBXController/AMBXController.h b/Controllers/AMBXController/AMBXController.h new file mode 100644 index 0000000..a186b6c --- /dev/null +++ b/Controllers/AMBXController/AMBXController.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------*\ +| AMBXController.h | +| | +| Driver for Philips amBX Gaming lights | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include + +#ifdef _WIN32 +#include "dependencies/libusb-1.0.27/include/libusb.h" +#else +#include +#endif + +#define AMBX_VID 0x0471 +#define AMBX_PID 0x083F +#define AMBX_ENDPOINT_OUT 0x02 +#define AMBX_PACKET_HEADER 0xA1 +#define AMBX_SET_COLOR 0x03 + +enum +{ + AMBX_LIGHT_LEFT = 0x0B, + AMBX_LIGHT_RIGHT = 0x1B, + AMBX_LIGHT_WALL_LEFT = 0x2B, + AMBX_LIGHT_WALL_CENTER = 0x3B, + AMBX_LIGHT_WALL_RIGHT = 0x4B +}; + +class AMBXController +{ +public: + AMBXController(const char* path); + ~AMBXController(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + + bool IsInitialized(); + void SetLEDColor(unsigned int led, RGBColor color); + void SetLEDColors(unsigned int* leds, RGBColor* colors, unsigned int count); + +private: + libusb_context* usb_context; + libusb_device_handle* dev_handle; + std::string location; + std::string serial; + bool initialized; + + void SendPacket(unsigned char* packet, unsigned int size); +}; diff --git a/Controllers/AMBXController/AMBXControllerDetect.cpp b/Controllers/AMBXController/AMBXControllerDetect.cpp new file mode 100644 index 0000000..af2a255 --- /dev/null +++ b/Controllers/AMBXController/AMBXControllerDetect.cpp @@ -0,0 +1,83 @@ +/*---------------------------------------------------------*\ +| AMBXControllerDetect.cpp | +| | +| Detector for Philips amBX Gaming lights | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AMBXController.h" +#include "RGBController.h" +#include "RGBController_AMBX.h" + +#ifdef _WIN32 +#include "dependencies/libusb-1.0.27/include/libusb.h" +#else +#include +#endif + +/******************************************************************************************\ +* * +* DetectAMBXControllers * +* * +* Detect Philips amBX Gaming devices * +* * +\******************************************************************************************/ + +void DetectAMBXControllers() +{ + libusb_context* ctx = NULL; + + if(libusb_init(&ctx) < 0) + { + return; + } + + libusb_device** devs; + ssize_t num_devs = libusb_get_device_list(ctx, &devs); + + if(num_devs <= 0) + { + libusb_exit(ctx); + return; + } + + for(ssize_t i = 0; i < num_devs; i++) + { + libusb_device* dev = devs[i]; + libusb_device_descriptor desc; + + if(libusb_get_device_descriptor(dev, &desc) != 0) + { + continue; + } + + if(desc.idVendor == AMBX_VID && desc.idProduct == AMBX_PID) + { + uint8_t bus = libusb_get_bus_number(dev); + uint8_t address = libusb_get_device_address(dev); + char device_path[32]; + snprintf(device_path, sizeof(device_path), "%d-%d", bus, address); + + // Use the AMBXController to handle opening and initializing + AMBXController* controller = new AMBXController(device_path); + + if(controller->IsInitialized()) + { + RGBController_AMBX* rgb_controller = new RGBController_AMBX(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + delete controller; + } + } + } + + libusb_free_device_list(devs, 1); + libusb_exit(ctx); +} + +REGISTER_DETECTOR("Philips amBX", DetectAMBXControllers); diff --git a/Controllers/AMBXController/RGBController_AMBX.cpp b/Controllers/AMBXController/RGBController_AMBX.cpp new file mode 100644 index 0000000..49feec5 --- /dev/null +++ b/Controllers/AMBXController/RGBController_AMBX.cpp @@ -0,0 +1,179 @@ +/*---------------------------------------------------------*\ +| RGBController_AMBX.cpp | +| | +| RGB Controller for Philips amBX Gaming lights | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AMBX.h" + +/**------------------------------------------------------------------*\ + @name Philips amBX + @category Accessory + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectAMBXControllers + @comment The Philips amBX Gaming lights system includes left and right + lights and a wall-washer bar with three zones. +\*-------------------------------------------------------------------*/ + +RGBController_AMBX::RGBController_AMBX(AMBXController* controller_ptr) +{ + controller = controller_ptr; + + name = "Philips amBX"; + vendor = "Philips"; + type = DEVICE_TYPE_ACCESSORY; + description = "Philips amBX Gaming Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_AMBX::~RGBController_AMBX() +{ + delete controller; +} + +void RGBController_AMBX::SetupZones() +{ + // Set up zones + zone side_lights_zone; + side_lights_zone.name = "Side Lights"; + side_lights_zone.type = ZONE_TYPE_LINEAR; + side_lights_zone.leds_min = 2; + side_lights_zone.leds_max = 2; + side_lights_zone.leds_count = 2; + side_lights_zone.matrix_map = NULL; + zones.push_back(side_lights_zone); + + zone wallwasher_zone; + wallwasher_zone.name = "Wallwasher"; + wallwasher_zone.type = ZONE_TYPE_LINEAR; + wallwasher_zone.leds_min = 3; + wallwasher_zone.leds_max = 3; + wallwasher_zone.leds_count = 3; + wallwasher_zone.matrix_map = NULL; + zones.push_back(wallwasher_zone); + + // Set up LEDs + led left_light; + left_light.name = "Left"; + left_light.value = AMBX_LIGHT_LEFT; + leds.push_back(left_light); + + led right_light; + right_light.name = "Right"; + right_light.value = AMBX_LIGHT_RIGHT; + leds.push_back(right_light); + + led wall_left; + wall_left.name = "Wall Left"; + wall_left.value = AMBX_LIGHT_WALL_LEFT; + leds.push_back(wall_left); + + led wall_center; + wall_center.name = "Wall Center"; + wall_center.value = AMBX_LIGHT_WALL_CENTER; + leds.push_back(wall_center); + + led wall_right; + wall_right.name = "Wall Right"; + wall_right.value = AMBX_LIGHT_WALL_RIGHT; + leds.push_back(wall_right); + + SetupColors(); +} + +void RGBController_AMBX::ResizeZone(int /*zone*/, int /*new_size*/) +{ + // This device does not support resizing zones +} + +void RGBController_AMBX::DeviceUpdateLEDs() +{ + if(!controller->IsInitialized()) + { + return; + } + + unsigned int led_values[5]; + RGBColor led_colors[5]; + + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + led_values[led_idx] = leds[led_idx].value; + led_colors[led_idx] = colors[led_idx]; + } + + controller->SetLEDColors(led_values, led_colors, static_cast(leds.size())); +} + +void RGBController_AMBX::UpdateZoneLEDs(int zone) +{ + if(!controller->IsInitialized()) + { + return; + } + + unsigned int start_idx = 0; + unsigned int zone_size = 0; + + // Calculate start index and size + for(unsigned int z_idx = 0; z_idx < zones.size(); z_idx++) + { + if(z_idx == (unsigned int)zone) + { + zone_size = zones[z_idx].leds_count; + break; + } + + start_idx += zones[z_idx].leds_count; + } + + unsigned int led_values[5]; + RGBColor led_colors[5]; + + for(unsigned int led_idx = 0; led_idx < zone_size; led_idx++) + { + unsigned int current_idx = start_idx + led_idx; + led_values[led_idx] = leds[current_idx].value; + led_colors[led_idx] = colors[current_idx]; + } + + controller->SetLEDColors(led_values, led_colors, zone_size); +} + +void RGBController_AMBX::UpdateSingleLED(int led) +{ + if(!controller->IsInitialized()) + { + return; + } + + unsigned int led_value = leds[led].value; + RGBColor color = colors[led]; + controller->SetLEDColor(led_value, color); +} + +void RGBController_AMBX::DeviceUpdateMode() +{ + if(!controller->IsInitialized()) + { + return; + } + + DeviceUpdateLEDs(); +} diff --git a/Controllers/AMBXController/RGBController_AMBX.h b/Controllers/AMBXController/RGBController_AMBX.h new file mode 100644 index 0000000..1f96448 --- /dev/null +++ b/Controllers/AMBXController/RGBController_AMBX.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| RGBController_AMBX.h | +| | +| RGB Controller for Philips amBX Gaming lights | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AMBXController.h" + +class RGBController_AMBX : public RGBController +{ +public: + RGBController_AMBX(AMBXController* controller_ptr); + ~RGBController_AMBX(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AMBXController* controller; +}; diff --git a/Controllers/AMDWraithPrismController/AMDWraithPrismController.cpp b/Controllers/AMDWraithPrismController/AMDWraithPrismController.cpp new file mode 100644 index 0000000..f199b2c --- /dev/null +++ b/Controllers/AMDWraithPrismController/AMDWraithPrismController.cpp @@ -0,0 +1,374 @@ +/*---------------------------------------------------------*\ +| AMDWraithPrismController.cpp | +| | +| Driver for AMD Wraith Prism | +| | +| Adam Honse (CalcProgrammer1) 06 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "AMDWraithPrismController.h" +#include "StringUtils.h" + +AMDWraithPrismController::AMDWraithPrismController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + current_fan_mode = AMD_WRAITH_PRISM_FAN_LOGO_MODE_STATIC; + current_fan_speed = 0xFF; + current_fan_random_color = false; + current_fan_brightness = 0xFF; + + current_logo_mode = AMD_WRAITH_PRISM_FAN_LOGO_MODE_STATIC; + current_logo_speed = 0xFF; + current_logo_random_color = false; + current_logo_brightness = 0xFF; + + current_ring_mode = AMD_WRAITH_PRISM_EFFECT_CHANNEL_STATIC; + current_ring_speed = 0xFF; + current_ring_direction = false; + current_ring_brightness = 0xFF; +} + +AMDWraithPrismController::~AMDWraithPrismController() +{ + hid_close(dev); +} + +std::string AMDWraithPrismController::GetLocationString() +{ + return("HID: " + location); +} + +std::string AMDWraithPrismController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string AMDWraithPrismController::GetFirmwareVersionString() +{ + std::string ret_string = ""; + + unsigned char usb_buf[] = + { + 0x00, + 0x12, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + unsigned char fw_buf[16] = {0x00}; + + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); + + for(int char_idx = 0; char_idx < 16; char_idx+=2) + { + if(usb_buf[char_idx + 0x08] != 0) + { + fw_buf[char_idx / 2] = usb_buf[char_idx + 0x08]; + } + else + { + break; + } + } + + ret_string.append((char *)fw_buf); + + return(ret_string); +} + +void AMDWraithPrismController::SetFanMode(unsigned char mode, unsigned char speed, unsigned char brightness, bool random_color) +{ + current_fan_mode = mode; + current_fan_speed = speed_values_fan_logo[mode][speed]; + current_fan_brightness = brightness; + current_fan_random_color = random_color; +} + +void AMDWraithPrismController::SetFanColor(unsigned char red, unsigned char green, unsigned char blue) +{ + SendEffectChannelUpdate + ( + AMD_WRAITH_PRISM_EFFECT_CHANNEL_FAN_LED, + current_fan_speed, + false, + current_fan_random_color, + current_fan_mode, + current_fan_brightness, + red, + green, + blue + ); +} + +void AMDWraithPrismController::SetLogoMode(unsigned char mode, unsigned char speed, unsigned char brightness, bool random_color) +{ + current_logo_mode = mode; + current_logo_speed = speed_values_fan_logo[mode][speed]; + current_logo_brightness = brightness; + current_logo_random_color = random_color; +} + +void AMDWraithPrismController::SetLogoColor(unsigned char red, unsigned char green, unsigned char blue) +{ + SendEffectChannelUpdate + ( + AMD_WRAITH_PRISM_EFFECT_CHANNEL_LOGO_LED, + current_logo_speed, + false, + current_logo_random_color, + current_logo_mode, + current_logo_brightness, + red, + green, + blue + ); +} + +void AMDWraithPrismController::SetRingMode(unsigned char mode, unsigned char speed, unsigned char brightness, bool direction, bool random_color) +{ + current_ring_mode = mode; + current_ring_speed = speed_values_ring[mode][speed]; + current_ring_direction = direction; + current_ring_brightness = brightness; + current_ring_random_color = random_color; +} + +void AMDWraithPrismController::SetRingColor(unsigned char red, unsigned char green, unsigned char blue) +{ + SetRingEffectChannel(current_ring_mode); + + SendEffectChannelUpdate + ( + current_ring_mode, + current_ring_speed, + current_ring_direction, + current_ring_random_color, + mode_value_ring[current_ring_mode], + current_ring_brightness, + red, + green, + blue + ); + + SendApplyCommand(); +} + +void AMDWraithPrismController::SetRingEffectChannel(unsigned char channel) +{ + SendChannelRemap(channel, AMD_WRAITH_PRISM_EFFECT_CHANNEL_LOGO_LED, AMD_WRAITH_PRISM_EFFECT_CHANNEL_FAN_LED); +} + +void AMDWraithPrismController::SendEnableCommand(bool direct) +{ + unsigned char usb_buf[] = + { + 0x00, + 0x41, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + if(direct) + { + usb_buf[0x02] = 0x03; + } + + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +} + +void AMDWraithPrismController::SendApplyCommand() +{ + unsigned char usb_buf[] = + { + 0x00, + 0x51, 0x28, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +} + +void AMDWraithPrismController::SendDirectPacket + ( + unsigned char size, + unsigned char * led_ids, + RGBColor * colors + ) +{ + unsigned char usb_buf[] = + { + 0x00, + 0xC0, 0x01, size, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + for(unsigned int led_idx = 0; led_idx < size; led_idx++) + { + unsigned int index = led_idx * 4; + + usb_buf[index + 5] = led_ids[led_idx]; + usb_buf[index + 6] = RGBGetRValue(colors[led_idx]); + usb_buf[index + 7] = RGBGetGValue(colors[led_idx]); + usb_buf[index + 8] = RGBGetBValue(colors[led_idx]); + } + + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +} + +void AMDWraithPrismController::SendEffectChannelUpdate + ( + unsigned char effect_channel, + unsigned char speed, + bool direction, + bool random_color, + unsigned char mode, + unsigned char brightness, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[] = + { + 0x00, + 0x51, 0x2C, 0x01, 0x00, + 0x05, 0xFF, 0x00, 0x01, + 0xFF, 0xFF, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF + }; + + usb_buf[0x05] = effect_channel; + usb_buf[0x06] = speed; + usb_buf[0x07] = (direction ? 0x01 : 0x00) | (random_color ? 0x80 : 0x00); + usb_buf[0x08] = mode; + + usb_buf[0x0A] = brightness; + + usb_buf[0x0B] = red; + usb_buf[0x0C] = green; + usb_buf[0x0D] = blue; + + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +} + +void AMDWraithPrismController::SendChannelRemap(unsigned char ring_channel, unsigned char logo_channel, unsigned char fan_channel) +{ + unsigned char usb_buf[] = + { + 0x00, + 0x51, 0xA0, 0x01, 0x00, + 0x00, 0x03, 0x00, 0x00, + 0x05, 0x06, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, + 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + usb_buf[0x09] = logo_channel; + usb_buf[0x0A] = fan_channel; + + for(int led = 0x0B; led <= 0x19; led++) + { + usb_buf[led] = ring_channel; + } + + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +} diff --git a/Controllers/AMDWraithPrismController/AMDWraithPrismController.h b/Controllers/AMDWraithPrismController/AMDWraithPrismController.h new file mode 100644 index 0000000..cf68ab9 --- /dev/null +++ b/Controllers/AMDWraithPrismController/AMDWraithPrismController.h @@ -0,0 +1,157 @@ +/*---------------------------------------------------------*\ +| AMDWraithPrismController.h | +| | +| Driver for AMD Wraith Prism | +| | +| Adam Honse (CalcProgrammer1) 06 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX 0xFF +#define AMD_WRAITH_PRISM_FAN_BRIGHTNESS_CYCLE_MAX 0x7F + +static const unsigned char speed_values_fan_logo[][5] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, /* */ + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, /* Static */ + { 0x96, 0x8C, 0x80, 0x6E, 0x68 }, /* Color Cycle */ + { 0x3C, 0x37, 0x31, 0x2C, 0x26 }, /* Breathing */ +}; + +static const unsigned char mode_value_ring[] = +{ + 0xFF, + 0x03, + 0xFF, + 0x00, + 0x00, + 0x00, + 0x00, + 0x05, + 0xFF, + 0xC3, + 0x4A, + 0x05 +}; + +static const unsigned char speed_values_ring[][5] = +{ + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, /* Static */ + { 0x3C, 0x37, 0x31, 0x2C, 0x26 }, /* Breathing */ + { 0x96, 0x8C, 0x80, 0x6E, 0x68 }, /* Color Cycle */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, /* */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, /* */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, /* */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, /* */ + { 0x72, 0x68, 0x64, 0x62, 0x61 }, /* Rainbow */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, /* Bounce */ + { 0x77, 0x74, 0x6E, 0x6B, 0x67 }, /* Chase */ + { 0x77, 0x74, 0x6E, 0x6B, 0x67 }, /* Swirl */ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, /* Morse Code */ +}; + +enum +{ + AMD_WRAITH_PRISM_FAN_LOGO_MODE_STATIC = 0x01, /* Fan/Logo Static Mode */ + AMD_WRAITH_PRISM_FAN_LOGO_MODE_COLOR_CYCLE = 0x02, /* Fan/Logo Color Cycle Mode */ + AMD_WRAITH_PRISM_FAN_LOGO_MODE_BREATHING = 0x03, /* Fan/Logo Breathing Mode */ +}; + +enum +{ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_STATIC = 0x00, /* Static effect channel */ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_BREATHING = 0x01, /* Breathing effect channel */ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_COLOR_CYCLE = 0x02, /* Color cycle effect channel */ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_LOGO_LED = 0x05, /* Logo LED effect channel */ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_FAN_LED = 0x06, /* Fan LED effect channel */ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_RAINBOW = 0x07, /* Rainbow effect channel */ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_BOUNCE = 0x08, /* Bounce effect channel */ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_CHASE = 0x09, /* Chase effect channel */ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_SWIRL = 0x0A, /* Swirl effect channel */ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_MORSE = 0x0B, /* Morse code effect channel */ + AMD_WRAITH_PRISM_EFFECT_CHANNEL_DIRECT = 0xFF, /* Value for direct mode */ +}; + +enum +{ + AMD_WRAITH_PRISM_SPEED_SLOWEST = 0x00, /* Slowest speed */ + AMD_WRAITH_PRISM_SPEED_SLOW = 0x01, /* Slow speed */ + AMD_WRAITH_PRISM_SPEED_NORMAL = 0x02, /* Normal speed */ + AMD_WRAITH_PRISM_SPEED_FAST = 0x03, /* Fast speed */ + AMD_WRAITH_PRISM_SPEED_FASTEST = 0x04, /* Fastest speed */ +}; + +class AMDWraithPrismController +{ +public: + AMDWraithPrismController(hid_device* dev_handle, const char* path); + ~AMDWraithPrismController(); + + std::string GetEffectChannelString(unsigned char channel); + std::string GetFirmwareVersionString(); + std::string GetLocationString(); + std::string GetSerialString(); + + unsigned char current_fan_mode; + unsigned char current_fan_speed; + unsigned char current_fan_brightness; + bool current_fan_random_color; + + unsigned char current_logo_mode; + unsigned char current_logo_speed; + unsigned char current_logo_brightness; + bool current_logo_random_color; + + unsigned char current_ring_mode; + unsigned char current_ring_speed; + unsigned char current_ring_brightness; + bool current_ring_direction; + bool current_ring_random_color; + + void SetFanMode(unsigned char mode, unsigned char speed, unsigned char brightness, bool random_color); + void SetFanColor(unsigned char red, unsigned char green, unsigned char blue); + void SetLogoMode(unsigned char mode, unsigned char speed, unsigned char brightness, bool random_color); + void SetLogoColor(unsigned char red, unsigned char green, unsigned char blue); + void SetRingMode(unsigned char mode, unsigned char speed, unsigned char brightness, bool direction, bool random_color); + void SetRingColor(unsigned char red, unsigned char green, unsigned char blue); + + void SendDirectPacket + ( + unsigned char size, + unsigned char * led_ids, + RGBColor * colors + ); + + void SendEnableCommand(bool direct); + + void SendApplyCommand(); + +private: + hid_device* dev; + std::string location; + + void SetRingEffectChannel(unsigned char channel); + + void SendEffectChannelUpdate + ( + unsigned char effect_channel, + unsigned char speed, + bool direction, + bool random_color, + unsigned char mode, + unsigned char brightness, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SendChannelRemap(unsigned char ring_channel, unsigned char logo_channel, unsigned char fan_channel); +}; diff --git a/Controllers/AMDWraithPrismController/AMDWraithPrismControllerDetect.cpp b/Controllers/AMDWraithPrismController/AMDWraithPrismControllerDetect.cpp new file mode 100644 index 0000000..2532370 --- /dev/null +++ b/Controllers/AMDWraithPrismController/AMDWraithPrismControllerDetect.cpp @@ -0,0 +1,48 @@ +/*---------------------------------------------------------*\ +| AMDWraithPrismControllerDetect.cpp | +| | +| Detector for AMD Wraith Prism | +| | +| Adam Honse (CalcProgrammer1) 06 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "AMDWraithPrismController.h" +#include "RGBController_AMDWraithPrism.h" + +/*---------------------------------------------------------*\ +| AMD Wraith Prism vendor ID | +\*---------------------------------------------------------*/ +#define AMD_WRAITH_PRISM_VID 0x2516 + +/*---------------------------------------------------------*\ +| AMD Wraith Prism product ID | +\*---------------------------------------------------------*/ +#define AMD_WRAITH_PRISM_PID 0x0051 + +/******************************************************************************************\ +* * +* DetectAMDWraithPrismControllers * +* * +* Tests the USB address to see if an AMD Wraith Prism controller exists there. * +* * +\******************************************************************************************/ + +void DetectAMDWraithPrismControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AMDWraithPrismController* controller = new AMDWraithPrismController(dev, info->path); + RGBController_AMDWraithPrism* rgb_controller = new RGBController_AMDWraithPrism(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IP("AMD Wraith Prism", DetectAMDWraithPrismControllers, AMD_WRAITH_PRISM_VID, AMD_WRAITH_PRISM_PID, 1, 0xFF00); diff --git a/Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.cpp b/Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.cpp new file mode 100644 index 0000000..4dcec37 --- /dev/null +++ b/Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.cpp @@ -0,0 +1,336 @@ +/*---------------------------------------------------------*\ +| RGBController_AMDWraithPrism.cpp | +| | +| RGBController for AMD Wraith Prism | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AMDWraithPrism.h" + +/**------------------------------------------------------------------*\ + @name AMD Wraith Prism + @category Cooler + @type USB + @save :o: + @direct :white_check_mark: + @effects :tools: + @detectors DetectAMDWraithPrismControllers + @comment The Wraith Prism comes with 2 cables but is only detectable + and controlable when using the USB cable. `Morse Code` and `Mirage` + modes have not been implemented. Saving to flash is supported by + the device but not yet implemented. +\*-------------------------------------------------------------------*/ + +RGBController_AMDWraithPrism::RGBController_AMDWraithPrism(AMDWraithPrismController* controller_ptr) +{ + controller = controller_ptr; + + name = "AMD Wraith Prism"; + vendor = "Cooler Master"; + type = DEVICE_TYPE_COOLER; + description = "AMD Wraith Prism Device"; + version = controller->GetFirmwareVersionString(); + location = controller->GetLocationString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = AMD_WRAITH_PRISM_EFFECT_CHANNEL_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = 0; + Direct.brightness_max = 0; + Direct.brightness = 0; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AMD_WRAITH_PRISM_EFFECT_CHANNEL_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.speed_min = AMD_WRAITH_PRISM_SPEED_SLOWEST; + Breathing.speed_max = AMD_WRAITH_PRISM_SPEED_FASTEST; + Breathing.brightness_min = 0; + Breathing.brightness_max = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX; + Breathing.brightness = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = AMD_WRAITH_PRISM_SPEED_NORMAL; + Breathing.colors.resize(3); + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Spectrum Cycle"; + ColorCycle.value = AMD_WRAITH_PRISM_EFFECT_CHANNEL_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + ColorCycle.speed_min = AMD_WRAITH_PRISM_SPEED_SLOWEST; + ColorCycle.speed_max = AMD_WRAITH_PRISM_SPEED_FASTEST; + ColorCycle.brightness_min = 0; + ColorCycle.brightness_max = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_CYCLE_MAX; + ColorCycle.brightness = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_CYCLE_MAX; + ColorCycle.color_mode = MODE_COLORS_NONE; + ColorCycle.speed = AMD_WRAITH_PRISM_SPEED_NORMAL; + modes.push_back(ColorCycle); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = AMD_WRAITH_PRISM_EFFECT_CHANNEL_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rainbow.speed_min = AMD_WRAITH_PRISM_SPEED_SLOWEST; + Rainbow.speed_max = AMD_WRAITH_PRISM_SPEED_FASTEST; + Rainbow.brightness_min = 0; + Rainbow.brightness_max = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX; + Rainbow.brightness = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX; //The Ring zone can not get brighter but Logo / Fan can + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed = AMD_WRAITH_PRISM_SPEED_NORMAL; + modes.push_back(Rainbow); + + mode Bounce; + Bounce.name = "Bounce"; + Bounce.value = AMD_WRAITH_PRISM_EFFECT_CHANNEL_BOUNCE; + Bounce.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Bounce.speed_min = AMD_WRAITH_PRISM_SPEED_SLOWEST; + Bounce.speed_max = AMD_WRAITH_PRISM_SPEED_FASTEST; + Bounce.brightness_min = 0; + Bounce.brightness_max = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX; + Bounce.brightness = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX; //The Ring zone can not get brighter but Logo / Fan can + Bounce.color_mode = MODE_COLORS_NONE; + Bounce.speed = AMD_WRAITH_PRISM_SPEED_NORMAL; + modes.push_back(Bounce); + + mode Chase; + Chase.name = "Chase"; + Chase.value = AMD_WRAITH_PRISM_EFFECT_CHANNEL_CHASE; + Chase.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Chase.speed_min = AMD_WRAITH_PRISM_SPEED_SLOWEST; + Chase.speed_max = AMD_WRAITH_PRISM_SPEED_FASTEST; + Chase.brightness_min = 0; + Chase.brightness_max = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX; + Chase.brightness = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX; + Chase.color_mode = MODE_COLORS_MODE_SPECIFIC; + Chase.speed = AMD_WRAITH_PRISM_SPEED_NORMAL; + Chase.colors.resize(3); + modes.push_back(Chase); + + mode Swirl; + Swirl.name = "Swirl"; + Swirl.value = AMD_WRAITH_PRISM_EFFECT_CHANNEL_SWIRL; + Swirl.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Swirl.speed_min = AMD_WRAITH_PRISM_SPEED_SLOWEST; + Swirl.speed_max = AMD_WRAITH_PRISM_SPEED_FASTEST; + Swirl.brightness_min = 0; + Swirl.brightness_max = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX; + Swirl.brightness = AMD_WRAITH_PRISM_FAN_BRIGHTNESS_DEFAULT_MAX; + Swirl.color_mode = MODE_COLORS_MODE_SPECIFIC; + Swirl.speed = AMD_WRAITH_PRISM_SPEED_NORMAL; + Swirl.colors.resize(3); + modes.push_back(Swirl); + + SetupZones(); +} + +RGBController_AMDWraithPrism::~RGBController_AMDWraithPrism() +{ + delete controller; +} + +void RGBController_AMDWraithPrism::SetupZones() +{ + /*-----------------------------------------------------*\ + | LED maps | + \*-----------------------------------------------------*/ + const unsigned int logo_leds[1] = { 0x00 }; + const unsigned int fan_leds[1] = { 0x01 }; + const unsigned int ring_leds[15] = { 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x10, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09 }; + + /*-----------------------------------------------------*\ + | Set up zones | + \*-----------------------------------------------------*/ + zone logo_zone; + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + zone fan_zone; + fan_zone.name = "Fan"; + fan_zone.type = ZONE_TYPE_SINGLE; + fan_zone.leds_min = 1; + fan_zone.leds_max = 1; + fan_zone.leds_count = 1; + fan_zone.matrix_map = NULL; + zones.push_back(fan_zone); + + zone ring_zone; + ring_zone.name = "Ring"; + ring_zone.type = ZONE_TYPE_LINEAR; + ring_zone.leds_min = 15; + ring_zone.leds_max = 15; + ring_zone.leds_count = 15; + ring_zone.matrix_map = NULL; + zones.push_back(ring_zone); + + /*-----------------------------------------------------*\ + | Set up LEDs | + \*-----------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < 1; led_idx++) + { + led logo_led; + logo_led.name = "Logo LED"; + logo_led.value = logo_leds[led_idx]; + leds.push_back(logo_led); + } + + for(unsigned int led_idx = 0; led_idx < 1; led_idx++) + { + led fan_led; + fan_led.name = "Fan LED"; + fan_led.value = fan_leds[led_idx]; + leds.push_back(fan_led); + } + + for(unsigned int led_idx = 0; led_idx < 15; led_idx++) + { + led ring_led; + ring_led.name = "Ring LED"; + ring_led.value = ring_leds[led_idx]; + leds.push_back(ring_led); + } + + SetupColors(); +} + +void RGBController_AMDWraithPrism::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*-----------------------------------------------------*\ + | This device does not support resizing zones | + \*-----------------------------------------------------*/ +} + +void RGBController_AMDWraithPrism::DeviceUpdateLEDs() +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + unsigned char led_ids[17]; + RGBColor color_buf[17]; + unsigned int leds_count; + + /*-------------------------------------------------*\ + | Send logo and fan LEDs in the first packet | + \*-------------------------------------------------*/ + leds_count = 0; + for(std::size_t led_idx = 0; led_idx < 2; led_idx++) + { + led_ids[leds_count] = (unsigned char)leds[led_idx].value; + color_buf[leds_count] = colors[led_idx]; + + leds_count++; + } + controller->SendDirectPacket(leds_count, led_ids, color_buf); + + /*-------------------------------------------------*\ + | Send all ring LEDs in the second packet | + \*-------------------------------------------------*/ + leds_count = 0; + for(std::size_t led_idx = 0; led_idx < ( colors.size() - 2 ); led_idx++) + { + led_ids[leds_count] = (unsigned char)leds[led_idx + 2].value; + color_buf[leds_count] = colors[led_idx + 2]; + + leds_count++; + } + controller->SendDirectPacket(leds_count, led_ids, color_buf); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu = RGBGetBValue(modes[active_mode].colors[0]); + controller->SetLogoColor(red, grn, blu); + + red = RGBGetRValue(modes[active_mode].colors[1]); + grn = RGBGetGValue(modes[active_mode].colors[1]); + blu = RGBGetBValue(modes[active_mode].colors[1]); + controller->SetFanColor(red, grn, blu); + + red = RGBGetRValue(modes[active_mode].colors[2]); + grn = RGBGetGValue(modes[active_mode].colors[2]); + blu = RGBGetBValue(modes[active_mode].colors[2]); + controller->SetRingColor(red, grn, blu); + } + else + { + controller->SetLogoColor(0, 0, 0); + controller->SetFanColor(0, 0, 0); + controller->SetRingColor(0, 0, 0); + } +} + +void RGBController_AMDWraithPrism::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AMDWraithPrism::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AMDWraithPrism::DeviceUpdateMode() +{ + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + + switch(modes[active_mode].value) + { + case AMD_WRAITH_PRISM_EFFECT_CHANNEL_DIRECT: + controller->SendEnableCommand(true); + controller->SendApplyCommand(); + break; + + case AMD_WRAITH_PRISM_EFFECT_CHANNEL_COLOR_CYCLE: + controller->SendEnableCommand(false); + controller->SetRingMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, modes[active_mode].direction, random); + controller->SetFanMode(AMD_WRAITH_PRISM_FAN_LOGO_MODE_COLOR_CYCLE, modes[active_mode].speed, modes[active_mode].brightness, random); + controller->SetLogoMode(AMD_WRAITH_PRISM_FAN_LOGO_MODE_COLOR_CYCLE, modes[active_mode].speed, modes[active_mode].brightness, random); + break; + + case AMD_WRAITH_PRISM_EFFECT_CHANNEL_RAINBOW: + case AMD_WRAITH_PRISM_EFFECT_CHANNEL_BOUNCE: + controller->SendEnableCommand(false); + controller->SetRingMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, modes[active_mode].direction, random); + controller->SetFanMode(AMD_WRAITH_PRISM_FAN_LOGO_MODE_COLOR_CYCLE, modes[active_mode].speed, (modes[active_mode].brightness >> 1), random); + controller->SetLogoMode(AMD_WRAITH_PRISM_FAN_LOGO_MODE_COLOR_CYCLE, modes[active_mode].speed, (modes[active_mode].brightness >> 1), random); + break; + + case AMD_WRAITH_PRISM_EFFECT_CHANNEL_BREATHING: + controller->SendEnableCommand(false); + controller->SetRingMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, modes[active_mode].direction, random); + controller->SetFanMode(AMD_WRAITH_PRISM_FAN_LOGO_MODE_BREATHING, modes[active_mode].speed, modes[active_mode].brightness, random); + controller->SetLogoMode(AMD_WRAITH_PRISM_FAN_LOGO_MODE_BREATHING, modes[active_mode].speed, modes[active_mode].brightness, random); + break; + + default: + if(random) + { + controller->SendEnableCommand(false); + controller->SetRingMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, modes[active_mode].direction, random); + controller->SetFanMode(AMD_WRAITH_PRISM_FAN_LOGO_MODE_COLOR_CYCLE, modes[active_mode].speed, modes[active_mode].brightness, random); + controller->SetLogoMode(AMD_WRAITH_PRISM_FAN_LOGO_MODE_COLOR_CYCLE, modes[active_mode].speed, modes[active_mode].brightness, random); + } + else + { + controller->SendEnableCommand(false); + controller->SetRingMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, modes[active_mode].direction, random); + controller->SetFanMode(AMD_WRAITH_PRISM_FAN_LOGO_MODE_STATIC, modes[active_mode].speed, modes[active_mode].brightness, random); + controller->SetLogoMode(AMD_WRAITH_PRISM_FAN_LOGO_MODE_STATIC, modes[active_mode].speed, modes[active_mode].brightness, random); + } + break; + } + + DeviceUpdateLEDs(); +} diff --git a/Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.h b/Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.h new file mode 100644 index 0000000..90937b3 --- /dev/null +++ b/Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_AMDWraithPrism.h | +| | +| RGBController for AMD Wraith Prism | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AMDWraithPrismController.h" + +class RGBController_AMDWraithPrism : public RGBController +{ +public: + RGBController_AMDWraithPrism(AMDWraithPrismController* controller_ptr); + ~RGBController_AMDWraithPrism(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AMDWraithPrismController* controller; +}; diff --git a/Controllers/AOCKeyboardController/AOCKeyboardController.cpp b/Controllers/AOCKeyboardController/AOCKeyboardController.cpp new file mode 100644 index 0000000..f2a5624 --- /dev/null +++ b/Controllers/AOCKeyboardController/AOCKeyboardController.cpp @@ -0,0 +1,216 @@ +/*---------------------------------------------------------*\ +| AOCKeyboardController.cpp | +| | +| Driver for AOC keyboard | +| | +| Adam Honse (CalcProgrammer1) 10 May 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "AOCKeyboardController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +AOCKeyboardController::AOCKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +AOCKeyboardController::~AOCKeyboardController() +{ + hid_close(dev); +} + +std::string AOCKeyboardController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string AOCKeyboardController::GetDeviceName() +{ + return(name); +} + +std::string AOCKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void AOCKeyboardController::SetLightingConfig + ( + unsigned char mode, + unsigned char random, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + RGBColor* color_data + ) +{ + SendStartPacket(); + std::this_thread::sleep_for(5ms); + + SendLightingConfigPacket(mode, random, brightness, speed, direction, color_data); + std::this_thread::sleep_for(5ms); + + SendEndPacket(); + std::this_thread::sleep_for(10ms); +} + +void AOCKeyboardController::SetCustom + ( + RGBColor* color_data + ) +{ + SendStartPacket(); + std::this_thread::sleep_for(5ms); + + SendCustomPacket(color_data); + std::this_thread::sleep_for(5ms); + + SendEndPacket(); + std::this_thread::sleep_for(5ms); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void AOCKeyboardController::SendStartPacket() +{ + unsigned char buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up start packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x09; + buf[0x01] = 0x21; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, buf, sizeof(buf)); +} + +void AOCKeyboardController::SendEndPacket() +{ + unsigned char buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up end packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x09; + buf[0x01] = 0x22; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, buf, sizeof(buf)); +} + +void AOCKeyboardController::SendCustomPacket + ( + RGBColor* color_data + ) +{ + unsigned char buf[361]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up custom lighting packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x20; + + /*-----------------------------------------------------*\ + | Copy in color data | + \*-----------------------------------------------------*/ + for(unsigned int color_idx = 0; color_idx < 120; color_idx++) + { + buf[color_idx + 1] = RGBGetRValue(color_data[color_idx]); + buf[color_idx + 121] = RGBGetGValue(color_data[color_idx]); + buf[color_idx + 241] = RGBGetBValue(color_data[color_idx]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, sizeof(buf)); +} + +void AOCKeyboardController::SendLightingConfigPacket + ( + unsigned char mode, + unsigned char random, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + RGBColor* color_data + ) +{ + unsigned char buf[117]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up lighting configuration packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x14; + buf[0x01] = 0x01; + + buf[0x06] = mode; + + buf[0x07 + (9 * mode) + 0] = RGBGetRValue(color_data[0]); + buf[0x07 + (9 * mode) + 1] = RGBGetGValue(color_data[0]); + buf[0x07 + (9 * mode) + 2] = RGBGetBValue(color_data[0]); + buf[0x07 + (9 * mode) + 3] = random; + buf[0x07 + (9 * mode) + 4] = direction; + buf[0x07 + (9 * mode) + 5] = speed; + buf[0x07 + (9 * mode) + 6] = brightness; + + unsigned short checksum = 0x4A9E; + + for(unsigned int buf_idx = 0; buf_idx < 115; buf_idx++) + { + checksum += buf[buf_idx]; + } + + buf[115] = checksum & 0xFF; + buf[116] = checksum >> 8; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 117); +} diff --git a/Controllers/AOCKeyboardController/AOCKeyboardController.h b/Controllers/AOCKeyboardController/AOCKeyboardController.h new file mode 100644 index 0000000..6f0a1f6 --- /dev/null +++ b/Controllers/AOCKeyboardController/AOCKeyboardController.h @@ -0,0 +1,111 @@ +/*---------------------------------------------------------*\ +| AOCKeyboardController.h | +| | +| Driver for AOC keyboard | +| | +| Adam Honse (CalcProgrammer1) 10 May 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*-----------------------------------------*\ +| AOC Keyboard Modes | +\*-----------------------------------------*/ +enum +{ + AOC_KEYBOARD_MODE_STATIC = 0x00, /* Static mode */ + AOC_KEYBOARD_MODE_BREATHING = 0x01, /* Breathing mode */ + AOC_KEYBOARD_MODE_REACT = 0x02, /* React mode */ + AOC_KEYBOARD_MODE_RIPPLE = 0x04, /* Ripple mode */ + AOC_KEYBOARD_MODE_RADAR = 0x05, /* Radar mode */ + AOC_KEYBOARD_MODE_FIREWORKS = 0x06, /* Fireworks mode */ + AOC_KEYBOARD_MODE_BLINK = 0x07, /* Blink mode */ + AOC_KEYBOARD_MODE_WAVE = 0x08, /* Wave mode */ + AOC_KEYBOARD_MODE_CUSTOM = 0x09, /* Custom mode */ + AOC_KEYBOARD_MODE_CONCENTRIC_CIRCLES = 0x0A, /* Concentric Circles mode */ + AOC_KEYBOARD_MODE_W_WAVE = 0x0B, /* W Wave mode */ +}; + +enum +{ + AOC_KEYBOARD_SPEED_SLOW = 0x03, /* Slowest speed */ + AOC_KEYBOARD_SPEED_MEDIUM = 0x02, /* Medium speed */ + AOC_KEYBOARD_SPEED_FAST = 0x01, /* Fastest speed */ +}; + +enum +{ + AOC_KEYBOARD_BRIGHTNESS_OFF = 0x00, /* Lowest brightness (off) */ + AOC_KEYBOARD_BRIGHTNESS_LOW = 0x01, /* Low brightness */ + AOC_KEYBOARD_BRIGHTNESS_MEDIUM = 0x02, /* Medium brightness */ + AOC_KEYBOARD_BRIGHTNESS_HIGH = 0x03, /* Highest brightness */ +}; + +enum +{ + AOC_KEYBOARD_SINGLE_COLOR = 0x00, /* Single color mode */ + AOC_KEYBOARD_RANDOM = 0x01, /* Random color mode */ +}; + +enum +{ + AOC_KEYBOARD_DIRECTION_CLOCKWISE = 0x00, /* Clockwise direction */ + AOC_KEYBOARD_DIRECTION_COUNTERCLOCKWISE = 0x01, /* Counter-clockwise direction */ +}; + + +class AOCKeyboardController +{ +public: + AOCKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name); + ~AOCKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + void SetLightingConfig + ( + unsigned char mode, + unsigned char random, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + RGBColor* color_data + ); + + void SetCustom + ( + RGBColor* color_data + ); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendStartPacket(); + void SendEndPacket(); + + void SendCustomPacket + ( + RGBColor* color_data + ); + + void SendLightingConfigPacket + ( + unsigned char mode, + unsigned char random, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + RGBColor* color_data + ); +}; diff --git a/Controllers/AOCKeyboardController/AOCKeyboardControllerDetect.cpp b/Controllers/AOCKeyboardController/AOCKeyboardControllerDetect.cpp new file mode 100644 index 0000000..1eb957a --- /dev/null +++ b/Controllers/AOCKeyboardController/AOCKeyboardControllerDetect.cpp @@ -0,0 +1,45 @@ +/*---------------------------------------------------------*\ +| AOCKeyboardControllerDetect.cpp | +| | +| Detector for AOC keyboard | +| | +| Adam Honse (CalcProgrammer1) 10 May 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AOCKeyboardController.h" +#include "RGBController_AOCKeyboard.h" + +/*-----------------------------------------------------*\ +| AOC Keyboard IDs | +\*-----------------------------------------------------*/ +#define AOC_VID 0x3938 +#define AOC_GK500_PID 0x1178 +#define AOC_GK500_PID_2 0x1229 + +/******************************************************************************************\ +* * +* DetectAOCKeyboardControllers * +* * +* Tests the USB address to see if an AOC Keyboard controller exists there. * +* * +\******************************************************************************************/ + +void DetectAOCKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AOCKeyboardController* controller = new AOCKeyboardController(dev, info->path, name); + RGBController_AOCKeyboard* rgb_controller = new RGBController_AOCKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("AOC GK500", DetectAOCKeyboardControllers, AOC_VID, AOC_GK500_PID, 0xFF19, 0xFF19); +REGISTER_HID_DETECTOR_PU("AOC GK500", DetectAOCKeyboardControllers, AOC_VID, AOC_GK500_PID_2, 0xFF19, 0xFF19); diff --git a/Controllers/AOCKeyboardController/RGBController_AOCKeyboard.cpp b/Controllers/AOCKeyboardController/RGBController_AOCKeyboard.cpp new file mode 100644 index 0000000..d6b3497 --- /dev/null +++ b/Controllers/AOCKeyboardController/RGBController_AOCKeyboard.cpp @@ -0,0 +1,371 @@ +/*---------------------------------------------------------*\ +| RGBController_AOCKeyboard.cpp | +| | +| RGBController for AOC keyboard | +| | +| Adam Honse (CalcProgrammer1) 10 May 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AOCKeyboard.h" +#include "KeyboardLayoutManager.h" + +/**--------------------------------------------------------------------*\ + @name AOC Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAOCKeyboardControllers + @comment +\*---------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------*\ +| AOC Keyboard KLM Layout | +\*---------------------------------------------------------------------*/ +layout_values aoc_keyboard_offset_values = +{ + { + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 90, 92, 77, 63, 79, 94, 81, 96, 82, 83, 98, 40, 55, 85, 100, 104, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC INS HOME PGUP NLCK NP/ NP* NP- */ + 75, 76, 91, 62, 48, 64, 50, 65, 66, 67, 97, 68, 84, 70, 59, 74, 89, 58, 73, 88, 103, + /* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NP7 NP8 NP9 NP+ */ + 60, 61, 47, 78, 33, 49, 35, 80, 51, 52, 53, 69, 99, 25, 44, 29, 14, 43, 28, 13, 102, + /* CPLK A S D F G H J K L ; " # ENTR NP4 NP5 NP6 */ + 45, 46, 32, 93, 18, 34, 20, 95, 36, 37, 38, 54, 0, 10, 57, 72, 87, + /* LSFT \ Z X C V B N M , . / RSFT ARWU NP1 NP2 NP3 NPEN */ + 30, 0, 31, 17, 2, 3, 19, 5, 6, 21, 22, 23, 39, 11, 42, 27, 12, 101, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR NP0 NP. */ + 15, 0, 1, 4, 7, 8, 24, 9, 26, 41, 56, 71, 86 + }, + { + /* Add more regional layout fixes here */ + } +}; + + +RGBController_AOCKeyboard::RGBController_AOCKeyboard(AOCKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "AOC"; + type = DEVICE_TYPE_KEYBOARD; + description = "AOC Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.value = AOC_KEYBOARD_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + Static.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Static.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = AOC_KEYBOARD_MODE_STATIC; + SpectrumCycle.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + SpectrumCycle.color_mode = MODE_COLORS_RANDOM; + SpectrumCycle.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + SpectrumCycle.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + SpectrumCycle.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + SpectrumCycle.speed_min = AOC_KEYBOARD_SPEED_SLOW; + SpectrumCycle.speed_max = AOC_KEYBOARD_SPEED_FAST; + SpectrumCycle.speed = AOC_KEYBOARD_SPEED_MEDIUM; + modes.push_back(SpectrumCycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AOC_KEYBOARD_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + Breathing.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Breathing.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Breathing.speed_min = AOC_KEYBOARD_SPEED_SLOW; + Breathing.speed_max = AOC_KEYBOARD_SPEED_FAST; + Breathing.speed = AOC_KEYBOARD_SPEED_MEDIUM; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode React; + React.name = "React"; + React.value = AOC_KEYBOARD_MODE_REACT; + React.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + React.color_mode = MODE_COLORS_MODE_SPECIFIC; + React.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + React.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + React.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + React.speed_min = AOC_KEYBOARD_SPEED_SLOW; + React.speed_max = AOC_KEYBOARD_SPEED_FAST; + React.speed = AOC_KEYBOARD_SPEED_MEDIUM; + React.colors_min = 1; + React.colors_max = 1; + React.colors.resize(1); + modes.push_back(React); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = AOC_KEYBOARD_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripple.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + Ripple.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Ripple.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Ripple.speed_min = AOC_KEYBOARD_SPEED_SLOW; + Ripple.speed_max = AOC_KEYBOARD_SPEED_FAST; + Ripple.speed = AOC_KEYBOARD_SPEED_MEDIUM; + Ripple.colors_min = 1; + Ripple.colors_max = 1; + Ripple.colors.resize(1); + modes.push_back(Ripple); + + mode Radar; + Radar.name = "Radar"; + Radar.value = AOC_KEYBOARD_MODE_RADAR; + Radar.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Radar.color_mode = MODE_COLORS_MODE_SPECIFIC; + Radar.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + Radar.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Radar.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Radar.speed_min = AOC_KEYBOARD_SPEED_SLOW; + Radar.speed_max = AOC_KEYBOARD_SPEED_FAST; + Radar.speed = AOC_KEYBOARD_SPEED_MEDIUM; + Radar.colors_min = 1; + Radar.colors_max = 1; + Radar.colors.resize(1); + modes.push_back(Radar); + + mode Fireworks; + Fireworks.name = "Fireworks"; + Fireworks.value = AOC_KEYBOARD_MODE_FIREWORKS; + Fireworks.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Fireworks.color_mode = MODE_COLORS_MODE_SPECIFIC; + Fireworks.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + Fireworks.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Fireworks.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Fireworks.speed_min = AOC_KEYBOARD_SPEED_SLOW; + Fireworks.speed_max = AOC_KEYBOARD_SPEED_FAST; + Fireworks.speed = AOC_KEYBOARD_SPEED_MEDIUM; + Fireworks.colors_min = 1; + Fireworks.colors_max = 1; + Fireworks.colors.resize(1); + modes.push_back(Fireworks); + + mode Blink; + Blink.name = "Flashing"; + Blink.value = AOC_KEYBOARD_MODE_BLINK; + Blink.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Blink.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blink.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + Blink.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Blink.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Blink.speed_min = AOC_KEYBOARD_SPEED_SLOW; + Blink.speed_max = AOC_KEYBOARD_SPEED_FAST; + Blink.speed = AOC_KEYBOARD_SPEED_MEDIUM; + Blink.colors_min = 1; + Blink.colors_max = 1; + Blink.colors.resize(1); + modes.push_back(Blink); + + mode Wave; + Wave.name = "Wave"; + Wave.value = AOC_KEYBOARD_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + Wave.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Wave.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Wave.speed_min = AOC_KEYBOARD_SPEED_SLOW; + Wave.speed_max = AOC_KEYBOARD_SPEED_FAST; + Wave.speed = AOC_KEYBOARD_SPEED_MEDIUM; + Wave.colors_min = 1; + Wave.colors_max = 1; + Wave.colors.resize(1); + modes.push_back(Wave); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = AOC_KEYBOARD_MODE_WAVE; + RainbowWave.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RainbowWave.color_mode = MODE_COLORS_RANDOM; + RainbowWave.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + RainbowWave.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + RainbowWave.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + RainbowWave.speed_min = AOC_KEYBOARD_SPEED_SLOW; + RainbowWave.speed_max = AOC_KEYBOARD_SPEED_FAST; + RainbowWave.speed = AOC_KEYBOARD_SPEED_MEDIUM; + modes.push_back(RainbowWave); + + mode ConcentricCircles; + ConcentricCircles.name = "Concentric Circles"; + ConcentricCircles.value = AOC_KEYBOARD_MODE_CONCENTRIC_CIRCLES; + ConcentricCircles.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + ConcentricCircles.color_mode = MODE_COLORS_MODE_SPECIFIC; + ConcentricCircles.brightness_min= AOC_KEYBOARD_BRIGHTNESS_OFF; + ConcentricCircles.brightness_max= AOC_KEYBOARD_BRIGHTNESS_HIGH; + ConcentricCircles.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + ConcentricCircles.speed_min = AOC_KEYBOARD_SPEED_SLOW; + ConcentricCircles.speed_max = AOC_KEYBOARD_SPEED_FAST; + ConcentricCircles.speed = AOC_KEYBOARD_SPEED_MEDIUM; + ConcentricCircles.colors_min = 1; + ConcentricCircles.colors_max = 1; + ConcentricCircles.colors.resize(1); + modes.push_back(ConcentricCircles); + + mode WWave; + WWave.name = "W Wave"; + WWave.value = AOC_KEYBOARD_MODE_W_WAVE; + WWave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + WWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + WWave.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + WWave.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + WWave.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + WWave.speed_min = AOC_KEYBOARD_SPEED_SLOW; + WWave.speed_max = AOC_KEYBOARD_SPEED_FAST; + WWave.speed = AOC_KEYBOARD_SPEED_MEDIUM; + WWave.colors_min = 1; + WWave.colors_max = 1; + WWave.colors.resize(1); + modes.push_back(WWave); + + mode Direct; + Direct.name = "Direct"; + Direct.value = AOC_KEYBOARD_MODE_CUSTOM; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = AOC_KEYBOARD_BRIGHTNESS_OFF; + Direct.brightness_max = AOC_KEYBOARD_BRIGHTNESS_HIGH; + Direct.brightness = AOC_KEYBOARD_BRIGHTNESS_HIGH; + modes.push_back(Direct); + + SetupZones(); +}; + +RGBController_AOCKeyboard::~RGBController_AOCKeyboard() +{ + delete controller; +} + +void RGBController_AOCKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Create the keyboard zone usiung Keyboard Layout Manager | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = ZONE_EN_KEYBOARD; + new_zone.type = ZONE_TYPE_MATRIX; + + KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ANSI_QWERTY, KEYBOARD_SIZE_FULL, aoc_keyboard_offset_values); + + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + new_zone.matrix_map->height = new_kb.GetRowCount(); + new_zone.matrix_map->width = new_kb.GetColumnCount(); + + new_zone.matrix_map->map = new unsigned int[new_map->height * new_map->width]; + new_zone.leds_count = new_kb.GetKeyCount(); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + /*---------------------------------------------------------*\ + | Matrix map still uses declared zone rows and columns | + | as the packet structure depends on the matrix map | + \*---------------------------------------------------------*/ + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT, new_map->height, new_map->width); + + /*---------------------------------------------------------*\ + | Create LEDs for the Matrix zone | + | Place keys in the layout to populate the matrix | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + leds.push_back(new_led); + } + + zones.push_back(new_zone); + + SetupColors(); +} + +void RGBController_AOCKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AOCKeyboard::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == AOC_KEYBOARD_MODE_CUSTOM) + { + RGBColor color_buf[120]; + + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + color_buf[leds[led_idx].value] = colors[led_idx]; + } + + controller->SetCustom(&color_buf[0]); + } + else + { + DeviceUpdateMode(); + } +} + +void RGBController_AOCKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AOCKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AOCKeyboard::DeviceUpdateMode() +{ + unsigned char aoc_direction = AOC_KEYBOARD_DIRECTION_CLOCKWISE; + unsigned char aoc_random = AOC_KEYBOARD_SINGLE_COLOR; + RGBColor* aoc_colors = &colors[0]; + + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + aoc_direction = AOC_KEYBOARD_DIRECTION_COUNTERCLOCKWISE; + } + + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + aoc_random = AOC_KEYBOARD_RANDOM; + } + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + aoc_colors = &modes[active_mode].colors[0]; + } + + controller->SetLightingConfig(modes[active_mode].value, + aoc_random, + modes[active_mode].brightness, + modes[active_mode].speed, + aoc_direction, + aoc_colors); +} diff --git a/Controllers/AOCKeyboardController/RGBController_AOCKeyboard.h b/Controllers/AOCKeyboardController/RGBController_AOCKeyboard.h new file mode 100644 index 0000000..e2bd5ee --- /dev/null +++ b/Controllers/AOCKeyboardController/RGBController_AOCKeyboard.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_AOCKeyboard.h | +| | +| RGBController for AOC keyboard | +| | +| Adam Honse (CalcProgrammer1) 10 May 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AOCKeyboardController.h" + +class RGBController_AOCKeyboard : public RGBController +{ +public: + RGBController_AOCKeyboard(AOCKeyboardController* controller_ptr); + ~RGBController_AOCKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AOCKeyboardController* controller; +}; diff --git a/Controllers/AOCMouseController/AOCMouseController.cpp b/Controllers/AOCMouseController/AOCMouseController.cpp new file mode 100644 index 0000000..6b385fa --- /dev/null +++ b/Controllers/AOCMouseController/AOCMouseController.cpp @@ -0,0 +1,136 @@ +/*---------------------------------------------------------*\ +| AOCMouseController.cpp | +| | +| Driver for AOC mouse | +| | +| Adam Honse (CalcProgrammer1) 20 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AOCMouseController.h" +#include "StringUtils.h" + +AOCMouseController::AOCMouseController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +AOCMouseController::~AOCMouseController() +{ + hid_close(dev); +} + +std::string AOCMouseController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string AOCMouseController::GetDeviceName() +{ + return(name); +} + +std::string AOCMouseController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void AOCMouseController::SendDirect + ( + RGBColor* color_data + ) +{ + SendPacket(AOC_MOUSE_MODE_STATIC_SINGLE_COLOR, + AOC_MOUSE_BRIGHTNESS_HIGH, + AOC_MOUSE_SPEED_MEDIUM, + AOC_MOUSE_DIRECTION_CLOCKWISE, + color_data); +} + +void AOCMouseController::SendPacket + ( + unsigned char mode, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + RGBColor* color_data + ) +{ + unsigned char buf[60]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x20; + buf[0x01] = 0x03; + buf[0x02] = 0x01; + buf[0x03] = mode; + buf[0x04] = speed; + buf[0x05] = brightness; + buf[0x06] = direction; + buf[0x07] = 0x01; + buf[0x08] = 0x02; + buf[0x09] = 0xFF; + buf[0x0D] = 0x01; + buf[0x0E] = 0x03; + buf[0x0F] = 0xFF; + buf[0x10] = 0x7F; + buf[0x13] = 0x01; + buf[0x14] = 0x04; + buf[0x17] = 0xFF; + buf[0x19] = 0x01; + buf[0x1A] = 0x05; + buf[0x1C] = 0xFF; + buf[0x1F] = 0x01; + buf[0x20] = 0x06; + buf[0x21] = 0xFF; + buf[0x23] = 0xFF; + buf[0x25] = 0x01; + buf[0x26] = 0x07; + buf[0x27] = 0xFF; + buf[0x28] = 0xFF; + buf[0x2C] = 0x0A; + buf[0x2D] = 0x0A; + buf[0x30] = 0x14; + buf[0x31] = 0x08; + buf[0x3A] = 0x32; + buf[0x3B] = 0x32; + + /*-----------------------------------------------------*\ + | Copy in color | + \*-----------------------------------------------------*/ + buf[0x33] = RGBGetRValue(color_data[0]); + buf[0x34] = RGBGetGValue(color_data[0]); + buf[0x35] = RGBGetBValue(color_data[0]); + + buf[0x36] = RGBGetRValue(color_data[1]); + buf[0x37] = RGBGetGValue(color_data[1]); + buf[0x38] = RGBGetBValue(color_data[1]); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 60); +} diff --git a/Controllers/AOCMouseController/AOCMouseController.h b/Controllers/AOCMouseController/AOCMouseController.h new file mode 100644 index 0000000..82f3479 --- /dev/null +++ b/Controllers/AOCMouseController/AOCMouseController.h @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| AOCMouseController.h | +| | +| Driver for AOC mouse | +| | +| Adam Honse (CalcProgrammer1) 20 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*-----------------------------------------*\ +| AOC Mousemat Modes | +| Note: The 0x80 bit is the random flag | +\*-----------------------------------------*/ +enum +{ + AOC_MOUSE_MODE_STATIC_SINGLE_COLOR = 0x00, /* Static single color mode */ + AOC_MOUSE_MODE_STATIC_RANDOM = 0x80, /* Static random color mode */ + AOC_MOUSE_MODE_BREATHING_SINGLE_COLOR = 0x01, /* Breathing single color mode */ + AOC_MOUSE_MODE_BREATHING_RANDOM = 0x81, /* Breathing random color mode */ + AOC_MOUSE_MODE_BLINK_SINGLE_COLOR = 0x02, /* Blink single color mode */ + AOC_MOUSE_MODE_BLINK_RANDOM = 0x82, /* Blink random color mode */ + AOC_MOUSE_MODE_WAVE_SINGLE_COLOR = 0x03, /* Wave single color mode */ + AOC_MOUSE_MODE_WAVE_RANDOM = 0x83, /* Wave random color mode */ + AOC_MOUSE_MODE_DPI = 0x04, /* DPI mode */ +}; + +enum +{ + AOC_MOUSE_SPEED_SLOW = 0x03, /* Slowest speed */ + AOC_MOUSE_SPEED_MEDIUM = 0x02, /* Medium speed */ + AOC_MOUSE_SPEED_FAST = 0x01, /* Fastest speed */ +}; + +enum +{ + AOC_MOUSE_BRIGHTNESS_OFF = 0x00, /* Lowest brightness (off) */ + AOC_MOUSE_BRIGHTNESS_LOW = 0x01, /* Low brightness */ + AOC_MOUSE_BRIGHTNESS_MEDIUM = 0x02, /* Medium brightness */ + AOC_MOUSE_BRIGHTNESS_HIGH = 0x03, /* Highest brightness */ +}; + +enum +{ + AOC_MOUSE_DIRECTION_CLOCKWISE = 0x00, /* Clockwise direction */ + AOC_MOUSE_DIRECTION_COUNTERCLOCKWISE = 0x01, /* Counter-clockwise direction */ +}; + +class AOCMouseController +{ +public: + AOCMouseController(hid_device* dev_handle, const char* path, std::string dev_name); + ~AOCMouseController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + void SendDirect + ( + RGBColor* color_data + ); + + void SendPacket + ( + unsigned char mode, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + RGBColor* color_data + ); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/AOCMouseController/AOCMouseControllerDetect.cpp b/Controllers/AOCMouseController/AOCMouseControllerDetect.cpp new file mode 100644 index 0000000..23b509f --- /dev/null +++ b/Controllers/AOCMouseController/AOCMouseControllerDetect.cpp @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| AOCMouseControllerDetect.cpp | +| | +| Detector for AOC mouse | +| | +| Adam Honse (CalcProgrammer1) 20 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AOCMouseController.h" +#include "RGBController_AOCMouse.h" + +/*-----------------------------------------------------*\ +| AOC Mouse IDs | +\*-----------------------------------------------------*/ +#define AOC_VID 0x3938 +#define AOC_GM500_PID 0x1179 + +/******************************************************************************************\ +* * +* DetectAOCMouseControllers * +* * +* Tests the USB address to see if an AOC Mouse controller exists there. * +* * +\******************************************************************************************/ + +void DetectAOCMouseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AOCMouseController* controller = new AOCMouseController(dev, info->path, name); + RGBController_AOCMouse* rgb_controller = new RGBController_AOCMouse(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("AOC GM500", DetectAOCMouseControllers, AOC_VID, AOC_GM500_PID, 1, 0xFF19, 0xFF19); diff --git a/Controllers/AOCMouseController/RGBController_AOCMouse.cpp b/Controllers/AOCMouseController/RGBController_AOCMouse.cpp new file mode 100644 index 0000000..dd192ae --- /dev/null +++ b/Controllers/AOCMouseController/RGBController_AOCMouse.cpp @@ -0,0 +1,218 @@ +/*---------------------------------------------------------*\ +| RGBController_AOCMouse.cpp | +| | +| RGBController for AOC mouse | +| | +| Adam Honse (CalcProgrammer1) 20 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AOCMouse.h" + +/**------------------------------------------------------------------*\ + @name AOC Mouse + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAOCMouseControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AOCMouse::RGBController_AOCMouse(AOCMouseController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "AOC"; + type = DEVICE_TYPE_MOUSE; + description = "AOC Mouse Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = AOC_MOUSE_MODE_STATIC_SINGLE_COLOR; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = AOC_MOUSE_BRIGHTNESS_OFF; + Direct.brightness_max = AOC_MOUSE_BRIGHTNESS_HIGH; + Direct.brightness = AOC_MOUSE_BRIGHTNESS_HIGH; + modes.push_back(Direct); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = AOC_MOUSE_MODE_STATIC_RANDOM; + SpectrumCycle.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + SpectrumCycle.color_mode = MODE_COLORS_RANDOM; + SpectrumCycle.brightness_min = AOC_MOUSE_BRIGHTNESS_OFF; + SpectrumCycle.brightness_max = AOC_MOUSE_BRIGHTNESS_HIGH; + SpectrumCycle.brightness = AOC_MOUSE_BRIGHTNESS_HIGH; + SpectrumCycle.speed_min = AOC_MOUSE_SPEED_SLOW; + SpectrumCycle.speed_max = AOC_MOUSE_SPEED_FAST; + SpectrumCycle.speed = AOC_MOUSE_SPEED_MEDIUM; + modes.push_back(SpectrumCycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AOC_MOUSE_MODE_BREATHING_SINGLE_COLOR; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = AOC_MOUSE_BRIGHTNESS_OFF; + Breathing.brightness_max = AOC_MOUSE_BRIGHTNESS_HIGH; + Breathing.brightness = AOC_MOUSE_BRIGHTNESS_HIGH; + Breathing.speed_min = AOC_MOUSE_SPEED_SLOW; + Breathing.speed_max = AOC_MOUSE_SPEED_FAST; + Breathing.speed = AOC_MOUSE_SPEED_MEDIUM; + modes.push_back(Breathing); + + mode Blink; + Blink.name = "Flashing"; + Blink.value = AOC_MOUSE_MODE_BLINK_SINGLE_COLOR; + Blink.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Blink.color_mode = MODE_COLORS_PER_LED; + Blink.brightness_min = AOC_MOUSE_BRIGHTNESS_OFF; + Blink.brightness_max = AOC_MOUSE_BRIGHTNESS_HIGH; + Blink.brightness = AOC_MOUSE_BRIGHTNESS_HIGH; + Blink.speed_min = AOC_MOUSE_SPEED_SLOW; + Blink.speed_max = AOC_MOUSE_SPEED_FAST; + Blink.speed = AOC_MOUSE_SPEED_MEDIUM; + modes.push_back(Blink); + + mode Wave; + Wave.name = "Wave"; + Wave.value = AOC_MOUSE_MODE_WAVE_SINGLE_COLOR; + Wave.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Wave.color_mode = MODE_COLORS_PER_LED; + Wave.brightness_min = AOC_MOUSE_BRIGHTNESS_OFF; + Wave.brightness_max = AOC_MOUSE_BRIGHTNESS_HIGH; + Wave.brightness = AOC_MOUSE_BRIGHTNESS_HIGH; + Wave.speed_min = AOC_MOUSE_SPEED_SLOW; + Wave.speed_max = AOC_MOUSE_SPEED_FAST; + Wave.speed = AOC_MOUSE_SPEED_MEDIUM; + modes.push_back(Wave); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = AOC_MOUSE_MODE_WAVE_RANDOM; + RainbowWave.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RainbowWave.color_mode = MODE_COLORS_RANDOM; + RainbowWave.brightness_min = AOC_MOUSE_BRIGHTNESS_OFF; + RainbowWave.brightness_max = AOC_MOUSE_BRIGHTNESS_HIGH; + RainbowWave.brightness = AOC_MOUSE_BRIGHTNESS_HIGH; + RainbowWave.speed_min = AOC_MOUSE_SPEED_SLOW; + RainbowWave.speed_max = AOC_MOUSE_SPEED_FAST; + RainbowWave.speed = AOC_MOUSE_SPEED_MEDIUM; + modes.push_back(RainbowWave); + + mode DPI; + DPI.name = "DPI"; + DPI.value = AOC_MOUSE_MODE_DPI; + DPI.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + DPI.color_mode = MODE_COLORS_RANDOM; + DPI.brightness_min = AOC_MOUSE_BRIGHTNESS_OFF; + DPI.brightness_max = AOC_MOUSE_BRIGHTNESS_HIGH; + DPI.brightness = AOC_MOUSE_BRIGHTNESS_HIGH; + modes.push_back(DPI); + + SetupZones(); +}; + +RGBController_AOCMouse::~RGBController_AOCMouse() +{ + delete controller; +} + +void RGBController_AOCMouse::SetupZones() +{ + zone logo_zone; + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + led logo_led; + logo_led.name = "Logo"; + leds.push_back(logo_led); + + zone scroll_wheel_zone; + scroll_wheel_zone.name = "Scroll Wheel"; + scroll_wheel_zone.type = ZONE_TYPE_SINGLE; + scroll_wheel_zone.leds_min = 1; + scroll_wheel_zone.leds_max = 1; + scroll_wheel_zone.leds_count = 1; + scroll_wheel_zone.matrix_map = NULL; + zones.push_back(scroll_wheel_zone); + + led scroll_wheel_led; + scroll_wheel_led.name = "Scroll Wheel"; + leds.push_back(scroll_wheel_led); + + SetupColors(); +} + +void RGBController_AOCMouse::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AOCMouse::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_AOCMouse::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AOCMouse::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AOCMouse::DeviceUpdateMode() +{ + if(modes[active_mode].value == AOC_MOUSE_MODE_STATIC_SINGLE_COLOR) + { + controller->SendDirect(&colors[0]); + } + else + { + unsigned char aoc_direction = AOC_MOUSE_DIRECTION_CLOCKWISE; + unsigned int aoc_mode = modes[active_mode].value; + + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + aoc_direction = AOC_MOUSE_DIRECTION_COUNTERCLOCKWISE; + } + + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + switch(modes[active_mode].value) + { + case AOC_MOUSE_MODE_BREATHING_SINGLE_COLOR: + aoc_mode = AOC_MOUSE_MODE_BREATHING_RANDOM; + break; + + case AOC_MOUSE_MODE_BLINK_SINGLE_COLOR: + aoc_mode = AOC_MOUSE_MODE_BLINK_RANDOM; + break; + } + } + + controller->SendPacket(aoc_mode, + modes[active_mode].brightness, + modes[active_mode].speed, + aoc_direction, + &colors[0]); + } +} diff --git a/Controllers/AOCMouseController/RGBController_AOCMouse.h b/Controllers/AOCMouseController/RGBController_AOCMouse.h new file mode 100644 index 0000000..416b4cb --- /dev/null +++ b/Controllers/AOCMouseController/RGBController_AOCMouse.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_AOCMouse.h | +| | +| RGBController for AOC mouse | +| | +| Adam Honse (CalcProgrammer1) 20 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AOCMouseController.h" + +class RGBController_AOCMouse : public RGBController +{ +public: + RGBController_AOCMouse(AOCMouseController* controller_ptr); + ~RGBController_AOCMouse(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AOCMouseController* controller; +}; diff --git a/Controllers/AOCMousematController/AOCMousematController.cpp b/Controllers/AOCMousematController/AOCMousematController.cpp new file mode 100644 index 0000000..95a4922 --- /dev/null +++ b/Controllers/AOCMousematController/AOCMousematController.cpp @@ -0,0 +1,117 @@ +/*---------------------------------------------------------*\ +| AOCMousematController.cpp | +| | +| Driver for AOC mousemat | +| | +| Adam Honse (CalcProgrammer1) 15 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AOCMousematController.h" +#include "StringUtils.h" + +AOCMousematController::AOCMousematController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +AOCMousematController::~AOCMousematController() +{ + hid_close(dev); +} + +std::string AOCMousematController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string AOCMousematController::GetDeviceName() +{ + return(name); +} + +std::string AOCMousematController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void AOCMousematController::SendDirect + ( + RGBColor* color_data + ) +{ + SendPacket(AOC_MOUSEMAT_MODE_STATIC_SINGLE_COLOR, + AOC_MOUSEMAT_BRIGHTNESS_HIGH, + AOC_MOUSEMAT_SPEED_MEDIUM, + AOC_MOUSEMAT_DIRECTION_CLOCKWISE, + color_data); +} + +void AOCMousematController::SendPacket + ( + unsigned char mode, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + RGBColor* color_data + ) +{ + unsigned char buf[32]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x20; + buf[0x01] = brightness; + buf[0x02] = speed; + buf[0x03] = direction; + buf[0x04] = 0x01; + buf[0x05] = mode; + buf[0x09] = 0xFF; + buf[0x0C] = 0xFF; + buf[0x0D] = 0x3F; + buf[0x0F] = 0xFF; + buf[0x10] = 0xFF; + buf[0x13] = 0xFF; + buf[0x17] = 0xFF; + buf[0x19] = 0xFF; + buf[0x1A] = 0xFF; + buf[0x1B] = 0xFF; + buf[0x1D] = 0xFF; + buf[0x1E] = 0x32; + buf[0x1F] = 0x32; + + /*-----------------------------------------------------*\ + | Copy in color | + \*-----------------------------------------------------*/ + buf[0x06] = RGBGetRValue(color_data[0]); + buf[0x07] = RGBGetGValue(color_data[0]); + buf[0x08] = RGBGetBValue(color_data[0]); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 32); +} diff --git a/Controllers/AOCMousematController/AOCMousematController.h b/Controllers/AOCMousematController/AOCMousematController.h new file mode 100644 index 0000000..e8f655d --- /dev/null +++ b/Controllers/AOCMousematController/AOCMousematController.h @@ -0,0 +1,83 @@ +/*---------------------------------------------------------*\ +| AOCMousematController.h | +| | +| Driver for AOC mousemat | +| | +| Adam Honse (CalcProgrammer1) 15 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*-----------------------------------------*\ +| AOC Mousemat Modes | +| Note: The 0x80 bit is the random flag | +\*-----------------------------------------*/ +enum +{ + AOC_MOUSEMAT_MODE_STATIC_SINGLE_COLOR = 0x00, /* Static single color mode */ + AOC_MOUSEMAT_MODE_STATIC_RANDOM = 0x80, /* Static random color mode */ + AOC_MOUSEMAT_MODE_BREATHING_SINGLE_COLOR = 0x01, /* Breathing single color mode */ + AOC_MOUSEMAT_MODE_BREATHING_RANDOM = 0x81, /* Breathing random color mode */ + AOC_MOUSEMAT_MODE_BLINK_SINGLE_COLOR = 0x02, /* Blink single color mode */ + AOC_MOUSEMAT_MODE_BLINK_RANDOM = 0x82, /* Blink random color mode */ + AOC_MOUSEMAT_MODE_WAVE_SINGLE_COLOR = 0x03, /* Wave single color mode */ + AOC_MOUSEMAT_MODE_WAVE_RANDOM = 0x83, /* Wave random color mode */ +}; + +enum +{ + AOC_MOUSEMAT_SPEED_SLOW = 0x03, /* Slowest speed */ + AOC_MOUSEMAT_SPEED_MEDIUM = 0x02, /* Medium speed */ + AOC_MOUSEMAT_SPEED_FAST = 0x01, /* Fastest speed */ +}; + +enum +{ + AOC_MOUSEMAT_BRIGHTNESS_OFF = 0x00, /* Lowest brightness (off) */ + AOC_MOUSEMAT_BRIGHTNESS_LOW = 0x01, /* Low brightness */ + AOC_MOUSEMAT_BRIGHTNESS_MEDIUM = 0x02, /* Medium brightness */ + AOC_MOUSEMAT_BRIGHTNESS_HIGH = 0x03, /* Highest brightness */ +}; + +enum +{ + AOC_MOUSEMAT_DIRECTION_CLOCKWISE = 0x00, /* Clockwise direction */ + AOC_MOUSEMAT_DIRECTION_COUNTERCLOCKWISE = 0x01, /* Counter-clockwise direction */ +}; + +class AOCMousematController +{ +public: + AOCMousematController(hid_device* dev_handle, const char* path, std::string dev_name); + ~AOCMousematController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + void SendDirect + ( + RGBColor* color_data + ); + + void SendPacket + ( + unsigned char mode, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + RGBColor* color_data + ); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/AOCMousematController/AOCMousematControllerDetect.cpp b/Controllers/AOCMousematController/AOCMousematControllerDetect.cpp new file mode 100644 index 0000000..de2bbd0 --- /dev/null +++ b/Controllers/AOCMousematController/AOCMousematControllerDetect.cpp @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| AOCMousematControllerDetect.cpp | +| | +| Detector for AOC mousemat | +| | +| Adam Honse (CalcProgrammer1) 15 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AOCMousematController.h" +#include "RGBController_AOCMousemat.h" + +/*-----------------------------------------------------*\ +| AOC Mousemat IDs | +\*-----------------------------------------------------*/ +#define AOC_VID 0x3938 +#define AOC_AMM700_PID 0x1162 + +/******************************************************************************************\ +* * +* DetectAOCMousematControllers * +* * +* Tests the USB address to see if an AOC Mousemat controller exists there. * +* * +\******************************************************************************************/ + +void DetectAOCMousematControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AOCMousematController* controller = new AOCMousematController(dev, info->path, name); + RGBController_AOCMousemat* rgb_controller = new RGBController_AOCMousemat(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("AOC AGON AMM700", DetectAOCMousematControllers, AOC_VID, AOC_AMM700_PID, 1, 0xFF19, 0xFF19); diff --git a/Controllers/AOCMousematController/RGBController_AOCMousemat.cpp b/Controllers/AOCMousematController/RGBController_AOCMousemat.cpp new file mode 100644 index 0000000..35a29c3 --- /dev/null +++ b/Controllers/AOCMousematController/RGBController_AOCMousemat.cpp @@ -0,0 +1,195 @@ +/*---------------------------------------------------------*\ +| RGBController_AOCMousemat.cpp | +| | +| RGBController for AOC mousemat | +| | +| Adam Honse (CalcProgrammer1) 15 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AOCMousemat.h" + +/**------------------------------------------------------------------*\ + @name AOC Mousemat + @category Mousemat + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAOCMousematControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AOCMousemat::RGBController_AOCMousemat(AOCMousematController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "AOC"; + type = DEVICE_TYPE_MOUSEMAT; + description = "AOC Mousemat Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = AOC_MOUSEMAT_MODE_STATIC_SINGLE_COLOR; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = AOC_MOUSEMAT_BRIGHTNESS_OFF; + Direct.brightness_max = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + Direct.brightness = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + modes.push_back(Direct); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = AOC_MOUSEMAT_MODE_STATIC_RANDOM; + SpectrumCycle.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + SpectrumCycle.color_mode = MODE_COLORS_RANDOM; + SpectrumCycle.brightness_min = AOC_MOUSEMAT_BRIGHTNESS_OFF; + SpectrumCycle.brightness_max = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + SpectrumCycle.brightness = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + SpectrumCycle.speed_min = AOC_MOUSEMAT_SPEED_SLOW; + SpectrumCycle.speed_max = AOC_MOUSEMAT_SPEED_FAST; + SpectrumCycle.speed = AOC_MOUSEMAT_SPEED_MEDIUM; + modes.push_back(SpectrumCycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AOC_MOUSEMAT_MODE_BREATHING_SINGLE_COLOR; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = AOC_MOUSEMAT_BRIGHTNESS_OFF; + Breathing.brightness_max = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + Breathing.brightness = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + Breathing.speed_min = AOC_MOUSEMAT_SPEED_SLOW; + Breathing.speed_max = AOC_MOUSEMAT_SPEED_FAST; + Breathing.speed = AOC_MOUSEMAT_SPEED_MEDIUM; + modes.push_back(Breathing); + + mode Blink; + Blink.name = "Flashing"; + Blink.value = AOC_MOUSEMAT_MODE_BLINK_SINGLE_COLOR; + Blink.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Blink.color_mode = MODE_COLORS_PER_LED; + Blink.brightness_min = AOC_MOUSEMAT_BRIGHTNESS_OFF; + Blink.brightness_max = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + Blink.brightness = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + Blink.speed_min = AOC_MOUSEMAT_SPEED_SLOW; + Blink.speed_max = AOC_MOUSEMAT_SPEED_FAST; + Blink.speed = AOC_MOUSEMAT_SPEED_MEDIUM; + modes.push_back(Blink); + + mode Wave; + Wave.name = "Wave"; + Wave.value = AOC_MOUSEMAT_MODE_WAVE_SINGLE_COLOR; + Wave.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Wave.color_mode = MODE_COLORS_PER_LED; + Wave.brightness_min = AOC_MOUSEMAT_BRIGHTNESS_OFF; + Wave.brightness_max = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + Wave.brightness = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + Wave.speed_min = AOC_MOUSEMAT_SPEED_SLOW; + Wave.speed_max = AOC_MOUSEMAT_SPEED_FAST; + Wave.speed = AOC_MOUSEMAT_SPEED_MEDIUM; + modes.push_back(Wave); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = AOC_MOUSEMAT_MODE_WAVE_RANDOM; + RainbowWave.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RainbowWave.color_mode = MODE_COLORS_RANDOM; + RainbowWave.brightness_min = AOC_MOUSEMAT_BRIGHTNESS_OFF; + RainbowWave.brightness_max = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + RainbowWave.brightness = AOC_MOUSEMAT_BRIGHTNESS_HIGH; + RainbowWave.speed_min = AOC_MOUSEMAT_SPEED_SLOW; + RainbowWave.speed_max = AOC_MOUSEMAT_SPEED_FAST; + RainbowWave.speed = AOC_MOUSEMAT_SPEED_MEDIUM; + modes.push_back(RainbowWave); + + SetupZones(); +}; + +RGBController_AOCMousemat::~RGBController_AOCMousemat() +{ + delete controller; +} + +void RGBController_AOCMousemat::SetupZones() +{ + zone mousemat_zone; + mousemat_zone.name = "Mousemat"; + mousemat_zone.type = ZONE_TYPE_SINGLE; + mousemat_zone.leds_min = 1; + mousemat_zone.leds_max = 1; + mousemat_zone.leds_count = 1; + mousemat_zone.matrix_map = NULL; + zones.push_back(mousemat_zone); + + led mousemat_led; + mousemat_led.name = "Mousemat"; + leds.push_back(mousemat_led); + + SetupColors(); +} + +void RGBController_AOCMousemat::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AOCMousemat::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_AOCMousemat::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AOCMousemat::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AOCMousemat::DeviceUpdateMode() +{ + if(modes[active_mode].value == AOC_MOUSEMAT_MODE_STATIC_SINGLE_COLOR) + { + controller->SendDirect(&colors[0]); + } + else + { + unsigned char aoc_direction = AOC_MOUSEMAT_DIRECTION_CLOCKWISE; + unsigned int aoc_mode = modes[active_mode].value; + + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + aoc_direction = AOC_MOUSEMAT_DIRECTION_COUNTERCLOCKWISE; + } + + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + switch(modes[active_mode].value) + { + case AOC_MOUSEMAT_MODE_BREATHING_SINGLE_COLOR: + aoc_mode = AOC_MOUSEMAT_MODE_BREATHING_RANDOM; + break; + + case AOC_MOUSEMAT_MODE_BLINK_SINGLE_COLOR: + aoc_mode = AOC_MOUSEMAT_MODE_BLINK_RANDOM; + break; + } + } + + controller->SendPacket(aoc_mode, + modes[active_mode].brightness, + modes[active_mode].speed, + aoc_direction, + &colors[0]); + } +} diff --git a/Controllers/AOCMousematController/RGBController_AOCMousemat.h b/Controllers/AOCMousematController/RGBController_AOCMousemat.h new file mode 100644 index 0000000..66aa9ed --- /dev/null +++ b/Controllers/AOCMousematController/RGBController_AOCMousemat.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_AOCMousemat.h | +| | +| RGBController for AOC mousemat | +| | +| Adam Honse (CalcProgrammer1) 15 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AOCMousematController.h" + +class RGBController_AOCMousemat : public RGBController +{ +public: + RGBController_AOCMousemat(AOCMousematController* controller_ptr); + ~RGBController_AOCMousemat(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AOCMousematController* controller; +}; diff --git a/Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBController.cpp b/Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBController.cpp new file mode 100644 index 0000000..cfca350 --- /dev/null +++ b/Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBController.cpp @@ -0,0 +1,588 @@ +/*---------------------------------------------------------*\ +| ASRockPolychromeUSBController.cpp | +| | +| Driver for ASRock Polychrome USB motherboards | +| | +| Ed Kambulow (dredvard) 20 Dec 2020 | +| Shady Nawara (ShadyNawara) 16 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController.h" +#include "ResourceManager.h" +#include "SettingsManager.h" +#include "StringUtils.h" +#include "ASRockPolychromeUSBController.h" +#include "dmiinfo.h" + +#define POLYCHROME_USB_READ_ZONE_CONFIG 0x11 +#define POLYCHROME_USB_READ_HEADER 0x14 +#define POLYCHROME_USB_WRITE_HEADER 0x15 +#define POLYCHROME_USB_SET_ZONE 0x10 +#define POLYCHROME_USB_INITIAL_CHUNK 0xE3 +#define POLYCHROME_USB_SEND_CHUNK 0xE4 +#define POLYCHROME_USB_INIT 0xA4 +#define POLYCHROME_USB_COMMIT 0x12 + +const char* polychrome_USB_zone_names[] = +{ + "RGB LED 1 Header", + "RGB LED 2 Header", + "Addressable Header 1", + "Addressable Header 2", + "PCH", + "IO Cover", + "PCB", + "Addressable Header 3/Audio", +}; + +PolychromeUSBController::PolychromeUSBController(hid_device* dev_handle, const char* path) +{ + DMIInfo dmi; + + dev = dev_handle; + device_name = "ASRock " + dmi.getMainboard(); + location = path; + + SetDeviceInfo(); +} + +PolychromeUSBController::~PolychromeUSBController() +{ + +} + +unsigned int PolychromeUSBController::GetChannelCount() +{ + return((unsigned int)device_info.size()); +} + +std::string PolychromeUSBController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string PolychromeUSBController::GetDeviceName() +{ + return(device_name); +} + +std::string PolychromeUSBController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void PolychromeUSBController::SetDeviceInfo() +{ + PolychromeDeviceInfo newdev_info; + + ReadConfigTables(); + + /*--------------------------------------------------*\ + | Read settings to check for configured RGSwap | + \*--------------------------------------------------*/ + const std::string detector_name = "ASRock Polychrome USB"; + const std::string json_rgswap = "RGSwap"; + SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager(); + json device_settings = settings_manager->GetSettings(detector_name); + + /*---------------------------------------------------------*\ + | Get RGSwap settings from the settings manager | + | If RGSwap settings are not found then write them out | + | Onboard leds are set to their existing values, | + | Addressable RGB and RGB headers are set to false | + \*---------------------------------------------------------*/ + if(!device_settings.contains(json_rgswap)) + { + device_settings[json_rgswap][polychrome_USB_zone_names[0]] = false; + device_settings[json_rgswap][polychrome_USB_zone_names[1]] = false; + device_settings[json_rgswap][polychrome_USB_zone_names[2]] = false; + device_settings[json_rgswap][polychrome_USB_zone_names[3]] = false; + device_settings[json_rgswap][polychrome_USB_zone_names[4]] = ((configtable[8] >> 4) & 1) ? true : false; + device_settings[json_rgswap][polychrome_USB_zone_names[5]] = ((configtable[8] >> 5) & 1) ? true : false; + device_settings[json_rgswap][polychrome_USB_zone_names[6]] = ((configtable[8] >> 6) & 1) ? true : false; + device_settings[json_rgswap][polychrome_USB_zone_names[7]] = false; + + settings_manager->SetSettings(detector_name, device_settings); + settings_manager->SaveSettings(); + } + else + { + for(std::size_t idx = 0; idx < 8; idx++) + { + if(device_settings[json_rgswap].contains(polychrome_USB_zone_names[idx])) + { + rgswapconfig[idx] = device_settings[json_rgswap][polychrome_USB_zone_names[idx]]; + } + } + } + + bool rgswap_final[8] = {0}; + + for (unsigned int zonecnt = 0; zonecnt < POLYCHROME_USB_ZONE_MAX_NUM; zonecnt++) + { + if(configtable[zonecnt] == 0x1E || !((configtable[9] >> zonecnt) & 1)) + { + /*-----------------------------------------------------------------------*\ + | If we don't have this device type (0x1E) we will skip it and continue. | + | Or if the device is not available we skip it and continue | + \*-----------------------------------------------------------------------*/ + continue; + } + + newdev_info.num_leds = configtable[zonecnt]; + newdev_info.rgswap = ((configtable[8] >> zonecnt) & 1) || rgswapconfig[zonecnt]; + rgswap_final[zonecnt] = newdev_info.rgswap; + + /*--------------------------------------------------------------------------------------------------*\ + | We will need to know what zone type this is, so that we can look up the name and make calls later. | + \*--------------------------------------------------------------------------------------------------*/ + newdev_info.zone_type = zonecnt; + + switch (zonecnt) + { + /*-----------------------------------------*\ + | Type: Addressable, configurable | + \*-----------------------------------------*/ + case POLYCHROME_USB_ZONE_ARGB1: + case POLYCHROME_USB_ZONE_ARGB2: + /*---------------------------------------------*\ + | The last led channel is allocated to a | + | third ARGB header on some newer motherboards | + \*---------------------------------------------*/ + case POLYCHROME_USB_ZONE_AUDIO: + newdev_info.device_type = PolychromeDeviceType::ADDRESSABLE; + break; + + /*-----------------------------------------*\ + | Type: Addressable, not configurable | + \*-----------------------------------------*/ + case POLYCHROME_USB_ZONE_PCH: + case POLYCHROME_USB_ZONE_IOCOVER: + case POLYCHROME_USB_ZONE_PCB: + newdev_info.device_type = PolychromeDeviceType::FIXED; + break; + + /*-----------------------------------------*\ + | Type: Fixed | + \*-----------------------------------------*/ + case POLYCHROME_USB_ZONE_RGB1: + case POLYCHROME_USB_ZONE_RGB2: + default: + newdev_info.device_type = PolychromeDeviceType::FIXED; + break; + } + + device_info.push_back(newdev_info); + } + + // set rgswap to match our settings + WriteRGSwap(rgswap_final[0], rgswap_final[1], rgswap_final[2], rgswap_final[3], rgswap_final[4], rgswap_final[5], rgswap_final[6], rgswap_final[7]); +} + +void PolychromeUSBController::ResizeZone(int zone, int new_size) +{ + unsigned char zonecfg[POLYCHROME_USB_ZONE_MAX_NUM]; + + memset(zonecfg, POLYCHROME_USB_ZONE_UNAVAILABLE, POLYCHROME_USB_ZONE_MAX_NUM); + + configtable[zone] = new_size; + + for(unsigned int i = 0; i < POLYCHROME_USB_ZONE_MAX_NUM; i++) + { + zonecfg[i] = configtable[i]; + } + + unsigned char zonecmd = POLYCHROME_USB_LEDCOUNT_CFG; + WriteHeader(zonecmd, zonecfg, sizeof(zonecfg)); +} + +void PolychromeUSBController::WriteZone + ( + unsigned char zone, + unsigned char mode, + unsigned char speed, + RGBColor rgb, + bool allzone = false + ) +{ + + /*----------------------------------------------------*\ + | Get the device info so we can look up the zone type. | + \*----------------------------------------------------*/ + PolychromeDeviceInfo device_info = GetPolychromeDevices()[zone]; + + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00 | + \*-----------------------------------------------------*/ + usb_buf[0x01] = POLYCHROME_USB_SET_ZONE; + usb_buf[0x03] = device_info.zone_type; + usb_buf[0x04] = mode; + + if(device_info.rgswap) + { + usb_buf[0x05] = RGBGetRValue(rgb); + usb_buf[0x06] = RGBGetGValue(rgb); + } + else + { + usb_buf[0x05] = RGBGetGValue(rgb); + usb_buf[0x06] = RGBGetRValue(rgb); + } + + usb_buf[0x07] = RGBGetBValue(rgb); + usb_buf[0x08] = speed; + usb_buf[0x09] = 0xFF; + usb_buf[0x10] = allzone; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +}; + +void PolychromeUSBController::WriteAllZones + ( + const std::vector& /*zones_info*/, + const std::vector& zones + ) +{ + std::vector combined_leds_rgb; + std::size_t max_led_count = 0; + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++){ + PolychromeDeviceInfo device_info = GetPolychromeDevices()[zone_idx]; + + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + if(device_info.rgswap) + { + combined_leds_rgb.push_back(RGBGetRValue(zones[zone_idx].colors[led_idx])); + combined_leds_rgb.push_back(RGBGetGValue(zones[zone_idx].colors[led_idx])); + } + else + { + combined_leds_rgb.push_back(RGBGetGValue(zones[zone_idx].colors[led_idx])); + combined_leds_rgb.push_back(RGBGetRValue(zones[zone_idx].colors[led_idx])); + } + combined_leds_rgb.push_back(RGBGetBValue(zones[zone_idx].colors[led_idx])); + } + max_led_count += zones[zone_idx].leds_max; + } + combined_leds_rgb.resize((max_led_count + 8) * 3, 0); + + unsigned char usb_buf[65]; + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up initial message packet with leading 00 | + \*-----------------------------------------------------*/ + usb_buf[0x01] = POLYCHROME_USB_SET_ZONE; + usb_buf[0x03] = 0xFF; + usb_buf[0x04] = POLYCHROME_USB_INITIAL_CHUNK; + usb_buf[0x07] = 0xFF; + usb_buf[0x40] = 0x65; + + std::size_t initial_led_offset = 9; + std::size_t message_byte_capacity = 54; // 18 leds * 3 bytes + + std::size_t led_byte_idx = 0; + + while(led_byte_idx < combined_leds_rgb.size()) + { + for(std::size_t byte_idx = 0; byte_idx < message_byte_capacity; byte_idx++) + { + usb_buf[initial_led_offset + byte_idx] = combined_leds_rgb[led_byte_idx++]; + if(led_byte_idx >= combined_leds_rgb.size()) + { + break; + } + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00 | + \*-----------------------------------------------------*/ + usb_buf[0x01] = POLYCHROME_USB_SET_ZONE; + usb_buf[0x03] = 0xFF; + usb_buf[0x04] = POLYCHROME_USB_SEND_CHUNK; + usb_buf[0x40] = 0x65; + + initial_led_offset = 5; + message_byte_capacity = 57; // 19 leds * 3 bytes + } +} + +/*-----------------------------------------------------*\ +| If reset is true, rgswap is set to the values | +| specified in the settings file, otherwise set to off | +\*-----------------------------------------------------*/ +void PolychromeUSBController::SetRGSwap(bool reset) +{ + if(reset) + { + bool rg[8] = {0}; + std::vector devices_info = GetPolychromeDevices(); + for (PolychromeDeviceInfo device : devices_info) + { + rg[device.zone_type] = device.rgswap; + } + WriteRGSwap(rg[0], rg[1], rg[2], rg[3], rg[4], rg[5], rg[6], rg[7]); + } + else + { + /*-----------------------------------------------------------------*\ + | Disable RGSwap as it causes flashing on each update in direct mode| + \*-----------------------------------------------------------------*/ + WriteRGSwap(0, 0, 0, 0, 0, 0, 0, 0); + } +} + +void PolychromeUSBController::WriteRGSwap + ( + bool hdr0, + bool hdr1, + bool ahdr0, + bool ahdr1, + bool pch, + bool io, + bool pcb, + bool chnl8 + ) +{ + unsigned char rgconfig[1] = {static_cast((((unsigned char)chnl8 << 7) | ((unsigned char)pcb << 6) | ((unsigned char)io << 5) | ((unsigned char)pch << 4) | ((unsigned char)ahdr1 << 3) | ((unsigned char)ahdr0 << 2) | ((unsigned char)hdr1 << 1) | (unsigned char)hdr0))}; + WriteHeader(POLYCHROME_USB_RGSWAP_CFG, rgconfig, 1); +} + +void PolychromeUSBController::WriteHeader + ( + unsigned char cfg, + unsigned char* configstring, + unsigned int configsize + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00 | + \*-----------------------------------------------------*/ + usb_buf[0x01] = POLYCHROME_USB_WRITE_HEADER; + usb_buf[0x03] = cfg; + memcpy(&usb_buf[4], configstring, configsize); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +} + +PolychromeZoneInfo PolychromeUSBController::GetZoneConfig(unsigned char zone) +{ + /*-----------------------------------------------------*\ + | Get the device info so we can look up the zone type | + \*-----------------------------------------------------*/ + PolychromeDeviceInfo device_info = GetPolychromeDevices()[zone]; + + unsigned char usb_buf[65]; + PolychromeZoneInfo zoneinfo; + unsigned char r; + unsigned char g; + unsigned char b; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x01] = POLYCHROME_USB_READ_ZONE_CONFIG; + usb_buf[0x03] = device_info.zone_type; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Read response | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + hid_read(dev, usb_buf, 64); + + r = usb_buf[0x05]; + g = usb_buf[0x06]; + b = usb_buf[0x07]; + + /*------------------------------------------------------*\ + | Set Chroma mode (0xE2) & Per-Led mode (0xE3) as Direct | + \*------------------------------------------------------*/ + zoneinfo.mode = usb_buf[0x04] != 0xE2 && usb_buf[0x04] != 0xE3 && usb_buf[0x04] < 0x0F ? usb_buf[0x04] : 0x0F; + + /*------------------------------------------------------*\ + | G & R are swapped since RGSwap is disabled on init, | + | Unless overwritten in settings | + \*------------------------------------------------------*/ + if(device_info.rgswap) + { + zoneinfo.color = ToRGBColor(r,g,b); + } + else + { + zoneinfo.color = ToRGBColor(g,r,b); + } + zoneinfo.speed = usb_buf[0x08]; + zoneinfo.zone = usb_buf[0x03]; + + return(zoneinfo); +} + +void PolychromeUSBController::ReadConfigTables() +{ + unsigned char usb_buf[65]; + unsigned char maxzoneleds[8]; + unsigned char rgswap; + unsigned char header1; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up max led config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x01] = POLYCHROME_USB_READ_HEADER; + usb_buf[0x03] = POLYCHROME_USB_LEDCOUNT_CFG; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Read response | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + hid_read(dev, usb_buf, 64); + + /*-----------------------------------------------------*\ + | Reads in format: RGB1, RGB2, ARGB1, ARGB2, PCH, IO, | + | PCB, AUDIO/ARGB3 | + \*-----------------------------------------------------*/ + memcpy(&maxzoneleds,&usb_buf[0x04],8); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up RGB Swap table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x01] = POLYCHROME_USB_READ_HEADER; + usb_buf[0x03] = POLYCHROME_USB_RGSWAP_CFG; + + hid_write(dev, usb_buf, 65); + memset(usb_buf, 0x00, sizeof(usb_buf)); + hid_read(dev, usb_buf, 64); + + /*-----------------------------------------------------*\ + | Reads bitwise in format: AUDIO/ARGB3, PCB, IO, PCH, | + | ARGB2,ARGB1, RGB2, RGB1 if available | + \*-----------------------------------------------------*/ + rgswap=usb_buf[4]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Header1 config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x01] = POLYCHROME_USB_READ_HEADER; + usb_buf[0x03] = 0x01; + + hid_write(dev, usb_buf, 64); + memset(usb_buf, 0x00, sizeof(usb_buf)); + hid_read(dev, usb_buf, 64); + + /*-----------------------------------------------------*\ + | Reads bitwise in format: AUDIO/ARGB3, PCB, IO, PCH, | + | ARGB2,ARGB1, RGB2, RGB1 if available | + \*-----------------------------------------------------*/ + header1=usb_buf[4]; + + memcpy(configtable,&maxzoneleds,8); + configtable[8]=rgswap; + configtable[9]=header1; + return; +} + +void PolychromeUSBController::Commit() +{ + /*-----------------------------------------------------*\ + | Saves all Writes in Device - Will keep after powerup | + \*-----------------------------------------------------*/ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00 | + \*-----------------------------------------------------*/ + usb_buf[0x01] = POLYCHROME_USB_COMMIT; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +}; + +const std::vector& PolychromeUSBController::GetPolychromeDevices() const +{ + return(device_info); +}; diff --git a/Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBController.h b/Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBController.h new file mode 100644 index 0000000..c045309 --- /dev/null +++ b/Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBController.h @@ -0,0 +1,166 @@ +/*---------------------------------------------------------*\ +| ASRockPolychromeUSBController.h | +| | +| Driver for ASRock Polychrome USB motherboards | +| | +| Ed Kambulow (dredvard) 20 Dec 2020 | +| Shady Nawara (ShadyNawara) 16 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*----------------------------------------------------------------------------------------------*\ +| Definitions for Polychrome USB | +\*----------------------------------------------------------------------------------------------*/ +#define POLYCHROME_USB_NUM_MODES 16 /* Number of Polychrome USB modes */ + +enum +{ + POLYCHROME_USB_MODE_OFF = 0x00, /* OFF mode */ + POLYCHROME_USB_MODE_STATIC = 0x01, /* Static color mode */ + POLYCHROME_USB_MODE_BREATHING = 0x02, /* Breathing effect mode */ + POLYCHROME_USB_MODE_STROBE = 0x03, /* Strobe effect mode */ + POLYCHROME_USB_MODE_SPECTRUM_CYCLE = 0x04, /* Spectrum Cycle effect mode */ + POLYCHROME_USB_MODE_RANDOM = 0x05, /* Random effect mode */ + POLYCHROME_USB_MODE_MUSIC = 0x06, /* Random effect mode */ + POLYCHROME_USB_MODE_WAVE = 0x07, /* Wave effect mode */ + POLYCHROME_USB_MODE_SPRING = 0x08, /* Spring effect mode */ + POLYCHROME_USB_MODE_STACK = 0x09, /* Stack effect mode */ + POLYCHROME_USB_MODE_CRAM = 0x0A, /* Cram effect mode */ + POLYCHROME_USB_MODE_SCAN = 0x0B, /* Scan effect mode */ + POLYCHROME_USB_MODE_NEON = 0x0C, /* Neon effect mode */ + POLYCHROME_USB_MODE_WATER = 0x0D, /* Water effect mode */ + POLYCHROME_USB_MODE_RAINBOW = 0x0E, /* Rainbow effect mode */ + POLYCHROME_USB_MODE_DIRECT = 0x0F, /* CHROMA CONNECT effect mode */ +}; + +enum +{ + POLYCHROME_USB_SPEED_MIN = 0xFF, /* Slowest speed */ + POLYCHROME_USB_SPEED_DEFAULT = 0xE0, /* Default speed */ + POLYCHROME_USB_SPEED_MAX = 0x00, /* Fastest speed */ +}; + +enum +{ + POLYCHROME_USB_ZONE_MAX_NUM = 0x08, /* Total Max number of zones */ + POLYCHROME_USB_ZONE_ADDRESSABLE_MAX = 0x64, /* Maxinum number of ARGB LEDs */ +}; + +enum +{ + POLYCHROME_USB_LEDCOUNT_CFG = 0x02, // Config for LED Count + POLYCHROME_USB_RGSWAP_CFG = 0x03, // Config for RGSWAP + POLYCHROME_USB_ZONE_UNAVAILABLE = 0x1E, // Value from LEDCOUNT CFG if zone not present +}; + +extern const char* polychrome_USB_zone_names[]; + +enum +{ + POLYCHROME_USB_ZONE_RGB1 = 0x00, // RGB Header 1 + POLYCHROME_USB_ZONE_RGB2 = 0X01, // RGB Header 2 + POLYCHROME_USB_ZONE_ARGB1 = 0X02, // ARGB Header 1 + POLYCHROME_USB_ZONE_ARGB2 = 0X03, // ARGB Header 2 + POLYCHROME_USB_ZONE_PCH = 0X04, // PCH + POLYCHROME_USB_ZONE_IOCOVER = 0X05, // IOCOVER + POLYCHROME_USB_ZONE_PCB = 0X06, // PCB - Could be mixed swapped with 0x07 + POLYCHROME_USB_ZONE_AUDIO = 0X07 // AUDIO/ARGB Header 3 +}; + +enum class PolychromeDeviceType +{ + FIXED, + ADDRESSABLE, +}; + +struct PolychromeZoneInfo +{ + unsigned char mode; + unsigned char zone; + unsigned char speed; + RGBColor color; +}; + +struct PolychromeDeviceInfo +{ + unsigned char effect_channel; + unsigned char num_leds; + unsigned char zone_type; + bool rgswap; + PolychromeDeviceType device_type; +}; + +class PolychromeUSBController +{ +public: + unsigned char zone_led_count[8]; + + PolychromeUSBController(hid_device* dev_handle, const char* path); + ~PolychromeUSBController(); + + unsigned int GetChannelCount(); + std::string GetDeviceLocation(); + std::string GetDeviceName(); + const std::vector& GetPolychromeDevices() const; + std::string GetSerialString(); + PolychromeZoneInfo GetZoneConfig (unsigned char zone); + + void WriteZone + ( + unsigned char zone, + unsigned char mode, + unsigned char speed, + RGBColor rgb, + bool allzone + ); + + void WriteAllZones + ( + const std::vector &zones_info, + const std::vector &zones + ); + + void WriteHeader + ( + unsigned char cfg, + unsigned char* configstring, + unsigned int configsize + ); + + void ResizeZone(int zone, int new_size); + void SetRGSwap(bool reset); + +protected: + hid_device* dev; + std::vector device_info; + std::string location; + + void WriteRGSwap + ( + bool hdr0, + bool hdr1, + bool ahdr0, + bool ahdr1, + bool pch, + bool io, + bool pcb, + bool chnl8 + ); + +private: + std::string device_name; + unsigned char configtable[12]; + bool rgswapconfig[8] = { 0 }; + + void SetDeviceInfo(); + void ReadConfigTables(); + void Commit(); +}; diff --git a/Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBControllerDetect.cpp b/Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBControllerDetect.cpp new file mode 100644 index 0000000..cbd2091 --- /dev/null +++ b/Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBControllerDetect.cpp @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| ASRockPolychromeUSBControllerDetect.cpp | +| | +| Detector for ASRock Polychrome USB motherboards | +| | +| Ed Kambulow (dredvard) 20 Dec 2020 | +| Shady Nawara (ShadyNawara) 16 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "ASRockPolychromeUSBController.h" +#include "RGBController_ASRockPolychromeUSB.h" + +/*---------------------------------------------------------*\ +| ASRock vendor ID | +\*---------------------------------------------------------*/ +#define ASROCK_VID 0x26CE + +/*---------------------------------------------------------*\ +| ASRock product ID | +\*---------------------------------------------------------*/ +#define ASROCK_MOTHERBOARD_1_PID 0x01A2 +#define ASROCK_DESKMINI_ADDRESSABLE_LED_STRIP_PID 0x01A6 + +void DetectPolychromeUSBControllers(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + PolychromeUSBController* controller = new PolychromeUSBController(dev, info->path); + RGBController_PolychromeUSB* rgb_controller = new RGBController_PolychromeUSB(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("ASRock Polychrome USB", DetectPolychromeUSBControllers, ASROCK_VID, ASROCK_MOTHERBOARD_1_PID); +REGISTER_HID_DETECTOR("ASRock Deskmini Addressable LED Strip", DetectPolychromeUSBControllers, ASROCK_VID, ASROCK_DESKMINI_ADDRESSABLE_LED_STRIP_PID); diff --git a/Controllers/ASRockPolychromeUSBController/RGBController_ASRockPolychromeUSB.cpp b/Controllers/ASRockPolychromeUSBController/RGBController_ASRockPolychromeUSB.cpp new file mode 100644 index 0000000..3f62d0d --- /dev/null +++ b/Controllers/ASRockPolychromeUSBController/RGBController_ASRockPolychromeUSB.cpp @@ -0,0 +1,361 @@ +/*---------------------------------------------------------*\ +| RGBController_ASRockPolychromeUSB.cpp | +| | +| RGBController for ASRock Polychrome USB motherboards | +| | +| Ed Kambulow (dredvard) 20 Dec 2020 | +| Shady Nawara (ShadyNawara) 16 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_ASRockPolychromeUSB.h" + +#define ASROCK_USB_MAX_ZONES 8 +#define ASROCK_ADDRESSABLE_MAX_LEDS 100 + +/**------------------------------------------------------------------*\ + @name ASrock Polychrome USB + @category Motherboard + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectPolychromeUSBControllers + @comment ASRock Polychrome controllers will save with each update. +\*-------------------------------------------------------------------*/ + +RGBController_PolychromeUSB::RGBController_PolychromeUSB(PolychromeUSBController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + description = "ASRock Polychrome USB Device"; + vendor = "ASRock"; + type = DEVICE_TYPE_MOTHERBOARD; + location = controller->GetDeviceLocation(); + + mode Off; + Off.name = "Off"; + Off.value = POLYCHROME_USB_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = POLYCHROME_USB_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = POLYCHROME_USB_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = POLYCHROME_USB_SPEED_MIN; + Breathing.speed_max = POLYCHROME_USB_SPEED_MAX; + Breathing.speed = POLYCHROME_USB_SPEED_DEFAULT; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Strobe; + Strobe.name = "Strobe"; + Strobe.value = POLYCHROME_USB_MODE_STROBE; + Strobe.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Strobe.speed_min = POLYCHROME_USB_SPEED_MIN; + Strobe.speed_max = POLYCHROME_USB_SPEED_MAX; + Strobe.speed = POLYCHROME_USB_SPEED_DEFAULT; + Strobe.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Strobe); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = POLYCHROME_USB_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED; + SpectrumCycle.speed_min = POLYCHROME_USB_SPEED_MIN; + SpectrumCycle.speed_max = POLYCHROME_USB_SPEED_MAX; + SpectrumCycle.speed = POLYCHROME_USB_SPEED_DEFAULT; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Random; + Random.name = "Random"; + Random.value = POLYCHROME_USB_MODE_RANDOM; + Random.flags = MODE_FLAG_HAS_SPEED; + Random.speed_min = POLYCHROME_USB_SPEED_MIN; + Random.speed_max = POLYCHROME_USB_SPEED_MAX; + Random.speed = POLYCHROME_USB_SPEED_DEFAULT; + Random.color_mode = MODE_COLORS_NONE; + modes.push_back(Random); + + mode Music; + Random.name = "Music"; + Random.value = POLYCHROME_USB_MODE_RANDOM; + Random.flags = MODE_FLAG_HAS_BRIGHTNESS; + Random.color_mode = MODE_COLORS_NONE; + modes.push_back(Random); + + mode Wave; + Wave.name = "Wave"; + Wave.value = POLYCHROME_USB_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED; + Wave.speed_min = POLYCHROME_USB_SPEED_MIN; + Wave.speed_max = POLYCHROME_USB_SPEED_MAX; + Wave.speed = POLYCHROME_USB_SPEED_DEFAULT; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + + mode Spring; + Spring.name = "Spring"; + Spring.value = POLYCHROME_USB_MODE_SPRING; + Spring.flags = MODE_FLAG_HAS_SPEED; + Spring.speed_min = POLYCHROME_USB_SPEED_MIN; + Spring.speed_max = POLYCHROME_USB_SPEED_MAX; + Spring.speed = POLYCHROME_USB_SPEED_DEFAULT; + Spring.color_mode = MODE_COLORS_NONE; + modes.push_back(Spring); + + mode Stack; + Stack.name = "Stack"; + Stack.value = POLYCHROME_USB_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED; + Stack.speed_min = POLYCHROME_USB_SPEED_MIN; + Stack.speed_max = POLYCHROME_USB_SPEED_MAX; + Stack.speed = POLYCHROME_USB_SPEED_DEFAULT; + Stack.color_mode = MODE_COLORS_NONE; + modes.push_back(Stack); + + mode Cram; + Cram.name = "Cram"; + Cram.value = POLYCHROME_USB_MODE_CRAM; + Cram.flags = MODE_FLAG_HAS_SPEED; + Cram.speed_min = POLYCHROME_USB_SPEED_MIN; + Cram.speed_max = POLYCHROME_USB_SPEED_MAX; + Cram.speed = POLYCHROME_USB_SPEED_DEFAULT; + Cram.color_mode = MODE_COLORS_NONE; + modes.push_back(Cram); + + mode Scan; + Scan.name = "Scan"; + Scan.value = POLYCHROME_USB_MODE_SCAN; + Scan.flags = MODE_FLAG_HAS_SPEED; + Scan.speed_min = POLYCHROME_USB_SPEED_MIN; + Scan.speed_max = POLYCHROME_USB_SPEED_MAX; + Scan.speed = POLYCHROME_USB_SPEED_DEFAULT; + Scan.color_mode = MODE_COLORS_NONE; + modes.push_back(Scan); + + mode Neon; + Neon.name = "Neon"; + Neon.value = POLYCHROME_USB_MODE_NEON; + Neon.flags = 0; + Neon.color_mode = MODE_COLORS_NONE; + modes.push_back(Neon); + + mode Water; + Water.name = "Water"; + Water.value = POLYCHROME_USB_MODE_WATER; + Water.flags = MODE_FLAG_HAS_SPEED; + Water.speed_min = POLYCHROME_USB_SPEED_MIN; + Water.speed_max = POLYCHROME_USB_SPEED_MAX; + Water.speed = POLYCHROME_USB_SPEED_DEFAULT; + Water.color_mode = MODE_COLORS_NONE; + modes.push_back(Water); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = POLYCHROME_USB_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED; + Rainbow.speed_min = POLYCHROME_USB_SPEED_MIN; + Rainbow.speed_max = POLYCHROME_USB_SPEED_MAX; + Rainbow.speed = POLYCHROME_USB_SPEED_DEFAULT; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Direct; + Direct.name = "Direct"; + Direct.value = POLYCHROME_USB_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +void RGBController_PolychromeUSB::SetupZones() +{ + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(controller->GetChannelCount()); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for(unsigned int channel_idx = 0; channel_idx < zones.size(); channel_idx++) + { + PolychromeDeviceInfo device_info = controller->GetPolychromeDevices()[channel_idx]; + + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + if(device_info.device_type== PolychromeDeviceType::ADDRESSABLE) + { + zones[channel_idx].name = polychrome_USB_zone_names[device_info.zone_type]; + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = ASROCK_ADDRESSABLE_MAX_LEDS; + zones[channel_idx].leds_count = device_info.num_leds; + } + else if(device_info.device_type==PolychromeDeviceType::FIXED) + { + zones[channel_idx].name = polychrome_USB_zone_names[device_info.zone_type]; + zones[channel_idx].leds_min = device_info.num_leds; + zones[channel_idx].leds_max = device_info.num_leds; + zones[channel_idx].leds_count = device_info.num_leds; + } + + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + led new_led; + new_led.name = zones[channel_idx].name; + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_ch_idx + 1)); + new_led.value = channel_idx; + + leds.push_back(new_led); + } + + zones[channel_idx].matrix_map = NULL; + } + + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize zone info to track zone, speed, mode | + | B550 Boards have modes, speed for each zone | + \*---------------------------------------------------------*/ + for (unsigned int channel_idx = 0; channel_idx < zones.size(); channel_idx++) + { + PolychromeZoneInfo zoneinfo; + zoneinfo = controller->GetZoneConfig(channel_idx); + zones_info.push_back(zoneinfo); + } + + /*---------------------------------------------------------*\ + | Initialize colors for each LED | + | We cannot currently get the individual led color so | + | we use the zone color in each zone to set all leds | + | !!TODO: in Per-Led mode, zone color is always black | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned char led = (unsigned char)leds[led_idx].value; + + colors[led_idx] = zones_info[led].color; + } + + /*-------------------------------------------------*\ + | Initialize active mode | + \*-------------------------------------------------*/ + active_mode = zones_info.size() > 1 ? zones_info[0].mode : 0x0; + + /*-----------------------------------------------------*\ + | If in Direct mode, reset all Leds to match interface | + \*-----------------------------------------------------*/ + if(active_mode == 0x0F) + { + controller->WriteAllZones(zones_info, zones); + } +} + +void RGBController_PolychromeUSB::ResizeZone(int zone, int new_size) +{ + zones[zone].leds_count = (unsigned char) new_size; + controller->ResizeZone(zones_info[zone].zone, new_size); +} + +void RGBController_PolychromeUSB::DeviceUpdateLEDs() +{ + if(POLYCHROME_USB_MODE_DIRECT == zones_info[0].mode ) + { + controller->WriteAllZones(zones_info,zones); + return; + } + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + unsigned char set_mode = zones_info[zone_idx].mode; + + if (set_mode>modes.size()) + { + set_mode = active_mode; + } + + controller->WriteZone((unsigned char)zone_idx, set_mode, zones_info[zone_idx].speed, zones[zone_idx].colors[0], false); + } +} + +void RGBController_PolychromeUSB::UpdateZoneLEDs(int zone) +{ + unsigned char set_mode=zones_info[zone].mode; + + if(set_mode > modes.size()) + { + set_mode = active_mode; + } + + controller->WriteZone(zone, set_mode, zones_info[zone].speed, zones[zone].colors[0], false); +} + +void RGBController_PolychromeUSB::UpdateSingleLED(int led) +{ + unsigned int channel = leds[led].value; + unsigned char set_mode = zones_info[channel].mode; + + if(set_mode > modes.size()) + { + set_mode = active_mode; + } + + controller->WriteZone(channel, set_mode, zones_info[channel].speed, zones[channel].colors[0], false); +} + +unsigned char RGBController_PolychromeUSB::GetDeviceMode(unsigned char zone) +{ + int dev_mode; + + dev_mode = controller->GetZoneConfig(zone).mode; + active_mode = dev_mode; + + return(active_mode); +} + +void RGBController_PolychromeUSB::DeviceUpdateMode() +{ + /*-----------------------------------------------------------------*\ + | Disable RGSwap as it causes flashing on each update in direct mode| + | Otherwise, reset to values specified in settings.json | + \*-----------------------------------------------------------------*/ + controller->SetRGSwap(modes[active_mode].name != "Direct"); + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].leds_count > 0) + { + unsigned char set_mode =(unsigned char) modes[active_mode].value; + zones_info[zone_idx].mode =(unsigned char) modes[active_mode].value; + zones_info[zone_idx].speed =(unsigned char) modes[active_mode].speed; + + if(set_mode > modes.size()) + { + set_mode = active_mode; + } + + controller->WriteZone(zone_idx, set_mode, zones_info[zone_idx].speed, zones[zone_idx].colors[0], false); + } + } +} diff --git a/Controllers/ASRockPolychromeUSBController/RGBController_ASRockPolychromeUSB.h b/Controllers/ASRockPolychromeUSBController/RGBController_ASRockPolychromeUSB.h new file mode 100644 index 0000000..c4d3e6d --- /dev/null +++ b/Controllers/ASRockPolychromeUSBController/RGBController_ASRockPolychromeUSB.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_ASRockPolychromeUSB.h | +| | +| RGBController for ASRock Polychrome USB motherboards | +| | +| Ed Kambulow (dredvard) 20 Dec 2020 | +| Shady Nawara (ShadyNawara) 16 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ASRockPolychromeUSBController.h" + +class RGBController_PolychromeUSB : public RGBController +{ +public: + RGBController_PolychromeUSB(PolychromeUSBController* controller_ptr); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PolychromeUSBController* controller; + std::vector zones_info; + + unsigned char GetDeviceMode(unsigned char zone); +}; diff --git a/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/ASRockASRRGBSMBusController.cpp b/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/ASRockASRRGBSMBusController.cpp new file mode 100644 index 0000000..35fdb34 --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/ASRockASRRGBSMBusController.cpp @@ -0,0 +1,124 @@ +/*---------------------------------------------------------*\ +| ASRockASRRGBSMBusController.cpp | +| | +| Driver for SMBus ASRock ASR RGB motherboards | +| | +| Adam Honse (CalcProgrammer1) 14 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "ASRockASRRGBSMBusController.h" +#include +#include "dmiinfo.h" +#include "LogManager.h" + +#define ASROCK_ZONE_LED_COUNT_MESSAGE_EN "[%s] Zone %i LED count: %02d" + +using namespace std::chrono_literals; + +ASRockASRRGBSMBusController::ASRockASRRGBSMBusController(i2c_smbus_interface* bus, polychrome_dev_id dev) +{ + this->bus = bus; + this->dev = dev; + + DMIInfo dmi; + + device_name = "ASRock " + dmi.getMainboard(); + +} + +ASRockASRRGBSMBusController::~ASRockASRRGBSMBusController() +{ + +} + +std::string ASRockASRRGBSMBusController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string ASRockASRRGBSMBusController::GetDeviceName() +{ + return(device_name); +} + +std::string ASRockASRRGBSMBusController::GetFirmwareVersion() +{ + uint8_t major_version = fw_version >> 8; + uint8_t minor_version = fw_version & 0xFF; + + return(std::to_string(major_version) + "." + std::to_string(minor_version)); +} + +uint8_t ASRockASRRGBSMBusController::GetMode() +{ + return(active_mode); +} + +void ASRockASRRGBSMBusController::SetColorsAndSpeed(uint8_t led, uint8_t red, uint8_t green, uint8_t blue) +{ + uint8_t color_speed_pkt[4] = { red, green, blue, active_speed }; + uint8_t select_led_pkt[1] = { led }; + + /*-----------------------------------------------------*\ + | Select LED | + \*-----------------------------------------------------*/ + if(active_mode != ASRLED_MODE_OFF) + { + bus->i2c_smbus_write_block_data(dev, ASROCK_ASR_REG_LED_SELECT, 1, select_led_pkt); + std::this_thread::sleep_for(1ms); + } + + switch(active_mode) + { + /*-----------------------------------------------------*\ + | These modes take 4 bytes in R/G/B/S order | + \*-----------------------------------------------------*/ + case ASRLED_MODE_BREATHING: + case ASRLED_MODE_STROBE: + case ASRLED_MODE_SPECTRUM_CYCLE: + bus->i2c_smbus_write_block_data(dev, active_mode, 4, color_speed_pkt); + break; + + /*-----------------------------------------------------*\ + | These modes take 3 bytes in R/G/B order | + \*-----------------------------------------------------*/ + default: + case ASRLED_MODE_STATIC: + case ASRLED_MODE_MUSIC: + bus->i2c_smbus_write_block_data(dev, active_mode, 3, color_speed_pkt); + break; + + /*-----------------------------------------------------*\ + | These modes take 1 byte - speed | + \*-----------------------------------------------------*/ + case ASRLED_MODE_RANDOM: + case ASRLED_MODE_WAVE: + bus->i2c_smbus_write_block_data(dev, active_mode, 1, &active_speed); + break; + + /*-----------------------------------------------------*\ + | These modes take no bytes | + \*-----------------------------------------------------*/ + case ASRLED_MODE_OFF: + break; + } + std::this_thread::sleep_for(1ms); +} + +void ASRockASRRGBSMBusController::SetMode(uint8_t zone,uint8_t mode, uint8_t speed) +{ + active_zone = zone; + active_mode = mode; + active_speed = speed; + + bus->i2c_smbus_write_block_data(dev, ASROCK_ASR_REG_MODE, 1, &active_mode); + std::this_thread::sleep_for(1ms); +} diff --git a/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/ASRockASRRGBSMBusController.h b/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/ASRockASRRGBSMBusController.h new file mode 100644 index 0000000..405e535 --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/ASRockASRRGBSMBusController.h @@ -0,0 +1,77 @@ +/*---------------------------------------------------------*\ +| ASRockASRRGBSMBusController.h | +| | +| Driver for SMBus ASRock ASR RGB motherboards | +| | +| Adam Honse (CalcProgrammer1) 13 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef uint8_t polychrome_dev_id; + +#define ASROCK_ASR_CONTROLLER_NAME "ASRock ASR RGB SMBus Controller" + +enum +{ + /*------------------------------------------------------------------------------------------*\ + | ASRock Common Registers | + \*------------------------------------------------------------------------------------------*/ + ASROCK_ASR_REG_FIRMWARE_VER = 0x00, /* Firmware version Major.Minor */ + ASROCK_ASR_REG_MODE = 0x30, /* Mode selection register */ + ASROCK_ASR_REG_LED_SELECT = 0x31, /* LED selection register */ +}; + +/*----------------------------------------------------------------------------------------------*\ +| Definitions for ASR LED | +\*----------------------------------------------------------------------------------------------*/ +#define ASRLED_NUM_MODES 8 /* Number of ASR LED modes */ + +enum +{ + ASRLED_MODE_OFF = 0x10, /* OFF mode */ + ASRLED_MODE_STATIC = 0x11, /* Static color mode */ + ASRLED_MODE_BREATHING = 0x12, /* Breathing effect mode */ + ASRLED_MODE_STROBE = 0x13, /* Strobe effect mode */ + ASRLED_MODE_SPECTRUM_CYCLE = 0x14, /* Spectrum Cycle effect mode */ + ASRLED_MODE_RANDOM = 0x15, /* Random effect mode */ + ASRLED_MODE_MUSIC = 0x17, /* Music effect mode */ + ASRLED_MODE_WAVE = 0x18, /* Wave effect mode */ +}; + +enum +{ + ASRLED_SPEED_MIN = 0x05, /* Slowest speed */ + ASRLED_SPEED_DEFAULT = 0x03, /* Default speed */ + ASRLED_SPEED_MAX = 0x00, /* Fastest speed */ +}; + +class ASRockASRRGBSMBusController +{ +public: + ASRockASRRGBSMBusController(i2c_smbus_interface* bus, polychrome_dev_id dev); + ~ASRockASRRGBSMBusController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetFirmwareVersion(); + uint8_t GetMode(); + void SetColorsAndSpeed(uint8_t led, uint8_t red, uint8_t green, uint8_t blue); + void SetMode(uint8_t zone, uint8_t mode, uint8_t speed); + + uint16_t fw_version; + +private: + std::string device_name; + uint8_t active_zone; + uint8_t active_mode; + uint8_t active_speed; + i2c_smbus_interface* bus; + polychrome_dev_id dev; +}; diff --git a/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/RGBController_ASRockASRRGBSMBus.cpp b/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/RGBController_ASRockASRRGBSMBus.cpp new file mode 100644 index 0000000..8ad74de --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/RGBController_ASRockASRRGBSMBus.cpp @@ -0,0 +1,207 @@ +/*---------------------------------------------------------*\ +| RGBController_ASRRGBSMBus.cpp | +| | +| RGBController for SMBus ASRock ASR LED motherboards | +| | +| Adam Honse (CalcProgrammer1) 15 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ASRockASRRGBSMBus.h" + +#define ASROCK_MAX_ZONES 4 +#define ASROCK_MAX_LEDS 22 + +/**------------------------------------------------------------------*\ + @name ASRock ASR RGB SMBus + @category Motherboard + @type SMBus + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectASRockSMBusControllers + @comment ASRock ASR RGB LED controllers will save with each update. + Per ARGB LED support is not possible with these devices. +\*-------------------------------------------------------------------*/ + +RGBController_ASRockASRRGBSMBus::RGBController_ASRockASRRGBSMBus(ASRockASRRGBSMBusController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "ASRock"; + version = controller->GetFirmwareVersion(); + type = DEVICE_TYPE_MOTHERBOARD; + description = "ASRock ASR RGB LED Device"; + location = controller->GetDeviceLocation(); + + mode Off; + Off.name = "Off"; + Off.value = ASRLED_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = ASRLED_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ASRLED_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = ASRLED_SPEED_MIN; + Breathing.speed_max = ASRLED_SPEED_MAX; + Breathing.speed = ASRLED_SPEED_DEFAULT; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Strobe; + Strobe.name = "Strobe"; + Strobe.value = ASRLED_MODE_STROBE; + Strobe.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Strobe.speed_min = ASRLED_SPEED_MIN; + Strobe.speed_max = ASRLED_SPEED_MAX; + Strobe.speed = ASRLED_SPEED_DEFAULT; + Strobe.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Strobe); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = ASRLED_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.speed_min = ASRLED_SPEED_MIN; + SpectrumCycle.speed_max = ASRLED_SPEED_MAX; + SpectrumCycle.speed = ASRLED_SPEED_DEFAULT; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Random; + Random.name = "Random"; + Random.value = ASRLED_MODE_RANDOM; + Random.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Random.speed_min = ASRLED_SPEED_MIN; + Random.speed_max = ASRLED_SPEED_MAX; + Random.speed = ASRLED_SPEED_DEFAULT; + Random.color_mode = MODE_COLORS_NONE; + modes.push_back(Random); + + mode Music; + Music.name = "Music"; + Music.value = ASRLED_MODE_MUSIC; + Music.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Music.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Music); + + mode Wave; + Wave.name = "Wave"; + Wave.value = ASRLED_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Wave.speed_min = ASRLED_SPEED_MIN; + Wave.speed_max = ASRLED_SPEED_MAX; + Wave.speed = ASRLED_SPEED_DEFAULT; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + + SetupZones(); +} + +RGBController_ASRockASRRGBSMBus::~RGBController_ASRockASRRGBSMBus() +{ + delete controller; +} + +void RGBController_ASRockASRRGBSMBus::SetupZones() +{ + /*---------------------------------------------------------*\ + | ASR LED motherboards only have a single zone/LED | + \*---------------------------------------------------------*/ + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + + /*---------------------------------------------------------*\ + | Set single zone name to "Motherboard" | + \*---------------------------------------------------------*/ + new_zone->name = "Motherboard"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + /*---------------------------------------------------------*\ + | Push new zone to zones vector | + \*---------------------------------------------------------*/ + zones.push_back(*new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + led* new_led = new led(); + + /*---------------------------------------------------------*\ + | Set single LED name to "Motherboard" | + \*---------------------------------------------------------*/ + new_led->name = "Motherboard"; + + /*---------------------------------------------------------*\ + | Push new LED to LEDs vector | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + + SetupColors(); +} + +void RGBController_ASRockASRRGBSMBus::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ASRockASRRGBSMBus::DeviceUpdateLEDs() +{ + for(unsigned int led = 0; led < colors.size(); led++) + { + UpdateSingleLED(led); + } +} + +void RGBController_ASRockASRRGBSMBus::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ASRockASRRGBSMBus::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + /*---------------------------------------------------------*\ + | If the LED value is non-zero, this LED overrides the LED | + | index | + \*---------------------------------------------------------*/ + if(leds[led].value != 0) + { + led = leds[led].value; + } + + controller->SetColorsAndSpeed(led, red, grn, blu); +} + +void RGBController_ASRockASRRGBSMBus::DeviceUpdateMode() +{ + + controller->SetMode(0, modes[active_mode].value, modes[active_mode].speed); + + DeviceUpdateLEDs(); +} diff --git a/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/RGBController_ASRockASRRGBSMBus.h b/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/RGBController_ASRockASRRGBSMBus.h new file mode 100644 index 0000000..8a192e7 --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/RGBController_ASRockASRRGBSMBus.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_ASRRGBSMBus.h | +| | +| RGBController for SMBus ASRock ASR RGB motherboards | +| | +| Adam Honse (CalcProgrammer1) 15 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ASRockASRRGBSMBusController.h" + +class RGBController_ASRockASRRGBSMBus : public RGBController +{ +public: + RGBController_ASRockASRRGBSMBus(ASRockASRRGBSMBusController* controller_ptr); + ~RGBController_ASRockASRRGBSMBus(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ASRockASRRGBSMBusController* controller; +}; diff --git a/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/ASRockPolychromeV1SMBusController.cpp b/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/ASRockPolychromeV1SMBusController.cpp new file mode 100644 index 0000000..30fec54 --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/ASRockPolychromeV1SMBusController.cpp @@ -0,0 +1,310 @@ +/*---------------------------------------------------------*\ +| ASRockPolychromeV1SMBusController.cpp | +| | +| Driver for SMBus ASRock Polychrome V1 motherboards | +| | +| Adam Honse (CalcProgrammer1) 14 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "ASRockPolychromeV1SMBusController.h" +#include "dmiinfo.h" +#include "LogManager.h" + +#define ASROCK_ZONE_LED_COUNT_MESSAGE_EN "[%s] Zone %i LED count: %02d" + +using namespace std::chrono_literals; + +ASRockPolychromeV1SMBusController::ASRockPolychromeV1SMBusController(i2c_smbus_interface* bus, polychrome_dev_id dev) +{ + this->bus = bus; + this->dev = dev; + + DMIInfo dmi; + device_name = "ASRock " + dmi.getMainboard(); + + ReadLEDConfiguration(); +} + +ASRockPolychromeV1SMBusController::~ASRockPolychromeV1SMBusController() +{ + +} + +std::string ASRockPolychromeV1SMBusController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string ASRockPolychromeV1SMBusController::GetDeviceName() +{ + return(device_name); +} + +std::string ASRockPolychromeV1SMBusController::GetFirmwareVersion() +{ + uint8_t major_version = fw_version >> 8; + uint8_t minor_version = fw_version & 0xFF; + + return(std::to_string(major_version) + "." + std::to_string(minor_version)); +} + +void ASRockPolychromeV1SMBusController::ReadLEDConfiguration() +{ + /*---------------------------------------------------------------------------------*\ + | The LED configuration register holds 6 bytes, so the first read should return 6 | + | If not, set all zone sizes to zero | + \*---------------------------------------------------------------------------------*/ + LOG_DEBUG("[%s] Reading Zone sizes from controller", device_name.c_str()); + uint8_t asrock_zone_count[I2C_SMBUS_BLOCK_MAX] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; + if (bus->i2c_smbus_read_block_data(dev, POLYCHROME_V1_REG_ZONE_SIZE, asrock_zone_count) == 0x06) + { + zone_led_count[POLYCHROME_V1_ZONE_1] = asrock_zone_count[0]; + zone_led_count[POLYCHROME_V1_ZONE_2] = asrock_zone_count[1]; + zone_led_count[POLYCHROME_V1_ZONE_3] = asrock_zone_count[2]; + zone_led_count[POLYCHROME_V1_ZONE_4] = asrock_zone_count[3]; + zone_led_count[POLYCHROME_V1_ZONE_5] = asrock_zone_count[4]; + zone_led_count[POLYCHROME_V1_ZONE_ADDRESSABLE] = asrock_zone_count[5]; + LOG_DEBUG(ASROCK_ZONE_LED_COUNT_MESSAGE_EN, device_name.c_str(), POLYCHROME_V1_ZONE_1, zone_led_count[POLYCHROME_V1_ZONE_1]); + LOG_DEBUG(ASROCK_ZONE_LED_COUNT_MESSAGE_EN, device_name.c_str(), POLYCHROME_V1_ZONE_2, zone_led_count[POLYCHROME_V1_ZONE_2]); + LOG_DEBUG(ASROCK_ZONE_LED_COUNT_MESSAGE_EN, device_name.c_str(), POLYCHROME_V1_ZONE_3, zone_led_count[POLYCHROME_V1_ZONE_3]); + LOG_DEBUG(ASROCK_ZONE_LED_COUNT_MESSAGE_EN, device_name.c_str(), POLYCHROME_V1_ZONE_4, zone_led_count[POLYCHROME_V1_ZONE_4]); + LOG_DEBUG(ASROCK_ZONE_LED_COUNT_MESSAGE_EN, device_name.c_str(), POLYCHROME_V1_ZONE_5, zone_led_count[POLYCHROME_V1_ZONE_5]); + LOG_DEBUG("[%s] Addressable Zone LED count: %02d", device_name.c_str(), zone_led_count[POLYCHROME_V1_ZONE_ADDRESSABLE]); + } + else + { + LOG_WARNING("[%s] LED config read failed", device_name.c_str()); + memset(zone_led_count, 0, sizeof(zone_led_count)); + } +} + +uint8_t ASRockPolychromeV1SMBusController::GetARGBColorOrder() +{ + uint8_t temp[1] = { 0x00 }; + LOG_TRACE("[%s] Reading ARGB color order config from the controller", device_name.c_str()); + + //Read the data + if(bus->i2c_smbus_read_block_data(dev, POLYCHROME_V1_REG_ARGB_GRB, temp) == 0x01) + { + if(temp[0] == 1) + { + LOG_DEBUG("[%s] Color order is GRB for the ARGB header", device_name.c_str()); + } + else + { + LOG_DEBUG("[%s] Color order is RGB for the ARGB header", device_name.c_str()); + } + return temp[0]; + } + else + { + return 0; + } +} + +RGBColor ASRockPolychromeV1SMBusController::GetZoneColor(uint8_t zone) +{ + LOG_TRACE("[%s] Reading color from zone %02d", device_name.c_str(), zone); + return zone_config[zone].color; +} + +uint8_t ASRockPolychromeV1SMBusController::GetZoneMode(uint8_t zone) +{ + LOG_TRACE("[%s] Retreving mode %02X from zone_modes for zone %02d", device_name.c_str(), zone_config[zone].mode, zone); + return(zone_config[zone].mode); +} + +void ASRockPolychromeV1SMBusController::LoadZoneConfig() +{ + uint8_t zone[1] = { 0x00 }; + uint8_t mode[1] = { 0xFF }; + uint8_t color_speed_pkt[4] = { 0, 0, 0, 0 }; + + LOG_TRACE("[%s] Reading modes from all zones", device_name.c_str()); + //Polychrome v1 supports per zone modes so we need to set the zone before we can read the mode. + //Write the zone index. + + for(uint8_t zone_idx = 0 ; zone_idx < POLYCHROME_V1_ZONE_COUNT; zone_idx ++) + { + if(zone_led_count[zone_idx] > 0) + { + zone[0] = zone_idx; + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V1_REG_ZONE_SELECT, 1, zone); + + //Read the data back. + if(bus->i2c_smbus_read_block_data(dev, POLYCHROME_V1_REG_ZONE_SELECT, zone) == 0x01) + { + //Validate that we changed correctly. + if(zone[0] == zone_idx) + { + //Read the mode for the zone. + if(bus->i2c_smbus_read_block_data(dev, POLYCHROME_V1_REG_MODE, mode) == 0x01) + { + LOG_DEBUG("[%s] Mode 0x%02x for zone %02d", device_name.c_str(), mode[0], zone_idx); + zone_config[zone_idx].mode = mode[0]; + + bus->i2c_smbus_read_block_data(dev, zone_config[zone_idx].mode, color_speed_pkt); + + zone_config[zone_idx].color = color_speed_pkt[0] << 16 | color_speed_pkt[1] << 8 | color_speed_pkt[2]; + zone_config[zone_idx].speed = color_speed_pkt [3]; + + LOG_TRACE("[%s] Mode config: %06X, %02X", device_name.c_str(), zone_config[zone_idx].color, zone_config[zone_idx].speed ); + } + } + else + { + LOG_WARNING("[%s] Zone mode register failed to change!", device_name.c_str() ); + } + } + } + else + { + zone_config[zone_idx].mode = 0; + zone_config[zone_idx].color = 0; + zone_config[zone_idx].speed = 0; + } + } +} + +void ASRockPolychromeV1SMBusController::SetARGBColorOrder(bool value) +{ + uint8_t temp[1] = { 0x00 }; + + LOG_TRACE("[%s] Setting ARGB color order config to the controller", device_name.c_str()); + + temp[0] = (value) ? 0x01 : 0x00; + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V1_REG_ARGB_GRB, 1, temp); + GetARGBColorOrder(); +} + +bool ASRockPolychromeV1SMBusController::SetARGBSize(uint8_t new_size) +{ + LOG_DEBUG("[%s] Setting ARGB header to %02d.", device_name.c_str(), new_size); + + uint8_t asrock_zone_count[6] = { 0x0 }; + uint8_t new_asrock_zone_count[6] = { 0x0 }; + + //memcpy(new_asrock_zone_count, zone_led_count, sizeof(new_asrock_zone_count - 1)); + new_asrock_zone_count[POLYCHROME_V1_ZONE_1] = zone_led_count[POLYCHROME_V1_ZONE_1]; + new_asrock_zone_count[POLYCHROME_V1_ZONE_2] = zone_led_count[POLYCHROME_V1_ZONE_2]; + new_asrock_zone_count[POLYCHROME_V1_ZONE_3] = zone_led_count[POLYCHROME_V1_ZONE_3]; + new_asrock_zone_count[POLYCHROME_V1_ZONE_4] = zone_led_count[POLYCHROME_V1_ZONE_4]; + new_asrock_zone_count[POLYCHROME_V1_ZONE_5] = zone_led_count[POLYCHROME_V1_ZONE_5]; + new_asrock_zone_count[POLYCHROME_V1_ZONE_ADDRESSABLE] = new_size; + + //Write the new config to the register + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V1_REG_ZONE_SIZE, 6, new_asrock_zone_count); + + //Validate the write + if (bus->i2c_smbus_read_block_data(dev, POLYCHROME_V1_REG_ZONE_SIZE, asrock_zone_count) == 0x06) + { + for (uint8_t i = 0; i < 6; i++) + { + if (new_asrock_zone_count[i] != asrock_zone_count[i]) + { + LOG_WARNING("[%s] Failed to validate zone %02d size!", device_name.c_str(), i); + return false; + } + } + LOG_DEBUG("[%s] Zone configuration validation completed.", device_name.c_str()); + return true; + } + return false; +} + +void ASRockPolychromeV1SMBusController::SetColorsAndSpeed(uint8_t zone, uint8_t red, uint8_t green, uint8_t blue) +{ + LOG_TRACE("[%s] Updating color and speed for zone %02d: 0x%06X", device_name.c_str(), zone, (red << 16 | green << 8 | blue)); + uint8_t color_speed_pkt[4] = { red, green, blue, zone_config[zone].speed }; + uint8_t select_zone_pkt[1] = { zone }; + + /*-----------------------------------------------------*\ + | Select Zone | + \*-----------------------------------------------------*/ + if(zone_config[zone].mode != POLYCHROME_V1_MODE_OFF) + { + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V1_REG_ZONE_SELECT, 1, select_zone_pkt); + std::this_thread::sleep_for(1ms); + } + + switch(zone_config[zone].mode) + { + /*-----------------------------------------------------*\ + | These modes take 4 bytes in R/G/B/S order | + \*-----------------------------------------------------*/ + case POLYCHROME_V1_MODE_BREATHING: + case POLYCHROME_V1_MODE_STROBE: + case POLYCHROME_V1_MODE_SPECTRUM_CYCLE: + case POLYCHROME_V1_MODE_SPRING: + case POLYCHROME_V1_MODE_METEOR: + case POLYCHROME_V1_MODE_STACK: + case POLYCHROME_V1_MODE_CRAM: + case POLYCHROME_V1_MODE_SCAN: + case POLYCHROME_V1_MODE_NEON: + case POLYCHROME_V1_MODE_WATER: + + bus->i2c_smbus_write_block_data(dev, zone_config[zone].mode, 4, color_speed_pkt); + break; + + /*-----------------------------------------------------*\ + | These modes take 3 bytes in R/G/B order | + \*-----------------------------------------------------*/ + default: + case POLYCHROME_V1_MODE_STATIC: + case POLYCHROME_V1_MODE_MUSIC: + bus->i2c_smbus_write_block_data(dev, zone_config[zone].mode, 3, color_speed_pkt); + break; + + /*-----------------------------------------------------*\ + | These modes take 1 byte - speed | + \*-----------------------------------------------------*/ + case POLYCHROME_V1_MODE_RANDOM: + case POLYCHROME_V1_MODE_WAVE: + case POLYCHROME_V1_MODE_RAINBOW: + bus->i2c_smbus_write_block_data(dev, zone_config[zone].mode, 1, &zone_config[zone].speed); + break; + + /*-----------------------------------------------------*\ + | These modes take no bytes | + \*-----------------------------------------------------*/ + case POLYCHROME_V1_MODE_OFF: + break; + } + std::this_thread::sleep_for(1ms); + +} + +void ASRockPolychromeV1SMBusController::SetMode(uint8_t zone, uint8_t mode, uint8_t speed) +{ + LOG_TRACE("[%s] Updating mode for zone %02d, Mode 0x%02X, Speed 0x%02X", device_name.c_str(), zone, mode, speed); + uint8_t led_count_pkt[1] = { 0x00 }; + zone_config[zone].mode = mode; + zone_config[zone].speed = speed; + + /*-----------------------------------------------------*\ + | Make sure set all register is set to 0 | + \*-----------------------------------------------------*/ + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V1_REG_SET_ALL, 1, led_count_pkt); + std::this_thread::sleep_for(1ms); + + /*-----------------------------------------------------*\ + | Set the zone we are working on | + \*-----------------------------------------------------*/ + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V1_REG_ZONE_SELECT, 1, &zone); + std::this_thread::sleep_for(1ms); + + /*-----------------------------------------------------*\ + | Write the mode | + \*-----------------------------------------------------*/ + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V1_REG_MODE, 1, &zone_config[zone].mode); + std::this_thread::sleep_for(1ms); +} diff --git a/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/ASRockPolychromeV1SMBusController.h b/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/ASRockPolychromeV1SMBusController.h new file mode 100644 index 0000000..2167ad3 --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/ASRockPolychromeV1SMBusController.h @@ -0,0 +1,160 @@ +/*---------------------------------------------------------*\ +| ASRockPolychromeV1SMBusController.h | +| | +| Driver for SMBus ASRock Polychrome V1 motherboards | +| | +| Adam Honse (CalcProgrammer1) 13 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +typedef uint8_t polychrome_dev_id; + +#define ASROCK_V1_CONTROLLER_NAME "ASRock Polychrome v1 SMBus Controller" + +enum +{ + /*------------------------------------------------------------------------------------------*\ + | ASRock Polychrome v1 Registers | + \*------------------------------------------------------------------------------------------*/ + POLYCHROME_V1_REG_FIRMWARE_VER = 0x00, /* Firmware version Major.Minor */ + POLYCHROME_V1_REG_MODE = 0x30, /* Mode selection register */ + POLYCHROME_V1_REG_ZONE_SELECT = 0x31, /* Zone selection register */ + POLYCHROME_V1_REG_SET_ALL = 0x32, /* Set All register 0x1 = set all */ + POLYCHROME_V1_REG_ZONE_SIZE = 0x33, /* Zone size configuration register */ + POLYCHROME_V1_REG_ARGB_GRB = 0x35, /* ARGB bitstream reversing register */ +}; + +enum +{ + POLYCHROME_V1_ZONE_1 = 0x00, /* RGB LED 1 Header */ + POLYCHROME_V1_ZONE_2 = 0x01, /* RGB LED 2 Header */ + POLYCHROME_V1_ZONE_3 = 0x02, /* PCH Zone */ + POLYCHROME_V1_ZONE_4 = 0x03, /* IO Cover Zone */ + POLYCHROME_V1_ZONE_5 = 0x04, /* Audio Zone LEDs */ + POLYCHROME_V1_ZONE_ADDRESSABLE = 0x05, /* Addressable LED header */ + POLYCHROME_V1_ZONE_COUNT = 0x06, /* Total number of zones */ + POLYCHROME_V1_ZONE_ADDRESSABLE_MAX = 0x64, /* Maximum number of ARGB LEDs */ +}; + +/*----------------------------------------------------------------------------------------------*\ +| Definitions for Polychrome V1 | +\*----------------------------------------------------------------------------------------------*/ +#define POLYCHROME_V1_NUM_MODES 16 /* Number of Polychrome V1 modes */ + +enum +{ + POLYCHROME_V1_MODE_OFF = 0x10, /* OFF mode */ + POLYCHROME_V1_MODE_STATIC = 0x11, /* Static color mode */ + POLYCHROME_V1_MODE_BREATHING = 0x12, /* Breathing effect mode */ + POLYCHROME_V1_MODE_STROBE = 0x13, /* Strobe effect mode */ + POLYCHROME_V1_MODE_SPECTRUM_CYCLE = 0x14, /* Spectrum Cycle effect mode */ + POLYCHROME_V1_MODE_RANDOM = 0x15, /* Random effect mode */ + POLYCHROME_V1_MODE_MUSIC = 0x17, /* Music effect mode */ + POLYCHROME_V1_MODE_WAVE = 0x18, /* Wave effect mode */ + /*------------------------------------------------------------------------------------------*\ + | Modes only available on ARGB headers | + \*------------------------------------------------------------------------------------------*/ + POLYCHROME_V1_MODE_SPRING = 0x19, /* Spring effect mode */ + POLYCHROME_V1_MODE_METEOR = 0x1A, /* Meteor effect mode */ + POLYCHROME_V1_MODE_STACK = 0x1B, /* Stack effect mode */ + POLYCHROME_V1_MODE_CRAM = 0x1C, /* Cram effect mode */ + POLYCHROME_V1_MODE_SCAN = 0x1D, /* Scan effect mode */ + POLYCHROME_V1_MODE_NEON = 0x1E, /* Neon effect mode */ + POLYCHROME_V1_MODE_WATER = 0x1F, /* Water effect mode */ + POLYCHROME_V1_MODE_RAINBOW = 0x20, /* Rainbow chase effect mode */ +}; + +enum +{ + /*------------------------------------------------------------------------------------------*\ + | POLYCHROME_V1_MODE_BREATHING | + \*------------------------------------------------------------------------------------------*/ + POLYCHROME_V1_SPEED_MIN_BREATHING = 0x0A, /* Slowest speed */ + POLYCHROME_V1_SPEED_DEFAULT_BREATHING = 0x02, /* Default speed */ + POLYCHROME_V1_SPEED_MAX_BREATHING = 0x02, /* Fastest speed */ + /*------------------------------------------------------------------------------------------*\ + | POLYCHROME_V1_MODE_STROBE | + \*------------------------------------------------------------------------------------------*/ + POLYCHROME_V1_SPEED_MIN_STROBE = 0xA0, /* Slowest speed */ + POLYCHROME_V1_SPEED_DEFAULT_STROBE = 0x14, /* Default speed */ + POLYCHROME_V1_SPEED_MAX_STROBE = 0x05, /* Fastest speed */ + /*------------------------------------------------------------------------------------------*\ + | POLYCHROME_V1_MODE_SPECTRUM_CYCLE | + \*------------------------------------------------------------------------------------------*/ + POLYCHROME_V1_SPEED_MIN_CYCLE = 0xA0, /* Slowest speed */ + POLYCHROME_V1_SPEED_DEFAULT_CYCLE = 0x14, /* Default speed */ + POLYCHROME_V1_SPEED_MAX_CYCLE = 0x0A, /* Fastest speed */ + /*------------------------------------------------------------------------------------------*\ + | POLYCHROME_V1_MODE_RANDOM | + \*------------------------------------------------------------------------------------------*/ + POLYCHROME_V1_SPEED_MIN_RANDOM = 0xA0, /* Slowest speed */ + POLYCHROME_V1_SPEED_DEFAULT_RANDOM = 0x28, /* Default speed */ + POLYCHROME_V1_SPEED_MAX_RANDOM = 0x05, /* Fastest speed */ + /*------------------------------------------------------------------------------------------*\ + | POLYCHROME_V1_MODE_WAVE | + \*------------------------------------------------------------------------------------------*/ + POLYCHROME_V1_SPEED_MIN_WAVE = 0x06, /* Slowest speed */ + POLYCHROME_V1_SPEED_DEFAULT_WAVE = 0x02, /* Default speed */ + POLYCHROME_V1_SPEED_MAX_WAVE = 0x01, /* Fastest speed */ + POLYCHROME_V1_SPEED_DEFAULT_SPRING = 0x04, /* Default speed */ + POLYCHROME_V1_SPEED_DEFAULT_METEOR = 0x0A, /* Default speed */ + POLYCHROME_V1_SPEED_DEFAULT_STACK = 0x04, /* Default speed */ + POLYCHROME_V1_SPEED_DEFAULT_CRAM = 0x04, /* Default speed */ + POLYCHROME_V1_SPEED_DEFAULT_SCAN = 0x0A, /* Default speed */ + POLYCHROME_V1_SPEED_DEFAULT_NEON = 0x20, /* Default speed */ + POLYCHROME_V1_SPEED_DEFAULT_WATER = 0x0A, /* Default speed */ + /*------------------------------------------------------------------------------------------*\ + | POLYCHROME_V1_MODE_RAINBOW | + \*------------------------------------------------------------------------------------------*/ + POLYCHROME_V1_SPEED_MIN_RAINBOW = 0x12, /* Slowest speed */ + POLYCHROME_V1_SPEED_DEFAULT_RAINBOW = 0x0A, /* Default speed */ + POLYCHROME_V1_SPEED_MAX_RAINBOW = 0x01, /* Fastest speed */ + POLYCHROME_V1_SPEED_MIN_ARGB = 0x20, /* Slowest speed */ + POLYCHROME_V1_SPEED_MAX_ARGB = 0x02, /* Fastest speed */ +}; + +struct zone_cfg +{ + uint8_t mode; + uint8_t speed; + RGBColor color; +}; + +class ASRockPolychromeV1SMBusController +{ +public: + ASRockPolychromeV1SMBusController(i2c_smbus_interface* bus, polychrome_dev_id dev); + ~ASRockPolychromeV1SMBusController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetFirmwareVersion(); + + uint8_t GetARGBColorOrder(); + RGBColor GetZoneColor(uint8_t zone); + uint8_t GetZoneMode(uint8_t zone); + void LoadZoneConfig(); + void SetARGBColorOrder(bool value); + bool SetARGBSize(uint8_t led_count); + void SetColorsAndSpeed(uint8_t led, uint8_t red, uint8_t green, uint8_t blue); + void SetMode(uint8_t zone, uint8_t mode, uint8_t speed); + + uint8_t zone_led_count[6]; + zone_cfg zone_config[6]; + uint16_t fw_version; + +private: + std::string device_name; + i2c_smbus_interface* bus; + polychrome_dev_id dev; + + void ReadLEDConfiguration(); +}; diff --git a/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/RGBController_ASRockPolychromeV1SMBus.cpp b/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/RGBController_ASRockPolychromeV1SMBus.cpp new file mode 100644 index 0000000..400c565 --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/RGBController_ASRockPolychromeV1SMBus.cpp @@ -0,0 +1,349 @@ +/*---------------------------------------------------------*\ +| RGBController_ASRockPolychromeV1SMBus.cpp | +| | +| RGBController for SMBus ASRock Polychrome V1 | +| motherboards | +| | +| Adam Honse (CalcProgrammer1) 15 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "RGBController_ASRockPolychromeV1SMBus.h" + +static const char* polychrome_v1_zone_names[] = +{ + "RGB LED 1 Header", + "RGB LED 2 Header", + "PCH", + "IO Cover", + "Audio", + "Addressable Header" +}; + +/**------------------------------------------------------------------*\ + @name ASRock Polychrome v1 SMBus + @category Motherboard + @type SMBus + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectASRockSMBusControllers + @comment ASRock Polychrome v1 controllers will save with each update. + Per ARGB LED support is not possible with these devices. + ARGB size and color order is set using the `ARGB Header Config mode` + `Right` = GRB mode that is needed for WS2812B ARGB devices and `Left` = RGB used for WS2811 strips + The modes speed slider will set the size of the header + Spectrum Cycles uses the RGB values to set the individual color brightness. +\*-------------------------------------------------------------------*/ + +RGBController_ASRockPolychromeV1SMBus::RGBController_ASRockPolychromeV1SMBus(ASRockPolychromeV1SMBusController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "ASRock"; + version = controller->GetFirmwareVersion(); + type = DEVICE_TYPE_MOTHERBOARD; + description = "ASRock Polychrome v1 Device"; + location = controller->GetDeviceLocation(); + + + mode Off; + Off.name = "Off"; + Off.value = POLYCHROME_V1_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = POLYCHROME_V1_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = POLYCHROME_V1_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = POLYCHROME_V1_SPEED_MIN_BREATHING; + Breathing.speed_max = POLYCHROME_V1_SPEED_MAX_BREATHING; + Breathing.speed = POLYCHROME_V1_SPEED_DEFAULT_BREATHING; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Strobe; + Strobe.name = "Strobe"; + Strobe.value = POLYCHROME_V1_MODE_STROBE; + Strobe.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Strobe.speed_min = POLYCHROME_V1_SPEED_MIN_STROBE; + Strobe.speed_max = POLYCHROME_V1_SPEED_MAX_STROBE; + Strobe.speed = POLYCHROME_V1_SPEED_DEFAULT_STROBE; + Strobe.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Strobe); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = POLYCHROME_V1_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.speed_min = POLYCHROME_V1_SPEED_MIN_CYCLE; + SpectrumCycle.speed_max = POLYCHROME_V1_SPEED_MAX_CYCLE; + SpectrumCycle.speed = POLYCHROME_V1_SPEED_DEFAULT_CYCLE; + SpectrumCycle.color_mode = MODE_COLORS_PER_LED; + modes.push_back(SpectrumCycle); + + mode Random; + Random.name = "Random"; + Random.value = POLYCHROME_V1_MODE_RANDOM; + Random.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Random.speed_min = POLYCHROME_V1_SPEED_MIN_RANDOM; + Random.speed_max = POLYCHROME_V1_SPEED_MAX_RANDOM; + Random.speed = POLYCHROME_V1_SPEED_DEFAULT_RANDOM; + Random.color_mode = MODE_COLORS_NONE; + modes.push_back(Random); + + mode Music; + Music.name = "Music"; + Music.value = POLYCHROME_V1_MODE_MUSIC; + Music.flags = MODE_FLAG_AUTOMATIC_SAVE; + Music.color_mode = MODE_COLORS_NONE; + modes.push_back(Music); + + mode Wave; + Wave.name = "Wave"; + Wave.value = POLYCHROME_V1_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Wave.speed_min = POLYCHROME_V1_SPEED_MIN_WAVE; + Wave.speed_max = POLYCHROME_V1_SPEED_MAX_WAVE; + Wave.speed = POLYCHROME_V1_SPEED_DEFAULT_WAVE; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + + /*---------------------------------------------------------------------*\ + | Comment out until per zone modes are working. These are only for ARGB | + \*---------------------------------------------------------------------*/ + mode Spring; + Spring.name = "Spring"; + Spring.value = POLYCHROME_V1_MODE_SPRING; + Spring.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Spring.speed_min = POLYCHROME_V1_SPEED_MIN_ARGB; + Spring.speed_max = POLYCHROME_V1_SPEED_MAX_ARGB; + Spring.speed = POLYCHROME_V1_SPEED_DEFAULT_SPRING; + Spring.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Spring); + + mode Stack; + Stack.name = "Stack"; + Stack.value = POLYCHROME_V1_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Stack.speed_min = POLYCHROME_V1_SPEED_MIN_ARGB; + Stack.speed_max = POLYCHROME_V1_SPEED_MAX_ARGB; + Stack.speed = POLYCHROME_V1_SPEED_DEFAULT_STACK; + Stack.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Stack); + + mode Cram; + Cram.name = "Cram"; + Cram.value = POLYCHROME_V1_MODE_CRAM; + Cram.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Cram.speed_min = POLYCHROME_V1_SPEED_MIN_ARGB; + Cram.speed_max = POLYCHROME_V1_SPEED_MAX_ARGB; + Cram.speed = POLYCHROME_V1_SPEED_DEFAULT_CRAM; + Cram.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Cram); + + mode Scan; + Scan.name = "Scan"; + Scan.value = POLYCHROME_V1_MODE_SCAN; + Scan.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Scan.speed_min = POLYCHROME_V1_SPEED_MIN_ARGB; + Scan.speed_max = POLYCHROME_V1_SPEED_MAX_ARGB; + Scan.speed = POLYCHROME_V1_SPEED_DEFAULT_SCAN; + Scan.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Scan); + + mode Neon; + Neon.name = "Neon"; + Neon.value = POLYCHROME_V1_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Neon.speed_min = POLYCHROME_V1_SPEED_MIN_ARGB; + Neon.speed_max = POLYCHROME_V1_SPEED_MAX_ARGB; + Neon.speed = POLYCHROME_V1_SPEED_DEFAULT_NEON; + Neon.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Neon); + + mode Water; + Water.name = "Water"; + Water.value = POLYCHROME_V1_MODE_WATER; + Water.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Water.speed_min = POLYCHROME_V1_SPEED_MIN_ARGB; + Water.speed_max = POLYCHROME_V1_SPEED_MAX_ARGB; + Water.speed = POLYCHROME_V1_SPEED_DEFAULT_WATER; + Water.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Water); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = POLYCHROME_V1_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.speed_min = POLYCHROME_V1_SPEED_MIN_RAINBOW; + Rainbow.speed_max = POLYCHROME_V1_SPEED_MAX_RAINBOW; + Rainbow.speed = POLYCHROME_V1_SPEED_DEFAULT_RAINBOW; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + /*---------------------------------------------------------------------*\ + | This ARGB_Config section is a hack to allow users to configure the | + | RGB vs GRB mode using the direction of the mode as well as set the | + | size of the devices connected to the header using the brightness | + | value. | + | | + | The mode should be removed onece the device settings are avaiable. | + \*---------------------------------------------------------------------*/ + mode ARGB_Config; + ARGB_Config.name = "ARGB Header Config"; + ARGB_Config.value = POLYCHROME_V1_REG_ARGB_GRB; + ARGB_Config.flags = MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + ARGB_Config.speed = controller->zone_led_count[POLYCHROME_V1_ZONE_ADDRESSABLE]; + ARGB_Config.speed_min = 1; + ARGB_Config.speed_max = POLYCHROME_V1_ZONE_ADDRESSABLE_MAX; + ARGB_Config.direction = controller -> GetARGBColorOrder(); + ARGB_Config.color_mode = MODE_COLORS_NONE; + modes.push_back(ARGB_Config); + + SetupZones(); + + controller->LoadZoneConfig(); + active_mode = getModeIndex(controller->zone_config[0].mode); // Hard coding zone 0 until per zone modes are available. + + if(active_mode != POLYCHROME_V1_MODE_OFF) + { + for( uint8_t zone_idx = 0; zone_idx < zoneIndexMap.size(); zone_idx++) + { + zones[zone_idx].colors[0] = controller->GetZoneColor(zoneIndexMap[zone_idx]); + } + } +} + +RGBController_ASRockPolychromeV1SMBus::~RGBController_ASRockPolychromeV1SMBus() +{ + delete controller; +} + +uint8_t RGBController_ASRockPolychromeV1SMBus::getModeIndex(uint8_t mode_value) +{ + for(uint8_t mode_index = 0; mode_index < modes.size(); mode_index++) + { + if (modes[mode_index].value == mode_value) + { + return mode_index; + } + } + return 0; +} + +void RGBController_ASRockPolychromeV1SMBus::SetupZones() +{ + /*---------------------------------------------------------*\ + | Polychrome motherboards should set up zones based on LED | + | configuration register read from device | + \*---------------------------------------------------------*/ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(uint8_t zone_idx = 0; zone_idx < POLYCHROME_V1_ZONE_COUNT; zone_idx++) + { + if(controller->zone_led_count[zone_idx] > 0) + { + zone* new_zone = new zone(); + /*---------------------------------------------------------*\ + | Set zone name to channel name | + \*---------------------------------------------------------*/ + new_zone->name = polychrome_v1_zone_names[zone_idx]; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + if(zone_idx == POLYCHROME_V1_ZONE_ADDRESSABLE) + { + new_zone->leds_max = POLYCHROME_V1_ZONE_ADDRESSABLE_MAX; + } + /*---------------------------------------------------------*\ + | Push new zone to zones vector | + \*---------------------------------------------------------*/ + zones.push_back(*new_zone); + + /*---------------------------------------------------------*\ + | Each zone only has one LED | + \*---------------------------------------------------------*/ + led* new_led = new led(); + + new_led->name = polychrome_v1_zone_names[zone_idx]; + new_led->value = zone_idx; + + /*---------------------------------------------------------*\ + | Push new LED to LEDs vector | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zoneIndexMap.push_back(zone_idx); + } + } + + SetupColors(); +} + +void RGBController_ASRockPolychromeV1SMBus::ResizeZone(int zone, int new_size) +{ + LOG_TRACE("[%s] ResizeZone(%02X, %02X)", name.c_str(), zone, new_size); + controller-> SetARGBSize(new_size & 0xFF); + zones[POLYCHROME_V1_ZONE_ADDRESSABLE].leds_count = 1; +} + +void RGBController_ASRockPolychromeV1SMBus::DeviceUpdateLEDs() +{ + LOG_TRACE("[%s] DeviceUpdateLEDs()", name.c_str()); + for (uint8_t zone_idx = 0; zone_idx < zoneIndexMap.size(); zone_idx++) + { + UpdateSingleLED(zone_idx); + } +} + +void RGBController_ASRockPolychromeV1SMBus::UpdateZoneLEDs(int /*zone*/) +{ + LOG_TRACE("[%s] UpdateZoneLEDs()", name.c_str()); + DeviceUpdateLEDs(); +} + +void RGBController_ASRockPolychromeV1SMBus::UpdateSingleLED(int zone) +{ + LOG_TRACE("[%s] UpdateSingleLED(%02X)", name.c_str(), zone); + + uint8_t red = RGBGetRValue(colors[zone]); + uint8_t grn = RGBGetGValue(colors[zone]); + uint8_t blu = RGBGetBValue(colors[zone]); + + controller->SetColorsAndSpeed(zoneIndexMap[zone], red, grn, blu); +} + +void RGBController_ASRockPolychromeV1SMBus::DeviceUpdateMode() +{ + LOG_TRACE("[%s] DeviceUpdateMode()", name.c_str()); + if(modes[active_mode].value != POLYCHROME_V1_REG_ARGB_GRB) + { + for(uint8_t zone_idx = 0; zone_idx < zoneIndexMap.size(); zone_idx++) + { + controller->SetMode(zoneIndexMap[zone_idx], modes[active_mode].value, modes[active_mode].speed); + UpdateSingleLED(zone_idx); + } + } + else + { + controller-> SetARGBColorOrder(modes[active_mode].direction); + controller-> SetARGBSize(modes[active_mode].speed); + } +} diff --git a/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/RGBController_ASRockPolychromeV1SMBus.h b/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/RGBController_ASRockPolychromeV1SMBus.h new file mode 100644 index 0000000..479922a --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/RGBController_ASRockPolychromeV1SMBus.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_ASRockPolychromeV1SMBus.h | +| | +| RGBController for SMBus ASRock Polychrome V1 | +| motherboards | +| | +| Adam Honse (CalcProgrammer1) 15 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ASRockPolychromeV1SMBusController.h" + +class RGBController_ASRockPolychromeV1SMBus : public RGBController +{ +public: + RGBController_ASRockPolychromeV1SMBus(ASRockPolychromeV1SMBusController* controller_ptr); + ~RGBController_ASRockPolychromeV1SMBus(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ASRockPolychromeV1SMBusController* controller; + uint8_t getModeIndex(uint8_t mode_value); + std::vector zoneIndexMap; +}; diff --git a/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/ASRockPolychromeV2SMBusController.cpp b/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/ASRockPolychromeV2SMBusController.cpp new file mode 100644 index 0000000..e56da1f --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/ASRockPolychromeV2SMBusController.cpp @@ -0,0 +1,158 @@ +/*---------------------------------------------------------*\ +| ASRockPolychromeV2SMBusController.cpp | +| | +| Driver for SMBus ASRock Polychrome V2 motherboards | +| | +| Adam Honse (CalcProgrammer1) 14 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "ASRockPolychromeV2SMBusController.h" +#include "dmiinfo.h" +#include "LogManager.h" + +#define ASROCK_ZONE_LED_COUNT_MESSAGE_EN "[%s] Zone %i LED count: %02d" + +using namespace std::chrono_literals; + +ASRockPolychromeV2SMBusController::ASRockPolychromeV2SMBusController(i2c_smbus_interface* bus, polychrome_dev_id dev) +{ + this->bus = bus; + this->dev = dev; + + DMIInfo dmi; + + device_name = "ASRock " + dmi.getMainboard(); + + ReadLEDConfiguration(); +} + +ASRockPolychromeV2SMBusController::~ASRockPolychromeV2SMBusController() +{ + +} + +std::string ASRockPolychromeV2SMBusController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string ASRockPolychromeV2SMBusController::GetDeviceName() +{ + return(device_name); +} + +std::string ASRockPolychromeV2SMBusController::GetFirmwareVersion() +{ + uint8_t major_version = fw_version >> 8; + uint8_t minor_version = fw_version & 0xFF; + + return(std::to_string(major_version) + "." + std::to_string(minor_version)); +} + + +void ASRockPolychromeV2SMBusController::ReadLEDConfiguration() +{ + /*---------------------------------------------------------------------------------*\ + | The LED configuration register holds 6 bytes, so the first read should return 6 | + | If not, set all zone sizes to zero | + \*---------------------------------------------------------------------------------*/ + LOG_DEBUG("[%s] Reading LED config from controller", device_name.c_str()); + uint8_t asrock_zone_count[I2C_SMBUS_BLOCK_MAX] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; + if (bus->i2c_smbus_read_block_data(dev, POLYCHROME_V2_REG_LED_CONFIG, asrock_zone_count) == 0x06) + { + zone_led_count[POLYCHROME_V2_ZONE_1] = asrock_zone_count[0]; + zone_led_count[POLYCHROME_V2_ZONE_2] = asrock_zone_count[1]; + zone_led_count[POLYCHROME_V2_ZONE_3] = asrock_zone_count[2]; + zone_led_count[POLYCHROME_V2_ZONE_4] = asrock_zone_count[3]; + zone_led_count[POLYCHROME_V2_ZONE_5] = asrock_zone_count[4]; + zone_led_count[POLYCHROME_V2_ZONE_ADDRESSABLE] = asrock_zone_count[5]; + LOG_DEBUG(ASROCK_ZONE_LED_COUNT_MESSAGE_EN, device_name.c_str(), POLYCHROME_V2_ZONE_1, zone_led_count[POLYCHROME_V2_ZONE_1]); + LOG_DEBUG(ASROCK_ZONE_LED_COUNT_MESSAGE_EN, device_name.c_str(), POLYCHROME_V2_ZONE_2, zone_led_count[POLYCHROME_V2_ZONE_2]); + LOG_DEBUG(ASROCK_ZONE_LED_COUNT_MESSAGE_EN, device_name.c_str(), POLYCHROME_V2_ZONE_3, zone_led_count[POLYCHROME_V2_ZONE_3]); + LOG_DEBUG(ASROCK_ZONE_LED_COUNT_MESSAGE_EN, device_name.c_str(), POLYCHROME_V2_ZONE_4, zone_led_count[POLYCHROME_V2_ZONE_4]); + LOG_DEBUG(ASROCK_ZONE_LED_COUNT_MESSAGE_EN, device_name.c_str(), POLYCHROME_V2_ZONE_5, zone_led_count[POLYCHROME_V2_ZONE_5]); + LOG_DEBUG("[%s] Addressable Zone LED count: %02d", device_name.c_str(), zone_led_count[POLYCHROME_V2_ZONE_ADDRESSABLE]); + } + else + { + LOG_WARNING("[%s] LED config read failed", device_name.c_str()); + memset(zone_led_count, 0, sizeof(zone_led_count)); + } +} + +uint8_t ASRockPolychromeV2SMBusController::GetMode() +{ + return(active_mode); +} + +void ASRockPolychromeV2SMBusController::SetColorsAndSpeed(uint8_t led, uint8_t red, uint8_t green, uint8_t blue) +{ + uint8_t color_speed_pkt[4] = { red, green, blue, active_speed }; + uint8_t select_led_pkt[1] = { led }; + + /*-----------------------------------------------------*\ + | Select LED | + \*-----------------------------------------------------*/ + switch(active_mode) + { + case POLYCHROME_V2_MODE_OFF: + case POLYCHROME_V2_MODE_RAINBOW: + case POLYCHROME_V2_MODE_SPECTRUM_CYCLE: + break; + + default: + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V2_REG_LED_SELECT, 1, select_led_pkt); + std::this_thread::sleep_for(1ms); + + /*-----------------------------------------------------*\ + | Polychrome firmware always writes color to fixed reg | + \*-----------------------------------------------------*/ + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V2_REG_COLOR, 3, color_speed_pkt); + std::this_thread::sleep_for(1ms); + break; + } +} + +void ASRockPolychromeV2SMBusController::SetMode(uint8_t zone,uint8_t mode, uint8_t speed) +{ + uint8_t led_count_pkt[1] = { 0x00 }; + active_zone = zone; + active_mode = mode; + active_speed = speed; + + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V2_REG_MODE, 1, &active_mode); + std::this_thread::sleep_for(1ms); + + /*-----------------------------------------------------*\ + | Select a single LED | + \*-----------------------------------------------------*/ + bus->i2c_smbus_write_block_data(dev, POLYCHROME_V2_REG_LED_COUNT, 0, led_count_pkt); + std::this_thread::sleep_for(1ms); + + switch(active_mode) + { + /*-----------------------------------------------------*\ + | These modes don't take a speed | + \*-----------------------------------------------------*/ + case POLYCHROME_V2_MODE_OFF: + case POLYCHROME_V2_MODE_STATIC: + break; + + /*-----------------------------------------------------*\ + | All other modes, write speed to active mode register | + \*-----------------------------------------------------*/ + default: + bus->i2c_smbus_write_block_data(dev, active_mode, 1, &speed); + std::this_thread::sleep_for(1ms); + break; + } +} diff --git a/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/ASRockPolychromeV2SMBusController.h b/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/ASRockPolychromeV2SMBusController.h new file mode 100644 index 0000000..1977660 --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/ASRockPolychromeV2SMBusController.h @@ -0,0 +1,146 @@ +/*---------------------------------------------------------*\ +| ASRockPolychromeV2SMBusController.h | +| | +| Driver for SMBus ASRock Polychrome V2 motherboards | +| | +| Adam Honse (CalcProgrammer1) 13 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef uint8_t polychrome_dev_id; + +#define ASROCK_V2_CONTROLLER_NAME "ASRock Polychrome v2 SMBus Controller" + +enum +{ + /*------------------------------------------------------------------------------------------*\ + | ASRock Polychrome v2 Registers | + \*------------------------------------------------------------------------------------------*/ + POLYCHROME_V2_REG_FIRMWARE_VER = 0x00, /* Firmware version Major.Minor */ + POLYCHROME_V2_REG_MODE = 0x30, /* Mode selection register */ + POLYCHROME_V2_REG_LED_SELECT = 0x31, /* LED selection register */ + POLYCHROME_V2_REG_LED_COUNT = 0x32, /* Additional LED count register */ + POLYCHROME_V2_REG_LED_CONFIG = 0x33, /* LED configuration register */ + POLYCHROME_V2_REG_COLOR = 0x34, /* Color register: Red, Green, Blue */ + POLYCHROME_V2_REG_ARGB_GRB = 0x35, /* ARGB bistream reversing register */ +}; + +enum +{ + POLYCHROME_V2_ZONE_1 = 0x00, /* RGB LED 1 Header */ + POLYCHROME_V2_ZONE_2 = 0x01, /* RGB LED 2 Header */ + POLYCHROME_V2_ZONE_3 = 0x02, /* Audio/PCH Zone LEDs */ + POLYCHROME_V2_ZONE_4 = 0x03, /* Audio/PCH Zone LEDs */ + POLYCHROME_V2_ZONE_5 = 0x04, /* IO Cover Zone LEDs */ + POLYCHROME_V2_ZONE_ADDRESSABLE = 0x05, /* Addressable LED header */ + POLYCHROME_V2_ZONE_COUNT = 0x06, /* Total number of zones */ + POLYCHROME_V2_ZONE_ADDRESSABLE_MAX = 0x64, /* Maxinum number of ARGB LEDs */ +}; + +/*----------------------------------------------------------------------------------------------*\ +| Definitions for Polychrome V2 | +\*----------------------------------------------------------------------------------------------*/ +#define POLYCHROME_V2_NUM_MODES 14 /* Number of Polychrome V2 modes */ + +enum +{ + POLYCHROME_V2_MODE_OFF = 0x10, /* OFF mode */ + POLYCHROME_V2_MODE_STATIC = 0x11, /* Static color mode */ + POLYCHROME_V2_MODE_BREATHING = 0x12, /* Breating effect mode */ + POLYCHROME_V2_MODE_STROBE = 0x13, /* Strobe effect mode */ + POLYCHROME_V2_MODE_SPECTRUM_CYCLE = 0x14, /* Spectrum Cycle effect mode */ + POLYCHROME_V2_MODE_RANDOM = 0x15, /* Random effect mode */ + POLYCHROME_V2_MODE_WAVE = 0x17, /* Wave effect mode */ + POLYCHROME_V2_MODE_SPRING = 0x18, /* Spring effect mode */ + POLYCHROME_V2_MODE_STACK = 0x19, /* Stack effect mode */ + POLYCHROME_V2_MODE_CRAM = 0x1A, /* Cram effect mode */ + POLYCHROME_V2_MODE_SCAN = 0x1B, /* Scan effect mode */ + POLYCHROME_V2_MODE_NEON = 0x1C, /* Neon effect mode */ + POLYCHROME_V2_MODE_WATER = 0x1D, /* Water effect mode */ + POLYCHROME_V2_MODE_RAINBOW = 0x1E, /* Rainbow effect mode */ +}; + +enum +{ + POLYCHROME_V2_BREATHING_SPEED_MIN = 0x0A, /* Slowest speed */ + POLYCHROME_V2_BREATHING_SPEED_DEFAULT = 0x06, /* Default speed */ + POLYCHROME_V2_BREATHING_SPEED_MAX = 0x02, /* Fastest speed */ + + POLYCHROME_V2_STROBE_SPEED_MIN = 0xA0, /* Slowest speed */ + POLYCHROME_V2_STROBE_SPEED_DEFAULT = 0x4D, /* Default speed */ + POLYCHROME_V2_STROBE_SPEED_MAX = 0x05, /* Fastest speed */ + + POLYCHROME_V2_SPECTRUM_CYCLE_SPEED_MIN = 0xA0, /* Slowest speed */ + POLYCHROME_V2_SPECTRUM_CYCLE_SPEED_DEFAULT = 0x50, /* Default speed */ + POLYCHROME_V2_SPECTRUM_CYCLE_SPEED_MAX = 0x0A, /* Fastest speed */ + + POLYCHROME_V2_RANDOM_SPEED_MIN = 0xA0, /* Slowest speed */ + POLYCHROME_V2_RANDOM_SPEED_DEFAULT = 0x4D, /* Default speed */ + POLYCHROME_V2_RANDOM_SPEED_MAX = 0x05, /* Fastest speed */ + + POLYCHROME_V2_WAVE_SPEED_MIN = 0x06, /* Slowest speed */ + POLYCHROME_V2_WAVE_SPEED_DEFAULT = 0x03, /* Default speed */ + POLYCHROME_V2_WAVE_SPEED_MAX = 0x01, /* Fastest speed */ + + POLYCHROME_V2_SPRING_SPEED_MIN = 0x20, /* Slowest speed */ + POLYCHROME_V2_SPRING_SPEED_DEFAULT = 0x11, /* Default speed */ + POLYCHROME_V2_SPRING_SPEED_MAX = 0x02, /* Fastest speed */ + + POLYCHROME_V2_STACK_SPEED_MIN = 0x20, /* Slowest speed */ + POLYCHROME_V2_STACK_SPEED_DEFAULT = 0x11, /* Default speed */ + POLYCHROME_V2_STACK_SPEED_MAX = 0x02, /* Fastest speed */ + + POLYCHROME_V2_CRAM_SPEED_MIN = 0x20, /* Slowest speed */ + POLYCHROME_V2_CRAM_SPEED_DEFAULT = 0x11, /* Default speed */ + POLYCHROME_V2_CRAM_SPEED_MAX = 0x02, /* Fastest speed */ + + POLYCHROME_V2_SCAN_SPEED_MIN = 0x20, /* Slowest speed */ + POLYCHROME_V2_SCAN_SPEED_DEFAULT = 0x11, /* Default speed */ + POLYCHROME_V2_SCAN_SPEED_MAX = 0x02, /* Fastest speed */ + + POLYCHROME_V2_NEON_SPEED_MIN = 0x20, /* Slowest speed */ + POLYCHROME_V2_NEON_SPEED_DEFAULT = 0x11, /* Default speed */ + POLYCHROME_V2_NEON_SPEED_MAX = 0x02, /* Fastest speed */ + + POLYCHROME_V2_WATER_SPEED_MIN = 0x20, /* Slowest speed */ + POLYCHROME_V2_WATER_SPEED_DEFAULT = 0x11, /* Default speed */ + POLYCHROME_V2_WATER_SPEED_MAX = 0x02, /* Fastest speed */ + + POLYCHROME_V2_RAINBOW_SPEED_MIN = 0x12, /* Slowest speed */ + POLYCHROME_V2_RAINBOW_SPEED_DEFAULT = 0x08, /* Default speed */ + POLYCHROME_V2_RAINBOW_SPEED_MAX = 0x02, /* Fastest speed */ +}; + +class ASRockPolychromeV2SMBusController +{ +public: + ASRockPolychromeV2SMBusController(i2c_smbus_interface* bus, polychrome_dev_id dev); + ~ASRockPolychromeV2SMBusController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetFirmwareVersion(); + uint8_t GetMode(); + void SetColorsAndSpeed(uint8_t led, uint8_t red, uint8_t green, uint8_t blue); + void SetMode(uint8_t zone, uint8_t mode, uint8_t speed); + + uint8_t zone_led_count[6]; + uint16_t fw_version; + +private: + std::string device_name; + uint8_t active_zone; + uint8_t active_mode; + uint8_t active_speed; + i2c_smbus_interface* bus; + polychrome_dev_id dev; + + void ReadLEDConfiguration(); +}; diff --git a/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/RGBController_ASRockPolychromeV2SMBus.cpp b/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/RGBController_ASRockPolychromeV2SMBus.cpp new file mode 100644 index 0000000..2988e2c --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/RGBController_ASRockPolychromeV2SMBus.cpp @@ -0,0 +1,328 @@ +/*---------------------------------------------------------*\ +| RGBController_ASRockPolychromeV2SMBus.cpp | +| | +| RGBController for SMBus ASRock Polychrome V2 | +| motherboards | +| | +| Adam Honse (CalcProgrammer1) 15 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ASRockPolychromeV2SMBus.h" + +static const char* polychrome_v2_zone_names[] = +{ + "RGB LED 1 Header", + "RGB LED 2 Header", + "Audio", + "PCH", + "IO Cover", + "Addressable Header" +}; + +/**------------------------------------------------------------------*\ + @name ASRock Polychrome v2 SMBus + @category Motherboard + @type SMBus + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectASRockSMBusControllers + @comment ASRock Polychrome v2 controllers will save with each update. + Per ARGB LED support is not possible with these devices. +\*-------------------------------------------------------------------*/ + +RGBController_ASRockPolychromeV2SMBus::RGBController_ASRockPolychromeV2SMBus(ASRockPolychromeV2SMBusController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "ASRock"; + version = controller->GetFirmwareVersion(); + type = DEVICE_TYPE_MOTHERBOARD; + description = "ASRock Polychrome v2 Device"; + location = controller->GetDeviceLocation(); + + + mode Off; + Off.name = "Off"; + Off.value = POLYCHROME_V2_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = POLYCHROME_V2_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = POLYCHROME_V2_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = POLYCHROME_V2_BREATHING_SPEED_MIN; + Breathing.speed_max = POLYCHROME_V2_BREATHING_SPEED_MAX; + Breathing.speed = POLYCHROME_V2_BREATHING_SPEED_DEFAULT; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Strobe; + Strobe.name = "Strobe"; + Strobe.value = POLYCHROME_V2_MODE_STROBE; + Strobe.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Strobe.speed_min = POLYCHROME_V2_STROBE_SPEED_MIN; + Strobe.speed_max = POLYCHROME_V2_STROBE_SPEED_MAX; + Strobe.speed = POLYCHROME_V2_STROBE_SPEED_DEFAULT; + Strobe.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Strobe); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = POLYCHROME_V2_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.speed_min = POLYCHROME_V2_SPECTRUM_CYCLE_SPEED_MIN; + SpectrumCycle.speed_max = POLYCHROME_V2_SPECTRUM_CYCLE_SPEED_MAX; + SpectrumCycle.speed = POLYCHROME_V2_SPECTRUM_CYCLE_SPEED_DEFAULT; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Random; + Random.name = "Random"; + Random.value = POLYCHROME_V2_MODE_RANDOM; + Random.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Random.speed_min = POLYCHROME_V2_RANDOM_SPEED_MIN; + Random.speed_max = POLYCHROME_V2_RANDOM_SPEED_MAX; + Random.speed = POLYCHROME_V2_RANDOM_SPEED_DEFAULT; + Random.color_mode = MODE_COLORS_NONE; + modes.push_back(Random); + + mode Wave; + Wave.name = "Wave"; + Wave.value = POLYCHROME_V2_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Wave.speed_min = POLYCHROME_V2_WAVE_SPEED_MIN; + Wave.speed_max = POLYCHROME_V2_WAVE_SPEED_MAX; + Wave.speed = POLYCHROME_V2_WAVE_SPEED_DEFAULT; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + + mode Spring; + Spring.name = "Spring"; + Spring.value = POLYCHROME_V2_MODE_SPRING; + Spring.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Spring.speed_min = POLYCHROME_V2_SPRING_SPEED_MIN; + Spring.speed_max = POLYCHROME_V2_SPRING_SPEED_MAX; + Spring.speed = POLYCHROME_V2_SPRING_SPEED_DEFAULT; + Spring.color_mode = MODE_COLORS_NONE; + modes.push_back(Spring); + + mode Stack; + Stack.name = "Stack"; + Stack.value = POLYCHROME_V2_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Stack.speed_min = POLYCHROME_V2_STACK_SPEED_MIN; + Stack.speed_max = POLYCHROME_V2_STACK_SPEED_MAX; + Stack.speed = POLYCHROME_V2_STACK_SPEED_DEFAULT; + Stack.color_mode = MODE_COLORS_NONE; + modes.push_back(Stack); + + mode Cram; + Cram.name = "Cram"; + Cram.value = POLYCHROME_V2_MODE_CRAM; + Cram.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Cram.speed_min = POLYCHROME_V2_CRAM_SPEED_MIN; + Cram.speed_max = POLYCHROME_V2_CRAM_SPEED_MAX; + Cram.speed = POLYCHROME_V2_CRAM_SPEED_DEFAULT; + Cram.color_mode = MODE_COLORS_NONE; + modes.push_back(Cram); + + mode Scan; + Scan.name = "Scan"; + Scan.value = POLYCHROME_V2_MODE_SCAN; + Scan.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Scan.speed_min = POLYCHROME_V2_SCAN_SPEED_MIN; + Scan.speed_max = POLYCHROME_V2_SCAN_SPEED_MAX; + Scan.speed = POLYCHROME_V2_SCAN_SPEED_DEFAULT; + Scan.color_mode = MODE_COLORS_NONE; + modes.push_back(Scan); + + mode Neon; + Neon.name = "Neon"; + Neon.value = POLYCHROME_V2_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Neon.speed_min = POLYCHROME_V2_NEON_SPEED_MIN; + Neon.speed_max = POLYCHROME_V2_NEON_SPEED_MAX; + Neon.speed = POLYCHROME_V2_NEON_SPEED_DEFAULT; + Neon.color_mode = MODE_COLORS_NONE; + modes.push_back(Neon); + + mode Water; + Water.name = "Water"; + Water.value = POLYCHROME_V2_MODE_WATER; + Water.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Water.speed_min = POLYCHROME_V2_WATER_SPEED_MIN; + Water.speed_max = POLYCHROME_V2_WATER_SPEED_MAX; + Water.speed = POLYCHROME_V2_WATER_SPEED_DEFAULT; + Water.color_mode = MODE_COLORS_NONE; + modes.push_back(Water); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = POLYCHROME_V2_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.speed_min = POLYCHROME_V2_RAINBOW_SPEED_MIN; + Rainbow.speed_max = POLYCHROME_V2_RAINBOW_SPEED_MAX; + Rainbow.speed = POLYCHROME_V2_RAINBOW_SPEED_DEFAULT; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + SetupZones(); +} + +RGBController_ASRockPolychromeV2SMBus::~RGBController_ASRockPolychromeV2SMBus() +{ + delete controller; +} + +void RGBController_ASRockPolychromeV2SMBus::SetupZones() +{ + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < POLYCHROME_V2_ZONE_COUNT; zone_idx++) + { + if(controller->zone_led_count[zone_idx] > 0) + { + zone* new_zone = new zone(); + + /*---------------------------------------------------------*\ + | Set zone name to channel name | + \*---------------------------------------------------------*/ + + new_zone->name = polychrome_v2_zone_names[zone_idx]; + + + if(zone_idx == POLYCHROME_V2_ZONE_ADDRESSABLE) + { + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + } + else + { + new_zone->leds_min = controller->zone_led_count[zone_idx]; + new_zone->leds_max = controller->zone_led_count[zone_idx]; + new_zone->leds_count = controller->zone_led_count[zone_idx]; + } + + if(new_zone->leds_count > 1) + { + new_zone->type = ZONE_TYPE_LINEAR; + } + else + { + new_zone->type = ZONE_TYPE_SINGLE; + } + + new_zone->matrix_map = NULL; + + /*---------------------------------------------------------*\ + | Push new zone to zones vector | + \*---------------------------------------------------------*/ + zones.push_back(*new_zone); + } + } + + unsigned int led_count = 0; + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < POLYCHROME_V2_ZONE_COUNT; zone_idx++) + { + if(controller->zone_led_count[zone_idx] > 0) + { + for(unsigned int led_idx = 0; led_idx < controller->zone_led_count[zone_idx]; led_idx++) + { + /*---------------------------------------------------------*\ + | Each zone only has one LED | + \*---------------------------------------------------------*/ + led* new_led = new led(); + + new_led->name = polychrome_v2_zone_names[zone_idx]; + + new_led->name.append(" " + std::to_string(led_idx + 1)); + new_led->value = 0; + + if(zone_idx == POLYCHROME_V2_ZONE_ADDRESSABLE) + { + new_led->value = 0x19; + } + + /*---------------------------------------------------------*\ + | Push new LED to LEDs vector | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + + led_count++; + + if(zone_idx == POLYCHROME_V2_ZONE_ADDRESSABLE) + { + break; + } + } + } + } + + SetupColors(); +} + +void RGBController_ASRockPolychromeV2SMBus::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ASRockPolychromeV2SMBus::DeviceUpdateLEDs() +{ + for(unsigned int led = 0; led < colors.size(); led++) + { + UpdateSingleLED(led); + } +} + +void RGBController_ASRockPolychromeV2SMBus::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ASRockPolychromeV2SMBus::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + /*---------------------------------------------------------*\ + | If the LED value is non-zero, this LED overrides the LED | + | index | + \*---------------------------------------------------------*/ + if(leds[led].value != 0) + { + led = leds[led].value; + } + + controller->SetColorsAndSpeed(led, red, grn, blu); +} + +void RGBController_ASRockPolychromeV2SMBus::DeviceUpdateMode() +{ + + controller->SetMode(0, modes[active_mode].value, modes[active_mode].speed); + + DeviceUpdateLEDs(); +} diff --git a/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/RGBController_ASRockPolychromeV2SMBus.h b/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/RGBController_ASRockPolychromeV2SMBus.h new file mode 100644 index 0000000..f932bca --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/RGBController_ASRockPolychromeV2SMBus.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_ASRockPolychromeV2SMBus.h | +| | +| RGBController for SMBus ASRock Polychrome V2 | +| motherboards | +| | +| Adam Honse (CalcProgrammer1) 15 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ASRockPolychromeV2SMBusController.h" + +class RGBController_ASRockPolychromeV2SMBus : public RGBController +{ +public: + RGBController_ASRockPolychromeV2SMBus(ASRockPolychromeV2SMBusController* controller_ptr); + ~RGBController_ASRockPolychromeV2SMBus(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ASRockPolychromeV2SMBusController* controller; +}; diff --git a/Controllers/ASRockSMBusController/ASRockSMBusControllerDetect.cpp b/Controllers/ASRockSMBusController/ASRockSMBusControllerDetect.cpp new file mode 100644 index 0000000..41d4e09 --- /dev/null +++ b/Controllers/ASRockSMBusController/ASRockSMBusControllerDetect.cpp @@ -0,0 +1,177 @@ +/*---------------------------------------------------------*\ +| ASRockSMBusControllerDetect.cpp | +| | +| Detector for SMBus ASRock ASR RGB and Polychrome | +| motherboards | +| | +| Adam Honse (CalcProgrammer1) 14 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "ASRockASRRGBSMBusController.h" +#include "ASRockPolychromeV1SMBusController.h" +#include "ASRockPolychromeV2SMBusController.h" +#include "LogManager.h" +#include "RGBController_ASRockASRRGBSMBus.h" +#include "RGBController_ASRockPolychromeV1SMBus.h" +#include "RGBController_ASRockPolychromeV2SMBus.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/*******************************************************************************************\ +* * +* TestForPolychromeSMBusController * +* * +* Tests the given address to see if an ASRock RGB controller exists there. * +* First does a quick write to test for a response * +* * +\*******************************************************************************************/ + +#define ASROCK_DETECTOR_NAME "ASRock SMBus Detectector" +#define VENDOR_NAME "ASRock" +#define SMBUS_ADDRESS 0x6A + +enum +{ + ASROCK_TYPE_UNKNOWN = 0x00, /* Unknown Type or Not ASRock Device */ + ASROCK_TYPE_ASRLED = 0x01, /* ASRock Firmware 1.x - ASR LED */ + ASROCK_TYPE_POLYCHROME_V1 = 0x02, /* ASRock Firmware 2.x - Polychrome V1 */ + ASROCK_TYPE_POLYCHROME_V2 = 0x03, /* ASRock Firmware 3.x - Polychrome V2 */ + ASROCK_REG_FIRMWARE_VER = 0x00, /* Firmware version Major.Minor */ +}; + +union u16_to_u8 +{ + uint16_t u16; + struct + { + uint8_t lsb; + uint8_t msb; + }; +}; + +bool TestForPolychromeSMBusController(i2c_smbus_interface* bus, uint8_t address) +{ + bool pass = false; + + int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE); + + if (res >= 0) + { + pass = true; + } + + return(pass); + +} /* TestForPolychromeController() */ + +uint16_t GetFirmwareVersion(i2c_smbus_interface* bus, uint8_t address) +{ + // The firmware register holds two bytes, so the first read should return 2 + // If not, report invalid firmware revision + LOG_DEBUG("[%s] Reading back device firmware version", ASROCK_DETECTOR_NAME); + u16_to_u8 asrock_version_u16; + + // Version response array needs to be 32 bytes to prevent non ASRock board from stack smashing + uint8_t asrock_version[I2C_SMBUS_BLOCK_MAX] = { 0x00, 0x00 }; + + if (bus->i2c_smbus_read_block_data(address, ASROCK_REG_FIRMWARE_VER, asrock_version) == 0x02) + { + asrock_version_u16.msb = asrock_version[0]; + asrock_version_u16.lsb = asrock_version[1]; + + LOG_DEBUG("[%s] Device firmware version: v%02d.%02d", ASROCK_DETECTOR_NAME, asrock_version_u16.msb, asrock_version_u16.lsb); + return(asrock_version_u16.u16); + } + else + { + LOG_WARNING("[%s] Firmware readback failed; Returning Unknown Version", ASROCK_DETECTOR_NAME); + return(ASROCK_TYPE_UNKNOWN); + } +} + +/******************************************************************************************\ +* * +* DetectPolychromeControllers * +* * +* Detect ASRock Polychrome RGB SMBus controllers on the enumerated I2C busses at * +* address 0x6A. * +* * +* bus - pointer to i2c_smbus_interface where Polychrome device is connected * +* dev - I2C address of Polychrome device * +* * +\******************************************************************************************/ + +void DetectASRockSMBusControllers(std::vector& busses) +{ + for(unsigned int bus = 0; bus < busses.size(); bus++) + { + IF_MOBO_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + if(busses[bus]->pci_subsystem_vendor == ASROCK_SUB_VEN) + { + LOG_DEBUG(SMBUS_CHECK_DEVICE_MESSAGE_EN, ASROCK_DETECTOR_NAME, bus, VENDOR_NAME, SMBUS_ADDRESS); + // Check for Polychrome controller at 0x6A + if(TestForPolychromeSMBusController(busses[bus], SMBUS_ADDRESS)) + { + LOG_DEBUG("[%s] Detected a device at address 0x%02X, testing for a known controller", ASROCK_DETECTOR_NAME, SMBUS_ADDRESS); + + u16_to_u8 version; + version.u16 = GetFirmwareVersion(busses[bus], SMBUS_ADDRESS); + + switch (version.msb) + { + case ASROCK_TYPE_ASRLED: + { + LOG_DEBUG("[%s] Found a ASR RGB LED Controller", ASROCK_DETECTOR_NAME); + ASRockASRRGBSMBusController* controller = new ASRockASRRGBSMBusController(busses[bus], SMBUS_ADDRESS); + controller-> fw_version = version.u16; + RGBController_ASRockASRRGBSMBus* rgb_controller = new RGBController_ASRockASRRGBSMBus(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + + case ASROCK_TYPE_POLYCHROME_V1: + { + LOG_DEBUG("[%s] Found a Polychrome v1 Controller", ASROCK_DETECTOR_NAME); + ASRockPolychromeV1SMBusController* controller = new ASRockPolychromeV1SMBusController(busses[bus], SMBUS_ADDRESS); + controller-> fw_version = version.u16; + RGBController_ASRockPolychromeV1SMBus* rgb_controller = new RGBController_ASRockPolychromeV1SMBus(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + + case ASROCK_TYPE_POLYCHROME_V2: + { + LOG_DEBUG("[%s] Found a Polychrome v2 Controller", ASROCK_DETECTOR_NAME); + ASRockPolychromeV2SMBusController* controller = new ASRockPolychromeV2SMBusController(busses[bus], SMBUS_ADDRESS); + controller-> fw_version = version.u16; + RGBController_ASRockPolychromeV2SMBus* rgb_controller = new RGBController_ASRockPolychromeV2SMBus(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + + default: + LOG_DEBUG("[%s] Not a Polychrome device or unknown type", ASROCK_DETECTOR_NAME); + break; + } + } + else + { + LOG_DEBUG("[%s] Bus %02d has no response at 0x%02X", ASROCK_DETECTOR_NAME, bus, SMBUS_ADDRESS); + } + } + else + { + LOG_DEBUG(SMBUS_CHECK_DEVICE_FAILURE_EN, ASROCK_DETECTOR_NAME, bus, VENDOR_NAME); + } + } + } + +} /* DetectSMBusPolychromeControllers() */ + +REGISTER_I2C_DETECTOR("ASRock Motherboard SMBus Controllers", DetectASRockSMBusControllers); diff --git a/Controllers/AlienwareController/AlienwareController.cpp b/Controllers/AlienwareController/AlienwareController.cpp new file mode 100644 index 0000000..9b052e1 --- /dev/null +++ b/Controllers/AlienwareController/AlienwareController.cpp @@ -0,0 +1,838 @@ +/*---------------------------------------------------------*\ +| AlienwareController.cpp | +| | +| Driver for Dell Alienware RGB USB controller | +| | +| Gabriel Marcano (gemarcano) 21 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include "RGBController.h" +#include "AlienwareController.h" +#include "LogManager.h" +#include "StringUtils.h" + +typedef uint32_t alienware_platform_id; + +/*---------------------------------------------------------*\ +| Some devices appear to report the wrong number of zones. | +| Record that here. | +\*---------------------------------------------------------*/ +static const std::map zone_quirks_table = +{ + { 0x0C01, 4 }, // Dell G5 SE 5505 + { 0x0A01, 16 }, // Dell G7 15 7500 + { 0x0E03, 4 }, // Dell G15 5511 + { 0x0E0A, 4 } // Dell G15 5530 + +}; + +/*---------------------------------------------------------*\ +| Add zones for devices here, mapping the platform ID to | +| the zone names | +\*---------------------------------------------------------*/ +static const std::map> zone_names_table = +{ + { 0x0C01, { "Left", "Middle", "Right", "Numpad" } }, + { 0x0A01, { "Left", "Center Left", "Center Right", "Right", + "Light Bar 1", "Light Bar 2", "Light Bar 3", + "Light Bar 4", "Light Bar 5", "Light Bar 6", + "Light Bar 7", "Light Bar 8", "Light Bar 9", + "Light Bar 10", "Light Bar 11", "Light Bar 12" } }, + { 0x0E03, { "Left", "Middle", "Right", "Numpad" } }, + { 0x0E0A, { "Left", "Middle", "Right", "Numpad" } } +}; + +static void SendHIDReport(hid_device *dev, const unsigned char* usb_buf, size_t usb_buf_size) +{ + using namespace std::chrono_literals; + + hid_send_feature_report(dev, usb_buf, usb_buf_size); + + /*-----------------------------------------------------*\ + | The controller really doesn't like really spammed by | + | too many commands at once... the delay may be command | + | dependent also. Delay for longer if the command is | + | changing animation state | + \*-----------------------------------------------------*/ + unsigned char command = usb_buf[2]; + unsigned char subcommand = usb_buf[3]; + + if( ( command == ALIENWARE_COMMAND_USER_ANIM ) + && ( ( subcommand == ALIENWARE_COMMAND_USER_ANIM_FINISH_PLAY ) + || ( subcommand == ALIENWARE_COMMAND_USER_ANIM_FINISH_SAVE ) ) ) + { + std::this_thread::sleep_for(1s); + } + else + { + std::this_thread::sleep_for(60ms); + } +} + +AlienwareController::AlienwareController(hid_device* dev_handle, const hid_device_info& info, std::string name) +{ + HidapiAlienwareReport report; + + dev = dev_handle; + device_name = name; + location = info.path; + + /*-----------------------------------------------------*\ + | Get serial number | + \*-----------------------------------------------------*/ + serial_number = StringUtils::wstring_to_string(info.serial_number); + + /*-----------------------------------------------------*\ + | Get zone information by checking firmware | + | configuration | + \*-----------------------------------------------------*/ + report = Report(ALIENWARE_COMMAND_REPORT_CONFIG); + alienware_platform_id platform_id = report.data[4] << 8 | report.data[5]; + + /*-----------------------------------------------------*\ + | Check if the device reports the wrong number of zones | + \*-----------------------------------------------------*/ + unsigned number_of_zones = zone_quirks_table.count(platform_id) ? zone_quirks_table.at(platform_id) : report.data[6]; + + /*-----------------------------------------------------*\ + | Get firmware version | + \*-----------------------------------------------------*/ + report = Report(ALIENWARE_COMMAND_REPORT_FIRMWARE); + + std::stringstream fw_string; + + fw_string << static_cast(report.data[4]) << '.' << static_cast(report.data[5]) << '.' << static_cast(report.data[6]); + version = fw_string.str(); + + /*-----------------------------------------------------*\ + | Initialize Alienware zones | + \*-----------------------------------------------------*/ + zones.resize(number_of_zones); + + if(zone_names_table.count(platform_id)) + { + LOG_INFO("[%s] Known platform: %8X, Number of zones: %d", ALIENWARE_CONTROLLER_NAME, platform_id, number_of_zones); + zone_names = zone_names_table.at(platform_id); + } + else + { + LOG_WARNING("[%s] Unknown platform: %8X, Number of zones: %d", ALIENWARE_CONTROLLER_NAME, platform_id, number_of_zones); + + /*-------------------------------------------------*\ + | If this is an unknown controller, set the name of | + | all regions to "Unknown" | + \*-------------------------------------------------*/ + for(size_t i = 0; i < number_of_zones; i++) + { + zone_names.emplace_back("Unknown"); + } + } + + /*-----------------------------------------------------*\ + | Set defaults for all zones | + | It doesn't seem possible to read the controller's | + | current state, hence the default value being set here.| + \*-----------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + zones[zone_idx].color[0] = 0x000000; + zones[zone_idx].color[1] = 0x000000; + zones[zone_idx].mode = ALIENWARE_MODE_COLOR; + + /*-------------------------------------------------*\ + | Default period value from ACC | + \*-------------------------------------------------*/ + zones[zone_idx].period = 2000; + zones[zone_idx].tempo = ALIENWARE_TEMPO_MAX; + zones[zone_idx].dim = 0; + } + + /*-----------------------------------------------------*\ + | Initialize dirty flags | + \*-----------------------------------------------------*/ + dirty = true; + dirty_dim = true; +} + +AlienwareController::~AlienwareController() +{ + +} + +unsigned int AlienwareController::GetZoneCount() +{ + return((unsigned int)zones.size()); +} + +std::vector AlienwareController::GetZoneNames() +{ + return(zone_names); +} + +std::string AlienwareController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AlienwareController::GetDeviceName() +{ + return(device_name); +} + +std::string AlienwareController::GetSerialString() +{ + return(serial_number); +} + +std::string AlienwareController::GetFirmwareVersion() +{ + return(version); +} + +AlienwareController::HidapiAlienwareReport AlienwareController::GetResponse() +{ + /*-----------------------------------------------------*\ + | Zero init. This is not updated if there's a problem. | + \*-----------------------------------------------------*/ + HidapiAlienwareReport result; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(result.data, 0x00, sizeof(result.data)); + + + hid_get_feature_report(dev, result.data, HIDAPI_ALIENWARE_REPORT_SIZE); + + return(result); +} + +AlienwareController::HidapiAlienwareReport AlienwareController::Report(uint8_t subcommand) +{ + unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00, per hidapi | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x03; + usb_buf[0x02] = ALIENWARE_COMMAND_REPORT; + usb_buf[0x03] = subcommand; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + SendHIDReport(dev, usb_buf, sizeof(usb_buf)); + + return(GetResponse()); +} + +AlienwareReport AlienwareController::GetStatus(uint8_t subcommand) +{ + HidapiAlienwareReport data = Report(subcommand); + AlienwareReport result = AlienwareReport{}; + + /*-----------------------------------------------------*\ + | Skip first byte, as that's the report number, which | + | should be 0 | + \*-----------------------------------------------------*/ + memcpy(result.data, &data.data[1], sizeof(result.data)); + + return(result); +} + +bool AlienwareController::Dim(std::vector zones, double percent) +{ + /*-----------------------------------------------------*\ + | Bail out if there are no zones to update | + \*-----------------------------------------------------*/ + if(!zones.size()) + { + return(true); + } + + unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00, per hidapi | + \*-----------------------------------------------------*/ + uint16_t num_zones = (uint16_t)zones.size(); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x03; + usb_buf[0x02] = ALIENWARE_COMMAND_DIM; + usb_buf[0x03] = static_cast(percent); + usb_buf[0x04] = num_zones >> 8; + usb_buf[0x05] = num_zones & 0xFF; + + for(size_t i = 0; i < num_zones; i++) + { + usb_buf[0x06+i] = zones[i]; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + SendHIDReport(dev, usb_buf, sizeof(usb_buf)); + + HidapiAlienwareReport response = GetResponse(); + + /*-----------------------------------------------------*\ + | For this command, error is if the output equals the | + | input | + \*-----------------------------------------------------*/ + return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE)); +} + +bool AlienwareController::UserAnimation(uint16_t subcommand, uint16_t animation, uint16_t duration) +{ + unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00 per hidapi | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x03; + usb_buf[0x02] = ALIENWARE_COMMAND_USER_ANIM; + usb_buf[0x03] = subcommand >> 8; + usb_buf[0x04] = subcommand & 0xFF; + usb_buf[0x05] = animation >> 8; + usb_buf[0x06] = animation & 0xFF; + usb_buf[0x07] = duration >> 8; + usb_buf[0x08] = duration & 0xFF; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + SendHIDReport(dev, usb_buf, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Every subcommand appears to report its result on a | + | different byte | + \*-----------------------------------------------------*/ + HidapiAlienwareReport response = GetResponse(); + + /*-----------------------------------------------------*\ + | The only time the 0x03 byte is zero is if the | + | controller has crashed | + \*-----------------------------------------------------*/ + if(response.data[1] == 0) + { + return(false); + } + + switch(subcommand) + { + case ALIENWARE_COMMAND_USER_ANIM_FINISH_SAVE: + return(!response.data[7]); + case ALIENWARE_COMMAND_USER_ANIM_FINISH_PLAY: + return(!response.data[5]); + case ALIENWARE_COMMAND_USER_ANIM_PLAY: + return(!response.data[7]); + default: + return(true); + } +} + +bool AlienwareController::SelectZones(const std::vector& zones) +{ + /*-----------------------------------------------------*\ + | Bail if zones is empty, and return false to indicate | + | nothing has changed | + \*-----------------------------------------------------*/ + if(!zones.size()) + { + return(false); + } + + unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00, per hidapi | + \*-----------------------------------------------------*/ + uint16_t num_zones = (uint16_t)zones.size(); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x03; + usb_buf[0x02] = ALIENWARE_COMMAND_SELECT_ZONES; + usb_buf[0x03] = 1; // loop? + usb_buf[0x04] = num_zones >> 8; + usb_buf[0x05] = num_zones & 0xFF; + + for(size_t i = 0; i < num_zones; i++) + { + usb_buf[0x06+i] = zones[i]; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + SendHIDReport(dev, usb_buf, sizeof(usb_buf)); + + HidapiAlienwareReport response = GetResponse(); + + /*-----------------------------------------------------*\ + | For this command, error is if the output equals the | + | input | + \*-----------------------------------------------------*/ + return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE)); +} + +bool AlienwareController::ModeAction(uint8_t mode, uint16_t duration, uint16_t tempo, RGBColor color) +{ + return(ModeAction(&mode, &duration, &tempo, &color, 1)); +} + +bool AlienwareController::ModeAction + ( + const uint8_t* mode, + const uint16_t* duration, + const uint16_t* tempo, + const RGBColor* color, + unsigned amount + ) +{ + unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Amount must be 3 or less, as that's how many | + | subcommands can fit into one report | + \*-----------------------------------------------------*/ + if(amount > 3) + { + return(false); + } + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00, per hidapi | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x03; + usb_buf[0x02] = ALIENWARE_COMMAND_ADD_ACTION; + + for(unsigned int i = 0; i < amount; i++) + { + usb_buf[0x03 + (8 * i)] = mode[i]; + usb_buf[0x04 + (8 * i)] = duration[i] >> 8; + usb_buf[0x05 + (8 * i)] = duration[i] & 0xFF; + usb_buf[0x06 + (8 * i)] = tempo[i] >> 8; + usb_buf[0x07 + (8 * i)] = tempo[i] & 0xFF; + usb_buf[0x08 + (8 * i)] = RGBGetRValue(color[i]); + usb_buf[0x09 + (8 * i)] = RGBGetGValue(color[i]); + usb_buf[0x0A + (8 * i)] = RGBGetBValue(color[i]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + SendHIDReport(dev, usb_buf, sizeof(usb_buf)); + + HidapiAlienwareReport response = GetResponse(); + + /*-----------------------------------------------------*\ + | For this command, error is if the output equals the | + | input | + \*-----------------------------------------------------*/ + return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE)); +} + +bool AlienwareController::MultiModeAction + ( + const uint8_t* mode, + const uint16_t* duration, + const uint16_t* tempo, + const RGBColor* color, + unsigned amount + ) +{ + bool result = true; + unsigned int left = amount; + + while(left && result) + { + unsigned int tmp_amount; + + tmp_amount = std::min(left, 3u); + result &= ModeAction(mode, duration, tempo, color, tmp_amount); + mode += tmp_amount; + duration += tmp_amount; + tempo += tmp_amount; + color += tmp_amount; + left -= tmp_amount; + } + + return(result); +} + +bool AlienwareController::SetColorDirect(RGBColor color, std::vector zones) +{ + /*-----------------------------------------------------*\ + | Bail if zones is empty | + \*-----------------------------------------------------*/ + if(zones.empty()) + { + return(true); + } + + unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00, per hidapi | + \*-----------------------------------------------------*/ + uint16_t num_zones = (uint16_t)zones.size(); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x03; + usb_buf[0x02] = ALIENWARE_COMMAND_SET_COLOR; + usb_buf[0x03] = RGBGetRValue(color); + usb_buf[0x04] = RGBGetGValue(color); + usb_buf[0x05] = RGBGetBValue(color); + usb_buf[0x06] = num_zones >> 8; + usb_buf[0x07] = num_zones & 0xFF; + + for(size_t i = 0; i < num_zones; i++) + { + usb_buf[0x08 + i] = zones[i]; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + SendHIDReport(dev, usb_buf, sizeof(usb_buf)); + + HidapiAlienwareReport response = GetResponse(); + + /*-----------------------------------------------------*\ + | For this command, error is if the output equals the | + | input | + \*-----------------------------------------------------*/ + return((response.data[1] == 0x03) && memcmp(usb_buf, response.data, HIDAPI_ALIENWARE_REPORT_SIZE)); +} + +bool AlienwareController::Reset() +{ + /*-----------------------------------------------------*\ + | Bail if zones is empty | + \*-----------------------------------------------------*/ + if(zones.empty()) + { + return(true); + } + + unsigned char usb_buf[HIDAPI_ALIENWARE_REPORT_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet with leading 00, per hidapi | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x03; + usb_buf[0x02] = ALIENWARE_COMMAND_RESET; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + SendHIDReport(dev, usb_buf, sizeof(usb_buf)); + + HidapiAlienwareReport response = GetResponse(); + + /*-----------------------------------------------------*\ + | For this command, error is if the output equals the | + | input | + \*-----------------------------------------------------*/ + return(response.data[1] == 0x03); +} + +void AlienwareController::SetMode(uint8_t zone, uint8_t mode) +{ + if(mode != zones[zone].mode) + { + zones[zone].mode = mode; + dirty = true; + } +} + +void AlienwareController::SetColor(uint8_t zone, RGBColor color) +{ + SetColor(zone, color, zones[zone].color[1]); +} + +void AlienwareController::SetColor(uint8_t zone, RGBColor color1, RGBColor color2) +{ + if ((color1 == zones[zone].color[0]) && (color2 == zones[zone].color[1])) + { + return; + } + + zones[zone].color[0] = color1; + zones[zone].color[1] = color2; + dirty = true; +} + +void AlienwareController::SetPeriod(uint8_t zone, uint16_t period) +{ + if(period != zones[zone].period) + { + zones[zone].period = period; + dirty = true; + } +} + +void AlienwareController::SetTempo(uint8_t zone, uint16_t tempo) +{ + if(tempo != zones[zone].tempo) + { + zones[zone].tempo = tempo; + dirty = true; + } +} + +void AlienwareController::SetDim(uint8_t zone, uint8_t dim) +{ + if(dim != zones[zone].dim) + { + zones[zone].dim = dim; + dirty_dim = true; + } +} + +void AlienwareController::UpdateDim() +{ + if(!dirty_dim) + { + return; + } + + /*-----------------------------------------------------*\ + | Collect all zones that share dim settings, and update | + | them together | + \*-----------------------------------------------------*/ + std::map> dim_zone_map; + + for(size_t i = 0; i < zones.size(); i++) + { + dim_zone_map[zones[i].dim].emplace_back((uint8_t)i); + } + + for(std::pair> &pair : dim_zone_map) + { + /*-------------------------------------------------*\ + | Bail on an error... | + \*-------------------------------------------------*/ + if(!Dim(pair.second, pair.first)) + { + return; + } + } + + dirty_dim = false; +} + +bool AlienwareController::UpdateDirect() +{ + /*-----------------------------------------------------*\ + | Collect all zones that share dim settings, and update | + | them together | + \*-----------------------------------------------------*/ + std::map> color_zone_map; + + for(size_t i = 0; i < zones.size(); i++) + { + color_zone_map[zones[i].color[0]].emplace_back((uint8_t)i); + } + + for(std::pair> &pair : color_zone_map) + { + /*-------------------------------------------------*\ + | Bail on an error... | + \*-------------------------------------------------*/ + if(!SetColorDirect(pair.first, pair.second)) + { + return false; + } + } + return true; +} + +static const RGBColor rainbow_colors[4][7] = +{ + { 0xFF0000, 0xFFA500, 0xFFFF00, 0x008000, 0x00BFFF, 0x0000FF, 0x800080 }, + { 0x800080, 0xFF0000, 0xFFA500, 0xFFFF00, 0x008000, 0x00BFFF, 0x0000FF }, + { 0x0000FF, 0x800080, 0xFF0000, 0xFFA500, 0xFFFF00, 0x008000, 0x00BFFF }, + { 0x00BFFF, 0x0000FF, 0x800080, 0xFF0000, 0xFFA500, 0xFFFF00, 0x008000 } +}; + +void AlienwareController::UpdateMode() +{ + /*-----------------------------------------------------*\ + | If there are no updates, don't bother running this | + \*-----------------------------------------------------*/ + if(!dirty) + { + return; + } + + bool result = UserAnimation(ALIENWARE_COMMAND_USER_ANIM_NEW, ALIENWARE_COMMAND_USER_ANIM_KEYBOARD, 0); + + if(!result) + { + return; + } + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + alienware_zone zone = zones[zone_idx]; + + result = SelectZones({static_cast(zone_idx)}); + + if(!result) + { + return; + } + + /*-------------------------------------------------*\ + | Some modes use 0x07D0 for their duration as sent | + | by AWCC traces, maybe 2000ms? | + \*-------------------------------------------------*/ + switch (zone.mode) + { + case ALIENWARE_MODE_COLOR: + result = ModeAction(zone.mode, 2000, ALIENWARE_TEMPO_MAX, zone.color[0]); + break; + + case ALIENWARE_MODE_PULSE: + result = ModeAction(zone.mode, zone.period, zone.tempo, zone.color[0]); + break; + + case ALIENWARE_MODE_MORPH: + { + uint8_t zones[2] = { zone.mode, zone.mode }; + uint16_t periods[2] = { zone.period, zone.period }; + uint16_t tempos[2] = { zone.tempo, zone.tempo }; + RGBColor colors[2] = { zone.color[0], zone.color[1] }; + + result = MultiModeAction(zones, periods, tempos, colors, 2); + } + break; + + case ALIENWARE_MODE_SPECTRUM: + { + uint8_t zones[7] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH, + ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH, + ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH, + ALIENWARE_MODE_MORPH }; + uint16_t periods[7] = { zone.period, zone.period, + zone.period, zone.period, + zone.period, zone.period, + zone.period }; + uint16_t tempos[7] = { zone.tempo, zone.tempo, + zone.tempo, zone.tempo, + zone.tempo, zone.tempo, + zone.tempo }; + + result = MultiModeAction(zones, periods, tempos, rainbow_colors[0], 7); + } + break; + + case ALIENWARE_MODE_RAINBOW: + { + uint8_t zones[7] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH, + ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH, + ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH, + ALIENWARE_MODE_MORPH }; + uint16_t periods[7] = { zone.period, zone.period, + zone.period, zone.period, + zone.period, zone.period, + zone.period }; + uint16_t tempos[7] = { zone.tempo, zone.tempo, + zone.tempo, zone.tempo, + zone.tempo, zone.tempo, + zone.tempo }; + + result = MultiModeAction(zones, periods, tempos, rainbow_colors[zone_idx], 7); + } + break; + + case ALIENWARE_MODE_BREATHING: + { + uint8_t zones[2] = { ALIENWARE_MODE_MORPH, ALIENWARE_MODE_MORPH }; + uint16_t periods[2] = { zone.period, zone.period }; + uint16_t tempos[2] = { zone.tempo, zone.tempo }; + RGBColor colors[2] = { zone.color[0], 0x0 }; + + result = MultiModeAction(zones, periods, tempos, colors, 2); + } + break; + + default: + result = false; + } + + if(!result) + { + return; + } + } + + result = UserAnimation(ALIENWARE_COMMAND_USER_ANIM_FINISH_PLAY, ALIENWARE_COMMAND_USER_ANIM_KEYBOARD, 0); + + /*-------------------------------------------------*\ + | Don't update dirty flag if there's an error | + \*-------------------------------------------------*/ + if(!result) + { + return; + } + + dirty = false; +} + +void AlienwareController::UpdateController() +{ + UpdateMode(); + UpdateDim(); +} diff --git a/Controllers/AlienwareController/AlienwareController.h b/Controllers/AlienwareController/AlienwareController.h new file mode 100644 index 0000000..50492d2 --- /dev/null +++ b/Controllers/AlienwareController/AlienwareController.h @@ -0,0 +1,163 @@ +/*---------------------------------------------------------*\ +| AlienwareController.h | +| | +| Driver for Dell Alienware RGB USB controller | +| | +| Gabriel Marcano (gemarcano) 19 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*----------------------------------------------------------------------------------------------*\ +| Definitions for Alienware Controller | +\*----------------------------------------------------------------------------------------------*/ + +#define ALIENWARE_REPORT_SIZE 33 +#define HIDAPI_ALIENWARE_REPORT_SIZE (ALIENWARE_REPORT_SIZE + 1) +#define ALIENWARE_CONTROLLER_NAME "AlienWare Controller" +enum +{ + ALIENWARE_COMMAND_REPORT = 0x20, /* Set report type to get */ + ALIENWARE_COMMAND_USER_ANIM = 0x21, /* Set user animation settings */ + ALIENWARE_COMMAND_POWER_ANIM = 0x22, /* Set power animation settings */ + ALIENWARE_COMMAND_SELECT_ZONES = 0x23, /* Select zones to apply actions to */ + ALIENWARE_COMMAND_ADD_ACTION = 0x24, /* Set actions to apply */ + ALIENWARE_COMMAND_UNKNOWN1 = 0x25, /* Supposedly set event? */ + ALIENWARE_COMMAND_DIM = 0x26, /* Set dim percentage */ + ALIENWARE_COMMAND_SET_COLOR = 0x27, /* Unclear (causes color flash) */ + ALIENWARE_COMMAND_RESET = 0x28, /* Reset */ + ALIENWARE_COMMAND_ERASE_FLASH = 0xFF, /* Erases flash memory on controller */ +}; + +enum +{ + ALIENWARE_COMMAND_REPORT_FIRMWARE = 0x00, /* Get firmware verion */ + ALIENWARE_COMMAND_REPORT_STATUS = 0x01, /* Get status */ + ALIENWARE_COMMAND_REPORT_CONFIG = 0x02, /* Get firmware config */ + ALIENWARE_COMMAND_REPORT_ANIMATION = 0x03, /* Get animation count and last id */ + ALIENWARE_COMMAND_REPORT_UNKNOWN1 = 0x04, /* Get ELC animation by ID */ + ALIENWARE_COMMAND_REPORT_UNKNOWN2 = 0x05, /* Read series??? */ + ALIENWARE_COMMAND_REPORT_UNKNOWN3 = 0x06, /* Get action??? */ + ALIENWARE_COMMAND_REPORT_UNKNOWN4 = 0x07, /* Get Caldera status??? */ +}; + +enum +{ + ALIENWARE_COMMAND_USER_ANIM_NEW = 0x0001, /* Start new animation */ + ALIENWARE_COMMAND_USER_ANIM_FINISH_SAVE = 0x0002, /* Finish and save animation */ + ALIENWARE_COMMAND_USER_ANIM_FINISH_PLAY = 0x0003, /* Finish and play animation */ + ALIENWARE_COMMAND_USER_ANIM_REMOVE = 0x0004, /* Remove/erase animation */ + ALIENWARE_COMMAND_USER_ANIM_PLAY = 0x0005, /* Play animation */ + ALIENWARE_COMMAND_USER_ANIM_DEFAULT = 0x0006, /* Set default animation */ + ALIENWARE_COMMAND_USER_ANIM_STARTUP = 0x0007, /* Set startup animation */ +}; + +enum +{ + ALIENWARE_ANIM_DEFAULT_STARTUP = 0x0008, /* Default slot for startup */ + ALIENWARE_ANIM_DEFAULT = 0x0061, /* Default slot */ + ALIENWARE_COMMAND_USER_ANIM_KEYBOARD = 0xFFFF, /* Non-saved animation slot */ +}; + +enum +{ + ALIENWARE_MODE_COLOR = 0x00, /* Action to set color mode */ + ALIENWARE_MODE_PULSE = 0x01, /* Action to set pulse mode */ + ALIENWARE_MODE_MORPH = 0x02, /* Action to set morph mode */ + ALIENWARE_MODE_SPECTRUM, /* Abitrary code for spectrum mode */ + ALIENWARE_MODE_RAINBOW, /* Arbitrary code for rainbow wave mode */ + ALIENWARE_MODE_BREATHING, /* Arbitrary code for rainbow wave mode */ +}; + +enum +{ + ALIENWARE_TEMPO_MIN = 0x0064, /* Min tempo (as used by AWCC) */ + ALIENWARE_TEMPO_MAX = 0x00FA, /* Max tempo (as used by AWCC) */ + ALIENWARE_TEMPO_SPECTRUM = 0x000F, /* Used by Spectrum mode */ +}; + +enum +{ + ALIENWARE_DURATION_LONG = 0x09C4, /* Min tempo (as used by AWCC) */ + ALIENWARE_DURATION_MED = 0x05DC, /* Max tempo (as used by AWCC) */ + ALIENWARE_DURATION_SHORT = 0x01F3, /* Max tempo (as used by AWCC) */ + ALIENWARE_DURATION_SPECTRUM = 0x01AC, /* Used by Spectrum mode */ +}; + +typedef struct +{ + unsigned char data[ALIENWARE_REPORT_SIZE]; +} AlienwareReport; + +class AlienwareController +{ +public: + AlienwareController(hid_device* dev_handle, const hid_device_info& info, std::string name); + ~AlienwareController(); + + std::string GetSerialString(); + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetFirmwareVersion(); + unsigned GetZoneCount(); + std::vector GetZoneNames(); + + void SetColor(uint8_t zone, RGBColor color); + void SetColor(uint8_t zone, RGBColor color1, RGBColor color2); + void SetMode(uint8_t zone, uint8_t mode); + void SetPeriod(uint8_t zone, uint16_t period); + void SetTempo(uint8_t zone, uint16_t tempo); + void SetDim(uint8_t zone, uint8_t dim); + AlienwareReport GetStatus(uint8_t subcommand); + + void UpdateDim(); + void UpdateMode(); + void UpdateController(); + +protected: + hid_device* dev; + +private: + typedef struct + { + RGBColor color[2]; + uint8_t mode; + uint16_t period; + uint16_t tempo; + uint8_t dim; + } alienware_zone; + + typedef struct + { + unsigned char data[HIDAPI_ALIENWARE_REPORT_SIZE]; + } HidapiAlienwareReport; + + std::string device_name; + std::string location; + std::vector zones; + std::string serial_number; + std::string version; + std::vector zone_names; + bool dirty; + bool dirty_dim; + + HidapiAlienwareReport GetResponse(); + HidapiAlienwareReport Report(uint8_t subcommand); + + bool Dim(std::vector zones, double percent); + bool UserAnimation(uint16_t subcommand, uint16_t animation, uint16_t duration); + bool SelectZones(const std::vector& zones); + bool ModeAction(uint8_t mode, uint16_t duration, uint16_t tempo, RGBColor color); + bool ModeAction(const uint8_t *mode, const uint16_t *duration, const uint16_t *tempo, const RGBColor *color, unsigned amount); + bool MultiModeAction(const uint8_t *mode, const uint16_t *duration, const uint16_t *tempo, const RGBColor *color, unsigned amount); + bool SetColorDirect(RGBColor color, std::vector zones); + bool UpdateDirect(); + bool Reset(); +}; diff --git a/Controllers/AlienwareController/AlienwareControllerDetect.cpp b/Controllers/AlienwareController/AlienwareControllerDetect.cpp new file mode 100644 index 0000000..9ae7a56 --- /dev/null +++ b/Controllers/AlienwareController/AlienwareControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| AlienwareControllerDetect.cpp | +| | +| Detector for Dell Alienware RGB USB controller | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AlienwareController.h" +#include "RGBController_Alienware.h" + +/*---------------------------------------------------------*\ +| Alienware vendor ID | +\*---------------------------------------------------------*/ +#define ALIENWARE_VID 0x187C + +/*---------------------------------------------------------*\ +| Alienware product ID | +\*---------------------------------------------------------*/ +#define ALIENWARE_G_SERIES_PID1 0x0550 +#define ALIENWARE_G_SERIES_PID2 0x0551 + +void DetectAlienwareControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AlienwareController* controller = new AlienwareController(dev, *info, name); + RGBController_Alienware* rgb_controller = new RGBController_Alienware(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("Dell G Series LED Controller", DetectAlienwareControllers, ALIENWARE_VID, ALIENWARE_G_SERIES_PID1); +REGISTER_HID_DETECTOR("Dell G Series LED Controller", DetectAlienwareControllers, ALIENWARE_VID, ALIENWARE_G_SERIES_PID2); diff --git a/Controllers/AlienwareController/RGBController_Alienware.cpp b/Controllers/AlienwareController/RGBController_Alienware.cpp new file mode 100644 index 0000000..1a80878 --- /dev/null +++ b/Controllers/AlienwareController/RGBController_Alienware.cpp @@ -0,0 +1,285 @@ +/*---------------------------------------------------------*\ +| RGBController_Alienware.cpp | +| | +| RGBController for Dell Alienware RGB USB controller | +| | +| Gabriel Marcano (gemarcano) 19 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_Alienware.h" + +/**------------------------------------------------------------------*\ + @name Alienware + @category Keyboard + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectAlienwareControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Alienware::RGBController_Alienware(AlienwareController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Alienware"; + type = DEVICE_TYPE_KEYBOARD; + description = "Alienware USB Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Color; + Color.name = "Static"; + Color.value = ALIENWARE_MODE_COLOR; + Color.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Color.color_mode = MODE_COLORS_PER_LED; + Color.colors_min = 1; + Color.colors_max = 1; + Color.brightness_min = 100; + Color.brightness_max = 0; + Color.brightness = 0; + modes.push_back(Color); + + mode Pulse; + Pulse.name = "Flashing"; + Pulse.value = ALIENWARE_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Pulse.color_mode = MODE_COLORS_PER_LED; + Pulse.colors_min = 1; + Pulse.colors_max = 1; + Pulse.speed_min = ALIENWARE_TEMPO_MIN; + Pulse.speed_max = ALIENWARE_TEMPO_MAX; + Pulse.speed = ALIENWARE_TEMPO_MIN; + Pulse.brightness_min = 100; + Pulse.brightness_max = 0; + Pulse.brightness = 0; + modes.push_back(Pulse); + + mode Morph; + Morph.name = "Morph"; + Morph.value = ALIENWARE_MODE_MORPH; + Morph.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Morph.color_mode = MODE_COLORS_MODE_SPECIFIC; + Morph.colors_min = 2 * controller->GetZoneCount(); + Morph.colors_max = Morph.colors_min; + Morph.colors.resize(Morph.colors_max); + Morph.speed_min = ALIENWARE_TEMPO_MIN; + Morph.speed_max = ALIENWARE_TEMPO_MAX; + Morph.speed = ALIENWARE_TEMPO_MIN; + Morph.brightness_min = 100; + Morph.brightness_max = 0; + Morph.brightness = 0; + modes.push_back(Morph); + + mode Spectrum; + Spectrum.name = "Spectrum Cycle"; + Spectrum.value = ALIENWARE_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.speed_min = ALIENWARE_TEMPO_SPECTRUM; + Spectrum.speed_max = ALIENWARE_TEMPO_MAX; + Spectrum.speed = ALIENWARE_TEMPO_SPECTRUM; + Spectrum.brightness_min = 100; + Spectrum.brightness_max = 0; + Spectrum.brightness = 0; + modes.push_back(Spectrum); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = ALIENWARE_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed_min = ALIENWARE_TEMPO_SPECTRUM; + Rainbow.speed_max = ALIENWARE_TEMPO_MAX; + Rainbow.speed = ALIENWARE_TEMPO_SPECTRUM; + Rainbow.brightness_min = 100; + Rainbow.brightness_max = 0; + Rainbow.brightness = 0; + modes.push_back(Rainbow); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ALIENWARE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.speed_min = ALIENWARE_TEMPO_MIN; + Breathing.speed_max = ALIENWARE_TEMPO_MAX; + Breathing.speed = ALIENWARE_TEMPO_MIN; + Breathing.brightness_min = 100; + Breathing.brightness_max = 0; + Breathing.brightness = 0; + modes.push_back(Breathing); + + SetupZones(); + + controller->UpdateDim(); +} + +void RGBController_Alienware::SetupZones() +{ + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + std::vector zone_names = controller->GetZoneNames(); + + for(unsigned int zone_idx = 0; zone_idx < controller->GetZoneCount(); zone_idx++) + { + zone new_zone; + + new_zone.name = zone_names[zone_idx]; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + } + + for(unsigned int led_idx = 0; led_idx < zones.size(); led_idx++) + { + led new_led; + + new_led.name = zones[led_idx].name + std::string(" LED"); + + leds.emplace_back(new_led); + } + + SetupColors(); +} + +void RGBController_Alienware::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Alienware::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_Alienware::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Alienware::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +static bool modes_eq(const mode& mode1, const mode& mode2) +{ + return( ( mode1.name == mode2.name ) + && ( mode1.value == mode2.value ) + && ( mode1.flags == mode2.flags ) + && ( mode1.speed_min == mode2.speed_min ) + && ( mode1.speed_max == mode2.speed_max ) + && ( mode1.colors_min == mode2.colors_min ) + && ( mode1.colors_max == mode2.colors_max ) + && ( mode1.speed == mode2.speed ) + && ( mode1.direction == mode2.direction ) + && ( mode1.color_mode == mode2.color_mode ) + && ( mode1.colors == mode2.colors ) + && ( mode1.brightness == mode2.brightness ) + && ( mode1.brightness_min == mode2.brightness_min ) + && ( mode1.brightness_max == mode2.brightness_max ) ); +} + +void RGBController_Alienware::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Copy mode to get the current state-- this is racy, as the | + | UI thread can be actively modifying this variable | + \*---------------------------------------------------------*/ + int current_mode_idx = active_mode; + mode current_mode = modes[current_mode_idx]; + + bool done = false; + + while(!done) + { + /*-----------------------------------------------------*\ + | Setup state per zone | + \*-----------------------------------------------------*/ + for(uint8_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + zone current_zone = zones[zone_idx]; + + /*-------------------------------------------------*\ + | Some modes use 2000ms (0x07D0) for their duration,| + | per traces | + \*-------------------------------------------------*/ + uint16_t period = 0x07d0; + + controller->SetMode(zone_idx, current_mode.value); + + switch(current_mode_idx) + { + case ALIENWARE_MODE_COLOR: + controller->SetPeriod(zone_idx, period); + controller->SetColor( zone_idx, colors[current_zone.start_idx]); + controller->SetTempo( zone_idx, ALIENWARE_TEMPO_MAX); + controller->SetDim( zone_idx, modes[current_mode_idx].brightness); + break; + + case ALIENWARE_MODE_PULSE: + controller->SetPeriod(zone_idx, period); + controller->SetColor( zone_idx, colors[current_zone.start_idx]); + controller->SetTempo( zone_idx, current_mode.speed); + controller->SetDim( zone_idx, modes[current_mode_idx].brightness); + break; + + case ALIENWARE_MODE_MORPH: + controller->SetPeriod(zone_idx, period); + controller->SetColor( zone_idx, current_mode.colors[zone_idx * 2], current_mode.colors[(zone_idx * 2) + 1]); + controller->SetTempo( zone_idx, current_mode.speed); + controller->SetDim( zone_idx, modes[current_mode_idx].brightness); + break; + + case ALIENWARE_MODE_SPECTRUM: + case ALIENWARE_MODE_RAINBOW: + controller->SetPeriod(zone_idx, ALIENWARE_DURATION_SPECTRUM); + controller->SetTempo( zone_idx, current_mode.speed); + controller->SetDim( zone_idx, modes[current_mode_idx].brightness); + break; + + case ALIENWARE_MODE_BREATHING: + controller->SetPeriod(zone_idx, period); + controller->SetColor( zone_idx, colors[current_zone.start_idx], 0x0); + controller->SetTempo( zone_idx, current_mode.speed); + controller->SetDim( zone_idx, modes[current_mode_idx].brightness); + break; + } + } + + /*-----------------------------------------------------*\ + | Due to rate-limiting, this can take more than one | + | second to execute | + \*-----------------------------------------------------*/ + controller->UpdateController(); + + /*-----------------------------------------------------*\ + | Re-run update if there's anything that's changed from | + | under us... | + \*-----------------------------------------------------*/ + int new_current_mode_idx = active_mode; + mode new_current_mode = modes[current_mode_idx]; + + done = (current_mode_idx == new_current_mode_idx && modes_eq(new_current_mode, current_mode)); + current_mode_idx = new_current_mode_idx; + current_mode = new_current_mode; + } +} diff --git a/Controllers/AlienwareController/RGBController_Alienware.h b/Controllers/AlienwareController/RGBController_Alienware.h new file mode 100644 index 0000000..2e0f16e --- /dev/null +++ b/Controllers/AlienwareController/RGBController_Alienware.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_Alienware.h | +| | +| RGBController for Dell Alienware RGB USB controller | +| | +| Gabriel Marcano (gemarcano) 19 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "AlienwareController.h" + +class RGBController_Alienware : public RGBController +{ +public: + RGBController_Alienware(AlienwareController* controller_ptr); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AlienwareController* controller; + std::chrono::steady_clock::time_point last_packet_ts; +}; diff --git a/Controllers/AlienwareKeyboardController/AlienwareAW410KController/AlienwareAW410KController.cpp b/Controllers/AlienwareKeyboardController/AlienwareAW410KController/AlienwareAW410KController.cpp new file mode 100644 index 0000000..3c679bc --- /dev/null +++ b/Controllers/AlienwareKeyboardController/AlienwareAW410KController/AlienwareAW410KController.cpp @@ -0,0 +1,453 @@ +/*---------------------------------------------------------*\ +| AlienwareAW410KController.cpp | +| | +| Driver for Alienware AW410K keyboard | +| | +| based on AW510K controller by Mohamad Sallal (msallal) | +| Dominik Mikolajczyk (dmiko) 23 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AlienwareAW410KController.h" +#include "StringUtils.h" + +AlienwareAW410KController::AlienwareAW410KController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + SendCommit(); +} + +AlienwareAW410KController::~AlienwareAW410KController() +{ + hid_close(dev); +} + +std::string AlienwareAW410KController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AlienwareAW410KController::GetDeviceName() +{ + return(name); +} + +std::string AlienwareAW410KController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void AlienwareAW410KController::SendCommit() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x05; + usb_buf[0x02] = 0x01; + usb_buf[0x0A] = 0x10; + usb_buf[0x0B] = 0x0A; + usb_buf[0x0C] = 0x01; + usb_buf[0x0D] = 0x02; + usb_buf[0x0E] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 20 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(20)); +} + +void AlienwareAW410KController::SendfeatureReport + ( + unsigned char first_byte, + unsigned char second_byte, + unsigned char third_byte, + unsigned char forth_byte + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Feature report packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = first_byte; + usb_buf[0x02] = second_byte; + usb_buf[0x03] = third_byte; + usb_buf[0x04] = forth_byte; + + /*-----------------------------------------------------*\ + | Send Feature report packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 10 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(10)); +} + +void AlienwareAW410KController::SendEdit() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Edit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x01; + usb_buf[0x02] = 0x00; + usb_buf[0x03] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 2 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void AlienwareAW410KController::SendInitialize() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x0E; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = 0xAD; + usb_buf[0x06] = 0x80; + usb_buf[0x07] = 0x10; + usb_buf[0x08] = 0xA5; + usb_buf[0x0A] = 0x0A; + usb_buf[0x12] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 2 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void AlienwareAW410KController::SetDirect + ( + unsigned char /*zone*/, + unsigned char r, + unsigned char g, + unsigned char b + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x05; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = r; + usb_buf[0x05] = g; + usb_buf[0x06] = b; + usb_buf[0x0B] = 0x0A; + usb_buf[0x0D] = 0x01; + usb_buf[0x0E] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 2 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void AlienwareAW410KController::SendDirectOn + ( + std::vector &frame_data + ) +{ + SendfeatureReport(0x0E, (unsigned char)frame_data.size(), 0x00, 0x01); + + /*-----------------------------------------------*\ + | To Guarantee the data are always %4 =0 append | + | zeros at end of last packet | + \*-----------------------------------------------*/ + for(unsigned int i = 0; i < (frame_data.size() % 4); i++) + { + SelectedButtons key; + key.idx = 0x00; + key.red = 0x00; + key.green = 0x00; + key.blue = 0x00; + + frame_data.push_back(key); + } + + unsigned char usb_buf[65]; + unsigned int frame_idx = 0; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, 65); + + for(unsigned int packet_idx = 0; packet_idx < frame_data.size(); packet_idx++) + { + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x0E; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = ++frame_idx; + usb_buf[0x05] = frame_data[packet_idx].idx; + usb_buf[0x06] = 0x81; + usb_buf[0x07] = 0x00; + usb_buf[0x08] = 0xA5; + usb_buf[0x0A] = 0x0A; + usb_buf[0x0B] = frame_data[packet_idx].red; + usb_buf[0x0C] = frame_data[packet_idx].green; + usb_buf[0x0D] = frame_data[packet_idx].blue; + usb_buf[0x12] = 0x01; + + usb_buf[0x14] = frame_data[++packet_idx].idx; + usb_buf[0x15] = 0x81; + usb_buf[0x16] = 0x00; + usb_buf[0x17] = 0xA5; + usb_buf[0x19] = 0x0A; + usb_buf[0x1A] = frame_data[packet_idx].red; + usb_buf[0x1B] = frame_data[packet_idx].green; + usb_buf[0x1C] = frame_data[packet_idx].blue; + usb_buf[0x21] = 0x01; + + usb_buf[0x23] = frame_data[++packet_idx].idx; + usb_buf[0x24] = 0x81; + usb_buf[0x25] = 0x00; + usb_buf[0x26] = 0xA5; + usb_buf[0x28] = 0x0A; + usb_buf[0x29] = frame_data[packet_idx].red; + usb_buf[0x2A] = frame_data[packet_idx].green; + usb_buf[0x2B] = frame_data[packet_idx].blue; + usb_buf[0x30] = 0x01; + + usb_buf[0x32] = frame_data[++packet_idx].idx; + usb_buf[0x33] = 0x81; + usb_buf[0x34] = 0x00; + usb_buf[0x35] = 0xA5; + usb_buf[0x37] = 0x0A; + usb_buf[0x38] = frame_data[packet_idx].red; + usb_buf[0x39] = frame_data[packet_idx].green; + usb_buf[0x3A] = frame_data[packet_idx].blue; + usb_buf[0x3F] = 0x01; + + hid_write(dev, (unsigned char *)usb_buf, 65); + } +} + + +void AlienwareAW410KController::SetMode + ( + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char colorMode, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SendMode(ALIENWARE_AW410K_ZONE_MODE_KEYBOARD, mode, speed, direction, colorMode, red, green, blue); + SendCommit(); +} + +void AlienwareAW410KController::UpdateSingleLED + ( + unsigned char led, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SendfeatureReport(0x0E, 0x01, 0x00, 0x01); + + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Single LED packet | + \*-----------------------------------------------------*/ + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x0E; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = led; + usb_buf[0x06] = 0x81; + usb_buf[0x07] = 0x00; + usb_buf[0x08] = 0xA5; + usb_buf[0x09] = 0x00; + usb_buf[0x0A] = 0x0A; + usb_buf[0x0B] = red; + usb_buf[0x0C] = green; + usb_buf[0x0D] = blue; + usb_buf[0x12] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 20 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + +} +void AlienwareAW410KController::SendMode + ( + unsigned char /*zone*/, + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char colorMode, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Mode Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x05; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = mode; + usb_buf[0x04] = red; + usb_buf[0x05] = green; + usb_buf[0x06] = blue; + usb_buf[0x0A] = speed; + usb_buf[0x0B] = 0x0A; + usb_buf[0x0D] = 0x01; + usb_buf[0x0E] = colorMode; + usb_buf[0x0F] = direction; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} + +void AlienwareAW410KController::SetMorphMode + ( + unsigned char mode, + unsigned char speed, + unsigned char red1, + unsigned char green1, + unsigned char blue1, + unsigned char red2, + unsigned char green2, + unsigned char blue2 + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Morph Mode packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x05; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = mode; + usb_buf[0x04] = red1; + usb_buf[0x05] = green1; + usb_buf[0x06] = blue1; + usb_buf[0x07] = red2; + usb_buf[0x08] = green2; + usb_buf[0x09] = blue2; + usb_buf[0x0E] = 0x02; + usb_buf[0x0A] = speed; + usb_buf[0x0B] = 0x0A; + usb_buf[0x0D] = 0x01; + usb_buf[0x0E] = ALIENWARE_AW410K_TWO_USER_DEFINED_COLOR_MODE; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} diff --git a/Controllers/AlienwareKeyboardController/AlienwareAW410KController/AlienwareAW410KController.h b/Controllers/AlienwareKeyboardController/AlienwareAW410KController/AlienwareAW410KController.h new file mode 100644 index 0000000..59a4735 --- /dev/null +++ b/Controllers/AlienwareKeyboardController/AlienwareAW410KController/AlienwareAW410KController.h @@ -0,0 +1,157 @@ +/*---------------------------------------------------------*\ +| AlienwareAW410KController.h | +| | +| Driver for Alienware AW410K keyboard | +| | +| based on AW510K controller by Mohamad Sallal (msallal) | +| Dominik Mikolajczyk (dmiko) 23 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + ALIENWARE_AW410K_ZONE_MODE_KEYBOARD = 0x01, +}; + +enum +{ + ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD = 0x01, + ALIENWARE_AW410K_ZONE_DIRECT_MEDIA = 0x02, + ALIENWARE_AW410K_ZONE_DIRECT_INDICATORS = 0x40, +}; + +enum +{ + ALIENWARE_AW410K_MODE_OFF = 0x00, + ALIENWARE_AW410K_MODE_DIRECT = 0x01, + ALIENWARE_AW410K_MODE_PULSE = 0x02, + ALIENWARE_AW410K_MODE_MORPH = 0x03, + ALIENWARE_AW410K_MODE_BREATHING = 0x07, + ALIENWARE_AW410K_MODE_SPECTRUM = 0x08, + ALIENWARE_AW410K_MODE_SINGLE_WAVE = 0x0F, + ALIENWARE_AW410K_MODE_RAINBOW_WAVE = 0x10, + ALIENWARE_AW410K_MODE_SCANNER = 0x11, + ALIENWARE_AW410K_MODE_STATIC = 0x13, +}; + +enum +{ + ALIENWARE_AW410K_SPEED_SLOWEST = 0x2D, /* Slowest speed */ + ALIENWARE_AW410K_SPEED_NORMAL = 0x19, /* Normal speed */ + ALIENWARE_AW410K_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +enum +{ + ALIENWARE_AW410K_DIRECTION_LEFT_TO_RIGHT = 0x01, + ALIENWARE_AW410K_DIRECTION_RIGHT_TO_LEFT = 0x02, + ALIENWARE_AW410K_DIRECTION_TOP_TO_BOTTOM = 0x03, + ALIENWARE_AW410K_DIRECTION_BOTTOM_TO_TOP = 0x04, +}; + +enum +{ + ALIENWARE_AW410K_SINGLE_COLOR_MODE = 0x01, + ALIENWARE_AW410K_TWO_USER_DEFINED_COLOR_MODE= 0x02, + ALIENWARE_AW410K_RANBOW_COLOR_MODE = 0x03, +}; + +struct SelectedButtons +{ + unsigned char idx; + unsigned char red; + unsigned char green; + unsigned char blue; +}; + +class AlienwareAW410KController +{ +public: + AlienwareAW410KController(hid_device* dev_handle, const char* path, std::string dev_name); + ~AlienwareAW410KController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + void SendInitialize(); + void SendCommit(); + void SendfeatureReport + ( + unsigned char first_byte, + unsigned char second_byte, + unsigned char third_byte, + unsigned char forth_byte + ); + + void SendEdit(); + + void SetDirect + ( + unsigned char zone, + unsigned char r, + unsigned char g, + unsigned char b + ); + + void SendDirectOn + ( + std::vector &frame_data + ); + + void SetMode + ( + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char colorMode, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SetMorphMode + ( + unsigned char mode, + unsigned char speed, + unsigned char red1, + unsigned char green1, + unsigned char blue1, + unsigned char red2, + unsigned char green2, + unsigned char blue2 + ); + + void UpdateSingleLED + ( + unsigned char led, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char colorMode, + unsigned char red, + unsigned char green, + unsigned char blue + ); +}; diff --git a/Controllers/AlienwareKeyboardController/AlienwareAW410KController/RGBController_AlienwareAW410K.cpp b/Controllers/AlienwareKeyboardController/AlienwareAW410KController/RGBController_AlienwareAW410K.cpp new file mode 100644 index 0000000..afb9415 --- /dev/null +++ b/Controllers/AlienwareKeyboardController/AlienwareAW410KController/RGBController_AlienwareAW410K.cpp @@ -0,0 +1,482 @@ +/*---------------------------------------------------------*\ +| RGBController_AlienwareAW410K.cpp | +| | +| RGBController for Alienware AW410K keyboard | +| | +| based on AW510K controller by Mohamad Sallal (msallal) | +| Dominik Mikolajczyk (dmiko) 23 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBControllerKeyNames.h" +#include "RGBController_AlienwareAW410K.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +int GetAW410K_WaveDirection(int input); + +static unsigned int matrix_map[6][24] = +{ { 0, NA, 1, 2, 3, 4, NA, 5, 6, 7, 8, 9, 10, 11, 12, NA, 13, 14, 15, NA, NA, 16, 17, 18 }, + { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, NA, NA, 33, 34, 35, NA, 36, 37, 38, 39 }, + { 40, NA, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, NA, 54, 55, 56, NA, 57, 58, 59, 60 }, + { 61, NA, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, NA, 73, NA, NA, NA, NA, NA, 74, 75, 76, NA }, + { 77, NA, NA, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, NA, NA, NA, 89, NA, NA, 90, 91, 92, 93 }, + { 94, NA, 95, 96, NA, NA, NA, 97, NA, NA, NA, 98, 99, 100, 101, NA, 102, 103, 104, NA, 105, NA, 106, NA }}; + +static const char* zone_names[] = +{ + "AW410K", +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 107, +}; + +typedef struct +{ + const char * name; + const unsigned char zone; + const unsigned char idx; +} aw410k_led_type; + +static const aw410k_led_type led_names[] = +{ + /* Key Label Zone, Index */ + { KEY_EN_ESCAPE, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xB0 }, + { KEY_EN_F1, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x98 }, + { KEY_EN_F2, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x90 }, + { KEY_EN_F3, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x88 }, + { KEY_EN_F4, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x80 }, + { KEY_EN_F5, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x70 }, + { KEY_EN_F6, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x68 }, + { KEY_EN_F7, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x60 }, + { KEY_EN_F8, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x58 }, + { KEY_EN_F9, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x50 }, + { KEY_EN_F10, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x48 }, + { KEY_EN_F11, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x40 }, + { KEY_EN_F12, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x38 }, + { KEY_EN_PRINT_SCREEN, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x30 }, + { KEY_EN_SCROLL_LOCK, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x28 }, + { KEY_EN_PAUSE_BREAK, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x20 }, + { KEY_EN_MEDIA_MUTE, ALIENWARE_AW410K_ZONE_DIRECT_MEDIA, 0x18 }, + { KEY_EN_MEDIA_VOLUME_DOWN, ALIENWARE_AW410K_ZONE_DIRECT_MEDIA, 0x10 }, + { KEY_EN_MEDIA_VOLUME_UP, ALIENWARE_AW410K_ZONE_DIRECT_MEDIA, 0x08 }, + { KEY_EN_BACK_TICK, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xB1 }, + { KEY_EN_1, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xA1 }, + { KEY_EN_2, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x99 }, + { KEY_EN_3, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x91 }, + { KEY_EN_4, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x89 }, + { KEY_EN_5, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x81 }, + { KEY_EN_6, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x79 }, + { KEY_EN_7, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x71 }, + { KEY_EN_8, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x69 }, + { KEY_EN_9, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x61 }, + { KEY_EN_0, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x59 }, + { KEY_EN_MINUS, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x51 }, + { KEY_EN_EQUALS, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x49 }, + { KEY_EN_BACKSPACE, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x39 }, + { KEY_EN_INSERT, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x31 }, + { KEY_EN_HOME, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x29 }, + { KEY_EN_PAGE_UP, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x21 }, + { KEY_EN_NUMPAD_LOCK, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x19 }, + { KEY_EN_NUMPAD_DIVIDE, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x11 }, + { KEY_EN_NUMPAD_TIMES, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x09 }, + { KEY_EN_NUMPAD_MINUS, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x01 }, + { KEY_EN_TAB, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xB2 }, + { KEY_EN_Q, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xA2 }, + { KEY_EN_W, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x9A }, + { KEY_EN_E, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x92 }, + { KEY_EN_R, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x8A }, + { KEY_EN_T, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x82 }, + { KEY_EN_Y, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x7A }, + { KEY_EN_U, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x72 }, + { KEY_EN_I, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x6A }, + { KEY_EN_O, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x62 }, + { KEY_EN_P, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x5A }, + { KEY_EN_LEFT_BRACKET, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x52 }, + { KEY_EN_RIGHT_BRACKET, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x4A }, + { KEY_EN_ANSI_BACK_SLASH, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x42 }, + { KEY_EN_DELETE, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x32 }, + { KEY_EN_END, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x2A }, + { KEY_EN_PAGE_DOWN, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x22 }, + { KEY_EN_NUMPAD_7, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x1A }, + { KEY_EN_NUMPAD_8, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x12 }, + { KEY_EN_NUMPAD_9, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x0A }, + { KEY_EN_NUMPAD_PLUS, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x03 }, + { KEY_EN_CAPS_LOCK, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xB3 }, + { KEY_EN_A, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xA3 }, + { KEY_EN_S, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x9B }, + { KEY_EN_D, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x93 }, + { KEY_EN_F, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x8B }, + { KEY_EN_G, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x83 }, + { KEY_EN_H, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x7B }, + { KEY_EN_J, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x73 }, + { KEY_EN_K, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x6B }, + { KEY_EN_L, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x63 }, + { KEY_EN_SEMICOLON, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x5B }, + { KEY_EN_QUOTE, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x53 }, + { KEY_EN_ANSI_ENTER, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x43 }, + { KEY_EN_NUMPAD_4, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x1B }, + { KEY_EN_NUMPAD_5, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x13 }, + { KEY_EN_NUMPAD_6, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x0B }, + { KEY_EN_LEFT_SHIFT, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xB4 }, + { KEY_EN_Z, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xA4 }, + { KEY_EN_X, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x9C }, + { KEY_EN_C, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x94 }, + { KEY_EN_V, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x8C }, + { KEY_EN_B, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x84 }, + { KEY_EN_N, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x7C }, + { KEY_EN_M, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x74 }, + { KEY_EN_COMMA, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x6C }, + { KEY_EN_PERIOD, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x64 }, + { KEY_EN_FORWARD_SLASH, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x5C }, + { KEY_EN_RIGHT_SHIFT, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x4C }, + { KEY_EN_UP_ARROW, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x2C }, + { KEY_EN_NUMPAD_1, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x1C }, + { KEY_EN_NUMPAD_2, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x14 }, + { KEY_EN_NUMPAD_3, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x0C }, + { KEY_EN_NUMPAD_ENTER, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x05 }, + { KEY_EN_LEFT_CONTROL, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xB5 }, + { KEY_EN_LEFT_WINDOWS, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xAD }, + { KEY_EN_LEFT_ALT, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0xA5 }, + { KEY_EN_SPACE, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x85 }, + { KEY_EN_RIGHT_ALT, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x65 }, + { KEY_EN_RIGHT_FUNCTION, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x5D }, + { KEY_EN_MENU, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x55 }, + { KEY_EN_RIGHT_CONTROL, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x4D }, + { KEY_EN_LEFT_ARROW, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x35 }, + { KEY_EN_DOWN_ARROW, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x2D }, + { KEY_EN_RIGHT_ARROW, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x25 }, + { KEY_EN_NUMPAD_0, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x1D }, + { KEY_EN_NUMPAD_PERIOD, ALIENWARE_AW410K_ZONE_DIRECT_KEYBOARD, 0x0D } +}; + +/**------------------------------------------------------------------*\ + @name Alienware AW410 Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAlienwareAW410KControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AlienwareAW410K::RGBController_AlienwareAW410K(AlienwareAW410KController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Alienware"; + type = DEVICE_TYPE_KEYBOARD; + description = "Alienware AW410K Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ALIENWARE_AW410K_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = ALIENWARE_AW410K_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = ALIENWARE_AW410K_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulse.speed_min = ALIENWARE_AW410K_SPEED_SLOWEST; + Pulse.speed_max = ALIENWARE_AW410K_SPEED_NORMAL; + Pulse.speed = ALIENWARE_AW410K_SPEED_NORMAL; + Pulse.colors_min = 1; + Pulse.colors_max = 1; + Pulse.colors.resize(1); + modes.push_back(Pulse); + + mode Morph; + Morph.name = "Morph"; + Morph.value = ALIENWARE_AW410K_MODE_MORPH; + Morph.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Morph.color_mode = MODE_COLORS_MODE_SPECIFIC; + Morph.speed_min = ALIENWARE_AW410K_SPEED_SLOWEST; + Morph.speed_max = ALIENWARE_AW410K_SPEED_NORMAL; + Morph.speed = ALIENWARE_AW410K_SPEED_NORMAL; + Morph.colors_min = 2; + Morph.colors_max = 2; + Morph.colors.resize(2); + modes.push_back(Morph); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ALIENWARE_AW410K_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + Breathing.speed_min = ALIENWARE_AW410K_SPEED_SLOWEST; + Breathing.speed_max = ALIENWARE_AW410K_SPEED_FASTEST; + Breathing.speed = ALIENWARE_AW410K_SPEED_NORMAL; + modes.push_back(Breathing); + + mode SingleWave; + SingleWave.name = "Single Wave"; + SingleWave.value = ALIENWARE_AW410K_MODE_SINGLE_WAVE; + SingleWave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + SingleWave.speed_min = ALIENWARE_AW410K_SPEED_SLOWEST; + SingleWave.speed_max = ALIENWARE_AW410K_SPEED_FASTEST; + SingleWave.speed = ALIENWARE_AW410K_SPEED_NORMAL; + SingleWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + SingleWave.colors_min = 1; + SingleWave.colors_max = 1; + SingleWave.colors.resize(1); + modes.push_back(SingleWave); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = ALIENWARE_AW410K_MODE_RAINBOW_WAVE; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + RainbowWave.speed_min = ALIENWARE_AW410K_SPEED_SLOWEST; + RainbowWave.speed_max = ALIENWARE_AW410K_SPEED_FASTEST; + RainbowWave.speed = ALIENWARE_AW410K_SPEED_NORMAL; + RainbowWave.colors_min = 1; + RainbowWave.colors_max = 1; + RainbowWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + RainbowWave.colors.resize(1); + modes.push_back(RainbowWave); + + mode Off; + Off.name = "Off"; + Off.value = ALIENWARE_AW410K_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + std::copy(colors.begin(), colors.end(),std::back_inserter(current_colors)); +} + +RGBController_AlienwareAW410K::~RGBController_AlienwareAW410K() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_AlienwareAW410K::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 24; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx].name; + new_led.value = led_names[led_idx].idx; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_AlienwareAW410K::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AlienwareAW410K::DeviceUpdateLEDs() +{ + std::vector frame_buf_keys; + std::vector new_colors; + + std::copy(colors.begin(), colors.end(),std::back_inserter(new_colors)); + + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + SelectedButtons key; + + key.idx = (unsigned char)leds[led_idx].value; + key.red = RGBGetRValue(colors[led_idx]); + key.green = RGBGetGValue(colors[led_idx]); + key.blue = RGBGetBValue(colors[led_idx]); + + frame_buf_keys.push_back(key); + } + + controller->SendInitialize(); + controller->SendfeatureReport(0x05, 0x01, 0x51, 0x00); + controller->SendCommit(); + + if(frame_buf_keys.size() > 0) + { + controller->SendDirectOn(frame_buf_keys); + } + + std::copy(new_colors.begin(), new_colors.end(),current_colors.begin()); +} + +void RGBController_AlienwareAW410K::UpdateZoneLEDs(int zone) +{ + controller->SetDirect((unsigned char) zone, RGBGetRValue(zones[zone].colors[0]), RGBGetGValue(zones[zone].colors[0]), RGBGetBValue(zones[zone].colors[0])); +} + +void RGBController_AlienwareAW410K::UpdateSingleLED(int led) +{ + controller->UpdateSingleLED(leds[led].value, RGBGetRValue(colors[led]), RGBGetGValue(colors[led]), RGBGetBValue(colors[led])); +} + +void RGBController_AlienwareAW410K::DeviceUpdateMode() +{ + if(active_mode == 0xFFFF) + { + UpdateLEDs(); + return; + } + + controller->SendfeatureReport(0x05, 0x01, 0x51, 0x00); + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + switch(modes[active_mode].value) + { + case ALIENWARE_AW410K_MODE_DIRECT: + /*-------------------------------------------------------------*\ + | Load LEDs again in case of profile load etc. | + \*-------------------------------------------------------------*/ + DeviceUpdateLEDs(); + break; + case ALIENWARE_AW410K_MODE_MORPH: + /*-------------------------------------------------------------*\ + | In case of morph it requires two colors (color1 and color2) | + \*-------------------------------------------------------------*/ + { + unsigned char red2 = RGBGetRValue(modes[active_mode].colors[1]); + unsigned char grn2 = RGBGetGValue(modes[active_mode].colors[1]); + unsigned char blu2 = RGBGetBValue(modes[active_mode].colors[1]); + + controller->SetMorphMode(modes[active_mode].value, modes[active_mode].speed, red, grn, blu, red2, grn2, blu2); + } + break; + + case ALIENWARE_AW410K_MODE_SPECTRUM: + /*-------------------------------------------------------------*\ + | Spectrum only set mode, speed and colorMode | + \*-------------------------------------------------------------*/ + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, 0x00, ALIENWARE_AW410K_RANBOW_COLOR_MODE, 0x00, 0x00, 0x00); + break; + + case ALIENWARE_AW410K_MODE_SINGLE_WAVE: + /*-------------------------------------------------------------*\ + | Wave only set mode, speed, direction and colorMode | + \*-------------------------------------------------------------*/ + { + int waveDirection = GetAW410K_WaveDirection(modes[active_mode].direction); + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, waveDirection, ALIENWARE_AW410K_SINGLE_COLOR_MODE, red, grn, blu); + } + break; + + case ALIENWARE_AW410K_MODE_RAINBOW_WAVE: + /*-------------------------------------------------------------*\ + | Wave only set mode, speed, direction and colorMode | + \*-------------------------------------------------------------*/ + { + int waveDirection = GetAW410K_WaveDirection(modes[active_mode].direction); + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, waveDirection, ALIENWARE_AW410K_RANBOW_COLOR_MODE, 0x00, 0x00, 0x00); + } + break; + + + default: + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, 0x00, ALIENWARE_AW410K_SINGLE_COLOR_MODE, red, grn, blu); + break; + } +} + +int GetAW410K_WaveDirection(int input) +{ + switch(input) + { + case MODE_DIRECTION_LEFT: + return(ALIENWARE_AW410K_DIRECTION_RIGHT_TO_LEFT); + + case MODE_DIRECTION_RIGHT: + return(ALIENWARE_AW410K_DIRECTION_LEFT_TO_RIGHT); + + case MODE_DIRECTION_UP: + return(ALIENWARE_AW410K_DIRECTION_BOTTOM_TO_TOP); + + case MODE_DIRECTION_DOWN: + return(ALIENWARE_AW410K_DIRECTION_TOP_TO_BOTTOM); + + default: + return(ALIENWARE_AW410K_DIRECTION_RIGHT_TO_LEFT); + } +} + diff --git a/Controllers/AlienwareKeyboardController/AlienwareAW410KController/RGBController_AlienwareAW410K.h b/Controllers/AlienwareKeyboardController/AlienwareAW410KController/RGBController_AlienwareAW410K.h new file mode 100644 index 0000000..776596a --- /dev/null +++ b/Controllers/AlienwareKeyboardController/AlienwareAW410KController/RGBController_AlienwareAW410K.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_AlienwareAW410K.h | +| | +| RGBController for Alienware AW410K keyboard | +| | +| based on AW510K controller by Mohamad Sallal (msallal) | +| Dominik Mikolajczyk (dmiko) 23 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AlienwareAW410KController.h" + +class RGBController_AlienwareAW410K : public RGBController +{ +public: + RGBController_AlienwareAW410K(AlienwareAW410KController* controller_ptr); + ~RGBController_AlienwareAW410K(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AlienwareAW410KController* controller; + std::vector current_colors; +}; diff --git a/Controllers/AlienwareKeyboardController/AlienwareAW510KController/AlienwareAW510KController.cpp b/Controllers/AlienwareKeyboardController/AlienwareAW510KController/AlienwareAW510KController.cpp new file mode 100644 index 0000000..af7e44b --- /dev/null +++ b/Controllers/AlienwareKeyboardController/AlienwareAW510KController/AlienwareAW510KController.cpp @@ -0,0 +1,452 @@ +/*---------------------------------------------------------*\ +| AlienwareAW510KController.cpp | +| | +| Driver for Alienware AW510K keyboard | +| | +| Mohamad Sallal (msallal) 22 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AlienwareAW510KController.h" +#include "StringUtils.h" + +AlienwareAW510KController::AlienwareAW510KController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + SendCommit(); +} + +AlienwareAW510KController::~AlienwareAW510KController() +{ + hid_close(dev); +} + +std::string AlienwareAW510KController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AlienwareAW510KController::GetDeviceName() +{ + return(name); +} + +std::string AlienwareAW510KController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void AlienwareAW510KController::SendCommit() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x05; + usb_buf[0x02] = 0x01; + usb_buf[0x0A] = 0x10; + usb_buf[0x0B] = 0x0A; + usb_buf[0x0C] = 0x01; + usb_buf[0x0D] = 0x02; + usb_buf[0x0E] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 20 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(20)); +} + +void AlienwareAW510KController::SendfeatureReport + ( + unsigned char first_byte, + unsigned char second_byte, + unsigned char third_byte, + unsigned char forth_byte + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Feature report packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = first_byte; + usb_buf[0x02] = second_byte; + usb_buf[0x03] = third_byte; + usb_buf[0x04] = forth_byte; + + /*-----------------------------------------------------*\ + | Send Feature report packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 10 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(10)); +} + +void AlienwareAW510KController::SendEdit() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Edit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x01; + usb_buf[0x02] = 0x00; + usb_buf[0x03] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 2 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void AlienwareAW510KController::SendInitialize() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x0E; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = 0xAD; + usb_buf[0x06] = 0x80; + usb_buf[0x07] = 0x10; + usb_buf[0x08] = 0xA5; + usb_buf[0x0A] = 0x0A; + usb_buf[0x12] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 2 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void AlienwareAW510KController::SetDirect + ( + unsigned char /*zone*/, + unsigned char r, + unsigned char g, + unsigned char b + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x05; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = r; + usb_buf[0x05] = g; + usb_buf[0x06] = b; + usb_buf[0x0B] = 0x0A; + usb_buf[0x0D] = 0x01; + usb_buf[0x0E] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 2 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void AlienwareAW510KController::SendDirectOn + ( + std::vector &frame_data + ) +{ + SendfeatureReport(0x0E, (unsigned char)frame_data.size(), 0x00, 0x01); + + /*-----------------------------------------------*\ + | To Guarantee the data are always %4 =0 append | + | zeros at end of last packet | + \*-----------------------------------------------*/ + for(unsigned int i = 0; i < (frame_data.size() % 4); i++) + { + SelectedKeys key; + key.idx = 0x00; + key.red = 0x00; + key.green = 0x00; + key.blue = 0x00; + + frame_data.push_back(key); + } + + unsigned char usb_buf[65]; + unsigned int frame_idx = 0; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, 65); + + for(unsigned int packet_idx = 0; packet_idx < frame_data.size(); packet_idx++) + { + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x0E; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = ++frame_idx; + usb_buf[0x05] = frame_data[packet_idx].idx; + usb_buf[0x06] = 0x81; + usb_buf[0x07] = 0x00; + usb_buf[0x08] = 0xA5; + usb_buf[0x0A] = 0x0A; + usb_buf[0x0B] = frame_data[packet_idx].red; + usb_buf[0x0C] = frame_data[packet_idx].green; + usb_buf[0x0D] = frame_data[packet_idx].blue; + usb_buf[0x12] = 0x01; + + usb_buf[0x14] = frame_data[++packet_idx].idx; + usb_buf[0x15] = 0x81; + usb_buf[0x16] = 0x00; + usb_buf[0x17] = 0xA5; + usb_buf[0x19] = 0x0A; + usb_buf[0x1A] = frame_data[packet_idx].red; + usb_buf[0x1B] = frame_data[packet_idx].green; + usb_buf[0x1C] = frame_data[packet_idx].blue; + usb_buf[0x21] = 0x01; + + usb_buf[0x23] = frame_data[++packet_idx].idx; + usb_buf[0x24] = 0x81; + usb_buf[0x25] = 0x00; + usb_buf[0x26] = 0xA5; + usb_buf[0x28] = 0x0A; + usb_buf[0x29] = frame_data[packet_idx].red; + usb_buf[0x2A] = frame_data[packet_idx].green; + usb_buf[0x2B] = frame_data[packet_idx].blue; + usb_buf[0x30] = 0x01; + + usb_buf[0x32] = frame_data[++packet_idx].idx; + usb_buf[0x33] = 0x81; + usb_buf[0x34] = 0x00; + usb_buf[0x35] = 0xA5; + usb_buf[0x37] = 0x0A; + usb_buf[0x38] = frame_data[packet_idx].red; + usb_buf[0x39] = frame_data[packet_idx].green; + usb_buf[0x3A] = frame_data[packet_idx].blue; + usb_buf[0x3F] = 0x01; + + hid_write(dev, (unsigned char *)usb_buf, 65); + } +} + + +void AlienwareAW510KController::SetMode + ( + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char colorMode, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SendMode(ALIENWARE_AW510K_ZONE_MODE_KEYBOARD, mode, speed, direction, colorMode, red, green, blue); + SendCommit(); +} + +void AlienwareAW510KController::UpdateSingleLED + ( + unsigned char led, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SendfeatureReport(0x0E, 0x01, 0x00, 0x01); + + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Single LED packet | + \*-----------------------------------------------------*/ + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x0E; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = led; + usb_buf[0x06] = 0x81; + usb_buf[0x07] = 0x00; + usb_buf[0x08] = 0xA5; + usb_buf[0x09] = 0x00; + usb_buf[0x0A] = 0x0A; + usb_buf[0x0B] = red; + usb_buf[0x0C] = green; + usb_buf[0x0D] = blue; + usb_buf[0x12] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 20 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + +} +void AlienwareAW510KController::SendMode + ( + unsigned char /*zone*/, + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char colorMode, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Mode Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x05; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = mode; + usb_buf[0x04] = red; + usb_buf[0x05] = green; + usb_buf[0x06] = blue; + usb_buf[0x0A] = speed; + usb_buf[0x0B] = 0x0A; + usb_buf[0x0D] = 0x01; + usb_buf[0x0E] = colorMode; + usb_buf[0x0F] = direction; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} + +void AlienwareAW510KController::SetMorphMode + ( + unsigned char mode, + unsigned char speed, + unsigned char red1, + unsigned char green1, + unsigned char blue1, + unsigned char red2, + unsigned char green2, + unsigned char blue2 + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Morph Mode packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x05; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = mode; + usb_buf[0x04] = red1; + usb_buf[0x05] = green1; + usb_buf[0x06] = blue1; + usb_buf[0x07] = red2; + usb_buf[0x08] = green2; + usb_buf[0x09] = blue2; + usb_buf[0x0E] = 0x02; + usb_buf[0x0A] = speed; + usb_buf[0x0B] = 0x0A; + usb_buf[0x0D] = 0x01; + usb_buf[0x0E] = ALIENWARE_AW510K_TWO_USER_DEFINED_COLOR_MODE; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} diff --git a/Controllers/AlienwareKeyboardController/AlienwareAW510KController/AlienwareAW510KController.h b/Controllers/AlienwareKeyboardController/AlienwareAW510KController/AlienwareAW510KController.h new file mode 100644 index 0000000..b5dd0a3 --- /dev/null +++ b/Controllers/AlienwareKeyboardController/AlienwareAW510KController/AlienwareAW510KController.h @@ -0,0 +1,159 @@ +/*---------------------------------------------------------*\ +| AlienwareAW510KController.h | +| | +| Driver for Alienware AW510K keyboard | +| | +| Mohamad Sallal (msallal) 22 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + ALIENWARE_AW510K_ZONE_MODE_KEYBOARD = 0x01, + ALIENWARE_AW510K_ZONE_MODE_LOGO = 0x07, /* logo is only need key value, which equal 07 */ +}; + +enum +{ + ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD = 0x01, + ALIENWARE_AW510K_ZONE_DIRECT_MEDIA = 0x02, + ALIENWARE_AW510K_ZONE_DIRECT_LOGO = 0x07, + ALIENWARE_AW510K_ZONE_DIRECT_INDICATORS = 0x40, +}; + +enum +{ + ALIENWARE_AW510K_MODE_OFF = 0x00, + ALIENWARE_AW510K_MODE_DIRECT_PER_LED = 0x01, + ALIENWARE_AW510K_MODE_DIRECT = 0x01, + ALIENWARE_AW510K_MODE_PULSE = 0x02, + ALIENWARE_AW510K_MODE_MORPH = 0x03, + ALIENWARE_AW510K_MODE_BREATHING = 0x07, + ALIENWARE_AW510K_MODE_SPECTRUM = 0x08, + ALIENWARE_AW510K_MODE_SINGLE_WAVE = 0x0F, + ALIENWARE_AW510K_MODE_RAINBOW_WAVE = 0x10, + ALIENWARE_AW510K_MODE_SCANNER = 0x11, + ALIENWARE_AW510K_MODE_STATIC = 0x13, +}; + +enum +{ + ALIENWARE_AW510K_SPEED_SLOWEST = 0x2D, /* Slowest speed */ + ALIENWARE_AW510K_SPEED_NORMAL = 0x19, /* Normal speed */ + ALIENWARE_AW510K_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +enum +{ + ALIENWARE_AW510K_DIRECTION_LEFT_TO_RIGHT = 0x01, + ALIENWARE_AW510K_DIRECTION_RIGHT_TO_LEFT = 0x02, + ALIENWARE_AW510K_DIRECTION_TOP_TO_BOTTOM = 0x03, + ALIENWARE_AW510K_DIRECTION_BOTTOM_TO_TOP = 0x04, +}; + +enum +{ + ALIENWARE_AW510K_SINGLE_COLOR_MODE = 0x01, + ALIENWARE_AW510K_TWO_USER_DEFINED_COLOR_MODE= 0x02, + ALIENWARE_AW510K_RANBOW_COLOR_MODE = 0x03, +}; + +struct SelectedKeys +{ + unsigned char idx; + unsigned char red; + unsigned char green; + unsigned char blue; +}; + +class AlienwareAW510KController +{ +public: + AlienwareAW510KController(hid_device* dev_handle, const char* path, std::string dev_name); + ~AlienwareAW510KController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + void SendInitialize(); + void SendCommit(); + void SendfeatureReport + ( + unsigned char first_byte, + unsigned char second_byte, + unsigned char third_byte, + unsigned char forth_byte + ); + + void SendEdit(); + + void SetDirect + ( + unsigned char zone, + unsigned char r, + unsigned char g, + unsigned char b + ); + + void SendDirectOn + ( + std::vector &frame_data + ); + + void SetMode + ( + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char colorMode, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SetMorphMode + ( + unsigned char mode, + unsigned char speed, + unsigned char red1, + unsigned char green1, + unsigned char blue1, + unsigned char red2, + unsigned char green2, + unsigned char blue2 + ); + + void UpdateSingleLED + ( + unsigned char led, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char colorMode, + unsigned char red, + unsigned char green, + unsigned char blue + ); +}; diff --git a/Controllers/AlienwareKeyboardController/AlienwareAW510KController/RGBController_AlienwareAW510K.cpp b/Controllers/AlienwareKeyboardController/AlienwareAW510KController/RGBController_AlienwareAW510K.cpp new file mode 100644 index 0000000..2ca26ad --- /dev/null +++ b/Controllers/AlienwareKeyboardController/AlienwareAW510KController/RGBController_AlienwareAW510K.cpp @@ -0,0 +1,497 @@ +/*---------------------------------------------------------*\ +| RGBController_AlienwareAW510K.cpp | +| | +| RGBController for Alienware AW510K keyboard | +| | +| Mohamad Sallal (msallal) 22 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBControllerKeyNames.h" +#include "RGBController_AlienwareAW510K.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +int GetAW520K_WaveDirection(int input); + +static unsigned int matrix_map[7][24] = +{ { 0, NA, 1, 2, 3, 4, NA, 5, 6, 7, 8, 9, 10, 11, 12, NA, 13, 14, 15, NA, 16, NA, NA, NA }, + { 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, NA, NA, 31, 32, 33, NA, 34, 35, 36, 37 }, + { 38, NA, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, NA, 52, 53, 54, NA, 55, 56, 57, 74 }, + { 58, NA, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, NA, 70, NA, NA, NA, NA, NA, 71, 72, 73, NA }, + { 75, NA, NA, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, NA, NA, NA, 87, NA, NA, 88, 89, 90, 104 }, + { 91, NA, 92, 93, NA, 94, NA, NA, NA, NA, NA, 95, 96, 97, 98, NA, 99, 100, 101, NA, 102, NA, 103, NA }, + { NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 105, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA }}; + +static const char* zone_names[] = +{ + "AW510K", +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 106, +}; + +typedef struct +{ + const char * name; + const unsigned char zone; + const unsigned char idx; +} aw510k_led_type; + +static const aw510k_led_type led_names[] = +{ + /* Key Label Zone, Index */ + { KEY_EN_ESCAPE, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xB0 }, + { KEY_EN_F1, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x98 }, + { KEY_EN_F2, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x90 }, + { KEY_EN_F3, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x88 }, + { KEY_EN_F4, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x80 }, + { KEY_EN_F5, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x70 }, + { KEY_EN_F6, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x68 }, + { KEY_EN_F7, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x60 }, + { KEY_EN_F8, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x58 }, + { KEY_EN_F9, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x50 }, + { KEY_EN_F10, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x48 }, + { KEY_EN_F11, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x40 }, + { KEY_EN_F12, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x38 }, + { KEY_EN_PRINT_SCREEN, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x30 }, + { KEY_EN_SCROLL_LOCK, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x28 }, + { KEY_EN_PAUSE_BREAK, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x20 }, + { KEY_EN_MEDIA_MUTE, ALIENWARE_AW510K_ZONE_DIRECT_MEDIA, 0x18 }, + { KEY_EN_BACK_TICK, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xB1 }, + { KEY_EN_1, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xA1 }, + { KEY_EN_2, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x99 }, + { KEY_EN_3, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x91 }, + { KEY_EN_4, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x89 }, + { KEY_EN_5, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x81 }, + { KEY_EN_6, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x79 }, + { KEY_EN_7, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x71 }, + { KEY_EN_8, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x69 }, + { KEY_EN_9, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x61 }, + { KEY_EN_0, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x59 }, + { KEY_EN_MINUS, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x51 }, + { KEY_EN_EQUALS, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x49 }, + { KEY_EN_BACKSPACE, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x39 }, + { KEY_EN_INSERT, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x31 }, + { KEY_EN_HOME, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x29 }, + { KEY_EN_PAGE_UP, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x21 }, + { KEY_EN_NUMPAD_LOCK, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x19 }, + { KEY_EN_NUMPAD_DIVIDE, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x11 }, + { KEY_EN_NUMPAD_TIMES, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x09 }, + { KEY_EN_NUMPAD_MINUS, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x01 }, + { KEY_EN_TAB, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xB2 }, + { KEY_EN_Q, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xA2 }, + { KEY_EN_W, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x9A }, + { KEY_EN_E, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x92 }, + { KEY_EN_R, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x8A }, + { KEY_EN_T, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x82 }, + { KEY_EN_Y, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x7A }, + { KEY_EN_U, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x72 }, + { KEY_EN_I, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x6A }, + { KEY_EN_O, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x62 }, + { KEY_EN_P, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x5A }, + { KEY_EN_LEFT_BRACKET, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x52 }, + { KEY_EN_RIGHT_BRACKET, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x4A }, + { KEY_EN_ANSI_BACK_SLASH, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x42 },//ANSI only + { KEY_EN_DELETE, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x32 }, + { KEY_EN_END, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x2A }, + { KEY_EN_PAGE_DOWN, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x22 }, + { KEY_EN_NUMPAD_7, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x1A }, + { KEY_EN_NUMPAD_8, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x12 }, + { KEY_EN_NUMPAD_9, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x0A }, + { KEY_EN_CAPS_LOCK, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xB3 }, + { KEY_EN_A, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xA3 }, + { KEY_EN_S, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x9B }, + { KEY_EN_D, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x93 }, + { KEY_EN_F, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x8B }, + { KEY_EN_G, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x83 }, + { KEY_EN_H, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x7B }, + { KEY_EN_J, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x73 }, + { KEY_EN_K, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x6B }, + { KEY_EN_L, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x63 }, + { KEY_EN_SEMICOLON, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x5B }, + { KEY_EN_QUOTE, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x53 }, + { KEY_EN_ANSI_ENTER, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x43 }, + { KEY_EN_NUMPAD_4, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x1B }, + { KEY_EN_NUMPAD_5, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x13 }, + { KEY_EN_NUMPAD_6, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x0B }, + { KEY_EN_NUMPAD_PLUS, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x03 }, + { KEY_EN_LEFT_SHIFT, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xB4 }, + { KEY_EN_Z, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xA4 }, + { KEY_EN_X, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x9C }, + { KEY_EN_C, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x94 }, + { KEY_EN_V, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x8C }, + { KEY_EN_B, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x84 }, + { KEY_EN_N, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x7C }, + { KEY_EN_M, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x74 }, + { KEY_EN_COMMA, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x6C }, + { KEY_EN_PERIOD, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x64 }, + { KEY_EN_FORWARD_SLASH, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x5C }, + { KEY_EN_RIGHT_SHIFT, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x4C }, + { KEY_EN_UP_ARROW, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x2C }, + { KEY_EN_NUMPAD_1, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x1C }, + { KEY_EN_NUMPAD_2, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x14 }, + { KEY_EN_NUMPAD_3, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x0C }, + { KEY_EN_LEFT_CONTROL, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xB5 }, + { KEY_EN_LEFT_WINDOWS, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xAD }, + { KEY_EN_LEFT_ALT, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0xA5 }, + { KEY_EN_SPACE, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x85 }, + { KEY_EN_RIGHT_ALT, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x65 }, + { KEY_EN_RIGHT_FUNCTION, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x5D }, + { KEY_EN_MENU, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x55 }, + { KEY_EN_RIGHT_CONTROL, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x4D }, + { KEY_EN_LEFT_ARROW, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x35 }, + { KEY_EN_DOWN_ARROW, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x2D }, + { KEY_EN_RIGHT_ARROW, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x25 }, + { KEY_EN_NUMPAD_0, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x1D }, + { KEY_EN_NUMPAD_PERIOD, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x0D }, + { KEY_EN_NUMPAD_ENTER, ALIENWARE_AW510K_ZONE_DIRECT_KEYBOARD, 0x05 }, + { "Logo", ALIENWARE_AW510K_ZONE_DIRECT_LOGO, 0x07 } +}; + +/**------------------------------------------------------------------*\ + @name Alienware AW510 Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAlienwareAW510KControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AlienwareAW510K::RGBController_AlienwareAW510K(AlienwareAW510KController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Alienware"; + type = DEVICE_TYPE_KEYBOARD; + description = "Alienware AW510K Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ALIENWARE_AW510K_MODE_DIRECT_PER_LED; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = ALIENWARE_AW510K_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = ALIENWARE_AW510K_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulse.speed_min = ALIENWARE_AW510K_SPEED_SLOWEST; + Pulse.speed_max = ALIENWARE_AW510K_SPEED_NORMAL; + Pulse.speed = ALIENWARE_AW510K_SPEED_NORMAL; + Pulse.colors_min = 1; + Pulse.colors_max = 1; + Pulse.colors.resize(1); + modes.push_back(Pulse); + + mode Morph; + Morph.name = "Morph"; + Morph.value = ALIENWARE_AW510K_MODE_MORPH; + Morph.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Morph.color_mode = MODE_COLORS_MODE_SPECIFIC; + Morph.speed_min = ALIENWARE_AW510K_SPEED_SLOWEST; + Morph.speed_max = ALIENWARE_AW510K_SPEED_NORMAL; + Morph.speed = ALIENWARE_AW510K_SPEED_NORMAL; + Morph.colors_min = 2; + Morph.colors_max = 2; + Morph.colors.resize(2); + modes.push_back(Morph); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ALIENWARE_AW510K_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + Breathing.speed_min = ALIENWARE_AW510K_SPEED_SLOWEST; + Breathing.speed_max = ALIENWARE_AW510K_SPEED_FASTEST; + Breathing.speed = ALIENWARE_AW510K_SPEED_NORMAL; + modes.push_back(Breathing); + + mode SingleWave; + SingleWave.name = "Single Wave"; + SingleWave.value = ALIENWARE_AW510K_MODE_SINGLE_WAVE; + SingleWave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + SingleWave.speed_min = ALIENWARE_AW510K_SPEED_SLOWEST; + SingleWave.speed_max = ALIENWARE_AW510K_SPEED_FASTEST; + SingleWave.speed = ALIENWARE_AW510K_SPEED_NORMAL; + SingleWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + SingleWave.colors_min = 1; + SingleWave.colors_max = 1; + SingleWave.colors.resize(1); + modes.push_back(SingleWave); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = ALIENWARE_AW510K_MODE_RAINBOW_WAVE; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + RainbowWave.speed_min = ALIENWARE_AW510K_SPEED_SLOWEST; + RainbowWave.speed_max = ALIENWARE_AW510K_SPEED_FASTEST; + RainbowWave.speed = ALIENWARE_AW510K_SPEED_NORMAL; + RainbowWave.colors_min = 1; + RainbowWave.colors_max = 1; + RainbowWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + RainbowWave.colors.resize(1); + modes.push_back(RainbowWave); + + mode Off; + Off.name = "Off"; + Off.value = ALIENWARE_AW510K_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + std::copy(colors.begin(), colors.end(),std::back_inserter(current_colors)); +} + +RGBController_AlienwareAW510K::~RGBController_AlienwareAW510K() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_AlienwareAW510K::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 7; + new_zone.matrix_map->width = 24; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx].name; + new_led.value = led_names[led_idx].idx; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_AlienwareAW510K::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AlienwareAW510K::DeviceUpdateLEDs() +{ + std::vector frame_buf_keys; + std::vector new_colors; + + std::copy(colors.begin(), colors.end(),std::back_inserter(new_colors)); + + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + if (current_colors[led_idx]==new_colors[led_idx]) + { + /*-------------------------------------------------*\ + | Don't send if key color is not changed | + \*-------------------------------------------------*/ + continue; + } + + if(RGBGetRValue(colors[led_idx]) != 0x00 || RGBGetBValue(colors[led_idx]) != 0x00 || RGBGetBValue(colors[led_idx]) != 0x00) + { + SelectedKeys key; + + key.idx = (unsigned char)leds[led_idx].value; + key.red = RGBGetRValue(colors[led_idx]); + key.green = RGBGetGValue(colors[led_idx]); + key.blue = RGBGetBValue(colors[led_idx]); + + frame_buf_keys.push_back(key); + } + else + { + SelectedKeys key; + + key.idx = (unsigned char)leds[led_idx].value; + key.red = RGBGetRValue(colors[led_idx]); + key.green = RGBGetGValue(colors[led_idx]); + key.blue = RGBGetBValue(colors[led_idx]); + + frame_buf_keys.push_back(key); + } + } + + controller->SendInitialize(); + controller->SendfeatureReport(0x05, 0x01, 0x51, 0x00); + controller->SendCommit(); + + if(frame_buf_keys.size() > 0) + { + controller->SendDirectOn(frame_buf_keys); + } + + std::copy(new_colors.begin(), new_colors.end(),current_colors.begin()); +} + +void RGBController_AlienwareAW510K::UpdateZoneLEDs(int zone) +{ + controller->SetDirect((unsigned char) zone, RGBGetRValue(zones[zone].colors[0]), RGBGetGValue(zones[zone].colors[0]), RGBGetBValue(zones[zone].colors[0])); +} + +void RGBController_AlienwareAW510K::UpdateSingleLED(int led) +{ + controller->UpdateSingleLED(leds[led].value, RGBGetRValue(colors[led]), RGBGetGValue(colors[led]), RGBGetBValue(colors[led])); +} + +void RGBController_AlienwareAW510K::DeviceUpdateMode() +{ + if(active_mode == 0xFFFF) + { + UpdateLEDs(); + return; + } + + controller->SendfeatureReport(0x05, 0x01, 0x51, 0x00); + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + switch(modes[active_mode].value) + { + case ALIENWARE_AW510K_MODE_MORPH: + /*-------------------------------------------------------------*\ + | In case of morph it requires two colors (color1 and color2) | + \*-------------------------------------------------------------*/ + { + unsigned char red2 = RGBGetRValue(modes[active_mode].colors[1]); + unsigned char grn2 = RGBGetGValue(modes[active_mode].colors[1]); + unsigned char blu2 = RGBGetBValue(modes[active_mode].colors[1]); + + controller->SetMorphMode(modes[active_mode].value, modes[active_mode].speed, red, grn, blu, red2, grn2, blu2); + } + break; + + case ALIENWARE_AW510K_MODE_SPECTRUM: + /*-------------------------------------------------------------*\ + | Spectrum only set mode, speed and colorMode | + \*-------------------------------------------------------------*/ + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, 0x00, ALIENWARE_AW510K_RANBOW_COLOR_MODE, 0x00, 0x00, 0x00); + break; + + case ALIENWARE_AW510K_MODE_SINGLE_WAVE: + /*-------------------------------------------------------------*\ + | Wave only set mode, speed, direction and colorMode | + \*-------------------------------------------------------------*/ + { + int waveDirection = GetAW520K_WaveDirection(modes[active_mode].direction); + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, waveDirection, ALIENWARE_AW510K_SINGLE_COLOR_MODE, red, grn, blu); + } + break; + + case ALIENWARE_AW510K_MODE_RAINBOW_WAVE: + /*-------------------------------------------------------------*\ + | Wave only set mode, speed, direction and colorMode | + \*-------------------------------------------------------------*/ + { + int waveDirection = GetAW520K_WaveDirection(modes[active_mode].direction); + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, waveDirection, ALIENWARE_AW510K_RANBOW_COLOR_MODE, 0x00, 0x00, 0x00); + } + break; + + + default: + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, 0x00, ALIENWARE_AW510K_SINGLE_COLOR_MODE, red, grn, blu); + break; + } +} + +int GetAW520K_WaveDirection(int input) +{ + switch(input) + { + case MODE_DIRECTION_LEFT: + return(ALIENWARE_AW510K_DIRECTION_RIGHT_TO_LEFT); + + case MODE_DIRECTION_RIGHT: + return(ALIENWARE_AW510K_DIRECTION_LEFT_TO_RIGHT); + + case MODE_DIRECTION_UP: + return(ALIENWARE_AW510K_DIRECTION_BOTTOM_TO_TOP); + + case MODE_DIRECTION_DOWN: + return(ALIENWARE_AW510K_DIRECTION_TOP_TO_BOTTOM); + + default: + return(ALIENWARE_AW510K_DIRECTION_RIGHT_TO_LEFT); + } +} + diff --git a/Controllers/AlienwareKeyboardController/AlienwareAW510KController/RGBController_AlienwareAW510K.h b/Controllers/AlienwareKeyboardController/AlienwareAW510KController/RGBController_AlienwareAW510K.h new file mode 100644 index 0000000..e4f3e36 --- /dev/null +++ b/Controllers/AlienwareKeyboardController/AlienwareAW510KController/RGBController_AlienwareAW510K.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_AlienwareAW510K.h | +| | +| RGBController for Alienware AW510K keyboard | +| | +| Mohamad Sallal (msallal) 22 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AlienwareAW510KController.h" + +class RGBController_AlienwareAW510K : public RGBController +{ +public: + RGBController_AlienwareAW510K(AlienwareAW510KController* controller_ptr); + ~RGBController_AlienwareAW510K(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AlienwareAW510KController* controller; + std::vector current_colors; +}; diff --git a/Controllers/AlienwareKeyboardController/AlienwareKeyboardControllerDetect.cpp b/Controllers/AlienwareKeyboardController/AlienwareKeyboardControllerDetect.cpp new file mode 100644 index 0000000..429bf15 --- /dev/null +++ b/Controllers/AlienwareKeyboardController/AlienwareKeyboardControllerDetect.cpp @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| AlienwareKeyboardControllerDetect.cpp | +| | +| Detector for Alienware Keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "AlienwareAW510KController.h" +#include "AlienwareAW410KController.h" +#include "RGBController_AlienwareAW510K.h" +#include "RGBController_AlienwareAW410K.h" + +/*-----------------------------------------------------*\ +| Alienware vendor ID | +\*-----------------------------------------------------*/ +#define ALIENWARE_VID 0x04F2 + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define ALIENWARE_AW510K_PID 0x1830 +#define ALIENWARE_AW410K_PID 0x1968 + +/******************************************************************************************\ +* * +* DetectAlienwareKeyboardControllers * +* * +* Tests the USB address to see if a Alienware RGB Keyboard controller exists there. * +* * +\******************************************************************************************/ + +void DetectAlienwareAW510KControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + if( dev ) + { + AlienwareAW510KController* controller = new AlienwareAW510KController(dev, info->path, name); + RGBController_AlienwareAW510K* rgb_controller = new RGBController_AlienwareAW510K(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAlienwareAW410KControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + if( dev ) + { + AlienwareAW410KController* controller = new AlienwareAW410KController(dev, info->path, name); + RGBController_AlienwareAW410K* rgb_controller = new RGBController_AlienwareAW410K(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +}/* DetectAlienwareKeyboardControllers() */ + + +REGISTER_HID_DETECTOR_IPU("Alienware AW510K", DetectAlienwareAW510KControllers, ALIENWARE_VID, ALIENWARE_AW510K_PID, 0x02, 0xFF00, 0x01); +REGISTER_HID_DETECTOR_IPU("Alienware AW410K", DetectAlienwareAW410KControllers, ALIENWARE_VID, ALIENWARE_AW410K_PID, 0x02, 0xFF00, 0x01); diff --git a/Controllers/AlienwareMonitorController/AlienwareAW3423DWFController.cpp b/Controllers/AlienwareMonitorController/AlienwareAW3423DWFController.cpp new file mode 100644 index 0000000..969603e --- /dev/null +++ b/Controllers/AlienwareMonitorController/AlienwareAW3423DWFController.cpp @@ -0,0 +1,183 @@ +/*---------------------------------------------------------*\ +| AlienwareAW3423DWFController.cpp | +| | +| Driver for the Alienware AW3423DWF monitor | +| | +| Ferréol DUBOIS COLI (Fefe_du_973) 23 Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include + +#include "AlienwareAW3423DWFController.h" +#include "StringUtils.h" + +AlienwareAW3423DWFController::AlienwareAW3423DWFController(hid_device *dev_handle, const char *path) : dev(dev_handle), location(path){} + +void AlienwareAW3423DWFController::SendControlPacket(const unsigned char *data, size_t length) +{ + unsigned char buffer[256] = {0x00}; + memcpy(buffer + 1, data, length); + + hid_write(dev, buffer, length + 1); +} + +std::vector AlienwareAW3423DWFController::GetReportResponse() +{ + uint8_t buffer[193]; + + memset(buffer, 0, 193); + + hid_get_input_report(dev, buffer, 193); + + return std::vector(buffer + 1, buffer + 18); +} + +void AlienwareAW3423DWFController::PerformLogin() +{ + unsigned char init_packet[64] = + { + 0x40, 0xE1, 0x01 + }; + SendControlPacket(init_packet, 4); + + std::vector response = GetReportResponse(); + + std::vector key = GenerateKey(response); + + unsigned char login_packet[192] = {0x00}; + login_packet[0] = 0x40; + login_packet[1] = 0xE1; + login_packet[2] = 0x02; + memcpy(login_packet + 64, key.data(), key.size()); + + SendControlPacket(login_packet, 192); +} + +void AlienwareAW3423DWFController::SendColor(unsigned char led_id, unsigned char r, unsigned char g, unsigned char b) +{ + unsigned char led_id_2 = (led_id == 0x01) ? 0xf6 : (led_id == 0x02) ? 0xf5 + : (led_id == 0x08) ? 0xff + : 0xfc; + PerformLogin(); + + unsigned char color_packet[192] = {0x00}; + + color_packet[0] = 0x40; + color_packet[1] = 0xC6; + color_packet[6] = 0x0A; + color_packet[8] = 0x6E; + color_packet[10] = 0x82; + color_packet[64] = 0x51; + color_packet[65] = 0x87; + color_packet[66] = 0xD0; + color_packet[67] = 0x04; + + color_packet[68] = led_id; + color_packet[69] = r; + color_packet[70] = g; + color_packet[71] = b; + color_packet[72] = 0x64; + color_packet[73] = led_id_2; + + SendControlPacket(color_packet, 192); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); +} + +std::vector AlienwareAW3423DWFController::GenerateKey( + const std::vector &response) +{ + std::vector syn_key(8, 0); + + const std::vector oem_key = { + 0xf5, 0x3f, 0xc1, 0x39, 0x44, 0x3a, 0x31, 0x79, 0x0d, 0xb1, 0x82, 0x76 + }; + + size_t sk_idx = 0, ok_idx = 0; + while(ok_idx < oem_key.size() && sk_idx < 8) + { + unsigned char ok_sub_len = (oem_key[ok_idx] & 1) + ((oem_key[ok_idx] & 0x10) >> 4); + + for(unsigned int i = 0; i < ok_sub_len && sk_idx < 8; i++) + { + syn_key[sk_idx] = oem_key[ok_idx + 1] ^ oem_key[ok_idx]; + ok_idx++; + sk_idx++; + } + ok_idx++; + } + + std::vector out_buffer; + + uint16_t v15 = static_cast(response[15]) | + (static_cast(response[0]) << 8); + bool parity_odd = (std::bitset<16>(v15).count() % 2 != 0); + + if(parity_odd) + { + size_t end = std::min(8, response.size()); + out_buffer = std::vector(response.begin(), response.begin() + end); + + if(response.size() > 14) + { + unsigned char idx = response[14] & 0x07; + if((idx + 8) < (unsigned char)response.size()) + { + out_buffer[idx] ^= response[idx + 8]; + } + } + } + else + { + size_t start = std::min(8, response.size()); + size_t end = std::min(start + 8, response.size()); + out_buffer = std::vector(response.begin() + start, response.begin() + end); + + if(response.size() > 6) + { + unsigned char idx = response[6] & 0x07; + if(idx < response.size()) + { + out_buffer[idx] ^= response[idx]; + } + } + } + + for(size_t i = 0; i < 8 && i < out_buffer.size(); i++) + { + syn_key[i] ^= out_buffer[i]; + } + + return syn_key; +} + +AlienwareAW3423DWFController::~AlienwareAW3423DWFController() +{ + if(dev) + { + hid_close(dev); + dev = nullptr; + } +} + +std::string AlienwareAW3423DWFController::GetLocation() +{ + return "HID: " + location; +} + +std::string AlienwareAW3423DWFController::GetSerialString() +{ + wchar_t serial[256]; + if(hid_get_serial_number_string(dev, serial, 256) >= 0) + { + std::wstring ws(serial); + return StringUtils::wstring_to_string(ws); + } + return ""; +} diff --git a/Controllers/AlienwareMonitorController/AlienwareAW3423DWFController.h b/Controllers/AlienwareMonitorController/AlienwareAW3423DWFController.h new file mode 100644 index 0000000..6f8bd7c --- /dev/null +++ b/Controllers/AlienwareMonitorController/AlienwareAW3423DWFController.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| AlienwareAW3423DWFController.h | +| | +| Driver for the Alienware AW3423DWF monitor | +| | +| Ferréol DUBOIS COLI (Fefe_du_973) 23 Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +class AlienwareAW3423DWFController +{ +public: + AlienwareAW3423DWFController(hid_device* dev_handle, const char* path); + ~AlienwareAW3423DWFController(); + + std::string GetLocation(); + std::string GetSerialString(); + void SendColor(unsigned char led_id, unsigned char r, unsigned char g, unsigned char b); + +private: + hid_device* dev; + std::string location; + + static const std::vector> OEM_KEYS; + + void PerformLogin(); + std::vector GenerateKey(const std::vector& response); + void SendControlPacket(const unsigned char* data, size_t length); + std::vector GetReportResponse(); +}; diff --git a/Controllers/AlienwareMonitorController/AlienwareMonitorController.cpp b/Controllers/AlienwareMonitorController/AlienwareMonitorController.cpp new file mode 100644 index 0000000..db110de --- /dev/null +++ b/Controllers/AlienwareMonitorController/AlienwareMonitorController.cpp @@ -0,0 +1,146 @@ +/*---------------------------------------------------------*\ +| AlienwareMonitorController.cpp | +| | +| Detector for Alienware monitors | +| | +| Adam Honse (CalcProgrammer1) 08 May 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include + +#include "AlienwareMonitorController.h" + +AlienwareMonitorController::AlienwareMonitorController(hid_device *dev_handle, const char *path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + Initialize(); +} + +AlienwareMonitorController::~AlienwareMonitorController() +{ + hid_close(dev); +} + +std::string AlienwareMonitorController::GetLocation() +{ + return("HID: " + location); +} + +std::string AlienwareMonitorController::GetName() +{ + return(name); +} + +std::string AlienwareMonitorController::GetSerialString() +{ + return(""); +} + +void fillInChecksum(unsigned char *packet) +{ + unsigned char checksum = 110; + + for(unsigned int index = 5; index <= 13; index++) + { + checksum ^= packet[index]; + } + + packet[14] = checksum; +} + +void AlienwareMonitorController::SendColor(unsigned char led_id, unsigned char r, unsigned char g, unsigned char b) +{ + unsigned char packet[65]; + + memset(packet, 0xFF, sizeof(packet)); + + packet[0] = 0x00; + packet[1] = 0x92; + packet[2] = 0x37; + packet[3] = 0x0a; + packet[4] = 0x00; + packet[5] = 0x51; + packet[6] = 0x87; + packet[7] = 0xd0; + packet[8] = 0x04; + + packet[9] = led_id; + packet[10] = r; + packet[11] = g; + packet[12] = b; + packet[13] = 0x64; + + fillInChecksum(packet); + + hid_write(dev, packet, sizeof(packet)); + + /*-----------------------------------------------------*\ + | Delay 50 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(50)); +} + +void AlienwareMonitorController::Initialize() +{ + unsigned char packet[65]; + + memset(packet, 0xFF, sizeof(packet)); + + packet[0] = 0x00; + packet[1] = 0x95; + packet[2] = 0x00; + packet[3] = 0x00; + packet[4] = 0x00; + + hid_write(dev, packet, sizeof(packet)); + + /*-----------------------------------------------------*\ + | Delay 50 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + memset(packet, 0xFF, sizeof(packet)); + + packet[0] = 0x00; + packet[1] = 0x92; + packet[2] = 0x37; + packet[3] = 0x08; + packet[4] = 0x00; + packet[5] = 0x51; + packet[6] = 0x85; + packet[7] = 0x01; + packet[8] = 0xFE; + packet[9] = 0x03; + packet[10] = 0x00; + packet[11] = 0x06; + packet[12] = 0x40; + + hid_write(dev, packet, sizeof(packet)); + + /*-----------------------------------------------------*\ + | Delay 50 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + memset(packet, 0x00, sizeof(packet)); + + packet[0] = 0x00; + packet[1] = 0x93; + packet[2] = 0x37; + packet[3] = 0x12; + + hid_write(dev, packet, sizeof(packet)); + + /*-----------------------------------------------------*\ + | Delay 50 milliseconds | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(50)); +} diff --git a/Controllers/AlienwareMonitorController/AlienwareMonitorController.h b/Controllers/AlienwareMonitorController/AlienwareMonitorController.h new file mode 100644 index 0000000..c869413 --- /dev/null +++ b/Controllers/AlienwareMonitorController/AlienwareMonitorController.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| AlienwareMonitorController.h | +| | +| Detector for Alienware monitors | +| | +| Adam Honse (CalcProgrammer1) 08 May 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +class AlienwareMonitorController +{ +public: +AlienwareMonitorController(hid_device* dev_handle, const char* path, std::string dev_name); + ~AlienwareMonitorController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + void SendColor(unsigned char led_id, unsigned char r, unsigned char g, unsigned char b); + +private: + hid_device* dev; + std::string location; + std::string name; + + void Initialize(); +}; diff --git a/Controllers/AlienwareMonitorController/AlienwareMonitorControllerDetect.cpp b/Controllers/AlienwareMonitorController/AlienwareMonitorControllerDetect.cpp new file mode 100644 index 0000000..8b0d3c7 --- /dev/null +++ b/Controllers/AlienwareMonitorController/AlienwareMonitorControllerDetect.cpp @@ -0,0 +1,66 @@ +/*---------------------------------------------------------*\ +| AlienwareMonitorControllerDetect.cpp | +| | +| Detector for Alienware monitors | +| | +| Adam Honse (CalcProgrammer1) 08 May 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AlienwareAW3423DWFController.h" +#include "AlienwareMonitorController.h" +#include "RGBController_AlienwareAW3423DWF.h" +#include "RGBController_AlienwareMonitor.h" +#include + +/*---------------------------------------------------------*\ +| Alienware Vendor ID | +\*---------------------------------------------------------*/ +#define ALIENWARE_VID 0x187C + +/*---------------------------------------------------------*\ +| Alienware Vendor ID | +\*---------------------------------------------------------*/ +#define ALIENWARE_AW3423DWF_PID 0x100E +#define ALIENWARE_AW3225QF_PID 0x1013 +#define ALIENWARE_USAGE_PAGE 0xFFDA +#define ALIENWARE_USAGE 0x00DA + +/******************************************************************************************\ +* * +* AlienwareAW3423DWFControllerDetect * +* * +* Tests the USB address to see if an Alienware AW3423DWF exists there. * +* * +\******************************************************************************************/ + +void DetectAlienwareAW3423DWFControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + if(dev) + { + AlienwareAW3423DWFController* controller = new AlienwareAW3423DWFController(dev, info->path); + RGBController_AlienwareAW3423DWF* rgb_controller = new RGBController_AlienwareAW3423DWF(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAlienwareMonitorControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AlienwareMonitorController* controller = new AlienwareMonitorController(dev, info->path, name); + RGBController_AlienwareMonitor* rgb_controller = new RGBController_AlienwareMonitor(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("Alienware AW3423DWF", DetectAlienwareAW3423DWFControllers, ALIENWARE_VID, ALIENWARE_AW3423DWF_PID); +REGISTER_HID_DETECTOR("Alienware AW3225QF", DetectAlienwareMonitorControllers, ALIENWARE_VID, ALIENWARE_AW3225QF_PID); diff --git a/Controllers/AlienwareMonitorController/RGBController_AlienwareAW3423DWF.cpp b/Controllers/AlienwareMonitorController/RGBController_AlienwareAW3423DWF.cpp new file mode 100644 index 0000000..45ea7d0 --- /dev/null +++ b/Controllers/AlienwareMonitorController/RGBController_AlienwareAW3423DWF.cpp @@ -0,0 +1,141 @@ +/*---------------------------------------------------------*\ +| RGBController_AlienwareAW3423DWF.cpp | +| | +| RGBController for the Alienware AW3423DWF monitor | +| | +| Ferréol DUBOIS COLI (Fefe_du_973) 23 Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AlienwareAW3423DWF.h" + +/**------------------------------------------------------------------*\ + @name AW3423DWF + @category Accessory + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectAlienwareAW3423DWFControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AlienwareAW3423DWF::RGBController_AlienwareAW3423DWF(AlienwareAW3423DWFController* controller_ptr) +{ + controller = controller_ptr; + + name = "Alienware AW3423DWF"; + vendor = "Alienware"; + type = DEVICE_TYPE_MONITOR; + description = "Alienware AW3423DWF Monitor Device"; + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + active_mode = 0; + + SetupZones(); +} + +RGBController_AlienwareAW3423DWF::~RGBController_AlienwareAW3423DWF() +{ + delete controller; +} + +void RGBController_AlienwareAW3423DWF::SetupZones() +{ + zone Logo; + Logo.name = "Logo"; + Logo.type = ZONE_TYPE_SINGLE; + Logo.leds_min = 1; + Logo.leds_max = 1; + Logo.leds_count = 1; + Logo.matrix_map = NULL; + zones.push_back(Logo); + + led Logo_LED; + Logo_LED.name = "Logo"; + Logo_LED.value = 0x01; + leds.push_back(Logo_LED); + + zone Number; + Number.name = "Number"; + Number.type = ZONE_TYPE_SINGLE; + Number.leds_min = 1; + Number.leds_max = 1; + Number.leds_count = 1; + Number.matrix_map = NULL; + zones.push_back(Number); + + led Number_LED; + Number_LED.name = "Number"; + Number_LED.value = 0x02; + leds.push_back(Number_LED); + + zone PowerButton; + PowerButton.name = "Power Button"; + PowerButton.type = ZONE_TYPE_SINGLE; + PowerButton.leds_min = 1; + PowerButton.leds_max = 1; + PowerButton.leds_count = 1; + PowerButton.matrix_map = NULL; + zones.push_back(PowerButton); + + led PowerButton_LED; + PowerButton_LED.name = "Power Button"; + PowerButton_LED.value = 0x08; + leds.push_back(PowerButton_LED); + + SetupColors(); +} + +void RGBController_AlienwareAW3423DWF::ResizeZone(int /*zone*/, int /*new_size*/) +{ +} + +void RGBController_AlienwareAW3423DWF::DeviceUpdateLEDs() +{ + /*-----------------------------------------------------*\ + | If all three colors are the same value, speed up the | + | direct mode by using the ALL target (0x0B) instead of | + | setting each LED individually. | + \*-----------------------------------------------------*/ + if((colors[0] == colors[1]) && (colors[1] == colors[2])) + { + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + controller->SendColor(0x0B, red, grn, blu); + } + else + { + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + UpdateSingleLED(led_idx); + } + } +} + +void RGBController_AlienwareAW3423DWF::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AlienwareAW3423DWF::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + controller->SendColor(leds[led].value, red, grn, blu); +} + +void RGBController_AlienwareAW3423DWF::DeviceUpdateMode() +{ +} diff --git a/Controllers/AlienwareMonitorController/RGBController_AlienwareAW3423DWF.h b/Controllers/AlienwareMonitorController/RGBController_AlienwareAW3423DWF.h new file mode 100644 index 0000000..b5f0541 --- /dev/null +++ b/Controllers/AlienwareMonitorController/RGBController_AlienwareAW3423DWF.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_AlienwareAW3423DWF.h | +| | +| RGBController for the Alienware AW3423DWF monitor | +| | +| Ferréol DUBOIS COLI (Fefe_du_973) 23 Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "AlienwareAW3423DWFController.h" +#include "RGBController.h" + +class RGBController_AlienwareAW3423DWF : public RGBController +{ +public: + explicit RGBController_AlienwareAW3423DWF(AlienwareAW3423DWFController* controller_ptr); + ~RGBController_AlienwareAW3423DWF(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AlienwareAW3423DWFController* controller; +}; diff --git a/Controllers/AlienwareMonitorController/RGBController_AlienwareMonitor.cpp b/Controllers/AlienwareMonitorController/RGBController_AlienwareMonitor.cpp new file mode 100644 index 0000000..57a10f0 --- /dev/null +++ b/Controllers/AlienwareMonitorController/RGBController_AlienwareMonitor.cpp @@ -0,0 +1,143 @@ +/*---------------------------------------------------------*\ +| RGBController_AlienwareMonitor.cpp | +| | +| RGBController for Alienware monitors | +| | +| Adam Honse (CalcProgrammer1) 08 May 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AlienwareMonitor.h" + +/**------------------------------------------------------------------*\ + @name Alienware Monitor + @category Accessory + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectAlienwareMonitorControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AlienwareMonitor::RGBController_AlienwareMonitor(AlienwareMonitorController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + description = "Alienware Monitor"; + vendor = "Alienware"; + type = DEVICE_TYPE_MONITOR; + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + active_mode = 0; + + SetupZones(); +} + +RGBController_AlienwareMonitor::~RGBController_AlienwareMonitor() +{ + delete controller; +} + +void RGBController_AlienwareMonitor::SetupZones() +{ + zone Logo; + Logo.name = "Logo"; + Logo.type = ZONE_TYPE_SINGLE; + Logo.leds_min = 1; + Logo.leds_max = 1; + Logo.leds_count = 1; + Logo.matrix_map = NULL; + zones.push_back(Logo); + + led Logo_LED; + Logo_LED.name = "Logo"; + Logo_LED.value = 0x01; + leds.push_back(Logo_LED); + + zone Number; + Number.name = "Number"; + Number.type = ZONE_TYPE_SINGLE; + Number.leds_min = 1; + Number.leds_max = 1; + Number.leds_count = 1; + Number.matrix_map = NULL; + zones.push_back(Number); + + led Number_LED; + Number_LED.name = "Number"; + Number_LED.value = 0x02; + leds.push_back(Number_LED); + + zone PowerButton; + PowerButton.name = "Power Button"; + PowerButton.type = ZONE_TYPE_SINGLE; + PowerButton.leds_min = 1; + PowerButton.leds_max = 1; + PowerButton.leds_count = 1; + PowerButton.matrix_map = NULL; + zones.push_back(PowerButton); + + led PowerButton_LED; + PowerButton_LED.name = "Power Button"; + PowerButton_LED.value = 0x08; + leds.push_back(PowerButton_LED); + + SetupColors(); +} + +void RGBController_AlienwareMonitor::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_AlienwareMonitor::DeviceUpdateLEDs() +{ + /*-----------------------------------------------------*\ + | If all three colors are the same value, speed up the | + | direct mode by using the ALL target (0x0B) instead of | + | setting each LED individually. | + \*-----------------------------------------------------*/ + if((colors[0] == colors[1]) && (colors[1] == colors[2])) + { + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + controller->SendColor(0x0B, red, grn, blu); + } + else + { + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + UpdateSingleLED(led_idx); + } + } +} + +void RGBController_AlienwareMonitor::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AlienwareMonitor::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + controller->SendColor(leds[led].value, red, grn, blu); +} + +void RGBController_AlienwareMonitor::DeviceUpdateMode() +{ + +} diff --git a/Controllers/AlienwareMonitorController/RGBController_AlienwareMonitor.h b/Controllers/AlienwareMonitorController/RGBController_AlienwareMonitor.h new file mode 100644 index 0000000..4021a36 --- /dev/null +++ b/Controllers/AlienwareMonitorController/RGBController_AlienwareMonitor.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_AlienwareMonitor.h | +| | +| RGBController for Alienware monitors | +| | +| Adam Honse (CalcProgrammer1) 08 May 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AlienwareMonitorController.h" + +class RGBController_AlienwareMonitor : public RGBController +{ +public: +RGBController_AlienwareMonitor(AlienwareMonitorController* controller_ptr); + ~RGBController_AlienwareMonitor(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: +AlienwareMonitorController* controller; +}; diff --git a/Controllers/AnnePro2Controller/AnnePro2Controller.cpp b/Controllers/AnnePro2Controller/AnnePro2Controller.cpp new file mode 100644 index 0000000..0f252ea --- /dev/null +++ b/Controllers/AnnePro2Controller/AnnePro2Controller.cpp @@ -0,0 +1,123 @@ +/*---------------------------------------------------------*\ +| AnnePro2Controller.cpp | +| | +| Driver for Obins Lab AnnePro2 keyboard | +| | +| Sergey Gavrilov (DrZlo13) 06 Jun 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "AnnePro2Controller.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +AnnePro2Controller::AnnePro2Controller(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; +} + +AnnePro2Controller::~AnnePro2Controller() +{ + hid_close(dev); +} + +std::string AnnePro2Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AnnePro2Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void AnnePro2Controller::SendDirect(unsigned char frame_count, unsigned char * frame_data) +{ + /*-------------------------------------------------------------*\ + | Reverse engineered by https://github.com/manualmanul/Annemone | + \*-------------------------------------------------------------*/ + + const unsigned char hid_cmd_service_data_length = 4; + const unsigned char hid_cmd_service_data[hid_cmd_service_data_length] = {0, 123, 16, 65}; + + const unsigned char hid_cmd_static_message_length = 3; + const unsigned char hid_cmd_static_message[hid_cmd_static_message_length] = {0, 0, 125}; + + const unsigned char hid_cmd_command_info_length = 3; + const unsigned char hid_cmd_command_info[hid_cmd_command_info_length] = {32, 3, 255}; + + const unsigned char real_command_info_length = hid_cmd_command_info_length + 1; + const unsigned char max_hid_length = 64; + + const unsigned char max_command_length = max_hid_length - hid_cmd_service_data_length - 2 - hid_cmd_static_message_length; + const unsigned char max_message_length = max_command_length - real_command_info_length; + const unsigned char messages_to_send_amount = frame_count / max_message_length + 1; + const unsigned char val_1 = frame_count % max_message_length; + const unsigned char val_2 = (0 == val_1) ? max_message_length : val_1; + + unsigned char hid_command[max_hid_length]; + + unsigned char led_data = 0; + + for(unsigned char p = 0; p < messages_to_send_amount; p++) + { + const unsigned char e = (messages_to_send_amount << 4) + p; + const unsigned char a = ((messages_to_send_amount - 1) == p) ? val_2 + real_command_info_length : max_message_length + real_command_info_length; + + /*---------------------------------------------------------*\ + | Service data | + \*---------------------------------------------------------*/ + hid_command[0] = hid_cmd_service_data[0]; + hid_command[1] = hid_cmd_service_data[1]; + hid_command[2] = hid_cmd_service_data[2]; + hid_command[3] = hid_cmd_service_data[3]; + + hid_command[4] = e; + hid_command[5] = a; + + /*---------------------------------------------------------*\ + | Static message | + \*---------------------------------------------------------*/ + hid_command[6] = hid_cmd_static_message[0]; + hid_command[7] = hid_cmd_static_message[1]; + hid_command[8] = hid_cmd_static_message[2]; + + /*---------------------------------------------------------*\ + | Command info | + \*---------------------------------------------------------*/ + hid_command[9] = hid_cmd_command_info[0]; + hid_command[10] = hid_cmd_command_info[1]; + hid_command[11] = hid_cmd_command_info[2]; + + hid_command[12] = 2; + + /*---------------------------------------------------------*\ + | LED data | + \*---------------------------------------------------------*/ + for(uint8_t i = 0; i < max_message_length; i++) + { + hid_command[13 + i] = frame_data[led_data]; + led_data++; + } + + hid_write(dev, hid_command, max_hid_length); + + /*---------------------------------------------------------*\ + | Needed due to Anne Pro 2 ignoring commands when they're | + | sent faster than 50ms apart from each other. | + \*---------------------------------------------------------*/ + std::this_thread::sleep_for(50ms); + } +} diff --git a/Controllers/AnnePro2Controller/AnnePro2Controller.h b/Controllers/AnnePro2Controller/AnnePro2Controller.h new file mode 100644 index 0000000..0e7d833 --- /dev/null +++ b/Controllers/AnnePro2Controller/AnnePro2Controller.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| AnnePro2Controller.h | +| | +| Driver for Obins Lab AnnePro2 keyboard | +| | +| Sergey Gavrilov (DrZlo13) 06 Jun 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +class AnnePro2Controller +{ +public: + AnnePro2Controller(hid_device* dev_handle, const char* path); + ~AnnePro2Controller(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + + void SendDirect + ( + unsigned char frame_count, + unsigned char * frame_data + ); + +private: + hid_device* dev; + std::string location; +}; diff --git a/Controllers/AnnePro2Controller/AnnePro2ControllerDetect.cpp b/Controllers/AnnePro2Controller/AnnePro2ControllerDetect.cpp new file mode 100644 index 0000000..9507ea8 --- /dev/null +++ b/Controllers/AnnePro2Controller/AnnePro2ControllerDetect.cpp @@ -0,0 +1,57 @@ +/*---------------------------------------------------------*\ +| AnnePro2ControllerDetect.cpp | +| | +| Detector for Obins Lab AnnePro2 keyboard | +| | +| Sergey Gavrilov (DrZlo13) 06 Jun 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AnnePro2Controller.h" +#include "RGBController_AnnePro2.h" +#include + +/*---------------------------------------------------------*\ +| Anne Pro 2 vendor IDs | +\*---------------------------------------------------------*/ +#define ANNE_PRO_2_VID_1 0x04D9 +#define ANNE_PRO_2_VID_2 0x3311 + +/*---------------------------------------------------------*\ +| Anne Pro 2 product IDs | +\*---------------------------------------------------------*/ +#define ANNE_PRO_2_PID_1 0x8008 +#define ANNE_PRO_2_PID_2 0x8009 +#define ANNE_PRO_2_PID_3 0xA292 +#define ANNE_PRO_2_PID_4 0xA293 +#define ANNE_PRO_2_PID_5 0xA297 + +/******************************************************************************************\ +* * +* DetectAnnePro2Controllers * +* * +* Tests the USB address to see if an Obins Lab AnnePro2 keyboard exists there. * +* * +\******************************************************************************************/ + +void DetectAnnePro2Controllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AnnePro2Controller* controller = new AnnePro2Controller(dev, info->path); + RGBController_AnnePro2* rgb_controller = new RGBController_AnnePro2(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_I("Anne Pro 2", DetectAnnePro2Controllers, ANNE_PRO_2_VID_1, ANNE_PRO_2_PID_1, 1); +REGISTER_HID_DETECTOR_I("Anne Pro 2", DetectAnnePro2Controllers, ANNE_PRO_2_VID_1, ANNE_PRO_2_PID_2, 1); +REGISTER_HID_DETECTOR_I("Anne Pro 2", DetectAnnePro2Controllers, ANNE_PRO_2_VID_1, ANNE_PRO_2_PID_3, 1); +REGISTER_HID_DETECTOR_I("Anne Pro 2", DetectAnnePro2Controllers, ANNE_PRO_2_VID_1, ANNE_PRO_2_PID_4, 1); +REGISTER_HID_DETECTOR_I("Anne Pro 2", DetectAnnePro2Controllers, ANNE_PRO_2_VID_2, ANNE_PRO_2_PID_5, 1); diff --git a/Controllers/AnnePro2Controller/RGBController_AnnePro2.cpp b/Controllers/AnnePro2Controller/RGBController_AnnePro2.cpp new file mode 100644 index 0000000..44e1ef2 --- /dev/null +++ b/Controllers/AnnePro2Controller/RGBController_AnnePro2.cpp @@ -0,0 +1,244 @@ +/*---------------------------------------------------------*\ +| RGBController_AnnePro2.cpp | +| | +| RGBController for Obins Lab AnnePro2 keyboard | +| | +| Sergey Gavrilov (DrZlo13) 06 Jun 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_AnnePro2.h" + +#define NA 0xFFFFFFFF +#define LED_REAL_COUNT (5*14) +#define LED_COUNT (LED_REAL_COUNT - 9) + +static unsigned int matrix_map[5][14] = + { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }, + { 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }, + { 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, NA }, + { 41, NA, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, NA }, + { 53, NA, 54, 55, NA, NA, 56, NA, NA, 57, 58, 59, 60, NA } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX +}; + +static const unsigned int zone_sizes[] = +{ + LED_COUNT, +}; + +typedef struct +{ + const char * name; + const unsigned char idx; +} annepro2_led_type; + +static const annepro2_led_type led_names[] = +{ + /* Key Label Index */ + { KEY_EN_ESCAPE, 0 }, + { KEY_EN_1, 1 }, + { KEY_EN_2, 2 }, + { KEY_EN_3, 3 }, + { KEY_EN_4, 4 }, + { KEY_EN_5, 5 }, + { KEY_EN_6, 6 }, + { KEY_EN_7, 7 }, + { KEY_EN_8, 8 }, + { KEY_EN_9, 9 }, + { KEY_EN_0, 10 }, + { KEY_EN_MINUS, 11 }, + { KEY_EN_EQUALS, 12 }, + { KEY_EN_BACKSPACE, 13 }, + { KEY_EN_TAB, 14 }, + { KEY_EN_Q, 15 }, + { KEY_EN_W, 16 }, + { KEY_EN_E, 17 }, + { KEY_EN_R, 18 }, + { KEY_EN_T, 19 }, + { KEY_EN_Y, 20 }, + { KEY_EN_U, 21 }, + { KEY_EN_I, 22 }, + { KEY_EN_O, 23 }, + { KEY_EN_P, 24 }, + { KEY_EN_LEFT_BRACKET, 25 }, + { KEY_EN_RIGHT_BRACKET, 26 }, + { KEY_EN_ANSI_BACK_SLASH, 27 }, + { KEY_EN_CAPS_LOCK, 28 }, + { KEY_EN_A, 29 }, + { KEY_EN_S, 30 }, + { KEY_EN_D, 31 }, + { KEY_EN_F, 32 }, + { KEY_EN_G, 33 }, + { KEY_EN_H, 34 }, + { KEY_EN_J, 35 }, + { KEY_EN_K, 36 }, + { KEY_EN_L, 37 }, + { KEY_EN_SEMICOLON, 38 }, + { KEY_EN_QUOTE, 39 }, + { KEY_EN_ANSI_ENTER, 40 }, + { KEY_EN_LEFT_SHIFT, 41 }, + { KEY_EN_Z, 42 }, + { KEY_EN_X, 43 }, + { KEY_EN_C, 44 }, + { KEY_EN_V, 45 }, + { KEY_EN_B, 46 }, + { KEY_EN_N, 47 }, + { KEY_EN_M, 48 }, + { KEY_EN_COMMA, 49 }, + { KEY_EN_PERIOD, 50 }, + { KEY_EN_FORWARD_SLASH, 51 }, + { KEY_EN_RIGHT_SHIFT, 52 }, + { KEY_EN_LEFT_CONTROL, 53 }, + { KEY_EN_LEFT_WINDOWS, 54 }, + { KEY_EN_LEFT_ALT, 55 }, + { KEY_EN_SPACE, 56 }, + { KEY_EN_RIGHT_ALT, 57 }, + { KEY_EN_RIGHT_FUNCTION, 58 }, + { KEY_EN_MENU, 59 }, + { KEY_EN_RIGHT_CONTROL, 60 }, +}; + +/**------------------------------------------------------------------*\ + @name Anne Pro 2 + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectAnnePro2Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AnnePro2::RGBController_AnnePro2(AnnePro2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = "Anne Pro 2"; + vendor = "Obinslab"; + type = DEVICE_TYPE_KEYBOARD; + description = "Obinslab Anne Pro 2 Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_AnnePro2::~RGBController_AnnePro2() +{ + delete controller; +} + +void RGBController_AnnePro2::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 5; + new_zone.matrix_map->width = 14; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx].name; + new_led.value = led_names[led_idx].idx; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_AnnePro2::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AnnePro2::DeviceUpdateLEDs() +{ + const unsigned char frame_buf_length = LED_REAL_COUNT * 3; + unsigned char frame_buf[frame_buf_length]; + + /*---------------------------------------------------------*\ + | TODO: Send packets with multiple LED frames | + \*---------------------------------------------------------*/ + std::size_t led_real_idx = 0; + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + frame_buf[(led_real_idx * 3) + 0] = RGBGetRValue(colors[led_idx]); + frame_buf[(led_real_idx * 3) + 1] = RGBGetGValue(colors[led_idx]); + frame_buf[(led_real_idx * 3) + 2] = RGBGetBValue(colors[led_idx]); + + if(led_idx == 40 || led_idx == 41 || led_idx == 52 || led_idx == 53 || led_idx == 60) + { + led_real_idx++; + } + else if(led_idx == 55 || led_idx == 56) + { + led_real_idx++; + led_real_idx++; + } + + led_real_idx++; + } + + controller->SendDirect(frame_buf_length, frame_buf); +} + +void RGBController_AnnePro2::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AnnePro2::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AnnePro2::DeviceUpdateMode() +{ + +} diff --git a/Controllers/AnnePro2Controller/RGBController_AnnePro2.h b/Controllers/AnnePro2Controller/RGBController_AnnePro2.h new file mode 100644 index 0000000..350718d --- /dev/null +++ b/Controllers/AnnePro2Controller/RGBController_AnnePro2.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_AnnePro2.h | +| | +| RGBController for Obins Lab AnnePro2 keyboard | +| | +| Sergey Gavrilov (DrZlo13) 06 Jun 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AnnePro2Controller.h" + +class RGBController_AnnePro2 : public RGBController +{ +public: + RGBController_AnnePro2(AnnePro2Controller* controller_ptr); + ~RGBController_AnnePro2(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AnnePro2Controller* controller; +}; diff --git a/Controllers/ArcticController/ArcticController.cpp b/Controllers/ArcticController/ArcticController.cpp new file mode 100644 index 0000000..89ac69e --- /dev/null +++ b/Controllers/ArcticController/ArcticController.cpp @@ -0,0 +1,158 @@ +/*---------------------------------------------------------*\ +| ArcticController.cpp | +| | +| Driver for Arctic devices | +| | +| Armin Wolf (Wer-Wolf) 09 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "ArcticController.h" + +using namespace std::chrono_literals; + +#define ARCTIC_COMMAND_SET_RGB 0x00 +#define ARCTIC_COMMAND_IDENTIFY 0x5C + +#define ARCTIC_RESPONSE_BUFFER_LENGTH 15 +#define ARCTIC_RESPONSE_COMMAND_OFFSET 0 +#define ARCTIC_RESPONSE_DATA_OFFSET 1 +#define ARCTIC_RESPONSE_DATA_LENGTH 12 +#define ARCTIC_RESPONSE_XOR_CSUM_OFFSET 13 +#define ARCTIC_RESPONSE_ADD_CSUM_OFFSET 14 + +#define ARCTIC_COMMAND_BUFFER_LENGTH(payload_size) (sizeof(header) + 1 + payload_size) +#define ARCTIC_COMMAND_COMMAND_OFFSET (sizeof(header)) +#define ARCTIC_COMMAND_PAYLOAD_OFFSET (sizeof(header) + 1) + +const unsigned char header[] = +{ + 0x01, + 0x02, + 0x03, + 0xFF, + 0x05, + 0xFF, + 0x02, + 0x03 +}; + +const unsigned char identify_payload[] = +{ + 0x01, + 0xFE, + 0x01, + 0xFE +}; + +ArcticController::ArcticController(const std::string &portname) +: serialport(portname.c_str(), 250000, SERIAL_PORT_PARITY_NONE, SERIAL_PORT_SIZE_8, SERIAL_PORT_STOP_BITS_2, false) +{ + port_name = portname; + serialport.serial_set_dtr(true); +} + +ArcticController::~ArcticController() +{ + serialport.serial_set_dtr(false); +} + +static void FormatCommandBuffer(char *buffer, char command) +{ + std::memcpy(buffer, header, sizeof(header)); + buffer[ARCTIC_COMMAND_COMMAND_OFFSET] = command; +} + +void ArcticController::SetChannels(std::vector colors) +{ + char* buffer = new char[ARCTIC_COMMAND_BUFFER_LENGTH(colors.size() * 3)]; + + FormatCommandBuffer(buffer, ARCTIC_COMMAND_SET_RGB); + + for(unsigned int channel = 0; channel < colors.size(); channel++) + { + const unsigned int offset = ARCTIC_COMMAND_PAYLOAD_OFFSET + channel * 3; + + buffer[offset + 0x00] = (char)std::min(254, RGBGetRValue(colors[channel])); + buffer[offset + 0x01] = (char)std::min(254, RGBGetGValue(colors[channel])); + buffer[offset + 0x02] = (char)std::min(254, RGBGetBValue(colors[channel])); + } + + serialport.serial_write(buffer, sizeof(buffer)); + + delete[] buffer; +} + +static char XORChecksum(char *data, int length) +{ + char sum = 0; + + for(int i = 0; i < length; i++) + { + sum ^= data[i]; + } + + return sum; +} + +static char AddChecksum(char *data, int length) +{ + char sum = 0; + + for(int i = 0; i < length; i++) + { + sum = (char)(sum + data[i]); + } + + return sum; +} + +bool ArcticController::IsPresent() +{ + char buffer[ARCTIC_COMMAND_BUFFER_LENGTH(sizeof(identify_payload))]; + char response[ARCTIC_RESPONSE_BUFFER_LENGTH]; + int ret; + + FormatCommandBuffer(buffer, ARCTIC_COMMAND_IDENTIFY); + std::memcpy(buffer + ARCTIC_COMMAND_PAYLOAD_OFFSET, identify_payload, sizeof(identify_payload)); + + serialport.serial_flush_rx(); + ret = serialport.serial_write(buffer, sizeof(buffer)); + if(ret != sizeof(buffer)) + { + return false; + } + + std::this_thread::sleep_for(100ms); + + ret = serialport.serial_read(response, sizeof(response)); + if(ret != sizeof(response)) + { + return false; + } + + if(response[ARCTIC_RESPONSE_COMMAND_OFFSET] != ARCTIC_COMMAND_IDENTIFY) + { + return false; + } + + if(response[ARCTIC_RESPONSE_XOR_CSUM_OFFSET] != XORChecksum(&response[ARCTIC_RESPONSE_DATA_OFFSET], ARCTIC_RESPONSE_DATA_LENGTH)) + { + return false; + } + + if(response[ARCTIC_RESPONSE_ADD_CSUM_OFFSET] != AddChecksum(&response[ARCTIC_RESPONSE_DATA_OFFSET], ARCTIC_RESPONSE_DATA_LENGTH)) + { + return false; + } + + return true; +} + +std::string ArcticController::GetLocation() +{ + return port_name; +} diff --git a/Controllers/ArcticController/ArcticController.h b/Controllers/ArcticController/ArcticController.h new file mode 100644 index 0000000..83e1254 --- /dev/null +++ b/Controllers/ArcticController/ArcticController.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| ArcticController.h | +| | +| Driver for Arctic devices | +| | +| Armin Wolf (Wer-Wolf) 09 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "serial_port.h" + +class ArcticController +{ +public: + ArcticController(const std::string &portname); + ~ArcticController(); + + void SetChannels(std::vector colors); + bool IsPresent(); + + std::string GetLocation(); + +private: + std::string port_name; + serial_port serialport; +}; diff --git a/Controllers/ArcticController/ArcticControllerDetect.cpp b/Controllers/ArcticController/ArcticControllerDetect.cpp new file mode 100644 index 0000000..6816b40 --- /dev/null +++ b/Controllers/ArcticController/ArcticControllerDetect.cpp @@ -0,0 +1,48 @@ +/*---------------------------------------------------------*\ +| ArcticControllerDetect.cpp | +| | +| Detector for Arctic devices | +| | +| Armin Wolf (Wer-Wolf) 09 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "ArcticController.h" +#include "RGBController_Arctic.h" +#include "find_usb_serial_port.h" + +#define CH341_VID 0x1A86 +#define CH341_PID 0x7523 + +void DetectArcticControllers() +{ + std::vector ports = find_usb_serial_port(CH341_VID, CH341_PID); + + for(unsigned int device = 0; device < ports.size(); device++) + { + ArcticController *controller = new ArcticController(*ports[device]); + + if(controller->IsPresent()) + { + RGBController_Arctic *rgb_controller = new RGBController_Arctic(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + delete controller; + } + + delete ports[device]; + } +} + +REGISTER_DETECTOR("Arctic RGB controller", DetectArcticControllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("Arctic RGB controller", DetectArcticControllers, 0x1A86, 0x7523 ) | +\*---------------------------------------------------------------------------------------------------------*/ diff --git a/Controllers/ArcticController/RGBController_Arctic.cpp b/Controllers/ArcticController/RGBController_Arctic.cpp new file mode 100644 index 0000000..2cdaab7 --- /dev/null +++ b/Controllers/ArcticController/RGBController_Arctic.cpp @@ -0,0 +1,134 @@ +/*---------------------------------------------------------*\ +| RGBController_Arctic.cpp | +| | +| RGBController for Arctic devices | +| | +| Armin Wolf (Wer-Wolf) 09 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_Arctic.h" + +using namespace std::chrono_literals; + +#define ARCTIC_NUM_CHANNELS 4 +#define ARCTIC_SLEEP_THRESHOLD 100ms +#define ARCTIC_KEEPALIVE_PERIOD 500ms /* Device requires at least 1s */ + +/**------------------------------------------------------------------*\ + @name Arctic RGB Controller Devices + @category LEDStrip + @type Serial + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectArcticControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Arctic::RGBController_Arctic(ArcticController* controller_ptr) +{ + controller = controller_ptr; + + name = "Arctic RGB Controller"; + vendor = "Arctic"; + description = "Arctic 4-Channel RGB Controller"; + location = controller->GetLocation(); + type = DEVICE_TYPE_LEDSTRIP; + + mode DirectMode; + DirectMode.name = "Direct"; + DirectMode.value = 0; + DirectMode.flags = MODE_FLAG_HAS_PER_LED_COLOR; + DirectMode.color_mode = MODE_COLORS_PER_LED; + + modes.push_back(DirectMode); + + SetupZones(); + + keepalive_thread_run = true; + keepalive_thread = std::thread(&RGBController_Arctic::KeepaliveThreadFunction, this); +} + +RGBController_Arctic::~RGBController_Arctic() +{ + keepalive_thread_run = false; + keepalive_thread.join(); + delete controller; +} + +void RGBController_Arctic::SetupZones() +{ + for(int channel = 0; channel < ARCTIC_NUM_CHANNELS; channel++) + { + zone LedZone; + LedZone.name = "LED Strip " + std::to_string(channel); + LedZone.type = ZONE_TYPE_SINGLE; + LedZone.leds_count = 1; + LedZone.leds_min = 1; + LedZone.leds_max = 1; + LedZone.matrix_map = nullptr; + + led Led; + Led.name = LedZone.name + " LED"; + Led.value = channel; + + zones.push_back(LedZone); + leds.push_back(Led); + } + + SetupColors(); +} + +void RGBController_Arctic::ResizeZone(int /* zone */, int /* new_size */) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Arctic::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + controller->SetChannels(colors); +} + +void RGBController_Arctic::UpdateZoneLEDs(int /* zone */) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Arctic::UpdateSingleLED(int /* led */) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Arctic::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | This device does not support mode updates | + \*---------------------------------------------------------*/ +} + +void RGBController_Arctic::KeepaliveThreadFunction() +{ + std::chrono::nanoseconds sleep_time; + + while(keepalive_thread_run.load()) + { + sleep_time = ARCTIC_KEEPALIVE_PERIOD - (std::chrono::steady_clock::now() - last_update_time); + if(sleep_time <= ARCTIC_SLEEP_THRESHOLD) + { + UpdateLEDs(); // Already protected thru a device update thread + std::this_thread::sleep_for(ARCTIC_KEEPALIVE_PERIOD); + } + else + { + std::this_thread::sleep_for(sleep_time); + } + } +} diff --git a/Controllers/ArcticController/RGBController_Arctic.h b/Controllers/ArcticController/RGBController_Arctic.h new file mode 100644 index 0000000..1a0a8bc --- /dev/null +++ b/Controllers/ArcticController/RGBController_Arctic.h @@ -0,0 +1,44 @@ +/*---------------------------------------------------------*\ +| RGBController_Arctic.h | +| | +| RGBController for Arctic devices | +| | +| Armin Wolf (Wer-Wolf) 09 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "ArcticController.h" +#include "RGBController.h" +#include "serial_port.h" + +class RGBController_Arctic : public RGBController +{ +public: + RGBController_Arctic(ArcticController* controller_ptr); + ~RGBController_Arctic(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThreadFunction(); + +private: + ArcticController* controller; + std::chrono::time_point last_update_time; + std::atomic keepalive_thread_run; + std::thread keepalive_thread; +}; + diff --git a/Controllers/AresonController/AresonController.cpp b/Controllers/AresonController/AresonController.cpp new file mode 100644 index 0000000..ae7f2da --- /dev/null +++ b/Controllers/AresonController/AresonController.cpp @@ -0,0 +1,140 @@ +/*---------------------------------------------------------*\ +| AresonController.cpp | +| | +| Driver for Areson mice | +| | +| Morgan Guimard (morg) 29 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "AresonController.h" + +AresonController::AresonController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +AresonController::~AresonController() +{ + hid_close(dev); +} + +std::string AresonController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AresonController::GetNameString() +{ + return(name); +} + +std::string AresonController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned char AresonController::GetSpeedValue(unsigned char speed, unsigned char mode_value) +{ + unsigned char speed_values_lookup_high[ARESON_SPEED_MAX] = + { + 0xFF, 0xE6, 0xD2, 0xBe, 0xAA, 0x96, 0x82, 0x6E, 0x46, 0x28 + }; + + unsigned char speed_values_lookup_low[ARESON_SPEED_MAX] = + { + 0x2D, 0x28, 0x23, 0x1E, 0x19, 0x13, 0x0F, 0x0A, 0x05, 0x03 + }; + + switch (mode_value) + { + case BREATHING_MODE_VALUE: + case SPECRTUM_CYCLE_MODE_VALUE: + case SINGLE_COLOR_WAVE_MODE_VALUE: + case BREATHING_COLORFUL_MODE_VALUE: + return speed_values_lookup_high[speed -1]; + + case RAINBOW_WAVE_MODE_VALUE: + return speed_values_lookup_low[speed -1]; + + default: + return 0; + } +} + +void AresonController::SetMode(RGBColor color, unsigned char brightness, unsigned char speed, unsigned char mode_value) +{ + /*---------------------------------------------------------*\ + | Init the packet buffer | + \*---------------------------------------------------------*/ + unsigned char usb_buf[ARESON_PACKET_SIZE]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*---------------------------------------------------------*\œ + | Constant data | + \*---------------------------------------------------------*/ + usb_buf[0x00] = ARESON_REPORT_ID; + usb_buf[0x01] = 0x07; + usb_buf[0x04] = 0xA0; + usb_buf[0x05] = 0x07; + + /*---------------------------------------------------------*\ + | Set the mode | + \*---------------------------------------------------------*/ + usb_buf[0x06] = mode_value; + + /*---------------------------------------------------------*\ + | Set the color | + \*---------------------------------------------------------*/ + usb_buf[0x07] = RGBGetRValue(color); + usb_buf[0x08] = RGBGetGValue(color); + usb_buf[0x09] = RGBGetBValue(color); + + /*---------------------------------------------------------*\ + | Set speed if needed | + \*---------------------------------------------------------*/ + usb_buf[0x0A] = GetSpeedValue(speed, mode_value); + + /*---------------------------------------------------------*\ + | Set brightness if needed | + \*---------------------------------------------------------*/ + if(mode_value != OFF_MODE_VALUE) + { + unsigned char brightness_values[ARESON_BRIGHTNESS_MAX] = + { + 0x19, 0x32, 0x4B, 0x64, 0x7D, 0x96, 0xAF, 0xC8, 0xE1, 0xFF + }; + + usb_buf[0x0B] = brightness_values[brightness - 1]; + } + + /*---------------------------------------------------------*\ + | Custom CRC - thanks to Vaker for this <3 | + \*---------------------------------------------------------*/ + usb_buf[0x0C] = 0x55 - usb_buf[0x06] - usb_buf[0x07] - usb_buf[0x08] - usb_buf[0x09] - usb_buf[0x0A] - usb_buf[0x0B]; + + /*---------------------------------------------------------*\ + | Constant data | + \*---------------------------------------------------------*/ + usb_buf[0x10] = ARESON_PACKET_END; + + /*---------------------------------------------------------*\ + | Send the report | + \*---------------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, ARESON_PACKET_SIZE); +} diff --git a/Controllers/AresonController/AresonController.h b/Controllers/AresonController/AresonController.h new file mode 100644 index 0000000..35abf6c --- /dev/null +++ b/Controllers/AresonController/AresonController.h @@ -0,0 +1,61 @@ +/*---------------------------------------------------------*\ +| AresonController.h | +| | +| Driver for Areson mice | +| | +| Morgan Guimard (morg) 29 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define ARESON_PACKET_SIZE 17 +#define ARESON_REPORT_ID 0x08 +#define ARESON_PACKET_END 0x4A + +enum +{ + RAINBOW_WAVE_MODE_VALUE = 0x00, + BREATHING_MODE_VALUE = 0x01, + STATIC_MODE_VALUE = 0x02, + SPECRTUM_CYCLE_MODE_VALUE = 0x03, + OFF_MODE_VALUE = 0x04, + SINGLE_COLOR_WAVE_MODE_VALUE = 0x05, + BREATHING_COLORFUL_MODE_VALUE = 0x07, +}; + + +enum +{ + ARESON_BRIGHTNESS_MIN = 1, + ARESON_BRIGHTNESS_MAX = 10, + ARESON_SPEED_MIN = 1, + ARESON_SPEED_MAX = 10 +}; + +class AresonController +{ +public: + AresonController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~AresonController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetMode(RGBColor color, unsigned char brightness, unsigned char speed, unsigned char mode_value); + +protected: + hid_device* dev; + +private: + std::string location; + std::string name; + unsigned char GetSpeedValue(unsigned char speed, unsigned char mode_value); +}; diff --git a/Controllers/AresonController/AresonControllerDetect.cpp b/Controllers/AresonController/AresonControllerDetect.cpp new file mode 100644 index 0000000..f510f7e --- /dev/null +++ b/Controllers/AresonController/AresonControllerDetect.cpp @@ -0,0 +1,49 @@ +/*---------------------------------------------------------*\ +| AresonControllerDetect.cpp | +| | +| Detector for Areson mice | +| | +| Morgan Guimard (morg) 29 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AresonController.h" +#include "RGBController_Areson.h" + +/*---------------------------------------------------------*\ +| Areson vendor ID | +\*---------------------------------------------------------*/ +#define ARESON_VID 0x25A7 + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define ZET_GAMING_EDGE_AIR_PRO_WIRELESS_PID 0xFA3F +#define ZET_GAMING_EDGE_AIR_PRO_PID 0xFA40 +#define ZET_GAMING_EDGE_AIR_ELIT_WIRELESS_PID 0xFA48 +#define ZET_GAMING_EDGE_AIR_ELIT_PID 0xFA49 +#define REDRAGON_M914_PID 0xFA7B +#define REDRAGON_M914_WIRELESS_PID 0xFA7C + +void DetectAresonControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AresonController* controller = new AresonController(dev, *info, name); + RGBController_Areson* rgb_controller = new RGBController_Areson(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("ZET GAMING Edge Air Pro (Wireless)", DetectAresonControllers, ARESON_VID, ZET_GAMING_EDGE_AIR_PRO_WIRELESS_PID, 1, 0xFF02, 2); +REGISTER_HID_DETECTOR_IPU("ZET GAMING Edge Air Pro", DetectAresonControllers, ARESON_VID, ZET_GAMING_EDGE_AIR_PRO_PID, 1, 0xFF02, 2); +REGISTER_HID_DETECTOR_IPU("ZET GAMING Edge Air Elit (Wireless)", DetectAresonControllers, ARESON_VID, ZET_GAMING_EDGE_AIR_ELIT_WIRELESS_PID, 1, 0xFF02, 2); +REGISTER_HID_DETECTOR_IPU("ZET GAMING Edge Air Elit", DetectAresonControllers, ARESON_VID, ZET_GAMING_EDGE_AIR_ELIT_PID, 1, 0xFF02, 2); +REGISTER_HID_DETECTOR_IPU("Redragon M914 NIX (Wireless)", DetectAresonControllers, ARESON_VID, REDRAGON_M914_WIRELESS_PID, 1, 0xFF02, 2); +REGISTER_HID_DETECTOR_IPU("Redragon M914 NIX", DetectAresonControllers, ARESON_VID, REDRAGON_M914_PID, 1, 0xFF02, 2); diff --git a/Controllers/AresonController/RGBController_Areson.cpp b/Controllers/AresonController/RGBController_Areson.cpp new file mode 100644 index 0000000..fd0fcc2 --- /dev/null +++ b/Controllers/AresonController/RGBController_Areson.cpp @@ -0,0 +1,189 @@ +/*---------------------------------------------------------*\ +| RGBController_Areson.cpp | +| | +| RGBController for Areson mice | +| | +| Morgan Guimard (morg) 29 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_Areson.h" + +/**------------------------------------------------------------------*\ + @name Areson + @category Mouse + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectAresonControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Areson::RGBController_Areson(AresonController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Areson"; + type = DEVICE_TYPE_MOUSE; + description = "Areson mouse"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.value = STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = ARESON_BRIGHTNESS_MIN; + Static.brightness_max = ARESON_BRIGHTNESS_MAX; + Static.brightness = ARESON_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = RAINBOW_WAVE_MODE_VALUE; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.brightness_min = ARESON_BRIGHTNESS_MIN; + RainbowWave.brightness_max = ARESON_BRIGHTNESS_MAX; + RainbowWave.brightness = ARESON_BRIGHTNESS_MAX; + RainbowWave.speed_min = ARESON_SPEED_MIN; + RainbowWave.speed_max = ARESON_SPEED_MAX; + RainbowWave.speed = ARESON_SPEED_MAX; + modes.push_back(RainbowWave); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = ARESON_BRIGHTNESS_MIN; + Breathing.brightness_max = ARESON_BRIGHTNESS_MAX; + Breathing.brightness = ARESON_BRIGHTNESS_MAX; + Breathing.speed_min = ARESON_SPEED_MIN; + Breathing.speed_max = ARESON_SPEED_MAX; + Breathing.speed = ARESON_SPEED_MIN; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = SPECRTUM_CYCLE_MODE_VALUE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = ARESON_BRIGHTNESS_MIN; + SpectrumCycle.brightness_max = ARESON_BRIGHTNESS_MAX; + SpectrumCycle.brightness = ARESON_BRIGHTNESS_MAX; + SpectrumCycle.speed_min = ARESON_SPEED_MIN; + SpectrumCycle.speed_max = ARESON_SPEED_MAX; + SpectrumCycle.speed = ARESON_SPEED_MAX; + modes.push_back(SpectrumCycle); + + mode SingleColorWave; + SingleColorWave.name = "Single Color Wave"; + SingleColorWave.value = SINGLE_COLOR_WAVE_MODE_VALUE; + SingleColorWave.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SingleColorWave.color_mode = MODE_COLORS_PER_LED; + SingleColorWave.brightness_min = ARESON_BRIGHTNESS_MIN; + SingleColorWave.brightness_max = ARESON_BRIGHTNESS_MAX; + SingleColorWave.brightness = ARESON_BRIGHTNESS_MAX; + SingleColorWave.speed_min = ARESON_SPEED_MIN; + SingleColorWave.speed_max = ARESON_SPEED_MAX; + SingleColorWave.speed = ARESON_SPEED_MAX; + SingleColorWave.colors.resize(1); + modes.push_back(SingleColorWave); + + mode ColorfulBreathing; + ColorfulBreathing.name = "Colorful Breathing"; + ColorfulBreathing.value = BREATHING_COLORFUL_MODE_VALUE; + ColorfulBreathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorfulBreathing.color_mode = MODE_COLORS_NONE; + ColorfulBreathing.brightness_min = ARESON_BRIGHTNESS_MIN; + ColorfulBreathing.brightness_max = ARESON_BRIGHTNESS_MAX; + ColorfulBreathing.brightness = ARESON_BRIGHTNESS_MAX; + ColorfulBreathing.speed_min = ARESON_SPEED_MIN; + ColorfulBreathing.speed_max = ARESON_SPEED_MAX; + ColorfulBreathing.speed = ARESON_SPEED_MAX; + ColorfulBreathing.colors.resize(1); + modes.push_back(ColorfulBreathing); + + mode OFF; + OFF.name = "Off"; + OFF.value = OFF_MODE_VALUE; + OFF.flags = MODE_FLAG_AUTOMATIC_SAVE; + OFF.color_mode = MODE_COLORS_NONE; + modes.push_back(OFF); + + SetupZones(); +} + +RGBController_Areson::~RGBController_Areson() +{ + delete controller; +} + +void RGBController_Areson::SetupZones() +{ + zone new_zone; + + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(1); + + led new_led; + new_led.name = "LED 1"; + leds[0] = new_led; + + SetupColors(); +} + +void RGBController_Areson::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Areson::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_Areson::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_Areson::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_Areson::DeviceUpdateMode() +{ + RGBColor color; + + if(modes[active_mode].flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + color = colors[0]; + } + else + { + color = ToRGBColor(0,0,0); + } + + controller->SetMode(color, modes[active_mode].brightness, modes[active_mode].speed, modes[active_mode].value); +} diff --git a/Controllers/AresonController/RGBController_Areson.h b/Controllers/AresonController/RGBController_Areson.h new file mode 100644 index 0000000..4d1d835 --- /dev/null +++ b/Controllers/AresonController/RGBController_Areson.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_Areson.h | +| | +| RGBController for Areson mice | +| | +| Morgan Guimard (morg) 29 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AresonController.h" + +class RGBController_Areson : public RGBController +{ +public: + RGBController_Areson(AresonController* controller_ptr); + ~RGBController_Areson(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AresonController* controller; +}; diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreController/AsusAuraCoreController.cpp b/Controllers/AsusAuraCoreController/AsusAuraCoreController/AsusAuraCoreController.cpp new file mode 100644 index 0000000..fc3e9ae --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreController/AsusAuraCoreController.cpp @@ -0,0 +1,410 @@ +/*---------------------------------------------------------*\ +| AsusAuraCoreController.cpp | +| | +| Driver for ASUS ROG Aura Core | +| | +| Adam Honse (CalcProgrammer1) 13 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusAuraCoreController.h" +#include "StringUtils.h" + +#define AURA_CORE_MAX_MESSAGE_SIZE 64 + +AuraCoreController::AuraCoreController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + aura_device.aura_type = AURA_CORE_DEVICE_UNKNOWN; + aura_device.buff_size = 0; + aura_device.report_id = 0x5D; + aura_device.num_leds = 4; + aura_device.supports_direct = false; + + IdentifyDevice(); + Handshake(); +} + +AuraCoreController::~AuraCoreController() +{ + hid_close(dev); +} + +std::string AuraCoreController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AuraCoreController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void AuraCoreController::SendBrightness + ( + unsigned char brightness + ) +{ + unsigned char usb_buf[AURA_CORE_MAX_MESSAGE_SIZE]; + + if(aura_device.aura_type != AURA_CORE_DEVICE_UNKNOWN) + { + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x5A; + usb_buf[0x01] = AURA_CORE_COMMAND_BRIGHTNESS; + usb_buf[0x02] = 0xC5; + usb_buf[0x03] = 0xC4; + usb_buf[0x04] = brightness; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, aura_device.buff_size); + } +} + +void AuraCoreController::SendUpdate + ( + unsigned char zone, + unsigned char mode, + unsigned char speed, + unsigned char dir, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[AURA_CORE_MAX_MESSAGE_SIZE]; + + if(aura_device.aura_type != AURA_CORE_DEVICE_UNKNOWN) + { + if(aura_device.aura_type == AURA_CORE_DEVICE_KEYBOARD) + { + zone += 1; + } + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = aura_device.report_id; + usb_buf[0x01] = AURA_CORE_COMMAND_UPDATE; + usb_buf[0x02] = zone; + usb_buf[0x03] = mode; + usb_buf[0x04] = red; + usb_buf[0x05] = green; + usb_buf[0x06] = blue; + usb_buf[0x07] = speed; + usb_buf[0x08] = dir; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, aura_device.buff_size); + } +} + +void AuraCoreController::SendSet() +{ + unsigned char usb_buf[AURA_CORE_MAX_MESSAGE_SIZE]; + + if(aura_device.aura_type != AURA_CORE_DEVICE_UNKNOWN) + { + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = aura_device.report_id; + usb_buf[0x01] = AURA_CORE_COMMAND_SET; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, aura_device.buff_size); + } +} + +void AuraCoreController::SendApply() +{ + unsigned char usb_buf[AURA_CORE_MAX_MESSAGE_SIZE]; + + if(aura_device.aura_type != AURA_CORE_DEVICE_UNKNOWN) + { + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = aura_device.report_id; + usb_buf[0x01] = AURA_CORE_COMMAND_APPLY; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, aura_device.buff_size); + } +} + +void AuraCoreController::InitDirectMode() +{ + unsigned char usb_buf[AURA_CORE_MAX_MESSAGE_SIZE]; + unsigned char msg_num = 0; + int led_count = aura_device.num_leds; + + if(aura_device.supports_direct) + { + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = aura_device.report_id; + usb_buf[0x01] = AURA_CORE_COMMAND_DIRECT; + usb_buf[0x02] = 0xD0; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, aura_device.buff_size); + + while(led_count > 0) + { + /*-----------------------------------------------------*\ + | Set up second message packet | + \*-----------------------------------------------------*/ + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x02; + usb_buf[0x05] = 0x00; + usb_buf[0x06] = msg_num++; + usb_buf[0x07] = (led_count > aura_device.max_leds_per_message) ? aura_device.max_leds_per_message : led_count; + usb_buf[0x08] = 0x00; + + /*-----------------------------------------------------*\ + | Send packet 2 | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, aura_device.buff_size); + + led_count -= aura_device.max_leds_per_message; + } + } +} + +void AuraCoreController::UpdateDirect(std::vector& color_set) +{ + unsigned char usb_buf[AURA_CORE_MAX_MESSAGE_SIZE]; + unsigned char msg_num = 0; + unsigned char color_index = 0; + unsigned char set_count = 0; + int led_count = aura_device.num_leds; + + if(aura_device.supports_direct) + { + while(led_count > 0) + { + unsigned char msg_index = 0x09; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = aura_device.report_id; + usb_buf[0x01] = AURA_CORE_COMMAND_DIRECT; + usb_buf[0x02] = 0xD0; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x02; + usb_buf[0x05] = 0x00; + usb_buf[0x06] = msg_num++; + usb_buf[0x07] = (led_count > aura_device.max_leds_per_message) ? aura_device.max_leds_per_message : led_count; + usb_buf[0x08] = 0x00; + + set_count = 0; + + while( (msg_index < sizeof(usb_buf) ) && + (led_count > 0 ) && + (set_count < aura_device.max_leds_per_message) ) + { + if(color_index < color_set.size()) + { + usb_buf[msg_index++] = color_set[color_index].red; + usb_buf[msg_index++] = color_set[color_index].green; + usb_buf[msg_index++] = color_set[color_index].blue; + } + else + { + usb_buf[msg_index++] = 0; + usb_buf[msg_index++] = 0; + usb_buf[msg_index++] = 0; + } + + led_count--; + color_index++; + set_count++; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, aura_device.buff_size); + } + } +} + +void AuraCoreController::IdentifyDevice() +{ + unsigned char usb_buf[AURA_CORE_MAX_MESSAGE_SIZE]; + int num_bytes = 0; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | First, attempt to read the report from the Keyboard | + \*-----------------------------------------------------*/ + usb_buf[0x00] = aura_device.report_id; + + num_bytes = hid_get_feature_report(dev, usb_buf, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Currently, there is no need to attempt to read the | + | returned data. If the device responded,to the report,| + | we're good. | + \*-----------------------------------------------------*/ + if(num_bytes > 0) + { + aura_device.aura_type = AURA_CORE_DEVICE_KEYBOARD; + aura_device.buff_size = 17; + aura_device.report_id = 0x5D; + aura_device.num_leds = 4; + aura_device.supports_direct = false; + } + else if(num_bytes == -1) + { + /*-------------------------------------------------*\ + | First, attempt to read the report from the | + | Keyboard | + \*-------------------------------------------------*/ + usb_buf[0] = 0x5E; + num_bytes = hid_get_feature_report(dev, usb_buf, sizeof(usb_buf)); + + if(num_bytes > 0) + { + /*---------------------------------------------*\ + | Currently, there is no need to attempt to | + | read the returned data. If the device | + | responded,to the report, we're good. | + \*---------------------------------------------*/ + aura_device.aura_type = AURA_CORE_DEVICE_GA15DH; + aura_device.buff_size = 64; + aura_device.report_id = 0x5E; + aura_device.num_leds = 20; + aura_device.max_leds_per_message = 16; + aura_device.supports_direct = true; + } + } +} + +void AuraCoreController::Handshake() +{ + unsigned char usb_buf[AURA_CORE_MAX_MESSAGE_SIZE]; + + + if(aura_device.aura_type == AURA_CORE_DEVICE_GA15DH) + { + usb_buf[0] = 0x5E; + SendIdString(); + hid_get_feature_report(dev, usb_buf, sizeof(usb_buf)); + SendQuery(); + hid_get_feature_report(dev, usb_buf, sizeof(usb_buf)); + } +} + +void AuraCoreController::SendIdString() +{ + unsigned char usb_buf[AURA_CORE_MAX_MESSAGE_SIZE]; + const char id[] = "ASUS Tech.Inc."; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = aura_device.report_id; + + /*-----------------------------------------------------*\ + | Copy in string data | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x01], id, sizeof(id)); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, aura_device.buff_size); +} + +void AuraCoreController::SendQuery() +{ + unsigned char usb_buf[AURA_CORE_MAX_MESSAGE_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = aura_device.report_id; + usb_buf[0x01] = 0x05; + usb_buf[0x02] = 0x20; + usb_buf[0x03] = 0x31; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = 0x10; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, aura_device.buff_size); +} diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreController/AsusAuraCoreController.h b/Controllers/AsusAuraCoreController/AsusAuraCoreController/AsusAuraCoreController.h new file mode 100644 index 0000000..0f5742f --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreController/AsusAuraCoreController.h @@ -0,0 +1,123 @@ +/*---------------------------------------------------------*\ +| AsusAuraCoreController.h | +| | +| Driver for ASUS ROG Aura Core | +| | +| Adam Honse (CalcProgrammer1) 13 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum AuraCoreDeviceType +{ + AURA_CORE_DEVICE_UNKNOWN = 0, + AURA_CORE_DEVICE_KEYBOARD = 1, + AURA_CORE_DEVICE_GA15DH = 2 +}; + +enum +{ + AURA_CORE_MODE_STATIC = 0, /* Static color mode */ + AURA_CORE_MODE_BREATHING = 1, /* Breathing effect mode */ + AURA_CORE_MODE_SPECTRUM_CYCLE = 2, /* Spectrum Cycle mode */ + AURA_CORE_MODE_RAINBOW = 3, /* Rainbow mode */ + AURA_CORE_MODE_STROBE = 10, /* Strobe mode */ + AURA_CORE_MODE_COMET = 11, /* Comet mode */ + AURA_CORE_MODE_FLASHNDASH = 12, /* Flash & Dash mode */ + AURA_CORE_MODE_IRRADIATION = 17, /* Irradiation mode */ + AURA_CORE_MODE_DIRECT = 255 /* Not a real mode - but need a way to differentiate */ +}; + +enum +{ + AURA_CORE_COMMAND_UPDATE = 0xB3, /* Update mode and color */ + AURA_CORE_COMMAND_SET = 0xB5, /* Set command */ + AURA_CORE_COMMAND_APPLY = 0xB4, /* Apply command */ + AURA_CORE_COMMAND_BRIGHTNESS = 0xBA, /* Brightness command */ + AURA_CORE_COMMAND_DIRECT = 0xBC /* Set LEDs directly */ +}; + +enum +{ + AURA_CORE_ZONE_IDX_ALL = 0x00, /* Update all zones */ + AURA_CORE_ZONE_IDX_1 = 0x01, /* Update zone 1 */ + AURA_CORE_ZONE_IDX_2 = 0x02, /* Update zone 2 */ + AURA_CORE_ZONE_IDX_3 = 0x03, /* Update zone 3 */ + AURA_CORE_ZONE_IDX_4 = 0x04, /* Update zone 4 */ +}; + +enum +{ + AURA_CORE_SPEED_SLOW = 0xE1, /* Slowest speed */ + AURA_CORE_SPEED_NORMAL = 0xEB, /* Normal speed */ + AURA_CORE_SPEED_FAST = 0xF5, /* Fastest speed */ +}; + +struct AuraDeviceDescriptor +{ + AuraCoreDeviceType aura_type; + unsigned char buff_size; + unsigned char report_id; + unsigned char num_leds; + unsigned char max_leds_per_message; + bool supports_direct; +}; + +struct AuraColor +{ + unsigned char red; + unsigned char green; + unsigned char blue; +}; + +class AuraCoreController +{ +public: + AuraDeviceDescriptor aura_device; + + AuraCoreController(hid_device* dev_handle, const char* path); + ~AuraCoreController(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + + void SendBrightness + ( + unsigned char brightness + ); + + void SendUpdate + ( + unsigned char zone, + unsigned char mode, + unsigned char speed, + unsigned char dir, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SendSet(); + + void SendApply(); + + void InitDirectMode(); + + void UpdateDirect(std::vector& color_set); + +private: + hid_device* dev; + std::string location; + + void IdentifyDevice(); + void Handshake(); + void SendIdString(); + void SendQuery(); +}; diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreController/RGBController_AsusAuraCore.cpp b/Controllers/AsusAuraCoreController/AsusAuraCoreController/RGBController_AsusAuraCore.cpp new file mode 100644 index 0000000..6f51697 --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreController/RGBController_AsusAuraCore.cpp @@ -0,0 +1,328 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraCore.cpp | +| | +| RGBController for ASUS ROG Aura Core | +| | +| Adam Honse (CalcProgrammer1) 13 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusAuraCore.h" + +/**------------------------------------------------------------------*\ + @name Asus AURA Core + @category Keyboard,LEDStrip + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectAsusAuraCoreControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AuraCore::RGBController_AuraCore(AuraCoreController* controller_ptr) +{ + controller = controller_ptr; + + name = "ASUS Aura Core Device"; + vendor = "ASUS"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + description = "ASUS Aura Core Device"; + type = DEVICE_TYPE_UNKNOWN; + + if(controller->aura_device.aura_type == AURA_CORE_DEVICE_KEYBOARD) + { + SetupKeyboard(); + } + else if(controller->aura_device.aura_type == AURA_CORE_DEVICE_GA15DH) + { + SetupGA15DH(); + } + + SetupZones(); +} + +void RGBController_AuraCore::SetupKeyboard() +{ + name = "ASUS Aura Keyboard"; + vendor = "ASUS"; + type = DEVICE_TYPE_KEYBOARD; + description = "ASUS Aura Core Device"; + + mode Static; + Static.name = "Static"; + Static.value = AURA_CORE_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AURA_CORE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = AURA_CORE_MODE_SPECTRUM_CYCLE; + ColorCycle.flags = 0; + ColorCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(ColorCycle); +} + +void RGBController_AuraCore::SetupGA15DH() +{ + name = "ASUS Aura GA15DH"; + vendor = "ASUS"; + type = DEVICE_TYPE_LEDSTRIP; + description = "ASUS Aura Core Device"; + + mode Static; + Static.name = "Static"; + Static.value = AURA_CORE_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AURA_CORE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.speed_min = AURA_CORE_SPEED_SLOW; + Breathing.speed_max = AURA_CORE_SPEED_FAST; + Breathing.speed = AURA_CORE_SPEED_NORMAL; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(2); + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = AURA_CORE_MODE_SPECTRUM_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED; + ColorCycle.speed_min = AURA_CORE_SPEED_SLOW; + ColorCycle.speed_max = AURA_CORE_SPEED_FAST; + ColorCycle.speed = AURA_CORE_SPEED_NORMAL; + ColorCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(ColorCycle); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = AURA_CORE_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.speed_min = AURA_CORE_SPEED_SLOW; + Rainbow.speed_max = AURA_CORE_SPEED_FAST; + Rainbow.speed = AURA_CORE_SPEED_NORMAL; + Rainbow.direction = MODE_DIRECTION_RIGHT; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Strobe; + Strobe.name = "Strobe"; + Strobe.value = AURA_CORE_MODE_STROBE; + Strobe.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Strobe.colors_min = 1; + Strobe.colors_max = 1; + Strobe.color_mode = MODE_COLORS_MODE_SPECIFIC; + Strobe.colors.resize(1); + modes.push_back(Strobe); + + mode Comet; + Comet.name = "Comet"; + Comet.value = AURA_CORE_MODE_COMET; + Comet.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Comet.colors_min = 1; + Comet.colors_max = 1; + Comet.color_mode = MODE_COLORS_MODE_SPECIFIC; + Comet.colors.resize(1); + modes.push_back(Comet); + + mode Flash; + Flash.name = "Flash & Dash"; + Flash.value = AURA_CORE_MODE_FLASHNDASH; + Flash.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Flash.colors_min = 1; + Flash.colors_max = 1; + Flash.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flash.colors.resize(1); + modes.push_back(Flash); + + mode Irradiation; + Irradiation.name = "Irradiation"; + Irradiation.value = AURA_CORE_MODE_IRRADIATION; + Irradiation.flags = 0; + Irradiation.color_mode = MODE_COLORS_NONE; + modes.push_back(Irradiation); + + mode Direct; + Static.name = "Direct"; + Static.value = AURA_CORE_MODE_DIRECT; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); +} + +RGBController_AuraCore::~RGBController_AuraCore() +{ + delete controller; +} + +void RGBController_AuraCore::SetupZones() +{ + zone auraZone; + + if(controller->aura_device.aura_type == AURA_CORE_DEVICE_KEYBOARD) + { + auraZone.name = "Keyboard"; + auraZone.type = ZONE_TYPE_SINGLE; + auraZone.leds_min = 4; + auraZone.leds_max = 4; + auraZone.leds_count = 4; + auraZone.matrix_map = NULL; + } + else if(controller->aura_device.aura_type == AURA_CORE_DEVICE_GA15DH) + { + auraZone.name = "GA15DH"; + auraZone.type = ZONE_TYPE_LINEAR; + auraZone.leds_min = 20; + auraZone.leds_max = 20; + auraZone.leds_count = 20; + auraZone.matrix_map = NULL; + } + else + { + auraZone.leds_count = 0; + } + + zones.push_back(auraZone); + + for(unsigned int led_idx = 0; led_idx < auraZone.leds_count; led_idx++) + { + led KeyLED; + KeyLED.name = auraZone.name + " "; + KeyLED.name.append(std::to_string(led_idx + 1)); + leds.push_back(KeyLED); + } + + SetupColors(); +} + +void RGBController_AuraCore::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AuraCore::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_AuraCore::UpdateZoneLEDs(int /*zone*/) +{ + if(modes[active_mode].value == AURA_CORE_MODE_DIRECT) + { + std::vector aura_colors; + std::vector& color_set = colors; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + color_set = modes[active_mode].colors; + } + + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + AuraColor new_color; + + new_color.red = RGBGetRValue(color_set[led_idx]); + new_color.green = RGBGetGValue(color_set[led_idx]); + new_color.blue = RGBGetBValue(color_set[led_idx]); + + aura_colors.push_back(new_color); + } + + controller->UpdateDirect(aura_colors); + } + else if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + UpdateSingleLED(led_idx); + } + } + else + { + UpdateSingleLED(0); + } +} + +void RGBController_AuraCore::UpdateSingleLED(int led) +{ + unsigned char speed = 0xFF; + unsigned char red = 0; + unsigned char green = 0; + unsigned char blue = 0; + unsigned char dir = 0; + mode& curr_mode = modes[active_mode]; + + if(curr_mode.color_mode == MODE_COLORS_PER_LED) + { + red = RGBGetRValue(colors[led]); + green = RGBGetGValue(colors[led]); + blue = RGBGetBValue(colors[led]); + } + else if(curr_mode.color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(curr_mode.colors[led]); + green = RGBGetGValue(curr_mode.colors[led]); + blue = RGBGetBValue(curr_mode.colors[led]); + } + + if(curr_mode.flags & MODE_FLAG_HAS_SPEED) + { + speed = curr_mode.speed; + } + + if(curr_mode.flags & MODE_FLAG_HAS_DIRECTION_LR) + { + if(curr_mode.direction == MODE_DIRECTION_RIGHT) + { + dir = 1; + } + } + + controller->SendUpdate + ( + led, + curr_mode.value, + speed, + dir, + red, + green, + blue + ); + + controller->SendSet(); + controller->SendApply(); +} + +void RGBController_AuraCore::DeviceUpdateMode() +{ + if(modes[active_mode].value == AURA_CORE_MODE_DIRECT) + { + controller->InitDirectMode(); + } + else + { + DeviceUpdateLEDs(); + } +} diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreController/RGBController_AsusAuraCore.h b/Controllers/AsusAuraCoreController/AsusAuraCoreController/RGBController_AsusAuraCore.h new file mode 100644 index 0000000..b9dbe9e --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreController/RGBController_AsusAuraCore.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraCore.h | +| | +| RGBController for ASUS ROG Aura Core | +| | +| Adam Honse (CalcProgrammer1) 13 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraCoreController.h" + +class RGBController_AuraCore : public RGBController +{ +public: + RGBController_AuraCore(AuraCoreController* controller_ptr); + ~RGBController_AuraCore(); + + void SetupKeyboard(); + void SetupGA15DH(); + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AuraCoreController* controller; +}; diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreControllerDetect.cpp b/Controllers/AsusAuraCoreController/AsusAuraCoreControllerDetect.cpp new file mode 100644 index 0000000..7d38e55 --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreControllerDetect.cpp @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| AsusAuraCoreControllerDetect.cpp | +| | +| Detector for ASUS ROG Aura Core | +| | +| Adam Honse (CalcProgrammer1) 13 Apr 2020 | +| Chris M (Dr_No) 28 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AsusAuraCoreController.h" +#include "RGBController.h" +#include "RGBController_AsusAuraCore.h" +#include "RGBController_AsusAuraCoreLaptop.h" +#include + +#define AURA_CORE_VID 0x0B05 + +/******************************************************************************************\ +* * +* DetectAuraCoreControllers * +* * +* Tests the USB address to see if an Asus ROG Aura Core controller exists there * +* * +\******************************************************************************************/ + +void DetectAsusAuraCoreControllers(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AuraCoreController* controller = new AuraCoreController(dev, info->path); + RGBController_AuraCore* rgb_controller = new RGBController_AuraCore(controller); + + if(rgb_controller->type != DEVICE_TYPE_UNKNOWN) + { + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + delete rgb_controller; + } + } +} + +void DetectAsusAuraCoreLaptopControllers(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AsusAuraCoreLaptopController* controller = new AsusAuraCoreLaptopController(dev, info->path); + RGBController_AsusAuraCoreLaptop* rgb_controller = new RGBController_AsusAuraCoreLaptop(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + + +REGISTER_HID_DETECTOR ("ASUS Aura Core", DetectAsusAuraCoreControllers, AURA_CORE_VID, 0x1854); +REGISTER_HID_DETECTOR ("ASUS Aura Core", DetectAsusAuraCoreControllers, AURA_CORE_VID, 0x1866); +REGISTER_HID_DETECTOR ("ASUS Aura Core", DetectAsusAuraCoreControllers, AURA_CORE_VID, 0x1869); +REGISTER_HID_DETECTOR_PU("ASUS ROG Strix SCAR 15", DetectAsusAuraCoreLaptopControllers, AURA_CORE_VID, AURA_STRIX_SCAR_15_PID, 0xFF31, 0x79); +REGISTER_HID_DETECTOR_PU("ASUS ROG Strix SCAR 17", DetectAsusAuraCoreLaptopControllers, AURA_CORE_VID, 0x1866, 0xFF31, 0x79); diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopController.cpp b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopController.cpp new file mode 100644 index 0000000..034ae53 --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopController.cpp @@ -0,0 +1,446 @@ +/*---------------------------------------------------------*\ +| AsusAuraCoreLaptopController.cpp | +| | +| Driver for ASUS ROG Aura Core Laptop | +| | +| Chris M (Dr_No) 28 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "AsusAuraCoreLaptopController.h" +#include "dmiinfo.h" +#include "SettingsManager.h" +#include "StringUtils.h" + +static std::string power_zones[ASUSAURACORELAPTOP_POWER_ZONES] = +{ + "Logo", + ZONE_EN_KEYBOARD, + "Lightbar", + "Lid Edges" +}; + +static std::string power_states[ASUSAURACORELAPTOP_POWER_STATES] = +{ + " when booting", + " when awake", + " when sleeping", + " when off", +}; + +AsusAuraCoreLaptopController::AsusAuraCoreLaptopController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + /*---------------------------------------------------------*\ + | The motherboard name will uniquely ID the laptop to | + | determine the metadata of the device. | + \*---------------------------------------------------------*/ + DMIInfo dmi_info; + std::string dmi_name = dmi_info.getMainboard(); + bool not_found = true; + + for(uint16_t i = 0; i < AURA_CORE_LAPTOP_DEVICE_COUNT; i++) + { + if(aura_core_laptop_device_list[i]->dmi_name == dmi_name) + { + /*---------------------------------------------------------*\ + | Set device ID | + \*---------------------------------------------------------*/ + not_found = false; + device_index = i; + break; + } + } + + if(not_found) + { + LOG_ERROR("[%s] device capabilities not found. Please creata a new device request.", + dmi_name.c_str()); + return; + } + + /*---------------------------------------------------------*\ + | Only set power config for known devices | + \*---------------------------------------------------------*/ + SetPowerConfigFromJSON(); + SendInitDirectMode(); +} + +AsusAuraCoreLaptopController::~AsusAuraCoreLaptopController() +{ + hid_close(dev); +} + +const aura_core_laptop_device* AsusAuraCoreLaptopController::GetDeviceData() +{ + return aura_core_laptop_device_list[device_index]; +} + +std::string AsusAuraCoreLaptopController::GetDeviceDescription() +{ + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + std::string name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + name.append(" ").append(StringUtils::wstring_to_string(name_string)); + return name; +} + +unsigned int AsusAuraCoreLaptopController::GetKeyboardLayout() +{ + const uint8_t index = 6; + uint8_t result = 0; + uint8_t rd_buf[ASUSAURACORELAPTOP_WRITE_PACKET_SIZE] = { ASUSAURACORELAPTOP_REPORT_ID }; + uint8_t buffer[ASUSAURACORELAPTOP_WRITE_PACKET_SIZE] = { ASUSAURACORELAPTOP_REPORT_ID, + ASUSAURACORELAPTOP_CMD_LAYOUT, + 0x20, + 0x31, + 0x00, + 0x10 }; + + /*---------------------------------------------------------*\ + | Clear the read buffer to ensure we read the right packet | + \*---------------------------------------------------------*/ + do + { + result = hid_read_timeout(dev, rd_buf, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE, 10); + } + while(result > 0); + + memset(&rd_buf[1], 0, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE - 1); + memset(&buffer[index], 0, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE - index); + + hid_send_feature_report(dev, buffer, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE); + result = hid_get_feature_report(dev, rd_buf, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE); + + LOG_DEBUG("[%s] GetKeyboardLayout %02X %02X %02X %02X %02X %02X %02X {%02X} %02X %02X %02X %02X", + aura_core_laptop_device_list[device_index]->dmi_name.c_str(), + rd_buf[5], rd_buf[6], rd_buf[7], rd_buf[8], rd_buf[9], rd_buf[10], + rd_buf[11], rd_buf[12], rd_buf[13], rd_buf[14], rd_buf[15], rd_buf[16]); + + if(result > 0) + { + return rd_buf[12]; + } + + LOG_DEBUG("[%s] GetKeyboardLayout: An error occurred! Setting layout to ANSI", + aura_core_laptop_device_list[device_index]->dmi_name.c_str()); + return ASUSAURACORELAPTOP_LAYOUT_ANSI; +} + +std::string AsusAuraCoreLaptopController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + LOG_DEBUG("[%s] Get HID Serial string failed", + aura_core_laptop_device_list[device_index]->dmi_name.c_str()); + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string AsusAuraCoreLaptopController::GetLocation() +{ + return("HID: " + location); +} + +void AsusAuraCoreLaptopController::SetMode(uint8_t mode, uint8_t speed, uint8_t brightness, RGBColor color1, RGBColor color2, uint8_t random, uint8_t direction) +{ + bool needs_update = !( (current_mode == mode ) && + (current_speed == speed ) && + (current_brightness == brightness ) && + (current_c1 == color1 ) && + (current_c2 == color2 ) && + (current_random == random ) && + (current_direction == direction ) ); + + if(needs_update) + { + current_mode = mode; + current_speed = speed; + current_brightness = brightness; + current_c1 = color1; + current_c2 = color2; + current_random = random; + current_direction = direction; + + if(current_mode == ASUSAURACORELAPTOP_MODE_DIRECT) + { + SendBrightness(); + SendInitDirectMode(); + return; + } + + SendUpdate(); + SendBrightness(); + } +} + +void AsusAuraCoreLaptopController::SendInitDirectMode() +{ + uint8_t buffer[ASUSAURACORELAPTOP_WRITE_PACKET_SIZE] = { ASUSAURACORELAPTOP_REPORT_ID, ASUSAURACORELAPTOP_CMD_DIRECT }; + memset(&buffer[2], 0, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE - 2); + + LOG_DEBUG("[%s] Resetting device for direct control", aura_core_laptop_device_list[device_index]->dmi_name.c_str()); + hid_send_feature_report(dev, buffer, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE); +} + +void AsusAuraCoreLaptopController::SetLedsDirect(std::vector colors) +{ + /*---------------------------------------------------------*\ + | The keyboard zone is a set of 168 keys (indexed from 0) | + | sent in 11 packets of 16 triplets. The Lid and Lightbar | + | zones are sent in one final packet afterwards. | + \*---------------------------------------------------------*/ + const uint8_t key_set = 167; + const uint8_t led_count = (uint8_t)colors.size(); + const uint16_t map_size = 3 * led_count; + const uint8_t leds_per_packet = 16; + uint8_t buffer[ASUSAURACORELAPTOP_WRITE_PACKET_SIZE] = { ASUSAURACORELAPTOP_REPORT_ID, ASUSAURACORELAPTOP_CMD_DIRECT, + 0x00, 0x01, 0x01, 0x01, 0x00, leds_per_packet, 0x00 }; + uint8_t* key_buf = new uint8_t[map_size]; + + memset(key_buf, 0, map_size); + + for(uint8_t led_index = 0; led_index < led_count; led_index++) + { + std::size_t buf_idx = (led_index * 3); + + key_buf[buf_idx] = RGBGetRValue(*colors[led_index]); + key_buf[buf_idx + 1] = RGBGetGValue(*colors[led_index]); + key_buf[buf_idx + 2] = RGBGetBValue(*colors[led_index]); + } + + for(uint8_t i = 0; i < key_set; i += leds_per_packet) + { + uint8_t leds_remaining = key_set - i; + + if(leds_remaining < leds_per_packet) + { + buffer[07] = leds_remaining; + + memset(&buffer[ASUSAURACORELAPTOP_DATA_BYTE], + 0, + ASUSAURACORELAPTOP_WRITE_PACKET_SIZE - ASUSAURACORELAPTOP_DATA_BYTE); + } + + buffer[06] = i; + memcpy(&buffer[ASUSAURACORELAPTOP_DATA_BYTE], &key_buf[3 * i], (3 * buffer[07])); + + LOG_DEBUG("[%s] Sending buffer @ index %d thru index %d", + aura_core_laptop_device_list[device_index]->dmi_name.c_str(), + i, + i + buffer[07]); + + hid_send_feature_report(dev, buffer, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE); + } + + buffer[4] = 0x04; + buffer[5] = 0x00; + buffer[6] = 0x00; + buffer[7] = 0x00; + + memset(&buffer[ASUSAURACORELAPTOP_DATA_BYTE], + 0, + ASUSAURACORELAPTOP_WRITE_PACKET_SIZE - ASUSAURACORELAPTOP_DATA_BYTE); + + if(led_count > key_set) + { + memcpy(&buffer[ASUSAURACORELAPTOP_DATA_BYTE], + &key_buf[3 * key_set], + (3 * (led_count - key_set))); + } + + LOG_DEBUG("[%s] Sending buffer @ index %d thru index %d", + aura_core_laptop_device_list[device_index]->dmi_name.c_str(), + key_set, + led_count); + + hid_send_feature_report(dev, buffer, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE); + delete[] key_buf; +} + +void AsusAuraCoreLaptopController::SendBrightness() +{ + const uint8_t index = 5; + uint8_t buffer[ASUSAURACORELAPTOP_WRITE_PACKET_SIZE] = { ASUSAURACORELAPTOP_REPORT_ID, ASUSAURACORELAPTOP_CMD_BRIGHTNESS, 0xC5, 0xC4}; + + memset(&buffer[index], 0, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE - index); + buffer[4] = current_brightness; + + hid_send_feature_report(dev, buffer, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE); +} + +void AsusAuraCoreLaptopController::SendUpdate() +{ + uint8_t buffer[ASUSAURACORELAPTOP_WRITE_PACKET_SIZE] = { ASUSAURACORELAPTOP_REPORT_ID, ASUSAURACORELAPTOP_CMD_UPDATE }; + + buffer[ASUSAURACORELAPTOP_ZONE_BYTE] = 0; + buffer[ASUSAURACORELAPTOP_MODE_BYTE] = current_mode; + buffer[ASUSAURACORELAPTOP_R1_BYTE] = RGBGetRValue(current_c1); + buffer[ASUSAURACORELAPTOP_G1_BYTE] = RGBGetGValue(current_c1); + buffer[ASUSAURACORELAPTOP_B1_BYTE] = RGBGetBValue(current_c1); + buffer[ASUSAURACORELAPTOP_SPEED_BYTE] = current_speed; + buffer[ASUSAURACORELAPTOP_DIRECTION_BYTE] = current_direction; + buffer[ASUSAURACORELAPTOP_DATA_BYTE] = current_random; + buffer[ASUSAURACORELAPTOP_R2_BYTE] = RGBGetRValue(current_c2); + buffer[ASUSAURACORELAPTOP_G2_BYTE] = RGBGetGValue(current_c2); + buffer[ASUSAURACORELAPTOP_B2_BYTE] = RGBGetBValue(current_c2); + + hid_send_feature_report(dev, buffer, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE); + + SendApply(); +} + +void AsusAuraCoreLaptopController::SendApply() +{ + const uint8_t index = 2; + uint8_t buffer[ASUSAURACORELAPTOP_WRITE_PACKET_SIZE] = { ASUSAURACORELAPTOP_REPORT_ID, ASUSAURACORELAPTOP_CMD_APPLY }; + + memset(&buffer[index], 0, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE - index); + + hid_send_feature_report(dev, buffer, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE); +} + +void AsusAuraCoreLaptopController::SendSet() +{ + const uint8_t index = 2; + uint8_t buffer[ASUSAURACORELAPTOP_WRITE_PACKET_SIZE] = { ASUSAURACORELAPTOP_REPORT_ID, ASUSAURACORELAPTOP_CMD_SET }; + + memset(&buffer[index], 0, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE - index); + + hid_send_feature_report(dev, buffer, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE); +} + +std::vector AsusAuraCoreLaptopController::PowerConfigArray() +{ + std::vector temp; + + for(uint8_t zone_index = 0; zone_index < ASUSAURACORELAPTOP_POWER_ZONES; zone_index++) + { + for(uint8_t state_index = 0; state_index < ASUSAURACORELAPTOP_POWER_STATES; state_index++) + { + p_state new_state; + + new_state.zone = power_zones[zone_index] + power_states[state_index]; + new_state.state = true; + + temp.push_back(new_state); + } + } + + return temp; +} + +void AsusAuraCoreLaptopController::SetPowerConfigFromJSON() +{ + std::vector power_config = PowerConfigArray(); + const std::string section_power = "PowerConfig"; + const std::string detector_name = "Asus Aura Core Laptop"; + SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager(); + json device_settings = settings_manager->GetSettings(detector_name); + + /*---------------------------------------------------------*\ + | Get Power state config from the settings manager | + | If PowerConfig is not found then write it to settings | + \*---------------------------------------------------------*/ + if(!device_settings.contains(section_power)) + { + json pcfg; + + for(size_t i = 0; i < power_config.size(); i++) + { + pcfg[power_config[i].zone] = power_config[i].state; + } + + device_settings[section_power] = pcfg; + settings_manager->SetSettings(detector_name, device_settings); + settings_manager->SaveSettings(); + LOG_DEBUG("[%s] default power config saved to openrgb.json", + aura_core_laptop_device_list[device_index]->dmi_name.c_str()); + } + else + { + for(size_t i = 0; i < power_config.size(); i++) + { + std::string key_name = power_config[i].zone; + + if(device_settings[section_power].contains(key_name)) + { + power_config[i].state = device_settings[section_power][key_name]; + LOG_DEBUG("[%s] Reading power config for %s: %s", + aura_core_laptop_device_list[device_index]->dmi_name.c_str(), + key_name.c_str(), ((power_config[i].state) ? "On" : "Off")); + } + } + } + + /*-----------------------------------------------------------------------------*\ + | Power state flags are packed in zones but the order is inconsistent. | + | With thanks to AsusCtl for helping to decipher the packet captures | + | https://gitlab.com/asus-linux/asusctl/-/blob/main/rog-aura/src/usb.rs#L150 | + \*-----------------------------------------------------------------------------*/ + bool flag_array[32] = + { + power_config[0].state, power_config[4].state, + power_config[1].state, power_config[5].state, + !power_config[2].state, !power_config[6].state, + !power_config[3].state, !power_config[7].state, + + false, power_config[8].state, + power_config[9].state, !power_config[10].state, + !power_config[11].state, false, + false, false, + + power_config[12].state, power_config[13].state, + !power_config[14].state, !power_config[15].state + }; + + uint32_t flags = PackPowerFlags(flag_array); + LOG_DEBUG("[%s] Sending power config Logo+KB: %02X Lightbar: %02X Lid Edges: %02X Raw: %08X", + aura_core_laptop_device_list[device_index]->dmi_name.c_str(), + (flags & 0xFF), ((flags >> 8) & 0xFF), ((flags >> 16) & 0xFF), flags); + SendPowerConfig(flags); +} + +void AsusAuraCoreLaptopController::SendPowerConfig(uint32_t flags) +{ + const uint8_t index = 6; + uint8_t buffer[ASUSAURACORELAPTOP_WRITE_PACKET_SIZE] = { ASUSAURACORELAPTOP_REPORT_ID, ASUSAURACORELAPTOP_CMD_POWER, 0x01, 0x00, 0x00, 0x0F }; + + memset(&buffer[index], 0, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE - index); + + buffer[3] = flags & 0xFF; + buffer[4] = (flags >> 8) & 0xFF; + buffer[5] = (flags >> 16) & 0xFF; + + hid_send_feature_report(dev, buffer, ASUSAURACORELAPTOP_WRITE_PACKET_SIZE); +} + +uint32_t AsusAuraCoreLaptopController::PackPowerFlags(bool flags[]) +{ + uint32_t temp = {}; + const uint8_t length = 32; + + for (size_t i = 0; i < length; ++i) + { + uint32_t flag = {flags[i]}; + flag <<= i; + temp |= flag; + } + + return temp; +} diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopController.h b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopController.h new file mode 100644 index 0000000..5b3e8fe --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopController.h @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| AsusAuraCoreLaptopController.h | +| | +| Driver for ASUS ROG Aura Core Laptop | +| | +| Chris M (Dr_No) 28 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "LogManager.h" +#include "RGBController.h" +#include "ResourceManager.h" +#include "RGBControllerKeyNames.h" +#include "AsusAuraCoreLaptopDevices.h" + +#define NA 0xFFFFFFFF +#define HID_MAX_STR 255 +#define ASUSAURACORELAPTOP_TIMEOUT 250 +#define ASUSAURACORELAPTOP_READ_PACKET_SIZE 64 +#define ASUSAURACORELAPTOP_WRITE_PACKET_SIZE 64 //Buffer requires a prepended ReportID hence + 1 + +#define ASUSAURACORELAPTOP_KEYCOUNT 91 +#define ASUSAURACORELAPTOP_KEY_WIDTH 18 +#define ASUSAURACORELAPTOP_KEY_HEIGHT 7 +#define ASUSAURACORELAPTOP_LIGHTBARCOUNT 6 +#define ASUSAURACORELAPTOP_LIDCOUNT 3 +#define ASUSAURACORELAPTOP_POWER_ZONES 4 +#define ASUSAURACORELAPTOP_POWER_STATES 4 +#define ASUSAURACORELAPTOP_BRIGHTNESS_MIN 0 +#define ASUSAURACORELAPTOP_BRIGHTNESS_MAX 3 // No device has proven to have 256 keyboard brightness levels, only 0..3 + +enum +{ + ASUSAURACORELAPTOP_MODE_OFF = 0x00, //Turn off - All leds off + ASUSAURACORELAPTOP_MODE_DIRECT = 0xFF, //Direct Led Control - Independently set LEDs in zone + ASUSAURACORELAPTOP_MODE_STATIC = 0x00, //Static Mode - Set entire zone to a single color. + ASUSAURACORELAPTOP_MODE_BREATHING = 0x01, //Breathing Mode - Fades between fully off and fully on. + ASUSAURACORELAPTOP_MODE_SPECTRUM = 0x02, //Spectrum Cycle Mode - Cycles through the color spectrum on all lights on the device + ASUSAURACORELAPTOP_MODE_RAINBOW = 0x03, //Rainbow Wave Mode - Cycle thru the color spectrum as a wave across all LEDs + ASUSAURACORELAPTOP_MODE_FLASHING = 0x0A, //Flashing Mode - Abruptly changing between fully off and fully on. + + /*-------------------------------------------------*\ + | Modes not implemented in the Armoury Crate | + | OEM software that were discovered. | + \*-------------------------------------------------*/ + ASUSAURACORELAPTOP_MODE_STARRY_NIGHT = 0x04, //Starry Night Mode + ASUSAURACORELAPTOP_MODE_RAIN = 0x05, //Rain Mode + ASUSAURACORELAPTOP_MODE_REACT_FADE = 0x06, //Reactive Fade Mode + ASUSAURACORELAPTOP_MODE_REACT_LASER = 0x07, //Reactive Laser Mode + ASUSAURACORELAPTOP_MODE_REACT_RIPPLE = 0x08, //Reactive Ripple Mode + ASUSAURACORELAPTOP_MODE_COMET = 0x0B, //Comet Mode + ASUSAURACORELAPTOP_MODE_FLASHNDASH = 0x0C, //Flash n Dash Mode + ASUSAURACORELAPTOP_MODE_KEYSTONE = 0x0D, //Keystone Mode +}; + +enum +{ + ASUSAURACORELAPTOP_ZONE_BYTE = 2, + ASUSAURACORELAPTOP_MODE_BYTE = 3, + ASUSAURACORELAPTOP_R1_BYTE = 4, + ASUSAURACORELAPTOP_G1_BYTE = 5, + ASUSAURACORELAPTOP_B1_BYTE = 6, + ASUSAURACORELAPTOP_SPEED_BYTE = 7, + ASUSAURACORELAPTOP_DIRECTION_BYTE = 8, + ASUSAURACORELAPTOP_DATA_BYTE = 9, + ASUSAURACORELAPTOP_R2_BYTE = 10, + ASUSAURACORELAPTOP_G2_BYTE = 11, + ASUSAURACORELAPTOP_B2_BYTE = 12, +}; + +enum +{ + ASUSAURACORELAPTOP_REPORT_ID = 0x5D, + ASUSAURACORELAPTOP_CMD_BRIGHTNESS = 0xBA, + ASUSAURACORELAPTOP_CMD_DIRECT = 0xBC, + ASUSAURACORELAPTOP_CMD_POWER = 0xBD, + ASUSAURACORELAPTOP_CMD_UPDATE = 0xB3, + ASUSAURACORELAPTOP_CMD_APPLY = 0xB4, + ASUSAURACORELAPTOP_CMD_SET = 0xB5, + ASUSAURACORELAPTOP_CMD_LAYOUT = 0x05, +}; + +enum +{ + ASUSAURACORELAPTOP_SPEED_SLOWEST = 0xE1, // Slowest speed + ASUSAURACORELAPTOP_SPEED_NORMAL = 0xEB, // Normal speed + ASUSAURACORELAPTOP_SPEED_FASTEST = 0xF5, // Fastest speed +}; + +enum aura_core_laptop_layout +{ + ASUSAURACORELAPTOP_LAYOUT_ANSI = 0x01, /* US ANSI Layout */ + ASUSAURACORELAPTOP_LAYOUT_ISO = 0x02, /* EURO ISO Layout */ +}; + +struct p_state +{ + std::string zone; + bool state; +}; + +class AsusAuraCoreLaptopController +{ +public: + AsusAuraCoreLaptopController(hid_device* dev_handle, const char* path); + ~AsusAuraCoreLaptopController(); + + const aura_core_laptop_device* GetDeviceData(); + std::string GetDeviceDescription(); + std::string GetSerial(); + unsigned int GetKeyboardLayout(); + std::string GetLocation(); + + void SetMode(uint8_t mode, uint8_t speed, uint8_t brightness, RGBColor color1, RGBColor color2, uint8_t random, uint8_t direction); + void SetLedsDirect(std::vectorcolors); + void SendInitDirectMode(); +private: + hid_device* dev; + uint16_t device_index; + std::string location; + + uint8_t current_mode; + uint8_t current_speed; + uint8_t current_direction; + + RGBColor current_c1; + RGBColor current_c2; + uint8_t current_brightness; + uint8_t current_random; + + void SendApply(); + void SendBrightness(); + void SendSet(); + void SendUpdate(); + + void SetPowerConfigFromJSON(); + void SendPowerConfig(uint32_t flags); + uint32_t PackPowerFlags(bool flags[]); + std::vector PowerConfigArray(); +}; diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopDevices.cpp b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopDevices.cpp new file mode 100644 index 0000000..4bc2fd2 --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopDevices.cpp @@ -0,0 +1,960 @@ +#include "AsusAuraCoreLaptopDevices.h" + +/*-------------------------------------------------------------------------*\ +| Aura Core Key Values | +| NULL values are keys that are handed to the KLM but are removed as | +| a part of customisation. They are included to maintain expected key | +| count is aligned to the value count. | +\*-------------------------------------------------------------------------*/ + +std::vector aura_core_laptop_15_16_values = +{ + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 DEL */ + 21, 23, 24, 25, 26, 28, 29, 30, 31, 33, 34, 35, 36, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC PLAY */ + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* TAB Q W E R T Y U I O P [ ] \ STOP */ + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + /* CPLK A S D F G H J K L ; " # ENTR PREV */ + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 98, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NEXT */ + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 119, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR */ + 126, 128, 129, 131, 135, 136, 136, 137, +}; + +std::vector aura_core_laptop_17_values = +{ + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 NULL NULL PAUS */ + 21, 23, 24, 25, 26, 28, 29, 30, 31, 33, 34, 35, 36, 0, 0, 39, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC NULL NULL NULL NMLK NMDV NMTM NMMI */ + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 56, 0, 0, 0, 59, 60, 61, 62, + /* TAB Q W E R T Y U I O P [ ] \ NULL NULL NULL NM7 NM8 NM9 NMPL */ + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 0, 0, 0, 80, 81, 82, 83, + /* CPLK A S D F G H J K L ; " # ENTR NM4 NM5 NM6 */ + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 98, 101, 102, 103, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 119, 139, 122, 123, 124, 125, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR NM0 NMPD */ + 126, 128, 129, 131, 135, 136, 136, 137, 159, 160, 161, 144, 145, +}; + +std::vector aura_core_laptop_18_values = +{ + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 NULL NULL PAUS */ + 21, 23, 24, 25, 26, 28, 29, 30, 31, 33, 34, 35, 36, 0, 0, 39, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC NULL NULL NULL NMLK NMDV NMTM NMMI */ + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 56, 0, 0, 0, 59, 60, 61, 62, + /* TAB Q W E R T Y U I O P [ ] \ NULL NULL NULL NM7 NM8 NM9 NMPL */ + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 0, 0, 0, 80, 81, 82, 83, + /* CPLK A S D F G H J K L ; " # ENTR NM4 NM5 NM6 */ + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 98, 101, 102, 103, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 119, 121, 122, 123, 124, 125, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR NM0 NMPD */ + 126, 128, 129, 131, 135, 136, 136, 137, 141, 142, 143, 144, 145, +}; + +std::vector aura_core_laptop_17_g733qr_values = +{ + /* volD volU Mute Fan ROG */ + 2, 3, 4, 5, 6, + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 DEL NUM7 NUM8 NUM9 */ + 21, 23, 24, 25, 26, 28, 29, 30, 31, 33, 34, 35, 36, 38, 39, 40, 41, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC /NUM NUM4 NUM5 NUM6 */ + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 56, 59, 60, 61, 62, + /* TAB Q W E R T Y U I O P [ ] # *NUM NUM1 NUM2 NUM3 */ + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 78, 80, 81, 82, 83, + /* CPLK A S D F G H J K L ; ' ENTR -NUM +NUM NUM0 DOT */ + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 98, 101, 102, 103, 104, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU */ + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 118, 123, + /* LCTL LFNC LWIN LALT SPC RALT PRSC RFNC RCTL ARWL ARWD ARWR KeyStone(only Red) */ + 126, 127, 128, 129, 132, 135, 137, 139, 141, 143, 144, 145, 175, +}; +/*-------------------------------------------------------------------------*\ +| KEYMAPS | +\*-------------------------------------------------------------------------*/ +keyboard_keymap_overlay_values g533zm_layout +{ + KEYBOARD_SIZE_SEVENTY_FIVE, + { + aura_core_laptop_15_16_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 15, 37, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Delete + { 0, 1, 15, 58, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Play / Pause + { 0, 2, 15, 79, KEY_EN_MEDIA_STOP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Stop + { 0, 3, 15, 100, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Previous + { 0, 4, 13, 139, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Up Arrow + { 0, 4, 15, 121, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Next + { 0, 5, 12, 159, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Left Arrow + { 0, 5, 13, 160, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Down Arrow + { 0, 5, 14, 161, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Right Arrow + } +}; + +keyboard_keymap_overlay_values g533zw_layout +{ + KEYBOARD_SIZE_SEVENTY_FIVE, + { + aura_core_laptop_15_16_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 15, 37, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Delete + { 0, 1, 15, 58, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Play / Pause + { 0, 2, 15, 79, KEY_EN_MEDIA_STOP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Stop + { 0, 3, 15, 100, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Previous + { 0, 4, 15, 121, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Media Next + { 0, 5, 1, 127, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Left Function key + { 0, 5, 4, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Unused key + { 0, 5, 11, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Right Func key + { 0, 5, 11, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Right Context key + { 0, 5, 13, 139, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Up Arrow + { 0, 5, 15, 142, KEY_EN_PRINT_SCREEN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Print Screen + { 0, 5, 16, 175, "Keystone", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Keystone (Red only) + { 0, 6, 12, 159, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Left Arrow on new row + { 0, 6, 13, 160, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Down Arrow + { 0, 6, 14, 161, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Right Arrow + { 0, 0, 2, 2, KEY_EN_MEDIA_VOLUME_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, // Insert Row before function keys + { 0, 0, 3, 3, KEY_EN_MEDIA_VOLUME_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Vol Up + { 0, 0, 4, 4, "Mic On / Off", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Mic On / Off + { 0, 0, 5, 5, "Hyperfan", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Hyperfan + { 0, 0, 6, 6, "Armoury Crate", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Armoury Crate + } +}; + +keyboard_keymap_overlay_values g533zw_lid_layout +{ + KEYBOARD_SIZE_EMPTY, + { + { /* Values not set in empty keyboard */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 167, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L1 + { 0, 0, 1, 176, "Vertical Cut Left", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L2 + { 0, 0, 2, 177, "Vertical Cut Right", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L3 + } +}; + +keyboard_keymap_overlay_values g533zw_lightbar_layout +{ + KEYBOARD_SIZE_EMPTY, + { + { /* Values not set in empty keyboard */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 174, "Lightbar L1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L1 + { 0, 0, 1, 173, "Lightbar L2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L2 + { 0, 0, 2, 172, "Lightbar L3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L3 + { 0, 0, 3, 171, "Lightbar R3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar R3 + { 0, 0, 4, 170, "Lightbar R2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar R2 + { 0, 0, 5, 169, "Lightbar R1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar R1 + } +}; + +keyboard_keymap_overlay_values g614jz_keyboard_layout +{ + KEYBOARD_SIZE_SEVENTY_FIVE, + { + aura_core_laptop_15_16_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 2, 2, KEY_EN_MEDIA_VOLUME_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, // Insert Row and add Volume Down + { 0, 0, 3, 3, KEY_EN_MEDIA_VOLUME_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Volume Up + { 0, 0, 4, 4, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Mute + { 0, 0, 5, 5, "Key: Fan", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert "Fan" key + { 0, 0, 6, 6, "Key: ROG", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert "ROG" key + { 0, 6, 1, 127, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Left Fuction + { 0, 6, 8, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove part of Spacebar + { 0, 6, 8, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove part of Spacebar + { 0, 6, 10, 136, KEY_EN_RIGHT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Right Fuction + { 0, 6, 10, 136, KEY_EN_RIGHT_WINDOWS, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Right Win Key + + { 0, 1, 15, 37, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Delete + { 0, 2, 15, 58, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Play / Pause + { 0, 3, 15, 79, KEY_EN_MEDIA_STOP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Stop + { 0, 3, 13, 76, KEY_EN_ANSI_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Force ANSI | even on ISO layouts + { 0, 4, 15, 100, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Previous + { 0, 5, 15, 121, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Next + { 0, 5, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // gap before arrow up key + + { 0, 6, 10, 136, KEY_EN_PRINT_SCREEN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert a Print Screen key + { 0, 6, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // trim extra column before numpad + + // Close numpad gap completely + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + + // Move Arrow keys + { 0, 6, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Arrow Down + { 0, 5, 13, 120, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Shift Arrow Ups4 + { 0, 6, 12, 140, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 13, 141, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 14, 142, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +keyboard_keymap_overlay_values g614jz_lightbar_layout +{ + KEYBOARD_SIZE_EMPTY, + { + { /* Values not set in empty keyboard */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 173, "Lightbar L1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L2 + { 0, 1, 0, 172, "Lightbar L2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L3 + { 0, 0, 14, 169, "Lightbar R1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar R1 + { 0, 1, 14, 170, "Lightbar R2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar R2 + } +}; + +keyboard_keymap_overlay_values g713rw_keyboard_layout +{ + KEYBOARD_SIZE_FULL, + { + aura_core_laptop_17_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 2, 2, KEY_EN_MEDIA_VOLUME_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, // Insert Row and add Volume Down + { 0, 0, 3, 3, KEY_EN_MEDIA_VOLUME_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Volume Up + { 0, 0, 4, 4, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Mute + { 0, 0, 5, 5, "Key: Fan", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert "Fan" key + { 0, 0, 6, 6, "Key: ROG", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert "ROG" key + { 0, 6, 1, 127, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Left Fuction + { 0, 6, 8, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove part of Spacebar + { 0, 6, 8, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove part of Spacebar + { 0, 6, 10, 136, KEY_EN_RIGHT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Right Fuction + { 0, 6, 10, 136, KEY_EN_RIGHT_WINDOWS, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Right Win Key + + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 1, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // trim extra column before numpad + { 0, 1, 15, 38, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert a Delete key + { 0, 1, 17, 40, KEY_EN_PRINT_SCREEN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert a Print Screen key + { 0, 1, 18, 41, KEY_EN_HOME, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert a Home key + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 3, 13, 76, KEY_EN_ANSI_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Force ANSI | even on ISO layouts + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 5, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // gap before arrow up key + { 0, 5, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // trim extra column before numpad + + { 0, 6, 11, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // 1 empty key before arrow keys + { 0, 6, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // trim extra column before numpad + { 0, 6, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // 1 empty key between numpad 0 and . + + // Close numpad gap completely + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 5, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 6, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + + // Move Arrow keys + { 0, 5, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Arrow Up + { 0, 6, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Arrow Down + { 0, 6, 12, 139, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Shift Arrow Up + { 0, 7, 11, 159, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 7, 12, 160, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 7, 13, 161, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +keyboard_keymap_overlay_values g713rw_lightbar_layout +{ + KEYBOARD_SIZE_EMPTY, + { + { /* Values not set in empty keyboard */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 174, "Lightbar L1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L1 + { 0, 0, 1, 173, "Lightbar L2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L2 + { 0, 0, 2, 172, "Lightbar L3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L3 + { 0, 0, 3, 171, "Lightbar R3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar R3 + { 0, 0, 4, 170, "Lightbar R2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar R2 + { 0, 0, 5, 169, "Lightbar R1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar R1 + } +}; + +keyboard_keymap_overlay_values g713rw_test_layout +{ + KEYBOARD_SIZE_EMPTY, + { + { /* Values not set in empty keyboard */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 7, "Test01", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 1, 8, "Test02", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 2, 9, "Test03", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 3, 10, "Test04", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 4, 11, "Test05", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 5, 12, "Test06", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 6, 13, "Test07", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 7, 14, "Test08", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 8, 15, "Test09", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 9, 16, "Test10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 10, 17, "Test11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 11, 18, "Test12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 12, 19, "Test13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 13, 20, "Test14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 14, 22, "Test15", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 15, 27, "Test16", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 16, 55, "Test17", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 17, 57, "Test18", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 58, "Test19", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 77, "Test20", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 78, "Test21", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 21, 79, "Test22", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 22, 96, "Test23", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 23, 97, "Test24", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 24, 99, "Test25", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 25, 100, "Test26", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 26, 104, "Test27", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 27, 117, "Test28", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 28, 118, "Test29", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 29, 120, "Test30", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 30, 121, "Test31", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 31, 130, "Test32", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 32, 132, "Test33", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 33, 133, "Test34", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 34, 134, "Test35", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 35, 138, "Test36", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 36, 140, "Test37", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 37, 141, "Test38", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 38, 142, "Test39", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 39, 143, "Test40", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 40, 146, "Test41", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 41, 147, "Test42", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 42, 148, "Test43", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 43, 149, "Test44", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 44, 150, "Test45", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 45, 151, "Test46", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 46, 152, "Test47", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 47, 153, "Test48", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 48, 154, "Test49", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 49, 155, "Test50", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 50, 156, "Test51", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 51, 157, "Test52", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 52, 158, "Test53", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 53, 162, "Test54", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 54, 163, "Test55", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 55, 164, "Test56", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 56, 165, "Test57", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 57, 166, "Test58", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 58, 167, "Test59", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +keyboard_keymap_overlay_values g733zm_layout +{ + KEYBOARD_SIZE_FULL, + { + aura_core_laptop_17_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + } +}; + +keyboard_keymap_overlay_values g733qr_keyboard_layout +{ + KEYBOARD_SIZE_EMPTY, + { + { /* No base values, we build everything via edit keys */ }, + { + /* Regional layout fixes (none yet) */ + } + }, + { + /*---------------------------------------------------------------------*\ + | Row 0 - Top macro row (left to right) | + \*---------------------------------------------------------------------*/ + //{ 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + //{ 0, 0, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + { 0, 0, 2, 2, KEY_EN_MEDIA_VOLUME_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 0, 3, 3, KEY_EN_MEDIA_VOLUME_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 0, 4, 4, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 0, 5, 5, "Key: Fan", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 0, 6, 6, "Key: ROG", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + /*---------------------------------------------------------------------*\ + | Row 1 - Esc, F-row, Delete, Num 7-9 | + \*---------------------------------------------------------------------*/ + { 0, 1, 0, 21, KEY_EN_ESCAPE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + //{ 0, 1, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + + { 0, 1, 2, 23, KEY_EN_F1, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1, 3, 24, KEY_EN_F2, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1, 4, 25, KEY_EN_F3, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1, 5, 26, KEY_EN_F4, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + //{ 0, 1, 6, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + + { 0, 1, 7, 28, KEY_EN_F5, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1, 8, 29, KEY_EN_F6, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1, 9, 30, KEY_EN_F7, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1,10, 31, KEY_EN_F8, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + //{ 0, 1,11, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + + { 0, 1,11, 33, KEY_EN_F9, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1,12, 34, KEY_EN_F10, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1,13, 35, KEY_EN_F11, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1,14, 36, KEY_EN_F12, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + //{ 0, 1,16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + + { 0, 1,15, 38, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1,16, 39, KEY_EN_NUMPAD_7, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1,17, 40, KEY_EN_NUMPAD_8, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 1,18, 41, KEY_EN_NUMPAD_9, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + /*---------------------------------------------------------------------*\ + | Row 2 - `123...BSPC, Num / 4 5 6 | + \*---------------------------------------------------------------------*/ + { 0, 2, 0, 42, KEY_EN_BACK_TICK, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2, 1, 43, KEY_EN_1, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2, 2, 44, KEY_EN_2, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2, 3, 45, KEY_EN_3, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2, 4, 46, KEY_EN_4, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2, 5, 47, KEY_EN_5, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2, 6, 48, KEY_EN_6, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2, 7, 49, KEY_EN_7, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2, 8, 50, KEY_EN_8, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2, 9, 51, KEY_EN_9, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2,10, 52, KEY_EN_0, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2,11, 53, KEY_EN_MINUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2,12, 54, KEY_EN_EQUALS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2,13, 56, KEY_EN_BACKSPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + //{ 0, 2,15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + + { 0, 2,15, 59, KEY_EN_NUMPAD_DIVIDE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2,16, 60, KEY_EN_NUMPAD_4, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2,17, 61, KEY_EN_NUMPAD_5, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 2,18, 62, KEY_EN_NUMPAD_6, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + /*---------------------------------------------------------------------*\ + | Row 3 - Tab row + Num * 1 2 3 | + \*---------------------------------------------------------------------*/ + { 0, 3, 0, 63, KEY_EN_TAB, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3, 1, 64, KEY_EN_Q, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3, 2, 65, KEY_EN_W, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3, 3, 66, KEY_EN_E, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3, 4, 67, KEY_EN_R, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3, 5, 68, KEY_EN_T, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3, 6, 69, KEY_EN_Y, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3, 7, 70, KEY_EN_U, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3, 8, 71, KEY_EN_I, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3, 9, 72, KEY_EN_O, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3,10, 73, KEY_EN_P, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3,11, 74, KEY_EN_LEFT_BRACKET, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3,12, 75, KEY_EN_RIGHT_BRACKET, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3,13, 78, KEY_EN_POUND, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + //{ 0, 3,14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + + { 0, 3,15, 80, KEY_EN_NUMPAD_TIMES, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3,16, 81, KEY_EN_NUMPAD_1, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3,17, 82, KEY_EN_NUMPAD_2, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3,18, 83, KEY_EN_NUMPAD_3, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + /*---------------------------------------------------------------------*\ + | Row 4 - Caps row + Num - + 0 . | + \*---------------------------------------------------------------------*/ + { 0, 4, 0, 84, KEY_EN_CAPS_LOCK, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4, 1, 85, KEY_EN_A, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4, 2, 86, KEY_EN_S, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4, 3, 87, KEY_EN_D, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4, 4, 88, KEY_EN_F, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4, 5, 89, KEY_EN_G, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4, 6, 90, KEY_EN_H, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4, 7, 91, KEY_EN_J, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4, 8, 92, KEY_EN_K, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4, 9, 93, KEY_EN_L, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4,10, 94, KEY_EN_SEMICOLON, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4,11, 95, KEY_EN_QUOTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4,12, 98, KEY_EN_ANSI_ENTER, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT}, + + //{ 0, 4,14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + + { 0, 4,15, 101, KEY_EN_NUMPAD_MINUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4,16, 102, KEY_EN_NUMPAD_PLUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4,17, 103, KEY_EN_NUMPAD_0, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 4,18, 104, KEY_EN_NUMPAD_PERIOD, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + /*---------------------------------------------------------------------*\ + | Row 5 - Shift row + Up Arrow | + \*---------------------------------------------------------------------*/ + { 0, 5, 0, 105, KEY_EN_LEFT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5, 1, 106, KEY_EN_ANSI_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5, 2, 107, KEY_EN_Z, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5, 3, 108, KEY_EN_X, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5, 4, 109, KEY_EN_C, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5, 5, 110, KEY_EN_V, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5, 6, 111, KEY_EN_B, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5, 7, 112, KEY_EN_N, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5, 8, 113, KEY_EN_M, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5, 9, 114, KEY_EN_COMMA, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5,10, 115, KEY_EN_PERIOD, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5,11, 116, KEY_EN_FORWARD_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5,12, 118, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + //{ 0, 5,14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + //{ 0, 5,15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + //{ 0, 5,16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + + { 0, 5,16, 123, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + /*---------------------------------------------------------------------*\ + | Row 6 - Ctrl/Win/Alt/Space/Fn/Arrows/Keystone | + \*---------------------------------------------------------------------*/ + { 0, 6, 0, 126, KEY_EN_LEFT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 6, 1, 127, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 6, 2, 128, KEY_EN_LEFT_WINDOWS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 6, 3, 129, KEY_EN_LEFT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 6, 4, 132, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + { 0, 6, 9, 135, KEY_EN_RIGHT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 6,10, 137, KEY_EN_PRINT_SCREEN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 6,11, 139, KEY_EN_RIGHT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 6,12, 141, KEY_EN_RIGHT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + + //{ 0, 6,13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + //{ 0, 6,14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // GAP + + { 0, 6,15, 143, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 6,16, 144, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 6,17, 145, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + } +}; + +keyboard_keymap_overlay_values g733qr_lid_layout +{ + KEYBOARD_SIZE_EMPTY, + { + { /* Values not set in empty keyboard */ }, + { + /* Regional layout fixes (none) */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 167, "ROG Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, // Single lid/logo zone + { 0, 0, 1, 176, "Vertical Cut Left", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L2 + { 0, 0, 2, 177, "Vertical Cut Right", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L3 + } +}; + +keyboard_keymap_overlay_values g733qr_lightbar_layout +{ + KEYBOARD_SIZE_EMPTY, + { + { /* Values not set in empty keyboard */ }, + { + /* Regional layout fixes (none) */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 174, "Lightbar L1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 0, 1, 173, "Lightbar L2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 0, 2, 172, "Lightbar L3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 0, 3, 171, "Lightbar R3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 0, 4, 170, "Lightbar R2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 0, 5, 169, "Lightbar R1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + } + +}; + +keyboard_keymap_overlay_values g733qr_keystone_layout +{ + KEYBOARD_SIZE_EMPTY, + { + { /* Values not set in empty keyboard */ }, + { + /* Regional layout fixes (none) */ + } + }, + { + { 0, 0, 0, 175, "Keystone", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + } +}; + +keyboard_keymap_overlay_values g814jv_keyboard_layout +{ + KEYBOARD_SIZE_FULL, + { + aura_core_laptop_18_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 2, 2, KEY_EN_MEDIA_VOLUME_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, // Insert Row and add Volume Down + { 0, 0, 3, 3, KEY_EN_MEDIA_VOLUME_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Volume Up + { 0, 0, 4, 4, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Mute + { 0, 0, 5, 5, "Key: Fan", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert "Fan" key + { 0, 0, 6, 6, "Key: ROG", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert "ROG" key + { 0, 6, 8, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove part of Spacebar + { 0, 6, 8, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove part of Spacebar + { 0, 6, 1, 127, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Left Fuction + { 0, 6, 10, 136, KEY_EN_PRINT_SCREEN, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Replace Right Fuction with PrtSc + { 0, 6, 11, 0, KEY_EN_RIGHT_WINDOWS, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Right Win Key + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 1, 14, 38, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert a Delete key + { 0, 1, 16, 40, KEY_EN_PRINT_SCREEN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert a Print Screen key + { 0, 1, 17, 41, KEY_EN_HOME, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert a Home key + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 3, 13, 76, KEY_EN_ANSI_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Force ANSI | even on ISO layouts + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Numpad gap + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 5, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 5, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 5, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + { 0, 6, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + } +}; + +keyboard_keymap_overlay_values g814jv_lightbar_layout +{ + KEYBOARD_SIZE_EMPTY, + { + { /* Values not set in empty keyboard */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 173, "Lightbar L1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L1 + { 0, 0, 1, 172, "Lightbar L2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar L2 + { 0, 0, 2, 170, "Lightbar R2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar R2 + { 0, 0, 3, 169, "Lightbar R1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lightbar R2 + } +}; + + +/*-------------------------------------------------------------------------*\ +| AURA CORE LAPTOP DEVICES | +\*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------*\ +| ASUS ROG Strix SCAR 15 G533ZM | +| | +| Zone "Keyboard" | +| Matrix | +| 7 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const aura_core_laptop_zone g533zm_zone = +{ + ZONE_EN_KEYBOARD, + &g533zm_layout +}; + +static const aura_core_laptop_device g533zm_device = +{ + "G533ZM", + { + &g533zm_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + } +}; + +/*-------------------------------------------------------------*\ +| ASUS ROG Strix SCAR 15 G533ZW | +| | +| Zone "Keyboard" | +| Matrix | +| 7 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const aura_core_laptop_zone g533zw_lid_zone = +{ + "Lid", + &g533zw_lid_layout +}; + +static const aura_core_laptop_zone g533zw_kb_zone = +{ + ZONE_EN_KEYBOARD, + &g533zw_layout +}; + +static const aura_core_laptop_zone g533zw_lightbar_zone = +{ + "Lightbar", + &g533zw_lightbar_layout +}; + +static const aura_core_laptop_device g533zw_device = +{ + "G533ZW", + { + &g533zw_kb_zone, + &g533zw_lid_zone, + &g533zw_lightbar_zone, + nullptr, + nullptr, + nullptr + } +}; + +/*-------------------------------------------------------------*\ +| ASUS ROG Strix SCAR 16 G614JZ | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 19 Columns | +\*-------------------------------------------------------------*/ +static const aura_core_laptop_zone g614jz_keyboard_zone = +{ + ZONE_EN_KEYBOARD, + &g614jz_keyboard_layout +}; +static const aura_core_laptop_zone g614jz_lightbar_zone = +{ + "Lightbar", + &g614jz_lightbar_layout +}; + + +static const aura_core_laptop_device g614jz_device = +{ + "G614JZ", + { + &g614jz_keyboard_zone, + &g614jz_lightbar_zone, + nullptr, + nullptr, + nullptr, + nullptr + } +}; + +/*-------------------------------------------------------------*\ +| ASUS ROG Strix SCAR G713RW | +| | +| Zone "Keyboard" | +| Matrix | +| 7 Rows, 20 Columns | +\*-------------------------------------------------------------*/ +static const aura_core_laptop_zone g713rw_keyboard_zone = +{ + ZONE_EN_KEYBOARD, + &g713rw_keyboard_layout +}; + +static const aura_core_laptop_zone g713rw_lightbar_zone = +{ + "Lightbar", + &g713rw_lightbar_layout +}; + +static const aura_core_laptop_device g713rw_device = +{ + "G713RW", + { + &g713rw_keyboard_zone, + &g713rw_lightbar_zone, + nullptr, + nullptr, + nullptr, + nullptr + } +}; + +/*-------------------------------------------------------------*\ +| ASUS ROG Strix G17 G733QR | +| | +| Zones: | +| Zone "Keyboard" | +| Matrix | +| 7 Rows, 19 Columns | +\*-------------------------------------------------------------*/ +static const aura_core_laptop_zone g733qr_keyboard_zone = + { + ZONE_EN_KEYBOARD, + &g733qr_keyboard_layout +}; + +static const aura_core_laptop_zone g733qr_lid_zone = + { + "Lid", + &g733qr_lid_layout +}; + +static const aura_core_laptop_zone g733qr_lightbar_zone = + { + "Lightbar", + &g733qr_lightbar_layout +}; + +static const aura_core_laptop_zone g733qr_keystone_zone = + { + "Keystone", + &g733qr_keystone_layout +}; + +static const aura_core_laptop_device g733qr_device = + { + "G733QR", + { + &g733qr_keyboard_zone, + &g733qr_lid_zone, + &g733qr_lightbar_zone, + &g733qr_keystone_zone, + nullptr, + nullptr + } +}; + +/*-------------------------------------------------------------*\ +| ASUS ROG Strix G18 (G814JV) | +| | +| Zone "Keyboard" | +| Matrix | +| 7 Rows, 16 Columns | +\*-------------------------------------------------------------*/ + +static const aura_core_laptop_zone g814rw_keyboard_zone = +{ + ZONE_EN_KEYBOARD, + &g814jv_keyboard_layout +}; + +static const aura_core_laptop_zone g814jv_lightbar_zone = +{ + "Lightbar", + &g814jv_lightbar_layout +}; + +static const aura_core_laptop_device g814jv_device = +{ + "G814JV", + { + &g814rw_keyboard_zone, + &g814jv_lightbar_zone, + nullptr, + nullptr, + nullptr, + nullptr + } +}; + +/*-------------------------------------------------------------------------*\ +| DEVICE MASTER LIST | +\*-------------------------------------------------------------------------*/ +const aura_core_laptop_device* aura_core_laptop_device_list_data[] = +{ +/*-----------------------------------------------------------------*\ +| KEYBOARDS | +\*-----------------------------------------------------------------*/ + &g533zm_device, + &g533zw_device, + &g614jz_device, + &g713rw_device, + &g733qr_device, + &g814jv_device, +}; + +const unsigned int AURA_CORE_LAPTOP_DEVICE_COUNT = (sizeof(aura_core_laptop_device_list_data) / sizeof(aura_core_laptop_device_list_data[ 0 ])); +const aura_core_laptop_device** aura_core_laptop_device_list = aura_core_laptop_device_list_data; diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopDevices.h b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopDevices.h new file mode 100644 index 0000000..9a6dfeb --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopDevices.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include "RGBController.h" +#include "RGBControllerKeyNames.h" +#include "KeyboardLayoutManager.h" + +#define AURA_CORE_LAPTOP_ZONES_MAX 6 + +enum aura_core_kb_layout +{ + AURA_CORE_LAPTOP_KB_LAYOUT_ANSI = 0x01, /* US ANSI Layout */ + AURA_CORE_LAPTOP_KB_LAYOUT_ISO = 0x02, /* EURO ISO Layout */ + AURA_CORE_LAPTOP_KB_LAYOUT_ABNT = 0x03, /* Brazilian Layout */ + AURA_CORE_LAPTOP_KB_LAYOUT_JIS = 0x04, /* Japanese Layout */ +}; + +typedef struct +{ + std::string name; + keyboard_keymap_overlay_values* layout_new; +} aura_core_laptop_zone; + +typedef struct +{ + uint8_t zone; + uint8_t row; + uint8_t col; + uint8_t index; + const char* name; +} aura_core_laptop_led; + +typedef struct +{ + std::string dmi_name; + const aura_core_laptop_zone* zones[AURA_CORE_LAPTOP_ZONES_MAX]; +} aura_core_laptop_device; + +/*-----------------------------------------------------*\ +| Aura Core Laptop Protocol Keyboards | +\*-----------------------------------------------------*/ +#define AURA_STRIX_SCAR_15_PID 0x19B6 + +/*-----------------------------------------------------*\ +| These constant values are defined in | +| AsusAuraCoreLaptopDevices.cpp | +\*-----------------------------------------------------*/ +extern const unsigned int AURA_CORE_LAPTOP_DEVICE_COUNT; +extern const aura_core_laptop_device** aura_core_laptop_device_list; diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/RGBController_AsusAuraCoreLaptop.cpp b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/RGBController_AsusAuraCoreLaptop.cpp new file mode 100644 index 0000000..50eb918 --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/RGBController_AsusAuraCoreLaptop.cpp @@ -0,0 +1,444 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraCoreLaptop.cpp | +| | +| RGBController for ASUS ROG Aura Core Laptop | +| | +| Chris M (Dr_No) 28 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusAuraCoreLaptop.h" + +/**------------------------------------------------------------------*\ + @name AsusAuraCoreLaptop + @category DEVICE_TYPE_KEYBOARD + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraCoreLaptopControllers + @comment Power profiles for this controller are set to `On` for all power + state and scan be adjusted in the JSON config file. + + For each zone available LEDs can be set as `On = true` or `Off = false` when + * Booting + * Awake (Normal Usage) + * Sleeping + * Shutdown / Power Off + + MatrixMaps can be found in ArmouryCrate (it needs a chance to download device data) + Default path is: + C:\ProgramData\ASUS\ROG Live Service\DeviceContent\\.csv + (Model name is the code like G814JV - can be found in the error in logs) +\*-------------------------------------------------------------------*/ + +RGBController_AsusAuraCoreLaptop::RGBController_AsusAuraCoreLaptop(AsusAuraCoreLaptopController *controller_ptr) +{ + controller = controller_ptr; + const aura_core_laptop_device* aura_dev = controller->GetDeviceData(); + + name = aura_dev->dmi_name; + vendor = "Asus"; + type = DEVICE_TYPE_LAPTOP; + description = controller->GetDeviceDescription(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ASUSAURACORELAPTOP_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + Direct.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Direct.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = ASUSAURACORELAPTOP_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + Static.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Static.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Static.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + Static.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ASUSAURACORELAPTOP_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(Breathing.colors_min); + Breathing.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + Breathing.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Breathing.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Breathing.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + Breathing.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = ASUSAURACORELAPTOP_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Flashing.colors_min = 1; + Flashing.colors_max = 1; + Flashing.colors.resize(Flashing.colors_max); + Flashing.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + Flashing.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Flashing.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Flashing.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + Flashing.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flashing.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(Flashing); + + mode Spectrum; + Spectrum.name = "Spectrum Cycle"; + Spectrum.value = ASUSAURACORELAPTOP_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Spectrum.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + Spectrum.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Spectrum.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Spectrum.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + Spectrum.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(Spectrum); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = ASUSAURACORELAPTOP_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + Rainbow.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + Rainbow.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Rainbow.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Rainbow.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + Rainbow.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(Rainbow); + + mode Starry; + Starry.name = "Starry Night"; + Starry.value = ASUSAURACORELAPTOP_MODE_STARRY_NIGHT; + Starry.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Starry.colors_min = 1; + Starry.colors_max = 2; + Starry.colors.resize(Starry.colors_min); + Starry.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + Starry.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Starry.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Starry.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + Starry.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + Starry.color_mode = MODE_COLORS_MODE_SPECIFIC; + Starry.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(Starry); + + mode Rain; + Rain.name = "Rain"; + Rain.value = ASUSAURACORELAPTOP_MODE_RAIN; + Rain.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rain.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + Rain.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Rain.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Rain.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + Rain.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + Rain.color_mode = MODE_COLORS_NONE; + Rain.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(Rain); + + mode ReactFade; + ReactFade.name = "Reactive - Fade"; + ReactFade.value = ASUSAURACORELAPTOP_MODE_REACT_FADE; + ReactFade.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + ReactFade.colors_min = 1; + ReactFade.colors_max = 1; + ReactFade.colors.resize(ReactFade.colors_max); + ReactFade.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + ReactFade.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + ReactFade.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + ReactFade.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + ReactFade.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + ReactFade.color_mode = MODE_COLORS_MODE_SPECIFIC; + ReactFade.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(ReactFade); + + mode ReactLaser; + ReactLaser.name = "Reactive - Laser"; + ReactLaser.value = ASUSAURACORELAPTOP_MODE_REACT_LASER; + ReactLaser.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + ReactLaser.colors_min = 1; + ReactLaser.colors_max = 1; + ReactLaser.colors.resize(ReactLaser.colors_max); + ReactLaser.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + ReactLaser.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + ReactLaser.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + ReactLaser.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + ReactLaser.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + ReactLaser.color_mode = MODE_COLORS_MODE_SPECIFIC; + ReactLaser.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(ReactLaser); + + mode ReactRipple; + ReactRipple.name = "Reactive - Ripple"; + ReactRipple.value = ASUSAURACORELAPTOP_MODE_REACT_RIPPLE; + ReactRipple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + ReactRipple.colors_min = 1; + ReactRipple.colors_max = 1; + ReactRipple.colors.resize(ReactRipple.colors_max); + ReactRipple.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + ReactRipple.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + ReactRipple.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + ReactRipple.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + ReactRipple.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + ReactRipple.color_mode = MODE_COLORS_MODE_SPECIFIC; + ReactRipple.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(ReactRipple); + + mode Comet; + Comet.name = "Comet"; + Comet.value = ASUSAURACORELAPTOP_MODE_COMET; + Comet.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Comet.colors_min = 1; + Comet.colors_max = 1; + Comet.colors.resize(Comet.colors_max); + Comet.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + Comet.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Comet.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Comet.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + Comet.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + Comet.color_mode = MODE_COLORS_MODE_SPECIFIC; + Comet.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(Comet); + + mode FlashNDash; + FlashNDash.name = "Flash N Dash"; + FlashNDash.value = ASUSAURACORELAPTOP_MODE_FLASHNDASH; + FlashNDash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + FlashNDash.colors_min = 1; + FlashNDash.colors_max = 1; + FlashNDash.colors.resize(FlashNDash.colors_max); + FlashNDash.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + FlashNDash.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + FlashNDash.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + FlashNDash.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + FlashNDash.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + FlashNDash.color_mode = MODE_COLORS_MODE_SPECIFIC; + FlashNDash.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(FlashNDash); + + mode Keystone; + Keystone.name = "Keystone"; + Keystone.value = ASUSAURACORELAPTOP_MODE_KEYSTONE; + Keystone.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Keystone.colors_min = 1; + Keystone.colors_max = 1; + Keystone.colors.resize(Keystone.colors_max); + Keystone.brightness_min = ASUSAURACORELAPTOP_BRIGHTNESS_MIN; + Keystone.brightness_max = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Keystone.brightness = ASUSAURACORELAPTOP_BRIGHTNESS_MAX; + Keystone.speed_min = ASUSAURACORELAPTOP_SPEED_SLOWEST; + Keystone.speed_max = ASUSAURACORELAPTOP_SPEED_FASTEST; + Keystone.color_mode = MODE_COLORS_MODE_SPECIFIC; + Keystone.speed = ASUSAURACORELAPTOP_SPEED_NORMAL; + modes.push_back(Keystone); + + mode Off; + Off.name = "Off"; + Off.value = ASUSAURACORELAPTOP_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + + SetMode(active_mode); +} + +RGBController_AsusAuraCoreLaptop::~RGBController_AsusAuraCoreLaptop() +{ + delete controller; +} + +void RGBController_AsusAuraCoreLaptop::SetupZones() +{ + std::string physical_size; + KEYBOARD_LAYOUT new_layout; + unsigned int max_led_value = 0; + + const aura_core_laptop_device* aura_dev = controller->GetDeviceData(); + unsigned int layout = controller->GetKeyboardLayout(); + + switch(layout) + { + case ASUSAURACORELAPTOP_LAYOUT_ISO: + new_layout = KEYBOARD_LAYOUT_ISO_QWERTY; + break; + + case ASUSAURACORELAPTOP_LAYOUT_ANSI: + default: + new_layout = KEYBOARD_LAYOUT_ANSI_QWERTY; + break; + } + LOG_DEBUG("[%s] layout set as %d", description.c_str(), new_layout); + + /*---------------------------------------------------------*\ + | Fill in zones from the device data | + \*---------------------------------------------------------*/ + for(size_t i = 0; i < AURA_CORE_LAPTOP_ZONES_MAX; i++) + { + LOG_DEBUG("[%s] setting up zone %d", description.c_str(), i); + + if(aura_dev->zones[i] == NULL) + { + break; + } + else + { + zone new_zone; + + new_zone.name = aura_dev->zones[i]->name; + + if(aura_dev->zones[i]->layout_new != NULL) + { + KeyboardLayoutManager new_kb(new_layout, + aura_dev->zones[i]->layout_new->base_size, + aura_dev->zones[i]->layout_new->key_values); + + if(aura_dev->zones[i]->layout_new->base_size != KEYBOARD_SIZE_EMPTY || + aura_dev->zones[i]->layout_new->edit_keys.size() > 0) + { + /*---------------------------------------------------------*\ + | Minor adjustments to keyboard layout | + \*---------------------------------------------------------*/ + keyboard_keymap_overlay_values* temp = aura_dev->zones[i]->layout_new; + new_kb.ChangeKeys(*temp); + + if(new_kb.GetRowCount() == 1 || new_kb.GetColumnCount() == 1) + { + if(new_kb.GetKeyCount() == 1) + { + new_zone.type = ZONE_TYPE_SINGLE; + } + else + { + new_zone.type = ZONE_TYPE_LINEAR; + } + } + else + { + new_zone.type = ZONE_TYPE_MATRIX; + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + + /*---------------------------------------------------------*\ + | Trusting the layout handed to the KLM is correct use the | + | row & column counts to set the matrix height & width | + \*---------------------------------------------------------*/ + new_map->height = new_kb.GetRowCount(); + new_map->width = new_kb.GetColumnCount(); + new_map->map = new unsigned int[new_map->height * new_map->width]; + + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT); + } + + /*---------------------------------------------------------*\ + | Create LEDs for the Matrix zone | + | Place keys in the layout to populate the matrix | + \*---------------------------------------------------------*/ + new_zone.leds_count = new_kb.GetKeyCount(); + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + max_led_value = std::max(max_led_value, new_led.value); + leds.push_back(new_led); + } + } + + /*---------------------------------------------------------*\ + | Add 1 the max_led_value to account for the 0th index | + \*---------------------------------------------------------*/ + max_led_value++; + } + + LOG_DEBUG("[%s] Creating a %s zone: %s with %d LEDs", name.c_str(), + ((new_zone.type == ZONE_TYPE_MATRIX) ? "matrix": "linear"), + new_zone.name.c_str(), new_zone.leds_count); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + zones.push_back(new_zone); + } + } + + SetupColors(); + + /*---------------------------------------------------------*\ + | Create a buffer map of pointers which contains the | + | layout order of colors the device expects. | + \*---------------------------------------------------------*/ + buffer_map.resize(max_led_value, &null_color); + + for(size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + buffer_map[leds[led_idx].value] = &colors[led_idx]; + } +} + +void RGBController_AsusAuraCoreLaptop::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AsusAuraCoreLaptop::DeviceUpdateLEDs() +{ + for(size_t i = 85; i < leds.size(); i++) + { + LOG_DEBUG("[%s] Setting %s @ LED index %d and buffer index %d to R: %02X G: %02X B: %02X", + name.c_str(), + leds[i].name.c_str(), + i, + leds[i].value, + RGBGetRValue(colors[i]), + RGBGetGValue(colors[i]), + RGBGetBValue(colors[i])); + } + + controller->SetLedsDirect(buffer_map); +} + +void RGBController_AsusAuraCoreLaptop::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLedsDirect(buffer_map); +} + +void RGBController_AsusAuraCoreLaptop::UpdateSingleLED(int /*led*/) +{ + controller->SetLedsDirect(buffer_map); +} + +void RGBController_AsusAuraCoreLaptop::DeviceUpdateMode() +{ + mode set_mode = modes[active_mode]; + + uint8_t random = (set_mode.color_mode == MODE_COLORS_RANDOM) ? 0xFF : 0; + RGBColor color1 = (set_mode.colors.size() > 0) ? set_mode.colors[0] : 0; + RGBColor color2 = (set_mode.colors.size() > 1) ? set_mode.colors[1] : 0; + + controller->SetMode(set_mode.value, set_mode.speed, set_mode.brightness, color1, color2, random, set_mode.direction ); +} diff --git a/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/RGBController_AsusAuraCoreLaptop.h b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/RGBController_AsusAuraCoreLaptop.h new file mode 100644 index 0000000..e8033a2 --- /dev/null +++ b/Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/RGBController_AsusAuraCoreLaptop.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraCoreLaptop.h | +| | +| RGBController for ASUS ROG Aura Core Laptop | +| | +| Chris M (Dr_No) 28 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "AsusAuraCoreLaptopController.h" + +class RGBController_AsusAuraCoreLaptop : public RGBController +{ +public: + RGBController_AsusAuraCoreLaptop(AsusAuraCoreLaptopController* controller_ptr); + ~RGBController_AsusAuraCoreLaptop(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RGBColor null_color = 0; + std::vector buffer_map; + + AsusAuraCoreLaptopController* controller; +}; diff --git a/Controllers/AsusAuraGPUController/AsusAuraGPUController.cpp b/Controllers/AsusAuraGPUController/AsusAuraGPUController.cpp new file mode 100644 index 0000000..563efd4 --- /dev/null +++ b/Controllers/AsusAuraGPUController/AsusAuraGPUController.cpp @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| AsusAuraGPUController.cpp | +| | +| Driver for ASUS Aura GPU | +| | +| Jan Rettig (Klapstuhl) 14 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusAuraGPUController.h" +#include "pci_ids.h" + +AuraGPUController::AuraGPUController(i2c_smbus_interface* bus, aura_gpu_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +AuraGPUController::~AuraGPUController() +{ + +} + +std::string AuraGPUController::GetDeviceName() +{ + return(name); +} + +std::string AuraGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return(return_string); +} + +unsigned char AuraGPUController::GetLEDRed() +{ + return(AuraGPURegisterRead(AURA_GPU_REG_RED)); +} + +unsigned char AuraGPUController::GetLEDGreen() +{ + return(AuraGPURegisterRead(AURA_GPU_REG_GREEN)); +} + +unsigned char AuraGPUController::GetLEDBlue() +{ + return(AuraGPURegisterRead(AURA_GPU_REG_BLUE)); +} + +void AuraGPUController::SetLEDColors(unsigned char red, unsigned char green, unsigned char blue) +{ + AuraGPURegisterWrite(AURA_GPU_REG_RED, red); + AuraGPURegisterWrite(AURA_GPU_REG_GREEN, green); + AuraGPURegisterWrite(AURA_GPU_REG_BLUE, blue); +} + +void AuraGPUController::SetMode(unsigned char mode) +{ + AuraGPURegisterWrite(AURA_GPU_REG_MODE, mode); +} + +unsigned char AuraGPUController::AuraGPURegisterRead(unsigned char reg) +{ + return(bus->i2c_smbus_read_byte_data(dev, reg)); +} + +void AuraGPUController::AuraGPURegisterWrite(unsigned char reg, unsigned char val) +{ + bus->i2c_smbus_write_byte_data(dev, reg, val); +} + +bool AuraGPUController::SaveOnlyApplies() +{ + switch (bus->pci_subsystem_device) + { + case ASUS_VEGA64_STRIX: + return false; + } + // Behavior on other GPU models is unknown and needs to be tested. + // Assume the safest option to prevent damaage from excessive writes. + return false; +} + +void AuraGPUController::Save() +{ + AuraGPURegisterWrite(AURA_GPU_REG_APPLY, AURA_GPU_APPLY_VAL); +} diff --git a/Controllers/AsusAuraGPUController/AsusAuraGPUController.h b/Controllers/AsusAuraGPUController/AsusAuraGPUController.h new file mode 100644 index 0000000..836e1d4 --- /dev/null +++ b/Controllers/AsusAuraGPUController/AsusAuraGPUController.h @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| AsusAuraGPUController.h | +| | +| Driver for ASUS Aura GPU | +| | +| Jan Rettig (Klapstuhl) 14 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char aura_gpu_dev_id; + +#define AURA_GPU_MAGIC_VAL 0x1589 /* This magic value is present in all Aura GPU controllers */ +#define AURA_GPU_APPLY_VAL 0x01 /* Value for Apply Changes Register */ + +enum +{ + AURA_GPU_REG_RED = 0x04, /* AURA GPU RED Register */ + AURA_GPU_REG_GREEN = 0x05, /* AURA GPU GREEN Register */ + AURA_GPU_REG_BLUE = 0x06, /* AURA GPU BLUE Register */ + AURA_GPU_REG_MODE = 0x07, /* AURA GPU Mode Selection Register */ + AURA_GPU_REG_SYNC = 0x0C, /* AURA GPU "Sync" Register */ + AURA_GPU_REG_APPLY = 0x0E, /* AURA GPU Save or Apply Register */ +}; + +enum +{ + AURA_GPU_MODE_OFF = 0, /* OFF mode (not a real Mode! Doesn't do anything!) */ + AURA_GPU_MODE_STATIC = 1, /* Static color mode */ + AURA_GPU_MODE_BREATHING = 2, /* Breathing effect mode */ + AURA_GPU_MODE_FLASHING = 3, /* Flashing effect mode */ + AURA_GPU_MODE_SPECTRUM_CYCLE = 4, /* Spectrum Cycle mode */ + AURA_GPU_MODE_DIRECT = 5, /* Direct mode (not a real Mode! Doesn't do anything!) */ + AURA_GPU_NUMBER_MODES +}; + +class AuraGPUController +{ +public: + AuraGPUController(i2c_smbus_interface* bus, aura_gpu_dev_id, std::string dev_name); + ~AuraGPUController(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + unsigned char GetLEDRed(); + unsigned char GetLEDGreen(); + unsigned char GetLEDBlue(); + void SetLEDColors(unsigned char red, unsigned char green, unsigned char blue); + void SetMode(unsigned char mode); + void Save(); + + unsigned char AuraGPURegisterRead(unsigned char reg); + void AuraGPURegisterWrite(unsigned char reg, unsigned char val); + + bool SaveOnlyApplies(); + bool direct = false; // Temporary solution to check if we are in "Direct" mode + +private: + i2c_smbus_interface * bus; + aura_gpu_dev_id dev; + std::string name; +}; diff --git a/Controllers/AsusAuraGPUController/AsusAuraGPUControllerDetect.cpp b/Controllers/AsusAuraGPUController/AsusAuraGPUControllerDetect.cpp new file mode 100644 index 0000000..5f01fd9 --- /dev/null +++ b/Controllers/AsusAuraGPUController/AsusAuraGPUControllerDetect.cpp @@ -0,0 +1,154 @@ +/*---------------------------------------------------------*\ +| AsusAuraGPUControllerDetect.cpp | +| | +| Detector for ASUS Aura GPU | +| | +| Jan Rettig (Klapstuhl) 14 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AsusAuraGPUController.h" +#include "LogManager.h" +#include "RGBController_AsusAuraGPU.h" +#include "i2c_amd_gpu.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +#define ASUSGPU_CONTROLLER_NAME "ASUS Aura GPU" + +/******************************************************************************************\ +* * +* TestForAuraGPUController * +* * +* Tests the given address to see if an Aura GPU controller exists there. * +* * +\******************************************************************************************/ + +bool TestForAsusAuraGPUController(i2c_smbus_interface* bus, unsigned char address) +{ + if(bus->pci_vendor == AMD_GPU_VEN && !is_amd_gpu_i2c_bus(bus)) + { + return false; + } + + bool pass = false; + + unsigned char aura_gpu_magic_high = bus->i2c_smbus_read_byte_data(address, 0x20); // High Byte of magic (0x15) + unsigned char aura_gpu_magic_low = bus->i2c_smbus_read_byte_data(address, 0x21); // Low Byte of magic (0x89) + + LOG_DEBUG("[%s] Test GPU expect: 0x1589 received: 0x%02X%02X", ASUSGPU_CONTROLLER_NAME, aura_gpu_magic_high, aura_gpu_magic_low); + + if((aura_gpu_magic_high << 8) + aura_gpu_magic_low == AURA_GPU_MAGIC_VAL) + { + pass = true; + } + + return(pass); + +} /* TestForAuraGPUController() */ + +/******************************************************************************************\ +* * +* DetectAuraGPUControllers * +* * +* Detect Aura GPU controllers on the enumerated I2C busses. * +* * +\******************************************************************************************/ + +void DetectAsusAuraGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(TestForAsusAuraGPUController(bus, i2c_addr)) + { + AuraGPUController* controller = new AuraGPUController(bus, i2c_addr, name); + RGBController_AuraGPU* rgb_controller = new RGBController_AuraGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectAsusAuraGPUControllers() */ + +/*-----------------------------------------*\ +| Nvidia GPUs | +\*-----------------------------------------*/ + +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1050 Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050_DEV, ASUS_SUB_VEN, ASUS_GTX1050_STRIX_O2G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1050 Ti Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1050TI_4G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1050 Ti Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1050TI_O4G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1050 Ti Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1050TI_O4G_GAMING_2, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1060 Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1060_6G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1060 Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1060, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1060 Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1060_865B, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1070 Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1070_8G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1070 Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1070_O8G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1070 OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1070_OC, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1070 Ti Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1070TI_8G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1070 A8G Ti Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1070TI_A8G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1080 Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, ASUS_SUB_VEN, ASUS_GTX1080_STRIX, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1080 A8G Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1080_A8G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1080 Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1080_O8G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1080 Gaming OC 11Gbps", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1080_O8G_11GBPS, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1080 Ti Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1080TI_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1080 Ti Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1080TI_11G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1080 Ti Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1080TI_O11G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1080 Ti Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1080TI_O11G_GAMING_A02, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG Poseidon GeForce GTX 1080 Ti", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, ASUS_SUB_VEN, ASUS_ROG_POSEIDON_GTX1080TI, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1650 SUPER A4G Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1650S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1650S_A4G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1650 SUPER Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1650S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1650S_OC, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1660 SUPER Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1660S_O6G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1660 SUPER Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_GTX1660S_6G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce GTX 1660 Ti Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660TI_DEV, ASUS_SUB_VEN, ASUS_ROG_GTX1660TI_OC, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060_6G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060_O6G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060_O6G_GAMING_86D2, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 EVO Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060_EVO_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 EVO V2 Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060_06G_EVO_V2_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 EVO Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU104_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060_O6G_EVO_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 SUPER Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060S_8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 SUPER A8G EVO Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060S_A8G_EVO_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 SUPER Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060S_O8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 SUPER Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060S_8G_GAMING_8702, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 SUPER A8G Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060S_A8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2060 SUPER A8G Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2060S_A8G_GAMING_86FD, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2070 A8G Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2070_A8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2070 Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2070_O8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2070 SUPER A8G Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2070S_A8G_GAMING_86FF, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2070 SUPER A8G Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2070S_A8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2070 SUPER A8G Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2070S_A8G_GAMING_8706, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2070 SUPER Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2070S_O8G_GAMING_8729, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2070 SUPER Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2070S_8G_GAMING_8707, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2070 SUPER Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2070S_O8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2080 Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2080_8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2080 Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2080_O8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2080 V2 Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2080_O8G_V2_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2080 SUPER A8G Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2080S_A8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2080 SUPER Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2080S_O8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2080 SUPER White OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2080S_O8G_WHITE, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2080 Ti Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2080TI_11G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2080 Ti Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2080TI_11G_GAMING_866C, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2080 Ti A11G Gaming", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2080TI_A11G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 2080 Ti Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX2080TI_O11G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 Ti Gaming OC", DetectAsusAuraGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3060TI_O8G_OC, 0x2A); + +/*-----------------------------------------*\ +| AMD GPUs | +\*-----------------------------------------*/ + +REGISTER_I2C_PCI_DETECTOR("ASUS AREZ STRIX Radeon RX Vega 56 Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_VEGA10_DEV, ASUS_SUB_VEN, ASUS_AREZ_STRIX_VEGA56_08G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon Vega 64", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_VEGA10_DEV, ASUS_SUB_VEN, ASUS_VEGA64_STRIX, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 470 Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_POLARIS_DEV, ASUS_SUB_VEN, ASUS_RX470_STRIX_O4G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 480 Gaming", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_POLARIS_DEV, ASUS_SUB_VEN, ASUS_RX480_STRIX_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 480 Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_POLARIS_DEV, ASUS_SUB_VEN, ASUS_RX480_STRIX_GAMING_OC, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 560 Gaming", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_POLARIS11, ASUS_SUB_VEN, ASUS_RX560_STRIX_4G_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 560 Gaming", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_POLARIS11, ASUS_SUB_VEN, ASUS_RX560_STRIX_4G_GAMING_04BE, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 570 Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_POLARIS_DEV, ASUS_SUB_VEN, ASUS_RX570_STRIX_O4G_GAMING_OC, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 570 Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_POLARIS_DEV, ASUS_SUB_VEN, ASUS_RX570_STRIX_O8G_GAMING_OC, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 580 Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_POLARIS_DEV, ASUS_SUB_VEN, ASUS_RX580_STRIX_GAMING_OC, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 580 Gaming TOP", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_POLARIS_DEV, ASUS_SUB_VEN, ASUS_RX580_STRIX_GAMING_TOP, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 590 Gaming", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_POLARIS_DEV, ASUS_SUB_VEN, ASUS_RX590_STRIX_GAMING, 0x29); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 5600 XT Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_NAVI10_DEV, ASUS_SUB_VEN, ASUS_RX5600XT_STRIX_O6G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 5700 Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_NAVI10_DEV, ASUS_SUB_VEN, ASUS_RX5700_STRIX_GAMING_OC, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 5700 XT Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_NAVI10_DEV, ASUS_SUB_VEN, ASUS_RX5700XT_STRIX_GAMING_OC, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 5700 XT Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_NAVI10_DEV, ASUS_SUB_VEN, ASUS_RX5700XT_STRIX_O8G_GAMING, 0x2A); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 5700 XT Gaming OC", DetectAsusAuraGPUControllers, AMD_GPU_VEN, AMD_NAVI10_DEV, ASUS_SUB_VEN, ASUS_RX5700XT_STRIX_O8G_GAMING_05C3, 0x2A); diff --git a/Controllers/AsusAuraGPUController/RGBController_AsusAuraGPU.cpp b/Controllers/AsusAuraGPUController/RGBController_AsusAuraGPU.cpp new file mode 100644 index 0000000..7358f7d --- /dev/null +++ b/Controllers/AsusAuraGPUController/RGBController_AsusAuraGPU.cpp @@ -0,0 +1,226 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraGPU.cpp | +| | +| RGBController for ASUS Aura GPU | +| | +| Jan Rettig (Klapstuhl) 14 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusAuraGPU.h" + +int RGBController_AuraGPU::GetDeviceMode() +{ + int dev_mode = controller->AuraGPURegisterRead(AURA_GPU_REG_MODE); + int color_mode = MODE_COLORS_PER_LED; + + if(dev_mode == AURA_GPU_MODE_STATIC) + { + if (controller->direct) + { + dev_mode = AURA_GPU_MODE_DIRECT; + } + } + + switch(dev_mode) + { + case AURA_GPU_MODE_OFF: + case AURA_GPU_MODE_SPECTRUM_CYCLE: + color_mode = MODE_COLORS_NONE; + break; + } + + for(unsigned int mode = 0; mode < modes.size(); mode++) + { + if(modes[mode].value == dev_mode) + { + active_mode = mode; + modes[mode].color_mode = color_mode; + } + } + + return(active_mode); +} + +/**------------------------------------------------------------------*\ + @name Asus Aura GPU + @category GPU + @type SMBus + @save :tools: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraGPUControllers + @comment On some models save command might function like apply. + Known models with correctly working save: ASUS Vega 64 Strix. + This may result in changes not applying until user clicks + "save to device". Contact OpenRGB developers if you have one + of the affected models. +\*-------------------------------------------------------------------*/ + +RGBController_AuraGPU::RGBController_AuraGPU(AuraGPUController * controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "ASUS"; + type = DEVICE_TYPE_GPU; + description = "ASUS Aura GPU Device"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = AURA_GPU_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + unsigned int save_flags = 0; + if(!controller->SaveOnlyApplies()) + { + save_flags |= MODE_FLAG_MANUAL_SAVE; + } + + mode Off; + Off.name = "Off"; + Off.value = AURA_GPU_MODE_OFF; + Off.flags = save_flags; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = AURA_GPU_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | save_flags; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AURA_GPU_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | save_flags; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = AURA_GPU_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR | save_flags; + Flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flashing); + + mode Spectrum_Cycle; + Spectrum_Cycle.name = "Spectrum Cycle"; + Spectrum_Cycle.value = AURA_GPU_MODE_SPECTRUM_CYCLE ; + Spectrum_Cycle.flags = save_flags; + Spectrum_Cycle.color_mode = MODE_COLORS_NONE; + modes.push_back(Spectrum_Cycle); + + SetupZones(); + + active_mode = GetDeviceMode(); +} + +RGBController_AuraGPU::~RGBController_AuraGPU() +{ + delete controller; +} + +void RGBController_AuraGPU::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone aura_gpu_zone; + aura_gpu_zone.name = "GPU"; + aura_gpu_zone.type = ZONE_TYPE_SINGLE; + aura_gpu_zone.leds_min = 1; + aura_gpu_zone.leds_max = 1; + aura_gpu_zone.leds_count = 1; + aura_gpu_zone.matrix_map = NULL; + zones.push_back(aura_gpu_zone); + + /*---------------------------------------------------------*\ + | Set up LED | + \*---------------------------------------------------------*/ + led aura_gpu_led; + aura_gpu_led.name = "GPU"; + leds.push_back(aura_gpu_led); + + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize color | + \*---------------------------------------------------------*/ + unsigned char red = controller->GetLEDRed(); + unsigned char grn = controller->GetLEDGreen(); + unsigned char blu = controller->GetLEDBlue(); + + colors[0] = ToRGBColor(red, grn, blu); +} + +void RGBController_AuraGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AuraGPU::DeviceUpdateLEDs() +{ + for(std::size_t led = 0; led < colors.size(); led++) + { + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + controller->SetLEDColors(red, grn, blu); + } + if (controller->SaveOnlyApplies() && GetMode() != 0) + { + controller->Save(); + } +} + +void RGBController_AuraGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraGPU::DeviceUpdateMode() +{ + int new_mode = modes[active_mode].value; + controller->direct = false; + + switch(new_mode) + { + // Set all LEDs to 0 and Mode to static as a workaround for the non existing Off Mode + case AURA_GPU_MODE_OFF: + controller->SetLEDColors(0, 0, 0); + new_mode = AURA_GPU_MODE_STATIC; + break; + + // Direct mode is done by switching to Static and not applying color changes + case AURA_GPU_MODE_DIRECT: + controller->direct = true; + new_mode = AURA_GPU_MODE_STATIC; + break; + } + + controller->SetMode(new_mode); + if (controller->SaveOnlyApplies()) + { + controller->Save(); + } +} + +void RGBController_AuraGPU::DeviceSaveMode() +{ + controller->Save(); +} diff --git a/Controllers/AsusAuraGPUController/RGBController_AsusAuraGPU.h b/Controllers/AsusAuraGPUController/RGBController_AsusAuraGPU.h new file mode 100644 index 0000000..bb4c4b9 --- /dev/null +++ b/Controllers/AsusAuraGPUController/RGBController_AsusAuraGPU.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraGPU.h | +| | +| RGBController for ASUS Aura GPU | +| | +| Jan Rettig (Klapstuhl) 14 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraGPUController.h" + +class RGBController_AuraGPU : public RGBController +{ +public: + RGBController_AuraGPU(AuraGPUController* controller_ptr); + ~RGBController_AuraGPU(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + AuraGPUController* controller; + + int GetDeviceMode(); +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/AsusAuraHeadsetStandController.cpp b/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/AsusAuraHeadsetStandController.cpp new file mode 100644 index 0000000..2ccf613 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/AsusAuraHeadsetStandController.cpp @@ -0,0 +1,157 @@ +/*---------------------------------------------------------*\ +| AsusAuraHeadsetStandController.cpp | +| | +| Driver for ASUS Aura headset stand | +| | +| Mola19 06 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusAuraHeadsetStandController.h" +#include "StringUtils.h" + +AuraHeadsetStandController::AuraHeadsetStandController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +AuraHeadsetStandController::~AuraHeadsetStandController() +{ + hid_close(dev); +} + +std::string AuraHeadsetStandController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AuraHeadsetStandController::GetName() +{ + return(name); +} + +std::string AuraHeadsetStandController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string AuraHeadsetStandController::GetVersion() +{ + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x12; + usb_buf[0x02] = 0x00; + + hid_write(dev, usb_buf, 65); + + unsigned char usb_buf_out[65]; + hid_read(dev, usb_buf_out, 65); + + char version[5]; + snprintf(version, 5, "%04X", (usb_buf_out[6] << 8) | usb_buf_out[7]); + return std::string(version); +} + +void AuraHeadsetStandController::UpdateLeds + ( + std::vector colors + ) +{ + unsigned char usb_buf_0[365]; + + memset(usb_buf_0, 0x00, sizeof(usb_buf_0)); + + usb_buf_0[0x00] = 0x00; + usb_buf_0[0x01] = 0xC0; + usb_buf_0[0x02] = 0x81; + usb_buf_0[0x03] = 0x00; + usb_buf_0[0x04] = 0x00; + + for(unsigned int i = 0; i < 60; i += 4) + { + usb_buf_0[5 + i] = 0x00; + usb_buf_0[6 + i] = RGBGetRValue(colors[i / 4]); + usb_buf_0[7 + i] = RGBGetGValue(colors[i / 4]); + usb_buf_0[8 + i] = RGBGetBValue(colors[i / 4]); + } + + hid_write(dev, usb_buf_0, 65); + + unsigned char usb_buf_1[65]; + + memset(usb_buf_1, 0x00, sizeof(usb_buf_1)); + + usb_buf_1[0x00] = 0x00; + usb_buf_1[0x01] = 0xC0; + usb_buf_1[0x02] = 0x81; + usb_buf_1[0x03] = 0x01; + usb_buf_1[0x04] = 0x00; + + for(unsigned int i = 0; i < 12; i += 4) + { + usb_buf_1[5 + i] = 0x00; + usb_buf_1[6 + i] = RGBGetRValue(colors[(i / 4) + 15]); + usb_buf_1[7 + i] = RGBGetGValue(colors[(i / 4) + 15]); + usb_buf_1[8 + i] = RGBGetBValue(colors[(i / 4) + 15]); + } + + hid_write(dev, usb_buf_1, 65); +} + +void AuraHeadsetStandController::UpdateDevice + ( + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu, + unsigned char speed, + unsigned char brightness + ) +{ + unsigned char usb_buf[65]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x28; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = mode; + usb_buf[0x06] = speed; + usb_buf[0x07] = brightness; + usb_buf[0x08] = 0x00; + usb_buf[0x09] = 0x00; + usb_buf[0x0a] = red; + usb_buf[0x0b] = grn; + usb_buf[0x0c] = blu; + hid_write(dev, usb_buf, 65); + +} + +void AuraHeadsetStandController::SaveMode() +{ + unsigned char usb_save_buf[65]; + + memset(usb_save_buf, 0x00, sizeof(usb_save_buf)); + + usb_save_buf[0x00] = 0x00; + usb_save_buf[0x01] = 0x50; + usb_save_buf[0x02] = 0x55; + + hid_write(dev, usb_save_buf, 65); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/AsusAuraHeadsetStandController.h b/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/AsusAuraHeadsetStandController.h new file mode 100644 index 0000000..fcefcf5 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/AsusAuraHeadsetStandController.h @@ -0,0 +1,65 @@ +/*---------------------------------------------------------*\ +| AsusAuraHeadsetStandController.h | +| | +| Driver for ASUS Aura headset stand | +| | +| Mola19 06 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +enum +{ + AURA_HEADSET_STAND_ZONE_UNDERGLOW = 0, + AURA_HEADSET_STAND_ZONE_LOGO = 1 +}; + +enum +{ + AURA_HEADSET_STAND_MODE_DIRECT = 0, + AURA_HEADSET_STAND_MODE_STATIC = 1, + AURA_HEADSET_STAND_MODE_BREATHING = 2, + AURA_HEADSET_STAND_MODE_STROBING = 3, + AURA_HEADSET_STAND_MODE_COLOR_CYCLE = 4, + AURA_HEADSET_STAND_MODE_RAINBOW = 5 +}; + +class AuraHeadsetStandController +{ +public: + AuraHeadsetStandController(hid_device* dev_handle, const char* path, std::string dev_name); + virtual ~AuraHeadsetStandController(); + + std::string GetDeviceLocation(); + std::string GetName(); + std::string GetSerialString(); + std::string GetVersion(); + + void UpdateLeds + ( + std::vector colors + ); + + void UpdateDevice + ( + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu, + unsigned char speed, + unsigned char brightness + ); + + void SaveMode(); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/RGBController_AsusAuraHeadsetStand.cpp b/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/RGBController_AsusAuraHeadsetStand.cpp new file mode 100644 index 0000000..fdca75f --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/RGBController_AsusAuraHeadsetStand.cpp @@ -0,0 +1,211 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraHeadsetStand.cpp | +| | +| RGBController for ASUS Aura headset stand | +| | +| Mola19 06 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusAuraHeadsetStand.h" + +/**------------------------------------------------------------------*\ + @name Asus Aura Headset Stand + @category HeadsetStand + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraUSBHeadsetStand + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AuraHeadsetStand::RGBController_AuraHeadsetStand(AuraHeadsetStandController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "ASUS"; + type = DEVICE_TYPE_HEADSET_STAND; + description = "ASUS Aura Headset Stand Device"; + version = controller->GetVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = AURA_HEADSET_STAND_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = AURA_HEADSET_STAND_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.brightness_min = AURA_HEADSETSTAND_BRIGHTNESS_MIN; + Static.brightness_max = AURA_HEADSETSTAND_BRIGHTNESS_MAX; + Static.brightness = AURA_HEADSETSTAND_BRIGHTNESS_DEFAULT; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AURA_HEADSET_STAND_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed_min = AURA_HEADSETSTAND_SPEED_MIN; + Breathing.speed_max = AURA_HEADSETSTAND_SPEED_MAX; + Breathing.speed = AURA_HEADSETSTAND_SPEED_DEFAULT; + Breathing.brightness_min = AURA_HEADSETSTAND_BRIGHTNESS_MIN; + Breathing.brightness_max = AURA_HEADSETSTAND_BRIGHTNESS_MAX; + Breathing.brightness = AURA_HEADSETSTAND_BRIGHTNESS_DEFAULT; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Strobing; + Strobing.name = "Flashing"; + Strobing.value = AURA_HEADSET_STAND_MODE_STROBING; + Strobing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Strobing.brightness_min = AURA_HEADSETSTAND_BRIGHTNESS_MIN; + Strobing.brightness_max = AURA_HEADSETSTAND_BRIGHTNESS_MAX; + Strobing.brightness = AURA_HEADSETSTAND_BRIGHTNESS_DEFAULT; + Strobing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Strobing.colors_min = 1; + Strobing.colors_max = 1; + Strobing.colors.resize(1); + modes.push_back(Strobing); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = AURA_HEADSET_STAND_MODE_COLOR_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + SpectrumCycle.speed_min = AURA_HEADSETSTAND_SPEED_MIN; + SpectrumCycle.speed_max = AURA_HEADSETSTAND_SPEED_MAX; + SpectrumCycle.speed = AURA_HEADSETSTAND_SPEED_DEFAULT; + SpectrumCycle.brightness_min = AURA_HEADSETSTAND_BRIGHTNESS_MIN; + SpectrumCycle.brightness_max = AURA_HEADSETSTAND_BRIGHTNESS_MAX; + SpectrumCycle.brightness = AURA_HEADSETSTAND_BRIGHTNESS_DEFAULT; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = AURA_HEADSET_STAND_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Rainbow.speed_min = AURA_HEADSETSTAND_SPEED_MIN; + Rainbow.speed_max = AURA_HEADSETSTAND_SPEED_MAX; + Rainbow.speed = AURA_HEADSETSTAND_SPEED_DEFAULT; + Rainbow.brightness_min = AURA_HEADSETSTAND_BRIGHTNESS_MIN; + Rainbow.brightness_max = AURA_HEADSETSTAND_BRIGHTNESS_MAX; + Rainbow.brightness = AURA_HEADSETSTAND_BRIGHTNESS_DEFAULT; + modes.push_back(Rainbow); + SetupZones(); +} + +RGBController_AuraHeadsetStand::~RGBController_AuraHeadsetStand() +{ + delete controller; +} + +void RGBController_AuraHeadsetStand::SetupZones() +{ + zone underglow_zone; + + underglow_zone.name = "Underglow"; + underglow_zone.type = ZONE_TYPE_LINEAR; + underglow_zone.leds_min = 17; + underglow_zone.leds_max = 17; + underglow_zone.leds_count = 17; + underglow_zone.matrix_map = NULL; + + zones.push_back(underglow_zone); + + for(unsigned int i = 0; i < 17; i++) + { + led underglow_led; + + underglow_led.name = "Underglow LED " + std::to_string(i); + + leds.push_back(underglow_led); + } + + zone logo_zone; + + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + + zones.push_back(logo_zone); + + led logo_led; + + logo_led.name = "Logo LED"; + + leds.push_back(logo_led); + + SetupColors(); +} + +void RGBController_AuraHeadsetStand::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AuraHeadsetStand::DeviceUpdateLEDs() +{ + controller->UpdateLeds(std::vector(colors)); +} + +void RGBController_AuraHeadsetStand::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraHeadsetStand::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraHeadsetStand::DeviceUpdateMode() +{ + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + switch(modes[active_mode].value) + { + case 0: + controller->UpdateLeds(std::vector(colors)); + break; + case 1: + case 2: + case 3: + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + controller->UpdateDevice(modes[active_mode].value, red, grn, blu, modes[active_mode].speed, modes[active_mode].brightness); + break; + case 4: + case 5: + controller->UpdateDevice(modes[active_mode].value, red, grn, blu, modes[active_mode].speed, modes[active_mode].brightness); + break; + } +} + +void RGBController_AuraHeadsetStand::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->SaveMode(); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/RGBController_AsusAuraHeadsetStand.h b/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/RGBController_AsusAuraHeadsetStand.h new file mode 100644 index 0000000..cd0c0e2 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/RGBController_AsusAuraHeadsetStand.h @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraHeadsetStand.h | +| | +| RGBController for ASUS Aura headset stand | +| | +| Mola19 06 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraHeadsetStandController.h" + +enum +{ + AURA_HEADSETSTAND_BRIGHTNESS_MIN = 0, + AURA_HEADSETSTAND_BRIGHTNESS_MAX = 4, + AURA_HEADSETSTAND_BRIGHTNESS_DEFAULT = 4, + AURA_HEADSETSTAND_SPEED_MIN = 0, + AURA_HEADSETSTAND_SPEED_MAX = 255, + AURA_HEADSETSTAND_SPEED_DEFAULT = 127, +}; + +class RGBController_AuraHeadsetStand : public RGBController +{ +public: + RGBController_AuraHeadsetStand(AuraHeadsetStandController* controller_ptr); + ~RGBController_AuraHeadsetStand(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + AuraHeadsetStandController* controller; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/AsusAuraKeyboardController.cpp b/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/AsusAuraKeyboardController.cpp new file mode 100644 index 0000000..37e887b --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/AsusAuraKeyboardController.cpp @@ -0,0 +1,120 @@ +/*---------------------------------------------------------*\ +| AsusAuraKeyboardController.cpp | +| | +| Driver for ASUS Aura keyboard | +| | +| Adam Honse (CalcProgrammer1) 19 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusAuraKeyboardController.h" +#include "StringUtils.h" + +AuraKeyboardController::AuraKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +AuraKeyboardController::~AuraKeyboardController() +{ + hid_close(dev); +} + +std::string AuraKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AuraKeyboardController::GetNameString() +{ + return(name); +} + +std::string AuraKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void AuraKeyboardController::SendDirect + ( + unsigned char frame_count, + unsigned char * frame_data + ) +{ + unsigned char usb_buf[65]; + unsigned int packet_count = frame_count / 15 + (((frame_count % 15) == 0) ? 0 : 1); + unsigned int total_frame_idx = 0; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0xC0; + usb_buf[0x02] = 0x81; + usb_buf[0x03] = packet_count * 15; + usb_buf[0x04] = 0x00; + + /*-----------------------------------------------------*\ + | Copy in frame data and send packets until all data | + | has been sent | + \*-----------------------------------------------------*/ + for(unsigned int packet_idx = 0; packet_idx < packet_count; packet_idx++) + { + unsigned int packet_data_size = 15 * 4; + + if((frame_count - total_frame_idx) < 15) + { + packet_data_size = (frame_count - total_frame_idx) * 4; + memset(&usb_buf[0x05 + packet_data_size], 0xFF, (15*4) - packet_data_size); + } + + memcpy(&usb_buf[0x05], &frame_data[packet_idx * 15 * 4], packet_data_size); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Decrement remaining frame count | + \*-----------------------------------------------------*/ + usb_buf[0x03] -= 0x0F; + total_frame_idx += 0x0F; + } + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0xC0; + usb_buf[0x02] = 0x81; + usb_buf[0x03] = 0x90; + + /*-----------------------------------------------------*\ + | Read to apply | + \*-----------------------------------------------------*/ + hid_read(dev,usb_buf, 65); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/AsusAuraKeyboardController.h b/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/AsusAuraKeyboardController.h new file mode 100644 index 0000000..5853136 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/AsusAuraKeyboardController.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| AsusAuraKeyboardController.h | +| | +| Driver for ASUS Aura keyboard | +| | +| Adam Honse (CalcProgrammer1) 19 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +class AuraKeyboardController +{ +public: + AuraKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name); + virtual ~AuraKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendDirect + ( + unsigned char frame_count, + unsigned char * frame_data + ); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/RGBController_AsusAuraKeyboard.cpp b/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/RGBController_AsusAuraKeyboard.cpp new file mode 100644 index 0000000..41f1c6f --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/RGBController_AsusAuraKeyboard.cpp @@ -0,0 +1,535 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraKeyboard.cpp | +| | +| RGBController for ASUS Aura keyboard | +| | +| Adam Honse (CalcProgrammer1) 19 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_AsusAuraKeyboard.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int flare_matrix_map[6][22] = + { { 0, NA, 13, 18, 23, 28, 38, 43, 49, 54, 60, 65, 69, 70, NA, 76, 80, 85, NA, NA, NA, NA }, + { 1, 8, 14, 19, 24, 29, 34, 39, 44, 50, 55, 61, 66, 71, NA, 77, 81, 86, 89, 94, 98, 103 }, + { 2, NA, 9, 15, 20, 25, 30, 35, 40, 45, 51, 56, 62, 67, 72, 78, 82, 87, 90, 95, 99, 104 }, + { 3, NA, 10, 16, 21, 26, 31, 36, 41, 46, 52, 57, 63, 68, 73, NA, NA, NA, 91, 96, 100, NA }, + { 4, 6, 11, 17, 22, 27, 32, 37, 42, 47, 53, 58, 74, NA, NA, NA, 83, NA, 92, 97, 101, 105 }, + { 5, 7, 12, NA, NA, NA, NA, 33, NA, 48, NA, 59, 64, 75, NA, 79, 84, 88, 93, NA, 102, NA } }; + +static unsigned int scope_matrix_map[6][22] = + { { 0, NA, 13, 18, 23, 28, 38, 43, 49, 54, 60, 65, 69, 70, NA, 76, 80, 85, NA, NA, NA, NA }, + { 1, 8, 14, 19, 24, 29, 34, 39, 44, 50, 55, 61, 66, 71, NA, 77, 81, 86, 89, 94, 98, 103 }, + { 2, NA, 9, 15, 20, 25, 30, 35, 40, 45, 51, 56, 62, 67, 72, 78, 82, 87, 90, 95, 99, 104 }, + { 3, NA, 10, 16, 21, 26, 31, 36, 41, 46, 52, 57, 63, 68, 73, NA, NA, NA, 91, 96, 100, NA }, + { 4, 6, 11, 17, 22, 27, 32, 37, 42, 47, 53, 58, 74, NA, NA, NA, 83, NA, 92, 97, 101, 105 }, + { 5, NA, 7, 12, NA, NA, NA, 33, NA, 48, NA, 59, 64, 75, NA, 79, 84, 88, 93, NA, 102, NA } }; + +static unsigned int scope_tkl_matrix_map[6][18] = + { { 0, NA, 13, 18, 23, 28, 38, 43, 49, 54, 60, 65, 69, 70, NA, NA, NA, NA }, + { 1, 8, 14, 19, 24, 29, 34, 39, 44, 50, 55, 61, 66, 71, NA, 76, 79, 83 }, + { 2, NA, 9, 15, 20, 25, 30, 35, 40, 45, 51, 56, 62, 67, 72, 77, 80, 84 }, + { 3, NA, 10, 16, 21, 26, 31, 36, 41, 46, 52, 57, 63, 68, 73, NA, NA, NA }, + { 4, 6, 11, 17, 22, 27, 32, 37, 42, 47, 53, 58, 74, NA, NA, NA, 81, NA }, + { 5, NA, 7, 12, NA, NA, NA, 33, NA, 48, NA, 59, 64, 75, NA, 78, 82, 85 } }; + +static unsigned int falchion_matrix_map[5][16] = + { { 0, 5, 9, 14, 18, 22, 26, 31, 35, 39, 44, 49, 54, 58, NA, 63 }, + { 1, NA, 6, 10, 15, 19, 23, 27, 32, 36, 40, 45, 50, 55, 59, 64 }, + { 2, NA, 7, 11, 16, 20, 24, 28, 33, 37, 41, 46, 51, 60, NA, 65 }, + { 3, NA, 12, 17, 21, 25, 29, 34, 38, 42, 47, 52, 56, NA, 61, 66 }, + { 4, 8, 13, NA, NA, NA, 30, NA, NA, NA, 43, 48, 53, 57, 62, 67 } }; + +static const std::vector default_led_names = +{ + /* Key Label Index */ + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_1, 0x11 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + { KEY_EN_6, 0x39 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + { KEY_EN_RIGHT_ALT, 0x4D }, + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_POUND, 0x6B }, + { KEY_EN_F11, 0x70 }, + { KEY_EN_F12, 0x78 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_ANSI_BACK_SLASH, 0x7A }, + { KEY_EN_ANSI_ENTER, 0x7B }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, +}; + +static const std::vector default_tkl_led_names = +{ + /* Key Label Index */ + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_1, 0x11 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + { KEY_EN_6, 0x39 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + { KEY_EN_RIGHT_ALT, 0x4D }, + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_POUND, 0x6B }, + { KEY_EN_F11, 0x70 }, + { KEY_EN_F12, 0x78 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_ANSI_BACK_SLASH, 0x7A }, + { KEY_EN_ANSI_ENTER, 0x7B }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + { "Logo 1", 0x80 }, + { "Logo 2", 0x90 }, + { "Underglow 1", 0x06 }, + { "Underglow 2", 0x0E }, + { "Underglow 3", 0x16 }, + { "Underglow 4", 0x1E }, + { "Underglow 5", 0x26 }, + { "Underglow 6", 0x2E }, + { "Underglow 7", 0x36 }, + { "Underglow 8", 0x3E }, + { "Underglow 9", 0x46 }, + { "Underglow 10", 0x4E }, + { "Underglow 11", 0x56 }, + { "Underglow 12", 0x5E }, + { "Underglow 13", 0x66 }, + { "Underglow 14", 0x6E }, + { "Underglow 15", 0x76 }, + { "Underglow 16", 0x7E }, + { "Underglow 17", 0x86 }, + { "Underglow 18", 0x8E }, + { "Underglow 19", 0x96 }, + { "Underglow 20", 0x9E }, + { "Underglow 21", 0xA6 }, + { "Underglow 22", 0xAE }, + { "Underglow 23", 0xB6 }, + { "Underglow 24", 0xBE }, + { "Underglow 25", 0xC6 }, + { "Underglow 26", 0xCE }, +}; + +static const std::vector default_65pct_led_names = +{ + /* Key Label Index */ + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_TAB, 0x01 }, + { KEY_EN_CAPS_LOCK, 0x02 }, + { KEY_EN_LEFT_SHIFT, 0x03 }, + { KEY_EN_LEFT_CONTROL, 0x04 }, + { KEY_EN_1, 0x08 }, + { KEY_EN_Q, 0x09 }, + { KEY_EN_A, 0x0A }, + { KEY_EN_LEFT_WINDOWS, 0x0C }, + { KEY_EN_2, 0x10 }, + { KEY_EN_W, 0x11 }, + { KEY_EN_S, 0x12 }, + { KEY_EN_Z, 0x13 }, + { KEY_EN_LEFT_ALT, 0x14 }, + { KEY_EN_3, 0x18 }, + { KEY_EN_E, 0x19 }, + { KEY_EN_D, 0x1A }, + { KEY_EN_X, 0x1B }, + { KEY_EN_4, 0x20 }, + { KEY_EN_R, 0x21 }, + { KEY_EN_F, 0x22 }, + { KEY_EN_C, 0x23 }, + { KEY_EN_5, 0x28 }, + { KEY_EN_T, 0x29 }, + { KEY_EN_G, 0x2A }, + { KEY_EN_V, 0x2B }, + { KEY_EN_6, 0x30 }, + { KEY_EN_Y, 0x31 }, + { KEY_EN_H, 0x32 }, + { KEY_EN_B, 0x33 }, + { KEY_EN_SPACE, 0x34 }, + { KEY_EN_7, 0x38 }, + { KEY_EN_U, 0x39 }, + { KEY_EN_J, 0x3A }, + { KEY_EN_N, 0x3B }, + { KEY_EN_8, 0x40 }, + { KEY_EN_I, 0x41 }, + { KEY_EN_K, 0x42 }, + { KEY_EN_M, 0x43 }, + { KEY_EN_9, 0x48 }, + { KEY_EN_O, 0x49 }, + { KEY_EN_L, 0x4A }, + { KEY_EN_COMMA, 0x4B }, + { KEY_EN_RIGHT_ALT, 0x4C }, + { KEY_EN_0, 0x50 }, + { KEY_EN_P, 0x51 }, + { KEY_EN_SEMICOLON, 0x52 }, + { KEY_EN_PERIOD, 0x53 }, + { KEY_EN_RIGHT_FUNCTION, 0x54 }, + { KEY_EN_MINUS, 0x58 }, + { KEY_EN_LEFT_BRACKET, 0x59 }, + { KEY_EN_QUOTE, 0x5A }, + { KEY_EN_FORWARD_SLASH, 0x5B }, + { KEY_EN_RIGHT_CONTROL, 0x5C }, + { KEY_EN_EQUALS, 0x60 }, + { KEY_EN_RIGHT_BRACKET, 0x61 }, + { KEY_EN_RIGHT_SHIFT, 0x63 }, + { KEY_EN_LEFT_ARROW, 0x64 }, + { KEY_EN_BACKSPACE, 0x68 }, + { KEY_EN_ANSI_BACK_SLASH, 0x69 }, + { KEY_EN_ANSI_ENTER, 0x6A }, + { KEY_EN_UP_ARROW, 0x6B }, + { KEY_EN_DOWN_ARROW, 0x6C }, + { KEY_EN_INSERT, 0x70 }, + { KEY_EN_DELETE, 0x71 }, + { KEY_EN_PAGE_UP, 0x72 }, + { KEY_EN_PAGE_DOWN, 0x73 }, + { KEY_EN_RIGHT_ARROW, 0x74 }, +}; + +/**------------------------------------------------------------------*\ + @name Asus Aura Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectAsusAuraUSBKeyboards + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AuraKeyboard::RGBController_AuraKeyboard(AuraKeyboardController* controller_ptr, AuraKeyboardMappingLayoutType keyboard_layout) +{ + controller = controller_ptr; + layout = keyboard_layout; + + name = controller->GetNameString(); + vendor = "ASUS"; + type = DEVICE_TYPE_KEYBOARD; + description = "ASUS Aura Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_AuraKeyboard::~RGBController_AuraKeyboard() +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].type == ZONE_TYPE_MATRIX) + { + delete zones[zone_idx].matrix_map; + } + } + + delete controller; +} + +void RGBController_AuraKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + std::vector led_zones; + std::vector led_names; + + switch(layout) + { + /*-----------------------------------------------------*\ + | On the ROG Scope keyboards Ctrl key is double sized, | + | so there is a layout shift | + \*-----------------------------------------------------*/ + case SCOPE_LAYOUT: + led_names = default_led_names; + led_zones.push_back({ZONE_EN_KEYBOARD, ZONE_TYPE_MATRIX, 106, new matrix_map_type{6, 22, (unsigned int *)&scope_matrix_map}}); + + led_names.insert(led_names.begin() + 7, {KEY_EN_LEFT_WINDOWS, 0x15}); + led_names.insert(led_names.begin() + 12, {KEY_EN_LEFT_ALT, 0x1D}); + break; + + case SCOPE_RX_LAYOUT: + led_names = default_led_names; + led_zones.push_back({ZONE_EN_KEYBOARD, ZONE_TYPE_MATRIX, 106, new matrix_map_type{6, 22, (unsigned int *)&scope_matrix_map}}); + led_zones.push_back({"Logo", ZONE_TYPE_SINGLE, 1, NULL}); + + led_names.insert(led_names.begin() + 7, {KEY_EN_LEFT_WINDOWS, 0x15}); + led_names.insert(led_names.begin() + 12, {KEY_EN_LEFT_ALT, 0x1D}); + led_names.push_back({ "Logo", 0xB0}); + break; + + case SCOPE_TKL_LAYOUT: + led_names = default_tkl_led_names; + led_zones.push_back({ZONE_EN_KEYBOARD, ZONE_TYPE_MATRIX, 86, new matrix_map_type{6, 18, (unsigned int *)&scope_tkl_matrix_map}}); + led_zones.push_back({"Logo", ZONE_TYPE_LINEAR, 2, NULL}); + led_zones.push_back({"Underglow", ZONE_TYPE_LINEAR, 26, NULL}); + + led_names.insert(led_names.begin() + 7, {KEY_EN_LEFT_WINDOWS, 0x15}); + led_names.insert(led_names.begin() + 12, {KEY_EN_LEFT_ALT, 0x1D}); + break; + + case FLARE_LAYOUT: + led_names = default_led_names; + led_zones.push_back({ZONE_EN_KEYBOARD, ZONE_TYPE_MATRIX, 106, new matrix_map_type{6, 22, (unsigned int *)&flare_matrix_map}}); + led_zones.push_back({"Logo", ZONE_TYPE_SINGLE, 1, NULL}); + led_zones.push_back({"Underglow", ZONE_TYPE_SINGLE, 2, NULL}); + + led_names.insert(led_names.begin() + 7, {KEY_EN_LEFT_WINDOWS, 0x0D}); + led_names.insert(led_names.begin() + 12, {KEY_EN_LEFT_ALT, 0x15}); + + led_names.push_back({ "Logo", 0xB8}); + led_names.push_back({ "Left Underglow", 0xB9}); + led_names.push_back({ "Right Underglow", 0xBA}); + break; + + case FALCHION_LAYOUT: + led_names = default_65pct_led_names; + led_zones.push_back({ZONE_EN_KEYBOARD, ZONE_TYPE_MATRIX, 68, new matrix_map_type{5, 16, (unsigned int *)&falchion_matrix_map}}); + break; + } + + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < led_zones.size(); zone_idx++) + { + zone new_zone; + new_zone.name = led_zones[zone_idx].name; + new_zone.type = led_zones[zone_idx].type; + new_zone.leds_min = led_zones[zone_idx].size; + new_zone.leds_max = led_zones[zone_idx].size; + new_zone.leds_count = led_zones[zone_idx].size; + + if(led_zones[zone_idx].type == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = led_zones[zone_idx].matrix; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += led_zones[zone_idx].size; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx].name; + new_led.value = led_names[led_idx].idx; + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_AuraKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AuraKeyboard::DeviceUpdateLEDs() +{ + std::vector frame_buf; + + /*---------------------------------------------------------*\ + | Resize the frame buffer, 4 bytes per LED | + \*---------------------------------------------------------*/ + frame_buf.resize(leds.size() * 4); + + /*---------------------------------------------------------*\ + | TODO: Send packets with multiple LED frames | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + frame_buf[(led_idx * 4) + 0] = leds[led_idx].value; + frame_buf[(led_idx * 4) + 1] = RGBGetRValue(colors[led_idx]); + frame_buf[(led_idx * 4) + 2] = RGBGetGValue(colors[led_idx]); + frame_buf[(led_idx * 4) + 3] = RGBGetBValue(colors[led_idx]); + } + + controller->SendDirect((unsigned char)leds.size(), frame_buf.data()); +} + +void RGBController_AuraKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraKeyboard::DeviceUpdateMode() +{ + +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/RGBController_AsusAuraKeyboard.h b/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/RGBController_AsusAuraKeyboard.h new file mode 100644 index 0000000..136a01d --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraKeyboardController/RGBController_AsusAuraKeyboard.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraKeyboard.h | +| | +| RGBController for ASUS Aura keyboard | +| | +| Adam Honse (CalcProgrammer1) 19 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraKeyboardController.h" + +enum AuraKeyboardMappingLayoutType +{ + FLARE_LAYOUT, + SCOPE_LAYOUT, + SCOPE_RX_LAYOUT, + SCOPE_TKL_LAYOUT, + FALCHION_LAYOUT, +}; + +typedef struct +{ + const char* name; + unsigned char idx; +} aura_keyboard_led; + +typedef struct +{ + const char* name; + const zone_type type; + const unsigned int size; + matrix_map_type* matrix; +} led_zone; + +class RGBController_AuraKeyboard : public RGBController +{ +public: + RGBController_AuraKeyboard(AuraKeyboardController* controller_ptr, AuraKeyboardMappingLayoutType keyboard_layout); + ~RGBController_AuraKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AuraKeyboardController* controller; + AuraKeyboardMappingLayoutType layout; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMonitorController/AsusAuraMonitorController.cpp b/Controllers/AsusAuraUSBController/AsusAuraMonitorController/AsusAuraMonitorController.cpp new file mode 100644 index 0000000..40eaac1 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMonitorController/AsusAuraMonitorController.cpp @@ -0,0 +1,123 @@ +/*---------------------------------------------------------*\ +| AsusAuraMonitorController.cpp | +| | +| Driver for ASUS Aura monitor | +| | +| Mola19 08 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusAuraMonitorController.h" +#include "LogManager.h" +#include "StringUtils.h" + +AuraMonitorController::AuraMonitorController(hid_device* dev_handle, const char* path, uint16_t pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + device_pid = pid; + name = dev_name; +} + +AuraMonitorController::~AuraMonitorController() +{ + hid_close(dev); +} + +std::string AuraMonitorController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AuraMonitorController::GetNameString() +{ + return(name); +} + +std::string AuraMonitorController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void AuraMonitorController::BeginUpdate() +{ + unsigned char usb_buf[8]; + + if (device_pid == AURA_ROG_PG32UQ_PID || device_pid == AURA_ROG_STRIX_XG32VC_PID) + { + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x03; + usb_buf[0x01] = 0x02; + usb_buf[0x02] = 0xA1; + usb_buf[0x03] = 0x80; + + usb_buf[0x04] = 0x20; + hid_send_feature_report(dev, usb_buf, 8); + + usb_buf[0x04] = 0x30; + hid_send_feature_report(dev, usb_buf, 8); + } +} + +void AuraMonitorController::UpdateLed + ( + int led, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[8]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x03; + usb_buf[0x01] = 0x02; + usb_buf[0x02] = 0xA1; + usb_buf[0x03] = 0x80; + + unsigned char offset = (device_pid == AURA_ROG_STRIX_XG279Q_PID) ? 0 : 16; + + usb_buf[0x04] = offset + led * 3; + usb_buf[0x05] = red; + + hid_send_feature_report(dev, usb_buf, 8); + + usb_buf[0x04] = offset + led * 3 + 1; + usb_buf[0x05] = blue; + + hid_send_feature_report(dev, usb_buf, 8); + + usb_buf[0x04] = offset + led * 3 + 2; + usb_buf[0x05] = green; + + hid_send_feature_report(dev, usb_buf, 8); +} + +void AuraMonitorController::ApplyChanges() +{ + unsigned char usb_buf[8]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x03; + usb_buf[0x01] = 0x02; + usb_buf[0x02] = 0xA1; + usb_buf[0x03] = 0x80; + usb_buf[0x04] = 0xA0; + usb_buf[0x05] = 0x01; + + hid_send_feature_report(dev, usb_buf, 8); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraMonitorController/AsusAuraMonitorController.h b/Controllers/AsusAuraUSBController/AsusAuraMonitorController/AsusAuraMonitorController.h new file mode 100644 index 0000000..2eceef8 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMonitorController/AsusAuraMonitorController.h @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| AsusAuraMonitorController.h | +| | +| Driver for ASUS Aura monitor | +| | +| Mola19 08 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +enum +{ + AURA_ROG_STRIX_XG27AQ_PID = 0x198C, + AURA_ROG_STRIX_XG27AQM_PID = 0x19BB, + AURA_ROG_STRIX_XG279Q_PID = 0x1919, + AURA_ROG_STRIX_XG27W_PID = 0x1933, + AURA_ROG_STRIX_XG32VC_PID = 0x1968, + AURA_ROG_PG32UQ_PID = 0x19B9, +}; + +class AuraMonitorController +{ +public: + AuraMonitorController(hid_device* dev_handle, const char* path, uint16_t pid, std::string dev_name); + virtual ~AuraMonitorController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void BeginUpdate(); + void UpdateLed(int led, unsigned char red, unsigned char green, unsigned char blue); + void ApplyChanges(); + + uint16_t device_pid; + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMonitorController/RGBController_AsusAuraMonitor.cpp b/Controllers/AsusAuraUSBController/AsusAuraMonitorController/RGBController_AsusAuraMonitor.cpp new file mode 100644 index 0000000..a14e269 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMonitorController/RGBController_AsusAuraMonitor.cpp @@ -0,0 +1,129 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraMonitor.cpp | +| | +| RGBController for ASUS Aura monitor | +| | +| Mola19 08 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusAuraMonitor.h" + +/**------------------------------------------------------------------*\ + @name Asus Aura Monitor + @category Accessory + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectAsusAuraUSBMonitor + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AuraMonitor::RGBController_AuraMonitor(AuraMonitorController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "ASUS"; + type = DEVICE_TYPE_MONITOR; + description = "ASUS Aura Monitor Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_AuraMonitor::~RGBController_AuraMonitor() +{ + delete controller; +} + +void RGBController_AuraMonitor::SetupZones() +{ + zone underglow_zone; + + underglow_zone.name = "Backlight"; + underglow_zone.type = ZONE_TYPE_LINEAR; + underglow_zone.leds_min = 3; + underglow_zone.leds_max = 3; + underglow_zone.leds_count = 3; + underglow_zone.matrix_map = NULL; + + zones.push_back(underglow_zone); + + for(unsigned int i = 0; i < 3; i++) + { + led underglow_led; + + underglow_led.name = "Backlight LED " + std::to_string(i + 1); + + leds.push_back(underglow_led); + } + + SetupColors(); +} + +void RGBController_AuraMonitor::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AuraMonitor::DeviceUpdateLEDs() +{ + controller->BeginUpdate(); + + for (int i = 0; i < 3; i++) + { + unsigned char red = RGBGetRValue(colors[i]); + unsigned char green = RGBGetGValue(colors[i]); + unsigned char blue = RGBGetBValue(colors[i]); + + controller->UpdateLed(i, red, green, blue); + } + + controller->ApplyChanges(); +} + +void RGBController_AuraMonitor::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraMonitor::UpdateSingleLED(int led) +{ + controller->BeginUpdate(); + + unsigned char red = RGBGetRValue(colors[led]); + unsigned char green = RGBGetGValue(colors[led]); + unsigned char blue = RGBGetBValue(colors[led]); + + controller->UpdateLed(led, red, green, blue); + + controller->ApplyChanges(); +} + +void RGBController_AuraMonitor::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | This device does not support Mode changing | + \*---------------------------------------------------------*/ +} + +void RGBController_AuraMonitor::DeviceSaveMode() +{ + /*---------------------------------------------------------*\ + | This device does not support Mode saving | + \*---------------------------------------------------------*/ +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraMonitorController/RGBController_AsusAuraMonitor.h b/Controllers/AsusAuraUSBController/AsusAuraMonitorController/RGBController_AsusAuraMonitor.h new file mode 100644 index 0000000..99bde88 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMonitorController/RGBController_AsusAuraMonitor.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraMonitor.h | +| | +| RGBController for ASUS Aura monitor | +| | +| Mola19 08 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraMonitorController.h" + +class RGBController_AuraMonitor : public RGBController +{ +public: + RGBController_AuraMonitor(AuraMonitorController* controller_ptr); + ~RGBController_AuraMonitor(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + AuraMonitorController* controller; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseController.cpp b/Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseController.cpp new file mode 100644 index 0000000..e812261 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseController.cpp @@ -0,0 +1,264 @@ +/*---------------------------------------------------------*\ +| AsusAuraMouseController.cpp | +| | +| Driver for ASUS Aura mouse | +| | +| Adam Honse (CalcProgrammer1) 23 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusAuraMouseController.h" +#include "StringUtils.h" + +#define HID_MAX_STR 255 + +AuraMouseController::AuraMouseController(hid_device* dev_handle, const char* path, uint16_t pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + device_pid = pid; + name = dev_name; +} + +AuraMouseController::~AuraMouseController() +{ + hid_close(dev); +} + +std::string AuraMouseController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AuraMouseController::GetName() +{ + return(name); +} + +std::string AuraMouseController::GetSerialString() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string AuraMouseController::GetVersion(bool wireless, int protocol) +{ + unsigned char usb_buf[ASUS_AURA_MOUSE_PACKET_SIZE]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x12; + hid_write(dev, usb_buf, ASUS_AURA_MOUSE_PACKET_SIZE); + + unsigned char usb_buf_out[ASUS_AURA_MOUSE_PACKET_SIZE]; + hid_read(dev, usb_buf_out, ASUS_AURA_MOUSE_PACKET_SIZE); + + std::string str; + + switch(protocol) + { + case 0: + { + unsigned char* offset = usb_buf_out + (wireless ? 13 : 4); + str = std::string(offset, offset + 4); + } + break; + + case 1: + case 2: + { + char version[9]; + int wireless_offset = (protocol == 2 ? 14 : 13); + int offset = (wireless ? wireless_offset : 4); + snprintf(version, 9, "%2X.%02X.%02X", usb_buf_out[offset + 2], usb_buf_out[offset + 1], usb_buf_out[offset]); + str = std::string(version); + } + break; + + case 3: + { + unsigned char* offset = usb_buf_out + (wireless ? 13 : 4); + str = std::string(offset, offset + 4); + str = "0." + str.substr(0, 2) + "." + str.substr(2, 2); + } + break; + + case 4: + { + char version[16]; + int offset = (wireless ? 13 : 4); + snprintf(version, 16, "%2d.%02d.%02d", usb_buf_out[offset + 1], usb_buf_out[offset + 2], usb_buf_out[offset + 3]); + str = std::string(version); + } + break; + } + + return str; +} + +std::string AuraMouseController::CleanSerial(const std::wstring& wstr) +{ + /*---------------------------------------------------------------*\ + | Cleans garbage at the end of serial numbers | + | (apparently 2 characters too much, but maybe variable) | + | Limited to new devices, old ones don't even have serial numbers | + \*---------------------------------------------------------------*/ + std::string result; + for(wchar_t c : wstr) + { + /*-----------------------------------------------------*\ + | Forbid anything besides digits and upper case letters | + \*-----------------------------------------------------*/ + bool isUpperCaseLetter = (c >= 64 && c <= 90); + bool isDigit = (c >= 48 && c <= 57); + if(!isUpperCaseLetter && !isDigit) + { + break; + } + + result += (char)c; + } + + return(result); +} + +void AuraMouseController::SaveMode() +{ + unsigned char usb_save_buf[ASUS_AURA_MOUSE_PACKET_SIZE] = { 0x00, 0x50, 0x03 }; + + hid_write(dev, usb_save_buf, ASUS_AURA_MOUSE_PACKET_SIZE); +} + +void AuraMouseController::SendUpdate + ( + unsigned char zone, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu, + unsigned char dir, + bool random, + unsigned char speed, + unsigned char brightness + ) +{ + int bytes_read = 1; + unsigned char usb_buf_flush[ASUS_AURA_MOUSE_PACKET_SIZE]; + + while(bytes_read > 0) + { + bytes_read = hid_read_timeout(dev, usb_buf_flush, ASUS_AURA_MOUSE_PACKET_SIZE, 0); + } + + unsigned char usb_buf[ASUS_AURA_MOUSE_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, ASUS_AURA_MOUSE_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + + if (device_pid == AURA_ROG_GLADIUS_II_ORIGIN_PNK_LTD_PID) + { + // this device supports 2 color for breathing, + // but since this mode is per led and openrgb doesn't support 2 colors per led this feature is not implemented + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x28; + usb_buf[0x03] = zone; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = mode; + usb_buf[0x06] = brightness; + usb_buf[0x07] = 0x00; // boolean signaling if the 2nd set of colors is in use + usb_buf[0x08] = red; + usb_buf[0x09] = grn; + usb_buf[0x0A] = blu; + usb_buf[0x0B] = 0; // 2nd red + usb_buf[0x0C] = 0; // 2nd green + usb_buf[0x0D] = 0; // 2nd blue + usb_buf[0x0E] = dir; + usb_buf[0x0F] = random; + usb_buf[0x10] = speed; + } + else + { + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x28; + usb_buf[0x03] = zone; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = mode; + usb_buf[0x06] = brightness; + usb_buf[0x07] = red; + usb_buf[0x08] = grn; + usb_buf[0x09] = blu; + usb_buf[0x0A] = dir; + usb_buf[0x0B] = random; + usb_buf[0x0C] = speed; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, ASUS_AURA_MOUSE_PACKET_SIZE); + + unsigned char usb_buf_out[ASUS_AURA_MOUSE_PACKET_SIZE]; + hid_read_timeout(dev, usb_buf_out, ASUS_AURA_MOUSE_PACKET_SIZE, 10); +} + +void AuraMouseController::SendDirect + ( + std::vector zone_colors + ) +{ + std::vector colors = {}; + colors.resize(aura_mouse_led_maps[device_pid].led_amount); + + for(unsigned char zone = 0; zone < zone_colors.size(); zone++) + { + std::vector zone_map = aura_mouse_led_maps[device_pid].map[zone]; + for(unsigned char led = 0; led < zone_map.size(); led++) + { + colors[zone_map[led]] = zone_colors[zone]; + } + } + + /*-----------------------------------------------------*\ + | Only 5 colors can be sent in each packet | + \*-----------------------------------------------------*/ + for(unsigned char led = 0; led < aura_mouse_led_maps[device_pid].led_amount; led += 5) + { + unsigned char usb_buf[ASUS_AURA_MOUSE_PACKET_SIZE]; + memset(usb_buf, 0x00, ASUS_AURA_MOUSE_PACKET_SIZE); + + unsigned char colors_in_packet = (aura_mouse_led_maps[device_pid].led_amount >= led + 5) ? 5 : aura_mouse_led_maps[device_pid].led_amount - led; + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x29; + usb_buf[0x03] = colors_in_packet; // colors in this packet + usb_buf[0x04] = 0x00; + usb_buf[0x05] = led; // offset + + for(unsigned char color = 0; color < colors_in_packet; color++) + { + usb_buf[0x06 + color * 3] = RGBGetRValue(colors[led + color]); + usb_buf[0x07 + color * 3] = RGBGetGValue(colors[led + color]); + usb_buf[0x08 + color * 3] = RGBGetBValue(colors[led + color]); + } + + hid_write(dev, usb_buf, ASUS_AURA_MOUSE_PACKET_SIZE); + } +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseController.h b/Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseController.h new file mode 100644 index 0000000..2b8a688 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseController.h @@ -0,0 +1,58 @@ +/*---------------------------------------------------------*\ +| AsusAuraMouseController.h | +| | +| Driver for ASUS Aura mouse | +| | +| Adam Honse (CalcProgrammer1) 23 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "AsusAuraMouseDevices.h" + +#define ASUS_AURA_MOUSE_PACKET_SIZE 65 + +class AuraMouseController +{ +public: + AuraMouseController(hid_device* dev_handle, const char* path, uint16_t pid, std::string dev_name); + virtual ~AuraMouseController(); + + std::string GetDeviceLocation(); + std::string GetName(); + std::string GetSerialString(); + std::string GetVersion(bool wireless, int protocol); + + std::string CleanSerial(const std::wstring& wstr); + + void SaveMode(); + void SendUpdate + ( + unsigned char zone, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu, + unsigned char dir, + bool random, + unsigned char speed, + unsigned char brightness + ); + void SendDirect + ( + std::vector zone_colors + ); + + uint16_t device_pid; + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseDevices.h b/Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseDevices.h new file mode 100644 index 0000000..02d8d1e --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseDevices.h @@ -0,0 +1,806 @@ +/*---------------------------------------------------------*\ +| AsusAuraMouseDevices.h | +| | +| Device list for ASUS Aura mouse | +| | +| Chris M (Dr_No) 11 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +#define AURA_ROG_GLADIUS_II_CORE_PID 0x18DD +#define AURA_ROG_GLADIUS_II_PID 0x1845 +#define AURA_ROG_GLADIUS_II_ORIGIN_PID 0x1877 +#define AURA_ROG_GLADIUS_II_ORIGIN_PNK_LTD_PID 0x18CD +#define AURA_ROG_GLADIUS_II_ORIGIN_COD_PID 0x18B1 +#define AURA_ROG_GLADIUS_II_WIRELESS_1_PID 0x189E +#define AURA_ROG_GLADIUS_II_WIRELESS_2_PID 0x18A0 +#define AURA_ROG_GLADIUS_III_PID 0x197B +#define AURA_ROG_GLADIUS_III_CORE_PID 0x1C8D +#define AURA_ROG_GLADIUS_III_WIRELESS_USB_PID 0x197D +#define AURA_ROG_GLADIUS_III_WIRELESS_2_4_PID 0x197F +#define AURA_ROG_GLADIUS_III_WIRELESS_BT_PID 0x1981 +#define AURA_ROG_GLADIUS_III_WIRELESS_AIMPOINT_USB_PID 0x1A70 +#define AURA_ROG_GLADIUS_III_WIRELESS_AIMPOINT_2_4_PID 0x1A72 +#define AURA_ROG_GLADIUS_III_WIRELESS_AIMPOINT_BT_PID 0x1A74 +#define AURA_ROG_CHAKRAM_WIRELESS_PID 0x18E5 +#define AURA_ROG_CHAKRAM_WIRED_1_PID 0x18E3 +#define AURA_ROG_CHAKRAM_CORE_PID 0x1958 +#define AURA_ROG_CHAKRAM_X_USB_PID 0x1A18 +#define AURA_ROG_CHAKRAM_X_2_4_PID 0x1A1A +#define AURA_ROG_SPATHA_X_USB_PID 0x1977 +#define AURA_ROG_SPATHA_X_2_4_PID 0x1979 +#define AURA_ROG_SPATHA_X_DOCK_PID 0x1979 +#define AURA_ROG_PUGIO_PID 0x1846 +#define AURA_ROG_PUGIO_II_WIRED_PID 0x1906 +#define AURA_ROG_PUGIO_II_WIRELESS_PID 0x1908 +#define AURA_ROG_STRIX_IMPACT_PID 0x1847 +#define AURA_ROG_STRIX_IMPACT_II_PID 0x18E1 +#define AURA_ROG_STRIX_IMPACT_II_GUNDAM_PID 0x189E +#define AURA_ROG_STRIX_IMPACT_II_PUNK_PID 0x1956 +#define AURA_ROG_STRIX_IMPACT_II_WHITE_PID 0x19D2 +#define AURA_ROG_STRIX_IMPACT_II_WIRELESS_USB_PID 0x1947 +#define AURA_ROG_STRIX_IMPACT_II_WIRELESS_2_4_PID 0x1949 +#define AURA_ROG_STRIX_IMPACT_III_PID 0x1A88 +#define AURA_ROG_KERIS 0x195C +#define AURA_ROG_KERIS_WIRELESS_USB_PID 0x195E +#define AURA_ROG_KERIS_WIRELESS_2_4_PID 0x1960 +#define AURA_ROG_KERIS_WIRELESS_BT_PID 0x1962 +#define AURA_ROG_KERIS_WIRELESS_AIMPOINT_USB_PID 0x1A66 +#define AURA_ROG_KERIS_WIRELESS_AIMPOINT_2_4_PID 0x1A68 +#define AURA_ROG_KERIS_WIRELESS_AIMPOINT_BT_PID 0x1A6A +#define AURA_TUF_M3_PID 0x1910 +#define AURA_TUF_M3_GEN_II_PID 0x1A9B +#define AURA_TUF_M5_PID 0x1898 + +#define AURA_ROG_SPATHA_X_DOCK_FAKE_PID 0xFFFF + +enum +{ + AURA_MOUSE_ZONE_LOGO = 0, + AURA_MOUSE_ZONE_SCROLL = 1, + AURA_MOUSE_ZONE_UNDERGLOW = 2, + AURA_MOUSE_ZONE_ALL = 3, + AURA_MOUSE_ZONE_DOCK = 4, +}; + +enum +{ + AURA_MOUSE_MODE_STATIC = 0, + AURA_MOUSE_MODE_BREATHING = 1, + AURA_MOUSE_MODE_SPECTRUM = 2, + AURA_MOUSE_MODE_WAVE = 3, + AURA_MOUSE_MODE_REACTIVE = 4, + AURA_MOUSE_MODE_COMET = 5, + AURA_MOUSE_MODE_BATTERY = 6, + AURA_MOUSE_MODE_DIRECT = 254, + AURA_MOUSE_MODE_NONE = 255, +}; + +typedef struct +{ + uint8_t speed_min; + uint8_t speed_max; + uint8_t brightness_min; + uint8_t brightness_max; + bool wireless; + int version_protocol; + bool direct; + std::vector mouse_zones; + std::vector mouse_modes; +} mouse_type; + +/*-----------------------------------------------------------------*\ +| DEVICE MAP | +| | +| This structure maps the OpenRGB modes to the mode values | +| sent to each mouse. As not all modes are present on each | +| mouse the "mode index" is different. Eg. "Reactive" is mode | +| 4 on the Gladius II and mode 3 on the Gladius II wireless | +\*-----------------------------------------------------------------*/ +static std::map aura_mouse_devices = +{ + { + AURA_ROG_GLADIUS_II_CORE_PID, // ROG Gladius II Core + { + 0, // Speed Min - The Asus Mouse protocol defines larger numbers as slow + 0, // Speed Max + 0, // Brightness Min + 4, // Brightness Max + false, // is wireless? (important for fetching the version) + 1, // version protocol + false, // direct - defines whether the mouse has a native direct mode or just uses static as direct (only present on newer devices) + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_GLADIUS_II_PID, // ROG Gladius II + { + 255, + 1, + 0, + 4, + false, + 0, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET } + } + }, + { + AURA_ROG_GLADIUS_II_ORIGIN_PID, // ROG Gladius II Origin + { + 255, + 1, + 0, + 4, + false, + 0, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET } + } + }, + { + AURA_ROG_GLADIUS_II_ORIGIN_COD_PID, // ROG Gladius II COD + { + 255, + 1, + 0, + 4, + false, + 0, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET } + } + }, + { + AURA_ROG_GLADIUS_II_ORIGIN_PNK_LTD_PID, // ROG Gladius II PNK LTD + { + 255, + 1, + 0, + 4, + false, + 0, + false, + { AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET } + } + }, + { + AURA_ROG_GLADIUS_II_WIRELESS_1_PID, // ROG Gladius II Wireless + { + 0, + 0, + 0, + 4, + true, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_GLADIUS_II_WIRELESS_2_PID, // ROG Gladius II Wireless + { + 0, + 0, + 0, + 4, + true, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_GLADIUS_III_PID, // ROG Gladius III + { + 255, + 1, + 0, + 100, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_GLADIUS_III_CORE_PID, // ROG Gladius III Core + { + 0, + 0, + 0, + 100, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_GLADIUS_III_WIRELESS_USB_PID, // ROG Gladius III Wireless USB + { + 255, + 1, + 0, + 100, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_GLADIUS_III_WIRELESS_2_4_PID, // ROG Gladius III Wireless 2.4 GHz Dongle + { + 255, + 1, + 0, + 100, + true, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_GLADIUS_III_WIRELESS_BT_PID, // ROG Gladius III Wireless Bluetooth + { + 255, + 1, + 0, + 100, + true, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_GLADIUS_III_WIRELESS_AIMPOINT_USB_PID, // ROG Gladius III Wireless AimPoint USB + { + 255, + 1, + 0, + 100, + false, + 2, + true, + { AURA_MOUSE_ZONE_LOGO }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_GLADIUS_III_WIRELESS_AIMPOINT_2_4_PID, // ROG Gladius III Wireless AimPoint 2.4 GHz Dongle + { + 255, + 1, + 0, + 100, + true, + 2, + true, + { AURA_MOUSE_ZONE_LOGO }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_CHAKRAM_WIRELESS_PID, // ROG Chakram Wireless + { + 15, + 1, + 0, + 4, + true, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_CHAKRAM_WIRED_1_PID, // ROG Chakram Wired 1 + { + 15, + 1, + 0, + 4, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_CHAKRAM_CORE_PID, // ROG Chakram Core + { + 0, + 0, + 0, + 4, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_CHAKRAM_X_USB_PID, // ROG Chakram X USB + { + 15, // technically until 255, but unusably slow after 15 + 1, + 0, + 100, + false, + 2, + true, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_CHAKRAM_X_2_4_PID, // ROG Chakram X 2.4GHz Dongle + { + 15, // technically until 255, but unusably slow after 15 + 1, + 0, + 100, + true, + 2, + true, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_SPATHA_X_USB_PID, // ROG Spatha X USB + { + 15, // technically until 255, but unusably slow after 15 + 1, + 0, + 100, + false, + 1, + true, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_SPATHA_X_2_4_PID, // ROG Spatha X 2.4GHz Dock + { + 15, // technically until 255, but unusably slow after 15 + 1, + 0, + 100, + true, + 1, + true, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_PUGIO_PID, // ROG Pugio + { + 255, + 1, + 0, + 4, + false, + 0, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET } + } + }, + { + AURA_ROG_PUGIO_II_WIRED_PID, // ROG Pugio II Wired + { + 15, + 1, + 0, + 4, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_PUGIO_II_WIRELESS_PID, // ROG Pugio II Wireless + { + 15, + 1, + 0, + 4, + true, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_WAVE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_COMET, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_STRIX_IMPACT_PID, // ROG Strix Impact + { + 0, + 0, + 0, + 4, + false, + 4, + false, + { AURA_MOUSE_ZONE_LOGO }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_STRIX_IMPACT_II_PID, // ROG Strix Impact II + { + 0, + 0, + 0, + 4, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_STRIX_IMPACT_II_GUNDAM_PID, // ROG Strix Impact II Gundam + { + 0, + 0, + 0, + 4, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_STRIX_IMPACT_II_PUNK_PID, // ROG Strix Impact II Electro Punk + { + 0, + 0, + 0, + 4, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_STRIX_IMPACT_II_WHITE_PID, // ROG Strix Impact II Moonlight White + { + 0, + 0, + 0, + 4, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL, AURA_MOUSE_ZONE_UNDERGLOW }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_STRIX_IMPACT_II_WIRELESS_USB_PID, // ROG Strix Impact II Wireless USB + { + 0, + 0, + 0, + 4, + false, + 1, // not tested, but likely same as ROG Strix Impact II non wireless + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_STRIX_IMPACT_II_WIRELESS_2_4_PID, // ROG Strix Impact II Wireless 2.4 GHz Dongle + { + 0, + 0, + 0, + 4, + true, + 1, // not tested, but likely same as ROG Strix Impact II non wireless + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_STRIX_IMPACT_III_PID, // ROG Strix Impact III + { + 0, + 0, + 0, + 100, + false, + 1, + true, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_KERIS, // ROG Keris + { + 0, + 0, + 0, + 4, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_KERIS_WIRELESS_USB_PID, // ROG Keris + { + 0, + 0, + 0, + 4, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_KERIS_WIRELESS_2_4_PID, // ROG Keris + { + 0, + 0, + 0, + 4, + true, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_KERIS_WIRELESS_BT_PID, // ROG Keris + { + 0, + 0, + 0, + 4, + true, + 1, + false, + { AURA_MOUSE_ZONE_LOGO, AURA_MOUSE_ZONE_SCROLL }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_KERIS_WIRELESS_AIMPOINT_USB_PID, // ROG Keris Wireless AimPoint + { + 0, + 0, + 0, + 100, + false, + 1, + true, + { AURA_MOUSE_ZONE_LOGO }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_ROG_KERIS_WIRELESS_AIMPOINT_2_4_PID, // ROG Keris Wireless AimPoint + { + 0, + 0, + 0, + 100, + true, + 2, + true, + { AURA_MOUSE_ZONE_LOGO }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, + { + AURA_TUF_M3_PID, // TUF M3 + { + 0, + 0, + 0, + 4, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_TUF_M3_GEN_II_PID, // TUF M3 Gen II + { + 0, + 0, + 0, + 100, + false, + 1, + false, + { AURA_MOUSE_ZONE_LOGO }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_TUF_M5_PID, // TUF M5 + { + 0, + 0, + 0, + 4, + false, + 3, + false, + { AURA_MOUSE_ZONE_LOGO }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_REACTIVE } + } + }, + { + AURA_ROG_SPATHA_X_DOCK_FAKE_PID, // Asus ROG Spatha X Dock (only in wireless mode) + { + 0, + 0, + 0, + 100, + false, + 1, + false, + { AURA_MOUSE_ZONE_DOCK }, + { AURA_MOUSE_MODE_STATIC, AURA_MOUSE_MODE_BREATHING, AURA_MOUSE_MODE_SPECTRUM, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_REACTIVE, AURA_MOUSE_MODE_NONE, AURA_MOUSE_MODE_BATTERY } + } + }, +}; + +typedef struct +{ + unsigned char led_amount; + std::map< unsigned char, std::vector> map; +} led_map; + +/*-----------------------------------------------------------------*\ +| LED MAP | +| | +| This maps the LEDs to the correct zone. | +| This is necessary, because openrgb doesn't support per-zone | +| lighting. To allow for per-zone color of modes like breathing, | +| the leds defined need to be the zones, which means the actual | +| LEDs can't be implemented. | +\*-----------------------------------------------------------------*/ +static std::map aura_mouse_led_maps = +{ + { + AURA_ROG_GLADIUS_III_WIRELESS_AIMPOINT_USB_PID, + { + 1, + { + { AURA_MOUSE_ZONE_LOGO, { 0 } }, + } + } + }, + { + AURA_ROG_GLADIUS_III_WIRELESS_AIMPOINT_2_4_PID, + { + 1, + { + { AURA_MOUSE_ZONE_LOGO, { 0 } }, + } + } + }, + { + AURA_ROG_KERIS_WIRELESS_AIMPOINT_USB_PID, + { + 1, + { + { AURA_MOUSE_ZONE_LOGO, { 0 } }, + } + } + }, + { + AURA_ROG_KERIS_WIRELESS_AIMPOINT_2_4_PID, + { + 1, + { + { AURA_MOUSE_ZONE_LOGO, { 0 } }, + } + } + }, + { + AURA_ROG_SPATHA_X_2_4_PID, + { + 5, + { + { AURA_MOUSE_ZONE_LOGO, { 0 } }, + { AURA_MOUSE_ZONE_SCROLL, { 1 } }, + { AURA_MOUSE_ZONE_UNDERGLOW, { 2, 3, 4 } }, + } + } + }, + { + AURA_ROG_SPATHA_X_USB_PID, + { + 5, + { + { AURA_MOUSE_ZONE_LOGO, { 0 } }, + { AURA_MOUSE_ZONE_SCROLL, { 1 } }, + { AURA_MOUSE_ZONE_UNDERGLOW, { 2, 3, 4 } }, + } + } + }, + { + AURA_ROG_CHAKRAM_X_USB_PID, + { + 9, + { + { AURA_MOUSE_ZONE_LOGO, { 7 } }, + { AURA_MOUSE_ZONE_SCROLL, { 8 } }, + { AURA_MOUSE_ZONE_UNDERGLOW, { 0, 1, 2, 3, 4, 5, 6 } }, + } + } + }, + { + AURA_ROG_CHAKRAM_X_2_4_PID, + { + 9, + { + { AURA_MOUSE_ZONE_LOGO, { 7 } }, + { AURA_MOUSE_ZONE_SCROLL, { 8 } }, + { AURA_MOUSE_ZONE_UNDERGLOW, { 0, 1, 2, 3, 4, 5, 6 } }, + } + } + }, + { + AURA_ROG_STRIX_IMPACT_III_PID, + { + 2, + { + { AURA_MOUSE_ZONE_LOGO, { 0 } }, + { AURA_MOUSE_ZONE_SCROLL, { 1 } }, + } + } + } +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseController/RGBController_AsusAuraMouse.cpp b/Controllers/AsusAuraUSBController/AsusAuraMouseController/RGBController_AsusAuraMouse.cpp new file mode 100644 index 0000000..d959fab --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseController/RGBController_AsusAuraMouse.cpp @@ -0,0 +1,304 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraMouse.cpp | +| | +| RGBController for ASUS Aura mouse | +| | +| Adam Honse (CalcProgrammer1) 25 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusAuraMouse.h" + +static std::string aura_mouse_zone_names[5] +{ + "Logo", + "Scroll Wheel", + "Underglow", + "All", + "Dock" +}; + +/**------------------------------------------------------------------*\ + @name Asus Aura Mouse + @category Mouse + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraUSBMice + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AuraMouse::RGBController_AuraMouse(AuraMouseController* controller_ptr) +{ + controller = controller_ptr; + + pid = controller->device_pid; + + name = controller->GetName(); + vendor = "ASUS"; + type = DEVICE_TYPE_MOUSE; + description = "ASUS Aura Mouse Device"; + version = controller->GetVersion(aura_mouse_devices[pid].wireless, aura_mouse_devices[pid].version_protocol); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + std::vector mm = aura_mouse_devices[pid].mouse_modes; + + if(aura_mouse_devices[pid].direct) + { + mode Direct; + Direct.name = "Direct"; + Direct.value = 254; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + } + + int mode_value = 0; + + for(std::vector::iterator it = mm.begin(); it != mm.end(); it++) + { + switch(*it) + { + case AURA_MOUSE_MODE_STATIC: + /*-----------------------------------------------------------------*\ + | If there is no direct mode, this mode can be used as direct | + | (Asus does it the same way). | + | The acutal direct mode is only found on new devices and on | + | these devices static can't be used as direct anymore as it is | + | too slow. The Spatha X's dock can't be controlled, because | + | static is too slow and it can't be adressed in direct | + \*-----------------------------------------------------------------*/ + { + mode Static; + Static.name = (aura_mouse_devices[pid].direct || pid == AURA_ROG_SPATHA_X_DOCK_FAKE_PID)? "Static" : "Direct"; + Static.value = mode_value; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.brightness_min = aura_mouse_devices[pid].brightness_min; + Static.brightness_max = aura_mouse_devices[pid].brightness_max; + Static.brightness = aura_mouse_devices[pid].brightness_max; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + } + break; + + case AURA_MOUSE_MODE_BREATHING: + { + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = mode_value; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.brightness_min = aura_mouse_devices[pid].brightness_min; + Breathing.brightness_max = aura_mouse_devices[pid].brightness_max; + Breathing.brightness = aura_mouse_devices[pid].brightness_max; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + } + break; + + case AURA_MOUSE_MODE_SPECTRUM: + { + mode ColorCycle; + ColorCycle.name = "Spectrum Cycle"; + ColorCycle.value = mode_value; + ColorCycle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE ; + ColorCycle.brightness_min = aura_mouse_devices[pid].brightness_min; + ColorCycle.brightness_max = aura_mouse_devices[pid].brightness_max; + ColorCycle.brightness = aura_mouse_devices[pid].brightness_max; + ColorCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(ColorCycle); + } + break; + + case AURA_MOUSE_MODE_WAVE: + { + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = mode_value; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Wave.direction = 0; + Wave.speed_min = aura_mouse_devices[pid].speed_min; + Wave.speed_max = aura_mouse_devices[pid].speed_max; + Wave.speed = (aura_mouse_devices[pid].speed_min + aura_mouse_devices[pid].speed_max) / 2; + Wave.brightness_min = aura_mouse_devices[pid].brightness_min; + Wave.brightness_max = aura_mouse_devices[pid].brightness_max; + Wave.brightness = aura_mouse_devices[pid].brightness_max; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + } + break; + + case AURA_MOUSE_MODE_REACTIVE: + { + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = mode_value; + Reactive.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Reactive.brightness_min = aura_mouse_devices[pid].brightness_min; + Reactive.brightness_max = aura_mouse_devices[pid].brightness_max; + Reactive.brightness = aura_mouse_devices[pid].brightness_max; + Reactive.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Reactive); + } + break; + + case AURA_MOUSE_MODE_COMET: + { + mode Comet; + Comet.name = "Comet"; + Comet.value = mode_value; + Comet.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Comet.brightness_min = aura_mouse_devices[pid].brightness_min; + Comet.brightness_max = aura_mouse_devices[pid].brightness_max; + Comet.brightness = aura_mouse_devices[pid].brightness_max; + Comet.color_mode = MODE_COLORS_MODE_SPECIFIC; + Comet.direction = 0; + Comet.colors_min = 1; + Comet.colors_max = 1; + Comet.colors.resize(1); + modes.push_back(Comet); + } + break; + + case AURA_MOUSE_MODE_BATTERY: + { + mode BatteryMode; + BatteryMode.name = "Battery"; + BatteryMode.value = mode_value; + BatteryMode.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + BatteryMode.brightness_min = aura_mouse_devices[pid].brightness_min; + BatteryMode.brightness_max = aura_mouse_devices[pid].brightness_max; + BatteryMode.brightness = aura_mouse_devices[pid].brightness_max; + BatteryMode.color_mode = MODE_COLORS_NONE; + modes.push_back(BatteryMode); + } + break; + } + mode_value++; + } + + SetupZones(); +} + +RGBController_AuraMouse::~RGBController_AuraMouse() +{ + delete controller; +} + +void RGBController_AuraMouse::SetupZones() +{ + for(std::vector::iterator zone_it = aura_mouse_devices[pid].mouse_zones.begin(); zone_it != aura_mouse_devices[pid].mouse_zones.end(); zone_it++) + { + zone mouse_zone; + + mouse_zone.name = aura_mouse_zone_names[*zone_it]; + mouse_zone.type = ZONE_TYPE_SINGLE; + mouse_zone.leds_min = 1; + mouse_zone.leds_max = 1; + mouse_zone.leds_count = 1; + mouse_zone.matrix_map = NULL; + + zones.push_back(mouse_zone); + + led mouse_led; + + mouse_led.name = mouse_zone.name + " LED"; + mouse_led.value = *zone_it; + + leds.push_back(mouse_led); + } + + SetupColors(); +} + +void RGBController_AuraMouse::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_AuraMouse::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == AURA_MOUSE_MODE_DIRECT) + { + controller->SendDirect(colors); + } + else + { + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + UpdateSingleLED(zone_index); + } + } +} + +void RGBController_AuraMouse::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraMouse::UpdateSingleLED(int led) +{ + if(modes[active_mode].value == AURA_MOUSE_MODE_DIRECT) + { + DeviceUpdateLEDs(); + return; + } + + uint8_t red = RGBGetRValue(colors[led]); + uint8_t grn = RGBGetGValue(colors[led]); + uint8_t blu = RGBGetBValue(colors[led]); + + controller->SendUpdate(leds[led].value, modes[active_mode].value, red, grn, blu, 0, false, 0, modes[active_mode].brightness); +} + +void RGBController_AuraMouse::DeviceUpdateMode() +{ + if(modes[active_mode].value == AURA_MOUSE_MODE_DIRECT) + { + return; + } + + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + DeviceUpdateLEDs(); + } + else + { + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + + if (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + + if(pid == AURA_ROG_SPATHA_X_DOCK_FAKE_PID) + { + controller->SendUpdate(AURA_MOUSE_ZONE_DOCK, modes[active_mode].value, red, grn, blu, modes[active_mode].direction, modes[active_mode].color_mode == MODE_COLORS_RANDOM, modes[active_mode].speed, modes[active_mode].brightness); + } + else if(pid == AURA_ROG_STRIX_IMPACT_PID) + { + /*-----------------------------------------------------------------*\ + | The ROG Impact doesn't accept AURA_MOUSE_ZONE_ALL | + \*-----------------------------------------------------------------*/ + controller->SendUpdate(AURA_MOUSE_ZONE_LOGO, modes[active_mode].value, red, grn, blu, modes[active_mode].direction, modes[active_mode].color_mode == MODE_COLORS_RANDOM, modes[active_mode].speed, modes[active_mode].brightness); + } + else + { + controller->SendUpdate(AURA_MOUSE_ZONE_ALL, modes[active_mode].value, red, grn, blu, modes[active_mode].direction, modes[active_mode].color_mode == MODE_COLORS_RANDOM, modes[active_mode].speed, modes[active_mode].brightness); + } + } +} + +void RGBController_AuraMouse::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->SaveMode(); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseController/RGBController_AsusAuraMouse.h b/Controllers/AsusAuraUSBController/AsusAuraMouseController/RGBController_AsusAuraMouse.h new file mode 100644 index 0000000..6eafb9c --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseController/RGBController_AsusAuraMouse.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraMouse.h | +| | +| RGBController for ASUS Aura mouse | +| | +| Adam Honse (CalcProgrammer1) 25 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraMouseController.h" + +class RGBController_AuraMouse : public RGBController +{ +public: + RGBController_AuraMouse(AuraMouseController* controller_ptr); + ~RGBController_AuraMouse(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + AuraMouseController* controller; + uint16_t pid; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/AsusAuraMouseGen1Controller.cpp b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/AsusAuraMouseGen1Controller.cpp new file mode 100644 index 0000000..8abf8d4 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/AsusAuraMouseGen1Controller.cpp @@ -0,0 +1,181 @@ +/*---------------------------------------------------------*\ +| AsusAuraMouseGen1Controller.cpp | +| | +| Driver for ASUS Aura gen 1 mouse | +| | +| Mola19 30 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "AsusAuraMouseGen1Controller.h" +#include "StringUtils.h" + +AsusAuraMouseGen1Controller::AsusAuraMouseGen1Controller(hid_device* dev_handle, const char* path, uint16_t pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + device_pid = pid; + name = dev_name; +} + +AsusAuraMouseGen1Controller::~AsusAuraMouseGen1Controller() +{ + hid_close(dev); +} + +std::string AsusAuraMouseGen1Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AsusAuraMouseGen1Controller::GetName() +{ + return(name); +} + +std::string AsusAuraMouseGen1Controller::GetSerialString() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string AsusAuraMouseGen1Controller::GetVersion() +{ + unsigned char usb_buf[9] = { 0x0C, 0xC4, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + usb_buf[3] = (device_pid == 0x1824) ? 0x02 : 0x00; + hid_send_feature_report(dev, usb_buf, 9); + + unsigned char usb_buf_out[9] = { 0x0C }; + hid_get_feature_report(dev, usb_buf_out, 9); + + return std::string("1." + std::to_string(usb_buf_out[3])); +} + +int AsusAuraMouseGen1Controller::GetActiveProfile() +{ + unsigned char profile; + + unsigned char profile_amount = 0; + unsigned char profile_key = 0; + + switch(device_pid) + { + case 0x185B: + profile_amount = 3; + profile_key = 0xF0; + break; + case 0x181C: + case 0x1824: + default: + profile_amount = 6; + profile_key = 0x60; + break; + } + + do + { + unsigned char usb_buf[9] = { 0x0C, 0xDF, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + hid_send_feature_report(dev, usb_buf, 9); + + unsigned char usb_buf_out[9] = { 0x0C }; + hid_get_feature_report(dev, usb_buf_out, 9); + + profile = usb_buf_out[4]; + } while(profile < profile_key || profile > profile_key + profile_amount - 1); + + return (profile % 16) + 1; +} + +void AsusAuraMouseGen1Controller::SendUpdate + ( + unsigned char key, + unsigned char value + ) +{ + unsigned char usb_buf[9]; + + memset(usb_buf, 0x00, 9); + + usb_buf[0x00] = 0x0C; + usb_buf[0x01] = 0xC4; + usb_buf[0x02] = 0x0F; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = key; + usb_buf[0x05] = value; + hid_send_feature_report(dev, usb_buf, 9); + + unsigned char buf_in[9]; + buf_in[0] = 0x0C; + hid_get_feature_report(dev, buf_in, 9); + + std::this_thread::sleep_for(std::chrono::milliseconds(15)); +} + + +void AsusAuraMouseGen1Controller::UpdateProfile + ( + unsigned char key, + unsigned char profile, + unsigned char value + ) +{ + unsigned char usb_buf[9]; + + memset(usb_buf, 0x00, 9); + + usb_buf[0x00] = 0x0C; + usb_buf[0x01] = 0xDE; + usb_buf[0x02] = key; + usb_buf[0x03] = profile; + usb_buf[0x04] = value; + hid_send_feature_report(dev, usb_buf, 9); + + unsigned char buf_in[9]; + buf_in[0] = 0x0C; + hid_get_feature_report(dev, buf_in, 9); +} + +void AsusAuraMouseGen1Controller::SendDirectSpatha(std::vector colors) +{ + unsigned char usb_buf[33]; + + memset(usb_buf, 0x00, 33); + + usb_buf[0x00] = 0x10; + usb_buf[0x01] = 0x00; + usb_buf[0x02] = 0xF0; + usb_buf[0x03] = 0x00; + + for(unsigned char i = 0; i < 3; i++) + { + usb_buf[4 + i * 3] = RGBGetRValue(colors[i]); + usb_buf[5 + i * 3] = RGBGetGValue(colors[i]); + usb_buf[6 + i * 3] = RGBGetBValue(colors[i]); + } + + hid_send_feature_report(dev, usb_buf, 33); +} + +void AsusAuraMouseGen1Controller::ResetToSavedLighting() +{ + unsigned char usb_buf[9]; + + memset(usb_buf, 0x00, 9); + + usb_buf[0x00] = 0x0C; + usb_buf[0x01] = 0xC4; + + hid_send_feature_report(dev, usb_buf, 9); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/AsusAuraMouseGen1Controller.h b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/AsusAuraMouseGen1Controller.h new file mode 100644 index 0000000..d1e0e20 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/AsusAuraMouseGen1Controller.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------*\ +| AsusAuraMouseGen1Controller.h | +| | +| Driver for ASUS Aura gen 1 mouse | +| | +| Mola19 30 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define HID_MAX_STR 255 + +class AsusAuraMouseGen1Controller +{ +public: + AsusAuraMouseGen1Controller(hid_device* dev_handle, const char* path, uint16_t pid, std::string dev_name); + virtual ~AsusAuraMouseGen1Controller(); + + std::string GetDeviceLocation(); + std::string GetName(); + std::string GetSerialString(); + std::string GetVersion(); + + int GetActiveProfile(); + + void SendUpdate + ( + unsigned char key, + unsigned char value + ); + + void UpdateProfile + ( + unsigned char key, + unsigned char profile, + unsigned char value + ); + + void SendDirectSpatha(std::vector colors); + + void ResetToSavedLighting(); + + uint16_t device_pid; + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGSpatha.cpp b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGSpatha.cpp new file mode 100644 index 0000000..18b295d --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGSpatha.cpp @@ -0,0 +1,274 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusROGSpatha.cpp | +| | +| RGBController for ASUS ROG Spatha | +| | +| Mola19 05 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusROGSpatha.h" + +/**------------------------------------------------------------------*\ + @name Asus Aura Spatha + @category Mouse + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraUSBSpatha + @comment This device allows indiviual modes for each zone, + which currently can't be implemented in OpenRGB. + Also there seem to be a firmware bug which causes static + to use a random colorand random to use a static color + that was previously set. This can be worked around by saving +\*-------------------------------------------------------------------*/ + +RGBController_AsusROGSpatha::RGBController_AsusROGSpatha(AsusAuraMouseGen1Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "ASUS"; + type = DEVICE_TYPE_MOUSE; + description = "ASUS Aura Mouse Device"; + version = controller->GetVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ASUS_ROG_SPATHA_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = ASUS_ROG_SPATHA_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.brightness_min = ASUS_ROG_SPATHA_BRIGHTNESS_MIN; + Static.brightness_max = ASUS_ROG_SPATHA_BRIGHTNESS_MAX; + Static.brightness = ASUS_ROG_SPATHA_BRIGHTNESS_DEFAULT; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ASUS_ROG_SPATHA_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.brightness_min = ASUS_ROG_SPATHA_BRIGHTNESS_MIN; + Breathing.brightness_max = ASUS_ROG_SPATHA_BRIGHTNESS_MAX; + Breathing.brightness = ASUS_ROG_SPATHA_BRIGHTNESS_DEFAULT; + Breathing.colors_min = 2; + Breathing.colors_max = 2; + Breathing.colors.resize(2); + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Spectrum Cycle"; + ColorCycle.value = ASUS_ROG_SPATHA_MODE_SPECTRUM_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + ColorCycle.brightness_min = ASUS_ROG_SPATHA_BRIGHTNESS_MIN; + ColorCycle.brightness_max = ASUS_ROG_SPATHA_BRIGHTNESS_MAX; + ColorCycle.brightness = ASUS_ROG_SPATHA_BRIGHTNESS_DEFAULT; + ColorCycle.colors_min = 12; + ColorCycle.colors_max = 12; + ColorCycle.colors.resize(12); + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(ColorCycle); + + mode Random; + Random.name = "Random"; + Random.value = ASUS_ROG_SPATHA_MODE_RANDOM; + Random.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Random.brightness_min = ASUS_ROG_SPATHA_BRIGHTNESS_MIN; + Random.brightness_max = ASUS_ROG_SPATHA_BRIGHTNESS_MAX; + Random.brightness = ASUS_ROG_SPATHA_BRIGHTNESS_DEFAULT; + Random.color_mode = MODE_COLORS_NONE; + modes.push_back(Random); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = ASUS_ROG_SPATHA_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Reactive.brightness_min = ASUS_ROG_SPATHA_BRIGHTNESS_MIN; + Reactive.brightness_max = ASUS_ROG_SPATHA_BRIGHTNESS_MAX; + Reactive.brightness = ASUS_ROG_SPATHA_BRIGHTNESS_DEFAULT; + Reactive.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Reactive); + + mode Battery; + Battery.name = "Battery"; + Battery.value = ASUS_ROG_SPATHA_MODE_BATTERY; + Battery.flags = MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Battery.brightness_min = ASUS_ROG_SPATHA_BRIGHTNESS_MIN; + Battery.brightness_max = ASUS_ROG_SPATHA_BRIGHTNESS_MAX; + Battery.brightness = ASUS_ROG_SPATHA_BRIGHTNESS_DEFAULT; + Battery.color_mode = MODE_COLORS_NONE; + modes.push_back(Battery); + + SetupZones(); +} + +RGBController_AsusROGSpatha::~RGBController_AsusROGSpatha() +{ + delete controller; +} + +void RGBController_AsusROGSpatha::SetupZones() +{ + + std::string zones_names[3] = {"Side", "Scroll Wheel", "Logo"}; + + for(unsigned char i = 0; i < 3; i++) + { + zone spatha_zone; + + spatha_zone.name = zones_names[i]; + spatha_zone.type = ZONE_TYPE_SINGLE; + spatha_zone.leds_min = 1; + spatha_zone.leds_max = 1; + spatha_zone.leds_count = 1; + spatha_zone.matrix_map = NULL; + + zones.push_back(spatha_zone); + + led spatha_led; + + spatha_led.name = zones_names[i]; + spatha_led.value = 1; + + leds.push_back(spatha_led); + } + + SetupColors(); +} + +void RGBController_AsusROGSpatha::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_AsusROGSpatha::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == ASUS_ROG_SPATHA_MODE_DIRECT) + { + controller->SendDirectSpatha(colors); + } + else + { + UpdateSingleLED(0); + UpdateSingleLED(1); + UpdateSingleLED(2); + } +} + +void RGBController_AsusROGSpatha::UpdateZoneLEDs(int zone) +{ + if(modes[active_mode].value == ASUS_ROG_SPATHA_MODE_DIRECT) + { + controller->SendDirectSpatha(colors); + } + else + { + UpdateSingleLED(zone); + } +} + +void RGBController_AsusROGSpatha::UpdateSingleLED(int led) +{ + if(modes[active_mode].value == ASUS_ROG_SPATHA_MODE_DIRECT) + { + controller->SendDirectSpatha(colors); + } + else + { + controller->SendUpdate(0x13 + led * 38, RGBGetRValue(colors[led])); + controller->SendUpdate(0x14 + led * 38, RGBGetGValue(colors[led])); + controller->SendUpdate(0x15 + led * 38, RGBGetBValue(colors[led])); + } +} + +void RGBController_AsusROGSpatha::DeviceUpdateMode() +{ + if(modes[active_mode].value == ASUS_ROG_SPATHA_MODE_DIRECT) + { + return; + } + + /*-----------------------------------------------------*\ + | Needed to overwrite direct | + \*-----------------------------------------------------*/ + controller->ResetToSavedLighting(); + + /*-----------------------------------------------------*\ + | Send data to all 3 zones | + \*-----------------------------------------------------*/ + for(int i = 0; i < 3; i++) + { + controller->SendUpdate(0x11 + i * 38, modes[active_mode].value); + + /*------------------------------------------------------------------*\ + | This mouse has independent brightness for wired and wireless. | + | Each is 4-bit in the same byte (wireless is the first/bigger one). | + | OpenRGB misses that feature, hence both are the same | + \*------------------------------------------------------------------*/ + controller->SendUpdate(0x12 + i * 38, (modes[active_mode].brightness << 4) + modes[active_mode].brightness); + + if(modes[active_mode].value == ASUS_ROG_SPATHA_MODE_SPECTRUM_CYCLE || modes[active_mode].value == ASUS_ROG_SPATHA_MODE_BREATHING) + { + for(unsigned int j = 0; j < modes[active_mode].colors.size(); j++) + { + controller->SendUpdate(0x13 + j * 3 + i * 38, RGBGetRValue(modes[active_mode].colors[j])); + controller->SendUpdate(0x14 + j * 3 + i * 38, RGBGetGValue(modes[active_mode].colors[j])); + controller->SendUpdate(0x15 + j * 3 + i * 38, RGBGetBValue(modes[active_mode].colors[j])); + } + } + } +} + +void RGBController_AsusROGSpatha::DeviceSaveMode() +{ + if(modes[active_mode].value == ASUS_ROG_SPATHA_MODE_DIRECT) + { + return; + } + + unsigned int profile = controller->GetActiveProfile(); + + /*-----------------------------------------------------*\ + | Send data to all 3 zones | + \*-----------------------------------------------------*/ + for(int i = 0; i < 3; i++) + { + controller->UpdateProfile(0x11 + i * 38, profile, modes[active_mode].value); + /*------------------------------------------------------------------*\ + | This mouse has independent brightness for wired and wireless. | + | Each is 4-bit in the same byte (wireless is the first/bigger one). | + | OpenRGB misses that feature, hence both are the same | + \*------------------------------------------------------------------*/ + controller->UpdateProfile(0x12 + i * 38, profile, (modes[active_mode].brightness << 4) + modes[active_mode].brightness); + + if(modes[active_mode].value == ASUS_ROG_SPATHA_MODE_SPECTRUM_CYCLE || modes[active_mode].value == ASUS_ROG_SPATHA_MODE_BREATHING) + { + for(unsigned int j = 0; j < modes[active_mode].colors.size(); j++) + { + controller->UpdateProfile(0x13 + j * 3 + i * 38, profile, RGBGetRValue(modes[active_mode].colors[j])); + controller->UpdateProfile(0x14 + j * 3 + i * 38, profile, RGBGetGValue(modes[active_mode].colors[j])); + controller->UpdateProfile(0x15 + j * 3 + i * 38, profile, RGBGetBValue(modes[active_mode].colors[j])); + } + } + else if(modes[active_mode].value == ASUS_ROG_SPATHA_MODE_STATIC || modes[active_mode].value == ASUS_ROG_SPATHA_MODE_REACTIVE) + { + controller->UpdateProfile(0x13 + i * 38, profile, RGBGetRValue(colors[i])); + controller->UpdateProfile(0x14 + i * 38, profile, RGBGetGValue(colors[i])); + controller->UpdateProfile(0x15 + i * 38, profile, RGBGetBValue(colors[i])); + } + } + + controller->ResetToSavedLighting(); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGSpatha.h b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGSpatha.h new file mode 100644 index 0000000..8a6d221 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGSpatha.h @@ -0,0 +1,54 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusROGSpatha.h | +| | +| RGBController for ASUS ROG Spatha | +| | +| Mola19 05 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraMouseGen1Controller.h" + +enum +{ + ASUS_ROG_SPATHA_BRIGHTNESS_MIN = 0, + ASUS_ROG_SPATHA_BRIGHTNESS_MAX = 15, + ASUS_ROG_SPATHA_BRIGHTNESS_DEFAULT = 15 +}; + +enum +{ + ASUS_ROG_SPATHA_MODE_DIRECT = 0xFF, + ASUS_ROG_SPATHA_MODE_STATIC = 0x01, + ASUS_ROG_SPATHA_MODE_SPECTRUM_CYCLE = 0x05, + ASUS_ROG_SPATHA_MODE_RANDOM = 0x06, + ASUS_ROG_SPATHA_MODE_BREATHING = 0x0A, + ASUS_ROG_SPATHA_MODE_BATTERY = 0x0B, + ASUS_ROG_SPATHA_MODE_REACTIVE = 0x0C, +}; + +class RGBController_AsusROGSpatha : public RGBController +{ +public: + RGBController_AsusROGSpatha(AsusAuraMouseGen1Controller* controller_ptr); + ~RGBController_AsusROGSpatha(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + AsusAuraMouseGen1Controller* controller; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGStrixEvolve.cpp b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGStrixEvolve.cpp new file mode 100644 index 0000000..20ad3de --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGStrixEvolve.cpp @@ -0,0 +1,152 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusROGStrixEvolve.cpp | +| | +| RGBController for ASUS ROG Evolve | +| | +| Mola19 30 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusROGStrixEvolve.h" + +/**------------------------------------------------------------------*\ + @name Asus Aura Strix Evolve + @category Mouse + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraUSBStrixEvolve + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AsusROGStrixEvolve::RGBController_AsusROGStrixEvolve(AsusAuraMouseGen1Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "ASUS"; + type = DEVICE_TYPE_MOUSE; + description = "ASUS Aura Mouse Device"; + version = controller->GetVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ASUS_ROG_STRIX_EVOLVE_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_MIN; + Direct.brightness_max = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_MAX; + Direct.brightness = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_DEFAULT; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ASUS_ROG_STRIX_EVOLVE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.brightness_min = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_MIN; + Breathing.brightness_max = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_MAX; + Breathing.brightness = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_DEFAULT; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Spectrum Cycle"; + ColorCycle.value = ASUS_ROG_STRIX_EVOLVE_MODE_SPECTRUM_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + ColorCycle.brightness_min = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_MIN; + ColorCycle.brightness_max = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_MAX; + ColorCycle.brightness = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_DEFAULT; + ColorCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(ColorCycle); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = ASUS_ROG_STRIX_EVOLVE_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Reactive.brightness_min = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_MIN; + Reactive.brightness_max = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_MAX; + Reactive.brightness = ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_DEFAULT; + Reactive.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Reactive); + + SetupZones(); +} + +RGBController_AsusROGStrixEvolve::~RGBController_AsusROGStrixEvolve() +{ + delete controller; +} + +void RGBController_AsusROGStrixEvolve::SetupZones() +{ + zone mouse_zone; + + mouse_zone.name = "Underglow"; + mouse_zone.type = ZONE_TYPE_SINGLE; + mouse_zone.leds_min = 1; + mouse_zone.leds_max = 1; + mouse_zone.leds_count = 1; + mouse_zone.matrix_map = NULL; + + zones.push_back(mouse_zone); + + led mouse_led; + + mouse_led.name = "Underglow"; + mouse_led.value = 1; + + leds.push_back(mouse_led); + + SetupColors(); +} + +void RGBController_AsusROGStrixEvolve::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_AsusROGStrixEvolve::DeviceUpdateLEDs() +{ + UpdateSingleLED(0); +} + +void RGBController_AsusROGStrixEvolve::UpdateZoneLEDs(int zone) +{ + UpdateSingleLED(zone); +} + +void RGBController_AsusROGStrixEvolve::UpdateSingleLED(int /*led*/) +{ + controller->SendUpdate(0x1C, RGBGetRValue(colors[0])); + controller->SendUpdate(0x1D, RGBGetGValue(colors[0])); + controller->SendUpdate(0x1E, RGBGetBValue(colors[0])); +} + +void RGBController_AsusROGStrixEvolve::DeviceUpdateMode() +{ + controller->SendUpdate(0x19, modes[active_mode].value); + controller->SendUpdate(0x1A, modes[active_mode].brightness); +} + +void RGBController_AsusROGStrixEvolve::DeviceSaveMode() +{ + unsigned int profile = controller->GetActiveProfile(); + + + controller->UpdateProfile(0x19, profile, modes[active_mode].value); + controller->UpdateProfile(0x1A, profile, modes[active_mode].brightness); + + if(modes[active_mode].value != ASUS_ROG_STRIX_EVOLVE_MODE_SPECTRUM_CYCLE) + { + controller->UpdateProfile(0x1C, profile, RGBGetRValue(colors[0])); + controller->UpdateProfile(0x1D, profile, RGBGetGValue(colors[0])); + controller->UpdateProfile(0x1E, profile, RGBGetBValue(colors[0])); + } + + controller->ResetToSavedLighting(); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGStrixEvolve.h b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGStrixEvolve.h new file mode 100644 index 0000000..88e30ae --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGStrixEvolve.h @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusROGStrixEvolve.h | +| | +| RGBController for ASUS ROG Evolve | +| | +| Mola19 30 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraMouseGen1Controller.h" + +enum +{ + ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_MIN = 0, + ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_MAX = 255, + ASUS_ROG_STRIX_EVOLVE_BRIGHTNESS_DEFAULT = 255, +}; + +enum +{ + ASUS_ROG_STRIX_EVOLVE_MODE_DIRECT = 0x01, + ASUS_ROG_STRIX_EVOLVE_MODE_BREATHING = 0x02, + ASUS_ROG_STRIX_EVOLVE_MODE_SPECTRUM_CYCLE = 0x03, + ASUS_ROG_STRIX_EVOLVE_MODE_REACTIVE = 0x04, +}; + +class RGBController_AsusROGStrixEvolve : public RGBController +{ +public: + RGBController_AsusROGStrixEvolve(AsusAuraMouseGen1Controller* controller_ptr); + ~RGBController_AsusROGStrixEvolve(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + AsusAuraMouseGen1Controller* controller; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMousematController/AsusAuraMousematController.cpp b/Controllers/AsusAuraUSBController/AsusAuraMousematController/AsusAuraMousematController.cpp new file mode 100644 index 0000000..c0981f2 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMousematController/AsusAuraMousematController.cpp @@ -0,0 +1,140 @@ +/*---------------------------------------------------------*\ +| AsusAuraMousematController.cpp | +| | +| Driver for ASUS Aura mousemat | +| | +| Adam Honse (CalcProgrammer1) 10 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusAuraMousematController.h" +#include "StringUtils.h" + +AuraMousematController::AuraMousematController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +AuraMousematController::~AuraMousematController() +{ + hid_close(dev); +} + +std::string AuraMousematController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AuraMousematController::GetName() +{ + return(name); +} + +std::string AuraMousematController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string AuraMousematController::GetVersion() +{ + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0xEE; + usb_buf[0x01] = 0x12; + usb_buf[0x02] = 0x00; + + hid_write(dev, usb_buf, 65); + + unsigned char usb_buf_out[65]; + hid_read(dev, usb_buf_out, 65); + + char version[9]; + snprintf(version, 9, "%X.%02X.%02X", usb_buf_out[6], usb_buf_out[7], usb_buf_out[8]); + return std::string(version); +} + +void AuraMousematController::UpdateLeds + ( + std::vector colors + ) +{ + unsigned char usb_buf[65]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0xEE; + usb_buf[0x01] = 0xC0; + usb_buf[0x02] = 0x81; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x00; + + for(unsigned int i = 0; i < 60; i += 4) + { + usb_buf[5 + i] = 0x00; + usb_buf[6 + i] = RGBGetRValue(colors[i / 4]); + usb_buf[7 + i] = RGBGetGValue(colors[i / 4]); + usb_buf[8 + i] = RGBGetBValue(colors[i / 4]); + } + + hid_write(dev, usb_buf, 65); +} + +void AuraMousematController::UpdateDevice + ( + unsigned char mode, + std::vector colors, + unsigned char speed, + unsigned char brightness, + unsigned char pattern + ) +{ + unsigned char usb_buf[65]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0xEE; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x28; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = mode; + usb_buf[0x06] = speed; + usb_buf[0x07] = brightness; + usb_buf[0x08] = pattern; + usb_buf[0x09] = 0x00; + + for(unsigned int i = 0; i < colors.size(); i++) + { + usb_buf[0x0a + i * 3] = RGBGetRValue(colors[i]); + usb_buf[0x0b + i * 3] = RGBGetGValue(colors[i]); + usb_buf[0x0c + i * 3] = RGBGetBValue(colors[i]); + } + + hid_write(dev, usb_buf, 65); +} + +void AuraMousematController::SaveMode() +{ + unsigned char usb_save_buf[65]; + + memset(usb_save_buf, 0x00, sizeof(usb_save_buf)); + + usb_save_buf[0x00] = 0xEE; + usb_save_buf[0x01] = 0x50; + usb_save_buf[0x02] = 0x03; + + hid_write(dev, usb_save_buf, 65); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraMousematController/AsusAuraMousematController.h b/Controllers/AsusAuraUSBController/AsusAuraMousematController/AsusAuraMousematController.h new file mode 100644 index 0000000..21569da --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMousematController/AsusAuraMousematController.h @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| AsusAuraMousematController.h | +| | +| Driver for ASUS Aura mousemat | +| | +| Adam Honse (CalcProgrammer1) 10 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +enum +{ + AURA_MOUSEMAT_MODE_STATIC = 0, + AURA_MOUSEMAT_MODE_BREATHING = 1, + AURA_MOUSEMAT_MODE_COLOR_CYCLE = 2, + AURA_MOUSEMAT_MODE_WAVE = 3, + AURA_MOUSEMAT_MODE_WAVE_PLANE = 4, + AURA_MOUSEMAT_MODE_COMET = 5, + AURA_MOUSEMAT_MODE_GLOWING_YOYO = 6, + AURA_MOUSEMAT_MODE_CROSS = 7, + AURA_MOUSEMAT_MODE_STARRY_NIGHT = 8, + AURA_MOUSEMAT_MODE_DIRECT = 0xFF, +}; + +class AuraMousematController +{ +public: + AuraMousematController(hid_device* dev_handle, const char* path, std::string dev_name); + virtual ~AuraMousematController(); + + std::string GetDeviceLocation(); + std::string GetName(); + std::string GetSerialString(); + std::string GetVersion(); + + void UpdateLeds + ( + std::vector colors + ); + + void UpdateDevice + ( + unsigned char mode, + std::vector colors, + unsigned char speed, + unsigned char brightness, + unsigned char pattern + ); + + void SaveMode(); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraMousematController/RGBController_AsusAuraMousemat.cpp b/Controllers/AsusAuraUSBController/AsusAuraMousematController/RGBController_AsusAuraMousemat.cpp new file mode 100644 index 0000000..fe2196d --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMousematController/RGBController_AsusAuraMousemat.cpp @@ -0,0 +1,300 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraMousemat.cpp | +| | +| RGBController for ASUS Aura mousemat | +| | +| Adam Honse (CalcProgrammer1) 10 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusAuraMousemat.h" + +/**------------------------------------------------------------------*\ + @name Asus Aura Mousemat + @category Mousemat + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraUSBMousemats + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AuraMousemat::RGBController_AuraMousemat(AuraMousematController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "ASUS"; + type = DEVICE_TYPE_MOUSEMAT; + description = "ASUS Aura Mousemat Device"; + version = controller->GetVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = AURA_MOUSEMAT_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = AURA_MOUSEMAT_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness_min = AURA_MOUSEMAT_BRIGHTNESS_MIN; + Static.brightness_max = AURA_MOUSEMAT_BRIGHTNESS_MAX; + Static.brightness = AURA_MOUSEMAT_BRIGHTNESS_DEFAULT; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AURA_MOUSEMAT_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC | MODE_COLORS_RANDOM; + Breathing.speed_min = AURA_MOUSEMAT_SPEED_MIN; + Breathing.speed_max = AURA_MOUSEMAT_SPEED_MAX; + Breathing.speed = AURA_MOUSEMAT_SPEED_DEFAULT_BREATHING; + Breathing.brightness_min = AURA_MOUSEMAT_BRIGHTNESS_MIN; + Breathing.brightness_max = AURA_MOUSEMAT_BRIGHTNESS_MAX; + Breathing.brightness = AURA_MOUSEMAT_BRIGHTNESS_DEFAULT; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = AURA_MOUSEMAT_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + ColorCycle.color_mode = MODE_COLORS_NONE; + ColorCycle.speed_min = AURA_MOUSEMAT_SPEED_MIN; + ColorCycle.speed_max = AURA_MOUSEMAT_SPEED_MAX; + ColorCycle.speed = AURA_MOUSEMAT_SPEED_DEFAULT_COLOR_CYCLE; + ColorCycle.brightness_min = AURA_MOUSEMAT_BRIGHTNESS_MIN; + ColorCycle.brightness_max = AURA_MOUSEMAT_BRIGHTNESS_MAX; + ColorCycle.brightness = AURA_MOUSEMAT_BRIGHTNESS_DEFAULT; + modes.push_back(ColorCycle); + + mode Wave; + Wave.name = "Wave"; + Wave.value = AURA_MOUSEMAT_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_MANUAL_SAVE; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.speed_min = AURA_MOUSEMAT_SPEED_MIN; + Wave.speed_max = AURA_MOUSEMAT_SPEED_MAX; + Wave.speed = AURA_MOUSEMAT_SPEED_DEFAULT_WAVE; + Wave.brightness_min = AURA_MOUSEMAT_BRIGHTNESS_MIN; + Wave.brightness_max = AURA_MOUSEMAT_BRIGHTNESS_MAX; + Wave.brightness = AURA_MOUSEMAT_BRIGHTNESS_DEFAULT; + Wave.direction = MODE_DIRECTION_LEFT; + Wave.colors_min = 7; + Wave.colors_max = 7; + Wave.colors.resize(7); + modes.push_back(Wave); + + mode WavePlane; + WavePlane.name = "Wave Plane"; + WavePlane.value = AURA_MOUSEMAT_MODE_WAVE_PLANE; + WavePlane.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_MANUAL_SAVE; + WavePlane.color_mode = MODE_COLORS_NONE; + WavePlane.speed_min = AURA_MOUSEMAT_SPEED_MIN; + WavePlane.speed_max = AURA_MOUSEMAT_SPEED_MAX; + WavePlane.speed = AURA_MOUSEMAT_SPEED_DEFAULT_WAVE_PLANE; + WavePlane.brightness_min = AURA_MOUSEMAT_BRIGHTNESS_MIN; + WavePlane.brightness_max = AURA_MOUSEMAT_BRIGHTNESS_MAX; + WavePlane.brightness = AURA_MOUSEMAT_BRIGHTNESS_DEFAULT; + WavePlane.direction = MODE_DIRECTION_LEFT; + modes.push_back(WavePlane); + + mode Comet; + Comet.name = "Comet"; + Comet.value = AURA_MOUSEMAT_MODE_COMET; + Comet.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_MANUAL_SAVE; + Comet.color_mode = MODE_COLORS_MODE_SPECIFIC; + Comet.speed_min = AURA_MOUSEMAT_SPEED_MIN; + Comet.speed_max = AURA_MOUSEMAT_SPEED_MAX; + Comet.speed = AURA_MOUSEMAT_SPEED_DEFAULT_COMET; + Comet.brightness_min = AURA_MOUSEMAT_BRIGHTNESS_MIN; + Comet.brightness_max = AURA_MOUSEMAT_BRIGHTNESS_MAX; + Comet.brightness = AURA_MOUSEMAT_BRIGHTNESS_DEFAULT; + Comet.direction = MODE_DIRECTION_LEFT; + Comet.colors_min = 1; + Comet.colors_max = 1; + Comet.colors.resize(1); + modes.push_back(Comet); + + mode GlowingYoyo; + GlowingYoyo.name = "Glowing Yoyo"; + GlowingYoyo.value = AURA_MOUSEMAT_MODE_GLOWING_YOYO; + GlowingYoyo.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_MANUAL_SAVE; + GlowingYoyo.color_mode = MODE_COLORS_NONE; + GlowingYoyo.speed_min = AURA_MOUSEMAT_SPEED_MIN; + GlowingYoyo.speed_max = AURA_MOUSEMAT_SPEED_MAX; + GlowingYoyo.speed = AURA_MOUSEMAT_SPEED_DEFAULT_GLOWING_YOYO; + GlowingYoyo.brightness_min = AURA_MOUSEMAT_BRIGHTNESS_MIN; + GlowingYoyo.brightness_max = AURA_MOUSEMAT_BRIGHTNESS_MAX; + GlowingYoyo.brightness = AURA_MOUSEMAT_BRIGHTNESS_DEFAULT; + GlowingYoyo.direction = MODE_DIRECTION_LEFT; + modes.push_back(GlowingYoyo); + + mode Cross; + Cross.name = "Cross"; + Cross.value = AURA_MOUSEMAT_MODE_CROSS; + Cross.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Cross.color_mode = MODE_COLORS_MODE_SPECIFIC; + Cross.speed_min = AURA_MOUSEMAT_SPEED_MIN; + Cross.speed_max = AURA_MOUSEMAT_SPEED_MAX; + Cross.speed = AURA_MOUSEMAT_SPEED_DEFAULT_CROSS; + Cross.brightness_min = AURA_MOUSEMAT_BRIGHTNESS_MIN; + Cross.brightness_max = AURA_MOUSEMAT_BRIGHTNESS_MAX; + Cross.brightness = AURA_MOUSEMAT_BRIGHTNESS_DEFAULT; + Cross.colors_min = 2; + Cross.colors_max = 2; + Cross.colors.resize(2); + modes.push_back(Cross); + + mode StarryNight; + StarryNight.name = "Starry Night"; + StarryNight.value = AURA_MOUSEMAT_MODE_STARRY_NIGHT; + StarryNight.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + StarryNight.color_mode = MODE_COLORS_MODE_SPECIFIC | MODE_COLORS_RANDOM; + StarryNight.speed_min = AURA_MOUSEMAT_SPEED_MIN; + StarryNight.speed_max = AURA_MOUSEMAT_SPEED_MAX; + StarryNight.speed = AURA_MOUSEMAT_SPEED_DEFAULT_STARRY_NIGHT; + StarryNight.brightness_min = AURA_MOUSEMAT_BRIGHTNESS_MIN; + StarryNight.brightness_max = AURA_MOUSEMAT_BRIGHTNESS_MAX; + StarryNight.brightness = AURA_MOUSEMAT_BRIGHTNESS_DEFAULT; + StarryNight.colors_min = 2; + StarryNight.colors_max = 2; + StarryNight.colors.resize(2); + modes.push_back(StarryNight); + + SetupZones(); +} + +RGBController_AuraMousemat::~RGBController_AuraMousemat() +{ + delete controller; +} + +void RGBController_AuraMousemat::SetupZones() +{ + zone mousemat_zone; + + mousemat_zone.name = "Mousemat"; + mousemat_zone.type = ZONE_TYPE_LINEAR; + mousemat_zone.leds_min = 15; + mousemat_zone.leds_max = 15; + mousemat_zone.leds_count = 15; + mousemat_zone.matrix_map = NULL; + + zones.push_back(mousemat_zone); + + for(unsigned int i = 0; i < 15; i++) + { + led mousemat_led; + + mousemat_led.name = "Mousemat LED " + std::to_string(i); + + leds.push_back(mousemat_led); + } + + SetupColors(); +} + +void RGBController_AuraMousemat::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AuraMousemat::DeviceUpdateLEDs() +{ + controller->UpdateLeds(std::vector(colors)); +} + +void RGBController_AuraMousemat::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraMousemat::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraMousemat::DeviceUpdateMode() +{ + if(modes[active_mode].value == AURA_MOUSEMAT_MODE_DIRECT) + { + DeviceUpdateLEDs(); + } + else + { + int pattern = 0; + + switch(modes[active_mode].value) + { + case AURA_MOUSEMAT_MODE_BREATHING: + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + pattern = 2; + } + else + { + pattern = (int)modes[active_mode].colors.size() - 1; + } + break; + case AURA_MOUSEMAT_MODE_WAVE: + pattern = (int)modes[active_mode].colors.size() * 16 + modes[active_mode].direction; + break; + case AURA_MOUSEMAT_MODE_WAVE_PLANE: + switch(modes[active_mode].direction) + { + case MODE_DIRECTION_LEFT: + pattern = 2; + break; + case MODE_DIRECTION_RIGHT: + pattern = 3; + break; + case MODE_DIRECTION_UP: + pattern = 0; + break; + case MODE_DIRECTION_DOWN: + pattern = 1; + break; + } + break; + case AURA_MOUSEMAT_MODE_COMET: + pattern = modes[active_mode].direction; + if(pattern == MODE_DIRECTION_HORIZONTAL || pattern == MODE_DIRECTION_VERTICAL) pattern = 2; + break; + case AURA_MOUSEMAT_MODE_GLOWING_YOYO: + pattern = modes[active_mode].direction; + break; + case AURA_MOUSEMAT_MODE_STARRY_NIGHT: + pattern = 16 + (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + break; + default: + pattern = 255; + break; + } + + controller->UpdateDevice(modes[active_mode].value, std::vector(modes[active_mode].colors), modes[active_mode].speed, modes[active_mode].brightness, pattern); + } +} + +void RGBController_AuraMousemat::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->SaveMode(); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraMousematController/RGBController_AsusAuraMousemat.h b/Controllers/AsusAuraUSBController/AsusAuraMousematController/RGBController_AsusAuraMousemat.h new file mode 100644 index 0000000..5c91585 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraMousematController/RGBController_AsusAuraMousemat.h @@ -0,0 +1,54 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraMousemat.h | +| | +| RGBController for ASUS Aura mousemat | +| | +| Mola19 06 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraMousematController.h" + +enum +{ + AURA_MOUSEMAT_BRIGHTNESS_MIN = 0, + AURA_MOUSEMAT_BRIGHTNESS_MAX = 4, + AURA_MOUSEMAT_BRIGHTNESS_DEFAULT = 4, + AURA_MOUSEMAT_SPEED_MIN = 127, + AURA_MOUSEMAT_SPEED_MAX = 0, + AURA_MOUSEMAT_SPEED_DEFAULT_STATIC = 0, + AURA_MOUSEMAT_SPEED_DEFAULT_BREATHING = 1, + AURA_MOUSEMAT_SPEED_DEFAULT_COLOR_CYCLE = 1, + AURA_MOUSEMAT_SPEED_DEFAULT_WAVE = 14, + AURA_MOUSEMAT_SPEED_DEFAULT_WAVE_PLANE = 2, + AURA_MOUSEMAT_SPEED_DEFAULT_COMET = 2, + AURA_MOUSEMAT_SPEED_DEFAULT_GLOWING_YOYO = 4, + AURA_MOUSEMAT_SPEED_DEFAULT_CROSS = 12, + AURA_MOUSEMAT_SPEED_DEFAULT_STARRY_NIGHT = 2, +}; + +class RGBController_AuraMousemat : public RGBController +{ +public: + RGBController_AuraMousemat(AuraMousematController* controller_ptr); + ~RGBController_AuraMousemat(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + AuraMousematController* controller; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/AsusAuraRyuoAIOController.cpp b/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/AsusAuraRyuoAIOController.cpp new file mode 100644 index 0000000..10de367 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/AsusAuraRyuoAIOController.cpp @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| AsusAuraRyuoAIOController.cpp | +| | +| Driver for ASUS Aura Ryuo | +| | +| Cooper Hall (geobot19 / Geo_bot) 08 Apr 2022 | +| using snipets from Chris M (Dr.No) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "AsusAuraRyuoAIOController.h" + +AsusAuraRyuoAIOController::AsusAuraRyuoAIOController(hid_device* dev_handle, const char* path, std::string dev_name) : AuraUSBController(dev_handle, path, dev_name) +{ + /*-----------------------------------------------------*\ + | Add addressable devices | + | Manually adding device info for now | + | TODO: Implement config table accurately | + \*-----------------------------------------------------*/ + uint8_t leds = (dev_name.find("Ryujin") != std::string::npos) ? 5 : 12; + + device_info.push_back({0x00, 0x00, leds, 0, AuraDeviceType::FIXED}); + +} + +AsusAuraRyuoAIOController::~AsusAuraRyuoAIOController() +{ + /*---------------------------------------------------------*\ + | HID device is closed in the base class | + \*---------------------------------------------------------*/ +} + +std::string AsusAuraRyuoAIOController::GetLocation() +{ + return("HID: " + location); +} + +void AsusAuraRyuoAIOController::SetMode(unsigned char /*channel*/, unsigned char /*mode*/, unsigned char /*red*/, unsigned char /*grn*/, unsigned char /*blu*/) +{ + /*---------------------------------------------------------*\ + | This interface is not used in this controller however is | + | required by the abstract class | + \*---------------------------------------------------------*/ +} + +void AsusAuraRyuoAIOController::SetMode(unsigned char mode, unsigned char speed, unsigned char direction, RGBColor colour) +{ + //check if update is needed + if(!((current_mode == mode) && (ToRGBColor(current_red, current_green, current_blue) == colour) && (current_speed == speed) && (current_direction == direction))) + { + current_mode = mode; + current_speed = speed; + current_direction = direction; + current_red = RGBGetRValue(colour); + current_green = RGBGetGValue(colour); + current_blue = RGBGetBValue(colour); + SendUpdate(); + } +} + +void AsusAuraRyuoAIOController::SetChannelLEDs(unsigned char /*channel*/, RGBColor* /*colors*/, unsigned int /*num_colors*/) +{ + /*---------------------------------------------------------*\ + | This interface is not used in this controller however is | + | required by the abstract class | + \*---------------------------------------------------------*/ +} + +void AsusAuraRyuoAIOController::SetLedsDirect(RGBColor * led_colours, uint8_t led_count) +{ + uint8_t buffer[write_packet_size] = { 0xEC, 0x40, 0x80, 0x00, led_count }; + + /*---------------------------------------------------------*\ + | Set the colour bytes in the packet | + \*---------------------------------------------------------*/ + for(uint8_t index = 0; index < led_count; index++) + { + uint8_t offset = (index * 3) + RED_BYTE; + + buffer[offset + 0] = RGBGetRValue(led_colours[index]); + buffer[offset + 1] = RGBGetGValue(led_colours[index]); + buffer[offset + 2] = RGBGetBValue(led_colours[index]); + } + + hid_write(dev, buffer, write_packet_size); +} + +void AsusAuraRyuoAIOController::GetStatus() +{ + uint8_t buffer[write_packet_size] = { 0xEC, 0x01, 0x02 }; + + hid_write(dev, buffer, write_packet_size); + hid_read_timeout(dev, buffer, read_packet_size, ASUSAURARYUOAIOCONTROLLER_TIMEOUT); + + current_red = buffer[RED_BYTE - 1]; + current_green = buffer[GREEN_BYTE - 1]; + current_blue = buffer[BLUE_BYTE - 1]; +} + +void AsusAuraRyuoAIOController::SendUpdate() +{ + uint8_t buffer[write_packet_size]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buffer, 0x00, write_packet_size); + + buffer[REPORTID_BYTE] = reportid; + buffer[COMMAND_BYTE] = modefx; + buffer[ZONE_BYTE] = 0; + buffer[PROGRAM_ID_BYTE] = program_id; + buffer[MODE_BYTE] = current_mode; + buffer[RED_BYTE] = current_red; + buffer[GREEN_BYTE] = current_green; + buffer[BLUE_BYTE] = current_blue; + + buffer[DIRECTION_BYTE]= current_direction; + buffer[SPEED_BYTE] = current_speed; + + hid_write(dev, buffer, write_packet_size); +} + diff --git a/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/AsusAuraRyuoAIOController.h b/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/AsusAuraRyuoAIOController.h new file mode 100644 index 0000000..c4af2aa --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/AsusAuraRyuoAIOController.h @@ -0,0 +1,96 @@ +/*---------------------------------------------------------*\ +| AsusAuraRyuoAIOController.h | +| | +| Driver for ASUS Aura Ryuo | +| | +| Cooper Hall (geobot19 / Geo_bot) 08 Apr 2022 | +| using snipets from Chris M (Dr.No) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "AsusAuraUSBController.h" + +#define ASUSAURARYUOAIOCONTROLLER_TIMEOUT 250 +#define ASUSAURARYUOAIOCONTROLLER_HID_MAX_STR 255 + +#define ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MIN 0 +#define ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX 255 + +class AsusAuraRyuoAIOController : public AuraUSBController +{ +public: + enum + { + MODE_DIRECT = 0xFF, //Direct Led Control - Independently set LEDs in zone + MODE_STATIC = 0x01, //Static Mode - Set entire zone to a single color. + MODE_BREATHING = 0x02, //Breathing Mode - Fades between fully off and fully on. + MODE_FLASHING = 0x03, //Flashing Mode - Abruptly changing between fully off and fully on. + MODE_SPECTRUM = 0x04, //Spectrum Cycle Mode - Cycles through the color spectrum on all lights on the device + MODE_RAINBOW = 0x05, //Rainbow Wave Mode - Cycle thru the color spectrum as a wave across all LEDs + MODE_FLASHANDDASH = 0x0A, //Flash n Dash - Flash twice and then flash in direction + }; + + enum PacketMap + { + REPORTID_BYTE = 0, + COMMAND_BYTE = 1, + ZONE_BYTE = 2, + PROGRAM_ID_BYTE= 3, + MODE_BYTE = 4, + RED_BYTE = 5, + GREEN_BYTE = 6, + BLUE_BYTE = 7, + DIRECTION_BYTE = 8, + SPEED_BYTE = 9, + }; + + enum + { + SPEED_SLOWEST = 0x04, // Slowest speed + SPEED_SLOW = 0x03, // Slower speed + SPEED_NORMAL = 0x02, // Normal speed + SPEED_FAST = 0x01, // Fast speed + SPEED_FASTEST = 0x00, // Fastest speed + }; + + AsusAuraRyuoAIOController(hid_device* dev_handle, const char* path, std::string dev_name); + ~AsusAuraRyuoAIOController(); + + std::string GetLocation(); + + void SetChannelLEDs(unsigned char channel, RGBColor *colors, unsigned int num_colors); + void SetLedsDirect(RGBColor * led_colours, uint8_t led_count); + + void SetMode(unsigned char channel, unsigned char mode, unsigned char red, unsigned char grn, unsigned char blu); + void SetMode(unsigned char mode, unsigned char speed, unsigned char direction, RGBColor colour); +private: + static const uint8_t read_packet_size = 65; + static const uint8_t write_packet_size = 65; + static const uint8_t modefx = 0x3B; + static const uint8_t direct = 0x40; + static const uint8_t reportid = 0xEC; + static const uint8_t program_id = 0x22; + + std::string location; + + uint8_t zone_index; + uint8_t current_mode; + uint8_t current_speed; + + uint8_t current_red; + uint8_t current_green; + uint8_t current_blue; + uint8_t current_direction; + + void GetStatus(); + void SendUpdate(); + void SendEffect(unsigned char channel, unsigned char mode, unsigned char red, unsigned char grn, unsigned char blu); + void SendDirectApply(unsigned char channel); +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/RGBController_AsusAuraRyuoAIO.cpp b/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/RGBController_AsusAuraRyuoAIO.cpp new file mode 100644 index 0000000..ece46d8 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/RGBController_AsusAuraRyuoAIO.cpp @@ -0,0 +1,220 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraRyuoAIO.cpp | +| | +| RGBController for ASUS Aura Ryuo | +| | +| Cooper Hall (geobot19 / Geo_bot) 08 Apr 2022 | +| using snipets from Chris M (Dr.No) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_AsusAuraRyuoAIO.h" + +/**------------------------------------------------------------------*\ + @name Asus Aura Ryuo AIO + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraUSBRyuoAIO + @category Cooler + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AsusAuraRyuoAIO::RGBController_AsusAuraRyuoAIO(AsusAuraRyuoAIOController *controller_ptr) +{ + controller = controller_ptr; + uint8_t speed = controller->SPEED_NORMAL; + + name = controller->GetDeviceName(); + vendor = "ASUS"; + type = DEVICE_TYPE_COOLER; + description = "ASUS Liquid Cooler with 120mm and 240mm radiators."; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = controller->MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = controller->MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = controller->MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(Breathing.colors_max); + Breathing.brightness_min = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MIN; + Breathing.brightness_max = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX; + Breathing.brightness = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX; + Breathing.speed_min = controller->SPEED_SLOWEST; + Breathing.speed_max = controller->SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = speed; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = controller->MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Flashing.colors_min = 1; + Flashing.colors_max = 1; + Flashing.colors.resize(Flashing.colors_max); + Flashing.brightness_min = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MIN; + Flashing.brightness_max = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX; + Flashing.brightness = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX; + Flashing.speed_min = controller->SPEED_SLOWEST; + Flashing.speed_max = controller->SPEED_FASTEST; + Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flashing.speed = speed; + modes.push_back(Flashing); + + mode Spectrum; + Spectrum.name = "Spectrum"; + Spectrum.value = controller->MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_SPEED; + Spectrum.brightness_min = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MIN; + Spectrum.brightness_max = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX; + Spectrum.brightness = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX; + Spectrum.speed_min = controller->SPEED_SLOWEST; + Spectrum.speed_max = controller->SPEED_FASTEST; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.speed = speed; + modes.push_back(Spectrum); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = controller->MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.brightness_min = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MIN; + Rainbow.brightness_max = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX; + Rainbow.brightness = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX; + Rainbow.speed_min = controller->SPEED_SLOWEST; + Rainbow.speed_max = controller->SPEED_FASTEST; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed = speed; + modes.push_back(Rainbow); + + mode FlashAndDash; + FlashAndDash.name = "Flash and Dash"; + FlashAndDash.value = controller->MODE_FLASHANDDASH; + FlashAndDash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + FlashAndDash.brightness_min = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MIN; + FlashAndDash.brightness_max = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX; + FlashAndDash.brightness = ASUSAURARYUOAIOCONTROLLER_BRIGHTNESS_MAX; + FlashAndDash.speed_min = controller->SPEED_SLOWEST; + FlashAndDash.speed_max = controller->SPEED_FASTEST; + FlashAndDash.color_mode = MODE_COLORS_NONE; + FlashAndDash.speed = speed; + modes.push_back(FlashAndDash); + + SetupZones(); +} + +RGBController_AsusAuraRyuoAIO::~RGBController_AsusAuraRyuoAIO() +{ + delete controller; +} + +void RGBController_AsusAuraRyuoAIO::SetupZones() +{ + /*-------------------------------------------------*\ + | Set up zones | + \*-------------------------------------------------*/ + LOG_DEBUG("[%s] - Get channel count: %i", name.c_str(), controller->GetChannelCount()); + + zones.resize(controller->GetChannelCount()); + + LOG_DEBUG("[%s] - Creating Zones and LEDs", name.c_str()); + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + AuraDeviceInfo device_info = controller->GetAuraDevices()[zone_idx]; + LOG_INFO("[%s] %s Zone %i - Header Count %i LED Count %i FX %02X Direct %02X", name.c_str(), + ((device_info.device_type == AuraDeviceType::FIXED) ? "Fixed" : "Addressable"), + zone_idx, device_info.num_headers, device_info.num_leds, device_info.effect_channel, device_info.direct_channel); + + zones[zone_idx].name = name + " Zone "; + zones[zone_idx].name.append(std::to_string(zone_idx)); + zones[zone_idx].type = ZONE_TYPE_LINEAR; + zones[zone_idx].leds_min = device_info.num_leds; + zones[zone_idx].leds_max = device_info.num_leds; + zones[zone_idx].leds_count = device_info.num_leds; + + for(unsigned int lp_idx = 0; lp_idx < zones[zone_idx].leds_count; lp_idx++) + { + led new_led; + + new_led.name = zones[zone_idx].name; + new_led.name.append(" LED " + std::to_string(lp_idx)); + new_led.value = lp_idx; + + leds.push_back(new_led); + } + } + + LOG_DEBUG("[%s] - Device zones and LEDs set", name.c_str()); + SetupColors(); +} + +void RGBController_AsusAuraRyuoAIO::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AsusAuraRyuoAIO::DeviceUpdateLEDs() +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + UpdateZoneLEDs(zone_idx); + } +} + +void RGBController_AsusAuraRyuoAIO::UpdateZoneLEDs(int zone) +{ + controller->SetLedsDirect(zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_AsusAuraRyuoAIO::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(GetLED_Zone(led)); +} + +void RGBController_AsusAuraRyuoAIO::DeviceUpdateMode() +{ + RGBColor colour = (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) ? modes[active_mode].colors[0] : 0; + + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].direction, colour); +} + +int RGBController_AsusAuraRyuoAIO::GetLED_Zone(int led_idx) +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + int zone_start = zones[zone_idx].start_idx; + int zone_end = zone_start + zones[zone_idx].leds_count - 1; + + if(zone_start <= led_idx && zone_end >= led_idx) + { + return(zone_idx); + } + } + + return(-1); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/RGBController_AsusAuraRyuoAIO.h b/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/RGBController_AsusAuraRyuoAIO.h new file mode 100644 index 0000000..baa6560 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/RGBController_AsusAuraRyuoAIO.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraRyuoAIO.h | +| | +| RGBController for ASUS Aura Ryuo | +| | +| Cooper Hall (geobot19 / Geo_bot) 08 Apr 2022 | +| using snipets from Chris M (Dr.No) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "LogManager.h" +#include "RGBController.h" +#include "AsusAuraRyuoAIOController.h" + +class RGBController_AsusAuraRyuoAIO : public RGBController +{ +public: + RGBController_AsusAuraRyuoAIO(AsusAuraRyuoAIOController* controller_ptr); + ~RGBController_AsusAuraRyuoAIO(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + int GetDeviceMode(); + int GetLED_Zone(int led_idx); + + AsusAuraRyuoAIOController* controller; +}; + diff --git a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.cpp b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.cpp new file mode 100644 index 0000000..4936ff8 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.cpp @@ -0,0 +1,733 @@ +/*---------------------------------------------------------*\ +| AsusAuraTUFKeyboardController.cpp | +| | +| Driver for ASUS Aura TUF keyboard | +| | +| Mola19 03 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include "AsusAuraTUFKeyboardController.h" +#include "StringUtils.h" + +#define HID_MAX_STR 128 + +AuraTUFKeyboardController::AuraTUFKeyboardController(hid_device* dev_handle, const char* path, uint16_t pid, unsigned short version, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + device_pid = pid; + rev_version = version; + + is_per_led_keyboard = (pid != AURA_TUF_K1_GAMING_PID && pid != AURA_TUF_K5_GAMING_PID); +} + +AuraTUFKeyboardController::~AuraTUFKeyboardController() +{ + hid_close(dev); +} + +std::string AuraTUFKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AuraTUFKeyboardController::GetName() +{ + return(name); +} + +std::string AuraTUFKeyboardController::GetSerialString() +{ + wchar_t serial_string[HID_MAX_STR]; + memset(serial_string, 0, sizeof(serial_string)); + + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + if(ret != 0) + { + return(""); + } + + /*-------------------------------------------------------------------------*\ + | Skip non-ASCII, trailing garbage in serial numbers. Required by the | + | Scope II 96, whose original firmware outputs garbage, which even differs | + | after computer reboots and therefore breaks OpenRGB profile matching. | + \*-------------------------------------------------------------------------*/ + switch(device_pid) + { + case AURA_ROG_STRIX_SCOPE_II_96_WIRELESS_USB_PID: + serial_string[12] = L'\0'; + break; + default: + break; + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string AuraTUFKeyboardController::GetVersion() +{ + if(device_pid != AURA_ROG_CLAYMORE_PID) + { + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x12; + usb_buf[0x02] = 0x00; + + ClearResponses(); + + hid_write(dev, usb_buf, 65); + + unsigned char usb_buf_out[65]; + hid_read(dev, usb_buf_out, 65); + + char version[9]; + + switch(device_pid) + { + case AURA_ROG_AZOTH_USB_PID: + case AURA_ROG_AZOTH_2_4_PID: + case AURA_TUF_K3_GAMING_PID: + case AURA_TUF_K3_GAMING_GEN_II_PID: + case AURA_ROG_STRIX_FLARE_II_ANIMATE_PID: + case AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_USB_PID: + case AURA_ROG_STRIX_SCOPE_II_PID: + case AURA_ROG_STRIX_SCOPE_II_RX_PID: + case AURA_ROG_STRIX_SCOPE_II_96_WIRELESS_USB_PID: + case AURA_ROG_STRIX_SCOPE_II_96_RX_WIRELESS_USB_PID: + snprintf(version, 9, "%02X.%02X.%02X", usb_buf_out[6], usb_buf_out[5], usb_buf_out[4]); + break; + case AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_2_4_PID: + snprintf(version, 9, "%02X.%02X.%02X", usb_buf_out[13], usb_buf_out[12], usb_buf_out[11]); + break; + default: + snprintf(version, 9, "%02X.%02X.%02X", usb_buf_out[5], usb_buf_out[6], usb_buf_out[7]); + } + + return std::string(version); + } + else + { + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x10; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = 0x00; // set to 1 to get firmware version of numpad + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); + + unsigned char usb_buf1[65]; + memset(usb_buf1, 0x00, sizeof(usb_buf1)); + usb_buf1[0x00] = 0x00; + usb_buf1[0x01] = 0x12; + usb_buf1[0x02] = 0x22; + + ClearResponses(); + hid_write(dev, usb_buf1, 65); + + unsigned char usb_buf_out[65]; + hid_read(dev, usb_buf_out, 65); + + char version[9]; + snprintf(version, 9, "%02X.%02X.%02X", usb_buf_out[8], usb_buf_out[9], usb_buf_out[10]); + + return std::string(version); + } +} + +int AuraTUFKeyboardController::GetLayout() +{ + if(device_pid != AURA_ROG_CLAYMORE_PID) + { + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x12; + usb_buf[0x02] = 0x12; + + ClearResponses(); + hid_write(dev, usb_buf, 65); + + unsigned char usb_buf_out[65]; + hid_read(dev, usb_buf_out, 65); + + return(usb_buf_out[4] * 100 + usb_buf_out[5]); + } + else + { + switch(rev_version >> 0b1100) + { + case 1: + return 117; + case 2: + return 204; + case 3: + return 221; + case 4: + return 117; + default: + return 117; + } + } +} + +/*---------------------------------------------------------*\ +| only needed for Claymore | +\*---------------------------------------------------------*/ +int AuraTUFKeyboardController::GetNumpadLocation() +{ + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x40; + usb_buf[0x02] = 0x60; + + ClearResponses(); + hid_write(dev, usb_buf, 65); + + unsigned char usb_buf_out[65]; + hid_read(dev, usb_buf_out, 65); + + return(usb_buf_out[5] * 2 + usb_buf_out[4]); +} + +void AuraTUFKeyboardController::SaveMode() +{ + unsigned char usb_save_buf[65]; + memset(usb_save_buf, 0x00, sizeof(usb_save_buf)); + + usb_save_buf[0x00] = 0x00; + usb_save_buf[0x01] = 0x50; + usb_save_buf[0x02] = 0x55; + + ClearResponses(); + hid_write(dev, usb_save_buf, 65); + AwaitResponse(60); +} + +/*---------------------------------------------------------*\ +| only needed for Claymore | +\*---------------------------------------------------------*/ +void AuraTUFKeyboardController::AllowRemoteControl(unsigned char type) +{ + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x41; + usb_buf[0x02] = type; + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); +} + +void AuraTUFKeyboardController::UpdateSingleLed + ( + int led, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + /*-----------------------------------------------------*\ + | Set up message packet for single LED | + \*-----------------------------------------------------*/ + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0] = 0x00; + usb_buf[1] = 0xC0; + usb_buf[2] = 0x81; + usb_buf[3] = 0x01; + usb_buf[4] = 0x00; + + /*-----------------------------------------------------*\ + | Convert LED index | + \*-----------------------------------------------------*/ + usb_buf[5] = led; + usb_buf[6] = red; + usb_buf[7] = green; + usb_buf[8] = blue; + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); +} + +void AuraTUFKeyboardController::UpdateLeds + ( + std::vector colors + ) +{ + int packets = (int)ceil((float)colors.size() / 15.0f); + + for(int i = 0; i < packets; i++) + { + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + int remaining = (int)colors.size() - i * 15; + + int leds = (remaining > 0x0F) ? 0x0F : remaining; + + usb_buf[0] = 0x00; + usb_buf[1] = 0xC0; + usb_buf[2] = 0x81; + usb_buf[3] = leds; + usb_buf[4] = 0x00; + + for(int j = 0; j < leds; j++) + { + usb_buf[j * 4 + 5] = (is_per_led_keyboard) ? colors[i * 15 + j].value : 0x00; + usb_buf[j * 4 + 6] = RGBGetRValue(colors[i * 15 + j].color); + usb_buf[j * 4 + 7] = RGBGetGValue(colors[i * 15 + j].color); + usb_buf[j * 4 + 8] = RGBGetBValue(colors[i * 15 + j].color); + } + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); + } +} + +void AuraTUFKeyboardController::UpdateK1Wave + ( + std::vector colors, + unsigned char direction, + unsigned char speed, + unsigned char brightness + ) +{ + unsigned char usb_buf[65]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x2C; + usb_buf[0x03] = 0x03; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = speed; + usb_buf[0x06] = brightness; + usb_buf[0x07] = 0x00; + usb_buf[0x08] = direction; + usb_buf[0x09] = 0x00; + + usb_buf[10] = 5; + usb_buf[11] = RGBGetRValue(colors[4]); + usb_buf[12] = RGBGetGValue(colors[4]); + usb_buf[13] = RGBGetBValue(colors[4]); + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); + + for(unsigned int i = 0; i < 4; i ++) + { + usb_buf[10 + i * 4] = i + 1; + usb_buf[11 + i * 4] = RGBGetRValue(colors[i]); + usb_buf[12 + i * 4] = RGBGetGValue(colors[i]); + usb_buf[13 + i * 4] = RGBGetBValue(colors[i]); + } + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); +} + +void AuraTUFKeyboardController::UpdateScopeIIRainbowRipple + ( + unsigned char mode, + std::vector colors, + unsigned char direction, + unsigned char color_mode, + unsigned char speed, + unsigned char brightness + ) +{ + unsigned char usb_buf[65]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x2C; + usb_buf[0x03] = mode; + usb_buf[0x04] = 0x02; + usb_buf[0x05] = speed; + usb_buf[0x06] = brightness; + usb_buf[0x07] = color_mode; + usb_buf[0x08] = direction; + usb_buf[0x09] = 0x02; + usb_buf[0x0A] = (unsigned char)colors.size(); + + for(unsigned int i = 0; i < 2; i++) + { + if(i >= colors.size()) + { + continue; + } + + usb_buf[11 + i * 4] = (unsigned char)(100.0f / (float)colors.size() * (i + 1)); + usb_buf[12 + i * 4] = RGBGetRValue(colors[i]); + usb_buf[13 + i * 4] = RGBGetGValue(colors[i]); + usb_buf[14 + i * 4] = RGBGetBValue(colors[i]); + } + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); + + memset(usb_buf + 4, 0x00, sizeof(usb_buf) - 4); + + usb_buf[0x04] = 0x01; + + for(unsigned int i = 0; i < 4; i++) + { + if((i + 2) >= colors.size()) + { + continue; + } + usb_buf[5 + i * 4] = (unsigned char)(100.0f / (float)colors.size() * (i + 1 + 2)); + usb_buf[6 + i * 4] = RGBGetRValue(colors[i + 2]); + usb_buf[7 + i * 4] = RGBGetGValue(colors[i + 2]); + usb_buf[8 + i * 4] = RGBGetBValue(colors[i + 2]); + } + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); + + memset(usb_buf + 4, 0x00, sizeof(usb_buf) - 4); + + usb_buf[0x04] = 0x00; + + for(unsigned int i = 0; i < 1; i ++) + { + if((i + 6) >= colors.size()) + { + continue; + } + + usb_buf[5 + i * 4] = (unsigned char)(100.0f / (float)colors.size() * (i + 1 + 6)); + usb_buf[6 + i * 4] = RGBGetRValue(colors[i + 6]); + usb_buf[7 + i * 4] = RGBGetGValue(colors[i + 6]); + usb_buf[8 + i * 4] = RGBGetBValue(colors[i + 6]); + } + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); +} + +void AuraTUFKeyboardController::UpdateScopeIIQuicksand + ( + std::vector colors, + unsigned char direction, + unsigned char color_mode, + unsigned char speed, + unsigned char brightness + ) +{ + unsigned char usb_buf[65]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x2C; + usb_buf[0x03] = 0x07; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = speed; + usb_buf[0x06] = brightness; + usb_buf[0x07] = color_mode; + usb_buf[0x08] = direction; + usb_buf[0x09] = 0x02; + + for(unsigned int i = 0; i < 3; i ++) + { + usb_buf[10 + i * 3] = RGBGetRValue(colors[i]); + usb_buf[11 + i * 3] = RGBGetGValue(colors[i]); + usb_buf[12 + i * 3] = RGBGetBValue(colors[i]); + } + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); + + memset(usb_buf + 4, 0x00, sizeof(usb_buf) - 4); + + usb_buf[0x04] = 0x00; + + for(unsigned int i = 0; i < 3; i ++) + { + usb_buf[5 + i * 3] = RGBGetRValue(colors[i+3]); + usb_buf[6 + i * 3] = RGBGetGValue(colors[i+3]); + usb_buf[7 + i * 3] = RGBGetBValue(colors[i+3]); + } + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); +} + +void AuraTUFKeyboardController::UpdateDevice + ( + unsigned char mode, + std::vector colors, + unsigned char direction, + unsigned char color_mode, + unsigned char speed, + unsigned char brightness + ) +{ + if(device_pid == AURA_TUF_K1_GAMING_PID && mode == AURA_KEYBOARD_MODE_WAVE) + { + return UpdateK1Wave(colors, direction, speed, brightness); + } + + if(device_pid == AURA_ROG_AZOTH_USB_PID + || device_pid == AURA_ROG_AZOTH_2_4_PID + || device_pid == AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_USB_PID + || device_pid == AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_2_4_PID + || device_pid == AURA_ROG_STRIX_SCOPE_II_PID + || device_pid == AURA_ROG_STRIX_SCOPE_II_RX_PID + || device_pid == AURA_ROG_STRIX_SCOPE_II_96_WIRELESS_USB_PID + || device_pid == AURA_ROG_STRIX_SCOPE_II_96_RX_WIRELESS_USB_PID) + { + if(mode == AURA_KEYBOARD_MODE_WAVE || mode == AURA_KEYBOARD_MODE_RIPPLE) + { + return UpdateScopeIIRainbowRipple(mode, colors, direction, color_mode, speed, brightness); + } + else if (mode == AURA_KEYBOARD_MODE_QUICKSAND) + { + return UpdateScopeIIQuicksand(colors, direction, color_mode, speed, brightness); + } + } + + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x2C; + + if(device_pid != AURA_ROG_CLAYMORE_PID) + { + usb_buf[0x03] = mode; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = speed; + usb_buf[0x06] = brightness; + usb_buf[0x07] = color_mode; + usb_buf[0x08] = direction; + + if(is_per_led_keyboard) + { + usb_buf[0x09] = 0x02; + + if(mode == AURA_KEYBOARD_MODE_WAVE || mode == AURA_KEYBOARD_MODE_RIPPLE) + { + usb_buf[0x0A] = (unsigned char)colors.size(); + + /*-----------------------------------------------------*\ + | Loop over every color given | + \*-----------------------------------------------------*/ + for(unsigned int i = 0; i < colors.size(); i ++) + { + if(colors[i]) + { + usb_buf[11 + i * 4] = (unsigned char)(100.0f / (float)colors.size() * (i + 1)); + usb_buf[12 + i * 4] = RGBGetRValue(colors[i]); + usb_buf[13 + i * 4] = RGBGetGValue(colors[i]); + usb_buf[14 + i * 4] = RGBGetBValue(colors[i]); + } + } + } + else + { + /*-----------------------------------------------------*\ + | Loop over Color1, Color2 and Background if there | + \*-----------------------------------------------------*/ + for(unsigned int i = 0; i != colors.size(); i++) + { + if(colors[i]) + { + usb_buf[10 + i * 3] = RGBGetRValue(colors[i]); + usb_buf[11 + i * 3] = RGBGetGValue(colors[i]); + usb_buf[12 + i * 3] = RGBGetBValue(colors[i]); + } + } + + } + } + else + { + /*-----------------------------------------------------------------*\ + | Only handles K5 Rainbow. | + | K1 is filtered out earlier and is executed in `UpdateK1Wave()` | + \*-----------------------------------------------------------------*/ + if(mode == AURA_KEYBOARD_MODE_WAVE) + { + usb_buf[0x09] = 0x05; + + for(unsigned int i = 0; i < 5; i ++) + { + usb_buf[10 + i * 4] = i + 1; + usb_buf[11 + i * 4] = RGBGetRValue(colors[i]); + usb_buf[12 + i * 4] = RGBGetGValue(colors[i]); + usb_buf[13 + i * 4] = RGBGetBValue(colors[i]); + } + } + else + { + /*-----------------------------------------------------*\ + | Loop over Color1, Color2 and Background if there | + \*-----------------------------------------------------*/ + for(unsigned int i = 0; i != colors.size(); i++) + { + if(colors[i]) + { + usb_buf[ 9 + i * 3] = RGBGetRValue(colors[i]); + usb_buf[10 + i * 3] = RGBGetGValue(colors[i]); + usb_buf[11 + i * 3] = RGBGetBValue(colors[i]); + } + } + } + } + } + else + { + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = mode; + usb_buf[0x06] = speed; + + bool random = (color_mode == 1); + bool pattern = (color_mode == 2); + + usb_buf[0x07] = random * 128 + pattern * 32 + direction; + usb_buf[0x08] = 0xFF; // "byteExt1" unknown usage + usb_buf[0x09] = 0xFF; // "byteExt2" unknown usage + usb_buf[0x0A] = (mode == 2) ? 0x80 : 0xFF; // "Lightness" (not Brightness) + + if(mode == 7) + { + UpdateQuicksandColors(colors); + } + else + { + if(mode == 4 && color_mode == 0) + { + usb_buf[11] = 0xFF; + usb_buf[12] = 0xFF; + usb_buf[13] = 0xFF; + } + + for(unsigned int i = 0; i < colors.size(); i ++) + { + if(colors[i]) + { + usb_buf[11 + i * 3] = RGBGetRValue(colors[i]); + usb_buf[12 + i * 3] = RGBGetGValue(colors[i]); + usb_buf[13 + i * 3] = RGBGetBValue(colors[i]); + } + } + } + + for(int i = 1; i < 5; i++) + { + usb_buf[5 + i * 12] = 0xFF; + } + } + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); +} + +/*---------------------------------------------------------*\ +| only needed for Claymore | +\*---------------------------------------------------------*/ +void AuraTUFKeyboardController::UpdateQuicksandColors + ( + std::vector colors + ) +{ + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x91; + + for(unsigned int i = 0; i < 6; i++) + { + if(colors[i]) + { + usb_buf[5 + i * 3] = RGBGetRValue(colors[i]); + usb_buf[6 + i * 3] = RGBGetGValue(colors[i]); + usb_buf[7 + i * 3] = RGBGetBValue(colors[i]); + } + } + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); +} + +/*---------------------------------------------------------*\ +| only needed for Claymore | +\*---------------------------------------------------------*/ +void AuraTUFKeyboardController::UpdateMode(unsigned char mode) +{ + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x28; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = mode; + + ClearResponses(); + hid_write(dev, usb_buf, 65); + AwaitResponse(20); +} + +void AuraTUFKeyboardController::AwaitResponse(int ms) +{ + unsigned char usb_buf_out[65]; + hid_read_timeout(dev, usb_buf_out, 65, ms); +} + +void AuraTUFKeyboardController::ClearResponses() +{ + int result = 1; + unsigned char usb_buf_flush[65]; + while(result > 0) + { + result = hid_read_timeout(dev, usb_buf_flush, 65, 0); + } +} + diff --git a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.h b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.h new file mode 100644 index 0000000..0485c2c --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.h @@ -0,0 +1,150 @@ +/*---------------------------------------------------------*\ +| AsusAuraTUFKeyboardController.h | +| | +| Driver for ASUS Aura TUF keyboard | +| | +| Mola19 03 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "AsusAuraTUFKeyboardLayouts.h" + +enum +{ + AURA_KEYBOARD_MODE_STATIC = 0, + AURA_KEYBOARD_MODE_BREATHING = 1, + AURA_KEYBOARD_MODE_COLOR_CYCLE = 2, + AURA_KEYBOARD_MODE_REACTIVE = 3, + AURA_KEYBOARD_MODE_WAVE = 4, + AURA_KEYBOARD_MODE_RIPPLE = 5, + AURA_KEYBOARD_MODE_STARRY_NIGHT = 6, + AURA_KEYBOARD_MODE_QUICKSAND = 7, + AURA_KEYBOARD_MODE_CURRENT = 8, + AURA_KEYBOARD_MODE_RAIN_DROP = 9, + AURA_KEYBOARD_MODE_DIRECT = 15, +}; + +enum +{ + AURA_ROG_AZOTH_USB_PID = 0x1A83, + AURA_ROG_AZOTH_2_4_PID = 0x1A85, + AURA_ROG_CLAYMORE_PID = 0x184D, + AURA_ROG_FALCHION_WIRED_PID = 0x193C, + AURA_ROG_FALCHION_WIRELESS_PID = 0x193E, + AURA_ROG_STRIX_FLARE_PID = 0x1875, + AURA_ROG_STRIX_FLARE_PNK_LTD_PID = 0x18CF, + AURA_ROG_STRIX_FLARE_COD_BO4_PID = 0x18AF, + AURA_ROG_STRIX_FLARE_II_PID = 0x19FE, + AURA_ROG_STRIX_FLARE_II_ANIMATE_PID = 0x19FC, + AURA_ROG_STRIX_SCOPE_PID = 0x18F8, + AURA_ROG_STRIX_SCOPE_RX_PID = 0x1951, + AURA_ROG_STRIX_SCOPE_RX_EVA_02_PID = 0x1B12, + AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_USB_PID = 0x19F6, + AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_2_4_PID = 0x19F8, + AURA_ROG_STRIX_SCOPE_II_PID = 0x1AB3, + AURA_ROG_STRIX_SCOPE_II_RX_PID = 0x1AB5, + AURA_ROG_STRIX_SCOPE_II_96_WIRELESS_USB_PID = 0x1AAE, + AURA_ROG_STRIX_SCOPE_II_96_RX_WIRELESS_USB_PID = 0x1B78, + AURA_TUF_K1_GAMING_PID = 0x1945, + AURA_TUF_K3_GAMING_PID = 0x194B, + AURA_TUF_K3_GAMING_GEN_II_PID = 0x1B30, + AURA_TUF_K5_GAMING_PID = 0x1899, + AURA_TUF_K7_GAMING_PID = 0x18AA, + AURA_TUF_K3_GENII_MIKU_EDITION_PID = 0x1C5E, +}; + +struct led_color +{ + unsigned int value; + RGBColor color; +}; + +class AuraTUFKeyboardController +{ +public: + AuraTUFKeyboardController(hid_device* dev_handle, const char* path, uint16_t pid, unsigned short version, std::string dev_name); + ~AuraTUFKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetName(); + std::string GetSerialString(); + std::string GetVersion(); + + int GetLayout(); + int GetNumpadLocation(); + void SaveMode(); + void AllowRemoteControl(unsigned char type); + + void UpdateSingleLed + ( + int led, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void UpdateLeds + ( + std::vector colors + ); + + void UpdateK1Wave + ( + std::vector colors, + unsigned char direction, + unsigned char speed, + unsigned char brightness + ); + + void UpdateScopeIIRainbowRipple + ( + unsigned char mode, + std::vector colors, + unsigned char direction, + unsigned char color_mode, + unsigned char speed, + unsigned char brightness + ); + + void UpdateScopeIIQuicksand + ( + std::vector colors, + unsigned char direction, + unsigned char color_mode, + unsigned char speed, + unsigned char brightness + ); + + void UpdateDevice + ( + unsigned char mode, + std::vector colors, + unsigned char direction, + unsigned char color_mode, + unsigned char speed, + unsigned char brightness + ); + + void UpdateQuicksandColors(std::vector colors); + void UpdateMode(unsigned char mode); + void AwaitResponse(int ms); + void ClearResponses(); + + uint16_t device_pid; + bool is_per_led_keyboard; + +private: + hid_device* dev; + std::string location; + std::string name; + unsigned short rev_version; +}; + diff --git a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardLayouts.h b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardLayouts.h new file mode 100644 index 0000000..7d1b68a --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardLayouts.h @@ -0,0 +1,3877 @@ +/*---------------------------------------------------------*\ +| AsusAuraTUFKeyboardLayouts.h | +| | +| Layouts for ASUS Aura TUF keyboard | +| | +| Mola19 02 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBControllerKeyNames.h" +#include "RGBController.h" + +enum +{ + ASUS_TUF_K7_LAYOUT_CA = 1, + ASUS_TUF_K7_LAYOUT_AR = 2, + ASUS_TUF_K7_LAYOUT_DE = 3, + ASUS_TUF_K7_LAYOUT_UK = 4, + ASUS_TUF_K7_LAYOUT_FR = 5, + ASUS_TUF_K7_LAYOUT_CN = 6, + ASUS_TUF_K7_LAYOUT_HU = 7, + ASUS_TUF_K7_LAYOUT_IT = 8, + ASUS_TUF_K7_LAYOUT_TH = 9, + ASUS_TUF_K7_LAYOUT_UA = 10, + ASUS_TUF_K7_LAYOUT_NO = 11, + ASUS_TUF_K7_LAYOUT_PT = 12, + ASUS_TUF_K7_LAYOUT_HE = 13, + ASUS_TUF_K7_LAYOUT_RU = 14, + ASUS_TUF_K7_LAYOUT_ES = 15, + ASUS_TUF_K7_LAYOUT_TW = 16, + ASUS_TUF_K7_LAYOUT_US = 17, + ASUS_TUF_K7_LAYOUT_TR = 18, + ASUS_TUF_K7_LAYOUT_CZ = 19, + ASUS_TUF_K7_LAYOUT_BE = 20, + ASUS_TUF_K7_LAYOUT_JP = 21, + ASUS_TUF_K7_LAYOUT_KR = 22, + ASUS_TUF_K7_LAYOUT_IS = 23, + ASUS_TUF_K7_LAYOUT_WB = 24, + ASUS_TUF_K7_LAYOUT_SW_CH = 25 +}; + +#define NA 0xFFFFFFFF + +struct led_value +{ + const char* name; + unsigned char id; +}; + +struct layout_info +{ + unsigned int* matrix_map; + int size; + int rows; + int cols; + std::vector led_names; +}; + +static unsigned int ASUS_TUF_K7_LAYOUT_KEYS_ANSI[6][24] = +{ + { 0, NA, 8, 14, 19, 24, NA, 34, 39, 44, 49, 55, 61, 66, 70, NA, 74, 78, 83, NA, NA, NA, NA, NA }, + { 1, 6, 9, 15, 20, 25, 29, 35, 40, 45, 50, 56, 62, 67, NA, NA, 75, 79, 84, NA, 87, 92, 96, 101 }, + { 2, NA, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, 71, NA, 76, 80, 85, NA, 88, 93, 97, 102 }, + { 3, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, NA, 72, NA, NA, NA, NA, NA, 89, 94, 98, NA }, + { 4, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, NA, 69, NA, NA, NA, 81, NA, NA, 90, 95, 99, 103 }, + { 5, 7, 13, NA, NA, NA, 33, NA, NA, NA, 54, 60, 65, NA, 73, NA, 77, 82, 86, NA, 91, NA, 100, NA } +}; + +static unsigned int ASUS_TUF_K7_LAYOUT_KEYS_ISO[6][24] = +{ + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, 75, 79, 84, NA, NA, NA, NA, NA }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, 76, 80, 85, NA, 88, 93, 97, 102 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, NA, NA, 77, 81, 86, NA, 89, 94, 98, 103 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 70, 73, NA, NA, NA, NA, NA, 90, 95, 99, NA }, + { 4, 7, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, NA, 71, NA, NA, NA, 82, NA, NA, 91, 96, 100, 104 }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, NA, 74, NA, 78, 83, 87, NA, 92, NA, 101, NA } +}; + +static unsigned int ASUS_ROG_AZOTH_LAYOUT_KEYS_US[6][16] = { + { 0, 6, 9, 15, 20, 25, 30, 36, 41, 46, 51, 57, 63, NA, NA, NA }, + { 1, 7, 10, 16, 21, 26, 31, 37, 42, 47, 52, 58, 64, 69, NA, 76 }, + { 2, NA, 11, 17, 22, 27, 32, 38, 43, 48, 53, 59, 65, 70, 72, 77 }, + { 3, NA, 12, 18, 23, 28, 33, 39, 44, 49, 54, 60, 66, NA, 73, 78 }, + { 4, NA, 13, 19, 24, 29, 34, 40, 45, 50, 55, 61, 67, NA, 74, 79 }, + { 5, 8, 14, NA, NA, NA, 35, NA, NA, NA, 56, 62, 68, 71, 75, 80 } +}; + +static unsigned int ASUS_ROG_AZOTH_LAYOUT_KEYS_UK[6][16] = { + { 0, 6, 10, 16, 21, 26, 31, 37, 42, 47, 52, 58, 64, NA, NA, NA }, + { 1, 7, 11, 17, 22, 27, 32, 38, 43, 48, 53, 59, 65, 70, NA, 77 }, + { 2, NA, 12, 18, 23, 28, 33, 39, 44, 49, 54, 60, 66, 71, NA, 78 }, + { 3, NA, 13, 19, 24, 29, 34, 40, 45, 50, 55, 61, 67, 72, 74, 79 }, + { 4, 8, 14, 20, 25, 30, 35, 41, 46, 51, 56, 62, 68, NA, 75, 80 }, + { 5, 9, 15, NA, NA, NA, 36, NA, NA, NA, 57, 63, 69, 73, 76, 81 } +}; + +static unsigned int ASUS_ROG_STRIX_SCOPE_LAYOUT_KEYS_ANSI[6][24] = +{ + { 0, NA, 8, 14, 19, 24, NA, 34, 39, 44, 49, 55, 61, 66, 70, NA, 74, 78, 83, NA, NA, NA, 104, 105 }, + { 1, 6, 9, 15, 20, 25, 29, 35, 40, 45, 50, 56, 62, 67, NA, NA, 75, 79, 84, NA, 87, 92, 96, 101 }, + { 2, NA, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, 71, NA, 76, 80, 85, NA, 88, 93, 97, 102 }, + { 3, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, NA, 72, NA, NA, NA, NA, NA, 89, 94, 98, NA }, + { 4, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, NA, 69, NA, NA, NA, 81, NA, NA, 90, 95, 99, 103 }, + { 5, 7, 13, NA, NA, NA, 33, NA, NA, NA, 54, 60, 65, NA, 73, NA, 77, 82, 86, NA, 91, NA, 100, NA } +}; + + +static unsigned int ASUS_ROG_STRIX_SCOPE_LAYOUT_KEYS_ISO[6][24] = +{ + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, 75, 79, 84, NA, NA, NA, 105, 106 }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, 76, 80, 85, NA, 88, 93, 97, 102 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, NA, NA, 77, 81, 86, NA, 89, 94, 98, 103 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 70, 73, NA, NA, NA, NA, NA, 90, 95, 99, NA }, + { 4, 7, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, NA, 71, NA, NA, NA, 82, NA, NA, 91, 96, 100, 104 }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, NA, 74, NA, 78, 83, 87, NA, 92, NA, 101, NA } +}; + +static unsigned int ASUS_ROG_STRIX_SCOPE_II_LAYOUT_KEYS_ANSI[6][24] = +{ + { 0, NA, 8, 14, 19, 24, NA, 35, 41, 46, 51, 57, 63, 68, 72, NA, 76, 80, 85, NA, NA, 94, NA, NA }, + { 1, 6, 9, 15, 20, 25, 30, 36, 42, 47, 52, 58, 64, 69, NA, NA, 77, 81, 86, NA, 89, 95, 99, 104 }, + { 2, NA, 10, 16, 21, 26, 31, 37, 43, 48, 53, 59, 65, 70, 73, NA, 78, 82, 87, NA, 90, 96, 100, 105 }, + { 3, NA, 11, 17, 22, 27, 32, 38, 44, 49, 54, 60, 66, NA, 74, NA, NA, NA, NA, NA, 91, 97, 101, NA }, + { 4, NA, 12, 18, 23, 28, 33, 39, 45, 50, 55, 61, NA, 71, NA, NA, NA, 83, NA, NA, 92, 98, 102, 106 }, + { 5, 7, 13, NA, NA, 29, 34, 40, NA, NA, 56, 62, 67, NA, 75, NA, 79, 84, 88, NA, 93, NA, 103, NA } +}; + + +static unsigned int ASUS_ROG_STRIX_SCOPE_II_LAYOUT_KEYS_ISO[6][24] = +{ + { 0, NA, 9, 15, 20, 25, NA, 36, 42, 47, 52, 58, 64, 69, 74, NA, 77, 81, 86, NA, NA, 95, NA, NA }, + { 1, 6, 10, 16, 21, 26, 31, 37, 43, 48, 53, 59, 65, 70, NA, NA, 78, 82, 87, NA, 90, 96, 100, 105 }, + { 2, NA, 11, 17, 22, 27, 32, 38, 44, 49, 54, 60, 66, 71, NA, NA, 79, 83, 88, NA, 91, 97, 101, 106 }, + { 3, NA, 12, 18, 23, 28, 33, 39, 45, 50, 55, 61, 67, 72, 75, NA, NA, NA, NA, NA, 92, 98, 102, NA }, + { 4, 7, 13, 19, 24, 29, 34, 40, 46, 51, 56, 62, NA, 73, NA, NA, NA, 84, NA, NA, 93, 99, 103, 107 }, + { 5, 8, 14, NA, NA, 30, 35, 41, NA, NA, 57, 63, 68, NA, 76, NA, 80, 85, 89, NA, 94, NA, 104, NA } +}; + +static unsigned int ASUS_ROG_STRIX_SCOPE_II_96_WIRELESS_LAYOUT_KEYS_ANSI[6][19] = +{ + { 0, 6, 11, 17, 22, 27, 33, 39, 45, 50, 55, 61, 67, 72, 77, 80, 86, NA, 97 }, + { 1, 7, 12, 18, 23, 28, 34, 40, 46, 51, 56, 62, 68, 73, NA, 81, 87, 92, 98 }, + { 2, 8, 13, 19, 24, 29, 35, 41, 47, 52, 57, 63, 69, 74, NA, 82, 88, 93, 99 }, + { 3, 9, 14, 20, 25, 30, 36, 42, 48, 53, 58, 64, NA, 75, NA, 83, 89, 94, NA }, + { 4, NA, 15, 21, 26, 31, 37, 43, 49, 54, 59, 65, 70, NA, 78, 84, 90, 95, 100 }, + { 5, 10, 16, NA, NA, 32, 38, 44, NA, NA, 60, 66, 71, 76, 79, 85, 91, 96, NA } +}; + +static unsigned int ASUS_ROG_STRIX_SCOPE_II_96_WIRELESS_LAYOUT_KEYS_ISO[6][19] = +{ + { 0, 6, 12, 18, 23, 28, 34, 40, 46, 51, 56, 62, 68, 74, 78, 81, 87, NA, 98 }, + { 1, 7, 13, 19, 24, 29, 35, 41, 47, 52, 57, 63, 69, 75, NA, 82, 88, 93, 99 }, + { 2, 8, 14, 20, 25, 30, 36, 42, 48, 53, 58, 64, 70, NA, NA, 83, 89, 94, 100 }, + { 3, 9, 15, 21, 26, 31, 37, 43, 49, 54, 59, 65, 71, 76, NA, 84, 90, 95, NA }, + { 4, 10, 16, 22, 27, 32, 38, 44, 50, 55, 60, 66, 72, NA, 79, 85, 91, 96, 101 }, + { 5, 11, 17, NA, NA, 33, 39, 45, NA, NA, 61, 67, 73, 77, 80, 86, 92, 97, NA } +}; + +static unsigned int ASUS_ROG_STRIX_SCOPE_II_96_RX_WIRELESS_LAYOUT_KEYS_ISO[6][19] = +{ + { 0, 6, 12, 18, 23, 28, 34, 40, 46, 51, 56, 62, 68, 74, 78, 81, 87, 98, NA }, + { 1, 7, 13, 19, 24, 29, 35, 41, 47, 52, 57, 63, 69, 75, NA, 82, 88, 93, 99 }, + { 2, 8, 14, 20, 25, 30, 36, 42, 48, 53, 58, 64, 70, NA, NA, 83, 89, 94, 100 }, + { 3, 9, 15, 21, 26, 31, 37, 43, 49, 54, 59, 65, 71, 76, NA, 84, 90, 95, NA }, + { 4, 10, 16, 22, 27, 32, 38, 44, 50, 55, 60, 66, 72, NA, 79, 85, 91, 96, 101 }, + { 5, 11, 17, NA, 102, 33, 39, 45, 103, NA, 61, 67, 73, 77, 80, 86, 92, 97, NA } +}; + +static unsigned int ASUS_ROG_STRIX_FLARE_LAYOUT_KEYS_ANSI[6][26] = +{ + { NA, 0, NA, 8, 14, 19, 24, NA, 34, 39, 44, 49, 55, 61, 66, 70, NA, 74, 78, 83, NA, 104, NA, NA, NA, NA }, + { NA, 1, 6, 9, 15, 20, 25, 29, 35, 40, 45, 50, 56, 62, 67, NA, NA, 75, 79, 84, NA, 87, 92, 96, 101, NA }, + { NA, 2, NA, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, 71, NA, 76, 80, 85, NA, 88, 93, 97, 102, NA }, + { 105, 3, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, NA, 72, NA, NA, NA, NA, NA, 89, 94, 98, NA, 106 }, + { NA, 4, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, NA, 69, NA, NA, NA, 81, NA, NA, 90, 95, 99, 103, NA }, + { NA, 5, 7, 13, NA, NA, NA, 33, NA, NA, NA, 54, 60, 65, NA, 73, NA, 77, 82, 86, NA, 91, NA, 100, NA, NA } +}; + +static unsigned int ASUS_ROG_STRIX_FLARE_LAYOUT_KEYS_ISO[6][26] = +{ + { NA, 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, 75, 79, 84, NA, 105, NA, NA, NA, NA }, + { NA, 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, 76, 80, 85, NA, 88, 93, 97, 102, NA }, + { NA, 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, NA, NA, 77, 81, 86, NA, 89, 94, 98, 103, NA }, + { 106, 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 70, 73, NA, NA, NA, NA, NA, 90, 95, 99, NA, 107 }, + { NA, 4, 7, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, NA, 71, NA, NA, NA, 82, NA, NA, 91, 96, 100, 104, NA }, + { NA, 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, NA, 74, NA, 78, 83, 87, NA, 92, NA, 101, NA, NA } +}; + +static unsigned int ASUS_ROG_STRIX_FLARE_II_LAYOUT_KEYS_ANSI[7][30] = +{ + { NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 104, 105, NA, NA }, + { 0, NA, 8, 14, 19, 24, NA, 34, 39, 44, 49, 55, 61, 66, 70, NA, NA, NA, NA, 74, 78, 83, NA, NA, NA, NA, NA, NA, NA, NA }, + { 1, 6, 9, 15, 20, 25, 29, 35, 40, 45, 50, 56, 62, 67, NA, NA, NA, NA, NA, 75, 79, 84, NA, NA, NA, NA, 87, 92, 96, 101 }, + { 2, NA, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, 71, NA, NA, NA, NA, 76, 80, 85, NA, NA, NA, NA, 88, 93, 97, 102 }, + { 3, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, NA, 72, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 89, 94, 98, NA }, + { 4, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, NA, 69, NA, NA, NA, NA, NA, NA, 81, NA, NA, NA, NA, NA, 90, 95, 99, 103 }, + { 5, 7, 13, NA, NA, NA, 33, NA, NA, NA, 54, 60, 65, NA, 73, NA, NA, NA, NA, 77, 82, 86, NA, NA, NA, NA, 91, NA, 100, NA }, +}; + +static unsigned int ASUS_ROG_STRIX_FLARE_II_LAYOUT_KEYS_ISO[7][30] = +{ + { NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 105, 106, NA, NA }, + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, NA, NA, NA, 75, 79, 84, NA, NA, NA, NA, NA, NA, NA, NA }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, NA, NA, NA, 76, 80, 85, NA, NA, NA, NA, 88, 93, 97, 102 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, NA, NA, NA, NA, NA, 77, 81, 86, NA, NA, NA, NA, 89, 94, 98, 103 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 70, 73, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 90, 95, 99, NA }, + { 4, 7, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, NA, 71, NA, NA, NA, NA, NA, NA, 82, NA, NA, NA, NA, NA, 91, 96, 100, 104 }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, NA, 74, NA, NA, NA, NA, 78, 83, 87, NA, NA, NA, NA, 92, NA, 101, NA }, +}; + +static unsigned int ASUS_ROG_STRIX_FLARE_II_ANIMATE_LAYOUT_KEYS_ANSI[7][30] = +{ + { 0, NA, 8, 14, 19, 24, NA, 34, 39, 44, 49, 55, 61, 66, 70, NA, NA, NA, NA, 74, 78, 83, NA, NA, NA, NA, NA, NA, NA, NA }, + { 1, 6, 9, 15, 20, 25, 29, 35, 40, 45, 50, 56, 62, 67, NA, NA, NA, NA, NA, 75, 79, 84, NA, NA, NA, NA, 87, 92, 96, 101 }, + { 2, NA, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, 71, NA, NA, NA, NA, 76, 80, 85, NA, NA, NA, NA, 88, 93, 97, 102 }, + { 3, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, NA, 72, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 89, 94, 98, NA }, + { 4, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, NA, 69, NA, NA, NA, NA, NA, NA, 81, NA, NA, NA, NA, NA, 90, 95, 99, 103 }, + { 5, 7, 13, NA, NA, NA, 33, NA, NA, NA, 54, 60, 65, NA, 73, NA, NA, NA, NA, 77, 82, 86, NA, NA, NA, NA, 91, NA, 100, NA }, + { 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133 } +}; + +static unsigned int ASUS_ROG_STRIX_FLARE_II_ANIMATE_LAYOUT_KEYS_ISO[7][30] = +{ + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, NA, NA, NA, 75, 79, 84, NA, NA, NA, NA, NA, NA, NA, NA }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, NA, NA, NA, 76, 80, 85, NA, NA, NA, NA, 88, 93, 97, 102 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, NA, NA, NA, NA, NA, 77, 81, 86, NA, NA, NA, NA, 89, 94, 98, 103 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 70, 73, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 90, 95, 99, NA }, + { 4, 7, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, NA, 71, NA, NA, NA, NA, NA, NA, 82, NA, NA, NA, NA, NA, 91, 96, 100, 104 }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, NA, 74, NA, NA, NA, NA, 78, 83, 87, NA, NA, NA, NA, 92, NA, 101, NA }, + { 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134 } +}; + +static unsigned int ASUS_FALCHION_LAYOUT_KEYS_ANSI[5][16] = +{ + { 0, 5, 7, 12, 16, 20, 24, 29, 33, 37, 41, 46, 51, 56, NA, 63 }, + { 1, NA, 8, 13, 17, 21, 25, 30, 34, 38, 42, 47, 52, 57, 59, 64 }, + { 2, NA, 9, 14, 18, 22, 26, 31, 35, 39, 43, 48, 53, NA, 60, 65 }, + { 3, NA, 10, 15, 19, 23, 27, 32, 36, 40, 44, 49, 54, NA, 61, 66 }, + { 4, 6, 11, NA, NA, NA, 28, NA, NA, NA, 45, 50, 55, 58, 62, 67 } +}; + +static unsigned int ASUS_FALCHION_LAYOUT_KEYS_ISO[5][16] = +{ + { 0, 5, 8, 13, 17, 21, 25, 30, 34, 38, 42, 47, 52, 57, NA, 64 }, + { 1, NA, 9, 14, 18, 22, 26, 31, 35, 39, 43, 48, 53, 58, NA, 65 }, + { 2, NA, 10, 15, 19, 23, 27, 32, 36, 40, 44, 49, 54, 59, 61, 66 }, + { 3, 6, 11, 16, 20, 24, 28, 33, 37, 41, 45, 50, 55, NA, 62, 67 }, + { 4, 7, 12, NA, NA, NA, 29, NA, NA, NA, 46, 51, 56, 60, 63, 68 } +}; + +static unsigned int ASUS_CLAYMORE_NO_NUMPAD_LAYOUT_KEYS_ANSI[7][19] = +{ + { 0, NA, 8, 14, 19, 24, NA, 34, 39, 45, 50, 56, 62, 67, 71, NA, 75, 79, 84 }, + { 1, 6, 9, 15, 20, 25, 29, 35, 40, 46, 51, 57, 63, 68, NA, NA, 76, 80, 85 }, + { 2, NA, 10, 16, 21, 26, 30, 36, 41, 47, 52, 58, 64, 69, 72, NA, 77, 81, 86 }, + { 3, NA, 11, 17, 22, 27, 31, 37, 42, 48, 53, 59, 65, NA, 73, NA, NA, NA, NA }, + { 4, NA, 12, 18, 23, 28, 32, 38, 43, 49, 54, 60, NA, 70, NA, NA, NA, 82, NA }, + { 5, 7, 13, NA, NA, NA, 33, NA, NA, NA, 55, 61, 66, NA, 74, NA, 78, 83, 87 }, + { NA, NA, NA, NA, NA, NA, NA, NA, 44, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA } +}; + +static unsigned int ASUS_CLAYMORE_NO_NUMPAD_LAYOUT_KEYS_ISO[7][19] = +{ + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 46, 51, 57, 63, 68, 73, NA, 76, 80, 85 }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 47, 52, 58, 64, 69, NA, NA, 77, 81, 86 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 48, 53, 59, 65, 70, NA, NA, 78, 82, 87 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 49, 54, 60, 66, 71, 74, NA, NA, NA, NA }, + { 4, 7, 13, 19, 24, 29, 33, 39, 44, 50, 55, 61, NA, 72, NA, NA, NA, 83, NA }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 56, 62, 67, NA, 75, NA, 79, 84, 88 }, + { NA, NA, NA, NA, NA, NA, NA, NA, 45, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA } +}; + +static unsigned int ASUS_CLAYMORE_NUMPAD_RIGHT_LAYOUT_KEYS_ANSI[7][24] = +{ + { 0, NA, 8, 14, 19, 24, NA, 34, 39, 45, 50, 56, 62, 67, 71, NA, 75, 79, 84, NA, NA, NA, NA, NA }, + { 1, 6, 9, 15, 20, 25, 29, 35, 40, 46, 51, 57, 63, 68, NA, NA, 76, 80, 85, NA, 88, 93, 97, 102 }, + { 2, NA, 10, 16, 21, 26, 30, 36, 41, 47, 52, 58, 64, 69, 72, NA, 77, 81, 86, NA, 89, 94, 98, 103 }, + { 3, NA, 11, 17, 22, 27, 31, 37, 42, 48, 53, 59, 65, NA, 73, NA, NA, NA, NA, NA, 90, 95, 99, NA }, + { 4, NA, 12, 18, 23, 28, 32, 38, 43, 49, 54, 60, NA, 70, NA, NA, NA, 82, NA, NA, 91, 96, 100, 104 }, + { 5, 7, 13, NA, NA, NA, 33, NA, NA, NA, 55, 61, 66, NA, 74, NA, 78, 83, 87, NA, 92, NA, 101, NA }, + { NA, NA, NA, NA, NA, NA, NA, NA, 44, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA } +}; + +static unsigned int ASUS_CLAYMORE_NUMPAD_RIGHT_LAYOUT_KEYS_ISO[7][24] = +{ + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 46, 51, 57, 63, 68, 73, NA, 76, 80, 85, NA, NA, NA, NA, NA }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 47, 52, 58, 64, 69, NA, NA, 77, 81, 86, NA, 89, 94, 98, 103 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 48, 53, 59, 65, 70, NA, NA, 78, 82, 87, NA, 90, 95, 99, 104 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 49, 54, 60, 66, 71, 74, NA, NA, NA, NA, NA, 91, 96, 100, NA }, + { 4, 7, 13, 19, 24, 29, 33, 39, 44, 50, 55, 61, NA, 72, NA, NA, NA, 83, NA, NA, 92, 97, 101, 105 }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 56, 62, 67, NA, 75, NA, 79, 84, 88, NA, 93, NA, 102, NA }, + { NA, NA, NA, NA, NA, NA, NA, NA, 45, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA } +}; + +static unsigned int ASUS_CLAYMORE_NUMPAD_LEFT_LAYOUT_KEYS_ANSI[7][24] = +{ + { NA, NA, NA, NA, NA, 17, NA, 25, 31, 36, 41, NA, 51, 56, 62, 67, 73, 79, 84, 88, NA, 92, 96, 101 }, + { 0, 5, 9, 14, NA, 18, 23, 26, 32, 37, 42, 46, 52, 57, 63, 68, 74, 80, 85, NA, NA, 93, 97, 102 }, + { 1, 6, 10, 15, NA, 19, NA, 27, 33, 38, 43, 47, 53, 58, 64, 69, 75, 81, 86, 89, NA, 94, 98, 103 }, + { 2, 7, 11, NA, NA, 20, NA, 28, 34, 39, 44, 48, 54, 59, 65, 70, 76, 82, NA, 90, NA, NA, NA, NA }, + { 3, 8, 12, 16, NA, 21, NA, 29, 35, 40, 45, 49, 55, 60, 66, 71, 77, NA, 87, NA, NA, NA, 99, NA }, + { 4, NA, 13, NA, NA, 22, 24, 30, NA, NA, NA, 50, NA, NA, NA, 72, 78, 83, NA, 91, NA, 95, 100, 104 }, + { NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 61, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA } +}; + +static unsigned int ASUS_CLAYMORE_NUMPAD_LEFT_LAYOUT_KEYS_ISO[7][24] = +{ + { NA, NA, NA, NA, NA, 17, NA, 26, 32, 37, 42, NA, 52, 57, 63, 68, 74, 80, 85, 90, NA, 93, 97, 102 }, + { 0, 5, 9, 14, NA, 18, 23, 27, 33, 38, 43, 47, 53, 58, 64, 69, 75, 81, 86, NA, NA, 94, 98, 103 }, + { 1, 6, 10, 15, NA, 19, NA, 28, 34, 39, 44, 48, 54, 59, 65, 70, 76, 82, 87, NA, NA, 95, 99, 104 }, + { 2, 7, 11, NA, NA, 20, NA, 29, 35, 40, 45, 49, 55, 60, 66, 71, 77, 83, 88, 91, NA, NA, NA, NA }, + { 3, 8, 12, 16, NA, 21, 24, 30, 36, 41, 46, 50, 56, 61, 67, 72, 78, NA, 89, NA, NA, NA, 100, NA }, + { 4, NA, 13, NA, NA, 22, 25, 31, NA, NA, NA, 51, NA, NA, NA, 73, 79, 84, NA, 92, NA, 96, 101, 105 }, + { NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 62, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA } +}; + +static unsigned int ASUS_TUF_K1_LAYOUT_KEYS[1][5] = +{ + { 0, 1, 2, 3, 4 }, +}; + +static unsigned int ASUS_TUF_K3_GAMING_GEN_II_LAYOUT_KEYS_ANSI[6][19] = +{ + { 0, NA, 8, 14, 19, 24, NA, 34, 39, 44, 49, 55, 61, 66, 71, 75, 81, 87, 93 }, + { 1, 6, 9, 15, 20, 25, 29, 35, 40, 45, 50, 56, 62, 67, NA, 76, 82, 88, 94 }, + { 2, NA, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, 72, 77, 83, 89, 95 }, + { 3, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, NA, 78, 84, 90, NA }, + { 4, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, NA, 73, 79, 85, 91, 96 }, + { 5, 7, 13, NA, NA, NA, 33, NA, NA, NA, 54, 60, NA, 70, 74, 80, 86, 92, NA } +}; + +static std::map AsusTUFK7Layouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_TUF_K7_LAYOUT_KEYS_ISO, + 105, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_POUND, 0x6B }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ISO_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 } + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_TUF_K7_LAYOUT_KEYS_ANSI, + 104, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ANSI_BACK_SLASH, 0x7A }, + { KEY_EN_ANSI_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 } + } + } + }, +}; + +static std::map AsusROGAzothLayouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_ROG_AZOTH_LAYOUT_KEYS_UK, + 82, + 6, + 16, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_F1, 0x08 }, + { KEY_EN_1, 0x09 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F2, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F3, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F4, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_F6, 0x30 }, + { KEY_EN_6, 0x31 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F7, 0x38 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F8, 0x40 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F9, 0x48 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F10, 0x50 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x55 }, + + { KEY_EN_F11, 0x58 }, + { KEY_EN_MINUS, 0x59 }, + { KEY_EN_P, 0x52 }, + { KEY_EN_SEMICOLON, 0x53 }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F12, 0x60 }, + { KEY_EN_EQUALS, 0x61 }, + { KEY_EN_LEFT_BRACKET, 0x5A }, + { KEY_EN_QUOTE, 0x5B }, + { KEY_EN_RIGHT_SHIFT, 0x64 }, + { KEY_EN_RIGHT_CONTROL, 0x65 }, + + { KEY_EN_BACKSPACE, 0x69 }, + { KEY_EN_RIGHT_BRACKET, 0x62 }, + { KEY_EN_POUND, 0x6B }, + { KEY_EN_LEFT_ARROW, 0x6D }, + + { KEY_EN_ISO_ENTER, 0x6A }, + { KEY_EN_UP_ARROW, 0x74 }, + { KEY_EN_DOWN_ARROW, 0x75 }, + + { KEY_EN_INSERT, 0x79 }, + { KEY_EN_DELETE, 0x7A }, + { KEY_EN_PAGE_UP, 0x7B }, + { KEY_EN_PAGE_DOWN, 0x7C }, + { KEY_EN_RIGHT_ARROW, 0x7D }, + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_ROG_AZOTH_LAYOUT_KEYS_US, + 81, + 6, + 16, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_F1, 0x08 }, + { KEY_EN_1, 0x09 }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F2, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F3, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F4, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_F6, 0x30 }, + { KEY_EN_6, 0x31 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F7, 0x38 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F8, 0x40 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F9, 0x48 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F10, 0x50 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x55 }, + + { KEY_EN_F11, 0x58 }, + { KEY_EN_MINUS, 0x59 }, + { KEY_EN_P, 0x52 }, + { KEY_EN_SEMICOLON, 0x53 }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F12, 0x60 }, + { KEY_EN_EQUALS, 0x61 }, + { KEY_EN_LEFT_BRACKET, 0x5A }, + { KEY_EN_QUOTE, 0x5B }, + { KEY_EN_RIGHT_SHIFT, 0x64 }, + { KEY_EN_RIGHT_CONTROL, 0x65 }, + + { KEY_EN_BACKSPACE, 0x69 }, + { KEY_EN_RIGHT_BRACKET, 0x62 }, + { KEY_EN_LEFT_ARROW, 0x6D }, + + { KEY_EN_ANSI_BACK_SLASH, 0x6A }, + { KEY_EN_ANSI_ENTER, 0x6B }, + { KEY_EN_UP_ARROW, 0x74 }, + { KEY_EN_DOWN_ARROW, 0x75 }, + + { KEY_EN_INSERT, 0x79 }, + { KEY_EN_DELETE, 0x7A }, + { KEY_EN_PAGE_UP, 0x7B }, + { KEY_EN_PAGE_DOWN, 0x7C }, + { KEY_EN_RIGHT_ARROW, 0x7D }, + } + } + }, +}; + +static std::map AsusROGStrixScopeLayouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_ROG_STRIX_SCOPE_LAYOUT_KEYS_ISO, + 107, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x15 }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x1D }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_POUND, 0x6B }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ISO_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, + + { "Logo 1", 0xA8 }, + { "Logo 2", 0xB0 }, + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_ROG_STRIX_SCOPE_LAYOUT_KEYS_ANSI, + 106, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_LEFT_WINDOWS, 0x15 }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x1D }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ANSI_BACK_SLASH, 0x7A }, + { KEY_EN_ANSI_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, + + { "Logo 1", 0xA8 }, + { "Logo 2", 0xB0 }, + } + } + }, +}; + +static std::map AsusROGStrixScopeIILayouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_ROG_STRIX_SCOPE_II_LAYOUT_KEYS_ISO, + 108, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + { KEY_EN_SPACE, 0x2D }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + { KEY_EN_SPACE, 0x3D }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_POUND, 0x6B }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ISO_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { "Logo", 0xA0 }, + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, + + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_ROG_STRIX_SCOPE_II_LAYOUT_KEYS_ANSI, + 107, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + { KEY_EN_SPACE, 0x2D }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + { KEY_EN_SPACE, 0x3D }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ANSI_BACK_SLASH, 0x7A }, + { KEY_EN_ANSI_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { "Logo", 0xA0 }, + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, + } + } + }, +}; + +static std::map AsusROGStrixScopeII96RxWirelessLayouts = +{ + + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_ROG_STRIX_SCOPE_II_96_RX_WIRELESS_LAYOUT_KEYS_ISO, + 104, + 6, + 19, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_F1, 0x08 }, + { KEY_EN_1, 0x09 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F2, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F3, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F4, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_V, 0x2C }, + { KEY_EN_SPACE, 0x2D }, + + { KEY_EN_F6, 0x30 }, + { KEY_EN_6, 0x31 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F7, 0x38 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_N, 0x3C }, + { KEY_EN_SPACE, 0x3D }, + + { KEY_EN_F8, 0x40 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F9, 0x48 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F10, 0x50 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_P, 0x52 }, + { KEY_EN_SEMICOLON, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x55 }, + + { KEY_EN_F11, 0x58 }, + { KEY_EN_MINUS, 0x59 }, + { KEY_EN_LEFT_BRACKET, 0x5A }, + { KEY_EN_QUOTE, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F12, 0x60 }, + { KEY_EN_EQUALS, 0x61 }, + { KEY_EN_RIGHT_BRACKET, 0x62 }, + { KEY_EN_POUND, 0x63 }, + { KEY_EN_RIGHT_SHIFT, 0x64 }, + { KEY_EN_RIGHT_CONTROL, 0x65 }, + + { KEY_EN_INSERT, 0x68 }, + { KEY_EN_BACKSPACE, 0x69 }, + { KEY_EN_ISO_ENTER, 0x6B }, + { KEY_EN_LEFT_ARROW, 0x6D }, + + { KEY_EN_DELETE, 0x70 }, + { KEY_EN_UP_ARROW, 0x74 }, + { KEY_EN_DOWN_ARROW, 0x75 }, + + { KEY_EN_PAGE_UP, 0x78 }, + { KEY_EN_NUMPAD_LOCK, 0x79 }, + { KEY_EN_NUMPAD_7, 0x7A }, + { KEY_EN_NUMPAD_4, 0x7B }, + { KEY_EN_NUMPAD_1, 0x7C }, + { KEY_EN_RIGHT_ARROW, 0x7D }, + + { KEY_EN_PAGE_DOWN, 0x80 }, + { KEY_EN_NUMPAD_DIVIDE, 0x81 }, + { KEY_EN_NUMPAD_8, 0x82 }, + { KEY_EN_NUMPAD_5, 0x83 }, + { KEY_EN_NUMPAD_2, 0x84 }, + { KEY_EN_NUMPAD_0, 0x85 }, + + { KEY_EN_NUMPAD_TIMES, 0x89 }, + { KEY_EN_NUMPAD_9, 0x8A }, + { KEY_EN_NUMPAD_6, 0x8B }, + { KEY_EN_NUMPAD_3, 0x8C }, + { KEY_EN_NUMPAD_PERIOD, 0x8D }, + + { "Logo", 0x88 }, + { KEY_EN_NUMPAD_MINUS, 0x91 }, + { KEY_EN_NUMPAD_PLUS, 0x92 }, + { KEY_EN_NUMPAD_ENTER, 0x94 }, + { KEY_EN_SPACE, 0x25 }, + { KEY_EN_SPACE, 0x45 } + } + } + } +}; + +static std::map AsusROGStrixScopeII96WirelessLayouts = +{ + { + ASUS_TUF_K7_LAYOUT_NO, + { + *ASUS_ROG_STRIX_SCOPE_II_96_WIRELESS_LAYOUT_KEYS_ISO, + 102, + 6, + 19, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_NORD_HALF, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_F1, 0x08 }, + { KEY_EN_1, 0x09 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_NORD_ANGLE_BRACKET, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F2, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F3, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F4, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_V, 0x2C }, + { KEY_EN_SPACE, 0x2D }, + + { KEY_EN_F6, 0x30 }, + { KEY_EN_6, 0x31 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F7, 0x38 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_N, 0x3C }, + { KEY_EN_SPACE, 0x3D }, + + { KEY_EN_F8, 0x40 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F9, 0x48 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F10, 0x50 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_P, 0x52 }, + { KEY_NORD_O_AE, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x55 }, + + { KEY_EN_F11, 0x58 }, + { KEY_NORD_PLUS_QUESTION, 0x59 }, + { KEY_NORD_AAL, 0x5A }, + { KEY_NORD_A_OE, 0x5B }, + { KEY_NORD_HYPHEN, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F12, 0x60 }, + { KEY_NORD_ACUTE_GRAVE, 0x61 }, + { KEY_NORD_DOTS_CARET, 0x62 }, + { KEY_NORD_QUOTE, 0x63 }, + { KEY_EN_RIGHT_SHIFT, 0x64 }, + { KEY_EN_RIGHT_CONTROL, 0x65 }, + + { KEY_EN_INSERT, 0x68 }, + { KEY_EN_BACKSPACE, 0x69 }, + { KEY_EN_ISO_ENTER, 0x6B }, + { KEY_EN_LEFT_ARROW, 0x6D }, + + { KEY_EN_DELETE, 0x70 }, + { KEY_EN_UP_ARROW, 0x74 }, + { KEY_EN_DOWN_ARROW, 0x75 }, + + { KEY_EN_PAGE_UP, 0x78 }, + { KEY_EN_NUMPAD_LOCK, 0x79 }, + { KEY_EN_NUMPAD_7, 0x7A }, + { KEY_EN_NUMPAD_4, 0x7B }, + { KEY_EN_NUMPAD_1, 0x7C }, + { KEY_EN_RIGHT_ARROW, 0x7D }, + + { KEY_EN_PAGE_DOWN, 0x80 }, + { KEY_EN_NUMPAD_DIVIDE, 0x81 }, + { KEY_EN_NUMPAD_8, 0x82 }, + { KEY_EN_NUMPAD_5, 0x83 }, + { KEY_EN_NUMPAD_2, 0x84 }, + { KEY_EN_NUMPAD_0, 0x85 }, + + { KEY_EN_NUMPAD_TIMES, 0x89 }, + { KEY_EN_NUMPAD_9, 0x8A }, + { KEY_EN_NUMPAD_6, 0x8B }, + { KEY_EN_NUMPAD_3, 0x8C }, + { KEY_EN_NUMPAD_PERIOD, 0x8D }, + + { "Logo", 0x88 }, + { KEY_EN_NUMPAD_MINUS, 0x91 }, + { KEY_EN_NUMPAD_PLUS, 0x92 }, + { KEY_EN_NUMPAD_ENTER, 0x94 } + } + } + }, + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_ROG_STRIX_SCOPE_II_96_WIRELESS_LAYOUT_KEYS_ISO, + 102, + 6, + 19, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_F1, 0x08 }, + { KEY_EN_1, 0x09 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F2, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F3, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F4, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_V, 0x2C }, + { KEY_EN_SPACE, 0x2D }, + + { KEY_EN_F6, 0x30 }, + { KEY_EN_6, 0x31 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F7, 0x38 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_N, 0x3C }, + { KEY_EN_SPACE, 0x3D }, + + { KEY_EN_F8, 0x40 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F9, 0x48 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F10, 0x50 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_P, 0x52 }, + { KEY_EN_SEMICOLON, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x55 }, + + { KEY_EN_F11, 0x58 }, + { KEY_EN_MINUS, 0x59 }, + { KEY_EN_LEFT_BRACKET, 0x5A }, + { KEY_EN_QUOTE, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F12, 0x60 }, + { KEY_EN_EQUALS, 0x61 }, + { KEY_EN_RIGHT_BRACKET, 0x62 }, + { KEY_EN_POUND, 0x63 }, + { KEY_EN_RIGHT_SHIFT, 0x64 }, + { KEY_EN_RIGHT_CONTROL, 0x65 }, + + { KEY_EN_INSERT, 0x68 }, + { KEY_EN_BACKSPACE, 0x69 }, + { KEY_EN_ISO_ENTER, 0x6B }, + { KEY_EN_LEFT_ARROW, 0x6D }, + + { KEY_EN_DELETE, 0x70 }, + { KEY_EN_UP_ARROW, 0x74 }, + { KEY_EN_DOWN_ARROW, 0x75 }, + + { KEY_EN_PAGE_UP, 0x78 }, + { KEY_EN_NUMPAD_LOCK, 0x79 }, + { KEY_EN_NUMPAD_7, 0x7A }, + { KEY_EN_NUMPAD_4, 0x7B }, + { KEY_EN_NUMPAD_1, 0x7C }, + { KEY_EN_RIGHT_ARROW, 0x7D }, + + { KEY_EN_PAGE_DOWN, 0x80 }, + { KEY_EN_NUMPAD_DIVIDE, 0x81 }, + { KEY_EN_NUMPAD_8, 0x82 }, + { KEY_EN_NUMPAD_5, 0x83 }, + { KEY_EN_NUMPAD_2, 0x84 }, + { KEY_EN_NUMPAD_0, 0x85 }, + + { KEY_EN_NUMPAD_TIMES, 0x89 }, + { KEY_EN_NUMPAD_9, 0x8A }, + { KEY_EN_NUMPAD_6, 0x8B }, + { KEY_EN_NUMPAD_3, 0x8C }, + { KEY_EN_NUMPAD_PERIOD, 0x8D }, + + { "Logo", 0x88 }, + { KEY_EN_NUMPAD_MINUS, 0x91 }, + { KEY_EN_NUMPAD_PLUS, 0x92 }, + { KEY_EN_NUMPAD_ENTER, 0x94 } + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_ROG_STRIX_SCOPE_II_96_WIRELESS_LAYOUT_KEYS_ANSI, + 101, + 6, + 19, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_F1, 0x08 }, + { KEY_EN_1, 0x09 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F2, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F3, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F4, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_V, 0x2C }, + { KEY_EN_SPACE, 0x2D }, + + { KEY_EN_F6, 0x30 }, + { KEY_EN_6, 0x31 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F7, 0x38 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_N, 0x3C }, + { KEY_EN_SPACE, 0x3D }, + + { KEY_EN_F8, 0x40 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F9, 0x48 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F10, 0x50 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_P, 0x52 }, + { KEY_EN_SEMICOLON, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x55 }, + + { KEY_EN_F11, 0x58 }, + { KEY_EN_MINUS, 0x59 }, + { KEY_EN_LEFT_BRACKET, 0x5A }, + { KEY_EN_QUOTE, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F12, 0x60 }, + { KEY_EN_EQUALS, 0x61 }, + { KEY_EN_RIGHT_BRACKET, 0x62 }, + { KEY_EN_RIGHT_SHIFT, 0x64 }, + { KEY_EN_RIGHT_CONTROL, 0x65 }, + + { KEY_EN_INSERT, 0x68 }, + { KEY_EN_BACKSPACE, 0x69 }, + { KEY_EN_ANSI_BACK_SLASH, 0x6A }, + { KEY_EN_ANSI_ENTER, 0x6B }, + { KEY_EN_LEFT_ARROW, 0x6D }, + + { KEY_EN_DELETE, 0x70 }, + { KEY_EN_UP_ARROW, 0x74 }, + { KEY_EN_DOWN_ARROW, 0x75 }, + + { KEY_EN_PAGE_UP, 0x78 }, + { KEY_EN_NUMPAD_LOCK, 0x79 }, + { KEY_EN_NUMPAD_7, 0x7A }, + { KEY_EN_NUMPAD_4, 0x7B }, + { KEY_EN_NUMPAD_1, 0x7C }, + { KEY_EN_RIGHT_ARROW, 0x7D }, + + { KEY_EN_PAGE_DOWN, 0x80 }, + { KEY_EN_NUMPAD_DIVIDE, 0x81 }, + { KEY_EN_NUMPAD_8, 0x82 }, + { KEY_EN_NUMPAD_5, 0x83 }, + { KEY_EN_NUMPAD_2, 0x84 }, + { KEY_EN_NUMPAD_0, 0x85 }, + + { KEY_EN_NUMPAD_TIMES, 0x89 }, + { KEY_EN_NUMPAD_9, 0x8A }, + { KEY_EN_NUMPAD_6, 0x8B }, + { KEY_EN_NUMPAD_3, 0x8C }, + { KEY_EN_NUMPAD_PERIOD, 0x8D }, + + { "Logo", 0x88 }, + { KEY_EN_NUMPAD_MINUS, 0x91 }, + { KEY_EN_NUMPAD_PLUS, 0x92 }, + { KEY_EN_NUMPAD_ENTER, 0x94 } + } + } + }, +}; + +static std::map AsusROGStrixFlareLayouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_ROG_STRIX_FLARE_LAYOUT_KEYS_ISO, + 108, + 6, + 26, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_POUND, 0x6B }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ISO_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, + + { "Logo", 0xB8 }, + { "Underglow left", 0xB9 }, + { "Underglow right", 0xBA } + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_ROG_STRIX_FLARE_LAYOUT_KEYS_ANSI, + 107, + 6, + 26, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ANSI_BACK_SLASH, 0x7A }, + { KEY_EN_ANSI_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, + + { "Logo", 0xB8 }, + { "Underglow left", 0xB9 }, + { "Underglow right", 0xBA } + } + } + }, +}; + +static std::map AsusROGStrixFlareIILayouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_ROG_STRIX_FLARE_II_LAYOUT_KEYS_ISO, + 107, + 7, + 30, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x15 }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x1D }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_POUND, 0x6B }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ISO_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, + + { "Logo 1", 0xB0 }, + { "Logo 2", 0xA8 }, + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_ROG_STRIX_FLARE_II_LAYOUT_KEYS_ANSI, + 106, + 7, + 30, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_LEFT_WINDOWS, 0x15 }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x1D }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ANSI_BACK_SLASH, 0x7A }, + { KEY_EN_ANSI_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, + + { "Logo 1", 0xB0 }, + { "Logo 2", 0xA8 }, + } + } + }, +}; + +static std::map AsusROGStrixFlareIIAnimateLayouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_ROG_STRIX_FLARE_II_ANIMATE_LAYOUT_KEYS_ISO, + 135, + 7, + 30, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x15 }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x1D }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_POUND, 0x6B }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ISO_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, + + { "Underglow LED 1", 0x06 }, + { "Underglow LED 2", 0x0E }, + { "Underglow LED 3", 0x16 }, + { "Underglow LED 4", 0x1E }, + { "Underglow LED 5", 0x26 }, + { "Underglow LED 6", 0x2E }, + { "Underglow LED 7", 0x36 }, + { "Underglow LED 8", 0x3E }, + { "Underglow LED 9", 0x46 }, + { "Underglow LED 10", 0x4E }, + { "Underglow LED 11", 0x56 }, + { "Underglow LED 12", 0x5E }, + { "Underglow LED 13", 0x66 }, + { "Underglow LED 14", 0x6E }, + { "Underglow LED 15", 0x76 }, + { "Underglow LED 16", 0x7E }, + { "Underglow LED 17", 0x86 }, + { "Underglow LED 18", 0x8E }, + { "Underglow LED 19", 0x96 }, + { "Underglow LED 20", 0x9E }, + { "Underglow LED 21", 0xA6 }, + { "Underglow LED 22", 0xAE }, + { "Underglow LED 23", 0xB6 }, + { "Underglow LED 24", 0xBE }, + { "Underglow LED 25", 0xC6 }, + { "Underglow LED 26", 0xCE }, + { "Underglow LED 27", 0xD6 }, + { "Underglow LED 28", 0xDE }, + { "Underglow LED 29", 0xE6 }, + { "Underglow LED 30", 0xEE }, + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_ROG_STRIX_FLARE_II_ANIMATE_LAYOUT_KEYS_ANSI, + 134, + 7, + 30, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x11 }, + { KEY_EN_LEFT_WINDOWS, 0x15 }, + + { KEY_EN_F1, 0x18 }, + { KEY_EN_2, 0x19 }, + { KEY_EN_Q, 0x12 }, + { KEY_EN_A, 0x13 }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x1D }, + + { KEY_EN_F2, 0x20 }, + { KEY_EN_3, 0x21 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_S, 0x1B }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x28 }, + { KEY_EN_4, 0x29 }, + { KEY_EN_E, 0x22 }, + { KEY_EN_D, 0x23 }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x30 }, + { KEY_EN_5, 0x31 }, + { KEY_EN_R, 0x2A }, + { KEY_EN_F, 0x2B }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x39 }, + { KEY_EN_T, 0x32 }, + { KEY_EN_G, 0x33 }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x40 }, + { KEY_EN_7, 0x41 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_H, 0x3B }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x48 }, + { KEY_EN_8, 0x49 }, + { KEY_EN_U, 0x42 }, + { KEY_EN_J, 0x43 }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x50 }, + { KEY_EN_9, 0x51 }, + { KEY_EN_I, 0x4A }, + { KEY_EN_K, 0x4B }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x58 }, + { KEY_EN_0, 0x59 }, + { KEY_EN_O, 0x52 }, + { KEY_EN_L, 0x53 }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x61 }, + { KEY_EN_P, 0x5A }, + { KEY_EN_SEMICOLON, 0x5B }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x5D }, + + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x69 }, + { KEY_EN_LEFT_BRACKET, 0x62 }, + { KEY_EN_QUOTE, 0x63 }, + { KEY_EN_MENU, 0x65 }, + + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x79 }, + { KEY_EN_RIGHT_BRACKET, 0x6A }, + { KEY_EN_RIGHT_SHIFT, 0x7C }, + + { KEY_EN_F12, 0x78 }, + { KEY_EN_ANSI_BACK_SLASH, 0x7A }, + { KEY_EN_ANSI_ENTER, 0x7B }, + { KEY_EN_RIGHT_CONTROL, 0x7D }, + + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 }, + + { "Underglow LED 1", 0x06 }, + { "Underglow LED 2", 0x0E }, + { "Underglow LED 3", 0x16 }, + { "Underglow LED 4", 0x1E }, + { "Underglow LED 5", 0x26 }, + { "Underglow LED 6", 0x2E }, + { "Underglow LED 7", 0x36 }, + { "Underglow LED 8", 0x3E }, + { "Underglow LED 9", 0x46 }, + { "Underglow LED 10", 0x4E }, + { "Underglow LED 11", 0x56 }, + { "Underglow LED 12", 0x5E }, + { "Underglow LED 13", 0x66 }, + { "Underglow LED 14", 0x6E }, + { "Underglow LED 15", 0x76 }, + { "Underglow LED 16", 0x7E }, + { "Underglow LED 17", 0x86 }, + { "Underglow LED 18", 0x8E }, + { "Underglow LED 19", 0x96 }, + { "Underglow LED 20", 0x9E }, + { "Underglow LED 21", 0xA6 }, + { "Underglow LED 22", 0xAE }, + { "Underglow LED 23", 0xB6 }, + { "Underglow LED 24", 0xBE }, + { "Underglow LED 25", 0xC6 }, + { "Underglow LED 26", 0xCE }, + { "Underglow LED 27", 0xD6 }, + { "Underglow LED 28", 0xDE }, + { "Underglow LED 29", 0xE6 }, + { "Underglow LED 30", 0xEE }, + } + } + }, +}; + +static std::map AsusFalchionLayouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_FALCHION_LAYOUT_KEYS_ISO, + 69, + 5, + 16, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_TAB, 0x01 }, + { KEY_EN_CAPS_LOCK, 0x02 }, + { KEY_EN_LEFT_SHIFT, 0x03 }, + { KEY_EN_LEFT_CONTROL, 0x04 }, + + { KEY_EN_1, 0x08 }, + { KEY_EN_ISO_BACK_SLASH, 0x0B }, + { KEY_EN_LEFT_WINDOWS, 0x0C }, + + { KEY_EN_2, 0x10 }, + { KEY_EN_Q, 0x09 }, + { KEY_EN_A, 0x0A }, + { KEY_EN_Z, 0x13 }, + { KEY_EN_LEFT_ALT, 0x14 }, + + { KEY_EN_3, 0x18 }, + { KEY_EN_W, 0x11 }, + { KEY_EN_S, 0x12 }, + { KEY_EN_X, 0x1B }, + + { KEY_EN_4, 0x20 }, + { KEY_EN_E, 0x19 }, + { KEY_EN_D, 0x1A }, + { KEY_EN_C, 0x23 }, + + { KEY_EN_5, 0x28 }, + { KEY_EN_R, 0x21 }, + { KEY_EN_F, 0x22 }, + { KEY_EN_V, 0x2B }, + + { KEY_EN_6, 0x30 }, + { KEY_EN_T, 0x29 }, + { KEY_EN_G, 0x2A }, + { KEY_EN_B, 0x33 }, + { KEY_EN_SPACE, 0x34 }, + + { KEY_EN_7, 0x38 }, + { KEY_EN_Y, 0x31 }, + { KEY_EN_H, 0x32 }, + { KEY_EN_N, 0x3B }, + + { KEY_EN_8, 0x40 }, + { KEY_EN_U, 0x39 }, + { KEY_EN_J, 0x3A }, + { KEY_EN_M, 0x43 }, + + { KEY_EN_9, 0x48 }, + { KEY_EN_I, 0x41 }, + { KEY_EN_K, 0x42 }, + { KEY_EN_COMMA, 0x4B }, + + { KEY_EN_0, 0x50 }, + { KEY_EN_O, 0x49 }, + { KEY_EN_L, 0x4A }, + { KEY_EN_PERIOD, 0x53 }, + { KEY_EN_RIGHT_ALT, 0x4C }, + + { KEY_EN_MINUS, 0x58 }, + { KEY_EN_P, 0x51 }, + { KEY_EN_SEMICOLON, 0x52 }, + { KEY_EN_FORWARD_SLASH, 0x5B }, + { KEY_EN_RIGHT_FUNCTION, 0x54 }, + + { KEY_EN_EQUALS, 0x60 }, + { KEY_EN_LEFT_BRACKET, 0x59 }, + { KEY_EN_QUOTE, 0x5A }, + { KEY_EN_RIGHT_SHIFT, 0x63 }, + { KEY_EN_RIGHT_CONTROL, 0x5C }, + + { KEY_EN_BACKSPACE, 0x68 }, + { KEY_EN_RIGHT_BRACKET, 0x61 }, + { KEY_EN_POUND, 0x62 }, + { KEY_EN_LEFT_ARROW, 0x64 }, + + { KEY_EN_ISO_ENTER, 0x6A }, + { KEY_EN_UP_ARROW, 0x6B }, + { KEY_EN_DOWN_ARROW, 0x6C }, + + { KEY_EN_INSERT, 0x70 }, + { KEY_EN_DELETE, 0x71 }, + { KEY_EN_PAGE_UP, 0x72 }, + { KEY_EN_PAGE_DOWN, 0x73 }, + { KEY_EN_RIGHT_ARROW, 0x74 }, + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_FALCHION_LAYOUT_KEYS_ANSI, + 68, + 5, + 16, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_TAB, 0x01 }, + { KEY_EN_CAPS_LOCK, 0x02 }, + { KEY_EN_LEFT_SHIFT, 0x03 }, + { KEY_EN_LEFT_CONTROL, 0x04 }, + + { KEY_EN_1, 0x08 }, + { KEY_EN_LEFT_WINDOWS, 0x0C }, + + { KEY_EN_2, 0x10 }, + { KEY_EN_Q, 0x09 }, + { KEY_EN_A, 0x0A }, + { KEY_EN_Z, 0x13 }, + { KEY_EN_LEFT_ALT, 0x14 }, + + { KEY_EN_3, 0x18 }, + { KEY_EN_W, 0x11 }, + { KEY_EN_S, 0x12 }, + { KEY_EN_X, 0x1B }, + + { KEY_EN_4, 0x20 }, + { KEY_EN_E, 0x19 }, + { KEY_EN_D, 0x1A }, + { KEY_EN_C, 0x23 }, + + { KEY_EN_5, 0x28 }, + { KEY_EN_R, 0x21 }, + { KEY_EN_F, 0x22 }, + { KEY_EN_V, 0x2B }, + + { KEY_EN_6, 0x30 }, + { KEY_EN_T, 0x29 }, + { KEY_EN_G, 0x2A }, + { KEY_EN_B, 0x33 }, + { KEY_EN_SPACE, 0x34 }, + + { KEY_EN_7, 0x38 }, + { KEY_EN_Y, 0x31 }, + { KEY_EN_H, 0x32 }, + { KEY_EN_N, 0x3B }, + + { KEY_EN_8, 0x40 }, + { KEY_EN_U, 0x39 }, + { KEY_EN_J, 0x3A }, + { KEY_EN_M, 0x43 }, + + { KEY_EN_9, 0x48 }, + { KEY_EN_I, 0x41 }, + { KEY_EN_K, 0x42 }, + { KEY_EN_COMMA, 0x4B }, + + { KEY_EN_0, 0x50 }, + { KEY_EN_O, 0x49 }, + { KEY_EN_L, 0x4A }, + { KEY_EN_PERIOD, 0x53 }, + { KEY_EN_RIGHT_ALT, 0x4C }, + + { KEY_EN_MINUS, 0x58 }, + { KEY_EN_P, 0x51 }, + { KEY_EN_SEMICOLON, 0x52 }, + { KEY_EN_FORWARD_SLASH, 0x5B }, + { KEY_EN_RIGHT_FUNCTION, 0x54 }, + + { KEY_EN_EQUALS, 0x60 }, + { KEY_EN_LEFT_BRACKET, 0x59 }, + { KEY_EN_QUOTE, 0x5A }, + { KEY_EN_RIGHT_SHIFT, 0x63 }, + { KEY_EN_RIGHT_CONTROL, 0x5C }, + + { KEY_EN_BACKSPACE, 0x68 }, + { KEY_EN_RIGHT_BRACKET, 0x61 }, + { KEY_EN_LEFT_ARROW, 0x64 }, + + { KEY_EN_ANSI_BACK_SLASH, 0x69 }, + { KEY_EN_ANSI_ENTER, 0x6A }, + { KEY_EN_UP_ARROW, 0x6B }, + { KEY_EN_DOWN_ARROW, 0x6C }, + + { KEY_EN_INSERT, 0x70 }, + { KEY_EN_DELETE, 0x71 }, + { KEY_EN_PAGE_UP, 0x72 }, + { KEY_EN_PAGE_DOWN, 0x73 }, + { KEY_EN_RIGHT_ARROW, 0x74 }, + } + } + }, +}; + + +static std::map AsusClaymoreNoNumpadLayouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_CLAYMORE_NO_NUMPAD_LAYOUT_KEYS_ISO, + 89, + 7, + 19, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + { KEY_EN_1, 0x09 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + { KEY_EN_F1, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + { KEY_EN_F2, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_X, 0x1C }, + { KEY_EN_F3, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_C, 0x24 }, + { KEY_EN_F4, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_V, 0x2C }, + { KEY_EN_6, 0x31 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x25 }, + { KEY_EN_F5, 0x38 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_N, 0x3C }, + { KEY_EN_F6, 0x40 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_M, 0x44 }, + { "Logo", 0x45 }, + { KEY_EN_F7, 0x48 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_COMMA, 0x4C }, + { KEY_EN_F8, 0x50 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x59 }, + { KEY_EN_P, 0x52 }, + { KEY_EN_SEMICOLON, 0x53 }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x55 }, + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x61 }, + { KEY_EN_LEFT_BRACKET, 0x5A }, + { KEY_EN_QUOTE, 0x5B }, + { KEY_EN_MENU, 0x5D }, + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x69 }, + { KEY_EN_RIGHT_BRACKET, 0x62 }, + { KEY_EN_POUND, 0x63 }, + { KEY_EN_RIGHT_SHIFT, 0x6C }, + { KEY_EN_F12, 0x78 }, + { KEY_EN_ISO_ENTER, 0x6B }, + { KEY_EN_RIGHT_CONTROL, 0x6D }, + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 } + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_CLAYMORE_NO_NUMPAD_LAYOUT_KEYS_ANSI, + 88, + 7, + 19, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + { KEY_EN_1, 0x09 }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + { KEY_EN_F1, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_EN_Z, 0x0C }, + { KEY_EN_LEFT_ALT, 0x15 }, + { KEY_EN_F2, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_X, 0x14 }, + { KEY_EN_F3, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_C, 0x1C }, + { KEY_EN_F4, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_V, 0x24 }, + { KEY_EN_6, 0x31 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_B, 0x2C }, + { KEY_EN_SPACE, 0x25 }, + { KEY_EN_F5, 0x38 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_N, 0x34 }, + { KEY_EN_F6, 0x40 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_M, 0x3C }, + { "Logo", 0x3D }, + { KEY_EN_F7, 0x48 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_COMMA, 0x44 }, + { KEY_EN_F8, 0x50 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_PERIOD, 0x4C }, + { KEY_EN_RIGHT_ALT, 0x4D }, + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x59 }, + { KEY_EN_P, 0x52 }, + { KEY_EN_SEMICOLON, 0x53 }, + { KEY_EN_FORWARD_SLASH, 0x54 }, + { KEY_EN_RIGHT_FUNCTION, 0x55 }, + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x61 }, + { KEY_EN_LEFT_BRACKET, 0x5A }, + { KEY_EN_QUOTE, 0x5B }, + { KEY_EN_MENU, 0x5D }, + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x69 }, + { KEY_EN_RIGHT_BRACKET, 0x62 }, + { KEY_EN_RIGHT_SHIFT, 0x6C }, + { KEY_EN_F12, 0x78 }, + { KEY_EN_ANSI_BACK_SLASH, 0x6A }, + { KEY_EN_ANSI_ENTER, 0x6B }, + { KEY_EN_RIGHT_CONTROL, 0x6D }, + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 } + } + } + }, +}; + +static std::map AsusClaymoreNumpadRightLayouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_CLAYMORE_NUMPAD_RIGHT_LAYOUT_KEYS_ISO, + 106, + 7, + 24, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + { KEY_EN_1, 0x09 }, + { KEY_EN_ISO_BACK_SLASH, 0x0C }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + { KEY_EN_F1, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + { KEY_EN_F2, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_X, 0x1C }, + { KEY_EN_F3, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_C, 0x24 }, + { KEY_EN_F4, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_V, 0x2C }, + { KEY_EN_6, 0x31 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x25 }, + { KEY_EN_F5, 0x38 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_N, 0x3C }, + { KEY_EN_F6, 0x40 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_M, 0x44 }, + { "Logo", 0x45 }, + { KEY_EN_F7, 0x48 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_COMMA, 0x4C }, + { KEY_EN_F8, 0x50 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_ALT, 0x4D }, + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x59 }, + { KEY_EN_P, 0x52 }, + { KEY_EN_SEMICOLON, 0x53 }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_FUNCTION, 0x55 }, + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x61 }, + { KEY_EN_LEFT_BRACKET, 0x5A }, + { KEY_EN_QUOTE, 0x5B }, + { KEY_EN_MENU, 0x5D }, + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x69 }, + { KEY_EN_RIGHT_BRACKET, 0x62 }, + { KEY_EN_POUND, 0x63 }, + { KEY_EN_RIGHT_SHIFT, 0x6C }, + { KEY_EN_F12, 0x78 }, + { KEY_EN_ISO_ENTER, 0x6B }, + { KEY_EN_RIGHT_CONTROL, 0x6D }, + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 } + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_CLAYMORE_NUMPAD_RIGHT_LAYOUT_KEYS_ANSI, + 105, + 7, + 24, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + { KEY_EN_1, 0x09 }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + { KEY_EN_F1, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_EN_Z, 0x0C }, + { KEY_EN_LEFT_ALT, 0x15 }, + { KEY_EN_F2, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_X, 0x14 }, + { KEY_EN_F3, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_C, 0x1C }, + { KEY_EN_F4, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_V, 0x24 }, + { KEY_EN_6, 0x31 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_B, 0x2C }, + { KEY_EN_SPACE, 0x25 }, + { KEY_EN_F5, 0x38 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_N, 0x34 }, + { KEY_EN_F6, 0x40 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_M, 0x3C }, + { "Logo", 0x3D }, + { KEY_EN_F7, 0x48 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_COMMA, 0x44 }, + { KEY_EN_F8, 0x50 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_PERIOD, 0x4C }, + { KEY_EN_RIGHT_ALT, 0x4D }, + { KEY_EN_F9, 0x60 }, + { KEY_EN_MINUS, 0x59 }, + { KEY_EN_P, 0x52 }, + { KEY_EN_SEMICOLON, 0x53 }, + { KEY_EN_FORWARD_SLASH, 0x54 }, + { KEY_EN_RIGHT_FUNCTION, 0x55 }, + { KEY_EN_F10, 0x68 }, + { KEY_EN_EQUALS, 0x61 }, + { KEY_EN_LEFT_BRACKET, 0x5A }, + { KEY_EN_QUOTE, 0x5B }, + { KEY_EN_MENU, 0x5D }, + { KEY_EN_F11, 0x70 }, + { KEY_EN_BACKSPACE, 0x69 }, + { KEY_EN_RIGHT_BRACKET, 0x62 }, + { KEY_EN_RIGHT_SHIFT, 0x6C }, + { KEY_EN_F12, 0x78 }, + { KEY_EN_ANSI_BACK_SLASH, 0x6A }, + { KEY_EN_ANSI_ENTER, 0x6B }, + { KEY_EN_RIGHT_CONTROL, 0x6D }, + { KEY_EN_PRINT_SCREEN, 0x80 }, + { KEY_EN_INSERT, 0x81 }, + { KEY_EN_DELETE, 0x82 }, + { KEY_EN_LEFT_ARROW, 0x85 }, + { KEY_EN_SCROLL_LOCK, 0x88 }, + { KEY_EN_HOME, 0x89 }, + { KEY_EN_END, 0x8A }, + { KEY_EN_UP_ARROW, 0x8C }, + { KEY_EN_DOWN_ARROW, 0x8D }, + { KEY_EN_PAUSE_BREAK, 0x90 }, + { KEY_EN_PAGE_UP, 0x91 }, + { KEY_EN_PAGE_DOWN, 0x92 }, + { KEY_EN_RIGHT_ARROW, 0x95 }, + { KEY_EN_NUMPAD_LOCK, 0x99 }, + { KEY_EN_NUMPAD_7, 0x9A }, + { KEY_EN_NUMPAD_4, 0x9B }, + { KEY_EN_NUMPAD_1, 0x9C }, + { KEY_EN_NUMPAD_0, 0x9D }, + { KEY_EN_NUMPAD_DIVIDE, 0xA1 }, + { KEY_EN_NUMPAD_8, 0xA2 }, + { KEY_EN_NUMPAD_5, 0xA3 }, + { KEY_EN_NUMPAD_2, 0xA4 }, + { KEY_EN_NUMPAD_TIMES, 0xA9 }, + { KEY_EN_NUMPAD_9, 0xAA }, + { KEY_EN_NUMPAD_6, 0xAB }, + { KEY_EN_NUMPAD_3, 0xAC }, + { KEY_EN_NUMPAD_PERIOD, 0xAD }, + { KEY_EN_NUMPAD_MINUS, 0xB1 }, + { KEY_EN_NUMPAD_PLUS, 0xB2 }, + { KEY_EN_NUMPAD_ENTER, 0xB4 } + } + } + }, +}; + +static std::map AsusClaymoreNumpadLeftLayouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_CLAYMORE_NUMPAD_LEFT_LAYOUT_KEYS_ISO, + 106, + 7, + 24, + { + { KEY_EN_NUMPAD_LOCK, 0x01 }, + { KEY_EN_NUMPAD_7, 0x02 }, + { KEY_EN_NUMPAD_4, 0x03 }, + { KEY_EN_NUMPAD_1, 0x04 }, + { KEY_EN_NUMPAD_0, 0x05 }, + { KEY_EN_NUMPAD_DIVIDE, 0x09 }, + { KEY_EN_NUMPAD_8, 0x0A }, + { KEY_EN_NUMPAD_5, 0x0B }, + { KEY_EN_NUMPAD_2, 0x0C }, + { KEY_EN_NUMPAD_TIMES, 0x11 }, + { KEY_EN_NUMPAD_9, 0x12 }, + { KEY_EN_NUMPAD_6, 0x13 }, + { KEY_EN_NUMPAD_3, 0x14 }, + { KEY_EN_NUMPAD_PERIOD, 0x15 }, + { KEY_EN_NUMPAD_MINUS, 0x19 }, + { KEY_EN_NUMPAD_PLUS, 0x1A }, + { KEY_EN_NUMPAD_ENTER, 0x1C }, + { KEY_EN_ESCAPE, 0x20 }, + { KEY_EN_BACK_TICK, 0x21 }, + { KEY_EN_TAB, 0x22 }, + { KEY_EN_CAPS_LOCK, 0x23 }, + { KEY_EN_LEFT_SHIFT, 0x24 }, + { KEY_EN_LEFT_CONTROL, 0x25 }, + { KEY_EN_1, 0x29 }, + { KEY_EN_ISO_BACK_SLASH, 0x2C }, + { KEY_EN_LEFT_WINDOWS, 0x2D }, + { KEY_EN_F1, 0x30 }, + { KEY_EN_2, 0x31 }, + { KEY_EN_Q, 0x2A }, + { KEY_EN_A, 0x2B }, + { KEY_EN_Z, 0x34 }, + { KEY_EN_LEFT_ALT, 0x35 }, + { KEY_EN_F2, 0x38 }, + { KEY_EN_3, 0x39 }, + { KEY_EN_W, 0x32 }, + { KEY_EN_S, 0x33 }, + { KEY_EN_X, 0x3C }, + { KEY_EN_F3, 0x40 }, + { KEY_EN_4, 0x41 }, + { KEY_EN_E, 0x3A }, + { KEY_EN_D, 0x3B }, + { KEY_EN_C, 0x44 }, + { KEY_EN_F4, 0x48 }, + { KEY_EN_5, 0x49 }, + { KEY_EN_R, 0x42 }, + { KEY_EN_F, 0x43 }, + { KEY_EN_V, 0x4C }, + { KEY_EN_6, 0x51 }, + { KEY_EN_T, 0x4A }, + { KEY_EN_G, 0x4B }, + { KEY_EN_B, 0x54 }, + { KEY_EN_SPACE, 0x45 }, + { KEY_EN_F5, 0x58 }, + { KEY_EN_7, 0x59 }, + { KEY_EN_Y, 0x52 }, + { KEY_EN_H, 0x53 }, + { KEY_EN_N, 0x5C }, + { KEY_EN_F6, 0x60 }, + { KEY_EN_8, 0x61 }, + { KEY_EN_U, 0x5A }, + { KEY_EN_J, 0x5B }, + { KEY_EN_M, 0x64 }, + { "Logo", 0x65 }, + { KEY_EN_F7, 0x68 }, + { KEY_EN_9, 0x69 }, + { KEY_EN_I, 0x62 }, + { KEY_EN_K, 0x63 }, + { KEY_EN_COMMA, 0x6C }, + { KEY_EN_F8, 0x70 }, + { KEY_EN_0, 0x71 }, + { KEY_EN_O, 0x6A }, + { KEY_EN_L, 0x6B }, + { KEY_EN_PERIOD, 0x74 }, + { KEY_EN_RIGHT_ALT, 0x6D }, + { KEY_EN_F9, 0x80 }, + { KEY_EN_MINUS, 0x79 }, + { KEY_EN_P, 0x72 }, + { KEY_EN_SEMICOLON, 0x73 }, + { KEY_EN_FORWARD_SLASH, 0x7C }, + { KEY_EN_RIGHT_FUNCTION, 0x75 }, + { KEY_EN_F10, 0x88 }, + { KEY_EN_EQUALS, 0x81 }, + { KEY_EN_LEFT_BRACKET, 0x7A }, + { KEY_EN_QUOTE, 0x7B }, + { KEY_EN_MENU, 0x7D }, + { KEY_EN_F11, 0x90 }, + { KEY_EN_BACKSPACE, 0x89 }, + { KEY_EN_RIGHT_BRACKET, 0x82 }, + { KEY_EN_POUND, 0x83 }, + { KEY_EN_RIGHT_SHIFT, 0x8C }, + { KEY_EN_F12, 0x98 }, + { KEY_EN_ISO_ENTER, 0x8B }, + { KEY_EN_RIGHT_CONTROL, 0x8D }, + { KEY_EN_PRINT_SCREEN, 0xA0 }, + { KEY_EN_INSERT, 0xA1 }, + { KEY_EN_DELETE, 0xA2 }, + { KEY_EN_LEFT_ARROW, 0xA5 }, + { KEY_EN_SCROLL_LOCK, 0xA8 }, + { KEY_EN_HOME, 0xA9 }, + { KEY_EN_END, 0xAA }, + { KEY_EN_UP_ARROW, 0xAC }, + { KEY_EN_DOWN_ARROW, 0xAD }, + { KEY_EN_PAUSE_BREAK, 0xB0 }, + { KEY_EN_PAGE_UP, 0xB1 }, + { KEY_EN_PAGE_DOWN, 0xB2 }, + { KEY_EN_RIGHT_ARROW, 0xB5 } + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_CLAYMORE_NUMPAD_LEFT_LAYOUT_KEYS_ANSI, + 105, + 7, + 24, + { + { KEY_EN_NUMPAD_LOCK, 0x01 }, + { KEY_EN_NUMPAD_7, 0x02 }, + { KEY_EN_NUMPAD_4, 0x03 }, + { KEY_EN_NUMPAD_1, 0x04 }, + { KEY_EN_NUMPAD_0, 0x05 }, + { KEY_EN_NUMPAD_DIVIDE, 0x09 }, + { KEY_EN_NUMPAD_8, 0x0A }, + { KEY_EN_NUMPAD_5, 0x0B }, + { KEY_EN_NUMPAD_2, 0x0C }, + { KEY_EN_NUMPAD_TIMES, 0x11 }, + { KEY_EN_NUMPAD_9, 0x12 }, + { KEY_EN_NUMPAD_6, 0x13 }, + { KEY_EN_NUMPAD_3, 0x14 }, + { KEY_EN_NUMPAD_PERIOD, 0x15 }, + { KEY_EN_NUMPAD_MINUS, 0x19 }, + { KEY_EN_NUMPAD_PLUS, 0x1A }, + { KEY_EN_NUMPAD_ENTER, 0x1C }, + { KEY_EN_ESCAPE, 0x20 }, + { KEY_EN_BACK_TICK, 0x21 }, + { KEY_EN_TAB, 0x22 }, + { KEY_EN_CAPS_LOCK, 0x23 }, + { KEY_EN_LEFT_SHIFT, 0x24 }, + { KEY_EN_LEFT_CONTROL, 0x25 }, + { KEY_EN_1, 0x29 }, + { KEY_EN_LEFT_WINDOWS, 0x2D }, + { KEY_EN_F1, 0x30 }, + { KEY_EN_2, 0x31 }, + { KEY_EN_Q, 0x2A }, + { KEY_EN_A, 0x2B }, + { KEY_EN_Z, 0x2C }, + { KEY_EN_LEFT_ALT, 0x35 }, + { KEY_EN_F2, 0x38 }, + { KEY_EN_3, 0x39 }, + { KEY_EN_W, 0x32 }, + { KEY_EN_S, 0x33 }, + { KEY_EN_X, 0x34 }, + { KEY_EN_F3, 0x40 }, + { KEY_EN_4, 0x41 }, + { KEY_EN_E, 0x3A }, + { KEY_EN_D, 0x3B }, + { KEY_EN_C, 0x3C }, + { KEY_EN_F4, 0x48 }, + { KEY_EN_5, 0x49 }, + { KEY_EN_R, 0x42 }, + { KEY_EN_F, 0x43 }, + { KEY_EN_V, 0x44 }, + { KEY_EN_6, 0x51 }, + { KEY_EN_T, 0x4A }, + { KEY_EN_G, 0x4B }, + { KEY_EN_B, 0x4C }, + { KEY_EN_SPACE, 0x45 }, + { KEY_EN_F5, 0x58 }, + { KEY_EN_7, 0x59 }, + { KEY_EN_Y, 0x52 }, + { KEY_EN_H, 0x53 }, + { KEY_EN_N, 0x54 }, + { KEY_EN_F6, 0x60 }, + { KEY_EN_8, 0x61 }, + { KEY_EN_U, 0x5A }, + { KEY_EN_J, 0x5B }, + { KEY_EN_M, 0x5C }, + { "Logo", 0x5D }, + { KEY_EN_F7, 0x68 }, + { KEY_EN_9, 0x69 }, + { KEY_EN_I, 0x62 }, + { KEY_EN_K, 0x63 }, + { KEY_EN_COMMA, 0x64 }, + { KEY_EN_F8, 0x70 }, + { KEY_EN_0, 0x71 }, + { KEY_EN_O, 0x6A }, + { KEY_EN_L, 0x6B }, + { KEY_EN_PERIOD, 0x6C }, + { KEY_EN_RIGHT_ALT, 0x6D }, + { KEY_EN_F9, 0x80 }, + { KEY_EN_MINUS, 0x79 }, + { KEY_EN_P, 0x72 }, + { KEY_EN_SEMICOLON, 0x73 }, + { KEY_EN_FORWARD_SLASH, 0x74 }, + { KEY_EN_RIGHT_FUNCTION, 0x75 }, + { KEY_EN_F10, 0x88 }, + { KEY_EN_EQUALS, 0x81 }, + { KEY_EN_LEFT_BRACKET, 0x7A }, + { KEY_EN_QUOTE, 0x7B }, + { KEY_EN_MENU, 0x7D }, + { KEY_EN_F11, 0x90 }, + { KEY_EN_BACKSPACE, 0x89 }, + { KEY_EN_RIGHT_BRACKET, 0x82 }, + { KEY_EN_RIGHT_SHIFT, 0x8C }, + { KEY_EN_F12, 0x98 }, + { KEY_EN_ANSI_BACK_SLASH, 0x8A }, + { KEY_EN_ANSI_ENTER, 0x8B }, + { KEY_EN_RIGHT_CONTROL, 0x8D }, + { KEY_EN_PRINT_SCREEN, 0xA0 }, + { KEY_EN_INSERT, 0xA1 }, + { KEY_EN_DELETE, 0xA2 }, + { KEY_EN_LEFT_ARROW, 0xA5 }, + { KEY_EN_SCROLL_LOCK, 0xA8 }, + { KEY_EN_HOME, 0xA9 }, + { KEY_EN_END, 0xAA }, + { KEY_EN_UP_ARROW, 0xAC }, + { KEY_EN_DOWN_ARROW, 0xAD }, + { KEY_EN_PAUSE_BREAK, 0xB0 }, + { KEY_EN_PAGE_UP, 0xB1 }, + { KEY_EN_PAGE_DOWN, 0xB2 }, + { KEY_EN_RIGHT_ARROW, 0xB5 } + } + } + }, +}; + +static std::map AsusTufK1Layouts = +{ + { + ASUS_TUF_K7_LAYOUT_UK, + { + *ASUS_TUF_K1_LAYOUT_KEYS, + 5, + 1, + 5, + { + { "Keyboard LED 1", 0x00 }, + { "Keyboard LED 2", 0x01 }, + { "Keyboard LED 3", 0x02 }, + { "Keyboard LED 4", 0x03 }, + { "Keyboard LED 5", 0x04 }, + } + } + }, + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_TUF_K1_LAYOUT_KEYS, + 5, + 1, + 5, + { + { "Keyboard LED 1", 0x00 }, + { "Keyboard LED 2", 0x01 }, + { "Keyboard LED 3", 0x02 }, + { "Keyboard LED 4", 0x03 }, + { "Keyboard LED 5", 0x04 }, + } + } + }, +}; + +static std::map AsusTUFK3GamingGen2Layouts = +{ + { + ASUS_TUF_K7_LAYOUT_US, + { + *ASUS_TUF_K3_GAMING_GEN_II_LAYOUT_KEYS_ANSI, + 97, + 6, + 19, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x09 }, + { KEY_EN_LEFT_WINDOWS, 0x0D }, + + { KEY_EN_F1, 0x10 }, + { KEY_EN_2, 0x11 }, + { KEY_EN_Q, 0x0A }, + { KEY_EN_A, 0x0B }, + { KEY_EN_Z, 0x14 }, + { KEY_EN_LEFT_ALT, 0x15 }, + + { KEY_EN_F2, 0x18 }, + { KEY_EN_3, 0x19 }, + { KEY_EN_W, 0x12 }, + { KEY_EN_S, 0x13 }, + { KEY_EN_X, 0x1C }, + + { KEY_EN_F3, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_E, 0x1A }, + { KEY_EN_D, 0x1B }, + { KEY_EN_C, 0x24 }, + + { KEY_EN_F4, 0x28 }, + { KEY_EN_5, 0x29 }, + { KEY_EN_R, 0x22 }, + { KEY_EN_F, 0x23 }, + { KEY_EN_V, 0x2C }, + + { KEY_EN_6, 0x31 }, + { KEY_EN_T, 0x2A }, + { KEY_EN_G, 0x2B }, + { KEY_EN_B, 0x34 }, + { KEY_EN_SPACE, 0x35 }, + + { KEY_EN_F5, 0x30 }, + { KEY_EN_7, 0x39 }, + { KEY_EN_Y, 0x32 }, + { KEY_EN_H, 0x33 }, + { KEY_EN_N, 0x3C }, + + { KEY_EN_F6, 0x38 }, + { KEY_EN_8, 0x41 }, + { KEY_EN_U, 0x3A }, + { KEY_EN_J, 0x3B }, + { KEY_EN_M, 0x44 }, + + { KEY_EN_F7, 0x40 }, + { KEY_EN_9, 0x49 }, + { KEY_EN_I, 0x42 }, + { KEY_EN_K, 0x43 }, + { KEY_EN_COMMA, 0x4C }, + + { KEY_EN_F8, 0x48 }, + { KEY_EN_0, 0x51 }, + { KEY_EN_O, 0x4A }, + { KEY_EN_L, 0x4B }, + { KEY_EN_PERIOD, 0x54 }, + { KEY_EN_RIGHT_FUNCTION, 0x55 }, + + { KEY_EN_F9, 0x58 }, + { KEY_EN_MINUS, 0x59 }, + { KEY_EN_P, 0x52 }, + { KEY_EN_SEMICOLON, 0x53 }, + { KEY_EN_FORWARD_SLASH, 0x5C }, + { KEY_EN_RIGHT_CONTROL, 0x5D }, + + { KEY_EN_F10, 0x60 }, + { KEY_EN_EQUALS, 0x61 }, + { KEY_EN_LEFT_BRACKET, 0x5A }, + { KEY_EN_QUOTE, 0x5B }, + { KEY_EN_RIGHT_SHIFT, 0x6C }, + + { KEY_EN_F11, 0x68 }, + { KEY_EN_BACKSPACE, 0x71 }, + { KEY_EN_RIGHT_BRACKET, 0x62 }, + { KEY_EN_ANSI_ENTER, 0x73 }, + { KEY_EN_LEFT_ARROW, 0x6D }, + + { KEY_EN_F12, 0x70 }, + { KEY_EN_ANSI_BACK_SLASH, 0x72 }, + { KEY_EN_UP_ARROW, 0x74 }, + { KEY_EN_DOWN_ARROW, 0x75 }, + + { KEY_EN_DELETE, 0x80 }, + { KEY_EN_NUMPAD_LOCK, 0x79 }, + { KEY_EN_NUMPAD_7, 0x7A }, + { KEY_EN_NUMPAD_4, 0x7B }, + { KEY_EN_NUMPAD_1, 0x7C }, + { KEY_EN_RIGHT_ARROW, 0x7D }, + + { KEY_EN_INSERT, 0x78 }, + { KEY_EN_NUMPAD_DIVIDE, 0x81 }, + { KEY_EN_NUMPAD_8, 0x82 }, + { KEY_EN_NUMPAD_5, 0x83 }, + { KEY_EN_NUMPAD_2, 0x84 }, + { KEY_EN_NUMPAD_0, 0x85 }, + + { KEY_EN_PAGE_UP, 0x88 }, + { KEY_EN_NUMPAD_TIMES, 0x89 }, + { KEY_EN_NUMPAD_9, 0x8A }, + { KEY_EN_NUMPAD_6, 0x8B }, + { KEY_EN_NUMPAD_3, 0x8C }, + { KEY_EN_NUMPAD_PERIOD, 0x8D }, + + { KEY_EN_PAGE_DOWN, 0x90 }, + { KEY_EN_NUMPAD_MINUS, 0x91 }, + { KEY_EN_NUMPAD_PLUS, 0x92 }, + { KEY_EN_NUMPAD_ENTER, 0x94 } + } + } + } +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.cpp b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.cpp new file mode 100644 index 0000000..baf33bd --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.cpp @@ -0,0 +1,672 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraTUFKeyboard.cpp | +| | +| RGBController for ASUS Aura TUF keyboard | +| | +| Mola19 03 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_AsusAuraTUFKeyboard.h" + +/**------------------------------------------------------------------*\ + @name Asus Aura TUF Keyboard + @category Keyboard + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraTUFUSBKeyboard + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AuraTUFKeyboard::RGBController_AuraTUFKeyboard(AuraTUFKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + pid = controller->device_pid; + + if(pid != AURA_ROG_CLAYMORE_PID) + { + name = controller->GetName(); + vendor = "ASUS"; + type = DEVICE_TYPE_KEYBOARD; + description = "ASUS Aura Keyboard Device"; + version = controller->GetVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + unsigned char AURA_KEYBOARD_SPEED_MIN = 0; + unsigned char AURA_KEYBOARD_SPEED_MAX = 0; + unsigned char AURA_KEYBOARD_SPEED_DEFAULT = 0; + + switch(pid) + { + case AURA_TUF_K1_GAMING_PID: + AURA_KEYBOARD_SPEED_MIN = 0; + AURA_KEYBOARD_SPEED_MAX = 2; + AURA_KEYBOARD_SPEED_DEFAULT = 1; + break; + + case AURA_ROG_STRIX_FLARE_PID: + case AURA_ROG_STRIX_FLARE_PNK_LTD_PID: + case AURA_ROG_STRIX_FLARE_COD_BO4_PID: + case AURA_TUF_K3_GAMING_PID: + case AURA_TUF_K7_GAMING_PID: + case AURA_TUF_K3_GENII_MIKU_EDITION_PID: + AURA_KEYBOARD_SPEED_MIN = 15; + AURA_KEYBOARD_SPEED_MAX = 0; + AURA_KEYBOARD_SPEED_DEFAULT = 8; + break; + + case AURA_ROG_AZOTH_USB_PID: + case AURA_ROG_AZOTH_2_4_PID: + case AURA_ROG_FALCHION_WIRED_PID: + case AURA_ROG_FALCHION_WIRELESS_PID: + case AURA_ROG_STRIX_FLARE_II_PID: + case AURA_ROG_STRIX_FLARE_II_ANIMATE_PID: + case AURA_ROG_STRIX_SCOPE_RX_PID: + case AURA_ROG_STRIX_SCOPE_RX_EVA_02_PID: + case AURA_ROG_STRIX_SCOPE_PID: + case AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_2_4_PID: + case AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_USB_PID: + case AURA_ROG_STRIX_SCOPE_II_PID: + case AURA_ROG_STRIX_SCOPE_II_RX_PID: + case AURA_ROG_STRIX_SCOPE_II_96_WIRELESS_USB_PID: + case AURA_ROG_STRIX_SCOPE_II_96_RX_WIRELESS_USB_PID: + case AURA_TUF_K5_GAMING_PID: + AURA_KEYBOARD_SPEED_MIN = 255; + AURA_KEYBOARD_SPEED_MAX = 0; + AURA_KEYBOARD_SPEED_DEFAULT = 30; + break; + + default: + AURA_KEYBOARD_SPEED_MIN = 15; + AURA_KEYBOARD_SPEED_MAX = 0; + AURA_KEYBOARD_SPEED_DEFAULT = 8; + break; + } + + mode Direct; + Direct.name = "Direct"; + Direct.value = AURA_KEYBOARD_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = AURA_KEYBOARD_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; + Static.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; + Static.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AURA_KEYBOARD_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + if(controller->is_per_led_keyboard) + { + Breathing.flags |= MODE_FLAG_HAS_RANDOM_COLOR; + } + Breathing.speed_min = AURA_KEYBOARD_SPEED_MIN; + Breathing.speed_max = AURA_KEYBOARD_SPEED_MAX; + Breathing.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Breathing.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; + Breathing.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; + Breathing.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Color_Cycle; + Color_Cycle.name = "Spectrum Cycle"; + Color_Cycle.value = AURA_KEYBOARD_MODE_COLOR_CYCLE; + Color_Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Color_Cycle.speed_min = AURA_KEYBOARD_SPEED_MIN; + Color_Cycle.speed_max = AURA_KEYBOARD_SPEED_MAX; + Color_Cycle.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Color_Cycle.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; + Color_Cycle.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; + Color_Cycle.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; + Color_Cycle.color_mode = MODE_COLORS_NONE; + modes.push_back(Color_Cycle); + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = AURA_KEYBOARD_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + if(controller->is_per_led_keyboard) + { + Wave.flags |= MODE_FLAG_HAS_DIRECTION_UD; + } + Wave.speed_min = AURA_KEYBOARD_SPEED_MIN; + Wave.speed_max = AURA_KEYBOARD_SPEED_MAX; + Wave.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Wave.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; + Wave.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; + Wave.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; + Wave.direction = MODE_DIRECTION_LEFT; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + + if(!controller->is_per_led_keyboard) + { + Wave.colors_min = 5; + Wave.colors_max = 5; + } + else + { + Wave.colors_min = 1; + Wave.colors_max = 7; + } + + Wave.colors.resize(Wave.colors_max); + modes.push_back(Wave); + + if(controller->is_per_led_keyboard) + { + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = AURA_KEYBOARD_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Reactive.speed_min = AURA_KEYBOARD_SPEED_MIN; + Reactive.speed_max = AURA_KEYBOARD_SPEED_MAX; + Reactive.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Reactive.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; + Reactive.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; + Reactive.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.colors_min = 1; + Reactive.colors_max = 2; + Reactive.colors.resize(1); + modes.push_back(Reactive); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = AURA_KEYBOARD_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Ripple.speed_min = AURA_KEYBOARD_SPEED_MIN; + Ripple.speed_max = AURA_KEYBOARD_SPEED_MAX; + Ripple.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Ripple.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; + Ripple.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; + Ripple.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripple.colors_min = 1; + Ripple.colors_max = 8; + Ripple.colors.resize(7); + modes.push_back(Ripple); + + mode Starry_Night; + Starry_Night.name = "Starry Night"; + Starry_Night.value = AURA_KEYBOARD_MODE_STARRY_NIGHT; + Starry_Night.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Starry_Night.speed_min = AURA_KEYBOARD_SPEED_MIN; + Starry_Night.speed_max = AURA_KEYBOARD_SPEED_MAX; + Starry_Night.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Starry_Night.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; + Starry_Night.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; + Starry_Night.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; + Starry_Night.color_mode = MODE_COLORS_MODE_SPECIFIC; + Starry_Night.colors_min = 1; + Starry_Night.colors_max = 3; + Starry_Night.colors.resize(1); + modes.push_back(Starry_Night); + + mode Quicksand; + Quicksand.name = "Quicksand"; + Quicksand.value = AURA_KEYBOARD_MODE_QUICKSAND; + Quicksand.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Quicksand.direction = MODE_DIRECTION_DOWN; + Quicksand.speed_min = AURA_KEYBOARD_SPEED_MIN; + Quicksand.speed_max = AURA_KEYBOARD_SPEED_MAX; + Quicksand.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Quicksand.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; + Quicksand.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; + Quicksand.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; + Quicksand.color_mode = MODE_COLORS_MODE_SPECIFIC; + Quicksand.colors_min = 6; + Quicksand.colors_max = 6; + Quicksand.colors.resize(6); + modes.push_back(Quicksand); + + mode Current; + Current.name = "Current"; + Current.value = AURA_KEYBOARD_MODE_CURRENT; + Current.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Current.speed_min = AURA_KEYBOARD_SPEED_MIN; + Current.speed_max = AURA_KEYBOARD_SPEED_MAX; + Current.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Current.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; + Current.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; + Current.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; + Current.color_mode = MODE_COLORS_MODE_SPECIFIC; + Current.colors_min = 1; + Current.colors_max = 3; + Current.colors.resize(1); + modes.push_back(Current); + + mode Rain_Drop; + Rain_Drop.name = "Rain Drop"; + Rain_Drop.value = AURA_KEYBOARD_MODE_RAIN_DROP; + Rain_Drop.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Rain_Drop.speed_min = AURA_KEYBOARD_SPEED_MIN; + Rain_Drop.speed_max = AURA_KEYBOARD_SPEED_MAX; + Rain_Drop.speed = AURA_KEYBOARD_SPEED_DEFAULT; + Rain_Drop.brightness_min = AURA_KEYBOARD_BRIGHTNESS_MIN; + Rain_Drop.brightness_max = AURA_KEYBOARD_BRIGHTNESS_MAX; + Rain_Drop.brightness = AURA_KEYBOARD_BRIGHTNESS_DEFAULT; + Rain_Drop.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rain_Drop.colors_min = 1; + Rain_Drop.colors_max = 3; + Rain_Drop.colors.resize(1); + modes.push_back(Rain_Drop); + } + } + else + { + + name = controller->GetName(); + vendor = "ASUS"; + type = DEVICE_TYPE_KEYBOARD; + description = "ASUS Aura Keyboard Device"; + version = controller->GetVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = AURA_KEYBOARD_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = AURA_KEYBOARD_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AURA_KEYBOARD_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = AURA_CLAYMORE_SPEED_MIN; + Breathing.speed_max = AURA_CLAYMORE_SPEED_MAX; + Breathing.speed = AURA_CLAYMORE_SPEED_DEFAULT_BREATHING; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(2); + modes.push_back(Breathing); + + mode Color_Cycle; + Color_Cycle.name = "Spectrum Cycle"; + Color_Cycle.value = AURA_KEYBOARD_MODE_COLOR_CYCLE; + Color_Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Color_Cycle.speed_min = AURA_CLAYMORE_SPEED_MIN; + Color_Cycle.speed_max = AURA_CLAYMORE_SPEED_MAX; + Color_Cycle.speed = AURA_CLAYMORE_SPEED_DEFAULT_COLOR_CYCLE; + Color_Cycle.color_mode = MODE_COLORS_NONE; + modes.push_back(Color_Cycle); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = AURA_KEYBOARD_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Reactive.speed_min = AURA_CLAYMORE_SPEED_MIN; + Reactive.speed_max = AURA_CLAYMORE_SPEED_MAX; + Reactive.speed = AURA_CLAYMORE_SPEED_DEFAULT_REACTIVE; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.colors_min = 1; + Reactive.colors_max = 2; + Reactive.colors.resize(2); + modes.push_back(Reactive); + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = AURA_KEYBOARD_MODE_WAVE; + Wave.flags = MODE_COLORS_NONE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_AUTOMATIC_SAVE; + Wave.speed_min = AURA_CLAYMORE_SPEED_MIN; + Wave.speed_max = AURA_CLAYMORE_SPEED_MAX; + Wave.speed = AURA_CLAYMORE_SPEED_DEFAULT_WAVE; + Wave.direction = MODE_DIRECTION_LEFT; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + + mode Color_Wave; + Color_Wave.name = "Color Wave"; + Color_Wave.value = AURA_KEYBOARD_MODE_WAVE; + Color_Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_AUTOMATIC_SAVE; + Color_Wave.speed_min = AURA_CLAYMORE_SPEED_MIN; + Color_Wave.speed_max = AURA_CLAYMORE_SPEED_MAX; + Color_Wave.speed = AURA_CLAYMORE_SPEED_DEFAULT_WAVE; + Color_Wave.direction = MODE_DIRECTION_LEFT; + Color_Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Color_Wave.colors_min = 1; + Color_Wave.colors_max = 2; + Color_Wave.colors.resize(2); + modes.push_back(Color_Wave); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = AURA_KEYBOARD_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Ripple.speed_min = AURA_CLAYMORE_SPEED_MIN; + Ripple.speed_max = AURA_CLAYMORE_SPEED_MAX; + Ripple.speed = AURA_CLAYMORE_SPEED_DEFAULT_RIPPLE; + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripple.colors_min = 1; + Ripple.colors_max = 2; + Ripple.colors.resize(2); + modes.push_back(Ripple); + + mode Starry_Night; + Starry_Night.name = "Starry Night"; + Starry_Night.value = AURA_KEYBOARD_MODE_STARRY_NIGHT; + Starry_Night.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Starry_Night.speed_min = AURA_CLAYMORE_SPEED_MIN; + Starry_Night.speed_max = AURA_CLAYMORE_SPEED_MAX; + Starry_Night.speed = AURA_CLAYMORE_SPEED_DEFAULT_STARRY_NIGHT; + Starry_Night.color_mode = MODE_COLORS_MODE_SPECIFIC; + Starry_Night.colors_min = 1; + Starry_Night.colors_max = 2; + Starry_Night.colors.resize(2); + modes.push_back(Starry_Night); + + mode Quicksand; + Quicksand.name = "Quicksand"; + Quicksand.value = AURA_KEYBOARD_MODE_QUICKSAND; + Quicksand.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Quicksand.direction = MODE_DIRECTION_DOWN; + Quicksand.speed_min = AURA_CLAYMORE_SPEED_MIN; + Quicksand.speed_max = AURA_CLAYMORE_SPEED_MAX; + Quicksand.speed = AURA_CLAYMORE_SPEED_DEFAULT_QUICKSAND; + Quicksand.color_mode = MODE_COLORS_MODE_SPECIFIC; + Quicksand.colors_min = 6; + Quicksand.colors_max = 6; + Quicksand.colors.resize(6); + modes.push_back(Quicksand); + } + + SetupZones(); +} + +RGBController_AuraTUFKeyboard::~RGBController_AuraTUFKeyboard() +{ + delete controller; +} + +void RGBController_AuraTUFKeyboard::SetupZones() +{ + std::map * keyboard_ptr; + + switch(pid) + { + case AURA_ROG_STRIX_FLARE_PID: + case AURA_ROG_STRIX_FLARE_PNK_LTD_PID: + case AURA_ROG_STRIX_FLARE_COD_BO4_PID: + keyboard_ptr = &AsusROGStrixFlareLayouts; + break; + case AURA_TUF_K3_GAMING_PID: + case AURA_TUF_K7_GAMING_PID: + case AURA_TUF_K3_GENII_MIKU_EDITION_PID: + keyboard_ptr = &AsusTUFK7Layouts; + break; + case AURA_ROG_STRIX_SCOPE_PID: + case AURA_ROG_STRIX_SCOPE_RX_PID: + case AURA_ROG_STRIX_SCOPE_RX_EVA_02_PID: + case AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_USB_PID: + case AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_2_4_PID: + keyboard_ptr = &AsusROGStrixScopeLayouts; + break; + case AURA_ROG_STRIX_SCOPE_II_PID: + case AURA_ROG_STRIX_SCOPE_II_RX_PID: + keyboard_ptr = &AsusROGStrixScopeIILayouts; + break; + case AURA_ROG_STRIX_SCOPE_II_96_WIRELESS_USB_PID: + keyboard_ptr = &AsusROGStrixScopeII96WirelessLayouts; + break; + case AURA_ROG_STRIX_SCOPE_II_96_RX_WIRELESS_USB_PID: + keyboard_ptr = &AsusROGStrixScopeII96RxWirelessLayouts; + break; + case AURA_ROG_STRIX_FLARE_II_PID: + keyboard_ptr = &AsusROGStrixFlareIILayouts; + break; + case AURA_ROG_STRIX_FLARE_II_ANIMATE_PID: + keyboard_ptr = &AsusROGStrixFlareIIAnimateLayouts; + break; + case AURA_ROG_AZOTH_USB_PID: + case AURA_ROG_AZOTH_2_4_PID: + keyboard_ptr = &AsusROGAzothLayouts; + break; + case AURA_ROG_FALCHION_WIRED_PID: + case AURA_ROG_FALCHION_WIRELESS_PID: + keyboard_ptr = &AsusFalchionLayouts; + break; + case AURA_ROG_CLAYMORE_PID: + unsigned char numpad; + numpad = controller->GetNumpadLocation(); + switch(numpad) + { + case 0: + keyboard_ptr = &AsusClaymoreNoNumpadLayouts; + break; + case 2: + keyboard_ptr = &AsusClaymoreNumpadRightLayouts; + break; + case 3: + keyboard_ptr = &AsusClaymoreNumpadLeftLayouts; + break; + default: + keyboard_ptr = &AsusClaymoreNoNumpadLayouts; + } + break; + case AURA_TUF_K1_GAMING_PID: + case AURA_TUF_K5_GAMING_PID: + keyboard_ptr = &AsusTufK1Layouts; + break; + case AURA_TUF_K3_GAMING_GEN_II_PID: + keyboard_ptr = &AsusTUFK3GamingGen2Layouts; + break; + default: + keyboard_ptr = &AsusTUFK7Layouts; + } + + std::map & keyboard = *keyboard_ptr; + + unsigned char layout = controller->GetLayout(); + + if(keyboard.find(layout % 100) == keyboard.end()) + { + /*---------------------------------------------------------*\ + | If Layout not found, take uk or us | + \*---------------------------------------------------------*/ + layout = std::floor(layout/100) == 2 ? ASUS_TUF_K7_LAYOUT_UK : ASUS_TUF_K7_LAYOUT_US; + } + else + { + layout = layout % 100; + } + + zone keyboard_zone; + keyboard_zone.name = "Keyboard"; + keyboard_zone.type = ZONE_TYPE_MATRIX; + keyboard_zone.leds_min = keyboard[layout].size; + keyboard_zone.leds_max = keyboard[layout].size; + keyboard_zone.leds_count = keyboard[layout].size; + keyboard_zone.matrix_map = new matrix_map_type; + keyboard_zone.matrix_map->height = keyboard[layout].rows; + keyboard_zone.matrix_map->width = keyboard[layout].cols; + keyboard_zone.matrix_map->map = keyboard[layout].matrix_map; + zones.push_back(keyboard_zone); + + for(int led_id = 0; led_id < keyboard[layout].size; led_id++) + { + led new_led; + new_led.name = keyboard[layout].led_names[led_id].name; + new_led.value = keyboard[layout].led_names[led_id].id; + leds.push_back(new_led); + } + + SetupColors(); + + /*---------------------------------------------------------*\ + | sends the init packet for the default mode (direct) | + \*---------------------------------------------------------*/ + DeviceUpdateMode(); +} + +void RGBController_AuraTUFKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_AuraTUFKeyboard::DeviceUpdateLEDs() +{ + std::vector led_color_list = {}; + + for(size_t i = 0; i < colors.size(); i++) + { + led_color_list.push_back({ leds[i].value, colors[i] }); + } + + controller->UpdateLeds(led_color_list); +} + +void RGBController_AuraTUFKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AuraTUFKeyboard::UpdateSingleLED(int led) +{ + if(!controller->is_per_led_keyboard) + { + return DeviceUpdateLEDs(); + } + + unsigned char red = RGBGetRValue(colors[led]); + unsigned char green = RGBGetGValue(colors[led]); + unsigned char blue = RGBGetBValue(colors[led]); + + controller->UpdateSingleLed(leds[led].value, red, green, blue); +} + +static const uint8_t direction_map[2][6] = +{ + { 4, 0, 6, 2, 8, 1 }, // Default directions Left, Right, Up, Down, Horizontal, Vertical + { 0, 4, 6, 2, 0xFF, 0xFF }, // AURA_ROG_CLAYMORE directions Left, Right, Up, Down +}; + +void RGBController_AuraTUFKeyboard::DeviceUpdateMode() +{ + if(pid == AURA_ROG_CLAYMORE_PID) + { + controller->AllowRemoteControl(1); + } + + unsigned char color_mode = 0; + unsigned char direction = 0; + unsigned char brightness = 0; + + if(modes[active_mode].value == AURA_KEYBOARD_MODE_DIRECT) + { + if(pid == AURA_ROG_CLAYMORE_PID) controller->AllowRemoteControl(3); + return; + }; + + if(pid != AURA_ROG_CLAYMORE_PID) + { + brightness = modes[active_mode].brightness * 25; + + switch(modes[active_mode].value) + { + case AURA_KEYBOARD_MODE_BREATHING: + case AURA_KEYBOARD_MODE_REACTIVE: + case AURA_KEYBOARD_MODE_STARRY_NIGHT: + case AURA_KEYBOARD_MODE_CURRENT: + case AURA_KEYBOARD_MODE_RAIN_DROP: + if(!controller->is_per_led_keyboard && modes[active_mode].colors.size() > 1) + { + color_mode = 1; + break; + } + + bool color_is_black = (modes[active_mode].colors.size() > 1 && modes[active_mode].colors[1] == 000); + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC && !color_is_black) + { + color_mode = 16; + } + break; + } + + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + color_mode = 1; + } + + if(modes[active_mode].value == AURA_KEYBOARD_MODE_WAVE || modes[active_mode].value == AURA_KEYBOARD_MODE_QUICKSAND) + { + /*----------------------------------------------------------*\ + | converting openrgb direction value to keyboard directions | + \*----------------------------------------------------------*/ + direction = direction_map[0][modes[active_mode].direction]; + } + } + else + { + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + color_mode = 1; + } + + if(modes[active_mode].value == AURA_KEYBOARD_MODE_WAVE) { + if (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) color_mode = 2; + + /*----------------------------------------------------------*\ + | converting openrgb direction value to keyboard directions | + \*----------------------------------------------------------*/ + direction = direction_map[1][modes[active_mode].direction]; + } + } + + + controller->UpdateDevice(modes[active_mode].value, std::vector(modes[active_mode].colors), direction, color_mode, modes[active_mode].speed, brightness); + + if(pid == AURA_ROG_CLAYMORE_PID) + { + controller->UpdateMode(modes[active_mode].value); + controller->SaveMode(); + controller->AllowRemoteControl(0); + } +} + +void RGBController_AuraTUFKeyboard::DeviceSaveMode() +{ + /*----------------------------------------------------------*\ + | not available for Claymore | + \*----------------------------------------------------------*/ + if(pid != AURA_ROG_CLAYMORE_PID) + { + DeviceUpdateMode(); + controller->SaveMode(); + } +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.h b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.h new file mode 100644 index 0000000..309d47b --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.h @@ -0,0 +1,58 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraTUFKeyboard.h | +| | +| RGBController for ASUS Aura TUF keyboard | +| | +| Mola19 03 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraTUFKeyboardController.h" + +enum +{ + AURA_KEYBOARD_BRIGHTNESS_MIN = 0, + AURA_KEYBOARD_BRIGHTNESS_MAX = 4, + AURA_KEYBOARD_BRIGHTNESS_DEFAULT = 4, +}; + +enum +{ + AURA_CLAYMORE_SPEED_MIN = 254, + AURA_CLAYMORE_SPEED_MAX = 0, + AURA_CLAYMORE_SPEED_DEFAULT_STATIC = 0, + AURA_CLAYMORE_SPEED_DEFAULT_BREATHING = 107, + AURA_CLAYMORE_SPEED_DEFAULT_COLOR_CYCLE = 121, + AURA_CLAYMORE_SPEED_DEFAULT_REACTIVE = 56, + AURA_CLAYMORE_SPEED_DEFAULT_WAVE = 50, + AURA_CLAYMORE_SPEED_DEFAULT_RIPPLE = 108, + AURA_CLAYMORE_SPEED_DEFAULT_STARRY_NIGHT = 54, + AURA_CLAYMORE_SPEED_DEFAULT_QUICKSAND = 103 +}; + +class RGBController_AuraTUFKeyboard : public RGBController +{ +public: + RGBController_AuraTUFKeyboard(AuraTUFKeyboardController* controller_ptr); + ~RGBController_AuraTUFKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + AuraTUFKeyboardController* controller; + uint16_t pid; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraAddressableController.cpp b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraAddressableController.cpp new file mode 100644 index 0000000..0e1f381 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraAddressableController.cpp @@ -0,0 +1,96 @@ +/*---------------------------------------------------------*\ +| AsusAuraAddressableController.cpp | +| | +| Driver for ASUS Aura addressable controller | +| | +| Adam Honse (CalcProgrammer1) 18 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusAuraAddressableController.h" + +AuraAddressableController::AuraAddressableController(hid_device* dev_handle, const char* path, std::string dev_name) : AuraUSBController(dev_handle, path, dev_name) +{ + /*-----------------------------------------------------*\ + | Add addressable devices | + \*-----------------------------------------------------*/ + for(int i = 0; i < config_table[0x02]; ++i) + { + device_info.push_back({0x01, (unsigned char)i, 0x01, 0, AuraDeviceType::ADDRESSABLE}); + } +} + +AuraAddressableController::~AuraAddressableController() +{ + +} + +void AuraAddressableController::SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors) +{ + SendDirect + ( + device_info[channel].direct_channel, + num_colors, + colors + ); +} + +void AuraAddressableController::SetMode + ( + unsigned char channel, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu + ) +{ + SendEffect + ( + channel, + mode, + red, + grn, + blu + ); +} + +void AuraAddressableController::SendEffect + ( + unsigned char channel, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = AURA_ADDRESSABLE_CONTROL_MODE_EFFECT; + usb_buf[0x02] = channel; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = mode; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + usb_buf[0x05] = red; + usb_buf[0x06] = grn; + usb_buf[0x07] = blu; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraAddressableController.h b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraAddressableController.h new file mode 100644 index 0000000..71f33a7 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraAddressableController.h @@ -0,0 +1,56 @@ +/*---------------------------------------------------------*\ +| AsusAuraAddressableController.h | +| | +| Driver for ASUS Aura addressable controller | +| | +| Adam Honse (CalcProgrammer1) 18 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "AsusAuraUSBController.h" + +enum +{ + AURA_ADDRESSABLE_CONTROL_MODE_EFFECT = 0x3B, /* Effect control mode */ +}; + +class AuraAddressableController : public AuraUSBController +{ +public: + AuraAddressableController(hid_device* dev_handle, const char* path, std::string dev_name); + ~AuraAddressableController(); + + void SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors + ); + + void SetMode + ( + unsigned char channel, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu + ); + +private: + + void SendEffect + ( + unsigned char channel, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu + ); +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraMainboardController.cpp b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraMainboardController.cpp new file mode 100644 index 0000000..5722cc2 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraMainboardController.cpp @@ -0,0 +1,235 @@ +/*---------------------------------------------------------*\ +| AsusAuraMainboardController.cpp | +| | +| Driver for ASUS Aura mainboard | +| | +| Martin Hartl (inlart) 25 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusAuraMainboardController.h" + +AuraMainboardController::AuraMainboardController(hid_device* dev_handle, const char* path, std::string dev_name) : AuraUSBController(dev_handle, path, dev_name), mode(AURA_MODE_DIRECT) +{ + unsigned char num_total_mainboard_leds = config_table[0x1B]; + unsigned char num_rgb_headers = config_table[0x1D]; + unsigned char num_addressable_headers = config_table[0x02]; + unsigned char effect_channel = 0; + + if(num_total_mainboard_leds < num_rgb_headers) + { + num_rgb_headers = 0; + } + + /*-----------------------------------------------------*\ + | Add mainboard device | + \*-----------------------------------------------------*/ + if(num_total_mainboard_leds > 0) + { + device_info.push_back({effect_channel, 0x04, num_total_mainboard_leds, num_rgb_headers, AuraDeviceType::FIXED}); + effect_channel++; + } + + /*-----------------------------------------------------*\ + | Add addressable devices | + \*-----------------------------------------------------*/ + for(int i = 0; i < num_addressable_headers; i++) + { + device_info.push_back({effect_channel, (unsigned char)i, 0x01, 0, AuraDeviceType::ADDRESSABLE}); + effect_channel++; + } + + SetGen1(); +} + +AuraMainboardController::~AuraMainboardController() +{ +} + +void AuraMainboardController::SetGen1() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up custom command packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = 0x52; + usb_buf[0x02] = 0x53; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} + +void AuraMainboardController::SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors) +{ + SendDirect + ( + device_info[channel].direct_channel, + num_colors, + colors + ); + +} + +void AuraMainboardController::SetMode + ( + unsigned char channel, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu + ) +{ + SetMode(channel, mode, red, grn, blu, false); +} + +void AuraMainboardController::SetMode + ( + unsigned char channel, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu, + bool shutdown_effect + ) +{ + this->mode = mode; + RGBColor color = ToRGBColor(red, grn, blu); + + SendEffect(device_info[channel].effect_channel, mode, shutdown_effect); + if(mode == AURA_MODE_DIRECT) + { + return; + } + + unsigned char led_data[60]; + unsigned char start_led = 0; + + for(std::size_t i = 0; i < channel; ++i) + { + start_led += device_info[i].num_leds; + } + + for(std::size_t led_idx = 0; led_idx < device_info[channel].num_leds; led_idx++) + { + led_data[(led_idx * 3) + 0] = RGBGetRValue(color); + led_data[(led_idx * 3) + 1] = RGBGetGValue(color); + led_data[(led_idx * 3) + 2] = RGBGetBValue(color); + } + + SendColor + ( + channel, + start_led, + device_info[channel].num_leds, + led_data, + shutdown_effect + ); +} + +unsigned short AuraMainboardController::GetMask(int start, int size) +{ + return(((1 << size) - 1) << start); +} + +void AuraMainboardController::SendEffect + ( + unsigned char channel, + unsigned char mode, + bool shutdown_effect + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = AURA_MAINBOARD_CONTROL_MODE_EFFECT; + usb_buf[0x02] = channel; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = shutdown_effect ? 0x01 : 0x00; + usb_buf[0x05] = mode; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} + +void AuraMainboardController::SendColor + ( + unsigned char /*channel*/, + unsigned char start_led, + unsigned char led_count, + unsigned char* led_data, + bool shutdown_effect + ) +{ + unsigned short mask = GetMask(start_led, led_count); + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = AURA_MAINBOARD_CONTROL_MODE_EFFECT_COLOR; + usb_buf[0x02] = mask >> 8; + usb_buf[0x03] = mask & 0xff; + usb_buf[0x04] = shutdown_effect ? 0x01 : 0x00; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x05 + 3 * start_led], led_data, led_count * 3); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} + +void AuraMainboardController::SendCommit() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = AURA_MAINBOARD_CONTROL_MODE_COMMIT; + usb_buf[0x02] = 0x55; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraMainboardController.h b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraMainboardController.h new file mode 100644 index 0000000..49f367f --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraMainboardController.h @@ -0,0 +1,82 @@ +/*---------------------------------------------------------*\ +| AsusAuraMainboardController.h | +| | +| Driver for ASUS Aura mainboard | +| | +| Martin Hartl (inlart) 25 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "AsusAuraUSBController.h" + +enum +{ + AURA_MAINBOARD_CONTROL_MODE_EFFECT = 0x35, /* Effect control mode */ + AURA_MAINBOARD_CONTROL_MODE_EFFECT_COLOR = 0x36, /* Effect color control mode */ + AURA_MAINBOARD_CONTROL_MODE_COMMIT = 0x3F, /* Commit mode */ +}; + +class AuraMainboardController : public AuraUSBController +{ +public: + AuraMainboardController(hid_device* dev_handle, const char* path, std::string dev_name); + ~AuraMainboardController(); + + void SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors + ); + + void SetMode + ( + unsigned char channel, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu + ); + + void SetMode + ( + unsigned char channel, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu, + bool shutdown_effect + ); + + void SendCommit(); + +private: + unsigned int mode; + + unsigned short GetMask(int start, int size); + + void SendEffect + ( + unsigned char channel, + unsigned char mode, + bool shutdown_effect + ); + + void SendColor + ( + unsigned char channel, + unsigned char start_led, + unsigned char led_count, + unsigned char* led_data, + bool shutdown_effect + ); + + void SetGen1(); +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraUSBController.cpp b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraUSBController.cpp new file mode 100644 index 0000000..27b2728 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraUSBController.cpp @@ -0,0 +1,203 @@ +/*---------------------------------------------------------*\ +| AsusAuraUSBController.cpp | +| | +| Driver for ASUS Aura USB device | +| | +| Martin Hartl (inlart) 25 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusAuraUSBController.h" +#include "LogManager.h" +#include "StringUtils.h" + +AuraUSBController::AuraUSBController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + GetFirmwareVersion(); + GetConfigTable(); +} + +AuraUSBController::~AuraUSBController() +{ + hid_close(dev); +} + +unsigned int AuraUSBController::GetChannelCount() +{ + return((unsigned int)device_info.size()); +} + +std::string AuraUSBController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AuraUSBController::GetDeviceName() +{ + return(name); +} + +std::string AuraUSBController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string AuraUSBController::GetDeviceVersion() +{ + return(std::string(version)); +} + +const std::vector& AuraUSBController::GetAuraDevices() const +{ + return(device_info); +} + +void AuraUSBController::GetConfigTable() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = AURA_REQUEST_CONFIG_TABLE; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Copy the firmware string if the reply ID is correct | + \*-----------------------------------------------------*/ + if(usb_buf[1] == 0x30) + { + memcpy(config_table, &usb_buf[4], 60); + + LOG_DEBUG("[%s] ASUS Aura USB config table:", version); + + for(int i = 0; i < 60; i+=6) + { + LOG_DEBUG("[%s] %02X %02X %02X %02X %02X %02X", version, + config_table[i + 0], + config_table[i + 1], + config_table[i + 2], + config_table[i + 3], + config_table[i + 4], + config_table[i + 5]); + } + } + else + { + LOG_INFO("[%s] Could not read config table, can not add device", version); + delete this; + } +} + +void AuraUSBController::GetFirmwareVersion() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up firmware version request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = AURA_REQUEST_FIRMWARE_VERSION; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Copy the firmware string if the reply ID is correct | + \*-----------------------------------------------------*/ + if(usb_buf[1] == 0x02) + { + memcpy(version, &usb_buf[2], 16); + } +} + +void AuraUSBController::SendDirect + ( + unsigned char device, + unsigned char led_count, + RGBColor* colors + ) +{ + unsigned char usb_buf[65]; + unsigned char offset = 0x00; + unsigned char sent_led_count = LEDS_PER_PACKET; + bool apply = false; + while(!apply) + { + if(offset + sent_led_count > led_count) + { + sent_led_count = led_count - offset; + } + + + if(offset + sent_led_count == led_count) + { + apply = true; + } + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = AURA_CONTROL_MODE_DIRECT; + usb_buf[0x02] = (apply ? 0x80 : 0x00) | device; + usb_buf[0x03] = offset; + usb_buf[0x04] = sent_led_count; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + for(unsigned char led_idx = 0; led_idx < sent_led_count; led_idx++) + { + + usb_buf[0x05 + (led_idx * 3)] = RGBGetRValue(colors[offset + led_idx]); + usb_buf[0x06 + (led_idx * 3)] = RGBGetGValue(colors[offset + led_idx]); + usb_buf[0x07 + (led_idx * 3)] = RGBGetBValue(colors[offset + led_idx]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + offset += sent_led_count; + } +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraUSBController.h b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraUSBController.h new file mode 100644 index 0000000..0f33ab9 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraUSBController.h @@ -0,0 +1,113 @@ +/*---------------------------------------------------------*\ +| AsusAuraUSBController.h | +| | +| Driver for ASUS Aura USB device | +| | +| Martin Hartl (inlart) 25 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "LogManager.h" + +enum +{ + AURA_MODE_OFF = 0, /* OFF mode */ + AURA_MODE_STATIC = 1, /* Static color mode */ + AURA_MODE_BREATHING = 2, /* Breathing effect mode */ + AURA_MODE_FLASHING = 3, /* Flashing effect mode */ + AURA_MODE_SPECTRUM_CYCLE = 4, /* Spectrum Cycle mode */ + AURA_MODE_RAINBOW = 5, /* Rainbow effect mode */ + AURA_MODE_SPECTRUM_CYCLE_BREATHING = 6, /* Rainbow Breathing effect mode */ + AURA_MODE_CHASE_FADE = 7, /* Chase with Fade effect mode */ + AURA_MODE_SPECTRUM_CYCLE_CHASE_FADE = 8, /* Chase with Fade, Rainbow effect mode */ + AURA_MODE_CHASE = 9, /* Chase effect mode */ + AURA_MODE_SPECTRUM_CYCLE_CHASE = 10, /* Chase with Rainbow effect mode */ + AURA_MODE_SPECTRUM_CYCLE_WAVE = 11, /* Wave effect mode */ + AURA_MODE_CHASE_RAINBOW_PULSE = 12, /* Chase with Rainbow Pulse effect mode*/ + AURA_MODE_RANDOM_FLICKER = 13, /* Random flicker effect mode */ + AURA_MODE_MUSIC = 14, /* Music effect mode */ + AURA_MODE_DIRECT = 0xFF, /* Direct control mode */ +}; + +enum +{ + AURA_REQUEST_FIRMWARE_VERSION = 0x82, /* Request firmware string */ + AURA_REQUEST_CONFIG_TABLE = 0xB0, /* Request configuration table */ + AURA_CONTROL_MODE_DIRECT = 0x40, /* Direct control mode */ +}; + +enum class AuraDeviceType +{ + FIXED, + ADDRESSABLE, +}; + +#define LEDS_PER_PACKET 0x14; + +struct AuraDeviceInfo +{ + unsigned char effect_channel; + unsigned char direct_channel; + unsigned char num_leds; + unsigned char num_headers; + AuraDeviceType device_type; +}; + +class AuraUSBController +{ +public: + AuraUSBController(hid_device* dev_handle, const char* path, std::string dev_name); + virtual ~AuraUSBController(); + + unsigned int GetChannelCount(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + std::string GetDeviceVersion(); + + const std::vector& GetAuraDevices() const; + + virtual void SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors + ) = 0; + + virtual void SetMode + ( + unsigned char channel, + unsigned char mode, + unsigned char red, + unsigned char grn, + unsigned char blu + ) = 0; + +protected: + hid_device* dev; + unsigned char config_table[60]; + std::vector device_info; + std::string location; + std::string name; + char version[16]; + + void SendDirect + ( + unsigned char device, + unsigned char led_count, + RGBColor * colors + ); + +private: + void GetConfigTable(); + void GetFirmwareVersion(); +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraMainboard.cpp b/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraMainboard.cpp new file mode 100644 index 0000000..e897d32 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraMainboard.cpp @@ -0,0 +1,77 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraMainboard.cpp | +| | +| RGBController for ASUS Aura mainboard | +| | +| rytypete 30 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusAuraMainboard.h" + +/**------------------------------------------------------------------*\ + @name Asus Aura USB Mainboard + @category Motherboard + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraUSBMotherboards + @comment The Asus Aura USB Mainboard controller applies to most + AMD and Intel mainboards from the x570 chipset onwards. +\*-------------------------------------------------------------------*/ + +RGBController_AuraMainboard::RGBController_AuraMainboard(AuraMainboardController* controller_ptr) : + RGBController_AuraUSB(controller_ptr) +{ + description = "ASUS Aura USB Mainboard Device"; + + /*-------------------------------------------------------*\ + | Add manual save flag to all modes except direct mode | + \*-------------------------------------------------------*/ + for(unsigned int mode_idx = 0; mode_idx < modes.size(); mode_idx++) + { + mode Mode = modes[mode_idx]; + if(Mode.value != AURA_MODE_DIRECT) + { + Mode.flags |= MODE_FLAG_MANUAL_SAVE; + modes[mode_idx] = Mode; + } + } +} + +void RGBController_AuraMainboard::DeviceUpdateShutdownEffect() +{ + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + AuraDeviceInfo device_info = controller->GetAuraDevices()[zone_idx]; + + /*---------------------------------------------------*\ + | Shutdown effect only works with onboard lighting | + \*---------------------------------------------------*/ + if(device_info.device_type == AuraDeviceType::FIXED && zones[zone_idx].leds_count > 0) + { + ((AuraMainboardController*) controller)->SetMode(zone_idx, modes[active_mode].value, red, grn, blu, true); + } + } +} + +void RGBController_AuraMainboard::DeviceSaveMode() +{ + DeviceUpdateMode(); + DeviceUpdateShutdownEffect(); + ((AuraMainboardController*) controller)->SendCommit(); +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraMainboard.h b/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraMainboard.h new file mode 100644 index 0000000..d7f9439 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraMainboard.h @@ -0,0 +1,26 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraMainboard.h | +| | +| RGBController for ASUS Aura mainboard | +| | +| rytypete 30 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController_AsusAuraUSB.h" +#include "AsusAuraMainboardController.h" + +class RGBController_AuraMainboard : public RGBController_AuraUSB +{ +public: + RGBController_AuraMainboard(AuraMainboardController* controller_ptr); + + void DeviceSaveMode(); + +private: + void DeviceUpdateShutdownEffect(); +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraUSB.cpp b/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraUSB.cpp new file mode 100644 index 0000000..d43d017 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraUSB.cpp @@ -0,0 +1,275 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraUSB.cpp | +| | +| RGBController for ASUS Aura USB device | +| | +| Adam Honse (CalcProgrammer1) 18 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusAuraUSB.h" + +/**------------------------------------------------------------------*\ + @name Asus Aura USB + @category Motherboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraUSBTerminal,DetectAsusAuraUSBAddressable + @comment The Asus Aura USB controller applies to most AMD and + Intel mainboards from the x470 and z390 chipset generations. +\*-------------------------------------------------------------------*/ + +RGBController_AuraUSB::RGBController_AuraUSB(AuraUSBController* controller_ptr) : + initializedMode(false) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "ASUS"; + type = DEVICE_TYPE_MOTHERBOARD; + description = "ASUS Aura USB Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetDeviceVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = AURA_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = AURA_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = AURA_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AURA_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = AURA_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Flashing.colors_min = 1; + Flashing.colors_max = 1; + Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flashing.colors.resize(1); + modes.push_back(Flashing); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = AURA_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = 0; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = AURA_MODE_RAINBOW; + Rainbow.flags = 0; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode ChaseFade; + ChaseFade.name = "Chase Fade"; + ChaseFade.value = AURA_MODE_CHASE_FADE; + ChaseFade.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + ChaseFade.colors_min = 1; + ChaseFade.colors_max = 1; + ChaseFade.color_mode = MODE_COLORS_MODE_SPECIFIC; + ChaseFade.colors.resize(1); + modes.push_back(ChaseFade); + + mode Chase; + Chase.name = "Chase"; + Chase.value = AURA_MODE_CHASE; + Chase.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Chase.colors_min = 1; + Chase.colors_max = 1; + Chase.color_mode = MODE_COLORS_MODE_SPECIFIC; + Chase.colors.resize(1); + modes.push_back(Chase); + + SetupZones(); +} + +RGBController_AuraUSB::~RGBController_AuraUSB() +{ + delete controller; +} + +void RGBController_AuraUSB::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(controller->GetChannelCount()); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + int addressableCounter = 1; + for (unsigned int channel_idx = 0; channel_idx < zones.size(); channel_idx++) + { + AuraDeviceInfo device_info = controller->GetAuraDevices()[channel_idx]; + + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + if(device_info.device_type == AuraDeviceType::FIXED) + { + zones[channel_idx].name = "Aura Mainboard"; + zones[channel_idx].leds_min = device_info.num_leds; + zones[channel_idx].leds_max = device_info.num_leds; + zones[channel_idx].leds_count = device_info.num_leds; + } + else + { + zones[channel_idx].name = "Aura Addressable "; + zones[channel_idx].name.append(std::to_string(addressableCounter)); + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = AURA_ADDRESSABLE_MAX_LEDS; + + addressableCounter++; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + } + + unsigned int num_mainboard_leds = device_info.num_leds - device_info.num_headers; + for (unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + unsigned led_idx = led_ch_idx + 1; + led new_led; + + new_led.name = zones[channel_idx].name; + if(device_info.device_type == AuraDeviceType::FIXED && led_ch_idx >= num_mainboard_leds) + { + new_led.name.append(", RGB Header "); + led_idx -= num_mainboard_leds; + } + else + { + new_led.name.append(", LED "); + } + new_led.name.append(std::to_string(led_idx)); + + new_led.value = channel_idx; + + leds.push_back(new_led); + } + + zones[channel_idx].matrix_map = NULL; + } + + SetupColors(); +} + +void RGBController_AuraUSB::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_AuraUSB::DeviceUpdateLEDs() +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} + +void RGBController_AuraUSB::UpdateZoneLEDs(int zone) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_AuraUSB::UpdateSingleLED(int led) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + + unsigned int channel = leds[led].value; + + controller->SetChannelLEDs(channel, zones[channel].colors, zones[channel].leds_count); +} + +void RGBController_AuraUSB::DeviceUpdateMode() +{ + initializedMode = true; + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].leds_count > 0) + { + controller->SetMode(zone_idx, modes[active_mode].value, red, grn, blu); + } + } +} diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraUSB.h b/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraUSB.h new file mode 100644 index 0000000..f09df21 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraUSB.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusAuraUSB.h | +| | +| RGBController for ASUS Aura USB device | +| | +| Adam Honse (CalcProgrammer1) 18 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusAuraUSBController.h" + +#define AURA_ADDRESSABLE_MAX_LEDS 120 + +class RGBController_AuraUSB : public RGBController +{ +public: + RGBController_AuraUSB(AuraUSBController* controller_ptr); + ~RGBController_AuraUSB(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +protected: + AuraUSBController* controller; + +private: + std::vector leds_channel; + std::vector zones_channel; + bool initializedMode; +}; diff --git a/Controllers/AsusAuraUSBController/AsusAuraUSBControllerDetect.cpp b/Controllers/AsusAuraUSBController/AsusAuraUSBControllerDetect.cpp new file mode 100644 index 0000000..2bcaedf --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusAuraUSBControllerDetect.cpp @@ -0,0 +1,463 @@ +/*---------------------------------------------------------*\ +| AsusAuraUSBControllerDetect.cpp | +| | +| Detector for ASUS Aura USB devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "AsusAuraAddressableController.h" +#include "AsusAuraHeadsetStandController.h" +#include "AsusAuraKeyboardController.h" +#include "AsusAuraTUFKeyboardController.h" +#include "AsusAuraMainboardController.h" +#include "AsusAuraMouseController.h" +#include "AsusROGAllyController.h" +#include "AsusROGStrixLCController.h" +#include "AsusAuraMouseGen1Controller.h" +#include "AsusAuraMousematController.h" +#include "AsusAuraMonitorController.h" +#include "AsusAuraRyuoAIOController.h" +#include "RGBController_AsusAuraUSB.h" +#include "RGBController_AsusAuraHeadsetStand.h" +#include "RGBController_AsusAuraKeyboard.h" +#include "RGBController_AsusAuraTUFKeyboard.h" +#include "RGBController_AsusAuraMainboard.h" +#include "RGBController_AsusAuraMouse.h" +#include "RGBController_AsusAuraMousemat.h" +#include "RGBController_AsusROGAlly.h" +#include "RGBController_AsusROGStrixLC.h" +#include "RGBController_AsusROGSpatha.h" +#include "RGBController_AsusROGStrixEvolve.h" +#include "RGBController_AsusAuraMonitor.h" +#include "RGBController_AsusAuraRyuoAIO.h" +#include "dmiinfo.h" + +#define AURA_USB_VID 0x0B05 + +/*-----------------------------------------------------------------*\ +| MOTHERBOARDS | +\*-----------------------------------------------------------------*/ +#define AURA_ADDRESSABLE_1_PID 0x1867 +#define AURA_ADDRESSABLE_2_PID 0x1872 +#define AURA_ADDRESSABLE_3_PID 0x18A3 +#define AURA_ADDRESSABLE_4_PID 0x18A5 +#define AURA_MOTHERBOARD_1_PID 0x18F3 +#define AURA_MOTHERBOARD_2_PID 0x1939 +#define AURA_MOTHERBOARD_3_PID 0x19AF +#define AURA_MOTHERBOARD_4_PID 0x1AA6 +#define AURA_MOTHERBOARD_5_PID 0x1BED + +/*-----------------------------------------------------------------*\ +| KEYBOARDS | +\*-----------------------------------------------------------------*/ +#define AURA_ROG_AZOTH_USB_PID 0x1A83 +#define AURA_ROG_AZOTH_2_4_PID 0x1A85 +#define AURA_ROG_CLAYMORE_PID 0x184D +#define AURA_ROG_FALCHION_WIRED_PID 0x193C +#define AURA_ROG_FALCHION_WIRELESS_PID 0x193E +#define AURA_ROG_STRIX_FLARE_PID 0x1875 +#define AURA_ROG_STRIX_FLARE_PNK_LTD_PID 0x18CF +#define AURA_ROG_STRIX_FLARE_COD_BO4_PID 0x18AF +#define AURA_ROG_STRIX_FLARE_II_ANIMATE_PID 0x19FC +#define AURA_ROG_STRIX_FLARE_II_PID 0x19FE +#define AURA_ROG_STRIX_SCOPE_PID 0x18F8 +#define AURA_ROG_STRIX_SCOPE_TKL_PID 0x190C +#define AURA_ROG_STRIX_SCOPE_TKL_PNK_LTD_PID 0x1954 +#define AURA_ROG_STRIX_SCOPE_RX_PID 0x1951 +#define AURA_ROG_STRIX_SCOPE_RX_EVA_02_PID 0x1B12 +#define AURA_ROG_STRIX_SCOPE_RX_TKL_DELUXE_PID 0x1A05 +#define AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_USB_PID 0x19F6 +#define AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_2_4_PID 0x19F8 +#define AURA_ROG_STRIX_SCOPE_II_PID 0x1AB3 +#define AURA_ROG_STRIX_SCOPE_II_RX_PID 0x1AB5 +#define AURA_ROG_STRIX_SCOPE_II_96_WIRELESS_USB_PID 0x1AAE +#define AURA_ROG_STRIX_SCOPE_II_96_RX_WIRELESS_USB_PID 0x1B78 +#define AURA_TUF_K1_GAMING_PID 0x1945 +#define AURA_TUF_K3_GAMING_PID 0x194B +#define AURA_TUF_K3_GAMING_GEN_II_PID 0x1B30 +#define AURA_TUF_K5_GAMING_PID 0x1899 +#define AURA_TUF_K7_GAMING_PID 0x18AA +#define AURA_TUF_K3_GENII_MIKU_EDITION_PID 0x1C5E + +/*-----------------------------------------------------------------*\ +| MICE - defined in AsusAuraMouseDevices.h | +\*-----------------------------------------------------------------*/ +#define AURA_ROG_STRIX_EVOLVE_PID 0x185B +#define AURA_ROG_SPATHA_WIRED_PID 0x181C +#define AURA_ROG_SPATHA_WIRELESS_PID 0x1824 + +/*-----------------------------------------------------------------*\ +| MOUSEMATS | +\*-----------------------------------------------------------------*/ +#define AURA_ROG_BALTEUS_PID 0x1891 +#define AURA_ROG_BALTEUS_QI_PID 0x1890 + +/*-----------------------------------------------------------------*\ +| MONITORS | +\*-----------------------------------------------------------------*/ +#define AURA_ROG_STRIX_XG27AQ_PID 0x198C +#define AURA_ROG_STRIX_XG27AQM_PID 0x19BB +#define AURA_ROG_STRIX_XG279Q_PID 0x1919 +#define AURA_ROG_STRIX_XG27W_PID 0x1933 +#define AURA_ROG_STRIX_XG32VC_PID 0x1968 +#define AURA_ROG_PG32UQ_PID 0x19B9 + +/*-----------------------------------------------------------------*\ +| HEADSET STANDS | +\*-----------------------------------------------------------------*/ +#define AURA_ROG_THRONE_PID 0x18D9 +#define AURA_ROG_THRONE_QI_PID 0x18C5 +#define AURA_ROG_THRONE_QI_GUNDAM_PID 0x1994 + +/*-----------------------------------------------------------------*\ +| OTHER | +\*-----------------------------------------------------------------*/ +#define AURA_TERMINAL_PID 0x1889 +#define ROG_STRIX_LC120_PID 0x879E +#define AURA_RYUO_AIO_PID 0x1887 +#define AURA_RYUJIN_AIO_PID 0x18AE +#define ASUS_ROG_ALLY_PID 0x1ABE +#define ASUS_ROG_ALLY_X_PID 0x1B4C + +AuraKeyboardMappingLayoutType GetKeyboardMappingLayoutType(int pid) +{ + switch(pid) + { + case AURA_ROG_STRIX_SCOPE_PID: + return SCOPE_LAYOUT; + + case AURA_ROG_STRIX_SCOPE_RX_PID: + return SCOPE_RX_LAYOUT; + + case AURA_ROG_STRIX_SCOPE_TKL_PID: + case AURA_ROG_STRIX_SCOPE_RX_TKL_DELUXE_PID: + case AURA_ROG_STRIX_SCOPE_TKL_PNK_LTD_PID: + return SCOPE_TKL_LAYOUT; + + default: + return FLARE_LAYOUT; + } +} + +void DetectAsusAuraUSBTerminal(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AuraAddressableController* controller = new AuraAddressableController(dev, info->path, name); + RGBController_AuraUSB* rgb_controller = new RGBController_AuraUSB(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraUSBAddressable(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + DMIInfo dmi; + AuraAddressableController* controller = new AuraAddressableController(dev, info->path, "ASUS " + dmi.getMainboard() + " Addressable"); + RGBController_AuraUSB* rgb_controller = new RGBController_AuraUSB(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraUSBMotherboards(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + try + { + DMIInfo dmi; + AuraMainboardController* controller = new AuraMainboardController(dev, info->path, "ASUS " + dmi.getMainboard()); + RGBController_AuraMainboard* rgb_controller = new RGBController_AuraMainboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + catch(const std::runtime_error& ex) + { + // reading the config table failed + LOG_ERROR("[AsusAuraUSB] An error occured while reading the config table: %s", ex.what()); + } + } +} + +void DetectAsusAuraUSBKeyboards(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AuraKeyboardController* controller = new AuraKeyboardController(dev, info->path, name); + AuraKeyboardMappingLayoutType layout = GetKeyboardMappingLayoutType(info->product_id); + RGBController_AuraKeyboard* rgb_controller = new RGBController_AuraKeyboard(controller, layout); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraUSBMice(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + uint16_t pid = (name == "Asus ROG Spatha X Dock") ? AURA_ROG_SPATHA_X_DOCK_FAKE_PID : info->product_id; + AuraMouseController* controller = new AuraMouseController(dev, info->path, pid, name); + RGBController_AuraMouse* rgb_controller = new RGBController_AuraMouse(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraUSBMousemats(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AuraMousematController* controller = new AuraMousematController(dev, info->path, name); + RGBController_AuraMousemat* rgb_controller = new RGBController_AuraMousemat(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraUSBROGStrixLC(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AsusROGStrixLCController* controller = new AsusROGStrixLCController(dev, info->path, name); + RGBController_AsusROGStrixLC* rgb_controller = new RGBController_AsusROGStrixLC(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraUSBRyuoAIO(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AsusAuraRyuoAIOController* controller = new AsusAuraRyuoAIOController(dev, info->path, name); + RGBController_AsusAuraRyuoAIO* rgb_controller = new RGBController_AsusAuraRyuoAIO(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraUSBStrixEvolve(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AsusAuraMouseGen1Controller* controller = new AsusAuraMouseGen1Controller(dev, info->path, info->product_id, name); + RGBController_AsusROGStrixEvolve* rgb_controller = new RGBController_AsusROGStrixEvolve(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraUSBSpatha(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AsusAuraMouseGen1Controller* controller = new AsusAuraMouseGen1Controller(dev, info->path, info->product_id, name); + RGBController_AsusROGSpatha* rgb_controller = new RGBController_AsusROGSpatha(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraUSBHeadsetStand(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AuraHeadsetStandController* controller = new AuraHeadsetStandController(dev, info->path, name); + RGBController_AuraHeadsetStand* rgb_controller = new RGBController_AuraHeadsetStand(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraTUFUSBKeyboard(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AuraTUFKeyboardController* controller = new AuraTUFKeyboardController(dev, info->path, info->product_id, info->release_number, name); + RGBController_AuraTUFKeyboard* rgb_controller = new RGBController_AuraTUFKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusAuraUSBMonitor(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AuraMonitorController* controller = new AuraMonitorController(dev, info->path, info->product_id, name); + RGBController_AuraMonitor* rgb_controller = new RGBController_AuraMonitor(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusROGAlly(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + ROGAllyController* controller = new ROGAllyController(dev, info->path, name); + RGBController_AsusROGAlly* rgb_controller = new RGBController_AsusROGAlly(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +/*-----------------------------------------------------------------*\ +| MOTHERBOARDS | +\*-----------------------------------------------------------------*/ +REGISTER_HID_DETECTOR ("ASUS Aura Addressable", DetectAsusAuraUSBAddressable, AURA_USB_VID, AURA_ADDRESSABLE_1_PID); +REGISTER_HID_DETECTOR ("ASUS Aura Addressable", DetectAsusAuraUSBAddressable, AURA_USB_VID, AURA_ADDRESSABLE_2_PID); +REGISTER_HID_DETECTOR ("ASUS Aura Addressable", DetectAsusAuraUSBAddressable, AURA_USB_VID, AURA_ADDRESSABLE_3_PID); +REGISTER_HID_DETECTOR ("ASUS Aura Addressable", DetectAsusAuraUSBAddressable, AURA_USB_VID, AURA_ADDRESSABLE_4_PID); +REGISTER_HID_DETECTOR ("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_1_PID); +REGISTER_HID_DETECTOR ("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_2_PID); +REGISTER_HID_DETECTOR ("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_3_PID); +REGISTER_HID_DETECTOR ("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_4_PID); +REGISTER_HID_DETECTOR ("ASUS Aura Motherboard", DetectAsusAuraUSBMotherboards, AURA_USB_VID, AURA_MOTHERBOARD_5_PID); + +/*-----------------------------------------------------------------*\ +| KEYBOARDS | +\*-----------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope TKL", DetectAsusAuraUSBKeyboards, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_TKL_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope RX TKL Wireless Deluxe", DetectAsusAuraUSBKeyboards, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_RX_TKL_DELUXE_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope TKL PNK LTD", DetectAsusAuraUSBKeyboards, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_TKL_PNK_LTD_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Azoth USB", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_AZOTH_USB_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Azoth 2.4GHz", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_AZOTH_2_4_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Claymore", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_CLAYMORE_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Falchion (Wired)", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_FALCHION_WIRED_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Falchion (Wireless)", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_FALCHION_WIRELESS_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Flare", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_FLARE_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Flare PNK LTD", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_FLARE_PNK_LTD_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Flare CoD Black Ops 4 Edition", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_FLARE_COD_BO4_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Flare II Animate", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_FLARE_II_ANIMATE_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Flare II", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_FLARE_II_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope RX", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_RX_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope RX EVA-02 Edition", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_RX_EVA_02_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope NX Wireless Deluxe USB", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_USB_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope NX Wireless Deluxe 2.4GHz", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_NX_WIRELESS_DELUXE_2_4_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope II", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_II_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope II RX", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_II_RX_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope II 96 Wireless USB", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_II_96_WIRELESS_USB_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Scope II 96 RX Wireless USB", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_ROG_STRIX_SCOPE_II_96_RX_WIRELESS_USB_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS TUF Gaming K1", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_TUF_K1_GAMING_PID, 2, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS TUF Gaming K3", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_TUF_K3_GAMING_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS TUF Gaming K3 GEN II", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_TUF_K3_GAMING_GEN_II_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS TUF Gaming K5", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_TUF_K5_GAMING_PID, 2, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS TUF Gaming K7", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_TUF_K7_GAMING_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("ASUS TUF Gaming K3 GEN II MIKU EDITION", DetectAsusAuraTUFUSBKeyboard, AURA_USB_VID, AURA_TUF_K3_GENII_MIKU_EDITION_PID, 1, 0xFF00); + +/*-----------------------------------------------------------------*\ +| MICE | +\*-----------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Core", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_CORE_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_PID, 2, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Origin", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_ORIGIN_PID, 2, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Origin PNK LTD", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_ORIGIN_PNK_LTD_PID, 2, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Origin COD", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_ORIGIN_COD_PID, 2, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Wireless", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_WIRELESS_1_PID, 1, 0xFF13); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius II Wireless", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_II_WIRELESS_2_PID, 2, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Core", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_CORE_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless USB", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_USB_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless 2.4Ghz", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_2_4_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless Bluetooth", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_BT_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless AimPoint USB", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_AIMPOINT_USB_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Gladius III Wireless AimPoint 2.4Ghz", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_GLADIUS_III_WIRELESS_AIMPOINT_2_4_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Chakram (Wireless)", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_CHAKRAM_WIRELESS_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("Asus ROG Chakram (Wired)", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_CHAKRAM_WIRED_1_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("Asus ROG Chakram Core", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_CHAKRAM_CORE_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("Asus ROG Chakram X USB", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_CHAKRAM_X_USB_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("Asus ROG Chakram X 2.4GHz", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_CHAKRAM_X_2_4_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("Asus ROG Spatha X USB", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_SPATHA_X_USB_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("Asus ROG Spatha X 2.4GHz", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_SPATHA_X_2_4_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("Asus ROG Spatha X Dock", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_SPATHA_X_DOCK_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Pugio", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_PUGIO_PID, 2, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Pugio II (Wired)", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_PUGIO_II_WIRED_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Pugio II (Wireless)", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_PUGIO_II_WIRELESS_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Impact", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_STRIX_IMPACT_PID, 2, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Impact II", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_STRIX_IMPACT_II_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Impact II Gundam", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_STRIX_IMPACT_II_GUNDAM_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Impact II Electro Punk", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_STRIX_IMPACT_II_PUNK_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Impact II Moonlight White", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_STRIX_IMPACT_II_WHITE_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Impact II Wireless USB", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_STRIX_IMPACT_II_WIRELESS_USB_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Impact II Wireless 2.4 Ghz", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_STRIX_IMPACT_II_WIRELESS_2_4_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Impact III", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_STRIX_IMPACT_III_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Keris", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_KERIS, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Keris Wireless USB", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_KERIS_WIRELESS_USB_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Keris Wireless 2.4Ghz", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_KERIS_WIRELESS_2_4_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Keris Wireless Bluetooth", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_KERIS_WIRELESS_BT_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Keris Wireless AimPoint USB", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_KERIS_WIRELESS_AIMPOINT_USB_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS ROG Keris Wireless AimPoint 2.4Ghz", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_ROG_KERIS_WIRELESS_AIMPOINT_2_4_PID, 0, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS TUF Gaming M3", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_TUF_M3_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS TUF Gaming M3 Gen II", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_TUF_M3_GEN_II_PID, 2, 0xFF01); +REGISTER_HID_DETECTOR_IP("ASUS TUF Gaming M5", DetectAsusAuraUSBMice, AURA_USB_VID, AURA_TUF_M5_PID, 2, 0xFF01); + +REGISTER_HID_DETECTOR_IP("ASUS ROG Strix Evolve", DetectAsusAuraUSBStrixEvolve, AURA_USB_VID, AURA_ROG_STRIX_EVOLVE_PID, 1, 0x0008); +REGISTER_HID_DETECTOR_IP("ASUS ROG Spatha Wired", DetectAsusAuraUSBSpatha, AURA_USB_VID, AURA_ROG_SPATHA_WIRED_PID, 1, 0x0008); +REGISTER_HID_DETECTOR_IP("ASUS ROG Spatha Wireless", DetectAsusAuraUSBSpatha, AURA_USB_VID, AURA_ROG_SPATHA_WIRELESS_PID, 1, 0x0008); + +/*-----------------------------------------------------------------*\ +| MOUSEMATS | +\*-----------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_PU("ASUS ROG Balteus", DetectAsusAuraUSBMousemats, AURA_USB_VID, AURA_ROG_BALTEUS_PID, 0xFF06, 1); +REGISTER_HID_DETECTOR_PU("ASUS ROG Balteus Qi", DetectAsusAuraUSBMousemats, AURA_USB_VID, AURA_ROG_BALTEUS_QI_PID, 0xFF06, 1); + +/*-----------------------------------------------------------------*\ +| MONITORS | +\*-----------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_PU("ASUS ROG Strix XG27AQ", DetectAsusAuraUSBMonitor, AURA_USB_VID, AURA_ROG_STRIX_XG27AQ_PID, 0xFFA0, 1); +REGISTER_HID_DETECTOR_PU("ASUS ROG Strix XG27AQM", DetectAsusAuraUSBMonitor, AURA_USB_VID, AURA_ROG_STRIX_XG27AQM_PID, 0xFFA0, 1); +REGISTER_HID_DETECTOR_PU("ASUS ROG Strix XG279Q", DetectAsusAuraUSBMonitor, AURA_USB_VID, AURA_ROG_STRIX_XG279Q_PID, 0xFFA0, 1); +REGISTER_HID_DETECTOR_PU("ASUS ROG Strix XG27W", DetectAsusAuraUSBMonitor, AURA_USB_VID, AURA_ROG_STRIX_XG27W_PID, 0xFFA0, 1); +REGISTER_HID_DETECTOR_PU("ASUS ROG Strix XG32VC", DetectAsusAuraUSBMonitor, AURA_USB_VID, AURA_ROG_STRIX_XG32VC_PID, 0xFFA0, 1); +REGISTER_HID_DETECTOR_PU("ASUS ROG PG32UQ", DetectAsusAuraUSBMonitor, AURA_USB_VID, AURA_ROG_PG32UQ_PID, 0xFFA0, 1); + +/*-----------------------------------------------------------------*\ +| OTHER | +\*-----------------------------------------------------------------*/ +REGISTER_HID_DETECTOR ("ASUS ROG AURA Terminal", DetectAsusAuraUSBTerminal, AURA_USB_VID, AURA_TERMINAL_PID); +REGISTER_HID_DETECTOR_PU ("ASUS ROG Strix LC", DetectAsusAuraUSBROGStrixLC, AURA_USB_VID, ROG_STRIX_LC120_PID, 0x00FF, 1); +REGISTER_HID_DETECTOR_PU ("ASUS ROG Ryuo AIO", DetectAsusAuraUSBRyuoAIO, AURA_USB_VID, AURA_RYUO_AIO_PID, 0xFF72, 0x00A1); +REGISTER_HID_DETECTOR_PU ("ASUS ROG Ryujin AIO", DetectAsusAuraUSBRyuoAIO, AURA_USB_VID, AURA_RYUJIN_AIO_PID, 0xFF72, 0x00A1); +REGISTER_HID_DETECTOR_I ("ASUS ROG Throne", DetectAsusAuraUSBHeadsetStand, AURA_USB_VID, AURA_ROG_THRONE_PID, 0); +REGISTER_HID_DETECTOR_I ("ASUS ROG Throne QI", DetectAsusAuraUSBHeadsetStand, AURA_USB_VID, AURA_ROG_THRONE_QI_PID, 0); +REGISTER_HID_DETECTOR_I ("ASUS ROG Throne QI GUNDAM", DetectAsusAuraUSBHeadsetStand, AURA_USB_VID, AURA_ROG_THRONE_QI_GUNDAM_PID, 0); +REGISTER_HID_DETECTOR_IPU("ASUS ROG Ally", DetectAsusROGAlly, AURA_USB_VID, ASUS_ROG_ALLY_PID, 2, 0xFF31, 0x0076); +REGISTER_HID_DETECTOR_IPU("ASUS ROG Ally X", DetectAsusROGAlly, AURA_USB_VID, ASUS_ROG_ALLY_X_PID, 2, 0xFF31, 0x0076); diff --git a/Controllers/AsusAuraUSBController/AsusROGAllyController/AsusROGAllyController.cpp b/Controllers/AsusAuraUSBController/AsusROGAllyController/AsusROGAllyController.cpp new file mode 100644 index 0000000..073e364 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusROGAllyController/AsusROGAllyController.cpp @@ -0,0 +1,183 @@ +/*---------------------------------------------------------*\ +| AsusROGAllyController.cpp | +| | +| Driver for ASUS ROG Ally | +| | +| Adam Honse (CalcProgrammer1) 12 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusROGAllyController.h" +#include "StringUtils.h" + +ROGAllyController::ROGAllyController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + SendInitialization(); +} + +ROGAllyController::~ROGAllyController() +{ + hid_close(dev); +} + +std::string ROGAllyController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string ROGAllyController::GetName() +{ + return(name); +} + +std::string ROGAllyController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string ROGAllyController::GetVersion() +{ + return(""); +} + +void ROGAllyController::SendInitialization() +{ + unsigned char usb_buf[64]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x5D; + usb_buf[0x01] = 0xB9; + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x5D; + usb_buf[0x01] = 0x41; + usb_buf[0x02] = 0x53; + usb_buf[0x03] = 0x55; + usb_buf[0x04] = 0x53; + usb_buf[0x05] = 0x20; + usb_buf[0x06] = 0x54; + usb_buf[0x07] = 0x65; + usb_buf[0x08] = 0x63; + usb_buf[0x09] = 0x68; + usb_buf[0x0A] = 0x2E; + usb_buf[0x0B] = 0x49; + usb_buf[0x0C] = 0x6E; + usb_buf[0x0D] = 0x63; + usb_buf[0x0E] = 0x2E; + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void ROGAllyController::UpdateBrightness + ( + unsigned char brightness + ) +{ + unsigned char usb_buf[64]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x5A; + usb_buf[0x01] = 0xBA; + usb_buf[0x02] = 0xC5; + usb_buf[0x03] = 0xC4; + usb_buf[0x04] = brightness; + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void ROGAllyController::UpdateLeds + ( + std::vector colors + ) +{ + unsigned char usb_buf[64]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x5A; + usb_buf[0x01] = 0xD1; + usb_buf[0x02] = 0x08; + usb_buf[0x03] = 0x0C; + + for(unsigned int color_idx = 0; color_idx < colors.size(); color_idx++) + { + usb_buf[color_idx * 3 + 4] = RGBGetRValue(colors[color_idx]); + usb_buf[color_idx * 3 + 5] = RGBGetGValue(colors[color_idx]); + usb_buf[color_idx * 3 + 6] = RGBGetBValue(colors[color_idx]); + } + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void ROGAllyController::UpdateDevice + ( + unsigned char mode, + std::vector colors, + unsigned char speed, + unsigned char direction + ) +{ + unsigned char usb_buf[64]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x5A; + usb_buf[0x01] = 0xB3; + usb_buf[0x02] = 0x00; + usb_buf[0x03] = mode; + if(colors.size() > 0) + { + usb_buf[0x04] = RGBGetRValue(colors[0]); + usb_buf[0x05] = RGBGetGValue(colors[0]); + usb_buf[0x06] = RGBGetBValue(colors[0]); + } + usb_buf[0x07] = speed; + usb_buf[0x08] = direction; + if(colors.size() > 1) + { + usb_buf[0x0A] = RGBGetRValue(colors[1]); + usb_buf[0x0B] = RGBGetGValue(colors[1]); + usb_buf[0x0C] = RGBGetBValue(colors[1]); + } + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x5A; + usb_buf[0x01] = 0xB5; + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void ROGAllyController::SaveMode() +{ + unsigned char usb_buf[64]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x5A; + usb_buf[0x01] = 0xB4; + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} diff --git a/Controllers/AsusAuraUSBController/AsusROGAllyController/AsusROGAllyController.h b/Controllers/AsusAuraUSBController/AsusROGAllyController/AsusROGAllyController.h new file mode 100644 index 0000000..9688a25 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusROGAllyController/AsusROGAllyController.h @@ -0,0 +1,77 @@ +/*---------------------------------------------------------*\ +| AsusROGAllyController.h | +| | +| Driver for ASUS ROG Ally | +| | +| Adam Honse (CalcProgrammer1) 12 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +enum +{ + ROG_ALLY_MODE_STATIC = 0, + ROG_ALLY_MODE_BREATHING = 1, + ROG_ALLY_MODE_COLOR_CYCLE = 2, + ROG_ALLY_MODE_RAINBOW = 3, + ROG_ALLY_MODE_STROBING = 10, + ROG_ALLY_MODE_DIRECT = 0xFF, +}; + +enum +{ + ROG_ALLY_SPEED_MIN = 0xE1, + ROG_ALLY_SPEED_MED = 0xEB, + ROG_ALLY_SPEED_MAX = 0xF5 +}; + +enum +{ + ROG_ALLY_DIRECTION_RIGHT = 0x00, + ROG_ALLY_DIRECTION_LEFT = 0x01 +}; + +class ROGAllyController +{ +public: + ROGAllyController(hid_device* dev_handle, const char* path, std::string dev_name); + virtual ~ROGAllyController(); + + std::string GetDeviceLocation(); + std::string GetName(); + std::string GetSerialString(); + std::string GetVersion(); + + void SendInitialization(); + + void UpdateBrightness + ( + unsigned char brightness + ); + + void UpdateLeds + ( + std::vector colors + ); + + void UpdateDevice + ( + unsigned char mode, + std::vector colors, + unsigned char speed, + unsigned char direction + ); + + void SaveMode(); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/AsusAuraUSBController/AsusROGAllyController/RGBController_AsusROGAlly.cpp b/Controllers/AsusAuraUSBController/AsusROGAllyController/RGBController_AsusROGAlly.cpp new file mode 100644 index 0000000..fe9496a --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusROGAllyController/RGBController_AsusROGAlly.cpp @@ -0,0 +1,218 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusROGAlly.cpp | +| | +| RGBController for ASUS ROG Ally | +| | +| Adam Honse (CalcProgrammer1) 12 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusROGAlly.h" + +/**------------------------------------------------------------------*\ + @name Asus ROG Ally + @category Gamepad + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusROGAlly + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AsusROGAlly::RGBController_AsusROGAlly(ROGAllyController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "ASUS"; + type = DEVICE_TYPE_GAMEPAD; + description = "ASUS ROG Ally Device"; + version = controller->GetVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ROG_ALLY_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 3; + Direct.brightness = 3; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = ROG_ALLY_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + Static.brightness_min = 0; + Static.brightness_max = 3; + Static.brightness = 3; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ROG_ALLY_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed_min = ROG_ALLY_SPEED_MIN; + Breathing.speed_max = ROG_ALLY_SPEED_MAX; + Breathing.speed = ROG_ALLY_SPEED_MED; + Breathing.colors_min = 2; + Breathing.colors_max = 2; + Breathing.colors.resize(2); + Breathing.brightness_min = 0; + Breathing.brightness_max = 3; + Breathing.brightness = 3; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Spectrum Cycle"; + ColorCycle.value = ROG_ALLY_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + ColorCycle.color_mode = MODE_COLORS_RANDOM; + ColorCycle.speed_min = ROG_ALLY_SPEED_MIN; + ColorCycle.speed_max = ROG_ALLY_SPEED_MAX; + ColorCycle.speed = ROG_ALLY_SPEED_MED; + ColorCycle.brightness_min = 0; + ColorCycle.brightness_max = 3; + ColorCycle.brightness = 3; + modes.push_back(ColorCycle); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = ROG_ALLY_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS; + Rainbow.color_mode = MODE_COLORS_RANDOM; + Rainbow.speed_min = ROG_ALLY_SPEED_MIN; + Rainbow.speed_max = ROG_ALLY_SPEED_MAX; + Rainbow.speed = ROG_ALLY_SPEED_MED; + Rainbow.brightness_min = 0; + Rainbow.brightness_max = 3; + Rainbow.brightness = 3; + modes.push_back(Rainbow); + + mode Strobing; + Strobing.name = "Strobing"; + Strobing.value = ROG_ALLY_MODE_STROBING; + Strobing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Strobing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Strobing.colors_min = 1; + Strobing.colors_max = 1; + Strobing.brightness_min = 0; + Strobing.brightness_max = 3; + Strobing.brightness = 3; + Strobing.colors.resize(1); + modes.push_back(Strobing); + + SetupZones(); +} + +RGBController_AsusROGAlly::~RGBController_AsusROGAlly() +{ + delete controller; +} + +void RGBController_AsusROGAlly::SetupZones() +{ + zone left_stick_zone; + + left_stick_zone.name = "Left Stick"; + left_stick_zone.type = ZONE_TYPE_SINGLE; + left_stick_zone.leds_min = 2; + left_stick_zone.leds_max = 2; + left_stick_zone.leds_count = 2; + left_stick_zone.matrix_map = NULL; + + zones.push_back(left_stick_zone); + + for(unsigned int i = 0; i < 2; i++) + { + led left_stick_led; + + left_stick_led.name = "Left Stick LED " + std::to_string(i); + + leds.push_back(left_stick_led); + } + + zone right_stick_zone; + + right_stick_zone.name = "Right Stick"; + right_stick_zone.type = ZONE_TYPE_SINGLE; + right_stick_zone.leds_min = 2; + right_stick_zone.leds_max = 2; + right_stick_zone.leds_count = 2; + right_stick_zone.matrix_map = NULL; + + zones.push_back(right_stick_zone); + + for(unsigned int i = 0; i < 2; i++) + { + led right_stick_led; + + right_stick_led.name = "Right Stick LED " + std::to_string(i); + + leds.push_back(right_stick_led); + } + + SetupColors(); +} + +void RGBController_AsusROGAlly::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AsusROGAlly::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == ROG_ALLY_MODE_DIRECT) + { + controller->UpdateLeds(std::vector(colors)); + } +} + +void RGBController_AsusROGAlly::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AsusROGAlly::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AsusROGAlly::DeviceUpdateMode() +{ + controller->UpdateBrightness(modes[active_mode].brightness); + + if(modes[active_mode].value == ROG_ALLY_MODE_DIRECT) + { + DeviceUpdateLEDs(); + } + else + { + unsigned int rog_ally_direction = ROG_ALLY_DIRECTION_RIGHT; + + if((modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) && (modes[active_mode].direction == MODE_DIRECTION_LEFT)) + { + rog_ally_direction = ROG_ALLY_DIRECTION_LEFT; + } + + controller->UpdateDevice(modes[active_mode].value, modes[active_mode].colors, modes[active_mode].speed, rog_ally_direction); + } +} + +void RGBController_AsusROGAlly::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->SaveMode(); +} diff --git a/Controllers/AsusAuraUSBController/AsusROGAllyController/RGBController_AsusROGAlly.h b/Controllers/AsusAuraUSBController/AsusROGAllyController/RGBController_AsusROGAlly.h new file mode 100644 index 0000000..9faca3c --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusROGAllyController/RGBController_AsusROGAlly.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusROGAlly.h | +| | +| RGBController for ASUS ROG Ally | +| | +| Adam Honse (CalcProgrammer1) 12 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusROGAllyController.h" + +class RGBController_AsusROGAlly : public RGBController +{ +public: + RGBController_AsusROGAlly(ROGAllyController* controller_ptr); + ~RGBController_AsusROGAlly(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + ROGAllyController* controller; +}; diff --git a/Controllers/AsusAuraUSBController/AsusROGStrixLCController/AsusROGStrixLCController.cpp b/Controllers/AsusAuraUSBController/AsusROGStrixLCController/AsusROGStrixLCController.cpp new file mode 100644 index 0000000..59ba536 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusROGStrixLCController/AsusROGStrixLCController.cpp @@ -0,0 +1,128 @@ +/*---------------------------------------------------------*\ +| AsusROGStrixLCController.cpp | +| | +| Driver for ASUS Aura liquid cooler | +| | +| Chris M (Dr_No) 17 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "AsusROGStrixLCController.h" + +AsusROGStrixLCController::AsusROGStrixLCController(hid_device* dev_handle, const char* path, std::string dev_name) : AuraUSBController(dev_handle, path, dev_name) +{ + /*-----------------------------------------------------*\ + | Add addressable devices | + | Manually adding device info for now | + | TODO: Implement config table accurately | + | LC120 - 1F FF 05 05 04 00 00 00 | + \*-----------------------------------------------------*/ + device_info.push_back({0x00, 0x00, 4, 0, AuraDeviceType::FIXED}); +} + +AsusROGStrixLCController::~AsusROGStrixLCController() +{ + // Device will close at AuraUSBController destructor +} + +std::string AsusROGStrixLCController::GetLocation() +{ + return("HID: " + location); +} + +void AsusROGStrixLCController::SetMode(unsigned char /*channel*/, unsigned char /*mode*/, unsigned char /*red*/, unsigned char /*grn*/, unsigned char /*blu*/) +{ + /*---------------------------------------------------------*\ + | This interface is not used in this controller however is | + | required by the abstract class | + \*---------------------------------------------------------*/ +} + +void AsusROGStrixLCController::SetMode(unsigned char mode, unsigned char speed, unsigned char direction, RGBColor colour) +{ + bool needs_update = !( (current_mode == mode) && (ToRGBColor(current_red, current_green, current_blue) == colour) && (current_speed == speed) && (current_direction == direction)); + + if (needs_update) + { + current_mode = mode; + current_speed = speed; + current_direction = direction; + current_red = RGBGetRValue(colour); + current_green = RGBGetGValue(colour); + current_blue = RGBGetBValue(colour); + SendUpdate(); + } +} + +void AsusROGStrixLCController::SetChannelLEDs(unsigned char /*channel*/, RGBColor* /*colors*/, unsigned int /*num_colors*/) +{ + /*---------------------------------------------------------*\ + | This interface is not used in this controller however is | + | required by the abstract class | + \*---------------------------------------------------------*/ +} + +void AsusROGStrixLCController::SetLedsDirect(RGBColor * led_colours, uint8_t led_count) +{ + uint8_t buffer[write_packet_size] = { 0xEC, 0x40, 0x00, 0xFF, led_count }; + + /*---------------------------------------------------------*\ + | Set the colour bytes in the packet | + \*---------------------------------------------------------*/ + for(uint8_t index = 0; index < led_count; index++) + { + uint8_t offset = (index * 3) + ROGSTRIXLC_GREEN_BYTE; + + buffer[offset + 0] = RGBGetRValue(led_colours[index]); + buffer[offset + 1] = RGBGetGValue(led_colours[index]); + buffer[offset + 2] = RGBGetBValue(led_colours[index]); + } + + /*---------------------------------------------------------*\ + | These 3 bytes might be timing bytes | + \*---------------------------------------------------------*/ + uint8_t offset = led_count * 3 + ROGSTRIXLC_GREEN_BYTE; + buffer[offset + 0] = 0x77; + buffer[offset + 1] = 0x10; + buffer[offset + 2] = 0xF3; + + hid_write(dev, buffer, write_packet_size); +} + +void AsusROGStrixLCController::GetStatus() +{ + uint8_t buffer[write_packet_size] = { 0xEC, 0x01, 0x02 }; + + hid_write(dev, buffer, write_packet_size); + hid_read_timeout(dev, buffer, read_packet_size, ROGSTRIXLC_CONTROLLER_TIMEOUT); + + current_red = buffer[ROGSTRIXLC_RED_BYTE - 1]; + current_green = buffer[ROGSTRIXLC_GREEN_BYTE - 1]; + current_blue = buffer[ROGSTRIXLC_BLUE_BYTE - 1]; +} + +void AsusROGStrixLCController::SendUpdate() +{ + uint8_t buffer[write_packet_size]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buffer, 0x00, write_packet_size); + + buffer[ROGSTRIXLC_REPORTID_BYTE] = rogstrixlc_reportid; + buffer[ROGSTRIXLC_COMMAND_BYTE] = rogstrixlc_modefx; + buffer[ROGSTRIXLC_ZONE_BYTE] = 0; + buffer[ROGSTRIXLC_MODE_BYTE] = current_mode; + buffer[ROGSTRIXLC_RED_BYTE] = current_red; + buffer[ROGSTRIXLC_GREEN_BYTE] = current_green; + buffer[ROGSTRIXLC_BLUE_BYTE] = current_blue; + + buffer[ROGSTRIXLC_DIRECTION_BYTE] = current_direction; + buffer[ROGSTRIXLC_SPEED_BYTE] = current_speed; + + hid_write(dev, buffer, write_packet_size); +} + diff --git a/Controllers/AsusAuraUSBController/AsusROGStrixLCController/AsusROGStrixLCController.h b/Controllers/AsusAuraUSBController/AsusROGStrixLCController/AsusROGStrixLCController.h new file mode 100644 index 0000000..1f01208 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusROGStrixLCController/AsusROGStrixLCController.h @@ -0,0 +1,93 @@ +/*---------------------------------------------------------*\ +| AsusROGStrixLCController.h | +| | +| Driver for ASUS Aura liquid cooler | +| | +| Chris M (Dr_No) 17 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "AsusAuraUSBController.h" + +#define ROGSTRIXLC_CONTROLLER_TIMEOUT 250 +#define HID_MAX_STR 255 + +#define ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MIN 0 +#define ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX 255 + +enum +{ + ROGSTRIXLC_CONTROLLER_MODE_DIRECT = 0xFF, //Direct Led Control - Independently set LEDs in zone + ROGSTRIXLC_CONTROLLER_MODE_STATIC = 0x01, //Static Mode - Set entire zone to a single color. + ROGSTRIXLC_CONTROLLER_MODE_BREATHING = 0x02, //Breathing Mode - Fades between fully off and fully on. + ROGSTRIXLC_CONTROLLER_MODE_FLASHING = 0x03, //Flashing Mode - Abruptly changing between fully off and fully on. + ROGSTRIXLC_CONTROLLER_MODE_SPECTRUM = 0x04, //Spectrum Cycle Mode - Cycles through the color spectrum on all lights on the device + ROGSTRIXLC_CONTROLLER_MODE_RAINBOW = 0x05, //Rainbow Wave Mode - Cycle thru the color spectrum as a wave across all LEDs + ROGSTRIXLC_CONTROLLER_MODE_FLASHANDDASH = 0x0A, //Flash n Dash - Flash twice and then flash in direction +}; + +enum AsusAuraStrixLC_PacketMap +{ + ROGSTRIXLC_REPORTID_BYTE = 0, + ROGSTRIXLC_COMMAND_BYTE = 1, + ROGSTRIXLC_ZONE_BYTE = 2, + ROGSTRIXLC_MODE_BYTE = 3, + ROGSTRIXLC_RED_BYTE = 4, + ROGSTRIXLC_GREEN_BYTE = 5, + ROGSTRIXLC_BLUE_BYTE = 6, + ROGSTRIXLC_DIRECTION_BYTE = 7, + ROGSTRIXLC_SPEED_BYTE = 8, +}; + +enum +{ + ROGSTRIXLC_CONTROLLER_SPEED_SLOWEST = 0x04, // Slowest speed + ROGSTRIXLC_CONTROLLER_SPEED_SLOW = 0x03, // Slower speed + ROGSTRIXLC_CONTROLLER_SPEED_NORMAL = 0x02, // Normal speed + ROGSTRIXLC_CONTROLLER_SPEED_FAST = 0x01, // Fast speed + ROGSTRIXLC_CONTROLLER_SPEED_FASTEST = 0x00, // Fastest speed +}; + +class AsusROGStrixLCController : public AuraUSBController +{ +public: + AsusROGStrixLCController(hid_device* dev_handle, const char* path, std::string dev_name); + ~AsusROGStrixLCController(); + + std::string GetLocation(); + + void SetChannelLEDs(unsigned char channel, RGBColor *colors, unsigned int num_colors); + void SetLedsDirect(RGBColor * led_colours, uint8_t led_count); + + void SetMode(unsigned char channel, unsigned char mode, unsigned char red, unsigned char grn, unsigned char blu); + void SetMode(unsigned char mode, unsigned char speed, unsigned char direction, RGBColor colour); +private: + static const uint8_t read_packet_size = 64; + static const uint8_t write_packet_size = read_packet_size + 1; + static const uint8_t rogstrixlc_modefx = 0x3B; + static const uint8_t rogstrixlc_direct = 0x40; + static const uint8_t rogstrixlc_reportid = 0xEC; + + std::string location; + + uint8_t zone_index; + uint8_t current_mode; + uint8_t current_speed; + + uint8_t current_red; + uint8_t current_green; + uint8_t current_blue; + uint8_t current_direction; + + void GetStatus(); + void SendUpdate(); + void SendEffect(unsigned char channel, unsigned char mode, unsigned char red, unsigned char grn, unsigned char blu); + void SendDirectApply(unsigned char channel); +}; diff --git a/Controllers/AsusAuraUSBController/AsusROGStrixLCController/RGBController_AsusROGStrixLC.cpp b/Controllers/AsusAuraUSBController/AsusROGStrixLCController/RGBController_AsusROGStrixLC.cpp new file mode 100644 index 0000000..8f2fead --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusROGStrixLCController/RGBController_AsusROGStrixLC.cpp @@ -0,0 +1,221 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusROGStrixLC.cpp | +| | +| RGBController for ASUS Aura liquid cooler | +| | +| Chris M (Dr_No) 17 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_AsusROGStrixLC.h" + +/**------------------------------------------------------------------*\ + @name Asus ROG Strix Liquid Cooler + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusAuraUSBROGStrixLC + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AsusROGStrixLC::RGBController_AsusROGStrixLC(AsusROGStrixLCController *controller_ptr) +{ + controller = controller_ptr; + uint8_t speed = ROGSTRIXLC_CONTROLLER_SPEED_NORMAL; + + name = controller->GetDeviceName(); + vendor = "ASUS"; + type = DEVICE_TYPE_COOLER; + description = "ASUS Liquid Cooler including 120mm, 140mm, 240mm, 280mm and 360mm radiators."; + version = KEY_EN_UNUSED; + serial = KEY_EN_UNUSED; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ROGSTRIXLC_CONTROLLER_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = ROGSTRIXLC_CONTROLLER_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ROGSTRIXLC_CONTROLLER_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(Breathing.colors_max); + Breathing.brightness_min = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MIN; + Breathing.brightness_max = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX; + Breathing.brightness = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX; + Breathing.speed_min = ROGSTRIXLC_CONTROLLER_SPEED_SLOWEST; + Breathing.speed_max = ROGSTRIXLC_CONTROLLER_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = speed; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = ROGSTRIXLC_CONTROLLER_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Flashing.colors_min = 1; + Flashing.colors_max = 1; + Flashing.colors.resize(Flashing.colors_max); + Flashing.brightness_min = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MIN; + Flashing.brightness_max = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX; + Flashing.brightness = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX; + Flashing.speed_min = ROGSTRIXLC_CONTROLLER_SPEED_SLOWEST; + Flashing.speed_max = ROGSTRIXLC_CONTROLLER_SPEED_FASTEST; + Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flashing.speed = speed; + modes.push_back(Flashing); + + mode Spectrum; + Spectrum.name = "Spectrum"; + Spectrum.value = ROGSTRIXLC_CONTROLLER_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_SPEED; + Spectrum.brightness_min = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MIN; + Spectrum.brightness_max = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX; + Spectrum.brightness = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX; + Spectrum.speed_min = ROGSTRIXLC_CONTROLLER_SPEED_SLOWEST; + Spectrum.speed_max = ROGSTRIXLC_CONTROLLER_SPEED_FASTEST; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.speed = speed; + modes.push_back(Spectrum); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = ROGSTRIXLC_CONTROLLER_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.brightness_min = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MIN; + Rainbow.brightness_max = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX; + Rainbow.brightness = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX; + Rainbow.speed_min = ROGSTRIXLC_CONTROLLER_SPEED_SLOWEST; + Rainbow.speed_max = ROGSTRIXLC_CONTROLLER_SPEED_FASTEST; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed = speed; + modes.push_back(Rainbow); + + mode FlashAndDash; + FlashAndDash.name = "Flash and Dash"; + FlashAndDash.value = ROGSTRIXLC_CONTROLLER_MODE_FLASHANDDASH; + FlashAndDash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + FlashAndDash.brightness_min = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MIN; + FlashAndDash.brightness_max = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX; + FlashAndDash.brightness = ROGSTRIXLC_CONTROLLER_BRIGHTNESS_MAX; + FlashAndDash.speed_min = ROGSTRIXLC_CONTROLLER_SPEED_SLOWEST; + FlashAndDash.speed_max = ROGSTRIXLC_CONTROLLER_SPEED_FASTEST; + FlashAndDash.color_mode = MODE_COLORS_NONE; + FlashAndDash.speed = speed; + modes.push_back(FlashAndDash); + + SetupZones(); +} + +RGBController_AsusROGStrixLC::~RGBController_AsusROGStrixLC() +{ + delete controller; +} + +void RGBController_AsusROGStrixLC::SetupZones() +{ + /*-------------------------------------------------*\ + | Set up zones | + \*-------------------------------------------------*/ + LOG_DEBUG("[%s] - Get channel count: %i", name.c_str(), controller->GetChannelCount()); + + zones.resize(controller->GetChannelCount()); + + LOG_DEBUG("[%s] - Creating Zones and LEDs", name.c_str()); + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + AuraDeviceInfo device_info = controller->GetAuraDevices()[zone_idx]; + LOG_INFO("[%s] %s Zone %i - Header Count %i LED Count %i FX %02X Direct %02X", name.c_str(), + ((device_info.device_type == AuraDeviceType::FIXED) ? "Fixed" : "Addressable"), + zone_idx, device_info.num_headers, device_info.num_leds, device_info.effect_channel, device_info.direct_channel); + + zones[zone_idx].name = name + " Zone "; + zones[zone_idx].name.append(std::to_string(zone_idx)); + zones[zone_idx].type = ZONE_TYPE_LINEAR; + zones[zone_idx].leds_min = device_info.num_leds; + zones[zone_idx].leds_max = device_info.num_leds; + zones[zone_idx].leds_count = device_info.num_leds; + + for(unsigned int lp_idx = 0; lp_idx < zones[zone_idx].leds_count; lp_idx++) + { + led new_led; + + new_led.name = zones[zone_idx].name; + new_led.name.append(" LED " + std::to_string(lp_idx)); + new_led.value = lp_idx; + + leds.push_back(new_led); + } + } + + LOG_DEBUG("[%s] - Device zones and LEDs set", name.c_str()); + SetupColors(); +} + +void RGBController_AsusROGStrixLC::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AsusROGStrixLC::DeviceUpdateLEDs() +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + UpdateZoneLEDs(zone_idx); + } +} + +void RGBController_AsusROGStrixLC::UpdateZoneLEDs(int zone) +{ + controller->SetLedsDirect( zones[zone].colors, zones[zone].leds_count ); +} + +void RGBController_AsusROGStrixLC::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(GetLED_Zone(led)); +} + +void RGBController_AsusROGStrixLC::DeviceUpdateMode() +{ + RGBColor colour = (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) ? modes[active_mode].colors[0] : 0; + + controller->SetMode( modes[active_mode].value, modes[active_mode].speed, modes[active_mode].direction, colour ); +} + +int RGBController_AsusROGStrixLC::GetLED_Zone(int led_idx) +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + int zone_start = zones[zone_idx].start_idx; + int zone_end = zone_start + zones[zone_idx].leds_count - 1; + + if(zone_start <= led_idx && zone_end >= led_idx) + { + return(zone_idx); + } + } + + return -1; +} diff --git a/Controllers/AsusAuraUSBController/AsusROGStrixLCController/RGBController_AsusROGStrixLC.h b/Controllers/AsusAuraUSBController/AsusROGStrixLCController/RGBController_AsusROGStrixLC.h new file mode 100644 index 0000000..5da4c88 --- /dev/null +++ b/Controllers/AsusAuraUSBController/AsusROGStrixLCController/RGBController_AsusROGStrixLC.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusROGStrixLC.h | +| | +| RGBController for ASUS Aura liquid cooler | +| | +| Chris M (Dr_No) 17 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "LogManager.h" +#include "RGBController.h" +#include "AsusROGStrixLCController.h" + +class RGBController_AsusROGStrixLC : public RGBController +{ +public: + RGBController_AsusROGStrixLC(AsusROGStrixLCController* controller_ptr); + ~RGBController_AsusROGStrixLC(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); +private: + int GetDeviceMode(); + int GetLED_Zone(int led_idx); + + AsusROGStrixLCController* controller; +}; diff --git a/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/AsusCerberusKeyboardController.cpp b/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/AsusCerberusKeyboardController.cpp new file mode 100644 index 0000000..5fe866b --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/AsusCerberusKeyboardController.cpp @@ -0,0 +1,203 @@ +/*---------------------------------------------------------*\ +| AsusCerberusKeyboardController.cpp | +| | +| Driver for ASUS Cerberus keyboard | +| | +| Mola19 28 May 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include "AsusCerberusKeyboardController.h" +#include "StringUtils.h" + +#define ASUS_CERBERUS_KB_PACKET_SIZE 8 + +AsusCerberusKeyboardController::AsusCerberusKeyboardController(hid_device* dev_handle, const char* path, unsigned short rev_version, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + version = rev_version; +} + +AsusCerberusKeyboardController::~AsusCerberusKeyboardController() +{ + hid_close(dev); +} + +std::string AsusCerberusKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AsusCerberusKeyboardController::GetDeviceName() +{ + return(name); +} + +std::string AsusCerberusKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string AsusCerberusKeyboardController::GetVersion() +{ + char versionstr[5]; + snprintf(versionstr, 5, "%X", version); + return(std::string(versionstr)); +} + +void AsusCerberusKeyboardController::SetProfile + ( + uint8_t profile + ) +{ + uint8_t usb_buf[ASUS_CERBERUS_KB_PACKET_SIZE]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x07; + usb_buf[0x01] = 0x03; + usb_buf[0x02] = profile; + + hid_send_feature_report(dev, usb_buf, ASUS_CERBERUS_KB_PACKET_SIZE); +} + +void AsusCerberusKeyboardController::SetPerLEDColor + ( + uint8_t key, + uint8_t red, + uint8_t green, + uint8_t blue + ) +{ + uint8_t profile = 1; + + uint8_t usb_buf[ASUS_CERBERUS_KB_PACKET_SIZE]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x07; + usb_buf[0x01] = 0x0D; + usb_buf[0x02] = profile; + usb_buf[0x03] = key; + usb_buf[0x04] = red; + usb_buf[0x05] = green; + usb_buf[0x06] = blue; + + hid_send_feature_report(dev, usb_buf, ASUS_CERBERUS_KB_PACKET_SIZE); +} + +void AsusCerberusKeyboardController::SendPerLEDColorEnd() +{ + SetPerLEDColor(255, 0, 0, 0); +} + +void AsusCerberusKeyboardController::SetPerLEDMode + ( + uint8_t mode + ) +{ + /*------------------------------------------------------------------------------------------------------*\ + | this device has 6 different profiles, but there is no way to fetch them from the device, | + | hence this device always sends to the first profile until a better solution is implemented in OpenRGB | + \*------------------------------------------------------------------------------------------------------*/ + uint8_t profile = 1; + + /*--------------------------------------------------------------------------------*\ + | 8 booleans per byte, each boolean controls one key | + | 0 = static, 1 = breathing | + | since openrgb doesn't support per led modes, either all static or all breathing | + \*--------------------------------------------------------------------------------*/ + + uint8_t modebyte = (mode == 1) ? 0xFF : 0x00; + + for(int i = 0; i < 4; i++) + { + uint8_t usb_buf[ASUS_CERBERUS_KB_PACKET_SIZE]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x07; + usb_buf[0x01] = 0x0E; + usb_buf[0x02] = profile; + usb_buf[0x03] = i; + usb_buf[0x04] = modebyte; + usb_buf[0x05] = modebyte; + usb_buf[0x06] = modebyte; + usb_buf[0x07] = modebyte; + + hid_send_feature_report(dev, usb_buf, ASUS_CERBERUS_KB_PACKET_SIZE); + } +} + +void AsusCerberusKeyboardController::SetMode + ( + uint8_t mode, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t direction, + uint8_t brightness + ) +{ + /*------------------------------------------------------------------------------------------------------*\ + | this device has 6 different profiles, but there is no way to fetch them from the device, | + | hence this device always sends to the first profile until a better solution is implemented in OpenRGB | + \*------------------------------------------------------------------------------------------------------*/ + uint8_t profile = 1; + + uint8_t usb_buf_1[ASUS_CERBERUS_KB_PACKET_SIZE]; + + memset(usb_buf_1, 0x00, sizeof(usb_buf_1)); + + usb_buf_1[0x00] = 0x07; + usb_buf_1[0x01] = 0x0A; + usb_buf_1[0x02] = profile; + usb_buf_1[0x03] = mode; + usb_buf_1[0x04] = direction; + + hid_send_feature_report(dev, usb_buf_1, ASUS_CERBERUS_KB_PACKET_SIZE); + + uint8_t usb_buf_2[ASUS_CERBERUS_KB_PACKET_SIZE]; + + memset(usb_buf_2, 0x00, sizeof(usb_buf_2)); + + usb_buf_2[0x00] = 0x07; + usb_buf_2[0x01] = 0x0B; + usb_buf_2[0x02] = profile; + usb_buf_2[0x03] = mode; + usb_buf_2[0x04] = red; + usb_buf_2[0x05] = green; + usb_buf_2[0x06] = blue; + + hid_send_feature_report(dev, usb_buf_2, ASUS_CERBERUS_KB_PACKET_SIZE); + + uint8_t usb_buf_3[ASUS_CERBERUS_KB_PACKET_SIZE]; + + memset(usb_buf_3, 0x00, sizeof(usb_buf_3)); + + usb_buf_3[0x00] = 0x07; + usb_buf_3[0x01] = 0x0C; + usb_buf_3[0x02] = profile; + usb_buf_3[0x03] = brightness; + + hid_send_feature_report(dev, usb_buf_3, ASUS_CERBERUS_KB_PACKET_SIZE); +} diff --git a/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/AsusCerberusKeyboardController.h b/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/AsusCerberusKeyboardController.h new file mode 100644 index 0000000..803c74d --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/AsusCerberusKeyboardController.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| AsusCerberusKeyboardController.h | +| | +| Driver for ASUS Cerberus keyboard | +| | +| Mola19 03 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +enum +{ + CERBERUS_KEYBOARD_MODE_STATIC = 0, + CERBERUS_KEYBOARD_MODE_BREATHING = 1, + CERBERUS_KEYBOARD_MODE_REACTIVE = 2, + CERBERUS_KEYBOARD_MODE_EXPLOSION = 3, + CERBERUS_KEYBOARD_MODE_COLOR_CYCLE = 4, + CERBERUS_KEYBOARD_MODE_WAVE = 6, + CERBERUS_KEYBOARD_MODE_CUSTOM = 7, +}; + +class AsusCerberusKeyboardController +{ +public: + AsusCerberusKeyboardController(hid_device* dev_handle, const char* path, unsigned short rev_version, std::string dev_name); + ~AsusCerberusKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + std::string GetVersion(); + + void SetProfile(uint8_t profile); + void SetPerLEDColor(uint8_t key, uint8_t red, uint8_t green, uint8_t blue); + void SendPerLEDColorEnd(); + void SetPerLEDMode(uint8_t mode); + void SetMode(uint8_t mode, uint8_t red, uint8_t green, uint8_t blue, uint8_t direction, uint8_t brightness); + + +private: + hid_device* dev; + std::string location; + std::string name; + unsigned short version; +}; diff --git a/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/RGBController_AsusCerberusKeyboard.cpp b/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/RGBController_AsusCerberusKeyboard.cpp new file mode 100644 index 0000000..faed1d8 --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/RGBController_AsusCerberusKeyboard.cpp @@ -0,0 +1,392 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusCerberusKeyboard.cpp | +| | +| RGBController for ASUS Cerberus keyboard | +| | +| Mola19 28 May 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBControllerKeyNames.h" +#include "RGBController_AsusCerberusKeyboard.h" + +/**------------------------------------------------------------------*\ + @name Asus Cerberus Mech Keyboard + @category Keyboard + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectAsusCerberusMech + @comment +\*-------------------------------------------------------------------*/ + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[6][24] = +{ + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, 75, 79, 84, NA, NA, NA, NA, NA }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, 76, 80, 85, NA, 88, 93, 97, 102 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, NA, NA, 77, 81, 86, NA, 89, 94, 98, 103 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 70, 73, NA, NA, NA, NA, NA, 90, 95, 99, NA }, + { 4, 7, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, NA, 71, NA, NA, NA, 82, NA, NA, 91, 96, 100, 104 }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, NA, 74, NA, 78, 83, 87, NA, 92, NA, 101, NA } +}; + +struct led_value +{ + const char* name; + uint8_t id; +}; + +static const std::vector led_names = +{ + { KEY_EN_ESCAPE, 0x0B }, + { KEY_EN_BACK_TICK, 0x0E }, + { KEY_EN_TAB, 0x09 }, + { KEY_EN_CAPS_LOCK, 0x11 }, + { KEY_EN_LEFT_SHIFT, 0x79 }, + { KEY_EN_LEFT_CONTROL, 0x06 }, + + { KEY_EN_1, 0x0F }, + { KEY_EN_ISO_BACK_SLASH, 0x13 }, + { KEY_EN_LEFT_WINDOWS, 0x7C }, + + { KEY_EN_F1, 0x16 }, + { KEY_EN_2, 0x17 }, + { KEY_EN_Q, 0x08 }, + { KEY_EN_A, 0x0A }, + { KEY_EN_Z, 0x0C }, + { KEY_EN_LEFT_ALT, 0x4B }, + + { KEY_EN_F2, 0x1E }, + { KEY_EN_3, 0x1F }, + { KEY_EN_W, 0x10 }, + { KEY_EN_S, 0x12 }, + { KEY_EN_X, 0x14 }, + + { KEY_EN_F3, 0x19 }, + { KEY_EN_4, 0x27 }, + { KEY_EN_E, 0x18 }, + { KEY_EN_D, 0x1A }, + { KEY_EN_C, 0x1C }, + + { KEY_EN_F4, 0x1B }, + { KEY_EN_5, 0x26 }, + { KEY_EN_R, 0x20 }, + { KEY_EN_F, 0x22 }, + { KEY_EN_V, 0x24 }, + + { KEY_EN_6, 0x2E }, + { KEY_EN_T, 0x21 }, + { KEY_EN_G, 0x23 }, + { KEY_EN_B, 0x25 }, + { KEY_EN_SPACE, 0x5B }, + + { KEY_EN_F5, 0x07 }, + { KEY_EN_7, 0x2F }, + { KEY_EN_Y, 0x29 }, + { KEY_EN_H, 0x2B }, + { KEY_EN_N, 0x2D }, + + { KEY_EN_F6, 0x33 }, + { KEY_EN_8, 0x37 }, + { KEY_EN_U, 0x28 }, + { KEY_EN_J, 0x2A }, + { KEY_EN_M, 0x2C }, + + { KEY_EN_F7, 0x39 }, + { KEY_EN_9, 0x3F }, + { KEY_EN_I, 0x30 }, + { KEY_EN_K, 0x32 }, + { KEY_EN_COMMA, 0x34 }, + + { KEY_EN_F8, 0x3E }, + { KEY_EN_0, 0x47 }, + { KEY_EN_O, 0x38 }, + { KEY_EN_L, 0x3A }, + { KEY_EN_PERIOD, 0x3C }, + { KEY_EN_RIGHT_ALT, 0x4D }, + + { KEY_EN_F9, 0x56 }, + { KEY_EN_MINUS, 0x46 }, + { KEY_EN_P, 0x40 }, + { KEY_EN_SEMICOLON, 0x42 }, + { KEY_EN_FORWARD_SLASH, 0x45 }, + { KEY_EN_RIGHT_FUNCTION, 0x7D }, + + { KEY_EN_F10, 0x57 }, + { KEY_EN_EQUALS, 0x36 }, + { KEY_EN_LEFT_BRACKET, 0x41 }, + { KEY_EN_QUOTE, 0x43 }, + { KEY_EN_MENU, 0x3D }, + + { KEY_EN_F11, 0x53 }, + { KEY_EN_BACKSPACE, 0x51 }, + { KEY_EN_RIGHT_BRACKET, 0x31 }, + { KEY_EN_POUND, 0x44 }, + { KEY_EN_RIGHT_SHIFT, 0x7A }, + + { KEY_EN_F12, 0x55 }, + { KEY_EN_ISO_ENTER, 0x54 }, + { KEY_EN_RIGHT_CONTROL, 0x04 }, + { KEY_EN_PRINT_SCREEN, 0x4F }, + + { KEY_EN_INSERT, 0x66 }, + { KEY_EN_DELETE, 0x5E }, + { KEY_EN_LEFT_ARROW, 0x75 }, + + { KEY_EN_SCROLL_LOCK, 0x48 }, + { KEY_EN_HOME, 0x76 }, + { KEY_EN_END, 0x77 }, + { KEY_EN_UP_ARROW, 0x73 }, + { KEY_EN_DOWN_ARROW, 0x5D }, + + { KEY_EN_PAUSE_BREAK, 0x00 }, + { KEY_EN_PAGE_UP, 0x6E }, + { KEY_EN_PAGE_DOWN, 0x6F }, + { KEY_EN_RIGHT_ARROW, 0x65 }, + + { KEY_EN_NUMPAD_LOCK, 0x5C }, + { KEY_EN_NUMPAD_7, 0x58 }, + { KEY_EN_NUMPAD_4, 0x59 }, + { KEY_EN_NUMPAD_1, 0x5A }, + { KEY_EN_NUMPAD_0, 0x63 }, + + { KEY_EN_NUMPAD_DIVIDE, 0x64 }, + { KEY_EN_NUMPAD_8, 0x60 }, + { KEY_EN_NUMPAD_5, 0x61 }, + { KEY_EN_NUMPAD_2, 0x62 }, + + { KEY_EN_NUMPAD_TIMES, 0x6C }, + { KEY_EN_NUMPAD_9, 0x68 }, + { KEY_EN_NUMPAD_6, 0x69 }, + { KEY_EN_NUMPAD_3, 0x6A }, + { KEY_EN_NUMPAD_PERIOD, 0x6B }, + + { KEY_EN_NUMPAD_MINUS, 0x6D }, + { KEY_EN_NUMPAD_PLUS, 0x70 }, + { KEY_EN_NUMPAD_ENTER, 0x72 } +}; + +RGBController_AsusCerberusKeyboard::RGBController_AsusCerberusKeyboard(AsusCerberusKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + /*------------------------------------------------------------------------------------------------------*\ + | this device has 6 different profiles, but there is no way to fetch them from the device, | + | hence this device always sends to the first profile until a better solution is implemented in OpenRGB | + \*------------------------------------------------------------------------------------------------------*/ + + controller->SetProfile(1); + + name = controller->GetDeviceName(); + vendor = "ASUS"; + type = DEVICE_TYPE_KEYBOARD; + description = "ASUS Cerberus Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetVersion(); + + mode Custom; + Custom.name = "Custom"; + Custom.value = CERBERUS_KEYBOARD_MODE_STATIC; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Custom.brightness_min = CERBERUS_MECH_BRIGHTNESS_MIN; + Custom.brightness_max = CERBERUS_MECH_BRIGHTNESS_MAX; + Custom.brightness = CERBERUS_MECH_BRIGHTNESS_DEFAULT; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode Static; + Static.name = "Static"; + Static.value = CERBERUS_KEYBOARD_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.brightness_min = CERBERUS_MECH_BRIGHTNESS_MIN; + Static.brightness_max = CERBERUS_MECH_BRIGHTNESS_MAX; + Static.brightness = CERBERUS_MECH_BRIGHTNESS_DEFAULT; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CERBERUS_KEYBOARD_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.brightness_min = CERBERUS_MECH_BRIGHTNESS_MIN; + Breathing.brightness_max = CERBERUS_MECH_BRIGHTNESS_MAX; + Breathing.brightness = CERBERUS_MECH_BRIGHTNESS_DEFAULT; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = CERBERUS_KEYBOARD_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Reactive.brightness_min = CERBERUS_MECH_BRIGHTNESS_MIN; + Reactive.brightness_max = CERBERUS_MECH_BRIGHTNESS_MAX; + Reactive.brightness = CERBERUS_MECH_BRIGHTNESS_DEFAULT; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.colors_min = 1; + Reactive.colors_max = 1; + Reactive.colors.resize(1); + modes.push_back(Reactive); + + mode Explosion; + Explosion.name = "Explosion"; + Explosion.value = CERBERUS_KEYBOARD_MODE_EXPLOSION; + Explosion.flags = MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Explosion.brightness_min = CERBERUS_MECH_BRIGHTNESS_MIN; + Explosion.brightness_max = CERBERUS_MECH_BRIGHTNESS_MAX; + Explosion.brightness = CERBERUS_MECH_BRIGHTNESS_DEFAULT; + Explosion.direction = MODE_DIRECTION_VERTICAL; + Explosion.color_mode = MODE_COLORS_MODE_SPECIFIC; + Explosion.colors_min = 1; + Explosion.colors_max = 1; + Explosion.colors.resize(1); + modes.push_back(Explosion); + + mode Color_Cycle; + Color_Cycle.name = "Spectrum Cycle"; + Color_Cycle.value = CERBERUS_KEYBOARD_MODE_COLOR_CYCLE; + Color_Cycle.flags = MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Color_Cycle.brightness_min = CERBERUS_MECH_BRIGHTNESS_MIN; + Color_Cycle.brightness_max = CERBERUS_MECH_BRIGHTNESS_MAX; + Color_Cycle.brightness = CERBERUS_MECH_BRIGHTNESS_DEFAULT; + Color_Cycle.direction = MODE_DIRECTION_HORIZONTAL; + Color_Cycle.color_mode = MODE_COLORS_NONE; + modes.push_back(Color_Cycle); + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = CERBERUS_KEYBOARD_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Wave.brightness_min = CERBERUS_MECH_BRIGHTNESS_MIN; + Wave.brightness_max = CERBERUS_MECH_BRIGHTNESS_MAX; + Wave.brightness = CERBERUS_MECH_BRIGHTNESS_DEFAULT; + Wave.direction = MODE_DIRECTION_LEFT; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + + SetupZones(); +} + +RGBController_AsusCerberusKeyboard::~RGBController_AsusCerberusKeyboard() +{ + delete controller; +} + +void RGBController_AsusCerberusKeyboard::SetupZones() +{ + int zone_size = 105; + + zone keyboard; + keyboard.name = "Keyboard"; + keyboard.type = ZONE_TYPE_MATRIX; + keyboard.leds_min = zone_size; + keyboard.leds_max = zone_size; + keyboard.leds_count = zone_size; + keyboard.matrix_map = new matrix_map_type; + keyboard.matrix_map->height = 6; + keyboard.matrix_map->width = 24; + keyboard.matrix_map->map = *matrix_map; + zones.push_back(keyboard); + + for(int led_id = 0; led_id < zone_size; led_id++) + { + led new_led; + new_led.name = led_names[led_id].name; + new_led.value = led_names[led_id].id; + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_AsusCerberusKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_AsusCerberusKeyboard::DeviceUpdateLEDs() +{ + for(unsigned int i = 0; i < colors.size(); i++) + { + uint8_t red = RGBGetRValue(colors[i]); + uint8_t green = RGBGetGValue(colors[i]); + uint8_t blue = RGBGetBValue(colors[i]); + + controller->SetPerLEDColor(led_names[i].id, red, green, blue); + } + controller->SendPerLEDColorEnd(); +} + +void RGBController_AsusCerberusKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AsusCerberusKeyboard::UpdateSingleLED(int led) +{ + uint8_t red = RGBGetRValue(colors[led]); + uint8_t green = RGBGetGValue(colors[led]); + uint8_t blue = RGBGetBValue(colors[led]); + + + controller->SetPerLEDColor(led_names[led].id, red, green, blue); + controller->SendPerLEDColorEnd(); +} + +void RGBController_AsusCerberusKeyboard::DeviceUpdateMode() +{ + uint8_t direction = 0; + uint8_t red = 0; + uint8_t green = 0; + uint8_t blue = 0; + + uint8_t mode = modes[active_mode].value; + + + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + mode = CERBERUS_KEYBOARD_MODE_CUSTOM; + direction = 1; + + controller->SetPerLEDMode(modes[active_mode].value); + } + + switch(modes[active_mode].value) + { + case CERBERUS_KEYBOARD_MODE_EXPLOSION: + direction = (modes[active_mode].direction == MODE_DIRECTION_HORIZONTAL) ? 1 : 0; + break; + + case CERBERUS_KEYBOARD_MODE_COLOR_CYCLE: + direction = (modes[active_mode].direction == MODE_DIRECTION_HORIZONTAL) ? 0 : 1; + break; + + case CERBERUS_KEYBOARD_MODE_WAVE: + direction = modes[active_mode].direction; + break; + } + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + green = RGBGetGValue(modes[active_mode].colors[0]); + blue = RGBGetBValue(modes[active_mode].colors[0]); + } + + controller->SetMode(mode, red, green, blue, direction, modes[active_mode].brightness); +} diff --git a/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/RGBController_AsusCerberusKeyboard.h b/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/RGBController_AsusCerberusKeyboard.h new file mode 100644 index 0000000..2b11077 --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/RGBController_AsusCerberusKeyboard.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusCerberusKeyboard.h | +| | +| RGBController for ASUS Cerberus keyboard | +| | +| Mola19 03 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusCerberusKeyboardController.h" + +enum +{ + CERBERUS_MECH_BRIGHTNESS_MIN = 0, + CERBERUS_MECH_BRIGHTNESS_MAX = 4, + CERBERUS_MECH_BRIGHTNESS_DEFAULT = 4 +}; + +class RGBController_AsusCerberusKeyboard : public RGBController +{ +public: + RGBController_AsusCerberusKeyboard(AsusCerberusKeyboardController* controller_ptr); + ~RGBController_AsusCerberusKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AsusCerberusKeyboardController* controller; +}; diff --git a/Controllers/AsusLegacyUSBController/AsusLegacyUSBControllerDetect.cpp b/Controllers/AsusLegacyUSBController/AsusLegacyUSBControllerDetect.cpp new file mode 100644 index 0000000..4f9afe4 --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusLegacyUSBControllerDetect.cpp @@ -0,0 +1,67 @@ +/*---------------------------------------------------------*\ +| AsusLegacyUSBControllerDetect.cpp | +| | +| Detector for ASUS legacy USB devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "AsusCerberusKeyboardController.h" +#include "AsusSagarisKeyboardController.h" +#include "AsusStrixClawController.h" +#include "RGBController_AsusCerberusKeyboard.h" +#include "RGBController_AsusSagarisKeyboard.h" +#include "RGBController_AsusStrixClaw.h" + +#define ASUS_LEGACY_USB_VID 0x195D +#define ASUS_USB_VID 0x0B05 + +#define ASUS_CERBERUS_MECH_PID 0x2047 +#define ASUS_SAGARIS_GK1100_PID 0x1835 +#define ASUS_ROG_STRIX_CLAW_PID 0x1016 + +void DetectAsusCerberusMech(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AsusCerberusKeyboardController* controller = new AsusCerberusKeyboardController(dev, info->path, info->release_number, name); + RGBController_AsusCerberusKeyboard* rgb_controller = new RGBController_AsusCerberusKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusSagarisKeyboard(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AsusSagarisKeyboardController* controller = new AsusSagarisKeyboardController(dev, info->path, info->release_number, name); + RGBController_AsusSagarisKeyboard* rgb_controller = new RGBController_AsusSagarisKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectAsusStrixClaw(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + StrixClawController* controller = new StrixClawController(dev, info->path, name); + RGBController_StrixClaw* rgb_controller = new RGBController_StrixClaw(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("ASUS Cerberus Mech", DetectAsusCerberusMech, ASUS_LEGACY_USB_VID, ASUS_CERBERUS_MECH_PID, 1, 0xFF01, 1); +REGISTER_HID_DETECTOR_IPU("ASUS Sagaris GK1100", DetectAsusSagarisKeyboard, ASUS_USB_VID, ASUS_SAGARIS_GK1100_PID, 1, 0xFF02, 2); +REGISTER_HID_DETECTOR_IPU("ASUS ROG Strix Claw", DetectAsusStrixClaw, ASUS_LEGACY_USB_VID, ASUS_ROG_STRIX_CLAW_PID, 0, 0xFF01, 1); diff --git a/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/AsusSagarisKeyboardController.cpp b/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/AsusSagarisKeyboardController.cpp new file mode 100644 index 0000000..12c29ed --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/AsusSagarisKeyboardController.cpp @@ -0,0 +1,201 @@ +/*---------------------------------------------------------*\ +| AsusSagarisKeyboardController.cpp | +| | +| Driver for ASUS Sagaris keyboard | +| | +| Mola19 20 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "AsusSagarisKeyboardController.h" +#include "LogManager.h" +#include "StringUtils.h" + +#define ASUS_SAGARIS_KB_PACKET_SIZE 65 + +AsusSagarisKeyboardController::AsusSagarisKeyboardController(hid_device* dev_handle, const char* path, unsigned short rev_version, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + version = rev_version; +} + +AsusSagarisKeyboardController::~AsusSagarisKeyboardController() +{ + hid_close(dev); +} + +std::string AsusSagarisKeyboardController::GetVersion() +{ + return std::to_string((int) floor(version / 0x100)) + "." + std::to_string(version % 0x100); +} + +std::string AsusSagarisKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AsusSagarisKeyboardController::GetDeviceName() +{ + return(name); +} + +std::string AsusSagarisKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +sagaris_mode AsusSagarisKeyboardController::GetMode() +{ + ClearResponses(); + + uint8_t usb_buf_out[ASUS_SAGARIS_KB_PACKET_SIZE]; + memset(usb_buf_out, 0x00, sizeof(usb_buf_out)); + + usb_buf_out[0x00] = 0x06; + usb_buf_out[0x01] = 0x0B; + usb_buf_out[0x02] = 0x03; + + hid_write(dev, usb_buf_out, ASUS_SAGARIS_KB_PACKET_SIZE); + + unsigned char usb_buf_in[ASUS_SAGARIS_KB_PACKET_SIZE]; + memset(usb_buf_in, 0x00, sizeof(usb_buf_in)); + int return_length = hid_read_timeout(dev, usb_buf_in, ASUS_SAGARIS_KB_PACKET_SIZE, 100); + + if(return_length == -1) + { + LOG_DEBUG("[Asus Sagaris GK1100]: Could not fetch mode"); + sagaris_mode default_mode; + default_mode.mode = SAGARIS_KEYBOARD_MODE_STATIC; + default_mode.brightness = SAGARIS_KEYBOARD_BRIGHTNESS_DEFAULT; + default_mode.speed = 0; + default_mode.colorIndex = 0; + + return default_mode; + } + + sagaris_mode current_mode; + current_mode.mode = usb_buf_in[3]; + current_mode.brightness = usb_buf_in[4]; + current_mode.speed = usb_buf_in[5]; + current_mode.colorIndex = usb_buf_in[6]; + + return current_mode; +} + +std::vector AsusSagarisKeyboardController::GetColors() +{ + ClearResponses(); + + uint8_t usb_buf_out[ASUS_SAGARIS_KB_PACKET_SIZE]; + memset(usb_buf_out, 0x00, sizeof(usb_buf_out)); + + usb_buf_out[0x00] = 0x06; + usb_buf_out[0x01] = 0x0B; + usb_buf_out[0x02] = 0x05; + + hid_write(dev, usb_buf_out, ASUS_SAGARIS_KB_PACKET_SIZE); + + std::vector colors; + colors.resize(7); + + for(int i = 0; i < 7; i++) + { + unsigned char usb_buf_in[ASUS_SAGARIS_KB_PACKET_SIZE]; + memset(usb_buf_in, 0x00, sizeof(usb_buf_in)); + int return_length = hid_read_timeout(dev, usb_buf_in, ASUS_SAGARIS_KB_PACKET_SIZE, 100); + + if(return_length == -1) + { + LOG_DEBUG("[Asus Sagaris GK1100]: Could not fetch color %i", i); + colors[i] = ToRGBColor(0, 0, 0); + continue; + } + + colors[i] = ToRGBColor(usb_buf_in[4] * 16, usb_buf_in[5] * 16, usb_buf_in[6] * 16); + } + + return colors; +} + +void AsusSagarisKeyboardController::SetColor + ( + uint8_t index, + uint8_t red, + uint8_t green, + uint8_t blue + ) +{ + uint8_t usb_buf[ASUS_SAGARIS_KB_PACKET_SIZE]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x06; + usb_buf[0x01] = 0x0B; + usb_buf[0x02] = 0x04; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = index; + usb_buf[0x05] = red; + usb_buf[0x06] = green; + usb_buf[0x07] = blue; + + hid_write(dev, usb_buf, ASUS_SAGARIS_KB_PACKET_SIZE); +} + +void AsusSagarisKeyboardController::SetMode + ( + uint8_t mode, + uint8_t brightness, + uint8_t speed, + uint8_t colorIndex + ) +{ + ClearResponses(); + + uint8_t usb_buf[ASUS_SAGARIS_KB_PACKET_SIZE]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x06; + usb_buf[0x01] = 0x0B; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = mode; + usb_buf[0x04] = brightness; + usb_buf[0x05] = speed; + usb_buf[0x06] = colorIndex; + + hid_write(dev, usb_buf, ASUS_SAGARIS_KB_PACKET_SIZE); + + AwaitResponse(20); +} + +void AsusSagarisKeyboardController::AwaitResponse(int ms) +{ + unsigned char usb_buf[ASUS_SAGARIS_KB_PACKET_SIZE]; + hid_read_timeout(dev, usb_buf, ASUS_SAGARIS_KB_PACKET_SIZE, ms); +} + +void AsusSagarisKeyboardController::ClearResponses() +{ + int result = 1; + unsigned char usb_buf_flush[65]; + while(result > 0) + { + result = hid_read_timeout(dev, usb_buf_flush, 65, 0); + } +} diff --git a/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/AsusSagarisKeyboardController.h b/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/AsusSagarisKeyboardController.h new file mode 100644 index 0000000..3139fd2 --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/AsusSagarisKeyboardController.h @@ -0,0 +1,79 @@ +/*---------------------------------------------------------*\ +| AsusSagarisKeyboardController.h | +| | +| Driver for ASUS Sagaris keyboard | +| | +| Mola19 20 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +enum +{ + SAGARIS_KEYBOARD_MODE_OFF = 0, + SAGARIS_KEYBOARD_MODE_STATIC = 1, + SAGARIS_KEYBOARD_MODE_SPRIAL = 2, + SAGARIS_KEYBOARD_MODE_CUSTOM = 3, + SAGARIS_KEYBOARD_MODE_BREATHING = 4, + SAGARIS_KEYBOARD_MODE_REACTIVE = 5, + SAGARIS_KEYBOARD_MODE_STARRY_NIGHT = 6, + SAGARIS_KEYBOARD_MODE_LASER = 7, +}; + +enum +{ + SAGARIS_KEYBOARD_BRIGHTNESS_MIN = 0, + SAGARIS_KEYBOARD_BRIGHTNESS_MAX = 3, + SAGARIS_KEYBOARD_BRIGHTNESS_DEFAULT = 3 +}; + +enum +{ + SAGARIS_KEYBOARD_SPEED_MIN = 0, + SAGARIS_KEYBOARD_SPEED_MAX = 3, + SAGARIS_KEYBOARD_SPEED_DEFAULT = 2 +}; + +typedef struct +{ + uint8_t mode; + uint8_t brightness; + uint8_t speed; + uint8_t colorIndex; +} sagaris_mode; + + +class AsusSagarisKeyboardController +{ +public: + AsusSagarisKeyboardController(hid_device* dev_handle, const char* path, unsigned short rev_version, std::string dev_name); + ~AsusSagarisKeyboardController(); + + std::string GetVersion(); + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + sagaris_mode GetMode(); + std::vector GetColors(); + + void SetMode(uint8_t mode, uint8_t brightness, uint8_t speed, uint8_t colorIndex); + void SetColor(uint8_t index, uint8_t red, uint8_t green, uint8_t blue); + + void AwaitResponse(int ms); + void ClearResponses(); + +private: + hid_device* dev; + std::string location; + std::string name; + unsigned short version; +}; diff --git a/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/RGBController_AsusSagarisKeyboard.cpp b/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/RGBController_AsusSagarisKeyboard.cpp new file mode 100644 index 0000000..7ce7cfd --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/RGBController_AsusSagarisKeyboard.cpp @@ -0,0 +1,250 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusSagarisKeyboard.cpp | +| | +| RGBController for ASUS Sagaris keyboard | +| | +| Mola19 20 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_AsusSagarisKeyboard.h" + +/**------------------------------------------------------------------*\ + @name Asus Sagaris Keyboard + @category Keyboard + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectAsusSagarisKeyboard + @comment Missing controls for modifier keys, as they have independent lighting +\*-------------------------------------------------------------------*/ + +RGBController_AsusSagarisKeyboard::RGBController_AsusSagarisKeyboard(AsusSagarisKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "ASUS"; + type = DEVICE_TYPE_KEYBOARD; + description = "ASUS Sagaris Keyboard Device"; + version = controller->GetVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Off; + Off.name = "Off"; + Off.value = SAGARIS_KEYBOARD_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = SAGARIS_KEYBOARD_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.brightness_min = SAGARIS_KEYBOARD_BRIGHTNESS_MIN; + Static.brightness_max = SAGARIS_KEYBOARD_BRIGHTNESS_MAX; + Static.brightness = SAGARIS_KEYBOARD_BRIGHTNESS_DEFAULT; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Spiral; + Spiral.name = "Spiral"; + Spiral.value = SAGARIS_KEYBOARD_MODE_SPRIAL; + Spiral.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Spiral.brightness_min = SAGARIS_KEYBOARD_BRIGHTNESS_MIN; + Spiral.brightness_max = SAGARIS_KEYBOARD_BRIGHTNESS_MAX; + Spiral.brightness = SAGARIS_KEYBOARD_BRIGHTNESS_DEFAULT; + Spiral.speed_min = SAGARIS_KEYBOARD_SPEED_MIN; + Spiral.speed_max = SAGARIS_KEYBOARD_SPEED_MAX; + Spiral.speed = SAGARIS_KEYBOARD_SPEED_DEFAULT; + Spiral.color_mode = MODE_COLORS_MODE_SPECIFIC; + Spiral.colors_min = 7; + Spiral.colors_max = 7; + Spiral.colors.resize(7); + modes.push_back(Spiral); + + mode Custom; + Custom.name = "Custom"; + Custom.value = SAGARIS_KEYBOARD_MODE_CUSTOM; + Custom.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Custom.brightness_min = SAGARIS_KEYBOARD_BRIGHTNESS_MIN; + Custom.brightness_max = SAGARIS_KEYBOARD_BRIGHTNESS_MAX; + Custom.brightness = SAGARIS_KEYBOARD_BRIGHTNESS_DEFAULT; + Custom.color_mode = MODE_COLORS_MODE_SPECIFIC; + Custom.colors_min = 7; + Custom.colors_max = 7; + Custom.colors.resize(7); + modes.push_back(Custom); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = SAGARIS_KEYBOARD_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + Breathing.speed_min = SAGARIS_KEYBOARD_BRIGHTNESS_MIN; + Breathing.speed_max = SAGARIS_KEYBOARD_BRIGHTNESS_MAX; + Breathing.speed = SAGARIS_KEYBOARD_BRIGHTNESS_DEFAULT; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 7; + Breathing.colors_max = 7; + Breathing.colors.resize(7); + modes.push_back(Breathing); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = SAGARIS_KEYBOARD_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Reactive.brightness_min = SAGARIS_KEYBOARD_BRIGHTNESS_MIN; + Reactive.brightness_max = SAGARIS_KEYBOARD_BRIGHTNESS_MAX; + Reactive.brightness = SAGARIS_KEYBOARD_BRIGHTNESS_DEFAULT; + Reactive.speed_min = SAGARIS_KEYBOARD_SPEED_MIN; + Reactive.speed_max = SAGARIS_KEYBOARD_SPEED_MAX; + Reactive.speed = SAGARIS_KEYBOARD_SPEED_DEFAULT; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.colors_min = 1; + Reactive.colors_max = 1; + Reactive.colors.resize(1); + modes.push_back(Reactive); + + mode Starry_Night; + Starry_Night.name = "Starry Night"; + Starry_Night.value = SAGARIS_KEYBOARD_MODE_STARRY_NIGHT; + Starry_Night.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Starry_Night.brightness_min = SAGARIS_KEYBOARD_SPEED_MIN; + Starry_Night.brightness_max = SAGARIS_KEYBOARD_SPEED_MAX; + Starry_Night.brightness = SAGARIS_KEYBOARD_SPEED_DEFAULT; + Starry_Night.speed_min = SAGARIS_KEYBOARD_BRIGHTNESS_MIN; + Starry_Night.speed_max = SAGARIS_KEYBOARD_BRIGHTNESS_MAX; + Starry_Night.speed = SAGARIS_KEYBOARD_BRIGHTNESS_DEFAULT; + Starry_Night.color_mode = MODE_COLORS_MODE_SPECIFIC; + Starry_Night.colors_min = 7; + Starry_Night.colors_max = 7; + Starry_Night.colors.resize(7); + modes.push_back(Starry_Night); + + mode Laser; + Laser.name = "Laser"; + Laser.value = SAGARIS_KEYBOARD_MODE_LASER; + Laser.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Laser.brightness_min = SAGARIS_KEYBOARD_SPEED_MIN; + Laser.brightness_max = SAGARIS_KEYBOARD_SPEED_MAX; + Laser.brightness = SAGARIS_KEYBOARD_SPEED_DEFAULT; + Laser.speed_min = SAGARIS_KEYBOARD_BRIGHTNESS_MIN; + Laser.speed_max = SAGARIS_KEYBOARD_BRIGHTNESS_MAX; + Laser.speed = SAGARIS_KEYBOARD_BRIGHTNESS_DEFAULT; + Laser.color_mode = MODE_COLORS_MODE_SPECIFIC; + Laser.colors_min = 7; + Laser.colors_max = 7; + Laser.colors.resize(7); + modes.push_back(Laser); + + SetupZones(); + + sagaris_mode current_mode = controller->GetMode(); + + active_mode = current_mode.mode; + last_mode = current_mode.mode; + modes[active_mode].brightness = current_mode.brightness; + modes[active_mode].speed = current_mode.speed; + + current_colors = controller->GetColors(); + + for(unsigned int i = 0; i < modes[active_mode].colors.size(); i++) + { + modes[active_mode].colors[i] = current_colors[i]; + } +} + +RGBController_AsusSagarisKeyboard::~RGBController_AsusSagarisKeyboard() +{ + delete controller; +} + +void RGBController_AsusSagarisKeyboard::SetupZones() +{ + zone keyboard; + keyboard.name = "Keyboard"; + keyboard.type = ZONE_TYPE_SINGLE; + keyboard.leds_min = 1; + keyboard.leds_max = 1; + keyboard.leds_count = 1; + keyboard.matrix_map = nullptr; + + SetupColors(); +} + +void RGBController_AsusSagarisKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_AsusSagarisKeyboard::DeviceUpdateLEDs() +{ + +} + +void RGBController_AsusSagarisKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + +} + +void RGBController_AsusSagarisKeyboard::UpdateSingleLED(int /*led*/) +{ + +} + +void RGBController_AsusSagarisKeyboard::DeviceUpdateMode() +{ + if(last_mode != active_mode) + { + last_mode = active_mode; + for(unsigned int i = 0; i < modes[active_mode].colors.size(); i++) + { + modes[active_mode].colors[i] = current_colors[i]; + } + } + else + { + for(unsigned int i = 0; i < modes[active_mode].colors.size(); i++) + { + current_colors[i] = modes[active_mode].colors[i]; + } + + for(unsigned int i = 0; i < modes[active_mode].colors.size(); i++) + { + /*-----------------------------------------*\ + | This device uses 4bit colorValues (0-16) | + \*-----------------------------------------*/ + uint8_t red = RGBGetRValue(modes[active_mode].colors[i]) / 16; + uint8_t green = RGBGetGValue(modes[active_mode].colors[i]) / 16; + uint8_t blue = RGBGetBValue(modes[active_mode].colors[i]) / 16; + + controller->SetColor(i, red, green, blue); + } + } + + /*------------------------------------------------------------------------------------------------------*\ + | This device uses a global color storage for 7 colors, | + | each time a mode is selected, it is specified which color is chosen (some use all). | + | As OpenRGB doesn't support selecting the color index, color 1 is used for single color modes. | + \*------------------------------------------------------------------------------------------------------*/ + + uint8_t colorIndex = 0; + + uint8_t mode = modes[active_mode].value; + + if(mode == SAGARIS_KEYBOARD_MODE_STARRY_NIGHT) + { + colorIndex = 7; + } + + controller->SetMode(mode, modes[active_mode].brightness, modes[active_mode].speed, colorIndex); +} diff --git a/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/RGBController_AsusSagarisKeyboard.h b/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/RGBController_AsusSagarisKeyboard.h new file mode 100644 index 0000000..0a11ba3 --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/RGBController_AsusSagarisKeyboard.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusSagarisKeyboard.h | +| | +| RGBController for ASUS Sagaris keyboard | +| | +| Mola19 20 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusSagarisKeyboardController.h" + +class RGBController_AsusSagarisKeyboard : public RGBController +{ +public: + RGBController_AsusSagarisKeyboard(AsusSagarisKeyboardController* controller_ptr); + ~RGBController_AsusSagarisKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AsusSagarisKeyboardController* controller; + std::vector current_colors; + uint8_t last_mode; +}; diff --git a/Controllers/AsusLegacyUSBController/AsusStrixClawController/AsusStrixClawController.cpp b/Controllers/AsusLegacyUSBController/AsusStrixClawController/AsusStrixClawController.cpp new file mode 100644 index 0000000..c29076c --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusStrixClawController/AsusStrixClawController.cpp @@ -0,0 +1,102 @@ +/*---------------------------------------------------------*\ +| AsusStrixClawController.cpp | +| | +| Driver for ASUS Strix Claw mouse | +| | +| Mola19 06 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "AsusStrixClawController.h" +#include "StringUtils.h" + +StrixClawController::StrixClawController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +StrixClawController::~StrixClawController() +{ + hid_close(dev); +} + +std::string StrixClawController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string StrixClawController::GetDeviceName() +{ + return(name); +} + +std::string StrixClawController::GetSerialString() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string StrixClawController::GetVersion() +{ + + // asking the device to prepare version information + unsigned char usb_buf_out[9]; + + memset(usb_buf_out, 0x00, 9); + + usb_buf_out[0x00] = 0x07; + usb_buf_out[0x01] = 0x80; + + hid_send_feature_report(dev, usb_buf_out, 9); + + // retrieving the version information + unsigned char usb_buf_in[9]; + + memset(usb_buf_in, 0x00, 9); + + usb_buf_in[0x00] = 0x07; + + hid_get_feature_report(dev, usb_buf_in, 9); + + return (std::to_string(usb_buf_in[1]) + std::to_string(usb_buf_in[2])); +} + +void StrixClawController::SetScrollWheelLED(bool OnOff) +{ + unsigned char usb_buf[9]; + + memset(usb_buf, 0x00, 9); + + usb_buf[0x00] = 0x07; + usb_buf[0x01] = 0x07; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = OnOff; + hid_send_feature_report(dev, usb_buf, 9); +} + +void StrixClawController::SetLogoLED(uint8_t brightness) +{ + unsigned char usb_buf[9]; + + memset(usb_buf, 0x00, 9); + + usb_buf[0x00] = 0x07; + usb_buf[0x01] = 0x0a; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = brightness; + hid_send_feature_report(dev, usb_buf, 9); +} diff --git a/Controllers/AsusLegacyUSBController/AsusStrixClawController/AsusStrixClawController.h b/Controllers/AsusLegacyUSBController/AsusStrixClawController/AsusStrixClawController.h new file mode 100644 index 0000000..7ac544e --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusStrixClawController/AsusStrixClawController.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| AsusStrixClawController.h | +| | +| Driver for ASUS Strix Claw mouse | +| | +| Mola19 06 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define HID_MAX_STR 255 + +class StrixClawController +{ +public: + StrixClawController(hid_device* dev_handle, const char* path, std::string dev_name); + virtual ~StrixClawController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + std::string GetVersion(); + + void SetScrollWheelLED(bool OnOff); + void SetLogoLED(uint8_t brightness); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/AsusLegacyUSBController/AsusStrixClawController/RGBController_AsusStrixClaw.cpp b/Controllers/AsusLegacyUSBController/AsusStrixClawController/RGBController_AsusStrixClaw.cpp new file mode 100644 index 0000000..4892273 --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusStrixClawController/RGBController_AsusStrixClaw.cpp @@ -0,0 +1,134 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusStrixClaw.cpp | +| | +| RGBController for ASUS Strix Claw mouse | +| | +| Mola19 06 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusStrixClaw.h" + +/**------------------------------------------------------------------*\ + @name Asus Strix Claw + @category Mouse + @type USB + @save :robot: + @direct :x: + @effects :tools: + @detectors DetectAsusStrixClaw + @comment +\*-------------------------------------------------------------------*/ + +RGBController_StrixClaw::RGBController_StrixClaw(StrixClawController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "ASUS"; + type = DEVICE_TYPE_MOUSE; + description = "ASUS Legacy Mouse Device"; + version = controller->GetVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Off; + Off.name = "Off"; + Off.value = 0; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode On; + On.name = "On"; + On.value = 1; + On.flags = MODE_FLAG_AUTOMATIC_SAVE; + On.color_mode = MODE_COLORS_NONE; + modes.push_back(On); + + SetupZones(); +} + +RGBController_StrixClaw::~RGBController_StrixClaw() +{ + delete controller; +} + +void RGBController_StrixClaw::SetupZones() +{ + zone scroll_wheel_zone; + + scroll_wheel_zone.name = "Scroll Wheel"; + scroll_wheel_zone.type = ZONE_TYPE_SINGLE; + scroll_wheel_zone.leds_min = 1; + scroll_wheel_zone.leds_max = 1; + scroll_wheel_zone.leds_count = 1; + scroll_wheel_zone.matrix_map = NULL; + + zones.push_back(scroll_wheel_zone); + + led scroll_wheel_led; + + scroll_wheel_led.name = "Scroll Wheel LED"; + scroll_wheel_led.value = 1; + + leds.push_back(scroll_wheel_led); + + zone logo_zone; + + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + + zones.push_back(logo_zone); + + led logo_led; + + logo_led.name = "Logo LED"; + logo_led.value = 1; + + leds.push_back(logo_led); + + SetupColors(); +} + +void RGBController_StrixClaw::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_StrixClaw::DeviceUpdateLEDs() +{ + +} + +void RGBController_StrixClaw::UpdateZoneLEDs(int /*zone*/) +{ + +} + +void RGBController_StrixClaw::UpdateSingleLED(int /*led*/) +{ + +} + +void RGBController_StrixClaw::DeviceUpdateMode() +{ + if(modes[active_mode].value == 0) + { + controller->SetScrollWheelLED(false); + controller->SetLogoLED(0); + + } + else if(modes[active_mode].value == 1) + { + controller->SetScrollWheelLED(true); + controller->SetLogoLED(255); + } +} + diff --git a/Controllers/AsusLegacyUSBController/AsusStrixClawController/RGBController_AsusStrixClaw.h b/Controllers/AsusLegacyUSBController/AsusStrixClawController/RGBController_AsusStrixClaw.h new file mode 100644 index 0000000..37d4357 --- /dev/null +++ b/Controllers/AsusLegacyUSBController/AsusStrixClawController/RGBController_AsusStrixClaw.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusStrixClaw.h | +| | +| RGBController for ASUS Strix Claw mouse | +| | +| Mola19 06 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusStrixClawController.h" + +class RGBController_StrixClaw : public RGBController +{ +public: + RGBController_StrixClaw(StrixClawController* controller_ptr); + ~RGBController_StrixClaw(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + StrixClawController* controller; +}; diff --git a/Controllers/AsusMonitorController/AsusMonitorController.cpp b/Controllers/AsusMonitorController/AsusMonitorController.cpp new file mode 100644 index 0000000..e2ff3e9 --- /dev/null +++ b/Controllers/AsusMonitorController/AsusMonitorController.cpp @@ -0,0 +1,102 @@ +/*---------------------------------------------------------*\ +| AsusMonitorController.cpp | +| | +| Driver for Asus monitors | +| | +| Morgan Guimard (morg) 19 oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusMonitorController.h" +#include "StringUtils.h" + +AsusMonitorController::AsusMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +AsusMonitorController::~AsusMonitorController() +{ + hid_close(dev); +} + +std::string AsusMonitorController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string AsusMonitorController::GetNameString() +{ + return(name); +} + +std::string AsusMonitorController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned int AsusMonitorController::GetNumberOfLEDs() +{ + uint8_t usb_buf[ASUS_MONITOR_REPORT_SIZE]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = 0xB0; + + hid_write(dev, usb_buf, sizeof(usb_buf)); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + int bytes = hid_read(dev, usb_buf, sizeof(usb_buf)); + + return bytes > 0 ? usb_buf[32] : 0; +} + +void AsusMonitorController::SendInit() +{ + uint8_t usb_buf[ASUS_MONITOR_REPORT_SIZE]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = 0x35; + usb_buf[0x05] = 0xFF; + usb_buf[0x08] = 0x01; + + hid_write(dev, usb_buf, sizeof(usb_buf)); +} + +void AsusMonitorController::SetDirect(std::vector colors) +{ + uint8_t usb_buf[ASUS_MONITOR_REPORT_SIZE]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0xEC; + usb_buf[0x01] = 0x40; + usb_buf[0x02] = 0x84; + usb_buf[0x04] = (uint8_t)colors.size(); + + for(size_t i = 0; i < colors.size(); i++) + { + usb_buf[0x05 + (3 * i)] = RGBGetRValue(colors[i]); + usb_buf[0x05 + (3 * i + 1)] = RGBGetGValue(colors[i]); + usb_buf[0x05 + (3 * i + 2)] = RGBGetBValue(colors[i]); + } + + hid_write(dev, usb_buf, sizeof(usb_buf)); +} diff --git a/Controllers/AsusMonitorController/AsusMonitorController.h b/Controllers/AsusMonitorController/AsusMonitorController.h new file mode 100644 index 0000000..891b862 --- /dev/null +++ b/Controllers/AsusMonitorController/AsusMonitorController.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| AsusMonitorController.h | +| | +| Driver for Asus monitors | +| | +| Morgan Guimard (morg) 19 oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define ASUS_MONITOR_REPORT_SIZE 65 + +class AsusMonitorController +{ +public: + AsusMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~AsusMonitorController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + unsigned int GetNumberOfLEDs(); + void SetDirect(std::vector colors); + void SendInit(); + +protected: + hid_device* dev; + +private: + std::string location; + std::string name; +}; diff --git a/Controllers/AsusMonitorController/AsusMonitorControllerDetect.cpp b/Controllers/AsusMonitorController/AsusMonitorControllerDetect.cpp new file mode 100644 index 0000000..0cfdfa7 --- /dev/null +++ b/Controllers/AsusMonitorController/AsusMonitorControllerDetect.cpp @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| AsusMonitorControllerDetect.cpp | +| | +| Detector for Asus monitors | +| | +| Morgan Guimard (morg) 19 oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AsusMonitorController.h" +#include "RGBController_AsusMonitor.h" + +/*---------------------------------------------------------*\ +| Asus vendor ID | +\*---------------------------------------------------------*/ +#define ASUS_VID 0x0B05 + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define ASUS_ROG_STRIX_XG27AQDMG_PID 0x1BA3 +#define ASUS_ROG_SWIFT_XG27UCG_PID 0x1BB4 +#define ASUS_ROG_SWIFT_PG32UCDM_PID 0x1B2B + +void DetectAsusMonitorControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + AsusMonitorController* controller = new AsusMonitorController(dev, *info, name); + RGBController_AsusMonitor* rgb_controller = new RGBController_AsusMonitor(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Asus ROG STRIX XG27AQDMG", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_STRIX_XG27AQDMG_PID, 1, 0xFF72, 0x00A1); +REGISTER_HID_DETECTOR_IPU("Asus ROG STRIX XG27UCG", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_SWIFT_XG27UCG_PID, 1, 0xFF72, 0x00A1); +REGISTER_HID_DETECTOR_IPU("Asus ROG STRIX PG32UCDM", DetectAsusMonitorControllers, ASUS_VID, ASUS_ROG_SWIFT_PG32UCDM_PID, 1, 0xFF72, 0x00A1); diff --git a/Controllers/AsusMonitorController/RGBController_AsusMonitor.cpp b/Controllers/AsusMonitorController/RGBController_AsusMonitor.cpp new file mode 100644 index 0000000..9571640 --- /dev/null +++ b/Controllers/AsusMonitorController/RGBController_AsusMonitor.cpp @@ -0,0 +1,102 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusMonitor.cpp | +| | +| RGBController for Asus monitors | +| | +| Morgan Guimard (morg) 19 oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusMonitor.h" + +/**------------------------------------------------------------------*\ + @name Asus monitors + @category Monitor + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectAsusMonitorControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AsusMonitor::RGBController_AsusMonitor(AsusMonitorController* controller_ptr) +{ + controller = controller_ptr; + name = controller->GetNameString(); + vendor = "ASUS"; + type = DEVICE_TYPE_MONITOR; + description = "ASUS monitor"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + number_of_leds = controller->GetNumberOfLEDs(); + + controller->SendInit(); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_AsusMonitor::~RGBController_AsusMonitor() +{ + delete controller; +} + +void RGBController_AsusMonitor::SetupZones() +{ + zone new_zone; + + new_zone.name = "Monitor"; + new_zone.type = ZONE_TYPE_LINEAR; + + new_zone.leds_min = number_of_leds; + new_zone.leds_max = number_of_leds; + new_zone.leds_count = number_of_leds; + + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(new_zone.leds_count); + + for(unsigned int i = 0; i < number_of_leds; i++) + { + leds[i].name = "LED " + std::to_string(i); + } + + SetupColors(); +} + +void RGBController_AsusMonitor::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AsusMonitor::DeviceUpdateLEDs() +{ + controller->SetDirect(colors); +} + +void RGBController_AsusMonitor::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AsusMonitor::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AsusMonitor::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/AsusMonitorController/RGBController_AsusMonitor.h b/Controllers/AsusMonitorController/RGBController_AsusMonitor.h new file mode 100644 index 0000000..478231b --- /dev/null +++ b/Controllers/AsusMonitorController/RGBController_AsusMonitor.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusMonitor.h | +| | +| RGBController for Asus monitors | +| | +| Morgan Guimard (morg) 19 oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusMonitorController.h" + +class RGBController_AsusMonitor : public RGBController +{ +public: + RGBController_AsusMonitor(AsusMonitorController* controller_ptr); + ~RGBController_AsusMonitor(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + AsusMonitorController* controller; + unsigned int number_of_leds; +}; diff --git a/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Linux.cpp b/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Linux.cpp new file mode 100644 index 0000000..c926f07 --- /dev/null +++ b/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Linux.cpp @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| AsusTUFLaptopController_Linux.cpp | +| | +| Driver for ASUS TUF laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "AsusTUFLaptopController_Linux.h" + +void AsusTUFLaptopLinuxController::SendUpdate + ( + unsigned char mode, + unsigned char speed, + unsigned char save, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + std::string s = ""; + s.append(ASUS_KBD_BACKLIGHT_BASE_PATH); + s.append(ASUS_KBD_BACKLIGHT_MODE_PATH); + FILE *controller = fopen(s.c_str(), "w"); + + s = ""; + s.append(std::to_string(save)); + s.append(" "); + s.append(std::to_string(mode)); + s.append(" "); + s.append(std::to_string(red)); + s.append(" "); + s.append(std::to_string(green)); + s.append(" "); + s.append(std::to_string(blue)); + s.append(" "); + s.append(std::to_string(speed)); + + fputs(s.c_str(), controller); + + fclose(controller); +} + +void AsusTUFLaptopLinuxController::SendBrightness + ( + unsigned char brightness + ) +{ + std::string s = ""; + s.append(ASUS_KBD_BACKLIGHT_BASE_PATH); + s.append(ASUS_KBD_BACKLIGHT_BRIGHTNESS_PATH); + FILE *controller = fopen(s.c_str(), "w"); + + fputs(std::to_string(brightness).c_str(), controller); + + fclose(controller); +} diff --git a/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Linux.h b/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Linux.h new file mode 100644 index 0000000..404e75d --- /dev/null +++ b/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Linux.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| AsusTUFLaptopController_Linux.h | +| | +| Driver for ASUS TUF laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" + +#define ASUS_KBD_BACKLIGHT_BRIGHTNESS_MIN 0 +#define ASUS_KBD_BACKLIGHT_BRIGHTNESS_MAX 3 +#define ASUS_KBD_BACKLIGHT_BRIGHTNESS 3 + +#define ASUS_KBD_BACKLIGHT_SPEED_MIN 0 +#define ASUS_KBD_BACKLIGHT_SPEED_MAX 2 +#define ASUS_KBD_BACKLIGHT_SPEED 1 + +#define ASUS_KBD_BACKLIGHT_BASE_PATH "/sys/devices/platform/asus-nb-wmi/leds/asus::kbd_backlight" +#define ASUS_KBD_BACKLIGHT_MODE_PATH "/kbd_rgb_mode" +#define ASUS_KBD_BACKLIGHT_BRIGHTNESS_PATH "/brightness" + +class AsusTUFLaptopLinuxController +{ +public: + void SendBrightness + ( + unsigned char brightness + ); + + void SendUpdate + ( + unsigned char mode, + unsigned char speed, + unsigned char save, + unsigned char red, + unsigned char green, + unsigned char blue + ); +}; diff --git a/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Windows.cpp b/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Windows.cpp new file mode 100644 index 0000000..2b1656e --- /dev/null +++ b/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Windows.cpp @@ -0,0 +1,343 @@ +/*---------------------------------------------------------*\ +| AsusTUFLaptopController_Windows.cpp | +| | +| Driver for ASUS TUF laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include "AsusTUFLaptopController_Windows.h" + +static bool coInitialized = 0; + +static GUID CLSID_GUID_DEVCLASS_SYSTEM = { 0x4D36E97D, 0xE325, 0x11CE, {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } }; + +int AsusTUFLaptopController::checkWMIType() +{ + int n; + int result = 0; + struct _SP_DEVINFO_DATA DeviceInfoData; + const int bufsize = 260; + wchar_t PropertyBuffer[bufsize]; + + HDEVINFO devinfo = SetupDiGetClassDevsW(&CLSID_GUID_DEVCLASS_SYSTEM, 0, 0, 2u); + + if(devinfo == HDEVINFO(-1)) + { + return 0; + } + + n = 0; + DeviceInfoData.cbSize = sizeof(DeviceInfoData); + + while(SetupDiEnumDeviceInfo(devinfo, n, &DeviceInfoData)) // Invalid buffer + { + if(SetupDiGetDeviceRegistryPropertyW(devinfo, + &DeviceInfoData, + SPDRP_ENUMERATOR_NAME, + NULL, + PBYTE(PropertyBuffer), + sizeof(PropertyBuffer), + 0)) + { + // If we found property "ACPI" + if(!wcscmp(PropertyBuffer, L"ACPI")) + { + memset(PropertyBuffer, 0, sizeof(PropertyBuffer)); + if(SetupDiGetDeviceInstanceIdW(devinfo, &DeviceInfoData, PropertyBuffer, bufsize, 0)) + { + _wcsupr_s(PropertyBuffer, bufsize); + if(wcsstr(PropertyBuffer, L"ACPI\\ATK0100")) + { + result = 1; + break; + } + if(!wcscmp(PropertyBuffer, L"ACPI\\PNP0C14\\ATK")) + { + result = 2; + break; + } + } + } + } + + n++; + } + SetupDiDestroyDeviceInfoList(devinfo); + + return(result); +} + +AsusTUFLaptopController::AsusTUFLaptopController() +{ + hDevice = CreateFileW(L"\\\\.\\ATKACPI", 0xC0000000, 3u, 0, 3u, 0, 0); +} + +AsusTUFLaptopController* AsusTUFLaptopController::checkAndCreate() +{ + // This might cause issues when coInitialize() is used in multiple places + HRESULT init = CoInitializeEx(0, COINIT_APARTMENTTHREADED); + + if(init < 0 && init != 0x80010106) + { + return(0); + } + + coInitialized = 1; + + int type = checkWMIType(); + if(type == 2) + { + AsusTUFLaptopController* controller = new AsusTUFLaptopController(); + if(controller->hDevice != HANDLE(-1)) + { + return(controller); + } + delete controller; + } + + return(nullptr); +} + +AsusTUFLaptopController::~AsusTUFLaptopController() +{ + if(hDevice && hDevice != HANDLE(-1)) + { + CloseHandle(hDevice); + hDevice = 0; + } + + /*-----------------------------------------------------*\ + | This might cause issues when coInitialize() is used | + | in multiple places | + \*-----------------------------------------------------*/ + if(coInitialized) + { + CoUninitialize(); + coInitialized = 0; + } +} + +bool AsusTUFLaptopController::deviceIoControlWrapper(const void *dataIn, int commandIndex, int dataSizeIn, void *dataOut, int *dataSizeOut) +{ + size_t BytesReturned; + const int bufsize = 1024; + char outBuffer[bufsize]; + + LPDWORD inBuffer = LPDWORD(malloc(dataSizeIn + 8)); + inBuffer[0] = commandIndex; + inBuffer[1] = dataSizeIn; + memmove(inBuffer + 2, dataIn, dataSizeIn); + memset(outBuffer, 0, bufsize); + BytesReturned = 0; + bool result = DeviceIoControl( + hDevice, + 0x22240Cu, + inBuffer, + dataSizeIn + 8, + outBuffer, + bufsize, + LPDWORD(&BytesReturned), + 0); + if(result) + { + if((size_t)*dataSizeOut < BytesReturned) + { + BytesReturned = (size_t)*dataSizeOut; + } + memmove(dataOut, outBuffer, BytesReturned); + } + free(inBuffer); + + return(result); +} + +bool AsusTUFLaptopController::deviceControl(int a1, int a2) +{ + if(hDevice && hDevice != HANDLE(-1)) + { + int data[2]; + data[0] = a1; + data[1] = a2; + int result; + int outBufSize = 4; + + if(deviceIoControlWrapper(&data, 1398162756, 8, &result, &outBufSize)) + { + if(outBufSize < 4) + { + result = 0; + } + if(result == 1) + { + return(1); + } + } + } + return(0); +} + +bool AsusTUFLaptopController::deviceControl(int a1, int a2, int a3) +{ + unsigned int data[3]; + data[0] = a1; + data[1] = a2; + data[2] = a3; + int outBuf; + int outBufSize = 4; + + if(hDevice && hDevice != HANDLE(-1)) + { + if(deviceIoControlWrapper(data, 0x53564544, 12, &outBuf, &outBufSize)) + { + if(outBufSize < 4) + { + outBuf = 0; + } + if(outBuf == 1) + { + return(1); + } + } + } + return(0); +} + +bool AsusTUFLaptopController::getStatus(int a1, int *out) +{ + int status; + int statusSize = 4; + + if(!hDevice || hDevice == HANDLE(-1) || (!deviceIoControlWrapper(&a1, 1398035268, 4, &status, &statusSize))) + { + return(0); + } + if(statusSize < 4) + { + status = 0; + } + + *out = status; + + return(1); +} + +bool AsusTUFLaptopController::getStatusExtended(int a1, int a2, int *status1, int *status2, int* status3) +{ + int commandData[2]; + commandData[0] = a1; + commandData[1] = a2; + int statusBuffer[3]; + int statusSize = 12; + + if(hDevice && hDevice != HANDLE(-1) && deviceIoControlWrapper(commandData, 1398035268, 8, statusBuffer, &statusSize)) + { + *status1 = statusBuffer[0]; + *status2 = statusBuffer[1]; + *status3 = statusBuffer[2]; + + return(1); + } + else + { + return(0); + } +} + +void AsusTUFLaptopController::setMode(unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char mode, + unsigned char speed, + bool save) +{ + /*--------------------------------------------------------*\ + | Use switch case since our speed values are magic numbers | + | Default to Medium/Normal speed | + \*--------------------------------------------------------*/ + unsigned char speed_val; + + switch(speed) + { + case(1): + speed_val = ASUS_WMI_KEYBOARD_SPEED_SLOW; + break; + default: + case(2): + speed_val = ASUS_WMI_KEYBOARD_SPEED_NORMAL; + break; + case(3): + speed_val = ASUS_WMI_KEYBOARD_SPEED_FAST; + break; + } + + /*----------------------------------------------------------*\ + | We need to use a magic value to save to firmware in order | + | To persist reboots. Save is normal op with different magic | + \*----------------------------------------------------------*/ + unsigned char save_val = ASUS_WMI_KEYBOARD_MAGIC_USE; + + if(save) + { + save_val = ASUS_WMI_KEYBOARD_MAGIC_SAVE; + } + + // B3 is store value + unsigned int high = save_val | (mode<<8) | (red<<16) | (green<<24); + unsigned int low = blue | (speed_val<<8); + + deviceControl(ASUS_WMI_DEVID_TUF_RGB_MODE, high, low); +} + +unsigned char AsusTUFLaptopController::getBrightness() +{ + int backlight_state = 0; + getStatus(ASUS_WMI_DEVID_KBD_BACKLIGHT, &backlight_state); + + /*-----------------------------------------------------*\ + | Only lowest two bits indicate brightness level | + \*-----------------------------------------------------*/ + return backlight_state & 0x7F; +} + +void AsusTUFLaptopController::setBrightness(unsigned char brightness) +{ + /*-----------------------------------------------------*\ + | Only calls in this format persistently set brightness | + \*-----------------------------------------------------*/ + int ctrl_param = 0x80 | (brightness & 0x7F); + deviceControl(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param); +} + +/*-----------------------------------------------------------*\ +| These settings will not persist a reboot unless save is set | +\*-----------------------------------------------------------*/ +void AsusTUFLaptopController::setPowerState(bool boot, + bool awake, + bool sleep, + bool shutdown, + bool save) +{ + unsigned int state = 0xBD; + + if(boot) state = state | ASUS_WMI_KEYBOARD_POWER_BOOT; + if(awake) state = state | ASUS_WMI_KEYBOARD_POWER_AWAKE; + if(sleep) state = state | ASUS_WMI_KEYBOARD_POWER_SLEEP; + if(shutdown) state = state | ASUS_WMI_KEYBOARD_POWER_SHUTDOWN; + if(save) state = state | ASUS_WMI_KEYBOARD_POWER_SAVE; + + deviceControl(ASUS_WMI_DEVID_TUF_RGB_STATE, state); +} + +void AsusTUFLaptopController::setFanMode(int mode) +{ + deviceControl(ASUS_WMI_DEVID_FAN_BOOST_MODE, mode); +} diff --git a/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Windows.h b/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Windows.h new file mode 100644 index 0000000..ac5d3e3 --- /dev/null +++ b/Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Windows.h @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| AsusTUFLaptopController_Windows.h | +| | +| Driver for ASUS TUF laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 +#define ASUS_WMI_DEVID_TUF_RGB_MODE 0x00100056 +#define ASUS_WMI_DEVID_TUF_RGB_STATE 0x00100057 + +#define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018 +#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075 + +#define ASUS_WMI_KEYBOARD_SPEED_SLOW 0xE1 +#define ASUS_WMI_KEYBOARD_SPEED_NORMAL 0xEB +#define ASUS_WMI_KEYBOARD_SPEED_FAST 0xF5 + +#define ASUS_WMI_KEYBOARD_SPEED_MIN 1 +#define ASUS_WMI_KEYBOARD_SPEED_MAX 3 + +#define ASUS_WMI_KEYBOARD_MODE_STATIC 0x00 +#define ASUS_WMI_KEYBOARD_MODE_BREATHING 0x01 +#define ASUS_WMI_KEYBOARD_MODE_COLORCYCLE 0x02 +#define ASUS_WMI_KEYBOARD_MODE_STROBING 0x0A + +#define ASUS_WMI_KEYBOARD_BRIGHTNESS_MIN 0 +#define ASUS_WMI_KEYBOARD_BRIGHTNESS_MAX 3 + +#define ASUS_WMI_KEYBOARD_MAGIC_USE 0xB3 +#define ASUS_WMI_KEYBOARD_MAGIC_SAVE 0xB4 + +#define ASUS_WMI_KEYBOARD_POWER_BOOT 0x03<<16 +#define ASUS_WMI_KEYBOARD_POWER_AWAKE 0x0C<<16 +#define ASUS_WMI_KEYBOARD_POWER_SLEEP 0x30<<16 +#define ASUS_WMI_KEYBOARD_POWER_SHUTDOWN 0xC0<<16 + +#define ASUS_WMI_KEYBOARD_POWER_SAVE 0x01<<8 + +#define ASUS_WMI_FAN_SPEED_NORMAL 0 +#define ASUS_WMI_FAN_SPEED_TURBO 1 +#define ASUS_WMI_FAN_SPEED_SILENT 2 + +class AsusTUFLaptopController +{ +private: + HANDLE hDevice; + static int checkWMIType(); + AsusTUFLaptopController(); + + bool deviceIoControlWrapper(const void *dataIn, int commandIndex, int dataSizeIn, void *dataOut, int *dataSizeOut); + bool deviceControl(int a1, int a2); + bool deviceControl(int a1, int a2, int a3); + bool getStatus(int a1, int *out); + bool getStatusExtended(int a1, int a2, int *status1, int *status2, int* status3); + +public: + static AsusTUFLaptopController * checkAndCreate(); + ~AsusTUFLaptopController(); + + void setMode(unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char mode, + unsigned char speed, + bool save); + + unsigned char getBrightness(); + void setBrightness(unsigned char brightness); + + void setPowerState(bool boot, + bool awake, + bool sleep, + bool shutdown, + bool save); + + void setFanMode(int mode); +}; diff --git a/Controllers/AsusTUFLaptopController/AsusTUFLaptopDetect_Linux.cpp b/Controllers/AsusTUFLaptopController/AsusTUFLaptopDetect_Linux.cpp new file mode 100644 index 0000000..c97e9f6 --- /dev/null +++ b/Controllers/AsusTUFLaptopController/AsusTUFLaptopDetect_Linux.cpp @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| AsusTUFLaptopDetect_Linux.cpp | +| | +| Detector for ASUS TUF laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "RGBController_AsusTUFLaptop_Linux.h" + +static void DetectAsusTUFLaptopLinuxControllers() +{ + /*-------------------------------------------------------------------------------------*\ + | If /sys/devices/platform/asus-nb-wmi/leds/asus::kbd_backlight/kbd_rgb_mode exists, | + | the kernel support TUF Laptop keyboard LED controlling. | + \*-------------------------------------------------------------------------------------*/ + + std::string s = ""; + s.append(ASUS_KBD_BACKLIGHT_BASE_PATH); + s.append(ASUS_KBD_BACKLIGHT_MODE_PATH); + + if(!access(s.c_str(), F_OK)) + { + AsusTUFLaptopLinuxController* controller = new AsusTUFLaptopLinuxController(); + RGBController_AsusTUFLaptopLinux* rgb_controller = new RGBController_AsusTUFLaptopLinux(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + return; +} + +REGISTER_DETECTOR("ASUS TUF Laptop", DetectAsusTUFLaptopLinuxControllers); diff --git a/Controllers/AsusTUFLaptopController/AsusTUFLaptopDetect_Windows.cpp b/Controllers/AsusTUFLaptopController/AsusTUFLaptopDetect_Windows.cpp new file mode 100644 index 0000000..0b231f7 --- /dev/null +++ b/Controllers/AsusTUFLaptopController/AsusTUFLaptopDetect_Windows.cpp @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| AsusTUFLaptopDetect_Windows.cpp | +| | +| Detector for ASUS TUF laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "RGBController_AsusTUFLaptop_Windows.h" +#include "wmi.h" + +static void DetectAsusTUFLaptopWMIControllers() +{ + // Try to retrieve ProductID / Device name from WMI; Possibly can be rewritten to use wmi.cpp + // IF you encounter false detection ( e.g. if your laptop keyboard backlight uses USB interface + // instead of ACPI WMI) please add a WHITELIST by checking the + // `name` variable for model substrings like "FX505DU" + // For now, checking for "TUF Gaming" should suffice + + Wmi wmi; + + std::vector systemProduct; + if (wmi.query("SELECT * FROM Win32_ComputerSystemProduct", systemProduct)) + { + return; + } + + // There should only be one, a cycle is a precaution + if(systemProduct.size() != 1) + { + return; + } + std::string& name = systemProduct[0]["Name"]; + + if(name.find("TUF Gaming") == name.npos) + { + return; + } + + AsusTUFLaptopController* controller = AsusTUFLaptopController::checkAndCreate(); + if(controller) + { + RGBController* new_controller = new RGBController_AsusTUFLaptopWMI(controller); + + ResourceManager::get()->RegisterRGBController(new_controller); + } +} /* DetectAsusTUFLaptopWMIControllers() */ + +REGISTER_DETECTOR("ASUS TUF Laptop", DetectAsusTUFLaptopWMIControllers); diff --git a/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Linux.cpp b/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Linux.cpp new file mode 100644 index 0000000..56d15a3 --- /dev/null +++ b/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Linux.cpp @@ -0,0 +1,159 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusTUFLaptop_Linux.cpp | +| | +| RGBController for ASUS TUF laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusTUFLaptop_Linux.h" + +/**------------------------------------------------------------------*\ + @name Asus TUF Laptop Linux WMI + @category Keyboard + @type File Stream + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusTUFLaptopLinuxControllers + @comment Tested on ASUS TUF Gaming A15 2022 + PLEASE UPDATE YOUR KERNEL TO A VERSION NEWER THAN 6.1.0 + + Every devices supported by asus-wmi would work technically. +\*-------------------------------------------------------------------*/ + +RGBController_AsusTUFLaptopLinux::RGBController_AsusTUFLaptopLinux(AsusTUFLaptopLinuxController* controller_ptr) +{ + controller = controller_ptr; + + name = "ASUS TUF Laptop Keyboard"; + vendor = "ASUS"; + type = DEVICE_TYPE_LAPTOP; + description = "Asus TUF Device"; + location = ASUS_KBD_BACKLIGHT_BASE_PATH; + + mode Direct; + Direct.name = "Direct"; + Direct.value = 4; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = ASUS_KBD_BACKLIGHT_BRIGHTNESS_MIN; + Direct.brightness_max = ASUS_KBD_BACKLIGHT_BRIGHTNESS_MAX; + Direct.brightness = ASUS_KBD_BACKLIGHT_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = 0; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.brightness_min = ASUS_KBD_BACKLIGHT_BRIGHTNESS_MIN; + Static.brightness_max = ASUS_KBD_BACKLIGHT_BRIGHTNESS_MAX; + Static.brightness = ASUS_KBD_BACKLIGHT_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = 1; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.speed_min = ASUS_KBD_BACKLIGHT_SPEED_MIN; + Breathing.speed_max = ASUS_KBD_BACKLIGHT_SPEED_MAX; + Breathing.brightness_min = ASUS_KBD_BACKLIGHT_BRIGHTNESS_MIN; + Breathing.brightness_max = ASUS_KBD_BACKLIGHT_BRIGHTNESS_MAX; + Breathing.brightness = ASUS_KBD_BACKLIGHT_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed = ASUS_KBD_BACKLIGHT_SPEED; + modes.push_back(Breathing); + + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = 2; + Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.speed_min = ASUS_KBD_BACKLIGHT_SPEED_MIN; + Cycle.speed_max = ASUS_KBD_BACKLIGHT_SPEED_MAX; + Cycle.brightness_min = ASUS_KBD_BACKLIGHT_BRIGHTNESS_MIN; + Cycle.brightness_max = ASUS_KBD_BACKLIGHT_BRIGHTNESS_MAX; + Cycle.brightness = ASUS_KBD_BACKLIGHT_BRIGHTNESS; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed = ASUS_KBD_BACKLIGHT_SPEED; + modes.push_back(Cycle); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = 0x0A; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Flashing.brightness_min = ASUS_KBD_BACKLIGHT_BRIGHTNESS_MIN; + Flashing.brightness_max = ASUS_KBD_BACKLIGHT_BRIGHTNESS_MAX; + Flashing.brightness = ASUS_KBD_BACKLIGHT_BRIGHTNESS; + Flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flashing); + + SetupZones(); +} + +void RGBController_AsusTUFLaptopLinux::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zones.resize(1); + zones[0].type = ZONE_TYPE_SINGLE; + zones[0].name = "Keyboard Backlight zone"; + zones[0].leds_min = 1; + zones[0].leds_max = 1; + zones[0].leds_count = 1; + zones[0].matrix_map = NULL; + + /*---------------------------------------------------------*\ + | Set up LED | + \*---------------------------------------------------------*/ + leds.resize(1); + leds[0].name = "Keyboard Backlight LED"; + + SetupColors(); +} + +void RGBController_AsusTUFLaptopLinux::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AsusTUFLaptopLinux::DeviceUpdateLEDs() +{ + uint8_t red = RGBGetRValue(colors[0]); + uint8_t green = RGBGetGValue(colors[0]); + uint8_t blue = RGBGetBValue(colors[0]); + uint8_t speed = 0; + uint8_t mode = modes[active_mode].value; + uint8_t save = 1; + if(mode == 4) + { + mode = 0; + save = 0; + } + if(mode == 1 || mode == 2) + { + speed = modes[active_mode].speed; + } + + controller->SendUpdate(mode, speed, save, red, green, blue); + controller->SendBrightness(modes[active_mode].brightness); +} + +void RGBController_AsusTUFLaptopLinux::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AsusTUFLaptopLinux::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_AsusTUFLaptopLinux::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Linux.h b/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Linux.h new file mode 100644 index 0000000..64c0da2 --- /dev/null +++ b/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Linux.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusTUFLaptop_Linux.h | +| | +| RGBController for ASUS TUF laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AsusTUFLaptopController_Linux.h" + +class RGBController_AsusTUFLaptopLinux : public RGBController +{ +public: + RGBController_AsusTUFLaptopLinux(AsusTUFLaptopLinuxController* controller_ptr); + + void SetupZones() override; + + void ResizeZone(int zone, int new_size) override; + + void DeviceUpdateLEDs() override; + void UpdateZoneLEDs(int zone) override; + void UpdateSingleLED(int led) override; + + void DeviceUpdateMode() override; + +private: + AsusTUFLaptopLinuxController* controller; +}; diff --git a/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Windows.cpp b/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Windows.cpp new file mode 100644 index 0000000..70a944c --- /dev/null +++ b/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Windows.cpp @@ -0,0 +1,181 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusTUFLaptop_Windows.cpp | +| | +| RGBController for ASUS TUF laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AsusTUFLaptop_Windows.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name Asus TUF Laptop + @category Keyboard + @type WMI + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectAsusTUFLaptopWMIControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AsusTUFLaptopWMI::RGBController_AsusTUFLaptopWMI(AsusTUFLaptopController* controller_ptr) +{ + name = "ASUS TUF Laptop Keyboard"; + vendor = "ASUS"; + type = DEVICE_TYPE_LAPTOP; + description = "WMI Device"; + location = "\\\\.\\ATKACPI"; + + mode Static; + Static.name = "Static"; + Static.value = ASUS_WMI_KEYBOARD_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_max = ASUS_WMI_KEYBOARD_BRIGHTNESS_MAX; + Static.brightness_min = ASUS_WMI_KEYBOARD_BRIGHTNESS_MIN; + Static.brightness = ASUS_WMI_KEYBOARD_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ASUS_WMI_KEYBOARD_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = ASUS_WMI_KEYBOARD_SPEED_MIN; + Breathing.speed_max = ASUS_WMI_KEYBOARD_SPEED_MAX; + Breathing.speed = 2; + Breathing.brightness_max = ASUS_WMI_KEYBOARD_BRIGHTNESS_MAX; + Breathing.brightness_min = ASUS_WMI_KEYBOARD_BRIGHTNESS_MIN; + Breathing.brightness = ASUS_WMI_KEYBOARD_BRIGHTNESS_MAX; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = ASUS_WMI_KEYBOARD_MODE_COLORCYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + ColorCycle.color_mode = MODE_COLORS_NONE; + ColorCycle.speed_min = ASUS_WMI_KEYBOARD_SPEED_MIN; + ColorCycle.speed_max = ASUS_WMI_KEYBOARD_SPEED_MAX; + ColorCycle.speed = 2; + ColorCycle.brightness_max = ASUS_WMI_KEYBOARD_BRIGHTNESS_MAX; + ColorCycle.brightness_min = ASUS_WMI_KEYBOARD_BRIGHTNESS_MIN; + ColorCycle.brightness = ASUS_WMI_KEYBOARD_BRIGHTNESS_MAX; + modes.push_back(ColorCycle); + + mode Strobing; + Strobing.name = "Strobing"; + Strobing.value = ASUS_WMI_KEYBOARD_MODE_STROBING; + Strobing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Strobing.color_mode = MODE_COLORS_PER_LED; + Strobing.brightness_max = ASUS_WMI_KEYBOARD_BRIGHTNESS_MAX; + Strobing.brightness_min = ASUS_WMI_KEYBOARD_BRIGHTNESS_MIN; + Strobing.brightness = ASUS_WMI_KEYBOARD_BRIGHTNESS_MAX; + modes.push_back(Strobing); + + SetupZones(); + + controller = controller_ptr; + + ReadConfiguration(); +} + +RGBController_AsusTUFLaptopWMI::~RGBController_AsusTUFLaptopWMI() +{ + delete controller; +} + +void RGBController_AsusTUFLaptopWMI::SetupZones() +{ + /*---------------------------------------------------------*\ + | Device only has one zone and one led | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->name = "Keyboard Backlight zone"; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = "Keyboard Backlight LED"; + + zones.push_back(*new_zone); + leds.push_back(*new_led); + + SetupColors(); +} + +void RGBController_AsusTUFLaptopWMI::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +/*---------------------------------------------------------*\ +| Break this function off since we have to call save in the | + same operation as doing everything else. | +\*---------------------------------------------------------*/ +void RGBController_AsusTUFLaptopWMI::ControllerSetMode(bool save) +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char green = RGBGetGValue(colors[0]); + unsigned char blue = RGBGetBValue(colors[0]); + + unsigned char mode = (unsigned char)modes[(unsigned int)active_mode].value; + + /*------------------------------------------------------------*\ + | Use speed only if the mode supports it. Otherwise set normal | + \*------------------------------------------------------------*/ + unsigned char speed = ASUS_WMI_KEYBOARD_SPEED_NORMAL; + + if (modes[(unsigned int)active_mode].flags & MODE_FLAG_HAS_SPEED) + { + speed = (unsigned char)modes[(unsigned int)active_mode].speed; + } + + controller->setMode(red, green, blue, mode, speed, save); +} + +void RGBController_AsusTUFLaptopWMI::DeviceUpdateLEDs() +{ + ControllerSetMode(false); +} + +void RGBController_AsusTUFLaptopWMI::UpdateZoneLEDs(int /*zone*/) +{ + ControllerSetMode(false); +} + +void RGBController_AsusTUFLaptopWMI::UpdateSingleLED(int /*led*/) +{ + ControllerSetMode(false); +} + +void RGBController_AsusTUFLaptopWMI::DeviceUpdateMode() +{ + if (modes[(unsigned int)active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + controller->setBrightness((unsigned char)modes[(unsigned int)active_mode].brightness); + } + ControllerSetMode(false); +} + +void RGBController_AsusTUFLaptopWMI::ReadConfiguration() +{ + if (modes[(unsigned int)active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + modes[(unsigned int)active_mode].brightness = controller->getBrightness(); + } +} + +void RGBController_AsusTUFLaptopWMI::DeviceSaveMode() +{ + ControllerSetMode(true); +} diff --git a/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Windows.h b/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Windows.h new file mode 100644 index 0000000..abec984 --- /dev/null +++ b/Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Windows.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_AsusTUFLaptop_Windows.h | +| | +| RGBController for ASUS TUF laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "AsusTUFLaptopController_Windows.h" +#include "RGBController.h" + +class RGBController_AsusTUFLaptopWMI : public RGBController +{ +public: + RGBController_AsusTUFLaptopWMI(AsusTUFLaptopController* controller_ptr); + virtual ~RGBController_AsusTUFLaptopWMI(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + AsusTUFLaptopController* controller; + + void ReadConfiguration(); + void ControllerSetMode(bool save); +}; diff --git a/Controllers/BlinkyTapeController/BlinkyTapeController.cpp b/Controllers/BlinkyTapeController/BlinkyTapeController.cpp new file mode 100644 index 0000000..675b135 --- /dev/null +++ b/Controllers/BlinkyTapeController/BlinkyTapeController.cpp @@ -0,0 +1,105 @@ +/*---------------------------------------------------------*\ +| BlinkyTapeController.cpp | +| | +| Driver for BlinkyTape | +| | +| Matt Mets (matt@blinkinlabs.com) 01 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "BlinkyTapeController.h" + +#ifndef WIN32 +#define LPSTR char * +#define strtok_s strtok_r +#endif + +BlinkyTapeController::BlinkyTapeController() +{ +} + +BlinkyTapeController::~BlinkyTapeController() +{ + if(serialport != nullptr) + { + serialport->serial_close(); + delete serialport; + } +} + +void BlinkyTapeController::Initialize(const std::string &portname) +{ + port_name = portname; + + serialport = new serial_port(); + + if(!serialport->serial_open(port_name.c_str(), 115200)) + { + delete serialport; + serialport = nullptr; + } +} + +std::string BlinkyTapeController::GetLocation() +{ + if(serialport == nullptr) + { + return(""); + } + + return("COM: " + port_name); +} + +char* BlinkyTapeController::GetLEDString() +{ + return(led_string); +} + +void BlinkyTapeController::SetLEDs(std::vector colors) +{ + if(serialport == nullptr) + { + return; + } + + /*-------------------------------------------------------------*\ + | BlinkyTape Protocol | + | | + | Packet size: Number of data bytes + 1 | + | | + | 0-n: Data Byte (0-254) | + | n+1: Packet End Byte (0xFF) | + \*-------------------------------------------------------------*/ + const unsigned int payload_size = ((unsigned int)colors.size() * 3); + const unsigned int packet_size = payload_size + 1; + + std::vector serial_buf(packet_size); + + /*-------------------------------------------------------------*\ + | Set up end byte | + \*-------------------------------------------------------------*/ + serial_buf[packet_size - 1] = 0xFF; + + /*-------------------------------------------------------------*\ + | Copy in color data in RGB order | + \*-------------------------------------------------------------*/ + for(unsigned int color_idx = 0; color_idx < colors.size(); color_idx++) + { + const unsigned int color_offset = color_idx * 3; + + serial_buf[0x00 + color_offset] = (unsigned char)std::min((unsigned int)254, RGBGetRValue(colors[color_idx])); + serial_buf[0x01 + color_offset] = (unsigned char)std::min((unsigned int)254, RGBGetGValue(colors[color_idx])); + serial_buf[0x02 + color_offset] = (unsigned char)std::min((unsigned int)254, RGBGetBValue(colors[color_idx])); + } + + /*-------------------------------------------------------------*\ + | Send the packet | + \*-------------------------------------------------------------*/ + serialport->serial_write((char *)serial_buf.data(), packet_size); +} diff --git a/Controllers/BlinkyTapeController/BlinkyTapeController.h b/Controllers/BlinkyTapeController/BlinkyTapeController.h new file mode 100644 index 0000000..86f8ae3 --- /dev/null +++ b/Controllers/BlinkyTapeController/BlinkyTapeController.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| BlinkyTapeController.h | +| | +| Driver for BlinkyTape | +| | +| Matt Mets (matt@blinkinlabs.com) 01 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "serial_port.h" + +struct BlinkyTapeDevice +{ + std::string port; + unsigned int num_leds; +}; + +class BlinkyTapeController +{ +public: + BlinkyTapeController(); + ~BlinkyTapeController(); + + void Initialize(const std::string &portname); + + char* GetLEDString(); + std::string GetLocation(); + + void SetLEDs(std::vector colors); + +private: + char led_string[1024]; + std::string port_name; + serial_port *serialport = nullptr; +}; diff --git a/Controllers/BlinkyTapeController/BlinkyTapeControllerDetect.cpp b/Controllers/BlinkyTapeController/BlinkyTapeControllerDetect.cpp new file mode 100644 index 0000000..1c2cc08 --- /dev/null +++ b/Controllers/BlinkyTapeController/BlinkyTapeControllerDetect.cpp @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| BlinkyTapeControllerDetect.cpp | +| | +| Detector for BlinkyTape | +| | +| Matt Mets (matt@blinkinlabs.com) 01 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "BlinkyTapeController.h" +#include "RGBController_BlinkyTape.h" +#include "find_usb_serial_port.h" + +/*-----------------------------------------------------*\ +| BlinkyTape VID and PID | +\*-----------------------------------------------------*/ +#define BLINKINLABS_VID 0x1D50 +#define BLINKYTAPE_PID 0x605E + +/******************************************************************************************\ +* * +* DetectBlinkyTapeControllers * +* * +* Detect BlinkyTape devices * +* * +\******************************************************************************************/ + +void DetectBlinkyTapeControllers() +{ + std::vector device_locations = find_usb_serial_port(BLINKINLABS_VID, BLINKYTAPE_PID); + + for(unsigned int device_idx = 0; device_idx < device_locations.size(); device_idx++) + { + BlinkyTapeController* controller = new BlinkyTapeController(); + controller->Initialize(*device_locations[device_idx]); + + RGBController_BlinkyTape* rgb_controller = new RGBController_BlinkyTape(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_DETECTOR("BlinkyTape", DetectBlinkyTapeControllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("BlinkyTape", DetectBlinkyTapeControllers, 0x1D50, 0x605E ) | +\*---------------------------------------------------------------------------------------------------------*/ diff --git a/Controllers/BlinkyTapeController/RGBController_BlinkyTape.cpp b/Controllers/BlinkyTapeController/RGBController_BlinkyTape.cpp new file mode 100644 index 0000000..8ea822e --- /dev/null +++ b/Controllers/BlinkyTapeController/RGBController_BlinkyTape.cpp @@ -0,0 +1,146 @@ +/*---------------------------------------------------------*\ +| RGBController_BlinkyTape.cpp | +| | +| RGBController for BlinkyTape | +| | +| Matt Mets (matt@blinkinlabs.com) 01 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_BlinkyTape.h" + +/**------------------------------------------------------------------*\ + @name Blinky Tape + @category LEDStrip + @type Serial + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectBlinkyTapeControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_BlinkyTape::RGBController_BlinkyTape(BlinkyTapeController* controller_ptr) +{ + controller = controller_ptr; + + name = "BlinkyTape"; + vendor = "Blinkinlabs"; + type = DEVICE_TYPE_LEDSTRIP; + description = "BlinkyTape Controller Device"; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_BlinkyTape::~RGBController_BlinkyTape() +{ + delete controller; +} + +void RGBController_BlinkyTape::SetupZones() +{ + zones.clear(); + leds.clear(); + + zone led_zone; + led_zone.name = "LED Strip"; + led_zone.type = ZONE_TYPE_LINEAR; + led_zone.leds_min = 0; + led_zone.leds_max = 512; + led_zone.leds_count = 0; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + ResizeZone(0, led_zone.leds_count); +} + +void RGBController_BlinkyTape::ResizeZone(int zone, int new_size) +{ + /*-------------------------------------------------*\ + | Explicitly cast these to avoid compiler warnings | + \*-------------------------------------------------*/ + const unsigned int zone_u = static_cast(zone); + const unsigned int new_size_u = static_cast(new_size); + + /*-------------------------------------------------*\ + | Check that the zone is in bounds | + \*-------------------------------------------------*/ + if((zone_u > zones.size()) || (zone < 0)) + { + return; + } + + /*-------------------------------------------------*\ + | And that the new size is in bounds | + \*-------------------------------------------------*/ + if((new_size_u > zones.at(zone).leds_max) || (new_size_u < zones.at(zone).leds_min)) + { + return; + } + + /*-------------------------------------------------*\ + | And that there's actually a change | + \*-------------------------------------------------*/ + if(zones.at(zone).leds_count == new_size_u) + { + return; + } + + /*-------------------------------------------------*\ + | If the new size is less than the current size, | + | just chop off the end | + \*-------------------------------------------------*/ + if(leds.size() > new_size_u) + { + leds.resize(new_size); + } + + /*-------------------------------------------------*\ + | Otherwise, add new LEDs to the end | + \*-------------------------------------------------*/ + if(leds.size() < new_size_u) + { + for(size_t led_idx = leds.size(); led_idx < new_size_u; led_idx++) + { + led new_led; + new_led.name = "LED "; + new_led.name.append(std::to_string(led_idx)); + + leds.push_back(new_led); + } + } + + zones.at(zone).leds_count = new_size; + + SetupColors(); +} + +void RGBController_BlinkyTape::DeviceUpdateLEDs() +{ + controller->SetLEDs(colors); +} + +void RGBController_BlinkyTape::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_BlinkyTape::UpdateSingleLED(int /*led*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_BlinkyTape::DeviceUpdateMode() +{ + +} diff --git a/Controllers/BlinkyTapeController/RGBController_BlinkyTape.h b/Controllers/BlinkyTapeController/RGBController_BlinkyTape.h new file mode 100644 index 0000000..f56a21e --- /dev/null +++ b/Controllers/BlinkyTapeController/RGBController_BlinkyTape.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_BlinkyTape.h | +| | +| RGBController for BlinkyTape | +| | +| Matt Mets (matt@blinkinlabs.com) 01 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "serial_port.h" +#include "BlinkyTapeController.h" + +class RGBController_BlinkyTape : public RGBController +{ +public: + RGBController_BlinkyTape(BlinkyTapeController* controller_ptr); + ~RGBController_BlinkyTape(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + BlinkyTapeController* controller; +}; diff --git a/Controllers/CherryKeyboardController/CherryKeyboardController.cpp b/Controllers/CherryKeyboardController/CherryKeyboardController.cpp new file mode 100644 index 0000000..457fd47 --- /dev/null +++ b/Controllers/CherryKeyboardController/CherryKeyboardController.cpp @@ -0,0 +1,269 @@ +/*---------------------------------------------------------*\ +| CherryKeyboardController.cpp | +| | +| Driver for Cherry keyboard | +| | +| Sebastian Kraus 25 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CherryKeyboardController.h" +#include "StringUtils.h" + +CherryKeyboardController::CherryKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +CherryKeyboardController::~CherryKeyboardController() +{ + hid_close(dev); +} + +std::string CherryKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string CherryKeyboardController::GetDeviceName() +{ + return(name); +} + +std::string CherryKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void CherryKeyboardController::SetKeyboardColors + ( + unsigned char * color_data, + unsigned int size + ) +{ + unsigned int packet_size = 0; + unsigned int packet_offset = 0; + + while(size > 0) + { + if(size >= CHERRY_KB_MAX_PACKET_SIZE) + { + packet_size = CHERRY_KB_MAX_PACKET_SIZE; + } + else + { + packet_size = size; + } + + SendKeyboardData + ( + &color_data[packet_offset], + packet_size, + packet_offset + ); + + size -= packet_size; + packet_offset += packet_size; + } +} + +void CherryKeyboardController::SendKeyboardMode + ( + unsigned char mode + ) +{ + SendKeyboardParameter(CHERRY_KB_PARAMETER_MODE, 1, &mode); +} + +void CherryKeyboardController::SendKeyboardModeEx + ( + unsigned char mode, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + unsigned char random_flag, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char parameter_data[9]; + + parameter_data[0] = 0x0; + parameter_data[1] = mode; + parameter_data[2] = brightness; + parameter_data[3] = speed; + parameter_data[4] = direction; + parameter_data[5] = random_flag; + parameter_data[6] = red; + parameter_data[7] = green; + parameter_data[8] = blue; + + SendKeyboardParameter(0, 9, parameter_data); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void CherryKeyboardController::ComputeChecksum + ( + char usb_buf[CHERRY_KB_PACKET_SIZE] + ) +{ + unsigned short checksum = 0; + + for(unsigned int byte_idx = 0x03; byte_idx < CHERRY_KB_PACKET_SIZE; byte_idx++) + { + checksum += usb_buf[byte_idx]; + } + + usb_buf[0x01] = checksum & 0xFF; + usb_buf[0x02] = checksum >> 8; +} + +void CherryKeyboardController::SendKeyboardBegin() +{ + unsigned char usb_buf[CHERRY_KB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Keyboard Begin (0x01) packet | + | Note: Not computing checksum as packet contents are | + | fixed | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x04; + usb_buf[0x01] = CHERRY_KB_COMMAND_BEGIN; + usb_buf[0x02] = 0x00; + usb_buf[0x03] = CHERRY_KB_COMMAND_BEGIN; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CHERRY_KB_PACKET_SIZE); + hid_read(dev, usb_buf, CHERRY_KB_PACKET_SIZE); +} + +void CherryKeyboardController::SendKeyboardEnd() +{ + char usb_buf[CHERRY_KB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Keyboard End (0x02) packet | + | Note: Not computing checksum as packet contents are | + | fixed | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x04; + usb_buf[0x01] = CHERRY_KB_COMMAND_END; + usb_buf[0x02] = 0x00; + usb_buf[0x03] = CHERRY_KB_COMMAND_END; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, CHERRY_KB_PACKET_SIZE); + hid_read(dev, (unsigned char *)usb_buf, CHERRY_KB_PACKET_SIZE); +} + +void CherryKeyboardController::SendKeyboardData + ( + unsigned char * data, + unsigned char data_size, + unsigned short data_offset + ) +{ + char usb_buf[CHERRY_KB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Keyboard Color Data (0x0B) packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x04; + usb_buf[0x03] = CHERRY_KB_COMMAND_WRITE_CUSTOM_COLOR_DATA; + + usb_buf[0x04] = data_size; + usb_buf[0x05] = data_offset & 0x00FF; + usb_buf[0x06] = data_offset >> 8; + + /*-----------------------------------------------------*\ + | Copy in data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x08], data, data_size); + + /*-----------------------------------------------------*\ + | Compute Checksum | + \*-----------------------------------------------------*/ + ComputeChecksum(usb_buf); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, CHERRY_KB_PACKET_SIZE); + hid_read(dev, (unsigned char *)usb_buf, CHERRY_KB_PACKET_SIZE); +} + +void CherryKeyboardController::SendKeyboardParameter + ( + unsigned char parameter, + unsigned char parameter_size, + unsigned char* parameter_data + ) +{ + char usb_buf[CHERRY_KB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Keyboard Parameter (0x08) packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x04; + usb_buf[0x03] = CHERRY_KB_COMMAND_SET_PARAMETER; + usb_buf[0x04] = parameter_size; + usb_buf[0x05] = parameter; + usb_buf[0x07] = 0x55; + + /*-----------------------------------------------------*\ + | Copy in data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x08], parameter_data, parameter_size); + + /*-----------------------------------------------------*\ + | Compute Checksum | + \*-----------------------------------------------------*/ + ComputeChecksum(usb_buf); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, CHERRY_KB_PACKET_SIZE); + hid_read(dev, (unsigned char *)usb_buf, CHERRY_KB_PACKET_SIZE); +} diff --git a/Controllers/CherryKeyboardController/CherryKeyboardController.h b/Controllers/CherryKeyboardController/CherryKeyboardController.h new file mode 100644 index 0000000..31a66f9 --- /dev/null +++ b/Controllers/CherryKeyboardController/CherryKeyboardController.h @@ -0,0 +1,184 @@ +/*---------------------------------------------------------*\ +| CherryKeyboardController.h | +| | +| Driver for Cherry keyboard | +| | +| Sebastian Kraus 25 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define CHERRY_KB_PACKET_SIZE 64 +#define CHERRY_KB_MAX_PACKET_SIZE ( 0x36 )/* max packet size for color*/ + /* update packets */ + +/*-----------------------------------------------------*\ +| Cherry keyboard product IDs | +\*-----------------------------------------------------*/ +#define MX_BOARD_3_0S_FL_NBL_PID 0x0077 +#define MX_BOARD_3_0S_FL_RGB_PID 0x0079 +#define MX_BOARD_3_0S_FL_RGB_KOR_PID 0x0083 +#define MX_1_0_FL_BL_PID 0x00AB +#define MX_BOARD_1_0_TKL_RGB_PID 0x00AC +#define MX_BOARD_8_0_TKL_RGB_PID 0x00B7 +#define MX_BOARD_10_0_FL_RGB_PID 0x00BB +#define G80_3000_TKL_NBL_PID 0x00C3 +#define MX_BOARD_2_0S_FL_RGB_EU_PID 0x01A6 +#define MX_BOARD_2_0S_FL_RGB_US_PID 0x00C4 +#define MX_BOARD_2_0S_FL_NBL_PID 0x00CE +#define G80_3000_TKL_RGB_PID 0x00C5 +#define MV_BOARD_3_0FL_RGB_PID 0x00C7 +#define CCF_MX_8_0_TKL_BL_PID 0x00C9 +#define CCF_MX_1_0_TKL_BL_PID 0x00CA +#define CCF_MX_1_0_TKL_NBL_PID 0x00CB +#define G80_3000_TKL_NBL_KOR_PID 0x00CD +#define MX_1_0_FL_NBL_PID 0x00D2 +#define MX_1_0_FL_RGB_PID 0x00D3 +#define G80_3000N_TKL_RGB_EU_PID 0x00DD +#define G80_3000N_TKL_RGB_US_PID 0x00E0 +#define G80_3000N_FL_RGB_EU_PID 0x00DE +#define G80_3000N_FL_RGB_US_PID 0x00E1 +#define MX_BOARD_10_0N_FL_RGB_EU_PID 0x00DF +#define MX_BOARD_10_0N_FL_RGB_US_PID 0x00E2 + + +enum +{ + CHERRY_KB_COMMAND_BEGIN = 0x01, /* Begin packet command */ + CHERRY_KB_COMMAND_END = 0x02, /* End packet command */ + CHERRY_KB_COMMAND_SET_PARAMETER = 0x06, /* Set parameter command */ + CHERRY_KB_COMMAND_READ_CUSTOM_COLOR_DATA = 0x1B, /* Read custom color data */ + CHERRY_KB_COMMAND_WRITE_CUSTOM_COLOR_DATA = 0x0B, /* Write custom color data */ +}; + +enum +{ + CHERRY_KB_PARAMETER_MODE = 0x00, /* Mode parameter */ + CHERRY_KB_PARAMETER_BRIGHTNESS = 0x01, /* Brightness parameter */ + CHERRY_KB_PARAMETER_SPEED = 0x02, /* Speed parameter */ + CHERRY_KB_PARAMETER_DIRECTION = 0x03, /* Direction parameter */ + CHERRY_KB_PARAMETER_RANDOM_COLOR_FLAG = 0x04, /* Random color parameter */ + CHERRY_KB_PARAMETER_MODE_COLOR = 0x05, /* Mode color (RGB) */ + CHERRY_KB_PARAMETER_POLLING_RATE = 0x0F, /* Polling rate */ + CHERRY_KB_PARAMETER_SURMOUNT_MODE_COLOR = 0x11, /* Surmount mode color */ +}; + +enum +{ + CHERRY_KB_MODE_WAVE = 0x00, + CHERRY_KB_MODE_SPECTRUM = 0x01, + CHERRY_KB_MODE_BREATHING = 0x02, + CHERRY_KB_MODE_STATIC = 0x03, + CHERRY_KB_MODE_RADAR = 0x04, + CHERRY_KB_MODE_VORTEX = 0x05, + CHERRY_KB_MODE_FIRE = 0x06, + CHERRY_KB_MODE_STARS = 0x07, + CHERRY_KB_MODE_CUSTOM = 0x08, + CHERRY_KB_MODE_ROLLING = 0x0A, + CHERRY_KB_MODE_RAIN = 0x0B, + CHERRY_KB_MODE_CURVE = 0x0C, + CHERRY_KB_MODE_WAVE_MID = 0x0E, + CHERRY_KB_MODE_SCAN = 0x0F, + CHERRY_KB_MODE_RADIATION = 0x12, + CHERRY_KB_MODE_RIPPLES = 0x13, + CHERRY_KB_MODE_SINGLE_KEY = 0x15, + +}; + +enum +{ + CHERRY_KB_BRIGHTNESS_LOWEST = 0x00, /* Lowest brightness (off) */ + CHERRY_KB_BRIGHTNESS_HIGHEST = 0x04, /* Highest brightness */ +}; + +enum +{ + CHERRY_KB_SPEED_SLOWEST = 0x04, /* Slowest speed setting */ + CHERRY_KB_SPEED_NORMAL = 0x02, /* Normal speed setting */ + CHERRY_KB_SPEED_FASTEST = 0x00, /* Fastest speed setting */ +}; + +enum +{ + CHERRY_KB_SURMOUNT_MODE_COLOR_RED = 0x01, /* Red surmount color */ + CHERRY_KB_SURMOUNT_MODE_COLOR_YELLOW = 0x02, /* Yellow surmount color */ + CHERRY_KB_SURMOUNT_MODE_COLOR_GREEN = 0x03, /* Green surmount color */ + CHERRY_KB_SURMOUNT_MODE_COLOR_BLUE = 0x04, /* Blue surmount color */ +}; + +enum +{ + CHERRY_KB_POLLING_RATE_125HZ = 0x00, /* 125Hz polling rate */ + CHERRY_KB_POLLING_RATE_250HZ = 0x01, /* 250Hz polling rate */ + CHERRY_KB_POLLING_RATE_500HZ = 0x02, /* 500Hz polling rate */ + CHERRY_KB_POLLING_RATE_1000HZ = 0x03, /* 1000Hz polling rate */ +}; + +class CherryKeyboardController +{ +public: + CherryKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name); + ~CherryKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + void SetKeyboardColors + ( + unsigned char * color_data, + unsigned int size + ); + + void SendKeyboardBegin(); + + void SendKeyboardMode + ( + unsigned char mode + ); + + void SendKeyboardModeEx + ( + unsigned char mode, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + unsigned char random_flag, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SendKeyboardData + ( + unsigned char * data, + unsigned char data_size, + unsigned short data_offset + ); + + void SendKeyboardEnd(); + +private: + hid_device* dev; + std::string location; + std::string name; + + void ComputeChecksum + ( + char usb_buf[CHERRY_KB_PACKET_SIZE] + ); + + void SendKeyboardParameter + ( + unsigned char parameter, + unsigned char parameter_size, + unsigned char* parameter_data + ); +}; diff --git a/Controllers/CherryKeyboardController/CherryKeyboardControllerDetect.cpp b/Controllers/CherryKeyboardController/CherryKeyboardControllerDetect.cpp new file mode 100644 index 0000000..2e49bbe --- /dev/null +++ b/Controllers/CherryKeyboardController/CherryKeyboardControllerDetect.cpp @@ -0,0 +1,70 @@ +/*---------------------------------------------------------*\ +| CherryKeyboardControllerDetect.cpp | +| | +| Detector for Cherry keyboard | +| | +| Sebastian Kraus 25 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "CherryKeyboardController.h" +#include "RGBController_CherryKeyboard.h" + +/*-----------------------------------------------------*\ +| Cherry keyboard VID and usage page | +\*-----------------------------------------------------*/ +#define CHERRY_KEYBOARD_VID 0x046A +#define CHERRY_KEYBOARD_USAGE_PAGE 0xFF1C + +/******************************************************************************************\ +* * +* DetectCherryKeyboards * +* * +* Tests the USB address to see if an Cherry RGB Keyboard controller exists there. * +* * +\******************************************************************************************/ + +void DetectCherryKeyboards(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + if( dev ) + { + CherryKeyboardController* controller = new CherryKeyboardController(dev, info->path, name); + RGBController_CherryKeyboard* rgb_controller = new RGBController_CherryKeyboard(controller, info->product_id); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +/*---------------------------------------------------------------------------------------------------------------------------------------------*\ +| Keyboards | +\*---------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 3.0S FL NBL", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_3_0S_FL_NBL_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 3.0S FL RGB", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_3_0S_FL_RGB_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 3.0S FL RGB KOREAN", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_3_0S_FL_RGB_KOR_PID, 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX 1.0 FL BL", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_1_0_FL_BL_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 1.0 TKL RGB", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_1_0_TKL_RGB_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 8.0 TKL RGB", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_8_0_TKL_RGB_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 10.0 FL RGB", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_10_0_FL_RGB_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard G80-3000 TKL NBL", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, G80_3000_TKL_NBL_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 2.0S FL RGB (EU)", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_2_0S_FL_RGB_EU_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 2.0S FL RGB (US)", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_2_0S_FL_RGB_US_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 2.0S FL NBL", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_2_0S_FL_NBL_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard G80-3000 TKL RGB", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, G80_3000_TKL_RGB_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MV BOARD 3.0 FL RGB", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MV_BOARD_3_0FL_RGB_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard CCF MX 8.0 TKL BL", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, CCF_MX_8_0_TKL_BL_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard CCF MX 1.0 TKL BL", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, CCF_MX_1_0_TKL_BL_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard CCF MX 1.0 TKL NBL", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, CCF_MX_1_0_TKL_NBL_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard G80-3000 TKL NBL KOREAN", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, G80_3000_TKL_NBL_KOR_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX 1.0 FL NBL", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_1_0_FL_NBL_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX 1.0 FL RGB", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_1_0_FL_RGB_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard G80-3000N TKL RGB (EU)", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, G80_3000N_TKL_RGB_EU_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard G80-3000N TKL RGB (US)", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, G80_3000N_TKL_RGB_US_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard G80-3000N FL RGB (EU)", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, G80_3000N_FL_RGB_EU_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard G80-3000N FL RGB (US)", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, G80_3000N_FL_RGB_US_PID , 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 10.0N FL RGB (EU)", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_10_0N_FL_RGB_EU_PID, 1, CHERRY_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Cherry Keyboard MX BOARD 10.0N FL RGB (US)", DetectCherryKeyboards, CHERRY_KEYBOARD_VID, MX_BOARD_10_0N_FL_RGB_US_PID, 1, CHERRY_KEYBOARD_USAGE_PAGE); diff --git a/Controllers/CherryKeyboardController/RGBController_CherryKeyboard.cpp b/Controllers/CherryKeyboardController/RGBController_CherryKeyboard.cpp new file mode 100644 index 0000000..1c66d0c --- /dev/null +++ b/Controllers/CherryKeyboardController/RGBController_CherryKeyboard.cpp @@ -0,0 +1,458 @@ +/*---------------------------------------------------------*\ +| RGBController_CherryKeyboard.cpp | +| | +| RGBController for Cherry keyboard | +| | +| Sebastian Kraus 25 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CherryKeyboard.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF +#define CHERRY_MATRIX_MAP_HEIGHT 6 +#define CHERRY_MATRIX_MAP_WIDTH 21 +#define CHERRY_MATRIX_CELL_COUNT ( CHERRY_MATRIX_MAP_HEIGHT * CHERRY_MATRIX_MAP_WIDTH ) + +/* The total byte count for all colors is 'number of matrix cells' times '3 color components' */ +#define CUSTOM_COLOR_ARRAY_BYTE_COUNT ( CHERRY_MATRIX_CELL_COUNT * 3 ) + +static unsigned int matrix_map[CHERRY_MATRIX_MAP_HEIGHT][CHERRY_MATRIX_MAP_WIDTH] = + { { 0, NA, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96, 102, 108, 114, 120 }, + { 1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, 85, 91, 97, 103, 109, 115, 121 }, + { 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 74, 80, 86, 92, 98, 104, 110, 116, NA }, + { 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 81, NA, NA, NA, 105, 111, 117, 122 }, + { 4, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70, NA, 82, NA, 94, NA, 106, 112, 118, NA }, + { 5, 11, 17, NA, NA, NA, 41, NA, NA, NA, 65, 71, 77, 83, 89, 95, 101, 113, NA, 119, 124 } }; + +/**------------------------------------------------------------------*\ + @name Cherry Keyboard + @category Keyboard + @type USB + @save :robot: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectCherryKeyboards + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CherryKeyboard::RGBController_CherryKeyboard(CherryKeyboardController* controller_ptr, uint16_t product_id) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Cherry"; + type = DEVICE_TYPE_KEYBOARD; + description = "Cherry Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Custom; + Custom.name = "Custom"; + Custom.value = CHERRY_KB_MODE_CUSTOM; + Custom.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Custom.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Custom.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Custom.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode Wave; + Wave.name = "Wave"; + Wave.value = CHERRY_KB_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Wave.speed_min = CHERRY_KB_SPEED_SLOWEST; + Wave.speed_max = CHERRY_KB_SPEED_FASTEST; + Wave.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Wave.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Wave.colors_min = 1; + Wave.colors_max = 1; + Wave.speed = CHERRY_KB_SPEED_NORMAL; + Wave.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.colors.resize(1); + modes.push_back(Wave); + + mode Spectrum; + Spectrum.name = "Spectrum"; + Spectrum.value = CHERRY_KB_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Spectrum.speed_min = CHERRY_KB_SPEED_SLOWEST; + Spectrum.speed_max = CHERRY_KB_SPEED_FASTEST; + Spectrum.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Spectrum.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Spectrum.speed = CHERRY_KB_SPEED_NORMAL; + Spectrum.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.colors.resize(1); + modes.push_back(Spectrum); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CHERRY_KB_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = CHERRY_KB_SPEED_SLOWEST; + Breathing.speed_max = CHERRY_KB_SPEED_FASTEST; + Breathing.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Breathing.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.speed = CHERRY_KB_SPEED_NORMAL; + Breathing.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Static; + Static.name = "Static"; + Static.value = CHERRY_KB_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Static.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Radar; + Radar.name = "Radar"; + Radar.value = CHERRY_KB_MODE_RADAR; + Radar.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Radar.speed_min = CHERRY_KB_SPEED_SLOWEST; + Radar.speed_max = CHERRY_KB_SPEED_FASTEST; + Radar.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Radar.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Radar.colors_min = 1; + Radar.colors_max = 1; + Radar.speed = CHERRY_KB_SPEED_NORMAL; + Radar.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Radar.color_mode = MODE_COLORS_MODE_SPECIFIC; + Radar.colors.resize(1); + if(hasUnofficialModeSupport(product_id)) + modes.push_back(Radar); + + mode Vortex; + Vortex.name = "Vortex"; + Vortex.value = CHERRY_KB_MODE_VORTEX; + Vortex.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Vortex.speed_min = CHERRY_KB_SPEED_SLOWEST; + Vortex.speed_max = CHERRY_KB_SPEED_FASTEST; + Vortex.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Vortex.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Vortex.colors_min = 1; + Vortex.colors_max = 1; + Vortex.speed = CHERRY_KB_SPEED_NORMAL; + Vortex.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Vortex.color_mode = MODE_COLORS_MODE_SPECIFIC; + Vortex.colors.resize(1); + if(hasUnofficialModeSupport(product_id)) + modes.push_back(Vortex); + + mode Fire; + Fire.name = "Fire"; + Fire.value = CHERRY_KB_MODE_FIRE; + Fire.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Fire.speed_min = CHERRY_KB_SPEED_SLOWEST; + Fire.speed_max = CHERRY_KB_SPEED_FASTEST; + Fire.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Fire.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Fire.colors_min = 1; + Fire.colors_max = 1; + Fire.speed = CHERRY_KB_SPEED_NORMAL; + Fire.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Fire.color_mode = MODE_COLORS_MODE_SPECIFIC; + Fire.colors.resize(1); + if(hasUnofficialModeSupport(product_id)) + modes.push_back(Fire); + + mode Stars; + Stars.name = "Stars"; + Stars.value = CHERRY_KB_MODE_STARS; + Stars.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Stars.speed_min = CHERRY_KB_SPEED_SLOWEST; + Stars.speed_max = CHERRY_KB_SPEED_FASTEST; + Stars.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Stars.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Stars.speed = CHERRY_KB_SPEED_NORMAL; + Stars.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Stars.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stars.colors.resize(1); + if(hasUnofficialModeSupport(product_id)) + modes.push_back(Stars); + + mode Rain; + Rain.name = "Rain"; + Rain.value = CHERRY_KB_MODE_RAIN; + Rain.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Rain.speed_min = CHERRY_KB_SPEED_SLOWEST; + Rain.speed_max = CHERRY_KB_SPEED_FASTEST; + Rain.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Rain.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Rain.colors_min = 1; + Rain.colors_max = 1; + Rain.speed = CHERRY_KB_SPEED_NORMAL; + Rain.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Rain.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rain.colors.resize(1); + if(hasUnofficialModeSupport(product_id)) + modes.push_back(Rain); + + mode Rolling; + Rolling.name = "Rolling"; + Rolling.value = CHERRY_KB_MODE_ROLLING; + Rolling.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Rolling.speed_min = CHERRY_KB_SPEED_SLOWEST; + Rolling.speed_max = CHERRY_KB_SPEED_FASTEST; + Rolling.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Rolling.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Rolling.speed = CHERRY_KB_SPEED_NORMAL; + Rolling.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Rolling.color_mode = MODE_COLORS_NONE; + modes.push_back(Rolling); + + mode Curve; + Curve.name = "Curve"; + Curve.value = CHERRY_KB_MODE_CURVE; + Curve.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Curve.speed_min = CHERRY_KB_SPEED_SLOWEST; + Curve.speed_max = CHERRY_KB_SPEED_FASTEST; + Curve.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Curve.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Curve.colors_min = 1; + Curve.colors_max = 1; + Curve.speed = CHERRY_KB_SPEED_NORMAL; + Curve.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Curve.color_mode = MODE_COLORS_MODE_SPECIFIC; + Curve.colors.resize(1); + modes.push_back(Curve); + + mode WaveMid; + WaveMid.name = "Wave Mid"; + WaveMid.value = CHERRY_KB_MODE_WAVE_MID; + WaveMid.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + WaveMid.speed_min = CHERRY_KB_SPEED_SLOWEST; + WaveMid.speed_max = CHERRY_KB_SPEED_FASTEST; + WaveMid.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + WaveMid.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + WaveMid.speed = CHERRY_KB_SPEED_NORMAL; + WaveMid.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + WaveMid.color_mode = MODE_COLORS_NONE; + WaveMid.colors.resize(1); + if(hasUnofficialModeSupport(product_id)) + modes.push_back(WaveMid); + + mode Scan; + Scan.name = "Scan"; + Scan.value = CHERRY_KB_MODE_SCAN; + Scan.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Scan.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Scan.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Scan.colors_min = 1; + Scan.colors_max = 1; + Scan.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Scan.color_mode = MODE_COLORS_MODE_SPECIFIC; + Scan.colors.resize(1); + modes.push_back(Scan); + + mode Radiation; + Radiation.name = "Radiation"; + Radiation.value = CHERRY_KB_MODE_RADIATION; + Radiation.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Radiation.speed_min = CHERRY_KB_SPEED_SLOWEST; + Radiation.speed_max = CHERRY_KB_SPEED_FASTEST; + Radiation.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Radiation.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Radiation.colors_min = 1; + Radiation.colors_max = 1; + Radiation.speed = CHERRY_KB_SPEED_NORMAL; + Radiation.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Radiation.color_mode = MODE_COLORS_MODE_SPECIFIC; + Radiation.colors.resize(1); + modes.push_back(Radiation); + + mode Ripples; + Ripples.name = "Ripples"; + Ripples.value = CHERRY_KB_MODE_RIPPLES; + Ripples.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Ripples.speed_min = CHERRY_KB_SPEED_SLOWEST; + Ripples.speed_max = CHERRY_KB_SPEED_FASTEST; + Ripples.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + Ripples.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + Ripples.colors_min = 1; + Ripples.colors_max = 1; + Ripples.speed = CHERRY_KB_SPEED_NORMAL; + Ripples.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + Ripples.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripples.colors.resize(1); + modes.push_back(Ripples); + + mode SingleKey; + SingleKey.name = "Single Key"; + SingleKey.value = CHERRY_KB_MODE_SINGLE_KEY; + SingleKey.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + SingleKey.speed_min = CHERRY_KB_SPEED_SLOWEST; + SingleKey.speed_max = CHERRY_KB_SPEED_FASTEST; + SingleKey.brightness_min = CHERRY_KB_BRIGHTNESS_LOWEST; + SingleKey.brightness_max = CHERRY_KB_BRIGHTNESS_HIGHEST; + SingleKey.colors_min = 1; + SingleKey.colors_max = 1; + SingleKey.speed = CHERRY_KB_SPEED_NORMAL; + SingleKey.brightness = CHERRY_KB_BRIGHTNESS_HIGHEST; + SingleKey.color_mode = MODE_COLORS_MODE_SPECIFIC; + SingleKey.colors.resize(1); + modes.push_back(SingleKey); + + SetupZones(); +} + +RGBController_CherryKeyboard::~RGBController_CherryKeyboard() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_CherryKeyboard::SetupZones() +{ + zone new_zone; + + new_zone.name = "Keyboard"; + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = CHERRY_MATRIX_CELL_COUNT; + new_zone.leds_max = CHERRY_MATRIX_CELL_COUNT; + new_zone.leds_count = CHERRY_MATRIX_CELL_COUNT; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = CHERRY_MATRIX_MAP_HEIGHT; + new_zone.matrix_map->width = CHERRY_MATRIX_MAP_WIDTH; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + + zones.push_back(new_zone); + + for(int led_idx = 0; led_idx < CHERRY_MATRIX_CELL_COUNT; led_idx++) + { + led new_led; + + new_led.name = "Keyboard LED "; + new_led.name.append(std::to_string(led_idx)); + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_CherryKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CherryKeyboard::DeviceUpdateLEDs() +{ + unsigned char color_data[CUSTOM_COLOR_ARRAY_BYTE_COUNT]; + + for(int led_idx = 0; led_idx < CHERRY_MATRIX_CELL_COUNT; led_idx++) + { + color_data[(3 * led_idx) + 0] = RGBGetRValue(colors[led_idx]); + color_data[(3 * led_idx) + 1] = RGBGetGValue(colors[led_idx]); + color_data[(3 * led_idx) + 2] = RGBGetBValue(colors[led_idx]); + } + + controller->SetKeyboardColors + ( + color_data, + CUSTOM_COLOR_ARRAY_BYTE_COUNT + ); +} + +void RGBController_CherryKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CherryKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CherryKeyboard::DeviceUpdateMode() +{ + unsigned char red = 0x00; + unsigned char grn = 0x00; + unsigned char blu = 0x00; + unsigned char random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + + if(modes[active_mode].colors.size() > 0) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + controller->SendKeyboardModeEx + ( + modes[active_mode].value, + modes[active_mode].brightness, + modes[active_mode].speed, + 0, + random, + red, + grn, + blu + ); +} + +bool RGBController_CherryKeyboard::hasUnofficialModeSupport(const uint16_t product_id) +{ + switch(product_id) + { + // no backlight: Why are they even listed here? (no lights, no macros) + case MX_BOARD_3_0S_FL_NBL_PID: + case G80_3000_TKL_NBL_PID: + case MX_1_0_FL_NBL_PID: + case G80_3000_TKL_NBL_KOR_PID: + case CCF_MX_1_0_TKL_NBL_PID: + // white backlight keyboards: very doubtful if any of those RGB modes match + case CCF_MX_8_0_TKL_BL_PID: + case CCF_MX_1_0_TKL_BL_PID: + case MX_1_0_FL_BL_PID: + return false; + // RGB keyboards known for not supporting unofficial modes + case MX_BOARD_3_0S_FL_RGB_PID: + case MX_BOARD_3_0S_FL_RGB_KOR_PID: + case MX_BOARD_2_0S_FL_RGB_US_PID: + case MX_BOARD_2_0S_FL_NBL_PID: + case MX_BOARD_2_0S_FL_RGB_EU_PID: + case MV_BOARD_3_0FL_RGB_PID: + return false; + // RGB keyboards which (probably) support unofficial modes + case MX_BOARD_1_0_TKL_RGB_PID: // unknown + case MX_BOARD_8_0_TKL_RGB_PID: // unknown + case MX_BOARD_10_0_FL_RGB_PID: // unknown (probably yes, related to 10.0N) + case G80_3000_TKL_RGB_PID: // unknown + case MX_1_0_FL_RGB_PID: // unkown + case G80_3000N_TKL_RGB_EU_PID: // yes + case G80_3000N_TKL_RGB_US_PID: // yes + case G80_3000N_FL_RGB_EU_PID: // firmware v0102: YES, firmware v0103: NO + case G80_3000N_FL_RGB_US_PID: // firmware v0102: YES, firmware v0103: NO + case MX_BOARD_10_0N_FL_RGB_EU_PID: // yes + case MX_BOARD_10_0N_FL_RGB_US_PID: // yes + default: + return true; + } +} diff --git a/Controllers/CherryKeyboardController/RGBController_CherryKeyboard.h b/Controllers/CherryKeyboardController/RGBController_CherryKeyboard.h new file mode 100644 index 0000000..9fe6075 --- /dev/null +++ b/Controllers/CherryKeyboardController/RGBController_CherryKeyboard.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_CherryKeyboard.h | +| | +| RGBController for Cherry keyboard | +| | +| Sebastian Kraus 25 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CherryKeyboardController.h" + +class RGBController_CherryKeyboard : public RGBController +{ +public: + RGBController_CherryKeyboard(CherryKeyboardController* controller_ptr, uint16_t product_id); + ~RGBController_CherryKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CherryKeyboardController* controller; + + static bool hasUnofficialModeSupport(uint16_t product_id); +}; diff --git a/Controllers/ClevoKeyboardController/ClevoKeyboardController.cpp b/Controllers/ClevoKeyboardController/ClevoKeyboardController.cpp new file mode 100644 index 0000000..a730058 --- /dev/null +++ b/Controllers/ClevoKeyboardController/ClevoKeyboardController.cpp @@ -0,0 +1,199 @@ +/*---------------------------------------------------------*\ +| ClevoKeyboardController.cpp | +| | +| Driver for Clevo laptop per-key RGB keyboard (ITE 8291) | +| Protocol based on tuxedo-drivers ite_8291 module | +| | +| Kyle Cascade (kyle@cascade.family) 16 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "ClevoKeyboardController.h" +#include "StringUtils.h" + +ClevoKeyboardController::ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info) +{ + dev = dev_handle; + location = info.path; + version = info.release_number; +} + +ClevoKeyboardController::~ClevoKeyboardController() +{ + hid_close(dev); +} + +std::string ClevoKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string ClevoKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string ClevoKeyboardController::GetFirmwareVersion() +{ + char version_string[16]; + snprintf(version_string, sizeof(version_string), "%d.%02d", version >> 8, version & 0xFF); + return(version_string); +} + +void ClevoKeyboardController::WriteControl(unsigned char* data) +{ + hid_send_feature_report(dev, data, CLEVO_KEYBOARD_REPORT_SIZE); +} + +void ClevoKeyboardController::WriteRowData(unsigned char* data) +{ + hid_write(dev, data, CLEVO_KEYBOARD_ROW_DATA_SIZE); +} + +void ClevoKeyboardController::TurnOff() +{ + /*---------------------------------------------------------*\ + | Turn off: 08 01 00 00 00 00 00 00 | + \*---------------------------------------------------------*/ + unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE]; + + memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE); + buf[0] = 0x08; + buf[1] = 0x01; + + WriteControl(buf); +} + +void ClevoKeyboardController::SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char behaviour) +{ + /*---------------------------------------------------------*\ + | Set params: 08 [power] [mode] [speed] [brightness] 08 | + | [behaviour] 00 | + | power: 01=off, 02=on | + \*---------------------------------------------------------*/ + unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE]; + + memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE); + buf[0] = 0x08; + buf[1] = 0x02; // Power on + buf[2] = mode; + buf[3] = speed; + buf[4] = brightness; + buf[5] = 0x08; + buf[6] = behaviour; + + WriteControl(buf); +} + +void ClevoKeyboardController::SetModeColor(unsigned char color_idx, unsigned char red, unsigned char green, unsigned char blue) +{ + /*---------------------------------------------------------*\ + | Set color define: 14 00 [index] R G B 00 00 | + | index: 1-7 for built-in effects | + \*---------------------------------------------------------*/ + unsigned char buf[CLEVO_KEYBOARD_REPORT_SIZE]; + + memset(buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE); + buf[0] = 0x14; + buf[1] = 0x00; + buf[2] = color_idx; + buf[3] = red; + buf[4] = green; + buf[5] = blue; + + WriteControl(buf); +} + +void ClevoKeyboardController::SendColors(unsigned char* color_data, unsigned char brightness) +{ + /*---------------------------------------------------------*\ + | Per-key RGB mode (mode 0x33) | + | 1. Set params with mode 0x33 and brightness | + | 2. For each row 0-5: | + | - Announce row: 16 00 [row] 00 00 00 00 00 | + | - Send 65 bytes row data via output report | + | | + | Row data format (65 bytes): | + | [0x00 padding][0x00 padding] | + | [B0..B20][G0..G20][R0..R20] | + \*---------------------------------------------------------*/ + unsigned char ctrl_buf[CLEVO_KEYBOARD_REPORT_SIZE]; + unsigned char row_buf[CLEVO_KEYBOARD_ROW_DATA_SIZE]; + + /*---------------------------------------------------------*\ + | Clamp brightness | + \*---------------------------------------------------------*/ + if(brightness > CLEVO_KEYBOARD_BRIGHTNESS_MAX) + { + brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + } + + /*---------------------------------------------------------*\ + | Set params for per-key mode | + \*---------------------------------------------------------*/ + memset(ctrl_buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE); + ctrl_buf[0] = 0x08; + ctrl_buf[1] = 0x02; // Power on + ctrl_buf[2] = CLEVO_KEYBOARD_MODE_DIRECT; // Per-key mode + ctrl_buf[3] = 0x00; // Speed (unused) + ctrl_buf[4] = brightness; + ctrl_buf[5] = 0x00; + ctrl_buf[6] = 0x00; + + WriteControl(ctrl_buf); + + /*---------------------------------------------------------*\ + | Send each row | + \*---------------------------------------------------------*/ + for(int row = 0; row < CLEVO_KEYBOARD_NUM_ROWS; row++) + { + /*-----------------------------------------------------*\ + | Announce row data | + \*-----------------------------------------------------*/ + memset(ctrl_buf, 0x00, CLEVO_KEYBOARD_REPORT_SIZE); + ctrl_buf[0] = 0x16; + ctrl_buf[1] = 0x00; + ctrl_buf[2] = row; + + WriteControl(ctrl_buf); + + /*-----------------------------------------------------*\ + | Build row data buffer | + | Format: [pad][pad][B0..B20][G0..G20][R0..R20] | + \*-----------------------------------------------------*/ + memset(row_buf, 0x00, CLEVO_KEYBOARD_ROW_DATA_SIZE); + + for(int col = 0; col < CLEVO_KEYBOARD_NUM_COLS; col++) + { + int led_idx = (row * CLEVO_KEYBOARD_NUM_COLS) + col; + int color_offset = led_idx * 3; + + unsigned char red = color_data[color_offset + 0]; + unsigned char green = color_data[color_offset + 1]; + unsigned char blue = color_data[color_offset + 2]; + + /*-------------------------------------------------*\ + | Row data layout (after 2-byte padding): | + | Bytes 2-22: Blue values for columns 0-20 | + | Bytes 23-43: Green values for columns 0-20 | + | Bytes 44-64: Red values for columns 0-20 | + \*-------------------------------------------------*/ + row_buf[2 + col] = blue; + row_buf[2 + CLEVO_KEYBOARD_NUM_COLS + col] = green; + row_buf[2 + CLEVO_KEYBOARD_NUM_COLS*2 + col] = red; + } + + WriteRowData(row_buf); + } +} diff --git a/Controllers/ClevoKeyboardController/ClevoKeyboardController.h b/Controllers/ClevoKeyboardController/ClevoKeyboardController.h new file mode 100644 index 0000000..b477787 --- /dev/null +++ b/Controllers/ClevoKeyboardController/ClevoKeyboardController.h @@ -0,0 +1,89 @@ +/*---------------------------------------------------------*\ +| ClevoKeyboardController.h | +| | +| Driver for Clevo laptop per-key RGB keyboard (ITE 8291) | +| | +| Kyle Cascade (kyle@cascade.family) 16 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +/*-----------------------------------------------------*\ +| ITE 8291 keyboard defines | +\*-----------------------------------------------------*/ +#define CLEVO_KEYBOARD_REPORT_SIZE 8 +#define CLEVO_KEYBOARD_ROW_DATA_SIZE 65 + +#define CLEVO_KEYBOARD_NUM_ROWS 6 +#define CLEVO_KEYBOARD_NUM_COLS 21 +#define CLEVO_KEYBOARD_NUM_LEDS (CLEVO_KEYBOARD_NUM_ROWS * CLEVO_KEYBOARD_NUM_COLS) + +#define CLEVO_KEYBOARD_BRIGHTNESS_MIN 0x00 +#define CLEVO_KEYBOARD_BRIGHTNESS_MAX 0x32 + +#define CLEVO_KEYBOARD_SPEED_MIN 0x01 +#define CLEVO_KEYBOARD_SPEED_MAX 0x0A + +/*-----------------------------------------------------*\ +| ITE 8291 modes | +\*-----------------------------------------------------*/ +enum +{ + CLEVO_KEYBOARD_MODE_DIRECT = 0x33, + CLEVO_KEYBOARD_MODE_BREATH = 0x02, + CLEVO_KEYBOARD_MODE_WAVE = 0x03, + CLEVO_KEYBOARD_MODE_REACTIVE = 0x04, + CLEVO_KEYBOARD_MODE_RAINBOW = 0x05, + CLEVO_KEYBOARD_MODE_RIPPLE = 0x06, + CLEVO_KEYBOARD_MODE_MARQUEE = 0x09, + CLEVO_KEYBOARD_MODE_RAINDROP = 0x0A, + CLEVO_KEYBOARD_MODE_AURORA = 0x0E, + CLEVO_KEYBOARD_MODE_SPARK = 0x11, +}; + +/*-----------------------------------------------------*\ +| Wave/reactive behaviour | +\*-----------------------------------------------------*/ +enum +{ + CLEVO_KEYBOARD_DIRECTION_LEFT = 0x01, + CLEVO_KEYBOARD_DIRECTION_RIGHT = 0x02, + CLEVO_KEYBOARD_DIRECTION_UP = 0x03, + CLEVO_KEYBOARD_DIRECTION_DOWN = 0x04, +}; + +enum +{ + CLEVO_KEYBOARD_REACTIVE_KEYPRESS = 0x00, + CLEVO_KEYBOARD_REACTIVE_AUTO = 0x01, +}; + +class ClevoKeyboardController +{ +public: + ClevoKeyboardController(hid_device* dev_handle, const hid_device_info& info); + ~ClevoKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + std::string GetFirmwareVersion(); + + void TurnOff(); + void SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char behaviour); + void SetModeColor(unsigned char color_idx, unsigned char red, unsigned char green, unsigned char blue); + void SendColors(unsigned char* color_data, unsigned char brightness); + +private: + hid_device* dev; + std::string location; + unsigned short version; + + void WriteControl(unsigned char* data); + void WriteRowData(unsigned char* data); +}; diff --git a/Controllers/ClevoKeyboardController/ClevoKeyboardControllerDetect.cpp b/Controllers/ClevoKeyboardController/ClevoKeyboardControllerDetect.cpp new file mode 100644 index 0000000..9f612fb --- /dev/null +++ b/Controllers/ClevoKeyboardController/ClevoKeyboardControllerDetect.cpp @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| ClevoKeyboardControllerDetect.cpp | +| | +| Detector for Clevo per-key RGB keyboard (ITE 8291) | +| | +| Kyle Cascade (kyle@cascade.family) 16 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ClevoKeyboardController.h" +#include "RGBController_ClevoKeyboard.h" +#include "RGBController.h" +#include + +/*-----------------------------------------------------*\ +| ITE Tech vendor ID | +\*-----------------------------------------------------*/ +#define ITE_VID 0x048D + +/*-----------------------------------------------------*\ +| Clevo Keyboard product IDs | +| These are ITE 8291 per-key RGB keyboard controllers | +\*-----------------------------------------------------*/ +#define CLEVO_KEYBOARD_PID_600B 0x600B + +void DetectClevoKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + ClevoKeyboardController* controller = new ClevoKeyboardController(dev, *info); + RGBController_ClevoKeyboard* rgb_controller = new RGBController_ClevoKeyboard(controller); + rgb_controller->name = name; + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("Clevo Keyboard", DetectClevoKeyboardControllers, 0x048D, 0x600B, 0xFF03, 0x01); diff --git a/Controllers/ClevoKeyboardController/ClevoKeyboardDevices.cpp b/Controllers/ClevoKeyboardController/ClevoKeyboardDevices.cpp new file mode 100644 index 0000000..c13de50 --- /dev/null +++ b/Controllers/ClevoKeyboardController/ClevoKeyboardDevices.cpp @@ -0,0 +1,164 @@ +/*---------------------------------------------------------*\ +| ClevoKeyboardDevices.cpp | +| | +| Device list for Clevo per-key RGB keyboards (ITE 8291) | +| | +| Kyle Cascade (kyle@cascade.family) 21 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "ClevoKeyboardDevices.h" + +/*---------------------------------------------------------*\ +| Clevo Keyboard Layout | +| | +| Based on KEYBOARD_SIZE_TKL with numpad added and | +| navigation cluster adjusted to match Clevo's layout. | +| | +| Hardware LED indices (value field): | +| - Row 5 (F-keys): 105-124 | +| - Row 4 (numbers): 84-102 | +| - Row 3 (QWERTY): 63-81 | +| - Row 2 (home): 42-59 | +| - Row 1 (Z row): 22-39 | +| - Row 0 (modifiers): 0-18 | +\*---------------------------------------------------------*/ + +/*---------------------------------------------------------*\ +| LED values in TKL order (fn_row + main + extras) | +| | +| Values follow the key order in KeyboardLayoutManager.cpp. | +| For ANSI-only keys (not present on this ISO keyboard), | +| use 0 as a placeholder - they won't be displayed. | +| Numpad values are added via edit_keys. | +\*---------------------------------------------------------*/ +static const std::vector clevo_tkl_values = +{ + /*---------------------------------------------------------*\ + | Function row (keyboard_zone_fn_row) | + \*---------------------------------------------------------*/ + 105, // Escape + 106, 107, 108, 109, // F1-F4 + 110, 111, 112, 113, // F5-F8 + 114, 115, 116, 117, // F9-F12 + + /*---------------------------------------------------------*\ + | Main block - Row 1 (keyboard_zone_main) | + \*---------------------------------------------------------*/ + 84, // Back tick + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, // 1-0 + 95, 96, // Minus, Equals + 98, // Backspace + + /*---------------------------------------------------------*\ + | Main block - Row 2 | + \*---------------------------------------------------------*/ + 63, // Tab + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, // Q-P + 75, 76, // [ ] + 0, // ANSI backslash (not on ISO) + + /*---------------------------------------------------------*\ + | Main block - Row 3 | + \*---------------------------------------------------------*/ + 42, // Caps Lock + 44, 45, 46, 47, 48, 49, 50, 51, 52, // A-L + 53, 54, // ; ' + 55, // ISO # (POUND) + 77, // Enter (ANSI/ISO share same LED) + + /*---------------------------------------------------------*\ + | Main block - Row 4 | + \*---------------------------------------------------------*/ + 22, // Left Shift + 23, // ISO backslash + 24, 25, 26, 27, 28, 29, 30, 31, 32, // Z-. (9 keys) + 33, // / + 35, // Right Shift + + /*---------------------------------------------------------*\ + | Main block - Row 5 | + \*---------------------------------------------------------*/ + 0, // Left Ctrl + 3, // Left Win + 4, // Left Alt + 7, // Space + 10, // Right Alt + 0, // Right Fn (removed via edit_keys) + 0, // Menu (removed via edit_keys) + 12, // Right Ctrl + + /*---------------------------------------------------------*\ + | Extras - Navigation cluster (keyboard_zone_extras) | + \*---------------------------------------------------------*/ + 118, // Print Screen + 0, // Scroll Lock (removed via edit_keys) + 0, // Pause (removed via edit_keys) + 119, // Insert + 121, // Home + 123, // Page Up + 120, // Delete + 122, // End + 124, // Page Down + + /*---------------------------------------------------------*\ + | Extras - Arrow keys | + \*---------------------------------------------------------*/ + 14, // Up + 13, // Left + 18, // Down + 15, // Right +}; + +keyboard_keymap_overlay_values clevo_keyboard_layout +{ + KEYBOARD_SIZE_TKL, + { + clevo_tkl_values, + { + /* No regional overlays needed */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + + /*---------------------------------------------------------*\ + | Remove keys not present on Clevo keyboard | + \*---------------------------------------------------------*/ + { 0, 0, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Scroll Lock + { 0, 0, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Pause + { 0, 5, 11, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Right Fn + { 0, 5, 11, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Menu + + /*---------------------------------------------------------*\ + | Add Left Function key | + \*---------------------------------------------------------*/ + { 0, 5, 1, 2, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + + /*---------------------------------------------------------*\ + | Add Numpad | + \*---------------------------------------------------------*/ + { 0, 1, 15, 99, KEY_EN_NUMPAD_LOCK, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 16, 100, KEY_EN_NUMPAD_DIVIDE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 17, 101, KEY_EN_NUMPAD_TIMES, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 18, 102, KEY_EN_NUMPAD_MINUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 15, 78, KEY_EN_NUMPAD_7, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 16, 79, KEY_EN_NUMPAD_8, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 17, 80, KEY_EN_NUMPAD_9, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 18, 81, KEY_EN_NUMPAD_PLUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 15, 57, KEY_EN_NUMPAD_4, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 16, 58, KEY_EN_NUMPAD_5, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 17, 59, KEY_EN_NUMPAD_6, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 15, 36, KEY_EN_NUMPAD_1, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 16, 37, KEY_EN_NUMPAD_2, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 17, 38, KEY_EN_NUMPAD_3, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 18, 39, KEY_EN_NUMPAD_ENTER, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 15, 16, KEY_EN_NUMPAD_0, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 16, 17, KEY_EN_NUMPAD_PERIOD, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; diff --git a/Controllers/ClevoKeyboardController/ClevoKeyboardDevices.h b/Controllers/ClevoKeyboardController/ClevoKeyboardDevices.h new file mode 100644 index 0000000..613d1b8 --- /dev/null +++ b/Controllers/ClevoKeyboardController/ClevoKeyboardDevices.h @@ -0,0 +1,20 @@ +/*---------------------------------------------------------*\ +| ClevoKeyboardDevices.h | +| | +| Device list for Clevo per-key RGB keyboards (ITE 8291) | +| | +| Kyle Cascade (kyle@cascade.family) 21 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "KeyboardLayoutManager.h" + +/*-----------------------------------------------------*\ +| Clevo keyboard layout definitions | +\*-----------------------------------------------------*/ +extern keyboard_keymap_overlay_values clevo_keyboard_layout; diff --git a/Controllers/ClevoKeyboardController/RGBController_ClevoKeyboard.cpp b/Controllers/ClevoKeyboardController/RGBController_ClevoKeyboard.cpp new file mode 100644 index 0000000..db5bc6d --- /dev/null +++ b/Controllers/ClevoKeyboardController/RGBController_ClevoKeyboard.cpp @@ -0,0 +1,377 @@ +/*---------------------------------------------------------*\ +| RGBController_ClevoKeyboard.cpp | +| | +| RGBController for Clevo per-key RGB keyboard (ITE 8291) | +| | +| Kyle Cascade (kyle@cascade.family) 16 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ClevoKeyboard.h" +#include "KeyboardLayoutManager.h" + +/**------------------------------------------------------------------*\ + @name CLEVO Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectClevoKeyboardControllers + @comment Per-key RGB keyboard on CLEVO laptops using ITE 8291 controller. +\*-------------------------------------------------------------------*/ + +RGBController_ClevoKeyboard::RGBController_ClevoKeyboard(ClevoKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = "CLEVO Keyboard"; + vendor = "CLEVO Computers"; + type = DEVICE_TYPE_KEYBOARD; + description = "CLEVO Laptop Keyboard"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CLEVO_KEYBOARD_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN; + Direct.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Direct.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = CLEVO_KEYBOARD_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rainbow.speed_min = CLEVO_KEYBOARD_SPEED_MAX; + Rainbow.speed_max = CLEVO_KEYBOARD_SPEED_MIN; + Rainbow.speed = 0x05; + Rainbow.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN; + Rainbow.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Rainbow.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Wave; + Wave.name = "Wave"; + Wave.value = CLEVO_KEYBOARD_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Wave.speed_min = CLEVO_KEYBOARD_SPEED_MAX; + Wave.speed_max = CLEVO_KEYBOARD_SPEED_MIN; + Wave.speed = 0x05; + Wave.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN; + Wave.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Wave.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Wave.direction = MODE_DIRECTION_LEFT; + Wave.colors_min = 1; + Wave.colors_max = 1; + Wave.colors.resize(1); + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Wave); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CLEVO_KEYBOARD_MODE_BREATH; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.speed_min = CLEVO_KEYBOARD_SPEED_MAX; + Breathing.speed_max = CLEVO_KEYBOARD_SPEED_MIN; + Breathing.speed = 0x05; + Breathing.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN; + Breathing.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Breathing.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Breathing); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = CLEVO_KEYBOARD_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Reactive.speed_min = CLEVO_KEYBOARD_SPEED_MAX; + Reactive.speed_max = CLEVO_KEYBOARD_SPEED_MIN; + Reactive.speed = 0x05; + Reactive.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN; + Reactive.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Reactive.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Reactive.colors_min = 1; + Reactive.colors_max = 1; + Reactive.colors.resize(1); + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Reactive); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = CLEVO_KEYBOARD_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Ripple.speed_min = CLEVO_KEYBOARD_SPEED_MAX; + Ripple.speed_max = CLEVO_KEYBOARD_SPEED_MIN; + Ripple.speed = 0x05; + Ripple.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN; + Ripple.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Ripple.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Ripple.colors_min = 1; + Ripple.colors_max = 1; + Ripple.colors.resize(1); + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Ripple); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = CLEVO_KEYBOARD_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Marquee.speed_min = CLEVO_KEYBOARD_SPEED_MAX; + Marquee.speed_max = CLEVO_KEYBOARD_SPEED_MIN; + Marquee.speed = 0x05; + Marquee.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN; + Marquee.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Marquee.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.colors.resize(1); + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Marquee); + + mode Raindrop; + Raindrop.name = "Raindrop"; + Raindrop.value = CLEVO_KEYBOARD_MODE_RAINDROP; + Raindrop.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Raindrop.speed_min = CLEVO_KEYBOARD_SPEED_MAX; + Raindrop.speed_max = CLEVO_KEYBOARD_SPEED_MIN; + Raindrop.speed = 0x05; + Raindrop.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN; + Raindrop.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Raindrop.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Raindrop.colors_min = 1; + Raindrop.colors_max = 1; + Raindrop.colors.resize(1); + Raindrop.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Raindrop); + + mode Aurora; + Aurora.name = "Aurora"; + Aurora.value = CLEVO_KEYBOARD_MODE_AURORA; + Aurora.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Aurora.speed_min = CLEVO_KEYBOARD_SPEED_MAX; + Aurora.speed_max = CLEVO_KEYBOARD_SPEED_MIN; + Aurora.speed = 0x05; + Aurora.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN; + Aurora.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Aurora.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Aurora.colors_min = 1; + Aurora.colors_max = 1; + Aurora.colors.resize(1); + Aurora.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Aurora); + + mode Spark; + Spark.name = "Spark"; + Spark.value = CLEVO_KEYBOARD_MODE_SPARK; + Spark.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Spark.speed_min = CLEVO_KEYBOARD_SPEED_MAX; + Spark.speed_max = CLEVO_KEYBOARD_SPEED_MIN; + Spark.speed = 0x05; + Spark.brightness_min = CLEVO_KEYBOARD_BRIGHTNESS_MIN; + Spark.brightness_max = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Spark.brightness = CLEVO_KEYBOARD_BRIGHTNESS_MAX; + Spark.colors_min = 1; + Spark.colors_max = 1; + Spark.colors.resize(1); + Spark.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Spark); + + mode Off; + Off.name = "Off"; + Off.value = 0xFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_ClevoKeyboard::~RGBController_ClevoKeyboard() +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].matrix_map != nullptr) + { + delete[] zones[zone_idx].matrix_map->map; + delete zones[zone_idx].matrix_map; + } + } + + delete controller; +} + +void RGBController_ClevoKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Create keyboard layout using KeyboardLayoutManager | + \*---------------------------------------------------------*/ + KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ISO_QWERTY, + clevo_keyboard_layout.base_size, + clevo_keyboard_layout.key_values); + + new_kb.ChangeKeys(clevo_keyboard_layout); + + /*---------------------------------------------------------*\ + | Create a matrix zone for the keyboard | + \*---------------------------------------------------------*/ + zone keyboard_zone; + + keyboard_zone.name = ZONE_EN_KEYBOARD; + keyboard_zone.type = ZONE_TYPE_MATRIX; + keyboard_zone.leds_count = new_kb.GetKeyCount(); + keyboard_zone.leds_min = keyboard_zone.leds_count; + keyboard_zone.leds_max = keyboard_zone.leds_count; + + /*---------------------------------------------------------*\ + | Set up the matrix map using KLM dimensions | + \*---------------------------------------------------------*/ + keyboard_zone.matrix_map = new matrix_map_type; + keyboard_zone.matrix_map->height = new_kb.GetRowCount(); + keyboard_zone.matrix_map->width = new_kb.GetColumnCount(); + keyboard_zone.matrix_map->map = new unsigned int[keyboard_zone.matrix_map->height * keyboard_zone.matrix_map->width]; + + new_kb.GetKeyMap(keyboard_zone.matrix_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT); + + zones.push_back(keyboard_zone); + + /*---------------------------------------------------------*\ + | Create LEDs from the KeyboardLayoutManager data | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < keyboard_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + + leds.push_back(new_led); + } + + SetupColors(); + + /*---------------------------------------------------------*\ + | Create buffer map to translate OpenRGB LED order to | + | hardware LED order. The hardware expects 126 color values | + | indexed by LED position (0-125). | + \*---------------------------------------------------------*/ + null_color = 0x00000000; + buffer_map.resize(CLEVO_KEYBOARD_NUM_LEDS, &null_color); + + for(size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + buffer_map[leds[led_idx].value] = &colors[led_idx]; + } +} + +void RGBController_ClevoKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ClevoKeyboard::DeviceUpdateLEDs() +{ + /*---------------------------------------------------------*\ + | Build color data array using buffer map and send to device| + | The buffer_map translates from hardware LED index to the | + | corresponding color pointer for that LED. | + \*---------------------------------------------------------*/ + unsigned char color_data[CLEVO_KEYBOARD_NUM_LEDS * 3]; + + for(int i = 0; i < CLEVO_KEYBOARD_NUM_LEDS; i++) + { + color_data[i * 3 + 0] = RGBGetRValue(*buffer_map[i]); + color_data[i * 3 + 1] = RGBGetGValue(*buffer_map[i]); + color_data[i * 3 + 2] = RGBGetBValue(*buffer_map[i]); + } + + controller->SendColors(color_data, modes[active_mode].brightness); +} + +void RGBController_ClevoKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ClevoKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ClevoKeyboard::DeviceUpdateMode() +{ + unsigned char mode_value = modes[active_mode].value; + + /*---------------------------------------------------------*\ + | Handle Off mode | + \*---------------------------------------------------------*/ + if(mode_value == 0xFF) + { + controller->TurnOff(); + return; + } + + /*---------------------------------------------------------*\ + | Handle Direct (per-key) mode | + \*---------------------------------------------------------*/ + if(mode_value == CLEVO_KEYBOARD_MODE_DIRECT) + { + DeviceUpdateLEDs(); + return; + } + + /*---------------------------------------------------------*\ + | Handle built-in effect modes | + \*---------------------------------------------------------*/ + unsigned char brightness = modes[active_mode].brightness; + unsigned char speed = modes[active_mode].speed; + unsigned char behaviour = 0x00; + + /*---------------------------------------------------------*\ + | Set direction for wave mode | + \*---------------------------------------------------------*/ + if(mode_value == CLEVO_KEYBOARD_MODE_WAVE) + { + switch(modes[active_mode].direction) + { + case MODE_DIRECTION_LEFT: + behaviour = CLEVO_KEYBOARD_DIRECTION_LEFT; + break; + case MODE_DIRECTION_RIGHT: + behaviour = CLEVO_KEYBOARD_DIRECTION_RIGHT; + break; + case MODE_DIRECTION_UP: + behaviour = CLEVO_KEYBOARD_DIRECTION_UP; + break; + case MODE_DIRECTION_DOWN: + behaviour = CLEVO_KEYBOARD_DIRECTION_DOWN; + break; + } + } + + /*---------------------------------------------------------*\ + | Set mode color if applicable | + \*---------------------------------------------------------*/ + if(modes[active_mode].colors.size() > 0) + { + unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char green = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blue = RGBGetBValue(modes[active_mode].colors[0]); + + controller->SetModeColor(1, red, green, blue); + } + + controller->SetMode(mode_value, brightness, speed, behaviour); +} diff --git a/Controllers/ClevoKeyboardController/RGBController_ClevoKeyboard.h b/Controllers/ClevoKeyboardController/RGBController_ClevoKeyboard.h new file mode 100644 index 0000000..7439e57 --- /dev/null +++ b/Controllers/ClevoKeyboardController/RGBController_ClevoKeyboard.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_ClevoKeyboard.h | +| | +| RGBController for Clevo per-key RGB keyboard (ITE 8291) | +| | +| Kyle Cascade (kyle@cascade.family) 16 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ClevoKeyboardController.h" +#include "ClevoKeyboardDevices.h" + +class RGBController_ClevoKeyboard : public RGBController +{ +public: + RGBController_ClevoKeyboard(ClevoKeyboardController* controller_ptr); + ~RGBController_ClevoKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ClevoKeyboardController* controller; + std::vector buffer_map; + RGBColor null_color; +}; diff --git a/Controllers/ClevoLightbarController/ClevoLightbarController.cpp b/Controllers/ClevoLightbarController/ClevoLightbarController.cpp new file mode 100644 index 0000000..0d8b8f9 --- /dev/null +++ b/Controllers/ClevoLightbarController/ClevoLightbarController.cpp @@ -0,0 +1,157 @@ +/*---------------------------------------------------------*\ +| ClevoLightbarController.cpp | +| | +| Driver for Clevo laptop lightbar (ITE 8291 rev 0.03) | +| Protocol based on tuxedo-drivers ite_8291_lb module | +| | +| Kyle Cascade (kyle@cascade.family) 16 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "ClevoLightbarController.h" +#include "StringUtils.h" + +ClevoLightbarController::ClevoLightbarController(hid_device* dev_handle, const hid_device_info& info) +{ + dev = dev_handle; + location = info.path; + version = info.release_number; +} + +ClevoLightbarController::~ClevoLightbarController() +{ + hid_close(dev); +} + +std::string ClevoLightbarController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string ClevoLightbarController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string ClevoLightbarController::GetFirmwareVersion() +{ + char version_string[16]; + snprintf(version_string, sizeof(version_string), "%d.%02d", version >> 8, version & 0xFF); + return(version_string); +} + +void ClevoLightbarController::WriteControl(unsigned char* data) +{ + hid_send_feature_report(dev, data, CLEVO_LIGHTBAR_REPORT_SIZE); +} + +void ClevoLightbarController::TurnOn() +{ + /*---------------------------------------------------------*\ + | Not required for 0x7001 - device turns on when setting | + | color/brightness | + \*---------------------------------------------------------*/ +} + +void ClevoLightbarController::TurnOff() +{ + /*---------------------------------------------------------*\ + | Turn off sequence for device 0x7001 | + \*---------------------------------------------------------*/ + unsigned char buf[CLEVO_LIGHTBAR_REPORT_SIZE]; + + memset(buf, 0x00, CLEVO_LIGHTBAR_REPORT_SIZE); + buf[0] = 0x12; + buf[2] = 0x03; + WriteControl(buf); + + memset(buf, 0x00, CLEVO_LIGHTBAR_REPORT_SIZE); + buf[0] = 0x08; + buf[1] = 0x05; + WriteControl(buf); + + memset(buf, 0x00, CLEVO_LIGHTBAR_REPORT_SIZE); + buf[0] = 0x08; + buf[1] = 0x01; + WriteControl(buf); + + memset(buf, 0x00, CLEVO_LIGHTBAR_REPORT_SIZE); + buf[0] = 0x1A; + buf[7] = 0x01; + WriteControl(buf); +} + +void ClevoLightbarController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + /*---------------------------------------------------------*\ + | Set color: 0x14 0x00 0x01 R G B 0x00 0x00 | + \*---------------------------------------------------------*/ + unsigned char buf[CLEVO_LIGHTBAR_REPORT_SIZE]; + + memset(buf, 0x00, CLEVO_LIGHTBAR_REPORT_SIZE); + buf[0] = 0x14; + buf[1] = 0x00; + buf[2] = 0x01; + buf[3] = red; + buf[4] = green; + buf[5] = blue; + + WriteControl(buf); +} + +void ClevoLightbarController::SetBrightness(unsigned char brightness) +{ + /*---------------------------------------------------------*\ + | Set brightness (mono mode): | + | 0x08 0x22 0x01 0x01 brightness 0x01 0x00 0x00 | + \*---------------------------------------------------------*/ + unsigned char buf[CLEVO_LIGHTBAR_REPORT_SIZE]; + + memset(buf, 0x00, CLEVO_LIGHTBAR_REPORT_SIZE); + buf[0] = 0x08; + buf[1] = 0x22; + buf[2] = 0x01; + buf[3] = 0x01; + buf[4] = brightness; + buf[5] = 0x01; + + WriteControl(buf); +} + +void ClevoLightbarController::SetMode(unsigned char mode, unsigned char brightness, unsigned char speed) +{ + /*---------------------------------------------------------*\ + | Set mode for device 0x7001: | + | 0x08 0x22 MODE SPEED BRIGHTNESS 0x01 0x00 0x00 | + | | + | SPEED: 0x01 (fastest) to 0x0a (slowest) - we invert | + \*---------------------------------------------------------*/ + unsigned char buf[CLEVO_LIGHTBAR_REPORT_SIZE]; + + /*---------------------------------------------------------*\ + | Invert speed: UI uses 1=slow, 10=fast | + | Protocol uses 1=fast, 10=slow | + \*---------------------------------------------------------*/ + unsigned char inverted_speed = (CLEVO_LIGHTBAR_SPEED_MAX + CLEVO_LIGHTBAR_SPEED_MIN) - speed; + + memset(buf, 0x00, CLEVO_LIGHTBAR_REPORT_SIZE); + buf[0] = 0x08; + buf[1] = 0x22; + buf[2] = mode; + buf[3] = inverted_speed; + buf[4] = brightness; + buf[5] = 0x01; + + WriteControl(buf); +} diff --git a/Controllers/ClevoLightbarController/ClevoLightbarController.h b/Controllers/ClevoLightbarController/ClevoLightbarController.h new file mode 100644 index 0000000..3c5219c --- /dev/null +++ b/Controllers/ClevoLightbarController/ClevoLightbarController.h @@ -0,0 +1,58 @@ +/*---------------------------------------------------------*\ +| ClevoLightbarController.h | +| | +| Driver for Clevo laptop lightbar (ITE 8291 rev 0.03) | +| | +| Kyle Cascade (kyle@cascade.family) 16 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include +#include + +#define CLEVO_LIGHTBAR_REPORT_SIZE 8 +#define CLEVO_LIGHTBAR_BRIGHTNESS_MIN 0 +#define CLEVO_LIGHTBAR_BRIGHTNESS_MAX 100 +#define CLEVO_LIGHTBAR_SPEED_MIN 1 +#define CLEVO_LIGHTBAR_SPEED_MAX 10 +#define CLEVO_LIGHTBAR_SPEED_DEFAULT 5 + +enum +{ + CLEVO_LIGHTBAR_MODE_DIRECT = 0x01, + CLEVO_LIGHTBAR_MODE_BREATHING = 0x02, + CLEVO_LIGHTBAR_MODE_WAVE = 0x03, + CLEVO_LIGHTBAR_MODE_BOUNCE = 0x04, + CLEVO_LIGHTBAR_MODE_MARQUEE = 0x05, + CLEVO_LIGHTBAR_MODE_SCAN = 0x06, + CLEVO_LIGHTBAR_MODE_OFF = 0x00 +}; + +class ClevoLightbarController +{ +public: + ClevoLightbarController(hid_device* dev_handle, const hid_device_info& info); + ~ClevoLightbarController(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + std::string GetFirmwareVersion(); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + void SetBrightness(unsigned char brightness); + void SetMode(unsigned char mode, unsigned char brightness, unsigned char speed); + void TurnOn(); + void TurnOff(); + +private: + hid_device* dev; + std::string location; + unsigned short version; + + void WriteControl(unsigned char* data); +}; diff --git a/Controllers/ClevoLightbarController/ClevoLightbarControllerDetect.cpp b/Controllers/ClevoLightbarController/ClevoLightbarControllerDetect.cpp new file mode 100644 index 0000000..a513441 --- /dev/null +++ b/Controllers/ClevoLightbarController/ClevoLightbarControllerDetect.cpp @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| ClevoLightbarControllerDetect.cpp | +| | +| Detector for Clevo laptop lightbar (ITE 8291 rev 0.03) | +| | +| Kyle Cascade (kyle@cascade.family) 16 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ClevoLightbarController.h" +#include "RGBController_ClevoLightbar.h" +#include "RGBController.h" +#include + +/*-----------------------------------------------------*\ +| ITE Tech vendor ID | +\*-----------------------------------------------------*/ +#define ITE_VID 0x048D + +/*-----------------------------------------------------*\ +| CLEVO Lightbar product ID | +\*-----------------------------------------------------*/ +#define CLEVO_LIGHTBAR_PID 0x7001 + +void DetectClevoLightbarControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + ClevoLightbarController* controller = new ClevoLightbarController(dev, *info); + RGBController_ClevoLightbar* rgb_controller = new RGBController_ClevoLightbar(controller); + rgb_controller->name = name; + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("CLEVO Lightbar", DetectClevoLightbarControllers, ITE_VID, CLEVO_LIGHTBAR_PID, 0xFF03, 0x02); diff --git a/Controllers/ClevoLightbarController/RGBController_ClevoLightbar.cpp b/Controllers/ClevoLightbarController/RGBController_ClevoLightbar.cpp new file mode 100644 index 0000000..4833c93 --- /dev/null +++ b/Controllers/ClevoLightbarController/RGBController_ClevoLightbar.cpp @@ -0,0 +1,205 @@ +/*---------------------------------------------------------*\ +| RGBController_ClevoLightbar.cpp | +| | +| Generic RGB Interface for Clevo laptop lightbar | +| | +| Kyle Cascade (kyle@cascade.family) 16 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ClevoLightbar.h" + +/**------------------------------------------------------------------*\ + @name CLEVO Lightbar + @category LEDStrip + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectClevoLightbarControllers + @comment Experimental effects based on ITE 8291 protocol +\*-------------------------------------------------------------------*/ + +RGBController_ClevoLightbar::RGBController_ClevoLightbar(ClevoLightbarController* controller_ptr) +{ + controller = controller_ptr; + + name = "CLEVO Lightbar"; + vendor = "CLEVO Computers"; + type = DEVICE_TYPE_LEDSTRIP; + description = "CLEVO Laptop Lightbar"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CLEVO_LIGHTBAR_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = CLEVO_LIGHTBAR_BRIGHTNESS_MIN; + Direct.brightness_max = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Direct.brightness = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CLEVO_LIGHTBAR_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = CLEVO_LIGHTBAR_BRIGHTNESS_MIN; + Breathing.brightness_max = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Breathing.brightness = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Breathing.speed_min = CLEVO_LIGHTBAR_SPEED_MIN; + Breathing.speed_max = CLEVO_LIGHTBAR_SPEED_MAX; + Breathing.speed = CLEVO_LIGHTBAR_SPEED_DEFAULT; + modes.push_back(Breathing); + + mode Wave; + Wave.name = "Wave"; + Wave.value = CLEVO_LIGHTBAR_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Wave.color_mode = MODE_COLORS_NONE; + Wave.brightness_min = CLEVO_LIGHTBAR_BRIGHTNESS_MIN; + Wave.brightness_max = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Wave.brightness = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Wave.speed_min = CLEVO_LIGHTBAR_SPEED_MIN; + Wave.speed_max = CLEVO_LIGHTBAR_SPEED_MAX; + Wave.speed = CLEVO_LIGHTBAR_SPEED_DEFAULT; + modes.push_back(Wave); + + mode Bounce; + Bounce.name = "Bounce"; + Bounce.value = CLEVO_LIGHTBAR_MODE_BOUNCE; + Bounce.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Bounce.color_mode = MODE_COLORS_PER_LED; + Bounce.brightness_min = CLEVO_LIGHTBAR_BRIGHTNESS_MIN; + Bounce.brightness_max = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Bounce.brightness = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Bounce.speed_min = CLEVO_LIGHTBAR_SPEED_MIN; + Bounce.speed_max = CLEVO_LIGHTBAR_SPEED_MAX; + Bounce.speed = CLEVO_LIGHTBAR_SPEED_DEFAULT; + modes.push_back(Bounce); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = CLEVO_LIGHTBAR_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Marquee.color_mode = MODE_COLORS_NONE; + Marquee.brightness_min = CLEVO_LIGHTBAR_BRIGHTNESS_MIN; + Marquee.brightness_max = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Marquee.brightness = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Marquee.speed_min = CLEVO_LIGHTBAR_SPEED_MIN; + Marquee.speed_max = CLEVO_LIGHTBAR_SPEED_MAX; + Marquee.speed = CLEVO_LIGHTBAR_SPEED_DEFAULT; + modes.push_back(Marquee); + + mode Scan; + Scan.name = "Scan"; + Scan.value = CLEVO_LIGHTBAR_MODE_SCAN; + Scan.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Scan.color_mode = MODE_COLORS_PER_LED; + Scan.brightness_min = CLEVO_LIGHTBAR_BRIGHTNESS_MIN; + Scan.brightness_max = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Scan.brightness = CLEVO_LIGHTBAR_BRIGHTNESS_MAX; + Scan.speed_min = CLEVO_LIGHTBAR_SPEED_MIN; + Scan.speed_max = CLEVO_LIGHTBAR_SPEED_MAX; + Scan.speed = CLEVO_LIGHTBAR_SPEED_DEFAULT; + modes.push_back(Scan); + + mode Off; + Off.name = "Off"; + Off.value = CLEVO_LIGHTBAR_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_ClevoLightbar::~RGBController_ClevoLightbar() +{ + delete controller; +} + +void RGBController_ClevoLightbar::SetupZones() +{ + zone lightbar_zone; + lightbar_zone.name = "Lightbar"; + lightbar_zone.type = ZONE_TYPE_SINGLE; + lightbar_zone.leds_min = 1; + lightbar_zone.leds_max = 1; + lightbar_zone.leds_count = 1; + lightbar_zone.matrix_map = NULL; + zones.push_back(lightbar_zone); + + led lightbar_led; + lightbar_led.name = "Lightbar"; + leds.push_back(lightbar_led); + + SetupColors(); +} + +void RGBController_ClevoLightbar::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ClevoLightbar::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char green = RGBGetGValue(colors[0]); + unsigned char blue = RGBGetBValue(colors[0]); + + controller->SetColor(red, green, blue); + + /*---------------------------------------------------------*\ + | Re-apply current mode to maintain effect state | + \*---------------------------------------------------------*/ + unsigned char brightness = modes[active_mode].brightness; + unsigned char speed = modes[active_mode].speed; + unsigned char mode_value = modes[active_mode].value; + + controller->SetMode(mode_value, brightness, speed); +} + +void RGBController_ClevoLightbar::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ClevoLightbar::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ClevoLightbar::DeviceUpdateMode() +{ + if(modes[active_mode].value == CLEVO_LIGHTBAR_MODE_OFF) + { + controller->TurnOff(); + } + else + { + unsigned char brightness = modes[active_mode].brightness; + unsigned char speed = modes[active_mode].speed; + unsigned char mode_value = modes[active_mode].value; + + /*---------------------------------------------------------*\ + | Set color first for modes that use it | + \*---------------------------------------------------------*/ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + unsigned char red = RGBGetRValue(colors[0]); + unsigned char green = RGBGetGValue(colors[0]); + unsigned char blue = RGBGetBValue(colors[0]); + controller->SetColor(red, green, blue); + } + + controller->SetMode(mode_value, brightness, speed); + } +} diff --git a/Controllers/ClevoLightbarController/RGBController_ClevoLightbar.h b/Controllers/ClevoLightbarController/RGBController_ClevoLightbar.h new file mode 100644 index 0000000..dc88dc6 --- /dev/null +++ b/Controllers/ClevoLightbarController/RGBController_ClevoLightbar.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_ClevoLightbar.h | +| | +| Generic RGB Interface for Clevo laptop lightbar | +| | +| Kyle Cascade (kyle@cascade.family) 16 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ClevoLightbarController.h" + +class RGBController_ClevoLightbar : public RGBController +{ +public: + RGBController_ClevoLightbar(ClevoLightbarController* controller_ptr); + ~RGBController_ClevoLightbar(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ClevoLightbarController* controller; +}; diff --git a/Controllers/ColorfulGPUController/ColorfulGPUController.cpp b/Controllers/ColorfulGPUController/ColorfulGPUController.cpp new file mode 100644 index 0000000..e7c4aa0 --- /dev/null +++ b/Controllers/ColorfulGPUController/ColorfulGPUController.cpp @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| ColorfulGPUController.cpp | +| | +| Driver for Colorful GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "ColorfulGPUController.h" +#include "pci_ids.h" +#include "LogManager.h" + +ColorfulGPUController::ColorfulGPUController(i2c_smbus_interface* bus, colorful_gpu_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +ColorfulGPUController::~ColorfulGPUController() +{ + +} + +std::string ColorfulGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string ColorfulGPUController::GetDeviceName() +{ + return(name); +} + +void ColorfulGPUController::SetDirect(RGBColor color) +{ + uint8_t r = RGBGetRValue(color); + uint8_t g = RGBGetGValue(color); + uint8_t b = RGBGetBValue(color); + + if(this->bus->pci_subsystem_device == COLORFUL_IGAME_RTX_4070_VULCAN_OCV) + { + uint8_t data_pkt[COLORFUL_PACKET_LENGTH_V2] = { 0xAA, 0xEF, 0x01, 0x04, 0x88, 0x26 }; + for(int i=6; i < COLORFUL_PACKET_LENGTH_V2 -2; i = i + 3) + { + data_pkt[i] = r; + data_pkt[i+1] = g; + data_pkt[i+2] = b; + } + + int crc = 0; + for(int i = 0; i < COLORFUL_PACKET_LENGTH_V2 - 2; ++i) + { + crc += data_pkt[i]; + } + + data_pkt[COLORFUL_PACKET_LENGTH_V2 - 2] = crc & 0xFF; + data_pkt[COLORFUL_PACKET_LENGTH_V2 - 1] = crc >> 8; + + bus->i2c_write_block(dev, COLORFUL_PACKET_LENGTH_V2, data_pkt); + } + else + { + uint8_t data_pkt[COLORFUL_PACKET_LENGTH_V1] = { 0xAA, 0xEF, 0x12, 0x03, 0x01, 0xFF, r, g, b}; + + int crc = 0; + for(int i = 0; i < COLORFUL_PACKET_LENGTH_V1 - 2; ++i) + { + crc += data_pkt[i]; + } + + data_pkt[COLORFUL_PACKET_LENGTH_V1 - 2] = crc & 0xFF; + data_pkt[COLORFUL_PACKET_LENGTH_V1 - 1] = crc >> 8; + + bus->i2c_write_block(dev, COLORFUL_PACKET_LENGTH_V1, data_pkt); + } +} diff --git a/Controllers/ColorfulGPUController/ColorfulGPUController.h b/Controllers/ColorfulGPUController/ColorfulGPUController.h new file mode 100644 index 0000000..4bd28d8 --- /dev/null +++ b/Controllers/ColorfulGPUController/ColorfulGPUController.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| ColorfulGPUController.h | +| | +| Driver for Colorful GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +typedef unsigned char colorful_gpu_dev_id; + +#define COLORFUL_PACKET_LENGTH_V1 11 +#define COLORFUL_PACKET_LENGTH_V2 122 + +class ColorfulGPUController +{ +public: + ColorfulGPUController(i2c_smbus_interface* bus, colorful_gpu_dev_id dev, std::string dev_name); + ~ColorfulGPUController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetDirect(RGBColor color); + +private: + i2c_smbus_interface * bus; + colorful_gpu_dev_id dev; + std::string name; +}; diff --git a/Controllers/ColorfulGPUController/ColorfulGPUControllerDetect.cpp b/Controllers/ColorfulGPUController/ColorfulGPUControllerDetect.cpp new file mode 100644 index 0000000..9fc4616 --- /dev/null +++ b/Controllers/ColorfulGPUController/ColorfulGPUControllerDetect.cpp @@ -0,0 +1,80 @@ +/*---------------------------------------------------------*\ +| ColorfulGPUControllerDetect.cpp | +| | +| Detector for Colorful GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LogManager.h" +#include "ColorfulGPUController.h" +#include "RGBController_ColorfulGPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +bool TestForColorfulGPU(i2c_smbus_interface* bus, uint8_t i2c_addr) +{ + int pktsz; + const int read_sz = 0x40; + const int write_sz = 6; + + uint8_t data_pkt[write_sz] = { 0xAA, 0xEF, 0x81, 0x02, 0x1C, 0x02}; + bus->i2c_write_block(i2c_addr, write_sz, data_pkt); + + uint8_t read_pkt[read_sz] = {}; + pktsz = read_sz; + + int res = bus->i2c_read_block(i2c_addr, &pktsz, read_pkt); + + LOG_DEBUG("[ColorfulGPUController] Handshake: res: %d. Expected 0xAA, 0xEF, 0x81. Received: 0x%02X, 0x%02X, 0x%02X.", res, read_pkt[0], read_pkt[1], read_pkt[2]); + + return res >= 0 && (read_pkt[0] == 0xAA && read_pkt[1] == 0xEF && read_pkt[2] == 0x81); +} + +void DetectColorfulGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(TestForColorfulGPU(bus, i2c_addr)) + { + ColorfulGPUController* controller = new ColorfulGPUController(bus, i2c_addr, name); + RGBController_ColorfulGPU* rgb_controller = new RGBController_ColorfulGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Advanced OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ADVANCED_OC_12G_LV, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA106_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ultra W OC 12G L-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Ultra W OC LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060_ULTRAW_OC_12G_2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3070_ADVANCED_OCV, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3060 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3060TI_ADVANCED_OC, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3070 Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3070_ADVANCED_OCV, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3070 Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3070_ADVANCED_OCV2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3070_ULTRAW_OC, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3070 Ultra W OC LHR", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3070_ULTRAW_OC, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3070 Ti Ultra W OC LHR", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3070TI_ULTRAW_OC, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3070 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3070TI_ADVANCED_OCV2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3080 Advanced OC 10G-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3080_ADVANCED_OC_10G, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3080 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3080TI_ADVANCED_OCV2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3080 Ultra W OC 10G LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3080_ULTRAW_OC_10G, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 3080 Ultra W OC 10G LHR-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_3080_ULTRAW_OC_10G_2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Ti Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070TI_ADVANCED_OCV, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Ti SUPER Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_AD102_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070TI_SUPER_ADVANCED_OCV2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Ti SUPER Ultra W", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070TI_SUPER_ULTRA_W, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 SUPER Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070_SUPER_ULTRA_W_OC, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 Vulcan OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070_VULCAN_OCV, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4070 SUPER Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4070S_ULTRAW_OCV, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4080_ULTRAW_OCV, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4080 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4080_ULTRAW_OCV2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4090 Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4090_ADVANCED_OCV, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 4090 Advanced OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_4090_ADVANCED_OCV2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060_ULTRAW_OC, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ultra W OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060_ULTRAW_OC_2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ti Ultra W DUO OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5060 Ti Ultra W DUO OC", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5060TI_ULTRAW_DUO_OC_2, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5070_ULTRAW_OCV, 0x61); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 5070 Ultra W OC-V", DetectColorfulGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_5070_ULTRAW_OCV2, 0x61); \ No newline at end of file diff --git a/Controllers/ColorfulGPUController/RGBController_ColorfulGPU.cpp b/Controllers/ColorfulGPUController/RGBController_ColorfulGPU.cpp new file mode 100644 index 0000000..1316411 --- /dev/null +++ b/Controllers/ColorfulGPUController/RGBController_ColorfulGPU.cpp @@ -0,0 +1,92 @@ +/*---------------------------------------------------------*\ +| RGBController_ColorfulGPU.cpp | +| | +| RGBController for Colorful GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_ColorfulGPU.h" + +/**------------------------------------------------------------------*\ + @name Colorful GPU + @category GPU + @type I2C + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectColorfulGPUControllers + @comment This card only supports direct mode +\*-------------------------------------------------------------------*/ + +RGBController_ColorfulGPU::RGBController_ColorfulGPU(ColorfulGPUController * colorful_gpu_ptr) +{ + controller = colorful_gpu_ptr; + + name = controller->GetDeviceName(); + vendor = "Colorful"; + type = DEVICE_TYPE_GPU; + description = name; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + +} + +RGBController_ColorfulGPU::~RGBController_ColorfulGPU() +{ + delete controller; +} + +void RGBController_ColorfulGPU::SetupZones() +{ + zone new_zone; + + new_zone.name = "GPU"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(new_zone.leds_count); + leds[0].name = "GPU LED"; + + SetupColors(); +} + +void RGBController_ColorfulGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_ColorfulGPU::DeviceUpdateLEDs() +{ + controller->SetDirect(colors[0]); +} + +void RGBController_ColorfulGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ColorfulGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ColorfulGPU::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/ColorfulGPUController/RGBController_ColorfulGPU.h b/Controllers/ColorfulGPUController/RGBController_ColorfulGPU.h new file mode 100644 index 0000000..a12136d --- /dev/null +++ b/Controllers/ColorfulGPUController/RGBController_ColorfulGPU.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| RGBController_ColorfulGPU.h | +| | +| RGBController for Colorful GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ColorfulGPUController.h" + +class RGBController_ColorfulGPU : public RGBController +{ +public: + RGBController_ColorfulGPU(ColorfulGPUController* colorful_gpu_ptr); + ~RGBController_ColorfulGPU(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ColorfulGPUController* controller; +}; diff --git a/Controllers/ColorfulTuringGPUController/ColorfulTuringGPUController.cpp b/Controllers/ColorfulTuringGPUController/ColorfulTuringGPUController.cpp new file mode 100644 index 0000000..31b2a48 --- /dev/null +++ b/Controllers/ColorfulTuringGPUController/ColorfulTuringGPUController.cpp @@ -0,0 +1,166 @@ +/*---------------------------------------------------------*\ +| ColorfulTuringGPUController.cpp | +| | +| Driver for Colorful Turing GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "ColorfulTuringGPUController.h" + +ColorfulTuringGPUController::ColorfulTuringGPUController(i2c_smbus_interface* bus, colorful_gpu_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +ColorfulTuringGPUController::~ColorfulTuringGPUController() +{ + +} + +std::string ColorfulTuringGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string ColorfulTuringGPUController::GetDeviceName() +{ + return(name); +} + +int ColorfulTuringGPUController::GetMode() +{ + uint8_t data_pkt[COLORFUL_MODE_PACKET_LENGTH]; + int size = COLORFUL_MODE_PACKET_LENGTH; + bus->i2c_read_block(dev, &size, data_pkt); + + int mode = data_pkt[2]<<16 | data_pkt[3]<<8 | data_pkt[4]; + + return mode; +} + +RGBColor ColorfulTuringGPUController::GetColor() +{ + uint8_t data_pkt[COLORFUL_MODE_PACKET_LENGTH]; + int size = COLORFUL_MODE_PACKET_LENGTH; + bus->i2c_read_block(dev, &size, data_pkt); + + RGBColor color = ToRGBColor(data_pkt[5], data_pkt[6], data_pkt[7]); + + return color; +} + +void ColorfulTuringGPUController::SetStateDisplay(RGBColor color) +{ + uint8_t r = RGBGetRValue(color); + uint8_t g = RGBGetGValue(color); + uint8_t b = RGBGetBValue(color); + uint8_t data_pkt[COLORFUL_COLOR_PACKET_LENGTH] = { 0x08, 0x01, 0x32, 0x04, r, g, b}; + + int crc = 1; + + for(int i = 0; i < COLORFUL_COLOR_PACKET_LENGTH - 1; ++i) + { + crc += data_pkt[i]; + } + crc &= 0xFF; + crc = 256-crc; + + data_pkt[COLORFUL_COLOR_PACKET_LENGTH - 1] = crc & 0xFF; + + bus->i2c_write_block(dev, COLORFUL_COLOR_PACKET_LENGTH, data_pkt); +} + +void ColorfulTuringGPUController::SetDirect(RGBColor color, bool save) +{ + uint8_t r = RGBGetRValue(color); + uint8_t g = RGBGetGValue(color); + uint8_t b = RGBGetBValue(color); + uint8_t data_pkt[COLORFUL_COLOR_PACKET_LENGTH] = { 0x08, 0x0, 0x20, 0x10, r, g, b}; + if(save) + { + data_pkt[0] = 0x88; + data_pkt[1] = 0x02; + data_pkt[2] = 0x32; + data_pkt[3] = 0x02; + } + + int crc = 1; + + for(int i = 0; i < COLORFUL_COLOR_PACKET_LENGTH - 1; ++i) + { + crc += data_pkt[i]; + } + crc &= 0xFF; + crc = 256-crc; + + data_pkt[COLORFUL_COLOR_PACKET_LENGTH - 1] = crc & 0xFF; + + bus->i2c_write_block(dev, COLORFUL_COLOR_PACKET_LENGTH, data_pkt); +} + +void ColorfulTuringGPUController::SetBreathing(RGBColor color) +{ + uint8_t r = RGBGetRValue(color); + uint8_t g = RGBGetGValue(color); + uint8_t b = RGBGetBValue(color); + uint8_t data_pkt[COLORFUL_COLOR_PACKET_LENGTH] = { 0x88, 0x01, 0x32, 0x02, r, g, b}; + + int crc = 1; + + for(int i = 0; i < COLORFUL_COLOR_PACKET_LENGTH - 1; ++i) + { + crc += data_pkt[i]; + } + crc &= 0xFF; + crc = 256-crc; + + data_pkt[COLORFUL_COLOR_PACKET_LENGTH - 1] = crc & 0xFF; + + bus->i2c_write_block(dev, COLORFUL_COLOR_PACKET_LENGTH, data_pkt); +} + +void ColorfulTuringGPUController::SetOff() +{ + uint8_t data_pkt[COLORFUL_NON_COLOR_PACKET_LENGTH] = { 0x85, 0x00, 0x00, 0x0A }; + + int crc = 1; + + for(int i = 0; i < COLORFUL_NON_COLOR_PACKET_LENGTH - 1; ++i) + { + crc += data_pkt[i]; + } + crc &= 0xFF; + crc = 256-crc; + + data_pkt[COLORFUL_NON_COLOR_PACKET_LENGTH - 1] = crc & 0xFF; + + bus->i2c_write_block(dev, COLORFUL_NON_COLOR_PACKET_LENGTH, data_pkt); +} + +void ColorfulTuringGPUController::SetRainbow() +{ + uint8_t data_pkt[COLORFUL_NON_COLOR_PACKET_LENGTH] = { 0x85, 0x04, 0x32, 0x02 }; + + int crc = 1; + + for(int i = 0; i < COLORFUL_NON_COLOR_PACKET_LENGTH - 1; ++i) + { + crc += data_pkt[i]; + } + crc &= 0xFF; + crc = 256-crc; + + data_pkt[COLORFUL_NON_COLOR_PACKET_LENGTH - 1] = crc & 0xFF; + + bus->i2c_write_block(dev, COLORFUL_NON_COLOR_PACKET_LENGTH, data_pkt); +} diff --git a/Controllers/ColorfulTuringGPUController/ColorfulTuringGPUController.h b/Controllers/ColorfulTuringGPUController/ColorfulTuringGPUController.h new file mode 100644 index 0000000..babfeeb --- /dev/null +++ b/Controllers/ColorfulTuringGPUController/ColorfulTuringGPUController.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| ColorfulTuringGPUController.h | +| | +| Driver for Colorful Turing GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +typedef unsigned char colorful_gpu_dev_id; + +#define COLORFUL_COLOR_PACKET_LENGTH 8 +#define COLORFUL_NON_COLOR_PACKET_LENGTH 5 +#define COLORFUL_MODE_PACKET_LENGTH 0x1B + +enum +{ + COLORFUL_TURING_GPU_RGB_MODE_STATE_DISPLAY = 0x013204, + COLORFUL_TURING_GPU_RGB_MODE_OFF = 0x00000A, + COLORFUL_TURING_GPU_RGB_MODE_STATIC = 0x023202, + COLORFUL_TURING_GPU_RGB_MODE_RAINBOW = 0x043202, + COLORFUL_TURING_GPU_RGB_MODE_BREATHING = 0x013202, +}; + +class ColorfulTuringGPUController +{ +public: + ColorfulTuringGPUController(i2c_smbus_interface* bus, colorful_gpu_dev_id dev, std::string dev_name); + ~ColorfulTuringGPUController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + int GetMode(); + RGBColor GetColor(); + void SetDirect(RGBColor color, bool save); + void SetStateDisplay(RGBColor color); + void SetBreathing(RGBColor color); + void SetOff(); + void SetRainbow(); + + +private: + i2c_smbus_interface * bus; + colorful_gpu_dev_id dev; + std::string name; +}; diff --git a/Controllers/ColorfulTuringGPUController/ColorfulTuringGPUControllerDetect.cpp b/Controllers/ColorfulTuringGPUController/ColorfulTuringGPUControllerDetect.cpp new file mode 100644 index 0000000..886eb46 --- /dev/null +++ b/Controllers/ColorfulTuringGPUController/ColorfulTuringGPUControllerDetect.cpp @@ -0,0 +1,28 @@ +/*---------------------------------------------------------*\ +| ColorfulTuringGPUControllerDetect.cpp | +| | +| Driver for Colorful Turing GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ColorfulTuringGPUController.h" +#include "RGBController_ColorfulTuringGPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +void DetectColorfulTuringGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(bus->port_id == 1) + { + ColorfulTuringGPUController* controller = new ColorfulTuringGPUController(bus, i2c_addr, name); + RGBController_ColorfulTuringGPU* rgb_controller = new RGBController_ColorfulTuringGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 2070 SUPER Advanced OC-V", DetectColorfulTuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_2070_SUPER_ADVANCED_OCV, 0x50); +REGISTER_I2C_PCI_DETECTOR("iGame GeForce RTX 2070 SUPER Advanced OC-V", DetectColorfulTuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, COLORFUL_SUB_VEN, COLORFUL_IGAME_RTX_2070_SUPER_ADVANCED_OCV2, 0x50); diff --git a/Controllers/ColorfulTuringGPUController/RGBController_ColorfulTuringGPU.cpp b/Controllers/ColorfulTuringGPUController/RGBController_ColorfulTuringGPU.cpp new file mode 100644 index 0000000..66511ab --- /dev/null +++ b/Controllers/ColorfulTuringGPUController/RGBController_ColorfulTuringGPU.cpp @@ -0,0 +1,167 @@ +/*---------------------------------------------------------*\ +| RGBController_ColorfulTuringGPU.cpp | +| | +| RGBController for Colorful Turing GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_ColorfulTuringGPU.h" + +/**------------------------------------------------------------------*\ + @name Colorful GPU + @category GPU + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectColorfulTuringGPUControllers + @comment This card supports off, direct, rainbow and pulse mode. +\*-------------------------------------------------------------------*/ + +RGBController_ColorfulTuringGPU::RGBController_ColorfulTuringGPU(ColorfulTuringGPUController * colorful_gpu_ptr) +{ + controller = colorful_gpu_ptr; + + name = controller->GetDeviceName(); + vendor = "Colorful"; + type = DEVICE_TYPE_GPU; + description = name; + location = controller->GetDeviceLocation(); + + mode Off; + Off.name = "Off"; + Off.value = COLORFUL_TURING_GPU_RGB_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = COLORFUL_TURING_GPU_RGB_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode StateDisplay; + StateDisplay.name = "State Display"; + StateDisplay.value = COLORFUL_TURING_GPU_RGB_MODE_STATE_DISPLAY; + StateDisplay.flags = MODE_FLAG_HAS_PER_LED_COLOR; + StateDisplay.color_mode = MODE_COLORS_PER_LED; + modes.push_back(StateDisplay); + + mode Rainbow; + Rainbow.name = "Spectrum Cycle"; + Rainbow.value = COLORFUL_TURING_GPU_RGB_MODE_RAINBOW; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = COLORFUL_TURING_GPU_RGB_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + SetupZones(); + + // Initialize active mode + active_mode = getModeIndex(controller->GetMode()); + colors[0] = controller->GetColor(); + +} + +RGBController_ColorfulTuringGPU::~RGBController_ColorfulTuringGPU() +{ + delete controller; +} + +int RGBController_ColorfulTuringGPU::getModeIndex(int mode_value) +{ + for(unsigned int mode_index = 0; mode_index < modes.size(); mode_index++) + { + if(modes[mode_index].value == mode_value) + { + return(mode_index); + } + } + + return(0); +} + +void RGBController_ColorfulTuringGPU::SetupZones() +{ + zone new_zone; + + new_zone.name = "GPU"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(new_zone.leds_count); + leds[0].name = "GPU LED"; + + SetupColors(); +} + +void RGBController_ColorfulTuringGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_ColorfulTuringGPU::DeviceUpdateLEDs() +{ + switch(modes[active_mode].value) + { + case COLORFUL_TURING_GPU_RGB_MODE_BREATHING: + controller->SetBreathing(colors[0]); + break; + case COLORFUL_TURING_GPU_RGB_MODE_OFF: + controller->SetOff(); + break; + case COLORFUL_TURING_GPU_RGB_MODE_RAINBOW: + controller->SetRainbow(); + break; + case COLORFUL_TURING_GPU_RGB_MODE_STATE_DISPLAY: + controller->SetStateDisplay(colors[0]); + break; + case COLORFUL_TURING_GPU_RGB_MODE_STATIC: + controller->SetDirect(colors[0], false); + break; + default: + controller->SetDirect(colors[0], false); + } +} + +void RGBController_ColorfulTuringGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ColorfulTuringGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ColorfulTuringGPU::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_ColorfulTuringGPU::DeviceSaveMode() +{ + switch(modes[active_mode].value) + { + case COLORFUL_TURING_GPU_RGB_MODE_STATIC: + controller->SetDirect(colors[0], true); + break; + default: + DeviceUpdateLEDs(); + } +} diff --git a/Controllers/ColorfulTuringGPUController/RGBController_ColorfulTuringGPU.h b/Controllers/ColorfulTuringGPUController/RGBController_ColorfulTuringGPU.h new file mode 100644 index 0000000..f0bec5b --- /dev/null +++ b/Controllers/ColorfulTuringGPUController/RGBController_ColorfulTuringGPU.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_ColorfulTuringGPU.h | +| | +| RGBController for Colorful Turing GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ColorfulTuringGPUController.h" + +class RGBController_ColorfulTuringGPU : public RGBController +{ +public: + RGBController_ColorfulTuringGPU(ColorfulTuringGPUController* colorful_gpu_ptr); + ~RGBController_ColorfulTuringGPU(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + ColorfulTuringGPUController* controller; + int getModeIndex(int mode_value); +}; + + diff --git a/Controllers/CoolerMasterController/CMARGBController/CMARGBController.cpp b/Controllers/CoolerMasterController/CMARGBController/CMARGBController.cpp new file mode 100644 index 0000000..aaa1761 --- /dev/null +++ b/Controllers/CoolerMasterController/CMARGBController/CMARGBController.cpp @@ -0,0 +1,298 @@ +/*---------------------------------------------------------*\ +| CMARGBController.cpp | +| | +| Driver for Cooler Master ARGB controller | +| | +| Chris M (Dr_No) 10 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CMARGBController.h" +#include "StringUtils.h" + +/*---------------------------------------------------------*\ +| Map to convert port index to port ID used in protocol | +\*---------------------------------------------------------*/ +static unsigned char cm_argb_port_index_to_id[5] = +{ + CM_ARGB_PORT_ARGB_1, + CM_ARGB_PORT_ARGB_2, + CM_ARGB_PORT_ARGB_3, + CM_ARGB_PORT_ARGB_4, + CM_ARGB_PORT_RGB +}; + +CMARGBController::CMARGBController(hid_device* dev_handle, char *path) +{ + dev = dev_handle; + location = path; + + /*-----------------------------------------------------*\ + | Get device name from HID manufacturer and product | + | strings | + \*-----------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); +} + +CMARGBController::~CMARGBController() +{ + hid_close(dev); +} + +std::string CMARGBController::GetDeviceName() +{ + return(device_name); +} + +std::string CMARGBController::GetLocation() +{ + return("HID: " + location); +} + +std::string CMARGBController::GetVersion() +{ + /*-----------------------------------------------------*\ + | This device uses the serial value to determine the | + | version. It does not report a proper unique serial. | + \*-----------------------------------------------------*/ + std::string serial_string = GetSerial(); + + if(serial_string == CM_ARGB_FW0023) + { + return("0023"); + } + else if(serial_string == CM_ARGB_FW0028) + { + return("0028"); + } + else + { + return("Unsupported"); + } +} + +std::string CMARGBController::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void CMARGBController::GetPortStatus + ( + unsigned char port_idx, + unsigned char* port_mode, + unsigned char* port_speed, + unsigned char* port_brightness, + bool* port_random, + unsigned char* port_red, + unsigned char* port_green, + unsigned char* port_blue + ) +{ + unsigned char buffer[CM_ARGB_PACKET_SIZE] = {0x00, 0x80, 0x0B, 0x01}; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + int rgb_offset = 0; + int zone; + + /*-----------------------------------------------------*\ + | RGB port is handled differently from ARGB ports | + \*-----------------------------------------------------*/ + if(cm_argb_port_index_to_id[port_idx] != CM_ARGB_PORT_RGB) + { + zone = cm_argb_port_index_to_id[port_idx]; + buffer[CM_ARGB_COMMAND_BYTE] = 0x0B; + } + else + { + zone = 0x00; + buffer[CM_ARGB_COMMAND_BYTE] = 0x0A; + rgb_offset = 1; + } + + /*-----------------------------------------------------*\ + | If this is the group then just return the first | + | status | + \*-----------------------------------------------------*/ + buffer[CM_ARGB_ZONE_BYTE] = ( zone > 0x08 ) ? 0x01 : zone; + + /*-----------------------------------------------------*\ + | Send the command and read the response | + \*-----------------------------------------------------*/ + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_ARGB_INTERRUPT_TIMEOUT); + + /*-----------------------------------------------------*\ + | Read data out of response | + \*-----------------------------------------------------*/ + *port_mode = buffer[4 - rgb_offset]; + *port_random = (buffer[5 - rgb_offset] == 0x00); + *port_speed = buffer[6 - rgb_offset]; + *port_brightness = buffer[7 - rgb_offset]; + *port_red = buffer[8 - rgb_offset]; + *port_green = buffer[9 - rgb_offset]; + *port_blue = buffer[10 - rgb_offset]; +} + +void CMARGBController::SetPortLEDCount(unsigned char port_idx, unsigned char led_count) +{ + unsigned char buffer[CM_ARGB_PACKET_SIZE] = {0x00, 0x80, 0x0D, 0x02}; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + + buffer[CM_ARGB_ZONE_BYTE] = cm_argb_port_index_to_id[port_idx]; + buffer[CM_ARGB_MODE_BYTE] = led_count; + buffer[CM_ARGB_COLOUR_INDEX_BYTE] = 1; + + /*-----------------------------------------------------*\ + | Send the command | + \*-----------------------------------------------------*/ + hid_write(dev, buffer, buffer_size); +} + +void CMARGBController::SetPortMode + ( + unsigned char port_idx, + unsigned char port_mode, + unsigned char port_speed, + unsigned char port_brightness, + bool port_random, + unsigned char port_red, + unsigned char port_green, + unsigned char port_blue + ) +{ + unsigned char buffer[CM_ARGB_PACKET_SIZE] = {0x00}; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + bool boolARGB_header = (cm_argb_port_index_to_id[port_idx] != CM_ARGB_PORT_RGB); + bool boolPassthru = (port_mode == CM_ARGB_MODE_PASSTHRU) || (port_mode == CM_RGB_MODE_PASSTHRU); + bool boolDirect = (port_mode == CM_ARGB_MODE_DIRECT); + unsigned char function = boolPassthru ? (boolARGB_header ? 0x02 : 0x04) : (boolARGB_header ? 0x01 : 0x03); + buffer[CM_ARGB_REPORT_BYTE] = 0x80; + buffer[CM_ARGB_COMMAND_BYTE] = 0x01; + + if(boolDirect) + { + buffer[CM_ARGB_FUNCTION_BYTE] = 0x01; + buffer[CM_ARGB_ZONE_BYTE] = 0x02; + + /*-------------------------------------------------*\ + | Send the command | + \*-------------------------------------------------*/ + hid_write(dev, buffer, buffer_size); + + /*-------------------------------------------------*\ + | Direct mode is now set up and no other mode | + | packet is required | + \*-------------------------------------------------*/ + return; + } + + buffer[CM_ARGB_FUNCTION_BYTE] = function; + + /*-----------------------------------------------------*\ + | Send the command | + \*-----------------------------------------------------*/ + hid_write(dev, buffer, buffer_size); + + /*-----------------------------------------------------*\ + | ARGB ports send command 0x0B, RGB port sends 0x04 | + \*-----------------------------------------------------*/ + if(boolARGB_header) + { + buffer[CM_ARGB_COMMAND_BYTE] = 0x0B; + buffer[CM_ARGB_FUNCTION_BYTE] = (false) ? 0x01 : 0x02; + buffer[CM_ARGB_ZONE_BYTE] = cm_argb_port_index_to_id[port_idx]; + buffer[CM_ARGB_MODE_BYTE] = port_mode; + buffer[CM_ARGB_COLOUR_INDEX_BYTE] = port_random ? 0x00 : 0x10; + buffer[CM_ARGB_SPEED_BYTE] = port_speed; + buffer[CM_ARGB_BRIGHTNESS_BYTE] = port_brightness; + buffer[CM_ARGB_RED_BYTE] = port_red; + buffer[CM_ARGB_GREEN_BYTE] = port_green; + buffer[CM_ARGB_BLUE_BYTE] = port_blue; + } + else + { + buffer[CM_ARGB_COMMAND_BYTE] = boolPassthru ? 0x01 : 0x04; + buffer[CM_ARGB_MODE_BYTE + CM_RGB_OFFSET] = port_mode; + buffer[CM_ARGB_COLOUR_INDEX_BYTE + CM_RGB_OFFSET] = port_random ? 0x00 : 0x10; + buffer[CM_ARGB_SPEED_BYTE + CM_RGB_OFFSET] = port_speed; + buffer[CM_ARGB_BRIGHTNESS_BYTE + CM_RGB_OFFSET] = port_brightness; + buffer[CM_ARGB_RED_BYTE + CM_RGB_OFFSET] = port_red; + buffer[CM_ARGB_GREEN_BYTE + CM_RGB_OFFSET] = port_green; + buffer[CM_ARGB_BLUE_BYTE + CM_RGB_OFFSET] = port_blue; + } + + /*-----------------------------------------------------*\ + | Send the command and wait for response | + \*-----------------------------------------------------*/ + hid_write(dev, buffer, buffer_size); +} + +void CMARGBController::SetPortLEDsDirect(unsigned char port_idx, RGBColor *led_colours, unsigned int led_count) +{ + const unsigned char buffer_size = CM_ARGB_PACKET_SIZE; + unsigned char buffer[buffer_size] = { 0x00, 0x00, 0x07, 0x02 }; + unsigned char packet_count = 0; + std::vector colours; + + /*-----------------------------------------------------*\ + | Set up the RGB triplets to send | + \*-----------------------------------------------------*/ + for(unsigned int i = 0; i < led_count; i++) + { + RGBColor colour = led_colours[i]; + + colours.push_back(RGBGetRValue(colour)); + colours.push_back(RGBGetGValue(colour)); + colours.push_back(RGBGetBValue(colour)); + } + + buffer[CM_ARGB_FUNCTION_BYTE] = port_idx; + buffer[CM_ARGB_ZONE_BYTE] = led_count; + unsigned char buffer_idx = CM_ARGB_MODE_BYTE; + + for(std::vector::iterator it = colours.begin(); it != colours.end(); buffer_idx = CM_ARGB_COMMAND_BYTE) + { + /*-------------------------------------------------*\ + | Fill the write buffer till its full or the | + | colour buffer is empty | + \*-------------------------------------------------*/ + buffer[CM_ARGB_REPORT_BYTE] = packet_count; + while((buffer_idx < buffer_size) && (it != colours.end())) + { + buffer[buffer_idx] = *it; + buffer_idx++; + it++; + } + + if(it == colours.end()) + { + buffer[CM_ARGB_REPORT_BYTE] += 0x80; + } + + /*-------------------------------------------------*\ + | Send the buffer | + \*-------------------------------------------------*/ + hid_write(dev, buffer, buffer_size); + + /*-------------------------------------------------*\ + | Reset the write buffer | + \*-------------------------------------------------*/ + memset(buffer, 0x00, buffer_size ); + packet_count++; + } +} diff --git a/Controllers/CoolerMasterController/CMARGBController/CMARGBController.h b/Controllers/CoolerMasterController/CMARGBController/CMARGBController.h new file mode 100644 index 0000000..7e5282f --- /dev/null +++ b/Controllers/CoolerMasterController/CMARGBController/CMARGBController.h @@ -0,0 +1,136 @@ +/*---------------------------------------------------------*\ +| CMARGBController.h | +| | +| Driver for Cooler Master ARGB controller | +| | +| Chris M (Dr_No) 10 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include "RGBController.h" + +#define CM_ARGB_COLOUR_MODE_DATA_SIZE (sizeof(colour_mode_data[0]) / sizeof(colour_mode_data[0][0])) +#define CM_ARGB_HEADER_DATA_SIZE (sizeof(argb_header_data) / sizeof(argb_headers) ) +#define CM_ARGB_INTERRUPT_TIMEOUT 250 +#define CM_ARGB_PACKET_SIZE 65 +#define CM_ARGB_DEVICE_NAME_SIZE (sizeof(device_name) / sizeof(device_name[ 0 ])) +#define CM_RGB_OFFSET -2 +#define HID_MAX_STR 255 + +#define CM_ARGB_BRIGHTNESS_MAX 255 +#define CM_ARGB_FW0000 std::string("A201804091608") +#define CM_ARGB_FW0023 std::string("A202011171238") +#define CM_ARGB_FW0028 std::string("A202105291658") + +enum +{ + CM_ARGB_REPORT_BYTE = 1, + CM_ARGB_COMMAND_BYTE = 2, + CM_ARGB_FUNCTION_BYTE = 3, + CM_ARGB_ZONE_BYTE = 4, + CM_ARGB_MODE_BYTE = 5, + CM_ARGB_COLOUR_INDEX_BYTE = 6, + CM_ARGB_SPEED_BYTE = 7, + CM_ARGB_BRIGHTNESS_BYTE = 8, + CM_ARGB_RED_BYTE = 9, + CM_ARGB_GREEN_BYTE = 10, + CM_ARGB_BLUE_BYTE = 11 +}; + +enum +{ + CM_ARGB_PORT_ARGB_1 = 0x01, + CM_ARGB_PORT_ARGB_2 = 0x02, + CM_ARGB_PORT_ARGB_3 = 0x04, + CM_ARGB_PORT_ARGB_4 = 0x08, + CM_ARGB_PORT_RGB = 0xFE, +}; + +enum +{ + CM_RGB_MODE_MIRAGE = 0x01, //Mirage + CM_RGB_MODE_FLASH = 0x02, //Flash + CM_RGB_MODE_BREATHING = 0x03, //Breathing + CM_RGB_MODE_STATIC = 0x05, //Static + CM_RGB_MODE_OFF = 0x06, //Turn off + CM_RGB_MODE_PASSTHRU = 0xFF //Motherboard Pass Thru Mode +}; + +enum +{ + CM_ARGB_MODE_OFF = 0x0B, //Turn off + CM_ARGB_MODE_SPECTRUM = 0x01, //Spectrum Mode + CM_ARGB_MODE_RELOAD = 0x02, //Reload Mode + CM_ARGB_MODE_RECOIL = 0x03, //Recoil Mode + CM_ARGB_MODE_BREATHING = 0x04, //Breathing Mode + CM_ARGB_MODE_REFILL = 0x05, //Refill Mode + CM_ARGB_MODE_DEMO = 0x06, //Demo Mode + CM_ARGB_MODE_FILLFLOW = 0x08, //Fill Flow Mode + CM_ARGB_MODE_RAINBOW = 0x09, //Rainbow Mode + CM_ARGB_MODE_STATIC = 0x0A, //Static Mode + CM_ARGB_MODE_DIRECT = 0xFE, //Direct Led Control + CM_ARGB_MODE_PASSTHRU = 0xFF //Motherboard Pass Thru Mode +}; + +enum +{ + CM_ARGB_SPEED_SLOWEST = 0x00, // Slowest speed + CM_ARGB_SPEED_SLOW = 0x01, // Slower speed + CM_ARGB_SPEED_NORMAL = 0x02, // Normal speed + CM_ARGB_SPEED_FAST = 0x03, // Fast speed + CM_ARGB_SPEED_FASTEST = 0x04, // Fastest speed +}; + +class CMARGBController +{ +public: + CMARGBController(hid_device* dev_handle, char* path); + ~CMARGBController(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + std::string GetVersion(); + + void GetPortStatus + ( + unsigned char port_idx, + unsigned char* port_mode, + unsigned char* port_speed, + unsigned char* port_brightness, + bool* port_random, + unsigned char* port_red, + unsigned char* port_green, + unsigned char* port_blue + ); + + void SetPortLEDCount(unsigned char port_idx, unsigned char led_count); + + void SetPortMode + ( + unsigned char port_idx, + unsigned char port_mode, + unsigned char port_speed, + unsigned char port_brightness, + bool port_random, + unsigned char port_red, + unsigned char port_green, + unsigned char port_blue + ); + + void SetPortLEDsDirect(unsigned char port_idx, RGBColor *led_colours, unsigned int led_count); + +private: + hid_device* dev; + std::string device_name; + std::string location; +}; diff --git a/Controllers/CoolerMasterController/CMARGBController/RGBController_CMARGBController.cpp b/Controllers/CoolerMasterController/CMARGBController/RGBController_CMARGBController.cpp new file mode 100644 index 0000000..a44a6b5 --- /dev/null +++ b/Controllers/CoolerMasterController/CMARGBController/RGBController_CMARGBController.cpp @@ -0,0 +1,440 @@ +/*---------------------------------------------------------*\ +| RGBController_CMARGBController.cpp | +| | +| RGBController for Cooler Master ARGB controller | +| | +| Chris M (Dr_No) 14 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CMARGBController.h" + +/**------------------------------------------------------------------*\ + @name Coolermaster ARGB + @category LEDStrip + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCoolerMasterARGB + @comment The Coolermaster ARGB device supports `Direct` mode from + firmware 0028 onwards. Check the serial number for the date + "A202105291658" or newer. +\*-------------------------------------------------------------------*/ + +RGBController_CMARGBController::RGBController_CMARGBController(CMARGBController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Cooler Master"; + type = DEVICE_TYPE_LEDSTRIP; + description = "Cooler Master ARGB Controller Device"; + version = controller->GetVersion(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + /*-----------------------------------------------------*\ + | The ARGB ports support more modes than the RGB port. | + | Define all of the modes the ARGB ports support and | + | map RGB modes to them as best as we can. Per-zone | + | support will be added in the future. | + \*-----------------------------------------------------*/ + mode Off; + Off.name = "Off"; + Off.value = CM_ARGB_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Reload; + Reload.name = "Reload"; + Reload.value = CM_ARGB_MODE_RELOAD; + Reload.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Reload.speed_min = CM_ARGB_SPEED_SLOWEST; + Reload.speed_max = CM_ARGB_SPEED_FASTEST; + Reload.speed = CM_ARGB_SPEED_NORMAL; + Reload.brightness_min = 0; + Reload.brightness_max = CM_ARGB_BRIGHTNESS_MAX; + Reload.brightness = CM_ARGB_BRIGHTNESS_MAX; + Reload.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reload.colors_min = 1; + Reload.colors_max = 1; + Reload.colors.resize(Reload.colors_max); + modes.push_back(Reload); + + mode Recoil; + Recoil.name = "Recoil"; + Recoil.value = CM_ARGB_MODE_RECOIL; + Recoil.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Recoil.color_mode = MODE_COLORS_MODE_SPECIFIC; + Recoil.speed_min = CM_ARGB_SPEED_SLOWEST; + Recoil.speed_max = CM_ARGB_SPEED_FASTEST; + Recoil.speed = CM_ARGB_SPEED_NORMAL; + Recoil.brightness_min = 0; + Recoil.brightness_max = CM_ARGB_BRIGHTNESS_MAX; + Recoil.brightness = CM_ARGB_BRIGHTNESS_MAX; + Recoil.colors_min = 1; + Recoil.colors_max = 1; + Recoil.colors.resize(Recoil.colors_max); + modes.push_back(Recoil); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_ARGB_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed_min = CM_ARGB_SPEED_SLOWEST; + Breathing.speed_max = CM_ARGB_SPEED_FASTEST; + Breathing.speed = CM_ARGB_SPEED_NORMAL; + Breathing.brightness_min = 0; + Breathing.brightness_max = CM_ARGB_BRIGHTNESS_MAX; + Breathing.brightness = CM_ARGB_BRIGHTNESS_MAX; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(Breathing.colors_max); + modes.push_back(Breathing); + + mode Refill; + Refill.name = "Refill"; + Refill.value = CM_ARGB_MODE_REFILL; + Refill.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Refill.color_mode = MODE_COLORS_MODE_SPECIFIC; + Refill.speed_min = CM_ARGB_SPEED_SLOWEST; + Refill.speed_max = CM_ARGB_SPEED_FASTEST; + Refill.speed = CM_ARGB_SPEED_NORMAL; + Refill.brightness_min = 0; + Refill.brightness_max = CM_ARGB_BRIGHTNESS_MAX; + Refill.brightness = CM_ARGB_BRIGHTNESS_MAX; + Refill.colors_min = 1; + Refill.colors_max = 1; + Refill.colors.resize(Refill.colors_max); + modes.push_back(Refill); + + mode Demo; + Demo.name = "Demo"; + Demo.value = CM_ARGB_MODE_DEMO; + Demo.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Demo.color_mode = MODE_COLORS_NONE; + Demo.speed_min = CM_ARGB_SPEED_SLOWEST; + Demo.speed_max = CM_ARGB_SPEED_FASTEST; + Demo.speed = CM_ARGB_SPEED_NORMAL; + Demo.brightness_min = 0; + Demo.brightness_max = CM_ARGB_BRIGHTNESS_MAX; + Demo.brightness = CM_ARGB_BRIGHTNESS_MAX; + modes.push_back(Demo); + + mode Spectrum; + Spectrum.name = "Rainbow Wave"; + Spectrum.value = CM_ARGB_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.speed_min = CM_ARGB_SPEED_SLOWEST; + Spectrum.speed_max = CM_ARGB_SPEED_FASTEST; + Spectrum.speed = CM_ARGB_SPEED_NORMAL; + Spectrum.brightness_min = 0; + Spectrum.brightness_max = CM_ARGB_BRIGHTNESS_MAX; + Spectrum.brightness = CM_ARGB_BRIGHTNESS_MAX; + modes.push_back(Spectrum); + + mode FillFlow; + FillFlow.name = "Fill Flow"; + FillFlow.value = CM_ARGB_MODE_FILLFLOW; + FillFlow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + FillFlow.color_mode = MODE_COLORS_NONE; + FillFlow.speed_min = CM_ARGB_SPEED_SLOWEST; + FillFlow.speed_max = CM_ARGB_SPEED_FASTEST; + FillFlow.speed = CM_ARGB_SPEED_NORMAL; + FillFlow.brightness_min = 0; + FillFlow.brightness_max = CM_ARGB_BRIGHTNESS_MAX; + FillFlow.brightness = CM_ARGB_BRIGHTNESS_MAX; + modes.push_back(FillFlow); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = CM_ARGB_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed_min = CM_ARGB_SPEED_SLOWEST; + Rainbow.speed_max = CM_ARGB_SPEED_FASTEST; + Rainbow.speed = CM_ARGB_SPEED_NORMAL; + Rainbow.brightness_min = 0; + Rainbow.brightness_max = CM_ARGB_BRIGHTNESS_MAX; + Rainbow.brightness = CM_ARGB_BRIGHTNESS_MAX; + modes.push_back(Rainbow); + + mode Static; + Static.name = "Static"; + Static.value = CM_ARGB_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.speed_min = CM_ARGB_SPEED_SLOWEST; + Static.speed_max = CM_ARGB_SPEED_FASTEST; + Static.speed = CM_ARGB_SPEED_NORMAL; + Static.brightness_min = 0; + Static.brightness_max = CM_ARGB_BRIGHTNESS_MAX; + Static.brightness = CM_ARGB_BRIGHTNESS_MAX; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + modes.push_back(Static); + + mode Direct; + Direct.name = (serial >= CM_ARGB_FW0028) ? "Direct" : "Custom"; + Direct.value = CM_ARGB_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode PassThru; + PassThru.name = "Pass Thru"; + PassThru.value = CM_ARGB_MODE_PASSTHRU; + PassThru.flags = 0; + PassThru.color_mode = MODE_COLORS_NONE; + modes.push_back(PassThru); + + SetupZones(); + + /*-----------------------------------------------------*\ + | Initialize the active mode to port 0 | + \*-----------------------------------------------------*/ + unsigned char port_mode; + unsigned char port_speed; + unsigned char port_brightness; + bool port_random; + unsigned char port_red; + unsigned char port_green; + unsigned char port_blue; + + controller->GetPortStatus(0, &port_mode, &port_speed, &port_brightness, &port_random, &port_red, &port_green, &port_blue); + + for(std::size_t mode_idx = 0; mode_idx < modes.size(); mode_idx++) + { + if(modes[mode_idx].value == port_mode) + { + active_mode = (int)mode_idx; + + if((modes[mode_idx].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) && (modes[mode_idx].colors.size() > 0)) + { + modes[mode_idx].colors[0] = ToRGBColor(port_red, port_green, port_blue); + } + + if(modes[mode_idx].flags & MODE_FLAG_HAS_SPEED) + { + modes[mode_idx].speed = port_speed; + } + + if(modes[mode_idx].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + modes[mode_idx].brightness = port_brightness; + } + + if(modes[mode_idx].flags & MODE_FLAG_HAS_RANDOM_COLOR) + { + if(port_random) + { + modes[mode_idx].color_mode = MODE_COLORS_RANDOM; + } + } + + break; + } + } +} + +RGBController_CMARGBController::~RGBController_CMARGBController() +{ + delete controller; +} + +void RGBController_CMARGBController::SetupZones() +{ + /*-----------------------------------------------------*\ + | Only set LED count on the first run | + \*-----------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-----------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-----------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(5); + + /*-----------------------------------------------------*\ + | Set up addressable zones | + \*-----------------------------------------------------*/ + for(unsigned int channel_idx = 0; channel_idx < 4; channel_idx++) + { + char ch_idx_string[2]; + snprintf(ch_idx_string, 2, "%d", channel_idx + 1); + + zones[channel_idx].name = "Addressable RGB Header "; + zones[channel_idx].name.append(ch_idx_string); + zones[channel_idx].type = ZONE_TYPE_LINEAR; + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = 48; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + + zones[channel_idx].matrix_map = NULL; + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + char led_idx_string[4]; + snprintf(led_idx_string, 4, "%d", led_ch_idx + 1); + + led new_led; + new_led.name = zones[channel_idx].name; + new_led.name.append(", LED "); + new_led.name.append(led_idx_string); + new_led.value = channel_idx; + + leds.push_back(new_led); + } + } + + /*-----------------------------------------------------*\ + | Set up RGB zone | + \*-----------------------------------------------------*/ + zones[4].name = "RGB Header"; + zones[4].type = ZONE_TYPE_SINGLE; + zones[4].leds_min = 1; + zones[4].leds_max = 1; + zones[4].leds_count = 1; + zones[4].matrix_map = NULL; + + led new_led; + new_led.name = "RGB Header"; + new_led.value = 4; + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_CMARGBController::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + controller->SetPortLEDCount(zone, zones[zone].leds_count); + + SetupZones(); + } +} + +void RGBController_CMARGBController::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + UpdateZoneLEDs((int)zone_idx); + } +} + +void RGBController_CMARGBController::UpdateZoneLEDs(int zone) +{ + /*-----------------------------------------------------*\ + | The RGB zone doesn't have a separate Direct mode, so | + | use static mode with the per-LED color for it | + \*-----------------------------------------------------*/ + if(zone < 4) + { + controller->SetPortLEDsDirect(zone, zones[zone].colors, zones[zone].leds_count); + } + else + { + controller->SetPortMode(zone, CM_RGB_MODE_STATIC, 0, 255, false, RGBGetRValue(zones[zone].colors[0]), RGBGetGValue(zones[zone].colors[0]), RGBGetBValue(zones[zone].colors[0])); + } +} + +void RGBController_CMARGBController::UpdateSingleLED(int led) +{ + unsigned int zone_idx = leds[led].value; + + UpdateZoneLEDs(zone_idx); +} + +void RGBController_CMARGBController::DeviceUpdateMode() +{ + /*-----------------------------------------------------*\ + | Determine mode parameters | + \*-----------------------------------------------------*/ + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + RGBColor color = (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) ? modes[active_mode].colors[0] : 0; + int rgb_mode; + bool rgb_random = random; + + /*-----------------------------------------------------*\ + | Map ARGB modes with the equivalent RGB modes | + \*-----------------------------------------------------*/ + switch(modes[active_mode].value) + { + case CM_ARGB_MODE_SPECTRUM: + case CM_ARGB_MODE_FILLFLOW: + case CM_ARGB_MODE_RAINBOW: + rgb_mode = CM_RGB_MODE_MIRAGE; + rgb_random = true; + break; + + case CM_ARGB_MODE_RELOAD: + case CM_ARGB_MODE_RECOIL: + rgb_mode = CM_RGB_MODE_FLASH; + break; + + case CM_ARGB_MODE_BREATHING: + rgb_mode = CM_RGB_MODE_BREATHING; + break; + + case CM_ARGB_MODE_REFILL: + case CM_ARGB_MODE_STATIC: + rgb_mode = CM_RGB_MODE_STATIC; + break; + + case CM_ARGB_MODE_DEMO: + rgb_mode = CM_RGB_MODE_FLASH; + rgb_random = true; + break; + + case CM_ARGB_MODE_OFF: + default: + rgb_mode = CM_RGB_MODE_OFF; + break; + + case CM_ARGB_MODE_PASSTHRU: + rgb_mode = CM_RGB_MODE_PASSTHRU; + break; + } + + /*-----------------------------------------------------*\ + | Apply mode to all zones | + \*-----------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetPortMode + ( + (unsigned char)zone_idx, + (zone_idx == 4) ? rgb_mode : modes[active_mode].value, + modes[active_mode].speed, + modes[active_mode].brightness, + (zone_idx == 4) ? rgb_random : random, + RGBGetRValue(color), + RGBGetGValue(color), + RGBGetBValue(color) + ); + } +} diff --git a/Controllers/CoolerMasterController/CMARGBController/RGBController_CMARGBController.h b/Controllers/CoolerMasterController/CMARGBController/RGBController_CMARGBController.h new file mode 100644 index 0000000..545f47f --- /dev/null +++ b/Controllers/CoolerMasterController/CMARGBController/RGBController_CMARGBController.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_CMARGBController.h | +| | +| RGBController for Cooler Master ARGB controller | +| | +| Chris M (Dr_No) 14 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "CMARGBController.h" +#include "RGBController.h" + +class RGBController_CMARGBController : public RGBController +{ +public: + RGBController_CMARGBController(CMARGBController* controller_ptr); + ~RGBController_CMARGBController(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CMARGBController* controller; + std::vector leds_channel; +}; diff --git a/Controllers/CoolerMasterController/CMARGBGen2A1Controller/CMARGBGen2A1Controller.cpp b/Controllers/CoolerMasterController/CMARGBGen2A1Controller/CMARGBGen2A1Controller.cpp new file mode 100644 index 0000000..bfd2db3 --- /dev/null +++ b/Controllers/CoolerMasterController/CMARGBGen2A1Controller/CMARGBGen2A1Controller.cpp @@ -0,0 +1,384 @@ +/*---------------------------------------------------------*\ +| CMARGBGen2A1Controller.cpp | +| | +| Driver for Cooler Master ARGB Gen 2 A1 controller | +| | +| Morgan Guimard (morg) 26 Jun 2022 | +| Fabian R (kderazorback) 11 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CMARGBGen2A1Controller.h" +#include "StringUtils.h" + +CMARGBGen2A1controller::CMARGBGen2A1controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + + /*---------------------------------------------*\ + | Setup direct mode on start | + \*---------------------------------------------*/ + SetupDirectMode(); +} + +CMARGBGen2A1controller::~CMARGBGen2A1controller() +{ + hid_close(dev); +} + +std::string CMARGBGen2A1controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string CMARGBGen2A1controller::GetNameString() +{ + return(name); +} + +std::string CMARGBGen2A1controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void CMARGBGen2A1controller::SaveToFlash() +{ + unsigned char usb_buf[CM_ARGB_GEN2_A1_PACKET_LENGTH]; + + memset(usb_buf, 0x00, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + usb_buf[1] = CM_ARGB_GEN2_A1_COMMAND; + usb_buf[2] = CM_ARGB_GEN2_A1_FLASH; + usb_buf[3] = CM_ARGB_GEN2_A1_WRITE; + + hid_write(dev, usb_buf, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + std::this_thread::sleep_for(std::chrono::milliseconds(CM_ARGB_GEN2_A1_SLEEP_LONG)); +} + +void CMARGBGen2A1controller::SetupDirectMode() +{ + ResetDevice(); + + unsigned char usb_buf[CM_ARGB_GEN2_A1_PACKET_LENGTH]; + + /*---------------------------------------------*\ + | Swith to direct mode | + \*---------------------------------------------*/ + usb_buf[1] = CM_ARGB_GEN2_A1_COMMAND; + usb_buf[2] = CM_ARGB_GEN2_A1_HW_MODE_SETUP; + usb_buf[3] = CM_ARGB_GEN2_A1_WRITE; + usb_buf[4] = CM_ARGB_GEN2_A1_CHANNEL_ALL; // CHANNEL + usb_buf[5] = CM_ARGB_GEN2_A1_SUBCHANNEL_ALL; // SUBCHANNEL + usb_buf[6] = CM_ARGB_GEN2_A1_CUSTOM_MODE; + usb_buf[7] = CM_ARGB_GEN2_A1_SPEED_HALF; + usb_buf[8] = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + usb_buf[9] = 0xFF; // R + usb_buf[10] = 0xFF; // G + usb_buf[11] = 0xFF; // B + + hid_write(dev, usb_buf, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + std::this_thread::sleep_for(std::chrono::milliseconds(CM_ARGB_GEN2_A1_SLEEP_SHORT)); + + std::vector colorOffChain; + colorOffChain.push_back(0); + for(unsigned int channel = 0; channel < CM_ARGB_GEN2_A1_CHANNEL_COUNT; channel++) + { + SendChannelColors(channel, CM_ARGB_GEN2_A1_SUBCHANNEL_ALL, colorOffChain); + } + + for(unsigned int channel = 0; channel < CM_ARGB_GEN2_A1_CHANNEL_COUNT; channel++) + { + SetCustomSequence(channel); + } + + software_mode_activated = true; +} + +void CMARGBGen2A1controller::SetupZoneSize(unsigned int zone_id, unsigned int size) +{ + /*---------------------------------------------*\ + | Set the mode sequence to full static | + | (01 for static) | + | | + | This device stores 2 distinct values | + | - effect speed | + | - approximated zone size | + | | + | It's probably based on standard ARGB sizes | + | Still, the 06 value has some mystery. | + | | + | ES= effect speed | + | LC= LEDs count | + | | + | ES LC | + | ----- | + | 0a 06 | + | 09 06 | + | 08 07 | + | 07 08 | + | 06 0a | + | 05 0c | + | 04 0f | + | 03 14 | + | 02 1e | + | 01 3c | + \*---------------------------------------------*/ + + const unsigned char gaps[10] = + { + 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0C, 0x0F, 0x14, 0x1E, 0x3C + }; + + unsigned char speed = 0x0A; + + for(unsigned int g = 0; g < 10; g++) + { + if(size <= gaps[g]) + { + break; + } + + speed--; + } + + unsigned char usb_buf[CM_ARGB_GEN2_A1_PACKET_LENGTH]; + + memset(usb_buf, 0x00, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + usb_buf[1] = CM_ARGB_GEN2_A1_COMMAND; + usb_buf[2] = CM_ARGB_GEN2_A1_SIZES; + usb_buf[3] = CM_ARGB_GEN2_A1_WRITE; + usb_buf[4] = 1 << zone_id; + + usb_buf[5] = speed; + usb_buf[6] = size; + + hid_write(dev, usb_buf, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + std::this_thread::sleep_for(std::chrono::milliseconds(CM_ARGB_GEN2_A1_SLEEP_LONG)); + + /*---------------------------------------------*\ + | Refresh direct mode to cycle the strips | + | with the new length | + \*---------------------------------------------*/ + if(software_mode_activated) + { + SetupDirectMode(); + } +} + +void CMARGBGen2A1controller::SendChannelColors(unsigned int zone_id, unsigned int subchannel_id, std::vector colors) +{ + /*---------------------------------------------*\ + | Create the color data array | + \*---------------------------------------------*/ + std::vector color_data = CreateColorData(colors); + + std::vector::iterator it = color_data.begin(); + + unsigned char usb_buf[CM_ARGB_GEN2_A1_PACKET_LENGTH]; + + unsigned int offset; + + /*----------------------------------------------------*\ + | Break-up color data in packet/s | + | Intentionally clearing first packet only | + | Leaving garbage on subsequent packets | + | Original software appears to not clear them anyways. | + \*----------------------------------------------------*/ + memset(usb_buf, 0x00, CM_ARGB_GEN2_A1_PACKET_LENGTH); + for(unsigned int p = 0; p < CM_ARGB_GEN2_A1_PACKETS_PER_CHANNEL && it != color_data.end(); p++) + { + offset = 1; + + usb_buf[offset++] = p; + usb_buf[offset++] = CM_ARGB_GEN2_A1_SET_RGB_VALUES; + usb_buf[offset++] = CM_ARGB_GEN2_A1_WRITE; + usb_buf[offset++] = 1 << zone_id; + usb_buf[offset++] = 1 << subchannel_id; + + while(it != color_data.end() && offset < CM_ARGB_GEN2_A1_PACKET_LENGTH) + { + usb_buf[offset++] = *it; + it++; + } + + if(p >= CM_ARGB_GEN2_A1_PACKETS_PER_CHANNEL - 1 || it == color_data.end()) + { + /*--------------------------*\ + | Rewrite as end packet | + \*--------------------------*/ + usb_buf[1] = p + 0x80; + } + + hid_write(dev, usb_buf, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + /*-----------------------------------------------*\ + | This device needs some delay before we send | + | any other packet :( | + | This time is critical since the device is | + | still latching its input buffer. | + | Reducing this may start to introduce artifacts | + \*-----------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(CM_ARGB_GEN2_A1_SLEEP_MEDIUM)); + } + + /*---------------------------------------------*\ + | Next channel needs some delay as well | + \*---------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(CM_ARGB_GEN2_A1_SLEEP_SHORT)); +} + +void CMARGBGen2A1controller::SetMode(unsigned int mode_value, unsigned char speed, unsigned char brightness, RGBColor color, bool random) +{ + unsigned char usb_buf[CM_ARGB_GEN2_A1_PACKET_LENGTH]; + + /*---------------------------------------------*\ + | Switch to hardware mode if needed | + \*---------------------------------------------*/ + if(software_mode_activated) + { + memset(usb_buf, 0x00, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + usb_buf[1] = CM_ARGB_GEN2_A1_COMMAND; + usb_buf[2] = CM_ARGB_GEN2_A1_LIGHTNING_CONTROL; + usb_buf[3] = CM_ARGB_GEN2_A1_WRITE; + + hid_write(dev, usb_buf, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + software_mode_activated = false; + + std::this_thread::sleep_for(std::chrono::milliseconds(CM_ARGB_GEN2_A1_SLEEP_LONG)); + } + + /*---------------------------------------------*\ + | Set the mode values and write to the device | + \*---------------------------------------------*/ + memset(usb_buf, 0x00, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + usb_buf[1] = CM_ARGB_GEN2_A1_COMMAND; + usb_buf[2] = CM_ARGB_GEN2_A1_HW_MODE_SETUP; + usb_buf[3] = CM_ARGB_GEN2_A1_WRITE; + + usb_buf[4] = CM_ARGB_GEN2_A1_CHANNEL_ALL; + usb_buf[5] = CM_ARGB_GEN2_A1_SUBCHANNEL_ALL; + + usb_buf[6] = mode_value; + + bool is_custom_mode = (mode_value == CM_ARGB_GEN2_A1_CUSTOM_MODE); + + if(is_custom_mode) + { + usb_buf[7] = CM_ARGB_GEN2_A1_SPEED_MAX; + usb_buf[8] = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + usb_buf[9] = 0xFF; // R + usb_buf[10] = 0xFF; // G + usb_buf[11] = 0xFF; // B + } + else + { + usb_buf[7] = speed; + usb_buf[8] = brightness; + usb_buf[9] = RGBGetRValue(color); + usb_buf[10] = RGBGetGValue(color); + usb_buf[11] = RGBGetBValue(color); + usb_buf[12] = random; + } + + hid_write(dev, usb_buf, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + std::this_thread::sleep_for(std::chrono::milliseconds(CM_ARGB_GEN2_A1_SLEEP_LONG)); + + if(is_custom_mode) + { + for(unsigned int channel = 0; channel < CM_ARGB_GEN2_A1_CHANNEL_COUNT; channel++) + { + SetCustomSequence(channel); + } + } +} + +std::vector CMARGBGen2A1controller::CreateColorData(std::vector colors) +{ + std::vector color_data; + + for(unsigned int c = 0; c < colors.size(); c++) + { + color_data.push_back(RGBGetRValue(colors[c])); + color_data.push_back(RGBGetGValue(colors[c])); + color_data.push_back(RGBGetBValue(colors[c])); + } + + return(color_data); +} + +void CMARGBGen2A1controller::SetCustomSequence(unsigned int zone_id) +{ + unsigned char usb_buf[CM_ARGB_GEN2_A1_PACKET_LENGTH]; + + /*---------------------------------------------*\ + | Set custom speed for sequence mode | + \*---------------------------------------------*/ + memset(usb_buf, 0x00, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + usb_buf[1] = CM_ARGB_GEN2_A1_COMMAND; + usb_buf[2] = CM_ARGB_GEN2_A1_CUSTOM_SPEED; + usb_buf[3] = CM_ARGB_GEN2_A1_WRITE; + usb_buf[4] = 1 << zone_id; // CHANNEL + usb_buf[5] = 0x32; + + hid_write(dev, usb_buf, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + std::this_thread::sleep_for(std::chrono::milliseconds(CM_ARGB_GEN2_A1_SLEEP_SHORT)); + + SetPipelineStaticSequence(zone_id); +} + +void CMARGBGen2A1controller::SetPipelineStaticSequence(unsigned int zone_id) +{ + /*------------------------------------------------*\ + | Set the mode sequence to full static | + | All steps on the effect pipeline to 0x01 STATIC | + \*------------------------------------------------*/ + unsigned char usb_buf[CM_ARGB_GEN2_A1_PACKET_LENGTH]; + memset(usb_buf, CM_ARGB_GEN2_A1_STATIC_MODE, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + usb_buf[0] = 0x00; + usb_buf[1] = CM_ARGB_GEN2_A1_COMMAND; + usb_buf[2] = CM_ARGB_GEN2_A1_CUSTOM_SEQUENCES; + usb_buf[3] = CM_ARGB_GEN2_A1_WRITE; + usb_buf[4] = 1 << zone_id; + + hid_write(dev, usb_buf, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + std::this_thread::sleep_for(std::chrono::milliseconds(CM_ARGB_GEN2_A1_SLEEP_LONG)); +} + +void CMARGBGen2A1controller::ResetDevice() +{ + unsigned char usb_buf[CM_ARGB_GEN2_A1_PACKET_LENGTH]; + + memset(usb_buf, 0x00, CM_ARGB_GEN2_A1_PACKET_LENGTH); + usb_buf[1] = CM_ARGB_GEN2_A1_COMMAND; + usb_buf[2] = CM_ARGB_GEN2_A1_RESET; + usb_buf[3] = CM_ARGB_GEN2_A1_WRITE; + + hid_write(dev, usb_buf, CM_ARGB_GEN2_A1_PACKET_LENGTH); + + std::this_thread::sleep_for(std::chrono::milliseconds(CM_ARGB_GEN2_A1_SLEEP_LONG)); +} diff --git a/Controllers/CoolerMasterController/CMARGBGen2A1Controller/CMARGBGen2A1Controller.h b/Controllers/CoolerMasterController/CMARGBGen2A1Controller/CMARGBGen2A1Controller.h new file mode 100644 index 0000000..aa2f857 --- /dev/null +++ b/Controllers/CoolerMasterController/CMARGBGen2A1Controller/CMARGBGen2A1Controller.h @@ -0,0 +1,113 @@ +/*---------------------------------------------------------*\ +| CMARGBGen2A1Controller.h | +| | +| Driver for Cooler Master ARGB Gen 2 A1 controller | +| | +| Morgan Guimard (morg) 26 Jun 2022 | +| Fabian R (kderazorback) 11 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define CM_ARGB_GEN2_A1_PACKET_LENGTH 65 +#define CM_ARGB_GEN2_A1_CHANNEL_MAX_SIZE 72 +#define CM_ARGB_GEN2_A1_CHANNEL_COUNT 3 +#define CM_ARGB_GEN2_A1_PACKETS_PER_CHANNEL 2 + +#define CM_ARGB_GEN2_A1_SLEEP_SHORT 5 +#define CM_ARGB_GEN2_A1_SLEEP_MEDIUM 45 +#define CM_ARGB_GEN2_A1_SLEEP_LONG 70 + +enum +{ + CM_ARGB_GEN2_A1_DIRECT_MODE = 0xFF, + CM_ARGB_GEN2_A1_SPECTRUM_MODE = 0x00, + CM_ARGB_GEN2_A1_STATIC_MODE = 0x01, + CM_ARGB_GEN2_A1_RELOAD_MODE = 0x02, + CM_ARGB_GEN2_A1_RECOIL_MODE = 0x03, + CM_ARGB_GEN2_A1_BREATHING_MODE = 0x04, + CM_ARGB_GEN2_A1_REFILL_MODE = 0x05, + CM_ARGB_GEN2_A1_DEMO_MODE = 0x06, + CM_ARGB_GEN2_A1_FILL_FLOW_MODE = 0x07, + CM_ARGB_GEN2_A1_RAINBOW_MODE = 0x08, + CM_ARGB_GEN2_A1_CUSTOM_MODE = 0xC0, + CM_ARGB_GEN2_A1_OFF_MODE = 0x09 +}; + +enum +{ + CM_ARGB_GEN2_A1_BRIGHTNESS_MAX = 0xFF, + CM_ARGB_GEN2_A1_BRIGHTNESS_MIN = 0x00, + CM_ARGB_GEN2_A1_SPEED_MAX = 0x04, + CM_ARGB_GEN2_A1_SPEED_HALF = 0x02, + CM_ARGB_GEN2_A1_SPEED_MIN = 0x00, +}; + +enum +{ + CM_ARGB_GEN2_A1_COMMAND = 0x80, + CM_ARGB_GEN2_A1_COMMAND_EXTRA_1 = 0x81, + CM_ARGB_GEN2_A1_COMMAND_EXTRA_2 = 0x82, + CM_ARGB_GEN2_A1_READ = 0x01, + CM_ARGB_GEN2_A1_WRITE = 0x02, + CM_ARGB_GEN2_A1_RESPONSE = 0x03 +}; + +enum +{ + CM_ARGB_GEN2_A1_SIZES = 0x06, + CM_ARGB_GEN2_A1_SET_RGB_VALUES = 0x08, + CM_ARGB_GEN2_A1_FLASH = 0x0B, + CM_ARGB_GEN2_A1_IDENTIFY = 0x0A, + CM_ARGB_GEN2_A1_LIGHTNING_CONTROL = 0x01, + CM_ARGB_GEN2_A1_HW_MODE_SETUP = 0x03, + CM_ARGB_GEN2_A1_CUSTOM_SEQUENCES = 0x10, + CM_ARGB_GEN2_A1_CUSTOM_SPEED = 0x11, + CM_ARGB_GEN2_A1_RESET = 0xC0, + CM_ARGB_GEN2_A1_APPLY_CHANGES = 0xB0 +}; + +enum +{ + CM_ARGB_GEN2_A1_CHANNEL_A = 0x01, + CM_ARGB_GEN2_A1_CHANNEL_B = 0x02, + CM_ARGB_GEN2_A1_CHANNEL_C = 0x04, + CM_ARGB_GEN2_A1_CHANNEL_ALL = 0xFF, + CM_ARGB_GEN2_A1_SUBCHANNEL_ALL = 0xFF +}; + +class CMARGBGen2A1controller +{ +public: + CMARGBGen2A1controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~CMARGBGen2A1controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendChannelColors(unsigned int zone_id, unsigned int subchannel_id, std::vector colors); + void SetupZoneSize(unsigned int zone_id, unsigned int size); + void SetupDirectMode(); + void SetMode(unsigned int mode_value, unsigned char speed, unsigned char brightness, RGBColor color, bool random); + void SetCustomColors(unsigned int zone_id, std::vector colors); + void SaveToFlash(); + +private: + std::string location; + std::string name; + bool software_mode_activated = false; + hid_device* dev; + + void SetCustomSequence(unsigned int zone_id); + void SetPipelineStaticSequence(unsigned int zone_id); + std::vector CreateColorData(std::vector colors); + void ResetDevice(); +}; diff --git a/Controllers/CoolerMasterController/CMARGBGen2A1Controller/RGBController_CMARGBGen2A1Controller.cpp b/Controllers/CoolerMasterController/CMARGBGen2A1Controller/RGBController_CMARGBGen2A1Controller.cpp new file mode 100644 index 0000000..d024b3a --- /dev/null +++ b/Controllers/CoolerMasterController/CMARGBGen2A1Controller/RGBController_CMARGBGen2A1Controller.cpp @@ -0,0 +1,340 @@ +/*---------------------------------------------------------*\ +| RGBController_CMARGBGen2A1Controller.cpp | +| | +| Driver for Cooler Master ARGB Gen 2 A1 controller | +| | +| Morgan Guimard (morg) 26 Jun 2022 | +| Fabian R (kderazorback) 11 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_CMARGBGen2A1Controller.h" + +/**------------------------------------------------------------------*\ + @name Coolermaster ARGB A1 + @category LEDStrip + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCoolerMasterARGBGen2A1 + @comment OpenRGB partially supports Gen 2 protocol for this device. + + Gen2 has auto-resize feature and parallel to serial magical stuff, + Strip size is auto detected by the controller but not reported + back to OpenRGB. Configure zones and segments for each channel + to allow individual addressing. + Take note that this controller is extremely slow, using fast + update rates may introduce color artifacts.< +\*-------------------------------------------------------------------*/ + +RGBController_CMARGBGen2A1Controller::RGBController_CMARGBGen2A1Controller(CMARGBGen2A1controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "CoolerMaster"; + type = DEVICE_TYPE_LEDSTRIP; + description = "CoolerMaster LED Controller A1 Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CM_ARGB_GEN2_A1_DIRECT_MODE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Spectrum; + Spectrum.name = "Spectrum"; + Spectrum.value = CM_ARGB_GEN2_A1_SPECTRUM_MODE; + Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.brightness = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Spectrum.brightness_min = CM_ARGB_GEN2_A1_BRIGHTNESS_MIN; + Spectrum.brightness_max = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Spectrum.speed = CM_ARGB_GEN2_A1_SPEED_MAX/2; + Spectrum.speed_min = CM_ARGB_GEN2_A1_SPEED_MIN; + Spectrum.speed_max = CM_ARGB_GEN2_A1_SPEED_MAX; + modes.push_back(Spectrum); + + mode Static; + Static.name = "Static"; + Static.value = CM_ARGB_GEN2_A1_STATIC_MODE; + Static.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Static.brightness_min = CM_ARGB_GEN2_A1_BRIGHTNESS_MIN; + Static.brightness_max = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Static.colors.resize(1); + modes.push_back(Static); + + mode Reload; + Reload.name = "Reload"; + Reload.value = CM_ARGB_GEN2_A1_RELOAD_MODE; + Reload.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Reload.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reload.brightness = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Reload.brightness_min = CM_ARGB_GEN2_A1_BRIGHTNESS_MIN; + Reload.brightness_max = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Reload.speed = CM_ARGB_GEN2_A1_SPEED_MAX/2; + Reload.speed_min = CM_ARGB_GEN2_A1_SPEED_MIN; + Reload.speed_max = CM_ARGB_GEN2_A1_SPEED_MAX; + Reload.colors.resize(1); + modes.push_back(Reload); + + mode Recoil; + Recoil.name = "Recoil"; + Recoil.value = CM_ARGB_GEN2_A1_RECOIL_MODE; + Recoil.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Recoil.color_mode = MODE_COLORS_MODE_SPECIFIC; + Recoil.brightness = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Recoil.brightness_min = CM_ARGB_GEN2_A1_BRIGHTNESS_MIN; + Recoil.brightness_max = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Recoil.speed = CM_ARGB_GEN2_A1_SPEED_MAX/2; + Recoil.speed_min = CM_ARGB_GEN2_A1_SPEED_MIN; + Recoil.speed_max = CM_ARGB_GEN2_A1_SPEED_MAX; + Recoil.colors.resize(1); + modes.push_back(Recoil); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_ARGB_GEN2_A1_BREATHING_MODE; + Breathing.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.brightness = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Breathing.brightness_min = CM_ARGB_GEN2_A1_BRIGHTNESS_MIN; + Breathing.brightness_max = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Breathing.speed = CM_ARGB_GEN2_A1_SPEED_MAX/2; + Breathing.speed_min = CM_ARGB_GEN2_A1_SPEED_MIN; + Breathing.speed_max = CM_ARGB_GEN2_A1_SPEED_MAX; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + + mode Refill; + Refill.name = "Refill"; + Refill.value = CM_ARGB_GEN2_A1_REFILL_MODE; + Refill.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Refill.color_mode = MODE_COLORS_MODE_SPECIFIC; + Refill.brightness = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Refill.brightness_min = CM_ARGB_GEN2_A1_BRIGHTNESS_MIN; + Refill.brightness_max = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Refill.speed = CM_ARGB_GEN2_A1_SPEED_MAX/2; + Refill.speed_min = CM_ARGB_GEN2_A1_SPEED_MIN; + Refill.speed_max = CM_ARGB_GEN2_A1_SPEED_MAX; + Refill.colors.resize(1); + modes.push_back(Refill); + + mode Demo; + Demo.name = "Demo"; + Demo.value = CM_ARGB_GEN2_A1_DEMO_MODE; + Demo.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Demo.color_mode = MODE_COLORS_NONE; + Demo.brightness = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Demo.brightness_min = CM_ARGB_GEN2_A1_BRIGHTNESS_MIN; + Demo.brightness_max = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + modes.push_back(Demo); + + mode FillFlow; + FillFlow.name = "Fill Flow"; + FillFlow.value = CM_ARGB_GEN2_A1_FILL_FLOW_MODE; + FillFlow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE;; + FillFlow.color_mode = MODE_COLORS_NONE; + FillFlow.brightness = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + FillFlow.brightness_min = CM_ARGB_GEN2_A1_BRIGHTNESS_MIN; + FillFlow.brightness_max = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + FillFlow.speed = CM_ARGB_GEN2_A1_SPEED_MAX/2; + FillFlow.speed_min = CM_ARGB_GEN2_A1_SPEED_MIN; + FillFlow.speed_max = CM_ARGB_GEN2_A1_SPEED_MAX; + modes.push_back(FillFlow); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = CM_ARGB_GEN2_A1_RAINBOW_MODE; + Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE;; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Rainbow.brightness_min = CM_ARGB_GEN2_A1_BRIGHTNESS_MIN; + Rainbow.brightness_max = CM_ARGB_GEN2_A1_BRIGHTNESS_MAX; + Rainbow.speed = CM_ARGB_GEN2_A1_SPEED_MAX/2; + Rainbow.speed_min = CM_ARGB_GEN2_A1_SPEED_MIN; + Rainbow.speed_max = CM_ARGB_GEN2_A1_SPEED_MAX; + modes.push_back(Rainbow); + + mode Custom; + Custom.name = "Custom"; + Custom.value = CM_ARGB_GEN2_A1_CUSTOM_MODE; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode Off; + Off.name = "Off"; + Off.value = CM_ARGB_GEN2_A1_OFF_MODE; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_CMARGBGen2A1Controller::~RGBController_CMARGBGen2A1Controller() +{ + delete controller; +} + +void RGBController_CMARGBGen2A1Controller::SetupZones() +{ + unsigned int total_leds = 0; + + for(unsigned int channel = 0; channel < CM_ARGB_GEN2_A1_CHANNEL_COUNT; channel++) + { + zone new_zone; + + new_zone.name = "Channel " + std::to_string(channel + 1); + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 0; + new_zone.leds_max = CM_ARGB_GEN2_A1_CHANNEL_MAX_SIZE; + new_zone.leds_count = 0; + new_zone.matrix_map = nullptr; + + zones.push_back(new_zone); + + total_leds += new_zone.leds_count; + } + + leds.resize(total_leds); + + for(unsigned int i = 0; i < total_leds; i++) + { + leds[i].name = "LED " + std::to_string(i + 1); + } + + SetupColors(); +} + +void RGBController_CMARGBGen2A1Controller::ResizeZone(int zone, int new_size) +{ + zones[zone].leds_count = new_size; + + unsigned int total_leds = 0; + + for(unsigned int channel = 0; channel < CM_ARGB_GEN2_A1_CHANNEL_COUNT; channel++) + { + total_leds += zones[channel].leds_count; + } + + leds.resize(total_leds); + + for(unsigned int i = 0; i < total_leds; i++) + { + leds[i].name = "LED " + std::to_string(i + 1); + } + + controller->SetupZoneSize(zone, new_size); + + SetupColors(); +} + +void RGBController_CMARGBGen2A1Controller::DeviceUpdateLEDs() +{ + for(unsigned int channel = 0; channel < CM_ARGB_GEN2_A1_CHANNEL_COUNT; channel ++) + { + if (zones[channel].segments.size() > 0) + { + unsigned int i = 0; + for(std::vector::iterator it = zones[channel].segments.begin(); it != zones[channel].segments.end(); it++) + { + UpdateSegmentLEDs(channel, i++); + } + } + else + { + UpdateSegmentLEDs(channel, CM_ARGB_GEN2_A1_SUBCHANNEL_ALL); + } + } +} + +void RGBController_CMARGBGen2A1Controller::UpdateZoneLEDs(int zone) +{ + if(zones[zone].leds_count > 0) + { + unsigned int start = zones[zone].start_idx; + unsigned int end = start + zones[zone].leds_count; + + std::vector zone_colors(colors.begin() + start , colors.begin() + end); + + controller->SendChannelColors(zone, CM_ARGB_GEN2_A1_SUBCHANNEL_ALL, zone_colors); + } +} + +void RGBController_CMARGBGen2A1Controller::UpdateSegmentLEDs(int zone, int subchannel) +{ + if(zones[zone].leds_count <= 0) + { + return; + } + + unsigned int start = zones[zone].start_idx; + unsigned int end = start + zones[zone].leds_count; + bool use_direct_mode = modes[active_mode].value == CM_ARGB_GEN2_A1_DIRECT_MODE || modes[active_mode].value == CM_ARGB_GEN2_A1_CUSTOM_MODE; + + std::vector color_vector(colors.begin() + start, colors.begin() + start + end); + + if(use_direct_mode) + { + if(zones[zone].segments.size() > 0) + { + start += zones[zone].segments[subchannel].start_idx; + end += zones[zone].segments[subchannel].start_idx + zones[zone].segments[subchannel].leds_count; + + color_vector = std::vector(colors.begin() + start , colors.begin() + end); + } + + controller->SendChannelColors(zone, subchannel, color_vector); + return; + } + + controller->SendChannelColors(zone, CM_ARGB_GEN2_A1_SUBCHANNEL_ALL, color_vector); +} + +void RGBController_CMARGBGen2A1Controller::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMARGBGen2A1Controller::DeviceUpdateMode() +{ + const mode& active = modes[active_mode]; + + if(active.value == CM_ARGB_GEN2_A1_DIRECT_MODE) + { + controller->SetupDirectMode(); + } + else + { + RGBColor color = active.color_mode == MODE_COLORS_MODE_SPECIFIC ? + active.colors[0] : 0; + + controller->SetMode + ( + active.value, + active.speed, + active.brightness, + color, + active.color_mode == MODE_COLORS_RANDOM + ); + } +} + +void RGBController_CMARGBGen2A1Controller::DeviceSaveMode() +{ + controller->SaveToFlash(); +} + diff --git a/Controllers/CoolerMasterController/CMARGBGen2A1Controller/RGBController_CMARGBGen2A1Controller.h b/Controllers/CoolerMasterController/CMARGBGen2A1Controller/RGBController_CMARGBGen2A1Controller.h new file mode 100644 index 0000000..41da768 --- /dev/null +++ b/Controllers/CoolerMasterController/CMARGBGen2A1Controller/RGBController_CMARGBGen2A1Controller.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_CMARGBGen2A1Controller.h | +| | +| Driver for Cooler Master ARGB Gen 2 A1 controller | +| | +| Morgan Guimard (morg) 26 Jun 2022 | +| Fabian R (kderazorback) 11 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "CMARGBGen2A1Controller.h" + +class RGBController_CMARGBGen2A1Controller : public RGBController +{ +public: + RGBController_CMARGBGen2A1Controller(CMARGBGen2A1controller* controller_ptr); + ~RGBController_CMARGBGen2A1Controller(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSegmentLEDs(int zone, int subchannel); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + CMARGBGen2A1controller* controller; +}; diff --git a/Controllers/CoolerMasterController/CMGD160Controller/CMGD160Controller.cpp b/Controllers/CoolerMasterController/CMGD160Controller/CMGD160Controller.cpp new file mode 100644 index 0000000..e0864ca --- /dev/null +++ b/Controllers/CoolerMasterController/CMGD160Controller/CMGD160Controller.cpp @@ -0,0 +1,216 @@ +/*---------------------------------------------------------*\ +| CMGD160Controller.cpp | +| | +| Driver for Cooler Master GD160 ARGB Gaming Desk | +| | +| Logan Phillips (Eclipse) 16 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| Adapted from CMMonitor controller code | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CMGD160Controller.h" +#include "StringUtils.h" + +CMGD160Controller::CMGD160Controller(hid_device* dev_handle, const hid_device_info& info, const std::string& name) +{ + dev = dev_handle; + device_name = name; + location = info.path; + ResetDevice(); +} + +CMGD160Controller::~CMGD160Controller() +{ + hid_close(dev); +} + +std::string CMGD160Controller::GetDeviceName() +{ + return(device_name); +} + +std::string CMGD160Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string CMGD160Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +/*------------------------------------------------------------*\ +| Desk requires 2 sets of packets sent for the front and back | +| Technically you could have both sides do something different | +| Not sure why you would though.... | +| Cooler Master's software doesn't allow that anyways | +\*------------------------------------------------------------*/ + +void CMGD160Controller::SetMode(uint8_t mode_value, uint8_t speed, uint8_t brightness, const RGBColor& color) +{ + if(is_software_mode_enabled) + { + SetControlMode(false); + } + + uint8_t usb_buf[CM_GD160_PACKET_LENGTH]; + + for(int side = 1; side <= 2; side++) + { + memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH); + + usb_buf[1] = 0x80; + usb_buf[2] = (mode_value == CM_GD160_OFF_MODE) ? 0x0F : 0x0B; + usb_buf[3] = 0x02; + usb_buf[4] = side; // 0x01 for front, 0x02 for back + usb_buf[5] = mode_value; + usb_buf[6] = (mode_value == CM_GD160_OFF_MODE) ? 0x00 : 0x08; + usb_buf[7] = speed; + usb_buf[8] = brightness; + usb_buf[9] = RGBGetRValue(color); + usb_buf[10] = RGBGetGValue(color); + usb_buf[11] = RGBGetBValue(color); + + hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH); + } +} + +/*-------------------------------------------------*\ +| How to request current color in custom mode. | +| Not like it matters since we default to direct | +| mode and it seems to clear the current colors... | +| | +| memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH); | +| usb_buf[1] = 0x80; | +| usb_buf[2] = 0x10; | +| usb_buf[3] = 0x01; or 0x02 | +| usb_buf[4] = 0x02; | +| usb_buf[5] = 0x80; | +| hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH); | +\*-------------------------------------------------*/ + +void CMGD160Controller::SendColorData(const std::vector& colors, uint8_t command, uint8_t mode_byte, uint8_t brightness, bool desired_control_mode) +{ + if(is_software_mode_enabled != desired_control_mode) + { + SetControlMode(desired_control_mode); + } + + uint8_t color_data[CM_GD160_COLOR_DATA_LENGTH]; + memset(color_data, 0x00, CM_GD160_COLOR_DATA_LENGTH); + + for(unsigned int i = 0; i < colors.size() && i < (CM_GD160_LEDS_PER_SIDE * 2); i++) + { + unsigned int side = i / CM_GD160_LEDS_PER_SIDE; + unsigned int led_in_side = i % CM_GD160_LEDS_PER_SIDE; + unsigned int buffer_offset = (side * CM_GD160_SIDE_DATA_LENGTH) + (led_in_side * 3); + + color_data[buffer_offset] = RGBGetRValue(colors[i]); + color_data[buffer_offset + 1] = RGBGetGValue(colors[i]); + color_data[buffer_offset + 2] = RGBGetBValue(colors[i]); + } + + uint8_t usb_buf[CM_GD160_PACKET_LENGTH]; + + for(unsigned int side = 1; side <= 2; side++) + { + unsigned int offset = (side - 1) * CM_GD160_SIDE_DATA_LENGTH; + + for(unsigned int packet = 0; packet < 7; packet++) + { + memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH); + + usb_buf[1] = (packet < 6) ? packet : 0x86; // Last packet uses 0x86 + + /*---------------------------------------------------------*\ + | First packet contains static data | + \*---------------------------------------------------------*/ + + if(packet == 0) + { + usb_buf[2] = command; + usb_buf[3] = 0x02; + usb_buf[4] = side; // 0x01 for front, 0x02 for back + usb_buf[5] = mode_byte; + usb_buf[6] = brightness; + + memcpy(&usb_buf[7], &color_data[offset], CM_GD160_FIRST_PACKET_DATA_SIZE); + offset += CM_GD160_FIRST_PACKET_DATA_SIZE; + } + else + { + memcpy(&usb_buf[2], &color_data[offset], CM_GD160_PACKET_DATA_SIZE); + offset += CM_GD160_PACKET_DATA_SIZE; + } + + hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH); + } + } +} + +/*------------------------------------------------------*\ +| True enables software mode | +| False enables hardware mode | +\*------------------------------------------------------*/ + +void CMGD160Controller::SetControlMode(bool software_mode) +{ + uint8_t usb_buf[CM_GD160_PACKET_LENGTH]; + + for(int side = 1; side <= 2; side++) + { + memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH); + + usb_buf[1] = 0x80; + usb_buf[2] = 0x07; + usb_buf[3] = 0x02; + usb_buf[4] = side; // 0x01 for front, 0x02 for back + usb_buf[6] = software_mode; + + hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH); + } + + is_software_mode_enabled = software_mode; +} + +/*------------------------------------------------------*\ +| Reset device on discovery in case it somehow landed | +| in a bad / unresponsive state | +\*------------------------------------------------------*/ + +void CMGD160Controller::ResetDevice() +{ + uint8_t usb_buf[CM_GD160_PACKET_LENGTH]; + + memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH); + usb_buf[1] = 0x80; + usb_buf[2] = 0x11; + hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH); + + memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH); + usb_buf[1] = 0x80; + usb_buf[2] = 0x0B; + usb_buf[3] = 0x01; + usb_buf[4] = 0x02; + hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH); + + memset(usb_buf, 0x00, CM_GD160_PACKET_LENGTH); + usb_buf[1] = 0x80; + usb_buf[2] = 0x18; + usb_buf[3] = 0x01; + usb_buf[4] = 0x02; + hid_write(dev, usb_buf, CM_GD160_PACKET_LENGTH); + + is_software_mode_enabled = false; +} diff --git a/Controllers/CoolerMasterController/CMGD160Controller/CMGD160Controller.h b/Controllers/CoolerMasterController/CMGD160Controller/CMGD160Controller.h new file mode 100644 index 0000000..c56eb69 --- /dev/null +++ b/Controllers/CoolerMasterController/CMGD160Controller/CMGD160Controller.h @@ -0,0 +1,67 @@ +/*---------------------------------------------------------*\ +| CMGD160Controller.h | +| | +| Driver for Cooler Master GD160 ARGB Gaming Desk | +| | +| Logan Phillips (Eclipse) 16 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| Adapted from CMMonitor controller code | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define CM_GD160_PACKET_LENGTH 65 +#define CM_GD160_COLOR_DATA_LENGTH 872 +#define CM_GD160_SIDE_DATA_LENGTH 436 // 96 LEDs * 3 bytes + header = 436 bytes per side +#define CM_GD160_LEDS_PER_SIDE 96 +#define CM_GD160_FIRST_PACKET_DATA_SIZE 58 // CM_GD160_PACKET_LENGTH - 7 (header bytes) +#define CM_GD160_PACKET_DATA_SIZE 63 // CM_GD160_PACKET_LENGTH - 2 (header bytes) + +enum +{ + CM_GD160_DIRECT_MODE = 0xFF, + CM_GD160_CUSTOM_MODE = 0xFE, + CM_GD160_SPECTRUM_MODE = 0x00, + CM_GD160_RELOAD_MODE = 0x01, + CM_GD160_RECOIL_MODE = 0x02, + CM_GD160_BREATHING_MODE = 0x03, + CM_GD160_REFILL_MODE = 0x04, + CM_GD160_OFF_MODE = 0x06 +}; + +enum +{ + CM_GD160_BRIGHTNESS_MAX = 0xFF, + CM_GD160_BRIGHTNESS_MIN = 0x00, + CM_GD160_SPEED_MAX = 0x04, + CM_GD160_SPEED_MIN = 0x00, +}; + +class CMGD160Controller +{ +public: + CMGD160Controller(hid_device* dev_handle, const hid_device_info& info, const std::string& name); + ~CMGD160Controller(); + + std::string GetDeviceName(); + std::string GetSerialString(); + std::string GetDeviceLocation(); + + void SetMode(uint8_t mode_value, uint8_t speed, uint8_t brightness, const RGBColor& color); + void SendColorData(const std::vector& colors, uint8_t command, uint8_t mode_byte, uint8_t brightness, bool enable_software_mode); + +private: + std::string device_name; + std::string serial_number; + std::string location; + hid_device* dev; + bool is_software_mode_enabled = false; + void SetControlMode(bool value); + void ResetDevice(); +}; diff --git a/Controllers/CoolerMasterController/CMGD160Controller/RGBController_CMGD160Controller.cpp b/Controllers/CoolerMasterController/CMGD160Controller/RGBController_CMGD160Controller.cpp new file mode 100644 index 0000000..bfc0b3f --- /dev/null +++ b/Controllers/CoolerMasterController/CMGD160Controller/RGBController_CMGD160Controller.cpp @@ -0,0 +1,238 @@ +/*---------------------------------------------------------*\ +| RGBController_CMGD160Controller.cpp | +| | +| RGBController for Cooler Master GD160 ARGB Gaming Desk | +| | +| Logan Phillips (Eclipse) 16 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| Adapted from CMMonitor controller code | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CMGD160Controller.h" + +/**------------------------------------------------------------------*\ + @name Cooler Master GD160 ARGB Gaming Desk + @category Accessory + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCoolerMasterGD160 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CMGD160Controller::RGBController_CMGD160Controller(CMGD160Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Cooler Master"; + type = DEVICE_TYPE_ACCESSORY; + description = "Cooler Master GD160 Gaming Desk Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CM_GD160_DIRECT_MODE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Spectrum; + Spectrum.name = "Spectrum Cycle"; + Spectrum.value = CM_GD160_SPECTRUM_MODE; + Spectrum.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.speed_min = CM_GD160_SPEED_MIN; + Spectrum.speed_max = CM_GD160_SPEED_MAX; + Spectrum.speed = CM_GD160_SPEED_MAX/2; + Spectrum.brightness_min = CM_GD160_BRIGHTNESS_MIN; + Spectrum.brightness_max = CM_GD160_BRIGHTNESS_MAX; + Spectrum.brightness = CM_GD160_BRIGHTNESS_MAX; + modes.push_back(Spectrum); + + mode Reload; + Reload.name = "Reload"; + Reload.value = CM_GD160_RELOAD_MODE; + Reload.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Reload.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reload.colors_min = 1; + Reload.colors_max = 1; + Reload.colors.resize(1); + Reload.speed_min = CM_GD160_SPEED_MIN; + Reload.speed_max = CM_GD160_SPEED_MAX; + Reload.speed = CM_GD160_SPEED_MAX/2; + Reload.brightness_min = CM_GD160_BRIGHTNESS_MIN; + Reload.brightness_max = CM_GD160_BRIGHTNESS_MAX; + Reload.brightness = CM_GD160_BRIGHTNESS_MAX; + modes.push_back(Reload); + + mode Recoil; + Recoil.name = "Recoil"; + Recoil.value = CM_GD160_RECOIL_MODE; + Recoil.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Recoil.color_mode = MODE_COLORS_MODE_SPECIFIC; + Recoil.colors_min = 1; + Recoil.colors_max = 1; + Recoil.colors.resize(1); + Recoil.speed_min = CM_GD160_SPEED_MIN; + Recoil.speed_max = CM_GD160_SPEED_MAX; + Recoil.speed = CM_GD160_SPEED_MAX/2; + Recoil.brightness_min = CM_GD160_BRIGHTNESS_MIN; + Recoil.brightness_max = CM_GD160_BRIGHTNESS_MAX; + Recoil.brightness = CM_GD160_BRIGHTNESS_MAX; + modes.push_back(Recoil); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_GD160_BREATHING_MODE; + Breathing.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + Breathing.speed_min = CM_GD160_SPEED_MIN; + Breathing.speed_max = CM_GD160_SPEED_MAX; + Breathing.speed = CM_GD160_SPEED_MAX/2; + Breathing.brightness_min = CM_GD160_BRIGHTNESS_MIN; + Breathing.brightness_max = CM_GD160_BRIGHTNESS_MAX; + Breathing.brightness = CM_GD160_BRIGHTNESS_MAX; + modes.push_back(Breathing); + + mode Refill; + Refill.name = "Refill"; + Refill.value = CM_GD160_REFILL_MODE; + Refill.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Refill.color_mode = MODE_COLORS_MODE_SPECIFIC; + Refill.colors_min = 1; + Refill.colors_max = 1; + Refill.colors.resize(1); + Refill.speed_min = CM_GD160_SPEED_MIN; + Refill.speed_max = CM_GD160_SPEED_MAX; + Refill.speed = CM_GD160_SPEED_MAX/2; + Refill.brightness_min = CM_GD160_BRIGHTNESS_MIN; + Refill.brightness_max = CM_GD160_BRIGHTNESS_MAX; + Refill.brightness = CM_GD160_BRIGHTNESS_MAX; + modes.push_back(Refill); + + mode Custom; + Custom.name = "Custom"; + Custom.value = CM_GD160_CUSTOM_MODE; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.brightness_min = CM_GD160_BRIGHTNESS_MIN; + Custom.brightness_max = CM_GD160_BRIGHTNESS_MAX; + Custom.brightness = CM_GD160_BRIGHTNESS_MAX; + modes.push_back(Custom); + + mode Off; + Off.name = "Off"; + Off.value = CM_GD160_OFF_MODE; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_CMGD160Controller::~RGBController_CMGD160Controller() +{ + delete controller; +} + +void RGBController_CMGD160Controller::SetupZones() +{ + zone front; + front.name = "Front Desk"; + front.type = ZONE_TYPE_LINEAR; + front.leds_min = CM_GD160_LEDS_PER_SIDE; + front.leds_max = CM_GD160_LEDS_PER_SIDE; + front.leds_count = CM_GD160_LEDS_PER_SIDE; + front.matrix_map = NULL; + zones.push_back(front); + + for(unsigned int i = 0; i < CM_GD160_LEDS_PER_SIDE; i++) + { + led l; + l.name = "Front LED " + std::to_string(i + 1); + l.value = i; + leds.push_back(l); + } + + zone back; + back.name = "Back Desk"; + back.type = ZONE_TYPE_LINEAR; + back.leds_min = CM_GD160_LEDS_PER_SIDE; + back.leds_max = CM_GD160_LEDS_PER_SIDE; + back.leds_count = CM_GD160_LEDS_PER_SIDE; + back.matrix_map = NULL; + zones.push_back(back); + + for(unsigned int i = 0; i < CM_GD160_LEDS_PER_SIDE; i++) + { + led l; + l.name = "Back LED " + std::to_string(i + 1); + l.value = i; + leds.push_back(l); + } + + SetupColors(); +} + +void RGBController_CMGD160Controller::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CMGD160Controller::DeviceUpdateLEDs() +{ + switch(modes[active_mode].value) + { + case CM_GD160_DIRECT_MODE: + controller->SendColorData(colors, 0x07, 0x01, 0xFF, true); + break; + + case CM_GD160_CUSTOM_MODE: + controller->SendColorData(colors, 0x10, 0x80, modes[active_mode].brightness, false); + break; + + default: + break; + } +} + +void RGBController_CMGD160Controller::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMGD160Controller::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMGD160Controller::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case CM_GD160_OFF_MODE: + case CM_GD160_SPECTRUM_MODE: + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, 0); + break; + + case CM_GD160_RELOAD_MODE: + case CM_GD160_RECOIL_MODE: + case CM_GD160_BREATHING_MODE: + case CM_GD160_REFILL_MODE: + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, modes[active_mode].colors[0]); + break; + + default: + break; + } +} diff --git a/Controllers/CoolerMasterController/CMGD160Controller/RGBController_CMGD160Controller.h b/Controllers/CoolerMasterController/CMGD160Controller/RGBController_CMGD160Controller.h new file mode 100644 index 0000000..7c92402 --- /dev/null +++ b/Controllers/CoolerMasterController/CMGD160Controller/RGBController_CMGD160Controller.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_CMGD160Controller.h | +| | +| RGBController for Cooler Master GD160 ARGB Gaming Desk | +| | +| Logan Phillips (Eclipse) 16 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| Adapted from CMMonitor controller code | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CMGD160Controller.h" + +class RGBController_CMGD160Controller : public RGBController +{ +public: + RGBController_CMGD160Controller(CMGD160Controller* controller_ptr); + ~RGBController_CMGD160Controller(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CMGD160Controller* controller; +}; \ No newline at end of file diff --git a/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardAbstractController.cpp b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardAbstractController.cpp new file mode 100644 index 0000000..a7a2ce4 --- /dev/null +++ b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardAbstractController.cpp @@ -0,0 +1,197 @@ +/*---------------------------------------------------------*\ +| CMKeyboardAbstractController.cpp | +| | +| Driver for Cooler Master keyboards | +| | +| Tam D (too.manyhobbies) 30 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CMKeyboardAbstractController.h" +#include "StringUtils.h" + +CMKeyboardAbstractController::CMKeyboardAbstractController(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name) +{ + wchar_t tmp[HID_MAX_STR]; + + m_pDev = dev_handle; + m_productId = dev_info->product_id; + m_sLocation = dev_info->path; + m_deviceName = dev_name; + + hid_get_manufacturer_string(m_pDev, tmp, HID_MAX_STR); + m_vendorName = StringUtils::wstring_to_string(tmp); + + hid_get_product_string(m_pDev, tmp, HID_MAX_STR); + m_serialNumber = StringUtils::wstring_to_string(tmp); + + bool bNotFound = true; + + for(uint16_t i = 0; i < COOLERMASTER_KEYBOARD_DEVICE_COUNT; i++) + { + if(cm_kb_device_list[i]->product_id == m_productId) + { + bNotFound = false; + m_deviceIndex = i; + break; + } + } + + if(bNotFound) + { + LOG_ERROR("[%s] device capabilities not found. Please creata a new device request.", m_deviceName.c_str()); + } +}; + +CMKeyboardAbstractController::~CMKeyboardAbstractController() +{ + hid_close(m_pDev); +}; + +std::string CMKeyboardAbstractController::GetDeviceName() +{ + return(m_deviceName); +} + +std::string CMKeyboardAbstractController::GetDeviceVendor() +{ + return(m_vendorName); +} + +std::string CMKeyboardAbstractController::GetDeviceSerial() +{ + return(m_serialNumber); +} + +const cm_kb_device* CMKeyboardAbstractController::GetDeviceData() +{ + return(cm_kb_device_list[m_deviceIndex]); +} + +std::string CMKeyboardAbstractController::GetLocation() +{ + return(m_sLocation); +} + +std::string CMKeyboardAbstractController::GetFirmwareVersion() +{ + return(m_sFirmwareVersion); +} + +int CMKeyboardAbstractController::GetProductID() +{ + return(m_productId); +} + +std::vector CMKeyboardAbstractController::SendCommand(std::vector buf, uint8_t fill) +{ + int status; + std::vector read; + + uint8_t data[CM_KEYBOARD_WRITE_SIZE]; + memset(data, fill, CM_KEYBOARD_WRITE_SIZE); + + size_t i = 1; + for(uint8_t b : buf) + { + data[i++] = b; + } + + std::lock_guard guard(m_mutexSendCommand); + status = hid_write(m_pDev, data, CM_KEYBOARD_WRITE_SIZE); + + if(status < 0) + { + LOG_ERROR("[%s] SendCommand() failed code %d.", m_deviceName.c_str(), status); + return(read); + } + + memset(data, 0, CM_KEYBOARD_WRITE_SIZE); + status = hid_read(m_pDev, data, CM_KEYBOARD_WRITE_SIZE); + + if(status < 0) + { + LOG_ERROR("[%s] SendCommand() failed code %d.", m_deviceName.c_str(), status); + return(read); + } + + for(i = 0; i < (size_t)status; i++) + { + read.push_back(data[i]); + } + + return(read); +} + +/*---------------------------------------------------------*\ +| Enter/leave direct control mode | +\*---------------------------------------------------------*/ +void CMKeyboardAbstractController::SetControlMode(uint8_t modeId) +{ + SendCommand({0x41, (uint8_t)modeId}); +}; + +/*---------------------------------------------------------*\ +| Sets the currently active profile. | +| byte[0] = 0x51 0x00 0x00 0x00 | +| byte[4] = profileId | +| - corresponds to saved keyboard profile i.e. [1-4] | +| - 0x05 - Used on MK and CK style keyboards? | +\*---------------------------------------------------------*/ +void CMKeyboardAbstractController::SetActiveProfile(uint8_t profileId) +{ + SendCommand({0x51, 0x00, 0x00, 0x00, profileId}); +}; + +uint8_t CMKeyboardAbstractController::GetActiveProfile() +{ + std::vector data = SendCommand({0x52, 0x00}); + + if(data.size() > 4) + { + return((int)data[4]); + } + + return(0xFF); // error +} + +/*---------------------------------------------------------*\ +| Saves changes in currently used profile. | +| byte[1] = 0x52 | +\*---------------------------------------------------------*/ +void CMKeyboardAbstractController::SaveActiveProfile() +{ + SendCommand({0x50, 0x55}); +} + +void CMKeyboardAbstractController::SetActiveEffect(uint8_t effectId) +{ + SendCommand({0x51, 0x28, 0x00, 0x00, effectId}); +}; + +void CMKeyboardAbstractController::SaveProfile() +{ + SendCommand({0x50, 0x55}); +} + + +uint8_t CMKeyboardAbstractController::GetModeStatus() +{ + std::vector data = SendCommand({0x52, 0x28}); + + return(data[4]); +}; + +std::string CMKeyboardAbstractController::GetHexString(std::vector buf) +{ + std::stringstream hexss; + + for(uint8_t b : buf) + { + hexss << std::hex << b << " "; + } + + return(hexss.str()); +} diff --git a/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardAbstractController.h b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardAbstractController.h new file mode 100644 index 0000000..ad765f3 --- /dev/null +++ b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardAbstractController.h @@ -0,0 +1,120 @@ +/*---------------------------------------------------------*\ +| CMKeyboardAbstractController.h | +| | +| Driver for Cooler Master keyboards | +| | +| Tam D (too.manyhobbies) 30 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "CMKeyboardDevices.h" +#include "KeyboardLayoutManager.h" +#include "RGBController.h" +#include "LogManager.h" + +#define HID_MAX_STR 255 +#define CM_KEYBOARD_WRITE_SIZE 65 +#define CM_MAX_LEDS 255 +#define CM_KEYBOARD_TIMEOUT 50 +#define CM_KEYBOARD_TIMEOUT_SHORT 3 + +struct cm_keyboard_effect +{ + uint8_t effectId; + uint8_t p1; + uint8_t p2; + uint8_t p3; + RGBColor color1; + RGBColor color2; +}; + +/*---------------------------------------------------------*\ +| byte[0] = 0x41 | +| byte[1] = modeId | +\*---------------------------------------------------------*/ +enum cm_keyboard_control_mode +{ + MODE_FIRMWARE = 0x00, + MODE_EFFECT = 0x01, + MODE_MANUAL_V2 = 0x02, + MODE_CUSTOM_PROFILE = 0x03, + MODE_CUSTOM_PROFILE_V2 = 0x05, + MODE_DIRECT = 0x80 +}; + +class CMKeyboardAbstractController +{ +public: + CMKeyboardAbstractController(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name); + virtual ~CMKeyboardAbstractController(); + + /*---------------------------------------------------------*\ + | Common USB controller fuctions | + \*---------------------------------------------------------*/ + int GetProductID(); + std::string GetDeviceName(); + std::string GetDeviceVendor(); + std::string GetDeviceSerial(); + std::string GetLocation(); + std::string GetFirmwareVersion(); + const cm_kb_device* GetDeviceData(); + + /*---------------------------------------------------------*\ + | Keyboard Layout Manager support funtions | + \*---------------------------------------------------------*/ + + /*---------------------------------------------------------*\ + | Common keyboard driver functions | + \*---------------------------------------------------------*/ + virtual void SetControlMode(uint8_t modeId); + virtual void SetActiveProfile(uint8_t profileId); + virtual uint8_t GetActiveProfile(); + virtual void SaveActiveProfile(); + virtual void SaveProfile(); + virtual void SetActiveEffect(uint8_t effectId); + virtual uint8_t GetModeStatus(); + virtual void InitializeModes(std::vector &modes) = 0; + virtual KEYBOARD_LAYOUT GetKeyboardLayout() = 0; + + /*---------------------------------------------------------*\ + | Protocol specific funtions to be implmented | + \*---------------------------------------------------------*/ + virtual void SetLeds(std::vector leds, std::vector colors) = 0; + virtual void SetSingleLED(uint8_t in_led, RGBColor in_color) = 0; + virtual void Initialize() = 0; + virtual void Shutdown() = 0; + virtual void SetLEDControl(bool bManual) = 0; // FW or SW control + virtual void SetCustomMode() = 0; + virtual void SetMode(mode selectedMode) = 0; + +protected: + /*---------------------------------------------------------*\ + | Utility functions. | + \*---------------------------------------------------------*/ + std::vector SendCommand(std::vector buf, uint8_t fill=0x00); + std::string GetHexString(std::vector buf); + std::string m_sFirmwareVersion; + std::string m_deviceName; + hid_device* m_pDev; + uint16_t m_productId; + uint16_t m_deviceIndex; + std::string m_vendorName; + std::string m_sLocation; + std::string m_serialNumber; + KEYBOARD_LAYOUT m_keyboardLayout; + std::map mapModeValueEffect; + std::mutex m_mutex; + std::mutex m_mutexSendCommand; +}; + diff --git a/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardDevices.cpp b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardDevices.cpp new file mode 100644 index 0000000..56b8afa --- /dev/null +++ b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardDevices.cpp @@ -0,0 +1,902 @@ +/*---------------------------------------------------------*\ +| CMKeyboardDevices.cpp | +| | +| Device list for Cooler Master keyboards | +| | +| Tam D (too.manyhobbies) 30 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CMKeyboardDevices.h" + +/*-------------------------------------------------------------------------*\ +| Coolermaster Key Values | +\*-------------------------------------------------------------------------*/ + +const std::vector mk_pro_s_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 96, 97, 98, 99, 104, 105, 106, 112, 113, 114, 67, 68, 69, 102, 103, 107, +/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP */ + 0, 1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, +/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN */ + 2, 3, 10, 11, 18, 19, 26, 27, 34, 35, 42, 43, 50, 51, 58, 59, 66, +/* CPLK A S D F G H J K L ; " # ENTR */ + 4, 5, 12, 13, 20, 21, 28, 29, 36, 37, 44, 45, 89, 52, +/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU */ + 6, 100, 7, 14, 15, 22, 23, 30, 31, 38, 39, 46, 47, 61, +/* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR */ + 91, 90, 92, 93, 94, 60, 95, 54, 63, 62, 70, +}; + +const std::vector mk_pro_l_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK P1 P2 P3 P4 */ + 11, 22, 30, 25, 27, 7, 51, 57, 62, 86, 87, 83, 85, 79, 72, 0, +/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP NMLK NMDV NMTM NMMI */ + 14, 15, 23, 31, 39, 38, 46, 47, 55, 63, 71, 70, 54, 81, 3, 1, 2, 100, 108, 116, 118, +/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NM7 NM8 NM9 NMPL */ + 9, 8, 16, 24, 32, 33, 41, 40, 48, 56, 64, 65, 49, 82, 94, 92, 88, 96, 104, 112, 110, +/* CPLK A S D F G H J K L ; ' \ ENTR NM4 NM5 NM6 */ + 17, 10, 18, 26, 34, 35, 43, 42, 50, 58, 66, 67, 68, 84, 97, 105, 113, +/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 73, 19, 12, 20, 28, 36, 37, 45, 44, 52, 60, 69, 74, 80, 98, 106, 114, 111, +/* LCTL LWIN LALT SPACE RALT RWFNC RMNU RCTRL ARWL ARDN ARWR NM0 NMPD */ + 6, 90, 75, 91, 77, 78, 61, 4, 95, 93, 5, 107, 115, +}; + +const std::vector mk850_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK AIM AIMU AIMD */ + 6, 27, 34, 41, 48, 62, 69, 76, 83, 90, 97, 104, 111, 118, 125, 132, +/* M1 BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP NMLK NMDV NMTM NMMI */ + 7, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98, 112, 119, 126, 133, 254, 147, 154, 161, +/* M2 TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NM7 NM8 NM9 NMPL */ + 8, 22, 29, 36, 43, 50, 57, 64, 71, 78, 85, 92, 99, 113, 120, 127, 134, 141, 148, 155, 162, +/* M3 CPLK A S D F G H J K L ; ' # ENTR NM4 NM5 NM6 */ + 9, 23, 30, 37, 44, 51, 58, 65, 72, 79, 86, 93, 0, 114, 142, 149, 156, +/* M4 LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 10, 0, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 115, 129, 143, 150, 157, 164, +/* M5 LCTL LWIN LALT SPACE RALT RWFNC RMNU RCTRL ARWL ARDN ARWR NM0 NMPD */ + 11, 18, 25, 53, 81, 88, 95, 116, 123, 130, 137, 144, 158, +}; + +/*-------------------------------------------------------------*\ +| CoolerMaster SK (60%) | +\*-------------------------------------------------------------*/ +const std::vector sk620_keymap = +{ +/* T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 */ +/* L1 ESC 1 2 3 4 5 6 7 8 9 0 - = BPSC R1 */ + 8, 15, 22, 29, 36, 43, 50, 57, 64, 71, 78, 85, 92, 106, +/* L2 TAB Q W E R T Y U I O P [ ] R2 */ + 9, 23, 30, 37, 44, 51, 58, 65, 72, 79, 86, 93, 100, +/* L3 CPLK A S D F G H J K L ; " \ ENTR R3 */ + 10, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94, 101, 108, +/* L4 LSFT ISO\ Z X C V B N M , . # RSFT ARWU DEL R$ */ + 11, 18, 25, 32, 39, 46, 53, 60, 67, 74, 81, 88, 95, 102, 109, +/* L5 LCTL LWIN LALT SPACE RALT RWFNC ARWL ARDN ARWR R5 */ + 12, 19, 26, 54, 82, 89, 96, 103, 110, +/* B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B15 */ +}; + +/*-------------------------------------------------------------*\ +| CoolerMaster SK (60%) | +\*-------------------------------------------------------------*/ +const std::vector sk622_keymap = +{ +/* T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 */ +/* L1 ESC 1 2 3 4 5 6 7 8 9 0 - = BPSC R1 */ + 8, 15, 22, 29, 36, 43, 50, 57, 64, 71, 78, 85, 92, 106, +/* L2 TAB Q W E R T Y U I O P [ ] R2 */ + 9, 23, 30, 37, 44, 51, 58, 65, 72, 79, 86, 93, 100, +/* L3 CPLK A S D F G H J K L ; " \ ENTR R3 */ + 10, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94, 101, 108, +/* L4 LSFT ISO\ Z X C V B N M , . # RSFT ARWU DEL R$ */ + 11, 18, 25, 32, 39, 46, 53, 60, 67, 74, 81, 88, 95, 102, 109, +/* L5 LCTL LWIN LALT SPACE RALT RWFNC ARWL ARDN ARWR R5 */ + 12, 19, 26, 54, 82, 89, 96, 103, 110, +/* B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B15 */ +}; + +const std::vector sk630_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 9, 33, 41, 49, 57, 73, 81, 89, 97, 105, 113, 121, 129, 137, 145, 153, +/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP */ + 10, 26, 34, 42, 50, 58, 66, 74, 82, 90, 98, 106, 114, 130, 138, 146, 154, +/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN */ + 11, 27, 35, 43, 51, 59, 67, 75, 83, 91, 99, 107, 115, 131, 139, 147, 155, +/* CPLK A S D F G H J K L ; " # ENTR */ + 12, 28, 36, 44, 52, 60, 68, 76, 84, 92, 100, 108, 116, 132, +/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU */ + 13, 21, 29, 37, 45, 53, 61, 69, 77, 85, 93, 101, 133, 149, +/* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR */ + 14, 22, 30, 62, 94, 102, 110, 134, 142, 150, 158, +}; + +const std::vector sk650_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 0, 24, 32, 40, 48, 64, 72, 80, 88, 96, 104, 112, 120, 128, 136, 144, +/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP NMLK NMDV NMTM NMMI */ + 1, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97, 105, 121, 129, 137, 145, 153, 161, 169, 177, +/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NM7 NM8 NM9 NMPL */ + 2, 18, 26, 34, 42, 50, 58, 66, 74, 82, 90, 98, 106, 122, 130, 138, 146, 154, 162, 170, 178, +/* CPLK A S D F G H J K L ; ' # ENTR NM4 NM5 NM6 */ + 3, 19, 27, 35, 43, 51, 59, 67, 75, 83, 91, 99, 107, 123, 155, 163, 171, +/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 4, 20, 28, 36, 44, 52, 60, 68, 76, 84, 92, 100, 124, 140, 156, 164, 172, 180, +/* LCTL LWIN LALT SPACE RALT RWFNC RMNU RCTRL ARWL ARDN ARWR NM0 NMPD */ + 5, 13, 21, 53, 85, 93, 101, 125, 133, 141, 149, 165, 173, +}; + +const std::vector sk652_keymap = +{ + +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 9, 33, 41, 49, 57, 73, 81, 89, 97, 105, 113, 121, 129, 137, 145, 153, +/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP NMLK NMDV NMTM NMMI */ + 10, 26, 34, 42, 50, 58, 66, 74, 82, 90, 98, 106, 114, 130, 138, 146, 154, 162, 170, 178, 186, +/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NM7 NM8 NM9 NMPL */ + 11, 27, 35, 43, 51, 59, 67, 75, 83, 91, 99, 107, 115, 131, 139, 147, 155, 163, 171, 179, 187, +/* CPLK A S D F G H J K L ; ' # ENTR NM4 NM5 NM6 */ + 12, 28, 36, 44, 52, 60, 68, 76, 84, 92, 100, 108, 116, 132, 164, 172, 180, +/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 13, 21, 29, 37, 45, 53, 61, 69, 77, 85, 93, 101, 133, 149, 165, 173, 181, 189, +/* LCTL LWIN LALT SPACE RALT RWFNC RMNU RCTRL ARWL ARDN ARWR NM0 NMPD */ + 14, 22, 30, 62, 94, 102, 110, 134, 142, 150, 158, 174, 182, +}; + +const std::vector sk653_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 0, 24, 32, 40, 48, 64, 72, 80, 88, 96, 104, 112, 120, 128, 136, 144, +/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP NMLK NMDV NMTM NMMI */ + 1, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97, 105, 121, 129, 137, 145, 153, 161, 169, 177, +/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NM7 NM8 NM9 NMPL */ + 2, 18, 26, 34, 42, 50, 58, 66, 74, 82, 90, 98, 106, 122, 130, 138, 146, 154, 162, 170, 178, +/* CPLK A S D F G H J K L ; ' # ENTR NM4 NM5 NM6 */ + 3, 19, 27, 35, 43, 51, 59, 67, 75, 83, 91, 99, 107, 123, 155, 163, 171, +/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 4, 20, 28, 36, 44, 52, 60, 68, 76, 84, 92, 100, 124, 140, 156, 164, 172, 180, +/* LCTL LWIN LALT SPACE RALT RWFNC RMNU RCTRL ARWL ARDN ARWR NM0 NMPD */ + 5, 13, 21, 53, 85, 93, 101, 125, 133, 141, 149, 165, 173, +}; + +const std::vector mk730_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 7, 28, 35, 42, 49, 63, 70, 77, 84, 91, 98, 105, 112, 119, 126, 133, +/* L1 BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP R1 */ + 8, 22, 29, 36, 43, 50, 57, 64, 71, 78, 85, 92, 99, 113, 120, 127, 134, +/* L2 TAB Q W E R T Y U I O P [ ] \ DEL END PGDN R2 */ + 9, 23, 30, 37, 44, 51, 58, 65, 72, 79, 86, 93, 100, 114, 121, 128, 135, +/* L3 CPLK A S D F G H J K L ; ' # ENTR R3 */ + 10, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94, 108, 115, +/* L4 LSFT ISO\ Z X C V B N M , . / RSFT ARWU R4 */ + 11, 18, 25, 32, 39, 46, 53, 60, 67, 74, 81, 88, 116, 130, +/* LCTL LWIN LALT SPACE RALT RWFNC RMNU RCTRL ARWL ARDN ARWR */ + 12, 19, 26, 54, 82, 89, 96, 117, 124, 131, 138, +/* B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B12 */ +}; + +const std::vector mk750_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK MUT PLA REW FFWD */ + 7, 28, 35, 42, 49, 63, 70, 77, 84, 91, 98, 105, 112, 119, 136, 133, +/* L1 BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP NMLK NMDV NMTM NMMI R1 */ + 8, 22, 29, 36, 43, 50, 57, 64, 71, 78, 85, 92, 99, 113, 120, 127, 134, 0, 148, 155, 162, +/* L2 TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NM7 NM8 NM9 NMPL R2 */ + 9, 23, 30, 37, 44, 51, 58, 65, 72, 79, 86, 93, 100, 114, 121, 128, 135, 142, 149, 156, 163, +/* L3 CPLK A S D F G H J K L ; ' # ENTR NM4 NM5 NM6 R3 */ + 10, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94, 108, 115, 143, 150, 157, +/* L4 LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER R4 */ + 11, 18, 25, 32, 39, 46, 53, 60, 67, 74, 81, 88, 116, 130, 144, 151, 158, 165, +/* LCTL LWIN LALT SPACE RALT RWFNC RMNU RCTRL ARWL ARDN ARWR NM0 NMPD */ + 12, 19, 26, 54, 82, 89, 96, 117, 124, 131, 138, 152, 159, +/* B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B12 B13 B14 B15 B16 B17 B18 */ +}; + + +const std::vector ck530_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 6, 21, 27, 34, 41, 55, 62, 69, 76, 83, 90, 97, 104, 111, 118, 125, +/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP */ + 7, 15, 22, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 105, 112, 119, 126, +/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN */ + 8, 16, 23, 29, 36, 43, 50, 57, 64, 71, 78, 85, 92, 106, 113, 120, 127, +/* CPLK A S D F G H J K L ; " # ENTR */ + 9, 17, 24, 30, 37, 44, 51, 58, 65, 72, 79, 86, 93, 107, +/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU */ + 10, 18, 25, 31, 38, 45, 52, 59, 66, 73, 80, 87, 108, 122, +/* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR */ + 11, 12, 19, 46, 74, 81, 88, 109, 116, 123, 130, +}; + +const std::vector ck530_v2_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 7, 28, 35, 42, 49, 63, 70, 77, 84, 91, 98, 105, 112, 119, 126, 133, +/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP */ + 8, 22, 29, 36, 43, 50, 57, 64, 71, 78, 85, 92, 99, 113, 120, 127, 134, +/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN */ + 9, 23, 30, 37, 44, 51, 58, 65, 72, 79, 86, 93, 100, 114, 121, 128, 135, +/* CPLK A S D F G H J K L ; " # ENTR */ + 10, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94, 108, 115, +/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU */ + 11, 18, 25, 32, 39, 46, 53, 60, 67, 74, 88, 81, 116, 130, +/* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR */ + 12, 19, 26, 54, 82, 89, 96, 117, 124, 131, 138, +}; + +const std::vector ck550_v2_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 0, 18, 24, 30, 36, 48, 54, 60, 66, 72, 78, 84, 90, 96, 102, 108, +/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP NMLK NMDV NMTM NMMI */ + 1, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, 91, 97, 103, 109, 115, 121, 127, 133, +/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NM7 NM8 NM9 NMPL */ + 2, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 74, 80, 92, 98, 104, 110, 116, 122, 128, 134, +/* CPLK A S D F G H J K L ; ' # ENTR NM4 NM5 NM6 */ + 3, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 87, 93, 117, 123, 129, +/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 4, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70,/*76,*/ 94, 106, 118, 124, 130, 136, +/* LCTL LWIN LALT SPACE RALT RWFNC RMNU RCTRL ARWL ARDN ARWR NM0 NMPD */ + 5, 11, 17, 41, 65, 71, 77, 95, 101, 107, 113, 125, 131, +}; + +const std::vector ck552_keymap = +{ +/* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 0, 18, 24, 30, 36, 48, 54, 60, 66, 72, 78, 84, 90, 96, 102, 108, +/* BKTK 1 2 3 4 5 6 7 8 9 0 - = BPSC INS HOME PGUP NMLK NMDV NMTM NMMI */ + 1, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, 91, 97, 103, 109, 115, 121, 127, 133, +/* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NM7 NM8 NM9 NMPL */ + 2, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 74, 80, 92, 98, 104, 110, 116, 122, 128, 134, +/* CPLK A S D F G H J K L ; ' # ENTR NM4 NM5 NM6 */ + 3, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 81, 93, 117, 123, 129, +/* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 4, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70,/*76,*/ 94, 106, 118, 124, 130, 136, +/* LCTL LWIN LALT SPACE RALT RWFNC RMNU RCTRL ARWL ARDN ARWR NM0 NMPD */ + 5, 11, 17, 41, 65, 71, 77, 95, 101, 107, 113, 125, 131, +}; + +/*-------------------------------------------------------------------------*\ +| KEYMAPS | +\*-------------------------------------------------------------------------*/ +keyboard_keymap_overlay_values mk_pro_s_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + mk_pro_s_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + }, +}; + +keyboard_keymap_overlay_values mk_pro_l_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + mk_pro_l_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + { 0, 0, 17, 101, "Key: P1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 109, "Key: P2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 117, "Key: P3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 119, "Key: P4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + }, +}; + +/*-------------------------------------------------------------*\ +| CoolerMaster MK85O Keyboard | +| Unknown Keys: ISO\, ISO# set to 0 | +\*-------------------------------------------------------------*/ +keyboard_keymap_overlay_values mk850_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + mk850_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + { 0, 0, 17, 146, "Key: Aim <|>", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // aimpad <|> + { 0, 0, 18, 153, "Key: Aim -", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // aimpad + + { 0, 0, 19, 160, "Key: Aim +", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // aimpad - + { 0, 1, 0, 0, "Key: M5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 1, "Key: M4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 2, "Key: M3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 3, "Key: M2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 4, "Key: M1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + }, +}; + +keyboard_keymap_overlay_values sk620_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_SIXTY, + { + sk620_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-----------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-----------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 7, "Light: Top 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 0, 1, 14, "Light: Top 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 2, 21, "Light: Top 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 3, 28, "Light: Top 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 4, 35, "Light: Top 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 5, 42, "Light: Top 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 6, 49, "Light: Top 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 7, 56, "Light: Top 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 8, 63, "Light: Top 9", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 9, 70, "Light: Top 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 10, 77, "Light: Top 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 11, 84, "Light: Top 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 12, 91, "Light: Top 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 13, 98, "Light: Top 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 14, 105, "Light: Top 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 0, "Light: Left 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 1, "Light: Left 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 2, "Light: Left 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 3, "Light: Left 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 4, "Light: Left 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 16, 112, "Light: Right 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 16, 113, "Light: Right 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 16, 114, "Light: Right 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 16, 115, "Light: Right 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 16, 116, "Light: Right 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 0, 6, "Light: Bottom 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 6, 1, 20, "Light: Bottom 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 2, 27, "Light: Bottom 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 3, 34, "Light: Bottom 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 4, 41, "Light: Bottom 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 5, 48, "Light: Bottom 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 6, 55, "Light: Bottom 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 7, 62, "Light: Bottom 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 8, 69, "Light: Bottom 9", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 9, 76, "Light: Bottom 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 10, 83, "Light: Bottom 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 11, 90, "Light: Bottom 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 12, 97, "Light: Bottom 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 13, 104, "Light: Bottom 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 14, 111, "Light: Bottom 15", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 14, 118, "Light: Bottom 16", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + }, +}; + +keyboard_keymap_overlay_values sk622_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_SIXTY, + { + sk622_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 7, "Light: Top 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 0, 1, 14, "Light: Top 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 2, 21, "Light: Top 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 3, 28, "Light: Top 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 4, 35, "Light: Top 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 5, 42, "Light: Top 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 6, 49, "Light: Top 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 7, 56, "Light: Top 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 8, 63, "Light: Top 9", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 9, 70, "Light: Top 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 10, 77, "Light: Top 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 11, 84, "Light: Top 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 12, 91, "Light: Top 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 13, 98, "Light: Top 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 14, 105, "Light: Top 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 0, "Light: Left 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 1, "Light: Left 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 2, "Light: Left 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 3, "Light: Left 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 4, "Light: Left 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 16, 112, "Light: Right 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 16, 113, "Light: Right 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 16, 114, "Light: Right 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 16, 115, "Light: Right 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 16, 116, "Light: Right 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 0, 6, "Light: Bottom 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 6, 1, 20, "Light: Bottom 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 2, 27, "Light: Bottom 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 3, 34, "Light: Bottom 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 4, 41, "Light: Bottom 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 5, 48, "Light: Bottom 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 6, 55, "Light: Bottom 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 7, 62, "Light: Bottom 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 8, 69, "Light: Bottom 9", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 9, 76, "Light: Bottom 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 10, 83, "Light: Bottom 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 11, 90, "Light: Bottom 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 12, 97, "Light: Bottom 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 13, 104, "Light: Bottom 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 14, 111, "Light: Bottom 15", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 14, 118, "Light: Bottom 16", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + }, +}; + +keyboard_keymap_overlay_values sk630_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + sk630_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + } +}; + +keyboard_keymap_overlay_values sk650_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + sk650_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + } +}; + +keyboard_keymap_overlay_values sk652_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + sk652_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + } +}; + +keyboard_keymap_overlay_values sk653_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + sk653_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + } +}; + +/*-------------------------------------------------------------*\ +| CoolerMaster MK730 Keyboard | +\*-------------------------------------------------------------*/ +keyboard_keymap_overlay_values mk730_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + mk730_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + { 0, 1, 0, 1, "Light: Left 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 2, "Light: Left 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 3, "Light: Left 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 4, "Light: Left 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 18, 141, "Light: Right 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 18, 142, "Light: Right 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 18, 143, "Light: Right 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 18, 144, "Light: Right 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 1, 13, "Light: Bottom 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 6, 2, 20, "Light: Bottom 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 3, 27, "Light: Bottom 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 4, 34, "Light: Bottom 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 5, 41, "Light: Bottom 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 6, 55, "Light: Bottom 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 7, 62, "Light: Bottom 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 8, 69, "Light: Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 10, 76, "Light: Bottom 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 11, 90, "Light: Bottom 9", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 12, 104, "Light: Bottom 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 13, 111, "Light: Bottom 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 14, 118, "Light: Bottom 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 15, 125, "Light: Bottom 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +/*-------------------------------------------------------------*\ +| CoolerMaster MK750 Keyboard | +| based on keymap defined in Signal. | +| The keymap needs the following adjustments | +| NMLK - Unknown set to 0 | +| SCLK - Unknown set to 0 | +| CAPS - Unknown set to 0 | +| Guesses on ISO\ and # | +\*-------------------------------------------------------------*/ +keyboard_keymap_overlay_values mk750_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + mk750_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + { 0, 0, 17, 140, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 147, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 154, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 161, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 1, "Light: Left 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 2, "Light: Left 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 3, "Light: Left 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 4, "Light: Left 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 22, 170, "Light: Right 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 22, 171, "Light: Right 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 22, 172, "Light: Right 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 22, 173, "Light: Right 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 1, 20, "Light: Bottom 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 6, 2, 27, "Light: Bottom 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 3, 34, "Light: Bottom 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 4, 41, "Light: Bottom 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 5, 55, "Light: Bottom 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 6, 62, "Light: Bottom 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 7, 69, "Light: Bottom 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 8, 76, "Light: Bottom 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 9, 83, "Light: Bottom 9", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 10, 90, "Light: Bottom 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 11, 104, "Light: Bottom 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 12, 111, "Light: Bottom 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 13, 118, "Light: Bottom 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 14, 125, "Light: Bottom 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 15, 132, "Light: Bottom 15", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 16, 146, "Light: Bottom 16", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 17, 153, "Light: Bottom 17", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 18, 160, "Light: Bottom 18", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +keyboard_keymap_overlay_values ck530_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + ck530_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + } +}; + +keyboard_keymap_overlay_values ck530_v2_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + ck530_v2_keymap, + { + /* Add more regional layout fixes here */ + } + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + } +}; + + +keyboard_keymap_overlay_values ck550v2_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + ck550_v2_keymap, + { + { + /* Add more regional layout fixes here */ + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + }, + }, + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + { 0, 0, 17, 120, "Indicator: N", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 126, "Indicator: C", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 132, "Indicator: S", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + }, +}; + +keyboard_keymap_overlay_values ck552_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + ck552_keymap, + { + { + /* Add more regional layout fixes here */ + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + }, + }, + }, + { + /*---------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, OpCode, | + \*---------------------------------------------------------------------------------------------------------*/ + }, +}; + +static const cm_kb_zone cm_generic_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, +}; + +cm_kb_device mk_pro_s_device +{ + COOLERMASTER_KEYBOARD_PRO_S_PID, + { + &cm_generic_zone, + }, + &mk_pro_s_layout, +}; + +cm_kb_device mk_pro_l_device +{ + COOLERMASTER_KEYBOARD_PRO_L_PID, + { + &cm_generic_zone, + }, + &mk_pro_l_layout, +}; + +cm_kb_device mk850_device +{ + COOLERMASTER_KEYBOARD_MK850_PID, + { + &cm_generic_zone, + }, + &mk850_layout, +}; + +cm_kb_device sk620w_device +{ + COOLERMASTER_KEYBOARD_SK620W_PID, + { + &cm_generic_zone, + }, + &sk620_layout, +}; + +cm_kb_device sk620b_device +{ + COOLERMASTER_KEYBOARD_SK620B_PID, + { + &cm_generic_zone, + }, + &sk620_layout, +}; + +cm_kb_device sk622w_device +{ + COOLERMASTER_KEYBOARD_SK622W_PID, + { + &cm_generic_zone, + }, + &sk622_layout, +}; + +cm_kb_device sk622b_device +{ + COOLERMASTER_KEYBOARD_SK622B_PID, + { + &cm_generic_zone, + }, + &sk622_layout, +}; + +cm_kb_device sk630_device +{ + COOLERMASTER_KEYBOARD_SK630_PID, + { + &cm_generic_zone, + }, + &sk630_layout, +}; + +cm_kb_device sk650_device +{ + COOLERMASTER_KEYBOARD_SK650_PID, + { + &cm_generic_zone, + }, + &sk650_layout, +}; + +cm_kb_device sk652_device +{ + COOLERMASTER_KEYBOARD_SK652_PID, + { + &cm_generic_zone, + }, + &sk652_layout, +}; + +cm_kb_device sk653_device +{ + COOLERMASTER_KEYBOARD_SK653_PID, + { + &cm_generic_zone, + }, + &sk652_layout, +}; + +/*---------------------------------------------------------*\ +| TODO: Keymap is incomplete. Extra keys mode enabled to | +| aid in key discovery. | +\*---------------------------------------------------------*/ +cm_kb_device mk730_device +{ + COOLERMASTER_KEYBOARD_MK730_PID, + { + &cm_generic_zone, + }, + &mk730_layout, +}; + +cm_kb_device mk750_device +{ + COOLERMASTER_KEYBOARD_MK750_PID, + { + &cm_generic_zone, + }, + &mk750_layout, +}; + +cm_kb_device ck530_device +{ + COOLERMASTER_KEYBOARD_CK530_PID, + { + &cm_generic_zone, + }, + &ck530_layout, +}; + +cm_kb_device ck530_v2_device +{ + COOLERMASTER_KEYBOARD_CK530_V2_PID, + { + &cm_generic_zone, + }, + &ck530_v2_layout, +}; + +cm_kb_device ck550_v2_device +{ + COOLERMASTER_KEYBOARD_CK550_V2_PID, + { + &cm_generic_zone, + }, + &ck550v2_layout, +}; + +cm_kb_device ck552_v2_device +{ + COOLERMASTER_KEYBOARD_CK552_V2_PID, + { + &cm_generic_zone, + }, + &ck552_layout, +}; + +cm_kb_device mk_pro_l_white_device +{ + COOLERMASTER_KEYBOARD_PRO_L_WHITE_PID, + { + &cm_generic_zone, + }, + &mk_pro_s_layout, +}; + +/*-----------------------------------------------------------------*\ +| KEYBOARDS | +\*-----------------------------------------------------------------*/ +const cm_kb_device* cm_kb_devices[] = +{ + &mk_pro_s_device, + &mk_pro_l_device, + &mk850_device, + &sk620w_device, + &sk620b_device, + &sk622w_device, + &sk622b_device, + &sk630_device, + &sk650_device, + &sk652_device, + &sk653_device, + &mk730_device, + &mk750_device, + &ck530_device, + &ck530_v2_device, + &ck550_v2_device, + &ck552_v2_device, + &mk_pro_l_white_device, +}; + +const unsigned int COOLERMASTER_KEYBOARD_DEVICE_COUNT = (sizeof(cm_kb_devices) / sizeof(cm_kb_devices[ 0 ])); +const cm_kb_device** cm_kb_device_list = cm_kb_devices; diff --git a/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardDevices.h b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardDevices.h new file mode 100644 index 0000000..c0ff86d --- /dev/null +++ b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardDevices.h @@ -0,0 +1,123 @@ +/*---------------------------------------------------------*\ +| CMKeyboardDevices.h | +| | +| Device list for Cooler Master keyboards | +| | +| Tam D (too.manyhobbies) 30 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "KeyboardLayoutManager.h" + +/*-----------------------------------------------------*\ +| List of all supported effects by this controller. | +| All of these effects are firmware controlled, and | +| they types of effects supported will depend on the | +| Keyboard. | +| | +| To enable a command, the SetEffect(effectId) needs | +| to be called. The specific effectId->Effect mapping | +| depends on the keyboard. | +\*-----------------------------------------------------*/ +enum cm_keyboard_effect_type +{ + NONE = 0, + DIRECT, + SINGLE, + FULLY_LIT, + STATIC, + BREATHE, + CYCLE, + WAVE, + RIPPLE, + CROSS, + RAINDROPS, + STARS, + SNAKE, + CUSTOMIZED, + INDICATOR, + MULTILAYER, + REACTIVE_FADE, + REACTIVE_PUNCH, + REACTIVE_TORNADO, + HEARTBEAT, + FIREBALL, + SNOW, + CIRCLE_SPECTRUM, + WATER_RIPPLE, + OFF +}; + +#define CM_KB_ZONES_MAX 1 + +typedef struct +{ + std::string name; + zone_type type; +} cm_kb_zone; + +typedef struct +{ + uint16_t product_id; + const cm_kb_zone * zones[CM_KB_ZONES_MAX]; + keyboard_keymap_overlay_values* layout_new; +} cm_kb_device; + +#define COOLERMASTER_VID 0x2516 + +#define CMKB_MAXKEYS 256 + +/*-----------------------------------------------------------------*\ +| keyboard support status is indicated to the right of | +| the PID definition. Attribution to products is also | +| indicated. | +| | +| libcmmk | +| signal https://gitlab.com/signalrgb/signal-plugins | +| openrgb | +| ck550-macos https://github.com/vookimedlo/ck550-macos/tree/master | +| reversed | +| | +| issue tickets, open merge requests etc are provided | +| for developer references. | +| # denotes issue ticket | +| ! denotes merge/pull request | +\*-----------------------------------------------------------------*/ +#define COOLERMASTER_KEYBOARD_CK351_PID 0x014F // unsupported +#define COOLERMASTER_KEYBOARD_CK530_PID 0x009F // [ck550-macos] +#define COOLERMASTER_KEYBOARD_CK530_V2_PID 0x0147 // [signal] +#define COOLERMASTER_KEYBOARD_CK550_V2_PID 0x0145 // [openrgb #800, #2863, signal] +#define COOLERMASTER_KEYBOARD_CK552_V2_PID 0x007F // [ck550-macos, signal] +#define COOLERMASTER_KEYBOARD_CK570_V2_PID 0x01E8 // unsupported +#define COOLERMASTER_KEYBOARD_CK720_PID 0x016B // unsupported +#define COOLERMASTER_KEYBOARD_CK721_PID 0x016D // unsupported +#define COOLERMASTER_KEYBOARD_CK721LINE_PID 0x01EE // unsupported +#define COOLERMASTER_KEYBOARD_PRO_L_PID 0x003B // [libcmmk !16] +#define COOLERMASTER_KEYBOARD_PRO_L_WHITE_PID 0x0047 // [libcmmk] +#define COOLERMASTER_KEYBOARD_PRO_S_PID 0x003C // [libcmmk #30 !31 !36, !37, !7, #5(closed), #3(closed)] +// MASTERKEYS PRO M [libcmmk #17] +#define COOLERMASTER_KEYBOARD_MK721_PID 0x016F // unsupported +#define COOLERMASTER_KEYBOARD_MK730_PID 0x008F // [openrgb #1630, libcmmk] +#define COOLERMASTER_KEYBOARD_MK750_PID 0x0067 // fw1.2 [libcmmk #25 !9, !14, signal] +#define COOLERMASTER_KEYBOARD_MK770_PID 0x01D5 // unsupported +#define COOLERMASTER_KEYBOARD_MK850_PID 0x0069 // [signal] +#define COOLERMASTER_KEYBOARD_SK620B_PID 0x0157 // [openrgb #4292] +#define COOLERMASTER_KEYBOARD_SK620W_PID 0x0159 // [openrgb #4292, signal] +#define COOLERMASTER_KEYBOARD_SK622B_PID 0x0149 // [openrgb #3110, signal #217(closed)] +#define COOLERMASTER_KEYBOARD_SK622W_PID 0x014B // [signal] +#define COOLERMASTER_KEYBOARD_SK630_PID 0x0089 // [openrgb #967, libcmmk !21] +#define COOLERMASTER_KEYBOARD_SK631B_PID 0x008B // unsupported +#define COOLERMASTER_KEYBOARD_SK631W_PID 0x0125 // [libcmmk] +#define COOLERMASTER_KEYBOARD_SK650_PID 0x008D // [openrgb #613, libcmmk #23 !37 !27 !28, signal] +#define COOLERMASTER_KEYBOARD_SK651B_PID 0x0091 // unsupported +#define COOLERMASTER_KEYBOARD_SK651W_PID 0x0127 // [signal] +#define COOLERMASTER_KEYBOARD_SK652_PID 0x015D // [signal] +#define COOLERMASTER_KEYBOARD_SK653_PID 0x01AB // [openrgb #3571, signal] + +extern const unsigned int COOLERMASTER_KEYBOARD_DEVICE_COUNT; +extern const cm_kb_device** cm_kb_device_list; diff --git a/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV1Controller.cpp b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV1Controller.cpp new file mode 100644 index 0000000..400af27 --- /dev/null +++ b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV1Controller.cpp @@ -0,0 +1,500 @@ +/*---------------------------------------------------------*\ +| CMKeyboardV1Controller.cpp | +| | +| Driver for Cooler Master MasterKeys (V1) keyboards | +| | +| Tam D (too.manyhobbies) 30 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CMKeyboardV1Controller.h" +#include "LogManager.h" + +CMKeyboardV1Controller::CMKeyboardV1Controller(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name) : CMKeyboardAbstractController(dev_handle, dev_info, dev_name) +{ + m_sFirmwareVersion = _GetFirmwareVersion(); +} + +CMKeyboardV1Controller::~CMKeyboardV1Controller() +{ +} + +void CMKeyboardV1Controller::Initialize() +{ + SetLEDControl(true); +} + +void CMKeyboardV1Controller::SetActiveEffect(uint8_t effectId) +{ + SendCommand({0x51, 0x28, 0x00, 0x00, effectId}); +} + +uint8_t CMKeyboardV1Controller::GetActiveEffect() +{ + std::vector data = SendCommand({0x52, 0x28}); + + return data[4]; +} + +void CMKeyboardV1Controller::SetEffect(uint8_t effectId, uint8_t p1, uint8_t p2, uint8_t p3, RGBColor color1, RGBColor color2) +{ + std::vector data; + + data.push_back(0x51); + data.push_back(0x2C); + data.push_back(0x00); // multilayer_mode - NOT SUPPORTED + data.push_back(0x00); + data.push_back(effectId); + data.push_back(p1); + data.push_back(p2); + data.push_back(p3); + data.push_back(0xFF); + data.push_back(0xFF); + data.push_back(RGBGetRValue(color1)); + data.push_back(RGBGetGValue(color1)); + data.push_back(RGBGetBValue(color1)); + data.push_back(RGBGetRValue(color2)); + data.push_back(RGBGetGValue(color2)); + data.push_back(RGBGetBValue(color2)); + + /*-------------------------------------------*\ + | Likely a bit mask for each LEDs. | + | 3 bits per LED x 127 possible LEDs ~48 bytes| + \*-------------------------------------------*/ + for(size_t i = 0; i < 48; i++) + { + data.push_back(0xFF); + } + + SetCustomMode(); + SetActiveEffect(effectId); + SendCommand(data); +} + +void CMKeyboardV1Controller::SetCustomMode() +{ + SetControlMode(0x01); +} + +void CMKeyboardV1Controller::SetMode(mode selectedMode) +{ + RGBColor color1 = 0; + RGBColor color2 = 0; + uint8_t cSpeed = 0; + uint8_t cDirection = 0; + uint8_t effectId = selectedMode.value; + bool bModeRandom = false; + + if(selectedMode.colors.size() >= 1) + { + color1 = selectedMode.colors[0]; + } + + if(selectedMode.colors.size() >= 2) + { + color2 = selectedMode.colors[1]; + } + + if(selectedMode.color_mode == MODE_COLORS_RANDOM) + { + bModeRandom = true; + } + + int selectedEffect = mapModeValueEffect[effectId]; + cSpeed = selectedMode.speed; + + switch(selectedMode.direction) + { + case MODE_DIRECTION_LEFT: + case MODE_DIRECTION_HORIZONTAL: + cDirection = 0x00; + break; + + case MODE_DIRECTION_RIGHT: + cDirection = 0x04; + break; + + case MODE_DIRECTION_UP: + case MODE_DIRECTION_VERTICAL: + cDirection = 0x06; + break; + + case MODE_DIRECTION_DOWN: + cDirection = 0x02; + break; + + default: + break; + } + + switch(selectedEffect) + { + case DIRECT: + case STATIC: + { + SetEffect(effectId, 0, 0, 0, color1, color2); + } + break; + + case CROSS: + case BREATHE: + case REACTIVE_PUNCH: + case CIRCLE_SPECTRUM: + case SNAKE: + { + SetEffect(effectId, cSpeed, 0, 0xFF, color1, color2); + } + break; + + case WAVE: + { + SetEffect(effectId, cSpeed, cDirection, 0xFF, color1, color2); + } + break; + + case RIPPLE: + { + SetEffect(effectId, cSpeed, bModeRandom ? 0x80 : 0x00, 0xFF, color1, color2); + } + break; + + case RAINDROPS: + { + SetEffect(effectId, 0x6a, 0x00, cSpeed, color1, color2); + } + break; + + case STARS: + { + SetEffect(effectId, cSpeed, 0x00, 0x10, color1, color2); + } + break; + + default: + break; + } +} + +void CMKeyboardV1Controller::InitializeModes(std::vector &modes) +{ + mode Direct; + Direct.name = "Direct"; + Direct.value = 0x02; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + + modes.push_back(Direct); + + mapModeValueEffect[0x02] = DIRECT; + + mode Static; + Static.name = "Static"; + Static.value = 0x00; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + mapModeValueEffect[0x00] = STATIC; + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = 0x01; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed_min = 0x46; + Breathing.speed_max = 0x27; + Breathing.speed = 0x36; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + modes.push_back(Breathing); + mapModeValueEffect[0x01] = BREATHE; + + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = 0x02; + Cycle.flags = MODE_FLAG_HAS_SPEED; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = 0x96; + Cycle.speed_max = 0x68; + Cycle.speed = 0x7F; + modes.push_back(Cycle); + mapModeValueEffect[0x02] = CIRCLE_SPECTRUM; + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = 0x03; + Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.speed_min = 0x3C; + Reactive.speed_max = 0x2F; + Reactive.speed = 0x35; + Reactive.colors_min = 2; + Reactive.colors_max = 2; + Reactive.colors.resize(2); + modes.push_back(Reactive); + mapModeValueEffect[0x03] = REACTIVE_PUNCH; + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = 0x04; + Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.speed_min = 0x48; + Wave.speed_max = 0x2A; + Wave.speed = 0x29; + Wave.direction = MODE_DIRECTION_LEFT; + Wave.colors_min = 1; + Wave.colors_max = 1; + Wave.colors.resize(1); + modes.push_back(Wave); + mapModeValueEffect[0x04] = WAVE; + + mode Ripple; + Ripple.name = "Ripple Effect"; + Ripple.value = 0x05; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED; + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripple.speed_min = 0x96; + Ripple.speed_max = 0x62; + Ripple.speed = 0x7C; + Ripple.colors_min = 2; + Ripple.colors_max = 2; + Ripple.colors.resize(2); + modes.push_back(Ripple); + mapModeValueEffect[0x05] = RIPPLE; + + mode Cross; + Cross.name = "Cross"; + Cross.value = 0x06; + Cross.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Cross.color_mode = MODE_COLORS_MODE_SPECIFIC; + Cross.speed_min = 0x2A; + Cross.speed_max = 0x48; + Cross.speed = 0x39; + Cross.colors_min = 2; + Cross.colors_max = 2; + Cross.colors.resize(2); + modes.push_back(Cross); + mapModeValueEffect[0x06] = CROSS; + + mode Raindrops; + Raindrops.name = "Raindrops"; + Raindrops.value = 0x07; + Raindrops.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Raindrops.color_mode = MODE_COLORS_MODE_SPECIFIC; + Raindrops.speed_min = 0x40; + Raindrops.speed_max = 0x08; + Raindrops.speed = 0x24; + Raindrops.colors_min = 2; + Raindrops.colors_max = 2; + Raindrops.colors.resize(2); + modes.push_back(Raindrops); + mapModeValueEffect[0x07] = RAINDROPS; + + mode Stars; + Stars.name = "Starfield"; + Stars.value = 0x08; + Stars.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Stars.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stars.speed_min = 0x46; + Stars.speed_max = 0x32; + Stars.speed = 0x3C; + Stars.colors_min = 2; + Stars.colors_max = 2; + Stars.colors.resize(2); + modes.push_back(Stars); + mapModeValueEffect[0x08] = STARS; + + mode Snake; + Snake.name = "Snake"; + Snake.value = 0x09; + Snake.flags = MODE_FLAG_HAS_SPEED; + Snake.color_mode = MODE_COLORS_NONE; + Snake.speed_min = 0x48; + Snake.speed_max = 0x2A; + Snake.speed = 0x39; + modes.push_back(Snake); + mapModeValueEffect[0x09] = SNAKE; +} + +struct cm_keyboard_effect CMKeyboardV1Controller::GetEffect(uint8_t effectId) +{ + std::vector data; + data.push_back(0x52); + data.push_back(0x2C); + data.push_back(0x00); + data.push_back(0x00); + data.push_back(effectId); + + data = SendCommand(data); + struct cm_keyboard_effect response; + + response.effectId = effectId; + response.p1 = data[5]; + response.p2 = data[6]; + response.p3 = data[7]; + response.color1 = ToRGBColor(data[10], data[11], data[12]); + response.color2 = ToRGBColor(data[13], data[14], data[15]); + + return response; +} + +std::vector CMKeyboardV1Controller::GetEnabledEffects() +{ + std::vector data; + + data = SendCommand({0x52, 0x29}); + + std::vector effects; + + for(size_t i = 4; data[i] != 0xFF; i++) + { + effects.push_back(data[i]); + } + + return effects; +} + + +void CMKeyboardV1Controller::SetLEDControl(bool bManual) +{ + uint8_t modeId = 0; // firmware + + if(bManual) + { + modeId = 0x02; // manual + } + + SetControlMode(modeId); +}; + +void CMKeyboardV1Controller::SetLeds(std::vector leds, std::vector colors) +{ + SetLEDControl(true); + + RGBColor rgbColorMap[CM_MAX_LEDS]; + memset(rgbColorMap, 0, sizeof(RGBColor)*CM_MAX_LEDS); + + for(size_t i = 0; i < leds.size(); i++) + { + rgbColorMap[leds[i].value] = colors[i]; + } + + RGBColor * pRGBColor = rgbColorMap; + + std::lock_guard guard(m_mutex); + + for(size_t i = 0; i < 8; i++) + { + std::vector data; + data.push_back(0xC0); + data.push_back(0x02); + data.push_back((uint8_t)(i * 2)); + data.push_back(0x00); + + for(size_t j = 0; j < 16; j++) + { + data.push_back(RGBGetRValue(*pRGBColor)); + data.push_back(RGBGetGValue(*pRGBColor)); + data.push_back(RGBGetBValue(*pRGBColor)); + + ++pRGBColor; + } + + SendCommand(data); + } +} + +void CMKeyboardV1Controller::SetSingleLED(uint8_t in_led, RGBColor in_color) +{ + std::vector data; + data.push_back(0xC0); + data.push_back(0x01); + data.push_back(0x01); + data.push_back(0x00); + data.push_back(in_led); + data.push_back(RGBGetRValue(in_color)); + data.push_back(RGBGetGValue(in_color)); + data.push_back(RGBGetBValue(in_color)); + + SendCommand(data); +} + +/*-------------------------------------------------------------------*\ +| Detect the Firmware Version | +| | +| Firmware version string is in the format: | +| .. | +| Where is: | +| UNK = 0, ANSI/US = 1, ISO/EU = 2, JP = 3 | +| Examples: | +| 1.2.1 = ANSI/US Keyboard (PRO S) | +| 2.2.1 = ISO/EU Keyboard (PRO L) | +\*-------------------------------------------------------------------*/ +std::string CMKeyboardV1Controller::_GetFirmwareVersion() +{ + std::vector read; + + SetControlMode(MODE_FIRMWARE); + read = SendCommand({0x01, 0x02}); + + char cVersionStr[CM_KEYBOARD_WRITE_SIZE]; + + for(size_t i = 0; i < read.size(); i++) + { + cVersionStr[i] = read[i]; + } + + cVersionStr[CM_KEYBOARD_WRITE_SIZE - 1] = 0; + + std::string sFirmwareVersion; + + sFirmwareVersion = std::string(cVersionStr+4); + + LOG_VERBOSE("[%s] GetFirmwareVersion(): [%s]", m_deviceName.c_str(), sFirmwareVersion.c_str()); + + return sFirmwareVersion; +} + +void CMKeyboardV1Controller::Shutdown() +{ + +} + +KEYBOARD_LAYOUT CMKeyboardV1Controller::GetKeyboardLayout() +{ + KEYBOARD_LAYOUT layout = KEYBOARD_LAYOUT_DEFAULT; + + if(m_sFirmwareVersion.empty()) + { + LOG_WARNING("[%s] GetKeyboardLayout() empty firmware string detected. Unable to detect firmware layout. Assuming defaults.", m_deviceName.c_str()); + + layout = KEYBOARD_LAYOUT_ANSI_QWERTY; + return layout; + } + + switch(m_sFirmwareVersion.c_str()[0]) + { + case '0': + default: + case '1': + layout = KEYBOARD_LAYOUT_ANSI_QWERTY; + break; + + case '2': + layout = KEYBOARD_LAYOUT_ISO_QWERTY; + break; + + case '3': + layout = KEYBOARD_LAYOUT_JIS; + break; + } + + return layout; +} diff --git a/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV1Controller.h b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV1Controller.h new file mode 100644 index 0000000..8e8e594 --- /dev/null +++ b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV1Controller.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| CMKeyboardV1Controller.h | +| | +| Driver for Cooler Master MasterKeys (V1) keyboards | +| | +| Tam D (too.manyhobbies) 30 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "CMKeyboardAbstractController.h" + +class CMKeyboardV1Controller : public CMKeyboardAbstractController +{ +public: + CMKeyboardV1Controller(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name); + ~CMKeyboardV1Controller(); + + /*---------------------------------------------------------*\ + | Protocol specific funtions to be implmented | + \*---------------------------------------------------------*/ + void SetLeds(std::vector leds, std::vector colors); + void SetSingleLED(uint8_t in_led, RGBColor in_color); + void Initialize(); + void Shutdown(); + void SetLEDControl(bool bManual); + + void SetActiveEffect(uint8_t effectId); + uint8_t GetActiveEffect(); + void SetEffect(uint8_t effectId, uint8_t p1, uint8_t p2, uint8_t p3, RGBColor color1, RGBColor color2); + struct cm_keyboard_effect GetEffect(uint8_t effectId); + void SetCustomMode(); + void SetMode(mode selectedMode); + std::vector GetEnabledEffects(); + void InitializeModes(std::vector &modes); + KEYBOARD_LAYOUT GetKeyboardLayout(); + +private: + std::string _GetFirmwareVersion(); +}; diff --git a/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV2Controller.cpp b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV2Controller.cpp new file mode 100644 index 0000000..29331d5 --- /dev/null +++ b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV2Controller.cpp @@ -0,0 +1,1055 @@ +/*---------------------------------------------------------*\ +| CMKeyboardV2Controller.cpp | +| | +| Driver for Cooler Master V2 keyboards | +| | +| Tam D (too.manyhobbies) 30 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "CMKeyboardV2Controller.h" +#include "LogManager.h" +#include "StringUtils.h" + +CMKeyboardV2Controller::CMKeyboardV2Controller(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name) : CMKeyboardAbstractController(dev_handle, dev_info, dev_name) +{ + m_sFirmwareVersion = _GetFirmwareVersion(); + m_bMoreFFs = false; +} + +CMKeyboardV2Controller::~CMKeyboardV2Controller() +{ +} + +/*-----------------------------------------------------------------*\ +| Firmware version string is in the format: | +| V.. | +| Where is: | +| UNK = 0, ANSI/US = 1, ISO/EU = 2, JP = 3 | +\*-----------------------------------------------------------------*/ +KEYBOARD_LAYOUT CMKeyboardV2Controller::GetKeyboardLayout() +{ + KEYBOARD_LAYOUT layout = KEYBOARD_LAYOUT_DEFAULT; + + if(m_sFirmwareVersion.empty()) + { + LOG_WARNING("[%s] _GetKeyboardLayout() empty firmware string detected. Assuming ANSI layout as default..", m_deviceName.c_str()); + + layout = KEYBOARD_LAYOUT_ANSI_QWERTY; + } + else + { + switch(m_sFirmwareVersion.c_str()[1]) + { + case '3': + layout = KEYBOARD_LAYOUT_JIS; + break; + + case '2': + layout = KEYBOARD_LAYOUT_ISO_QWERTY; + break; + + default: + case '1': + layout = KEYBOARD_LAYOUT_ANSI_QWERTY; + break; + } + } + + /*------------------------------------------------------------*\ + | Double check keyboard type per #3592, EU keyboard reported | + | with version ID in serial number. | + \*------------------------------------------------------------*/ + if(m_serialNumber.find("JP") != std::string::npos) + { + layout = KEYBOARD_LAYOUT_JIS; + } + + if(m_serialNumber.find("EU") != std::string::npos) + { + layout = KEYBOARD_LAYOUT_ISO_QWERTY; + } + + return(layout); +} + +/*-----------------------------------------------------------------*\ +| Gets the firmware version. | +| Strings are stored as UTF-16 so need to be converted to string | +| Firmware format is VX.YY.ZZ . | +\*-----------------------------------------------------------------*/ +std::string CMKeyboardV2Controller::_GetFirmwareVersion() +{ + std::vector read; + + SetControlMode(MODE_FIRMWARE); + read = SendCommand({0x12, 0x20}); + + uint8_t cVersionStr[CM_KEYBOARD_WRITE_SIZE]; + + size_t i = 0; + for(uint8_t it : read) + { + cVersionStr[i++] = it; + } + + std::u16string usFirmwareVersion(reinterpret_cast(cVersionStr+8)); + std::string sFirmwareVersion(StringUtils::u16string_to_string(usFirmwareVersion)); + + LOG_VERBOSE("[%s] GetFirmwareVersion(): [%s]", m_deviceName.c_str(), sFirmwareVersion.c_str()); + + return sFirmwareVersion; +} + +void CMKeyboardV2Controller::Initialize() +{ + switch(m_productId) + { + case COOLERMASTER_KEYBOARD_SK620B_PID: + case COOLERMASTER_KEYBOARD_SK620W_PID: + MagicCommand(0x09); + break; + + case COOLERMASTER_KEYBOARD_SK622B_PID: + case COOLERMASTER_KEYBOARD_SK622W_PID: + MagicStartupPacket(); + MagicCommand(0x09); + break; + + case COOLERMASTER_KEYBOARD_SK630_PID: + case COOLERMASTER_KEYBOARD_SK650_PID: + SetActiveProfile(0x0C); + m_bMoreFFs = true; + break; + + case COOLERMASTER_KEYBOARD_SK652_PID: + case COOLERMASTER_KEYBOARD_SK653_PID: + MagicStartupPacket(); + MagicCommand(0x0C); + m_bMoreFFs = true; + break; + + case COOLERMASTER_KEYBOARD_CK530_PID: + case COOLERMASTER_KEYBOARD_CK530_V2_PID: + SetActiveProfile(0x05); + MagicCommand(0x0A); + break; + + case COOLERMASTER_KEYBOARD_CK550_V2_PID: + SetActiveProfile(0x05); + MagicCommand(0x09); + break; + + case COOLERMASTER_KEYBOARD_MK730_PID: + SetActiveProfile(0x05); + MagicCommand(0x0A); + m_bMoreFFs = true; + break; + + case COOLERMASTER_KEYBOARD_CK552_V2_PID: + SetControlMode(0x80); + MagicCommand(0x01); + break; + + default: + SendCommand({0x56, 0x81, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xBB, 0xBB, 0xBB}); + break; + } +} + +void CMKeyboardV2Controller::Shutdown() +{ + +} + +/*---------------------------------------------------------*\ +| Required for some keyboards. No idea what it does. | +\*---------------------------------------------------------*/ +void CMKeyboardV2Controller::MagicStartupPacket() +{ + std::lock_guard guard(m_mutex); + + SendCommand({0x12}); + SendCommand({0x12, 0x20}); + SendCommand({0x12, 0x01}); + SendCommand({0x12, 0x22}); + SendCommand({0x42, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01}); + SendCommand({0x43, 0x00, 0x00, 0x00, 0x01}); +}; + +void CMKeyboardV2Controller::SetLEDControl(bool bManual) +{ + uint8_t modeId = 0; + + if(bManual) + { + modeId = 0x05; + } + + SetControlMode(modeId); +} + +void CMKeyboardV2Controller::SetLeds(std::vector leds, std::vector colors) +{ + SetLEDControl(true); + Initialize(); + + RGBColor rgbColorMap[CM_MAX_LEDS]; + memset(rgbColorMap, 0, sizeof(RGBColor)*CM_MAX_LEDS); + + for(size_t i = 0; i < leds.size(); i++) + { + rgbColorMap[leds[i].value] = colors[i]; + } + + /*---------------------------------------------------------*\ + | Convert color array to linear map of RGB positions. | + | Map is sequential layout of RGB for each LED. i indicates | + | the position in the map (i*3), | + \*---------------------------------------------------------*/ + uint8_t linearColorMap[CM_MAX_LEDS*3] = {0,}; + + for(size_t i = 0; i < CM_MAX_LEDS; i++) + { + linearColorMap[i*3] = RGBGetRValue(rgbColorMap[i]); + linearColorMap[i*3+1] = RGBGetGValue(rgbColorMap[i]); + linearColorMap[i*3+2] = RGBGetBValue(rgbColorMap[i]); + } + + /*---------------------------------------------------------*\ + | For future reference, some keyboares may designate a | + | different number of max LEDs. | + \*---------------------------------------------------------*/ + const uint8_t nLEDs = 0xC1; + + /*---------------------------------------------------------*\ + | Build initial packet. | + | This specifies some of the initial parameters such as the | + | number of LEDs, and the first grouping of LEDs. | + | The packet structure seems to be fixed for these first 24 | + | bytes. | + \*---------------------------------------------------------*/ + std::vector data = + { + 0x56, 0x83, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x00, nLEDs, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 + }; + + size_t pktIdx; + for(pktIdx = 0; pktIdx < 40; pktIdx++) + { + data.push_back(linearColorMap[pktIdx]); + } + + std::lock_guard guard(m_mutex); + + SendCommand(data); + + /*---------------------------------------------------------*\ + | Build subsequent packets. | + | Aproximately 9 packets of 60 bytes each. Total may change | + | if we get a keyboard with an insane number of LEDs. | + \*---------------------------------------------------------*/ + for(size_t i = 1; i < 10; ++i) + { + data.clear(); + data.push_back(0x56); + data.push_back(0x83); + data.push_back((uint8_t)i); + data.push_back(0x00); + + for(size_t j = 0; j < 60; j++) + { + data.push_back(linearColorMap[pktIdx++]); + } + + SendCommand(data); + } + + SendApplyPacket(0xFF); +} + +void CMKeyboardV2Controller::SetSingleLED(uint8_t /*in_led*/, RGBColor /*in_color*/) +{ +} + +void CMKeyboardV2Controller::MagicCommand(uint8_t profileId) +{ + SendCommand( + { + 0x56, 0x81, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + profileId, 0x00, 0x00, 0x00, 0xBB, 0xBB, 0xBB, 0xBB} + ); +}; + +void CMKeyboardV2Controller::_SetEffectMode(uint8_t effectId) +{ + std::vector data; + + switch(effectId) + { + case STATIC: + case DIRECT: + case CYCLE: + case BREATHE: + case CIRCLE_SPECTRUM: + case REACTIVE_TORNADO: + data = std::vector + { + 0x56, 0x81, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, + }; + break; + + case WAVE: + data = std::vector + { + 0x56, 0x81, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x99, 0x99, 0x99, 0x99 + }; + break; + + case STARS: + case RAINDROPS: + case SNOW: + case CROSS: + case RIPPLE: + case REACTIVE_PUNCH: + case REACTIVE_FADE: + case HEARTBEAT: + case FIREBALL: + case WATER_RIPPLE: + data = std::vector + { + 0x56, 0x81, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, + }; + break; + + case CUSTOMIZED: + data = std::vector{ + 0x56, 0x81, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, + }; + break; + + default: + break; + } + + SendCommand(data); +} + +void CMKeyboardV2Controller::SendApplyPacket(uint8_t mode) +{ + SendCommand({0x51, 0x28, 0x00, 0x00, mode}); +} + + +/*---------------------------------------------------------*\ +| Sets the speed for each mode. Seems like almost every mode| +| has a different set of speed settings. | +\*---------------------------------------------------------*/ +void CMKeyboardV2Controller::_UpdateSpeed(mode selectedMode, uint8_t &cSpeed1, uint8_t &cSpeed2) +{ + std::vectorvSpeed1{0x00, 0x00, 0x00, 0x00, 0x00}; + std::vectorvSpeed2{0x00, 0x00, 0x00, 0x00, 0x00}; + + switch(selectedMode.value) + { + case CROSS: + case REACTIVE_FADE: + vSpeed1 = std::vector{0x17, 0x0E, 0x0B, 0x0A, 0x04}; + vSpeed2 = std::vector{0x01, 0x01, 0x02, 0x05, 0x04}; + break; + + case WAVE: + vSpeed1 = std::vector{0x17, 0x0D, 0x07, 0x09, 0x08}; + vSpeed2 = std::vector{0x01, 0x04, 0x06, 0x0C, 0x11}; + break; + + case REACTIVE_PUNCH: + vSpeed1 = std::vector{0x0E, 0x0A, 0x04, 0x07, 0x01}; + break; + + case BREATHE: + vSpeed1 = std::vector{0x08, 0x0A, 0x0C, 0x07, 0x09}; + vSpeed2 = std::vector{0x01, 0x02, 0x04, 0x04, 0x09}; + break; + + case CIRCLE_SPECTRUM: + case REACTIVE_TORNADO: + switch (selectedMode.direction) + { + case MODE_DIRECTION_LEFT: + vSpeed1 = std::vector{0xFF, 0xFE, 0xFD, 0xFC, 0xFC}; + vSpeed2 = std::vector{0x04, 0x08, 0x08, 0x0C, 0x00}; + break; + + default: + case MODE_DIRECTION_RIGHT: + vSpeed1 = std::vector{0x00, 0x01, 0x02, 0x03, 0x04}; + vSpeed2 = std::vector{0x0C, 0x08, 0x08, 0x04, 0x00}; + break; + } + break; + + case CYCLE: + vSpeed1 = std::vector{0x10, 0x0C, 0x08, 0x04, 0x00}; + break; + + case RIPPLE: + case WATER_RIPPLE: + vSpeed1 = std::vector{0x36, 0x18, 0x0C, 0x06, 0x02}; + break; + + case FIREBALL: + case HEARTBEAT: + vSpeed1 = std::vector{0x01, 0x02, 0x03, 0x05, 0x09}; + break; + + case RAINDROPS: + case SNOW: + vSpeed1 = std::vector{0x0B, 0x08, 0x05, 0x02, 0x00}; + vSpeed2 = std::vector{0x08, 0x18, 0x30, 0x38, 0x40}; + break; + + case STARS: + vSpeed1 = std::vector{0x17, 0x0E, 0x08, 0x0A, 0x0A}; + vSpeed2 = std::vector{0x01, 0x01, 0x01, 0x02, 0x04}; + break; + + case OFF: + default: + break; + } + + if(selectedMode.speed >= 1 && selectedMode.speed <= 5) + { + cSpeed1 = vSpeed1[selectedMode.speed-1]; + cSpeed2 = vSpeed2[selectedMode.speed-1]; + } +} + +void CMKeyboardV2Controller::SetCustomMode() +{ + std::lock_guard guard(m_mutex); + + SendCommand({0x41, 0x80}); + SendCommand({0x52}); +} + +void CMKeyboardV2Controller::SetMode(mode selectedMode) +{ + std::vector data(64, 0); + std::vector data1(64, 0); + std::vector data2(64, 0); + + uint8_t cColor1_R = 0; + uint8_t cColor1_G = 0; + uint8_t cColor1_B = 0; + uint8_t cColor2_R = 0; + uint8_t cColor2_G = 0; + uint8_t cColor2_B = 0; + + RGBColor color1 = 0; + RGBColor color2 = 0; + uint8_t cSpeed1 = 0; + uint8_t cSpeed2 = 0; + + _UpdateSpeed(selectedMode, cSpeed1, cSpeed2); + + uint8_t cBright = (uint8_t)selectedMode.brightness; + uint8_t cDirection = 0; + uint8_t effectId = selectedMode.value; + + if(selectedMode.colors.size() >= 1) + { + color1 = selectedMode.colors[0]; + cColor1_R = (uint8_t)RGBGetRValue(color1); + cColor1_G = (uint8_t)RGBGetGValue(color1); + cColor1_B = (uint8_t)RGBGetBValue(color1); + } + + if(selectedMode.colors.size() >= 2) + { + color2 = selectedMode.colors[1]; + cColor2_R = (uint8_t)RGBGetRValue(color2); + cColor2_G = (uint8_t)RGBGetGValue(color2); + cColor2_B = (uint8_t)RGBGetBValue(color2); + } + + switch(selectedMode.direction) + { + case MODE_DIRECTION_LEFT: + case MODE_DIRECTION_HORIZONTAL: + cDirection = 0x04; + break; + + case MODE_DIRECTION_RIGHT: + cDirection = 0x00; + break; + + case MODE_DIRECTION_UP: + case MODE_DIRECTION_VERTICAL: + cDirection = 0x06; + break; + + case MODE_DIRECTION_DOWN: + cDirection = 0x02; + break; + + default: + break; + } + + SetCustomMode(); + + std::lock_guard guard(m_mutex); + _SetEffectMode(effectId); + + switch(effectId) + { + case OFF: + break; + + case DIRECT: + break; + + case STATIC: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, 0x00, 0x00, + }; + SendCommand(data); + } + break; + + case WAVE: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x32, 0x00, 0xC1, cSpeed1, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, cBright, 0x00, cDirection, cSpeed2, 0x00, + 0x00, 0x04, 0x08 + }; + SendCommand(data); + } + break; + + case STARS: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x0D, 0x00, 0x0D, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor2_R, cColor2_G, cColor2_B, cBright, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x80, cSpeed1, 0x10, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, cSpeed2, 0x01, + 0x01, 0x10, 0x08, 0x01, 0x10, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + + data1 = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF + }; + + data2 = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF + }; + + data = m_bMoreFFs ? data2 : data1; + + SendCommand(data); + } + break; + + case RAINDROPS: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x0D, 0x00, 0x0D, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor2_R, cColor2_G, cColor2_B, cBright, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x40, 0x00, 0x80, cSpeed1, 0x10, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, 0x03, 0x00, + cSpeed2, 0x18, 0x04, 0x10, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + + data1 = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF + }; + + data2 = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF + }; + + data = m_bMoreFFs ? data2 : data1; + + SendCommand(data); + } + break; + + case SNOW: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x0D, 0x00, 0x0D, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor2_R, cColor2_G, cColor2_B, cBright, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x40, 0x00, 0x80, cSpeed1, 0x10, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, 0x03, 0x00, + cSpeed2, 0xFF, 0x10, 0x10, 0x01, 0x40, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + + data = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + } + break; + + case CYCLE: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x31, 0x00, 0xC1, cSpeed1, 0x00, 0x00, 0x00, + 0x40, 0x00, 0xFF, cBright, 0x00, 0x00, 0x03, 0x00, + }; + + SendCommand(data); + } + break; + + case BREATHE: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0xC1, cSpeed1, 0x00, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x01, 0x00, cSpeed2, 0x00, + }; + SendCommand(data); + } + break; + + case CIRCLE_SPECTRUM: + { + /*---------------------------------------------------*\ + | Speed and directions are interlinked. Probably has | + | to do how speed is computed within the firmware. | + | The speed values for this mode take into account | + | the direction. | + \*---------------------------------------------------*/ + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x34, 0x00, 0xC1, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, cBright, 0x00, 0x00, cSpeed1, 0x00, + 0x00, cSpeed2, 0x00, 0x04, 0xA0, 0x00, 0x30, 0x00, + }; + SendCommand(data); + } + break; + + case CROSS: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x0C, 0x00, 0x0C, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor2_R, cColor2_G, cColor2_B, cBright, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x80, cSpeed1, 0x10, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, cSpeed2, 0x01, + 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + + data1 = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; + + data2 = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; + + data = m_bMoreFFs ? data2 : data1; + SendCommand(data); + } + break; + + case SINGLE: + break; + + case RIPPLE: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x0C, 0x00, 0x0C, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor2_R, cColor2_G, cColor2_B, cBright, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x82, 0x00, 0x80, cSpeed1, 0x10, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + + data = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + } + break; + + case REACTIVE_PUNCH: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x0B, 0x00, 0x0B, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor2_R, cColor2_G, cColor2_B, cBright, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x80, 0x00, 0x80, cSpeed1, 0x10, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, 0x01, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + SendCommand(data); + + data1 = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF + }; + + data2 = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF + }; + + data = m_bMoreFFs ? data2 : data1; + + SendCommand(data); + } + break; + + case REACTIVE_FADE: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x0C, 0x00, 0x0C, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor2_R, cColor2_G, cColor2_B, cBright, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x80, cSpeed1, 0x10, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + + data1 = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; + + data2 = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; + + data = m_bMoreFFs ? data2 : data1; + + SendCommand(data); + } + break; + + case REACTIVE_TORNADO: + { + /*---------------------------------------------------*\ + | Speed and directions are interlinked. Probably has | + | to do how speed is computed within the firmware. | + | The speed values for this mode take into account | + | the direction. | + \*---------------------------------------------------*/ + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x83, 0x00, 0xC1, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, cBright, 0x00, 0x00, cSpeed1, 0x00, + 0x00, cSpeed2, 0x00, 0x00, 0x30, 0x00, 0x10, 0x00, + }; + SendCommand(data); + } + break; + + case HEARTBEAT: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x0C, 0x00, 0x0C, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor2_R, cColor2_G, cColor2_B, cBright, 0x00, 0x00, 0x00, 0x00, + 0x20, 0xA0, 0x00, 0x80, 0x20, 0x10, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, cSpeed1, 0x00, + 0x02, 0xFF, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + + data = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; + SendCommand(data); + } + break; + + case FIREBALL: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x0C, 0x00, 0x0C, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor2_R, cColor2_G, cColor2_B, cBright, 0x00, 0x00, 0x00, 0x00, + 0x20, 0xA0, 0x00, 0x80, 0x10, 0x10, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, cSpeed1, 0x00, + 0x01, 0x08, 0x09, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + + data = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; + SendCommand(data); + } + break; + + case WATER_RIPPLE: + { + data = std::vector + { + 0x56, 0x83, 0x00, 0x00, 0x0C, 0x00, 0x0C, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, + cColor2_R, cColor2_G, cColor2_B, cBright, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x82, 0x00, 0x80, cSpeed1, 0x10, 0x00, 0x00, + cColor1_R, cColor1_G, cColor1_B, cBright, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x90, 0x14, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + SendCommand(data); + + data = std::vector + { + 0x56, 0x83, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; + SendCommand(data); + } + break; + + case SNAKE: + default: + break; + } + + switch(effectId) + { + case OFF: + SendCommand({0x51, 0x28, 0x00, 0x00, 0x10}); + SendCommand({0x41, 0x80}); + break; + + default: + SendCommand({0x41, 0x80}); + SendCommand({0x51, 0x28, 0x00, 0x00, 0xFF}); + break; + } +} + +struct stCMKeyboardV2_mode CMKeyboardV2_modes[] = +{ +/*----------------------------------------------------------------------------------------------------------------------------------------*\ +| Speed Brightness | +| NAME VALUE MIN MAX SET MAX DIR Number of Colors COLOR_MODE FLAGS | +\*----------------------------------------------------------------------------------------------------------------------------------------*/ + {"Direct", DIRECT, 1, 5, 3, 0xFF, 0, 0, MODE_COLORS_PER_LED, MODE_FLAG_HAS_PER_LED_COLOR}, + {"Static", STATIC, 1, 5, 3, 0xFF, 0, 1, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS }, + {"Wave", WAVE, 1, 5, 3, 0xFF, MODE_DIRECTION_LEFT,1, MODE_COLORS_NONE, MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_DIRECTION_LR | + MODE_FLAG_HAS_DIRECTION_UD}, + {"Crosshair", CROSS, 1, 5, 3, 0xFF, 0, 2, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Reactive Fade",REACTIVE_FADE,1,5, 3, 0xFF, 0, 2, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Stars", STARS, 1, 5, 3, 0xFF, 0, 2, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Raindrops", RAINDROPS, 1, 5, 3, 0xFF, 0, 2, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Color Cycle", CYCLE, 1, 5, 3, 0xFF, 0, 0, MODE_COLORS_NONE, MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Breathing", BREATHE, 1, 5, 3, 0xFF, 0, 1, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Ripple", RIPPLE, 1, 5, 3, 0xFF, 0, 2, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Fireball", FIREBALL, 1, 5, 3, 0xFF, 0, 2, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Water Ripple",WATER_RIPPLE,1,5, 3, 0xFF, 0, 2, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Reactive Punch",REACTIVE_PUNCH,1,5,3, 0xFF, 0, 2, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Snowing", SNOW, 1, 5, 3, 0xFF, 0, 2, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Heartbeat", HEARTBEAT, 1, 5, 3, 0xFF, 0, 2, MODE_COLORS_MODE_SPECIFIC, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED }, + {"Circle Spectrum",CIRCLE_SPECTRUM,1,5,3,0xFF, MODE_DIRECTION_LEFT,0, MODE_COLORS_NONE, MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_DIRECTION_LR}, + {"Reactive Tornado",REACTIVE_TORNADO,1,5,3,0xFF,MODE_DIRECTION_LEFT,0, MODE_COLORS_NONE, MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_DIRECTION_LR}, + {"Off", OFF, 0, 0, 0, 0xFF, MODE_DIRECTION_LEFT, 0, MODE_COLORS_NONE, 0}, + { 0, NONE, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +void CMKeyboardV2Controller::InitializeModes(std::vector &modes) +{ + stCMKeyboardV2_mode * pCurrentMode = CMKeyboardV2_modes; + + while(pCurrentMode->value != NONE) + { + mode m; + + m.name = std::string(pCurrentMode->name); + m.value = pCurrentMode->value; + m.flags = pCurrentMode->flags; + m.color_mode = pCurrentMode->color_mode; + m.speed_min = pCurrentMode->speed_min; + m.speed_max = pCurrentMode->speed_max; + m.speed = pCurrentMode->speed; + m.brightness_min = 0x00; + m.brightness_max = 0xFF; + m.brightness = 0xFF; + m.colors_min = pCurrentMode->nColors; + m.colors_max = pCurrentMode->nColors; + m.colors.resize(pCurrentMode->nColors); + m.direction = pCurrentMode->direction; + + modes.push_back(m); + + pCurrentMode++; + } +} diff --git a/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV2Controller.h b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV2Controller.h new file mode 100644 index 0000000..0b09edd --- /dev/null +++ b/Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV2Controller.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| CMKeyboardV2Controller.h | +| | +| Driver for Cooler Master V2 keyboards | +| | +| Tam D (too.manyhobbies) 30 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "CMKeyboardAbstractController.h" + +struct stCMKeyboardV2_mode +{ + const char *name; + unsigned int value; + unsigned int speed_min; + unsigned int speed_max; + unsigned int speed; + unsigned int brightness; + unsigned int direction; + unsigned int nColors; + unsigned int color_mode; + unsigned int flags; +}; + +class CMKeyboardV2Controller : public CMKeyboardAbstractController +{ +public: + CMKeyboardV2Controller(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name); + ~CMKeyboardV2Controller(); + + /*---------------------------------------------------------*\ + | Protocol specific funtions to be implmented | + \*---------------------------------------------------------*/ + void SetLeds(std::vector leds, std::vector colors); + void SetSingleLED(uint8_t in_led, RGBColor in_color); + void Initialize(); + void Shutdown(); + void SetLEDControl(bool bManual); + void SendApplyPacket(uint8_t mode); + void MagicStartupPacket(); + void MagicCommand(uint8_t profileId); + void SetCustomMode(); + void SetMode(mode selectedMode); + void SetEffect(uint8_t effectId, uint8_t p1, uint8_t p2, uint8_t p3, RGBColor color1, RGBColor color2); + void InitializeModes(std::vector &modes); + KEYBOARD_LAYOUT GetKeyboardLayout(); + +private: + void _SetEffectMode(uint8_t effectId); + void _UpdateSpeed(mode selectedMode, uint8_t &cSpeed1, uint8_t &cSpeed2); + std::string _GetFirmwareVersion(); + + bool m_bMoreFFs; +}; diff --git a/Controllers/CoolerMasterController/CMKeyboardController/RGBController_CMKeyboardController.cpp b/Controllers/CoolerMasterController/CMKeyboardController/RGBController_CMKeyboardController.cpp new file mode 100644 index 0000000..8cedd17 --- /dev/null +++ b/Controllers/CoolerMasterController/CMKeyboardController/RGBController_CMKeyboardController.cpp @@ -0,0 +1,197 @@ +/*---------------------------------------------------------*\ +| RGBController_CMKeyboardController.cpp | +| | +| RGBController for Cooler Master keyboards | +| | +| Tam D (too.manyhobbies) 30 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CMKeyboardController.h" +#include "CMKeyboardDevices.h" + +/**------------------------------------------------------------------*\ + @name Coolermaster Masterkeys Keyboards + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCoolerMasterV1Keyboards,DetectCoolerMasterV2Keyboards + @comment + In CMKeyboardV1Controller brightness control not supported. + Supported effects differ between CMKeyboardV1Controller and + CMKeyboardV2Controller. +\*-------------------------------------------------------------------*/ +RGBController_CMKeyboardController::RGBController_CMKeyboardController(CMKeyboardAbstractController* pController) +{ + m_pController = pController; + vendor = m_pController->GetDeviceVendor(); + type = DEVICE_TYPE_KEYBOARD; + description = "Cooler Master Keyboard Device"; + version = m_pController->GetFirmwareVersion(); + + /*----------------------------------------------------------------*\ + | Coolermaster uses the name field to store the serial number in | + | many of their keyboards. | + \*----------------------------------------------------------------*/ + serial = m_pController->GetDeviceSerial(); + location = m_pController->GetLocation(); + m_keyboardLayout = m_pController->GetKeyboardLayout(); + name = m_pController->GetDeviceName(); + + m_pController->InitializeModes(modes); + + SetupZones(); +} + +RGBController_CMKeyboardController::~RGBController_CMKeyboardController() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + if(zones[zone_index].matrix_map->map != NULL) + { + delete zones[zone_index].matrix_map->map; + } + + delete zones[zone_index].matrix_map; + } + } + + if(m_pController) + { + delete m_pController; + } +} + +#define COOLERMASTER_ZONES_MAX 1 +void RGBController_CMKeyboardController::SetupZones() +{ + std::string physical_size; + unsigned int max_led_value = 0; + const cm_kb_device* coolermaster = m_pController->GetDeviceData(); + + /*---------------------------------------------------------*\ + | Fill in zones from the device data | + \*---------------------------------------------------------*/ + for(size_t i = 0; i < COOLERMASTER_ZONES_MAX; i++) + { + if(coolermaster->zones[i] == NULL) + { + break; + } + else + { + zone new_zone; + + new_zone.name = coolermaster->zones[i]->name; + new_zone.type = coolermaster->zones[i]->type; + + if(new_zone.type == ZONE_TYPE_MATRIX) + { + KeyboardLayoutManager new_kb(m_keyboardLayout, coolermaster->layout_new->base_size, coolermaster->layout_new->key_values); + + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + + if(coolermaster->layout_new->base_size != KEYBOARD_SIZE_EMPTY) + { + /*---------------------------------------------------------*\ + | Minor adjustments to keyboard layout | + \*---------------------------------------------------------*/ + keyboard_keymap_overlay_values* temp = coolermaster->layout_new; + new_kb.ChangeKeys(*temp); + + new_map->height = new_kb.GetRowCount(); + new_map->width = new_kb.GetColumnCount(); + new_map->map = new unsigned int[new_map->height * new_map->width]; + + /*---------------------------------------------------------*\ + | Matrix map still uses declared zone rows and columns | + | as the packet structure depends on the matrix map | + \*---------------------------------------------------------*/ + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT, new_map->height, new_map->width); + + /*---------------------------------------------------------*\ + | Create LEDs for the Matrix zone | + | Place keys in the layout to populate the matrix | + \*---------------------------------------------------------*/ + new_zone.leds_count = new_kb.GetKeyCount(); + LOG_DEBUG("[%s] Created KB matrix with %d rows and %d columns containing %d keys", + m_pController->GetDeviceName().c_str(), new_kb.GetRowCount(), new_kb.GetColumnCount(), new_zone.leds_count); + + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + max_led_value = std::max(max_led_value, new_led.value); + leds.push_back(new_led); + } + } + + /*---------------------------------------------------------*\ + | Add 1 the max_led_value to account for the 0th index | + \*---------------------------------------------------------*/ + max_led_value++; + } + + /*---------------------------------------------------------*\ + | name is not set yet so description is used instead | + \*---------------------------------------------------------*/ + LOG_DEBUG("[%s] Creating a %s zone: %s with %d LEDs", description.c_str(), + ((new_zone.type == ZONE_TYPE_MATRIX) ? "matrix": "linear"), + new_zone.name.c_str(), new_zone.leds_count); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + zones.push_back(new_zone); + + } + } + + + SetupColors(); +} + +void RGBController_CMKeyboardController::ResizeZone(int /*zone*/, int /*new_size*/) +{ +} + +void RGBController_CMKeyboardController::DeviceUpdateLEDs() +{ + m_pController->SetLeds(leds, colors); +} + +void RGBController_CMKeyboardController::UpdateSingleLED(int led, RGBColor color) +{ + uint8_t key_value = m_pLayoutManager->GetKeyValueAt(led); + m_pController->SetSingleLED(key_value, color); +} + +void RGBController_CMKeyboardController::UpdateSingleLED(int led) +{ + m_pController->SetSingleLED(led, colors[led]); +} + +void RGBController_CMKeyboardController::UpdateZoneLEDs(int /*zone_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMKeyboardController::DeviceUpdateMode() +{ + m_pController->SetMode(modes[active_mode]); +} + +void RGBController_CMKeyboardController::SetCustomMode() +{ + +} diff --git a/Controllers/CoolerMasterController/CMKeyboardController/RGBController_CMKeyboardController.h b/Controllers/CoolerMasterController/CMKeyboardController/RGBController_CMKeyboardController.h new file mode 100644 index 0000000..4ecdfc8 --- /dev/null +++ b/Controllers/CoolerMasterController/CMKeyboardController/RGBController_CMKeyboardController.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_CMKeyboardController.h | +| | +| RGBController for Cooler Master keyboards | +| | +| Tam D (too.manyhobbies) 30 Nov 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CMKeyboardAbstractController.h" +#include "CMKeyboardV1Controller.h" +#include "CMKeyboardV2Controller.h" +#include "CMKeyboardDevices.h" + +class RGBController_CMKeyboardController : public RGBController +{ +public: + RGBController_CMKeyboardController(CMKeyboardAbstractController* pController); + ~RGBController_CMKeyboardController(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateSingleLED(int led, RGBColor color); + void UpdateSingleLED(int led); + void UpdateZoneLEDs(int zone_idx); + + void SetCustomMode(); + void DeviceUpdateMode(); + +private: + CMKeyboardAbstractController* m_pController;; + KeyboardLayoutManager* m_pLayoutManager; + KEYBOARD_LAYOUT m_keyboardLayout; + layout_values m_layoutValues; +}; diff --git a/Controllers/CoolerMasterController/CMMM711Controller/CMMM711Controller.cpp b/Controllers/CoolerMasterController/CMMM711Controller/CMMM711Controller.cpp new file mode 100644 index 0000000..8504c2b --- /dev/null +++ b/Controllers/CoolerMasterController/CMMM711Controller/CMMM711Controller.cpp @@ -0,0 +1,206 @@ +/*---------------------------------------------------------*\ +| CMMM711Controller.cpp | +| | +| Driver for Cooler Master M711 mouse | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CMMM711Controller.h" +#include "StringUtils.h" + +CMMM711Controller::CMMM711Controller(hid_device* dev_handle, char *_path) +{ + dev = dev_handle; + location = _path; + current_speed = CM_MM711_SPEED_NORMAL; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + + SendInitPacket(); + GetColourStatus(); + GetCustomStatus(); + GetModeStatus(); +} + +CMMM711Controller::~CMMM711Controller() +{ + hid_close(dev); +} + +void CMMM711Controller::GetColourStatus() +{ + uint8_t buffer[CM_MM711_PACKET_SIZE] = { 0x00, 0x52, 0x2B }; + + hid_write(dev, buffer, CM_MM711_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM711_PACKET_SIZE, CM_MM711_INTERRUPT_TIMEOUT); + + current_brightness = buffer[CM_MM711_BRIGHTNESS_BYTE - 1]; + current_red = buffer[CM_MM711_RED_BYTE - 1]; + current_green = buffer[CM_MM711_GREEN_BYTE - 1]; + current_blue = buffer[CM_MM711_BLUE_BYTE - 1]; +} + +void CMMM711Controller::GetCustomStatus() +{ + uint8_t buffer[CM_MM711_PACKET_SIZE] = { 0x00, 0x52, 0xA8 }; + int read_size = CM_MM711_PACKET_SIZE - 1; + int result = 0; + + hid_write(dev, buffer, CM_MM711_PACKET_SIZE); + do + { + result = hid_read_timeout(dev, buffer, read_size, CM_MM711_INTERRUPT_TIMEOUT); + }while(buffer[1] != 0xA8 && result == read_size); + + if(result == read_size) + { + wheel_colour = ToRGBColor(buffer[4], buffer[5], buffer[6]); + logo_colour = ToRGBColor(buffer[7], buffer[8], buffer[9]); + } +} + +void CMMM711Controller::GetModeStatus() +{ + uint8_t buffer[CM_MM711_PACKET_SIZE] = { 0x00, 0x52, 0x28 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_MM711_INTERRUPT_TIMEOUT); + + current_mode = buffer[CM_MM711_MODE_BYTE - 1]; +} + +std::string CMMM711Controller::GetDeviceName() +{ + return(device_name); +} + +std::string CMMM711Controller::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string CMMM711Controller::GetLocation() +{ + return("HID: " + location); +} + +unsigned char CMMM711Controller::GetMode() +{ + return(current_mode); +} + +unsigned char CMMM711Controller::GetLedRed() +{ + return(current_red); +} + +unsigned char CMMM711Controller::GetLedGreen() +{ + return(current_green); +} + +unsigned char CMMM711Controller::GetLedBlue() +{ + return(current_blue); +} + +unsigned char CMMM711Controller::GetLedSpeed() +{ + return(current_speed); +} + +RGBColor CMMM711Controller::GetWheelColour() +{ + return(wheel_colour); +} + +RGBColor CMMM711Controller::GetLogoColour() +{ + return(logo_colour); +} + +void CMMM711Controller::SetLedsDirect(RGBColor wheel_colour, RGBColor logo_colour) +{ + unsigned char buffer[CM_MM711_PACKET_SIZE] = { 0x00, 0x51, 0xA8, 0x00, 0x00 }; + + buffer[CM_MM711_MODE_BYTE] = RGBGetRValue(wheel_colour); + buffer[CM_MM711_SPEED_BYTE] = RGBGetGValue(wheel_colour); + buffer[CM_MM711_NFI_1] = RGBGetBValue(wheel_colour); + buffer[CM_MM711_NFI_2] = RGBGetRValue(logo_colour); + buffer[CM_MM711_NFI_3] = RGBGetGValue(logo_colour); + buffer[CM_MM711_BRIGHTNESS_BYTE] = RGBGetBValue(logo_colour); + + hid_write(dev, buffer, CM_MM711_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM711_PACKET_SIZE, CM_MM711_INTERRUPT_TIMEOUT); + + //SendApplyPacket(0xB0); //Apply custom mode +} + +void CMMM711Controller::SendUpdate(uint8_t mode, uint8_t speed, RGBColor colour, uint8_t brightness) +{ + unsigned char buffer[CM_MM711_PACKET_SIZE] = { 0x00, 0x51, 0x2B, 0x00, 0x00 }; + + buffer[CM_MM711_MODE_BYTE] = mode; + buffer[CM_MM711_SPEED_BYTE] = speed; + buffer[CM_MM711_NFI_1] = 0x20; + buffer[CM_MM711_NFI_2] = 0xFF; + buffer[CM_MM711_NFI_3] = 0xFF; + buffer[CM_MM711_BRIGHTNESS_BYTE] = brightness; + buffer[CM_MM711_RED_BYTE] = RGBGetRValue(colour); + buffer[CM_MM711_GREEN_BYTE] = RGBGetGValue(colour); + buffer[CM_MM711_BLUE_BYTE] = RGBGetBValue(colour); + + hid_write(dev, buffer, CM_MM711_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM711_PACKET_SIZE, CM_MM711_INTERRUPT_TIMEOUT); + + SendApplyPacket(mode); +} + +void CMMM711Controller::SendInitPacket() +{ + unsigned char buffer[CM_MM711_PACKET_SIZE] = { 0x00, 0x41, 0x80 }; + + hid_write(dev, buffer, CM_MM711_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM711_PACKET_SIZE, CM_MM711_INTERRUPT_TIMEOUT); +} + +void CMMM711Controller::SendApplyPacket(uint8_t mode) +{ + unsigned char buffer[CM_MM711_PACKET_SIZE] = { 0x00, 0x51, 0x28, 0x00, 0x00 }; + + buffer[CM_MM711_MODE_BYTE] = mode; + + hid_write(dev, buffer, CM_MM711_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM711_PACKET_SIZE, CM_MM711_INTERRUPT_TIMEOUT); +} + +void CMMM711Controller::SendSavePacket() +{ + unsigned char buffer[CM_MM711_PACKET_SIZE] = { 0x00, 0x50, 0x55 }; + + hid_write(dev, buffer, CM_MM711_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM711_PACKET_SIZE, CM_MM711_INTERRUPT_TIMEOUT); +} diff --git a/Controllers/CoolerMasterController/CMMM711Controller/CMMM711Controller.h b/Controllers/CoolerMasterController/CMMM711Controller/CMMM711Controller.h new file mode 100644 index 0000000..a7ed389 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMM711Controller/CMMM711Controller.h @@ -0,0 +1,103 @@ +/*---------------------------------------------------------*\ +| CMMM711Controller.h | +| | +| Driver for Cooler Master M711 mouse | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define CM_MM711_PACKET_SIZE 65 +#define CM_MM711_COLOUR_MODE_DATA_SIZE (sizeof(colour_mode_data[0]) / sizeof(colour_mode_data[0][0])) +#define CM_MM711_HEADER_DATA_SIZE (sizeof(argb_header_data) / sizeof(argb_headers) ) +#define CM_MM711_INTERRUPT_TIMEOUT 250 +#define CM_MM711_DEVICE_NAME_SIZE (sizeof(device_name) / sizeof(device_name[ 0 ])) +#define HID_MAX_STR 255 + +enum +{ + CM_MM711_REPORT_BYTE = 1, + CM_MM711_COMMAND_BYTE = 2, + CM_MM711_FUNCTION_BYTE = 3, + CM_MM711_ZONE_BYTE = 4, + CM_MM711_MODE_BYTE = 5, + CM_MM711_SPEED_BYTE = 6, + CM_MM711_NFI_1 = 7, + CM_MM711_NFI_2 = 8, + CM_MM711_NFI_3 = 9, + CM_MM711_BRIGHTNESS_BYTE = 10, + CM_MM711_RED_BYTE = 11, + CM_MM711_GREEN_BYTE = 12, + CM_MM711_BLUE_BYTE = 13, +}; + +enum +{ + CM_MM711_MODE_STATIC = 0, //Static Mode + CM_MM711_MODE_BREATHING = 1, //Breathing Mode + CM_MM711_MODE_SPECTRUM_CYCLE = 2, //Spectrum Cycle Mode + CM_MM711_MODE_INDICATOR = 4, //Indicator Mode + CM_MM711_MODE_CUSTOM = 176, //Custom LED Control + CM_MM711_MODE_OFF = 254 //Turn Off +}; + +enum +{ + CM_MM711_SPEED_SLOWEST = 0x5F, // Slowest speed + CM_MM711_SPEED_NORMAL = 0x38, // Normal speed + CM_MM711_SPEED_FASTEST = 0x20, // Fastest speed +}; + +class CMMM711Controller +{ +public: + CMMM711Controller(hid_device* dev_handle, char *_path); + ~CMMM711Controller(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + uint8_t GetZoneIndex(); + uint8_t GetMode(); + uint8_t GetLedRed(); + uint8_t GetLedGreen(); + uint8_t GetLedBlue(); + uint8_t GetLedSpeed(); + RGBColor GetWheelColour(); + RGBColor GetLogoColour(); + + void SendUpdate(uint8_t mode, uint8_t speed, RGBColor colour, uint8_t brightness); + void SetLedsDirect(RGBColor wheel_colour, RGBColor logo_colour); + void SendSavePacket(); +private: + std::string device_name; + std::string serial; + std::string location; + hid_device* dev; + + uint8_t current_mode; + uint8_t current_speed; + + uint8_t current_brightness; + uint8_t current_red; + uint8_t current_green; + uint8_t current_blue; + RGBColor wheel_colour; + RGBColor logo_colour; + + void GetColourStatus(); + void GetCustomStatus(); + void GetModeStatus(); + void SendInitPacket(); + void SendApplyPacket(uint8_t mode); +}; diff --git a/Controllers/CoolerMasterController/CMMM711Controller/RGBController_CMMM711Controller.cpp b/Controllers/CoolerMasterController/CMMM711Controller/RGBController_CMMM711Controller.cpp new file mode 100644 index 0000000..efbc759 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMM711Controller/RGBController_CMMM711Controller.cpp @@ -0,0 +1,206 @@ +/*---------------------------------------------------------*\ +| RGBController_CMMM711Controller.cpp | +| | +| RGBController for Cooler Master M711 mouse | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CMMM711Controller.h" + +#define applyBrightness(c, bright) ((RGBColor) ((RGBGetBValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT) << 16 | (RGBGetGValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT) << 8 | (RGBGetRValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT))) + +/**------------------------------------------------------------------*\ + @name Coolermaster Master Mouse + @category Mouse + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCoolerMasterMouse + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CMMM711Controller::RGBController_CMMM711Controller(CMMM711Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Cooler Master"; + type = DEVICE_TYPE_MOUSE; + description = controller->GetDeviceName(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Custom; + Custom.name = "Direct"; + Custom.value = CM_MM711_MODE_CUSTOM; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Custom.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Custom.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Custom.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode Static; + Static.name = "Static"; + Static.value = CM_MM711_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Static.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Static.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.speed_min = CM_MM711_SPEED_SLOWEST; + Static.speed_max = CM_MM711_SPEED_FASTEST; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.speed = CM_MM711_SPEED_NORMAL; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_MM711_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Breathing.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Breathing.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(Breathing.colors_max); + Breathing.speed_min = CM_MM711_SPEED_SLOWEST; + Breathing.speed_max = CM_MM711_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = CM_MM711_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Spectrum_Cycle; + Spectrum_Cycle.name = "Spectrum Cycle"; + Spectrum_Cycle.value = CM_MM711_MODE_SPECTRUM_CYCLE; + Spectrum_Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Spectrum_Cycle.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Spectrum_Cycle.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM; + Spectrum_Cycle.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM; + Spectrum_Cycle.speed_min = CM_MM711_SPEED_SLOWEST; + Spectrum_Cycle.speed_max = CM_MM711_SPEED_FASTEST; + Spectrum_Cycle.color_mode = MODE_COLORS_NONE; + Spectrum_Cycle.speed = CM_MM711_SPEED_NORMAL; + modes.push_back(Spectrum_Cycle); + + mode Indicator; + Indicator.name = "Indicator"; + Indicator.value = CM_MM711_MODE_INDICATOR; + Indicator.flags = MODE_FLAG_MANUAL_SAVE; + Indicator.color_mode = MODE_COLORS_NONE; + modes.push_back(Indicator); + + mode Off; + Off.name = "Turn Off"; + Off.value = CM_MM711_MODE_OFF; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + Init_Controller(); //Only processed on first run + SetupZones(); + + uint8_t temp_mode = controller->GetMode(); + + for(int mode_index = 0; mode_index < (int)modes.size(); mode_index++) + { + if(modes[mode_index].value == temp_mode) + { + active_mode = mode_index; + break; + } + } + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + modes[active_mode].colors[0] = ToRGBColor(controller->GetLedRed(),controller->GetLedGreen(),controller->GetLedBlue()); + } + + colors[0] = controller->GetWheelColour(); + colors[1] = controller->GetLogoColour(); +} + +RGBController_CMMM711Controller::~RGBController_CMMM711Controller() +{ + delete controller; +} + +void RGBController_CMMM711Controller::Init_Controller() +{ + zone mouse_zone; + mouse_zone.name = name; + mouse_zone.type = ZONE_TYPE_LINEAR; + mouse_zone.leds_min = 2; + mouse_zone.leds_max = 2; + mouse_zone.leds_count = 2; + mouse_zone.matrix_map = NULL; + zones.push_back(mouse_zone); + + led wheel_led; + wheel_led.name = "Scroll Wheel LED"; + wheel_led.value = 0; + leds.push_back(wheel_led); + + led logo_led; + logo_led.name = "Logo LED"; + logo_led.value = 1; + leds.push_back(logo_led); +} + +void RGBController_CMMM711Controller::SetupZones() +{ + SetupColors(); +} + +void RGBController_CMMM711Controller::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CMMM711Controller::DeviceUpdateLEDs() +{ + RGBColor wheel = applyBrightness(colors[0], modes[active_mode].brightness); + RGBColor logo = applyBrightness(colors[1], modes[active_mode].brightness); + + controller->SetLedsDirect( wheel, logo); +} + +void RGBController_CMMM711Controller::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMMM711Controller::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMMM711Controller::DeviceUpdateMode() +{ + RGBColor colour = 0; + + if(modes[active_mode].value != CM_MM711_MODE_CUSTOM) + { + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC ) + { + colour = modes[active_mode].colors[0]; + } + + controller->SendUpdate(modes[active_mode].value, modes[active_mode].speed, colour, modes[active_mode].brightness); + } +} + +void RGBController_CMMM711Controller::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->SendSavePacket(); +} diff --git a/Controllers/CoolerMasterController/CMMM711Controller/RGBController_CMMM711Controller.h b/Controllers/CoolerMasterController/CMMM711Controller/RGBController_CMMM711Controller.h new file mode 100644 index 0000000..e084199 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMM711Controller/RGBController_CMMM711Controller.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_CMMM711Controller.h | +| | +| RGBController for Cooler Master M711 mouse | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "CMMM711Controller.h" + +#define CM_MM_ARGB_BRIGHTNESS_MIN 0x00 +#define CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT 0xFF +#define CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM 0x7F + +class RGBController_CMMM711Controller : public RGBController +{ +public: + RGBController_CMMM711Controller(CMMM711Controller* controller_ptr); + ~RGBController_CMMM711Controller(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); +private: + void Init_Controller(); + int GetDeviceMode(); + + CMMM711Controller* controller; +}; diff --git a/Controllers/CoolerMasterController/CMMM712Controller/CMMM712Controller.cpp b/Controllers/CoolerMasterController/CMMM712Controller/CMMM712Controller.cpp new file mode 100644 index 0000000..1c7603c --- /dev/null +++ b/Controllers/CoolerMasterController/CMMM712Controller/CMMM712Controller.cpp @@ -0,0 +1,192 @@ +/*---------------------------------------------------------*\ +| CMMM712Controller.cpp | +| | +| Driver for Cooler Master MM712 mouse | +| Derived from CMMM711Controller.cpp | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| Frans Meulenbroeks 08 Dec 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CMMM712Controller.h" +#include "StringUtils.h" + +#define CM_MM712_PACKET_SIZE 65 +#define CM_MM712_INTERRUPT_TIMEOUT 250 +#define HID_MAX_STR 255 + +enum +{ + CM_MM712_MODE_BYTE = 4, + CM_MM712_BRIGHTNESS_BYTE = 6, + CM_MM712_SPEED_BYTE = 7, + CM_MM712_RED_BYTE = 8, + CM_MM712_GREEN_BYTE = 9, + CM_MM712_BLUE_BYTE = 10, +}; + +CMMM712Controller::CMMM712Controller(hid_device* dev_handle, char *_path) +{ + dev = dev_handle; + location = _path; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + + SendInitPacket(); + GetModeStatus(); + GetColorStatus(current_mode); +} + +CMMM712Controller::~CMMM712Controller() +{ + hid_close(dev); +} + +void CMMM712Controller::SendBuffer(uint8_t *buffer, uint8_t buffer_size) +{ + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_MM712_INTERRUPT_TIMEOUT); +} + +void CMMM712Controller::GetColorStatus(uint8_t mode) +{ + uint8_t buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x4C, 0x81, 0x03, mode }; + + SendBuffer(buffer, CM_MM712_PACKET_SIZE); + initial_color = ToRGBColor(buffer[CM_MM712_RED_BYTE - 2], buffer[CM_MM712_GREEN_BYTE - 2], buffer[CM_MM712_BLUE_BYTE - 2]); +} + +void CMMM712Controller::GetModeStatus() +{ + uint8_t buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x4C, 0x81, 0x07 }; + + SendBuffer(buffer, CM_MM712_PACKET_SIZE); + current_mode = buffer[CM_MM712_MODE_BYTE - 1]; + SetMode(current_mode); +} + +std::string CMMM712Controller::GetDeviceName() +{ + return(device_name); +} + +std::string CMMM712Controller::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string CMMM712Controller::GetLocation() +{ + return("HID: " + location); +} + +unsigned char CMMM712Controller::GetMode() +{ + return(current_mode); +} + +RGBColor CMMM712Controller::GetInitialLedColor() +{ + return initial_color; +} + +void CMMM712Controller::SetLedsDirect(RGBColor color) +{ + unsigned char buffer[CM_MM712_PACKET_SIZE] = + { + 0x00, 0x5A, 0x81, 0x03, + (unsigned char)RGBGetRValue(color), + (unsigned char)RGBGetGValue(color), + (unsigned char)RGBGetBValue(color) + }; + + if(current_mode!=CM_MM712_MODE_DIRECT) + { + SetDirectMode(true); + } + hid_write(dev, buffer, CM_MM712_PACKET_SIZE); +// SendBuffer(buffer, CM_MM712_PACKET_SIZE); +} + +void CMMM712Controller::SendUpdate(uint8_t mode, uint8_t speed, RGBColor color, uint8_t brightness) +{ + unsigned char buffer[CM_MM712_PACKET_SIZE] = + { + 0x00, 0x4C, 0x81, 0x04, mode, 0xFF, brightness, speed, + (unsigned char)RGBGetRValue(color), + (unsigned char)RGBGetGValue(color), + (unsigned char)RGBGetBValue(color), + 0xFF + }; + + if(current_mode==CM_MM712_MODE_DIRECT) + { + SetDirectMode(false); + SendInitPacket(); + } + + SendBuffer(buffer, CM_MM712_PACKET_SIZE); + SetMode(mode); +} + +void CMMM712Controller::SendInitPacket() +{ + unsigned char buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x44, 0x81, 0x02 }; + + SendBuffer(buffer, CM_MM712_PACKET_SIZE); +} + +void CMMM712Controller::SetDirectMode(bool onoff) +{ + unsigned char buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x5a, 0x81, (unsigned char)(0x01+onoff) }; + + hid_write(dev, buffer, CM_MM712_PACKET_SIZE); +} + +void CMMM712Controller::SetMode(uint8_t mode) +{ + unsigned char buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x4C, 0x81, 0x08, mode}; + + if(current_mode==CM_MM712_MODE_DIRECT) + { + SendInitPacket(); + } + + SendBuffer(buffer, CM_MM712_PACKET_SIZE); + current_mode = mode; +} + +void CMMM712Controller::SetProfile(uint8_t profile) +{ + unsigned char buffer[CM_MM712_PACKET_SIZE] = { 0x00, 0x44, 0x81, 0x01, profile}; + + SendBuffer(buffer, CM_MM712_PACKET_SIZE); +} + +void CMMM712Controller::SaveStatus() +{ + unsigned char buffer[CM_MM712_PACKET_SIZE] = { 0x54, 0x81, 1}; + + SendBuffer(buffer, CM_MM712_PACKET_SIZE); +} diff --git a/Controllers/CoolerMasterController/CMMM712Controller/CMMM712Controller.h b/Controllers/CoolerMasterController/CMMM712Controller/CMMM712Controller.h new file mode 100644 index 0000000..bd506fe --- /dev/null +++ b/Controllers/CoolerMasterController/CMMM712Controller/CMMM712Controller.h @@ -0,0 +1,69 @@ +/*---------------------------------------------------------*\ +| CMMM712Controller.h | +| | +| Driver for Cooler Master MM712 mouse | +| Derived from CMMM711Controller.h | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| Frans Meulenbroeks 08 Dec 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + CM_MM712_MODE_STATIC = 0, //Static Mode + CM_MM712_MODE_BREATHING = 1, //Breathing Mode + CM_MM712_MODE_SPECTRUM_CYCLE = 2, //Spectrum Cycle Mode + CM_MM712_MODE_OFF = 3, //Turn Off + CM_MM712_MODE_DIRECT = 4, //Direct LED Control +}; + +enum +{ + CM_MM712_SPEED_SLOWEST = 0x0, //Slowest speed + CM_MM712_SPEED_NORMAL = 0x2, //Normal speed + CM_MM712_SPEED_FASTEST = 0x4, //Fastest speed +}; + +class CMMM712Controller +{ +public: + CMMM712Controller(hid_device* dev_handle, char *_path); + ~CMMM712Controller(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + uint8_t GetMode(); + RGBColor GetInitialLedColor(); + + void SendUpdate(uint8_t mode, uint8_t speed, RGBColor color, uint8_t brightness); + void SetMode(uint8_t mode); + void SetDirectMode(bool onoff); + void SetLedsDirect(RGBColor color); + void SaveStatus(); +private: + std::string device_name; + std::string serial; + std::string location; + hid_device* dev; + + uint8_t current_mode; + RGBColor initial_color; + + void GetColorStatus(uint8_t mode); + void GetModeStatus(); + void SendInitPacket(); + void SetProfile(uint8_t profile); + void SendBuffer(uint8_t *buffer, uint8_t buffer_size); + +}; diff --git a/Controllers/CoolerMasterController/CMMM712Controller/MM712protocol.txt b/Controllers/CoolerMasterController/CMMM712Controller/MM712protocol.txt new file mode 100644 index 0000000..6a6ab4e --- /dev/null +++ b/Controllers/CoolerMasterController/CMMM712Controller/MM712protocol.txt @@ -0,0 +1,108 @@ +Analysis of the MM712 protocol +By Frans Meulenbroeks +PID 0x2516, VID 0x0169 +We must use interface 3 +C = Command, R = Response + +Init: +===== +C: 0x00 0x44 0x81 0x02 +R: 0x45 0x81 0x02 0x02 0x01 +First byte is second byte of command+1, 2nd and 3rd byte are the 3rd and 4th byte of the command +No idea what the last two bytes are. +This init command inits to normal state. +After that one can submit all normal commands. These give a response. + +C: 0x00 0x5a 0x81 0x02 +R: 0x5b 0x81 0x02 +This init command inits to direct state. +After that one can submit all direct state commands. These give no response + +Note that you can always change between the two states by giving the appropriate init command. + +I have also seen +C: 0x00, 0x46, 0x81 +0x46 is command code +R: 47 81 50 03 00 00 f2 9b 1e 00 64 02 00 00 00 ff 03 06 00 ... +No idea what the response data is +This also brought the device to type-4 state. + +NORMAL COMMANDS +=============== + +Query stored colors: +==================== +C: 0x00 0x4c 0x81 0x03 0 +R: 0x4d 0x81 0x03 0x06 0xff 0x00 0xff 0xff 0x00 + Brig Spee Red Gree Blue Speed is not really relevant +These are the settings for the static mode + +C: 0x00 0x4c 0x81 0x03 1 +R: 0x4d 0x81 0x03 0xff 0xff 0x04 0xff 0x00 0x00 0xff + Brig Spee Red Gree Blue +These are the settings for the breathing mode + +C: 0x00 0x4c 0x81 0x03 2 +R: 0x81 0x03 0x06 0x7f 0x02 + Brig Spee +These are the settings for the cycling mode + +C: 0x00 0x4c 0x81 0x03 3 +R: 0x4d 0x81 0x03 0x06 0x00 +This is the response for the off mode +Other/higher numbers also return this value + +Detecting the mode: +=================== +C: 0x00 0x4c 0x81 0x07 +R: 0x4d 0x81 0x07 0x01 + ^ actual mode + +Setting the mode: +================= +C: 0x00 0x4c 0x81 0x08 0x01 + ^ new mode 0=static,1=breathing,2=cycling,3 or higher=off +R: 0x4d 0x81 0x07 0x01 + can't explain the 0x07; later calls returned 0x08 in this field + +C: 0x00 0x4c 0x81 0x08 0x02 +R: 0x4d 0x81 0x08 0x02 + +Setting the color: +================== +C: 0x00 0x4c 0x81 0x04 0x00 0xff 0xff 0x02 0x00 0xff 0x00 + cmd mode ???? brig spee red gree blue Speed is only relevant for breathing and cyclic, color is not relevant for cyclic + This sets the color, speed and brightness for the specific mode. + Note that this does not imply a mode switch + The first 0xff byte does not seem to do anything. I've changed to a different number but saw no result +R: 0x47 0x81 0x50 0x03 0x00 0x00 0xf2 0x9b 0x1e 0x00 0x64 0x02 0x00 0x00 0x00 0xff 0x01 0xff 0x00 0x00 +No idea what this means, Reply seems independent of color set + +Saving values: +============== +C: 0x00 0x54 0x81 0x01 +This saves the actual color for all modes and the mode itself to internal flash +R: 0x55 0x81 0x01 0x00 + +Change Profile +============== +C: 0x00 0x44 0x81 0x01 0x02 + ^ new profile must be in [0..4] otherwise this is a no-op +R: 0x45 0x81 0x01 0x02 0x01 + ^ it is unclear what this value is, values 0, 1 and 2 are observed + + +DIRECT COMMANDS +============== +C: 0x00 0x5a 0x81 0x01 +Leave direct state (return to normal, note that a new init for normal is needed) + +C: 0x00 0x5a 0x81 0x03 0xff 0x00 0x25 + red gree blue This changes the color right away. No response is generated. + CoolerMaster MasterPlus software uses this to dynamically generate animations + +Final notes: +It is possible to change the mode on the mouse (see mouse doc). +This also changes the value in flash +It is also possible to change the colors in mode 0 and 1 using the mouse. +8 different colors can be selected. I did not find a way to define these colors from software so I suspect these are hardcoded diff --git a/Controllers/CoolerMasterController/CMMM712Controller/RGBController_CMMM712Controller.cpp b/Controllers/CoolerMasterController/CMMM712Controller/RGBController_CMMM712Controller.cpp new file mode 100644 index 0000000..540b872 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMM712Controller/RGBController_CMMM712Controller.cpp @@ -0,0 +1,199 @@ +/*---------------------------------------------------------*\ +| RGBController_CMMM712Controller.cpp | +| | +| RGBController for Cooler Master MM712 mouse | +| Derived from RGBController_CMMM712Controller.cpp | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| Frans Meulenbroeks 08 Dec 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CMMM712Controller.h" + +#define applyBrightness(c, bright) ((RGBColor) ((RGBGetBValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT) << 16 | (RGBGetGValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT) << 8 | (RGBGetRValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT))) + +/**------------------------------------------------------------------*\ + @name Coolermaster Master Mouse + @category Mouse + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCoolerMasterMouse + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CMMM712Controller::RGBController_CMMM712Controller(CMMM712Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Cooler Master"; + type = DEVICE_TYPE_MOUSE; + description = controller->GetDeviceName(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CM_MM712_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Direct.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Direct.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = CM_MM712_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Static.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Static.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.speed_min = CM_MM712_SPEED_SLOWEST; + Static.speed_max = CM_MM712_SPEED_FASTEST; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.speed = CM_MM712_SPEED_NORMAL; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_MM712_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Breathing.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Breathing.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(Breathing.colors_max); + Breathing.speed_min = CM_MM712_SPEED_SLOWEST; + Breathing.speed_max = CM_MM712_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = CM_MM712_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Spectrum_Cycle; + Spectrum_Cycle.name = "Spectrum Cycle"; + Spectrum_Cycle.value = CM_MM712_MODE_SPECTRUM_CYCLE; + Spectrum_Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Spectrum_Cycle.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Spectrum_Cycle.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM; + Spectrum_Cycle.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM; + Spectrum_Cycle.speed_min = CM_MM712_SPEED_SLOWEST; + Spectrum_Cycle.speed_max = CM_MM712_SPEED_FASTEST; + Spectrum_Cycle.color_mode = MODE_COLORS_NONE; + Spectrum_Cycle.speed = CM_MM712_SPEED_NORMAL; + modes.push_back(Spectrum_Cycle); + + mode Off; + Off.name = "Off"; + Off.value = CM_MM712_MODE_OFF; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + Init_Controller(); //Only processed on first run + SetupZones(); + + uint8_t temp_mode = controller->GetMode(); + + for(int mode_index = 0; mode_index < (int)modes.size(); mode_index++) + { + if(modes[mode_index].value == temp_mode) + { + active_mode = mode_index; + break; + } + } + + colors[0] = controller->GetInitialLedColor(); + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + modes[active_mode].colors[0] = colors[0]; + } +} + +RGBController_CMMM712Controller::~RGBController_CMMM712Controller() +{ + delete controller; +} + +void RGBController_CMMM712Controller::Init_Controller() +{ + zone mouse_zone; + mouse_zone.name = name; + mouse_zone.type = ZONE_TYPE_SINGLE; + mouse_zone.leds_min = 1; + mouse_zone.leds_max = 1; + mouse_zone.leds_count = 1; + mouse_zone.matrix_map = NULL; + zones.push_back(mouse_zone); + + led logo_led; + logo_led.name = "Logo LED"; + logo_led.value = 0; + leds.push_back(logo_led); +} + +void RGBController_CMMM712Controller::SetupZones() +{ + SetupColors(); +} + +void RGBController_CMMM712Controller::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CMMM712Controller::DeviceUpdateLEDs() +{ + modes[active_mode].brightness=255; + RGBColor logo = applyBrightness(colors[0], modes[active_mode].brightness); + + controller->SetLedsDirect(logo); +} + +void RGBController_CMMM712Controller::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMMM712Controller::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMMM712Controller::DeviceUpdateMode() +{ + if(modes[active_mode].value==CM_MM712_MODE_DIRECT) + { + controller->SetDirectMode(true); + } + else + { + controller->SetDirectMode(false); + RGBColor colour = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC ) + { + colour = modes[active_mode].colors[0]; + } + + controller->SendUpdate(modes[active_mode].value, modes[active_mode].speed, colour, modes[active_mode].brightness); + } +} + +void RGBController_CMMM712Controller::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->SaveStatus(); +} diff --git a/Controllers/CoolerMasterController/CMMM712Controller/RGBController_CMMM712Controller.h b/Controllers/CoolerMasterController/CMMM712Controller/RGBController_CMMM712Controller.h new file mode 100644 index 0000000..23705c0 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMM712Controller/RGBController_CMMM712Controller.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_CMMM712Controller.h | +| | +| RGBController for Cooler Master M712 mouse | +| Derived from RGBController_CMMM712Controller.h | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| Frans Meulenbroeks 08 Dec 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CMMM712Controller.h" + +#define CM_MM_ARGB_BRIGHTNESS_MIN 0x00 +#define CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT 0xFF +#define CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM 0x7F + +class RGBController_CMMM712Controller : public RGBController +{ +public: + RGBController_CMMM712Controller(CMMM712Controller* controller_ptr); + ~RGBController_CMMM712Controller(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); +private: + void Init_Controller(); + + CMMM712Controller* controller; +}; diff --git a/Controllers/CoolerMasterController/CMMMController/CMMMController.cpp b/Controllers/CoolerMasterController/CMMMController/CMMMController.cpp new file mode 100644 index 0000000..7a25922 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMMController/CMMMController.cpp @@ -0,0 +1,346 @@ +/*---------------------------------------------------------*\ +| CMMMController.cpp | +| | +| Driver for Cooler Master mouse | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| Dracrius 12 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CMMMController.h" +#include "StringUtils.h" + +CMMMController::CMMMController(hid_device* dev_handle, char *_path, uint16_t pid, std::string dev_name) +{ + dev = dev_handle; + location = _path; + name = dev_name; + current_speed = CM_MM_SPEED_3; + product_id = pid; + + if(product_id == CM_MM530_PID || product_id == CM_MM531_PID) + { + command_code = CM_MM5XX_COMMAND; + if(pid == CM_MM530_PID) + { + buttons_bytes[0] = CM_MM_MODE_BYTE; + buttons_bytes[1] = CM_MM_SPEED_BYTE; + buttons_bytes[2] = CM_MM_NFI_1; + wheel_bytes[0] = CM_MM_RED_BYTE; + wheel_bytes[1] = CM_MM_GREEN_BYTE; + wheel_bytes[2] = CM_MM_BLUE_BYTE; + } + else if(product_id == CM_MM531_PID) //Still Need Captures for Proper Mapping From a MM531 User + { + buttons_bytes[0] = CM_MM_MODE_BYTE; + buttons_bytes[1] = CM_MM_SPEED_BYTE; + buttons_bytes[2] = CM_MM_NFI_1; + wheel_bytes[0] = CM_MM_RED_BYTE; + wheel_bytes[1] = CM_MM_GREEN_BYTE; + wheel_bytes[2] = CM_MM_BLUE_BYTE; + } + } + else + { + command_code = CM_MM7XX_COMMAND; + + buttons_bytes[0] = CM_MM_RED_BYTE; + buttons_bytes[1] = CM_MM_GREEN_BYTE; + buttons_bytes[2] = CM_MM_BLUE_BYTE; + wheel_bytes[0] = CM_MM_MODE_BYTE; + wheel_bytes[1] = CM_MM_SPEED_BYTE; + wheel_bytes[2] = CM_MM_NFI_1; + } + + logo_bytes[0] = CM_MM_NFI_2; + logo_bytes[1] = CM_MM_NFI_3; + logo_bytes[2] = CM_MM_BRIGHTNESS_BYTE; + + SendInitPacket(); + GetColourStatus(); + GetCustomStatus(); + GetModeStatus(); +} + +CMMMController::~CMMMController() +{ + hid_close(dev); +} + +void CMMMController::GetColourStatus() +{ + uint8_t buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x52, command_code }; + + hid_write(dev, buffer, CM_MM_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM_PACKET_SIZE, CM_MM_INTERRUPT_TIMEOUT); + + current_brightness = buffer[CM_MM_BRIGHTNESS_BYTE - 1]; + current_red = buffer[CM_MM_RED_BYTE - 1]; + current_green = buffer[CM_MM_GREEN_BYTE - 1]; + current_blue = buffer[CM_MM_BLUE_BYTE - 1]; +} + +void CMMMController::GetCustomStatus() +{ + uint8_t buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x52, 0xA8 }; + int read_size = CM_MM_PACKET_SIZE - 1; + int result = 0; + + hid_write(dev, buffer, CM_MM_PACKET_SIZE); + do + { + result = hid_read_timeout(dev, buffer, read_size, CM_MM_INTERRUPT_TIMEOUT); + }while(buffer[1] != 0xA8 && result == read_size); + + if(result == read_size) + { + buttons_colour = ToRGBColor(buffer[4], buffer[5], buffer[6]); + logo_colour = ToRGBColor(buffer[7], buffer[8], buffer[9]); + wheel_colour = ToRGBColor(buffer[10], buffer[11], buffer[12]); + } +} + +void CMMMController::GetModeStatus() +{ + uint8_t buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x52, 0x28 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_MM_INTERRUPT_TIMEOUT); + + current_mode = buffer[CM_MM_MODE_BYTE - 1]; +} + +std::string CMMMController::GetDeviceVendor() +{ + wchar_t vendor_string[HID_MAX_STR]; + int ret = hid_get_manufacturer_string(dev, vendor_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(vendor_string)); +} + +std::string CMMMController::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_indexed_string(dev, 2, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string CMMMController::GetLocation() +{ + return("HID: " + location); +} + +std::string CMMMController::GetName() +{ + return(name); +} + +uint16_t CMMMController::GetProductID() +{ + return product_id; +} + +unsigned char CMMMController::GetMode() +{ + return current_mode; +} + +unsigned char CMMMController::GetLedRed() +{ + return current_red; +} + +unsigned char CMMMController::GetLedGreen() +{ + return current_green; +} + +unsigned char CMMMController::GetLedBlue() +{ + return current_blue; +} + +unsigned char CMMMController::GetLedSpeed() +{ + return current_speed; +} + +RGBColor CMMMController::GetWheelColour() +{ + return wheel_colour; +} + +RGBColor CMMMController::GetButtonsColour() +{ + return buttons_colour; +} + +RGBColor CMMMController::GetLogoColour() +{ + return logo_colour; +} + +void CMMMController::SetLedsDirect(RGBColor wheel_colour, RGBColor buttons_colour, RGBColor logo_colour) +{ + unsigned char buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x51, 0xA8, 0x00, 0x00 }; + + buffer[buttons_bytes[0]] = RGBGetRValue(buttons_colour); + buffer[buttons_bytes[1]] = RGBGetGValue(buttons_colour); + buffer[buttons_bytes[2]] = RGBGetBValue(buttons_colour); + buffer[logo_bytes[0]] = RGBGetRValue(logo_colour); + buffer[logo_bytes[1]] = RGBGetGValue(logo_colour); + buffer[logo_bytes[2]] = RGBGetBValue(logo_colour); + buffer[wheel_bytes[0]] = RGBGetRValue(wheel_colour); + buffer[wheel_bytes[1]] = RGBGetGValue(wheel_colour); + buffer[wheel_bytes[2]] = RGBGetBValue(wheel_colour); + + hid_write(dev, buffer, CM_MM_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM_PACKET_SIZE, CM_MM_INTERRUPT_TIMEOUT); +} + +void CMMMController::SendUpdate(uint8_t mode, uint8_t speed, RGBColor colour, uint8_t brightness) +{ + if (mode == CM_MM_MODE_CUSTOM || mode == CM_MM_MODE_MULTILAYER) + { + SendUsingZonesPacket(mode); + } + else + { + SendInitPacket(); + SendApplyPacket(mode); + } + + uint8_t nfi_1 = 0x20; + + if (mode == CM_MM_MODE_STATIC || mode == CM_MM_MODE_SPECTRUM_CYCLE) + { + nfi_1 = 0x00; + } + + if (mode != CM_MM_MODE_OFF) + { + unsigned char buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x51, command_code, 0x00, 0x00 }; + + buffer[CM_MM_MODE_BYTE] = mode; + buffer[CM_MM_SPEED_BYTE] = speed; + buffer[CM_MM_NFI_1] = nfi_1; + buffer[CM_MM_NFI_2] = 0xFF; + buffer[CM_MM_NFI_3] = 0xFF; + buffer[CM_MM_BRIGHTNESS_BYTE] = brightness; + buffer[CM_MM_RED_BYTE] = RGBGetRValue(colour); + buffer[CM_MM_GREEN_BYTE] = RGBGetGValue(colour); + buffer[CM_MM_BLUE_BYTE] = RGBGetBValue(colour); + buffer[CM_MM_SKY_RED_BYTE] = 0x00; + buffer[CM_MM_SKY_GREEN_BYTE] = 0x00; + buffer[CM_MM_SKY_BLUE_BYTE] = 0x00; + + for (int i = 17; i < CM_MM_PACKET_SIZE; i++) + { + buffer[i] = 0xFF; + } + + hid_write(dev, buffer, CM_MM_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM_PACKET_SIZE, CM_MM_INTERRUPT_TIMEOUT); + + } + + if (mode == CM_MM_MODE_CUSTOM || mode == CM_MM_MODE_MULTILAYER) + { + SendApplyPacket(mode); //Post Apply for Zoned Modes + } +} + +void CMMMController::SendUpdate(uint8_t mode, uint8_t speed, RGBColor mode_one, RGBColor mode_two, uint8_t brightness) +{ + SendApplyPacket(mode); + + unsigned char buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x51, command_code, 0x00, 0x00 }; + + buffer[CM_MM_MODE_BYTE] = mode; + buffer[CM_MM_SPEED_BYTE] = speed; + buffer[CM_MM_NFI_1] = 0x00; + buffer[CM_MM_NFI_2] = 0x21; + buffer[CM_MM_NFI_3] = 0xFF; + buffer[CM_MM_BRIGHTNESS_BYTE] = brightness; + buffer[CM_MM_RED_BYTE] = RGBGetRValue(mode_one); + buffer[CM_MM_GREEN_BYTE] = RGBGetGValue(mode_one); + buffer[CM_MM_BLUE_BYTE] = RGBGetBValue(mode_one); + buffer[CM_MM_SKY_RED_BYTE] = RGBGetRValue(mode_two); + buffer[CM_MM_SKY_GREEN_BYTE] = RGBGetGValue(mode_two); + buffer[CM_MM_SKY_BLUE_BYTE] = RGBGetBValue(mode_two); + + for (int i = 17; i < CM_MM_PACKET_SIZE; i++) + { + buffer[i] = 0xFF; + } + + hid_write(dev, buffer, CM_MM_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM_PACKET_SIZE, CM_MM_INTERRUPT_TIMEOUT); +} + +void CMMMController::SendInitPacket() +{ + unsigned char buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x41, 0x80 }; + + hid_write(dev, buffer, CM_MM_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM_PACKET_SIZE, CM_MM_INTERRUPT_TIMEOUT); +} + +void CMMMController::SendUsingZonesPacket(uint8_t mode) +{ + unsigned char buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x51, 0x30, 0x00, 0x00 }; + + if (mode == CM_MM_MODE_MULTILAYER) + { + buffer[CM_MM_MODE_BYTE] = 0x01; + } + + hid_write(dev, buffer, CM_MM_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM_PACKET_SIZE, CM_MM_INTERRUPT_TIMEOUT); +} + +void CMMMController::SendApplyPacket(uint8_t mode) +{ + unsigned char buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x51, 0x28, 0x00, 0x00 }; + + buffer[CM_MM_MODE_BYTE] = mode; + + hid_write(dev, buffer, CM_MM_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM_PACKET_SIZE, CM_MM_INTERRUPT_TIMEOUT); +} + +void CMMMController::SendMultilayerPacket(uint8_t zones[3]) +{ + unsigned char buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x51, 0xA0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00 }; + + buffer[CM_MM_NFI_3] = zones[0]; + buffer[CM_MM_BRIGHTNESS_BYTE] = zones[1]; + buffer[CM_MM_RED_BYTE] = zones[2]; + + hid_write(dev, buffer, CM_MM_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM_PACKET_SIZE, CM_MM_INTERRUPT_TIMEOUT); +} + +void CMMMController::SendSavePacket() +{ + unsigned char buffer[CM_MM_PACKET_SIZE] = { 0x00, 0x50, 0x55 }; + + hid_write(dev, buffer, CM_MM_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_MM_PACKET_SIZE, CM_MM_INTERRUPT_TIMEOUT); +} diff --git a/Controllers/CoolerMasterController/CMMMController/CMMMController.h b/Controllers/CoolerMasterController/CMMMController/CMMMController.h new file mode 100644 index 0000000..98481c5 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMMController/CMMMController.h @@ -0,0 +1,149 @@ +/*---------------------------------------------------------*\ +| CMMMController.h | +| | +| Driver for Cooler Master mouse | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| Dracrius 12 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define CM_MM_PACKET_SIZE 65 +#define CM_MM_COLOUR_MODE_DATA_SIZE (sizeof(colour_mode_data[0]) / sizeof(colour_mode_data[0][0])) +#define CM_MM_HEADER_DATA_SIZE (sizeof(argb_header_data) / sizeof(argb_headers) ) +#define CM_MM_INTERRUPT_TIMEOUT 250 +#define CM_MM_DEVICE_NAME_SIZE (sizeof(device_name) / sizeof(device_name[ 0 ])) +#define HID_MAX_STR 255 + +enum +{ + CM_MM530_PID = 0x0065, + CM_MM531_PID = 0x0097, + CM_MM711_PID = 0x0101, + CM_MM720_PID = 0x0141, + CM_MM730_PID = 0x0165, +}; + +enum +{ + CM_MM_REPORT_BYTE = 1, + CM_MM_COMMAND_BYTE = 2, + CM_MM_FUNCTION_BYTE = 3, + CM_MM_ZONE_BYTE = 4, + CM_MM_MODE_BYTE = 5, + CM_MM_SPEED_BYTE = 6, + CM_MM_NFI_1 = 7, + CM_MM_NFI_2 = 8, + CM_MM_NFI_3 = 9, + CM_MM_BRIGHTNESS_BYTE = 10, + CM_MM_RED_BYTE = 11, + CM_MM_GREEN_BYTE = 12, + CM_MM_BLUE_BYTE = 13, + CM_MM_SKY_RED_BYTE = 14, + CM_MM_SKY_GREEN_BYTE = 15, + CM_MM_SKY_BLUE_BYTE = 16 +}; + +enum +{ + CM_MM5XX_COMMAND = 0x2C, + CM_MM7XX_COMMAND = 0x2B +}; + +enum +{ + CM_MM_CUSTOM_APPLY = 0x30, //Also Used for Multilayer Mode + CM_MM_APPLY = 0x28 //Sent Before Update, Unless using a Zoned Mode then UsingZones Before and Apply After +}; + +enum +{ + CM_MM_MODE_STATIC = 0, //Static Mode + CM_MM_MODE_BREATHING = 1, //Breathing Mode + CM_MM_MODE_SPECTRUM_CYCLE = 2, //Spectrum Cycle Mode + CM_MM_MODE_STARS = 3, //Stars Mode + CM_MM_MODE_INDICATOR = 4, //Indicator Mode + CM_MM_MODE_CUSTOM = 176, //Custom LED Control + CM_MM_MODE_MULTILAYER = 224, //Multilayer Mode, i.e. Effect per Zone. + CM_MM_MODE_OFF = 254 //Turn Off +}; + +enum +{ + CM_MM_SPEED_1 = 0x3C, // Slowest speed + CM_MM_SPEED_2 = 0x37, + CM_MM_SPEED_3 = 0x31, // Normal speed + CM_MM_SPEED_4 = 0x2C, + CM_MM_SPEED_5 = 0x26 // Fastest speed +}; + +class CMMMController +{ +public: + CMMMController(hid_device* dev_handle, char *_path, uint16_t pid, std::string dev_name); + ~CMMMController(); + + std::string GetDeviceVendor(); + std::string GetSerial(); + std::string GetLocation(); + std::string GetName(); + + uint16_t GetProductID(); + + uint8_t GetZoneIndex(); + uint8_t GetMode(); + uint8_t GetLedRed(); + uint8_t GetLedGreen(); + uint8_t GetLedBlue(); + uint8_t GetLedSpeed(); + RGBColor GetWheelColour(); + RGBColor GetButtonsColour(); + RGBColor GetLogoColour(); + + void SendUpdate(uint8_t mode, uint8_t speed, RGBColor colour, uint8_t brightness); + void SendUpdate(uint8_t mode, uint8_t speed, RGBColor mode_one, RGBColor mode_two, uint8_t brightness); + void SetLedsDirect(RGBColor wheel_colour, RGBColor buttons_colour, RGBColor logo_colour); + void SendSavePacket(); +private: + std::string name; + std::string location; + hid_device* dev; + + uint16_t product_id; + + uint8_t command_code; + + uint8_t current_mode; + uint8_t current_speed; + + uint8_t current_brightness; + uint8_t current_red; + uint8_t current_green; + uint8_t current_blue; + + uint8_t buttons_bytes[3]; + uint8_t logo_bytes[3]; + uint8_t wheel_bytes[3]; + + RGBColor buttons_colour; + RGBColor logo_colour; + RGBColor wheel_colour; + + + void GetColourStatus(); + void GetCustomStatus(); + void GetModeStatus(); + void SendInitPacket(); + void SendUsingZonesPacket(uint8_t mode); + void SendApplyPacket(uint8_t mode); + void SendMultilayerPacket(uint8_t zones[3]); +}; diff --git a/Controllers/CoolerMasterController/CMMMController/RGBController_CMMMController.cpp b/Controllers/CoolerMasterController/CMMMController/RGBController_CMMMController.cpp new file mode 100644 index 0000000..e740a17 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMMController/RGBController_CMMMController.cpp @@ -0,0 +1,285 @@ +/*---------------------------------------------------------*\ +| RGBController_CMMMController.cpp | +| | +| RGBController for Cooler Master mouse | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| Dracrius 12 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CMMMController.h" + +#define applyBrightness(c, bright) ((RGBColor) ((RGBGetBValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT) << 16 | (RGBGetGValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT) << 8 | (RGBGetRValue(c) * bright / CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT))) + +/**------------------------------------------------------------------*\ + @name Coolermaster Master Mouse + @category Mouse + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCoolerMasterMouse + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CMMMController::RGBController_CMMMController(CMMMController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = controller->GetDeviceVendor(); + type = DEVICE_TYPE_MOUSE; + description = "Cooler Master MasterMouse Device"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Custom; + Custom.name = "Direct"; + Custom.value = CM_MM_MODE_CUSTOM; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Custom.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Custom.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Custom.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode Static; + Static.name = "Static"; + Static.value = CM_MM_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Static.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Static.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.speed_min = CM_MM_SPEED_1; + Static.speed_max = CM_MM_SPEED_5; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.speed = CM_MM_SPEED_3; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_MM_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Breathing.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Breathing.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(Breathing.colors_max); + Breathing.speed_min = CM_MM_SPEED_1; + Breathing.speed_max = CM_MM_SPEED_5; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = CM_MM_SPEED_3; + modes.push_back(Breathing); + + mode Spectrum_Cycle; + Spectrum_Cycle.name = "Spectrum Cycle"; + Spectrum_Cycle.value = CM_MM_MODE_SPECTRUM_CYCLE; + Spectrum_Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Spectrum_Cycle.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Spectrum_Cycle.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM; + Spectrum_Cycle.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM; + Spectrum_Cycle.speed_min = CM_MM_SPEED_1; + Spectrum_Cycle.speed_max = CM_MM_SPEED_5; + Spectrum_Cycle.color_mode = MODE_COLORS_NONE; + Spectrum_Cycle.speed = CM_MM_SPEED_3; + modes.push_back(Spectrum_Cycle); + + mode Stars; + Stars.name = "Stars"; + Stars.value = CM_MM_MODE_STARS; + Stars.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Stars.brightness_min = CM_MM_ARGB_BRIGHTNESS_MIN; + Stars.brightness_max = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Stars.brightness = CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT; + Stars.colors_min = 2; + Stars.colors_max = 2; + Stars.colors.resize(Stars.colors_max); + Stars.speed_min = CM_MM_SPEED_1; + Stars.speed_max = CM_MM_SPEED_5; + Stars.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stars.speed = CM_MM_SPEED_3; + modes.push_back(Stars); + + mode Indicator; + Indicator.name = "Indicator"; + Indicator.value = CM_MM_MODE_INDICATOR; + Indicator.flags = MODE_FLAG_MANUAL_SAVE; + Indicator.color_mode = MODE_COLORS_NONE; + modes.push_back(Indicator); + + mode Off; + Off.name = "Turn Off"; + Off.value = CM_MM_MODE_OFF; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + uint16_t pid = controller->GetProductID(); + + if(pid == 0x0065 || pid == 0x0097) + { + leds_count = 3; + } + else + { + leds_count = 2; + } + + Init_Controller(); //Only processed on first run + SetupZones(); + + uint8_t temp_mode = controller->GetMode(); + + for(int mode_index = 0; mode_index < (int)modes.size(); mode_index++) + { + if(modes[mode_index].value == temp_mode) + { + active_mode = mode_index; + break; + } + + } + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + modes[active_mode].colors[0] = ToRGBColor(controller->GetLedRed(),controller->GetLedGreen(),controller->GetLedBlue()); + } + + + if(pid == 0x0065 || pid == 0x0097) + { + colors[0] = controller->GetWheelColour(); + colors[1] = controller->GetButtonsColour(); + colors[2] = controller->GetLogoColour(); + } + else + { + colors[0] = controller->GetWheelColour(); + colors[1] = controller->GetLogoColour(); + } +} + +RGBController_CMMMController::~RGBController_CMMMController() +{ + delete controller; +} + +void RGBController_CMMMController::Init_Controller() +{ + zone mouse_zone; + mouse_zone.name = name; + mouse_zone.type = ZONE_TYPE_LINEAR; + mouse_zone.leds_min = leds_count; + mouse_zone.leds_max = leds_count; + mouse_zone.leds_count = leds_count; + mouse_zone.matrix_map = NULL; + zones.push_back(mouse_zone); + + int value = 0; + uint16_t pid = controller->GetProductID(); + + led wheel_led; + wheel_led.name = "Scroll Wheel"; + wheel_led.value = value; + leds.push_back(wheel_led); + + value++; + + if(pid == 0x0065 || pid == 0x0097) + { + led buttons_led; + buttons_led.name = "Buttons"; + buttons_led.value = value; + leds.push_back(buttons_led); + value++; + } + + led logo_led; + logo_led.name = "Logo"; + logo_led.value = value; + leds.push_back(logo_led); +} + +void RGBController_CMMMController::SetupZones() +{ + SetupColors(); +} + +void RGBController_CMMMController::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CMMMController::DeviceUpdateLEDs() +{ + int value = 0; + uint16_t pid = controller->GetProductID(); + + RGBColor wheel = applyBrightness(colors[value], modes[active_mode].brightness); + RGBColor buttons = ToRGBColor(0, 0, 0); + value++; + + if(pid == 0x0065 || pid == 0x0097) + { + buttons = applyBrightness(colors[value], modes[active_mode].brightness); + value++; + } + + RGBColor logo = applyBrightness(colors[value], modes[active_mode].brightness); + + controller->SetLedsDirect(wheel, buttons, logo); +} + +void RGBController_CMMMController::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMMMController::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMMMController::DeviceUpdateMode() +{ + RGBColor mode_one = 0; + RGBColor mode_two = 0; + + if(modes[active_mode].value != CM_MM_MODE_CUSTOM) + { + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC ) + { + mode_one = modes[active_mode].colors[0]; + + if(modes[active_mode].colors.size() > 1) + { + mode_two = modes[active_mode].colors[1]; + } + } + + } + + if(modes[active_mode].value == CM_MM_MODE_STARS) + { + controller->SendUpdate(modes[active_mode].value, modes[active_mode].speed, mode_one, mode_two, modes[active_mode].brightness); + } + else + { + controller->SendUpdate(modes[active_mode].value, modes[active_mode].speed, mode_one, modes[active_mode].brightness); + } +} + +void RGBController_CMMMController::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->SendSavePacket(); +} diff --git a/Controllers/CoolerMasterController/CMMMController/RGBController_CMMMController.h b/Controllers/CoolerMasterController/CMMMController/RGBController_CMMMController.h new file mode 100644 index 0000000..55bc618 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMMController/RGBController_CMMMController.h @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| RGBController_CMMMController.h | +| | +| RGBController for Cooler Master mouse | +| | +| Chris M (Dr_No) 14 Feb 2021 | +| Dracrius 12 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "CMMMController.h" + +#define CM_MM_ARGB_BRIGHTNESS_MIN 0x00 +#define CM_MM_ARGB_BRIGHTNESS_MAX_DEFAULT 0xFF +#define CM_MM_ARGB_BRIGHTNESS_MAX_SPECTRUM 0x7F + +class RGBController_CMMMController : public RGBController +{ +public: + RGBController_CMMMController(CMMMController* controller_ptr); + ~RGBController_CMMMController(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + void Init_Controller(); + int GetDeviceMode(); + + int leds_count; + + CMMMController* controller; +}; diff --git a/Controllers/CoolerMasterController/CMMP750Controller/CMMP750Controller.cpp b/Controllers/CoolerMasterController/CMMP750Controller/CMMP750Controller.cpp new file mode 100644 index 0000000..f825b85 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMP750Controller/CMMP750Controller.cpp @@ -0,0 +1,176 @@ +/*---------------------------------------------------------*\ +| CMMP750Controller.cpp | +| | +| Driver for Cooler Master MP750 mousemat | +| | +| Chris M (Dr_No) 16 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CMMP750Controller.h" +#include "StringUtils.h" + +static unsigned char colour_mode_data[][6] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* Off */ + { 0x01, 0x04, 0xFF, 0x00, 0xFF, 0x00 }, /* Static */ + { 0x02, 0x04, 0xFF, 0x00, 0xFF, 0x80 }, /* Blinking */ + { 0x03, 0x04, 0xFF, 0x00, 0xFF, 0x80 }, /* Breathing */ + { 0x04, 0x04, 0x80, 0x00, 0x00, 0x00 }, /* Colour Cycle */ + { 0x05, 0x04, 0x80, 0x00, 0x00, 0x00 } /* Colour Breath */ +}; + +static unsigned char speed_mode_data[9] = +{ + 0xFF, 0xE0, 0xC0, 0xA0, 0x80, 0x60, 0x40, 0x20, 0x00 /* Speed Definition */ +}; + +CMMP750Controller::CMMP750Controller(hid_device* dev_handle, char *_path) +{ + dev = dev_handle; + location = _path; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + + GetStatus(); //When setting up device get current status +} + +CMMP750Controller::~CMMP750Controller() +{ + hid_close(dev); +} + +void CMMP750Controller::GetStatus() +{ + unsigned char buffer[0x41] = { 0x00 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + buffer[1] = 0x07; + + hid_write(dev, buffer, buffer_size); + hid_read(dev, buffer, buffer_size); + + if((buffer[0] == 0x80) && (buffer[1] == 0x05)) + { + current_mode = buffer[2]; + current_red = buffer[3]; + current_green = buffer[4]; + current_blue = buffer[5]; + + for(int i = 0; (speed_mode_data[i] >= buffer[6] && i <= MP750_SPEED_FASTEST); i++) + { + current_speed = i; + } + } + else + { + //Code should never reach here however just in case there is a failure set something + current_mode = CM_MP750_MODE_COLOR_CYCLE; //Unicorn Spew + current_red = 0xFF; + current_green = 0xFF; + current_blue = 0xFF; + current_speed = MP750_SPEED_NORMAL; + } +} + +std::string CMMP750Controller::GetDeviceName() +{ + return(device_name); +} + +std::string CMMP750Controller::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string CMMP750Controller::GetLocation() +{ + return("HID: " + location); +} + +unsigned char CMMP750Controller::GetMode() +{ + return(current_mode); +} + +unsigned char CMMP750Controller::GetLedRed() +{ + return(current_red); +} + +unsigned char CMMP750Controller::GetLedGreen() +{ + return(current_green); +} + +unsigned char CMMP750Controller::GetLedBlue() +{ + return(current_blue); +} + +unsigned char CMMP750Controller::GetLedSpeed() +{ + return(current_speed); +} + +void CMMP750Controller::SetMode(unsigned char mode, unsigned char speed) +{ + current_mode = mode; + current_speed = speed; + + SendUpdate(); +} + +void CMMP750Controller::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + current_red = red; + current_green = green; + current_blue = blue; + + SendUpdate(); +} + +void CMMP750Controller::SendUpdate() +{ + unsigned char buffer[0x41] = { 0x00 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + + for(std::size_t i = 0; i < CM_COLOUR_MODE_DATA_SIZE; i++) + { + buffer[i+1] = colour_mode_data[current_mode][i]; + } + + if(current_mode > CM_MP750_MODE_BREATHING) + { + //If the mode is random colours set SPEED at BYTE2 + buffer[CM_RED_BYTE] = speed_mode_data[current_speed]; + } + else + { + //Otherwise SPEED is BYTE5 + buffer[CM_RED_BYTE] = current_red; + buffer[CM_GREEN_BYTE] = current_green; + buffer[CM_BLUE_BYTE] = current_blue; + buffer[CM_SPEED_BYTE] = speed_mode_data[current_speed]; + } + + hid_write(dev, buffer, buffer_size); +} diff --git a/Controllers/CoolerMasterController/CMMP750Controller/CMMP750Controller.h b/Controllers/CoolerMasterController/CMMP750Controller/CMMP750Controller.h new file mode 100644 index 0000000..abb8bae --- /dev/null +++ b/Controllers/CoolerMasterController/CMMP750Controller/CMMP750Controller.h @@ -0,0 +1,99 @@ +/*---------------------------------------------------------*\ +| CMMP750Controller.h | +| | +| Driver for Cooler Master MP750 mousemat | +| | +| Chris M (Dr_No) 16 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +#define CM_COLOUR_MODE_DATA_SIZE (sizeof(colour_mode_data[0]) / sizeof(colour_mode_data[0][0])) +#define CM_INTERRUPT_TIMEOUT 250 +#define CM_DEVICE_NAME_SIZE (sizeof(device_name) / sizeof(device_name[ 0 ])) +#define CM_SERIAL_SIZE (sizeof(serial) / sizeof(serial[ 0 ])) +#define HID_MAX_STR 255 + +/*-------------------------------------------------------------------*\ +| Simple RGB device with 5 modes | +| BYTE0 = Mode (0x01 thru 0x05 | +| BYTE1 = ?? Must be set to 0x04 for colour modes otherwise ignored | +| BYTE2 = Colour Modes: RED else Cycle SPEED | +| BYTE3 = Colour Modes: GREEN else ignored | +| BYTE4 = Colour Modes: BLUE else ignored | +| BYTE5 = Colour Modes: SPEED else ignored | +\*-------------------------------------------------------------------*/ + +enum +{ + CM_MODE_BYTE = 1, + CM_LENGTH_BYTE = 2, + CM_RED_BYTE = 3, + CM_GREEN_BYTE = 4, + CM_BLUE_BYTE = 5, + CM_SPEED_BYTE = 6 +}; + +enum +{ + CM_MP750_MODE_OFF = 0x00, //Off + CM_MP750_MODE_STATIC = 0x01, //Static Mode + CM_MP750_MODE_BLINK = 0x02, //Blinking Mode + CM_MP750_MODE_BREATHING = 0x03, //Breathing Mode + CM_MP750_MODE_COLOR_CYCLE = 0x04, //Color Cycle Mode + CM_MP750_MODE_BREATH_CYCLE = 0x05 //Breathing Cycle Mode +}; + +enum +{ + MP750_SPEED_SLOWEST = 0x00, /* Slowest speed */ + MP750_SPEED_SLOWER = 0x01, /* Slower speed */ + MP750_SPEED_SLOW = 0x02, /* Slow speed */ + MP750_SPEED_SLOWISH = 0x03, /* Slowish speed */ + MP750_SPEED_NORMAL = 0x04, /* Normal speed */ + MP750_SPEED_FASTISH = 0x05, /* Fastish speed */ + MP750_SPEED_FAST = 0x06, /* Fast speed */ + MP750_SPEED_FASTER = 0x07, /* Faster speed */ + MP750_SPEED_FASTEST = 0x08, /* Fastest speed */ +}; + +class CMMP750Controller +{ +public: + CMMP750Controller(hid_device* dev_handle, char *_path); + ~CMMP750Controller(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + unsigned char GetMode(); + unsigned char GetLedRed(); + unsigned char GetLedGreen(); + unsigned char GetLedBlue(); + unsigned char GetLedSpeed(); + void SetMode(unsigned char mode, unsigned char speed); + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + +private: + std::string device_name; + std::string location; + hid_device* dev; + + unsigned char current_mode; + unsigned char current_speed; + + unsigned char current_red; + unsigned char current_green; + unsigned char current_blue; + + void GetStatus(); + void SendUpdate(); +}; diff --git a/Controllers/CoolerMasterController/CMMP750Controller/RGBController_CMMP750Controller.cpp b/Controllers/CoolerMasterController/CMMP750Controller/RGBController_CMMP750Controller.cpp new file mode 100644 index 0000000..1572134 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMP750Controller/RGBController_CMMP750Controller.cpp @@ -0,0 +1,181 @@ +/*---------------------------------------------------------*\ +| RGBController_CMMP750Controller.cpp | +| | +| RGBController for Cooler Master MP750 mousemat | +| | +| Chris M (Dr_No) 18 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CMMP750Controller.h" + +/**------------------------------------------------------------------*\ + @name Coolermaster Mouse Pad + @category Mousemat + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectCoolerMasterMousemats + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CMMP750Controller::RGBController_CMMP750Controller(CMMP750Controller* controller_ptr) +{ + controller = controller_ptr; + unsigned char speed = controller->GetLedSpeed(); + + name = controller->GetDeviceName(); + vendor = "Cooler Master"; + type = DEVICE_TYPE_MOUSEMAT; + description = controller->GetDeviceName(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Static; + Static.name = "Static"; + Static.value = CM_MP750_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Blink; + Blink.name = "Blink"; + Blink.value = CM_MP750_MODE_BLINK; + Blink.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Blink.speed_min = MP750_SPEED_SLOWEST; + Blink.speed_max = MP750_SPEED_FASTEST; + Blink.color_mode = MODE_COLORS_PER_LED; + Blink.speed = speed; + modes.push_back(Blink); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_MP750_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = MP750_SPEED_SLOWEST; + Breathing.speed_max = MP750_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed = speed; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = CM_MP750_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED; + ColorCycle.speed_min = MP750_SPEED_SLOWEST; + ColorCycle.speed_max = MP750_SPEED_FASTEST; + ColorCycle.color_mode = MODE_COLORS_NONE; + ColorCycle.speed = speed; + modes.push_back(ColorCycle); + + mode BreathCycle; + BreathCycle.name = "Breath Cycle"; + BreathCycle.value = CM_MP750_MODE_BREATH_CYCLE; + BreathCycle.flags = MODE_FLAG_HAS_SPEED; + BreathCycle.speed_min = MP750_SPEED_SLOWEST; + BreathCycle.speed_max = MP750_SPEED_FASTEST; + BreathCycle.color_mode = MODE_COLORS_NONE; + BreathCycle.speed = speed; + modes.push_back(BreathCycle); + + mode Off; + Off.name = "Turn Off"; + Off.value = CM_MP750_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + active_mode = GetDeviceMode(); +} + +RGBController_CMMP750Controller::~RGBController_CMMP750Controller() +{ + delete controller; +} + +int RGBController_CMMP750Controller::GetDeviceMode() +{ + int temp_mode = controller->GetMode(); + + for(unsigned int i = 0; i < modes.size(); i++) + { + if (temp_mode == modes[i].value) + { + return i; + } + } + + /*---------------------------------------------------------*\ + | If not found return 0 | + \*---------------------------------------------------------*/ + return 0; +} + +void RGBController_CMMP750Controller::SetupZones() +{ + zone MP_zone; + MP_zone.name = "Mousepad"; + MP_zone.type = ZONE_TYPE_SINGLE; + MP_zone.leds_min = 1; + MP_zone.leds_max = 1; + MP_zone.leds_count = 1; + MP_zone.matrix_map = NULL; + zones.push_back(MP_zone); + + led MP_led; + MP_led.name = "Mousepad LED"; + leds.push_back(MP_led); + + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize colors for each LED | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned char red = controller->GetLedRed(); + unsigned char grn = controller->GetLedGreen(); + unsigned char blu = controller->GetLedBlue(); + + colors[led_idx] = ToRGBColor(red, grn, blu); + } +} + +void RGBController_CMMP750Controller::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CMMP750Controller::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetColor(red, grn, blu); +} + +void RGBController_CMMP750Controller::UpdateZoneLEDs(int zone) +{ + RGBColor color = colors[zone]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetColor(red, grn, blu); +} + +void RGBController_CMMP750Controller::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_CMMP750Controller::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].value, modes[active_mode].speed); +} diff --git a/Controllers/CoolerMasterController/CMMP750Controller/RGBController_CMMP750Controller.h b/Controllers/CoolerMasterController/CMMP750Controller/RGBController_CMMP750Controller.h new file mode 100644 index 0000000..71880b0 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMP750Controller/RGBController_CMMP750Controller.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_CMMP750Controller.h | +| | +| RGBController for Cooler Master MP750 mousemat | +| | +| Chris M (Dr_No) 18 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CMMP750Controller.h" + +class RGBController_CMMP750Controller : public RGBController +{ +public: + RGBController_CMMP750Controller(CMMP750Controller* controller_ptr); + ~RGBController_CMMP750Controller(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CMMP750Controller* controller; + + int GetDeviceMode(); +}; diff --git a/Controllers/CoolerMasterController/CMMonitorController/CMMonitorController.cpp b/Controllers/CoolerMasterController/CMMonitorController/CMMonitorController.cpp new file mode 100644 index 0000000..a039978 --- /dev/null +++ b/Controllers/CoolerMasterController/CMMonitorController/CMMonitorController.cpp @@ -0,0 +1,208 @@ +/*---------------------------------------------------------*\ +| CMMonitorController.cpp | +| | +| Driver for Cooler Master monitor | +| | +| Morgan Guimard (morg) 18 Sep 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CMMonitorController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +CMMonitorController::CMMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +CMMonitorController::~CMMonitorController() +{ + hid_close(dev); +} + +std::string CMMonitorController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string CMMonitorController::GetNameString() +{ + return(name); +} + +std::string CMMonitorController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void CMMonitorController::SetMode(uint8_t mode_value, const RGBColor& color, uint8_t speed, uint8_t brightness) +{ + if(software_mode_enabled) + { + SetSoftwareModeEnabled(false); + } + + uint8_t usb_buf[CM_MONITOR_PACKET_LENGTH]; + memset(usb_buf, 0x00, CM_MONITOR_PACKET_LENGTH); + + usb_buf[1] = 0x80; + usb_buf[2] = (mode_value == CM_MONITOR_OFF_MODE) ? 0x0F : 0x0B; + usb_buf[3] = 0x02; + usb_buf[4] = 0x02; + usb_buf[5] = mode_value; + usb_buf[6] = (mode_value == CM_MONITOR_OFF_MODE) ? 0x00 : 0x08; + usb_buf[7] = speed; + usb_buf[8] = brightness; + usb_buf[9] = RGBGetRValue(color); + usb_buf[10] = RGBGetGValue(color); + usb_buf[11] = RGBGetBValue(color); + + hid_write(dev, usb_buf, CM_MONITOR_PACKET_LENGTH); +} + +void CMMonitorController::SetCustomMode(const std::vector& colors, uint8_t brightnesss) +{ + if(software_mode_enabled) + { + SetSoftwareModeEnabled(false); + } + + /*---------------------------------------------------------*\ + | Creates the color buffer | + \*---------------------------------------------------------*/ + uint8_t color_data[CM_MONITOR_COLOR_DATA_LENGTH]; + memset(color_data, 0x00, CM_MONITOR_COLOR_DATA_LENGTH); + + uint8_t offset = 0; + + for(const RGBColor& color: colors) + { + color_data[offset++] = RGBGetRValue(color); + color_data[offset++] = RGBGetGValue(color); + color_data[offset++] = RGBGetBValue(color); + } + + /*---------------------------------------------------------*\ + | Sends the 7 sequence packets | + \*---------------------------------------------------------*/ + uint8_t usb_buf[CM_MONITOR_PACKET_LENGTH]; + + offset = 0; + + for(unsigned int i = 0; i < 7; i++) + { + memset(usb_buf, 0x00, CM_MONITOR_PACKET_LENGTH); + + usb_buf[1] = i < 6 ? i : 0x86; + + /*---------------------------------------------------------*\ + | First packet contains static data | + \*---------------------------------------------------------*/ + if(i == 0) + { + usb_buf[2] = 0x10; + usb_buf[3] = 0x02; + usb_buf[4] = 0x02; + usb_buf[5] = 0x80; + usb_buf[6] = brightnesss; + + memcpy(&usb_buf[7], &color_data[offset], CM_MONITOR_PACKET_LENGTH - 7); + offset += CM_MONITOR_PACKET_LENGTH - 7; + } + else + { + memcpy(&usb_buf[2], &color_data[offset], CM_MONITOR_PACKET_LENGTH - 2); + offset += (CM_MONITOR_PACKET_LENGTH - 2); + } + + hid_write(dev, usb_buf, CM_MONITOR_PACKET_LENGTH); + } +} + +void CMMonitorController::SendDirect(const std::vector& colors) +{ + if(!software_mode_enabled) + { + SetSoftwareModeEnabled(true); + } + + /*---------------------------------------------------------*\ + | Creates the color buffer | + \*---------------------------------------------------------*/ + uint8_t color_data[CM_MONITOR_COLOR_DATA_LENGTH]; + memset(color_data, 0x00, CM_MONITOR_COLOR_DATA_LENGTH); + + unsigned int offset = 0; + + for(const RGBColor& color: colors) + { + color_data[offset++] = RGBGetRValue(color); + color_data[offset++] = RGBGetGValue(color); + color_data[offset++] = RGBGetBValue(color); + } + + /*---------------------------------------------------------*\ + | Sends the 7 sequence packets | + \*---------------------------------------------------------*/ + uint8_t usb_buf[CM_MONITOR_PACKET_LENGTH]; + + offset = 0; + + for(unsigned int i = 0; i < 7; i++) + { + memset(usb_buf, 0x00, CM_MONITOR_PACKET_LENGTH); + + usb_buf[1] = i < 6 ? i : 0x86; + + if(i == 0) + { + usb_buf[2] = 0x07; + usb_buf[3] = 0x02; + usb_buf[4] = 0x02; + usb_buf[5] = 0x01; + usb_buf[6] = 0x80; + + memcpy(&usb_buf[7], &color_data[offset], CM_MONITOR_PACKET_LENGTH - 7); + offset += CM_MONITOR_PACKET_LENGTH - 7; + } + else + { + memcpy(&usb_buf[2], &color_data[offset], CM_MONITOR_PACKET_LENGTH - 2); + offset += (CM_MONITOR_PACKET_LENGTH - 2); + } + + hid_write(dev, usb_buf, CM_MONITOR_PACKET_LENGTH); + } + +} + +void CMMonitorController::SetSoftwareModeEnabled(bool value) +{ + uint8_t usb_buf[CM_MONITOR_PACKET_LENGTH]; + memset(usb_buf, 0x00, CM_MONITOR_PACKET_LENGTH); + + usb_buf[1] = 0x80; + usb_buf[2] = 0x07; + usb_buf[3] = 0x02; + usb_buf[4] = 0x02; + usb_buf[6] = value; + + hid_write(dev, usb_buf, CM_MONITOR_PACKET_LENGTH); + + software_mode_enabled = value; +} diff --git a/Controllers/CoolerMasterController/CMMonitorController/CMMonitorController.h b/Controllers/CoolerMasterController/CMMonitorController/CMMonitorController.h new file mode 100644 index 0000000..300e35d --- /dev/null +++ b/Controllers/CoolerMasterController/CMMonitorController/CMMonitorController.h @@ -0,0 +1,61 @@ +/*---------------------------------------------------------*\ +| CMMonitorController.h | +| | +| Driver for Cooler Master monitor | +| | +| Morgan Guimard (morg) 18 Sep 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define CM_MONITOR_PACKET_LENGTH 65 +#define CM_MONITOR_COLOR_DATA_LENGTH 436 + +enum +{ + CM_MONITOR_DIRECT_MODE = 0xFF, + CM_MONITOR_CUSTOM_MODE = 0xFE, + CM_MONITOR_SPECTRUM_MODE = 0x00, + CM_MONITOR_RELOAD_MODE = 0x01, + CM_MONITOR_RECOIL_MODE = 0x02, + CM_MONITOR_BREATHING_MODE = 0x03, + CM_MONITOR_REFILL_MODE = 0x04, + CM_MONITOR_OFF_MODE = 0x06 +}; + +enum +{ + CM_MONITOR_BRIGHTNESS_MAX = 0xFF, + CM_MONITOR_BRIGHTNESS_MIN = 0x00, + CM_MONITOR_SPEED_MAX = 0x04, + CM_MONITOR_SPEED_MIN = 0x00, +}; + +class CMMonitorController +{ +public: + CMMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~CMMonitorController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendDirect(const std::vector& colors); + void SetMode(uint8_t mode_value, const RGBColor& color, uint8_t speed, uint8_t brightness); + void SetCustomMode(const std::vector& colors, uint8_t brightnesss); + +private: + std::string location; + std::string name; + hid_device* dev; + bool software_mode_enabled = false; + void SetSoftwareModeEnabled(bool value); +}; diff --git a/Controllers/CoolerMasterController/CMMonitorController/RGBController_CMMonitorController.cpp b/Controllers/CoolerMasterController/CMMonitorController/RGBController_CMMonitorController.cpp new file mode 100644 index 0000000..e88666c --- /dev/null +++ b/Controllers/CoolerMasterController/CMMonitorController/RGBController_CMMonitorController.cpp @@ -0,0 +1,221 @@ +/*---------------------------------------------------------*\ +| RGBController_CMMonitorController.cpp | +| | +| RGBController for Cooler Master monitor | +| | +| Morgan Guimard (morg) 18 Sep 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_CMMonitorController.h" + +/**------------------------------------------------------------------*\ + @name Coolermaster Gaming Monitor + @category Accessory + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCoolerMasterMonitor + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CMMonitorController::RGBController_CMMonitorController(CMMonitorController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "CoolerMaster"; + type = DEVICE_TYPE_MONITOR; + description = "CoolerMaster Monitor Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CM_MONITOR_DIRECT_MODE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Spectrum; + Spectrum.name = "Spectrum cycle"; + Spectrum.value = CM_MONITOR_SPECTRUM_MODE; + Spectrum.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.speed_min = CM_MONITOR_SPEED_MIN; + Spectrum.speed_max = CM_MONITOR_SPEED_MAX; + Spectrum.speed = CM_MONITOR_SPEED_MAX/2; + Spectrum.brightness_min = CM_MONITOR_BRIGHTNESS_MIN; + Spectrum.brightness_max = CM_MONITOR_BRIGHTNESS_MAX; + Spectrum.brightness = CM_MONITOR_BRIGHTNESS_MAX; + modes.push_back(Spectrum); + + mode Reload; + Reload.name = "Reload"; + Reload.value = CM_MONITOR_RELOAD_MODE; + Reload.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Reload.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reload.colors_min = 1; + Reload.colors_max = 1; + Reload.colors.resize(1); + Reload.speed_min = CM_MONITOR_SPEED_MIN; + Reload.speed_max = CM_MONITOR_SPEED_MAX; + Reload.speed = CM_MONITOR_SPEED_MAX/2; + Reload.brightness_min = CM_MONITOR_BRIGHTNESS_MIN; + Reload.brightness_max = CM_MONITOR_BRIGHTNESS_MAX; + Reload.brightness = CM_MONITOR_BRIGHTNESS_MAX; + modes.push_back(Reload); + + mode Recoil; + Recoil.name = "Recoil"; + Recoil.value = CM_MONITOR_RECOIL_MODE; + Recoil.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Recoil.color_mode = MODE_COLORS_MODE_SPECIFIC; + Recoil.colors_min = 1; + Recoil.colors_max = 1; + Recoil.colors.resize(1); + Recoil.speed_min = CM_MONITOR_SPEED_MIN; + Recoil.speed_max = CM_MONITOR_SPEED_MAX; + Recoil.speed = CM_MONITOR_SPEED_MAX/2; + Recoil.brightness_min = CM_MONITOR_BRIGHTNESS_MIN; + Recoil.brightness_max = CM_MONITOR_BRIGHTNESS_MAX; + Recoil.brightness = CM_MONITOR_BRIGHTNESS_MAX; + modes.push_back(Recoil); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_MONITOR_BREATHING_MODE; + Breathing.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + Breathing.speed_min = CM_MONITOR_SPEED_MIN; + Breathing.speed_max = CM_MONITOR_SPEED_MAX; + Breathing.speed = CM_MONITOR_SPEED_MAX/2; + Breathing.brightness_min = CM_MONITOR_BRIGHTNESS_MIN; + Breathing.brightness_max = CM_MONITOR_BRIGHTNESS_MAX; + Breathing.brightness = CM_MONITOR_BRIGHTNESS_MAX; + modes.push_back(Breathing); + + mode Refill; + Refill.name = "Refill"; + Refill.value = CM_MONITOR_REFILL_MODE; + Refill.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Refill.color_mode = MODE_COLORS_MODE_SPECIFIC; + Refill.colors_min = 1; + Refill.colors_max = 1; + Refill.colors.resize(1); + Refill.speed_min = CM_MONITOR_SPEED_MIN; + Refill.speed_max = CM_MONITOR_SPEED_MAX; + Refill.speed = CM_MONITOR_SPEED_MAX/2; + Refill.brightness_min = CM_MONITOR_BRIGHTNESS_MIN; + Refill.brightness_max = CM_MONITOR_BRIGHTNESS_MAX; + Refill.brightness = CM_MONITOR_BRIGHTNESS_MAX; + modes.push_back(Refill); + + mode Custom; + Custom.name = "Custom"; + Custom.value = CM_MONITOR_CUSTOM_MODE; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.brightness_min = CM_MONITOR_BRIGHTNESS_MIN; + Custom.brightness_max = CM_MONITOR_BRIGHTNESS_MAX; + Custom.brightness = CM_MONITOR_BRIGHTNESS_MAX; + modes.push_back(Custom); + + mode Off; + Off.name = "Off"; + Off.value = CM_MONITOR_SPECTRUM_MODE; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_CMMonitorController::~RGBController_CMMonitorController() +{ + delete controller; +} + +void RGBController_CMMonitorController::SetupZones() +{ + zone z; + + z.name = "Monitor"; + z.type = ZONE_TYPE_LINEAR; + z.leds_min = 47; + z.leds_max = 47; + z.leds_count = 47; + z.matrix_map = NULL; + + zones.push_back(z); + + for(unsigned int i = 0; i < 47; i++) + { + led l; + l.name = std::to_string(i + 1); + l.value = i; + leds.push_back(l); + } + + SetupColors(); +} + +void RGBController_CMMonitorController::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CMMonitorController::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == CM_MONITOR_DIRECT_MODE) + { + controller->SendDirect(colors); + } + else if(modes[active_mode].value == CM_MONITOR_CUSTOM_MODE) + { + controller->SetCustomMode(colors, modes[active_mode].brightness); + } +} + +void RGBController_CMMonitorController::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMMonitorController::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMMonitorController::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case CM_MONITOR_OFF_MODE: + case CM_MONITOR_SPECTRUM_MODE: + controller->SetMode(modes[active_mode].value, 0, modes[active_mode].speed, modes[active_mode].brightness); + break; + + case CM_MONITOR_RELOAD_MODE: + case CM_MONITOR_RECOIL_MODE: + case CM_MONITOR_BREATHING_MODE: + case CM_MONITOR_REFILL_MODE: + controller->SetMode(modes[active_mode].value, modes[active_mode].colors[0], modes[active_mode].speed, modes[active_mode].brightness); + break; + + case CM_MONITOR_CUSTOM_MODE: + DeviceUpdateLEDs(); + break; + default: break; + } +} diff --git a/Controllers/CoolerMasterController/CMMonitorController/RGBController_CMMonitorController.h b/Controllers/CoolerMasterController/CMMonitorController/RGBController_CMMonitorController.h new file mode 100644 index 0000000..1d1d23a --- /dev/null +++ b/Controllers/CoolerMasterController/CMMonitorController/RGBController_CMMonitorController.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_CMMonitorController.h | +| | +| RGBController for Cooler Master monitor | +| | +| Morgan Guimard (morg) 18 Sep 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CMMonitorController.h" + +class RGBController_CMMonitorController : public RGBController +{ +public: + RGBController_CMMonitorController(CMMonitorController* controller_ptr); + ~RGBController_CMMonitorController(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CMMonitorController* controller; +}; diff --git a/Controllers/CoolerMasterController/CMR6000Controller/CMR6000Controller.cpp b/Controllers/CoolerMasterController/CMR6000Controller/CMR6000Controller.cpp new file mode 100644 index 0000000..76df720 --- /dev/null +++ b/Controllers/CoolerMasterController/CMR6000Controller/CMR6000Controller.cpp @@ -0,0 +1,214 @@ +/*---------------------------------------------------------*\ +| CMR6000Controller.cpp | +| | +| Driver for Cooler Master AMD Radeon 6000 series GPU | +| | +| Eric S (edbgon) 02 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CMR6000Controller.h" +#include "StringUtils.h" + +CMR6000Controller::CMR6000Controller(hid_device* dev_handle, char *_path, uint16_t _pid) +{ + dev = dev_handle; + location = _path; + pid = _pid; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); +} + +CMR6000Controller::~CMR6000Controller() +{ + if(dev) + { + hid_close(dev); + } +} + +std::string CMR6000Controller::GetDeviceName() +{ + return(device_name); +} + +std::string CMR6000Controller::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string CMR6000Controller::GetLocation() +{ + return("HID: " + location); +} + +unsigned char CMR6000Controller::GetMode() +{ + return(current_mode); +} + +unsigned char CMR6000Controller::GetLedSpeed() +{ + return(current_speed); +} + +unsigned char CMR6000Controller::GetBrightness() +{ + return(current_brightness); +} + +bool CMR6000Controller::GetRandomColours() +{ + return(current_random); +} + +uint16_t CMR6000Controller::GetPID() +{ + return(pid); +} + +void CMR6000Controller::SetMode(unsigned char mode, unsigned char speed, RGBColor color1, RGBColor color2, unsigned char random, unsigned char brightness) +{ + current_mode = mode; + current_speed = speed; + primary = color1; + secondary = color2; + current_random = random; + current_brightness = brightness; + + SendUpdate(); +} + +void CMR6000Controller::SendUpdate() +{ + if(current_mode == CM_MR6000_MODE_OFF) + { + unsigned char buffer[CM_6K_PACKET_SIZE] = { 0x00, 0x41, 0x43 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + hid_write(dev, buffer, buffer_size); + } + else + { + SendEnableCommand(); + + if(pid == COOLERMASTER_RADEON_6900_PID) + { + SendSecondColour(); + } + + unsigned char buffer[CM_6K_PACKET_SIZE] = { 0x00 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + memset(buffer, 0xFF, buffer_size); + + buffer[0x00] = 0x00; + buffer[0x01] = 0x51; + buffer[0x02] = 0x2C; + buffer[0x03] = 0x01; + buffer[0x04] = 0x00; + buffer[0x05] = current_mode; + buffer[0x06] = current_speed; + buffer[0x07] = current_random; //random (A0) + //buffer[0x09] = 0xFF; + buffer[0x0A] = current_brightness; + buffer[0x0B] = (current_mode == CM_MR6000_MODE_COLOR_CYCLE) ? 0xFF : RGBGetRValue(primary); + buffer[0x0C] = (current_mode == CM_MR6000_MODE_COLOR_CYCLE) ? 0xFF : RGBGetGValue(primary); + buffer[0x0D] = (current_mode == CM_MR6000_MODE_COLOR_CYCLE) ? 0xFF : RGBGetBValue(primary); + buffer[0x0E] = 0x00; + buffer[0x0F] = 0x00; + buffer[0x10] = 0x00; + + /*-----------------------------------------------------------------*\ + | Index 0x08 looks to be mode specific flags / options | + \*-----------------------------------------------------------------*/ + switch(current_mode) + { + case CM_MR6000_MODE_BREATHE: + buffer[0x08] = 0x03; + break; + case CM_MR6000_MODE_RAINBOW: + buffer[0x08] = 0x05; + break; + case CM_MR6000_MODE_CHASE: + buffer[0x08] = 0xC3; + break; + case CM_MR6000_MODE_SWIRL: + buffer[0x08] = 0x4A; + break; + default: + buffer[0x08] = 0xFF; + } + + + hid_write(dev, buffer, buffer_size); + + SendColourConfig(); + SendApplyCommand(); + } +} + +void CMR6000Controller::SendEnableCommand() +{ + unsigned char buffer[CM_6K_PACKET_SIZE] = { 0x00, 0x41, 0x80 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_6K_INTERRUPT_TIMEOUT); +} + +void CMR6000Controller::SendApplyCommand() +{ + unsigned char buffer[CM_6K_PACKET_SIZE] = { 0x00, 0x51, 0x28, 0x00, 0x00, 0xE0 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_6K_INTERRUPT_TIMEOUT); +} + +void CMR6000Controller::SendColourConfig() +{ + unsigned char buffer[CM_6K_PACKET_SIZE] = { 0x00, 0x51, 0xA0, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x06 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + + for(int i = 0x0B; i < 0x1A; i++) + { + buffer[i] = current_mode; + } + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_6K_INTERRUPT_TIMEOUT); +} + +void CMR6000Controller::SendSecondColour() +{ + unsigned char buffer[CM_6K_PACKET_SIZE] = { 0x00, 0x51, 0x9C, 0x01, 0x00 }; + + buffer[5] = RGBGetRValue(primary); + buffer[6] = RGBGetGValue(primary); + buffer[7] = RGBGetBValue(primary); + buffer[8] = RGBGetRValue(secondary); + buffer[9] = RGBGetGValue(secondary); + buffer[10] = RGBGetBValue(secondary); + + hid_write(dev, buffer, CM_6K_PACKET_SIZE); + hid_read_timeout(dev, buffer, CM_6K_PACKET_SIZE, CM_6K_INTERRUPT_TIMEOUT); +} diff --git a/Controllers/CoolerMasterController/CMR6000Controller/CMR6000Controller.h b/Controllers/CoolerMasterController/CMR6000Controller/CMR6000Controller.h new file mode 100644 index 0000000..5ab3e1c --- /dev/null +++ b/Controllers/CoolerMasterController/CMR6000Controller/CMR6000Controller.h @@ -0,0 +1,98 @@ +/*---------------------------------------------------------*\ +| CMR6000Controller.h | +| | +| Driver for Cooler Master AMD Radeon 6000 series GPU | +| | +| Eric S (edbgon) 02 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define COOLERMASTER_RADEON_6000_PID 0x014D +#define COOLERMASTER_RADEON_6900_PID 0x015B + +#define CM_6K_PACKET_SIZE 65 //Includes extra first byte for non HID Report packets +#define CM_6K_INTERRUPT_TIMEOUT 250 +#define CM_6K_DEVICE_NAME_SIZE (sizeof(device_name) / sizeof(device_name[ 0 ])) +#define CM_6K_SERIAL_SIZE (sizeof(serial) / sizeof(serial[ 0 ])) +#define HID_MAX_STR 255 + +enum +{ + CM_MR6000_MODE_DIRECT = 0x00, //Direct Mode + CM_MR6000_MODE_BREATHE = 0x01, //Breathe Mode + CM_MR6000_MODE_COLOR_CYCLE = 0x02, //Color cycle + + CM_MR6000_MODE_RAINBOW = 0x07, //Rainbow + CM_MR6000_MODE_BOUNCE = 0x08, //Bounce + CM_MR6000_MODE_CHASE = 0x09, //Chase + CM_MR6000_MODE_SWIRL = 0x0A, //Swirl + + CM_MR6000_MODE_OFF = 0xFF, //Off +}; + +enum +{ + MR6000_CYCLE_SPEED_SLOWEST = 0x96, /* Slowest speed */ + MR6000_CYCLE_SPEED_SLOW = 0x8C, /* Slow speed */ + MR6000_CYCLE_SPEED_NORMAL = 0x80, /* Normal speed */ + MR6000_CYCLE_SPEED_FAST = 0x6E, /* Fast speed */ + MR6000_CYCLE_SPEED_FASTEST = 0x68, /* Fastest speed */ + + MR6000_RAINBOW_SPEED_SLOWEST = 0x78, /* Slowest speed */ + MR6000_RAINBOW_SPEED_NORMAL = 0x6B, /* Normal speed */ + MR6000_RAINBOW_SPEED_FASTEST = 0x60, /* Fastest speed */ + + MR6000_BREATHE_SPEED_SLOWEST = 0x3C, /* Slowest speed */ + MR6000_BREATHE_SPEED_SLOW = 0x37, /* Slow speed */ + MR6000_BREATHE_SPEED_NORMAL = 0x31, /* Normal speed */ + MR6000_BREATHE_SPEED_FAST = 0x2C, /* Fast speed */ + MR6000_BREATHE_SPEED_FASTEST = 0x26, /* Fastest speed */ +}; + +class CMR6000Controller +{ +public: + CMR6000Controller(hid_device* dev_handle, char *_path, uint16_t _pid); + ~CMR6000Controller(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + unsigned char GetMode(); + unsigned char GetLedSpeed(); + unsigned char GetBrightness(); + bool GetRandomColours(); + uint16_t GetPID(); + + void SetMode(unsigned char mode, unsigned char speed, RGBColor color1, RGBColor color2, unsigned char random, unsigned char brightness); + +private: + std::string device_name; + std::string location; + hid_device* dev; + uint16_t pid; + + unsigned char current_mode; + unsigned char current_speed; + unsigned char current_random; + + unsigned char current_brightness; + RGBColor primary; + RGBColor secondary; + + void SendUpdate(); + void SendEnableCommand(); + void SendApplyCommand(); + void SendColourConfig(); + void SendSecondColour(); +}; diff --git a/Controllers/CoolerMasterController/CMR6000Controller/RGBController_CMR6000Controller.cpp b/Controllers/CoolerMasterController/CMR6000Controller/RGBController_CMR6000Controller.cpp new file mode 100644 index 0000000..e20dd4e --- /dev/null +++ b/Controllers/CoolerMasterController/CMR6000Controller/RGBController_CMR6000Controller.cpp @@ -0,0 +1,228 @@ +/*---------------------------------------------------------*\ +| RGBController_CMR6000Controller.cpp | +| | +| RGBController for Cooler Master AMD Radeon 6000 series | +| GPU | +| | +| Eric S (edbgon) 02 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CMR6000Controller.h" + +/**------------------------------------------------------------------*\ + @name AMD Radeon 6000 + @category GPU + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCoolerMasterGPU + @comment Similar to the Wraith Spire before it the AMD branded Radeon + GPUs have an RGB controller provided by Coolermaster. +\*-------------------------------------------------------------------*/ + +RGBController_CMR6000Controller::RGBController_CMR6000Controller(CMR6000Controller* controller_ptr) +{ + controller = controller_ptr; + + name = "AMD RX 6xxx GPU"; + vendor = "Cooler Master"; + type = DEVICE_TYPE_GPU; + description = controller->GetDeviceName(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Off; + Off.name = "Off"; + Off.flags = 0; + Off.value = CM_MR6000_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CM_MR6000_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR| MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.speed = 0xFF; + Direct.brightness_min = 0x00; + Direct.brightness_max = 0xFF; + Direct.brightness = 0xFF; + modes.push_back(Direct); + + mode ColorCycle; + ColorCycle.name = "Spectrum Cycle"; + ColorCycle.value = CM_MR6000_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + ColorCycle.speed_min = MR6000_CYCLE_SPEED_SLOWEST; + ColorCycle.speed = MR6000_CYCLE_SPEED_NORMAL; + ColorCycle.speed_max = MR6000_CYCLE_SPEED_FASTEST; + ColorCycle.color_mode = MODE_COLORS_NONE; + ColorCycle.speed = MR6000_CYCLE_SPEED_NORMAL; + ColorCycle.brightness_min = 0x00; + ColorCycle.brightness_max = 0xFF; + ColorCycle.brightness = 0x7F; + modes.push_back(ColorCycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_MR6000_MODE_BREATHE; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Breathing.speed_min = MR6000_BREATHE_SPEED_SLOWEST; + Breathing.speed = MR6000_BREATHE_SPEED_NORMAL; + Breathing.speed_max = MR6000_BREATHE_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + Breathing.speed = MR6000_BREATHE_SPEED_NORMAL; + modes.push_back(Breathing); + + if(controller->GetPID() == COOLERMASTER_RADEON_6900_PID) + { + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = CM_MR6000_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rainbow.speed_min = MR6000_RAINBOW_SPEED_SLOWEST; + Rainbow.speed = MR6000_RAINBOW_SPEED_NORMAL; + Rainbow.speed_max = MR6000_RAINBOW_SPEED_FASTEST; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed = MR6000_RAINBOW_SPEED_NORMAL; + Rainbow.brightness_min = 0x00; + Rainbow.brightness_max = 0xFF; + Rainbow.brightness = 0xFF; + modes.push_back(Rainbow); + + mode Bounce; + Bounce.name = "Bounce"; + Bounce.value = CM_MR6000_MODE_BOUNCE; + Bounce.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Bounce.speed_min = MR6000_CYCLE_SPEED_SLOWEST; + Bounce.speed = MR6000_CYCLE_SPEED_NORMAL; + Bounce.speed_max = MR6000_CYCLE_SPEED_FASTEST; + Bounce.color_mode = MODE_COLORS_NONE; + Bounce.speed = MR6000_CYCLE_SPEED_NORMAL; + Bounce.brightness_min = 0x00; + Bounce.brightness_max = 0xFF; + Bounce.brightness = 0xFF; + modes.push_back(Bounce); + + mode Chase; + Chase.name = "Chase"; + Chase.value = CM_MR6000_MODE_CHASE; + Chase.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Chase.speed_min = MR6000_CYCLE_SPEED_SLOWEST; + Chase.speed = MR6000_CYCLE_SPEED_NORMAL; + Chase.speed_max = MR6000_CYCLE_SPEED_FASTEST; + Chase.color_mode = MODE_COLORS_MODE_SPECIFIC; + Chase.colors_min = 2; + Chase.colors_max = 2; + Chase.colors.resize(2); + Chase.speed = MR6000_CYCLE_SPEED_NORMAL; + Chase.brightness_min = 0; + Chase.brightness_max = 0xFF; + Chase.brightness = 0xFF; + modes.push_back(Chase); + + mode Swirl; + Swirl.name = "Swirl"; + Swirl.value = CM_MR6000_MODE_SWIRL; + Swirl.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Swirl.speed_min = MR6000_CYCLE_SPEED_SLOWEST; + Swirl.speed = MR6000_CYCLE_SPEED_NORMAL; + Swirl.speed_max = MR6000_CYCLE_SPEED_FASTEST; + Swirl.color_mode = MODE_COLORS_MODE_SPECIFIC; + Swirl.colors_min = 1; + Swirl.colors_max = 1; + Swirl.colors.resize(1); + Swirl.speed = MR6000_CYCLE_SPEED_NORMAL; + Swirl.brightness_min = 0; + Swirl.brightness_max = 0xFF; + Swirl.brightness = 0xFF; + modes.push_back(Swirl); + } + + SetupZones(); + active_mode = 1; +} + +RGBController_CMR6000Controller::~RGBController_CMR6000Controller() +{ + delete controller; +} + +void RGBController_CMR6000Controller::SetupZones() +{ + zone GP_zone; + GP_zone.name = "GPU"; + GP_zone.type = ZONE_TYPE_SINGLE; + GP_zone.leds_min = 1; + GP_zone.leds_max = 1; + GP_zone.leds_count = 1; + GP_zone.matrix_map = NULL; + zones.push_back(GP_zone); + + led GP_led; + GP_led.name = "Logo"; + GP_led.value = 0; + leds.push_back(GP_led); + + SetupColors(); + +} + +void RGBController_CMR6000Controller::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CMR6000Controller::DeviceUpdateLEDs() +{ + mode new_mode = modes[active_mode]; + RGBColor color1 = (new_mode.colors.size() > 0) ? new_mode.colors[0] : colors[0]; + RGBColor color2 = (new_mode.colors.size() > 1) ? new_mode.colors[1] : 0; + unsigned char bri = (new_mode.flags & MODE_FLAG_HAS_BRIGHTNESS) ? new_mode.brightness : 0xFF; + unsigned char rnd = 0x20; + + switch(new_mode.value) + { + /*-----------------------------------------------------------------*\ + | Breathing mode requires value 0x20 when in MODE_SPECIFIC_COLOR | + \*-----------------------------------------------------------------*/ + case CM_MR6000_MODE_BREATHE: + if(new_mode.color_mode == MODE_COLORS_RANDOM) + { + rnd = 0xA0; + } + break; + case CM_MR6000_MODE_SWIRL: + case CM_MR6000_MODE_CHASE: + rnd = new_mode.direction; + break; + default: + rnd = 0; + } + + controller->SetMode(new_mode.value, new_mode.speed, color1, color2, rnd, bri); +} + +void RGBController_CMR6000Controller::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMR6000Controller::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CMR6000Controller::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/CoolerMasterController/CMR6000Controller/RGBController_CMR6000Controller.h b/Controllers/CoolerMasterController/CMR6000Controller/RGBController_CMR6000Controller.h new file mode 100644 index 0000000..6d3a5d3 --- /dev/null +++ b/Controllers/CoolerMasterController/CMR6000Controller/RGBController_CMR6000Controller.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_CMR6000Controller.h | +| | +| RGBController for Cooler Master AMD Radeon 6000 series | +| GPU | +| | +| Eric S (edbgon) 02 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CMR6000Controller.h" + +class RGBController_CMR6000Controller : public RGBController +{ +public: + RGBController_CMR6000Controller(CMR6000Controller* controller_ptr); + ~RGBController_CMR6000Controller(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); +private: + CMR6000Controller* controller; + + int GetDeviceMode(); +}; diff --git a/Controllers/CoolerMasterController/CMRGBController/CMRGBController.cpp b/Controllers/CoolerMasterController/CMRGBController/CMRGBController.cpp new file mode 100644 index 0000000..ab91979 --- /dev/null +++ b/Controllers/CoolerMasterController/CMRGBController/CMRGBController.cpp @@ -0,0 +1,374 @@ +/*---------------------------------------------------------*\ +| CMRGBController.cpp | +| | +| Driver for Cooler Master RGB controller | +| | +| Nic W (midgetspy) 13 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_CMRGBController.h" +#include "CMRGBController.h" +#include "StringUtils.h" + +CMRGBController::CMRGBController(hid_device* dev_handle, char* path) +{ + dev = dev_handle; + location = path; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + + ReadCurrentMode(); +} + +void CMRGBController::SendFlowControl(unsigned char byte_flag) +{ + const unsigned char buffer_size = CM_RGBC_PACKET_SIZE; + unsigned char buffer[buffer_size] = { 0x00, CM_RGBC_OPCODE_OP_FLOW_CONTROL }; //Packets on Windows need a 0x00 if they don't use ReportIDs + + buffer[0x02] = byte_flag; + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_RGBC_INTERRUPT_TIMEOUT); +} + +void CMRGBController::SendApply() +{ + const unsigned char buffer_size = CM_RGBC_PACKET_SIZE; + unsigned char buffer[buffer_size] = { 0x00, CM_RGBC_OPCODE_OP_UNKNOWN_50, CM_RGBC_OPCODE_TYPE_UNKNOWN_55 }; //Packets on Windows need a 0x00 if they don't use ReportIDs + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_RGBC_INTERRUPT_TIMEOUT); +} + +void CMRGBController::SendReadMode() +{ + const unsigned char buffer_size = CM_RGBC_PACKET_SIZE; + unsigned char buffer[buffer_size] = { }; + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_OP] = CM_RGBC_OPCODE_OP_READ; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_TYPE] = CM_RGBC_OPCODE_TYPE_MODE; + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_RGBC_INTERRUPT_TIMEOUT); + + current_mode = buffer[CM_RGBC_PACKET_OFFSET_MODE]; +} + +void CMRGBController::SendSetMode(unsigned char mode) +{ + const unsigned char buffer_size = CM_RGBC_PACKET_SIZE; + unsigned char buffer[buffer_size] = { }; + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_OP] = CM_RGBC_OPCODE_OP_WRITE; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_TYPE] = CM_RGBC_OPCODE_TYPE_MODE; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MODE] = mode; + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_RGBC_INTERRUPT_TIMEOUT); +} + +void CMRGBController::SendSetCustomColors(RGBColor color_1, RGBColor color_2, RGBColor color_3, RGBColor color_4) +{ + const unsigned char buffer_size = CM_RGBC_PACKET_SIZE; + unsigned char buffer[buffer_size] = { }; + + current_port1_color = color_1; + current_port2_color = color_2; + current_port3_color = color_3; + current_port4_color = color_4; + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_OP] = CM_RGBC_OPCODE_OP_WRITE; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_TYPE] = CM_RGBC_OPCODE_TYPE_LED_INFO; + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_1] = RGBGetRValue(color_1); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_1 + 1] = RGBGetGValue(color_1); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_1 + 2] = RGBGetBValue(color_1); + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_2] = RGBGetRValue(color_2); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_2 + 1] = RGBGetGValue(color_2); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_2 + 2] = RGBGetBValue(color_2); + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_3] = RGBGetRValue(color_3); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_3 + 1] = RGBGetGValue(color_3); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_3 + 2] = RGBGetBValue(color_3); + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_4] = RGBGetRValue(color_4); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_4 + 1] = RGBGetGValue(color_4); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_4 + 2] = RGBGetBValue(color_4); + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_RGBC_INTERRUPT_TIMEOUT); +} + +void CMRGBController::SendReadCustomColors() +{ + const unsigned char buffer_size = CM_RGBC_PACKET_SIZE; + unsigned char buffer[buffer_size] = { }; + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_OP] = CM_RGBC_OPCODE_OP_READ; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_TYPE] = CM_RGBC_OPCODE_TYPE_LED_INFO; + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_RGBC_INTERRUPT_TIMEOUT); + + current_port1_color = ToRGBColor( + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_1], + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_1 + 1], + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_1 + 2]); + current_port2_color = ToRGBColor( + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_2], + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_2 + 1], + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_2 + 2]); + current_port3_color = ToRGBColor( + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_3], + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_3 + 1], + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_3 + 2]); + current_port4_color = ToRGBColor( + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_4], + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_4 + 1], + buffer[CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_4 + 2]); +} + +void CMRGBController::SendSetConfig(unsigned char mode, unsigned char speed, unsigned char brightness, RGBColor color_1, RGBColor color_2, bool simplified=false, bool multilayer=false) +{ + const unsigned char buffer_size = CM_RGBC_PACKET_SIZE; + unsigned char buffer[buffer_size] = { }; + + current_mode = mode; + current_speed = speed; + current_brightness = brightness; + current_mode_color_1 = color_1; + current_mode_color_2 = color_2; + + /*---------------------------------------------*\ + | Handle special cases | + \*---------------------------------------------*/ + switch(mode) + { + case CM_RGBC_MODE_COLOR_CYCLE: + brightness = 0xDF; + color_1 = 0xFFFFFF; + color_2 = 0x000000; + break; + + case CM_RGBC_MODE_OFF: + brightness = 0x03; + break; + } + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_OP] = CM_RGBC_OPCODE_OP_WRITE; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_TYPE] = simplified ? CM_RGBC_OPCODE_TYPE_CONFIG_SIMPLIFIED : CM_RGBC_OPCODE_TYPE_CONFIG_FULL; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MODE] = mode; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_SPEED] = speed; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_BRIGHTNESS] = brightness; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_COLOR_1] = RGBGetRValue(color_1); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_COLOR_1 + 1] = RGBGetGValue(color_1); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_COLOR_1 + 2] = RGBGetBValue(color_1); + + /*---------------------------------------------*\ + | Magic values, meaning unknown | + \*---------------------------------------------*/ + buffer[REPORT_ID_OFFSET + 0x06] = (mode == CM_RGBC_MODE_BREATHING) ? 0x20 : 0x00; + buffer[REPORT_ID_OFFSET + 0x07] = (mode == CM_RGBC_MODE_STAR) ? 0x19 : 0xFF; + buffer[REPORT_ID_OFFSET + 0x08] = 0xFF; + + if(!simplified) + { + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MULTILAYER] = multilayer ? 0x01 : 0x00; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_COLOR_2] = RGBGetRValue(color_2); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_COLOR_2 + 1] = RGBGetGValue(color_2); + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_COLOR_2 + 2] = RGBGetBValue(color_2); + + for(int i = REPORT_ID_OFFSET + 16; i < CM_RGBC_PACKET_SIZE; i++) + { + buffer[i] = 0xFF; + } + } + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_RGBC_INTERRUPT_TIMEOUT); +} + +void CMRGBController::SendReadConfig(unsigned char mode) +{ + const unsigned char buffer_size = CM_RGBC_PACKET_SIZE; + unsigned char buffer[buffer_size] = { }; + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_OP] = CM_RGBC_OPCODE_OP_READ; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_TYPE] = CM_RGBC_OPCODE_TYPE_CONFIG_FULL; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_MODE] = mode; + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_RGBC_INTERRUPT_TIMEOUT); + + current_mode = mode; + current_speed = buffer[CM_RGBC_PACKET_OFFSET_SPEED]; + current_brightness = buffer[CM_RGBC_PACKET_OFFSET_BRIGHTNESS]; + + current_mode_color_1 = ToRGBColor( + buffer[CM_RGBC_PACKET_OFFSET_COLOR_1], + buffer[CM_RGBC_PACKET_OFFSET_COLOR_1 + 1], + buffer[CM_RGBC_PACKET_OFFSET_COLOR_1 + 2]); + current_mode_color_2 = ToRGBColor( + buffer[CM_RGBC_PACKET_OFFSET_COLOR_2], + buffer[CM_RGBC_PACKET_OFFSET_COLOR_2 + 1], + buffer[CM_RGBC_PACKET_OFFSET_COLOR_2 + 2]); +} + +void CMRGBController::SendCustomColorStart() +{ + const unsigned char buffer_size = CM_RGBC_PACKET_SIZE; + unsigned char buffer[buffer_size] = { }; + + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_OP] = CM_RGBC_OPCODE_OP_WRITE; + buffer[REPORT_ID_OFFSET + CM_RGBC_PACKET_OFFSET_TYPE] = CM_RGBC_OPCODE_TYPE_UNKNOWN_30; + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_RGBC_INTERRUPT_TIMEOUT); +} + +void CMRGBController::ReadCurrentMode() +{ + SendFlowControl(CM_RGBC_OPCODE_FLOW_01); + + SendReadMode(); +} + +void CMRGBController::ReadModeConfig(unsigned char mode) +{ + SendFlowControl(CM_RGBC_OPCODE_FLOW_00); + + SendReadConfig(mode); + + if(mode == CM_RGBC_MODE_MULTIPLE) + { + SendReadCustomColors(); + } +} + +void CMRGBController::SetMode(unsigned char mode, unsigned char speed, unsigned char brightness, RGBColor color_1, RGBColor color_2) +{ + SendFlowControl(CM_RGBC_OPCODE_FLOW_01); + + SendSetConfig(mode, speed, brightness, color_1, color_2, false); + + SendSetMode(mode); + + SendApply(); + + SendFlowControl(CM_RGBC_OPCODE_FLOW_00); +} + +void CMRGBController::SetLedsDirect(RGBColor color_1, RGBColor color_2, RGBColor color_3, RGBColor color_4) +{ + SendFlowControl(CM_RGBC_OPCODE_FLOW_80); + + SendCustomColorStart(); + + SendSetCustomColors(color_1, color_2, color_3, color_4); + + SendSetMode(CM_RGBC_MODE_MULTIPLE); + + SendCustomColorStart(); + + SendSetConfig(CM_RGBC_MODE_MULTIPLE, 0x00, 0xFF, color_1, 0x000000, false); + + SendApply(); + + SendFlowControl(CM_RGBC_OPCODE_FLOW_00); +} + +std::string CMRGBController::GetDeviceName() +{ + return(device_name); +} + +std::string CMRGBController::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string CMRGBController::GetLocation() +{ + return("HID: " + location); +} + +unsigned char CMRGBController::GetMode() +{ + return(current_mode); +} + +unsigned char CMRGBController::GetSpeed() +{ + return(current_speed); +} + +unsigned char CMRGBController::GetBrightness() +{ + return(current_brightness); +} + +RGBColor CMRGBController::GetModeColor(int color_number) +{ + switch(color_number) + { + case 0: + return(current_mode_color_1); + + case 1: + return(current_mode_color_2); + + default: + return(ToRGBColor(0, 0, 0)); + } +} + +RGBColor CMRGBController::GetPortColor(int port_number) +{ + switch(port_number) + { + case 0: + return(current_port1_color); + + case 1: + return(current_port2_color); + + case 2: + return(current_port3_color); + + case 3: + return(current_port4_color); + + default: + return(ToRGBColor(0, 0, 0)); + } +} + +CMRGBController::~CMRGBController() +{ + if(dev) + { + hid_close(dev); + } +} diff --git a/Controllers/CoolerMasterController/CMRGBController/CMRGBController.h b/Controllers/CoolerMasterController/CMRGBController/CMRGBController.h new file mode 100644 index 0000000..bf5ba9f --- /dev/null +++ b/Controllers/CoolerMasterController/CMRGBController/CMRGBController.h @@ -0,0 +1,152 @@ +/*---------------------------------------------------------*\ +| CMRGBController.h | +| | +| Driver for Cooler Master RGB controller | +| | +| Nic W (midgetspy) 13 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define CM_RGBC_NUM_LEDS 4 + +#define REPORT_ID_OFFSET 1 +#define CM_RGBC_PACKET_SIZE 64 + REPORT_ID_OFFSET //This needs to have one byte extra for the report ID thing +#define CM_RGBC_PACKET_OFFSET_OP 0x00 +#define CM_RGBC_PACKET_OFFSET_TYPE 0x01 +#define CM_RGBC_PACKET_OFFSET_MULTILAYER 0x02 +#define CM_RGBC_PACKET_OFFSET_MODE 0x04 +#define CM_RGBC_PACKET_OFFSET_SPEED 0x05 +#define CM_RGBC_PACKET_OFFSET_BRIGHTNESS 0x09 +#define CM_RGBC_PACKET_OFFSET_COLOR_1 0x0A +#define CM_RGBC_PACKET_OFFSET_COLOR_2 0x0D + +#define CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_1 0x04 +#define CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_2 0x07 +#define CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_3 0x0A +#define CM_RGBC_PACKET_OFFSET_MULTIPLE_COLOR_4 0x0D + +#define CM_RGBC_INTERRUPT_TIMEOUT 250 + +#define CM_RGBC_SPEED_NONE 0x05 +#define CM_RGBC_BRIGHTNESS_OFF 0x03 +#define HID_MAX_STR 255 + +/*-------------------------------------------------*\ +| OP OPCODES | +\*-------------------------------------------------*/ +enum +{ + CM_RGBC_OPCODE_OP_FLOW_CONTROL = 0x41, + CM_RGBC_OPCODE_OP_UNKNOWN_50 = 0x50, + CM_RGBC_OPCODE_OP_WRITE = 0x51, + CM_RGBC_OPCODE_OP_READ = 0x52, +}; + +/*-------------------------------------------------*\ +| CONTROL FLOW OPCODES | +\*-------------------------------------------------*/ +enum +{ + CM_RGBC_OPCODE_FLOW_00 = 0x00, + CM_RGBC_OPCODE_FLOW_01 = 0x01, + CM_RGBC_OPCODE_FLOW_80 = 0x80, +}; + +/*-------------------------------------------------*\ +| OP TYPE OPCODES | +\*-------------------------------------------------*/ +enum +{ + CM_RGBC_OPCODE_TYPE_MODE = 0x28, + CM_RGBC_OPCODE_TYPE_CONFIG_SIMPLIFIED = 0x2B, + CM_RGBC_OPCODE_TYPE_CONFIG_FULL = 0x2C, + CM_RGBC_OPCODE_TYPE_UNKNOWN_30 = 0x30, + CM_RGBC_OPCODE_TYPE_UNKNOWN_55 = 0x55, + CM_RGBC_OPCODE_TYPE_LED_INFO = 0xA8, +}; + +/*-------------------------------------------------*\ +| MODES | +\*-------------------------------------------------*/ +enum +{ + CM_RGBC_MODE_STATIC = 0x00, + CM_RGBC_MODE_BREATHING = 0x01, + CM_RGBC_MODE_COLOR_CYCLE = 0x02, + CM_RGBC_MODE_STAR = 0x03, + CM_RGBC_MODE_MULTIPLE = 0x04, + CM_RGBC_MODE_MULTILAYER = 0xE0, + CM_RGBC_MODE_OFF = 0xFE, +}; + +/*-------------------------------------------------*\ +| SPEED | +\*-------------------------------------------------*/ +enum +{ + CM_RGBC_SPEED_BREATHING_SLOWEST = 0x3C, + CM_RGBC_SPEED_BREATHING_FASTEST = 0x26, + CM_RGBC_SPEED_COLOR_CYCLE_SLOWEST = 0x96, + CM_RGBC_SPEED_COLOR_CYCLE_FASTEST = 0x68, + CM_RGBC_SPEED_STAR_SLOWEST = 0x46, + CM_RGBC_SPEED_STAR_FASTEST = 0x32, +}; + +class CMRGBController +{ +public: + CMRGBController(hid_device* dev_handle, char* path); + ~CMRGBController(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + unsigned char GetMode(); + unsigned char GetSpeed(); + unsigned char GetBrightness(); + RGBColor GetModeColor(int color_number); + RGBColor GetPortColor(int port_number); + + void SetMode(unsigned char mode, unsigned char speed, unsigned char brightness, RGBColor color_1, RGBColor color_2); + void SetLedsDirect(RGBColor color_1, RGBColor color_2, RGBColor color_3, RGBColor color_4); + + void ReadCurrentMode(); + void ReadModeConfig(unsigned char mode); + +private: + std::string device_name; + std::string location; + hid_device* dev; + + unsigned char current_mode; + unsigned char current_speed; + unsigned char current_brightness; + RGBColor current_mode_color_1; + RGBColor current_mode_color_2; + RGBColor current_port1_color; + RGBColor current_port2_color; + RGBColor current_port3_color; + RGBColor current_port4_color; + + void SendFlowControl(unsigned char byte_flag); + void SendApply(); + void SendCustomColorStart(); + + void SendReadMode(); + void SendSetMode(unsigned char mode); + + void SendReadCustomColors(); + void SendSetCustomColors(RGBColor color_1, RGBColor color_2, RGBColor color_3, RGBColor color_4); + + void SendReadConfig(unsigned char mode); + void SendSetConfig(unsigned char mode, unsigned char speed, unsigned char brightness, RGBColor color_1, RGBColor color_2, bool simplified, bool multilayer); +}; diff --git a/Controllers/CoolerMasterController/CMRGBController/RGBController_CMRGBController.cpp b/Controllers/CoolerMasterController/CMRGBController/RGBController_CMRGBController.cpp new file mode 100644 index 0000000..db7924d --- /dev/null +++ b/Controllers/CoolerMasterController/CMRGBController/RGBController_CMRGBController.cpp @@ -0,0 +1,274 @@ +/*---------------------------------------------------------*\ +| RGBController_CMRGBController.cpp | +| | +| RGBController for Cooler Master RGB controller | +| | +| Nic W (midgetspy) 13 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CMRGBController.h" + +/*-----------------------------------------------------------------------------------------------------------------------------------------*\ +| This controller has 4 ports, each for a 12v non-addressable LED item. | +| | +| It supports the following modes: | +| Static: All 4 ports a single color. Has brightness option. | +| Breathing: All ports a single color, fading in and out. Has brightness and speed option. | +| Star: Some weird effect using all 4 ports and a single color. Has brightness and speed option. | +| Color Cycle: All ports cycle through the rainbow in unison. Has brightness and speed option. | +| Off: All 4 ports off | +| | +| Plus some "special" modes: | +| Multilayer: Each of the 4 ports can have any of the modes above applied individually | +| Multiple Color/Customize: Each port can be set to its own static color | +| Mirage: A strobe effect that varies the LED pulse frequency which affects any of the above modes | +| | +| Note: | +| Multiple Color/Customize is equivalent to Multilayer + Static, but the device supports both separately | +| Static is equivalent to Multiple Color/Customize with the same color on each port, but the device supports both separately | +| | +| It can be controlled with 2 different pieces of software: MasterPlus+ or "RGB LED Controller". They appear to use different protocols. | +| | +| RGB LED Controller: | +| Sets changes temporarily and then applies them or cancels the changes separately | +| Supports all modes above | +| Has 3 brightness increments | +| Has two different colors for the Star effect (Star/Sky) | +| | +| MasterPlus+: | +| Sets changes permanently as soon as you change anything in the UI | +| Doesn't support Multilayer or Mirage | +| Has 5 brightness increments | +| Single color for Star | +\*-----------------------------------------------------------------------------------------------------------------------------------------*/ + +/**------------------------------------------------------------------*\ + @name Coolermaster RGB + @category LEDStrip + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectCoolerMasterRGB + @comment This is a 12V analogue RGB controller only. +\*-------------------------------------------------------------------*/ + +RGBController_CMRGBController::RGBController_CMRGBController(CMRGBController* controller_ptr) +{ + controller = controller_ptr; + + name = "Cooler Master RGB Controller"; + vendor = "Cooler Master"; + type = DEVICE_TYPE_LEDSTRIP; + description = controller->GetDeviceName(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Static; + Static.name = "Static"; + Static.value = CM_RGBC_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness_min = 0x00; + Static.brightness_max = 0xFF; + Static.brightness = 0xFF; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_RGBC_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(Breathing.colors_max); + Breathing.speed_min = CM_RGBC_SPEED_BREATHING_SLOWEST; + Breathing.speed_max = CM_RGBC_SPEED_BREATHING_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = CM_RGBC_SPEED_BREATHING_SLOWEST; + Breathing.brightness_min = 0x00; + Breathing.brightness_max = 0xFF; + Breathing.brightness = 0xFF; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Spectrum Cycle"; + ColorCycle.value = CM_RGBC_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR; + ColorCycle.speed_min = CM_RGBC_SPEED_COLOR_CYCLE_SLOWEST; + ColorCycle.speed_max = CM_RGBC_SPEED_COLOR_CYCLE_FASTEST; + ColorCycle.color_mode = MODE_COLORS_RANDOM; + ColorCycle.speed = CM_RGBC_SPEED_COLOR_CYCLE_SLOWEST; + ColorCycle.brightness_min = 0x00; + ColorCycle.brightness_max = 0xFF; + ColorCycle.brightness = 0xFF; + modes.push_back(ColorCycle); + + mode Star; + Star.name = "Star"; + Star.value = CM_RGBC_MODE_STAR; + Star.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Star.colors_min = 2; + Star.colors_max = 2; + Star.colors.resize(Star.colors_max); + Star.speed_min = CM_RGBC_SPEED_STAR_SLOWEST; + Star.speed_max = CM_RGBC_SPEED_STAR_FASTEST; + Star.color_mode = MODE_COLORS_MODE_SPECIFIC; + Star.speed = CM_RGBC_SPEED_STAR_SLOWEST; + Star.brightness_min = 0x00; + Star.brightness_max = 0xFF; + Star.brightness = 0xFF; + modes.push_back(Star); + + mode Multiple; + Multiple.name = "Custom"; + Multiple.value = CM_RGBC_MODE_MULTIPLE; + Multiple.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Multiple.colors_min = 1; + Multiple.colors_max = 1; + Multiple.colors.resize(Multiple.colors_max); + Multiple.color_mode = MODE_COLORS_PER_LED; + Multiple.speed = 0; + Multiple.brightness_min = 0x00; + Multiple.brightness_max = 0xFF; + Multiple.brightness = 0xFF; + modes.push_back(Multiple); + + mode Off; + Off.name = "Off"; + Off.value = CM_RGBC_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + Off.flags = 0; + modes.push_back(Off); + + SetupZones(); + + ReadAllModeConfigsFromDevice(); +} + +RGBController_CMRGBController::~RGBController_CMRGBController() +{ + delete controller; +} + +void RGBController_CMRGBController::ReadAllModeConfigsFromDevice() +{ + int device_mode = controller->GetMode(); + + for(int mode_idx = 0; mode_idx < (int)modes.size(); mode_idx++) + { + if(device_mode == modes[mode_idx].value) + { + active_mode = mode_idx; + continue; + } + + if(!modes[mode_idx].flags) + { + continue; + } + + controller->ReadModeConfig(modes[mode_idx].value); + LoadConfigFromDeviceController(mode_idx); + } + + /*---------------------------------------------------------*\ + | Do the active mode last so the device controller state | + | is left with the active mode's config | + \*---------------------------------------------------------*/ + if(active_mode != -1) + { + controller->ReadModeConfig(modes[active_mode].value); + LoadConfigFromDeviceController(active_mode); + } +} + +void RGBController_CMRGBController::LoadConfigFromDeviceController(int mode_idx) +{ + for(int color_idx = 0; color_idx < (int)modes[mode_idx].colors.size(); color_idx++) + { + modes[mode_idx].colors[0] = controller->GetModeColor(color_idx); + } + + if(modes[mode_idx].flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + for(int led_idx = 0; led_idx < (int)leds.size(); led_idx++) + { + SetLED(led_idx, controller->GetPortColor(led_idx)); + } + } + + if(modes[mode_idx].flags & MODE_FLAG_HAS_SPEED) + { + modes[mode_idx].speed = controller->GetSpeed(); + } + + if(modes[mode_idx].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + modes[active_mode].brightness = controller->GetBrightness(); + } +} + + +void RGBController_CMRGBController::SetupZones() +{ + leds.clear(); + zones.clear(); + colors.clear(); + + /*-----------------------------------------------------*\ + | One zone, 4 leds. This might not actually work with | + | the Multilayer mode, but we'll deal with that later | + \*-----------------------------------------------------*/ + zone* new_zone = new zone(); + new_zone->name = "Controller"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 4; + new_zone->leds_count = 4; + new_zone->matrix_map = NULL; + + for(int i = 1; i <= CM_RGBC_NUM_LEDS; i++) + { + led* new_led = new led(); + new_led->name = "LED " + std::to_string(i); + leds.push_back(*new_led); + } + + zones.push_back(*new_zone); + SetupColors(); +} + +void RGBController_CMRGBController::ResizeZone(int /*zone*/, int /*new_size*/) +{ +} + +void RGBController_CMRGBController::DeviceUpdateLEDs() +{ + for(int zone_idx = 0; zone_idx < (int)zones.size(); zone_idx++) + { + UpdateZoneLEDs(zone_idx); + } +} + +void RGBController_CMRGBController::UpdateZoneLEDs(int zone) +{ + controller->SetLedsDirect(zones[zone].colors[0], zones[zone].colors[1], zones[zone].colors[2], zones[zone].colors[3]); +} + +void RGBController_CMRGBController::UpdateSingleLED(int /*led*/) +{ +} + +void RGBController_CMRGBController::DeviceUpdateMode() +{ + RGBColor color_1 = (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) ? modes[active_mode].colors[0] : 0; + RGBColor color_2 = (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC && modes[active_mode].colors.size() > 1) ? modes[active_mode].colors[1] : 0; + + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, color_1, color_2); +} diff --git a/Controllers/CoolerMasterController/CMRGBController/RGBController_CMRGBController.h b/Controllers/CoolerMasterController/CMRGBController/RGBController_CMRGBController.h new file mode 100644 index 0000000..bb13c41 --- /dev/null +++ b/Controllers/CoolerMasterController/CMRGBController/RGBController_CMRGBController.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_CMRGBController.h | +| | +| RGBController for Cooler Master RGB controller | +| | +| Nic W (midgetspy) 13 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "CMRGBController.h" + +class RGBController_CMRGBController : public RGBController +{ +public: + RGBController_CMRGBController(CMRGBController* controller_ptr); + ~RGBController_CMRGBController(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CMRGBController* controller; + void LoadConfigFromDeviceController(int device_mode); + void ReadAllModeConfigsFromDevice(); +}; diff --git a/Controllers/CoolerMasterController/CMSmallARGBController/CMSmallARGBController.cpp b/Controllers/CoolerMasterController/CMSmallARGBController/CMSmallARGBController.cpp new file mode 100644 index 0000000..1172b92 --- /dev/null +++ b/Controllers/CoolerMasterController/CMSmallARGBController/CMSmallARGBController.cpp @@ -0,0 +1,239 @@ +/*---------------------------------------------------------*\ +| CMSmallARGBController.cpp | +| | +| Driver for Cooler Master Small ARGB controller | +| | +| Chris M (Dr_No) 31 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CMSmallARGBController.h" +#include "StringUtils.h" + +cm_small_argb_headers cm_small_argb_header_data[1] = +{ + { "CM Small ARGB", 0x01, true, 12 } +}; + +CMSmallARGBController::CMSmallARGBController(hid_device* dev_handle, char *_path, unsigned char _zone_idx) +{ + dev = dev_handle; + location = _path; + zone_index = _zone_idx; + current_speed = CM_SMALL_ARGB_SPEED_NORMAL; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + + GetStatus(); +} + +CMSmallARGBController::~CMSmallARGBController() +{ + if(dev) + { + hid_close(dev); + } +} + +void CMSmallARGBController::GetStatus() +{ + unsigned char buffer[CM_SMALL_ARGB_PACKET_SIZE] = { 0x00, 0x80, 0x01, 0x01 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + int header = zone_index - 1; + + buffer[CM_SMALL_ARGB_ZONE_BYTE] = header; + buffer[CM_SMALL_ARGB_MODE_BYTE] = 0x01; + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_SMALL_ARGB_INTERRUPT_TIMEOUT); + + memset(buffer, 0x00, buffer_size ); + + buffer[CM_SMALL_ARGB_COMMAND_BYTE] = 0x0B; + buffer[CM_SMALL_ARGB_FUNCTION_BYTE] = 0x01; + buffer[CM_SMALL_ARGB_ZONE_BYTE] = 0x01; + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_SMALL_ARGB_INTERRUPT_TIMEOUT); + + current_mode = buffer[4]; + current_speed = buffer[5]; + bool_random = buffer[6] == 0x00; + current_brightness = buffer[7]; + current_red = buffer[8]; + current_green = buffer[9]; + current_blue = buffer[10]; +} + +std::string CMSmallARGBController::GetDeviceName() +{ + return(device_name); +} + +std::string CMSmallARGBController::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string CMSmallARGBController::GetLocation() +{ + return("HID: " + location); +} + +unsigned char CMSmallARGBController::GetZoneIndex() +{ + return(zone_index); +} + +unsigned char CMSmallARGBController::GetMode() +{ + return(current_mode); +} + +unsigned char CMSmallARGBController::GetLedRed() +{ + return(current_red); +} + +unsigned char CMSmallARGBController::GetLedGreen() +{ + return(current_green); +} + +unsigned char CMSmallARGBController::GetLedBlue() +{ + return(current_blue); +} + +unsigned char CMSmallARGBController::GetLedSpeed() +{ + return(current_speed); +} + +bool CMSmallARGBController::GetRandomColours() +{ + return(bool_random); +} + +void CMSmallARGBController::SetLedCount(int zone, int led_count) +{ + unsigned char buffer[CM_SMALL_ARGB_PACKET_SIZE] = { 0x00, 0x80, 0x0D, 0x02 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + + buffer[CM_SMALL_ARGB_ZONE_BYTE] = zone; + buffer[CM_SMALL_ARGB_MODE_BYTE] = (0x0F - led_count > 0) ? 0x0F - led_count : 0x01; + buffer[CM_SMALL_ARGB_SPEED_BYTE] = led_count; + + hid_write(dev, buffer, buffer_size); +} + +void CMSmallARGBController::SetMode(unsigned char mode, unsigned char speed, unsigned char brightness, RGBColor colour, bool random_colours) +{ + current_mode = mode; + current_speed = speed; + current_brightness = brightness; + current_red = RGBGetRValue(colour); + current_green = RGBGetGValue(colour); + current_blue = RGBGetBValue(colour); + bool_random = random_colours; + + SendUpdate(); +} + +void CMSmallARGBController::SetLedsDirect(RGBColor* led_colours, unsigned int led_count) +{ + const unsigned char buffer_size = CM_SMALL_ARGB_PACKET_SIZE; + unsigned char buffer[buffer_size] = { 0x00, 0x00, 0x10, 0x02 }; + unsigned char packet_count = 0; + std::vector colours; + + /*---------------------------------------------*\ + | Set up the RGB triplets to send | + \*---------------------------------------------*/ + for(unsigned int i = 0; i < led_count; i++) + { + RGBColor colour = led_colours[i]; + + colours.push_back( RGBGetRValue(colour) ); + colours.push_back( RGBGetGValue(colour) ); + colours.push_back( RGBGetBValue(colour) ); + } + + buffer[CM_SMALL_ARGB_ZONE_BYTE] = zone_index - 1; //argb_header_data[zone_index].header; + buffer[CM_SMALL_ARGB_MODE_BYTE] = led_count; + unsigned char buffer_idx = CM_SMALL_ARGB_MODE_BYTE + 1; + + for(std::vector::iterator it = colours.begin(); it != colours.end(); buffer_idx = CM_SMALL_ARGB_COMMAND_BYTE) + { + /*-----------------------------------------------------------------*\ + | Fill the write buffer till its full or the colour buffer is empty | + \*-----------------------------------------------------------------*/ + buffer[CM_SMALL_ARGB_REPORT_BYTE] = packet_count; + while (( buffer_idx < buffer_size) && ( it != colours.end() )) + { + buffer[buffer_idx] = *it; + buffer_idx++; + it++; + } + + if(it == colours.end()) + { + buffer[CM_SMALL_ARGB_REPORT_BYTE] += 0x80; + } + + hid_write(dev, buffer, buffer_size); + + /*-----------------------------------------------------------------*\ + | Reset the write buffer | + \*-----------------------------------------------------------------*/ + memset(buffer, 0x00, buffer_size ); + packet_count++; + } +} + +void CMSmallARGBController::SendUpdate() +{ + unsigned char buffer[CM_SMALL_ARGB_PACKET_SIZE] = { 0x00 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + bool boolPassthru = ( current_mode == CM_SMALL_ARGB_MODE_PASSTHRU ); + bool boolDirect = ( current_mode == CM_SMALL_ARGB_MODE_DIRECT ); + unsigned char function = boolPassthru ? 0x02 : 0x01; + buffer[CM_SMALL_ARGB_REPORT_BYTE] = 0x80; + buffer[CM_SMALL_ARGB_COMMAND_BYTE] = boolDirect ? 0x10 : 0x01; + buffer[CM_SMALL_ARGB_FUNCTION_BYTE] = boolDirect ? 0x01 : function; + buffer[CM_SMALL_ARGB_MODE_BYTE] = boolPassthru ? 0x00 : 0x02; + + hid_write(dev, buffer, buffer_size); + + buffer[CM_SMALL_ARGB_COMMAND_BYTE] = 0x0b; + buffer[CM_SMALL_ARGB_FUNCTION_BYTE] = (false) ? 0x01 : 0x02; //This controls custom mode TODO + buffer[CM_SMALL_ARGB_ZONE_BYTE] = cm_small_argb_header_data[zone_index].header; + buffer[CM_SMALL_ARGB_MODE_BYTE] = current_mode; + buffer[CM_SMALL_ARGB_SPEED_BYTE] = current_speed; + buffer[CM_SMALL_ARGB_COLOUR_INDEX_BYTE] = (bool_random) ? 0x00 : 0x10; //This looks to still be the colour index and controls random colours + buffer[CM_SMALL_ARGB_BRIGHTNESS_BYTE] = current_brightness; + buffer[CM_SMALL_ARGB_RED_BYTE] = current_red; + buffer[CM_SMALL_ARGB_GREEN_BYTE] = current_green; + buffer[CM_SMALL_ARGB_BLUE_BYTE] = current_blue; + + hid_write(dev, buffer, buffer_size); + hid_read_timeout(dev, buffer, buffer_size, CM_SMALL_ARGB_INTERRUPT_TIMEOUT); +} diff --git a/Controllers/CoolerMasterController/CMSmallARGBController/CMSmallARGBController.h b/Controllers/CoolerMasterController/CMSmallARGBController/CMSmallARGBController.h new file mode 100644 index 0000000..eaf41cb --- /dev/null +++ b/Controllers/CoolerMasterController/CMSmallARGBController/CMSmallARGBController.h @@ -0,0 +1,114 @@ +/*---------------------------------------------------------*\ +| CMSmallARGBController.h | +| | +| Driver for Cooler Master Small ARGB controller | +| | +| Chris M (Dr_No) 31 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" //Needed to set the direct mode + +/*---------------------------------------------------------*\ +| Simple RGB device with 5 modes | +\*---------------------------------------------------------*/ + +#define CM_SMALL_ARGB_PACKET_SIZE 65 +#define CM_SMALL_ARGB_INTERRUPT_TIMEOUT 250 +#define HID_MAX_STR 255 + +enum +{ + CM_SMALL_ARGB_REPORT_BYTE = 1, + CM_SMALL_ARGB_COMMAND_BYTE = 2, + CM_SMALL_ARGB_FUNCTION_BYTE = 3, + CM_SMALL_ARGB_ZONE_BYTE = 4, + CM_SMALL_ARGB_MODE_BYTE = 5, + CM_SMALL_ARGB_SPEED_BYTE = 6, + CM_SMALL_ARGB_COLOUR_INDEX_BYTE = 7, //Not used on the small controller + CM_SMALL_ARGB_BRIGHTNESS_BYTE = 8, //0x00 thru 0xFF + CM_SMALL_ARGB_RED_BYTE = 9, + CM_SMALL_ARGB_GREEN_BYTE = 10, + CM_SMALL_ARGB_BLUE_BYTE = 11, +}; + +struct cm_small_argb_headers +{ + const char* name; + unsigned char header; + bool digital; + unsigned int count; +}; + +extern cm_small_argb_headers cm_small_argb_header_data[1]; + +enum +{ + CM_SMALL_ARGB_MODE_SPECTRUM = 0x01, //Spectrum Mode + CM_SMALL_ARGB_MODE_RELOAD = 0x02, //Reload Mode + CM_SMALL_ARGB_MODE_RECOIL = 0x03, //Recoil Mode + CM_SMALL_ARGB_MODE_BREATHING = 0x04, //Breathing Mode + CM_SMALL_ARGB_MODE_REFILL = 0x05, //Refill Mode + CM_SMALL_ARGB_MODE_DEMO = 0x06, //Demo Mode + CM_SMALL_ARGB_MODE_OFF = 0x09, //Turn off + CM_SMALL_ARGB_MODE_DIRECT = 0xFE, //Direct Led Control (possibly N?A for small controller) + CM_SMALL_ARGB_MODE_PASSTHRU = 0xFF //Motherboard Pass Thru Mode +}; + +enum +{ + CM_SMALL_ARGB_SPEED_SLOWEST = 0x00, // Slowest speed + CM_SMALL_ARGB_SPEED_SLOW = 0x01, // Slower speed + CM_SMALL_ARGB_SPEED_NORMAL = 0x02, // Normal speed + CM_SMALL_ARGB_SPEED_FAST = 0x03, // Fast speed + CM_SMALL_ARGB_SPEED_FASTEST = 0x04, // Fastest speed +}; + +class CMSmallARGBController +{ +public: + CMSmallARGBController(hid_device* dev_handle, char *_path, unsigned char _zone_idx); + ~CMSmallARGBController(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + unsigned char GetZoneIndex(); + unsigned char GetMode(); + unsigned char GetLedRed(); + unsigned char GetLedGreen(); + unsigned char GetLedBlue(); + unsigned char GetLedSpeed(); + bool GetRandomColours(); + + void SetLedCount(int zone, int led_count); + void SetMode(unsigned char mode, unsigned char speed, unsigned char brightness, RGBColor colour, bool random_colours); + void SetLedsDirect(RGBColor * led_colours, unsigned int led_count); +private: + std::string device_name; + std::string location; + hid_device* dev; + + unsigned char zone_index; + unsigned char current_mode; + unsigned char current_speed; + + unsigned char current_red; + unsigned char current_green; + unsigned char current_blue; + unsigned char current_brightness; + bool bool_random; + + unsigned int GetLargestColour(unsigned int red, unsigned int green, unsigned int blue); + unsigned char GetColourIndex(unsigned char red, unsigned char green, unsigned char blue); + void GetStatus(); + void SendUpdate(); +}; diff --git a/Controllers/CoolerMasterController/CMSmallARGBController/RGBController_CMSmallARGBController.cpp b/Controllers/CoolerMasterController/CMSmallARGBController/RGBController_CMSmallARGBController.cpp new file mode 100644 index 0000000..a4729ca --- /dev/null +++ b/Controllers/CoolerMasterController/CMSmallARGBController/RGBController_CMSmallARGBController.cpp @@ -0,0 +1,305 @@ +/*---------------------------------------------------------*\ +| RGBController_CMSmallARGBController.cpp | +| | +| RGBController for Cooler Master Small ARGB controller | +| | +| Chris M (Dr_No) 31 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CMSmallARGBController.h" + +/**------------------------------------------------------------------*\ + @name Coolermaster Small ARGB + @category LEDStrip + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCoolerMasterSmallARGB + @comment The Coolermaster Small ARGB device supports `Direct` mode + from firmware 0012 onwards. Check the serial number for the date + "A202104052336" or newer. +\*-------------------------------------------------------------------*/ + +RGBController_CMSmallARGBController::RGBController_CMSmallARGBController(CMSmallARGBController* controller_ptr) +{ + controller = controller_ptr; + unsigned char speed = controller->GetLedSpeed(); + + name = cm_small_argb_header_data[controller->GetZoneIndex()].name; + vendor = "Cooler Master"; + type = DEVICE_TYPE_LEDSTRIP; + description = controller->GetDeviceName(); + version = "2.0 for FW0012"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + + if(serial >= CM_SMALL_ARGB_FW0012) + { + mode Direct; + Direct.name = "Direct"; + Direct.value = CM_SMALL_ARGB_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = 0; + Direct.brightness_max = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Direct.brightness = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + } + + mode Off; + Off.name = "Turn Off"; + Off.value = CM_SMALL_ARGB_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Reload; + Reload.name = "Reload"; + Reload.value = CM_SMALL_ARGB_MODE_RELOAD; + Reload.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Reload.colors_min = 1; + Reload.colors_max = 1; + Reload.colors.resize(Reload.colors_max); + Reload.brightness_min = 0; + Reload.brightness_max = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Reload.brightness = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Reload.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST; + Reload.speed_max = CM_SMALL_ARGB_SPEED_FASTEST; + Reload.color_mode = MODE_COLORS_RANDOM; + Reload.speed = speed; + modes.push_back(Reload); + + mode Recoil; + Recoil.name = "Recoil"; + Recoil.value = CM_SMALL_ARGB_MODE_RECOIL; + Recoil.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Recoil.colors_min = 1; + Recoil.colors_max = 1; + Recoil.colors.resize(Recoil.colors_max); + Recoil.brightness_min = 0; + Recoil.brightness_max = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Recoil.brightness = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Recoil.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST; + Recoil.speed_max = CM_SMALL_ARGB_SPEED_FASTEST; + Recoil.color_mode = MODE_COLORS_RANDOM; + Recoil.speed = speed; + modes.push_back(Recoil); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CM_SMALL_ARGB_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(Breathing.colors_max); + Breathing.brightness_min = 0; + Breathing.brightness_max = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Breathing.brightness = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Breathing.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST; + Breathing.speed_max = CM_SMALL_ARGB_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_RANDOM; + Breathing.speed = speed; + modes.push_back(Breathing); + + mode Refill; + Refill.name = "Refill"; + Refill.value = CM_SMALL_ARGB_MODE_REFILL; + Refill.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Refill.colors_min = 1; + Refill.colors_max = 1; + Refill.colors.resize(Refill.colors_max); + Refill.brightness_min = 0; + Refill.brightness_max = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Refill.brightness = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Refill.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST; + Refill.speed_max = CM_SMALL_ARGB_SPEED_FASTEST; + Refill.color_mode = MODE_COLORS_RANDOM; + Refill.speed = speed; + modes.push_back(Refill); + + mode Demo; + Demo.name = "Demo"; + Demo.value = CM_SMALL_ARGB_MODE_DEMO; + Demo.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Demo.brightness_min = 0; + Demo.brightness_max = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Demo.brightness = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Demo.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST; + Demo.speed_max = CM_SMALL_ARGB_SPEED_FASTEST; + Demo.color_mode = MODE_COLORS_NONE; + Demo.speed = speed; + modes.push_back(Demo); + + mode Spectrum; + Spectrum.name = "Spectrum"; + Spectrum.value = CM_SMALL_ARGB_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Spectrum.brightness_min = 0; + Spectrum.brightness_max = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Spectrum.brightness = CM_SMALL_ARGB_BRIGHTNESS_MAX; + Spectrum.speed_min = CM_SMALL_ARGB_SPEED_SLOWEST; + Spectrum.speed_max = CM_SMALL_ARGB_SPEED_FASTEST; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.speed = speed; + modes.push_back(Spectrum); + + mode PassThru; + PassThru.name = "Pass Thru"; + PassThru.value = CM_SMALL_ARGB_MODE_PASSTHRU; + PassThru.color_mode = MODE_COLORS_NONE; + modes.push_back(PassThru); + + Init_Controller(); //Only processed on first run + SetupZones(); + + int temp_mode = controller->GetMode(); + + for(int mode_idx = 0; mode_idx < (int)modes.size() ; mode_idx++) + { + if(temp_mode == modes[mode_idx].value) + { + active_mode = mode_idx; + break; + } + } + + if (modes[active_mode].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + modes[active_mode].colors[0] = ToRGBColor(controller->GetLedRed(), controller->GetLedGreen(), controller->GetLedBlue()); + } + + modes[active_mode].color_mode = (controller->GetRandomColours()) ? MODE_COLORS_RANDOM : MODE_COLORS_MODE_SPECIFIC; + + if (modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + modes[active_mode].speed = controller->GetLedSpeed(); + } +} + +RGBController_CMSmallARGBController::~RGBController_CMSmallARGBController() +{ + delete controller; +} + +void RGBController_CMSmallARGBController::Init_Controller() +{ + int zone_idx = controller->GetZoneIndex(); + int zone_led_count = cm_small_argb_header_data[zone_idx].count; + bool boolSingleLED = ( zone_led_count == 1 ); //If argb_header_data[zone_idx].count == 1 then the zone is ZONE_TYPE_SINGLE + + zone ARGB_zone; + ARGB_zone.name = std::to_string(zone_idx); + ARGB_zone.type = (boolSingleLED) ? ZONE_TYPE_SINGLE : ZONE_TYPE_LINEAR; + ARGB_zone.leds_min = CM_SMALL_ARGB_MIN_LEDS; + ARGB_zone.leds_max = CM_SMALL_ARGB_MAX_LEDS; + ARGB_zone.leds_count = zone_led_count; + ARGB_zone.matrix_map = NULL; + zones.push_back(ARGB_zone); +} + +void RGBController_CMSmallARGBController::SetupZones() +{ + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + bool boolSingleLED = (zones[zone_idx].type == ZONE_TYPE_SINGLE); //Calculated for later use + + if (!boolSingleLED) + { + controller->SetLedCount(cm_small_argb_header_data[zone_idx].header, zones[zone_idx].leds_count); + } + + for(unsigned int lp_idx = 0; lp_idx < zones[zone_idx].leds_count; lp_idx++) + { + led new_led; + unsigned int i = std::stoi(zones[zone_idx].name); + + if(boolSingleLED) + { + new_led.name = i; + new_led.value = cm_small_argb_header_data[i].header; + } + else + { + new_led.name = i; + new_led.name.append(" LED " + std::to_string(lp_idx)); + new_led.value = cm_small_argb_header_data[i].header; + } + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_CMSmallARGBController::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_CMSmallARGBController::DeviceUpdateLEDs() +{ + for(int zone_idx = 0; zone_idx < (int)zones.size(); zone_idx++) + { + UpdateZoneLEDs(zone_idx); + } +} + +void RGBController_CMSmallARGBController::UpdateZoneLEDs(int zone) +{ + if(serial >= CM_SMALL_ARGB_FW0012) + { + controller->SetLedsDirect( zones[zone].colors, zones[zone].leds_count ); + } +} + +void RGBController_CMSmallARGBController::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_CMSmallARGBController::SetCustomMode() +{ + /*-------------------------------------------------*\ + | The small ARGB may not support "Direct" mode | + | in which case this will select "Pass Thru" | + \*-------------------------------------------------*/ + if(serial >= CM_SMALL_ARGB_FW0012) + { + active_mode = 0; + } + else + { + active_mode = 7; + } +} + +void RGBController_CMSmallARGBController::DeviceUpdateMode() +{ + bool random_colours = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + RGBColor colour = (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) ? modes[active_mode].colors[0] : 0; + + controller->SetMode( modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, colour, random_colours); +} diff --git a/Controllers/CoolerMasterController/CMSmallARGBController/RGBController_CMSmallARGBController.h b/Controllers/CoolerMasterController/CMSmallARGBController/RGBController_CMSmallARGBController.h new file mode 100644 index 0000000..61faf48 --- /dev/null +++ b/Controllers/CoolerMasterController/CMSmallARGBController/RGBController_CMSmallARGBController.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_CMSmallARGBController.h | +| | +| RGBController for Cooler Master Small ARGB controller | +| | +| Chris M (Dr_No) 31 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "CMSmallARGBController.h" + +#define CM_SMALL_ARGB_MIN_LEDS 4 +#define CM_SMALL_ARGB_MAX_LEDS 48 +#define CM_SMALL_ARGB_BRIGHTNESS_MAX 0xFF +#define CM_SMALL_ARGB_FW0012 "A202104052336" + +class RGBController_CMSmallARGBController : public RGBController +{ +public: + RGBController_CMSmallARGBController(CMSmallARGBController* controller_ptr); + ~RGBController_CMSmallARGBController(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void SetCustomMode(); + void DeviceUpdateMode(); +private: + void Init_Controller(); + int GetDeviceMode(); + + CMSmallARGBController* controller; +}; diff --git a/Controllers/CoolerMasterController/CoolerMasterControllerDetect.cpp b/Controllers/CoolerMasterController/CoolerMasterControllerDetect.cpp new file mode 100644 index 0000000..d6132df --- /dev/null +++ b/Controllers/CoolerMasterController/CoolerMasterControllerDetect.cpp @@ -0,0 +1,382 @@ +/*---------------------------------------------------------*\ +| CoolerMasterControllerDetect.cpp | +| | +| Detector for Cooler Master devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +/*-----------------------------------------------------*\ +| OpenRGB includes | +\*-----------------------------------------------------*/ +#include +#include "Detector.h" +#include "LogManager.h" + +/*-----------------------------------------------------*\ +| Coolermaster specific includes | +\*-----------------------------------------------------*/ +#include "RGBController_CMMMController.h" +#include "RGBController_CMMM711Controller.h" +#include "RGBController_CMMM712Controller.h" +#include "RGBController_CMMP750Controller.h" +#include "RGBController_CMARGBController.h" +#include "RGBController_CMSmallARGBController.h" +#include "RGBController_CMARGBGen2A1Controller.h" +#include "RGBController_CMRGBController.h" +#include "RGBController_CMR6000Controller.h" +#include "RGBController_CMMonitorController.h" +#include "RGBController_CMGD160Controller.h" +#include "RGBController_CMKeyboardController.h" + +/*-----------------------------------------------------*\ +| Coolermaster USB vendor ID | +\*-----------------------------------------------------*/ +#define COOLERMASTER_VID 0x2516 + +/*-----------------------------------------------------*\ +| Coolermaster Keyboards | +| PIDs defined in `CMMKControllerV2.h` | +\*-----------------------------------------------------*/ + +/*-----------------------------------------------------*\ +| Coolermaster GPUs | +| PIDs defined in `CMR6000Controller.h` | +\*-----------------------------------------------------*/ + +/*-----------------------------------------------------*\ +| Coolermaster LEDstrip controllers | +\*-----------------------------------------------------*/ +#define COOLERMASTER_ARGB_PID 0x1011 +#define COOLERMASTER_ARGB_GEN2_A1_PID 0x0173 +#define COOLERMASTER_ARGB_GEN2_A1_V2_PID 0x01C9 +#define COOLERMASTER_ARGB_GEN2_A1_MINI_PID 0x01CB +#define COOLERMASTER_SMALL_ARGB_PID 0x1000 +#define COOLERMASTER_RGB_PID 0x004F + +/*-----------------------------------------------------*\ +| Coolermaster Mice | +\*-----------------------------------------------------*/ +#define COOLERMASTER_MM530_PID 0x0065 +#define COOLERMASTER_MM531_PID 0x0097 +#define COOLERMASTER_MM711_PID 0x0101 +#define COOLERMASTER_MM712_PID 0x0169 +#define COOLERMASTER_MM720_PID 0x0141 +#define COOLERMASTER_MM730_PID 0x0165 + +/*-----------------------------------------------------*\ +| Coolermaster Mousemats | +\*-----------------------------------------------------*/ +#define COOLERMASTER_MP750_XL_PID 0x0109 +#define COOLERMASTER_MP750_L_PID 0x0107 +#define COOLERMASTER_MP750_MEDIUM_PID 0x0105 + +/*-----------------------------------------------------*\ +| Coolermaster Monitors | +\*-----------------------------------------------------*/ +#define COOLERMASTER_GM27_FQS_PID 0x01BB + +/*-----------------------------------------------------*\ +| Coolermaster Desks | +\*-----------------------------------------------------*/ +#define COOLERMASTER_GD160_PID 0x01A9 + +/******************************************************************************************\ +* * +* DetectCoolerMasterControllers * +* * +* Tests the USB address to see if any CoolerMaster controllers exists there. * +* * +\******************************************************************************************/ + +void DetectCoolerMasterARGB(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMARGBController* controller = new CMARGBController(dev, info->path); + + if(controller->GetVersion() != "Unsupported") + { + RGBController_CMARGBController* rgb_controller = new RGBController_CMARGBController(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + LOG_ERROR("[CMARGBController] Unsupported firmware version"); + delete controller; + } + } +} + +void DetectCoolerMasterARGBGen2A1(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMARGBGen2A1controller* controller = new CMARGBGen2A1controller(dev, *info, name); + RGBController_CMARGBGen2A1Controller* rgb_controller = new RGBController_CMARGBGen2A1Controller(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectCoolerMasterGPU(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMR6000Controller* controller = new CMR6000Controller(dev, info->path, info->product_id); + RGBController_CMR6000Controller* rgb_controller = new RGBController_CMR6000Controller(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectCoolerMasterV1Keyboards(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + switch(info->product_id) + { + case COOLERMASTER_KEYBOARD_PRO_L_PID: + case COOLERMASTER_KEYBOARD_PRO_L_WHITE_PID: + case COOLERMASTER_KEYBOARD_PRO_S_PID: + { + CMKeyboardV1Controller* controller = new CMKeyboardV1Controller(dev, info, name); + RGBController_CMKeyboardController* rgb_controller = new RGBController_CMKeyboardController(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + + default: + LOG_DEBUG("[%s] Controller not created as the product ID %04X is missing from detector switch", name.c_str(), info->product_id); + break; + } + } +} + +void DetectCoolerMasterV2Keyboards(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + switch(info->product_id) + { + case COOLERMASTER_KEYBOARD_PRO_L_PID: + case COOLERMASTER_KEYBOARD_PRO_L_WHITE_PID: + case COOLERMASTER_KEYBOARD_PRO_S_PID: + { + CMKeyboardV1Controller* controller = new CMKeyboardV1Controller(dev, info, name); + RGBController_CMKeyboardController* rgb_controller = new RGBController_CMKeyboardController(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + + case COOLERMASTER_KEYBOARD_SK622B_PID: + case COOLERMASTER_KEYBOARD_SK622W_PID: + case COOLERMASTER_KEYBOARD_SK630_PID: + case COOLERMASTER_KEYBOARD_SK650_PID: + case COOLERMASTER_KEYBOARD_SK652_PID: + case COOLERMASTER_KEYBOARD_SK653_PID: + case COOLERMASTER_KEYBOARD_CK530_PID: + case COOLERMASTER_KEYBOARD_CK530_V2_PID: + case COOLERMASTER_KEYBOARD_CK550_V2_PID: + case COOLERMASTER_KEYBOARD_CK552_V2_PID: + case COOLERMASTER_KEYBOARD_MK730_PID: + case COOLERMASTER_KEYBOARD_MK750_PID: + { + CMKeyboardV2Controller* controller = new CMKeyboardV2Controller(dev, info, name); + RGBController_CMKeyboardController* rgb_controller = new RGBController_CMKeyboardController(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + + default: + LOG_DEBUG("[%s] Controller not created as the product ID %04X is missing from detector switch", name.c_str(), info->product_id); + break; + } + } +} + +void DetectCoolerMasterMouse(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMMMController* controller = new CMMMController(dev, info->path, info->product_id, name); + RGBController_CMMMController* rgb_controller = new RGBController_CMMMController(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectCoolerMasterMouse711(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMMM711Controller* controller = new CMMM711Controller(dev, info->path); + RGBController_CMMM711Controller* rgb_controller = new RGBController_CMMM711Controller(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectCoolerMasterMouse712(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMMM712Controller* controller = new CMMM712Controller(dev, info->path); + RGBController_CMMM712Controller* rgb_controller = new RGBController_CMMM712Controller(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectCoolerMasterMousemats(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMMP750Controller* controller = new CMMP750Controller(dev, info->path); + RGBController_CMMP750Controller* rgb_controller = new RGBController_CMMP750Controller(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectCoolerMasterRGB(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMRGBController* controller = new CMRGBController(dev, info->path); + RGBController_CMRGBController* rgb_controller = new RGBController_CMRGBController(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectCoolerMasterSmallARGB(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMSmallARGBController* controller = new CMSmallARGBController(dev, info->path, 0); + RGBController_CMSmallARGBController* rgb_controller = new RGBController_CMSmallARGBController(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectCoolerMasterMonitor(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMMonitorController* controller = new CMMonitorController(dev, *info, name); + RGBController_CMMonitorController* rgb_controller = new RGBController_CMMonitorController(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectCoolerMasterGD160(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CMGD160Controller* controller = new CMGD160Controller(dev, *info, name); + RGBController_CMGD160Controller* rgb_controller = new RGBController_CMGD160Controller(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + + +/*-----------------------------------------------------*\ +| Coolermaster Keyboards | +| PIDs defined in `CMKeyboardDevices.h` | +\*-----------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Cooler Master MasterKeys Pro S", DetectCoolerMasterV1Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_PRO_S_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master MasterKeys Pro L", DetectCoolerMasterV1Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_PRO_L_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master MasterKeys Pro L White", DetectCoolerMasterV1Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_PRO_L_WHITE_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master MK850", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_MK850_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master SK620 White", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_SK620W_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master SK620 Black", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_SK620B_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master SK622 White", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_SK622W_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master SK622 Black", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_SK622B_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master SK630", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_SK630_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master SK650", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_SK650_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master SK652", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_SK652_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master SK653", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_SK653_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master MK730", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_MK730_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master MK750", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_MK750_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master CK530", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_CK530_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master CK530 V2", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_CK530_V2_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master CK550 V2", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_CK550_V2_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master CK550 V1 / CK552", DetectCoolerMasterV2Keyboards, COOLERMASTER_VID, COOLERMASTER_KEYBOARD_CK552_V2_PID, 1, 0xFF00, 1); + +/*-----------------------------------------------------*\ +| Coolermaster LEDstrip controllers | +\*-----------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Cooler Master ARGB", DetectCoolerMasterARGB, COOLERMASTER_VID, COOLERMASTER_ARGB_PID, 0, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master ARGB Gen 2 A1", DetectCoolerMasterARGBGen2A1, COOLERMASTER_VID, COOLERMASTER_ARGB_GEN2_A1_PID, 1, 0xFF01, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master ARGB Gen 2 A1 V2", DetectCoolerMasterARGBGen2A1, COOLERMASTER_VID, COOLERMASTER_ARGB_GEN2_A1_V2_PID, 1, 0xFF01, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master ARGB Gen 2 A1 Mini", DetectCoolerMasterARGBGen2A1, COOLERMASTER_VID, COOLERMASTER_ARGB_GEN2_A1_MINI_PID, 1, 0xFF01, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master RGB", DetectCoolerMasterRGB, COOLERMASTER_VID, COOLERMASTER_RGB_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master Small ARGB", DetectCoolerMasterSmallARGB, COOLERMASTER_VID, COOLERMASTER_SMALL_ARGB_PID, 0, 0xFF00, 1); + +/*-----------------------------------------------------*\ +| Coolermaster Mice | +\*-----------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Cooler Master MM530", DetectCoolerMasterMouse, COOLERMASTER_VID, COOLERMASTER_MM530_PID, 1, 0xFF00, 1); +//REGISTER_HID_DETECTOR_IPU("Cooler Master MM531", DetectCoolerMasterMouse, COOLERMASTER_VID, COOLERMASTER_MM531_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master MM711", DetectCoolerMasterMouse711, COOLERMASTER_VID, COOLERMASTER_MM711_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master MM712", DetectCoolerMasterMouse712, COOLERMASTER_VID, COOLERMASTER_MM712_PID, 3, 0xFF0A, 2); +REGISTER_HID_DETECTOR_IPU("Cooler Master MM720", DetectCoolerMasterMouse, COOLERMASTER_VID, COOLERMASTER_MM720_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cooler Master MM730", DetectCoolerMasterMouse, COOLERMASTER_VID, COOLERMASTER_MM730_PID, 1, 0xFF00, 1); + +/*-----------------------------------------------------*\ +| Coolermaster Mousemats | +\*-----------------------------------------------------*/ +REGISTER_HID_DETECTOR_PU ("Cooler Master MP750 XL", DetectCoolerMasterMousemats, COOLERMASTER_VID, COOLERMASTER_MP750_XL_PID, 0xFF00, 1); +REGISTER_HID_DETECTOR_PU ("Cooler Master MP750 Large", DetectCoolerMasterMousemats, COOLERMASTER_VID, COOLERMASTER_MP750_L_PID, 0xFF00, 1); +REGISTER_HID_DETECTOR_PU ("Cooler Master MP750 Medium", DetectCoolerMasterMousemats, COOLERMASTER_VID, COOLERMASTER_MP750_MEDIUM_PID, 0xFF00, 1); + +/*-----------------------------------------------------*\ +| Coolermaster GPUs | +| PIDs defined in `CMR6000Controller.h` | +\*-----------------------------------------------------*/ +REGISTER_HID_DETECTOR_I ("Cooler Master Radeon RX 6000 GPU", DetectCoolerMasterGPU, COOLERMASTER_VID, COOLERMASTER_RADEON_6000_PID, 1 ); +REGISTER_HID_DETECTOR_I ("Cooler Master Radeon RX 6900 GPU", DetectCoolerMasterGPU, COOLERMASTER_VID, COOLERMASTER_RADEON_6900_PID, 1 ); + +/*-----------------------------------------------------*\ +| Coolermaster Monitors | +\*-----------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Cooler Master GM27-FQS ARGB Monitor", DetectCoolerMasterMonitor, COOLERMASTER_VID, COOLERMASTER_GM27_FQS_PID, 0, 0xFF00, 1); + +/*-----------------------------------------------------*\ +| Coolermaster Desks | +\*-----------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Cooler Master GD160 ARGB Gaming Desk", DetectCoolerMasterGD160, COOLERMASTER_VID, COOLERMASTER_GD160_PID, 0, 0xFF00, 1); diff --git a/Controllers/CorsairCommanderCoreController/CorsairCommanderCoreController.cpp b/Controllers/CorsairCommanderCoreController/CorsairCommanderCoreController.cpp new file mode 100644 index 0000000..c72d72c --- /dev/null +++ b/Controllers/CorsairCommanderCoreController/CorsairCommanderCoreController.cpp @@ -0,0 +1,485 @@ +/*---------------------------------------------------------*\ +| CorsairCommanderCoreController.cpp | +| | +| Driver for Corsair Commander Core | +| | +| Jeff P. | +| Nikola Jurkovic (jurkovic.nikola) 14 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "CorsairCommanderCoreController.h" +#include "CorsairDeviceGuard.h" + +using namespace std::chrono_literals; + +CorsairCommanderCoreController::CorsairCommanderCoreController(hid_device* dev_handle, const char* path, int pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + keepalive_thread_run = 1; + controller_ready = 0; + packet_size = CORSAIR_COMMANDER_CORE_PACKET_SIZE_V2; + command_res_size = packet_size - 4; + this->pid = pid; + guard_manager_ptr = new DeviceGuardManager(new CorsairDeviceGuard()); + + if(pid == CORSAIR_COMMANDER_CORE2_PID) + { + packet_size = CORSAIR_COMMANDER_CORE_PACKET_SIZE_V3; + command_res_size = packet_size - 4; + } + else if(pid == CORSAIR_COMMANDER_CORE_XT_PID) + { + /*-----------------------------------------------------*\ + | Commander Core XT | + \*-----------------------------------------------------*/ + packet_size = CORSAIR_COMMANDER_CORE_XT_PACKET_SIZE; + command_res_size = packet_size - 4; + } + + /*-----------------------------------------------------*\ + | Initialize controller | + \*-----------------------------------------------------*/ + InitController(); + + /*-----------------------------------------------------*\ + | Start keepalive thread | + \*-----------------------------------------------------*/ + keepalive_thread = new std::thread(&CorsairCommanderCoreController::KeepaliveThread, this); +} + +CorsairCommanderCoreController::~CorsairCommanderCoreController() +{ + /*-----------------------------------------------------*\ + | Hardware mode | + \*-----------------------------------------------------*/ + unsigned char command[2] = {0x01, 0x03}; + unsigned char cmd_data[2] = {0x00, 0x01}; + SendCommand(command, cmd_data, 2, NULL); + + /*-----------------------------------------------------*\ + | Close keepalive thread | + \*-----------------------------------------------------*/ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + /*-----------------------------------------------------*\ + | Close HID device | + \*-----------------------------------------------------*/ + hid_close(dev); + delete guard_manager_ptr; +} + +void CorsairCommanderCoreController::InitController() +{ + /*-----------------------------------------------------*\ + | Get version | + \*-----------------------------------------------------*/ + unsigned char command[2] = {0x02, 0x13}; + unsigned char* res = new unsigned char[command_res_size]; + + SendCommand(command, NULL, 0, res); + version[0] = res[0]; + version[1] = res[1]; + version[2] = res[2]; + delete[] res; + + if(pid == CORSAIR_COMMANDER_CORE_PID && version[0] == 1) + { + packet_size = CORSAIR_COMMANDER_CORE_PACKET_SIZE_V1; + command_res_size = packet_size - 4; + } + + SetFanMode(false); +} + +std::string CorsairCommanderCoreController::GetFirmwareString() +{ + return "v"+std::to_string(version[0]) + "." + std::to_string(version[1]) + "." + std::to_string(version[2]); +} + +std::string CorsairCommanderCoreController::GetLocationString() +{ + return("HID: " + location); +} + +std::string CorsairCommanderCoreController::GetNameString() +{ + return(name); +} + +int CorsairCommanderCoreController::GetPidInt() +{ + return(this->pid); +} + +std::vector CorsairCommanderCoreController::GetLedCounts() +{ + /*-----------------------------------------------------*\ + | Get the LED count per device | + \*-----------------------------------------------------*/ + std::vector led_counts; + unsigned char endpoint[2] = {0x20, 0x00}; + unsigned char* res = new unsigned char[command_res_size]; + ReadData(endpoint, res); + for(int i = 0; i < res[2]; i++) + { + led_counts.push_back(res[i*4+6] << 8 | res[i*4+5]); + } + delete[] res; + + return led_counts; +} + +void CorsairCommanderCoreController::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(controller_ready) + { + if((std::chrono::steady_clock::now() - last_commit_time) > std::chrono::seconds(10)) + { + SendCommit(); + } + } + std::this_thread::sleep_for(1s); + } +} + +void CorsairCommanderCoreController::SendCommit() +{ + if(!lastcolors.empty()) + { + /*-----------------------------------------------------*\ + | If colors remain to be sent, send them | + \*-----------------------------------------------------*/ + SetDirectColor(lastcolors, lastzones); + } + else + { + /*-----------------------------------------------------*\ + | Update last commit time | + \*-----------------------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); + + /*-----------------------------------------------------*\ + | Keepalive | + \*-----------------------------------------------------*/ + unsigned char command[2] = {0x02, 0x13}; + SendCommand(command, NULL, 2, NULL, false); + } +} + + +void CorsairCommanderCoreController::SendCommand(unsigned char command[2], unsigned char data[], unsigned short int data_len, unsigned char res[], bool dev_read) +{ + /*---------------------------------------------------------*\ + | Private function to send a command | + | data_len must be <= 93 for V2 or <= 1021 for V1 | + \*---------------------------------------------------------*/ + unsigned char* buf = new unsigned char[packet_size]; + + memset(buf, 0, packet_size); + buf[0] = 0x00; + buf[1] = 0x08; + + memcpy(&buf[2], command, 2); + if(data != NULL) + { + memcpy(&buf[4], data, data_len); + } + + /*---------------------------------------------------------*\ + | HID I/O start | + \*---------------------------------------------------------*/ + { + DeviceGuardLock _ = guard_manager_ptr->AwaitExclusiveAccess(); + + hid_write(dev, buf, packet_size); + if(dev_read) + { + do + { + hid_read(dev, buf, packet_size); + } + while(buf[0] != 0x00); + } + } + + /*---------------------------------------------------------*\ + | HID I/O end (lock released) | + \*---------------------------------------------------------*/ + + if(res != NULL) + { + memcpy(res, &buf[3], command_res_size); + } + + delete[] buf; +} + +void CorsairCommanderCoreController::WriteData(unsigned char endpoint[2], unsigned char data_type[2], unsigned char data[], unsigned short int data_len) +{ + /*---------------------------------------------------------*\ + | Private function to write data to an endpoint | + \*---------------------------------------------------------*/ + + /*---------------------------------------------------------*\ + | Open endpoint | + \*---------------------------------------------------------*/ + unsigned char command[2] = {0x0D, 0x00}; + SendCommand(command, endpoint, 2, NULL); + + /*---------------------------------------------------------*\ + | Write data | + \*---------------------------------------------------------*/ + unsigned short int data_start_index = 0; + while(data_start_index < data_len) + { + if(data_start_index == 0) + { + /*---------------------------------------------------------*\ + | First packet | + \*---------------------------------------------------------*/ + int packet_data_len = packet_size - 10; + if(data_len < packet_data_len) + { + packet_data_len = data_len; + } + unsigned char* buf = new unsigned char[packet_data_len+6]; + unsigned short int real_len = data_len+2; + /*---------------------------------------------------------*\ + | Convert length to little endian | + \*---------------------------------------------------------*/ + buf[0] = (unsigned char) real_len & 0xFF; + buf[1] = (unsigned char) (real_len >> 8) & 0xFF; + buf[2] = 0x00; + buf[3] = 0x00; + memcpy(&buf[4], data_type, 2); + memcpy(&buf[6], data, packet_data_len); + + command[0] = 0x06; + command[1] = 0x00; + SendCommand(command, buf, packet_data_len+6, NULL); + delete[] buf; + data_start_index += packet_data_len; + } + else + { + /*-----------------------------------------------------------------------------------------------------*\ + | The rest of the packets | + | This command is not in v1 but it should never be reached as all data should fit in the first packet | + \*-----------------------------------------------------------------------------------------------------*/ + int packet_data_len = packet_size - 4; + if(data_len-data_start_index < packet_data_len) + { + packet_data_len = data_len-data_start_index; + } + + command[0] = 0x07; + command[1] = 0x00; + SendCommand(command, &data[data_start_index], packet_data_len, NULL); + data_start_index += packet_data_len; + } + } + + /*---------------------------------------------------------*\ + | Close endpoint | + \*---------------------------------------------------------*/ + command[0] = 0x05; + command[1] = 0x01; + SendCommand(command, NULL, 0, NULL); +} + +void CorsairCommanderCoreController::ReadData(unsigned char endpoint[2], unsigned char data[]) +{ + /*---------------------------------------------------------*\ + | Private function to read data from an endpoint | + | Note: Right now we only know how to read the first packet.| + | It is not currently know how to read more. | + \*---------------------------------------------------------*/ + + /*---------------------------------------------------------*\ + | Open endpoint | + \*---------------------------------------------------------*/ + unsigned char command[2] = {0x0D, 0x00}; + SendCommand(command, endpoint, 2, NULL); + + /*---------------------------------------------------------*\ + | Read data | + \*---------------------------------------------------------*/ + command[0] = 0x08; + command[1] = 0x00; + SendCommand(command, NULL, 0, data); + + /*---------------------------------------------------------*\ + | Close endpoint | + \*---------------------------------------------------------*/ + command[0] = 0x05; + command[1] = 0x01; + SendCommand(command, NULL, 0, NULL); +} + +void CorsairCommanderCoreController::SetDirectColor + ( + std::vector colors, + std::vector zones + ) +{ + if(controller_ready == 1 && ((std::chrono::steady_clock::now() - last_commit_time) > std::chrono::milliseconds(33))) + { + lastcolors = colors; + lastzones = zones; + int packet_offset = 0; + int led_idx = 0; + int channel_idx = 0; + int packet_len = CORSAIR_COMMANDER_CORE_RGB_DATA_LENGTH; + + if(pid == CORSAIR_COMMANDER_CORE_XT_PID) + { + packet_len = CORSAIR_COMMANDER_CORE_XT_RGB_DATA_LENGTH; + } + + unsigned char* usb_buf = new unsigned char[packet_len]; + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + /*-------------------------------------------------*\ + | Add led colors | + \*-------------------------------------------------*/ + for(unsigned int i = led_idx; i < led_idx + zones[zone_idx].leds_count; i++) + { + usb_buf[packet_offset] = RGBGetRValue(colors[i]); + usb_buf[packet_offset+1] = RGBGetGValue(colors[i]); + usb_buf[packet_offset+2] = RGBGetBValue(colors[i]); + packet_offset += 3; + } + + led_idx = led_idx + zones[zone_idx].leds_count; + + if(zone_idx != 0) + { + packet_offset += 3 * (34 - zones[zone_idx].leds_count); + } + + channel_idx++; + } + + /*-----------------------------------------------------*\ + | Sending a direct mode color packet resets the timeout | + \*-----------------------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); + + unsigned char endpoint[2] = {0x22, 0x00}; + unsigned char data_type[2] = {0x12, 0x00}; + WriteData(endpoint, data_type, usb_buf, packet_offset); + + delete[] usb_buf; + } +} + +void CorsairCommanderCoreController::SetFanMode(bool external_rgb_port) +{ + controller_ready = 0; + DeviceGuardLock _ = guard_manager_ptr->AwaitExclusiveAccess(); + + /*-----------------------------------------------------*\ + | Force controller to 6 QL fan mode to expose maximum | + | number of LEDs per rgb port (34 LEDs per port) | + \*-----------------------------------------------------*/ + unsigned int index = 3; + unsigned int max_index = 15; + unsigned char endpoint[2] = {0x1E, 0x00}; + unsigned char data_type[2] = {0x0D, 0x00}; + + unsigned char buf[15]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, 15); + + buf[0] = 0x07; + if(pid == CORSAIR_COMMANDER_CORE_XT_PID) + { + /*-------------------------------------------------*\ + | Commander Core XT external RGB port | + \*-------------------------------------------------*/ + if(external_rgb_port) + { + /*---------------------------------------------*\ + | Enable external port | + \*---------------------------------------------*/ + buf[1] = 0x01; + buf[2] = 0x01; + } + else + { + /*---------------------------------------------*\ + | Shift packet start position and maximum index | + \*---------------------------------------------*/ + buf[1] = 0x00; + buf[2] = 0x00; + index = 2; + max_index = 14; + } + } + else + { + /*-------------------------------------------------*\ + | Commander Core, Set AIO mode | + \*-------------------------------------------------*/ + buf[1] = 0x01; + buf[2] = 0x08; + } + + /*-----------------------------------------------------*\ + | SET fan modes | + \*-----------------------------------------------------*/ + for(unsigned int i = index; i < max_index; i = i + 2) + { + buf[i] = 0x01; + buf[i + 1] = 0x06; + } + + WriteData(endpoint, data_type, buf, 15); + controller_ready = 1; + + /*-----------------------------------------------------*\ + | Wake up device, needs to be done after setting fan | + | mode to reinitialize device if fan mode has changed | + \*-----------------------------------------------------*/ + unsigned char command[2] = {0x01, 0x03}; + unsigned char cmd_data[2] = {0x00, 0x02}; + SendCommand(command, cmd_data, 2, NULL); +} + +void CorsairCommanderCoreController::SetLedAmount(int led_amount) +{ + controller_ready = 0; + DeviceGuardLock _ = guard_manager_ptr->AwaitExclusiveAccess(); + + unsigned char buf[15]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, 15); + + unsigned char endpoint[2] = {0x1D, 0x00}; + unsigned char data_type[2] = {0x0C, 0x00}; + + buf[0] = 0x07; + buf[1] = led_amount; + + WriteData(endpoint, data_type, buf, 15); + controller_ready = 1; +} diff --git a/Controllers/CorsairCommanderCoreController/CorsairCommanderCoreController.h b/Controllers/CorsairCommanderCoreController/CorsairCommanderCoreController.h new file mode 100644 index 0000000..530e1bc --- /dev/null +++ b/Controllers/CorsairCommanderCoreController/CorsairCommanderCoreController.h @@ -0,0 +1,92 @@ +/*---------------------------------------------------------*\ +| CorsairCommanderCoreController.h | +| | +| Driver for Corsair Commander Core | +| | +| Jeff P. | +| Nikola Jurkovic (jurkovic.nikola) 14 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "DeviceGuardManager.h" + +/*-----------------------------------------------------*\ +| Packet size per device | +\*-----------------------------------------------------*/ +#define CORSAIR_COMMANDER_CORE_PACKET_SIZE_V1 1025 +#define CORSAIR_COMMANDER_CORE_PACKET_SIZE_V2 97 +#define CORSAIR_COMMANDER_CORE_PACKET_SIZE_V3 65 +#define CORSAIR_COMMANDER_CORE_XT_PACKET_SIZE 385 + +#define CORSAIR_COMMANDER_CORE_RGB_DATA_LENGTH 699 +#define CORSAIR_COMMANDER_CORE_XT_RGB_DATA_LENGTH 1224 + +#define CORSAIR_QL_FAN_ZONE_OFFSET 102 +#define CORSAIR_COMMANDER_CORE_NUM_CHANNELS 6 + +#define CORSAIR_COMMANDER_CORE_PID 0x0C1C +#define CORSAIR_COMMANDER_CORE2_PID 0x0C32 +#define CORSAIR_COMMANDER_CORE3_PID 0x0C1D +#define CORSAIR_COMMANDER_CORE4_PID 0x0C3C +#define CORSAIR_COMMANDER_CORE5_PID 0x0C3D +#define CORSAIR_COMMANDER_CORE6_PID 0x0C3E +#define CORSAIR_COMMANDER_CORE_XT_PID 0x0C2A + +enum +{ + CORSAIR_COMMANDER_CORE_MODE_DIRECT = 0x00, +}; + +class CorsairCommanderCoreController +{ +public: + CorsairCommanderCoreController(hid_device* dev_handle, const char* path, int pid, std::string dev_name); + ~CorsairCommanderCoreController(); + + std::string GetFirmwareString(); + std::vector GetLedCounts(); + std::string GetLocationString(); + std::string GetNameString(); + int GetPidInt(); + + void SetDirectColor + ( + std::vector, + std::vector + ); + + void KeepaliveThread(); + void SetFanMode(bool external_rgb_port); + void SetLedAmount(int led_amount); + +private: + hid_device* dev; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::atomic controller_ready; + std::string location; + std::vector lastcolors; + std::vector lastzones; + std::string name; + unsigned short int version[3] = {0, 0, 0}; + int packet_size; + int command_res_size; + int pid; + std::chrono::time_point last_commit_time; + DeviceGuardManager* guard_manager_ptr; + + void SendCommand(unsigned char command[2], unsigned char data[], unsigned short int data_len, unsigned char res[], bool dev_read = true); + void WriteData(unsigned char endpoint[2], unsigned char data_type[2], unsigned char data[], unsigned short int data_len); + void ReadData(unsigned char endpoint[2], unsigned char data[]); + + void SendCommit(); + void InitController(); +}; diff --git a/Controllers/CorsairCommanderCoreController/CorsairCommanderCoreControllerDetect.cpp b/Controllers/CorsairCommanderCoreController/CorsairCommanderCoreControllerDetect.cpp new file mode 100644 index 0000000..4a7a3cc --- /dev/null +++ b/Controllers/CorsairCommanderCoreController/CorsairCommanderCoreControllerDetect.cpp @@ -0,0 +1,49 @@ +/*---------------------------------------------------------*\ +| CorsairCommanderCoreControllerDetect.cpp | +| | +| Detector for Corsair Commander Core | +| | +| Jeff P. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "CorsairCommanderCoreController.h" +#include "RGBController_CorsairCommanderCore.h" + +/*-----------------------------------------------------*\ +| Corsair vendor ID | +\*-----------------------------------------------------*/ +#define CORSAIR_VID 0x1B1C + +/******************************************************************************************\ +* * +* DetectCorsairCommanderCoreControllers * +* * +* Tests the USB address to see if a Corsair RGB Cooler controller exists there. * +* * +\******************************************************************************************/ + +void DetectCorsairCommanderCoreControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CorsairCommanderCoreController* controller = new CorsairCommanderCoreController(dev, info->path, info->product_id, name); + RGBController_CorsairCommanderCore* rgb_controller = new RGBController_CorsairCommanderCore(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Corsair Commander Core", DetectCorsairCommanderCoreControllers, CORSAIR_VID, CORSAIR_COMMANDER_CORE_PID, 0x00, 0xFF42, 0x01); +REGISTER_HID_DETECTOR_IPU("Corsair Commander Core", DetectCorsairCommanderCoreControllers, CORSAIR_VID, CORSAIR_COMMANDER_CORE2_PID, 0x00, 0xFF42, 0x01); +REGISTER_HID_DETECTOR_IPU("Corsair Commander Core", DetectCorsairCommanderCoreControllers, CORSAIR_VID, CORSAIR_COMMANDER_CORE3_PID, 0x00, 0xFF42, 0x01); +REGISTER_HID_DETECTOR_IPU("Corsair Commander Core", DetectCorsairCommanderCoreControllers, CORSAIR_VID, CORSAIR_COMMANDER_CORE4_PID, 0x00, 0xFF42, 0x01); +REGISTER_HID_DETECTOR_IPU("Corsair Commander Core", DetectCorsairCommanderCoreControllers, CORSAIR_VID, CORSAIR_COMMANDER_CORE5_PID, 0x00, 0xFF42, 0x01); +REGISTER_HID_DETECTOR_IPU("Corsair Commander Core", DetectCorsairCommanderCoreControllers, CORSAIR_VID, CORSAIR_COMMANDER_CORE6_PID, 0x00, 0xFF42, 0x01); +REGISTER_HID_DETECTOR_IPU("Corsair Commander Core XT", DetectCorsairCommanderCoreControllers, CORSAIR_VID, CORSAIR_COMMANDER_CORE_XT_PID, 0x00, 0xFF42, 0x01); diff --git a/Controllers/CorsairCommanderCoreController/RGBController_CorsairCommanderCore.cpp b/Controllers/CorsairCommanderCoreController/RGBController_CorsairCommanderCore.cpp new file mode 100644 index 0000000..777abe0 --- /dev/null +++ b/Controllers/CorsairCommanderCoreController/RGBController_CorsairCommanderCore.cpp @@ -0,0 +1,198 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairCCommanderCore.cpp | +| | +| RGBController for Corsair Commander Core | +| | +| Jeff P. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CorsairCommanderCore.h" + +/**------------------------------------------------------------------*\ + @name Corsair Commander Core + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectCorsairCommanderCoreControllers + @comment +\*-------------------------------------------------------------------*/ + +#define NA 0xFFFFFFFF +static unsigned int matrix_map29[7][7] = +{ + { 28, NA, 27, NA, 26, NA, 25 }, + { NA, 16, NA, 15, NA, 14, NA }, + { 17, NA, 0, 5, 3, NA, 24 }, + { NA, 9, 4, 8, 6, 13, NA }, + { 18, NA, 1, 7, 2, NA, 23 }, + { NA, 10, NA, 11, NA, 12, NA }, + { 19, NA, 20, NA, 21, NA, 22 }, +}; + +static unsigned int matrix_map24[11][11] = +{ + { NA, NA, NA, NA, NA, 6, NA, NA, NA, NA, NA }, + { NA, NA, NA, 4, 5, NA, 7, 8, NA, NA, NA }, + { NA, NA, 3, NA, NA, NA, NA, NA, 9, NA, NA }, + { NA, 2, NA, NA, NA, NA, NA, NA, NA, 10, NA }, + { NA, 1, NA, NA, NA, NA, NA, NA, NA, 11, NA }, + { 0, NA, NA, NA, NA, NA, NA, NA, NA, NA, 12 }, + { NA, 23, NA, NA, NA, NA, NA, NA, NA, 13, NA }, + { NA, 22, NA, NA, NA, NA, NA, NA, NA, 14, NA }, + { NA, NA, 21, NA, NA, NA, NA, NA, 15, NA, NA }, + { NA, NA, NA, 20, 19, NA, 17, 16, NA, NA, NA }, + { NA, NA, NA, NA, NA, 18, NA, NA, NA, NA, NA } +}; + +RGBController_CorsairCommanderCore::RGBController_CorsairCommanderCore(CorsairCommanderCoreController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Corsair"; + description = "Corsair Commander Core Device"; + version = controller->GetFirmwareString(); + type = DEVICE_TYPE_COOLER; + location = controller->GetLocationString(); + SetupZones(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); +} + +RGBController_CorsairCommanderCore::~RGBController_CorsairCommanderCore() +{ + delete controller; +} + +void RGBController_CorsairCommanderCore::SetupZones() +{ + std::atomic first_run; + first_run = 0; + + if(zones.size() == 0) + { + first_run = 1; + } + + std::vector led_count = controller->GetLedCounts(); + zones.resize(7); + if(controller->GetPidInt() == CORSAIR_COMMANDER_CORE_XT_PID) + { + zones[0].name = "External RGB Port"; + zones[0].type = ZONE_TYPE_LINEAR; + zones[0].leds_min = zones[0].leds_min; + zones[0].leds_max = 204; + zones[0].leds_count = zones[0].leds_count; + } + else + { + zones[0].name = "Pump"; + zones[0].type = ZONE_TYPE_MATRIX; + zones[0].leds_min = led_count.at(0); + zones[0].leds_max = led_count.at(0); + zones[0].leds_count = led_count.at(0); + zones[0].matrix_map = new matrix_map_type; + if(led_count.at(0) == 24) + { + zones[0].matrix_map->height = 11; + zones[0].matrix_map->width = 11; + zones[0].matrix_map->map = (unsigned int *)&matrix_map24; + } + else + { + zones[0].matrix_map->height = 7; + zones[0].matrix_map->width = 7; + zones[0].matrix_map->map = (unsigned int *)&matrix_map29; + } + } + + for(unsigned int i = 1; i < (CORSAIR_COMMANDER_CORE_NUM_CHANNELS + 1); i++) + { + zones[i].name = "RGB Port " + std::to_string(i); + zones[i].type = ZONE_TYPE_LINEAR; + zones[i].leds_min = 0; + zones[i].leds_max = 34; + + if(first_run) + { + zones[i].leds_count = (led_count.size() > i) ? led_count.at(i) : 0; + } + } + + leds.clear(); + colors.clear(); + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + new_led.name = zones[zone_idx].name + " LED " + std::to_string(led_idx+1); + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_CorsairCommanderCore::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + if(zone == 0 && controller->GetPidInt() == CORSAIR_COMMANDER_CORE_XT_PID) + { + if(new_size > 0) + { + controller->SetFanMode(true); + controller->SetLedAmount(new_size); + } + else + { + controller->SetFanMode(false); + } + } + SetupZones(); + } +} + +void RGBController_CorsairCommanderCore::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_CorsairCommanderCore::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairCommanderCore::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairCommanderCore::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case CORSAIR_COMMANDER_CORE_MODE_DIRECT: + controller->SetDirectColor(colors, zones); + break; + } +} diff --git a/Controllers/CorsairCommanderCoreController/RGBController_CorsairCommanderCore.h b/Controllers/CorsairCommanderCoreController/RGBController_CorsairCommanderCore.h new file mode 100644 index 0000000..d2b5b68 --- /dev/null +++ b/Controllers/CorsairCommanderCoreController/RGBController_CorsairCommanderCore.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairCCommanderCore.h | +| | +| RGBController for Corsair Commander Core | +| | +| Jeff P. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairCommanderCoreController.h" + +class RGBController_CorsairCommanderCore : public RGBController +{ +public: + RGBController_CorsairCommanderCore(CorsairCommanderCoreController* controller_ptr); + ~RGBController_CorsairCommanderCore(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CorsairCommanderCoreController* controller; + std::vector fanleds{0}; +}; diff --git a/Controllers/CorsairController/CorsairDeviceGuard.cpp b/Controllers/CorsairController/CorsairDeviceGuard.cpp new file mode 100644 index 0000000..96ba243 --- /dev/null +++ b/Controllers/CorsairController/CorsairDeviceGuard.cpp @@ -0,0 +1,64 @@ +/*---------------------------------------------------------*\ +| CorsairDeviceGuard.cpp | +| | +| DeviceGuard for Corsair devices | +| | +| Evan Mulawski 04 Sep 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CorsairDeviceGuard.h" + +CorsairDeviceGuard::CorsairDeviceGuard() : DeviceGuard() +{ +#ifdef _WIN32 + mutex_handle = CreateWindowsMutex(); +#endif +} + +void CorsairDeviceGuard::Acquire() +{ +#ifdef _WIN32 + while(true) + { + DWORD result = WaitForSingleObject(mutex_handle, INFINITE); + + if(result == WAIT_OBJECT_0) + { + break; + } + + if(result == WAIT_ABANDONED) + { + ReleaseMutex(mutex_handle); + } + } +#endif +} + +void CorsairDeviceGuard::Release() +{ +#ifdef _WIN32 + ReleaseMutex(mutex_handle); +#endif +} + +#ifdef _WIN32 + +HANDLE CorsairDeviceGuard::CreateWindowsMutex() +{ + SECURITY_DESCRIPTOR sd; + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + return CreateMutex(&sa, FALSE, "Global\\CorsairLinkReadWriteGuardMutex"); +} + +#endif diff --git a/Controllers/CorsairController/CorsairDeviceGuard.h b/Controllers/CorsairController/CorsairDeviceGuard.h new file mode 100644 index 0000000..14a9564 --- /dev/null +++ b/Controllers/CorsairController/CorsairDeviceGuard.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| CorsairDeviceGuard.cpp | +| | +| DeviceGuard for Corsair devices | +| | +| Evan Mulawski 04 Sep 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "DeviceGuard.h" + +#ifdef _WIN32 +/*---------------------------------------------------------*\ +| Windows interferes with std::max unless NOMINMAX defined | +\*---------------------------------------------------------*/ +#define NOMINMAX +#include +#endif + +class CorsairDeviceGuard : public DeviceGuard +{ +public: + CorsairDeviceGuard(); + + void Acquire() override; + void Release() override; + +private: +#ifdef _WIN32 + HANDLE mutex_handle; + + HANDLE CreateWindowsMutex(); +#endif +}; diff --git a/Controllers/CorsairDRAMController/CorsairDRAMController.cpp b/Controllers/CorsairDRAMController/CorsairDRAMController.cpp new file mode 100644 index 0000000..aa5fe8b --- /dev/null +++ b/Controllers/CorsairDRAMController/CorsairDRAMController.cpp @@ -0,0 +1,489 @@ +/*---------------------------------------------------------*\ +| CorsairDRAMController.cpp | +| | +| Driver for Corsair DRAM RGB controllers | +| | +| Adam Honse (CalcProgrammer1) 30 Jun 2019 | +| Erik Gilling (konkers) 25 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CRC.h" +#include "CorsairDRAMController.h" +#include "LogManager.h" + +#define CORSAIR_DRAM_NAME "Corsair DRAM" + +using namespace std::chrono_literals; + +CorsairDRAMController::CorsairDRAMController(i2c_smbus_interface *bus, corsair_dev_id dev) +{ + /*-----------------------------------------------------*\ + | Initialize class variables | + \*-----------------------------------------------------*/ + this->bus = bus; + this->dev = dev; + device_index = 0; + direct_mode = true; + pid = 0; + vid = 0; + protocol_version = 0; + + /*-----------------------------------------------------*\ + | Read device information | + \*-----------------------------------------------------*/ + ReadDeviceInfo(); +} + +CorsairDRAMController::~CorsairDRAMController() +{ +} + +unsigned int CorsairDRAMController::GetLEDCount() +{ + return(corsair_dram_device_list[device_index]->led_count); +} + +unsigned char CorsairDRAMController::GetProtocolVersion() +{ + return(protocol_version); +} + +std::string CorsairDRAMController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string CorsairDRAMController::GetDeviceName() +{ + return(corsair_dram_device_list[device_index]->name); +} + +std::string CorsairDRAMController::GetDeviceVersion() +{ + return(firmware_version); +} + +void CorsairDRAMController::SetColorsPerLED(RGBColor* colors) +{ + /*-----------------------------------------------------*\ + | Get LED count from device list | + \*-----------------------------------------------------*/ + unsigned int led_count = corsair_dram_device_list[device_index]->led_count; + + if(direct_mode) + { + /*-------------------------------------------------*\ + | Sanity check - Direct mode can only be used on | + | protocol 4+ | + \*-------------------------------------------------*/ + if(protocol_version < 4) + { + LOG_ERROR("[%s] Protocol version %d tried to use direct mode, ignoring", CORSAIR_DRAM_NAME, protocol_version); + return; + } + + /*-------------------------------------------------*\ + | Packet format: | + | Size n is (LED count * 3) + 2 | + | 0: Command byte (0x0A or 0x0C) | + | 1 to (n-2): LED color data in R/G/B order | + | (n-1): CRC8 of bytes 0 to (n-2) | + \*-------------------------------------------------*/ + unsigned int direct_packet_size = (led_count * 3) + 2; + unsigned char* direct_packet = new unsigned char[direct_packet_size]; + + /*-------------------------------------------------*\ + | First byte in packet is LED count | + \*-------------------------------------------------*/ + direct_packet[0] = led_count; + + /*-------------------------------------------------*\ + | Fill in LED data | + \*-------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < led_count; led_idx++) + { + unsigned int color_index = led_idx; + unsigned int offset = (led_idx * 3) + 1; + + if(corsair_dram_device_list[device_index]->reverse) + { + color_index = (led_count -1) - led_idx; + } + + direct_packet[offset + 0] = RGBGetRValue(colors[color_index]); + direct_packet[offset + 1] = RGBGetGValue(colors[color_index]); + direct_packet[offset + 2] = RGBGetBValue(colors[color_index]); + } + + /*-------------------------------------------------*\ + | Last byte in packet is CRC of all data up to it | + \*-------------------------------------------------*/ + direct_packet[direct_packet_size - 1] = CRCPP::CRC::Calculate(direct_packet, (direct_packet_size - 1), CRCPP::CRC::CRC_8()); + + /*-------------------------------------------------*\ + | Write using block writes, if packet exceeds 32 | + | bytes, use a second block write to the second | + | block write address for the remaining data | + \*-------------------------------------------------*/ + s32 ret = bus->i2c_smbus_write_block_data(dev, CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_1, 32, direct_packet); + + if((ret >= 0) && (direct_packet_size > 32)) + { + bus->i2c_smbus_write_block_data(dev, CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_2, direct_packet_size - 32, direct_packet + 32); + } + + /*-------------------------------------------------*\ + | Corsair DRAM supports an alternate means of | + | writing block data without using SMBus block | + | operations. If block operations are not | + | available, fall back to this scheme. | + | | + | Blocks are split up into word data writes. | + | Some data bytes are packed into the lower nibble | + | of the register address byte. | + \*-------------------------------------------------*/ + if(ret < 0) + { + unsigned int block_index = 0; + bool even_frame = true; + bool first_frame = true; + + while(block_index < direct_packet_size) + { + unsigned char reg_value_0 = 0xA0; + unsigned char reg_value_1 = 0x00; + unsigned short word_value_0 = 0; + unsigned short word_value_1 = 0; + + if(block_index == 0) + { + reg_value_0 = 0x90; + } + + word_value_0 = (direct_packet[block_index]); + block_index++; + + if(block_index < direct_packet_size) + { + word_value_0 |= (direct_packet[block_index] << 8); + block_index++; + } + + if(block_index < direct_packet_size) + { + reg_value_1 = 0xA0; + reg_value_0 |= (direct_packet[block_index] & 0x0F); + reg_value_1 |= (direct_packet[block_index] & 0xF0) >> 4; + block_index++; + } + + if(block_index < direct_packet_size) + { + word_value_1 = (direct_packet[block_index]); + block_index++; + } + + if(block_index < direct_packet_size) + { + word_value_1 |= (direct_packet[block_index] << 8); + block_index++; + } + + bus->i2c_smbus_write_word_data(dev, reg_value_0, word_value_0); + + if(reg_value_1 > 0) + { + bus->i2c_smbus_write_word_data(dev, reg_value_1, word_value_1); + } + } + } + + /*-------------------------------------------------*\ + | Remember to delete the data buffer | + \*-------------------------------------------------*/ + delete[] direct_packet; + } + else + { + /*-------------------------------------------------*\ + | Local variables | + \*-------------------------------------------------*/ + unsigned char device_crc; + unsigned char calc_crc; + + /*-------------------------------------------------*\ + | Packet format: | + | Size n is (LED count * 4) | + | Format is 0xRR, 0xGG, 0xBB, 0xFF for each LED | + \*-------------------------------------------------*/ + unsigned int color_data_size = (led_count * 4); + unsigned char* color_data_packet = new unsigned char[color_data_size]; + + for(unsigned int led_idx = 0; led_idx < led_count; led_idx++) + { + unsigned int color_index = led_idx; + + if(corsair_dram_device_list[device_index]->reverse) + { + color_index = (led_count -1) - led_idx; + } + + color_data_packet[(led_idx * 4) + 0] = RGBGetRValue(colors[color_index]); + color_data_packet[(led_idx * 4) + 1] = RGBGetGValue(colors[color_index]); + color_data_packet[(led_idx * 4) + 2] = RGBGetBValue(colors[color_index]); + color_data_packet[(led_idx * 4) + 3] = 0xFF; + } + + /*-------------------------------------------------*\ + | Write LED color data packet | + \*-------------------------------------------------*/ + bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_RESET_BUFFER, 0x00); + bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_BINARY_START, 0x00); + + for(unsigned int i = 0; i < color_data_size; i++) + { + bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_SET_BINARY_DATA, color_data_packet[i]); + } + + /*-------------------------------------------------*\ + | Calculate CRC and read CRC reported by device | + \*-------------------------------------------------*/ + calc_crc = CRCPP::CRC::Calculate(color_data_packet, color_data_size, CRCPP::CRC::CRC_8()); + device_crc = bus->i2c_smbus_read_byte_data(dev, CORSAIR_DRAM_REG_GET_CHECKSUM); + + /*-------------------------------------------------*\ + | Write effect configuration only if CRCs match | + \*-------------------------------------------------*/ + if(calc_crc == device_crc) + { + bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_WRITE_CONFIGURATION, CORSAIR_DRAM_ID_COLOR_DATA); + WaitReady(); + } + + /*-------------------------------------------------*\ + | Remember to delete the data buffer | + \*-------------------------------------------------*/ + delete[] color_data_packet; + } +} + +void CorsairDRAMController::SetDirect(bool direct) +{ + direct_mode = direct; +} + +void CorsairDRAMController::SetEffect + ( + unsigned char mode, + unsigned char speed, + unsigned char direction, + bool random, + unsigned char brightness, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2 + ) +{ + /*-----------------------------------------------------*\ + | Local variables | + \*-----------------------------------------------------*/ + unsigned char effect_data[20]; + unsigned char device_crc; + unsigned char calc_crc; + unsigned char random_byte; + + /*-----------------------------------------------------*\ + | If mode is direct (which is a dummy value not | + | understood by the hardware), return. Direct mode is | + | not set in the effect configuration. | + \*-----------------------------------------------------*/ + direct_mode = (mode == CORSAIR_DRAM_MODE_DIRECT); + + if(direct_mode) + { + return; + } + + /*-----------------------------------------------------*\ + | Determine random byte | + \*-----------------------------------------------------*/ + if(random) + { + random_byte = CORSAIR_DRAM_EFFECT_RANDOM_COLORS; + } + else + { + random_byte = CORSAIR_DRAM_EFFECT_CUSTOM_COLORS; + } + + /*-----------------------------------------------------*\ + | Fill in effect packet | + \*-----------------------------------------------------*/ + effect_data[0] = mode; // Mode + effect_data[1] = speed; // Speed + effect_data[2] = random_byte; // Custom color + effect_data[3] = direction; // Direction + effect_data[4] = red1; // Custom color 1 red + effect_data[5] = grn1; // Custom color 1 green + effect_data[6] = blu1; // Custom color 1 blue + effect_data[7] = brightness; + effect_data[8] = red2; // Custom color 2 red + effect_data[9] = grn2; // Custom color 2 green + effect_data[10] = blu2; // Custom color 2 blue + effect_data[11] = brightness; + effect_data[12] = 0x00; + effect_data[13] = 0x00; + effect_data[14] = 0x00; + effect_data[15] = 0x00; + effect_data[16] = 0x00; + effect_data[17] = 0x00; + effect_data[18] = 0x00; + effect_data[19] = 0x00; + + /*-----------------------------------------------------*\ + | Write effect packet | + \*-----------------------------------------------------*/ + bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_RESET_BUFFER, 0x00); + bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_BINARY_START, 0x00); + + for(unsigned int i = 0; i < 20; i++) + { + bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_SET_BINARY_DATA, effect_data[i]); + } + + /*-----------------------------------------------------*\ + | Calculate CRC and read CRC reported by device | + \*-----------------------------------------------------*/ + calc_crc = CRCPP::CRC::Calculate(effect_data, sizeof(effect_data), CRCPP::CRC::CRC_8()); + device_crc = bus->i2c_smbus_read_byte_data(dev, CORSAIR_DRAM_REG_GET_CHECKSUM); + + /*-----------------------------------------------------*\ + | Write effect configuration only if CRCs match | + \*-----------------------------------------------------*/ + if(calc_crc == device_crc) + { + bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_WRITE_CONFIGURATION, CORSAIR_DRAM_ID_EFFECT_CONFIGURATION); + WaitReady(); + } +} + +bool CorsairDRAMController::WaitReady() +{ + /*-----------------------------------------------------*\ + | Poll status register 0x30; bit 3 (0x08) = busy. | + | Device is ready when bit 3 is clear. | + \*-----------------------------------------------------*/ + for(int retry = 0; retry < 5; retry++) + { + int status = bus->i2c_smbus_read_byte_data(dev, CORSAIR_DRAM_REG_STATUS); + + if(status >= 0 && (status & 0x08) == 0) + { + return true; + } + + std::this_thread::sleep_for(10ms); + } + + return false; +} + +void CorsairDRAMController::ReadDeviceInfo() +{ + unsigned char device_information_data[32]; + unsigned char device_crc; + unsigned char calc_crc; + + /*-----------------------------------------------------*\ + | Request Device Information Data | + \*-----------------------------------------------------*/ + bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_GET_DEVICE_INFO, 0x00); + bus->i2c_smbus_write_byte_data(dev, CORSAIR_DRAM_REG_BINARY_START, 0x00); + + /*-----------------------------------------------------*\ + | Read Device Information Data | + \*-----------------------------------------------------*/ + for(unsigned int i = 0; i < 32; i++) + { + device_information_data[i] = bus->i2c_smbus_read_byte_data(dev, CORSAIR_DRAM_REG_GET_BINARY_DATA); + } + + /*-----------------------------------------------------*\ + | Compare CRC | + \*-----------------------------------------------------*/ + calc_crc = CRCPP::CRC::Calculate(device_information_data, sizeof(device_information_data), CRCPP::CRC::CRC_8()); + device_crc = bus->i2c_smbus_read_byte_data(dev, CORSAIR_DRAM_REG_GET_CHECKSUM); + + if(calc_crc != device_crc) + { + LOG_ERROR("[%s] ReadDeviceInfo CRC Mismatch", CORSAIR_DRAM_NAME); + } + + /*-----------------------------------------------------*\ + | Log Device Information Data | + \*-----------------------------------------------------*/ + if(LogManager::get()->getLoglevel() >= LL_TRACE) + { + char device_info_buf[256]; + unsigned int pos; + + pos = snprintf(device_info_buf, sizeof(device_info_buf), "%02X: ", dev); + + for(unsigned int i = 0; i < 32; i++) + { + pos += snprintf(&device_info_buf[pos], sizeof(device_info_buf) - pos, "%02X ", device_information_data[i]); + } + + LOG_TRACE("[%s] Device Info: %s", CORSAIR_DRAM_NAME, device_info_buf); + } + + /*-----------------------------------------------------*\ + | Read VID | + \*-----------------------------------------------------*/ + vid = (device_information_data[1] << 8) | device_information_data[0]; + + /*-----------------------------------------------------*\ + | Read PID | + \*-----------------------------------------------------*/ + pid = (device_information_data[3] << 8) | device_information_data[2]; + + /*-----------------------------------------------------*\ + | Format Firwmare Version | + \*-----------------------------------------------------*/ + firmware_version = std::to_string(device_information_data[9]) + "." + std::to_string(device_information_data[8]) + "." + std::to_string((device_information_data[11] << 8) | device_information_data[10]); + + /*-----------------------------------------------------*\ + | Read Protocol Version | + \*-----------------------------------------------------*/ + protocol_version = device_information_data[28]; + + /*-----------------------------------------------------*\ + | Loop through all known devices to look for a PID | + | match | + \*-----------------------------------------------------*/ + for(unsigned int i = 0; i < CORSAIR_DRAM_NUM_DEVICES; i++) + { + for(unsigned int j = 0; j < CORSAIR_DRAM_MAX_PIDS; j++) + { + if(corsair_dram_device_list[i]->pids[j] == pid) + { + /*-----------------------------------------*\ + | Set device ID | + \*-----------------------------------------*/ + device_index = i; + break; + } + } + } +} diff --git a/Controllers/CorsairDRAMController/CorsairDRAMController.h b/Controllers/CorsairDRAMController/CorsairDRAMController.h new file mode 100644 index 0000000..ed8dc9c --- /dev/null +++ b/Controllers/CorsairDRAMController/CorsairDRAMController.h @@ -0,0 +1,149 @@ +/*---------------------------------------------------------*\ +| CorsairDRAMController.h | +| | +| Driver for Corsair DRAM RGB controllers | +| | +| Adam Honse (CalcProgrammer1) 30 Jun 2019 | +| Erik Gilling (konkers) 25 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "CorsairDRAMDevices.h" +#include "RGBController.h" + +typedef unsigned char corsair_dev_id; + +enum +{ /* (*) indicates deprecated registers no longer used by iCue */ + CORSAIR_DRAM_REG_RESET_BUFFER = 0x0B, /* Reset buffer by writing 0x00 */ + CORSAIR_DRAM_REG_SET_BINARY_DATA = 0x20, /* Write byte to active binary data buffer */ + CORSAIR_DRAM_REG_BINARY_START = 0x21, /* Start binary data transfer by writing 0x00 */ + CORSAIR_DRAM_REG_SWITCH_MODE = 0x23, /* Switch between Bootloader(0x00) and Normal(0x01) */ + CORSAIR_DRAM_REG_SET_BUFFER = 0x26, /* Select configuration buffer to write by ID (*) */ + CORSAIR_DRAM_REG_STATUS = 0x30, /* Status register */ + CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_1 = 0x31, /* Direct color buffer block register 1 */ + CORSAIR_DRAM_REG_COLOR_BUFFER_BLOCK_2 = 0x32, /* Direct color buffer block register 2 */ + CORSAIR_DRAM_REG_GET_BINARY_DATA = 0x40, /* Read byte from active binary data buffer */ + CORSAIR_DRAM_REG_BUSY_STATUS = 0x41, /* Reads nonzero while busy, zero when ready(*) */ + CORSAIR_DRAM_REG_GET_CHECKSUM = 0x42, /* Get checksum (CRC8) of active binary data buffer */ + CORSAIR_DRAM_REG_GET_DEVICE_INFO = 0x61, /* Select device info buffer */ + CORSAIR_DRAM_REG_GET_CONFIGURATION = 0x63, /* Select configuration buffer to read by ID */ + CORSAIR_DRAM_REG_WRITE_CONFIGURATION = 0x82, /* Write/Apply configuration by writing buffer ID */ +}; + +enum +{ + CORSAIR_DRAM_ID_COMMAND_LIST = 0, /* Command list */ + CORSAIR_DRAM_ID_EFFECT_CONFIGURATION = 1, /* Effect configuration */ + CORSAIR_DRAM_ID_COLOR_DATA = 2, /* Color data */ +}; + +enum +{ + CORSAIR_DRAM_MODE_DIRECT = 0xDD, /* Arbitrary value to compare against later. Not the actual packet */ + CORSAIR_DRAM_MODE_COLOR_SHIFT = 0x00, /* Color Shift mode */ + CORSAIR_DRAM_MODE_COLOR_PULSE = 0x01, /* Color Pulse mode */ + CORSAIR_DRAM_MODE_RAINBOW_WAVE = 0x03, /* Rainbow Wave mode */ + CORSAIR_DRAM_MODE_COLOR_WAVE = 0x04, /* Color Wave mode */ + CORSAIR_DRAM_MODE_VISOR = 0x05, /* Visor mode */ + CORSAIR_DRAM_MODE_RAIN = 0x06, /* Rain mode */ + CORSAIR_DRAM_MODE_MARQUEE = 0x07, /* Marquee mode */ + CORSAIR_DRAM_MODE_RAINBOW = 0x08, /* Rainbow mode */ + CORSAIR_DRAM_MODE_SEQUENTIAL = 0x09, /* Sequential mode */ + CORSAIR_DRAM_MODE_STATIC = 0x10, /* Static mode */ + + CORSAIR_DRAM_NUMBER_MODES = 10, /* Number of Corsair Pro modes */ +}; + +enum +{ + CORSAIR_DRAM_SPEED_SLOW = 0x00, /* Slow speed */ + CORSAIR_DRAM_SPEED_MEDIUM = 0x01, /* Medium speed */ + CORSAIR_DRAM_SPEED_FAST = 0x02, /* Fast speed */ +}; + +enum +{ + CORSAIR_DRAM_EFFECT_RANDOM_COLORS = 0x00, /* Random colors */ + CORSAIR_DRAM_EFFECT_CUSTOM_COLORS = 0x01, /* Custom colors */ +}; + +enum +{ + CORSAIR_DRAM_DIRECTION_UP = 0x00, /* Up direction */ + CORSAIR_DRAM_DIRECTION_DOWN = 0x01, /* Down direction */ + CORSAIR_DRAM_DIRECTION_LEFT = 0x02, /* Left direction */ + CORSAIR_DRAM_DIRECTION_RIGHT = 0x03, /* Right direction */ + CORSAIR_DRAM_DIRECTION_VERTICAL = 0x01, /* Vertical direction */ + CORSAIR_DRAM_DIRECTION_HORIZONTAL = 0x03, /* Horizontal direction */ +}; + +enum +{ + CORSAIR_DRAM_BRIGHTNESS_MIN = 0, /* Minimum brightness */ + CORSAIR_DRAM_BRIGHTNESS_MAX = 255, /* Maximum brightness */ + CORSAIR_DRAM_BRIGHTNESS_DEFAULT = 255, /* Default brightness */ +}; + +class CorsairDRAMController +{ +public: + CorsairDRAMController(i2c_smbus_interface *bus, corsair_dev_id dev); + ~CorsairDRAMController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetDeviceVersion(); + + unsigned int GetLEDCount(); + unsigned char GetProtocolVersion(); + + void SetColorsPerLED(RGBColor* colors); + void SetDirect(bool direct); + void SetEffect(unsigned char mode, + unsigned char speed, + unsigned char direction, + bool random, + unsigned char brightness, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2); + + bool WaitReady(); + +private: + /*-----------------------------------------------------*\ + | I2C | + \*-----------------------------------------------------*/ + i2c_smbus_interface* bus; + corsair_dev_id dev; + + /*-----------------------------------------------------*\ + | State tracking | + \*-----------------------------------------------------*/ + bool direct_mode; + + /*-----------------------------------------------------*\ + | Corsair DRAM information | + \*-----------------------------------------------------*/ + unsigned short vid; + unsigned short pid; + std::string firmware_version; + unsigned char protocol_version; + + unsigned int device_index; + + /*-----------------------------------------------------*\ + | Private functions | + \*-----------------------------------------------------*/ + void ReadDeviceInfo(); +}; diff --git a/Controllers/CorsairDRAMController/CorsairDRAMControllerDetect.cpp b/Controllers/CorsairDRAMController/CorsairDRAMControllerDetect.cpp new file mode 100644 index 0000000..cbda9a1 --- /dev/null +++ b/Controllers/CorsairDRAMController/CorsairDRAMControllerDetect.cpp @@ -0,0 +1,89 @@ +/*---------------------------------------------------------*\ +| CorsairDRAMControllerDetect.cpp | +| | +| Detector for Corsair DRAM RGB controllers | +| | +| Adam Honse (CalcProgrammer1) 30 Jun 2019 | +| Erik Gilling (konkers) 25 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "CorsairDRAMController.h" +#include "RGBController_CorsairDRAM.h" +#include "LogManager.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +using namespace std::chrono_literals; + +#define CORSAIR_DRAM_NAME "Corsair DRAM" + +bool TestForCorsairDRAMController(i2c_smbus_interface *bus, unsigned char address) +{ + LOG_DEBUG("[%s] Trying address %02X", CORSAIR_DRAM_NAME, address); + + int res = bus->i2c_smbus_read_byte_data(address, 0x43); + + if(res < 0) + { + return false; + } + + if(!(res == 0x1A || res == 0x1B || res == 0x1C)) + { + LOG_DEBUG("[%s] Failed: expected 0x1A, 0x1B, or 0x1C, got %04X", CORSAIR_DRAM_NAME, res); + return false; + } + + res = bus->i2c_smbus_read_byte_data(address, 0x44); + + if(!(res == 0x01 || res == 0x03 || res == 0x04)) + { + LOG_DEBUG("[%s] Failed: expected 0x01, 0x03, or 0x04, got %04X", CORSAIR_DRAM_NAME, res); + return false; + } + + return true; +} + +void DetectCorsairDRAMControllers(std::vector &busses) +{ + for(unsigned int bus = 0; bus < busses.size(); bus++) + { + IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + LOG_DEBUG("[%s] Testing bus %d", CORSAIR_DRAM_NAME, bus); + + std::vector addresses; + + for(unsigned char addr = 0x58; addr <= 0x5F; addr++) + { + addresses.push_back(addr); + } + + for(unsigned char addr = 0x18; addr <= 0x1F; addr++) + { + addresses.push_back(addr); + } + + for(unsigned char addr : addresses) + { + if(TestForCorsairDRAMController(busses[bus], addr)) + { + CorsairDRAMController* controller = new CorsairDRAMController(busses[bus], addr); + RGBController_CorsairDRAM* rgb_controller = new RGBController_CorsairDRAM(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + + std::this_thread::sleep_for(10ms); + } + } + } +} + +REGISTER_I2C_DETECTOR(CORSAIR_DRAM_NAME, DetectCorsairDRAMControllers); diff --git a/Controllers/CorsairDRAMController/CorsairDRAMDevices.cpp b/Controllers/CorsairDRAMController/CorsairDRAMDevices.cpp new file mode 100644 index 0000000..2ea1cdd --- /dev/null +++ b/Controllers/CorsairDRAMController/CorsairDRAMDevices.cpp @@ -0,0 +1,163 @@ +/*---------------------------------------------------------*\ +| CorsairDRAMDevices.cpp | +| | +| Device list for Corsair DRAM RGB controllers | +| | +| Adam Honse (CalcProgrammer1) 07 Apr 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CorsairDRAMDevices.h" + +static const corsair_dram_device corsair_vengeance_pro_ddr4_device = +{ + "Corsair Vengeance RGB Pro DDR4", + { + CORSAIR_VENGEANCE_PRO_DDR4_PID_1, + CORSAIR_VENGEANCE_PRO_DDR4_PID_2, + 0, + 0, + 0, + 0, + }, + 10, + false +}; + +static const corsair_dram_device corsair_dominator_platinum_ddr4_device = +{ + "Corsair Dominator Platinum RGB DDR4", + { + CORSAIR_DOMINATOR_PLATINUM_DDR4_PID_1, + CORSAIR_DOMINATOR_PLATINUM_DDR4_PID_2, + 0, + 0, + 0, + 0, + }, + 12, + true +}; + +static const corsair_dram_device corsair_vengeance_pro_sl_ddr4_device = +{ + "Corsair Vengeance RGB Pro SL DDR4", + { + CORSAIR_VENGEANCE_PRO_SL_DDR4_PID_1, + CORSAIR_VENGEANCE_PRO_SL_DDR4_PID_2, + 0, + 0, + 0, + 0, + }, + 10, + false +}; + +static const corsair_dram_device corsair_vengeance_rs_ddr4_device = +{ + "Corsair Vengeance RGB RS DDR4", + { + CORSAIR_VENGEANCE_RS_DDR4_PID_1, + CORSAIR_VENGEANCE_RS_DDR4_PID_2, + 0, + 0, + 0, + 0, + }, + 6, + false +}; + +static const corsair_dram_device corsair_dominator_platinum_ddr5_device = +{ + "Corsair Dominator Platinum RGB DDR5", + { + CORSAIR_DOMINATOR_PLATINUM_DDR5_PID_1, + CORSAIR_DOMINATOR_PLATINUM_DDR5_PID_2, + 0, + 0, + 0, + 0, + }, + 12, + true +}; + +static const corsair_dram_device corsair_dominator_titanium_ddr5_device = +{ + "Corsair Dominator Titanium RGB DDR5", + { + CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_1, + CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_2, + CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_3, + CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_4, + 0, + 0, + }, + 12, + true +}; + +static const corsair_dram_device corsair_vengeance_ddr5_device = +{ + "Corsair Vengeance RGB DDR5", + { + CORSAIR_VENGEANCE_DDR5_PID_1, + CORSAIR_VENGEANCE_DDR5_PID_2, + CORSAIR_VENGEANCE_DDR5_PID_3, + CORSAIR_VENGEANCE_DDR5_PID_4, + CORSAIR_VENGEANCE_DDR5_PID_5, + CORSAIR_VENGEANCE_DDR5_PID_6, + }, + 10, + false +}; + +static const corsair_dram_device corsair_vengeance_shugo_series_ddr5_device = +{ + "Corsair Vengeance Shugo Series DDR5", + { + CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_1, + CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_2, + CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_3, + CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_4, + 0, + 0, + }, + 10, + false +}; + +static const corsair_dram_device corsair_vengeance_rs_ddr5_device = +{ + "Corsair Vengeance RGB RS DDR5", + { + CORSAIR_VENGEANCE_RS_DDR5_PID_1, + CORSAIR_VENGEANCE_RS_DDR5_PID_2, + 0, + 0, + 0, + 0, + }, + 6, + false +}; + +static const corsair_dram_device* device_list[] = +{ + &corsair_vengeance_pro_ddr4_device, + &corsair_dominator_platinum_ddr4_device, + &corsair_vengeance_pro_sl_ddr4_device, + &corsair_vengeance_rs_ddr4_device, + &corsair_dominator_platinum_ddr5_device, + &corsair_dominator_titanium_ddr5_device, + &corsair_vengeance_ddr5_device, + &corsair_vengeance_shugo_series_ddr5_device, + &corsair_vengeance_rs_ddr5_device, +}; + +const unsigned int CORSAIR_DRAM_NUM_DEVICES = (sizeof(device_list) / sizeof(device_list[ 0 ])); +const corsair_dram_device** corsair_dram_device_list = device_list; diff --git a/Controllers/CorsairDRAMController/CorsairDRAMDevices.h b/Controllers/CorsairDRAMController/CorsairDRAMDevices.h new file mode 100644 index 0000000..5482230 --- /dev/null +++ b/Controllers/CorsairDRAMController/CorsairDRAMDevices.h @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| CorsairDRAMDevices.h | +| | +| Device list for Corsair DRAM RGB controllers | +| | +| Adam Honse (CalcProgrammer1) 07 Apr 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +/*---------------------------------------------------------*\ +| Maximum number of PIDs for a given DRAM model | +\*---------------------------------------------------------*/ +#define CORSAIR_DRAM_MAX_PIDS 6 + +/*---------------------------------------------------------*\ +| Corsair DRAM vendor ID | +\*---------------------------------------------------------*/ +#define CORSAIR_DRAM_VID 0x1B1C + +/*---------------------------------------------------------*\ +| Corsair DRAM product IDs | +\*---------------------------------------------------------*/ +#define CORSAIR_VENGEANCE_PRO_DDR4_PID_1 0x0100 +#define CORSAIR_VENGEANCE_PRO_DDR4_PID_2 0x0101 +#define CORSAIR_DOMINATOR_PLATINUM_DDR4_PID_1 0x0200 +#define CORSAIR_DOMINATOR_PLATINUM_DDR4_PID_2 0x0201 +#define CORSAIR_VENGEANCE_PRO_SL_DDR4_PID_1 0x0300 +#define CORSAIR_VENGEANCE_PRO_SL_DDR4_PID_2 0x0301 +#define CORSAIR_VENGEANCE_RS_DDR4_PID_1 0x0400 +#define CORSAIR_VENGEANCE_RS_DDR4_PID_2 0x0401 +#define CORSAIR_DOMINATOR_PLATINUM_DDR5_PID_1 0x0600 +#define CORSAIR_DOMINATOR_PLATINUM_DDR5_PID_2 0x0601 +#define CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_1 0x0800 +#define CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_2 0x0801 +#define CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_3 0x0810 +#define CORSAIR_DOMINATOR_TITANIUM_DDR5_PID_4 0x0811 +#define CORSAIR_VENGEANCE_DDR5_PID_1 0x0700 +#define CORSAIR_VENGEANCE_DDR5_PID_2 0x0701 +#define CORSAIR_VENGEANCE_DDR5_PID_3 0x0900 +#define CORSAIR_VENGEANCE_DDR5_PID_4 0x0901 +#define CORSAIR_VENGEANCE_DDR5_PID_5 0x0910 +#define CORSAIR_VENGEANCE_DDR5_PID_6 0x0911 +#define CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_1 0x0A00 +#define CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_2 0x0A01 +#define CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_3 0x0A10 +#define CORSAIR_VENGEANCE_SHUGO_SERIES_DDR5_PID_4 0x0A11 +#define CORSAIR_VENGEANCE_RS_DDR5_PID_1 0x0B00 +#define CORSAIR_VENGEANCE_RS_DDR5_PID_2 0x0B01 + +typedef struct +{ + std::string name; + unsigned short pids[CORSAIR_DRAM_MAX_PIDS]; + unsigned int led_count; + bool reverse; +} corsair_dram_device; + +/*-----------------------------------------------------*\ +| These constant values are defined in RazerDevices.cpp | +\*-----------------------------------------------------*/ +extern const unsigned int CORSAIR_DRAM_NUM_DEVICES; +extern const corsair_dram_device** corsair_dram_device_list; diff --git a/Controllers/CorsairDRAMController/RGBController_CorsairDRAM.cpp b/Controllers/CorsairDRAMController/RGBController_CorsairDRAM.cpp new file mode 100644 index 0000000..c0904b2 --- /dev/null +++ b/Controllers/CorsairDRAMController/RGBController_CorsairDRAM.cpp @@ -0,0 +1,330 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairDRAM.cpp | +| | +| RGBController for Corsair DRAM RGB controllers | +| | +| Adam Honse (CalcProgrammer1) 30 Jun 2019 | +| Erik Gilling (konkers) 25 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CorsairDRAM.h" + +/**------------------------------------------------------------------*\ + @name Corsair DRAM + @category RAM + @type SMBus + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectCorsairDRAMControllers + @comment + The Corsair DRAM RGB controller chip can be found on several + Corsair memory sticks which have different LED counts. This can be controlled + by editing the Part Number in OpenRGB.json with values in the below table. + + | Part Number | LED Count | + | :---------: | --------: | + | CMG | 6 | + | CMH | 10 | + | CMN | 10 | + | CMT | 12 | +\*-------------------------------------------------------------------*/ + +RGBController_CorsairDRAM::RGBController_CorsairDRAM(CorsairDRAMController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Corsair"; + type = DEVICE_TYPE_DRAM; + description = "Corsair DRAM RGB Device"; + location = controller->GetDeviceLocation(); + version = controller->GetDeviceVersion(); + + if(controller->GetProtocolVersion() >= 4) + { + mode Direct; + Direct.name = "Direct"; + Direct.value = CORSAIR_DRAM_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + } + + mode Custom; + Custom.name = "Custom"; + Custom.value = CORSAIR_DRAM_MODE_STATIC; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode ColorShift; + ColorShift.name = "Color Shift"; + ColorShift.value = CORSAIR_DRAM_MODE_COLOR_SHIFT; + ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorShift.speed_min = CORSAIR_DRAM_SPEED_SLOW; + ColorShift.speed_max = CORSAIR_DRAM_SPEED_FAST; + ColorShift.speed = CORSAIR_DRAM_SPEED_SLOW; + ColorShift.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN; + ColorShift.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX; + ColorShift.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT; + ColorShift.colors_min = 2; + ColorShift.colors_max = 2; + ColorShift.colors.resize(2); + modes.push_back(ColorShift); + + mode ColorPulse; + ColorPulse.name = "Color Pulse"; + ColorPulse.value = CORSAIR_DRAM_MODE_COLOR_PULSE; + ColorPulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorPulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorPulse.speed_min = CORSAIR_DRAM_SPEED_SLOW; + ColorPulse.speed_max = CORSAIR_DRAM_SPEED_FAST; + ColorPulse.speed = CORSAIR_DRAM_SPEED_SLOW; + ColorPulse.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN; + ColorPulse.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX; + ColorPulse.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT; + ColorPulse.colors_min = 2; + ColorPulse.colors_max = 2; + ColorPulse.colors.resize(2); + modes.push_back(ColorPulse); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = CORSAIR_DRAM_MODE_RAINBOW_WAVE; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_AUTOMATIC_SAVE; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.speed_min = CORSAIR_DRAM_SPEED_SLOW; + RainbowWave.speed_max = CORSAIR_DRAM_SPEED_FAST; + RainbowWave.speed = CORSAIR_DRAM_SPEED_SLOW; + RainbowWave.direction = MODE_DIRECTION_DOWN; + modes.push_back(RainbowWave); + + mode ColorWave; + ColorWave.name = "Color Wave"; + ColorWave.value = CORSAIR_DRAM_MODE_COLOR_WAVE; + ColorWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorWave.speed_min = CORSAIR_DRAM_SPEED_SLOW; + ColorWave.speed_max = CORSAIR_DRAM_SPEED_FAST; + ColorWave.speed = CORSAIR_DRAM_SPEED_SLOW; + ColorWave.direction = MODE_DIRECTION_DOWN; + ColorWave.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN; + ColorWave.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX; + ColorWave.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT; + ColorWave.colors_min = 2; + ColorWave.colors_max = 2; + ColorWave.colors.resize(2); + modes.push_back(ColorWave); + + mode Visor; + Visor.name = "Visor"; + Visor.value = CORSAIR_DRAM_MODE_VISOR; + Visor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Visor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Visor.speed_min = CORSAIR_DRAM_SPEED_SLOW; + Visor.speed_max = CORSAIR_DRAM_SPEED_FAST; + Visor.speed = CORSAIR_DRAM_SPEED_SLOW; + Visor.direction = MODE_DIRECTION_VERTICAL; + Visor.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN; + Visor.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX; + Visor.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT; + Visor.colors_min = 2; + Visor.colors_max = 2; + Visor.colors.resize(2); + modes.push_back(Visor); + + mode Rain; + Rain.name = "Rain"; + Rain.value = CORSAIR_DRAM_MODE_RAIN; + Rain.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Rain.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rain.speed_min = CORSAIR_DRAM_SPEED_SLOW; + Rain.speed_max = CORSAIR_DRAM_SPEED_FAST; + Rain.speed = CORSAIR_DRAM_SPEED_SLOW; + Rain.direction = MODE_DIRECTION_DOWN; + Rain.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN; + Rain.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX; + Rain.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT; + Rain.colors_min = 2; + Rain.colors_max = 2; + Rain.colors.resize(2); + modes.push_back(Rain); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = CORSAIR_DRAM_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + Marquee.speed_min = CORSAIR_DRAM_SPEED_SLOW; + Marquee.speed_max = CORSAIR_DRAM_SPEED_FAST; + Marquee.speed = CORSAIR_DRAM_SPEED_SLOW; + Marquee.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN; + Marquee.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX; + Marquee.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT; + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.colors.resize(1); + modes.push_back(Marquee); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = CORSAIR_DRAM_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed_min = CORSAIR_DRAM_SPEED_SLOW; + Rainbow.speed_max = CORSAIR_DRAM_SPEED_FAST; + Rainbow.speed = CORSAIR_DRAM_SPEED_SLOW; + modes.push_back(Rainbow); + + mode Sequential; + Sequential.name = "Sequential"; + Sequential.value = CORSAIR_DRAM_MODE_SEQUENTIAL; + Sequential.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Sequential.color_mode = MODE_COLORS_MODE_SPECIFIC; + Sequential.speed_min = CORSAIR_DRAM_SPEED_SLOW; + Sequential.speed_max = CORSAIR_DRAM_SPEED_FAST; + Sequential.speed = CORSAIR_DRAM_SPEED_SLOW; + Sequential.direction = MODE_DIRECTION_DOWN; + Sequential.brightness_min = CORSAIR_DRAM_BRIGHTNESS_MIN; + Sequential.brightness_max = CORSAIR_DRAM_BRIGHTNESS_MAX; + Sequential.brightness = CORSAIR_DRAM_BRIGHTNESS_DEFAULT; + Sequential.colors_min = 1; + Sequential.colors_max = 1; + Sequential.colors.resize(1); + modes.push_back(Sequential); + + SetupZones(); +} + +RGBController_CorsairDRAM::~RGBController_CorsairDRAM() +{ + delete controller; +} + +void RGBController_CorsairDRAM::SetupZones() +{ + /*-----------------------------------------------------*\ + | Set up zone | + \*-----------------------------------------------------*/ + zone new_zone; + new_zone.name = "Corsair DRAM"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = controller->GetLEDCount(); + new_zone.leds_max = controller->GetLEDCount(); + new_zone.leds_count = controller->GetLEDCount(); + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + /*-----------------------------------------------------*\ + | Set up LEDs | + \*-----------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led new_led; + new_led.name = "Corsair DRAM LED "; + new_led.name.append(std::to_string(led_idx)); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_CorsairDRAM::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*-----------------------------------------------------*\ + | This device does not support resizing zones | + \*-----------------------------------------------------*/ +} + +void RGBController_CorsairDRAM::DeviceUpdateLEDs() +{ + controller->SetColorsPerLED(colors.data()); +} + +void RGBController_CorsairDRAM::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairDRAM::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairDRAM::DeviceUpdateMode() +{ + unsigned int corsair_direction = 0; + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + unsigned char mode_colors[6]; + + switch(modes[active_mode].direction) + { + case MODE_DIRECTION_LEFT: + corsair_direction = CORSAIR_DRAM_DIRECTION_LEFT; + break; + case MODE_DIRECTION_RIGHT: + corsair_direction = CORSAIR_DRAM_DIRECTION_RIGHT; + break; + case MODE_DIRECTION_UP: + corsair_direction = CORSAIR_DRAM_DIRECTION_UP; + break; + case MODE_DIRECTION_DOWN: + corsair_direction = CORSAIR_DRAM_DIRECTION_DOWN; + break; + case MODE_DIRECTION_HORIZONTAL: + corsair_direction = CORSAIR_DRAM_DIRECTION_HORIZONTAL; + break; + case MODE_DIRECTION_VERTICAL: + corsair_direction = CORSAIR_DRAM_DIRECTION_VERTICAL; + break; + } + + mode_colors[0] = 0; + mode_colors[1] = 0; + mode_colors[2] = 0; + mode_colors[3] = 0; + mode_colors[4] = 0; + mode_colors[5] = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + mode_colors[0] = RGBGetRValue(modes[active_mode].colors[0]); + mode_colors[1] = RGBGetGValue(modes[active_mode].colors[0]); + mode_colors[2] = RGBGetBValue(modes[active_mode].colors[0]); + + if(modes[active_mode].colors.size() == 2) + { + mode_colors[3] = RGBGetRValue(modes[active_mode].colors[1]); + mode_colors[4] = RGBGetGValue(modes[active_mode].colors[1]); + mode_colors[5] = RGBGetBValue(modes[active_mode].colors[1]); + } + } + + if(modes[active_mode].name == "Direct") + { + controller->SetDirect(true); + } + else + { + controller->SetDirect(false); + } + + controller->SetEffect(modes[active_mode].value, + modes[active_mode].speed, + corsair_direction, + random, + (unsigned char)modes[active_mode].brightness, + mode_colors[0], + mode_colors[1], + mode_colors[2], + mode_colors[3], + mode_colors[4], + mode_colors[5]); + + std::this_thread::sleep_for(std::chrono::milliseconds(15)); +} diff --git a/Controllers/CorsairDRAMController/RGBController_CorsairDRAM.h b/Controllers/CorsairDRAMController/RGBController_CorsairDRAM.h new file mode 100644 index 0000000..56b3ea2 --- /dev/null +++ b/Controllers/CorsairDRAMController/RGBController_CorsairDRAM.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairDRAM.h | +| | +| RGBController for Corsair DRAM RGB controllers | +| | +| Adam Honse (CalcProgrammer1) 30 Jun 2019 | +| Erik Gilling (konkers) 25 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairDRAMController.h" + +class RGBController_CorsairDRAM : public RGBController +{ +public: + RGBController_CorsairDRAM(CorsairDRAMController* controller_ptr); + ~RGBController_CorsairDRAM(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CorsairDRAMController* controller; +}; diff --git a/Controllers/CorsairHydro2Controller/CorsairHydro2Controller.cpp b/Controllers/CorsairHydro2Controller/CorsairHydro2Controller.cpp new file mode 100644 index 0000000..6c1367c --- /dev/null +++ b/Controllers/CorsairHydro2Controller/CorsairHydro2Controller.cpp @@ -0,0 +1,94 @@ +/*---------------------------------------------------------*\ +| CorsairHydro2Controller.cpp | +| | +| Driver for Corsair H100i v2 | +| | +| Tim Demand (tim.dmd) 10 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "CorsairHydro2Controller.h" + +CorsairHydro2Controller::CorsairHydro2Controller(libusb_device_handle* dev_handle) +{ + dev = dev_handle; + + libusb_device_descriptor descriptor; + libusb_get_device_descriptor(libusb_get_device(dev_handle), &descriptor); + + std::stringstream location_stream; + location_stream << std::hex << std::setfill('0') << std::setw(4) << descriptor.idVendor << ":" << std::hex << std::setfill('0') << std::setw(4) << descriptor.idProduct; + location = location_stream.str(); + + SendInit(); +} + +CorsairHydro2Controller::~CorsairHydro2Controller() +{ + if(dev) + { + libusb_close(dev); + } +} + +std::string CorsairHydro2Controller::GetLocation() +{ + return("USB: " + location); +} + +void CorsairHydro2Controller::SetLED(std::vector& colors) +{ + unsigned char usb_buf[32]; + memset(usb_buf, 0, sizeof(usb_buf)); + int actual; + + unsigned char led_enable = 0x01; + + unsigned char rr = RGBGetRValue(colors[0]); + unsigned char gg = RGBGetBValue(colors[0]); + unsigned char bb = RGBGetGValue(colors[0]); + + if((rr + gg + bb) == 0) + { + led_enable = 0x00; // needed because leds won't turn off completely if color is 00 00 00 + } + + usb_buf[0] = 0x10; + usb_buf[1] = rr; + usb_buf[2] = bb; + usb_buf[3] = gg; + usb_buf[4] = 0x00; + usb_buf[5] = 0xFF; + usb_buf[6] = 0xFF; + usb_buf[7] = 0xFF; + usb_buf[8] = 0xFF; + usb_buf[9] = 0xFF; + usb_buf[10] = 0x00; + usb_buf[11] = 0x0A; + usb_buf[12] = 0x05; + usb_buf[13] = led_enable; + usb_buf[14] = 0x00; + usb_buf[15] = 0x00; + usb_buf[16] = 0x00; + usb_buf[17] = 0x00; + usb_buf[18] = 0x01; + + libusb_bulk_transfer(dev, 0x02, usb_buf, 19, &actual, 1000); + libusb_bulk_transfer(dev, 0x82, usb_buf, 32, &actual, 1000); + if(actual != 32) SendInit(); // reinitialization after sleep +} + +void CorsairHydro2Controller::SendInit() +{ + libusb_reset_device(dev); // needed for reinitialization after sleep + + libusb_control_transfer(dev, 0x40, 0, 0xffff, 0x0000, NULL, 0, 0); + libusb_control_transfer(dev, 0x40, 2, 0x0002, 0x0000, NULL, 0, 0); + libusb_control_transfer(dev, 0x40, 1, 0x0002, 0x0000, NULL, 0, 0); + libusb_control_transfer(dev, 0x40, 4, 0x0002, 0x0000, NULL, 0, 0); +} diff --git a/Controllers/CorsairHydro2Controller/CorsairHydro2Controller.h b/Controllers/CorsairHydro2Controller/CorsairHydro2Controller.h new file mode 100644 index 0000000..9955ef9 --- /dev/null +++ b/Controllers/CorsairHydro2Controller/CorsairHydro2Controller.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| CorsairHydro2Controller.h | +| | +| Driver for Corsair H100i v2 | +| | +| Tim Demand (tim.dmd) 10 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class CorsairHydro2Controller +{ +public: + CorsairHydro2Controller(libusb_device_handle* dev_handle); + ~CorsairHydro2Controller(); + + std::string GetLocation(); + + void SetLED(std::vector& colors); + +private: + libusb_device_handle* dev; + std::string firmware_version; + std::string location; + + void SendInit(); +}; diff --git a/Controllers/CorsairHydro2Controller/CorsairHydro2ControllerDetect.cpp b/Controllers/CorsairHydro2Controller/CorsairHydro2ControllerDetect.cpp new file mode 100644 index 0000000..d301075 --- /dev/null +++ b/Controllers/CorsairHydro2Controller/CorsairHydro2ControllerDetect.cpp @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| CorsairHydro2ControllerDetect.cpp | +| | +| Detector for Corsair H100i v2 | +| | +| Tim Demand (tim.dmd) 10 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "CorsairHydro2Controller.h" +#include "RGBController_CorsairHydro2.h" + +#define CORSAIR_VID 0x1B1C +#define H100I_V2_PID 0x0C09 + +void DetectCorsairHydro2Controllers() +{ + libusb_init(NULL); + + #ifdef _WIN32 + libusb_set_option(NULL, LIBUSB_OPTION_USE_USBDK); + #endif + + libusb_device_handle* dev = libusb_open_device_with_vid_pid(NULL, CORSAIR_VID, H100I_V2_PID); + + if(dev) + { + libusb_detach_kernel_driver(dev, 0); + libusb_claim_interface(dev, 0); + + CorsairHydro2Controller* controller = new CorsairHydro2Controller(dev); + RGBController_CorsairHydro2* rgb_controller = new RGBController_CorsairHydro2(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_DETECTOR("Corsair H100i v2", DetectCorsairHydro2Controllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("Corsair H100i v2", DetectCorsairHydro2Controllers, 0x1B1C, 0x0C09 ) | +\*---------------------------------------------------------------------------------------------------------*/ diff --git a/Controllers/CorsairHydro2Controller/RGBController_CorsairHydro2.cpp b/Controllers/CorsairHydro2Controller/RGBController_CorsairHydro2.cpp new file mode 100644 index 0000000..8e258b2 --- /dev/null +++ b/Controllers/CorsairHydro2Controller/RGBController_CorsairHydro2.cpp @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairHydro2.cpp | +| | +| RGBController for Corsair H100i v2 | +| | +| Tim Demand (tim.dmd) 10 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CorsairHydro2.h" + +/**------------------------------------------------------------------*\ + @name Corsair Hydro Series H100i v2 AIO + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectCorsairHydro2Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CorsairHydro2::RGBController_CorsairHydro2(CorsairHydro2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = "Corsair H100i v2"; + vendor = "Corsair"; + description = "Corsair H100i v2"; + type = DEVICE_TYPE_COOLER; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_CorsairHydro2::~RGBController_CorsairHydro2() +{ + delete controller; +} + +void RGBController_CorsairHydro2::SetupZones() +{ + zone new_zone; + + new_zone.name = "Pump Zone"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + led new_led; + + new_led.name = "Pump LED"; + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_CorsairHydro2::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CorsairHydro2::DeviceUpdateLEDs() +{ + controller->SetLED(colors); +} + +void RGBController_CorsairHydro2::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLED(colors); +} + +void RGBController_CorsairHydro2::UpdateSingleLED(int /*led*/) +{ + controller->SetLED(colors); +} + +void RGBController_CorsairHydro2::DeviceUpdateMode() +{ + +} diff --git a/Controllers/CorsairHydro2Controller/RGBController_CorsairHydro2.h b/Controllers/CorsairHydro2Controller/RGBController_CorsairHydro2.h new file mode 100644 index 0000000..ece8952 --- /dev/null +++ b/Controllers/CorsairHydro2Controller/RGBController_CorsairHydro2.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairHydro2.h | +| | +| RGBController for Corsair H100i v2 | +| | +| Tim Demand (tim.dmd) 10 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairHydro2Controller.h" + +class RGBController_CorsairHydro2 : public RGBController +{ +public: + RGBController_CorsairHydro2(CorsairHydro2Controller* controller_ptr); + ~RGBController_CorsairHydro2(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CorsairHydro2Controller* controller; +}; diff --git a/Controllers/CorsairHydroController/CorsairHydroController.cpp b/Controllers/CorsairHydroController/CorsairHydroController.cpp new file mode 100644 index 0000000..0c492d6 --- /dev/null +++ b/Controllers/CorsairHydroController/CorsairHydroController.cpp @@ -0,0 +1,286 @@ +/*---------------------------------------------------------*\ +| CorsairHydroController.cpp | +| | +| Driver for Corsair Hydro Series coolers | +| | +| Adam Honse (calcprogrammer1@gmail.com) 17 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "CorsairHydroController.h" + +CorsairHydroController::CorsairHydroController(libusb_device_handle* dev_handle, std::string dev_name) +{ + dev = dev_handle; + name = dev_name; + + /*-----------------------------------------------------*\ + | Fill in location string with USB ID | + \*-----------------------------------------------------*/ + libusb_device_descriptor descriptor; + libusb_get_device_descriptor(libusb_get_device(dev_handle), &descriptor); + + std::stringstream location_stream; + location_stream << std::hex << std::setfill('0') << std::setw(4) << descriptor.idVendor << ":" << std::hex << std::setfill('0') << std::setw(4) << descriptor.idProduct; + location = location_stream.str(); + + SendInit(); + + SendFirmwareRequest(); +} + +CorsairHydroController::~CorsairHydroController() +{ + if(dev) + { + libusb_close(dev); + } +} + +std::string CorsairHydroController::GetFirmwareString() +{ + return(firmware_version); +} + +std::string CorsairHydroController::GetLocation() +{ + return("USB: " + location); +} + +std::string CorsairHydroController::GetNameString() +{ + return(name); +} + +void CorsairHydroController::SetBlink + ( + std::vector & colors, + unsigned char speed + ) +{ + SendColors(colors); + SendSpeed(speed); + SendApplyBlink(); +} + +void CorsairHydroController::SetFixed + ( + std::vector & colors + ) +{ + SendColors(colors); + + /*-----------------------------------------------------*\ + | Fixed mode seems to just be shift mode with the same | + | value for both colors | + \*-----------------------------------------------------*/ + SendApplyShift(); +} + +void CorsairHydroController::SetPulse + ( + std::vector & colors, + unsigned char speed + ) +{ + SendColors(colors); + SendSpeed(speed); + SendApplyPulse(); +} + +void CorsairHydroController::SetShift + ( + std::vector & colors, + unsigned char speed + ) +{ + SendColors(colors); + SendSpeed(speed); + SendApplyShift(); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void CorsairHydroController::SendApplyBlink() +{ + unsigned char usb_buf[3]; + int actual; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Apply Blink packet | + \*-----------------------------------------------------*/ + usb_buf[0] = 0x58; + usb_buf[1] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + libusb_bulk_transfer(dev, 0x01, usb_buf, 2, &actual, 1000); + libusb_bulk_transfer(dev, 0x81, usb_buf, 3, &actual, 1000); +} + +void CorsairHydroController::SendApplyPulse() +{ + unsigned char usb_buf[3]; + int actual; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Apply Pulse packet | + \*-----------------------------------------------------*/ + usb_buf[0] = 0x52; + usb_buf[1] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + libusb_bulk_transfer(dev, 0x01, usb_buf, 2, &actual, 1000); + libusb_bulk_transfer(dev, 0x81, usb_buf, 3, &actual, 1000); +} + +void CorsairHydroController::SendApplyShift() +{ + unsigned char usb_buf[3]; + int actual; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Apply Shift packet | + \*-----------------------------------------------------*/ + usb_buf[0] = 0x55; + usb_buf[1] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + libusb_bulk_transfer(dev, 0x01, usb_buf, 2, &actual, 1000); + libusb_bulk_transfer(dev, 0x81, usb_buf, 3, &actual, 1000); +} + +void CorsairHydroController::SendFirmwareRequest() +{ + unsigned char usb_buf[8]; + int actual; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Firmware Request packet | + \*-----------------------------------------------------*/ + usb_buf[0] = 0xAA; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + libusb_bulk_transfer(dev, 0x01, usb_buf, 1, &actual, 1000); + libusb_bulk_transfer(dev, 0x81, usb_buf, 7, &actual, 1000); + + firmware_version = std::to_string(usb_buf[3]) + "." + std::to_string(usb_buf[4]) + "." + std::to_string(usb_buf[5]) + "." + std::to_string(usb_buf[6]); +} + +void CorsairHydroController::SendColors + ( + std::vector & colors + ) +{ + unsigned char usb_buf[23]; + int actual; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Send Colors packet | + \*-----------------------------------------------------*/ + usb_buf[0] = 0x56; + usb_buf[1] = (unsigned char)colors.size(); + + /*---------------------------------------------------------*\ + | Fill in colors from vector | + \*---------------------------------------------------------*/ + for(std::size_t color_idx = 0; color_idx < colors.size(); color_idx++) + { + usb_buf[(color_idx * 3) + 2] = RGBGetRValue(colors[color_idx]); + usb_buf[(color_idx * 3) + 3] = RGBGetGValue(colors[color_idx]); + usb_buf[(color_idx * 3) + 4] = RGBGetBValue(colors[color_idx]); + + /*---------------------------------------------------------*\ + | If the color vector only has one entry, duplicate it, as | + | the controller appears to require two colors in order to | + | update. | + \*---------------------------------------------------------*/ + if((color_idx == 0) && colors.size() == 1) + { + usb_buf[1] = (unsigned char)colors.size() + 1; + usb_buf[(color_idx * 3) + 5] = RGBGetRValue(colors[color_idx]); + usb_buf[(color_idx * 3) + 6] = RGBGetGValue(colors[color_idx]); + usb_buf[(color_idx * 3) + 7] = RGBGetBValue(colors[color_idx]); + } + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + libusb_bulk_transfer(dev, 0x01, usb_buf, 2 + (usb_buf[1] * 3), &actual, 1000); + libusb_bulk_transfer(dev, 0x81, usb_buf, 3, &actual, 1000); +} + +void CorsairHydroController::SendInit() +{ + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + libusb_control_transfer( dev, 0x40, 0x00, 0xffff, 0x0000, NULL, 0, 0 ); + libusb_control_transfer( dev, 0x40, 0x02, 0x0002, 0x0000, NULL, 0, 0 ); +} + +void CorsairHydroController::SendSpeed + ( + unsigned char speed + ) +{ + unsigned char usb_buf[3]; + int actual; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Send Speed packet | + \*-----------------------------------------------------*/ + usb_buf[0] = 0x53; + usb_buf[1] = speed; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + libusb_bulk_transfer(dev, 0x01, usb_buf, 2, &actual, 1000); + libusb_bulk_transfer(dev, 0x81, usb_buf, 3, &actual, 1000); +} diff --git a/Controllers/CorsairHydroController/CorsairHydroController.h b/Controllers/CorsairHydroController/CorsairHydroController.h new file mode 100644 index 0000000..eb03b5e --- /dev/null +++ b/Controllers/CorsairHydroController/CorsairHydroController.h @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| CorsairHydroController.h | +| | +| Driver for Corsair Hydro Series coolers | +| | +| Adam Honse (calcprogrammer1@gmail.com) 17 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + CORSAIR_HYDRO_CMD_READ_PUMP_SPEED = 0x31, + CORSAIR_HYDRO_CMD_WRITE_PUMP_MODE = 0x32, + CORSAIR_HYDRO_CMD_READ_PUMP_MODE = 0x33, + CORSAIR_HYDRO_CMD_READ_FAN_SPEED = 0x41, + CORSAIR_HYDRO_CMD_READ_AIO_TEMP = 0xA9, + CORSAIR_HYDRO_CMD_READ_FIRMWARE = 0xAA, +}; + +enum +{ + CORSAIR_HYDRO_PUMP_MODE_QUIET = 0x00, + CORSAIR_HYDRO_PUMP_MODE_BALANCED = 0x01, + CORSAIR_HYDRO_PUMP_MODE_PERFORMANCE = 0x02, +}; + +class CorsairHydroController +{ +public: + CorsairHydroController(libusb_device_handle* dev_handle, std::string dev_name); + ~CorsairHydroController(); + + unsigned char GetFanPercent(unsigned char fan_channel); + + unsigned short GetFanRPM(unsigned char fan_channel); + + std::string GetFirmwareString(); + std::string GetLocation(); + std::string GetNameString(); + + void SetBlink + ( + std::vector & colors, + unsigned char speed + ); + + void SetFixed + ( + std::vector & colors + ); + + void SetPulse + ( + std::vector & colors, + unsigned char speed + ); + + void SetShift + ( + std::vector & colors, + unsigned char speed + ); + +private: + libusb_device_handle* dev; + std::string firmware_version; + std::string location; + std::string name; + + void SendApplyBlink(); + void SendApplyPulse(); + void SendApplyShift(); + + void SendColors + ( + std::vector & colors + ); + + void SendFirmwareRequest(); + + void SendInit(); + + void SendSpeed + ( + unsigned char speed + ); +}; diff --git a/Controllers/CorsairHydroController/CorsairHydroControllerDetect.cpp b/Controllers/CorsairHydroController/CorsairHydroControllerDetect.cpp new file mode 100644 index 0000000..031685d --- /dev/null +++ b/Controllers/CorsairHydroController/CorsairHydroControllerDetect.cpp @@ -0,0 +1,90 @@ +/*---------------------------------------------------------*\ +| CorsairHydroControllerDetect.cpp | +| | +| Detector for Corsair Hydro Series coolers | +| | +| Adam Honse (calcprogrammer1@gmail.com) 17 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "CorsairHydroController.h" +#include "RGBController_CorsairHydro.h" + +/*-----------------------------------------------------*\ +| Corsair vendor ID | +\*-----------------------------------------------------*/ +#define CORSAIR_VID 0x1B1C + +/*-----------------------------------------------------*\ +| Keyboard Hydro Series product IDs | +\*-----------------------------------------------------*/ +#define CORSAIR_H115I_PRO_RGB_PID 0x0C13 +#define CORSAIR_H100I_PRO_RGB_PID 0x0C15 +#define CORSAIR_H150I_PRO_RGB_PID 0x0C12 + +typedef struct +{ + unsigned short usb_vid; + unsigned short usb_pid; + unsigned char usb_interface; + const char * name; +} corsair_hydro_device; + +#define CORSAIR_NUM_DEVICES (sizeof(device_list) / sizeof(device_list[ 0 ])) + +static const corsair_hydro_device device_list[] = +{ + /*-----------------------------------------------------------------------------------------------------*\ + | Coolers | + \*-----------------------------------------------------------------------------------------------------*/ + { CORSAIR_VID, CORSAIR_H100I_PRO_RGB_PID, 0, "Corsair H100i PRO RGB" }, + { CORSAIR_VID, CORSAIR_H115I_PRO_RGB_PID, 0, "Corsair H115i PRO RGB" }, + { CORSAIR_VID, CORSAIR_H150I_PRO_RGB_PID, 0, "Corsair H150i PRO RGB" }, +}; + +/******************************************************************************************\ +* * +* DetectCorsairHydroControllers * +* * +* Tests the USB address to see if a Corsair RGB Cooler controller exists there. * +* * +\******************************************************************************************/ + +void DetectCorsairHydroControllers() +{ + libusb_init(NULL); + + #ifdef _WIN32 + libusb_set_option(NULL, LIBUSB_OPTION_USE_USBDK); + #endif + + for(std::size_t device_idx = 0; device_idx < CORSAIR_NUM_DEVICES; device_idx++) + { + libusb_device_handle * dev = libusb_open_device_with_vid_pid(NULL, device_list[device_idx].usb_vid, device_list[device_idx].usb_pid); + + //Look for Corsair RGB Peripheral + if(dev) + { + libusb_detach_kernel_driver(dev, 0); + libusb_claim_interface(dev, 0); + + CorsairHydroController* controller = new CorsairHydroController(dev, device_list[device_idx].name); + RGBController_CorsairHydro* rgb_controller = new RGBController_CorsairHydro(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} /* DetectCorsairHydroControllers() */ + +REGISTER_DETECTOR("Corsair Hydro Series", DetectCorsairHydroControllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("Corsair Hydro Series", DetectCorsairHydroControllers, 0x1B1C, 0x0C12 ) | +| DUMMY_DEVICE_DETECTOR("Corsair Hydro Series", DetectCorsairHydroControllers, 0x1B1C, 0x0C13 ) | +| DUMMY_DEVICE_DETECTOR("Corsair Hydro Series", DetectCorsairHydroControllers, 0x1B1C, 0x0C15 ) | +\*---------------------------------------------------------------------------------------------------------*/ diff --git a/Controllers/CorsairHydroController/RGBController_CorsairHydro.cpp b/Controllers/CorsairHydroController/RGBController_CorsairHydro.cpp new file mode 100644 index 0000000..2ba7698 --- /dev/null +++ b/Controllers/CorsairHydroController/RGBController_CorsairHydro.cpp @@ -0,0 +1,152 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairHydro.cpp | +| | +| RGBController for Corsair Hydro Series coolers | +| | +| Adam Honse (calcprogrammer1@gmail.com) 18 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CorsairHydro.h" + +/**------------------------------------------------------------------*\ + @name Corsair Hydro + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCorsairHydroControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CorsairHydro::RGBController_CorsairHydro(CorsairHydroController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Corsair"; + description = "Corsair Hydro Series Device"; + version = controller->GetFirmwareString(); + type = DEVICE_TYPE_COOLER; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Blinking; + Blinking.name = "Blinking"; + Blinking.value = 1; + Blinking.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Blinking.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blinking.speed_min = 0x0F; + Blinking.speed_max = 0x05; + Blinking.speed = 0x0A; + Blinking.colors_min = 2; + Blinking.colors_max = 2; + Blinking.colors.resize(2); + modes.push_back(Blinking); + + mode ColorShift; + ColorShift.name = "Color Shift"; + ColorShift.value = 2; + ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorShift.speed_min = 0x46; + ColorShift.speed_max = 0x0F; + ColorShift.speed = 0x28; + ColorShift.colors_min = 2; + ColorShift.colors_max = 2; + ColorShift.colors.resize(2); + modes.push_back(ColorShift); + + mode Pulsing; + Pulsing.name = "Pulsing"; + Pulsing.value = 3; + Pulsing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Pulsing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulsing.speed_min = 0x50; + Pulsing.speed_max = 0x1E; + Pulsing.speed = 0x37; + Pulsing.colors_min = 2; + Pulsing.colors_max = 2; + Pulsing.colors.resize(2); + modes.push_back(Pulsing); + + SetupZones(); +} + +RGBController_CorsairHydro::~RGBController_CorsairHydro() +{ + delete controller; +} + +void RGBController_CorsairHydro::SetupZones() +{ + zone new_zone; + + new_zone.name = "Pump Zone"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + led new_led; + + new_led.name = "Pump LED"; + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_CorsairHydro::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CorsairHydro::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_CorsairHydro::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairHydro::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairHydro::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case 0: + controller->SetFixed(colors); + break; + + case 1: + controller->SetBlink(modes[active_mode].colors, modes[active_mode].speed); + break; + + case 2: + controller->SetShift(modes[active_mode].colors, modes[active_mode].speed); + break; + + case 3: + controller->SetPulse(modes[active_mode].colors, modes[active_mode].speed); + break; + } +} diff --git a/Controllers/CorsairHydroController/RGBController_CorsairHydro.h b/Controllers/CorsairHydroController/RGBController_CorsairHydro.h new file mode 100644 index 0000000..964fa31 --- /dev/null +++ b/Controllers/CorsairHydroController/RGBController_CorsairHydro.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairHydro.h | +| | +| RGBController for Corsair Hydro Series coolers | +| | +| Adam Honse (calcprogrammer1@gmail.com) 17 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairHydroController.h" + +class RGBController_CorsairHydro : public RGBController +{ +public: + RGBController_CorsairHydro(CorsairHydroController* controller_ptr); + ~RGBController_CorsairHydro(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CorsairHydroController* controller; +}; diff --git a/Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumController.cpp b/Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumController.cpp new file mode 100644 index 0000000..733ea7d --- /dev/null +++ b/Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumController.cpp @@ -0,0 +1,338 @@ +/*---------------------------------------------------------*\ +| CorsairHydroPlatinumController.cpp | +| | +| Driver for Corsair Hydro Platinum coolers | +| | +| Kasper 28 Mar 2021 | +| Nikola Jurkovic (jurkovic.nikola) 13 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CorsairHydroPlatinumController.h" +#include "CorsairDeviceGuard.h" + +static const uint8_t CRC_TABLE[256] = +{ + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +static const uint8_t MAGIC_1[61] = +{ + 0x01, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, + 0x7F, 0x7F, 0x7F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const uint8_t MAGIC_2[61] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const uint8_t MAGIC_3[61] = +{ + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const uint8_t HARDWARE_MAGIC_1[61] = +{ + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x09, 0x20, 0x07, 0x00, + 0x0B, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const uint8_t HARDWARE_MAGIC_2[61] = +{ + 0x0A, 0x01, 0x04, 0x07, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x01, 0x0A, 0x07, 0x04, 0x0B, 0x0A, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x01, 0x0A, 0x07, 0x04, 0x01, 0x0A, 0x09, 0x08, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const uint8_t HARDWARE_MAGIC_3[61] = +{ + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const uint8_t HARDWARE_MAGIC_4[61] = +{ + 0x00, 0x00, 0xFF, 0x00, 0x4A, 0xFF, 0x00, 0x94, + 0xFF, 0x00, 0xDF, 0xFF, 0x00, 0xFF, 0xAA, 0x00, + 0xFF, 0x15, 0x7F, 0x7F, 0x00, 0xFA, 0x00, 0x06, + 0xDB, 0x00, 0x32, 0xD7, 0x00, 0x58, 0xF6, 0x00, + 0x76, 0x94, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +CorsairHydroPlatinumController::CorsairHydroPlatinumController(hid_device* dev_handle, const char* path, bool dev_rgb_fan, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + have_rgb_fan = dev_rgb_fan; + guard_manager_ptr = new DeviceGuardManager(new CorsairDeviceGuard()); + + SendMagic(MAGIC_1, CORSAIR_HYDRO_PLATINUM_MAGIC_1); + SendMagic(MAGIC_2, CORSAIR_HYDRO_PLATINUM_MAGIC_2); + SendMagic(MAGIC_3, CORSAIR_HYDRO_PLATINUM_MAGIC_3); +} + +CorsairHydroPlatinumController::~CorsairHydroPlatinumController() +{ + /*-----------------------------------------------------*\ + | Hardware lights, 2,3,4,1 | + \*-----------------------------------------------------*/ + SendMagic(HARDWARE_MAGIC_2, CORSAIR_HYDRO_PLATINUM_MAGIC_2); + SendMagic(HARDWARE_MAGIC_3, CORSAIR_HYDRO_PLATINUM_MAGIC_3); + SendMagic(HARDWARE_MAGIC_4, CORSAIR_HYDRO_PLATINUM_SET_LIGHTING_1); + SendMagic(HARDWARE_MAGIC_1, CORSAIR_HYDRO_PLATINUM_MAGIC_1); + + hid_close(dev); + delete guard_manager_ptr; +} + +std::string CorsairHydroPlatinumController::GetLocation() +{ + return("HID: " + location); +} + +std::string CorsairHydroPlatinumController::GetFirmwareString() +{ + return(firmware_version); +} + +std::string CorsairHydroPlatinumController::GetName() +{ + return(name); +} + +void CorsairHydroPlatinumController::SetupColors(std::vector colors) +{ + unsigned int end_led = (colors.size() >= 20) ? 20 : (unsigned int)colors.size(); + SendColors(colors, 0, end_led, CORSAIR_HYDRO_PLATINUM_SET_LIGHTING_1); + + if(colors.size() > 20) + { + end_led = (colors.size() >= 40) ? 40 : (unsigned int)colors.size(); + SendColors(colors, 20, end_led, CORSAIR_HYDRO_PLATINUM_SET_LIGHTING_2); + } + if(colors.size() > 40) + { + end_led = (colors.size() >= 48) ? 48 : (unsigned int)colors.size(); + SendColors(colors, 40, end_led, CORSAIR_HYDRO_PLATINUM_SET_LIGHTING_3); + } +} + +bool CorsairHydroPlatinumController::HaveRgbFan() +{ + return(have_rgb_fan); +} + +void CorsairHydroPlatinumController::SendMagic(const uint8_t* magic, unsigned int command) +{ + unsigned char usb_buf[CORSAIR_HYDRO_PLATINUM_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x3F; + usb_buf[0x02] = (GetSequenceNumber()) | command; + + /*-----------------------------------------------------*\ + | Copy the magic bytes into the buffer | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[3], magic, 61 * sizeof magic[0]); + + /*-----------------------------------------------------*\ + | The data sent to the PEC function should not contain | + | the first (report id), second (prefix) and | + | last (checksum) bytes | + \*-----------------------------------------------------*/ + std::vector checksum_array; + checksum_array.insert(checksum_array.begin(), std::begin(usb_buf) + 2, std::end(usb_buf) - 1); + usb_buf[64] = ComputePEC(static_cast(checksum_array.data()), 62); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + /*---------------------------------------------------------*\ + | HID I/O start | + \*---------------------------------------------------------*/ + { + DeviceGuardLock _ = guard_manager_ptr->AwaitExclusiveAccess(); + + hid_write(dev, usb_buf, CORSAIR_HYDRO_PLATINUM_PACKET_SIZE); + hid_read(dev, usb_buf, CORSAIR_HYDRO_PLATINUM_PACKET_SIZE); + } + /*---------------------------------------------------------*\ + | HID I/O end (lock released) | + \*---------------------------------------------------------*/ + + if(firmware_version.empty()) + { + firmware_version = + std::to_string(usb_buf[2] >> 4) + "." + + std::to_string(usb_buf[2] & 0xf) + "." + + std::to_string(usb_buf[3]); + } + + /*-----------------------------------------------------*\ + | This delay prevents the AIO from soft-locking when | + | using an EE. | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(CORSAIR_HYDRO_PLATINUM_PACKET_DELAY)); +} + +void CorsairHydroPlatinumController::SendColors(std::vector colors, unsigned int start, unsigned int end, unsigned int command) +{ + unsigned char usb_buf[CORSAIR_HYDRO_PLATINUM_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_HYDRO_PLATINUM_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x3F; + usb_buf[0x02] = (GetSequenceNumber()) | command; + + unsigned int i = 0; + for(std::size_t color_idx = start; color_idx < end; color_idx++) + { + usb_buf[(i * 3) + 3] = RGBGetBValue(colors[color_idx]); + usb_buf[(i * 3) + 4] = RGBGetGValue(colors[color_idx]); + usb_buf[(i * 3) + 5] = RGBGetRValue(colors[color_idx]); + i++; + } + + /*-----------------------------------------------------*\ + | The data sent to the PEC function should not contain | + | the first (report id), second (prefix) and | + | last (checksum) bytes | + \*-----------------------------------------------------*/ + std::vector checksum_array; + checksum_array.insert(checksum_array.begin(), std::begin(usb_buf) + 2, std::end(usb_buf) - 1); + usb_buf[64] = ComputePEC(static_cast(checksum_array.data()), 62); + + /*---------------------------------------------------------*\ + | HID I/O start | + \*---------------------------------------------------------*/ + { + DeviceGuardLock _ = guard_manager_ptr->AwaitExclusiveAccess(); + + hid_write(dev, usb_buf, CORSAIR_HYDRO_PLATINUM_PACKET_SIZE); + } + /*---------------------------------------------------------*\ + | HID I/O end (lock released) | + \*---------------------------------------------------------*/ + + /*-----------------------------------------------------*\ + | This delay prevents the AIO from soft-locking when | + | using an EE. | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(CORSAIR_HYDRO_PLATINUM_PACKET_DELAY)); +} + +unsigned int CorsairHydroPlatinumController::GetSequenceNumber() +{ + if(sequence_number < 31) + { + sequence_number++; + } + else + { + sequence_number = 1; + } + + return(sequence_number << 3); +} + +uint8_t CorsairHydroPlatinumController::ComputePEC(const void * data, size_t size) +{ + uint8_t val = 0; + + uint8_t * pos = (uint8_t *) data; + uint8_t * end = pos + size; + + while(pos < end) + { + val = CRC_TABLE[val ^ *pos]; + pos++; + } + + return val; +} diff --git a/Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumController.h b/Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumController.h new file mode 100644 index 0000000..52bab43 --- /dev/null +++ b/Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumController.h @@ -0,0 +1,60 @@ +/*---------------------------------------------------------*\ +| CorsairHydroPlatinumController.h | +| | +| Driver for Corsair Hydro Platinum coolers | +| | +| Kasper 28 Mar 2021 | +| Nikola Jurkovic (jurkovic.nikola) 13 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "DeviceGuardManager.h" + +#define CORSAIR_HYDRO_PLATINUM_PACKET_SIZE 65 +#define CORSAIR_HYDRO_PLATINUM_PACKET_DELAY 5 + +enum +{ + CORSAIR_HYDRO_PLATINUM_MAGIC_1 = 0b001, + CORSAIR_HYDRO_PLATINUM_MAGIC_2 = 0b010, + CORSAIR_HYDRO_PLATINUM_MAGIC_3 = 0b011, + + CORSAIR_HYDRO_PLATINUM_SET_LIGHTING_1 = 0b100, + CORSAIR_HYDRO_PLATINUM_SET_LIGHTING_2 = 0b101, + CORSAIR_HYDRO_PLATINUM_SET_LIGHTING_3 = 0b110, +}; + +class CorsairHydroPlatinumController +{ +public: + CorsairHydroPlatinumController(hid_device* dev_handle, const char* path, bool dev_rgb_fan, std::string dev_name); + ~CorsairHydroPlatinumController(); + + std::string GetLocation(); + std::string GetFirmwareString(); + std::string GetName(); + void SetupColors(std::vector colors); + bool HaveRgbFan(); + +private: + hid_device* dev; + std::string location; + std::string firmware_version; + std::string name; + std::atomic sequence_number; + DeviceGuardManager* guard_manager_ptr; + bool have_rgb_fan = true; + + void SendMagic(const uint8_t* magic, unsigned int command); + void SendColors(std::vector colors, unsigned int start, unsigned int end, unsigned int command); + unsigned int GetSequenceNumber(); + uint8_t ComputePEC(const void * data, size_t size); +}; diff --git a/Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumControllerDetect.cpp b/Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumControllerDetect.cpp new file mode 100644 index 0000000..d55dd72 --- /dev/null +++ b/Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumControllerDetect.cpp @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| CorsairHydroPlatinumControllerDetect.cpp | +| | +| Detector for Corsair Hydro Platinum coolers | +| | +| Kasper 28 Mar 2021 | +| Nikola Jurkovic (jurkovic.nikola) 13 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "CorsairHydroPlatinumController.h" +#include "RGBController_CorsairHydroPlatinum.h" + +/*-----------------------------------------------------*\ +| Corsair vendor ID | +\*-----------------------------------------------------*/ +#define CORSAIR_VID 0x1B1C + +/*-----------------------------------------------------*\ +| Product IDs | +\*-----------------------------------------------------*/ +#define CORSAIR_HYDRO_H100I_PLATINUM_PID 0x0C18 +#define CORSAIR_HYDRO_H100I_PLATINUM_SE_PID 0x0C19 +#define CORSAIR_HYDRO_H115I_PLATINUM_PID 0x0C17 +#define CORSAIR_HYDRO_H60I_PRO_XT_PID 0x0C29 +#define CORSAIR_HYDRO_H100I_PRO_XT_PID 0x0C20 +#define CORSAIR_HYDRO_H100I_PRO_XT_V2_PID 0x0C2D +#define CORSAIR_HYDRO_H115I_PRO_XT_PID 0x0C21 +#define CORSAIR_HYDRO_H150I_PRO_XT_PID 0x0C22 +#define CORSAIR_HYDRO_H100I_ELITE_RGB_PID 0x0C35 +#define CORSAIR_HYDRO_H115I_ELITE_RGB_PID 0x0C36 +#define CORSAIR_HYDRO_H150I_ELITE_RGB_PID 0x0C37 +#define CORSAIR_HYDRO_H100I_ELITE_RGB_PID_WHITE 0x0C40 +#define CORSAIR_HYDRO_H150I_ELITE_RGB_PID_WHITE 0x0C41 + +void DetectCorsairHydroPlatinumControllers(hid_device_info* info, const std::string& name) +{ + uint16_t no_rgb_fan_models[] = + { + CORSAIR_HYDRO_H100I_ELITE_RGB_PID, + CORSAIR_HYDRO_H115I_ELITE_RGB_PID, + CORSAIR_HYDRO_H150I_ELITE_RGB_PID, + CORSAIR_HYDRO_H100I_ELITE_RGB_PID_WHITE, + CORSAIR_HYDRO_H150I_ELITE_RGB_PID_WHITE + }; + + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + bool dev_rgb_fan = true; + for(uint16_t pid : no_rgb_fan_models) + { + if(info->product_id == pid) + { + dev_rgb_fan = false; + break; + } + } + + CorsairHydroPlatinumController* controller = new CorsairHydroPlatinumController(dev, info->path, dev_rgb_fan, name); + RGBController_CorsairHydroPlatinum* rgb_controller = new RGBController_CorsairHydroPlatinum(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("Corsair Hydro H100i Platinum", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H100I_PLATINUM_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H100i Platinum SE", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H100I_PLATINUM_SE_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H115i Platinum", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H115I_PLATINUM_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H60i Pro XT", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H60I_PRO_XT_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H100i Pro XT", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H100I_PRO_XT_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H100i Pro XT v2", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H100I_PRO_XT_V2_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H115i Pro XT", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H115I_PRO_XT_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H150i Pro XT", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H150I_PRO_XT_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H100i Elite", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H100I_ELITE_RGB_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H115i Elite", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H115I_ELITE_RGB_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H150i Elite", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H150I_ELITE_RGB_PID ); +REGISTER_HID_DETECTOR("Corsair Hydro H100i Elite White", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H100I_ELITE_RGB_PID_WHITE ); +REGISTER_HID_DETECTOR("Corsair Hydro H150i Elite White", DetectCorsairHydroPlatinumControllers, CORSAIR_VID, CORSAIR_HYDRO_H150I_ELITE_RGB_PID_WHITE ); diff --git a/Controllers/CorsairHydroPlatinumController/RGBController_CorsairHydroPlatinum.cpp b/Controllers/CorsairHydroPlatinumController/RGBController_CorsairHydroPlatinum.cpp new file mode 100644 index 0000000..5c26522 --- /dev/null +++ b/Controllers/CorsairHydroPlatinumController/RGBController_CorsairHydroPlatinum.cpp @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairHydroPlatinum.cpp | +| | +| RGBController for Corsair Hydro Platinum coolers | +| | +| Kasper 28 Mar 2021 | +| Nikola Jurkovic (jurkovic.nikola) 13 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CorsairHydroPlatinum.h" + +#define NA 0xFFFFFFFF +static unsigned int matrix_map[5][5] = +{ + { NA, 11, 12, 13, NA }, + { 10, NA, 1, NA, 14 }, + { 9, 0, NA, 2, 15 }, + { 8, NA, 3, NA, 4 }, + { NA, 7, 6, 5, NA } +}; + +/**------------------------------------------------------------------*\ + @name Corsair Hydro Platinum + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectCorsairHydroPlatinumControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CorsairHydroPlatinum::RGBController_CorsairHydroPlatinum(CorsairHydroPlatinumController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Corsair"; + description = "Corsair Hydro Platinum Series Device"; + type = DEVICE_TYPE_COOLER; + location = controller->GetLocation(); + version = controller->GetFirmwareString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + Init_Controller(); + SetupZones(); +} + +RGBController_CorsairHydroPlatinum::~RGBController_CorsairHydroPlatinum() +{ + delete controller; +} + +void RGBController_CorsairHydroPlatinum::Init_Controller() +{ + zone cpu_block_zone; + cpu_block_zone.name = "CPU Block"; + cpu_block_zone.type = ZONE_TYPE_MATRIX; + cpu_block_zone.leds_min = 16; + cpu_block_zone.leds_max = 16; + cpu_block_zone.leds_count = 16; + cpu_block_zone.matrix_map = new matrix_map_type; + cpu_block_zone.matrix_map->height = 5; + cpu_block_zone.matrix_map->width = 5; + cpu_block_zone.matrix_map->map = (unsigned int *)&matrix_map; + zones.push_back(cpu_block_zone); + + /*-----------------------------------------------------*\ + | If the device is RGB fan-capable, set up fan zones. | + \*-----------------------------------------------------*/ + if(controller->HaveRgbFan()) + { + zone fans_zone; + fans_zone.name = "Fans"; + fans_zone.type = ZONE_TYPE_LINEAR; + fans_zone.leds_min = 0; + fans_zone.leds_max = 32; + fans_zone.leds_count = 0; + fans_zone.matrix_map = NULL; + zones.push_back(fans_zone); + } +} + +void RGBController_CorsairHydroPlatinum::SetupZones() +{ + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + new_led.name = zones[zone_idx].name + " " + std::to_string(led_idx);; + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_CorsairHydroPlatinum::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_CorsairHydroPlatinum::DeviceUpdateLEDs() +{ + controller->SetupColors(colors); +} + +void RGBController_CorsairHydroPlatinum::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairHydroPlatinum::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairHydroPlatinum::DeviceUpdateMode() +{ + +} diff --git a/Controllers/CorsairHydroPlatinumController/RGBController_CorsairHydroPlatinum.h b/Controllers/CorsairHydroPlatinumController/RGBController_CorsairHydroPlatinum.h new file mode 100644 index 0000000..37f4f48 --- /dev/null +++ b/Controllers/CorsairHydroPlatinumController/RGBController_CorsairHydroPlatinum.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairHydroPlatinum.h | +| | +| RGBController for Corsair Hydro Platinum coolers | +| | +| Kasper 28 Mar 2021 | +| Nikola Jurkovic (jurkovic.nikola) 13 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairHydroPlatinumController.h" + +class RGBController_CorsairHydroPlatinum : public RGBController +{ +public: + RGBController_CorsairHydroPlatinum(CorsairHydroPlatinumController* controller_ptr); + ~RGBController_CorsairHydroPlatinum(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CorsairHydroPlatinumController* controller; + + void Init_Controller(); +}; diff --git a/Controllers/CorsairICueLinkController/CorsairICueLinkController.cpp b/Controllers/CorsairICueLinkController/CorsairICueLinkController.cpp new file mode 100644 index 0000000..7d28e69 --- /dev/null +++ b/Controllers/CorsairICueLinkController/CorsairICueLinkController.cpp @@ -0,0 +1,315 @@ +/*---------------------------------------------------------*\ +| CorsairICueLinkController.cpp | +| | +| Driver for Corsair iCue Link System Hub | +| | +| Aiden Vigue (acvigue) 02 Mar 2025 | +| Adam Honse 01 Aug 2025 | +| Nikola Jurkovic (jurkovic.nikola) 11 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include + +#include "CorsairDeviceGuard.h" +#include "CorsairICueLinkController.h" +#include "CorsairICueLinkProtocol.h" + +using namespace std::chrono_literals; + +CorsairICueLinkController::CorsairICueLinkController(hid_device* dev_handle, const char* path, std::string name) +{ + dev = dev_handle; + location = path; + this->name = name; + + guard_manager_ptr = new DeviceGuardManager(new CorsairDeviceGuard()); + + GetControllerFirmware(); // Firmware + SetControllerSoftwareMode(); // Software mode + GetControllerDevices(); // Get connected devices +} + +CorsairICueLinkController::~CorsairICueLinkController() +{ + SetControllerHardwareMode(); // Release device back to hardware mode + hid_close(dev); + delete guard_manager_ptr; +} + +void CorsairICueLinkController::SetControllerSoftwareMode() +{ + SendCommand(CORSAIR_ICUE_LINK_CMD_SOFTWARE_MODE, { }, { }); +} + +void CorsairICueLinkController::SetControllerHardwareMode() +{ + SendCommand(CORSAIR_ICUE_LINK_CMD_HARDWARE_MODE, { }, { }); +} + +void CorsairICueLinkController::GetControllerFirmware() +{ + /*-----------------------------------------------------*\ + | Get the firmware version | + \*-----------------------------------------------------*/ + std::vector firmware_data = SendCommand(CORSAIR_ICUE_LINK_CMD_GET_FIRMWARE, { }, { }); + version = + { + firmware_data[4], + firmware_data[5], + static_cast(firmware_data[6] | (firmware_data[7] << 8)) + }; +} + +void CorsairICueLinkController::GetControllerDevices() +{ + /*-----------------------------------------------------*\ + | Get the endpoints data | + \*-----------------------------------------------------*/ + std::vector endpoint_data = Read(CORSAIR_ICUE_LINK_MODE_GET_DEVICES, CORSAIR_ICUE_LINK_DATA_TYPE_GET_DEVICES); + unsigned char channel = endpoint_data[6]; + std::vector index = std::vector(endpoint_data.begin() + 7, endpoint_data.end()); + std::size_t pos = 0; + + /*-----------------------------------------------------*\ + | Process each channel | + \*-----------------------------------------------------*/ + for(std::size_t channel_idx = 1; channel_idx < (std::size_t)(channel + 1); channel_idx++) + { + std::size_t device_id_length = index[pos + 7]; + + if(device_id_length == 0) + { + pos += 8; + continue; + } + + /*-------------------------------------------------*\ + | Extract endpoint metadata and ID from data | + \*-------------------------------------------------*/ + std::vector endpoint_metadata = std::vector(index.begin() + pos, index.begin() + pos + 8); + std::vector endpoint_id = std::vector(index.begin() + pos + 8, index.begin() + pos + 8 + device_id_length); + + /*-------------------------------------------------*\ + | Get device information for this endpoint | + \*-------------------------------------------------*/ + unsigned char type = endpoint_metadata[2]; + unsigned char model = endpoint_metadata[3]; + const CorsairICueLinkDevice * device = FindCorsairICueLinkDevice(type, model); + + if(device == nullptr) + { + pos += 8 + device_id_length; + LOG_WARNING("[CorsairICueLinkController] Unknown device type: 0x%02x, model: 0x%02x", type, model); + continue; + } + + /*-------------------------------------------------*\ + | Dont process internal device due to duplication | + \*-------------------------------------------------*/ + if(device->internal == true) + { + pos += 8 + device_id_length; + continue; + } + + if(device->led_channels == 0) + { + LOG_WARNING("[CorsairICueLinkController] Device type %s has 0 LEDs, please open issue", device->display_name.c_str()); + pos += 8 + device_id_length; + continue; + } + + /*-------------------------------------------------*\ + | Append this endpoint's serial number to the | + | device's serial number string | + \*-------------------------------------------------*/ + std::string endpoint_id_str(endpoint_id.begin(), endpoint_id.end()); + serial += "\r\n" + endpoint_id_str; // Why do we need endpoint IDs when colors are managed via endpoint channel IDs ? + + /*-------------------------------------------------*\ + | Add endpoint device to list | + \*-------------------------------------------------*/ + endpoints.push_back(device); + + pos += 8 + device_id_length; + } +} + +std::string CorsairICueLinkController::GetFirmwareString() +{ + char buffer[20]; + std::snprintf(buffer, sizeof(buffer), "v%d.%d.%d", version[0], version[1], version[2]); + return std::string(buffer); +} + +std::string CorsairICueLinkController::GetNameString() +{ + return(name); +} + +std::string CorsairICueLinkController::GetLocationString() +{ + return("HID: " + location); +} + +std::string CorsairICueLinkController::GetSerialString() +{ + return(serial); +} + +std::vector CorsairICueLinkController::GetEndpoints() +{ + return(endpoints); +} + +void CorsairICueLinkController::UpdateLights(RGBColor* colors, std::size_t num_colors) +{ + /*-------------------------------------------------*\ + | Send color buffer, packed RGBRGBRGB | + \*-------------------------------------------------*/ + std::vector color_data; + for(std::size_t i = 0; i < num_colors; i++) + { + color_data.push_back(RGBGetRValue(colors[i])); + color_data.push_back(RGBGetGValue(colors[i])); + color_data.push_back(RGBGetBValue(colors[i])); + } + + Write(CORSAIR_ICUE_LINK_MODE_SET_COLOR, CORSAIR_ICUE_LINK_DATA_TYPE_SET_COLOR, color_data, CORSAIR_ICUE_ENDPOINT_TYPE_COLOR); +} + +std::vector> CorsairICueLinkController::ProcessMultiChunkPacket(const std::vector& data, size_t max_chunk_size) +{ + std::vector> result; + size_t offset = 0; + + while(offset < data.size()) + { + size_t end = std::min(max_chunk_size, data.size() - offset); + std::vector chunk(data.begin() + offset, data.begin() + offset + end); + result.push_back(chunk); + offset += end; + } + + return result; +} + +std::vector CorsairICueLinkController::SendCommand(std::vector command, std::vector data, std::vector waitForDataType) +{ + DeviceGuardLock lock = guard_manager_ptr->AwaitExclusiveAccess(); + + std::vector write_buf(CORSAIR_ICUE_LINK_BUFFER_WRITE_LENGTH); + write_buf[2] = 0x01; + + size_t command_size = command.size(); + size_t data_size = data.size(); + + for(size_t i = 0; i < command_size; i++) + { + write_buf[3 + i] = command[i]; + } + + for(size_t i = 0; i < data_size; i++) + { + write_buf[3 + command_size + i] = data[i]; + } + + std::vector read_buf(CORSAIR_ICUE_LINK_BUFFER_READ_LENGTH); + + hid_write(dev, write_buf.data(), CORSAIR_ICUE_LINK_BUFFER_WRITE_LENGTH); + hid_read_timeout(dev, read_buf.data(), CORSAIR_ICUE_LINK_BUFFER_READ_LENGTH, 1000); + + if(waitForDataType.size() != 2) + { + return read_buf; + } + + int tries = 0; + while((read_buf[4] != waitForDataType[0]) && tries < 5) + { + std::fill(read_buf.begin(), read_buf.end(), 0); // Clear the buffer before reading again + hid_read_timeout(dev, read_buf.data(), CORSAIR_ICUE_LINK_BUFFER_READ_LENGTH, 1000); + tries++; + } + + return read_buf; +} + +std::vector CorsairICueLinkController::Read(std::vector endpoint, std::vector data_type) +{ + /*-----------------------------------------------------*\ + | Private function to read data from an endpoint | + \*-----------------------------------------------------*/ + DeviceGuardLock lock = guard_manager_ptr->AwaitExclusiveAccess(); + + SendCommand(CORSAIR_ICUE_LINK_CMD_CLOSE_ENDPOINT, endpoint, { }); + SendCommand(CORSAIR_ICUE_LINK_CMD_OPEN_ENDPOINT, endpoint, { }); + std::vector res = SendCommand(CORSAIR_ICUE_LINK_CMD_READ, { }, data_type); + SendCommand(CORSAIR_ICUE_LINK_CMD_CLOSE_ENDPOINT, endpoint, { }); + + return res; +} + +void CorsairICueLinkController::Write(std::vector endpoint, std::vector data_type, std::vector data, CORSAIR_ICUE_ENDPOINT_TYPE endpoint_type) +{ + DeviceGuardLock lock = guard_manager_ptr->AwaitExclusiveAccess(); + + std::vector buf(data_type.size() + data.size() + CORSAIR_ICUE_LINK_WRITE_HEADER_SIZE); + + unsigned short data_len = (unsigned short)(data.size() + 2); + buf[0] = (unsigned char)(data_len & 0xFF); + buf[1] = (unsigned char)((data_len >> 8) & 0xFF); + + /*-----------------------------------------------------*\ + | Pack data into next bytes | + \*-----------------------------------------------------*/ + for(size_t i = 0; i < data_type.size(); i++) + { + buf[CORSAIR_ICUE_LINK_WRITE_HEADER_SIZE + i] = data_type[i]; + } + + /*-----------------------------------------------------*\ + | Pack data into next bytes | + \*-----------------------------------------------------*/ + for(size_t i = 0; i < data.size(); i++) + { + buf[CORSAIR_ICUE_LINK_WRITE_HEADER_SIZE + data_type.size() + i] = data[i]; + } + + SendCommand(CORSAIR_ICUE_LINK_CMD_CLOSE_ENDPOINT, endpoint, { }); + + if(endpoint_type == CORSAIR_ICUE_ENDPOINT_TYPE_DEFAULT) + { + SendCommand(CORSAIR_ICUE_LINK_CMD_OPEN_ENDPOINT, endpoint, { }); + SendCommand(CORSAIR_ICUE_LINK_CMD_WRITE, buf, { }); + SendCommand(CORSAIR_ICUE_LINK_CMD_CLOSE_ENDPOINT, endpoint, { }); + return; + } + + SendCommand(CORSAIR_ICUE_LINK_CMD_OPEN_COLOR_ENDPOINT, endpoint, { }); + std::vector> chunks = ProcessMultiChunkPacket(buf, CORSAIR_ICUE_LINK_MAXIMUM_BUFFER_PER_REQUEST); + + for(size_t i = 0; i < chunks.size(); i++) + { + if(i == 0) + { + /*-----------------------------------------------------*\ + | Initial color packet | + \*-----------------------------------------------------*/ + SendCommand(CORSAIR_ICUE_LINK_CMD_WRITE_COLOR, chunks[i], {}); + } + else + { + /*-----------------------------------------------------*\ + | Everything else follows 0x07, 0x00 | + \*-----------------------------------------------------*/ + SendCommand(CORSAIR_ICUE_LINK_CMD_WRITE_COLOR_NEXT, chunks[i], {}); + } + } + + SendCommand(CORSAIR_ICUE_LINK_CMD_CLOSE_ENDPOINT, endpoint, { }); +} diff --git a/Controllers/CorsairICueLinkController/CorsairICueLinkController.h b/Controllers/CorsairICueLinkController/CorsairICueLinkController.h new file mode 100644 index 0000000..a7be69f --- /dev/null +++ b/Controllers/CorsairICueLinkController/CorsairICueLinkController.h @@ -0,0 +1,60 @@ +/*---------------------------------------------------------*\ +| CorsairICueLinkController.h | +| | +| Driver for Corsair iCue Link System Hub | +| | +| Aiden Vigue (acvigue) 02 Mar 2025 | +| Adam Honse 01 Aug 2025 | +| Nikola Jurkovic 11 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +#include "CorsairICueLinkProtocol.h" +#include "DeviceGuardManager.h" +#include "RGBController.h" + +class CorsairICueLinkController +{ +public: + CorsairICueLinkController(hid_device* dev_handle, const char* path, std::string name); + ~CorsairICueLinkController(); + + std::string GetFirmwareString(); + std::string GetNameString(); + std::string GetLocationString(); + std::string GetSerialString(); + + std::vector GetEndpoints(); + + void UpdateLights(RGBColor* colors, std::size_t num_colors); + +private: + hid_device* dev; + std::string name; + std::string location; + std::string serial; + std::vector version; + + DeviceGuardManager* guard_manager_ptr; + + std::vector endpoints; + + void GetControllerFirmware(); + void GetControllerDevices(); + void SetControllerSoftwareMode(); + void SetControllerHardwareMode(); + + std::vector> ProcessMultiChunkPacket(const std::vector& data, size_t max_chunk_size); + std::vector SendCommand(std::vector command, std::vector data, std::vector waitForDataType); + + std::vector Read(std::vector endpoint, std::vector data_type); + void Write(std::vector endpoint, std::vector data_type, std::vector data, CORSAIR_ICUE_ENDPOINT_TYPE endpoint_type); +}; diff --git a/Controllers/CorsairICueLinkController/CorsairICueLinkControllerDetect.cpp b/Controllers/CorsairICueLinkController/CorsairICueLinkControllerDetect.cpp new file mode 100644 index 0000000..e271788 --- /dev/null +++ b/Controllers/CorsairICueLinkController/CorsairICueLinkControllerDetect.cpp @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| CorsairICueLinkControllerDetect.cpp | +| | +| Detector for Corsair iCue Link System Hub | +| | +| Aiden Vigue (acvigue) 02 Mar 2025 | +| Adam Honse 01 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "LogManager.h" +#include "CorsairICueLinkController.h" +#include "RGBController_CorsairICueLink.h" + +#define CORSAIR_VID 0x1B1C +#define CORSAIR_ICUE_LINK_SYSTEM_HUB_PID 0x0C3F + +void DetectCorsairICueLinkControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CorsairICueLinkController* controller = new CorsairICueLinkController(dev, info->path, name); + RGBController_CorsairICueLink* rgb_controller = new RGBController_CorsairICueLink(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Corsair iCUE Link System Hub", DetectCorsairICueLinkControllers, CORSAIR_VID, CORSAIR_ICUE_LINK_SYSTEM_HUB_PID, 0x00, 0xFF42, 0x01); diff --git a/Controllers/CorsairICueLinkController/CorsairICueLinkProtocol.cpp b/Controllers/CorsairICueLinkController/CorsairICueLinkProtocol.cpp new file mode 100644 index 0000000..ebb51c7 --- /dev/null +++ b/Controllers/CorsairICueLinkController/CorsairICueLinkProtocol.cpp @@ -0,0 +1,25 @@ +/*---------------------------------------------------------*\ +| CorsairICueLinkProtocol.cpp | +| | +| Driver for Corsair iCue Link System Hub | +| | +| Aiden Vigue (acvigue) 2 Mar 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CorsairICueLinkProtocol.h" + +const CorsairICueLinkDevice* FindCorsairICueLinkDevice(unsigned char type, unsigned char model) +{ + for(size_t i = 0; i < sizeof(known_devices) / sizeof(known_devices[0]); i++) + { + if(known_devices[i].type == type && known_devices[i].model == model) + { + return(&known_devices[i]); + } + } + + return nullptr; +} diff --git a/Controllers/CorsairICueLinkController/CorsairICueLinkProtocol.h b/Controllers/CorsairICueLinkController/CorsairICueLinkProtocol.h new file mode 100644 index 0000000..9528acc --- /dev/null +++ b/Controllers/CorsairICueLinkController/CorsairICueLinkProtocol.h @@ -0,0 +1,100 @@ +/*---------------------------------------------------------*\ +| CorsairICueLinkProtocol.h | +| | +| Driver for Corsair iCue Link System Hub | +| | +| Aiden Vigue (acvigue) 02 Mar 2025 | +| Nikola Jurkovic (jurkovic.nikola) 11 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +typedef struct CorsairICueLinkDevice +{ + unsigned char type = 0x00; + unsigned char model = 0x00; + std::string display_name = "Unknown"; + unsigned char led_channels = 0; + bool internal = false; +} CorsairICueLinkDevice; + +static const CorsairICueLinkDevice known_devices[] = +{ + { 0x05, 0x02, "iCUE LINK 5000T RGB", 160 }, + { 0x05, 0x01, "iCUE LINK 9000D RGB AIRFLOW", 22 }, + { 0x05, 0x00, "iCUE LINK ADAPTER", 0 }, + { 0x06, 0x00, "iCUE LINK COOLER PUMP LCD", 24 }, + { 0x11, 0x00, "iCUE LINK TITAN 240", 20 }, + { 0x11, 0x04, "iCUE LINK TITAN 240", 20 }, + { 0x07, 0x00, "iCUE LINK H100i RGB", 20 }, + { 0x07, 0x04, "iCUE LINK H100i RGB", 20 }, + { 0x11, 0x01, "iCUE LINK TITAN 280", 20 }, + { 0x07, 0x01, "iCUE LINK H115i RGB", 20 }, + { 0x11, 0x02, "iCUE LINK TITAN 360", 20 }, + { 0x11, 0x05, "iCUE LINK TITAN 360", 20 }, + { 0x07, 0x02, "iCUE LINK H150i RGB", 20 }, + { 0x07, 0x05, "iCUE LINK H150i RGB", 20 }, + { 0x11, 0x03, "iCUE LINK TITAN 420", 20 }, + { 0x07, 0x03, "iCUE LINK H170i RGB", 20 }, + { 0x10, 0x00, "VRM COOLER MODULE", 0 }, + { 0x02, 0x00, "iCUE LINK LX RGB", 18 }, + { 0x14, 0x00, "ORIGIN OA", 0 }, + { 0x01, 0x00, "iCUE LINK QX RGB", 34 }, + { 0x13, 0x00, "iCUE LINK RX", 0 }, + { 0x04, 0x00, "iCUE LINK RX MAX", 0 }, + { 0x0F, 0x00, "iCUE LINK RX RGB", 8 }, + { 0x03, 0x00, "iCUE LINK RX RGB MAX", 8 }, + { 0x09, 0x00, "iCUE LINK XC7 ELITE", 24 }, + { 0x0C, 0x00, "iCUE LINK XD5 ELITE", 22 }, + { 0x0E, 0x00, "iCUE LINK XD5 ELITE LCD", 22, true }, + { 0x19, 0x00, "iCUE LINK XD6 ELITE", 22 }, + { 0x0A, 0x00, "iCUE LINK XG3 HYBRID", 0 }, + { 0x0D, 0x00, "iCUE LINK XG7 RGB", 16 } +}; + +//Lengths +#define CORSAIR_ICUE_LINK_BUFFER_WRITE_LENGTH 513 +#define CORSAIR_ICUE_LINK_BUFFER_READ_LENGTH 512 +#define CORSAIR_ICUE_LINK_READ_HEADER_SIZE 3 +#define CORSAIR_ICUE_LINK_WRITE_HEADER_SIZE 4 +#define CORSAIR_ICUE_LINK_MAXIMUM_BUFFER_PER_REQUEST 508 + +//Commands +#define CORSAIR_ICUE_LINK_CMD_OPEN_ENDPOINT {0x0d, 0x01} +#define CORSAIR_ICUE_LINK_CMD_OPEN_COLOR_ENDPOINT {0x0d, 0x00} +#define CORSAIR_ICUE_LINK_CMD_CLOSE_ENDPOINT {0x05, 0x01, 0x01} +#define CORSAIR_ICUE_LINK_CMD_GET_FIRMWARE {0x02, 0x13} +#define CORSAIR_ICUE_LINK_CMD_SOFTWARE_MODE {0x01, 0x03, 0x00, 0x02} +#define CORSAIR_ICUE_LINK_CMD_HARDWARE_MODE {0x01, 0x03, 0x00, 0x01} +#define CORSAIR_ICUE_LINK_CMD_WRITE {0x06, 0x01} +#define CORSAIR_ICUE_LINK_CMD_WRITE_COLOR {0x06, 0x00} +#define CORSAIR_ICUE_LINK_CMD_WRITE_COLOR_NEXT {0x07, 0x00} +#define CORSAIR_ICUE_LINK_CMD_READ {0x08, 0x01} +#define CORSAIR_ICUE_LINK_CMD_GET_DEVICE_MODE {0x01, 0x08, 0x01} + +//Command modes +#define CORSAIR_ICUE_LINK_MODE_GET_DEVICES {0x36,} +#define CORSAIR_ICUE_LINK_MODE_GET_TEMPERATURES {0x21,} +#define CORSAIR_ICUE_LINK_MODE_GET_SPEEDS {0x17,} +#define CORSAIR_ICUE_LINK_MODE_SET_SPEED {0x18,} +#define CORSAIR_ICUE_LINK_MODE_SET_COLOR {0x22,} + +//Command data types +#define CORSAIR_ICUE_LINK_DATA_TYPE_GET_DEVICES {0x21, 0x00} +#define CORSAIR_ICUE_LINK_DATA_TYPE_GET_TEMPERATURES {0x10, 0x00} +#define CORSAIR_ICUE_LINK_DATA_TYPE_GET_SPEEDS {0x25, 0x00} +#define CORSAIR_ICUE_LINK_DATA_TYPE_SET_SPEED {0x07, 0x00} +#define CORSAIR_ICUE_LINK_DATA_TYPE_SET_COLOR {0x12, 0x00} + +typedef enum CORSAIR_ICUE_ENDPOINT_TYPE +{ + CORSAIR_ICUE_ENDPOINT_TYPE_DEFAULT, + CORSAIR_ICUE_ENDPOINT_TYPE_COLOR +} CORSAIR_ICUE_ENDPOINT_TYPE; + +const CorsairICueLinkDevice* FindCorsairICueLinkDevice(unsigned char type, unsigned char model); diff --git a/Controllers/CorsairICueLinkController/RGBController_CorsairICueLink.cpp b/Controllers/CorsairICueLinkController/RGBController_CorsairICueLink.cpp new file mode 100644 index 0000000..28d40a6 --- /dev/null +++ b/Controllers/CorsairICueLinkController/RGBController_CorsairICueLink.cpp @@ -0,0 +1,162 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairICueLink.cpp | +| | +| Driver for Corsair iCue Link Devices | +| | +| Aiden Vigue (acvigue) 02 Mar 2025 | +| Adam Honse 01 Aug 2025 | +| Nikola Jurkovic (jurkovic.nikola) 11 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CorsairICueLinkProtocol.h" +#include "RGBController_CorsairICueLink.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name Corsair iCUE Link Device Controller + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectCorsairICueLinkController + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CorsairICueLink::RGBController_CorsairICueLink(CorsairICueLinkController* controller) +{ + this->controller = controller; + + name = controller->GetNameString(); + vendor = "Corsair"; + description = "iCUE Link Device"; + version = controller->GetFirmwareString(); + location = controller->GetLocationString(); + serial = controller->GetSerialString(); + type = DEVICE_TYPE_COOLER; + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_CorsairICueLink::KeepaliveThread, this); +} + +RGBController_CorsairICueLink::~RGBController_CorsairICueLink() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_CorsairICueLink::SetupZones() +{ + for(std::size_t zone_idx = 0; zone_idx < controller->GetEndpoints().size(); zone_idx++) + { + if(controller->GetEndpoints()[zone_idx]->type == 0x06) + { + /*-----------------------------------------------------*\ + | We skip LCD processing here | + \*-----------------------------------------------------*/ + continue; + } + + zone new_zone; + new_zone.name = controller->GetEndpoints()[zone_idx]->display_name; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = controller->GetEndpoints()[zone_idx]->led_channels; + new_zone.leds_max = new_zone.leds_min; + new_zone.leds_count = new_zone.leds_min; + zones.push_back(new_zone); + + if(controller->GetEndpoints()[zone_idx]->type == 0x07 || controller->GetEndpoints()[zone_idx]->type == 0x11) + { + /*---------------------------------------------------------*\ + | iCUE LINK AIO 'H' Series || iCUE LINK AIO 'TITAN' Series | + \*---------------------------------------------------------*/ + for(std::size_t lcd_idx = 0; lcd_idx < controller->GetEndpoints().size(); lcd_idx++) + { + if(controller->GetEndpoints()[lcd_idx]->type == 0x06) + { + zone lcd_zone; + lcd_zone.name = controller->GetEndpoints()[lcd_idx]->display_name; + lcd_zone.type = ZONE_TYPE_LINEAR; + lcd_zone.leds_min = controller->GetEndpoints()[lcd_idx]->led_channels; + lcd_zone.leds_max = lcd_zone.leds_min; + lcd_zone.leds_count = lcd_zone.leds_min; + + zones.push_back(lcd_zone); + + for(unsigned int led_idx = 0; led_idx < lcd_zone.leds_count; led_idx++) + { + led new_led; + new_led.name = "LED " + std::to_string(led_idx + 1); + + leds.push_back(new_led); + } + } + } + } + + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + new_led.name = "LED " + std::to_string(led_idx + 1); + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_CorsairICueLink::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*-----------------------------------------------------*\ + | Device does not support resizing zones | + \*-----------------------------------------------------*/ +} + +void RGBController_CorsairICueLink::DeviceUpdateLEDs() +{ + controller->UpdateLights(&colors[0], colors.size()); +} + +void RGBController_CorsairICueLink::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairICueLink::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairICueLink::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairICueLink::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_commit_time) > std::chrono::seconds(5)) + { + DeviceUpdateLEDs(); + } + std::this_thread::sleep_for(1s); + } +} \ No newline at end of file diff --git a/Controllers/CorsairICueLinkController/RGBController_CorsairICueLink.h b/Controllers/CorsairICueLinkController/RGBController_CorsairICueLink.h new file mode 100644 index 0000000..dc3fdfd --- /dev/null +++ b/Controllers/CorsairICueLinkController/RGBController_CorsairICueLink.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairICueLink.h | +| | +| RGBController for Corsair iCue Link Devices | +| | +| Aiden Vigue (acvigue) 02 Mar 2025 | +| Adam Honse 01 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "CorsairICueLinkController.h" +#include "RGBController.h" + +class RGBController_CorsairICueLink : public RGBController +{ +public: + RGBController_CorsairICueLink(CorsairICueLinkController* controller); + ~RGBController_CorsairICueLink(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CorsairICueLinkController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_commit_time; + + void KeepaliveThread(); +}; diff --git a/Controllers/CorsairLightingNodeController/CorsairLightingNodeController.cpp b/Controllers/CorsairLightingNodeController/CorsairLightingNodeController.cpp new file mode 100644 index 0000000..b397942 --- /dev/null +++ b/Controllers/CorsairLightingNodeController/CorsairLightingNodeController.cpp @@ -0,0 +1,540 @@ +/*---------------------------------------------------------*\ +| CorsairLightingNodeController.cpp | +| | +| Driver for Corsair Lighting Node devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "CorsairLightingNodeController.h" +#include "CorsairDeviceGuard.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +CorsairLightingNodeController::CorsairLightingNodeController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + guard_manager_ptr = new DeviceGuardManager(new CorsairDeviceGuard()); + + SendFirmwareRequest(); + + /*-----------------------------------------------------*\ + | The Corsair Lighting Node Pro requires a packet within| + | 20 seconds of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 5s | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&CorsairLightingNodeController::KeepaliveThread, this); +} + +CorsairLightingNodeController::~CorsairLightingNodeController() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + hid_close(dev); + delete guard_manager_ptr; +} + +void CorsairLightingNodeController::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_commit_time) > std::chrono::seconds(5)) + { + SendCommit(); + } + std::this_thread::sleep_for(1s); + } +} + +std::string CorsairLightingNodeController::GetFirmwareString() +{ + return(firmware_version); +} + +std::string CorsairLightingNodeController::GetLocationString() +{ + return("HID: " + location); +} + +std::string CorsairLightingNodeController::GetNameString() +{ + return(name); +} + +std::string CorsairLightingNodeController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void CorsairLightingNodeController::SetBrightness(unsigned char brightness) +{ + for(unsigned int channel = 0; channel < CORSAIR_LIGHTING_NODE_NUM_CHANNELS; channel++) + { + SendBrightness(channel, brightness); + } +} + +void CorsairLightingNodeController::SetChannelEffect(unsigned char channel, + unsigned char num_leds, + unsigned char mode, + unsigned char speed, + unsigned char direction, + bool random, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2, + unsigned char red3, + unsigned char grn3, + unsigned char blu3 + ) +{ + /*-----------------------------------------------------*\ + | Send Reset packet | + \*-----------------------------------------------------*/ + SendReset(channel); + + /*-----------------------------------------------------*\ + | Send Begin packet | + \*-----------------------------------------------------*/ + SendBegin(channel); + + /*-----------------------------------------------------*\ + | Set Port State packet | + \*-----------------------------------------------------*/ + SendPortState(channel, CORSAIR_LIGHTING_NODE_PORT_STATE_HARDWARE); + + /*-----------------------------------------------------*\ + | Set Effect Configuration packet | + \*-----------------------------------------------------*/ + SendEffectConfig + ( + channel, + 0, + num_leds, + mode, + speed, + direction, + random, + red1, + grn1, + blu1, + red2, + grn2, + blu2, + red3, + grn3, + blu3, + 0, + 0, + 0 + ); + + /*-----------------------------------------------------*\ + | Send Commit packet | + \*-----------------------------------------------------*/ + SendCommit(); +} + +void CorsairLightingNodeController::SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors) +{ + unsigned char red_color_data[50]; + unsigned char grn_color_data[50]; + unsigned char blu_color_data[50]; + unsigned char pkt_offset = 0; + unsigned char pkt_size = 0; + unsigned int colors_remaining = num_colors; + + /*-----------------------------------------------------*\ + | Send Port State packet | + \*-----------------------------------------------------*/ + SendPortState(channel, CORSAIR_LIGHTING_NODE_PORT_STATE_SOFTWARE); + + /*-----------------------------------------------------*\ + | Loop through colors and send 50 at a time | + \*-----------------------------------------------------*/ + while(colors_remaining > 0) + { + if(colors_remaining < 50) + { + pkt_size = colors_remaining; + } + else + { + pkt_size = 50; + } + + for(int color_idx = 0; color_idx < pkt_size; color_idx++) + { + red_color_data[color_idx] = RGBGetRValue(colors[pkt_offset + color_idx]); + grn_color_data[color_idx] = RGBGetGValue(colors[pkt_offset + color_idx]); + blu_color_data[color_idx] = RGBGetBValue(colors[pkt_offset + color_idx]); + } + + SendDirect(channel, pkt_offset, pkt_size, CORSAIR_LIGHTING_NODE_DIRECT_CHANNEL_RED, red_color_data); + SendDirect(channel, pkt_offset, pkt_size, CORSAIR_LIGHTING_NODE_DIRECT_CHANNEL_GREEN, grn_color_data); + SendDirect(channel, pkt_offset, pkt_size, CORSAIR_LIGHTING_NODE_DIRECT_CHANNEL_BLUE, blu_color_data); + + colors_remaining -= pkt_size; + pkt_offset += pkt_size; + } + + /*-----------------------------------------------------*\ + | Send Commit packet | + \*-----------------------------------------------------*/ + SendCommit(); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void CorsairLightingNodeController::SendFirmwareRequest() +{ + int actual; + unsigned char usb_buf[CORSAIR_LIGHTING_NODE_WRITE_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Firmware Version Request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_LIGHTING_NODE_PACKET_ID_FIRMWARE; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + actual = WriteAndRead(usb_buf); + + if(actual > 0) + { + firmware_version = std::to_string(usb_buf[0x01]) + "." + std::to_string(usb_buf[0x02]) + "." + std::to_string(usb_buf[0x03]); + } +} + +void CorsairLightingNodeController::SendDirect + ( + unsigned char channel, + unsigned char start, + unsigned char count, + unsigned char color_channel, + unsigned char* color_data + ) +{ + unsigned char usb_buf[CORSAIR_LIGHTING_NODE_WRITE_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_LIGHTING_NODE_PACKET_ID_DIRECT; + usb_buf[0x02] = channel; + usb_buf[0x03] = start; + usb_buf[0x04] = count; + usb_buf[0x05] = color_channel; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x06], color_data, count); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + WriteAndRead(usb_buf, CORSAIR_LIGHTING_NODE_READ_TIMEOUT); +} + +void CorsairLightingNodeController::SendCommit() +{ + unsigned char usb_buf[CORSAIR_LIGHTING_NODE_WRITE_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Update last commit time | + \*-----------------------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_LIGHTING_NODE_PACKET_ID_COMMIT; + usb_buf[0x02] = 0xFF; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + WriteAndRead(usb_buf, CORSAIR_LIGHTING_NODE_READ_TIMEOUT); +} + +void CorsairLightingNodeController::SendBegin + ( + unsigned char channel + ) +{ + unsigned char usb_buf[CORSAIR_LIGHTING_NODE_WRITE_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Begin packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_LIGHTING_NODE_PACKET_ID_BEGIN; + usb_buf[0x02] = channel; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + WriteAndRead(usb_buf); +} + +void CorsairLightingNodeController::SendEffectConfig + ( + unsigned char channel, + unsigned char count, + unsigned char led_type, + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char change_style, + unsigned char color_0_red, + unsigned char color_0_green, + unsigned char color_0_blue, + unsigned char color_1_red, + unsigned char color_1_green, + unsigned char color_1_blue, + unsigned char color_2_red, + unsigned char color_2_green, + unsigned char color_2_blue, + unsigned short temperature_0, + unsigned short temperature_1, + unsigned short temperature_2 + ) +{ + unsigned char usb_buf[CORSAIR_LIGHTING_NODE_WRITE_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Effect Config packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_LIGHTING_NODE_PACKET_ID_EFFECT_CONFIG; + usb_buf[0x02] = channel; + usb_buf[0x03] = count; + usb_buf[0x04] = led_type; + + /*-----------------------------------------------------*\ + | Set up mode parameters | + \*-----------------------------------------------------*/ + usb_buf[0x05] = mode; + usb_buf[0x06] = speed; + usb_buf[0x07] = direction; + usb_buf[0x08] = change_style; + usb_buf[0x09] = 0; + + /*-----------------------------------------------------*\ + | Set up mode colors | + \*-----------------------------------------------------*/ + usb_buf[0x0A] = color_0_red; + usb_buf[0x0B] = color_0_green; + usb_buf[0x0C] = color_0_blue; + usb_buf[0x0D] = color_1_red; + usb_buf[0x0E] = color_1_green; + usb_buf[0x0F] = color_1_blue; + usb_buf[0x10] = color_2_red; + usb_buf[0x11] = color_2_green; + usb_buf[0x12] = color_2_blue; + + /*-----------------------------------------------------*\ + | Set up temperatures | + \*-----------------------------------------------------*/ + usb_buf[0x13] = (temperature_0 >> 8); + usb_buf[0x14] = (temperature_0 & 0xFF); + usb_buf[0x15] = (temperature_1 >> 8); + usb_buf[0x16] = (temperature_1 & 0xFF); + usb_buf[0x17] = (temperature_2 >> 8); + usb_buf[0x18] = (temperature_2 & 0xFF); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + WriteAndRead(usb_buf); +} + +void CorsairLightingNodeController::SendTemperature() +{ + +} + +void CorsairLightingNodeController::SendReset + ( + unsigned char channel + ) +{ + unsigned char usb_buf[CORSAIR_LIGHTING_NODE_WRITE_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Reset packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_LIGHTING_NODE_PACKET_ID_RESET; + usb_buf[0x02] = channel; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + WriteAndRead(usb_buf); +} + +void CorsairLightingNodeController::SendPortState + ( + unsigned char channel, + unsigned char state + ) +{ + unsigned char usb_buf[CORSAIR_LIGHTING_NODE_WRITE_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Port State packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_LIGHTING_NODE_PACKET_ID_PORT_STATE; + usb_buf[0x02] = channel; + usb_buf[0x03] = state; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + WriteAndRead(usb_buf, CORSAIR_LIGHTING_NODE_READ_TIMEOUT); +} + +void CorsairLightingNodeController::SendBrightness + ( + unsigned char channel, + unsigned char brightness + ) +{ + unsigned char usb_buf[CORSAIR_LIGHTING_NODE_WRITE_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Brightness goes from 0-100 | + \*-----------------------------------------------------*/ + if(brightness > 100) + { + brightness = 100; + } + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Port State packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_LIGHTING_NODE_PACKET_ID_BRIGHTNESS; + usb_buf[0x02] = channel; + usb_buf[0x03] = brightness; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + WriteAndRead(usb_buf); +} + +void CorsairLightingNodeController::SendLEDCount() +{ + +} + +void CorsairLightingNodeController::SendProtocol() +{ + +} + +int CorsairLightingNodeController::WriteAndRead + ( + unsigned char *buf, + int read_timeout_ms + ) +{ + int hid_read_ret; + + /*---------------------------------------------------------*\ + | HID I/O start | + \*---------------------------------------------------------*/ + { + DeviceGuardLock _ = guard_manager_ptr->AwaitExclusiveAccess(); + + hid_write(dev, buf, CORSAIR_LIGHTING_NODE_WRITE_PACKET_SIZE); + if(read_timeout_ms > 0) + { + hid_read_ret = hid_read_timeout(dev, buf, CORSAIR_LIGHTING_NODE_READ_PACKET_SIZE, read_timeout_ms); + } + else + { + hid_read_ret = hid_read(dev, buf, CORSAIR_LIGHTING_NODE_READ_PACKET_SIZE); + } + } + /*---------------------------------------------------------*\ + | HID I/O end (lock released) | + \*---------------------------------------------------------*/ + + return hid_read_ret; +} diff --git a/Controllers/CorsairLightingNodeController/CorsairLightingNodeController.h b/Controllers/CorsairLightingNodeController/CorsairLightingNodeController.h new file mode 100644 index 0000000..d8431b3 --- /dev/null +++ b/Controllers/CorsairLightingNodeController/CorsairLightingNodeController.h @@ -0,0 +1,203 @@ +/*---------------------------------------------------------*\ +| CorsairLightingNodeController.h | +| | +| Driver for Corsair Lighting Node devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "DeviceGuardManager.h" +#include "RGBController.h" + +#define CORSAIR_LIGHTING_NODE_WRITE_PACKET_SIZE 65 /* First byte is the report number */ +#define CORSAIR_LIGHTING_NODE_READ_PACKET_SIZE 17 /* First byte is the report number */ +#define CORSAIR_LIGHTING_NODE_READ_TIMEOUT 15 /* Timeout in milliseconds */ +enum +{ + CORSAIR_LIGHTING_NODE_PACKET_ID_FIRMWARE = 0x02, /* Get firmware version */ + CORSAIR_LIGHTING_NODE_PACKET_ID_DIRECT = 0x32, /* Direct mode LED update packet */ + CORSAIR_LIGHTING_NODE_PACKET_ID_COMMIT = 0x33, /* Commit changes packet */ + CORSAIR_LIGHTING_NODE_PACKET_ID_BEGIN = 0x34, /* Begin effect packet */ + CORSAIR_LIGHTING_NODE_PACKET_ID_EFFECT_CONFIG = 0x35, /* Effect mode configuration packet */ + CORSAIR_LIGHTING_NODE_PACKET_ID_TEMPERATURE = 0x36, /* Update temperature value packet */ + CORSAIR_LIGHTING_NODE_PACKET_ID_RESET = 0x37, /* Reset channel packet */ + CORSAIR_LIGHTING_NODE_PACKET_ID_PORT_STATE = 0x38, /* Set port state packet */ + CORSAIR_LIGHTING_NODE_PACKET_ID_BRIGHTNESS = 0x39, /* Set brightness packet */ + CORSAIR_LIGHTING_NODE_PACKET_ID_LED_COUNT = 0x3A, /* Set LED count packet */ + CORSAIR_LIGHTING_NODE_PACKET_ID_PROTOCOL = 0x3B, /* Set protocol packet */ +}; + +enum +{ + CORSAIR_LIGHTING_NODE_DIRECT_CHANNEL_RED = 0x00, /* Red channel for direct update */ + CORSAIR_LIGHTING_NODE_DIRECT_CHANNEL_GREEN = 0x01, /* Green channel for direct update */ + CORSAIR_LIGHTING_NODE_DIRECT_CHANNEL_BLUE = 0x02, /* Blue channel for direct update */ +}; + +enum +{ + CORSAIR_LIGHTING_NODE_PORT_STATE_HARDWARE = 0x01, /* Effect hardware control of channel */ + CORSAIR_LIGHTING_NODE_PORT_STATE_SOFTWARE = 0x02, /* Direct software control of channel */ +}; + +enum +{ + CORSAIR_LIGHTING_NODE_LED_TYPE_LED_STRIP = 0x0A, /* Corsair LED Strip Type */ + CORSAIR_LIGHTING_NODE_LED_TYPE_HD_FAN = 0x0C, /* Corsair HD-series Fan Type */ + CORSAIR_LIGHTING_NODE_LED_TYPE_SP_FAN = 0x01, /* Corsair SP-series Fan Type */ + CORSAIR_LIGHTING_NODE_LED_TYPE_ML_FAN = 0x02, /* Corsair ML-series Fan Type */ +}; + +enum +{ + CORSAIR_LIGHTING_NODE_CHANNEL_1 = 0x00, /* Channel 1 */ + CORSAIR_LIGHTING_NODE_CHANNEL_2 = 0x01, /* Channel 2 */ + CORSAIR_LIGHTING_NODE_NUM_CHANNELS = 0x02, /* Number of channels */ +}; + +enum +{ + CORSAIR_LIGHTING_NODE_SPEED_FAST = 0x00, /* Fast speed */ + CORSAIR_LIGHTING_NODE_SPEED_MEDIUM = 0x01, /* Medium speed */ + CORSAIR_LIGHTING_NODE_SPEED_SLOW = 0x02, /* Slow speed */ +}; + +enum +{ + CORSAIR_LIGHTING_NODE_MODE_RAINBOW_WAVE = 0x00, /* Rainbow Wave mode */ + CORSAIR_LIGHTING_NODE_MODE_COLOR_SHIFT = 0x01, /* Color Shift mode */ + CORSAIR_LIGHTING_NODE_MODE_COLOR_PULSE = 0x02, /* Color Pulse mode */ + CORSAIR_LIGHTING_NODE_MODE_COLOR_WAVE = 0x03, /* Color Wave mode */ + CORSAIR_LIGHTING_NODE_MODE_STATIC = 0x04, /* Static mode */ + CORSAIR_LIGHTING_NODE_MODE_TEMPERATURE = 0x05, /* Temperature mode */ + CORSAIR_LIGHTING_NODE_MODE_VISOR = 0x06, /* Visor mode */ + CORSAIR_LIGHTING_NODE_MODE_MARQUEE = 0x07, /* Marquee mode */ + CORSAIR_LIGHTING_NODE_MODE_BLINK = 0x08, /* Blink mode */ + CORSAIR_LIGHTING_NODE_MODE_SEQUENTIAL = 0x09, /* Sequential mode */ + CORSAIR_LIGHTING_NODE_MODE_RAINBOW = 0x0A, /* Rainbow mode */ +}; + +class CorsairLightingNodeController +{ +public: + CorsairLightingNodeController(hid_device* dev_handle, const char* path, std::string dev_name); + ~CorsairLightingNodeController(); + + std::string GetFirmwareString(); + std::string GetLocationString(); + std::string GetNameString(); + std::string GetSerialString(); + + unsigned int GetStripsOnChannel(unsigned int channel); + + void SetBrightness(unsigned char brightness); + + void SetChannelEffect(unsigned char channel, + unsigned char num_leds, + unsigned char mode, + unsigned char speed, + unsigned char direction, + bool random, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2, + unsigned char red3, + unsigned char grn3, + unsigned char blu3 + ); + + void SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors); + + void KeepaliveThread(); + +private: + hid_device* dev; + std::string firmware_version; + std::string location; + std::string name; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_commit_time; + DeviceGuardManager* guard_manager_ptr; + + void SendFirmwareRequest(); + + void SendDirect + ( + unsigned char channel, + unsigned char start, + unsigned char count, + unsigned char color_channel, + unsigned char* color_data + ); + + void SendCommit(); + + void SendBegin + ( + unsigned char channel + ); + + void SendEffectConfig + ( + unsigned char channel, + unsigned char count, + unsigned char led_type, + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char change_style, + unsigned char color_0_red, + unsigned char color_0_green, + unsigned char color_0_blue, + unsigned char color_1_red, + unsigned char color_1_green, + unsigned char color_1_blue, + unsigned char color_2_red, + unsigned char color_2_green, + unsigned char color_2_blue, + unsigned short temperature_0, + unsigned short temperature_1, + unsigned short temperature_2 + ); + + void SendTemperature(); + + void SendReset + ( + unsigned char channel + ); + + void SendPortState + ( + unsigned char channel, + unsigned char state + ); + + void SendBrightness + ( + unsigned char channel, + unsigned char brightness + ); + + void SendLEDCount(); + + void SendProtocol(); + + int WriteAndRead + ( + unsigned char *buf, + int read_timeout_ms = -1 + ); +}; diff --git a/Controllers/CorsairLightingNodeController/CorsairLightingNodeControllerDetect.cpp b/Controllers/CorsairLightingNodeController/CorsairLightingNodeControllerDetect.cpp new file mode 100644 index 0000000..e094a84 --- /dev/null +++ b/Controllers/CorsairLightingNodeController/CorsairLightingNodeControllerDetect.cpp @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| CorsairLightingNodeControllerDetect.cpp | +| | +| Detector for Corsair Lighting Node devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "CorsairLightingNodeController.h" +#include "RGBController_CorsairLightingNode.h" + +#define CORSAIR_VID 0x1B1C +#define CORSAIR_LIGHTING_NODE_CORE_PID 0x0C1A +#define CORSAIR_LIGHTING_NODE_PRO_PID 0x0C0B +#define CORSAIR_COMMANDER_PRO_PID 0x0C10 +#define CORSAIR_LS100_PID 0x0C1E +#define CORSAIR_1000D_OBSIDIAN_PID 0x1D00 +#define CORSAIR_SPEC_OMEGA_RGB_PID 0x1D04 +#define CORSAIR_LT100_PID 0x0C23 + +/******************************************************************************************\ +* * +* DetectCorsairLightingNodeControllers * +* * +* Detect devices supported by the Corsair Lighting Node Pro driver * +* * +\******************************************************************************************/ + +void DetectCorsairLightingNodeControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CorsairLightingNodeController* controller = new CorsairLightingNodeController(dev, info->path, name); + RGBController_CorsairLightingNode* rgb_controller = new RGBController_CorsairLightingNode(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectCorsairLightingNodeControllers() */ + +REGISTER_HID_DETECTOR("Corsair Lighting Node Core", DetectCorsairLightingNodeControllers, CORSAIR_VID, CORSAIR_LIGHTING_NODE_CORE_PID); // 1 channel +REGISTER_HID_DETECTOR("Corsair Lighting Node Pro", DetectCorsairLightingNodeControllers, CORSAIR_VID, CORSAIR_LIGHTING_NODE_PRO_PID); // 2 channels +REGISTER_HID_DETECTOR("Corsair Commander Pro", DetectCorsairLightingNodeControllers, CORSAIR_VID, CORSAIR_COMMANDER_PRO_PID); // 2 channels +REGISTER_HID_DETECTOR("Corsair LS100 Lighting Kit", DetectCorsairLightingNodeControllers, CORSAIR_VID, CORSAIR_LS100_PID); // 1 channel +REGISTER_HID_DETECTOR("Corsair 1000D Obsidian", DetectCorsairLightingNodeControllers, CORSAIR_VID, CORSAIR_1000D_OBSIDIAN_PID); // 2 channels +REGISTER_HID_DETECTOR("Corsair SPEC OMEGA RGB", DetectCorsairLightingNodeControllers, CORSAIR_VID, CORSAIR_SPEC_OMEGA_RGB_PID); // 2 channels +REGISTER_HID_DETECTOR("Corsair LT100", DetectCorsairLightingNodeControllers, CORSAIR_VID, CORSAIR_LT100_PID); // 2 channels diff --git a/Controllers/CorsairLightingNodeController/RGBController_CorsairLightingNode.cpp b/Controllers/CorsairLightingNodeController/RGBController_CorsairLightingNode.cpp new file mode 100644 index 0000000..df70793 --- /dev/null +++ b/Controllers/CorsairLightingNodeController/RGBController_CorsairLightingNode.cpp @@ -0,0 +1,376 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairLightingNode.cpp | +| | +| RGBController for Corsair Lighting Node devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CorsairLightingNode.h" + +/**------------------------------------------------------------------*\ + @name Corsair Lighting Node + @category LEDStrip + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCorsairLightingNodeControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CorsairLightingNode::RGBController_CorsairLightingNode(CorsairLightingNodeController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Corsair"; + description = "Corsair Lighting Node Device"; + type = DEVICE_TYPE_LEDSTRIP; + version = controller->GetFirmwareString(); + location = controller->GetLocationString(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = CORSAIR_LIGHTING_NODE_MODE_RAINBOW_WAVE; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RainbowWave.speed_min = CORSAIR_LIGHTING_NODE_SPEED_SLOW; + RainbowWave.speed_max = CORSAIR_LIGHTING_NODE_SPEED_FAST; + RainbowWave.speed = CORSAIR_LIGHTING_NODE_SPEED_MEDIUM; + RainbowWave.direction = MODE_DIRECTION_RIGHT; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.brightness_min = 0; + RainbowWave.brightness_max = 100; + RainbowWave.brightness = 100; + modes.push_back(RainbowWave); + + mode ColorShift; + ColorShift.name = "Color Shift"; + ColorShift.value = CORSAIR_LIGHTING_NODE_MODE_COLOR_SHIFT; + ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorShift.colors_min = 2; + ColorShift.colors_max = 2; + ColorShift.speed_min = CORSAIR_LIGHTING_NODE_SPEED_SLOW; + ColorShift.speed_max = CORSAIR_LIGHTING_NODE_SPEED_FAST; + ColorShift.speed = CORSAIR_LIGHTING_NODE_SPEED_MEDIUM; + ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorShift.colors.resize(2); + ColorShift.brightness_min = 0; + ColorShift.brightness_max = 100; + ColorShift.brightness = 100; + modes.push_back(ColorShift); + + mode ColorPulse; + ColorPulse.name = "Color Pulse"; + ColorPulse.value = CORSAIR_LIGHTING_NODE_MODE_COLOR_PULSE; + ColorPulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorPulse.colors_min = 2; + ColorPulse.colors_max = 2; + ColorPulse.speed_min = CORSAIR_LIGHTING_NODE_SPEED_SLOW; + ColorPulse.speed_max = CORSAIR_LIGHTING_NODE_SPEED_FAST; + ColorPulse.speed = CORSAIR_LIGHTING_NODE_SPEED_MEDIUM; + ColorPulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorPulse.colors.resize(2); + ColorPulse.brightness_min = 0; + ColorPulse.brightness_max = 100; + ColorPulse.brightness = 100; + modes.push_back(ColorPulse); + + mode ColorWave; + ColorWave.name = "Color Wave"; + ColorWave.value = CORSAIR_LIGHTING_NODE_MODE_COLOR_WAVE; + ColorWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorWave.colors_min = 2; + ColorWave.colors_max = 2; + ColorWave.speed_min = CORSAIR_LIGHTING_NODE_SPEED_SLOW; + ColorWave.speed_max = CORSAIR_LIGHTING_NODE_SPEED_FAST; + ColorWave.speed = CORSAIR_LIGHTING_NODE_SPEED_MEDIUM; + ColorWave.direction = MODE_DIRECTION_RIGHT; + ColorWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorWave.colors.resize(2); + ColorWave.brightness_min = 0; + ColorWave.brightness_max = 100; + ColorWave.brightness = 100; + modes.push_back(ColorWave); + + mode Static; + Static.name = "Static"; + Static.value = CORSAIR_LIGHTING_NODE_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + Static.brightness_min = 0; + Static.brightness_max = 100; + Static.brightness = 100; + modes.push_back(Static); + + mode Temperature; + Temperature.name = "Temperature"; + Temperature.value = CORSAIR_LIGHTING_NODE_MODE_TEMPERATURE; + Temperature.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Temperature.colors_min = 3; + Temperature.colors_max = 3; + Temperature.color_mode = MODE_COLORS_MODE_SPECIFIC; + Temperature.colors.resize(3); + Temperature.brightness_min = 0; + Temperature.brightness_max = 100; + Temperature.brightness = 100; + modes.push_back(Temperature); + + mode Visor; + Visor.name = "Visor"; + Visor.value = CORSAIR_LIGHTING_NODE_MODE_VISOR; + Visor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Visor.colors_min = 2; + Visor.colors_max = 2; + Visor.speed_min = CORSAIR_LIGHTING_NODE_SPEED_SLOW; + Visor.speed_max = CORSAIR_LIGHTING_NODE_SPEED_FAST; + Visor.speed = CORSAIR_LIGHTING_NODE_SPEED_MEDIUM; + Visor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Visor.colors.resize(2); + Visor.brightness_min = 0; + Visor.brightness_max = 100; + Visor.brightness = 100; + modes.push_back(Visor); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = CORSAIR_LIGHTING_NODE_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.speed_min = CORSAIR_LIGHTING_NODE_SPEED_SLOW; + Marquee.speed_max = CORSAIR_LIGHTING_NODE_SPEED_FAST; + Marquee.speed = CORSAIR_LIGHTING_NODE_SPEED_MEDIUM; + Marquee.direction = MODE_DIRECTION_RIGHT; + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + Marquee.colors.resize(1); + Marquee.brightness_min = 0; + Marquee.brightness_max = 100; + Marquee.brightness = 100; + modes.push_back(Marquee); + + mode Blink; + Blink.name = "Blink"; + Blink.value = CORSAIR_LIGHTING_NODE_MODE_BLINK; + Blink.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Blink.colors_min = 2; + Blink.colors_max = 2; + Blink.speed_min = CORSAIR_LIGHTING_NODE_SPEED_SLOW; + Blink.speed_max = CORSAIR_LIGHTING_NODE_SPEED_FAST; + Blink.speed = CORSAIR_LIGHTING_NODE_SPEED_MEDIUM; + Blink.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blink.colors.resize(2); + Blink.brightness_min = 0; + Blink.brightness_max = 100; + Blink.brightness = 100; + modes.push_back(Blink); + + mode Sequential; + Sequential.name = "Sequential"; + Sequential.value = CORSAIR_LIGHTING_NODE_MODE_SEQUENTIAL; + Sequential.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Sequential.colors_min = 1; + Sequential.colors_max = 1; + Sequential.speed_min = CORSAIR_LIGHTING_NODE_SPEED_SLOW; + Sequential.speed_max = CORSAIR_LIGHTING_NODE_SPEED_FAST; + Sequential.speed = CORSAIR_LIGHTING_NODE_SPEED_MEDIUM; + Sequential.direction = MODE_DIRECTION_RIGHT; + Sequential.color_mode = MODE_COLORS_MODE_SPECIFIC; + Sequential.colors.resize(1); + Sequential.brightness_min = 0; + Sequential.brightness_max = 100; + Sequential.brightness = 100; + modes.push_back(Sequential); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = CORSAIR_LIGHTING_NODE_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.speed_min = CORSAIR_LIGHTING_NODE_SPEED_SLOW; + Rainbow.speed_max = CORSAIR_LIGHTING_NODE_SPEED_FAST; + Rainbow.speed = CORSAIR_LIGHTING_NODE_SPEED_MEDIUM; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness_min = 0; + Rainbow.brightness_max = 100; + Rainbow.brightness = 100; + modes.push_back(Rainbow); + + SetupZones(); +} + +RGBController_CorsairLightingNode::~RGBController_CorsairLightingNode() +{ + delete controller; +} + +void RGBController_CorsairLightingNode::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(CORSAIR_LIGHTING_NODE_NUM_CHANNELS); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for (unsigned int channel_idx = 0; channel_idx < CORSAIR_LIGHTING_NODE_NUM_CHANNELS; channel_idx++) + { + char ch_idx_string[2]; + snprintf(ch_idx_string, 2, "%d", channel_idx + 1); + + zones[channel_idx].name = "Corsair Channel "; + zones[channel_idx].name.append(ch_idx_string); + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + /*-------------------------------------------------*\ + | I did some experimenting and determined that the | + | maximum number of LEDs the Corsair Commander Pro | + | can support is 200. | + \*-------------------------------------------------*/ + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = 204; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + + zones[channel_idx].matrix_map = NULL; + + for (unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + char led_idx_string[4]; + snprintf(led_idx_string, 4, "%d", led_ch_idx + 1); + + led new_led; + new_led.name = "Corsair Channel "; + new_led.name.append(ch_idx_string); + new_led.name.append(", LED "); + new_led.name.append(led_idx_string); + + leds.push_back(new_led); + leds_channel.push_back(channel_idx); + } + } + + SetupColors(); +} + +void RGBController_CorsairLightingNode::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_CorsairLightingNode::DeviceUpdateLEDs() +{ + for(unsigned char zone_idx = 0; zone_idx < (unsigned char)zones.size(); zone_idx++) + { + controller->SetChannelLEDs(zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} + +void RGBController_CorsairLightingNode::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_CorsairLightingNode::UpdateSingleLED(int led) +{ + unsigned int channel = leds_channel[led]; + + controller->SetChannelLEDs(channel, zones[channel].colors, zones[channel].leds_count); +} + +void RGBController_CorsairLightingNode::DeviceUpdateMode() +{ + if(modes[active_mode].value == 0xFFFF) + { + DeviceUpdateLEDs(); + } + else + { + for(int channel = 0; channel < CORSAIR_LIGHTING_NODE_NUM_CHANNELS; channel++) + { + unsigned int direction = 0; + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + + if(modes[active_mode].direction == MODE_DIRECTION_RIGHT) + { + direction = 1; + } + + unsigned char mode_colors[9]; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + for(std::size_t i = 0; i < modes[active_mode].colors.size(); i++) + { + mode_colors[(3 * i) + 0] = RGBGetRValue(modes[active_mode].colors[i]); + mode_colors[(3 * i) + 1] = RGBGetGValue(modes[active_mode].colors[i]); + mode_colors[(3 * i) + 2] = RGBGetBValue(modes[active_mode].colors[i]); + } + } + + controller->SetChannelEffect(channel, + zones[channel].leds_count, + modes[active_mode].value, + modes[active_mode].speed, + direction, + random, + mode_colors[0], + mode_colors[1], + mode_colors[2], + mode_colors[3], + mode_colors[4], + mode_colors[5], + mode_colors[6], + mode_colors[7], + mode_colors[8]); + } + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + controller->SetBrightness(modes[active_mode].brightness); + } + else + { + controller->SetBrightness(100); + } +} diff --git a/Controllers/CorsairLightingNodeController/RGBController_CorsairLightingNode.h b/Controllers/CorsairLightingNodeController/RGBController_CorsairLightingNode.h new file mode 100644 index 0000000..5f27517 --- /dev/null +++ b/Controllers/CorsairLightingNodeController/RGBController_CorsairLightingNode.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairLightingNode.h | +| | +| RGBController for Corsair Lighting Node devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairLightingNodeController.h" + +class RGBController_CorsairLightingNode : public RGBController +{ +public: + RGBController_CorsairLightingNode(CorsairLightingNodeController* controller_ptr); + ~RGBController_CorsairLightingNode(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CorsairLightingNodeController* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/CorsairPeripheralController/CorsairK55RGBPROXTController.cpp b/Controllers/CorsairPeripheralController/CorsairK55RGBPROXTController.cpp new file mode 100644 index 0000000..6693d91 --- /dev/null +++ b/Controllers/CorsairPeripheralController/CorsairK55RGBPROXTController.cpp @@ -0,0 +1,287 @@ +/*---------------------------------------------------------*\ +| CorsairK55RGBPROXTController.cpp | +| | +| Driver for Corsair K55 RGB PRO XT keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CorsairK55RGBPROXTController.h" +#include "LogManager.h" +#include "StringUtils.h" + +#define COLOR_BANK_SIZE 137 +#define HID_PACKET_LENGTH 65 +#define HID_PAYLOAD_SIZE1 (HID_PACKET_LENGTH - 12) +#define HID_PAYLOAD_SIZE2 (HID_PACKET_LENGTH - 4) + +static const unsigned int keys[] = + { 127, 128, 129, 130, 131, 132, 37, 49, 39, 53, 102, 101, 26, 96, 104, 54, 27, 16, 0, 25, + 103, 55, 28, 22, 18, 23, 56, 29, 4, 3, 2, 57, 30, 17, 5, 21, 31, 58, 32, 19, + 6, 1, 40, 59, 33, 24, 7, 60, 34, 20, 9, 13, 61, 35, 8, 10, 12, 14, 11, 50, + 62, 41, 15, 47, 51, 107, 63, 42, 43, 48, 52, 118, 64, 38, 44, 46, 106, 97, 65, 36, + 105, 66, 69, 72, 76, 67, 70, 73, 78, 77, 68, 71, 74, 75, 79, 91, 88, 85, 94, 80, + 92, 89, 86, 81, 93, 90, 87, 95, 82, 83, 84 }; + + +static unsigned char color_bank[3][COLOR_BANK_SIZE]; + +static const unsigned char filler[] = + { 0x70, 0x6E, 0x4E, 0x4D, 0x4C, 0x65, 0x6F, 0x2C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x05, + 0x19, 0x06, 0x1B, 0x1D, 0x64, 0x6A, 0x34, 0x6B, 0x6C, 0x69, 0x38, 0x37, 0x36, 0x10, 0x11, 0x16, + 0x04, 0x39, 0x2F, 0x13, 0x12, 0x0C, 0x18, 0x33, 0x0F, 0x0E, 0x0D, 0x0B, 0x0A, 0x09, 0x07, 0x27, + 0x7A, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1C, 0x17, 0x15, 0x08, 0x1A, 0x14, 0x2B, 0x2D, + 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x29, 0x1F, 0x1E, 0x35, 0x44, 0x43, 0x42, 0x41, 0x40, 0x62, + 0x00, 0x5B, 0x5A, 0x59, 0x5E, 0x5D, 0x5C, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x63, 0x53, 0x4F, + 0x61, 0x60, 0x5F, 0x58, 0x57, 0x56, 0x55, 0x54, 0x2A, 0x2E, 0x28, 0x32, 0x30, 0x51, 0x50, 0x52, + 0x6D }; + + +CorsairK55RGBPROXTController::CorsairK55RGBPROXTController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + LightingControl(); +} + +CorsairK55RGBPROXTController::~CorsairK55RGBPROXTController() +{ + hid_close(dev); +} + +std::string CorsairK55RGBPROXTController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string CorsairK55RGBPROXTController::GetNameString() +{ + return(name); +} + +std::string CorsairK55RGBPROXTController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void CorsairK55RGBPROXTController::LightingControl() +{ + unsigned char usb_buf[HID_PACKET_LENGTH]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x03; + usb_buf[0x05] = 0x02; + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x02; + usb_buf[0x03] = 0x5F; + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x0D; + usb_buf[0x04] = 0x01; + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); +} + +void CorsairK55RGBPROXTController::SetLEDs(std::vectorcolors) +{ + for(std::size_t color_idx = 0; color_idx < colors.size(); ++color_idx) + { + RGBColor color = colors[color_idx]; + color_bank[0][keys[color_idx]] = RGBGetRValue(color); + color_bank[1][keys[color_idx]] = RGBGetGValue(color); + color_bank[2][keys[color_idx]] = RGBGetBValue(color); + } + + unsigned char* color_ptr = &color_bank[0][0]; + unsigned char usb_buf[HID_PACKET_LENGTH]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x06; + usb_buf[0x04] = 0x9B; + usb_buf[0x05] = 0x01; + + memcpy(&usb_buf[12], color_ptr, HID_PAYLOAD_SIZE1); + color_ptr += HID_PAYLOAD_SIZE1; + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); + + usb_buf[0x02] = 0x07; + + for(std::size_t i = 0; i < 6; ++i) + { + memcpy(&usb_buf[4], color_ptr, HID_PAYLOAD_SIZE2); + color_ptr += HID_PAYLOAD_SIZE2; + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); + } +} + +void CorsairK55RGBPROXTController::SetHardwareMode + ( + int mode_value, + unsigned int color_mode, + std::vector colors, + unsigned int speed, + unsigned int direction + ) +{ + LightingControl(); + + unsigned char usb_buf[HID_PACKET_LENGTH]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x0D; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x61; + usb_buf[0x05] = 0x6D; + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x09; + usb_buf[0x03] = 0x01; + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x06; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x78; + usb_buf[0x08] = mode_value & 0x00FF; + usb_buf[0x09] = mode_value >> 8; + + if(color_mode != MODE_COLORS_NONE) + { + usb_buf[0x0A] = color_mode == MODE_COLORS_RANDOM ? CORSAIR_HW_MODE_COLOR_RANDOM : CORSAIR_HW_MODE_COLOR_PREDEF; + } + + if(mode_value == CORSAIR_HW_MODE_WATER_COLOR_VALUE) + { + usb_buf[0x0A] = CORSAIR_HW_MODE_COLOR_UNKNOWN; + } + + usb_buf[0x0B] = speed; + + if((mode_value == CORSAIR_HW_MODE_COLOR_WAVE_VALUE) || + (mode_value == CORSAIR_HW_MODE_RAINBOW_WAVE_VALUE) || + (mode_value == CORSAIR_HW_MODE_RAIN_VALUE) || + (mode_value == CORSAIR_HW_MODE_SPIRAL_VALUE) || + (mode_value == CORSAIR_HW_MODE_VISOR_VALUE)) + { + switch(direction) + { + case MODE_DIRECTION_LEFT: + if(mode_value == CORSAIR_HW_MODE_SPIRAL_VALUE) + { + usb_buf[0x0C] = CORSAIE_HW_MODE_DIR_COUNTER_CLOCK_WISE; + } + else + { + usb_buf[0x0C] = CORSAIR_HW_MODE_DIR_LEFT; + } + break; + + case MODE_DIRECTION_RIGHT: + if(mode_value == CORSAIR_HW_MODE_SPIRAL_VALUE) + { + usb_buf[0x0C] = CORSAIR_HW_MODE_DIR_CLOCK_WISE; + } + else + { + usb_buf[0x0C] = CORSAIR_HW_MODE_DIR_RIGHT; + } + break; + + case MODE_DIRECTION_UP: + usb_buf[0x0C] = CORSAIR_HW_MODE_DIR_UP; + break; + + case MODE_DIRECTION_DOWN: + usb_buf[0x0C] = CORSAIR_HW_MODE_DIR_DOWN; + break; + + default: + usb_buf[0x0C] = CORSAIR_HW_MODE_DIR_NONE; + break; + } + } + + int fill_dest_index = 0x0F; + int fill_src_index = 0x00; + + if(usb_buf[0x0A] != CORSAIR_HW_MODE_COLOR_RANDOM) + { + usb_buf[0x0E] = (unsigned char)colors.size(); + + for(size_t i = 0; i < colors.size(); ++i) + { + usb_buf[0x0F + i * 4] = 0xFF; + usb_buf[0x10 + i * 4] = RGBGetBValue(colors[i]); + usb_buf[0x11 + i * 4] = RGBGetGValue(colors[i]); + usb_buf[0x12 + i * 4] = RGBGetRValue(colors[i]); + usb_buf[4] += 4; + fill_dest_index += 4; + } + } + + memcpy(&usb_buf[fill_dest_index], &filler[fill_src_index], HID_PACKET_LENGTH - fill_dest_index); + fill_src_index += (HID_PACKET_LENGTH - fill_dest_index); + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x07; + usb_buf[0x03] = 0x01; + memcpy(&usb_buf[4], &filler[fill_src_index], HID_PACKET_LENGTH - 4); + fill_src_index += (HID_PACKET_LENGTH - 4); + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x07; + usb_buf[0x03] = 0x01; + memcpy(&usb_buf[4], &filler[fill_src_index], sizeof(filler) - fill_src_index); + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x05; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x01; + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); +} + +void CorsairK55RGBPROXTController::SwitchMode(bool software) +{ + if(software) + { + LightingControl(); + } + else + { + unsigned char usb_buf[HID_PACKET_LENGTH]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x00; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x03; + usb_buf[0x05] = 0x01; + hid_write(dev, (unsigned char *)usb_buf, HID_PACKET_LENGTH); + } +} diff --git a/Controllers/CorsairPeripheralController/CorsairK55RGBPROXTController.h b/Controllers/CorsairPeripheralController/CorsairK55RGBPROXTController.h new file mode 100644 index 0000000..a8aba1b --- /dev/null +++ b/Controllers/CorsairPeripheralController/CorsairK55RGBPROXTController.h @@ -0,0 +1,87 @@ +/*---------------------------------------------------------*\ +| CorsairK55RGBPROXTController.h | +| | +| Driver for Corsair K55 RGB PRO XT keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class CorsairK55RGBPROXTController +{ +public: + CorsairK55RGBPROXTController(hid_device* dev_handle, const char* path, std::string dev_name); + ~CorsairK55RGBPROXTController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetLEDs(std::vector colors); + void SetHardwareMode + ( + int mode_value, + unsigned int color_mode, + std::vector colors, + unsigned int speed, + unsigned int direction + ); + void SwitchMode(bool software); + + enum + { + CORSAIR_MODE_DIRECT_VALUE = 0xFFFF, + CORSAIR_HW_MODE_STATIC_VALUE = 0x207E, + CORSAIR_HW_MODE_COLOR_PULSE_VALUE = 0xAD4F, + CORSAIR_HW_MODE_COLOR_SHIFT_VALUE = 0xA5FA, + CORSAIR_HW_MODE_COLOR_WAVE_VALUE = 0x7BFF, + CORSAIR_HW_MODE_RAINBOW_WAVE_VALUE = 0xB94C, + CORSAIR_HW_MODE_RAIN_VALUE = 0xA07E, + CORSAIR_HW_MODE_SPIRAL_VALUE = 0xAB87, + CORSAIR_HW_MODE_WATER_COLOR_VALUE = 0x0022, + CORSAIR_HW_MODE_TYPE_KEY_VALUE = 0xB1F9, + CORSAIR_HW_MODE_TYPE_RIPPLE_VALUE = 0x09A2, + CORSAIR_HW_MODE_VISOR_VALUE = 0x90c0 + }; + + enum + { + CORSAIR_HW_MODE_COLOR_NONE = 0x00, + CORSAIR_HW_MODE_COLOR_PREDEF = 0x01, + CORSAIR_HW_MODE_COLOR_RANDOM = 0x02, + CORSAIR_HW_MODE_COLOR_UNKNOWN = 0x03 + }; + + enum + { + CORSAIR_HW_MODE_SPEED_NONE = 0x00, + CORSAIR_HW_MODE_SPEED_MIN = 0x03, + CORSAIR_HW_MODE_SPEED_MED = 0x04, + CORSAIR_HW_MODE_SPEED_MAX = 0x05 + }; + + enum + { + CORSAIR_HW_MODE_DIR_NONE = 0x00, + CORSAIR_HW_MODE_DIR_DOWN = 0x01, + CORSAIR_HW_MODE_DIR_UP = 0x02, + CORSAIR_HW_MODE_DIR_RIGHT = 0x04, + CORSAIR_HW_MODE_DIR_LEFT = 0x05, + CORSAIR_HW_MODE_DIR_CLOCK_WISE = 0x06, + CORSAIE_HW_MODE_DIR_COUNTER_CLOCK_WISE = 0x07 + }; + +private: + hid_device* dev; + + std::string location; + std::string name; + + void LightingControl(); +}; diff --git a/Controllers/CorsairPeripheralController/CorsairK65MiniController.cpp b/Controllers/CorsairPeripheralController/CorsairK65MiniController.cpp new file mode 100644 index 0000000..22e2908 --- /dev/null +++ b/Controllers/CorsairPeripheralController/CorsairK65MiniController.cpp @@ -0,0 +1,111 @@ +/*---------------------------------------------------------*\ +| CorsairK65MiniController.cpp | +| | +| Driver for Corsair K65 Mini keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CorsairK65MiniController.h" +#include "LogManager.h" +#include "StringUtils.h" + +CorsairK65MiniController::CorsairK65MiniController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + LightingControl(); +} + +CorsairK65MiniController::~CorsairK65MiniController() +{ + hid_close(dev); +} + +std::string CorsairK65MiniController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string CorsairK65MiniController::GetName() +{ + return(name); +} + +std::string CorsairK65MiniController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void CorsairK65MiniController::LightingControl() +{ + /*-----------------------------------------*\ + | A few init packets have to be sent to | + | enabled software control | + \*-----------------------------------------*/ + unsigned char usb_buf[PACKET_LENGTH]; + memset(usb_buf, 0x00, PACKET_LENGTH); + + usb_buf[0x01] = K65_WRITE_COMMAND; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x03; + usb_buf[0x05] = 0x02; + + hid_write(dev, usb_buf, PACKET_LENGTH); + + memset(usb_buf, 0x00, PACKET_LENGTH); + + usb_buf[0x01] = K65_WRITE_COMMAND; + usb_buf[0x02] = 0x02; + usb_buf[0x03] = 0x6E; + + hid_write(dev, usb_buf, PACKET_LENGTH); + + memset(usb_buf, 0x00, PACKET_LENGTH); + + usb_buf[0x01] = K65_WRITE_COMMAND; + usb_buf[0x02] = 0x0D; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x22; + + hid_write(dev, usb_buf, PACKET_LENGTH); +} + +void CorsairK65MiniController::SetLEDs(std::vectorcolors, std::vector positions) +{ + unsigned char usb_buf[PACKET_LENGTH]; + memset(usb_buf, 0x00, PACKET_LENGTH); + + /*-----------------------------------------*\ + | Direct mode pattern | + | 08 06 01 73 01 00 00 12 00 RR GG BB ... | + \*-----------------------------------------*/ + usb_buf[0x01] = K65_WRITE_COMMAND; + usb_buf[0x02] = 0x06; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x73; + + usb_buf[0x05] = 0x01; + usb_buf[0x08] = 0x12; + + for(unsigned int i = 0 ; i < colors.size(); i++) + { + unsigned int position = positions[i]; + usb_buf[0x0A + position * 3] = RGBGetRValue(colors[i]); + usb_buf[0x0A + position * 3 + 1] = RGBGetGValue(colors[i]); + usb_buf[0x0A + position * 3 + 2] = RGBGetBValue(colors[i]); + } + + hid_write(dev, usb_buf, PACKET_LENGTH); +} diff --git a/Controllers/CorsairPeripheralController/CorsairK65MiniController.h b/Controllers/CorsairPeripheralController/CorsairK65MiniController.h new file mode 100644 index 0000000..64cfa8a --- /dev/null +++ b/Controllers/CorsairPeripheralController/CorsairK65MiniController.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| CorsairK65MiniController.cpp | +| | +| Driver for Corsair K65 Mini keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define PACKET_LENGTH 1025 +#define K65_WRITE_COMMAND 0x08 + +class CorsairK65MiniController +{ +public: + CorsairK65MiniController(hid_device* dev_handle, const char* path, std::string dev_name); + ~CorsairK65MiniController(); + + std::string GetDeviceLocation(); + std::string GetName(); + std::string GetSerialString(); + void SetLEDs(std::vector colors, std::vector positions); + +private: + hid_device* dev; + + std::string location; + std::string name; + + void LightingControl(); +}; diff --git a/Controllers/CorsairPeripheralController/CorsairPeripheralController.cpp b/Controllers/CorsairPeripheralController/CorsairPeripheralController.cpp new file mode 100644 index 0000000..88b741d --- /dev/null +++ b/Controllers/CorsairPeripheralController/CorsairPeripheralController.cpp @@ -0,0 +1,983 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralController.cpp | +| | +| Driver for Corsair peripherals | +| | +| Adam Honse (CalcProgrammer1) 09 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CorsairPeripheralController.h" +#include "LogManager.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +static unsigned int keys[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, + 0x14, 0x15, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x2A, 0x2B, 0x2C, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x42, 0x43, 0x44, 0x45, 0x48, 73, 74, 75, 76, 78, + 79, 80, 81, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 108, 109, 110, 111, 112, 113, 115, + 116, 117, 120, 121, 122, 123, 124, 126, 127, 128, 129, 132, 133, 134, 135, + 136, 137, 139, 140, 141, 0x10, 114}; + +static unsigned int keys_k70_mk2[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, + 0x14, 0x15, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x2A, 0x2B, 0x2C, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x42, 0x43, 0x44, 0x45, 0x48, 73, 74, 75, 76, 78, + 79, 80, 81, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 108, 109, 110, 111, 112, 113, 115, + 116, 117, 120, 121, 122, 123, 124, 126, 127, 128, 129, 132, 133, 134, 135, + 136, 137, 139, 140, 141, 16, 114, 47, 59, 125 }; + + +static unsigned int keys_k95_plat[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, + 0x14, 0x15, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x2A, 0x2B, 0x2C, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x42, 0x43, 0x44, 0x45, 0x48, 73, 74, 75, 76, 78, + 79, 80, 81, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 108, 109, 110, 111, 112, 113, 115, + 116, 117, 120, 121, 122, 123, 124, 126, 127, 128, 129, 132, 133, 134, 135, + 136, 137, 139, 140, 141, + 0x10, 114, 0x0a, 0x16, 0x22, 0x2e, 0x3a, 0x46, 125, + 144, 145, 146, 158, 160, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 159, 162, 161, 156, 157}; + +static unsigned int keys_k95[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, + 0x14, 0x15, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x2A, 0x2B, 0x2C, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x42, 0x43, 0x44, 0x45, 0x48, 73, 74, 75, 76, 78, + 79, 80, 81, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 108, 109, 110, 111, 112, 113, 115, + 116, 117, 120, 121, 122, 123, 124, 126, 127, 128, 129, 132, 133, 134, 135, + 136, 137, 139, 140, 141, + 0x10, 114, 0x0a, 0x16, 0x22, 0x2e, 0x3a, 0x46, 0x52, 0x5e, 0x6a, 0x76, 0x3b, 0x47, 0x53, + 0x5f, 0x6b, 0x77, 0x83, 0x8f, 0x0b, 0x17, 0x23, 0x2f}; + +static unsigned int st100[] = { 0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x07, 0x08, 0x04 }; + +static unsigned int key_mapping_k95_plat_ansi[] = { 0x31, 0x3f, 0x41, 0x42, 0x51, 0x53, 0x55, 0x6f, 0x7e, 0x7f, 0x80, 0x81 }; +static unsigned int key_mapping_k95_plat_iso[] = { 0x3f, 0x41, 0x42, 0x50, 0x53, 0x55, 0x6f, 0x78, 0x7e, 0x7f, 0x80, 0x81 }; +static unsigned int key_mapping_k70_mk2_plat_iso[] = { 0x3f, 0x41, 0x42, 0x50, 0x53, 0x55, 0x6f, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81 }; + +#define CORSAIR_PERIPHERAL_CONTROLLER_NAME "Corsair peripheral" + +CorsairPeripheralController::CorsairPeripheralController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + ReadFirmwareInfo(); + + /*-----------------------------------------------------*\ + | K55 and K95 Platinum require additional steps | + \*-----------------------------------------------------*/ + if (logical_layout == CORSAIR_TYPE_K55 || logical_layout == CORSAIR_TYPE_K95_PLAT || logical_layout == CORSAIR_TYPE_K70_MK2 || logical_layout == CORSAIR_TYPE_K68) + { + SpecialFunctionControl(); + } + + LightingControl(); + + if (logical_layout == CORSAIR_TYPE_K55 || logical_layout == CORSAIR_TYPE_K95_PLAT || logical_layout == CORSAIR_TYPE_K70_MK2 || logical_layout == CORSAIR_TYPE_K68) + { + SetupK55AndK95LightingControl(); + } +} + +CorsairPeripheralController::~CorsairPeripheralController() +{ + hid_close(dev); +} + +device_type CorsairPeripheralController::GetDeviceType() +{ + return type; +} + +std::string CorsairPeripheralController::GetDeviceLocation() +{ + return("HID: " + location); +} + +int CorsairPeripheralController::GetPhysicalLayout() +{ + return physical_layout; +} + +int CorsairPeripheralController::GetLogicalLayout() +{ + return logical_layout; +} + +std::string CorsairPeripheralController::GetFirmwareString() +{ + return firmware_version; +} + +std::string CorsairPeripheralController::GetName() +{ + return name; +} + +std::string CorsairPeripheralController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void CorsairPeripheralController::SetLEDs(std::vectorcolors) +{ + switch(type) + { + case DEVICE_TYPE_KEYBOARD: + if (logical_layout == CORSAIR_TYPE_K55) + { + SubmitKeyboardZonesColors(colors[0], colors[1], colors[2]); + } + else + { + SetLEDsKeyboardFull(colors); + } + break; + + case DEVICE_TYPE_MOUSE: + SetLEDsMouse(colors); + break; + + case DEVICE_TYPE_MOUSEMAT: + SetLEDsMousemat(colors); + break; + + case DEVICE_TYPE_HEADSET_STAND: + /*-----------------------------------------------------*\ + | The logo zone of the ST100 is in the middle of the | + | base LED strip, so remap the colors so that the logo | + | is the last LED in the sequence. | + \*-----------------------------------------------------*/ + std::vector remap_colors; + remap_colors.resize(colors.size()); + + for(int i = 0; i < 9; i++) + { + remap_colors[st100[i]] = colors[i]; + } + + /*-----------------------------------------------------*\ + | The ST100 uses the mousemat protocol | + \*-----------------------------------------------------*/ + SetLEDsMousemat(remap_colors); + break; + } +} + +void CorsairPeripheralController::SetLEDsKeyboardFull(std::vector colors) +{ + unsigned char red_val[168]; + unsigned char grn_val[168]; + unsigned char blu_val[168]; + unsigned char data_sz = 24; + + /*-----------------------------------------------------*\ + | Zero out buffers | + \*-----------------------------------------------------*/ + memset(red_val, 0x00, sizeof( red_val )); + memset(grn_val, 0x00, sizeof( grn_val )); + memset(blu_val, 0x00, sizeof( blu_val )); + + /*-----------------------------------------------------*\ + | Copy red, green, and blue components into buffers | + \*-----------------------------------------------------*/ + for(std::size_t color_idx = 0; color_idx < colors.size(); color_idx++) + { + RGBColor color = colors[color_idx]; + if (logical_layout == CORSAIR_TYPE_K95_PLAT) + { + red_val[keys_k95_plat[color_idx]] = RGBGetRValue(color); + grn_val[keys_k95_plat[color_idx]] = RGBGetGValue(color); + blu_val[keys_k95_plat[color_idx]] = RGBGetBValue(color); + data_sz = 48; + } + else if (logical_layout == CORSAIR_TYPE_K95) + { + red_val[keys_k95[color_idx]] = RGBGetRValue(color); + grn_val[keys_k95[color_idx]] = RGBGetGValue(color); + blu_val[keys_k95[color_idx]] = RGBGetBValue(color); + data_sz = 48; //untested + } + else if (logical_layout == CORSAIR_TYPE_K70_MK2) + { + red_val[keys_k70_mk2[color_idx]] = RGBGetRValue(color); + grn_val[keys_k70_mk2[color_idx]] = RGBGetGValue(color); + blu_val[keys_k70_mk2[color_idx]] = RGBGetBValue(color); + } + else + { + red_val[keys[color_idx]] = RGBGetRValue(color); + grn_val[keys[color_idx]] = RGBGetGValue(color); + blu_val[keys[color_idx]] = RGBGetBValue(color); + } + } + + /*-----------------------------------------------------*\ + | Send red bytes | + \*-----------------------------------------------------*/ + StreamPacket(1, 60, &red_val[0]); + StreamPacket(2, 60, &red_val[60]); + StreamPacket(3, data_sz, &red_val[120]); + SubmitKeyboardFullColors(1, 3, 1); + + /*-----------------------------------------------------*\ + | Send green bytes | + \*-----------------------------------------------------*/ + StreamPacket(1, 60, &grn_val[0]); + StreamPacket(2, 60, &grn_val[60]); + StreamPacket(3, data_sz, &grn_val[120]); + SubmitKeyboardFullColors(2, 3, 1); + + /*-----------------------------------------------------*\ + | Send blue bytes | + \*-----------------------------------------------------*/ + StreamPacket(1, 60, &blu_val[0]); + StreamPacket(2, 60, &blu_val[60]); + StreamPacket(3, data_sz, &blu_val[120]); + SubmitKeyboardFullColors(3, 3, 2); +} + +void CorsairPeripheralController::SetLEDsMouse(std::vector colors) +{ + SubmitMouseColors((unsigned char)colors.size(), &colors[0]); +} + +void CorsairPeripheralController::SetLEDsMousemat(std::vector colors) +{ + SubmitMousematColors((unsigned char)colors.size(), &colors[0]); +} + +void CorsairPeripheralController::SetLEDsKeyboardLimited(std::vector colors) +{ + unsigned char data_pkt[216]; + unsigned char red_val[144]; + unsigned char grn_val[144]; + unsigned char blu_val[144]; + + /*-----------------------------------------------------*\ + | Zero out buffers | + \*-----------------------------------------------------*/ + memset(data_pkt, 0x00, sizeof( data_pkt )); + memset(red_val, 0x00, sizeof( red_val )); + memset(grn_val, 0x00, sizeof( grn_val )); + memset(blu_val, 0x00, sizeof( blu_val )); + + /*-----------------------------------------------------*\ + | Scale color values to 9-bit | + \*-----------------------------------------------------*/ + for(std::size_t color_idx = 0; color_idx < colors.size(); color_idx++) + { + RGBColor color = colors[color_idx]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + if( red > 7 ) red = 7; + if( grn > 7 ) grn = 7; + if( blu > 7 ) blu = 7; + + red = 7 - red; + grn = 7 - grn; + blu = 7 - blu; + + red_val[keys[color_idx]] = red; + grn_val[keys[color_idx]] = grn; + blu_val[keys[color_idx]] = blu; + } + + /*-----------------------------------------------------*\ + | Pack the color values, 2 values per byte | + \*-----------------------------------------------------*/ + for(int red_idx = 0; red_idx < 72; red_idx++) + { + data_pkt[red_idx] = red_val[(red_idx * 2) + 1] << 4 | red_val[red_idx * 2]; + } + + for(int grn_idx = 0; grn_idx < 72; grn_idx++) + { + data_pkt[grn_idx + 72] = grn_val[(grn_idx * 2) + 1] << 4 | grn_val[grn_idx * 2]; + } + + for(int blu_idx = 0; blu_idx < 72; blu_idx++) + { + data_pkt[blu_idx + 144] = blu_val[(blu_idx * 2) + 1] << 4 | blu_val[blu_idx * 2]; + } + + /*-----------------------------------------------------*\ + | Send the packets | + \*-----------------------------------------------------*/ + StreamPacket(1, 60, &data_pkt[0]); + StreamPacket(2, 60, &data_pkt[60]); + StreamPacket(3, 60, &data_pkt[120]); + StreamPacket(4, 36, &data_pkt[180]); + + SubmitKeyboardLimitedColors(216); +} + +void CorsairPeripheralController::SwitchMode(bool software) +{ + if(software) + { + if (logical_layout == CORSAIR_TYPE_K55 || logical_layout == CORSAIR_TYPE_K95_PLAT || logical_layout == CORSAIR_TYPE_K70_MK2 || logical_layout == CORSAIR_TYPE_K68) + { + SpecialFunctionControl(); + } + + LightingControl(); + + if (logical_layout == CORSAIR_TYPE_K55 || logical_layout == CORSAIR_TYPE_K95_PLAT || logical_layout == CORSAIR_TYPE_K70_MK2 || logical_layout == CORSAIR_TYPE_K68) + { + SetupK55AndK95LightingControl(); + } + } + else + { + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + usb_buf[1] = CORSAIR_COMMAND_WRITE; + usb_buf[2] = CORSAIR_PROPERTY_SPECIAL_FUNCTION; + usb_buf[3] = CORSAIR_LIGHTING_CONTROL_HARDWARE; + + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); + } +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void CorsairPeripheralController::LightingControl() +{ + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_WRITE; + usb_buf[0x02] = CORSAIR_PROPERTY_LIGHTING_CONTROL; + usb_buf[0x03] = CORSAIR_LIGHTING_CONTROL_SOFTWARE; + + /*-----------------------------------------------------*\ + | Lighting control byte needs to be 3 for keyboards and | + | headset stand, 1 for mice and mousepads | + \*-----------------------------------------------------*/ + switch(type) + { + default: + case DEVICE_TYPE_KEYBOARD: + usb_buf[0x05] = 0x03; // On K95 Platinum, this controls keyboard brightness + break; + + case DEVICE_TYPE_MOUSE: + usb_buf[0x05] = 0x01; + break; + + case DEVICE_TYPE_MOUSEMAT: + usb_buf[0x05] = 0x04; + break; + + case DEVICE_TYPE_HEADSET_STAND: + usb_buf[0x05] = 0x03; + break; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); +} + +/*-----------------------------------------------------*\ +| Probably a key mapping packet? | +\*-----------------------------------------------------*/ + +void CorsairPeripheralController::SetupK55AndK95LightingControl() +{ + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Set up a packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_WRITE; + usb_buf[0x02] = CORSAIR_PROPERTY_LIGHTING_CONTROL; + usb_buf[0x03] = 0x08; + + usb_buf[0x05] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + unsigned int* skipped_identifiers = key_mapping_k95_plat_ansi; + int skipped_identifiers_count = sizeof(key_mapping_k95_plat_ansi) / sizeof(key_mapping_k95_plat_ansi[0]); + + if (physical_layout == CORSAIR_LAYOUT_ISO) + { + if(logical_layout == CORSAIR_TYPE_K70_MK2) + { + skipped_identifiers = key_mapping_k70_mk2_plat_iso; + skipped_identifiers_count = sizeof(key_mapping_k70_mk2_plat_iso) / sizeof(key_mapping_k70_mk2_plat_iso[0]); + } + else + { + skipped_identifiers = key_mapping_k95_plat_iso; + skipped_identifiers_count = sizeof(key_mapping_k95_plat_iso) / sizeof(key_mapping_k95_plat_iso[0]); + } + } + + unsigned int identifier = 0; + for (int i = 0; i < 4; i++) + { + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Set up a packet - a sequence of 120 ids | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_WRITE; + usb_buf[0x02] = 0x40; + usb_buf[0x03] = 0x1E; + + for (int j = 0; j < 30; j++) + { + for (int j = 0; j < skipped_identifiers_count; j++) + { + if (identifier == skipped_identifiers[j]) + { + identifier++; + } + } + + usb_buf[5 + 2 * j] = identifier++; + usb_buf[5 + 2 * j + 1] = 0xC0; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); + } +} + +void CorsairPeripheralController::SpecialFunctionControl() +{ + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_WRITE; + usb_buf[0x02] = CORSAIR_PROPERTY_SPECIAL_FUNCTION; + usb_buf[0x03] = CORSAIR_LIGHTING_CONTROL_SOFTWARE; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); +} + +void CorsairPeripheralController::ReadFirmwareInfo() +{ + int actual; + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + char offset = 0; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Set up Read Firmware Info packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_READ; + usb_buf[0x02] = CORSAIR_PROPERTY_FIRMWARE_INFO; + + /*-----------------------------------------------------*\ + | Send packet and try reading it using an HID read | + | If that fails, repeat the send and read the reply as | + | a feature report. | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); + actual = hid_read_timeout(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH, 1000); + + if(actual == 0) + { + /*-------------------------------------------------*\ + | Zero out buffer | + \*-------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-------------------------------------------------*\ + | Set up Read Firmware Info packet | + \*-------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_READ; + usb_buf[0x02] = CORSAIR_PROPERTY_FIRMWARE_INFO; + + hid_send_feature_report(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); + actual = hid_get_feature_report(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); + offset = 1; + } + + /*-----------------------------------------------------*\ + | Get device type | + | 0xC0 Device is a keyboard | + | 0xC1 Device is a mouse | + | 0xC2 Device is a mousepad or headset stand | + \*-----------------------------------------------------*/ + LOG_DEBUG("[%s] Device type %02X", CORSAIR_PERIPHERAL_CONTROLLER_NAME, usb_buf[0x14 + offset]); + + switch(usb_buf[0x14 + offset]) + { + case 0xC0: + { + unsigned short pid = (unsigned short)(usb_buf[0x0E] << 8) + (unsigned short)(usb_buf[0x0F]); + + /*-----------------------------------------------------*\ + | Get the correct Keyboard Type | + \*-----------------------------------------------------*/ + switch(pid) + { + case 0x1B2D: + logical_layout = CORSAIR_TYPE_K95_PLAT; + break; + + case 0x1B11: + logical_layout = CORSAIR_TYPE_K95; + break; + + case 0x1B3D: + logical_layout = CORSAIR_TYPE_K55; + break; + + case 0x1B38: + case 0x1B49: + case 0x1B6B: + case 0x1B55: + logical_layout = CORSAIR_TYPE_K70_MK2; + break; + + case 0x1B4F: + logical_layout = CORSAIR_TYPE_K68; + break; + + default: + logical_layout = CORSAIR_TYPE_NORMAL; + } + + /*-----------------------------------------------------*\ + | Get the correct Keyboard Layout. | + | Currently unused but can be implemented in the future.| + \*-----------------------------------------------------*/ + switch(usb_buf[0x17 + offset]) + { + case CORSAIR_LAYOUT_ANSI: + physical_layout = CORSAIR_LAYOUT_ANSI; + break; + case CORSAIR_LAYOUT_ISO: + physical_layout = CORSAIR_LAYOUT_ISO; + break; + case CORSAIR_LAYOUT_ABNT: + physical_layout = CORSAIR_LAYOUT_ABNT; + break; + case CORSAIR_LAYOUT_JIS: + physical_layout = CORSAIR_LAYOUT_JIS; + break; + case CORSAIR_LAYOUT_DUBEOLSIK: + physical_layout = CORSAIR_LAYOUT_DUBEOLSIK; + break; + default: + physical_layout = CORSAIR_LAYOUT_ANSI; + } + + } + type = DEVICE_TYPE_KEYBOARD; + break; + + case 0xC1: + type = DEVICE_TYPE_MOUSE; + SpecialFunctionControl(); + break; + + case 0xC2: + { + unsigned short pid = (unsigned short)(usb_buf[0x0F] << 8) + (unsigned short)(usb_buf[0x0E]); + + switch(pid) + { + case 0x0A34: + type = DEVICE_TYPE_HEADSET_STAND; + SpecialFunctionControl(); + break; + + default: + type = DEVICE_TYPE_MOUSEMAT; + SpecialFunctionControl(); + break; + } + } + break; + + default: + type = DEVICE_TYPE_UNKNOWN; + break; + } + + /*-----------------------------------------------------*\ + | Format firmware version string if device type is valid| + \*-----------------------------------------------------*/ + if(type != DEVICE_TYPE_UNKNOWN) + { + firmware_version = std::to_string(usb_buf[0x09 + offset]) + "." + std::to_string(usb_buf[0x08 + offset]); + } +} + +void CorsairPeripheralController::StreamPacket + ( + unsigned char packet_id, + unsigned char data_sz, + unsigned char* data_ptr + ) +{ + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Set up Stream packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_STREAM; + usb_buf[0x02] = packet_id; + usb_buf[0x03] = data_sz; + + /*-----------------------------------------------------*\ + | Copy in data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x05], data_ptr, data_sz); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); +} + +void CorsairPeripheralController::SetHardwareMode + ( + int mode_value, + unsigned int color_mode, + std::vector colors, + unsigned int speed, + unsigned int direction, + unsigned char brightness + ) +{ + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Set the brightness | + \*-----------------------------------------------------*/ + usb_buf[1] = CORSAIR_COMMAND_WRITE; + usb_buf[2] = 0x05; + usb_buf[3] = 0x02; + usb_buf[5] = brightness; + + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Send "lght_00.d" | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + usb_buf[1] = CORSAIR_COMMAND_WRITE; + usb_buf[2] = 0x17; + usb_buf[3] = 0x05; + usb_buf[5] = 0x6c; + usb_buf[6] = 0x67; + usb_buf[7] = 0x68; + usb_buf[8] = 0x74; + usb_buf[9] = 0x5F; + usb_buf[10] = 0x30; + usb_buf[11] = 0x30; + usb_buf[12] = 0x2E; + usb_buf[13] = 0x64; + + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Stream the mode data | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + usb_buf[1] = CORSAIR_COMMAND_STREAM; + usb_buf[2] = 0x01; + usb_buf[3] = 0x0D; + usb_buf[5] = mode_value; + usb_buf[8] = direction; + + if(mode_value == CORSAIR_HW_MODE_TYPE_KEY_VALUE) + { + usb_buf[9] = speed; + } + else + { + usb_buf[6] = speed; + } + + if(color_mode == MODE_COLORS_RANDOM) + { + usb_buf[7] = 0x01; + } + else if (color_mode == MODE_COLORS_MODE_SPECIFIC) + { + usb_buf[7] = 0x03; + + usb_buf[10] = RGBGetRValue(colors[0]); + usb_buf[11] = RGBGetGValue(colors[0]); + usb_buf[12] = RGBGetBValue(colors[0]); + usb_buf[13] = 0xFF; + + usb_buf[14] = RGBGetRValue(colors[1]); + usb_buf[15] = RGBGetGValue(colors[1]); + usb_buf[16] = RGBGetBValue(colors[1]); + usb_buf[17] = 0xFF; + } + + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Stop stream and commit | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + usb_buf[1] = CORSAIR_COMMAND_WRITE; + usb_buf[2] = 0x17; + usb_buf[3] = 0x09; + + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + usb_buf[3] = 0x08; + + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); +} + +void CorsairPeripheralController::SubmitKeyboardFullColors + ( + unsigned char color_channel, + unsigned char packet_count, + unsigned char finish_val + ) +{ + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Set up Submit Keyboard 24-Bit Colors packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_WRITE; + usb_buf[0x02] = CORSAIR_PROPERTY_SUBMIT_KEYBOARD_COLOR_24; + usb_buf[0x03] = color_channel; + usb_buf[0x04] = packet_count; + usb_buf[0x05] = finish_val; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); +} + +void CorsairPeripheralController::SubmitKeyboardZonesColors + ( + RGBColor left, + RGBColor mid, + RGBColor right + ) +{ + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Submit Keyboard 24-Bit Colors packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_WRITE; + usb_buf[0x02] = CORSAIR_PROPERTY_SUBMIT_KBZONES_COLOR_24; + usb_buf[0x03] = 0x00; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = RGBGetRValue(left); + usb_buf[0x06] = RGBGetGValue(left); + usb_buf[0x07] = RGBGetBValue(left); + usb_buf[0x08] = RGBGetRValue(mid); + usb_buf[0x09] = RGBGetGValue(mid); + usb_buf[0x0A] = RGBGetBValue(mid); + usb_buf[0x0B] = RGBGetRValue(right); + usb_buf[0x0C] = RGBGetGValue(right); + usb_buf[0x0D] = RGBGetBValue(right); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); +} + +void CorsairPeripheralController::SubmitKeyboardLimitedColors + ( + unsigned char byte_count + ) +{ + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Submit Keyboard 9-Bit Colors packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_WRITE; + usb_buf[0x02] = CORSAIR_PROPERTY_SUBMIT_KEYBOARD_COLOR_9; + usb_buf[0x05] = byte_count; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); +} + +void CorsairPeripheralController::SubmitMouseColors + ( + unsigned char num_zones, + RGBColor * color_data + ) +{ + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Set up Submit Mouse Colors packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_WRITE; + usb_buf[0x02] = CORSAIR_PROPERTY_SUBMIT_MOUSE_COLOR; + usb_buf[0x03] = num_zones; + usb_buf[0x04] = 0x00; + + /*-----------------------------------------------------*\ + | Copy in colors in order | + \*-----------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < num_zones; zone_idx++) + { + usb_buf[(zone_idx * 4) + 5] = zone_idx; + usb_buf[(zone_idx * 4) + 6] = RGBGetRValue(color_data[zone_idx]); + usb_buf[(zone_idx * 4) + 7] = RGBGetGValue(color_data[zone_idx]); + usb_buf[(zone_idx * 4) + 8] = RGBGetBValue(color_data[zone_idx]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); +} + +void CorsairPeripheralController::SubmitMousematColors + ( + unsigned char num_zones, + RGBColor * color_data + ) +{ + unsigned char usb_buf[CORSAIR_PERIPHERAL_PACKET_LENGTH]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, CORSAIR_PERIPHERAL_PACKET_LENGTH); + + /*-----------------------------------------------------*\ + | Set up Submit Mouse Colors packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = CORSAIR_COMMAND_WRITE; + usb_buf[0x02] = CORSAIR_PROPERTY_SUBMIT_MOUSE_COLOR; + usb_buf[0x03] = num_zones; + usb_buf[0x04] = 0x00; + + /*-----------------------------------------------------*\ + | Copy in colors in order | + \*-----------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < num_zones; zone_idx++) + { + usb_buf[(zone_idx * 3) + 5] = RGBGetRValue(color_data[zone_idx]); + usb_buf[(zone_idx * 3) + 6] = RGBGetGValue(color_data[zone_idx]); + usb_buf[(zone_idx * 3) + 7] = RGBGetBValue(color_data[zone_idx]); + } + + /*-----------------------------------------------------*\ + | Send packet using feature reports, as headset stand | + | seems to not update completely using HID writes | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, CORSAIR_PERIPHERAL_PACKET_LENGTH); +} diff --git a/Controllers/CorsairPeripheralController/CorsairPeripheralController.h b/Controllers/CorsairPeripheralController/CorsairPeripheralController.h new file mode 100644 index 0000000..9dbee08 --- /dev/null +++ b/Controllers/CorsairPeripheralController/CorsairPeripheralController.h @@ -0,0 +1,180 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralController.h | +| | +| Driver for Corsair peripherals | +| | +| Adam Honse (CalcProgrammer1) 09 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define CORSAIR_PERIPHERAL_PACKET_LENGTH 65 + +enum +{ + CORSAIR_COMMAND_WRITE = 0x07, + CORSAIR_COMMAND_READ = 0x0E, + CORSAIR_COMMAND_STREAM = 0x7F +}; + +enum +{ + CORSAIR_PROPERTY_FIRMWARE_INFO = 0x01, + CORSAIR_PROPERTY_RESET = 0x02, + CORSAIR_PROPERTY_SPECIAL_FUNCTION = 0x04, + CORSAIR_PROPERTY_LIGHTING_CONTROL = 0x05, + CORSAIR_PROPERTY_HARDWARE_PROFILE = 0x13, + CORSAIR_PROPERTY_SUBMIT_MOUSE_COLOR = 0x22, + CORSAIR_PROPERTY_SUBMIT_KBZONES_COLOR_24 = 0x25, + CORSAIR_PROPERTY_SUBMIT_KEYBOARD_COLOR_9 = 0x27, + CORSAIR_PROPERTY_SUBMIT_KEYBOARD_COLOR_24 = 0x28, +}; + +enum +{ + CORSAIR_LIGHTING_CONTROL_HARDWARE = 0x01, + CORSAIR_LIGHTING_CONTROL_SOFTWARE = 0x02 +}; + +enum +{ + CORSAIR_COLOR_CHANNEL_RED = 0x01, + CORSAIR_COLOR_CHANNEL_GREEN = 0x02, + CORSAIR_COLOR_CHANNEL_BLUE = 0x03 +}; + +enum +{ + CORSAIR_LAYOUT_ANSI = 0x00, + CORSAIR_LAYOUT_ISO = 0x01, + CORSAIR_LAYOUT_ABNT = 0x02, + CORSAIR_LAYOUT_JIS = 0x03, + CORSAIR_LAYOUT_DUBEOLSIK = 0x04 +}; + +enum +{ + CORSAIR_TYPE_NORMAL = 0, + CORSAIR_TYPE_K95_PLAT = 1, + CORSAIR_TYPE_K95 = 2, + CORSAIR_TYPE_K55 = 3, + CORSAIR_TYPE_K70_MK2 = 4, + CORSAIR_TYPE_K68 = 5 +}; + +enum +{ + CORSAIR_MODE_DIRECT_VALUE = 0xFF, + CORSAIR_HW_MODE_COLOR_PULSE_VALUE = 0x01, + CORSAIR_HW_MODE_COLOR_SHIFT_VALUE = 0x00, + CORSAIR_HW_MODE_COLOR_WAVE_VALUE = 0x04, + CORSAIR_HW_MODE_RAINBOW_WAVE_VALUE = 0x03, + CORSAIR_HW_MODE_RAIN_VALUE = 0x06, + CORSAIR_HW_MODE_SPIRAL_VALUE = 0x02, + CORSAIR_HW_MODE_TYPE_KEY_VALUE = 0x08, + CORSAIR_HW_MODE_TYPE_RIPPLE_VALUE = 0x09, + CORSAIR_HW_MODE_VISOR_VALUE = 0x05 +}; + +enum +{ + CORSAIR_HW_MODE_SPEED_MIN = 0x01, + CORSAIR_HW_MODE_SPEED_MAX = 0x03, + CORSAIR_HW_MODE_BRIGHTNESS_MIN = 0x00, + CORSAIR_HW_MODE_BRIGHTNESS_MAX = 0x03 +}; + +class CorsairPeripheralController +{ +public: + CorsairPeripheralController(hid_device* dev_handle, const char* path, std::string dev_name); + ~CorsairPeripheralController(); + + int GetLogicalLayout(); + int GetPhysicalLayout(); + device_type GetDeviceType(); + std::string GetDeviceLocation(); + std::string GetFirmwareString(); + std::string GetName(); + std::string GetSerialString(); + + void SetLEDs(std::vector colors); + void SetLEDsKeyboardFull(std::vector colors); + void SetLEDsKeyboardLimited(std::vector colors); + void SetLEDsMouse(std::vector colors); + void SetLEDsMousemat(std::vector colors); + void SetHardwareMode + ( + int mode_value, + unsigned int color_mode, + std::vector colors, + unsigned int speed, + unsigned int direction, + unsigned char brightness + ); + + + void SwitchMode(bool software); + +private: + hid_device* dev; + + std::string firmware_version; + std::string location; + std::string name; + device_type type; + int physical_layout; //ANSI, ISO, etc. + int logical_layout; //Normal, K95 or K95 Platinum + + void LightingControl(); + void SetupK55AndK95LightingControl(); + void SpecialFunctionControl(); + + void ReadFirmwareInfo(); + + void StreamPacket + ( + unsigned char packet_id, + unsigned char data_sz, + unsigned char* data_ptr + ); + + void SubmitKeyboardFullColors + ( + unsigned char color_channel, + unsigned char packet_count, + unsigned char finish_val + ); + + void SubmitKeyboardZonesColors + ( + RGBColor left, + RGBColor mid, + RGBColor right + ); + + + void SubmitKeyboardLimitedColors + ( + unsigned char byte_count + ); + + void SubmitMouseColors + ( + unsigned char num_zones, + RGBColor * color_data + ); + + void SubmitMousematColors + ( + unsigned char num_zones, + RGBColor * color_data + ); +}; diff --git a/Controllers/CorsairPeripheralController/CorsairPeripheralControllerDetect.cpp b/Controllers/CorsairPeripheralController/CorsairPeripheralControllerDetect.cpp new file mode 100644 index 0000000..e5b0296 --- /dev/null +++ b/Controllers/CorsairPeripheralController/CorsairPeripheralControllerDetect.cpp @@ -0,0 +1,234 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralControllerDetect.cpp | +| | +| Driver for Corsair peripherals | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +/*-----------------------------------------------------*\ +| OpenRGB includes | +\*-----------------------------------------------------*/ +#include +#include "Detector.h" +#include "LogManager.h" +#include "RGBController.h" + +/*-----------------------------------------------------*\ +| Corsair Peripheral specific includes | +\*-----------------------------------------------------*/ +#include "RGBController_CorsairPeripheral.h" +#include "RGBController_CorsairK55RGBPROXT.h" +#include "RGBController_CorsairK65Mini.h" + +#define CORSAIR_PERIPHERAL_CONTROLLER_NAME "Corsair peripheral" + +/*-----------------------------------------------------*\ +| Corsair vendor ID | +\*-----------------------------------------------------*/ +#define CORSAIR_VID 0x1B1C + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +| List taken from ckb-next | +| Non-RGB keyboards were omitted from this list | +\*-----------------------------------------------------*/ +#define CORSAIR_K55_RGB_PID 0x1B3D + +#define CORSAIR_K65_RGB_PID 0x1B17 +#define CORSAIR_K65_LUX_RGB_PID 0x1B37 +#define CORSAIR_K65_RGB_RAPIDFIRE_PID 0x1B39 + +#define CORSAIR_K68_RGB_PID 0x1B4F + +#define CORSAIR_K70_RGB_PID 0x1B13 +#define CORSAIR_K70_LUX_RGB_PID 0x1B33 +#define CORSAIR_K70_RGB_RAPIDFIRE_PID 0x1B38 +#define CORSAIR_K70_RGB_MK2_PID 0x1B49 +#define CORSAIR_K70_RGB_MK2_SE_PID 0x1B6B +#define CORSAIR_K70_RGB_MK2_LP_PID 0x1B55 + +#define CORSAIR_K95_RGB_PID 0x1B11 +#define CORSAIR_K95_PLATINUM_PID 0x1B2D +#define CORSAIR_K95_PLATINUM_SE_PID 0x1B82 + +#define CORSAIR_STRAFE_PID 0x1B20 +#define CORSAIR_STRAFE_RED_PID 0x1B44 +#define CORSAIR_STRAFE_MK2_PID 0x1B48 + +/*-----------------------------------------------------*\ +| Non-RGB Keyboard product IDs | +\*-----------------------------------------------------*/ +#define CORSAIR_K70_LUX_PID 0x1B36 +#define CORSAIR_K68_RED_PID 0x1B3F +#define CORSAIR_K68_RED_SHADOW_PID 0x1BA5 + +/*-----------------------------------------------------*\ +| Mouse product IDs | +| List taken from ckb-next | +\*-----------------------------------------------------*/ +#define CORSAIR_GLAIVE_RGB_PID 0x1B34 +#define CORSAIR_GLAIVE_RGB_PRO_PID 0x1B74 +#define CORSAIR_HARPOON_RGB_PID 0x1B3C +#define CORSAIR_HARPOON_RGB_PRO_PID 0x1B75 +#define CORSAIR_IRONCLAW_RGB_PID 0x1B5D +#define CORSAIR_M65_PID 0x1B12 +#define CORSAIR_M65_PRO_PID 0x1B2E +#define CORSAIR_M65_RGB_ELITE_PID 0x1B5A +#define CORSAIR_NIGHTSWORD_PID 0x1B5C +#define CORSAIR_SCIMITAR_RGB_PID 0x1B1E +#define CORSAIR_SCIMITAR_PRO_RGB_PID 0x1B3E +#define CORSAIR_SCIMITAR_ELITE_RGB_PID 0x1B8B +#define CORSAIR_SABRE_RGB_PID 0x1B2F + +/*-----------------------------------------------------*\ +| Mousepad product IDs | +| List taken from ckb-next | +\*-----------------------------------------------------*/ +#define CORSAIR_MM800_RGB_POLARIS_PID 0x1B3B + +/*-----------------------------------------------------*\ +| Headset Stand product IDs | +| List taken from ckb-next | +\*-----------------------------------------------------*/ +#define CORSAIR_ST100_PID 0x0A34 + +/*-----------------------------------------------------*\ +| Corsair K55 RGB PRO XT Keyboard product ID | +| This keyboard uses a separate driver | +\*-----------------------------------------------------*/ +#define CORSAIR_K55_RGB_PRO_XT_PID 0x1BA1 + +/*-----------------------------------------------------*\ +| Corsair K65 Mini Keyboard product ID | +| This keyboard uses a separate driver | +\*-----------------------------------------------------*/ +#define CORSAIR_K65_MINI_PID 0x1BAF + +void DetectCorsairK55RGBPROXTControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CorsairK55RGBPROXTController* controller = new CorsairK55RGBPROXTController(dev, info->path, name); + RGBController_CorsairK55RGBPROXT* rgb_controller = new RGBController_CorsairK55RGBPROXT(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectCorsairK55RGBPROXTControllers() */ + +void DetectCorsairK65MiniControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CorsairK65MiniController* controller = new CorsairK65MiniController(dev, info->path, name); + RGBController_CorsairK65Mini* rgb_controller = new RGBController_CorsairK65Mini(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectCorsairK65MiniControllers() */ + +/******************************************************************************************\ +* * +* DetectCorsairPeripheralControllers * +* * +* Tests the USB address to see if a Corsair RGB Keyboard controller exists there. * +* * +\******************************************************************************************/ +void DetectCorsairPeripheralControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LOG_DEBUG("[%s] Device opened. VID/PID %02X:%02X", CORSAIR_PERIPHERAL_CONTROLLER_NAME, info->vendor_id , info->product_id); + + CorsairPeripheralController* controller = new CorsairPeripheralController(dev, info->path, name); + + if(controller->GetDeviceType() != DEVICE_TYPE_UNKNOWN) + { + bool supports_hardware_modes = + (info->product_id == CORSAIR_K70_RGB_MK2_PID) || + (info->product_id == CORSAIR_K70_RGB_MK2_LP_PID); + + RGBController_CorsairPeripheral* rgb_controller = new RGBController_CorsairPeripheral(controller, supports_hardware_modes); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + LOG_DEBUG("[%s] Device type is unknown", CORSAIR_PERIPHERAL_CONTROLLER_NAME); + delete controller; + } + } +} /* DetectCorsairPeripheralControllers() */ + +/*-----------------------------------------------------------------------------------------------------*\ +| Keyboards | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("Corsair K55 RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K55_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K65 RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K65_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K65 LUX RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K65_LUX_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K65 RGB RAPIDFIRE", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K65_RGB_RAPIDFIRE_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K68 RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K68_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K68 RED", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K68_RED_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K68 RED SHADOW", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K68_RED_SHADOW_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K70 RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K70 LUX", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_LUX_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K70 LUX RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_LUX_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K70 RGB RAPIDFIRE", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_RGB_RAPIDFIRE_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K70 RGB MK.2", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_RGB_MK2_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K70 RGB MK.2 SE", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_RGB_MK2_SE_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K70 RGB MK.2 Low Profile",DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K70_RGB_MK2_LP_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K95 RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K95_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K95 RGB PLATINUM", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K95_PLATINUM_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair K95 RGB PLATINUM SE", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_K95_PLATINUM_SE_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Strafe", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_STRAFE_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Strafe Red", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_STRAFE_RED_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Strafe MK.2", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_STRAFE_MK2_PID, 1, 0xFFC2); +/*-----------------------------------------------------------------------------------------------------*\ +| Mice | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("Corsair Glaive RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_GLAIVE_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Glaive RGB PRO", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_GLAIVE_RGB_PRO_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Harpoon RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_HARPOON_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Harpoon RGB PRO", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_HARPOON_RGB_PRO_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Ironclaw RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_IRONCLAW_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair M65", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_M65_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair M65 PRO", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_M65_PRO_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair M65 RGB Elite", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_M65_RGB_ELITE_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Nightsword", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_NIGHTSWORD_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Scimitar RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_SCIMITAR_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Scimitar PRO RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_SCIMITAR_PRO_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Scimitar Elite RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_SCIMITAR_ELITE_RGB_PID, 1, 0xFFC2); +REGISTER_HID_DETECTOR_IP("Corsair Sabre RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_SABRE_RGB_PID, 1, 0xFFC2); + +/*-----------------------------------------------------------------------------------------------------*\ +| Mousemats | +\*-----------------------------------------------------------------------------------------------------*/ +#ifdef USE_HID_USAGE +REGISTER_HID_DETECTOR_P("Corsair MM800 RGB Polaris", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_MM800_RGB_POLARIS_PID, 0xFFC2); +#else +REGISTER_HID_DETECTOR_I("Corsair MM800 RGB Polaris", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_MM800_RGB_POLARIS_PID, 0); +#endif +/*-----------------------------------------------------------------------------------------------------*\ +| Headset Stands | +\*-----------------------------------------------------------------------------------------------------*/ +#ifdef USE_HID_USAGE +REGISTER_HID_DETECTOR_P("Corsair ST100 RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_ST100_PID, 0xFFC2); +#else +REGISTER_HID_DETECTOR_I("Corsair ST100 RGB", DetectCorsairPeripheralControllers, CORSAIR_VID, CORSAIR_ST100_PID, 0); +#endif + +/*-----------------------------------------------------------------------------------------------------*\ +| Corsair K65 Mini Keyboard | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_I("Corsair K65 Mini", DetectCorsairK65MiniControllers, CORSAIR_VID, CORSAIR_K65_MINI_PID, 1); + +/*-----------------------------------------------------------------------------------------------------*\ +| Corsair K55 RGB PRO XT Keyboard | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("Corsair K55 RGB PRO XT", DetectCorsairK55RGBPROXTControllers, CORSAIR_VID, CORSAIR_K55_RGB_PRO_XT_PID, 1, 0xFF42); diff --git a/Controllers/CorsairPeripheralController/RGBController_CorsairK55RGBPROXT.cpp b/Controllers/CorsairPeripheralController/RGBController_CorsairK55RGBPROXT.cpp new file mode 100644 index 0000000..6ae7e1d --- /dev/null +++ b/Controllers/CorsairPeripheralController/RGBController_CorsairK55RGBPROXT.cpp @@ -0,0 +1,446 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairK55RGBPROXT.cpp | +| | +| RGBController for Corsair K55 RGB PRO XT keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CorsairK55RGBPROXT.h" +#include "RGBControllerKeyNames.h" +#include "LogManager.h" + +using namespace std::chrono_literals; + +#define NA 0xFFFFFFFF +#define WIDTH 24 +#define HEIGHT 6 + +static unsigned int matrix_map[HEIGHT][WIDTH] = + { { 0, 6, NA, 15, 21, 26, 31, NA, 37, 43, 47, 52, NA, 60, 66, 72, 78, 81, 85, 90, NA, NA, NA, NA }, + { 1, 7, 12, 16, 22, 27, 32, 36, 38, 44, 48, 53, NA, 61, 67, 73, NA, 82, 86, 91, 94, 99, 103, 108 }, + { 2, 8, NA, 17, 23, 28, 33, NA, 39, 45, 49, 54, 57, 62, 68, 74, 79, 83, 87, 92, 95, 100, 104, 109 }, + { 3, 9, NA, 18, 24, 29, 34, NA, 40, 46, 50, 55, 58, 63, 69, 75, NA, NA, NA, NA, 96, 101, 105, NA }, + { 4, 10, 13, 19, 25, 30, 35, NA, 41, NA, 51, 56, 59, 64, 70, 76, NA, NA, 88, NA, 97, 102, 106, 110 }, + { 5, 11, 14, 20, NA, NA, NA, NA, 42, NA, NA, NA, NA, 65, 71, 77, 80, 84, 89, 93, 98, NA, 107, NA } }; + +std::vector key_names = +{ + // col 0 + "Key: G1", + "Key: G2", + "Key: G3", + "Key: G4", + "Key: G5", + "Key: G6", + + // col 1 + KEY_EN_ESCAPE, + KEY_EN_BACK_TICK, + KEY_EN_TAB, + KEY_EN_CAPS_LOCK, + KEY_EN_LEFT_SHIFT, + KEY_EN_LEFT_CONTROL, + + // col 2 + KEY_EN_1, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_LEFT_WINDOWS, + + // col 3 + KEY_EN_F1, + KEY_EN_2, + KEY_EN_Q, + KEY_EN_A, + KEY_EN_Z, + KEY_EN_LEFT_ALT, + + // col 4 + KEY_EN_F2, + KEY_EN_3, + KEY_EN_W, + KEY_EN_S, + KEY_EN_X, + + // col 5 + KEY_EN_F3, + KEY_EN_4, + KEY_EN_E, + KEY_EN_D, + KEY_EN_C, + + // col 6 + KEY_EN_F4, + KEY_EN_5, + KEY_EN_R, + KEY_EN_F, + KEY_EN_V, + + // col 7 + KEY_EN_6, + + // col 8 + KEY_EN_F5, + KEY_EN_7, + KEY_EN_T, + KEY_EN_G, + KEY_EN_B, + KEY_EN_SPACE, + + // col 9 + KEY_EN_F6, + KEY_EN_8, + KEY_EN_Y, + KEY_EN_H, + + // col 10 + KEY_EN_F7, + KEY_EN_9, + KEY_EN_U, + KEY_EN_J, + KEY_EN_N, + + // col 11 + KEY_EN_F8, + KEY_EN_0, + KEY_EN_I, + KEY_EN_K, + KEY_EN_M, + + // col 12 + KEY_EN_O, + KEY_EN_L, + KEY_EN_COMMA, + + // col 13 + KEY_EN_F9, + KEY_EN_MINUS, + KEY_EN_P, + KEY_EN_SEMICOLON, + KEY_EN_PERIOD, + KEY_EN_RIGHT_ALT, + + // col 14 + KEY_EN_F10, + KEY_EN_EQUALS, + KEY_EN_LEFT_BRACKET, + KEY_EN_QUOTE, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_FUNCTION, + + // col 15 + KEY_EN_F11, + KEY_EN_BACKSPACE, + KEY_EN_RIGHT_BRACKET, + KEY_EN_POUND, + KEY_EN_RIGHT_SHIFT, + KEY_EN_MENU, + + // col 16 + KEY_EN_F12, + KEY_EN_ANSI_ENTER, + KEY_EN_RIGHT_CONTROL, + + // col 17 + KEY_EN_PRINT_SCREEN, + KEY_EN_INSERT, + KEY_EN_DELETE, + KEY_EN_LEFT_ARROW, + + // col 18 + KEY_EN_SCROLL_LOCK, + KEY_EN_HOME, + KEY_EN_END, + KEY_EN_UP_ARROW, + KEY_EN_DOWN_ARROW, + + // col 19 + KEY_EN_PAUSE_BREAK, + KEY_EN_PAGE_UP, + KEY_EN_PAGE_DOWN, + KEY_EN_RIGHT_ARROW, + + // col 20 + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_0, + + // col 21 + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_2, + + // col 22 + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_PERIOD, + + // col 23 + KEY_EN_NUMPAD_MINUS, + KEY_EN_NUMPAD_PLUS, + KEY_EN_NUMPAD_ENTER +}; + + +/**------------------------------------------------------------------*\ + @name Corsair K55 RGB Pro XT + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCorsairK55RGBPROXTControllers + @comment +\*-------------------------------------------------------------------*/ + + +RGBController_CorsairK55RGBPROXT::RGBController_CorsairK55RGBPROXT(CorsairK55RGBPROXTController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Corsair"; + description = "Corsair K55 RGB PRO XT Keyboard Device"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CorsairK55RGBPROXTController::CORSAIR_MODE_DIRECT_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_STATIC_VALUE; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode ColorPulse; + ColorPulse.name = "ColorPulse"; + ColorPulse.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_COLOR_PULSE_VALUE; + ColorPulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + ColorPulse.color_mode = MODE_COLORS_RANDOM; + ColorPulse.speed = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MED; + ColorPulse.speed_min = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MIN; + ColorPulse.speed_max = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MAX; + ColorPulse.colors.resize(2); + modes.push_back(ColorPulse); + + mode ColorShift; + ColorShift.name = "ColorShift"; + ColorShift.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_COLOR_SHIFT_VALUE; + ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + ColorShift.color_mode = MODE_COLORS_RANDOM; + ColorShift.speed = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MED; + ColorShift.speed_min = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MIN; + ColorShift.speed_max = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MAX; + ColorShift.colors.resize(2); + modes.push_back(ColorShift); + + mode ColorWave; + ColorWave.name = "ColorWave"; + ColorWave.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_COLOR_WAVE_VALUE; + ColorWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + ColorWave.color_mode = MODE_COLORS_RANDOM; + ColorWave.direction = MODE_DIRECTION_RIGHT; + ColorWave.speed = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MED; + ColorWave.speed_min = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MIN; + ColorWave.speed_max = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MAX; + ColorWave.colors.resize(2); + modes.push_back(ColorWave); + + mode RainbowWave; + RainbowWave.name = "RainbowWave"; + RainbowWave.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_RAINBOW_WAVE_VALUE; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.direction = MODE_DIRECTION_RIGHT; + RainbowWave.speed = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MED; + RainbowWave.speed_min = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MIN; + RainbowWave.speed_max = CorsairK55RGBPROXTController:: CORSAIR_HW_MODE_SPEED_MAX; + modes.push_back(RainbowWave); + + mode Rain; + Rain.name = "Rain"; + Rain.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_RAIN_VALUE; + Rain.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Rain.color_mode = MODE_COLORS_RANDOM; + Rain.direction = MODE_DIRECTION_DOWN; + Rain.speed = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MED; + Rain.speed_min = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MIN; + Rain.speed_max = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MAX; + Rain.colors.resize(2); + modes.push_back(Rain); + + mode Spiral; + Spiral.name = "Spiral"; + Spiral.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPIRAL_VALUE; + Spiral.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Spiral.color_mode = MODE_COLORS_NONE; + Spiral.direction = MODE_DIRECTION_RIGHT; + Spiral.speed = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MED; + Spiral.speed_min = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MIN; + Spiral.speed_max = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MAX; + modes.push_back(Spiral); + + mode WaterColor; + WaterColor.name = "WaterColor"; + WaterColor.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_WATER_COLOR_VALUE; + WaterColor.flags = MODE_FLAG_HAS_SPEED; + WaterColor.color_mode = MODE_COLORS_NONE; + WaterColor.speed = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MED; + WaterColor.speed_min = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MIN; + WaterColor.speed_max = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MAX; + WaterColor.colors.resize(1); + WaterColor.colors[0] = 0x00FFFFFF; + modes.push_back(WaterColor); + + mode TypeKey; + TypeKey.name = "TypeKey"; + TypeKey.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_TYPE_KEY_VALUE; + TypeKey.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + TypeKey.color_mode = MODE_COLORS_RANDOM; + TypeKey.speed = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MED; + TypeKey.speed_min = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MIN; + TypeKey.speed_max = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MAX; + TypeKey.colors.resize(2); + modes.push_back(TypeKey); + + mode TypeRipple; + TypeRipple.name = "TypeRipple"; + TypeRipple.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_TYPE_RIPPLE_VALUE; + TypeRipple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + TypeRipple.color_mode = MODE_COLORS_RANDOM; + TypeRipple.speed = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MED; + TypeRipple.speed_min = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MIN; + TypeRipple.speed_max = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MAX; + TypeRipple.colors.resize(2); + modes.push_back(TypeRipple); + + mode Visor; + Visor.name = "Visor"; + Visor.value = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_VISOR_VALUE; + Visor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Visor.color_mode = MODE_COLORS_RANDOM; + Visor.direction = MODE_DIRECTION_RIGHT; + Visor.speed = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MED; + Visor.speed_min = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MIN; + Visor.speed_max = CorsairK55RGBPROXTController::CORSAIR_HW_MODE_SPEED_MAX; + Visor.colors.resize(2); + modes.push_back(Visor); + + SetupZones(); + /*-----------------------------------------------------*\ + | The Corsair K55 RGB PRO XT requires a packet within | + | 1 minutes of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 50 sec | + \*-----------------------------------------------------*/ + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RGBController_CorsairK55RGBPROXT::KeepaliveThread, this); +} + +RGBController_CorsairK55RGBPROXT::~RGBController_CorsairK55RGBPROXT() +{ + /*-----------------------------------------------------*\ + | Close keepalive thread | + \*-----------------------------------------------------*/ + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + delete[] zones[0].matrix_map; + + delete controller; +} + +void RGBController_CorsairK55RGBPROXT::SetupZones() +{ + zone keyboard_zone; + keyboard_zone.name = "Keyboard"; + keyboard_zone.type = ZONE_TYPE_MATRIX; + + keyboard_zone.matrix_map = new matrix_map_type; + keyboard_zone.matrix_map->map = (unsigned int *)&matrix_map; + keyboard_zone.matrix_map->height = HEIGHT; + keyboard_zone.matrix_map->width = WIDTH; + + for(size_t led_index = 0; led_index < key_names.size(); ++led_index) + { + led new_led; + new_led.name = key_names[led_index]; + leds.push_back(new_led); + } + + keyboard_zone.leds_min = (unsigned int)leds.size(); + keyboard_zone.leds_max = (unsigned int)leds.size(); + keyboard_zone.leds_count = (unsigned int)leds.size(); + + zones.push_back(keyboard_zone); + + SetupColors(); +} + +void RGBController_CorsairK55RGBPROXT::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CorsairK55RGBPROXT::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + controller->SetLEDs(colors); +} + +void RGBController_CorsairK55RGBPROXT::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_CorsairK55RGBPROXT::UpdateSingleLED(int /*led*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_CorsairK55RGBPROXT::DeviceUpdateMode() +{ + if(modes[active_mode].value == CorsairK55RGBPROXTController::CORSAIR_MODE_DIRECT_VALUE) + { + controller->SwitchMode(true); + } + else + { + const mode& active = modes[active_mode]; + + controller->SetHardwareMode(active.value, active.color_mode, active.colors, active.speed, active.direction); + controller->SwitchMode(false); + } +} + +void RGBController_CorsairK55RGBPROXT::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50000)) + { + DeviceUpdateLEDs(); + } + } + std::this_thread::sleep_for(3000ms); + } +} diff --git a/Controllers/CorsairPeripheralController/RGBController_CorsairK55RGBPROXT.h b/Controllers/CorsairPeripheralController/RGBController_CorsairK55RGBPROXT.h new file mode 100644 index 0000000..d821ffa --- /dev/null +++ b/Controllers/CorsairPeripheralController/RGBController_CorsairK55RGBPROXT.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| CorsairK55RGBPROXTController.h | +| | +| Driver for Corsair K55 RGB PRO XT keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairK55RGBPROXTController.h" + +class RGBController_CorsairK55RGBPROXT : public RGBController +{ +public: + RGBController_CorsairK55RGBPROXT(CorsairK55RGBPROXTController* controller_ptr); + ~RGBController_CorsairK55RGBPROXT(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void KeepaliveThread(); + +private: + CorsairK55RGBPROXTController* controller; + + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/CorsairPeripheralController/RGBController_CorsairK65Mini.cpp b/Controllers/CorsairPeripheralController/RGBController_CorsairK65Mini.cpp new file mode 100644 index 0000000..ca8ae2b --- /dev/null +++ b/Controllers/CorsairPeripheralController/RGBController_CorsairK65Mini.cpp @@ -0,0 +1,259 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairK65Mini.cpp | +| | +| RGBController for Corsair K65 Mini keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CorsairK65Mini.h" +#include "LogManager.h" +#include "RGBControllerKeyNames.h" + +using namespace std::chrono_literals; + +#define NA 0xFFFFFFFF +#define WIDTH 15 +#define HEIGHT 5 + +unsigned int matrix_map[HEIGHT][WIDTH] = +{ + { 0, 5, 8, 13, 17, 22, 26, 31, 35, 40, 44, 49, 54, NA, 60}, + { 1, NA, 9, 14, 18, 23, 27, 32, 36, 41, 45, 50, 55, 58, NA}, + { 2, NA, 10, 15, 19, 24, 28, 33, 37, 42, 46, 51, 56, 59, 61}, + { 3, 6, 11, 16, 20, 25, 29, 34, 38, 43, 47, 52, NA, NA, 62}, + { 4, 7, 12, NA, 21, NA, 30, NA, 39, NA, 48, 53, 57, NA, 63} +}; + +std::vector> keys = +{ + // col 1 + {41, KEY_EN_ESCAPE}, + {43, KEY_EN_TAB}, + {57, KEY_EN_CAPS_LOCK}, + {106, KEY_EN_LEFT_SHIFT}, + {105, KEY_EN_LEFT_CONTROL}, + + // col 2 + {30, KEY_EN_1}, + {100, KEY_EN_ISO_BACK_SLASH}, + {108, KEY_EN_LEFT_WINDOWS}, + + // col 3 + {31, KEY_EN_2}, + {20, KEY_EN_Q}, + {4, KEY_EN_A}, + {29, KEY_EN_Z}, + {107, KEY_EN_LEFT_ALT}, + + // col 4 + {32, KEY_EN_3}, + {26, KEY_EN_W}, + {22, KEY_EN_S}, + {27, KEY_EN_X}, + + // col 5 + {33, KEY_EN_4}, + {8, KEY_EN_E}, + {7, KEY_EN_D}, + {6, KEY_EN_C}, + {0, ""}, // space bar, left LED + + // col 6 + {34, KEY_EN_5}, + {21, KEY_EN_R}, + {9, KEY_EN_F}, + {25, KEY_EN_V}, + + // col 7 + {35, KEY_EN_6}, + {23, KEY_EN_T}, + {10, KEY_EN_G}, + {5, KEY_EN_B}, + {44, KEY_EN_SPACE}, + + // col 8 + {36, KEY_EN_7}, + {28, KEY_EN_Y}, + {11, KEY_EN_H}, + {17, KEY_EN_N}, + + // col 9 + {37, KEY_EN_8}, + {24, KEY_EN_U}, + {13, KEY_EN_J}, + {16, KEY_EN_M}, + {1, ""}, // space bar, right LED + + // col 10 + {38, KEY_EN_9}, + {12, KEY_EN_I}, + {14, KEY_EN_K}, + {54, KEY_EN_COMMA}, + + // col 11 + {39, KEY_EN_0}, + {18, KEY_EN_O}, + {15, KEY_EN_L}, + {55, KEY_EN_PERIOD}, + {111, KEY_EN_RIGHT_ALT}, + + // col 12 + {45, KEY_EN_MINUS}, + {19, KEY_EN_P}, + {51, KEY_EN_SEMICOLON}, + {56, KEY_EN_FORWARD_SLASH}, + {122, KEY_EN_RIGHT_FUNCTION}, + + // col 13 + {46, KEY_EN_EQUALS}, + {47, KEY_EN_LEFT_BRACKET}, + {52, KEY_EN_QUOTE}, + {101, KEY_EN_MENU}, + + // col 14 + {48, KEY_EN_RIGHT_BRACKET}, + {50, KEY_EN_POUND}, + + // col 15 + {42, KEY_EN_BACKSPACE}, + {40, KEY_EN_ISO_ENTER}, + {110, KEY_EN_RIGHT_SHIFT}, + {109, KEY_EN_RIGHT_CONTROL} +}; + +/**------------------------------------------------------------------*\ + @name Corsair K65 Mini + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectCorsairK65MiniControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CorsairK65Mini::RGBController_CorsairK65Mini(CorsairK65MiniController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Corsair"; + description = "Corsair K65 Mini Keyboard Device"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + /*-----------------------------------------------------*\ + | The Corsair K65 Mini requires a packet within | + | 1 minutes of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 50 sec | + \*-----------------------------------------------------*/ + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RGBController_CorsairK65Mini::KeepaliveThread, this); +} + +RGBController_CorsairK65Mini::~RGBController_CorsairK65Mini() +{ + /*-----------------------------------------------------*\ + | Close keepalive thread | + \*-----------------------------------------------------*/ + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_CorsairK65Mini::SetupZones() +{ + unsigned int zone_size = 0; + + zone keyboard_zone; + keyboard_zone.name = ZONE_EN_KEYBOARD; + keyboard_zone.type = ZONE_TYPE_MATRIX; + + keyboard_zone.matrix_map = new matrix_map_type; + keyboard_zone.matrix_map->height = HEIGHT; + keyboard_zone.matrix_map->width = WIDTH; + + keyboard_zone.matrix_map->map = new unsigned int[HEIGHT * WIDTH]; + + for(unsigned int w = 0; w < WIDTH; w++) + { + for(unsigned int h = 0; h < HEIGHT; h++) + { + unsigned int key = matrix_map[h][w]; + keyboard_zone.matrix_map->map[h * WIDTH + w] = key; + + if(key != NA) + { + led new_led; + new_led.name = std::get<1>(keys[key]); + leds.push_back(new_led); + zone_size++; + led_positions.push_back(std::get<0>(keys[key])); + } + } + } + + keyboard_zone.leds_min = zone_size; + keyboard_zone.leds_max = zone_size; + keyboard_zone.leds_count = zone_size; + + zones.push_back(keyboard_zone); + + SetupColors(); +} + +void RGBController_CorsairK65Mini::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CorsairK65Mini::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + controller->SetLEDs(colors, led_positions); +} + +void RGBController_CorsairK65Mini::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairK65Mini::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairK65Mini::DeviceUpdateMode() +{ + +} + +void RGBController_CorsairK65Mini::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50000)) + { + DeviceUpdateLEDs(); + } + + std::this_thread::sleep_for(3000ms); + } +} diff --git a/Controllers/CorsairPeripheralController/RGBController_CorsairK65Mini.h b/Controllers/CorsairPeripheralController/RGBController_CorsairK65Mini.h new file mode 100644 index 0000000..d1f79b1 --- /dev/null +++ b/Controllers/CorsairPeripheralController/RGBController_CorsairK65Mini.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairK65Mini.h | +| | +| RGBController for Corsair K65 Mini keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairK65MiniController.h" + +class RGBController_CorsairK65Mini : public RGBController +{ +public: + RGBController_CorsairK65Mini(CorsairK65MiniController* controller_ptr); + ~RGBController_CorsairK65Mini(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + CorsairK65MiniController* controller; + + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; + std::vector led_positions; +}; diff --git a/Controllers/CorsairPeripheralController/RGBController_CorsairPeripheral.cpp b/Controllers/CorsairPeripheralController/RGBController_CorsairPeripheral.cpp new file mode 100644 index 0000000..e1b6446 --- /dev/null +++ b/Controllers/CorsairPeripheralController/RGBController_CorsairPeripheral.cpp @@ -0,0 +1,1242 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairPeripheral.cpp | +| | +| RGBController for Corsair peripherals | +| | +| Adam Honse (CalcProgrammer1) 09 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_CorsairPeripheral.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[6][23] = + { { 0, NA, 10, 18, 28, 36, NA, 46, 55, 64, 74, NA, 84, 93, 102, 6, 15, 24, 33, 26, 35, 44, 53 }, + { 1, 11, 19, 29, 37, 47, 56, 65, 75, 85, 94, NA, 103, 7, 25, NA, 42, 51, 60, 62, 72, 82, 91 }, + { 2, NA, 12, 20, 30, 38, NA, 48, 57, 66, 76, 86, 95, 104, 70, 80, 34, 43, 52, 9, 17, 27, 100 }, + { 3, NA, 13, 21, 31, 39, NA, 49, 58, 67, 77, 87, 96, 105, 98, 112, NA, NA, NA, 45, 54, 63, NA }, + { 4, 111, 22, 32, 40, 50, NA, 59, NA, 68, 78, 88, 97, 106, 61, NA, NA, 81, NA, 73, 83, 92, 109 }, + { 5, 14, 23, NA, NA, NA, NA, 41, NA, NA, NA, NA, 69, 79, 89, 71, 90, 99, 108, 101, NA, 110, NA } }; + +static unsigned int matrix_map_k70_mk2[7][23] = + { { NA, NA, NA, 115, 107, 8, NA, NA, NA, NA, NA, 113, 114, NA, NA, NA, NA, NA, NA, 16, NA, NA, NA,}, + { 0, NA, 10, 18, 28, 36, NA, 46, 55, 64, 74, NA, 84, 93, 102, 6, 15, 24, 33, 26, 35, 44, 53 }, + { 1, 11, 19, 29, 37, 47, 56, 65, 75, 85, 94, NA, 103, 7, 25, NA, 42, 51, 60, 62, 72, 82, 91 }, + { 2, NA, 12, 20, 30, 38, NA, 48, 57, 66, 76, 86, 95, 104, 70, 80, 34, 43, 52, 9, 17, 27, 100 }, + { 3, NA, 13, 21, 31, 39, NA, 49, 58, 67, 77, 87, 96, 105, 98, 112, NA, NA, NA, 45, 54, 63, NA }, + { 4, 111, 22, 32, 40, 50, NA, 59, NA, 68, 78, 88, 97, 106, 61, NA, NA, 81, NA, 73, 83, 92, 109 }, + { 5, 14, 23, NA, NA, NA, NA, 41, NA, NA, NA, NA, 69, 79, 89, 71, 90, 99, 108, 101, NA, 110, NA } }; + +static unsigned int matrix_map_k95_platinum[7][24] = + { { NA, NA, NA, 119, 107, 8, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 16, NA, NA, NA,}, + { 113, 0, NA, 10, 18, 28, 36, NA, 46, 55, 64, 74, NA, 84, 93, 102, 6, 15, 24, 33, 26, 35, 44, 53 }, + { 114, 1, 11, 19, 29, 37, 47, 56, 65, 75, 85, 94, NA, 103, 7, 25, NA, 42, 51, 60, 62, 72, 82, 91 }, + { 115, 2, NA, 12, 20, 30, 38, NA, 48, 57, 66, 76, 86, 95, 104, 70, 80, 34, 43, 52, 9, 17, 27, 100 }, + { 116, 3, NA, 13, 21, 31, 39, NA, 49, 58, 67, 77, 87, 96, 105, 98, 112, NA, NA, NA, 45, 54, 63, NA }, + { 117, 4, 111, 22, 32, 40, 50, NA, 59, NA, 68, 78, 88, 97, 106, 61, NA, NA, 81, NA, 73, 83, 92, 109 }, + { 118, 5, 14, 23, NA, NA, NA, NA, 41, NA, NA, NA, NA, 69, 79, 89, 71, 90, 99, 108, 101, NA, 110, NA } }; + +static unsigned int matrix_map_k95[7][26] = + { { NA, NA, NA, 131, 132, 133, 134, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 107, 8, NA, NA, 16, NA, NA,}, + { 113, 114, 115, 0, NA, 10, 18, 28, 36, NA, 46, 55, 64, 74, NA, 84, 93, 102, 6, 15, 24, 33, 26, 35, 44, 53 }, + { 116, 117, 118, 1, 11, 19, 29, 37, 47, 56, 65, 75, 85, 94, NA, 103, 7, 25, NA, 42, 51, 60, 62, 72, 82, 91 }, + { 119, 120, 121, 2, NA, 12, 20, 30, 38, NA, 48, 57, 66, 76, 86, 95, 104, 70, 80, 34, 43, 52, 9, 17, 27, 100 }, + { 122, 123, 124, 3, NA, 13, 21, 31, 39, NA, 49, 58, 67, 77, 87, 96, 105, 98, 112, NA, NA, NA, 45, 54, 63, NA }, + { 125, 126, 127, 4, 111, 22, 32, 40, 50, NA, 59, NA, 68, 78, 88, 97, 106, 61, NA, NA, 81, NA, 73, 83, 92, 109 }, + { 128, 129, 130, 5, 14, 23, NA, NA, NA, NA, 41, NA, NA, NA, NA, 69, 79, 89, 71, 90, 99, 108, 101, NA, 110, NA } }; + +/*---------------------------------------------------------*\ +| Normal Corsair Layout | +\*---------------------------------------------------------*/ +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static const unsigned int zone_sizes[] = +{ + 113 +}; + +static const zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +/*---------------------------------------------------------*\ +| K70 MK2 Corsair Layout | +\*---------------------------------------------------------*/ +static const char* zone_names_k70_mk2[] = +{ + ZONE_EN_KEYBOARD, +}; + +static const unsigned int zone_sizes_k70_mk2[] = +{ + 116 +}; + +static const zone_type zone_types_k70_mk2[] = +{ + ZONE_TYPE_MATRIX, +}; + +/*---------------------------------------------------------*\ +| K95 Platinum | +\*---------------------------------------------------------*/ +static const char* zone_names_k95_platinum[] = +{ + ZONE_EN_KEYBOARD, + "Light Bar" +}; + +static const unsigned int zone_sizes_k95_platinum[] = +{ + 120, + 19 +}; + +static const zone_type zone_types_k95_platinum[] = +{ + ZONE_TYPE_MATRIX, + ZONE_TYPE_LINEAR +}; + +/*---------------------------------------------------------*\ +| K95 non-Platinum | +\*---------------------------------------------------------*/ +static const char* zone_names_k95[] = +{ + ZONE_EN_KEYBOARD, +}; + +static const unsigned int zone_sizes_k95[] = +{ + 135 +}; + +static const zone_type zone_types_k95[] = +{ + ZONE_TYPE_MATRIX +}; + +/*---------------------------------------------------------*\ +| K55 | +\*---------------------------------------------------------*/ +static const char* zone_names_k55[] = +{ + "Left", + "Middle", + "Right", +}; + +static const unsigned int zone_sizes_k55[] = +{ + 1, + 1, + 1 +}; + +static const zone_type zone_types_k55[] = +{ + ZONE_TYPE_SINGLE, + ZONE_TYPE_SINGLE, + ZONE_TYPE_SINGLE +}; + +static const char* led_names[] = +{ + KEY_EN_ESCAPE, //0 + KEY_EN_BACK_TICK, //1 + KEY_EN_TAB, //2 + KEY_EN_CAPS_LOCK, //3 + KEY_EN_LEFT_SHIFT, //4 + KEY_EN_LEFT_CONTROL, //5 + KEY_EN_F12, //6 + KEY_EN_EQUALS, //7 + "Key: Lock", //8 + KEY_EN_NUMPAD_7, //9 + KEY_EN_F1, //12 + KEY_EN_1, //13 + KEY_EN_Q, //14 + KEY_EN_A, //15 + KEY_EN_LEFT_WINDOWS, //17 + KEY_EN_PRINT_SCREEN, //18 + KEY_EN_MEDIA_MUTE, //20 + KEY_EN_NUMPAD_8, //21 + KEY_EN_F2, //24 + KEY_EN_2, //25 + KEY_EN_W, //26 + KEY_EN_S, //27 + KEY_EN_Z, //28 + KEY_EN_LEFT_ALT, //29 + KEY_EN_SCROLL_LOCK, //30 + KEY_EN_BACKSPACE, //31 + KEY_EN_MEDIA_STOP, //32 + KEY_EN_NUMPAD_9, //33 + KEY_EN_F3, //36 + KEY_EN_3, //37 + KEY_EN_E, //38 + KEY_EN_D, //39 + KEY_EN_X, //40 + KEY_EN_PAUSE_BREAK, //42 + KEY_EN_DELETE, //43 + KEY_EN_MEDIA_PREVIOUS, //44 + KEY_EN_F4, //48 + KEY_EN_4, //49 + KEY_EN_R, //50 + KEY_EN_F, //51 + KEY_EN_C, //52 + KEY_EN_SPACE, //53 + KEY_EN_INSERT, //54 + KEY_EN_END, //55 + KEY_EN_MEDIA_PLAY_PAUSE, //56 + KEY_EN_NUMPAD_4, //57 + KEY_EN_F5, //60 + KEY_EN_5, //61 + KEY_EN_T, //62 + KEY_EN_G, //63 + KEY_EN_V, //64 + KEY_EN_HOME, //66 + KEY_EN_PAGE_DOWN, //67 + KEY_EN_MEDIA_NEXT, //68 + KEY_EN_NUMPAD_5, //69 + KEY_EN_F6, //72 + KEY_EN_6, //73 + KEY_EN_Y, //74 + KEY_EN_H, //75 + KEY_EN_B, //76 + KEY_EN_PAGE_UP, //78 + KEY_EN_RIGHT_SHIFT, //79 + KEY_EN_NUMPAD_LOCK, //80 + KEY_EN_NUMPAD_6, //81 + KEY_EN_F7, //84 + KEY_EN_7, //85 + KEY_EN_U, //86 + KEY_EN_J, //87 + KEY_EN_N, //88 + KEY_EN_RIGHT_ALT, //89 + KEY_EN_RIGHT_BRACKET, //90 + KEY_EN_RIGHT_CONTROL, //91 + KEY_EN_NUMPAD_DIVIDE, //92 + KEY_EN_NUMPAD_1, //93 + KEY_EN_F8, //96 + KEY_EN_8, //97 + KEY_EN_I, //98 + KEY_EN_K, //99 + KEY_EN_M, //100 + KEY_EN_RIGHT_WINDOWS, //101 + KEY_EN_ANSI_BACK_SLASH, //102 + KEY_EN_UP_ARROW, //103 + KEY_EN_NUMPAD_TIMES, //104 + KEY_EN_NUMPAD_2, //105 + KEY_EN_F9, //108 + KEY_EN_9, //109 + KEY_EN_O, //110 + KEY_EN_L, //111 + KEY_EN_COMMA, //112 + KEY_EN_MENU, //113 + KEY_EN_LEFT_ARROW, //115 + KEY_EN_NUMPAD_MINUS, //116 + KEY_EN_NUMPAD_3, //117 + KEY_EN_F10, //120 + KEY_EN_0, //121 + KEY_EN_P, //122 + KEY_EN_SEMICOLON, //123 + KEY_EN_PERIOD, //124 + KEY_EN_ANSI_ENTER, //126 + KEY_EN_DOWN_ARROW, //127 + KEY_EN_NUMPAD_PLUS, //128 + KEY_EN_NUMPAD_0, //129 + KEY_EN_F11, //132 + KEY_EN_MINUS, //133 + KEY_EN_LEFT_BRACKET, //134 + KEY_EN_QUOTE, //135 + KEY_EN_FORWARD_SLASH, //136 + "Key: Brightness", //137 + KEY_EN_RIGHT_ARROW, //139 + KEY_EN_NUMPAD_ENTER, //140 + KEY_EN_NUMPAD_PERIOD, //141 + "Key: / (ISO)", + KEY_EN_ISO_BACK_SLASH, +}; + +static const char* led_names_k70_mk2[] = +{ + KEY_EN_ESCAPE, //0 + KEY_EN_BACK_TICK, //1 + KEY_EN_TAB, //2 + KEY_EN_CAPS_LOCK, //3 + KEY_EN_LEFT_SHIFT, //4 + KEY_EN_LEFT_CONTROL, //5 + KEY_EN_F12, //6 + KEY_EN_EQUALS, //7 + "Key: Lock", //8 + KEY_EN_NUMPAD_7, //9 + KEY_EN_F1, //12 + KEY_EN_1, //13 + KEY_EN_Q, //14 + KEY_EN_A, //15 + //"Key: / (ISO)", //16 + KEY_EN_LEFT_WINDOWS, //17 + KEY_EN_PRINT_SCREEN, //18 + KEY_EN_MEDIA_MUTE, //20 + KEY_EN_NUMPAD_8, //21 + KEY_EN_F2, //24 + KEY_EN_2, //25 + KEY_EN_W, //26 + KEY_EN_S, //27 + KEY_EN_Z, //28 + KEY_EN_LEFT_ALT, //29 + KEY_EN_SCROLL_LOCK, //30 + KEY_EN_BACKSPACE, //31 + KEY_EN_MEDIA_STOP, //32 + KEY_EN_NUMPAD_9, //33 + KEY_EN_F3, //36 + KEY_EN_3, //37 + KEY_EN_E, //38 + KEY_EN_D, //39 + KEY_EN_X, //40 + KEY_EN_PAUSE_BREAK, //42 + KEY_EN_DELETE, //43 + KEY_EN_MEDIA_PREVIOUS, //44 + //"Key: Logo Left", //047 + KEY_EN_F4, //48 + KEY_EN_4, //49 + KEY_EN_R, //50 + KEY_EN_F, //51 + KEY_EN_C, //52 + KEY_EN_SPACE, //53 + KEY_EN_INSERT, //54 + KEY_EN_END, //55 + KEY_EN_MEDIA_PLAY_PAUSE, //56 + KEY_EN_NUMPAD_4, //57 + //"Key: Logo Right", //059 + KEY_EN_F5, //60 + KEY_EN_5, //61 + KEY_EN_T, //62 + KEY_EN_G, //63 + KEY_EN_V, //64 + KEY_EN_HOME, //66 + KEY_EN_PAGE_DOWN, //67 + KEY_EN_MEDIA_NEXT, //68 + KEY_EN_NUMPAD_5, //69 + KEY_EN_F6, //72 + KEY_EN_6, //73 + KEY_EN_Y, //74 + KEY_EN_H, //75 + KEY_EN_B, //76 + KEY_EN_PAGE_UP, //78 + KEY_EN_RIGHT_SHIFT, //79 + KEY_EN_NUMPAD_LOCK, //80 + KEY_EN_NUMPAD_6, //81 + KEY_EN_F7, //84 + KEY_EN_7, //85 + KEY_EN_U, //86 + KEY_EN_J, //87 + KEY_EN_N, //88 + KEY_EN_RIGHT_ALT, //89 + KEY_EN_RIGHT_BRACKET, //90 + KEY_EN_RIGHT_CONTROL, //91 + KEY_EN_NUMPAD_DIVIDE, //92 + KEY_EN_NUMPAD_1, //93 + KEY_EN_F8, //96 + KEY_EN_8, //97 + KEY_EN_I, //98 + KEY_EN_K, //99 + KEY_EN_M, //100 + KEY_EN_RIGHT_WINDOWS, //101 + KEY_EN_ANSI_BACK_SLASH, //102 + KEY_EN_UP_ARROW, //103 + KEY_EN_NUMPAD_TIMES, //104 + KEY_EN_NUMPAD_2, //105 + KEY_EN_F9, //108 + KEY_EN_9, //109 + KEY_EN_O, //110 + KEY_EN_L, //111 + KEY_EN_COMMA, //112 + KEY_EN_MENU, //113 + //KEY_EN_ISO_BACK_SLASH, //114 + KEY_EN_LEFT_ARROW, //115 + KEY_EN_NUMPAD_MINUS, //116 + KEY_EN_NUMPAD_3, //117 + KEY_EN_F10, //120 + KEY_EN_0, //121 + KEY_EN_P, //122 + KEY_EN_SEMICOLON, //123 + //"Key: Profile", //125 + KEY_EN_PERIOD, //124 + KEY_EN_ANSI_ENTER, //126 + KEY_EN_DOWN_ARROW, //127 + KEY_EN_NUMPAD_PLUS, //128 + KEY_EN_NUMPAD_0, //129 + KEY_EN_F11, //132 + KEY_EN_MINUS, //133 + KEY_EN_LEFT_BRACKET, //134 + KEY_EN_QUOTE, //135 + KEY_EN_FORWARD_SLASH, //136 + "Key: Brightness", //137 + KEY_EN_RIGHT_ARROW, //139 + KEY_EN_NUMPAD_ENTER, //140 + KEY_EN_NUMPAD_PERIOD, //141 + "Key: / (ISO)", //16 + KEY_EN_ISO_BACK_SLASH, //114 + "Key: Logo Left", //047 + "Key: Logo Right", //059 + "Key: Profile", //125 +}; + +static const char* led_names_k95_plat[] = +{ + KEY_EN_ESCAPE, //0 + KEY_EN_BACK_TICK, //1 + KEY_EN_TAB, //2 + KEY_EN_CAPS_LOCK , //3 + KEY_EN_LEFT_SHIFT, //4 + KEY_EN_LEFT_CONTROL, //5 + KEY_EN_F12, //6 + KEY_EN_EQUALS, //7 + "Key: Lock", //8 + KEY_EN_NUMPAD_7, //9 + KEY_EN_F1, //12 + KEY_EN_1, //13 + KEY_EN_Q, //14 + KEY_EN_A, //15 + KEY_EN_LEFT_WINDOWS, //17 + KEY_EN_PRINT_SCREEN, //18 + KEY_EN_MEDIA_MUTE, //20 + KEY_EN_NUMPAD_8, //21 + KEY_EN_F2, //24 + KEY_EN_2, //25 + KEY_EN_W, //26 + KEY_EN_S, //27 + KEY_EN_Z, //28 + KEY_EN_LEFT_ALT, //29 + KEY_EN_SCROLL_LOCK, //30 + KEY_EN_BACKSPACE, //31 + KEY_EN_MEDIA_STOP, //32 + KEY_EN_NUMPAD_9, //33 + KEY_EN_F3, //36 + KEY_EN_3, //37 + KEY_EN_E, //38 + KEY_EN_D, //39 + KEY_EN_X, //40 + KEY_EN_PAUSE_BREAK, //42 + KEY_EN_DELETE, //43 + KEY_EN_MEDIA_PREVIOUS, //44 + KEY_EN_F4, //48 + KEY_EN_4, //49 + KEY_EN_R, //50 + KEY_EN_F, //51 + KEY_EN_C, //52 + KEY_EN_SPACE, //53 + KEY_EN_INSERT, //54 + KEY_EN_END, //55 + KEY_EN_MEDIA_PLAY_PAUSE, //56 + KEY_EN_NUMPAD_4, //57 + KEY_EN_F5, //60 + KEY_EN_5, //61 + KEY_EN_T, //62 + KEY_EN_G, //63 + KEY_EN_V, //64 + KEY_EN_HOME, //66 + KEY_EN_PAGE_DOWN, //67 + KEY_EN_MEDIA_NEXT, //68 + KEY_EN_NUMPAD_5, //69 + KEY_EN_F6, //72 + KEY_EN_6, //73 + KEY_EN_Y, //74 + KEY_EN_H, //75 + KEY_EN_B, //76 + KEY_EN_PAGE_UP, //78 + KEY_EN_RIGHT_SHIFT, //79 + KEY_EN_NUMPAD_LOCK, //80 + KEY_EN_NUMPAD_6, //81 + KEY_EN_F7, //84 + KEY_EN_7, //85 + KEY_EN_U, //86 + KEY_EN_J, //87 + KEY_EN_N, //88 + KEY_EN_RIGHT_ALT, //89 + KEY_EN_RIGHT_BRACKET, //90 + KEY_EN_RIGHT_CONTROL, //91 + KEY_EN_NUMPAD_DIVIDE, //92 + KEY_EN_NUMPAD_1, //93 + KEY_EN_F8, //96 + KEY_EN_8, //97 + KEY_EN_I, //98 + KEY_EN_K, //99 + KEY_EN_M, //100 + KEY_EN_RIGHT_WINDOWS, //101 + KEY_EN_ANSI_BACK_SLASH, //102 + KEY_EN_UP_ARROW, //103 + KEY_EN_NUMPAD_TIMES, //104 + KEY_EN_NUMPAD_2, //105 + KEY_EN_F9, //108 + KEY_EN_9, //109 + KEY_EN_O, //110 + KEY_EN_L, //111 + KEY_EN_COMMA, //112 + KEY_EN_MENU, //113 + KEY_EN_LEFT_ARROW, //115 + KEY_EN_NUMPAD_MINUS, //116 + KEY_EN_NUMPAD_3, //117 + KEY_EN_F10, //120 + KEY_EN_0, //121 + KEY_EN_P, //122 + KEY_EN_SEMICOLON, //123 + KEY_EN_PERIOD, //124 + KEY_EN_ANSI_ENTER, //126 + KEY_EN_DOWN_ARROW, //127 + KEY_EN_NUMPAD_PLUS, //128 + KEY_EN_NUMPAD_0, //129 + KEY_EN_F11, //132 + KEY_EN_MINUS, //133 + KEY_EN_LEFT_BRACKET, //134 + KEY_EN_QUOTE, //135 + KEY_EN_FORWARD_SLASH, //136 + "Key: Brightness", //137 + KEY_EN_RIGHT_ARROW, //139 + KEY_EN_NUMPAD_ENTER, //140 + KEY_EN_NUMPAD_PERIOD, //141 + "Key: / (ISO)", + KEY_EN_ISO_BACK_SLASH, + "Key: Macro G1", + "Key: Macro G2", + "Key: Macro G3", + "Key: Macro G4", + "Key: Macro G5", + "Key: Macro G6", + "Key: Preset", + "Light Bar 1", + "Light Bar 2", + "Light Bar 3", + "Light Bar 4", + "Light Bar 5", + "Light Bar 6", + "Light Bar 7", + "Light Bar 8", + "Light Bar 9", + "Light Bar 10", + "Light Bar 11", + "Light Bar 12", + "Light Bar 13", + "Light Bar 14", + "Light Bar 15", + "Light Bar 16", + "Light Bar 17", + "Light Bar 18", + "Light Bar 19" +}; + +static const char* led_names_k95[] = +{ + KEY_EN_ESCAPE, //0 + KEY_EN_BACK_TICK, //1 + KEY_EN_TAB, //2 + KEY_EN_CAPS_LOCK, //3 + KEY_EN_LEFT_SHIFT, //4 + KEY_EN_LEFT_CONTROL, //5 + KEY_EN_F12, //6 + KEY_EN_EQUALS, //7 + "Key: Lock", //8 + KEY_EN_NUMPAD_7, //9 + KEY_EN_F1, //12 + KEY_EN_1, //13 + KEY_EN_Q, //14 + KEY_EN_A, //15 + KEY_EN_LEFT_WINDOWS, //17 + KEY_EN_PRINT_SCREEN, //18 + KEY_EN_MEDIA_MUTE, //20 + KEY_EN_NUMPAD_8, //21 + KEY_EN_F2, //24 + KEY_EN_2, //25 + KEY_EN_W, //26 + KEY_EN_S, //27 + KEY_EN_Z, //28 + KEY_EN_LEFT_ALT, //29 + KEY_EN_SCROLL_LOCK, //30 + KEY_EN_BACKSPACE, //31 + KEY_EN_MEDIA_STOP, //32 + KEY_EN_NUMPAD_9, //33 + KEY_EN_F3, //36 + KEY_EN_3, //37 + KEY_EN_E, //38 + KEY_EN_D, //39 + KEY_EN_X, //40 + KEY_EN_PAUSE_BREAK, //42 + KEY_EN_DELETE, //43 + KEY_EN_MEDIA_PREVIOUS, //44 + KEY_EN_F4, //48 + KEY_EN_4, //49 + KEY_EN_R, //50 + KEY_EN_F, //51 + KEY_EN_C, //52 + KEY_EN_SPACE, //53 + KEY_EN_INSERT, //54 + KEY_EN_END, //55 + KEY_EN_MEDIA_PLAY_PAUSE, //56 + KEY_EN_NUMPAD_4, //57 + KEY_EN_F5, //60 + KEY_EN_5, //61 + KEY_EN_T, //62 + KEY_EN_G, //63 + KEY_EN_V, //64 + KEY_EN_HOME, //66 + KEY_EN_PAGE_DOWN, //67 + KEY_EN_MEDIA_NEXT, //68 + KEY_EN_NUMPAD_5, //69 + KEY_EN_F6, //72 + KEY_EN_6, //73 + KEY_EN_Y, //74 + KEY_EN_H, //75 + KEY_EN_B, //76 + KEY_EN_PAGE_UP, //78 + KEY_EN_RIGHT_SHIFT, //79 + KEY_EN_NUMPAD_LOCK, //80 + KEY_EN_NUMPAD_6, //81 + KEY_EN_F7, //84 + KEY_EN_7, //85 + KEY_EN_U, //86 + KEY_EN_J, //87 + KEY_EN_N, //88 + KEY_EN_RIGHT_ALT, //89 + KEY_EN_RIGHT_BRACKET, //90 + KEY_EN_RIGHT_CONTROL, //91 + KEY_EN_NUMPAD_DIVIDE, //92 + KEY_EN_NUMPAD_1, //93 + KEY_EN_F8, //96 + KEY_EN_8, //97 + KEY_EN_I, //98 + KEY_EN_K, //99 + KEY_EN_M, //100 + KEY_EN_RIGHT_WINDOWS, //101 + KEY_EN_ANSI_BACK_SLASH, //102 + KEY_EN_UP_ARROW, //103 + KEY_EN_NUMPAD_TIMES, //104 + KEY_EN_NUMPAD_2, //105 + KEY_EN_F9, //108 + KEY_EN_9, //109 + KEY_EN_O, //110 + KEY_EN_L, //111 + KEY_EN_COMMA, //112 + KEY_EN_MENU, //113 + KEY_EN_LEFT_ARROW, //115 + KEY_EN_NUMPAD_MINUS, //116 + KEY_EN_NUMPAD_3, //117 + KEY_EN_F10, //120 + KEY_EN_0, //121 + KEY_EN_P, //122 + KEY_EN_SEMICOLON, //123 + KEY_EN_PERIOD, //124 + KEY_EN_ANSI_ENTER, //126 + KEY_EN_DOWN_ARROW, //127 + KEY_EN_NUMPAD_PLUS, //128 + KEY_EN_NUMPAD_0, //129 + KEY_EN_F11, //132 + KEY_EN_MINUS, //133 + KEY_EN_LEFT_BRACKET, //134 + KEY_EN_QUOTE, //135 + KEY_EN_FORWARD_SLASH, //136 + "Key: Brightness", //137 + KEY_EN_RIGHT_ARROW, //139 + KEY_EN_NUMPAD_ENTER, //140 + KEY_EN_NUMPAD_PERIOD, //141 + "Key: / (ISO)", + KEY_EN_ISO_BACK_SLASH, + "Key: Macro G1", + "Key: Macro G2", + "Key: Macro G3", + "Key: Macro G4", + "Key: Macro G5", + "Key: Macro G6", + "Key: Macro G7", + "Key: Macro G8", + "Key: Macro G9", + "Key: Macro G10", + "Key: Macro G11", + "Key: Macro G12", + "Key: Macro G13", + "Key: Macro G14", + "Key: Macro G15", + "Key: Macro G16", + "Key: Macro G17", + "Key: Macro G18", + "Key: MR", + "Key: M1", + "Key: M2", + "Key: M3", +}; + +static const char* corsair_mouse_leds[] = +{ + "Mouse LED 1", + "Mouse LED 2", + "Mouse LED 3", + "Mouse LED 4", + "Mouse LED 5", + "Mouse LED 6", + "Mouse LED 7", + "Mouse LED 8", + "Mouse LED 9", + "Mouse LED 10", + "Mouse LED 11", + "Mouse LED 12", + "Mouse LED 13", + "Mouse LED 14", + "Mouse LED 15", +}; + +static const char* led_names_k55[] = +{ + "LEFT", + "MIDDLE", + "RIGHT", +}; + +static const char* corsair_m65_elite_leds[] = +{ + "", + "", + "Logo", + "DPI", + "Scroll Wheel", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", +}; + +static const char* corsair_sabre_rgb_leds[] = +{ + "", + "Underglow", + "Logo", + "DPI", + "Scroll Wheel", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" +}; + +static const char* corsair_harpoon_pro_leds[] = +{ + "", + "", + "", + "Logo", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", +}; + +/**------------------------------------------------------------------*\ + @name Corsair Peripheral + @category Keyboard,Mouse,Mousemat,HeadsetStand + @type USB + @save :x: + @direct :white_check_mark: + @effects :tools: + @detectors DetectCorsairPeripheralControllers + @comment + All controllers support `Direct` mode + Currently HW modes are implemented for the following devices: + * Corsair K70 RGB MK.2 + * Corsair K70 RGB MK.2 Low Profile +\*-------------------------------------------------------------------*/ + +RGBController_CorsairPeripheral::RGBController_CorsairPeripheral(CorsairPeripheralController* controller_ptr, bool supports_hardware_modes) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Corsair"; + description = "Corsair RGB Peripheral Device"; + type = controller->GetDeviceType(); + version = controller->GetFirmwareString(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + physical_layout = controller->GetPhysicalLayout(); + logical_layout = controller->GetLogicalLayout(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CORSAIR_MODE_DIRECT_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + if(supports_hardware_modes) + { + mode ColorPulse; + ColorPulse.name = "ColorPulse"; + ColorPulse.value = CORSAIR_HW_MODE_COLOR_PULSE_VALUE; + ColorPulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorPulse.color_mode = MODE_COLORS_RANDOM; + ColorPulse.speed = CORSAIR_HW_MODE_SPEED_MIN; + ColorPulse.speed_min = CORSAIR_HW_MODE_SPEED_MIN; + ColorPulse.speed_max = CORSAIR_HW_MODE_SPEED_MAX; + ColorPulse.brightness = CORSAIR_HW_MODE_BRIGHTNESS_MAX / 2; + ColorPulse.brightness_min = CORSAIR_HW_MODE_BRIGHTNESS_MIN; + ColorPulse.brightness_max = CORSAIR_HW_MODE_BRIGHTNESS_MAX; + ColorPulse.colors.resize(2); + modes.push_back(ColorPulse); + + mode ColorShift; + ColorShift.name = "ColorShift"; + ColorShift.value = CORSAIR_HW_MODE_COLOR_SHIFT_VALUE; + ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorShift.color_mode = MODE_COLORS_RANDOM; + ColorShift.speed = CORSAIR_HW_MODE_SPEED_MIN; + ColorShift.speed_min = CORSAIR_HW_MODE_SPEED_MIN; + ColorShift.speed_max = CORSAIR_HW_MODE_SPEED_MAX; + ColorShift.brightness = CORSAIR_HW_MODE_BRIGHTNESS_MAX / 2; + ColorShift.brightness_min = CORSAIR_HW_MODE_BRIGHTNESS_MIN; + ColorShift.brightness_max = CORSAIR_HW_MODE_BRIGHTNESS_MAX; + ColorShift.colors.resize(2); + modes.push_back(ColorShift); + + mode ColorWave; + ColorWave.name = "ColorWave"; + ColorWave.value = CORSAIR_HW_MODE_COLOR_WAVE_VALUE; + ColorWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorWave.color_mode = MODE_COLORS_RANDOM; + ColorWave.direction = MODE_DIRECTION_LEFT; + ColorWave.speed = CORSAIR_HW_MODE_SPEED_MIN; + ColorWave.speed_min = CORSAIR_HW_MODE_SPEED_MIN; + ColorWave.speed_max = CORSAIR_HW_MODE_SPEED_MAX; + ColorWave.brightness = CORSAIR_HW_MODE_BRIGHTNESS_MAX / 2; + ColorWave.brightness_min = CORSAIR_HW_MODE_BRIGHTNESS_MIN; + ColorWave.brightness_max = CORSAIR_HW_MODE_BRIGHTNESS_MAX; + ColorWave.colors.resize(2); + modes.push_back(ColorWave); + + mode RainbowWave; + RainbowWave.name = "RainbowWave"; + RainbowWave.value = CORSAIR_HW_MODE_RAINBOW_WAVE_VALUE; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.direction = MODE_DIRECTION_LEFT; + RainbowWave.speed = CORSAIR_HW_MODE_SPEED_MIN; + RainbowWave.speed_min = CORSAIR_HW_MODE_SPEED_MIN; + RainbowWave.speed_max = CORSAIR_HW_MODE_SPEED_MAX; + RainbowWave.brightness = CORSAIR_HW_MODE_BRIGHTNESS_MAX / 2; + RainbowWave.brightness_min = CORSAIR_HW_MODE_BRIGHTNESS_MIN; + RainbowWave.brightness_max = CORSAIR_HW_MODE_BRIGHTNESS_MAX; + modes.push_back(RainbowWave); + + mode Rain; + Rain.name = "Rain"; + Rain.value = CORSAIR_HW_MODE_RAIN_VALUE; + Rain.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Rain.color_mode = MODE_COLORS_RANDOM; + Rain.speed = CORSAIR_HW_MODE_SPEED_MIN; + Rain.speed_min = CORSAIR_HW_MODE_SPEED_MIN; + Rain.speed_max = CORSAIR_HW_MODE_SPEED_MAX; + Rain.brightness = CORSAIR_HW_MODE_BRIGHTNESS_MAX / 2; + Rain.brightness_min = CORSAIR_HW_MODE_BRIGHTNESS_MIN; + Rain.brightness_max = CORSAIR_HW_MODE_BRIGHTNESS_MAX; + Rain.colors.resize(2); + modes.push_back(Rain); + + mode Spiral; + Spiral.name = "Spiral"; + Spiral.value = CORSAIR_HW_MODE_SPIRAL_VALUE; + Spiral.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Spiral.color_mode = MODE_COLORS_NONE; + Spiral.speed = CORSAIR_HW_MODE_SPEED_MIN; + Spiral.speed_min = CORSAIR_HW_MODE_SPEED_MIN; + Spiral.speed_max = CORSAIR_HW_MODE_SPEED_MAX; + Spiral.brightness = CORSAIR_HW_MODE_BRIGHTNESS_MAX / 2; + Spiral.brightness_min = CORSAIR_HW_MODE_BRIGHTNESS_MIN; + Spiral.brightness_max = CORSAIR_HW_MODE_BRIGHTNESS_MAX; + Spiral.direction = MODE_DIRECTION_LEFT; + modes.push_back(Spiral); + + mode TypeKey; + TypeKey.name = "TypeKey"; + TypeKey.value = CORSAIR_HW_MODE_TYPE_KEY_VALUE; + TypeKey.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + TypeKey.color_mode = MODE_COLORS_RANDOM; + TypeKey.speed = CORSAIR_HW_MODE_SPEED_MIN; + TypeKey.speed_min = CORSAIR_HW_MODE_SPEED_MIN; + TypeKey.speed_max = CORSAIR_HW_MODE_SPEED_MAX; + TypeKey.brightness = CORSAIR_HW_MODE_BRIGHTNESS_MAX / 2; + TypeKey.brightness_min = CORSAIR_HW_MODE_BRIGHTNESS_MIN; + TypeKey.brightness_max = CORSAIR_HW_MODE_BRIGHTNESS_MAX; + TypeKey.colors.resize(2); + modes.push_back(TypeKey); + + mode TypeRipple; + TypeRipple.name = "TypeRipple"; + TypeRipple.value = CORSAIR_HW_MODE_TYPE_RIPPLE_VALUE; + TypeRipple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + TypeRipple.color_mode = MODE_COLORS_RANDOM; + TypeRipple.speed = CORSAIR_HW_MODE_SPEED_MIN; + TypeRipple.speed_min = CORSAIR_HW_MODE_SPEED_MIN; + TypeRipple.speed_max = CORSAIR_HW_MODE_SPEED_MAX; + TypeRipple.brightness = CORSAIR_HW_MODE_BRIGHTNESS_MAX / 2; + TypeRipple.brightness_min = CORSAIR_HW_MODE_BRIGHTNESS_MIN; + TypeRipple.brightness_max = CORSAIR_HW_MODE_BRIGHTNESS_MAX; + TypeRipple.colors.resize(2); + modes.push_back(TypeRipple); + + mode Visor; + Visor.name = "Visor"; + Visor.value = CORSAIR_HW_MODE_VISOR_VALUE; + Visor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Visor.color_mode = MODE_COLORS_RANDOM; + Visor.speed = CORSAIR_HW_MODE_SPEED_MIN; + Visor.speed_min = CORSAIR_HW_MODE_SPEED_MIN; + Visor.speed_max = CORSAIR_HW_MODE_SPEED_MAX; + Visor.brightness = CORSAIR_HW_MODE_BRIGHTNESS_MAX / 2; + Visor.brightness_min = CORSAIR_HW_MODE_BRIGHTNESS_MIN; + Visor.brightness_max = CORSAIR_HW_MODE_BRIGHTNESS_MAX; + Visor.colors.resize(2); + modes.push_back(Visor); + } + + + SetupZones(); +} + +RGBController_CorsairPeripheral::~RGBController_CorsairPeripheral() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_CorsairPeripheral::SetupZones() +{ + /*---------------------------------------------------------*\ + | Determine number of zones | + | For now, keyboard has 2 zones and mousemat has 1 | + \*---------------------------------------------------------*/ + unsigned int num_zones = 0; + + switch(type) + { + case DEVICE_TYPE_KEYBOARD: + if (logical_layout == CORSAIR_TYPE_K95_PLAT) + { + num_zones = 2; + break; + } + if (logical_layout == CORSAIR_TYPE_K55) + { + num_zones = 3; + break; + } + num_zones = 1; + break; + + case DEVICE_TYPE_MOUSE: + case DEVICE_TYPE_MOUSEMAT: + num_zones = 1; + break; + + case DEVICE_TYPE_HEADSET_STAND: + num_zones = 2; + break; + } + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < num_zones; zone_idx++) + { + zone new_zone; + switch(type) + { + case DEVICE_TYPE_KEYBOARD: + if (logical_layout == CORSAIR_TYPE_K95_PLAT) + { + new_zone.name = zone_names_k95_platinum[zone_idx]; + new_zone.type = zone_types_k95_platinum[zone_idx]; + new_zone.leds_min = zone_sizes_k95_platinum[zone_idx]; + new_zone.leds_max = zone_sizes_k95_platinum[zone_idx]; + new_zone.leds_count = zone_sizes_k95_platinum[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 7; + new_zone.matrix_map->width = 24; + new_zone.matrix_map->map = (unsigned int *)&matrix_map_k95_platinum; + } + else + { + new_zone.matrix_map = NULL; + } + } + else if (logical_layout == CORSAIR_TYPE_K95) + { + new_zone.name = zone_names_k95[zone_idx]; + new_zone.type = zone_types_k95[zone_idx]; + new_zone.leds_min = zone_sizes_k95[zone_idx]; + new_zone.leds_max = zone_sizes_k95[zone_idx]; + new_zone.leds_count = zone_sizes_k95[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 7; + new_zone.matrix_map->width = 26; + new_zone.matrix_map->map = (unsigned int *)&matrix_map_k95; + } + else + { + new_zone.matrix_map = NULL; + } + } + else if (logical_layout == CORSAIR_TYPE_K55) + { + new_zone.name = zone_names_k55[zone_idx]; + new_zone.type = zone_types_k55[zone_idx]; + new_zone.leds_min = zone_sizes_k55[zone_idx]; + new_zone.leds_max = zone_sizes_k55[zone_idx]; + new_zone.leds_count = zone_sizes_k55[zone_idx]; + new_zone.matrix_map = NULL; + } + else if (logical_layout == CORSAIR_TYPE_K70_MK2) + { + new_zone.name = zone_names_k70_mk2[zone_idx]; + new_zone.type = zone_types_k70_mk2[zone_idx]; + new_zone.leds_min = zone_sizes_k70_mk2[zone_idx]; + new_zone.leds_max = zone_sizes_k70_mk2[zone_idx]; + new_zone.leds_count = zone_sizes_k70_mk2[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 7; + new_zone.matrix_map->width = 23; + new_zone.matrix_map->map = (unsigned int *)&matrix_map_k70_mk2; + } + else + { + new_zone.matrix_map = NULL; + } + } + else //default layout + { + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 23; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + } + break; + + + case DEVICE_TYPE_MOUSE: + new_zone.name = "Mouse Zone"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 15; + new_zone.leds_max = 15; + new_zone.leds_count = 15; + new_zone.matrix_map = NULL; + break; + + case DEVICE_TYPE_MOUSEMAT: + new_zone.name = "Mousemat Zone"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 15; + new_zone.leds_max = 15; + new_zone.leds_count = 15; + new_zone.matrix_map = NULL; + break; + + case DEVICE_TYPE_HEADSET_STAND: + if(zone_idx == 0) + { + new_zone.name = "Base LED Strip"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 8; + new_zone.leds_max = 8; + new_zone.leds_count = 8; + new_zone.matrix_map = NULL; + } + else + { + new_zone.name = "Logo"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + } + break; + } + + zones.push_back(new_zone); + + total_led_count += new_zone.leds_count; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + + switch(type) + { + case DEVICE_TYPE_KEYBOARD: + if(logical_layout == CORSAIR_TYPE_K95_PLAT) + { + new_led.name = led_names_k95_plat[led_idx]; + } + else if(logical_layout == CORSAIR_TYPE_K95) + { + new_led.name = led_names_k95[led_idx]; + } + else if(logical_layout == CORSAIR_TYPE_K55) + { + new_led.name = led_names_k55[led_idx]; + } + else if(logical_layout == CORSAIR_TYPE_K70_MK2) + { + new_led.name = led_names_k70_mk2[led_idx]; + } + else + { + new_led.name = led_names[led_idx]; + } + break; + + case DEVICE_TYPE_MOUSE: + if(name == "Corsair M65 RGB Elite") + { + new_led.name = corsair_m65_elite_leds[led_idx]; + } + else if(name == "Corsair Harpoon RGB PRO") + { + new_led.name = corsair_harpoon_pro_leds[led_idx]; + } + if(name == "Corsair Ironclaw RGB") + { + new_led.name = corsair_m65_elite_leds[led_idx]; + } + else if(name == "Corsair Sabre RGB") + { + new_led.name = corsair_sabre_rgb_leds[led_idx]; + } + else + { + new_led.name = corsair_mouse_leds[led_idx]; + } + break; + + case DEVICE_TYPE_MOUSEMAT: + case DEVICE_TYPE_HEADSET_STAND: + new_led.name = "Mousemat LED "; + new_led.name.append(std::to_string(led_idx + 1)); + break; + } + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_CorsairPeripheral::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CorsairPeripheral::DeviceUpdateLEDs() +{ + controller->SetLEDs(colors); +} + +void RGBController_CorsairPeripheral::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_CorsairPeripheral::UpdateSingleLED(int /*led*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_CorsairPeripheral::DeviceUpdateMode() +{ + + if(modes[active_mode].value == CORSAIR_MODE_DIRECT_VALUE) + { + controller->SwitchMode(true); + } + else + { + const mode& active = modes[active_mode]; + + unsigned int direction = active.direction; + + if(active.flags & MODE_FLAG_HAS_DIRECTION_LR || active.flags & MODE_FLAG_HAS_DIRECTION_UD) + { + direction += 1; + + if(active.value == CORSAIR_HW_MODE_SPIRAL_VALUE) + { + direction += 4; + } + } + + controller->SetHardwareMode(active.value, active.color_mode, active.colors, active.speed, direction, active.brightness); + + controller->SwitchMode(false); + } + +} diff --git a/Controllers/CorsairPeripheralController/RGBController_CorsairPeripheral.h b/Controllers/CorsairPeripheralController/RGBController_CorsairPeripheral.h new file mode 100644 index 0000000..cc0cbe0 --- /dev/null +++ b/Controllers/CorsairPeripheralController/RGBController_CorsairPeripheral.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairPeripheral.h | +| | +| RGBController for Corsair peripherals | +| | +| Adam Honse (CalcProgrammer1) 09 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairPeripheralController.h" + +class RGBController_CorsairPeripheral : public RGBController +{ +public: + RGBController_CorsairPeripheral(CorsairPeripheralController* controller_ptr, bool supports_hardware_modes); + ~RGBController_CorsairPeripheral(); + + int physical_layout; + int logical_layout; + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CorsairPeripheralController* controller; +}; diff --git a/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Controller.cpp b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Controller.cpp new file mode 100644 index 0000000..1d996cf --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Controller.cpp @@ -0,0 +1,423 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralV2Controller.cpp | +| | +| Driver for Corsair V2 peripherals | +| | +| Chris M (Dr_No) 07 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CorsairPeripheralV2Controller.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +CorsairPeripheralV2Controller::CorsairPeripheralV2Controller(hid_device* dev_handle, const char* path, std::string name) +{ + dev = dev_handle; + location = path; + device_name = name; + + /*---------------------------------------------------------*\ + | Get PID | + | If the PID is in the know wireless receivers list | + | switch the write_cmd to talk to the device and retry | + \*---------------------------------------------------------*/ + unsigned int pid = GetAddress(0x12); + + switch(pid) + { + case CORSAIR_SLIPSTREAM_WIRELESS_PID1: + case CORSAIR_SLIPSTREAM_WIRELESS_V2_PID1: + case CORSAIR_SLIPSTREAM_WIRELESS_PID2: + write_cmd = CORSAIR_V2_WRITE_WIRELESS_ID; + pid = GetAddress(0x12); + break; + + case CORSAIR_K57_RGB_WIRED_PID: + write_cmd = 0x80; + light_ctrl = CORSAIR_V2_LIGHT_CTRL1; + skip_reads = true; + break; + } + + /*---------------------------------------------------------*\ + | If the hid_pid passed in from the detector does not match | + | the pid reported by the device then it is likey | + | behind a wireless receiver. | + \*---------------------------------------------------------*/ + LOG_DEBUG("[%s] Setting write CMD to %02X for %s mode for PID %04X", device_name.c_str(), + write_cmd, (write_cmd == CORSAIR_V2_WRITE_WIRELESS_ID) ? "wireless" : "wired", pid); + + /*---------------------------------------------------------*\ + | Get VID | + | NB: this can be achieved with GetAddress(0x11) but we | + | also need to set the packet length capabilities for | + | the device being set up. | + \*---------------------------------------------------------*/ + uint8_t buffer[CORSAIR_V2_PACKET_SIZE]; + buffer[1] = write_cmd; + buffer[2] = CORSAIR_V2_CMD_GET; + buffer[3] = 0x11; + hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE); + uint16_t result = hid_read_timeout(dev, buffer, CORSAIR_V2_PACKET_SIZE, CORSAIR_V2_TIMEOUT); + result++; + pkt_sze = std::max(result, (uint16_t)CORSAIR_V2_WRITE_SIZE); + LOG_DEBUG("[%s] Packet length set to %d", device_name.c_str(), pkt_sze); + + /*---------------------------------------------------------*\ + | NB: If the device is not found in the device list | + | then wireless mode may not work reliably | + \*---------------------------------------------------------*/ + bool not_found = true; + + for(uint16_t i = 0; i < CORSAIR_V2_DEVICE_COUNT; i++) + { + LOG_DEBUG("[%s] Checking PID %04X against index %d with %04X - %smatch", device_name.c_str(), + pid, i, corsair_v2_device_list[i]->pid, corsair_v2_device_list[i]->pid == pid ? "" : "no "); + if(corsair_v2_device_list[i]->pid == pid) + { + /*---------------------------------------------------------*\ + | Set device ID | + \*---------------------------------------------------------*/ + not_found = false; + device_index = i; + break; + } + } + + if(not_found) + { + LOG_ERROR("[%s] device capabilities not found. Please creata a new device request.", + device_name.c_str()); + } + + /*---------------------------------------------------------*\ + | Check lighting control endpoints | + | If lighting control endpoint 2 is unavailable | + | then use endpoint 1. | + \*---------------------------------------------------------*/ + if(light_ctrl == CORSAIR_V2_LIGHT_CTRL2) + { + result = StartTransaction(0); + if(result > 0) + { + light_ctrl = CORSAIR_V2_LIGHT_CTRL1; + StartTransaction(0); + } + StopTransaction(0); + LOG_DEBUG("[%s] Lighting Endpoint set to %02X", device_name.c_str(), light_ctrl); + } +} + +CorsairPeripheralV2Controller::~CorsairPeripheralV2Controller() +{ + hid_close(dev); +} + +const corsair_v2_device* CorsairPeripheralV2Controller::GetDeviceData() +{ + return corsair_v2_device_list[device_index]; +} + +std::string CorsairPeripheralV2Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string CorsairPeripheralV2Controller::GetErrorString(uint8_t err) +{ + switch(err) + { + case 1: + return "Invalid Value"; + case 3: + return "Failed"; + case 5: + return "Unsupported"; + default: + return "Protocol Error (Unknown)"; + } +} + +std::string CorsairPeripheralV2Controller::GetFirmwareString() +{ + return ""; +} + +std::string CorsairPeripheralV2Controller::GetName() +{ + return device_name; +} + +std::string CorsairPeripheralV2Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void CorsairPeripheralV2Controller::SetRenderMode(corsair_v2_device_mode mode) +{ + uint8_t buffer[CORSAIR_V2_WRITE_SIZE]; + + memset(buffer, 0, CORSAIR_V2_WRITE_SIZE); + + /*---------------------------------------------------------*\ + | Set Mode | + \*---------------------------------------------------------*/ + buffer[1] = write_cmd; + buffer[2] = CORSAIR_V2_CMD_SET; + buffer[3] = CORSAIR_V2_VALUE_MODE; + buffer[5] = mode; + + hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE); + + if(!skip_reads) + { + hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT); + } +} + +void CorsairPeripheralV2Controller::LightingControl(uint8_t opt1) +{ + uint8_t buffer[CORSAIR_V2_WRITE_SIZE]; + + memset(buffer, 0, CORSAIR_V2_WRITE_SIZE); + + /*---------------------------------------------------------*\ + | The Corsair command is the same for each initialisation | + | packet and the registers and options differ for | + | each peripheral supported by the protocol | + \*---------------------------------------------------------*/ + buffer[1] = write_cmd; + buffer[2] = CORSAIR_V2_CMD_GET; + buffer[3] = opt1; + buffer[5] = 0x00; + + hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE); + + if(!skip_reads) + { + hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT); + } +} + +unsigned int CorsairPeripheralV2Controller::GetKeyboardLayout() +{ + return GetAddress(0x41); +} + +unsigned int CorsairPeripheralV2Controller::GetAddress(uint8_t address) +{ + uint8_t buffer[CORSAIR_V2_WRITE_SIZE]; + uint8_t read[CORSAIR_V2_WRITE_SIZE]; + + memset(buffer, 0, CORSAIR_V2_WRITE_SIZE); + memset(read, 0, CORSAIR_V2_WRITE_SIZE); + + buffer[1] = write_cmd; + buffer[2] = CORSAIR_V2_CMD_GET; + buffer[3] = address; + + hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE); + hid_read_timeout(dev, read, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT); + + unsigned int temp = (unsigned int)(read[6] << 24 | read[5] << 16 | read[4] << 8 | read[3]); + LOG_DEBUG("[%s] GetAddress %02X - %02X %02X - %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), + address, read[0], read[1], read[2], read[3], read[4], read[5], read[6], read[7], read[8], read[9]); + + uint8_t result = read[2]; + if(result > 0) + { + LOG_DEBUG("[%s] An error occurred! Get Address %02X failed - %d %s", device_name.c_str(), + address, result, GetErrorString(result).c_str()); + return -1; + } + return temp; +} + +unsigned char CorsairPeripheralV2Controller::StartTransaction(uint8_t opt1) +{ + uint8_t buffer[CORSAIR_V2_WRITE_SIZE]; + + memset(buffer, 0, CORSAIR_V2_WRITE_SIZE); + + buffer[1] = write_cmd; + buffer[2] = CORSAIR_V2_CMD_START_TX; + buffer[3] = opt1; + buffer[4] = light_ctrl; + + hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE); + + if(!skip_reads) + { + hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT); + } + + return buffer[2]; +} + +void CorsairPeripheralV2Controller::StopTransaction(uint8_t opt1) +{ + uint8_t buffer[CORSAIR_V2_WRITE_SIZE]; + + memset(buffer, 0, CORSAIR_V2_WRITE_SIZE); + + buffer[1] = write_cmd; + buffer[2] = CORSAIR_V2_CMD_STOP_TX; + buffer[3] = 0x01; + buffer[4] = opt1; + + hid_write(dev, buffer, CORSAIR_V2_WRITE_SIZE); + + if(!skip_reads) + { + hid_read_timeout(dev, buffer, CORSAIR_V2_WRITE_SIZE, CORSAIR_V2_TIMEOUT); + } +} + +void CorsairPeripheralV2Controller::ClearPacketBuffer() +{ + if(skip_reads) + { + return; + } + + uint8_t result = 0; + uint8_t buffer[CORSAIR_V2_PACKET_SIZE]; + + do + { + result = hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT); + } + while(result > 0); +} + +void CorsairPeripheralV2Controller::SetLEDs(uint8_t *data, uint16_t data_size) +{ + const uint8_t offset1 = 8; + const uint8_t offset2 = 4; + uint16_t remaining = data_size; + + uint8_t buffer[CORSAIR_V2_PACKET_SIZE]; + memset(buffer, 0, CORSAIR_V2_PACKET_SIZE); + + ClearPacketBuffer(); + StartTransaction(0); + /*---------------------------------------------------------*\ + | Set the data header in packet 1 with the data length | + | signaling how many packets to expect to the device | + \*---------------------------------------------------------*/ + buffer[1] = write_cmd; + buffer[2] = CORSAIR_V2_CMD_BLK_W1; + buffer[4] = data_size & 0xFF; + buffer[5] = data_size >> 8; + + /*---------------------------------------------------------*\ + | Check if the data needs more than 1 packet | + \*---------------------------------------------------------*/ + uint16_t copy_bytes = pkt_sze - offset1; + if(remaining < copy_bytes) + { + copy_bytes = remaining; + } + + memcpy(&buffer[offset1], &data[0], copy_bytes); + + hid_write(dev, buffer, pkt_sze); + + if(!skip_reads) + { + hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT); + } + + remaining -= copy_bytes; + buffer[2] = CORSAIR_V2_CMD_BLK_WN; + copy_bytes = pkt_sze - offset2; + + /*---------------------------------------------------------*\ + | Send the remaining packets | + \*---------------------------------------------------------*/ + while(remaining) + { + uint16_t index = data_size - remaining; + if(remaining < copy_bytes) + { + memset(&buffer[offset2], 0, copy_bytes); + copy_bytes = remaining; + } + + memcpy(&buffer[offset2], &data[index], copy_bytes); + + hid_write(dev, buffer, pkt_sze); + + if(!skip_reads) + { + hid_read_timeout(dev, buffer, pkt_sze, CORSAIR_V2_TIMEOUT_SHORT); + } + + remaining -= copy_bytes; + } + + StopTransaction(0); +} + +void CorsairPeripheralV2Controller::UpdateHWMode(uint16_t mode, corsair_v2_color /*color_mode*/, uint8_t /*speed*/, + uint8_t /*direction*/, uint8_t /*brightness*/, std::vector /*colors*/) +{ + /*---------------------------------------------------------*\ + | If we are switching to `Direct` mode | + | set device in software mode | + \*---------------------------------------------------------*/ + if(mode == CORSAIR_V2_MODE_DIRECT) + { + SetRenderMode(CORSAIR_V2_MODE_SW); + return; + } + + /* + SetRenderMode(CORSAIR_V2_MODE_HW); + + uint8_t buffer[CORSAIR_V2_WRITE_SIZE]; + memset(buffer, 0, CORSAIR_V2_WRITE_SIZE); + */ + + /*---------------------------------------------------------*\ + | Set the data header in packet 1 with the data length | + | signaling how many packets to expect to the device | + \*---------------------------------------------------------*/ + + /* + buffer[1] = write_cmd; + buffer[2] = CORSAIR_V2_CMD_BLK_W1; + buffer[3] = CORSAIR_V2_MODE_HW; + buffer[4] = 0x30; + buffer[8] = mode & 0xFF; + buffer[9] = mode >> 8; + + buffer[10] = CORSAIR_V2_COLOR_SPECIFIC; + buffer[11] = speed; + + buffer[14] = colors.size(); + + for(size_t i = 0; i < colors.size(); ++i) + { + uint8_t offset = 15 + (i * 4); + + buffer[offset] = brightness; + buffer[offset + 1] = RGBGetBValue(colors[i]); + buffer[offset + 2] = RGBGetGValue(colors[i]); + buffer[offset + 3] = RGBGetRValue(colors[i]); + } + */ + +} diff --git a/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Controller.h b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Controller.h new file mode 100644 index 0000000..6471368 --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Controller.h @@ -0,0 +1,115 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralV2Controller.h | +| | +| Driver for Corsair V2 peripherals | +| | +| Chris M (Dr_No) 07 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "LogManager.h" +#include "RGBController.h" +#include "CorsairPeripheralV2Devices.h" + +#define NA 0xFFFFFFFF +#define HID_MAX_STR 255 + +#define CORSAIR_V2_TIMEOUT 50 +#define CORSAIR_V2_TIMEOUT_SHORT 3 +#define CORSAIR_V2_VALUE_MODE 3 +#define CORSAIR_V2_WRITE_WIRED_ID 8 +#define CORSAIR_V2_WRITE_WIRELESS_ID 9 +#define CORSAIR_V2_WRITE_SIZE 65 +#define CORSAIR_V2_PACKET_SIZE 1024 + +#define CORSAIR_V2_LIGHT_CTRL1 1 +#define CORSAIR_V2_LIGHT_CTRL2 34 /* 0x22 */ +#define CORSAIR_V2_UPDATE_PERIOD 30000 +#define CORSAIR_V2_SLEEP_PERIOD 12500ms + +#define CORSAIR_V2_BRIGHTNESS_MIN 0 +#define CORSAIR_V2_BRIGHTNESS_MAX 0xFF + +enum corsair_v2_cmd +{ + CORSAIR_V2_CMD_SET = 0x01, /* Command for setting values */ + CORSAIR_V2_CMD_GET = 0x02, /* Command for getting values */ + CORSAIR_V2_CMD_STOP_TX = 0x05, /* Finish Transaction */ + CORSAIR_V2_CMD_BLK_W1 = 0x06, /* Block write packet 1 */ + CORSAIR_V2_CMD_BLK_WN = 0x07, /* Block write remaining packets */ + CORSAIR_V2_CMD_START_TX = 0x0D, /* Start Transaction */ +}; + +enum corsair_v2_mode +{ + CORSAIR_V2_MODE_DIRECT = 0x0012, + CORSAIR_V2_MODE_STATIC = 0x207E, + CORSAIR_V2_MODE_FLASHING = 0xAD4F, + CORSAIR_V2_MODE_BREATHING = 0xA5FA, + CORSAIR_V2_MODE_SPECTRUM = 0x7BFF, + CORSAIR_V2_MODE_RAINBOW = 0xB94C, + CORSAIR_V2_MODE_RAIN = 0xA07E, + CORSAIR_V2_MODE_SPIRAL = 0xAB87, + CORSAIR_V2_MODE_WATERCOLOR = 0x0022, + CORSAIR_V2_MODE_REACTIVE = 0xB1F9, + CORSAIR_V2_MODE_RIPPLE = 0x09A2, + CORSAIR_V2_MODE_VISOR = 0x90C0 +}; + +enum corsair_v2_color +{ + CORSAIR_V2_COLOR_NONE = 0x00, + CORSAIR_V2_COLOR_SPECIFIC = 0x01, + CORSAIR_V2_COLOR_RANDOM = 0x02, + CORSAIR_V2_COLOR_UNKNOWN = 0x03 +}; + + +class CorsairPeripheralV2Controller +{ +public: + CorsairPeripheralV2Controller(hid_device* dev_handle, const char* path, std::string name); + virtual ~CorsairPeripheralV2Controller(); + + std::string GetDeviceLocation(); + std::string GetErrorString(uint8_t err); + std::string GetFirmwareString(); + std::string GetName(); + std::string GetSerialString(); + const corsair_v2_device* GetDeviceData(); + unsigned int GetKeyboardLayout(); + + void SetRenderMode(corsair_v2_device_mode mode); + void LightingControl(uint8_t opt1); + void SetLEDs(uint8_t *data, uint16_t data_size); + void UpdateHWMode(uint16_t mode, corsair_v2_color color_mode, uint8_t speed, + uint8_t direction, uint8_t brightness, std::vector colors); + + virtual void SetLedsDirect(std::vector colors) = 0; + +protected: + uint16_t device_index; + std::string device_name; + uint8_t light_ctrl = CORSAIR_V2_LIGHT_CTRL2; + +private: + void ClearPacketBuffer(); + unsigned int GetAddress(uint8_t address); + unsigned char StartTransaction(uint8_t opt1); + void StopTransaction(uint8_t opt1); + + hid_device* dev; + + uint8_t write_cmd = CORSAIR_V2_WRITE_WIRED_ID; + uint16_t pkt_sze = CORSAIR_V2_WRITE_SIZE; + bool skip_reads = false; + std::string firmware_version; + std::string location; +}; diff --git a/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2ControllerDetect.cpp b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2ControllerDetect.cpp new file mode 100644 index 0000000..9e0a5fd --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2ControllerDetect.cpp @@ -0,0 +1,100 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralV2ControllerDetect.cpp | +| | +| Detector for Corsair V2 peripherals | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +/*-----------------------------------------------------*\ +| OpenRGB includes | +\*-----------------------------------------------------*/ +#include +#include "Detector.h" + +/*-----------------------------------------------------*\ +| Corsair Peripheral specific includes | +\*-----------------------------------------------------*/ +#include "CorsairPeripheralV2Devices.h" +#include "RGBController_CorsairV2Hardware.h" +#include "RGBController_CorsairV2Software.h" + +#define CORSAIR_PERIPHERAL_CONTROLLER_NAME "Corsair V2 Peripheral" + +/*-----------------------------------------------------*\ +| Corsair vendor ID | +\*-----------------------------------------------------*/ +#define CORSAIR_VID 0x1B1C + +void DetectCorsairV2HardwareControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CorsairPeripheralV2HWController* controller = new CorsairPeripheralV2HWController(dev, info->path, name); + RGBController_CorsairV2HW* rgb_controller = new RGBController_CorsairV2HW(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectCorsairV2HardwareControllers() */ + +void DetectCorsairV2SoftwareControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CorsairPeripheralV2SWController* controller = new CorsairPeripheralV2SWController(dev, info->path, name); + RGBController_CorsairV2SW* rgb_controller = new RGBController_CorsairV2SW(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectCorsairV2SoftwareControllers() */ + +/*-----------------------------------------------------------------------------------------------------*\ +| Keyboards | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("Corsair K55 RGB PRO", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K55_RGB_PRO_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K57 RGB (Wired)", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K57_RGB_WIRED_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO Low Profile", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_LP_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO TKL Black", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_TKL_B_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K60 RGB PRO TKL White", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K60_RGB_PRO_TKL_W_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K70 Core RGB", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K70_CORE_RGB_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K70 Core RGB TKL", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K70_CORE_RGB_TKL_PID, 1, 0xFF42); + +REGISTER_HID_DETECTOR_IP("Corsair K70 RGB PRO", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K70_RGB_PRO_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K70 RGB PRO V2", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K70_RGB_PRO_V2_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K70 RGB TKL", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K70_RGB_TKL_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K70 RGB TKL Champion Series", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K70_RGB_TKL_CS_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K95 RGB PLATINUM XT", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K95_PLATINUM_XT_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K100 RGB Optical", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K100_OPTICAL_V1_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K100 RGB Optical", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K100_OPTICAL_V2_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair K100 MX Red", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_K100_MXRED_PID, 1, 0xFF42); + +/*-----------------------------------------------------------------------------------------------------*\ +| Mice | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("Corsair Dark Core RGB SE (Wired)", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_DARK_CORE_RGB_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair Dark Core RGB Pro SE (Wired)", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_DARK_CORE_RGB_PRO_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair Harpoon Wireless (Wired)", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_HARPOON_WIRELESS_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair Ironclaw Wireless (Wired)", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_IRONCLAW_WIRELESS_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair Katar Pro", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_KATAR_PRO_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair Katar Pro V2", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_KATAR_PRO_V2_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair Katar Pro XT", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_KATAR_PRO_XT_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair M55 RGB PRO", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_M55_RGB_PRO_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair M65 RGB Ultra Wired", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_M65_RGB_ULTRA_WIRED_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair M65 RGB Ultra Wireless (Wired)", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_M65_RGB_ULTRA_WIRELESS_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair M75 Gaming Mouse", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_M75_GAMING_MOUSE_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair Slipstream Wireless Receiver HW", DetectCorsairV2HardwareControllers, CORSAIR_VID, CORSAIR_SLIPSTREAM_WIRELESS_PID1, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair Slipstream Wireless Receiver SW", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_SLIPSTREAM_WIRELESS_PID2, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair Slipstream Wireless Receiver HW", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_SLIPSTREAM_WIRELESS_V2_PID1, 1, 0xFF42); + + +/*-----------------------------------------------------------------------------------------------------*\ +| Mousemat | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("Corsair MM700", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_MM700_PID, 1, 0xFF42); +REGISTER_HID_DETECTOR_IP("Corsair MM700 3XL", DetectCorsairV2SoftwareControllers, CORSAIR_VID, CORSAIR_MM700_3XL_PID, 1, 0xFF42); diff --git a/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Devices.cpp b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Devices.cpp new file mode 100644 index 0000000..beecf86 --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Devices.cpp @@ -0,0 +1,1562 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralV2Devices.cpp | +| | +| Device list for Corsair V2 peripherals | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CorsairPeripheralV2Devices.h" + +/*-------------------------------------------------------------------------*\ +| Corsair Key Values | +\*-------------------------------------------------------------------------*/ + +std::vector corsair_tkl_values = +{ + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 41, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC INS HOME PGUP */ + 53, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 45, 46, 42, 73, 74, 75, + /* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN */ + 43, 20, 26, 8, 21, 23, 28, 24, 12, 18, 19, 47, 48, 49, 76, 77, 78, + /* CPLK A S D F G H J K L ; " # ENTR */ + 57, 4, 22, 7, 9, 10, 11, 13, 14, 15, 51, 52, 50, 40, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU */ + 106, 100, 29, 27, 6, 25, 5, 17, 16, 54, 55, 56, 110, 82, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWR ARWD ARWR */ + 105, 108, 107, 44, 111, 122, 101, 109, 80, 81, 79, +}; + +std::vector corsair_full_size_values = +{ + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 41, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC INS HOME PGUP NMLK NMDV NMTM NMMI */ + 53, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 45, 46, 42, 73, 74, 75, 83, 84, 85, 86, + /* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NM7 NM8 NM9 NMPL */ + 43, 20, 26, 8, 21, 23, 28, 24, 12, 18, 19, 47, 48, 49, 76, 77, 78, 95, 96, 97, 87, + /* CPLK A S D F G H J K L ; " # ENTR NM4 NM5 NM6 */ + 57, 4, 22, 7, 9, 10, 11, 13, 14, 15, 51, 52, 50, 40, 92, 93, 94, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 106, 100, 29, 27, 6, 25, 5, 17, 16, 54, 55, 56, 110, 82, 89, 90, 91, 88, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWR ARWD ARWR NM0 NMPD */ + 105, 108, 107, 44, 111, 122, 101, 109, 80, 81, 79, 98, 99, +}; + +/*-------------------------------------------------------------------------*\ +| KEYMAPS | +\*-------------------------------------------------------------------------*/ +keyboard_keymap_overlay_values corsair_K57_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + corsair_full_size_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 17, 0, "Power/Wireless Indicator", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, // Insert Profile into new row + { 0, 0, 18, 1, "Lock/Macro Indicator", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Light key + { 0, 1, 0, 131, "Key: G1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G1 key + { 0, 2, 0, 132, "Key: G2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G2 key + { 0, 3, 0, 133, "Key: G3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G3 key + { 0, 4, 0, 134, "Key: G4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G4 key + { 0, 5, 0, 135, "Key: G5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G5 key + { 0, 6, 0, 136, "Key: G6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert G6 key + } +}; + +keyboard_keymap_overlay_values corsair_K60_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + corsair_full_size_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + } +}; + +keyboard_keymap_overlay_values corsair_K60_tkl_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + corsair_tkl_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + } +}; + +keyboard_keymap_overlay_values corsair_k70_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + corsair_full_size_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + } +}; + +keyboard_keymap_overlay_values corsair_k70_pro_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + corsair_full_size_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 128, "Profile", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, // Insert Profile into new row + { 0, 0, 1, 113, "Light", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Light key + { 0, 0, 2, 114, "Lock", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lock Key + { 0, 0, 10, 138, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Logo + { 0, 0, 18, 102, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Mute Key + { 0, 1, 17, 123, KEY_EN_MEDIA_STOP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Stop Key + { 0, 1, 18, 126, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Previous Track Key + { 0, 1, 19, 124, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Play Pause Key + { 0, 1, 20, 125, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Next Tack Key + { 0, 6, 5, 140, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert spacebar L + { 0, 6, 7, 141, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert spacebar R + } +}; + +keyboard_keymap_overlay_values corsair_K70_TKL_cs_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + corsair_tkl_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 1, 123, KEY_EN_MEDIA_STOP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, // Insert Stop Key into new media keys row + { 0, 0, 2, 126, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Previous Track Key + { 0, 0, 3, 124, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Play Pause Key + { 0, 0, 4, 125, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Next Tack Key + { 0, 0, 7, 1, "Logo L", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert 'Logo Left' + { 0, 0, 8, 3, "Logo R", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert 'Logo Right' + { 0, 0, 11, 128, "Profile", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Profile + { 0, 0, 12, 113, "Light", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Light key + { 0, 0, 13, 114, "Lock", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Lock Key + { 0, 0, 14, 102, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Mute Key + } +}; + + +keyboard_keymap_overlay_values corsair_K70_CORE_TKL_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + corsair_tkl_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 14, 70, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap PRSC with Mute + { 0, 0, 15, 71, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove SCLK + { 0, 0, 16, 72, KEY_EN_MEDIA_VOLUME_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap PSBK with Volume Potion Up + + } +}; + +keyboard_keymap_overlay_values corsair_k95_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + corsair_full_size_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + //swap right fn with right windows + { 0, 5, 11, 112, KEY_EN_RIGHT_WINDOWS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + //media keys + { 0, 0, 17, 123, KEY_EN_MEDIA_STOP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 126, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 124, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 125, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //upper row + { 0, 0, 4, 128, "Profile", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 0, 5, 113, "Light", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 6, 114, "Lock", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 102, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //macro keys + { 0, 1, 0, 131, "G1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 132, "G2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 133, "G3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 134, "G4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 135, "G5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 0, 136, "G6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //top bar + { 0, 0, 0, 137, "Top Bar 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 0, 1, 138, "Top Bar 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 2, 139, "Top Bar 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 3, 140, "Top Bar 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 4, 141, "Top Bar 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 5, 142, "Top Bar 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 6, 143, "Top Bar 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 7, 144, "Top Bar 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 8, 145, "Top Bar 9", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 9, 146, "Top Bar 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 10, 147, "Top Bar 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 11, 148, "Top Bar 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 12, 149, "Top Bar 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 13, 150, "Top Bar 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 14, 151, "Top Bar 15", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 15, 152, "Top Bar 16", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 16, 153, "Top Bar 17", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 17, 154, "Top Bar 18", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 155, "Top Bar 19", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +keyboard_keymap_overlay_values corsair_k100_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + corsair_full_size_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + //media keys + { 0, 0, 17, 123, KEY_EN_MEDIA_STOP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 126, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 124, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 125, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //upper row + { 0, 0, 1, 128, "Profile", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 0, 2, 137, "iCue", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 3, 114, "Lock", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 10, 190, "Logo L", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 11, 191, "Logo M", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 12, 192, "Logo R", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 102, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //macro keys + { 0, 1, 0, 131, "G1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 132, "G2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 133, "G3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 134, "G4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 135, "G5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 0, 136, "G6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //underglow 44 entries + //underglow upper + { 0, 0, 0, 138, "Underglow 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 0, 1, 139, "Underglow 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 2, 140, "Underglow 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 3, 141, "Underglow 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 4, 142, "Underglow 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 5, 143, "Underglow 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 6, 144, "Underglow 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 7, 145, "Underglow 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 8, 146, "Underglow 9", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 9, 147, "Underglow 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 10, 148, "Underglow 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 11, 149, "Underglow 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 12, 150, "Underglow 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 13, 151, "Underglow 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 14, 152, "Underglow 15", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 15, 153, "Underglow 16", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 16, 154, "Underglow 17", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 17, 155, "Underglow 18", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 156, "Underglow 19", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 157, "Underglow 20", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 158, "Underglow 21", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 21, 159, "Underglow 22", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //underglow left + { 0, 0, 0, 160, "Underglow 23", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 161, "Underglow 24", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 162, "Underglow 25", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 163, "Underglow 26", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 164, "Underglow 27", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 165, "Underglow 28", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 0, 166, "Underglow 29", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 7, 0, 167, "Underglow 30", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 8, 0, 168, "Underglow 31", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 9, 0, 169, "Underglow 32", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 10, 0, 170, "Underglow 33", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //underglow right + { 0, 0, 23, 171, "Underglow 34", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 23, 172, "Underglow 35", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 23, 173, "Underglow 36", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 23, 174, "Underglow 37", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 23, 175, "Underglow 38", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 23, 176, "Underglow 39", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 23, 177, "Underglow 40", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 7, 23, 178, "Underglow 41", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 8, 23, 179, "Underglow 42", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 9, 23, 180, "Underglow 43", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 10, 23, 181, "Underglow 44", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //wheel + { 0, 0, 0, 182, "Wheel 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 0, 1, 183, "Wheel 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 2, 184, "Wheel 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 3, 185, "Wheel 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 4, 186, "Wheel 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 5, 187, "Wheel 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 6, 188, "Wheel 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 7, 189, "Wheel 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +/*-------------------------------------------------------------------------*\ +| CORSAIR DEVICES | +\*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------*\ +| Corsair Dark Core SE 1B1C:1B4B | +| | +| Zone "Scroll Wheel" | +| Single | +| | +| Zone "Side Buttons" | +| Linear | +| 1 Row, 4 Columns | +| | +| Zone "Rear Left" | +| Single | +| | +| Zone "Logo" | +| Single | +| | +| Zone "Rear Right" | +| Single | +| | +| Zone "DPI & Indicator" | +| Linear | +| 1 Row, 4 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone dark_core_se_scroll_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone dark_core_se_button_zone = +{ + "Side Buttons", + ZONE_TYPE_LINEAR, + 1, + 4 +}; + +static const corsair_v2_zone dark_core_se_left_zone = +{ + "Rear Left", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone dark_core_se_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone dark_core_se_right_zone = +{ + "Rear Right", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone dark_core_se_dpi_zone = +{ + "DPI & Indicator Zone", + ZONE_TYPE_LINEAR, + 1, + 4 +}; + +static const corsair_v2_device dark_core_se_device = +{ + CORSAIR_DARK_CORE_RGB_PID, + DEVICE_TYPE_MOUSE, + 1, + 12, + { + &dark_core_se_scroll_zone, + &dark_core_se_button_zone, + &dark_core_se_left_zone, + &dark_core_se_logo_zone, + &dark_core_se_right_zone, + &dark_core_se_dpi_zone + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair Dark Core Pro SE 1B1C:1B7E | +| | +| Zone "Scroll Wheel" | +| Single | +| | +| Zone "Side Buttons" | +| Linear | +| 1 Row, 4 Columns | +| | +| Zone "Rear Left" | +| Single | +| | +| Zone "Logo" | +| Single | +| | +| Zone "Rear Right" | +| Single | +| | +| Zone "DPI & Indicator" | +| Linear | +| 1 Row, 4 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone dark_core_pro_se_scroll_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone dark_core_pro_se_button_zone = +{ + "Side Buttons", + ZONE_TYPE_LINEAR, + 1, + 4 +}; + +static const corsair_v2_zone dark_core_pro_se_left_zone = +{ + "Rear Left", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone dark_core_pro_se_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone dark_core_pro_se_right_zone = +{ + "Rear Right", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone dark_core_pro_se_dpi_zone = +{ + "DPI & Indicator Zone", + ZONE_TYPE_LINEAR, + 1, + 4 +}; + +static const corsair_v2_device dark_core_pro_se_device = +{ + CORSAIR_DARK_CORE_RGB_PRO_PID, + DEVICE_TYPE_MOUSE, + 1, + 12, + { + &dark_core_pro_se_scroll_zone, + &dark_core_pro_se_button_zone, + &dark_core_pro_se_left_zone, + &dark_core_pro_se_logo_zone, + &dark_core_pro_se_right_zone, + &dark_core_pro_se_dpi_zone + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair Harpoon Wireless 1B1C:1B5E | +| | +| Zone "Logo" | +| Single | +| | +| Zone "Scroll Wheel" | +| Single | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone harpoon_indicator_zone = +{ + "Indicator", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone harpoon_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_device harpoon_wireless_device = +{ + CORSAIR_HARPOON_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + 1, + 2, + { + &harpoon_indicator_zone, + &harpoon_logo_zone, + nullptr, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair Ironclaw Wireless 1B1C:1B4C | +| | +| Zone "Logo" | +| Single | +| | +| Zone "Scroll Wheel" | +| Single | +| | +| Zone "Buttons" | +| Single | +| | +| Zone "Side" | +| Linear | +| 1 Row, 3 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone ironclaw_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone ironclaw_scroll_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone ironclaw_button_zone = +{ + "Buttons", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone ironclaw_side_zone = +{ + "Side Zone", + ZONE_TYPE_LINEAR, + 1, + 3 +}; + +static const corsair_v2_device ironclaw_wireless_device = +{ + CORSAIR_IRONCLAW_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + 1, + 6, + { + &ironclaw_logo_zone, + &ironclaw_scroll_zone, + &ironclaw_button_zone, + &ironclaw_side_zone, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair Katar Pro 1B1C:1B93 | +| | +| Zone "Scroll Wheel" | +| Single | +| | +| Zone "DPI" | +| Single | +| | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone katar_pro_scroll_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone katar_pro_dpi_zone = +{ + "DPI", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_device katar_pro_device = +{ + CORSAIR_KATAR_PRO_PID, + DEVICE_TYPE_MOUSE, + 1, + 2, + { + &katar_pro_scroll_zone, + &katar_pro_dpi_zone, + nullptr, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair Katar Pro V2 1B1C:1BBA | +| | +| Zone "Scroll Wheel" | +| Single | +| | +| Zone "DPI" | +| Single | +| | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone katar_pro_v2_scroll_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone katar_pro_v2_dpi_zone = +{ + "DPI", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_device katar_pro_v2_device = +{ + CORSAIR_KATAR_PRO_V2_PID, + DEVICE_TYPE_MOUSE, + 1, + 2, + { + &katar_pro_v2_scroll_zone, + &katar_pro_v2_dpi_zone, + nullptr, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair Katar Pro XT 1B1C:1BAC | +| | +| Zone "Scroll Wheel" | +| Single | +| | +| Zone "DPI" | +| Single | +| | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone katar_pro_xt_scroll_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone katar_pro_xt_dpi_zone = +{ + "DPI", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_device katar_pro_xt_device = +{ + CORSAIR_KATAR_PRO_XT_PID, + DEVICE_TYPE_MOUSE, + 1, + 2, + { + &katar_pro_xt_scroll_zone, + &katar_pro_xt_dpi_zone, + nullptr, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair K55 RGB Pro 1B1C:1BA4 | +| | +| Zone "Keyboard" | +| Linear | +| 1 Row, 6 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone k55_rgb_pro_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_LINEAR, + 1, + 6 +}; + +static const corsair_v2_device k55_rgb_pro_device = +{ + CORSAIR_K55_RGB_PRO_PID, + DEVICE_TYPE_KEYBOARD, + 1, + 6, + { + &k55_rgb_pro_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair K57 RGB (Wired) 1B1C:1B6E | +| | +| Zone "Keyboard" | +| Matrix | +| 7 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone k57_rgb_wired_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 7, + 22 +}; + +static const corsair_v2_device k57_rgb_wired_device = +{ + CORSAIR_K57_RGB_WIRED_PID, + DEVICE_TYPE_KEYBOARD, + 7, + 22, + { + &k57_rgb_wired_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_K57_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K60 RGB Pro 1B1C:1BA0 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 21 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone k60_rgb_pro_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 21 +}; + +static const corsair_v2_device k60_rgb_pro_device = +{ + CORSAIR_K60_RGB_PRO_PID, + DEVICE_TYPE_KEYBOARD, + 6, + 21, + { + &k60_rgb_pro_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_K60_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K60 RGB Pro Low Profile 1B1C:1BAD | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 21 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone k60_rgb_pro_lp_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 21 +}; + +static const corsair_v2_device k60_rgb_pro_lp_device = +{ + CORSAIR_K60_RGB_PRO_LP_PID, + DEVICE_TYPE_KEYBOARD, + 6, + 21, + { + &k60_rgb_pro_lp_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_K60_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K60 RGB Pro TKL 1B1C:1BC7 (black) | +| Corsair K60 RGB Pro TKL 1B1C:1BED (white) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 21 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone k60_rgb_pro_tkl_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 21 +}; + +static const corsair_v2_device k60_rgb_pro_tkl_device_b = +{ + CORSAIR_K60_RGB_PRO_TKL_B_PID, + DEVICE_TYPE_KEYBOARD, + 6, + 21, + { + &k60_rgb_pro_lp_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_K60_tkl_layout +}; + +static const corsair_v2_device k60_rgb_pro_tkl_device_w = +{ + CORSAIR_K60_RGB_PRO_TKL_W_PID, + DEVICE_TYPE_KEYBOARD, + 6, + 21, + { + &k60_rgb_pro_lp_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_K60_tkl_layout +}; + + +/*-------------------------------------------------------------*\ +| Corsair K70 Core RGB 1B1C:1BFD | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 21 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone k70_core_rgb_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 21 +}; + +static const corsair_v2_device k70_core_rgb_device = +{ + CORSAIR_K70_CORE_RGB_PID, + DEVICE_TYPE_KEYBOARD, + 6, + 21, + { + &k70_core_rgb_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_k70_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K70 RGB TKL 1B1C:1B73 | +| | +| Zone "Keyboard" | +| Matrix | +| 7 Rows, 17 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone k70_rgb_tkl_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 7, + 17 +}; + +static const corsair_v2_device k70_rgb_tkl_device = +{ + CORSAIR_K70_RGB_TKL_PID, + DEVICE_TYPE_KEYBOARD, + 7, + 17, + { + &k70_rgb_tkl_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_K70_TKL_cs_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K70 Core RGB TKL 1B1C:2B01 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 1 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone k70_core_rgb_tkl_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 17 +}; + +static const corsair_v2_device k70_core_rgb_tkl_device = +{ + CORSAIR_K70_CORE_RGB_TKL_PID, + DEVICE_TYPE_KEYBOARD, + 6, + 17, + { + &k70_core_rgb_tkl_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_K70_CORE_TKL_layout +}; + + +/*-------------------------------------------------------------*\ +| Corsair K70 RGB TKL Champion Series 1B1C:1BB9 | +| | +| Zone "Keyboard" | +| Matrix | +| 7 Rows, 17 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone k70_rgb_tkl_cs_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 7, + 17 +}; + +static const corsair_v2_device k70_rgb_tkl_cs_device = +{ + CORSAIR_K70_RGB_TKL_CS_PID, + DEVICE_TYPE_KEYBOARD, + 7, + 17, + { + &k70_rgb_tkl_cs_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_K70_TKL_cs_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K70 RGB Pro 1B1C:1BC4 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 21 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone k70_rgb_pro_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 7, + 21 +}; + +static const corsair_v2_device k70_rgb_pro_device = +{ + CORSAIR_K70_RGB_PRO_PID, + DEVICE_TYPE_KEYBOARD, + 7, + 21, + { + &k70_rgb_pro_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_k70_pro_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K70 RGB Pro V2 1B1C:1BB3 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 21 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_device k70_rgb_pro_v2_device = + { + CORSAIR_K70_RGB_PRO_V2_PID, + DEVICE_TYPE_KEYBOARD, + 7, + 21, + { + &k70_rgb_pro_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_k70_pro_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K95 RGB PLATINUM XT 1B1C:1B89 | +| | +| Zone "Keyboard" | +| Matrix | +| 8 Rows, 22 Columns | +\*-------------------------------------------------------------*/ + +static const corsair_v2_zone k95_platinum_xt_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 8, + 22 +}; + +/*--------------------------------------------------------------------------------*\ +| TODO: Add a "Top Bar" zone for the lights defined in the device layout | +| currently, a bug in the device controller causes linear zones to have | +| incorrect led values, this can be done once it's fixed: | +| https://gitlab.com/CalcProgrammer1/OpenRGB/-/merge_requests/2951#note_2679300236 | +\*--------------------------------------------------------------------------------*/ + +static const corsair_v2_device k95_platinum_xt_device = +{ + CORSAIR_K95_PLATINUM_XT_PID, + DEVICE_TYPE_KEYBOARD, + 8, + 22, + { + &k95_platinum_xt_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_k95_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K100 MX Red 1B1C:1B7D | +| | +| Zone "Keyboard" | +| Matrix | +| 12 Rows, 24 Columns | +\*-------------------------------------------------------------*/ + +static const corsair_v2_zone k100_mx_red_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 12, + 24 +}; + +static const corsair_v2_device k100_mx_red_device = +{ + CORSAIR_K100_MXRED_PID, + DEVICE_TYPE_KEYBOARD, + 12, + 24, + { + &k100_mx_red_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_k100_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K100 RGB Optical V1 1B1C:1B7C | +| | +| Zone "Keyboard" | +| Matrix | +| 12 Rows, 24 Columns | +\*-------------------------------------------------------------*/ + +static const corsair_v2_zone k100_rgb_opt_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 12, + 24 +}; + +static const corsair_v2_device k100_rgb_opt_v1_device = +{ + CORSAIR_K100_OPTICAL_V1_PID, + DEVICE_TYPE_KEYBOARD, + 12, + 24, + { + &k100_rgb_opt_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_k100_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair K100 RGB Optical V2 1B1C:1BC5 | +| | +| Zone "Keyboard" | +| Matrix | +| 12 Rows, 24 Columns | +\*-------------------------------------------------------------*/ + +static const corsair_v2_device k100_rgb_opt_v2_device = +{ + CORSAIR_K100_OPTICAL_V2_PID, + DEVICE_TYPE_KEYBOARD, + 12, + 24, + { + &k100_rgb_opt_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }, + &corsair_k100_layout +}; + +/*-------------------------------------------------------------*\ +| Corsair M55 1B1C:1B70 | +| | +| Zone "Logo" | +| Single | +| | +| Zone "Edge" | +| Linear | +| 1 Row, 2 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone m55_mid_zone = +{ + "Middle Button", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone m55_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_device m55_device = +{ + CORSAIR_M55_RGB_PRO_PID, + DEVICE_TYPE_MOUSE, + 1, + 2, + { + &m55_mid_zone, + &m55_logo_zone, + nullptr, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair M65 RGB Ultra Wired 1B1C:1B9E | +| | +| Zone "Scroll Wheel" | +| Single | +| | +| Zone "Logo" | +| Single | +| | +| Zone "Indicator" | +| Single | +| | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone m65_rgb_ultra_wired_scroll_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone m65_rgb_ultra_wired_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone m65_rgb_ultra_wired_indicator_zone = +{ + "Indicator", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_device m65_rgb_ultra_wired_device = +{ + CORSAIR_M65_RGB_ULTRA_WIRED_PID, + DEVICE_TYPE_MOUSE, + 1, + 3, + { + &m65_rgb_ultra_wired_logo_zone, + &m65_rgb_ultra_wired_scroll_zone, + &m65_rgb_ultra_wired_indicator_zone, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair M65 RGB Ultra Wireless 1B1C:1BB5 | +| | +| Zone "Logo" | +| Single | +| | +| Zone "DPI" | +| Single | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone m65_ultra_rgb_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone m65_ultra_rgb_dpi_zone = +{ + "DPI", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_device m65_ultra_rgb_device = +{ + CORSAIR_M65_RGB_ULTRA_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + 1, + 2, + { + &m65_ultra_rgb_logo_zone, + &m65_ultra_rgb_dpi_zone, + nullptr, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair M75 Gaming Mouse 1B1C:1BF0 | +| | +| Zone "Logo" | +| Single | +| | +| Zone "Scroll Wheel" | +| Single | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone m75_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone m75_scroll_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_device m75_device = +{ + CORSAIR_M75_GAMING_MOUSE_PID, + DEVICE_TYPE_MOUSE, + 1, + 2, + { + &m75_logo_zone, + &m75_scroll_zone, + nullptr, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------*\ +| Corsair MM700 1B1C:1B9B | +| | +| Zone "Logo" | +| Single | +| | +| Zone "Edge" | +| Linear | +| 1 Row, 2 Columns | +\*-------------------------------------------------------------*/ +static const corsair_v2_zone mm700_right_zone = +{ + "Right", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone mm700_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_zone mm700_left_zone = +{ + "Left", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const corsair_v2_device mm700_device = +{ + CORSAIR_MM700_PID, + DEVICE_TYPE_MOUSEMAT, + 1, + 3, + { + &mm700_left_zone, + &mm700_right_zone, + &mm700_logo_zone, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +static const corsair_v2_device mm700_3xl_device = +{ + CORSAIR_MM700_3XL_PID, + DEVICE_TYPE_MOUSEMAT, + 1, + 3, + { + &mm700_left_zone, + &mm700_right_zone, + &mm700_logo_zone, + nullptr, + nullptr, + nullptr + }, + nullptr +}; + +/*-------------------------------------------------------------------------*\ +| DEVICE MASTER LIST | +\*-------------------------------------------------------------------------*/ +const corsair_v2_device* corsair_v2_device_list_data[] = +{ +/*-----------------------------------------------------------------*\ +| KEYBOARDS | +\*-----------------------------------------------------------------*/ + &k55_rgb_pro_device, + &k57_rgb_wired_device, + &k60_rgb_pro_device, + &k60_rgb_pro_lp_device, + &k60_rgb_pro_tkl_device_b, + &k60_rgb_pro_tkl_device_w, + &k70_core_rgb_device, + &k70_core_rgb_tkl_device, + &k70_rgb_pro_device, + &k70_rgb_pro_v2_device, + &k70_rgb_tkl_device, + &k70_rgb_tkl_cs_device, + &k95_platinum_xt_device, + &k100_mx_red_device, + &k100_rgb_opt_v1_device, + &k100_rgb_opt_v2_device, + +/*-----------------------------------------------------------------*\ +| MICE | +\*-----------------------------------------------------------------*/ + &dark_core_se_device, + &dark_core_pro_se_device, + &harpoon_wireless_device, + &ironclaw_wireless_device, + &katar_pro_device, + &katar_pro_v2_device, + &katar_pro_xt_device, + &m55_device, + &m65_rgb_ultra_wired_device, + &m65_ultra_rgb_device, + &m75_device, + +/*-----------------------------------------------------------------*\ +| MOUSEMATS | +\*-----------------------------------------------------------------*/ + &mm700_device, + &mm700_3xl_device, +}; + +const unsigned int CORSAIR_V2_DEVICE_COUNT = (sizeof(corsair_v2_device_list_data) / sizeof(corsair_v2_device_list_data[ 0 ])); +const corsair_v2_device** corsair_v2_device_list = corsair_v2_device_list_data; diff --git a/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Devices.h b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Devices.h new file mode 100644 index 0000000..e746d74 --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Devices.h @@ -0,0 +1,118 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralV2Devices.h | +| | +| Device list for Corsair V2 peripherals | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "RGBControllerKeyNames.h" +#include "KeyboardLayoutManager.h" + +#define CORSAIR_ZONES_MAX 6 + +enum corsair_v2_device_mode +{ + CORSAIR_V2_MODE_HW = 0x01, /* Hardware RGB mode */ + CORSAIR_V2_MODE_SW = 0x02, /* Software RGB mode */ +}; + +enum corsair_v2_supports +{ + CORSAIR_V2_TYPE_SW_COLOUR_BLOCK = 1, + CORSAIR_V2_TYPE_HW_COLOUR_BLOCK = 2, + CORSAIR_V2_TYPE_SW_TRIPLETS = 3, + CORSAIR_V2_TYPE_HW_TRIPLETS = 4, +}; + +enum corsair_v2_kb_layout +{ + CORSAIR_V2_KB_LAYOUT_ANSI = 0x01, /* US ANSI Layout */ + CORSAIR_V2_KB_LAYOUT_ISO = 0x02, /* EURO ISO Layout */ + CORSAIR_V2_KB_LAYOUT_ABNT = 0x03, /* Brazilian Layout */ + CORSAIR_V2_KB_LAYOUT_JIS = 0x04, /* Japanese Layout */ +}; + +typedef struct +{ + std::string name; + zone_type type; + uint8_t rows; + uint8_t cols; +} corsair_v2_zone; + +typedef struct +{ + uint8_t zone; + uint8_t row; + uint8_t col; + uint8_t index; + const char* name; +} corsair_v2_led; + +typedef struct +{ + uint16_t pid; + device_type type; + uint8_t rows; + uint8_t cols; + const corsair_v2_zone* zones[CORSAIR_ZONES_MAX]; + keyboard_keymap_overlay_values* layout_new; +} corsair_v2_device; + +/*-----------------------------------------------------*\ +| Corsair V2 Protocol Keyboards | +\*-----------------------------------------------------*/ +#define CORSAIR_K55_RGB_PRO_PID 0x1BA4 +#define CORSAIR_K57_RGB_WIRED_PID 0x1B6E +#define CORSAIR_K57_RGB_WIRELESS_PID 0x1B62 +#define CORSAIR_K60_RGB_PRO_PID 0x1BA0 +#define CORSAIR_K60_RGB_PRO_LP_PID 0x1BAD +#define CORSAIR_K60_RGB_PRO_TKL_B_PID 0x1BC7 +#define CORSAIR_K60_RGB_PRO_TKL_W_PID 0x1BED +#define CORSAIR_K70_CORE_RGB_PID 0x1BFD +#define CORSAIR_K70_CORE_RGB_TKL_PID 0x2B01 +#define CORSAIR_K70_RGB_PRO_PID 0x1BC4 +#define CORSAIR_K70_RGB_PRO_V2_PID 0x1BB3 +#define CORSAIR_K70_RGB_TKL_PID 0x1B73 +#define CORSAIR_K70_RGB_TKL_CS_PID 0x1BB9 +#define CORSAIR_K95_PLATINUM_XT_PID 0x1B89 +#define CORSAIR_K100_OPTICAL_V1_PID 0x1B7C +#define CORSAIR_K100_OPTICAL_V2_PID 0x1BC5 +#define CORSAIR_K100_MXRED_PID 0x1B7D + +/*-----------------------------------------------------*\ +| Corsair V2 Protocol Mice | +\*-----------------------------------------------------*/ +#define CORSAIR_DARK_CORE_RGB_PID 0x1B4B +#define CORSAIR_DARK_CORE_RGB_PRO_PID 0x1B7E +#define CORSAIR_HARPOON_WIRELESS_PID 0x1B5E +#define CORSAIR_IRONCLAW_WIRELESS_PID 0x1B4C +#define CORSAIR_KATAR_PRO_PID 0x1B93 +#define CORSAIR_KATAR_PRO_V2_PID 0x1BBA +#define CORSAIR_KATAR_PRO_XT_PID 0x1BAC +#define CORSAIR_M55_RGB_PRO_PID 0x1B70 +#define CORSAIR_M65_RGB_ULTRA_WIRED_PID 0x1B9E +#define CORSAIR_M65_RGB_ULTRA_WIRELESS_PID 0x1BB5 +#define CORSAIR_M75_GAMING_MOUSE_PID 0x1BF0 +#define CORSAIR_SLIPSTREAM_WIRELESS_PID1 0x1BA6 +#define CORSAIR_SLIPSTREAM_WIRELESS_V2_PID1 0x1B66 +#define CORSAIR_SLIPSTREAM_WIRELESS_PID2 0x1B65 + +/*-----------------------------------------------------*\ +| Corsair V2 Protocol Mousemats | +\*-----------------------------------------------------*/ +#define CORSAIR_MM700_PID 0x1B9B +#define CORSAIR_MM700_3XL_PID 0x1BC9 + +/*-----------------------------------------------------*\ +| These constant values are defined in | +| CorsairPeripheralV2Devices.cpp | +\*-----------------------------------------------------*/ +extern const unsigned int CORSAIR_V2_DEVICE_COUNT; +extern const corsair_v2_device** corsair_v2_device_list; diff --git a/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2HardwareController.cpp b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2HardwareController.cpp new file mode 100644 index 0000000..8b6d1e8 --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2HardwareController.cpp @@ -0,0 +1,88 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralV2HardwareController.cpp | +| | +| Driver for Corsair V2 peripherals - hardware modes | +| | +| Chris M (Dr_No) 07 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "CorsairPeripheralV2HardwareController.h" + +CorsairPeripheralV2HWController::CorsairPeripheralV2HWController(hid_device* dev_handle, const char* path, std::string name) : CorsairPeripheralV2Controller(dev_handle, path, name) +{ + SetRenderMode(CORSAIR_V2_MODE_SW); + LightingControl(0x5F); +} + +CorsairPeripheralV2HWController::~CorsairPeripheralV2HWController() +{ + +} + +void CorsairPeripheralV2HWController::SetLedsDirect(std::vectorcolors) +{ + switch(light_ctrl) + { + case CORSAIR_V2_LIGHT_CTRL1: + SetLedsDirectColourBlocks(colors); + break; + case CORSAIR_V2_LIGHT_CTRL2: + SetLedsDirectTriplets(colors); + break; + default: + LOG_ERROR("[%s] Error setting Direct mode: Device supportes returned %i", + device_name.c_str(), light_ctrl); + break; + } +} + +void CorsairPeripheralV2HWController::SetLedsDirectColourBlocks(std::vectorcolors) +{ + uint16_t count = (uint16_t)colors.size(); + uint16_t green = count; + uint16_t blue = (count * 2); + uint16_t length = (count * 3); + uint8_t* buffer = new uint8_t[length]; + + memset(buffer, 0, length); + + for(std::size_t i = 0; i < count; i++) + { + RGBColor color = *colors[i]; + + buffer[i] = RGBGetRValue(color); + buffer[i + green] = RGBGetGValue(color); + buffer[i + blue] = RGBGetBValue(color); + } + + SetLEDs(buffer, length); + delete[] buffer; +} + +void CorsairPeripheralV2HWController::SetLedsDirectTriplets(std::vectorcolors) +{ + uint16_t count = (uint16_t)colors.size(); + uint16_t length = (count * 3) + CORSAIR_V2HW_DATA_OFFSET; + uint8_t* buffer = new uint8_t[length]; + + memset(buffer, 0, length); + + buffer[0] = CORSAIR_V2_MODE_DIRECT & 0xFF; + buffer[1] = CORSAIR_V2_MODE_DIRECT >> 8; + for(std::size_t i = 0; i < count; i++) + { + RGBColor color = *colors[i]; + std::size_t idx = (i * 3) + CORSAIR_V2HW_DATA_OFFSET; + + buffer[idx] = RGBGetRValue(color); + buffer[idx + 1] = RGBGetGValue(color); + buffer[idx + 2] = RGBGetBValue(color); + } + + SetLEDs(buffer, length); + delete[] buffer; +} diff --git a/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2HardwareController.h b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2HardwareController.h new file mode 100644 index 0000000..82739d4 --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2HardwareController.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralV2HardwareController.h | +| | +| Driver for Corsair V2 peripherals - hardware modes | +| | +| Chris M (Dr_No) 07 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairPeripheralV2Controller.h" + +#include +#include + +#undef CORSAIR_V2_WRITE_SIZE +#define CORSAIR_V2_WRITE_SIZE 1025 +#define CORSAIR_V2HW_DATA_OFFSET 2 + +class CorsairPeripheralV2HWController : public CorsairPeripheralV2Controller +{ +public: + CorsairPeripheralV2HWController(hid_device* dev_handle, const char* path, std::string name); + ~CorsairPeripheralV2HWController(); + + void SetLedsDirect(std::vector colors); + +private: + void SetLedsDirectColourBlocks(std::vector colors); + void SetLedsDirectTriplets(std::vector colors); +}; diff --git a/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2SoftwareController.cpp b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2SoftwareController.cpp new file mode 100644 index 0000000..1b51fbb --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2SoftwareController.cpp @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralV2SoftwareController.cpp | +| | +| Driver for Corsair V2 peripherals - software modes | +| | +| Chris M (Dr_No) 11 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "CorsairPeripheralV2SoftwareController.h" + +CorsairPeripheralV2SWController::CorsairPeripheralV2SWController(hid_device* dev_handle, const char* path, std::string name) : CorsairPeripheralV2Controller(dev_handle, path, name) +{ + SetRenderMode(CORSAIR_V2_MODE_SW); + LightingControl(0x5F); +} + +CorsairPeripheralV2SWController::~CorsairPeripheralV2SWController() +{ + +} + +void CorsairPeripheralV2SWController::SetLedsDirect(std::vectorcolors) +{ + uint16_t count = (uint16_t)colors.size(); + uint16_t green = count; + uint16_t blue = count * 2; + uint16_t length = count * 3; + uint8_t* buffer = new uint8_t[length]; + + memset(buffer, 0, length); + + for(std::size_t i = 0; i < count; i++) + { + RGBColor color = *colors[i]; + + buffer[i] = RGBGetRValue(color); + buffer[green + i] = RGBGetGValue(color); + buffer[blue + i] = RGBGetBValue(color); + } + + SetLEDs(buffer, length); + delete[] buffer; +} diff --git a/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2SoftwareController.h b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2SoftwareController.h new file mode 100644 index 0000000..580e9ab --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2SoftwareController.h @@ -0,0 +1,30 @@ +/*---------------------------------------------------------*\ +| CorsairPeripheralV2SoftwareController.h | +| | +| Driver for Corsair V2 peripherals - software modes | +| | +| Chris M (Dr_No) 11 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairPeripheralV2Controller.h" + +#include +#include + +class CorsairPeripheralV2SWController : public CorsairPeripheralV2Controller +{ +public: + CorsairPeripheralV2SWController(hid_device* dev_handle, const char* path, std::string name); + ~CorsairPeripheralV2SWController(); + + void SetLedsDirect(std::vector colors); + +private: + +}; diff --git a/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Hardware.cpp b/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Hardware.cpp new file mode 100644 index 0000000..25a446f --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Hardware.cpp @@ -0,0 +1,280 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairV2HardwareController.cpp | +| | +| RGBController for Corsair V2 peripherals - hardware | +| modes | +| | +| Chris M (Dr_No) 10 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "RGBController_CorsairV2Hardware.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name Corsair Peripherals V2 Hardware + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCorsairV2HardwareControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CorsairV2HW::RGBController_CorsairV2HW(CorsairPeripheralV2Controller *controller_ptr) +{ + controller = controller_ptr; + const corsair_v2_device* corsair = controller->GetDeviceData(); + + name = controller->GetName(); + vendor = "Corsair"; + description = "Corsair Peripheral V2 HW Device"; + type = corsair->type; + version = controller->GetFirmwareString(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CORSAIR_V2_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = CORSAIR_V2_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.brightness_min = CORSAIR_V2_BRIGHTNESS_MIN; + Static.brightness_max = CORSAIR_V2_BRIGHTNESS_MAX; + Static.brightness = CORSAIR_V2_BRIGHTNESS_MAX; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Static); + + SetupZones(); + /*-----------------------------------------------------*\ + | The Corsair K55 RGB PRO requires a packet within | + | 1 minutes of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 50 sec | + \*-----------------------------------------------------*/ + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RGBController_CorsairV2HW::KeepaliveThread, this); +} + +RGBController_CorsairV2HW::~RGBController_CorsairV2HW() +{ + /*-----------------------------------------------------*\ + | Close keepalive thread | + \*-----------------------------------------------------*/ + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].type == ZONE_TYPE_MATRIX) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_CorsairV2HW::SetupZones() +{ + std::string physical_size; + KEYBOARD_LAYOUT new_layout; + unsigned int max_led_value = 0; + const corsair_v2_device* corsair = controller->GetDeviceData(); + unsigned int layout = controller->GetKeyboardLayout(); + + switch(layout) + { + case CORSAIR_V2_KB_LAYOUT_ISO: + new_layout = KEYBOARD_LAYOUT_ISO_QWERTY; + break; + + case CORSAIR_V2_KB_LAYOUT_JIS: + new_layout = KEYBOARD_LAYOUT_JIS; + break; + + case CORSAIR_V2_KB_LAYOUT_ANSI: + case CORSAIR_V2_KB_LAYOUT_ABNT: + default: + new_layout = KEYBOARD_LAYOUT_ANSI_QWERTY; + break; + } + + /*---------------------------------------------------------*\ + | Fill in zones from the device data | + \*---------------------------------------------------------*/ + for(size_t i = 0; i < CORSAIR_ZONES_MAX; i++) + { + if(corsair->zones[i] == NULL) + { + break; + } + else + { + zone new_zone; + + new_zone.name = corsair->zones[i]->name; + new_zone.type = corsair->zones[i]->type; + + if(new_zone.type == ZONE_TYPE_MATRIX) + { + KeyboardLayoutManager new_kb(new_layout, corsair->layout_new->base_size, corsair->layout_new->key_values); + + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + + new_map->height = corsair->zones[i]->rows; + new_map->width = corsair->zones[i]->cols; + new_map->map = new unsigned int[new_map->height * new_map->width]; + + if(corsair->layout_new->base_size != KEYBOARD_SIZE_EMPTY) + { + /*---------------------------------------------------------*\ + | Minor adjustments to keyboard layout | + \*---------------------------------------------------------*/ + keyboard_keymap_overlay_values* temp = corsair->layout_new; + new_kb.ChangeKeys(*temp); + + /*---------------------------------------------------------*\ + | Matrix map still uses declared zone rows and columns | + | as the packet structure depends on the matrix map | + \*---------------------------------------------------------*/ + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT, new_map->height, new_map->width); + + /*---------------------------------------------------------*\ + | Create LEDs for the Matrix zone | + | Place keys in the layout to populate the matrix | + \*---------------------------------------------------------*/ + new_zone.leds_count = new_kb.GetKeyCount(); + LOG_DEBUG("[%s] Created KB matrix with %d rows and %d columns containing %d keys", + controller->GetName().c_str(), new_kb.GetRowCount(), new_kb.GetColumnCount(), new_zone.leds_count); + + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + max_led_value = std::max(max_led_value, new_led.value); + leds.push_back(new_led); + } + } + + /*---------------------------------------------------------*\ + | Add 1 the max_led_value to account for the 0th index | + \*---------------------------------------------------------*/ + max_led_value++; + } + else + { + new_zone.leds_count = corsair->zones[i]->rows * corsair->zones[i]->cols; + new_zone.matrix_map = NULL; + + /*---------------------------------------------------------*\ + | Create LEDs for the Linear / Single zone | + \*---------------------------------------------------------*/ + for(size_t led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_zone.name + " "; + new_led.name.append(std::to_string( led_idx )); + new_led.value = (unsigned int)leds.size(); + + leds.push_back(new_led); + } + + max_led_value = std::max(max_led_value, (unsigned int)leds.size()); + } + + /*---------------------------------------------------------*\ + | name is not set yet so description is used instead | + \*---------------------------------------------------------*/ + LOG_DEBUG("[%s] Creating a %s zone: %s with %d LEDs", description.c_str(), + ((new_zone.type == ZONE_TYPE_MATRIX) ? "matrix": "linear"), + new_zone.name.c_str(), new_zone.leds_count); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + zones.push_back(new_zone); + } + } + + SetupColors(); + + /*---------------------------------------------------------*\ + | Create a buffer map of pointers which contains the | + | layout order of colors the device expects. | + \*---------------------------------------------------------*/ + for(size_t led_idx = 0; led_idx < max_led_value; led_idx++) + { + buffer_map.push_back(&null_color); + } + + for(size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + buffer_map[leds[led_idx].value] = &colors[led_idx]; + } +} + +void RGBController_CorsairV2HW::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CorsairV2HW::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + controller->SetLedsDirect(buffer_map); +} + +void RGBController_CorsairV2HW::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLedsDirect(buffer_map); +} + +void RGBController_CorsairV2HW::UpdateSingleLED(int /*led*/) +{ + controller->SetLedsDirect(buffer_map); +} + +void RGBController_CorsairV2HW::DeviceUpdateMode() +{ + +} + +void RGBController_CorsairV2HW::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > + std::chrono::milliseconds(CORSAIR_V2_UPDATE_PERIOD)) + { + DeviceUpdateLEDs(); + } + } + std::this_thread::sleep_for(CORSAIR_V2_SLEEP_PERIOD); + } +} diff --git a/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Hardware.h b/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Hardware.h new file mode 100644 index 0000000..3881537 --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Hardware.h @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairV2HardwareController.h | +| | +| RGBController for Corsair V2 peripherals - hardware | +| modes | +| | +| Chris M (Dr_No) 10 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairPeripheralV2Controller.h" +#include "CorsairPeripheralV2HardwareController.h" + +class RGBController_CorsairV2HW : public RGBController +{ +public: + RGBController_CorsairV2HW(CorsairPeripheralV2Controller* controller_ptr); + ~RGBController_CorsairV2HW(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void KeepaliveThread(); + +private: + CorsairPeripheralV2Controller* controller; + + RGBColor null_color = 0; + std::vector buffer_map; + + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point + last_update_time; + +}; diff --git a/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Software.cpp b/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Software.cpp new file mode 100644 index 0000000..d29d2c6 --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Software.cpp @@ -0,0 +1,267 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairV2SoftwareController.cpp | +| | +| RGBController for Corsair V2 peripherals - software | +| modes | +| | +| Chris M (Dr_No) 11 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "RGBController_CorsairV2Software.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name Corsair Peripherals V2 Software + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectCorsairV2SoftwareControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CorsairV2SW::RGBController_CorsairV2SW(CorsairPeripheralV2Controller *controller_ptr) +{ + controller = controller_ptr; + const corsair_v2_device* corsair = controller->GetDeviceData(); + + name = controller->GetName(); + vendor = "Corsair"; + description = "Corsair Peripheral V2 SW Device"; + type = corsair->type; + version = controller->GetFirmwareString(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CORSAIR_V2_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + /*-----------------------------------------------------*\ + | The Corsair K55 RGB PRO requires a packet within | + | 1 minutes of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 50 sec | + \*-----------------------------------------------------*/ + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RGBController_CorsairV2SW::KeepaliveThread, this); +} + +RGBController_CorsairV2SW::~RGBController_CorsairV2SW() +{ + /*-----------------------------------------------------*\ + | Close keepalive thread | + \*-----------------------------------------------------*/ + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].type == ZONE_TYPE_MATRIX) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_CorsairV2SW::SetupZones() +{ + std::string physical_size; + KEYBOARD_LAYOUT new_layout; + unsigned int max_led_value = 0; + const corsair_v2_device* corsair = controller->GetDeviceData(); + unsigned int layout = controller->GetKeyboardLayout(); + + switch(layout) + { + case CORSAIR_V2_KB_LAYOUT_ISO: + new_layout = KEYBOARD_LAYOUT_ISO_QWERTY; + break; + + case CORSAIR_V2_KB_LAYOUT_JIS: + new_layout = KEYBOARD_LAYOUT_JIS; + break; + + case CORSAIR_V2_KB_LAYOUT_ANSI: + case CORSAIR_V2_KB_LAYOUT_ABNT: + default: + new_layout = KEYBOARD_LAYOUT_ANSI_QWERTY; + break; + } + + /*---------------------------------------------------------*\ + | Fill in zones from the device data | + \*---------------------------------------------------------*/ + for(size_t i = 0; i < CORSAIR_ZONES_MAX; i++) + { + if(corsair->zones[i] == NULL) + { + break; + } + else + { + zone new_zone; + + new_zone.name = corsair->zones[i]->name; + new_zone.type = corsair->zones[i]->type; + + if(new_zone.type == ZONE_TYPE_MATRIX) + { + KeyboardLayoutManager new_kb(new_layout, corsair->layout_new->base_size, corsair->layout_new->key_values); + + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + + new_map->height = corsair->zones[i]->rows; + new_map->width = corsair->zones[i]->cols; + new_map->map = new unsigned int[new_map->height * new_map->width]; + + if(corsair->layout_new->base_size != KEYBOARD_SIZE_EMPTY) + { + /*---------------------------------------------------------*\ + | Minor adjustments to keyboard layout | + \*---------------------------------------------------------*/ + keyboard_keymap_overlay_values* temp = corsair->layout_new; + new_kb.ChangeKeys(*temp); + + /*---------------------------------------------------------*\ + | Matrix map still uses declared zone rows and columns | + | as the packet structure depends on the matrix map | + \*---------------------------------------------------------*/ + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT, new_map->height, new_map->width); + + /*---------------------------------------------------------*\ + | Create LEDs for the Matrix zone | + | Place keys in the layout to populate the matrix | + \*---------------------------------------------------------*/ + new_zone.leds_count = new_kb.GetKeyCount(); + LOG_DEBUG("[%s] Created KB matrix with %d rows and %d columns containing %d keys", + controller->GetName().c_str(), new_kb.GetRowCount(), new_kb.GetColumnCount(), new_zone.leds_count); + + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + max_led_value = std::max(max_led_value, new_led.value); + leds.push_back(new_led); + } + } + + /*---------------------------------------------------------*\ + | Add 1 the max_led_value to account for the 0th index | + \*---------------------------------------------------------*/ + max_led_value++; + } + else + { + new_zone.leds_count = corsair->zones[i]->rows * corsair->zones[i]->cols; + new_zone.matrix_map = NULL; + + /*---------------------------------------------------------*\ + | Create LEDs for the Linear / Single zone | + \*---------------------------------------------------------*/ + for(size_t led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_zone.name + " "; + new_led.name.append(std::to_string( led_idx )); + new_led.value = (unsigned int)leds.size(); + + leds.push_back(new_led); + } + + max_led_value = std::max(max_led_value, (unsigned int)leds.size()); + } + + /*---------------------------------------------------------*\ + | name is not set yet so description is used instead | + \*---------------------------------------------------------*/ + LOG_DEBUG("[%s] Creating a %s zone: %s with %d LEDs", description.c_str(), + ((new_zone.type == ZONE_TYPE_MATRIX) ? "matrix": "linear"), + new_zone.name.c_str(), new_zone.leds_count); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + zones.push_back(new_zone); + } + } + + SetupColors(); + + /*---------------------------------------------------------*\ + | Create a buffer map of pointers which contains the | + | layout order of colors the device expects. | + \*---------------------------------------------------------*/ + for(size_t led_idx = 0; led_idx < max_led_value; led_idx++) + { + buffer_map.push_back(&null_color); + } + + for(size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + buffer_map[leds[led_idx].value] = &colors[led_idx]; + } +} + +void RGBController_CorsairV2SW::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CorsairV2SW::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + controller->SetLedsDirect(buffer_map); +} + +void RGBController_CorsairV2SW::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLedsDirect(buffer_map); +} + +void RGBController_CorsairV2SW::UpdateSingleLED(int /*led*/) +{ + controller->SetLedsDirect(buffer_map); +} + +void RGBController_CorsairV2SW::DeviceUpdateMode() +{ + +} + +void RGBController_CorsairV2SW::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > + std::chrono::milliseconds(CORSAIR_V2_UPDATE_PERIOD)) + { + DeviceUpdateLEDs(); + } + } + std::this_thread::sleep_for(CORSAIR_V2_SLEEP_PERIOD); + } +} diff --git a/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Software.h b/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Software.h new file mode 100644 index 0000000..d61c7da --- /dev/null +++ b/Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Software.h @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairV2SoftwareController.h | +| | +| RGBController for Corsair V2 peripherals - software | +| modes | +| | +| Chris M (Dr_No) 11 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairPeripheralV2Controller.h" +#include "CorsairPeripheralV2HardwareController.h" +#include "CorsairPeripheralV2SoftwareController.h" + +class RGBController_CorsairV2SW : public RGBController +{ +public: + RGBController_CorsairV2SW(CorsairPeripheralV2Controller* controller_ptr); + ~RGBController_CorsairV2SW(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void KeepaliveThread(); + +private: + CorsairPeripheralV2Controller* controller; + + RGBColor null_color = 0; + std::vector buffer_map; + + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point + last_update_time; + +}; diff --git a/Controllers/CorsairVengeanceController/CorsairVengeanceController.cpp b/Controllers/CorsairVengeanceController/CorsairVengeanceController.cpp new file mode 100644 index 0000000..ae0314e --- /dev/null +++ b/Controllers/CorsairVengeanceController/CorsairVengeanceController.cpp @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| CorsairVengeanceController.cpp | +| | +| Driver for original single-zone Corsair Vengeance DDR4 | +| RGB RAM | +| | +| Adam Honse (CalcProgrammer1) 08 Mar 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CorsairVengeanceController.h" + +CorsairVengeanceController::CorsairVengeanceController(i2c_smbus_interface* bus, corsair_dev_id dev) +{ + this->bus = bus; + this->dev = dev; + + strcpy(device_name, "Corsair Vengeance RGB"); + led_count = 1; +} + +CorsairVengeanceController::~CorsairVengeanceController() +{ + +} + +std::string CorsairVengeanceController::GetDeviceName() +{ + return(device_name); +} + +std::string CorsairVengeanceController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +unsigned int CorsairVengeanceController::GetLEDCount() +{ + return(led_count); +} + +void CorsairVengeanceController::SetLEDColor(unsigned char red, unsigned char green, unsigned char blue) +{ + bus->i2c_smbus_write_byte_data(dev, CORSAIR_VENGEANCE_RGB_CMD_FADE_TIME, 0x00); + bus->i2c_smbus_write_byte_data(dev, CORSAIR_VENGEANCE_RGB_CMD_RED_VAL, red); + bus->i2c_smbus_write_byte_data(dev, CORSAIR_VENGEANCE_RGB_CMD_GREEN_VAL, green); + bus->i2c_smbus_write_byte_data(dev, CORSAIR_VENGEANCE_RGB_CMD_BLUE_VAL, blue); + bus->i2c_smbus_write_byte_data(dev, CORSAIR_VENGEANCE_RGB_CMD_MODE, CORSAIR_VENGEANCE_RGB_MODE_SINGLE); +} + +void CorsairVengeanceController::SetMode(unsigned char /*mode*/) +{ + bus->i2c_smbus_write_byte_data(dev, CORSAIR_VENGEANCE_RGB_CMD_MODE, CORSAIR_VENGEANCE_RGB_MODE_SINGLE); +} diff --git a/Controllers/CorsairVengeanceController/CorsairVengeanceController.h b/Controllers/CorsairVengeanceController/CorsairVengeanceController.h new file mode 100644 index 0000000..4c6db84 --- /dev/null +++ b/Controllers/CorsairVengeanceController/CorsairVengeanceController.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------*\ +| CorsairVengeanceController.h | +| | +| Driver for original single-zone Corsair Vengeance DDR4 | +| RGB RAM | +| | +| Adam Honse (CalcProgrammer1) 08 Mar 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char corsair_dev_id; +typedef unsigned char corsair_cmd; + +enum +{ + CORSAIR_VENGEANCE_RGB_CMD_FADE_TIME = 0xA4, /* Fade Time, 0 for Static */ + CORSAIR_VENGEANCE_RGB_CMD_HOLD_TIME = 0xA5, /* Hold Time */ + CORSAIR_VENGEANCE_RGB_CMD_MODE = 0xA6, /* Mode Control Value */ + CORSAIR_VENGEANCE_RGB_CMD_RED_VAL = 0xB0, /* Red Color Value */ + CORSAIR_VENGEANCE_RGB_CMD_GREEN_VAL = 0xB1, /* Green Color Value */ + CORSAIR_VENGEANCE_RGB_CMD_BLUE_VAL = 0xB2, /* Blue Color Value */ +}; + +enum +{ + CORSAIR_VENGEANCE_RGB_MODE_SINGLE = 0x00, /* Single Color Effect Mode */ + CORSAIR_VENGEANCE_RGB_MODE_FADE = 0x01, /* Fade Through Colors */ + CORSAIR_VENGEANCE_RGB_MODE_PULSE = 0x02, /* Pulse Through Colors */ + CORSAIR_NUMBER_MODES /* Number of Corsair modes */ +}; + +class CorsairVengeanceController +{ +public: + CorsairVengeanceController(i2c_smbus_interface* bus, corsair_dev_id dev); + ~CorsairVengeanceController(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + unsigned int GetLEDCount(); + void SetMode(unsigned char mode); + + void SetLEDColor(unsigned char red, unsigned char green, unsigned char blue); + +private: + char device_name[32]; + unsigned int led_count; + i2c_smbus_interface * bus; + corsair_dev_id dev; +}; diff --git a/Controllers/CorsairVengeanceController/CorsairVengeanceControllerDetect.cpp b/Controllers/CorsairVengeanceController/CorsairVengeanceControllerDetect.cpp new file mode 100644 index 0000000..7e3b3d1 --- /dev/null +++ b/Controllers/CorsairVengeanceController/CorsairVengeanceControllerDetect.cpp @@ -0,0 +1,96 @@ +/*---------------------------------------------------------*\ +| CorsairVengeanceControllerDetect.cpp | +| | +| Detector for original single-zone Corsair Vengeance | +| DDR4 RGB RAM | +| | +| Adam Honse (CalcProgrammer1) 08 Mar 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "CorsairVengeanceController.h" +#include "RGBController_CorsairVengeance.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* TestForCorsairVengeanceController * +* * +* Tests the given address to see if a Corsair controller exists there. * +* * +\******************************************************************************************/ + +bool TestForCorsairVengeanceController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + + int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE); + + if (res >= 0) + { + pass = true; + + for (int i = 0xA0; i < 0xB0; i++) + { + res = bus->i2c_smbus_read_byte_data(address, i); + + if (res != 0xBA) + { + pass = false; + } + } + } + + return(pass); + +} /* TestForCorsairVengeanceController() */ + +/******************************************************************************************\ +* * +* DetectCorsairVengeanceControllers * +* * +* Detect Corsair controllers on the enumerated I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where device is connected * +* slots - list of SPD entries with matching JEDEC ID * +* * +\******************************************************************************************/ + +void DetectCorsairVengeanceControllers(i2c_smbus_interface* bus, std::vector &slots, const std::string &/*name*/) +{ + for(SPDWrapper *slot : slots) + { + /*-------------------------------------------------*\ + | Test first address range 0x58-0x5F | + \*-------------------------------------------------*/ + unsigned char address = slot->address() + 8; + + if(TestForCorsairVengeanceController(bus, address)) + { + CorsairVengeanceController* new_controller = new CorsairVengeanceController(bus, address); + RGBController_CorsairVengeance* new_rgbcontroller = new RGBController_CorsairVengeance(new_controller); + + ResourceManager::get()->RegisterRGBController(new_rgbcontroller); + } + + /*-------------------------------------------------*\ + | Test second address range 0x18-0x1F | + \*-------------------------------------------------*/ + address = slot->address() - 0x40 + 8; + + if(TestForCorsairVengeanceController(bus, address)) + { + CorsairVengeanceController* new_controller = new CorsairVengeanceController(bus, address); + RGBController_CorsairVengeance* new_rgbcontroller = new RGBController_CorsairVengeance(new_controller); + + ResourceManager::get()->RegisterRGBController(new_rgbcontroller); + } + } +} /* DetectCorsairVengeanceControllers() */ + +REGISTER_I2C_DIMM_DETECTOR("Corsair Vengeance RGB DRAM", DetectCorsairVengeanceControllers, JEDEC_CORSAIR, SPD_DDR4_SDRAM); diff --git a/Controllers/CorsairVengeanceController/RGBController_CorsairVengeance.cpp b/Controllers/CorsairVengeanceController/RGBController_CorsairVengeance.cpp new file mode 100644 index 0000000..19dc7fa --- /dev/null +++ b/Controllers/CorsairVengeanceController/RGBController_CorsairVengeance.cpp @@ -0,0 +1,122 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairVengeance.cpp | +| | +| RGBController for original single-zone Corsair | +| Vengeance DDR4 RGB RAM | +| | +| Adam Honse (CalcProgrammer1) 16 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CorsairVengeance.h" + +/**------------------------------------------------------------------*\ + @name Corsair Vengeance + @category RAM + @type SMBus + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectCorsairVengeanceControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CorsairVengeance::RGBController_CorsairVengeance(CorsairVengeanceController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Corsair"; + type = DEVICE_TYPE_DRAM; + description = "Corsair Vengeance RGB Device"; + location = controller->GetDeviceLocation(); + + mode Static; + Static.name = "Static"; + Static.value = CORSAIR_VENGEANCE_RGB_MODE_SINGLE; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Fade; + Fade.name = "Fade"; + Fade.value = CORSAIR_VENGEANCE_RGB_MODE_FADE; + Fade.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Fade.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Fade); + + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = CORSAIR_VENGEANCE_RGB_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Pulse.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Pulse); + + SetupZones(); +} + +RGBController_CorsairVengeance::~RGBController_CorsairVengeance() +{ + delete controller; +} + +void RGBController_CorsairVengeance::SetupZones() +{ + /*---------------------------------------------------------*\ + | Create a single zone | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = "Corsair Zone"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = controller->GetLEDCount(); + new_zone.leds_max = controller->GetLEDCount(); + new_zone.leds_count = controller->GetLEDCount(); + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led* new_led = new led(); + new_led->name = "Corsair LED"; + leds.push_back(*new_led); + } + + SetupColors(); +} + +void RGBController_CorsairVengeance::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CorsairVengeance::DeviceUpdateLEDs() +{ + RGBColor color = colors[0]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetLEDColor(red, grn, blu); +} + +void RGBController_CorsairVengeance::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairVengeance::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CorsairVengeance::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].value); +} diff --git a/Controllers/CorsairVengeanceController/RGBController_CorsairVengeance.h b/Controllers/CorsairVengeanceController/RGBController_CorsairVengeance.h new file mode 100644 index 0000000..67cc970 --- /dev/null +++ b/Controllers/CorsairVengeanceController/RGBController_CorsairVengeance.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_CorsairVengeance.h | +| | +| RGBController for original single-zone Corsair | +| Vengeance DDR4 RGB RAM | +| | +| Adam Honse (CalcProgrammer1) 16 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CorsairVengeanceController.h" + +class RGBController_CorsairVengeance : public RGBController +{ +public: + RGBController_CorsairVengeance(CorsairVengeanceController* controller_ptr); + ~RGBController_CorsairVengeance(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CorsairVengeanceController* controller; +}; diff --git a/Controllers/CougarController/CougarControllerDetect.cpp b/Controllers/CougarController/CougarControllerDetect.cpp new file mode 100644 index 0000000..d0ccdf7 --- /dev/null +++ b/Controllers/CougarController/CougarControllerDetect.cpp @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| CougarControllerDetect.cpp | +| | +| Detector for Cougar devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBController_CougarKeyboard.h" +#include "RGBController_CougarRevengerST.h" + +/*----------------------------------------------------------*\ +| Cougar vendor ID | +\*----------------------------------------------------------*/ +#define COUGAR_VID 0x12CF +#define COUGAR_VID_2 0x060B + +/*----------------------------------------------------------*\ +| Product ID | +\*----------------------------------------------------------*/ +#define COUGAR_700K_EVO_PID 0x7010 +#define COUGAR_REVENGER_ST_PID 0x0412 + +void DetectCougarRevengerSTControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CougarRevengerSTController* controller = new CougarRevengerSTController(dev, *info, name); + RGBController_CougarRevengerST* rgb_controller = new RGBController_CougarRevengerST(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectCougar700kEvo(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if (dev) + { + CougarKeyboardController* controller = new CougarKeyboardController(dev, info->path, name); + RGBController_CougarKeyboard* rgb_controller = new RGBController_CougarKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Cougar 700K EVO Gaming Keyboard", DetectCougar700kEvo, COUGAR_VID_2, COUGAR_700K_EVO_PID, 3, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Cougar Revenger ST", DetectCougarRevengerSTControllers, COUGAR_VID, COUGAR_REVENGER_ST_PID, 0, 0x0001, 2); diff --git a/Controllers/CougarController/CougarKeyboardController/CougarKeyboardController.cpp b/Controllers/CougarController/CougarKeyboardController/CougarKeyboardController.cpp new file mode 100644 index 0000000..b6cf95c --- /dev/null +++ b/Controllers/CougarController/CougarKeyboardController/CougarKeyboardController.cpp @@ -0,0 +1,216 @@ +/*---------------------------------------------------------*\ +| CougarKeyboardController.cpp | +| | +| Driver for Cougar keyboard | +| | +| Chris M (DrNo) 05 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CougarKeyboardController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +static uint8_t keyvalue_map[113] = +{ +/*00 ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 */ + 0, 9, 18, 27, 36, 45, 54, 63, 72, 81, + +/*10 F10 F11 F12 PRT SLK PBK PLY VDN VUP MTE */ + 90, 99, 108, 117, 126, 15, 135, 140, 141, 142, + +/*20 ` 1 2 3 4 5 6 7 8 9 */ + 1, 10, 19, 28, 37, 46, 55, 64, 73, 82, + +/*30 0 - = BSP INS HME PUP NLK NM/ NM* */ + 91, 100, 109, 127, 128, 24, 33, 42, 51, 60, + +/*40 NM- TAB Q W E R T Y U I */ + 69, 2, 11, 20, 29, 38, 47, 56, 65, 74, + +/*50 O P [ ] \ DEL END PDN NM7 NM8 */ + 83, 92, 101, 110, 119, 129, 78, 87, 96, 105, + +/*60 NM9 NM+ CAP A S D F G H J */ + 16, 25, 3, 12, 21, 30, 39, 48, 57, 66, + +/*70 K L ; ' ENT NM4 NM5 NM6 LSH Z */ + 75, 84, 93, 102, 120, 34, 43, 52, 4, 22, + +/*80 X C V B N M , . / RSH */ + 31, 40, 49, 58, 67, 76, 85, 94, 103, 121, + +/*90 UP NM1 NM2 NM3 NETR LCTL LWIN LALT SPC RALT */ + 130, 70, 79, 88, 106, 5, 14, 23, 50, 86, + +/*100 RWIN RFNC RCTL LFT DWN RGT NM0 NM. G1 G2 */ + 95, 104, 113, 122, 131, 132, 133, 97, 147, 148, + +/*110 G3 G4 G5 */ + 149, 150, 151 +}; + +CougarKeyboardController::CougarKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +CougarKeyboardController::~CougarKeyboardController() +{ + hid_close(dev); +} + +std::string CougarKeyboardController::GetDeviceName() +{ + return(name); +} + +std::string CougarKeyboardController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string CougarKeyboardController::GetLocation() +{ + return("HID: " + location); +} + +void CougarKeyboardController::SetMode(uint8_t mode, uint8_t speed, uint8_t brightness, uint8_t direction, std::vector colours, bool random_colours) +{ + uint8_t buffer[COUGARKEYBOARDCONTROLLER_WRITE_PACKET_SIZE] = { 0x00, 0x14, 0x2C }; + + buffer[COUGARKEYBOARDCONTROLLER_MODE_BYTE] = mode; + buffer[COUGARKEYBOARDCONTROLLER_SPEED_BYTE] = speed; + buffer[COUGARKEYBOARDCONTROLLER_BRIGHTNESS_BYTE] = brightness; + buffer[COUGARKEYBOARDCONTROLLER_RANDOM_BYTE] = (random_colours) ? 1 : 0; + buffer[COUGARKEYBOARDCONTROLLER_DIRECTION_BYTE] = direction; + + switch(mode) + { + /*-----------------------------------------------------*\ + | Off mode does not need any further settings & should | + | skip the default case to avoid an indexing error | + \*-----------------------------------------------------*/ + case COUGARKEYBOARDCONTROLLER_MODE_OFF: + break; + + /*-----------------------------------------------------*\ + | Spectrum Cycle mode (Circle) always sets random | + \*-----------------------------------------------------*/ + case COUGARKEYBOARDCONTROLLER_MODE_CIRCLE: + buffer[COUGARKEYBOARDCONTROLLER_RANDOM_BYTE] = 1; + break; + + /*-----------------------------------------------------*\ + | Wave mode does not have a true "random" colour and | + | needs to set the "rainbow" mode (val = 2) instead | + \*-----------------------------------------------------*/ + case COUGARKEYBOARDCONTROLLER_MODE_WAVE: + buffer[COUGARKEYBOARDCONTROLLER_RANDOM_BYTE] = (random_colours) ? 2 : 0; + buffer[COUGARKEYBOARDCONTROLLER_DATA_BYTE] = 2; + + case COUGARKEYBOARDCONTROLLER_MODE_RIPPLE: + case COUGARKEYBOARDCONTROLLER_MODE_SCAN: + { + uint8_t count = (uint8_t)colours.size(); + uint8_t timer = 100 / count; + buffer[COUGARKEYBOARDCONTROLLER_DATA_BYTE + 1] = count; + + for(uint8_t i = 0; i < count; i++) + { + uint8_t offset = 11 + (i * 4); + + buffer[offset] = (i + 1) * timer; + buffer[offset + 1] = RGBGetRValue(colours[i]); + buffer[offset + 2] = RGBGetGValue(colours[i]); + buffer[offset + 3] = RGBGetBValue(colours[i]); + } + } + break; + + case COUGARKEYBOARDCONTROLLER_MODE_RHYTHM: + buffer[COUGARKEYBOARDCONTROLLER_RANDOM_BYTE] = (random_colours) ? 2 : 0; + + default: + buffer[COUGARKEYBOARDCONTROLLER_DATA_BYTE + 1] = RGBGetRValue(colours[0]); + buffer[COUGARKEYBOARDCONTROLLER_DATA_BYTE + 2] = RGBGetGValue(colours[0]); + buffer[COUGARKEYBOARDCONTROLLER_DATA_BYTE + 3] = RGBGetBValue(colours[0]); + break; + } + + hid_write(dev, buffer, COUGARKEYBOARDCONTROLLER_WRITE_PACKET_SIZE); +} + +void CougarKeyboardController::SetLedsDirect(std::vector colours) +{ + uint8_t max_leds = 14; + uint8_t leds_remaining = (uint8_t)colours.size(); + uint8_t packet_flag = COUGARKEYBOARDCONTROLLER_DIRECTION_BYTE; + uint8_t buffer[COUGARKEYBOARDCONTROLLER_WRITE_PACKET_SIZE] = { 0x00, 0x14, 0x2C, 0x0B, 0x00, 0xFF, 0x64, 0x00, 0x01 }; + + /*-----------------------------------------------------------------*\ + | Set up Direct packet | + | keyvalue_map is the index of the Key from full_matrix_map | + \*-----------------------------------------------------------------*/ + for(uint8_t leds2send = 0; leds2send < leds_remaining; leds2send += max_leds) + { + /*-----------------------------------------------------------------*\ + | Check if there is enough leds for another pass | + \*-----------------------------------------------------------------*/ + if(leds2send + max_leds > leds_remaining) + { + max_leds = leds_remaining - leds2send; + + /*-----------------------------------------------------------------*\ + | The last packet flag should be 0x03 | + \*-----------------------------------------------------------------*/ + buffer[packet_flag] = 3; + } + + for(uint8_t i = 0; i < max_leds; i++) + { + uint8_t offset = COUGARKEYBOARDCONTROLLER_DATA_BYTE + (i * 4); + uint8_t led_num = leds2send + i; + + buffer[offset] = keyvalue_map[led_num]; + buffer[offset + 1] = RGBGetRValue(colours[led_num]); + buffer[offset + 2] = RGBGetGValue(colours[led_num]); + buffer[offset + 3] = RGBGetBValue(colours[led_num]); + } + hid_write(dev, buffer, COUGARKEYBOARDCONTROLLER_WRITE_PACKET_SIZE); + std::this_thread::sleep_for(1ms); + + /*-----------------------------------------------------------------*\ + | After the first packet the packet flag should be 0x02 | + \*-----------------------------------------------------------------*/ + buffer[packet_flag] = 2; + } +} + +void CougarKeyboardController::Save(uint8_t flag) +{ + uint8_t buffer[COUGARKEYBOARDCONTROLLER_WRITE_PACKET_SIZE] = { 0x00, 0x12, flag, 0x00, 0x00 }; + + hid_write(dev, buffer, COUGARKEYBOARDCONTROLLER_WRITE_PACKET_SIZE); +} + +void CougarKeyboardController::SendProfile(uint8_t profile, uint8_t light) +{ + uint8_t buffer[COUGARKEYBOARDCONTROLLER_WRITE_PACKET_SIZE] = { 0x00, 0x14, 0x00, 0x00, 0x00, profile, light, 0x00, 0x00}; + + hid_write(dev, buffer, COUGARKEYBOARDCONTROLLER_WRITE_PACKET_SIZE); +} diff --git a/Controllers/CougarController/CougarKeyboardController/CougarKeyboardController.h b/Controllers/CougarController/CougarKeyboardController/CougarKeyboardController.h new file mode 100644 index 0000000..c3eada4 --- /dev/null +++ b/Controllers/CougarController/CougarKeyboardController/CougarKeyboardController.h @@ -0,0 +1,85 @@ +/*---------------------------------------------------------*\ +| CougarKeyboardController.h | +| | +| Driver for Cougar keyboard | +| | +| Chris M (DrNo) 05 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define COUGARKEYBOARDCONTROLLER_WRITE_PACKET_SIZE 65 //Buffer requires a prepended ReportID hence + 1 +#define HID_MAX_STR 255 + +#define COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN 0 +#define COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX 100 +#define COUGARKEYBOARDCONTROLLER_MATRIX_WIDTH 23 + +static const uint8_t direction_map[6] = +{ + 4, 0, 6, 2, 11, 12 //Left, Right, Up, Down, Horizontal, Vertical +}; + +enum Cougar_Keyboard_Controller_Modes +{ + COUGARKEYBOARDCONTROLLER_MODE_OFF = 0x0C, //Turn off - All leds off + COUGARKEYBOARDCONTROLLER_MODE_DIRECT = 0x0B, //Customize Mode + COUGARKEYBOARDCONTROLLER_MODE_STATIC = 0x00, //Steady Mode + COUGARKEYBOARDCONTROLLER_MODE_BREATHING = 0x01, //Breathing Mode - Fades between fully off and fully on. + COUGARKEYBOARDCONTROLLER_MODE_CIRCLE = 0x02, + COUGARKEYBOARDCONTROLLER_MODE_REACTIVE = 0x03, //Click Mode + COUGARKEYBOARDCONTROLLER_MODE_WAVE = 0x04, + COUGARKEYBOARDCONTROLLER_MODE_RIPPLE = 0x05, + COUGARKEYBOARDCONTROLLER_MODE_STAR = 0x06, + COUGARKEYBOARDCONTROLLER_MODE_SCAN = 0x07, + COUGARKEYBOARDCONTROLLER_MODE_RHYTHM = 0x08, + COUGARKEYBOARDCONTROLLER_MODE_RAIN = 0x09, + COUGARKEYBOARDCONTROLLER_MODE_SNAKE = 0x0A, +}; + +enum Cougar_Keyboard_Controller_Byte_Map +{ + COUGARKEYBOARDCONTROLLER_REPORT_BYTE = 1, + COUGARKEYBOARDCONTROLLER_COMMAND_BYTE = 2, + COUGARKEYBOARDCONTROLLER_MODE_BYTE = 3, + COUGARKEYBOARDCONTROLLER_SPEED_BYTE = 5, + COUGARKEYBOARDCONTROLLER_BRIGHTNESS_BYTE = 6, + COUGARKEYBOARDCONTROLLER_RANDOM_BYTE = 7, + COUGARKEYBOARDCONTROLLER_DIRECTION_BYTE = 8, + COUGARKEYBOARDCONTROLLER_DATA_BYTE = 9, +}; + +enum Cougar_Keyboard_Controller_Speeds +{ + COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST = 0x0A, // Slowest speed + COUGARKEYBOARDCONTROLLER_SPEED_NORMAL = 0x05, // Normal speed + COUGARKEYBOARDCONTROLLER_SPEED_FASTEST = 0x01, // Fastest speed +}; + +class CougarKeyboardController +{ +public: + CougarKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name); + ~CougarKeyboardController(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + void SetMode(uint8_t mode, uint8_t speed, uint8_t brightness, uint8_t direction, std::vector colours, bool random_colours); + void SetLedsDirect(std::vector colours); + void Save(uint8_t flag); + void SendProfile(uint8_t profile, uint8_t light); +private: + std::string serial; + std::string location; + std::string name; + hid_device* dev; +}; diff --git a/Controllers/CougarController/CougarKeyboardController/RGBController_CougarKeyboard.cpp b/Controllers/CougarController/CougarKeyboardController/RGBController_CougarKeyboard.cpp new file mode 100644 index 0000000..1bd0d30 --- /dev/null +++ b/Controllers/CougarController/CougarKeyboardController/RGBController_CougarKeyboard.cpp @@ -0,0 +1,511 @@ +/*---------------------------------------------------------*\ +| RGBController_CougarKeyboard.cpp | +| | +| RGBController for Cougar keyboard | +| | +| Chris M (DrNo) 05 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#define NA 0xFFFFFFFF + +#include +#include "hsv.h" +#include "RGBControllerKeyNames.h" +#include "RGBController_CougarKeyboard.h" + +using namespace std::chrono_literals; + +static unsigned int matrix_map[6][COUGARKEYBOARDCONTROLLER_MATRIX_WIDTH] = +{ + { NA, 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, NA, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }, + { 108, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, NA, 34, 35, 36, 37, 38, 39, 40 }, + { 109, 41, NA, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61 }, + { 110, 62, NA, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, NA, 74, NA, NA, NA, 75, 76, 77, NA }, + { 111, 78, NA, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, NA, 89, NA, NA, 90, NA, 91, 92, 93, 94 }, + { 112, 95, 96, 97, NA, NA, NA, 98, NA, NA, NA, 99, 100, NA, 101, 102, 103, 104, 105, 106, NA, 107, NA } +}; + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, //00 + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, //10 + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAUSE_BREAK, + KEY_EN_MEDIA_PLAY_PAUSE, //OPEN MEDIA + KEY_EN_MEDIA_VOLUME_DOWN, + KEY_EN_MEDIA_VOLUME_UP, + KEY_EN_MEDIA_MUTE, + + KEY_EN_BACK_TICK, //20 + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, //30 + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_HOME, + KEY_EN_PAGE_UP, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, //40 + + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, //50 + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, //60 + KEY_EN_NUMPAD_PLUS, + + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, //70 + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, + KEY_EN_X, //80 + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, //90 + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_ENTER, + + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_WINDOWS, //100 + KEY_EN_RIGHT_FUNCTION, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD, + + "Key: G1", + "Key: G2", + "Key: G3", //110 + "Key: G4", + "Key: G5" +}; + +/**------------------------------------------------------------------*\ + @name Cougar 700K Evo Keyboard + @category Keyboard + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCougar700kEvo + @comment The Cougar 700K Evo controller implements all hardware modes + found in the OEM software but has not been able to include all + options for some modes. eg. Rainbow colour mode. Music mode was + deteremined to be a software driven effect which can be added with + the (OpenRGB Effects Engine plugin)[https://gitlab.com/OpenRGBDevelopers/OpenRGBEffectsPlugin]. +\*-------------------------------------------------------------------*/ + +RGBController_CougarKeyboard::RGBController_CougarKeyboard(CougarKeyboardController *controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Cougar"; + type = DEVICE_TYPE_KEYBOARD; + description = "Cougar Keyboard Device"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = COUGARKEYBOARDCONTROLLER_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = COUGARKEYBOARDCONTROLLER_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Static.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Static.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = COUGARKEYBOARDCONTROLLER_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(Breathing.colors_max); + Breathing.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Breathing.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Breathing.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Breathing.speed_min = COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST; + Breathing.speed_max = COUGARKEYBOARDCONTROLLER_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = COUGARKEYBOARDCONTROLLER_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Circle; + Circle.name = "Spectrum Cycle"; + Circle.value = COUGARKEYBOARDCONTROLLER_MODE_CIRCLE; + Circle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Circle.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Circle.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Circle.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Circle.speed_min = COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST; + Circle.speed_max = COUGARKEYBOARDCONTROLLER_SPEED_FASTEST; + Circle.color_mode = MODE_COLORS_NONE; + Circle.speed = COUGARKEYBOARDCONTROLLER_SPEED_NORMAL; + modes.push_back(Circle); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = COUGARKEYBOARDCONTROLLER_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Reactive.colors_min = 1; + Reactive.colors_max = 1; + Reactive.colors.resize(Reactive.colors_max); + Reactive.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Reactive.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Reactive.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Reactive.speed_min = COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST; + Reactive.speed_max = COUGARKEYBOARDCONTROLLER_SPEED_FASTEST; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.speed = COUGARKEYBOARDCONTROLLER_SPEED_NORMAL; + modes.push_back(Reactive); + + mode Wave; + Wave.name = "Wave"; + Wave.value = COUGARKEYBOARDCONTROLLER_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_MANUAL_SAVE; + Wave.colors_min = 1; + Wave.colors_max = 1; + Wave.colors.resize(Wave.colors_max); + Wave.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Wave.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Wave.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Wave.speed_min = COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST; + Wave.speed_max = COUGARKEYBOARDCONTROLLER_SPEED_FASTEST; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.speed = COUGARKEYBOARDCONTROLLER_SPEED_NORMAL; + modes.push_back(Wave); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = COUGARKEYBOARDCONTROLLER_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Ripple.colors_min = 1; + Ripple.colors_max = 1; + Ripple.colors.resize(Ripple.colors_max); + Ripple.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Ripple.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Ripple.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Ripple.speed_min = COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST; + Ripple.speed_max = COUGARKEYBOARDCONTROLLER_SPEED_FASTEST; + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripple.speed = COUGARKEYBOARDCONTROLLER_SPEED_NORMAL; + modes.push_back(Ripple); + + mode Star; + Star.name = "Star"; + Star.value = COUGARKEYBOARDCONTROLLER_MODE_STAR; + Star.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Star.colors_min = 1; + Star.colors_max = 1; + Star.colors.resize(Star.colors_max); + Star.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Star.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Star.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Star.speed_min = COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST; + Star.speed_max = COUGARKEYBOARDCONTROLLER_SPEED_FASTEST; + Star.color_mode = MODE_COLORS_MODE_SPECIFIC; + Star.speed = COUGARKEYBOARDCONTROLLER_SPEED_NORMAL; + modes.push_back(Star); + + mode Scan; + Scan.name = "Scan"; + Scan.value = COUGARKEYBOARDCONTROLLER_MODE_SCAN; + Scan.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | + MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_MANUAL_SAVE; + Scan.colors_min = 1; + Scan.colors_max = 1; + Scan.colors.resize(Scan.colors_max); + Scan.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Scan.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Scan.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Scan.speed_min = COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST; + Scan.speed_max = COUGARKEYBOARDCONTROLLER_SPEED_FASTEST; + Scan.color_mode = MODE_COLORS_MODE_SPECIFIC; + Scan.speed = COUGARKEYBOARDCONTROLLER_SPEED_NORMAL; + modes.push_back(Scan); + + mode Rhythm; + Rhythm.name = "Rhythm"; + Rhythm.value = COUGARKEYBOARDCONTROLLER_MODE_RHYTHM; + Rhythm.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Rhythm.colors_min = 1; + Rhythm.colors_max = 1; + Rhythm.colors.resize(Rhythm.colors_max); + Rhythm.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Rhythm.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Rhythm.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Rhythm.speed_min = COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST; + Rhythm.speed_max = COUGARKEYBOARDCONTROLLER_SPEED_FASTEST; + Rhythm.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rhythm.speed = COUGARKEYBOARDCONTROLLER_SPEED_NORMAL; + modes.push_back(Rhythm); + + mode Rain; + Rain.name = "Rain"; + Rain.value = COUGARKEYBOARDCONTROLLER_MODE_RAIN; + Rain.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Rain.colors_min = 1; + Rain.colors_max = 1; + Rain.colors.resize(Rain.colors_max); + Rain.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Rain.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Rain.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Rain.speed_min = COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST; + Rain.speed_max = COUGARKEYBOARDCONTROLLER_SPEED_FASTEST; + Rain.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rain.speed = COUGARKEYBOARDCONTROLLER_SPEED_NORMAL; + modes.push_back(Rain); + + mode Snake; + Snake.name = "Snake"; + Snake.value = COUGARKEYBOARDCONTROLLER_MODE_SNAKE; + Snake.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Snake.colors_min = 1; + Snake.colors_max = 1; + Snake.colors.resize(Snake.colors_max); + Snake.brightness_min = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MIN; + Snake.brightness_max = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Snake.brightness = COUGARKEYBOARDCONTROLLER_BRIGHTNESS_MAX; + Snake.speed_min = COUGARKEYBOARDCONTROLLER_SPEED_SLOWEST; + Snake.speed_max = COUGARKEYBOARDCONTROLLER_SPEED_FASTEST; + Snake.color_mode = MODE_COLORS_MODE_SPECIFIC; + Snake.speed = COUGARKEYBOARDCONTROLLER_SPEED_NORMAL; + modes.push_back(Snake); + + mode Off; + Off.name = "Off"; + Off.value = COUGARKEYBOARDCONTROLLER_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_CougarKeyboard::~RGBController_CougarKeyboard() +{ + delete controller; +} + +void RGBController_CougarKeyboard::SetupZones() +{ + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + zone KB_zone; + KB_zone.name = ZONE_EN_KEYBOARD; + KB_zone.type = ZONE_TYPE_MATRIX; + KB_zone.leds_count = 113; + KB_zone.leds_min = KB_zone.leds_count; + KB_zone.leds_max = KB_zone.leds_count; + + KB_zone.matrix_map = new matrix_map_type; + KB_zone.matrix_map->height = 6; + KB_zone.matrix_map->width = COUGARKEYBOARDCONTROLLER_MATRIX_WIDTH; + KB_zone.matrix_map->map = (unsigned int *)&matrix_map; + zones.push_back(KB_zone); + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_index = 0; zone_index < zones.size(); zone_index++) + { + for(unsigned int led_index = 0; led_index < zones[zone_index].leds_count; led_index++) + { + led new_led; + new_led.name = led_names[led_index]; + new_led.value = led_index; + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_CougarKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CougarKeyboard::DeviceUpdateLEDs() +{ + controller->SetLedsDirect(colors); +} + +void RGBController_CougarKeyboard::UpdateZoneLEDs(int zone) +{ + std::vector colour; + for(size_t i = 0; i < zones[zone].leds_count; i++) + { + colour.push_back(zones[zone].colors[i]); + } + + controller->SetLedsDirect(colour); +} + +void RGBController_CougarKeyboard::UpdateSingleLED(int led) +{ + std::vector colour; + colour.push_back(colors[led]); + + controller->SetLedsDirect(colour); +} + +void RGBController_CougarKeyboard::DeviceUpdateMode() +{ + mode set_mode = modes[active_mode]; + std::vector colours = (set_mode.colors); + + /*---------------------------------------------------------*\ + | No mode set packets required for Direct mode | + | Wave mode requires 5 colours based on the selected colour | + \*---------------------------------------------------------*/ + switch(set_mode.value) + { + case COUGARKEYBOARDCONTROLLER_MODE_DIRECT: + return; + case COUGARKEYBOARDCONTROLLER_MODE_WAVE: + if(set_mode.color_mode == MODE_COLORS_MODE_SPECIFIC) + { + hsv_t temp; + colours.resize(1); + rgb2hsv( colours[0], &temp); + + temp.value = temp.value / 2; + RGBColor half = hsv2rgb(&temp); + + temp.value = temp.value / 4; + RGBColor eighth = hsv2rgb(&temp); + + colours.push_back(half); + colours.push_back(eighth); + colours.push_back(half); + colours.push_back(colours[0]); + } + break; + } + + uint8_t direction = direction_map[set_mode.direction]; + bool random_colours = (set_mode.color_mode == MODE_COLORS_RANDOM); + + controller->SetMode( set_mode.value, set_mode.speed, set_mode.brightness, direction, colours, random_colours ); +} + +void RGBController_CougarKeyboard::DeviceSaveMode() +{ + const uint8_t start = 0x02; + const uint8_t end = 0x03; + + /*---------------------------------------------------------*\ + | The Keyboard has the ability to save 3 light modes across | + | 3 profiles but currently we will only set the 1st of each | + | Profiles = 1 thru 3 | + | Lights = 0 thru 2 | + \*---------------------------------------------------------*/ + controller->Save(start); + std::this_thread::sleep_for(10ms); + controller->SendProfile(1, 0); + std::this_thread::sleep_for(150ms); + DeviceUpdateMode(); + std::this_thread::sleep_for(10ms); + controller->Save(end); +} diff --git a/Controllers/CougarController/CougarKeyboardController/RGBController_CougarKeyboard.h b/Controllers/CougarController/CougarKeyboardController/RGBController_CougarKeyboard.h new file mode 100644 index 0000000..bee0795 --- /dev/null +++ b/Controllers/CougarController/CougarKeyboardController/RGBController_CougarKeyboard.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| RGBController_CougarKeyboard.h | +| | +| RGBController for Cougar keyboard | +| | +| Chris M (DrNo) 05 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "LogManager.h" +#include "RGBController.h" +#include "CougarKeyboardController.h" + +class RGBController_CougarKeyboard : public RGBController +{ +public: + RGBController_CougarKeyboard(CougarKeyboardController* controller_ptr); + ~RGBController_CougarKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + int GetDeviceMode(); + int GetLED_Zone(int led_idx); + + CougarKeyboardController* controller; +}; diff --git a/Controllers/CougarController/CougarRevengerSTController/CougarRevengerSTController.cpp b/Controllers/CougarController/CougarRevengerSTController/CougarRevengerSTController.cpp new file mode 100644 index 0000000..eda118a --- /dev/null +++ b/Controllers/CougarController/CougarRevengerSTController/CougarRevengerSTController.cpp @@ -0,0 +1,213 @@ +/*---------------------------------------------------------*\ +| CougarRevengerSTController.cpp | +| | +| Driver for Cougar Revenger ST | +| | +| Morgan Guimard (morg) 17 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CougarRevengerSTController.h" +#include "StringUtils.h" + +CougarRevengerSTController::CougarRevengerSTController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + version = ""; + + ActivateMode(0, DIRECT_MODE_VALUE); + ActivateMode(1, DIRECT_MODE_VALUE); + ActivateMode(2, DIRECT_MODE_VALUE); +} + +CougarRevengerSTController::~CougarRevengerSTController() +{ + hid_close(dev); +} + +std::string CougarRevengerSTController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string CougarRevengerSTController::GetNameString() +{ + return(name); +} + +std::string CougarRevengerSTController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string CougarRevengerSTController::GetFirmwareVersion() +{ + return(version); +} + +void CougarRevengerSTController::SetDirect(unsigned char zone, RGBColor color, unsigned char brightness) +{ + const cougar_mode& m = modes_mapping.at(DIRECT_MODE_VALUE); + + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x01] = PACKET_START; + usb_buf[0x02] = ACTION_SET; + usb_buf[0x05] = m.zone_mode_byte[zone]; + + /*-----------------------------------------*\ + | Set RGB Values | + \*-----------------------------------------*/ + SendColourPacket(m.zone_rgb_mapping[zone], RGBGetRValue(color), usb_buf); + SendColourPacket(m.zone_rgb_mapping[zone] + 1, RGBGetGValue(color), usb_buf); + SendColourPacket(m.zone_rgb_mapping[zone] + 2, RGBGetBValue(color), usb_buf); + + /*-----------------------------------------*\ + | Set Brightness | + \*-----------------------------------------*/ + usb_buf[0x04] = m.zone_brightness_mapping[zone]; + usb_buf[0x06] = brightness; + + hid_send_feature_report(dev,usb_buf, PACKET_DATA_LENGTH); +} + +void CougarRevengerSTController::SetModeData(unsigned char zone, unsigned char mode_value, std::vector colors, unsigned char brightness, unsigned char speed) +{ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + + const cougar_mode& m = modes_mapping.at(mode_value); + + /*-----------------------------------------*\ + | Define colors | + \*-----------------------------------------*/ + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x01] = PACKET_START; + usb_buf[0x02] = ACTION_SET; + + unsigned char offset = m.zone_rgb_mapping[zone]; + + usb_buf[0x05] = m.zone_rgb_byte[zone]; + + for(unsigned int i = 0; i < COLORS_SIZE; i++) + { + if(mode_value == BREATHING_MODE_VALUE && i >= 3 && zone == 2) + { + usb_buf[0x05] = 0x01; + } + + SendColourPacket(offset, RGBGetRValue(colors[i]), usb_buf); + SendColourPacket(offset + 1, RGBGetGValue(colors[i]), usb_buf); + SendColourPacket(offset + 2, RGBGetBValue(colors[i]), usb_buf); + + offset += 4; + } + + /*-----------------------------------------*\ + | Define speed | + \*-----------------------------------------*/ + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x01] = PACKET_START; + usb_buf[0x02] = ACTION_SET; + usb_buf[0x04] = m.zone_speed_mapping[zone]; + usb_buf[0x05] = m.zone_speed_byte[zone]; + + /*-----------------------------------------*\ + | Dirty hack here: | + | The OEM app or firmware seems to be | + | broken, at full speed it flashes on | + | the bottom zone. Let's adjust a bit the | + | value so it's almost consistent with | + | the other zones at full speed. | + \*-----------------------------------------*/ + if(mode_value == SWIFT_MODE_VALUE && zone == 0) + { + usb_buf[0x06] = (speed + 1) * 3; + } + else + { + usb_buf[0x06] = speed; + } + + hid_send_feature_report(dev,usb_buf, PACKET_DATA_LENGTH); + + /*-----------------------------------------*\ + | Define brightness | + \*-----------------------------------------*/ + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x01] = PACKET_START; + usb_buf[0x02] = ACTION_SET; + + usb_buf[0x05] = m.zone_brightness_byte[zone]; + usb_buf[0x06] = brightness; + + offset = m.zone_brightness_mapping[zone]; + + for(unsigned int i = 0; i < COLORS_SIZE; i++) + { + if(mode_value == BREATHING_MODE_VALUE && i >= 4 && zone == 2) + { + usb_buf[0x05] = 0x01; + } + + usb_buf[0x04] = offset; + hid_send_feature_report(dev,usb_buf, PACKET_DATA_LENGTH); + + offset += 4; + } + + Apply(); +} + +void CougarRevengerSTController::SendColourPacket(unsigned char address, unsigned char value, unsigned char *buffer) +{ + buffer[0x04] = address; + buffer[0x06] = value; + + hid_send_feature_report(dev, buffer, PACKET_DATA_LENGTH); +} + +void CougarRevengerSTController::ActivateMode(unsigned char zone, unsigned char mode_value) +{ + const cougar_mode& m = modes_mapping.at(mode_value); + + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x01] = PACKET_START; + usb_buf[0x02] = ACTION_SET; + usb_buf[0x04] = m.zone_mode_mapping[zone]; // zone dependent? + usb_buf[0x05] = m.zone_mode_byte[zone]; + usb_buf[0x06] = mode_value; + + hid_send_feature_report(dev,usb_buf, PACKET_DATA_LENGTH); +} + +void CougarRevengerSTController::Apply() +{ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x01] = PACKET_START; + usb_buf[0x02] = 0x03; + usb_buf[0x03] = 0x03; + usb_buf[0x04] = 0x03; + + hid_send_feature_report(dev,usb_buf, PACKET_DATA_LENGTH); +} diff --git a/Controllers/CougarController/CougarRevengerSTController/CougarRevengerSTController.h b/Controllers/CougarController/CougarRevengerSTController/CougarRevengerSTController.h new file mode 100644 index 0000000..4fea64b --- /dev/null +++ b/Controllers/CougarController/CougarRevengerSTController/CougarRevengerSTController.h @@ -0,0 +1,251 @@ +/*---------------------------------------------------------*\ +| CougarRevengerSTController.h | +| | +| Driver for Cougar Revenger ST | +| | +| Morgan Guimard (morg) 17 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include +#include +#include + +#define PACKET_DATA_LENGTH 9 + +enum +{ + PACKET_START = 0xC4, + ACTION_SET = 0x0F +}; + +enum +{ + MIN_BRIGHTNESS = 0x00, + MAX_BRIGHTNESS = 0xFF, + MIN_SPEED_SWIFT = 0x2F, + MIN_SPEED = 0x0F, + MAX_SPEED = 0x00, + COLORS_SIZE = 0x07 +}; + +enum +{ + OFF_MODE_VALUE = 0x00, + DIRECT_MODE_VALUE = 0x01, + BREATHING_MODE_VALUE = 0x02, + FLOW_MODE_VALUE = 0x03, + SWIFT_MODE_VALUE = 0x04, + FLOW_LEFT_MODE_VALUE = 0x09, + FLOW_RIGHT_MODE_VALUE = 0x0B +}; + +typedef struct +{ + unsigned char zone_mode_mapping[3]; + unsigned char zone_mode_byte[3]; + unsigned char zone_rgb_mapping[3]; + unsigned char zone_rgb_byte[3]; + unsigned char zone_brightness_mapping[3]; + unsigned char zone_brightness_byte[3]; + unsigned char zone_speed_mapping[3]; + unsigned char zone_speed_byte[3]; +} cougar_mode; + +const cougar_mode DIRECT_MODE = +{ + { + 0x77, 0x55, 0xE6 + }, + { + 0x01, 0x00, 0x00 + }, + { + 0x79, 0x57, 0xE8 + }, + { + 0x01, 0x00, 0x00 + }, + { + 0x78, 0x56, 0xE7 + }, + { + 0x01, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00 + }, + { + 0x01, 0x00, 0x00 + } +}; + +const cougar_mode OFF_MODE = +{ + { + 0x77, 0x55, 0xE6 + }, + { + 0x01, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00 + } +}; + +const cougar_mode BREATHING_MODE = +{ + { + 0x77, 0x55, 0xE6 + }, + { + 0x01, 0x00, 0x00 + }, + { + 0x85, 0x63, 0xF4 + }, + { + 0x01, 0x00, 0x00 + }, + { + 0x84, 0x62, 0xF3 + }, + { + 0x01, 0x00, 0x00 + }, + { + 0x7C, 0x5A, 0xEB + }, + { + 0x01, 0x00, 0x00 + } +}; + +const cougar_mode SWIFT_MODE = +{ + { + 0x77, 0x55, 0xE6 + }, + { + 0x01, 0x00, 0x00 + }, + { + 0xB0, 0x8E, 0x1F + }, + { + 0x01, 0x00, 0x01 + }, + { + 0xAF, 0x8D, 0x1E + }, + { + 0x01, 0x00, 0x01 + }, + { + 0xAD, 0x8B, 0x1C + }, + { + 0x01, 0x00, 0x01 + } +}; + +const cougar_mode FLOW_MODE = +{ + { + 0x77, 0x00, 0xE6 + }, + { + 0x01, 0x00, 0x00 + }, + { + 0xB0, 0x00, 0x1F + }, + { + 0x01, 0x00, 0x01 + }, + { + 0xAF, 0x00, 0x1E + }, + { + 0x01, 0x00, 0x01 + }, + { + 0xAD, 0x00, 0x1C + }, + { + 0x01, 0x00, 0x01 + } +}; + +static const std::map modes_mapping = +{ + { + DIRECT_MODE_VALUE, + DIRECT_MODE + }, + { + OFF_MODE_VALUE, + OFF_MODE + }, + { + BREATHING_MODE_VALUE, + BREATHING_MODE + }, + { + SWIFT_MODE_VALUE, + SWIFT_MODE + }, + { + FLOW_LEFT_MODE_VALUE, + FLOW_MODE + }, + { + FLOW_RIGHT_MODE_VALUE, + FLOW_MODE + } +}; + +class CougarRevengerSTController +{ +public: + CougarRevengerSTController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~CougarRevengerSTController(); + + std::string GetSerialString(); + std::string GetDeviceLocation(); + std::string GetFirmwareVersion(); + std::string GetNameString(); + + void ActivateMode(unsigned char zone, unsigned char mode_value); + void SetDirect(unsigned char zone, RGBColor color, unsigned char brightness); + void SetModeData(unsigned char zone, unsigned char mode_value, std::vector colors, unsigned char brightness, unsigned char speed); + +private: + hid_device* dev; + std::string location; + std::string name; + std::string version; + + void Apply(); + void SendColourPacket(unsigned char address, unsigned char value, unsigned char * buffer); +}; diff --git a/Controllers/CougarController/CougarRevengerSTController/RGBController_CougarRevengerST.cpp b/Controllers/CougarController/CougarRevengerSTController/RGBController_CougarRevengerST.cpp new file mode 100644 index 0000000..61153c0 --- /dev/null +++ b/Controllers/CougarController/CougarRevengerSTController/RGBController_CougarRevengerST.cpp @@ -0,0 +1,199 @@ +/*---------------------------------------------------------*\ +| RGBController_CougarRevengerST.cpp | +| | +| RGBController for Cougar Revenger ST | +| | +| Morgan Guimard (morg) 17 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_CougarRevengerST.h" + +/**------------------------------------------------------------------*\ + @name Cougar Revenger ST + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCougarRevengerSTControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CougarRevengerST::RGBController_CougarRevengerST(CougarRevengerSTController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Cougar"; + type = DEVICE_TYPE_MOUSE; + description = "Cougar Revenger ST Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = DIRECT_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = MIN_BRIGHTNESS; + Direct.brightness_max = MAX_BRIGHTNESS; + Direct.brightness = MAX_BRIGHTNESS; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = OFF_MODE_VALUE; + Off.flags = MODE_FLAG_HAS_BRIGHTNESS; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.brightness = MAX_BRIGHTNESS; + Breathing.brightness_min = MIN_BRIGHTNESS; + Breathing.brightness_max = MAX_BRIGHTNESS; + Breathing.speed = MAX_SPEED; + Breathing.speed_min = MIN_SPEED; + Breathing.speed_max = MAX_SPEED; + Breathing.colors_min = COLORS_SIZE; + Breathing.colors_max = COLORS_SIZE; + Breathing.colors.resize(COLORS_SIZE); + modes.push_back(Breathing); + + mode Swift; + Swift.name = "Swift"; + Swift.value = SWIFT_MODE_VALUE; + Swift.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Swift.color_mode = MODE_COLORS_MODE_SPECIFIC; + Swift.brightness = MAX_BRIGHTNESS; + Swift.brightness_min = MIN_BRIGHTNESS; + Swift.brightness_max = MAX_BRIGHTNESS; + Swift.speed = MAX_SPEED; + Swift.speed_min = MIN_SPEED_SWIFT; + Swift.speed_max = MAX_SPEED; + Swift.colors_min = COLORS_SIZE; + Swift.colors_max = COLORS_SIZE; + Swift.colors.resize(COLORS_SIZE); + modes.push_back(Swift); + + mode Flow; + Flow.name = "Flow"; + Flow.value = FLOW_MODE_VALUE; + Flow.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Flow.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flow.brightness = MAX_BRIGHTNESS; + Flow.brightness_min = MIN_BRIGHTNESS; + Flow.brightness_max = MAX_BRIGHTNESS; + Flow.speed = MAX_SPEED; + Flow.speed_min = MIN_SPEED; + Flow.speed_max = MAX_SPEED; + Flow.colors_min = COLORS_SIZE; + Flow.colors_max = COLORS_SIZE; + Flow.direction = MODE_DIRECTION_LEFT; + Flow.colors.resize(COLORS_SIZE); + modes.push_back(Flow); + + SetupZones(); +} + +RGBController_CougarRevengerST::~RGBController_CougarRevengerST() +{ + delete controller; +} + +void RGBController_CougarRevengerST::SetupZones() +{ + zone new_zone; + + new_zone.name = "Bottom"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + zones.push_back(new_zone); + + new_zone.name = "Mouse wheel"; + zones.push_back(new_zone); + + new_zone.name = "Logo"; + zones.push_back(new_zone); + + leds.resize(zones.size()); + + for(unsigned int i = 0; i < leds.size(); i++) + { + leds[i].name = "LED " + std::to_string(i+1); + } + + SetupColors(); +} + +void RGBController_CougarRevengerST::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CougarRevengerST::DeviceUpdateLEDs() +{ + for(unsigned int i = 0; i < colors.size(); i++) + { + UpdateZoneLEDs(i); + } +} + +void RGBController_CougarRevengerST::UpdateZoneLEDs(int zone) +{ + controller->SetDirect(zone, colors[zone], modes[active_mode].brightness); +} + +void RGBController_CougarRevengerST::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_CougarRevengerST::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case DIRECT_MODE_VALUE: + case OFF_MODE_VALUE: + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->ActivateMode(zone_idx, modes[active_mode].value); + } + break; + case BREATHING_MODE_VALUE: + case SWIFT_MODE_VALUE: + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->ActivateMode(zone_idx, modes[active_mode].value); + controller->SetModeData(zone_idx, modes[active_mode].value, modes[active_mode].colors, modes[active_mode].brightness, modes[active_mode].speed); + } + break; + + case FLOW_MODE_VALUE: + + unsigned char mode_value = modes[active_mode].direction == MODE_DIRECTION_LEFT ? FLOW_LEFT_MODE_VALUE : FLOW_RIGHT_MODE_VALUE; + + controller->ActivateMode(0, mode_value); + controller->ActivateMode(1, OFF_MODE_VALUE); + controller->ActivateMode(2, mode_value); + + controller->SetModeData(0, mode_value, modes[active_mode].colors, modes[active_mode].brightness, modes[active_mode].speed); + controller->SetModeData(2, mode_value, modes[active_mode].colors, modes[active_mode].brightness, modes[active_mode].speed); + + break; + } +} diff --git a/Controllers/CougarController/CougarRevengerSTController/RGBController_CougarRevengerST.h b/Controllers/CougarController/CougarRevengerSTController/RGBController_CougarRevengerST.h new file mode 100644 index 0000000..6a699be --- /dev/null +++ b/Controllers/CougarController/CougarRevengerSTController/RGBController_CougarRevengerST.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_CougarRevengerST.h | +| | +| RGBController for Cougar Revenger ST | +| | +| Morgan Guimard (morg) 17 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CougarRevengerSTController.h" + +class RGBController_CougarRevengerST : public RGBController +{ +public: + RGBController_CougarRevengerST(CougarRevengerSTController* controller_ptr); + ~RGBController_CougarRevengerST(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CougarRevengerSTController* controller; +}; diff --git a/Controllers/CreativeController/CreativeControllerDetect.cpp b/Controllers/CreativeController/CreativeControllerDetect.cpp new file mode 100644 index 0000000..5f508e1 --- /dev/null +++ b/Controllers/CreativeController/CreativeControllerDetect.cpp @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| CreativeControllerDetect.cpp | +| | +| Detector for Creative devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "CreativeSoundBlasterXG6Controller.h" +#include "RGBController_CreativeSoundBlasterXG6.h" +#include "Detector.h" + +/*-----------------------------------------------------*\ +| Creative vendor ID | +\*-----------------------------------------------------*/ +#define CREATIVE_VID 0x041E +/*-----------------------------------------------------*\ +| SoundCards | +\*-----------------------------------------------------*/ +#define CREATIVE_SOUNDBLASTERX_G6_PID 0x3256 + +void DetectCreativeDevice(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CreativeSoundBlasterXG6Controller* controller = new CreativeSoundBlasterXG6Controller(dev, info->path, name); + RGBController_CreativeSoundBlasterXG6* rgb_controller = new RGBController_CreativeSoundBlasterXG6(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Sound Cards | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_I("Creative SoundBlasterX G6", DetectCreativeDevice, CREATIVE_VID, CREATIVE_SOUNDBLASTERX_G6_PID, 4); diff --git a/Controllers/CreativeController/CreativeSoundBlasterAE5ControllerBase.h b/Controllers/CreativeController/CreativeSoundBlasterAE5ControllerBase.h new file mode 100644 index 0000000..4bc64de --- /dev/null +++ b/Controllers/CreativeController/CreativeSoundBlasterAE5ControllerBase.h @@ -0,0 +1,28 @@ +/*---------------------------------------------------------*\ +| CreativeSoundBlasterAE5ControllerBase.h | +| | +| Base interface for Creative SoundBlaster AE-5 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +class CreativeSoundBlasterAE5ControllerBase +{ +public: + virtual ~CreativeSoundBlasterAE5ControllerBase() = default; + + virtual bool Initialize() = 0; + virtual std::string GetDeviceLocation() = 0; + virtual std::string GetDeviceName() = 0; + virtual unsigned int GetLEDCount() = 0; + virtual unsigned int GetExternalLEDCount() = 0; + virtual void SetExternalLEDCount(unsigned int count) = 0; + + virtual void SetLEDColors(unsigned char led_count, unsigned char* red_values, + unsigned char* green_values, unsigned char* blue_values) = 0; +}; \ No newline at end of file diff --git a/Controllers/CreativeController/CreativeSoundBlasterAE5ControllerDetect_Windows.cpp b/Controllers/CreativeController/CreativeSoundBlasterAE5ControllerDetect_Windows.cpp new file mode 100644 index 0000000..78705b7 --- /dev/null +++ b/Controllers/CreativeController/CreativeSoundBlasterAE5ControllerDetect_Windows.cpp @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| CreativeSoundBlasterAE5ControllerDetect_Windows.cpp | +| | +| Detector for Creative SoundBlaster AE-5 (Windows) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "CreativeSoundBlasterAE5Controller_Windows.h" +#include "RGBController_CreativeSoundBlasterAE5_Windows.h" +#include "LogManager.h" + +void DetectCreativeAE5Device() +{ + LOG_INFO("[Creative SoundBlaster AE-5] Windows detection function called"); + + CreativeSoundBlasterAE5Controller_Windows* controller = new CreativeSoundBlasterAE5Controller_Windows(); + + if(controller->Initialize()) + { + LOG_INFO("[Creative SoundBlaster AE-5] Device initialized successfully, registering controller"); + RGBController_CreativeSoundBlasterAE5* rgb_controller = new RGBController_CreativeSoundBlasterAE5(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + LOG_WARNING("[Creative SoundBlaster AE-5] Device initialization failed"); + delete controller; + } +} + +REGISTER_DETECTOR("Creative SoundBlaster AE-5", DetectCreativeAE5Device); \ No newline at end of file diff --git a/Controllers/CreativeController/CreativeSoundBlasterAE5Controller_Windows.cpp b/Controllers/CreativeController/CreativeSoundBlasterAE5Controller_Windows.cpp new file mode 100644 index 0000000..a40aeca --- /dev/null +++ b/Controllers/CreativeController/CreativeSoundBlasterAE5Controller_Windows.cpp @@ -0,0 +1,451 @@ +/*---------------------------------------------------------*\ +| CreativeSoundBlasterAE5Controller_Windows.cpp | +| | +| Driver for Creative SoundBlaster AE-5 (Windows) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "CreativeSoundBlasterAE5Controller_Windows.h" +#include "LogManager.h" +#include + +/*---------------------------------------------------------*\ +| Lifecycle | +\*---------------------------------------------------------*/ + +CreativeSoundBlasterAE5Controller_Windows::CreativeSoundBlasterAE5Controller_Windows() +{ + name = "Creative SoundBlaster AE-5"; + location = ""; + device_found = false; + device_handle = INVALID_HANDLE_VALUE; + device_opened = false; + external_led_count = 0; + led_mutex = CreateMutexA(NULL, FALSE, "OpenRGB_AE5_LED_Mutex"); +} + +CreativeSoundBlasterAE5Controller_Windows::~CreativeSoundBlasterAE5Controller_Windows() +{ + CloseDevice(); + if(led_mutex != NULL) + { + CloseHandle(led_mutex); + led_mutex = NULL; + } +} + +bool CreativeSoundBlasterAE5Controller_Windows::Initialize() +{ + if(!FindDevice()) + { + return false; + } + + hdaudio_device_path = FindHDAudioDevicePath(); + if(hdaudio_device_path.empty()) + { + LOG_WARNING("[%s] Failed to find HDAudio device path", name.c_str()); + return false; + } + + return OpenDevice(); +} + +/*---------------------------------------------------------*\ +| Device Discovery | +\*---------------------------------------------------------*/ + +bool CreativeSoundBlasterAE5Controller_Windows::FindDevice() +{ + LOG_DEBUG("[%s] Looking for vendor 0x%04X, device 0x%04X", name.c_str(), AE5_VENDOR_ID, AE5_DEVICE_ID); + + HDEVINFO device_info_set = SetupDiGetClassDevs( + NULL, // No class GUID + TEXT("PCI"), // Enumerator + NULL, // No parent window + DIGCF_PRESENT | DIGCF_ALLCLASSES // Only present devices + ); + + if(device_info_set == INVALID_HANDLE_VALUE) + { + LOG_ERROR("[%s] SetupDiGetClassDevs failed: %lu", name.c_str(), GetLastError()); + return false; + } + + SP_DEVINFO_DATA device_info_data; + device_info_data.cbSize = sizeof(SP_DEVINFO_DATA); + + for(DWORD device_index = 0; SetupDiEnumDeviceInfo(device_info_set, device_index, &device_info_data); device_index++) + { + TCHAR hardware_id[256]; + if(!SetupDiGetDeviceRegistryProperty(device_info_set, &device_info_data, SPDRP_HARDWAREID, + NULL, (PBYTE)hardware_id, sizeof(hardware_id), NULL)) + { + continue; + } + + std::string hw_id_str(hardware_id); + std::transform(hw_id_str.begin(), hw_id_str.end(), hw_id_str.begin(), ::toupper); + + if(hw_id_str.find("PCI\\VEN_1102&DEV_0012") != std::string::npos) + { + + /*---------------------------------------------*\ + | Check subsystem ID to determine AE-5 variant | + | Format is SUBSYS_{DEVICE_ID}{VENDOR_ID} | + \*---------------------------------------------*/ + + if(hw_id_str.find("SUBSYS_01911102") != std::string::npos) + { + name = "Creative SoundBlaster AE-5 Plus"; + } + else if(hw_id_str.find("SUBSYS_00511102") != std::string::npos) + { + name = "Creative SoundBlaster AE-5"; + } + else + { + name = "Creative SoundBlaster AE-5"; + LOG_WARNING("[%s] Unknown subsystem variant found in hardware ID: %s", name.c_str(), hardware_id); + LOG_WARNING("[%s] Please report this to @eclipse_sol84 in the OpenRGB Discord!", name.c_str()); + } + + LOG_INFO("[%s] Found matching device: %s", name.c_str(), hardware_id); + + TCHAR location_info[256]; + if(SetupDiGetDeviceRegistryProperty(device_info_set, &device_info_data, SPDRP_LOCATION_INFORMATION, + NULL, (PBYTE)location_info, sizeof(location_info), NULL)) + { + location = "PCI: " + std::string(location_info); + } + else + { + location = "PCI: " + hw_id_str; + } + + device_found = true; + SetupDiDestroyDeviceInfoList(device_info_set); + + LOG_INFO("[%s] Device successfully detected at %s", name.c_str(), location.c_str()); + return true; + } + } + + SetupDiDestroyDeviceInfoList(device_info_set); + LOG_WARNING("[%s] No matching device found", name.c_str()); + return false; +} + +std::string CreativeSoundBlasterAE5Controller_Windows::FindHDAudioDevicePath() +{ + LOG_INFO("[%s] Searching for HDAudio device path...", name.c_str()); + + HKEY device_classes_key; + LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\DeviceClasses", + 0, KEY_READ, &device_classes_key); + + if(result != ERROR_SUCCESS) + { + LOG_ERROR("[%s] Failed to open DeviceClasses registry key: %lu", name.c_str(), result); + return ""; + } + + DWORD guid_index = 0; + CHAR guid_name[256]; + DWORD guid_name_size; + + /*---------------------------------------------------------*\ + | Enumerate all GUID folders in DeviceClasses | + | Then enumerate device instances to find VID & PID | + | Finally, check if this device has a #GPDHDA subkey | + \*---------------------------------------------------------*/ + + while(true) + { + guid_name_size = sizeof(guid_name); + result = RegEnumKeyExA(device_classes_key, guid_index, guid_name, &guid_name_size, + NULL, NULL, NULL, NULL); + + if(result != ERROR_SUCCESS) break; + + HKEY guid_key; + result = RegOpenKeyExA(device_classes_key, guid_name, 0, KEY_READ, &guid_key); + if(result != ERROR_SUCCESS) + { + guid_index++; + continue; + } + + DWORD device_index = 0; + CHAR device_name[512]; + DWORD device_name_size; + + while(true) + { + device_name_size = sizeof(device_name); + result = RegEnumKeyExA(guid_key, device_index, device_name, &device_name_size, + NULL, NULL, NULL, NULL); + + if(result != ERROR_SUCCESS) break; + + std::string device_str(device_name); + std::transform(device_str.begin(), device_str.end(), device_str.begin(), ::toupper); + + if(device_str.find("VEN_1102&DEV_0011") != std::string::npos) + { + HKEY device_key; + result = RegOpenKeyExA(guid_key, device_name, 0, KEY_READ, &device_key); + if(result == ERROR_SUCCESS) + { + HKEY gpdhda_key; + result = RegOpenKeyExA(device_key, "#GPDHDA", 0, KEY_READ, &gpdhda_key); + if(result == ERROR_SUCCESS) + { + LOG_INFO("[%s] Found HDAudio device interface", name.c_str()); + RegCloseKey(gpdhda_key); + RegCloseKey(device_key); + + /*---------------------------------------------------------*\ + | Convert registry path format to device path format | + \*---------------------------------------------------------*/ + + std::string device_path(device_name); + std::transform(device_path.begin(), device_path.end(), device_path.begin(), ::tolower); + + if(device_path.substr(0, 4) == "##?#") + { + device_path = "\\\\?\\" + device_path.substr(4); + } + + device_path += "\\gpdhda"; + + RegCloseKey(guid_key); + RegCloseKey(device_classes_key); + return device_path; + } + RegCloseKey(device_key); + } + } + + device_index++; + } + + RegCloseKey(guid_key); + guid_index++; + } + + RegCloseKey(device_classes_key); + LOG_WARNING("[%s] No Creative HDAudio device found in registry", name.c_str()); + return ""; +} + +bool CreativeSoundBlasterAE5Controller_Windows::OpenDevice() +{ + if(hdaudio_device_path.empty()) + { + LOG_ERROR("[%s] No HDAudio device path available", name.c_str()); + return false; + } + + LOG_INFO("[%s] Opening device: %s", name.c_str(), hdaudio_device_path.c_str()); + + std::wstring wide_path(hdaudio_device_path.begin(), hdaudio_device_path.end()); + + device_handle = CreateFileW( + wide_path.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL + ); + + if(device_handle == INVALID_HANDLE_VALUE) + { + DWORD error = GetLastError(); + LOG_ERROR("[%s] Failed to open device: Error %lu", name.c_str(), error); + + if(error == ERROR_PATH_NOT_FOUND) // Error 3 + { + LOG_ERROR("[%s] This device requires Creative's official drivers to be installed for RGB control", name.c_str()); + LOG_ERROR("[%s] Please install Creative Sound Blaster Command software or drivers from Creative's website", name.c_str()); + } + + return false; + } + + LOG_INFO("[%s] Device opened successfully", name.c_str()); + device_opened = true; + return true; +} + +void CreativeSoundBlasterAE5Controller_Windows::CloseDevice() +{ + if(device_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(device_handle); + device_handle = INVALID_HANDLE_VALUE; + device_opened = false; + LOG_INFO("[%s] Device closed", name.c_str()); + } +} + +/*---------------------------------------------------------*\ +| Getters | +\*---------------------------------------------------------*/ + +std::string CreativeSoundBlasterAE5Controller_Windows::GetDeviceLocation() +{ + return location; +} + +std::string CreativeSoundBlasterAE5Controller_Windows::GetDeviceName() +{ + return name; +} + +unsigned int CreativeSoundBlasterAE5Controller_Windows::GetLEDCount() +{ + return AE5_INTERNAL_LED_COUNT + external_led_count; +} + +unsigned int CreativeSoundBlasterAE5Controller_Windows::GetExternalLEDCount() +{ + return external_led_count; +} + +void CreativeSoundBlasterAE5Controller_Windows::SetExternalLEDCount(unsigned int count) +{ + external_led_count = count; +} + +/*---------------------------------------------------------*\ +| LED Control | +\*---------------------------------------------------------*/ + +void CreativeSoundBlasterAE5Controller_Windows::SetLEDColors(unsigned char led_count, unsigned char* red_values, + unsigned char* green_values, unsigned char* blue_values) +{ + if(!device_opened || device_handle == INVALID_HANDLE_VALUE) + { + LOG_ERROR("[%s] Device not opened, cannot set LED colors", name.c_str()); + return; + } + + if(led_count == 0) + { + return; + } + + /*----------------------------------------------------------*\ + | Wait for mutex to prevent conflicts with Creative software | + \*----------------------------------------------------------*/ + + if(led_mutex != NULL) + { + DWORD wait_result = WaitForSingleObject(led_mutex, 5000); // 5 second timeout + if(wait_result != WAIT_OBJECT_0) + { + LOG_WARNING("[%s] Failed to acquire LED mutex, proceeding anyway", name.c_str()); + } + } + + /*---------------------------------------------------------------*\ + | Internal LEDs - Setting LEDs can sometimes | + | not change them fully, we send this command twice to ensure | + | they fully change colors. It's particularly visable when | + | switching to dim colors or turning off the LEDs. | + \*---------------------------------------------------------------*/ + + if(led_count > 0) + { + SendLEDCommand(0x03, AE5_INTERNAL_LED_COUNT, red_values, green_values, blue_values); + SendLEDCommand(0x03, AE5_INTERNAL_LED_COUNT, red_values, green_values, blue_values); + } + + /*---------------------------------------------------------*\ + | External LEDs | + \*---------------------------------------------------------*/ + + if(led_count > AE5_INTERNAL_LED_COUNT && external_led_count > 0) + { + SendLEDCommand(0x02, external_led_count, red_values + AE5_INTERNAL_LED_COUNT, green_values + AE5_INTERNAL_LED_COUNT, blue_values + AE5_INTERNAL_LED_COUNT); + } + + if(led_mutex != NULL) + { + ReleaseMutex(led_mutex); + } +} + +bool CreativeSoundBlasterAE5Controller_Windows::SendLEDCommand(BYTE command_byte, unsigned int led_count_to_set, + unsigned char* red_values, unsigned char* green_values, unsigned char* blue_values) +{ + AE5_LED_Command cmd; + memset(&cmd, 0, sizeof(AE5_LED_Command)); + + cmd.command = command_byte; + cmd.packet_led_count = led_count_to_set; + + /*---------------------------------------------------------*\ + | Calculate data length | + \*---------------------------------------------------------*/ + + unsigned int data_length = led_count_to_set * 4; + cmd.data_length_low = data_length & 0xFF; + cmd.data_length_high = (data_length >> 8) & 0xFF; + + /*---------------------------------------------------------*\ + | Fill LED data | + \*---------------------------------------------------------*/ + + for(unsigned int i = 0; i < led_count_to_set; i++) + { + unsigned int offset = i * 4; + + if(command_byte == 0x02) // External WS2812B LED Strip + { + cmd.led_data[offset + 0] = blue_values[i]; + cmd.led_data[offset + 1] = red_values[i]; + cmd.led_data[offset + 2] = green_values[i]; + cmd.led_data[offset + 3] = 0x00; + } + else // Internal APA102 LED Strip + { + cmd.led_data[offset + 0] = red_values[i]; + cmd.led_data[offset + 1] = green_values[i]; + cmd.led_data[offset + 2] = blue_values[i]; + cmd.led_data[offset + 3] = 0xFF; + } + } + + AE5_LED_Command output_cmd; + memset(&output_cmd, 0, sizeof(AE5_LED_Command)); + DWORD bytes_returned = 0; + + BOOL result = DeviceIoControl( + device_handle, + 0x77772400, // Custom IOCTL command from Creative Lab's drivers + &cmd, + sizeof(AE5_LED_Command), + &output_cmd, + sizeof(AE5_LED_Command), + &bytes_returned, + NULL + ); + + if(!result) + { + DWORD error = GetLastError(); + LOG_ERROR("[%s] DeviceIoControl failed: Error %lu", name.c_str(), error); + return false; + } + + return true; +} diff --git a/Controllers/CreativeController/CreativeSoundBlasterAE5Controller_Windows.h b/Controllers/CreativeController/CreativeSoundBlasterAE5Controller_Windows.h new file mode 100644 index 0000000..ce94119 --- /dev/null +++ b/Controllers/CreativeController/CreativeSoundBlasterAE5Controller_Windows.h @@ -0,0 +1,74 @@ +/*---------------------------------------------------------*\ +| CreativeSoundBlasterAE5Controller_Windows.h | +| | +| Driver for Creative SoundBlaster AE-5 (Windows) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "CreativeSoundBlasterAE5ControllerBase.h" + +#pragma pack(push, 1) +struct AE5_LED_Command +{ + BYTE command; // 0x03 for internal, 0x02 for external + BYTE padding1[11]; // 11 zero bytes + BYTE packet_led_count; // Number of LEDs in this packet + BYTE padding2[3]; // 3 zero bytes + BYTE data_length_low; // Low byte of data length (LEDs × 4) + BYTE data_length_high; // High byte of data length + BYTE padding3[2]; // 2 zero bytes + BYTE led_data[400]; // LED data (RGBA), max 100 LEDs × 4 bytes + BYTE padding4[624]; // Rest filled with zeros (1044 - 420 bytes used) +}; +#pragma pack(pop) + +class CreativeSoundBlasterAE5Controller_Windows : public CreativeSoundBlasterAE5ControllerBase +{ +public: + CreativeSoundBlasterAE5Controller_Windows(); + ~CreativeSoundBlasterAE5Controller_Windows(); + + bool Initialize(); + std::string GetDeviceLocation(); + std::string GetDeviceName(); + unsigned int GetLEDCount(); + unsigned int GetExternalLEDCount(); + void SetExternalLEDCount(unsigned int count); + + void SetLEDColors(unsigned char led_count, unsigned char* red_values, + unsigned char* green_values, unsigned char* blue_values); + +private: + bool FindDevice(); + std::string FindHDAudioDevicePath(); + bool OpenDevice(); + void CloseDevice(); + bool SendLEDCommand(BYTE command_byte, unsigned int led_count_to_set, + unsigned char* red_values, unsigned char* green_values, unsigned char* blue_values); + + + std::string location; + std::string name; + std::string hdaudio_device_path; + bool device_found; + + HANDLE device_handle; + bool device_opened; + + HANDLE led_mutex; + + unsigned int external_led_count; + +#define AE5_VENDOR_ID 0x1102 +#define AE5_DEVICE_ID 0x0012 +#define AE5_INTERNAL_LED_COUNT 5 +#define AE5_EXTERNAL_LED_COUNT_MAX 100 +}; diff --git a/Controllers/CreativeController/CreativeSoundBlasterXG6Controller.cpp b/Controllers/CreativeController/CreativeSoundBlasterXG6Controller.cpp new file mode 100644 index 0000000..25c0ef6 --- /dev/null +++ b/Controllers/CreativeController/CreativeSoundBlasterXG6Controller.cpp @@ -0,0 +1,100 @@ +/*---------------------------------------------------------*\ +| CreativeSoundBlasterXG6Controller.cpp | +| | +| Driver for Creative SoundBlaster XG6 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "CreativeSoundBlasterXG6Controller.h" + +CreativeSoundBlasterXG6Controller::CreativeSoundBlasterXG6Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +CreativeSoundBlasterXG6Controller::~CreativeSoundBlasterXG6Controller() +{ + hid_close(dev); +} + +std::string CreativeSoundBlasterXG6Controller::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string CreativeSoundBlasterXG6Controller::GetDeviceName() +{ + return(name); +} + +void CreativeSoundBlasterXG6Controller::SetLedColor (unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness) +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + //5A 3A 02 06 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + usb_buf[0x01] = 0x5A; + usb_buf[0x02] = 0x3A; + usb_buf[0x03] = 0x02; + usb_buf[0x04] = 0x06; + usb_buf[0x05] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, 64); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + //5A 3A 06 04 00 03 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + usb_buf[0x01] = 0x5A; + usb_buf[0x02] = 0x3A; + usb_buf[0x03] = 0x06; + usb_buf[0x04] = 0x04; + usb_buf[0x06] = 0x03; + usb_buf[0x07] = 0x01; + usb_buf[0x09] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, 64); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + //5A 3A 09 0A 00 03 01 01 FF BB GG RR + usb_buf[0x01] = 0x5A; + usb_buf[0x02] = 0x3A; + usb_buf[0x03] = 0x09; + usb_buf[0x04] = 0x0A; + usb_buf[0x06] = 0x03; + usb_buf[0x07] = 0x01; + usb_buf[0x08] = 0x01; + usb_buf[0x09] = brightness; + + usb_buf[0x0A] = blue; + usb_buf[0x0B] = green; + usb_buf[0x0C] = red; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, 64); +} diff --git a/Controllers/CreativeController/CreativeSoundBlasterXG6Controller.h b/Controllers/CreativeController/CreativeSoundBlasterXG6Controller.h new file mode 100644 index 0000000..1e6f5fe --- /dev/null +++ b/Controllers/CreativeController/CreativeSoundBlasterXG6Controller.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| CreativeSoundBlasterXG6Controller.h | +| | +| Driver for Creative SoundBlaster XG6 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class CreativeSoundBlasterXG6Controller +{ +public: + CreativeSoundBlasterXG6Controller(hid_device* dev_handle, const char* path, std::string dev_name); + ~CreativeSoundBlasterXG6Controller(); + + void SetLedColor(unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/CreativeController/RGBController_CreativeSoundBlasterAE5_Windows.cpp b/Controllers/CreativeController/RGBController_CreativeSoundBlasterAE5_Windows.cpp new file mode 100644 index 0000000..74f8d20 --- /dev/null +++ b/Controllers/CreativeController/RGBController_CreativeSoundBlasterAE5_Windows.cpp @@ -0,0 +1,173 @@ +/*---------------------------------------------------------*\ +| RGBController_CreativeSoundBlasterAE5_Windows.cpp | +| | +| RGBController for Creative SoundBlaster AE-5 (Windows) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CreativeSoundBlasterAE5_Windows.h" + +/**------------------------------------------------------------------*\ + @name Creative Sound Blaster AE-5 + @category Audio + @type PCI + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectCreativeAE5Device + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CreativeSoundBlasterAE5::RGBController_CreativeSoundBlasterAE5(CreativeSoundBlasterAE5ControllerBase* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Creative Labs"; + type = DEVICE_TYPE_SPEAKER; + description = controller->GetDeviceName() + " Device"; + location = controller->GetDeviceLocation(); + serial = ""; + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_CreativeSoundBlasterAE5::~RGBController_CreativeSoundBlasterAE5() +{ + delete controller; +} + +void RGBController_CreativeSoundBlasterAE5::SetupZones() +{ + zone internal_zone; + internal_zone.name = "Internal"; + internal_zone.type = ZONE_TYPE_LINEAR; + internal_zone.leds_min = 5; + internal_zone.leds_max = 5; + internal_zone.leds_count = 5; + internal_zone.matrix_map = NULL; + zones.push_back(internal_zone); + + for(unsigned int led_idx = 0; led_idx < 5; led_idx++) + { + led new_led; + new_led.name = "Internal LED " + std::to_string(led_idx + 1); + leds.push_back(new_led); + } + + zone external_zone; + external_zone.name = "External"; + external_zone.type = ZONE_TYPE_LINEAR; + external_zone.leds_min = 0; + external_zone.leds_max = 100; + external_zone.leds_count = controller->GetExternalLEDCount(); + external_zone.matrix_map = NULL; + zones.push_back(external_zone); + + for(unsigned int led_idx = 0; led_idx < controller->GetExternalLEDCount(); led_idx++) + { + led new_led; + new_led.name = "External LED " + std::to_string(led_idx + 1); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_CreativeSoundBlasterAE5::ResizeZone(int zone, int new_size) +{ + if(zone == 1) // External zone + { + zones[zone].leds_count = new_size; + + leds.resize(5); + + for(unsigned int led_idx = 0; led_idx < (unsigned int)new_size; led_idx++) + { + led new_led; + new_led.name = "External LED " + std::to_string(led_idx + 1); + leds.push_back(new_led); + } + + controller->SetExternalLEDCount(new_size); + SetupColors(); + } +} + +void RGBController_CreativeSoundBlasterAE5::UpdateLEDRange(unsigned int start_led, unsigned int led_count) +{ + if(led_count == 0) + { + return; + } + + unsigned char* red_values = new unsigned char[led_count]; + unsigned char* green_values = new unsigned char[led_count]; + unsigned char* blue_values = new unsigned char[led_count]; + + for(unsigned int i = 0; i < led_count; i++) + { + unsigned int led_idx = start_led + i; + red_values[i] = RGBGetRValue(colors[led_idx]); + green_values[i] = RGBGetGValue(colors[led_idx]); + blue_values[i] = RGBGetBValue(colors[led_idx]); + } + + controller->SetLEDColors(led_count, red_values, green_values, blue_values); + + delete[] red_values; + delete[] green_values; + delete[] blue_values; +} + +void RGBController_CreativeSoundBlasterAE5::DeviceUpdateLEDs() +{ + UpdateLEDRange(0, controller->GetLEDCount()); +} + +void RGBController_CreativeSoundBlasterAE5::UpdateZoneLEDs(int zone) +{ + if(zone >= 0 && zone < (int)zones.size()) + { + unsigned int start_led = 0; + + for(int i = 0; i < zone; i++) + { + start_led += zones[i].leds_count; + } + + UpdateLEDRange(start_led, zones[zone].leds_count); + } +} + +void RGBController_CreativeSoundBlasterAE5::UpdateSingleLED(int led) +{ + /*-------------------------------------------------------------*\ + | Find which zone this LED belongs to and update only that zone | + \*-------------------------------------------------------------*/ + + unsigned int current_led = 0; + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(led >= (int)current_led && led < (int)(current_led + zones[zone_idx].leds_count)) + { + UpdateLEDRange(current_led, zones[zone_idx].leds_count); + return; + } + current_led += zones[zone_idx].leds_count; + } +} + +void RGBController_CreativeSoundBlasterAE5::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/CreativeController/RGBController_CreativeSoundBlasterAE5_Windows.h b/Controllers/CreativeController/RGBController_CreativeSoundBlasterAE5_Windows.h new file mode 100644 index 0000000..3a199aa --- /dev/null +++ b/Controllers/CreativeController/RGBController_CreativeSoundBlasterAE5_Windows.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_CreativeSoundBlasterAE5_Windows.h | +| | +| RGBController for Creative SoundBlaster AE-5 (Windows) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CreativeSoundBlasterAE5ControllerBase.h" + +class RGBController_CreativeSoundBlasterAE5: public RGBController +{ +public: + RGBController_CreativeSoundBlasterAE5(CreativeSoundBlasterAE5ControllerBase* controller_ptr); + ~RGBController_CreativeSoundBlasterAE5(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CreativeSoundBlasterAE5ControllerBase* controller; + void UpdateLEDRange(unsigned int start_led, unsigned int led_count); +}; \ No newline at end of file diff --git a/Controllers/CreativeController/RGBController_CreativeSoundBlasterXG6.cpp b/Controllers/CreativeController/RGBController_CreativeSoundBlasterXG6.cpp new file mode 100644 index 0000000..50f2abf --- /dev/null +++ b/Controllers/CreativeController/RGBController_CreativeSoundBlasterXG6.cpp @@ -0,0 +1,99 @@ +/*---------------------------------------------------------*\ +| RGBController_CreativeSoundBlasterXG6.cpp | +| | +| RGBController for Creative SoundBlaster XG6 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CreativeSoundBlasterXG6.h" + +/**------------------------------------------------------------------*\ + @name Creative Sound BlasterX G6 + @category Headset + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectCreativeDevice + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CreativeSoundBlasterXG6::RGBController_CreativeSoundBlasterXG6(CreativeSoundBlasterXG6Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Creative"; + type = DEVICE_TYPE_HEADSET; + description = "Creative SoundBlasterX G6 Device"; + location = controller->GetDeviceLocation(); + serial = ""; + + mode Static; + Static.name = "Direct"; + Static.value = 0; + Static.flags = MODE_COLORS_PER_LED | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = XG6_BRIGHTNESS_MIN; + Static.brightness_max = XG6_BRIGHTNESS_MAX; + Static.brightness = XG6_BRIGHTNESS_MAX; + modes.push_back(Static); + + SetupZones(); +} + +RGBController_CreativeSoundBlasterXG6::~RGBController_CreativeSoundBlasterXG6() +{ + delete controller; +} + +void RGBController_CreativeSoundBlasterXG6::SetupZones() +{ + zone logo_zone; + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + led logo_led; + logo_led.name = "Logo"; + leds.push_back(logo_led); + + SetupColors(); +} + +void RGBController_CreativeSoundBlasterXG6::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_CreativeSoundBlasterXG6::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetLedColor(red, grn, blu, modes[active_mode].brightness); +} + +void RGBController_CreativeSoundBlasterXG6::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CreativeSoundBlasterXG6::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_CreativeSoundBlasterXG6::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/CreativeController/RGBController_CreativeSoundBlasterXG6.h b/Controllers/CreativeController/RGBController_CreativeSoundBlasterXG6.h new file mode 100644 index 0000000..ed69985 --- /dev/null +++ b/Controllers/CreativeController/RGBController_CreativeSoundBlasterXG6.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_CreativeSoundBlasterXG6.h | +| | +| RGBController for Creative SoundBlaster XG6 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CreativeSoundBlasterXG6Controller.h" + +#define XG6_BRIGHTNESS_MIN 0x00 +#define XG6_BRIGHTNESS_MAX 0xFF + +class RGBController_CreativeSoundBlasterXG6: public RGBController +{ +public: + RGBController_CreativeSoundBlasterXG6(CreativeSoundBlasterXG6Controller* controller_ptr); + ~RGBController_CreativeSoundBlasterXG6(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CreativeSoundBlasterXG6Controller* controller; +}; diff --git a/Controllers/CrucialController/CrucialController.cpp b/Controllers/CrucialController/CrucialController.cpp new file mode 100644 index 0000000..658c467 --- /dev/null +++ b/Controllers/CrucialController/CrucialController.cpp @@ -0,0 +1,163 @@ +/*---------------------------------------------------------*\ +| CrucialController.cpp | +| | +| Driver for Crucial Ballistix RAM | +| | +| Adam Honse (CalcProgrammer1) 19 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController.h" +#include "CrucialController.h" + +CrucialController::CrucialController(i2c_smbus_interface* bus, crucial_dev_id dev) +{ + this->bus = bus; + this->dev = dev; + + for(int i = 0; i < 16; i++) + { + device_version[i] = CrucialRegisterRead(CRUCIAL_REG_DEVICE_VERSION + i); + } +} + +CrucialController::~CrucialController() +{ + +} + +std::string CrucialController::GetDeviceVersion() +{ + return(device_version); +} + +std::string CrucialController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +void CrucialController::SetMode(unsigned char mode) +{ + SendEffectMode(mode, 0x10); +} + +void CrucialController::SetAllColorsDirect(RGBColor* colors) +{ + SendDirectColors(colors); +} + +void CrucialController::SetAllColorsEffect(RGBColor* colors) +{ + for(int led_idx = 0; led_idx < 8; led_idx++) + { + unsigned char red = RGBGetRValue(colors[led_idx]); + unsigned char grn = RGBGetGValue(colors[led_idx]); + unsigned char blu = RGBGetBValue(colors[led_idx]); + SendEffectColor(led_idx, red, grn, blu); + } +} + +void CrucialController::SendBrightness(unsigned char brightness) +{ + CrucialRegisterWrite(0x82EE, 0xFF); + CrucialRegisterWrite(0x82EF, brightness); + CrucialRegisterWrite(0x82F0, 0x83); +} + +void CrucialController::SendEffectMode(unsigned char mode, unsigned char speed) +{ + CrucialRegisterWrite(0x820F, mode); + CrucialRegisterWrite(0x82EE, 0x00); + CrucialRegisterWrite(0x82EF, speed); + CrucialRegisterWrite(0x82F0, 0x84); +} + +void CrucialController::SendDirectColors(RGBColor* color_buf) +{ + unsigned char color_blk[8]; + + for(unsigned int led = 0; led < 8; led++) + { + color_blk[led] = RGBGetRValue(color_buf[led]); + } + + //Red Channels + CrucialRegisterWriteBlock(0x8300, color_blk, 8); + + for(unsigned int led = 0; led < 8; led++) + { + color_blk[led] = RGBGetGValue(color_buf[led]); + } + + //Green Channels + CrucialRegisterWriteBlock(0x8340, color_blk, 8); + + for(unsigned int led = 0; led < 8; led++) + { + color_blk[led] = RGBGetBValue(color_buf[led]); + } + + //Blue Channels + CrucialRegisterWriteBlock(0x8380, color_blk, 8); +} + +unsigned char CrucialController::CrucialRegisterRead(crucial_register reg) +{ + //Write Crucial register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Read Crucial value + return(bus->i2c_smbus_read_byte_data(dev, 0x81)); + +} + +void CrucialController::CrucialRegisterWrite(crucial_register reg, unsigned char val) +{ + //Write Crucial register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write Crucial value + bus->i2c_smbus_write_byte_data(dev, 0x01, val); + +} + +void CrucialController::CrucialRegisterWriteBlock(crucial_register reg, unsigned char * data, unsigned char sz) +{ + //Write Crucial register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write Crucial block data + if(bus->i2c_smbus_write_block_data(dev, 0x03, sz, data) == -1) + { + //Fall back to individual byte operations if the block operation fails + for(unsigned int block_byte = 0; block_byte < sz; block_byte++) + { + bus->i2c_smbus_write_byte_data(dev, 0x01, data[block_byte]); + } + } +} + +void CrucialController::SendEffectColor + ( + unsigned int led_idx, + unsigned int red, + unsigned int green, + unsigned int blue + ) +{ + CrucialRegisterWrite(0x82E9, (1 << led_idx)); + CrucialRegisterWrite(0x82EA, 0x00); + CrucialRegisterWrite(0x82EB, 0x00); + CrucialRegisterWrite(0x82EC, 0x00); + CrucialRegisterWrite(0x82ED, red); + CrucialRegisterWrite(0x82EE, green); + CrucialRegisterWrite(0x82EF, blue); + CrucialRegisterWrite(0x82F0, 0x01); +} diff --git a/Controllers/CrucialController/CrucialController.h b/Controllers/CrucialController/CrucialController.h new file mode 100644 index 0000000..60e5c70 --- /dev/null +++ b/Controllers/CrucialController/CrucialController.h @@ -0,0 +1,77 @@ +/*---------------------------------------------------------*\ +| CrucialController.h | +| | +| Driver for Crucial Ballistix RAM | +| | +| Adam Honse (CalcProgrammer1) 19 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "i2c_smbus.h" + +typedef unsigned char crucial_dev_id; +typedef unsigned short crucial_register; + +enum +{ + CRUCIAL_REG_DEVICE_VERSION = 0x1000, /* Version (Date) String 16 bytes */ + CRUCIAL_REG_MICRON_CHECK_1 = 0x1025, /* "Micron" string location 1 */ + CRUCIAL_REG_MICRON_CHECK_2 = 0x1030 /* "Micron" string location 2 */ +}; + +enum +{ + CRUCIAL_MODE_UNKNOWN = 0x00, /* We don't know what the mode is */ + CRUCIAL_MODE_SHIFT = 0x1F, /* Shift effect mode */ + CRUCIAL_MODE_GRADIENT_SHIFT = 0x2F, /* Gradient shift mode */ + CRUCIAL_MODE_FILL = 0x3F, /* Fill effect mode */ + CRUCIAL_MODE_STACK = 0x4F, /* Stack effect mode */ + CRUCIAL_MODE_DOUBLE_STACK = 0x5F, /* Double stack effect mode */ + CRUCIAL_MODE_BREATHING = 0x6F, /* Breathing effect mode */ + CRUCIAL_MODE_MOTION_POINT = 0x7F, /* Motion point effect mode */ + CRUCIAL_MODE_INSIDE_OUT = 0x8F, /* Inside out effect mode */ + CRUCIAL_MODE_COLOR_STEP = 0x9F, /* Color step effect mode */ + CRUCIAL_MODE_WATER_WAVE = 0xAF, /* Water wave effect mode */ + CRUCIAL_MODE_FLASHING = 0xBF, /* Flashing effect mode */ + CRUCIAL_MODE_STATIC = 0xCF, /* Static effect mode */ +}; + +class CrucialController +{ +public: + CrucialController(i2c_smbus_interface* bus, crucial_dev_id dev); + ~CrucialController(); + + std::string GetDeviceVersion(); + std::string GetDeviceLocation(); + void SetAllColorsDirect(RGBColor* colors); + void SetAllColorsEffect(RGBColor* colors); + void SetMode(unsigned char mode); + + unsigned char CrucialRegisterRead(crucial_register reg); + void CrucialRegisterWrite(crucial_register reg, unsigned char val); + void CrucialRegisterWriteBlock(crucial_register reg, unsigned char * data, unsigned char sz); + +private: + char device_version[16]; + i2c_smbus_interface * bus; + crucial_dev_id dev; + + void SendEffectColor + ( + unsigned int led_idx, + unsigned int red, + unsigned int green, + unsigned int blue + ); + + void SendDirectColors(RGBColor* color_buf); + void SendBrightness(unsigned char brightness); + void SendEffectMode(unsigned char mode, unsigned char speed); +}; diff --git a/Controllers/CrucialController/CrucialControllerDetect.cpp b/Controllers/CrucialController/CrucialControllerDetect.cpp new file mode 100644 index 0000000..9b38d0d --- /dev/null +++ b/Controllers/CrucialController/CrucialControllerDetect.cpp @@ -0,0 +1,230 @@ +/*---------------------------------------------------------*\ +| CrucialControllerDetect.cpp | +| | +| Detector for Crucial Ballistix RAM | +| | +| Adam Honse (CalcProgrammer1) 19 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "CrucialController.h" +#include "LogManager.h" +#include "RGBController_Crucial.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +using namespace std::chrono_literals; + +/*----------------------------------------------------------------------*\ +| This list contains the available SMBus addresses for Crucial RAM | +\*----------------------------------------------------------------------*/ +#define CRUCIAL_ADDRESS_COUNT 8 + +static const unsigned char crucial_addresses[] = +{ + 0x39, + 0x3A, + 0x3B, + 0x3C, + 0x20, + 0x21, + 0x22, + 0x23 +}; + +#define CRUCIAL_CONTROLLER_NAME "Crucial DRAM" +std::string concatHexArray(const unsigned char array[], int count, const char split_char[]) +{ + std::string addresses = ""; + for(int i = 0; i < count; i++) + { + char buffer[6]; + snprintf(buffer, 6, "0x%02X%s", array[i], (i < count-1)? split_char: ""); + addresses += buffer; + } + return addresses; +} +#define TESTING_ADDRESSES concatHexArray(crucial_addresses, CRUCIAL_ADDRESS_COUNT, "|").c_str() + +/******************************************************************************************\ +* * +* CrucialRegisterRead * +* * +* A standalone version of the AuraSMBusController::AuraRegisterRead function for * +* access to Aura devices without instancing the AuraSMBusController class or reading * +* the config table from the device. * +* * +\******************************************************************************************/ + +unsigned char CrucialRegisterRead(i2c_smbus_interface* bus, crucial_dev_id dev, crucial_register reg) +{ + //Write Aura register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Read Aura value + return(bus->i2c_smbus_read_byte_data(dev, 0x81)); +} + +/******************************************************************************************\ +* * +* TestForCrucialController * +* * +* Tests the given address to see if an Crucial controller exists there. First does a* +* byte read to test for a response, and if so does a simple read at 0xA0 to test * +* for incrementing values 0...F which was observed at this location during data dump * +* * +\******************************************************************************************/ + +bool TestForCrucialController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + + int res = bus->i2c_smbus_read_byte(address); + + if(res >= 0) + { + pass = true; + + LOG_DEBUG("[%s] Detected an I2C device at address %02X", CRUCIAL_CONTROLLER_NAME, address); + + for(int i = 0xA0; i < 0xB0; i++) + { + res = bus->i2c_smbus_read_byte_data(address, i); + + if(res != (i - 0xA0)) + { + LOG_VERBOSE("[%s] Detection failed testing register %02X. Expected %02X, got %02X.", CRUCIAL_CONTROLLER_NAME, i, (i - 0xA0), res); + + pass = false; + } + } + + if(pass) + { + LOG_DEBUG("[%s] Checking for Micron string", CRUCIAL_CONTROLLER_NAME); + + char buf[16]; + for(int i = 0; i < 16; i++) + { + buf[i] = CrucialRegisterRead(bus, address, CRUCIAL_REG_MICRON_CHECK_1 + i); + } + + if(strcmp(buf, "Micron") == 0) + { + LOG_DEBUG("[%s] Device %02X is a Micron device, continuing", CRUCIAL_CONTROLLER_NAME, address); + } + else + { + for(int i = 0; i < 16; i++) + { + buf[i] = CrucialRegisterRead(bus, address, CRUCIAL_REG_MICRON_CHECK_2 + i); + } + + if(strcmp(buf, "Micron") == 0) + { + LOG_DEBUG("[%s] Device %02X is a Micron device, continuing", CRUCIAL_CONTROLLER_NAME, address); + } + else + { + LOG_DEBUG("[%s] Device %02X is not a Micron device, skipping", CRUCIAL_CONTROLLER_NAME, address); + pass = false; + } + } + } + } + + return(pass); + +} /* TestForCrucialController() */ + +void CrucialRegisterWrite(i2c_smbus_interface* bus, unsigned char dev, unsigned short reg, unsigned char val) +{ + //Write Crucial register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write Crucial value + bus->i2c_smbus_write_byte_data(dev, 0x01, val); +} + +/******************************************************************************************\ +* * +* DetectCrucialControllers * +* * +* Detect Crucial controllers on the enumerated I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where Aura device is connected * +* dev - I2C address of Aura device * +* * +\******************************************************************************************/ + +void DetectCrucialControllers(std::vector &busses) +{ + for(unsigned int bus = 0; bus < busses.size(); bus++) + { + int address_list_idx = -1; + + IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + for(unsigned int slot = 0; slot < 4; slot++) + { + int res = busses[bus]->i2c_smbus_read_byte(0x27); + + if(res < 0) + { + break; + } + + LOG_DEBUG("[%s] Remapping RAM module on 0x27", CRUCIAL_CONTROLLER_NAME); + do + { + address_list_idx++; + + if(address_list_idx < CRUCIAL_ADDRESS_COUNT) + { + res = busses[bus]->i2c_smbus_read_byte(crucial_addresses[address_list_idx]); + } + else + { + break; + } + } while(res >= 0); + + if(address_list_idx < CRUCIAL_ADDRESS_COUNT) + { + LOG_DEBUG("[%s] Remapping slot %d to address %02X", CRUCIAL_CONTROLLER_NAME, slot, crucial_addresses[address_list_idx]); + CrucialRegisterWrite(busses[bus], 0x27, 0x82EE, slot); + CrucialRegisterWrite(busses[bus], 0x27, 0x82EF, (crucial_addresses[address_list_idx] << 1)); + CrucialRegisterWrite(busses[bus], 0x27, 0x82F0, 0xF0); + } + + std::this_thread::sleep_for(1ms); + } + + LOG_DEBUG("[%s] In bus: %02X:%02X looking for devices at [%s]", CRUCIAL_CONTROLLER_NAME, busses[bus]->pci_vendor, busses[bus]->pci_device, TESTING_ADDRESSES); + + // Add Crucial controllers + for(unsigned int address_list_idx = 0; address_list_idx < CRUCIAL_ADDRESS_COUNT; address_list_idx++) + { + LOG_DEBUG("[%s] Testing address %02X to see if there is a device there", CRUCIAL_CONTROLLER_NAME, crucial_addresses[address_list_idx]); + + if(TestForCrucialController(busses[bus], crucial_addresses[address_list_idx])) + { + CrucialController* controller = new CrucialController(busses[bus], crucial_addresses[address_list_idx]); + RGBController_Crucial* rgb_controller = new RGBController_Crucial(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + + std::this_thread::sleep_for(1ms); + } + } + } + +} /* DetectCrucialControllers() */ + +REGISTER_I2C_DETECTOR("Crucial Ballistix", DetectCrucialControllers); diff --git a/Controllers/CrucialController/RGBController_Crucial.cpp b/Controllers/CrucialController/RGBController_Crucial.cpp new file mode 100644 index 0000000..55073bb --- /dev/null +++ b/Controllers/CrucialController/RGBController_Crucial.cpp @@ -0,0 +1,212 @@ +/*---------------------------------------------------------*\ +| RGBController_Crucial.cpp | +| | +| RGBController for Crucial Ballistix RAM | +| | +| Adam Honse (CalcProgrammer1) 19 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Crucial.h" + +/**------------------------------------------------------------------*\ + @name Crucial RAM + @category RAM + @type SMBus + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCrucialControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Crucial::RGBController_Crucial(CrucialController* controller_ptr) +{ + controller = controller_ptr; + + name = "Crucial DRAM"; + vendor = "Crucial"; + type = DEVICE_TYPE_DRAM; + description = "Crucial DRAM Device"; + version = controller->GetDeviceVersion(); + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Shift; + Shift.name = "Shift"; + Shift.value = CRUCIAL_MODE_SHIFT; + Shift.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Shift.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Shift); + + mode GradientShift; + GradientShift.name = "Gradient Shift"; + GradientShift.value = CRUCIAL_MODE_GRADIENT_SHIFT; + GradientShift.flags = MODE_FLAG_HAS_PER_LED_COLOR; + GradientShift.color_mode = MODE_COLORS_PER_LED; + modes.push_back(GradientShift); + + mode Fill; + Fill.name = "Fill"; + Fill.value = CRUCIAL_MODE_FILL; + Fill.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Fill.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Fill); + + mode Stack; + Stack.name = "Stack"; + Stack.value = CRUCIAL_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Stack.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Stack); + + mode DoubleStack; + DoubleStack.name = "Double Stack"; + DoubleStack.value = CRUCIAL_MODE_DOUBLE_STACK; + DoubleStack.flags = MODE_FLAG_HAS_PER_LED_COLOR; + DoubleStack.color_mode = MODE_COLORS_PER_LED; + modes.push_back(DoubleStack); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CRUCIAL_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode MotionPoint; + MotionPoint.name = "Motion Point"; + MotionPoint.value = CRUCIAL_MODE_MOTION_POINT; + MotionPoint.flags = MODE_FLAG_HAS_PER_LED_COLOR; + MotionPoint.color_mode = MODE_COLORS_PER_LED; + modes.push_back(MotionPoint); + + mode InsideOut; + InsideOut.name = "Inside Out"; + InsideOut.value = CRUCIAL_MODE_INSIDE_OUT; + InsideOut.flags = MODE_FLAG_HAS_PER_LED_COLOR; + InsideOut.color_mode = MODE_COLORS_PER_LED; + modes.push_back(InsideOut); + + mode ColorStep; + ColorStep.name = "Color Step"; + ColorStep.value = CRUCIAL_MODE_COLOR_STEP; + ColorStep.flags = MODE_FLAG_HAS_PER_LED_COLOR; + ColorStep.color_mode = MODE_COLORS_PER_LED; + modes.push_back(ColorStep); + + mode WaterWave; + WaterWave.name = "Water Wave (Color Blending)"; + WaterWave.value = CRUCIAL_MODE_WATER_WAVE; + WaterWave.flags = MODE_FLAG_HAS_PER_LED_COLOR; + WaterWave.color_mode = MODE_COLORS_PER_LED; + modes.push_back(WaterWave); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = CRUCIAL_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flashing); + + mode Static; + Static.name = "Static"; + Static.value = CRUCIAL_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + SetupZones(); +} + +RGBController_Crucial::~RGBController_Crucial() +{ + delete controller; +} + +void RGBController_Crucial::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = "DRAM"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 8; + new_zone.leds_max = 8; + new_zone.leds_count = 8; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led new_led; + new_led.name = "DRAM LED "; + new_led.name.append(std::to_string(led_idx)); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_Crucial::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Crucial::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == 0xFFFF) + { + controller->SetAllColorsDirect(&colors[0]); + } + else + { + controller->SetAllColorsEffect(&colors[0]); + + if(modes[active_mode].value == CRUCIAL_MODE_STATIC) + { + controller->SetMode(modes[active_mode].value); + } + } +} + +void RGBController_Crucial::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Crucial::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Crucial::DeviceUpdateMode() +{ + if(modes[active_mode].value == 0xFFFF) + { + controller->SetMode(CRUCIAL_MODE_STATIC); + controller->SetAllColorsEffect(&colors[0]); + return; + } + + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SetAllColorsEffect(&colors[0]); + } + + controller->SetMode(modes[active_mode].value); +} diff --git a/Controllers/CrucialController/RGBController_Crucial.h b/Controllers/CrucialController/RGBController_Crucial.h new file mode 100644 index 0000000..63d53f6 --- /dev/null +++ b/Controllers/CrucialController/RGBController_Crucial.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_Crucial.h | +| | +| RGBController for Crucial Ballistix RAM | +| | +| Adam Honse (CalcProgrammer1) 19 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CrucialController.h" + +class RGBController_Crucial : public RGBController +{ +public: + RGBController_Crucial(CrucialController* controller_ptr); + ~RGBController_Crucial(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CrucialController* controller; +}; diff --git a/Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiController.cpp b/Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiController.cpp new file mode 100644 index 0000000..5389b3b --- /dev/null +++ b/Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiController.cpp @@ -0,0 +1,242 @@ +/*---------------------------------------------------------*\ +| CryorigH7QuadLumiController.cpp | +| | +| Driver for Cryorig H7 Quad Lumi | +| | +| Adam Honse (calcprogrammer1@gmail.com) 15 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "CryorigH7QuadLumiController.h" +#include "LogManager.h" +#include "StringUtils.h" + +CryorigH7QuadLumiController::CryorigH7QuadLumiController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + SendFirmwareRequest(); +} + +CryorigH7QuadLumiController::~CryorigH7QuadLumiController() +{ + hid_close(dev); +} + +std::string CryorigH7QuadLumiController::GetLocation() +{ + return("HID: " + location); +} + +std::string CryorigH7QuadLumiController::GetName() +{ + return(name); +} + +std::string CryorigH7QuadLumiController::GetFirmwareVersion() +{ + return(firmware_version); +} + +std::string CryorigH7QuadLumiController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void CryorigH7QuadLumiController::SetChannelEffect + ( + unsigned char channel, + unsigned char mode, + unsigned char speed, + bool direction, + RGBColor * colors, + unsigned int num_colors + ) +{ + unsigned char color_data[120]; + + /*-----------------------------------------------------*\ + | If mode requires no colors, send packet | + \*-----------------------------------------------------*/ + if(num_colors == 0) + { + /*-----------------------------------------------------*\ + | Send mode without color data | + \*-----------------------------------------------------*/ + SendPacket(channel, mode, direction, 0, speed, 0, NULL); + } + /*-----------------------------------------------------*\ + | If mode requires indexed colors, send color index | + | packets for each mode color | + \*-----------------------------------------------------*/ + else if(num_colors <= 8) + { + for(unsigned int color_idx = 0; color_idx < num_colors; color_idx++) + { + /*-----------------------------------------------------*\ + | Fill in color data (5 entries per color) | + \*-----------------------------------------------------*/ + for(int idx = 0; idx < 40; idx++) + { + int pixel_idx = idx * 3; + RGBColor color = colors[color_idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send mode and color data | + \*-----------------------------------------------------*/ + SendPacket(channel, mode, direction, (unsigned char)color_idx, speed, 5, &color_data[0]); + } + } + /*-----------------------------------------------------*\ + | If mode requires per-LED colors, fill colors array | + \*-----------------------------------------------------*/ + else + { + /*-----------------------------------------------------*\ + | Fill in color data (up to 40 colors) | + \*-----------------------------------------------------*/ + for(unsigned int idx = 0; idx < num_colors; idx++) + { + unsigned int pixel_idx = idx * 3; + RGBColor color = colors[idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send mode and color data | + \*-----------------------------------------------------*/ + SendPacket(channel, mode, direction, 0, speed, num_colors, &color_data[0]); + } +} + +void CryorigH7QuadLumiController::SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors + ) +{ + unsigned char color_data[120]; + + /*-----------------------------------------------------*\ + | Fill in color data (up to 40 colors) | + \*-----------------------------------------------------*/ + for(unsigned int idx = 0; idx < num_colors; idx++) + { + unsigned int pixel_idx = idx * 3; + RGBColor color = colors[idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send color data | + \*-----------------------------------------------------*/ + SendPacket(channel, CRYORIG_H7_QUAD_LUMI_MODE_FIXED, false, 0, 0, num_colors, &color_data[0]); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void CryorigH7QuadLumiController::SendPacket + ( + unsigned char channel, + unsigned char mode, + bool direction, + unsigned char color_idx, + unsigned char speed, + unsigned char color_count, + unsigned char* color_data + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up RGB packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x02; + usb_buf[0x01] = 0x4C; + + /*-----------------------------------------------------*\ + | Set channel and direction in RGB packet | + \*-----------------------------------------------------*/ + usb_buf[0x02] = (channel + 1) | (direction ? (1 << 4) : 0); + + /*-----------------------------------------------------*\ + | Set mode in RGB packet | + \*-----------------------------------------------------*/ + usb_buf[0x03] = mode; + + /*-----------------------------------------------------*\ + | Set color index and speed in RGB packet | + \*-----------------------------------------------------*/ + usb_buf[0x04] = ( color_idx << 5 ) | speed; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x05 + (channel * 15)], color_data, color_count * 3); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} + +void CryorigH7QuadLumiController::SendFirmwareRequest() +{ + unsigned char usb_buf[17]; + unsigned int ret_val = 0; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Firmware Request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x02; + usb_buf[0x01] = 0x5C; + + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Receive packets until 0x11 0x01 is received | + \*-----------------------------------------------------*/ + do + { + ret_val = hid_read(dev, usb_buf, sizeof(usb_buf)); + } while( (ret_val != 17) ); + + snprintf(firmware_version, 16, "%u.%u", usb_buf[0x0D], usb_buf[0x0E]); +} diff --git a/Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiController.h b/Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiController.h new file mode 100644 index 0000000..45e3579 --- /dev/null +++ b/Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiController.h @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| CryorigH7QuadLumiController.h | +| | +| Driver for Cryorig H7 Quad Lumi | +| | +| Adam Honse (calcprogrammer1@gmail.com) 15 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +enum +{ + CRYORIG_H7_QUAD_LUMI_CHANNEL_ALL = 0x00, /* All channels */ + CRYORIG_H7_QUAD_LUMI_CHANNEL_1 = 0x01, /* Channel 1 */ + CRYORIG_H7_QUAD_LUMI_CHANNEL_2 = 0x02, /* Channel 2 */ + CRYORIG_H7_QUAD_LUMI_NUM_CHANNELS = 0x02 /* Number of channels */ +}; + +enum +{ + CRYORIG_H7_QUAD_LUMI_SPEED_SLOWEST = 0x00, /* Slowest speed */ + CRYORIG_H7_QUAD_LUMI_SPEED_SLOW = 0x01, /* Slow speed */ + CRYORIG_H7_QUAD_LUMI_SPEED_NORMAL = 0x02, /* Normal speed */ + CRYORIG_H7_QUAD_LUMI_SPEED_FAST = 0x03, /* Fast speed */ + CRYORIG_H7_QUAD_LUMI_SPEED_FASTEST = 0x04, /* Fastest speed */ +}; + +enum +{ + CRYORIG_H7_QUAD_LUMI_MODE_FIXED = 0x00, /* Fixed colors mode */ + CRYORIG_H7_QUAD_LUMI_MODE_FADING = 0x01, /* Fading mode */ + CRYORIG_H7_QUAD_LUMI_MODE_SPECTRUM = 0x02, /* Spectrum cycle mode */ + CRYORIG_H7_QUAD_LUMI_MODE_MARQUEE = 0x03, /* Marquee mode */ + CRYORIG_H7_QUAD_LUMI_MODE_COVER_MARQUEE = 0x04, /* Cover marquee mode */ + CRYORIG_H7_QUAD_LUMI_MODE_ALTERNATING = 0x05, /* Alternating mode */ + CRYORIG_H7_QUAD_LUMI_MODE_BREATHING = 0x06, /* Breathing mode */ + CRYORIG_H7_QUAD_LUMI_MODE_PULSING = 0x07, /* Pulsing mode */ +}; + +class CryorigH7QuadLumiController +{ +public: + CryorigH7QuadLumiController(hid_device* dev_handle, const char* path, std::string dev_name); + ~CryorigH7QuadLumiController(); + + std::string GetFirmwareVersion(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + + void SetChannelEffect + ( + unsigned char channel, + unsigned char mode, + unsigned char speed, + bool direction, + RGBColor * colors, + unsigned int num_colors + ); + + void SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors + ); + +private: + hid_device* dev; + + char firmware_version[16]; + std::string location; + std::string name; + + void SendPacket + ( + unsigned char channel, + unsigned char mode, + bool direction, + unsigned char color_idx, + unsigned char speed, + unsigned char color_count, + unsigned char* color_data + ); + + void SendFirmwareRequest(); +}; diff --git a/Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiControllerDetect.cpp b/Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiControllerDetect.cpp new file mode 100644 index 0000000..a84a4e9 --- /dev/null +++ b/Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiControllerDetect.cpp @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| CryorigH7QuadLumiControllerDetect.cpp | +| | +| Detector for Cryorig H7 Quad Lumi | +| | +| Adam Honse (calcprogrammer1@gmail.com) 15 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "CryorigH7QuadLumiController.h" +#include "RGBController_CryorigH7QuadLumi.h" + +/*-----------------------------------------------------*\ +| CRYORIG/NZXT USB IDs | +\*-----------------------------------------------------*/ +#define NZXT_VID 0x1E71 +#define CRYORIG_H7_QUAD_LUMI_PID 0x1712 + +static void DetectCryorigH7QuadLumi(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + CryorigH7QuadLumiController* controller = new CryorigH7QuadLumiController(dev, info->path, name); + RGBController_CryorigH7QuadLumi* rgb_controller = new RGBController_CryorigH7QuadLumi(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("CRYORIG H7 Quad Lumi", DetectCryorigH7QuadLumi, NZXT_VID, CRYORIG_H7_QUAD_LUMI_PID); diff --git a/Controllers/CryorigH7QuadLumiController/RGBController_CryorigH7QuadLumi.cpp b/Controllers/CryorigH7QuadLumiController/RGBController_CryorigH7QuadLumi.cpp new file mode 100644 index 0000000..e741686 --- /dev/null +++ b/Controllers/CryorigH7QuadLumiController/RGBController_CryorigH7QuadLumi.cpp @@ -0,0 +1,244 @@ +/*---------------------------------------------------------*\ +| RGBController_CryorigH7QuadLumi.cpp | +| | +| RGBController for Cryorig H7 Quad Lumi | +| | +| Adam Honse (CalcProgrammer1) 15 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_CryorigH7QuadLumi.h" + +/**------------------------------------------------------------------*\ + @name Cryorig H7 Quad Lumi + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectCryorigH7QuadLumi + @comment +\*-------------------------------------------------------------------*/ + +RGBController_CryorigH7QuadLumi::RGBController_CryorigH7QuadLumi(CryorigH7QuadLumiController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "CRYORIG"; + type = DEVICE_TYPE_COOLER; + description = "CRYORIG H7 Quad Lumi Device"; + version = controller->GetFirmwareVersion(); + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = CRYORIG_H7_QUAD_LUMI_MODE_FIXED; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Fading; + Fading.name = "Fading"; + Fading.value = CRYORIG_H7_QUAD_LUMI_MODE_FADING; + Fading.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Fading.speed_min = CRYORIG_H7_QUAD_LUMI_SPEED_SLOWEST; + Fading.speed_max = CRYORIG_H7_QUAD_LUMI_SPEED_FASTEST; + Fading.colors_min = 1; + Fading.colors_max = 8; + Fading.speed = CRYORIG_H7_QUAD_LUMI_SPEED_NORMAL; + Fading.color_mode = MODE_COLORS_MODE_SPECIFIC; + Fading.colors.resize(2); + modes.push_back(Fading); + + mode SpectrumCycle; + SpectrumCycle.name = "Rainbow Wave"; + SpectrumCycle.value = CRYORIG_H7_QUAD_LUMI_MODE_SPECTRUM; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + SpectrumCycle.speed_min = CRYORIG_H7_QUAD_LUMI_SPEED_SLOWEST; + SpectrumCycle.speed_max = CRYORIG_H7_QUAD_LUMI_SPEED_FASTEST; + SpectrumCycle.speed = CRYORIG_H7_QUAD_LUMI_SPEED_NORMAL; + SpectrumCycle.direction = MODE_DIRECTION_RIGHT; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = CRYORIG_H7_QUAD_LUMI_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Marquee.speed_min = CRYORIG_H7_QUAD_LUMI_SPEED_SLOWEST; + Marquee.speed_max = CRYORIG_H7_QUAD_LUMI_SPEED_FASTEST; + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.speed = CRYORIG_H7_QUAD_LUMI_SPEED_NORMAL; + Marquee.direction = MODE_DIRECTION_RIGHT; + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + Marquee.colors.resize(1); + modes.push_back(Marquee); + + mode CoverMarquee; + CoverMarquee.name = "Cover Marquee"; + CoverMarquee.value = CRYORIG_H7_QUAD_LUMI_MODE_COVER_MARQUEE; + CoverMarquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + CoverMarquee.speed_min = CRYORIG_H7_QUAD_LUMI_SPEED_SLOWEST; + CoverMarquee.speed_max = CRYORIG_H7_QUAD_LUMI_SPEED_FASTEST; + CoverMarquee.colors_min = 1; + CoverMarquee.colors_max = 8; + CoverMarquee.speed = CRYORIG_H7_QUAD_LUMI_SPEED_NORMAL; + CoverMarquee.direction = MODE_DIRECTION_RIGHT; + CoverMarquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + CoverMarquee.colors.resize(2); + modes.push_back(CoverMarquee); + + mode Alternating; + Alternating.name = "Alternating"; + Alternating.value = CRYORIG_H7_QUAD_LUMI_MODE_ALTERNATING; + Alternating.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Alternating.speed_min = CRYORIG_H7_QUAD_LUMI_SPEED_SLOWEST; + Alternating.speed_max = CRYORIG_H7_QUAD_LUMI_SPEED_FASTEST; + Alternating.colors_min = 1; + Alternating.colors_max = 2; + Alternating.speed = CRYORIG_H7_QUAD_LUMI_SPEED_NORMAL; + Alternating.direction = MODE_DIRECTION_RIGHT; + Alternating.color_mode = MODE_COLORS_MODE_SPECIFIC; + Alternating.colors.resize(2); + modes.push_back(Alternating); + + mode Pulsing; + Pulsing.name = "Pulsing"; + Pulsing.value = CRYORIG_H7_QUAD_LUMI_MODE_PULSING; + Pulsing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Pulsing.speed_min = CRYORIG_H7_QUAD_LUMI_SPEED_SLOWEST; + Pulsing.speed_max = CRYORIG_H7_QUAD_LUMI_SPEED_FASTEST; + Pulsing.colors_min = 1; + Pulsing.colors_max = 8; + Pulsing.speed = CRYORIG_H7_QUAD_LUMI_SPEED_NORMAL; + Pulsing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulsing.colors.resize(2) ; + modes.push_back(Pulsing); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = CRYORIG_H7_QUAD_LUMI_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = CRYORIG_H7_QUAD_LUMI_SPEED_SLOWEST; + Breathing.speed_max = CRYORIG_H7_QUAD_LUMI_SPEED_FASTEST; + Breathing.colors_min = 1; + Breathing.colors_max = 8; + Breathing.speed = CRYORIG_H7_QUAD_LUMI_SPEED_NORMAL; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(2); + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_CryorigH7QuadLumi::~RGBController_CryorigH7QuadLumi() +{ + delete controller; +} + +void RGBController_CryorigH7QuadLumi::SetupZones() +{ + const char* zone_names[] = { "Logo", "Underglow" }; + + /*-------------------------------------------------*\ + | Set up zones | + \*-------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < 2; zone_idx++) + { + zone* new_zone = new zone; + + new_zone->name = zone_names[zone_idx]; + new_zone->type = ZONE_TYPE_LINEAR; + new_zone->leds_min = 5; + new_zone->leds_max = 5; + new_zone->leds_count = 5; + new_zone->matrix_map = NULL; + + zones.push_back(*new_zone); + } + + /*-------------------------------------------------*\ + | Set up LEDs | + \*-------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + new_led.name = zone_names[zone_idx]; + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_idx + 1)); + new_led.value = zone_idx; + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_CryorigH7QuadLumi::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_CryorigH7QuadLumi::DeviceUpdateLEDs() +{ + for(unsigned char zone_idx = 0; zone_idx < (unsigned char)zones.size(); zone_idx++) + { + controller->SetChannelLEDs(zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} + +void RGBController_CryorigH7QuadLumi::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_CryorigH7QuadLumi::UpdateSingleLED(int led) +{ + unsigned int zone_idx = leds[led].value; + + controller->SetChannelLEDs(zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); +} + +void RGBController_CryorigH7QuadLumi::DeviceUpdateMode() +{ + if(modes[active_mode].value == CRYORIG_H7_QUAD_LUMI_MODE_FIXED) + { + DeviceUpdateLEDs(); + } + else + { + for(unsigned char zone_idx = 0; zone_idx < (unsigned char)zones.size(); zone_idx++) + { + RGBColor* colors = NULL; + bool direction = false; + + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + direction = true; + } + + if(modes[active_mode].colors.size() > 0) + { + colors = &modes[active_mode].colors[0]; + } + + controller->SetChannelEffect + ( + zone_idx, + modes[active_mode].value, + modes[active_mode].speed, + direction, + colors, + (unsigned int)modes[active_mode].colors.size() + ); + } + } +} diff --git a/Controllers/CryorigH7QuadLumiController/RGBController_CryorigH7QuadLumi.h b/Controllers/CryorigH7QuadLumiController/RGBController_CryorigH7QuadLumi.h new file mode 100644 index 0000000..ce7995e --- /dev/null +++ b/Controllers/CryorigH7QuadLumiController/RGBController_CryorigH7QuadLumi.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_CryorigH7QuadLumi.h | +| | +| RGBController for Cryorig H7 Quad Lumi | +| | +| Adam Honse (CalcProgrammer1) 15 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "CryorigH7QuadLumiController.h" + +class RGBController_CryorigH7QuadLumi : public RGBController +{ +public: + RGBController_CryorigH7QuadLumi(CryorigH7QuadLumiController* controller_ptr); + ~RGBController_CryorigH7QuadLumi(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + CryorigH7QuadLumiController* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/DDPController/DDPController.cpp b/Controllers/DDPController/DDPController.cpp new file mode 100644 index 0000000..2165691 --- /dev/null +++ b/Controllers/DDPController/DDPController.cpp @@ -0,0 +1,309 @@ +/*---------------------------------------------------------*\ +| DDPController.cpp | +| | +| Driver for DDP protocol devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "DDPController.h" +#include "LogManager.h" +#include +#include + +DDPController::DDPController(const std::vector& device_list) +{ + devices = device_list; + unique_endpoints = NULL; + num_endpoints = 0; + sequence_number = 0; + keepalive_time_ms = 1000; + keepalive_thread_run = false; + + InitializeNetPorts(); + + if(!devices.empty()) + { + keepalive_thread_run = true; + keepalive_thread = std::thread(&DDPController::KeepaliveThreadFunction, this); + } +} + +DDPController::~DDPController() +{ + keepalive_thread_run = false; + if(keepalive_thread.joinable()) + { + keepalive_thread.join(); + } + + CloseNetPorts(); + if(unique_endpoints != NULL) + { + delete[] unique_endpoints; + } +} + +bool DDPController::InitializeNetPorts() +{ + if(devices.empty()) + { + return true; + } + + num_endpoints = 0; + + for(unsigned int dev_idx = 0; dev_idx < devices.size(); dev_idx++) + { + bool found = false; + for(unsigned int ep_idx = 0; ep_idx < num_endpoints; ep_idx++) + { + if(strcmp(unique_endpoints[ep_idx].ip, devices[dev_idx].ip.c_str()) == 0 && + unique_endpoints[ep_idx].port == devices[dev_idx].port) + { + found = true; + break; + } + } + if(!found) + { + num_endpoints++; + } + } + + unique_endpoints = new DDPEndpoint[num_endpoints]; + unsigned int endpoint_count = 0; + + for(unsigned int dev_idx = 0; dev_idx < devices.size(); dev_idx++) + { + bool found = false; + for(unsigned int ep_idx = 0; ep_idx < endpoint_count; ep_idx++) + { + if(strcmp(unique_endpoints[ep_idx].ip, devices[dev_idx].ip.c_str()) == 0 && + unique_endpoints[ep_idx].port == devices[dev_idx].port) + { + found = true; + break; + } + } + if(!found) + { + strncpy(unique_endpoints[endpoint_count].ip, devices[dev_idx].ip.c_str(), 15); + unique_endpoints[endpoint_count].ip[15] = '\0'; + unique_endpoints[endpoint_count].port = devices[dev_idx].port; + endpoint_count++; + } + } + + for(unsigned int ep_idx = 0; ep_idx < num_endpoints; ep_idx++) + { + net_port* port = new net_port(); + char port_str[16]; + snprintf(port_str, 16, "%d", unique_endpoints[ep_idx].port); + + if(port->udp_client(unique_endpoints[ep_idx].ip, port_str)) + { + udp_ports.push_back(port); + } + else + { + udp_ports.push_back(NULL); + } + } + + return true; +} + +void DDPController::CloseNetPorts() +{ + for(unsigned int port_idx = 0; port_idx < udp_ports.size(); port_idx++) + { + if(udp_ports[port_idx] != NULL) + { + delete udp_ports[port_idx]; + } + } + udp_ports.clear(); +} + +int DDPController::GetPortIndex(const DDPDevice& device) +{ + for(unsigned int ep_idx = 0; ep_idx < num_endpoints; ep_idx++) + { + if(strcmp(unique_endpoints[ep_idx].ip, device.ip.c_str()) == 0 && + unique_endpoints[ep_idx].port == device.port) + { + return (int)ep_idx; + } + } + return -1; +} + +void DDPController::UpdateLEDs(const std::vector& colors) +{ + if(udp_ports.empty()) return; + + { + std::lock_guard lock(last_update_mutex); + last_colors = colors; + last_update_time = std::chrono::steady_clock::now(); + } + + unsigned int color_index = 0; + + for(unsigned int dev_idx = 0; dev_idx < devices.size(); dev_idx++) + { + if(color_index >= colors.size()) break; + + unsigned int bytes_per_pixel = 3; + unsigned int total_bytes = devices[dev_idx].num_leds * bytes_per_pixel; + std::vector device_data(total_bytes); + + for(unsigned int led_idx = 0; led_idx < devices[dev_idx].num_leds && (color_index + led_idx) < colors.size(); led_idx++) + { + unsigned int color = colors[color_index + led_idx]; + unsigned char r = color & 0xFF; + unsigned char g = (color >> 8) & 0xFF; + unsigned char b = (color >> 16) & 0xFF; + unsigned int pixel_offset = led_idx * bytes_per_pixel; + + device_data[pixel_offset + 0] = r; + device_data[pixel_offset + 1] = g; + device_data[pixel_offset + 2] = b; + } + + unsigned int max_data_per_packet = DDP_MAX_DATA_SIZE; + unsigned int bytes_sent = 0; + + while(bytes_sent < total_bytes) + { + unsigned int chunk_size = (max_data_per_packet < (total_bytes - bytes_sent)) ? max_data_per_packet : (total_bytes - bytes_sent); + + if(!SendDDPPacket(devices[dev_idx], device_data.data() + bytes_sent, (unsigned short)chunk_size, bytes_sent)) + break; + + bytes_sent += chunk_size; + } + + color_index += devices[dev_idx].num_leds; + } + + sequence_number++; +} + +bool DDPController::SendDDPPacket(const DDPDevice& device, const unsigned char* data, unsigned short length, unsigned int offset) +{ + int port_index = GetPortIndex(device); + if(port_index < 0 || port_index >= (int)udp_ports.size()) + { + return false; + } + + if(udp_ports[port_index] == NULL) + { + net_port* port = new net_port(); + char port_str[16]; + snprintf(port_str, 16, "%d", unique_endpoints[port_index].port); + + if(port->udp_client(unique_endpoints[port_index].ip, port_str)) + { + udp_ports[port_index] = port; + } + else + { + delete port; + return false; + } + } + + std::vector packet(DDP_HEADER_SIZE + length); + ddp_header* header = (ddp_header*)packet.data(); + + header->flags = DDP_FLAG_VER_1 | DDP_FLAG_PUSH; + header->sequence = sequence_number & 0x0F; + header->data_type = 1; + header->dest_id = 1; + header->data_offset = htonl(offset); + header->data_length = htons(length); + + memcpy(packet.data() + DDP_HEADER_SIZE, data, length); + + int bytes_sent = udp_ports[port_index]->udp_write((char*)packet.data(), (int)packet.size()); + + return bytes_sent == (int)packet.size(); +} + +void DDPController::SetKeepaliveTime(unsigned int time_ms) +{ + keepalive_time_ms = time_ms; +} + +void DDPController::KeepaliveThreadFunction() +{ + while(keepalive_thread_run) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + if(keepalive_time_ms == 0) + continue; + + std::vector colors_to_send; + bool should_send = false; + + { + std::lock_guard lock(last_update_mutex); + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + long long time_since_update = std::chrono::duration_cast(now - last_update_time).count(); + + if(time_since_update >= keepalive_time_ms && !last_colors.empty()) + { + colors_to_send = last_colors; + should_send = true; + last_update_time = now; + } + } + + if(should_send) + { + unsigned int color_index = 0; + + for(unsigned int dev_idx = 0; dev_idx < devices.size(); dev_idx++) + { + if(color_index >= colors_to_send.size()) break; + + unsigned int bytes_per_pixel = 3; + unsigned int total_bytes = devices[dev_idx].num_leds * bytes_per_pixel; + std::vector device_data(total_bytes); + + for(unsigned int led_idx = 0; led_idx < devices[dev_idx].num_leds && (color_index + led_idx) < colors_to_send.size(); led_idx++) + { + unsigned int color = colors_to_send[color_index + led_idx]; + unsigned char r = color & 0xFF; + unsigned char g = (color >> 8) & 0xFF; + unsigned char b = (color >> 16) & 0xFF; + unsigned int pixel_offset = led_idx * bytes_per_pixel; + + device_data[pixel_offset + 0] = r; + device_data[pixel_offset + 1] = g; + device_data[pixel_offset + 2] = b; + } + + unsigned int max_data_per_packet = DDP_MAX_DATA_SIZE; + unsigned int bytes_sent = 0; + + while(bytes_sent < total_bytes) + { + unsigned int chunk_size = (max_data_per_packet < (total_bytes - bytes_sent)) ? max_data_per_packet : (total_bytes - bytes_sent); + + if(!SendDDPPacket(devices[dev_idx], device_data.data() + bytes_sent, (unsigned short)chunk_size, bytes_sent)) + break; + + bytes_sent += chunk_size; + } + + color_index += devices[dev_idx].num_leds; + } + } + } +} diff --git a/Controllers/DDPController/DDPController.h b/Controllers/DDPController/DDPController.h new file mode 100644 index 0000000..31f3bb0 --- /dev/null +++ b/Controllers/DDPController/DDPController.h @@ -0,0 +1,97 @@ +/*---------------------------------------------------------*\ +| DDPController.h | +| | +| Driver for DDP protocol devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "net_port.h" + +#define DDP_DEFAULT_PORT 4048 +#define DDP_HEADER_SIZE 10 +#define DDP_HEADER_SIZE_TC 14 +#define DDP_VERSION 1 +#define DDP_MAX_PACKET_SIZE 1450 +#define DDP_MAX_DATA_SIZE 1440 + +#define DDP_FLAG_VER_MASK 0xC0 +#define DDP_FLAG_VER_1 0x40 +#define DDP_FLAG_TIMECODE 0x10 +#define DDP_FLAG_STORAGE 0x08 +#define DDP_FLAG_REPLY 0x04 +#define DDP_FLAG_QUERY 0x02 +#define DDP_FLAG_PUSH 0x01 + +#define DDP_TYPE_RGB8 0x0B +#define DDP_TYPE_RGB_SIMPLE 1 + +#pragma pack(push, 1) +struct ddp_header +{ + unsigned char flags; + unsigned char sequence; + unsigned char data_type; + unsigned char dest_id; + unsigned int data_offset; + unsigned short data_length; +}; +#pragma pack(pop) + +struct DDPDevice +{ + std::string name; + std::string ip; + unsigned short port; + unsigned int num_leds; +}; + +struct DDPEndpoint +{ + char ip[16]; + unsigned short port; +}; + +class DDPController +{ +public: + DDPController(const std::vector& devices); + ~DDPController(); + + void UpdateLEDs(const std::vector& colors); + void SetKeepaliveTime(unsigned int time_ms); + +private: + std::vector devices; + std::vector udp_ports; + DDPEndpoint* unique_endpoints; + unsigned int num_endpoints; + unsigned char sequence_number; + + + std::atomic keepalive_thread_run; + std::thread keepalive_thread; + std::mutex last_update_mutex; + std::chrono::steady_clock::time_point last_update_time; + std::vector last_colors; + unsigned int keepalive_time_ms; + + bool InitializeNetPorts(); + void CloseNetPorts(); + int GetPortIndex(const DDPDevice& device); + bool SendDDPPacket(const DDPDevice& device, + const unsigned char* data, + unsigned short length, + unsigned int offset = 0); + void KeepaliveThreadFunction(); +}; diff --git a/Controllers/DDPController/DDPControllerDetect.cpp b/Controllers/DDPController/DDPControllerDetect.cpp new file mode 100644 index 0000000..3e9f766 --- /dev/null +++ b/Controllers/DDPController/DDPControllerDetect.cpp @@ -0,0 +1,92 @@ +/*---------------------------------------------------------*\ +| DDPControllerDetect.cpp | +| | +| Detector for DDP devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "RGBController.h" +#include "RGBController_DDP.h" +#include "SettingsManager.h" +#include "LogManager.h" +#include "nlohmann/json.hpp" + +using json = nlohmann::json; + +void DetectDDPControllers() +{ + json ddp_settings; + std::vector> device_lists; + DDPDevice dev; + + ddp_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("DDPDevices"); + + if(ddp_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < ddp_settings["devices"].size(); device_idx++) + { + dev.name = ""; + dev.ip = ""; + dev.port = DDP_DEFAULT_PORT; + dev.num_leds = 0; + + if(ddp_settings["devices"][device_idx].contains("name")) + dev.name = ddp_settings["devices"][device_idx]["name"]; + if(ddp_settings["devices"][device_idx].contains("ip")) + dev.ip = ddp_settings["devices"][device_idx]["ip"]; + if(ddp_settings["devices"][device_idx].contains("port")) + dev.port = ddp_settings["devices"][device_idx]["port"]; + if(ddp_settings["devices"][device_idx].contains("num_leds")) + dev.num_leds = ddp_settings["devices"][device_idx]["num_leds"]; + + if(dev.name.empty()) + dev.name = "DDP Device " + std::to_string(device_idx + 1); + if(dev.ip.empty()) + { + continue; + } + if(dev.num_leds == 0) + { + continue; + } + + bool device_added_to_existing_list = false; + + for(unsigned int list_idx = 0; list_idx < device_lists.size(); list_idx++) + { + for(unsigned int existing_device_idx = 0; existing_device_idx < device_lists[list_idx].size(); existing_device_idx++) + { + if(dev.ip == device_lists[list_idx][existing_device_idx].ip && + dev.port == device_lists[list_idx][existing_device_idx].port) + { + device_lists[list_idx].push_back(dev); + device_added_to_existing_list = true; + break; + } + } + if(device_added_to_existing_list) + break; + } + + if(!device_added_to_existing_list) + { + std::vector new_list; + new_list.push_back(dev); + device_lists.push_back(new_list); + } + } + + for(unsigned int list_idx = 0; list_idx < device_lists.size(); list_idx++) + { + RGBController_DDP* rgb_controller = new RGBController_DDP(device_lists[list_idx]); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} + +REGISTER_DETECTOR("DDP", DetectDDPControllers); diff --git a/Controllers/DDPController/RGBController_DDP.cpp b/Controllers/DDPController/RGBController_DDP.cpp new file mode 100644 index 0000000..8779af3 --- /dev/null +++ b/Controllers/DDPController/RGBController_DDP.cpp @@ -0,0 +1,131 @@ +/*---------------------------------------------------------*\ +| RGBController_DDP.cpp | +| | +| RGBController for DDP devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_DDP.h" + +/**------------------------------------------------------------------*\ + @name DDP Devices + @category LEDStrip + @type Network + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectDDPControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_DDP::RGBController_DDP(std::vector device_list) +{ + devices = device_list; + name = "DDP Device Group"; + type = DEVICE_TYPE_LEDSTRIP; + description = "Distributed Display Protocol Device"; + location = "DDP: "; + + if(devices.size() == 1) + name = devices[0].name; + else if(!devices[0].ip.empty()) + name += " (" + devices[0].ip + ")"; + + if(!devices[0].ip.empty()) + location += devices[0].ip + ":" + std::to_string(devices[0].port); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.brightness = 100; + modes.push_back(Direct); + + controller = new DDPController(devices); + SetupZones(); +} + +RGBController_DDP::~RGBController_DDP() +{ + delete controller; +} + +void RGBController_DDP::SetupZones() +{ + for(unsigned int zone_idx = 0; zone_idx < devices.size(); zone_idx++) + { + zone led_zone; + led_zone.name = devices[zone_idx].name; + led_zone.type = ZONE_TYPE_LINEAR; + led_zone.leds_min = devices[zone_idx].num_leds; + led_zone.leds_max = devices[zone_idx].num_leds; + led_zone.leds_count = devices[zone_idx].num_leds; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + } + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + new_led.name = zones[zone_idx].name + " LED " + std::to_string(led_idx + 1); + new_led.value = 0; + leds.push_back(new_led); + } + } + SetupColors(); +} + +void RGBController_DDP::ResizeZone(int /*zone*/, int /*new_size*/) +{ +} + +void RGBController_DDP::DeviceUpdateLEDs() +{ + std::vector brightness_adjusted_colors; + brightness_adjusted_colors.reserve(colors.size()); + float brightness_scale = (float)modes[active_mode].brightness / 100.0f; + + for(unsigned int color_idx = 0; color_idx < colors.size(); color_idx++) + { + unsigned int color = colors[color_idx]; + unsigned char r = color & 0xFF; + unsigned char g = (color >> 8) & 0xFF; + unsigned char b = (color >> 16) & 0xFF; + r = (unsigned char)(r * brightness_scale); + g = (unsigned char)(g * brightness_scale); + b = (unsigned char)(b * brightness_scale); + unsigned int adjusted_color = r | (g << 8) | (b << 16); + brightness_adjusted_colors.push_back(adjusted_color); + } + + controller->UpdateLEDs(brightness_adjusted_colors); +} + +void RGBController_DDP::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DDP::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DDP::DeviceUpdateMode() +{ +} + +void RGBController_DDP::SetKeepaliveTime(unsigned int time_ms) +{ + if(controller != nullptr) + { + controller->SetKeepaliveTime(time_ms); + } +} diff --git a/Controllers/DDPController/RGBController_DDP.h b/Controllers/DDPController/RGBController_DDP.h new file mode 100644 index 0000000..4fa40c3 --- /dev/null +++ b/Controllers/DDPController/RGBController_DDP.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_DDP.h | +| | +| RGBController for DDP devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "DDPController.h" + +class RGBController_DDP : public RGBController +{ +public: + RGBController_DDP(std::vector device_list); + ~RGBController_DDP(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void SetKeepaliveTime(unsigned int time_ms); + +private: + std::vector devices; + DDPController* controller; +}; diff --git a/Controllers/DMXController/DMXControllerDetect.cpp b/Controllers/DMXController/DMXControllerDetect.cpp new file mode 100644 index 0000000..0a0d1c2 --- /dev/null +++ b/Controllers/DMXController/DMXControllerDetect.cpp @@ -0,0 +1,144 @@ +/*---------------------------------------------------------*\ +| DMXControllerDetect.cpp | +| | +| Detector for DMX devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "Detector.h" +#include "RGBController_DMX.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectDMXControllers * +* * +* Detect devices supported by the DMX driver * +* * +\******************************************************************************************/ + +void DetectDMXControllers() +{ + json dmx_settings; + + std::vector> device_lists; + DMXDevice dev; + + /*-------------------------------------------------*\ + | Get DMX settings from settings manager | + \*-------------------------------------------------*/ + dmx_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("DMXDevices"); + + /*-------------------------------------------------*\ + | If the DMX settings contains devices, process | + \*-------------------------------------------------*/ + if(dmx_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < dmx_settings["devices"].size(); device_idx++) + { + /*-------------------------------------------------*\ + | Clear DMX device data | + \*-------------------------------------------------*/ + dev.name = ""; + dev.keepalive_time = 0; + + if(dmx_settings["devices"][device_idx].contains("name")) + { + dev.name = dmx_settings["devices"][device_idx]["name"]; + } + + if(dmx_settings["devices"][device_idx].contains("port")) + { + dev.port = dmx_settings["devices"][device_idx]["port"]; + } + + if(dmx_settings["devices"][device_idx].contains("keepalive_time")) + { + dev.keepalive_time = dmx_settings["devices"][device_idx]["keepalive_time"]; + } + + if(dmx_settings["devices"][device_idx].contains("red_channel")) + { + dev.red_channel = dmx_settings["devices"][device_idx]["red_channel"]; + } + + if(dmx_settings["devices"][device_idx].contains("green_channel")) + { + dev.green_channel = dmx_settings["devices"][device_idx]["green_channel"]; + } + + if(dmx_settings["devices"][device_idx].contains("blue_channel")) + { + dev.blue_channel = dmx_settings["devices"][device_idx]["blue_channel"]; + } + + if(dmx_settings["devices"][device_idx].contains("brightness_channel")) + { + dev.brightness_channel = dmx_settings["devices"][device_idx]["brightness_channel"]; + } + + /*---------------------------------------------------------*\ + | Determine whether to create a new list or add this device | + | to an existing list. A device is added to an existing | + | list if both devices share one or more universes for the | + | same output destination | + \*---------------------------------------------------------*/ + bool device_added_to_existing_list = false; + + /*---------------------------------------------------------*\ + | Track grouping for all controllers. | + \*---------------------------------------------------------*/ + for(unsigned int list_idx = 0; list_idx < device_lists.size(); list_idx++) + { + for(unsigned int device_idx = 0; device_idx < device_lists[list_idx].size(); device_idx++) + { + /*---------------------------------------------------------*\ + | Check if the port used by this new device is the same as | + | in the existing device. If so, add the new device to the | + | existing list. | + \*---------------------------------------------------------*/ + if(1) + { + device_lists[list_idx].push_back(dev); + device_added_to_existing_list = true; + break; + } + } + + if(device_added_to_existing_list) + { + break; + } + } + + /*---------------------------------------------------------*\ + | If the device did not overlap with existing devices, | + | create a new list for it | + \*---------------------------------------------------------*/ + if(!device_added_to_existing_list) + { + std::vector new_list; + + new_list.push_back(dev); + + device_lists.push_back(new_list); + } + } + + + for(unsigned int list_idx = 0; list_idx < device_lists.size(); list_idx++) + { + RGBController_DMX* rgb_controller; + rgb_controller = new RGBController_DMX(device_lists[list_idx]); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + +} /* DetectDMXControllers() */ + +REGISTER_DETECTOR("DMX", DetectDMXControllers); diff --git a/Controllers/DMXController/RGBController_DMX.cpp b/Controllers/DMXController/RGBController_DMX.cpp new file mode 100644 index 0000000..230201e --- /dev/null +++ b/Controllers/DMXController/RGBController_DMX.cpp @@ -0,0 +1,232 @@ +/*---------------------------------------------------------*\ +| RGBController_DMX.cpp | +| | +| RGBController for DMX devices | +| | +| Adam Honse (CalcProgrammer1) 30 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "RGBController_DMX.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name DMX Devices + @category LEDStrip + @type Serial + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectDMXControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_DMX::RGBController_DMX(std::vector device_list) +{ + devices = device_list; + + name = "DMX Device Group"; + type = DEVICE_TYPE_LEDSTRIP; + description = "DMX Device"; + location = "DMX: " + devices[0].port; + + /*-----------------------------------------*\ + | If this controller only represents a | + | single device, use the device name for the| + | controller name | + \*-----------------------------------------*/ + if(devices.size() == 1) + { + name = devices[0].name; + } + + /*-----------------------------------------*\ + | Open OpenDMX port | + \*-----------------------------------------*/ + port = new serial_port(devices[0].port.c_str(), 250000, SERIAL_PORT_PARITY_NONE, SERIAL_PORT_SIZE_8, SERIAL_PORT_STOP_BITS_2, false); + + /*-----------------------------------------*\ + | Clear the RTS signal, which enables the | + | OpenDMX RS-485 drive enable | + \*-----------------------------------------*/ + port->serial_set_rts(false); + + /*-----------------------------------------*\ + | Set up modes | + \*-----------------------------------------*/ + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness = 255; + Direct.brightness_min = 0; + Direct.brightness_max = 255; + modes.push_back(Direct); + + keepalive_delay = 0ms; + + SetupZones(); + + for (std::size_t device_idx = 0; device_idx < devices.size(); device_idx++) + { + /*-----------------------------------------*\ + | Update keepalive delay | + \*-----------------------------------------*/ + if(devices[device_idx].keepalive_time > 0) + { + if(keepalive_delay.count() == 0 || keepalive_delay.count() > devices[device_idx].keepalive_time) + { + keepalive_delay = std::chrono::milliseconds(devices[device_idx].keepalive_time); + } + } + } + + if(keepalive_delay.count() > 0) + { + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_DMX::KeepaliveThreadFunction, this); + } + else + { + keepalive_thread_run = 0; + keepalive_thread = nullptr; + } +} + +RGBController_DMX::~RGBController_DMX() +{ + if(keepalive_thread != nullptr) + { + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + } + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + if(zones[zone_index].matrix_map->map != NULL) + { + delete zones[zone_index].matrix_map->map; + } + + delete zones[zone_index].matrix_map; + } + } +} + +void RGBController_DMX::SetupZones() +{ + /*-----------------------------------------*\ + | Add Zones | + \*-----------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < devices.size(); zone_idx++) + { + zone led_zone; + led_zone.name = devices[zone_idx].name; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + + zones.push_back(led_zone); + } + + /*-----------------------------------------*\ + | Add LEDs | + \*-----------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + + new_led.name = zones[zone_idx].name + " LED "; + new_led.name.append(std::to_string(led_idx)); + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_DMX::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_DMX::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + unsigned char dmx_data[513]; + + memset(dmx_data, 0, sizeof(dmx_data)); + + for(unsigned int device_idx = 0; device_idx < devices.size(); device_idx++) + { + if(devices[device_idx].brightness_channel > 0) + { + dmx_data[devices[device_idx].brightness_channel] = modes[0].brightness; + } + + if(devices[device_idx].red_channel > 0) + { + dmx_data[devices[device_idx].red_channel] = RGBGetRValue(colors[device_idx]); + } + + if(devices[device_idx].green_channel > 0) + { + dmx_data[devices[device_idx].green_channel] = RGBGetGValue(colors[device_idx]); + } + + if(devices[device_idx].blue_channel > 0) + { + dmx_data[devices[device_idx].blue_channel] = RGBGetBValue(colors[device_idx]); + } + } + + port->serial_break(); + port->serial_write((char*)&dmx_data, sizeof(dmx_data)); +} + +void RGBController_DMX::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DMX::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DMX::DeviceUpdateMode() +{ + +} + +void RGBController_DMX::KeepaliveThreadFunction() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_update_time) > ( keepalive_delay * 0.95f ) ) + { + UpdateLEDs(); + } + std::this_thread::sleep_for(keepalive_delay / 2); + } +} diff --git a/Controllers/DMXController/RGBController_DMX.h b/Controllers/DMXController/RGBController_DMX.h new file mode 100644 index 0000000..3cc44f8 --- /dev/null +++ b/Controllers/DMXController/RGBController_DMX.h @@ -0,0 +1,54 @@ +/*---------------------------------------------------------*\ +| RGBController_DMX.h | +| | +| RGBController for DMX devices | +| | +| Adam Honse (CalcProgrammer1) 30 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "serial_port.h" +struct DMXDevice +{ + std::string name; + std::string port; + unsigned int keepalive_time; + unsigned int red_channel; + unsigned int green_channel; + unsigned int blue_channel; + unsigned int brightness_channel; +}; + +class RGBController_DMX : public RGBController +{ +public: + RGBController_DMX(std::vector device_list); + ~RGBController_DMX(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThreadFunction(); + +private: + std::vector devices; + serial_port * port; + std::thread * keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::milliseconds keepalive_delay; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/DRGBController/DRGBController.cpp b/Controllers/DRGBController/DRGBController.cpp new file mode 100644 index 0000000..8fb98bb --- /dev/null +++ b/Controllers/DRGBController/DRGBController.cpp @@ -0,0 +1,155 @@ +/*---------------------------------------------------------*\ +| DRGBController.cpp | +| | +| Driver for DRGBmods | +| | +| Zhi Yan 25 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "DRGBController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +DRGBController::DRGBController(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + device_pid = pid; + + /*-----------------------------------------------------*\ + | Exit hardware effects. Start a thread to continuously| + | send a keepalive packet every 500ms | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&DRGBController::KeepaliveThread, this); +} + +DRGBController::~DRGBController() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + hid_close(dev); +} + +void DRGBController::KeepaliveThread() +{ + unsigned char sleep_buf[65]; + sleep_buf[0] = 0x65; + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_commit_time) > std::chrono::milliseconds(500)) + { + SendPacketFS(sleep_buf, 1, 0); + } + std::this_thread::sleep_for(300ms); + } +} + +std::string DRGBController::GetFirmwareString() +{ + return "v"+std::to_string(version[0]) + "." + std::to_string(version[1]) + "." + std::to_string(version[2]) + "." + std::to_string(version[3]); +} + +std::string DRGBController::GetLocationString() +{ + return("HID: " + location); +} + +std::string DRGBController::GetNameString() +{ + return(name); +} + +std::string DRGBController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned short DRGBController::GetDevicePID() +{ + return(device_pid); +} + +void DRGBController::SetChannelLEDs(unsigned char /*channel*/, RGBColor* /*colors*/, unsigned int /*num_colors*/) +{ + +} + +void DRGBController::SendPacket(unsigned char* colors, unsigned int buf_packets , unsigned int LEDtotal) +{ + unsigned char usb_buf[1025]; + unsigned int buf_idx = 0; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0x00; + + /*-----------------------------------------------------*\ + | Update last commit time | + \*-----------------------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); + + unsigned int HigCount = LEDtotal / 256 >= 1 ? 1 : 0; + unsigned int LowCount = LEDtotal >= DRGB_V4_ONE_PACKAGE_SIZE ? 60 : (LEDtotal % 256) ; + LEDtotal = LEDtotal <= DRGB_V4_ONE_PACKAGE_SIZE ? 0 : (LEDtotal-DRGB_V4_ONE_PACKAGE_SIZE); + for(unsigned int i = 0; i < buf_packets; i++) + { + usb_buf[1] = i + 100 ; + usb_buf[2] = buf_packets + 99 ; + usb_buf[3] = HigCount; + usb_buf[4] = LowCount; + buf_idx = i*1020; + memcpy(usb_buf + 5, colors + buf_idx, 1020); + hid_write(dev, usb_buf, 1025); + if(LEDtotal) + { + HigCount = LEDtotal / 256 >= 1 ? 1 : 0; + LowCount = LEDtotal >= DRGB_V4_PACKAGE_SIZE ? 84 : (LEDtotal % 256) ; + LEDtotal = LEDtotal <= DRGB_V4_PACKAGE_SIZE ? 0 : (LEDtotal-DRGB_V4_PACKAGE_SIZE); + } + } +} + +void DRGBController::SendPacketFS(unsigned char* colors, unsigned int buf_packets , unsigned int Array) +{ + unsigned char usb_buf[65] = {0}; + unsigned int current_index = 0; + + /*-----------------------------------------------------*\ + | Update last commit time | + \*-----------------------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); + + if(Array == 0x64 || Array == 0x47) + { + const unsigned int offset = (Array == 0x64) ? 100 : 92; + + for(unsigned int i = 0; i < buf_packets; i++) + { + const bool is_last_packet = (i == buf_packets - 1); + usb_buf[1] = is_last_packet ? (Array + offset + i) : (Array + i); + current_index = i * 63; + memcpy(usb_buf + 2, colors + current_index, 63); + + hid_write(dev, usb_buf, 65); + } + } + else + { + memcpy(usb_buf + 1, colors, 64); + hid_write(dev, usb_buf, 65); + } +} diff --git a/Controllers/DRGBController/DRGBController.h b/Controllers/DRGBController/DRGBController.h new file mode 100644 index 0000000..ec24f3c --- /dev/null +++ b/Controllers/DRGBController/DRGBController.h @@ -0,0 +1,48 @@ +/*---------------------------------------------------------*\ +| DRGBController.h | +| | +| Driver for DRGBmods | +| | +| Zhi Yan 25 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define DRGB_V4_ONE_PACKAGE_SIZE 316 +#define DRGB_V4_PACKAGE_SIZE 340 +#define DRGB_V3_PACKAGE_SIZE 21 +#define DRGB_V2_PACKAGE_SIZE 20 + +class DRGBController +{ +public: + DRGBController(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name); + ~DRGBController(); + + void KeepaliveThread(); + std::string GetFirmwareString(); + std::string GetLocationString(); + std::string GetNameString(); + std::string GetSerialString(); + unsigned short GetDevicePID(); + void SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors); + void SendPacket(unsigned char* colors,unsigned int buf_packets ,unsigned int LEDtotal); + void SendPacketFS(unsigned char* colors,unsigned int buf_packets ,unsigned int Array); +private: + hid_device* dev; + std::string location; + std::string name; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_commit_time; + unsigned char version[4] = {0, 0, 0,0}; + unsigned short device_pid; +}; diff --git a/Controllers/DRGBController/DRGBControllerDetect.cpp b/Controllers/DRGBController/DRGBControllerDetect.cpp new file mode 100644 index 0000000..d027c50 --- /dev/null +++ b/Controllers/DRGBController/DRGBControllerDetect.cpp @@ -0,0 +1,72 @@ +/*---------------------------------------------------------*\ +| DRGBControllerDetect.cpp | +| | +| Driver for DRGBmods | +| | +| Zhi Yan 25 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "DRGBController.h" +#include "RGBController_DRGB.h" + +void DetectDRGBControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + wchar_t product[128]; + hid_get_product_string(dev, product, 128); + std::wstring product_str(product); + + DRGBController* controller = new DRGBController(dev, info->path, info->product_id, name); + RGBController_DRGB* rgb_controller = new RGBController_DRGB(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("DeepRGB LED V4", DetectDRGBControllers, DRGBV4_VID, DRGB_LED_V4_PID); +REGISTER_HID_DETECTOR("DeepRGB ULTRA V4F", DetectDRGBControllers, DRGBV4_VID, DRGB_ULTRA_V4F_PID); +REGISTER_HID_DETECTOR("DeepRGB CORE V4F", DetectDRGBControllers, DRGBV4_VID, DRGB_CORE_V4F_PID); +REGISTER_HID_DETECTOR("DeepRGB SIG V4F", DetectDRGBControllers, DRGBV4_VID, DRGB_SIG_V4F_PID); + +REGISTER_HID_DETECTOR("Airgoo AG-DRGB04", DetectDRGBControllers, DRGBV4_VID, DRGB_AG_04_V4F_PID); +REGISTER_HID_DETECTOR("Airgoo AG-DRGB16", DetectDRGBControllers, DRGBV4_VID, DRGB_AG_16_V4F_PID); +REGISTER_HID_DETECTOR("Airgoo AG-DRGB08", DetectDRGBControllers, DRGBV4_VID, DRGB_AG_08_PID); +REGISTER_HID_DETECTOR("Airgoo AG-F8-DRGB08", DetectDRGBControllers, DRGBV4_VID, DRGB_AG_08_F08_PID); +REGISTER_HID_DETECTOR("Airgoo AG-F12-DRGB16", DetectDRGBControllers, DRGBV4_VID, DRGB_AG_16_F12_PID); + +REGISTER_HID_DETECTOR("DeepRGB L8 V5", DetectDRGBControllers, DRGBV4_VID, DRGB_L8_V5_PID); +REGISTER_HID_DETECTOR("DeepRGB U16 V5", DetectDRGBControllers, DRGBV4_VID, DRGB_U16_V5_PID); +REGISTER_HID_DETECTOR("DeepRGB U16 V5F", DetectDRGBControllers, DRGBV4_VID, DRGB_U16_V5F_PID); +REGISTER_HID_DETECTOR("DeepRGB C16 V5", DetectDRGBControllers, DRGBV4_VID, DRGB_C16_V5_PID); +REGISTER_HID_DETECTOR("DeepRGB C16 V5F", DetectDRGBControllers, DRGBV4_VID, DRGB_C16_V5F_PID); +REGISTER_HID_DETECTOR("DeepRGB S16 V5F", DetectDRGBControllers, DRGBV4_VID, DRGB_S16_V5F_PID); + +REGISTER_HID_DETECTOR("DeepRGB LED", DetectDRGBControllers, DRGBV3_VID, DRGB_LED_V3_PID); +REGISTER_HID_DETECTOR("DeepRGB Ultra V3", DetectDRGBControllers, DRGBV3_VID, DRGB_Ultra_V3_PID); +REGISTER_HID_DETECTOR("DeepRGB CORE V3", DetectDRGBControllers, DRGBV3_VID, DRGB_CORE_V3_PID); +REGISTER_HID_DETECTOR("DeepRGB E8 F", DetectDRGBControllers, DRGBV3_VID, DRGB_E8_F_PID); +REGISTER_HID_DETECTOR("DeepRGB E8", DetectDRGBControllers, DRGBV3_VID, DRGB_E8_PID); +REGISTER_HID_DETECTOR("DeepRGB E16", DetectDRGBControllers, DRGBV3_VID, DRGB_E16_PID); +REGISTER_HID_DETECTOR("NEEDMAX 10 ELITE", DetectDRGBControllers, DRGBV3_VID, DM_10_PID); +REGISTER_HID_DETECTOR("JPU ELITE", DetectDRGBControllers, DRGBV3_VID, JPU_12_PID); + +REGISTER_HID_DETECTOR("DeepRGB LED Controller", DetectDRGBControllers, DRGBV2_VID, DRGB_LED_PID); +REGISTER_HID_DETECTOR("DeepRGB ULTRA", DetectDRGBControllers, DRGBV2_VID, DRGB_ULTRA_PID); +REGISTER_HID_DETECTOR("DeepRGB SIG AB", DetectDRGBControllers, DRGBV2_VID, DRGB_SIG_AB_PID); +REGISTER_HID_DETECTOR("DeepRGB SIG CD", DetectDRGBControllers, DRGBV2_VID, DRGB_SIG_CD_PID); +REGISTER_HID_DETECTOR("DeepRGB Strimer Controller", DetectDRGBControllers, DRGBV2_VID, DRGB_Strimer_PID); + +REGISTER_HID_DETECTOR("YICO 8 ELITE", DetectDRGBControllers, YICO_VID, YICO_8_PID); +REGISTER_HID_DETECTOR("YICO 08 ELITE", DetectDRGBControllers, YICO_VID, YICO_08_PID); +REGISTER_HID_DETECTOR("YICO 08 ELITE", DetectDRGBControllers, YICO_VID, YICO_08_1_PID); +REGISTER_HID_DETECTOR("YICO 14 LCD", DetectDRGBControllers, DRGBV3_VID, YICO_14_PID); +REGISTER_HID_DETECTOR("YICO 16 ELITE", DetectDRGBControllers, DRGBV4_VID, YICO_16_PID); + diff --git a/Controllers/DRGBController/RGBController_DRGB.cpp b/Controllers/DRGBController/RGBController_DRGB.cpp new file mode 100644 index 0000000..739d132 --- /dev/null +++ b/Controllers/DRGBController/RGBController_DRGB.cpp @@ -0,0 +1,522 @@ +/*---------------------------------------------------------*\ +| RGBController_DRGB.cpp | +| | +| Driver for DRGBmods | +| | +| Zhi Yan 25 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_DRGB.h" + +/**------------------------------------------------------------------*\ + @name DRGB Controller + @category LEDStrip + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectDRGBControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_DRGB::RGBController_DRGB(DRGBController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "DRGB"; + description = "DRGB Controller Device"; + type = DEVICE_TYPE_LEDSTRIP; + version = controller->GetFirmwareString(); + location = controller->GetLocationString(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_DRGB::~RGBController_DRGB() +{ + delete controller; +} + +void RGBController_DRGB::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + if(zones.size() == 0) + { + first_run = true; + } + leds.clear(); + colors.clear(); + + unsigned int NUM_CHANNELS = 0; + unsigned int NUM_Channel_led = 0; + switch(controller->GetDevicePID()) + { + case DRGB_LED_V4_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 512; + Version = 4; + break; + case DRGB_ULTRA_V4F_PID: + NUM_CHANNELS = 16; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_CORE_V4F_PID: + NUM_CHANNELS = 32; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_SIG_V4F_PID: + NUM_CHANNELS = 36; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_AG_04_V4F_PID: + NUM_CHANNELS = 4; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_AG_08_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_AG_08_F08_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_AG_16_V4F_PID: + NUM_CHANNELS = 16; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_AG_16_F12_PID: + NUM_CHANNELS = 16; + NUM_Channel_led = 256; + Version = 4; + break; + + case DRGB_L8_V5_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 512; + Version = 4; + break; + case DRGB_U16_V5_PID: + NUM_CHANNELS = 16; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_U16_V5F_PID: + NUM_CHANNELS = 16; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_C16_V5_PID: + NUM_CHANNELS = 32; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_C16_V5F_PID: + NUM_CHANNELS = 32; + NUM_Channel_led = 256; + Version = 4; + break; + case DRGB_S16_V5F_PID: + NUM_CHANNELS = 32; + NUM_Channel_led = 256; + Version = 4; + break; + + case DRGB_LED_V3_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 256; + Version = 3; + break; + case DRGB_Ultra_V3_PID: + NUM_CHANNELS = 16; + NUM_Channel_led = 256; + Version = 3; + break; + case DRGB_CORE_V3_PID: + NUM_CHANNELS = 30; + NUM_Channel_led = 256; + Version = 3; + break; + case DRGB_E8_F_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 132; + Version = 1; + break; + case DRGB_E8_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 132; + Version = 1; + break; + case DRGB_E16_PID: + NUM_CHANNELS = 16; + NUM_Channel_led = 132; + Version = 1; + break; + case DM_10_PID: + NUM_CHANNELS = 10; + NUM_Channel_led = 132; + Version = 1; + break; + case JPU_12_PID: + NUM_CHANNELS = 12; + NUM_Channel_led = 60; + Version = 1; + break; + + case DRGB_LED_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 256; + Version = 2; + break; + case DRGB_ULTRA_PID: + NUM_CHANNELS = 16; + NUM_Channel_led = 256; + Version = 2; + break; + case DRGB_SIG_AB_PID: + NUM_CHANNELS = 16; + NUM_Channel_led = 256; + Version = 2; + break; + case DRGB_SIG_CD_PID: + NUM_CHANNELS = 6; + NUM_Channel_led = 256; + Version = 2; + break; + case DRGB_Strimer_PID: + NUM_CHANNELS = 6; + NUM_Channel_led = 256; + Version = 2; + break; + + case YICO_8_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 256; + Version = 3; + break; + case YICO_08_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 256; + Version = 3; + break; + case YICO_08_1_PID: + NUM_CHANNELS = 8; + NUM_Channel_led = 132; + Version = 3; + break; + case YICO_14_PID: + NUM_CHANNELS = 14; + NUM_Channel_led = 132; + Version = 1; + break; + case YICO_16_PID: + NUM_CHANNELS = 16; + NUM_Channel_led = 256; + Version = 4; + break; + } + + zones.resize(NUM_CHANNELS); + + for(unsigned int channel_idx = 0; channel_idx < NUM_CHANNELS; channel_idx++) + { + char ch_idx_string[4]; + if(NUM_CHANNELS == 6) + { + if(channel_idx==0) + { + snprintf(ch_idx_string, 2, "%d", channel_idx+1 ); + zones[channel_idx].name = "Strimer ATX"; + } + else if(channel_idx<3) + { + snprintf(ch_idx_string, 2, "%d", channel_idx ); + zones[channel_idx].name = "Channel C"; + } + else if(channel_idx==3) + { + snprintf(ch_idx_string, 2, "%d", channel_idx-2 ); + zones[channel_idx].name = "Strimer GPU"; + } + else if(channel_idx<6) + { + snprintf(ch_idx_string, 2, "%d", channel_idx -3); + zones[channel_idx].name = "Channel D"; + } + } + else if(NUM_CHANNELS == 10 || NUM_CHANNELS == 12) + { + snprintf(ch_idx_string, 4, "%d", channel_idx+1 ); + zones[channel_idx].name = "Channel "; + } + else if(NUM_CHANNELS == 14) + { + if(channel_idx<4) + { + snprintf(ch_idx_string, 3, "%d", channel_idx+1 ); + zones[channel_idx].name = "LCD "; + } + else if(channel_idx<6) + { + snprintf(ch_idx_string, 3, "%d", channel_idx+1 ); + zones[channel_idx].name = "LED "; + } + else if(channel_idx<16) + { + snprintf(ch_idx_string, 3, "%d", channel_idx-5 ); + zones[channel_idx].name = "ARGB "; + } + } + else if(channel_idx<8) + { + snprintf(ch_idx_string, 3, "%d", channel_idx + 1); + zones[channel_idx].name = "Channel A"; + } + else if(channel_idx<16) + { + snprintf(ch_idx_string, 2, "%d", channel_idx -7); + zones[channel_idx].name = "Channel B"; + } + else if(NUM_CHANNELS == 30) + { + if(channel_idx<24) + { + snprintf(ch_idx_string, 2, "%d", channel_idx -15); + zones[channel_idx].name = "Channel C"; + } + else if(channel_idx<30) + { + snprintf(ch_idx_string, 2, "%d", channel_idx -23); + zones[channel_idx].name = "Channel D"; + } + } + else if(channel_idx<22) + { + snprintf(ch_idx_string, 2, "%d", channel_idx -15); + zones[channel_idx].name = "Channel C"; + } + else if(channel_idx<28) + { + snprintf(ch_idx_string, 2, "%d", channel_idx -21); + zones[channel_idx].name = "Channel D"; + } + else if(channel_idx<36) + { + snprintf(ch_idx_string, 2, "%d", channel_idx -27); + zones[channel_idx].name = "Channel E"; + } + zones[channel_idx].name.append(ch_idx_string); + zones[channel_idx].type = ZONE_TYPE_LINEAR; + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = NUM_Channel_led; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + + zones[channel_idx].matrix_map = NULL; + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + char led_idx_string[4]; + snprintf(led_idx_string, 4, "%d", led_ch_idx + 1); + led new_led; + new_led.name = "LED "; + new_led.name.append(led_idx_string); + leds.push_back(new_led); + leds_channel.push_back(channel_idx); + } + } + + SetupColors(); + +} + +void RGBController_DRGB::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + SetupZones(); + } +} + +void RGBController_DRGB::DeviceUpdateLEDs() +{ + switch(Version) + { + case 4: + { + unsigned int led_index = 0; + unsigned char RGBData[8192*3 + 72] = {0}; + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + unsigned char LEDnum = zones[zone_idx].leds_count; + unsigned int HighCount = (LEDnum & 0xFFFF)>>8; + unsigned int LowCount = LEDnum & 0xFF; + RGBData[zone_idx * 2 ] = HighCount; + RGBData[zone_idx * 2 + 1] = LowCount; + for(unsigned int i=0; i> 8) & 0xFF; + RGBData[led_index * 3 +74] = (RGBcolors >> 16) & 0xFF; + led_index++; + } + if(led_index>8192) + { + break; + } + } + unsigned int col_packets = 1 ; + if(led_index > DRGB_V4_ONE_PACKAGE_SIZE) + { + col_packets = 1 + ((led_index - DRGB_V4_ONE_PACKAGE_SIZE) / DRGB_V4_PACKAGE_SIZE) + (((led_index - DRGB_V4_ONE_PACKAGE_SIZE) % DRGB_V4_PACKAGE_SIZE) > 0); + } + controller->SendPacket(&RGBData[0], col_packets,led_index); + break; + } + + case 3: + { + unsigned int led_index = 0; + unsigned char RGBData[1801*3] = {0}; + unsigned char ArrayData[64] = {0}; + ArrayData[0] = 0x60; + ArrayData[1] = 0xBB; + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + unsigned char LEDnum = zones[zone_idx].leds_count; + unsigned int HighCount = (LEDnum & 0xFFFF)>>8; + unsigned int LowCount = LEDnum & 0xFF; + ArrayData[zone_idx * 2 + 2] = HighCount; + ArrayData[zone_idx * 2 + 3] = LowCount; + for(unsigned int i=0; i> 8) & 0xFF; + RGBData[led_index * 3 +2] = (RGBcolors >> 16) & 0xFF; + led_index++; + } + if(led_index>1800) + { + break; + } + } + unsigned int col_packets = (led_index / DRGB_V3_PACKAGE_SIZE) + ((led_index % DRGB_V3_PACKAGE_SIZE) > 0); + controller->SendPacketFS(&ArrayData[0], 1,0); + controller->SendPacketFS(&RGBData[0], col_packets,0x64); + break; + } + + case 2: + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + unsigned char RGBData[256*3] = {0}; + unsigned char ArrayData[64] = {0}; + unsigned char LEDnum = zones[zone_idx].leds_count; + for(unsigned int i = 0; i < LEDnum; i++) + { + unsigned int RGBcolors = zones[zone_idx].colors[i]; + RGBData[i * 3] = RGBcolors & 0xFF; + RGBData[i * 3 +1] = (RGBcolors >> 8) & 0xFF; + RGBData[i * 3 +2] = (RGBcolors >> 16) & 0xFF; + } + + unsigned char NumPackets = LEDnum / DRGB_V2_PACKAGE_SIZE + ((LEDnum % DRGB_V2_PACKAGE_SIZE) > 0); + for(unsigned char CurrPacket = 1; CurrPacket <= NumPackets; CurrPacket++) + { + ArrayData[0] = CurrPacket; + ArrayData[1] = NumPackets; + ArrayData[2] = (unsigned char)zone_idx; + ArrayData[3] = 0xBB; + for(unsigned int i=0; i<60;i++) + { + ArrayData[4+i] = RGBData[(CurrPacket -1)*60 + i]; + } + controller->SendPacketFS(&ArrayData[0], 1,0); + } + } + break; + + case 1: + { + unsigned int led_index = 0; + unsigned char RGBData[1801*3] = {0}; + unsigned char ArrayData[64] = {0}; + ArrayData[0] = 0x46; + ArrayData[1] = 0xBB; + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + unsigned char LEDnum = zones[zone_idx].leds_count; + unsigned int HighCount = (LEDnum & 0xFFFF)>>8; + unsigned int LowCount = LEDnum & 0xFF; + ArrayData[zone_idx * 2 + 2] = HighCount; + ArrayData[zone_idx * 2 + 3] = LowCount; + for(unsigned int i=0; i> 8) & 0xFF; + RGBData[led_index * 3 +2] = (RGBcolors >> 16) & 0xFF; + led_index++; + } + if(led_index>1800) + { + break; + } + } + unsigned int col_packets = (led_index / DRGB_V3_PACKAGE_SIZE) + ((led_index % DRGB_V3_PACKAGE_SIZE) > 0); + controller->SendPacketFS(&ArrayData[0], 1,0); + controller->SendPacketFS(&RGBData[0], col_packets,0x47); + break; + } + } +} + +void RGBController_DRGB::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_DRGB::UpdateSingleLED(int led) +{ + unsigned int channel = leds_channel[led]; + controller->SetChannelLEDs(channel, zones[channel].colors, zones[channel].leds_count); +} + +void RGBController_DRGB::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/DRGBController/RGBController_DRGB.h b/Controllers/DRGBController/RGBController_DRGB.h new file mode 100644 index 0000000..b689d80 --- /dev/null +++ b/Controllers/DRGBController/RGBController_DRGB.h @@ -0,0 +1,77 @@ +/*---------------------------------------------------------*\ +| RGBController_DRGB.h | +| | +| Driver for DRGBmods | +| | +| Zhi Yan 25 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "DRGBController.h" + +#define DRGBV4_VID 0x2486 +#define DRGB_LED_V4_PID 0x3608 +#define DRGB_ULTRA_V4F_PID 0x3616 +#define DRGB_CORE_V4F_PID 0x3628 +#define DRGB_SIG_V4F_PID 0x3636 +#define DRGB_AG_04_V4F_PID 0x3204 +#define DRGB_AG_16_V4F_PID 0x3216 +#define DRGB_AG_08_PID 0x3F08 +#define DRGB_AG_08_F08_PID 0x3F16 +#define DRGB_AG_16_F12_PID 0x3F28 + +#define DRGB_L8_V5_PID 0x3208 +#define DRGB_U16_V5_PID 0x3215 +#define DRGB_U16_V5F_PID 0x3217 +#define DRGB_C16_V5_PID 0x3228 +#define DRGB_C16_V5F_PID 0x3229 +#define DRGB_S16_V5F_PID 0x3232 + +#define DRGBV3_VID 0x2023 +#define DRGB_LED_V3_PID 0x1209 +#define DRGB_Ultra_V3_PID 0x1221 +#define DRGB_CORE_V3_PID 0x1226 +#define DRGB_E8_F_PID 0x1408 +#define DRGB_E8_PID 0x1407 +#define DRGB_E16_PID 0x1416 +#define DM_10_PID 0x1410 +#define JPU_12_PID 0x1412 + +#define DRGBV2_VID 0x2023 +#define DRGB_LED_PID 0x1208 +#define DRGB_ULTRA_PID 0x1220 +#define DRGB_SIG_AB_PID 0x1210 +#define DRGB_SIG_CD_PID 0x1211 +#define DRGB_Strimer_PID 0x1215 + +#define YICO_VID 0x1368 +#define YICO_8_PID 0x6077 +#define YICO_08_PID 0x6078 +#define YICO_08_1_PID 0x6079 +#define YICO_14_PID 0x1614 +#define YICO_16_PID 0x1616 + +class RGBController_DRGB : public RGBController +{ +public: + RGBController_DRGB(DRGBController* controller_ptr); + ~RGBController_DRGB(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + +private: + DRGBController* controller; + std::vector leds_channel; + std::vector zones_channel; + unsigned int Version = 4; +}; diff --git a/Controllers/DarkProject/DarkProjectControllerDetect.cpp b/Controllers/DarkProject/DarkProjectControllerDetect.cpp new file mode 100644 index 0000000..5dddc73 --- /dev/null +++ b/Controllers/DarkProject/DarkProjectControllerDetect.cpp @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| DarkProjectControllerDetect.cpp | +| | +| Detector for Dark Project devices | +| | +| Chris M (DrNo) 08 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBController_DarkProjectKeyboard.h" + +/*---------------------------------------------------------*\ +| Dark Project vendor ID | +\*---------------------------------------------------------*/ +#define DARKPROJECT_VID 0x195D + +/*---------------------------------------------------------*\ +| Product IDs | +\*---------------------------------------------------------*/ +#define KD3B_V2_PID 0x2061 + +void DetectDarkProjectKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + DarkProjectKeyboardController* controller = new DarkProjectKeyboardController(dev, info->path, name); + RGBController_DarkProjectKeyboard* rgb_controller = new RGBController_DarkProjectKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Dark Project KD3B V2", DetectDarkProjectKeyboardControllers, DARKPROJECT_VID, KD3B_V2_PID, 2, 0xFFC2, 4); diff --git a/Controllers/DarkProject/DarkProjectKeyboardController.cpp b/Controllers/DarkProject/DarkProjectKeyboardController.cpp new file mode 100644 index 0000000..fec324c --- /dev/null +++ b/Controllers/DarkProject/DarkProjectKeyboardController.cpp @@ -0,0 +1,106 @@ +/*---------------------------------------------------------*\ +| DarkProjectKeyboardController.cpp | +| | +| Driver for Dark Project keyboard | +| | +| Chris M (DrNo) 08 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "DarkProjectKeyboardController.h" +#include "LogManager.h" +#include "StringUtils.h" + +static uint8_t packet_map[88] = +{ +/*00 ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 */ + 5, 11, 17, 23, 29, 35, 41, 47, 53, 59, + +/*10 F10 F11 F12 PRT SLK PBK ` 1 2 3 */ + 65, 71, 77, 83, 89, 95, 0, 6, 12, 18, + +/*20 4 5 6 7 8 9 0 - = BSP */ + 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, + +/*30 INS HME PUP TAB Q W E R T Y */ + 84, 90, 96, 1, 7, 13, 19, 25, 31, 37, + +/*40 U I O P [ ] \ DEL END PDN */ + 43, 49, 55, 61, 67, 73, 79, 85, 91, 97, + +/*50 CAP A S D F G H J K L */ + 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, + +/*60 ; ' ENT LSH Z X C V B N */ + 62, 68, 80, 3, 15, 21, 27, 33, 39, 45, + +/*70 M , . / RSH UP LCTL LWIN LALT SPC */ + 51, 57, 63, 69, 81, 93, 4, 10, 16, 34, + +/*80 RALT RFNC MENU RCTL LFT DWN RGT */ + 52, 58, 64, 76, 88, 94, 100 + +/* Missing Indexes 9, 22, 28, 40, 46, 70, 74, 75, 82, 86, 87, 92, 98, 99, 101 */ +}; + +DarkProjectKeyboardController::DarkProjectKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +DarkProjectKeyboardController::~DarkProjectKeyboardController() +{ + hid_close(dev); +} + +std::string DarkProjectKeyboardController::GetLocation() +{ + return("HID: " + location); +} + +std::string DarkProjectKeyboardController::GetName() +{ + return(name); +} + +std::string DarkProjectKeyboardController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void DarkProjectKeyboardController::SetLedsDirect(std::vector colors) +{ + uint8_t RGbuffer[DARKPROJECTKEYBOARD_PACKET_SIZE] = { 0x08, 0x07, 0x00, 0x00, 0x00 }; + uint8_t BAbuffer[DARKPROJECTKEYBOARD_PACKET_SIZE] = { 0x08, 0x07, 0x00, 0x01, 0x00 }; + + /*-----------------------------------------------------------------*\ + | Set up Direct packet | + | packet_map is the index of the Key from full_matrix_map and | + | the value is the position in the direct packet buffer | + \*-----------------------------------------------------------------*/ + for(size_t i = 0; i < colors.size(); i++) + { + RGBColor key = colors[i]; + uint16_t offset = packet_map[i]; + + RGbuffer[DARKPROJECTKEYBOARD_RED_BLUE_BYTE + offset] = RGBGetRValue(key); + RGbuffer[DARKPROJECTKEYBOARD_GREEN_BYTE + offset] = RGBGetGValue(key); + BAbuffer[DARKPROJECTKEYBOARD_RED_BLUE_BYTE + offset] = RGBGetBValue(key); + } + + hid_write(dev, RGbuffer, DARKPROJECTKEYBOARD_PACKET_SIZE); + hid_write(dev, BAbuffer, DARKPROJECTKEYBOARD_PACKET_SIZE); +} + diff --git a/Controllers/DarkProject/DarkProjectKeyboardController.h b/Controllers/DarkProject/DarkProjectKeyboardController.h new file mode 100644 index 0000000..e62e5d9 --- /dev/null +++ b/Controllers/DarkProject/DarkProjectKeyboardController.h @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| DarkProjectKeyboardController.h | +| | +| Driver for Dark Project keyboard | +| | +| Chris M (DrNo) 08 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define NA 0xFFFFFFFF +#define HID_MAX_STR 255 + +#define DARKPROJECTKEYBOARD_PACKET_SIZE 256 +#define DARKPROKECTKEYBOARD_TKL_KEYCOUNT 87 + +enum +{ + DARKPROJECTKEYBOARD_MODE_DIRECT = 0x01, //Direct Led Control - Independently set LEDs in zone +}; + +enum +{ + DARKPROJECTKEYBOARD_REPORT_BYTE = 1, + DARKPROJECTKEYBOARD_COMMAND_BYTE = 2, + DARKPROJECTKEYBOARD_RED_BLUE_BYTE = 5, + DARKPROJECTKEYBOARD_GREEN_BYTE = 107 +}; + +class DarkProjectKeyboardController +{ +public: + DarkProjectKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name); + ~DarkProjectKeyboardController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + + void SetLedsDirect(std::vector colors); +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/DarkProject/RGBController_DarkProjectKeyboard.cpp b/Controllers/DarkProject/RGBController_DarkProjectKeyboard.cpp new file mode 100644 index 0000000..8df1264 --- /dev/null +++ b/Controllers/DarkProject/RGBController_DarkProjectKeyboard.cpp @@ -0,0 +1,237 @@ +/*---------------------------------------------------------*\ +| RGBController_DarkProjectKeyboard.cpp | +| | +| RGBController for Dark Project keyboard | +| | +| Chris M (DrNo) 08 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_DarkProjectKeyboard.h" + +static unsigned int matrix_map[6][18] = +{ + { 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, NA, 9, 10, 11, 12, 13, 14, 15 }, + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, NA, 30, 31, 32 }, + { 33, NA, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49 }, + { 50, NA, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, NA, 62, NA, NA, NA }, + { 63, NA, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, NA, 74, NA, NA, 75, NA }, + { 76, 77, 78, NA, NA, NA, 79, NA, NA, NA, 80, 81, NA, 82, 83, 84, 85, 86 } +}; + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, //00 + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, //10 + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAUSE_BREAK, + + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, //20 + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, //30 + KEY_EN_HOME, + KEY_EN_PAGE_UP, + + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, //40 + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + + KEY_EN_CAPS_LOCK, //50 + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, //60 + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, //70 + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, //80 + KEY_EN_RIGHT_FUNCTION, + KEY_EN_MENU, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW +}; + +/**------------------------------------------------------------------*\ + @name Dark Project Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectDarkProjectKeyboardControllers + @comment The Dark Project keyboard controller currently supports + the full size KD3B Version 2 (ANSI layout). +\*-------------------------------------------------------------------*/ + +RGBController_DarkProjectKeyboard::RGBController_DarkProjectKeyboard(DarkProjectKeyboardController *controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Dark Project"; + type = DEVICE_TYPE_KEYBOARD; + description = "Dark Project Keyboard Device"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = DARKPROJECTKEYBOARD_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_DarkProjectKeyboard::~RGBController_DarkProjectKeyboard() +{ + delete controller; +} + +void RGBController_DarkProjectKeyboard::SetupZones() +{ + /*-------------------------------------------------*\ + | Create the Keyboard zone and add the matix map | + \*-------------------------------------------------*/ + zone KB_zone; + KB_zone.name = ZONE_EN_KEYBOARD; + KB_zone.type = ZONE_TYPE_MATRIX; + KB_zone.leds_min = DARKPROKECTKEYBOARD_TKL_KEYCOUNT; + KB_zone.leds_max = DARKPROKECTKEYBOARD_TKL_KEYCOUNT; + KB_zone.leds_count = DARKPROKECTKEYBOARD_TKL_KEYCOUNT; + + KB_zone.matrix_map = new matrix_map_type; + KB_zone.matrix_map->height = 6; + KB_zone.matrix_map->width = 18; + KB_zone.matrix_map->map = (unsigned int *)&matrix_map; + zones.push_back(KB_zone); + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_index = 0; zone_index < zones.size(); zone_index++) + { + for(unsigned int led_index = 0; led_index < zones[zone_index].leds_count; led_index++) + { + led new_led; + new_led.name = led_names[led_index]; + new_led.value = led_index; + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_DarkProjectKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + + +void RGBController_DarkProjectKeyboard::DeviceUpdateLEDs() +{ + controller->SetLedsDirect(colors); +} + +void RGBController_DarkProjectKeyboard::UpdateZoneLEDs(int zone) +{ + std::vector colour; + for(size_t i = 0; i < zones[zone].leds_count; i++) + { + colour.push_back(zones[zone].colors[i]); + } + + controller->SetLedsDirect(colour); +} + +void RGBController_DarkProjectKeyboard::UpdateSingleLED(int led) +{ + std::vector colour; + colour.push_back(colors[led]); + + controller->SetLedsDirect(colour); +} + +void RGBController_DarkProjectKeyboard::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | This device only supports `Direct` mode | + \*---------------------------------------------------------*/ +} diff --git a/Controllers/DarkProject/RGBController_DarkProjectKeyboard.h b/Controllers/DarkProject/RGBController_DarkProjectKeyboard.h new file mode 100644 index 0000000..2f9c315 --- /dev/null +++ b/Controllers/DarkProject/RGBController_DarkProjectKeyboard.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_DarkProjectKeyboard.h | +| | +| RGBController for Dark Project keyboard | +| | +| Chris M (DrNo) 08 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "LogManager.h" +#include "RGBController.h" +#include "DarkProjectKeyboardController.h" + +class RGBController_DarkProjectKeyboard : public RGBController +{ +public: + RGBController_DarkProjectKeyboard(DarkProjectKeyboardController* controller_ptr); + ~RGBController_DarkProjectKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + DarkProjectKeyboardController* controller; +}; diff --git a/Controllers/DasKeyboardController/DasKeyboardController.cpp b/Controllers/DasKeyboardController/DasKeyboardController.cpp new file mode 100644 index 0000000..31dd0c1 --- /dev/null +++ b/Controllers/DasKeyboardController/DasKeyboardController.cpp @@ -0,0 +1,354 @@ +/*---------------------------------------------------------*\ +| DasKeyboardController.cpp | +| | +| Driver for Das Keyboard keyboard | +| | +| Frank Niessen (denk_mal) 16 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "DasKeyboardController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +DasKeyboardController::DasKeyboardController(hid_device *dev_handle, const char *path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + version = ""; + useTraditionalSendData = false; + + SendInitialize(); +} + +DasKeyboardController::~DasKeyboardController() +{ + hid_close(dev); +} + +std::string DasKeyboardController::GetLayoutString() +{ + /*-----------------------------------------------------------*\ + | Experimental for now; should be '16 or 63' for US and '28' | + | for EU layout | + \*-----------------------------------------------------------*/ + if(version.length() < 17) + { + return("NONE"); + } + std::string layout_id = version.substr(3, 2); + + if(layout_id == "16" || layout_id == "63") + { + return("US"); + } + + return("EU"); +} + +std::string DasKeyboardController::GetLocationString() +{ + return("HID: " + location); +} + +std::string DasKeyboardController::GetNameString() +{ + return(name); +} + +std::string DasKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string DasKeyboardController::GetVersionString() +{ + if(version.length() < 17) + { + return(version); + } + + std::string fw_version = "V"; + fw_version += version.substr(6, 2); + fw_version += "."; + fw_version += version.substr(15, 2); + fw_version += ".0"; + + return(fw_version); +} + +void DasKeyboardController::SendColors(unsigned char key_id, unsigned char mode, + unsigned char red, unsigned char green, unsigned char blue) +{ + if(key_id < 130) + { + unsigned char usb_buf[] = {0xEA, + 0x08, + 0x78, + 0x08, + static_cast(key_id), + mode, + red, + green, + blue}; + + SendData(usb_buf, sizeof(usb_buf)); + } + else + { + /*-----------------------------------------------------*\ + | Special handling for the Q-Button; only color, no mode| + \*-----------------------------------------------------*/ + unsigned char usb_buf[] = {0xEA, + 0x06, + 0x78, + 0x06, + red, + green, + blue}; + + SendData(usb_buf, sizeof(usb_buf)); + } +} + + +void DasKeyboardController::SendInitialize() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + int cnt_receive = 0; + + while(!cnt_receive) + { + /*-----------------------------------------------------*\ + | Set up Initialize connection | + \*-----------------------------------------------------*/ + unsigned char usb_init[] = {0xEA, 0x02, 0xB0}; + SendData(usb_init, sizeof(usb_init)); + + /*-----------------------------------------------------*\ + | Get Version String | + \*-----------------------------------------------------*/ + cnt_receive = ReceiveData(usb_buf, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | check if the faster modern transfer method is working | + \*-----------------------------------------------------*/ + if(!cnt_receive) + { + if(useTraditionalSendData) + { + break; + } + useTraditionalSendData = true; + } + } + + std::string fw_version(reinterpret_cast(&usb_buf[2])); + version = fw_version; +} + +void DasKeyboardController::SendApply() +{ + /*-----------------------------------------------------*\ + | Set up Terminate Color packet | + \*-----------------------------------------------------*/ + unsigned char usb_buf_send[] = {0xEA, 0x03, 0x78, 0x0a}; + unsigned char usb_buf_receive[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + SendData(usb_buf_send, sizeof(usb_buf_send)); + ReceiveData(usb_buf_receive, sizeof(usb_buf_receive)); +} + +void DasKeyboardController::SendData(const unsigned char *data, const unsigned int length) +{ + if(useTraditionalSendData) + { + SendDataTraditional(data, length); + } + else + { + SendDataModern(data, length); + } +} + +void DasKeyboardController::SendDataModern(const unsigned char *data, const unsigned int length) +{ + /*-----------------------------------------------------*\ + | modern SendData (send whole bytes in one transfer) | + \*-----------------------------------------------------*/ + unsigned char usb_buf[65]; + + unsigned int err_cnt = 3; + int res = -1; + while(res == -1) + { + /*-----------------------------------------------------*\ + | Fill data into send buffer | + \*-----------------------------------------------------*/ + unsigned int chk_sum = 0; + usb_buf[0] = 1; + + for(unsigned int idx = 0; idx < length; idx++) + { + usb_buf[idx + 1] = data[idx]; + chk_sum ^= data[idx]; + } + usb_buf[length + 1] = chk_sum; + + res = hid_send_feature_report(dev, usb_buf, length + 2); + if(res == -1) + { + if(!err_cnt--) + { + return; + } + } + /*-----------------------------------------------------*\ + | Hack to work around a firmware bug in v21.27.0 | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(0.3ms); + } +} + +void DasKeyboardController::SendDataTraditional(const unsigned char *data, const unsigned int length) +{ + /*-----------------------------------------------------*\ + | traditional SendData (split into chunks of 8 byte) | + \*-----------------------------------------------------*/ + unsigned char usb_buf[9]; + + /*-----------------------------------------------------*\ + | Fill data into send buffer | + \*-----------------------------------------------------*/ + unsigned int err_cnt = 3; + unsigned int chk_sum = 0; + usb_buf[8] = 0; + + for(unsigned int idx = 0; idx < length + 1; idx += 7) + { + usb_buf[0] = 1; + for(unsigned int fld_idx = 1; fld_idx < 8; fld_idx++) + { + unsigned int tmp_idx = idx + fld_idx - 1; + if(tmp_idx < length) + { + usb_buf[fld_idx] = data[tmp_idx]; + chk_sum ^= data[tmp_idx]; + } + else if(tmp_idx == length) + { + usb_buf[fld_idx] = chk_sum; + } + else + { + usb_buf[fld_idx] = 0; + } + } + int res = hid_send_feature_report(dev, usb_buf, 8); + if(res == -1) + { + idx = 0; + if(!err_cnt--) + { + return; + } + } + + /*-----------------------------------------------------*\ + | Hack to work around a firmware bug in v21.27.0 | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(0.3ms); + } +} + +int DasKeyboardController::ReceiveData(unsigned char *data, const unsigned int max_length) +{ + unsigned char usb_buf[9]; + std::vector receive_buf; + + /*-----------------------------------------------------*\ + | Fill data from receive buffer | + \*-----------------------------------------------------*/ + unsigned int chk_sum = 0; + + do + { + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0x01; + + int res = hid_get_feature_report(dev, usb_buf, 8); + if(res == -1) + { + break; + } + + if(usb_buf[0]) + { + for(unsigned int ii = 0; ii < 8; ii++) + { + receive_buf.push_back(usb_buf[ii]); + chk_sum ^= usb_buf[ii]; + } + } + } while(usb_buf[0]); + + /*-----------------------------------------------------*\ + | clean up data buffer | + \*-----------------------------------------------------*/ + for(unsigned int ii = 0; ii < max_length; ii++) + { + data[ii] = 0; + } + + /*-----------------------------------------------------*\ + | If checksum is not correct, return with empty buffer | + \*-----------------------------------------------------*/ + if(chk_sum) + { + return(-1); + } + + unsigned int response_size = 0; + if(receive_buf.size() > 1) + { + response_size = receive_buf.at(1); + + if(response_size + 2 > receive_buf.size()) + { + return(-1); + } + if(response_size > max_length) + { + response_size = max_length; + } + + /*-----------------------------------------------------*\ + | Remove first two bytes (signature?) and content length| + \*-----------------------------------------------------*/ + for(unsigned int ii = 0; ii < response_size - 1; ii++) + { + data[ii] = receive_buf.at(ii + 2); + } + } + + return(response_size); +} diff --git a/Controllers/DasKeyboardController/DasKeyboardController.h b/Controllers/DasKeyboardController/DasKeyboardController.h new file mode 100644 index 0000000..a1984cd --- /dev/null +++ b/Controllers/DasKeyboardController/DasKeyboardController.h @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| DasKeyboardController.h | +| | +| Driver for Das Keyboard keyboard | +| | +| Frank Niessen (denk_mal) 16 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class DasKeyboardController +{ +public: + DasKeyboardController(hid_device *dev_handle, const char *path, std::string dev_name); + + ~DasKeyboardController(); + + std::string GetLayoutString(); + std::string GetLocationString(); + std::string GetNameString(); + std::string GetSerialString(); + std::string GetVersionString(); + + void SendColors(unsigned char key_id, unsigned char mode, unsigned char red, unsigned char green, unsigned char blue); + + void SendApply(); + +private: + hid_device *dev; + std::string location; + std::string name; + std::string version; + bool useTraditionalSendData; + + void SendInitialize(); + + void SendData(const unsigned char *data, unsigned int length); + + void SendDataTraditional(const unsigned char *data, unsigned int length); + + void SendDataModern(const unsigned char *data, unsigned int length); + + int ReceiveData(unsigned char *data, unsigned int max_length); +}; diff --git a/Controllers/DasKeyboardController/DasKeyboardControllerDetect.cpp b/Controllers/DasKeyboardController/DasKeyboardControllerDetect.cpp new file mode 100644 index 0000000..b7221b0 --- /dev/null +++ b/Controllers/DasKeyboardController/DasKeyboardControllerDetect.cpp @@ -0,0 +1,61 @@ +/*---------------------------------------------------------*\ +| DasKeyboardControllerDetect.cpp | +| | +| Detector for Das Keyboard keyboard | +| | +| Frank Niessen (denk_mal) 16 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "DasKeyboardController.h" +#include "RGBController_DasKeyboard.h" +#include + +/*-----------------------------------------------------*\ +| Das Keyboard vendor ID | +\*-----------------------------------------------------*/ +#define DAS_KEYBOARD_VID 0x24F0 + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define DAS_KEYBOARD_Q4_PID 0x2037 +#define DAS_KEYBOARD_Q5_PID 0x2020 +#define DAS_KEYBOARD_Q5S_PID 0x209A + +/******************************************************************************************\ +* * +* DetectDasKeyboardControllers * +* * +* Tests the USB address to see if a Das Keyboard RGB controller exists there. * +* We need the second interface to communicate with the keyboard * +* * +\******************************************************************************************/ + +void DetectDasKeyboardControllers(hid_device_info *info, const std::string &name) +{ + hid_device *dev = hid_open_path(info->path); + + if(dev) + { + DasKeyboardController *controller = new DasKeyboardController(dev, info->path, name); + + if(controller->GetLayoutString() == "NONE") + { + delete controller; + } + else + { + RGBController_DasKeyboard *rgb_controller = new RGBController_DasKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} /* DetectDasKeyboardControllers() */ + +REGISTER_HID_DETECTOR_IPU("Das Keyboard Q4 RGB", DetectDasKeyboardControllers, DAS_KEYBOARD_VID, DAS_KEYBOARD_Q4_PID, 1, 0x01, 0x80); +REGISTER_HID_DETECTOR_I ("Das Keyboard Q5 RGB", DetectDasKeyboardControllers, DAS_KEYBOARD_VID, DAS_KEYBOARD_Q5_PID, 1); +REGISTER_HID_DETECTOR_I ("Das Keyboard Q5S RGB", DetectDasKeyboardControllers, DAS_KEYBOARD_VID, DAS_KEYBOARD_Q5S_PID, 1); diff --git a/Controllers/DasKeyboardController/RGBController_DasKeyboard.cpp b/Controllers/DasKeyboardController/RGBController_DasKeyboard.cpp new file mode 100644 index 0000000..6a56263 --- /dev/null +++ b/Controllers/DasKeyboardController/RGBController_DasKeyboard.cpp @@ -0,0 +1,357 @@ +/*---------------------------------------------------------*\ +| RGBController_DasKeyboard.cpp | +| | +| RGBController for Das Keyboard keyboard | +| | +| Frank Niessen (denk_mal) 16 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_DasKeyboard.h" + +using namespace std::chrono_literals; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +// US Layout +static unsigned int matrix_map_us[7][21] = + { + {NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA}, + { 5, NA, 17, 23, 29, 35, 41, 47, 53, 59, 65, 71, 77, 83, 89, 95, 101, 127, 128, 129, 130}, + { 4, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70, 76, 82, 88, 94, 100, 106, 112, 118, 124}, + { 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 7, 87, 93, 99, 105, 111, 117, 123}, + { 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, NA, 80, NA, NA, NA, 104, 110, 116, NA}, + { 1, NA, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 79, NA, NA, 91, NA, 103, 109, 115, 122}, + { 0, 6, 12, NA, NA, NA, 36, NA, NA, NA, 60, 66, 72, 78, 84, 90, 96, 102, NA, 114, NA} + }; + +// EU Layout +static unsigned int matrix_map_eu[7][21] = + { + {NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 126, NA, NA, NA}, + { 5, NA, 17, 23, 29, 35, 41, 47, 53, 59, 65, 71, 77, 83, 89, 95, 101, 127, 128, 129, 130}, + { 4, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70, 76, 82, 88, 94, 100, 106, 112, 118, 124}, + { 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, NA, 87, 93, 99, 105, 111, 117, 123}, + { 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 81, 80, NA, NA, NA, 104, 110, 116, NA}, + { 1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 79, NA, NA, 91, NA, 103, 109, 115, 122}, + { 0, 6, 12, NA, NA, NA, 36, NA, NA, NA, 60, 66, 72, 78, 84, 90, 96, 102, NA, 114, NA} + }; + +static const char *zone_names[] = + { + ZONE_EN_KEYBOARD + }; + +static zone_type zone_types[] = + { + ZONE_TYPE_MATRIX, + }; + +static const unsigned int zone_sizes[] = + { + 131 + }; + +// UK Layout +static const char *led_names[] = + { + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_SHIFT, + KEY_EN_CAPS_LOCK, + KEY_EN_TAB, + KEY_EN_BACK_TICK, + KEY_EN_ESCAPE, + KEY_EN_LEFT_WINDOWS, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_A, + KEY_EN_Q, + KEY_EN_1, + KEY_EN_UNUSED, + KEY_EN_LEFT_ALT, + KEY_EN_Z, + KEY_EN_S, + KEY_EN_W, + KEY_EN_2, + KEY_EN_F1, + KEY_EN_UNUSED, + KEY_EN_X, + KEY_EN_D, + KEY_EN_E, + KEY_EN_3, + KEY_EN_F2, + KEY_EN_UNUSED, + KEY_EN_C, + KEY_EN_F, + KEY_EN_R, + KEY_EN_4, + KEY_EN_F3, + KEY_EN_UNUSED, + KEY_EN_V, + KEY_EN_G, + KEY_EN_T, + KEY_EN_5, + KEY_EN_F4, + KEY_EN_SPACE, + KEY_EN_B, + KEY_EN_H, + KEY_EN_Y, + KEY_EN_6, + KEY_EN_F5, + KEY_EN_UNUSED, + KEY_EN_N, + KEY_EN_J, + KEY_EN_U, + KEY_EN_7, + KEY_EN_F6, + KEY_EN_UNUSED, + KEY_EN_M, + KEY_EN_K, + KEY_EN_I, + KEY_EN_8, + KEY_EN_F7, + KEY_EN_UNUSED, + KEY_EN_COMMA, + KEY_EN_L, + KEY_EN_O, + KEY_EN_9, + KEY_EN_F8, + KEY_EN_RIGHT_ALT, + KEY_EN_PERIOD, + KEY_EN_SEMICOLON, + KEY_EN_P, + KEY_EN_0, + KEY_EN_F9, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_FORWARD_SLASH, + KEY_EN_QUOTE, + KEY_EN_LEFT_BRACKET, + KEY_EN_MINUS, + KEY_EN_F10, + KEY_EN_MENU, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_RIGHT_BRACKET, + KEY_EN_EQUALS, + KEY_EN_F11, + KEY_EN_RIGHT_CONTROL, + KEY_EN_RIGHT_SHIFT, + KEY_EN_ANSI_ENTER, + KEY_EN_POUND, + KEY_EN_BACKSPACE, + KEY_EN_F12, + KEY_EN_LEFT_ARROW, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_DELETE, + KEY_EN_INSERT, + KEY_EN_PRINT_SCREEN, + KEY_EN_DOWN_ARROW, + KEY_EN_UP_ARROW, + KEY_EN_UNUSED, + KEY_EN_END, + KEY_EN_HOME, + KEY_EN_SCROLL_LOCK, + KEY_EN_RIGHT_ARROW, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_PAGE_DOWN, + KEY_EN_PAGE_UP, + KEY_EN_PAUSE_BREAK, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_LOCK, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_UNUSED, + KEY_EN_NUMPAD_PERIOD, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_TIMES, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_NUMPAD_ENTER, + KEY_EN_NUMPAD_PLUS, + KEY_EN_NUMPAD_MINUS, + KEY_EN_UNUSED, + "Key: Sleep", + "Key: Brightness", + KEY_EN_MEDIA_PLAY_PAUSE, + KEY_EN_MEDIA_NEXT, + "Key: Q-Button" + }; + +/**------------------------------------------------------------------*\ + @name Das Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectDasKeyboardControllers,DetectDas4QKeyboard + @comment +\*-------------------------------------------------------------------*/ + +RGBController_DasKeyboard::RGBController_DasKeyboard(DasKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + for(unsigned int ii = 0; ii < zone_sizes[0]; ii++) + { + double_buffer.push_back(-1); + } + + updateDevice = true; + + name = controller->GetNameString(); + vendor = "Metadot"; + type = DEVICE_TYPE_KEYBOARD; + description = "Das Keyboard Device"; + location = controller->GetLocationString(); + serial = controller->GetSerialString(); + version = controller->GetVersionString(); + + modes.resize(4); + modes[0].name = "Direct"; + modes[0].value = DAS_KEYBOARD_MODE_DIRECT; + modes[0].flags = MODE_FLAG_HAS_PER_LED_COLOR; + modes[0].color_mode = MODE_COLORS_PER_LED; + + modes[1].name = "Flashing"; + modes[1].value = DAS_KEYBOARD_MODE_FLASHING; + modes[1].flags = MODE_FLAG_HAS_PER_LED_COLOR; + modes[1].color_mode = MODE_COLORS_PER_LED; + + modes[2].name = "Breathing"; + modes[2].value = DAS_KEYBOARD_MODE_BREATHING; + modes[2].flags = MODE_FLAG_HAS_PER_LED_COLOR; + modes[2].color_mode = MODE_COLORS_PER_LED; + + modes[3].name = "Spectrum Cycle"; + modes[3].value = DAS_KEYBOARD_MODE_SPECTRUM_CYCLE; + modes[3].flags = MODE_FLAG_HAS_PER_LED_COLOR; + modes[3].color_mode = MODE_COLORS_PER_LED; + + SetupZones(); +} + +RGBController_DasKeyboard::~RGBController_DasKeyboard() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + unsigned int zone_size = (unsigned int)zones.size(); + + for(unsigned int zone_index = 0; zone_index < zone_size; zone_index++) + { + delete zones[zone_index].matrix_map; + } + + delete controller; +} + +void RGBController_DasKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 7; + new_zone.matrix_map->width = 21; + + if(controller->GetLayoutString() == "US") + { + new_zone.matrix_map->map = (unsigned int *) &matrix_map_us; + } + else + { + new_zone.matrix_map->map = (unsigned int *) &matrix_map_eu; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_DasKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_DasKeyboard::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_DasKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + updateDevice = false; + + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + UpdateSingleLED(static_cast(led_idx)); + } + + updateDevice = true; + + controller->SendApply(); +} + +void RGBController_DasKeyboard::UpdateSingleLED(int led) +{ + mode selected_mode = modes[active_mode]; + + if(double_buffer[led] == colors[led]) + { + return; + } + + controller->SendColors(led, selected_mode.value, + RGBGetRValue(colors[led]), + RGBGetGValue(colors[led]), + RGBGetBValue(colors[led])); + + double_buffer[led] = colors[led]; + + if(updateDevice) + { + controller->SendApply(); + } +} + +void RGBController_DasKeyboard::DeviceUpdateMode() +{ +} diff --git a/Controllers/DasKeyboardController/RGBController_DasKeyboard.h b/Controllers/DasKeyboardController/RGBController_DasKeyboard.h new file mode 100644 index 0000000..c5d93b5 --- /dev/null +++ b/Controllers/DasKeyboardController/RGBController_DasKeyboard.h @@ -0,0 +1,45 @@ +/*---------------------------------------------------------*\ +| RGBController_DasKeyboard.h | +| | +| RGBController for Das Keyboard keyboard | +| | +| Frank Niessen (denk_mal) 16 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "DasKeyboardController.h" + +enum +{ + DAS_KEYBOARD_MODE_DIRECT = 0x01, + DAS_KEYBOARD_MODE_FLASHING = 0x1F, + DAS_KEYBOARD_MODE_BREATHING = 0x08, + DAS_KEYBOARD_MODE_SPECTRUM_CYCLE = 0x14 +}; + +class RGBController_DasKeyboard : public RGBController +{ +public: + RGBController_DasKeyboard(DasKeyboardController* controller_ptr); + ~RGBController_DasKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + DasKeyboardController* controller; + + std::vector double_buffer; + bool updateDevice; +}; diff --git a/Controllers/DebugController/DebugControllerDetect.cpp b/Controllers/DebugController/DebugControllerDetect.cpp new file mode 100644 index 0000000..77d7d7c --- /dev/null +++ b/Controllers/DebugController/DebugControllerDetect.cpp @@ -0,0 +1,78 @@ +/*---------------------------------------------------------*\ +| DebugControllerDetect.cpp | +| | +| Detector for debug devices | +| | +| Adam Honse 31 Jul 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBController.h" +#include "RGBController_Debug.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectDebugControllers * +* * +* Add debug controllers based on the DebugDevices key in the settings json * +* * +\******************************************************************************************/ + +void DetectDebugControllers() +{ + json debug_settings; + + /*-----------------------------------------------------*\ + | Get Debug Device settings from settings manager | + \*-----------------------------------------------------*/ + debug_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("DebugDevices"); + + /*-----------------------------------------------------*\ + | If the Debug settings contains devices, process | + \*-----------------------------------------------------*/ + if(debug_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < debug_settings["devices"].size(); device_idx++) + { + RGBController_Debug * debug_controller = new RGBController_Debug(false, debug_settings["devices"][device_idx]); + ResourceManager::get()->RegisterRGBController(debug_controller); + } + } + + if (debug_settings.contains("CustomDevices")) + { + for(unsigned int device_idx = 0; device_idx < debug_settings["CustomDevices"].size(); device_idx++) + { + json custom_device_settings = debug_settings["CustomDevices"][device_idx]; + + /*---------------------------------------------*\ + | If ANY of the attributes are missing then go | + | ahead and skip the entry | + \*---------------------------------------------*/ + if( + !custom_device_settings.contains("DeviceName") || + !custom_device_settings.contains("DeviceType") || + !custom_device_settings.contains("DeviceDescription") || + !custom_device_settings.contains("DeviceLocation") || + !custom_device_settings.contains("DeviceVersion") || + !custom_device_settings.contains("DeviceSerial") || + !custom_device_settings.contains("DeviceZones") + ) + { + continue; + } + else + { + RGBController_Debug * debug_controller = new RGBController_Debug(true, custom_device_settings); + ResourceManager::get()->RegisterRGBController(debug_controller); + } + } + } + +} /* DetectDebugControllers() */ + +REGISTER_DETECTOR("Debug Controllers", DetectDebugControllers); diff --git a/Controllers/DebugController/RGBController_Debug.cpp b/Controllers/DebugController/RGBController_Debug.cpp new file mode 100644 index 0000000..8ec6b90 --- /dev/null +++ b/Controllers/DebugController/RGBController_Debug.cpp @@ -0,0 +1,633 @@ +/*---------------------------------------------------------*\ +| RGBController_Debug.cpp | +| | +| Debug RGBController that can mimic various devices for | +| development and test purposes | +| | +| Adam Honse (CalcProgrammer1) 31 Jul 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "KeyboardLayoutManager.h" +#include "RGBController_Debug.h" + +/**------------------------------------------------------------------*\ + @name Debug + @category Unknown + @type I2C + @save :x: + @direct :x: + @effects :x: + @detectors DetectDebugControllers + @comment +\*-------------------------------------------------------------------*/ + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +#define NUM_LAYOUTS 6 + +static const std::string layout_names[] = +{ + "Default", + "ANSI QWERTY", + "ISO QWERTY", + "ISO QWERTZ", + "ISO AZERTY", + "JIS" +}; + +static unsigned int debug_keyboard_underglow_map[3][10] = + { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, + { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }, + { 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 } }; + +RGBController_Debug::RGBController_Debug(bool custom_controller, json debug_settings) +{ + if(custom_controller) + { + /*-------------------------------------------------*\ + | Set the name | + \*-------------------------------------------------*/ + name = debug_settings["DeviceName"]; + + /*-------------------------------------------------*\ + | Find the device type | + \*-------------------------------------------------*/ + if (debug_settings["DeviceType"] == "motherboard") type = DEVICE_TYPE_MOTHERBOARD; + else if (debug_settings["DeviceType"] == "dram") type = DEVICE_TYPE_DRAM; + else if (debug_settings["DeviceType"] == "gpu") type = DEVICE_TYPE_GPU; + else if (debug_settings["DeviceType"] == "cooler") type = DEVICE_TYPE_COOLER; + else if (debug_settings["DeviceType"] == "led_strip") type = DEVICE_TYPE_LEDSTRIP; + else if (debug_settings["DeviceType"] == "keyboard") type = DEVICE_TYPE_KEYBOARD; + else if (debug_settings["DeviceType"] == "mouse") type = DEVICE_TYPE_MOUSE; + else if (debug_settings["DeviceType"] == "mousemat") type = DEVICE_TYPE_MOUSEMAT; + else if (debug_settings["DeviceType"] == "headset") type = DEVICE_TYPE_HEADSET; + else if (debug_settings["DeviceType"] == "headset_stand") type = DEVICE_TYPE_HEADSET_STAND; + else if (debug_settings["DeviceType"] == "gamepad") type = DEVICE_TYPE_GAMEPAD; + else if (debug_settings["DeviceType"] == "light") type = DEVICE_TYPE_LIGHT; + else if (debug_settings["DeviceType"] == "speaker") type = DEVICE_TYPE_SPEAKER; + else if (debug_settings["DeviceType"] == "unknown") type = DEVICE_TYPE_UNKNOWN; + + /*-------------------------------------------------*\ + | Set description, location, version, and serial | + \*-------------------------------------------------*/ + description = debug_settings["DeviceDescription"]; + location = debug_settings["DeviceLocation"]; + version = debug_settings["DeviceVersion"]; + serial = debug_settings["DeviceSerial"]; + + /*-------------------------------------------------*\ + | Create the mode | + \*-------------------------------------------------*/ + mode Direct; + + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + + modes.push_back(Direct); + + /*-------------------------------------------------*\ + | Fill in zones | + \*-------------------------------------------------*/ + for(int ZoneID = 0; ZoneID < (int)debug_settings["DeviceZones"].size(); ZoneID++) + { + json ZoneJson = debug_settings["DeviceZones"][ZoneID]; + + if + ( + !ZoneJson.contains("name") || + !ZoneJson.contains("type") || + !ZoneJson.contains("leds_min") || + !ZoneJson.contains("leds_max") || + !ZoneJson.contains("leds_count") + ) + { + continue; + } + zone custom_zone; + + custom_zone.name = ZoneJson["name"]; + + if (ZoneJson["type"] == "linear") custom_zone.type = ZONE_TYPE_LINEAR; + else if (ZoneJson["type"] == "matrix") custom_zone.type = ZONE_TYPE_MATRIX; + else if (ZoneJson["type"] == "single") custom_zone.type = ZONE_TYPE_SINGLE; + else + { + continue; + } + + custom_zone.leds_min = ZoneJson["leds_min"]; + custom_zone.leds_max = ZoneJson["leds_max"]; + custom_zone.leds_count = ZoneJson["leds_count"]; + + /*---------------------------------------------*\ + | Fill in the matrix map | + \*---------------------------------------------*/ + bool BadVal = false; + + if(custom_zone.type == ZONE_TYPE_MATRIX) + { + if + ( + !ZoneJson.contains("matrix_height") || + !ZoneJson.contains("matrix_width") || + !ZoneJson.contains("matrix_map") + ) + { + /*-------------------------------------*\ + | If there is no map then the zone | + | can't be valid. Don't add it | + \*-------------------------------------*/ + continue; + } + + custom_zone.matrix_map = new matrix_map_type; + + custom_zone.matrix_map->width = ZoneJson["matrix_width"]; + custom_zone.matrix_map->height = ZoneJson["matrix_height"]; + + int H = custom_zone.matrix_map->height; + int W = custom_zone.matrix_map->width; + + BadVal = (ZoneJson["matrix_map"].size() != custom_zone.matrix_map->height); + + unsigned int* MatrixARR = new unsigned int[H * W]; + + for(int MatrixMapRow = 0; MatrixMapRow < H; MatrixMapRow++) + { + /*-------------------------------------*\ + | If something went wrong then make no | + | attempt to recover and just move on | + | in a way that doesn't crash. Even 1 | + | bad row can corrupt the map so skip | + | the zone entirely | + \*-------------------------------------*/ + if((custom_zone.matrix_map->width != ZoneJson["matrix_map"][MatrixMapRow].size()) || BadVal) + { + BadVal = true; + break; + } + + for(int MatrixMapCol = 0; MatrixMapCol < W; MatrixMapCol++) + { + int Val = ZoneJson["matrix_map"][MatrixMapRow][MatrixMapCol]; + + if((signed)Val == -1) + { + MatrixARR[MatrixMapRow * W + MatrixMapCol] = NA; + } + else + { + MatrixARR[MatrixMapRow * W + MatrixMapCol] = (unsigned)Val; + } + } + } + + custom_zone.matrix_map->map = MatrixARR; + } + + /*---------------------------------------------*\ + | Don't add the zone if it is invalid | + \*---------------------------------------------*/ + if(BadVal) + { + continue; + } + + bool UseCustomLabels = false; + if(ZoneJson.contains("custom_labels")) + { + /*-----------------------------------------*\ + | If the count is correct and the zone is | + | non-resizeable | + \*-----------------------------------------*/ + if((ZoneJson["custom_labels"].size() == custom_zone.leds_count) && (custom_zone.leds_min == custom_zone.leds_max)) + { + UseCustomLabels = true; + } + } + + /*---------------------------------------------*\ + | Set the LED names | + \*---------------------------------------------*/ + for(int LED_ID = 0; LED_ID < (int)custom_zone.leds_count; LED_ID++) + { + led custom_led; + if(UseCustomLabels) + { + /*-------------------------------------*\ + | Set the label to the user defined | + | label | + \*-------------------------------------*/ + custom_led.name = ZoneJson["custom_labels"][LED_ID]; + } + else + { + /*-------------------------------------*\ + | Set default labels because something | + | went wrong | + \*-------------------------------------*/ + custom_led.name = ("Custom LED. Zone " + std::to_string(ZoneID) + ", LED " + std::to_string(LED_ID)); + } + + leds.push_back(custom_led); + } + + zones.push_back(custom_zone); + } + + SetupColors(); + } + else + { + bool zone_single = true; + bool zone_linear = true; + bool zone_resizable = false; + bool zone_keyboard = false; + bool zone_underglow = false; + std::string name_setting = ""; + std::string type_setting = "keyboard"; + + if(debug_settings.contains("name")) + { + name_setting = debug_settings["name"]; + } + + if(debug_settings.contains("type")) + { + type_setting = debug_settings["type"]; + } + + if(debug_settings.contains("single")) + { + zone_single = debug_settings["single"]; + } + + if(debug_settings.contains("linear")) + { + zone_linear = debug_settings["linear"]; + } + + if(debug_settings.contains("resizable")) + { + zone_resizable = debug_settings["resizable"]; + } + + if(debug_settings.contains("keyboard")) + { + zone_keyboard = debug_settings["keyboard"]; + } + + if(debug_settings.contains("underglow")) + { + zone_underglow = debug_settings["underglow"]; + } + + if(type_setting == "motherboard") + { + name = "Debug Motherboard"; + type = DEVICE_TYPE_MOTHERBOARD; + } + else if(type_setting == "dram") + { + name = "Debug DRAM"; + type = DEVICE_TYPE_DRAM; + } + else if(type_setting == "gpu") + { + name = "Debug GPU"; + type = DEVICE_TYPE_GPU; + } + else if(type_setting == "keyboard") + { + name = "Debug Keyboard"; + type = DEVICE_TYPE_KEYBOARD; + } + else if(type_setting == "mouse") + { + name = "Debug Mouse"; + type = DEVICE_TYPE_MOUSE; + } + else if(type_setting == "argb") + { + name = "Debug ARGB Controller"; + type = DEVICE_TYPE_LEDSTRIP; + } + + /*---------------------------------------------------------*\ + | Fill in debug controller information | + \*---------------------------------------------------------*/ + description = name + " Device"; + vendor = name + " Vendor String"; + location = name + " Location String"; + version = name + " Version String"; + serial = name + " Serial String"; + + if(name_setting != "") + { + name = name_setting; + } + + /*---------------------------------------------------------*\ + | Create a direct mode | + \*---------------------------------------------------------*/ + mode Direct; + + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + + modes.push_back(Direct); + + /*---------------------------------------------------------*\ + | Create a single zone/LED | + \*---------------------------------------------------------*/ + if(zone_single) + { + zone single_zone; + + single_zone.name = "Single Zone"; + single_zone.type = ZONE_TYPE_SINGLE; + single_zone.leds_min = 1; + single_zone.leds_max = 1; + single_zone.leds_count = 1; + single_zone.matrix_map = NULL; + + zones.push_back(single_zone); + + led single_led; + + single_led.name = "Single LED"; + + leds.push_back(single_led); + + led_alt_names.push_back(""); + } + + /*---------------------------------------------------------*\ + | Create a linear zone | + \*---------------------------------------------------------*/ + if(zone_linear) + { + zone linear_zone; + + linear_zone.name = "Linear Zone"; + linear_zone.type = ZONE_TYPE_LINEAR; + linear_zone.leds_min = 10; + linear_zone.leds_max = 10; + linear_zone.leds_count = 10; + linear_zone.matrix_map = NULL; + + zones.push_back(linear_zone); + + for(std::size_t led_idx = 0; led_idx < 10; led_idx++) + { + led linear_led; + + linear_led.name = "Linear LED " + std::to_string(led_idx); + + leds.push_back(linear_led); + + led_alt_names.push_back(""); + } + } + /*---------------------------------------------------------*\ + | Create a keyboard matrix zone | + \*---------------------------------------------------------*/ + if(zone_keyboard) + { + KEYBOARD_LAYOUT layout = KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ANSI_QWERTY; + KEYBOARD_SIZE size = KEYBOARD_SIZE::KEYBOARD_SIZE_FULL; + + if(debug_settings.contains("layout")) + { + KEYBOARD_LAYOUT temp_layout = debug_settings["layout"]; + + if(temp_layout < NUM_LAYOUTS) + { + layout = temp_layout; + } + } + + if(debug_settings.contains("size")) + { + size = debug_settings["size"]; + } + + KeyboardLayoutManager new_kb(layout, size); + + description += ", Layout: " + layout_names[layout] + ", Size: " + new_kb.GetName(); + + /*-----------------------------------------------------*\ + | Check for custom key inserts and swaps | + \*-----------------------------------------------------*/ + const char* change_keys = "change_keys"; + + if(debug_settings.contains(change_keys)) + { + std::vector change; + + const char* ins_row = "ins_row"; + const char* rmv_key = "rmv_key"; + const char* rmv_row = "rmv_row"; + const char* swp_key = "swp_key"; + + const char* dbg_zone = "Zone"; + const char* dbg_row = "Row"; + const char* dbg_col = "Col"; + const char* dbg_val = "Val"; + const char* dbg_name = "Name"; + const char* dbg_opcode = "Opcode"; + + for(size_t i = 0; i < debug_settings[change_keys].size(); i++) + { + keyboard_led* key = new keyboard_led; + + key->zone = debug_settings[change_keys][i][dbg_zone]; + key->row = debug_settings[change_keys][i][dbg_row]; + key->col = debug_settings[change_keys][i][dbg_col]; + key->value = debug_settings[change_keys][i][dbg_val]; + key->name = debug_settings[change_keys][i][dbg_name].get_ref().c_str(); + + if(debug_settings[change_keys][i][dbg_opcode] == ins_row) + { + key->opcode = KEYBOARD_OPCODE_INSERT_ROW; + } + else if(debug_settings[change_keys][i][dbg_opcode] == rmv_key) + { + key->opcode = KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT; + } + else if(debug_settings[change_keys][i][dbg_opcode] == rmv_row) + { + key->opcode = KEYBOARD_OPCODE_REMOVE_ROW; + } + else if(debug_settings[change_keys][i][dbg_opcode] == swp_key) + { + key->opcode = KEYBOARD_OPCODE_SWAP_ONLY; + } + else + { + key->opcode = KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT; + } + + change.push_back(*key); + } + + new_kb.ChangeKeys(change); + } + + zone keyboard_zone; + + keyboard_zone.name = "Keyboard Zone"; + keyboard_zone.type = ZONE_TYPE_MATRIX; + keyboard_zone.leds_min = new_kb.GetKeyCount(); + keyboard_zone.leds_max = new_kb.GetKeyCount(); + keyboard_zone.leds_count = new_kb.GetKeyCount(); + keyboard_zone.matrix_map = new matrix_map_type; + keyboard_zone.matrix_map->height = new_kb.GetRowCount(); + keyboard_zone.matrix_map->width = new_kb.GetColumnCount(); + keyboard_zone.matrix_map->map = new unsigned int[keyboard_zone.matrix_map->height * keyboard_zone.matrix_map->width]; + + new_kb.GetKeyMap(keyboard_zone.matrix_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT); + + zones.push_back(keyboard_zone); + + for(unsigned int led_idx = 0; led_idx < keyboard_zone.leds_count; led_idx++) + { + led keyboard_led; + + keyboard_led.name = new_kb.GetKeyNameAt(led_idx); + + leds.push_back(keyboard_led); + + led_alt_names.push_back(new_kb.GetKeyAltNameAt(led_idx)); + } + } + /*---------------------------------------------------------*\ + | Create an underglow matrix zone | + \*---------------------------------------------------------*/ + if(zone_underglow) + { + zone underglow_zone; + + underglow_zone.name = "Underglow Zone"; + underglow_zone.type = ZONE_TYPE_MATRIX; + underglow_zone.leds_min = 30; + underglow_zone.leds_max = 30; + underglow_zone.leds_count = 30; + underglow_zone.matrix_map = new matrix_map_type; + underglow_zone.matrix_map->height = 3; + underglow_zone.matrix_map->width = 10; + underglow_zone.matrix_map->map = (unsigned int*)&debug_keyboard_underglow_map; + + zones.push_back(underglow_zone); + + for(std::size_t led_idx = 0; led_idx < underglow_zone.leds_count; led_idx++) + { + led underglow_led; + + underglow_led.name = "Underglow LED " + std::to_string(led_idx);; + + leds.push_back(underglow_led); + + led_alt_names.push_back(""); + } + } + /*---------------------------------------------------------*\ + | Create a resizable linear zone | + \*---------------------------------------------------------*/ + if(zone_resizable) + { + zone resizable_zone; + + resizable_zone.name = "Resizable Zone"; + resizable_zone.type = ZONE_TYPE_LINEAR; + resizable_zone.leds_min = 0; + resizable_zone.leds_max = 100; + resizable_zone.leds_count = 0; + resizable_zone.matrix_map = NULL; + + zones.push_back(resizable_zone); + } + } + + SetupColors(); +} + +RGBController_Debug::~RGBController_Debug() +{ + +} + +void RGBController_Debug::SetupZones() +{ + +} + +void RGBController_Debug::ResizeZone(int index, int new_size) +{ + //Make sure that it isn't out of bounds (negative numbers) + if(new_size < int(zones[index].leds_min)) + { + new_size = zones[index].leds_min; + } + + // Same thing as the above line except for over 100 + if(new_size > int(zones[index].leds_max)) + { + new_size = zones[index].leds_max; + } + + // Store the previous amount of LEDs + int old_size = zones[index].leds_count; + + // Set the LED count in the zone to the new ammount + zones[index].leds_count = new_size; + + // Set the new ammount of LEDs for to the new size + size_t old_leds_size = leds.size(); + + // Add the new ammount of LEDs to the old ammount + size_t new_leds_size = leds.size() - old_size + new_size; + + leds.resize(std::max(old_leds_size, new_leds_size)); + + memmove((void *)(&leds[zones[index].start_idx] + old_leds_size), (const void *)(&leds[zones[index].start_idx] + new_leds_size), (old_leds_size - zones[index].start_idx - old_size) * sizeof(led)); + + leds.resize(new_leds_size); + + for(int i = 0; i < new_size; ++i) + { + leds[zones[index].start_idx + i].name = "Linear LED " + std::to_string(i); + } + + SetupColors(); +} + +void RGBController_Debug::DeviceUpdateLEDs() +{ + +} + +void RGBController_Debug::UpdateZoneLEDs(int /*zone*/) +{ + +} + +void RGBController_Debug::UpdateSingleLED(int /*led*/) +{ + +} + +void RGBController_Debug::DeviceUpdateMode() +{ + +} diff --git a/Controllers/DebugController/RGBController_Debug.h b/Controllers/DebugController/RGBController_Debug.h new file mode 100644 index 0000000..cdf1f58 --- /dev/null +++ b/Controllers/DebugController/RGBController_Debug.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_Debug.h | +| | +| Debug RGBController that can mimic various devices for | +| development and test purposes | +| | +| Adam Honse (CalcProgrammer1) 31 Jul 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +using json = nlohmann::json; + +class RGBController_Debug : public RGBController +{ +public: + RGBController_Debug(bool custom_controller, json debug_settings); + ~RGBController_Debug(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); +}; diff --git a/Controllers/DreamCheekyController/DreamCheekyController.cpp b/Controllers/DreamCheekyController/DreamCheekyController.cpp new file mode 100644 index 0000000..732e364 --- /dev/null +++ b/Controllers/DreamCheekyController/DreamCheekyController.cpp @@ -0,0 +1,89 @@ +/*---------------------------------------------------------*\ +| DreamCheekyController.cpp | +| | +| Driver for Dream Cheeky devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 06 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "DreamCheekyController.h" +#include "StringUtils.h" + +DreamCheekyController::DreamCheekyController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + /*-----------------------------------------------------*\ + | The Dream Cheeky Webmail Notifier requires four | + | initialization packets before sending colors. | + \*-----------------------------------------------------*/ + const unsigned char init_0[9] = { 0x00, 0x1F, 0x02, 0x00, 0x5F, 0x00, 0x00, 0x1F, 0x03 }; + const unsigned char init_1[9] = { 0x00, 0x00, 0x02, 0x00, 0x5F, 0x00, 0x00, 0x1F, 0x04 }; + const unsigned char init_2[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x05 }; + const unsigned char init_3[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + + hid_write(dev, init_0, sizeof(init_0)); + hid_write(dev, init_1, sizeof(init_1)); + hid_write(dev, init_2, sizeof(init_2)); + hid_write(dev, init_3, sizeof(init_3)); +} + +DreamCheekyController::~DreamCheekyController() +{ + hid_close(dev); +} + +std::string DreamCheekyController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string DreamCheekyController::GetNameString() +{ + return(name); +} + +std::string DreamCheekyController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void DreamCheekyController::SetColor(unsigned char red, unsigned char grn, unsigned char blu) +{ + unsigned char usb_buf[9]; + + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | The Dream Cheeky Webmail Notifier color values range | + | from 0-64, so we scale the 0-255 input range down by | + | right shifting by 2 to get the range 0-63. Add 1 if | + | the value is exactly 255 because otherwise the maximum| + | value of 64 would never be reached. | + \*-----------------------------------------------------*/ + usb_buf[0] = 0x00; + usb_buf[1] = (red >> 2) + (red == 255); + usb_buf[2] = (grn >> 2) + (grn == 255); + usb_buf[3] = (blu >> 2) + (blu == 255); + usb_buf[4] = 0x00; + usb_buf[5] = 0x00; + usb_buf[6] = 0x00; + usb_buf[7] = 0x1F; + usb_buf[8] = 0x05; + + hid_write(dev, usb_buf, sizeof(usb_buf)); +} diff --git a/Controllers/DreamCheekyController/DreamCheekyController.h b/Controllers/DreamCheekyController/DreamCheekyController.h new file mode 100644 index 0000000..f05307f --- /dev/null +++ b/Controllers/DreamCheekyController/DreamCheekyController.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| DreamCheekyController.h | +| | +| Driver for Dream Cheeky devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 06 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +class DreamCheekyController +{ +public: + DreamCheekyController(hid_device* dev_handle, const char* path, std::string dev_name); + ~DreamCheekyController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetColor(unsigned char red, unsigned char grn, unsigned char blu); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/DreamCheekyController/DreamCheekyControllerDetect.cpp b/Controllers/DreamCheekyController/DreamCheekyControllerDetect.cpp new file mode 100644 index 0000000..19992bb --- /dev/null +++ b/Controllers/DreamCheekyController/DreamCheekyControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| DreamCheekyControllerDetect.cpp | +| | +| Detector for Dream Cheeky devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 06 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "DreamCheekyController.h" +#include "RGBController_DreamCheeky.h" + +/*---------------------------------------------------------*\ +| Dream Cheeky USB Vendor ID | +\*---------------------------------------------------------*/ +#define DREAM_CHEEKY_VID 0x1D34 + +/*---------------------------------------------------------*\ +| Dream Cheeky USB Product ID | +\*---------------------------------------------------------*/ +#define DREAM_CHEEKY_WEBMAIL_NOTIFIER_PID 0x0004 + +void DetectDreamCheekyControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + DreamCheekyController* controller = new DreamCheekyController(dev, info->path, name); + RGBController_DreamCheeky* rgb_controller = new RGBController_DreamCheeky(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR( "Dream Cheeky Webmail Notifier", DetectDreamCheekyControllers, DREAM_CHEEKY_VID, DREAM_CHEEKY_WEBMAIL_NOTIFIER_PID ); diff --git a/Controllers/DreamCheekyController/RGBController_DreamCheeky.cpp b/Controllers/DreamCheekyController/RGBController_DreamCheeky.cpp new file mode 100644 index 0000000..1f458b3 --- /dev/null +++ b/Controllers/DreamCheekyController/RGBController_DreamCheeky.cpp @@ -0,0 +1,94 @@ +/*---------------------------------------------------------*\ +| RGBController_DreamCheeky.cpp | +| | +| RGBController for Dream Cheeky devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 06 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_DreamCheeky.h" + +RGBController_DreamCheeky::RGBController_DreamCheeky(DreamCheekyController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + type = DEVICE_TYPE_ACCESSORY; + vendor = "Dream Cheeky"; + description = "Dream Cheeky Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_DreamCheeky::~RGBController_DreamCheeky() +{ + +} + +void RGBController_DreamCheeky::SetupZones() +{ + zone mail_zone; + mail_zone.name = "LED"; + mail_zone.type = ZONE_TYPE_SINGLE; + mail_zone.leds_min = 1; + mail_zone.leds_max = 1; + mail_zone.leds_count = 1; + mail_zone.matrix_map = NULL; + zones.push_back(mail_zone); + + led mail_led; + mail_led.name = "LED"; + leds.push_back(mail_led); + + SetupColors(); +} + +void RGBController_DreamCheeky::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*-----------------------------------------------------*\ + | This device does not support resizing zones | + \*-----------------------------------------------------*/ +} + +void RGBController_DreamCheeky::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetColor(red, grn, blu); +} + +void RGBController_DreamCheeky::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DreamCheeky::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DreamCheeky::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_DreamCheeky::DeviceSaveMode() +{ + /*-----------------------------------------------------*\ + | This device does not support saving | + \*-----------------------------------------------------*/ +} diff --git a/Controllers/DreamCheekyController/RGBController_DreamCheeky.h b/Controllers/DreamCheekyController/RGBController_DreamCheeky.h new file mode 100644 index 0000000..f8075a0 --- /dev/null +++ b/Controllers/DreamCheekyController/RGBController_DreamCheeky.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_DreamCheeky.h | +| | +| RGBController for Dream Cheeky devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 06 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "DreamCheekyController.h" +#include "RGBController.h" + +class RGBController_DreamCheeky : public RGBController +{ +public: + RGBController_DreamCheeky(DreamCheekyController* controller_ptr); + ~RGBController_DreamCheeky(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + DreamCheekyController* controller; +}; diff --git a/Controllers/DuckyKeyboardController/DuckyKeyboardController.cpp b/Controllers/DuckyKeyboardController/DuckyKeyboardController.cpp new file mode 100644 index 0000000..e8d068f --- /dev/null +++ b/Controllers/DuckyKeyboardController/DuckyKeyboardController.cpp @@ -0,0 +1,225 @@ +/*---------------------------------------------------------*\ +| DuckyKeyboardController.cpp | +| | +| Driver for Ducky keyboard | +| | +| Adam Honse (CalcProgrammer1) 04 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "DuckyKeyboardController.h" +#include "StringUtils.h" + +DuckyKeyboardController::DuckyKeyboardController(hid_device* dev_handle, const char* path, const unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + usb_pid = pid; + + SendInitialize(); +} + +DuckyKeyboardController::~DuckyKeyboardController() +{ + hid_close(dev); +} + +std::string DuckyKeyboardController::GetLocationString() +{ + return("HID: " + location); +} + +std::string DuckyKeyboardController::GetNameString() +{ + return(name); +} + +std::string DuckyKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned short DuckyKeyboardController::GetUSBPID() +{ + return(usb_pid); +} + +void DuckyKeyboardController::SendColors + ( + unsigned char* color_data, + unsigned int color_data_size + ) +{ + unsigned int bytes_sent; + unsigned char* color_data_ptr = color_data; + + SendInitializeColorPacket(); + + for(int i = 0; i < 8; i++) + { + bytes_sent = SendColorDataPacket(i, color_data_ptr, color_data_size); + + color_data_ptr += bytes_sent; + color_data_size -= bytes_sent; + } + + SendTerminateColorPacket(); +} + +void DuckyKeyboardController::SendInitialize() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Initialize Direct Mode packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x41; + usb_buf[0x02] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void DuckyKeyboardController::SendInitializeColorPacket() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Initialize Color packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x56; + usb_buf[0x02] = 0x81; + usb_buf[0x05] = 0x01; + usb_buf[0x09] = 0x08; + usb_buf[0x0D] = 0xAA; + usb_buf[0x0E] = 0xAA; + usb_buf[0x0F] = 0xAA; + usb_buf[0x10] = 0xAA; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +unsigned int DuckyKeyboardController::SendColorDataPacket + ( + unsigned char packet_id, + unsigned char* color_data, + unsigned int color_size + ) +{ + unsigned int bytes_sent; + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Color Data packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x56; + usb_buf[0x02] = 0x83; + usb_buf[0x03] = packet_id; + + if(packet_id == 0x00) + { + usb_buf[0x05] = 0x01; + usb_buf[0x09] = 0x80; + usb_buf[0x0A] = 0x01; + usb_buf[0x0C] = 0xC1; + usb_buf[0x11] = 0xFF; + usb_buf[0x12] = 0xFF; + usb_buf[0x13] = 0xFF; + usb_buf[0x14] = 0xFF; + } + + /*-----------------------------------------------------*\ + | Copy in color data | + \*-----------------------------------------------------*/ + if(packet_id == 0x00) + { + bytes_sent = 65 - 0x19; + + if(color_size < bytes_sent) + { + bytes_sent = color_size; + } + + memcpy(&usb_buf[0x19], color_data, bytes_sent); + } + else + { + bytes_sent = 65 - 0x05; + + if(color_size < bytes_sent) + { + bytes_sent = color_size; + } + + memcpy(&usb_buf[0x05], color_data, bytes_sent); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + + return(bytes_sent); +} + +void DuckyKeyboardController::SendTerminateColorPacket() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Terminate Color packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x51; + usb_buf[0x02] = 0x28; + usb_buf[0x05] = 0xFF; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} diff --git a/Controllers/DuckyKeyboardController/DuckyKeyboardController.h b/Controllers/DuckyKeyboardController/DuckyKeyboardController.h new file mode 100644 index 0000000..7a595a4 --- /dev/null +++ b/Controllers/DuckyKeyboardController/DuckyKeyboardController.h @@ -0,0 +1,61 @@ +/*---------------------------------------------------------*\ +| DuckyKeyboardController.h | +| | +| Driver for Ducky keyboard | +| | +| Adam Honse (CalcProgrammer1) 04 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*-----------------------------------------------------*\ +| Ducky vendor ID | +\*-----------------------------------------------------*/ +#define DUCKY_VID 0x04D9 + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define DUCKY_SHINE_7_ONE_2_RGB_PID 0x0348 +#define DUCKY_ONE_2_RGB_TKL_PID 0x0356 + +class DuckyKeyboardController +{ +public: + DuckyKeyboardController(hid_device* dev_handle, const char* path, const unsigned short pid, std::string dev_name); + ~DuckyKeyboardController(); + + std::string GetLocationString(); + std::string GetNameString(); + std::string GetSerialString(); + unsigned short GetUSBPID(); + + void SendColors + ( + unsigned char* color_data, + unsigned int color_data_size + ); + +private: + hid_device* dev; + std::string location; + std::string name; + unsigned short usb_pid; + + void SendInitialize(); + void SendInitializeColorPacket(); + unsigned int SendColorDataPacket + ( + unsigned char packet_id, + unsigned char* color_data, + unsigned int color_size + ); + void SendTerminateColorPacket(); +}; diff --git a/Controllers/DuckyKeyboardController/DuckyKeyboardControllerDetect.cpp b/Controllers/DuckyKeyboardController/DuckyKeyboardControllerDetect.cpp new file mode 100644 index 0000000..cf0724d --- /dev/null +++ b/Controllers/DuckyKeyboardController/DuckyKeyboardControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| DuckyKeyboardControllerDetect.cpp | +| | +| Detector for Ducky keyboard | +| | +| Adam Honse (CalcProgrammer1) 04 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "DuckyKeyboardController.h" +#include "RGBController_DuckyKeyboard.h" +#include + +/******************************************************************************************\ +* * +* DetectDuckyKeyboardControllers * +* * +* Tests the USB address to see if a Ducky RGB Keyboard controller exists there. * +* * +\******************************************************************************************/ + +void DetectDuckyKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + DuckyKeyboardController* controller = new DuckyKeyboardController(dev, info->path, info->product_id, name); + RGBController_DuckyKeyboard* rgb_controller = new RGBController_DuckyKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectDuckyKeyboardControllers() */ + +REGISTER_HID_DETECTOR_I("Ducky Shine 7/Ducky One 2 RGB", DetectDuckyKeyboardControllers, DUCKY_VID, DUCKY_SHINE_7_ONE_2_RGB_PID, 1); +REGISTER_HID_DETECTOR_I("Ducky One 2 RGB TKL", DetectDuckyKeyboardControllers, DUCKY_VID, DUCKY_ONE_2_RGB_TKL_PID, 1); diff --git a/Controllers/DuckyKeyboardController/RGBController_DuckyKeyboard.cpp b/Controllers/DuckyKeyboardController/RGBController_DuckyKeyboard.cpp new file mode 100644 index 0000000..5cbcf89 --- /dev/null +++ b/Controllers/DuckyKeyboardController/RGBController_DuckyKeyboard.cpp @@ -0,0 +1,325 @@ +/*---------------------------------------------------------*\ +| RGBController_DuckyKeyboard.cpp | +| | +| RGBController for Ducky keyboard | +| | +| Adam Honse (CalcProgrammer1) 04 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_DuckyKeyboard.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[6][23] = + { { 0, NA, 12, 18, 24, 30, NA, 42, 48, 54, 60, NA, 66, 72, 78, 84, 90, 96, 102, 108, 114, 120, 126 }, + { 1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, NA, 67, 73, 85, NA, 91, 97, 103, 109, 115, 121, 127 }, + { 2, NA, 8, 14, 20, 26, NA, 32, 38, 44, 50, 56, 62, 68, 74, 86, 92, 98, 104, 110, 116, 122, 128 }, + { 3, NA, 9, 15, 21, 27, NA, 33, 39, 45, 51, 57, 63, 69, 75, 87, NA, NA, NA, 111, 117, 123, NA }, + { 4, 10, 16, 22, 28, 34, NA, 40, NA, 46, 52, 58, 64, 70, 82, NA, NA, 100, NA, 112, 118, 124, 131 }, + { 5, 11, 17, NA, NA, NA, NA, 41, NA, NA, NA, NA, 65, 77, 83, 89, 95, 101, 107, 113, NA, 125, NA } }; + +static unsigned int matrix_map_tkl[6][19] = + { { 0, NA, 12, 18, 24, 30, NA, 42, 48, 54, 60, NA, 66, 72, 78, 84, 90, 96, 102 }, + { 1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, NA, 67, 73, 85, NA, 91, 97, 103 }, + { 2, NA, 8, 14, 20, 26, NA, 32, 38, 44, 50, 56, 62, 68, 74, 86, 92, 98, 104 }, + { 3, NA, 9, 15, 21, 27, NA, 33, 39, 45, 51, 57, 63, 69, 75, 87, NA, NA, NA }, + { 4, 10, 16, 22, 28, 34, NA, 40, NA, 46, 52, 58, 64, 70, 82, NA, NA, 100, NA }, + { 5, 11, 17, NA, NA, NA, NA, 41, NA, NA, NA, NA, 65, 77, 83, 89, 95, 101, 107 } }; + + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 132 +}; + +static const unsigned int zone_sizes_tkl[] = +{ + 108 +}; + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, + KEY_EN_BACK_TICK, + KEY_EN_TAB, + KEY_EN_CAPS_LOCK, + KEY_EN_LEFT_SHIFT, + KEY_EN_LEFT_CONTROL, + KEY_EN_UNUSED, + KEY_EN_1, + KEY_EN_Q, + KEY_EN_A, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_LEFT_WINDOWS, + KEY_EN_F1, + KEY_EN_2, + KEY_EN_W, + KEY_EN_S, + KEY_EN_Z, + KEY_EN_LEFT_ALT, + KEY_EN_F2, + KEY_EN_3, + KEY_EN_E, + KEY_EN_D, + KEY_EN_X, + KEY_EN_UNUSED, + KEY_EN_F3, + KEY_EN_4, + KEY_EN_R, + KEY_EN_F, + KEY_EN_C, + KEY_EN_UNUSED, + KEY_EN_F4, + KEY_EN_5, + KEY_EN_T, + KEY_EN_G, + KEY_EN_V, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_6, + KEY_EN_Y, + KEY_EN_H, + KEY_EN_B, + KEY_EN_SPACE, + KEY_EN_F5, + KEY_EN_7, + KEY_EN_U, + KEY_EN_J, + KEY_EN_N, + KEY_EN_UNUSED, + KEY_EN_F6, + KEY_EN_8, + KEY_EN_I, + KEY_EN_K, + KEY_EN_M, + KEY_EN_UNUSED, + KEY_EN_F7, + KEY_EN_9, + KEY_EN_O, + KEY_EN_L, + KEY_EN_COMMA, + KEY_EN_UNUSED, + KEY_EN_F8, + KEY_EN_0, + KEY_EN_P, + KEY_EN_SEMICOLON, + KEY_EN_PERIOD, + KEY_EN_RIGHT_ALT, + KEY_EN_F9, + KEY_EN_MINUS, + KEY_EN_LEFT_BRACKET, + KEY_EN_QUOTE, + KEY_EN_FORWARD_SLASH, + KEY_EN_UNUSED, + KEY_EN_F10, + KEY_EN_EQUALS, + KEY_EN_RIGHT_BRACKET, + KEY_EN_POUND, + KEY_EN_UNUSED, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_F11, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_RIGHT_SHIFT, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_F12, + KEY_EN_BACKSPACE, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_ANSI_ENTER, + KEY_EN_UNUSED, + KEY_EN_RIGHT_CONTROL, + KEY_EN_PRINT_SCREEN, + KEY_EN_INSERT, + KEY_EN_DELETE, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_LEFT_ARROW, + KEY_EN_SCROLL_LOCK, + KEY_EN_HOME, + KEY_EN_END, + KEY_EN_UNUSED, + KEY_EN_UP_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_PAUSE_BREAK, + KEY_EN_PAGE_UP, + KEY_EN_PAGE_DOWN, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_RIGHT_ARROW, + "Key: Calculator", + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_0, + KEY_EN_MEDIA_MUTE, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_2, + KEY_EN_UNUSED, + KEY_EN_MEDIA_VOLUME_DOWN, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_PERIOD, + KEY_EN_MEDIA_VOLUME_UP, + KEY_EN_NUMPAD_MINUS, + KEY_EN_NUMPAD_PLUS, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_NUMPAD_ENTER, +}; + +/**------------------------------------------------------------------*\ + @name Ducky Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectDuckyKeyboardControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_DuckyKeyboard::RGBController_DuckyKeyboard(DuckyKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Ducky"; + type = DEVICE_TYPE_KEYBOARD; + description = "Ducky Keyboard Device"; + location = controller->GetLocationString(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_DuckyKeyboard::~RGBController_DuckyKeyboard() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_DuckyKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + unsigned int zone_size = 0; + unsigned int matrix_width = 0; + unsigned int* matrix_map_ptr = NULL; + + switch(controller->GetUSBPID()) + { + case DUCKY_SHINE_7_ONE_2_RGB_PID: + zone_size = zone_sizes[zone_idx]; + matrix_width = 23; + matrix_map_ptr = (unsigned int *)&matrix_map; + break; + + case DUCKY_ONE_2_RGB_TKL_PID: + zone_size = zone_sizes_tkl[zone_idx]; + matrix_width = 19; + matrix_map_ptr = (unsigned int *)&matrix_map_tkl; + break; + } + + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_size; + new_zone.leds_max = zone_size; + new_zone.leds_count = zone_size; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = matrix_width; + new_zone.matrix_map->map = matrix_map_ptr; + zones.push_back(new_zone); + + total_led_count += zone_size; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_DuckyKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_DuckyKeyboard::DeviceUpdateLEDs() +{ + unsigned char colordata[155*3]; + + for(std::size_t color_idx = 0; color_idx < colors.size(); color_idx++) + { + colordata[(color_idx*3)+0] = RGBGetRValue(colors[color_idx]); + colordata[(color_idx*3)+1] = RGBGetGValue(colors[color_idx]); + colordata[(color_idx*3)+2] = RGBGetBValue(colors[color_idx]); + } + + controller->SendColors(colordata, sizeof(colordata)); +} + +void RGBController_DuckyKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DuckyKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DuckyKeyboard::DeviceUpdateMode() +{ + +} diff --git a/Controllers/DuckyKeyboardController/RGBController_DuckyKeyboard.h b/Controllers/DuckyKeyboardController/RGBController_DuckyKeyboard.h new file mode 100644 index 0000000..feec624 --- /dev/null +++ b/Controllers/DuckyKeyboardController/RGBController_DuckyKeyboard.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_DuckyKeyboard.h | +| | +| RGBController for Ducky keyboard | +| | +| Adam Honse (CalcProgrammer1) 04 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "DuckyKeyboardController.h" + +class RGBController_DuckyKeyboard : public RGBController +{ +public: + RGBController_DuckyKeyboard(DuckyKeyboardController* controller_ptr); + ~RGBController_DuckyKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + DuckyKeyboardController* controller; +}; diff --git a/Controllers/DygmaRaiseController/DygmaRaiseController.cpp b/Controllers/DygmaRaiseController/DygmaRaiseController.cpp new file mode 100644 index 0000000..188b8cc --- /dev/null +++ b/Controllers/DygmaRaiseController/DygmaRaiseController.cpp @@ -0,0 +1,100 @@ +/*---------------------------------------------------------*\ +| DygmaRaiseController.cpp | +| | +| Driver for Dygma Raise keyboard | +| | +| Timo Schlegel (@eispalast) Dec 12 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "DygmaRaiseController.h" + +using namespace std::chrono_literals; + +static int val_char_len(int number) +{ + if(number < 10) + { + return 1; + } + else if + (number < 100) + { + return 2; + } + else + { + return 3; + } +} + +DygmaRaiseController::DygmaRaiseController() +{ + +} + +DygmaRaiseController::~DygmaRaiseController() +{ + serialport->serial_close(); + delete serialport; +} + +void DygmaRaiseController::Initialize(char* port) +{ + port_name = port; + + serialport = new serial_port(port_name.c_str(), DYGMA_RAISE_BAUD); +} + +std::string DygmaRaiseController::GetDeviceLocation() +{ + return("COM: " + port_name); +} + +void DygmaRaiseController::SendDirect(std::vectorcolors, size_t led_num) +{ + char serial_buf[MAX_LEN]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(serial_buf,0x00,sizeof(serial_buf)); + + /*-----------------------------------------------------*\ + | Set up led theme packet | + \*-----------------------------------------------------*/ + snprintf(serial_buf, MAX_LEN, "led.theme"); + int actual_length=9; + + /*-----------------------------------------------------*\ + | Fill packet with color values | + \*-----------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < led_num; led_idx++) + { + int r = RGBGetRValue(colors[led_idx]); + int g = RGBGetGValue(colors[led_idx]); + int b = RGBGetBValue(colors[led_idx]); + + snprintf(serial_buf + actual_length, MAX_LEN - actual_length, " %d", r); + actual_length += val_char_len(r) + 1; + + snprintf(serial_buf + actual_length, MAX_LEN - actual_length, " %d", g); + actual_length += val_char_len(g) + 1; + + snprintf(serial_buf + actual_length, MAX_LEN - actual_length, " %d", b); + actual_length += val_char_len(b) + 1; + } + + /*-----------------------------------------------------*\ + | Add the final newline | + \*-----------------------------------------------------*/ + snprintf(serial_buf + actual_length, MAX_LEN - actual_length, "\n"); + actual_length++; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + serialport->serial_write(serial_buf, actual_length); +} diff --git a/Controllers/DygmaRaiseController/DygmaRaiseController.h b/Controllers/DygmaRaiseController/DygmaRaiseController.h new file mode 100644 index 0000000..b596fdc --- /dev/null +++ b/Controllers/DygmaRaiseController/DygmaRaiseController.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| DygmaRaiseController.h | +| | +| Driver for Dygma Raise keyboard | +| | +| Timo Schlegel (@eispalast) Dec 12 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "serial_port.h" + +#define DYGMA_RAISE_VID 0x1209 +#define DYGMA_RAISE_PID 0x2201 + +#define DYGMA_RAISE_BAUD 115200 +#define MAX_LEN (4*3*132+9) //max. 4 Bytes per led channel * 3 channels * 132 leds + codeword led.theme + +class DygmaRaiseController +{ +public: + DygmaRaiseController(); + ~DygmaRaiseController(); + + void Initialize(char* port); + std::string GetDeviceLocation(); + void SendDirect(std::vectorcolors, size_t led_num); +private: + std::string location; + std::string port_name; + serial_port * serialport = nullptr; +}; diff --git a/Controllers/DygmaRaiseController/DygmaRaiseControllerDetect.cpp b/Controllers/DygmaRaiseController/DygmaRaiseControllerDetect.cpp new file mode 100644 index 0000000..e8e2f52 --- /dev/null +++ b/Controllers/DygmaRaiseController/DygmaRaiseControllerDetect.cpp @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| DygmaRaiseControllerDetect.cpp | +| | +| Detector for Dygma Raise keyboard | +| | +| Timo Schlegel (@eispalast) Dec 12 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "DygmaRaiseController.h" +#include "RGBController_DygmaRaise.h" +#include "find_usb_serial_port.h" +#include + +#define DYGMA_RAISE_VID 0x1209 +#define DYGMA_RAISE_PID 0x2201 + +/******************************************************************************************\ +* * +* DetectDygmaRaiseControllers * +* * +* Tests the USB address to see if a DygmaRaise keyboard exists there. * +* Then opens a serial port to communicate with the KB * +* * +\******************************************************************************************/ + +void DetectDygmaRaiseControllers() +{ + std::vector ports = find_usb_serial_port(DYGMA_RAISE_VID, DYGMA_RAISE_PID); + + for(std::size_t i = 0; i < ports.size(); i++) + { + if(*ports[i] != "") + { + DygmaRaiseController* controller = new DygmaRaiseController(); + controller->Initialize((char *)ports[i]->c_str()); + + RGBController_DygmaRaise* rgb_controller = new RGBController_DygmaRaise(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} + +REGISTER_DETECTOR("Dygma Raise", DetectDygmaRaiseControllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("Dygma Raise", DetectDygmaRaiseControllers, 0x1209, 0x2201 ) | +\*---------------------------------------------------------------------------------------------------------*/ diff --git a/Controllers/DygmaRaiseController/RGBController_DygmaRaise.cpp b/Controllers/DygmaRaiseController/RGBController_DygmaRaise.cpp new file mode 100644 index 0000000..1c786ba --- /dev/null +++ b/Controllers/DygmaRaiseController/RGBController_DygmaRaise.cpp @@ -0,0 +1,257 @@ +/*---------------------------------------------------------*\ +| RGBController_DygmaRaise.cpp | +| | +| RGBController for Dygma Raise keyboard | +| | +| Timo Schlegel (@eispalast) Dec 12 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_DygmaRaise.h" + +#define NA 0xFFFFFFFF +#define LED_REAL_COUNT ((6*14)+(12*14)) +#define LED_COUNT (LED_REAL_COUNT - 121) + +static unsigned int kb_matrix_map_ISO[6][14] = + { { 0, 1, 2, 3, 4, 5, 6, 39, 38, 37, 36, 35, 34, 33 }, + { 7, 8, 9, 10, 11, 12, 47, 46, 45, 44, 43, 42, 41, 40 }, + { 13, 14, 15, 16, 17, 18, 54, 53, 52, 51, 50, 49, 48, NA }, + { 19, 20, 21, 22, 23, 24, 25, 60, 59, 58, 57, 56, 55, NA }, + { 26, 27, 28, NA, 29, 30, NA, 66, 65, NA, 64, 63, 62, 61 }, + { NA, NA, NA, NA, 31, 32, NA, 68, 67, NA, NA, NA, NA, NA } }; + +static unsigned int underglow_matrix[11][14] = + { { 2, 3, 4, 5, 6, 7, NA, 38, 37, 36, 35, 34, 33, 32 }, + { 1, NA, NA, NA, NA, 8, NA, 39, NA, NA, NA, NA, NA, 31 }, + { 0, NA, NA, NA, NA, 9, NA, 40, NA, NA, NA, NA, NA, 30 }, + { 29, NA, NA, NA, NA, 10, NA, 41, NA, NA, NA, NA, NA, 61 }, + { 28, NA, NA, NA, NA, 11, NA, 42, NA, NA, NA, NA, NA, 60 }, + { 27, NA, NA, NA, NA, 12, NA, 43, NA, NA, NA, NA, NA, 59 }, + { 26, NA, NA, NA, NA, 13, NA, 44, NA, NA, NA, NA, NA, 58 }, + { 25, NA, NA, NA, NA, 14, NA, 45, NA, NA, NA, NA, NA, 57 }, + { 24, NA, NA, NA, NA, 15, NA, 46, NA, NA, NA, NA, NA, 56 }, + { 23, NA, NA, NA, NA, 16, NA, 47, NA, NA, NA, NA, NA, 55 }, + { 22, 21, 20, 19, 18, 17, NA, 48, 49, 50, 51, 52, 53, 54, } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, + "Underglow", + "Neuron", +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, + ZONE_TYPE_MATRIX, + ZONE_TYPE_SINGLE, +}; + +static const unsigned int zone_sizes[] = +{ + 69, + 62, + 1, +}; + +static const char* led_names[] = +{ + KEY_EN_ESCAPE, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_LEFT_SHIFT, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + "Key: T1", + "Key: T2", + "Key: T3", + "Key: T4", + KEY_EN_BACKSPACE, + KEY_EN_EQUALS, + KEY_EN_MINUS, + KEY_EN_0, + KEY_EN_9, + KEY_EN_8, + KEY_EN_7, + KEY_EN_ANSI_ENTER, + KEY_EN_RIGHT_BRACKET, + KEY_EN_LEFT_BRACKET, + KEY_EN_P, + KEY_EN_O, + KEY_EN_I, + KEY_EN_U, + KEY_EN_Y, + KEY_EN_POUND, + KEY_EN_QUOTE, + KEY_EN_SEMICOLON, + KEY_EN_L, + KEY_EN_K, + KEY_EN_J, + KEY_EN_H, + KEY_EN_RIGHT_SHIFT, + KEY_EN_FORWARD_SLASH, + KEY_EN_PERIOD, + KEY_EN_COMMA, + KEY_EN_M, + KEY_EN_N, + KEY_EN_RIGHT_CONTROL, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_RIGHT_ALT, + "Key: T6", + "Key: T5", + "Key: T8", + "Key: T7", +}; + +/**------------------------------------------------------------------*\ + @name Dygma Raise Keyboard + @category Keyboard + @type Serial + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectDygmaRaiseControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_DygmaRaise::RGBController_DygmaRaise(DygmaRaiseController* controller_ptr) +{ + controller = controller_ptr; + + name = "Raise"; + vendor = "Dygma"; + type = DEVICE_TYPE_KEYBOARD; + description = "Dygma Raise Split Mech KB"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_DygmaRaise::~RGBController_DygmaRaise() +{ + delete controller; +} + +void RGBController_DygmaRaise::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(size_t i=0; i<3; i++) + { + zone new_zone; + new_zone.name = zone_names[i]; + new_zone.type = zone_types[i]; + new_zone.leds_min = zone_sizes[i]; + new_zone.leds_max = zone_sizes[i]; + new_zone.leds_count = zone_sizes[i]; + + if(i==0) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 14; + new_zone.matrix_map->map = (unsigned int *)&kb_matrix_map_ISO; + } + else if(i==1) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 11; + new_zone.matrix_map->width = 14; + new_zone.matrix_map->map = (unsigned int *)&underglow_matrix; + } + + zones.push_back(new_zone); + } + + /*---------------------------------------------------------*\ + | Set up keyboard LEDs | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < zone_sizes[0]; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + /*---------------------------------------------------------*\ + | Set up underglow LEDs | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < zone_sizes[1]; led_idx++) + { + led new_led; + new_led.name = "Underglow"; + leds.push_back(new_led); + } + + /*---------------------------------------------------------*\ + | Set up Neuron LED | + \*---------------------------------------------------------*/ + led new_led; + new_led.name = "Neuron"; + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_DygmaRaise::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_DygmaRaise::DeviceUpdateLEDs() +{ + controller->SendDirect(colors,leds.size()); +} + +void RGBController_DygmaRaise::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DygmaRaise::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_DygmaRaise::DeviceUpdateMode() +{ + +} diff --git a/Controllers/DygmaRaiseController/RGBController_DygmaRaise.h b/Controllers/DygmaRaiseController/RGBController_DygmaRaise.h new file mode 100644 index 0000000..0a59b38 --- /dev/null +++ b/Controllers/DygmaRaiseController/RGBController_DygmaRaise.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_DygmaRaise.h | +| | +| RGBController for Dygma Raise keyboard | +| | +| Timo Schlegel (@eispalast) Dec 12 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "DygmaRaiseController.h" + +class RGBController_DygmaRaise : public RGBController +{ +public: + RGBController_DygmaRaise(DygmaRaiseController* controller_ptr); + ~RGBController_DygmaRaise(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + DygmaRaiseController* controller; +}; diff --git a/Controllers/E131Controller/E131ControllerDetect.cpp b/Controllers/E131Controller/E131ControllerDetect.cpp new file mode 100644 index 0000000..9d2e6db --- /dev/null +++ b/Controllers/E131Controller/E131ControllerDetect.cpp @@ -0,0 +1,287 @@ +/*---------------------------------------------------------*\ +| E131ControllerDetect.cpp | +| | +| Detector for E1.31 devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "Detector.h" +#include "RGBController.h" +#include "RGBController_E131.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectE131Controllers * +* * +* Detect devices supported by the E131 driver * +* * +\******************************************************************************************/ + +void DetectE131Controllers() +{ + json e131_settings; + + std::vector> device_lists; + E131Device dev; + + /*-------------------------------------------------*\ + | Get E1.31 settings from settings manager | + \*-------------------------------------------------*/ + e131_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("E131Devices"); + + /*-------------------------------------------------*\ + | If the E1.31 settings contains devices, process | + \*-------------------------------------------------*/ + if(e131_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < e131_settings["devices"].size(); device_idx++) + { + /*-------------------------------------------------*\ + | Clear E1.31 device data | + \*-------------------------------------------------*/ + dev.name = ""; + dev.ip = ""; + dev.type = ZONE_TYPE_SINGLE; + dev.num_leds = 0; + dev.rgb_order = E131_RGB_ORDER_RBG; + dev.matrix_order = E131_MATRIX_ORDER_HORIZONTAL_TOP_LEFT; + dev.matrix_width = 0; + dev.matrix_height = 0; + dev.start_channel = 1; + dev.start_universe = 1; + dev.keepalive_time = 0; + dev.universe_size = 512; + + if(e131_settings["devices"][device_idx].contains("name")) + { + dev.name = e131_settings["devices"][device_idx]["name"]; + } + + if(e131_settings["devices"][device_idx].contains("ip")) + { + dev.ip = e131_settings["devices"][device_idx]["ip"]; + } + + if(e131_settings["devices"][device_idx].contains("num_leds")) + { + dev.num_leds = e131_settings["devices"][device_idx]["num_leds"]; + } + + if(e131_settings["devices"][device_idx].contains("start_universe")) + { + dev.start_universe = e131_settings["devices"][device_idx]["start_universe"]; + } + + if(e131_settings["devices"][device_idx].contains("start_channel")) + { + dev.start_channel = e131_settings["devices"][device_idx]["start_channel"]; + } + + if(e131_settings["devices"][device_idx].contains("keepalive_time")) + { + dev.keepalive_time = e131_settings["devices"][device_idx]["keepalive_time"]; + } + + if(e131_settings["devices"][device_idx].contains("matrix_order")) + { + if(e131_settings["devices"][device_idx]["matrix_order"].is_string()) + { + std::string matrix_order_val = e131_settings["devices"][device_idx]["matrix_order"]; + + if(matrix_order_val == "HORIZONTAL_TOP_LEFT") + { + dev.matrix_order = E131_MATRIX_ORDER_HORIZONTAL_TOP_LEFT; + } + else if(matrix_order_val == "HORIZONTAL_TOP_RIGHT") + { + dev.matrix_order = E131_MATRIX_ORDER_HORIZONTAL_TOP_RIGHT; + } + else if(matrix_order_val == "HORIZONTAL_BOTTOM_LEFT") + { + dev.matrix_order = E131_MATRIX_ORDER_HORIZONTAL_BOTTOM_LEFT; + } + else if(matrix_order_val == "HORIZONTAL_BOTTOM_RIGHT") + { + dev.matrix_order = E131_MATRIX_ORDER_HORIZONTAL_BOTTOM_RIGHT; + } + else if(matrix_order_val == "VERTICAL_TOP_LEFT") + { + dev.matrix_order = E131_MATRIX_ORDER_VERTICAL_TOP_LEFT; + } + else if(matrix_order_val == "VERTICAL_TOP_RIGHT") + { + dev.matrix_order = E131_MATRIX_ORDER_VERTICAL_TOP_RIGHT; + } + else if(matrix_order_val == "VERTICAL_BOTTOM_LEFT") + { + dev.matrix_order = E131_MATRIX_ORDER_VERTICAL_BOTTOM_LEFT; + } + else if(matrix_order_val == "VERTICAL_BOTTOM_RIGHT") + { + dev.matrix_order = E131_MATRIX_ORDER_VERTICAL_BOTTOM_RIGHT; + } + } + else + { + dev.matrix_order = e131_settings["devices"][device_idx]["matrix_order"]; + } + } + + if(e131_settings["devices"][device_idx].contains("rgb_order")) + { + if(e131_settings["devices"][device_idx]["rgb_order"].is_string()) + { + std::string rgb_order_val = e131_settings["devices"][device_idx]["rgb_order"]; + + if(rgb_order_val == "RGB") + { + dev.rgb_order = E131_RGB_ORDER_RGB; + } + else if(rgb_order_val == "RBG") + { + dev.rgb_order = E131_RGB_ORDER_RBG; + } + else if(rgb_order_val == "GRB") + { + dev.rgb_order = E131_RGB_ORDER_GRB; + } + else if(rgb_order_val == "GBR") + { + dev.rgb_order = E131_RGB_ORDER_GBR; + } + else if(rgb_order_val == "BRG") + { + dev.rgb_order = E131_RGB_ORDER_BRG; + } + else if(rgb_order_val == "BGR") + { + dev.rgb_order = E131_RGB_ORDER_BGR; + } + } + else + { + dev.rgb_order = e131_settings["devices"][device_idx]["rgb_order"]; + } + } + + if(e131_settings["devices"][device_idx].contains("matrix_width")) + { + dev.matrix_width = e131_settings["devices"][device_idx]["matrix_width"]; + } + + if(e131_settings["devices"][device_idx].contains("matrix_height")) + { + dev.matrix_height = e131_settings["devices"][device_idx]["matrix_height"]; + } + + if(e131_settings["devices"][device_idx].contains("universe_size")) + { + dev.universe_size = e131_settings["devices"][device_idx]["universe_size"]; + } + + if(e131_settings["devices"][device_idx].contains("type")) + { + if(e131_settings["devices"][device_idx]["type"].is_string()) + { + std::string type_val = e131_settings["devices"][device_idx]["type"]; + + if(type_val == "SINGLE") + { + dev.type = ZONE_TYPE_SINGLE; + } + else if(type_val == "LINEAR") + { + dev.type = ZONE_TYPE_LINEAR; + } + else if(type_val == "MATRIX") + { + dev.type = ZONE_TYPE_MATRIX; + } + } + else + { + dev.type = e131_settings["devices"][device_idx]["type"]; + } + } + + /*---------------------------------------------------------*\ + | Determine whether to create a new list or add this device | + | to an existing list. A device is added to an existing | + | list if both devices share one or more universes for the | + | same output destination | + \*---------------------------------------------------------*/ + bool device_added_to_existing_list = false; + + /*---------------------------------------------------------*\ + | Track grouping for all controllers. | + \*---------------------------------------------------------*/ + for(unsigned int list_idx = 0; list_idx < device_lists.size(); list_idx++) + { + for(unsigned int device_idx = 0; device_idx < device_lists[list_idx].size(); device_idx++) + { + /*---------------------------------------------------------*\ + | Determine if there is any overlap between this device and | + | any existing device list | + | Offset the end by two - one because the range is 1-512 | + | rather than 0-511, and one because the start channel is | + | included in the first set of 3 channels. | + \*---------------------------------------------------------*/ + unsigned int dev_start = dev.start_universe; + unsigned int list_start = device_lists[list_idx][device_idx].start_universe; + unsigned int dev_end = dev.start_universe + ((dev.start_channel + (3 * dev.num_leds) - 2) / 512); + unsigned int list_end = device_lists[list_idx][device_idx].start_universe + ((device_lists[list_idx][device_idx].start_channel + (3 * device_lists[list_idx][device_idx].num_leds) - 2) / 512); + std::string dev_ip = dev.ip; + std::string list_ip = device_lists[list_idx][device_idx].ip; + + bool overlap = dev_ip == list_ip && !(dev_end < list_start || list_end < dev_start); + + /*---------------------------------------------------------*\ + | Check if any universes used by this new device exist in | + | the existing device. If so, add the new device to the | + | existing list. | + \*---------------------------------------------------------*/ + if(overlap) + { + device_lists[list_idx].push_back(dev); + device_added_to_existing_list = true; + break; + } + } + + if(device_added_to_existing_list) + { + break; + } + } + + /*---------------------------------------------------------*\ + | If the device did not overlap with existing devices, | + | create a new list for it | + \*---------------------------------------------------------*/ + if(!device_added_to_existing_list) + { + std::vector new_list; + + new_list.push_back(dev); + + device_lists.push_back(new_list); + } + } + + for(unsigned int list_idx = 0; list_idx < device_lists.size(); list_idx++) + { + RGBController_E131* rgb_controller; + rgb_controller = new RGBController_E131(device_lists[list_idx]); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + +} /* DetectE131Controllers() */ + +REGISTER_DETECTOR("E1.31", DetectE131Controllers); diff --git a/Controllers/E131Controller/RGBController_E131.cpp b/Controllers/E131Controller/RGBController_E131.cpp new file mode 100644 index 0000000..43bd9ed --- /dev/null +++ b/Controllers/E131Controller/RGBController_E131.cpp @@ -0,0 +1,470 @@ +/*---------------------------------------------------------*\ +| RGBController_E131.cpp | +| | +| RGBController for E1.31 devices | +| | +| Adam Honse (CalcProgrammer1) 18 Oct 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_E131.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name E1.31 Devices + @category LEDStrip + @type E1.31 + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectE131Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_E131::RGBController_E131(std::vector device_list) +{ + bool multicast = false; + + devices = device_list; + + name = "E1.31 Device Group"; + type = DEVICE_TYPE_LEDSTRIP; + description = "E1.31 Streaming ACN Device"; + location = "E1.31: "; + + /*-----------------------------------------*\ + | If this controller only represents a | + | single device, use the device name for the| + | controller name | + \*-----------------------------------------*/ + if(devices.size() == 1) + { + name = devices[0].name; + } + else if(devices[0].ip != "") + { + name += " (" + devices[0].ip + ")"; + } + + /*-----------------------------------------*\ + | Append the destination address to the | + | location field | + \*-----------------------------------------*/ + if(devices[0].ip != "") + { + location += "Unicast " + devices[0].ip + ", "; + } + else + { + location += "Multicast, "; + multicast = true; + } + + /*-----------------------------------------*\ + | Calculate universe list | + | Use this to fill in the location field | + \*-----------------------------------------*/ + std::vector universe_list; + + for(unsigned int device_idx = 0; device_idx < devices.size(); device_idx++) + { + float universe_size = (float)devices[device_idx].universe_size; + unsigned int total_universes = (unsigned int)ceil( ( ( devices[device_idx].num_leds * 3 ) + devices[device_idx].start_channel ) / universe_size ); + + for(unsigned int univ_idx = 0; univ_idx < total_universes; univ_idx++) + { + bool found = false; + + for(unsigned int univ_list_idx = 0; univ_list_idx < universe_list.size(); univ_list_idx++) + { + if((devices[device_idx].start_universe + univ_idx) == universe_list[univ_list_idx]) + { + found = true; + break; + } + } + + if(!found) + { + universe_list.push_back(devices[device_idx].start_universe + univ_idx); + } + } + } + + /*-----------------------------------------*\ + | Append "Universe" and make plural if there| + | are multiple universes in use | + \*-----------------------------------------*/ + location += "Universe"; + + if(universe_list.size() > 1) + { + location += "s "; + } + else + { + location += " "; + } + + /*-----------------------------------------*\ + | Append comma separated list of universes | + \*-----------------------------------------*/ + for(unsigned int univ_list_idx = 0; univ_list_idx < universe_list.size(); univ_list_idx++) + { + location += std::to_string(universe_list[univ_list_idx]); + + if(univ_list_idx < (universe_list.size() - 1)) + { + location += ", "; + } + } + + /*-----------------------------------------*\ + | Set up modes | + \*-----------------------------------------*/ + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + /*-----------------------------------------*\ + | Create E1.31 socket | + \*-----------------------------------------*/ + sockfd = e131_socket(); + + keepalive_delay = 0ms; + + SetupZones(); + + for (std::size_t device_idx = 0; device_idx < devices.size(); device_idx++) + { + /*-----------------------------------------*\ + | Update keepalive delay | + \*-----------------------------------------*/ + if(devices[device_idx].keepalive_time > 0) + { + if(keepalive_delay.count() == 0 || keepalive_delay.count() > devices[device_idx].keepalive_time) + { + keepalive_delay = std::chrono::milliseconds(devices[device_idx].keepalive_time); + } + } + + /*-----------------------------------------*\ + | Add Universes | + \*-----------------------------------------*/ + float universe_size = (float)devices[device_idx].universe_size; + unsigned int total_universes = (unsigned int)ceil( ( ( devices[device_idx].num_leds * 3 ) + devices[device_idx].start_channel ) / universe_size ); + + for (unsigned int univ_idx = 0; univ_idx < total_universes; univ_idx++) + { + unsigned int universe = devices[device_idx].start_universe + univ_idx; + bool universe_exists = false; + + for (std::size_t pkt_idx = 0; pkt_idx < packets.size(); pkt_idx++) + { + if(universes[pkt_idx] == universe) + { + universe_exists = true; + } + } + + if(!universe_exists) + { + e131_packet_t packet; + e131_addr_t dest_addr; + + e131_pkt_init(&packet, (uint16_t)universe, (uint16_t)universe_size); + + if(multicast) + { + e131_multicast_dest(&dest_addr, universe, E131_DEFAULT_PORT); + } + else + { + e131_unicast_dest(&dest_addr, devices[0].ip.c_str(), E131_DEFAULT_PORT); + } + + packets.push_back(packet); + universes.push_back(universe); + dest_addrs.push_back(dest_addr); + } + } + + /*-----------------------------------------*\ + | Generate matrix maps | + \*-----------------------------------------*/ + if(devices[device_idx].type == ZONE_TYPE_MATRIX) + { + unsigned int led_idx = 0; + matrix_map_type * new_map = new matrix_map_type; + + new_map->width = devices[device_idx].matrix_width; + new_map->height = devices[device_idx].matrix_height; + new_map->map = new unsigned int[devices[device_idx].matrix_width * devices[device_idx].matrix_height]; + + switch(devices[device_idx].matrix_order) + { + case E131_MATRIX_ORDER_HORIZONTAL_TOP_LEFT: + for(unsigned int y = 0; y < new_map->height; y++) + { + for(unsigned int x = 0; x < new_map->width; x++) + { + new_map->map[(y * new_map->width) + x] = led_idx; + led_idx++; + } + } + break; + case E131_MATRIX_ORDER_HORIZONTAL_TOP_RIGHT: + for(unsigned int y = 0; y < new_map->height; y++) + { + for(int x = new_map->width - 1; x >= 0; x--) + { + new_map->map[(y * new_map->width) + x] = led_idx; + led_idx++; + } + } + break; + case E131_MATRIX_ORDER_HORIZONTAL_BOTTOM_LEFT: + for(int y = new_map->height; y >= 0; y--) + { + for(unsigned int x = 0; x < new_map->width; x++) + { + new_map->map[(y * new_map->width) + x] = led_idx; + led_idx++; + } + } + break; + case E131_MATRIX_ORDER_HORIZONTAL_BOTTOM_RIGHT: + for(int y = new_map->height; y >= 0; y--) + { + for(int x = new_map->width - 1; x >= 0; x--) + { + new_map->map[(y * new_map->width) + x] = led_idx; + led_idx++; + } + } + break; + case E131_MATRIX_ORDER_VERTICAL_TOP_LEFT: + for(unsigned int x = 0; x < new_map->width; x++) + { + for(unsigned int y = 0; y < new_map->height; y++) + { + new_map->map[(y * new_map->width) + x] = led_idx; + led_idx++; + } + } + break; + case E131_MATRIX_ORDER_VERTICAL_TOP_RIGHT: + for(int x = new_map->width - 1; x >= 0; x--) + { + for(unsigned int y = 0; y < new_map->height; y++) + { + new_map->map[(y * new_map->width) + x] = led_idx; + led_idx++; + } + } + break; + case E131_MATRIX_ORDER_VERTICAL_BOTTOM_LEFT: + for(unsigned int x = 0; x < new_map->width; x++) + { + for(int y = new_map->height - 1; y >= 0; y--) + { + new_map->map[(y * new_map->width) + x] = led_idx; + led_idx++; + } + } + break; + case E131_MATRIX_ORDER_VERTICAL_BOTTOM_RIGHT: + for(int x = new_map->width - 1; x >= 0; x--) + { + for(int y = new_map->height - 1; y >= 0; y--) + { + new_map->map[(y * new_map->width) + x] = led_idx; + led_idx++; + } + } + break; + } + zones[device_idx].matrix_map = new_map; + } + } + + if(keepalive_delay.count() > 0) + { + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_E131::KeepaliveThreadFunction, this); + } + else + { + keepalive_thread_run = 0; + keepalive_thread = nullptr; + } +} + +RGBController_E131::~RGBController_E131() +{ + if(keepalive_thread != nullptr) + { + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + } + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + if(zones[zone_index].matrix_map->map != NULL) + { + delete zones[zone_index].matrix_map->map; + } + + delete zones[zone_index].matrix_map; + } + } +} + +void RGBController_E131::SetupZones() +{ + /*-----------------------------------------*\ + | Add Zones | + \*-----------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < devices.size(); zone_idx++) + { + zone led_zone; + led_zone.name = devices[zone_idx].name; + led_zone.type = devices[zone_idx].type; + led_zone.leds_min = devices[zone_idx].num_leds; + led_zone.leds_max = devices[zone_idx].num_leds; + led_zone.leds_count = devices[zone_idx].num_leds; + led_zone.matrix_map = NULL; + + zones.push_back(led_zone); + } + + /*-----------------------------------------*\ + | Add LEDs | + \*-----------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + + new_led.name = zones[zone_idx].name + " LED "; + new_led.name.append(std::to_string(led_idx)); + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_E131::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_E131::DeviceUpdateLEDs() +{ + int color_idx = 0; + + last_update_time = std::chrono::steady_clock::now(); + + for(std::size_t device_idx = 0; device_idx < devices.size(); device_idx++) + { + float universe_size = (float)devices[device_idx].universe_size; + unsigned int total_universes = (unsigned int)ceil( ( ( devices[device_idx].num_leds * 3 ) + devices[device_idx].start_channel ) / universe_size ); + unsigned int channel_idx = devices[device_idx].start_channel; + unsigned int led_idx = 0; + unsigned int rgb_idx = 0; + bool done = false; + + for (unsigned int univ_idx = 0; univ_idx < total_universes; univ_idx++) + { + unsigned int universe = devices[device_idx].start_universe + univ_idx; + + for(std::size_t packet_idx = 0; packet_idx < packets.size(); packet_idx++) + { + if(!done && (universes[packet_idx] == universe)) + { + while(!done && (channel_idx <= universe_size)) + { + switch(rgb_idx) + { + case 0: + packets[packet_idx].dmp.prop_val[channel_idx] = RGBGetRValue( colors[color_idx] ); + rgb_idx = 1; + break; + case 1: + packets[packet_idx].dmp.prop_val[channel_idx] = RGBGetGValue( colors[color_idx] ); + rgb_idx = 2; + break; + case 2: + packets[packet_idx].dmp.prop_val[channel_idx] = RGBGetBValue( colors[color_idx] ); + rgb_idx = 0; + led_idx++; + color_idx++; + break; + } + + if(led_idx >= devices[device_idx].num_leds) + { + done = true; + } + + channel_idx++; + } + } + } + + channel_idx = 1; + } + } + + for(std::size_t packet_idx = 0; packet_idx < packets.size(); packet_idx++) + { + e131_send(sockfd, &packets[packet_idx], &dest_addrs[packet_idx]); + packets[packet_idx].frame.seq_number++; + } +} + +void RGBController_E131::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_E131::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_E131::DeviceUpdateMode() +{ + +} + +void RGBController_E131::KeepaliveThreadFunction() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_update_time) > ( keepalive_delay * 0.95f ) ) + { + UpdateLEDs(); + } + std::this_thread::sleep_for(keepalive_delay / 2); + } +} diff --git a/Controllers/E131Controller/RGBController_E131.h b/Controllers/E131Controller/RGBController_E131.h new file mode 100644 index 0000000..bc051cb --- /dev/null +++ b/Controllers/E131Controller/RGBController_E131.h @@ -0,0 +1,89 @@ +/*---------------------------------------------------------*\ +| RGBController_E131.h | +| | +| RGBController for E1.31 devices | +| | +| Adam Honse (CalcProgrammer1) 18 Oct 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +typedef unsigned int e131_rgb_order; + +enum +{ + E131_RGB_ORDER_RGB, + E131_RGB_ORDER_RBG, + E131_RGB_ORDER_GRB, + E131_RGB_ORDER_GBR, + E131_RGB_ORDER_BRG, + E131_RGB_ORDER_BGR +}; + +enum +{ + E131_MATRIX_ORDER_HORIZONTAL_TOP_LEFT, + E131_MATRIX_ORDER_HORIZONTAL_TOP_RIGHT, + E131_MATRIX_ORDER_HORIZONTAL_BOTTOM_LEFT, + E131_MATRIX_ORDER_HORIZONTAL_BOTTOM_RIGHT, + E131_MATRIX_ORDER_VERTICAL_TOP_LEFT, + E131_MATRIX_ORDER_VERTICAL_TOP_RIGHT, + E131_MATRIX_ORDER_VERTICAL_BOTTOM_LEFT, + E131_MATRIX_ORDER_VERTICAL_BOTTOM_RIGHT +}; + +typedef unsigned int e131_matrix_order; + +struct E131Device +{ + std::string name; + std::string ip; + unsigned int num_leds; + unsigned int start_universe; + unsigned int start_channel; + unsigned int keepalive_time; + e131_rgb_order rgb_order; + zone_type type; + unsigned int matrix_width; + unsigned int matrix_height; + unsigned int universe_size; + e131_matrix_order matrix_order; +}; + +class RGBController_E131 : public RGBController +{ +public: + RGBController_E131(std::vector device_list); + ~RGBController_E131(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThreadFunction(); + +private: + std::vector devices; + std::vector packets; + std::vector dest_addrs; + std::vector universes; + int sockfd; + std::thread * keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::milliseconds keepalive_delay; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/EKController/EKController.cpp b/Controllers/EKController/EKController.cpp new file mode 100644 index 0000000..8a6b3fd --- /dev/null +++ b/Controllers/EKController/EKController.cpp @@ -0,0 +1,136 @@ +/*---------------------------------------------------------*\ +| EKController.cpp | +| | +| Driver for EK Loop Connect | +| | +| Chris M (Dr_No) 16 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "EKController.h" +#include "StringUtils.h" + +static unsigned char ek_colour_mode_data[][16] = +{ + { 0x10, 0x12, 0x29, 0xAA, 0x01, 0x10, 0xA2, 0x60, + 0x00, 0x10, 0x20, 0x01, 0x01, 0x00, 0xFF, 0x64}, // Static + { 0x10, 0x12, 0x29, 0xAA, 0x01, 0x10, 0xA2, 0x60, + 0x00, 0x10, 0x20, 0x01, 0x02, 0x00, 0xFF, 0x64}, // Breathing + { 0x10, 0x12, 0x29, 0xAA, 0x01, 0x10, 0xA2, 0x60, + 0x00, 0x10, 0x20, 0x01, 0x03, 0xFF, 0xFF, 0x64}, // Fading + { 0x10, 0x12, 0x29, 0xAA, 0x01, 0x10, 0xA2, 0x60, + 0x00, 0x10, 0x20, 0x01, 0x04, 0x00, 0xFF, 0x64}, // Marquee + { 0x10, 0x12, 0x29, 0xAA, 0x01, 0x10, 0xA2, 0x60, + 0x00, 0x10, 0x20, 0x01, 0x05, 0x00, 0xFF, 0x64}, // Covering Marquee + { 0x10, 0x12, 0x29, 0xAA, 0x01, 0x10, 0xA2, 0x60, + 0x00, 0x10, 0x20, 0x01, 0x06, 0x00, 0xFF, 0x64}, // Pulse + { 0x10, 0x12, 0x29, 0xAA, 0x01, 0x10, 0xA2, 0x60, + 0x00, 0x10, 0x20, 0x01, 0x07, 0x00, 0xFF, 0x64}, // Wave + { 0x10, 0x12, 0x29, 0xAA, 0x01, 0x10, 0xA2, 0x60, + 0x00, 0x10, 0x20, 0x01, 0x08, 0x00, 0xFF, 0x64}, // Alternating + { 0x10, 0x12, 0x29, 0xAA, 0x01, 0x10, 0xA2, 0x60, + 0x00, 0x10, 0x20, 0x01, 0x09, 0x00, 0xFF, 0x64}, // Candle +}; + +static unsigned char ek_speed_mode_data[][9] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Static + { 0x00, 0x0C, 0x19, 0x25, 0x32, 0x3E, 0x4B, 0x57, 0x64 }, // Breathing + { 0x00, 0x0C, 0x19, 0x25, 0x32, 0x3E, 0x4B, 0x57, 0x64 }, // Fading + { 0x00, 0x0C, 0x19, 0x25, 0x32, 0x3E, 0x4B, 0x57, 0x64 }, // Marquee + { 0x00, 0x0C, 0x19, 0x25, 0x32, 0x3E, 0x4B, 0x57, 0x64 }, // Covering Marquee + { 0x00, 0x0C, 0x19, 0x25, 0x32, 0x3E, 0x4B, 0x57, 0x64 }, // Pulse + { 0x00, 0x0C, 0x19, 0x25, 0x32, 0x3E, 0x4B, 0x57, 0x64 }, // Wave + { 0x00, 0x0C, 0x19, 0x25, 0x32, 0x3E, 0x4B, 0x57, 0x64 }, // Alternating + { 0x00, 0x0C, 0x19, 0x25, 0x32, 0x3E, 0x4B, 0x57, 0x64 } // Candle +}; + +EKController::EKController(hid_device* dev_handle, char *_path) +{ + dev = dev_handle; + location = _path; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + + current_mode = EK_MODE_STATIC; + current_speed = EK_SPEED_NORMAL; +} + +EKController::~EKController() +{ + hid_close(dev); +} + +std::string EKController::GetDeviceName() +{ + return device_name; +} + +std::string EKController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string EKController::GetLocation() +{ + return("HID: " + location); +} + +void EKController::SetMode(unsigned char mode, unsigned char speed) +{ + current_mode = mode; + current_speed = speed; + + SendUpdate(); +} + +void EKController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + current_red = red; + current_green = green; + current_blue = blue; + + SendUpdate(); +} + +void EKController::SendUpdate() +{ + unsigned char buffer[EK_PACKET_LENGTH] = { 0x00 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + + for(std::size_t i = 0; i < EK_COLOUR_MODE_DATA_SIZE; i++) + { + buffer[i] = ek_colour_mode_data[current_mode][i]; + } + + //Set the relevant colour info + buffer[EK_RED_BYTE] = current_red; + buffer[EK_GREEN_BYTE] = current_green; + buffer[EK_BLUE_BYTE] = current_blue; + buffer[EK_SPEED_BYTE] = ek_speed_mode_data[current_mode][current_speed]; + + buffer[10] = 0x10; + buffer[47] = 0xFF; + buffer[48] = 0x00; + + hid_write(dev, buffer, buffer_size); +} diff --git a/Controllers/EKController/EKController.h b/Controllers/EKController/EKController.h new file mode 100644 index 0000000..21dce52 --- /dev/null +++ b/Controllers/EKController/EKController.h @@ -0,0 +1,83 @@ +/*---------------------------------------------------------*\ +| EKController.h | +| | +| Driver for EK Loop Connect | +| | +| Chris M (Dr_No) 16 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +#define EK_COLOUR_MODE_DATA_SIZE (sizeof(ek_colour_mode_data[0]) / sizeof(ek_colour_mode_data[0][0])) +#define EK_DEVICE_NAME_SIZE (sizeof(device_name) / sizeof(device_name[ 0 ])) +#define EK_PACKET_LENGTH 0x3F +#define HID_MAX_STR 255 + +enum +{ + EK_MODE_BYTE = 12, + EK_SPEED_BYTE = 14, + EK_RED_BYTE = 16, + EK_GREEN_BYTE = 17, + EK_BLUE_BYTE = 18 +}; + +enum +{ + EK_MODE_STATIC = 0x00, //Static Mode + EK_MODE_BREATHING = 0x01, //Breathing Mode + EK_MODE_FADING = 0x02, //Fading Mode + EK_MODE_MARQUEE = 0x03, //Marquee Mode + EK_MODE_COVERING_MARQUEE = 0x04, //Covering Marquee Mode + EK_MODE_PULSE = 0x05, //Pulse Mode + EK_MODE_SPECTRUM_WAVE = 0x06, //Spectrum Wave Mode + EK_MODE_ALTERNATING = 0x07, //Alternating Mode + EK_MODE_CANDLE = 0x08 //Candle Mode +}; + +enum +{ + EK_SPEED_SLOWEST = 0x00, // Slowest speed + EK_SPEED_SLOWER = 0x01, // Slower speed + EK_SPEED_SLOW = 0x02, // Slow speed + EK_SPEED_SLOWISH = 0x03, // Slowish speed + EK_SPEED_NORMAL = 0x04, // Normal speed + EK_SPEED_FASTISH = 0x05, // Fastish speed + EK_SPEED_FAST = 0x06, // Fast speed + EK_SPEED_FASTER = 0x07, // Faster speed + EK_SPEED_FASTEST = 0x08, // Fastest speed +}; + +class EKController +{ +public: + EKController(hid_device* dev_handle, char *_path); + ~EKController(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + void SetMode(unsigned char mode, unsigned char speed); + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + +private: + std::string device_name; + std::string location; + hid_device* dev; + + unsigned char current_mode; + unsigned char current_speed; + + unsigned char current_red; + unsigned char current_green; + unsigned char current_blue; + + void SendUpdate(); +}; diff --git a/Controllers/EKController/EKControllerDetect.cpp b/Controllers/EKController/EKControllerDetect.cpp new file mode 100644 index 0000000..1cbf81a --- /dev/null +++ b/Controllers/EKController/EKControllerDetect.cpp @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| EKControllerDetect.cpp | +| | +| Detector for EK Loop Connect | +| | +| Chris M (Dr_No) 16 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "EKController.h" +#include "RGBController_EKController.h" + +#define EK_VID 0x0483 +#define EK_LOOP_CONNECT 0x5750 + +/******************************************************************************************\ +* * +* DetectEKControllers * +* * +* Tests the USB address to see if any EK Controllers exists there. * +* * +\******************************************************************************************/ + +void DetectEKControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + EKController* controller = new EKController(dev, info->path); + RGBController_EKController* rgb_controller = new RGBController_EKController(controller); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectEKControllers() */ + +REGISTER_HID_DETECTOR_IPU("EK Loop Connect", DetectEKControllers, EK_VID, EK_LOOP_CONNECT, 0, 0xFFA0, 1); diff --git a/Controllers/EKController/RGBController_EKController.cpp b/Controllers/EKController/RGBController_EKController.cpp new file mode 100644 index 0000000..ba1b82d --- /dev/null +++ b/Controllers/EKController/RGBController_EKController.cpp @@ -0,0 +1,183 @@ +/*---------------------------------------------------------*\ +| RGBController_EKController.cpp | +| | +| RGBController for EK Loop Connect | +| | +| Chris M (Dr_No) 16 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_EKController.h" + +/**------------------------------------------------------------------*\ + @name EK Loop Connect + @category LEDStrip + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectEKControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_EKController::RGBController_EKController(EKController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "EK"; + type = DEVICE_TYPE_LEDSTRIP; + description = controller->GetDeviceName(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Static; + Static.name = "Static"; + Static.value = EK_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EK_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = EK_SPEED_SLOWEST; + Breathing.speed_max = EK_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed = EK_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Fading; + Fading.name = "Fading"; + Fading.value = EK_MODE_FADING; + Fading.flags = MODE_FLAG_HAS_SPEED; + Fading.speed_min = EK_SPEED_SLOWEST; + Fading.speed_max = EK_SPEED_FASTEST; + Fading.color_mode = MODE_COLORS_NONE; + Fading.speed = EK_SPEED_NORMAL; + modes.push_back(Fading); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = EK_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Marquee.speed_min = EK_SPEED_SLOWEST; + Marquee.speed_max = EK_SPEED_FASTEST; + Marquee.color_mode = MODE_COLORS_PER_LED; + Marquee.speed = EK_SPEED_NORMAL; + modes.push_back(Marquee); + + mode Covering_Marquee; + Covering_Marquee.name = "Covering Marquee"; + Covering_Marquee.value = EK_MODE_COVERING_MARQUEE; + Covering_Marquee.flags = MODE_FLAG_HAS_SPEED; + Covering_Marquee.speed_min = EK_SPEED_SLOWEST; + Covering_Marquee.speed_max = EK_SPEED_FASTEST; + Covering_Marquee.color_mode = MODE_COLORS_NONE; + Covering_Marquee.speed = EK_SPEED_NORMAL; + modes.push_back(Covering_Marquee); + + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = EK_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Pulse.speed_min = EK_SPEED_SLOWEST; + Pulse.speed_max = EK_SPEED_FASTEST; + Pulse.color_mode = MODE_COLORS_PER_LED; + Pulse.speed = EK_SPEED_NORMAL; + modes.push_back(Pulse); + + mode Spectrum_Wave; + Spectrum_Wave.name = "Spectrum_Wave"; + Spectrum_Wave.value = EK_MODE_SPECTRUM_WAVE; + Spectrum_Wave.flags = MODE_FLAG_HAS_SPEED; + Spectrum_Wave.speed_min = EK_SPEED_SLOWEST; + Spectrum_Wave.speed_max = EK_SPEED_FASTEST; + Spectrum_Wave.color_mode = MODE_COLORS_NONE; + Spectrum_Wave.speed = EK_SPEED_NORMAL; + modes.push_back(Spectrum_Wave); + + mode Alternating; + Alternating.name = "Alternating"; + Alternating.value = EK_MODE_ALTERNATING; + Alternating.flags = MODE_FLAG_HAS_SPEED; + Alternating.speed_min = EK_SPEED_SLOWEST; + Alternating.speed_max = EK_SPEED_FASTEST; + Alternating.color_mode = MODE_COLORS_PER_LED; + Alternating.speed = EK_SPEED_NORMAL; + modes.push_back(Alternating); + + mode Candle; + Candle.name = "Candle"; + Candle.value = EK_MODE_CANDLE; + Candle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Candle.speed_min = EK_SPEED_SLOWEST; + Candle.speed_max = EK_SPEED_FASTEST; + Candle.color_mode = MODE_COLORS_PER_LED; + Candle.speed = EK_SPEED_NORMAL; + modes.push_back(Candle); + + SetupZones(); +} + +RGBController_EKController::~RGBController_EKController() +{ + delete controller; +} + +void RGBController_EKController::SetupZones() +{ + zone EK_zone; + EK_zone.name = "Loop Connect"; + EK_zone.type = ZONE_TYPE_SINGLE; + EK_zone.leds_min = 1; + EK_zone.leds_max = 1; + EK_zone.leds_count = 1; + EK_zone.matrix_map = NULL; + zones.push_back(EK_zone); + + led EK_led; + EK_led.name = "EK LED"; + leds.push_back(EK_led); + + SetupColors(); +} + +void RGBController_EKController::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | ToDo | + \*---------------------------------------------------------*/ +} + +void RGBController_EKController::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetColor(red, grn, blu); +} + +void RGBController_EKController::UpdateZoneLEDs(int zone) +{ + RGBColor color = colors[zone]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetColor(red, grn, blu); +} + +void RGBController_EKController::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_EKController::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].value, modes[active_mode].speed); +} diff --git a/Controllers/EKController/RGBController_EKController.h b/Controllers/EKController/RGBController_EKController.h new file mode 100644 index 0000000..459ffbc --- /dev/null +++ b/Controllers/EKController/RGBController_EKController.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_EKController.h | +| | +| RGBController for EK Loop Connect | +| | +| Chris M (Dr_No) 16 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "EKController.h" + +class RGBController_EKController : public RGBController +{ +public: + RGBController_EKController(EKController* controller_ptr); + ~RGBController_EKController(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + EKController* controller; +}; diff --git a/Controllers/ENESMBusController/ENESMBusController.cpp b/Controllers/ENESMBusController/ENESMBusController.cpp new file mode 100644 index 0000000..6b686f9 --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusController.cpp @@ -0,0 +1,558 @@ +/*---------------------------------------------------------*\ +| ENESMBusController.cpp | +| | +| Driver for ENE SMBus devices | +| | +| Adam Honse (CalcProgrammer1) 19 Aug 2018 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "ENESMBusController.h" +#include "LogManager.h" + +static const char* ene_channels[] = /* ENE channel strings */ +{ + "Audio", + "Backplate", + "Back I/O", + "Center", + "Center", + "DRAM", + "PCIe", + "RGB Header", + "RGB Header 2", + "RGB Header", + "SSD", + "Unknown", +}; + +ENESMBusController::ENESMBusController(ENESMBusInterface* interface, ene_dev_id dev, std::string dev_name, device_type dev_type) +{ + this->interface = interface; + this->dev = dev; + this->name = dev_name; + this->type = dev_type; + supports_mode_14 = false; + + if(interface->GetInterfaceType() != ENE_INTERFACE_TYPE_ROG_ARION) + { + UpdateDeviceName(); + + /*-------------------------------------------------*\ + | Read the device configuration table | + \*-------------------------------------------------*/ + for(int i = 0; i < 64; i++) + { + config_table[i] = ENERegisterRead(ENE_REG_CONFIG_TABLE + i); + } + + /*-------------------------------------------------*\ + | If this is running with TRACE or higher loglevel | + | then dump the entire Feature list to log | + \*-------------------------------------------------*/ + if(LogManager::get()->getLoglevel() >= LL_TRACE) + { + LOG_TRACE("[ENE SMBus] ENE config table for 0x%02X:", dev); + LOG_TRACE(" %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", config_table[0], config_table[1], config_table[2], config_table[3], + config_table[4], config_table[5], config_table[6], config_table[7], + config_table[8], config_table[9], config_table[10], config_table[11], + config_table[12], config_table[13], config_table[14], config_table[15]); + + LOG_TRACE(" %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", config_table[16], config_table[17], config_table[18], config_table[19], + config_table[20], config_table[21], config_table[22], config_table[23], + config_table[24], config_table[25], config_table[26], config_table[27], + config_table[28], config_table[29], config_table[30], config_table[31]); + + LOG_TRACE(" %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", config_table[32], config_table[33], config_table[34], config_table[35], + config_table[36], config_table[37], config_table[38], config_table[39], + config_table[40], config_table[41], config_table[42], config_table[43], + config_table[44], config_table[45], config_table[46], config_table[47]); + + LOG_TRACE(" %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", config_table[48], config_table[49], config_table[50], config_table[51], + config_table[52], config_table[53], config_table[54], config_table[55], + config_table[56], config_table[57], config_table[58], config_table[59], + config_table[60], config_table[61], config_table[62], config_table[63]); + } + } + else + { + LOG_TRACE("[ENE SMBus] ROG STRIX ARION detected, filling in hard coded config table entries.", dev); + memset(config_table, 0, sizeof(config_table)); + config_table[ENE_CONFIG_LED_COUNT] = 4; + config_table[0x03] = 4; + strcpy(device_version, "ROG STRIX ARION"); + } + + /*-----------------------------------------------------*\ + | Read LED count from configuration table | + \*-----------------------------------------------------*/ + led_count = config_table[ENE_CONFIG_LED_COUNT]; + + /*-----------------------------------------------------*\ + | LED-0116 - First generation motherboard controller | + \*-----------------------------------------------------*/ + if(strcmp(device_version, "LED-0116") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT; + effect_reg = ENE_REG_COLORS_EFFECT; + channel_cfg = ENE_CONFIG_CHANNEL_V1; + } + /*-----------------------------------------------------*\ + | DIMM_LED-0102 - First generation DRAM controller | + | (Trident Z RGB) | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "DIMM_LED-0102") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT; + effect_reg = ENE_REG_COLORS_EFFECT; + channel_cfg = ENE_CONFIG_CHANNEL_V1; + } + /*-----------------------------------------------------*\ + | AUDA0-E6K5-0101 - Second generation DRAM controller | + | (Geil Super Luce) | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUDA0-E6K5-0101") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V1; + + /*-------------------------------------------------*\ + | Check for Mode 14 support, only known to exist on | + | modules where the DRAM 3 zone ID exists | + \*-------------------------------------------------*/ + for(std::size_t cfg_zone_idx = 0; cfg_zone_idx < ENE_NUM_ZONES; cfg_zone_idx++) + { + if(config_table[channel_cfg + cfg_zone_idx] == (unsigned char)ENE_LED_CHANNEL_DRAM_3) + { + supports_mode_14 = true; + break; + } + } + } + /*-----------------------------------------------------*\ + | AUMA0-E6K5-0106 - Second generation motherboard | + | controller | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E6K5-0106") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V2; + } + /*-----------------------------------------------------*\ + | AUMA0-E6K5-0105 - Second generation motherboard | + | controller | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E6K5-0105") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V2; + } + /*-----------------------------------------------------*\ + | AUMA0-E6K5-0104 - Second generation motherboard | + | controller | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E6K5-0104") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V2; + } + /*-----------------------------------------------------*\ + | AUMA0-E8K4-0101 - First generation motherboard | + | controller | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E8K4-0101") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT; + effect_reg = ENE_REG_COLORS_EFFECT; + channel_cfg = ENE_CONFIG_CHANNEL_V1; + } + /*-----------------------------------------------------*\ + | AUMA0-E6K5-0107 - Second generation GPU controller | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E6K5-0107") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V2; + + /*-------------------------------------------------*\ + | Read LED count from configuration table | + \*-------------------------------------------------*/ + led_count = config_table[ENE_CONFIG_LED_COUNT_0107]; + } + /*-----------------------------------------------------*\ + | AUMA0-E6K5-1110 - Third generation GPU controller? | + | found an ASUS ROG Strix 4080 OC, seems to be equal to | + | AUMA0-E6K5-0107 | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E6K5-1110") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V2; + + /*-------------------------------------------------*\ + | Read LED count from configuration table | + \*-------------------------------------------------*/ + led_count = config_table[ENE_CONFIG_LED_COUNT_1110]; + } + /*-----------------------------------------------------*\ + | AUMA0-E6K5-1111 - Fourth generation GPU controller? | + | found on ASUS ROG Strix 4090 OC EVA-02 Edition, seems | + | to be equal to AUMA0-E6K5-0107 | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E6K5-1111") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V2; + + /*-------------------------------------------------*\ + | Read LED count from configuration table | + \*-------------------------------------------------*/ + led_count = config_table[ENE_CONFIG_LED_COUNT_0107]; + } + /*-----------------------------------------------------*\ + | AUMA0-E6K5-1107 - Second generation GPU controller | + | Found on ASUS TUF 4070 TI OC, seems to be equal to | + | AUMA0-E6K5-0107 | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E6K5-1107") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V2; + + /*-------------------------------------------------*\ + | Read LED count from configuration table | + \*-------------------------------------------------*/ + led_count = config_table[ENE_CONFIG_LED_COUNT_0107]; + } + /*-----------------------------------------------------*\ + | AUMA0-E6K5-0008 | + | Found on ASUS STRIX 4070 Super OC | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E6K5-0008") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V1; + + /*-------------------------------------------------*\ + | Read LED count from configuration table | + \*-------------------------------------------------*/ + led_count = config_table[ENE_CONFIG_LED_COUNT_0107]; + } + /*-----------------------------------------------------*\ + | AUMA0-E6K5-1113 | + | Found on ASUS ASTRAL 5080 OC | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E6K5-1113") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V2; + + /*-------------------------------------------------*\ + | Read LED count from configuration table | + \*-------------------------------------------------*/ + led_count = config_table[ENE_CONFIG_LED_COUNT_0107]; + } + /*-----------------------------------------------------*\ + | AUMA0-E6K5-1114 | + | Found on ASUS ROG MATRIX PLATINUM 5090 | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "AUMA0-E6K5-1114") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V2; + + /*-------------------------------------------------*\ + | Read LED count from configuration table | + \*-------------------------------------------------*/ + led_count = config_table[ENE_CONFIG_LED_COUNT_0107]; + } + /*-----------------------------------------------------*\ + | ROG ARION - ASUS ROG Arion external SSD enclosure | + | This device does not support ENE read, so we fake the | + | device name string if the interface is ROG Arion type.| + | It uses second generation registers. | + \*-----------------------------------------------------*/ + else if(strcmp(device_version, "ROG STRIX ARION") == 0) + { + direct_reg = ENE_REG_COLORS_DIRECT_V2; + effect_reg = ENE_REG_COLORS_EFFECT_V2; + channel_cfg = ENE_CONFIG_CHANNEL_V2; + } + /*-----------------------------------------------------*\ + | Assume first generation controller if string does not | + | match | + \*-----------------------------------------------------*/ + else + { + direct_reg = ENE_REG_COLORS_DIRECT; + effect_reg = ENE_REG_COLORS_EFFECT; + channel_cfg = ENE_CONFIG_CHANNEL_V1; + } +} + +ENESMBusController::~ENESMBusController() +{ + delete interface; +} + +std::string ENESMBusController::GetLocation() +{ + std::string return_string = interface->GetLocation(); + + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + + return(return_string); +} + +std::string ENESMBusController::GetName() +{ + return(name); +} + +std::string ENESMBusController::GetVersion() +{ + return(device_version); +} + +device_type ENESMBusController::GetType() +{ + return(type); +} + +const char * ENESMBusController::GetChannelName(unsigned int cfg_zone) +{ + LOG_TRACE("[%s] Config table for zone %02d: %02d", device_version, cfg_zone, config_table[channel_cfg + cfg_zone]); + + if(interface->GetInterfaceType() == ENE_INTERFACE_TYPE_ROG_ARION) + { + return(ene_channels[10]); + } + else + { + switch(config_table[channel_cfg + cfg_zone]) + { + case (unsigned char)ENE_LED_CHANNEL_AUDIO: + return(ene_channels[0]); + break; + + case (unsigned char)ENE_LED_CHANNEL_BACKPLATE: + return(ene_channels[1]); + break; + + case (unsigned char)ENE_LED_CHANNEL_BACK_IO: + return(ene_channels[2]); + break; + + case (unsigned char)ENE_LED_CHANNEL_CENTER: + return(ene_channels[3]); + break; + + case (unsigned char)ENE_LED_CHANNEL_CENTER_START: + return(ene_channels[4]); + break; + + case (unsigned char)ENE_LED_CHANNEL_DRAM: + case (unsigned char)ENE_LED_CHANNEL_DRAM_2: + case (unsigned char)ENE_LED_CHANNEL_DRAM_3: + return(ene_channels[5]); + break; + + case (unsigned char)ENE_LED_CHANNEL_PCIE: + return(ene_channels[6]); + break; + + case (unsigned char)ENE_LED_CHANNEL_RGB_HEADER: + return(ene_channels[7]); + break; + + case (unsigned char)ENE_LED_CHANNEL_RGB_HEADER_2: + return(ene_channels[8]); + break; + + case (unsigned char)ENE_LED_CHANNEL_RGB_HEADER_3: + return(ene_channels[9]); + break; + + default: + return(ene_channels[11]); + break; + } + } +} + +unsigned int ENESMBusController::GetLEDCount(unsigned int cfg_zone) +{ + LOG_TRACE("[%s] LED Count for zone %02d: %02d", device_version, cfg_zone, config_table[0x03 + cfg_zone]); + return(config_table[0x03 + cfg_zone]); +} + +unsigned char ENESMBusController::GetLEDRed(unsigned int led) +{ + return(ENERegisterRead(direct_reg + ( 3 * led ))); +} + +unsigned char ENESMBusController::GetLEDGreen(unsigned int led) +{ + return(ENERegisterRead(direct_reg + ( 3 * led ) + 2)); +} + +unsigned char ENESMBusController::GetLEDBlue(unsigned int led) +{ + return(ENERegisterRead(direct_reg + ( 3 * led ) + 1)); +} + +unsigned char ENESMBusController::GetLEDRedEffect(unsigned int led) +{ + return(ENERegisterRead(effect_reg + ( 3 * led ))); +} + +unsigned char ENESMBusController::GetLEDGreenEffect(unsigned int led) +{ + return(ENERegisterRead(effect_reg + ( 3 * led ) + 2)); +} + +unsigned char ENESMBusController::GetLEDBlueEffect(unsigned int led) +{ + return(ENERegisterRead(effect_reg + ( 3 * led ) + 1)); +} + +void ENESMBusController::SaveMode() +{ + ENERegisterWrite(ENE_REG_APPLY, ENE_SAVE_VAL); +} + +void ENESMBusController::SetAllColorsDirect(RGBColor* colors) +{ + unsigned char* color_buf = new unsigned char[led_count * 3]; + unsigned int bytes_sent = 0; + + for(unsigned int i = 0; i < (led_count * 3); i += 3) + { + color_buf[i + 0] = RGBGetRValue(colors[i / 3]); + color_buf[i + 1] = RGBGetBValue(colors[i / 3]); + color_buf[i + 2] = RGBGetGValue(colors[i / 3]); + } + + while(bytes_sent < (led_count * 3)) + { + int bytes_to_send = (led_count * 3) - bytes_sent; + + if(bytes_to_send > interface->GetMaxBlock()) + { + bytes_to_send = interface->GetMaxBlock(); + } + + ENERegisterWriteBlock(direct_reg + bytes_sent, &color_buf[bytes_sent], bytes_to_send); + + bytes_sent += bytes_to_send; + } + + delete[] color_buf; +} + +void ENESMBusController::SetAllColorsEffect(RGBColor* colors) +{ + unsigned char* color_buf = new unsigned char[led_count * 3]; + unsigned int bytes_sent = 0; + + for(unsigned int i = 0; i < (led_count * 3); i += 3) + { + color_buf[i + 0] = RGBGetRValue(colors[i / 3]); + color_buf[i + 1] = RGBGetBValue(colors[i / 3]); + color_buf[i + 2] = RGBGetGValue(colors[i / 3]); + } + + while(bytes_sent < (led_count * 3)) + { + int bytes_to_send = (led_count * 3) - bytes_sent; + + if(bytes_to_send > interface->GetMaxBlock()) + { + bytes_to_send = interface->GetMaxBlock(); + } + + ENERegisterWriteBlock(effect_reg + bytes_sent, &color_buf[bytes_sent], bytes_to_send); + + bytes_sent += bytes_to_send; + } + + ENERegisterWrite(ENE_REG_APPLY, ENE_APPLY_VAL); + + delete[] color_buf; +} + + +void ENESMBusController::SetDirect(unsigned char direct) +{ + ENERegisterWrite(ENE_REG_DIRECT, direct); + ENERegisterWrite(ENE_REG_APPLY, ENE_APPLY_VAL); +} + +void ENESMBusController::SetLEDColorDirect(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char colors[3] = { red, blue, green }; + + ENERegisterWriteBlock(direct_reg + ( 3 * led ), colors, 3); +} + +void ENESMBusController::SetLEDColorEffect(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char colors[3] = { red, blue, green }; + + ENERegisterWriteBlock(effect_reg + (3 * led), colors, 3); + + ENERegisterWrite(ENE_REG_APPLY, ENE_APPLY_VAL); +} + +void ENESMBusController::SetMode(unsigned char mode, unsigned char speed, unsigned char direction) +{ + ENERegisterWrite(ENE_REG_MODE, mode); + ENERegisterWrite(ENE_REG_SPEED, speed); + ENERegisterWrite(ENE_REG_DIRECTION, direction); + ENERegisterWrite(ENE_REG_APPLY, ENE_APPLY_VAL); +} + +bool ENESMBusController::SupportsMode14() +{ + return(supports_mode_14); +} + +void ENESMBusController::UpdateDeviceName() +{ + for (int i = 0; i < 16; i++) + { + device_version[i] = ENERegisterRead(ENE_REG_DEVICE_NAME + i); + } +} + +unsigned char ENESMBusController::ENERegisterRead(ene_register reg) +{ + return(interface->ENERegisterRead(dev, reg)); +} + +void ENESMBusController::ENERegisterWrite(ene_register reg, unsigned char val) +{ + interface->ENERegisterWrite(dev, reg, val); +} + +void ENESMBusController::ENERegisterWriteBlock(ene_register reg, unsigned char * data, unsigned char sz) +{ + interface->ENERegisterWriteBlock(dev, reg, data, sz); +} diff --git a/Controllers/ENESMBusController/ENESMBusController.h b/Controllers/ENESMBusController/ENESMBusController.h new file mode 100644 index 0000000..eed6e04 --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusController.h @@ -0,0 +1,146 @@ +/*---------------------------------------------------------*\ +| ENESMBusController.h | +| | +| Driver for ENE SMBus devices | +| | +| Adam Honse (CalcProgrammer1) 19 Aug 2018 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "ENESMBusInterface.h" +#include "RGBController.h" + +#define ENE_APPLY_VAL 0x01 /* Value for Apply Changes Register */ +#define ENE_SAVE_VAL 0xAA /* Value for Save Changes */ +#define ENE_NUM_ZONES 8 /* Number of ENE config table zones */ + +enum +{ + ENE_REG_DEVICE_NAME = 0x1000, /* Device String 16 bytes */ + ENE_REG_MICRON_CHECK = 0x1030, /* If "Micron" appears here, skip */ + ENE_REG_CONFIG_TABLE = 0x1C00, /* Start of LED configuration bytes */ + ENE_REG_COLORS_DIRECT = 0x8000, /* Colors for Direct Mode 15 bytes */ + ENE_REG_COLORS_EFFECT = 0x8010, /* Colors for Internal Effects 15 bytes */ + ENE_REG_DIRECT = 0x8020, /* "Direct Access" Selection Register */ + ENE_REG_MODE = 0x8021, /* Mode Selection Register */ + ENE_REG_SPEED = 0x8022, /* Speed Control Register */ + ENE_REG_DIRECTION = 0x8023, /* Direction Control Register */ + ENE_REG_APPLY = 0x80A0, /* Apply Changes Register */ + ENE_REG_SLOT_INDEX = 0x80F8, /* Slot Index Register (RAM only) */ + ENE_REG_I2C_ADDRESS = 0x80F9, /* I2C Address Register (RAM only) */ + ENE_REG_COLORS_DIRECT_V2 = 0x8100, /* Direct Colors (v2) 30 bytes */ + ENE_REG_COLORS_EFFECT_V2 = 0x8160, /* Internal Colors (v2) 30 bytes */ +}; + +enum +{ + ENE_MODE_OFF = 0, /* OFF mode */ + ENE_MODE_STATIC = 1, /* Static color mode */ + ENE_MODE_BREATHING = 2, /* Breathing effect mode */ + ENE_MODE_FLASHING = 3, /* Flashing effect mode */ + ENE_MODE_SPECTRUM_CYCLE = 4, /* Spectrum Cycle mode */ + ENE_MODE_RAINBOW = 5, /* Rainbow effect mode */ + ENE_MODE_SPECTRUM_CYCLE_BREATHING = 6, /* Rainbow Breathing effect mode */ + ENE_MODE_CHASE_FADE = 7, /* Chase with Fade effect mode */ + ENE_MODE_SPECTRUM_CYCLE_CHASE_FADE = 8, /* Chase with Fade, Rainbow effect mode */ + ENE_MODE_CHASE = 9, /* Chase effect mode */ + ENE_MODE_SPECTRUM_CYCLE_CHASE = 10, /* Chase with Rainbow effect mode */ + ENE_MODE_SPECTRUM_CYCLE_WAVE = 11, /* Wave effect mode */ + ENE_MODE_CHASE_RAINBOW_PULSE = 12, /* Chase with Rainbow Pulse effect mode*/ + ENE_MODE_RANDOM_FLICKER = 13, /* Random flicker effect mode */ + ENE_MODE_DOUBLE_FADE = 14, /* Rainbow fade to dual color */ + ENE_NUMBER_MODES /* Number of Aura modes */ +}; + +enum +{ + ENE_SPEED_SLOWEST = 0x04, /* Slowest effect speed */ + ENE_SPEED_SLOW = 0x03, /* Slow effect speed */ + ENE_SPEED_NORMAL = 0x02, /* Normal effect speed */ + ENE_SPEED_FAST = 0x01, /* Fast effect speed */ + ENE_SPEED_FASTEST = 0x00, /* Fastest effect speed */ +}; + +enum +{ + ENE_DIRECTION_FORWARD = 0x0, /* Forward effect direction */ + ENE_DIRECTION_REVERSE = 0x1, /* Reverse effect direction */ +}; + +enum +{ + ENE_LED_CHANNEL_DRAM_2 = 0x05, /* DRAM LED channel */ + ENE_LED_CHANNEL_DRAM_3 = 0x0E, /* DRAM LED channel */ + ENE_LED_CHANNEL_CENTER_START = 0x82, /* Center zone first LED channel */ + ENE_LED_CHANNEL_CENTER = 0x83, /* Center zone LED channel */ + ENE_LED_CHANNEL_AUDIO = 0x84, /* Audio zone LED channel */ + ENE_LED_CHANNEL_BACK_IO = 0x85, /* Back I/O zone LED channel */ + ENE_LED_CHANNEL_RGB_HEADER = 0x86, /* RGB Header LED channel */ + ENE_LED_CHANNEL_RGB_HEADER_2 = 0x87, /* RGB Header 2 LED channel */ + ENE_LED_CHANNEL_BACKPLATE = 0x88, /* Backplate zone LED channel */ + ENE_LED_CHANNEL_DRAM = 0x8A, /* DRAM LED channel */ + ENE_LED_CHANNEL_PCIE = 0x8B, /* PCIe zone LED channel */ + ENE_LED_CHANNEL_RGB_HEADER_3 = 0x91, /* RGB Header 3 LED channel */ +}; + +enum +{ + ENE_CONFIG_LED_COUNT = 0x02, /* LED Count configuration offset */ + ENE_CONFIG_LED_COUNT_0107 = 0x03, /* LED Count configuration offset */ + ENE_CONFIG_LED_COUNT_1110 = 0x03, /* LED Count configuration offset */ + ENE_CONFIG_CHANNEL_V1 = 0x13, /* LED Channel configuration offset */ + ENE_CONFIG_CHANNEL_V2 = 0x1B, /* LED Channel V2 configuration offset */ +}; + +class ENESMBusController +{ +public: + ENESMBusController(ENESMBusInterface* interface, ene_dev_id dev, std::string dev_name, device_type dev_type); + ~ENESMBusController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetVersion(); + device_type GetType(); + + const char* GetChannelName(unsigned int cfg_zone); + unsigned int GetLEDCount(unsigned int cfg_zone); + unsigned char GetLEDRed(unsigned int led); + unsigned char GetLEDGreen(unsigned int led); + unsigned char GetLEDBlue(unsigned int led); + unsigned char GetLEDRedEffect(unsigned int led); + unsigned char GetLEDGreenEffect(unsigned int led); + unsigned char GetLEDBlueEffect(unsigned int led); + void SaveMode(); + void SetAllColorsDirect(RGBColor* colors); + void SetAllColorsEffect(RGBColor* colors); + void SetDirect(unsigned char direct); + void SetLEDColorDirect(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void SetLEDColorEffect(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void SetMode(unsigned char mode, unsigned char speed, unsigned char direction); + bool SupportsMode14(); + + void UpdateDeviceName(); + + unsigned char ENERegisterRead(ene_register reg); + void ENERegisterWrite(ene_register reg, unsigned char val); + void ENERegisterWriteBlock(ene_register reg, unsigned char * data, unsigned char sz); + +private: + char device_version[16]; + unsigned char config_table[64]; + unsigned int led_count; + ene_register direct_reg; + ene_register effect_reg; + unsigned char channel_cfg; + ENESMBusInterface* interface; + ene_dev_id dev; + bool supports_mode_14; + std::string name; + device_type type; +}; diff --git a/Controllers/ENESMBusController/ENESMBusControllerDetect.cpp b/Controllers/ENESMBusController/ENESMBusControllerDetect.cpp new file mode 100644 index 0000000..a13a290 --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusControllerDetect.cpp @@ -0,0 +1,510 @@ +/*---------------------------------------------------------*\ +| ENESMBusControllerDetect.cpp | +| | +| Detector for ENE SMBus devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "ENESMBusController.h" +#include "ENESMBusInterface_i2c_smbus.h" +#include "LogManager.h" +#include "RGBController.h" +#include "RGBController_ENESMBus.h" +#include "i2c_smbus.h" +#include "pci_ids.h" +#include "dmiinfo.h" + +#define DETECTOR_NAME "ENE (ASUS Aura) SMBus Controller" +#define VENDOR_NAME "ASUS" //This should match the Vendor name from DMI + +using namespace std::chrono_literals; + +/*----------------------------------------------------------------------*\ +| Windows defines "interface" for some reason. Work around this | +\*----------------------------------------------------------------------*/ +#ifdef interface +#undef interface +#endif + +/*---------------------------------------------------------*\ +| This list contains the available SMBus addresses for | +| mapping ENE RAM | +\*---------------------------------------------------------*/ +#define ENE_RAM_ADDRESS_COUNT (sizeof(ene_ram_addresses) / sizeof(ene_ram_addresses[0])) + +static const unsigned char ene_ram_addresses[] = +{ + 0x70, + 0x71, + 0x72, + 0x73, + 0x74, + 0x75, + 0x76, + 0x77, + 0x4F, + 0x66, + 0x67, + 0x39, + 0x3A, + 0x3B, + 0x3C, + 0x3D +}; + +/*---------------------------------------------------------*\ +| This list contains the available SMBus addresses for | +| mapping Aura motherboards | +\*---------------------------------------------------------*/ +#define AURA_MOBO_ADDRESS_COUNT (sizeof(aura_mobo_addresses) / sizeof(aura_mobo_addresses[0])) + +static const unsigned char aura_mobo_addresses[] = +{ + 0x40, + 0x4E, + 0x4F +}; + +/******************************************************************************************\ +* * +* ENERegisterRead * +* * +* A standalone version of the ENESMBusController::ENERegisterRead function for * +* access to ENE devices without instancing the ENESMBusController class or reading * +* the config table from the device. * +* * +\******************************************************************************************/ + +static unsigned char ENERegisterRead(i2c_smbus_interface* bus, ene_dev_id dev, ene_register reg) +{ + //Write ENE register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Read ENE value + return(bus->i2c_smbus_read_byte_data(dev, 0x81)); +} + +/******************************************************************************************\ +* * +* ENERegisterWrite * +* * +* A standalone version of the ENESMBusController::ENERegisterWrite function for * +* access to ENE devices without instancing the ENESMBusController class or reading * +* the config table from the device. * +* * +\******************************************************************************************/ + +static void ENERegisterWrite(i2c_smbus_interface* bus, ene_dev_id dev, ene_register reg, unsigned char val) +{ + //Write ENE register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write ENE value + bus->i2c_smbus_write_byte_data(dev, 0x01, val); +} + +/******************************************************************************************\ +* * +* TestForENESMBusController * +* * +* Tests the given address to see if an ENE controller exists there. First does a * +* byte read to test for a response, and if so does a simple read at 0xA0 to test * +* for incrementing values 0...F which was observed at this location during data dump * +* * +* Also tests for the string "Micron" in the ENE register space. Crucial (Micron) * +* DRAM modules use an ENE controller with custom, incompatible firmware and must * +* be excluded from this controller. * +* * +\******************************************************************************************/ + +bool TestForENESMBusController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + + LOG_DEBUG("[ENE SMBus] looking for devices at 0x%02X...", address); + + int res = bus->i2c_smbus_read_byte(address); + + if(res < 0) + { + res = bus->i2c_smbus_read_byte_data(address, 0x00); + } + + if(res >= 0) + { + pass = true; + + LOG_DEBUG("[ENE SMBus] Detected an I2C device at address %02X, testing register range", address); + + for (int i = 0xA0; i < 0xB0; i++) + { + res = bus->i2c_smbus_read_byte_data(address, i); + + if (res != (i - 0xA0)) + { + LOG_VERBOSE("[ENE SMBus] Detection failed testing register %02X. Expected %02X, got %02X.", i, (i - 0xA0), res); + + pass = false; + break; + } + } + + if(pass) + { + LOG_DEBUG("[ENE SMBus] Checking for Micron string"); + + char buf[16]; + for(int i = 0; i < 16; i++) + { + buf[i] = ENERegisterRead(bus, address, ENE_REG_MICRON_CHECK + i); + } + + if(strcmp(buf, "Micron") == 0) + { + LOG_DEBUG("[ENE SMBus] Device %02X is a Micron device, skipping", address); + pass = false; + } + else + { + LOG_VERBOSE("[ENE SMBus] Detection successful, address %02X", address); + } + } + } + + return(pass); + +} /* TestForENESMBusController() */ + +/******************************************************************************************\ +* * +* DetectENESMBusDRAMControllers * +* * +* Detects ENE SMBus controllers on DRAM devices * +* * +* bus - pointer to i2c_smbus_interface where device is connected * +* dev - I2C address of device * +* * +\******************************************************************************************/ + +void DetectENESMBusDRAMControllers(std::vector &busses) +{ + for (unsigned int bus = 0; bus < busses.size(); bus++) + { + int address_list_idx = -1; + + IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + LOG_DEBUG("[ENE SMBus DRAM] Remapping ENE SMBus RAM modules on 0x77"); + + for (unsigned int slot = 0; slot < 8; slot++) + { + int res = busses[bus]->i2c_smbus_read_byte(0x77); + + if(res < 0) + { + LOG_DEBUG("[ENE SMBus DRAM] No device detected at 0x77, aborting remap"); + + break; + } + + do + { + address_list_idx++; + + if(address_list_idx < (int)ENE_RAM_ADDRESS_COUNT) + { + LOG_DEBUG("[ENE SMBus DRAM] Testing address %02X to see if there is a device there", ene_ram_addresses[address_list_idx]); + + res = busses[bus]->i2c_smbus_read_byte(ene_ram_addresses[address_list_idx]); + } + else + { + break; + } + } while (res >= 0); + + if(address_list_idx < (int)ENE_RAM_ADDRESS_COUNT) + { + LOG_DEBUG("[ENE SMBus DRAM] Remapping slot %d to address %02X", slot, ene_ram_addresses[address_list_idx]); + + ENERegisterWrite(busses[bus], 0x77, ENE_REG_SLOT_INDEX, slot); + ENERegisterWrite(busses[bus], 0x77, ENE_REG_I2C_ADDRESS, (ene_ram_addresses[address_list_idx] << 1)); + } + } + + // Add ENE controllers at their remapped addresses + for (unsigned int address_list_idx = 0; address_list_idx < ENE_RAM_ADDRESS_COUNT; address_list_idx++) + { + if (TestForENESMBusController(busses[bus], ene_ram_addresses[address_list_idx])) + { + ENESMBusInterface_i2c_smbus* interface = new ENESMBusInterface_i2c_smbus(busses[bus]); + ENESMBusController* controller = new ENESMBusController(interface, ene_ram_addresses[address_list_idx], "ENE DRAM", DEVICE_TYPE_DRAM); + RGBController_ENESMBus* rgb_controller = new RGBController_ENESMBus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + + std::this_thread::sleep_for(1ms); + } + } + } +} /* DetectENESMBusDRAMControllers() */ + +/******************************************************************************************\ +* * +* DetectENESMBusMotherboardControllers * +* * +* Detects ENE (ASUS Aura) SMBus controllers on ASUS motherboard devices * +* * +* bus - pointer to i2c_smbus_interface where Aura device is connected * +* dev - I2C address of Aura device * +* * +\******************************************************************************************/ + +void DetectENESMBusMotherboardControllers(std::vector &busses) +{ + for (unsigned int bus = 0; bus < busses.size(); bus++) + { + // Add ENE (ASUS Aura) motherboard controllers + IF_MOBO_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + if(busses[bus]->pci_subsystem_vendor == ASUS_SUB_VEN || busses[bus]->pci_subsystem_vendor == 0 || busses[bus]->pci_subsystem_vendor == 0xFFFF) + { + for (unsigned int address_list_idx = 0; address_list_idx < AURA_MOBO_ADDRESS_COUNT; address_list_idx++) + { + LOG_DEBUG(SMBUS_CHECK_DEVICE_MESSAGE_EN, DETECTOR_NAME, bus, VENDOR_NAME, aura_mobo_addresses[address_list_idx]); + + if (TestForENESMBusController(busses[bus], aura_mobo_addresses[address_list_idx])) + { + DMIInfo dmi; + + ENESMBusInterface_i2c_smbus* interface = new ENESMBusInterface_i2c_smbus(busses[bus]); + ENESMBusController* controller = new ENESMBusController(interface, aura_mobo_addresses[address_list_idx], "ASUS " + dmi.getMainboard(), DEVICE_TYPE_MOTHERBOARD); + RGBController_ENESMBus* rgb_controller = new RGBController_ENESMBus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + + std::this_thread::sleep_for(1ms); + } + } + else + { + LOG_DEBUG(SMBUS_CHECK_DEVICE_FAILURE_EN, DETECTOR_NAME, bus, VENDOR_NAME); + } + } + } +} /* DetectENESMBusMotherboardControllers() */ + +/******************************************************************************************\ +* * +* DetectENESMBusGPUControllers * +* * +* Detects ENE (ASUS Aura) SMBus controllers on ASUS GPU devices * +* * +\******************************************************************************************/ + +#define GPU_CHECK_DEVICE_MESSAGE_EN "[%s] Bus %02d is a GPU and the subvendor matches the one for %s, looking for a device at 0x%02X" + +void DetectENESMBusGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(TestForENESMBusController(bus, i2c_addr)) + { + ENESMBusInterface_i2c_smbus* interface = new ENESMBusInterface_i2c_smbus(bus); + ENESMBusController* controller = new ENESMBusController(interface, i2c_addr, name, DEVICE_TYPE_GPU); + RGBController_ENESMBus* rgb_controller = new RGBController_ENESMBus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + LOG_DEBUG("[ENE SMBus ASUS GPU] Testing for controller at %d failed", i2c_addr); + } +} /* DetectENESMBusGPUControllers() */ + +REGISTER_I2C_DETECTOR("ENE SMBus DRAM", DetectENESMBusDRAMControllers); +REGISTER_I2C_DETECTOR("ASUS Aura SMBus Motherboard", DetectENESMBusMotherboardControllers); + +/*-----------------------------------------*\ +| Nvidia GPUs | +\*-----------------------------------------*/ + +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3050 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3050_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX3050_8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS KO GeForce RTX 3060 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, ASUS_SUB_VEN, ASUS_KO_RTX_3060_OC_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3060_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA104_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3060_O12G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3060_O12G_LHR_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060_12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060_O12G_LHR_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS KO GeForce RTX 3060 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, ASUS_SUB_VEN, ASUS_KO_RTX_3060_O12G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS KO GeForce RTX 3060 Ti V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_KO_RTX3060TI_O8G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 Ti OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_GDDR6X_DEV,ASUS_SUB_VEN, ASUS_TUF_RTX_3060TI_O8G, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS KO GeForce RTX 3060 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, ASUS_SUB_VEN, ASUS_KO_RTX3060TI_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS KO GeForce RTX 3060 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, ASUS_SUB_VEN, ASUS_KO_RTX3060TI_08G_GAMING_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3060TI_O8G_OC, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Ti OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060TI_O8G_OC, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3060 Ti OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3060TI_O8G_OC_V2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Ti V2 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060TI_O8G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3060 Ti V2 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3060TI_O8G_V2_2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_OC, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_O8G_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 V2 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_8G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_O8G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 V2 White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070_O8G_V2_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3070 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3070_8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3070_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS KO GeForce RTX 3070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, ASUS_SUB_VEN, ASUS_KO_RTX_3070_O8G_GAMING_V1, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS KO GeForce RTX 3070 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, ASUS_SUB_VEN, ASUS_KO_RTX_3070_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS KO GeForce RTX 3070 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, ASUS_SUB_VEN, ASUS_KO_RTX_3070_O8G_GAMING_V2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3070 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3070_O8G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3070TI_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3070 Ti V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3070TI_O8G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3070 Ti V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_GA102_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3070TI_O8G_V2_GAMING_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3070 Ti V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_GA102_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3070TI_O8G_V2_GAMING_3, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3070TI_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3080 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080_10G_GAMING_PD, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3080 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080_10G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG GeForce RTX 3080 GUNDAM EDITION", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080_10G_GUNDAM_EDITION, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3080 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080_O10G_OC, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3080 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080_10G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3080 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080_O10G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3080 White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080_O10G_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3080 V2 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080_10G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3080 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080_O10G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3080 V2 White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080_O10G_V2_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3080 V2 Gaming OC ", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080_O10G_V2_GAMING_8822, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3080 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080_O10G_V2_GAMING_882B, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3080 V2 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080_O10G_V2_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3080 12G Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080_12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3080 12G Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3080 12G", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080_12G, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3080 12G OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080_O12G_OC, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3080 12G OC EVA EDITION", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080_O12G_EVA, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3090 OC EVA EDITION", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3090_O24G_EVA, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3090 GUNDAM EDITION", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3090_GUNDAM_EDITION, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3080 Ti Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080TI_12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3080 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3080TI_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG GeForce RTX STRIX 3080 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080TI_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX LC GeForce RTX 3080 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3080TI_O12G_GAMING_LC, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3090 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3090_24G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3090 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3090_24G_GAMING_V2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3090_O24G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 3090 Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_3090_O24G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3090_O24G, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3090_O24G_OC, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3090 Ti Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3090TI_24G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 3090 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_3090TI_O24G_OC_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX LC GeForce RTX 3090 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_LC_RTX_3090TI_O24G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4060 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4060TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4060TI_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4060 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4060_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4060_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4060 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4060TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4060TI_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070_12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070_O12G_GAMING_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070_O12G_GAMING_3, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 SUPER Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070S_12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4070_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4070 SUPER Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4070S_12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4070 SUPER Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4070S_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Ti Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070TI_12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Ti Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070TI_12G_GAMING_88DD, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070TI_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070TI_O12G_GAMING_88DC, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Ti Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070TI_O12G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Ti SUPER Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070TI_SUPER_16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Ti SUPER Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070TI_SUPER_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4070 Ti SUPER Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4070TI_SUPER_O16G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4070 Ti Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4070TI_12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4070TI_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4070TI_O12G_GAMING_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4070 Ti SUPER Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4070TI_SUPER_16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4070 Ti SUPER Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4070TI_SUPER_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4080 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4080_16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4080 Gaming White", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4080_16G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4080 Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4080_O16G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4080 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4080_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4080 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4080_O16G_GAMING_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4080 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4080_16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4080 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4080_16G_GAMING_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4080 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4080_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4080 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4080_O16G_OC_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4080 SUPER Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4080S_16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4080 SUPER OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4080S_016G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4080 SUPER White", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4080S_16G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4080 SUPER White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4080S_016G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4080 SUPER Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4080S_O16G_OC_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4080 SUPER Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4080S_O16G_OC_GAMING_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4090_O24G_OC_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4090 Gaming OG OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4090_O24G_OG_OC_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4090_O24G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4090_O24G_GAMING_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 4090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_4090_O24G_GAMING_3, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX LC GeForce RTX 4090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_LC_RTX_4090_O24G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_24G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_O24G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_O24G_GAMING_213S, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_24G_GAMING_88F0, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 OC EVA-02", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_024G_EVA_02, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_24G_GAMING_8932, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_24G_GAMING_8933, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_24G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_24G_GAMING_WHITE_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_O24G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 4090 Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_4090_O24G_GAMING_WHITE_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG MATRIX PLATINUM GeForce RTX 4090", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ASUS_SUB_VEN, ASUS_ROG_MATRIX_PLATINUM_RTX_4090_24G, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5060 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5060_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming BTF White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING_BTF_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5070 Ti Gaming White OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5070TI_O16G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX GeForce RTX 5070 Ti Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RTX_5070TI_O16G_GAMING_OC, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5080 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5080_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5090 Gaming OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5090_O32G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF GeForce RTX 5090 Gaming", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_TUF_RTX_5090_32G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 OC WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_O16G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5080 WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5080_16G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_2, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC BTF", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_BTF, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL GeForce RTX 5090 OC WHITE", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_RTX_5090_O32G_GAMING_WHITE, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL LC GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_LC_RTX_5090_O32G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG ASTRAL LC OC GeForce RTX 5090 OC", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_ASTRAL_LC_OC_RTX_5090_O32G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG MATRIX PLATINUM GeForce RTX 5090", DetectENESMBusGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ASUS_SUB_VEN, ASUS_ROG_MATRIX_PLATINUM_RTX_5090_P32G, 0x67); + +/*-----------------------------------------*\ +| AMD GPUs | +\*-----------------------------------------*/ + +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 6600 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI23_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RX_6600XT_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 6650 XT Gaming", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI23_DEV1, ASUS_SUB_VEN, ASUS_ROG_STRIX_RX_6650XT_O8G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 6700 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_6700XT_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 6700 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RX_6700XT_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 6750 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, ASUS_SUB_VEN, ASUS_ROG_STRIX_RX_6750XT_O12G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 6800 Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, ASUS_SUB_VEN, ASUS_RX6800_TUF_GAMING_OC, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX Radeon RX 6800 Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, ASUS_SUB_VEN, ASUS_ROG_STRIX_RX_6800_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 6800 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, ASUS_SUB_VEN, ASUS_TUF_RX_6800XT_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX LC Radeon RX 6800 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, ASUS_SUB_VEN, ASUS_ROG_STRIX_LC_RX6800XT_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX LC Radeon RX 6900 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, ASUS_SUB_VEN, ASUS_ROG_STRIX_LC_RX6900XT_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX LC Radeon RX 6900 XT Gaming OC TOP", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV2, ASUS_SUB_VEN, ASUS_ROG_STRIX_LC_RX6900XT_O16G_GAMING_TOP, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 6900 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, ASUS_SUB_VEN, ASUS_TUF_RX_6900XT_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 6900 XT T16G Gaming", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV2, ASUS_SUB_VEN, ASUS_TUF_RX_6900XT_T16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 6950 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV3, ASUS_SUB_VEN, ASUS_TUF_RX_6950XT_016G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS ROG STRIX LC Radeon RX 6950 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV3, ASUS_SUB_VEN, ASUS_ROG_STRIX_LC_RX_6950XT_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7600 XT O16G Gaming", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI33_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7600XT_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7700 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7700XT_012G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7800 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7800XT_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7800 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7800XT_O16G_GAMING_0606, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7800 XT Gaming White OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7800XT_O16G_WHITE_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7900 GRE Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7900GRE_O16G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7900 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7900XT_020G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 7900 XTX Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_7900XTX_O24G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 9070 Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_9070_016G_GAMING, 0x67); +REGISTER_I2C_PCI_DETECTOR("ASUS TUF Radeon RX 9070 XT Gaming OC", DetectENESMBusGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, ASUS_SUB_VEN, ASUS_TUF_RX_9070XT_016G_GAMING, 0x67); diff --git a/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface.h b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface.h new file mode 100644 index 0000000..fc4a4b2 --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| ENESMBusInterface.h | +| | +| ENE SMBus interface | +| | +| Adam Honse (CalcProgrammer1) 21 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +typedef unsigned short ene_register; +typedef unsigned char ene_dev_id; +typedef unsigned int ene_interface_type; + +/*-----------------------------------------*\ +| Known interface types | +\*-----------------------------------------*/ +enum +{ + ENE_INTERFACE_TYPE_I2C_SMBUS, + ENE_INTERFACE_TYPE_SPECTRIX_S40G, + ENE_INTERFACE_TYPE_ROG_ARION, +}; + +class ENESMBusInterface +{ +public: + virtual ~ENESMBusInterface() = default; + + virtual ene_interface_type GetInterfaceType() = 0; + virtual std::string GetLocation() = 0; + virtual int GetMaxBlock() = 0; + virtual unsigned char ENERegisterRead(ene_dev_id dev, ene_register reg) = 0; + virtual void ENERegisterWrite(ene_dev_id dev, ene_register reg, unsigned char val) = 0; + virtual void ENERegisterWriteBlock(ene_dev_id dev, ene_register reg, unsigned char * data, unsigned char sz) = 0; +}; diff --git a/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_ROGArion.cpp b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_ROGArion.cpp new file mode 100644 index 0000000..f2c84ce --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_ROGArion.cpp @@ -0,0 +1,96 @@ +/*---------------------------------------------------------*\ +| ENESMBusInterface_ROGArion.cpp | +| | +| ENE SMBus interface for ASUS ROG Arion | +| | +| Adam Honse (CalcProgrammer1) 17 Sep 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "ENESMBusInterface_ROGArion.h" + +ENESMBusInterface_ROGArion::ENESMBusInterface_ROGArion(scsi_device* dev_handle, char* dev_path) +{ + scsi_dev = dev_handle; + path = dev_path; +} + +ENESMBusInterface_ROGArion::~ENESMBusInterface_ROGArion() +{ + +} + +ene_interface_type ENESMBusInterface_ROGArion::GetInterfaceType() +{ + return(ENE_INTERFACE_TYPE_ROG_ARION); +} + +std::string ENESMBusInterface_ROGArion::GetLocation() +{ + std::string str(path.begin(), path.end()); + return("SCSI: " + str); +} + +int ENESMBusInterface_ROGArion::GetMaxBlock() +{ + return(24); +} + +unsigned char ENESMBusInterface_ROGArion::ENERegisterRead(ene_dev_id /*dev*/, ene_register /*reg*/) +{ + /*-----------------------------------------------------------------------------*\ + | This interface does not support reading | + \*-----------------------------------------------------------------------------*/ + return( 0 ); +} + +void ENESMBusInterface_ROGArion::ENERegisterWrite(ene_dev_id /*dev*/, ene_register reg, unsigned char val) +{ + SendPacket(reg, &val, sizeof(unsigned char)); +} + +void ENESMBusInterface_ROGArion::ENERegisterWriteBlock(ene_dev_id /*dev*/, ene_register reg, unsigned char * data, unsigned char sz) +{ + SendPacket(reg, data, sz); +} + +void ENESMBusInterface_ROGArion::SendPacket + ( + ene_register reg, + unsigned char * packet, + unsigned char packet_sz + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold CDB | + \*-----------------------------------------------------------------------------*/ + unsigned char cdb[16] = {0}; + cdb[0] = 0xEC; + cdb[1] = 0x41; + cdb[2] = 0x53; + cdb[3] = ((reg >> 8) & 0x00FF); + cdb[4] = ( reg & 0x00FF ); + cdb[5] = 0x00; + cdb[6] = 0x00; + cdb[7] = 0x00; + cdb[8] = 0x00; + cdb[9] = 0x00; + cdb[10] = 0x00; + cdb[11] = 0x00; + cdb[12] = 0x00; + cdb[13] = packet_sz; + cdb[14] = 0x00; + cdb[15] = 0x00; + + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold sense data | + \*-----------------------------------------------------------------------------*/ + unsigned char sense[32] = {0}; + + /*-----------------------------------------------------------------------------*\ + | Write SCSI packet | + \*-----------------------------------------------------------------------------*/ + scsi_write(scsi_dev, packet, packet_sz, cdb, 16, sense, 32); +} diff --git a/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_ROGArion.h b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_ROGArion.h new file mode 100644 index 0000000..db20ce5 --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_ROGArion.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| ENESMBusInterface_ROGArion.h | +| | +| ENE SMBus interface for ASUS ROG Arion | +| | +| Adam Honse (CalcProgrammer1) 17 Sep 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "ENESMBusInterface.h" +#include "scsiapi.h" + +class ENESMBusInterface_ROGArion : public ENESMBusInterface +{ +public: + ENESMBusInterface_ROGArion(scsi_device* dev_handle, char* dev_path); + ~ENESMBusInterface_ROGArion(); + + ene_interface_type GetInterfaceType(); + std::string GetLocation(); + int GetMaxBlock(); + unsigned char ENERegisterRead(ene_dev_id dev, ene_register reg); + void ENERegisterWrite(ene_dev_id dev, ene_register reg, unsigned char val); + void ENERegisterWriteBlock(ene_dev_id dev, ene_register reg, unsigned char * data, unsigned char sz); + +private: + scsi_device* scsi_dev; + std::string path; + + void SendPacket + ( + ene_register reg, + unsigned char * packet, + unsigned char packet_sz + ); +}; diff --git a/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Linux.cpp b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Linux.cpp new file mode 100644 index 0000000..4d6f65b --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Linux.cpp @@ -0,0 +1,215 @@ +/*---------------------------------------------------------*\ +| ENESMBusInterface_SpectrixS40G_Linux.cpp | +| | +| ENE SMBus interface for XPG Spectrix S40G (Linux) | +| | +| Adam Honse (CalcProgrammer1) 21 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "ENESMBusInterface_SpectrixS40G_Linux.h" + +/*---------------------------------------------------------------------*\ +| Functions for submitting NVME admin passthrough command taken from | +| libnvme: https://github.com/linux-nvme/libnvme | +\*---------------------------------------------------------------------*/ + +#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_passthru_cmd) + +struct nvme_passthru_cmd +{ + uint8_t opcode; + uint8_t flags; + uint16_t rsvd1; + uint32_t nsid; + uint32_t cdw2; + uint32_t cdw3; + uint64_t metadata; + uint64_t addr; + uint32_t metadata_len; + uint32_t data_len; + uint32_t cdw10; + uint32_t cdw11; + uint32_t cdw12; + uint32_t cdw13; + uint32_t cdw14; + uint32_t cdw15; + uint32_t timeout_ms; + uint32_t result; +}; + +static int nvme_submit_passthru(int fd, unsigned long ioctl_cmd, + struct nvme_passthru_cmd *cmd, uint32_t *result) +{ + int err = ioctl(fd, ioctl_cmd, cmd); + + if (err >= 0 && result) + *result = cmd->result; + return err; +} + +static int nvme_passthru(int fd, unsigned long ioctl_cmd, uint8_t opcode, + uint8_t flags, uint16_t rsvd, uint32_t nsid, uint32_t cdw2, + uint32_t cdw3, uint32_t cdw10, uint32_t cdw11, uint32_t cdw12, + uint32_t cdw13, uint32_t cdw14, uint32_t cdw15, uint32_t data_len, + void *data, uint32_t metadata_len, void *metadata, + uint32_t timeout_ms, uint32_t *result) +{ + struct nvme_passthru_cmd cmd = { + .opcode = opcode, + .flags = flags, + .rsvd1 = rsvd, + .nsid = nsid, + .cdw2 = cdw2, + .cdw3 = cdw3, + .metadata = (uint64_t)(uintptr_t)metadata, + .addr = (uint64_t)(uintptr_t)data, + .metadata_len = metadata_len, + .data_len = data_len, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = cdw12, + .cdw13 = cdw13, + .cdw14 = cdw14, + .cdw15 = cdw15, + .timeout_ms = timeout_ms, + .result = 0, + }; + + return nvme_submit_passthru(fd, ioctl_cmd, &cmd, result); +} + +int nvme_admin_passthru(int fd, uint8_t opcode, uint8_t flags, uint16_t rsvd, + uint32_t nsid, uint32_t cdw2, uint32_t cdw3, uint32_t cdw10, + uint32_t cdw11, uint32_t cdw12, uint32_t cdw13, uint32_t cdw14, + uint32_t cdw15, uint32_t data_len, void *data, + uint32_t metadata_len, void *metadata, uint32_t timeout_ms, + uint32_t *result) +{ + return nvme_passthru(fd, NVME_IOCTL_ADMIN_CMD, opcode, flags, rsvd, + nsid, cdw2, cdw3, cdw10, cdw11, cdw12, cdw13, + cdw14, cdw15, data_len, data, metadata_len, + metadata, timeout_ms, result); +} + +/*---------------------------------------------------------------------*\ +| ENESMBusInterface_SpectrixS40G implementation | +\*---------------------------------------------------------------------*/ + +ENESMBusInterface_SpectrixS40G::ENESMBusInterface_SpectrixS40G(int fd, char* path) +{ + this->nvme_fd = fd; + this->path = path; +} + +ENESMBusInterface_SpectrixS40G::~ENESMBusInterface_SpectrixS40G() +{ + +} + +ene_interface_type ENESMBusInterface_SpectrixS40G::GetInterfaceType() +{ + return(ENE_INTERFACE_TYPE_SPECTRIX_S40G); +} + +std::string ENESMBusInterface_SpectrixS40G::GetLocation() +{ + return("NVMe: " + path); +} + +int ENESMBusInterface_SpectrixS40G::GetMaxBlock() +{ + return(24); +} + +unsigned char ENESMBusInterface_SpectrixS40G::ENERegisterRead(ene_dev_id dev, ene_register reg) +{ + struct nvme_passthru_cmd cfg; + + memset(&cfg, 0, sizeof(nvme_passthru_cmd)); + + unsigned short corrected_reg = ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF); + + cfg.opcode = 0xFA; + cfg.cdw12 = (corrected_reg << 16) | (dev << 1); + cfg.cdw13 = 0x81100001; + cfg.data_len = 1; + + unsigned char data[1]; + unsigned char metadata[1]; + unsigned int result; + + /*-----------------------------------------------------------------------------*\ + | Send the command to the device | + \*-----------------------------------------------------------------------------*/ + nvme_admin_passthru(nvme_fd, cfg.opcode, cfg.flags, cfg.rsvd1, + cfg.nsid, cfg.cdw2, cfg.cdw3, cfg.cdw10, + cfg.cdw11, cfg.cdw12, cfg.cdw13, cfg.cdw14, + cfg.cdw15, cfg.data_len, data, cfg.metadata_len, + metadata, cfg.timeout_ms, &result); + + return(data[0]); +} + +void ENESMBusInterface_SpectrixS40G::ENERegisterWrite(ene_dev_id dev, ene_register reg, unsigned char val) +{ + struct nvme_passthru_cmd cfg; + + memset(&cfg, 0, sizeof(nvme_passthru_cmd)); + + unsigned short corrected_reg = ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF); + + cfg.opcode = 0xFB; + cfg.cdw12 = (corrected_reg << 16) | (dev << 1); + cfg.cdw13 = 0x01100001; + cfg.data_len = 1; + + unsigned char data[1]; + + data[0] = val; + + unsigned char metadata[1]; + unsigned int result; + + /*-----------------------------------------------------------------------------*\ + | Send the command to the device | + \*-----------------------------------------------------------------------------*/ + nvme_admin_passthru(nvme_fd, cfg.opcode, cfg.flags, cfg.rsvd1, + cfg.nsid, cfg.cdw2, cfg.cdw3, cfg.cdw10, + cfg.cdw11, cfg.cdw12, cfg.cdw13, cfg.cdw14, + cfg.cdw15, cfg.data_len, data, cfg.metadata_len, + metadata, cfg.timeout_ms, &result); + +} + +void ENESMBusInterface_SpectrixS40G::ENERegisterWriteBlock(ene_dev_id dev, ene_register reg, unsigned char * data, unsigned char sz) +{ + struct nvme_passthru_cmd cfg; + + memset(&cfg, 0, sizeof(nvme_passthru_cmd)); + + unsigned short corrected_reg = ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF); + + cfg.opcode = 0xFB; + cfg.cdw12 = (corrected_reg << 16) | (dev << 1); + cfg.cdw13 = 0x03100000 | sz; + cfg.data_len = sz; + + unsigned char metadata[1]; + unsigned int result; + + /*-----------------------------------------------------------------------------*\ + | Send the command to the device | + \*-----------------------------------------------------------------------------*/ + nvme_admin_passthru(nvme_fd, cfg.opcode, cfg.flags, cfg.rsvd1, + cfg.nsid, cfg.cdw2, cfg.cdw3, cfg.cdw10, + cfg.cdw11, cfg.cdw12, cfg.cdw13, cfg.cdw14, + cfg.cdw15, cfg.data_len, data, cfg.metadata_len, + metadata, cfg.timeout_ms, &result); + +} diff --git a/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Linux.h b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Linux.h new file mode 100644 index 0000000..0a508f5 --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Linux.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| ENESMBusInterface_SpectrixS40G_Linux.h | +| | +| ENE SMBus interface for XPG Spectrix S40G (Linux) | +| | +| Adam Honse (CalcProgrammer1) 21 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "ENESMBusInterface.h" + +class ENESMBusInterface_SpectrixS40G : public ENESMBusInterface +{ +public: + ENESMBusInterface_SpectrixS40G(int fd, char* path); + ~ENESMBusInterface_SpectrixS40G(); + + ene_interface_type GetInterfaceType(); + std::string GetLocation(); + int GetMaxBlock(); + unsigned char ENERegisterRead(ene_dev_id dev, ene_register reg); + void ENERegisterWrite(ene_dev_id dev, ene_register reg, unsigned char val); + void ENERegisterWriteBlock(ene_dev_id dev, ene_register reg, unsigned char * data, unsigned char sz); + +private: + int nvme_fd; + std::string path; +}; diff --git a/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Windows.cpp b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Windows.cpp new file mode 100644 index 0000000..1343743 --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Windows.cpp @@ -0,0 +1,261 @@ +/*---------------------------------------------------------*\ +| ENESMBusInterface_SpectrixS40G_Windows.cpp | +| | +| ENE SMBus interface for XPG Spectrix S40G (Windows) | +| | +| Adam Honse (CalcProgrammer1) 21 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include + +#include "ENESMBusInterface_SpectrixS40G_Windows.h" +#include "StringUtils.h" + +ENESMBusInterface_SpectrixS40G::ENESMBusInterface_SpectrixS40G(HANDLE fd, wchar_t* path) +{ + this->nvme_fd = fd; + this->path = path; +} + +ENESMBusInterface_SpectrixS40G::~ENESMBusInterface_SpectrixS40G() +{ + +} + +ene_interface_type ENESMBusInterface_SpectrixS40G::GetInterfaceType() +{ + return(ENE_INTERFACE_TYPE_SPECTRIX_S40G); +} + +std::string ENESMBusInterface_SpectrixS40G::GetLocation() +{ + return("NVMe: " + StringUtils::wstring_to_string(path)); +} + +int ENESMBusInterface_SpectrixS40G::GetMaxBlock() +{ + return(24); +} + +unsigned char ENESMBusInterface_SpectrixS40G::ENERegisterRead(ene_dev_id dev, ene_register reg) +{ + if(nvme_fd != INVALID_HANDLE_VALUE) + { + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold STORAGE_PROTOCOL_COMMAND | + | Size must be enough for the STORAGE_PROTOCOL_COMMAND struct plus the command | + | data. Subtract sizeof(DWORD) as the Command field in the structure overlaps | + | the actual command data. | + \*-----------------------------------------------------------------------------*/ + unsigned char buffer[sizeof(STORAGE_PROTOCOL_COMMAND) + (sizeof(DWORD) * 34) - sizeof(DWORD)] = {0}; + + /*-----------------------------------------------------------------------------*\ + | Create STORAGE_PROTOCOL_COMMAND pointer and point it to the buffer | + \*-----------------------------------------------------------------------------*/ + PSTORAGE_PROTOCOL_COMMAND command = (PSTORAGE_PROTOCOL_COMMAND)buffer; + + /*-----------------------------------------------------------------------------*\ + | Fill in STORAGE_PROTOCOL_COMMAND structure | + \*-----------------------------------------------------------------------------*/ + command->Version = STORAGE_PROTOCOL_STRUCTURE_VERSION; + command->Length = sizeof(STORAGE_PROTOCOL_COMMAND); + command->ProtocolType = ProtocolTypeNvme; + command->Flags = STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST; + command->ReturnStatus = 0x00000000; + command->ErrorCode = 0x00000000; + command->CommandLength = STORAGE_PROTOCOL_COMMAND_LENGTH_NVME; + command->ErrorInfoLength = 0x00000040; + command->DataToDeviceTransferLength = 0x00000000; + command->DataFromDeviceTransferLength = 0x00000001; + command->TimeOutValue = 0x00000001; + command->ErrorInfoOffset = 0x00000090; + command->DataToDeviceBufferOffset = 0x00000000; + command->DataFromDeviceBufferOffset = 0x000000D0; + command->CommandSpecific = STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND; + command->Reserved0 = 0x00000000; + command->FixedProtocolReturnData = 0x00000000; + command->Reserved1[0] = 0x00000000; + command->Reserved1[1] = 0x00000000; + command->Reserved1[2] = 0x00000000; + + /*-----------------------------------------------------------------------------*\ + | Create ENE Register Write command, filling in the appropriate register and | + | value | + \*-----------------------------------------------------------------------------*/ + PNVME_COMMAND CommandValue = (PNVME_COMMAND)command->Command; + + unsigned short corrected_reg = ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF); + + CommandValue->CDW0.OPC = 0xFA; + CommandValue->u.GENERAL.CDW12 = (corrected_reg << 16) | (dev << 1); + CommandValue->u.GENERAL.CDW13 = 0x81100001; + + DWORD ExtraValue[18] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + + /*-----------------------------------------------------------------------------*\ + | Send the STORAGE_PROTOCOL_COMMAND to the device | + \*-----------------------------------------------------------------------------*/ + DWORD bytesreturned = 0; + DeviceIoControl(nvme_fd, IOCTL_STORAGE_PROTOCOL_COMMAND, buffer, sizeof(buffer), buffer, sizeof(buffer), &bytesreturned, (LPOVERLAPPED)0x0); + + /*-----------------------------------------------------------------------------*\ + | Copy the ENE Register Write extra data into the STORAGE_PROTOCOL_COMMAND | + | buffer | + \*-----------------------------------------------------------------------------*/ + memcpy(ExtraValue, &command->Command + sizeof(NVME_COMMAND), sizeof(ExtraValue)); + + return((unsigned char)ExtraValue[16]); + } + + return(0); +} + +void ENESMBusInterface_SpectrixS40G::ENERegisterWrite(ene_dev_id dev, ene_register reg, unsigned char val) +{ + if(nvme_fd != INVALID_HANDLE_VALUE) + { + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold STORAGE_PROTOCOL_COMMAND | + | Size must be enough for the STORAGE_PROTOCOL_COMMAND struct plus the command | + | data. Subtract sizeof(DWORD) as the Command field in the structure overlaps | + | the actual command data. | + \*-----------------------------------------------------------------------------*/ + unsigned char buffer[sizeof(STORAGE_PROTOCOL_COMMAND) + (sizeof(DWORD) * 34) - sizeof(DWORD)]; + + /*-----------------------------------------------------------------------------*\ + | Create STORAGE_PROTOCOL_COMMAND pointer and point it to the buffer | + \*-----------------------------------------------------------------------------*/ + PSTORAGE_PROTOCOL_COMMAND command = (PSTORAGE_PROTOCOL_COMMAND)buffer; + + /*-----------------------------------------------------------------------------*\ + | Fill in STORAGE_PROTOCOL_COMMAND structure | + \*-----------------------------------------------------------------------------*/ + command->Version = STORAGE_PROTOCOL_STRUCTURE_VERSION; + command->Length = sizeof(STORAGE_PROTOCOL_COMMAND); + command->ProtocolType = ProtocolTypeNvme; + command->Flags = STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST; + command->ReturnStatus = 0x00000000; + command->ErrorCode = 0x00000000; + command->CommandLength = STORAGE_PROTOCOL_COMMAND_LENGTH_NVME; + command->ErrorInfoLength = 0x00000040; + command->DataToDeviceTransferLength = 0x00000001; + command->DataFromDeviceTransferLength = 0x00000000; + command->TimeOutValue = 0x00000001; + command->ErrorInfoOffset = 0x00000090; + command->DataToDeviceBufferOffset = 0x000000D0; + command->DataFromDeviceBufferOffset = 0x00000000; + command->CommandSpecific = STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND; + command->Reserved0 = 0x00000000; + command->FixedProtocolReturnData = 0x00000000; + command->Reserved1[0] = 0x00000000; + command->Reserved1[1] = 0x00000000; + command->Reserved1[2] = 0x00000000; + + /*-----------------------------------------------------------------------------*\ + | Create ENE Register Write command, filling in the appropriate register and | + | value | + \*-----------------------------------------------------------------------------*/ + PNVME_COMMAND CommandValue = (PNVME_COMMAND)command->Command; + + unsigned short corrected_reg = ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF); + + CommandValue->CDW0.OPC = 0xFB; + CommandValue->u.GENERAL.CDW12 = (corrected_reg << 16) | (dev << 1); + CommandValue->u.GENERAL.CDW13 = 0x01100001; + + DWORD ExtraValue[18] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + + ExtraValue[16] = val; + + /*-----------------------------------------------------------------------------*\ + | Copy the ENE Register Write extra data into the STORAGE_PROTOCOL_COMMAND | + | buffer | + \*-----------------------------------------------------------------------------*/ + memcpy(&command->Command + sizeof(NVME_COMMAND), ExtraValue, sizeof(ExtraValue)); + + /*-----------------------------------------------------------------------------*\ + | Send the STORAGE_PROTOCOL_COMMAND to the device | + \*-----------------------------------------------------------------------------*/ + DeviceIoControl(nvme_fd, IOCTL_STORAGE_PROTOCOL_COMMAND, buffer, sizeof(buffer), buffer, sizeof(buffer), 0x0, (LPOVERLAPPED)0x0); + } +} + +void ENESMBusInterface_SpectrixS40G::ENERegisterWriteBlock(ene_dev_id dev, ene_register reg, unsigned char * data, unsigned char sz) +{ + if(nvme_fd != INVALID_HANDLE_VALUE) + { + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold STORAGE_PROTOCOL_COMMAND | + | Size must be enough for the STORAGE_PROTOCOL_COMMAND struct plus the command | + | data. Subtract sizeof(DWORD) as the Command field in the structure overlaps | + | the actual command data. | + \*-----------------------------------------------------------------------------*/ + unsigned char buffer[sizeof(STORAGE_PROTOCOL_COMMAND) + (sizeof(DWORD) * 39) - sizeof(DWORD)]; + + /*-----------------------------------------------------------------------------*\ + | Create STORAGE_PROTOCOL_COMMAND pointer and point it to the buffer | + \*-----------------------------------------------------------------------------*/ + PSTORAGE_PROTOCOL_COMMAND command = (PSTORAGE_PROTOCOL_COMMAND)buffer; + + /*-----------------------------------------------------------------------------*\ + | Fill in STORAGE_PROTOCOL_COMMAND structure | + \*-----------------------------------------------------------------------------*/ + command->Version = STORAGE_PROTOCOL_STRUCTURE_VERSION; + command->Length = sizeof(STORAGE_PROTOCOL_COMMAND); + command->ProtocolType = ProtocolTypeNvme; + command->Flags = STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST; + command->ReturnStatus = 0x00000000; + command->ErrorCode = 0x00000000; + command->CommandLength = STORAGE_PROTOCOL_COMMAND_LENGTH_NVME; + command->ErrorInfoLength = 0x00000040; + command->DataToDeviceTransferLength = sz; + command->DataFromDeviceTransferLength = 0x00000000; + command->TimeOutValue = 0x00000001; + command->ErrorInfoOffset = 0x00000090; + command->DataToDeviceBufferOffset = 0x000000D0; + command->DataFromDeviceBufferOffset = 0x00000000; + command->CommandSpecific = STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND; + command->Reserved0 = 0x00000000; + command->FixedProtocolReturnData = 0x00000000; + command->Reserved1[0] = 0x00000000; + command->Reserved1[1] = 0x00000000; + command->Reserved1[2] = 0x00000000; + + /*-----------------------------------------------------------------------------*\ + | Create ENE Register Write Block command, filling in the appropriate register | + | and value | + \*-----------------------------------------------------------------------------*/ + PNVME_COMMAND CommandValue = (PNVME_COMMAND)command->Command; + + unsigned short corrected_reg = ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF); + + CommandValue->CDW0.OPC = 0xFB; + CommandValue->u.GENERAL.CDW12 = (corrected_reg << 16) | (dev << 1); + CommandValue->u.GENERAL.CDW13 = 0x03100000 | sz; + + DWORD ExtraValue[23] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000 }; + + memcpy(&ExtraValue[16], data, sz); + + /*-----------------------------------------------------------------------------*\ + | Copy the ENE Register Write Block extra data into the | + | STORAGE_PROTOCOL_COMMAND buffer | + \*-----------------------------------------------------------------------------*/ + memcpy(&command->Command + sizeof(NVME_COMMAND), ExtraValue, sizeof(ExtraValue)); + + /*-----------------------------------------------------------------------------*\ + | Send the STORAGE_PROTOCOL_COMMAND to the device | + \*-----------------------------------------------------------------------------*/ + DeviceIoControl(nvme_fd, IOCTL_STORAGE_PROTOCOL_COMMAND, buffer, sizeof(buffer), buffer, sizeof(buffer), 0x0, (LPOVERLAPPED)0x0); + } +} diff --git a/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Windows.h b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Windows.h new file mode 100644 index 0000000..50a34cc --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Windows.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| ENESMBusInterface_SpectrixS40G_Windows.h | +| | +| ENE SMBus interface for XPG Spectrix S40G (Windows) | +| | +| Adam Honse (CalcProgrammer1) 21 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "ENESMBusInterface.h" + +class ENESMBusInterface_SpectrixS40G : public ENESMBusInterface +{ +public: + ENESMBusInterface_SpectrixS40G(HANDLE fd, wchar_t* path); + ~ENESMBusInterface_SpectrixS40G(); + + ene_interface_type GetInterfaceType(); + std::string GetLocation(); + int GetMaxBlock(); + unsigned char ENERegisterRead(ene_dev_id dev, ene_register reg); + void ENERegisterWrite(ene_dev_id dev, ene_register reg, unsigned char val); + void ENERegisterWriteBlock(ene_dev_id dev, ene_register reg, unsigned char * data, unsigned char sz); + +private: + HANDLE nvme_fd; + std::wstring path; +}; diff --git a/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_i2c_smbus.cpp b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_i2c_smbus.cpp new file mode 100644 index 0000000..2754689 --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_i2c_smbus.cpp @@ -0,0 +1,72 @@ +/*---------------------------------------------------------*\ +| ENESMBusInterface_i2c_smbus.cpp | +| | +| ENE SMBus interface for I2C/SMBus | +| | +| Adam Honse (CalcProgrammer1) 21 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "ENESMBusInterface_i2c_smbus.h" + +ENESMBusInterface_i2c_smbus::ENESMBusInterface_i2c_smbus(i2c_smbus_interface* bus) +{ + this->bus = bus; +} + +ENESMBusInterface_i2c_smbus::~ENESMBusInterface_i2c_smbus() +{ + +} + +ene_interface_type ENESMBusInterface_i2c_smbus::GetInterfaceType() +{ + return(ENE_INTERFACE_TYPE_I2C_SMBUS); +} + +std::string ENESMBusInterface_i2c_smbus::GetLocation() +{ + std::string return_string(bus->device_name); + return("I2C: " + return_string); +} + +int ENESMBusInterface_i2c_smbus::GetMaxBlock() +{ + return(3); +} + +unsigned char ENESMBusInterface_i2c_smbus::ENERegisterRead(ene_dev_id dev, ene_register reg) +{ + //Write ENE register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Read ENE value + return(bus->i2c_smbus_read_byte_data(dev, 0x81)); +} + +void ENESMBusInterface_i2c_smbus::ENERegisterWrite(ene_dev_id dev, ene_register reg, unsigned char val) +{ + //Write ENE register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write ENE value + bus->i2c_smbus_write_byte_data(dev, 0x01, val); +} + +void ENESMBusInterface_i2c_smbus::ENERegisterWriteBlock(ene_dev_id dev, ene_register reg, unsigned char * data, unsigned char sz) +{ + //Write ENE register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write ENE block data + if(bus->i2c_smbus_write_block_data(dev, 0x03, sz, data) == -1) + { + //Fall back to individual byte operations if the block operation fails + for(unsigned int block_byte = 0; block_byte < sz; block_byte++) + { + bus->i2c_smbus_write_byte_data(dev, 0x01, data[block_byte]); + } + } +} diff --git a/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_i2c_smbus.h b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_i2c_smbus.h new file mode 100644 index 0000000..9b2e2fb --- /dev/null +++ b/Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_i2c_smbus.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| ENESMBusInterface_i2c_smbus.h | +| | +| ENE SMBus interface for I2C/SMBus | +| | +| Adam Honse (CalcProgrammer1) 21 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "ENESMBusInterface.h" +#include "i2c_smbus.h" + +class ENESMBusInterface_i2c_smbus : public ENESMBusInterface +{ +public: + ENESMBusInterface_i2c_smbus(i2c_smbus_interface* bus); + ~ENESMBusInterface_i2c_smbus(); + + ene_interface_type GetInterfaceType(); + std::string GetLocation(); + int GetMaxBlock(); + unsigned char ENERegisterRead(ene_dev_id dev, ene_register reg); + void ENERegisterWrite(ene_dev_id dev, ene_register reg, unsigned char val); + void ENERegisterWriteBlock(ene_dev_id dev, ene_register reg, unsigned char * data, unsigned char sz); + +private: + i2c_smbus_interface * bus; +}; diff --git a/Controllers/ENESMBusController/RGBController_ENESMBus.cpp b/Controllers/ENESMBusController/RGBController_ENESMBus.cpp new file mode 100644 index 0000000..a0f4d98 --- /dev/null +++ b/Controllers/ENESMBusController/RGBController_ENESMBus.cpp @@ -0,0 +1,511 @@ +/*---------------------------------------------------------*\ +| RGBController_ENESMBus.cpp | +| | +| RGBController for ENE SMBus devices | +| | +| Adam Honse (CalcProgrammer1) 13 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ENESMBus.h" +#include "LogManager.h" +#include "SettingsManager.h" +#include "ResourceManager.h" + +/**------------------------------------------------------------------*\ + @name ENE SMBus Device + @category RAM,Motherboard,GPU,Storage + @type SMBus + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectENESMBusDRAMControllers,DetectENESMBusMotherboardControllers,DetectENESMBusGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_ENESMBus::RGBController_ENESMBus(ENESMBusController * controller_ptr) +{ + controller = controller_ptr; + + /*---------------------------------------------------------*\ + | Get ENEController settings | + \*---------------------------------------------------------*/ + json ene_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("ENESMBusSettings"); + + /*---------------------------------------------------------*\ + | Check if save to device is enabled | + \*---------------------------------------------------------*/ + unsigned int save_flag = 0; + + if(ene_settings.contains("enable_save")) + { + if(ene_settings["enable_save"] == true) + { + save_flag = MODE_FLAG_MANUAL_SAVE; + } + } + + /*---------------------------------------------------------*\ + | Determine name and type (DRAM or Motherboard) by checking | + | the ENE controller's version string | + \*---------------------------------------------------------*/ + name = controller->GetName(); + description = "ENE SMBus Device"; + version = controller->GetVersion(); + location = controller->GetLocation(); + type = controller->GetType(); + + if((version.find("DIMM_LED") != std::string::npos) || (version.find("AUDA") != std::string::npos) ) + { + vendor = "ENE"; + } + else if(version.find("ROG STRIX ARION") != std::string::npos) + { + vendor = "ASUS"; + } + else if(location.find("NVMe:") != std::string::npos) + { + vendor = "XPG"; + } + else + { + vendor = "ASUS"; + } + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = ENE_MODE_OFF; + Off.flags = save_flag; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = ENE_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | save_flag; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ENE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | save_flag; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = ENE_SPEED_SLOWEST; + Breathing.speed_max = ENE_SPEED_FASTEST; + Breathing.speed = ENE_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = ENE_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | save_flag; + Flashing.color_mode = MODE_COLORS_PER_LED; + Flashing.speed_min = ENE_SPEED_SLOWEST; + Flashing.speed_max = ENE_SPEED_FASTEST; + Flashing.speed = ENE_SPEED_NORMAL; + modes.push_back(Flashing); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = ENE_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | save_flag; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.speed_min = ENE_SPEED_SLOWEST; + SpectrumCycle.speed_max = ENE_SPEED_FASTEST; + SpectrumCycle.speed = ENE_SPEED_NORMAL; + modes.push_back(SpectrumCycle); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = ENE_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | save_flag; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed_min = ENE_SPEED_SLOWEST; + Rainbow.speed_max = ENE_SPEED_FASTEST; + Rainbow.speed = ENE_SPEED_NORMAL; + Rainbow.direction = MODE_DIRECTION_LEFT; + modes.push_back(Rainbow); + + mode ChaseFade; + ChaseFade.name = "Chase Fade"; + ChaseFade.value = ENE_MODE_CHASE_FADE; + ChaseFade.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | save_flag; + ChaseFade.color_mode = MODE_COLORS_PER_LED; + ChaseFade.speed_min = ENE_SPEED_SLOWEST; + ChaseFade.speed_max = ENE_SPEED_FASTEST; + ChaseFade.speed = ENE_SPEED_NORMAL; + ChaseFade.direction = MODE_DIRECTION_LEFT; + modes.push_back(ChaseFade); + + mode Chase; + Chase.name = "Chase"; + Chase.value = ENE_MODE_CHASE; + Chase.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | save_flag; + Chase.color_mode = MODE_COLORS_PER_LED; + Chase.speed_min = ENE_SPEED_SLOWEST; + Chase.speed_max = ENE_SPEED_FASTEST; + Chase.speed = ENE_SPEED_NORMAL; + ChaseFade.direction = MODE_DIRECTION_LEFT; + modes.push_back(Chase); + + mode RandomFlicker; + RandomFlicker.name = "Random Flicker"; + RandomFlicker.value = ENE_MODE_RANDOM_FLICKER; + RandomFlicker.flags = MODE_FLAG_HAS_SPEED | save_flag; + RandomFlicker.color_mode = MODE_COLORS_NONE; + RandomFlicker.speed_min = ENE_SPEED_SLOWEST; + RandomFlicker.speed_max = ENE_SPEED_FASTEST; + RandomFlicker.speed = ENE_SPEED_NORMAL; + modes.push_back(RandomFlicker); + + if(controller->SupportsMode14()) + { + mode DoubleFade; + DoubleFade.name = "Double Fade"; + DoubleFade.value = ENE_MODE_DOUBLE_FADE; + DoubleFade.flags = MODE_FLAG_HAS_SPEED; + DoubleFade.color_mode = MODE_COLORS_NONE; + DoubleFade.speed_min = ENE_SPEED_SLOWEST; + DoubleFade.speed_max = ENE_SPEED_FASTEST; + DoubleFade.speed = ENE_SPEED_NORMAL; + modes.push_back(DoubleFade); + } + + SetupZones(); + + /*-------------------------------------------------*\ + | Initialize active mode | + \*-------------------------------------------------*/ + active_mode = GetDeviceMode(); +} + +RGBController_ENESMBus::~RGBController_ENESMBus() +{ + delete controller; +} + +int RGBController_ENESMBus::GetDeviceMode() +{ + /*-----------------------------------------------------------------*\ + | Determine starting mode by reading the mode and direct registers | + \*-----------------------------------------------------------------*/ + int dev_mode = controller->ENERegisterRead(ENE_REG_MODE); + int color_mode = MODE_COLORS_PER_LED; + int speed = controller->ENERegisterRead(ENE_REG_SPEED); + int direction = controller->ENERegisterRead(ENE_REG_DIRECTION); + + LOG_TRACE("[%s] Retrieved ENE mode from module: %02d", name.c_str(), dev_mode); + + if(controller->ENERegisterRead(ENE_REG_DIRECT)) + { + dev_mode = 0xFFFF; + } + + switch(dev_mode) + { + case ENE_MODE_OFF: + case ENE_MODE_RAINBOW: + case ENE_MODE_SPECTRUM_CYCLE: + case ENE_MODE_RANDOM_FLICKER: + case ENE_MODE_DOUBLE_FADE: + color_mode = MODE_COLORS_NONE; + break; + + case ENE_MODE_SPECTRUM_CYCLE_CHASE: + dev_mode = ENE_MODE_CHASE; + color_mode = MODE_COLORS_RANDOM; + break; + + case ENE_MODE_SPECTRUM_CYCLE_BREATHING: + dev_mode = ENE_MODE_BREATHING; + color_mode = MODE_COLORS_RANDOM; + break; + + case ENE_MODE_SPECTRUM_CYCLE_CHASE_FADE: + dev_mode = ENE_MODE_CHASE_FADE; + color_mode = MODE_COLORS_RANDOM; + break; + } + + for(int mode = 0; mode < (int)modes.size(); mode++) + { + if(modes[mode].value == dev_mode) + { + active_mode = mode; + modes[mode].color_mode = color_mode; + + if(modes[mode].flags & MODE_FLAG_HAS_SPEED) + { + modes[mode].speed = speed; + } + + if(modes[mode].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + modes[mode].direction = direction; + } + + break; + } + } + + /*---------------------------------------------------------*\ + | Initialize colors for each LED | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned int led = leds[led_idx].value; + unsigned char red; + unsigned char grn; + unsigned char blu; + + if(active_mode == 0) + { + red = controller->GetLEDRed(led); + grn = controller->GetLEDGreen(led); + blu = controller->GetLEDBlue(led); + } + else + { + red = controller->GetLEDRedEffect(led); + grn = controller->GetLEDGreenEffect(led); + blu = controller->GetLEDBlueEffect(led); + } + + colors[led_idx] = ToRGBColor(red, grn, blu); + } + + return(active_mode); +} + +void RGBController_ENESMBus::DeviceUpdateLEDs() +{ + if(GetMode() == 0) + { + controller->SetAllColorsDirect(&colors[0]); + } + else + { + controller->SetAllColorsEffect(&colors[0]); + } + +} + +void RGBController_ENESMBus::UpdateZoneLEDs(int zone) +{ + for(std::size_t led_idx = 0; led_idx < zones[zone].leds_count; led_idx++) + { + int led = zones[zone].leds[led_idx].value; + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + if(GetMode() == 0) + { + controller->SetLEDColorDirect(led, red, grn, blu); + } + else + { + controller->SetLEDColorEffect(led, red, grn, blu); + } + } +} + +void RGBController_ENESMBus::UpdateSingleLED(int led) +{ + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + if(GetMode() == 0) + { + controller->SetLEDColorDirect(led, red, grn, blu); + } + else + { + controller->SetLEDColorEffect(led, red, grn, blu); + } +} + +void RGBController_ENESMBus::SetupZones() +{ + /*---------------------------------------------------------*\ + | Search through all LEDs and create zones for each channel | + | type | + \*---------------------------------------------------------*/ + for(unsigned int cfg_zone_idx = 0; cfg_zone_idx < ENE_NUM_ZONES; cfg_zone_idx++) + { + /*---------------------------------------------------------*\ + | Get the number of LEDs in the zone | + \*---------------------------------------------------------*/ + unsigned int leds_in_zone = controller->GetLEDCount(cfg_zone_idx); + + if(leds_in_zone > 0) + { + /*---------------------------------------------------------*\ + | Search through existing zones to make sure we don't | + | create a duplicate zone | + \*---------------------------------------------------------*/ + bool matched = false; + std::size_t existing_zone_idx = 0; + + for(existing_zone_idx = 0; existing_zone_idx < zones.size(); existing_zone_idx++) + { + if(controller->GetChannelName(cfg_zone_idx) == zones[existing_zone_idx].name) + { + matched = true; + break; + } + } + + /*---------------------------------------------------------*\ + | If zone does not already exist, create it | + \*---------------------------------------------------------*/ + if(matched == false) + { + zone* new_zone = new zone(); + + /*---------------------------------------------------------*\ + | Set zone name to channel name | + \*---------------------------------------------------------*/ + new_zone->name = controller->GetChannelName(cfg_zone_idx); + + /*---------------------------------------------------------*\ + | Set zone LED count to LEDs in zone | + \*---------------------------------------------------------*/ + new_zone->leds_count = leds_in_zone; + + /*---------------------------------------------------------*\ + | Push new zone to zones vector | + \*---------------------------------------------------------*/ + zones.push_back(*new_zone); + } + /*---------------------------------------------------------*\ + | Otherwise, add the number of LEDs from this zone to the | + | existing one. | + \*---------------------------------------------------------*/ + else + { + zones[existing_zone_idx].leds_count += leds_in_zone; + } + } + } + + /*---------------------------------------------------------*\ + | Finish setting up the zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + zones[zone_idx].leds_min = zones[zone_idx].leds_count; + zones[zone_idx].leds_max = zones[zone_idx].leds_count; + + if(zones[zone_idx].leds_count > 1) + { + zones[zone_idx].type = ZONE_TYPE_LINEAR; + } + else + { + zones[zone_idx].type = ZONE_TYPE_SINGLE; + } + + zones[zone_idx].matrix_map = NULL; + } + + /*---------------------------------------------------------*\ + | Create LED entries for each zone | + \*---------------------------------------------------------*/ + unsigned int led_idx = 0; + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(std::size_t zone_led_idx = 0; zone_led_idx < zones[zone_idx].leds_count; zone_led_idx++) + { + led* new_led = new led(); + + new_led->name = zones[zone_idx].name + " LED "; + new_led->name.append(std::to_string(zone_led_idx + 1)); + + new_led->value = led_idx; + led_idx++; + + leds.push_back(*new_led); + } + } + + SetupColors(); +} + +void RGBController_ENESMBus::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ENESMBus::DeviceUpdateMode() +{ + if (modes[active_mode].value == 0xFFFF) + { + controller->SetDirect(true); + } + else + { + int new_mode = modes[active_mode].value; + int new_speed = 0; + int new_direction = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + switch(new_mode) + { + case ENE_MODE_CHASE: + new_mode = ENE_MODE_SPECTRUM_CYCLE_CHASE; + break; + case ENE_MODE_BREATHING: + new_mode = ENE_MODE_SPECTRUM_CYCLE_BREATHING; + break; + case ENE_MODE_CHASE_FADE: + new_mode = ENE_MODE_SPECTRUM_CYCLE_CHASE_FADE; + break; + } + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + new_speed = modes[active_mode].speed; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + switch(modes[active_mode].direction) + { + case MODE_DIRECTION_LEFT: + new_direction = ENE_DIRECTION_FORWARD; + break; + + case MODE_DIRECTION_RIGHT: + new_direction = ENE_DIRECTION_REVERSE; + break; + } + } + + controller->SetMode(new_mode, new_speed, new_direction); + controller->SetDirect(false); + } +} + +void RGBController_ENESMBus::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->SaveMode(); +} diff --git a/Controllers/ENESMBusController/RGBController_ENESMBus.h b/Controllers/ENESMBusController/RGBController_ENESMBus.h new file mode 100644 index 0000000..79d2b6e --- /dev/null +++ b/Controllers/ENESMBusController/RGBController_ENESMBus.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_ENESMBus.h | +| | +| RGBController for ENE SMBus devices | +| | +| Adam Honse (CalcProgrammer1) 13 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ENESMBusController.h" + +class RGBController_ENESMBus : public RGBController +{ +public: + RGBController_ENESMBus(ENESMBusController* controller_ptr); + ~RGBController_ENESMBus(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + ENESMBusController* controller; + + int GetDeviceMode(); +}; diff --git a/Controllers/ENESMBusController/ROGArionDetect.cpp b/Controllers/ENESMBusController/ROGArionDetect.cpp new file mode 100644 index 0000000..f689459 --- /dev/null +++ b/Controllers/ENESMBusController/ROGArionDetect.cpp @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| ROGArionDetect.cpp | +| | +| Detector for ASUS ROG Arion | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ENESMBusController.h" +#include "ENESMBusInterface_ROGArion.h" +#include "RGBController_ENESMBus.h" +#include "scsiapi.h" + +/******************************************************************************************\ +* * +* DetectROGArionControllers * +* * +* Detects ENE SMBus controllers on ASUS ROG Arion devices * +* * +\******************************************************************************************/ + +void DetectROGArionControllers() +{ + scsi_device_info * info = scsi_enumerate(NULL, NULL); + + while(info) + { + if(strncmp(info->vendor, "ROG", 3) == 0 && strncmp(info->product, "ESD-S1C", 7) == 0) + { + scsi_device * dev = scsi_open_path(info->path); + + if(dev) + { + ENESMBusInterface_ROGArion* interface = new ENESMBusInterface_ROGArion(dev, info->path); + ENESMBusController* controller = new ENESMBusController(interface, 0x67, "Asus ROG Strix Arion", DEVICE_TYPE_STORAGE); + RGBController_ENESMBus* rgb_controller = new RGBController_ENESMBus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + info = info->next; + } + + scsi_free_enumeration(info); + +} /* DetectROGArionControllers() */ + +REGISTER_DETECTOR("ASUS ROG Arion", DetectROGArionControllers); diff --git a/Controllers/ENESMBusController/XPGSpectrixS40GDetect_Linux.cpp b/Controllers/ENESMBusController/XPGSpectrixS40GDetect_Linux.cpp new file mode 100644 index 0000000..bae7486 --- /dev/null +++ b/Controllers/ENESMBusController/XPGSpectrixS40GDetect_Linux.cpp @@ -0,0 +1,93 @@ +/*---------------------------------------------------------*\ +| XPGSpectrixS40GDetect_Linux.cpp | +| | +| Detector for XPG Spectrix S40G (Linux) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include "Detector.h" +#include "ENESMBusController.h" +#include "ENESMBusInterface_SpectrixS40G_Linux.h" +#include "LogManager.h" +#include "RGBController.h" +#include "RGBController_ENESMBus.h" + +/******************************************************************************************\ +* * +* DetectSpectrixS40GControllers * +* * +* Detects ENE SMBus controllers on XPG Spectrix S40G NVMe devices * +* * +\******************************************************************************************/ + +void DetectSpectrixS40GControllers() +{ + /*---------------------------------------------------------------------*\ + | Search for /dev/nvmeX nodes with model matching "XPG SPECTRIX S40G" | + \*---------------------------------------------------------------------*/ + unsigned int nvme_idx = 0; + + while(1) + { + /*-------------------------------------------------*\ + | Create the nvme class model path | + \*-------------------------------------------------*/ + char nvme_dev_buf[1024]; + + snprintf(nvme_dev_buf, 1024, "/sys/class/nvme/nvme%d/model", nvme_idx); + + /*-------------------------------------------------*\ + | Open the input event path to get the name | + \*-------------------------------------------------*/ + int nvme_model_fd = open(nvme_dev_buf, O_RDONLY|O_NONBLOCK); + + if(nvme_model_fd < 0) + { + break; + } + + memset(nvme_dev_buf, 0, 1024); + + if(read(nvme_model_fd, nvme_dev_buf, 1024) < 0) + { + LOG_WARNING("[XPG Spectrix S40G] Probing %d, failed to read NVMe model", nvme_idx); + } + else + { + LOG_DEBUG("[XPG Spectrix S40G] Probing %d, model: %s", nvme_idx, nvme_dev_buf); + } + + close(nvme_model_fd); + + /*-------------------------------------------------*\ + | Check if this NVMe device is a SPECTRIX S40G | + \*-------------------------------------------------*/ + if(strncmp(nvme_dev_buf, "XPG SPECTRIX S40G", 17) == 0) + { + snprintf(nvme_dev_buf, 1024, "/dev/nvme%d", nvme_idx); + + int nvme_fd = open(nvme_dev_buf, O_RDWR); + + if(nvme_fd > 0) + { + ENESMBusInterface_SpectrixS40G* interface = new ENESMBusInterface_SpectrixS40G(nvme_fd, nvme_dev_buf); + ENESMBusController* controller = new ENESMBusController(interface, 0x67, "XPG Spectrix S40G", DEVICE_TYPE_STORAGE); + RGBController_ENESMBus* rgb_controller = new RGBController_ENESMBus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + + nvme_idx++; + } +} /* DetectSpectrixS40GControllers() */ + +REGISTER_DETECTOR( "XPG Spectrix S40G", DetectSpectrixS40GControllers); diff --git a/Controllers/ENESMBusController/XPGSpectrixS40GDetect_Windows.cpp b/Controllers/ENESMBusController/XPGSpectrixS40GDetect_Windows.cpp new file mode 100644 index 0000000..db5b601 --- /dev/null +++ b/Controllers/ENESMBusController/XPGSpectrixS40GDetect_Windows.cpp @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| XPGSpectrixS40GDetect_Windows.cpp | +| | +| Detector for XPG Spectrix S40G (Windows) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "Detector.h" +#include "ENESMBusController.h" +#include "ENESMBusInterface_SpectrixS40G_Windows.h" +#include "RGBController.h" +#include "RGBController_ENESMBus.h" + +#define DEVBUFSIZE (128 * 1024) + +/*----------------------------------------------------------------------*\ +| Windows defines "interface" for some reason. Work around this | +\*----------------------------------------------------------------------*/ +#ifdef interface +#undef interface +#endif + +/******************************************************************************************\ +* * +* Search * +* * +* Search for an NVMe device matching "XPG SPECTRIX S40G" * +* * +\******************************************************************************************/ + +int Search(wchar_t *dev_name) +{ + wchar_t buff[DEVBUFSIZE] = L""; + int wchar_count; + + wchar_count = QueryDosDeviceW(NULL, buff, DEVBUFSIZE); + + if(wchar_count == 0) + { + return 0; + } + + for(int i = 0; i < wchar_count; i++) + { + if(wcsstr(buff + i, L"SCSI#Disk&Ven_NVMe&Prod_XPG_SPECTRIX_S40#")) + { + wcsncpy(dev_name, buff + i, MAX_PATH); + (dev_name)[MAX_PATH - 1] = '\0'; + return 1; + } + + i += (int)wcslen(buff + i); + } + + return 0; +} + +/******************************************************************************************\ +* * +* OpenDevice * +* * +* Open a handle to the given device path * +* * +\******************************************************************************************/ + +HANDLE OpenDevice(wchar_t buff[MAX_PATH]) +{ + wchar_t path[MAX_PATH]; + + wcscpy(path, L"\\\\?\\"); + wcsncat(path, buff, MAX_PATH - 4); + + for(size_t i = 0; i < MAX_PATH && path[i] != '\0'; i++) + { + path[i] = tolower(path[i]); + } + + wprintf(L"%s\n", path); + + HANDLE hDevice = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)0x0, OPEN_EXISTING, 0x0, (HANDLE)0x0); + + return(hDevice); +} + +/******************************************************************************************\ +* * +* DetectSpectrixS40GControllers * +* * +* Detects ENE SMBus controllers on XPG Spectrix S40G NVMe devices * +* * +* Tests for the existance of a file descriptor matching * +* SCSI#Disk&Ven_NVMe&Prod_XPG_SPECTRIX_S40# on Windows machines * +* * +\******************************************************************************************/ + +void DetectSpectrixS40GControllers() +{ + /*-------------------------------------------------------------------------------------------------*\ + | https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-scsi-devices | + \*-------------------------------------------------------------------------------------------------*/ + wchar_t dev_name[MAX_PATH]; + + if(Search(dev_name)) + { + HANDLE nvme_fd = OpenDevice(dev_name); + + if(nvme_fd != INVALID_HANDLE_VALUE) + { + ENESMBusInterface_SpectrixS40G* interface = new ENESMBusInterface_SpectrixS40G(nvme_fd, dev_name); + ENESMBusController* controller = new ENESMBusController(interface, 0x67, "XPG Spectrix S40G", DEVICE_TYPE_STORAGE); + RGBController_ENESMBus* rgb_controller = new RGBController_ENESMBus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} /* DetectSpectrixS40GControllers() */ + + +REGISTER_DETECTOR( "XPG Spectrix S40G", DetectSpectrixS40GControllers); diff --git a/Controllers/EVGAAmpereGPUController/EVGAAmpereGPUControllerDetect.cpp b/Controllers/EVGAAmpereGPUController/EVGAAmpereGPUControllerDetect.cpp new file mode 100644 index 0000000..878b37d --- /dev/null +++ b/Controllers/EVGAAmpereGPUController/EVGAAmpereGPUControllerDetect.cpp @@ -0,0 +1,112 @@ +/*---------------------------------------------------------*\ +| EVGAAmpereGPUControllerDetect.cpp | +| | +| Detector for EVGA V3 (Ampere) GPU | +| | +| TheRogueZeta 15 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "EVGAGPUv3Controller.h" +#include "LogManager.h" +#include "RGBController_EVGAGPUv3.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* DetectEVGAAmpereGPUControllers * +* * +* Detect EVGA Ampere GPU controllers on the enumerated I2C busses at address 0x2D. * +* * +* bus - pointer to i2c_smbus_interface where EVGA GPU device is connected * +* dev - I2C address of EVGA GPU device * +* * +\******************************************************************************************/ + +void DetectEVGAAmpereGPUControllers(i2c_smbus_interface* bus, uint8_t address, const std::string& name) +{ + if(bus->port_id == 1) + { + EVGAGPUv3Controller* controller; + RGBController_EVGAGPUv3* rgb_controller; + + controller = new EVGAGPUv3Controller(bus, address, name); + + if(controller-> ReadFWVersion() != "") + { + rgb_controller = new RGBController_EVGAGPUv3(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + LOG_INFO("[%s] Failed to get a valid FW version, does the i2c interface support `i2c_smbus_read_i2c_block_data`?", controller->GetDeviceName().c_str()); + delete controller; + } + } +} /* DetectEVGAAmpereGPUControllers() */ + +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3060 Ti FTW3 Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, EVGA_SUB_VEN, EVGA_RTX3060TI_FTW3_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3060 Ti FTW3 Ultra" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, EVGA_SUB_VEN, EVGA_RTX3060TI_FTW3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3060 Ti FTW3 Ultra LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3060TI_FTW3_ULTRA_KL_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3060 Ti FTW3 Ultra Gaming LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3060TI_FTW3_ULTRA_GAMING_LHR_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 Black Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, EVGA_SUB_VEN, EVGA_RTX3070_XC3_BLACK_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 XC3 Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, EVGA_SUB_VEN, EVGA_RTX3070_XC3_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 XC3 Ultra" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, EVGA_SUB_VEN, EVGA_RTX3070_XC3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 XC3 Ultra LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3070_XC3_ULTRA_LHR_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 XC3 Ultra Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3070_XC3_ULTRA_GAMING_LHR_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 FTW3 Ultra" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, EVGA_SUB_VEN, EVGA_RTX3070_FTW3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 FTW3 Ultra LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3070_FTW3_ULTRA_LHR_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 FTW3 Ultra LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3070_FTW3_ULTRA_LHR_ALT_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 Ti XC3 Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, EVGA_SUB_VEN, EVGA_RTX3070TI_XC3_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 Ti XC3 Ultra" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, EVGA_SUB_VEN, EVGA_RTX3070TI_XC3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 Ti XC3 Ultra v2" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, EVGA_SUB_VEN, EVGA_RTX3070TI_XC3_ULTRA_V2_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 Ti FTW3 Ultra" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, EVGA_SUB_VEN, EVGA_RTX3070TI_FTW3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3070 Ti FTW3 Ultra v2" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, EVGA_SUB_VEN, EVGA_RTX3070TI_FTW3_ULTRA_V2_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 XC3 Black" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, EVGA_SUB_VEN, EVGA_RTX3080_XC3_BLACK_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 XC3 Black LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_XC3_BLACK_LHR_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 XC3 Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, EVGA_SUB_VEN, EVGA_RTX3080_XC3_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 XC3 Gaming LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_XC3_GAMING_LHR_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 XC3 Ultra" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, EVGA_SUB_VEN, EVGA_RTX3080_XC3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 XC3 Ultra LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_XC3_ULTRA_LHR_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 XC3 Ultra Hybrid" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, EVGA_SUB_VEN, EVGA_RTX3080_XC3_ULTRA_HYBRID_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 XC3 Ultra Hybrid LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_XC3_ULTRA_HYBRID_LHR_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 XC3 Ultra Hydro Copper" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, EVGA_SUB_VEN, EVGA_RTX3080_XC3_ULTRA_HC_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 FTW3 Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, EVGA_SUB_VEN, EVGA_RTX3080_FTW3_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 FTW3 Ultra" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, EVGA_SUB_VEN, EVGA_RTX3080_FTW3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 FTW3 Ultra v2 LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_FTW3_ULTRA_LHR_V2_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 FTW3 Ultra LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_FTW3_ULTRA_LHR_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 FTW3 Ultra Hybrid" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, EVGA_SUB_VEN, EVGA_RTX3080_FTW3_ULTRA_HYBRID_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 FTW3 Ultra Hybrid LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_FTW3_ULTRA_HYBRID_LHR_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 FTW3 Ultra Hybrid Gaming LHR" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_FTW3_ULTRA_HYBRID_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 FTW3 Ultra Hydro Copper" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, EVGA_SUB_VEN, EVGA_RTX3080_FTW3_ULTRA_HC_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 XC3 Ultra 12G" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_12G_XC3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 FTW3 Ultra 12GB" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_12G_FTW3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 FTW3 Ultra Hydro Copper 12G" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, EVGA_SUB_VEN, EVGA_RTX3080_12G_FTW3_ULTRA_HC_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 Ti XC3 Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, EVGA_SUB_VEN, EVGA_RTX3080TI_XC3_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 Ti XC3 Ultra Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, EVGA_SUB_VEN, EVGA_RTX3080TI_XC3_ULTRA_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 Ti XC3 Gaming Hybrid" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, EVGA_SUB_VEN, EVGA_RTX3080TI_XC3_GAMING_HYBRID_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 Ti XC3 Gaming Hydro Copper" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, EVGA_SUB_VEN, EVGA_RTX3080TI_XC3_GAMING_HC_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 Ti FTW3 Ultra" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, EVGA_SUB_VEN, EVGA_RTX3080TI_FTW3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 Ti FTW3 Ultra Hybrid" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, EVGA_SUB_VEN, EVGA_RTX3080TI_FTW3_ULTRA_HYBRID_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3080 Ti FTW3 Ultra Hydro Copper" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, EVGA_SUB_VEN, EVGA_RTX3080TI_FTW3_ULTRA_HC_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 XC3 Black" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_XC3_BLACK_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 XC3 Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_XC3_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 XC3 Ultra" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_XC3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 XC3 Ultra Hybrid" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_XC3_ULTRA_HYBRID_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 XC3 Ultra Hydro Copper" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_XC3_ULTRA_HC_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 FTW3 Ultra" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_FTW3_ULTRA_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 FTW3 Ultra v2" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_FTW3_ULTRA_V2_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 FTW3 Ultra v3" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_FTW3_ULTRA_V3_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 FTW3 Ultra Hybrid" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_FTW3_ULTRA_HYBRID_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 FTW3 Ultra Hydro Copper" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_FTW3_ULTRA_HC_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 K|NGP|N Hybrid" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_KINGPIN_HYBRID_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 K|NGP|N Hydro Copper" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, EVGA_SUB_VEN, EVGA_RTX3090_KINGPIN_HC_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 Ti FTW3 Black Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, EVGA_SUB_VEN, EVGA_RTX3090TI_FTW3_BLACK_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 Ti FTW3 Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, EVGA_SUB_VEN, EVGA_RTX3090TI_FTW3_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 Ti FTW3 Ultra Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, EVGA_SUB_VEN, EVGA_RTX3090TI_FTW3_ULTRA_GAMING_SUB_DEV, 0x2D); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 3090 Ti FTW3 Ultra Hybrid Gaming" , DetectEVGAAmpereGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, EVGA_SUB_VEN, EVGA_RTX3090TI_FTW3_ULTRA_HYBRID_GAMING_SUB_DEV,0x2D); diff --git a/Controllers/EVGAAmpereGPUController/EVGAGPUv3Controller.cpp b/Controllers/EVGAAmpereGPUController/EVGAGPUv3Controller.cpp new file mode 100644 index 0000000..4501608 --- /dev/null +++ b/Controllers/EVGAAmpereGPUController/EVGAGPUv3Controller.cpp @@ -0,0 +1,523 @@ +/*---------------------------------------------------------*\ +| EVGAGPUv3Controller.cpp | +| | +| Driver for EVGA V3 (Ampere) GPU | +| | +| TheRogueZeta 15 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "EVGAGPUv3Controller.h" +#include "LogManager.h" + +EVGAGPUv3Controller::EVGAGPUv3Controller(i2c_smbus_interface* bus, evga_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +EVGAGPUv3Controller::~EVGAGPUv3Controller() +{ + +} + +std::string EVGAGPUv3Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string EVGAGPUv3Controller::GetDeviceName() +{ + return(name); +} + +void EVGAGPUv3Controller::GetDeviceModes() +{ + LOG_DEBUG("[%s] Getting Zone and LED count from HW", name.c_str()); + uint8_t data_pkt[I2C_SMBUS_BLOCK_MAX] = {}; + + uint8_t result = bus->i2c_smbus_read_i2c_block_data(dev, EVGA_GPU_V3_REG_MODE, 10, data_pkt); + if (result == 10) + { + for(uint8_t zone = 0 ; zone < 4; zone ++) + { + zone_modes[zone] = data_pkt[zone + 1]; + zone_led_count[zone] = data_pkt[zone + 5]; + LOG_DEBUG("[%s] Zone %1d LED count: %02d, mode: %02d", name.c_str(), zone + 1, zone_led_count[zone], zone_modes[zone]); + } + zone_sync = data_pkt[9]; + LOG_DEBUG("[%s] Zone Sync is %1d", name.c_str(), zone_sync); + } + else + { + LOG_DEBUG("[%s] Invalid block read result: %02d", name.c_str(), result); + memset(zone_led_count, 0, sizeof(zone_led_count)); + memset(zone_modes, 0, sizeof(zone_modes)); + } + initCard(); +} + +std::string EVGAGPUv3Controller::GetFWVersion() +{ + return(fwVersion); +} + +std::string EVGAGPUv3Controller::ReadFWVersion() +{ + LOG_TRACE("[%s] Getting FW from HW", name.c_str()); + uint8_t data_pkt[I2C_SMBUS_BLOCK_MAX] = {}; + std::string return_string = ""; + char version[10]; + + uint8_t result = bus->i2c_smbus_read_i2c_block_data(dev, EVGA_GPU_V3_REG_FIRMWARE, 6, data_pkt); + if (result == 6) + { + uint8_t major = data_pkt[4]; + uint8_t minor = data_pkt[5]; + + snprintf(version, 10, "1.%02d.%02d", major, minor); + return_string.append(version); + LOG_TRACE("[%s] Firmware %s", name.c_str(), version); + fwVersion = return_string; + return(return_string); + } + else + { + return ""; + } +} + +uint8_t EVGAGPUv3Controller::GetZoneMode(uint8_t zone) +{ + return zone_modes[zone]; +} + +EVGAv3_config EVGAGPUv3Controller::GetZoneConfig(uint8_t zone, uint8_t mode) +{ + EVGAv3_config zone_config; + u16_to_u8 speed16; + bool readFail = false; + + zone_config.brightness = EVGA_GPU_V3_BRIGHTNESS_DEFAULT; + zone_config.direction = 0; + zone_config.numberOfColors = 0; + zone_config.speed = EVGA_GPU_V3_SPEED_GENERIC_NORMAL; + + LOG_DEBUG("[%s] Retriving Zone %1d config for mode %1d from HW", name.c_str(), zone, mode); + uint8_t data_pkt[I2C_SMBUS_BLOCK_MAX] = {}; + + switch (mode) + { + case EVGA_GPU_V3_MODE_STATIC: + { + uint8_t result = bus->i2c_smbus_read_i2c_block_data(dev, EVGA_GPU_V3_REG_STATIC + zone, 5, data_pkt); + if (result == 5) + { + //Load data + zone_config.brightness = data_pkt[1]; + uint8_t red = data_pkt[2]; + uint8_t green = data_pkt[3]; + uint8_t blue = data_pkt[4]; + zone_config.colors[0] = ToRGBColor(red, green, blue); + zone_config.numberOfColors = 1; + zone_config.speed = 0; + zone_config.direction = 0; + } + else + { + readFail = true; + } + } + break; + + case EVGA_GPU_V3_MODE_BREATHING: + { + uint8_t result = bus->i2c_smbus_read_i2c_block_data(dev, EVGA_GPU_V3_REG_BREATHING + zone, 10, data_pkt); + if (result == 10) + { + //Load data + zone_config.brightness = data_pkt[1]; + uint8_t red1 = data_pkt[2]; + uint8_t green1 = data_pkt[3]; + uint8_t blue1 = data_pkt[4]; + zone_config.colors[0] = ToRGBColor(red1, green1, blue1); + uint8_t red2 = data_pkt[5]; + uint8_t green2 = data_pkt[6]; + uint8_t blue2 = data_pkt[7]; + zone_config.colors[1] = ToRGBColor(red2, green2, blue2); + zone_config.numberOfColors= (zone_config.colors[1] != 0 ) ? 2 : 1 ; + speed16.lsb = data_pkt[8]; + speed16.msb = data_pkt[9]; + zone_config.speed = speed16.u16; + zone_config.direction = 0; + } + else + { + readFail = true; + } + } + break; + + case EVGA_GPU_V3_MODE_RAINBOW: + case EVGA_GPU_V3_MODE_RAINBOW_WAVE: + case EVGA_GPU_V3_MODE_STAR: + { + uint8_t result = bus->i2c_smbus_read_i2c_block_data(dev, EVGA_GPU_V3_REG_STATIC + ((mode -1) * 4) + zone, 4, data_pkt); + if (result == 4) + { + //Load data + zone_config.brightness = data_pkt[1]; + zone_config.numberOfColors = 0; + speed16.lsb = data_pkt[2]; + speed16.msb = data_pkt[3]; + zone_config.speed = speed16.u16; + } + else + { + readFail = true; + } + } + break; + + case EVGA_GPU_V3_MODE_WAVE: + { + uint8_t result = bus->i2c_smbus_read_i2c_block_data(dev, EVGA_GPU_V3_REG_STATIC + ((mode -1) * 4) + zone, 7, data_pkt); + if (result == 7) + { + //Load data + zone_config.brightness = data_pkt[1]; + uint8_t red = data_pkt[2]; + uint8_t green = data_pkt[3]; + uint8_t blue = data_pkt[4]; + zone_config.colors[0] = ToRGBColor(red, green, blue); + zone_config.numberOfColors = 1; + speed16.lsb = data_pkt[5]; + speed16.msb = data_pkt[6]; + zone_config.speed = speed16.u16; + } + else + { + readFail = true; + } + } + break; + + case EVGA_GPU_V3_MODE_COLOR_CYCLE: + case EVGA_GPU_V3_MODE_COLOR_STACK: + { + uint8_t cd_pkt[5]; + + if(mode == EVGA_GPU_V3_MODE_COLOR_STACK) + { + uint8_t color_count = bus->i2c_smbus_read_i2c_block_data(dev, EVGA_GPU_V3_REG_COLOR_STACK_COLOR_COUNT, 5, cd_pkt); + + if(color_count == 5) + { + zone_config.numberOfColors = cd_pkt[zone + 1]; + } + else + { + readFail = true; + } + + uint8_t direction = bus->i2c_smbus_read_i2c_block_data(dev, EVGA_GPU_V3_REG_COLOR_STACK_DIRECTION, 5, cd_pkt); + if(direction == 5) + { + zone_config.direction = cd_pkt[zone + 1]; + } + else + { + readFail = true; + } + + } + else + { + uint8_t color_count = bus->i2c_smbus_read_i2c_block_data(dev, EVGA_GPU_V3_REG_COLOR_CYCLE_COUNT, 5, cd_pkt); + + if(color_count == 5) + { + zone_config.numberOfColors = cd_pkt[zone + 1]; + } + else + { + readFail = true; + } + } + + uint8_t result = bus->i2c_smbus_read_i2c_block_data(dev, EVGA_GPU_V3_REG_STATIC + ((mode -1) * 4) + zone, 31, data_pkt); + if (result == 31) + { + zone_config.brightness = data_pkt[1]; + for(uint8_t color_index = 0; color_index < zone_config.numberOfColors; color_index++) + { + uint8_t red = data_pkt[(color_index * 4) + 2]; + uint8_t green = data_pkt[(color_index * 4) + 3]; + uint8_t blue = data_pkt[(color_index * 4) + 4]; + zone_config.colors[color_index] = ToRGBColor(red, green, blue); + } + speed16.lsb = data_pkt[29]; + speed16.msb = data_pkt[30]; + zone_config.speed = speed16.u16; + } + else + { + readFail = true; + } + } + break; + + default: + break; + } + + if(readFail == false) + { + LOG_TRACE("[%s] Zone %1d Brightness: 0x%02X, Colors: %1d, Speed: 0x%04X, Direction %1d.", name.c_str(), zone, zone_config.brightness, zone_config.numberOfColors, zone_config.speed, zone_config.direction); + for(uint8_t color_index = 0; color_index < zone_config.numberOfColors; color_index++) + { + LOG_TRACE("[%s] Color Index [%2d]: 0x%06X", name.c_str(), color_index, zone_config.colors[color_index]); + } + LOG_DEBUG("[%s] Done loading Zone %1d configuration from HW", name.c_str(), zone); + } + else + { + zone_config.direction = 0; + zone_config.numberOfColors = 0; + for(uint8_t i = 0; i < 7; i++) + { + zone_config.colors[i] = 0; + } + LOG_DEBUG("[%s] Failed while loading Zone %1d configuration from HW", name.c_str(), zone); + } + return zone_config; +} + +void EVGAGPUv3Controller::initCard() +{ + // This command needs to be sent before the card will respond to OpenRGB commands + // NvAPI_I2CWriteEx: Dev: 0x2D RegSize: 0x01 Reg: 0xB2 Size: 0x05 Data: 0x04 0xC6 0xEB 0xEA 0x15 + uint8_t data_pkt[5] = {0x04, 0xC6, 0xEB, 0xEA, 0x15}; + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_ENABLE, sizeof(data_pkt), data_pkt); + LOG_TRACE("[%s] Sending SW int packet", name.c_str()); + return; +} + +void EVGAGPUv3Controller::SaveConfig() +{ + LOG_DEBUG("[%s] Sending save packet", name.c_str()); + + //NvAPI_I2CWriteEx: Dev: 0x2D RegSize: 0x01 Reg: 0x90 Size: 0x05 Data: 0x04 0x9E 0xEB 0x00 0x90 //Sent on close of PX1 + uint8_t data_pkt[5] = {0x04, 0x9E, 0xEB, 0x00, 0x90}; + + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_SAVE, sizeof(data_pkt), data_pkt); + return; +} + +void EVGAGPUv3Controller::ResizeARGB(uint8_t newSize) +{ + if(newSize < EVGAGPUV3_LEDS_MIN) + { + newSize = EVGAGPUV3_LEDS_MIN; + } + else if(newSize > EVGAGPUV3_LEDS_MAX) + { + newSize = EVGAGPUV3_LEDS_MAX; + } + + LOG_DEBUG("[%s] Resizing ARGB header with %02d size", name.c_str(), newSize); + uint8_t data_pkt[EVGAGPUV3_MODE_PACKET_SIZE] = { 0x09, EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, + EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, newSize, EVGAGPUV3_INIT}; + + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_MODE, sizeof(data_pkt), data_pkt); + return; +} + +void EVGAGPUv3Controller::SetAllModes(uint8_t zone_0_mode, uint8_t zone_1_mode,uint8_t zone_2_mode,uint8_t zone_3_mode, bool sync) +{ + uint8_t mode_pkt[EVGAGPUV3_MODE_PACKET_SIZE] = { EVGAGPUV3_MODE_PACKET_SIZE - 1, + EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, + EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, 0x0 }; + + // Hack to keep card in sync after power loss (standby) without requireing a rescan. + // To be replaced with code that calls this once upon system resume once OpenRGB + // knows about system power states. + initCard(); + + // Keep zone_modes in sync + zone_modes[0] = zone_0_mode; + zone_modes[1] = zone_1_mode; + zone_modes[2] = zone_2_mode; + zone_modes[3] = zone_3_mode; + + // Prep packet + mode_pkt[1] = zone_0_mode; + mode_pkt[2] = zone_1_mode; + mode_pkt[3] = zone_2_mode; + mode_pkt[4] = zone_3_mode; + mode_pkt[9] = sync; + + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_MODE, EVGAGPUV3_MODE_PACKET_SIZE, mode_pkt); + //LOG_TRACE("[%s] Setting all zones to mode: %02d, %2d, %2d, %2d, zone sync %1d.", name.c_str(), zone_0_mode, zone_1_mode, zone_2_mode, zone_3_mode, sync); +} + +void EVGAGPUv3Controller::SetZoneMode(uint8_t zone, uint8_t mode) +{ + uint8_t mode_pkt[EVGAGPUV3_MODE_PACKET_SIZE] = { EVGAGPUV3_MODE_PACKET_SIZE - 1, + EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, + EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, 0x0 }; + + // Hack to keep card in sync after power loss (standby) without requireing a rescan. + // To be replaced with code that calls this once upon system resume once OpenRGB + // knows about system power states. + initCard(); + + // Keep zone_modes in sync + zone_modes[zone] = mode; + // Prep packet + mode_pkt[zone + 1] = mode; + + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_MODE, EVGAGPUV3_MODE_PACKET_SIZE, mode_pkt); + //LOG_TRACE("[%s] Setting individual zone %1d to mode %02d", name.c_str(), zone, mode); +} + +void EVGAGPUv3Controller::SetZone(uint8_t zone, uint8_t mode, EVGAv3_config zone_config) +{ + std::string mode_name; + u16_to_u8 speed16 = { (uint16_t) zone_config.speed }; + + switch (mode) + { + case EVGA_GPU_V3_MODE_OFF: + break; + + case EVGA_GPU_V3_MODE_STATIC: + { + uint8_t zone_pkt[5] = {EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT, EVGAGPUV3_INIT}; + + zone_pkt[0] = sizeof(zone_pkt) - 1; + zone_pkt[1] = zone_config.brightness; + zone_pkt[2] = RGBGetRValue(zone_config.colors[0]); + zone_pkt[3] = RGBGetGValue(zone_config.colors[0]); + zone_pkt[4] = RGBGetBValue(zone_config.colors[0]); + + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_STATIC + zone, sizeof(zone_pkt), zone_pkt); + } + break; + + case EVGA_GPU_V3_MODE_BREATHING: + { + uint8_t zone_pkt[10]; + memset(zone_pkt, EVGAGPUV3_INIT, sizeof(zone_pkt)); + + zone_pkt[0] = sizeof(zone_pkt) - 1; + zone_pkt[1] = zone_config.brightness; + zone_pkt[2] = RGBGetRValue(zone_config.colors[0]); + zone_pkt[3] = RGBGetGValue(zone_config.colors[0]); + zone_pkt[4] = RGBGetBValue(zone_config.colors[0]); + if(zone_config.numberOfColors == 1) + { + zone_config.colors[1] = 0; + } + zone_pkt[5] = RGBGetRValue(zone_config.colors[1]); + zone_pkt[6] = RGBGetGValue(zone_config.colors[1]); + zone_pkt[7] = RGBGetBValue(zone_config.colors[1]); + zone_pkt[8] = speed16.lsb; + zone_pkt[9] = speed16.msb; + + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_STATIC + ((mode -1) * 4) + zone, sizeof(zone_pkt), zone_pkt); + } + break; + + case EVGA_GPU_V3_MODE_RAINBOW: + case EVGA_GPU_V3_MODE_RAINBOW_WAVE: + case EVGA_GPU_V3_MODE_STAR: + { + uint8_t zone_pkt[4]; + memset(zone_pkt, EVGAGPUV3_INIT, sizeof(zone_pkt)); + + zone_pkt[0] = sizeof(zone_pkt) - 1; + zone_pkt[1] = zone_config.brightness; + zone_pkt[2] = speed16.lsb; + zone_pkt[3] = speed16.msb; + + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_STATIC + ((mode -1) * 4) + zone, sizeof(zone_pkt), zone_pkt); + } + break; + + case EVGA_GPU_V3_MODE_WAVE: + { + uint8_t zone_pkt[7]; + memset(zone_pkt, EVGAGPUV3_INIT, sizeof(zone_pkt)); + + zone_pkt[0] = sizeof(zone_pkt) - 1; + zone_pkt[1] = zone_config.brightness; + zone_pkt[2] = RGBGetRValue(zone_config.colors[0]); + zone_pkt[3] = RGBGetGValue(zone_config.colors[0]); + zone_pkt[4] = RGBGetBValue(zone_config.colors[0]); + zone_pkt[5] = speed16.lsb; + zone_pkt[6] = speed16.msb; + + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_STATIC + ((mode -1) * 4) + zone, sizeof(zone_pkt), zone_pkt); + } + break; + + case EVGA_GPU_V3_MODE_COLOR_CYCLE: + case EVGA_GPU_V3_MODE_COLOR_STACK: + { + uint8_t zone_pkt[31];; + memset(zone_pkt, EVGAGPUV3_INIT, sizeof(zone_pkt)); + uint8_t color_cnt_pkt[5]; + memset(color_cnt_pkt, EVGAGPUV3_INIT, sizeof(color_cnt_pkt)); + + // Zone packet construction + zone_pkt[0] = sizeof(zone_pkt) - 1; + for(uint8_t color_index = 0; color_index < zone_config.numberOfColors; color_index++) + { + zone_pkt[1 + color_index * 4] = zone_config.brightness; + zone_pkt[2 + color_index * 4] = RGBGetRValue(zone_config.colors[color_index]); + zone_pkt[3 + color_index * 4] = RGBGetGValue(zone_config.colors[color_index]); + zone_pkt[4 + color_index * 4] = RGBGetBValue(zone_config.colors[color_index]); + } + zone_pkt[29] = speed16.lsb; + zone_pkt[30] = speed16.msb; + + // Color Count packet construction + color_cnt_pkt[0] = sizeof(color_cnt_pkt) - 1; + color_cnt_pkt[zone+1] = zone_config.numberOfColors; + + if(mode == EVGA_GPU_V3_MODE_COLOR_STACK) + { + uint8_t direction_pkt[5]; + memset(direction_pkt, EVGAGPUV3_INIT, sizeof(direction_pkt)); + // Direction packet construction + direction_pkt[0] = sizeof(direction_pkt) - 1; + direction_pkt[zone+1] = zone_config.direction; + + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_STATIC + ((mode -1) * 4) + zone, sizeof(zone_pkt), zone_pkt); + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_COLOR_STACK_COLOR_COUNT, sizeof(color_cnt_pkt), color_cnt_pkt); + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_COLOR_STACK_DIRECTION, sizeof(direction_pkt), direction_pkt); + } + else + { + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_STATIC + ((mode -1) * 4) + zone, sizeof(zone_pkt), zone_pkt); + bus->i2c_smbus_write_i2c_block_data(dev, EVGA_GPU_V3_REG_COLOR_CYCLE_COUNT, sizeof(color_cnt_pkt), color_cnt_pkt); + } + } + break; + default: + { + LOG_TRACE("[%s] Mode %02d not found", name.c_str(), mode); + } + break; + } +} diff --git a/Controllers/EVGAAmpereGPUController/EVGAGPUv3Controller.h b/Controllers/EVGAAmpereGPUController/EVGAGPUv3Controller.h new file mode 100644 index 0000000..a6715dc --- /dev/null +++ b/Controllers/EVGAAmpereGPUController/EVGAGPUv3Controller.h @@ -0,0 +1,130 @@ +/*---------------------------------------------------------*\ +| EVGAGPUv3Controller.h | +| | +| Driver for EVGA V3 (Ampere) GPU | +| | +| TheRogueZeta 15 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +typedef unsigned char evga_dev_id; + +#define SPEED_MULTIPLIER 10 +#define EVGAGPUV3_MODE_PACKET_SIZE 10 +#define EVGAGPUV3_LEDS_MIN 01 +#define EVGAGPUV3_LEDS_MAX 60 +#define EVGAGPUV3_INIT 0xFF +#define EVGAGPUV3_CONTROLLER_NAME "EVGAv3" + +union u16_to_u8 +{ + uint16_t u16; + struct + { + uint8_t lsb; + uint8_t msb; + }; +}; + +struct EVGAv3_config +{ + uint8_t brightness; + RGBColor colors[7]; + uint8_t numberOfColors; + uint16_t speed; + uint8_t direction; +}; + +enum //Control registers and offsets +{ + EVGA_GPU_V3_REG_FIRMWARE = 0xB1, + EVGA_GPU_V3_REG_ENABLE = 0xB2, + EVGA_GPU_V3_REG_MODE = 0xC0, + EVGA_GPU_V3_OFFSET_ZONE_1 = 0x00, + EVGA_GPU_V3_OFFSET_ZONE_2 = 0x01, + EVGA_GPU_V3_OFFSET_ZONE_3 = 0x02, + EVGA_GPU_V3_OFFSET_ZONE_4 = 0x03, + EVGA_GPU_V3_REG_STATIC = 0xC1, + EVGA_GPU_V3_REG_BREATHING = 0xC5, + EVGA_GPU_V3_REG_RAINBOW = 0xC9, + EVGA_GPU_V3_REG_COLOR_CYCLE = 0xCD, + EVGA_GPU_V3_REG_RAINBOW_WAVE = 0xD1, + EVGA_GPU_V3_REG_WAVE = 0xD5, + EVGA_GPU_V3_REG_STAR = 0xD9, + EVGA_GPU_V3_REG_COLOR_STACK = 0xDD, + EVGA_GPU_V3_REG_COLOR_CYCLE_COUNT = 0xE5, + EVGA_GPU_V3_REG_COLOR_STACK_COLOR_COUNT = 0xE6, + EVGA_GPU_V3_REG_COLOR_STACK_DIRECTION = 0xEB, + EVGA_GPU_V3_REG_SAVE = 0x90, +}; + +enum //Mode values for EVGA_GPU_V3_REG_MODE +{ + EVGA_GPU_V3_MODE_OFF = 0x00, + EVGA_GPU_V3_MODE_STATIC = 0x01, + EVGA_GPU_V3_MODE_BREATHING = 0x02, + EVGA_GPU_V3_MODE_RAINBOW = 0x03, + EVGA_GPU_V3_MODE_COLOR_CYCLE = 0x04, + EVGA_GPU_V3_MODE_RAINBOW_WAVE = 0x05, + EVGA_GPU_V3_MODE_WAVE = 0x06, + EVGA_GPU_V3_MODE_STAR = 0x07, + EVGA_GPU_V3_MODE_COLOR_STACK = 0x08, +}; + +enum // Value limits for speeds +{ + EVGA_GPU_V3_BRIGHTNESS_MIN = 0x0, + EVGA_GPU_V3_BRIGHTNESS_DEFAULT = 0xFF, + EVGA_GPU_V3_BRIGHTNESS_MAX = 0xFF, + EVGA_GPU_V3_SPEED_GENERIC_SLOWEST = 0x4E20, //20000 + EVGA_GPU_V3_SPEED_GENERIC_NORMAL = 0x1388, //5000 + EVGA_GPU_V3_SPEED_GENERIC_FASTEST = 0x03E8, //1000 + EVGA_GPU_V3_SPEED_WAVE_SLOWEST = 0x0148, //355 + EVGA_GPU_V3_SPEED_WAVE_NORMAL = 0x0028, //40 + EVGA_GPU_V3_SPEED_WAVE_FASTEST = 0x000A, //10 + EVGA_GPU_V3_SPEED_STAR_SLOWEST = 0x2710, //10000 + EVGA_GPU_V3_SPEED_STAR_NORMAL = 0x07D0, //2000 + EVGA_GPU_V3_SPEED_STAR_FASTEST = 0x01F4, //500 +}; + +class EVGAGPUv3Controller +{ +public: + EVGAGPUv3Controller(i2c_smbus_interface* bus, evga_dev_id dev, std::string dev_name); + ~EVGAGPUv3Controller(); + + uint8_t zone_led_count[4]; + uint8_t zone_modes[4]; + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetFWVersion(); + std::string ReadFWVersion(); + + void GetDeviceModes(); + uint8_t GetZoneMode(uint8_t zone); + EVGAv3_config GetZoneConfig(uint8_t zone, uint8_t mode); + void SaveConfig(); + + void ResizeARGB(uint8_t newSize); + void SetAllModes(uint8_t zone0, uint8_t zone1, uint8_t zone2, uint8_t zone3, bool sync); + void SetZoneMode(uint8_t zone, uint8_t mode); + void SetZone(uint8_t zone, uint8_t mode, EVGAv3_config zone_config); + +private: + i2c_smbus_interface* bus; + evga_dev_id dev; + bool zone_sync; + std::string fwVersion; + std::string name; + + void initCard(); +}; diff --git a/Controllers/EVGAAmpereGPUController/RGBController_EVGAGPUv3.cpp b/Controllers/EVGAAmpereGPUController/RGBController_EVGAGPUv3.cpp new file mode 100644 index 0000000..b51f31c --- /dev/null +++ b/Controllers/EVGAAmpereGPUController/RGBController_EVGAGPUv3.cpp @@ -0,0 +1,321 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAGPUv3.cpp | +| | +| RGBController for EVGA V3 (Ampere) GPU | +| | +| TheRogueZeta 15 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_EVGAGPUv3.h" +#include "LogManager.h" + +static const char* evga_v3_zone_names[] = +{ + "Front Logo", + "End plate Logo", + "Back Logo", + "Addressable Header" +}; + +/**------------------------------------------------------------------*\ + @name EVGA RGB v3 GPU + @category GPU + @type I2C + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectEVGAAmpereGPUControllers + @comment EVGA has not exposed a per LED control method yet so OpenRGB + is only able to set all LED's to a single color. +\*-------------------------------------------------------------------*/ + +RGBController_EVGAGPUv3::RGBController_EVGAGPUv3(EVGAGPUv3Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "EVGA"; + description = "EVGA Ampere RGB GPU Device"; + location = controller->GetDeviceLocation(); + version = controller->GetFWVersion(); + type = DEVICE_TYPE_GPU; + + mode Off; + Off.name = "Off"; + Off.value = EVGA_GPU_V3_MODE_OFF; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Direct"; + Static.value = EVGA_GPU_V3_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness_min = EVGA_GPU_V3_BRIGHTNESS_MIN; + Static.brightness = EVGA_GPU_V3_BRIGHTNESS_DEFAULT; + Static.brightness_max = EVGA_GPU_V3_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EVGA_GPU_V3_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.speed_min = EVGA_GPU_V3_SPEED_GENERIC_SLOWEST; + Breathing.speed = EVGA_GPU_V3_SPEED_GENERIC_NORMAL; + Breathing.speed_max = EVGA_GPU_V3_SPEED_GENERIC_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(1); + Breathing.brightness_min = EVGA_GPU_V3_BRIGHTNESS_MIN; + Breathing.brightness = EVGA_GPU_V3_BRIGHTNESS_DEFAULT; + Breathing.brightness_max = EVGA_GPU_V3_BRIGHTNESS_MAX; + modes.push_back(Breathing); + + mode Rainbow; + Rainbow.name = "Spectrum Cycle"; + Rainbow.value = EVGA_GPU_V3_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Rainbow.speed_min = EVGA_GPU_V3_SPEED_GENERIC_SLOWEST; + Rainbow.speed = EVGA_GPU_V3_SPEED_GENERIC_NORMAL; + Rainbow.speed_max = EVGA_GPU_V3_SPEED_GENERIC_FASTEST; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness_min = EVGA_GPU_V3_BRIGHTNESS_MIN; + Rainbow.brightness = EVGA_GPU_V3_BRIGHTNESS_DEFAULT; + Rainbow.brightness_max = EVGA_GPU_V3_BRIGHTNESS_MAX; + modes.push_back(Rainbow); + + mode Color_Cycle; + Color_Cycle.name = "Color Cycle"; + Color_Cycle.value = EVGA_GPU_V3_MODE_COLOR_CYCLE; + Color_Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Color_Cycle.speed_min = EVGA_GPU_V3_SPEED_GENERIC_SLOWEST; + Color_Cycle.speed = EVGA_GPU_V3_SPEED_GENERIC_NORMAL; + Color_Cycle.speed_max = EVGA_GPU_V3_SPEED_GENERIC_FASTEST; + Color_Cycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + Color_Cycle.colors_min = 2; + Color_Cycle.colors_max = 7; + Color_Cycle.colors.resize(2); + Color_Cycle.brightness_min = EVGA_GPU_V3_BRIGHTNESS_MIN; + Color_Cycle.brightness = EVGA_GPU_V3_BRIGHTNESS_DEFAULT; + Color_Cycle.brightness_max = EVGA_GPU_V3_BRIGHTNESS_MAX; + modes.push_back(Color_Cycle); + + mode Rainbow_Wave; + Rainbow_Wave.name = "Rainbow Wave"; + Rainbow_Wave.value = EVGA_GPU_V3_MODE_RAINBOW_WAVE; + Rainbow_Wave.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Rainbow_Wave.speed_min = EVGA_GPU_V3_SPEED_GENERIC_SLOWEST; + Rainbow_Wave.speed = EVGA_GPU_V3_SPEED_GENERIC_NORMAL; + Rainbow_Wave.speed_max = EVGA_GPU_V3_SPEED_GENERIC_FASTEST; + Rainbow_Wave.color_mode = MODE_COLORS_NONE; + Rainbow_Wave.brightness_min = EVGA_GPU_V3_BRIGHTNESS_MIN; + Rainbow_Wave.brightness = EVGA_GPU_V3_BRIGHTNESS_DEFAULT; + Rainbow_Wave.brightness_max = EVGA_GPU_V3_BRIGHTNESS_MAX; + modes.push_back(Rainbow_Wave); + + mode Wave; + Wave.name = "Wave"; + Wave.value = EVGA_GPU_V3_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Wave.speed_min = EVGA_GPU_V3_SPEED_WAVE_SLOWEST; + Wave.speed = EVGA_GPU_V3_SPEED_WAVE_NORMAL; + Wave.speed_max = EVGA_GPU_V3_SPEED_WAVE_FASTEST; + Wave.color_mode = MODE_COLORS_PER_LED; + Wave.brightness_min = EVGA_GPU_V3_BRIGHTNESS_MIN; + Wave.brightness = EVGA_GPU_V3_BRIGHTNESS_DEFAULT; + Wave.brightness_max = EVGA_GPU_V3_BRIGHTNESS_MAX; + modes.push_back(Wave); + + mode Star; + Star.name = "Star"; + Star.value = EVGA_GPU_V3_MODE_STAR; + Star.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Star.speed_min = EVGA_GPU_V3_SPEED_STAR_SLOWEST; + Star.speed = EVGA_GPU_V3_SPEED_STAR_NORMAL; + Star.speed_max = EVGA_GPU_V3_SPEED_STAR_FASTEST; + Star.color_mode = MODE_COLORS_NONE; + Star.brightness_min = EVGA_GPU_V3_BRIGHTNESS_MIN; + Star.brightness = EVGA_GPU_V3_BRIGHTNESS_DEFAULT; + Star.brightness_max = EVGA_GPU_V3_BRIGHTNESS_MAX; + modes.push_back(Star); + + mode Color_Stack; + Color_Stack.name = "Color Stack"; + Color_Stack.value = EVGA_GPU_V3_MODE_COLOR_STACK; + Color_Stack.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Color_Stack.speed_min = EVGA_GPU_V3_SPEED_WAVE_SLOWEST; + Color_Stack.speed = EVGA_GPU_V3_SPEED_WAVE_NORMAL; + Color_Stack.speed_max = EVGA_GPU_V3_SPEED_WAVE_FASTEST; + Color_Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + Color_Stack.colors_min = 2; + Color_Stack.colors_max = 7; + Color_Stack.colors.resize(2); + Color_Stack.brightness_min = EVGA_GPU_V3_BRIGHTNESS_MIN; + Color_Stack.brightness = EVGA_GPU_V3_BRIGHTNESS_DEFAULT; + Color_Stack.brightness_max = EVGA_GPU_V3_BRIGHTNESS_MAX; + modes.push_back(Color_Stack); + + + SetupZones(); + + // Initialize active mode + for( uint8_t zone_idx = 0; zone_idx < zoneIndexMap.size(); zone_idx++) + { + active_mode = controller->GetZoneMode(0); // Hard coding zone 0 until per zone modes are available. + + if(active_mode != EVGA_GPU_V3_MODE_OFF) + { + EVGAv3_config hw_config = controller->GetZoneConfig(zoneIndexMap[zone_idx], active_mode); + + /*---------------------------------------------------------*\ + | The LED color (color[0]) will always be set. Mode colors | + | are only set for the MODE_COLORS_MODE_SPECIFIC modes | + \*---------------------------------------------------------*/ + + zones[zone_idx].colors[0] = hw_config.colors[0]; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC && zone_idx == 0) // Hard coding zone 0 until per zone modes are available. + { + for( uint8_t j = 0 ; j < hw_config.numberOfColors; j ++) + { + if(modes[active_mode].colors.size() > j) + { + modes[active_mode].colors[j] = hw_config.colors[j]; + } + else + { + modes[active_mode].colors.push_back(hw_config.colors[j]); + } + } + } + + modes[active_mode].speed = hw_config.speed; + modes[active_mode].brightness = hw_config.brightness; + modes[active_mode].direction = hw_config.direction; + } + } +} + +RGBController_EVGAGPUv3::~RGBController_EVGAGPUv3() +{ + delete controller; +} + +uint8_t RGBController_EVGAGPUv3::getModeIndex(uint8_t mode_value) +{ + for(uint8_t mode_index = 0; mode_index < modes.size(); mode_index++) + { + if (modes[mode_index].value == mode_value) + { + return mode_index; + } + } + return 0; +} + +void RGBController_EVGAGPUv3::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only allows setting the entire zone for all | + | LED's in the zone and does not allow per LED control. | + | Resizing is only possible on zone 4, addressable header | + \*---------------------------------------------------------*/ + + controller->GetDeviceModes(); + + for(uint8_t zone_idx = 0; zone_idx < 4; zone_idx++) + { + if(controller->zone_led_count[zone_idx] > 0) + { + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = evga_v3_zone_names[zone_idx]; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = evga_v3_zone_names[zone_idx]; + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zones.push_back(*new_zone); + zoneIndexMap.push_back(zone_idx); + } + } + SetupColors(); +} + +void RGBController_EVGAGPUv3::ResizeZone(int /*zone*/, int newSize) +{ + controller->ResizeARGB(newSize); +} + +void RGBController_EVGAGPUv3::DeviceUpdateLEDs() +{ + /*---------------------------------------------------------*\ + | DeviceUpdateLEDs() is only used in MODE_COLORS_PER_LED | + | modes and as such colorB will always be black (0x000000) | + \*---------------------------------------------------------*/ + EVGAv3_config zone_config; + + zone_config.brightness = modes[active_mode].brightness; + zone_config.speed = modes[active_mode].speed; + zone_config.direction = modes[active_mode].direction; + zone_config.numberOfColors = (uint8_t) modes[active_mode].colors.size(); + + for(uint8_t zone_idx = 0; zone_idx < zoneIndexMap.size(); zone_idx++) + { + zone_config.colors[0] = colors[zone_idx]; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + for( uint8_t i = 0 ; i < zone_config.numberOfColors; i ++) + { + zone_config.colors[i] = modes[active_mode].colors[i]; + } + } + //LOG_TRACE("[%s] Updating LED %1d", controller->evgaGPUName, zone_idx); + controller->SetZone(zoneIndexMap[zone_idx], modes[active_mode].value, zone_config); + } +} + +void RGBController_EVGAGPUv3::UpdateZoneLEDs(int /*zone*/) +{ + //LOG_TRACE("[%s] Updating zone %1d", controller->evgaGPUName, zone); + DeviceUpdateLEDs(); +} + +void RGBController_EVGAGPUv3::UpdateSingleLED(int /*led*/) +{ + //LOG_TRACE("[%s] Updating single LED %1d", controller->evgaGPUName, led); + DeviceUpdateLEDs(); +} + +void RGBController_EVGAGPUv3::DeviceUpdateMode() +{ + /* Update all zone modes in a loop, each one with a packet to be use with per zone control + for(uint8_t zone = 0; zone < 4; zone++) + { + controller->SetZoneMode(zone, modes[active_mode].value); + } + */ + //LOG_TRACE("[%s] Updating to mode %1d", controller->evgaGPUName, modes[active_mode].value); + DeviceUpdateLEDs(); + controller->SetAllModes(modes[active_mode].value, modes[active_mode].value, modes[active_mode].value,modes[active_mode].value, true); //Set all zones to the same mode +} + +void RGBController_EVGAGPUv3::DeviceSaveMode() +{ + controller->SaveConfig(); +} diff --git a/Controllers/EVGAAmpereGPUController/RGBController_EVGAGPUv3.h b/Controllers/EVGAAmpereGPUController/RGBController_EVGAGPUv3.h new file mode 100644 index 0000000..66b40cf --- /dev/null +++ b/Controllers/EVGAAmpereGPUController/RGBController_EVGAGPUv3.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAGPUv3.h | +| | +| RGBController for EVGA V3 (Ampere) GPU | +| | +| TheRogueZeta 15 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "EVGAGPUv3Controller.h" + +class RGBController_EVGAGPUv3 : public RGBController +{ +public: + RGBController_EVGAGPUv3(EVGAGPUv3Controller* controller_ptr); + ~RGBController_EVGAGPUv3(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + EVGAGPUv3Controller* controller; + std::vector zoneIndexMap; + + uint8_t getModeIndex(uint8_t mode_value); +}; diff --git a/Controllers/EVGAGP102GPUController/EVGAGP102Controller.cpp b/Controllers/EVGAGP102GPUController/EVGAGP102Controller.cpp new file mode 100644 index 0000000..96cee24 --- /dev/null +++ b/Controllers/EVGAGP102GPUController/EVGAGP102Controller.cpp @@ -0,0 +1,138 @@ +/*---------------------------------------------------------*\ +| EVGAGP102Controller.cpp | +| | +| Driver for EVGA GP102 GPU | +| | +| Fabricio Murta (avengerx) 31 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "EVGAGP102Controller.h" +#include "LogManager.h" + +EVGAGP102Controller::EVGAGP102Controller(i2c_smbus_interface* bus_ptr, zoneinfo info, std::string dev_name) +{ + bus = bus_ptr; + zi = info; + name = dev_name; +} + +EVGAGP102Controller::~EVGAGP102Controller() +{ +} + +std::string EVGAGP102Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", zi.dev_addr); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string EVGAGP102Controller::GetDeviceName() +{ + return(name); +} + +std::string EVGAGP102Controller::GetZoneName() +{ + return(zi.zone_name); +} + +void EVGAGP102Controller::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + SendCommand(EVGA_GP102_CMD_BEGIN); + SendCommand(EVGA_GP102_CMD_COLOR); + + if (CommandAcknowledged()) + { + unsigned char rgb[] = { red, green, blue }; + + for (int i = 0; i < 3; i++) + { + bus->i2c_smbus_write_byte_data(zi.dev_addr, zi.color_addrs[i], rgb[i]); + } + + SendCommand(EVGA_GP102_CMD_END); + + if (!CommandCompleted()) + { + LOG_WARNING("[%s] Non-clear status report from hardware.", EVGA_GP102_CONTROLLER_NAME); + } + } +} +std::array EVGAGP102Controller::GetColor() +{ + return { GetRed(), GetGreen(), GetBlue() }; +} + +bool EVGAGP102Controller::IsValid() +{ + for (int i = 0; i < 3; i++) + { + unsigned char res = bus->i2c_smbus_read_byte_data(zi.dev_addr, EVGA_GP102_REG_VALID); + if (res == 0x1F || res == 0x91) + { + LOG_TRACE("[%s] Zone discovery successful on address: 0x%02X.", EVGA_GP102_CONTROLLER_NAME, zi.dev_addr); + return true; + } + LOG_DEBUG("[%s] Zone discovery failed on address: 0x%02X expected: 0x1F received: 0x%02X.", EVGA_GP102_CONTROLLER_NAME, zi.dev_addr, res); + } + return false; +} + +void EVGAGP102Controller::SetMode(unsigned char mode) +{ + bus->i2c_smbus_write_byte_data(zi.dev_addr, EVGA_GP102_REG_MODE, mode); +} + +unsigned char EVGAGP102Controller::GetMode() +{ + return(bus->i2c_smbus_read_byte_data(zi.dev_addr, EVGA_GP102_REG_MODE)); +} + +void EVGAGP102Controller::SendCommand(s32 command) +{ + bus->i2c_smbus_write_byte_data(zi.dev_addr, EVGA_GP102_REG_CMD, command); +} + +s32 EVGAGP102Controller::QueryCommand(s32 command) +{ + return bus->i2c_smbus_read_byte_data(zi.dev_addr, command); +} + +bool EVGAGP102Controller::CommandAcknowledged() +{ + return QueryCommand(EVGA_GP102_REG_CMD) == zi.resp_ready; +} + +bool EVGAGP102Controller::CommandCompleted() +{ + return QueryCommand(EVGA_GP102_REG_CMD) == zi.resp_clear; +} + +unsigned char EVGAGP102Controller::GetRed() +{ + return(bus->i2c_smbus_read_byte_data(zi.dev_addr, zi.color_addrs[EVGA_GP102_CIDX_RED])); +} + +unsigned char EVGAGP102Controller::GetGreen() +{ + return(bus->i2c_smbus_read_byte_data(zi.dev_addr, zi.color_addrs[EVGA_GP102_CIDX_GREEN])); +} + +unsigned char EVGAGP102Controller::GetBlue() +{ + return(bus->i2c_smbus_read_byte_data(zi.dev_addr, zi.color_addrs[EVGA_GP102_CIDX_BLUE])); +} + +void EVGAGP102Controller::SaveSettings() +{ + //Tested and not worked + //bus->i2c_smbus_write_byte_data(zi.dev_addr, 0x21, 0xE5); + //bus->i2c_smbus_write_byte_data(zi.dev_addr, 0x22, 0xE7); +} diff --git a/Controllers/EVGAGP102GPUController/EVGAGP102Controller.h b/Controllers/EVGAGP102GPUController/EVGAGP102Controller.h new file mode 100644 index 0000000..c13c70a --- /dev/null +++ b/Controllers/EVGAGP102GPUController/EVGAGP102Controller.h @@ -0,0 +1,111 @@ +/*---------------------------------------------------------*\ +| EVGAGP102Controller.h | +| | +| Driver for EVGA GP102 GPU | +| | +| Fabricio Murta (avengerx) 31 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "i2c_smbus.h" + +#define EVGA_GP102_CONTROLLER_NAME "EVGA GP102 Nvidia GPU" + +enum +{ + EVGA_GP102_REG_MODE = 0x0C, + EVGA_GP102_REG_CMD = 0x0E, + EVGA_GP102_REG_VALID = 0x04 +}; + +enum +{ + EVGA_GP102_MODE_OFF = 0x00, + EVGA_GP102_MODE_CUSTOM = 0x01 + // TODO: Other LEDSync modes (rainbow, breath, pulse) +}; + +enum +{ + EVGA_GP102_CIDX_RED = 0, + EVGA_GP102_CIDX_GREEN = 1, + EVGA_GP102_CIDX_BLUE = 2 +}; + +enum +{ + EVGA_GP102_CMD_BEGIN = 0xE5, + EVGA_GP102_CMD_COLOR = 0xE9, + EVGA_GP102_CMD_END = 0xE0 +}; + +typedef struct +{ + std::string zone_name; + s32 dev_addr; + s32 color_addrs[3]; + s32 resp_ready; + s32 resp_clear; +} zoneinfo; + +const static zoneinfo gpuzoneinfos[] +{ + { + "Nameplate", + 0x4A, + {0x09, 0x0A, 0x0B}, + 0x03, + 0x00 + }, + { + "Backplate", // for 1080Ti K|NGP|N + 0x2A, + {0x30, 0x31, 0x32}, + 0xE9, + 0xE0 + }, + { + "Backplate", // for 1080Ti FTW3 + 0x4F, + {0x30, 0x31, 0x32}, + 0x03, + 0x00 + }, +}; + +class EVGAGP102Controller +{ +public: + EVGAGP102Controller(i2c_smbus_interface* bus, zoneinfo info, std::string dev_name); + ~EVGAGP102Controller(); + + bool IsValid(); + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetZoneName(); + unsigned char GetMode(); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + std::array GetColor(); + void SetMode(unsigned char mode); + void SaveSettings(); + +private: + i2c_smbus_interface* bus; + zoneinfo zi; + std::string name; + + bool CommandAcknowledged(); + bool CommandCompleted(); + s32 QueryCommand(s32 command); + void SendCommand(s32 command); + unsigned char GetRed(); + unsigned char GetGreen(); + unsigned char GetBlue(); +}; diff --git a/Controllers/EVGAGP102GPUController/EVGAGP102GPUControllerDetect.cpp b/Controllers/EVGAGP102GPUController/EVGAGP102GPUControllerDetect.cpp new file mode 100644 index 0000000..133452b --- /dev/null +++ b/Controllers/EVGAGP102GPUController/EVGAGP102GPUControllerDetect.cpp @@ -0,0 +1,73 @@ +/*---------------------------------------------------------*\ +| EVGAGP102ControllerDetect.cpp | +| | +| Detector for EVGA GP102 GPU | +| | +| Fabricio Murta (avengerx) 31 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "EVGAGP102Controller.h" +#include "LogManager.h" +#include "RGBController_EVGAGP102.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* DetectEVGAGP102GPUControllers * +* * +* Detect EVGA GP102 GPU controllers on the enumerated I2C busses at address 0x49. * +* * +* bus - pointer to i2c_smbus_interface where EVGA GPU device is connected * +* address - unused, the address comes from the GPU zone info table * +* name - name string of detected PCI device * +* * +\******************************************************************************************/ + +void DetectEVGAGP102GPUControllers(i2c_smbus_interface* bus, uint8_t /*address*/, const std::string& name) +{ + if(bus->port_id == 1) + { + RGBController_EVGAGP102* new_rgbcontroller; + std::vector controllers; + + for(unsigned int i = 0; i < sizeof(gpuzoneinfos) / sizeof(zoneinfo); i++) + { + EVGAGP102Controller* controller = new EVGAGP102Controller(bus, gpuzoneinfos[i], name); + + if(controller->IsValid()) + { + controllers.push_back(controller); + } + else + { + delete controller; + } + } + + if(controllers.size() != 0) + { + new_rgbcontroller = new RGBController_EVGAGP102(controllers); + + ResourceManager::get()->RegisterRGBController(new_rgbcontroller); + } + } +} /* DetectEVGAGP102GPUControllers() */ + +/*---------------------------------------------------------*\ +| The I2C address is provided by the GPU Zone Info table, | +| as these GPUs have multiple I2C devices per card. | +\*---------------------------------------------------------*/ +REGISTER_I2C_PCI_DETECTOR( "EVGA GeForce GTX 1070 FTW2 Gaming", DetectEVGAGP102GPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, EVGA_SUB_VEN, EVGA_GTX1070_FTW2_SUB_DEV, 0x00 ); +REGISTER_I2C_PCI_DETECTOR( "EVGA GeForce GTX 1080 FTW2 Gaming", DetectEVGAGP102GPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, EVGA_SUB_VEN, EVGA_GTX1080_FTW2_SUB_DEV, 0x00 ); +REGISTER_I2C_PCI_DETECTOR( "EVGA GeForce GTX 1080 FTW2 11G", DetectEVGAGP102GPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, EVGA_SUB_VEN, EVGA_GTX1080_FTW2_11G_SUB_DEV, 0x00 ); +REGISTER_I2C_PCI_DETECTOR( "EVGA GeForce GTX 1080 FTW2 DT", DetectEVGAGP102GPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, EVGA_SUB_VEN, EVGA_GTX1080_FTW2_DT_SUB_DEV, 0x00 ); +REGISTER_I2C_PCI_DETECTOR( "EVGA GeForce GTX 1080 Ti SC2 Gaming", DetectEVGAGP102GPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, EVGA_SUB_VEN, EVGA_GTX1080TI_SC2_GAMING_SUB_DEV, 0x00 ); +REGISTER_I2C_PCI_DETECTOR( "EVGA GeForce GTX 1080 Ti FTW3", DetectEVGAGP102GPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, EVGA_SUB_VEN, EVGA_GTX1080TI_FTW3_SUB_DEV, 0x00 ); +REGISTER_I2C_PCI_DETECTOR( "EVGA GeForce GTX 1080 Ti FTW3 Hybrid", DetectEVGAGP102GPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, EVGA_SUB_VEN, EVGA_GTX1080TI_FTW3_HYBRID_SUB_DEV, 0x00 ); +REGISTER_I2C_PCI_DETECTOR( "EVGA GeForce GTX 1080 Ti K|NGP|N", DetectEVGAGP102GPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, EVGA_SUB_VEN, EVGA_GTX1080TI_KINGPIN_SUB_DEV, 0x00 ); diff --git a/Controllers/EVGAGP102GPUController/RGBController_EVGAGP102.cpp b/Controllers/EVGAGP102GPUController/RGBController_EVGAGP102.cpp new file mode 100644 index 0000000..8aacf5e --- /dev/null +++ b/Controllers/EVGAGP102GPUController/RGBController_EVGAGP102.cpp @@ -0,0 +1,154 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAGP102.cpp | +| | +| RGBController for EVGA GP102 GPU | +| | +| Fabricio Murta (avengerx) 31 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_EVGAGP102.h" + +/**------------------------------------------------------------------*\ + @name EVGA GP102 GPU + @category GPU + @type I2C + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectEVGAGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_EVGAGP102::RGBController_EVGAGP102(std::vector controller_list) +{ + controllers = controller_list; + + name = controllers[0]->GetDeviceName(); + vendor = "EVGA"; + description = "EVGA GP102-based RGB GPU Device"; + + for(unsigned int i = 0; i < zones.size(); i++) + { + location += controllers[i]->GetDeviceLocation() + " "; + } + + type = DEVICE_TYPE_GPU; + + mode Off; + Off.name = "Off"; + Off.value = EVGA_GP102_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = EVGA_GP102_MODE_CUSTOM; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + // Initialize active mode and stored color + + unsigned char raw_active_mode = controllers[0]->GetMode(); + + active_mode = 0; + for(unsigned int i = 0; i < modes.size(); i++) + { + if (modes[i].value == raw_active_mode) + { + active_mode = i; + break; + } + } + for(unsigned int i = 0; i < zones.size(); i++) + { + std::array rgb = controllers[i]->GetColor(); + + colors[i] = ToRGBColor(rgb[0], rgb[1], rgb[2]); + } +} + +RGBController_EVGAGP102::~RGBController_EVGAGP102() +{ + for(unsigned int i = 0; i < controllers.size(); i++) + { + delete controllers[i]; + } +} + +void RGBController_EVGAGP102::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device basically has two controllable zones, one at | + | the top of the board with GeForce 1080 Ti and another for | + | the backplate logo (K|NGP|N logo, or EVGA GeForce 1080 Ti | + | for the FTW3). + \*---------------------------------------------------------*/ + for(unsigned int i = 0; i < controllers.size(); i++) + { + zone new_zone; + led new_led; + + new_zone.name = controllers[i]->GetZoneName(); + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + + new_led.name = controllers[i]->GetZoneName(); + + leds.push_back(new_led); + zones.push_back(new_zone); + } + + SetupColors(); +} + +void RGBController_EVGAGP102::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_EVGAGP102::DeviceUpdateLEDs() +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + UpdateZoneLEDs(zone_idx); + } +} + +void RGBController_EVGAGP102::UpdateZoneLEDs(int zone) +{ + RGBColor color = colors[zone]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + controllers[zone]->SetColor(red, grn, blu); +} + +void RGBController_EVGAGP102::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_EVGAGP102::DeviceUpdateMode() +{ + for(unsigned int i = 0; i < controllers.size(); i++) + { + controllers[i]->SetMode((unsigned char)modes[(unsigned int)active_mode].value); + } +} + +void RGBController_EVGAGP102::DeviceSaveMode() +{ +} diff --git a/Controllers/EVGAGP102GPUController/RGBController_EVGAGP102.h b/Controllers/EVGAGP102GPUController/RGBController_EVGAGP102.h new file mode 100644 index 0000000..f7c4e78 --- /dev/null +++ b/Controllers/EVGAGP102GPUController/RGBController_EVGAGP102.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAGP102.h | +| | +| RGBController for EVGA GP102 GPU | +| | +| Fabricio Murta (avengerx) 31 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "EVGAGP102Controller.h" + +class RGBController_EVGAGP102 : public RGBController +{ +public: + RGBController_EVGAGP102(std::vector controller_list); + ~RGBController_EVGAGP102(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + std::vector controllers; +}; diff --git a/Controllers/EVGAPascalGPUController/EVGAGPUv1Controller.cpp b/Controllers/EVGAPascalGPUController/EVGAGPUv1Controller.cpp new file mode 100644 index 0000000..abf52d4 --- /dev/null +++ b/Controllers/EVGAPascalGPUController/EVGAGPUv1Controller.cpp @@ -0,0 +1,76 @@ +/*---------------------------------------------------------*\ +| EVGAGPUv1Controller.cpp | +| | +| Driver for EVGA V1 (Pascal) GPU | +| | +| Adam Honse (CalcProgrammer1) 11 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "EVGAGPUv1Controller.h" + +EVGAGPUv1Controller::EVGAGPUv1Controller(i2c_smbus_interface* bus, evga_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +EVGAGPUv1Controller::~EVGAGPUv1Controller() +{ + +} + +std::string EVGAGPUv1Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string EVGAGPUv1Controller::GetDeviceName() +{ + return(name); +} + +unsigned char EVGAGPUv1Controller::GetMode() +{ + return(bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V1_REG_MODE)); +} + +unsigned char EVGAGPUv1Controller::GetRed() +{ + return(bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V1_REG_RED)); +} + +unsigned char EVGAGPUv1Controller::GetGreen() +{ + return(bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V1_REG_GREEN)); +} + +unsigned char EVGAGPUv1Controller::GetBlue() +{ + return(bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V1_REG_BLUE)); +} + +void EVGAGPUv1Controller::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V1_REG_RED, red); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V1_REG_GREEN, green); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V1_REG_BLUE, blue); +} + +void EVGAGPUv1Controller::SetMode(unsigned char mode) +{ + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V1_REG_MODE, mode); +} + +void EVGAGPUv1Controller::SaveSettings() +{ + bus->i2c_smbus_write_byte_data(dev, 0x23, 0xE5); +} diff --git a/Controllers/EVGAPascalGPUController/EVGAGPUv1Controller.h b/Controllers/EVGAPascalGPUController/EVGAGPUv1Controller.h new file mode 100644 index 0000000..2fd3aab --- /dev/null +++ b/Controllers/EVGAPascalGPUController/EVGAGPUv1Controller.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| EVGAGPUv1Controller.h | +| | +| Driver for EVGA V1 (Pascal) GPU | +| | +| Adam Honse (CalcProgrammer1) 11 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char evga_dev_id; + +#define EVGAGPUV1_CONTROLLER_NAME "EVGAv1" + +enum +{ + EVGA_GPU_V1_REG_MODE = 0x0C, + EVGA_GPU_V1_REG_RED = 0x09, + EVGA_GPU_V1_REG_GREEN = 0x0A, + EVGA_GPU_V1_REG_BLUE = 0x0B, +}; + +enum +{ + EVGA_GPU_V1_MODE_OFF = 0x00, + EVGA_GPU_V1_MODE_CUSTOM = 0x01, + EVGA_GPU_V1_MODE_RAINBOW = 0x02, + EVGA_GPU_V1_MODE_BREATHING = 0x05, +}; + +class EVGAGPUv1Controller +{ +public: + EVGAGPUv1Controller(i2c_smbus_interface* bus, evga_dev_id dev, std::string dev_name); + ~EVGAGPUv1Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + unsigned char GetMode(); + unsigned char GetRed(); + unsigned char GetGreen(); + unsigned char GetBlue(); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + void SetMode(unsigned char mode); + void SaveSettings(); + +private: + i2c_smbus_interface* bus; + evga_dev_id dev; + std::string name; +}; diff --git a/Controllers/EVGAPascalGPUController/EVGAPascalGPUControllerDetect.cpp b/Controllers/EVGAPascalGPUController/EVGAPascalGPUControllerDetect.cpp new file mode 100644 index 0000000..a4a02eb --- /dev/null +++ b/Controllers/EVGAPascalGPUController/EVGAPascalGPUControllerDetect.cpp @@ -0,0 +1,45 @@ +/*---------------------------------------------------------*\ +| EVGAGPUv1ControllerDetect.cpp | +| | +| Detector for EVGA V1 (Pascal) GPU | +| | +| Adam Honse (CalcProgrammer1) 11 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "EVGAGPUv1Controller.h" +#include "LogManager.h" +#include "RGBController_EVGAGPUv1.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* DetectEVGAGPUControllers * +* * +* Detect EVGA Pascal GPU controllers on the enumerated I2C busses at address 0x49. * +* * +* bus - pointer to i2c_smbus_interface where EVGA GPU device is connected * +* dev - I2C address of EVGA GPU device * +* * +\******************************************************************************************/ + +void DetectEVGAPascalGPUControllers(i2c_smbus_interface* bus, uint8_t address, const std::string& name) +{ + if(bus->port_id == 1) + { + EVGAGPUv1Controller* controller = new EVGAGPUv1Controller(bus, address, name); + RGBController_EVGAGPUv1* rgb_controller = new RGBController_EVGAGPUv1(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectEVGAPascalGPUControllers() */ + +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce GTX 1070 FTW DT Gaming", DetectEVGAPascalGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, EVGA_SUB_VEN, EVGA_GTX1070_FTW_DT_GAMING_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce GTX 1070 FTW", DetectEVGAPascalGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, EVGA_SUB_VEN, EVGA_GTX1070_FTW_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce GTX 1070 FTW HYBRID", DetectEVGAPascalGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, EVGA_SUB_VEN, EVGA_GTX1070_FTW_HYBRID_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce GTX 1070 Ti FTW2", DetectEVGAPascalGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070TI_DEV, EVGA_SUB_VEN, EVGA_GTX1070TI_FTW2_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce GTX 1080 FTW", DetectEVGAPascalGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, EVGA_SUB_VEN, EVGA_GTX1080_FTW_SUB_DEV, 0x49); diff --git a/Controllers/EVGAPascalGPUController/RGBController_EVGAGPUv1.cpp b/Controllers/EVGAPascalGPUController/RGBController_EVGAGPUv1.cpp new file mode 100644 index 0000000..b155dae --- /dev/null +++ b/Controllers/EVGAPascalGPUController/RGBController_EVGAGPUv1.cpp @@ -0,0 +1,154 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAGPUv1.cpp | +| | +| RGBController for EVGA V1 (Pascal) GPU | +| | +| Adam Honse (CalcProgrammer1) 11 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_EVGAGPUv1.h" + +/**------------------------------------------------------------------*\ + @name EVGA RGB v1 GPU + @category GPU + @type I2C + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectEVGAGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_EVGAGPUv1::RGBController_EVGAGPUv1(EVGAGPUv1Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "EVGA"; + description = "EVGA RGB v1 GPU Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Off; + Off.name = "Off"; + Off.value = EVGA_GPU_V1_MODE_OFF; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = EVGA_GPU_V1_MODE_CUSTOM; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = EVGA_GPU_V1_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_MANUAL_SAVE; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EVGA_GPU_V1_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + SetupZones(); + + // Initialize active mode and stored color + + unsigned char raw_active_mode = controller->GetMode(); + + active_mode = 0; + for(unsigned int i = 0; i < modes.size(); i++) + { + if (modes[i].value == raw_active_mode) + { + active_mode = i; + break; + } + } + + unsigned char r = controller->GetRed(); + unsigned char g = controller->GetGreen(); + unsigned char b = controller->GetBlue(); + + RGBColor color = ToRGBColor(r, g, b); + colors[0] = color; +} + +RGBController_EVGAGPUv1::~RGBController_EVGAGPUv1() +{ + delete controller; +} + +void RGBController_EVGAGPUv1::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has one LED, so create a single zone and | + | LED for it | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = "GPU Zone"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = "GPU LED"; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zones.push_back(*new_zone); + + SetupColors(); +} + +void RGBController_EVGAGPUv1::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_EVGAGPUv1::DeviceUpdateLEDs() +{ + RGBColor color = colors[0]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetColor(red, grn, blu); +} + +void RGBController_EVGAGPUv1::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_EVGAGPUv1::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_EVGAGPUv1::DeviceUpdateMode() +{ + controller->SetMode((unsigned char)modes[(unsigned int)active_mode].value); +} + +void RGBController_EVGAGPUv1::DeviceSaveMode() +{ + controller->SaveSettings(); +} diff --git a/Controllers/EVGAPascalGPUController/RGBController_EVGAGPUv1.h b/Controllers/EVGAPascalGPUController/RGBController_EVGAGPUv1.h new file mode 100644 index 0000000..d3431ea --- /dev/null +++ b/Controllers/EVGAPascalGPUController/RGBController_EVGAGPUv1.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAGPUv1.h | +| | +| RGBController for EVGA V1 (Pascal) GPU | +| | +| Adam Honse (CalcProgrammer1) 11 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "EVGAGPUv1Controller.h" + +class RGBController_EVGAGPUv1 : public RGBController +{ +public: + RGBController_EVGAGPUv1(EVGAGPUv1Controller* controller_ptr); + ~RGBController_EVGAGPUv1(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + EVGAGPUv1Controller* controller; +}; diff --git a/Controllers/EVGASMBusController/EVGAACX30SMBusController.cpp b/Controllers/EVGASMBusController/EVGAACX30SMBusController.cpp new file mode 100644 index 0000000..7350027 --- /dev/null +++ b/Controllers/EVGASMBusController/EVGAACX30SMBusController.cpp @@ -0,0 +1,109 @@ +/*---------------------------------------------------------*\ +| EVGAACX30SMBusController.cpp | +| | +| Driver for SMBus EVGA ACX 30 motherboards | +| | +| Balázs Triszka (balika011) 21 May 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "EVGAACX30SMBusController.h" +#include "dmiinfo.h" +#include "LogManager.h" + +using namespace std::chrono_literals; + +EVGAACX30SMBusController::EVGAACX30SMBusController(i2c_smbus_interface *bus, uint8_t dev) +{ + this->bus = bus; + this->dev = dev; + + DMIInfo dmi; + + device_name = "EVGA " + dmi.getMainboard(); +} + +EVGAACX30SMBusController::~EVGAACX30SMBusController() +{ + +} + +std::string EVGAACX30SMBusController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string EVGAACX30SMBusController::GetDeviceName() +{ + return(device_name); +} + +std::string EVGAACX30SMBusController::GetFirmwareVersion() +{ + uint16_t version = bus->i2c_smbus_read_byte_data(dev, ACX30_REG_VER_HIGH) << 8 | bus->i2c_smbus_read_byte_data(dev, ACX30_REG_VER_LOW); + uint8_t ptype = bus->i2c_smbus_read_byte_data(dev, ACX30_REG_PTYPE); + + char ver[9]; + snprintf(ver, 9, "0x%X", version); + char pt[9]; + snprintf(pt, 9, "0x%X", ptype); + + std::string return_string; + return_string.append(ver); + return_string.append(", ptype "); + return_string.append(pt); + return return_string; +} + +uint8_t EVGAACX30SMBusController::GetMode() +{ + return bus->i2c_smbus_read_byte_data(dev, ACX30_REG_MODE); +} + +void EVGAACX30SMBusController::Unlock() +{ + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_CONTROL, 0xE5); + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_CONTROL, 0xE9); + bus->i2c_smbus_read_byte_data(dev, ACX30_REG_CONTROL); +} + +void EVGAACX30SMBusController::Lock() +{ + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_CONTROL, 0xE0); + bus->i2c_smbus_read_byte_data(dev, ACX30_REG_CONTROL); +} + +void EVGAACX30SMBusController::SetColors(uint8_t red, uint8_t green, uint8_t blue) +{ + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_RED, red); + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_GREEN, green); + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_BLUE, blue); +} + +void EVGAACX30SMBusController::SetMode(uint8_t mode) +{ + if (mode == ACX30_MODE_OFF) + { + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_21, 0xE7); + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_22, 0xCE); + } + else + { + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_21, 0xE5); + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_22, 0xE7); + } + + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_MODE, mode); +} + +void EVGAACX30SMBusController::SetSpeed(uint8_t speed) +{ + bus->i2c_smbus_write_byte_data(dev, ACX30_REG_SPEED, speed); +} diff --git a/Controllers/EVGASMBusController/EVGAACX30SMBusController.h b/Controllers/EVGASMBusController/EVGAACX30SMBusController.h new file mode 100644 index 0000000..867e31f --- /dev/null +++ b/Controllers/EVGASMBusController/EVGAACX30SMBusController.h @@ -0,0 +1,76 @@ +/*---------------------------------------------------------*\ +| EVGAACX30SMBusController.h | +| | +| Driver for SMBus EVGA ACX 30 motherboards | +| | +| Balázs Triszka (balika011) 21 May 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +enum +{ + /*------------------------------------------------------------------------------------------*\ + | Acx30 Common Registers | + \*------------------------------------------------------------------------------------------*/ + ACX30_REG_01 = 0x01, + ACX30_REG_PTYPE = 0x03, + ACX30_REG_VER_LOW = 0x04, + ACX30_REG_VER_HIGH = 0x05, + ACX30_REG_RED = 0x09, + ACX30_REG_GREEN = 0x0A, + ACX30_REG_BLUE = 0x0B, + ACX30_REG_MODE = 0x0C, + ACX30_REG_CONTROL = 0x0E, + ACX30_REG_SPEED = 0x19, + ACX30_REG_20 = 0x20, + ACX30_REG_21 = 0x21, + ACX30_REG_22 = 0x22, +}; + +/*----------------------------------------------------------------------------------------------*\ +| Definitions for Acx30 | +\*----------------------------------------------------------------------------------------------*/ + +enum +{ + ACX30_MODE_OFF = 0x00, /* OFF mode */ + ACX30_MODE_STATIC = 0x01, /* Static color mode */ + ACX30_MODE_SPECTRUM_CYCLE = 0x02, /* Spectrum Cycle effect mode */ + ACX30_MODE_BREATHING = 0x05, /* Breathing effect mode */ +}; + +enum +{ + ACX30_SPEED_MIN = 0x00, /* Slowest speed */ + ACX30_SPEED_DEFAULT = 0x04, /* Default speed */ + ACX30_SPEED_MAX = 0xFF, /* Fastest speed */ +}; + +class EVGAACX30SMBusController +{ +public: + EVGAACX30SMBusController(i2c_smbus_interface *bus, uint8_t dev); + ~EVGAACX30SMBusController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetFirmwareVersion(); + uint8_t GetMode(); + void Unlock(); + void Lock(); + void SetColors(uint8_t red, uint8_t green, uint8_t blue); + void SetMode(uint8_t mode); + void SetSpeed(uint8_t speed); + +private: + std::string device_name; + i2c_smbus_interface* bus; + uint8_t dev; +}; diff --git a/Controllers/EVGASMBusController/EVGASMBusControllerDetect.cpp b/Controllers/EVGASMBusController/EVGASMBusControllerDetect.cpp new file mode 100644 index 0000000..f82b5ea --- /dev/null +++ b/Controllers/EVGASMBusController/EVGASMBusControllerDetect.cpp @@ -0,0 +1,87 @@ +/*---------------------------------------------------------*\ +| EVGASMBusControllerDetect.cpp | +| | +| Detector for SMBus EVGA ACX 30 motherboards | +| | +| Balázs Triszka (balika011) 21 May 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "EVGAACX30SMBusController.h" +#include "LogManager.h" +#include "RGBController_EVGAACX30SMBus.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* TestForAcx30SMBusController * +* * +* Tests the given address to see if an EVGA ACX 30 controller exists there. * +* First does a quick write to test for a response * +* Then checks if it has 1st bit set in register 1 * +* * +\******************************************************************************************/ + +#define EVGA_DETECTOR_NAME "EVGA SMBus Detectector" +#define VENDOR_NAME "EVGA" +#define SMBUS_ADDRESS 0x28 + +bool TestForAcx30SMBusController(i2c_smbus_interface *bus, uint8_t address) +{ + bool pass = false; + + int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE); + + if (res >= 0) + { + res = bus->i2c_smbus_read_byte_data(address, 0x01); + + if (res > 0 && (res & 1)) + { + pass = true; + } + } + + return(pass); +} /* TestForAcx30SMBusController() */ + +/******************************************************************************************\ +* * +* DetectAcx30SMBusControllers * +* * +* Detect EVGA ACX 30 SMBus controllers on the enumerated I2C busses at address 0x28. * +* * +\******************************************************************************************/ + +void DetectAcx30SMBusControllers(std::vector &busses) +{ + for(unsigned int bus = 0; bus < busses.size(); bus++) + { + IF_MOBO_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + if(busses[bus]->pci_subsystem_vendor == EVGA_SUB_VEN) + { + LOG_DEBUG(SMBUS_CHECK_DEVICE_MESSAGE_EN, EVGA_DETECTOR_NAME, bus, VENDOR_NAME, SMBUS_ADDRESS); + // Check for ACX 30 controller at 0x28 + if(TestForAcx30SMBusController(busses[bus], SMBUS_ADDRESS)) + { + EVGAACX30SMBusController *controller = new EVGAACX30SMBusController(busses[bus], SMBUS_ADDRESS); + RGBController_EVGAACX30SMBus *rgb_controller = new RGBController_EVGAACX30SMBus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + else + { + LOG_DEBUG(SMBUS_CHECK_DEVICE_FAILURE_EN, EVGA_DETECTOR_NAME, bus, VENDOR_NAME); + } + } + } +} /* DetectAcx30SMBusControllers() */ + +REGISTER_I2C_DETECTOR("EVGA Motherboard SMBus Controllers", DetectAcx30SMBusControllers); diff --git a/Controllers/EVGASMBusController/RGBController_EVGAACX30SMBus.cpp b/Controllers/EVGASMBusController/RGBController_EVGAACX30SMBus.cpp new file mode 100644 index 0000000..5947696 --- /dev/null +++ b/Controllers/EVGASMBusController/RGBController_EVGAACX30SMBus.cpp @@ -0,0 +1,163 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAACX30SMBus.cpp | +| | +| RGBController for SMBus EVGA ACX 30 motherboards | +| | +| Balázs Triszka (balika011) 21 May 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_EVGAACX30SMBus.h" + +/**------------------------------------------------------------------*\ + @name EVGA ACX 30 + @category Motherboard + @type SMBus + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectAcx30SMBusControllers + @comment EVGA ACX 30 LED controllers will save with each update. + Per ARGB LED support is not possible with these devices. +\*-------------------------------------------------------------------*/ + +RGBController_EVGAACX30SMBus::RGBController_EVGAACX30SMBus(EVGAACX30SMBusController *controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "EVGA"; + version = controller->GetFirmwareVersion(); + type = DEVICE_TYPE_MOTHERBOARD; + description = "EVGA ACX 30 LED Device"; + location = controller->GetDeviceLocation(); + active_mode = controller->GetMode(); + + mode Off; + Off.name = "Off"; + Off.value = ACX30_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = ACX30_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = ACX30_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.speed_min = ACX30_SPEED_MIN; + SpectrumCycle.speed_max = ACX30_SPEED_MAX; + SpectrumCycle.speed = ACX30_SPEED_DEFAULT; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ACX30_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = ACX30_SPEED_MIN; + Breathing.speed_max = ACX30_SPEED_MAX; + Breathing.speed = ACX30_SPEED_DEFAULT; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_EVGAACX30SMBus::~RGBController_EVGAACX30SMBus() +{ + delete controller; +} + +void RGBController_EVGAACX30SMBus::SetupZones() +{ + /*---------------------------------------------------------*\ + | Acx30 motherboards only have a single zone/LED | + \*---------------------------------------------------------*/ + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + + /*---------------------------------------------------------*\ + | Set single zone name to "Motherboard" | + \*---------------------------------------------------------*/ + new_zone->name = "Motherboard"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + /*---------------------------------------------------------*\ + | Push new zone to zones vector | + \*---------------------------------------------------------*/ + zones.push_back(*new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + led* new_led = new led(); + + /*---------------------------------------------------------*\ + | Set single LED name to "Motherboard" | + \*---------------------------------------------------------*/ + new_led->name = "Motherboard"; + + /*---------------------------------------------------------*\ + | Push new LED to LEDs vector | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + + SetupColors(); +} + +void RGBController_EVGAACX30SMBus::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_EVGAACX30SMBus::DeviceUpdateLEDs() +{ + for(unsigned int led = 0; led < colors.size(); led++) + { + UpdateSingleLED(led); + } +} + +void RGBController_EVGAACX30SMBus::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_EVGAACX30SMBus::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + controller->Unlock(); + controller->SetColors(red, grn, blu); + controller->Lock(); +} + +void RGBController_EVGAACX30SMBus::DeviceUpdateMode() +{ + controller->Unlock(); + controller->SetMode(modes[active_mode].value); + controller->SetSpeed(modes[active_mode].speed); + controller->Lock(); + + DeviceUpdateLEDs(); +} diff --git a/Controllers/EVGASMBusController/RGBController_EVGAACX30SMBus.h b/Controllers/EVGASMBusController/RGBController_EVGAACX30SMBus.h new file mode 100644 index 0000000..394e107 --- /dev/null +++ b/Controllers/EVGASMBusController/RGBController_EVGAACX30SMBus.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAACX30SMBus.h | +| | +| RGBController for SMBus EVGA ACX 30 motherboards | +| | +| Balázs Triszka (balika011) 21 May 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "EVGAACX30SMBusController.h" + +class RGBController_EVGAACX30SMBus : public RGBController +{ +public: + RGBController_EVGAACX30SMBus(EVGAACX30SMBusController* controller_ptr); + ~RGBController_EVGAACX30SMBus(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + EVGAACX30SMBusController *controller; +}; diff --git a/Controllers/EVGATuringGPUController/EVGAGPUv2Controller.cpp b/Controllers/EVGATuringGPUController/EVGAGPUv2Controller.cpp new file mode 100644 index 0000000..9959494 --- /dev/null +++ b/Controllers/EVGATuringGPUController/EVGAGPUv2Controller.cpp @@ -0,0 +1,264 @@ +/*---------------------------------------------------------*\ +| EVGAGPUv2Controller.cpp | +| | +| Driver for EVGA V2 (Turing) GPU | +| | +| TheRogueZeta 15 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "EVGAGPUv2Controller.h" + +EVGAGPUv2Controller::EVGAGPUv2Controller(i2c_smbus_interface* bus, evga_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +EVGAGPUv2Controller::~EVGAGPUv2Controller() +{ + +} + +std::string EVGAGPUv2Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string EVGAGPUv2Controller::GetDeviceName() +{ + return(name); +} + +unsigned char EVGAGPUv2Controller::GetBrightnessA() +{ + return(bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_COLOR_A_BRIGHTNESS)); +} + +RGBColor EVGAGPUv2Controller::GetColorA() +{ + int red = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_COLOR_A_RED); + int green = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_COLOR_A_GREEN); + int blue = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_COLOR_A_BLUE); + return(ToRGBColor(red, green, blue)); +} + +RGBColor EVGAGPUv2Controller::GetColorB() +{ + int red = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_COLOR_B_RED); + int green = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_COLOR_B_GREEN); + int blue = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_COLOR_B_BLUE); + return(ToRGBColor(red, green, blue)); +} + +unsigned char EVGAGPUv2Controller::GetMode() +{ + unsigned char return_mode = 0; + unsigned char mode = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_MODE); + + if(mode == 0xFF) + { + //Registers may not ready after saving config. Read again if 0xFF. + mode = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_MODE); + } + + switch (mode) + { + case EVGA_GPU_V2_MODE_OFF: + { + return_mode = EVGA_GPU_V2_RGB_MODE_OFF; + } + break; + + case EVGA_GPU_V2_MODE_STATIC: + { + return_mode = EVGA_GPU_V2_RGB_MODE_STATIC; + } + break; + + case EVGA_GPU_V2_MODE_RAINBOW: + { + return_mode = EVGA_GPU_V2_RGB_MODE_RAINBOW; + } + break; + + case EVGA_GPU_V2_MODE_BREATHING: + { + u16_to_u8 speed_16 = { (uint16_t) 0 }; + + speed_16.lsb = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_B_TO_A_SPEED_LSB); + speed_16.msb = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_B_TO_A_SPEED_MSB); + + if (speed_16.u16 == 0) + { + return_mode = EVGA_GPU_V2_RGB_MODE_PULSE; + } + else + { + return_mode = EVGA_GPU_V2_RGB_MODE_BREATHING; + } + } + break; + + default: + break; + } + + return(return_mode); +} + +unsigned char EVGAGPUv2Controller::GetSpeed() +{ + u16_to_u8 speed_16 = { (uint16_t) 0 }; + + speed_16.lsb = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_COLOR_A_ONTIME_LSB); + speed_16.msb = bus->i2c_smbus_read_byte_data(dev, EVGA_GPU_V2_REG_COLOR_A_ONTIME_MSB); + + speed_16.u16 /= SPEED_MULTIPLIER; + + return (unsigned char) speed_16.u16; +} + +void EVGAGPUv2Controller::SetMode(uint8_t mode, RGBColor color1, RGBColor color2, uint16_t speed, uint8_t brightness) +{ + + EnableWrite(true); + switch (mode) + { + case EVGA_GPU_V2_RGB_MODE_OFF: + { + SendMode(EVGA_GPU_V2_MODE_OFF); + } + break; + + case EVGA_GPU_V2_RGB_MODE_STATIC: + { + SendMode(EVGA_GPU_V2_MODE_STATIC); + SendColor(EVGA_GPU_V2_REG_COLOR_A_RED, RGBGetRValue(color1), RGBGetGValue(color1), RGBGetBValue(color1), brightness); + } + break; + + case EVGA_GPU_V2_RGB_MODE_RAINBOW: + { + SendMode(EVGA_GPU_V2_MODE_RAINBOW); + SendBrightness(brightness); //Default = 0x64 + // Set Rainbow speed? No control in the GUI but this register is only set in Ranbow mode. + bus->i2c_smbus_write_byte_data(dev, 0x19, 0x11);; + } + break; + + case EVGA_GPU_V2_RGB_MODE_BREATHING: + case EVGA_GPU_V2_RGB_MODE_PULSE: + { + SendMode(EVGA_GPU_V2_MODE_BREATHING); + + /*---------------------------------------------------------*\ + | It is expected that color2 will be 0x000000 (black) for | + | 1 color mode otherwise set correctly therfore no further | + | inspection is required. | + \*---------------------------------------------------------*/ + + SendColor(EVGA_GPU_V2_REG_COLOR_A_RED, RGBGetRValue(color1), RGBGetGValue(color1), RGBGetBValue(color1), brightness); + SendColor(EVGA_GPU_V2_REG_COLOR_B_RED, RGBGetRValue(color2), RGBGetGValue(color2), RGBGetBValue(color2), brightness); + + /*-----------------------------------------------------------------*\ + | Breathing mode speeds are consistent for B_TO_A and A_TO_B | + | Pulse (Blink) mode is on/off ergo B_TO_A and A_TO_B = 0 (instant) | + \*-----------------------------------------------------------------*/ + u16_to_u8 speed_16 = { (uint16_t) (speed * SPEED_MULTIPLIER) }; + u16_to_u8 rise_fall_un_16 = { (mode == EVGA_GPU_V2_RGB_MODE_PULSE) ? (uint16_t) 0 : speed_16.u16 }; + SendSpeed(speed_16, speed_16, rise_fall_un_16, rise_fall_un_16, rise_fall_un_16); + + // 0x61 = 0x01 + bus->i2c_smbus_write_byte_data(dev, 0x61, 0x01); + // 0x6A and 0x6B = 0x00 + bus->i2c_smbus_write_byte_data(dev, 0x6A, 0x00); + bus->i2c_smbus_write_byte_data(dev, 0x6B, 0x00); + } + break; + + default: + break; + } + + //Disable writes + EnableWrite(false); +} + +void EVGAGPUv2Controller::EnableWrite(bool boolEnable) +{ + if(boolEnable) + { + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xE5); + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xE9); + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xF5); + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xF9); + } + else + { + bus->i2c_smbus_write_byte_data(dev, 0x08, 0x01); + //Dissable commands + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xF0); + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xE0); + } +} + +void EVGAGPUv2Controller::SaveSettings() +{ + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xE5); + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xE9); + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xF0); + bus->i2c_smbus_write_byte_data(dev, 0x1F, 0xE5); + bus->i2c_smbus_write_byte_data(dev, 0x23, 0xE5); + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xE0); + bus->i2c_smbus_write_byte_data(dev, 0x0E, 0xE0); +} + +void EVGAGPUv2Controller::SendBrightness(uint8_t brightness) +{ + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_COLOR_A_BRIGHTNESS, brightness); +} + +void EVGAGPUv2Controller::SendColor(uint8_t start_register, uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness) +{ + bus->i2c_smbus_write_byte_data(dev, start_register, red); + bus->i2c_smbus_write_byte_data(dev, (start_register + 1), green); + bus->i2c_smbus_write_byte_data(dev, (start_register + 2), blue); + bus->i2c_smbus_write_byte_data(dev, (start_register + 3), brightness); +} + +void EVGAGPUv2Controller::SendMode(uint8_t mode) +{ + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_MODE, mode); +} + +void EVGAGPUv2Controller::SendSpeed(u16_to_u8 aOnTime, u16_to_u8 bOnTime, u16_to_u8 b2a, u16_to_u8 a2b, u16_to_u8 speed_un) +{ + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_COLOR_B_UN_LSB, (unsigned char) speed_un.lsb ); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_COLOR_B_UN_MSB, (unsigned char) speed_un.msb ); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_B_TO_A_SPEED_LSB, (unsigned char) b2a.lsb ); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_B_TO_A_SPEED_MSB, (unsigned char) b2a.msb ); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_COLOR_B_ONTIME_LSB, (unsigned char) bOnTime.lsb ); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_COLOR_B_ONTIME_MSB, (unsigned char) bOnTime.msb ); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_A_TO_B_SPEED_LSB, (unsigned char) a2b.lsb ); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_A_TO_B_SPEED_MSB, (unsigned char) a2b.msb ); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_COLOR_A_ONTIME_LSB, (unsigned char) aOnTime.lsb ); + bus->i2c_smbus_write_byte_data(dev, EVGA_GPU_V2_REG_COLOR_A_ONTIME_MSB, (unsigned char) aOnTime.msb ); +} + +void EVGAGPUv2Controller::SetColor(RGBColor colorA, RGBColor colorB, uint8_t brightness) +{ + EnableWrite(true); + SendColor(EVGA_GPU_V2_REG_COLOR_A_RED, RGBGetRValue(colorA), RGBGetGValue(colorA), RGBGetBValue(colorA), brightness); + SendColor(EVGA_GPU_V2_REG_COLOR_B_RED, RGBGetRValue(colorB), RGBGetGValue(colorB), RGBGetBValue(colorB), brightness); + EnableWrite(false); +} diff --git a/Controllers/EVGATuringGPUController/EVGAGPUv2Controller.h b/Controllers/EVGATuringGPUController/EVGAGPUv2Controller.h new file mode 100644 index 0000000..916f72b --- /dev/null +++ b/Controllers/EVGATuringGPUController/EVGAGPUv2Controller.h @@ -0,0 +1,115 @@ +/*---------------------------------------------------------*\ +| EVGAGPUv2Controller.h | +| | +| Driver for EVGA V2 (Turing) GPU | +| | +| TheRogueZeta 15 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +typedef unsigned char evga_dev_id; + +#define SPEED_MULTIPLIER 10 +#define EVGA_GPU_V2_BRIGHTNESS_MIN 0x01 +#define EVGA_GPU_V2_BRIGHTNESS_DEFAULT 0x64 +#define EVGA_GPU_V2_BRIGHTNESS_MAX 0x64 +#define EVGAGPUV2_CONTROLLER_NAME "EVGAv2" + +union u16_to_u8 +{ + uint16_t u16; + struct + { + uint8_t lsb; + uint8_t msb; + }; +}; + +enum +{ + EVGA_GPU_V2_REG_MODE = 0x60, + EVGA_GPU_V2_REG_A_TO_B_SPEED_LSB = 0x62, + EVGA_GPU_V2_REG_A_TO_B_SPEED_MSB = 0x63, + EVGA_GPU_V2_REG_B_TO_A_SPEED_LSB = 0x64, + EVGA_GPU_V2_REG_B_TO_A_SPEED_MSB = 0x65, + EVGA_GPU_V2_REG_COLOR_A_ONTIME_LSB = 0x66, + EVGA_GPU_V2_REG_COLOR_A_ONTIME_MSB = 0x67, + EVGA_GPU_V2_REG_COLOR_B_ONTIME_LSB = 0x68, + EVGA_GPU_V2_REG_COLOR_B_ONTIME_MSB = 0x69, + EVGA_GPU_V2_REG_COLOR_A_RED = 0x6C, + EVGA_GPU_V2_REG_COLOR_A_GREEN = 0x6D, + EVGA_GPU_V2_REG_COLOR_A_BLUE = 0x6E, + EVGA_GPU_V2_REG_COLOR_A_BRIGHTNESS = 0x6F, + EVGA_GPU_V2_REG_COLOR_B_RED = 0x70, + EVGA_GPU_V2_REG_COLOR_B_GREEN = 0x71, + EVGA_GPU_V2_REG_COLOR_B_BLUE = 0x72, + EVGA_GPU_V2_REG_COLOR_B_BRIGHTNESS = 0x73, + EVGA_GPU_V2_REG_COLOR_B_UN_LSB = 0x74, + EVGA_GPU_V2_REG_COLOR_B_UN_MSB = 0x75, +}; + +enum +{ + EVGA_GPU_V2_RGB_MODE_OFF = 0x00, + EVGA_GPU_V2_RGB_MODE_STATIC = 0x01, + EVGA_GPU_V2_RGB_MODE_RAINBOW = 0x02, + EVGA_GPU_V2_RGB_MODE_BREATHING = 0x03, + EVGA_GPU_V2_RGB_MODE_PULSE = 0x04, +}; + +enum +{ + EVGA_GPU_V2_MODE_OFF = 0x00, + EVGA_GPU_V2_MODE_STATIC = 0x01, + EVGA_GPU_V2_MODE_RAINBOW = 0x0F, + EVGA_GPU_V2_MODE_BREATHING = 0x22, +}; + +enum +{ + EVGA_GPU_V2_SPEED_BREATHING_SLOWEST = 0x7D, + EVGA_GPU_V2_SPEED_BREATHING_NORMAL = 0x4B, + EVGA_GPU_V2_SPEED_BREATHING_FASTEST = 0x19, + EVGA_GPU_V2_SPEED_PULSE_SLOWEST = 0xFA, + EVGA_GPU_V2_SPEED_PULSE_NORMAL = 0x96, + EVGA_GPU_V2_SPEED_PULSE_FASTEST = 0x32, +}; + +class EVGAGPUv2Controller +{ +public: + EVGAGPUv2Controller(i2c_smbus_interface* bus, evga_dev_id dev, std::string dev_name); + ~EVGAGPUv2Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + unsigned char GetBrightnessA(); + RGBColor GetColorA(); + RGBColor GetColorB(); + unsigned char GetMode(); + unsigned char GetSpeed(); + + void SetColor(RGBColor colorA, RGBColor colorB, uint8_t brightness); + void SetMode(uint8_t mode, RGBColor color1, RGBColor color2, uint16_t speed, uint8_t brightness); + void SaveSettings(); + +private: + void EnableWrite(bool enable); + void SendBrightness(uint8_t brightness); + void SendColor(uint8_t start_register, uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness); + void SendMode(uint8_t mode); + void SendSpeed(u16_to_u8 aOnTime, u16_to_u8 bOnTime, u16_to_u8 b2a, u16_to_u8 a2b, u16_to_u8 speed_un); + + i2c_smbus_interface* bus; + evga_dev_id dev; + std::string name; +}; diff --git a/Controllers/EVGATuringGPUController/EVGATuringGPUControllerDetect.cpp b/Controllers/EVGATuringGPUController/EVGATuringGPUControllerDetect.cpp new file mode 100644 index 0000000..9484b69 --- /dev/null +++ b/Controllers/EVGATuringGPUController/EVGATuringGPUControllerDetect.cpp @@ -0,0 +1,65 @@ +/*---------------------------------------------------------*\ +| EVGATuringGPUControllerDetect.cpp | +| | +| Detector for EVGA V2 (Turing) GPU | +| | +| TheRogueZeta 15 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "EVGAGPUv2Controller.h" +#include "RGBController_EVGAGPUv2.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* DetectEVGATuringGPUControllers * +* * +* Detect EVGA Turing GPU controllers on the enumerated I2C busses at address 0x49. * +* * +* bus - pointer to i2c_smbus_interface where EVGA GPU device is connected * +* dev - I2C address of EVGA GPU device * +* * +\******************************************************************************************/ + +void DetectEVGATuringGPUControllers(i2c_smbus_interface* bus, uint8_t address, const std::string& name) +{ + if(bus->port_id == 1) + { + EVGAGPUv2Controller* controller = new EVGAGPUv2Controller(bus, address, name); + RGBController_EVGAGPUv2* rgb_controller = new RGBController_EVGAGPUv2(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectEVGATuringGPUControllers() */ + +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2070 XC Black" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_DEV, EVGA_SUB_VEN, EVGA_RTX2070_XC_BLACK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2070 XC Gaming" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, EVGA_SUB_VEN, EVGA_RTX2070_XC_GAMING_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2070 XC OC" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, EVGA_SUB_VEN, EVGA_RTX2070_XC_OC_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2070 FTW3 Ultra OC" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, EVGA_SUB_VEN, EVGA_RTX2070_FTW3_ULTRA_OC_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2070 SUPER XC Gaming" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, EVGA_SUB_VEN, EVGA_RTX2070S_XC_GAMING_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2070 SUPER XC Ultra" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, EVGA_SUB_VEN, EVGA_RTX2070S_XC_ULTRA_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2070 SUPER XC Ultra+" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, EVGA_SUB_VEN, EVGA_RTX2070S_XC_ULTRA_PLUS_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2070 SUPER FTW3 Ultra" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, EVGA_SUB_VEN, EVGA_RTX2070S_FTW3_ULTRA_OC_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2070 SUPER FTW3 Ultra+" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, EVGA_SUB_VEN, EVGA_RTX2070S_FTW3_ULTRA_PLUS_OC_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 Black" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_DEV, EVGA_SUB_VEN, EVGA_RTX2080_BLACK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 XC Black" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, EVGA_SUB_VEN, EVGA_RTX2080_XC_BLACK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 XC Gaming" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, EVGA_SUB_VEN, EVGA_RTX2080_XC_GAMING_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 XC Ultra Gaming" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, EVGA_SUB_VEN, EVGA_RTX2080_XC_ULTRA_GAMING_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 XC Hybrid Gaming" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, EVGA_SUB_VEN, EVGA_RTX2080_XC_HYBRID_GAMING_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 SUPER XC Gaming" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, EVGA_SUB_VEN, EVGA_RTX2080S_XC_GAMING_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 SUPER XC Ultra" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, EVGA_SUB_VEN, EVGA_RTX2080S_XC_ULTRA_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 SUPER XC Hybrid Gaming" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, EVGA_SUB_VEN, EVGA_RTX2080S_XC_HYBRID_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 SUPER FTW3 Ultra" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, EVGA_SUB_VEN, EVGA_RTX2080S_FTW3_ULTRA_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 SUPER FTW3 Hybrid OC" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, EVGA_SUB_VEN, EVGA_RTX2080S_FTW3_HYBRID_OC_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 SUPER FTW3 Ultra Hydro Copper" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, EVGA_SUB_VEN, EVGA_RTX2080S_FTW3_ULTRA_HC_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 Ti Black" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_DEV, EVGA_SUB_VEN, EVGA_RTX2080TI_BLACK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 Ti XC Ultra" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, EVGA_SUB_VEN, EVGA_RTX2080TI_XC_ULTRA_GAMING_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 Ti XC HYBRID GAMING" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, EVGA_SUB_VEN, EVGA_RTX2080TI_XC_HYBRID_GAMING_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 Ti XC HYDRO COPPER" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_DEV, EVGA_SUB_VEN, EVGA_RTX2080TI_XC_HYDRO_COPPER_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 Ti FTW3 Ultra" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, EVGA_SUB_VEN, EVGA_RTX2080TI_FTW3_ULTRA_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("EVGA GeForce RTX 2080 Ti FTW3 Ultra Hydro Copper" , DetectEVGATuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, EVGA_SUB_VEN, EVGA_RTX2080TI_FTW3_ULTRA_HYDRO_COPPER_SUB_DEV, 0x49); diff --git a/Controllers/EVGATuringGPUController/RGBController_EVGAGPUv2.cpp b/Controllers/EVGATuringGPUController/RGBController_EVGAGPUv2.cpp new file mode 100644 index 0000000..4e25fe0 --- /dev/null +++ b/Controllers/EVGATuringGPUController/RGBController_EVGAGPUv2.cpp @@ -0,0 +1,225 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAGPUv2.cpp | +| | +| RGBController for EVGA V2 (Turing) GPU | +| | +| TheRogueZeta 15 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_EVGAGPUv2.h" + +/**------------------------------------------------------------------*\ + @name EVGA RGB v2 GPU + @category GPU + @type I2C + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectEVGATuringGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_EVGAGPUv2::RGBController_EVGAGPUv2(EVGAGPUv2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "EVGA"; + description = "EVGA Turing RGB GPU Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Off; + Off.name = "Off"; + Off.value = EVGA_GPU_V2_RGB_MODE_OFF; + Off.flags = 0; //pretty sure not needed + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Direct"; + Static.value = EVGA_GPU_V2_RGB_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness_min = EVGA_GPU_V2_BRIGHTNESS_MIN; + Static.brightness = EVGA_GPU_V2_BRIGHTNESS_DEFAULT; + Static.brightness_max = EVGA_GPU_V2_BRIGHTNESS_MAX; + + modes.push_back(Static); + + mode Rainbow; + Rainbow.name = "Spectrum Cycle"; + Rainbow.value = EVGA_GPU_V2_RGB_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness_min = EVGA_GPU_V2_BRIGHTNESS_MIN; + Rainbow.brightness = EVGA_GPU_V2_BRIGHTNESS_DEFAULT; + Rainbow.brightness_max = EVGA_GPU_V2_BRIGHTNESS_MAX; + modes.push_back(Rainbow); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EVGA_GPU_V2_RGB_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE; + Breathing.speed_min = EVGA_GPU_V2_SPEED_BREATHING_SLOWEST; + Breathing.speed = EVGA_GPU_V2_SPEED_BREATHING_NORMAL; + Breathing.speed_max = EVGA_GPU_V2_SPEED_BREATHING_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.brightness_min = EVGA_GPU_V2_BRIGHTNESS_MIN; + Breathing.brightness = EVGA_GPU_V2_BRIGHTNESS_DEFAULT; + Breathing.brightness_max = EVGA_GPU_V2_BRIGHTNESS_MAX; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Pulse; + Pulse.name = "Flashing"; + Pulse.value = EVGA_GPU_V2_RGB_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE; + Pulse.speed_min = EVGA_GPU_V2_SPEED_PULSE_SLOWEST; + Pulse.speed = EVGA_GPU_V2_SPEED_PULSE_NORMAL; + Pulse.speed_max = EVGA_GPU_V2_SPEED_PULSE_FASTEST; + Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulse.colors_min = 1; + Pulse.colors_max = 2; + Pulse.brightness_min = EVGA_GPU_V2_BRIGHTNESS_MIN; + Pulse.brightness = EVGA_GPU_V2_BRIGHTNESS_DEFAULT; + Pulse.brightness_max = EVGA_GPU_V2_BRIGHTNESS_MAX; + Pulse.colors.resize(1); + modes.push_back(Pulse); + + + SetupZones(); + + // Initialize active mode + active_mode = getModeIndex(controller->GetMode()); + + /*---------------------------------------------------------*\ + | The LED color (color[0]) will always be set. Mode colors | + | are only set for the MODE_COLORS_MODE_SPECIFIC modes and | + | by extension colorB is only necessary if its not black | + \*---------------------------------------------------------*/ + + colors[0] = controller->GetColorA(); + RGBColor colorB = controller->GetColorB(); + + int breathing_mode_index = getModeIndex(EVGA_GPU_V2_RGB_MODE_BREATHING); + int pulse_mode_index = getModeIndex(EVGA_GPU_V2_RGB_MODE_PULSE); + + // Pre fill in colors for mode specific colors + modes[breathing_mode_index].colors[0] = colors[0]; + modes[pulse_mode_index].colors[0] = colors[0]; + // Add colors if colorB is not equal to 0. + if(colorB != 0) + { + modes[breathing_mode_index].colors.push_back(colorB); + modes[pulse_mode_index].colors.push_back(colorB); + } + + // Load speed settings from the card: + modes[active_mode].speed = controller->GetSpeed(); + modes[active_mode].brightness = controller->GetBrightnessA(); +} + +RGBController_EVGAGPUv2::~RGBController_EVGAGPUv2() +{ + delete controller; +} + +int RGBController_EVGAGPUv2::getModeIndex(unsigned char mode_value) +{ + for(int mode_index = 0; mode_index < (int)modes.size(); mode_index++) + { + if(modes[mode_index].value == mode_value) + { + return(mode_index); + } + } + + return(0); +} + +void RGBController_EVGAGPUv2::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has one LED, so create a single zone and | + | LED for it | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = "GPU Zone"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = "GPU LED"; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zones.push_back(*new_zone); + + SetupColors(); +} + +void RGBController_EVGAGPUv2::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_EVGAGPUv2::DeviceUpdateLEDs() +{ + /*---------------------------------------------------------*\ + | DeviceUpdateLEDs() is only used in MODE_COLORS_PER_LED | + | modes and as such colorB will always be black (0x000000) | + \*---------------------------------------------------------*/ + + controller->SetColor(colors[0], /* colorB*/ 0, modes[active_mode].brightness); +} + +void RGBController_EVGAGPUv2::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_EVGAGPUv2::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_EVGAGPUv2::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Modes with MODE_COLORS_MODE_SPECIFIC may have either | + | 1 or 2 colors associated with it. The device controller | + | expects colorB as black (0x000000) in 1 color scenarios | + \*---------------------------------------------------------*/ + + RGBColor colorA = colors[0]; + RGBColor colorB = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + colorA = modes[active_mode].colors[0]; + colorB = (modes[active_mode].colors.size() == 2) ? modes[active_mode].colors[1] : 0 ; + } + + controller->SetMode( modes[active_mode].value, colorA, colorB, modes[active_mode].speed, modes[active_mode].brightness); +} + +void RGBController_EVGAGPUv2::DeviceSaveMode() +{ + controller->SaveSettings(); +} diff --git a/Controllers/EVGATuringGPUController/RGBController_EVGAGPUv2.h b/Controllers/EVGATuringGPUController/RGBController_EVGAGPUv2.h new file mode 100644 index 0000000..be8db77 --- /dev/null +++ b/Controllers/EVGATuringGPUController/RGBController_EVGAGPUv2.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAGPUv2.h | +| | +| RGBController for EVGA V2 (Turing) GPU | +| | +| TheRogueZeta 15 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "EVGAGPUv2Controller.h" + +class RGBController_EVGAGPUv2 : public RGBController +{ +public: + RGBController_EVGAGPUv2(EVGAGPUv2Controller* controller_ptr); + ~RGBController_EVGAGPUv2(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + EVGAGPUv2Controller* controller; + + int getModeIndex(unsigned char mode_value); +}; diff --git a/Controllers/EVGAUSBController/EVGAKeyboardController/EVGAKeyboardController.cpp b/Controllers/EVGAUSBController/EVGAKeyboardController/EVGAKeyboardController.cpp new file mode 100644 index 0000000..f3c360d --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAKeyboardController/EVGAKeyboardController.cpp @@ -0,0 +1,426 @@ +/*---------------------------------------------------------*\ +| EVGAKeyboardController.cpp | +| | +| Driver for EVGA keyboard | +| | +| Chris M (Dr_No) 25 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "EVGAKeyboardController.h" +#include "StringUtils.h" + +static uint8_t packet_map[EVGA_KEYBOARD_FULL_SIZE_KEYCOUNT + EVGA_KEYBOARD_Z20_EXTRA_KEYS] = +{ +/*00 ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 */ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + +/*10 F10 F11 F12 PRT SLK PBK ` 1 2 3 */ + 11, 12, 13, 14, 15, 16, 22, 23, 24, 25, + +/*20 4 5 6 7 8 9 0 - = BSP */ + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + +/*30 INS HME PUP TAB Q W E R T Y */ + 36, 37, 38, 44, 45, 46, 47, 48, 49, 50, + +/*40 U I O P [ ] \ DEL END PDN */ + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + +/*50 CAP A S D F G H J K L */ + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + +/*60 ; ' ENT LSH Z X C V B N */ + 76, 77, 78, 83, 84, 85, 86, 87, 88, 89, + +/*70 M , . / RSH UP LCTL LWIN LALT SPC */ + 90, 91, 92, 93, 94, 96, 103, 104, 105, 106, + +/*80 RALT RFNC MENU RCTL LFT DWN RGT NLK NM/ NM* */ + 107, 108, 109, 110, 111, 112, 113, 39, 40, 41, + +/*90 NM- NM+ NETR NM1 NM2 NM3 NM4 NM5 NM6 NM7 */ + 42, 64, 101, 98, 99, 100, 79, 80, 81, 61, + +/*100 NM8 NM9 NM0 NM. PRV PLY NXT MTE R1 R2 */ + 62, 63, 114, 115, 18, 19, 20, 118, 176, 177, + +/*110 R3 R4 R5 R6 R7 R8 R9 L1 L2 L3 */ + 178, 179, 180, 181, 182, 183, 184, 160, 161, 162, + +/*120 L4 L5 L6 L7 L8 L9 GM M1 M2 M3 */ + 163, 164, 165, 166, 167, 168, 0, 21, 43, 65, + +/*130 M4 M5 */ + 82, 102 +}; + +EVGAKeyboardController::EVGAKeyboardController(hid_device* dev_handle, const char* path, uint16_t kb_pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + pid = kb_pid; + + SetSleepTime(); +} + +EVGAKeyboardController::~EVGAKeyboardController() +{ + hid_close(dev); +} + +std::string EVGAKeyboardController::GetName() +{ + return(name); +} + +std::string EVGAKeyboardController::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string EVGAKeyboardController::GetLocation() +{ + return("HID: " + location); +} + +uint16_t EVGAKeyboardController::GetPid() +{ + return(pid); +} + +void EVGAKeyboardController::SetLedsDirect(std::vector colors) +{ + uint8_t buffer[EVGA_KEYBOARD_CONTROLLER_ID_6_SIZE] = { 0x06, 0xEA, 0x02, 0x01 }; + + /*-----------------------------------------------------------------*\ + | Set up Direct packet | + | packet_map is the index of the Key from full_matrix_map and | + | the value is the position in the direct packet buffer | + \*-----------------------------------------------------------------*/ + for(size_t i = 0; i < colors.size(); i++) + { + RGBColor key = colors[i]; + uint16_t offset = EVGA_KB_ZONE_BYTE + (packet_map[i] * 4); + + buffer[offset + 0] = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + buffer[offset + 1] = RGBGetRValue(key); + buffer[offset + 2] = RGBGetGValue(key); + buffer[offset + 3] = RGBGetBValue(key); + } + + buffer[EVGA_KB_CRC_BYTE] = GetChecksum(&buffer[8], EVGA_KEYBOARD_CONTROLLER_ID_6_SIZE - EVGA_KB_ZONE_BYTE); + hid_send_feature_report(dev, buffer, EVGA_KEYBOARD_CONTROLLER_ID_6_SIZE); +} + +void EVGAKeyboardController::SaveMode() +{ + uint8_t buffer[EVGA_KEYBOARD_CONTROLLER_ID_4_SIZE] = { 0x04, 0xEA, 0x02, 0x12 }; + + hid_send_feature_report(dev, buffer, EVGA_KEYBOARD_CONTROLLER_ID_4_SIZE); +} + +void EVGAKeyboardController::SetMode(uint8_t mode, uint16_t speed, uint8_t brightness, + uint8_t direction, std::vector colors) +{ + SetHWModes(); + SendMode(mode, direction); + SendColour(mode, speed, brightness, direction, colors); +} + +void EVGAKeyboardController::GetStatus(mode *mode) +{ + /*-----------------------------------------------------------------*\ + | Gets the status of mode mode->value from the keyboard and then | + | sets Colors, Brightness, Speed, Direction for the mode. | + \*-----------------------------------------------------------------*/ + uint8_t buffer[EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE] = { 0x07, 0xEA, 0x02, 0x0C, 0x01 }; + buffer[EVGA_KB_MODE_BYTE] += mode->value; + + hid_send_feature_report(dev, buffer, EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE); + int result = hid_get_feature_report (dev, buffer, EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE); + + /*-----------------------------------------------------------------*\ + | If the read is successful fill in value from the packet | + \*-----------------------------------------------------------------*/ + if(result > 0) + { + switch(mode->value) + { + case EVGA_KEYBOARD_CONTROLLER_MODE_STATIC: + mode->brightness = FindColours(&buffer[EVGA_KB_SPEED_LSB], mode->colors_max, mode->colors); + break; + + case EVGA_KEYBOARD_CONTROLLER_MODE_BREATHING: + mode->brightness = buffer[27]; + mode->speed = buffer[EVGA_KB_SPEED_LSB] << 8 | buffer[EVGA_KB_SPEED_MSB]; + mode->colors.push_back(ToRGBColor(buffer[28], buffer[29], buffer[30])); + mode->colors.push_back(ToRGBColor(buffer[33], buffer[34], buffer[35])); + break; + + case EVGA_KEYBOARD_CONTROLLER_MODE_PULSE: + mode->brightness = FindColours(&buffer[33], buffer[EVGA_KB_COLORS_SZ], mode->colors); + mode->speed = buffer[EVGA_KB_SPEED_LSB] << 8 | buffer[EVGA_KB_SPEED_MSB]; + break; + + case EVGA_KEYBOARD_CONTROLLER_MODE_SPIRAL: + case EVGA_KEYBOARD_CONTROLLER_MODE_RAINBOW: + case EVGA_KEYBOARD_CONTROLLER_MODE_TRIGGER: + mode->direction = FindDirection(mode->value, buffer[11] + buffer[12]); + mode->brightness = FindColours(&buffer[27], buffer[EVGA_KB_COLORS_SZ], mode->colors); + mode->speed = buffer[EVGA_KB_SPEED_LSB] << 8 | buffer[EVGA_KB_SPEED_MSB]; + break; + + case EVGA_KEYBOARD_CONTROLLER_MODE_STAR: + mode->brightness = buffer[EVGA_KB_COLORS_SZ]; + mode->speed = buffer[EVGA_KB_SPEED_LSB]; + break; + } + LOG_DEBUG("[%s] Mode %d Setup with %d colours @ %04X speed and %02X brightness", name.c_str(), mode->value, mode->colors.size(), mode->speed, mode->brightness); + } + else + { + LOG_INFO("[%s] An error occured reading data for mode %d", name.c_str(), mode->value); + } +} + +void EVGAKeyboardController::SetHWModes() +{ + /*-----------------------------------------------------------------*\ + | Send Initialise Hardware Modes | + \*-----------------------------------------------------------------*/ + uint8_t buffer[EVGA_KEYBOARD_CONTROLLER_ID_6_SIZE] = { 0x06, 0xEA, 0x02 }; + + hid_send_feature_report(dev, buffer, EVGA_KEYBOARD_CONTROLLER_ID_6_SIZE); +} + +void EVGAKeyboardController::SendMode(uint8_t mode, uint8_t direction) +{ + /*-----------------------------------------------------------------*\ + | Send Mode | + \*-----------------------------------------------------------------*/ + uint8_t buffer[EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE] = { 0x07, 0xEA, 0x02, 0x0C }; + + buffer[EVGA_KB_ZONE_BYTE] = mode; + buffer[EVGA_KB_DIR_BYTE] = direction; + + buffer[EVGA_KB_CRC_BYTE] = GetChecksum(&buffer[8], EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE - EVGA_KB_ZONE_BYTE); + hid_send_feature_report(dev, buffer, EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE); +} + +void EVGAKeyboardController::SendColour(uint8_t mode, uint16_t speed, uint8_t brightness, uint8_t direction, std::vector colors) +{ + uint8_t buffer[EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE] = { 0x07, 0xEA, 0x02, 0x0C }; + + speed *= (mode == EVGA_KEYBOARD_CONTROLLER_MODE_TRIGGER) ? 10 : 100; + buffer[EVGA_KB_MODE_BYTE] += mode; + buffer[EVGA_KB_ZONE_BYTE] = EVGA_KEYBOARD_CONTROLLER_ZONE_ALL_KEYS; //zone + + /*-----------------------------------------------------------------*\ + | Static mode does not have speed but it will be overwritten | + \*-----------------------------------------------------------------*/ + buffer[EVGA_KB_SPEED_LSB] = speed & 0xFF; + buffer[EVGA_KB_SPEED_MSB] = speed >> 8; + /*-----------------------------------------------------------------*\ + | Static, Breathing and Star modes have fixed colour sizes | + | buffer[26] will be overwritten for these modes | + \*-----------------------------------------------------------------*/ + buffer[EVGA_KB_COLORS_SZ] = (uint8_t)colors.size(); + + switch(mode) + { + case EVGA_KEYBOARD_CONTROLLER_MODE_STATIC: + FillColours(&buffer[24], brightness, colors); + break; + + case EVGA_KEYBOARD_CONTROLLER_MODE_BREATHING: + for(size_t i = 0; i < colors.size(); i++) + { + uint8_t offset = (uint8_t)(26 + (i * 5)); + + buffer[offset + 0] = 0x0A; + buffer[offset + 1] = brightness; + buffer[offset + 2] = RGBGetRValue(colors[i]); + buffer[offset + 3] = RGBGetGValue(colors[i]); + buffer[offset + 4] = RGBGetBValue(colors[i]); + } + break; + + case EVGA_KEYBOARD_CONTROLLER_MODE_PULSE: + /*-----------------------------------------------------------------*\ + | Buffer 27 thru 32 could be defining a "Transition Color" | + | 27 Identifier ?? | + | 28 Time in ms ?? | + | 29 - 32 Looks to be "Black" | + \*-----------------------------------------------------------------*/ + buffer[27] = 0x0A; + buffer[28] = 0x0A; + buffer[29] = 0xFF; + + FillColours(&buffer[33], brightness, colors); + break; + + case EVGA_KEYBOARD_CONTROLLER_MODE_SPIRAL: + buffer[11] = direction; + + FillColours(&buffer[27], brightness, colors); + break; + + case EVGA_KEYBOARD_CONTROLLER_MODE_RAINBOW: + buffer[12] = direction; + + FillColours(&buffer[27], brightness, colors); + break; + + case EVGA_KEYBOARD_CONTROLLER_MODE_TRIGGER: + buffer[3]--; //Why EVGA?? + buffer[12] = direction; + + FillColours(&buffer[27], brightness, colors); + break; + + case EVGA_KEYBOARD_CONTROLLER_MODE_STAR: + buffer[3]++; //Why EVGA?? + buffer[26] = brightness; + break; + } + + buffer[EVGA_KB_CRC_BYTE] = GetChecksum(&buffer[8], EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE - EVGA_KB_ZONE_BYTE); + hid_send_feature_report(dev, buffer, EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE); +} + +void EVGAKeyboardController::FillColours(uint8_t * buffer, uint8_t brightness, std::vector colors) +{ + for(size_t i = 0; i < colors.size(); i++) + { + uint8_t offset = (uint8_t)(i * 4); + + buffer[offset + 0] = brightness; + buffer[offset + 1] = RGBGetRValue(colors[i]); + buffer[offset + 2] = RGBGetGValue(colors[i]); + buffer[offset + 3] = RGBGetBValue(colors[i]); + } +} + +uint8_t EVGAKeyboardController::GetChecksum(uint8_t * data, size_t count) +{ + uint8_t checksum = 0; + + for(size_t i = 0; i < count; i++) + { + checksum -= data[i]; + } + + return(checksum); +} + +uint8_t EVGAKeyboardController::FindDirection(uint8_t mode, uint8_t direction) +{ + /*-----------------------------------------------------------------*\ + | Converts EVGAs buffer direction value to OpenRGB's directions | + \*-----------------------------------------------------------------*/ + uint8_t temp = 0; + + for(size_t i = 0; i < sizeof(direction_map[mode]); i++) + { + if(direction_map[mode][i] == direction) + { + temp = direction_map[mode][i]; + break; + } + } + + return(temp); +} + +uint8_t EVGAKeyboardController::FindColours(uint8_t * data, uint8_t count, std::vector &colors) +{ + /*-----------------------------------------------------------------*\ + | Converts EVGAs buffer colours to OpenRGB's colours | + \*-----------------------------------------------------------------*/ + colors.clear(); + + for(size_t i = 0; i < count; i++) + { + uint8_t offset = (uint8_t)(i * 4); + + colors.push_back(ToRGBColor(data[offset + 1],data[offset + 2],data[offset + 3])); + } + + return(data[0]); +} + +uint8_t EVGAKeyboardController::GetMode() +{ + static const uint16_t index = 1289; + NFIPacket(); + /*-----------------------------------------------------------------*\ + | Requests the current set mode from the keyboard | + | | + | Request: 04 ea 02 07 01 00 00 6c 00 00 00 00 00 00 00 00 00 | + | Response: 04 ea 02 07 01 00 c0 6c 04 00 00 00 00 00 00 00 00 | + | Key Count?? ⇗ | + \*-----------------------------------------------------------------*/ + uint8_t buffer[EVGA_KEYBOARD_CONTROLLER_ID_3_SIZE] = { 0x08, 0xEA, 0x02, 0x01, 0xFE }; + + hid_send_feature_report(dev, buffer, EVGA_KEYBOARD_CONTROLLER_ID_3_SIZE); + int result = hid_get_feature_report (dev, buffer, EVGA_KEYBOARD_CONTROLLER_ID_3_SIZE); + + if(result > 0) + { + LOG_DEBUG("[%s] Returned mode %02X - %02X %02X %02X %02X %02X", name.c_str(), buffer[index], buffer[index-2], buffer[index-1], buffer[index], buffer[index+1], buffer[index+2]); + return(buffer[index]); + } + else + { + LOG_INFO("[%s] An error occured reading current mode", name.c_str()); + return(0); + } +} + +void EVGAKeyboardController::NFIPacket() +{ + /*-----------------------------------------------------------------*\ + | Not sure what this packet is doing but it appears to be | + | required to retrieve the current mode from the (first) profile | + \*-----------------------------------------------------------------*/ + uint8_t buffer1[EVGA_KEYBOARD_CONTROLLER_ID_4_SIZE] = { 0x04, 0xEA, 0x02, 0x33, 0x00, 0x00, 0x00, 0x01 }; + hid_send_feature_report(dev, buffer1, EVGA_KEYBOARD_CONTROLLER_ID_4_SIZE); + + uint8_t buffer2[EVGA_KEYBOARD_CONTROLLER_ID_4_SIZE] = { 0x04, 0xEA, 0x02, 0x06, 0x01 }; + hid_send_feature_report(dev, buffer2, EVGA_KEYBOARD_CONTROLLER_ID_4_SIZE); +} + +void EVGAKeyboardController::SetSleepTime() +{ + /*-----------------------------------------------------------------*\ + | After a set timer the LED lighting on this keyboard will "sleep" | + \*-----------------------------------------------------------------*/ + const uint16_t minutes = 0; //Max value in Unleashed is 300min + const uint8_t multiply = 0xEA; + + uint8_t buffer1[EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE] = { 0x07, 0xEA, 0x02, 0x1B, 0x00, 0x00, 0x00, 0xFE, 0x02 }; + hid_send_feature_report(dev, buffer1, EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE); + + uint8_t buffer2[EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE] = { 0x07, 0xEA, 0x02, 0x03 }; + + uint32_t sleep = minutes * multiply; + buffer2[EVGA_KB_ZONE_BYTE] = (sleep == 0) ? 0 : EVGA_KEYBOARD_CONTROLLER_ZONE_ALL_KEYS; //zone + buffer2[9] = sleep & 0xFF; + buffer2[10] = (sleep >> 8) & 0xFF; + buffer2[11] = (sleep >> 16) & 0xFF; + + buffer2[EVGA_KB_CRC_BYTE] = GetChecksum(&buffer2[8], EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE - EVGA_KB_ZONE_BYTE); + hid_send_feature_report(dev, buffer2, EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE); +} diff --git a/Controllers/EVGAUSBController/EVGAKeyboardController/EVGAKeyboardController.h b/Controllers/EVGAUSBController/EVGAKeyboardController/EVGAKeyboardController.h new file mode 100644 index 0000000..369ddca --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAKeyboardController/EVGAKeyboardController.h @@ -0,0 +1,124 @@ +/*---------------------------------------------------------*\ +| EVGAKeyboardController.h | +| | +| Driver for EVGA keyboard | +| | +| Chris M (Dr_No) 25 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "LogManager.h" +#include "RGBController.h" + +#define NA 0xFFFFFFFF +#define HID_MAX_STR 255 + +#define EVGA_KEYBOARD_CONTROLLER_ID_3_SIZE 1597 +#define EVGA_KEYBOARD_CONTROLLER_ID_4_SIZE 17 +#define EVGA_KEYBOARD_CONTROLLER_ID_6_SIZE 792 +#define EVGA_KEYBOARD_CONTROLLER_ID_7_SIZE 136 +#define EVGA_KEYBOARD_CONTROLLER_INTERRUPT_TIMEOUT 250 + +#define EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MIN 0 +#define EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX 255 +#define EVGA_KEYBOARD_FULL_SIZE_KEYCOUNT 108 +#define EVGA_KEYBOARD_Z20_EXTRA_KEYS 24 +#define EVGA_KEYBOARD_Z20_EXTRA_ZONES 3 + +static const uint8_t direction_map[8][4] = +{ + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, //Spiral - Left = Anti Clockwise, Right = Clockwise + { 3, 2, 0, 1 }, //Rainbow + { 0, 0, 0, 0 }, + { 0, 1, 2, 0 }, //Trigger - Right = Typing, Left = Single, Up = 3x3 +}; + +enum EVGA_Keyboard_Controller_Modes +{ + EVGA_KEYBOARD_CONTROLLER_MODE_OFF = 0x00, //Turn off - All leds off + EVGA_KEYBOARD_CONTROLLER_MODE_STATIC = 0x01, //Static Mode - Set entire zone to a single color. + EVGA_KEYBOARD_CONTROLLER_MODE_BREATHING = 0x02, //Breathing Mode - Fades between fully off and fully on. + EVGA_KEYBOARD_CONTROLLER_MODE_PULSE = 0x03, //Flashing Mode - Abruptly changing between fully off and fully on. + EVGA_KEYBOARD_CONTROLLER_MODE_SPIRAL = 0x04, //Spiral Mode - All keys light in a colourful spiral + EVGA_KEYBOARD_CONTROLLER_MODE_RAINBOW = 0x05, //Rainbow Wave Mode - Cycle thru the color spectrum as a wave across all LEDs + EVGA_KEYBOARD_CONTROLLER_MODE_STAR = 0x06, //Starry Night effect + EVGA_KEYBOARD_CONTROLLER_MODE_TRIGGER = 0x07, //Key reactive + EVGA_KEYBOARD_CONTROLLER_MODE_DIRECT = 0xFF, //Direct Led Control - Independently set LEDs in zone +}; + +enum EVGA_Keyboard_Controller_Zones +{ + EVGA_KEYBOARD_CONTROLLER_ZONE_NUMPAD_KEYS = (1 << 0), + EVGA_KEYBOARD_CONTROLLER_ZONE_FUNCTION_KEYS = (1 << 1), + EVGA_KEYBOARD_CONTROLLER_ZONE_NUMBER_KEYS = (1 << 2), + EVGA_KEYBOARD_CONTROLLER_ZONE_ARROW_KEYS = (1 << 3), + EVGA_KEYBOARD_CONTROLLER_ZONE_WASD_KEYS = (1 << 4), + EVGA_KEYBOARD_CONTROLLER_ZONE_ALL_KEYS = (1 << 7), +}; + +enum EVGA_Keyboard_Controller_Byte_Map +{ + EVGA_KB_REPORT_BYTE = 0, + EVGA_KB_COMMAND_BYTE = 1, + EVGA_KB_FUNCTION_BYTE = 2, + EVGA_KB_MODE_BYTE = 3, + EVGA_KB_CRC_BYTE = 7, + EVGA_KB_ZONE_BYTE = 8, + EVGA_KB_DIR_BYTE = 9, + EVGA_KB_SPEED_LSB = 24, + EVGA_KB_SPEED_MSB = 25, + EVGA_KB_COLORS_SZ = 26, +}; + +enum EVGA_Keyboard_Controller_Speed +{ + EVGA_KEYBOARD_CONTROLLER_SPEED_SLOWEST = 0x64, // Slowest speed + EVGA_KEYBOARD_CONTROLLER_SPEED_SLOWISH = 0x3E, // Slowish speed + EVGA_KEYBOARD_CONTROLLER_SPEED_NORMAL = 0x32, // Normal speed + EVGA_KEYBOARD_CONTROLLER_SPEED_FASTEST = 0x05, // Fastest speed +}; + +class EVGAKeyboardController +{ +public: + EVGAKeyboardController(hid_device* dev_handle, const char* path, uint16_t kb_pid, std::string dev_name); + ~EVGAKeyboardController(); + + std::string GetName(); + std::string GetSerial(); + std::string GetLocation(); + + void SaveMode(); + void SetHWModes(); + void SetLedsDirect(std::vector colors); + void SetMode(uint8_t mode, uint16_t speed, uint8_t brightness, + uint8_t direction, std::vector colors); + void SetSleepTime(); + void GetStatus(mode *mode); + uint8_t GetMode(); + uint16_t GetPid(); +private: + std::string name; + std::string location; + hid_device* dev; + uint16_t pid; + + void NFIPacket(); + void FillColours(uint8_t * buffer, uint8_t brightness, std::vector colors); + void SendMode(uint8_t mode, uint8_t direction); + void SendColour(uint8_t mode, uint16_t speed, uint8_t brightness, uint8_t direction, std::vector colors); + uint8_t GetChecksum(uint8_t * data, size_t count); + + uint8_t FindDirection(uint8_t mode, uint8_t direction); + uint8_t FindColours(uint8_t * data, uint8_t count, std::vector &colors); +}; diff --git a/Controllers/EVGAUSBController/EVGAKeyboardController/RGBController_EVGAKeyboard.cpp b/Controllers/EVGAUSBController/EVGAKeyboardController/RGBController_EVGAKeyboard.cpp new file mode 100644 index 0000000..f215045 --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAKeyboardController/RGBController_EVGAKeyboard.cpp @@ -0,0 +1,549 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAKeyboard.cpp | +| | +| RGBController for EVGA keyboard | +| | +| Chris M (Dr_No) 25 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Colors.h" +#include "RGBControllerKeyNames.h" +#include "RGBController_EVGAKeyboard.h" + +static unsigned int full_matrix_map[6][21] = +{ + { 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 104, 105, 106, 107 }, + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 87, 88, 89, 90 }, + { 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 99, 100, 101, 91 }, + { 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, NA, NA, NA, NA, 96, 97, 98, NA }, + { 63, NA, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, NA, NA, 75, NA, 93, 94, 95, 92 }, + { 76, 77, 78, NA, NA, NA, 79, NA, NA, NA, 80, 81, 82, 83, 84, 85, 86, 102, NA, 103, NA } +}; + +static unsigned int Z20_extra_zones[EVGA_KEYBOARD_Z20_EXTRA_ZONES][9] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, //Index 108 + { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, //Index 117 + { 0, 1, 2, 3, 4, 5, NA, NA, NA } //Index 126 +}; + +const char* Z20_zone_names[EVGA_KEYBOARD_Z20_EXTRA_ZONES] = +{ + "Right Side LEDs", + "Left Side LEDs", + "Macro Keys" +}; + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, // 00 + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, // 10 + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAUSE_BREAK, + + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, // 20 + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, // 30 + KEY_EN_HOME, + KEY_EN_PAGE_UP, + + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, // 40 + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + + KEY_EN_CAPS_LOCK, // 50 + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, // 60 + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, // 70 + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, // 80 + KEY_EN_RIGHT_FUNCTION, + KEY_EN_MENU, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, // 90 + KEY_EN_NUMPAD_PLUS, + KEY_EN_NUMPAD_ENTER, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, // 100 + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD, + + KEY_EN_MEDIA_PREVIOUS, + KEY_EN_MEDIA_PLAY_PAUSE, + KEY_EN_MEDIA_NEXT, + KEY_EN_MEDIA_MUTE, + + "Key: Right LED 1", + "Key: Right LED 2", + "Key: Right LED 3", //110 + "Key: Right LED 4", + "Key: Right LED 5", + "Key: Right LED 6", + "Key: Right LED 7", + "Key: Right LED 8", + "Key: Right LED 9", + + "Key: Left LED 1", + "Key: Left LED 2", + "Key: Left LED 3", + "Key: Left LED 4", //120 + "Key: Left LED 5", + "Key: Left LED 6", + "Key: Left LED 7", + "Key: Left LED 8", + "Key: Left LED 9", + + "Key: Feature Button", + "Key: Macro 1", + "Key: Macro 2", + "Key: Macro 3", + "Key: Macro 4", //130 + "Key: Macro 5", +}; + +/**------------------------------------------------------------------*\ + @name EVGA USB Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectEVGAKeyboardControllers + @comment The EVGA USB keyboard controller currently supports + the Z15 (both ISO & ANSI) as well as the Z20 ANSI keyboards +\*-------------------------------------------------------------------*/ + +RGBController_EVGAKeyboard::RGBController_EVGAKeyboard(EVGAKeyboardController* controller_ptr) +{ + /*-----------------------------------------------------*\ + | Initialise the random functions from the clock | + \*-----------------------------------------------------*/ + std::srand((unsigned int)time(NULL)); + + controller = controller_ptr; + + name = controller->GetName(); + vendor = "EVGA"; + type = DEVICE_TYPE_KEYBOARD; + description = "EVGA Keyboard Device"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = EVGA_KEYBOARD_CONTROLLER_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = EVGA_KEYBOARD_CONTROLLER_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness_min = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MIN; + Static.brightness_max = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + + //controller->GetStatus(&Static); + Static.colors.resize(Static.colors_max); + Static.brightness = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EVGA_KEYBOARD_CONTROLLER_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.brightness_min = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MIN; + Breathing.brightness_max = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Breathing.speed_min = EVGA_KEYBOARD_CONTROLLER_SPEED_SLOWEST; + Breathing.speed_max = EVGA_KEYBOARD_CONTROLLER_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + + //controller->GetStatus(&Breathing); + Breathing.colors.resize(Breathing.colors_max); + Breathing.brightness = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Breathing.speed = EVGA_KEYBOARD_CONTROLLER_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Pulse; + Pulse.name = "Flashing"; + Pulse.value = EVGA_KEYBOARD_CONTROLLER_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Pulse.colors_min = 2; + Pulse.colors_max = 7; + Pulse.brightness_min = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MIN; + Pulse.brightness_max = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Pulse.speed_min = EVGA_KEYBOARD_CONTROLLER_SPEED_SLOWEST; + Pulse.speed_max = EVGA_KEYBOARD_CONTROLLER_SPEED_FASTEST; + Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + + //controller->GetStatus(&Pulse); + Pulse.colors.resize(Pulse.colors_max); + Pulse.brightness = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Pulse.speed = EVGA_KEYBOARD_CONTROLLER_SPEED_NORMAL; + modes.push_back(Pulse); + + mode Spiral; + Spiral.name = "Spiral"; + Spiral.value = EVGA_KEYBOARD_CONTROLLER_MODE_SPIRAL; + Spiral.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Spiral.colors_min = 7; + Spiral.colors_max = 7; + Spiral.brightness_min = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MIN; + Spiral.brightness_max = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Spiral.speed_min = EVGA_KEYBOARD_CONTROLLER_SPEED_SLOWEST; + Spiral.speed_max = EVGA_KEYBOARD_CONTROLLER_SPEED_FASTEST; + Spiral.color_mode = MODE_COLORS_MODE_SPECIFIC; + + //controller->GetStatus(&Spiral); + Spiral.colors.resize(Spiral.colors_max); + Spiral.colors = { COLOR_RED, COLOR_DARKORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_BLUE, COLOR_DARKVIOLET, COLOR_MAGENTA}; + Spiral.brightness = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Spiral.speed = EVGA_KEYBOARD_CONTROLLER_SPEED_NORMAL; + modes.push_back(Spiral); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = EVGA_KEYBOARD_CONTROLLER_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Rainbow.colors_min = 3; + Rainbow.colors_max = 7; + Rainbow.brightness_min = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MIN; + Rainbow.brightness_max = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Rainbow.speed_min = EVGA_KEYBOARD_CONTROLLER_SPEED_SLOWISH; + Rainbow.speed_max = EVGA_KEYBOARD_CONTROLLER_SPEED_FASTEST; + Rainbow.color_mode = MODE_COLORS_MODE_SPECIFIC; + + //controller->GetStatus(&Rainbow); + Rainbow.colors.resize(Rainbow.colors_max); + Rainbow.colors = { COLOR_RED, COLOR_DARKORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_BLUE, COLOR_DARKVIOLET, COLOR_MAGENTA}; + Rainbow.brightness = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Rainbow.speed = EVGA_KEYBOARD_CONTROLLER_SPEED_NORMAL; + modes.push_back(Rainbow); + + mode Star; + Star.name = "Star Shining"; + Star.value = EVGA_KEYBOARD_CONTROLLER_MODE_STAR; + Star.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Star.brightness_min = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MIN; + Star.brightness_max = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Star.speed_min = EVGA_KEYBOARD_CONTROLLER_SPEED_SLOWEST; + Star.speed_max = EVGA_KEYBOARD_CONTROLLER_SPEED_FASTEST; + Star.color_mode = MODE_COLORS_NONE; + + //controller->GetStatus(&Star); + Star.brightness = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Star.speed = EVGA_KEYBOARD_CONTROLLER_SPEED_NORMAL; + modes.push_back(Star); + + mode Typing; + Typing.name = "Typing Lighting"; + Typing.value = EVGA_KEYBOARD_CONTROLLER_MODE_TRIGGER; + Typing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Typing.colors_min = 1; + Typing.colors_max = 7; + Typing.brightness_min = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MIN; + Typing.brightness_max = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Typing.speed_min = EVGA_KEYBOARD_CONTROLLER_SPEED_SLOWEST; + Typing.speed_max = EVGA_KEYBOARD_CONTROLLER_SPEED_FASTEST; + Typing.color_mode = MODE_COLORS_MODE_SPECIFIC; + + //controller->GetStatus(&Typing); + Typing.direction = 0; + Typing.colors.resize(Typing.colors_max); + Typing.colors = { COLOR_RED, COLOR_DARKORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_BLUE, COLOR_DARKVIOLET, COLOR_MAGENTA}; + Typing.brightness = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Typing.speed = EVGA_KEYBOARD_CONTROLLER_SPEED_NORMAL; + modes.push_back(Typing); + + mode Single; + Single.name = "Reactive (Single Key)"; + Single.value = EVGA_KEYBOARD_CONTROLLER_MODE_TRIGGER; + Single.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Single.colors_min = 1; + Single.colors_max = 7; + Single.brightness_min = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MIN; + Single.brightness_max = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Single.speed_min = EVGA_KEYBOARD_CONTROLLER_SPEED_SLOWEST; + Single.speed_max = EVGA_KEYBOARD_CONTROLLER_SPEED_FASTEST; + Single.color_mode = MODE_COLORS_MODE_SPECIFIC; + + //controller->GetStatus(&Single); + Single.direction = 1; + Single.colors.resize(Single.colors_max); + Single.colors = { COLOR_RED, COLOR_DARKORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_BLUE, COLOR_DARKVIOLET, COLOR_MAGENTA}; + Single.brightness = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + Single.speed = EVGA_KEYBOARD_CONTROLLER_SPEED_NORMAL; + modes.push_back(Single); + + mode ThreeBy3; + ThreeBy3.name = "Reactive (3x3 Key)"; + ThreeBy3.value = EVGA_KEYBOARD_CONTROLLER_MODE_TRIGGER; + ThreeBy3.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + ThreeBy3.colors_min = 1; + ThreeBy3.colors_max = 7; + ThreeBy3.brightness_min = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MIN; + ThreeBy3.brightness_max = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + ThreeBy3.speed_min = EVGA_KEYBOARD_CONTROLLER_SPEED_SLOWEST; + ThreeBy3.speed_max = EVGA_KEYBOARD_CONTROLLER_SPEED_FASTEST; + ThreeBy3.color_mode = MODE_COLORS_MODE_SPECIFIC; + + //controller->GetStatus(&ThreeBy3); + ThreeBy3.direction = 2; + ThreeBy3.colors.resize(ThreeBy3.colors_max); + ThreeBy3.colors = { COLOR_RED, COLOR_DARKORANGE, COLOR_YELLOW, COLOR_LIME, COLOR_BLUE, COLOR_DARKVIOLET, COLOR_MAGENTA}; + ThreeBy3.brightness = EVGA_KEYBOARD_CONTROLLER_BRIGHTNESS_MAX; + ThreeBy3.speed = EVGA_KEYBOARD_CONTROLLER_SPEED_NORMAL; + modes.push_back(ThreeBy3); + + //uint8_t set_mode = controller->GetMode(); + //active_mode = set_mode; + SetupZones(); +} + +RGBController_EVGAKeyboard::~RGBController_EVGAKeyboard() +{ + delete controller; +} + +void RGBController_EVGAKeyboard::SetupZones() +{ + /*-------------------------------------------------*\ + | Set up the base configuration common to | + | Z15 and Z20 | + \*-------------------------------------------------*/ + zone KB_zone; + KB_zone.name = ZONE_EN_KEYBOARD; + KB_zone.type = ZONE_TYPE_MATRIX; + KB_zone.leds_min = EVGA_KEYBOARD_FULL_SIZE_KEYCOUNT; + KB_zone.leds_max = EVGA_KEYBOARD_FULL_SIZE_KEYCOUNT; + KB_zone.leds_count = EVGA_KEYBOARD_FULL_SIZE_KEYCOUNT; + + KB_zone.matrix_map = new matrix_map_type; + KB_zone.matrix_map->height = 6; + KB_zone.matrix_map->width = 21; + KB_zone.matrix_map->map = (unsigned int *)&full_matrix_map; + zones.push_back(KB_zone); + + /*-------------------------------------------------*\ + | Add configuration for the Z20 | + \*-------------------------------------------------*/ + if(controller->GetPid() == 0x260A || controller->GetPid() == 0x2610) + { + + for(uint8_t i = 0; i < EVGA_KEYBOARD_Z20_EXTRA_ZONES; i++) + { + uint8_t zone_size = sizeof(Z20_extra_zones[i]) / sizeof(Z20_extra_zones[i][0]); + + for(uint8_t count = 0; count < zone_size; count++) + { + if(Z20_extra_zones[i][count] == NA) + { + zone_size = count; + break; + } + } + + zone new_zone; + new_zone.name = Z20_zone_names[i]; + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = zone_size; + new_zone.leds_max = zone_size; + new_zone.leds_count = zone_size; + + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 1; + new_zone.matrix_map->width = zone_size; + new_zone.matrix_map->map = (unsigned int *)&Z20_extra_zones[i]; + zones.push_back(new_zone); + } + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*---------------------------------------------------------*\ + | Set up leds | + \*---------------------------------------------------------*/ + for(std::size_t zone_index = 0; zone_index < zones.size(); zone_index++) + { + unsigned int zone_offset = (unsigned int)leds.size(); + + for(unsigned int led_index = 0; led_index < zones[zone_index].leds_count; led_index++) + { + led new_led; + new_led.value = led_index + zone_offset; + new_led.name = led_names[new_led.value]; + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_EVGAKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_EVGAKeyboard::DeviceUpdateLEDs() +{ + controller->SetLedsDirect(colors); +} + +void RGBController_EVGAKeyboard::UpdateZoneLEDs(int zone) +{ + std::vector colour; + for(size_t i = 0; i < zones[zone].leds_count; i++) + { + colour.push_back(zones[zone].colors[i]); + } + + controller->SetLedsDirect(colour); +} + +void RGBController_EVGAKeyboard::UpdateSingleLED(int led) +{ + std::vector colour; + colour.push_back(colors[led]); + + controller->SetLedsDirect(colour); +} + +void RGBController_EVGAKeyboard::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | No mode set packets required for Direct mode but an | + | extra packet is sent when switching back to HW modes | + \*---------------------------------------------------------*/ + mode set_mode = modes[active_mode]; + + if(current_mode == EVGA_KEYBOARD_CONTROLLER_MODE_DIRECT) + { + controller->SetHWModes(); + } + current_mode = set_mode.value; + + if(set_mode.value == EVGA_KEYBOARD_CONTROLLER_MODE_DIRECT) + { + return; + } + + /*---------------------------------------------------------*\ + | Random colours are generated randoms from software | + \*---------------------------------------------------------*/ + uint8_t direction = direction_map[set_mode.value][set_mode.direction]; + std::vector colours = (set_mode.colors); + + if(set_mode.color_mode == MODE_COLORS_RANDOM) + { + for(unsigned int i = 0; i < colours.size(); i++) + { + colours[i] = GetRandomColor(); + } + } + + controller->SetMode( set_mode.value, set_mode.speed, set_mode.brightness, direction, colours ); +} + +void RGBController_EVGAKeyboard::DeviceSaveMode() +{ + controller->SaveMode(); +} + +RGBColor RGBController_EVGAKeyboard::GetRandomColor() +{ + return (rand() % 16777215); +} diff --git a/Controllers/EVGAUSBController/EVGAKeyboardController/RGBController_EVGAKeyboard.h b/Controllers/EVGAUSBController/EVGAKeyboardController/RGBController_EVGAKeyboard.h new file mode 100644 index 0000000..8ea784e --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAKeyboardController/RGBController_EVGAKeyboard.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAKeyboard.h | +| | +| RGBController for EVGA keyboard | +| | +| Chris M (Dr_No) 25 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "EVGAKeyboardController.h" + +class RGBController_EVGAKeyboard : public RGBController +{ +public: + RGBController_EVGAKeyboard(EVGAKeyboardController* controller_ptr); + ~RGBController_EVGAKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + uint8_t current_mode; + + int GetDeviceMode(); + RGBColor GetRandomColor(); + + EVGAKeyboardController* controller; +}; diff --git a/Controllers/EVGAUSBController/EVGAMouseController/EVGAMouseController.cpp b/Controllers/EVGAUSBController/EVGAMouseController/EVGAMouseController.cpp new file mode 100644 index 0000000..645da6c --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAMouseController/EVGAMouseController.cpp @@ -0,0 +1,345 @@ +/*---------------------------------------------------------*\ +| EVGAMouseController.cpp | +| | +| Driver for EVGA mouse | +| | +| Cooper Knaak 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "EVGAMouseController.h" +#include "LogManager.h" +#include "StringUtils.h" + +#define HID_MAX_STR 255 +#define EVGA_PERIPHERAL_LED_SOURCE_OF_TRUTH EVGA_PERIPHERAL_LED_LOGO +/*----------------------------------------------------------------*\ +| Maximum number of attempts to read from a device before failing. | +\*----------------------------------------------------------------*/ +#define EVGA_PERIPHERAL_MAX_ATTEMPTS 100 +/*-----------------------------------------------------------------*\ +| The delay between sending packets to the device in wireless mode. | +| In wireless mode, sending packets too close to each other causes | +| them to have no effect, despite the device responding properly. | +\*-----------------------------------------------------------------*/ +#define EVGA_PERIPHERAL_PACKET_DELAY std::chrono::milliseconds(10) + +/*--------------------------------------------------------------------------------*\ +| Returns true if both buffers have equal bytes at each position, false otherwise. | +| Each buffer must be an array of bytes at least size bytes long. | +\*--------------------------------------------------------------------------------*/ +static bool BuffersAreEqual(unsigned char *buffer1, unsigned char *buffer2, int size) +{ + for(int i = 0; i < size; i++) + { + if(buffer1[i] != buffer2[i]) + { + return false; + } + } + return true; +} + +EVGAMouseController::EVGAMouseController(hid_device* dev_handle, char * path, int connection_type, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + this->connection_type = connection_type; + + led_states.resize(EVGA_PERIPHERAL_LED_COUNT); + for(EVGAMouseControllerDeviceState &led_state : led_states) + { + led_state.mode = EVGA_PERIPHERAL_MODE_STATIC; + led_state.brightness = 255; + led_state.speed = 100; + led_state.colors.resize(1); + led_state.colors[0] = ToRGBColor(255, 255, 255); + } +} + +EVGAMouseController::~EVGAMouseController() +{ + hid_close(dev); +} + +std::string EVGAMouseController::GetName() +{ + return(name); +} + +std::string EVGAMouseController::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_indexed_string(dev, 2, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string EVGAMouseController::GetLocation() +{ + return location; +} + +uint8_t EVGAMouseController::GetMode() +{ + return GetState().mode; +} + +EVGAMouseControllerDeviceState EVGAMouseController::GetState() +{ + RefreshDeviceState(EVGA_PERIPHERAL_LED_SOURCE_OF_TRUTH); + return led_states[EVGA_PERIPHERAL_LED_SOURCE_OF_TRUTH]; +} + +RGBColor EVGAMouseController::GetColorOfLed(int led) +{ + RefreshDeviceState(led); + return led_states[led].colors[0]; +} + +void EVGAMouseController::SetMode(uint8_t mode, uint8_t index) +{ + unsigned char buffer[EVGA_PERIPHERAL_PACKET_SIZE] = + { + 0x00, /* report id - must be 0x00 according to hid_send_feature_report */ + 0x00, 0x00, 0x00, 0x1D, /* header bits - always the same */ + 0x02, 0x81, 0x01 /* 0x81 sets the mode, which is specified below. */ + }; + + buffer[EVGA_PERIPHERAL_LED_INDEX_BYTE] = index; + buffer[EVGA_PERIPHERAL_MODE_BYTE] = mode; + int err = hid_send_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE); + if(err == -1) + { + const wchar_t* err_str = hid_error(dev); + LOG_DEBUG("[%s] Error writing buffer %s", name.c_str(), err_str); + } + led_states[index].mode = mode; + err = hid_get_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE); + if(err == -1) + { + const wchar_t* err_str = hid_error(dev); + LOG_DEBUG("[%s] Error reading buffer %s", name.c_str(), err_str); + } +} + +void EVGAMouseController::SetLed(uint8_t index, uint8_t brightness, uint8_t speed, RGBColor color) +{ + std::vector colors; + colors.push_back(color); + + SetLed(index, brightness, speed, colors, false); +} + +void EVGAMouseController::SetLed(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector& colors) +{ + SetLed(index, brightness, speed, colors, false); +} + +void EVGAMouseController::SetLedAndActivate(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector& colors) +{ + /*------------------------------------------------------------------------------------------------------------------------------*\ + | Activating some modes requires two identical packets: one for setting the color, and one for setting the color AND activating. | + \*------------------------------------------------------------------------------------------------------------------------------*/ + SetLed(index, brightness, speed, colors, false); + SetLed(index, brightness, speed, colors, true); +} + +void EVGAMouseController::SetAllLeds(uint8_t brightness, uint8_t speed, const std::vector& colors) +{ + for(unsigned int i = 0; i < EVGA_PERIPHERAL_LED_COUNT; i++) + { + SetLed(i, brightness, speed, colors); + } +} + +void EVGAMouseController::SetAllLedsAndActivate(uint8_t brightness, uint8_t speed, const std::vector& colors) +{ + for(unsigned int i = 0; i < EVGA_PERIPHERAL_LED_COUNT; i++) + { + SetLedAndActivate(i, brightness, speed, colors); + } +} + +void EVGAMouseController::SetLed(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector& colors, bool activate) +{ + unsigned char buffer[EVGA_PERIPHERAL_PACKET_SIZE] = + { + 0x00, /* report id - must be 0x00 according to hid_send_feature_report */ + 0x00, 0x00, static_cast(connection_type), 0x1D, + 0x02, 0x00, 0x02 /* header bits - always the same */ + }; + + /*---------------------------------------------------------------------------------------------------------------*\ + | Setting the mode to breathing sends 3 packets: first to activate the mode, second to set the list of colors and | + | third to send a packet identical to the second but with the first byte set ot 0xA1. This "activates" the mode. | + \*---------------------------------------------------------------------------------------------------------------*/ + if(activate) + { + buffer[1] = 0xA1; + } + + buffer[EVGA_PERIPHERAL_LED_INDEX_BYTE] = index; + /*-----------------------------------------------------------------------------------------*\ + | Unleash RGB supports individual modes on the LEDs, but OpenRGB does not. Use one specific | + | LED's mode for any LED. | + \*-----------------------------------------------------------------------------------------*/ + buffer[EVGA_PERIPHERAL_MODE_BYTE] = led_states[EVGA_PERIPHERAL_LED_SOURCE_OF_TRUTH].mode; + buffer[EVGA_PERIPHERAL_BRIGHTNESS_BYTE] = brightness; + buffer[EVGA_PERIPHERAL_SPEED_BYTE] = speed; + + /*-----------------------------------------------------------------------*\ + | 7 is the maximum number of colors that can be set from the vendor's UI. | + \*-----------------------------------------------------------------------*/ + unsigned char color_count = (unsigned char)std::min(colors.size(), static_cast::size_type>(7)); + buffer[EVGA_PERIPHERAL_COLOR_COUNT_BYTE] = color_count; + for(unsigned char i = 0; i < color_count; i++) + { + buffer[15 + i * 3] = RGBGetRValue(colors[i]); + buffer[16 + i * 3] = RGBGetGValue(colors[i]); + buffer[17 + i * 3] = RGBGetBValue(colors[i]); + } + int err = hid_send_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE); + if(err == -1) + { + const wchar_t* err_str = hid_error(dev); + LOG_DEBUG("[%s] Error writing buffer %s", name.c_str(), err_str); + } + led_states[index].brightness = brightness; + led_states[index].speed = speed; + led_states[index].colors = colors; + /*------------------------------------------------------------------------------------*\ + | If the device returns a response not ready packet, future writes will silently fail. | + | Wait until the device sends a valid packet to proceed. | + \*------------------------------------------------------------------------------------*/ + ReadPacketOrLogErrors(buffer, EVGA_PERIPHERAL_MAX_ATTEMPTS); +} + +void EVGAMouseController::RefreshDeviceState() +{ + RefreshDeviceState(EVGA_PERIPHERAL_LED_FRONT); + RefreshDeviceState(EVGA_PERIPHERAL_LED_WHEEL); + RefreshDeviceState(EVGA_PERIPHERAL_LED_LOGO); +} + +void EVGAMouseController::RefreshDeviceState(int led) +{ + unsigned char buffer[EVGA_PERIPHERAL_PACKET_SIZE] = + { + 0x00, + 0x00, 0x00, static_cast(connection_type), 0x1D, + 0x02, 0x80, 0x02 + }; + buffer[EVGA_PERIPHERAL_LED_INDEX_BYTE] = static_cast(led); + int err = hid_send_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE); + if(err == -1) + { + const wchar_t* err_str = hid_error(dev); + LOG_DEBUG("[%s] Error writing buffer %s", name.c_str(), err_str); + } + /*------------------------------------------------------------------------------*\ + | Wait in wireless mode or else packets might be sent too quickly to take effect | + \*------------------------------------------------------------------------------*/ + Wait(); + if(ReadPacketOrLogErrors(buffer, EVGA_PERIPHERAL_MAX_ATTEMPTS)) + { + int color_count = buffer[EVGA_PERIPHERAL_COLOR_COUNT_BYTE]; + if(color_count == 0) + { + LOG_VERBOSE("[%s] No colors read from response. The device is likely asleep.", name.c_str()); + return; + } + led_states[led].mode = buffer[EVGA_PERIPHERAL_MODE_BYTE]; + led_states[led].brightness = buffer[EVGA_PERIPHERAL_BRIGHTNESS_BYTE]; + led_states[led].speed = buffer[EVGA_PERIPHERAL_SPEED_BYTE]; + led_states[led].colors.resize(std::max(color_count, 1)); + for(int i = 0; i < color_count; i++) + { + uint8_t r = buffer[EVGA_PERIPHERAL_RED_BYTE + i * 3]; + uint8_t g = buffer[EVGA_PERIPHERAL_GREEN_BYTE + i * 3]; + uint8_t b = buffer[EVGA_PERIPHERAL_BLUE_BYTE + i * 3]; + led_states[led].colors[i] = ToRGBColor(r, g, b); + } + } +} + +bool EVGAMouseController::ReadPacketOrLogErrors(unsigned char *buffer, int max_attempts) +{ + int bytes_read = ReadPacketOrWait(buffer, max_attempts); + if(bytes_read == -1) + { + const wchar_t* err_str = hid_error(dev); + LOG_DEBUG("[%s] Error reading buffer %s", name.c_str(), err_str); + return false; + } + else if(IsResponseNotReadyPacket(buffer)) + { + LOG_VERBOSE("[%s] Retries exhausted reading from device. Write may have failed.", name.c_str()); + return false; + } + else if(IsAsleepPacket(buffer)) + { + LOG_VERBOSE("[%s] Device is asleep. Cannot send or receive packets until the device is awoken.", name.c_str()); + return false; + } + return true; +} + +int EVGAMouseController::ReadPacketOrWait(unsigned char *buffer, int max_attempts) +{ + int attempts = 1; + Wait(); + int bytes_read = hid_get_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE); + while(bytes_read == EVGA_PERIPHERAL_PACKET_SIZE && attempts < max_attempts && IsResponseNotReadyPacket(buffer)) + { + Wait(); + bytes_read = hid_get_feature_report(dev, buffer, EVGA_PERIPHERAL_PACKET_SIZE); + attempts++; + } + return bytes_read; +} + +void EVGAMouseController::Wait() +{ + if(connection_type == EVGA_PERIPHERAL_CONNECTION_TYPE_WIRELESS) + { + std::this_thread::sleep_for(EVGA_PERIPHERAL_PACKET_DELAY); + } +} + +bool EVGAMouseController::IsAsleepPacket(unsigned char *buffer) +{ + const int expected_packet_size = 8; + unsigned char expected_buffer[expected_packet_size] = + { + 0x00, + 0xA4, 0x00, 0x02, 0x1D, + 0x02, 0x80, 0x02 + }; + return BuffersAreEqual(buffer, expected_buffer, expected_packet_size); +} + +bool EVGAMouseController::IsResponseNotReadyPacket(unsigned char *buffer) +{ + const int expected_packet_size = 8; + unsigned char expected_buffer[expected_packet_size] = + { + 0x00, + 0xA0, 0x00, 0x02, 0x1D, + 0x02, 0x80, 0x02 + }; + return BuffersAreEqual(buffer, expected_buffer, expected_packet_size); +} + diff --git a/Controllers/EVGAUSBController/EVGAMouseController/EVGAMouseController.h b/Controllers/EVGAUSBController/EVGAMouseController/EVGAMouseController.h new file mode 100644 index 0000000..474598b --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAMouseController/EVGAMouseController.h @@ -0,0 +1,206 @@ +/*---------------------------------------------------------*\ +| EVGAMouseController.h | +| | +| Driver for EVGA mouse | +| | +| Cooper Knaak 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define EVGA_PERIPHERAL_PACKET_SIZE 65 +#define EVGA_PERIPHERAL_LED_COUNT 3 +#define HID_MAX_STR 255 + +enum +{ + EVGA_PERIPHERAL_MODE_STATIC = 1, + EVGA_PERIPHERAL_MODE_BREATHING = 2, + EVGA_PERIPHERAL_MODE_RAINBOW = 3, + EVGA_PERIPHERAL_MODE_PULSE = 4, + EVGA_PERIPHERAL_MODE_TRIGGER = 6 +}; + +enum +{ + EVGA_PERIPHERAL_LED_FRONT = 0, + EVGA_PERIPHERAL_LED_WHEEL = 1, + EVGA_PERIPHERAL_LED_LOGO = 2 +}; + +/*----------------------------------------------------------------------*\ +| All values in this enum account for the required 0x0 byte at index 0 | +| when using hid* APIs. The byte controlling the red value of an LED is | +| at index 15 in the buffer passed to hid* APIs because there is a 0x0 | +| byte at the beginning of said buffer. It would only be index 14 in the | +| raw packet received by the peripheral. | +\*----------------------------------------------------------------------*/ +enum +{ + EVGA_PERIPHERAL_CONNECTION_TYPE_BYTE = 3, + EVGA_PERIPHERAL_LED_INDEX_BYTE = 8, + EVGA_PERIPHERAL_MODE_BYTE = 9, + /*--------------*\ + | Range [0, 100] | + \*--------------*/ + EVGA_PERIPHERAL_BRIGHTNESS_BYTE = 10, + /*--------------*\ + | Range [0, 100] | + \*--------------*/ + EVGA_PERIPHERAL_SPEED_BYTE = 11, + /*-------------------------------------------------------*\ + | Determines when the lights initiate and terminate: | + | immediately, on key press, or on key release. | + | Currently unused because OpenRGB does not support this. | + \*-------------------------------------------------------*/ + EVGA_PERIPHERAL_EFFECT_DURATION_BYTE = 13, + /*---------------------------------------------------------------------------------*\ + | The byte at this index specifies how many colors are being passed in this packet. | + | The colors are a sequence of 3 bytes per color: red, green, then blue. Thus, when | + | passing 1 for this byte, the device will read the 3 next bytes as the color. When | + | passing 7, the device will read the next 21 bytes in sets of 3. | + \*---------------------------------------------------------------------------------*/ + EVGA_PERIPHERAL_COLOR_COUNT_BYTE = 14, + EVGA_PERIPHERAL_RED_BYTE = 15, + EVGA_PERIPHERAL_GREEN_BYTE = 16, + EVGA_PERIPHERAL_BLUE_BYTE = 17, +}; + +enum +{ + EVGA_PERIPHERAL_CONNECTION_TYPE_WIRED = 0, + EVGA_PERIPHERAL_CONNECTION_TYPE_WIRELESS = 2, +}; + +struct EVGAMouseControllerDeviceState +{ + uint8_t mode; + uint8_t brightness; + uint8_t speed; + std::vector colors; +}; + +class EVGAMouseController +{ +public: + EVGAMouseController(hid_device* dev_handle, char * path, int connection_type, std::string dev_name); + ~EVGAMouseController(); + + std::string GetName(); + std::string GetSerial(); + std::string GetLocation(); + + /*---------------------------------------------------------------*\ + | Gets the mode, colors, or entire state currently on the device. | + | OpenRGB does not support per-zone modes. All zones must be set | + | to the same mode. It's possible to use the vendor's software | + | to set each LED to separate states. These methods use the logo | + | LED (#2) as the source of truth. | + \*---------------------------------------------------------------*/ + uint8_t GetMode(); + EVGAMouseControllerDeviceState GetState(); + + /*-------------------------------------------------------------------------*\ + | Gets the color of the given LED from the device. If a device is in a mode | + | with multiple colors, returns the first color in the list. | + \*-------------------------------------------------------------------------*/ + RGBColor GetColorOfLed(int led); + + inline void SetMode(uint8_t mode) + { + SetMode(mode, 0); + SetMode(mode, 1); + SetMode(mode, 2); + } + void SetMode(uint8_t mode, uint8_t index); + + /*-----------------------------------*\ + | Set a single LED to a single color. | + \*-----------------------------------*/ + void SetLed(uint8_t index, uint8_t brightness, uint8_t speed, RGBColor color); + /*---------------------------------------------------*\ + | Set the LED at the given index to a list of colors. | + \*---------------------------------------------------*/ + void SetLed(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector& colors); + /*---------------------------------------------------------------------------*\ + | Set the LED at the given index to a list of colors, then activate the mode. | + \*---------------------------------------------------------------------------*/ + void SetLedAndActivate(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector& colors); + /*---------------------------------*\ + | Set all LEDs to a list of colors. | + \*---------------------------------*/ + void SetAllLeds(uint8_t brightness, uint8_t speed, const std::vector& colors); + /*---------------------------------------------------------*\ + | Set all LEDs to a list of colors, then activate the mode. | + \*---------------------------------------------------------*/ + void SetAllLedsAndActivate(uint8_t brightness, uint8_t speed, const std::vector& colors); + +private: + hid_device* dev; + std::string location; + std::string name; + int connection_type; + + std::vector led_states; + + /*----------------------------------------------------------------------------------------------------------------*\ + | Sets the led to the given colors with the given brightness and speed. if activate is true, activates the current | + | mode. If false, just sets the colors. | + \*----------------------------------------------------------------------------------------------------------------*/ + void SetLed(uint8_t index, uint8_t brightness, uint8_t speed, const std::vector& colors, bool activate); + + /*-----------------------------------------------------------------------------*\ + | Requests and stores the current mode and colors for all leds from the device. | + \*-----------------------------------------------------------------------------*/ + void RefreshDeviceState(); + + /*----------------------------------------------------------------------------------*\ + | Requests and stores the current mode and colors for the given led from the device. | + \*----------------------------------------------------------------------------------*/ + void RefreshDeviceState(int led); + + /*-----------------------------------------------------------------------------------*\ + | Repeatedly reads a packet from the device until a valid packet is received. If no | + | such packet is received after max_attempts tries, returns false. Otherwise, returns | + | true. buffer must be an array with size EVGA_PERIPHERAL_PACKET_SIZE. | + \*-----------------------------------------------------------------------------------*/ + bool ReadPacketOrLogErrors(unsigned char *buffer, int max_attempts); + + /*-----------------------------------------------------------------------------------*\ + | Repeatedly reads a packet from the device until a valid packet is received. If a | + | "response not ready" packet is returned, try again, up to a maximum number of tries | + | max_attempts. buffer must be an array with size EVGA_PERIPHERAL_PACKET_SIZE. | + | Returns the number of bytes read or -1 on error. | + \*-----------------------------------------------------------------------------------*/ + int ReadPacketOrWait(unsigned char *buffer, int max_attempts); + + /*-----------------------------------------------------------------------------*\ + | Waits a predetermined amount of time to avoid sending packets to frequently. | + | In wireless mode, packets sent too close together might overwrite each other, | + | causing earlier ones to silently not propagate. Does not wait in connection | + | types that do not have this problem. | + \*-----------------------------------------------------------------------------*/ + void Wait(); + + /*------------------------------------------------------------------------------*\ + | Returns true if the packet received from the device signals that the device is | + | asleep and will not send or receive other packets. | + \*------------------------------------------------------------------------------*/ + bool IsAsleepPacket(unsigned char *buffer); + + /*------------------------------------------------------------------------------*\ + | Returns true if the packet received from the device signals that the device is | + | still processing a request device state packet. In this case, the request to | + | read from the device should be retried at a later time. | + \*------------------------------------------------------------------------------*/ + bool IsResponseNotReadyPacket(unsigned char *buffer); +}; + diff --git a/Controllers/EVGAUSBController/EVGAMouseController/RGBController_EVGAMouse.cpp b/Controllers/EVGAUSBController/EVGAMouseController/RGBController_EVGAMouse.cpp new file mode 100644 index 0000000..c66ab67 --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAMouseController/RGBController_EVGAMouse.cpp @@ -0,0 +1,259 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAMouse.cpp | +| | +| RGBController for EVGA mouse | +| | +| Cooper Knaak 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_EVGAMouse.h" +#include "Colors.h" + +/**------------------------------------------------------------------*\ + @name EVGA USB X20 Mouse + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectWiredEVGAMouse,DetectWirelessEVGAMouse + @comment The EVGA USB mouse currently supports the X20 (both wired + and wireless, but not bluetooth). +\*-------------------------------------------------------------------*/ +RGBController_EVGAMouse::RGBController_EVGAMouse(EVGAMouseController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "EVGA"; + type = DEVICE_TYPE_MOUSE; + description = "EVGA Mouse Device"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Static; + Static.name = "Static"; + Static.value = EVGA_PERIPHERAL_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.brightness_min = EVGA_PERIPHERAL_BRIGHTNESS_MIN; + Static.brightness_max = EVGA_PERIPHERAL_BRIGHTNESS_MAX; + Static.brightness = EVGA_PERIPHERAL_BRIGHTNESS_MAX; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EVGA_PERIPHERAL_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.brightness_min = EVGA_PERIPHERAL_BRIGHTNESS_MIN; + Breathing.brightness_max = EVGA_PERIPHERAL_BRIGHTNESS_MAX; + Breathing.brightness = EVGA_PERIPHERAL_BRIGHTNESS_MAX; + Breathing.colors_min = 2; + Breathing.colors_max = 2; + Breathing.colors = {COLOR_GREEN, COLOR_BLUE}; + Breathing.speed_min = EVGA_PERIPHERAL_SPEED_SLOWEST; + Breathing.speed_max = EVGA_PERIPHERAL_SPEED_FASTEST; + Breathing.speed = EVGA_PERIPHERAL_SPEED_FASTEST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Breathing); + + mode Rainbow; + Rainbow.name = "Spectrum Cycle"; + Rainbow.value = EVGA_PERIPHERAL_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.brightness_min = EVGA_PERIPHERAL_BRIGHTNESS_MIN; + Rainbow.brightness_max = EVGA_PERIPHERAL_BRIGHTNESS_MAX; + Rainbow.brightness = EVGA_PERIPHERAL_BRIGHTNESS_MAX; + Rainbow.speed_min = EVGA_PERIPHERAL_SPEED_SLOWEST; + Rainbow.speed_max = EVGA_PERIPHERAL_SPEED_FASTEST; + Rainbow.speed = EVGA_PERIPHERAL_SPEED_FASTEST; + Rainbow.colors = {COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_MAGENTA, COLOR_WHITE}; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = EVGA_PERIPHERAL_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Pulse.brightness_min = EVGA_PERIPHERAL_BRIGHTNESS_MIN; + Pulse.brightness_max = EVGA_PERIPHERAL_BRIGHTNESS_MAX; + Pulse.brightness = EVGA_PERIPHERAL_BRIGHTNESS_MAX; + Pulse.speed_min = EVGA_PERIPHERAL_SPEED_SLOWEST; + Pulse.speed_max = EVGA_PERIPHERAL_SPEED_FASTEST; + Pulse.speed = EVGA_PERIPHERAL_SPEED_FASTEST; + Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulse.colors_min = 2; + Pulse.colors_max = 7; + Pulse.colors = {COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_MAGENTA, COLOR_WHITE}; + modes.push_back(Pulse); + + mode Trigger; + Trigger.name = "Trigger"; + /*-----------------------------------*\ + | Pulse to Trigger skips from 4 to 6. | + \*-----------------------------------*/ + Trigger.value = EVGA_PERIPHERAL_MODE_TRIGGER; + Trigger.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Trigger.brightness_min = EVGA_PERIPHERAL_BRIGHTNESS_MIN; + Trigger.brightness_max = EVGA_PERIPHERAL_BRIGHTNESS_MAX; + Trigger.brightness = EVGA_PERIPHERAL_BRIGHTNESS_MAX; + Trigger.color_mode = MODE_COLORS_MODE_SPECIFIC; + Trigger.colors_min = 7; + Trigger.colors_max = 7; + Trigger.colors = {COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_MAGENTA, COLOR_WHITE}; + modes.push_back(Trigger); + + active_mode = EVGA_PERIPHERAL_MODE_STATIC; + + Init_Controller(); + SetupZones(); + + EVGAMouseControllerDeviceState current_state = controller->GetState(); + for(unsigned int i = 0; i < modes.size(); i++) + { + if(modes[i].value == current_state.mode) + { + active_mode = i; + break; + } + } + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + modes[active_mode].colors = current_state.colors; + } + modes[active_mode].brightness = current_state.brightness; + modes[active_mode].speed = current_state.speed; + colors.resize(EVGA_PERIPHERAL_LED_COUNT); + for(unsigned int i = 0; i < colors.size(); i++) + { + colors[i] = controller->GetColorOfLed(i); + } +} + +RGBController_EVGAMouse::~RGBController_EVGAMouse() +{ + delete controller; +} + +void RGBController_EVGAMouse::Init_Controller() +{ + /*------------------------------------------------------------------------------------------*\ + | Since each LED can have its own mode, each one needs to be its own zone with a single LED. | + \*------------------------------------------------------------------------------------------*/ + zone front_zone; + front_zone.name = "Front"; + front_zone.type = ZONE_TYPE_SINGLE; + front_zone.leds_min = 1; + front_zone.leds_max = 1; + front_zone.leds_count = 1; + front_zone.matrix_map = NULL; + zones.push_back(front_zone); + + zone wheel_zone; + wheel_zone.name = "Scroll Wheel"; + wheel_zone.type = ZONE_TYPE_SINGLE; + wheel_zone.leds_min = 1; + wheel_zone.leds_max = 1; + wheel_zone.leds_count = 1; + wheel_zone.matrix_map = NULL; + zones.push_back(wheel_zone); + + zone logo_zone; + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + led front_led; + front_led.name = "Front LED"; + front_led.value = 0; + leds.push_back(front_led); + + led wheel_led; + wheel_led.name = "Scroll Wheel LED"; + wheel_led.value = 1; + leds.push_back(wheel_led); + + led back_led; + back_led.name = "Back LED"; + back_led.value = 2; + leds.push_back(back_led); +} + +void RGBController_EVGAMouse::SetupZones() +{ + SetupColors(); +} + +void RGBController_EVGAMouse::ResizeZone(int /* zone */, int /* new_size */) +{ + /*--------------------------------------*\ + | This device does not support resizing. | + \*--------------------------------------*/ +} + +void RGBController_EVGAMouse::DeviceUpdateLEDs() +{ + for(unsigned int i = 0; i < colors.size(); i++) + { + controller->SetLed(i, modes[active_mode].brightness, modes[active_mode].speed, colors[i]); + } +} + +void RGBController_EVGAMouse::UpdateZoneLEDs(int zone) +{ + controller->SetLed(zone, modes[active_mode].brightness, modes[active_mode].speed, colors[zone]); +} + +void RGBController_EVGAMouse::UpdateSingleLED(int led) +{ + controller->SetLed(led, modes[active_mode].brightness, modes[active_mode].speed, colors[led]); +} + +void RGBController_EVGAMouse::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].value); + /*--------------------------------------------------------------------*\ + | Modes with specific colors should use their mode's colors. All other | + | modes should use the colors stored in this controller. | + \*--------------------------------------------------------------------*/ + std::vector* temp_colors = &colors; + /*-------------------------------------------------------------------------------------------------*\ + | Rainbow does not have mode specific colors that can be controlled by OpenRGB, so it does not have | + | the corresponding flag. However, to properly activate it, you still must pass the correct list of | + | colors in the LED packet. Specifying the wrong number of colors causes the effect to restart the | + | cycle earlier in the sequence than it is supposed to. | + \*-------------------------------------------------------------------------------------------------*/ + if((modes[active_mode].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) || modes[active_mode].value == EVGA_PERIPHERAL_MODE_RAINBOW) + { + temp_colors = &(modes[active_mode].colors); + } + if(modes[active_mode].value == EVGA_PERIPHERAL_MODE_BREATHING) + { + controller->SetAllLedsAndActivate(modes[active_mode].brightness, modes[active_mode].speed, *temp_colors); + } + else + { + controller->SetAllLeds(modes[active_mode].brightness, modes[active_mode].speed, *temp_colors); + } +} + +void RGBController_EVGAMouse::DeviceSaveMode() +{ + +} + +int RGBController_EVGAMouse::GetDeviceMode() +{ + return controller->GetMode(); +} + diff --git a/Controllers/EVGAUSBController/EVGAMouseController/RGBController_EVGAMouse.h b/Controllers/EVGAUSBController/EVGAMouseController/RGBController_EVGAMouse.h new file mode 100644 index 0000000..b54dee5 --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAMouseController/RGBController_EVGAMouse.h @@ -0,0 +1,44 @@ +/*---------------------------------------------------------*\ +| RGBController_EVGAMouse.h | +| | +| RGBController for EVGA mouse | +| | +| Cooper Knaak 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "EVGAMouseController.h" + +#define EVGA_PERIPHERAL_BRIGHTNESS_MIN 0 +#define EVGA_PERIPHERAL_BRIGHTNESS_MAX 100 +#define EVGA_PERIPHERAL_SPEED_SLOWEST 0 +#define EVGA_PERIPHERAL_SPEED_FASTEST 100 + +class RGBController_EVGAMouse : public RGBController +{ +public: + RGBController_EVGAMouse(EVGAMouseController* evga); + ~RGBController_EVGAMouse(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + void Init_Controller(); + int GetDeviceMode(); + + EVGAMouseController* controller; +}; diff --git a/Controllers/EVGAUSBController/EVGAUSBControllerDetect.cpp b/Controllers/EVGAUSBController/EVGAUSBControllerDetect.cpp new file mode 100644 index 0000000..38a9f69 --- /dev/null +++ b/Controllers/EVGAUSBController/EVGAUSBControllerDetect.cpp @@ -0,0 +1,77 @@ +/*---------------------------------------------------------*\ +| EVGAUSBControllerDetect.cpp | +| | +| Detector for EVGA USB devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "RGBController_EVGAKeyboard.h" +#include "RGBController_EVGAMouse.h" + +/*-----------------------------------------------------*\ +| EVGA USB vendor ID | +\*-----------------------------------------------------*/ +#define EVGA_USB_VID 0x3842 + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define Z15_ISO_PID 0x260E +#define Z15_ANSI_PID 0x2608 +#define Z20_ANSI_PID 0x260A +#define Z20_UK_PID 0x2610 + +/*-----------------------------------------------------*\ +| Mouse product IDs | +\*-----------------------------------------------------*/ +#define X20_WIRED_PID 0x2420 +#define X20_WIRELESS_ADAPTER_PID 0x2402 + +void DetectEVGAKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + EVGAKeyboardController* controller = new EVGAKeyboardController(dev, info->path, info->product_id, name); + RGBController_EVGAKeyboard* rgb_controller = new RGBController_EVGAKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectEVGAMouse(hid_device_info* info, const std::string &name, int connection_type) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + EVGAMouseController* controller = new EVGAMouseController(dev, info->path, connection_type, name); + RGBController_EVGAMouse* rgb_controller = new RGBController_EVGAMouse(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectWiredEVGAMouse(hid_device_info* info, const std::string &name) +{ + DetectEVGAMouse(info, name, EVGA_PERIPHERAL_CONNECTION_TYPE_WIRED); +} + +void DetectWirelessEVGAMouse(hid_device_info* info, const std::string &name) +{ + DetectEVGAMouse(info, name, EVGA_PERIPHERAL_CONNECTION_TYPE_WIRELESS); +} + + +REGISTER_HID_DETECTOR_IPU("EVGA Z15 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z15_ISO_PID, 1, 0x08, 0x4B); +REGISTER_HID_DETECTOR_IPU("EVGA Z15 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z15_ANSI_PID, 1, 0x08, 0x4B); +REGISTER_HID_DETECTOR_IPU("EVGA Z20 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z20_ANSI_PID, 1, 0x08, 0x4B); +REGISTER_HID_DETECTOR_IPU("EVGA Z20 Keyboard", DetectEVGAKeyboardControllers, EVGA_USB_VID, Z20_UK_PID, 1, 0x08, 0x4B); + +REGISTER_HID_DETECTOR_IPU("EVGA X20 Gaming Mouse", DetectWiredEVGAMouse, EVGA_USB_VID, X20_WIRED_PID, 2, 0xFFFF, 0); +REGISTER_HID_DETECTOR_IPU("EVGA X20 USB Receiver", DetectWirelessEVGAMouse, EVGA_USB_VID, X20_WIRELESS_ADAPTER_PID, 2, 0xFFFF, 0); diff --git a/Controllers/EVisionKeyboardController/EVisionKeyboardController.cpp b/Controllers/EVisionKeyboardController/EVisionKeyboardController.cpp new file mode 100644 index 0000000..0c99571 --- /dev/null +++ b/Controllers/EVisionKeyboardController/EVisionKeyboardController.cpp @@ -0,0 +1,268 @@ +/*---------------------------------------------------------*\ +| EVisionKeyboardController.cpp | +| | +| Driver for EVision keyboard (Redragon, Glorious, Ajazz, | +| Tecware, and many other brands) | +| | +| Adam Honse (CalcProgrammer1) 15 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "EVisionKeyboardController.h" +#include "StringUtils.h" + +EVisionKeyboardController::EVisionKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +EVisionKeyboardController::~EVisionKeyboardController() +{ + hid_close(dev); +} + +std::string EVisionKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string EVisionKeyboardController::GetNameString() +{ + return(name); +} + +std::string EVisionKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void EVisionKeyboardController::SetKeyboardColors + ( + unsigned char * color_data, + unsigned int size + ) +{ + unsigned int packet_size = 0; + unsigned int packet_offset = 0; + + while(size > 0) + { + if(size >= EVISION_KB_MAX_PACKET_SIZE) + { + packet_size = EVISION_KB_MAX_PACKET_SIZE; + } + else + { + packet_size = size; + } + + SendKeyboardData + ( + &color_data[packet_offset], + packet_size, + packet_offset + ); + + size -= packet_size; + packet_offset += packet_size; + } +} + +void EVisionKeyboardController::SendKeyboardMode + ( + unsigned char mode + ) +{ + SendKeyboardParameter(EVISION_KB_PARAMETER_MODE, 1, &mode); +} + +void EVisionKeyboardController::SendKeyboardModeEx + ( + unsigned char mode, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + unsigned char random_flag, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char parameter_data[8]; + + parameter_data[0] = mode; + parameter_data[1] = brightness; + parameter_data[2] = speed; + parameter_data[3] = direction; + parameter_data[4] = random_flag; + parameter_data[5] = red; + parameter_data[6] = green; + parameter_data[7] = blue; + + SendKeyboardParameter(0, 8, parameter_data); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void EVisionKeyboardController::ComputeChecksum + ( + char usb_buf[64] + ) +{ + unsigned short checksum = 0; + + for(unsigned int byte_idx = 0x03; byte_idx < 64; byte_idx++) + { + checksum += usb_buf[byte_idx]; + } + + usb_buf[0x01] = checksum & 0xFF; + usb_buf[0x02] = checksum >> 8; +} + +void EVisionKeyboardController::SendKeyboardBegin() +{ + char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Keyboard Begin (0x01) packet | + | Note: Not computing checksum as packet contents are | + | fixed | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x04; + usb_buf[0x01] = EVISION_KB_COMMAND_BEGIN; + usb_buf[0x02] = 0x00; + usb_buf[0x03] = EVISION_KB_COMMAND_BEGIN; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, 64); + hid_read(dev, (unsigned char *)usb_buf, 64); +} + +void EVisionKeyboardController::SendKeyboardEnd() +{ + char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Keyboard End (0x02) packet | + | Note: Not computing checksum as packet contents are | + | fixed | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x04; + usb_buf[0x01] = EVISION_KB_COMMAND_END; + usb_buf[0x02] = 0x00; + usb_buf[0x03] = EVISION_KB_COMMAND_END; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, 64); + hid_read(dev, (unsigned char *)usb_buf, 64); +} + +void EVisionKeyboardController::SendKeyboardData + ( + unsigned char * data, + unsigned char data_size, + unsigned short data_offset + ) +{ + char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Keyboard Color Data (0x11) packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x04; + usb_buf[0x03] = 0x11; + + usb_buf[0x04] = data_size; + usb_buf[0x05] = data_offset & 0x00FF; + usb_buf[0x06] = data_offset >> 8; + + /*-----------------------------------------------------*\ + | Copy in data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x08], data, data_size); + + /*-----------------------------------------------------*\ + | Compute Checksum | + \*-----------------------------------------------------*/ + ComputeChecksum(usb_buf); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, 64); + hid_read(dev, (unsigned char *)usb_buf, 64); +} + +void EVisionKeyboardController::SendKeyboardParameter + ( + unsigned char parameter, + unsigned char parameter_size, + unsigned char* parameter_data + ) +{ + char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Keyboard Parameter (0x06) packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x04; + usb_buf[0x03] = EVISION_KB_COMMAND_SET_PARAMETER; + usb_buf[0x04] = parameter_size; + usb_buf[0x05] = parameter; + + /*-----------------------------------------------------*\ + | Copy in data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x08], parameter_data, parameter_size); + + /*-----------------------------------------------------*\ + | Compute Checksum | + \*-----------------------------------------------------*/ + ComputeChecksum(usb_buf); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, 64); + hid_read(dev, (unsigned char *)usb_buf, 64); +} diff --git a/Controllers/EVisionKeyboardController/EVisionKeyboardController.h b/Controllers/EVisionKeyboardController/EVisionKeyboardController.h new file mode 100644 index 0000000..70ff24b --- /dev/null +++ b/Controllers/EVisionKeyboardController/EVisionKeyboardController.h @@ -0,0 +1,154 @@ +/*---------------------------------------------------------*\ +| EVisionKeyboardController.h | +| | +| Driver for EVision keyboard (Redragon, Glorious, Ajazz, | +| Tecware, and many other brands) | +| | +| Adam Honse (CalcProgrammer1) 15 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define EVISION_KB_MAX_PACKET_SIZE ( 0x36 )/* max packet size for color*/ + /* update packets */ +enum +{ + EVISION_KB_COMMAND_BEGIN = 0x01, /* Begin packet command */ + EVISION_KB_COMMAND_END = 0x02, /* End packet command */ + EVISION_KB_COMMAND_SET_PARAMETER = 0x06, /* Set parameter command */ + EVISION_KB_COMMAND_READ_CUSTOM_COLOR_DATA = 0x10, /* Read custom color data */ + EVISION_KB_COMMAND_WRITE_CUSTOM_COLOR_DATA = 0x11, /* Write custom color data */ +}; + +enum +{ + EVISION_KB_PARAMETER_MODE = 0x00, /* Mode parameter */ + EVISION_KB_PARAMETER_BRIGHTNESS = 0x01, /* Brightness parameter */ + EVISION_KB_PARAMETER_SPEED = 0x02, /* Speed parameter */ + EVISION_KB_PARAMETER_DIRECTION = 0x03, /* Direction parameter */ + EVISION_KB_PARAMETER_RANDOM_COLOR_FLAG = 0x04, /* Random color parameter */ + EVISION_KB_PARAMETER_MODE_COLOR = 0x05, /* Mode color (RGB) */ + EVISION_KB_PARAMETER_POLLING_RATE = 0x0F, /* Polling rate */ + EVISION_KB_PARAMETER_SURMOUNT_MODE_COLOR = 0x11, /* Surmount mode color */ +}; + +enum +{ + EVISION_KB_MODE_COLOR_WAVE_SHORT = 0x01, /* "Go with the stream" */ + EVISION_KB_MODE_COLOR_WAVE_LONG = 0x02, /* "Clouds fly" */ + EVISION_KB_MODE_COLOR_WHEEL = 0x03, /* "Winding paths" */ + EVISION_KB_MODE_SPECTRUM_CYCLE = 0x04, /* "The trial of light" */ + EVISION_KB_MODE_BREATHING = 0x05, /* "Breathing" */ + EVISION_KB_MODE_STATIC = 0x06, /* "Normally on" */ + EVISION_KB_MODE_REACTIVE = 0x07, /* "Pass without trace" */ + EVISION_KB_MODE_REACTIVE_RIPPLE = 0x08, /* "Ripple graff" */ + EVISION_KB_MODE_REACTIVE_LINE = 0x09, /* "Fast run without trace" */ + EVISION_KB_MODE_STARLIGHT_FAST = 0x0A, /* "Swift action" */ + EVISION_KB_MODE_BLOOMING = 0x0B, /* "Flowers blooming" */ + EVISION_KB_MODE_RAINBOW_WAVE_VERTICAL = 0x0C, /* "Snow winter jasmine" */ + EVISION_KB_MODE_HURRICANE = 0x0D, /* "Hurricane" */ + EVISION_KB_MODE_ACCUMULATE = 0x0E, /* "Accumulate" */ + EVISION_KB_MODE_STARLIGHT_SLOW = 0x0F, /* "Digital times" */ + EVISION_KB_MODE_VISOR = 0x10, /* "Both ways" */ + EVISION_KB_MODE_SURMOUNT = 0x11, /* "Surmount" */ + EVISION_KB_MODE_RAINBOW_WAVE_CIRCLE = 0x12, /* "Fast and the Furious" */ + EVISION_KB_MODE_CUSTOM = 0x14, /* "Coastal" */ +}; + +enum +{ + EVISION_KB_BRIGHTNESS_LOWEST = 0x00, /* Lowest brightness (off) */ + EVISION_KB_BRIGHTNESS_HIGHEST = 0x04, /* Highest brightness */ +}; + +enum +{ + EVISION_KB_SPEED_SLOWEST = 0x05, /* Slowest speed setting */ + EVISION_KB_SPEED_NORMAL = 0x03, /* Normal speed setting */ + EVISION_KB_SPEED_FASTEST = 0x00, /* Fastest speed setting */ +}; + +enum +{ + EVISION_KB_SURMOUNT_MODE_COLOR_RED = 0x01, /* Red surmount color */ + EVISION_KB_SURMOUNT_MODE_COLOR_YELLOW = 0x02, /* Yellow surmount color */ + EVISION_KB_SURMOUNT_MODE_COLOR_GREEN = 0x03, /* Green surmount color */ + EVISION_KB_SURMOUNT_MODE_COLOR_BLUE = 0x04, /* Blue surmount color */ +}; + +enum +{ + EVISION_KB_POLLING_RATE_125HZ = 0x00, /* 125Hz polling rate */ + EVISION_KB_POLLING_RATE_250HZ = 0x01, /* 250Hz polling rate */ + EVISION_KB_POLLING_RATE_500HZ = 0x02, /* 500Hz polling rate */ + EVISION_KB_POLLING_RATE_1000HZ = 0x03, /* 1000Hz polling rate */ +}; + +class EVisionKeyboardController +{ +public: + EVisionKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name); + ~EVisionKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetKeyboardColors + ( + unsigned char * color_data, + unsigned int size + ); + + void SendKeyboardBegin(); + + void SendKeyboardMode + ( + unsigned char mode + ); + + void SendKeyboardModeEx + ( + unsigned char mode, + unsigned char brightness, + unsigned char speed, + unsigned char direction, + unsigned char random_flag, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SendKeyboardData + ( + unsigned char * data, + unsigned char data_size, + unsigned short data_offset + ); + + void SendKeyboardEnd(); + +private: + hid_device* dev; + std::string location; + std::string name; + + void ComputeChecksum + ( + char usb_buf[64] + ); + + void SendKeyboardParameter + ( + unsigned char parameter, + unsigned char parameter_size, + unsigned char* parameter_data + ); +}; diff --git a/Controllers/EVisionKeyboardController/EVisionKeyboardControllerDetect.cpp b/Controllers/EVisionKeyboardController/EVisionKeyboardControllerDetect.cpp new file mode 100644 index 0000000..f1a96d6 --- /dev/null +++ b/Controllers/EVisionKeyboardController/EVisionKeyboardControllerDetect.cpp @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| EVisionKeyboardControllerDetect.cpp | +| | +| Detector for EVision keyboards | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "EVisionKeyboardController.h" +#include "EVisionV2KeyboardController.h" +#include "RGBController_EVisionKeyboard.h" +#include "RGBController_EVisionV2Keyboard.h" +#include "SettingsManager.h" + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define EVISION_KEYBOARD_VID 0x0C45 +#define EVISION_KEYBOARD2_VID 0x320F +#define EVISION_KEYBOARD3_VID 0x3299 +#define EVISION_KEYBOARD_USAGE_PAGE 0xFF1C +#define ENDORFY_OMNIS_PID 0x0012 +#define DEXP_BLAZE_PID 0x5084 +#define GLORIOUS_GMMK_TKL_PID 0x5064 +#define REDRAGON_K550_PID 0x5204 +#define MARS_GAMING_MKMINI_PID 0x5078 +#define SKILLKORP_K5_PID 0x505B +#define REDRAGON_K552_PID 0x5104 +#define REDRAGON_K552_V2_PID 0x5000 +#define REDRAGON_K556_PID 0x5004 +#define TECWARE_PHANTOM_ELITE_PID 0x652F +#define WARRIOR_KANE_TC235 0x8520 +#define WOMIER_K87_PID 0x502A +#define WOMIER_K66_PID 0x7698 +#define BYGG_CSB_ICL01_PID 0x5041 +#define GAMEPOWER_OGRE_RGB_PID 0x7672 + +/******************************************************************************************\ +* * +* DetectEVisionKeyboards * +* * +* Tests the USB address to see if an EVision RGB Keyboard controller exists there. * +* * +\******************************************************************************************/ + +void DetectEVisionKeyboards(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + EVisionKeyboardController* controller = new EVisionKeyboardController(dev, info->path, name); + RGBController_EVisionKeyboard* rgb_controller = new RGBController_EVisionKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectEVisionV2Keyboards(hid_device_info* info, const std::string& name) +{ + json settings = ResourceManager::get()->GetSettingsManager()->GetSettings("EVision2Settings"); + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + EVisionV2KeyboardController* controller = new EVisionV2KeyboardController(dev, info->path, EVISION_V2_KEYBOARD_LAYOUT, name); + RGBController_EVisionV2Keyboard* rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_KEYBOARD); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + + if(!settings.contains("AdditionalZones") || settings["AdditionalZones"] == true) + { + rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_LOGO); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + + rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_EDGE); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} + +void DetectEndorfyKeyboards(hid_device_info* info, const std::string& name) +{ + json settings = ResourceManager::get()->GetSettingsManager()->GetSettings("EndorfySettings"); + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + EVisionV2KeyboardController* controller = new EVisionV2KeyboardController(dev, info->path, ENDORFY_KEYBOARD_LAYOUT, name); + RGBController_EVisionV2Keyboard* rgb_controller = new RGBController_EVisionV2Keyboard(controller, EVISION_V2_KEYBOARD_PART_KEYBOARD); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + + if(!settings.contains("AdditionalZones") || settings["AdditionalZones"] == true) + { + rgb_controller = new RGBController_EVisionV2Keyboard(controller, ENDORFY_KEYBOARD_PART_EDGE); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} + +/*---------------------------------------------------------------------------------------------------------------------------------------------*\ +| Keyboards | +\*---------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5078", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, MARS_GAMING_MKMINI_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:5204", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, REDRAGON_K550_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:5104", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, REDRAGON_K552_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5000", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, REDRAGON_K552_V2_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:5004", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, REDRAGON_K556_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:652F", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, TECWARE_PHANTOM_ELITE_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:8520", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, WARRIOR_KANE_TC235, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:502A", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, WOMIER_K87_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 0C45:7698", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, WOMIER_K66_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5064", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, GLORIOUS_GMMK_TKL_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:5084", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, DEXP_BLAZE_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("EVision Keyboard 320F:505B", DetectEVisionKeyboards, EVISION_KEYBOARD2_VID, SKILLKORP_K5_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Endorfy Omnis", DetectEndorfyKeyboards, EVISION_KEYBOARD3_VID, ENDORFY_OMNIS_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("CSB/ICL01 Keyboard", DetectEVisionV2Keyboards, EVISION_KEYBOARD2_VID, BYGG_CSB_ICL01_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Gamepower Ogre RGB 0C45:7672", DetectEVisionKeyboards, EVISION_KEYBOARD_VID, GAMEPOWER_OGRE_RGB_PID, 1, EVISION_KEYBOARD_USAGE_PAGE); diff --git a/Controllers/EVisionKeyboardController/EVisionV2KeyboardController.cpp b/Controllers/EVisionKeyboardController/EVisionV2KeyboardController.cpp new file mode 100644 index 0000000..4abe22c --- /dev/null +++ b/Controllers/EVisionKeyboardController/EVisionV2KeyboardController.cpp @@ -0,0 +1,465 @@ +/*---------------------------------------------------------*\ +| EVisionV2KeyboardController.cpp | +| | +| Driver for EVision V2 keyboard | +| | +| Le Philousophe 25 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "EVisionV2KeyboardController.h" +#include "StringUtils.h" + +#define BLANK_SPACE 6 +#define query_check_buffer(c) \ + do \ + { \ + if(!(c)) \ + { \ + return -256; \ + } \ + } while(0) + +using namespace std::chrono_literals; + +static uint8_t evisionv2_map[EVISION_V2_MATRIX_WIDTH * EVISION_V2_MATRIX_HEIGHT] = +{ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 19 19 20 */ + 0, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96, + 1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, 85, 91, 97, 103, 109, 115, 121, + 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 74, 80, 86, 92, 98, 104, 110, 116, 122, + 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 81, 105, 111, 117, + 4, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70, 82, 94, 106, 112, 118, 124, + 5, 11, 17, 41, 65, 71, 77, 83, 89, 95, 101, 113, 119, +}; + +static uint8_t endorfy_map[EVISION_V2_MATRIX_WIDTH * EVISION_V2_MATRIX_HEIGHT] = +{ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 19 19 20 */ + 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 80, 81, 82, + 84, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 97, 99, 101, 102, 103, 104, + 105, 106, 107, 111, 115, 116, 117, 118, 119, 120, 121, 123, 124, +}; + +EVisionV2KeyboardController::EVisionV2KeyboardController(hid_device* dev_handle, const char* path, EVisionV2KeyboardLayout dev_layout, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + layout = dev_layout; + + /*---------------------------------------------------------*\ + | Get capabilities and layout | + \*---------------------------------------------------------*/ + uint8_t buffer[7]; + if(Read(EVISION_V2_CMD_READ_CAPABILITIES, 0, sizeof(buffer), buffer) < 0) + { + return; + } + if(buffer[0] != 0xAA && buffer[1] != 0x55) + { + return; + } + + map_size = buffer[5]; + macros_size = buffer[6] * 0x80; + + switch(layout) + { + case EVISION_V2_KEYBOARD_LAYOUT: + keyvalue_map = evisionv2_map; + led_count = 106; + break; + + case ENDORFY_KEYBOARD_LAYOUT: + keyvalue_map = endorfy_map; + led_count = 104; + break; + } +} + +EVisionV2KeyboardController::~EVisionV2KeyboardController() +{ + hid_close(dev); +} + +std::string EVisionV2KeyboardController::GetName() +{ + return(name); +} + +std::string EVisionV2KeyboardController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string EVisionV2KeyboardController::GetLocation() +{ + return("HID: " + location); +} + +int EVisionV2KeyboardController::Query(uint8_t cmd, uint16_t offset, const uint8_t* idata, uint8_t size, uint8_t* odata) +{ + uint8_t buffer[EVISION_V2_PACKET_SIZE]; + memset(buffer, 0, sizeof(buffer)); + + buffer[0] = EVISION_V2_REPORT_ID; + buffer[3] = cmd; + buffer[4] = size; + buffer[5] = offset & 0xff; + buffer[6] = (offset >> 8) & 0xff; + + if(idata) + { + memcpy(buffer + 8, idata, size); + } + + uint16_t chksum = 0; + for(uint8_t* p = &buffer[3]; p != &buffer[EVISION_V2_PACKET_SIZE]; p++) + { + chksum += *p; + } + buffer[1] = chksum & 0xff; + buffer[2] = (chksum >> 8) & 0xff; + + int bytes_read; + { + const std::lock_guard lock(query_mutex); + + hid_write(dev, buffer, sizeof(buffer)); + + do + { + bytes_read = hid_read(dev, buffer, sizeof(buffer)); + } while(bytes_read != 0 && buffer[0] != EVISION_V2_REPORT_ID); + } + query_check_buffer(bytes_read == sizeof(buffer)); + + query_check_buffer(buffer[0] == EVISION_V2_REPORT_ID); + query_check_buffer(buffer[1] == (chksum & 0xff)); + query_check_buffer(buffer[2] == ((chksum >> 8) & 0xff)); + query_check_buffer(buffer[3] == cmd); + query_check_buffer(buffer[5] == (offset & 0xff)); + query_check_buffer(buffer[6] == ((offset >> 8) & 0xff)); + + if(buffer[7] != 0) + { + return -buffer[7]; + } + + size = buffer[4]; + if(size > EVISION_V2_PACKET_SIZE - 8) + { + return -256; + } + + if(odata) + { + memcpy(odata, buffer + 8, size); + } + + return size; +} + +int EVisionV2KeyboardController::BeginConfigure() +{ + return Query(EVISION_V2_CMD_BEGIN_CONFIGURE); +} + +int EVisionV2KeyboardController::EndConfigure() +{ + return Query(EVISION_V2_CMD_END_CONFIGURE); +} + +int EVisionV2KeyboardController::Read(uint8_t cmd, uint16_t offset, uint16_t size, uint8_t* odata) +{ + while(size > 0) + { + uint8_t pktsz = (uint8_t)std::min(size, EVISION_V2_PACKET_SIZE - 8); + int result = Query(cmd, offset, nullptr, pktsz, odata); + if(result <= 0) + { + return result; + } + else if(result > size) + { + return -256; + } + offset += result; + odata += result; + size -= result; + } + return 0; +} + +int EVisionV2KeyboardController::Write(uint8_t cmd, uint16_t offset, const uint8_t* idata, uint16_t size) +{ + while(size > 0) + { + uint8_t pktsz = (uint8_t)std::min(size, EVISION_V2_PACKET_SIZE - 8); + int result = Query(cmd, offset, idata, pktsz); + if(result <= 0) + { + return result; + } + offset += pktsz; + idata += pktsz; + size -= pktsz; + } + return 0; +} + +int EVisionV2KeyboardController::GetMode(EVisionV2KeyboardPart part, EvisionV2ModeConfig& config) +{ + uint8_t buffer[18]; + memset(buffer, 0, sizeof(buffer)); + + uint8_t current_profile; + int ret = Read(EVISION_V2_CMD_READ_CONFIG, EVISION_V2_OFFSET_CURRENT_PROFILE, 1, ¤t_profile); + if(ret < 0) + { + return ret; + } + if(current_profile > 2) + { + current_profile = 0; + } + + uint16_t offset; + uint8_t size; + + offset = current_profile * 0x40 + EVISION_V2_OFFSET_FIRST_PROFILE; + + switch(part) + { + case EVISION_V2_KEYBOARD_PART_KEYBOARD: + size = sizeof(buffer); + break; + case EVISION_V2_KEYBOARD_PART_LOGO: + case ENDORFY_KEYBOARD_PART_EDGE: + offset += EVISION_V2_PARAMETER_LOGO; + size = EVISION_V2_PARAMETER_LOGO_ON_OFF - EVISION_V2_PARAMETER_LOGO + 1; + break; + case EVISION_V2_KEYBOARD_PART_EDGE: + offset += EVISION_V2_PARAMETER_EDGE; + size = EVISION_V2_PARAMETER_END - EVISION_V2_PARAMETER_EDGE; + break; + default: + size = 0; + break; + } + + ret = Read(EVISION_V2_CMD_READ_CONFIG, offset, size, buffer); + if(ret < 0) + { + return ret; + } + + config.mode = buffer[EVISION_V2_PARAMETER_MODE]; + config.brightness = buffer[EVISION_V2_PARAMETER_BRIGHTNESS]; + config.speed = buffer[EVISION_V2_PARAMETER_SPEED]; + config.direction = buffer[EVISION_V2_PARAMETER_DIRECTION]; + config.random_colours = buffer[EVISION_V2_PARAMETER_RANDOM_COLOR_FLAG] != 0; + config.colour = ToRGBColor(buffer[EVISION_V2_PARAMETER_MODE_COLOR + 0], + buffer[EVISION_V2_PARAMETER_MODE_COLOR + 1], buffer[EVISION_V2_PARAMETER_MODE_COLOR + 2]); + if(part == EVISION_V2_KEYBOARD_PART_KEYBOARD) + { + config.ledmode = buffer[EVISION_V2_PARAMETER_LED_MODE_COLOR]; + if(config.mode == EVISION_V2_MODE_CUSTOM) + { + ret = Read(EVISION_V2_CMD_READ_CONFIG, current_profile * 0x40 + EVISION_V2_OFFSET_FIRST_PROFILE + EVISION_V2_PARAMETER_CURRENT_CUSTOM_MODE, sizeof(config.ledmode), &config.ledmode); + if(ret < 0) + { + return ret; + } + } + } + else if(part == EVISION_V2_KEYBOARD_PART_LOGO || part == ENDORFY_KEYBOARD_PART_EDGE) + { + // Use ledmode for logo on/off + config.ledmode = buffer[EVISION_V2_PARAMETER_LOGO_ON_OFF - EVISION_V2_PARAMETER_LOGO]; + } + + return 0; +} + +void EVisionV2KeyboardController::SetMode(EVisionV2KeyboardPart part, const EvisionV2ModeConfig& config) +{ + uint8_t buffer[18]; + memset(buffer, 0, sizeof(buffer)); + + buffer[EVISION_V2_PARAMETER_MODE] = config.mode; + buffer[EVISION_V2_PARAMETER_BRIGHTNESS] = config.brightness; + buffer[EVISION_V2_PARAMETER_SPEED] = config.speed; + buffer[EVISION_V2_PARAMETER_DIRECTION] = config.direction; + buffer[EVISION_V2_PARAMETER_RANDOM_COLOR_FLAG] = (config.random_colours) ? 255 : 0; + buffer[EVISION_V2_PARAMETER_MODE_COLOR + 0] = RGBGetRValue(config.colour); + buffer[EVISION_V2_PARAMETER_MODE_COLOR + 1] = RGBGetGValue(config.colour); + buffer[EVISION_V2_PARAMETER_MODE_COLOR + 2] = RGBGetBValue(config.colour); + if(part == EVISION_V2_KEYBOARD_PART_KEYBOARD) + { + buffer[EVISION_V2_PARAMETER_COLOR_OFFSET] = 0; + if(config.mode != EVISION_V2_MODE_CUSTOM) + { + buffer[EVISION_V2_PARAMETER_LED_MODE_COLOR] = config.ledmode; + } + } + else if(part == EVISION_V2_KEYBOARD_PART_LOGO || part == ENDORFY_KEYBOARD_PART_EDGE) + { + // Use ledmode for logo on/off + buffer[EVISION_V2_PARAMETER_LOGO_ON_OFF - EVISION_V2_PARAMETER_LOGO] = config.ledmode; + } + + BeginConfigure(); + uint8_t current_profile; + + int ret = Read(EVISION_V2_CMD_READ_CONFIG, EVISION_V2_OFFSET_CURRENT_PROFILE, 1, ¤t_profile); + if(ret < 0) + { + return; + } + if(current_profile > 2) + { + current_profile = 0; + Write(EVISION_V2_CMD_WRITE_CONFIG, EVISION_V2_OFFSET_CURRENT_PROFILE, ¤t_profile, 1); + } + + uint16_t offset = 0; + uint8_t size = 0; + + offset = current_profile * 0x40 + EVISION_V2_OFFSET_FIRST_PROFILE; + + switch(part) + { + case EVISION_V2_KEYBOARD_PART_KEYBOARD: + size = sizeof(buffer); + break; + case EVISION_V2_KEYBOARD_PART_LOGO: + case ENDORFY_KEYBOARD_PART_EDGE: + offset += EVISION_V2_PARAMETER_LOGO; + size = EVISION_V2_PARAMETER_LOGO_ON_OFF - EVISION_V2_PARAMETER_LOGO + 1; + break; + case EVISION_V2_KEYBOARD_PART_EDGE: + offset += EVISION_V2_PARAMETER_EDGE; + size = EVISION_V2_PARAMETER_END - EVISION_V2_PARAMETER_EDGE; + break; + } + + Write(EVISION_V2_CMD_WRITE_CONFIG, offset, buffer, size); + if((part == EVISION_V2_KEYBOARD_PART_KEYBOARD) && (config.mode == EVISION_V2_MODE_CUSTOM)) + { + Write(EVISION_V2_CMD_WRITE_CONFIG, current_profile * 0x40 + EVISION_V2_OFFSET_FIRST_PROFILE + EVISION_V2_PARAMETER_CURRENT_CUSTOM_MODE, &config.ledmode, sizeof(config.ledmode)); + } + EndConfigure(); +} + +void EVisionV2KeyboardController::SetLedsDirect(const std::vector& colours) +{ + const size_t colours_num = std::min(colours.size(), led_count); + + uint8_t* buffer = new uint8_t[3 * map_size]; + memset(buffer, 0, 3 * map_size); + + for(size_t i = 0; i < colours_num; i++) + { + size_t j = (size_t)keyvalue_map[i] * 3; + buffer[j + 0] = RGBGetRValue(colours[i]); + buffer[j + 1] = RGBGetGValue(colours[i]); + buffer[j + 2] = RGBGetBValue(colours[i]); + } + + Write(EVISION_V2_CMD_SEND_DYNAMIC_COLORS, 0, buffer, (uint16_t)(3 * map_size)); + + delete[] buffer; +} + +void EVisionV2KeyboardController::SetLedDirect(int led, RGBColor colour) +{ + uint8_t buffer[3]; + buffer[0] = RGBGetRValue(colour); + buffer[1] = RGBGetGValue(colour); + buffer[2] = RGBGetBValue(colour); + + Write(EVISION_V2_CMD_SEND_DYNAMIC_COLORS, keyvalue_map[led] * 3, buffer, sizeof(buffer)); +} + +void EVisionV2KeyboardController::RefreshLedDirect() +{ + // Write one zero byte in the first blank space + Query(EVISION_V2_CMD_SEND_DYNAMIC_COLORS, BLANK_SPACE * 3, nullptr, 1, nullptr); +} + +void EVisionV2KeyboardController::EndLedsDirect() +{ + Query(EVISION_V2_CMD_END_DYNAMIC_COLORS); +} + +int EVisionV2KeyboardController::GetLedsCustom(uint8_t colorset, std::vector& colours) +{ + if(colorset > 9) + { + return -256; + } + + const size_t colours_num = std::min(colours.size(), led_count); + + uint8_t* buffer = new uint8_t[3 * map_size]; + memset(buffer, 0, 3 * map_size); + + int ret = Read(EVISION_V2_CMD_READ_CUSTOM_COLORS, 512 * colorset, (uint16_t)(3 * map_size), buffer); + if(ret < 0) + { + return ret; + } + + for(size_t i = 0; i < colours_num; i++) + { + size_t j = (size_t)keyvalue_map[i] * 3; + colours[i] = ToRGBColor(buffer[j + 0], buffer[j + 1], buffer[j + 2]); + } + + delete[] buffer; + return 0; +} + +void EVisionV2KeyboardController::SetLedsCustom(uint8_t colorset, const std::vector& colours) +{ + if(colorset > 9) + { + return; + } + + const size_t colours_num = std::min(colours.size(), led_count); + + uint8_t* buffer = new uint8_t[3 * map_size]; + memset(buffer, 0, 3 * map_size); + + for(size_t i = 0; i < colours_num; i++) + { + size_t j = (size_t)keyvalue_map[i] * 3; + buffer[j + 0] = RGBGetRValue(colours[i]); + buffer[j + 1] = RGBGetGValue(colours[i]); + buffer[j + 2] = RGBGetBValue(colours[i]); + } + + BeginConfigure(); + Write(EVISION_V2_CMD_WRITE_CUSTOM_COLORS, 512 * colorset, buffer, (uint16_t)(3 * map_size)); + EndConfigure(); + + delete[] buffer; +} diff --git a/Controllers/EVisionKeyboardController/EVisionV2KeyboardController.h b/Controllers/EVisionKeyboardController/EVisionV2KeyboardController.h new file mode 100644 index 0000000..33a8d6f --- /dev/null +++ b/Controllers/EVisionKeyboardController/EVisionV2KeyboardController.h @@ -0,0 +1,197 @@ +/*---------------------------------------------------------*\ +| EVisionV2KeyboardController.h | +| | +| Driver for EVision V2 keyboard | +| | +| Le Philousophe 25 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define EVISION_V2_PACKET_SIZE 64 +#define HID_MAX_STR 255 + +#define EVISION_V2_REPORT_ID 4 + +#define EVISION_V2_MATRIX_HEIGHT 6 +#define EVISION_V2_MATRIX_WIDTH 21 + +enum +{ + EVISION_V2_MODE_COLOR_WAVE_SHORT = 0x01, /* "Go with the stream" */ + EVISION_V2_MODE_COLOR_WAVE_LONG = 0x02, /* "Clouds fly" */ + EVISION_V2_MODE_COLOR_WHEEL = 0x03, /* "Winding paths" */ + EVISION_V2_MODE_SPECTRUM_CYCLE = 0x04, /* "Spectrum" */ + EVISION_V2_MODE_BREATHING = 0x05, /* "Breath" */ + EVISION_V2_MODE_STATIC = 0x06, /* "Normal" */ + EVISION_V2_MODE_REACTIVE = 0x07, /* "Pass without trace" */ + EVISION_V2_MODE_REACTIVE_RIPPLE = 0x08, /* "Ripples" */ + EVISION_V2_MODE_REACTIVE_LINE = 0x09, /* "Stream" */ + EVISION_V2_MODE_STARLIGHT_FAST = 0x0A, /* "Stars" */ + EVISION_V2_MODE_BLOOMING = 0x0B, /* "Flowers" */ + EVISION_V2_MODE_RAINBOW_WAVE_VERTICAL = 0x0C, /* "Swift action" */ + EVISION_V2_MODE_HURRICANE = 0x0D, /* "Hurricane" */ + EVISION_V2_MODE_ACCUMULATE = 0x0E, /* "Cartoon" */ + EVISION_V2_MODE_STARLIGHT_SLOW = 0x0F, /* "Digital times" */ + EVISION_V2_MODE_VISOR = 0x10, /* "Both ways" */ + EVISION_V2_MODE_SURMOUNT = 0x11, /* "Surmount" */ + EVISION_V2_MODE_RAINBOW_WAVE_CIRCLE = 0x12, /* "Speed" */ + EVISION_V2_MODE_CUSTOM = 0x14, /* "Custom" */ + + EVISION_V2_MODE_DIRECT = 0xFF, /* Software controlled mode */ +}; + +enum +{ + EVISION_V2_MODE2_COLOR_WAVE = 0x00, /* "Pulsation" */ + EVISION_V2_MODE2_BREATHING = 0x01, /* "Breath" */ + EVISION_V2_MODE2_YOYO = 0x02, /* "Yoyo" */ + EVISION_V2_MODE2_BLINK = 0x03, /* "Blink" */ + EVISION_V2_MODE2_STATIC = 0x04, /* "Normal" */ + EVISION_V2_MODE2_OFF = 0x05, /* "Off" */ +}; + +enum +{ + /* Official software doesn't support changing edges */ + ENDORFY_MODE2_FREEZE = 0x00, + ENDORFY_MODE2_COLOR_WAVE = 0x01, + ENDORFY_MODE2_SPECTRUM_CYCLE = 0x02, + ENDORFY_MODE2_BREATHING = 0x03, + ENDORFY_MODE2_STATIC = 0x04, + ENDORFY_MODE2_OFF = 0x05, +}; + +enum +{ + EVISION_V2_CMD_BEGIN_CONFIGURE = 0x01, + EVISION_V2_CMD_END_CONFIGURE = 0x02, + EVISION_V2_CMD_READ_CAPABILITIES = 0x03, + //EVISION_V2_CMD_WRITE_CAPABILITIES = 0x04, + EVISION_V2_CMD_READ_CONFIG = 0x05, + EVISION_V2_CMD_WRITE_CONFIG = 0x06, + EVISION_V2_CMD_READ_CUSTOM_COLORS = 0x0A, + EVISION_V2_CMD_WRITE_CUSTOM_COLORS = 0x0B, + EVISION_V2_CMD_SEND_DYNAMIC_COLORS = 0x12, + EVISION_V2_CMD_END_DYNAMIC_COLORS = 0x13, +}; + +enum +{ + EVISION_V2_OFFSET_CURRENT_PROFILE = 0x00, + EVISION_V2_OFFSET_FIRST_PROFILE = 0x01, + EVISION_V2_OFFSET_SECOND_PROFILE = 0x41, + EVISION_V2_OFFSET_THIRD_PROFILE = 0x81, +}; + +enum +{ + EVISION_V2_PARAMETER_MODE = 0x00, /* Mode parameter */ + EVISION_V2_PARAMETER_BRIGHTNESS = 0x01, /* Brightness parameter */ + EVISION_V2_PARAMETER_SPEED = 0x02, /* Speed parameter */ + EVISION_V2_PARAMETER_DIRECTION = 0x03, /* Direction parameter */ + EVISION_V2_PARAMETER_RANDOM_COLOR_FLAG = 0x04, /* Random color parameter */ + EVISION_V2_PARAMETER_MODE_COLOR = 0x05, /* Mode color (RGB) */ + EVISION_V2_PARAMETER_COLOR_OFFSET = 0x08, /* Unknown color offset */ + EVISION_V2_PARAMETER_LED_MODE_COLOR = 0x11, /* Led mode color */ + EVISION_V2_PARAMETER_CURRENT_CUSTOM_MODE = 0x19, /* Custom mode current colorset */ + EVISION_V2_PARAMETER_LOGO = 0x1a, /* Logo parameters */ + EVISION_V2_PARAMETER_LOGO_ON_OFF = 0x23, /* Logo on/off */ + EVISION_V2_PARAMETER_EDGE = 0x24, /* Edge parameters */ + EVISION_V2_PARAMETER_END = 0x2b, /* Address after last parameter */ +}; + +enum +{ + EVISION_V2_BRIGHTNESS_LOWEST = 0x00, /* Lowest brightness (off) */ + EVISION_V2_BRIGHTNESS_HIGHEST = 0x04, /* Highest brightness */ +}; + +enum +{ + EVISION_V2_SPEED_SLOWEST = 0x05, /* Slowest speed setting */ + EVISION_V2_SPEED_NORMAL = 0x03, /* Normal speed setting */ + EVISION_V2_SPEED_FASTEST = 0x00, /* Fastest speed setting */ +}; + +enum +{ + EVISION_V2_SURMOUNT_MODE_COLOR_RED = 0x00, /* Red surmount color */ + EVISION_V2_SURMOUNT_MODE_COLOR_YELLOW = 0x01, /* Yellow surmount color */ + EVISION_V2_SURMOUNT_MODE_COLOR_GREEN = 0x02, /* Green surmount color */ + EVISION_V2_SURMOUNT_MODE_COLOR_CYAN = 0x03, /* Cyan surmount color */ +}; + +enum EVisionV2KeyboardLayout +{ + EVISION_V2_KEYBOARD_LAYOUT, + ENDORFY_KEYBOARD_LAYOUT, +}; + +enum EVisionV2KeyboardPart +{ + EVISION_V2_KEYBOARD_PART_KEYBOARD, + EVISION_V2_KEYBOARD_PART_LOGO, + EVISION_V2_KEYBOARD_PART_EDGE, + ENDORFY_KEYBOARD_PART_EDGE, +}; + +struct EvisionV2ModeConfig +{ + uint8_t mode; + uint8_t brightness; + uint8_t speed; + uint8_t direction; + bool random_colours; + RGBColor colour; + uint8_t ledmode; +}; + +class EVisionV2KeyboardController +{ +public: + EVisionV2KeyboardController(hid_device* dev_handle, const char* path, EVisionV2KeyboardLayout dev_layout, std::string dev_name); + ~EVisionV2KeyboardController(); + + std::string GetName(); + std::string GetSerial(); + std::string GetLocation(); + + int Query(uint8_t cmd, uint16_t offset = 0, const uint8_t* idata = nullptr, uint8_t size = 0, uint8_t* odata = nullptr); + int BeginConfigure(); + int EndConfigure(); + int Read(uint8_t cmd, uint16_t offset, uint16_t size, uint8_t* odata); + int Write(uint8_t cmd, uint16_t offset, const uint8_t* idata, uint16_t size); + + int GetMode(EVisionV2KeyboardPart part, EvisionV2ModeConfig& config); + void SetMode(EVisionV2KeyboardPart part, const EvisionV2ModeConfig& config); + void SetLedsDirect(const std::vector& colours); + void SetLedDirect(int led, RGBColor colours); + void RefreshLedDirect(); + void EndLedsDirect(); + int GetLedsCustom(uint8_t colorset, std::vector& colours); + void SetLedsCustom(uint8_t colorset, const std::vector& colours); + + EVisionV2KeyboardLayout layout; + +private: + std::string name; + std::string serial; + std::string location; + hid_device* dev; + + size_t map_size; + size_t macros_size; + + uint8_t * keyvalue_map; + size_t led_count; + + std::mutex query_mutex; +}; diff --git a/Controllers/EVisionKeyboardController/RGBController_EVisionKeyboard.cpp b/Controllers/EVisionKeyboardController/RGBController_EVisionKeyboard.cpp new file mode 100644 index 0000000..3591d98 --- /dev/null +++ b/Controllers/EVisionKeyboardController/RGBController_EVisionKeyboard.cpp @@ -0,0 +1,408 @@ +/*---------------------------------------------------------*\ +| RGBController_EVisionKeyboard.cpp | +| | +| RGBController for EVision keyboard (Redragon, Glorious, | +| Ajazz, Tecware, and many other brands) | +| | +| Adam Honse (CalcProgrammer1) 25 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_EVisionKeyboard.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[6][23] = + { { 0, NA, 1, 2, 3, 4, NA, 5, 6, 7, 8, NA, 9, 10, 11, 12, 14, 15, 16, NA, NA, NA, NA }, + { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, NA, 32, 33, 34, NA, 35, 36, 37, 38, 39, 40, 41 }, + { 42, NA, 43, 44, 45, 46, NA, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62 }, + { 63, NA, 64, 65, 66, 67, NA, 68, 69, 70, 71, 72, 73, 74, 76, NA, NA, NA, NA, 80, 81, 82, NA }, + { 84, NA, 86, 87, 88, 89, NA, 90, NA, 91, 92, 93, 94, 95, 97, NA, NA, 99, NA, 101, 102, 103, 104 }, + { 105, 106, 107, NA, NA, NA, NA, 108, NA, NA, NA, NA, 109, 110, 111, 113, 119, 120, 121, 123, NA, 124, NA } }; + +/**------------------------------------------------------------------*\ + @name EVision Keyboard + @category Keyboard + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectEVisionKeyboards + @comment +\*-------------------------------------------------------------------*/ + +RGBController_EVisionKeyboard::RGBController_EVisionKeyboard(EVisionKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "EVision"; + type = DEVICE_TYPE_KEYBOARD; + description = "EVision Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Custom; + Custom.name = "Custom"; + Custom.value = EVISION_KB_MODE_CUSTOM; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Custom.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + Custom.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + Custom.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode ColorWave; + ColorWave.name = "Color Wave"; + ColorWave.value = EVISION_KB_MODE_COLOR_WAVE_LONG; + ColorWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE; + ColorWave.speed_min = EVISION_KB_SPEED_SLOWEST; + ColorWave.speed_max = EVISION_KB_SPEED_FASTEST; + ColorWave.speed = EVISION_KB_SPEED_NORMAL; + ColorWave.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + ColorWave.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + ColorWave.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + ColorWave.direction = MODE_DIRECTION_LEFT; + ColorWave.colors_min = 1; + ColorWave.colors_max = 1; + ColorWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorWave.colors.resize(1); + modes.push_back(ColorWave); + + mode ColorWaveShort; + ColorWaveShort.name = "Color Wave (Short)"; + ColorWaveShort.value = EVISION_KB_MODE_COLOR_WAVE_SHORT; + ColorWaveShort.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE; + ColorWaveShort.speed_min = EVISION_KB_SPEED_SLOWEST; + ColorWaveShort.speed_max = EVISION_KB_SPEED_FASTEST; + ColorWaveShort.speed = EVISION_KB_SPEED_NORMAL; + ColorWaveShort.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + ColorWaveShort.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + ColorWaveShort.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + ColorWaveShort.direction = MODE_DIRECTION_LEFT; + ColorWaveShort.colors_min = 1; + ColorWaveShort.colors_max = 1; + ColorWaveShort.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorWaveShort.colors.resize(1); + modes.push_back(ColorWaveShort); + + mode ColorWheel; + ColorWheel.name = "Color Wheel"; + ColorWheel.value = EVISION_KB_MODE_COLOR_WHEEL; + ColorWheel.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE; + ColorWheel.speed_min = EVISION_KB_SPEED_SLOWEST; + ColorWheel.speed_max = EVISION_KB_SPEED_FASTEST; + ColorWheel.speed = EVISION_KB_SPEED_NORMAL; + ColorWheel.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + ColorWheel.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + ColorWheel.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + ColorWheel.direction = MODE_DIRECTION_LEFT; + ColorWheel.colors_min = 1; + ColorWheel.colors_max = 1; + ColorWheel.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorWheel.colors.resize(1); + modes.push_back(ColorWheel); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = EVISION_KB_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.speed_min = EVISION_KB_SPEED_SLOWEST; + SpectrumCycle.speed_max = EVISION_KB_SPEED_FASTEST; + SpectrumCycle.speed = EVISION_KB_SPEED_NORMAL; + SpectrumCycle.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + SpectrumCycle.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + SpectrumCycle.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EVISION_KB_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = EVISION_KB_SPEED_SLOWEST; + Breathing.speed_max = EVISION_KB_SPEED_FASTEST; + Breathing.speed = EVISION_KB_SPEED_NORMAL; + Breathing.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + Breathing.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + Breathing.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Hurricane; + Hurricane.name = "Hurricane"; + Hurricane.value = EVISION_KB_MODE_HURRICANE; + Hurricane.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Hurricane.speed_min = EVISION_KB_SPEED_SLOWEST; + Hurricane.speed_max = EVISION_KB_SPEED_FASTEST; + Hurricane.speed = EVISION_KB_SPEED_NORMAL; + Hurricane.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + Hurricane.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + Hurricane.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + Hurricane.colors_min = 1; + Hurricane.colors_max = 1; + Hurricane.color_mode = MODE_COLORS_MODE_SPECIFIC; + Hurricane.colors.resize(1); + modes.push_back(Hurricane); + + mode Accumulate; + Accumulate.name = "Accumulate"; + Accumulate.value = EVISION_KB_MODE_ACCUMULATE; + Accumulate.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Accumulate.speed_min = EVISION_KB_SPEED_SLOWEST; + Accumulate.speed_max = EVISION_KB_SPEED_FASTEST; + Accumulate.speed = EVISION_KB_SPEED_NORMAL; + Accumulate.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + Accumulate.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + Accumulate.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + Accumulate.colors_min = 1; + Accumulate.colors_max = 1; + Accumulate.color_mode = MODE_COLORS_MODE_SPECIFIC; + Accumulate.colors.resize(1); + modes.push_back(Accumulate); + + mode Starlight; + Starlight.name = "Starlight"; + Starlight.value = EVISION_KB_MODE_STARLIGHT_FAST; + Starlight.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Starlight.speed_min = EVISION_KB_SPEED_SLOWEST; + Starlight.speed_max = EVISION_KB_SPEED_FASTEST; + Starlight.speed = EVISION_KB_SPEED_NORMAL; + Starlight.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + Starlight.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + Starlight.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + Starlight.colors_min = 1; + Starlight.colors_max = 1; + Starlight.color_mode = MODE_COLORS_MODE_SPECIFIC; + Starlight.colors.resize(1); + modes.push_back(Starlight); + + mode Visor; + Visor.name = "Visor"; + Visor.value = EVISION_KB_MODE_VISOR; + Visor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Visor.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + Visor.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + Visor.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + Visor.colors_min = 1; + Visor.colors_max = 1; + Visor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Visor.colors.resize(1); + modes.push_back(Visor); + + mode Static; + Static.name = "Static"; + Static.value = EVISION_KB_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + Static.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + Static.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode RainbowCircle; + RainbowCircle.name = "Rainbow Circle"; + RainbowCircle.value = EVISION_KB_MODE_RAINBOW_WAVE_CIRCLE; + RainbowCircle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + RainbowCircle.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + RainbowCircle.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + RainbowCircle.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + RainbowCircle.color_mode = MODE_COLORS_RANDOM; + modes.push_back(RainbowCircle); + + mode VerticalRainbow; + VerticalRainbow.name = "Vertical Rainbow"; + VerticalRainbow.value = EVISION_KB_MODE_RAINBOW_WAVE_VERTICAL; + VerticalRainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_AUTOMATIC_SAVE; + VerticalRainbow.speed_min = EVISION_KB_SPEED_SLOWEST; + VerticalRainbow.speed_max = EVISION_KB_SPEED_FASTEST; + VerticalRainbow.speed = EVISION_KB_SPEED_NORMAL; + VerticalRainbow.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + VerticalRainbow.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + VerticalRainbow.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + VerticalRainbow.direction = MODE_DIRECTION_UP; + VerticalRainbow.colors_min = 1; + VerticalRainbow.colors_max = 1; + VerticalRainbow.color_mode = MODE_COLORS_MODE_SPECIFIC; + VerticalRainbow.colors.resize(1); + modes.push_back(VerticalRainbow); + + mode Blooming; + Blooming.name = "Blooming"; + Blooming.value = EVISION_KB_MODE_BLOOMING; + Blooming.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Blooming.speed_min = EVISION_KB_SPEED_SLOWEST; + Blooming.speed_max = EVISION_KB_SPEED_FASTEST; + Blooming.speed = EVISION_KB_SPEED_NORMAL; + Blooming.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + Blooming.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + Blooming.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + Blooming.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Blooming); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = EVISION_KB_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Reactive.speed_min = EVISION_KB_SPEED_SLOWEST; + Reactive.speed_max = EVISION_KB_SPEED_FASTEST; + Reactive.speed = EVISION_KB_SPEED_NORMAL; + Reactive.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + Reactive.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + Reactive.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + Reactive.colors_min = 1; + Reactive.colors_max = 1; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.colors.resize(1); + modes.push_back(Reactive); + + mode ReactiveRipple; + ReactiveRipple.name = "Reactive Ripple"; + ReactiveRipple.value = EVISION_KB_MODE_REACTIVE_RIPPLE; + ReactiveRipple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ReactiveRipple.speed_min = EVISION_KB_SPEED_SLOWEST; + ReactiveRipple.speed_max = EVISION_KB_SPEED_FASTEST; + ReactiveRipple.speed = EVISION_KB_SPEED_NORMAL; + ReactiveRipple.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + ReactiveRipple.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + ReactiveRipple.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + ReactiveRipple.colors_min = 1; + ReactiveRipple.colors_max = 1; + ReactiveRipple.color_mode = MODE_COLORS_MODE_SPECIFIC; + ReactiveRipple.colors.resize(1); + modes.push_back(ReactiveRipple); + + mode ReactiveLine; + ReactiveLine.name = "Reactive Line"; + ReactiveLine.value = EVISION_KB_MODE_REACTIVE_LINE; + ReactiveLine.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ReactiveLine.speed_min = EVISION_KB_SPEED_SLOWEST; + ReactiveLine.speed_max = EVISION_KB_SPEED_FASTEST; + ReactiveLine.speed = EVISION_KB_SPEED_NORMAL; + ReactiveLine.brightness_min = EVISION_KB_BRIGHTNESS_LOWEST; + ReactiveLine.brightness_max = EVISION_KB_BRIGHTNESS_HIGHEST; + ReactiveLine.brightness = EVISION_KB_BRIGHTNESS_HIGHEST; + ReactiveLine.colors_min = 1; + ReactiveLine.colors_max = 1; + ReactiveLine.color_mode = MODE_COLORS_MODE_SPECIFIC; + ReactiveLine.colors.resize(1); + modes.push_back(ReactiveLine); + + SetupZones(); +} + +RGBController_EVisionKeyboard::~RGBController_EVisionKeyboard() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_EVisionKeyboard::SetupZones() +{ + zone new_zone; + + new_zone.name = "Keyboard"; + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = 126; + new_zone.leds_max = 126; + new_zone.leds_count = 126; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 23; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + + zones.push_back(new_zone); + + for(int led_idx = 0; led_idx < 126; led_idx++) + { + led new_led; + + new_led.name = "Keyboard LED "; + new_led.name.append(std::to_string(led_idx)); + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_EVisionKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_EVisionKeyboard::DeviceUpdateLEDs() +{ + unsigned char color_data[7*0x36]; + + for(int led_idx = 0; led_idx < 126; led_idx++) + { + color_data[(3 * led_idx) + 0] = RGBGetRValue(colors[led_idx]); + color_data[(3 * led_idx) + 1] = RGBGetGValue(colors[led_idx]); + color_data[(3 * led_idx) + 2] = RGBGetBValue(colors[led_idx]); + } + + controller->SetKeyboardColors + ( + color_data, + 0x36 * 7 + ); +} + +void RGBController_EVisionKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_EVisionKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_EVisionKeyboard::DeviceUpdateMode() +{ + unsigned char red = 0x00; + unsigned char grn = 0x00; + unsigned char blu = 0x00; + unsigned char random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + + if(modes[active_mode].colors.size() > 0) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + controller->SendKeyboardModeEx + ( + modes[active_mode].value, + modes[active_mode].brightness, + modes[active_mode].speed, + modes[active_mode].direction, + random, + red, + grn, + blu + ); +} diff --git a/Controllers/EVisionKeyboardController/RGBController_EVisionKeyboard.h b/Controllers/EVisionKeyboardController/RGBController_EVisionKeyboard.h new file mode 100644 index 0000000..ca8147f --- /dev/null +++ b/Controllers/EVisionKeyboardController/RGBController_EVisionKeyboard.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_EVisionKeyboard.h | +| | +| RGBController for EVision keyboard (Redragon, Glorious, | +| Ajazz, Tecware, and many other brands) | +| | +| Adam Honse (CalcProgrammer1) 25 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "EVisionKeyboardController.h" + +class RGBController_EVisionKeyboard : public RGBController +{ +public: + RGBController_EVisionKeyboard(EVisionKeyboardController* controller_ptr); + ~RGBController_EVisionKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + EVisionKeyboardController* controller; +}; diff --git a/Controllers/EVisionKeyboardController/RGBController_EVisionV2Keyboard.cpp b/Controllers/EVisionKeyboardController/RGBController_EVisionV2Keyboard.cpp new file mode 100644 index 0000000..e00533f --- /dev/null +++ b/Controllers/EVisionKeyboardController/RGBController_EVisionV2Keyboard.cpp @@ -0,0 +1,1041 @@ +/*---------------------------------------------------------*\ +| RGBController_EVisionV2Keyboard.cpp | +| | +| RGBController for EVision V2 keyboard | +| | +| Le Philousophe 25 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#define NA 0xFFFFFFFF + +#include +#include "hsv.h" +#include "RGBControllerKeyNames.h" +#include "RGBController_EVisionV2Keyboard.h" + +using namespace std::chrono_literals; + +static unsigned int evisionv2_matrix[EVISION_V2_MATRIX_HEIGHT][EVISION_V2_MATRIX_WIDTH] = +{ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 19 19 20 */ + { 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, NA, NA, NA, NA }, + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 }, + { 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 }, + { 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, NA, NA, NA, 72, 73, 74, NA }, + { 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, NA, 87, NA, 88, NA, 89, 90, 91, 92 }, + { 93, 94, 95, NA, NA, NA, 96, NA, NA, NA, 97, 98, 99, 100, 101, 102, 103, NA, 104, 105, NA } +}; + +static unsigned int endorfy_matrix[EVISION_V2_MATRIX_HEIGHT][EVISION_V2_MATRIX_WIDTH] = +{ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 19 19 20 */ + { 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, NA, NA, NA, NA }, + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 }, + { 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 }, + { 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, NA, 70, NA, NA, NA, 71, 72, 73, NA }, + { 74, NA, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, NA, NA, 86, NA, 87, 88, 89, 90 }, + { 91, 92, 93, NA, NA, NA, 94, NA, NA, NA, 95, 96, 97, 98, 99, 100, 101, 102, NA, 103, NA } +}; + +static const char *led_evisionv2[] = +{ + KEY_EN_ESCAPE, //00 + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, //10 + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAUSE_BREAK, + + KEY_EN_BACK_TICK, //16 + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, //20 + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_HOME, //30 + KEY_EN_PAGE_UP, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, + + KEY_EN_TAB, //37 + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, //40 + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, //50 + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_PLUS, + + KEY_EN_CAPS_LOCK, //58 + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_POUND, //70 + KEY_EN_ISO_ENTER, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + + KEY_EN_LEFT_SHIFT, //75 + KEY_EN_ISO_BACK_SLASH, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, //80 + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, //90 + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_ENTER, + + KEY_EN_LEFT_CONTROL, //93 + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_RIGHT_CONTROL, //100 + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD, +}; + +static const char *led_endorfy[] = +{ + KEY_EN_ESCAPE, //00 + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, //10 + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAUSE_BREAK, + + KEY_EN_BACK_TICK, //16 + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, //20 + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_HOME, //30 + KEY_EN_PAGE_UP, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, + + KEY_EN_TAB, //37 + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, //40 + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, //50 + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_PLUS, + + KEY_EN_CAPS_LOCK, //58 + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, //70 + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, //75 + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, //80 + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, //90 + KEY_EN_NUMPAD_ENTER, + + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, //93 + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_MENU, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, //100 + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD, +}; + +/**------------------------------------------------------------------*\ + @name EVision V2 Keyboard + @category Keyboard + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectEVisionV2Keyboard + @comment The Evision V2 controller implements all hardware modes + found in the OEM software. Some options may not be named correctly + like directions for some modes. +\*-------------------------------------------------------------------*/ + +RGBController_EVisionV2Keyboard::RGBController_EVisionV2Keyboard(EVisionV2KeyboardController* controller_ptr, EVisionV2KeyboardPart kb_part) +{ + controller = controller_ptr; + part = kb_part; + + name = controller->GetName(); + vendor = "Evision"; + type = DEVICE_TYPE_KEYBOARD; + description = "EVision Keyboard Device"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + layout = controller->layout; + + switch(part) + { + case EVISION_V2_KEYBOARD_PART_KEYBOARD: + SetupKeyboardModes(); + break; + + case EVISION_V2_KEYBOARD_PART_LOGO: + name += " Logo"; + SetupLogoEdgeModes(); + break; + + case EVISION_V2_KEYBOARD_PART_EDGE: + name += " Edge"; + SetupLogoEdgeModes(); + break; + + case ENDORFY_KEYBOARD_PART_EDGE: + SetupEdgeModes(); + break; + } + + SetupZones(); + + LoadConfig(); + + keepalive_thread_run = false; + keepalive_thread = nullptr; + if(part == EVISION_V2_KEYBOARD_PART_KEYBOARD) + { + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RGBController_EVisionV2Keyboard::KeepaliveThread, this); + } +} + +RGBController_EVisionV2Keyboard::~RGBController_EVisionV2Keyboard() +{ + if(keepalive_thread) + { + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + } + + delete controller; +} + +void RGBController_EVisionV2Keyboard::SetupKeyboardModes() +{ + mode Direct; + Direct.name = "Direct"; + Direct.value = EVISION_V2_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode ColorWave; + ColorWave.name = "Color Wave short"; + ColorWave.value = EVISION_V2_MODE_COLOR_WAVE_SHORT; + ColorWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorWave.speed_min = EVISION_V2_SPEED_SLOWEST; + ColorWave.speed_max = EVISION_V2_SPEED_FASTEST; + ColorWave.speed = EVISION_V2_SPEED_NORMAL; + ColorWave.colors_min = 1; + ColorWave.colors_max = 1; + ColorWave.color_mode = MODE_COLORS_RANDOM; + ColorWave.colors.resize(1); + ColorWave.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + ColorWave.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + ColorWave.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(ColorWave); + + mode ColorWaveLong; + ColorWaveLong.name = "Color Wave long"; + ColorWaveLong.value = EVISION_V2_MODE_COLOR_WAVE_LONG; + ColorWaveLong.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorWaveLong.speed_min = EVISION_V2_SPEED_SLOWEST; + ColorWaveLong.speed_max = EVISION_V2_SPEED_FASTEST; + ColorWaveLong.speed = EVISION_V2_SPEED_NORMAL; + ColorWaveLong.colors_min = 1; + ColorWaveLong.colors_max = 1; + ColorWaveLong.color_mode = MODE_COLORS_RANDOM; + ColorWaveLong.colors.resize(1); + ColorWaveLong.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + ColorWaveLong.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + ColorWaveLong.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(ColorWaveLong); + + mode ColorWheel; + ColorWheel.name = "Color Wheel"; + ColorWheel.value = EVISION_V2_MODE_COLOR_WHEEL; + ColorWheel.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorWheel.speed_min = EVISION_V2_SPEED_SLOWEST; + ColorWheel.speed_max = EVISION_V2_SPEED_FASTEST; + ColorWheel.speed = EVISION_V2_SPEED_NORMAL; + ColorWheel.colors_min = 1; + ColorWheel.colors_max = 1; + ColorWheel.color_mode = MODE_COLORS_RANDOM; + ColorWheel.colors.resize(1); + ColorWheel.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + ColorWheel.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + ColorWheel.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(ColorWheel); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = EVISION_V2_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.speed_min = EVISION_V2_SPEED_SLOWEST; + SpectrumCycle.speed_max = EVISION_V2_SPEED_FASTEST; + SpectrumCycle.speed = EVISION_V2_SPEED_NORMAL; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + SpectrumCycle.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + SpectrumCycle.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(SpectrumCycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EVISION_V2_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = EVISION_V2_SPEED_SLOWEST; + Breathing.speed_max = EVISION_V2_SPEED_FASTEST; + Breathing.speed = EVISION_V2_SPEED_NORMAL; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_RANDOM; + Breathing.colors.resize(1); + Breathing.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Breathing.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Breathing.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Breathing); + + mode Static; + Static.name = "Static"; + Static.value = EVISION_V2_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_RANDOM; + Static.colors.resize(1); + Static.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Static.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Static.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Static); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = EVISION_V2_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Reactive.speed_min = EVISION_V2_SPEED_SLOWEST; + Reactive.speed_max = EVISION_V2_SPEED_FASTEST; + Reactive.speed = EVISION_V2_SPEED_NORMAL; + Reactive.colors_min = 1; + Reactive.colors_max = 1; + Reactive.color_mode = MODE_COLORS_RANDOM; + Reactive.colors.resize(1); + Reactive.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Reactive.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Reactive.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Reactive); + + mode ReactiveRipple; + ReactiveRipple.name = "Reactive Ripple"; + ReactiveRipple.value = EVISION_V2_MODE_REACTIVE_RIPPLE; + ReactiveRipple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ReactiveRipple.speed_min = EVISION_V2_SPEED_SLOWEST; + ReactiveRipple.speed_max = EVISION_V2_SPEED_FASTEST; + ReactiveRipple.speed = EVISION_V2_SPEED_NORMAL; + ReactiveRipple.colors_min = 1; + ReactiveRipple.colors_max = 1; + ReactiveRipple.color_mode = MODE_COLORS_RANDOM; + ReactiveRipple.colors.resize(1); + ReactiveRipple.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + ReactiveRipple.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + ReactiveRipple.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(ReactiveRipple); + + mode ReactiveLine; + ReactiveLine.name = "Reactive Line"; + ReactiveLine.value = EVISION_V2_MODE_REACTIVE_LINE; + ReactiveLine.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ReactiveLine.speed_min = EVISION_V2_SPEED_SLOWEST; + ReactiveLine.speed_max = EVISION_V2_SPEED_FASTEST; + ReactiveLine.speed = EVISION_V2_SPEED_NORMAL; + ReactiveLine.colors_min = 1; + ReactiveLine.colors_max = 1; + ReactiveLine.color_mode = MODE_COLORS_RANDOM; + ReactiveLine.colors.resize(1); + ReactiveLine.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + ReactiveLine.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + ReactiveLine.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(ReactiveLine); + + mode Starlight; + Starlight.name = "Starlight"; + Starlight.value = EVISION_V2_MODE_STARLIGHT_FAST; + Starlight.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Starlight.speed_min = EVISION_V2_SPEED_SLOWEST; + Starlight.speed_max = EVISION_V2_SPEED_FASTEST; + Starlight.speed = EVISION_V2_SPEED_NORMAL; + Starlight.colors_min = 1; + Starlight.colors_max = 1; + Starlight.color_mode = MODE_COLORS_RANDOM; + Starlight.colors.resize(1); + Starlight.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Starlight.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Starlight.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Starlight); + + mode Blooming; + Blooming.name = "Blooming"; + Blooming.value = EVISION_V2_MODE_BLOOMING; + Blooming.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Blooming.speed_min = EVISION_V2_SPEED_SLOWEST; + Blooming.speed_max = EVISION_V2_SPEED_FASTEST; + Blooming.speed = EVISION_V2_SPEED_NORMAL; + Blooming.color_mode = MODE_COLORS_NONE; + Blooming.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Blooming.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Blooming.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Blooming); + + mode RainbowWaveVertical; + RainbowWaveVertical.name = "Rainbow Wave vertical"; + RainbowWaveVertical.value = EVISION_V2_MODE_RAINBOW_WAVE_VERTICAL; + RainbowWaveVertical.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RainbowWaveVertical.speed_min = EVISION_V2_SPEED_SLOWEST; + RainbowWaveVertical.speed_max = EVISION_V2_SPEED_FASTEST; + RainbowWaveVertical.speed = EVISION_V2_SPEED_NORMAL; + RainbowWaveVertical.color_mode = MODE_COLORS_NONE; + RainbowWaveVertical.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + RainbowWaveVertical.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + RainbowWaveVertical.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(RainbowWaveVertical); + + mode Hurricane; + Hurricane.name = "Hurricane"; + Hurricane.value = EVISION_V2_MODE_HURRICANE; + Hurricane.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Hurricane.speed_min = EVISION_V2_SPEED_SLOWEST; + Hurricane.speed_max = EVISION_V2_SPEED_FASTEST; + Hurricane.speed = EVISION_V2_SPEED_NORMAL; + Hurricane.colors_min = 1; + Hurricane.colors_max = 1; + Hurricane.color_mode = MODE_COLORS_RANDOM; + Hurricane.colors.resize(1); + Hurricane.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Hurricane.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Hurricane.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Hurricane); + + mode Accumulate; + Accumulate.name = "Accumulate"; + Accumulate.value = EVISION_V2_MODE_ACCUMULATE; + Accumulate.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Accumulate.speed_min = EVISION_V2_SPEED_SLOWEST; + Accumulate.speed_max = EVISION_V2_SPEED_FASTEST; + Accumulate.speed = EVISION_V2_SPEED_NORMAL; + Accumulate.colors_min = 1; + Accumulate.colors_max = 1; + Accumulate.color_mode = MODE_COLORS_RANDOM; + Accumulate.colors.resize(1); + Accumulate.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Accumulate.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Accumulate.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Accumulate); + + mode StarlightSlow; + StarlightSlow.name = "Starlight slow"; + StarlightSlow.value = EVISION_V2_MODE_STARLIGHT_SLOW; + StarlightSlow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + StarlightSlow.speed_min = EVISION_V2_SPEED_SLOWEST; + StarlightSlow.speed_max = EVISION_V2_SPEED_FASTEST; + StarlightSlow.speed = EVISION_V2_SPEED_NORMAL; + StarlightSlow.colors_min = 1; + StarlightSlow.colors_max = 1; + StarlightSlow.color_mode = MODE_COLORS_RANDOM; + StarlightSlow.colors.resize(1); + StarlightSlow.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + StarlightSlow.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + StarlightSlow.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(StarlightSlow); + + mode Visor; + Visor.name = "Visor"; + Visor.value = EVISION_V2_MODE_VISOR; + Visor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Visor.speed_min = EVISION_V2_SPEED_SLOWEST; + Visor.speed_max = EVISION_V2_SPEED_FASTEST; + Visor.speed = EVISION_V2_SPEED_NORMAL; + Visor.colors_min = 1; + Visor.colors_max = 1; + Visor.color_mode = MODE_COLORS_RANDOM; + Visor.colors.resize(1); + Visor.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Visor.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Visor.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Visor); + + mode Surmount; + Surmount.name = "Surmount"; + Surmount.value = EVISION_V2_MODE_SURMOUNT; + Surmount.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Surmount.colors_min = 1; + Surmount.colors_max = 1; + Surmount.color_mode = MODE_COLORS_MODE_SPECIFIC; + Surmount.colors.resize(1); + Surmount.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Surmount.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Surmount.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Surmount); + + mode RainbowCircle; + RainbowCircle.name = "Rainbow Circle"; + RainbowCircle.value = EVISION_V2_MODE_RAINBOW_WAVE_CIRCLE; + RainbowCircle.flags = MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RainbowCircle.color_mode = MODE_COLORS_NONE; + RainbowCircle.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + RainbowCircle.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + RainbowCircle.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(RainbowCircle); + + for(unsigned int i = 0; i < 10; i++) + { + mode Custom; + Custom.name = "Custom "; + Custom.name += std::to_string(i+1); + Custom.value = EVISION_V2_MODE_CUSTOM | i << 8; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Custom.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Custom.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Custom); + } +} + +void RGBController_EVisionV2Keyboard::SetupLogoEdgeModes() +{ + mode ColorWave; + ColorWave.name = "Color Wave"; + ColorWave.value = EVISION_V2_MODE2_COLOR_WAVE; + ColorWave.flags = MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorWave.speed_min = EVISION_V2_SPEED_SLOWEST; + ColorWave.speed_max = EVISION_V2_SPEED_FASTEST; + ColorWave.speed = EVISION_V2_SPEED_NORMAL; + ColorWave.colors_min = 1; + ColorWave.colors_max = 1; + ColorWave.color_mode = MODE_COLORS_RANDOM; + ColorWave.colors.resize(1); + ColorWave.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + ColorWave.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + ColorWave.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(ColorWave); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EVISION_V2_MODE2_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = EVISION_V2_SPEED_SLOWEST; + Breathing.speed_max = EVISION_V2_SPEED_FASTEST; + Breathing.speed = EVISION_V2_SPEED_NORMAL; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_RANDOM; + Breathing.colors.resize(1); + Breathing.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Breathing.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Breathing.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Breathing); + + mode Yoyo; + Yoyo.name = "Yoyo"; + Yoyo.value = EVISION_V2_MODE2_YOYO; + Yoyo.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Yoyo.speed_min = EVISION_V2_SPEED_SLOWEST; + Yoyo.speed_max = EVISION_V2_SPEED_FASTEST; + Yoyo.speed = EVISION_V2_SPEED_NORMAL; + Yoyo.colors_min = 1; + Yoyo.colors_max = 1; + Yoyo.color_mode = MODE_COLORS_RANDOM; + Yoyo.colors.resize(1); + Yoyo.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Yoyo.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Yoyo.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Yoyo); + + mode Blink; + Blink.name = "Blink"; + Blink.value = EVISION_V2_MODE2_BLINK; + Blink.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Blink.speed_min = EVISION_V2_SPEED_SLOWEST; + Blink.speed_max = EVISION_V2_SPEED_FASTEST; + Blink.speed = EVISION_V2_SPEED_NORMAL; + Blink.colors_min = 1; + Blink.colors_max = 1; + Blink.color_mode = MODE_COLORS_RANDOM; + Blink.colors.resize(1); + Blink.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Blink.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Blink.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Blink); + + mode Static; + Static.name = "Static"; + Static.value = EVISION_V2_MODE2_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_RANDOM; + Static.colors.resize(1); + Static.brightness_min = EVISION_V2_BRIGHTNESS_LOWEST; + Static.brightness_max = EVISION_V2_BRIGHTNESS_HIGHEST; + Static.brightness = EVISION_V2_BRIGHTNESS_HIGHEST; + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = EVISION_V2_MODE2_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + modes.push_back(Off); +} + +void RGBController_EVisionV2Keyboard::SetupEdgeModes() +{ + mode Freeze; + Freeze.name = "Freeze"; + Freeze.value = ENDORFY_MODE2_FREEZE; + Freeze.flags = MODE_FLAG_AUTOMATIC_SAVE; + modes.push_back(Freeze); + + mode ColorWave; + ColorWave.name = "Color Wave"; + ColorWave.value = ENDORFY_MODE2_COLOR_WAVE; + ColorWave.flags = MODE_FLAG_AUTOMATIC_SAVE; + modes.push_back(ColorWave); + + mode Spectrum; + Spectrum.name = "Spectrum Cycle"; + Spectrum.value = ENDORFY_MODE2_SPECTRUM_CYCLE; + Spectrum.flags = MODE_FLAG_AUTOMATIC_SAVE; + modes.push_back(Spectrum); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ENDORFY_MODE2_BREATHING; + Breathing.flags = MODE_FLAG_AUTOMATIC_SAVE; + modes.push_back(Breathing); + + mode Static; + Static.name = "Static"; + Static.value = ENDORFY_MODE2_STATIC; + Static.flags = MODE_FLAG_AUTOMATIC_SAVE; + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = ENDORFY_MODE2_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + modes.push_back(Off); +} + +void RGBController_EVisionV2Keyboard::SetupZones() +{ + unsigned short leds_count; + unsigned int *matrix_map; + const char **led_names; + + switch(layout) + { + default: + case EVISION_V2_KEYBOARD_LAYOUT: + led_names = led_evisionv2; + matrix_map = (unsigned int *)evisionv2_matrix; + leds_count = 106; + break; + case ENDORFY_KEYBOARD_LAYOUT: + led_names = led_endorfy; + matrix_map = (unsigned int *)endorfy_matrix; + leds_count = 104; + break; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + zone KB_zone; + KB_zone.name = ZONE_EN_KEYBOARD; + KB_zone.type = ZONE_TYPE_MATRIX; + KB_zone.leds_count = leds_count; + KB_zone.leds_min = KB_zone.leds_count; + KB_zone.leds_max = KB_zone.leds_count; + + KB_zone.matrix_map = new matrix_map_type; + KB_zone.matrix_map->height = EVISION_V2_MATRIX_HEIGHT; + KB_zone.matrix_map->width = EVISION_V2_MATRIX_WIDTH; + KB_zone.matrix_map->map = matrix_map; + zones.push_back(KB_zone); + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_index = 0; zone_index < zones.size(); zone_index++) + { + for(unsigned int led_index = 0; led_index < zones[zone_index].leds_count; led_index++) + { + led new_led; + new_led.name = led_names[led_index]; + new_led.value = led_index; + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_EVisionV2Keyboard::LoadConfig() +{ + EvisionV2ModeConfig config; + + controller->GetMode(part, config); + + int mode = config.mode; + if((part == EVISION_V2_KEYBOARD_PART_KEYBOARD) && (mode == EVISION_V2_MODE_CUSTOM)) + { + mode |= config.ledmode << 8; + } + config.direction = (config.direction == 0) ? 0 : 1; + + // Define default colors + for(int mode_index = 0; mode_index < (int)modes.size(); mode_index++) + { + if(config.random_colours) + { + if(modes[mode_index].flags & MODE_FLAG_HAS_RANDOM_COLOR) + { + modes[mode_index].color_mode = MODE_COLORS_RANDOM; + } + if(modes[mode_index].colors.size() > 0) + { + modes[mode_index].colors[0] = 0xffffff; + } + } + else + { + if(modes[mode_index].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + modes[mode_index].color_mode = MODE_COLORS_MODE_SPECIFIC; + } + if(modes[mode_index].colors.size() > 0) + { + modes[mode_index].colors[0] = config.colour; + } + } + if(modes[mode_index].value == mode) + { + modes[mode_index].brightness = config.brightness; + modes[mode_index].speed = config.speed; + if(modes[mode_index].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + modes[mode_index].direction = (1 - config.direction) + MODE_DIRECTION_LEFT; + } + else if(modes[mode_index].flags & MODE_FLAG_HAS_DIRECTION_UD) + { + modes[mode_index].direction = (1 - config.direction) + MODE_DIRECTION_UP; + } + else if(modes[mode_index].flags & MODE_FLAG_HAS_DIRECTION_HV) + { + modes[mode_index].direction = (1 - config.direction) + MODE_DIRECTION_HORIZONTAL; + } + + if(part == EVISION_V2_KEYBOARD_PART_KEYBOARD) + { + if(mode == EVISION_V2_MODE_SURMOUNT) + { + switch(config.ledmode) + { + case EVISION_V2_SURMOUNT_MODE_COLOR_RED: + modes[mode_index].colors[0] = ToRGBColor(0xff, 0, 0); + break; + case EVISION_V2_SURMOUNT_MODE_COLOR_YELLOW: + modes[mode_index].colors[0] = ToRGBColor(0xff, 0xff, 0); + break; + case EVISION_V2_SURMOUNT_MODE_COLOR_GREEN: + modes[mode_index].colors[0] = ToRGBColor(0, 0xff, 0); + break; + case EVISION_V2_SURMOUNT_MODE_COLOR_CYAN: + modes[mode_index].colors[0] = ToRGBColor(0, 0xff, 0xff); + break; + default: + break; + } + } + else if(config.mode == EVISION_V2_MODE_CUSTOM) + { + controller->GetLedsCustom(config.ledmode, colors); + } + } + + active_mode = mode_index; + } + } +} + +void RGBController_EVisionV2Keyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_EVisionV2Keyboard::DeviceUpdateLEDs() +{ + if(part != EVISION_V2_KEYBOARD_PART_KEYBOARD) + { + return; + } + + controller->SetLedsDirect(colors); + has_color_set = true; + last_update_time = std::chrono::steady_clock::now(); +} + +void RGBController_EVisionV2Keyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_EVisionV2Keyboard::UpdateSingleLED(int led) +{ + if(part != EVISION_V2_KEYBOARD_PART_KEYBOARD) + { + return; + } + + controller->SetLedDirect(led, colors[led]); + has_color_set = true; + last_update_time = std::chrono::steady_clock::now(); +} + +void RGBController_EVisionV2Keyboard::DeviceUpdateMode() +{ + mode set_mode = modes[active_mode]; + + // No mode set packets required for Direct mode + if((part == EVISION_V2_KEYBOARD_PART_KEYBOARD) && (set_mode.value == EVISION_V2_MODE_DIRECT)) + { + return; + } + + EvisionV2ModeConfig config; + + config.mode = set_mode.value & 0xff; + config.brightness = set_mode.brightness; + config.speed = set_mode.speed; + config.direction = 1 - (set_mode.direction & 0x1); + config.random_colours = (set_mode.color_mode == MODE_COLORS_RANDOM); + config.colour = 0; + if(modes[active_mode].colors.size() > 0) + { + config.colour = set_mode.colors[0]; + } + + config.ledmode = 0; + if(part == EVISION_V2_KEYBOARD_PART_KEYBOARD) + { + if(config.mode == EVISION_V2_MODE_SURMOUNT) + { + hsv_t temp; + rgb2hsv(config.colour, &temp); + + if(temp.hue <= 30 || temp.hue > 300) + { + config.ledmode = EVISION_V2_SURMOUNT_MODE_COLOR_RED; + } + else if(temp.hue > 30 && temp.hue <= 90) + { + config.ledmode = EVISION_V2_SURMOUNT_MODE_COLOR_YELLOW; + } + else if(temp.hue > 90 && temp.hue <= 150) + { + config.ledmode = EVISION_V2_SURMOUNT_MODE_COLOR_GREEN; + } + else if(temp.hue > 150 && temp.hue <= 300) + { + config.ledmode = EVISION_V2_SURMOUNT_MODE_COLOR_CYAN; + } + } + else if(config.mode == EVISION_V2_MODE_CUSTOM) + { + config.ledmode = (set_mode.value >> 8) & 0xff; + } + } + + controller->SetMode(part, config); + + if((part == EVISION_V2_KEYBOARD_PART_KEYBOARD) && (config.mode == EVISION_V2_MODE_CUSTOM)) + { + controller->GetLedsCustom(config.ledmode, colors); + SignalUpdate(); + } +} + +void RGBController_EVisionV2Keyboard::DeviceSaveMode() +{ + if(part != EVISION_V2_KEYBOARD_PART_KEYBOARD) + { + return; + } + int value = modes[active_mode].value; + if((value & 0xff) == EVISION_V2_MODE_CUSTOM) + { + controller->SetLedsCustom((value >> 8) & 0xff, colors); + } +} + +void RGBController_EVisionV2Keyboard::KeepaliveThread() +{ + bool was_active = false; + while(keepalive_thread_run.load()) + { + if(modes[active_mode].value == EVISION_V2_MODE_DIRECT && has_color_set) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(200)) + { + controller->RefreshLedDirect(); + last_update_time = std::chrono::steady_clock::now(); + was_active = true; + } + } + else if(was_active) + { + controller->EndLedsDirect(); + was_active = false; + } + std::this_thread::sleep_for(100ms); + } + controller->EndLedsDirect(); +} diff --git a/Controllers/EVisionKeyboardController/RGBController_EVisionV2Keyboard.h b/Controllers/EVisionKeyboardController/RGBController_EVisionV2Keyboard.h new file mode 100644 index 0000000..b350b15 --- /dev/null +++ b/Controllers/EVisionKeyboardController/RGBController_EVisionV2Keyboard.h @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| RGBController_EVisionV2Keyboard.h | +| | +| RGBController for EVision V2 keyboard | +| | +| Le Philousophe 25 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "LogManager.h" +#include "RGBController.h" +#include "EVisionV2KeyboardController.h" + +class RGBController_EVisionV2Keyboard : public RGBController +{ +public: + RGBController_EVisionV2Keyboard(EVisionV2KeyboardController* controller_ptr, EVisionV2KeyboardPart kb_part); + ~RGBController_EVisionV2Keyboard(); + + void SetupZones() override; + void ResizeZone(int zone, int new_size) override; + + void DeviceUpdateLEDs() override; + void UpdateZoneLEDs(int zone) override; + void UpdateSingleLED(int led) override; + + void DeviceUpdateMode() override; + void DeviceSaveMode() override; + +private: + void SetupKeyboardModes(); + void SetupLogoEdgeModes(); + void SetupEdgeModes(); + void LoadConfig(); + + void KeepaliveThread(); + + EVisionV2KeyboardController* controller; + EVisionV2KeyboardPart part; + EVisionV2KeyboardLayout layout; + + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::atomic has_color_set; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/ElgatoKeyLightController/ElgatoKeyLightController.cpp b/Controllers/ElgatoKeyLightController/ElgatoKeyLightController.cpp new file mode 100644 index 0000000..5bfc1d5 --- /dev/null +++ b/Controllers/ElgatoKeyLightController/ElgatoKeyLightController.cpp @@ -0,0 +1,115 @@ +/*---------------------------------------------------------*\ +| ElgatoKeyLightController.cpp | +| | +| Driver for Elgato Key Light | +| | +| Monks (imtherealestmonkey@gmail.com), 03 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "ElgatoKeyLightController.h" +#include + +using json = nlohmann::json; + +ElgatoKeyLightController::ElgatoKeyLightController(std::string ip) +{ + /*-----------------------------------------------------------------*\ + | Fill in location string with device's IP address | + \*-----------------------------------------------------------------*/ + location = "IP: " + ip; + + /*-----------------------------------------------------------------*\ + | Open a TCP client sending to the device's IP, port 9123 | + \*-----------------------------------------------------------------*/ + port.tcp_client(ip.c_str(), "9123"); +} + +ElgatoKeyLightController::~ElgatoKeyLightController() +{ +} + +std::string ElgatoKeyLightController::GetLocation() +{ + return(location); +} + +std::string ElgatoKeyLightController::GetName() +{ + return("Elgato KeyLight"); +} + +std::string ElgatoKeyLightController::GetVersion() +{ + return(""); +} + +std::string ElgatoKeyLightController::GetManufacturer() +{ + return("Elgato"); +} + +std::string ElgatoKeyLightController::GetUniqueID() +{ + return(""); +} + +void ElgatoKeyLightController::SetColor(hsv_t hsv_color) +{ + // Weird elgato color format + int k_value = HSVToK(hsv_color.hue); + + port.tcp_client_connect(); + std::string buf = GetRequest(hsv_color.value, k_value); + port.tcp_client_write((char *)buf.c_str(), (int)buf.length() + 1); + + port.tcp_close(); +} + +std::string ElgatoKeyLightController::GetRequest(int brightness, int temperature) +{ + json command; + + command["numberOfLights"] = 1; + + auto lights = json::array(); + lights.push_back(json::object({ {"on", 1}, {"temperature", temperature}, {"brightness", brightness}})); + command["lights"] = lights; + + std::string command_str = command.dump(); + std::string buf = "PUT /elgato/lights HTTP/1.1\r\nContent-Type: application/json\r\nContent-Length: " + + std::to_string(command_str.length()) + + "\r\nConnection: close\r\n\r\n" + command_str + "\r\n\r\n"; + return(buf); +} + +int ElgatoKeyLightController::HSVToK(int hue) +{ + int k_value; + + if(hue <= 60 && hue >= 0) + { + k_value = 2900; + } + else if(hue >= 61 && hue <= 120) + { + k_value = 4000; + } + else if(hue >= 121 && hue <= 180) + { + k_value = 5000; + } + else if(hue >= 181 && hue <= 240) + { + k_value = 6000; + } + else + { + k_value = 7000; + } + + return k_value; +} diff --git a/Controllers/ElgatoKeyLightController/ElgatoKeyLightController.h b/Controllers/ElgatoKeyLightController/ElgatoKeyLightController.h new file mode 100644 index 0000000..1cae0b8 --- /dev/null +++ b/Controllers/ElgatoKeyLightController/ElgatoKeyLightController.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| ElgatoKeyLightController.h | +| | +| Driver for Elgato Key Light | +| | +| Monks (imtherealestmonkey@gmail.com), 11 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBController.h" +#include "net_port.h" +#include "hsv.h" + +class ElgatoKeyLightController +{ +public: + ElgatoKeyLightController(std::string ip); + ~ElgatoKeyLightController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetVersion(); + std::string GetManufacturer(); + std::string GetUniqueID(); + + void SetColor(hsv_t hsv_color); + +private: + std::string GetRequest(int brightness, int temperature); + int HSVToK(int hue); + std::string location; + net_port port; +}; diff --git a/Controllers/ElgatoKeyLightController/ElgatoKeyLightControllerDetect.cpp b/Controllers/ElgatoKeyLightController/ElgatoKeyLightControllerDetect.cpp new file mode 100644 index 0000000..8d38366 --- /dev/null +++ b/Controllers/ElgatoKeyLightController/ElgatoKeyLightControllerDetect.cpp @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| ElgatoKeyLightControllerDetect.cpp | +| | +| Detector for Elgato Key Light | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ElgatoKeyLightController.h" +#include "RGBController_ElgatoKeyLight.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectElgatoKeyLightControllers * +* * +* Detect Elgato KeyLight devices * +* * +\******************************************************************************************/ + +void DetectElgatoKeyLightControllers() +{ + json elgato_keylight_settings; + + /*-------------------------------------------------*\ + | Get KeyLight settings from settings manager | + \*-------------------------------------------------*/ + elgato_keylight_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("ElgatoKeyLightDevices"); + + /*----------------------------------------------------------*\ + | If the Elgato Key Light settings contains devices, process| + \*----------------------------------------------------------*/ + if(elgato_keylight_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < elgato_keylight_settings["devices"].size(); device_idx++) + { + if(elgato_keylight_settings["devices"][device_idx].contains("ip")) + { + std::string elgato_keylight_ip = elgato_keylight_settings["devices"][device_idx]["ip"]; + + ElgatoKeyLightController* controller = new ElgatoKeyLightController(elgato_keylight_ip); + RGBController_ElgatoKeyLight* rgb_controller = new RGBController_ElgatoKeyLight(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } + +} /* DetectElgatoKeyLightControllers() */ + +REGISTER_DETECTOR("ElgatoKeyLight", DetectElgatoKeyLightControllers); diff --git a/Controllers/ElgatoKeyLightController/RGBController_ElgatoKeyLight.cpp b/Controllers/ElgatoKeyLightController/RGBController_ElgatoKeyLight.cpp new file mode 100644 index 0000000..720c435 --- /dev/null +++ b/Controllers/ElgatoKeyLightController/RGBController_ElgatoKeyLight.cpp @@ -0,0 +1,89 @@ +/*---------------------------------------------------------*\ +| RGBController_ElgatoKeyLight.cpp | +| | +| RGBController for Elgato Key Light | +| | +| Monks (@iamtherealestmonkey) 03 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ElgatoKeyLight.h" +#include "hsv.h" + +RGBController_ElgatoKeyLight::RGBController_ElgatoKeyLight(ElgatoKeyLightController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = controller->GetManufacturer(); + type = DEVICE_TYPE_LIGHT; + version = controller->GetVersion(); + description = "Elgato KeyLight Device"; + serial = controller->GetUniqueID(); + location = controller->GetLocation(); + + mode Static; + Static.name = "Static"; + Static.value = 0; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + SetupZones(); +} + +RGBController_ElgatoKeyLight::~RGBController_ElgatoKeyLight() +{ + delete controller; +} + +void RGBController_ElgatoKeyLight::SetupZones() +{ + zone led_zone; + led_zone.name = "Keylight"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + led new_led; + new_led.name = "Keylight"; + + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_ElgatoKeyLight::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ElgatoKeyLight::DeviceUpdateLEDs() +{ + RGBColor rgb_color = colors[0]; + hsv_t hsv_color; + rgb2hsv(rgb_color, &hsv_color); + controller->SetColor(hsv_color); +} + +void RGBController_ElgatoKeyLight::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ElgatoKeyLight::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ElgatoKeyLight::DeviceUpdateMode() +{ + +} diff --git a/Controllers/ElgatoKeyLightController/RGBController_ElgatoKeyLight.h b/Controllers/ElgatoKeyLightController/RGBController_ElgatoKeyLight.h new file mode 100644 index 0000000..eac4570 --- /dev/null +++ b/Controllers/ElgatoKeyLightController/RGBController_ElgatoKeyLight.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_ElgatoKeyLight.h | +| | +| RGBController for Elgato Key Light | +| | +| Monks (@iamtherealestmonkey) 01 Nov 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ElgatoKeyLightController.h" + +class RGBController_ElgatoKeyLight : public RGBController +{ +public: + RGBController_ElgatoKeyLight(ElgatoKeyLightController* controller_ptr); + ~RGBController_ElgatoKeyLight(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ElgatoKeyLightController* controller; +}; diff --git a/Controllers/ElgatoLightStripController/ElgatoLightStripController.cpp b/Controllers/ElgatoLightStripController/ElgatoLightStripController.cpp new file mode 100644 index 0000000..28022fb --- /dev/null +++ b/Controllers/ElgatoLightStripController/ElgatoLightStripController.cpp @@ -0,0 +1,159 @@ +/*---------------------------------------------------------*\ +| ElgatoLightStripController.cpp | +| | +| Driver for Elgato Light Strip | +| | +| Monks (@iamtherealestmonkey) 03 Nov 2021 | +| DomePlaysHD 14 Mar 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "ElgatoLightStripController.h" +#include +#include "LogManager.h" + +using json = nlohmann::json; +using namespace std::chrono_literals; + +ElgatoLightStripController::ElgatoLightStripController(std::string ip) +{ + /*-----------------------------------------------------------------*\ + | Fill in location string with device's IP address | + \*-----------------------------------------------------------------*/ + location = "IP: " + ip; + + /*-----------------------------------------------------------------*\ + | Open a TCP client sending to the device's IP, port 9123 | + \*-----------------------------------------------------------------*/ + port.tcp_client(ip.c_str(), "9123"); + + /*-----------------------------------------------------------*\ + | Handle responses received from the Elgato LightStrip device | + \*-----------------------------------------------------------*/ + port.tcp_client_connect(); + std::string buf = "GET /elgato/accessory-info HTTP/1.1\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n"; + port.tcp_client_write((char *)buf.c_str(), (int)buf.length() + 1); + + char recv_buf[1024]; + int size = port.tcp_listen(recv_buf, sizeof(recv_buf)); + port.tcp_close(); + + if(size > 0) + { + /*-----------------------------------------------------------*\ + | Get response body | + \*-----------------------------------------------------------*/ + std::istringstream recv_stream(recv_buf); + std::vector recv_list; + std::string current_line; + + while(std::getline(recv_stream, current_line, '\n')) + { + recv_list.push_back(current_line); + } + + std::string result = recv_list[5]; + json elgato_lightstrip_data = json::parse(result); + + firmware_version = elgato_lightstrip_data["firmwareVersion"]; + serialnumber = elgato_lightstrip_data["serialNumber"]; + displayname = elgato_lightstrip_data["displayName"]; + + LOG_DEBUG("[ElgatoLightStrip] [%s]", result.data()); + } +} + +ElgatoLightStripController::~ElgatoLightStripController() +{ +} + +std::string ElgatoLightStripController::GetLocation() +{ + return(location); +} + +std::string ElgatoLightStripController::GetName() +{ + return(displayname); +} + +std::string ElgatoLightStripController::GetVersion() +{ + return(firmware_version); +} + +std::string ElgatoLightStripController::GetManufacturer() +{ + return("Elgato"); +} + +std::string ElgatoLightStripController::GetUniqueID() +{ + return(serialnumber); +} + +void ElgatoLightStripController::SetColor(hsv_t hsv_color) +{ + if(hsv_color.hue > 360) + { + hsv_color.hue = 360; + } + + if(hsv_color.saturation > 100) + { + hsv_color.saturation = 100; + } + + /*-------------------------------------------------*\ + | Delay to prevent it from getting stuck on effects | + \*-------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + + port.tcp_client_connect(); + std::string buf = GetRequest(hsv_color.hue, hsv_color.saturation, GetBrightness()); + port.tcp_client_write((char *)buf.c_str(), (int)buf.length() + 1); + port.tcp_close(); +} + +std::string ElgatoLightStripController::GetRequest(int hue, int saturation, int brightness) +{ + json command; + + command["numberOfLights"] = 1; + + json lights = json::array(); + lights.push_back(json::object({ {"on", 1}, {"hue", hue}, {"saturation", saturation}, {"brightness", brightness}})); + command["lights"] = lights; + + std::string command_str = command.dump(); + std::string buf = "PUT /elgato/lights HTTP/1.1\r\nContent-Type: application/json\r\nContent-Length: " + + std::to_string(command_str.length()) + + "\r\nConnection: close\r\n\r\n" + command_str + "\r\n\r\n"; + + return(buf); +} + +int ElgatoLightStripController::GetBrightness() +{ + if(device_brightness > 100 || device_brightness < 0) + { + device_brightness = 100; + } + + return device_brightness; +} + +void ElgatoLightStripController::SetBrightness(int brightness) +{ + if(brightness > 100 || device_brightness < 0) + { + brightness = 100; + } + + device_brightness = brightness; +} diff --git a/Controllers/ElgatoLightStripController/ElgatoLightStripController.h b/Controllers/ElgatoLightStripController/ElgatoLightStripController.h new file mode 100644 index 0000000..654c41d --- /dev/null +++ b/Controllers/ElgatoLightStripController/ElgatoLightStripController.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| ElgatoLightStripController.h | +| | +| Driver for Elgato Light Strip | +| | +| Monks (@iamtherealestmonkey) 03 Nov 2021 | +| DomePlaysHD 12 Mar 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "net_port.h" +#include "hsv.h" + +class ElgatoLightStripController +{ + public: + ElgatoLightStripController(std::string ip); + ~ElgatoLightStripController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetVersion(); + std::string GetManufacturer(); + std::string GetUniqueID(); + + void SetColor(hsv_t hsv_color); + int GetBrightness(); + void SetBrightness(int brightness); + + private: + std::string GetRequest(int hue, int saturation, int brightness); + std::string location; + std::string firmware_version; + std::string serialnumber; + std::string displayname; + net_port port; + int device_brightness; +}; diff --git a/Controllers/ElgatoLightStripController/ElgatoLightStripControllerDetect.cpp b/Controllers/ElgatoLightStripController/ElgatoLightStripControllerDetect.cpp new file mode 100644 index 0000000..8aa8eca --- /dev/null +++ b/Controllers/ElgatoLightStripController/ElgatoLightStripControllerDetect.cpp @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| ElgatoLightStripControllerDetect.cpp | +| | +| Detector for Elgato Light Strip | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ElgatoLightStripController.h" +#include "RGBController_ElgatoLightStrip.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* Detect Elgato LightStrip devices * +* * +\******************************************************************************************/ + +void DetectElgatoLightStripControllers() +{ + json elgato_lightstrip_settings; + + /*-------------------------------------------------*\ + | Get LightStrip settings from settings manager | + \*-------------------------------------------------*/ + elgato_lightstrip_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("ElgatoLightStripDevices"); + + /*------------------------------------------------------------*\ + | If the Elgato Light Strip settings contains devices, process | + \*------------------------------------------------------------*/ + if(elgato_lightstrip_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < elgato_lightstrip_settings["devices"].size(); device_idx++) + { + if(elgato_lightstrip_settings["devices"][device_idx].contains("ip")) + { + std::string elgato_lightstrip_ip = elgato_lightstrip_settings["devices"][device_idx]["ip"]; + + ElgatoLightStripController* controller = new ElgatoLightStripController(elgato_lightstrip_ip); + RGBController_ElgatoLightStrip* rgb_controller = new RGBController_ElgatoLightStrip(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } +} + +REGISTER_DETECTOR("Elgato Light Strip", DetectElgatoLightStripControllers); diff --git a/Controllers/ElgatoLightStripController/RGBController_ElgatoLightStrip.cpp b/Controllers/ElgatoLightStripController/RGBController_ElgatoLightStrip.cpp new file mode 100644 index 0000000..d33aa30 --- /dev/null +++ b/Controllers/ElgatoLightStripController/RGBController_ElgatoLightStrip.cpp @@ -0,0 +1,93 @@ +/*---------------------------------------------------------*\ +| RGBController_ElgatoLightStrip.cpp | +| | +| RGBController for Elgato Light Strip | +| | +| Monks (@iamtherealestmonkey) 03 Nov 2021 | +| DomePlaysHD 12 Mar 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ElgatoLightStrip.h" +#include "hsv.h" + +RGBController_ElgatoLightStrip::RGBController_ElgatoLightStrip(ElgatoLightStripController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = controller->GetManufacturer(); + type = DEVICE_TYPE_LEDSTRIP; + version = controller->GetVersion(); + description = "Elgato LightStrip Device"; + serial = controller->GetUniqueID(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.brightness = 100; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_ElgatoLightStrip::~RGBController_ElgatoLightStrip() +{ + delete controller; +} + +void RGBController_ElgatoLightStrip::SetupZones() +{ + zone led_zone; + led_zone.name = "Lightstrip"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + led new_led; + new_led.name = "Lightstrip"; + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_ElgatoLightStrip::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ElgatoLightStrip::DeviceUpdateLEDs() +{ + RGBColor rgb_color = colors[0]; + hsv_t hsv_color; + rgb2hsv(rgb_color, &hsv_color); + controller->SetColor(hsv_color); + controller->SetBrightness((unsigned char)modes[(unsigned int)active_mode].brightness); +} + +void RGBController_ElgatoLightStrip::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ElgatoLightStrip::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ElgatoLightStrip::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/ElgatoLightStripController/RGBController_ElgatoLightStrip.h b/Controllers/ElgatoLightStripController/RGBController_ElgatoLightStrip.h new file mode 100644 index 0000000..f6cef9b --- /dev/null +++ b/Controllers/ElgatoLightStripController/RGBController_ElgatoLightStrip.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_ElgatoLightStrip.h | +| | +| RGBController for Elgato Light Strip | +| | +| Monks (@iamtherealestmonkey) 01 Nov 2021 | +| DomePlaysHD 12 Mar 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ElgatoLightStripController.h" + +class RGBController_ElgatoLightStrip : public RGBController +{ + public: + RGBController_ElgatoLightStrip(ElgatoLightStripController* controller_ptr); + ~RGBController_ElgatoLightStrip(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + private: + ElgatoLightStripController* controller; +}; diff --git a/Controllers/EpomakerController/EpomakerController.cpp b/Controllers/EpomakerController/EpomakerController.cpp new file mode 100644 index 0000000..b436a52 --- /dev/null +++ b/Controllers/EpomakerController/EpomakerController.cpp @@ -0,0 +1,133 @@ +/*---------------------------------------------------------*\ +| EpomakerController.cpp | +| | +| Driver for Epomaker keyboard | +| | +| Alvaro Munoz (alvaromunoz) 05 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "EpomakerController.h" +#include "LogManager.h" +#include "StringUtils.h" + +EpomakerController::EpomakerController(hid_device* dev_handle, char *_path) +{ + dev = dev_handle; + location = _path; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + + current_mode = EPOMAKER_MODE_ALWAYS_ON; + current_speed = EPOMAKER_SPEED_DEFAULT; + current_brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + current_dazzle = EPOMAKER_OPTION_DAZZLE_OFF; + current_option = EPOMAKER_OPTION_DEFAULT; +} + +EpomakerController::~EpomakerController() +{ + hid_close(dev); +} + +std::string EpomakerController::GetDeviceName() +{ + return(device_name); +} + +std::string EpomakerController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string EpomakerController::GetLocation() +{ + return("HID: " + location); +} + +void EpomakerController::SetMode(unsigned char mode, unsigned char speed, unsigned char brightness) +{ + current_mode = mode; + current_speed = speed; + current_brightness = brightness; + + SendUpdate(); +} + +void EpomakerController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + current_red = red; + current_green = green; + current_blue = blue; + + SendUpdate(); +} + +void EpomakerController::SetDazzle(bool is_dazzle) +{ + if(is_dazzle) + { + current_dazzle = EPOMAKER_OPTION_DAZZLE_ON; + } + else + { + current_dazzle = EPOMAKER_OPTION_DAZZLE_OFF; + } +} + +void EpomakerController::SetOption(unsigned char option) +{ + current_option = option; +} + +void EpomakerController::SendUpdate() +{ + unsigned char buffer[EPOMAKER_PACKET_LENGTH + 1] = { 0x00 }; + + buffer[EPOMAKER_BYTE_COMMAND] = EPOMAKER_COMMAND_RGB; + buffer[EPOMAKER_BYTE_MODE] = current_mode; + buffer[EPOMAKER_BYTE_SPEED] = current_speed; + buffer[EPOMAKER_BYTE_BRIGHTNESS] = current_brightness; + buffer[EPOMAKER_BYTE_FLAGS] = current_option | current_dazzle; + buffer[EPOMAKER_BYTE_RED] = current_red; + buffer[EPOMAKER_BYTE_GREEN] = current_green; + buffer[EPOMAKER_BYTE_BLUE] = current_blue; + + int sum_bits = 0; + for(int i = EPOMAKER_BYTE_COMMAND; i <= EPOMAKER_BYTE_BLUE; i++) + { + sum_bits += buffer[i]; + } + + int next_pow2 = (int)(pow(2, ceil(log2((double)(sum_bits))))); + int filler = next_pow2 - sum_bits - 1; + + buffer[EPOMAKER_BYTE_FILLER] = filler; + + int send_buffer_result = hid_send_feature_report(dev, buffer, (sizeof(buffer) / sizeof(buffer[0]))); + if(send_buffer_result<0) + { + LOG_ERROR("[EPOMAKER]: Send Buffer Error. HIDAPI Error: %ls", hid_error(dev)); + } + +} diff --git a/Controllers/EpomakerController/EpomakerController.h b/Controllers/EpomakerController/EpomakerController.h new file mode 100644 index 0000000..dc4f4d3 --- /dev/null +++ b/Controllers/EpomakerController/EpomakerController.h @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| EpomakerController.h | +| | +| Driver for Epomaker keyboard | +| | +| Alvaro Munoz (alvaromunoz) 05 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +#define EPOMAKER_PACKET_LENGTH 0x40 +#define EPOMAKER_COMMAND_RGB 0x07 +#define EPOMAKER_COMMAND_SET 0xF60A +#define EPOMAKER_COMMAND_PING 0xF7 +#define HID_MAX_STR 255 + +enum +{ + EPOMAKER_BYTE_COMMAND = 1, + EPOMAKER_BYTE_MODE = 2, + EPOMAKER_BYTE_SPEED = 3, + EPOMAKER_BYTE_BRIGHTNESS = 4, + EPOMAKER_BYTE_FLAGS = 5, + EPOMAKER_BYTE_RED = 6, + EPOMAKER_BYTE_GREEN = 7, + EPOMAKER_BYTE_BLUE = 8, + EPOMAKER_BYTE_FILLER = 9 +}; + +enum +{ + EPOMAKER_MODE_ALWAYS_ON = 0x01, + EPOMAKER_MODE_DYNAMIC_BREATHING = 0x02, + EPOMAKER_MODE_SPECTRUM_CYCLE = 0x03, + EPOMAKER_MODE_DRIFT = 0x04, + EPOMAKER_MODE_WAVES_RIPPLE = 0x05, + EPOMAKER_MODE_STARS_TWINKLE = 0x06, + EPOMAKER_MODE_STEADY_STREAM = 0x07, + EPOMAKER_MODE_SHADOWING = 0x08, + EPOMAKER_MODE_PEAKS_RISING_ONE_AFTER_ANOTHER = 0x09, + EPOMAKER_MODE_SINE_WAVE = 0x0a, + EPOMAKER_MODE_CAISPRING_SURGING = 0x0b, + EPOMAKER_MODE_FLOWERS_BLOOMING = 0x0c, + EPOMAKER_MODE_LASER = 0x0e, + EPOMAKER_MODE_PEAK_TURN = 0x0f, + EPOMAKER_MODE_INCLINED_RAIN = 0x10, + EPOMAKER_MODE_SNOW = 0x11, + EPOMAKER_MODE_METEOR = 0x12, + EPOMAKER_MODE_THROUGH_THE_SNOW_NON_TRACE = 0x13, + EPOMAKER_MODE_LIGHT_SHADOW = 0x15 +}; + +enum +{ + EPOMAKER_SPEED_MIN = 0x00, + EPOMAKER_SPEED_MAX = 0x05, + EPOMAKER_SPEED_MAX_SPECIAL = 0x04, + EPOMAKER_SPEED_DEFAULT = 0x04 +}; + +enum +{ + EPOMAKER_BRIGHTNESS_MIN = 0x00, + EPOMAKER_BRIGHTNESS_MAX = 0x04, + EPOMAKER_BRIGHTNESS_DEFAULT = 0x04 +}; + +enum +{ + EPOMAKER_OPTION_DAZZLE_OFF = 0x07, + EPOMAKER_OPTION_DAZZLE_ON = 0x08, + EPOMAKER_OPTION_DEFAULT = 0x00, + EPOMAKER_OPTION_DRIFT_RIGHT = 0X00, + EPOMAKER_OPTION_DRIFT_LEFT = 0X10, + EPOMAKER_OPTION_DRIFT_DOWN = 0X20, + EPOMAKER_OPTION_DRIFT_UP = 0X30, + EPOMAKER_OPTION_STEADY_STREAM_ZIG_ZAG = 0x00, + EPOMAKER_OPTION_STEADY_STREAM_RETURN = 0x10, + EPOMAKER_OPTION_CAISPRING_SURGING_OUT = 0x00, + EPOMAKER_OPTION_CAISPRING_SURGING_IN = 0x10, + EPOMAKER_OPTION_FLOWERS_BLOOMING_RIGHT = 0x00, + EPOMAKER_OPTION_FLOWERS_BLOOMING_LEFT = 0x10, + EPOMAKER_OPTION_PEAK_TURN_ANTI_CLOCKWISE = 0x00, + EPOMAKER_OPTION_PEAK_TURN_CLOCKWISE = 0x10, +}; + +class EpomakerController +{ +public: + EpomakerController(hid_device* dev_handle, char *_path); + ~EpomakerController(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + void SetDazzle(bool is_dazzle); + void SetOption(unsigned char option); + + void SetMode(unsigned char mode, unsigned char speed, unsigned char brightness); + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + +private: + std::string device_name; + std::string location; + hid_device* dev; + + unsigned char current_mode; + unsigned char current_speed; + unsigned char current_brightness; + unsigned char current_dazzle; + unsigned char current_option; + + unsigned char current_red; + unsigned char current_green; + unsigned char current_blue; + + void SendUpdate(); +}; diff --git a/Controllers/EpomakerController/EpomakerControllerDetect.cpp b/Controllers/EpomakerController/EpomakerControllerDetect.cpp new file mode 100644 index 0000000..4597eca --- /dev/null +++ b/Controllers/EpomakerController/EpomakerControllerDetect.cpp @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| EpomakerControllerDetect.cpp | +| | +| Detector for Epomaker keyboard | +| | +| Alvaro Munoz (alvaromunoz) 05 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "EpomakerController.h" +#include "RGBController_EpomakerController.h" + +#define EPOMAKER_VID 0x3151 +#define EPOMAKER_TH80_Pro_USB_PID 0x4010 +#define ATTACKSHARK_K86_USB_PID 0x4015 +#define EPOMAKER_TH80_Pro_Dongle_PID 0x4011 /* Attack shark's Dongle is the same. */ +#define EPOMAKER_TH80_Pro_BT_PID 0x4013 +#define ATTACKSHARK_K86_BT_PID 0x4012 + +/******************************************************************************************\ +* * +* DetectEpomakerControllers * +* * +* Tests the USB address to see if any Epomaker Controllers exists there. * +* * +\******************************************************************************************/ + +void DetectEpomakerControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + EpomakerController* controller = new EpomakerController(dev, info->path); + RGBController_EpomakerController* rgb_controller = new RGBController_EpomakerController(controller); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectEpomakerControllers() */ + +REGISTER_HID_DETECTOR_I("Epomaker TH80 Pro (USB Cable)", DetectEpomakerControllers, EPOMAKER_VID, EPOMAKER_TH80_Pro_USB_PID, 2); +REGISTER_HID_DETECTOR_I("Epomaker TH80 Pro (USB Dongle)", DetectEpomakerControllers, EPOMAKER_VID, EPOMAKER_TH80_Pro_Dongle_PID, 2); +REGISTER_HID_DETECTOR_I("Attack Shark K86 (USB Cable)", DetectEpomakerControllers, EPOMAKER_VID, ATTACKSHARK_K86_USB_PID, 2); + +/*---------------------------------------------------------*\ +| Bluetooth Not implemented | +\*---------------------------------------------------------*/ +//REGISTER_HID_DETECTOR("Epomaker TH80 Pro (Bluetooth)", DetectEpomakerControllers, EPOMAKER_VID, EPOMAKER_TH80_Pro_BT_PID); +//REGISTER_HID_DETECTOR("Attack Shark K86 (Bluetooth)", DetectEpomakerControllers, EPOMAKER_VID, ATTACKSHARK_K86_BT_PID); diff --git a/Controllers/EpomakerController/RGBController_EpomakerController.cpp b/Controllers/EpomakerController/RGBController_EpomakerController.cpp new file mode 100644 index 0000000..3568f66 --- /dev/null +++ b/Controllers/EpomakerController/RGBController_EpomakerController.cpp @@ -0,0 +1,413 @@ +/*---------------------------------------------------------*\ +| RGBController_EpomakerController.cpp | +| | +| RGBController for Epomaker keyboard | +| | +| Alvaro Munoz (alvaromunoz) 05 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_EpomakerController.h" + +/**------------------------------------------------------------------*\ + @name Epomaker TH80 Pro + @category Keyboard + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectEpomakerControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_EpomakerController::RGBController_EpomakerController(EpomakerController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Epomaker"; + type = DEVICE_TYPE_KEYBOARD; + description = controller->GetDeviceName(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Off; + Off.name = "Off"; + Off.value = 0; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Always_on; + Always_on.name = "Direct"; + Always_on.value = EPOMAKER_MODE_ALWAYS_ON; + Always_on.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Always_on.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Always_on.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Always_on.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Always_on.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + modes.push_back(Always_on); + + mode Dynamic_breathing; + Dynamic_breathing.name = "Breathing"; + Dynamic_breathing.value = EPOMAKER_MODE_DYNAMIC_BREATHING; + Dynamic_breathing.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Dynamic_breathing.color_mode = MODE_COLORS_PER_LED; + Dynamic_breathing.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Dynamic_breathing.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Dynamic_breathing.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Dynamic_breathing.speed_min = EPOMAKER_SPEED_MIN; + Dynamic_breathing.speed_max = EPOMAKER_SPEED_MAX; + Dynamic_breathing.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Dynamic_breathing); + + mode Spectrum_cycle; + Spectrum_cycle.name = "Spectrum Cycle"; + Spectrum_cycle.value = EPOMAKER_MODE_SPECTRUM_CYCLE; + Spectrum_cycle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Spectrum_cycle.color_mode = MODE_COLORS_NONE; + Spectrum_cycle.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Spectrum_cycle.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Spectrum_cycle.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Spectrum_cycle.speed_min = EPOMAKER_SPEED_MIN; + Spectrum_cycle.speed_max = EPOMAKER_SPEED_MAX_SPECIAL; + Spectrum_cycle.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Spectrum_cycle); + + mode Drift; + Drift.name = "Drift"; + Drift.value = EPOMAKER_MODE_DRIFT; + Drift.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + Drift.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Drift.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Drift.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Drift.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Drift.speed_min = EPOMAKER_SPEED_MIN; + Drift.speed_max = EPOMAKER_SPEED_MAX; + Drift.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Drift); + + mode Waves_ripple; + Waves_ripple.name = "Waves ripple"; + Waves_ripple.value = EPOMAKER_MODE_WAVES_RIPPLE; + Waves_ripple.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Waves_ripple.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Waves_ripple.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Waves_ripple.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Waves_ripple.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + modes.push_back(Waves_ripple); + + mode Stars_twinkle; + Stars_twinkle.name = "Stars twinkle"; + Stars_twinkle.value = EPOMAKER_MODE_STARS_TWINKLE; + Stars_twinkle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Stars_twinkle.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Stars_twinkle.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Stars_twinkle.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Stars_twinkle.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Stars_twinkle.speed_min = EPOMAKER_SPEED_MIN; + Stars_twinkle.speed_max = EPOMAKER_SPEED_MAX; + Stars_twinkle.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Stars_twinkle); + + mode Steady_stream; + Steady_stream.name = "Steady stream"; + Steady_stream.value = EPOMAKER_MODE_STEADY_STREAM; + Steady_stream.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Steady_stream.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Steady_stream.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Steady_stream.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Steady_stream.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Steady_stream.speed_min = EPOMAKER_SPEED_MIN; + Steady_stream.speed_max = EPOMAKER_SPEED_MAX; + Steady_stream.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Steady_stream); + + mode Shadowing; + Shadowing.name = "Reactive"; + Shadowing.value = EPOMAKER_MODE_SHADOWING; + Shadowing.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Shadowing.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Shadowing.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Shadowing.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Shadowing.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Shadowing.speed_min = EPOMAKER_SPEED_MIN; + Shadowing.speed_max = EPOMAKER_SPEED_MAX; + Shadowing.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Shadowing); + + mode Peaks_rising; + Peaks_rising.name = "Peaks rising one after another"; + Peaks_rising.value = EPOMAKER_MODE_PEAKS_RISING_ONE_AFTER_ANOTHER; + Peaks_rising.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Peaks_rising.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Peaks_rising.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Peaks_rising.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Peaks_rising.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Peaks_rising.speed_min = EPOMAKER_SPEED_MIN; + Peaks_rising.speed_max = EPOMAKER_SPEED_MAX; + Peaks_rising.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Peaks_rising); + + mode Sine_wave; + Sine_wave.name = "Sine wave"; + Sine_wave.value = EPOMAKER_MODE_SINE_WAVE; + Sine_wave.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Sine_wave.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Sine_wave.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Sine_wave.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Sine_wave.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Sine_wave.speed_min = EPOMAKER_SPEED_MIN; + Sine_wave.speed_max = EPOMAKER_SPEED_MAX; + Sine_wave.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Sine_wave); + + mode Caispring; + Caispring.name = "Caispring Surging"; + Caispring.value = EPOMAKER_MODE_CAISPRING_SURGING; + Caispring.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Caispring.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Caispring.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Caispring.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Caispring.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Caispring.speed_min = EPOMAKER_SPEED_MIN; + Caispring.speed_max = EPOMAKER_SPEED_MAX; + Caispring.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Caispring); + + mode Flowers_blooming; + Flowers_blooming.name = "Flowers blooming"; + Flowers_blooming.value = EPOMAKER_MODE_FLOWERS_BLOOMING; + Flowers_blooming.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Flowers_blooming.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Flowers_blooming.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Flowers_blooming.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Flowers_blooming.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Flowers_blooming.speed_min = EPOMAKER_SPEED_MIN; + Flowers_blooming.speed_max = EPOMAKER_SPEED_MAX; + Flowers_blooming.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Flowers_blooming); + + mode Laser; + Laser.name = "Laser"; + Laser.value = EPOMAKER_MODE_LASER; + Laser.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Laser.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Laser.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Laser.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Laser.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Laser.speed_min = EPOMAKER_SPEED_MIN; + Laser.speed_max = EPOMAKER_SPEED_MAX; + Laser.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Laser); + + mode Peak_turn; + Peak_turn.name = "Peak turn"; + Peak_turn.value = EPOMAKER_MODE_PEAK_TURN; + Peak_turn.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Peak_turn.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Peak_turn.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Peak_turn.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Peak_turn.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Peak_turn.speed_min = EPOMAKER_SPEED_MIN; + Peak_turn.speed_max = EPOMAKER_SPEED_MAX_SPECIAL; + Peak_turn.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Peak_turn); + + mode Inclined_rain; + Inclined_rain.name = "Inclined Rain"; + Inclined_rain.value = EPOMAKER_MODE_INCLINED_RAIN; + Inclined_rain.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Inclined_rain.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Inclined_rain.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Inclined_rain.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Inclined_rain.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Inclined_rain.speed_min = EPOMAKER_SPEED_MIN; + Inclined_rain.speed_max = EPOMAKER_SPEED_MAX; + Inclined_rain.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Inclined_rain); + + mode Snow; + Snow.name = "Snow"; + Snow.value = EPOMAKER_MODE_SNOW; + Snow.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Snow.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Snow.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Snow.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Snow.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Snow.speed_min = EPOMAKER_SPEED_MIN; + Snow.speed_max = EPOMAKER_SPEED_MAX_SPECIAL; + Snow.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Snow); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = EPOMAKER_MODE_METEOR; + Meteor.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Meteor.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Meteor.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Meteor.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Meteor.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Meteor.speed_min = EPOMAKER_SPEED_MIN; + Meteor.speed_max = EPOMAKER_SPEED_MAX_SPECIAL; + Meteor.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Meteor); + + mode Through_the_snow; + Through_the_snow.name = "Through the snow (non trace)"; + Through_the_snow.value = EPOMAKER_MODE_THROUGH_THE_SNOW_NON_TRACE; + Through_the_snow.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Through_the_snow.color_mode = MODE_COLORS_PER_LED | MODE_COLORS_RANDOM; + Through_the_snow.brightness_min = EPOMAKER_BRIGHTNESS_MIN; + Through_the_snow.brightness_max = EPOMAKER_BRIGHTNESS_MAX; + Through_the_snow.brightness = EPOMAKER_BRIGHTNESS_DEFAULT; + Through_the_snow.speed_min = EPOMAKER_SPEED_MIN; + Through_the_snow.speed_max = EPOMAKER_SPEED_MAX_SPECIAL; + Through_the_snow.speed = EPOMAKER_SPEED_DEFAULT; + modes.push_back(Through_the_snow); + + SetupZones(); +} + +RGBController_EpomakerController::~RGBController_EpomakerController() +{ + delete controller; +} + +void RGBController_EpomakerController::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetDazzle(modes[active_mode].color_mode == MODE_COLORS_RANDOM); + controller->SetColor(red, grn, blu); +} + +void RGBController_EpomakerController::DeviceUpdateMode() +{ + if(modes[active_mode].value == EPOMAKER_MODE_DRIFT) + { + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + controller->SetOption(EPOMAKER_OPTION_DRIFT_LEFT); + } + else if(modes[active_mode].direction == MODE_DIRECTION_RIGHT) + { + controller->SetOption(EPOMAKER_OPTION_DRIFT_RIGHT); + } + else if(modes[active_mode].direction == MODE_DIRECTION_UP) + { + controller->SetOption(EPOMAKER_OPTION_DRIFT_UP); + } + else if(modes[active_mode].direction == MODE_DIRECTION_DOWN) + { + controller->SetOption(EPOMAKER_OPTION_DRIFT_DOWN); + } + } + else if(modes[active_mode].value == EPOMAKER_MODE_STEADY_STREAM) + { + /*---------------------------------------------------------*\ + | TODO: These OPTIONS (zig-zag, return) should not | + | be DIRECTIONS (left, right) | + \*---------------------------------------------------------*/ + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + controller->SetOption(EPOMAKER_OPTION_STEADY_STREAM_ZIG_ZAG); + } + else if(modes[active_mode].direction == MODE_DIRECTION_RIGHT) + { + controller->SetOption(EPOMAKER_OPTION_STEADY_STREAM_RETURN); + } + } + else if(modes[active_mode].value == EPOMAKER_MODE_CAISPRING_SURGING) + { + /*---------------------------------------------------------*\ + | TODO: These OPTIONS (in, out) should not | + | be DIRECTIONS (left, right) | + \*---------------------------------------------------------*/ + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + controller->SetOption(EPOMAKER_OPTION_CAISPRING_SURGING_OUT); + } + else if(modes[active_mode].direction == MODE_DIRECTION_RIGHT) + { + controller->SetOption(EPOMAKER_OPTION_CAISPRING_SURGING_IN); + } + } + else if(modes[active_mode].value == EPOMAKER_MODE_FLOWERS_BLOOMING) + { + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + controller->SetOption(EPOMAKER_OPTION_FLOWERS_BLOOMING_LEFT); + } + else if(modes[active_mode].direction == MODE_DIRECTION_RIGHT) + { + controller->SetOption(EPOMAKER_OPTION_FLOWERS_BLOOMING_RIGHT); + } + } + else if(modes[active_mode].value == EPOMAKER_MODE_PEAK_TURN) + { + /*---------------------------------------------------------*\ + | TODO: These OPTIONS (clockwise, anti-clockwise) | + | should not be DIRECTIONS (left, right) | + \*---------------------------------------------------------*/ + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + controller->SetOption(EPOMAKER_OPTION_PEAK_TURN_ANTI_CLOCKWISE); + } + else if(modes[active_mode].direction == MODE_DIRECTION_RIGHT) + { + controller->SetOption(EPOMAKER_OPTION_PEAK_TURN_CLOCKWISE); + } + } + else + { + controller->SetOption(EPOMAKER_OPTION_DEFAULT); + } + + controller->SetDazzle(modes[active_mode].color_mode == MODE_COLORS_RANDOM); + + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness); +} + +void RGBController_EpomakerController::SetupZones() +{ + zone new_zone; + new_zone.name = "Keyboard"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + + zones.push_back(new_zone); + + led keyboard_led; + keyboard_led.name = "Keyboard LEDs"; + keyboard_led.value = 0x00; + leds.push_back(keyboard_led); + + SetupColors(); +} + + +void RGBController_EpomakerController::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | Not implemented | + \*---------------------------------------------------------*/ +} + +void RGBController_EpomakerController::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_EpomakerController::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/EpomakerController/RGBController_EpomakerController.h b/Controllers/EpomakerController/RGBController_EpomakerController.h new file mode 100644 index 0000000..cf3efd0 --- /dev/null +++ b/Controllers/EpomakerController/RGBController_EpomakerController.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_EpomakerController.h | +| | +| RGBController for Epomaker keyboard | +| | +| Alvaro Munoz (alvaromunoz) 05 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "EpomakerController.h" + +class RGBController_EpomakerController : public RGBController +{ +public: + RGBController_EpomakerController(EpomakerController* controller_ptr); + ~RGBController_EpomakerController(); + + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + EpomakerController* controller; +}; diff --git a/Controllers/EspurnaController/EspurnaController.cpp b/Controllers/EspurnaController/EspurnaController.cpp new file mode 100644 index 0000000..f54bd6f --- /dev/null +++ b/Controllers/EspurnaController/EspurnaController.cpp @@ -0,0 +1,78 @@ +/*---------------------------------------------------------*\ +| EspurnaController.cpp | +| | +| Driver for Espurna | +| | +| Adam Honse (calcprogrammer1@gmail.com) 11 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "EspurnaController.h" + +EspurnaController::EspurnaController() +{ + +} + +EspurnaController::~EspurnaController() +{ +} + +void EspurnaController::Initialize(char* ledstring) +{ + LPSTR apikey = NULL; + LPSTR source = NULL; + LPSTR udpport_baud = NULL; + LPSTR next = NULL; + + source = strtok_s(ledstring, ",", &next); + + //Check for either the UDP port or the serial baud rate + if (strlen(next)) + { + udpport_baud = strtok_s(next, ",", &next); + } + + //Espurna protocol requires API key + if (strlen(next)) + { + apikey = strtok_s(next, ",", &next); + } + + InitializeEspurna(source, udpport_baud, apikey); +} + +void EspurnaController::InitializeEspurna(char * clientname, char * port, char * apikey) +{ + client_name = clientname; + port_name = port; + + strcpy(espurna_apikey, apikey); + tcpport = new net_port; + tcpport->tcp_client(client_name.c_str(), port_name.c_str()); +} + +std::string EspurnaController::GetLocation() +{ + return("TCP: " + client_name + ":" + port_name); +} + +void EspurnaController::SetLEDs(std::vector colors) +{ + if (tcpport != NULL) + { + RGBColor color = colors[0]; + + char get_request[1024]; + snprintf(get_request, 1024, "GET /api/rgb?apikey=%s&value=%%23%02X%02X%02X HTTP/1.1\r\nHost: %s\r\n\r\n", espurna_apikey, RGBGetRValue(color), RGBGetGValue(color), RGBGetBValue(color), client_name.c_str()); + tcpport->tcp_client_connect(); + tcpport->tcp_client_write(get_request, (int)strlen(get_request)); + tcpport->tcp_close(); + } +} diff --git a/Controllers/EspurnaController/EspurnaController.h b/Controllers/EspurnaController/EspurnaController.h new file mode 100644 index 0000000..0f5b01e --- /dev/null +++ b/Controllers/EspurnaController/EspurnaController.h @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| EspurnaController.h | +| | +| Driver for Espurna | +| | +| Adam Honse (calcprogrammer1@gmail.com) 11 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "net_port.h" + +#ifndef TRUE +#define TRUE true +#define FALSE false +#endif + +#ifndef WIN32 +#define LPSTR char * +#define strtok_s strtok_r +#endif + +class EspurnaController +{ +public: + EspurnaController(); + ~EspurnaController(); + + void Initialize(char* ledstring); + void InitializeEspurna(char* clientname, char* port, char * apikey); + + std::string GetLocation(); + + void SetLEDs(std::vector colors); + +private: + std::string port_name; + std::string client_name; + char espurna_apikey[128]; + + net_port *tcpport; +}; diff --git a/Controllers/EspurnaController/EspurnaControllerDetect.cpp b/Controllers/EspurnaController/EspurnaControllerDetect.cpp new file mode 100644 index 0000000..f93c70b --- /dev/null +++ b/Controllers/EspurnaController/EspurnaControllerDetect.cpp @@ -0,0 +1,80 @@ +/*---------------------------------------------------------*\ +| EspurnaControllerDetect.cpp | +| | +| Detctor for Espurna | +| | +| Adam Honse (calcprogrammer1@gmail.com) 11 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "EspurnaController.h" +#include "RGBController_Espurna.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectEspurnaControllers * +* * +* Detect devices supported by the Espurna driver * +* * +\******************************************************************************************/ + +void DetectEspurnaControllers() +{ + json espurna_settings; + + /*-------------------------------------------------*\ + | Get Espurna settings from settings manager | + \*-------------------------------------------------*/ + espurna_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("EspurnaDevices"); + + /*-------------------------------------------------*\ + | If the Espurna settings contains devices, process | + \*-------------------------------------------------*/ + if(espurna_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < espurna_settings["devices"].size(); device_idx++) + { + std::string ip; + std::string port; + std::string apikey; + + if(espurna_settings["devices"][device_idx].contains("ip")) + { + ip = espurna_settings["devices"][device_idx]["ip"]; + } + + if(espurna_settings["devices"][device_idx].contains("port")) + { + if(espurna_settings["devices"][device_idx]["port"].type() == json::value_t::string) + { + port = espurna_settings["devices"][device_idx]["port"]; + } + else + { + port = std::to_string((unsigned int)espurna_settings["devices"][device_idx]["port"]); + } + } + + if(espurna_settings["devices"][device_idx].contains("apikey")) + { + apikey = espurna_settings["devices"][device_idx]["apikey"]; + } + + std::string value = ip + "," + port + "," + apikey; + + EspurnaController* controller = new EspurnaController(); + controller->Initialize((char *)value.c_str()); + + RGBController_Espurna* rgb_controller = new RGBController_Espurna(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + +} /* DetectEspurnaControllers() */ + +REGISTER_DETECTOR("Espurna", DetectEspurnaControllers); diff --git a/Controllers/EspurnaController/RGBController_Espurna.cpp b/Controllers/EspurnaController/RGBController_Espurna.cpp new file mode 100644 index 0000000..cd9bb60 --- /dev/null +++ b/Controllers/EspurnaController/RGBController_Espurna.cpp @@ -0,0 +1,93 @@ +/*---------------------------------------------------------*\ +| RGBController_Espurna.cpp | +| | +| RGBController for Espurna | +| | +| Adam Honse (calcprogrammer1@gmail.com) 11 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Espurna.h" + +/**------------------------------------------------------------------*\ + @name Espurna + @category Light + @type TCP + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectEspurnaControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Espurna::RGBController_Espurna(EspurnaController* controller_ptr) +{ + controller = controller_ptr; + + name = "Espurna"; + type = DEVICE_TYPE_LIGHT; + description = "Espurna Device"; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_Espurna::~RGBController_Espurna() +{ + delete controller; +} + +void RGBController_Espurna::SetupZones() +{ + zone led_zone; + led_zone.name = "RGB Light"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + led new_led; + new_led.name = "RGB Light"; + + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_Espurna::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Espurna::DeviceUpdateLEDs() +{ + controller->SetLEDs(colors); +} + +void RGBController_Espurna::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_Espurna::UpdateSingleLED(int /*led*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_Espurna::DeviceUpdateMode() +{ + +} diff --git a/Controllers/EspurnaController/RGBController_Espurna.h b/Controllers/EspurnaController/RGBController_Espurna.h new file mode 100644 index 0000000..e3e00d3 --- /dev/null +++ b/Controllers/EspurnaController/RGBController_Espurna.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_Espurna.h | +| | +| RGBController for Espurna | +| | +| Adam Honse (calcprogrammer1@gmail.com) 11 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "EspurnaController.h" + +class RGBController_Espurna : public RGBController +{ +public: + RGBController_Espurna(EspurnaController* controller_ptr); + ~RGBController_Espurna(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + EspurnaController* controller; +}; diff --git a/Controllers/FanBusController/FanBusController.cpp b/Controllers/FanBusController/FanBusController.cpp new file mode 100644 index 0000000..18a15ca --- /dev/null +++ b/Controllers/FanBusController/FanBusController.cpp @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| FanBusController.cpp | +| | +| Driver for FanBus devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "FanBusController.h" + +FanBusController::FanBusController(FanBusInterface* bus_ptr, unsigned char dev_addr) +{ + bus = bus_ptr; + dev = dev_addr; +} + +FanBusController::~FanBusController() +{ + delete bus; +} + +std::string FanBusController::GetLocation() +{ + std::string location_string; + + location_string = "FanBus: "; + location_string.append(bus->GetPort()); + location_string.append(":"); + location_string.append(std::to_string(dev)); + + return(location_string); +} + +void FanBusController::SetLEDs(std::vector colors) +{ + for(unsigned int led_idx = 0; led_idx < 4; led_idx++) + { + unsigned char red = RGBGetRValue(colors[led_idx]); + unsigned char grn = RGBGetGValue(colors[led_idx]); + unsigned char blu = RGBGetBValue(colors[led_idx]); + + bus->write_queue(dev, 0x10 + (led_idx * 3), red); + bus->write_queue(dev, 0x11 + (led_idx * 3), grn); + bus->write_queue(dev, 0x12 + (led_idx * 3), blu); + } + + bus->write_queue(dev, 0x0C, 0x01); + + bus->process_queue(); +} diff --git a/Controllers/FanBusController/FanBusController.h b/Controllers/FanBusController/FanBusController.h new file mode 100644 index 0000000..521e16a --- /dev/null +++ b/Controllers/FanBusController/FanBusController.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| FanBusController.h | +| | +| Driver for FanBus devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "FanBusInterface.h" +#include "RGBController.h" + +class FanBusController +{ +public: + FanBusController(FanBusInterface* bus_ptr, unsigned char dev_addr); + ~FanBusController(); + + std::string GetLocation(); + + void SetLEDs(std::vector colors); + +private: + std::string port_name; + FanBusInterface* bus; + unsigned char dev; +}; diff --git a/Controllers/FanBusController/FanBusControllerDetect.cpp b/Controllers/FanBusController/FanBusControllerDetect.cpp new file mode 100644 index 0000000..bc4e8f7 --- /dev/null +++ b/Controllers/FanBusController/FanBusControllerDetect.cpp @@ -0,0 +1,54 @@ +/*---------------------------------------------------------*\ +| FanBusControllerDetect.cpp | +| | +| Detector for FanBus devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "FanBusController.h" +#include "RGBController_FanBus.h" +#include "SettingsManager.h" + +void DetectFanBusControllers() +{ + FanBusInterface* new_interface; + json fanbus_settings; + + /*-------------------------------------------------*\ + | Get LED Strip settings from settings manager | + \*-------------------------------------------------*/ + fanbus_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("FanBusDevices"); + + /*-------------------------------------------------*\ + | If the LEDStrip settings contains devices, process| + \*-------------------------------------------------*/ + if(fanbus_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < fanbus_settings["devices"].size(); device_idx++) + { + if(fanbus_settings["devices"][device_idx].contains("port")) + { + std::string port_val = fanbus_settings["devices"][device_idx]["port"]; + + new_interface = new FanBusInterface(port_val.c_str()); + + std::vector detected_controllers = new_interface->DetectControllers(); + + for(unsigned int controller_idx = 0; controller_idx < detected_controllers.size(); controller_idx++) + { + FanBusController* controller = new FanBusController(new_interface, detected_controllers[controller_idx]); + RGBController_FanBus* rgb_controller = new RGBController_FanBus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } + } +} + +REGISTER_DETECTOR("FanBus", DetectFanBusControllers); diff --git a/Controllers/FanBusController/FanBusInterface.cpp b/Controllers/FanBusController/FanBusInterface.cpp new file mode 100644 index 0000000..765dc69 --- /dev/null +++ b/Controllers/FanBusController/FanBusInterface.cpp @@ -0,0 +1,149 @@ +/*---------------------------------------------------------*\ +| FanBusInterface.cpp | +| | +| Interface for FanBus devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "FanBusInterface.h" + +using namespace std::chrono_literals; + +FanBusInterface::FanBusInterface(const char* portname) +{ + port_name = portname; + serialport = new serial_port(portname, 38400); + + /*-----------------------------------------------------*\ + | Flush any data in the receive queue | + \*-----------------------------------------------------*/ + unsigned char read_buf[6]; + + while(serialport->serial_read((char *)read_buf, 6) > 0) + { + + } + + read_buf[0] = 0xFF; + + serialport->serial_write((char *)read_buf, 1); + + std::this_thread::sleep_for(10ms); + + int test = serialport->serial_read((char *)read_buf, 1); + + if(test > 0) + { + half_duplex = true; + } + else + { + half_duplex = false; + } +} + +FanBusInterface::~FanBusInterface() +{ + serialport->serial_close(); + delete serialport; +} + +std::string FanBusInterface::GetPort() +{ + return(port_name); +} + +int FanBusInterface::read + ( + unsigned char dev_addr, + unsigned char int_addr + ) +{ + unsigned char fanbus_msg[] = { 0x01, int_addr, dev_addr, 0x00, 0xFF }; + + serialport->serial_write((char *)fanbus_msg, 5); + + std::this_thread::sleep_for(10ms); + + char read_buf[6]; + + if(half_duplex) + { + if(serialport->serial_read(read_buf, 6) == 6) + { + return(read_buf[5]); + } + else + { + return(-1); + } + } + else + { + if(serialport->serial_read(read_buf, 1) == 1) + { + return(read_buf[0]); + } + else + { + return(-1); + } + } +} + +int FanBusInterface::write + ( + unsigned char dev_addr, + unsigned char int_addr, + unsigned char val + ) +{ + unsigned char fanbus_msg[] = { 0x00, int_addr, dev_addr, val, 0xFF }; + + return(serialport->serial_write((char *)fanbus_msg, 5)); +} + +void FanBusInterface::write_queue + ( + unsigned char dev_addr, + unsigned char int_addr, + unsigned char val + ) +{ + unsigned char fanbus_msg[] = { 0x00, int_addr, dev_addr, val, 0xFF }; + + for(unsigned int i = 0; i < sizeof(fanbus_msg); i++) + { + fanbus_msg_queued.push_back(fanbus_msg[i]); + } +} + +int FanBusInterface::process_queue() +{ + int return_val = serialport->serial_write((char *)&fanbus_msg_queued[0], (int)fanbus_msg_queued.size()); + + fanbus_msg_queued.clear(); + + return(return_val); +} + +std::vector FanBusInterface::DetectControllers() +{ + std::vector detected_controllers; + + for(unsigned int dev_addr = 0x03; dev_addr < 0xFF; dev_addr++) + { + if(read(dev_addr, 0x00) >= 0) + { + detected_controllers.push_back(dev_addr); + } + } + + return(detected_controllers); +} diff --git a/Controllers/FanBusController/FanBusInterface.h b/Controllers/FanBusController/FanBusInterface.h new file mode 100644 index 0000000..cbcec7c --- /dev/null +++ b/Controllers/FanBusController/FanBusInterface.h @@ -0,0 +1,54 @@ +/*---------------------------------------------------------*\ +| FanBusInterface.h | +| | +| Interface for FanBus devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "serial_port.h" + +class FanBusInterface +{ +public: + FanBusInterface(const char* portname); + ~FanBusInterface(); + + std::vector DetectControllers(); + + std::string GetPort(); + + int read + ( + unsigned char dev_addr, + unsigned char int_addr + ); + + int write + ( + unsigned char dev_addr, + unsigned char int_addr, + unsigned char val + ); + + void write_queue + ( + unsigned char dev_addr, + unsigned char int_addr, + unsigned char val + ); + + int process_queue(); + +private: + serial_port * serialport; + std::string port_name; + bool half_duplex; + + std::vector fanbus_msg_queued; +}; diff --git a/Controllers/FanBusController/RGBController_FanBus.cpp b/Controllers/FanBusController/RGBController_FanBus.cpp new file mode 100644 index 0000000..97cbb32 --- /dev/null +++ b/Controllers/FanBusController/RGBController_FanBus.cpp @@ -0,0 +1,97 @@ +/*---------------------------------------------------------*\ +| RGBController_FanBus.cpp | +| | +| RGBController for FanBus devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_FanBus.h" + +/**------------------------------------------------------------------*\ + @name FanBus + @category Cooler + @type Serial + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectFanBusControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_FanBus::RGBController_FanBus(FanBusController* controller_ptr) +{ + controller = controller_ptr; + + name = "FanBus Device"; + type = DEVICE_TYPE_COOLER; + description = "FanBus Device"; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_FanBus::~RGBController_FanBus() +{ + delete controller; +} + +void RGBController_FanBus::SetupZones() +{ + zone led_zone; + led_zone.name = "Fan LEDs"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 4; + led_zone.leds_max = 4; + led_zone.leds_count = 4; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + for(unsigned int led_idx = 0; led_idx < led_zone.leds_count; led_idx++) + { + led new_led; + new_led.name = "LED "; + new_led.name.append(std::to_string(led_idx)); + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_FanBus::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_FanBus::DeviceUpdateLEDs() +{ + controller->SetLEDs(colors); +} + +void RGBController_FanBus::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_FanBus::UpdateSingleLED(int /*led*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_FanBus::DeviceUpdateMode() +{ + +} diff --git a/Controllers/FanBusController/RGBController_FanBus.h b/Controllers/FanBusController/RGBController_FanBus.h new file mode 100644 index 0000000..73feccb --- /dev/null +++ b/Controllers/FanBusController/RGBController_FanBus.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_FanBus.h | +| | +| RGBController for FanBus devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "FanBusController.h" + +class RGBController_FanBus : public RGBController +{ +public: + RGBController_FanBus(FanBusController* controller_ptr); + ~RGBController_FanBus(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + FanBusController* controller; +}; diff --git a/Controllers/FaustusController/RGBController_Faustus_Linux.cpp b/Controllers/FaustusController/RGBController_Faustus_Linux.cpp new file mode 100644 index 0000000..6aac48e --- /dev/null +++ b/Controllers/FaustusController/RGBController_Faustus_Linux.cpp @@ -0,0 +1,204 @@ +/*---------------------------------------------------------*\ +| RGBController_Faustus_Linux.cpp | +| | +| RGBController for Faustus devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_Faustus_Linux.h" +#include "Detector.h" + +/**------------------------------------------------------------------*\ + @name ASUS TUF Keyboard (Faustus) + @category Keyboard + @type File Stream + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectFaustusControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Faustus::RGBController_Faustus(const std::string& dev_path) +{ + name = "ASUS TUF Laptop Keyboard"; + vendor = "ASUS"; + type = DEVICE_TYPE_LAPTOP; + description = "Faustus Device"; + + modes.resize(4); + modes[0].name = "Static"; + modes[0].value = FAUSTUS_MODE_STATIC; + modes[0].flags = MODE_FLAG_HAS_PER_LED_COLOR; + modes[0].color_mode = MODE_COLORS_PER_LED; + + modes[1].name = "Breathing"; + modes[1].value = FAUSTUS_MODE_BREATHING; + modes[1].flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + modes[1].speed_min = FAUSTUS_SPEED_SLOWEST; + modes[1].speed_max = FAUSTUS_SPEED_FASTEST; + modes[1].color_mode = MODE_COLORS_PER_LED; + modes[1].speed = FAUSTUS_SPEED_NORMAL; + + modes[2].name = "Color Cycle"; + modes[2].value = FAUSTUS_MODE_COLOR_CYCLE; + modes[2].flags = MODE_FLAG_HAS_SPEED; + modes[2].speed_min = FAUSTUS_SPEED_SLOWEST; + modes[2].speed_max = FAUSTUS_SPEED_FASTEST; + modes[2].color_mode = MODE_COLORS_NONE; + modes[2].speed = FAUSTUS_SPEED_NORMAL; + + modes[3].name = "Strobe"; + modes[3].value = FAUSTUS_MODE_STROBE; + modes[3].flags = MODE_FLAG_HAS_PER_LED_COLOR; + modes[3].color_mode = MODE_COLORS_PER_LED; + + SetupZones(); + + // Prepare file streams + r_path = dev_path; + g_path = dev_path; + b_path = dev_path; + mode_path = dev_path; + flags_path = dev_path; + set_path = dev_path; + + r_path.append("/kbbl_red"); + g_path.append("/kbbl_green"); + b_path.append("/kbbl_blue"); + mode_path.append("/kbbl_mode"); + flags_path.append("/kbbl_flags"); + set_path.append("/kbbl_set"); +} + +void RGBController_Faustus::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zones.resize(1); + zones[0].type = ZONE_TYPE_SINGLE; + zones[0].name = "Keyboard Backlight zone"; + zones[0].leds_min = 1; + zones[0].leds_max = 1; + zones[0].leds_count = 1; + zones[0].matrix_map = NULL; + + /*---------------------------------------------------------*\ + | Set up LED | + \*---------------------------------------------------------*/ + leds.resize(1); + leds[0].name = "Keyboard Backlight LED"; + + SetupColors(); +} + +void RGBController_Faustus::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Faustus::DeviceUpdateLEDs() +{ + int rv = uint8_t(RGBGetRValue(colors[0])); + int gv = uint8_t(RGBGetGValue(colors[0])); + int bv = uint8_t(RGBGetBValue(colors[0])); + + std::ofstream str_r; + std::ofstream str_g; + std::ofstream str_b; + std::ofstream str_mode; + std::ofstream str_flags; + std::ofstream str_set; + + str_r.open(r_path, std::ios::out | std::ios::trunc); + str_g.open(g_path, std::ios::out | std::ios::trunc); + str_b.open(b_path, std::ios::out | std::ios::trunc); + str_mode.open(mode_path, std::ios::out | std::ios::trunc); + str_flags.open(flags_path, std::ios::out | std::ios::trunc); + str_set.open(set_path, std::ios::out | std::ios::trunc); + + str_r << std::hex; + str_g << std::hex; + str_b << std::hex; + str_mode << std::hex; + str_flags << std::hex; + str_set << std::hex; + + str_r << rv; + str_g << gv; + str_b << bv; + str_mode << active_mode; + str_flags << 0x2a; // All of em + str_set << 2; + + // Flush everything + str_r.close(); + str_g.close(); + str_b.close(); + str_mode.close(); + str_flags.close(); + str_set.close(); +} + +void RGBController_Faustus::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Faustus::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Faustus::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void DetectFaustusControllers() +{ + const char* base_path = "/sys/devices/platform/faustus/kbbl"; + DIR* dir = opendir(base_path); + + if(!dir) + { + return; + } + + // Directory is present - we pretty much have a driver confirmation already, but double check for all files required just in case + struct dirent* dent = readdir(dir); + + if(!dent) + { + return; + } + + int found = 0; + while(dent) + { + const char* fname = dent->d_name; + if(!strcmp(fname, "kbbl_red") || !strcmp(fname, "kbbl_green") || !strcmp(fname, "kbbl_blue") || !strcmp(fname, "kbbl_mode") || !strcmp(fname, "kbbl_flags") || !strcmp(fname, "kbbl_set")) + { + ++found; + } + dent = readdir(dir); + } + + closedir(dir); + + if(found != 6) + { + return; + } + + ResourceManager::get()->RegisterRGBController(new RGBController_Faustus(base_path)); +} /* DetectFaustusControllers() */ + +REGISTER_DETECTOR("Faustus", DetectFaustusControllers); diff --git a/Controllers/FaustusController/RGBController_Faustus_Linux.h b/Controllers/FaustusController/RGBController_Faustus_Linux.h new file mode 100644 index 0000000..9b3928b --- /dev/null +++ b/Controllers/FaustusController/RGBController_Faustus_Linux.h @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| RGBController_Faustus_Linux.h | +| | +| RGBController for Faustus devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +enum +{ + FAUSTUS_MODE_STATIC = 0, + FAUSTUS_MODE_BREATHING = 1, + FAUSTUS_MODE_COLOR_CYCLE = 2, + FAUSTUS_MODE_STROBE = 3 +}; +enum +{ + FAUSTUS_SPEED_SLOWEST = 0, + FAUSTUS_SPEED_NORMAL = 1, + FAUSTUS_SPEED_FASTEST = 2, +}; + +class RGBController_Faustus : public RGBController +{ + private: + std::string r_path; + std::string g_path; + std::string b_path; + std::string mode_path; + std::string flags_path; + std::string set_path; + + public: + RGBController_Faustus(const std::string& dev_path); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); +}; diff --git a/Controllers/FnaticStreakController/FnaticStreakController.cpp b/Controllers/FnaticStreakController/FnaticStreakController.cpp new file mode 100644 index 0000000..26a93d6 --- /dev/null +++ b/Controllers/FnaticStreakController/FnaticStreakController.cpp @@ -0,0 +1,379 @@ +/*---------------------------------------------------------*\ +| FnaticStreakController.cpp | +| | +| Driver for Fnatic Streak and miniStreak keyboards | +| | +| Based on leddy project by Hanna Czenczek | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "FnaticStreakController.h" +#include "StringUtils.h" +#include "LogManager.h" + +FnaticStreakController::FnaticStreakController(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name, FnaticStreakType kb_type) +{ + dev = dev_handle; + location = dev_info->path; + name = dev_name; + keyboard_type = kb_type; + profile = 1; + software_effect_mode = false; + + memset(color_buf, 0x00, sizeof(color_buf)); + + /*-----------------------------------------------------*\ + | Get the firmware version from the device info | + \*-----------------------------------------------------*/ + char fw_version_buf[8]; + memset(fw_version_buf, '\0', sizeof(fw_version_buf)); + + unsigned short version = dev_info->release_number; + snprintf(fw_version_buf, 8, "%.2X.%.2X", (version & 0xFF00) >> 8, version & 0x00FF); + + firmware_version = fw_version_buf; +} + +FnaticStreakController::~FnaticStreakController() +{ + hid_close(dev); +} + +std::string FnaticStreakController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string FnaticStreakController::GetNameString() +{ + return(name); +} + +std::string FnaticStreakController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string FnaticStreakController::GetFirmwareVersion() +{ + return(firmware_version); +} + +FnaticStreakType FnaticStreakController::GetKeyboardType() +{ + return(keyboard_type); +} + +unsigned int FnaticStreakController::GetLEDCount() +{ + if(keyboard_type == FNATIC_STREAK_TYPE_MINI) + { + return 106; + } + else + { + return 124; + } +} + +void FnaticStreakController::SetProfile(unsigned char new_profile) +{ + if(new_profile >= 1 && new_profile <= 4) + { + profile = new_profile; + SoftwareEffectEnd(); + } +} + +void FnaticStreakController::SoftwareEffectStart() +{ + software_effect_mode = true; +} + +void FnaticStreakController::SoftwareEffectEnd() +{ + software_effect_mode = false; + RefreshProfile(); +} + +void FnaticStreakController::SendKeepalive() +{ + /*-----------------------------------------------------*\ + | Send keepalive packet (0x07 or 0xfe) to prevent | + | keyboard from reverting to profile effect during | + | direct/preview mode | + \*-----------------------------------------------------*/ + unsigned char prefix[] = { 0x07 }; + SendRequest(prefix, sizeof(prefix), nullptr, 0); +} + +void FnaticStreakController::RefreshProfile() +{ + unsigned char data[] = { profile }; + unsigned char prefix[] = { 0x04 }; + SendRequest(prefix, sizeof(prefix), data, sizeof(data)); +} + +void FnaticStreakController::SendRequest(const unsigned char* prefix, size_t prefix_len, const unsigned char* raw_data, size_t data_len) +{ + size_t total_len = prefix_len + data_len; + size_t offset = 0; + unsigned char cmd = (prefix_len > 0) ? prefix[0] : raw_data[0]; + + while(offset < total_len) + { + unsigned char packet[65]; + memset(packet, 0x00, sizeof(packet)); + + /*-----------------------------------------------------*\ + | Packet format: | + | [0] = Report ID (0x00) | + | [1] = Command | + | [2-4] = Total length (24-bit little endian) | + | [5-7] = Offset (24-bit little endian) | + | [8-64] = Data (57 bytes max per packet) | + \*-----------------------------------------------------*/ + packet[0] = 0x00; + packet[1] = cmd; + packet[2] = (unsigned char)(total_len & 0xFF); + packet[3] = (unsigned char)((total_len >> 8) & 0xFF); + packet[4] = (unsigned char)((total_len >> 16) & 0xFF); + packet[5] = (unsigned char)(offset & 0xFF); + packet[6] = (unsigned char)((offset >> 8) & 0xFF); + packet[7] = (unsigned char)((offset >> 16) & 0xFF); + + for(size_t i = offset; i < offset + 57 && i < total_len; i++) + { + if(i < prefix_len) + { + packet[i - offset + 8] = prefix[i]; + } + else + { + packet[i - offset + 8] = raw_data[i - prefix_len]; + } + } + + hid_write(dev, packet, 65); + offset += 57; + } + + /*-----------------------------------------------------*\ + | For command 0x05, save changes and refresh profile | + \*-----------------------------------------------------*/ + if(cmd == 0x05) + { + unsigned char save_prefix[] = { 0x13 }; + SendRequest(save_prefix, sizeof(save_prefix), nullptr, 0); + + unsigned char profile_data[] = { profile }; + unsigned char profile_prefix[] = { 0x04 }; + SendRequest(profile_prefix, sizeof(profile_prefix), profile_data, sizeof(profile_data)); + } +} + +void FnaticStreakController::SetLEDsDirect(std::vector leds, std::vector colors, unsigned int brightness) +{ + unsigned int total_leds = GetLEDCount(); + + /*-----------------------------------------------------*\ + | Clear the color buffer | + \*-----------------------------------------------------*/ + memset(color_buf, 0x00, sizeof(color_buf)); + + /*-----------------------------------------------------*\ + | Transfer colors to the buffer | + | Format: sequential RGB triplets indexed by LED value | + | The LED value corresponds to the physical LED index | + | in the keyboard hardware (0-123 for full, 0-105 mini) | + | Apply brightness scaling (0-100%) | + \*-----------------------------------------------------*/ + unsigned int leds_to_set = (unsigned int)std::min(colors.size(), leds.size()); + + for(unsigned int i = 0; i < leds_to_set; i++) + { + unsigned int led_idx = leds[i].value; + if(led_idx < total_leds) + { + if(brightness >= 100) + { + /*-----------------------------------------*\ + | Full brightness - no scaling needed | + \*-----------------------------------------*/ + color_buf[led_idx * 3 + 0] = RGBGetRValue(colors[i]); + color_buf[led_idx * 3 + 1] = RGBGetGValue(colors[i]); + color_buf[led_idx * 3 + 2] = RGBGetBValue(colors[i]); + } + else + { + /*-----------------------------------------*\ + | Apply brightness scaling | + \*-----------------------------------------*/ + color_buf[led_idx * 3 + 0] = (unsigned char)(RGBGetRValue(colors[i]) * brightness / 100); + color_buf[led_idx * 3 + 1] = (unsigned char)(RGBGetGValue(colors[i]) * brightness / 100); + color_buf[led_idx * 3 + 2] = (unsigned char)(RGBGetBValue(colors[i]) * brightness / 100); + } + } + } +} + +void FnaticStreakController::SendRGBToDevice() +{ + if(keyboard_type == FNATIC_STREAK_TYPE_65) + { + unsigned char data[64]; + memset(data, 0x00, sizeof(data)); + + data[0] = 0x0F; + data[1] = 0x15; + + data[7] = 0x0F; + data[8] = 0x03; + + for(unsigned int i = 9; i < 25; i++) + { + data[i] = 0xFF; + } + + data[25] = color_buf[0]; + data[26] = color_buf[1]; + data[27] = color_buf[2]; + + hid_write(dev, data, sizeof(data)); + } + else + { + unsigned int total_leds = GetLEDCount(); + unsigned int data_size = total_leds * 3; + + /*-----------------------------------------------------*\ + | For direct/software control, use command 0x0f | + | This bypasses the profile and allows immediate update | + | The 0x03 subcommand indicates per-key color data | + \*-----------------------------------------------------*/ + unsigned char prefix[] = { 0x0f, 0x03 }; + SendRequest(prefix, sizeof(prefix), color_buf, data_size); + } +} + +void FnaticStreakController::SetPulse(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed) +{ + /*-----------------------------------------------------*\ + | Pulse effect - cmd 0x06 | + | Data: [mode, r, g, b, speed] | + \*-----------------------------------------------------*/ + unsigned char prefix[] = { 0x05, profile, 0x02 }; + unsigned char data[] = { FNATIC_STREAK_CMD_PULSE, color_mode, r, g, b, speed }; + SendRequest(prefix, sizeof(prefix), data, sizeof(data)); +} + +void FnaticStreakController::SetWave(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, unsigned char direction) +{ + /*-----------------------------------------------------*\ + | Wave effect - cmd 0x07 | + | Data: [mode, r, g, b, speed, direction] | + \*-----------------------------------------------------*/ + unsigned char prefix[] = { 0x05, profile, 0x02 }; + unsigned char data[] = { FNATIC_STREAK_CMD_WAVE, color_mode, r, g, b, speed, direction }; + SendRequest(prefix, sizeof(prefix), data, sizeof(data)); +} + +void FnaticStreakController::SetReactive(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, bool keyup) +{ + /*-----------------------------------------------------*\ + | Reactive effect - cmd 0x09 | + | Data: [mode, r, g, b, speed, trigger] | + | trigger: 0 = keydown, 1 = keyup | + \*-----------------------------------------------------*/ + unsigned char prefix[] = { 0x05, profile, 0x02 }; + unsigned char data[] = { FNATIC_STREAK_CMD_REACTIVE, color_mode, r, g, b, speed, (unsigned char)(keyup ? 0 : 1) }; + SendRequest(prefix, sizeof(prefix), data, sizeof(data)); +} + +void FnaticStreakController::SetReactiveRipple(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, bool keyup) +{ + /*-----------------------------------------------------*\ + | Reactive Ripple effect - cmd 0x0A | + | Data: [mode, r, g, b, speed, trigger] | + | trigger: 0 = keydown, 1 = keyup | + \*-----------------------------------------------------*/ + unsigned char prefix[] = { 0x05, profile, 0x02 }; + unsigned char data[] = { FNATIC_STREAK_CMD_REACTIVE_RIPPLE, color_mode, r, g, b, speed, (unsigned char)(keyup ? 0 : 1) }; + SendRequest(prefix, sizeof(prefix), data, sizeof(data)); +} + +void FnaticStreakController::SetRain(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, unsigned char direction) +{ + /*-----------------------------------------------------*\ + | Rain effect - cmd 0x0B | + | Data: [mode, r, g, b, speed, direction] | + | Note: Does not support rainbow mode | + \*-----------------------------------------------------*/ + unsigned char prefix[] = { 0x05, profile, 0x02 }; + unsigned char mode = (color_mode == FNATIC_STREAK_COLOR_MODE_RAINBOW) ? FNATIC_STREAK_COLOR_MODE_RANDOM : color_mode; + unsigned char data[] = { FNATIC_STREAK_CMD_RAIN, mode, r, g, b, speed, direction }; + SendRequest(prefix, sizeof(prefix), data, sizeof(data)); +} + +void FnaticStreakController::SetGradient(unsigned char colors[][3], unsigned char positions[], unsigned int count) +{ + /*-----------------------------------------------------*\ + | Gradient effect - cmd 0x0C | + | Data: [count, {r, g, b, pos} * 10] | + | (always sends 10 color slots, unused are zeroed) | + \*-----------------------------------------------------*/ + unsigned char prefix[] = { 0x05, profile, 0x02 }; + unsigned char data[42]; + memset(data, 0x00, sizeof(data)); + + data[0] = FNATIC_STREAK_CMD_GRADIENT; + data[1] = (unsigned char)count; + + for(unsigned int i = 0; i < count && i < 10; i++) + { + data[2 + i * 4 + 0] = colors[i][0]; + data[2 + i * 4 + 1] = colors[i][1]; + data[2 + i * 4 + 2] = colors[i][2]; + data[2 + i * 4 + 3] = positions[i]; + } + + SendRequest(prefix, sizeof(prefix), data, sizeof(data)); +} + +void FnaticStreakController::SetFade(unsigned char color_mode, unsigned char colors[][3], unsigned char positions[], unsigned int count, unsigned char speed) +{ + /*-----------------------------------------------------*\ + | Fade effect - cmd 0x0D | + | Data: [mode, count, {r, g, b, pos} * 10, speed] | + \*-----------------------------------------------------*/ + unsigned char prefix[] = { 0x05, profile, 0x02 }; + unsigned char data[44]; + memset(data, 0x00, sizeof(data)); + + data[0] = FNATIC_STREAK_CMD_FADE; + data[1] = color_mode; + data[2] = (unsigned char)count; + + for(unsigned int i = 0; i < count && i < 10; i++) + { + data[3 + i * 4 + 0] = colors[i][0]; + data[3 + i * 4 + 1] = colors[i][1]; + data[3 + i * 4 + 2] = colors[i][2]; + data[3 + i * 4 + 3] = positions[i]; + } + + data[43] = speed; + + SendRequest(prefix, sizeof(prefix), data, sizeof(data)); +} diff --git a/Controllers/FnaticStreakController/FnaticStreakController.h b/Controllers/FnaticStreakController/FnaticStreakController.h new file mode 100644 index 0000000..589cd4b --- /dev/null +++ b/Controllers/FnaticStreakController/FnaticStreakController.h @@ -0,0 +1,109 @@ +/*---------------------------------------------------------*\ +| FnaticStreakController.h | +| | +| Driver for Fnatic Streak and miniStreak keyboards | +| | +| Based on leddy project by Hanna Czenczek | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*-----------------------------------------------------*\ +| Fnatic Streak keyboard layout variants | +\*-----------------------------------------------------*/ +#define FNATIC_STREAK_VARIANT_ISO 0x00 +#define FNATIC_STREAK_VARIANT_ANSI 0x01 + +/*-----------------------------------------------------*\ +| Fnatic Streak effect command bytes | +\*-----------------------------------------------------*/ +#define FNATIC_STREAK_CMD_PULSE 0x06 +#define FNATIC_STREAK_CMD_WAVE 0x07 +#define FNATIC_STREAK_CMD_REACTIVE 0x09 +#define FNATIC_STREAK_CMD_REACTIVE_RIPPLE 0x0A +#define FNATIC_STREAK_CMD_RAIN 0x0B +#define FNATIC_STREAK_CMD_GRADIENT 0x0C +#define FNATIC_STREAK_CMD_FADE 0x0D + +/*-----------------------------------------------------*\ +| Fnatic Streak color modes | +\*-----------------------------------------------------*/ +#define FNATIC_STREAK_COLOR_MODE_SINGLE 0x00 +#define FNATIC_STREAK_COLOR_MODE_RAINBOW 0x01 +#define FNATIC_STREAK_COLOR_MODE_RANDOM 0x02 +#define FNATIC_STREAK_COLOR_MODE_GRADIENT 0x03 + +/*-----------------------------------------------------*\ +| Fnatic Streak directions | +\*-----------------------------------------------------*/ +#define FNATIC_STREAK_DIRECTION_RIGHT 0x01 +#define FNATIC_STREAK_DIRECTION_LEFT 0x02 +#define FNATIC_STREAK_DIRECTION_DOWN 0x03 +#define FNATIC_STREAK_DIRECTION_UP 0x04 + +enum FnaticStreakType +{ + FNATIC_STREAK_TYPE_FULL, + FNATIC_STREAK_TYPE_MINI, + FNATIC_STREAK_TYPE_65 +}; + +class FnaticStreakController +{ +public: + FnaticStreakController(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name, FnaticStreakType kb_type); + ~FnaticStreakController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + std::string GetFirmwareVersion(); + FnaticStreakType GetKeyboardType(); + unsigned int GetLEDCount(); + + void SetProfile(unsigned char profile); + void SetLEDsDirect(std::vector leds, std::vector colors, unsigned int brightness); + void SendRGBToDevice(); + + /*-----------------------------------------------------*\ + | Hardware effect methods | + \*-----------------------------------------------------*/ + void SetPulse(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed); + void SetWave(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, unsigned char direction); + void SetReactive(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, bool keyup); + void SetReactiveRipple(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, bool keyup); + void SetRain(unsigned char color_mode, unsigned char r, unsigned char g, unsigned char b, unsigned char speed, unsigned char direction); + void SetGradient(unsigned char colors[][3], unsigned char positions[], unsigned int count); + void SetFade(unsigned char color_mode, unsigned char colors[][3], unsigned char positions[], unsigned int count, unsigned char speed); + + void SoftwareEffectStart(); + void SoftwareEffectEnd(); + void SendKeepalive(); + +private: + void SendRequest(const unsigned char* prefix, size_t prefix_len, const unsigned char* raw_data, size_t data_len); + void RefreshProfile(); + + hid_device* dev; + std::string location; + std::string firmware_version; + std::string name; + FnaticStreakType keyboard_type; + unsigned char profile; + bool software_effect_mode; + + /*-----------------------------------------------------*\ + | Buffer for LED colors | + | Full: 124 LEDs * 3 (RGB) = 372 bytes | + | Mini: 106 LEDs * 3 (RGB) = 318 bytes | + | Using max size to support both | + \*-----------------------------------------------------*/ + unsigned char color_buf[372]; +}; diff --git a/Controllers/FnaticStreakController/FnaticStreakControllerDetect.cpp b/Controllers/FnaticStreakController/FnaticStreakControllerDetect.cpp new file mode 100644 index 0000000..56bbb43 --- /dev/null +++ b/Controllers/FnaticStreakController/FnaticStreakControllerDetect.cpp @@ -0,0 +1,61 @@ +/*---------------------------------------------------------*\ +| FnaticStreakControllerDetect.cpp | +| | +| Detector for Fnatic Streak and miniStreak keyboards | +| | +| Based on leddy project by Hanna Czenczek | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "FnaticStreakController.h" +#include "RGBController_FnaticStreak.h" + +/*-----------------------------------------------------*\ +| Fnatic keyboard vendor and product IDs | +| Based on leddy project keyboard.rs | +| VID: 0x2f0e | +| PID: 0x0101 (Streak full) | +| PID: 0x0102 (miniStreak) | +| Interface: 1 | +\*-----------------------------------------------------*/ +#define FNATIC_VID 0x2F0E + +#define FNATIC_STREAK_PID 0x0101 +#define FNATIC_MINISTREAK_PID 0x0102 +#define FNATIC_STREAK65_PID 0x0105 + +void DetectFnaticStreakKeyboard(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + FnaticStreakType kb_type; + + if(info->product_id == FNATIC_MINISTREAK_PID) + { + kb_type = FNATIC_STREAK_TYPE_MINI; + } + else if(info->product_id == FNATIC_STREAK65_PID) + { + kb_type = FNATIC_STREAK_TYPE_65; + } + else + { + kb_type = FNATIC_STREAK_TYPE_FULL; + } + + FnaticStreakController* controller = new FnaticStreakController(dev, info, name, kb_type); + RGBController_FnaticStreak* rgb_controller = new RGBController_FnaticStreak(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_I("Fnatic Streak", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_STREAK_PID, 1); +REGISTER_HID_DETECTOR_I("Fnatic miniStreak", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_MINISTREAK_PID, 1); +REGISTER_HID_DETECTOR_I("Fnatic Streak65", DetectFnaticStreakKeyboard, FNATIC_VID, FNATIC_STREAK65_PID, 1); diff --git a/Controllers/FnaticStreakController/RGBController_FnaticStreak.cpp b/Controllers/FnaticStreakController/RGBController_FnaticStreak.cpp new file mode 100644 index 0000000..826141c --- /dev/null +++ b/Controllers/FnaticStreakController/RGBController_FnaticStreak.cpp @@ -0,0 +1,719 @@ +/*---------------------------------------------------------*\ +| RGBController_FnaticStreak.cpp | +| | +| RGBController for Fnatic Streak and miniStreak keyboard | +| | +| Based on leddy project by Hanna Czenczek | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_FnaticStreak.h" +#include "RGBControllerKeyNames.h" + +using namespace std::chrono_literals; + +/*---------------------------------------------------------------------*\ +| 0xFFFFFFFF indicates an unused entry in matrix | +\*---------------------------------------------------------------------*/ +#define NA 0xFFFFFFFF + +/*---------------------------------------------------------------------*\ +| Fnatic Streak Full Size Matrix Map (6 rows x 22 columns) | +| Based on leddy project keyboard.rs ledmap | +| Values are LED indices that map to the physical LED positions | +\*---------------------------------------------------------------------*/ +static unsigned int matrix_map_full[6][22] = +{ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 */ + /* ESC FNLK F1 F2 F3 F4 F5 F6 F7 F8 SIG F9 F10 F11 F12 PRSC SCLK PAUS MMIC GAME MSPK VOL */ + { 1, 0, 7, 13, 19, 25, 31, 37, 43, 49, 120, 55, 67, 73, 79, 90, 93, 98, 91, 97, 92, 118 }, + /* BKT 1 2 3 4 5 6 7 8 9 0 - = NA BSP INS HOME PGUP NLCK NP/ NP* NP- */ + { 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 61, 62, 68, NA, 80, 89, 94, 99, 100, 108, 109, 116 }, + /* TAB Q W E R T Y U I O P [ ] NA BS DEL END PGDN NP7 NP8 NP9 NP+ */ + { 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, NA, 81, 88, 95, 96, 101, 107, 110, 115 }, + /* CAPS NA A S D F G H J K L ; ' # ENT NA NA NA NP4 NP5 NP6 NA */ + { 4, NA, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70, 76, 82, NA, NA, NA, 102, 106, 111, NA }, + /* LSFT ISO\ Z X C V B N M , . / NA RSFT NA NA UP NA NP1 NP2 NP3 NPEN */ + { 5, 11, 17, 23, 29, 35, 41, 47, 53, 59, 65, 66, NA, 77, NA, NA, 87, NA, 103, 105, 112, 114 }, + /* LCTL LWIN LALT NA NA NA SPC NA NA NA RALT FN NA MENU RCTL LEFT DOWN RGHT NP0 NA NP. NA */ + { 6, 12, 18, NA, NA, NA, 36, NA, NA, NA, 60, 72, NA, 78, 83, 84, 85, 86, 104, NA, 113, NA } +}; + +/*---------------------------------------------------------------------*\ +| Fnatic miniStreak TKL Matrix Map (6 rows x 18 columns) | +| Based on leddy project keyboard.rs ledmap | +\*---------------------------------------------------------------------*/ +static unsigned int matrix_map_mini[6][18] = +{ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 */ + /* ESC FNLK F1 F2 F3 F4 F5 F6 F7 F8 SIG F9 F10 F11 F12 PRSC SCLK PAUS */ + { 1, 0, 7, 13, 19, 25, 31, 37, 43, 49, 103, 55, 67, 73, 79, 90, 93, 98 }, + /* BKT 1 2 3 4 5 6 7 8 9 0 - = NA BSP INS HOME PGUP */ + { 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 61, 62, 68, NA, 80, 89, 94, 99 }, + /* TAB Q W E R T Y U I O P [ ] NA BS DEL END PGDN */ + { 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, NA, 81, 88, 95, 96 }, + /* CAPS NA A S D F G H J K L ; ' # ENT NA NA NA */ + { 4, NA, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70, 76, 82, NA, NA, NA }, + /* LSFT ISO\ Z X C V B N M , . / NA RSFT NA NA UP NA */ + { 5, 11, 17, 23, 29, 35, 41, 47, 53, 59, 65, 66, NA, 77, NA, NA, 87, NA }, + /* LCTL LWIN LALT NA NA NA SPC NA NA NA RALT FN NA MENU RCTL LEFT DOWN RGHT */ + { 6, 12, 18, NA, NA, NA, 36, NA, NA, NA, 60, 72, NA, 78, 83, 84, 85, 86 } +}; + +/*---------------------------------------------------------------------*\ +| LED Names - Full keyboard (124 LEDs, indices 0-123) | +| Names are in LED index order based on leddy keyboard.rs constants | +\*---------------------------------------------------------------------*/ +static const char* led_names_full[] = +{ + /* 0 */ "Key: Fn Lock", + /* 1 */ KEY_EN_ESCAPE, + /* 2 */ KEY_EN_BACK_TICK, + /* 3 */ KEY_EN_TAB, + /* 4 */ KEY_EN_CAPS_LOCK, + /* 5 */ KEY_EN_LEFT_SHIFT, + /* 6 */ KEY_EN_LEFT_CONTROL, + /* 7 */ KEY_EN_F1, + /* 8 */ KEY_EN_1, + /* 9 */ KEY_EN_Q, + /* 10 */ KEY_EN_A, + /* 11 */ KEY_EN_ISO_BACK_SLASH, + /* 12 */ KEY_EN_LEFT_WINDOWS, + /* 13 */ KEY_EN_F2, + /* 14 */ KEY_EN_2, + /* 15 */ KEY_EN_W, + /* 16 */ KEY_EN_S, + /* 17 */ KEY_EN_Z, + /* 18 */ KEY_EN_LEFT_ALT, + /* 19 */ KEY_EN_F3, + /* 20 */ KEY_EN_3, + /* 21 */ KEY_EN_E, + /* 22 */ KEY_EN_D, + /* 23 */ KEY_EN_X, + /* 24 */ KEY_EN_UNUSED, + /* 25 */ KEY_EN_F4, + /* 26 */ KEY_EN_4, + /* 27 */ KEY_EN_R, + /* 28 */ KEY_EN_F, + /* 29 */ KEY_EN_C, + /* 30 */ KEY_EN_UNUSED, + /* 31 */ KEY_EN_F5, + /* 32 */ KEY_EN_5, + /* 33 */ KEY_EN_T, + /* 34 */ KEY_EN_G, + /* 35 */ KEY_EN_V, + /* 36 */ KEY_EN_SPACE, + /* 37 */ KEY_EN_F6, + /* 38 */ KEY_EN_6, + /* 39 */ KEY_EN_Y, + /* 40 */ KEY_EN_H, + /* 41 */ KEY_EN_B, + /* 42 */ KEY_EN_UNUSED, + /* 43 */ KEY_EN_F7, + /* 44 */ KEY_EN_7, + /* 45 */ KEY_EN_U, + /* 46 */ KEY_EN_J, + /* 47 */ KEY_EN_N, + /* 48 */ KEY_EN_UNUSED, + /* 49 */ KEY_EN_F8, + /* 50 */ KEY_EN_8, + /* 51 */ KEY_EN_I, + /* 52 */ KEY_EN_K, + /* 53 */ KEY_EN_M, + /* 54 */ KEY_EN_UNUSED, + /* 55 */ KEY_EN_F9, + /* 56 */ KEY_EN_9, + /* 57 */ KEY_EN_O, + /* 58 */ KEY_EN_L, + /* 59 */ KEY_EN_COMMA, + /* 60 */ KEY_EN_RIGHT_ALT, + /* 61 */ KEY_EN_0, + /* 62 */ KEY_EN_MINUS, + /* 63 */ KEY_EN_P, + /* 64 */ KEY_EN_SEMICOLON, + /* 65 */ KEY_EN_PERIOD, + /* 66 */ KEY_EN_FORWARD_SLASH, + /* 67 */ KEY_EN_F10, + /* 68 */ KEY_EN_EQUALS, + /* 69 */ KEY_EN_LEFT_BRACKET, + /* 70 */ KEY_EN_QUOTE, + /* 71 */ KEY_EN_UNUSED, + /* 72 */ KEY_EN_RIGHT_FUNCTION, + /* 73 */ KEY_EN_F11, + /* 74 */ KEY_EN_UNUSED, + /* 75 */ KEY_EN_RIGHT_BRACKET, + /* 76 */ KEY_EN_POUND, + /* 77 */ KEY_EN_RIGHT_SHIFT, + /* 78 */ KEY_EN_MENU, + /* 79 */ KEY_EN_F12, + /* 80 */ KEY_EN_BACKSPACE, + /* 81 */ KEY_EN_ANSI_BACK_SLASH, + /* 82 */ KEY_EN_ANSI_ENTER, + /* 83 */ KEY_EN_RIGHT_CONTROL, + /* 84 */ KEY_EN_LEFT_ARROW, + /* 85 */ KEY_EN_DOWN_ARROW, + /* 86 */ KEY_EN_RIGHT_ARROW, + /* 87 */ KEY_EN_UP_ARROW, + /* 88 */ KEY_EN_DELETE, + /* 89 */ KEY_EN_INSERT, + /* 90 */ KEY_EN_PRINT_SCREEN, + /* 91 */ "Key: Mute Mic", + /* 92 */ "Key: Mute Speaker", + /* 93 */ KEY_EN_SCROLL_LOCK, + /* 94 */ KEY_EN_HOME, + /* 95 */ KEY_EN_END, + /* 96 */ KEY_EN_PAGE_DOWN, + /* 97 */ "Key: Gaming Mode", + /* 98 */ KEY_EN_PAUSE_BREAK, + /* 99 */ KEY_EN_PAGE_UP, + /* 100 */ KEY_EN_NUMPAD_LOCK, + /* 101 */ KEY_EN_NUMPAD_7, + /* 102 */ KEY_EN_NUMPAD_4, + /* 103 */ KEY_EN_NUMPAD_1, + /* 104 */ KEY_EN_NUMPAD_0, + /* 105 */ KEY_EN_NUMPAD_2, + /* 106 */ KEY_EN_NUMPAD_5, + /* 107 */ KEY_EN_NUMPAD_8, + /* 108 */ KEY_EN_NUMPAD_DIVIDE, + /* 109 */ KEY_EN_NUMPAD_TIMES, + /* 110 */ KEY_EN_NUMPAD_9, + /* 111 */ KEY_EN_NUMPAD_6, + /* 112 */ KEY_EN_NUMPAD_3, + /* 113 */ KEY_EN_NUMPAD_PERIOD, + /* 114 */ KEY_EN_NUMPAD_ENTER, + /* 115 */ KEY_EN_NUMPAD_PLUS, + /* 116 */ KEY_EN_NUMPAD_MINUS, + /* 117 */ KEY_EN_UNUSED, + /* 118 */ "Key: Volume Knob", + /* 119 */ KEY_EN_UNUSED, + /* 120 */ "Key: Signature Plate", + /* 121 */ KEY_EN_UNUSED, + /* 122 */ KEY_EN_UNUSED, + /* 123 */ KEY_EN_UNUSED, +}; + +/*---------------------------------------------------------------------*\ +| LED Names - Mini keyboard (106 LEDs, indices 0-105) | +| Same as full but with different signature plate location | +\*---------------------------------------------------------------------*/ +static const char* led_names_mini[] = +{ + /* 0 */ "Key: Fn Lock", + /* 1 */ KEY_EN_ESCAPE, + /* 2 */ KEY_EN_BACK_TICK, + /* 3 */ KEY_EN_TAB, + /* 4 */ KEY_EN_CAPS_LOCK, + /* 5 */ KEY_EN_LEFT_SHIFT, + /* 6 */ KEY_EN_LEFT_CONTROL, + /* 7 */ KEY_EN_F1, + /* 8 */ KEY_EN_1, + /* 9 */ KEY_EN_Q, + /* 10 */ KEY_EN_A, + /* 11 */ KEY_EN_ISO_BACK_SLASH, + /* 12 */ KEY_EN_LEFT_WINDOWS, + /* 13 */ KEY_EN_F2, + /* 14 */ KEY_EN_2, + /* 15 */ KEY_EN_W, + /* 16 */ KEY_EN_S, + /* 17 */ KEY_EN_Z, + /* 18 */ KEY_EN_LEFT_ALT, + /* 19 */ KEY_EN_F3, + /* 20 */ KEY_EN_3, + /* 21 */ KEY_EN_E, + /* 22 */ KEY_EN_D, + /* 23 */ KEY_EN_X, + /* 24 */ KEY_EN_UNUSED, + /* 25 */ KEY_EN_F4, + /* 26 */ KEY_EN_4, + /* 27 */ KEY_EN_R, + /* 28 */ KEY_EN_F, + /* 29 */ KEY_EN_C, + /* 30 */ KEY_EN_UNUSED, + /* 31 */ KEY_EN_F5, + /* 32 */ KEY_EN_5, + /* 33 */ KEY_EN_T, + /* 34 */ KEY_EN_G, + /* 35 */ KEY_EN_V, + /* 36 */ KEY_EN_SPACE, + /* 37 */ KEY_EN_F6, + /* 38 */ KEY_EN_6, + /* 39 */ KEY_EN_Y, + /* 40 */ KEY_EN_H, + /* 41 */ KEY_EN_B, + /* 42 */ KEY_EN_UNUSED, + /* 43 */ KEY_EN_F7, + /* 44 */ KEY_EN_7, + /* 45 */ KEY_EN_U, + /* 46 */ KEY_EN_J, + /* 47 */ KEY_EN_N, + /* 48 */ KEY_EN_UNUSED, + /* 49 */ KEY_EN_F8, + /* 50 */ KEY_EN_8, + /* 51 */ KEY_EN_I, + /* 52 */ KEY_EN_K, + /* 53 */ KEY_EN_M, + /* 54 */ KEY_EN_UNUSED, + /* 55 */ KEY_EN_F9, + /* 56 */ KEY_EN_9, + /* 57 */ KEY_EN_O, + /* 58 */ KEY_EN_L, + /* 59 */ KEY_EN_COMMA, + /* 60 */ KEY_EN_RIGHT_ALT, + /* 61 */ KEY_EN_0, + /* 62 */ KEY_EN_MINUS, + /* 63 */ KEY_EN_P, + /* 64 */ KEY_EN_SEMICOLON, + /* 65 */ KEY_EN_PERIOD, + /* 66 */ KEY_EN_FORWARD_SLASH, + /* 67 */ KEY_EN_F10, + /* 68 */ KEY_EN_EQUALS, + /* 69 */ KEY_EN_LEFT_BRACKET, + /* 70 */ KEY_EN_QUOTE, + /* 71 */ KEY_EN_UNUSED, + /* 72 */ KEY_EN_RIGHT_FUNCTION, + /* 73 */ KEY_EN_F11, + /* 74 */ KEY_EN_UNUSED, + /* 75 */ KEY_EN_RIGHT_BRACKET, + /* 76 */ KEY_EN_POUND, + /* 77 */ KEY_EN_RIGHT_SHIFT, + /* 78 */ KEY_EN_MENU, + /* 79 */ KEY_EN_F12, + /* 80 */ KEY_EN_BACKSPACE, + /* 81 */ KEY_EN_ANSI_BACK_SLASH, + /* 82 */ KEY_EN_ANSI_ENTER, + /* 83 */ KEY_EN_RIGHT_CONTROL, + /* 84 */ KEY_EN_LEFT_ARROW, + /* 85 */ KEY_EN_DOWN_ARROW, + /* 86 */ KEY_EN_RIGHT_ARROW, + /* 87 */ KEY_EN_UP_ARROW, + /* 88 */ KEY_EN_DELETE, + /* 89 */ KEY_EN_INSERT, + /* 90 */ KEY_EN_PRINT_SCREEN, + /* 91 */ KEY_EN_UNUSED, + /* 92 */ KEY_EN_UNUSED, + /* 93 */ KEY_EN_SCROLL_LOCK, + /* 94 */ KEY_EN_HOME, + /* 95 */ KEY_EN_END, + /* 96 */ KEY_EN_PAGE_DOWN, + /* 97 */ KEY_EN_UNUSED, + /* 98 */ KEY_EN_PAUSE_BREAK, + /* 99 */ KEY_EN_PAGE_UP, + /* 100 */ KEY_EN_UNUSED, + /* 101 */ KEY_EN_UNUSED, + /* 102 */ KEY_EN_UNUSED, + /* 103 */ "Key: Signature Plate", + /* 104 */ KEY_EN_UNUSED, + /* 105 */ KEY_EN_UNUSED, +}; + +/**------------------------------------------------------------------*\ + @name Fnatic Streak + @category Keyboard + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectFnaticStreakKeyboard + @comment The Fnatic Streak and miniStreak are gaming keyboards + with per-key RGB lighting. Supports hardware effects like Wave, + Pulse, Reactive, Rain, Gradient, and Fade. +\*-------------------------------------------------------------------*/ + +RGBController_FnaticStreak::RGBController_FnaticStreak(FnaticStreakController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Fnatic"; + type = DEVICE_TYPE_KEYBOARD; + description = "Fnatic Streak Keyboard"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + /*-----------------------------------------------------*\ + | Direct mode - per-key color control | + \*-----------------------------------------------------*/ + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.brightness = 100; + modes.push_back(Direct); + + /*-----------------------------------------------------*\ + | Pulse mode - breathing/pulsing effect | + \*-----------------------------------------------------*/ + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = FNATIC_STREAK_CMD_PULSE; + Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulse.speed_min = 0; + Pulse.speed_max = 100; + Pulse.speed = 50; + Pulse.brightness_min = 0; + Pulse.brightness_max = 100; + Pulse.brightness = 100; + Pulse.colors_min = 1; + Pulse.colors_max = 1; + Pulse.colors.resize(1); + modes.push_back(Pulse); + + /*-----------------------------------------------------*\ + | Wave mode - wave rolling across keyboard | + \*-----------------------------------------------------*/ + mode Wave; + Wave.name = "Wave"; + Wave.value = FNATIC_STREAK_CMD_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_BRIGHTNESS; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.speed_min = 0; + Wave.speed_max = 100; + Wave.speed = 50; + Wave.brightness_min = 0; + Wave.brightness_max = 100; + Wave.brightness = 100; + Wave.direction = MODE_DIRECTION_RIGHT; + Wave.colors_min = 1; + Wave.colors_max = 1; + Wave.colors.resize(1); + modes.push_back(Wave); + + /*-----------------------------------------------------*\ + | Reactive mode - LED lights on keypress | + \*-----------------------------------------------------*/ + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = FNATIC_STREAK_CMD_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.speed_min = 0; + Reactive.speed_max = 100; + Reactive.speed = 50; + Reactive.brightness_min = 0; + Reactive.brightness_max = 100; + Reactive.brightness = 100; + Reactive.colors_min = 1; + Reactive.colors_max = 1; + Reactive.colors.resize(1); + modes.push_back(Reactive); + + /*-----------------------------------------------------*\ + | Reactive Ripple mode - ripple effect on keypress | + \*-----------------------------------------------------*/ + mode ReactiveRipple; + ReactiveRipple.name = "Reactive Ripple"; + ReactiveRipple.value = FNATIC_STREAK_CMD_REACTIVE_RIPPLE; + ReactiveRipple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + ReactiveRipple.color_mode = MODE_COLORS_MODE_SPECIFIC; + ReactiveRipple.speed_min = 0; + ReactiveRipple.speed_max = 100; + ReactiveRipple.speed = 50; + ReactiveRipple.brightness_min = 0; + ReactiveRipple.brightness_max = 100; + ReactiveRipple.brightness = 100; + ReactiveRipple.colors_min = 1; + ReactiveRipple.colors_max = 1; + ReactiveRipple.colors.resize(1); + modes.push_back(ReactiveRipple); + + /*-----------------------------------------------------*\ + | Rain mode - raindrop effect | + \*-----------------------------------------------------*/ + mode Rain; + Rain.name = "Rain"; + Rain.value = FNATIC_STREAK_CMD_RAIN; + Rain.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_BRIGHTNESS; + Rain.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rain.speed_min = 0; + Rain.speed_max = 100; + Rain.speed = 50; + Rain.brightness_min = 0; + Rain.brightness_max = 100; + Rain.brightness = 100; + Rain.direction = MODE_DIRECTION_DOWN; + Rain.colors_min = 1; + Rain.colors_max = 1; + Rain.colors.resize(1); + modes.push_back(Rain); + + /*-----------------------------------------------------*\ + | Spectrum Cycle (Fade) mode - fade through colors | + \*-----------------------------------------------------*/ + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = FNATIC_STREAK_CMD_FADE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + SpectrumCycle.color_mode = MODE_COLORS_RANDOM; + SpectrumCycle.speed_min = 0; + SpectrumCycle.speed_max = 100; + SpectrumCycle.speed = 50; + SpectrumCycle.brightness_min = 0; + SpectrumCycle.brightness_max = 100; + SpectrumCycle.brightness = 100; + modes.push_back(SpectrumCycle); + + /*-----------------------------------------------------*\ + | Rainbow Gradient mode - static rainbow gradient | + \*-----------------------------------------------------*/ + mode RainbowGradient; + RainbowGradient.name = "Rainbow Gradient"; + RainbowGradient.value = FNATIC_STREAK_CMD_GRADIENT; + RainbowGradient.flags = MODE_FLAG_HAS_BRIGHTNESS; + RainbowGradient.color_mode = MODE_COLORS_NONE; + RainbowGradient.brightness_min = 0; + RainbowGradient.brightness_max = 100; + RainbowGradient.brightness = 100; + modes.push_back(RainbowGradient); + + SetupZones(); + + /*-----------------------------------------------------*\ + | Initialize last_update_time to now | + \*-----------------------------------------------------*/ + last_update_time = std::chrono::steady_clock::now(); + + /*-----------------------------------------------------*\ + | The Fnatic Streak requires periodic packets to | + | maintain direct control. Start a keepalive thread. | + \*-----------------------------------------------------*/ + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RGBController_FnaticStreak::KeepaliveThread, this); +} + +RGBController_FnaticStreak::~RGBController_FnaticStreak() +{ + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_FnaticStreak::SetupZones() +{ + bool is_mini = (controller->GetKeyboardType() == FNATIC_STREAK_TYPE_MINI); + unsigned int total_led_count = is_mini ? 106 : 124; + unsigned int matrix_cols = is_mini ? 18 : 22; + unsigned int* matrix_map_ptr = is_mini ? (unsigned int*)matrix_map_mini : (unsigned int*)matrix_map_full; + const char** led_name_ptr = is_mini ? led_names_mini : led_names_full; + + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = ZONE_EN_KEYBOARD; + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = total_led_count; + new_zone.leds_max = total_led_count; + new_zone.leds_count = total_led_count; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = matrix_cols; + new_zone.matrix_map->map = matrix_map_ptr; + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_name_ptr[led_idx]; + new_led.value = led_idx; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_FnaticStreak::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_FnaticStreak::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + controller->SoftwareEffectStart(); + + /*-----------------------------------------------------*\ + | Apply brightness scaling to colors | + \*-----------------------------------------------------*/ + unsigned int brightness = modes[active_mode].brightness; + controller->SetLEDsDirect(leds, colors, brightness); + controller->SendRGBToDevice(); +} + +void RGBController_FnaticStreak::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_FnaticStreak::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_FnaticStreak::DeviceUpdateMode() +{ + unsigned char r = 0; + unsigned char g = 0; + unsigned char b = 0; + unsigned char color_mode = FNATIC_STREAK_COLOR_MODE_RAINBOW; + unsigned char speed = (unsigned char)modes[active_mode].speed; + unsigned char direction = FNATIC_STREAK_DIRECTION_RIGHT; + unsigned int brightness = modes[active_mode].brightness; + + /*-----------------------------------------------------*\ + | Get color from mode if available and apply brightness | + \*-----------------------------------------------------*/ + if(modes[active_mode].colors.size() > 0) + { + r = (unsigned char)(RGBGetRValue(modes[active_mode].colors[0]) * brightness / 100); + g = (unsigned char)(RGBGetGValue(modes[active_mode].colors[0]) * brightness / 100); + b = (unsigned char)(RGBGetBValue(modes[active_mode].colors[0]) * brightness / 100); + color_mode = FNATIC_STREAK_COLOR_MODE_SINGLE; + } + + /*-----------------------------------------------------*\ + | Check for random color mode | + \*-----------------------------------------------------*/ + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + color_mode = FNATIC_STREAK_COLOR_MODE_RAINBOW; + } + + /*-----------------------------------------------------*\ + | Convert OpenRGB direction to Fnatic direction | + \*-----------------------------------------------------*/ + switch(modes[active_mode].direction) + { + case MODE_DIRECTION_RIGHT: + direction = FNATIC_STREAK_DIRECTION_RIGHT; + break; + case MODE_DIRECTION_LEFT: + direction = FNATIC_STREAK_DIRECTION_LEFT; + break; + case MODE_DIRECTION_DOWN: + direction = FNATIC_STREAK_DIRECTION_DOWN; + break; + case MODE_DIRECTION_UP: + direction = FNATIC_STREAK_DIRECTION_UP; + break; + default: + direction = FNATIC_STREAK_DIRECTION_RIGHT; + break; + } + + /*-----------------------------------------------------*\ + | Send hardware effect command based on mode | + \*-----------------------------------------------------*/ + switch(modes[active_mode].value) + { + case 0xFFFF: + /*-------------------------------------------------*\ + | Direct mode - handled by DeviceUpdateLEDs | + \*-------------------------------------------------*/ + break; + + case FNATIC_STREAK_CMD_PULSE: + controller->SetPulse(color_mode, r, g, b, speed); + break; + + case FNATIC_STREAK_CMD_WAVE: + controller->SetWave(color_mode, r, g, b, speed, direction); + break; + + case FNATIC_STREAK_CMD_REACTIVE: + controller->SetReactive(color_mode, r, g, b, speed, false); + break; + + case FNATIC_STREAK_CMD_REACTIVE_RIPPLE: + controller->SetReactiveRipple(color_mode, r, g, b, speed, false); + break; + + case FNATIC_STREAK_CMD_RAIN: + controller->SetRain(color_mode, r, g, b, speed, direction); + break; + + case FNATIC_STREAK_CMD_GRADIENT: + { + /*---------------------------------------------*\ + | Send rainbow gradient with brightness scaling | + \*---------------------------------------------*/ + unsigned char gradient_colors[6][3] = + { + { (unsigned char)(0xFF * brightness / 100), 0x00, 0x00 }, + { (unsigned char)(0xFF * brightness / 100), (unsigned char)(0xFF * brightness / 100), 0x00 }, + { 0x00, (unsigned char)(0xFF * brightness / 100), 0x00 }, + { 0x00, (unsigned char)(0xFF * brightness / 100), (unsigned char)(0xFF * brightness / 100) }, + { 0x00, 0x00, (unsigned char)(0xFF * brightness / 100) }, + { (unsigned char)(0xFF * brightness / 100), 0x00, (unsigned char)(0xFF * brightness / 100) }, + }; + unsigned char gradient_positions[6] = { 0, 20, 40, 60, 80, 100 }; + controller->SetGradient(gradient_colors, gradient_positions, 6); + } + break; + + case FNATIC_STREAK_CMD_FADE: + { + /*---------------------------------------------*\ + | Send rainbow fade with brightness scaling | + \*---------------------------------------------*/ + unsigned char fade_colors[6][3] = + { + { (unsigned char)(0xFF * brightness / 100), 0x00, 0x00 }, + { (unsigned char)(0xFF * brightness / 100), (unsigned char)(0xFF * brightness / 100), 0x00 }, + { 0x00, (unsigned char)(0xFF * brightness / 100), 0x00 }, + { 0x00, (unsigned char)(0xFF * brightness / 100), (unsigned char)(0xFF * brightness / 100) }, + { 0x00, 0x00, (unsigned char)(0xFF * brightness / 100) }, + { (unsigned char)(0xFF * brightness / 100), 0x00, (unsigned char)(0xFF * brightness / 100) }, + }; + unsigned char fade_positions[6] = { 0, 20, 40, 60, 80, 100 }; + controller->SetFade(FNATIC_STREAK_COLOR_MODE_RAINBOW, fade_colors, fade_positions, 6, speed); + } + break; + + default: + break; + } +} + +void RGBController_FnaticStreak::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + /*-------------------------------------------------*\ + | In Direct mode, send keepalive to prevent | + | keyboard from reverting to profile effect | + \*-------------------------------------------------*/ + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(500)) + { + controller->SendKeepalive(); + } + } + std::this_thread::sleep_for(100ms); + } +} diff --git a/Controllers/FnaticStreakController/RGBController_FnaticStreak.h b/Controllers/FnaticStreakController/RGBController_FnaticStreak.h new file mode 100644 index 0000000..aa0aff6 --- /dev/null +++ b/Controllers/FnaticStreakController/RGBController_FnaticStreak.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_FnaticStreak.h | +| | +| RGBController for Fnatic Streak and miniStreak keyboard | +| | +| Based on leddy project by Hanna Czenczek | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "FnaticStreakController.h" + +class RGBController_FnaticStreak : public RGBController +{ +public: + RGBController_FnaticStreak(FnaticStreakController* controller_ptr); + ~RGBController_FnaticStreak(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + FnaticStreakController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/GaiZongGaiKeyboardController/GaiZhongGaiController.cpp b/Controllers/GaiZongGaiKeyboardController/GaiZhongGaiController.cpp new file mode 100644 index 0000000..c361415 --- /dev/null +++ b/Controllers/GaiZongGaiKeyboardController/GaiZhongGaiController.cpp @@ -0,0 +1,285 @@ +/*---------------------------------------------------------*\ +| GaiZhongGaiController.cpp | +| | +| Driver for GaiZhongGai keyboard | +| | +| An Yang 24 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "GaiZhongGaiController.h" +#include "StringUtils.h" + +/*---------------------------------------------------------------*\ +| https://oshwlab.com/yangdsada/GaiZhongGai-Keyboard-68-4PRO | +| https://oshwhub.com/myng/42-jian-pan | +| https://oshwhub.com/hivisme/17jian-shuo-zi-xiao-jian-pan | +| https://oshwhub.com/yangzen/xing-huo-2-qi-guang-ban-qu-dong- | +| https://oshwhub.com/morempty/CH552gyin-liang-xuan-niu | +\*---------------------------------------------------------------*/ + +GaiZhongGaiKeyboardController::GaiZhongGaiKeyboardController(hid_device* dev_handle, hid_device_info* info, std::string dev_name) +{ + dev = dev_handle; + location = info->path; + name = dev_name; + usb_pid = info->product_id; + /*-----------------------------------------------------*\ + | Obtaining the Firmware Version | + \*-----------------------------------------------------*/ + char str[10]; + snprintf(str, 10, "Ver%04X", info->release_number); + version = str; + /*-----------------------------------------------------*\ + | Gets the light board connection shape | + \*-----------------------------------------------------*/ + if( usb_pid == GAIZHONGGAI_LIGHT_BOARD_PID ) + { + uint8_t usb_write_buf[65]; + uint8_t usb_read_buf[65]; + memset(usb_write_buf, 0x00, sizeof(usb_write_buf) ); + memset(usb_read_buf , 0x00, sizeof(usb_read_buf) ); + + usb_write_buf[1] = 0x85; + usb_write_buf[2] = 0x00; + usb_write_buf[3] = 60; + + hid_write(dev, usb_write_buf, 65); + hid_read (dev, usb_read_buf , 65); + memcpy(data_flash, usb_read_buf + 3, 60); + + usb_write_buf[2] = 60; + hid_write(dev, usb_write_buf, 65); + hid_read (dev, usb_read_buf , 65); + memcpy(data_flash + 60, usb_read_buf + 3, 60); + + usb_write_buf[2] = 120; + usb_write_buf[3] = 8; + hid_write(dev, usb_write_buf, 65); + hid_read (dev, usb_read_buf , 65); + memcpy(data_flash + 120, usb_read_buf + 3, 8); + } + + /*-----------------------------------------------------*\ + | Gets the RGB_HUB LED LEN | + \*-----------------------------------------------------*/ + if( usb_pid == GAIZHONGGAI_RGB_HUB_GREEN_PID || + usb_pid == GAIZHONGGAI_RGB_HUB_BLUE_PID + ) + { + uint8_t usb_write_buf[65]; + uint8_t usb_read_buf[65]; + memset(usb_write_buf, 0x00, sizeof(usb_write_buf) ); + memset(usb_read_buf , 0x00, sizeof(usb_read_buf) ); + memset(data_flash , 0x00, sizeof(data_flash) ); + + usb_write_buf[1] = 0x87; //Read length command + hid_write(dev, usb_write_buf, 65); + hid_read (dev, usb_read_buf , 65); + + memcpy(data_flash , usb_read_buf + 2 , 16); + } +} + +uint8_t* GaiZhongGaiKeyboardController::GetDataFlash() +{ + return data_flash; +} + +uint16_t GaiZhongGaiKeyboardController::GetChannelLen(uint8_t ch) +{ + uint8_t offset; + offset = ch * 2; + return (data_flash[offset] << 8) | data_flash[offset + 1]; +} + +void GaiZhongGaiKeyboardController::SetChannelLen(uint8_t ch , uint16_t len) +{ + uint8_t offset; + offset = ch * 2; + + if( usb_pid == GAIZHONGGAI_RGB_HUB_GREEN_PID && ch == 3 && len== 637 ) + { + /*-----------------------------------------------------*\ + | Automatic measurement of quantity | + \*-----------------------------------------------------*/ + uint8_t usb_write_buf[65]; + uint8_t usb_read_buf[65]; + memset(usb_write_buf, 0x00, sizeof(usb_write_buf) ); + memset(usb_read_buf , 0x00, sizeof(usb_read_buf) ); + usb_write_buf[1] = 0x88; + + hid_write(dev, usb_write_buf, 65); + hid_read (dev, usb_read_buf , 65); //Wait about 10ms + + memcpy(data_flash , usb_read_buf + 2 , 16); + } + else if( + usb_pid == GAIZHONGGAI_RGB_HUB_GREEN_PID || + usb_pid == GAIZHONGGAI_RGB_HUB_BLUE_PID + ) + { + uint8_t usb_write_buf[65]; + uint8_t usb_read_buf[65]; + memset(usb_write_buf, 0x00, sizeof(usb_write_buf) ); + memset(usb_read_buf , 0x00, sizeof(usb_read_buf) ); + + usb_write_buf[1] = 0x86; + data_flash[offset] = len >> 8; + data_flash[offset + 1] = len & 0xFF; + memcpy(usb_write_buf + 3 , data_flash , 16); + + hid_write(dev, usb_write_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + memset(usb_write_buf, 0x00, sizeof(usb_write_buf) ); + usb_write_buf[1] = 0x87; //Read length command + + hid_write(dev, usb_write_buf, 65); + hid_read (dev, usb_read_buf , 65); + + memcpy(data_flash , usb_read_buf + 2 , 16); + } +} + +GaiZhongGaiKeyboardController::~GaiZhongGaiKeyboardController() +{ + /*-----------------------------------------------------*\ + | Restore built-in light effect | + \*-----------------------------------------------------*/ + uint8_t usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[1] = 0xFF; + hid_write(dev, usb_buf, 65); + + hid_close(dev); +} + +std::string GaiZhongGaiKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string GaiZhongGaiKeyboardController::GetNameString() +{ + return(name); +} + +std::string GaiZhongGaiKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string GaiZhongGaiKeyboardController::GetVersion() +{ + return(version); +} + +unsigned short GaiZhongGaiKeyboardController::GetUSBPID() +{ + return(usb_pid); +} + +void GaiZhongGaiKeyboardController::SendColors + ( + unsigned char* color_data, + unsigned int color_data_size/*color_data_size*/ + ) +{ + uint8_t usb_buf[65]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + switch(usb_pid) + { + case GAIZHONGGAI_68_PRO_PID: //68% + usb_buf[1] = 0x10; + memcpy(usb_buf + 2, color_data + 0 * 3, 63); + hid_write(dev, usb_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + + usb_buf[1] = 0x11; + memcpy(usb_buf + 2, color_data + 21 * 3, 63); + hid_write(dev, usb_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + + usb_buf[1] = 0x12; + memcpy(usb_buf + 2, color_data + 42 * 3, 63); + hid_write(dev, usb_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + + memset(usb_buf, 0x00, sizeof(usb_buf)); //Redundant data set 0 + usb_buf[1] = 0x13; + memcpy(usb_buf + 2, color_data + 63 * 3, 15); + hid_write(dev, usb_buf, 65); + break; + + case GAIZHONGGAI_42_PRO_PID: //42% + usb_buf[1] = 0x10; + memcpy(usb_buf + 2, color_data + 0 * 3, 63); + hid_write(dev, usb_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + + usb_buf[1] = 0x11; + memcpy(usb_buf + 2, color_data + 21 * 3, 63); + hid_write(dev, usb_buf, 65); + break; + + case GAIZHONGGAI_17_TOUCH_PRO_PID: //17PAD+Touch + case GAIZHONGGAI_20_PRO_PID: //20PAD + usb_buf[1] = 0x10; + memcpy(usb_buf + 2, color_data + 68 * 3, 60); + hid_write(dev, usb_buf, 65); + break; + + case GAIZHONGGAI_17_PRO_PID: //17PAD + usb_buf[1] = 0x10; + memcpy(usb_buf + 2, color_data + 68 * 3, 51); + hid_write(dev, usb_buf, 65); + break; + + case GAIZHONGGAI_DIAL_PID: //Dial + usb_buf[1] = 0x10; + memcpy(usb_buf + 2, color_data + 85 * 3, 63); + hid_write(dev, usb_buf, 65); + break; + + case GAIZHONGGAI_LIGHT_BOARD_PID: //"Cololight" + case GAIZHONGGAI_RGB_HUB_GREEN_PID: //WS2812 controller + case GAIZHONGGAI_RGB_HUB_BLUE_PID: //WS2812 controller + for(uint8_t i = 0; i < 32; i++) //Maximum up to 640 RGB LED + { + if(i > color_data_size / 60) + { + break; + } + + usb_buf[1] = i; + for(uint8_t j = 0; j < 60; j++ ) + { + if((unsigned int)(i * 60 + j) < color_data_size) + { + usb_buf[j + 2] = color_data[i * 60 + j]; + } + else + { + usb_buf[j + 2] = 0; //Redundant data set 0 + } + } + hid_write(dev, usb_buf, 65); + } + break; + } +} diff --git a/Controllers/GaiZongGaiKeyboardController/GaiZhongGaiController.h b/Controllers/GaiZongGaiKeyboardController/GaiZhongGaiController.h new file mode 100644 index 0000000..653bc73 --- /dev/null +++ b/Controllers/GaiZongGaiKeyboardController/GaiZhongGaiController.h @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| GaiZhongGaiController.h | +| | +| Driver for GaiZhongGai keyboard | +| | +| An Yang 24 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*-----------------------------------------------------*\ +| GaiZhongGai vendor ID | +\*-----------------------------------------------------*/ +#define GAIZHONGGAI_VID 0x3061 + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define GAIZHONGGAI_68_PRO_PID 0x4700 +#define GAIZHONGGAI_42_PRO_PID 0x4701 +#define GAIZHONGGAI_17_TOUCH_PRO_PID 0x4770 +#define GAIZHONGGAI_17_PRO_PID 0x4771 +#define GAIZHONGGAI_20_PRO_PID 0x4772 + +/*-----------------------------------------------------*\ +| Other product IDs | +\*-----------------------------------------------------*/ +#define GAIZHONGGAI_LIGHT_BOARD_PID 0x4710 +#define GAIZHONGGAI_RGB_HUB_GREEN_PID 0x4711 +#define GAIZHONGGAI_RGB_HUB_BLUE_PID 0x4712 +#define GAIZHONGGAI_DIAL_PID 0x4720 + +class GaiZhongGaiKeyboardController +{ +public: + GaiZhongGaiKeyboardController(hid_device* dev_handle, hid_device_info* info, std::string dev_name); + ~GaiZhongGaiKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + std::string GetVersion(); + unsigned short GetUSBPID(); + uint8_t* GetDataFlash(); + uint16_t GetChannelLen(uint8_t ch); + void SetChannelLen(uint8_t ch , uint16_t len); + + void SendColors + ( + unsigned char* color_data, + unsigned int color_data_size + ); + +private: + hid_device* dev; + std::string location; + std::string name; + std::string version; + unsigned short usb_pid; + uint8_t data_flash[128]; +}; diff --git a/Controllers/GaiZongGaiKeyboardController/GaiZhongGaiControllerDetect.cpp b/Controllers/GaiZongGaiKeyboardController/GaiZhongGaiControllerDetect.cpp new file mode 100644 index 0000000..84db3b0 --- /dev/null +++ b/Controllers/GaiZongGaiKeyboardController/GaiZhongGaiControllerDetect.cpp @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| GaiZhongGaiControllerDetect.cpp | +| | +| Detector for GaiZhongGai keyboard | +| | +| An Yang 24 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "GaiZhongGaiController.h" +#include "RGBController_GaiZhongGai.h" + +/******************************************************************************************\ +* * +* DetectGaiZhongGaiKeyboardControllers * +* * +* Tests the USB address to see if a GaiZhongGai RGB Keyboard controller exists there.* +* * +\******************************************************************************************/ + +void DetectGaiZhongGaiKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + GaiZhongGaiKeyboardController* controller = new GaiZhongGaiKeyboardController(dev, info, name); + RGBController_GaiZhongGaiKeyboard* rgb_controller = new RGBController_GaiZhongGaiKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectGaiZhongGaiKeyboardControllers() */ + +REGISTER_HID_DETECTOR_I("GaiZhongGai 68+4 PRO", DetectGaiZhongGaiKeyboardControllers, GAIZHONGGAI_VID, GAIZHONGGAI_68_PRO_PID, 3); +REGISTER_HID_DETECTOR_I("GaiZhongGai 42 PRO", DetectGaiZhongGaiKeyboardControllers, GAIZHONGGAI_VID, GAIZHONGGAI_42_PRO_PID, 3); +REGISTER_HID_DETECTOR_I("GaiZhongGai Dial", DetectGaiZhongGaiKeyboardControllers, GAIZHONGGAI_VID, GAIZHONGGAI_DIAL_PID, 3); +REGISTER_HID_DETECTOR_I("GaiZhongGai LightBoard", DetectGaiZhongGaiKeyboardControllers, GAIZHONGGAI_VID, GAIZHONGGAI_LIGHT_BOARD_PID, 3); +REGISTER_HID_DETECTOR_I("GaiZhongGai RGB HUB Green", DetectGaiZhongGaiKeyboardControllers, GAIZHONGGAI_VID, GAIZHONGGAI_RGB_HUB_GREEN_PID, 3); +REGISTER_HID_DETECTOR_I("GaiZhongGai RGB HUB Blue", DetectGaiZhongGaiKeyboardControllers, GAIZHONGGAI_VID, GAIZHONGGAI_RGB_HUB_BLUE_PID, 3); +REGISTER_HID_DETECTOR_I("GaiZhongGai 17+4+Touch PRO", DetectGaiZhongGaiKeyboardControllers, GAIZHONGGAI_VID, GAIZHONGGAI_17_TOUCH_PRO_PID, 3); +REGISTER_HID_DETECTOR_I("GaiZhongGai 17 PRO", DetectGaiZhongGaiKeyboardControllers, GAIZHONGGAI_VID, GAIZHONGGAI_17_PRO_PID, 3); +REGISTER_HID_DETECTOR_I("GaiZhongGai 20 PRO", DetectGaiZhongGaiKeyboardControllers, GAIZHONGGAI_VID, GAIZHONGGAI_20_PRO_PID, 3); diff --git a/Controllers/GaiZongGaiKeyboardController/RGBController_GaiZhongGai.cpp b/Controllers/GaiZongGaiKeyboardController/RGBController_GaiZhongGai.cpp new file mode 100644 index 0000000..e9be0e5 --- /dev/null +++ b/Controllers/GaiZongGaiKeyboardController/RGBController_GaiZhongGai.cpp @@ -0,0 +1,705 @@ +/*---------------------------------------------------------*\ +| RGBController_GaiZhongGai.cpp | +| | +| RGBController for GaiZhongGai keyboard | +| | +| An Yang 24 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_GaiZhongGai.h" +#include "RGBControllerKeyNames.h" + +using namespace std; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map_68[5][17] = +{ + { 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, NA, 66, 67 }, + { 36, NA, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }, + { 23, NA, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, NA, 35, NA, NA }, + { 10, NA, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, NA, 21, NA, 22, NA }, + { 0, 1, 2, NA, NA, NA, 3, NA, NA, NA, 4, 5, 6, NA, 7, 8, 9 } +}; + +static unsigned int matrix_map_42[4][12] = +{ + { 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30 }, + { 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, NA, 19 }, + { 18, NA, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8 }, + { 7, 6, 5, NA, 4, NA, NA, 3, NA, 2, 1, 0 } +}; + +static unsigned int matrix_map_dial[2][2] = +{ + { 88, 85 }, + { 87, 86 } +}; + +static unsigned int matrix_map_17PAD[5][4] = +{ + { 84, 83, 82, 81 }, + { 80, 79, 78, 77 }, + { 76, 75, 74, NA }, + { 73, 72, 71, 68 }, + { 70, NA, 69, NA } +}; + +static unsigned int matrix_map_20PAD[6][4] = +{ + { 86, 87, 85, NA }, + { 84, 83, 82, 81 }, + { 80, 79, 78, 77 }, + { 76, 75, 74, NA }, + { 73, 72, 71, 68 }, + { 70, NA, 69, NA } +}; + +static unsigned int matrix_map_PAD_Touch[5][5] = +{ + { 84, 83, 82, 81 , NA}, + { 80, 79, 78, 77 , 85}, + { 76, 75, 74, NA , 86}, + { 73, 72, 71, 68 , 87}, + { 70, NA, 69, NA , NA} +}; + +static const char* zone_names[] = +{ + "Keyboard", + "RGB HUB CH1", + "RGB HUB CH2", + "RGB HUB CH3", + "RGB HUB CH4", + "RGB HUB CH5", + "RGB HUB CH6", + "RGB HUB CH7", + "RGB HUB CH8" +}; + +static const char *led_names_general[] = +{ + KEY_EN_LEFT_CONTROL,//0 + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, + KEY_EN_MENU, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + + KEY_EN_LEFT_SHIFT,//10 + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + + KEY_EN_CAPS_LOCK,//23 + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + + KEY_EN_TAB,//36 + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_PAGE_DOWN, + + KEY_EN_ESCAPE,//52 + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_PRINT_SCREEN, + KEY_EN_PAGE_UP, + + KEY_EN_NUMPAD_ENTER,//68 + KEY_EN_NUMPAD_PERIOD, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_PLUS, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_MINUS, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_LOCK, + + "RGB Strip 1", + "RGB Strip 2", + "RGB Strip 3", + "RGB Strip 4", + "RGB Strip 5", + "RGB Strip 6" +}; + +static const char *led_names_42key[] = +{ + KEY_EN_RIGHT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_LEFT_ARROW, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_SPACE, + KEY_EN_LEFT_ALT, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_CONTROL, + + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + KEY_EN_FORWARD_SLASH, + KEY_EN_M, + KEY_EN_N, + KEY_EN_B, + KEY_EN_V, + KEY_EN_C, + KEY_EN_X, + KEY_EN_Z, + KEY_EN_LEFT_SHIFT, + + KEY_EN_ANSI_ENTER, + KEY_EN_L, + KEY_EN_K, + KEY_EN_J, + KEY_EN_H, + KEY_EN_G, + KEY_EN_F, + KEY_EN_D, + KEY_EN_S, + KEY_EN_A, + KEY_EN_CAPS_LOCK, + + KEY_EN_BACKSPACE, + KEY_EN_P, + KEY_EN_O, + KEY_EN_I, + KEY_EN_U, + KEY_EN_Y, + KEY_EN_T, + KEY_EN_R, + KEY_EN_E, + KEY_EN_W, + KEY_EN_Q, + KEY_EN_ESCAPE +}; + +/*---------------------------------------------------------*\ +| Enumerated coordinates | +\*---------------------------------------------------------*/ +static unsigned int matrix_map_light_board[1024]; +void board_led_xy_self_call + ( + uint8_t* p_in, uint8_t* offset, + unsigned int* map, + uint16_t* led_len, + float x, float y, + float distance, float angle + ) +{ + uint8_t i; + uint8_t temp; + uint8_t range_num = 0; //cycle index + float led_distance = 0; //Light to center(cm) + float board_distance = 0; //Edge to center(cm) + float angle_step = 0; //Step radians + float pi = acosf(-1.0f); //PI + float new_x, new_y; //New center point coordinates + float new_angle; + + if (*offset == 120) + return; + + temp = p_in[*offset]; + switch(temp >> 5) + { + case 0x07://END + return; + + case 0x01://regular hexagon + led_distance = 3.0f; + board_distance = 4.33f; + range_num = 12; + angle_step = pi / 6.0f; + break; + + case 0x00://transferred meaning + switch(temp >> 3) + { + case 0x01://square + led_distance = 3.54f; + board_distance = 5.0f; + range_num = 8; + angle_step = pi / 4.0f; + break; + case 0x00://transferred meaning + if(temp >> 2 == 1)//regular triangle + { + led_distance = 2.8f; + board_distance = 2.9f; + range_num = 6; + angle_step = pi / 3.0f; + } + break; + } + break; + } + //New center point coordinates + new_x = x + cosf(angle) * (distance + board_distance); + new_y = y + sinf(angle) * (distance + board_distance); + + //Rotate 180 degrees + if(angle > pi) + { + new_angle = angle - pi; + } + else + { + new_angle = angle + pi; + } + + for(i = 1; i < range_num; i++) + { + new_angle -= angle_step;//clockwise + if(i & 1)//Is led + { + uint8_t x_u8 = (int16_t)(((new_x + cos(new_angle) * led_distance) * + p_in[120] * 0.01f + 0.5f) / 1) - + *(int8_t*)&p_in[122]; + + uint8_t y_u8 = (int16_t)(((new_y + sin(new_angle) * led_distance) * + p_in[120] * 0.01f + 0.5f) / -1) - + *(int8_t*)&p_in[123]; + + map[y_u8*p_in[124]+x_u8] = *led_len; + + (*led_len)++; + } + else//Is COM + { + if(temp & (1 << (i / 2 - 1)))//child node + { + (*offset)++; + board_led_xy_self_call( + p_in, offset, + map, led_len, + new_x, new_y, + board_distance, new_angle + ); + } + } + } +} + +uint16_t LightBoard_init(uint8_t* p_in) +{ + for(uint16_t i = 0 ;i < 1024; i++) + { + matrix_map_light_board[i] = NA; + } + + float angle; + uint16_t led_len = 0; + uint8_t offset = 0; + + angle = ((21 - p_in[121]) % 12) * acosf(-1.0f) / 6; + + board_led_xy_self_call(p_in, &offset,matrix_map_light_board , &led_len, 0, 0, 0, angle); + + return led_len; +} + +/**------------------------------------------------------------------*\ + @name GaiZhongGai Keyboard/Controller + @category Keyboard/Controller + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectGaiZhongGaiKeyboardControllers + @comment + Open source web : https://oshwhub.com/yangzen/zui-gai68- + + | function | command code | format | format | format | format | + | ----------------------------- | ----------------- | -------- | -------- | -------- | ----------------------------------------- | + | | [0] | [1] | [2] | [3] | [4] | + | ----------------------------- | ----------------- | -------- | -------- | -------- | ----------------------------------------- | + | Restore offline effects | 0xFF | undefined | undefined | undefined | undefined | + | Set color | 0x10 | LED0_G | LED0_R | LED0_B | LED1_G(And so on to LED20) | + | Set color | 0x11 | LED21_G | LED21_R | LED21_B | LED22_G(The sequence is WS2812 network) | + | Get color | 0x20 | LED0_G | LED0_R | LED0_B | LED1_G(And so on to LED20) | + | Get color | 0x21 | LED21_G | LED21_R | LED21_B | LED22_G(The sequence is WS2812 network) | + + Note: Get color only sends command code, and the keyboard returns LED color data +\*-------------------------------------------------------------------*/ + +RGBController_GaiZhongGaiKeyboard::RGBController_GaiZhongGaiKeyboard(GaiZhongGaiKeyboardController* controller_ptr) +{ + controller = controller_ptr; + name = controller->GetNameString(); + + switch(controller->GetUSBPID()) + { + case GAIZHONGGAI_68_PRO_PID: + type = DEVICE_TYPE_KEYBOARD; + description = "https://oshwhub.com/yangzen/zui-gai68-/"; + break; + + case GAIZHONGGAI_42_PRO_PID: + type = DEVICE_TYPE_KEYBOARD; + description = "https://oshwhub.com/myng/42-jian-pan/"; + break; + + case GAIZHONGGAI_17_TOUCH_PRO_PID: + type = DEVICE_TYPE_KEYPAD; + description = "https://oshwhub.com/yangzen/xing-huo-ji-hua-zui-gai-17-4-chu-mo-ji-xie-jian-pan-pro/"; + break; + + case GAIZHONGGAI_17_PRO_PID: + type = DEVICE_TYPE_KEYPAD; + description = "https://oshwhub.com/hivisme/17jian-shuo-zi-xiao-jian-pan/"; + break; + + case GAIZHONGGAI_20_PRO_PID: + type = DEVICE_TYPE_KEYPAD; + description = "https://oshwhub.com/runkuny/19keys_pad_normal/"; + break; + + case GAIZHONGGAI_LIGHT_BOARD_PID: + type = DEVICE_TYPE_ACCESSORY; + description = "https://oshwhub.com/yangzen/xing-huo-2-qi-guang-ban-qu-dong-/"; + break; + + case GAIZHONGGAI_RGB_HUB_GREEN_PID: + type = DEVICE_TYPE_LEDSTRIP; + description = "https://oshwhub.com/yangzen/album/gai-zhong-gai-jian-pan-ge-ji/"; + break; + + case GAIZHONGGAI_RGB_HUB_BLUE_PID: + type = DEVICE_TYPE_LEDSTRIP; + description = "https://oshwhub.com/yangzen/album/gai-zhong-gai-jian-pan-ge-ji/"; + break; + + case GAIZHONGGAI_DIAL_PID: + type = DEVICE_TYPE_UNKNOWN; + description = "https://oshwhub.com/morempty/CH552gyin-liang-xuan-niu/"; + break; + } + + vendor = "Yang"; + version = controller->GetVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_GaiZhongGaiKeyboard::~RGBController_GaiZhongGaiKeyboard() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_GaiZhongGaiKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + char str[10]; + unsigned int total_led_count = 0; + unsigned int zone_idx_len = 1; + unsigned int temp; + + leds.clear(); + colors.clear(); + zones.clear(); + + for(unsigned int zone_idx = 0; zone_idx < zone_idx_len; zone_idx++) + { + zone new_zone; + + switch(controller->GetUSBPID()) + { + case GAIZHONGGAI_68_PRO_PID: + { + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = 68; + new_zone.leds_max = 68; + new_zone.leds_count = 68; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->width = 17; + new_zone.matrix_map->height = 5; + new_zone.matrix_map->map = (unsigned int *)&matrix_map_68; + new_zone.name = zone_names[zone_idx]; + } + break; + + case GAIZHONGGAI_42_PRO_PID: + { + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = 42; + new_zone.leds_max = 42; + new_zone.leds_count = 42; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->width = 12; + new_zone.matrix_map->height = 4; + new_zone.matrix_map->map = (unsigned int *)&matrix_map_42; + new_zone.name = zone_names[zone_idx]; + } + break; + + case GAIZHONGGAI_17_TOUCH_PRO_PID: + { + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = 88; + new_zone.leds_max = 88; + new_zone.leds_count = 88; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->width = 5; + new_zone.matrix_map->height = 5; + new_zone.matrix_map->map = (unsigned int *)&matrix_map_PAD_Touch; + new_zone.name = zone_names[zone_idx]; + } + break; + + case GAIZHONGGAI_17_PRO_PID: + { + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = 85; + new_zone.leds_max = 85; + new_zone.leds_count = 85; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->width = 4; + new_zone.matrix_map->height = 5; + new_zone.matrix_map->map = (unsigned int *)&matrix_map_17PAD; + new_zone.name = zone_names[zone_idx]; + } + break; + + case GAIZHONGGAI_20_PRO_PID: + { + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = 88; + new_zone.leds_max = 88; + new_zone.leds_count = 88; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->width = 4; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->map = (unsigned int *)&matrix_map_20PAD; + new_zone.name = zone_names[zone_idx]; + } + break; + + case GAIZHONGGAI_DIAL_PID: + { + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = 89; + new_zone.leds_max = 89; + new_zone.leds_count = 89; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->width = 2; + new_zone.matrix_map->height = 2; + new_zone.matrix_map->map = (unsigned int *)&matrix_map_dial; + new_zone.name = zone_names[zone_idx]; + } + break; + + case GAIZHONGGAI_LIGHT_BOARD_PID: + { + temp = LightBoard_init(controller->GetDataFlash());//get led_len + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = temp; + new_zone.leds_max = temp; + new_zone.leds_count = temp; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->width = controller->GetDataFlash()[124]; + new_zone.matrix_map->height = controller->GetDataFlash()[125]; + new_zone.matrix_map->map = (unsigned int *)&matrix_map_light_board; + new_zone.name = zone_names[zone_idx + 1]; + } + break; + + case GAIZHONGGAI_RGB_HUB_GREEN_PID: + { + zone_idx_len = 4; + temp = controller->GetChannelLen(zone_idx);//get led_len + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 1; + new_zone.leds_max = 637; + new_zone.leds_count = temp; + new_zone.name = zone_names[zone_idx + 1]; + } + break; + + case GAIZHONGGAI_RGB_HUB_BLUE_PID: + { + zone_idx_len = 8; + temp = controller->GetChannelLen(zone_idx);//get led_len + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 1; + new_zone.leds_max = 633; + new_zone.leds_count = temp; + new_zone.name = zone_names[zone_idx + 1]; + } + break; + } + + zones.push_back(new_zone); + + total_led_count += new_zone.leds_count; + } + + switch(controller->GetUSBPID()) + { + case GAIZHONGGAI_LIGHT_BOARD_PID: + case GAIZHONGGAI_RGB_HUB_GREEN_PID: + case GAIZHONGGAI_RGB_HUB_BLUE_PID: + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + snprintf(str, 10, "RGB_%03d", led_idx + 1); + new_led.name = str; + leds.push_back(new_led); + } + break; + case GAIZHONGGAI_42_PRO_PID: + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names_42key[led_idx]; + leds.push_back(new_led); + } + break; + default: + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names_general[led_idx]; + leds.push_back(new_led); + } + break; + + } + + SetupColors(); +} + +void RGBController_GaiZhongGaiKeyboard::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + switch(controller->GetUSBPID()) + { + case GAIZHONGGAI_RGB_HUB_GREEN_PID: + case GAIZHONGGAI_RGB_HUB_BLUE_PID: + controller->SetChannelLen(zone, new_size); + break; + default: + return; + } + + SetupZones(); + } +} + +void RGBController_GaiZhongGaiKeyboard::DeviceUpdateLEDs() +{ + unsigned char colordata[1024 * 3]; + unsigned int data_size = (unsigned int)colors.size(); + + for(unsigned int color_idx = 0; color_idx < data_size; color_idx++) + { + unsigned int offset = color_idx * 3; + + colordata[offset + 0] = RGBGetGValue(colors[color_idx]); + colordata[offset + 1] = RGBGetRValue(colors[color_idx]); + colordata[offset + 2] = RGBGetBValue(colors[color_idx]); + } + + controller->SendColors(colordata, data_size * 3); +} + +void RGBController_GaiZhongGaiKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GaiZhongGaiKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GaiZhongGaiKeyboard::DeviceUpdateMode() +{ + +} diff --git a/Controllers/GaiZongGaiKeyboardController/RGBController_GaiZhongGai.h b/Controllers/GaiZongGaiKeyboardController/RGBController_GaiZhongGai.h new file mode 100644 index 0000000..c5a4d05 --- /dev/null +++ b/Controllers/GaiZongGaiKeyboardController/RGBController_GaiZhongGai.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_GaiZhongGai.h | +| | +| RGBController for GaiZhongGai keyboard | +| | +| An Yang 24 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once +#include "RGBController.h" +#include "GaiZhongGaiController.h" + +class RGBController_GaiZhongGaiKeyboard : public RGBController +{ +public: + RGBController_GaiZhongGaiKeyboard(GaiZhongGaiKeyboardController* controller_ptr); + ~RGBController_GaiZhongGaiKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + GaiZhongGaiKeyboardController* controller; +}; diff --git a/Controllers/GainwardGPUController/GainwardGPUControllerDetect.cpp b/Controllers/GainwardGPUController/GainwardGPUControllerDetect.cpp new file mode 100644 index 0000000..29a4a40 --- /dev/null +++ b/Controllers/GainwardGPUController/GainwardGPUControllerDetect.cpp @@ -0,0 +1,114 @@ +/*---------------------------------------------------------*\ +| GainwardGPUControllerDetect.cpp | +| | +| Detector for Gainward GPU | +| | +| TheRogueZeta 05 Nov 2020 | +| KundaPanda 04 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "GainwardGPUv1Controller.h" +#include "GainwardGPUv2Controller.h" +#include "RGBController_GainwardGPUv1.h" +#include "RGBController_GainwardGPUv2.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* TestForGainwardGPUController * +* * +* Tests the given address to see if a Gainward GPU controller exists there. * +* * +\******************************************************************************************/ + +bool TestForGainwardGPUController(i2c_smbus_interface* bus, uint8_t i2c_addr) +{ + bool pass = false; + + switch(i2c_addr) + { + /*-----------------------------------------------------------------*\ + | V1 Controller | + \*-----------------------------------------------------------------*/ + case 0x08: + pass = bus->i2c_smbus_write_quick(i2c_addr, I2C_SMBUS_WRITE); + break; + + /*-----------------------------------------------------------------*\ + | V2 Controller | + \*-----------------------------------------------------------------*/ + case 0x49: + /*-------------------------------------------------------------*\ + | This detection might need some modifications | + | Reading 0x6F*0x73 and comparing to 0x64 might be a possibility| + \*-------------------------------------------------------------*/ + s32 data = bus->i2c_smbus_read_byte_data(i2c_addr, 0x0); + s32 mode_data = bus->i2c_smbus_read_byte_data(i2c_addr, 0xe0); + pass = (data == 0x0) && (mode_data < 0x5); + break; + } + + return(pass); + +} /* TestForGainwardGPUController() */ + + +/******************************************************************************************\ +* * +* DetectGainwardGPUControllers * +* * +* Detect Gainward GPU controllers on the enumerated I2C busses. * +* * +\******************************************************************************************/ + +void DetectGainwardGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(TestForGainwardGPUController(bus, i2c_addr)) + { + switch(i2c_addr) + { + /*-----------------------------------------------------------------*\ + | V1 Controller | + \*-----------------------------------------------------------------*/ + case 0x08: + { + GainwardGPUv1Controller* controller = new GainwardGPUv1Controller(bus, i2c_addr, name); + RGBController_GainwardGPUv1* rgb_controller = new RGBController_GainwardGPUv1(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + + /*-----------------------------------------------------------------*\ + | V2 Controller | + \*-----------------------------------------------------------------*/ + case 0x49: + { + GainwardGPUv2Controller* controller = new GainwardGPUv2Controller(bus, i2c_addr, name); + RGBController_GainwardGPUv2* rgb_controller = new RGBController_GainwardGPUv2(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + } + } +} /* DetectGainwardGPUControllers() */ + +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce GTX 1080 Phoenix", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, GAINWARD_SUB_VEN, GAINWARD_GTX_1080_PHOENIX, 0x08); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce GTX 1080 Ti Phoenix", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, GAINWARD_SUB_VEN, GAINWARD_GTX_1080TI_PHOENIX, 0x08); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce GTX 1660 SUPER Ghost", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660S_DEV, NVIDIA_SUB_VEN, NVIDIA_GTX1660S_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 2070 SUPER Phantom", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, GAINWARD_SUB_VEN, NVIDIA_RTX2070S_OC_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 2080 Phoenix GS", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_DEV, GAINWARD_SUB_VEN, NVIDIA_RTX2080_A_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3060 Pegasus 12G", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3060_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3070 Phantom", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, GAINWARD_SUB_VEN, GAINWARD_RTX_3070_PHANTOM, 0x49); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3070 Phoenix", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, GAINWARD_SUB_VEN, NVIDIA_RTX3070_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3070 Ti Phoenix", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, GAINWARD_SUB_VEN, NVIDIA_RTX3070TI_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3080 Phoenix", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, GAINWARD_SUB_VEN, NVIDIA_RTX3080_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3080 Ti Phoenix", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, GAINWARD_SUB_VEN, NVIDIA_RTX3080TI_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3090 Phoenix", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, GAINWARD_SUB_VEN, NVIDIA_RTX3090_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Gainward GeForce RTX 3090 Ti Phantom", DetectGainwardGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, GAINWARD_SUB_VEN, GAINWARD_RTX_3090TI_PHANTOM, 0x49); diff --git a/Controllers/GainwardGPUController/GainwardGPUv1Controller/GainwardGPUv1Controller.cpp b/Controllers/GainwardGPUController/GainwardGPUv1Controller/GainwardGPUv1Controller.cpp new file mode 100644 index 0000000..a4b45cb --- /dev/null +++ b/Controllers/GainwardGPUController/GainwardGPUv1Controller/GainwardGPUv1Controller.cpp @@ -0,0 +1,78 @@ +/*---------------------------------------------------------*\ +| GainwardGPUv1Controller.cpp | +| | +| Driver for Gainward v1 GPU | +| | +| TheRogueZeta 05 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "GainwardGPUv1Controller.h" + +GainwardGPUv1Controller::GainwardGPUv1Controller(i2c_smbus_interface* bus, gainward_gpu_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +GainwardGPUv1Controller::~GainwardGPUv1Controller() +{ + +} + +std::string GainwardGPUv1Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string GainwardGPUv1Controller::GetDeviceName() +{ + return(name); +} + +unsigned char GainwardGPUv1Controller::GetLEDRed() +{ + return(GainwardGPURegisterRead(GAINWARD_RED_REGISTER)); +} + +unsigned char GainwardGPUv1Controller::GetLEDGreen() +{ + return(GainwardGPURegisterRead(GAINWARD_GREEN_REGISTER)); +} + +unsigned char GainwardGPUv1Controller::GetLEDBlue() +{ + return(GainwardGPURegisterRead(GAINWARD_BLUE_REGISTER)); +} + +void GainwardGPUv1Controller::SetLEDColors(unsigned char red, unsigned char green, unsigned char blue) +{ + GainwardGPURegisterWrite(GAINWARD_RED_REGISTER, red); + GainwardGPURegisterWrite(GAINWARD_GREEN_REGISTER, green); + GainwardGPURegisterWrite(GAINWARD_BLUE_REGISTER, blue); + GainwardGPURegisterWrite(GAINWARD_06_REGISTER, 0xFF); +} + +void GainwardGPUv1Controller::SetMode() +{ + +} + +unsigned char GainwardGPUv1Controller::GainwardGPURegisterRead(unsigned char reg) +{ + return(bus->i2c_smbus_read_byte_data(dev, reg)); +} + +void GainwardGPUv1Controller::GainwardGPURegisterWrite(unsigned char reg, unsigned char val) +{ + bus->i2c_smbus_write_byte_data(dev, reg, val); +} diff --git a/Controllers/GainwardGPUController/GainwardGPUv1Controller/GainwardGPUv1Controller.h b/Controllers/GainwardGPUController/GainwardGPUv1Controller/GainwardGPUv1Controller.h new file mode 100644 index 0000000..e1d4a51 --- /dev/null +++ b/Controllers/GainwardGPUController/GainwardGPUv1Controller/GainwardGPUv1Controller.h @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| GainwardGPUv1Controller.h | +| | +| Driver for Gainward v1 GPU | +| | +| TheRogueZeta 05 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char gainward_gpu_dev_id; + +enum +{ + /* RGB Registers */ + GAINWARD_RED_REGISTER = 0x03, /* Red Register */ + GAINWARD_GREEN_REGISTER = 0x04, /* Green Register */ + GAINWARD_BLUE_REGISTER = 0x05, /* Blue Register */ + GAINWARD_06_REGISTER = 0x06, /* Unknown (Brightness/Mode?) Register */ +}; + +class GainwardGPUv1Controller +{ +public: + GainwardGPUv1Controller(i2c_smbus_interface* bus, gainward_gpu_dev_id, std::string dev_name); + ~GainwardGPUv1Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + unsigned char GetLEDRed(); + unsigned char GetLEDGreen(); + unsigned char GetLEDBlue(); + void SetLEDColors(unsigned char red, unsigned char green, unsigned char blue); + void SetMode(); + + unsigned char GainwardGPURegisterRead(unsigned char reg); + void GainwardGPURegisterWrite(unsigned char reg, unsigned char val); + +private: + i2c_smbus_interface * bus; + gainward_gpu_dev_id dev; + std::string name; +}; diff --git a/Controllers/GainwardGPUController/GainwardGPUv1Controller/RGBController_GainwardGPUv1.cpp b/Controllers/GainwardGPUController/GainwardGPUv1Controller/RGBController_GainwardGPUv1.cpp new file mode 100644 index 0000000..7515de3 --- /dev/null +++ b/Controllers/GainwardGPUController/GainwardGPUv1Controller/RGBController_GainwardGPUv1.cpp @@ -0,0 +1,121 @@ +/*---------------------------------------------------------*\ +| RGBController_GainwardGPUv1.cpp | +| | +| RGBController for Gainward v1 GPU | +| | +| TheRogueZeta 05 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GainwardGPUv1.h" + +int RGBController_GainwardGPUv1::GetDeviceMode() +{ + active_mode = 1; + return(active_mode); +} + +/**------------------------------------------------------------------*\ + @name Gainward GPU v1 + @category GPU + @type I2C + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectGainwardGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_GainwardGPUv1::RGBController_GainwardGPUv1(GainwardGPUv1Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Gainward"; + type = DEVICE_TYPE_GPU; + description = "Gainward GPU V1 Device"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 1; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_GainwardGPUv1::~RGBController_GainwardGPUv1() +{ + delete controller; +} + +void RGBController_GainwardGPUv1::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone gainward_gpu_zone; + gainward_gpu_zone.name = "GPU"; + gainward_gpu_zone.type = ZONE_TYPE_SINGLE; + gainward_gpu_zone.leds_min = 1; + gainward_gpu_zone.leds_max = 1; + gainward_gpu_zone.leds_count = 1; + gainward_gpu_zone.matrix_map = NULL; + zones.push_back(gainward_gpu_zone); + + /*---------------------------------------------------------*\ + | Set up LED | + \*---------------------------------------------------------*/ + led gainward_gpu_led; + gainward_gpu_led.name = "GPU"; + leds.push_back(gainward_gpu_led); + + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize color | + \*---------------------------------------------------------*/ + unsigned char red = controller->GetLEDRed(); + unsigned char grn = controller->GetLEDGreen(); + unsigned char blu = controller->GetLEDBlue(); + + colors[0] = ToRGBColor(red, grn, blu); +} + +void RGBController_GainwardGPUv1::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_GainwardGPUv1::DeviceUpdateLEDs() +{ + for(std::size_t led = 0; led < colors.size(); led++) + { + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + controller->SetLEDColors(red, grn, blu); + } +} + +void RGBController_GainwardGPUv1::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GainwardGPUv1::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GainwardGPUv1::DeviceUpdateMode() +{ + +} diff --git a/Controllers/GainwardGPUController/GainwardGPUv1Controller/RGBController_GainwardGPUv1.h b/Controllers/GainwardGPUController/GainwardGPUv1Controller/RGBController_GainwardGPUv1.h new file mode 100644 index 0000000..f66e54e --- /dev/null +++ b/Controllers/GainwardGPUController/GainwardGPUv1Controller/RGBController_GainwardGPUv1.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_GainwardGPUv1.h | +| | +| RGBController for Gainward v1 GPU | +| | +| TheRogueZeta 05 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GainwardGPUv1Controller.h" + +class RGBController_GainwardGPUv1 : public RGBController +{ +public: + RGBController_GainwardGPUv1(GainwardGPUv1Controller* controller_ptr); + ~RGBController_GainwardGPUv1(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + GainwardGPUv1Controller* controller; + + int GetDeviceMode(); +}; diff --git a/Controllers/GainwardGPUController/GainwardGPUv2Controller/GainwardGPUv2Controller.cpp b/Controllers/GainwardGPUController/GainwardGPUv2Controller/GainwardGPUv2Controller.cpp new file mode 100644 index 0000000..5d762bc --- /dev/null +++ b/Controllers/GainwardGPUController/GainwardGPUv2Controller/GainwardGPUv2Controller.cpp @@ -0,0 +1,97 @@ +/*---------------------------------------------------------*\ +| GainwardGPUv2Controller.cpp | +| | +| Driver for Gainward v2 GPU | +| | +| KundaPanda 04 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "GainwardGPUv2Controller.h" + +GainwardGPUv2Controller::GainwardGPUv2Controller(i2c_smbus_interface* bus, gainward_gpu_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +GainwardGPUv2Controller::~GainwardGPUv2Controller() = default; + +std::string GainwardGPUv2Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string GainwardGPUv2Controller::GetDeviceName() +{ + return(name); +} + +unsigned char GainwardGPUv2Controller::GetLEDRed() +{ + return(bus->i2c_smbus_read_byte_data(dev, GAINWARD_V2_RED_REGISTER)); +} + +unsigned char GainwardGPUv2Controller::GetLEDGreen() +{ + return(bus->i2c_smbus_read_byte_data(dev, GAINWARD_V2_GREEN_REGISTER)); +} + +unsigned char GainwardGPUv2Controller::GetLEDBlue() +{ + return(bus->i2c_smbus_read_byte_data(dev, GAINWARD_V2_BLUE_REGISTER)); +} + +void GainwardGPUv2Controller::SetLEDColors(unsigned char red, unsigned char green, unsigned char blue, unsigned char color_register) +{ + switch (color_register) + { + default: + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_RED_REGISTER, red); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_GREEN_REGISTER, green); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_BLUE_REGISTER, blue); + break; + case GAINWARD_V2_COLOR_REGISTER_SECONDARY: + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_RED_SECONDARY_REGISTER, red); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_GREEN_SECONDARY_REGISTER, green); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_BLUE_SECONDARY_REGISTER, blue); + break; + case GAINWARD_V2_COLOR_REGISTER_TERTIARY: + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_RED_TERTIARY_REGISTER, red); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_GREEN_TERTIARY_REGISTER, green); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_BLUE_TERTIARY_REGISTER, blue); + break; + } +} + +void GainwardGPUv2Controller::SetMode(unsigned char mode, unsigned char speed, unsigned char static_mode) +{ + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_MODE_REGISTER, mode); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_STATIC_CONTROL_REGISTER, static_mode); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_SPEED_REGISTER, speed); +} + +void GainwardGPUv2Controller::SetDirection(unsigned char direction) +{ + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_MODE_DIRECTION_REGISTER, direction); +} + +void GainwardGPUv2Controller::SetBreathingSpeed(unsigned int speed) +{ + unsigned char lower = speed & 0xFF; + unsigned char upper = (speed >> 2 * 4) & 0xFF; + + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_BREATHE_SPEED_REGISTER_A, lower); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_BREATHE_SPEED_SECONDARY_REGISTER_A, lower); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_BREATHE_SPEED_REGISTER_B, upper); + bus->i2c_smbus_write_byte_data(dev, GAINWARD_V2_BREATHE_SPEED_SECONDARY_REGISTER_B, upper); +} diff --git a/Controllers/GainwardGPUController/GainwardGPUv2Controller/GainwardGPUv2Controller.h b/Controllers/GainwardGPUController/GainwardGPUv2Controller/GainwardGPUv2Controller.h new file mode 100644 index 0000000..b8af088 --- /dev/null +++ b/Controllers/GainwardGPUController/GainwardGPUv2Controller/GainwardGPUv2Controller.h @@ -0,0 +1,101 @@ +/*---------------------------------------------------------*\ +| GainwardGPUv2Controller.h | +| | +| Driver for Gainward v2 GPU | +| | +| KundaPanda 04 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char gainward_gpu_dev_id; + +/*---------------------------------------------------------------------------------*\ + | Newer Gainward models seem to use addresses very similar to those used by EVGA | + \*-------------------------------------------------------------------------------**/ + +enum +{ + /* RGB Registers */ + GAINWARD_V2_RED_REGISTER = 0x6C, /* Red Register */ + GAINWARD_V2_GREEN_REGISTER = 0x6D, /* Green Register */ + GAINWARD_V2_BLUE_REGISTER = 0x6E, /* Blue Register */ + GAINWARD_V2_RED_SECONDARY_REGISTER = 0x70, /* Red Register 2 */ + GAINWARD_V2_GREEN_SECONDARY_REGISTER = 0x71, /* Green Register 2 */ + GAINWARD_V2_BLUE_SECONDARY_REGISTER = 0x72, /* Blue Register 2 */ + GAINWARD_V2_RED_TERTIARY_REGISTER = 0xE4, /* Red Register 3 */ + GAINWARD_V2_GREEN_TERTIARY_REGISTER = 0xE5, /* Green Register 3 */ + GAINWARD_V2_BLUE_TERTIARY_REGISTER = 0xE6, /* Blue Register 3 */ + GAINWARD_V2_MODE_REGISTER = 0xE0, /* Mode Register */ + /* Direct mode switch register + * 0x1 Software control + * 0x22 Breathing */ + GAINWARD_V2_STATIC_CONTROL_REGISTER = 0x60, + GAINWARD_V2_BREATHE_SPEED_REGISTER_A = 0x62, /* Lower part of speed control for breathe effect */ + GAINWARD_V2_BREATHE_SPEED_REGISTER_B = 0x63, /* Upper part of speed control for breathe effect */ + GAINWARD_V2_BREATHE_SPEED_SECONDARY_REGISTER_A = 0x64, /* Lower part of speed control for breathe effect - prob. secondary color */ + GAINWARD_V2_BREATHE_SPEED_SECONDARY_REGISTER_B = 0x65, /* Upper part of speed control for breathe effect - prob. secondary color */ + /* Mode direction Register + * 0x0 + * 0x1 */ + GAINWARD_V2_MODE_DIRECTION_REGISTER = 0xE1, + /* Mode speed Register + * 0x0 MAX + * 0xF MIN */ + GAINWARD_V2_SPEED_REGISTER = 0xE2, +}; + +enum +{ + /* Manual color selection using primary registers */ + GAINWARD_V2_MODE_STATIC = 0x00, + /* Rainbow cycling with direction and speed controls */ + GAINWARD_V2_MODE_CYCLE = 0x01, + /* One strobing color running around the fan + * Color using primary registers, direction and speed control */ + GAINWARD_V2_MODE_STROBE = 0x02, +}; + +enum +{ + GAINWARD_V2_COLOR_REGISTER_PRIMARY, + GAINWARD_V2_COLOR_REGISTER_SECONDARY, + GAINWARD_V2_COLOR_REGISTER_TERTIARY, +}; + +enum +{ + /* Software controlled direct mode */ + GAINWARD_V2_STATIC_SOFTWARE = 0x01, + /* GPU controlled direct mode with breathing effect */ + GAINWARD_V2_STATIC_BREATHING = 0x22, +}; + +class GainwardGPUv2Controller +{ +public: + GainwardGPUv2Controller(i2c_smbus_interface* bus, gainward_gpu_dev_id, std::string dev_name); + ~GainwardGPUv2Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + unsigned char GetLEDRed(); + unsigned char GetLEDGreen(); + unsigned char GetLEDBlue(); + void SetLEDColors(unsigned char red, unsigned char green, unsigned char blue, unsigned char color_register = GAINWARD_V2_COLOR_REGISTER_PRIMARY); + void SetMode(unsigned char mode, unsigned char speed, unsigned char direct_mode = GAINWARD_V2_STATIC_SOFTWARE); + void SetBreathingSpeed(unsigned int speed); + void SetDirection(unsigned char direction); + +private: + i2c_smbus_interface * bus; + gainward_gpu_dev_id dev; + std::string name; +}; diff --git a/Controllers/GainwardGPUController/GainwardGPUv2Controller/RGBController_GainwardGPUv2.cpp b/Controllers/GainwardGPUController/GainwardGPUv2Controller/RGBController_GainwardGPUv2.cpp new file mode 100644 index 0000000..95b11fe --- /dev/null +++ b/Controllers/GainwardGPUController/GainwardGPUv2Controller/RGBController_GainwardGPUv2.cpp @@ -0,0 +1,193 @@ +/*---------------------------------------------------------*\ +| RGBController_GainwardGPUv2.cpp | +| | +| RGBController for Gainward v2 GPU | +| | +| KundaPanda 04 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GainwardGPUv2.h" + +/**------------------------------------------------------------------*\ + @name Gainward GPU v2 + @category GPU + @type I2C + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectGainwardGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_GainwardGPUv2::RGBController_GainwardGPUv2(GainwardGPUv2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Gainward"; + type = DEVICE_TYPE_GPU; + description = "Gainward GPU V2 Device"; + location = controller->GetDeviceLocation(); + + mode Static; + Static.name = "Static"; + Static.value = GAINWARD_V2_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathe; + Breathe.name = "Breathing"; + Breathe.value = GAINWARD_V2_MODE_STATIC; + Breathe.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathe.speed_max = 0x000a; + Breathe.speed_min = 0x1324; + Breathe.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathe.colors_min = 2; + Breathe.colors_max = 2; + Breathe.colors.resize(2); + modes.push_back(Breathe); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = GAINWARD_V2_MODE_CYCLE; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RainbowWave.speed_max = 0x0; + RainbowWave.speed_min = 0xF; + RainbowWave.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowWave); + + mode Strobe; + Strobe.name = "Strobe"; + Strobe.value = GAINWARD_V2_MODE_STROBE; + Strobe.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Strobe.color_mode = MODE_COLORS_MODE_SPECIFIC; + Strobe.colors_min = 1; + Strobe.colors_max = 1; + Strobe.colors.resize(1); + Strobe.speed_max = 0x0; + Strobe.speed_min = 0xF; + modes.push_back(Strobe); + + SetupZones(); + + /*-------------------------*\ + | Initialize active mode | + \*-------------------------*/ + active_mode = 0; +} + +RGBController_GainwardGPUv2::~RGBController_GainwardGPUv2() +{ + delete controller; +} + +void RGBController_GainwardGPUv2::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone gainward_gpu_zone; + gainward_gpu_zone.name = "GPU"; + gainward_gpu_zone.type = ZONE_TYPE_SINGLE; + gainward_gpu_zone.leds_min = 1; + gainward_gpu_zone.leds_max = 1; + gainward_gpu_zone.leds_count = 1; + gainward_gpu_zone.matrix_map = NULL; + zones.push_back(gainward_gpu_zone); + + /*---------------------------------------------------------*\ + | Set up LED | + \*---------------------------------------------------------*/ + led gainward_gpu_led; + gainward_gpu_led.name = "GPU"; + leds.push_back(gainward_gpu_led); + + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize color | + \*---------------------------------------------------------*/ + unsigned char red = controller->GetLEDRed(); + unsigned char grn = controller->GetLEDGreen(); + unsigned char blu = controller->GetLEDBlue(); + + colors[0] = ToRGBColor(red, grn, blu); +} + +void RGBController_GainwardGPUv2::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_GainwardGPUv2::DeviceUpdateLEDs() +{ + for(unsigned int color : colors) + { + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetLEDColors(red, grn, blu); + controller->SetMode(GAINWARD_V2_MODE_STATIC, 0x2); + } +} + +void RGBController_GainwardGPUv2::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GainwardGPUv2::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GainwardGPUv2::DeviceUpdateMode() +{ + mode current_mode = modes[(unsigned int)active_mode]; + switch (active_mode) + { + case 1: + { + controller->SetBreathingSpeed(current_mode.speed); + + unsigned char r1 = RGBGetRValue(current_mode.colors[0]); + unsigned char g1 = RGBGetGValue(current_mode.colors[0]); + unsigned char b1 = RGBGetBValue(current_mode.colors[0]); + controller->SetLEDColors(r1, g1, b1); + + unsigned char r2 = RGBGetRValue(current_mode.colors[1]); + unsigned char g2 = RGBGetGValue(current_mode.colors[1]); + unsigned char b2 = RGBGetBValue(current_mode.colors[1]); + controller->SetLEDColors(r2, g2, b2, GAINWARD_V2_COLOR_REGISTER_SECONDARY); + controller->SetMode(GAINWARD_V2_MODE_STATIC, 0x2, GAINWARD_V2_STATIC_BREATHING); + } + break; + + /*---------------------------------------------*\ + | Case 3 intentionally falls through to case 2 | + \*---------------------------------------------*/ + case 3: + { + unsigned char r = RGBGetRValue(current_mode.colors[0]); + unsigned char g = RGBGetGValue(current_mode.colors[0]); + unsigned char b = RGBGetBValue(current_mode.colors[0]); + controller->SetLEDColors(r, g, b, GAINWARD_V2_COLOR_REGISTER_TERTIARY); + } + + case 2: + controller->SetMode((unsigned char)(current_mode.value), (unsigned char)(current_mode.speed)); + controller->SetDirection(current_mode.direction); + break; + + default: + controller->SetMode((unsigned char)(current_mode.value), (unsigned char)(current_mode.speed)); + break; + } +} diff --git a/Controllers/GainwardGPUController/GainwardGPUv2Controller/RGBController_GainwardGPUv2.h b/Controllers/GainwardGPUController/GainwardGPUv2Controller/RGBController_GainwardGPUv2.h new file mode 100644 index 0000000..4376c3d --- /dev/null +++ b/Controllers/GainwardGPUController/GainwardGPUv2Controller/RGBController_GainwardGPUv2.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_GainwardGPUv2.h | +| | +| RGBController for Gainward v2 GPU | +| | +| KundaPanda 04 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GainwardGPUv2Controller.h" + +class RGBController_GainwardGPUv2 : public RGBController +{ +public: + RGBController_GainwardGPUv2(GainwardGPUv2Controller* controller_ptr); + ~RGBController_GainwardGPUv2(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + GainwardGPUv2Controller* controller; +}; diff --git a/Controllers/GalaxGPUController/GalaxGPUControllerDetect.cpp b/Controllers/GalaxGPUController/GalaxGPUControllerDetect.cpp new file mode 100644 index 0000000..e68e9b5 --- /dev/null +++ b/Controllers/GalaxGPUController/GalaxGPUControllerDetect.cpp @@ -0,0 +1,126 @@ +/*---------------------------------------------------------*\ +| GalaxGPUControllerDetect.cpp | +| | +| Detector for Galax/KFA2 GPU | +| | +| Niels Westphal (crashniels) 12 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "GalaxGPUv1Controller.h" +#include "GalaxGPUv2Controller.h" +#include "RGBController_GalaxGPUv1.h" +#include "RGBController_GalaxGPUv2.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* TestForGalaxGPUController * +* * +* Tests the given address to see if a Galax GPU controller exists there. * +* * +\******************************************************************************************/ + +bool TestForGalaxGPUController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + unsigned char res; + + switch (address) + { + /*-----------------------------------------------------------------*\ + | V1 Controller | + \*-----------------------------------------------------------------*/ + case 0x32: + res = bus->i2c_smbus_read_byte_data(address, 0x00); + if(res == 0x27 || res == 0x26) + { + res = bus->i2c_smbus_read_byte_data(address, 0x01); + if(res == 0x10 || res == 0x20) + { + pass = true; + } + } + break; + + /*-----------------------------------------------------------------*\ + | V1 Controller | + \*-----------------------------------------------------------------*/ + case 0x23: + res = bus->i2c_smbus_read_byte_data(address, 0x00); + if(res == 0x27 || res == 0x30) + { + pass = true; + } + break; + + /*-----------------------------------------------------------------*\ + | V2 Controller | + \*-----------------------------------------------------------------*/ + case 0x51: + res = bus->i2c_smbus_read_byte_data(address, 0x00); + if(res == 0x80) + { + pass = true; + } + break; + } + + return(pass); +} /* TestForGalaxGPUController() */ + + +/******************************************************************************************\ +* * +* DetectGalaxGPUControllers * +* * +* Detect Galax GPU controllers on the enumerated I2C busses. * +* * +\******************************************************************************************/ + +void DetectGalaxGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(TestForGalaxGPUController(bus, i2c_addr)) + { + switch(i2c_addr) + { + /*-----------------------------------------------------------------*\ + | V1 Controller | + \*-----------------------------------------------------------------*/ + case 0x32: + case 0x23: + { + GalaxGPUv1Controller* controller = new GalaxGPUv1Controller(bus, i2c_addr, name); + RGBController_GalaxGPUv1* rgb_controller = new RGBController_GalaxGPUv1(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + + /*-----------------------------------------------------------------*\ + | V2 Controller | + \*-----------------------------------------------------------------*/ + case 0x51: + { + GalaxGPUv2Controller* controller = new GalaxGPUv2Controller(bus, i2c_addr, name); + RGBController_GalaxGPUv2* rgb_controller = new RGBController_GalaxGPUv2(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + } + } +} /* DetectGalaxGPUControllers() */ + +REGISTER_I2C_PCI_DETECTOR("KFA2 GeForce RTX 2070 EX", DetectGalaxGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_DEV, NVIDIA_SUB_VEN, KFA2_RTX_2070_EX_SUB_DEV, 0x23); +REGISTER_I2C_PCI_DETECTOR("KFA2 GeForce RTX 2070 OC", DetectGalaxGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, NVIDIA_SUB_VEN, KFA2_RTX_2070_OC_SUB_DEV, 0x23); +REGISTER_I2C_PCI_DETECTOR("GALAX GeForce RTX 2070 SUPER EX Gamer Black", DetectGalaxGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, NVIDIA_SUB_VEN, GALAX_RTX_2070S_EX_GAMER_BLACK_SUB_DEV, 0x23); +REGISTER_I2C_PCI_DETECTOR("KFA2 GeForce RTX 2080 EX OC", DetectGalaxGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_DEV, NVIDIA_SUB_VEN, KFA2_RTX_2080_EX_OC_SUB_DEV, 0x23); +REGISTER_I2C_PCI_DETECTOR("KFA2 GeForce RTX 2080 SUPER EX OC", DetectGalaxGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, NVIDIA_SUB_VEN, KFA2_RTX_2080_SUPER_EX_OC_SUB_DEV, 0x23); +REGISTER_I2C_PCI_DETECTOR("KFA2 GeForce RTX 2080 Ti EX OC", DetectGalaxGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_DEV, NVIDIA_SUB_VEN, KFA2_RTX_2080TI_EX_OC_SUB_DEV, 0x23); +REGISTER_I2C_PCI_DETECTOR("GALAX GeForce RTX 3080 SG", DetectGalaxGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, NVIDIA_SUB_VEN, GALAX_RTX_3080_SG_SUB_DEV, 0x23); +REGISTER_I2C_PCI_DETECTOR("GALAX GeForce RTX 5070 Ti EX Gamer 1-Click OC", DetectGalaxGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, NVIDIA_SUB_VEN, GALAX_RTX_5070TI_EX_OC_SUB_DEV, 0x51); diff --git a/Controllers/GalaxGPUController/GalaxGPUv1Controller/GalaxGPUv1Controller.cpp b/Controllers/GalaxGPUController/GalaxGPUv1Controller/GalaxGPUv1Controller.cpp new file mode 100644 index 0000000..497c716 --- /dev/null +++ b/Controllers/GalaxGPUController/GalaxGPUv1Controller/GalaxGPUv1Controller.cpp @@ -0,0 +1,108 @@ +/*---------------------------------------------------------*\ +| GalaxGPUv1Controller.cpp | +| | +| Driver for Galax/KFA2 GPU | +| | +| Niels Westphal (crashniels) 12 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "GalaxGPUv1Controller.h" + +GalaxGPUv1Controller::GalaxGPUv1Controller(i2c_smbus_interface* bus, galax_gpu_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +GalaxGPUv1Controller::~GalaxGPUv1Controller() +{ + +} + +std::string GalaxGPUv1Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string GalaxGPUv1Controller::GetDeviceName() +{ + return(name); +} + +unsigned char GalaxGPUv1Controller::GetLEDRed() +{ + return(GalaxGPURegisterRead(GALAX_V1_RED_REGISTER)); +} + +unsigned char GalaxGPUv1Controller::GetLEDGreen() +{ + return(GalaxGPURegisterRead(GALAX_V1_GREEN_REGISTER)); +} + +unsigned char GalaxGPUv1Controller::GetLEDBlue() +{ + return(GalaxGPURegisterRead(GALAX_V1_BLUE_REGISTER)); +} + +void GalaxGPUv1Controller::SetLEDColorsDirect(unsigned char red, unsigned char green, unsigned char blue) // Direct Mode is just Static Mode without applying color changes +{ + GalaxGPURegisterWrite(GALAX_V1_RED_REGISTER, red); + GalaxGPURegisterWrite(GALAX_V1_GREEN_REGISTER, green); + GalaxGPURegisterWrite(GALAX_V1_BLUE_REGISTER, blue); +} + +void GalaxGPUv1Controller::SetLEDColorsEffect(unsigned char red, unsigned char green, unsigned char blue) +{ + GalaxGPURegisterWrite(GALAX_V1_RED_REGISTER, red); + GalaxGPURegisterWrite(GALAX_V1_GREEN_REGISTER, green); + GalaxGPURegisterWrite(GALAX_V1_BLUE_REGISTER, blue); +} + +void GalaxGPUv1Controller::SetMode(unsigned char mode) +{ + switch(mode) + { + case 1: + GalaxGPURegisterWrite(GALAX_V1_MODE_REGISTER_1, GALAX_V1_MODE_STATIC_VALUE_1); + GalaxGPURegisterWrite(GALAX_V1_MODE_REGISTER_2, GALAX_V1_MODE_STATIC_VALUE_2); + break; + + case 2: + GalaxGPURegisterWrite(GALAX_V1_MODE_REGISTER_1, GALAX_V1_MODE_BREATHING_VALUE_1); + GalaxGPURegisterWrite(GALAX_V1_MODE_REGISTER_2, GALAX_V1_MODE_BREATHING_VALUE_2); + break; + + case 3: + GalaxGPURegisterWrite(GALAX_V1_MODE_REGISTER_1, GALAX_V1_MODE_RAINBOW_VALUE_1); + GalaxGPURegisterWrite(GALAX_V1_MODE_REGISTER_2, GALAX_V1_MODE_RAINBOW_VALUE_2); + break; + + case 4: + GalaxGPURegisterWrite(GALAX_V1_MODE_REGISTER_1, GALAX_V1_MODE_CYCLE_BREATHING_VALUE_1); + GalaxGPURegisterWrite(GALAX_V1_MODE_REGISTER_2, GALAX_V1_MODE_CYCLE_BREATHING_VALUE_2); + break; + + default: + break; + } +} + +unsigned char GalaxGPUv1Controller::GalaxGPURegisterRead(unsigned char reg) +{ + return(bus->i2c_smbus_read_byte_data(dev, reg)); +} + +void GalaxGPUv1Controller::GalaxGPURegisterWrite(unsigned char reg, unsigned char val) +{ + bus->i2c_smbus_write_byte_data(dev, reg, val); +} diff --git a/Controllers/GalaxGPUController/GalaxGPUv1Controller/GalaxGPUv1Controller.h b/Controllers/GalaxGPUController/GalaxGPUv1Controller/GalaxGPUv1Controller.h new file mode 100644 index 0000000..cd00ad0 --- /dev/null +++ b/Controllers/GalaxGPUController/GalaxGPUv1Controller/GalaxGPUv1Controller.h @@ -0,0 +1,70 @@ +/*---------------------------------------------------------*\ +| GalaxGPUv1Controller.h | +| | +| Driver for Galax/KFA2 GPU | +| | +| Niels Westphal (crashniels) 12 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char galax_gpu_dev_id; + +enum +{ + /* RGB Registers */ + GALAX_V1_RED_REGISTER = 0x02, /* Red Register */ + GALAX_V1_GREEN_REGISTER = 0x03, /* Green Register */ + GALAX_V1_BLUE_REGISTER = 0x04, /* Blue Register */ + /* MODE Registers */ + GALAX_V1_MODE_REGISTER_1 = 0x05, /* Mode Register 1 */ + GALAX_V1_MODE_REGISTER_2 = 0x06, /* Mode Register 2 */ +}; + +enum +{ + /* Static Mode Values */ + GALAX_V1_MODE_STATIC_VALUE_1 = 0x00, + GALAX_V1_MODE_STATIC_VALUE_2 = 0x01, + /* Breathing Mode Values */ + GALAX_V1_MODE_BREATHING_VALUE_1 = 0x04, + GALAX_V1_MODE_BREATHING_VALUE_2 = 0x00, + /* Rainbow Mode Values */ + GALAX_V1_MODE_RAINBOW_VALUE_1 = 0x84, + GALAX_V1_MODE_RAINBOW_VALUE_2 = 0x02, + /* Cycle Breathing Mode Values */ + GALAX_V1_MODE_CYCLE_BREATHING_VALUE_1 = 0x84, + GALAX_V1_MODE_CYCLE_BREATHING_VALUE_2 = 0x40, +}; + +class GalaxGPUv1Controller +{ +public: + GalaxGPUv1Controller(i2c_smbus_interface* bus, galax_gpu_dev_id, std::string dev_name); + ~GalaxGPUv1Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + unsigned char GetLEDRed(); + unsigned char GetLEDGreen(); + unsigned char GetLEDBlue(); + void SetLEDColorsDirect(unsigned char red, unsigned char green, unsigned char blue); + void SetLEDColorsEffect(unsigned char red, unsigned char green, unsigned char blue); + void SetMode(unsigned char mode); + + unsigned char GalaxGPURegisterRead(unsigned char reg); + void GalaxGPURegisterWrite(unsigned char reg, unsigned char val); + + bool direct = false; // Temporary solution to check if we are in "Direct" mode + +private: + i2c_smbus_interface * bus; + galax_gpu_dev_id dev; + std::string name; +}; diff --git a/Controllers/GalaxGPUController/GalaxGPUv1Controller/RGBController_GalaxGPUv1.cpp b/Controllers/GalaxGPUController/GalaxGPUv1Controller/RGBController_GalaxGPUv1.cpp new file mode 100644 index 0000000..334445b --- /dev/null +++ b/Controllers/GalaxGPUController/GalaxGPUv1Controller/RGBController_GalaxGPUv1.cpp @@ -0,0 +1,179 @@ +/*---------------------------------------------------------*\ +| RGBController_GalaxGPUv1.cpp | +| | +| RGBController for Galax/KFA2 GPU | +| | +| Niels Westphal (crashniels) 12 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GalaxGPUv1.h" + +int RGBController_GalaxGPUv1::GetDeviceMode() +{ + int modereg1 = controller->GalaxGPURegisterRead(GALAX_V1_MODE_REGISTER_1); + int modereg2 = controller->GalaxGPURegisterRead(GALAX_V1_MODE_REGISTER_2); + + if(modereg1 == GALAX_V1_MODE_STATIC_VALUE_1 && modereg2 == GALAX_V1_MODE_STATIC_VALUE_2) + { + active_mode = 1; + modes[active_mode].color_mode = MODE_COLORS_PER_LED; + } + + if(modereg1 == GALAX_V1_MODE_BREATHING_VALUE_1 && modereg2 == GALAX_V1_MODE_BREATHING_VALUE_2) + { + active_mode = 2; + modes[active_mode].color_mode = MODE_COLORS_PER_LED; + } + + if(modereg1 == GALAX_V1_MODE_RAINBOW_VALUE_1 && modereg2 == GALAX_V1_MODE_RAINBOW_VALUE_2) + { + active_mode = 3; + modes[active_mode].color_mode = MODE_COLORS_NONE; + } + + if(modereg1 == GALAX_V1_MODE_CYCLE_BREATHING_VALUE_1 && modereg2 == GALAX_V1_MODE_CYCLE_BREATHING_VALUE_2) + { + active_mode = 4; + modes[active_mode].color_mode = MODE_COLORS_NONE; + } + + return(active_mode); +} + +/**------------------------------------------------------------------*\ + @name Galax GPU v1 + @category GPU + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGalaxGPUv1Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_GalaxGPUv1::RGBController_GalaxGPUv1(GalaxGPUv1Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "GALAX"; + type = DEVICE_TYPE_GPU; + description = "GALAX / KFA2 RTX GPU"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 1; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = 2; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = 3; + Rainbow.flags = 0; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Cycle_Breathing; + Cycle_Breathing.name = "Cycle Breathing"; + Cycle_Breathing.value = 4; + Cycle_Breathing.flags = 0; + Cycle_Breathing.color_mode = MODE_COLORS_NONE; + modes.push_back(Cycle_Breathing); + + SetupZones(); + + active_mode = GetDeviceMode(); +} + +RGBController_GalaxGPUv1::~RGBController_GalaxGPUv1() +{ + delete controller; +} + +void RGBController_GalaxGPUv1::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone galax_gpu_zone; + galax_gpu_zone.name = "GPU"; + galax_gpu_zone.type = ZONE_TYPE_SINGLE; + galax_gpu_zone.leds_min = 1; + galax_gpu_zone.leds_max = 1; + galax_gpu_zone.leds_count = 1; + galax_gpu_zone.matrix_map = NULL; + zones.push_back(galax_gpu_zone); + + /*---------------------------------------------------------*\ + | Set up LED | + \*---------------------------------------------------------*/ + led galax_gpu_led; + galax_gpu_led.name = "GPU"; + leds.push_back(galax_gpu_led); + + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize color | + \*---------------------------------------------------------*/ + unsigned char red = controller->GetLEDRed(); + unsigned char grn = controller->GetLEDGreen(); + unsigned char blu = controller->GetLEDBlue(); + + colors[0] = ToRGBColor(red, grn, blu); +} + +void RGBController_GalaxGPUv1::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_GalaxGPUv1::DeviceUpdateLEDs() +{ + for(std::size_t led = 0; led < colors.size(); led++) + { + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + if(GetMode() == 1) + { + controller->SetLEDColorsDirect(red, grn, blu); + } + else + { + controller->SetLEDColorsEffect(red, grn, blu); + } + } +} + +void RGBController_GalaxGPUv1::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GalaxGPUv1::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GalaxGPUv1::DeviceUpdateMode() +{ + int new_mode = modes[active_mode].value; + + controller->SetMode(new_mode); +} diff --git a/Controllers/GalaxGPUController/GalaxGPUv1Controller/RGBController_GalaxGPUv1.h b/Controllers/GalaxGPUController/GalaxGPUv1Controller/RGBController_GalaxGPUv1.h new file mode 100644 index 0000000..282c3a8 --- /dev/null +++ b/Controllers/GalaxGPUController/GalaxGPUv1Controller/RGBController_GalaxGPUv1.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_GalaxGPUv1.h | +| | +| RGBController for Galax/KFA2 GPU | +| | +| Niels Westphal (crashniels) 12 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GalaxGPUv1Controller.h" + +class RGBController_GalaxGPUv1 : public RGBController +{ +public: + RGBController_GalaxGPUv1(GalaxGPUv1Controller* controller_ptr); + ~RGBController_GalaxGPUv1(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + GalaxGPUv1Controller* controller; + + int GetDeviceMode(); +}; diff --git a/Controllers/GalaxGPUController/GalaxGPUv2Controller/GalaxGPUv2Controller.cpp b/Controllers/GalaxGPUController/GalaxGPUv2Controller/GalaxGPUv2Controller.cpp new file mode 100644 index 0000000..03aa499 --- /dev/null +++ b/Controllers/GalaxGPUController/GalaxGPUv2Controller/GalaxGPUv2Controller.cpp @@ -0,0 +1,119 @@ +/*---------------------------------------------------------*\ +| GalaxGPUv2Controller.cpp | +| | +| RGBController for Galax GPUs (Xtreme Tuner) | +| | +| Daniel Stuart (daniel.stuart14) 26 may 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "GalaxGPUv2Controller.h" + +GalaxGPUv2Controller::GalaxGPUv2Controller(i2c_smbus_interface* bus, galax_gpu_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +GalaxGPUv2Controller::~GalaxGPUv2Controller() +{ + +} + +std::string GalaxGPUv2Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string GalaxGPUv2Controller::GetDeviceName() +{ + return(name); +} + +unsigned char GalaxGPUv2Controller::GetLEDRed() +{ + return(GalaxGPURegisterRead(GALAX_V2_RED_REGISTER)); +} + +unsigned char GalaxGPUv2Controller::GetLEDGreen() +{ + return(GalaxGPURegisterRead(GALAX_V2_GREEN_REGISTER)); +} + +unsigned char GalaxGPUv2Controller::GetLEDBlue() +{ + return(GalaxGPURegisterRead(GALAX_V2_BLUE_REGISTER)); +} + +unsigned char GalaxGPUv2Controller::GetMode() +{ + return GalaxGPURegisterRead(GALAX_V2_MODE_REGISTER); +} + +unsigned char GalaxGPUv2Controller::GetSync() +{ + return GalaxGPURegisterRead(GALAX_V2_SYNC_REGISTER); +} + +unsigned char GalaxGPUv2Controller::GetSpeed() +{ + return GalaxGPURegisterRead(GALAX_V2_SPEED_REGISTER_A); +} + +unsigned char GalaxGPUv2Controller::GetBrightness() +{ + return GalaxGPURegisterRead(GALAX_V2_BRIGHTNESS_REGISTER); +} + +void GalaxGPUv2Controller::SetLEDColors(unsigned char red, unsigned char green, unsigned char blue) +{ + GalaxGPURegisterWrite(GALAX_V2_RED_REGISTER, red); + GalaxGPURegisterWrite(GALAX_V2_GREEN_REGISTER, green); + GalaxGPURegisterWrite(GALAX_V2_BLUE_REGISTER, blue); +} + +void GalaxGPUv2Controller::SetMode(unsigned char value) +{ + GalaxGPURegisterWrite(GALAX_V2_MODE_REGISTER, value); +} + +void GalaxGPUv2Controller::SetSync(unsigned char value) +{ + GalaxGPURegisterWrite(GALAX_V2_SYNC_REGISTER, value); +} + +void GalaxGPUv2Controller::SetSpeed(unsigned char value) +{ + // We just duplicate the value to both speed registers + GalaxGPURegisterWrite(GALAX_V2_SPEED_REGISTER_A, value); + GalaxGPURegisterWrite(GALAX_V2_SPEED_REGISTER_B, value); +} + +void GalaxGPUv2Controller::SetBrightness(unsigned char value) +{ + GalaxGPURegisterWrite(GALAX_V2_BRIGHTNESS_REGISTER, value); +} + +void GalaxGPUv2Controller::SaveMode() +{ + GalaxGPURegisterWrite(GALAX_V2_SAVE_REGISTER, GALAX_V2_SAVE_VALUE); +} + +unsigned char GalaxGPUv2Controller::GalaxGPURegisterRead(unsigned char reg) +{ + return(bus->i2c_smbus_read_byte_data(dev, reg)); +} + +void GalaxGPUv2Controller::GalaxGPURegisterWrite(unsigned char reg, unsigned char val) +{ + bus->i2c_smbus_write_byte_data(dev, reg, val); +} diff --git a/Controllers/GalaxGPUController/GalaxGPUv2Controller/GalaxGPUv2Controller.h b/Controllers/GalaxGPUController/GalaxGPUv2Controller/GalaxGPUv2Controller.h new file mode 100644 index 0000000..01b59af --- /dev/null +++ b/Controllers/GalaxGPUController/GalaxGPUv2Controller/GalaxGPUv2Controller.h @@ -0,0 +1,82 @@ +/*---------------------------------------------------------*\ +| GalaxGPUv2Controller.h | +| | +| RGBController for Galax GPUs (Xtreme Tuner) | +| | +| Daniel Stuart (daniel.stuart14) 26 may 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char galax_gpu_dev_id; + +enum +{ + GALAX_V2_RED_REGISTER = 0x02, /* Red Register */ + GALAX_V2_GREEN_REGISTER = 0x03, /* Green Register */ + GALAX_V2_BLUE_REGISTER = 0x04, /* Blue Register */ + GALAX_V2_SPEED_REGISTER_A = 0x21, /* Speed Register A */ + GALAX_V2_SPEED_REGISTER_B = 0x22, /* Speed Register B */ + GALAX_V2_SYNC_REGISTER = 0x27, /* Sync Register */ + GALAX_V2_BRIGHTNESS_REGISTER = 0x2D, /* Brightness Register */ + GALAX_V2_MODE_REGISTER = 0x30, /* Mode Register */ + GALAX_V2_SAVE_REGISTER = 0x40, /* Save Register */ +}; + +enum +{ + GALAX_V2_MODE_STATIC_VALUE = 0x01, /* Static Mode */ + GALAX_V2_MODE_BREATHING_VALUE = 0x02, /* Breathing Mode */ + GALAX_V2_MODE_RAINBOW_VALUE = 0x16, /* Rainbow Mode */ + GALAX_V2_MODE_OFF_VALUE = 0x19, /* Off Mode */ +}; + +enum +{ + GALAX_V2_SYNC_OFF = 0x00, /* Sync Off */ + GALAX_V2_SYNC_ON = 0x01, /* Sync On */ +}; + +enum +{ + GALAX_V2_SAVE_VALUE = 0x5A, /* Save Value */ +}; + +class GalaxGPUv2Controller +{ +public: + GalaxGPUv2Controller(i2c_smbus_interface* bus, galax_gpu_dev_id, std::string dev_name); + ~GalaxGPUv2Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + unsigned char GetLEDRed(); + unsigned char GetLEDGreen(); + unsigned char GetLEDBlue(); + unsigned char GetMode(); + unsigned char GetSync(); + unsigned char GetSpeed(); + unsigned char GetBrightness(); + void SetLEDColors(unsigned char red, unsigned char green, unsigned char blue); + void SetMode(unsigned char value); + void SetSync(unsigned char value); + void SetSpeed(unsigned char value); + void SetBrightness(unsigned char value); + void SaveMode(); + + unsigned char GalaxGPURegisterRead(unsigned char reg); + void GalaxGPURegisterWrite(unsigned char reg, unsigned char val); + + bool direct = false; // Temporary solution to check if we are in "Direct" mode + +private: + i2c_smbus_interface * bus; + galax_gpu_dev_id dev; + std::string name; +}; diff --git a/Controllers/GalaxGPUController/GalaxGPUv2Controller/RGBController_GalaxGPUv2.cpp b/Controllers/GalaxGPUController/GalaxGPUv2Controller/RGBController_GalaxGPUv2.cpp new file mode 100644 index 0000000..743780b --- /dev/null +++ b/Controllers/GalaxGPUController/GalaxGPUv2Controller/RGBController_GalaxGPUv2.cpp @@ -0,0 +1,221 @@ +/*---------------------------------------------------------*\ +| RGBController_GalaxGPUv2.cpp | +| | +| RGBController for Galax GPUs (Xtreme Tuner) | +| | +| Daniel Stuart (daniel.stuart14) 26 may 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GalaxGPUv2.h" + +int RGBController_GalaxGPUv2::GetDeviceMode() +{ + int modereg = controller->GetMode(); + int syncreg = controller->GetSync(); + + int cur_mode = 0; // Static mode by default + + if (syncreg == GALAX_V2_SYNC_ON) + { + cur_mode = 3; // External Sync mode + } + else + { + switch (modereg) + { + case GALAX_V2_MODE_BREATHING_VALUE: + cur_mode = 1; + break; + + case GALAX_V2_MODE_RAINBOW_VALUE: + cur_mode = 2; + break; + + case GALAX_V2_MODE_OFF_VALUE: + cur_mode = 4; // Off mode + break; + } + } + + return(cur_mode); +} + +/**------------------------------------------------------------------*\ + @name Galax GPU v2 + @category GPU + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGalaxGPUv2Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_GalaxGPUv2::RGBController_GalaxGPUv2(GalaxGPUv2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "GALAX"; + type = DEVICE_TYPE_GPU; + description = "GALAX RTX 40+ GPU"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.brightness_min = 0x01; + Direct.brightness_max = 0x03; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = 1; + Breathing.brightness_min = 0x01; + Breathing.brightness_max = 0x03; + Breathing.speed_min = 0x00; + Breathing.speed_max = 0x09; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = 2; + Rainbow.flags = 0; + Rainbow.brightness_min = 0x01; + Rainbow.brightness_max = 0x03; + Rainbow.speed_min = 0x00; + Rainbow.speed_max = 0x09; + Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Sync; + Sync.name = "External Sync"; + Sync.value = 3; + Sync.flags = MODE_FLAG_MANUAL_SAVE; + Sync.color_mode = MODE_COLORS_NONE; + modes.push_back(Sync); + + mode Off; + Off.name = "Off"; + Off.value = 4; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + + active_mode = GetDeviceMode(); + modes[active_mode].brightness = controller->GetBrightness(); + modes[active_mode].speed = controller->GetSpeed(); +} + +RGBController_GalaxGPUv2::~RGBController_GalaxGPUv2() +{ + delete controller; +} + +void RGBController_GalaxGPUv2::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone galax_gpu_zone; + galax_gpu_zone.name = "GPU"; + galax_gpu_zone.type = ZONE_TYPE_SINGLE; + galax_gpu_zone.leds_min = 1; + galax_gpu_zone.leds_max = 1; + galax_gpu_zone.leds_count = 1; + galax_gpu_zone.matrix_map = NULL; + zones.push_back(galax_gpu_zone); + + /*---------------------------------------------------------*\ + | Set up LED | + \*---------------------------------------------------------*/ + led galax_gpu_led; + galax_gpu_led.name = "GPU"; + leds.push_back(galax_gpu_led); + + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize color | + \*---------------------------------------------------------*/ + unsigned char red = controller->GetLEDRed(); + unsigned char grn = controller->GetLEDGreen(); + unsigned char blu = controller->GetLEDBlue(); + + colors[0] = ToRGBColor(red, grn, blu); +} + +void RGBController_GalaxGPUv2::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_GalaxGPUv2::DeviceUpdateLEDs() +{ + for(std::size_t led = 0; led < colors.size(); led++) + { + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + controller->SetLEDColors(red, grn, blu); + } +} + +void RGBController_GalaxGPUv2::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GalaxGPUv2::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GalaxGPUv2::DeviceUpdateMode() +{ + unsigned char mode_value = GALAX_V2_MODE_STATIC_VALUE; // Default to static mode + unsigned char sync_value = GALAX_V2_SYNC_OFF; // Default to sync off + + switch(modes[active_mode].value) + { + case 1: + mode_value = GALAX_V2_MODE_BREATHING_VALUE; + break; + + case 2: + mode_value = GALAX_V2_MODE_RAINBOW_VALUE; + break; + + case 3: + sync_value = GALAX_V2_SYNC_ON; // Enable sync + break; + + case 4: + mode_value = GALAX_V2_MODE_OFF_VALUE; // Off mode + break; + } + + controller->SetSync(sync_value); + controller->SetMode(mode_value); + controller->SetSpeed(modes[active_mode].speed); + controller->SetBrightness(modes[active_mode].brightness); +} + +void RGBController_GalaxGPUv2::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->SaveMode(); +} diff --git a/Controllers/GalaxGPUController/GalaxGPUv2Controller/RGBController_GalaxGPUv2.h b/Controllers/GalaxGPUController/GalaxGPUv2Controller/RGBController_GalaxGPUv2.h new file mode 100644 index 0000000..80a68b1 --- /dev/null +++ b/Controllers/GalaxGPUController/GalaxGPUv2Controller/RGBController_GalaxGPUv2.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_GalaxGPUv2.h | +| | +| RGBController for Galax GPUs (Xtreme Tuner) | +| | +| Daniel Stuart (daniel.stuart14) 26 may 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GalaxGPUv2Controller.h" + +class RGBController_GalaxGPUv2 : public RGBController +{ +public: + RGBController_GalaxGPUv2(GalaxGPUv2Controller* controller_ptr); + ~RGBController_GalaxGPUv2(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + GalaxGPUv2Controller* controller; + + int GetDeviceMode(); +}; diff --git a/Controllers/GigabyteAorusCPUCoolerController/ATC800Controller.cpp b/Controllers/GigabyteAorusCPUCoolerController/ATC800Controller.cpp new file mode 100644 index 0000000..6b97d77 --- /dev/null +++ b/Controllers/GigabyteAorusCPUCoolerController/ATC800Controller.cpp @@ -0,0 +1,287 @@ +/*---------------------------------------------------------*\ +| ATC800Controller.cpp | +| | +| Driver for Aorus ATC800 cooler | +| | +| Felipe Cavalcanti 13 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "ATC800Controller.h" +#include "StringUtils.h" + +ATC800Controller::ATC800Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +ATC800Controller::~ATC800Controller() +{ + hid_close(dev); +} + +std::string ATC800Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string ATC800Controller::GetNameString() +{ + return(name); +} + +std::string ATC800Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void ATC800Controller::DisableTempRPMIndicator() +{ + uint8_t usb_buf[9] = { 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc }; + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void ATC800Controller::SendMode(uint8_t mode, uint8_t brightness, uint8_t speed, uint8_t mystery_flag, uint8_t zone) +{ + uint8_t usb_buf[9] = { 0x00, 0xc9, mode, brightness, speed, mystery_flag, zone, 0x00, 0x00 }; + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void ATC800Controller::SendOneColor(uint8_t color_flag, uint8_t red, uint8_t green, uint8_t blue) +{ + uint8_t usb_buf[9] = { 0x00, color_flag, red, green, blue, 0x00, 0x00, 0x00, 0x00 }; + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void ATC800Controller::SendMultiColor(uint8_t flag, uint8_t mode, uint8_t red1, uint8_t green1, uint8_t blue1, uint8_t red2, uint8_t green2, uint8_t blue2) +{ + uint8_t usb_buf[9] = { 0x00, flag, mode, red1, green1, blue1, red2, green2, blue2 }; + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void ATC800Controller::SendOk() +{ + uint8_t usb_buf[9] = { 0x00, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void ATC800Controller::SendCoolerMode(uint8_t zone, uint8_t mode, aorus_atc800_mode_config zone_config) +{ + switch (mode) + { + case AORUS_ATC800_MODE_OFF: + { + if (zone == AORUS_ATC800_TOP_ZONE) + { + DisableTempRPMIndicator(); + SendMode(0x01, 0x3c, 0x02, 0x02, 0x00); + SendOneColor(0xbc, 0x00, 0x00, 0x00); + SendOk(); + } + else if (zone == AORUS_ATC800_FANS_ZONE) + { + SendMode(0x01, 0x3c, 0x02, 0x02, 0x01); + for(int i = 0xb0; i <= 0xb3; i++) + { + SendMultiColor(i, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + } + SendOk(); + } + } + break; + + case AORUS_ATC800_MODE_CUSTOM: + { + if (zone == AORUS_ATC800_TOP_ZONE) + { + DisableTempRPMIndicator(); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, 0x02, 0x00, 0x00); + SendOneColor(0xbc, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0])); + SendOk(); + } + else if (zone == AORUS_ATC800_FANS_ZONE) + { + SendMode(mode, (zone_config.brightness + 1) * 0x0a, 0x02, 0x00, 0x01); + for(int i = 0xb0; i <= 0xb3; i++) + { + SendMultiColor(i, mode, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0]), RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0])); + } + SendOk(); + } + } + break; + + case AORUS_ATC800_MODE_BREATHING: + { + if (zone == AORUS_ATC800_TOP_ZONE) + { + DisableTempRPMIndicator(); + SendMode(mode, 0x3c, zone_config.speed, 0x00, 0x00); + SendOneColor(0xbc, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0])); + SendOk(); + } + else if (zone == AORUS_ATC800_FANS_ZONE) + { + SendMode(mode, 0x3c, zone_config.speed, 0x00, 0x01); + for(int i = 0xb0; i <= 0xb3; i++) + { + SendMultiColor(i, mode, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0]), RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0])); + } + SendOk(); + } + } + break; + + case AORUS_ATC800_MODE_SPECTRUM_CYCLE: + { + if (zone == AORUS_ATC800_TOP_ZONE) + { + DisableTempRPMIndicator(); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x00, 0x00); + SendOk(); + } + else if (zone == AORUS_ATC800_FANS_ZONE) + { + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x00, 0x01); + SendOk(); + } + } + break; + + case AORUS_ATC800_MODE_FLASHING: + case AORUS_ATC800_MODE_DOUBLE_FLASHING: + { + if (zone == AORUS_ATC800_TOP_ZONE) + { + DisableTempRPMIndicator(); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x00, 0x00); + SendOneColor(0xbc, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0])); + SendOk(); + } + else if (zone == AORUS_ATC800_FANS_ZONE) + { + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x00, 0x01); + for(int i = 0xb0; i <= 0xb3; i++) + { + SendMultiColor(i, mode, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0]), RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0])); + } + SendOk(); + } + } + break; + + case AORUS_ATC800_MODE_GRADIENT: + { + if (zone == AORUS_ATC800_TOP_ZONE) + { + DisableTempRPMIndicator(); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x00, 0x00); + SendOneColor(0xcd, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0])); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x0a, 0x00); + SendOk(); + } + else if (zone == AORUS_ATC800_FANS_ZONE) + { + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x08, 0x01); + SendOneColor(0xcd, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0])); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x0a, 0x01); + SendOk(); + } + } + break; + + case AORUS_ATC800_MODE_COLOR_SHIFT: + { + DisableTempRPMIndicator(); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, zone_config.numberOfColors, 0x02); + SendMultiColor(0xb0, mode, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0]), RGBGetRValue(zone_config.colors[1]), RGBGetGValue(zone_config.colors[1]), RGBGetBValue(zone_config.colors[1])); + SendMultiColor(0xb1, mode, RGBGetRValue(zone_config.colors[2]), RGBGetGValue(zone_config.colors[2]), RGBGetBValue(zone_config.colors[2]), RGBGetRValue(zone_config.colors[3]), RGBGetGValue(zone_config.colors[3]), RGBGetBValue(zone_config.colors[3])); + SendMultiColor(0xb2, mode, RGBGetRValue(zone_config.colors[4]), RGBGetGValue(zone_config.colors[4]), RGBGetBValue(zone_config.colors[4]), RGBGetRValue(zone_config.colors[5]), RGBGetGValue(zone_config.colors[5]), RGBGetBValue(zone_config.colors[5])); + SendMultiColor(0xb3, mode, RGBGetRValue(zone_config.colors[6]), RGBGetGValue(zone_config.colors[6]), RGBGetBValue(zone_config.colors[6]), RGBGetRValue(zone_config.colors[7]), RGBGetGValue(zone_config.colors[7]), RGBGetBValue(zone_config.colors[7])); + SendOk(); + } + break; + + case AORUS_ATC800_MODE_RAINBOW_WAVE: + { + if (zone == AORUS_ATC800_TOP_ZONE) + { + DisableTempRPMIndicator(); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x02, 0x00); + SendOk(); + } + else if (zone == AORUS_ATC800_FANS_ZONE) + { + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x02, 0x01); + SendOk(); + } + } + break; + + case AORUS_ATC800_MODE_RADIATE: + { + DisableTempRPMIndicator(); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x02, 0x02); + SendMultiColor(0xb0, mode, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0]), RGBGetRValue(zone_config.colors[1]), RGBGetGValue(zone_config.colors[1]), RGBGetBValue(zone_config.colors[1])); + SendMultiColor(0xb1, mode, RGBGetRValue(zone_config.colors[2]), RGBGetGValue(zone_config.colors[2]), RGBGetBValue(zone_config.colors[2]), RGBGetRValue(zone_config.colors[3]), RGBGetGValue(zone_config.colors[3]), RGBGetBValue(zone_config.colors[3])); + SendMultiColor(0xb2, mode, RGBGetRValue(zone_config.colors[4]), RGBGetGValue(zone_config.colors[4]), RGBGetBValue(zone_config.colors[4]), RGBGetRValue(zone_config.colors[5]), RGBGetGValue(zone_config.colors[5]), RGBGetBValue(zone_config.colors[5])); + SendMultiColor(0xb3, mode, RGBGetRValue(zone_config.colors[6]), RGBGetGValue(zone_config.colors[6]), RGBGetBValue(zone_config.colors[6]), RGBGetRValue(zone_config.colors[7]), RGBGetGValue(zone_config.colors[7]), RGBGetBValue(zone_config.colors[7])); + SendOk(); + } + break; + + case AORUS_ATC800_MODE_RAINBOW_LOOP: + { + if (zone == AORUS_ATC800_TOP_ZONE) + { + DisableTempRPMIndicator(); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x00, zone); + SendOk(); + } + else if (zone == AORUS_ATC800_FANS_ZONE) + { + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x08, zone); + SendOk(); + } + } + break; + + case AORUS_ATC800_MODE_TRICOLOR: + { + if (zone == AORUS_ATC800_TOP_ZONE) + { + DisableTempRPMIndicator(); + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x02, 0x00); + SendMultiColor(0xb0, mode, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0]), RGBGetRValue(zone_config.colors[1]), RGBGetGValue(zone_config.colors[1]), RGBGetBValue(zone_config.colors[1])); + SendMultiColor(0xb1, mode, RGBGetRValue(zone_config.colors[2]), RGBGetGValue(zone_config.colors[2]), RGBGetBValue(zone_config.colors[2]), 0x00, 0x00, 0x00); + SendMultiColor(0xb2, mode, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + SendMultiColor(0xb3, mode, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + SendOk(); + } + else if (zone == AORUS_ATC800_FANS_ZONE) + { + SendMode(mode, (zone_config.brightness + 1) * 0x0a, zone_config.speed, 0x02, 0x01); + SendMultiColor(0xb0, mode, RGBGetRValue(zone_config.colors[0]), RGBGetGValue(zone_config.colors[0]), RGBGetBValue(zone_config.colors[0]), RGBGetRValue(zone_config.colors[1]), RGBGetGValue(zone_config.colors[1]), RGBGetBValue(zone_config.colors[1])); + SendMultiColor(0xb1, mode, RGBGetRValue(zone_config.colors[2]), RGBGetGValue(zone_config.colors[2]), RGBGetBValue(zone_config.colors[2]), 0x00, 0x00, 0x00); + SendMultiColor(0xb2, mode, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + SendMultiColor(0xb3, mode, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + SendOk(); + } + } + break; + + } +} diff --git a/Controllers/GigabyteAorusCPUCoolerController/ATC800Controller.h b/Controllers/GigabyteAorusCPUCoolerController/ATC800Controller.h new file mode 100644 index 0000000..c192269 --- /dev/null +++ b/Controllers/GigabyteAorusCPUCoolerController/ATC800Controller.h @@ -0,0 +1,82 @@ +/*---------------------------------------------------------*\ +| ATC800Controller.h | +| | +| Driver for Aorus ATC800 cooler | +| | +| Felipe Cavalcanti 13 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +struct aorus_atc800_mode_config +{ + RGBColor colors[8]; + uint8_t numberOfColors; + uint8_t speed; + uint8_t brightness; +}; + +enum +{ + AORUS_ATC800_MODE_OFF = 0x00, + AORUS_ATC800_MODE_CUSTOM = 0x01, + AORUS_ATC800_MODE_BREATHING = 0x02, + AORUS_ATC800_MODE_SPECTRUM_CYCLE = 0x03, + AORUS_ATC800_MODE_FLASHING = 0x04, + AORUS_ATC800_MODE_DOUBLE_FLASHING = 0x05, + AORUS_ATC800_MODE_GRADIENT = 0x06, + AORUS_ATC800_MODE_COLOR_SHIFT = 0x07, + AORUS_ATC800_MODE_RAINBOW_WAVE = 0x08, + AORUS_ATC800_MODE_RADIATE = 0x09, + AORUS_ATC800_MODE_RAINBOW_LOOP = 0x0A, + AORUS_ATC800_MODE_TRICOLOR = 0x0B, +}; + +enum +{ + AORUS_ATC800_SPEED_SLOWEST = 0x00, /* Slowest speed */ + AORUS_ATC800_SPEED_NORMAL = 0x02, /* Normal speed */ + AORUS_ATC800_SPEED_FASTEST = 0x05, /* Fastest speed */ +}; + +enum +{ + AORUS_ATC800_BRIGHTNESS_MIN = 0x00, + AORUS_ATC800_BRIGHTNESS_MAX = 0x05 +}; + +enum +{ + AORUS_ATC800_FANS_ZONE = 0, + AORUS_ATC800_TOP_ZONE = 1 +}; + +class ATC800Controller +{ +public: + ATC800Controller(hid_device* dev_handle, const char* path, std::string name); + ~ATC800Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void DisableTempRPMIndicator(); + void SendMode(uint8_t mode, uint8_t brightness, uint8_t speed, uint8_t mystery_flag, uint8_t zone); + void SendOneColor(uint8_t color_flag, uint8_t red, uint8_t green, uint8_t blue); + void SendMultiColor(uint8_t flag, uint8_t mode, uint8_t red1, uint8_t green1, uint8_t blue1, uint8_t red2, uint8_t green2, uint8_t blue2); + void SendOk(); + void SendCoolerMode(uint8_t zone, uint8_t mode, aorus_atc800_mode_config zone_config); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/GigabyteAorusCPUCoolerController/GigabyteAorusCPUCoolerControllerDetect.cpp b/Controllers/GigabyteAorusCPUCoolerController/GigabyteAorusCPUCoolerControllerDetect.cpp new file mode 100644 index 0000000..0ed7d28 --- /dev/null +++ b/Controllers/GigabyteAorusCPUCoolerController/GigabyteAorusCPUCoolerControllerDetect.cpp @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| GigabyteAorusCPUCoolerControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus CPU coolers | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "ATC800Controller.h" +#include "RGBController_AorusATC800.h" + +/*-----------------------------------------------------*\ +| Vendor ID | +\*-----------------------------------------------------*/ +#define HOLTEK_VID 0x1044 + +/*-----------------------------------------------------*\ +| Controller product ids | +\*-----------------------------------------------------*/ +#define ATC_800_CONTROLLER_PID 0x7A42 + +/******************************************************************************************\ +* * +* DetectAorusCPUCoolerControllers * +* * +* Tests the USB address to see if a Aorus RGB CPU Cooler exists there. * +* * +\******************************************************************************************/ + +void DetectGigabyteAorusCPUCoolerControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + ATC800Controller* controller = new ATC800Controller(dev, info->path, name); + RGBController_AorusATC800* rgb_controller = new RGBController_AorusATC800(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Gigabyte AORUS ATC800", DetectGigabyteAorusCPUCoolerControllers, HOLTEK_VID, ATC_800_CONTROLLER_PID, 0, 0xFF01, 1); diff --git a/Controllers/GigabyteAorusCPUCoolerController/RGBController_AorusATC800.cpp b/Controllers/GigabyteAorusCPUCoolerController/RGBController_AorusATC800.cpp new file mode 100644 index 0000000..8b41b09 --- /dev/null +++ b/Controllers/GigabyteAorusCPUCoolerController/RGBController_AorusATC800.cpp @@ -0,0 +1,272 @@ +/*---------------------------------------------------------*\ +| RGBController_AorusATC800.cpp | +| | +| RGBController for Aorus ATC800 cooler | +| | +| Felipe Cavalcanti 13 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_AorusATC800.h" + +/**------------------------------------------------------------------*\ + @name Aorus ATC800 + @category Cooler + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectGigabyteAorusCPUCoolerControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_AorusATC800::RGBController_AorusATC800(ATC800Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Gigabyte"; + type = DEVICE_TYPE_COOLER; + description = "Aorus ATC800 CPU Cooler Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Custom; + Custom.name = "Custom"; + Custom.value = AORUS_ATC800_MODE_CUSTOM; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.brightness_min = AORUS_ATC800_BRIGHTNESS_MIN; + Custom.brightness_max = AORUS_ATC800_BRIGHTNESS_MAX; + Custom.brightness = AORUS_ATC800_BRIGHTNESS_MAX; + modes.push_back(Custom); + + mode Off; + Off.name = "Off"; + Off.value = AORUS_ATC800_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = AORUS_ATC800_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Flashing.color_mode = MODE_COLORS_PER_LED; + Flashing.speed_min = AORUS_ATC800_SPEED_SLOWEST; + Flashing.speed_max = AORUS_ATC800_SPEED_FASTEST; + Flashing.speed = AORUS_ATC800_SPEED_NORMAL; + Flashing.brightness_min = AORUS_ATC800_BRIGHTNESS_MIN; + Flashing.brightness_max = AORUS_ATC800_BRIGHTNESS_MAX; + Flashing.brightness = AORUS_ATC800_BRIGHTNESS_MAX; + modes.push_back(Flashing); + + mode DoubleFlashing; + DoubleFlashing.name = "Double Flashing"; + DoubleFlashing.value = AORUS_ATC800_MODE_DOUBLE_FLASHING; + DoubleFlashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + DoubleFlashing.color_mode = MODE_COLORS_PER_LED; + DoubleFlashing.speed_min = AORUS_ATC800_SPEED_SLOWEST; + DoubleFlashing.speed_max = AORUS_ATC800_SPEED_FASTEST; + DoubleFlashing.speed = AORUS_ATC800_SPEED_NORMAL; + DoubleFlashing.brightness_min = AORUS_ATC800_BRIGHTNESS_MIN; + DoubleFlashing.brightness_max = AORUS_ATC800_BRIGHTNESS_MAX; + DoubleFlashing.brightness = AORUS_ATC800_BRIGHTNESS_MAX; + modes.push_back(DoubleFlashing); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AORUS_ATC800_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = AORUS_ATC800_SPEED_SLOWEST; + Breathing.speed_max = AORUS_ATC800_SPEED_FASTEST; + Breathing.speed = AORUS_ATC800_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Gradient; + Gradient.name = "Gradient"; + Gradient.value = AORUS_ATC800_MODE_GRADIENT; + Gradient.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Gradient.color_mode = MODE_COLORS_MODE_SPECIFIC; + Gradient.colors_min = 1; + Gradient.colors_max = 1; + Gradient.colors = { 0x0000FF }; + Gradient.speed_min = AORUS_ATC800_SPEED_SLOWEST; + Gradient.speed_max = AORUS_ATC800_SPEED_FASTEST; + Gradient.speed = AORUS_ATC800_SPEED_NORMAL; + Gradient.brightness_min = AORUS_ATC800_BRIGHTNESS_MIN; + Gradient.brightness_max = AORUS_ATC800_BRIGHTNESS_MAX; + Gradient.brightness = AORUS_ATC800_BRIGHTNESS_MAX; + modes.push_back(Gradient); + + mode ColorShift; + ColorShift.name = "Color Shift"; + ColorShift.value = AORUS_ATC800_MODE_COLOR_SHIFT; + ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorShift.colors_min = 2; + ColorShift.colors_max = 8; + ColorShift.colors = { 0x0000FF, 0x0072FF, 0x00FFFF, 0x00FF00, 0xFFFF00, 0xFF0000, 0xFF00FF, 0x8080FF }; + ColorShift.speed_min = AORUS_ATC800_SPEED_SLOWEST; + ColorShift.speed_max = AORUS_ATC800_SPEED_FASTEST; + ColorShift.speed = AORUS_ATC800_SPEED_NORMAL; + ColorShift.brightness_min = AORUS_ATC800_BRIGHTNESS_MIN; + ColorShift.brightness_max = AORUS_ATC800_BRIGHTNESS_MAX; + ColorShift.brightness = AORUS_ATC800_BRIGHTNESS_MAX; + modes.push_back(ColorShift); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = AORUS_ATC800_MODE_RAINBOW_WAVE; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.speed_min = AORUS_ATC800_SPEED_SLOWEST; + RainbowWave.speed_max = AORUS_ATC800_SPEED_FASTEST; + RainbowWave.speed = AORUS_ATC800_SPEED_NORMAL; + RainbowWave.brightness_min = AORUS_ATC800_BRIGHTNESS_MIN; + RainbowWave.brightness_max = AORUS_ATC800_BRIGHTNESS_MAX; + RainbowWave.brightness = AORUS_ATC800_BRIGHTNESS_MAX; + modes.push_back(RainbowWave); + + mode Radiate; + Radiate.name = "Radiate"; + Radiate.value = AORUS_ATC800_MODE_RADIATE; + Radiate.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Radiate.color_mode = MODE_COLORS_MODE_SPECIFIC; + Radiate.colors_min = 8; + Radiate.colors_max = 8; + Radiate.colors = { 0x0000FF, 0x0072FF, 0x00FFFF, 0x00FF00, 0xFFFF00, 0xFFFF00, 0xFF00FF, 0x8080FF }; + Radiate.speed_min = AORUS_ATC800_SPEED_SLOWEST; + Radiate.speed_max = AORUS_ATC800_SPEED_FASTEST; + Radiate.speed = AORUS_ATC800_SPEED_NORMAL; + Radiate.brightness_min = AORUS_ATC800_BRIGHTNESS_MIN; + Radiate.brightness_max = AORUS_ATC800_BRIGHTNESS_MAX; + Radiate.brightness = AORUS_ATC800_BRIGHTNESS_MAX; + modes.push_back(Radiate); + + mode RainbowLoop; + RainbowLoop.name = "Rainbow Loop"; + RainbowLoop.value = AORUS_ATC800_MODE_RAINBOW_LOOP; + RainbowLoop.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RainbowLoop.color_mode = MODE_COLORS_NONE; + RainbowLoop.speed_min = AORUS_ATC800_SPEED_SLOWEST; + RainbowLoop.speed_max = AORUS_ATC800_SPEED_FASTEST; + RainbowLoop.speed = AORUS_ATC800_SPEED_NORMAL; + RainbowLoop.brightness_min = AORUS_ATC800_BRIGHTNESS_MIN; + RainbowLoop.brightness_max = AORUS_ATC800_BRIGHTNESS_MAX; + RainbowLoop.brightness = AORUS_ATC800_BRIGHTNESS_MAX; + modes.push_back(RainbowLoop); + + mode Tricolor; + Tricolor.name = "Tricolor"; + Tricolor.value = AORUS_ATC800_MODE_TRICOLOR; + Tricolor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Tricolor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Tricolor.colors_min = 3; + Tricolor.colors_max = 3; + Tricolor.colors = { 0xFF0000, 0xFF00FF, 0xFFFF00 }; + Tricolor.speed_min = AORUS_ATC800_SPEED_SLOWEST; + Tricolor.speed_max = AORUS_ATC800_SPEED_FASTEST; + Tricolor.speed = AORUS_ATC800_SPEED_NORMAL; + Tricolor.brightness_min = AORUS_ATC800_BRIGHTNESS_MIN; + Tricolor.brightness_max = AORUS_ATC800_BRIGHTNESS_MAX; + Tricolor.brightness = AORUS_ATC800_BRIGHTNESS_MAX; + modes.push_back(Tricolor); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = AORUS_ATC800_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.speed_min = AORUS_ATC800_SPEED_SLOWEST; + SpectrumCycle.speed_max = AORUS_ATC800_SPEED_FASTEST; + SpectrumCycle.speed = AORUS_ATC800_SPEED_NORMAL; + SpectrumCycle.brightness_min = AORUS_ATC800_BRIGHTNESS_MIN; + SpectrumCycle.brightness_max = AORUS_ATC800_BRIGHTNESS_MAX; + SpectrumCycle.brightness = AORUS_ATC800_BRIGHTNESS_MAX; + modes.push_back(SpectrumCycle); + + SetupZones(); +} + +RGBController_AorusATC800::~RGBController_AorusATC800() +{ + delete controller; +} + +void RGBController_AorusATC800::SetupZones() +{ + zone atc800_cpu_fans_zone; + atc800_cpu_fans_zone.name = "Fan"; + atc800_cpu_fans_zone.type = ZONE_TYPE_SINGLE; + atc800_cpu_fans_zone.leds_min = 1; + atc800_cpu_fans_zone.leds_max = 1; + atc800_cpu_fans_zone.leds_count = 1; + atc800_cpu_fans_zone.matrix_map = NULL; + zones.push_back(atc800_cpu_fans_zone); + + led atc800_fan_led; + atc800_fan_led.name = "Fan"; + leds.push_back(atc800_fan_led); + + zone atc800_top_zone; + atc800_top_zone.name = "Top"; + atc800_top_zone.type = ZONE_TYPE_SINGLE; + atc800_top_zone.leds_min = 1; + atc800_top_zone.leds_max = 1; + atc800_top_zone.leds_count = 1; + atc800_top_zone.matrix_map = NULL; + zones.push_back(atc800_top_zone); + + led atc800_top_led; + atc800_top_led.name = "Top"; + leds.push_back(atc800_top_led); + + SetupColors(); +} + +void RGBController_AorusATC800::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_AorusATC800::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); + UpdateZoneLEDs(1); +} + +void RGBController_AorusATC800::UpdateZoneLEDs(int zone) +{ + aorus_atc800_mode_config zone_config; + + zone_config.colors[0] = colors[zone]; + zone_config.numberOfColors = (uint8_t)modes[active_mode].colors.size(); + zone_config.speed = modes[active_mode].speed; + zone_config.brightness = modes[active_mode].brightness; + + if (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + for (uint8_t i = 0; i < zone_config.numberOfColors; i++) + { + zone_config.colors[i] = modes[active_mode].colors[i]; + } + } + + controller->SendCoolerMode(zone, modes[active_mode].value, zone_config); +} + +void RGBController_AorusATC800::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_AorusATC800::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/GigabyteAorusCPUCoolerController/RGBController_AorusATC800.h b/Controllers/GigabyteAorusCPUCoolerController/RGBController_AorusATC800.h new file mode 100644 index 0000000..4546dc9 --- /dev/null +++ b/Controllers/GigabyteAorusCPUCoolerController/RGBController_AorusATC800.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_AorusATC800.h | +| | +| RGBController for Aorus ATC800 cooler | +| | +| Felipe Cavalcanti 13 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ATC800Controller.h" + +class RGBController_AorusATC800 : public RGBController +{ +public: + RGBController_AorusATC800(ATC800Controller* controller_ptr); + ~RGBController_AorusATC800(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ATC800Controller* controller; +}; diff --git a/Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopController.cpp b/Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopController.cpp new file mode 100644 index 0000000..e1291a2 --- /dev/null +++ b/Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopController.cpp @@ -0,0 +1,274 @@ +/*---------------------------------------------------------*\ +| GigabyteAorusLaptopController.cpp | +| | +| Driver for Gigabyte Aorus laptop | +| | +| Morgan Guimard (morg) 05 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "GigabyteAorusLaptopController.h" +#include "StringUtils.h" + +/*---------------------------------------------------------*\ +| Indexed colors mapping | +| blue 04 | +| green 02 | +| orange 05 | +| purple 06 | +| red 01 | +| white 07 | +| yellow 03 | +\*---------------------------------------------------------*/ +static unsigned char argb_colour_index_data[2][2][2] = + { //B0 B1 + { { 0x01, 0x04 }, //G0 R0 + { 0x02, 0x04 }, }, //G1 R0 + { { 0x01, 0x06 }, //G0 R1 + { 0x05, 0x07 }, } //G1 R1 +}; + +GigabyteAorusLaptopController::GigabyteAorusLaptopController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +GigabyteAorusLaptopController::~GigabyteAorusLaptopController() +{ + hid_close(dev); +} + +std::string GigabyteAorusLaptopController::GetNameString() +{ + return(name); +} + +std::string GigabyteAorusLaptopController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string GigabyteAorusLaptopController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void GigabyteAorusLaptopController::SetDirect(uint8_t brightness, RGBColor color) +{ + /*---------------------------------------------------------*\ + | Direct mode protocol | + | ID C R G B Br C Ch | + | 08 01 00 00 FF 32 00 C5 | + | | + | C = constant | + | RGB = color | + | Br= brightness | + | Ch = checksum: 0xFF - all bytes | + \*---------------------------------------------------------*/ + + unsigned char usb_buf[GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1]; + memset(usb_buf, 0x00, GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1); + + usb_buf[1] = GIGABYTE_AORUS_LAPTOP_REPORT_ID; + usb_buf[2] = 0x01; + usb_buf[3] = RGBGetRValue(color); + usb_buf[4] = RGBGetGValue(color); + usb_buf[5] = RGBGetBValue(color); + usb_buf[6] = brightness; + + unsigned char checksum = 0xFF; + + for(unsigned int i = 1; i < 8; i++) + { + checksum -= usb_buf[i]; + } + + usb_buf[8] = checksum; + + hid_send_feature_report(dev, usb_buf, GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1); +} + +void GigabyteAorusLaptopController::SetMode(uint8_t mode_value, uint8_t speed, uint8_t brightness, uint8_t direction, RGBColor color) +{ + /*---------------------------------------------------------*\ + | Hardware mode protocol | + | ID C M Sp Br Cl Dr Ch | + | 08 00 01 08 32 01 00 XX | + | | + | C = constant | + | M = mode | + | Sp = speed | + | Br = brightness | + | Cl = Indexed color | + | Dr = Direction | + | Ch = checksum: 0xFF - all bytes | + \*---------------------------------------------------------*/ + + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + unsigned char indexed_color = GetColourIndex(red,grn,blu); + + unsigned char usb_buf[GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1]; + memset(usb_buf, 0x00, GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1); + + usb_buf[1] = GIGABYTE_AORUS_LAPTOP_REPORT_ID; // report id + + usb_buf[3] = mode_value; // mode value + usb_buf[4] = speed; // speed 0x01 -> 0x09 + usb_buf[5] = brightness; // brightness 0x00 -> 0x32 + usb_buf[6] = indexed_color; // color (red, orange, yellow, green, blue, purple, white) + usb_buf[7] = direction; // direction 0x01: right, 0x02 left, 0x03 up , 0x04 down + + unsigned char checksum = 0xFF; + + for(unsigned int i = 1; i < 8; i++) + { + checksum -= usb_buf[i]; + } + + usb_buf[8] = checksum; // checksum + + hid_send_feature_report(dev, usb_buf, GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1); +} + +unsigned char GigabyteAorusLaptopController::GetColourIndex(unsigned char red, unsigned char green, unsigned char blue) +{ + /*-----------------------------------------------------*\ + | This device uses a limited colour pallette referenced | + | by an index | + | 0x01 red | + | 0x02 orange | + | 0x03 yellow | + | 0x04 green | + | 0x05 blue | + | 0x06 purple | + | 0x07 white | + \*-----------------------------------------------------*/ + unsigned int divisor = GetLargestColour( red, green, blue); + unsigned int r = (int)round( red / divisor ); + unsigned int g = (int)round( green / divisor ); + unsigned int b = (int)round( blue / divisor ); + unsigned char idx = argb_colour_index_data[r][g][b]; + return idx; +} + +unsigned int GigabyteAorusLaptopController::GetLargestColour(unsigned int red, unsigned int green, unsigned int blue) +{ + unsigned int largest; + + if ( red > green ) + { + ( red > blue ) ? largest = red : largest = blue; + } + else + { + ( green > blue ) ? largest = green : largest = blue; + } + + return (largest == 0) ? 1 : largest; +} + +void GigabyteAorusLaptopController::SetCustom(std::vector colors, std::vector positions, unsigned char brightness) +{ + /*---------------------------------------------------------*\ + | Custom mode protocol | + | ID C M Sp Br Cl Dr Ch | + | 08 00 33 01 2D 05 01 90 | + | 92 00 00 00 00 00 00 6D -> | + | GET REPORT REQUEST <- | + | read 8x64 bytes <- | + | 12 00 00 08 00 00 00 E5 -> | + | 00 RR GG BB ..... -> data (8 * 64 bytes) | + \*---------------------------------------------------------*/ + + /*---------------------------------------------------------*\ + | Sends mode packet | + \*---------------------------------------------------------*/ + unsigned char usb_buf[GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1]; + memset(usb_buf, 0x00, GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1); + + usb_buf[1] = GIGABYTE_AORUS_LAPTOP_REPORT_ID; + usb_buf[3] = GIGABYTE_AORUS_LAPTOP_CUSTOM_MODE_VALUE; + usb_buf[4] = 0x01; + usb_buf[5] = brightness; + usb_buf[6] = 0x05; + usb_buf[7] = 0x01; + + unsigned char checksum = 0xFF; + + for(unsigned int i = 1; i < 8; i++) + { + checksum -= usb_buf[i]; + } + + usb_buf[8] = checksum; + + hid_send_feature_report(dev, usb_buf, GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1); + + /*---------------------------------------------------------*\ + | Sends first packet | + \*---------------------------------------------------------*/ + unsigned char start_packet[GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1] = + { + 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D + }; + + hid_send_feature_report(dev, start_packet, GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1); + + /*---------------------------------------------------------*\ + | GET REPORT REQUEST | + \*---------------------------------------------------------*/ + unsigned char report_packet[GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1]; + memset(report_packet, 0x00, GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1); + + report_packet[1] = GIGABYTE_AORUS_LAPTOP_REPORT_ID; + hid_get_feature_report(dev, report_packet, GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1); + + /*---------------------------------------------------------*\ + | Sends 2nd packet | + \*---------------------------------------------------------*/ + unsigned char second_packet[GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1] = + { + 0x00, 0x12, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xE5 + }; + + hid_send_feature_report(dev, second_packet, GIGABYTE_AORUS_LAPTOP_REPORT_SIZE+1); + + /*---------------------------------------------------------*\ + | Creates the data packets | + \*---------------------------------------------------------*/ + unsigned char color_data[64 * 8]; + memset(color_data, 0x00, 64 * 8); + + for(unsigned int i = 0; i < positions.size(); i++) + { + color_data[positions[i] * 4 + 1] = RGBGetRValue(colors[i]); + color_data[positions[i] * 4 + 2] = RGBGetGValue(colors[i]); + color_data[positions[i] * 4 + 3] = RGBGetBValue(colors[i]); + } + + unsigned char color_buf[64 + 1]; + color_buf[0] = 0; + + for(unsigned int i = 0; i < 8; i++) + { + memcpy(&color_buf[1], &(color_data[64*i]), 64); + hid_write(dev, color_buf, 65); + } +} diff --git a/Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopController.h b/Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopController.h new file mode 100644 index 0000000..34d3080 --- /dev/null +++ b/Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopController.h @@ -0,0 +1,73 @@ +/*---------------------------------------------------------*\ +| GigabyteAorusLaptopController.h | +| | +| Driver for Gigabyte Aorus laptop | +| | +| Morgan Guimard (morg) 05 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define GIGABYTE_AORUS_LAPTOP_REPORT_SIZE 8 +#define GIGABYTE_AORUS_LAPTOP_REPORT_ID 0x08 + +enum +{ + GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN = 0x00, + GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX = 0x32, + GIGABYTE_AORUS_LAPTOP_SPEED_MIN = 0x01, + GIGABYTE_AORUS_LAPTOP_SPEED_MAX = 0x09 +}; + +enum +{ + GIGABYTE_AORUS_LAPTOP_DIRECT_MODE_VALUE = 0x00, + GIGABYTE_AORUS_LAPTOP_STATIC_MODE_VALUE = 0x01, + GIGABYTE_AORUS_LAPTOP_PULSE_MODE_VALUE = 0x02, + GIGABYTE_AORUS_LAPTOP_WAVE_MODE_VALUE = 0x03, + GIGABYTE_AORUS_LAPTOP_REACTIVE_MODE_VALUE = 0x04, + GIGABYTE_AORUS_LAPTOP_MARQUEE_MODE_VALUE = 0x05, + GIGABYTE_AORUS_LAPTOP_RIPPLE_MODE_VALUE = 0x06, + GIGABYTE_AORUS_LAPTOP_CYCLE_MODE_VALUE = 0x08, + GIGABYTE_AORUS_LAPTOP_RAINBOW_MARQUEE_MODE_VALUE = 0x09, + GIGABYTE_AORUS_LAPTOP_DROPLET_MODE_VALUE = 0x0A, + GIGABYTE_AORUS_LAPTOP_CIRCLE_MARQUEE_MODE_VALUE = 0x0B, + GIGABYTE_AORUS_LAPTOP_HEDGE_MODE_VALUE = 0x0C, + GIGABYTE_AORUS_LAPTOP_SPIRAL_MODE_VALUE = 0x0D, + GIGABYTE_AORUS_LAPTOP_CURTAIN_MODE_VALUE = 0x40, + GIGABYTE_AORUS_LAPTOP_COMET_MODE_VALUE = 0x41, + GIGABYTE_AORUS_LAPTOP_CHASE_MODE_VALUE = 0x43, + GIGABYTE_AORUS_LAPTOP_CUSTOM_MODE_VALUE = 0x33 +}; + +class GigabyteAorusLaptopController +{ +public: + GigabyteAorusLaptopController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~GigabyteAorusLaptopController(); + + std::string GetNameString(); + std::string GetSerialString(); + std::string GetDeviceLocation(); + + void SetDirect(uint8_t brightness, RGBColor color); + void SetMode(uint8_t mode_value, uint8_t speed, uint8_t brightness, uint8_t direction, RGBColor color); + void SetCustom(std::vector colors, std::vector positions, unsigned char brightness); + +protected: + hid_device* dev; + +private: + unsigned int GetLargestColour(unsigned int red, unsigned int green, unsigned int blue); + unsigned char GetColourIndex(unsigned char red, unsigned char green, unsigned char blue); + + std::string location; + std::string name; +}; diff --git a/Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopControllerDetect.cpp b/Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopControllerDetect.cpp new file mode 100644 index 0000000..a15e835 --- /dev/null +++ b/Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopControllerDetect.cpp @@ -0,0 +1,56 @@ +/*---------------------------------------------------------*\ +| GigabyteAorusLaptopControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus laptop | +| | +| Morgan Guimard (morg) 05 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "GigabyteAorusLaptopController.h" +#include "RGBController_GigabyteAorusLaptop.h" + +/*---------------------------------------------------------*\ +| Gigabyte vendor ID | +\*---------------------------------------------------------*/ +#define GIGABYTE_AORUS_LAPTOP_VID 0x0414 + +/*---------------------------------------------------------*\ +| AORUS Laptops PID | +\*---------------------------------------------------------*/ +#define AORUS_17X_BACKLIGHT_PID 0x7A42 +#define AORUS_17X_KEYBOARD_PID 0x7A3F + +#define AORUS_15BKF_BACKLIGHT_PID 0x7A44 +#define AORUS_15BKF_KEYBOARD_PID 0x7A43 + +void DetectGigabyteAorusLaptopControllers(hid_device_info* info, const std::string& name, GIGABYTE_AORUS_LAPTOP_DEV_TYPE dev_type) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + GigabyteAorusLaptopController* controller = new GigabyteAorusLaptopController(dev, *info, name); + RGBController_GigabyteAorusLaptop* rgb_controller = new RGBController_GigabyteAorusLaptop(controller, dev_type); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectGigabyteAorusLaptopKeyboardControllers(hid_device_info* info, const std::string& name) +{ + DetectGigabyteAorusLaptopControllers(info, name, GIGABYTE_AORUS_LAPTOP_KEYBOARD_TYPE); +} + +void DetectGigabyteAorusLaptopBacklightControllers(hid_device_info* info, const std::string& name) +{ + DetectGigabyteAorusLaptopControllers(info, name, GIGABYTE_AORUS_LAPTOP_BACKLIGHT_TYPE); +} + +REGISTER_HID_DETECTOR_IPU("Gigabyte Aorus 17X Keyboard", DetectGigabyteAorusLaptopKeyboardControllers, GIGABYTE_AORUS_LAPTOP_VID, AORUS_17X_KEYBOARD_PID, 3, 0xFF01, 0x01); +REGISTER_HID_DETECTOR_IPU("Gigabyte Aorus 17X Backlight", DetectGigabyteAorusLaptopBacklightControllers, GIGABYTE_AORUS_LAPTOP_VID, AORUS_17X_BACKLIGHT_PID, 3, 0xFF01, 0x01); +REGISTER_HID_DETECTOR_IPU("Gigabyte Aorus 15BKF Keyboard", DetectGigabyteAorusLaptopKeyboardControllers, GIGABYTE_AORUS_LAPTOP_VID, AORUS_15BKF_KEYBOARD_PID, 3, 0xFF01, 0x01); +REGISTER_HID_DETECTOR_IPU("Gigabyte Aorus 15BKF Backlight", DetectGigabyteAorusLaptopBacklightControllers, GIGABYTE_AORUS_LAPTOP_VID, AORUS_15BKF_BACKLIGHT_PID, 3, 0xFF01, 0x01); diff --git a/Controllers/GigabyteAorusLaptopController/RGBController_GigabyteAorusLaptop.cpp b/Controllers/GigabyteAorusLaptopController/RGBController_GigabyteAorusLaptop.cpp new file mode 100644 index 0000000..2b1a3e7 --- /dev/null +++ b/Controllers/GigabyteAorusLaptopController/RGBController_GigabyteAorusLaptop.cpp @@ -0,0 +1,502 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteAorusLaptop.cpp | +| | +| RGBController for Gigabyte Aorus laptop | +| | +| Morgan Guimard (morg) 05 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GigabyteAorusLaptop.h" +#include "RGBControllerKeyNames.h" + +/**------------------------------------------------------------------*\ + @name Aorus Laptop + @category + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGigabyteAorusLaptopKeyboardControllers,DetectGigabyteAorusLaptopBacklightControllers + @comment Direct mode will only exposes the whole keyboard as one + big led, only custom mode can do real per key lightning. + This is impossible to determine if it auto saves to flash + (the battery cannot be removed) then we assume it does. + This device has 5 onboard memory profiles, we only use + the first one. +\*-------------------------------------------------------------------*/ + +#define NA 0xFFFFFFFF + +typedef struct +{ + const unsigned int width; /* matrix width */ + const unsigned int height; /* matrix height */ + std::vector> matrix_map; /* matrix map */ + std::vector led_names; /* led names */ + std::vector led_sequence_positions; /* position in buffers */ +} aorus_laptop_keyboard_layout; + +static aorus_laptop_keyboard_layout aorus_laptop_default_keyboard_layout = +{ + 19, + 6, + { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, // 19 + { 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, NA, 32, 33, 34, 35, 36}, // 18 + { 37, NA, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54}, // 18 + { 55, NA, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, NA, 67, 68, 69, 70, NA}, // 16 + { 71, NA, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, NA, 82, 83, 84, 85, 86, 87}, // 17 + { 88, 89, 90, 91, NA, NA, NA, 92, NA, NA, 93, 94, 95, 96, 97, 98, 99, 100, NA} // 13 + }, + { + KEY_EN_ESCAPE, KEY_EN_F1, KEY_EN_F2, KEY_EN_F3, KEY_EN_F4, KEY_EN_F5, KEY_EN_F6, KEY_EN_F7, KEY_EN_F8, KEY_EN_F9, KEY_EN_F10, KEY_EN_F11, KEY_EN_F12, KEY_EN_PAUSE_BREAK, KEY_EN_DELETE, KEY_EN_HOME, KEY_EN_PAGE_UP, KEY_EN_PAGE_DOWN, KEY_EN_END, + KEY_EN_BACK_TICK, KEY_EN_1, KEY_EN_2, KEY_EN_3, KEY_EN_4, KEY_EN_5, KEY_EN_6, KEY_EN_7, KEY_EN_8, KEY_EN_9, KEY_EN_0, KEY_EN_MINUS, KEY_EN_EQUALS, KEY_EN_BACKSPACE, KEY_EN_NUMPAD_LOCK, KEY_EN_NUMPAD_DIVIDE, KEY_EN_NUMPAD_TIMES, KEY_EN_NUMPAD_MINUS, + KEY_EN_TAB, KEY_EN_Q, KEY_EN_W, KEY_EN_E, KEY_EN_R, KEY_EN_T, KEY_EN_Y, KEY_EN_U, KEY_EN_I, KEY_EN_O, KEY_EN_P, KEY_EN_LEFT_BRACKET, KEY_EN_RIGHT_BRACKET, KEY_EN_BACK_SLASH, KEY_EN_NUMPAD_7, KEY_EN_NUMPAD_8, KEY_EN_NUMPAD_9, KEY_EN_NUMPAD_PLUS, + KEY_EN_CAPS_LOCK, KEY_EN_A, KEY_EN_S, KEY_EN_D, KEY_EN_F, KEY_EN_G, KEY_EN_H, KEY_EN_J, KEY_EN_K, KEY_EN_L, KEY_EN_SEMICOLON, KEY_EN_QUOTE, KEY_EN_ISO_ENTER, KEY_EN_NUMPAD_4, KEY_EN_NUMPAD_5, KEY_EN_NUMPAD_6, + KEY_EN_LEFT_SHIFT, KEY_EN_Z, KEY_EN_X, KEY_EN_C, KEY_EN_V, KEY_EN_B, KEY_EN_N, KEY_EN_M, KEY_EN_COMMA, KEY_EN_PERIOD, KEY_EN_FORWARD_SLASH, KEY_EN_RIGHT_SHIFT, KEY_EN_UP_ARROW, KEY_EN_NUMPAD_1, KEY_EN_NUMPAD_2, KEY_EN_NUMPAD_3, KEY_EN_NUMPAD_ENTER, + KEY_EN_LEFT_CONTROL, KEY_EN_LEFT_FUNCTION, KEY_EN_LEFT_WINDOWS, KEY_EN_LEFT_ALT, KEY_EN_SPACE, KEY_EN_RIGHT_ALT, KEY_EN_MENU, KEY_EN_RIGHT_CONTROL, KEY_EN_LEFT_ARROW, KEY_EN_DOWN_ARROW, KEY_EN_RIGHT_ARROW, KEY_EN_NUMPAD_0, KEY_EN_NUMPAD_PERIOD + }, + { + 11, 17, 23, 29, 35, 41, 47, 53, 59, 65, 71, 77, 83, 89, 95, 101, 107, 113, 119, + 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70, 76, 82, 94, 100, 106, 112, 118, + 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 81, 87, 99, 105, 111, 116, + 8, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 74, 92, 98, 104, 110, + 7, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 85, 91, 97, 103, 109, 114, + 6, 12, 18, 24, 42, 60, 66, 72, 84, 90, 96, 102, 108 + } +}; + + +RGBController_GigabyteAorusLaptop::RGBController_GigabyteAorusLaptop(GigabyteAorusLaptopController* controller_ptr, GIGABYTE_AORUS_LAPTOP_DEV_TYPE dev_type) +{ + this->dev_type = dev_type; + controller = controller_ptr; + name = controller->GetNameString(); + vendor = "Gigabyte"; + type = DEVICE_TYPE_LAPTOP; + description = "Aorus Laptop"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + /*---------------------------------------------------------*\ + | Only keyboard supports Direct mode | + \*---------------------------------------------------------*/ + if(dev_type == GIGABYTE_AORUS_LAPTOP_KEYBOARD_TYPE) + { + mode Direct; + Direct.name = "Direct"; + Direct.value = GIGABYTE_AORUS_LAPTOP_DIRECT_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Direct.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Direct.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + modes.push_back(Direct); + } + + /*---------------------------------------------------------*\ + | Common modes to keyboard + backlight | + \*---------------------------------------------------------*/ + mode Static; + Static.name = "Static"; + Static.value = GIGABYTE_AORUS_LAPTOP_STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Static.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Static.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + modes.push_back(Static); + + mode Pulse; + Pulse.name = "Breathing"; + Pulse.value = GIGABYTE_AORUS_LAPTOP_PULSE_MODE_VALUE; + Pulse.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulse.colors.resize(1); + Pulse.colors_min = 1; + Pulse.colors_max = 1; + Pulse.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Pulse.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Pulse.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + Pulse.speed_min = GIGABYTE_AORUS_LAPTOP_SPEED_MIN; + Pulse.speed_max = GIGABYTE_AORUS_LAPTOP_SPEED_MAX; + Pulse.speed = GIGABYTE_AORUS_LAPTOP_SPEED_MAX/2; + modes.push_back(Pulse); + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = GIGABYTE_AORUS_LAPTOP_WAVE_MODE_VALUE; + Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_AUTOMATIC_SAVE; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.colors.resize(1); + Wave.colors_min = 1; + Wave.colors_max = 1; + Wave.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Wave.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Wave.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + Wave.speed_min = GIGABYTE_AORUS_LAPTOP_SPEED_MIN; + Wave.speed_max = GIGABYTE_AORUS_LAPTOP_SPEED_MAX; + Wave.speed = GIGABYTE_AORUS_LAPTOP_SPEED_MAX/2; + Wave.direction = MODE_DIRECTION_LEFT; + modes.push_back(Wave); + + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = GIGABYTE_AORUS_LAPTOP_CYCLE_MODE_VALUE; + Cycle.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Cycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + Cycle.colors.resize(1); + Cycle.colors_min = 1; + Cycle.colors_max = 1; + Cycle.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Cycle.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Cycle.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + Cycle.speed_min = GIGABYTE_AORUS_LAPTOP_SPEED_MIN; + Cycle.speed_max = GIGABYTE_AORUS_LAPTOP_SPEED_MAX; + Cycle.speed = GIGABYTE_AORUS_LAPTOP_SPEED_MAX/2; + modes.push_back(Cycle); + + mode Droplet; + Droplet.name = "Droplet"; + Droplet.value = GIGABYTE_AORUS_LAPTOP_DROPLET_MODE_VALUE; + Droplet.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Droplet.color_mode = MODE_COLORS_MODE_SPECIFIC; + Droplet.colors.resize(1); + Droplet.colors_min = 1; + Droplet.colors_max = 1; + Droplet.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Droplet.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Droplet.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + Droplet.speed_min = GIGABYTE_AORUS_LAPTOP_SPEED_MIN; + Droplet.speed_max = GIGABYTE_AORUS_LAPTOP_SPEED_MAX; + Droplet.speed = GIGABYTE_AORUS_LAPTOP_SPEED_MAX/2; + modes.push_back(Droplet); + + mode Spiral; + Spiral.name = "Spiral"; + Spiral.value = GIGABYTE_AORUS_LAPTOP_SPIRAL_MODE_VALUE; + Spiral.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE; + Spiral.color_mode = MODE_COLORS_MODE_SPECIFIC; + Spiral.colors.resize(1); + Spiral.colors_min = 1; + Spiral.colors_max = 1; + Spiral.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Spiral.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Spiral.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + Spiral.speed_min = GIGABYTE_AORUS_LAPTOP_SPEED_MIN; + Spiral.speed_max = GIGABYTE_AORUS_LAPTOP_SPEED_MAX; + Spiral.speed = GIGABYTE_AORUS_LAPTOP_SPEED_MAX/2; + Spiral.direction = MODE_DIRECTION_LEFT; + modes.push_back(Spiral); + + /*---------------------------------------------------------*\ + | Modes for backlight only | + \*---------------------------------------------------------*/ + if(dev_type == GIGABYTE_AORUS_LAPTOP_BACKLIGHT_TYPE) + { + mode Curtain; + Curtain.name = "Curtain"; + Curtain.value = GIGABYTE_AORUS_LAPTOP_CURTAIN_MODE_VALUE; + Curtain.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Curtain.color_mode = MODE_COLORS_MODE_SPECIFIC; + Curtain.colors.resize(1); + Curtain.colors_min = 1; + Curtain.colors_max = 1; + Curtain.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Curtain.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Curtain.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + modes.push_back(Curtain); + + mode Comet; + Comet.name = "Comet"; + Comet.value = GIGABYTE_AORUS_LAPTOP_COMET_MODE_VALUE; + Comet.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Comet.color_mode = MODE_COLORS_MODE_SPECIFIC; + Comet.colors.resize(1); + Comet.colors_min = 1; + Comet.colors_max = 1; + Comet.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Comet.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Comet.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + modes.push_back(Comet); + + mode Chase; + Chase.name = "Chase"; + Chase.value = GIGABYTE_AORUS_LAPTOP_CHASE_MODE_VALUE; + Chase.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Chase.color_mode = MODE_COLORS_MODE_SPECIFIC; + Chase.colors.resize(1); + Chase.colors_min = 1; + Chase.colors_max = 1; + Chase.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Chase.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Chase.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + modes.push_back(Chase); + } + + /*---------------------------------------------------------*\ + | Modes for keyboard only | + \*---------------------------------------------------------*/ + if(dev_type == GIGABYTE_AORUS_LAPTOP_KEYBOARD_TYPE) + { + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = GIGABYTE_AORUS_LAPTOP_REACTIVE_MODE_VALUE; + Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.colors.resize(1); + Reactive.colors_min = 1; + Reactive.colors_max = 1; + Reactive.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Reactive.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Reactive.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + modes.push_back(Reactive); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = GIGABYTE_AORUS_LAPTOP_MARQUEE_MODE_VALUE; + Marquee.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + Marquee.colors.resize(1); + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Marquee.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Marquee.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + Marquee.speed_min = GIGABYTE_AORUS_LAPTOP_SPEED_MIN; + Marquee.speed_max = GIGABYTE_AORUS_LAPTOP_SPEED_MAX; + Marquee.speed = GIGABYTE_AORUS_LAPTOP_SPEED_MAX/2; + modes.push_back(Marquee); + + mode CircleMarquee; + CircleMarquee.name = "Circle Marquee"; + CircleMarquee.value = GIGABYTE_AORUS_LAPTOP_CIRCLE_MARQUEE_MODE_VALUE; + CircleMarquee.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + CircleMarquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + CircleMarquee.colors.resize(1); + CircleMarquee.colors_min = 1; + CircleMarquee.colors_max = 1; + CircleMarquee.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + CircleMarquee.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + CircleMarquee.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + CircleMarquee.speed_min = GIGABYTE_AORUS_LAPTOP_SPEED_MIN; + CircleMarquee.speed_max = GIGABYTE_AORUS_LAPTOP_SPEED_MAX; + CircleMarquee.speed = GIGABYTE_AORUS_LAPTOP_SPEED_MAX/2; + modes.push_back(CircleMarquee); + + mode RainbowMarquee; + RainbowMarquee.name = "Rainbow Marquee"; + RainbowMarquee.value = GIGABYTE_AORUS_LAPTOP_RAINBOW_MARQUEE_MODE_VALUE; + RainbowMarquee.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + RainbowMarquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + RainbowMarquee.colors.resize(1); + RainbowMarquee.colors_min = 1; + RainbowMarquee.colors_max = 1; + RainbowMarquee.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + RainbowMarquee.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + RainbowMarquee.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + RainbowMarquee.speed_min = GIGABYTE_AORUS_LAPTOP_SPEED_MIN; + RainbowMarquee.speed_max = GIGABYTE_AORUS_LAPTOP_SPEED_MAX; + RainbowMarquee.speed = GIGABYTE_AORUS_LAPTOP_SPEED_MAX/2; + modes.push_back(RainbowMarquee); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = GIGABYTE_AORUS_LAPTOP_RIPPLE_MODE_VALUE; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripple.colors.resize(1); + Ripple.colors_min = 1; + Ripple.colors_max = 1; + Ripple.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Ripple.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Ripple.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + Ripple.speed_min = GIGABYTE_AORUS_LAPTOP_SPEED_MIN; + Ripple.speed_max = GIGABYTE_AORUS_LAPTOP_SPEED_MAX; + Ripple.speed = GIGABYTE_AORUS_LAPTOP_SPEED_MAX/2; + modes.push_back(Ripple); + + mode Hedge; + Hedge.name = "Hedge"; + Hedge.value = GIGABYTE_AORUS_LAPTOP_HEDGE_MODE_VALUE; + Hedge.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Hedge.color_mode = MODE_COLORS_MODE_SPECIFIC; + Hedge.colors.resize(1); + Hedge.colors_min = 1; + Hedge.colors_max = 1; + Hedge.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Hedge.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Hedge.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + Hedge.speed_min = GIGABYTE_AORUS_LAPTOP_SPEED_MIN; + Hedge.speed_max = GIGABYTE_AORUS_LAPTOP_SPEED_MAX; + Hedge.speed = GIGABYTE_AORUS_LAPTOP_SPEED_MAX/2; + modes.push_back(Hedge); + + mode Custom; + Custom.name = "Custom"; + Custom.value = GIGABYTE_AORUS_LAPTOP_CUSTOM_MODE_VALUE; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.brightness_min = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MIN; + Custom.brightness_max = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX; + Custom.brightness = GIGABYTE_AORUS_LAPTOP_BRIGHTNESS_MAX/2; + modes.push_back(Custom); + } + + SetupZones(); +} + +RGBController_GigabyteAorusLaptop::~RGBController_GigabyteAorusLaptop() +{ + delete controller; +} + +void RGBController_GigabyteAorusLaptop::SetupZones() +{ + /*---------------------------------------------------------*\ + | Main zone 1 LED only | + \*---------------------------------------------------------*/ + zone new_zone; + + switch(dev_type) + { + case GIGABYTE_AORUS_LAPTOP_KEYBOARD_TYPE: + new_zone.name = "Keyboard"; + break; + case GIGABYTE_AORUS_LAPTOP_BACKLIGHT_TYPE: + new_zone.name = "Backlight"; + break; + default: + new_zone.name = "Unknonw"; + break; + } + + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + + zones.push_back(new_zone); + + led new_led; + new_led.name = "LED"; + leds.push_back(new_led); + + /*---------------------------------------------------------*\ + | Adding an extra zone for the keyboard real layout | + \*---------------------------------------------------------*/ + if(dev_type == GIGABYTE_AORUS_LAPTOP_KEYBOARD_TYPE) + { + /*-----------------------------------------*\ + | Create the zone | + \*-----------------------------------------*/ + unsigned int zone_size = 0; + + zone keyboard_zone; + keyboard_zone.name = "Keyboard layout"; + keyboard_zone.type = ZONE_TYPE_MATRIX; + + keyboard_zone.matrix_map = new matrix_map_type; + keyboard_zone.matrix_map->height = aorus_laptop_default_keyboard_layout.height; + keyboard_zone.matrix_map->width = aorus_laptop_default_keyboard_layout.width; + + keyboard_zone.matrix_map->map = new unsigned int[aorus_laptop_default_keyboard_layout.height * aorus_laptop_default_keyboard_layout.width]; + + for(unsigned int h = 0; h < aorus_laptop_default_keyboard_layout.height; h++) + { + for(unsigned int w = 0; w < aorus_laptop_default_keyboard_layout.width; w++) + { + unsigned int key = aorus_laptop_default_keyboard_layout.matrix_map[h][w]; + keyboard_zone.matrix_map->map[h * aorus_laptop_default_keyboard_layout.width + w] = key; + + if(key != NA) + { + led new_led; + new_led.name = aorus_laptop_default_keyboard_layout.led_names[key]; + leds.push_back(new_led); + zone_size++; + } + } + } + + keyboard_zone.leds_min = zone_size; + keyboard_zone.leds_max = zone_size; + keyboard_zone.leds_count = zone_size; + + zones.push_back(keyboard_zone); + } + + SetupColors(); +} + +void RGBController_GigabyteAorusLaptop::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_GigabyteAorusLaptop::DeviceUpdateLEDs() +{ + /*---------------------------------------------------------*\ + | This device supports direct mode per LED for main zone | + | only | + \*---------------------------------------------------------*/ + if(modes[active_mode].value == GIGABYTE_AORUS_LAPTOP_DIRECT_MODE_VALUE) + { + controller->SetDirect(modes[active_mode].brightness, colors[0]); + } + + /*---------------------------------------------------------*\ + | This device supports custom mode per LED for the layout | + | zone only, this isnt a direct mode | + | Skip first color from colors array (that's the first zone | + \*---------------------------------------------------------*/ + else if(modes[active_mode].value == GIGABYTE_AORUS_LAPTOP_CUSTOM_MODE_VALUE) + { + std::vector layout_zone_colors; + + for(unsigned int i = 1; i < colors.size(); i++) + { + layout_zone_colors.push_back(colors[i]); + } + + controller->SetCustom(layout_zone_colors, aorus_laptop_default_keyboard_layout.led_sequence_positions, modes[active_mode].brightness); + } +} + +void RGBController_GigabyteAorusLaptop::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GigabyteAorusLaptop::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GigabyteAorusLaptop::DeviceUpdateMode() +{ + const mode& current_mode = modes[active_mode]; + + /*---------------------------------------------------------*\ + | Redirect direct and custom mode to per led handler | + \*---------------------------------------------------------*/ + if(current_mode.value == GIGABYTE_AORUS_LAPTOP_DIRECT_MODE_VALUE || current_mode.value == GIGABYTE_AORUS_LAPTOP_CUSTOM_MODE_VALUE) + { + return DeviceUpdateLEDs(); + } + + /*---------------------------------------------------------*\ + | Hardware modes update | + \*---------------------------------------------------------*/ + unsigned char brightness = current_mode.colors[0] == 0 ? 0 : current_mode.brightness; // handles black color (not indexed) + controller->SetMode(current_mode.value, current_mode.speed, brightness, current_mode.direction + 1, current_mode.colors[0]); +} diff --git a/Controllers/GigabyteAorusLaptopController/RGBController_GigabyteAorusLaptop.h b/Controllers/GigabyteAorusLaptopController/RGBController_GigabyteAorusLaptop.h new file mode 100644 index 0000000..08095a4 --- /dev/null +++ b/Controllers/GigabyteAorusLaptopController/RGBController_GigabyteAorusLaptop.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteAorusLaptop.h | +| | +| RGBController for Gigabyte Aorus laptop | +| | +| Morgan Guimard (morg) 05 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteAorusLaptopController.h" + +enum GIGABYTE_AORUS_LAPTOP_DEV_TYPE +{ + GIGABYTE_AORUS_LAPTOP_KEYBOARD_TYPE, + GIGABYTE_AORUS_LAPTOP_BACKLIGHT_TYPE +}; + +class RGBController_GigabyteAorusLaptop : public RGBController +{ +public: + RGBController_GigabyteAorusLaptop(GigabyteAorusLaptopController* controller_ptr, GIGABYTE_AORUS_LAPTOP_DEV_TYPE dev_type); + ~RGBController_GigabyteAorusLaptop(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + GigabyteAorusLaptopController* controller; + GIGABYTE_AORUS_LAPTOP_DEV_TYPE dev_type; +}; diff --git a/Controllers/GigabyteAorusMouseController/GigabyteAorusMouseController.cpp b/Controllers/GigabyteAorusMouseController/GigabyteAorusMouseController.cpp new file mode 100644 index 0000000..4049eaf --- /dev/null +++ b/Controllers/GigabyteAorusMouseController/GigabyteAorusMouseController.cpp @@ -0,0 +1,85 @@ +/*---------------------------------------------------------*\ +| GigabyteAorusMouseController.cpp | +| | +| Driver for Gigabyte Aorus mouse | +| | +| Morgan Guimard (morg) 29 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "GigabyteAorusMouseController.h" +#include "StringUtils.h" + +GigabyteAorusMouseController::GigabyteAorusMouseController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + version = ""; +} + +GigabyteAorusMouseController::~GigabyteAorusMouseController() +{ + hid_close(dev); +} + +std::string GigabyteAorusMouseController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string GigabyteAorusMouseController::GetFirmwareVersion() +{ + return(version); +} + +std::string GigabyteAorusMouseController::GetNameString() +{ + return(name); +} + +std::string GigabyteAorusMouseController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void GigabyteAorusMouseController::SetMode(RGBColor color, uint8_t mode_value, uint8_t brightness, uint8_t speed) +{ + uint8_t usb_buf[GIGABYTE_AORUS_MOUSE_REPORT_SIZE]; + + usb_buf[0] = GIGABYTE_AORUS_MOUSE_HARDWARE_CMD; + usb_buf[1] = mode_value; + usb_buf[2] = brightness; + usb_buf[3] = RGBGetRValue(color); + usb_buf[4] = RGBGetGValue(color); + usb_buf[5] = RGBGetBValue(color); + usb_buf[6] = speed; + usb_buf[7] = 0x00; + + hid_send_feature_report(dev, usb_buf, GIGABYTE_AORUS_MOUSE_REPORT_SIZE); +} + +void GigabyteAorusMouseController::SendDirect(RGBColor color) +{ + uint8_t usb_buf[8]; + + memset(usb_buf, 0x00, GIGABYTE_AORUS_MOUSE_REPORT_SIZE); + + usb_buf[0] = GIGABYTE_AORUS_MOUSE_DIRECT_CMD; + usb_buf[2] = RGBGetRValue(color); + usb_buf[3] = RGBGetGValue(color); + usb_buf[4] = RGBGetBValue(color); + + hid_send_feature_report(dev, usb_buf, GIGABYTE_AORUS_MOUSE_REPORT_SIZE); +} diff --git a/Controllers/GigabyteAorusMouseController/GigabyteAorusMouseController.h b/Controllers/GigabyteAorusMouseController/GigabyteAorusMouseController.h new file mode 100644 index 0000000..a97d215 --- /dev/null +++ b/Controllers/GigabyteAorusMouseController/GigabyteAorusMouseController.h @@ -0,0 +1,60 @@ +/*---------------------------------------------------------*\ +| GigabyteAorusMouseController.h | +| | +| Driver for Gigabyte Aorus mouse | +| | +| Morgan Guimard (morg) 29 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define GIGABYTE_AORUS_MOUSE_REPORT_SIZE 8 +#define GIGABYTE_AORUS_MOUSE_DIRECT_CMD 0xCD +#define GIGABYTE_AORUS_MOUSE_HARDWARE_CMD 0xCC + +enum +{ + GIGABYTE_AORUS_MOUSE_DIRECT_MODE_VALUE = 0x00, + GIGABYTE_AORUS_MOUSE_STATIC_MODE_VALUE = 0x01, + GIGABYTE_AORUS_MOUSE_PULSE_MODE_VALUE = 0x02, + GIGABYTE_AORUS_MOUSE_COLOR_CYCLE_MODE_VALUE = 0x03, + GIGABYTE_AORUS_MOUSE_FLASH_MODE_VALUE = 0x04, + GIGABYTE_AORUS_MOUSE_DOUBLE_FLASH_MODE_VALUE = 0x05, +}; + +enum +{ + GIGABYTE_AORUS_MOUSE_SPEED_MIN = 0x16, + GIGABYTE_AORUS_MOUSE_SPEED_MAX = 0x00, + GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MIN = 0x00, + GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX = 0x64 +}; + +class GigabyteAorusMouseController +{ +public: + GigabyteAorusMouseController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~GigabyteAorusMouseController(); + + std::string GetDeviceLocation(); + std::string GetFirmwareVersion(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetMode(RGBColor color, uint8_t mode_value, uint8_t brightness, uint8_t speed); + void SendDirect(RGBColor color); + +private: + hid_device* dev; + + std::string location; + std::string name; + std::string version; +}; diff --git a/Controllers/GigabyteAorusMouseController/GigabyteAorusMouseControllerDetect.cpp b/Controllers/GigabyteAorusMouseController/GigabyteAorusMouseControllerDetect.cpp new file mode 100644 index 0000000..aded1e2 --- /dev/null +++ b/Controllers/GigabyteAorusMouseController/GigabyteAorusMouseControllerDetect.cpp @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| GigabyteAorusMouseControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus mouse | +| | +| Morgan Guimard (morg) 29 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "hidapi.h" +#include "GigabyteAorusMouseController.h" +#include "RGBController_GigabyteAorusMouse.h" + +/*-----------------------------------------------------*\ +| Vendor ID | +\*-----------------------------------------------------*/ +#define HOLTEK_VID 0x1044 + +/*-----------------------------------------------------*\ +| Controller product ids | +\*-----------------------------------------------------*/ +#define AORUS_M2_PID 0x7A40 + +void DetectGigabyteAorusMouseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + GigabyteAorusMouseController* controller = new GigabyteAorusMouseController(dev, *info, name); + RGBController_GigabyteAorusMouse* rgb_controller = new RGBController_GigabyteAorusMouse(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Gigabyte Aorus M2", DetectGigabyteAorusMouseControllers, HOLTEK_VID, AORUS_M2_PID, 3, 0xFF01, 0x01); diff --git a/Controllers/GigabyteAorusMouseController/RGBController_GigabyteAorusMouse.cpp b/Controllers/GigabyteAorusMouseController/RGBController_GigabyteAorusMouse.cpp new file mode 100644 index 0000000..fb7c189 --- /dev/null +++ b/Controllers/GigabyteAorusMouseController/RGBController_GigabyteAorusMouse.cpp @@ -0,0 +1,185 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteAorusMouse.cpp | +| | +| RGBController for Gigabyte Aorus mouse | +| | +| Morgan Guimard (morg) 29 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_GigabyteAorusMouse.h" + +/**------------------------------------------------------------------*\ + @name Gigabyte Aorus mouse + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGigabyteAorusMouseControllers + @comment +\*-------------------------------------------------------------------*/ +RGBController_GigabyteAorusMouse::RGBController_GigabyteAorusMouse(GigabyteAorusMouseController* controller_ptr) +{ + controller = controller_ptr; + name = controller->GetNameString(); + vendor = "Gigabyte"; + type = DEVICE_TYPE_MOUSE; + description = "Gigabyte Mouse Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = GIGABYTE_AORUS_MOUSE_DIRECT_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MIN; + Direct.brightness_max = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + Direct.brightness = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = GIGABYTE_AORUS_MOUSE_STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness_min = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MIN; + Static.brightness_max = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + Static.brightness = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + Static.colors.resize(1); + modes.push_back(Static); + + mode Pulse; + Pulse.name = "Breathing"; + Pulse.value = GIGABYTE_AORUS_MOUSE_PULSE_MODE_VALUE; + Pulse.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulse.brightness_min = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MIN; + Pulse.brightness_max = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + Pulse.brightness = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + Pulse.speed_min = GIGABYTE_AORUS_MOUSE_SPEED_MIN; + Pulse.speed_max = GIGABYTE_AORUS_MOUSE_SPEED_MAX; + Pulse.speed = GIGABYTE_AORUS_MOUSE_SPEED_MIN / 2; + Pulse.colors.resize(1); + modes.push_back(Pulse); + + mode ColorCycle; + ColorCycle.name = "Spectrum Cycle"; + ColorCycle.value = GIGABYTE_AORUS_MOUSE_COLOR_CYCLE_MODE_VALUE; + ColorCycle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + ColorCycle.color_mode = MODE_COLORS_NONE; + ColorCycle.brightness_min = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MIN; + ColorCycle.brightness_max = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + ColorCycle.brightness = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + ColorCycle.speed_min = GIGABYTE_AORUS_MOUSE_SPEED_MIN; + ColorCycle.speed_max = GIGABYTE_AORUS_MOUSE_SPEED_MAX; + ColorCycle.speed = GIGABYTE_AORUS_MOUSE_SPEED_MIN / 2; + modes.push_back(ColorCycle); + + mode Flash; + Flash.name = "Flashing"; + Flash.value = GIGABYTE_AORUS_MOUSE_FLASH_MODE_VALUE; + Flash.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Flash.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flash.brightness_min = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MIN; + Flash.brightness_max = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + Flash.brightness = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + Flash.speed_min = GIGABYTE_AORUS_MOUSE_SPEED_MIN; + Flash.speed_max = GIGABYTE_AORUS_MOUSE_SPEED_MAX; + Flash.speed = GIGABYTE_AORUS_MOUSE_SPEED_MIN / 2; + Flash.colors.resize(1); + modes.push_back(Flash); + + mode DoubleFlash; + DoubleFlash.name = "Double Flash"; + DoubleFlash.value = GIGABYTE_AORUS_MOUSE_DOUBLE_FLASH_MODE_VALUE; + DoubleFlash.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + DoubleFlash.color_mode = MODE_COLORS_MODE_SPECIFIC; + DoubleFlash.brightness_min = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MIN; + DoubleFlash.brightness_max = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + DoubleFlash.brightness = GIGABYTE_AORUS_MOUSE_BRIGHTNESS_MAX; + DoubleFlash.speed_min = GIGABYTE_AORUS_MOUSE_SPEED_MIN; + DoubleFlash.speed_max = GIGABYTE_AORUS_MOUSE_SPEED_MAX; + DoubleFlash.speed = GIGABYTE_AORUS_MOUSE_SPEED_MIN / 2; + DoubleFlash.colors.resize(1); + modes.push_back(DoubleFlash); + + mode Off; + Off.name = "Off"; + Off.value = GIGABYTE_AORUS_MOUSE_STATIC_MODE_VALUE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_GigabyteAorusMouse::~RGBController_GigabyteAorusMouse() +{ + delete controller; +} + +void RGBController_GigabyteAorusMouse::SetupZones() +{ + zone new_zone; + + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(1); + leds[0].name = "LED 1"; + + SetupColors(); +} + +void RGBController_GigabyteAorusMouse::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_GigabyteAorusMouse::DeviceUpdateLEDs() +{ + controller->SendDirect(colors[0]); +} + +void RGBController_GigabyteAorusMouse::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GigabyteAorusMouse::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GigabyteAorusMouse::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Brightness cannot be updated in the direct mode packet | + \*---------------------------------------------------------*/ + if(modes[active_mode].value == GIGABYTE_AORUS_MOUSE_DIRECT_MODE_VALUE) + { + controller->SetMode(colors[0], GIGABYTE_AORUS_MOUSE_STATIC_MODE_VALUE, modes[active_mode].brightness, 0); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + controller->SetMode(modes[active_mode].colors[0], modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed); + } + else + { + controller->SetMode(0, modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed); + } +} diff --git a/Controllers/GigabyteAorusMouseController/RGBController_GigabyteAorusMouse.h b/Controllers/GigabyteAorusMouseController/RGBController_GigabyteAorusMouse.h new file mode 100644 index 0000000..944bb6b --- /dev/null +++ b/Controllers/GigabyteAorusMouseController/RGBController_GigabyteAorusMouse.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteAorusMouse.h | +| | +| RGBController for Gigabyte Aorus mouse | +| | +| Morgan Guimard (morg) 29 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteAorusMouseController.h" + +class RGBController_GigabyteAorusMouse : public RGBController +{ +public: + RGBController_GigabyteAorusMouse(GigabyteAorusMouseController* controller_ptr); + ~RGBController_GigabyteAorusMouse(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + GigabyteAorusMouseController* controller; +}; diff --git a/Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseController.cpp b/Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseController.cpp new file mode 100644 index 0000000..f09eead --- /dev/null +++ b/Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseController.cpp @@ -0,0 +1,111 @@ +/*---------------------------------------------------------*\ +| GigabyteAorusPCCaseController.cpp | +| | +| Driver for Gigabyte Aorus case | +| | +| Denis Nazarov (nenderus) 10 Feb 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "GigabyteAorusPCCaseController.h" +#include "StringUtils.h" + +GigabyteAorusPCCaseController::GigabyteAorusPCCaseController(hid_device *dev_handle, const char *path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +GigabyteAorusPCCaseController::~GigabyteAorusPCCaseController() +{ + hid_close(dev); +} + +std::string GigabyteAorusPCCaseController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string GigabyteAorusPCCaseController::GetNameString() +{ + return(name); +} + +std::string GigabyteAorusPCCaseController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void GigabyteAorusPCCaseController::SendColor(uint8_t red, uint8_t green, uint8_t blue) +{ + uint8_t usb_buf[9] = { 0x00, 0x01, 0xC8, red, green, blue, 0x08, 0x01, 0x00 }; + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void GigabyteAorusPCCaseController::SendMode(uint8_t mode, uint8_t speed, uint8_t brightness) +{ + uint8_t usb_buf[9] = { 0x00, 0x01, 0xC9, mode, brightness, speed, 0x01, 0x08, 0x00 }; + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void GigabyteAorusPCCaseController::SendOk() +{ + uint8_t usb_buf[9] = { 0x00, 0x01, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void GigabyteAorusPCCaseController::SetMode(uint8_t mode, aorus_pc_case_mode_config zone_config) +{ + switch (mode) + { + case AORUS_PC_CASE_MODE_CUSTOM: + { + SendColor(RGBGetRValue(zone_config.color), RGBGetGValue(zone_config.color), RGBGetBValue(zone_config.color)); + SendMode(mode, AORUS_PC_CASE_SPEED_NORMAL, zone_config.brightness); + } + break; + + case AORUS_PC_CASE_MODE_OFF: + { + SendColor(0x00, 0x00, 0x00); + SendMode(AORUS_PC_CASE_MODE_CUSTOM, AORUS_PC_CASE_SPEED_SLOWEST, AORUS_PC_CASE_BRIGHTNESS_MAX + 0x01); + } + break; + + case AORUS_PC_CASE_MODE_BREATHING: + { + SendColor(RGBGetRValue(zone_config.color), RGBGetGValue(zone_config.color), RGBGetBValue(zone_config.color)); + SendMode(mode, zone_config.speed, AORUS_PC_CASE_BRIGHTNESS_MAX); + } + break; + + case AORUS_PC_CASE_MODE_SPECTRUM_CYCLE: + { + SendColor(0xFF, 0x00, 0x00); + SendMode(mode, zone_config.speed, AORUS_PC_CASE_BRIGHTNESS_MAX); + } + break; + + case AORUS_PC_CASE_MODE_FLASHING: + case AORUS_PC_CASE_MODE_DOUBLE_FLASHING: + { + SendColor(RGBGetRValue(zone_config.color), RGBGetGValue(zone_config.color), RGBGetBValue(zone_config.color)); + SendMode(mode, zone_config.speed, zone_config.brightness * 0x0A); + } + break; + } + + SendOk(); +} diff --git a/Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseController.h b/Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseController.h new file mode 100644 index 0000000..69b9e54 --- /dev/null +++ b/Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseController.h @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| GigabyteAorusPCCaseController.h | +| | +| Driver for Gigabyte Aorus case | +| | +| Denis Nazarov (nenderus) 10 Feb 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +struct aorus_pc_case_mode_config +{ + RGBColor color; + uint8_t speed; + uint8_t brightness; +}; + +enum +{ + AORUS_PC_CASE_MODE_OFF = 0x00, + AORUS_PC_CASE_MODE_CUSTOM = 0x01, + AORUS_PC_CASE_MODE_BREATHING = 0x02, + AORUS_PC_CASE_MODE_SPECTRUM_CYCLE = 0x03, + AORUS_PC_CASE_MODE_FLASHING = 0x04, + AORUS_PC_CASE_MODE_DOUBLE_FLASHING = 0x05, +}; + +enum +{ + AORUS_PC_CASE_SPEED_SLOWEST = 0x0A, + AORUS_PC_CASE_SPEED_NORMAL = 0x09, + AORUS_PC_CASE_SPEED_FASTEST = 0x06, +}; + +enum +{ + AORUS_PC_CASE_BRIGHTNESS_MIN = 0x00, + AORUS_PC_CASE_BRIGHTNESS_MAX = 0x09, +}; + +class GigabyteAorusPCCaseController +{ +public: + GigabyteAorusPCCaseController(hid_device* dev_handle, const char* path, std::string dev_name); + ~GigabyteAorusPCCaseController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendColor(uint8_t red, uint8_t green, uint8_t blue); + void SendMode(uint8_t mode, uint8_t speed, uint8_t brightness); + void SendOk(); + + void SetMode(uint8_t mode, aorus_pc_case_mode_config zone_config); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseControllerDetect.cpp b/Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseControllerDetect.cpp new file mode 100644 index 0000000..f068620 --- /dev/null +++ b/Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseControllerDetect.cpp @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| GigabyteAorusPCCaseControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus case | +| | +| Denis Nazarov (nenderus) 10 Feb 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "GigabyteAorusPCCaseController.h" +#include "RGBController_GigabyteAorusPCCase.h" + +/*-----------------------------------------------------*\ +| Vendor ID | +\*-----------------------------------------------------*/ +#define HOLTEK_VID 0x1044 + +/*-----------------------------------------------------*\ +| Controller product ids | +\*-----------------------------------------------------*/ +#define C300_GLASS_PID 0x7A30 + +/******************************************************************************************\ +* * +* DetectGigabyteAorusPCCaseControllers * +* * +* Tests the USB address to see if a Gigabyte Aorus PC Case exists there. * +* * +\******************************************************************************************/ +void DetectGigabyteAorusPCCaseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + GigabyteAorusPCCaseController* controller = new GigabyteAorusPCCaseController(dev, info->path, name); + RGBController_GigabyteAorusPCCase* rgb_controller = new RGBController_GigabyteAorusPCCase(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Gigabyte AORUS C300 GLASS", DetectGigabyteAorusPCCaseControllers, HOLTEK_VID, C300_GLASS_PID, 0, 0xFF01, 0x01); diff --git a/Controllers/GigabyteAorusPCCaseController/RGBController_GigabyteAorusPCCase.cpp b/Controllers/GigabyteAorusPCCaseController/RGBController_GigabyteAorusPCCase.cpp new file mode 100644 index 0000000..9fa7c90 --- /dev/null +++ b/Controllers/GigabyteAorusPCCaseController/RGBController_GigabyteAorusPCCase.cpp @@ -0,0 +1,178 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteAorusPCCase.cpp | +| | +| RGBController for Gigabyte Aorus case | +| | +| Denis Nazarov (nenderus) 10 Feb 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GigabyteAorusPCCase.h" + +/**------------------------------------------------------------------*\ + @name Gigabyte AORUS PC Case + @category Case + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectGigabyteAorusPCCaseControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_GigabyteAorusPCCase::RGBController_GigabyteAorusPCCase(GigabyteAorusPCCaseController *controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Gigabyte"; + description = "Gigabyte AORUS PC Case Device"; + type = DEVICE_TYPE_CASE; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Custom; + Custom.name = "Custom"; + Custom.value = AORUS_PC_CASE_MODE_CUSTOM; + Custom.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Custom.color_mode = MODE_COLORS_MODE_SPECIFIC; + Custom.colors_min = 1; + Custom.colors_max = 1; + Custom.colors.resize(1); + Custom.brightness_min = AORUS_PC_CASE_BRIGHTNESS_MIN; + Custom.brightness_max = AORUS_PC_CASE_BRIGHTNESS_MAX; + Custom.brightness = AORUS_PC_CASE_BRIGHTNESS_MAX; + modes.push_back(Custom); + + mode Off; + Off.name = "Off"; + Off.value = AORUS_PC_CASE_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = AORUS_PC_CASE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + Breathing.speed_min = AORUS_PC_CASE_SPEED_SLOWEST; + Breathing.speed_max = AORUS_PC_CASE_SPEED_FASTEST; + Breathing.speed = AORUS_PC_CASE_SPEED_NORMAL; + modes.push_back(Breathing); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = AORUS_PC_CASE_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.speed_min = AORUS_PC_CASE_SPEED_SLOWEST; + SpectrumCycle.speed_max = AORUS_PC_CASE_SPEED_FASTEST; + SpectrumCycle.speed = AORUS_PC_CASE_SPEED_NORMAL; + modes.push_back(SpectrumCycle); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = AORUS_PC_CASE_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flashing.colors_min = 1; + Flashing.colors_max = 1; + Flashing.colors.resize(1); + Flashing.speed_min = AORUS_PC_CASE_SPEED_SLOWEST; + Flashing.speed_max = AORUS_PC_CASE_SPEED_FASTEST; + Flashing.speed = AORUS_PC_CASE_SPEED_NORMAL; + Flashing.brightness_min = AORUS_PC_CASE_BRIGHTNESS_MIN; + Flashing.brightness_max = AORUS_PC_CASE_BRIGHTNESS_MAX; + Flashing.brightness = AORUS_PC_CASE_BRIGHTNESS_MAX; + modes.push_back(Flashing); + + mode DoubleFlashing; + DoubleFlashing.name = "Double Flashing"; + DoubleFlashing.value = AORUS_PC_CASE_MODE_DOUBLE_FLASHING; + DoubleFlashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + DoubleFlashing.color_mode = MODE_COLORS_MODE_SPECIFIC; + DoubleFlashing.colors_min = 1; + DoubleFlashing.colors_max = 1; + DoubleFlashing.colors.resize(1); + DoubleFlashing.speed_min = AORUS_PC_CASE_SPEED_SLOWEST; + DoubleFlashing.speed_max = AORUS_PC_CASE_SPEED_FASTEST; + DoubleFlashing.speed = AORUS_PC_CASE_SPEED_NORMAL; + DoubleFlashing.brightness_min = AORUS_PC_CASE_BRIGHTNESS_MIN; + DoubleFlashing.brightness_max = AORUS_PC_CASE_BRIGHTNESS_MAX; + DoubleFlashing.brightness = AORUS_PC_CASE_BRIGHTNESS_MAX; + modes.push_back(DoubleFlashing); + + SetupZones(); +} + +RGBController_GigabyteAorusPCCase::~RGBController_GigabyteAorusPCCase() +{ + delete controller; +} + +void RGBController_GigabyteAorusPCCase::SetupZones() +{ + zone case_zone; + case_zone.name = "Case"; + case_zone.type = ZONE_TYPE_SINGLE; + case_zone.leds_min = 1; + case_zone.leds_max = 1; + case_zone.leds_count = 1; + case_zone.matrix_map = NULL; + zones.push_back(case_zone); + + led case_led; + case_led.name = "Case"; + leds.push_back(case_led); + + SetupColors(); +} + +void RGBController_GigabyteAorusPCCase::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_GigabyteAorusPCCase::DeviceUpdateLEDs() +{ + /*---------------------------------------------------------*\ + | This device does not need update leds | + \*---------------------------------------------------------*/ +} + +void RGBController_GigabyteAorusPCCase::UpdateZoneLEDs(int /*zone*/) +{ + /*---------------------------------------------------------*\ + | This device does not need update zone leds | + \*---------------------------------------------------------*/ +} + +void RGBController_GigabyteAorusPCCase::UpdateSingleLED(int /*led*/) +{ + /*---------------------------------------------------------*\ + | This device does not need update single led | + \*---------------------------------------------------------*/ +} + +void RGBController_GigabyteAorusPCCase::DeviceUpdateMode() +{ + aorus_pc_case_mode_config zone_config; + zone_config.color = 0x000000; + zone_config.speed = modes[active_mode].speed; + zone_config.brightness = modes[active_mode].brightness; + + if (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + zone_config.color = modes[active_mode].colors[0]; + } + + controller->SetMode(modes[active_mode].value, zone_config); +} diff --git a/Controllers/GigabyteAorusPCCaseController/RGBController_GigabyteAorusPCCase.h b/Controllers/GigabyteAorusPCCaseController/RGBController_GigabyteAorusPCCase.h new file mode 100644 index 0000000..6af7c36 --- /dev/null +++ b/Controllers/GigabyteAorusPCCaseController/RGBController_GigabyteAorusPCCase.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteAorusPCCase.h | +| | +| RGBController for Gigabyte Aorus case | +| | +| Denis Nazarov (nenderus) 10 Feb 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteAorusPCCaseController.h" + +class RGBController_GigabyteAorusPCCase : public RGBController +{ +public: + RGBController_GigabyteAorusPCCase(GigabyteAorusPCCaseController* controller_ptr); + ~RGBController_GigabyteAorusPCCase(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + GigabyteAorusPCCaseController* controller; +}; diff --git a/Controllers/GigabyteCastor3Controller/GigabyteCastor3Controller.cpp b/Controllers/GigabyteCastor3Controller/GigabyteCastor3Controller.cpp new file mode 100644 index 0000000..02cb17a --- /dev/null +++ b/Controllers/GigabyteCastor3Controller/GigabyteCastor3Controller.cpp @@ -0,0 +1,238 @@ +/*---------------------------------------------------------*\ +| GigabyteCastor3Controller.cpp | +| | +| Driver for Gigabyte Aorus Waterforce X II 360 AIO | +| (Castor3 USB HID controller) | +| | +| RGB ring control only — LCD not implemented | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "GigabyteCastor3Controller.h" +#include "StringUtils.h" + +GigabyteCastor3Controller::GigabyteCastor3Controller(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + /*---------------------------------------------------------*\ + | Query firmware version via de 00 | + | Response: de [version_byte] | + | Observed: de 02 -> version 2 | + \*---------------------------------------------------------*/ + unsigned char de_payload[] = { 0xDE, 0x00 }; + SendPacket(de_payload, 2); + + unsigned char response[CASTOR3_IN_SIZE]; + int bytes_read = hid_read_timeout(dev, response, CASTOR3_IN_SIZE, CASTOR3_IN_TIMEOUT_MS); + + if(bytes_read > 1) + { + /*-----------------------------------------------------*\ + | Strip report ID 0x99 if present on IN packets | + \*-----------------------------------------------------*/ + int offset = 0; + if(response[0] == CASTOR3_REPORT_ID) + { + offset = 1; + } + + if(response[offset] == 0xDE && bytes_read > offset + 1) + { + firmware_version = std::to_string(response[offset + 1]); + } + } +} + +GigabyteCastor3Controller::~GigabyteCastor3Controller() +{ + hid_close(dev); +} + +std::string GigabyteCastor3Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string GigabyteCastor3Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string GigabyteCastor3Controller::GetFirmwareVersion() +{ + return firmware_version; +} + +void GigabyteCastor3Controller::SetEffect + ( + unsigned char style, + unsigned char speed, + unsigned char brightness, + unsigned char b4, + unsigned char b5, + castor3_color_type color_type, + std::vector colors + ) +{ + /*---------------------------------------------------------*\ + | Wire protocol for LED effect apply | + | (from castor3.py led_set_effect, confirmed from | + | led_profile.pcapng + .cled.dat cross-reference) | + | | + | Step 1: c9 [Style] [Speed*20] [Bright] [b4] [b5] | + | Step 2: cd [R] [G] [B] (single-color only) | + | -OR- b0..b3 palette registers (palette effects) | + | Step 3: b6 (commit) | + \*---------------------------------------------------------*/ + + /*---------------------------------------------------------*\ + | Step 1: c9 — effect select + parameters | + \*---------------------------------------------------------*/ + unsigned char c9_payload[] = + { + 0xC9, + style, + (unsigned char)(speed * 20), + brightness, + b4, + b5 + }; + SendPacket(c9_payload, sizeof(c9_payload)); + + /*---------------------------------------------------------*\ + | Step 2a: cd — primary color for single-color effects | + | Effects: static, pulse, flash, dflash, gradient, off | + \*---------------------------------------------------------*/ + if(color_type == CASTOR3_COLORS_SINGLE) + { + unsigned char r = 0xFF, g = 0x66, b = 0x00; + + if(colors.size() > 0) + { + r = RGBGetRValue(colors[0]); + g = RGBGetGValue(colors[0]); + b = RGBGetBValue(colors[0]); + } + + unsigned char cd_payload[] = { 0xCD, r, g, b }; + SendPacket(cd_payload, sizeof(cd_payload)); + } + /*---------------------------------------------------------*\ + | Step 2b: b0..b3 — palette registers for multi-color | + | Each register carries 2 colors (6 bytes RGB + 1 pad): | + | bN [Style] [R1 G1 B1] [R2 G2 B2] 0x00 | + | b0=colors 1+2, b1=colors 3+4, b2=colors 5+6, b3=7+8 | + | | + | Effects: colorshift(8), tricolor(3), spin(3), switch(2) | + \*---------------------------------------------------------*/ + else if(color_type == CASTOR3_COLORS_PALETTE) + { + /*-----------------------------------------------------*\ + | Build palette: pad to 8 colors by repeating last | + \*-----------------------------------------------------*/ + RGBColor palette[8]; + + for(int i = 0; i < 8; i++) + { + if(i < (int)colors.size()) + { + palette[i] = colors[i]; + } + else if(colors.size() > 0) + { + palette[i] = colors[colors.size() - 1]; + } + else + { + palette[i] = ToRGBColor(0xFF, 0x00, 0x00); + } + } + + unsigned char regs[] = { 0xB0, 0xB1, 0xB2, 0xB3 }; + + for(int i = 0; i < 4; i++) + { + RGBColor c1 = palette[i * 2]; + RGBColor c2 = palette[i * 2 + 1]; + + unsigned char bN_payload[] = + { + regs[i], + style, + (unsigned char)RGBGetRValue(c1), (unsigned char)RGBGetGValue(c1), (unsigned char)RGBGetBValue(c1), + (unsigned char)RGBGetRValue(c2), (unsigned char)RGBGetGValue(c2), (unsigned char)RGBGetBValue(c2), + 0x00 + }; + SendPacket(bN_payload, sizeof(bN_payload)); + } + } + + /*---------------------------------------------------------*\ + | Step 3: b6 — commit/apply | + \*---------------------------------------------------------*/ + unsigned char b6_payload[] = { 0xB6 }; + SendPacket(b6_payload, sizeof(b6_payload)); +} + +void GigabyteCastor3Controller::SetOff() +{ + /*---------------------------------------------------------*\ + | Off = Style 0x01 (Static) with Speed=0 | + | From castor3.py: off uses style 0x01, speed_wire=0 | + \*---------------------------------------------------------*/ + std::vector black; + black.push_back(ToRGBColor(0x00, 0x00, 0x00)); + + unsigned char c9_payload[] = + { + 0xC9, + CASTOR3_STYLE_STATIC, + 0x00, /* speed_wire = 0 for off */ + CASTOR3_BRIGHTNESS_DEFAULT, + 0x02, /* b4 */ + 0x01 /* b5 */ + }; + SendPacket(c9_payload, sizeof(c9_payload)); + + unsigned char cd_payload[] = { 0xCD, 0x00, 0x00, 0x00 }; + SendPacket(cd_payload, sizeof(cd_payload)); + + unsigned char b6_payload[] = { 0xB6 }; + SendPacket(b6_payload, sizeof(b6_payload)); +} + +void GigabyteCastor3Controller::SendPacket(const unsigned char* payload, unsigned int payload_len) +{ + /*---------------------------------------------------------*\ + | Castor3 HID OUT transport: | + | - Byte 0: Report ID (0x99) | + | - Bytes 1..6143: payload, zero-padded | + | Total write size: 6144 bytes | + \*---------------------------------------------------------*/ + unsigned char buf[CASTOR3_OUT_TOTAL]; + memset(buf, 0x00, CASTOR3_OUT_TOTAL); + + buf[0] = CASTOR3_REPORT_ID; + + if(payload_len > CASTOR3_OUT_PAYLOAD) + { + payload_len = CASTOR3_OUT_PAYLOAD; + } + + memcpy(&buf[1], payload, payload_len); + + hid_write(dev, buf, CASTOR3_OUT_TOTAL); +} diff --git a/Controllers/GigabyteCastor3Controller/GigabyteCastor3Controller.h b/Controllers/GigabyteCastor3Controller/GigabyteCastor3Controller.h new file mode 100644 index 0000000..f01af59 --- /dev/null +++ b/Controllers/GigabyteCastor3Controller/GigabyteCastor3Controller.h @@ -0,0 +1,123 @@ +/*---------------------------------------------------------*\ +| GigabyteCastor3Controller.h | +| | +| Driver for Gigabyte Aorus Waterforce X II 360 AIO | +| (Castor3 USB HID controller) | +| | +| RGB ring control only — LCD not implemented | +| | +| Protocol reference: castor3.py reverse engineering | +| VID=0x0414 PID=0x7A5E Report ID=0x99 | +| OUT size: 6144 (report ID + 6143 payload) | +| IN size: 255 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +/*---------------------------------------------------------*\ +| Castor3 USB IDs | +\*---------------------------------------------------------*/ + +#define CASTOR3_VID 0x0414 +#define CASTOR3_PID 0x7A5E + +/*---------------------------------------------------------*\ +| Castor3 HID transport | +\*---------------------------------------------------------*/ + +#define CASTOR3_REPORT_ID 0x99 +#define CASTOR3_OUT_PAYLOAD 6143 /* payload after report ID */ +#define CASTOR3_OUT_TOTAL 6144 /* report ID + payload */ +#define CASTOR3_IN_SIZE 255 +#define CASTOR3_IN_TIMEOUT_MS 2000 + +/*---------------------------------------------------------*\ +| LED effect style IDs (c9 byte[1]) | +| From castor3.py LED_EFFECTS, cross-referenced with | +| .cled.dat profile files and led_profile.pcapng | +\*---------------------------------------------------------*/ + +#define CASTOR3_STYLE_STATIC 0x01 +#define CASTOR3_STYLE_PULSE 0x02 +#define CASTOR3_STYLE_CYCLE 0x03 +#define CASTOR3_STYLE_FLASH 0x04 +#define CASTOR3_STYLE_DFLASH 0x05 +#define CASTOR3_STYLE_GRADIENT 0x06 +#define CASTOR3_STYLE_COLORSHIFT 0x07 +#define CASTOR3_STYLE_WAVE 0x08 +#define CASTOR3_STYLE_RAINBOW 0x0A +#define CASTOR3_STYLE_TRICOLOR 0x0B +#define CASTOR3_STYLE_SPIN 0x0C +#define CASTOR3_STYLE_SWITCH 0x0D + +/*---------------------------------------------------------*\ +| LED speed range: 1-5 (wire value = speed * 20) | +| LED brightness range: 1-10 (direct on wire) | +\*---------------------------------------------------------*/ + +#define CASTOR3_SPEED_MIN 1 +#define CASTOR3_SPEED_MAX 5 +#define CASTOR3_SPEED_DEFAULT 5 +#define CASTOR3_BRIGHTNESS_MIN 1 +#define CASTOR3_BRIGHTNESS_MAX 10 +#define CASTOR3_BRIGHTNESS_DEFAULT 10 + +/*---------------------------------------------------------*\ +| Color type for effects | +\*---------------------------------------------------------*/ + +enum castor3_color_type +{ + CASTOR3_COLORS_NONE = 0, /* cycle, wave, rainbow */ + CASTOR3_COLORS_SINGLE = 1, /* static, pulse, flash... */ + CASTOR3_COLORS_PALETTE = 2, /* colorshift, tricolor... */ +}; + +class GigabyteCastor3Controller +{ +public: + GigabyteCastor3Controller(hid_device* dev_handle, const char* path); + ~GigabyteCastor3Controller(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + std::string GetFirmwareVersion(); + + /*---------------------------------------------------------*\ + | LED effect application | + | | + | Wire sequence per effect apply: | + | c9 [Style] [Speed*20] [Bright] [b4] [b5] | + | cd [R] [G] [B] — single-color only | + | b0 [Style] [R1G1B1] [R2G2B2] 00 — palette colors 1+2 | + | b1 [Style] [R3G3B3] [R4G4B4] 00 — colors 3+4 | + | b2 [Style] [R5G5B5] [R6G6B6] 00 — colors 5+6 | + | b3 [Style] [R7G7B7] [R8G8B8] 00 — colors 7+8 | + | b6 — commit/apply | + \*---------------------------------------------------------*/ + + void SetEffect(unsigned char style, + unsigned char speed, + unsigned char brightness, + unsigned char b4, + unsigned char b5, + castor3_color_type color_type, + std::vector colors); + + void SetOff(); + +private: + hid_device* dev; + std::string location; + std::string firmware_version; + + void SendPacket(const unsigned char* payload, unsigned int payload_len); +}; diff --git a/Controllers/GigabyteCastor3Controller/GigabyteCastor3ControllerDetect.cpp b/Controllers/GigabyteCastor3Controller/GigabyteCastor3ControllerDetect.cpp new file mode 100644 index 0000000..3cccfd8 --- /dev/null +++ b/Controllers/GigabyteCastor3Controller/GigabyteCastor3ControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| GigabyteCastor3ControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus Waterforce X II 360 AIO | +| (Castor3 USB HID controller) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "GigabyteCastor3Controller.h" +#include "RGBController_GigabyteCastor3.h" + +#define GIGABYTE_CASTOR3_VID 0x0414 +#define GIGABYTE_CASTOR3_PID 0x7A5E + +void DetectGigabyteCastor3Controllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + GigabyteCastor3Controller* controller = new GigabyteCastor3Controller(dev, info->path); + RGBController_GigabyteCastor3* rgb_controller = new RGBController_GigabyteCastor3(controller); + rgb_controller->name = name; + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Gigabyte Aorus Waterforce X II 360", + DetectGigabyteCastor3Controllers, + GIGABYTE_CASTOR3_VID, + GIGABYTE_CASTOR3_PID, + 0, /* interface 0 */ + 0x0000, /* usage_page */ + 0x0002); /* usage */ diff --git a/Controllers/GigabyteCastor3Controller/RGBController_GigabyteCastor3.cpp b/Controllers/GigabyteCastor3Controller/RGBController_GigabyteCastor3.cpp new file mode 100644 index 0000000..6ce7fd9 --- /dev/null +++ b/Controllers/GigabyteCastor3Controller/RGBController_GigabyteCastor3.cpp @@ -0,0 +1,446 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteCastor3.cpp | +| | +| RGBController for Gigabyte Aorus Waterforce X II 360 | +| AIO Cooler (Castor3 controller) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GigabyteCastor3.h" + +/**------------------------------------------------------------------*\ + @name Gigabyte Aorus Waterforce X II 360 + @category Cooler + @type USB + @save :o: + @direct :o: + @effects :white_check_mark: + @detectors DetectGigabyteCastor3Controllers + @comment Controls the LED ring on the pump head of the + Gigabyte Aorus Waterforce X II 360 AIO cooler. + Uses the Castor3 USB HID controller (VID 0x0414, PID 0x7A5E). + The LED ring and fans share the same controller — they are + not independently addressable and follow the same effect. + LCD display control is not implemented. + No direct/per-LED mode available — all modes are hardware + effects with a single color or palette. +\*-------------------------------------------------------------------*/ + +RGBController_GigabyteCastor3::RGBController_GigabyteCastor3(GigabyteCastor3Controller* controller_ptr) +{ + controller = controller_ptr; + + name = "Gigabyte Aorus Waterforce X II 360"; + vendor = "Gigabyte"; + type = DEVICE_TYPE_COOLER; + description = "Gigabyte Aorus Waterforce X II 360 AIO Cooler"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + /*---------------------------------------------------------*\ + | All modes from castor3.py LED_EFFECTS (12 effects + off) | + | | + | The Castor3 has no direct/per-LED mode. All effects are | + | hardware-driven, either single-color, palette, or | + | firmware-color (no user color). | + | | + | Speed: 1-5 mapped to MODE_FLAG_HAS_SPEED | + | Brightness: 1-10 mapped to MODE_FLAG_HAS_BRIGHTNESS | + | | + | mode.value stores the style byte for the c9 command. | + | mode.speed stores 1-5 (scaled to speed*20 on wire). | + | mode.brightness stores 1-10 (direct on wire). | + \*---------------------------------------------------------*/ + + /*---------------------------------------------------------*\ + | Off | + | Wire: Style=0x01 (Static) with speed_wire=0 | + \*---------------------------------------------------------*/ + mode Off; + Off.name = "Off"; + Off.value = 0xFF; /* sentinel — handled specially */ + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + /*---------------------------------------------------------*\ + | Static — single color, no speed | + \*---------------------------------------------------------*/ + mode Static; + Static.name = "Static"; + Static.value = CASTOR3_STYLE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness_min = CASTOR3_BRIGHTNESS_MIN; + Static.brightness_max = CASTOR3_BRIGHTNESS_MAX; + Static.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + Static.colors.resize(1); + modes.push_back(Static); + + /*---------------------------------------------------------*\ + | Pulse — single color, speed + brightness | + \*---------------------------------------------------------*/ + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = CASTOR3_STYLE_PULSE; + Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Pulse.speed_min = CASTOR3_SPEED_MIN; + Pulse.speed_max = CASTOR3_SPEED_MAX; + Pulse.speed = CASTOR3_SPEED_DEFAULT; + Pulse.colors_min = 1; + Pulse.colors_max = 1; + Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulse.brightness_min = CASTOR3_BRIGHTNESS_MIN; + Pulse.brightness_max = CASTOR3_BRIGHTNESS_MAX; + Pulse.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + Pulse.colors.resize(1); + modes.push_back(Pulse); + + /*---------------------------------------------------------*\ + | Flash — single color, speed + brightness | + \*---------------------------------------------------------*/ + mode Flash; + Flash.name = "Flash"; + Flash.value = CASTOR3_STYLE_FLASH; + Flash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Flash.speed_min = CASTOR3_SPEED_MIN; + Flash.speed_max = CASTOR3_SPEED_MAX; + Flash.speed = CASTOR3_SPEED_DEFAULT; + Flash.colors_min = 1; + Flash.colors_max = 1; + Flash.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flash.brightness_min = CASTOR3_BRIGHTNESS_MIN; + Flash.brightness_max = CASTOR3_BRIGHTNESS_MAX; + Flash.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + Flash.colors.resize(1); + modes.push_back(Flash); + + /*---------------------------------------------------------*\ + | Double Flash — single color, speed + brightness | + \*---------------------------------------------------------*/ + mode DFlash; + DFlash.name = "Double Flash"; + DFlash.value = CASTOR3_STYLE_DFLASH; + DFlash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + DFlash.speed_min = CASTOR3_SPEED_MIN; + DFlash.speed_max = CASTOR3_SPEED_MAX; + DFlash.speed = CASTOR3_SPEED_DEFAULT; + DFlash.colors_min = 1; + DFlash.colors_max = 1; + DFlash.color_mode = MODE_COLORS_MODE_SPECIFIC; + DFlash.brightness_min = CASTOR3_BRIGHTNESS_MIN; + DFlash.brightness_max = CASTOR3_BRIGHTNESS_MAX; + DFlash.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + DFlash.colors.resize(1); + modes.push_back(DFlash); + + /*---------------------------------------------------------*\ + | Cycle — firmware colors, speed only | + \*---------------------------------------------------------*/ + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = CASTOR3_STYLE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.speed_min = CASTOR3_SPEED_MIN; + Cycle.speed_max = CASTOR3_SPEED_MAX; + Cycle.speed = CASTOR3_SPEED_DEFAULT; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.brightness_min = CASTOR3_BRIGHTNESS_MIN; + Cycle.brightness_max = CASTOR3_BRIGHTNESS_MAX; + Cycle.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + modes.push_back(Cycle); + + /*---------------------------------------------------------*\ + | Gradient — single color, speed + brightness | + | Note: b4 = brightness on wire (special case) | + \*---------------------------------------------------------*/ + mode Gradient; + Gradient.name = "Gradient"; + Gradient.value = CASTOR3_STYLE_GRADIENT; + Gradient.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Gradient.speed_min = CASTOR3_SPEED_MIN; + Gradient.speed_max = CASTOR3_SPEED_MAX; + Gradient.speed = CASTOR3_SPEED_DEFAULT; + Gradient.colors_min = 1; + Gradient.colors_max = 1; + Gradient.color_mode = MODE_COLORS_MODE_SPECIFIC; + Gradient.brightness_min = CASTOR3_BRIGHTNESS_MIN; + Gradient.brightness_max = CASTOR3_BRIGHTNESS_MAX; + Gradient.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + Gradient.colors.resize(1); + modes.push_back(Gradient); + + /*---------------------------------------------------------*\ + | Color Shift — 8-color palette, speed + brightness | + \*---------------------------------------------------------*/ + mode ColorShift; + ColorShift.name = "Color Shift"; + ColorShift.value = CASTOR3_STYLE_COLORSHIFT; + ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + ColorShift.speed_min = CASTOR3_SPEED_MIN; + ColorShift.speed_max = CASTOR3_SPEED_MAX; + ColorShift.speed = CASTOR3_SPEED_DEFAULT; + ColorShift.colors_min = 1; + ColorShift.colors_max = 8; + ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorShift.brightness_min = CASTOR3_BRIGHTNESS_MIN; + ColorShift.brightness_max = CASTOR3_BRIGHTNESS_MAX; + ColorShift.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + ColorShift.colors.resize(8); + ColorShift.colors[0] = ToRGBColor(0xFF, 0x00, 0x00); + ColorShift.colors[1] = ToRGBColor(0xFF, 0x72, 0x00); + ColorShift.colors[2] = ToRGBColor(0xFF, 0xFF, 0x00); + ColorShift.colors[3] = ToRGBColor(0x00, 0xFF, 0x00); + ColorShift.colors[4] = ToRGBColor(0x00, 0xFF, 0xFF); + ColorShift.colors[5] = ToRGBColor(0x00, 0x00, 0xFF); + ColorShift.colors[6] = ToRGBColor(0xFF, 0x00, 0xFF); + ColorShift.colors[7] = ToRGBColor(0xFF, 0x80, 0x80); + modes.push_back(ColorShift); + + /*---------------------------------------------------------*\ + | Wave — firmware colors, speed only | + \*---------------------------------------------------------*/ + mode Wave; + Wave.name = "Wave"; + Wave.value = CASTOR3_STYLE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Wave.speed_min = CASTOR3_SPEED_MIN; + Wave.speed_max = CASTOR3_SPEED_MAX; + Wave.speed = CASTOR3_SPEED_DEFAULT; + Wave.color_mode = MODE_COLORS_NONE; + Wave.brightness_min = CASTOR3_BRIGHTNESS_MIN; + Wave.brightness_max = CASTOR3_BRIGHTNESS_MAX; + Wave.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + modes.push_back(Wave); + + /*---------------------------------------------------------*\ + | Rainbow — firmware colors, speed only | + \*---------------------------------------------------------*/ + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = CASTOR3_STYLE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rainbow.speed_min = CASTOR3_SPEED_MIN; + Rainbow.speed_max = CASTOR3_SPEED_MAX; + Rainbow.speed = CASTOR3_SPEED_DEFAULT; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness_min = CASTOR3_BRIGHTNESS_MIN; + Rainbow.brightness_max = CASTOR3_BRIGHTNESS_MAX; + Rainbow.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + modes.push_back(Rainbow); + + /*---------------------------------------------------------*\ + | Tri-Color — 3-color palette, speed + brightness | + \*---------------------------------------------------------*/ + mode TriColor; + TriColor.name = "Tri-Color"; + TriColor.value = CASTOR3_STYLE_TRICOLOR; + TriColor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + TriColor.speed_min = CASTOR3_SPEED_MIN; + TriColor.speed_max = CASTOR3_SPEED_MAX; + TriColor.speed = CASTOR3_SPEED_DEFAULT; + TriColor.colors_min = 1; + TriColor.colors_max = 3; + TriColor.color_mode = MODE_COLORS_MODE_SPECIFIC; + TriColor.brightness_min = CASTOR3_BRIGHTNESS_MIN; + TriColor.brightness_max = CASTOR3_BRIGHTNESS_MAX; + TriColor.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + TriColor.colors.resize(3); + TriColor.colors[0] = ToRGBColor(0x00, 0x00, 0xFF); + TriColor.colors[1] = ToRGBColor(0x7D, 0x00, 0xFF); + TriColor.colors[2] = ToRGBColor(0xFF, 0x00, 0xFF); + modes.push_back(TriColor); + + /*---------------------------------------------------------*\ + | Spin — 3-color palette, speed + brightness | + \*---------------------------------------------------------*/ + mode Spin; + Spin.name = "Spin"; + Spin.value = CASTOR3_STYLE_SPIN; + Spin.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Spin.speed_min = CASTOR3_SPEED_MIN; + Spin.speed_max = CASTOR3_SPEED_MAX; + Spin.speed = CASTOR3_SPEED_DEFAULT; + Spin.colors_min = 1; + Spin.colors_max = 3; + Spin.color_mode = MODE_COLORS_MODE_SPECIFIC; + Spin.brightness_min = CASTOR3_BRIGHTNESS_MIN; + Spin.brightness_max = CASTOR3_BRIGHTNESS_MAX; + Spin.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + Spin.colors.resize(3); + Spin.colors[0] = ToRGBColor(0xFF, 0x00, 0xFE); + Spin.colors[1] = ToRGBColor(0x00, 0xFF, 0xFB); + Spin.colors[2] = ToRGBColor(0xFF, 0xFF, 0x00); + modes.push_back(Spin); + + /*---------------------------------------------------------*\ + | Switch — 2-color palette, speed + brightness | + \*---------------------------------------------------------*/ + mode Switch; + Switch.name = "Switch"; + Switch.value = CASTOR3_STYLE_SWITCH; + Switch.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Switch.speed_min = CASTOR3_SPEED_MIN; + Switch.speed_max = CASTOR3_SPEED_MAX; + Switch.speed = CASTOR3_SPEED_DEFAULT; + Switch.colors_min = 1; + Switch.colors_max = 2; + Switch.color_mode = MODE_COLORS_MODE_SPECIFIC; + Switch.brightness_min = CASTOR3_BRIGHTNESS_MIN; + Switch.brightness_max = CASTOR3_BRIGHTNESS_MAX; + Switch.brightness = CASTOR3_BRIGHTNESS_DEFAULT; + Switch.colors.resize(2); + Switch.colors[0] = ToRGBColor(0xFF, 0x00, 0xFE); + Switch.colors[1] = ToRGBColor(0x00, 0xFF, 0xFB); + modes.push_back(Switch); + + SetupZones(); +} + +RGBController_GigabyteCastor3::~RGBController_GigabyteCastor3() +{ + delete controller; +} + +void RGBController_GigabyteCastor3::SetupZones() +{ + /*---------------------------------------------------------*\ + | Single zone: LED ring + fans (not independently | + | addressable — fans follow the pump ring effect) | + | | + | This is a SINGLE zone because the protocol does not | + | expose per-LED addressing. All LEDs display the same | + | hardware effect simultaneously. | + \*---------------------------------------------------------*/ + zone led_ring; + led_ring.name = "LED Ring"; + led_ring.type = ZONE_TYPE_SINGLE; + led_ring.leds_min = 1; + led_ring.leds_max = 1; + led_ring.leds_count = 1; + led_ring.matrix_map = NULL; + zones.push_back(led_ring); + + led new_led; + new_led.name = "LED Ring"; + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_GigabyteCastor3::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | Fixed zone size — not resizable | + \*---------------------------------------------------------*/ +} + +void RGBController_GigabyteCastor3::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_GigabyteCastor3::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GigabyteCastor3::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GigabyteCastor3::DeviceUpdateMode() +{ + unsigned char style = (unsigned char)modes[active_mode].value; + + /*---------------------------------------------------------*\ + | Handle Off mode (sentinel value 0xFF) | + \*---------------------------------------------------------*/ + if(style == 0xFF) + { + controller->SetOff(); + return; + } + + /*---------------------------------------------------------*\ + | Determine speed, brightness, b4, b5, and color type | + | from the effect lookup table matching castor3.py | + \*---------------------------------------------------------*/ + unsigned char speed = CASTOR3_SPEED_DEFAULT; + unsigned char brightness = CASTOR3_BRIGHTNESS_DEFAULT; + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + speed = (unsigned char)modes[active_mode].speed; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + brightness = (unsigned char)modes[active_mode].brightness; + } + + /*---------------------------------------------------------*\ + | Look up b4, b5, and color_type per effect | + | These match the LED_EFFECTS dict in castor3.py | + \*---------------------------------------------------------*/ + unsigned char b4 = 0x02; + unsigned char b5 = 0x01; + castor3_color_type color_type = CASTOR3_COLORS_NONE; + + switch(style) + { + case CASTOR3_STYLE_STATIC: + case CASTOR3_STYLE_PULSE: + case CASTOR3_STYLE_FLASH: + case CASTOR3_STYLE_DFLASH: + b4 = 0x02; + b5 = 0x01; + color_type = CASTOR3_COLORS_SINGLE; + break; + + case CASTOR3_STYLE_GRADIENT: + /*-------------------------------------------------*\ + | Gradient: b4 = brightness on wire (special case) | + \*-------------------------------------------------*/ + b4 = brightness; + b5 = 0x01; + color_type = CASTOR3_COLORS_SINGLE; + break; + + case CASTOR3_STYLE_CYCLE: + case CASTOR3_STYLE_WAVE: + case CASTOR3_STYLE_RAINBOW: + b4 = 0x02; + b5 = 0x01; + color_type = CASTOR3_COLORS_NONE; + break; + + case CASTOR3_STYLE_COLORSHIFT: + b4 = 0x08; /* ClrCount=8 */ + b5 = 0x02; /* CmbIndex+1=2 */ + color_type = CASTOR3_COLORS_PALETTE; + break; + + case CASTOR3_STYLE_TRICOLOR: + case CASTOR3_STYLE_SPIN: + b4 = 0x02; + b5 = 0x01; + color_type = CASTOR3_COLORS_PALETTE; + break; + + case CASTOR3_STYLE_SWITCH: + b4 = 0x02; + b5 = 0x01; + color_type = CASTOR3_COLORS_PALETTE; + break; + } + + controller->SetEffect(style, speed, brightness, b4, b5, + color_type, modes[active_mode].colors); +} diff --git a/Controllers/GigabyteCastor3Controller/RGBController_GigabyteCastor3.h b/Controllers/GigabyteCastor3Controller/RGBController_GigabyteCastor3.h new file mode 100644 index 0000000..bb956ac --- /dev/null +++ b/Controllers/GigabyteCastor3Controller/RGBController_GigabyteCastor3.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteCastor3.h | +| | +| RGBController for Gigabyte Aorus Waterforce X II 360 | +| AIO Cooler (Castor3 controller) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteCastor3Controller.h" + +class RGBController_GigabyteCastor3 : public RGBController +{ +public: + RGBController_GigabyteCastor3(GigabyteCastor3Controller* controller_ptr); + ~RGBController_GigabyteCastor3(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + GigabyteCastor3Controller* controller; +}; diff --git a/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUController.cpp b/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUController.cpp new file mode 100644 index 0000000..6d1b7e1 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUController.cpp @@ -0,0 +1,113 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2BlackwellGPUController.cpp | +| | +| Driver for Gigabyte RGB Fusion 2 Blackwell GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "GigabyteRGBFusion2BlackwellGPUController.h" +#include "GigabyteRGBFusion2BlackwellGPUDefinitions.h" +#include "LogManager.h" + +using namespace std::chrono_literals; + +RGBFusion2BlackwellGPUController::RGBFusion2BlackwellGPUController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev, std::string dev_name, int gpu_layout) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; + this->gpu_layout = gpu_layout; +} + +RGBFusion2BlackwellGPUController::~RGBFusion2BlackwellGPUController() +{ + +} + +std::string RGBFusion2BlackwellGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string RGBFusion2BlackwellGPUController::GetDeviceName() +{ + return(name); +} + +void RGBFusion2BlackwellGPUController::SaveConfig() +{ + uint8_t data_pkt[64] = { 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + bus->i2c_write_block(dev, sizeof(data_pkt), data_pkt); +} + +void RGBFusion2BlackwellGPUController::SetMode(uint8_t type, uint8_t zone, uint8_t mode, fusion2_config zone_config) +{ + if(zone_config.numberOfColors == 0 && zone < RGB_FUSION_2_BLACKWELL_GPU_NUMBER_OF_ZONES) + this->zone_color[zone] = zone_config.colors[0]; + + /************************************************************************************\ + * * + * Packet (total size = 64 bytes) * + * TYPE MODE SPD BRT R G B 0 ZONE SZ0-8 * + * 0x12 0x01 0x08 0x06 0x0A 0xFF 0xFF 0x00 0x00 0x00 0x08 [R] [G] [B] [R] [G] [B] ... * + * * + * SZ is the amount of colors that will be sent in the format of 3 bytes RGB * + * * + \************************************************************************************/ + uint8_t zone_pkt[64] = {type, 0x01, mode, zone_config.speed, zone_config.brightness, (uint8_t)RGBGetRValue(this->zone_color[zone]), (uint8_t)RGBGetGValue(this->zone_color[zone]), (uint8_t)RGBGetBValue(this->zone_color[zone]), 0x00, zone, zone_config.numberOfColors, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + if(zone_config.numberOfColors > 0) + { + int currentPos = 12; + switch(gpu_layout) + { + case RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT: + case RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT: + currentPos = 11; + break; + default: + break; + } + + for(uint8_t i = 0; i < zone_config.numberOfColors; i++) + { + zone_pkt[currentPos + 0] = RGBGetRValue(zone_config.colors[i]); + zone_pkt[currentPos + 1] = RGBGetGValue(zone_config.colors[i]); + zone_pkt[currentPos + 2] = RGBGetBValue(zone_config.colors[i]); + currentPos += 3; + } + } + + bus->i2c_write_block(dev, sizeof(zone_pkt), zone_pkt); +} + +void RGBFusion2BlackwellGPUController::SetZone(uint8_t zone, uint8_t mode, fusion2_config zone_config) +{ + if(mode == RGB_FUSION2_BLACKWELL_GPU_MODE_BREATHING) + zone_config.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + + switch(gpu_layout) + { + case RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT: + if(mode == RGB_FUSION2_BLACKWELL_GPU_MODE_DIRECT) + mode = RGB_FUSION2_BLACKWELL_GPU_MODE_STATIC; + break; + default: + break; + } + + uint8_t type = RGB_FUSION2_BLACKWELL_GPU_REG_COLOR; + if(mode != RGB_FUSION2_BLACKWELL_GPU_MODE_DIRECT) + type = RGB_FUSION2_BLACKWELL_GPU_REG_MODE; + + SetMode(type, zone, mode, zone_config); +} diff --git a/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUController.h b/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUController.h new file mode 100644 index 0000000..3243c3b --- /dev/null +++ b/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUController.h @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2BlackwellGPUController.h | +| | +| Driver for Gigabyte RGB Fusion 2 Blackwell GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" +#include "GigabyteRGBFusion2BlackwellGPUDefinitions.h" + +typedef unsigned char rgb_fusion_dev_id; + +struct fusion2_config +{ + uint8_t brightness; + RGBColor colors[8]; + uint8_t numberOfColors; + uint8_t speed; + uint8_t direction; +}; + +enum +{ + RGB_FUSION2_BLACKWELL_GPU_REG_MODE = 0x12, // Limits updates to at most 9 per second + RGB_FUSION2_BLACKWELL_GPU_REG_COLOR = 0x16, // Used for 'Intelligent' mode, faster updates (used for direct mode) +}; + +enum +{ + RGB_FUSION2_BLACKWELL_GPU_MODE_DIRECT = 0x00, // Used for Intelligent mode (0x16) and Off (0x12) + RGB_FUSION2_BLACKWELL_GPU_MODE_STATIC = 0x01, + RGB_FUSION2_BLACKWELL_GPU_MODE_BREATHING = 0x02, + RGB_FUSION2_BLACKWELL_GPU_MODE_FLASHING = 0x03, + RGB_FUSION2_BLACKWELL_GPU_MODE_DUAL_FLASHING = 0x04, + RGB_FUSION2_BLACKWELL_GPU_MODE_COLOR_CYCLE = 0x05, + RGB_FUSION2_BLACKWELL_GPU_MODE_WAVE = 0x06, // Not available to Eagle/Aero + RGB_FUSION2_BLACKWELL_GPU_MODE_GRADIENT = 0x07, // Not available to Eagle/Aero + RGB_FUSION2_BLACKWELL_GPU_MODE_COLOR_SHIFT = 0x08, // Not available to Eagle/Aero + RGB_FUSION2_BLACKWELL_GPU_MODE_TRICOLOR = 0x09, // Available to Waterforce + RGB_FUSION2_BLACKWELL_GPU_MODE_DAZZLE = 0x0A, // Not available to Eagle/Aero/Waterforce + RGB_FUSION2_BLACKWELL_GPU_MODE_CLAWS = 0x0C, // Available to AORUS 5080 MASTER +}; + +enum +{ + RGB_FUSION2_BLACKWELL_GPU_SPEED_SLOWEST = 0x01, + RGB_FUSION2_BLACKWELL_GPU_SPEED_NORMAL = 0x03, + RGB_FUSION2_BLACKWELL_GPU_SPEED_FASTEST = 0x06 +}; + +enum +{ + RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN = 0x01, + RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX = 0x0A +}; + +enum +{ + RGB_FUSION2_BLACKWELL_GPU_SINGLE_ZONE = 0, + RGB_FUSION2_BLACKWELL_GPU_GAMING_LAYOUT = 1, + RGB_FUSION2_BLACKWELL_GPU_WATERFORCE_LAYOUT = 2, + RGB_FUSION2_BLACKWELL_GPU_AORUS_WATERFORCE_LAYOUT = 3, + RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT = 4, + RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT = 5, +}; + +class RGBFusion2BlackwellGPUController +{ +public: + RGBFusion2BlackwellGPUController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev, std::string dev_name, int gpu_layout); + ~RGBFusion2BlackwellGPUController(); + + RGBColor zone_color[RGB_FUSION_2_BLACKWELL_GPU_NUMBER_OF_ZONES]; + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SaveConfig(); + + void SetZone(uint8_t zone, uint8_t mode, fusion2_config zone_config); + void SetMode(uint8_t type, uint8_t zone, uint8_t mode, fusion2_config zone_config); + +private: + i2c_smbus_interface* bus; + rgb_fusion_dev_id dev; + std::string name; + int gpu_layout; + +}; diff --git a/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUControllerDetect.cpp b/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUControllerDetect.cpp new file mode 100644 index 0000000..9158f2c --- /dev/null +++ b/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUControllerDetect.cpp @@ -0,0 +1,238 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2BlackwellGPUControllerDetect.cpp | +| | +| Detector for Gigabyte RGB Fusion 2 Blackwell GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "GigabyteRGBFusion2BlackwellGPUController.h" +#include "LogManager.h" +#include "RGBController_GigabyteRGBFusion2BlackwellGPU.h" +#include "i2c_amd_gpu.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +#define GIGABYTEGPU_CONTROLLER_NAME3 "Gigabyte RGB Fusion2 Blackwell GPU" + +/******************************************************************************************\ +* * +* TestForGigabyteRGBFusion2BlackwellGPUController * +* * +* Tests the given address to see if an RGB Fusion2 controller exists there. First * +* does a quick write to test for a response * +* * +\******************************************************************************************/ + +bool TestForGigabyteRGBFusion2BlackwellGPUController(i2c_smbus_interface* bus, unsigned char address) +{ + if(bus->pci_vendor == AMD_GPU_VEN && !is_amd_gpu_i2c_bus(bus)) + { + return false; + } + + bool pass = false; + int res, pktsz; + const int read_sz = 4; + const int write_sz = 64; //0x40 + uint8_t data_pkt[write_sz] = { 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t data_readpkt[read_sz] = {}; + + res = bus->i2c_write_block(address, write_sz, data_pkt); + + pass = true; + + pktsz = read_sz; + res = bus->i2c_read_block(address, &pktsz, data_readpkt); + + //What we have seen returned so far... + //GeForce RTX 5070 Ti Eagle OC 16G 0x01 0x01 0x01 0x00 + //GeForce RTX 5070 Ti Gaming OC 16G 0x01 0x01 0x01 0x00 + //GeForce RTX 5070 Gaming OC 12G 0x01 0x01 0x01 0x00 + //GeForce RTX 5080 AORUS MASTER 16G 0x01 0x01 0x01 0x10 + + if(res < 0 || data_readpkt[0] != 0x01 || data_readpkt[1] != 0x01 || data_readpkt[2] != 0x01) + { + // Assemble C-string with respons for debugging + std::string text = ""; + + for(int idx = 0; idx < read_sz; ++idx) + { + char str[6]; + snprintf(str, 6, " 0x%02X", data_readpkt[idx]); + text.append(str); + } + + LOG_DEBUG("[%s] at address 0x%02X invalid. Expected 0x01 0x01 0x01 [0x*] but received:%s", GIGABYTEGPU_CONTROLLER_NAME3, address, text.c_str()); + pass = false; + } + + return(pass); +} /* TestForRGBFusion2BlackwellGPUController() */ + +/*******************************************************************************************\ +* * +* DetectGigabyteRGBFusion2BlackwellGPUControllers * +* * +* Detect GigabyteRGB Fusion2 controllers with a specified layout on the enumerated * +* I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion2 device is connected * +* dev - I2C address of RGB Fusion2 device * +* * +\*******************************************************************************************/ + +void DetectGigabyteRGBFusion2BlackwellGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name, uint8_t led_zones) +{ + // Check for RGB Fusion2 controller + if(TestForGigabyteRGBFusion2BlackwellGPUController(bus, i2c_addr)) + { + RGBFusion2BlackwellGPUController* controller = new RGBFusion2BlackwellGPUController(bus, i2c_addr, name, led_zones); + RGBController_RGBFusion2BlackwellGPU* rgb_controller = new RGBController_RGBFusion2BlackwellGPU(controller, led_zones); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectGigabyteRGBFusion2BlackwellGPUControllers() */ + +/*******************************************************************************************\ +* * +* DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers * +* * +* Detect GigabyteRGB Fusion2 controllers with one zone on the enumerated I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion2 device is connected * +* dev - I2C address of RGB Fusion2 device * +* * +\*******************************************************************************************/ + +void DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + DetectGigabyteRGBFusion2BlackwellGPUControllers(bus, i2c_addr, name, RGB_FUSION2_BLACKWELL_GPU_SINGLE_ZONE); +} /* DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers() */ + +/*******************************************************************************************\ +* * +* DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers * +* * +* Detect GigabyteRGB Fusion2 controllers with gaming layouts on the enumerated I2C * +* busses. * +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion2 device is connected * +* dev - I2C address of RGB Fusion2 device * +* * +\*******************************************************************************************/ + +void DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + DetectGigabyteRGBFusion2BlackwellGPUControllers(bus, i2c_addr, name, RGB_FUSION2_BLACKWELL_GPU_GAMING_LAYOUT); +} /* DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers() */ + +/*******************************************************************************************\ +* * +* DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers * +* * +* Detect GigabyteRGB Fusion2 controllers with waterforce layouts on the enumerated * +* I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion2 device is connected * +* dev - I2C address of RGB Fusion2 device * +* * +\*******************************************************************************************/ + +void DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + DetectGigabyteRGBFusion2BlackwellGPUControllers(bus, i2c_addr, name, RGB_FUSION2_BLACKWELL_GPU_WATERFORCE_LAYOUT); +} /* DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers() */ + +/*******************************************************************************************\ +* * +* DetectGigabyteRGBFusion2BlackwellAorusWaterforceLayoutGPUControllers * +* * +* Detect GigabyteRGB Fusion2 controllers with AORUS waterforce layout on the * +* enumerated I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion2 device is connected * +* dev - I2C address of RGB Fusion2 device * +* * +\*******************************************************************************************/ + +void DetectGigabyteRGBFusion2BlackwellAorusWaterforceLayoutGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + DetectGigabyteRGBFusion2BlackwellGPUControllers(bus, i2c_addr, name, RGB_FUSION2_BLACKWELL_GPU_AORUS_WATERFORCE_LAYOUT); +} /* DetectGigabyteRGBFusion2BlackwellAorusWaterforceLayoutGPUControllers() */ + +/*******************************************************************************************\ +* * +* DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers * +* * +* Detect GigabyteRGB Fusion2 controllers with AORUS waterforce layout on the * +* enumerated I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion2 device is connected * +* dev - I2C address of RGB Fusion2 device * +* * +\*******************************************************************************************/ + +void DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + DetectGigabyteRGBFusion2BlackwellGPUControllers(bus, i2c_addr, name, RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT); +} /* DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers() */ + +/*******************************************************************************************\ +* * +* DetectGigabyteRGBFusion2BlackwellAorusMaster5090DV2IceLayoutGPUControllers * +* * +* Detect GigabyteRGB Fusion2 controllers with AORUS master 5090 D V2 ICE layout on * +* enumerated I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion2 device is connected * +* dev - I2C address of RGB Fusion2 device * +* * +\*******************************************************************************************/ + +void DetectGigabyteRGBFusion2BlackwellAorusMaster5090DV2IceLayoutGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + DetectGigabyteRGBFusion2BlackwellGPUControllers(bus, i2c_addr, name, RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT); +} /* DetectGigabyteRGBFusion2BlackwellAorusMaster5090DV2IceLayoutGPUControllers() */ + +/*-----------------------------------------*\ +| Nvidia GPUs | +\*-----------------------------------------*/ + +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5060 Ti Gaming OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5060TI_GAMING_OC_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Aero OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_AERO_OC_12G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Eagle OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_EAGLE_OC_12G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Eagle OC ICE", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_EAGLE_OC_ICE_12G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070_GAMING_OC_12G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Eagle OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_EAGLE_OC_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Eagle OC ICE", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_EAGLE_OC_ICE_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Aero OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_AERO_OC_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5070 Ti Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5070TI_GAMING_OC_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 Aero OC", DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_AERO_OC_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_GAMING_OC_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 MASTER", DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_MASTER_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellAorusWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5080 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5080_XTREME_WATERFORCE_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5080 MASTER ICE", DetectGigabyteRGBFusion2BlackwellAorusMaster5080LayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5080_MASTER_ICE_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 Gaming OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_GAMING_OC_32G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_XTREME_WATERFORCE_32G_SUB_DEV1, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 XTREME WATERFORCE", DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_XTREME_WATERFORCE_32G_SUB_DEV2, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 MASTER", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090_MASTER_32G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 MASTER ICE", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090_MASTER_ICE_32G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 D V2 MASTER ICE", DetectGigabyteRGBFusion2BlackwellAorusMaster5090DV2IceLayoutGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090D_V2_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090D_V2_MASTER_ICE_24G_SUB_DEV, 0x75); + +/*-----------------------------------------*\ +| AMD GPUs | +\*-----------------------------------------*/ + +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9060 XT GAMING", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI44_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9060XT_GAMING_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9060 XT GAMING OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI44_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9060XT_GAMING_OC_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS Radeon RX 9070 XT Elite", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX9070XT_ELITE_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 XT GAMING OC", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070XT_GAMING_OC_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 XT GAMING OC ICE", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070XT_GAMING_OC_ICE_16G_SUB_DEV, 0x75); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 XT GAMING", DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070XT_GAMING_16G_SUB_DEV, 0x75); + + diff --git a/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUDefinitions.h b/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUDefinitions.h new file mode 100644 index 0000000..0188c5a --- /dev/null +++ b/Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUDefinitions.h @@ -0,0 +1,13 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2BlackwellGPUDefinitions.h | +| | +| Definitions for Gigabyte RGB Fusion 2 Blackwell GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#define RGB_FUSION_2_BLACKWELL_GPU_NUMBER_OF_ZONES 6 +#define RGB_FUSION_2_BLACKWELL_AORUS_MASTER_FAN_LEDS 8 diff --git a/Controllers/GigabyteRGBFusion2BlackwellGPUController/RGBController_GigabyteRGBFusion2BlackwellGPU.cpp b/Controllers/GigabyteRGBFusion2BlackwellGPUController/RGBController_GigabyteRGBFusion2BlackwellGPU.cpp new file mode 100644 index 0000000..b3d596b --- /dev/null +++ b/Controllers/GigabyteRGBFusion2BlackwellGPUController/RGBController_GigabyteRGBFusion2BlackwellGPU.cpp @@ -0,0 +1,595 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusion2BlackwellGPU.cpp | +| | +| RGBController for Gigabyte RGB Fusion 2 Blackwell GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GigabyteRGBFusion2BlackwellGPU.h" +#include "LogManager.h" +#include "GigabyteRGBFusion2BlackwellGPUDefinitions.h" + +/**------------------------------------------------------------------*\ + @name Gigabyte Fusion 2 Blackwell GPU + @category GPU + @type I2C + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGigabyteRGBFusion2BlackwellSingleZoneGPUControllers,DetectGigabyteRGBFusion2BlackwellGamingLayoutGPUControllers,DetectGigabyteRGBFusion2BlackwellWaterforceLayoutGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RGBFusion2BlackwellGPU::RGBController_RGBFusion2BlackwellGPU(RGBFusion2BlackwellGPUController* controller_ptr, uint8_t led_layout) +{ + controller = controller_ptr; + gpu_layout = led_layout; + + name = controller->GetDeviceName(); + vendor = "Gigabyte"; + description = "Gigabyte RGB Fusion 2 Blackwell GPU Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Static; + Static.name = "Static"; + Static.value = RGB_FUSION2_BLACKWELL_GPU_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + Static.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + Static.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(Static); + + // Some GPU models (Gaming) dont maintain the colors but it has faster updates, useful for Effects (and the reason it has to be named Direct) + mode Direct; + Direct.name = "Direct"; + Direct.value = RGB_FUSION2_BLACKWELL_GPU_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + Direct.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + Direct.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Pulse"; + Breathing.value = RGB_FUSION2_BLACKWELL_GPU_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Breathing.speed_min = RGB_FUSION2_BLACKWELL_GPU_SPEED_SLOWEST; + Breathing.speed_max = RGB_FUSION2_BLACKWELL_GPU_SPEED_FASTEST; + Breathing.speed = RGB_FUSION2_BLACKWELL_GPU_SPEED_NORMAL; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + Breathing.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + Breathing.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flash"; + Flashing.value = RGB_FUSION2_BLACKWELL_GPU_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Flashing.speed_min = RGB_FUSION2_BLACKWELL_GPU_SPEED_SLOWEST; + Flashing.speed_max = RGB_FUSION2_BLACKWELL_GPU_SPEED_FASTEST; + Flashing.speed = RGB_FUSION2_BLACKWELL_GPU_SPEED_NORMAL; + Flashing.color_mode = MODE_COLORS_PER_LED; + Flashing.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + Flashing.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + Flashing.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(Flashing); + + mode DualFlashing; + DualFlashing.name = "Double Flash"; + DualFlashing.value = RGB_FUSION2_BLACKWELL_GPU_MODE_DUAL_FLASHING; + DualFlashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + DualFlashing.speed_min = RGB_FUSION2_BLACKWELL_GPU_SPEED_SLOWEST; + DualFlashing.speed_max = RGB_FUSION2_BLACKWELL_GPU_SPEED_FASTEST; + DualFlashing.speed = RGB_FUSION2_BLACKWELL_GPU_SPEED_NORMAL; + DualFlashing.color_mode = MODE_COLORS_PER_LED; + DualFlashing.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + DualFlashing.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + DualFlashing.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(DualFlashing); + + mode SpectrumCycle; + SpectrumCycle.name = "Color Cycle"; + SpectrumCycle.value = RGB_FUSION2_BLACKWELL_GPU_MODE_COLOR_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + SpectrumCycle.speed_min = RGB_FUSION2_BLACKWELL_GPU_SPEED_SLOWEST; + SpectrumCycle.speed_max = RGB_FUSION2_BLACKWELL_GPU_SPEED_FASTEST; + SpectrumCycle.speed = RGB_FUSION2_BLACKWELL_GPU_SPEED_NORMAL; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + SpectrumCycle.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + SpectrumCycle.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(SpectrumCycle); + + if(led_layout != RGB_FUSION2_BLACKWELL_GPU_SINGLE_ZONE) + { + mode Wave; + Wave.name = "Wave"; + Wave.value = RGB_FUSION2_BLACKWELL_GPU_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Wave.speed_min = RGB_FUSION2_BLACKWELL_GPU_SPEED_SLOWEST; + Wave.speed_max = RGB_FUSION2_BLACKWELL_GPU_SPEED_FASTEST; + Wave.speed = RGB_FUSION2_BLACKWELL_GPU_SPEED_NORMAL; + Wave.color_mode = MODE_COLORS_NONE; + Wave.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + Wave.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + Wave.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(Wave); + + mode Gradient; + Gradient.name = "Gradient"; + Gradient.value = RGB_FUSION2_BLACKWELL_GPU_MODE_GRADIENT; + Gradient.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Gradient.speed_min = RGB_FUSION2_BLACKWELL_GPU_SPEED_SLOWEST; + Gradient.speed_max = RGB_FUSION2_BLACKWELL_GPU_SPEED_FASTEST; + Gradient.speed = RGB_FUSION2_BLACKWELL_GPU_SPEED_NORMAL; + Gradient.color_mode = MODE_COLORS_PER_LED; + Gradient.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + Gradient.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + Gradient.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(Gradient); + + mode ColorShift; + ColorShift.name = "Color Shift"; + ColorShift.value = RGB_FUSION2_BLACKWELL_GPU_MODE_COLOR_SHIFT; + ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + ColorShift.speed_min = RGB_FUSION2_BLACKWELL_GPU_SPEED_SLOWEST; + ColorShift.speed_max = RGB_FUSION2_BLACKWELL_GPU_SPEED_FASTEST; + ColorShift.speed = RGB_FUSION2_BLACKWELL_GPU_SPEED_NORMAL; + ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorShift.colors_min = 1; + ColorShift.colors_max = 8; + ColorShift.colors.resize(8); + ColorShift.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + ColorShift.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + ColorShift.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(ColorShift); + + if(led_layout != RGB_FUSION2_BLACKWELL_GPU_AORUS_WATERFORCE_LAYOUT) + { + mode Dazzle; + Dazzle.name = "Dazzle"; + Dazzle.value = RGB_FUSION2_BLACKWELL_GPU_MODE_DAZZLE; + Dazzle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Dazzle.speed_min = RGB_FUSION2_BLACKWELL_GPU_SPEED_SLOWEST; + Dazzle.speed_max = RGB_FUSION2_BLACKWELL_GPU_SPEED_FASTEST; + Dazzle.speed = RGB_FUSION2_BLACKWELL_GPU_SPEED_NORMAL; + Dazzle.color_mode = MODE_COLORS_MODE_SPECIFIC; + Dazzle.colors_min = 1; + Dazzle.colors_max = 8; + Dazzle.colors.resize(8); + Dazzle.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + Dazzle.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + Dazzle.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(Dazzle); + } + } + + if(led_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT) + { + mode Claws; + Claws.name = "Claws"; + Claws.value = RGB_FUSION2_BLACKWELL_GPU_MODE_CLAWS; + Claws.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Claws.speed_min = RGB_FUSION2_BLACKWELL_GPU_SPEED_SLOWEST; + Claws.speed_max = RGB_FUSION2_BLACKWELL_GPU_SPEED_FASTEST; + Claws.speed = RGB_FUSION2_BLACKWELL_GPU_SPEED_NORMAL; + Claws.color_mode = MODE_COLORS_MODE_SPECIFIC; + Claws.brightness_min = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MIN; + Claws.brightness_max = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + Claws.brightness = RGB_FUSION2_BLACKWELL_GPU_BRIGHTNESS_MAX; + modes.push_back(Claws); + + for(size_t i = 0; i < modes.size(); i++) + { + if(modes[i].color_mode == MODE_COLORS_PER_LED) + { + modes[i].colors_min = 1; + modes[i].colors_max = RGB_FUSION_2_BLACKWELL_AORUS_MASTER_FAN_LEDS; + modes[i].colors.resize(RGB_FUSION_2_BLACKWELL_AORUS_MASTER_FAN_LEDS); + } + } + } + + RGBController_RGBFusion2BlackwellGPU::SetupZones(); +} + +RGBController_RGBFusion2BlackwellGPU::~RGBController_RGBFusion2BlackwellGPU() +{ + delete controller; +} + +void RGBController_RGBFusion2BlackwellGPU::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only allows setting the entire zone for all | + | LED's in the zone and does not allow per LED control. | + \*---------------------------------------------------------*/ + if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_SINGLE_ZONE) + { + zone new_zone; + led new_led; + + new_zone.name = "Side"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + + new_led.name = new_zone.name; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(new_led); + zones.push_back(new_zone); + } + else if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_GAMING_LAYOUT) + { + for(uint8_t zone_idx = 0; zone_idx < 4; zone_idx++) + { + zone new_zone; + led new_led; + + switch(zone_idx) + { + case 0: + new_zone.name = "Right fan"; + break; + + case 1: + new_zone.name = "Left fan"; + break; + + case 2: + new_zone.name = "Center fan"; + break; + + case 3: + new_zone.name = "Side"; + break; + } + + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + + new_led.name = new_zone.name; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(new_led); + zones.push_back(new_zone); + } + } + else if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_WATERFORCE_LAYOUT) + { + for(uint8_t zone_idx = 0; zone_idx < 2; zone_idx++) + { + zone new_zone; + led new_led; + + switch(zone_idx) + { + case 0: + new_zone.name = "Waterblock"; + break; + + case 1: + new_zone.name = "Backplate"; + break; + } + + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + + new_led.name = new_zone.name; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(new_led); + zones.push_back(new_zone); + } + } + else if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_WATERFORCE_LAYOUT) + { + /*---------------------------------------------------------*\ + | Skip zone 0 - it doesn't exist on this card variant | + | Only add zones 1, 2, 3, 4 to the UI | + \*---------------------------------------------------------*/ + for(uint8_t zone_idx = 1; zone_idx < 5; zone_idx++) + { + zone new_zone; + led new_led; + + switch(zone_idx) + { + case 1: + new_zone.name = "Bottom Logo"; + break; + + case 2: + new_zone.name = "Radiator Fans"; + break; + + case 3: + new_zone.name = "Top Logo"; + break; + + case 4: + new_zone.name = "Side Logo"; + break; + } + + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + + new_led.name = new_zone.name; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(new_led); + zones.push_back(new_zone); + } + } + else if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT) + { + const char * fan_names[] = { "Right Fan", "Left Fan", "Middle Fan" }; + for(int i = 0; i < 3; i++) + { + zone fan_zone; + fan_zone.name = fan_names[i]; + fan_zone.type = ZONE_TYPE_LINEAR; + fan_zone.leds_min = RGB_FUSION_2_BLACKWELL_AORUS_MASTER_FAN_LEDS; + fan_zone.leds_max = RGB_FUSION_2_BLACKWELL_AORUS_MASTER_FAN_LEDS; + fan_zone.leds_count = RGB_FUSION_2_BLACKWELL_AORUS_MASTER_FAN_LEDS; + fan_zone.matrix_map = NULL; + zones.push_back(fan_zone); + + for(unsigned int led_idx = 0; led_idx < fan_zone.leds_count; led_idx++) + { + led new_led; + new_led.name = fan_names[i]; + new_led.name.append(" LED "); + new_led.name.append(std::to_string(led_idx + 1)); + leds.push_back(new_led); + } + } + + zone side_logo; + side_logo.name = "Side Logo"; + side_logo.type = ZONE_TYPE_SINGLE; + side_logo.leds_min = 1; + side_logo.leds_max = 1; + side_logo.leds_count = 1; + side_logo.matrix_map = NULL; + zones.push_back(side_logo); + + led logo_led; + logo_led.name = "Side Logo"; + leds.push_back(logo_led); + + zone top_logo; + top_logo.name = "Top Logo"; + top_logo.type = ZONE_TYPE_SINGLE; + top_logo.leds_min = 1; + top_logo.leds_max = 1; + top_logo.leds_count = 1; + top_logo.matrix_map = NULL; + zones.push_back(top_logo); + + led top_led; + top_led.name = "Top Logo"; + leds.push_back(top_led); + } + + else if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT) + { + const char * fan_names[] = { "Right Fan", "Left Fan", "Middle Fan" }; + for(int i = 0; i < 3; i++) + { + zone fan_zone; + fan_zone.name = fan_names[i]; + fan_zone.type = ZONE_TYPE_SINGLE; + fan_zone.leds_min = 1; + fan_zone.leds_max = 1; + fan_zone.leds_count = 1; + fan_zone.matrix_map = NULL; + zones.push_back(fan_zone); + + led new_led; + new_led.name = fan_names[i]; + leds.push_back(new_led); + } + + zone backplate; + backplate.name = "Backplate"; + backplate.type = ZONE_TYPE_SINGLE; + backplate.leds_min = 1; + backplate.leds_max = 1; + backplate.leds_count = 1; + backplate.matrix_map = NULL; + zones.push_back(backplate); + + led bp_led; + bp_led.name = "Backplate"; + leds.push_back(bp_led); + + zone side_logo; + side_logo.name = "Side Logo"; + side_logo.type = ZONE_TYPE_SINGLE; + side_logo.leds_min = 1; + side_logo.leds_max = 1; + side_logo.leds_count = 1; + side_logo.matrix_map = NULL; + zones.push_back(side_logo); + + led sl_led; + sl_led.name = "Side Logo"; + leds.push_back(sl_led); + } + + SetupColors(); +} + +void RGBController_RGBFusion2BlackwellGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RGBFusion2BlackwellGPU::DeviceUpdateLEDs() +{ + fusion2_config zone_config; + zone_config.brightness = modes[active_mode].brightness; + zone_config.speed = modes[active_mode].speed; + zone_config.direction = modes[active_mode].direction; + zone_config.numberOfColors = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + zone_config.numberOfColors = (uint8_t)modes[active_mode].colors.size(); + } + + uint8_t gpu_zones; + switch(gpu_layout) // replicating GCC that sends more packets even when there is less zones + { + case RGB_FUSION2_BLACKWELL_GPU_SINGLE_ZONE: + gpu_zones = 1; + break; + + case RGB_FUSION2_BLACKWELL_GPU_GAMING_LAYOUT: + gpu_zones = 6; + break; + + case RGB_FUSION2_BLACKWELL_GPU_WATERFORCE_LAYOUT: + gpu_zones = 3; + break; + + case RGB_FUSION2_BLACKWELL_GPU_AORUS_WATERFORCE_LAYOUT: + gpu_zones = 5; // Hardware zones 0-4, but zone 0 is skipped + break; + + case RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT: + gpu_zones = 6; // Zones 4 and 5 refer to ui zone 4 + break; + + case RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT: + gpu_zones = 6; + break; + + default: + LOG_TRACE("[%s] Invalid GPU layout (%d) when updating LEDs.", name.c_str(), gpu_layout); + return; // should not happen + } + + for(uint8_t zone_idx = 0; zone_idx < gpu_zones; zone_idx++) + { + /*---------------------------------------------------------*\ + | For AORUS WATERFORCE layout, map UI zones to hardware | + | UI zone 0 -> HW zone 1 (Bottom Logo) | + | UI zone 1 -> HW zone 2 (Radiator Fans) | + | UI zone 2 -> HW zone 3 (Top Logo) | + | UI zone 3 -> HW zone 4 (Side Logo) | + | Skip HW zone 0 (doesn't exist on this card) | + \*---------------------------------------------------------*/ + uint8_t hardware_zone_idx = zone_idx; + uint8_t ui_zone_idx = zone_idx; + + if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_WATERFORCE_LAYOUT) + { + if(zone_idx == 0) + { + continue; // Skip hardware zone 0 + } + ui_zone_idx = zone_idx - 1; // Map: HW zone 1->UI zone 0, HW zone 2->UI zone 1, HW zone 3->UI zone 2, HW zone 4->UI zone 3 + } + else if(gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5080_LAYOUT || + gpu_layout == RGB_FUSION2_BLACKWELL_GPU_AORUS_MASTER_5090D_V2_ICE_LAYOUT) + { + if(zone_idx == 5) + { + ui_zone_idx = zone_idx - 1; // Map: HW zone 5->UI zone 4 + } + } + + if(ui_zone_idx >= zones.size()) + { + zone_config.colors[0] = colors.back(); + } + else + { + /*---------------------------------------------------------*\ + | Equivalent of: | + | zone_config.colors[0] = colors[ui_zone_idx]; | + | when all zones have 1 led. | + \*---------------------------------------------------------*/ + uint8_t led_start = 0; + for(uint8_t i = 0; i < ui_zone_idx; i++) + { + led_start += zones[i].leds_count; + } + for(unsigned int i = 0; i < zones[ui_zone_idx].leds_count; i++) + { + zone_config.colors[i] = colors[led_start + i]; + } + + /*---------------------------------------------------------*\ + | If not all led are the same color, then we must pass all | + | led colors in the i2c write. | + \*---------------------------------------------------------*/ + if(!std::all_of(zone_config.colors, zone_config.colors + zones[ui_zone_idx].leds_count, [first = zone_config.colors[0]](RGBColor x) { return x == first; })) + { + zone_config.numberOfColors = zones[ui_zone_idx].leds_count; + } + } + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + for(uint8_t i = 0; i < zone_config.numberOfColors; i++) // specific for MODE_COLORS_MODE_SPECIFIC + { + zone_config.colors[i] = modes[active_mode].colors[i]; + } + } + + controller->SetZone(hardware_zone_idx, modes[active_mode].value, zone_config); + } +} + +void RGBController_RGBFusion2BlackwellGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RGBFusion2BlackwellGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RGBFusion2BlackwellGPU::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_RGBFusion2BlackwellGPU::DeviceSaveMode() +{ + controller->SaveConfig(); +} diff --git a/Controllers/GigabyteRGBFusion2BlackwellGPUController/RGBController_GigabyteRGBFusion2BlackwellGPU.h b/Controllers/GigabyteRGBFusion2BlackwellGPUController/RGBController_GigabyteRGBFusion2BlackwellGPU.h new file mode 100644 index 0000000..512d4de --- /dev/null +++ b/Controllers/GigabyteRGBFusion2BlackwellGPUController/RGBController_GigabyteRGBFusion2BlackwellGPU.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusion2BlackwellGPU.h | +| | +| RGBController for Gigabyte RGB Fusion 2 Blackwell GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteRGBFusion2BlackwellGPUController.h" + +class RGBController_RGBFusion2BlackwellGPU : public RGBController +{ +public: + RGBController_RGBFusion2BlackwellGPU(RGBFusion2BlackwellGPUController* controller_ptr, uint8_t led_layout); + ~RGBController_RGBFusion2BlackwellGPU(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + RGBFusion2BlackwellGPUController* controller; + uint8_t gpu_layout; +}; diff --git a/Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMController.cpp b/Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMController.cpp new file mode 100644 index 0000000..399b48d --- /dev/null +++ b/Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMController.cpp @@ -0,0 +1,141 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2DRAMController.cpp | +| | +| Driver for Gigabyte Aorus RGB Fusion 2 RAM | +| | +| Adam Honse (CalcProgrammer1) 07 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "GigabyteRGBFusion2DRAMController.h" + +RGBFusion2DRAMController::RGBFusion2DRAMController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev) +{ + /*-----------------------------------------------------*\ + | Initialize pointers | + \*-----------------------------------------------------*/ + this->bus = bus; + this->dev = dev; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(led_data, 0, sizeof(led_data)); + + /*-----------------------------------------------------*\ + | Initialize controller with 6 LEDs | + | This is hard coded for Aorus RGB RAM | + \*-----------------------------------------------------*/ + led_count = 6; + + direct_initialized = false; +} + +RGBFusion2DRAMController::~RGBFusion2DRAMController() +{ + +} + +unsigned int RGBFusion2DRAMController::GetLEDCount() +{ + return(led_count); +} + +std::string RGBFusion2DRAMController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +void RGBFusion2DRAMController::Apply() +{ + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_2_DRAM_APPLY_ADDR, RGB_FUSION_2_DRAM_ACTION_APPLY); +} + +void RGBFusion2DRAMController::SetLEDEffect + ( + unsigned int led, + int mode, + unsigned int brightness, + unsigned int /*speed*/, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + bool truncate_packet = false; + + if(mode == RGB_FUSION_2_DRAM_MODE_DIRECT) + { + /*-----------------------------------------------------*\ + | In Direct mode, set one LED at a time | + \*-----------------------------------------------------*/ + led_data[RGB_FUSION_2_DRAM_LED_EN_MASK] = (1 << led); + + /*-----------------------------------------------------*\ + | Hack for Direct mode | + \*-----------------------------------------------------*/ + led_data[16] = 1; + led_data[22] = 2; + led_data[29] = 1; + led_data[30] = 1; + + /*-----------------------------------------------------*\ + | If bytes 15-31 have already been set, we can speed up | + | repeat direct mode packets by only sending the portion| + | that changes, in this case bytes 0-14. If direct mode| + | has already been initialized, truncate the packet by | + | only sending the first 15 bytes. | + \*-----------------------------------------------------*/ + if(direct_initialized) + { + truncate_packet = true; + } + + direct_initialized = true; + + /*-----------------------------------------------------*\ + | Direct mode is implemented using Pulse mode | + \*-----------------------------------------------------*/ + mode = RGB_FUSION_2_DRAM_MODE_PULSE; + } + else + { + /*-----------------------------------------------------*\ + | In all other modes, set all LEDs at once | + \*-----------------------------------------------------*/ + led_data[RGB_FUSION_2_DRAM_LED_EN_MASK] = 0x3F; + + /*-----------------------------------------------------*\ + | Clear direct mode initialized flag when setting a non-| + | Direct mode | + \*-----------------------------------------------------*/ + direct_initialized = false; + } + + led_data[RGB_FUSION_2_DRAM_IDX_MODE] = mode; + led_data[RGB_FUSION_2_DRAM_IDX_BRIGHTNESS] = brightness; + led_data[RGB_FUSION_2_DRAM_IDX_RED] = red; + led_data[RGB_FUSION_2_DRAM_IDX_GREEN] = green; + led_data[RGB_FUSION_2_DRAM_IDX_BLUE] = blue; + + if(truncate_packet) + { + bus->i2c_smbus_write_block_data(dev, RGB_FUSION_2_DRAM_LED_START_ADDR, 15, led_data); + } + else + { + bus->i2c_smbus_write_block_data(dev, RGB_FUSION_2_DRAM_LED_START_ADDR, 32, led_data); + } + + Apply(); +} diff --git a/Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMController.h b/Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMController.h new file mode 100644 index 0000000..df069dd --- /dev/null +++ b/Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMController.h @@ -0,0 +1,85 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2DRAMController.cpp | +| | +| Driver for Gigabyte Aorus RGB Fusion 2 RAM | +| | +| Adam Honse (CalcProgrammer1) 07 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char rgb_fusion_dev_id; + +enum +{ + RGB_FUSION_2_DRAM_LED_EN_MASK = 0x00, /* LED enable bitfield */ + RGB_FUSION_2_DRAM_IDX_MODE = 0x09, /* Mode index */ + RGB_FUSION_2_DRAM_IDX_BRIGHTNESS = 0x0A, /* Brightness index */ + RGB_FUSION_2_DRAM_IDX_BLUE = 0x0C, /* Blue index */ + RGB_FUSION_2_DRAM_IDX_GREEN = 0x0D, /* Green index */ + RGB_FUSION_2_DRAM_IDX_RED = 0x0E, /* Red index */ +// RGB_FUSION_2_DRAM_TIMER_1_LSB = 0x08, /* Timer 1 LSB. Valid timer values [0-65535] */ +// RGB_FUSION_2_DRAM_TIMER_1_MSB = 0x09, /* Timer 1 MSB. Timer unis are milliseconds */ +// RGB_FUSION_2_DRAM_TIMER_2_LSB = 0x0A, /* Timer 2 LSB */ +// RGB_FUSION_2_DRAM_TIMER_2_MSB = 0x0B, /* Timer 2 MSB */ +// RGB_FUSION_2_DRAM_TIMER_3_LSB = 0x0C, /* Timer 3 LSB */ +// RGB_FUSION_2_DRAM_TIMER_3_MSB = 0x0D, /* Timer 3 MSB */ +// RGB_FUSION_2_DRAM_IDX_OPT_1 = 0x0E, /* Option 1. Use case varies by mode */ +// RGB_FUSION_2_DRAM_IDX_OPT_2 = 0x0F, /* Option 2. Use case varies by mode */ +}; + +enum +{ + RGB_FUSION_2_DRAM_LED_START_ADDR = 0x20, + RGB_FUSION_2_DRAM_APPLY_ADDR = 0x28, +}; + +enum +{ + RGB_FUSION_2_DRAM_ACTION_APPLY = 0x0F, +}; + +enum +{ + RGB_FUSION_2_DRAM_MODE_OFF = 0x00, /* Off mode */ + RGB_FUSION_2_DRAM_MODE_STATIC = 0x01, /* Static mode */ + RGB_FUSION_2_DRAM_MODE_PULSE = 0x02, /* Pulsing mode */ + RGB_FUSION_2_DRAM_MODE_FLASH = 0x03, /* Flashing mode */ + RGB_FUSION_2_DRAM_MODE_DIRECT = 0xFF /* Dummy mode, implements per LED using Pulse */ +}; + +class RGBFusion2DRAMController +{ +public: + RGBFusion2DRAMController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev); + ~RGBFusion2DRAMController(); + + std::string GetDeviceLocation(); + unsigned int GetLEDCount(); + void Apply(); + + void SetLEDEffect + ( + unsigned int led, + int mode, + unsigned int brightness, + unsigned int speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + unsigned int led_count; + i2c_smbus_interface* bus; + rgb_fusion_dev_id dev; + bool direct_initialized; + + unsigned char led_data[32]; +}; diff --git a/Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMControllerDetect.cpp b/Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMControllerDetect.cpp new file mode 100644 index 0000000..aa58aed --- /dev/null +++ b/Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMControllerDetect.cpp @@ -0,0 +1,91 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2DRAMControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus RGB Fusion 2 RAM | +| | +| Adam Honse (CalcProgrammer1) 07 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "LogManager.h" +#include "GigabyteRGBFusion2DRAMController.h" +#include "RGBController_GigabyteRGBFusion2DRAM.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* TestForGigabyteRGBFusion2DRAMController * +* * +* Tests the given address to see if an RGB 2 Fusion DRAMcontroller exists there. * +* First does a quick write to test for a response * +* * +\******************************************************************************************/ + +bool TestForGigabyteRGBFusion2DRAMController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + + int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE); + + if(res >= 0) + { + bus->i2c_smbus_write_byte_data(address, 0xE1, 0x01); + + res = bus->i2c_smbus_read_word_data(address, 0xED); + + LOG_TRACE("[Gigabyte RGB Fusion 2 DRAM] Read from 0xED: 0x%04X", res); + + if(res == 0x3282) + { + res = bus->i2c_smbus_read_word_data(address, 0xEB); + + LOG_TRACE("[Gigabyte RGB Fusion 2 DRAM] Read from 0xEB: 0x%04X", res); + + if(res == 0x0800) + { + pass = true; + } + } + } + + return(pass); + +} /* TestForGigabyteRGBFusion2DRAMController() */ + +/***********************************************************************************************\ +* * +* DetectGigabyteRGBFusion2DRAMControllers * +* * +* Detect Gigabyte RGB Fusion 2 controllers on the enumerated I2C buses at address 0x67. * +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion device is connected * +* dev - I2C address of RGB Fusion device * +* * +\***********************************************************************************************/ + +void DetectGigabyteRGBFusion2DRAMControllers(std::vector& busses) +{ + for(unsigned int bus = 0; bus < busses.size(); bus++) + { + IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + // Check for RGB Fusion 2 DRAM controller at 0x67 + if(TestForGigabyteRGBFusion2DRAMController(busses[bus], 0x67)) + { + RGBFusion2DRAMController* controller = new RGBFusion2DRAMController(busses[bus], 0x67); + RGBController_RGBFusion2DRAM* rgb_controller = new RGBController_RGBFusion2DRAM(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } + +} /* DetectGigabyteRGBFusion2DRAMControllers() */ + +REGISTER_I2C_DETECTOR("Gigabyte RGB Fusion 2 DRAM", DetectGigabyteRGBFusion2DRAMControllers); diff --git a/Controllers/GigabyteRGBFusion2DRAMController/RGBController_GigabyteRGBFusion2DRAM.cpp b/Controllers/GigabyteRGBFusion2DRAMController/RGBController_GigabyteRGBFusion2DRAM.cpp new file mode 100644 index 0000000..28b7110 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2DRAMController/RGBController_GigabyteRGBFusion2DRAM.cpp @@ -0,0 +1,172 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBController_RGBFusion2DRAM.cpp | +| | +| RGBController for Gigabyte Aorus RGB Fusion 2 RAM | +| | +| Adam Honse (CalcProgrammer1) 07 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GigabyteRGBFusion2DRAM.h" + +/**------------------------------------------------------------------*\ + @name Gigabyte Fusion2 DRAM + @category RAM + @type I2C + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectGigabyteRGBFusion2DRAMControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RGBFusion2DRAM::RGBController_RGBFusion2DRAM(RGBFusion2DRAMController* controller_ptr) +{ + controller = controller_ptr; + + name = "RGB Fusion 2 DRAM"; + vendor = "Gigabyte"; + description = "RGB Fusion 2 DRAM Device"; + location = controller->GetDeviceLocation(); + + type = DEVICE_TYPE_DRAM; + + /*-----------------------------------------------------*\ + | Direct mode is achieved through bit of a hack. Use | + | pulse mode but set the configuration such that it does| + | not actually pulse. This allows each LED to be set | + | independently. | + | See this Discord conversation: | + | https://discord.com/channels/699861463375937578/ | + | 699861463887773729/719700736845414453 | + \*-----------------------------------------------------*/ + mode Direct; + Direct.name = "Direct"; + Direct.value = RGB_FUSION_2_DRAM_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.brightness = 100; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = RGB_FUSION_2_DRAM_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = RGB_FUSION_2_DRAM_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness_min = 0; + Static.brightness_max = 100; + Static.brightness = 100; + modes.push_back(Static); + + SetupZones(); +} + +RGBController_RGBFusion2DRAM::~RGBController_RGBFusion2DRAM() +{ + delete controller; +} + +void RGBController_RGBFusion2DRAM::SetupZones() +{ + /*---------------------------------------------------------*\ + | Search through all LEDs and create zones for each channel | + | type | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + + // Set zone name to channel name + new_zone->name = "DRAM"; + new_zone->leds_min = controller->GetLEDCount(); + new_zone->leds_max = controller->GetLEDCount(); + new_zone->leds_count = controller->GetLEDCount(); + + // Push new zone to zones vector + zones.push_back(*new_zone); + + for(unsigned int led_idx = 0; led_idx < controller->GetLEDCount(); led_idx++) + { + led* new_led = new led(); + new_led->name = "DRAM LED"; + + // Push new LED to LEDs vector + leds.push_back(*new_led); + } + + SetupColors(); +} + +void RGBController_RGBFusion2DRAM::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RGBFusion2DRAM::DeviceUpdateLEDs() +{ + /*---------------------------------------------------------*\ + | Loop through all LEDs and set effect parameters. Must | + | apply after every effect set | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < colors.size(); led_idx++) + { + RGBColor color = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + color = colors[led_idx]; + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + color = modes[active_mode].colors[0]; + } + + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + int mode = modes[active_mode].value; + unsigned int speed = modes[active_mode].speed; + unsigned int brightness = modes[active_mode].brightness; + + controller->SetLEDEffect(led_idx, mode, brightness, speed, red, grn, blu); + + /*---------------------------------------------------------*\ + | Only update once unless in direct mode | + \*---------------------------------------------------------*/ + if(modes[active_mode].value != RGB_FUSION_2_DRAM_MODE_DIRECT) + { + break; + } + } +} + +void RGBController_RGBFusion2DRAM::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RGBFusion2DRAM::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RGBFusion2DRAM::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + diff --git a/Controllers/GigabyteRGBFusion2DRAMController/RGBController_GigabyteRGBFusion2DRAM.h b/Controllers/GigabyteRGBFusion2DRAMController/RGBController_GigabyteRGBFusion2DRAM.h new file mode 100644 index 0000000..97fa2cf --- /dev/null +++ b/Controllers/GigabyteRGBFusion2DRAMController/RGBController_GigabyteRGBFusion2DRAM.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBController_RGBFusion2DRAM.h | +| | +| RGBController for Gigabyte Aorus RGB Fusion 2 RAM | +| | +| Adam Honse (CalcProgrammer1) 07 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteRGBFusion2DRAMController.h" + +class RGBController_RGBFusion2DRAM : public RGBController +{ +public: + RGBController_RGBFusion2DRAM(RGBFusion2DRAMController* controller_ptr); + ~RGBController_RGBFusion2DRAM(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RGBFusion2DRAMController* controller; +}; diff --git a/Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUController.cpp b/Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUController.cpp new file mode 100644 index 0000000..a81ef3c --- /dev/null +++ b/Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUController.cpp @@ -0,0 +1,186 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2GPUController.cpp | +| | +| Driver for Gigabyte Aorus RGB Fusion 2 GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "GigabyteRGBFusion2GPUController.h" +#include "LogManager.h" + +using namespace std::chrono_literals; + +RGBFusion2GPUController::RGBFusion2GPUController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +RGBFusion2GPUController::~RGBFusion2GPUController() +{ + +} + +std::string RGBFusion2GPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string RGBFusion2GPUController::GetDeviceName() +{ + return(name); +} + +void RGBFusion2GPUController::SaveConfig() +{ + uint8_t data_pkt[8] = { 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + bus->i2c_write_block(dev, sizeof(data_pkt), data_pkt); +} + +void RGBFusion2GPUController::SetMode(uint8_t zone, uint8_t mode, fusion2_config zone_config, uint8_t mystery_flag) +{ + if(zone < 4) + { + this->zone_color[zone] = zone_config.colors[0]; + } + + uint8_t zone_pkt[8] = { RGB_FUSION2_GPU_REG_MODE, mode, zone_config.speed, zone_config.brightness, mystery_flag, (uint8_t)(zone + 1), 0x00, 0x00 }; + bus->i2c_write_block(dev, sizeof(zone_pkt), zone_pkt); + + uint8_t zone_pkt2[8] = { 0 }; + switch(zone) + { + case 0: + case 1: + zone_pkt2[0] = RGB_FUSION2_GPU_REG_COLOR_LEFT_MID; + zone_pkt2[1] = mode; + zone_pkt2[2] = (uint8_t)RGBGetRValue(this->zone_color[0]); + zone_pkt2[3] = (uint8_t)RGBGetGValue(this->zone_color[0]); + zone_pkt2[4] = (uint8_t)RGBGetBValue(this->zone_color[0]); + zone_pkt2[5] = (uint8_t)RGBGetRValue(this->zone_color[1]); + zone_pkt2[6] = (uint8_t)RGBGetGValue(this->zone_color[1]); + zone_pkt2[7] = (uint8_t)RGBGetBValue(this->zone_color[1]); + break; + case 2: + zone_pkt2[0] = RGB_FUSION2_GPU_REG_COLOR_RIGHT; + zone_pkt2[1] = mode; + zone_pkt2[2] = (uint8_t)RGBGetRValue(this->zone_color[2]); + zone_pkt2[3] = (uint8_t)RGBGetGValue(this->zone_color[2]); + zone_pkt2[4] = (uint8_t)RGBGetBValue(this->zone_color[2]); + break; + default: + zone_pkt2[0] = RGB_FUSION2_GPU_REG_COLOR; + zone_pkt2[1] = (uint8_t)RGBGetRValue(zone_config.colors[0]); + zone_pkt2[2] = (uint8_t)RGBGetGValue(zone_config.colors[0]); + zone_pkt2[3] = (uint8_t)RGBGetBValue(zone_config.colors[0]); + zone_pkt2[4] = (uint8_t)(zone + 1); + break; + } + bus->i2c_write_block(dev, sizeof(zone_pkt2), zone_pkt2); +} + +void RGBFusion2GPUController::SetZone(uint8_t zone, uint8_t mode, fusion2_config zone_config) +{ + std::string mode_name; + uint8_t mystery_flag = 0x00; + + switch(mode) + { + case RGB_FUSION2_GPU_MODE_STATIC: + { + SetMode(zone, mode, zone_config, mystery_flag); + } + break; + + case RGB_FUSION2_GPU_MODE_BREATHING: + { + zone_config.brightness = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + SetMode(zone, mode, zone_config, mystery_flag); + } + break; + + case RGB_FUSION2_GPU_MODE_COLOR_CYCLE: + { + uint8_t zone_pkt[8] = { RGB_FUSION2_GPU_REG_MODE, mode, zone_config.speed, zone_config.brightness, mystery_flag, (uint8_t)(zone + 1), 0x00, 0x00 }; + bus->i2c_write_block(dev, sizeof(zone_pkt), zone_pkt); + } + break; + + case RGB_FUSION2_GPU_MODE_GRADIENT: + { + mystery_flag = 0x08; + SetMode(zone, mode, zone_config, mystery_flag); + } + break; + + case RGB_FUSION2_GPU_MODE_FLASHING: + { + SetMode(zone, mode, zone_config, mystery_flag); + } + break; + + case RGB_FUSION2_GPU_MODE_DUAL_FLASHING: + { + SetMode(zone, mode, zone_config, mystery_flag); + } + break; + + case RGB_FUSION2_GPU_MODE_WAVE: + { + SetMode(zone, mode, zone_config, mystery_flag); + } + break; + + case RGB_FUSION2_GPU_MODE_COLOR_SHIFT: + { + mystery_flag = zone_config.numberOfColors; + uint8_t zone_pkt[8] = { RGB_FUSION2_GPU_REG_MODE, mode, zone_config.speed, zone_config.brightness, mystery_flag, (uint8_t)(zone + 1), 0x00, 0x00 }; + bus->i2c_write_block(dev, sizeof(zone_pkt), zone_pkt); + + uint8_t bank = (0xB0 + (zone * 4)); + uint8_t zone_pktA[8] = { (uint8_t)bank, mode, (uint8_t)RGBGetRValue(zone_config.colors[0]), (uint8_t)RGBGetGValue(zone_config.colors[0]), (uint8_t)RGBGetBValue(zone_config.colors[0]), (uint8_t)RGBGetRValue(zone_config.colors[1]), (uint8_t)RGBGetGValue(zone_config.colors[1]), (uint8_t)RGBGetBValue(zone_config.colors[1]) }; + bus->i2c_write_block(dev, sizeof(zone_pktA), zone_pktA); + uint8_t zone_pktB[8] = { (uint8_t)(bank + 1), mode, (uint8_t)RGBGetRValue(zone_config.colors[2]), (uint8_t)RGBGetGValue(zone_config.colors[2]), (uint8_t)RGBGetBValue(zone_config.colors[2]), (uint8_t)RGBGetRValue(zone_config.colors[3]), (uint8_t)RGBGetGValue(zone_config.colors[3]), (uint8_t)RGBGetBValue(zone_config.colors[3]) }; + bus->i2c_write_block(dev, sizeof(zone_pktB), zone_pktB); + uint8_t zone_pktC[8] = { (uint8_t)(bank + 2), mode, (uint8_t)RGBGetRValue(zone_config.colors[4]), (uint8_t)RGBGetGValue(zone_config.colors[4]), (uint8_t)RGBGetBValue(zone_config.colors[4]), (uint8_t)RGBGetRValue(zone_config.colors[5]), (uint8_t)RGBGetGValue(zone_config.colors[5]), (uint8_t)RGBGetBValue(zone_config.colors[5]) }; + bus->i2c_write_block(dev, sizeof(zone_pktC), zone_pktC); + uint8_t zone_pktD[8] = { (uint8_t)(bank + 3), mode, (uint8_t)RGBGetRValue(zone_config.colors[6]), (uint8_t)RGBGetGValue(zone_config.colors[6]), (uint8_t)RGBGetBValue(zone_config.colors[6]), (uint8_t)RGBGetRValue(zone_config.colors[7]), (uint8_t)RGBGetGValue(zone_config.colors[7]), (uint8_t)RGBGetBValue(zone_config.colors[7]) }; + bus->i2c_write_block(dev, sizeof(zone_pktD), zone_pktD); + } + break; + + case RGB_FUSION2_GPU_MODE_TRICOLOR: + { + mystery_flag = 0x08; + uint8_t zone_pkt[8] = { RGB_FUSION2_GPU_REG_MODE, mode, zone_config.speed, zone_config.brightness, mystery_flag, (uint8_t)(zone + 1), 0x00, 0x00 }; + bus->i2c_write_block(dev, sizeof(zone_pkt), zone_pkt); + + uint8_t bank = (0xB0 + (zone * 4)); + uint8_t zone_pktA[8] = { (uint8_t)bank, mode, (uint8_t)RGBGetRValue(zone_config.colors[0]), (uint8_t)RGBGetGValue(zone_config.colors[0]), (uint8_t)RGBGetBValue(zone_config.colors[0]), (uint8_t)RGBGetRValue(zone_config.colors[1]), (uint8_t)RGBGetGValue(zone_config.colors[1]), (uint8_t)RGBGetBValue(zone_config.colors[1]) }; + bus->i2c_write_block(dev, sizeof(zone_pktA), zone_pktA); + uint8_t zone_pktB[8] = { (uint8_t)(bank + 1), mode, (uint8_t)RGBGetRValue(zone_config.colors[2]), (uint8_t)RGBGetGValue(zone_config.colors[2]), (uint8_t)RGBGetBValue(zone_config.colors[2]), (uint8_t)RGBGetRValue(zone_config.colors[3]), (uint8_t)RGBGetGValue(zone_config.colors[3]), (uint8_t)RGBGetBValue(zone_config.colors[3]) }; + bus->i2c_write_block(dev, sizeof(zone_pktB), zone_pktB); + uint8_t zone_pktC[8] = { (uint8_t)(bank + 2), mode, (uint8_t)RGBGetRValue(zone_config.colors[4]), (uint8_t)RGBGetGValue(zone_config.colors[4]), (uint8_t)RGBGetBValue(zone_config.colors[4]), (uint8_t)RGBGetRValue(zone_config.colors[5]), (uint8_t)RGBGetGValue(zone_config.colors[5]), (uint8_t)RGBGetBValue(zone_config.colors[5]) }; + bus->i2c_write_block(dev, sizeof(zone_pktC), zone_pktC); + uint8_t zone_pktD[8] = { (uint8_t)(bank + 3), mode, (uint8_t)RGBGetRValue(zone_config.colors[6]), (uint8_t)RGBGetGValue(zone_config.colors[6]), (uint8_t)RGBGetBValue(zone_config.colors[6]), (uint8_t)RGBGetRValue(zone_config.colors[7]), (uint8_t)RGBGetGValue(zone_config.colors[7]), (uint8_t)RGBGetBValue(zone_config.colors[7]) }; + bus->i2c_write_block(dev, sizeof(zone_pktD), zone_pktD); + } + break; + + default: + { + LOG_TRACE("[%s] Mode %02d not found", "fusion2 gpu", mode); + } + break; + } +} diff --git a/Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUController.h b/Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUController.h new file mode 100644 index 0000000..7fba812 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUController.h @@ -0,0 +1,81 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2GPUController.h | +| | +| Driver for Gigabyte Aorus RGB Fusion 2 GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +typedef unsigned char rgb_fusion_dev_id; + +struct fusion2_config +{ + uint8_t brightness; + RGBColor colors[8]; + uint8_t numberOfColors; + uint8_t speed; + uint8_t direction; +}; + +enum +{ + RGB_FUSION2_GPU_REG_COLOR = 0x40, + RGB_FUSION2_GPU_REG_MODE = 0x88, + RGB_FUSION2_GPU_REG_COLOR_LEFT_MID = 0xB0, + RGB_FUSION2_GPU_REG_COLOR_RIGHT = 0xB1 +}; + +enum +{ + RGB_FUSION2_GPU_MODE_STATIC = 0x01, + RGB_FUSION2_GPU_MODE_BREATHING = 0x02, + RGB_FUSION2_GPU_MODE_COLOR_CYCLE = 0x03, + RGB_FUSION2_GPU_MODE_FLASHING = 0x04, + RGB_FUSION2_GPU_MODE_GRADIENT = 0x05, + RGB_FUSION2_GPU_MODE_COLOR_SHIFT = 0x06, + RGB_FUSION2_GPU_MODE_WAVE = 0x07, + RGB_FUSION2_GPU_MODE_DUAL_FLASHING = 0x08, + RGB_FUSION2_GPU_MODE_TRICOLOR = 0x0B +}; + +enum +{ + RGB_FUSION2_GPU_SPEED_SLOWEST = 0x00, + RGB_FUSION2_GPU_SPEED_NORMAL = 0x02, + RGB_FUSION2_GPU_SPEED_FASTEST = 0x05 +}; + +enum +{ + RGB_FUSION2_GPU_BRIGHTNESS_MIN = 0x00, + RGB_FUSION2_GPU_BRIGHTNESS_MAX = 0x63 +}; + +class RGBFusion2GPUController +{ +public: + RGBFusion2GPUController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev, std::string dev_name); + ~RGBFusion2GPUController(); + + RGBColor zone_color[4]; + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SaveConfig(); + + void SetZone(uint8_t zone, uint8_t mode, fusion2_config zone_config); + void SetMode(uint8_t zone, uint8_t mode, fusion2_config zone_config, uint8_t mystery_flag); + +private: + i2c_smbus_interface* bus; + rgb_fusion_dev_id dev; + std::string name; +}; diff --git a/Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUControllerDetect.cpp b/Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUControllerDetect.cpp new file mode 100644 index 0000000..76bc6ca --- /dev/null +++ b/Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUControllerDetect.cpp @@ -0,0 +1,205 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2GPUControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus RGB Fusion 2 GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "GigabyteRGBFusion2GPUController.h" +#include "LogManager.h" +#include "RGBController_GigabyteRGBFusion2GPU.h" +#include "i2c_amd_gpu.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +#define GIGABYTEGPU_CONTROLLER_NAME2 "Gigabyte RGB Fusion2 GPU" + +/******************************************************************************************\ +* * +* TestForGigabyteRGBFusion2GPUController * +* * +* Tests the given address to see if an RGB Fusion2 controller exists there. First * +* does a quick write to test for a response * +* * +\******************************************************************************************/ + +bool TestForGigabyteRGBFusion2GPUController(i2c_smbus_interface* bus, unsigned char address) +{ + if(bus->pci_vendor == AMD_GPU_VEN && !is_amd_gpu_i2c_bus(bus)) + { + return false; + } + + bool pass = false; + int res, pktsz; + const int read_sz = 4; + const int write_sz = 8; + uint8_t data_pkt[write_sz] = { 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t data_readpkt[read_sz] = {}; + + res = bus->i2c_write_block(address, write_sz, data_pkt); + + pass = true; + + pktsz = read_sz; + res = bus->i2c_read_block(address, &pktsz, data_readpkt); + + //What we have seen returned so far... + //GeForce RTX 3070 AORUS MASTER 8G 0xAB 0x11 0x52 0x03 + //GeForce RTX 3060 Ti GAMING OC PRO 8G 0xAB 0x10 0x01 0x02 + //GeForce RTX 3070 AORUS ELITE 12G 0xAB 0x11 0x52 0x03 + //GeForce RTX 3080 Ti AORUS XTREME WATERFORCE 12G 0xAB 0x11 0x01 0x00 + //GeForce RTX 3080 Ti AORUS XTREME WATERFORCE 12G LHS 0xAB 0x11 0x52 0x00 + //GeForce RTX 3080 AORUS XTREME WATERFORCE WB 10G 0xAB 0x10 0x01 0x00 + //GeForce RTX 4080 Gigabyte AORUS MASTER 16G 0xAB 0x10 0x52 0x07 + //Note that GeForce RTX 3080 Ti AORUS XTREME WATERFORCE 12G LHS exposes three i2c buses but only one returns a 0xAB + //response and controls the RGB lighting. The other buses return 0x00 0x00 0x00 0x00. + //Note that GeForce RTX 4080 Gigabyte AORUS MASTER 16G exposes two i2c bus with writable address 0x71 but one respond + //0x00 0x00 0x00 0x00 so it should be the one controlling the LCD screen. So we skip this bus + + //All seen responses start with 0xAB, so we check for this. + if(res < 0 || data_readpkt[0] != 0xAB) + { + // Assemble C-string with respons for debugging + std::string text = ""; + + for(int idx = 0; idx < read_sz; ++idx) + { + char str[6]; + snprintf(str, 6, " 0x%02X", data_readpkt[idx]); + text.append(str); + } + + LOG_DEBUG("[%s] at address 0x%02X invalid. Expected 0xAB [0x*] but received:%s", GIGABYTEGPU_CONTROLLER_NAME2, address, text.c_str()); + pass = false; + } + + return(pass); +} /* TestForRGBFusion2GPUController() */ + +/*******************************************************************************************\ +* * +* DetectRGBFusion2GPUControllers * +* * +* Detect GigabyteRGB Fusion2 controllers on the enumerated I2C busses at address 0x70.* +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion2 device is connected * +* dev - I2C address of RGB Fusion2 device * +* * +\*******************************************************************************************/ + +void DetectGigabyteRGBFusion2GPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + // Check for RGB Fusion2 controller + if(TestForGigabyteRGBFusion2GPUController(bus, i2c_addr)) + { + RGBFusion2GPUController* controller = new RGBFusion2GPUController(bus, i2c_addr, name); + RGBController_RGBFusion2GPU* rgb_controller = new RGBController_RGBFusion2GPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectGigabyteRGBFusion2GPUControllers() */ + +/*-----------------------------------------*\ +| Nvidia GPUs | +\*-----------------------------------------*/ + +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2060 SUPER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2060S_V1_SUB_DEV_H, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2060 SUPER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2060S_V1_SUB_DEV_P, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2070", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2070_SUB_DEV, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2070 XTREME", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2070_XTREME_SUB_DEV_H, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2070 XTREME", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2070_XTREME_SUB_DEV_P, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2070 SUPER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2070S_8G_SUB_DEV_H, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2070 SUPER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2070S_8G_SUB_DEV_P, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080_SUB_DEV_H, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080_SUB_DEV_P, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080 XTREME", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080_XTREME_SUB_DEV_H, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080 XTREME", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080_XTREME_SUB_DEV_P, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080 SUPER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080S_SUB_DEV_H, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080 SUPER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080S_SUB_DEV_P, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080 SUPER Waterforce WB", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080S_WATERFORCE_WB_SUB_DEV_H,0x51); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080 SUPER Waterforce WB", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080S_WATERFORCE_WB_SUB_DEV_P,0x51); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080 SUPER Waterforce", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080S_WATERFORCE_SUB_DEV_H, 0x08); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080 SUPER Waterforce", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080S_WATERFORCE_SUB_DEV_P, 0x08); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080 Ti XTREME", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080TI_EXTREME_SUB_DEV_H, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 2080 Ti XTREME", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX2080TI_EXTREME_SUB_DEV_P, 0x50); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3060 ELITE", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA104_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_ELITE_12GB_SUB_DEV, 0x70); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3060 ELITE Rev A1", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA106_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_ELITE_12GB_SUB_DEV, 0x70); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3060 ELITE LHR", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_ELITE_12GB_SUB_DEV, 0x70); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3060 Ti ELITE LHR", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_ELITE_8GB_SUB_DEV, 0x70); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_GDDR6X_DEV,GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_GAMING_OC_8G_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti GAMING OC LHR Rev 2", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_GAMING_OC_SUB_DEV, 0x32); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti GAMING OC LHR", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti GAMING OC PRO", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_GAMING_OC_PRO_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti Gaming OC PRO LHR", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_GAMING_OC_PRO_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 MASTER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070_MASTER_OC_SUB_DEV, 0x66); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 MASTER LHR", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070_MASTER_OC_SUB_DEV, 0x66); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3070 Ti MASTER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX3070TI_MASTER_8G_SUB_DEV, 0x70); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3080 XTREME WATERFORCE WB", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080_XTREME_WATERFORCE_SUB_DEV, 0x64); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3080 XTREME WATERFORCE WB", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080_XTREME_WATERFORCE_SUB_DEV, 0x64); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3080 XTREME WATERFORCE V2", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080_XTREME_WATERFORCE_V2_SUB_DEV, 0x65); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3080 12G XTREME WATERFORCE WB", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080_XTREME_WATERFORCE_12G_SUB_DEV, 0x64); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3080 Ti Vision OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080TI_VISION_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3080 Ti XTREME WATERFORCE", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080TI_XTREME_WATERFORCE_SUB_DEV, 0x65); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3080 Ti XTREME WATERFORCE", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080TI_XTREME_WATERFORCE_SUB_DEV2, 0x64); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3090 VISION OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3090_VISION_OC_24G_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3090 XTREME WATERFORCE", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3090_XTREME_WATERFORCE_SUB_DEV, 0x65); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 3090 XTREME WATERFORCE WB", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3090_XTREME_WATERFORCE_WB_SUB_DEV, 0x64); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070_GAMING_OC_12G, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070_GAMING_OC_12G_V2, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_AD103_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070_GAMING_OC_12G_V2, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Geforce RTX 4070 Aero OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070_AERO_OC_12G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 SUPER GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070S_GAMING_OC_12G, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 SUPER Aero OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070S_AERO_OC_12G, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 SUPER Eagle OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070S_EAGLE_OC_ICE_12G, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti GAMING", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_GAMING_12G, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_GAMING_OC_12G, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_GAMING_OC_12G_SUB_DEV2, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti Eagle", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_EAGLE_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti Eagle OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_EAGLE_OC_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Eagle OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070_EAGLE_OC_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti Eagle OC Rev 2", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_EAGLE_OC_V2_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti Master", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_MASTER_12G, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 4070 Ti ELITE", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TI_ELITE_12G, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti SUPER GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TIS_GAMING_OC_16G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4080 AERO OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4080_AERO_OC_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4080 Eagle OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4080_EAGLE_OC_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4080 SUPER GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4080S_GAMING_OC_16GB_SUB_DEV, 0x72); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4080 SUPER AERO OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4080S_AERO_OC_16GB_SUB_DEV, 0x72); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4080 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4080_GAMING_OC_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 4080 MASTER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX4080_MASTER_16G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4090 AERO OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4090_AERO_OC_24G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4090 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4090_GAMING_OC_24G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 4090 MASTER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX4090_MASTER_24G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 D MASTER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX5090D_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090D_MASTER_32G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 5090 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX5090D_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX5090_GAMING_OC_32G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 MASTER", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090_MASTER_32G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS GeForce RTX 5090 MASTER ICE", DetectGigabyteRGBFusion2GPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RTX5090_MASTER_ICE_32G_SUB_DEV, 0x71); + +/*-----------------------------------------*\ +| AMD GPUs | +\*-----------------------------------------*/ + +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7600 GAMING OC 8G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI33_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7600_GAMING_OC_8G_SUB_DEV, 0x55); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7600 GAMING OC 8G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI33_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7600_GAMING_OC_8G_SUB_DEV2, 0x55); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7600 XT GAMING OC 16G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI33_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7600XT_GAMING_OC_16G_SUB_DEV, 0x55); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 6700 XT GAMING OC 12G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX6700XT_GAMING_OC_12G_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 6800 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, GIGABYTE_SUB_VEN, GIGABYTE_RX6800XT_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 6900 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, GIGABYTE_SUB_VEN, GIGABYTE_RX6900XT_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7700 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7700XT_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7800 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI32_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7800XT_GAMING_OC_16G_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 GRE GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900GRE_GAMING_OC_16G_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900XT_GAMING_OC_20G_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 7900 XTX GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX7900XTX_GAMING_OC_24G_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS Radeon RX 7900 XTX ELITE 24G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX7900XTX_ELITE_24G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS RX 6750 XT ELITE 12G", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX_6750_XT_ELITE_12G_SUB_DEV, 0x70); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS RX 6900 XT EXTREME WATERFORCE WB", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV2, GIGABYTE_SUB_VEN, GIGABYTE_RX6900XT_XTREME_WATERFORCE_WB_SUB_DEV, 0x70); +REGISTER_I2C_PCI_DETECTOR("Gigabyte AORUS Radeon RX 9070 XT Elite", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_AORUS_RX9070XT_ELITE_16G_SUB_DEV, 0x73); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 XT GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070XT_GAMING_OC_16G_SUB_DEV, 0x73); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 XT GAMING OC ICE", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070XT_GAMING_OC_ICE_16G_SUB_DEV, 0x73); +REGISTER_I2C_PCI_DETECTOR("Gigabyte Radeon RX 9070 GAMING OC", DetectGigabyteRGBFusion2GPUControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RX9070_GAMING_OC_16G_SUB_DEV, 0x73); diff --git a/Controllers/GigabyteRGBFusion2GPUController/RGBController_GigabyteRGBFusion2GPU.cpp b/Controllers/GigabyteRGBFusion2GPUController/RGBController_GigabyteRGBFusion2GPU.cpp new file mode 100644 index 0000000..376e49c --- /dev/null +++ b/Controllers/GigabyteRGBFusion2GPUController/RGBController_GigabyteRGBFusion2GPU.cpp @@ -0,0 +1,246 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusion2GPU.cpp | +| | +| RGBController for Gigabyte Aorus RGB Fusion 2 GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GigabyteRGBFusion2GPU.h" +#include "LogManager.h" + +/**------------------------------------------------------------------*\ + @name Gigabyte Fusion 2 GPU + @category GPU + @type I2C + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGigabyteRGBFusion2GPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RGBFusion2GPU::RGBController_RGBFusion2GPU(RGBFusion2GPUController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Gigabyte"; + description = "Gigabyte RGB Fusion 2 GPU Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Direct; + Direct.name = "Direct"; + Direct.value = RGB_FUSION2_GPU_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = RGB_FUSION2_GPU_BRIGHTNESS_MIN; + Direct.brightness_max = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + Direct.brightness = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Pulse"; + Breathing.value = RGB_FUSION2_GPU_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Breathing.speed_min = RGB_FUSION2_GPU_SPEED_SLOWEST; + Breathing.speed_max = RGB_FUSION2_GPU_SPEED_FASTEST; + Breathing.speed = RGB_FUSION2_GPU_SPEED_NORMAL; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = RGB_FUSION2_GPU_BRIGHTNESS_MIN; + Breathing.brightness_max = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + Breathing.brightness = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flash"; + Flashing.value = RGB_FUSION2_GPU_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Flashing.speed_min = RGB_FUSION2_GPU_SPEED_SLOWEST; + Flashing.speed_max = RGB_FUSION2_GPU_SPEED_FASTEST; + Flashing.speed = RGB_FUSION2_GPU_SPEED_NORMAL; + Flashing.color_mode = MODE_COLORS_PER_LED; + Flashing.brightness_min = RGB_FUSION2_GPU_BRIGHTNESS_MIN; + Flashing.brightness_max = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + Flashing.brightness = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + modes.push_back(Flashing); + + mode DualFlashing; + DualFlashing.name = "Double Flash"; + DualFlashing.value = RGB_FUSION2_GPU_MODE_DUAL_FLASHING; + DualFlashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + DualFlashing.speed_min = RGB_FUSION2_GPU_SPEED_SLOWEST; + DualFlashing.speed_max = RGB_FUSION2_GPU_SPEED_FASTEST; + DualFlashing.speed = RGB_FUSION2_GPU_SPEED_NORMAL; + DualFlashing.color_mode = MODE_COLORS_PER_LED; + DualFlashing.brightness_min = RGB_FUSION2_GPU_BRIGHTNESS_MIN; + DualFlashing.brightness_max = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + DualFlashing.brightness = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + modes.push_back(DualFlashing); + + mode SpectrumCycle; + SpectrumCycle.name = "Color Cycle"; + SpectrumCycle.value = RGB_FUSION2_GPU_MODE_COLOR_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS;; + SpectrumCycle.speed_min = RGB_FUSION2_GPU_SPEED_SLOWEST; + SpectrumCycle.speed_max = RGB_FUSION2_GPU_SPEED_FASTEST; + SpectrumCycle.speed = RGB_FUSION2_GPU_SPEED_NORMAL; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = RGB_FUSION2_GPU_BRIGHTNESS_MIN; + SpectrumCycle.brightness_max = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + SpectrumCycle.brightness = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + modes.push_back(SpectrumCycle); + + mode Gradient; + Gradient.name = "Gradient"; + Gradient.value = RGB_FUSION2_GPU_MODE_GRADIENT; + Gradient.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Gradient.speed_min = RGB_FUSION2_GPU_SPEED_SLOWEST; + Gradient.speed_max = RGB_FUSION2_GPU_SPEED_FASTEST; + Gradient.speed = RGB_FUSION2_GPU_SPEED_NORMAL; + Gradient.color_mode = MODE_COLORS_PER_LED; + Gradient.brightness_min = RGB_FUSION2_GPU_BRIGHTNESS_MIN; + Gradient.brightness_max = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + Gradient.brightness = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + modes.push_back(Gradient); + + mode Wave; + Wave.name = "Wave"; + Wave.value = RGB_FUSION2_GPU_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Wave.speed_min = RGB_FUSION2_GPU_SPEED_SLOWEST; + Wave.speed_max = RGB_FUSION2_GPU_SPEED_FASTEST; + Wave.speed = RGB_FUSION2_GPU_SPEED_NORMAL; + Wave.color_mode = MODE_COLORS_NONE; + Wave.brightness_min = RGB_FUSION2_GPU_BRIGHTNESS_MIN; + Wave.brightness_max = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + Wave.brightness = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + modes.push_back(Wave); + + mode Shift; + Shift.name = "Color Shift"; + Shift.value = RGB_FUSION2_GPU_MODE_COLOR_SHIFT; + Shift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Shift.speed_min = RGB_FUSION2_GPU_SPEED_SLOWEST; + Shift.speed_max = RGB_FUSION2_GPU_SPEED_FASTEST; + Shift.speed = RGB_FUSION2_GPU_SPEED_NORMAL; + Shift.color_mode = MODE_COLORS_MODE_SPECIFIC; + Shift.colors_min = 1; + Shift.colors_max = 8; + Shift.colors.resize(8); + Shift.brightness_min = RGB_FUSION2_GPU_BRIGHTNESS_MIN; + Shift.brightness_max = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + Shift.brightness = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + modes.push_back(Shift); + + mode Tricolor; + Tricolor.name = "Tricolor"; + Tricolor.value = RGB_FUSION2_GPU_MODE_TRICOLOR; + Tricolor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Tricolor.speed_min = RGB_FUSION2_GPU_SPEED_SLOWEST; + Tricolor.speed_max = RGB_FUSION2_GPU_SPEED_FASTEST; + Tricolor.speed = RGB_FUSION2_GPU_SPEED_NORMAL; + Tricolor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Tricolor.colors_min = 1; + Tricolor.colors_max = 3; + Tricolor.colors.resize(3); + Tricolor.brightness_min = RGB_FUSION2_GPU_BRIGHTNESS_MIN; + Tricolor.brightness_max = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + Tricolor.brightness = RGB_FUSION2_GPU_BRIGHTNESS_MAX; + modes.push_back(Tricolor); + + SetupZones(); +} + +RGBController_RGBFusion2GPU::~RGBController_RGBFusion2GPU() +{ + delete controller; +} + +void RGBController_RGBFusion2GPU::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only allows setting the entire zone for all | + | LED's in the zone and does not allow per LED control. | + \*---------------------------------------------------------*/ + + for(uint8_t zone_idx = 0; zone_idx < RGB_FUSION_2_GPU_NUMBER_OF_ZONES; zone_idx++) + { + zone new_zone; + led new_led; + + new_zone.name = "GPU zone " + std::to_string(zone_idx + 1); + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + + new_led.name = new_zone.name; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(new_led); + zones.push_back(new_zone); + } + + SetupColors(); +} + +void RGBController_RGBFusion2GPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RGBFusion2GPU::DeviceUpdateLEDs() +{ + fusion2_config zone_config; + + zone_config.brightness = modes[active_mode].brightness; + zone_config.speed = modes[active_mode].speed; + zone_config.direction = modes[active_mode].direction; + zone_config.numberOfColors = (uint8_t)modes[active_mode].colors.size(); + + for(uint8_t zone_idx = 0; zone_idx < RGB_FUSION_2_GPU_NUMBER_OF_ZONES; zone_idx++) + { + zone_config.colors[0] = colors[zone_idx]; + + if (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + for (uint8_t i = 0; i < zone_config.numberOfColors; i++) + { + zone_config.colors[i] = modes[active_mode].colors[i]; + } + } + + controller->SetZone(zone_idx, modes[active_mode].value, zone_config); + } +} + +void RGBController_RGBFusion2GPU::UpdateZoneLEDs(int zone) +{ + LOG_TRACE("[%s] Update zone #%d", name.c_str(), zone); + DeviceUpdateLEDs(); +} + +void RGBController_RGBFusion2GPU::UpdateSingleLED(int led) +{ + LOG_TRACE("[%s] Update single led : %d", name.c_str(), led); + DeviceUpdateLEDs(); +} + +void RGBController_RGBFusion2GPU::DeviceUpdateMode() +{ + LOG_TRACE("[%s] Switching to mode %s @ brightness %d and speed %d", name.c_str(), modes[active_mode].name.c_str(), modes[active_mode].brightness, modes[active_mode].speed); + DeviceUpdateLEDs(); +} + +void RGBController_RGBFusion2GPU::DeviceSaveMode() +{ + controller->SaveConfig(); +} diff --git a/Controllers/GigabyteRGBFusion2GPUController/RGBController_GigabyteRGBFusion2GPU.h b/Controllers/GigabyteRGBFusion2GPUController/RGBController_GigabyteRGBFusion2GPU.h new file mode 100644 index 0000000..32e1d18 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2GPUController/RGBController_GigabyteRGBFusion2GPU.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusion2GPU.h | +| | +| RGBController for Gigabyte Aorus RGB Fusion 2 GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteRGBFusion2GPUController.h" + +#define RGB_FUSION_2_GPU_NUMBER_OF_ZONES 5 + +class RGBController_RGBFusion2GPU : public RGBController +{ +public: + RGBController_RGBFusion2GPU(RGBFusion2GPUController* controller_ptr); + ~RGBController_RGBFusion2GPU(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + RGBFusion2GPUController* controller; +}; diff --git a/Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusController.cpp b/Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusController.cpp new file mode 100644 index 0000000..ee400f2 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusController.cpp @@ -0,0 +1,196 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2SMBusController.cpp | +| | +| Driver for Gigabyte Aorus RGB Fusion 2 SMBus | +| motherboard | +| | +| Adam Honse (CalcProgrammer1) 12 Mar 2020 | +| Matt Harper 05 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "GigabyteRGBFusion2SMBusController.h" + +RGBFusion2SMBusController::RGBFusion2SMBusController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev, std::string mb_name) +{ + this->bus = bus; + this->dev = dev; + this->name = mb_name; + + memset(led_data, 0, 10*16); + + led_count = 10; // Protocol supports 10 'slots' +} + +RGBFusion2SMBusController::~RGBFusion2SMBusController() +{ + +} + +unsigned int RGBFusion2SMBusController::GetLEDCount() +{ + return(led_count); +} + +std::string RGBFusion2SMBusController::GetDeviceName() +{ + return(name); +} + +std::string RGBFusion2SMBusController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +/* Writes are performed in 32 byte chunks. If we need to write the second 16 bytes, +* we must necessarily write the first 16 as well. Given that reading the existing state +* from the device is not yet possible, unfortunately we may overwrite existing device +* states if the state transition did not occur within in the same OpenRGB instance. +* That is to say, the current behavior is non-deal but the best we have +*/ +void RGBFusion2SMBusController::WriteLED(int led) +{ + unsigned short register_offset = led / 2; // Relying on integer division to truncate + unsigned short write_register = RGB_FUSION_2_LED_START_ADDR + 2*register_offset; + + // Adjust if we are writing the second 16 bytes + if(led % 2) + { + led -= 1; + } + + bus->i2c_smbus_write_block_data(RGB_FUSION_2_SMBUS_ADDR, (u8)write_register, 32, led_data[led]); +} + +void RGBFusion2SMBusController::Apply() +{ + // Protocol expects terminating sequence 0x01ff written to register 0x17 + bus->i2c_smbus_write_word_data(RGB_FUSION_2_SMBUS_ADDR, + RGB_FUSION_2_APPLY_ADDR, + RGB_FUSION_2_ACTION_APPLY); +} + +void RGBFusion2SMBusController::SetLEDEffect + ( + unsigned int led, + int mode, + unsigned int brightness, + unsigned int speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + led_data[led][RGB_FUSION_2_IDX_MODE] = mode; + led_data[led][RGB_FUSION_2_IDX_RED] = red; + led_data[led][RGB_FUSION_2_IDX_GREEN] = green; + led_data[led][RGB_FUSION_2_IDX_BLUE] = blue; + led_data[led][RGB_FUSION_2_IDX_BRIGHTNESS] = 0x64; + + switch (mode) + { + case RGB_FUSION_2_MODE_PULSE: + // Timer 1: On time + // Timer 2: Off time + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = 0x20 * speed; + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0x03 * speed; + led_data[led][RGB_FUSION_2_TIMER_2_LSB] = 0x20 * speed; + led_data[led][RGB_FUSION_2_TIMER_2_MSB] = 0x03 * speed; + break; + + case RGB_FUSION_2_MODE_DIGITAL_WAVE: + // Timer 1: Wave Speed + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = speed; + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0x00; + led_data[led][RGB_FUSION_2_IDX_BRIGHTNESS] = brightness; + break; + + case RGB_FUSION_2_MODE_DIGITAL_A: + // Timer 1: Directly controls speed of LED sections + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = speed; + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0x00; // Setting this to any number other than 0 slows down the effect + led_data[led][RGB_FUSION_2_IDX_OPT_1] = 0x01; // Doesn't do anything, but always defaults to 0x01 + break; + + case RGB_FUSION_2_MODE_DIGITAL_B: + // Timer 1: Direct control of section speed and pulsing + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = speed; // Main effect speed + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0x00; // Only sets to 0x01 in min speed. Makes light section blink for shorter periods + led_data[led][RGB_FUSION_2_IDX_OPT_1] = 0x05 - (speed / 60); // Changes between 0x01 and 0x04 along with speed. Controls light trail length. + break; + + case RGB_FUSION_2_MODE_DIGITAL_C: + // Timer 1: Effect Speed + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = speed; + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0x00; // Like Digital A, slows down the effect, but never sets to another number. + led_data[led][RGB_FUSION_2_IDX_OPT_1] = 0x04; // Fixed at 0x04. Does nothing. + break; + + case RGB_FUSION_2_MODE_DIGITAL_D: + // Timer 1: Effect Speed + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = speed; + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0x00; // Like Digital A, slows down the effect, but never sets to another number. + led_data[led][RGB_FUSION_2_IDX_OPT_1] = 0x04; // Fixed at 0x04. Does nothing. + led_data[led][RGB_FUSION_2_IDX_BRIGHTNESS] = brightness; + break; + + case RGB_FUSION_2_MODE_DIGITAL_E: + // Timer 1: Effect Speed + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = speed; + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0; // Like Digital A, slows down the effect, but never sets to another number. + led_data[led][RGB_FUSION_2_IDX_OPT_1] = 0x05 - (speed / 50); // Changes between 0x02 and 0x04 according to speed, might be similar to Digital B. + break; + + case RGB_FUSION_2_MODE_DIGITAL_F: + // Timer 1: Effect Speed + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = speed; + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0x00; // Like Digital A, slows down the effect, but never sets to another number. + led_data[led][RGB_FUSION_2_IDX_OPT_1] = 0x04; // Fixed at 0x04. Does nothing. + led_data[led][RGB_FUSION_2_IDX_BRIGHTNESS] = brightness; + break; + + case RGB_FUSION_2_MODE_DIGITAL_G: + // Timer 1: Effect Speed + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = speed; + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0x00; // Like Digital A, slows down the effect, but never sets to another number. + led_data[led][RGB_FUSION_2_IDX_OPT_1] = 0x04; // Fixed at 0x04. Does nothing. + led_data[led][RGB_FUSION_2_IDX_BRIGHTNESS] = brightness; + break; + + case RGB_FUSION_2_MODE_COLOR_CYCLE: + // Timer 1: Cycle time + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = 0x00; + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0x03 * speed; + led_data[led][RGB_FUSION_2_IDX_OPT_1] = 0x07; // Number of colors to cycle through. Valid range [1-7] + led_data[led][RGB_FUSION_2_IDX_OPT_2] = 0x00; // Color cycle, or color cycle and pulse. [0,1] + led_data[led][RGB_FUSION_2_IDX_BRIGHTNESS] = brightness; + break; + + case RGB_FUSION_2_MODE_FLASHING: + /* Timer 1: On time + * Timer 2: Interval + * Timer 3: Cycle time */ + led_data[led][RGB_FUSION_2_TIMER_1_LSB] = 0x64; + led_data[led][RGB_FUSION_2_TIMER_1_MSB] = 0; + led_data[led][RGB_FUSION_2_TIMER_2_LSB] = 0xc8; + led_data[led][RGB_FUSION_2_TIMER_2_MSB] = 0; + led_data[led][RGB_FUSION_2_TIMER_3_LSB] = 0xd0; + led_data[led][RGB_FUSION_2_TIMER_3_MSB] = 0x07; + + led_data[led][RGB_FUSION_2_IDX_OPT_1] = speed; // Controls number of flashes + break; + } + + WriteLED(led); +} + diff --git a/Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusController.h b/Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusController.h new file mode 100644 index 0000000..39cddf0 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusController.h @@ -0,0 +1,120 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2SMBusController.h | +| | +| Driver for Gigabyte Aorus RGB Fusion 2 SMBus | +| motherboard | +| | +| Adam Honse (CalcProgrammer1) 12 Mar 2020 | +| Matt Harper 05 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char rgb_fusion_dev_id; + +enum +{ + RGB_FUSION_2_IDX_MODE = 0x01, /* Mode index */ + RGB_FUSION_2_IDX_BRIGHTNESS = 0x02, /* Brightness index */ + RGB_FUSION_2_IDX_MAX_BRIGHTNESS = 0x02, /* Max brightness index */ + RGB_FUSION_2_IDX_MIN_BRIGHTNESS = 0x03, /* Minimum brightness index */ + RGB_FUSION_2_IDX_BLUE = 0x04, /* Blue index */ + RGB_FUSION_2_IDX_GREEN = 0x05, /* Green index */ + RGB_FUSION_2_IDX_RED = 0x06, /* Red index */ + RGB_FUSION_2_IDX_WHITE = 0x07, /* White index */ + RGB_FUSION_2_TIMER_1_LSB = 0x08, /* Timer 1 LSB. Valid timer values [0-65535] */ + RGB_FUSION_2_TIMER_1_MSB = 0x09, /* Timer 1 MSB. Timer unis are milliseconds */ + RGB_FUSION_2_TIMER_2_LSB = 0x0A, /* Timer 2 LSB */ + RGB_FUSION_2_TIMER_2_MSB = 0x0B, /* Timer 2 MSB */ + RGB_FUSION_2_TIMER_3_LSB = 0x0C, /* Timer 3 LSB */ + RGB_FUSION_2_TIMER_3_MSB = 0x0D, /* Timer 3 MSB */ + RGB_FUSION_2_IDX_OPT_1 = 0x0E, /* Option 1. Use case varies by mode */ + RGB_FUSION_2_IDX_OPT_2 = 0x0F, /* Option 2. Use case varies by mode */ +}; + +enum +{ + RGB_FUSION_2_APPLY_ADDR = 0x17, + RGB_FUSION_2_LED_START_ADDR = 0x20, + RGB_FUSION_2_SMBUS_ADDR = 0x68, +}; + +enum +{ + RGB_FUSION_2_ACTION_APPLY = 0x01ff, +}; + +enum +{ + RGB_FUSION_2_MODE_PULSE = 0x01, /* Pulse mode */ + RGB_FUSION_2_MODE_MUSIC = 0x02, /* Music mode */ + RGB_FUSION_2_MODE_COLOR_CYCLE = 0x03, /* Color cycle mode */ + RGB_FUSION_2_MODE_STATIC = 0x04, /* Static color mode */ + RGB_FUSION_2_MODE_FLASHING = 0x05, /* Flashing / Double Flashing mode */ + RGB_FUSION_2_MODE_TRANSITION = 0x09, /* Gradual transition from current */ + RGB_FUSION_2_MODE_DIGITAL_WAVE = 0x0A, /* Wave mode */ + RGB_FUSION_2_MODE_DIGITAL_A = 0x0B, /* */ + RGB_FUSION_2_MODE_DIGITAL_B = 0x0C, /* */ + RGB_FUSION_2_MODE_DIGITAL_C = 0x0D, /* */ + RGB_FUSION_2_MODE_DIGITAL_D = 0x0F, /* */ + RGB_FUSION_2_MODE_DIGITAL_E = 0x10, /* */ + RGB_FUSION_2_MODE_DIGITAL_F = 0x11, /* */ + RGB_FUSION_2_MODE_DIGITAL_G = 0x12, /* */ +// RGB_FUSION_2_MODE_DIGITAL_H = 0x11, +// RGB_FUSION_2_MODE_DIGITAL_I = 0x12, They are variants of DIGITAL F and G +}; + +enum +{ + RGB_FUSION_2_SPEED_FAST = 0x01, + RGB_FUSION_2_SPEED_NORMAL = 0x02, + RGB_FUSION_2_SPEED_SLOW = 0x04, + RGB_FUSION_2_DIGITAL_SPEED = 0x91, + RGB_FUSION_2_DIGITAL_SPEED_MIN = 0xe6, + RGB_FUSION_2_DIGITAL_SPEED_MAX = 0x32, +}; + +enum +{ + RGB_FUSION_2_BRIGHTNESS_MAX = 0x64, + RGB_FUSION_2_BRIGHTNESS_MIN = 0x0f, +}; + +class RGBFusion2SMBusController +{ +public: + RGBFusion2SMBusController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev, std::string mb_name); + ~RGBFusion2SMBusController(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + unsigned int GetLEDCount(); + void Apply(); + + void SetLEDEffect + ( + unsigned int led, + int mode, + unsigned int brightness, + unsigned int speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + unsigned int led_count; + i2c_smbus_interface* bus; + rgb_fusion_dev_id dev; + std::string name; + + unsigned char led_data[10][16]; + + void WriteLED(int); +}; diff --git a/Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusControllerDetect.cpp b/Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusControllerDetect.cpp new file mode 100644 index 0000000..05378f7 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusControllerDetect.cpp @@ -0,0 +1,169 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2SMBusControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus RGB Fusion 2 SMBus | +| motherboard | +| | +| Matt Harper 05 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "GigabyteRGBFusion2SMBusController.h" +#include "LogManager.h" +#include "RGBController_GigabyteRGBFusion2SMBus.h" +#include "SettingsManager.h" +#include "i2c_smbus.h" +#include "pci_ids.h" +#include "dmiinfo.h" + +#define DETECTOR_NAME "Gigabyte RGB Fusion 2 SMBus" +#define VENDOR_NAME "Gigabyte Technology Co., Ltd." +#define GIGABYTE_FOUND_MB_MESSAGE_EN "[%s] Success - Found '%s' in the JSON list" +#define GIGABYTE_NOT_FOUND_MB_MESSAGE_EN "[%s] FAILED - '%s' was not found in the JSON list. Do NOT enable if this is a USB based board." +#define SMBUS_ADDRESS 0x68 + +typedef struct +{ + const std::string manufacturer; + const std::string motherboard; +} motherboard_info; + +#define RGB_FUSION_2_SMBUS_NUM_DEVICES (sizeof(rgb_fusion_2_smbus_motherboards) / sizeof(rgb_fusion_2_smbus_motherboards[ 0 ])) + +json rgb_fusion_2_smbus_motherboards[] = +{ + "B450 AORUS ELITE", + "B450 AORUS ELITE V2", + "B450 AORUS M", + "B450 AORUS PRO WIFI-CF", + "B450 AORUS PRO-CF", + "B450 AORUS PRO-CF4", + "B450 I AORUS PRO WIFI-CF", + "B450M DS3H-CF", + "X299 DESIGNARE EX-CF", + "X399 AORUS XTREME-CF", + "X399 DESIGNARE EX-CF", + "X470 AORUS GAMING 5 WIFI", + "X470 AORUS GAMING 5 WIFI-CF", + "X470 AORUS GAMING 7 WIFI-CF", + "X470 AORUS GAMING 7 WIFI-50-CF", + "X470 AORUS ULTRA GAMING", + "X470 AORUS ULTRA GAMING-CF", + "B360M AORUS Gaming 3-CF", + "Z370 AORUS Gaming 5-CF", + "Z370 AORUS Ultra Gaming-CF" +}; + +/******************************************************************************************\ +* * +* TestForGigabyteRGBFusion2SMBusController * +* * +* Tests the given address to see if an RGB 2 Fusion controller exists there. First * +* does a quick write to test for a response * +* * +\******************************************************************************************/ + +bool TestForGigabyteRGBFusion2SMBusController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + + int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE); + + if (res >= 0) + { + pass = true; + } + + return(pass); + +} /* TestForRGBFusion2SMBusController() */ + +/******************************************************************************************\ +* * +* DetectGigabyteRGBFusion2SMBusControllers * +* * +* Detect RGB Fusion 2 controllers on the enumerated I2C busses at address 0x68. * +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion device is connected * +* dev - I2C address of RGB Fusion device * +* * +\******************************************************************************************/ + +void DetectGigabyteRGBFusion2SMBusControllers(std::vector& busses) +{ + SettingsManager* set_man = ResourceManager::get()->GetSettingsManager(); + json device_settings; + + DMIInfo dmi; + bool found = false; + + /*-------------------------------------------------*\ + | Get Linux LED settings from settings manager | + \*-------------------------------------------------*/ + device_settings = set_man->GetSettings(DETECTOR_NAME); + + if(!device_settings.contains("SupportedDevices")) + { + //If supported devices is not found then write it to settings + device_settings["SupportedDevices"] = rgb_fusion_2_smbus_motherboards; + set_man->SetSettings(DETECTOR_NAME, device_settings); + set_man->SaveSettings(); + } + + bool boolVendor = ( dmi.getManufacturer() == VENDOR_NAME ); + bool boolMotherboard = false; + nlohmann::detail::iter_impl result = std::find(std::begin(device_settings["SupportedDevices"]), std::end(device_settings["SupportedDevices"]), dmi.getMainboard() ); + + if(result != std::end(device_settings["SupportedDevices"])) + { + boolMotherboard = true; + } + found = ( boolVendor && boolMotherboard ); + + if(found) + { + LOG_DEBUG(GIGABYTE_FOUND_MB_MESSAGE_EN, DETECTOR_NAME, dmi.getMainboard().c_str()); + for(unsigned int bus = 0; bus < busses.size(); bus++) + { + IF_MOBO_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + if(busses[bus]->pci_subsystem_vendor == GIGABYTE_SUB_VEN) + { + // TODO - Is this necessary? Or an artifact of my own system? + // Skip dmcd devices + std::string device_name = std::string(busses[bus]->device_name); + + if(device_name.find("dmdc") == std::string::npos) + { + LOG_DEBUG(SMBUS_CHECK_DEVICE_MESSAGE_EN, DETECTOR_NAME, bus, VENDOR_NAME, SMBUS_ADDRESS); + + // Check for RGB Fusion 2 controller at 0x68 + if(TestForGigabyteRGBFusion2SMBusController(busses[bus], SMBUS_ADDRESS)) + { + RGBFusion2SMBusController* controller = new RGBFusion2SMBusController(busses[bus], SMBUS_ADDRESS, dmi.getMainboard() ); + RGBController_RGBFusion2SMBus* rgb_controller = new RGBController_RGBFusion2SMBus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } + else + { + LOG_DEBUG(SMBUS_CHECK_DEVICE_FAILURE_EN, DETECTOR_NAME, bus, VENDOR_NAME); + } + } + } + } + else + { + LOG_DEBUG(GIGABYTE_NOT_FOUND_MB_MESSAGE_EN, DETECTOR_NAME, dmi.getMainboard().c_str()); + } + +} /* DetectRGBFusion2SMBusControllers() */ + +REGISTER_I2C_DETECTOR(DETECTOR_NAME, DetectGigabyteRGBFusion2SMBusControllers); diff --git a/Controllers/GigabyteRGBFusion2SMBusController/RGBController_GigabyteRGBFusion2SMBus.cpp b/Controllers/GigabyteRGBFusion2SMBusController/RGBController_GigabyteRGBFusion2SMBus.cpp new file mode 100644 index 0000000..9a7b37c --- /dev/null +++ b/Controllers/GigabyteRGBFusion2SMBusController/RGBController_GigabyteRGBFusion2SMBus.cpp @@ -0,0 +1,298 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusion2SMBus.cpp | +| | +| RGBController for Gigabyte Aorus RGB Fusion 2 SMBus | +| motherboard | +| | +| Matt Harper 05 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GigabyteRGBFusion2SMBus.h" + +/* TODO - Validate all of these + * CPU + * ??? + * Mobo logo - Verified + * Case rear + * Case + * ??? + * ??? + * ARGB header 1 + * ARGB header 2 + * + * Do ??? actually map to anything? Are they even supported? + * If not, what is an elegant way to display but not wreck existing logic? + */ +static const char* rgb_fusion_zone_names[] = +{ + "CPU", + "???", + "Motherboard Logo", + "Case Rear", + "Case", + "???", + "???", + "ARGB Header 1", + "ARGB Header 2", + "???" +}; + +/**------------------------------------------------------------------*\ + @name Gigabyte Fusion2 SMBus + @category Motherboard + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGigabyteRGBFusion2SMBusControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RGBFusion2SMBus::RGBController_RGBFusion2SMBus(RGBFusion2SMBusController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Gigabyte"; + description = "RGB Fusion 2 SMBus"; + location = controller->GetDeviceLocation(); + + type = DEVICE_TYPE_MOTHERBOARD; + + mode Direct; + Direct.name = "Direct"; + Direct.value = RGB_FUSION_2_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = RGB_FUSION_2_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Pulse.speed_min = RGB_FUSION_2_SPEED_SLOW; + Pulse.speed_max = RGB_FUSION_2_SPEED_FAST; + Pulse.speed = RGB_FUSION_2_SPEED_NORMAL; + Pulse.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Pulse); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = RGB_FUSION_2_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Flashing.speed_min = 0x01; + Flashing.speed_max = 0x04; + Flashing.speed = 0x02; + Flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flashing); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = RGB_FUSION_2_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + ColorCycle.speed_min = RGB_FUSION_2_SPEED_SLOW; + ColorCycle.speed_max = RGB_FUSION_2_SPEED_FAST; + ColorCycle.speed = RGB_FUSION_2_SPEED_NORMAL; + ColorCycle.brightness_min = RGB_FUSION_2_BRIGHTNESS_MIN; + ColorCycle.brightness_max = RGB_FUSION_2_BRIGHTNESS_MAX; + ColorCycle.brightness = RGB_FUSION_2_BRIGHTNESS_MAX; + ColorCycle.color_mode = MODE_COLORS_PER_LED; + modes.push_back(ColorCycle); + + mode DigitalWave; + DigitalWave.name = "Digital Wave"; + DigitalWave.value = RGB_FUSION_2_MODE_DIGITAL_WAVE; + DigitalWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + DigitalWave.speed_min = 0xff; + DigitalWave.speed_max = 0x49; + DigitalWave.speed = 0xc1; + DigitalWave.brightness_min = RGB_FUSION_2_BRIGHTNESS_MIN; + DigitalWave.brightness_max = RGB_FUSION_2_BRIGHTNESS_MAX; + DigitalWave.brightness = RGB_FUSION_2_BRIGHTNESS_MAX; + DigitalWave.color_mode = MODE_COLORS_PER_LED; + modes.push_back(DigitalWave); + + mode DigitalA; + DigitalA.name = "Digital A"; + DigitalA.value = RGB_FUSION_2_MODE_DIGITAL_A; + DigitalA.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + DigitalA.speed_min = RGB_FUSION_2_DIGITAL_SPEED_MIN; + DigitalA.speed_max = RGB_FUSION_2_DIGITAL_SPEED_MAX; + DigitalA.speed = RGB_FUSION_2_DIGITAL_SPEED; + DigitalA.color_mode = MODE_COLORS_PER_LED; + modes.push_back(DigitalA); + + mode DigitalB; + DigitalB.name = "Digital B"; + DigitalB.value = RGB_FUSION_2_MODE_DIGITAL_B; + DigitalB.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + DigitalB.speed_min = 0xf0; + DigitalB.speed_max = 0x3c; + DigitalB.speed = 0xa0; + DigitalB.color_mode = MODE_COLORS_PER_LED; + modes.push_back(DigitalB); + + mode DigitalC; + DigitalC.name = "Digital C"; + DigitalC.value = RGB_FUSION_2_MODE_DIGITAL_C; + DigitalC.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + DigitalC.speed_min = RGB_FUSION_2_DIGITAL_SPEED_MIN; + DigitalC.speed_max = RGB_FUSION_2_DIGITAL_SPEED_MAX; + DigitalC.speed = RGB_FUSION_2_DIGITAL_SPEED; + DigitalC.color_mode = MODE_COLORS_PER_LED; + modes.push_back(DigitalC); + + mode DigitalD; + DigitalD.name = "Digital D"; + DigitalD.value = RGB_FUSION_2_MODE_DIGITAL_D; + DigitalD.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + DigitalD.speed_min = RGB_FUSION_2_DIGITAL_SPEED_MIN; + DigitalD.speed_max = RGB_FUSION_2_DIGITAL_SPEED_MAX; + DigitalD.speed = RGB_FUSION_2_DIGITAL_SPEED; + DigitalD.brightness_min = RGB_FUSION_2_BRIGHTNESS_MIN; + DigitalD.brightness_max = RGB_FUSION_2_BRIGHTNESS_MAX; + DigitalD.brightness = RGB_FUSION_2_BRIGHTNESS_MAX; + DigitalD.color_mode = MODE_COLORS_PER_LED; + modes.push_back(DigitalD); + + mode DigitalE; + DigitalE.name = "Digital E"; + DigitalE.value = RGB_FUSION_2_MODE_DIGITAL_E; + DigitalE.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + DigitalE.speed_min = 0x96; + DigitalE.speed_max = RGB_FUSION_2_DIGITAL_SPEED_MAX; + DigitalE.speed = 0x6e; + DigitalE.color_mode = MODE_COLORS_PER_LED; + modes.push_back(DigitalE); + + mode DigitalF; + DigitalF.name = "Digital F"; + DigitalF.value = RGB_FUSION_2_MODE_DIGITAL_F; + DigitalF.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; // F technically needs brightness when in color cycle mode (no color selected) + DigitalF.speed_min = RGB_FUSION_2_DIGITAL_SPEED_MIN; + DigitalF.speed_max = RGB_FUSION_2_DIGITAL_SPEED_MAX; + DigitalF.speed = RGB_FUSION_2_DIGITAL_SPEED; + DigitalF.brightness_min = RGB_FUSION_2_BRIGHTNESS_MIN; + DigitalF.brightness_max = RGB_FUSION_2_BRIGHTNESS_MAX; + DigitalF.brightness = RGB_FUSION_2_BRIGHTNESS_MAX; + DigitalF.color_mode = MODE_COLORS_PER_LED; + modes.push_back(DigitalF); + + mode DigitalG; + DigitalG.name = "Digital G"; + DigitalG.value = RGB_FUSION_2_MODE_DIGITAL_G; + DigitalG.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + DigitalG.speed_min = 0x8c; + DigitalG.speed_max = 0x46; + DigitalG.speed = 0x6e; + DigitalG.brightness_min = RGB_FUSION_2_BRIGHTNESS_MIN; + DigitalG.brightness_max = RGB_FUSION_2_BRIGHTNESS_MAX; + DigitalG.brightness = RGB_FUSION_2_BRIGHTNESS_MAX; + DigitalG.color_mode = MODE_COLORS_PER_LED; + modes.push_back(DigitalG); + + SetupZones(); + + // Initialize active mode + // TODO - broken. Need to complete GetDeviceMode + active_mode = GetDeviceMode(); +} + +RGBController_RGBFusion2SMBus::~RGBController_RGBFusion2SMBus() +{ + delete controller; +} + +void RGBController_RGBFusion2SMBus::SetupZones() +{ + /*---------------------------------------------------------*\ + | Search through all LEDs and create zones for each channel | + | type | + \*---------------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < controller->GetLEDCount(); zone_idx++) + { + zone* new_zone = new zone(); + + // Set zone name to channel name + new_zone->name = rgb_fusion_zone_names[zone_idx]; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + + // Push new zone to zones vector + zones.push_back(*new_zone); + } + + for(unsigned int led_idx = 0; led_idx < zones.size(); led_idx++) + { + led* new_led = new led(); + + // Set LED name to channel name + new_led->name = rgb_fusion_zone_names[led_idx]; + + // Push new LED to LEDs vector + leds.push_back(*new_led); + } + + SetupColors(); +} + +void RGBController_RGBFusion2SMBus::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RGBFusion2SMBus::DeviceUpdateLEDs() +{ + for(unsigned int led = 0; led < (unsigned int)colors.size(); led++) + { + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + int mode = modes[active_mode].value; + unsigned int speed = modes[active_mode].speed; + unsigned int brightness = modes[active_mode].brightness; + + controller->SetLEDEffect(led, mode, brightness, speed, red, grn, blu); + } + + controller->Apply(); +} + +void RGBController_RGBFusion2SMBus::UpdateZoneLEDs(int zone) +{ + RGBColor color = colors[zone]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + int mode = modes[active_mode].value; + unsigned int speed = modes[active_mode].speed; + unsigned int brightness = modes[active_mode].brightness; + + controller->SetLEDEffect(zone, mode, brightness, speed, red, grn, blu); + controller->Apply(); +} + +void RGBController_RGBFusion2SMBus::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_RGBFusion2SMBus::DeviceUpdateMode() +{ + +} + +// TODO - Research if possible to read device state +int RGBController_RGBFusion2SMBus::GetDeviceMode() +{ + return(0); +} diff --git a/Controllers/GigabyteRGBFusion2SMBusController/RGBController_GigabyteRGBFusion2SMBus.h b/Controllers/GigabyteRGBFusion2SMBusController/RGBController_GigabyteRGBFusion2SMBus.h new file mode 100644 index 0000000..1f3d1f1 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2SMBusController/RGBController_GigabyteRGBFusion2SMBus.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusion2SMBus.h | +| | +| RGBController for Gigabyte Aorus RGB Fusion 2 SMBus | +| motherboard | +| | +| Matt Harper 05 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteRGBFusion2SMBusController.h" + +class RGBController_RGBFusion2SMBus : public RGBController +{ +public: + RGBController_RGBFusion2SMBus(RGBFusion2SMBusController* controller_ptr); + ~RGBController_RGBFusion2SMBus(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RGBFusion2SMBusController* controller; + + int GetDeviceMode(); +}; diff --git a/Controllers/GigabyteRGBFusion2USBController/GigabyteFusion2USB_Devices.cpp b/Controllers/GigabyteRGBFusion2USBController/GigabyteFusion2USB_Devices.cpp new file mode 100644 index 0000000..be04ef7 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2USBController/GigabyteFusion2USB_Devices.cpp @@ -0,0 +1,7281 @@ +/*---------------------------------------------------------*\ +| Gigabyte_Fusion2_USB_Devices.cpp | +| | +| Gigabyte Fusion 2 USB Device layouts and | +| and mapping to the device IDs stored on chip | +| | +| megadjc 31 Jul 2025 | +| chrism 29 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "GigabyteRGBFusion2USBController.h" + +/*-------------------------------------------------------------------------*\ +| GB Fusion2 - Common Zone Definitions | +\*-------------------------------------------------------------------------*/ +static const gb_fusion2_zone common_d_led_zone = +{ + HDR_D_LED1, + 0, + 1024, + "D_LED" +}; + +static const gb_fusion2_zone common_d_led1_zone = +{ + HDR_D_LED1, + 0, + 1024, + "D_LED1" +}; + +static const gb_fusion2_zone common_dled1_dled2_zone = +{ + HDR_D_LED1, + 0, + 1024, + "D_LED1/D_LED2" +}; + +static const gb_fusion2_zone common_argb_v2_1_zone = +{ + HDR_D_LED1, + 0, + 1024, + "ARGB_V2_1" +}; + +static const gb_fusion2_zone alt_argb_v2_1_zone = +{ + LED3, + 0, + 1024, + "ARGB_V2_1" +}; + +static const gb_fusion2_zone common_argb_v2_1_3_zone = +{ + HDR_D_LED1, + 0, + 1024, + "ARGB_V2_1/ARGB_V2_3" +}; + +static const gb_fusion2_zone common_d_led2_zone = +{ + HDR_D_LED2, + 0, + 1024, + "D_LED2" +}; + +static const gb_fusion2_zone common_d_led2_aux_zone = +{ + HDR_D_LED2, + 2, + 1024, + "D_LED2 + Aux" +}; + +static const gb_fusion2_zone common_argb_v2_2_zone = +{ + HDR_D_LED2, + 0, + 1024, + "ARGB_V2_2" +}; + +static const gb_fusion2_zone alt_argb_v2_2_zone = +{ + LED4, + 0, + 1024, + "ARGB_V2_2" +}; + +static const gb_fusion2_zone common_argb_v2_3_1_zone = +{ + HDR_D_LED1, + 0, + 1024, + "ARGB_V2_3" +}; + +static const gb_fusion2_zone common_argb_v2_3_zone = +{ + HDR_D_LED3, + 0, + 1024, + "ARGB_V2_3" +}; + +static const gb_fusion2_zone common_argb_v2_4_zone = +{ + HDR_D_LED4, + 0, + 1024, + "ARGB_V2_4" +}; + +static const gb_fusion2_zone common_led1_zone = +{ + LED1, + 1, + 1, + "Name for Led 1" +}; + +static const gb_fusion2_zone common_led2_zone = +{ + LED2, + 1, + 1, + "Name for Led 2" +}; + +static const gb_fusion2_zone common_led3_zone = +{ + LED3, + 1, + 1, + "Name for Led 3" +}; + +static const gb_fusion2_zone common_led4_zone = +{ + LED4, + 1, + 1, + "Name for Led 4" +}; + +static const gb_fusion2_zone common_led5_zone = +{ + LED5, + 1, + 1, + "Name for Led 5" +}; + +static const gb_fusion2_zone common_led8_zone = +{ + LED8, + 1, + 1, + "Name for Led 8" +}; + +static const gb_fusion2_zone common_led9_zone = +{ + LED9, + 1, + 1, + "Name for Led 9" +}; + +static const gb_fusion2_zone common_led10_zone = +{ + LED10, + 1, + 1, + "Name for Led 10" +}; + +static const gb_fusion2_zone common_led11_zone = +{ + LED11, + 1, + 1, + "Name for Led 11" +}; + +static const gb_fusion2_zone common_amp_up_logo_4_zone = +{ + LED4, + 1, + 1, + "AMP UP Logo" +}; + +static const gb_fusion2_zone common_aor_logo_3_zone = +{ + LED3, + 1, + 1, + "Aorus Logo" +}; + +static const gb_fusion2_zone common_brs_3_zone = +{ + LED3, + 1, + 1, + "Board Accent (Right Side)" +}; + +static const gb_fusion2_zone common_brs_4_zone = +{ + LED4, + 1, + 1, + "Board Accent (Right Side)" +}; + +static const gb_fusion2_zone common_brs_8_zone = +{ + LED8, + 1, + 1, + "Board Accent (Right Side)" +}; + +static const gb_fusion2_zone common_brst_1_zone = +{ + LED1, + 1, + 1, + "Board Accent (Right Side, Top)" +}; + +static const gb_fusion2_zone common_brst_3_zone = +{ + LED3, + 1, + 1, + "Board Accent (Right Side, Top)" +}; + +static const gb_fusion2_zone common_brstm_2_zone = +{ + LED2, + 1, + 1, + "Board Accent (Right Side, Top Middle)" +}; + +static const gb_fusion2_zone common_brstm_4_zone = +{ + LED4, + 1, + 1, + "Board Accent (Right Side, Top Middle)" +}; + +static const gb_fusion2_zone common_brsbm_3_zone = +{ + LED3, + 1, + 1, + "Board Accent (Right Side, Bottom Middle)" +}; + +static const gb_fusion2_zone common_brsb_4_zone = +{ + LED4, + 1, + 1, + "Board Accent (Right Side, Bottom)" +}; + +static const gb_fusion2_zone common_chip_acc_3_zone = +{ + LED3, + 1, + 1, + "Chipset Accent" +}; + +static const gb_fusion2_zone common_chip_acc_4_zone = +{ + LED4, + 1, + 1, + "Chipset Accent" +}; + +static const gb_fusion2_zone common_chip_acc_6_zone = +{ + LED6, + 1, + 1, + "Chipset Accent" +}; + +static const gb_fusion2_zone common_chip_acc_7_zone = +{ + LED7, + 1, + 1, + "Chipset Accent" +}; + +static const gb_fusion2_zone common_chip_acc_11_zone = +{ + LED11, + 1, + 1, + "Chipset Accent" +}; + +static const gb_fusion2_zone common_led_c_2_zone = +{ + LED2, + 1, + 1, + "LED_C" +}; + +static const gb_fusion2_zone common_led_c1_2_zone = +{ + LED2, + 1, + 1, + "LED_C1" +}; + +static const gb_fusion2_zone common_led_c3_4_zone = +{ + LED4, + 1, + 1, + "LED_C3" +}; + +static const gb_fusion2_zone common_led_c_5_zone = +{ + LED5, + 1, + 1, + "LED_C" +}; + +static const gb_fusion2_zone common_led_c2_5_zone = +{ + LED5, + 1, + 1, + "LED_C2" +}; + +static const gb_fusion2_zone common_led_c1_c2_5_zone = +{ + LED5, + 1, + 1, + "LED_C1/LED_C2" +}; + +static const gb_fusion2_zone common_led_cpu_2_zone = +{ + LED2, + 1, + 1, + "LED_CPU" +}; + +static const gb_fusion2_zone common_led_cpu_3_zone = +{ + LED3, + 1, + 1, + "LED_CPU" +}; + +static const gb_fusion2_zone common_led_cpu_8_zone = +{ + LED8, + 1, + 1, + "LED_CPU" +}; + +static const gb_fusion2_zone common_ess_logo_4_zone = +{ + LED4, + 1, + 1, + "ESS Logo" +}; + +static const gb_fusion2_zone common_game_on_1_zone = +{ + LED1, + 1, + 1, + "Game On LED" +}; + +static const gb_fusion2_zone common_io_cov_1_zone = +{ + LED1, + 1, + 1, + "I/O Cover" +}; + +static const gb_fusion2_zone common_io_cov_7_zone = +{ + LED7, + 6, + 6, + "I/O Cover" +}; + +static const gb_fusion2_zone common_io_cov_10_zone = +{ + LED10, + 1, + 1, + "I/O Cover" +}; + +static const gb_fusion2_zone common_io_cov_btm_1_zone = +{ + LED1, + 1, + 1, + "I/O Cover (Bottom)" +}; + +static const gb_fusion2_zone common_io_cov_btm_4_zone = +{ + LED4, + 1, + 1, + "I/O Cover (Bottom)" +}; + +static const gb_fusion2_zone common_io_cov_btm_mid_2_zone = +{ + LED2, + 1, + 1, + "I/O Cover (Bottom Middle)" +}; + +static const gb_fusion2_zone common_chip_io_cov_tm_3_zone = +{ + LED3, + 1, + 1, + "I/O Cover (Top Middle)" +}; + +static const gb_fusion2_zone common_io_cov_top_1_zone = +{ + LED1, + 1, + 1, + "I/O Cover (Top)" +}; + +static const gb_fusion2_zone common_io_cov_top_4_zone = +{ + LED4, + 1, + 1, + "I/O Cover (Top)" +}; + +static const gb_fusion2_zone common_pcie_acc_1_zone = +{ + LED1, + 1, + 1, + "PCI-E Accent" +}; + +static const gb_fusion2_zone common_pcie_acc_3_zone = +{ + LED3, + 1, + 1, + "PCI-E Accent" +}; + +static const gb_fusion2_zone common_pcie_acc_4_zone = +{ + LED4, + 1, + 1, + "PCI-E Accent" +}; + +static const gb_fusion2_zone common_ram_accent_2_zone = +{ + LED2, + 1, + 1, + "RAM Accent" +}; + +static const gb_fusion2_zone common_ram_cov_4_zone = +{ + LED4, + 1, + 1, + "RAM Cover" +}; + +static const gb_fusion2_zone common_ram_cov_7_zone = +{ + LED7, + 1, + 1, + "RAM Cover" +}; + +static const gb_fusion2_zone common_ram_cov_8_zone = +{ + LED8, + 1, + 1, + "RAM Cover" +}; + +static const gb_fusion2_zone common_ssd_cov_7_zone = +{ + LED7, + 1, + 1, + "SSD Cover" +}; + +static const gb_fusion2_zone common_ssd_cov_8_zone = +{ + LED8, + 1, + 1, + "SSD Cover" +}; + +static const gb_fusion2_zone common_wifi_ant_9_zone = +{ + LED9, + 1, + 1, + "WIFI Antenna" +}; + +static const gb_fusion2_zone common_wifi_ant_11_zone = +{ + LED11, + 1, + 1, + "WIFI Antenna" +}; + +static const gb_fusion2_zone common_xmp_logo_2_zone = +{ + LED2, + 1, + 1, + "XMP Logo" +}; + +static const gb_fusion2_zone common_xtrm_logo_3_zone = +{ + LED3, + 1, + 1, + "XTREME Logo" +}; + +/*-------------------------------------------------------------------------*\ +| GB Fusion2 Layouts | +\*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------*\ +| Generic 048D:8297/048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "Name for Led 1" : Single | +| Zone "Name for LED 2" : Single | +| Zone "Name for LED 3" : Single | +| Zone "Name for LED 4" : Single | +| Zone "Name for LED 5" : Single | +| Zone "Name for LED 8" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led1_zone, + &common_led2_zone, + &common_led3_zone, + &common_led4_zone, + &common_led5_zone, + &common_led8_zone, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device generic_it8297_device = +{ + &it8297_device, + 0x0000005F, + 1, + "GENERIC IT8297/IT5702 LAYOUT", +}; + + +/*-------------------------------------------------------------*\ +| Generic 048D:8950 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8950_device = +{ + &alt_argb_v2_1_zone, + &alt_argb_v2_2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device generic_it8950_device = +{ + &it8950_device, + 0x0000005F, + 0, + "GENERIC IT82950 LAYOUT", +}; + +/*-------------------------------------------------------------*\ +| Generic 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "ARGB_V2_4" : Linear | +| Zone "Name for Led 1" : Single | +| Zone "Name for LED 2" : Single | +| Zone "Name for LED 3" : Single | +| Zone "Name for LED 4" : Single | +| Zone "Name for LED 5" : Single | +| Zone "Name for LED 9" : Single | +| Zone "Name for LED 10" : Single | +| Zone "Name for LED 11" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_argb_v2_4_zone, + &common_led1_zone, + &common_led2_zone, + &common_led3_zone, + &common_led4_zone, + &common_led5_zone, + &common_led9_zone, + &common_led10_zone, + &common_led11_zone, +}; + +static const gb_fusion2_device generic_it5711_device = +{ + &it5711_device, + 0x000001DF, + 0, + "GENERIC IT5711 LAYOUT", +}; + +/*-------------------------------------------------------------*\ +| Layout 1 048D:8950 (IT82950) | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8950_1_device = +{ + &alt_argb_v2_1_zone, + &alt_argb_v2_2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + + +static const gb_fusion2_device h810m_gmg_wifi6_device = +{ + &it8950_1_device, + 0x1810004D, + 0, + "H810M GAMING WIFI6", +}; + +static const gb_fusion2_device h810m_h_device = +{ + &it8950_1_device, + 0x1810004D, + 0, + "H810M H", +}; + +static const gb_fusion2_device h810m_s2h_device = +{ + &it8950_1_device, + 0x1810004D, + 0, + "H810M S2H", +}; + +/*-------------------------------------------------------------*\ +| Hybrid device with IT82950 and Super I/O | +\*-------------------------------------------------------------*/ +static const gb_fusion2_device b860m_d_device = +{ + &it8950_1_device, + 0x08F1004D, + 0, + "B860M D", +}; + +static const gb_fusion2_device b860m_d3hp_device = +{ + &it8950_1_device, + 0x08E1004D, + 0, + "B860M D3HP", +}; + +static const gb_fusion2_device b860m_e_device = +{ + &it8950_1_device, + 0x08F1004D, + 0, + "B860M E", +}; + +static const gb_fusion2_device b860m_h_device = +{ + &it8950_1_device, + 0x08F1004D, + 0, + "B860M H", +}; + +static const gb_fusion2_device b860m_k_device = +{ + &it8950_1_device, + 0x08F1004D, + 0, + "B860M K", +}; + +static const gb_fusion2_device z890m_gmg_x_device = +{ + &it8950_1_device, + 0x00F1004D, + 0, + "Z890M GAMING X", +}; + +/*-------------------------------------------------------------*\ +| Layout 1 048D:8297 | +| | +| Zone "D_LED1/D_LED2" : Linear | +| Zone "LED_CPU" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_1_device = +{ + &common_dled1_dled2_zone, + &common_led_cpu_2_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b450_gmg_x_device = +{ + &it8297_1_device, + 0x1110005F, + 0, + "B450 GAMING X", +}; + +static const gb_fusion2_device b450m_ds3h_wifi_8297_device = +{ + &it8297_1_device, + 0x1010005F, + 0, + "B450M DS3H WIFI", +}; + +/*-------------------------------------------------------------*\ +| Layout 2 048D:8297 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "Board Accent (Right Side)" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_2_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_brs_3_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z390_i_aor_pro_wifi_device = +{ + &it8297_2_device, + 0x0370005F, + 0, + "Z390 I AORUS PRO WIFI", +}; + +/*-------------------------------------------------------------*\ +| Layout 3 048D:8297 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_CPU" : Single | +| Zone "LED_C1/LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_3_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_cpu_3_zone, + &common_led_c1_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x570_gaming_x_device = +{ + &it8297_3_device, + 0x0110005F, + 0, + "X570 GAMING X", +}; + +/*-------------------------------------------------------------*\ +| Layout 4 048D:8297 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "I/O Cover" : Single | +| Zone "LED_C1" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_4_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_1_zone, + &common_led_c1_2_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device trx40_aor_master_device = +{ + &it8297_4_device, + 0x0210005F, + 0, + "TRX40 AORUS MASTER", +}; + +static const gb_fusion2_device trx40_aor_designare_device = +{ + &it8297_4_device, + 0x0210005F, + 0, + "TRX40 DESIGNARE", +}; + +/*-------------------------------------------------------------*\ +| Layout 5 048D:8297 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "I/O Cover" : Single | +| Zone "LED_C1" : Single | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_5_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_1_zone, + &common_led_c1_2_zone, + &common_pcie_acc_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device trx40_aor_pro_wifi_device = +{ + &it8297_5_device, + 0x0120005F, + 0, + "TRX40 AORUS PRO WIFI", +}; + +/*-------------------------------------------------------------*\ +| Layout 6 048D:8297 | +| | +| Zone "D_LED1/D_LED2" : Linear | +| Zone "IO Cover" : Linear | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C1/LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_6_device = +{ + &common_dled1_dled2_zone, + &common_io_cov_7_zone, + &common_pcie_acc_4_zone, + &common_led_c1_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x570_aor_mstr_device = +{ + &it8297_6_device, + 0x0120005F, + 0, + "X570 AORUS MASTER", +}; + +/*-------------------------------------------------------------*\ +| Layout 7 048D:8297 | +| | +| Zone "D_LED1/D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "XMP Logo" : Single | +| Zone "Chipset Accent" : Single | +| Zone "AMP Up Logo" : Single | +| Zone "LED_C1/LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_7_device = +{ + &common_dled1_dled2_zone, + &common_io_cov_1_zone, + &common_xmp_logo_2_zone, + &common_chip_acc_3_zone, + &common_amp_up_logo_4_zone, + &common_led_c1_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z390_aor_ultra_device = +{ + &it8297_7_device, + 0x0190005F, + 0, + "Z390 AORUS ULTRA", +}; + +/*-------------------------------------------------------------*\ +| Layout 8 048D:8297 | +| | +| Zone "D_LED1/D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "RAM Accent" : Single | +| Zone "Chipset Accent" : Single | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C1/LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_8_device = +{ + &common_dled1_dled2_zone, + &common_io_cov_1_zone, + &common_ram_accent_2_zone, + &common_chip_acc_3_zone, + &common_pcie_acc_4_zone, + &common_led_c1_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z390_aor_pro_device = +{ + &it8297_8_device, + 0x0180005F, + 0, + "Z390 AORUS PRO", +}; + +static const gb_fusion2_device z390_aor_pro_wifi_device = +{ + &it8297_8_device, + 0x0180005F, + 0, + "Z390 AORUS PRO WIFI", +}; + +/*-------------------------------------------------------------*\ +| Layout 9 048D:8297 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "LED_CPU" : Single | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C1/LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_9_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_1_zone, + &common_led_cpu_2_zone, + &common_pcie_acc_4_zone, + &common_led_c1_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x570_aor_elite_device = +{ + &it8297_9_device, + 0x0130005F, + 0, + "X570 AORUS ELITE", +}; + +static const gb_fusion2_device x570_aor_elite_wifi_device = +{ + &it8297_9_device, + 0x0130005F, + 0, + "X570 AORUS ELITE WIFI", +}; + +static const gb_fusion2_device x570_aor_pro_device = +{ + &it8297_9_device, + 0x0130005F, + 0, + "X570 AORUS PRO", +}; + +static const gb_fusion2_device x570_aor_pro_wifi_device = +{ + &it8297_9_device, + 0x0130005F, + 0, + "X570 AORUS PRO WIFI", +}; + +static const gb_fusion2_device x570_aor_ultra_device = +{ + &it8297_9_device, + 0x0140005F, + 0, + "X570 AORUS ULTRA", +}; + +/*-------------------------------------------------------------*\ +| Layout 10 048D:8297 | +| | +| Zone "D_LED1/D_LED2" : Linear | +| Zone "IO Cover" : Linear | +| Zone "PCI-E Accent" : Single | +| Zone "Chipset Accent" : Single | +| Zone "LED_C1/LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_10_device = +{ + &common_dled1_dled2_zone, + &common_io_cov_7_zone, + &common_pcie_acc_3_zone, + &common_chip_acc_4_zone, + &common_led_c1_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x570_aor_xtrm_device = +{ + &it8297_10_device, + 0x016001DF, + 0, + "X570 AORUS XTREME", +}; + +/*-------------------------------------------------------------*\ +| Layout 11 048D:8297 | +| | +| Zone "D_LED" : Linear | +| Zone "Board Accent (Right Side, Top)" : Single | +| Zone "Board Accent (Right Side, Top Middle)" : Single | +| Zone "Board Accent (Right Side, Bottom Middle)" : Single | +| Zone "Board Accent (Right Side, Bottom)" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_11_device = +{ + &common_d_led_zone, + &common_brst_1_zone, + &common_brstm_2_zone, + &common_brsbm_3_zone, + &common_brsb_4_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x570_i_aor_pro_wifi_device = +{ + &it8297_11_device, + 0x0350007F, + 0, + "X570 I AORUS PRO WIFI", +}; + +/*-------------------------------------------------------------*\ +| Layout 12 048D:8297 | +| | +| Zone "D_LED1/D_LED2" : Linear | +| Zone "IO Cover" : Linear | +| Zone "XMP Logo" : Single | +| Zone "Chipset Accent" : Single | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C1/LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_12_device = +{ + &common_dled1_dled2_zone, + &common_io_cov_7_zone, + &common_chip_acc_3_zone, + &common_pcie_acc_4_zone, + &common_led_c1_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z390_aor_mstr_device = +{ + &it8297_12_device, + 0x01A0005F, + 0, + "Z390 AORUS MASTER", +}; + +static const gb_fusion2_device z390_aor_mstr_g2_device = +{ + &it8297_12_device, + 0x01A0005F, + 0, + "Z390 AORUS MASTER G2 EDITION", +}; + +/*-------------------------------------------------------------*\ +| Layout 13 048D:8297 | +| | +| Zone "D_LED1/D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "XMP Logo" : Single | +| Zone "XTREME Logo" : Single | +| Zone "ESS Logo" : Single | +| Zone "LED_C1/LED_C2" : Single | +| Zone "Chipset Accent" : Single | +| Zone "Board Accent (Right Side)" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_13_device = +{ + &common_dled1_dled2_zone, + &common_xmp_logo_2_zone, + &common_xtrm_logo_3_zone, + &common_ess_logo_4_zone, + &common_led_c1_c2_5_zone, + &common_chip_acc_7_zone, + &common_brs_8_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z390_aor_xtrm_device = +{ + &it8297_13_device, + 0x01B0005F, + 0, + "Z390 AORUS XTREME", +}; + +static const gb_fusion2_device z390_aor_xtrm_wtr_force_device = +{ + &it8297_13_device, + 0x01B0005F, + 0, + "Z390 AORUS XTREME WATERFORCE", +}; + +static const gb_fusion2_device z390_aor_xtrm_wtr_force_5g_device = +{ + &it8297_13_device, + 0x01B0005F, + 0, + "Z390 AORUS XTREME WATERFORCE 5G", +}; + +/*-------------------------------------------------------------*\ +| Layout 14 048D:8297 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2 + AUX" : Linear | +| Zone "LED_C1" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_14_device = +{ + &common_d_led1_zone, + &common_d_led2_aux_zone, + &common_led_c1_2_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device trx40_aor_xtrm_device = +{ + &it8297_14_device, + 0x023001DF, + 0, + "TRX40 AORUS XTREME", +}; + +/*-------------------------------------------------------------*\ +| Layout 15 048D:8297 | +| | +| Zone "Aorus Logo" : Single | +| Zone "Chipset Accent" : Single | +| Zone "Board Accent (Right Side)" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it8297_15_device = +{ + &common_aor_logo_3_zone, + &common_chip_acc_4_zone, + &common_brs_8_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device trx40_aor_xtrm_2_device = +{ + &it8297_15_device, + 0x023001DF, + 1, + "TRX40 AORUS XTREME", +}; + +/*-------------------------------------------------------------*\ +| Layout 16 048D:5702 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_16_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device h610m_d3h_ddr4_device = +{ + &it5702_16_device, + 0x1810005F, + 0, + "H610M D3H DDR4", +}; + +static const gb_fusion2_device h610m_d3h_wifi_ddr4_device = +{ + &it5702_16_device, + 0x1810005F, + 0, + "H610M D3H WIFI DDR4", +}; + +static const gb_fusion2_device h610m_d3w_device = +{ + &it5702_16_device, + 0x1810005F, + 0, + "H610M D3W", +}; + +static const gb_fusion2_device h610m_d3w_wifi6_device = +{ + &it5702_16_device, + 0x1810005F, + 0, + "H610M D3W WIFI6", +}; + +static const gb_fusion2_device h610m_gmg_wifi_ddr4_device = +{ + &it5702_16_device, + 0x1810005F, + 0, + "H610M GAMING WIFI DDR4", +}; + +/*-------------------------------------------------------------*\ +| Layout 17 048D:5702 | +| | +| Zone "ARGB_V2_3" : Linear | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_17_device = +{ + &common_argb_v2_3_1_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650_ud_ac_device = +{ + &it5702_17_device, + 0x2120005F, + 0, + "B650 UD AC", +}; + +static const gb_fusion2_device b650_ud_ax_device = +{ + &it5702_17_device, + 0x2120005F, + 0, + "B650 UD AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 18 048D:5702 | +| | +| Zone "D_LED" : Linear | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_18_device = +{ + &common_d_led_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650i_aor_ultra_device = +{ + &it5702_18_device, + 0x0B30005F, + 0, + "B650I AORUS ULTRA", +}; + +/*-------------------------------------------------------------*\ +| Layout 19 048D:5702 | +| | +| Zone "D_LED" : Linear | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_19_device = +{ + &common_d_led_zone, + &common_led_c_2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device a620i_ax_device = +{ + &it5702_19_device, + 0x1330005F, + 0, + "A620I AX", +}; + +static const gb_fusion2_device a620m_c_device = +{ + &it5702_19_device, + 0x1030005F, + 0, + "A620M C", +}; + +static const gb_fusion2_device a620m_ds3h_device = +{ + &it5702_19_device, + 0x1030005F, + 0, + "A620M DS3H", +}; + +static const gb_fusion2_device a620m_gmg_x_device = +{ + &it5702_19_device, + 0x1030005F, + 0, + "A620M GAMING X", +}; + +static const gb_fusion2_device a620m_gmg_x_ax_device = +{ + &it5702_19_device, + 0x1030005F, + 0, + "A620M GAMING X AX", +}; + +static const gb_fusion2_device a620m_h_device = +{ + &it5702_19_device, + 0x1030005F, + 0, + "A620M H", +}; + +static const gb_fusion2_device a620m_s2h_device = +{ + &it5702_19_device, + 0x1030005F, + 0, + "A620M S2H", +}; + +static const gb_fusion2_device b650i_ax_device = +{ + &it5702_19_device, + 0x0B20005F, + 0, + "B650I AX", +}; + +static const gb_fusion2_device b650m_c_device = +{ + &it5702_19_device, + 0x0820005F, + 0, + "B650M C", +}; + +static const gb_fusion2_device b650m_c_v2_device = +{ + &it5702_19_device, + 0x0820005F, + 0, + "B650M C V2", +}; + +static const gb_fusion2_device b650m_c_v3_device = +{ + &it5702_19_device, + 0x0820005F, + 0, + "B650M C V3", +}; + +static const gb_fusion2_device b650m_d2h_device = +{ + &it5702_19_device, + 0x0820005F, + 0, + "B650M D2H", +}; + +static const gb_fusion2_device b650m_d2hp_device = +{ + &it5702_19_device, + 0x0820005F, + 0, + "B650M D2HP", +}; + +static const gb_fusion2_device b650m_ds3h_device = +{ + &it5702_19_device, + 0x0820005F, + 0, + "B650M DS3H", +}; + +static const gb_fusion2_device b650m_h_device = +{ + &it5702_19_device, + 0x0820005F, + 0, + "B650M H", +}; + +static const gb_fusion2_device b650m_k_device = +{ + &it5702_19_device, + 0x0820005F, + 0, + "B650M K", +}; + +static const gb_fusion2_device b650m_s2h_device = +{ + &it5702_19_device, + 0x0820005F, + 0, + "B650M S2H", +}; + +static const gb_fusion2_device b760m_pwr_device = +{ + &it5702_19_device, + 0x0810005F, + 0, + "B760M POWER", +}; + +static const gb_fusion2_device b760m_pwr_ddr4_device = +{ + &it5702_19_device, + 0x0810005F, + 0, + "B760M POWER DDR4", +}; + +static const gb_fusion2_device a520i_ac_device = +{ + &it5702_19_device, + 0x0040005F, + 0, + "A520I AC", +}; + +static const gb_fusion2_device a520m_ds3h_device = +{ + &it5702_19_device, + 0x0040005F, + 0, + "A520M DS3H", +}; + +static const gb_fusion2_device a520m_ds3h_ac_device = +{ + &it5702_19_device, + 0x0040005F, + 0, + "A520M DS3H AC", +}; + +static const gb_fusion2_device a520m_h_device = +{ + &it5702_19_device, + 0x0040005F, + 0, + "A520M H", +}; + +static const gb_fusion2_device a520m_s2h_device = +{ + &it5702_19_device, + 0x0040005F, + 0, + "A520M S2H", +}; + +static const gb_fusion2_device b550m_gmg_device = +{ + &it5702_19_device, + 0x0020005F, + 0, + "B550M GAMING", +}; + +static const gb_fusion2_device b550m_h_device = +{ + &it5702_19_device, + 0x0020005F, + 0, + "B550M H", +}; + +static const gb_fusion2_device b550m_s2h_device = +{ + &it5702_19_device, + 0x0020005F, + 0, + "B550M S2H", +}; + +static const gb_fusion2_device z590i_vis_d_device = +{ + &it5702_19_device, + 0x0310005F, + 0, + "Z590I VISION D", +}; + +/*-------------------------------------------------------------*\ +| Layout 20 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "LED_C1" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_20_device = +{ + &common_d_led1_zone, + &common_led_c1_2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z590_d_device = +{ + &it5702_20_device, + 0x0110005F, + 0, + "Z590 D", +}; + +static const gb_fusion2_device z490m_device = +{ + &it5702_20_device, + 0x00A0005F, + 0, + "Z490M", +}; + +static const gb_fusion2_device h490m_ds3h_device = +{ + &it5702_20_device, + 0x0870005F, + 0, + "H470M DS3H", +}; + +static const gb_fusion2_device b460m_ds3h_v2_device = +{ + &it5702_20_device, + 0x0870005F, + 0, + "B460M DS3H V2", +}; + +/*-------------------------------------------------------------*\ +| Layout 21 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_21_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b760m_h_v2_device = +{ + &it5702_21_device, + 0x1830005F, + 0, + "B760M H V2", +}; + +static const gb_fusion2_device z790_d_device = +{ + &it5702_21_device, + 0x0180005F, + 0, + "Z790 D", +}; + +static const gb_fusion2_device z790_d_ac_device = +{ + &it5702_21_device, + 0x0180005F, + 0, + "Z790 D AC", +}; + +static const gb_fusion2_device z790_d_ax_device = +{ + &it5702_21_device, + 0x0180005F, + 0, + "Z790 D AX", +}; + +static const gb_fusion2_device z790_d_wifi_device = +{ + &it5702_21_device, + 0x0180005F, + 0, + "Z790 D WIFI", +}; + +static const gb_fusion2_device z790_eagle_device = +{ + &it5702_21_device, + 0x0180005F, + 0, + "Z790 EAGLE", +}; + +static const gb_fusion2_device z790_eagle_ax_device = +{ + &it5702_21_device, + 0x0180005F, + 0, + "Z790 EAGLE AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 22 048D:5702 | +| | +| Zone "ARGB_V2_1/ARGB_V2_3" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_22_device = +{ + &common_argb_v2_1_3_zone, + &common_argb_v2_2_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650_eagle_device = +{ + &it5702_22_device, + 0x2130005F, + 0, + "B650 EAGLE", +}; + +static const gb_fusion2_device b650_eagle_ax_device = +{ + &it5702_22_device, + 0x2130005F, + 0, + "B650 EAGLE AX", +}; + +static const gb_fusion2_device trx50_aero_d_device = +{ + &it5702_22_device, + 0x2A10005F, + 0, + "TRX50 AERO D", +}; + +static const gb_fusion2_device x670_gmg_x_ax_v2_device = +{ + &it5702_22_device, + 0x1910005F, + 0, + "X670 GAMING X AX V2", +}; + +static const gb_fusion2_device z790_aor_mstr_x_device = +{ + &it5702_22_device, + 0x123001DF, + 0, + "Z790 AORUS MASTER X", +}; + +/*-------------------------------------------------------------*\ +| Layout 23 048D:5702 | +| | +| Zone "D_LED" : Linear | +| Zone "Board Accent(Right Side)" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_23_device = +{ + &common_d_led_zone, + &common_brs_3_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z490i_aor_ultra_device = +{ + &it5702_23_device, + 0x0350007F, + 0, + "Z490I AORUS ULTRA", +}; + +static const gb_fusion2_device h490i_aor_pro_ax_device = +{ + &it5702_23_device, + 0x0B50007F, + 0, + "H470I AORUS PRO AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 24 048D:5702 | +| | +| Zone "D_LED" : Linear | +| Zone "LED_CPU" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_24_device = +{ + &common_d_led_zone, + &common_led_cpu_3_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650e_tachyon_device = +{ + &it5702_24_device, + 0x0950005F, + 0, + "B650E TACHYON", +}; + +/*-------------------------------------------------------------*\ +| Layout 25 048D:5702 | +| | +| Zone "D_LED" : Linear | +| Zone "LED_C" : Single | +| Zone "Chipset Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_25_device = +{ + &common_d_led_zone, + &common_led_c_2_zone, + &common_chip_acc_4_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650m_gmg_x_ax_device = +{ + &it5702_25_device, + 0x0840015F, + 0, + "B650M GAMING X AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 26 048D:5702 | +| | +| Zone "D_LED" : Linear | +| Zone "LED_C" : Single | +| Zone "PCI-E Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_26_device = +{ + &common_d_led_zone, + &common_led_c_2_zone, + &common_pcie_acc_4_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650m_d2h_ddr4_device = +{ + &it5702_26_device, + 0x0810005F, + 0, + "B660M D2H DDR4", +}; + +static const gb_fusion2_device b650m_d3h_ddr4_device = +{ + &it5702_26_device, + 0x0810005F, + 0, + "B660M D3H DDR4", +}; + +static const gb_fusion2_device b650m_ds3h_ax_ddr4_device = +{ + &it5702_26_device, + 0x0810005F, + 0, + "B660M DS3H AX DDR4", +}; + +static const gb_fusion2_device b650m_ds3h_ddr4_device = +{ + &it5702_26_device, + 0x0810005F, + 0, + "B660M DS3H DDR4", +}; + +static const gb_fusion2_device b650m_gmg_ac_device = +{ + &it5702_26_device, + 0x0810005F, + 0, + "B660M GAMING AC", +}; + +static const gb_fusion2_device b650m_gmg_ac_ddr4_device = +{ + &it5702_26_device, + 0x0810005F, + 0, + "B660M GAMING AC DDR4", +}; + +static const gb_fusion2_device b650m_gmg_ddr4_device = +{ + &it5702_26_device, + 0x0810005F, + 0, + "B660M GAMING DDR4", +}; + +static const gb_fusion2_device b650m_pwr_ddr4_device = +{ + &it5702_26_device, + 0x0810005F, + 0, + "B660M POWER DDR4", +}; + +static const gb_fusion2_device z690m_ds3h_ddr4_device = +{ + &it5702_26_device, + 0x00C0005F, + 0, + "Z690M DS3H DDR4", +}; + +static const gb_fusion2_device b560_hd3_device = +{ + &it5702_26_device, + 0x1120005F, + 0, + "B560 HD3", +}; + +static const gb_fusion2_device b560m_d2v_device = +{ + &it5702_26_device, + 0x1020005F, + 0, + "B560M D2V", +}; + +static const gb_fusion2_device b560m_d3h_device = +{ + &it5702_26_device, + 0x1020005F, + 0, + "B560M D3H", +}; + +static const gb_fusion2_device b560m_ds3h_device = +{ + &it5702_26_device, + 0x1020005F, + 0, + "B560M DS3H", +}; + +static const gb_fusion2_device b560m_ds3h_ac_device = +{ + &it5702_26_device, + 0x1060005F, + 0, + "B560M DS3H AC", +}; + +static const gb_fusion2_device b560m_ds3h_plus_device = +{ + &it5702_26_device, + 0x1020005F, + 0, + "B560M DS3H PLUS", +}; + +static const gb_fusion2_device b560m_ds3h_v2_device = +{ + &it5702_26_device, + 0x1020005F, + 0, + "B560M DS3H V2", +}; + +static const gb_fusion2_device b560m_gmg_hd_device = +{ + &it5702_26_device, + 0x1020005F, + 0, + "B560M GAMING HD", +}; + +static const gb_fusion2_device b560m_h_device = +{ + &it5702_26_device, + 0x1020005F, + 0, + "B560M H", +}; + +static const gb_fusion2_device b560m_pwr_device = +{ + &it5702_26_device, + 0x1020005F, + 0, + "B560M POWER", +}; + +/*-------------------------------------------------------------*\ +| Layout 27 048D:5702 | +| | +| Zone "D_LED" : Linear | +| Zone "LED_C1" : Single | +| Zone "PCI-E Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_27_device = +{ + &common_d_led_zone, + &common_led_c1_2_zone, + &common_pcie_acc_4_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b760m_d2h_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M D2H", +}; + +static const gb_fusion2_device b760m_d2h_ddr4_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M D2H DDR4", +}; + +static const gb_fusion2_device b760m_d3h_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M D3H", +}; + +static const gb_fusion2_device b760m_d3h_ddr4_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M D3H DDR4", +}; + +static const gb_fusion2_device b760m_d3hp_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M D3HP", +}; + +static const gb_fusion2_device b760m_d3hp_ddr4_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M D3HP DDR4", +}; + +static const gb_fusion2_device b760m_d3hp_wifi6_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M D3HP WIFI6", +}; + +static const gb_fusion2_device b760m_ds3h_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M DS3H", +}; + +static const gb_fusion2_device b760m_ds3h_ax_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M DS3H AX", +}; + +static const gb_fusion2_device b760m_ds3h_ax_ddr4_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M DS3H AX DDR4", +}; + +static const gb_fusion2_device b760m_ds3h_ddr4_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M DS3H DDR4", +}; + +static const gb_fusion2_device b760m_gmg_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M GAMING", +}; + +static const gb_fusion2_device b760m_gmg_ac_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M GAMING AC", +}; + +static const gb_fusion2_device b760m_gmg_ac_ddr4_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M GAMING AC DDR4", +}; + +static const gb_fusion2_device b760m_gmg_ddr4_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M GAMING DDR4", +}; + +static const gb_fusion2_device b760m_gmg_plus_wifi_ddr4_device = +{ + &it5702_27_device, + 0x0810005F, + 0, + "B760M GAMING PLUS WIFI DDR4", +}; + +/*-------------------------------------------------------------*\ +| Layout 28 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_28_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c_2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device h470_hd3_device = +{ + &it5702_28_device, + 0x0970005F, + 0, + "H470 HD3", +}; + +static const gb_fusion2_device a620m_ds3h_2_device = +{ + &it5702_28_device, + 0x1040005F, + 0, + "A620M DS3H", +}; + +static const gb_fusion2_device a620m_gmg_x_2_device = +{ + &it5702_28_device, + 0x1040005F, + 0, + "A620M GAMING X", +}; + +static const gb_fusion2_device a620m_gmg_x_ax_2_device = +{ + &it5702_28_device, + 0x1040005F, + 0, + "A620M GAMING X AX", +}; + +static const gb_fusion2_device a620m_h_2_device = +{ + &it5702_28_device, + 0x1040005F, + 0, + "A620M H", +}; + +static const gb_fusion2_device a620m_s2h_2_device = +{ + &it5702_28_device, + 0x1040005F, + 0, + "A620M S2H", +}; + +static const gb_fusion2_device b650m_d3hp_device = +{ + &it5702_28_device, + 0x0870005F, + 0, + "B650M D3HP", +}; + +static const gb_fusion2_device b650m_d3hp_ax_device = +{ + &it5702_28_device, + 0x0870005F, + 0, + "B650M D3HP AX", +}; + +static const gb_fusion2_device b650m_ds3h_2_device = +{ + &it5702_28_device, + 0x0870005F, + 0, + "B650M DS3H", +}; + +static const gb_fusion2_device b650m_gmg_plus_wifi_device = +{ + &it5702_28_device, + 0x0870005F, + 0, + "B650M GAMING PLUS WIFI", +}; + +static const gb_fusion2_device b650m_gmg_wifi_device = +{ + &it5702_28_device, + 0x0870005F, + 0, + "B650M GAMING WIFI", +}; + +static const gb_fusion2_device b650m_gmg_wifi6e_device = +{ + &it5702_28_device, + 0x0870005F, + 0, + "B650M GAMING WIFI6E", +}; + +static const gb_fusion2_device b650m_k_2_device = +{ + &it5702_28_device, + 0x0870005F, + 0, + "B650M K", +}; + +/*-------------------------------------------------------------*\ +| Layout 29 048D:5702 | +| | +| Zone "ARGB_V2_1/ARGB_V2_3" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "Chipset Accent" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_29_device = +{ + &common_argb_v2_1_3_zone, + &common_argb_v2_2_zone, + &common_chip_acc_4_zone, + &common_led_c_2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650_aor_elite_ax_ice_device = +{ + &it5702_29_device, + 0x2110015F, + 0, + "B650 AORUS ELITE AX ICE", +}; + +static const gb_fusion2_device b650_aor_elite_ax_v2_device = +{ + &it5702_29_device, + 0x2110015F, + 0, + "B650 AORUS ELITE AX V2", +}; + +static const gb_fusion2_device b650_aor_elite_v2_device = +{ + &it5702_29_device, + 0x2110015F, + 0, + "B650 AORUS ELITE V2", +}; + +static const gb_fusion2_device b650_aor_elite_x_ax_ice_device = +{ + &it5702_29_device, + 0x2110015F, + 0, + "B650E AORUS ELITE X AX ICE", +}; + +/*-------------------------------------------------------------*\ +| Layout 30 048D:5702 | +| | +| Zone "ARGB_V2_1/ARGB_V2_3" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "Chipset Accent" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_30_device = +{ + &common_argb_v2_1_3_zone, + &common_argb_v2_2_zone, + &common_chip_acc_3_zone, + &common_led_c_2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z790_aor_elite_x_device = +{ + &it5702_30_device, + 0x1120015F, + 0, + "Z790 AORUS ELITE X", +}; + +static const gb_fusion2_device z790_aor_elite_x_ax_device = +{ + &it5702_30_device, + 0x1120015F, + 0, + "Z790 AORUS ELITE X AX", +}; + +static const gb_fusion2_device z790_aor_elite_x_wifi7_device = +{ + &it5702_30_device, + 0x1120015F, + 0, + "Z790 AORUS ELITE X WIFI7", +}; + +static const gb_fusion2_device z790_aor_tachyon_x_device = +{ + &it5702_30_device, + 0x1120015F, + 0, + "Z790 AORUS TACHYON X", +}; + +/*-------------------------------------------------------------*\ +| Layout 31 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_CPU" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_31_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_cpu_3_zone, + &common_led_c_2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b450m_ds3h_v3_device = +{ + &it5702_31_device, + 0x2010005F, + 0, + "B450M DS3H V3", +}; + +static const gb_fusion2_device b450m_ds3h_wifi_device = +{ + &it5702_31_device, + 0x2010005F, + 0, + "B450M DS3H WIFI", +}; + +/*-------------------------------------------------------------*\ +| Layout 32 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C1" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_32_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c1_2_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z490_gmg_x_device = +{ + &it5702_32_device, + 0x0140005F, + 0, + "Z490 GAMING X", +}; + +static const gb_fusion2_device z490_gmg_x_ax_device = +{ + &it5702_32_device, + 0x0140005F, + 0, + "Z490 GAMING X AX", +}; + +static const gb_fusion2_device z490_ud_device = +{ + &it5702_32_device, + 0x0140005F, + 0, + "Z490 UD", +}; + +static const gb_fusion2_device z490_ud_ac_device = +{ + &it5702_32_device, + 0x0140005F, + 0, + "Z490 UD AC", +}; + +static const gb_fusion2_device b460m_ds3h_ac_device = +{ + &it5702_32_device, + 0x1080005F, + 0, + "B460M DS3H AC", +}; + +static const gb_fusion2_device b660_ds3h_ac_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B660 DS3H AC", +}; + +static const gb_fusion2_device b660_ds3h_ac_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B660 DS3H AC DDR4", +}; + +static const gb_fusion2_device b660_ds3h_ax_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B660 DS3H AX DDR4", +}; + +static const gb_fusion2_device b660_ds3h_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B660 DS3H DDR4", +}; + +static const gb_fusion2_device b660_gmg_x_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B660 GAMING X", +}; + +static const gb_fusion2_device b660_gmg_x_ax_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B660 GAMING X AX DDR4", +}; + +static const gb_fusion2_device b660_gmg_x_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B660 GAMING X DDR4", +}; + +static const gb_fusion2_device b660m_aor_elite_ax_ddr4_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B660M AORUS ELITE AX DDR4", +}; + +static const gb_fusion2_device b660m_aor_elite_ddr4_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B660M AORUS ELITE DDR4", +}; + +static const gb_fusion2_device b660m_gmg_x_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B660M GAMING X", +}; + +static const gb_fusion2_device b660m_gmg_x_ax_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B660M GAMING X AX", +}; + +static const gb_fusion2_device b660m_gmg_x_ax_ddr4_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B660M GAMING X AX DDR4", +}; + +static const gb_fusion2_device b660m_gmg_x_ddr4_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B660M GAMING X DDR4", +}; + +static const gb_fusion2_device b760_aor_elite_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 AORUS ELITE", +}; + +static const gb_fusion2_device b760_aor_elite_ax_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 AORUS ELITE AX", +}; + +static const gb_fusion2_device b760_aor_elite_ax_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 AORUS ELITE AX DDR4", +}; + +static const gb_fusion2_device b760_aor_elite_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 AORUS ELITE DDR4", +}; + +static const gb_fusion2_device b760_ds3h_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 DS3H", +}; + +static const gb_fusion2_device b760_ds3h_ac_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 DS3H AC", +}; + +static const gb_fusion2_device b760_ds3h_ac_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 DS3H AC DDR4", +}; + +static const gb_fusion2_device b760_ds3h_ax_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 DS3H AX", +}; + +static const gb_fusion2_device b760_ds3h_ax_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 DS3H AX DDR4", +}; + +static const gb_fusion2_device b760_ds3h_ax_v2_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 DS3H AX V2", +}; + +static const gb_fusion2_device b760_ds3h_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 DS3H DDR4", +}; + +static const gb_fusion2_device b760_gmg_x_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 GAMING X", +}; + +static const gb_fusion2_device b760_gmg_x_ax_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 GAMING X AX", +}; + +static const gb_fusion2_device b760_gmg_x_ax_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 GAMING X AX DDR4", +}; + +static const gb_fusion2_device b760_gmg_x_ddr4_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "B760 GAMING X DDR4", +}; + +static const gb_fusion2_device b760m_c_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B760M C", +}; + +static const gb_fusion2_device b760m_c_v2_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B760M C V2", +}; + +static const gb_fusion2_device b760m_gmg_x_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B760M GAMING X", +}; + +static const gb_fusion2_device b760m_gmg_x_ax_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B760M GAMING X AX", +}; + +static const gb_fusion2_device b760m_gmg_x_ax_ddr4_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B760M GAMING X AX DDR4", +}; + +static const gb_fusion2_device b760m_gmg_x_ddr4_device = +{ + &it5702_32_device, + 0x0820005F, + 0, + "B760M GAMING X DDR4", +}; + +static const gb_fusion2_device z690_aero_d_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 AERO D", +}; + +static const gb_fusion2_device z690_aero_g_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 AERO G", +}; + +static const gb_fusion2_device z690_aero_g_ddr4_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 AERO G DDR4", +}; + +static const gb_fusion2_device z690_aor_elite_ax_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 AORUS ELITE AX", +}; + +static const gb_fusion2_device z690_ud_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 UD", +}; + +static const gb_fusion2_device z690_ud_ac_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 UD AC", +}; + +static const gb_fusion2_device z690_ud_ax_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 UD AX", +}; + +static const gb_fusion2_device z690_ud_ax_ddr4_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 UD AX DDR4", +}; + +static const gb_fusion2_device z690_ud_ax_ddr4_v2_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 UD AX DDR4 V2", +}; + +static const gb_fusion2_device z690_ud_ax_v2_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 UD AX V2", +}; + +static const gb_fusion2_device z690_ud_ddr4_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 UD DDR4", +}; + +static const gb_fusion2_device z690_ud_ddr4_v2_device = +{ + &it5702_32_device, + 0x0110005F, + 0, + "Z690 UD DDR4 V2", +}; + +static const gb_fusion2_device z690m_aor_elite_ax_ddr4_device = +{ + &it5702_32_device, + 0x0010005F, + 0, + "Z690M AORUS ELITE AX DDR4", +}; + +static const gb_fusion2_device z690m_aor_elite_ddr4_device = +{ + &it5702_32_device, + 0x0010005F, + 0, + "Z690M AORUS ELITE DDR4", +}; + +static const gb_fusion2_device z790_aero_g_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z790 AERO G", +}; + +static const gb_fusion2_device z790_d_ddr4_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z790 D DDR4", +}; + +static const gb_fusion2_device z790_gmg_plus_ax_device = +{ + &it5702_32_device, + 0x0920005F, + 0, + "Z790 GAMING PLUS AX", +}; + +static const gb_fusion2_device z790_gmg_x_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z790 GAMING X", +}; + +static const gb_fusion2_device z790_gmg_x_ax_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z790 GAMING X AX", +}; + +static const gb_fusion2_device z790_s_ddr4_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z790 S DDR4", +}; + +static const gb_fusion2_device z790_s_wifi_ddr4_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z790 S WIFI DDR4", +}; + +static const gb_fusion2_device z790_ud_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z790 UD", +}; + +static const gb_fusion2_device z790_ud_ac_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z790 UD AC", +}; + +static const gb_fusion2_device z790_ud_ax_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z790 UD AX", +}; + +static const gb_fusion2_device z590_ud_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z590 UD", +}; + +static const gb_fusion2_device z590_ud_ac_device = +{ + &it5702_32_device, + 0x0120005F, + 0, + "Z590 UD AC", +}; + +static const gb_fusion2_device z790_aor_mstr_device = +{ + &it5702_32_device, + 0x015001DF, + 0, + "Z790 AORUS MASTER", +}; + +static const gb_fusion2_device z690_aor_xtrm_device = +{ + &it5702_32_device, + 0x028001DF, + 0, + "Z690 AORUS XTREME", +}; + +static const gb_fusion2_device z690_aor_xtrm_waterforce_device = +{ + &it5702_32_device, + 0x029001DF, + 0, + "Z690 AORUS XTREME WATERFORCE", +}; + +static const gb_fusion2_device z490_aor_xtrm_device = +{ + &it5702_32_device, + 0x029001DF, + 0, + "Z490 AORUS XTREME", +}; + +static const gb_fusion2_device z490_aor_xtrm_waterforce_device = +{ + &it5702_32_device, + 0x029001DF, + 0, + "Z490 AORUS XTREME WATERFORCE", +}; + +static const gb_fusion2_device z590_aor_xtrm_waterforce_device = +{ + &it5702_32_device, + 0x029001DF, + 0, + "Z590 AORUS XTREME WATERFORCE", +}; + +static const gb_fusion2_device z790_aor_xtrm_device = +{ + &it5702_32_device, + 0x026001DF, + 0, + "Z790 AORUS XTREME", +}; + +/*-------------------------------------------------------------*\ +| Layout 33 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C" : Single | +| Zone "Chipset Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_33_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c_2_zone, + &common_chip_acc_4_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650m_gmg_x_ax_2_device = +{ + &it5702_33_device, + 0x0880015F, + 0, + "B650M GAMING X AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 34 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C" : Single | +| Zone "PCI-E Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_34_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c_2_zone, + &common_pcie_acc_4_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b760m_g_ax_device = +{ + &it5702_34_device, + 0x0870005F, + 0, + "B760M G AX", +}; + +static const gb_fusion2_device b760m_gmg_wifi_device = +{ + &it5702_34_device, + 0x0870005F, + 0, + "B760M GAMING WIFI", +}; + +static const gb_fusion2_device b760m_gmg_wifi_plus_device = +{ + &it5702_34_device, + 0x0870005F, + 0, + "B760M GAMING WIFI PLUS", +}; + +/*-------------------------------------------------------------*\ +| Layout 35 048D:5702 | +| | +| Zone "ARGB_V2_1/ARGB_V2_3" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "IO Cover (Top)" : Single | +| Zone "IO Cover (Bottom)" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_35_device = +{ + &common_argb_v2_3_1_zone, + &common_argb_v2_2_zone, + &common_io_cov_1_zone, + &common_io_cov_btm_4_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x670e_aor_pro_x_device = +{ + &it5702_35_device, + 0x192001DF, + 0, + "X670E AORUS PRO X", +}; + +static const gb_fusion2_device z790_aor_pro_x_device = +{ + &it5702_35_device, + 0x111001DF, + 0, + "Z790 AORUS PRO X", +}; + +static const gb_fusion2_device z790_aor_pro_x_wifi7_device = +{ + &it5702_35_device, + 0x111001DF, + 0, + "Z790 AORUS PRO X WIFI7", +}; + +/*-------------------------------------------------------------*\ +| Layout 36 048D:5702 | +| | +| Zone "ARGB_V2_1/ARGB_V2_3" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "LED_C1" : Single | +| Zone "LED_C2" : Single | +| Zone "LED_C3" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_36_device = +{ + &common_argb_v2_3_1_zone, + &common_argb_v2_2_zone, + &common_led_c1_2_zone, + &common_led_c3_4_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z790_aor_xtreme_x_ice_device = +{ + &it5702_36_device, + 0x1250005F, + 0, + "Z790 AORUS XTREME X ICE", +}; + +static const gb_fusion2_device z790_aor_xtreme_x_device = +{ + &it5702_36_device, + 0x124001DF, + 0, + "Z790 AORUS XTREME X", +}; + +/*-------------------------------------------------------------*\ +| Layout 37 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "LED_C1" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_37_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_1_zone, + &common_led_c1_2_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z490_vision_g_device = +{ + &it5702_37_device, + 0x01B0005F, + 0, + "Z490 VISION G", +}; + +static const gb_fusion2_device z690_aor_pro_device = +{ + &it5702_37_device, + 0x0130015F, + 0, + "Z690 AORUS PRO", +}; + +static const gb_fusion2_device z690_aor_pro_ddr4_device = +{ + &it5702_37_device, + 0x0130015F, + 0, + "Z690 AORUS PRO DDR4", +}; + +static const gb_fusion2_device z690_aor_ultra_device = +{ + &it5702_37_device, + 0x0130015F, + 0, + "Z690 AORUS ULTRA", +}; + +static const gb_fusion2_device z590_aor_ultra_device = +{ + &it5702_37_device, + 0x0130015F, + 0, + "Z590 AORUS ULTRA", +}; + +static const gb_fusion2_device z590_vision_d_device = +{ + &it5702_37_device, + 0x0130015F, + 0, + "Z590 VISION D", +}; + +static const gb_fusion2_device z590_vision_g_device = +{ + &it5702_37_device, + 0x0130015F, + 0, + "Z590 VISION G", +}; + +/*-------------------------------------------------------------*\ +| Layout 38 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C1" : Single | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_38_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c1_2_zone, + &common_pcie_acc_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z490m_gmg_x_device = +{ + &it5702_38_device, + 0x0040005F, + 0, + "Z490M GAMING X", +}; + +static const gb_fusion2_device b460m_aor_elite_device = +{ + &it5702_38_device, + 0x1060005F, + 0, + "B460M AORUS ELITE", +}; + +static const gb_fusion2_device b460m_aor_pro_device = +{ + &it5702_38_device, + 0x1060005F, + 0, + "B460M AORUS PRO", +}; + +static const gb_fusion2_device b560m_aor_elite_device = +{ + &it5702_38_device, + 0x1040005F, + 0, + "B560M AORUS ELITE", +}; + +static const gb_fusion2_device b560m_aor_pro_device = +{ + &it5702_38_device, + 0x1040005F, + 0, + "B560M AORUS PRO", +}; + +static const gb_fusion2_device b560m_aor_pro_ax_device = +{ + &it5702_38_device, + 0x1040005F, + 0, + "B560M AORUS PRO AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 39 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C1" : Single | +| Zone "LED_CPU" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_39_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c1_2_zone, + &common_led_cpu_3_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b550m_ds3h_r2_device = +{ + &it5702_39_device, + 0x0030005F, + 0, + "B550M DS3H R2", +}; + +static const gb_fusion2_device b650_aero_g_device = +{ + &it5702_39_device, + 0x0910005F, + 0, + "B650 AERO G", +}; + +static const gb_fusion2_device b650_gmg_x_device = +{ + &it5702_39_device, + 0x0910005F, + 0, + "B650 GAMING X", +}; + +static const gb_fusion2_device b650_gmg_x_ax_device = +{ + &it5702_39_device, + 0x0910005F, + 0, + "B650 GAMING X AX", +}; + +static const gb_fusion2_device b650_gmg_x_ax_v2_device = +{ + &it5702_39_device, + 0x0910005F, + 0, + "B650 GAMING X AX V2", +}; + +static const gb_fusion2_device x670_aor_elite_ax_device = +{ + &it5702_39_device, + 0x0110005F, + 0, + "X670 AORUS ELITE AX", +}; + +static const gb_fusion2_device x670_gmg_x_ax_device = +{ + &it5702_39_device, + 0x0110005F, + 0, + "X670 GAMING X AX", +}; + +static const gb_fusion2_device b550_eagle_device = +{ + &it5702_39_device, + 0x0140005F, + 0, + "B550 EAGLE", +}; + +static const gb_fusion2_device b550_eagle_wifi6_device = +{ + &it5702_39_device, + 0x0140005F, + 0, + "B550 EAGLE WIFI6", +}; + +static const gb_fusion2_device b550_gmg_x_device = +{ + &it5702_39_device, + 0x0140005F, + 0, + "B550 GAMING X", +}; + +static const gb_fusion2_device b550_gmg_x_v2_device = +{ + &it5702_39_device, + 0x0140005F, + 0, + "B550 GAMING X V2", +}; + +static const gb_fusion2_device b550_vision_d_device = +{ + &it5702_39_device, + 0x0140005F, + 0, + "B550 VISION D", +}; + +static const gb_fusion2_device b550_vision_dp_device = +{ + &it5702_39_device, + 0x0140005F, + 0, + "B550 VISION D-P", +}; + +static const gb_fusion2_device b550m_aor_elite_device = +{ + &it5702_39_device, + 0x0030005F, + 0, + "B550M AORUS ELITE", +}; + +static const gb_fusion2_device b550m_aor_elite_ax_device = +{ + &it5702_39_device, + 0x0030005F, + 0, + "B550M AORUS ELITE AX", +}; + +static const gb_fusion2_device b550m_ds3h_device = +{ + &it5702_39_device, + 0x0030005F, + 0, + "B550M DS3H", +}; + +static const gb_fusion2_device b550m_ds3h_ac_device = +{ + &it5702_39_device, + 0x0030005F, + 0, + "B550M DS3H AC", +}; + +static const gb_fusion2_device b550m_ds3h_ac_r2_device = +{ + &it5702_39_device, + 0x0030005F, + 0, + "B550M DS3H AC R2", +}; + +static const gb_fusion2_device b550m_gmg_x_wifi6_device = +{ + &it5702_39_device, + 0x0030005F, + 0, + "B550M GAMING X WIFI6", +}; + +static const gb_fusion2_device x570s_aero_g_device = +{ + &it5702_39_device, + 0x0110015F, + 0, + "X570S AERO G", +}; + +static const gb_fusion2_device x570s_ud_device = +{ + &it5702_39_device, + 0x0110015F, + 0, + "X570S UD", +}; + +static const gb_fusion2_device x670e_aor_xtrm_device = +{ + &it5702_39_device, + 0x023001DF, + 0, + "X670E AORUS XTREME", +}; + +/*-------------------------------------------------------------*\ +| Layout 40 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C1" : Single | +| Zone "Chipset Accent" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_40_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c1_2_zone, + &common_chip_acc_3_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b660m_aor_pro_device = +{ + &it5702_40_device, + 0x0870015F, + 0, + "B660M AORUS PRO", +}; + +static const gb_fusion2_device b660m_aor_pro_ax_device = +{ + &it5702_40_device, + 0x0870015F, + 0, + "B660M AORUS PRO AX", +}; + +static const gb_fusion2_device b660m_aor_pro_ax_ddr4_device = +{ + &it5702_40_device, + 0x0870015F, + 0, + "B660M AORUS PRO AX DDR4", +}; + +static const gb_fusion2_device b660m_aor_pro_ddr4_device = +{ + &it5702_40_device, + 0x0870015F, + 0, + "B660M AORUS PRO DDR4", +}; + +static const gb_fusion2_device b760m_aor_elite_device = +{ + &it5702_40_device, + 0x0860015F, + 0, + "B760M AORUS ELITE", +}; + +static const gb_fusion2_device b760m_aor_elite_ax_device = +{ + &it5702_40_device, + 0x0860015F, + 0, + "B760M AORUS ELITE AX", +}; + +static const gb_fusion2_device b760m_aor_elite_ax_ddr4_device = +{ + &it5702_40_device, + 0x0860015F, + 0, + "B760M AORUS ELITE AX DDR4", +}; + +static const gb_fusion2_device b760m_aor_elite_ddr4_device = +{ + &it5702_40_device, + 0x0860015F, + 0, + "B760M AORUS ELITE DDR4", +}; + +static const gb_fusion2_device z690_aor_tachyon_device = +{ + &it5702_40_device, + 0x0120015F, + 0, + "Z690 AORUS TACHYON", +}; + +static const gb_fusion2_device z790_aor_elite_device = +{ + &it5702_40_device, + 0x0130015F, + 0, + "Z790 AORUS ELITE", +}; + +static const gb_fusion2_device z790_aor_elite_ax_device = +{ + &it5702_40_device, + 0x0130015F, + 0, + "Z790 AORUS ELITE AX", +}; + +static const gb_fusion2_device z790_aor_elite_ax_ddr4_device = +{ + &it5702_40_device, + 0x0130015F, + 0, + "Z790 AORUS ELITE AX DDR4", +}; + +static const gb_fusion2_device z790_aor_elite_ax_ice_device = +{ + &it5702_40_device, + 0x0130015F, + 0, + "Z790 AORUS ELITE AX ICE", +}; + +static const gb_fusion2_device z790_aor_elite_ax_w_device = +{ + &it5702_40_device, + 0x0130015F, + 0, + "Z790 AORUS ELITE AX-W", +}; + +static const gb_fusion2_device z790_aor_elite_ddr4_device = +{ + &it5702_40_device, + 0x0130015F, + 0, + "Z790 AORUS ELITE DDR4", +}; + +static const gb_fusion2_device z790_aor_tachyon_device = +{ + &it5702_40_device, + 0x0130015F, + 0, + "Z790 AORUS TACHYON", +}; + +static const gb_fusion2_device z790m_aor_elite_device = +{ + &it5702_40_device, + 0x0030015F, + 0, + "Z790M AORUS ELITE", +}; + +static const gb_fusion2_device z790m_aor_elite_ax_device = +{ + &it5702_40_device, + 0x0030015F, + 0, + "Z790M AORUS ELITE AX", +}; + +static const gb_fusion2_device z790m_aor_elite_ax_ice_device = +{ + &it5702_40_device, + 0x0030015F, + 0, + "Z790M AORUS ELITE AX ICE", +}; + +static const gb_fusion2_device a520_aor_elite_device = +{ + &it5702_40_device, + 0x0150005F, + 0, + "A520 AORUS ELITE", +}; + +static const gb_fusion2_device z590_aor_tachyon_device = +{ + &it5702_40_device, + 0x0180005F, + 0, + "Z590 AORUS TACHYON", +}; + +/*-------------------------------------------------------------*\ +| Layout 41 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C1" : Single | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_41_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c1_2_zone, + &common_pcie_acc_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z590m_device = +{ + &it5702_41_device, + 0x0020005F, + 0, + "Z590M", +}; + +static const gb_fusion2_device z590m_gmg_x_device = +{ + &it5702_41_device, + 0x0020005F, + 0, + "Z590M GAMING X", +}; + +/*-------------------------------------------------------------*\ +| Layout 42 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover (Top)" : Single | +| Zone "Chipset Accent" : Single | +| Zone "IO Cover (Bottom)" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_42_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_top_1_zone, + &common_chip_acc_3_zone, + &common_io_cov_btm_4_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650e_aor_pro_x_usb4_device = +{ + &it5702_42_device, + 0x214001DF, + 0, + "B650E AORUS PRO X USB4", +}; + +/*-------------------------------------------------------------*\ +| Layout 43 048D:5702 | +| | +| Zone "D_LED" : Linear | +| Zone "Board Accent (Right Side, Top)" : Single | +| Zone "Board Accent (Right Side, Top Middle)" : Single | +| Zone "Board Accent (Right Side, Bottom Middle)" : Single | +| Zone "Board Accent (Right Side, Bottom)" : Single | +| Zone "Chipset Accent" : Single | +| Zone "IO Cover (Bottom)" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_43_device = +{ + &common_d_led_zone, + &common_brst_1_zone, + &common_brstm_2_zone, + &common_brsbm_3_zone, + &common_brsb_4_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b660i_aor_pro_ddr4_device = +{ + &it5702_43_device, + 0x0B5001DF, + 0, + "B660I AORUS PRO DDR4", +}; + +static const gb_fusion2_device b760i_aor_pro_device = +{ + &it5702_43_device, + 0x0B3001DF, + 0, + "B760I AORUS PRO", +}; + +static const gb_fusion2_device b760i_aor_pro_ddr4_device = +{ + &it5702_43_device, + 0x0B3001DF, + 0, + "B760I AORUS PRO DDR4", +}; + +static const gb_fusion2_device b690i_aor_ultra_device = +{ + &it5702_43_device, + 0x034001DF, + 0, + "Z690I AORUS ULTRA", +}; + +static const gb_fusion2_device b690i_aor_ultra_ddr4_device = +{ + &it5702_43_device, + 0x034001DF, + 0, + "Z690I AORUS ULTRA DDR4", +}; + +static const gb_fusion2_device b690i_aor_ultra_lite_device = +{ + &it5702_43_device, + 0x034001DF, + 0, + "Z690I AORUS ULTRA LITE", +}; + +static const gb_fusion2_device b690i_aor_ultra_lite_ddr4_device = +{ + &it5702_43_device, + 0x034001DF, + 0, + "Z690I AORUS ULTRA LITE DDR4", +}; + +static const gb_fusion2_device b690i_aor_ultra_plus_device = +{ + &it5702_43_device, + 0x034001DF, + 0, + "Z690I AORUS ULTRA PLUS", +}; + +static const gb_fusion2_device b690i_aor_ultra_plus_ddr4_device = +{ + &it5702_43_device, + 0x034001DF, + 0, + "Z690I AORUS ULTRA PLUS DDR4", +}; + +static const gb_fusion2_device b790i_aor_ultra_device = +{ + &it5702_43_device, + 0x034001DF, + 0, + "Z790I AORUS ULTRA", +}; + +static const gb_fusion2_device b550i_aor_pro_ax_device = +{ + &it5702_43_device, + 0x0360007F, + 0, + "B550I AORUS PRO AX", +}; + +static const gb_fusion2_device b560i_aor_pro_ax_device = +{ + &it5702_43_device, + 0x1310007F, + 0, + "B560I AORUS PRO AX", +}; + +static const gb_fusion2_device x570si_aor_pro_ax_device = +{ + &it5702_43_device, + 0x034000FF, + 0, + "X570SI AORUS PRO AX", +}; + +static const gb_fusion2_device z590i_aor_ultra_device = +{ + &it5702_43_device, + 0x0350007F, + 0, + "Z590I AORUS ULTRA", +}; + +/*-------------------------------------------------------------*\ +| Layout 44 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "LED_C1" : Single | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_44_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_1_zone, + &common_led_c1_2_zone, + &common_pcie_acc_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z490_aor_elite_device = +{ + &it5702_44_device, + 0x0160005F, + 0, + "Z490 AORUS ELITE", +}; + +static const gb_fusion2_device z490_aor_elite_ac_device = +{ + &it5702_44_device, + 0x0160005F, + 0, + "Z490 AORUS ELITE AC", +}; + +static const gb_fusion2_device z490_aor_pro_ax_device = +{ + &it5702_44_device, + 0x0160005F, + 0, + "Z490 AORUS PRO AX", +}; + +static const gb_fusion2_device h470_aor_pro_ax_device = +{ + &it5702_44_device, + 0x0960005F, + 0, + "H470 AORUS PRO AX", +}; + +static const gb_fusion2_device b460_aor_pro_ac_device = +{ + &it5702_44_device, + 0x1170005F, + 0, + "B460 AORUS PRO AC", +}; + +static const gb_fusion2_device z590_aor_pro_ax_device = +{ + &it5702_44_device, + 0x0160005F, + 0, + "Z590 AORUS PRO AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 45 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "LED_C1" : Single | +| Zone "Chipset Accent" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_45_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_1_zone, + &common_led_c1_2_zone, + &common_chip_acc_3_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z490_aor_mstr_device = +{ + &it5702_45_device, + 0x0170005F, + 0, + "Z490 AORUS MASTER", +}; + +static const gb_fusion2_device z490_aor_mstr_waterforce_device = +{ + &it5702_45_device, + 0x0170005F, + 0, + "Z490 AORUS MASTER WATERFORCE", +}; + +static const gb_fusion2_device z490_aor_ultra_device = +{ + &it5702_45_device, + 0x0170005F, + 0, + "Z490 AORUS ULTRA", +}; + +static const gb_fusion2_device z490_aor_ultra_g2_device = +{ + &it5702_45_device, + 0x0170005F, + 0, + "Z490 AORUS ULTRA G2", +}; + +static const gb_fusion2_device b760m_aor_pro_device = +{ + &it5702_45_device, + 0x0840015F, + 0, + "B760M AORUS PRO", +}; + +static const gb_fusion2_device b760m_aor_pro_ax_device = +{ + &it5702_45_device, + 0x0840015F, + 0, + "B760M AORUS PRO AX", +}; + +static const gb_fusion2_device b760m_aor_pro_ax_ddr4_device = +{ + &it5702_45_device, + 0x0840015F, + 0, + "B760M AORUS PRO AX DDR4", +}; + +static const gb_fusion2_device b760m_aor_pro_ddr4_device = +{ + &it5702_45_device, + 0x0840015F, + 0, + "B760M AORUS PRO DDR4", +}; + +static const gb_fusion2_device z590_aor_mstr_device = +{ + &it5702_45_device, + 0x0170005F, + 0, + "Z590 AORUS MASTER", +}; + +static const gb_fusion2_device z590_aor_xtrm_device = +{ + &it5702_45_device, + 0x02B001DF, + 0, + "Z590 AORUS XTREME", +}; + +/*-------------------------------------------------------------*\ +| Layout 46 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C1" : Single | +| Zone "LED_CPU" : Single | +| Zone "Chipset Accent" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_46_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c1_2_zone, + &common_led_cpu_3_zone, + &common_chip_acc_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650_aor_elite_device = +{ + &it5702_46_device, + 0x0960015F, + 0, + "B650 AORUS ELITE", +}; + +static const gb_fusion2_device b650_aor_elite_ax_device = +{ + &it5702_46_device, + 0x0960015F, + 0, + "B650 AORUS ELITE AX", +}; + +static const gb_fusion2_device b650m_aor_elite_device = +{ + &it5702_46_device, + 0x0860015F, + 0, + "B650M AORUS ELITE", +}; + +static const gb_fusion2_device b650m_aor_elite_ax_device = +{ + &it5702_46_device, + 0x0860015F, + 0, + "B650M AORUS ELITE AX", +}; + +static const gb_fusion2_device b650m_aor_elite_ax_ice_device = +{ + &it5702_46_device, + 0x0860015F, + 0, + "B650M AORUS ELITE AX ICE", +}; + +static const gb_fusion2_device b650m_aor_pro_ax_device = +{ + &it5702_46_device, + 0x0860015F, + 0, + "B650M AORUS PRO AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 47 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C1" : Single | +| Zone "Board Accent (Right Side, Top)" : Single | +| Zone "Board Accent (Right Side, Top Middle)" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_47_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c1_2_zone, + &common_brst_3_zone, + &common_brstm_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b660_aor_elite_ax_ddr4_device = +{ + &it5702_47_device, + 0x096001DF, + 0, + "B660 AORUS ELITE AX DDR4", +}; + +static const gb_fusion2_device b660_aor_elite_ddr4_device = +{ + &it5702_47_device, + 0x096001DF, + 0, + "B660 AORUS ELITE DDR4", +}; + +static const gb_fusion2_device z690_gmg_x_device = +{ + &it5702_47_device, + 0x01A001DF, + 0, + "Z690 GAMING X", +}; + +static const gb_fusion2_device z690_gmg_x_ddr4_device = +{ + &it5702_47_device, + 0x01A001DF, + 0, + "Z690 GAMING X DDR4", +}; + +static const gb_fusion2_device z690_gmg_x_ddr4_v2_device = +{ + &it5702_47_device, + 0x01A001DF, + 0, + "Z690 GAMING X DDR4 V2", +}; + +static const gb_fusion2_device z590_gmg_x_device = +{ + &it5702_47_device, + 0x0140005F, + 0, + "Z590 GAMING X", +}; + +/*-------------------------------------------------------------*\ +| Layout 48 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "LED_C1" : Single | +| Zone "LED_CPU" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_48_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_1_zone, + &common_led_c1_2_zone, + &common_led_cpu_3_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b550_aor_mstr_device = +{ + &it5702_48_device, + 0x0180005F, + 0, + "B550 AORUS MASTER", +}; + +static const gb_fusion2_device x570s_aor_mstr_device = +{ + &it5702_48_device, + 0x0130015F, + 0, + "X570S AORUS MASTER", +}; + +/*-------------------------------------------------------------*\ +| Layout 49 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "LED_C1" : Single | +| Zone "LED_CPU" : Single | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_49_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_led_c1_2_zone, + &common_led_cpu_3_zone, + &common_pcie_acc_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b550m_aor_pro_device = +{ + &it5702_49_device, + 0x0050005F, + 0, + "B550M AORUS PRO", +}; + +static const gb_fusion2_device b550m_aor_pro_ax_device = +{ + &it5702_49_device, + 0x0050005F, + 0, + "B550M AORUS PRO AX", +}; + +static const gb_fusion2_device b550m_aor_pro_p_device = +{ + &it5702_49_device, + 0x0050005F, + 0, + "B550M AORUS PRO-P", +}; + +static const gb_fusion2_device b560_aor_pro_ax_device = +{ + &it5702_49_device, + 0x1150005F, + 0, + "B560 AORUS PRO AX", +}; + +static const gb_fusion2_device z590_aor_elite_device = +{ + &it5702_49_device, + 0x01A0005F, + 0, + "Z590 AORUS ELITE", +}; + +static const gb_fusion2_device z590_aor_elite_ax_device = +{ + &it5702_49_device, + 0x01A0005F, + 0, + "Z590 AORUS ELITE AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 50 048D:5702 | +| | +| Zone "ARGB_V2_1/ARGB_V2_3" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "IO Cover (Bottom)" : Single | +| Zone "IO Cover (Bottom Middle)" : Single | +| Zone "IO Cover (Top Middle)" : Single | +| Zone "IO Cover (Top)" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_50_device = +{ + &common_argb_v2_1_3_zone, + &common_argb_v2_2_zone, + &common_io_cov_btm_1_zone, + &common_io_cov_btm_mid_2_zone, + &common_chip_io_cov_tm_3_zone, + &common_io_cov_top_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b760m_aor_elite_x_ax_device = +{ + &it5702_50_device, + 0x181001DF, + 0, + "B760M AORUS ELITE X AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 51 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "LED_C1" : Single | +| Zone "Chipset Accent" : Single | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_51_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_btm_1_zone, + &common_io_cov_btm_mid_2_zone, + &common_chip_io_cov_tm_3_zone, + &common_io_cov_top_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z490_vision_d_device = +{ + &it5702_51_device, + 0x0180005F, + 0, + "Z490 VISION D", +}; + +/*-------------------------------------------------------------*\ +| Layout 52 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover (Top)" : Single | +| Zone "LED_C1" : Single | +| Zone "LED_CPU" : Single | +| Zone "IO Cover (Bottom)" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_52_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_top_1_zone, + &common_led_c1_2_zone, + &common_led_cpu_3_zone, + &common_io_cov_btm_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650_aor_pro_ax_device = +{ + &it5702_52_device, + 0x09A001DF, + 0, + "B650 AORUS PRO AX", +}; + +static const gb_fusion2_device b650e_aor_mstr_device = +{ + &it5702_52_device, + 0x09A001DF, + 0, + "B650E AORUS MASTER", +}; + +static const gb_fusion2_device x670e_aor_mstr_device = +{ + &it5702_52_device, + 0x024001DF, + 0, + "X670E AORUS MASTER", +}; + +/*-------------------------------------------------------------*\ +| Layout 53 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "LED_C1" : Single | +| Zone "Board Accent (Right Side, Top)" : Single | +| Zone "Board Accent (Right Side, Top Middle)" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_53_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_1_zone, + &common_led_c1_2_zone, + &common_brst_3_zone, + &common_brstm_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b660_aor_mstr_device = +{ + &it5702_53_device, + 0x094001DF, + 0, + "B660 AORUS MASTER", +}; + +static const gb_fusion2_device b660_aor_mstr_ddr4_device = +{ + &it5702_53_device, + 0x094001DF, + 0, + "B660 AORUS MASTER DDR4", +}; + +static const gb_fusion2_device b760_aor_mstr_ddr4_device = +{ + &it5702_53_device, + 0x094001DF, + 0, + "B760 AORUS MASTER DDR4", +}; + +static const gb_fusion2_device z690_aor_elite_device = +{ + &it5702_53_device, + 0x016001DF, + 0, + "Z690 AORUS ELITE", +}; + +static const gb_fusion2_device z690_aor_elite_ax_2_device = +{ + &it5702_53_device, + 0x016001DF, + 0, + "Z690 AORUS ELITE AX", +}; + +static const gb_fusion2_device z690_aor_elite_ax_ddr4_device = +{ + &it5702_53_device, + 0x016001DF, + 0, + "Z690 AORUS ELITE AX DDR4", +}; + +static const gb_fusion2_device z690_aor_elite_ax_ddr4_v2_device = +{ + &it5702_53_device, + 0x016001DF, + 0, + "Z690 AORUS ELITE AX DDR4 V2", +}; + +static const gb_fusion2_device z690_aor_elite_ddr4_device = +{ + &it5702_53_device, + 0x016001DF, + 0, + "Z690 AORUS ELITE DDR4", +}; + +/*-------------------------------------------------------------*\ +| Layout 54 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover" : Single | +| Zone "LED_C1" : Single | +| Zone "LED_CPU" : Single | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_54_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_1_zone, + &common_led_c1_2_zone, + &common_led_cpu_3_zone, + &common_pcie_acc_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b550_aor_elite_device = +{ + &it5702_54_device, + 0x0170005F, + 0, + "B550 AORUS ELITE", +}; + +static const gb_fusion2_device b550_aor_elite_ax_device = +{ + &it5702_54_device, + 0x0170005F, + 0, + "B550 AORUS ELITE AX", +}; + +static const gb_fusion2_device b550_aor_elite_ax_v2_device = +{ + &it5702_54_device, + 0x0170005F, + 0, + "B550 AORUS ELITE AX V2", +}; + +static const gb_fusion2_device b550_aor_elite_ax_v3_device = +{ + &it5702_54_device, + 0x0170005F, + 0, + "B550 AORUS ELITE AX V3", +}; + +static const gb_fusion2_device b550_aor_elite_v2_device = +{ + &it5702_54_device, + 0x0170005F, + 0, + "B550 AORUS ELITE V2", +}; + +static const gb_fusion2_device b550_aor_pro_device = +{ + &it5702_54_device, + 0x0170005F, + 0, + "B550 AORUS PRO", +}; + +static const gb_fusion2_device b550_aor_pro_ac_device = +{ + &it5702_54_device, + 0x0170005F, + 0, + "B550 AORUS PRO AC", +}; + +static const gb_fusion2_device b550_aor_pro_ax_device = +{ + &it5702_54_device, + 0x0170005F, + 0, + "B550 AORUS PRO AX", +}; + +static const gb_fusion2_device b550_aor_pro_v2_device = +{ + &it5702_54_device, + 0x0170005F, + 0, + "B550 AORUS PRO V2", +}; + +static const gb_fusion2_device x570s_aor_pro_ax_device = +{ + &it5702_54_device, + 0x0150015F, + 0, + "X570S AORUS PRO AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 55 048D:5702 | +| | +| Zone "IO Cover" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_55_device = +{ + &common_io_cov_1_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z790_aor_mstr_x_2_device = +{ + &it5702_55_device, + 0x123001DF, + 1, + "Z790 AORUS MASTER X", +}; + +static const gb_fusion2_device z790_aor_mstr_2_device = +{ + &it5702_55_device, + 0x015001DF, + 1, + "Z790 AORUS MASTER", +}; + +static const gb_fusion2_device x670_aor_xtrm_2_device = +{ + &it5702_55_device, + 0x023001DF, + 1, + "X670E AORUS XTREME", +}; + +/*-------------------------------------------------------------*\ +| Layout 56 048D:5702 | +| | +| Zone "Chipset Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_56_device = +{ + &common_chip_acc_3_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z790_aor_xtrm_2_device = +{ + &it5702_56_device, + 0x026001DF, + 1, + "Z790 AORUS XTREME", +}; + +static const gb_fusion2_device z790_aor_xtrm_x_2_device = +{ + &it5702_56_device, + 0x124001DF, + 1, + "Z790 AORUS XTREME X", +}; + +/*-------------------------------------------------------------*\ +| Layout 57 048D:5702 | +| | +| Zone "IO Cover" : Single | +| Zone "LED_CPU" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_57_device = +{ + &common_chip_acc_3_zone, + &common_led_cpu_8_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z690_aor_xtrm_waterforce_2_device = +{ + &it5702_57_device, + 0x029001DF, + 1, + "Z690 AORUS XTREME WATERFORCE", +}; + +/*-------------------------------------------------------------*\ +| Layout 58 048D:5702 | +| | +| Zone "IO Cover" : Single | +| Zone "Chipset Accent" : Single | +| Zone "LED_CPU" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_58_device = +{ + &common_chip_acc_3_zone, + &common_chip_acc_3_zone, + &common_led_cpu_8_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z490_aor_xtrm_2_device = +{ + &it5702_58_device, + 0x029001DF, + 1, + "Z490 AORUS XTREME", +}; + +static const gb_fusion2_device z490_aor_xtrm_waterforce_2_device = +{ + &it5702_58_device, + 0x029001DF, + 1, + "Z490 AORUS XTREME WATERFORCE", +}; + +static const gb_fusion2_device z590_aor_xtrm_waterforce_2_device = +{ + &it5702_58_device, + 0x029001DF, + 1, + "Z590 AORUS XTREME WATERFORCE", +}; + +/*-------------------------------------------------------------*\ +| Layout 59 048D:5702 | +| | +| Zone "IO Cover" : Single | +| Zone "Chipset Accent" : Single | +| Zone "RAM Cover" : Single | +| Zone "LED_CPU" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_59_device = +{ + &common_chip_acc_3_zone, + &common_chip_acc_3_zone, + &common_ram_cov_4_zone, + &common_led_cpu_8_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z690_aor_xtrm_2_device = +{ + &it5702_59_device, + 0x028001DF, + 1, + "Z690 AORUS XTREME", +}; + +/*-------------------------------------------------------------*\ +| Layout 60 048D:5702 | +| | +| Zone "D_LED1" : Linear | +| Zone "D_LED2" : Linear | +| Zone "IO Cover (Top)" : Single | +| Zone "LED_C1" : Single | +| Zone "Chipset Accent" : Single | +| Zone "IO Cover (Bottom)" : Single | +| Zone "LED_C2" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5702_60_device = +{ + &common_d_led1_zone, + &common_d_led2_zone, + &common_io_cov_top_1_zone, + &common_led_c1_2_zone, + &common_chip_acc_3_zone, + &common_io_cov_btm_4_zone, + &common_led_c2_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z690_aor_mstr_device = +{ + &it5702_60_device, + 0x017001DF, + 0, + "Z690 AORUS MASTER", +}; + +/*-------------------------------------------------------------*\ +| Layout 60 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_60_device = +{ + &common_argb_v2_1_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device a620i_ax_5711_device = +{ + &it5711_60_device, + 0x4310005F, + 0, + "A620I AX", +}; + +/*-------------------------------------------------------------*\ +| Layout 61 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_61_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b860i_aor_pro_ice_5711_device = +{ + &it5711_61_device, + 0x0B10005F, + 0, + "B860I AORUS PRO ICE", +}; + +/*-------------------------------------------------------------*\ +| Layout 62 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_62_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device a620m_ds3h_5711_device = +{ + &it5711_62_device, + 0x4020005F, + 0, + "A620M DS3H", +}; + +static const gb_fusion2_device a620m_gmg_x_5711_device = +{ + &it5711_62_device, + 0x4020005F, + 0, + "A620M GAMING X", +}; + +static const gb_fusion2_device a620m_h_5711_device = +{ + &it5711_62_device, + 0x4030005F, + 0, + "A620M H", +}; + +static const gb_fusion2_device a620m_s2h_5711_device = +{ + &it5711_62_device, + 0x4030005F, + 0, + "A620M S2H", +}; + +static const gb_fusion2_device b840_eagle_wifi6e_5711_device = +{ + &it5711_62_device, + 0x112001DF, + 0, + "B840 EAGLE WIFI6E", +}; + +static const gb_fusion2_device b840m_d2h_5711_device = +{ + &it5711_62_device, + 0x1010005F, + 0, + "B840M D2H", +}; + +static const gb_fusion2_device b840m_ds3h_5711_device = +{ + &it5711_62_device, + 0x1010005F, + 0, + "B840M DS3H", +}; + +static const gb_fusion2_device b840m_ds3h_wifi6_5711_device = +{ + &it5711_62_device, + 0x1010005F, + 0, + "B840M DS3H WIFI6", +}; + +static const gb_fusion2_device b840m_eagle_wifi6_5711_device = +{ + &it5711_62_device, + 0x1010005F, + 0, + "B840M EAGLE WIFI6", +}; + +static const gb_fusion2_device b850i_aor_pro_5711_device = +{ + &it5711_62_device, + 0x0B20005F, + 0, + "B850I AORUS PRO", +}; + +static const gb_fusion2_device b850m_d3hp_5711_device = +{ + &it5711_62_device, + 0x0820005F, + 0, + "B850M D3HP", +}; + +static const gb_fusion2_device x870i_aor_pro_ice_5711_device = +{ + &it5711_62_device, + 0x0310005F, + 0, + "X870I AORUS PRO ICE", +}; + +/*-------------------------------------------------------------*\ +| Layout 63 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "IO Cover" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_63_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_io_cov_10_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z890i_aor_ultra_5711_device = +{ + &it5711_63_device, + 0x032001DF, + 0, + "Z890I AORUS ULTRA", +}; + +/*-------------------------------------------------------------*\ +| Layout 64 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_64_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650e_eagle_wifi6e_5711_device = +{ + &it5711_64_device, + 0x3130005F, + 0, + "B650E EAGLE WIFI6E", +}; + +static const gb_fusion2_device b650em_c_5711_device = +{ + &it5711_64_device, + 0x3030005F, + 0, + "B650EM C", +}; + +static const gb_fusion2_device b650em_ds3h_wifi6e_5711_device = +{ + &it5711_64_device, + 0x3030005F, + 0, + "B650EM DS3H WIFI6E", +}; + +static const gb_fusion2_device b650em_force_wifi6e_5711_device = +{ + &it5711_64_device, + 0x3030005F, + 0, + "B650EM FORCE WIFI6E", +}; + +static const gb_fusion2_device b760_ds3h_gen5_5711_device = +{ + &it5711_64_device, + 0x2120005F, + 0, + "B760 DS3H GEN5", +}; + +static const gb_fusion2_device b760_ds3h_wifi6e_gen5_5711_device = +{ + &it5711_64_device, + 0x2120005F, + 0, + "B760 DS3H WIFI6E GEN5", +}; + +static const gb_fusion2_device b760_gmg_x_ddr4_gen5_5711_device = +{ + &it5711_64_device, + 0x2120005F, + 0, + "B760 GAMING X DDR4 GEN5", +}; + +static const gb_fusion2_device b760_gmg_x_gen5_5711_device = +{ + &it5711_64_device, + 0x2120005F, + 0, + "B760 GAMING X GEN5", +}; + +static const gb_fusion2_device b760_gmg_x_wifi6e_gen5_5711_device = +{ + &it5711_64_device, + 0x2120005F, + 0, + "B760 GAMING X WIFI6E GEN5", +}; + +static const gb_fusion2_device b760m_c_v3_5711_device = +{ + &it5711_64_device, + 0x2020005F, + 0, + "B760M C V3", +}; + +static const gb_fusion2_device b760m_gmg_x_ddr4_gen5_5711_device = +{ + &it5711_64_device, + 0x2020005F, + 0, + "B760M GAMING X DDR4 GEN5", +}; + +static const gb_fusion2_device b760m_gmg_x_gen5_5711_device = +{ + &it5711_64_device, + 0x2020005F, + 0, + "B760M GAMING X GEN5", +}; + +static const gb_fusion2_device b760m_gmg_x_wifi6e_ddr4_gen5_5711_device = +{ + &it5711_64_device, + 0x2020005F, + 0, + "B760M GAMING X WIFI6E DDR4 GEN5", +}; + +static const gb_fusion2_device b760m_gmg_x_wifi6e_gen5_5711_device = +{ + &it5711_64_device, + 0x2020005F, + 0, + "B760M GAMING X WIFI6E GEN5", +}; + +static const gb_fusion2_device b840_gmg_x_wifi6e_5711_device = +{ + &it5711_64_device, + 0x112001DF, + 0, + "B840 GAMING X WIFI6E", +}; + +static const gb_fusion2_device b840m_h_5711_device = +{ + &it5711_64_device, + 0x1030005F, + 0, + "B840M H", +}; + +static const gb_fusion2_device b850_ai_top_5711_device = +{ + &it5711_64_device, + 0x0930005F, + 0, + "B850 AI TOP", +}; + +static const gb_fusion2_device b850_aor_stealth_5711_device = +{ + &it5711_64_device, + 0x0930005F, + 0, + "B850 AORUS STEALTH", +}; + +static const gb_fusion2_device b850_aor_stealth_ice_5711_device = +{ + &it5711_64_device, + 0x0930005F, + 0, + "B850 AORUS STEALTH ICE", +}; + +static const gb_fusion2_device b850_eagle_ice_5711_device = +{ + &it5711_64_device, + 0x0930005F, + 0, + "B850 EAGLE ICE", +}; + +static const gb_fusion2_device b850_eagle_wifi6e_5711_device = +{ + &it5711_64_device, + 0x0930005F, + 0, + "B850 EAGLE WIFI6E", +}; + +static const gb_fusion2_device b850_eagle_wifi7_ice_5711_device = +{ + &it5711_64_device, + 0x0930005F, + 0, + "B850 EAGLE WIFI7 ICE", +}; + +static const gb_fusion2_device b850_gmg_wifi6_5711_device = +{ + &it5711_64_device, + 0x0930005F, + 0, + "B850 GAMING WIFI6", +}; + +static const gb_fusion2_device b850m_c_5711_device = +{ + &it5711_64_device, + 0x0830005F, + 0, + "B850M C", +}; + +static const gb_fusion2_device b850m_ds3h_5711_device = +{ + &it5711_64_device, + 0x0830005F, + 0, + "B850M DS3H", +}; + +static const gb_fusion2_device b850m_ds3h_ice_5711_device = +{ + &it5711_64_device, + 0x0830005F, + 0, + "B850M DS3H ICE", +}; + +static const gb_fusion2_device b850m_eagle_wifi6e_5711_device = +{ + &it5711_64_device, + 0x0830005F, + 0, + "B850M EAGLE WIFI6E", +}; + +static const gb_fusion2_device b850m_eagle_wifi6e_ice_5711_device = +{ + &it5711_64_device, + 0x0830005F, + 0, + "B850M EAGLE WIFI6E ICE", +}; + +static const gb_fusion2_device b850m_force_5711_device = +{ + &it5711_64_device, + 0x0830005F, + 0, + "B850M FORCE", +}; + +static const gb_fusion2_device b850m_force_v2_5711_device = +{ + &it5711_64_device, + 0x0830005F, + 0, + "B850M FORCE V2", +}; + +static const gb_fusion2_device b850m_force_wifi6e_5711_device = +{ + &it5711_64_device, + 0x0830005F, + 0, + "B850M FORCE WIFI6E", +}; + +static const gb_fusion2_device b850m_force_wifi6e_v2_5711_device = +{ + &it5711_64_device, + 0x0830005F, + 0, + "B850M FORCE WIFI6E V2", +}; + +static const gb_fusion2_device b860_ds3h_5711_device = +{ + &it5711_64_device, + 0x0940005F, + 0, + "B860 DS3H", +}; + +static const gb_fusion2_device b860_ds3h_wifi6e_5711_device = +{ + &it5711_64_device, + 0x0940005F, + 0, + "B860 DS3H WIFI6E", +}; + +static const gb_fusion2_device b860m_gmg_x_5711_device = +{ + &it5711_64_device, + 0x0840005F, + 0, + "B860M GAMING X", +}; + +static const gb_fusion2_device b860m_gmg_x_wifi6e_5711_device = +{ + &it5711_64_device, + 0x0840005F, + 0, + "B860M GAMING X WIFI6E", +}; + +static const gb_fusion2_device x870_aor_tachyon_ice_5711_device = +{ + &it5711_64_device, + 0x0230005F, + 0, + "X870 AORUS TACHYON ICE", +}; + +static const gb_fusion2_device x870_gaming_wifi6_5711_device = +{ + &it5711_64_device, + 0x0230005F, + 0, + "X870 GAMING WIFI6", +}; + +static const gb_fusion2_device trx50_ai_top_5711_device = +{ + &it5711_64_device, + 0x3910005F, + 0, + "TRX50 AI TOP", +}; + +static const gb_fusion2_device w790_ai_top_5711_device = +{ + &it5711_64_device, + 0x2210005F, + 0, + "W790 AI TOP", +}; + +static const gb_fusion2_device w880_ai_top_5711_device = +{ + &it5711_64_device, + 0x1110005F, + 0, + "W880 AI TOP", +}; + +static const gb_fusion2_device z890_aero_d_5711_device = +{ + &it5711_64_device, + 0x0110005F, + 0, + "Z890 AERO D", +}; + +static const gb_fusion2_device z890_aero_g_5711_device = +{ + &it5711_64_device, + 0x0110005F, + 0, + "Z890 AERO G", +}; + +static const gb_fusion2_device z890_ai_top_5711_device = +{ + &it5711_64_device, + 0x0110005F, + 0, + "Z890 AI TOP", +}; + +static const gb_fusion2_device z890_aor_tachyon_ice_5711_device = +{ + &it5711_64_device, + 0x0210005F, + 0, + "Z890 AORUS TACHYON ICE", +}; + +static const gb_fusion2_device z890_ud_5711_device = +{ + &it5711_64_device, + 0x0110005F, + 0, + "Z890 UD", +}; + +static const gb_fusion2_device z890_ud_wifi6e_5711_device = +{ + &it5711_64_device, + 0x0110005F, + 0, + "Z890 UD WIFI6E", +}; + +/*-------------------------------------------------------------*\ +| Layout 65 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_65_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_pcie_acc_1_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b760m_ds3h_ddr4_gen5_5711_device = +{ + &it5711_65_device, + 0x2010005F, + 0, + "B760M DS3H DDR4 GEN5", +}; + +static const gb_fusion2_device b760m_ds3h_gen5_5711_device = +{ + &it5711_65_device, + 0x2010005F, + 0, + "B760M DS3H GEN5", +}; + +static const gb_fusion2_device b760m_ds3h_wifi6e_ddr4_gen5_5711_device = +{ + &it5711_65_device, + 0x2010005F, + 0, + "B760M DS3H WIFI6E DDR4 GEN5", +}; + +static const gb_fusion2_device b760m_ds3h_wifi6e_gen5_5711_device = +{ + &it5711_65_device, + 0x2010005F, + 0, + "B760M DS3H WIFI6E GEN5", +}; + +static const gb_fusion2_device b760m_gmg_ac_ddr4_gen5_5711_device = +{ + &it5711_65_device, + 0x2010005F, + 0, + "B760M GAMING AC DDR4 GEN5", +}; + +static const gb_fusion2_device b760m_gmg_wifi6_plus_gen5_5711_device = +{ + &it5711_65_device, + 0x2010005F, + 0, + "B760M GAMING WIFI6 PLUS GEN5", +}; + +static const gb_fusion2_device b760m_gmg_wifi6e_gen5_5711_device = +{ + &it5711_65_device, + 0x2010005F, + 0, + "B760M GAMING WIFI6E GEN5", +}; + +/*-------------------------------------------------------------*\ +| Layout 66 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "PCI-E Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_66_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_pcie_acc_1_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b860m_d2h_5711_device = +{ + &it5711_66_device, + 0x0870005F, + 0, + "B860M D2H", +}; + +static const gb_fusion2_device b860m_eagle_5711_device = +{ + &it5711_66_device, + 0x0870005F, + 0, + "B860M EAGLE", +}; + +static const gb_fusion2_device b860m_eagle_v2_5711_device = +{ + &it5711_66_device, + 0x0870005F, + 0, + "B860M EAGLE V2", +}; + +static const gb_fusion2_device b860m_eagle_wifi6_5711_device = +{ + &it5711_66_device, + 0x0870005F, + 0, + "B860M EAGLE WIFI6", +}; + +static const gb_fusion2_device b860m_eagle_wifi6_v2_5711_device = +{ + &it5711_66_device, + 0x0870005F, + 0, + "B860M EAGLE WIFI6 V2", +}; + +static const gb_fusion2_device b860m_gmg_wifi6_5711_device = +{ + &it5711_66_device, + 0x0870005F, + 0, + "B860M GAMING WIFI6", +}; + +static const gb_fusion2_device b860m_power_5711_device = +{ + &it5711_66_device, + 0x0870005F, + 0, + "B860M POWER", +}; + +/*-------------------------------------------------------------*\ +| Layout 67 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "PCI-E Accent" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_67_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_pcie_acc_1_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b860m_c_5711_device = +{ + &it5711_67_device, + 0x0850005F, + 0, + "B860M C", +}; + +static const gb_fusion2_device b860m_ds3h_5711_device = +{ + &it5711_67_device, + 0x0850005F, + 0, + "B860M DS3H", +}; + +static const gb_fusion2_device b860m_ds3h_wifi6e_5711_device = +{ + &it5711_67_device, + 0x0850005F, + 0, + "B860M DS3H WIFI6E", +}; + +static const gb_fusion2_device b860m_eagle_plus_wifi6e_5711_device = +{ + &it5711_67_device, + 0x0850005F, + 0, + "B860M EAGLE PLUS WIFI6E", +}; + +/*-------------------------------------------------------------*\ +| Layout 68 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "Chipset Accent" : Single | +| Zone "LED_C" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_68_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_chip_acc_3_zone, + &common_led_c_5_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b760m_aor_elite_ddr4_gen5_5711_device = +{ + &it5711_68_device, + 0x2030015F, + 0, + "B760M AORUS ELITE DDR4 GEN5", +}; + +static const gb_fusion2_device b760m_aor_elite_gen5_5711_device = +{ + &it5711_68_device, + 0x2030015F, + 0, + "B760M AORUS ELITE GEN5", +}; + +static const gb_fusion2_device b760m_aor_elite_wifi6e_ddr4_gen5_5711_device = +{ + &it5711_68_device, + 0x2030015F, + 0, + "B760M AORUS ELITE WIFI6E DDR4 GEN5", +}; + +static const gb_fusion2_device b760m_aor_elite_wifi6e_gen5_5711_device = +{ + &it5711_68_device, + 0x2030015F, + 0, + "B760M AORUS ELITE WIFI6E GEN5", +}; + +static const gb_fusion2_device b760m_gmg_x_wifi6e_ddr4_gen5_2_5711_device = +{ + &it5711_68_device, + 0x2030015F, + 0, + "B760M GAMING X WIFI6E DDR4 GEN5", +}; + +static const gb_fusion2_device b840m_aor_elite_wifi6e_5711_device = +{ + &it5711_68_device, + 0x102001DF, + 0, + "B840M AORUS ELITE WIFI6E", +}; + +static const gb_fusion2_device b850_aor_elite_wifi7_5711_device = +{ + &it5711_68_device, + 0x094001DF, + 0, + "B850 AORUS ELITE WIFI7", +}; + +static const gb_fusion2_device b850_aor_elite_wifi7_ice_5711_device = +{ + &it5711_68_device, + 0x094001DF, + 0, + "B850 AORUS ELITE WIFI7 ICE", +}; + +static const gb_fusion2_device b850m_aor_elite_5711_device = +{ + &it5711_68_device, + 0x084001DF, + 0, + "B850M AORUS ELITE", +}; + +static const gb_fusion2_device b850m_aor_elite_wifi6e_5711_device = +{ + &it5711_68_device, + 0x084001DF, + 0, + "B850M AORUS ELITE WIFI6E", +}; + +static const gb_fusion2_device b850m_aor_elite_wifi6e_ice_5711_device = +{ + &it5711_68_device, + 0x084001DF, + 0, + "B850M AORUS ELITE WIFI6E ICE", +}; + +static const gb_fusion2_device b850m_aor_elite_wifi6e_ice_p_5711_device = +{ + &it5711_68_device, + 0x084001DF, + 0, + "B850M AORUS ELITE WIFI7 ICE-P", +}; + +static const gb_fusion2_device b850m_gmg_x_wifi6e_5711_device = +{ + &it5711_68_device, + 0x084001DF, + 0, + "B850M GAMING X WIFI6E", +}; + +static const gb_fusion2_device x870_aor_elite_wifi7_5711_device = +{ + &it5711_68_device, + 0x014001DF, + 0, + "X870 AORUS ELITE WIFI7", +}; + +static const gb_fusion2_device x870_aor_elite_wifi7_ice_5711_device = +{ + &it5711_68_device, + 0x014001DF, + 0, + "X870 AORUS ELITE WIFI7 ICE", +}; + +static const gb_fusion2_device x870_eagle_wifi7_5711_device = +{ + &it5711_68_device, + 0x014001DF, + 0, + "X870 EAGLE WIFI7", +}; + +static const gb_fusion2_device x870_gmg_x_wifi7_5711_device = +{ + &it5711_68_device, + 0x014001DF, + 0, + "X870 GAMING X WIFI7", +}; + +static const gb_fusion2_device x870m_aor_elite_wifi7_5711_device = +{ + &it5711_68_device, + 0x004001DF, + 0, + "X870M AORUS ELITE WIFI7", +}; + +static const gb_fusion2_device x870m_aor_elite_wifi7_ice_5711_device = +{ + &it5711_68_device, + 0x004001DF, + 0, + "X870M AORUS ELITE WIFI7 ICE", +}; + +/*-------------------------------------------------------------*\ +| Layout 69 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "LED_C" : Single | +| Zone "Chipset Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_69_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_led_c_5_zone, + &common_chip_acc_11_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x870_aor_elite_x3d_ice_5711_device = +{ + &it5711_69_device, + 0x015001DF, + 0, + "X870 AORUS ELITE X3D ICE", +}; + +static const gb_fusion2_device x870_aor_stealth_5711_device = +{ + &it5711_69_device, + 0x015001DF, + 0, + "X870 AORUS STEALTH", +}; + +static const gb_fusion2_device x870_aor_stealth_ice_5711_device = +{ + &it5711_69_device, + 0x015001DF, + 0, + "X870 AORUS STEALTH ICE", +}; + +static const gb_fusion2_device x870e_aor_elite_wifi7_5711_device = +{ + &it5711_69_device, + 0x015001DF, + 0, + "X870E AORUS ELITE WIFI7", +}; + +static const gb_fusion2_device x870e_aor_elite_wifi7_ice_5711_device = +{ + &it5711_69_device, + 0x015001DF, + 0, + "X870E AORUS ELITE WIFI7 ICE", +}; + +/*-------------------------------------------------------------*\ +| Layout 70 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "LED_C" : Single | +| Zone "IO Cover" : Single | +| Zone "Chipset Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_70_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_led_c_5_zone, + &common_io_cov_10_zone, + &common_chip_acc_11_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b650e_aor_stealth_ice_5711_device = +{ + &it5711_70_device, + 0x311001DF, + 0, + "B650E AORUS STEALTH ICE", +}; + +static const gb_fusion2_device x870e_aero_x3d_wood_5711_device = +{ + &it5711_70_device, + 0x016001DF, + 0, + "X870E AERO X3D WOOD", +}; + +static const gb_fusion2_device x870e_aor_elite_x3d_5711_device = +{ + &it5711_70_device, + 0x016001DF, + 0, + "X870E AORUS ELITE X3D", +}; + +static const gb_fusion2_device x870e_aor_elite_x3d_ice_5711_device = +{ + &it5711_70_device, + 0x016001DF, + 0, + "X870E AORUS ELITE X3D ICE", +}; + +static const gb_fusion2_device x870e_aor_pro_5711_device = +{ + &it5711_70_device, + 0x016001DF, + 0, + "X870E AORUS PRO", +}; + +static const gb_fusion2_device x870e_aor_pro_ice_5711_device = +{ + &it5711_70_device, + 0x016001DF, + 0, + "X870E AORUS PRO ICE", +}; + +static const gb_fusion2_device x870e_aor_pro_x3d_5711_device = +{ + &it5711_70_device, + 0x016001DF, + 0, + "X870E AORUS PRO X3D", +}; + +static const gb_fusion2_device x870e_aor_pro_x3d_ice_5711_device = +{ + &it5711_70_device, + 0x016001DF, + 0, + "X870E AORUS PRO X3D ICE", +}; + +/*-------------------------------------------------------------*\ +| Layout 71 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "Chipset Accent" : Single | +| Zone "LED_C" : Single | +| Zone "IO Cover" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_71_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_chip_acc_3_zone, + &common_led_c_5_zone, + &common_io_cov_10_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device b850m_aor_pro_wifi7_5711_device = +{ + &it5711_71_device, + 0x085001DF, + 0, + "B850M AORUS PRO WIFI7", +}; + +static const gb_fusion2_device b860_aor_elite_wifi7_ice_5711_device = +{ + &it5711_71_device, + 0x096001DF, + 0, + "B860 AORUS ELITE WIFI7 ICE", +}; + +static const gb_fusion2_device b860_eagle_wifi6e_5711_device = +{ + &it5711_71_device, + 0x096001DF, + 0, + "B860 EAGLE WIFI6E", +}; + +static const gb_fusion2_device b860_gmg_x_wifi6e_5711_device = +{ + &it5711_71_device, + 0x096001DF, + 0, + "B860 GAMING X WIFI6E", +}; + +static const gb_fusion2_device b860m_aor_elite_5711_device = +{ + &it5711_71_device, + 0x086001DF, + 0, + "B860M AORUS ELITE", +}; + +static const gb_fusion2_device b860m_aor_elite_wifi6e_5711_device = +{ + &it5711_71_device, + 0x086001DF, + 0, + "B860M AORUS ELITE WIFI6E", +}; + +static const gb_fusion2_device b860m_aor_elite_wifi6e_ice_5711_device = +{ + &it5711_71_device, + 0x086001DF, + 0, + "B860M AORUS ELITE WIFI6E ICE", +}; + +static const gb_fusion2_device b860m_aor_pro_wifi7_5711_device = +{ + &it5711_71_device, + 0x086001DF, + 0, + "B860M AORUS PRO WIFI7", +}; + +static const gb_fusion2_device z890_aor_elite_wifi7_5711_device = +{ + &it5711_71_device, + 0x013001DF, + 0, + "Z890 AORUS ELITE WIFI7", +}; + +static const gb_fusion2_device z890_aor_elite_wifi7_ice_5711_device = +{ + &it5711_71_device, + 0x013001DF, + 0, + "Z890 AORUS ELITE WIFI7 ICE", +}; + +static const gb_fusion2_device z890_aor_elite_x_ice_5711_device = +{ + &it5711_71_device, + 0x013001DF, + 0, + "Z890 AORUS ELITE X ICE", +}; + +static const gb_fusion2_device z890_aor_pro_ice_5711_device = +{ + &it5711_71_device, + 0x013001DF, + 0, + "Z890 AORUS PRO ICE", +}; + +static const gb_fusion2_device z890m_aor_elite_wifi7_5711_device = +{ + &it5711_71_device, + 0x003001DF, + 0, + "Z890M AORUS ELITE WIFI7", +}; + +static const gb_fusion2_device z890m_aor_elite_wifi7_ice_5711_device = +{ + &it5711_71_device, + 0x003001DF, + 0, + "Z890M AORUS ELITE WIFI7 ICE", +}; + +/*-------------------------------------------------------------*\ +| Layout 72 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "ARGB_V2_4" : Linear | +| Zone "LED_C" : Single | +| Zone "IO Cover" : Single | +| Zone "Chipset Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_72_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_argb_v2_4_zone, + &common_led_c_5_zone, + &common_io_cov_10_zone, + &common_chip_acc_11_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x870e_aor_mstr_5711_device = +{ + &it5711_72_device, + 0x017001DF, + 0, + "X870E AORUS MASTER", +}; + +static const gb_fusion2_device x870e_aor_mstr_x3d_5711_device = +{ + &it5711_72_device, + 0x017001DF, + 0, + "X870E AORUS MASTER X3D", +}; + +static const gb_fusion2_device x870e_aor_mstr_x3d_ice_5711_device = +{ + &it5711_72_device, + 0x017001DF, + 0, + "X870E AORUS MASTER X3D ICE", +}; + +static const gb_fusion2_device z890_aor_mstr_ai_top_5711_device = +{ + &it5711_72_device, + 0x026001DF, + 0, + "Z890 AORUS MASTER AI TOP", +}; + +/*-------------------------------------------------------------*\ +| Layout 73 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "ARGB_V2_4" : Linear | +| Zone "LED_C" : Single | +| Zone "WIFI Antenna" : Single | +| Zone "IO Cover" : Single | +| Zone "Chipset Accent" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_73_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_argb_v2_4_zone, + &common_led_c_5_zone, + &common_wifi_ant_9_zone, + &common_io_cov_10_zone, + &common_chip_acc_11_zone, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x870e_aor_xtrm_ai_top_5711_device = +{ + &it5711_73_device, + 0x028001DF, + 0, + "X870E AORUS XTREME AI TOP", +}; + +/*-------------------------------------------------------------*\ +| Layout 74 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "Game On LED" : Single | +| Zone "Chipset Accent" : Single | +| Zone "LED_C" : Single | +| Zone "IO Cover" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_74_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_game_on_1_zone, + &common_chip_acc_3_zone, + &common_led_c_5_zone, + &common_io_cov_10_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z890_eagle_5711_device = +{ + &it5711_74_device, + 0x014001DF, + 0, + "Z890 EAGLE", +}; + +static const gb_fusion2_device z890_eagle_wifi7_5711_device = +{ + &it5711_74_device, + 0x014001DF, + 0, + "Z890 EAGLE WIFI7", +}; + +static const gb_fusion2_device z890_gmg_x_wifi7_5711_device = +{ + &it5711_74_device, + 0x014001DF, + 0, + "Z890 GAMING X WIFI7", +}; + +/*-------------------------------------------------------------*\ +| Layout 75 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "ARGB_V2_4" : Linear | +| Zone "LED_C" : Single | +| Zone "WIFI Antenna" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_75_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_argb_v2_4_zone, + &common_led_c_5_zone, + &common_wifi_ant_11_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x870e_aor_xtrm_x3d_ai_top_5711_device = +{ + &it5711_75_device, + 0x029001DF, + 0, + "X870E AORUS XTREME X3D AI TOP", +}; + +/*-------------------------------------------------------------*\ +| Layout 76 048D:5711 | +| | +| Zone "ARGB_V2_1" : Linear | +| Zone "ARGB_V2_2" : Linear | +| Zone "ARGB_V2_3" : Linear | +| Zone "ARGB_V2_4" : Linear | +| Zone "LED_C" : Single | +| Zone "IO Cover" : Single | +| Zone "WIFI Antenna" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_76_device = +{ + &common_argb_v2_1_zone, + &common_argb_v2_2_zone, + &common_argb_v2_3_zone, + &common_argb_v2_4_zone, + &common_led_c_5_zone, + &common_io_cov_10_zone, + &common_wifi_ant_11_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z890_aor_xtrm_ai_top_5711_device = +{ + &it5711_76_device, + 0x027001DF, + 0, + "Z890 AORUS XTREME AI TOP", +}; + +/*-------------------------------------------------------------*\ +| Layout 77 048D:5711 | +| | +| Zone "Chipset Accent" : Single | +| Zone "RAM Cover" : Single | +| Zone "SSD Cover" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_77_device = +{ + &common_chip_acc_6_zone, + &common_ram_cov_7_zone, + &common_ssd_cov_8_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device x870e_aor_xtrm_ai_x3d_top_5711_device = +{ + &it5711_77_device, + 0x029001DF, + 1, + "X870E AORUS XTREME X3D AI TOP", +}; + +/*-------------------------------------------------------------*\ +| Layout 78 048D:5711 | +| | +| Zone "Chipset Accent" : Single | +| Zone "SSD Cover" : Single | +| Zone "RAM Cover" : Single | +\*-------------------------------------------------------------*/ +static gb_fusion2_layout it5711_78_device = +{ + &common_chip_acc_6_zone, + &common_ssd_cov_7_zone, + &common_ram_cov_8_zone, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const gb_fusion2_device z890_aor_xtrm_ai_top_2_5711_device = +{ + &it5711_78_device, + 0x027001DF, + 1, + "Z890 AORUS XTREME AI TOP", +}; + +/*-------------------------------------------------------------------------*\ +| DEVICE MASTER LIST | +\*-------------------------------------------------------------------------*/ +const gb_fusion2_device* gb_fusion2_device_list_data[] = +{ +/*-------------------------------------------------------------------------*\ +| Generic Layout (Used when no match found) | +\*-------------------------------------------------------------------------*/ + &generic_it8297_device, + &generic_it8950_device, + &generic_it5711_device, + +/*-----------------------------------------------------------------*\ +| IT8297 Devices | +\*-----------------------------------------------------------------*/ + &b450_gmg_x_device, + &b450m_ds3h_wifi_8297_device, + &trx40_aor_designare_device, + &trx40_aor_master_device, + &trx40_aor_pro_wifi_device, + &trx40_aor_xtrm_2_device, + &trx40_aor_xtrm_device, + &x570_aor_elite_device, + &x570_aor_elite_wifi_device, + &x570_aor_mstr_device, + &x570_aor_pro_device, + &x570_aor_ultra_device, + &x570_aor_xtrm_device, + &x570_i_aor_pro_wifi_device, + &z390_aor_mstr_device, + &z390_aor_mstr_g2_device, + &z390_aor_pro_device, + &z390_aor_pro_wifi_device, + &z390_aor_ultra_device, + &z390_aor_xtrm_device, + &z390_aor_xtrm_wtr_force_5g_device, + &z390_aor_xtrm_wtr_force_device, + &z390_i_aor_pro_wifi_device, + +/*-----------------------------------------------------------------*\ +| IT8950 Devices | +\*-----------------------------------------------------------------*/ + &h810m_gmg_wifi6_device, + &h810m_h_device, + &h810m_s2h_device, + +/*-----------------------------------------------------------------*\ +| IT8950 + Super I/O Hybrid Devices | +\*-----------------------------------------------------------------*/ + &b860m_d_device, + &b860m_d3hp_device, + &b860m_e_device, + &b860m_h_device, + &b860m_k_device, + &z890m_gmg_x_device, + +/*-----------------------------------------------------------------*\ +| IT5702 Devices | +\*-----------------------------------------------------------------*/ + &a520_aor_elite_device, + &a520i_ac_device, + &a520m_ds3h_ac_device, + &a520m_ds3h_device, + &a520m_h_device, + &a520m_s2h_device, + &a620i_ax_device, + &a620m_c_device, + &a620m_ds3h_device, + &a620m_ds3h_2_device, + &a620m_gmg_x_ax_device, + &a620m_gmg_x_ax_2_device, + &a620m_gmg_x_device, + &a620m_gmg_x_2_device, + &a620m_h_device, + &a620m_h_2_device, + &a620m_s2h_device, + &a620m_s2h_2_device, + &b450m_ds3h_v3_device, + &b450m_ds3h_wifi_device, + &b460_aor_pro_ac_device, + &b460m_aor_elite_device, + &b460m_aor_pro_device, + &b460m_ds3h_ac_device, + &b460m_ds3h_v2_device, + &b550_aor_elite_ax_device, + &b550_aor_elite_ax_v2_device, + &b550_aor_elite_ax_v3_device, + &b550_aor_elite_device, + &b550_aor_elite_v2_device, + &b550_aor_mstr_device, + &b550_aor_pro_ac_device, + &b550_aor_pro_ax_device, + &b550_aor_pro_device, + &b550_aor_pro_v2_device, + &b550_eagle_device, + &b550_eagle_wifi6_device, + &b550_gmg_x_device, + &b550_gmg_x_v2_device, + &b550_vision_d_device, + &b550_vision_dp_device, + &b550i_aor_pro_ax_device, + &b550m_aor_elite_ax_device, + &b550m_aor_elite_device, + &b550m_aor_pro_ax_device, + &b550m_aor_pro_device, + &b550m_aor_pro_p_device, + &b550m_ds3h_ac_device, + &b550m_ds3h_ac_r2_device, + &b550m_ds3h_device, + &b550m_ds3h_r2_device, + &b550m_gmg_device, + &b550m_gmg_x_wifi6_device, + &b550m_h_device, + &b550m_s2h_device, + &b560_hd3_device, + &b560i_aor_pro_ax_device, + &b560m_aor_elite_device, + &b560m_aor_pro_ax_device, + &b560m_aor_pro_device, + &b560m_d2v_device, + &b560m_d3h_device, + &b560m_ds3h_ac_device, + &b560m_ds3h_device, + &b560m_ds3h_plus_device, + &b560m_gmg_hd_device, + &b560m_h_device, + &b560m_pwr_device, + &b650_aero_g_device, + &b650_aor_elite_ax_device, + &b650_aor_elite_ax_ice_device, + &b650_aor_elite_ax_v2_device, + &b650_aor_elite_device, + &b650_aor_elite_v2_device, + &b650_aor_elite_x_ax_ice_device, + &b650_aor_pro_ax_device, + &b650_eagle_ax_device, + &b650_eagle_device, + &b650_gmg_x_ax_device, + &b650_gmg_x_ax_v2_device, + &b650_gmg_x_device, + &b650_ud_ac_device, + &b650_ud_ax_device, + &b650e_aor_mstr_device, + &b650e_aor_pro_x_usb4_device, + &b650e_tachyon_device, + &b650i_aor_ultra_device, + &b650i_ax_device, + &b650m_aor_elite_ax_device, + &b650m_aor_elite_ax_ice_device, + &b650m_aor_elite_device, + &b650m_aor_pro_ax_device, + &b650m_c_device, + &b650m_c_v2_device, + &b650m_c_v3_device, + &b650m_d2h_ddr4_device, + &b650m_d2h_device, + &b650m_d2hp_device, + &b650m_d3h_ddr4_device, + &b650m_d3hp_ax_device, + &b650m_d3hp_device, + &b650m_ds3h_ax_ddr4_device, + &b650m_ds3h_ddr4_device, + &b650m_ds3h_device, + &b650m_ds3h_2_device, + &b650m_gmg_ac_ddr4_device, + &b650m_gmg_ac_device, + &b650m_gmg_ddr4_device, + &b650m_gmg_plus_wifi_device, + &b650m_gmg_wifi_device, + &b650m_gmg_wifi6e_device, + &b650m_gmg_x_ax_device, + &b650m_gmg_x_ax_2_device, + &b650m_h_device, + &b650m_k_device, + &b650m_k_2_device, + &b650m_pwr_ddr4_device, + &b650m_s2h_device, + &b660_aor_elite_ax_ddr4_device, + &b660_aor_elite_ddr4_device, + &b660_aor_mstr_ddr4_device, + &b660_aor_mstr_device, + &b660_ds3h_ac_ddr4_device, + &b660_ds3h_ac_device, + &b660_ds3h_ax_ddr4_device, + &b660_ds3h_ddr4_device, + &b660_gmg_x_ax_ddr4_device, + &b660_gmg_x_ddr4_device, + &b660_gmg_x_device, + &b660i_aor_pro_ddr4_device, + &b660m_aor_elite_ax_ddr4_device, + &b660m_aor_elite_ddr4_device, + &b660m_aor_pro_ax_ddr4_device, + &b660m_aor_pro_ax_device, + &b660m_aor_pro_ddr4_device, + &b660m_aor_pro_device, + &b660m_gmg_x_ax_ddr4_device, + &b660m_gmg_x_ax_device, + &b660m_gmg_x_ddr4_device, + &b660m_gmg_x_device, + &b690i_aor_ultra_ddr4_device, + &b690i_aor_ultra_device, + &b690i_aor_ultra_lite_ddr4_device, + &b690i_aor_ultra_lite_device, + &b690i_aor_ultra_plus_ddr4_device, + &b690i_aor_ultra_plus_device, + &b760_aor_elite_ax_ddr4_device, + &b760_aor_elite_ax_device, + &b760_aor_elite_ddr4_device, + &b760_aor_elite_device, + &b760_aor_mstr_ddr4_device, + &b760_ds3h_ac_ddr4_device, + &b760_ds3h_ac_device, + &b760_ds3h_ax_ddr4_device, + &b760_ds3h_ax_device, + &b760_ds3h_ax_v2_device, + &b760_ds3h_ddr4_device, + &b760_ds3h_device, + &b760_gmg_x_ax_ddr4_device, + &b760_gmg_x_ax_device, + &b760_gmg_x_ddr4_device, + &b760_gmg_x_device, + &b760i_aor_pro_ddr4_device, + &b760i_aor_pro_device, + &b760m_aor_elite_ax_ddr4_device, + &b760m_aor_elite_ax_device, + &b760m_aor_elite_ddr4_device, + &b760m_aor_elite_device, + &b760m_aor_elite_x_ax_device, + &b760m_aor_pro_ax_ddr4_device, + &b760m_aor_pro_ax_device, + &b760m_aor_pro_ddr4_device, + &b760m_aor_pro_device, + &b760m_c_device, + &b760m_c_v2_device, + &b760m_d2h_ddr4_device, + &b760m_d2h_device, + &b760m_d3h_ddr4_device, + &b760m_d3h_device, + &b760m_d3hp_ddr4_device, + &b760m_d3hp_device, + &b760m_d3hp_wifi6_device, + &b760m_ds3h_ax_ddr4_device, + &b760m_ds3h_ax_device, + &b760m_ds3h_ddr4_device, + &b760m_ds3h_device, + &b760m_gmg_ac_ddr4_device, + &b760m_gmg_ac_device, + &b760m_gmg_ddr4_device, + &b760m_gmg_device, + &b760m_gmg_plus_wifi_ddr4_device, + &b760m_gmg_wifi_device, + &b760m_gmg_wifi_plus_device, + &b760m_gmg_x_ax_ddr4_device, + &b760m_gmg_x_ax_device, + &b760m_gmg_x_ddr4_device, + &b760m_gmg_x_device, + &b760m_h_v2_device, + &b760m_pwr_ddr4_device, + &b760m_pwr_device, + &b790i_aor_ultra_device, + &h470_aor_pro_ax_device, + &h470_hd3_device, + &h490i_aor_pro_ax_device, + &h490m_ds3h_device, + &h610m_d3h_ddr4_device, + &h610m_d3h_wifi_ddr4_device, + &h610m_d3w_device, + &h610m_d3w_wifi6_device, + &h610m_gmg_wifi_ddr4_device, + &trx50_aero_d_device, + &x570s_aero_g_device, + &x570s_aor_mstr_device, + &x570s_aor_pro_ax_device, + &x570si_aor_pro_ax_device, + &x670_aor_elite_ax_device, + &x670_aor_xtrm_2_device, + &x670_gmg_x_ax_device, + &x670_gmg_x_ax_v2_device, + &x670e_aor_mstr_device, + &x670e_aor_pro_x_device, + &x670e_aor_xtrm_device, + &z490_aor_elite_ac_device, + &z490_aor_elite_device, + &z490_aor_mstr_device, + &z490_aor_mstr_waterforce_device, + &z490_aor_pro_ax_device, + &z490_aor_ultra_device, + &z490_aor_ultra_g2_device, + &z490_aor_xtrm_2_device, + &z490_aor_xtrm_device, + &z490_aor_xtrm_waterforce_2_device, + &z490_aor_xtrm_waterforce_device, + &z490_gmg_x_ax_device, + &z490_gmg_x_device, + &z490_ud_ac_device, + &z490_ud_device, + &z490_vision_d_device, + &z490_vision_g_device, + &z490i_aor_ultra_device, + &z490m_device, + &z490m_gmg_x_device, + &z590_aor_elite_ax_device, + &z590_aor_elite_device, + &z590_aor_mstr_device, + &z590_aor_pro_ax_device, + &z590_aor_tachyon_device, + &z590_aor_ultra_device, + &z590_aor_xtrm_device, + &z590_aor_xtrm_waterforce_2_device, + &z590_aor_xtrm_waterforce_device, + &z590_d_device, + &z590_gmg_x_device, + &z590_ud_ac_device, + &z590_ud_device, + &z590_vision_d_device, + &z590_vision_g_device, + &z590i_aor_ultra_device, + &z590i_vis_d_device, + &z590m_device, + &z590m_gmg_x_device, + &z690_aero_d_device, + &z690_aero_g_ddr4_device, + &z690_aero_g_device, + &z690_aor_elite_ax_ddr4_device, + &z690_aor_elite_ax_ddr4_v2_device, + &z690_aor_elite_ax_device, + &z690_aor_elite_ax_2_device, + &z690_aor_elite_ddr4_device, + &z690_aor_elite_device, + &z690_aor_mstr_device, + &z690_aor_pro_ddr4_device, + &z690_aor_pro_device, + &z690_aor_tachyon_device, + &z690_aor_ultra_device, + &z690_aor_xtrm_2_device, + &z690_aor_xtrm_device, + &z690_aor_xtrm_waterforce_2_device, + &z690_aor_xtrm_waterforce_device, + &z690_gmg_x_ddr4_device, + &z690_gmg_x_ddr4_v2_device, + &z690_gmg_x_device, + &z690_ud_ac_device, + &z690_ud_ax_ddr4_device, + &z690_ud_ax_ddr4_v2_device, + &z690_ud_ax_device, + &z690_ud_ax_v2_device, + &z690_ud_ddr4_device, + &z690_ud_ddr4_v2_device, + &z690_ud_device, + &z690m_aor_elite_ax_ddr4_device, + &z690m_ds3h_ddr4_device, + &z790_aero_g_device, + &z790_aor_elite_ax_ddr4_device, + &z790_aor_elite_ax_device, + &z790_aor_elite_ax_ice_device, + &z790_aor_elite_ax_w_device, + &z790_aor_elite_ddr4_device, + &z790_aor_elite_device, + &z790_aor_elite_x_ax_device, + &z790_aor_elite_x_device, + &z790_aor_elite_x_wifi7_device, + &z790_aor_mstr_2_device, + &z790_aor_mstr_device, + &z790_aor_mstr_x_2_device, + &z790_aor_mstr_x_device, + &z790_aor_pro_x_device, + &z790_aor_pro_x_wifi7_device, + &z790_aor_tachyon_device, + &z790_aor_tachyon_x_device, + &z790_aor_xtreme_x_device, + &z790_aor_xtrm_2_device, + &z790_aor_xtrm_device, + &z790_aor_xtrm_x_2_device, + &z790_d_ac_device, + &z790_d_ax_device, + &z790_d_ddr4_device, + &z790_d_device, + &z790_d_wifi_device, + &z790_eagle_ax_device, + &z790_eagle_device, + &z790_gmg_plus_ax_device, + &z790_gmg_x_ax_device, + &z790_gmg_x_device, + &z790_s_ddr4_device, + &z790_s_wifi_ddr4_device, + &z790_ud_ac_device, + &z790_ud_ax_device, + &z790_ud_device, + &z790m_aor_elite_ax_device, + &z790m_aor_elite_ax_ice_device, + &z790m_aor_elite_device, + + +/*-----------------------------------------------------------------*\ +| IT5711 Devices | +\*-----------------------------------------------------------------*/ + &a620i_ax_5711_device, + &a620m_ds3h_5711_device, + &a620m_gmg_x_5711_device, + &a620m_h_5711_device, + &a620m_s2h_5711_device, + &b650e_aor_stealth_ice_5711_device, + &b650e_eagle_wifi6e_5711_device, + &b650em_c_5711_device, + &b650em_ds3h_wifi6e_5711_device, + &b650em_force_wifi6e_5711_device, + &b760_ds3h_gen5_5711_device, + &b760_ds3h_wifi6e_gen5_5711_device, + &b760_gmg_x_ddr4_gen5_5711_device, + &b760_gmg_x_gen5_5711_device, + &b760_gmg_x_wifi6e_gen5_5711_device, + &b760m_aor_elite_ddr4_gen5_5711_device, + &b760m_aor_elite_gen5_5711_device, + &b760m_aor_elite_wifi6e_ddr4_gen5_5711_device, + &b760m_aor_elite_wifi6e_gen5_5711_device, + &b760m_c_v3_5711_device, + &b760m_ds3h_ddr4_gen5_5711_device, + &b760m_ds3h_gen5_5711_device, + &b760m_ds3h_wifi6e_ddr4_gen5_5711_device, + &b760m_ds3h_wifi6e_gen5_5711_device, + &b760m_gmg_ac_ddr4_gen5_5711_device, + &b760m_gmg_wifi6_plus_gen5_5711_device, + &b760m_gmg_wifi6e_gen5_5711_device, + &b760m_gmg_x_ddr4_gen5_5711_device, + &b760m_gmg_x_gen5_5711_device, + &b760m_gmg_x_wifi6e_ddr4_gen5_5711_device, + &b760m_gmg_x_wifi6e_ddr4_gen5_2_5711_device, + &b760m_gmg_x_wifi6e_gen5_5711_device, + &b840_eagle_wifi6e_5711_device, + &b840_gmg_x_wifi6e_5711_device, + &b840m_aor_elite_wifi6e_5711_device, + &b840m_d2h_5711_device, + &b840m_ds3h_5711_device, + &b840m_ds3h_wifi6_5711_device, + &b840m_eagle_wifi6_5711_device, + &b840m_h_5711_device, + &b850_ai_top_5711_device, + &b850_aor_elite_wifi7_5711_device, + &b850_aor_elite_wifi7_ice_5711_device, + &b850_aor_stealth_5711_device, + &b850_aor_stealth_ice_5711_device, + &b850_eagle_ice_5711_device, + &b850_eagle_wifi6e_5711_device, + &b850_eagle_wifi7_ice_5711_device, + &b850_gmg_wifi6_5711_device, + &b850i_aor_pro_5711_device, + &b850m_aor_elite_5711_device, + &b850m_aor_elite_wifi6e_5711_device, + &b850m_aor_elite_wifi6e_ice_5711_device, + &b850m_aor_elite_wifi6e_ice_p_5711_device, + &b850m_aor_pro_wifi7_5711_device, + &b850m_c_5711_device, + &b850m_d3hp_5711_device, + &b850m_ds3h_5711_device, + &b850m_ds3h_ice_5711_device, + &b850m_eagle_wifi6e_5711_device, + &b850m_eagle_wifi6e_ice_5711_device, + &b850m_force_5711_device, + &b850m_force_v2_5711_device, + &b850m_force_wifi6e_5711_device, + &b850m_force_wifi6e_v2_5711_device, + &b850m_gmg_x_wifi6e_5711_device, + &b860_aor_elite_wifi7_ice_5711_device, + &b860_ds3h_5711_device, + &b860_ds3h_wifi6e_5711_device, + &b860_eagle_wifi6e_5711_device, + &b860_gmg_x_wifi6e_5711_device, + &b860i_aor_pro_ice_5711_device, + &b860m_aor_elite_5711_device, + &b860m_aor_elite_wifi6e_5711_device, + &b860m_aor_elite_wifi6e_ice_5711_device, + &b860m_aor_pro_wifi7_5711_device, + &b860m_c_5711_device, + &b860m_d2h_5711_device, + &b860m_ds3h_5711_device, + &b860m_ds3h_wifi6e_5711_device, + &b860m_eagle_plus_wifi6e_5711_device, + &b860m_eagle_v2_5711_device, + &b860m_eagle_wifi6_5711_device, + &b860m_eagle_wifi6_v2_5711_device, + &b860m_gmg_wifi6_5711_device, + &b860m_gmg_x_5711_device, + &b860m_gmg_x_wifi6e_5711_device, + &b860m_power_5711_device, + &trx50_ai_top_5711_device, + &w790_ai_top_5711_device, + &w880_ai_top_5711_device, + &x870_aor_elite_wifi7_5711_device, + &x870_aor_elite_wifi7_ice_5711_device, + &x870_aor_elite_x3d_ice_5711_device, + &x870_aor_stealth_5711_device, + &x870_aor_stealth_ice_5711_device, + &x870_aor_tachyon_ice_5711_device, + &x870_eagle_wifi7_5711_device, + &x870_gaming_wifi6_5711_device, + &x870_gmg_x_wifi7_5711_device, + &x870e_aero_x3d_wood_5711_device, + &x870e_aor_elite_wifi7_5711_device, + &x870e_aor_elite_wifi7_ice_5711_device, + &x870e_aor_elite_x3d_5711_device, + &x870e_aor_elite_x3d_ice_5711_device, + &x870e_aor_mstr_5711_device, + &x870e_aor_mstr_x3d_5711_device, + &x870e_aor_mstr_x3d_ice_5711_device, + &x870e_aor_pro_5711_device, + &x870e_aor_pro_ice_5711_device, + &x870e_aor_pro_x3d_5711_device, + &x870e_aor_pro_x3d_ice_5711_device, + &x870e_aor_xtrm_ai_top_5711_device, + &x870e_aor_xtrm_ai_x3d_top_5711_device, + &x870e_aor_xtrm_x3d_ai_top_5711_device, + &x870i_aor_pro_ice_5711_device, + &x870m_aor_elite_wifi7_5711_device, + &x870m_aor_elite_wifi7_ice_5711_device, + &z890_aero_d_5711_device, + &z890_aero_g_5711_device, + &z890_ai_top_5711_device, + &z890_aor_elite_wifi7_5711_device, + &z890_aor_elite_wifi7_ice_5711_device, + &z890_aor_elite_x_ice_5711_device, + &z890_aor_mstr_ai_top_5711_device, + &z890_aor_pro_ice_5711_device, + &z890_aor_tachyon_ice_5711_device, + &z890_aor_xtrm_ai_top_5711_device, + &z890_aor_xtrm_ai_top_2_5711_device, + &z890_eagle_5711_device, + &z890_eagle_wifi7_5711_device, + &z890_gmg_x_wifi7_5711_device, + &z890_ud_5711_device, + &z890_ud_wifi6e_5711_device, + &z890i_aor_ultra_5711_device, + &z890m_aor_elite_wifi7_5711_device, + &z890m_aor_elite_wifi7_ice_5711_device, +}; +const unsigned int GB_FUSION2_DEVICE_COUNT = (sizeof(gb_fusion2_device_list_data) / sizeof(gb_fusion2_device_list_data[ 0 ])); +const gb_fusion2_device** gb_fusion2_device_list = gb_fusion2_device_list_data; + diff --git a/Controllers/GigabyteRGBFusion2USBController/GigabyteFusion2USB_Devices.h b/Controllers/GigabyteRGBFusion2USBController/GigabyteFusion2USB_Devices.h new file mode 100644 index 0000000..52a81ca --- /dev/null +++ b/Controllers/GigabyteRGBFusion2USBController/GigabyteFusion2USB_Devices.h @@ -0,0 +1,132 @@ +/*---------------------------------------------------------*\ +| Gigabyte_Fusion2_USB_Devices.h | +| | +| Gigabyte Fusion 2 USB Device layouts and | +| and mapping to the device IDs stored on chip | +| | +| megadjc 31 Jul 2025 | +| chrism 29 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +#define GB_FUSION2_ZONES_MAX 12 + +/*--------------------------------------------------------*\ +| Base LED mappings found on all controllers. | +\*--------------------------------------------------------*/ +enum GB_FUSION2_LED_IDX +{ + LED1 = 0, + LED2 = 1, + LED3 = 2, + LED4 = 3, + LED5 = 4, + LED6 = 5, + LED7 = 6, + LED8 = 7, + +/*--------------------------------------------------------*\ +| IT8297/IT5701/IT5702 ARGB Headers | +\*--------------------------------------------------------*/ + HDR_D_LED1 = 5, + HDR_D_LED2 = 6, + HDR_D_LED1_ARGB = 0x58, + HDR_D_LED2_ARGB = 0x59, + +/*--------------------------------------------------------*\ +| Additional LED mappings found on IT5711 controllers. | +\*--------------------------------------------------------*/ + LED9 = 8, + LED10 = 9, + LED11 = 10, + +/*--------------------------------------------------------*\ +| IT5711 additional ARGB Headers. | +\*--------------------------------------------------------*/ + HDR_D_LED3 = 7, + HDR_D_LED4 = 8, + HDR_D_LED3_ARGB = 0x62, + HDR_D_LED4_ARGB = 0x63, +}; + +/*-------------------------------------------------*\ +| LED mapping | +\*-------------------------------------------------*/ +using FwdLedHeaders = std::map; +using RvrseLedHeaders = std::map; +const FwdLedHeaders LedLookup = +{ + {"LED1", LED1 }, + {"LED2", LED2 }, + {"LED3", LED3 }, + {"LED4", LED4 }, + {"LED5", LED5 }, + {"LED6", LED6 }, + {"LED7", LED7 }, + {"LED8", LED8 }, + {"LED9", LED9 }, + {"LED10", LED10 }, + {"LED11", LED11 }, + {"HDR_D_LED1", HDR_D_LED1 }, + {"HDR_D_LED2", HDR_D_LED2 }, + {"HDR_D_LED3", HDR_D_LED3 }, + {"HDR_D_LED4", HDR_D_LED4 }, + /*-------------------------------------------------*\ + | The DLED ARGB index is not required for parsing | + \*-------------------------------------------------*/ + /*-------------------------------------------------*\ + {"HDR_D_LED1_RGB", HDR_D_LED1_ARGB }, + {"HDR_D_LED2_RGB", HDR_D_LED2_ARGB }, + {"HDR_D_LED3_RGB", HDR_D_LED3_ARGB }, + {"HDR_D_LED4_RGB", HDR_D_LED4_ARGB }, + \*-------------------------------------------------*/ +}; + +/*--------------------------------------------------------*\ +| The layout_id masks for supported effects. | +\*--------------------------------------------------------*/ +enum GB_LID_EFFECTS_MASKS : uint32_t +{ + GB_EFF_BREATH = 0x001, + GB_EFF_BEAT = 0x002, + GB_EFF_CYCLE = 0x004, + GB_EFF_FLASH = 0x008, + GB_EFF_RANDOM = 0x010, + GB_EFF_WAVE = 0x020, + GB_EFF_DFLASH = 0x040, + GB_EFF_WAVE1 = 0x080, + GB_EFF_WAVE2 = 0x100, + GB_EFF_CORE_MASK = 0x1FF +}; + +typedef struct +{ + GB_FUSION2_LED_IDX idx; + uint16_t leds_min; + uint16_t leds_max; + std::string name; +} gb_fusion2_zone; + +typedef const gb_fusion2_zone* gb_fusion2_layout[GB_FUSION2_ZONES_MAX]; + +typedef struct +{ + gb_fusion2_layout* zones; + uint32_t layout_id; + uint8_t device_num; + std::string name; +} gb_fusion2_device; + +/*---------------------------------------------------------------------*\ +| These constant values are defined in GigabyteFusion2USB_Devices.cpp | +\*---------------------------------------------------------------------*/ +extern const unsigned int GB_FUSION2_DEVICE_COUNT; +extern const gb_fusion2_device** gb_fusion2_device_list; + diff --git a/Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBController.cpp b/Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBController.cpp new file mode 100644 index 0000000..4d1e549 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBController.cpp @@ -0,0 +1,815 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2USBController.cpp | +| | +| Driver for Gigabyte Aorus RGB Fusion 2 USB motherboard | +| | +| jackun 08 Jan 2020 | +| megadjc 31 Jul 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "GigabyteRGBFusion2USBController.h" + +/*-------------------------------------------------------------------------*\ +| Low level RGB value conversion table | +| This is stored as a uint32_t in the chip so is trasmitted LSB to MSB | +| Therefore the numbers represent the index where the controller will find | +| respective colour in a regular packet | +\*-------------------------------------------------------------------------*/ +static RGBCalibration GigabyteCalibrationsLookup +{ + { "BGR", {{{0x00, 0x01, 0x02, 0x00}}}}, + { "BRG", {{{0x01, 0x00, 0x02, 0x00}}}}, + { "GRB", {{{0x02, 0x00, 0x01, 0x00}}}}, + { "GBR", {{{0x00, 0x02, 0x01, 0x00}}}}, + { "RGB", {{{0x02, 0x01, 0x00, 0x00}}}}, + { "RBG", {{{0x01, 0x02, 0x00, 0x00}}}} +}; + +/*---------------------------------------------------------*\ +| Converts LED counts to divisions in hardware | +\*---------------------------------------------------------*/ +static LEDCount LedCountToEnum(unsigned int c) +{ + if(c <= 32) + { + return(LEDS_32); + } + else if(c <= 64) + { + return(LEDS_64); + } + else if(c <= 256) + { + return(LEDS_256); + } + else if(c <= 512) + { + return(LEDS_512); + } + else + { + return(LEDS_1024); + } +} + +RGBFusion2USBController::RGBFusion2USBController(hid_device* handle, const char* path, std::string mb_name, uint16_t pid): dev(handle), product_id(pid) +{ + name = mb_name; + location = path; + + if(!RefreshHardwareInfo()) + { + return; + } + if(report.support_cmd_flag >= 0x02) + { + EnableLampArray(false); + } + ResetController(); + EnableBeat(false); +} + +RGBFusion2USBController::~RGBFusion2USBController() +{ + hid_close(dev); +} + +/*---------------------------------------------------------*\ +| Read configuration data from hardware. | +| Returns false if read fails. | +\*---------------------------------------------------------*/ +bool RGBFusion2USBController::RefreshHardwareInfo() +{ + unsigned char buffer[FUSION2_USB_BUFFER_SIZE] = {0}; + + SendCCReport(0x60, 0x00); + buffer[0] = report_id; + int res = hid_get_feature_report(dev, buffer, sizeof(buffer)); + + if(res < static_cast(sizeof(IT8297Report))) + { + report_loaded = false; + return false; + } + + std::memcpy(&report, buffer, sizeof(IT8297Report)); + report_loaded = true; + device_num = report.device_num; + description = std::string(report.str_product, 28); + if(std::string::iterator nul = std::find(description.begin(), description.end(), '\0'); + nul != description.end()) + { + description.erase(nul, description.end()); + } + + { + char text[16]{}; + + std::snprintf( + text, + sizeof(text), + "%u.%u.%u.%u", + (report.fw_ver ) & 0xFF, + (report.fw_ver >> 8) & 0xFF, + (report.fw_ver >> 16) & 0xFF, + (report.fw_ver >> 24) & 0xFF + ); + version = text; + std::snprintf(text, sizeof(text), "0x%08X", report.chip_id); + chip_id = text; + } + + D_LED1_count = LedCountToEnum(report.curr_led_count_low & 0x0F); + D_LED2_count = LedCountToEnum((report.curr_led_count_low >> 4) & 0x0F); + D_LED3_count = LedCountToEnum(report.curr_led_count_high & 0x0F); + D_LED4_count = LedCountToEnum((report.curr_led_count_high >> 4) & 0x0F); + + cal_data.dled[0] = report.cal_strip0; + cal_data.dled[1] = report.cal_strip1; + cal_data.mainboard = report.rgb_cali; + cal_data.spare[0] = report.cal_spare0; + cal_data.spare[1] = report.cal_spare1; + + cali_loaded = false; + if(product_id == 0x5711) + { + unsigned char buffer2[FUSION2_USB_BUFFER_SIZE] = {0}; + SendCCReport(0x61, 0x00); + buffer2[0] = report_id; + int res2 = hid_get_feature_report(dev, buffer2, sizeof(buffer2)); + + if(res2 >= static_cast(sizeof(IT5711Calibration))) + { + IT5711Calibration cali; + + std::memcpy(&cali, buffer2, sizeof(IT5711Calibration)); + cali_loaded = true; + + cal_data.dled[2] = cali.cal_strip2; + cal_data.dled[3] = cali.cal_strip3; + cal_data.spare[2] = cali.cal_spare2; + cal_data.spare[3] = cali.cal_spare3; + } + else + { + cal_data.dled[2] = 0; + cal_data.dled[3] = 0; + cal_data.spare[2] = 0; + cal_data.spare[3] = 0; + cali_loaded = false; + } + } + else + { + cal_data.dled[2] = 0; + cal_data.dled[3] = 0; + cal_data.spare[2] = 0; + cal_data.spare[3] = 0; + } + + return report_loaded; +} + +std::string RGBFusion2USBController::DecodeCalibrationBuffer(uint32_t value) const +{ + std::string out = "OFF"; + if(value == 0) + { + return out; + } + + uint8_t bo_b = value & 0xFF; + uint8_t bo_g = (value >> 8 ) & 0xFF; + uint8_t bo_r = (value >> 16) & 0xFF; + + bool in_range = (bo_r < 3 && bo_g < 3 && bo_b < 3); + bool distinct = (bo_r != bo_g && bo_r != bo_b && bo_g != bo_b); + + if(in_range && distinct) + { + out[bo_r] = 'R'; + out[bo_g] = 'G'; + out[bo_b] = 'B'; + return out; + } + + return "BAD"; +} + +uint32_t RGBFusion2USBController::EncodeCalibrationBuffer(const std::string& rgb_order) +{ + if(rgb_order.empty()) + { + return 0u; + } + + std::string key = rgb_order; + std::transform(key.begin(), key.end(), key.begin(), + [](unsigned char c){ return char(std::toupper(c)); }); + + if(key=="OFF" || key=="0") + { + return 0u; + } + + RGBCalibration::const_iterator it = GigabyteCalibrationsLookup.find(key); + if(it == GigabyteCalibrationsLookup.end()) + { + return 0u; + } + + const RGBA &rgb_cal = it->second; + return (uint32_t(rgb_cal.raw[0])) + | (uint32_t(rgb_cal.raw[1]) << 8) + | (uint32_t(rgb_cal.raw[2]) << 16) + | (uint32_t(rgb_cal.raw[3]) << 24); +} + + +EncodedCalibration RGBFusion2USBController::GetCalibration(bool refresh_from_hw) +{ + if(refresh_from_hw || !report_loaded || (product_id == 0x5711 && !cali_loaded)) + { + if(!RefreshHardwareInfo()) + { + return EncodedCalibration{}; + } + } + + EncodedCalibration out{}; + out.dled[0] = DecodeCalibrationBuffer(cal_data.dled[0]); + out.dled[1] = DecodeCalibrationBuffer(cal_data.dled[1]); + out.spare[0] = DecodeCalibrationBuffer(cal_data.spare[0]); + out.spare[1] = DecodeCalibrationBuffer(cal_data.spare[1]); + out.mainboard = DecodeCalibrationBuffer(cal_data.mainboard); + + if(product_id == 0x5711) + { + out.dled[2] = DecodeCalibrationBuffer(cal_data.dled[2]); + out.dled[3] = DecodeCalibrationBuffer(cal_data.dled[3]); + out.spare[2] = DecodeCalibrationBuffer(cal_data.spare[2]); + out.spare[3] = DecodeCalibrationBuffer(cal_data.spare[3]); + } + else + { + out.dled[2] = "OFF"; + out.dled[3] = "OFF"; + out.spare[2] = "OFF"; + out.spare[3] = "OFF"; + } + + return out; +} + +bool RGBFusion2USBController::SetCalibration(const EncodedCalibration& cal, bool refresh_from_hw) +{ + if(refresh_from_hw && !RefreshHardwareInfo()) + { + return false; + } + + if(EncodeCalibrationBuffer(cal.dled[0]) == cal_data.dled[0] + && EncodeCalibrationBuffer(cal.dled[1]) == cal_data.dled[1] + && EncodeCalibrationBuffer(cal.mainboard) == cal_data.mainboard + && EncodeCalibrationBuffer(cal.spare[0]) == cal_data.spare[0] + && EncodeCalibrationBuffer(cal.spare[1]) == cal_data.spare[1] + && (product_id != 0x5711 + || (EncodeCalibrationBuffer(cal.dled[2]) == cal_data.dled[2] + && EncodeCalibrationBuffer(cal.dled[3]) == cal_data.dled[3] + && EncodeCalibrationBuffer(cal.spare[2]) == cal_data.spare[2] + && EncodeCalibrationBuffer(cal.spare[3]) == cal_data.spare[3]))) + { + return true; + } + + CMD_0x33 desired; + + desired.c.d_strip_c0 = EncodeCalibrationBuffer(cal.dled[0]); + desired.c.d_strip_c1 = EncodeCalibrationBuffer(cal.dled[1]); + desired.c.rgb_cali = EncodeCalibrationBuffer(cal.mainboard); + desired.c.c_spare0 = EncodeCalibrationBuffer(cal.spare[0]); + desired.c.c_spare1 = EncodeCalibrationBuffer(cal.spare[1]); + + if(product_id == 0x5711) + { + desired.c.d_strip_c2 = EncodeCalibrationBuffer(cal.dled[2]); + desired.c.d_strip_c3 = EncodeCalibrationBuffer(cal.dled[3]); + desired.c.c_spare2 = EncodeCalibrationBuffer(cal.spare[2]); + desired.c.c_spare3 = EncodeCalibrationBuffer(cal.spare[3]); + } + + int rc = SendPacket(desired.buffer); + if(rc < 0) + { + return false; + } + + ResetController(); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + SaveCalState(); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + + cal_data.dled[0] = desired.c.d_strip_c0; + cal_data.dled[1] = desired.c.d_strip_c1; + cal_data.mainboard = desired.c.rgb_cali; + cal_data.spare[0] = desired.c.c_spare0; + cal_data.spare[1] = desired.c.c_spare1; + + if(product_id == 0x5711) + { + cal_data.dled[2] = desired.c.d_strip_c2; + cal_data.dled[3] = desired.c.d_strip_c3; + cal_data.spare[2] = desired.c.c_spare2; + cal_data.spare[3] = desired.c.c_spare3; + } + else + { + cal_data.dled[2] = 0u; + cal_data.dled[3] = 0u; + cal_data.spare[2] = 0u; + cal_data.spare[3] = 0u; + } + + return true; +} + +void RGBFusion2USBController::SetLedCount(unsigned int c0, unsigned int c1, unsigned int c2, unsigned int c3) +{ + LEDCount new_d1 = LedCountToEnum(c0); + LEDCount new_d2 = LedCountToEnum(c1); + LEDCount new_d3 = LedCountToEnum(c2); + LEDCount new_d4 = LedCountToEnum(c3); + + if(new_d1 == D_LED1_count && new_d2 == D_LED2_count && new_d3 == D_LED3_count && new_d4 == D_LED4_count) + { + return; + } + + D_LED1_count = new_d1; + D_LED2_count = new_d2; + D_LED3_count = new_d3; + D_LED4_count = new_d4; + + SendCCReport(0x34, (new_d2 << 4) | new_d1, (new_d4 << 4) | new_d3); +} + +/*---------------------------------------------------------*\ +| Switch ARGB header mode (single/addressable) | +\*---------------------------------------------------------*/ +bool RGBFusion2USBController::SetStripBuiltinEffectState(int hdr, bool enable) +{ + int bitmask = 0; + + if(hdr == -1) + { + bitmask = 0x01 | 0x02 | 0x08 | 0x10; + } + else + { + switch(hdr) + { + case LED4: + case HDR_D_LED2: + case HDR_D_LED2_ARGB: + bitmask = 0x02; + break; + case HDR_D_LED3: + case HDR_D_LED3_ARGB: + bitmask = 0x08; + break; + case HDR_D_LED4: + case HDR_D_LED4_ARGB: + bitmask = 0x10; + break; + default: + bitmask = 0x01; + break; + } + } + + int base_mask = (effect_disabled < 0) ? 0 : effect_disabled; + int new_effect_disabled = enable + ? (base_mask & ~bitmask) + : (base_mask | bitmask); + + // Skip redundant writes only after we have synchronized at least once + if(effect_disabled >= 0 && new_effect_disabled == effect_disabled) + { + return true; + } + effect_disabled = new_effect_disabled; + int res = SendCCReport(0x32, effect_disabled); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + return res; +} + +/*---------------------------------------------------------*\ +| Persist LED config data | +\*---------------------------------------------------------*/ +bool RGBFusion2USBController::SaveLEDState(bool e) +{ + return SendCCReport(0x47, e ? 1 : 0); +} + +/*---------------------------------------------------------*\ +| Persist calibration | +\*---------------------------------------------------------*/ +bool RGBFusion2USBController::SaveCalState() +{ + return SendCCReport(0x5E, 0); +} + +/*---------------------------------------------------------*\ +| Set beat mode (hardware audio sync mode) | +\*---------------------------------------------------------*/ +bool RGBFusion2USBController::EnableBeat(bool e) +{ + return SendCCReport(0x31, e ? 1 : 0); +} + +/*---------------------------------------------------------*\ +| Set Lamp Array mode (MSDL) | +\*---------------------------------------------------------*/ +bool RGBFusion2USBController::EnableLampArray(bool enable) +{ + return SendCCReport(0x48, enable ? 1 : 0); +} + +std::string RGBFusion2USBController::GetDeviceName() +{ + return(name); +} + +std::string RGBFusion2USBController::GetDeviceDescription() +{ + return(description); +} + +std::string RGBFusion2USBController::GetFWVersion() +{ + return(version); +} + +std::string RGBFusion2USBController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RGBFusion2USBController::GetSerial() +{ + return(chip_id); +} + +/*---------------------------------------------------------*\ +| PID (controller feature support) | +\*---------------------------------------------------------*/ +uint16_t RGBFusion2USBController::GetProductID() +{ + return(product_id); +} + +/*---------------------------------------------------------*\ +| Low level controller number (multi-controller) | +\*---------------------------------------------------------*/ +uint8_t RGBFusion2USBController::GetDeviceNum() +{ + return(device_num); +} + +/*---------------------------------------------------------*\ +| Set ARGB strips (addressable) | +\*---------------------------------------------------------*/ +void RGBFusion2USBController::SetStripColors(unsigned int hdr, RGBColor* colors, unsigned int num_colors, int single_led) +{ + PktRGB pkt; + pkt.Init(hdr, report_id); + uint32_t byteorder; + + switch(pkt.s.header) + { + case HDR_D_LED2_ARGB: + byteorder = cal_data.dled[1]; + break; + case HDR_D_LED3_ARGB: + byteorder = cal_data.dled[2]; + break; + case HDR_D_LED4_ARGB: + byteorder = cal_data.dled[3]; + break; + default: + byteorder = cal_data.dled[0]; + break; + } + + unsigned char bo_r = byteorder >> 16; + unsigned char bo_g = byteorder >> 8; + unsigned char bo_b = byteorder & 0xFF; + + int res; + int leds_left = num_colors; + int sent_data = 0; + int k = 0; + int leds_in_pkt = sizeof(pkt.s.leds) / sizeof(*pkt.s.leds); /* 19 */ + + if(single_led > -1) + { + leds_left = 1; + k = single_led; + sent_data = k * 3; + leds_in_pkt = 1; + } + + while(leds_left > 0) + { + leds_in_pkt = (std::min)(leds_in_pkt, leds_left); + leds_left -= leds_in_pkt; + + pkt.s.bcount = leds_in_pkt * 3; + pkt.s.boffset = sent_data; + sent_data += pkt.s.bcount; + + for(int i = 0; i < leds_in_pkt; i++) + { + RGBColor color = colors[k]; + uint8_t offset = (i * 3) + 5; + + pkt.buffer[offset + bo_r] = RGBGetRValue(color); + pkt.buffer[offset + bo_g] = RGBGetGValue(color); + pkt.buffer[offset + bo_b] = RGBGetBValue(color); + k++; + } + + res = SendPacket(pkt.buffer); + + if(res < 0) + { + return; + } + } +} + +/*---------------------------------------------------------*\ +| Set hardware effects (single) | +| Note: Effects paramters match that of gigabyte software. | +| -(2)Gigabyte breathe ranges are 400-1000ms in 100ms steps | +| and 1000-1600ms in 200ms steps | +| -(3)Gigabyte flash ranges are 600-2400ms in 200ms steps | +| -(4)Gigabyte color cycle ranges are 300-2400ms for period0| +| and 100-2200ms for period1 in 100ms steps. | +| the follow this trend between 300-1100/100-1000ms | +| then jump to 2400ms and 2200ms respective on speed 9. | +| -(6)Gigabyte Wave ranges are 30-300ms in steps following | +| the following formula. 2.5(s+1)^2 + 2.5(s+1) + 25. | +| -(15)Gigabyte dflash ranges are 800-2600ms in 200ms steps | +| -(3)(15)flash and dflash parameters were combined. | +\*---------------------------------------------------------*/ +void RGBFusion2USBController::SetLEDEffect(int led, int mode, unsigned int speed, unsigned char brightness, bool random, uint32_t* color) +{ + PktEffect pkt; + pkt.Init(led, report_id, product_id); + if(led == -1) + { + effect_zone_mask = pkt.e.zone0; + } + else if((effect_zone_mask & pkt.e.zone0) == 0) + { + effect_zone_mask |= pkt.e.zone0; + } + pkt.e.max_brightness = brightness; + pkt.e.effect_type = mode; + pkt.e.effect_param0 = random ? 7 : 0; + pkt.e.color0 = RGBToBGRColor(*color); + + switch(mode) + { + case EFFECT_PULSE: + pkt.e.period0 = (speed <= 6) ? (400 + speed * 100) : (1000 + (speed - 6) * 200); + pkt.e.period1 = pkt.e.period0; + pkt.e.period2 = 200; + break; + case EFFECT_DFLASH: + pkt.e.effect_type = 3; + pkt.e.effect_param1 = 1; + pkt.e.effect_param2 = 2; + case EFFECT_BLINKING: + pkt.e.period0 = 100; + pkt.e.period1 = 100; + pkt.e.period2 = (speed * 200) + 700; + break; + case EFFECT_COLORCYCLE: + pkt.e.period0 = (speed * 100 + 300) + (speed > 8 ? 1300 * (speed - 8) : 0); + pkt.e.period1 = pkt.e.period0 -200; + pkt.e.effect_param0 = 7; + break; + case EFFECT_WAVE: + pkt.e.period0 = (((speed + 1)^2) + (speed + 1) + 10) * 5 / 2; + pkt.e.effect_param0 = 7; + pkt.e.effect_param1 = 1; + break; + case EFFECT_RANDOM: + pkt.e.period0 = 100; + pkt.e.effect_param0 = 1; + pkt.e.effect_param1 = 5; + break; + case EFFECT_WAVE1: + pkt.e.period0 = 1200; + pkt.e.period1 = 100; + pkt.e.period2 = 360; + pkt.e.period3 = 1200; + break; + case EFFECT_WAVE2: + case EFFECT_WAVE4: + pkt.e.period0 = 200; + pkt.e.effect_param0 = 7; + break; + case EFFECT_WAVE3: + pkt.e.period0 = 840; + pkt.e.period1 = 20; + pkt.e.period2 = 200; + pkt.e.period3 = 840; + break; + } + SendPacket(pkt.buffer); +} + +/*---------------------------------------------------------*\ +| Apply hardware effects (single) | +\*---------------------------------------------------------*/ +bool RGBFusion2USBController::ApplyEffect(bool fast_apply) +{ + if(fast_apply) + { + if(product_id == 0x5711) + { + return SendCCReport(0x28, 0xFF, 0x07); + } + else + { + return SendCCReport(0x28, 0xFF, 0x00); + } + } + + PktEffectApply pkt = {}; + pkt.a.zone_sel0 = effect_zone_mask; + + effect_zone_mask = 0; + return SendPacket(pkt.buffer); +} + +bool RGBFusion2USBController::SendCCReport(uint8_t a, uint8_t b, uint8_t c) +{ + return(SendReport(report_id, a, b, c)); +} + +bool RGBFusion2USBController::SendReport(uint8_t id, uint8_t a, uint8_t b, uint8_t c) +{ + unsigned char buffer[FUSION2_USB_BUFFER_SIZE] {}; + std::memset(buffer, 0, FUSION2_USB_BUFFER_SIZE); + + buffer[0] = id; + buffer[1] = a; + buffer[2] = b; + buffer[3] = c; + + return(SendPacket(buffer) == FUSION2_USB_BUFFER_SIZE); +} + +int RGBFusion2USBController::SendPacket(unsigned char* packet) +{ + return hid_send_feature_report(dev, packet, FUSION2_USB_BUFFER_SIZE); +} + +/*---------------------------------------------------------*\ +| Reset controller parameters | +\*---------------------------------------------------------*/ +void RGBFusion2USBController::ResetController() +{ + for(uint8_t reg = 0x20; reg <= 0x27; ++reg) + { + SendCCReport(reg, 0x00, 0x00); + } + + if(product_id == 0x5711) + { + for(uint8_t reg = 0x90; reg <= 0x92; ++reg) + { + SendCCReport(reg, 0x00, 0x00); + } + } + ApplyEffect(true); +} + +/*---------------------------------------------------------*\ +| Check controller for gen2 ARGB support | +| Checks for supported device number | +| Then checks for supported controllers | +| Then checks for supported feature bit (SaveLEDState) | +| Finally checks for strip detection value. | +\*---------------------------------------------------------*/ +bool RGBFusion2USBController::SupportsGen2() const +{ + bool supports_gen2 = false; + + supports_gen2 = (device_num == 0x00) + && (product_id == 0x5702 || product_id==0x5711 || product_id==0x8950) + && (report.support_cmd_flag & 0x01) + && (report.strip_detect == 0x01); + + return supports_gen2; +} + +std::vector RGBFusion2USBController::ExportGen2Strips() const +{ + size_t count = (product_id == 0x5711) ? 4u : 2u; + std::vector out; + out.reserve(count); + for(size_t i = 0; i < count; ++i) + { + out.push_back(g2_strip_info[i]); + } + return out; +} + +/*---------------------------------------------------------*\ +| Scan Headers for Gen2 Devices | +\*---------------------------------------------------------*/ +bool RGBFusion2USBController::ScanGen2Strips() +{ + for(unsigned i = 0; i < 4; ++i) + { + if(g2_strip_info[i].LedsOfStrip.capacity() < 15) + { + g2_strip_info[i].LedsOfStrip.reserve(15); + } + g2_strip_info[i].numStrip = 0; + g2_strip_info[i].totalLeds = 0; + g2_strip_info[i].LedsOfStrip.resize(0); + } + + const unsigned int hdr_lim = (product_id == 0x5711) ? 4u : 2u; + + for(unsigned int slot = 0; slot < hdr_lim; ++slot) + { + static constexpr uint8_t delta[4] = {4, 5, 0, 1}; + uint8_t scan_cmd = static_cast(GEN2_LED_BASE_SCAN + delta[slot]); + uint8_t info_cmd = static_cast(scan_cmd + 2); + + if(!SendCCReport(scan_cmd, 0x00, 0x00)) + { + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(700)); + + if(!SendCCReport(info_cmd, 0x00, 0x00)) + { + return false; + } + + unsigned char feature_buf[64] = {0}; + feature_buf[0] = report_id; + int recv_len = hid_get_feature_report(dev, feature_buf, sizeof(feature_buf)); + if(recv_len < 64) + { + return false; + } + + int seg_count = static_cast(feature_buf[1]); + if(seg_count < 0) + { + seg_count = 0; + } + if(seg_count > 15) + { + seg_count = 15; + } + Gen2StripInfo& dst = g2_strip_info[slot]; + dst.numStrip = static_cast(seg_count); + dst.LedsOfStrip.resize(static_cast(seg_count)); + + uint32_t total_leds = 0; + const int counts_base = 2; + + for(int k = 0; k < seg_count; ++k) + { + const int off = counts_base + (k * 2); + uint16_t lo = static_cast(feature_buf[off + 0]); + uint16_t hi = static_cast(feature_buf[off + 1]); + uint16_t cnt = static_cast(lo | (hi << 8)); + + dst.LedsOfStrip[static_cast(k)] = cnt; + total_leds += cnt; + } + + dst.totalLeds = total_leds; + + SetLedCount(0, 0, 0, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + SaveLEDState(false); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + + return true; +} diff --git a/Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBController.h b/Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBController.h new file mode 100644 index 0000000..bef1cc9 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBController.h @@ -0,0 +1,386 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2USBController.h | +| | +| Driver for Gigabyte Aorus RGB Fusion 2 USB motherboard | +| | +| jackun 08 Jan 2020 | +| megadjc 31 Jul 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBController.h" +#include "GigabyteFusion2USB_Devices.h" + +#define FUSION2_USB_BUFFER_SIZE 64 + +/*--------------------------------------------------------*\ +| Gen2 scan/info opcode base | +\*--------------------------------------------------------*/ +#define GEN2_LED_BASE_SCAN 0x38 + +/*---------------------------------------------------------*\ +| Effects mode list | +\*---------------------------------------------------------*/ +enum EffectType +{ + EFFECT_NONE = 0, + EFFECT_STATIC = 1, + EFFECT_PULSE = 2, + EFFECT_BLINKING = 3, + EFFECT_COLORCYCLE = 4, + EFFECT_WAVE = 6, + EFFECT_RANDOM = 8, + EFFECT_WAVE1 = 9, + EFFECT_WAVE2 = 10, + EFFECT_WAVE3 = 11, + EFFECT_WAVE4 = 12, + EFFECT_DFLASH = 15, + // to be continued... +}; + +/*---------------------------------------------------------*\ +| Low level strip length divisions | +\*---------------------------------------------------------*/ +enum LEDCount +{ + LEDS_32 = 0, + LEDS_64, + LEDS_256, + LEDS_512, + LEDS_1024, +}; + +/*---------------------------------------------------------*\ +| Defines the RGB led data structure. | +\*---------------------------------------------------------*/ +struct LEDs +{ + uint8_t r; + uint8_t g; + uint8_t b; +}; + +/*---------------------------------------------------------*\ +| Defines structure for low level calibration data. | +\*---------------------------------------------------------*/ +struct CalibrationData +{ + uint32_t dled[4] = {0, 0, 0, 0}; + uint32_t spare[4] = {0, 0, 0, 0}; + uint32_t mainboard = 0; +}; + +/*---------------------------------------------------------*\ +| Defines structure for high level calibration data. | +\*---------------------------------------------------------*/ +struct EncodedCalibration +{ + std::string dled[4]; + std::string spare[4]; + std::string mainboard; +}; + +/*---------------------------------------------------------*\ +| High level struct to contain Gen2 Header Data | +\*---------------------------------------------------------*/ +struct Gen2StripInfo +{ + uint8_t numStrip = 0; + std::vector LedsOfStrip; + uint32_t totalLeds = 0; +}; + +#pragma pack(push, 1) + +/*---------------------------------------------------------*\ +| Packet structure for applying effects | +\*---------------------------------------------------------*/ +union PktEffectApply +{ + unsigned char buffer[FUSION2_USB_BUFFER_SIZE]; + struct apply_data + { + uint8_t report_id = 0xCC; + uint8_t command_id = 0x28; + uint32_t zone_sel0 = 0; + uint32_t zone_sel1 = 0; + uint8_t padding[54]; + } a; + + PktEffectApply() : a {} + { + std::memset(a.padding, 0, sizeof(a.padding)); + } +}; + +/*---------------------------------------------------------*\ +| Single LED Calibration struct | +\*---------------------------------------------------------*/ +struct RGBA +{ + union + { + uint8_t raw[4]; + struct + { + uint8_t blue; + uint8_t green; + uint8_t red; + uint8_t alpha; + }; + }; +}; + +typedef std::map< std::string, RGBA > RGBCalibration; +typedef std::map< std::string, std::string> calibration; + +/*---------------------------------------------------------*\ +| Packet structure for ARGB headers (addressable) | +\*---------------------------------------------------------*/ +union PktRGB +{ + unsigned char buffer[FUSION2_USB_BUFFER_SIZE]; + struct RGBData + { + uint8_t report_id; + uint8_t header; + uint16_t boffset; + uint8_t bcount; + LEDs leds[19]; + uint16_t padding0; + } s; + + PktRGB() : s {} + { + } + + void Init(uint8_t header, uint8_t report_id) + { + switch(header) + { + case LED4: + case HDR_D_LED2: + header = HDR_D_LED2_ARGB; + break; + case HDR_D_LED3: + header = HDR_D_LED3_ARGB; + break; + case HDR_D_LED4: + header = HDR_D_LED4_ARGB; + break; + default: + header = HDR_D_LED1_ARGB; + break; + } + s.report_id = report_id; + s.header = header; + s.boffset = 0; + s.bcount = 0; + memset(s.leds, 0, sizeof(s.leds)); + } +}; + +/*---------------------------------------------------------*\ +| Packet structure for hardware effects | +| Default values for Hardware Effects mode. | +| Old init values. | +| (All values 0 unless otherwise noted below) | +| e.color0 = 0x00FF2100; //orange | +| e.period1 = 1200; | +| e.period2 = 200; | +| e.period3 = 200; | +| e.effect_param2 = 1; | +\*---------------------------------------------------------*/ +union PktEffect +{ + unsigned char buffer[FUSION2_USB_BUFFER_SIZE]; + struct Effect + { + uint8_t report_id = 0; + uint8_t header = 0; + uint32_t zone0 = 0; // RGB Fusion sets it to pow(2, led) + uint32_t zone1 = 0; + uint8_t reserved0 = 0; + uint8_t effect_type = EFFECT_STATIC; + uint8_t max_brightness = 255; + uint8_t min_brightness = 0; + uint32_t color0 = 0; + uint32_t color1 = 0; + uint16_t period0 = 0; // Fade in - Rising Timer - Needs to be 0 for "Direct" + uint16_t period1 = 0; // Fade out + uint16_t period2 = 0; // Hold + uint16_t period3 = 0; + uint8_t effect_param0 = 0; // ex color count to cycle through (max seems to be 7) + uint8_t effect_param1 = 0; + uint8_t effect_param2 = 0; // ex flash repeat count + uint8_t effect_param3 = 0; + uint8_t padding0[30]; + } e; + + PktEffect() : e {} + { + } + + void Init(int led, uint8_t report_id, uint16_t pid) + { + memset(buffer, 0, sizeof(buffer)); + + e.report_id = report_id; + if(led == -1) + { + e.zone0 = (pid == 0x5711) ? 0x07FF : 0xFF; + e.header = 0x20; + } + else if(led < 8) + { + e.zone0 = 1U << led; + e.header = 0x20 + led; + } + else if(led < 11) + { + e.zone0 = 1U << led; + e.header = 0x90 + (led - 8); + } + else + { + e.zone0 = 0; + e.header = 0; + } + } +}; + +/*---------------------------------------------------------*\ +| Basic Controller Init Struct | +\*---------------------------------------------------------*/ +struct IT8297Report +{ + uint8_t report_id; + uint8_t product; + uint8_t device_num; + uint8_t strip_detect; + uint32_t fw_ver; + uint8_t curr_led_count_high; + uint8_t curr_led_count_low; + uint8_t strip_ctrl_length1; + uint8_t support_cmd_flag; + char str_product[28]; + uint32_t cal_spare0; + uint32_t cal_strip0; + uint32_t cal_strip1; + uint32_t rgb_cali; + uint32_t chip_id; + uint32_t cal_spare1; +}; + +/*---------------------------------------------------------*\ +| CC61 Calibration Struct (For IT5711) | +\*---------------------------------------------------------*/ +struct IT5711Calibration +{ + uint8_t report_id; + uint8_t reserved[3]; + uint32_t cal_strip2; + uint32_t cal_strip3; + uint32_t cal_spare2; + uint32_t cal_spare3; + uint8_t padding[44]; +}; + +/*---------------------------------------------------------*\ +| CC33 Set Calibration Struct | +\*---------------------------------------------------------*/ +union CMD_0x33 +{ + unsigned char buffer[FUSION2_USB_BUFFER_SIZE]; + struct Calibration + { + uint8_t report_id = 0xCC; + uint8_t command_id = 0x33; + uint32_t d_strip_c0 = 0; + uint32_t d_strip_c1 = 0; + uint32_t rgb_cali = 0; + uint32_t c_spare0 = 0; + uint32_t c_spare1 = 0; + uint32_t d_strip_c2 = 0; + uint32_t d_strip_c3 = 0; + uint32_t c_spare2 = 0; + uint32_t c_spare3 = 0; + uint8_t reserved[25]; + } c; + + CMD_0x33() : c{} + { + memset(c.reserved, 0, sizeof(c.reserved)); + } +}; + +#pragma pack(pop) + +class RGBFusion2USBController +{ +public: + RGBFusion2USBController(hid_device* handle, const char *path, std::string mb_name, uint16_t pid); + ~RGBFusion2USBController(); + + bool ApplyEffect(bool batch_commit = false); + bool SetCalibration(const EncodedCalibration& cal, bool refresh_from_hw); + void SetLedCount(unsigned int c0, unsigned int c1, unsigned int c2, unsigned int c3); + void SetLEDEffect(int led, int mode, unsigned int speed, unsigned char brightness, bool random, uint32_t* color); + bool SetStripBuiltinEffectState(int hdr, bool enable); + void SetStripColors(unsigned int hdr, RGBColor * colors, unsigned int num_colors, int single_led = -1); + bool SupportsGen2() const; + bool ScanGen2Strips(); + + EncodedCalibration GetCalibration(bool refresh_from_hw = false); + std::string GetDeviceName(); + uint8_t GetDeviceNum(); + std::string GetDeviceDescription(); + std::string GetDeviceLocation(); + std::string GetFWVersion(); + uint16_t GetProductID(); + std::string GetSerial(); + std::vector ExportGen2Strips() const; + Gen2StripInfo g2_strip_info[4]; + +private: + std::string DecodeCalibrationBuffer(uint32_t value) const; + bool EnableLampArray(bool enable); + bool EnableBeat(bool enable); + uint32_t EncodeCalibrationBuffer(const std::string& rgb_order); + bool RefreshHardwareInfo(); + void ResetController(); + bool SaveLEDState(bool enable); + bool SaveCalState(); + bool SendCCReport(uint8_t a, uint8_t b, uint8_t c = 0); + bool SendReport(uint8_t id, uint8_t a, uint8_t b, uint8_t c = 0); + int SendPacket(unsigned char* packet); + + hid_device* dev; + uint8_t device_num; + uint16_t product_id; + uint32_t effect_zone_mask = 0; + int mode; + IT8297Report report; + CalibrationData cal_data; + std::string name; + std::string description; + std::string location; + std::string version; + std::string chip_id; + int effect_disabled = -1; + int report_id = 0xCC; + bool report_loaded = false; + bool cali_loaded = false; + LEDCount D_LED1_count; + LEDCount D_LED2_count; + LEDCount D_LED3_count; + LEDCount D_LED4_count; +}; diff --git a/Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBControllerDetect.cpp b/Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBControllerDetect.cpp new file mode 100644 index 0000000..58eabc2 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBControllerDetect.cpp @@ -0,0 +1,54 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusion2USBControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus RGB Fusion 2 USB | +| motherboard | +| | +| jackun 08 Jan 2020 | +| megadjc 31 Jul 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "GigabyteRGBFusion2USBController.h" +#include "RGBController_GigabyteRGBFusion2USB.h" +#include "dmiinfo.h" + +#define DETECTOR_NAME "Gigabyte RGB Fusion 2 USB" + +#define IT8297_VID 0x048D +#define IT8297_IFC 0 +#define IT8297_U 0xCC +#define IT8297_UPG 0xFF89 + +/*---------------------------------------------------------*\ +| Detector for Gigabyte RGB Fusion USB controllers | +\*---------------------------------------------------------*/ +void DetectGigabyteRGBFusion2USBControllers(hid_device_info* info, const std::string&) +{ + DMIInfo MB_info; + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RGBFusion2USBController* controller = new RGBFusion2USBController(dev, info->path, MB_info.getMainboard(), info->product_id); + RGBController_RGBFusion2USB* rgb_controller = new RGBController_RGBFusion2USB(controller, DETECTOR_NAME); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +#ifdef USE_HID_USAGE +REGISTER_HID_DETECTOR_PU(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x8297, IT8297_UPG, IT8297_U); +REGISTER_HID_DETECTOR_PU(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x8950, IT8297_UPG, IT8297_U); +REGISTER_HID_DETECTOR_PU(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x5702, IT8297_UPG, IT8297_U); +REGISTER_HID_DETECTOR_PU(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x5711, IT8297_UPG, IT8297_U); +#else +REGISTER_HID_DETECTOR_I(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x8297, IT8297_IFC); +REGISTER_HID_DETECTOR_I(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x8950, IT8297_IFC); +REGISTER_HID_DETECTOR_I(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x5702, IT8297_IFC); +REGISTER_HID_DETECTOR_I(DETECTOR_NAME, DetectGigabyteRGBFusion2USBControllers, IT8297_VID, 0x5711, IT8297_IFC); +#endif + diff --git a/Controllers/GigabyteRGBFusion2USBController/RGBController_GigabyteRGBFusion2USB.cpp b/Controllers/GigabyteRGBFusion2USBController/RGBController_GigabyteRGBFusion2USB.cpp new file mode 100644 index 0000000..cfbaab4 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2USBController/RGBController_GigabyteRGBFusion2USB.cpp @@ -0,0 +1,980 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusion2USB.cpp | +| | +| RGBController for Gigabyte Aorus RGB Fusion 2 USB | +| motherboard | +| | +| jackun 08 Jan 2020 | +| megadjc 31 Jul 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "GigabyteFusion2USB_Devices.h" +#include "LogManager.h" +#include "RGBController_GigabyteRGBFusion2USB.h" +#include "ResourceManager.h" + +/**------------------------------------------------------------------*\ + @name Gigabyte RGB Fusion 2 USB + @category Motherboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGigabyteRGBFusion2USBControllers + @comment The Fusion 2 USB controller applies to most AMD and + Intel mainboards from the X570 and z390 chipsets onwards. +\*-------------------------------------------------------------------*/ + +RGBController_RGBFusion2USB::RGBController_RGBFusion2USB(RGBFusion2USBController* controller_ptr, std::string detector) +{ + controller = controller_ptr; + name = controller->GetDeviceName(); + detector_name = detector; + vendor = "Gigabyte"; + type = DEVICE_TYPE_MOTHERBOARD; + description = controller->GetDeviceDescription(); + version = controller->GetFWVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + product_id = controller->GetProductID(); + device_num = controller->GetDeviceNum(); + + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Direct.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Direct.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + Direct.brightness = RGBFUSION2_BRIGHTNESS_MAX; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = EFFECT_STATIC; + Static.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Static.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + Static.brightness = RGBFUSION2_BRIGHTNESS_MAX; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = EFFECT_PULSE; + Breathing.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Breathing.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Breathing.brightness_max = 100; // Set 100 max due to controller quirks + Breathing.brightness = Breathing.brightness_max; + Breathing.speed_min = RGBFUSION2_SPEED_MIN; + Breathing.speed_max = RGBFUSION2_SPEED_MAX; + Breathing.speed = RGBFUSION2_SPEED_MID; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Blinking; + Blinking.name = "Flashing"; + Blinking.value = EFFECT_BLINKING; + Blinking.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Blinking.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Blinking.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + Blinking.brightness = RGBFUSION2_BRIGHTNESS_MAX; + Blinking.speed_min = RGBFUSION2_SPEED_MIN; + Blinking.speed_max = RGBFUSION2_SPEED_MAX; + Blinking.speed = RGBFUSION2_SPEED_MID; + Blinking.colors_min = 1; + Blinking.colors_max = 1; + Blinking.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blinking.colors.resize(1); + modes.push_back(Blinking); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = EFFECT_COLORCYCLE; + ColorCycle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + ColorCycle.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + ColorCycle.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + ColorCycle.brightness = RGBFUSION2_BRIGHTNESS_MAX; + ColorCycle.speed_min = RGBFUSION2_SPEED_MIN; + ColorCycle.speed_max = RGBFUSION2_SPEED_MAX; + ColorCycle.speed = RGBFUSION2_SPEED_MID; + ColorCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(ColorCycle); + + mode Flashing; + Flashing.name = "Double Flash"; + Flashing.value = EFFECT_DFLASH; + Flashing.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Flashing.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Flashing.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + Flashing.brightness = RGBFUSION2_BRIGHTNESS_MAX; + Flashing.speed_min = RGBFUSION2_SPEED_MIN; + Flashing.speed_max = RGBFUSION2_SPEED_MAX; + Flashing.speed = RGBFUSION2_SPEED_MID; + Flashing.colors_min = 1; + Flashing.colors_max = 1; + Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flashing.colors.resize(1); + modes.push_back(Flashing); + + mode Wave; + Wave.name = "Wave"; + Wave.value = EFFECT_WAVE; + Wave.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Wave.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Wave.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + Wave.brightness = RGBFUSION2_BRIGHTNESS_MAX; + Wave.speed_min = RGBFUSION2_SPEED_MIN; + Wave.speed_max = RGBFUSION2_SPEED_MAX; + Wave.speed = RGBFUSION2_SPEED_MID; + Wave.colors_min = 0; + Wave.colors_max = 0; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + + mode Random; + Random.name = "Random"; + Random.value = EFFECT_RANDOM; + Random.flags = MODE_FLAG_HAS_BRIGHTNESS; + Random.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Random.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + Random.brightness = RGBFUSION2_BRIGHTNESS_MAX; + Random.colors_min = 0; + Random.colors_max = 0; + Random.color_mode = MODE_COLORS_NONE; + modes.push_back(Random); + + mode Wave1; + Wave1.name = "Wave 1"; + Wave1.value = EFFECT_WAVE1; + Wave1.flags = MODE_FLAG_HAS_BRIGHTNESS; + Wave1.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Wave1.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + Wave1.brightness = RGBFUSION2_BRIGHTNESS_MAX; + Wave1.colors_min = 0; + Wave1.colors_max = 0; + Wave1.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave1); + + mode Wave2; + Wave2.name = "Wave 2"; + Wave2.value = EFFECT_WAVE2; + Wave2.flags = MODE_FLAG_HAS_BRIGHTNESS; + Wave2.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Wave2.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + Wave2.brightness = RGBFUSION2_BRIGHTNESS_MAX; + Wave2.colors_min = 0; + Wave2.colors_max = 0; + Wave2.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave2); + + mode Wave3; + Wave3.name = "Wave 3"; + Wave3.value = EFFECT_WAVE3; + Wave3.flags = MODE_FLAG_HAS_BRIGHTNESS; + Wave3.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Wave3.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + Wave3.brightness = RGBFUSION2_BRIGHTNESS_MAX; + Wave3.colors_min = 0; + Wave3.colors_max = 0; + Wave3.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave3); + + mode Wave4; + Wave4.name = "Wave 4"; + Wave4.value = EFFECT_WAVE4; + Wave4.flags = MODE_FLAG_HAS_BRIGHTNESS; + Wave4.brightness_min = RGBFUSION2_BRIGHTNESS_MIN; + Wave4.brightness_max = RGBFUSION2_BRIGHTNESS_MAX; + Wave4.brightness = RGBFUSION2_BRIGHTNESS_MAX; + Wave4.colors_min = 0; + Wave4.colors_max = 0; + Wave4.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave4); + + Init_Controller(); + SetupZones(); +} + +RGBController_RGBFusion2USB::~RGBController_RGBFusion2USB() +{ + // Free any zones we allocated for the per-instance layout + for(gb_fusion2_zone* z : allocated_zones) + { + delete z; + } + allocated_zones.clear(); + + delete controller; +} + +/*---------------------------------------------------------*\ +| Loads JSON config data | +\*---------------------------------------------------------*/ +void RGBController_RGBFusion2USB::Init_Controller() +{ + + const gb_fusion2_device* src_layout = gb_fusion2_device_list[device_index]; + const std::string SectionGen2 = "Gigabyte-Gen2-ARGB"; + const std::string SectionCustomBase = "CustomLayout"; + const std::string SectionCustom = SectionCustomBase + std::to_string(device_num); + const std::string SectionCalibration = "Calibration"; + RvrseLedHeaders ReverseLedLookup = reverse_map(LedLookup); + SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager(); + nlohmann::json device_settings = settings_manager->GetSettings(detector_name); + + /*---------------------------------------------------------*\ + | Checks for Gen2 support and adds flag to json. | + \*---------------------------------------------------------*/ + if(controller->SupportsGen2()) + { + if(!device_settings.contains(SectionGen2)) + { + device_settings[SectionGen2]["Enabled"] = false; + settings_manager->SetSettings(detector_name, device_settings); + settings_manager->SaveSettings(); + } + + supports_gen2 = device_settings[SectionGen2]["Enabled"]; + + if(supports_gen2) + { + controller->ScanGen2Strips(); + } + } + + /*---------------------------------------------------------*\ + | Create the custom layout from the generic layout | + \*---------------------------------------------------------*/ + switch(product_id) + { + case 0x8950: + src_layout = gb_fusion2_device_list[device_index + 1]; + break; + case 0x5711: + src_layout = gb_fusion2_device_list[device_index + 2]; + break; + default: + break; + } + + if(!device_settings.contains(SectionCustom)) + { + device_settings[SectionCustom]["Enabled"] = false; + device_settings[SectionCustom]["Data"] = BuildCustomLayoutJson(src_layout, ReverseLedLookup); + settings_manager->SetSettings(detector_name, device_settings); + settings_manager->SaveSettings(); + } + + bool custom_layout = device_settings[SectionCustom]["Enabled"]; + + EncodedCalibration hw_cal = controller->GetCalibration(false); + + if(device_num == 0) + { + if(!device_settings.contains(SectionCalibration)) + { + device_settings[SectionCalibration]["Enabled"] = false; + device_settings[SectionCalibration]["Data"] = WriteCalJsonFrom(hw_cal); + settings_manager->SetSettings(detector_name, device_settings); + settings_manager->SaveSettings(); + } + else + { + nlohmann::json& cal_sec = device_settings[SectionCalibration]; + bool cal_enable = cal_sec.value("Enabled", false); + + if(!cal_sec.contains("Data") || !cal_sec["Data"].is_object()) + { + cal_sec["Data"] = WriteCalJsonFrom(hw_cal); + settings_manager->SetSettings(detector_name, device_settings); + settings_manager->SaveSettings(); + } + else + { + nlohmann::json& cdata = cal_sec["Data"]; + FillMissingWith(cdata, hw_cal); + + if(!cal_enable) + { + cal_sec["Data"] = WriteCalJsonFrom(hw_cal); + settings_manager->SetSettings(detector_name, device_settings); + settings_manager->SaveSettings(); + } + } + + if(cal_enable) + { + const nlohmann::json& cdata = cal_sec["Data"]; + + EncodedCalibration desired; + desired.dled[0] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED1"); + desired.dled[1] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED2"); + desired.mainboard = GET_JSON_VAL_ELSE_OFF(cdata, "Mainboard"); + desired.spare[0] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare0"); + desired.spare[1] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare1"); + + if(controller->GetProductID() == 0x5711) + { + desired.dled[2] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED3"); + desired.dled[3] = GET_JSON_VAL_ELSE_OFF(cdata, "HDR_D_LED4"); + desired.spare[2] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare2"); + desired.spare[3] = GET_JSON_VAL_ELSE_OFF(cdata, "Spare3"); + } + else + { + desired.dled[2] = "OFF"; + desired.dled[3] = "OFF"; + desired.spare[2] = "OFF"; + desired.spare[3] = "OFF"; + } + controller->SetCalibration(desired, false); + } + } + } + /*---------------------------------------------------------------------*\ + | When no match found the first entry (generic_device) will be used | + | otherwise look up channel map based on device name | + \*---------------------------------------------------------------------*/ + if(!custom_layout) + { + /*-----------------------------------------------------------------*\ + | Loop through all known devices to look for a name match | + | NB: Can be switched to device IDs lookup when acpi table | + | is able to be probed accurately | + \*-----------------------------------------------------------------*/ + for(unsigned int i = 0; i < GB_FUSION2_DEVICE_COUNT; i++) + { + if(gb_fusion2_device_list[i]->name == name && + gb_fusion2_device_list[i]->device_num == device_num) + { + /*---------------------------------------------------------*\ + | Set device ID | + \*---------------------------------------------------------*/ + device_index = i; + src_layout = gb_fusion2_device_list[i]; + break; + } + } + } + /*---------------------------------------------------------------------*\ + | Creates per instance copy of layouts. | + \*---------------------------------------------------------------------*/ + instance_layout.zones = &instance_zones; + instance_layout.layout_id = src_layout->layout_id; + instance_layout.device_num = src_layout->device_num; + instance_layout.name = src_layout->name; + + for(uint8_t zi = 0; zi < GB_FUSION2_ZONES_MAX; ++zi) + { + (*instance_layout.zones)[zi] = (*src_layout->zones)[zi]; + } + + if(custom_layout) + { + LoadCustomLayoutFromJson(device_settings[SectionCustom]["Data"], LedLookup, &instance_layout); + } + /*---------------------------------------------------------------------*\ + | Culls the mode support based on layout_id. | + \*---------------------------------------------------------------------*/ + const uint32_t effect_mask = instance_layout.layout_id & GB_EFF_CORE_MASK; + modes.erase(std::remove_if(modes.begin(), modes.end(), + [effect_mask](const mode& m) + { + if(m.value == 0xFFFF /* Direct */) { return false; } + if(m.value == EFFECT_STATIC) { return false; } + + uint32_t bit = 0u; + switch(m.value) + { + case EFFECT_PULSE: bit = GB_EFF_BREATH; break; + case EFFECT_COLORCYCLE: bit = GB_EFF_CYCLE; break; + case EFFECT_BLINKING: bit = GB_EFF_FLASH; break; + case EFFECT_RANDOM: bit = GB_EFF_RANDOM; break; + case EFFECT_WAVE: bit = GB_EFF_WAVE; break; + case EFFECT_DFLASH: bit = GB_EFF_DFLASH; break; + case EFFECT_WAVE1: bit = GB_EFF_WAVE1; break; + case EFFECT_WAVE2: bit = GB_EFF_WAVE2; break; + case EFFECT_WAVE3: bit = GB_EFF_WAVE1; break; + case EFFECT_WAVE4: bit = GB_EFF_WAVE2; break; + default: bit = 0u; break; + } + return (bit == 0u) || ((effect_mask & bit) == 0u); + }), + modes.end()); + + /*---------------------------------------------------------*\ + | Iterate through layout and process each zone | + \*---------------------------------------------------------*/ + for(uint8_t zone_idx = 0; zone_idx < GB_FUSION2_ZONES_MAX; zone_idx++) + { + if(!(*instance_layout.zones)[zone_idx]) + { + continue; + } + const gb_fusion2_zone* zone_at_idx = (*instance_layout.zones)[zone_idx]; + + zone new_zone; + new_zone.name = zone_at_idx->name; + new_zone.leds_min = zone_at_idx->leds_min; + new_zone.leds_max = zone_at_idx->leds_max; + new_zone.leds_count = new_zone.leds_min; + new_zone.type = ((new_zone.leds_min == 1) && (new_zone.leds_max == 1)) ? ZONE_TYPE_SINGLE : ZONE_TYPE_LINEAR; + new_zone.matrix_map = NULL; + zones.emplace_back(new_zone); + } +} + +void RGBController_RGBFusion2USB::SetupZones() +{ + /*---------------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*---------------------------------------------------------*/ + leds.clear(); + colors.clear(); + + unsigned int d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + /*---------------------------------------------------------*\ + | Set up zones (Fixed so as to not spam the controller) | + \*---------------------------------------------------------*/ + + std::vector strips = controller->ExportGen2Strips(); + + for(uint8_t zone_idx = 0; zone_idx < GB_FUSION2_ZONES_MAX; zone_idx++) + { + const gb_fusion2_zone* zone_at_idx = (*instance_layout.zones)[zone_idx]; + if(!zone_at_idx) + { + continue; + } + bool single_zone = ((zone_at_idx->leds_min == 1) && (zone_at_idx->leds_max == 1)); + + if(!single_zone) + { + if(supports_gen2) + { + int slot = 0; + switch(zone_at_idx->idx) + { + case LED4: + case HDR_D_LED2: + slot = 1; + break; + case HDR_D_LED3: + slot = 2; + break; + case HDR_D_LED4: + slot = 3; + break; + default: + break; + } + + if(slot >= 0 && static_cast(slot) < strips.size()) + { + const Gen2StripInfo& info = strips[static_cast(slot)]; + if(info.totalLeds > 0u) + { + zones[zone_idx].leds_count = static_cast(info.totalLeds); + zones[zone_idx].leds_min = static_cast(info.totalLeds); + zones[zone_idx].leds_max = static_cast(info.totalLeds); + zones[zone_idx].segments.clear(); + + zones[zone_idx].segments.reserve(info.LedsOfStrip.size()); + unsigned int offset = 0; + for(size_t si = 0; si < info.LedsOfStrip.size(); ++si) + { + const uint16_t cnt = info.LedsOfStrip[si]; + if(cnt == 0) continue; + + segment seg; + seg.name = std::string("Segment ") + std::to_string(si); + seg.type = ZONE_TYPE_LINEAR; + seg.start_idx = offset; + seg.leds_count = static_cast(cnt); + + zones[zone_idx].segments.push_back(seg); + offset += static_cast(cnt); + } + } + } + } + + switch(zone_at_idx->idx) + { + case LED4: + case HDR_D_LED2: + d2 = zones[zone_idx].leds_count; + break; + case HDR_D_LED3: + d3 = zones[zone_idx].leds_count; + break; + case HDR_D_LED4: + d4 = zones[zone_idx].leds_count; + break; + default: + d1 = zones[zone_idx].leds_count; + break; + } + } + + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + + new_led.name = zone_at_idx->name; + new_led.value = zone_at_idx->idx; + + if(!single_zone) + { + new_led.name.append(" LED " + std::to_string(led_idx)); + } + + leds.push_back(new_led); + } + } + + controller->SetLedCount(d1, d2, d3, d4); + controller->SetStripBuiltinEffectState(-1, false); + SetupColors(); +} + +void RGBController_RGBFusion2USB::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_RGBFusion2USB::DeviceUpdateLEDs() +{ + int mode_value = (modes[active_mode].value); + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + uint32_t* color = &null_color; + + /*---------------------------------------------------------*\ + | If Wave 1-4 then use special sequence. | + \*---------------------------------------------------------*/ + if(mode_value == 6 || (mode_value >= 9 && mode_value <= 12)) + { + controller->SetStripBuiltinEffectState(-1, true); + controller->SetLEDEffect(-1, 1, 0, 0xFF, 0, color); + controller->ApplyEffect(); + controller->SetLEDEffect( 2, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color); + controller->ApplyEffect(); + return; + } + + for(int zone_idx = 0; zone_idx < (int)zones.size(); zone_idx++) + { + if(zones[zone_idx].type == ZONE_TYPE_SINGLE) + { + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + /*---------------------------------------------------------*\ + | Motherboard LEDs always use effect mode, so use static for| + | direct mode but get colors from zone | + \*---------------------------------------------------------*/ + if(modes[active_mode].value == 0xFFFF) + { + color = &zones[zone_idx].colors[led_idx]; + mode_value = EFFECT_STATIC; + } + /*---------------------------------------------------------*\ + | If the mode uses mode-specific color, get color from mode | + \*---------------------------------------------------------*/ + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + color = &modes[active_mode].colors[0]; + } + + /*---------------------------------------------------------*\ + | Apply the mode and color to the zone | + \*---------------------------------------------------------*/ + controller->SetLEDEffect(zones[zone_idx].leds[led_idx].value, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color); + } + } + /*---------------------------------------------------------*\ + | Set strip LEDs | + \*---------------------------------------------------------*/ + else + { + if(zones[zone_idx].leds && zones[zone_idx].leds_count) + { + unsigned char hdr = zones[zone_idx].leds->value; + + /*---------------------------------------------------------*\ + | Direct mode addresses a different register | + \*---------------------------------------------------------*/ + if(modes[active_mode].value == 0xFFFF) + { + controller->SetStripBuiltinEffectState(hdr, false); + controller->SetStripColors(hdr, zones[zone_idx].colors, zones[zone_idx].leds_count); + } + + /*---------------------------------------------------------*\ + | Effect mode | + \*---------------------------------------------------------*/ + else + { + /*---------------------------------------------------------*\ + | If mode has mode specific color, load color from mode | + \*---------------------------------------------------------*/ + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + color = &modes[active_mode].colors[0]; + } + + /*---------------------------------------------------------*\ + | Apply hardware effects to LED strips | + \*---------------------------------------------------------*/ + controller->SetStripBuiltinEffectState(hdr, true); + controller->SetLEDEffect(hdr, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color); + } + } + } + } + controller->ApplyEffect(); +} + +void RGBController_RGBFusion2USB::UpdateZoneLEDs(int zone) +{ + /*---------------------------------------------------------*\ + | Get mode parameters | + \*---------------------------------------------------------*/ + int mode_value = (modes[active_mode].value); + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + uint32_t* color = &null_color; + + /*---------------------------------------------------------*\ + | If Wave 1-4 then use special sequence. | + \*---------------------------------------------------------*/ + if(mode_value == 6 || (mode_value >= 9 && mode_value <= 12)) + { + controller->SetStripBuiltinEffectState(-1, true); + controller->SetLEDEffect(-1, 1, 0, 0xFF, 0, color); + controller->ApplyEffect(); + controller->SetLEDEffect( 2, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color); + controller->ApplyEffect(); + return; + } + + /*---------------------------------------------------------*\ + | Set motherboard LEDs | + \*---------------------------------------------------------*/ + if(zones[zone].type == ZONE_TYPE_SINGLE) + { + for(std::size_t led_idx = 0; led_idx < zones[zone].leds_count; led_idx++) + { + /*------------------------------------------------------------*\ + | Motherboard LEDs always use effect mode, so use static for | + | direct mode but get colors from zone | + \*------------------------------------------------------------*/ + if(mode_value == 0xFFFF) + { + color = &zones[zone].colors[led_idx]; + mode_value = EFFECT_STATIC; + } + + /*---------------------------------------------------------*\ + | If the mode uses mode-specific color, get color from mode | + \*---------------------------------------------------------*/ + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + color = &modes[active_mode].colors[0]; + } + + /*---------------------------------------------------------*\ + | Apply the mode and color to the zone | + \*---------------------------------------------------------*/ + controller->SetLEDEffect(zones[zone].leds[led_idx].value, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color); + controller->ApplyEffect(); + } + } + + /*---------------------------------------------------------*\ + | Set strip LEDs | + \*---------------------------------------------------------*/ + else + { + if(zones[zone].leds && zones[zone].leds_count) + { + unsigned char hdr = zones[zone].leds->value; + + /*---------------------------------------------------------*\ + | Direct mode addresses a different register | + \*---------------------------------------------------------*/ + if(mode_value == 0xFFFF) + { + controller->SetStripBuiltinEffectState(hdr, false); + controller->SetStripColors(hdr, zones[zone].colors, zones[zone].leds_count); + } + + /*---------------------------------------------------------*\ + | Effect mode | + \*---------------------------------------------------------*/ + else + { + /*---------------------------------------------------------*\ + | If mode has mode specific color, load color from mode | + \*---------------------------------------------------------*/ + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + color = &modes[active_mode].colors[0]; + } + + /*---------------------------------------------------------*\ + | Apply built-in effects to LED strips | + \*---------------------------------------------------------*/ + controller->SetStripBuiltinEffectState(hdr, true); + controller->SetLEDEffect(hdr, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color); + controller->ApplyEffect(); + } + } + } +} + +void RGBController_RGBFusion2USB::UpdateSingleLED(int led) +{ + /*---------------------------------------------------------*\ + | Get mode parameters | + \*---------------------------------------------------------*/ + int mode_value = (modes[active_mode].value); + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + uint32_t* color = &null_color; + + /*---------------------------------------------------------*\ + | If Wave 1-4 then use special sequence. | + \*---------------------------------------------------------*/ + if(mode_value == 6 || (mode_value >= 9 && mode_value <= 12)) + { + controller->SetStripBuiltinEffectState(-1, true); + controller->SetLEDEffect(-1, 1, 0, 0xFF, 0, color); + controller->ApplyEffect(); + controller->SetLEDEffect( 2, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color); + controller->ApplyEffect(); + return; + } + unsigned int zone_idx = GetLED_Zone(led); + + /*---------------------------------------------------------*\ + | Set motherboard LEDs | + \*---------------------------------------------------------*/ + if(zones[zone_idx].type == ZONE_TYPE_SINGLE) + { + /*---------------------------------------------------------*\ + | Motherboard LEDs always use effect mode, so use static for| + | direct mode but get colors from zone | + \*---------------------------------------------------------*/ + if(mode_value == 0xFFFF) + { + color = &colors[led]; + mode_value = EFFECT_STATIC; + } + + /*---------------------------------------------------------*\ + | If the mode uses mode-specific color, get color from mode | + \*---------------------------------------------------------*/ + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + color = &modes[active_mode].colors[0]; + } + + controller->SetLEDEffect(leds[led].value, mode_value, modes[active_mode].speed, modes[active_mode].brightness, random, color); + controller->ApplyEffect(); + } + + /*---------------------------------------------------------*\ + | Set strip LEDs | + \*---------------------------------------------------------*/ + else + { + UpdateZoneLEDs(zone_idx); + } +} + +void RGBController_RGBFusion2USB::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +int RGBController_RGBFusion2USB::GetLED_Zone(int led_idx) +{ + for(int zone_idx = 0; zone_idx < (int)zones.size(); zone_idx++) + { + int zone_start = zones[zone_idx].start_idx; + int zone_end = zone_start + zones[zone_idx].leds_count - 1; + + if((zone_start <= led_idx) && (zone_end >= led_idx)) + { + return(zone_idx); + } + } + + /*---------------------------------------------------------*\ + | If zone is not found, return -1 | + \*---------------------------------------------------------*/ + return(-1); +} + +/*---------------------------------------------------------*\ +| Convert calibration data to JSON | +\*---------------------------------------------------------*/ +nlohmann::json RGBController_RGBFusion2USB::WriteCalJsonFrom(const EncodedCalibration& src) +{ + nlohmann::json calib_json; + calib_json["HDR_D_LED1"] = src.dled[0]; + calib_json["HDR_D_LED2"] = src.dled[1]; + calib_json["HDR_D_LED3"] = src.dled[2]; + calib_json["HDR_D_LED4"] = src.dled[3]; + calib_json["Mainboard"] = src.mainboard; + calib_json["Spare0"] = src.spare[0]; + calib_json["Spare1"] = src.spare[1]; + calib_json["Spare2"] = src.spare[2]; + calib_json["Spare3"] = src.spare[3]; + + return calib_json; +} + +/*---------------------------------------------------------*\ +| Fill missing JSON calibration keys | +\*---------------------------------------------------------*/ +void RGBController_RGBFusion2USB::FillMissingWith(nlohmann::json& dst, const EncodedCalibration& fb) +{ + struct SetIfMissing + { + nlohmann::json& dst; + + void operator()(const char* key, const std::string& val) const + { + if(!dst.contains(key)) + { + dst[key] = val; + } + } + }; + + SetIfMissing set_if_missing{dst}; + + set_if_missing("HDR_D_LED1", fb.dled[0]); + set_if_missing("HDR_D_LED2", fb.dled[1]); + set_if_missing("Mainboard", fb.mainboard); + set_if_missing("Spare0", fb.spare[0]); + set_if_missing("Spare1", fb.spare[1]); + + if(controller->GetProductID() == 0x5711) + { + set_if_missing("HDR_D_LED3", fb.dled[2]); + set_if_missing("HDR_D_LED4", fb.dled[3]); + set_if_missing("Spare2", fb.spare[2]); + set_if_missing("Spare3", fb.spare[3]); + } +} + +/*---------------------------------------------------------*\ +| Build custom layout in JSON | +\*---------------------------------------------------------*/ +nlohmann::json RGBController_RGBFusion2USB::BuildCustomLayoutJson( + const gb_fusion2_device* layout, + const RvrseLedHeaders& reverseLookup) +{ + nlohmann::json json_custom; + for(uint8_t zone_idx = 0; zone_idx < GB_FUSION2_ZONES_MAX; zone_idx++) + { + if(!layout->zones[0][zone_idx]) + { + continue; + } + + nlohmann::json json_zone; + json_zone["name"] = layout->zones[0][zone_idx]->name; + json_zone["header"] = reverseLookup.at(layout->zones[0][zone_idx]->idx); + json_zone["leds_min"] = layout->zones[0][zone_idx]->leds_min; + json_zone["leds_max"] = layout->zones[0][zone_idx]->leds_max; + + json_custom[layout->name].push_back(json_zone); + } + return json_custom; +} + +/*---------------------------------------------------------*\ +| Build custom layout from JSON | +\*---------------------------------------------------------*/ +void RGBController_RGBFusion2USB::LoadCustomLayoutFromJson( + const nlohmann::json& json_custom, + const FwdLedHeaders& forwardLookup, + gb_fusion2_device* layout) +{ + for(uint8_t zone_idx = 0; zone_idx < GB_FUSION2_ZONES_MAX; zone_idx++) + { + /*---------------------------------------------------------*\ + | Check if there are more JSON objects to parse | + \*---------------------------------------------------------*/ + if(json_custom[layout->name].size() <= zone_idx) + { + layout->zones[0][zone_idx] = nullptr; + continue; + } + nlohmann::json json_zone = json_custom[layout->name].at(zone_idx); + gb_fusion2_zone* new_zone = new gb_fusion2_zone(); + + new_zone->name = json_zone["name"].get(); + std::string header = json_zone["header"].get(); + new_zone->idx = forwardLookup.at(header); + if( header == "HDR_D_LED1" + || header == "HDR_D_LED2" + || header == "HDR_D_LED3" + || header == "HDR_D_LED4") + { + new_zone->leds_min = std::max(json_zone["leds_min"].get(), 1); + new_zone->leds_max = std::min(json_zone["leds_max"].get(), 1024); + } + else + { + new_zone->leds_min = 1; + new_zone->leds_max = 1; + } + + /*---------------------------------------------------------*\ + | Check for valid values from JSON | + \*---------------------------------------------------------*/ + if(new_zone->name != "" + && new_zone->leds_min <= new_zone->leds_max + && new_zone->idx >= GB_FUSION2_LED_IDX::LED1 + && new_zone->idx <= GB_FUSION2_LED_IDX::LED11) + { + layout->zones[0][zone_idx] = new_zone; + allocated_zones.push_back(new_zone); + } + else + { + LOG_ERROR("[%s] Error creating zone %d: Validation failed for %s @ index %d (LEDs min %d to %d max)", + controller->GetDeviceName().c_str(), + zone_idx, + new_zone->name.c_str(), + new_zone->idx, + new_zone->leds_min, + new_zone->leds_max); + } + } +} + diff --git a/Controllers/GigabyteRGBFusion2USBController/RGBController_GigabyteRGBFusion2USB.h b/Controllers/GigabyteRGBFusion2USBController/RGBController_GigabyteRGBFusion2USB.h new file mode 100644 index 0000000..3bfc091 --- /dev/null +++ b/Controllers/GigabyteRGBFusion2USBController/RGBController_GigabyteRGBFusion2USB.h @@ -0,0 +1,97 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusion2USB.h | +| | +| RGBController for Gigabyte Aorus RGB Fusion 2 USB | +| motherboard | +| | +| jackun 08 Jan 2020 | +| megadjc 31 Jul 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "GigabyteFusion2USB_Devices.h" +#include "GigabyteRGBFusion2USBController.h" +#include "SettingsManager.h" + +#define RGBFUSION2_DIGITAL_LEDS_MIN 0 +#define RGBFUSION2_DIGITAL_LEDS_MAX 1024 +#define RGBFUSION2_BRIGHTNESS_MIN 0 +#define RGBFUSION2_BRIGHTNESS_MAX 255 +#define RGBFUSION2_SPEED_MIN 9 +#define RGBFUSION2_SPEED_MID 4 +#define RGBFUSION2_SPEED_MAX 0 + +#define GET_JSON_VAL_ELSE_OFF(obj, key) obj.contains(key) ? obj.at(key).get() : std::string("OFF") + +template +static std::map reverse_map(const std::map& map) +{ + std::map reversed_map; + + for(const std::pair entry : map) + { + reversed_map[entry.second] = entry.first; + } + + return reversed_map; +} + +class RGBController_RGBFusion2USB: public RGBController +{ +public: + RGBController_RGBFusion2USB(RGBFusion2USBController* controller_ptr, std::string _detector_name); + ~RGBController_RGBFusion2USB(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + std::string detector_name; + + RGBFusion2USBController* controller; + uint8_t device_num; + RGBColor null_color = 0; + bool supports_gen2 = 0; + /*---------------------------------------------------------*\ + | The intial value of device_index should point to the | + | layout for the generic_device | + \*---------------------------------------------------------*/ + uint32_t device_index = 0; + uint16_t product_id = 0; + uint32_t effects_mask = 0; + void Init_Controller(); + int GetLED_Zone(int led_idx); + + /*---------------------------------------------------------*\ + | Per instance layout lookup tables. | + \*---------------------------------------------------------*/ + gb_fusion2_device instance_layout{}; + gb_fusion2_layout instance_zones{}; + std::vector allocated_zones; + + nlohmann::json WriteCalJsonFrom( + const EncodedCalibration& src); + void FillMissingWith( + nlohmann::json& dst, + const EncodedCalibration& fb); + nlohmann::json BuildCustomLayoutJson( + const gb_fusion2_device* layout, + const RvrseLedHeaders& reverseLookup); + void LoadCustomLayoutFromJson( + const nlohmann::json& json_custom, + const FwdLedHeaders& forwardLookup, + gb_fusion2_device* layout); +}; diff --git a/Controllers/GigabyteRGBFusionController/GigabyteRGBFusionController.cpp b/Controllers/GigabyteRGBFusionController/GigabyteRGBFusionController.cpp new file mode 100644 index 0000000..6b70aa9 --- /dev/null +++ b/Controllers/GigabyteRGBFusionController/GigabyteRGBFusionController.cpp @@ -0,0 +1,196 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusionController.cpp | +| | +| Driver for Gigabyte Aorus RGB Fusion SMBus motherboard | +| | +| Adam Honse (CalcProgrammer1) 10 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "GigabyteRGBFusionController.h" + +RGBFusionController::RGBFusionController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev) +{ + this->bus = bus; + this->dev = dev; + + // Set Device name + strcpy(device_name, "Gigabyte Motherboard"); + + // Set LED count + led_count = 2; + + // Enable control + switch_bank(0); + bus->i2c_smbus_write_byte_data(dev, 0x02, 0x09); +} + +RGBFusionController::~RGBFusionController() +{ + +} + +std::string RGBFusionController::GetDeviceName() +{ + return(device_name); +} + +std::string RGBFusionController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +unsigned int RGBFusionController::GetLEDCount() +{ + return(led_count); +} + +unsigned char RGBFusionController::GetMode() +{ + switch_bank(0); + return(get_mode_ch_0()); +} + +void RGBFusionController::SetAllColors(unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char mode_ch_0; + unsigned char mode_ch_1; + + switch_bank(1); + set_color_ch_0(red, green, blue); + set_color_ch_1(red, green, blue); + + switch_bank(0); + mode_ch_0 = get_mode_ch_0(); + mode_ch_1 = get_mode_ch_1(); + set_mode_ch_0(mode_ch_0); + set_mode_ch_1(mode_ch_1); + +} + +void RGBFusionController::SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char mode; + + switch (led) + { + case 0: + switch_bank(1); + set_color_ch_0(red, green, blue); + + switch_bank(0); + mode = get_mode_ch_0(); + set_mode_ch_0(mode); + break; + + case 1: + switch_bank(1); + set_color_ch_1(red, green, blue); + + switch_bank(0); + mode = get_mode_ch_1(); + set_mode_ch_1(mode); + break; + } +} + +void RGBFusionController::SetMode(unsigned char mode, unsigned char speed) +{ + switch_bank(0); + set_mode_ch_0(mode); + set_timers_ch_0(speed_table[0][speed], speed_table[1][speed]); + set_mode_ch_1(mode); + set_timers_ch_1(speed_table[0][speed], speed_table[1][speed]); +} + +void RGBFusionController::dump() +{ + int i, j; + + int start = 0x00; + + FILE* file = freopen("rgb_fusion_dump.txt", "a", stdout); + + printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n"); + + for (i = 0; i < 0xFF; i += 16) + { + printf("%04x: ", i + start); + + for (j = 0; j < 16; j++) + { + printf("%02x ", bus->i2c_smbus_read_byte_data(dev, (start + i + j))); + } + + printf("\r\n"); + } + + fclose(file); +} + +unsigned char RGBFusionController::get_mode_ch_0() +{ + return(bus->i2c_smbus_read_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_0_MODE)); +} + +unsigned char RGBFusionController::get_mode_ch_1() +{ + return(bus->i2c_smbus_read_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_1_MODE)); +} + +void RGBFusionController::set_color_ch_0(unsigned char red, unsigned char green, unsigned char blue) +{ + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_1_REG_CH_0_R, red); + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_1_REG_CH_0_G, green); + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_1_REG_CH_0_B, blue); + bus->i2c_smbus_write_byte_data(dev, 0x03, 0x01); +} + +void RGBFusionController::set_color_ch_1(unsigned char red, unsigned char green, unsigned char blue) +{ + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_1_REG_CH_1_R, red); + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_1_REG_CH_1_G, green); + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_1_REG_CH_1_B, blue); + bus->i2c_smbus_write_byte_data(dev, 0x0B, 0x01); +} + +void RGBFusionController::set_mode_ch_0(unsigned char mode) +{ + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_0_MODE, mode + RGB_FUSION_WRITE_MODE_OFST); +} + +void RGBFusionController::set_mode_ch_1(unsigned char mode) +{ + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_1_MODE, mode + RGB_FUSION_WRITE_MODE_OFST); +} + +void RGBFusionController::set_timers_ch_0(unsigned short timer0, unsigned short timer1) +{ + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_0_TIMER_0_MSB, timer0 >> 8); + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_0_TIMER_0_LSB, timer0 & 0xFF); + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_0_TIMER_1_MSB, timer1 >> 8); + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_0_TIMER_1_LSB, timer1 & 0xFF); +} + +void RGBFusionController::set_timers_ch_1(unsigned short timer0, unsigned short timer1) +{ + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_1_TIMER_0_MSB, timer0 >> 8); + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_1_TIMER_0_LSB, timer0 & 0xFF); + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_1_TIMER_1_MSB, timer1 >> 8); + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_0_REG_CH_1_TIMER_1_LSB, timer1 & 0xFF); +} + +void RGBFusionController::switch_bank(unsigned char bank) +{ + bus->i2c_smbus_write_byte_data(dev, RGB_FUSION_BANK_SWITCH_REG, bank); +} diff --git a/Controllers/GigabyteRGBFusionController/GigabyteRGBFusionController.h b/Controllers/GigabyteRGBFusionController/GigabyteRGBFusionController.h new file mode 100644 index 0000000..a687839 --- /dev/null +++ b/Controllers/GigabyteRGBFusionController/GigabyteRGBFusionController.h @@ -0,0 +1,104 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusionController.h | +| | +| Driver for Gigabyte Aorus RGB Fusion SMBus motherboard | +| | +| Adam Honse (CalcProgrammer1) 10 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char rgb_fusion_dev_id; + +enum +{ + RGB_FUSION_BANK_0_REG_CH_0_MODE = 0x03, /* Channel 0 Mode Selection */ + RGB_FUSION_BANK_0_REG_CH_0_TIMER_0_MSB + = 0x06, /* Channel 0 Timer 0 MSB */ + RGB_FUSION_BANK_0_REG_CH_0_TIMER_0_LSB + = 0x07, /* Channel 0 Timer 0 LSB */ + RGB_FUSION_BANK_0_REG_CH_0_TIMER_1_MSB + = 0x08, /* Channel 0 Timer 1 MSB */ + RGB_FUSION_BANK_0_REG_CH_0_TIMER_1_LSB + = 0x09, /* Channel 0 Timer 1 LSB */ + RGB_FUSION_BANK_0_REG_CH_1_MODE = 0x13, /* Channel 1 Mode Selection */ + RGB_FUSION_BANK_0_REG_CH_1_TIMER_0_MSB + = 0x16, /* Channel 1 Timer 0 MSB */ + RGB_FUSION_BANK_0_REG_CH_1_TIMER_0_LSB + = 0x17, /* Channel 1 Timer 0 LSB */ + RGB_FUSION_BANK_0_REG_CH_1_TIMER_1_MSB + = 0x18, /* Channel 1 Timer 1 MSB */ + RGB_FUSION_BANK_0_REG_CH_1_TIMER_1_LSB + = 0x19, /* Channel 1 Timer 1 LSB */ + RGB_FUSION_BANK_1_REG_CH_0_R = 0x00, /* Channel 0 Red Value */ + RGB_FUSION_BANK_1_REG_CH_0_G = 0x01, /* Channel 0 Green Value */ + RGB_FUSION_BANK_1_REG_CH_0_B = 0x02, /* Channel 0 Blue Value */ + RGB_FUSION_BANK_1_REG_CH_1_R = 0x08, /* Channel 1 Red Value */ + RGB_FUSION_BANK_1_REG_CH_1_G = 0x09, /* Channel 1 Green Value */ + RGB_FUSION_BANK_1_REG_CH_1_B = 0x0A, /* Channel 1 Blue Value */ + RGB_FUSION_BANK_SWITCH_REG = 0xF0, /* Bank Switch Register */ +}; + +enum +{ + RGB_FUSION_SPEED_SLOW = 0x00, /* Slowest speed */ + RGB_FUSION_SPEED_NORMAL = 0x01, /* Normal speed */ + RGB_FUSION_SPEED_FAST = 0x02, /* Fastest speed */ +}; + +static const short speed_table[2][3] = +{ + { 0x01E0, 0x00F0, 0x0078 }, + { 0x4000, 0x2000, 0x1000 } +}; + +#define RGB_FUSION_NUMBER_MODES 3 /* Number of RGB Fusion modes */ +#define RGB_FUSION_WRITE_MODE_OFST 0x10 /* offset to add when writing mode */ + +enum +{ + RGB_FUSION_MODE_STATIC = 0x00, /* Static color mode */ + RGB_FUSION_MODE_BREATHING = 0x01, /* Breathing effect mode */ + RGB_FUSION_MODE_FLASHING = 0x02, /* Flashing effect mode */ +}; + +class RGBFusionController +{ +public: + RGBFusionController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev); + ~RGBFusionController(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + unsigned int GetLEDCount(); + unsigned char GetMode(); + void SetAllColors(unsigned char red, unsigned char green, unsigned char blue); + void SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void SetMode(unsigned char mode, unsigned char speed); + +private: + void dump(); + + unsigned char get_mode_ch_0(); + unsigned char get_mode_ch_1(); + + void set_color_ch_0(unsigned char red, unsigned char green, unsigned char blue); + void set_color_ch_1(unsigned char red, unsigned char green, unsigned char blue); + void set_mode_ch_0(unsigned char mode); + void set_mode_ch_1(unsigned char mode); + void set_timers_ch_0(unsigned short timer0, unsigned short timer1); + void set_timers_ch_1(unsigned short timer0, unsigned short timer1); + void switch_bank(unsigned char bank); + + char device_name[32]; + unsigned int led_count; + i2c_smbus_interface* bus; + rgb_fusion_dev_id dev; + +}; diff --git a/Controllers/GigabyteRGBFusionController/GigabyteRGBFusionControllerDetect.cpp b/Controllers/GigabyteRGBFusionController/GigabyteRGBFusionControllerDetect.cpp new file mode 100644 index 0000000..e0c0d29 --- /dev/null +++ b/Controllers/GigabyteRGBFusionController/GigabyteRGBFusionControllerDetect.cpp @@ -0,0 +1,94 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusionControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus RGB Fusion SMBus | +| motherboard | +| | +| Adam Honse (CalcProgrammer1) 10 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "GigabyteRGBFusionController.h" +#include "LogManager.h" +#include "RGBController_GigabyteRGBFusion.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +#define DETECTOR_NAME "Gigabyte RGB Fusion SMBus" +#define VENDOR_NAME "Gigabyte Technology Co., Ltd." +#define SMBUS_ADDRESS 0x28 + +/******************************************************************************************\ +* * +* TestForGigabyteRGBFusionController * +* * +* Tests the given address to see if an RGB Fusion controller exists there. First * +* does a quick write to test for a response * +* * +\******************************************************************************************/ + +bool TestForGigabyteRGBFusionController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + + int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE); + + if (res >= 0) + { + pass = true; + + res = bus->i2c_smbus_read_byte_data(address, 0xF2); + + if (res != 0xC4) + { + pass = false; + } + } + + return(pass); + +} /* TestForGigabyteRGBFusionController() */ + +/******************************************************************************************\ +* * +* DetectGigabyteRGBFusionControllers * +* * +* Detect RGB Fusion controllers on the enumerated I2C busses at address 0x28. * +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion device is connected * +* dev - I2C address of RGB Fusion device * +* * +\******************************************************************************************/ + +void DetectGigabyteRGBFusionControllers(std::vector& busses) +{ + for(unsigned int bus = 0; bus < busses.size(); bus++) + { + IF_MOBO_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + if(busses[bus]->pci_subsystem_vendor == GIGABYTE_SUB_VEN) + { + LOG_DEBUG(SMBUS_CHECK_DEVICE_MESSAGE_EN, DETECTOR_NAME, bus, VENDOR_NAME, SMBUS_ADDRESS); + + // Check for RGB Fusion controller at 0x28 + if(TestForGigabyteRGBFusionController(busses[bus], SMBUS_ADDRESS)) + { + RGBFusionController* controller = new RGBFusionController(busses[bus], SMBUS_ADDRESS); + RGBController_RGBFusion* rgb_controller = new RGBController_RGBFusion(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + else + { + LOG_DEBUG(SMBUS_CHECK_DEVICE_FAILURE_EN, DETECTOR_NAME, bus, VENDOR_NAME); + } + } + } +} /* DetectGigabyteRGBFusionControllers() */ + +REGISTER_I2C_DETECTOR("Gigabyte RGB Fusion", DetectGigabyteRGBFusionControllers); diff --git a/Controllers/GigabyteRGBFusionController/RGBController_GigabyteRGBFusion.cpp b/Controllers/GigabyteRGBFusionController/RGBController_GigabyteRGBFusion.cpp new file mode 100644 index 0000000..730f11a --- /dev/null +++ b/Controllers/GigabyteRGBFusionController/RGBController_GigabyteRGBFusion.cpp @@ -0,0 +1,176 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusion.cpp | +| | +| RGBController for Gigabyte Aorus RGB Fusion SMBus | +| motherboard | +| | +| Adam Honse (CalcProgrammer1) 11 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GigabyteRGBFusion.h" + +static const char* rgb_fusion_zone_names[] = +{ + "Motherboard", + "RGB Header" +}; + +/**------------------------------------------------------------------*\ + @name Gigabyte Fusion SMBus + @category Motherboard + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGigabyteRGBFusionControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RGBFusion::RGBController_RGBFusion(RGBFusionController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Gigabyte"; + description = "RGB Fusion 1.0"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_MOTHERBOARD; + + mode Direct; + Direct.name = "Direct"; + Direct.value = RGB_FUSION_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = RGB_FUSION_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = RGB_FUSION_SPEED_SLOW; + Breathing.speed_max = RGB_FUSION_SPEED_FAST; + Breathing.speed = RGB_FUSION_SPEED_NORMAL; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = RGB_FUSION_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Flashing.speed_min = RGB_FUSION_SPEED_SLOW; + Flashing.speed_max = RGB_FUSION_SPEED_FAST; + Flashing.speed = RGB_FUSION_SPEED_NORMAL; + Flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flashing); + + SetupZones(); + + // Initialize active mode + active_mode = GetDeviceMode(); +} + +RGBController_RGBFusion::~RGBController_RGBFusion() +{ + delete controller; +} + +void RGBController_RGBFusion::SetupZones() +{ + /*---------------------------------------------------------*\ + | Search through all LEDs and create zones for each channel | + | type | + \*---------------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < controller->GetLEDCount(); zone_idx++) + { + zone* new_zone = new zone(); + + /*---------------------------------------------------------*\ + | Set zone name to channel name | + \*---------------------------------------------------------*/ + new_zone->name = rgb_fusion_zone_names[zone_idx]; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + /*---------------------------------------------------------*\ + | Push new zone to zones vector | + \*---------------------------------------------------------*/ + zones.push_back(*new_zone); + } + + for(unsigned int led_idx = 0; led_idx < zones.size(); led_idx++) + { + led* new_led = new led(); + + /*---------------------------------------------------------*\ + | Set LED name to channel name | + \*---------------------------------------------------------*/ + new_led->name = rgb_fusion_zone_names[led_idx]; + + /*---------------------------------------------------------*\ + | Push new LED to LEDs vector | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + } + + SetupColors(); +} + +void RGBController_RGBFusion::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RGBFusion::DeviceUpdateLEDs() +{ + for(unsigned int led = 0; led < (unsigned int)colors.size(); led++) + { + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetLEDColor(led, red, grn, blu); + } +} + +void RGBController_RGBFusion::UpdateZoneLEDs(int zone) +{ + RGBColor color = colors[zone]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetLEDColor(zone, red, grn, blu); +} + +void RGBController_RGBFusion::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +int RGBController_RGBFusion::GetDeviceMode() +{ + int dev_mode = controller->GetMode(); + + for(int mode = 0; mode < (int)modes.size(); mode++) + { + if(modes[mode].value == dev_mode) + { + return(mode); + } + } + + return(0); +} + +void RGBController_RGBFusion::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].value, modes[active_mode].speed); +} diff --git a/Controllers/GigabyteRGBFusionController/RGBController_GigabyteRGBFusion.h b/Controllers/GigabyteRGBFusionController/RGBController_GigabyteRGBFusion.h new file mode 100644 index 0000000..75d23b8 --- /dev/null +++ b/Controllers/GigabyteRGBFusionController/RGBController_GigabyteRGBFusion.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusion.h | +| | +| RGBController for Gigabyte Aorus RGB Fusion SMBus | +| motherboard | +| | +| Adam Honse (CalcProgrammer1) 11 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteRGBFusionController.h" + +class RGBController_RGBFusion : public RGBController +{ +public: + RGBController_RGBFusion(RGBFusionController* controller_ptr); + ~RGBController_RGBFusion(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RGBFusionController* controller; + + int GetDeviceMode(); +}; diff --git a/Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUController.cpp b/Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUController.cpp new file mode 100644 index 0000000..c1b87bc --- /dev/null +++ b/Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUController.cpp @@ -0,0 +1,96 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusionGPUController.cpp | +| | +| Driver for Gigabyte Aorus RGB Fusion GPU | +| | +| Adam Honse (CalcProgrammer1) 20 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "GigabyteRGBFusionGPUController.h" + +RGBFusionGPUController::RGBFusionGPUController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +RGBFusionGPUController::~RGBFusionGPUController() +{ + +} + +std::string RGBFusionGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string RGBFusionGPUController::GetDeviceName() +{ + return(name); +} + +void RGBFusionGPUController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + bus->i2c_smbus_write_byte(dev, RGB_FUSION_GPU_REG_COLOR); + bus->i2c_smbus_write_byte(dev, red); + bus->i2c_smbus_write_byte(dev, green); + bus->i2c_smbus_write_byte(dev, blue); + + /*-----------------------------------------------------*\ + | Pad commands with 4 zero-bytes for NVIDIA_RTX3060_DEV | + \*-----------------------------------------------------*/ + if(dev == 0x62) + { + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + } +} + +void RGBFusionGPUController::SetMode(unsigned char mode, unsigned char speed, unsigned char brightness) +{ + bus->i2c_smbus_write_byte(dev, RGB_FUSION_GPU_REG_MODE); + bus->i2c_smbus_write_byte(dev, mode); + bus->i2c_smbus_write_byte(dev, speed); + bus->i2c_smbus_write_byte(dev, brightness); + + /*-----------------------------------------------------*\ + | Pad commands with 4 zero-bytes for NVIDIA_RTX3060_DEV | + \*-----------------------------------------------------*/ + if(dev == 0x62) + { + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + } +} + +void RGBFusionGPUController::Save() +{ + bus->i2c_smbus_write_byte(dev, RGB_FUSION_GPU_REG_SAVE); + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + + /*-----------------------------------------------------*\ + | Pad commands with 4 zero-bytes for NVIDIA_RTX3060_DEV | + \*-----------------------------------------------------*/ + if(dev == 0x62) + { + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + bus->i2c_smbus_write_byte(dev, 0x00); + } +} diff --git a/Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUController.h b/Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUController.h new file mode 100644 index 0000000..78ca9a7 --- /dev/null +++ b/Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUController.h @@ -0,0 +1,66 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusionGPUController.h | +| | +| Driver for Gigabyte Aorus RGB Fusion GPU | +| | +| Adam Honse (CalcProgrammer1) 20 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char rgb_fusion_dev_id; + +enum +{ + RGB_FUSION_GPU_REG_COLOR = 0x40, + RGB_FUSION_GPU_REG_MODE = 0x88, + RGB_FUSION_GPU_REG_SAVE = 0xAA, +}; + +enum +{ + RGB_FUSION_GPU_MODE_STATIC = 0x01, + RGB_FUSION_GPU_MODE_BREATHING = 0x02, + RGB_FUSION_GPU_MODE_FLASHING = 0x04, + RGB_FUSION_GPU_MODE_DUAL_FLASHING = 0x08, + RGB_FUSION_GPU_MODE_COLOR_CYCLE = 0x10, + RGB_FUSION_GPU_MODE_SPECTRUM_CYCLE = 0x11 +}; + +enum +{ + RGB_FUSION_GPU_SPEED_SLOWEST = 0x00, + RGB_FUSION_GPU_SPEED_NORMAL = 0x05, + RGB_FUSION_GPU_SPEED_FASTEST = 0x09 +}; + +enum +{ + RGB_FUSION_GPU_BRIGHTNESS_MIN = 0x00, + RGB_FUSION_GPU_BRIGHTNESS_MAX = 0x63 +}; + +class RGBFusionGPUController +{ +public: + RGBFusionGPUController(i2c_smbus_interface* bus, rgb_fusion_dev_id dev, std::string dev_name); + ~RGBFusionGPUController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + void SetMode(unsigned char mode, unsigned char speed, unsigned char brightness); + void Save(); + +private: + i2c_smbus_interface* bus; + rgb_fusion_dev_id dev; + std::string name; +}; diff --git a/Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUControllerDetect.cpp b/Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUControllerDetect.cpp new file mode 100644 index 0000000..7b85e3f --- /dev/null +++ b/Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUControllerDetect.cpp @@ -0,0 +1,192 @@ +/*---------------------------------------------------------*\ +| GigabyteRGBFusionGPUControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus RGB Fusion GPU | +| | +| Adam Honse (CalcProgrammer1) 20 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "GigabyteRGBFusionGPUController.h" +#include "LogManager.h" +#include "RGBController_GigabyteRGBFusionGPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +#define GIGABYTEGPU_CONTROLLER_NAME "Gigabyte RGB Fusion GPU" + +/******************************************************************************************\ +* * +* TestForGigabyteRGBFusionGPUController * +* * +* Tests the given address to see if an RGB Fusion controller exists there. First * +* does a quick write to test for a response * +* * +\******************************************************************************************/ + +bool TestForGigabyteRGBFusionGPUController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + int res; + + //Write out 0xAB 0x00 0x00 0x00 sequence + res = bus->i2c_smbus_write_byte(address, 0xAB); + + if (res >= 0) + { + bus->i2c_smbus_write_byte(address, 0x00); + bus->i2c_smbus_write_byte(address, 0x00); + bus->i2c_smbus_write_byte(address, 0x00); + + // NVIDIA_RTX3060_DEV requires additional bytes to initialise + if (address == 0x62) + { + bus->i2c_smbus_write_byte(address, 0x00); + bus->i2c_smbus_write_byte(address, 0x00); + bus->i2c_smbus_write_byte(address, 0x00); + bus->i2c_smbus_write_byte(address, 0x00); + } + + pass = true; + + res = bus->i2c_smbus_read_byte(address); + + if (res != 0xAB) + { + LOG_DEBUG("[%s] at 0x%02X address expected 0xAB but recieved: 0x%02X", GIGABYTEGPU_CONTROLLER_NAME, address, res); + pass = false; + } + + res = bus->i2c_smbus_read_byte(address); + + if ((res != 0x14)&& (res != 0x12) && (res != 0x10) && (res != 0x11)) + { + LOG_DEBUG("[%s] at 0x%02X address expected 0x10|0x11|0x12|0x14 but recieved: 0x%02X", GIGABYTEGPU_CONTROLLER_NAME, address, res); + pass = false; + } + + bus->i2c_smbus_read_byte(address); + bus->i2c_smbus_read_byte(address); + + //We don't know what the 0x48 controller returns, so for now just assume it exists + if(address == 0x48) + { + pass = true; + } + } + + return(pass); + +} /* TestForRGBFusionGPUController() */ + +/******************************************************************************************\ +* * +* DetectRGBFusionGPUControllers * +* * +* Detect GigabyteRGB Fusion controllers on the enumerated I2C busses at address 0x47.* +* * +* bus - pointer to i2c_smbus_interface where RGB Fusion device is connected * +* dev - I2C address of RGB Fusion device * +* * +\******************************************************************************************/ + +void DetectGigabyteRGBFusionGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + // Check for RGB Fusion controller + if(TestForGigabyteRGBFusionGPUController(bus, i2c_addr)) + { + RGBFusionGPUController* controller = new RGBFusionGPUController(bus, i2c_addr, name); + RGBController_RGBFusionGPU* rgb_controller = new RGBController_RGBFusionGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectGigabyteRGBFusionGPUControllers() */ + +REGISTER_I2C_PCI_DETECTOR("Gigabyte GTX 1050 G1 Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1050_G1_GAMING_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1050 Ti G1 Gaming Rev A1", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1050TI_G1_GAMING_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1050 Ti G1 Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1050TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1050TI_G1_GAMING_SUB_DEV, 0x48); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1060 G1 Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1060_G1_GAMING_SUB_DEV, 0x48); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1060 G1 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1060_G1_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1060 Xtreme Gaming V1", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1060_XTREME_V1_SUB_DEV_D, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1060 Xtreme Gaming V1", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1060_XTREME_V1_SUB_DEV_H, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1060 Xtreme Gaming V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1060_XTREME_V2_SUB_DEV_D, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1060 Xtreme Gaming V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1060_XTREME_V2_SUB_DEV_H, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1070 Xtreme Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1070_XTREME_SUB_DEV_D, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1070 Xtreme Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1070_XTREME_SUB_DEV_H, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1070 G1 Gaming V1", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1070_G1_GAMING_8G_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1070 Ti Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1070TI_GAMING_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1080 G1 Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1080_G1_GAMING_SUB_DEV, 0x48); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1080 Ti Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1080TI_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1080 Ti Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1080TI_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1080 Ti Gaming OC BLACK", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1080TI_GAMING_OC_BLACK_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1080 Ti Xtreme Edition", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1080TI_XTREME_SUB_DEV_D, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1080 Ti Xtreme Edition", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1080TI_XTREME_SUB_DEV_H, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1080 Ti Xtreme Waterforce Edition", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1080TI_XTREME_WATERFORCE_SUB_DEV_D, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1080 Ti Xtreme Waterforce Edition", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1080TI_XTREME_WATERFORCE_SUB_DEV_H, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1650 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1650_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1650_GAMING_OC_SUB_DEV, 0x55); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1660 Gaming OC 6G", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1660_GAMING_OC_6G_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1660 SUPER Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1660S_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce GTX 1660 Ti Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_GTX1660TI_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2060 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2060_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2060 Gaming OC PRO", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2060_GAMING_OC_PRO_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2060 Gaming OC PRO V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU104_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2060_GAMING_OC_PRO_SUB_DEV2, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2060 Gaming OC PRO V3", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2060_GAMING_OC_PRO_SUB_DEV2, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2060 Gaming OC PRO White", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2060_GAMING_OC_PRO_WHITE_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2060 SUPER Gaming", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2060S_GAMING_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2060 SUPER Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2060S_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2060 SUPER Gaming OC 3X White", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2060S_GAMING_OC_WHITE_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2060 SUPER Gaming OC 3X V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2060S_GAMING_OC_3X_V2_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2070 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2070_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2070 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2070_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2070 Gaming OC 8GC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2070_GAMING_OC_8GC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2070 Windforce", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2070_WINDFORCE_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2070 SUPER Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2070S_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2070 SUPER Gaming OC 3X", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2070S_GAMING_OC_3X_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2070 SUPER Gaming OC 3X", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2070S_GAMING_OC_3X_SUB_DEV, 0x55); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2070 SUPER Gaming OC 3X White", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2070S_GAMING_OC_3X_WHITE_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2080 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2080_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2080 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2080_A_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2080 Ti GAMING OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2080TI_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 2080 SUPER Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX2080S_GAMING_OC_SUB_DEV, 0x47); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3050 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3050_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3050_GAMING_OC_8GB_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 EAGLE OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_EAGLE_OC_12GB_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 EAGLE OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA104_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_EAGLE_OC_12GB_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 EAGLE OC V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_EAGLE_OC_12GB_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 EAGLE LHR V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_EAGLE_12GB_V2_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Vision OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_VISION_OC_12GB_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Vision OC LHR", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_VISION_OC_12GB_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Vision OC V3", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA104_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_VISION_OC_12GB_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_GAMING_OC_12GB_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Gaming OC V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA104_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_GAMING_OC_12GB_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060_GAMING_OC_12GB_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti Gaming OC LHR V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_GAMING_OC_SUB_DEV, 0x32); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti EAGLE OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_EAGLE_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti EAGLE OC LHR V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_EAGLE_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti EAGLE OC LHR V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_EAGLE_OC_LHR_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3060 Ti Vision OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3060TI_VISION_OC_8G_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 Gaming OC LHR V3", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 Vision", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070_VISION_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 Vision LHR V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070_VISION_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 Eagle OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070_EAGLE_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 Eagle OC LHR V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070_EAGLE_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 Ti Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070TI_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 Ti EAGLE", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070TI_EAGLE_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3070 Ti Vision OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3070TI_VISION_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3080 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3080 Vision OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080_VISION_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3080 EAGLE OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080_EAGLE_OC_10G_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3080 Gaming OC LHR", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3080 Vision OC LHR V2", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080_VISION_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3080 12G Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080_GAMING_OC_12G_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3080 Ti Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080TI_GAMING_OC_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3080 Ti EAGLE", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080TI_EAGLE_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3080 Ti EAGLE OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3080TI_EAGLE_OC_SUB_DEV, 0x63); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 3090 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX3090_GAMING_OC_24GB_SUB_DEV, 0x62); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4060 Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX4060_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4060_GAMING_OC_8G_SUB_DEV, 0x55); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4060 Ti Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX4060TI_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4060TI_GAMING_OC_8G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4060 Ti Gaming OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX4060TI_16G_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4060TI_GAMING_OC_16G_SUB_DEV, 0x71); +REGISTER_I2C_PCI_DETECTOR("Gigabyte GeForce RTX 4070 Ti SUPER EAGLE OC", DetectGigabyteRGBFusionGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, GIGABYTE_SUB_VEN, GIGABYTE_RTX4070TIS_EAGLE_OC_16G_SUB_DEV, 0x71); diff --git a/Controllers/GigabyteRGBFusionGPUController/RGBController_GigabyteRGBFusionGPU.cpp b/Controllers/GigabyteRGBFusionGPUController/RGBController_GigabyteRGBFusionGPU.cpp new file mode 100644 index 0000000..6164b8f --- /dev/null +++ b/Controllers/GigabyteRGBFusionGPUController/RGBController_GigabyteRGBFusionGPU.cpp @@ -0,0 +1,181 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusionGPU.cpp | +| | +| RGBController for Gigabyte Aorus RGB Fusion GPU | +| | +| Adam Honse (CalcProgrammer1) 23 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GigabyteRGBFusionGPU.h" + +/**------------------------------------------------------------------*\ + @name Gigabyte Fusion GPU + @category GPU + @type I2C + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGigabyteRGBFusionGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RGBFusionGPU::RGBController_RGBFusionGPU(RGBFusionGPUController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Gigabyte"; + description = "RGB Fusion GPU Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Direct; + Direct.name = "Direct"; + Direct.value = RGB_FUSION_GPU_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = RGB_FUSION_GPU_BRIGHTNESS_MIN; + Direct.brightness_max = RGB_FUSION_GPU_BRIGHTNESS_MAX; + Direct.brightness = RGB_FUSION_GPU_BRIGHTNESS_MAX; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = RGB_FUSION_GPU_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.speed_min = RGB_FUSION_GPU_SPEED_SLOWEST; + Breathing.speed_max = RGB_FUSION_GPU_SPEED_FASTEST; + Breathing.speed = RGB_FUSION_GPU_SPEED_NORMAL; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = RGB_FUSION_GPU_BRIGHTNESS_MIN; + Breathing.brightness_max = RGB_FUSION_GPU_BRIGHTNESS_MAX; + Breathing.brightness = RGB_FUSION_GPU_BRIGHTNESS_MAX; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = RGB_FUSION_GPU_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Flashing.speed_min = RGB_FUSION_GPU_SPEED_SLOWEST; + Flashing.speed_max = RGB_FUSION_GPU_SPEED_FASTEST; + Flashing.speed = RGB_FUSION_GPU_SPEED_NORMAL; + Flashing.color_mode = MODE_COLORS_PER_LED; + Flashing.brightness_min = RGB_FUSION_GPU_BRIGHTNESS_MIN; + Flashing.brightness_max = RGB_FUSION_GPU_BRIGHTNESS_MAX; + Flashing.brightness = RGB_FUSION_GPU_BRIGHTNESS_MAX; + modes.push_back(Flashing); + + mode DualFlashing; + DualFlashing.name = "Dual Flashing"; + DualFlashing.value = RGB_FUSION_GPU_MODE_DUAL_FLASHING; + DualFlashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + DualFlashing.speed_min = RGB_FUSION_GPU_SPEED_SLOWEST; + DualFlashing.speed_max = RGB_FUSION_GPU_SPEED_FASTEST; + DualFlashing.speed = RGB_FUSION_GPU_SPEED_NORMAL; + DualFlashing.color_mode = MODE_COLORS_PER_LED; + DualFlashing.brightness_min = RGB_FUSION_GPU_BRIGHTNESS_MIN; + DualFlashing.brightness_max = RGB_FUSION_GPU_BRIGHTNESS_MAX; + DualFlashing.brightness = RGB_FUSION_GPU_BRIGHTNESS_MAX; + modes.push_back(DualFlashing); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = RGB_FUSION_GPU_MODE_COLOR_CYCLE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED; + ColorCycle.speed_min = RGB_FUSION_GPU_SPEED_SLOWEST; + ColorCycle.speed_max = RGB_FUSION_GPU_SPEED_FASTEST; + ColorCycle.speed = RGB_FUSION_GPU_SPEED_NORMAL; + ColorCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(ColorCycle); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = RGB_FUSION_GPU_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + SpectrumCycle.speed_min = RGB_FUSION_GPU_SPEED_SLOWEST; + SpectrumCycle.speed_max = RGB_FUSION_GPU_SPEED_FASTEST; + SpectrumCycle.speed = RGB_FUSION_GPU_SPEED_NORMAL; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = RGB_FUSION_GPU_BRIGHTNESS_MIN; + SpectrumCycle.brightness_max = RGB_FUSION_GPU_BRIGHTNESS_MAX; + SpectrumCycle.brightness = RGB_FUSION_GPU_BRIGHTNESS_MAX; + modes.push_back(SpectrumCycle); + + SetupZones(); + + // Initialize active mode + active_mode = 0; +} + +RGBController_RGBFusionGPU::~RGBController_RGBFusionGPU() +{ + delete controller; +} + +void RGBController_RGBFusionGPU::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has one LED, so create a single zone and | + | LED for it | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = "GPU Zone"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = "GPU LED"; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zones.push_back(*new_zone); + + SetupColors(); +} + +void RGBController_RGBFusionGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RGBFusionGPU::DeviceUpdateLEDs() +{ + RGBColor color = colors[0]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetColor(red, grn, blu); +} + +void RGBController_RGBFusionGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RGBFusionGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RGBFusionGPU::DeviceUpdateMode() +{ + controller->SetMode((unsigned char)modes[(unsigned int)active_mode].value, (unsigned char)modes[(unsigned int)active_mode].speed, (unsigned char)modes[(unsigned int)active_mode].brightness); +} + +void RGBController_RGBFusionGPU::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->Save(); +} diff --git a/Controllers/GigabyteRGBFusionGPUController/RGBController_GigabyteRGBFusionGPU.h b/Controllers/GigabyteRGBFusionGPUController/RGBController_GigabyteRGBFusionGPU.h new file mode 100644 index 0000000..071506b --- /dev/null +++ b/Controllers/GigabyteRGBFusionGPUController/RGBController_GigabyteRGBFusionGPU.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteRGBFusionGPU.h | +| | +| RGBController for Gigabyte Aorus RGB Fusion GPU | +| | +| Adam Honse (CalcProgrammer1) 23 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteRGBFusionGPUController.h" + +class RGBController_RGBFusionGPU : public RGBController +{ +public: + RGBController_RGBFusionGPU(RGBFusionGPUController* controller_ptr); + ~RGBController_RGBFusionGPU(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + RGBFusionGPUController* controller; +}; diff --git a/Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBController.cpp b/Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBController.cpp new file mode 100644 index 0000000..268eb9c --- /dev/null +++ b/Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBController.cpp @@ -0,0 +1,142 @@ +/*---------------------------------------------------------*\ +| GigabyteSuperIORGBController.cpp | +| | +| Driver for Gigabyte Aorus Super IO motherboard | +| | +| Ryan Frankcombe (422gRdHuX5uk) 11 Sep 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "GigabyteSuperIORGBController.h" +#include "super_io.h" + +GigabyteSuperIORGBController::GigabyteSuperIORGBController(int sioaddr, std::string dev_name) +{ + gig_sioaddr = sioaddr; + name = dev_name; +} + +GigabyteSuperIORGBController::~GigabyteSuperIORGBController() +{ + +} + +std::string GigabyteSuperIORGBController::GetDeviceLocation() +{ + char hex[12]; + snprintf(hex, sizeof(hex), "0x%X", gig_sioaddr); + return("SIO: " + std::string(hex)); +} + +std::string GigabyteSuperIORGBController::GetDeviceName() +{ + return(name); +} + +void GigabyteSuperIORGBController::ChipEntry() +{ + /*--------------------------------*\ + | Chip Entry Command | + \*_-------------------------------*/ + superio_enter(gig_sioaddr); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_CHIPENTRY_REGISTER_1, GIGABYTE_SUPERIO_CHIPENTRY_VALUE_1); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_CHIPENTRY_REGISTER_2, GIGABYTE_SUPERIO_CHIPENTRY_VALUE_2); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_CHIPENTRY_REGISTER_2, GIGABYTE_SUPERIO_CHIPENTRY_VALUE_2); + + /*--------------------------------*\ + | Chip Select Command | + \*_-------------------------------*/ + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_CHIPSELECT_REGISTER_1, GIGABYTE_SUPERIO_CHIPSELECT_VALUE_1); +} + +void GigabyteSuperIORGBController::ChipExit() +{ + /*-----------------------------------------------------------------------------------*\ + | Chip Exit Command | + | Per https://pdf1.alldatasheetde.com/datasheet-pdf/download/1132513/ITE/IT8712F.html | + \*_----------------------------------------------------------------------------------*/ + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_CHIPEXIT_REGISTER_1, GIGABYTE_SUPERIO_CHIPEXIT_VALUE_1); +} + +void GigabyteSuperIORGBController::SetColor(unsigned int red, unsigned int green, unsigned int blue) +{ + /*--------------------------------*\ + | Chip Entry Command | + \*_-------------------------------*/ + ChipEntry(); + + /*--------------------------------*\ + | Set Colors | + \*_-------------------------------*/ + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_RED_REGISTER_1, red); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_GREEN_REGISTER_1, green); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_BLUE_REGISTER_1, blue); + + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_RED_REGISTER_2, red); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_GREEN_REGISTER_2, green); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_BLUE_REGISTER_2, blue); + + /*--------------------------------*\ + | Chip Exit Command | + \*_-------------------------------*/ + ChipExit(); +} + +void GigabyteSuperIORGBController::SetMode(int new_mode) +{ + if(new_mode>=GIGABYTE_MODE1_STATIC && new_mode<=GIGABYTE_MODE1_FLASHING) + { + ChipEntry(); + } + + /*-----------------------------------------------------*\ + | Write the colors to the color sequence registers | + \*-----------------------------------------------------*/ + switch (new_mode) + { + case GIGABYTE_MODE1_STATIC: + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_STATIC_REGISTER_1, GIGABYTE_SUPERIO_STATIC_VALUE_1); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_STATIC_REGISTER_2, GIGABYTE_SUPERIO_STATIC_VALUE_2); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_STATIC_REGISTER_3, GIGABYTE_SUPERIO_STATIC_VALUE_3); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_STATIC_REGISTER_4, GIGABYTE_SUPERIO_STATIC_VALUE_4); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_STATIC_REGISTER_5, GIGABYTE_SUPERIO_STATIC_VALUE_5); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_STATIC_REGISTER_6, GIGABYTE_SUPERIO_STATIC_VALUE_6); + break; + + case GIGABYTE_MODE1_RAINBOW: + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_RAINBOW_REGISTER_1, GIGABYTE_SUPERIO_RAINBOW_VALUE_1); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_RAINBOW_REGISTER_2, GIGABYTE_SUPERIO_RAINBOW_VALUE_2); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_RAINBOW_REGISTER_3, GIGABYTE_SUPERIO_RAINBOW_VALUE_3); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_RAINBOW_REGISTER_4, GIGABYTE_SUPERIO_RAINBOW_VALUE_4); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_RAINBOW_REGISTER_5, GIGABYTE_SUPERIO_RAINBOW_VALUE_5); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_RAINBOW_REGISTER_6, GIGABYTE_SUPERIO_RAINBOW_VALUE_6); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_RAINBOW_REGISTER_7, GIGABYTE_SUPERIO_RAINBOW_VALUE_7); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_RAINBOW_REGISTER_8, GIGABYTE_SUPERIO_RAINBOW_VALUE_8); + break; + + case GIGABYTE_MODE1_BREATHING: + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_BREATHING_REGISTER_1, GIGABYTE_SUPERIO_BREATHING_VALUE_1); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_BREATHING_REGISTER_2, GIGABYTE_SUPERIO_BREATHING_VALUE_2); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_BREATHING_REGISTER_3, GIGABYTE_SUPERIO_BREATHING_VALUE_3); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_BREATHING_REGISTER_4, GIGABYTE_SUPERIO_BREATHING_VALUE_4); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_BREATHING_REGISTER_5, GIGABYTE_SUPERIO_BREATHING_VALUE_5); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_BREATHING_REGISTER_6, GIGABYTE_SUPERIO_BREATHING_VALUE_6); + break; + + case GIGABYTE_MODE1_FLASHING: + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_FLASHING_REGISTER_1, GIGABYTE_SUPERIO_FLASHING_VALUE_1); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_FLASHING_REGISTER_2, GIGABYTE_SUPERIO_FLASHING_VALUE_2); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_FLASHING_REGISTER_3, GIGABYTE_SUPERIO_FLASHING_VALUE_3); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_FLASHING_REGISTER_4, GIGABYTE_SUPERIO_FLASHING_VALUE_4); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_FLASHING_REGISTER_5, GIGABYTE_SUPERIO_FLASHING_VALUE_5); + superio_outb(gig_sioaddr, GIGABYTE_SUPERIO_FLASHING_REGISTER_6, GIGABYTE_SUPERIO_FLASHING_VALUE_6); + break; + } + + if(new_mode>=GIGABYTE_MODE1_STATIC && new_mode<=GIGABYTE_MODE1_FLASHING) + { + ChipExit(); + } +} diff --git a/Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBController.h b/Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBController.h new file mode 100644 index 0000000..15c2dba --- /dev/null +++ b/Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBController.h @@ -0,0 +1,150 @@ +/*---------------------------------------------------------*\ +| GigabyteSuperIORGBController.h | +| | +| Driver for Gigabyte Aorus Super IO motherboard | +| | +| Ryan Frankcombe (422gRdHuX5uk) 11 Sep 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +enum +{ + /*--------------------------------*\ + | Chip Entry Registers and Values | + \*_-------------------------------*/ + GIGABYTE_SUPERIO_CHIPENTRY_REGISTER_1 = 0x01, + GIGABYTE_SUPERIO_CHIPENTRY_REGISTER_2 = 0x55, + GIGABYTE_SUPERIO_CHIPENTRY_VALUE_1 = 0x00, + GIGABYTE_SUPERIO_CHIPENTRY_VALUE_2 = 0x00, + + /*--------------------------------*\ + | Chip Select Registers and Values | + \*_-------------------------------*/ + GIGABYTE_SUPERIO_CHIPSELECT_REGISTER_1 = 0x07, + GIGABYTE_SUPERIO_CHIPSELECT_VALUE_1 = 0x00, + + /*--------------------------------*\ + | Chip Exit Registers and Values | + \*_-------------------------------*/ + GIGABYTE_SUPERIO_CHIPEXIT_REGISTER_1 = 0x02, + GIGABYTE_SUPERIO_CHIPEXIT_VALUE_1 = 0x01, +}; + +enum +{ + /*--------------------------------*\ + | Chip Color Registers | + \*_-------------------------------*/ + GIGABYTE_SUPERIO_RED_REGISTER_1 = 0xB3, + GIGABYTE_SUPERIO_RED_REGISTER_2 = 0xC3, + GIGABYTE_SUPERIO_GREEN_REGISTER_1 = 0xB4, + GIGABYTE_SUPERIO_GREEN_REGISTER_2 = 0xC4, + GIGABYTE_SUPERIO_BLUE_REGISTER_1 = 0xB5, + GIGABYTE_SUPERIO_BLUE_REGISTER_2 = 0xC5, +}; + +enum +{ + /*--------------------------------*\ + | Chip Modes | + \*_-------------------------------*/ + GIGABYTE_MODE1_STATIC = 0x00, /* Mode 1 static effect */ + GIGABYTE_MODE1_RAINBOW = 0x01, /* Mode 1 rainbow effect */ + GIGABYTE_MODE1_BREATHING = 0x02, /* Mode 1 breathing effect */ + GIGABYTE_MODE1_FLASHING = 0x03, /* Mode 1 flashing effect */ + + /*--------------------------------*\ + | Chip Static Mode Registers | + \*_-------------------------------*/ + GIGABYTE_SUPERIO_STATIC_REGISTER_1 = 0xB0, + GIGABYTE_SUPERIO_STATIC_REGISTER_2 = 0xB1, + GIGABYTE_SUPERIO_STATIC_REGISTER_3 = 0xB2, + GIGABYTE_SUPERIO_STATIC_REGISTER_4 = 0xC0, + GIGABYTE_SUPERIO_STATIC_REGISTER_5 = 0xC1, + GIGABYTE_SUPERIO_STATIC_REGISTER_6 = 0xC2, + GIGABYTE_SUPERIO_STATIC_VALUE_1 = 0x0F, + GIGABYTE_SUPERIO_STATIC_VALUE_2 = 0x00, + GIGABYTE_SUPERIO_STATIC_VALUE_3 = 0x20, + GIGABYTE_SUPERIO_STATIC_VALUE_4 = 0x0F, + GIGABYTE_SUPERIO_STATIC_VALUE_5 = 0x00, + GIGABYTE_SUPERIO_STATIC_VALUE_6 = 0x10, + + /*--------------------------------*\ + | Chip Rainbow Mode Registers | + \*_-------------------------------*/ + GIGABYTE_SUPERIO_RAINBOW_REGISTER_1 = 0xB0, + GIGABYTE_SUPERIO_RAINBOW_REGISTER_2 = 0xB1, + GIGABYTE_SUPERIO_RAINBOW_REGISTER_3 = 0xB2, + GIGABYTE_SUPERIO_RAINBOW_REGISTER_4 = 0xB0, + GIGABYTE_SUPERIO_RAINBOW_REGISTER_5 = 0xC0, + GIGABYTE_SUPERIO_RAINBOW_REGISTER_6 = 0xC1, + GIGABYTE_SUPERIO_RAINBOW_REGISTER_7 = 0xC2, + GIGABYTE_SUPERIO_RAINBOW_REGISTER_8 = 0xC0, + GIGABYTE_SUPERIO_RAINBOW_VALUE_1 = 0x00, + GIGABYTE_SUPERIO_RAINBOW_VALUE_2 = 0x00, + GIGABYTE_SUPERIO_RAINBOW_VALUE_3 = 0xA0, + GIGABYTE_SUPERIO_RAINBOW_VALUE_4 = 0x7F, + GIGABYTE_SUPERIO_RAINBOW_VALUE_5 = 0x00, + GIGABYTE_SUPERIO_RAINBOW_VALUE_6 = 0x00, + GIGABYTE_SUPERIO_RAINBOW_VALUE_7 = 0x90, + GIGABYTE_SUPERIO_RAINBOW_VALUE_8 = 0x7F, + + /*--------------------------------*\ + | Chip Breathing Mode Registers | + \*_-------------------------------*/ + GIGABYTE_SUPERIO_BREATHING_REGISTER_1 = 0xB0, + GIGABYTE_SUPERIO_BREATHING_REGISTER_2 = 0xB1, + GIGABYTE_SUPERIO_BREATHING_REGISTER_3 = 0xB2, + GIGABYTE_SUPERIO_BREATHING_REGISTER_4 = 0xC0, + GIGABYTE_SUPERIO_BREATHING_REGISTER_5 = 0xC1, + GIGABYTE_SUPERIO_BREATHING_REGISTER_6 = 0xC2, + GIGABYTE_SUPERIO_BREATHING_VALUE_1 = 0x8F, + GIGABYTE_SUPERIO_BREATHING_VALUE_2 = 0x00, + GIGABYTE_SUPERIO_BREATHING_VALUE_3 = 0x20, + GIGABYTE_SUPERIO_BREATHING_VALUE_4 = 0x8F, + GIGABYTE_SUPERIO_BREATHING_VALUE_5 = 0x00, + GIGABYTE_SUPERIO_BREATHING_VALUE_6 = 0x10, + + /*--------------------------------*\ + | Chip Flashing Mode Registers | + \*_-------------------------------*/ + GIGABYTE_SUPERIO_FLASHING_REGISTER_1 = 0xB0, + GIGABYTE_SUPERIO_FLASHING_REGISTER_2 = 0xB1, + GIGABYTE_SUPERIO_FLASHING_REGISTER_3 = 0xB2, + GIGABYTE_SUPERIO_FLASHING_REGISTER_4 = 0xC0, + GIGABYTE_SUPERIO_FLASHING_REGISTER_5 = 0xC1, + GIGABYTE_SUPERIO_FLASHING_REGISTER_6 = 0xC2, + GIGABYTE_SUPERIO_FLASHING_VALUE_1 = 0x0F, + GIGABYTE_SUPERIO_FLASHING_VALUE_2 = 0x08, + GIGABYTE_SUPERIO_FLASHING_VALUE_3 = 0x20, + GIGABYTE_SUPERIO_FLASHING_VALUE_4 = 0x0F, + GIGABYTE_SUPERIO_FLASHING_VALUE_5 = 0x08, + GIGABYTE_SUPERIO_FLASHING_VALUE_6 = 0x10, + +}; + +class GigabyteSuperIORGBController +{ +public: + GigabyteSuperIORGBController(int sioaddr, std::string dev_name); + ~GigabyteSuperIORGBController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + unsigned int GetMode(); + void SetMode(int new_mode); + + void SetColor(unsigned int red, unsigned int green, unsigned int blue); + void ChipEntry(); + void ChipExit(); +private: + int gig_sioaddr; + std::string name; +}; diff --git a/Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBControllerDetect.cpp b/Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBControllerDetect.cpp new file mode 100644 index 0000000..806117b --- /dev/null +++ b/Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBControllerDetect.cpp @@ -0,0 +1,70 @@ +/*---------------------------------------------------------*\ +| GigabyteSuperIORGBControllerDetect.cpp | +| | +| Detector for Gigabyte Aorus Super IO motherboard | +| | +| Ryan Frankcombe (422gRdHuX5uk) 11 Sep 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "GigabyteSuperIORGBController.h" +#include "RGBController_GigabyteSuperIORGB.h" +#include "super_io.h" +#include "dmiinfo.h" + +#define NUM_COMPATIBLE_DEVICES (sizeof(compatible_devices) / sizeof(compatible_devices[0])) + +typedef struct +{ + const char* name; +} gig_device; + +static gig_device compatible_devices[] = +{ + {"X570 UD"} +}; + +void DetectGigabyteSuperIORGBControllers() +{ + int sio_addrs[2] = {0x2E, 0x4E}; + + DMIInfo board; + std::string board_dmi = board.getMainboard(); + std::string manufacturer = board.getManufacturer(); + + if(manufacturer != "Gigabyte Technology Co., Ltd.") + { + return; + } + + for(int sioaddr_idx = 0; sioaddr_idx < 2; sioaddr_idx++) + { + int sioaddr = sio_addrs[sioaddr_idx]; + + superio_enter(sioaddr); + + int val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) | superio_inb(sioaddr, SIO_REG_DEVID + 1); + + switch(val & SIO_ID_MASK) + { + case SIO_ITE8688_ID: + for(unsigned int i = 0; i < NUM_COMPATIBLE_DEVICES; i++) + { + if(board_dmi.find(std::string(compatible_devices[i].name)) != std::string::npos) + { + GigabyteSuperIORGBController* controller = new GigabyteSuperIORGBController(sioaddr, "Gigabyte " + board_dmi); + RGBController_GigabyteSuperIORGB* rgb_controller = new RGBController_GigabyteSuperIORGB(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + break; + } + } + break; + } + } +} /* DetectGigabyteSuperIORGBControllers() */ + +REGISTER_DETECTOR("Gigabyte RGB", DetectGigabyteSuperIORGBControllers); diff --git a/Controllers/GigabyteSuperIORGBController/RGBController_GigabyteSuperIORGB.cpp b/Controllers/GigabyteSuperIORGBController/RGBController_GigabyteSuperIORGB.cpp new file mode 100644 index 0000000..07b77a4 --- /dev/null +++ b/Controllers/GigabyteSuperIORGBController/RGBController_GigabyteSuperIORGB.cpp @@ -0,0 +1,187 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteSuperIORGB.cpp | +| | +| RGBController for Gigabyte Aorus Super IO motherboard | +| | +| Ryan Frankcombe (422gRdHuX5uk) 11 Sep 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_GigabyteSuperIORGB.h" + +/**------------------------------------------------------------------*\ + @name Gigabyte SuperIO RGB + @category Motherboard + @type SuperIO + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectGigabyteSuperIORGBControllers + @comment + Testing was done on an ITE8688 Chipset, and adds the support for motherboards that can only be controlled with Gigabyte’s “Ambient Led” NOT EITHER RGB Fusion 1.0/2.0. + + You should first check to confirm that you DO NOT HAVE EITHER A USB OR SMBUS controllable chipset, use the following powershell command to confirm if you have a USB device: + ```powershell + gwmi Win32_USBControllerDevice |%{[wmi]($_.Dependent)} | Sort Manufacturer,Description,DeviceID | Ft -GroupBy Manufacturer Description,Service,DeviceID + ``` + + If you see anything with a VID of 048D or 8297, DO NOT PROCEED, the software is likely supportable by OpenRGB's Fusion2 USB controller. + + After that check output on the SMBus: Using the OpenRGB I2C Sniffer to confirm that no data is output on either bus while changing colors in “Ambient LED”, if either bus outputs data, DO NOT PROCEED, the software is likely supportable in Gigabyte RGB Fusion. + + The following chipsets are likely supported: + | Chipset ID | + | :---: | + | ITE8620E | + | ITE8626 | + | ITE8686E | + | ITE8688 | + | ITE8689 | + | ITE8728F | + | ITE8790F | + | ITE8791E | + + To confirm your chipset, you can open CPU-Z, the MCU chipset model is found under Mainboard like below: + + To confirm that your RGB is compatible with this Controller in OpenRGB, you will need to add the motherboard DMI Model name, which is the output of the following powershell command: + + ```powershell + wmic baseboard get product + ``` + + To the struct array that is found in GigabyteSuperIORGBControllerDetect.cpp, like below + + ```c++ + gig_device compatible_devices[] = + { + {"X570 UD"}, + {"EXAMPLEBASEBOARDPRODUCTNAME"} + }; + ``` + + If your chipset is also NOT an ITE8688, you will also need to add the chipset, to get this is a bit harder, for myself I added a breakpoint, on this line, in GigabyteSuperIORGBControllerDetect.cpp: + ```c++ + switch (val & SIO_ID_MASK) + ``` + + For an ITE8688, the value was 0x8688 in Hexidecimal, it is likely the case with other models as well that the code matches the MCU model. + + To add this chipset you will need to first add it to the SuperIO definitions file, which is named super_io.h like below + ```c++ + #define SIO_ITE8688_ID 0x8688 // Device ID for ITE8688 (8688) + #define SIO_NEWCHIPSETMODEL_ID 0xFOUNDHEXIDECIMALVALUE // Device ID for NEWCHIPSETMODEL (FOUNDHEXIDECIMALVALUE) + ``` + + And lastly you will need to add the chip to the + + GigabyteSuperIORGBControllerDetect.cpp like below: + ```c++ + switch (val & SIO_ID_MASK) + { + case SIO_ITE8688_ID: + case SIO_NEWCHIPSETMODEL_ID: + ``` +\*-------------------------------------------------------------------*/ + +RGBController_GigabyteSuperIORGB::RGBController_GigabyteSuperIORGB(GigabyteSuperIORGBController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Gigabyte"; + type = DEVICE_TYPE_MOTHERBOARD; + description = "Gigabyte SuperIO RGB Device"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = GIGABYTE_MODE1_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = GIGABYTE_MODE1_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Rainbow.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Rainbow); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = GIGABYTE_MODE1_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = GIGABYTE_MODE1_FLASHING; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flashing); + + SetupZones(); +} + +RGBController_GigabyteSuperIORGB::~RGBController_GigabyteSuperIORGB() +{ + delete controller; +} + +void RGBController_GigabyteSuperIORGB::SetupZones() +{ + zone gig_zone; + gig_zone.name = "Gigabyte Zone"; + gig_zone.type = ZONE_TYPE_SINGLE; + gig_zone.leds_min = 1; + gig_zone.leds_max = 1; + gig_zone.leds_count = 1; + gig_zone.matrix_map = NULL; + zones.push_back(gig_zone); + + led gig_led; + gig_led.name = "LED_C1"; + leds.push_back(gig_led); + + SetupColors(); +} + +void RGBController_GigabyteSuperIORGB::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_GigabyteSuperIORGB::DeviceUpdateLEDs() +{ + RGBColor color = colors[0]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetColor(red, grn, blu); +} + +void RGBController_GigabyteSuperIORGB::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GigabyteSuperIORGB::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_GigabyteSuperIORGB::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].value); +} diff --git a/Controllers/GigabyteSuperIORGBController/RGBController_GigabyteSuperIORGB.h b/Controllers/GigabyteSuperIORGBController/RGBController_GigabyteSuperIORGB.h new file mode 100644 index 0000000..79a55a8 --- /dev/null +++ b/Controllers/GigabyteSuperIORGBController/RGBController_GigabyteSuperIORGB.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_GigabyteSuperIORGB.h | +| | +| RGBController for Gigabyte Aorus Super IO motherboard | +| | +| Ryan Frankcombe (422gRdHuX5uk) 11 Sep 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GigabyteSuperIORGBController.h" + +class RGBController_GigabyteSuperIORGB : public RGBController +{ +public: + RGBController_GigabyteSuperIORGB(GigabyteSuperIORGBController* controller_ptr); + ~RGBController_GigabyteSuperIORGB(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + GigabyteSuperIORGBController* controller; +}; diff --git a/Controllers/GoveeController/GoveeController.cpp b/Controllers/GoveeController/GoveeController.cpp new file mode 100644 index 0000000..0e862f9 --- /dev/null +++ b/Controllers/GoveeController/GoveeController.cpp @@ -0,0 +1,352 @@ +/*---------------------------------------------------------*\ +| GoveeController.cpp | +| | +| Driver for Govee wireless lighting devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 01 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "base64.hpp" +#include "GoveeController.h" + +using json = nlohmann::json; +using namespace std::chrono_literals; + +base64::byte CalculateXorChecksum(std::vector packet) +{ + base64::byte checksum = 0; + + for(unsigned int i = 0; i < packet.size(); i++) + { + checksum ^= packet[i]; + } + + return(checksum); +} + +GoveeController::GoveeController(std::string ip) +{ + /*-----------------------------------------------------*\ + | Fill in location string with device's IP address | + \*-----------------------------------------------------*/ + ip_address = ip; + + /*-----------------------------------------------------*\ + | Register callback for receiving broadcasts | + \*-----------------------------------------------------*/ + RegisterReceiveBroadcastCallback(this); + + broadcast_received = false; + + /*-----------------------------------------------------*\ + | Request device information | + \*-----------------------------------------------------*/ + SendScan(); + + /*-----------------------------------------------------*\ + | Wait up to 5s for device information to be received | + \*-----------------------------------------------------*/ + for(unsigned int wait_count = 0; wait_count < 500; wait_count++) + { + if(broadcast_received) + { + break; + } + + std::this_thread::sleep_for(10ms); + } + + /*-----------------------------------------------------*\ + | Open a UDP client sending to the Govee device IP, | + | port 4003 | + \*-----------------------------------------------------*/ + port.udp_client(ip_address.c_str(), "4003"); +} + +GoveeController::~GoveeController() +{ + UnregisterReceiveBroadcastCallback(this); +} + +std::string GoveeController::GetLocation() +{ + return("IP: " + ip_address); +} + +std::string GoveeController::GetSku() +{ + return(sku); +} + +std::string GoveeController::GetVersion() +{ + return("BLE Hardware Version: " + bleVersionHard + "\r\n" + + "BLE Software Version: " + bleVersionSoft + "\r\n" + + "WiFi Hardware Version: " + wifiVersionHard + "\r\n" + + "WiFI Software Version: " + wifiVersionSoft + "\r\n"); +} + +void GoveeController::ReceiveBroadcast(char* recv_buf, int size) +{ + if(broadcast_received) + { + return; + } + + /*-----------------------------------------------------*\ + | Responses are not null-terminated, so add termination | + \*-----------------------------------------------------*/ + recv_buf[size] = '\0'; + + /*-----------------------------------------------------*\ + | Convert null-terminated response to JSON | + \*-----------------------------------------------------*/ + json response = json::parse(recv_buf); + + /*-----------------------------------------------------*\ + | Check if the response contains the method name | + \*-----------------------------------------------------*/ + if(response.contains("msg")) + { + /*-------------------------------------------------*\ + | Handle responses for scan command | + | This command's response should contain a msg | + | object containing a data member with ip, device, | + | sku, among others. | + \*-------------------------------------------------*/ + if(response["msg"].contains("cmd")) + { + if(response["msg"]["cmd"] == "scan") + { + if(response["msg"].contains("data")) + { + if(response["msg"]["data"].contains("ip")) + { + if(response["msg"]["data"]["ip"] == ip_address) + { + if(response["msg"]["data"].contains("sku")) + { + sku = response["msg"]["data"]["sku"]; + } + + if(response["msg"]["data"].contains("bleVersionHard")) + { + bleVersionHard = response["msg"]["data"]["bleVersionHard"]; + } + + if(response["msg"]["data"].contains("bleVersionSoft")) + { + bleVersionSoft = response["msg"]["data"]["bleVersionSoft"]; + } + + if(response["msg"]["data"].contains("wifiVersionHard")) + { + wifiVersionHard = response["msg"]["data"]["wifiVersionHard"]; + } + + if(response["msg"]["data"].contains("wifiVersionSoft")) + { + wifiVersionSoft = response["msg"]["data"]["wifiVersionSoft"]; + } + + broadcast_received = true; + } + } + } + } + } + } +} + +void GoveeController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + json command; + + command["msg"]["cmd"] = "colorwc"; + command["msg"]["data"]["color"]["r"] = red; + command["msg"]["data"]["color"]["g"] = green; + command["msg"]["data"]["color"]["b"] = blue; + command["msg"]["data"]["colorTemInKelvin"] = "0"; + + /*-----------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------*/ + std::string command_str = command.dump(); + + port.udp_write((char *)command_str.c_str(), (int)command_str.length() + 1); +} + +void GoveeController::SendRazerData(RGBColor* colors, unsigned int size) +{ + /*-----------------------------------------------------*\ + | Do not send an empty frame (this was producing | + | length=2, count=0) | + \*-----------------------------------------------------*/ + if(size == 0) + { + return; + } + + /*-----------------------------------------------------*\ + | PT payload: BB [len_hi] [len_lo] B0 [gradient_off=1] | + | [led_count] (RGB * N) [xor] | + | length = 2 + 3*N (bytes after 0xB0: gradient_off + | + | led_count + RGB*count) | + \*-----------------------------------------------------*/ + const unsigned int count = std::min(size, 255u); + const unsigned int payload_len = 2 + (3 * count); + + /*-----------------------------------------------------*\ + | Create buffer with fixed size and fill sequentially | + \*-----------------------------------------------------*/ + + std::vector pkt; + pkt.reserve(7 + (3 * count)); + + pkt.push_back(0xBB); + pkt.push_back(static_cast((payload_len >> 8) & 0xFF)); /* len_hi */ + pkt.push_back(static_cast(payload_len & 0xFF)); /* len_lo */ + pkt.push_back(0xB0); /* subcommand */ + pkt.push_back(0x01); /* gradient_off = 1 */ + pkt.push_back(static_cast(count)); /* led_count */ + + for(std::size_t led_idx = 0; led_idx < count; led_idx++) + { + pkt.push_back(RGBGetRValue(colors[led_idx])); + pkt.push_back(RGBGetGValue(colors[led_idx])); + pkt.push_back(RGBGetBValue(colors[led_idx])); + } + + pkt.push_back(CalculateXorChecksum(pkt)); + + json command; + command["msg"]["cmd"] = "razer"; + command["msg"]["data"]["pt"] = base64::encode(pkt); + + /*-----------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------*/ + std::string command_str = command.dump(); + + port.udp_write((char *)command_str.c_str(), (int)command_str.length() + 1); +} + +void GoveeController::SendRazerDisable() +{ + const std::vector pkt = { 0xBB, 0x00, 0x01, 0xB1, 0x00, 0x0B }; + json command; + + command["msg"]["cmd"] = "razer"; + command["msg"]["data"]["pt"] = base64::encode(pkt); + + /*-----------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------*/ + std::string command_str = command.dump(); + + port.udp_write((char *)command_str.c_str(), (int)command_str.length() + 1); +} + +void GoveeController::SendRazerEnable() +{ + const std::vector pkt = { 0xBB, 0x00, 0x01, 0xB1, 0x01, 0x0A }; + json command; + + command["msg"]["cmd"] = "razer"; + command["msg"]["data"]["pt"] = base64::encode(pkt); + + /*-----------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------*/ + std::string command_str = command.dump(); + + port.udp_write((char *)command_str.c_str(), (int)command_str.length() + 1); +} + +void GoveeController::SendScan() +{ + json command; + + command["msg"]["cmd"] = "scan"; + /*-----------------------------------------------------*\ + | Matches what Govee devices commonly accept for LAN | + | scan | + \*-----------------------------------------------------*/ + command["msg"]["data"]["account_topic"] = "reserve"; + + /*-----------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------*/ + std::string command_str = command.dump(); + + broadcast_port.udp_write((char *)command_str.c_str(), (int)command_str.length() + 1); +} + +/*---------------------------------------------------------*\ +| Static class members for shared broadcast receiver | +\*---------------------------------------------------------*/ +net_port GoveeController::broadcast_port; +std::vector GoveeController::callbacks; +std::thread* GoveeController::ReceiveThread; +std::atomic GoveeController::ReceiveThreadRun; + +void GoveeController::ReceiveBroadcastThreadFunction() +{ + char recv_buf[1024]; + + broadcast_port.set_receive_timeout(1, 0); + + while(ReceiveThreadRun.load()) + { + /*-------------------------------------------------*\ + | Receive up to 1024 bytes from the device with a | + | 1s timeout | + \*-------------------------------------------------*/ + int size = broadcast_port.udp_listen(recv_buf, 1024); + + /*-------------------------------------------------*\ + | If data was received, loop through registered | + | callback controllers and call the | + | ReceiveBroadcast function for the controller | + | matching the received data | + | | + | NOTE: As implemented, it doesn't actually match | + | the intended controller and just calls all | + | registered controllers. As they are all called | + | sequence, this should work, but if parallel calls | + | are ever needed, receives should be filtered by | + | IP address | + \*-------------------------------------------------*/ + if(size > 0) + { + for(std::size_t callback_idx = 0; callback_idx < callbacks.size(); callback_idx++) + { + GoveeController* controller = callbacks[callback_idx]; + + controller->ReceiveBroadcast(recv_buf, size); + } + } + } +} + +void GoveeController::RegisterReceiveBroadcastCallback(GoveeController* controller_ptr) +{ + callbacks.push_back(controller_ptr); +} + +void GoveeController::UnregisterReceiveBroadcastCallback(GoveeController* controller_ptr) +{ + for(std::size_t callback_idx = 0; callback_idx < callbacks.size(); callback_idx++) + { + if(callbacks[callback_idx] == controller_ptr) + { + callbacks.erase(callbacks.begin() + callback_idx); + break; + } + } +} diff --git a/Controllers/GoveeController/GoveeController.h b/Controllers/GoveeController/GoveeController.h new file mode 100644 index 0000000..71ed008 --- /dev/null +++ b/Controllers/GoveeController/GoveeController.h @@ -0,0 +1,71 @@ +/*---------------------------------------------------------*\ +| GoveeController.h | +| | +| Driver for Govee wireless lighting devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 01 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "net_port.h" + +class GoveeController +{ +public: + GoveeController(std::string ip); + ~GoveeController(); + + std::string GetLocation(); + std::string GetSku(); + std::string GetVersion(); + + void ReceiveBroadcast(char* recv_buf, int size); + + void SendRazerData(RGBColor* colors, unsigned int size); + void SendRazerDisable(); + void SendRazerEnable(); + + void SendScan(); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + +private: + std::string firmware_version; + std::string ip_address; + std::string module_name; + std::string module_mac; + + std::string sku; + std::string bleVersionHard; + std::string bleVersionSoft; + std::string wifiVersionHard; + std::string wifiVersionSoft; + + bool broadcast_received; + + net_port port; + +public: + /*-----------------------------------------------------*\ + | One receive thread is shared among all instances of | + | GoveeController, so the receive thread function is | + | static and the thread is initialized in the detector | + | if any GoveeControllers are created. | + \*-----------------------------------------------------*/ + static net_port broadcast_port; + static std::vector callbacks; + static std::thread* ReceiveThread; + static std::atomic ReceiveThreadRun; + + static void ReceiveBroadcastThreadFunction(); + static void RegisterReceiveBroadcastCallback(GoveeController* controller_ptr); + static void UnregisterReceiveBroadcastCallback(GoveeController* controller_ptr); +}; diff --git a/Controllers/GoveeController/GoveeControllerDetect.cpp b/Controllers/GoveeController/GoveeControllerDetect.cpp new file mode 100644 index 0000000..967085c --- /dev/null +++ b/Controllers/GoveeController/GoveeControllerDetect.cpp @@ -0,0 +1,92 @@ +/*---------------------------------------------------------*\ +| GoveeControllerDetect.cpp | +| | +| Detector for Govee wireless lighting devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 01 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "Detector.h" +#include "GoveeController.h" +#include "RGBController.h" +#include "RGBController_Govee.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectGoveeControllers * +* * +* Detect Govee devices * +* * +\******************************************************************************************/ + +void DetectGoveeControllers() +{ + json govee_settings; + + /*-----------------------------------------------------*\ + | Get Govee settings from settings manager | + \*-----------------------------------------------------*/ + govee_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("GoveeDevices"); + + /*-----------------------------------------------------*\ + | If the Govee settings contains devices, process | + \*-----------------------------------------------------*/ + if(govee_settings.contains("devices")) + { + GoveeController::ReceiveThreadRun = false; + + if(govee_settings["devices"].size() > 0) + { + /*---------------------------------------------*\ + | Open a UDP client sending to and receiving | + | from the Govee Multicast IP, send port 4001 | + | and receive port 4002 | + \*---------------------------------------------*/ + GoveeController::broadcast_port.udp_client("239.255.255.250", "4001", "4002"); + GoveeController::broadcast_port.udp_join_multicast_group("239.255.255.250"); + + /*---------------------------------------------*\ + | Start a thread to handle responses received | + | from the Govee device | + \*---------------------------------------------*/ + GoveeController::ReceiveThreadRun = true; + GoveeController::ReceiveThread = new std::thread(&GoveeController::ReceiveBroadcastThreadFunction); + } + + for(unsigned int device_idx = 0; device_idx < govee_settings["devices"].size(); device_idx++) + { + if(govee_settings["devices"][device_idx].contains("ip")) + { + std::string govee_ip = govee_settings["devices"][device_idx]["ip"]; + + GoveeController* controller = new GoveeController(govee_ip); + RGBController_Govee* rgb_controller = new RGBController_Govee(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + + /*-------------------------------------------------*\ + | All controllers have been created, the broadcast | + | receiver thread is no longer needed and can be | + | shut down | + \*-------------------------------------------------*/ + if(GoveeController::ReceiveThreadRun) + { + GoveeController::ReceiveThreadRun = false; + GoveeController::ReceiveThread->join(); + delete GoveeController::ReceiveThread; + GoveeController::broadcast_port.tcp_close(); + } + } + +} /* DetectGoveeControllers() */ + +REGISTER_DETECTOR("Govee", DetectGoveeControllers); diff --git a/Controllers/GoveeController/RGBController_Govee.cpp b/Controllers/GoveeController/RGBController_Govee.cpp new file mode 100644 index 0000000..b345f85 --- /dev/null +++ b/Controllers/GoveeController/RGBController_Govee.cpp @@ -0,0 +1,228 @@ +/*---------------------------------------------------------*\ +| RGBController_Govee.cpp | +| | +| RGBController for Govee wireless lighting devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 27 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "RGBController_Govee.h" + +using namespace std::chrono_literals; + +struct GoveeHardwareInfo +{ + unsigned int led_count; + unsigned int matrix_row_len; // 0 = linear +}; + +const unsigned int GOVEE_FALLBACK_LED_COUNT = 20; + +static std::map govee_hardware_info +{ + { "H6022", { 132, 12 } }, // Govee Smart Table Lamp 2 + { "H612F", { 12, 0 } }, // Govee Strip Light S (3m) + { "H619A", { 20, 0 } }, // Govee RGBIC Led Strip Lights + { "H70B1", { 20, 0 } }, // Govee LED Curtain Lights + { "H607C", { 174, 0 } }, // Govee Floor Lamp 2 +}; + +RGBController_Govee::RGBController_Govee(GoveeController* controller_ptr) +{ + controller = controller_ptr; + + name = "Govee " + controller->GetSku(); + vendor = "Govee"; + type = DEVICE_TYPE_LIGHT; + description = "Govee Device"; + location = controller->GetLocation(); + version = controller->GetVersion(); + + mode Static; + Static.name = "Static"; + Static.value = 1; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_Govee::KeepaliveThread, this); +} + +RGBController_Govee::~RGBController_Govee() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_Govee::SetupZones() +{ + GoveeHardwareInfo hw = { GOVEE_FALLBACK_LED_COUNT, 0 }; + bool resizable = true; + + std::map::iterator it = govee_hardware_info.find(controller->GetSku()); + if(it != govee_hardware_info.end()) + { + hw = it->second; + resizable = false; + } + + zone strip; + strip.leds_count = hw.led_count; + strip.leds_min = resizable ? 0 : hw.led_count; + strip.leds_max = resizable ? 255 : hw.led_count; + + if(hw.matrix_row_len == 0) + { + strip.name = "Govee Strip"; + strip.type = ZONE_TYPE_LINEAR; + strip.matrix_map = NULL; + } + else + { + strip.name = "Govee Matrix"; + strip.type = ZONE_TYPE_MATRIX; + + unsigned int width = hw.matrix_row_len; + unsigned int height = hw.led_count / width; + + strip.matrix_map = new matrix_map_type; + strip.matrix_map->height = height; + strip.matrix_map->width = width; + strip.matrix_map->map = new unsigned int[hw.led_count]; + + /*-----------------------------------------------------*\ + | On H6022, LEDs indexed bottom to top, alternating | + | clockwise and counterclockwise for each row. | + \*-----------------------------------------------------*/ + for(unsigned int y = 0; y < height; y++) + { + /*-------------------------------------------------*\ + | LEDs numbered bottom to top, opposite of matrix | + | clockwise and counterclockwise for each row. | + \*-------------------------------------------------*/ + unsigned int led_y = (height - 1) - y; + + for(unsigned int x = 0; x < width; x++) + { + /*---------------------------------------------*\ + | LED is right-to-left for even rows, including | + | first one | + \*---------------------------------------------*/ + unsigned int led_x = led_y & 1 ? x : (width - 1) - x; + strip.matrix_map->map[y * width + x] = led_y * width + led_x; + } + } + } + + zones.push_back(strip); + + for(std::size_t led_idx = 0; led_idx < strip.leds_count; led_idx++) + { + led strip_led; + strip_led.name = "Govee LED " + std::to_string(led_idx); + leds.push_back(strip_led); + } + + SetupColors(); +} + +void RGBController_Govee::ResizeZone(int zone, int new_size) +{ + if(zones[zone].type == ZONE_TYPE_MATRIX) + { + return; + } + + if(zone < 0 || zone >= (int)zones.size() || new_size <= 0) + { + return; + } + + new_size = std::max(1, std::min(255, new_size)); + zones[zone].leds_count = new_size; + zones[zone].leds_min = 1; + zones[zone].leds_max = 255; + + leds.clear(); + leds.resize(new_size); + for(int i = 0; i < new_size; ++i) + { + leds[i].name = "Govee LED " + std::to_string(i); + } + + SetupColors(); /* re-sync color buffers with LED count */ + DeviceUpdateLEDs(); /* push an updated frame */ +} + +void RGBController_Govee::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + if(!colors.empty()) + { + controller->SendRazerData(&colors[0], (unsigned int)colors.size()); + } + } +} + +void RGBController_Govee::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Govee::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Govee::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu = RGBGetBValue(modes[active_mode].colors[0]); + controller->SetColor(red, grn, blu); + } + else + { + controller->SendRazerEnable(); + DeviceUpdateLEDs(); + } +} + +void RGBController_Govee::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::seconds(30)) + { + DeviceUpdateLEDs(); + } + std::this_thread::sleep_for(10s); + } +} diff --git a/Controllers/GoveeController/RGBController_Govee.h b/Controllers/GoveeController/RGBController_Govee.h new file mode 100644 index 0000000..0495480 --- /dev/null +++ b/Controllers/GoveeController/RGBController_Govee.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_Govee.h | +| | +| RGBController for Govee wireless lighting devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 01 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "GoveeController.h" + +class RGBController_Govee : public RGBController +{ +public: + RGBController_Govee(GoveeController* controller_ptr); + ~RGBController_Govee(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + GoveeController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/GoveeController/base64.hpp b/Controllers/GoveeController/base64.hpp new file mode 100644 index 0000000..0cb01f8 --- /dev/null +++ b/Controllers/GoveeController/base64.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include +#include + +namespace base64 +{ + inline static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + inline static const char kPadCharacter = '='; + + using byte = std::uint8_t; + + inline std::string encode(const std::vector& input) + { + std::string encoded; + encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4); + + std::uint32_t temp{}; + auto it = input.begin(); + + for(std::size_t i = 0; i < input.size() / 3; ++i) + { + temp = (*it++) << 16; + temp += (*it++) << 8; + temp += (*it++); + encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); + encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); + encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]); + encoded.append(1, kEncodeLookup[(temp & 0x0000003F) ]); + } + + switch(input.size() % 3) + { + case 1: + temp = (*it++) << 16; + encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); + encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); + encoded.append(2, kPadCharacter); + break; + case 2: + temp = (*it++) << 16; + temp += (*it++) << 8; + encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); + encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); + encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]); + encoded.append(1, kPadCharacter); + break; + } + + return encoded; + } + + inline std::vector decode(const std::string& input) + { + if(input.length() % 4) + throw std::runtime_error("Invalid base64 length!"); + + std::size_t padding{}; + + if(input.length()) + { + if(input[input.length() - 1] == kPadCharacter) padding++; + if(input[input.length() - 2] == kPadCharacter) padding++; + } + + std::vector decoded; + decoded.reserve(((input.length() / 4) * 3) - padding); + + std::uint32_t temp{}; + auto it = input.begin(); + + while(it < input.end()) + { + for(std::size_t i = 0; i < 4; ++i) + { + temp <<= 6; + if (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41; + else if(*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47; + else if(*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04; + else if(*it == 0x2B) temp |= 0x3E; + else if(*it == 0x2F) temp |= 0x3F; + else if(*it == kPadCharacter) + { + switch(input.end() - it) + { + case 1: + decoded.push_back((temp >> 16) & 0x000000FF); + decoded.push_back((temp >> 8 ) & 0x000000FF); + return decoded; + case 2: + decoded.push_back((temp >> 10) & 0x000000FF); + return decoded; + default: + throw std::runtime_error("Invalid padding in base64!"); + } + } + else throw std::runtime_error("Invalid character in base64!"); + + ++it; + } + + decoded.push_back((temp >> 16) & 0x000000FF); + decoded.push_back((temp >> 8 ) & 0x000000FF); + decoded.push_back((temp ) & 0x000000FF); + } + + return decoded; + } +} diff --git a/Controllers/HPOmen30LController/HPOmen30LController.cpp b/Controllers/HPOmen30LController/HPOmen30LController.cpp new file mode 100644 index 0000000..a881e3d --- /dev/null +++ b/Controllers/HPOmen30LController/HPOmen30LController.cpp @@ -0,0 +1,208 @@ +/*---------------------------------------------------------*\ +| HPOmen30LController.cpp | +| | +| Driver for HP Omen 30L | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "HPOmen30LController.h" + +#define HP_OMEN_30L_BUFFER_SIZE 58 +#define HP_OMEN_30L_VERSION_ID 0x12 +#define HP_OMEN_30L_MAX_BRIGHTNESS 0x64 + +HPOmen30LController::HPOmen30LController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + strcpy(device_name, "HP Omen 30L"); + + hp_zone logo; + logo.value = HP_OMEN_30L_LOGO_ZONE; + logo.mode = HP_OMEN_30L_DIRECT; + logo.speed = HP_OMEN_30L_SPEED_MED; + logo.brightness = HP_OMEN_30L_MAX_BRIGHTNESS; + hp_zones.push_back(logo); + + hp_zone bar; + bar.value = HP_OMEN_30L_BAR_ZONE; + bar.mode = HP_OMEN_30L_DIRECT; + bar.speed = HP_OMEN_30L_SPEED_MED; + bar.brightness = HP_OMEN_30L_MAX_BRIGHTNESS; + hp_zones.push_back(bar); + + hp_zone fan; + fan.value = HP_OMEN_30L_FAN_ZONE; + fan.mode = HP_OMEN_30L_DIRECT; + fan.speed = HP_OMEN_30L_SPEED_MED; + fan.brightness = HP_OMEN_30L_MAX_BRIGHTNESS; + hp_zones.push_back(fan); + + hp_zone cpu; + cpu.value = HP_OMEN_30L_CPU_ZONE; + cpu.mode = HP_OMEN_30L_DIRECT; + cpu.speed = HP_OMEN_30L_SPEED_MED; + cpu.brightness = HP_OMEN_30L_MAX_BRIGHTNESS; + hp_zones.push_back(cpu); + + hp_zone botFan; + botFan.value = HP_OMEN_30L_BOT_FAN_ZONE; + botFan.mode = HP_OMEN_30L_DIRECT; + botFan.speed = HP_OMEN_30L_SPEED_MED; + botFan.brightness = HP_OMEN_30L_MAX_BRIGHTNESS; + hp_zones.push_back(botFan); + + hp_zone midFan; + midFan.value = HP_OMEN_30L_MID_FAN_ZONE; + midFan.mode = HP_OMEN_30L_DIRECT; + midFan.speed = HP_OMEN_30L_SPEED_MED; + midFan.brightness = HP_OMEN_30L_MAX_BRIGHTNESS; + hp_zones.push_back(midFan); + + hp_zone topFan; + topFan.value = HP_OMEN_30L_TOP_FAN_ZONE; + topFan.mode = HP_OMEN_30L_DIRECT; + topFan.speed = HP_OMEN_30L_SPEED_MED; + topFan.brightness = HP_OMEN_30L_MAX_BRIGHTNESS; + hp_zones.push_back(topFan); +} + +HPOmen30LController::~HPOmen30LController() +{ + hid_close(dev); +} + +std::string HPOmen30LController::GetLocationString() +{ + return("HID: " + location); +} + +char* HPOmen30LController::GetDeviceName() +{ + return device_name; +} + +std::string HPOmen30LController::GetSerialString() +{ + std::string ret_string = ""; + return(ret_string); +} + +std::string HPOmen30LController::GetEffectChannelString(unsigned char /*channel*/) +{ + std::string ret_string = ""; + return(ret_string); +} + +std::string HPOmen30LController::GetFirmwareVersionString() +{ + std::string ret_string = ""; + return(ret_string); +} + +void HPOmen30LController::SetZoneMode(int zone,unsigned char mode, unsigned char speed,unsigned char brightness) +{ + hp_zones[zone].mode = mode; + hp_zones[zone].speed = speed; + hp_zones[zone].brightness = brightness; + +} + +void HPOmen30LController::SetZoneColor(int zone, std::vector colors) +{ + SendZoneUpdate(zone, colors); +} + +void HPOmen30LController::SendZoneUpdate(int zone, std::vector colors) +{ + unsigned char usb_buf[HP_OMEN_30L_BUFFER_SIZE] = {}; // zero-initialize array + // 0x00 - 0x01: Unknown + // 0x02: Version ID (HP_OMEN_30L_VERSION_ID) + // 0x03: Lighting mode (static, direct, off, breathing, cycle, blinking, ...) + // 0x04: Total color count (only different from 1 in modes with changing colors) + // 0x05: Current color number (see above, used to set the different colors, one at a time, starting at 1) + // 0x06 - 0x07: Unknown + // 0x08 - 0x23: Used to control the RGB (static) or RGBA (direct) of the first seven LED zones + // 0x24 - 0x2f: Unknown, probably also used for RGB/RGBA control if there's support up to 10 zones + // 0x30: Brightness + // 0x31: Type (static=0x02, direct=0x04, changing colors=0x0A) + // 0x32 - 0x35: Unknown + // 0x36: Zone to update (can only update one at a time) + // 0x37: Power mode to update (on, suspend) + // 0x38: Theme (either 0 to use the colors set in 0x04/0x05, or the ID of a predefined theme) + // 0x39: Color change speed (only relevant for modes with changing colors) + + usb_buf[0x02] = HP_OMEN_30L_VERSION_ID; + usb_buf[0x36] = hp_zones[zone].value; + + // The Omen controller allows setting different modes for when the computer is powered on + // vs when the computer is suspended. + // Because the OpenRGB UI does not allow such a choice, we're only changing the powered on mode. + // If this ever changes, we need to take the user choice into consideration (HP_OMEN_30L_POWER_SUSPEND) + usb_buf[0x37] = HP_OMEN_30L_POWER_ON; + usb_buf[0x03] = hp_zones[zone].mode; + + if (hp_zones[zone].mode == HP_OMEN_30L_OFF) + { + hid_write(dev, usb_buf, HP_OMEN_30L_BUFFER_SIZE); + return; + } + + usb_buf[0x30] = hp_zones[zone].brightness; + int index = hp_zones[zone].value - 1; + if(hp_zones[zone].mode == HP_OMEN_30L_DIRECT) + { + usb_buf[0x31] = HP_OMEN_30L_DIRECT; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = 0x01; + usb_buf[0x08 + index * 4] = HP_OMEN_30L_MAX_BRIGHTNESS; + usb_buf[0x09 + index * 4] = RGBGetRValue(colors[zone]); + usb_buf[0x0A + index * 4] = RGBGetGValue(colors[zone]); + usb_buf[0x0B + index * 4] = RGBGetBValue(colors[zone]); + + hid_write(dev, usb_buf, HP_OMEN_30L_BUFFER_SIZE); + } + else if(hp_zones[zone].mode == HP_OMEN_30L_STATIC) + { + usb_buf[0x31] = 0x02; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = 0x01; + usb_buf[0x08 + index * 3] = RGBGetRValue(colors[zone]); + usb_buf[0x09 + index * 3] = RGBGetGValue(colors[zone]); + usb_buf[0x0A + index * 3] = RGBGetBValue(colors[zone]); + + hid_write(dev, usb_buf, HP_OMEN_30L_BUFFER_SIZE); + } + else + { + usb_buf[0x31] = 0x0A; + usb_buf[0x39] = hp_zones[zone].speed; + + // Theme is custom by default, but if we could select a theme through the UI, + // we would set it in usb_buf[0x38] here and ignore the custom colors vector + unsigned char theme = HP_OMEN_30L_THEME_CUSTOM; + usb_buf[0x38] = theme; + if (theme == HP_OMEN_30L_THEME_CUSTOM) + { + usb_buf[0x04] = (unsigned char)colors.size(); + for(unsigned int i = 0; i < (unsigned int)colors.size(); i++) + { + usb_buf[0x05] = i + 1; + usb_buf[0x08 + index * 3] = RGBGetRValue(colors[i]); + usb_buf[0x09 + index * 3] = RGBGetGValue(colors[i]); + usb_buf[0x0A + index * 3] = RGBGetBValue(colors[i]); + hid_write(dev, usb_buf, HP_OMEN_30L_BUFFER_SIZE); + } + } + else + { + hid_write(dev, usb_buf, HP_OMEN_30L_BUFFER_SIZE); + } + } +} diff --git a/Controllers/HPOmen30LController/HPOmen30LController.h b/Controllers/HPOmen30LController/HPOmen30LController.h new file mode 100644 index 0000000..5de22fb --- /dev/null +++ b/Controllers/HPOmen30LController/HPOmen30LController.h @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| HPOmen30LController.h | +| | +| Driver for HP Omen 30L | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +typedef struct +{ + unsigned char value; + unsigned char mode; + unsigned char speed; + unsigned char brightness; +} hp_zone; + +enum +{ + HP_OMEN_30L_STATIC = 0x01, /* Static effect channel */ + HP_OMEN_30L_DIRECT = 0x04, /* Direct for effects plugin */ + HP_OMEN_30L_OFF = 0x05, /* Turns off the led */ + HP_OMEN_30L_BREATHING = 0x06, /* Breathing effect channel */ + HP_OMEN_30L_COLOR_CYCLE = 0x07, /* Color cycle effect channel */ + HP_OMEN_30L_BLINKING = 0x08, /* Blinking effect channel */ + HP_OMEN_30L_WAVE = 0x09, /* Wave effect channel */ + HP_OMEN_30L_RADIAL = 0x0A, /* Radial effect channel */ +}; + +enum +{ + HP_OMEN_30L_SPEED_SLOW = 0x01, /* Slow speed */ + HP_OMEN_30L_SPEED_MED = 0x02, /* Normal speed */ + HP_OMEN_30L_SPEED_FAST = 0x03, /* Fast speed */ +}; + +enum +{ + HP_OMEN_30L_LOGO_ZONE = 0x01, + HP_OMEN_30L_BAR_ZONE = 0x02, + HP_OMEN_30L_FAN_ZONE = 0x03, + HP_OMEN_30L_CPU_ZONE = 0x04, + HP_OMEN_30L_BOT_FAN_ZONE = 0x05, + HP_OMEN_30L_MID_FAN_ZONE = 0x06, + HP_OMEN_30L_TOP_FAN_ZONE = 0x07, +}; + +enum +{ + HP_OMEN_30L_POWER_ON = 0x01, /* Settings for powered on */ + HP_OMEN_30L_POWER_SUSPEND = 0x02, /* Settings for suspended */ +}; + +enum +{ + HP_OMEN_30L_THEME_CUSTOM = 0x00, + HP_OMEN_30L_THEME_GALAXY = 0x01, + HP_OMEN_30L_THEME_VOLCANO = 0x02, + HP_OMEN_30L_THEME_JUNGLE = 0x03, + HP_OMEN_30L_THEME_OCEAN = 0x04, + HP_OMEN_30L_THEME_UNICORN = 0x05, +}; + +class HPOmen30LController +{ +public: + HPOmen30LController(hid_device* dev_handle, const char* path); + ~HPOmen30LController(); + + char* GetDeviceName(); + + std::string GetEffectChannelString(unsigned char channel); + std::string GetFirmwareVersionString(); + std::string GetLocationString(); + std::string GetSerialString(); + + void SetZoneMode(int zone,unsigned char mode, unsigned char speed, unsigned char brightness); + void SetZoneColor(int zone, std::vector colors); + +private: + char device_name[32]; + hid_device* dev; + std::string location; + + std::vector hp_zones; + + void SendZoneUpdate(int zone, std::vector colors); +}; diff --git a/Controllers/HPOmen30LController/HPOmen30LControllerDetect.cpp b/Controllers/HPOmen30LController/HPOmen30LControllerDetect.cpp new file mode 100644 index 0000000..3374424 --- /dev/null +++ b/Controllers/HPOmen30LController/HPOmen30LControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| HPOmen30LControllerDetect.cpp | +| | +| Detector for HP Omen 30L | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "HPOmen30LController.h" +#include "RGBController_HPOmen30L.h" + +#define HP_OMEN_30L_VID 0x103C +#define HP_OMEN_30L_PID 0x84FD + +/******************************************************************************************\ +* * +* DetectHPOmen30LController * +* * +* Tests the USB address to see if an HP Omen 30L controller exists there. * +* * +\******************************************************************************************/ + +void DetectHPOmen30LController(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HPOmen30LController* controller = new HPOmen30LController(dev, info->path); + RGBController_HPOmen30L* rgb_controller = new RGBController_HPOmen30L(controller); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("HP Omen 30L", DetectHPOmen30LController, HP_OMEN_30L_VID, HP_OMEN_30L_PID); diff --git a/Controllers/HPOmen30LController/RGBController_HPOmen30L.cpp b/Controllers/HPOmen30LController/RGBController_HPOmen30L.cpp new file mode 100644 index 0000000..6c721ca --- /dev/null +++ b/Controllers/HPOmen30LController/RGBController_HPOmen30L.cpp @@ -0,0 +1,292 @@ +/*---------------------------------------------------------*\ +| RGBController_HPOmen30L.cpp | +| | +| RGBController for HP Omen 30L | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HPOmen30L.h" + +/**------------------------------------------------------------------*\ + @name HP Omen 30L + @category Motherboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectHPOmen30LController + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HPOmen30L::RGBController_HPOmen30L(HPOmen30LController* controller_ptr) +{ + controller = controller_ptr; + + name = "HP Omen 30L"; + vendor = "HP"; + type = DEVICE_TYPE_MOTHERBOARD; + description = "HP Omen 30L Device"; + location = controller->GetLocationString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = HP_OMEN_30L_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.brightness = 100; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = HP_OMEN_30L_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = 0; + Static.brightness_max = 100; + Static.brightness = 100; + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = HP_OMEN_30L_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = HP_OMEN_30L_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.speed_min = HP_OMEN_30L_SPEED_SLOW; + Breathing.speed_max = HP_OMEN_30L_SPEED_FAST; + Breathing.speed = HP_OMEN_30L_SPEED_MED; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 6; + Breathing.colors.resize(4); + Breathing.brightness_min = 0; + Breathing.brightness_max = 100; + Breathing.brightness = 100; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = HP_OMEN_30L_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + ColorCycle.speed_min = HP_OMEN_30L_SPEED_SLOW; + ColorCycle.speed_max = HP_OMEN_30L_SPEED_FAST; + ColorCycle.speed = HP_OMEN_30L_SPEED_MED; + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorCycle.colors_min = 1; + ColorCycle.colors_max = 6; + ColorCycle.colors.resize(4); + ColorCycle.brightness_min = 0; + ColorCycle.brightness_max = 100; + ColorCycle.brightness = 100; + modes.push_back(ColorCycle); + + mode Blinking; + Blinking.name = "Blinking"; + Blinking.value = HP_OMEN_30L_BLINKING; + Blinking.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Blinking.speed_min = HP_OMEN_30L_SPEED_SLOW; + Blinking.speed_max = HP_OMEN_30L_SPEED_FAST; + Blinking.speed = HP_OMEN_30L_SPEED_MED; + Blinking.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blinking.colors_min = 1; + Blinking.colors_max = 6; + Blinking.colors.resize(4); + Blinking.brightness_min = 0; + Blinking.brightness_max = 100; + Blinking.brightness = 100; + modes.push_back(Blinking); + + mode Wave; + Wave.name = "Wave"; + Wave.value = HP_OMEN_30L_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Wave.speed_min = HP_OMEN_30L_SPEED_SLOW; + Wave.speed_max = HP_OMEN_30L_SPEED_FAST; + Wave.speed = HP_OMEN_30L_SPEED_MED; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.colors_min = 6; + Wave.colors_max = 6; + Wave.colors.resize(6); + Wave.brightness_min = 0; + Wave.brightness_max = 100; + Wave.brightness = 100; + modes.push_back(Wave); + + mode Radial; + Radial.name = "Radial"; + Radial.value = HP_OMEN_30L_RADIAL; + Radial.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Radial.speed_min = HP_OMEN_30L_SPEED_SLOW; + Radial.speed_max = HP_OMEN_30L_SPEED_FAST; + Radial.speed = HP_OMEN_30L_SPEED_MED; + Radial.color_mode = MODE_COLORS_MODE_SPECIFIC; + Radial.colors_min = 1; + Radial.colors_max = 6; + Radial.colors.resize(4); + Radial.brightness_min = 0; + Radial.brightness_max = 100; + Radial.brightness = 100; + modes.push_back(Radial); + + SetupZones(); +} + +RGBController_HPOmen30L::~RGBController_HPOmen30L() +{ + delete controller; +} + +void RGBController_HPOmen30L::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + zone logo_zone; + logo_zone.name = "Omen Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + zone light_bar; + light_bar.name = "Light Bar"; + light_bar.type = ZONE_TYPE_SINGLE; + light_bar.leds_min = 1; + light_bar.leds_max = 1; + light_bar.leds_count = 1; + light_bar.matrix_map = NULL; + zones.push_back(light_bar); + + zone ring_zone; + ring_zone.name = "Front Fan"; + ring_zone.type = ZONE_TYPE_SINGLE; + ring_zone.leds_min = 1; + ring_zone.leds_max = 1; + ring_zone.leds_count = 1; + ring_zone.matrix_map = NULL; + zones.push_back(ring_zone); + + zone cpu_zone; + cpu_zone.name = "CPU Cooler"; + cpu_zone.type = ZONE_TYPE_SINGLE; + cpu_zone.leds_min = 1; + cpu_zone.leds_max = 1; + cpu_zone.leds_count = 1; + cpu_zone.matrix_map = NULL; + zones.push_back(cpu_zone); + + zone bot_fan; + bot_fan.name = "Front Bottom Fan"; + bot_fan.type = ZONE_TYPE_SINGLE; + bot_fan.leds_min = 1; + bot_fan.leds_max = 1; + bot_fan.leds_count = 1; + bot_fan.matrix_map = NULL; + zones.push_back(bot_fan); + + zone mid_fan; + mid_fan.name = "Front Middle Fan"; + mid_fan.type = ZONE_TYPE_SINGLE; + mid_fan.leds_min = 1; + mid_fan.leds_max = 1; + mid_fan.leds_count = 1; + mid_fan.matrix_map = NULL; + zones.push_back(mid_fan); + + zone top_fan; + top_fan.name = "Front Top Fan"; + top_fan.type = ZONE_TYPE_SINGLE; + top_fan.leds_min = 1; + top_fan.leds_max = 1; + top_fan.leds_count = 1; + top_fan.matrix_map = NULL; + zones.push_back(top_fan); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + led logo_led; + logo_led.name = "Logo LED"; + leds.push_back(logo_led); + + led bar_led; + bar_led.name = "Bar LED"; + leds.push_back(bar_led); + + led fan_led; + fan_led.name = "Fan LED"; + leds.push_back(fan_led); + + led cpu_led; + cpu_led.name = "CPU LED"; + leds.push_back(cpu_led); + + led bot_fan_led; + bot_fan_led.name = "Bottom Fan LED"; + leds.push_back(bot_fan_led); + + led mid_fan_led; + bot_fan_led.name = "Middle Fan LED"; + leds.push_back(bot_fan_led); + + led top_fan_led; + bot_fan_led.name = "Top Fan LED"; + leds.push_back(bot_fan_led); + + SetupColors(); +} + +void RGBController_HPOmen30L::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HPOmen30L::DeviceUpdateLEDs() +{ + for(unsigned int i = 0; i < zones.size(); i++) + { + if(modes[active_mode].value == HP_OMEN_30L_STATIC || + modes[active_mode].value == HP_OMEN_30L_DIRECT || + modes[active_mode].value == HP_OMEN_30L_OFF) + { + controller->SetZoneColor(i, colors); + } + else + { + controller->SetZoneColor(i, modes[active_mode].colors); + } + } +} + +void RGBController_HPOmen30L::UpdateZoneLEDs(int zone) +{ + controller->SetZoneColor(zone,colors); +} + +void RGBController_HPOmen30L::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_HPOmen30L::DeviceUpdateMode() +{ + for(unsigned int i = 0; i < zones.size(); i++) + { + controller->SetZoneMode(i, modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness); + } + + DeviceUpdateLEDs(); +} diff --git a/Controllers/HPOmen30LController/RGBController_HPOmen30L.h b/Controllers/HPOmen30LController/RGBController_HPOmen30L.h new file mode 100644 index 0000000..f57bded --- /dev/null +++ b/Controllers/HPOmen30LController/RGBController_HPOmen30L.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_HPOmen30L.h | +| | +| RGBController for HP Omen 30L | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "HPOmen30LController.h" + +class RGBController_HPOmen30L : public RGBController +{ +public: + RGBController_HPOmen30L(HPOmen30LController* controller_ptr); + ~RGBController_HPOmen30L(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + HPOmen30LController* controller; +}; diff --git a/Controllers/HPOmenLaptopController/HPOmenLaptopController_Windows.cpp b/Controllers/HPOmenLaptopController/HPOmenLaptopController_Windows.cpp new file mode 100644 index 0000000..4a76d94 --- /dev/null +++ b/Controllers/HPOmenLaptopController/HPOmenLaptopController_Windows.cpp @@ -0,0 +1,340 @@ +/*---------------------------------------------------------*\ +| HPOmenLaptopController_Windows.cpp | +| | +| Driver for HP Omen laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "HPOmenLaptopController_Windows.h" +#include +#include + +#define RESULT_STEP 5 +#define PARAM_STEP 4 +#define OBJ_STEP 3 +#define SERVICE_STEP 2 +#define LOCATE_STEP 1 +#define INIT_STEP 0 + +HPOmenLaptopController_Windows::HPOmenLaptopController_Windows() +{ + +} + +HPOmenLaptopController_Windows::~HPOmenLaptopController_Windows() +{ + +} + +void HPOmenLaptopController_Windows::cleanup(int fail_level) +{ + /*-----------------------------------------------------*\ + | Cleanup for the execute method | + \*-----------------------------------------------------*/ + + switch(fail_level) + { + case RESULT_STEP: + if (callResult) + { + callResult->Release(); + } + case PARAM_STEP: + methodParameters->Release(); + case OBJ_STEP: + classObject->Release(); + case SERVICE_STEP: + pSvc->Release(); + case LOCATE_STEP: + pLoc->Release(); + case INIT_STEP: + CoUninitialize(); + } +} + +int HPOmenLaptopController_Windows::execute(int command, int commandType, int inputDataSize, BYTE* inputData, int* returnDataSize, BYTE** returnData) +{ + /*-----------------------------------------------------*\ + | Talk to WMI | + \*-----------------------------------------------------*/ + // magic constant + static const BYTE Sign[4] = { 83, 69, 67, 85 }; + + // will hold the return codes from all the calls to WMI + HRESULT hres; + + // initialize COM interface + hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED); + if(FAILED(hres)) + { + return 1; + } + + // obtain the initial locator to the Windows Management Instrumentation + pLoc = nullptr; + hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*) &pLoc ); + if(FAILED(hres)) + { + cleanup(INIT_STEP); + return 1; + } + + pSvc = nullptr; + hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\WMI"), NULL, NULL, 0, NULL, 0, 0, &pSvc); + if(FAILED(hres)) + { + cleanup(LOCATE_STEP); + return 1; + } + + hres = CoSetProxyBlanket( + pSvc, // Indicates the proxy to set + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + NULL, // Server principal name + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + NULL, // client identity + EOAC_NONE // proxy capabilities + ); + + if(FAILED(hres)) + { + cleanup(SERVICE_STEP); + return 1; + } + + /*-----------------------------------------------------*\ + | Get all the required custom hp wmi obejcts | + \*-----------------------------------------------------*/ + classObject = nullptr; + hres = pSvc->GetObject(_bstr_t(L"hpqBIntM"), 0, NULL, &classObject, NULL); + if(FAILED(hres)) + { + cleanup(SERVICE_STEP); + return 1; + } + + methodParameters = nullptr; + hres = classObject->GetMethod(L"hpqBIOSInt128", 0, &methodParameters, NULL); + if(FAILED(hres)) + { + cleanup(OBJ_STEP); + return 1; + } + + dataInClass = nullptr; + hres = pSvc->GetObject(_bstr_t(L"hpqBDataIn"), 0, NULL, &dataInClass, NULL); + if(FAILED(hres)) + { + cleanup(PARAM_STEP); + return 1; + } + + callResult = nullptr; + hres = pSvc->GetObject(_bstr_t(L"hpqBDataOut128"), 0, NULL, NULL, &callResult); + if(FAILED(hres)) + { + dataInClass->Release(); + cleanup(PARAM_STEP); + return 1; + } + + /*-----------------------------------------------------*\ + | Populate the input parameters | + \*-----------------------------------------------------*/ + // Sign + VARIANT signVar; + VariantInit(&signVar); + signVar.vt = VT_UI1 | VT_ARRAY; + SAFEARRAYBOUND safeArrayBound = { 4, 0 }; + signVar.parray = SafeArrayCreate(VT_UI1, 1, &safeArrayBound); + SafeArrayLock(signVar.parray); + memcpy(signVar.parray->pvData, Sign, sizeof(Sign)); + SafeArrayUnlock(signVar.parray); + dataInClass->Put(L"Sign", 0, &signVar, 0); + VariantClear(&signVar); + + // Command + VARIANT commandVar; + VariantInit(&commandVar); + commandVar.vt = VT_I4; + commandVar.lVal = command; + dataInClass->Put(L"Command", 0, &commandVar, 0); + VariantClear(&commandVar); + + // CommandType + VARIANT commandTypeVar; + VariantInit(&commandTypeVar); + commandTypeVar.vt = VT_I4; + commandTypeVar.lVal = commandType; + dataInClass->Put(L"CommandType", 0, &commandTypeVar, 0); + VariantClear(&commandTypeVar); + + // Size + VARIANT sizeVar; + VariantInit(&sizeVar); + sizeVar.vt = VT_I4; + sizeVar.lVal = inputDataSize; + dataInClass->Put(L"Size", 0, &sizeVar, 0); + + // hpqBData + VARIANT hpqBDataVar; + VariantInit(&hpqBDataVar); + hpqBDataVar.vt = VT_UI1 | VT_ARRAY; + SAFEARRAYBOUND safeArrayBoundData = { static_cast(inputDataSize), 0 }; + hpqBDataVar.parray = SafeArrayCreate(VT_UI1, 1, &safeArrayBoundData); + SafeArrayLock(hpqBDataVar.parray); + memcpy(hpqBDataVar.parray->pvData, inputData, inputDataSize); + SafeArrayUnlock(hpqBDataVar.parray); + dataInClass->Put(L"hpqBData", 0, &hpqBDataVar, 0); + VariantClear(&hpqBDataVar); + + /*-----------------------------------------------------------*\ + | Fill the 'InData' parameter from the 'hpqBIOSInt128' method | + \*-----------------------------------------------------------*/ + + // InData + VARIANT inDataVar; + VariantInit(&inDataVar); + inDataVar.vt = VT_UNKNOWN; + inDataVar.punkVal = dataInClass; + methodParameters->Put(L"InData", 0, &inDataVar, 0); + VariantClear(&inDataVar); + + /*-----------------------------------------------------------*\ + | Call the 'hpqBIOSInt128' method from the 'hpqBIntM' class | + \*-----------------------------------------------------------*/ + + hres = pSvc->ExecMethod(_bstr_t(L"hpqBIntM.InstanceName='ACPI\\PNP0C14\\0_0'"), _bstr_t(L"hpqBIOSInt128"), 0, NULL, methodParameters, NULL, &callResult); + if(FAILED(hres)) + { + cleanup(RESULT_STEP); + return 1; + } + + /*-------------------------------------------------------*\ + | Get the returned data | + \*-------------------------------------------------------*/ + if(returnDataSize != NULL && returnData != NULL) + { + IWbemClassObject* ppResultObject = nullptr; + callResult->GetResultObject(WBEM_INFINITE, &ppResultObject); + + // get OutData from object returned (is an object of type hpqBDataOut128) + VARIANT outDataVar; + VariantInit(&outDataVar); + ppResultObject->Get(L"OutData", 0, &outDataVar, NULL, NULL); + + // get 'Data' property from the object returned + IWbemClassObject* retData = (IWbemClassObject*)outDataVar.punkVal; + retData->Get(L"Data", 0, &outDataVar, NULL, NULL); + + // extract the byte array from the result VARIANT + long lower, upper; + SAFEARRAY* safeArray = outDataVar.parray; + SafeArrayGetLBound(safeArray, 1, &lower); + SafeArrayGetUBound(safeArray, 1, &upper); + long length = upper - lower + 1; + *returnData = new BYTE[length]; + *returnDataSize = length; + SafeArrayLock(safeArray); + memcpy(*returnData, safeArray->pvData, length); + SafeArrayUnlock(safeArray); + + // cleanup + retData->Release(); + VariantClear(&outDataVar); + ppResultObject->Release(); + } + + // cleanup + cleanup(RESULT_STEP); + return 0; +} + +void HPOmenLaptopController_Windows::setColors(std::vector& colors) +{ + /*-----------------------------------------------------*\ + | Set the new colors | + \*-----------------------------------------------------*/ + int returnDataSize = 0; + BYTE* returnData = nullptr; + int num = execute(131081, 2, 0, nullptr, &returnDataSize, &returnData); + + if(num == 0 && returnData != nullptr) + { + // prepare the data byte array to be sent to WMI + for (int i = 0; i < 4; i++) + { + returnData[25 + i * 3] = RGBGetRValue(colors[3 - i]); + returnData[25 + i * 3 + 1] = RGBGetGValue(colors[3 - i]); + returnData[25 + i * 3 + 2] = RGBGetBValue(colors[3 - i]); + } + + // make the WMI call to set the colors + execute(131081, 3, returnDataSize, returnData, NULL, NULL); + delete[] returnData; + } +} + +bool HPOmenLaptopController_Windows::isLightingSupported() +{ + /*-----------------------------------------------------*\ + | Check if the laptop supports rgb lighting | + \*-----------------------------------------------------*/ + + BYTE b = 0; + int returnDataSize = 0; + BYTE* returnData = nullptr; + if(execute(131081, 1, 0, nullptr, &returnDataSize, &returnData) == 0) + { + b = (BYTE)(returnData[0] & 1u); + } + + delete[] returnData; + return b == 1; +} + +KeyboardType HPOmenLaptopController_Windows::getKeyboardType() +{ + /*-----------------------------------------------------*\ + | Get keyboard type | + \*-----------------------------------------------------*/ + int returnDataSize = 0; + BYTE* returnData = nullptr; + if(execute(131080, 43, 0, nullptr, &returnDataSize, &returnData) == 0) + { + int result = returnData[0]; + delete[] returnData; + return (KeyboardType)(result + 1); + } + + return KeyboardType::INVALID; +} + +void HPOmenLaptopController_Windows::changeMode(KeyboardMode mode) +{ + /*-----------------------------------------------------*\ + | Change keyboard rgb mode | + \*-----------------------------------------------------*/ + switch(mode) + { + case KeyboardMode::OFF: + { + BYTE array[4] = { 100, 0, 0, 0 }; + execute(131081, 5, sizeof(array), array, NULL, NULL); + break; + } + case KeyboardMode::DIRECT: + { + BYTE array[4] = { 228, 0, 0, 0 }; + execute(131081, 5, sizeof(array), array, NULL, NULL); + break; + } + default: + break; + } +} diff --git a/Controllers/HPOmenLaptopController/HPOmenLaptopController_Windows.h b/Controllers/HPOmenLaptopController/HPOmenLaptopController_Windows.h new file mode 100644 index 0000000..d8a56b9 --- /dev/null +++ b/Controllers/HPOmenLaptopController/HPOmenLaptopController_Windows.h @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| HPOmenLaptopController_Windows.h | +| | +| Driver for HP Omen laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include + +struct IWbemLocator; +struct IWbemServices; +struct IWbemClassObject; +struct IWbemCallResult; + +enum KeyboardType +{ + INVALID = 0, + NORMAL, + WITH_NUMPAD, + WITHOUT_NUMPAD, + RGB +}; + +enum KeyboardMode +{ + OFF = 0, + DIRECT +}; + +class HPOmenLaptopController_Windows +{ +private: + /*-----------------------------------------------------*\ + | Controller private functions | + \*-----------------------------------------------------*/ + int execute(int command, int commandType, int inputDataSize, BYTE* inputData, int* returnDataSize, BYTE** returnData); + void cleanup(int fail_level); + + IWbemLocator* pLoc; + IWbemServices* pSvc; + IWbemClassObject* classObject; + IWbemClassObject* methodParameters; + IWbemClassObject* dataInClass; + IWbemCallResult* callResult; + +public: + HPOmenLaptopController_Windows(); + ~HPOmenLaptopController_Windows(); + + /*-----------------------------------------------------*\ + | Controller public functions | + \*-----------------------------------------------------*/ + void setColors(std::vector& colors); + bool isLightingSupported(); + KeyboardType getKeyboardType(); + void changeMode(KeyboardMode mode); +}; diff --git a/Controllers/HPOmenLaptopController/HPOmenLaptopWMIDetect_Windows.cpp b/Controllers/HPOmenLaptopController/HPOmenLaptopWMIDetect_Windows.cpp new file mode 100644 index 0000000..b05e22b --- /dev/null +++ b/Controllers/HPOmenLaptopController/HPOmenLaptopWMIDetect_Windows.cpp @@ -0,0 +1,28 @@ +/*---------------------------------------------------------*\ +| HPOmenLaptopWMIDetect_Windows.cpp | +| | +| Detector for HP Omen laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HPOmenLaptopWMI_Windows.h" +#include "HPOmenLaptopController_Windows.h" +#include "Detector.h" + +static void DetectHPOmenLaptopWMIControllers() +{ + HPOmenLaptopController_Windows *controller = new HPOmenLaptopController_Windows(); + + if(!controller->isLightingSupported() || controller->getKeyboardType() != KeyboardType::WITHOUT_NUMPAD) + { + delete controller; + return; + } + + RGBController *hp_omen_controller = new RGBController_HPOmenLaptopWMI_Windows(controller); + ResourceManager::get()->RegisterRGBController(hp_omen_controller); +} + +REGISTER_DETECTOR("HP Omen 4-Zone Laptop Keyboard", DetectHPOmenLaptopWMIControllers); diff --git a/Controllers/HPOmenLaptopController/RGBController_HPOmenLaptopWMI_Windows.cpp b/Controllers/HPOmenLaptopController/RGBController_HPOmenLaptopWMI_Windows.cpp new file mode 100644 index 0000000..6e4bffa --- /dev/null +++ b/Controllers/HPOmenLaptopController/RGBController_HPOmenLaptopWMI_Windows.cpp @@ -0,0 +1,131 @@ +/*---------------------------------------------------------*\ +| RGBController_HPOmenLaptopWMI_Windows.cpp | +| | +| RGBController for HP Omen laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HPOmenLaptopWMI_Windows.h" + +/**------------------------------------------------------------------*\ + @name Omen 4-Zone Laptop Keyboard + @category Keyboard + @type WMI + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectHPOmenLaptopWMIControllers + @comment Currently only supported on Windows (requires admin privileges) due to the WMI interface. +\*-------------------------------------------------------------------*/ + +RGBController_HPOmenLaptopWMI_Windows::RGBController_HPOmenLaptopWMI_Windows(HPOmenLaptopController_Windows *controller) +{ + /*-----------------------------------------------------*\ + | Configure the keyboard modes | + \*-----------------------------------------------------*/ + this->controller = controller; + + this->name = "Omen 4-Zone Laptop Keyboard"; + this->vendor = "HP"; + this->description = "WMI Device"; + this->location = "ROOT\\\\WMI:hpqBIntM"; + this->type = DEVICE_TYPE_KEYBOARD; + + mode Direct; + Direct.name = "Direct"; + Direct.value = KeyboardMode::DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + this->modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = KeyboardMode::OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + this->modes.push_back(Off); + + SetupZones(); +} + +RGBController_HPOmenLaptopWMI_Windows::~RGBController_HPOmenLaptopWMI_Windows() +{ + delete this->controller; +} + +void RGBController_HPOmenLaptopWMI_Windows::SetupZones() +{ + /*-----------------------------------------------------*\ + | Set up the zone | + \*-----------------------------------------------------*/ + zone keyboard_zone; + keyboard_zone.leds_count = 4; + keyboard_zone.leds_min = 0; + keyboard_zone.leds_max = 4; + keyboard_zone.name = "Keyboard"; + keyboard_zone.matrix_map = NULL; + keyboard_zone.type = ZONE_TYPE_LINEAR; + this->zones.push_back(keyboard_zone); + + /*-----------------------------------------------------*\ + | Set up the LEDs | + \*-----------------------------------------------------*/ + led wasd_led; + wasd_led.name = "Keyboard WASD"; + this->leds.push_back(wasd_led); + + led left_led; + left_led.name = "Keyboard Left"; + this->leds.push_back(left_led); + + led mid_led; + mid_led.name = "Keyboard Middle"; + this->leds.push_back(mid_led); + + led right_led; + right_led.name = "Keyboard Right"; + this->leds.push_back(right_led); + + SetupColors(); +} + +void RGBController_HPOmenLaptopWMI_Windows::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*-----------------------------------------------------*\ + | Not Supported | + \*-----------------------------------------------------*/ +} + +void RGBController_HPOmenLaptopWMI_Windows::DeviceUpdateLEDs() +{ + /*-----------------------------------------------------*\ + | Set new colors | + \*-----------------------------------------------------*/ + controller->setColors(this->colors); +} + +void RGBController_HPOmenLaptopWMI_Windows::UpdateZoneLEDs(int /*zone*/) +{ + /*-----------------------------------------------------*\ + | Set new colors | + \*-----------------------------------------------------*/ + controller->setColors(this->colors); +} + +void RGBController_HPOmenLaptopWMI_Windows::UpdateSingleLED(int /*led*/) +{ + /*-----------------------------------------------------*\ + | Set new colors | + \*-----------------------------------------------------*/ + controller->setColors(this->colors); +} + +void RGBController_HPOmenLaptopWMI_Windows::DeviceUpdateMode() +{ + /*-----------------------------------------------------*\ + | Change keyboard rgb mode | + \*-----------------------------------------------------*/ + controller->changeMode((KeyboardMode)this->modes[active_mode].value); +} diff --git a/Controllers/HPOmenLaptopController/RGBController_HPOmenLaptopWMI_Windows.h b/Controllers/HPOmenLaptopController/RGBController_HPOmenLaptopWMI_Windows.h new file mode 100644 index 0000000..d8ff8fa --- /dev/null +++ b/Controllers/HPOmenLaptopController/RGBController_HPOmenLaptopWMI_Windows.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_HPOmenLaptopWMI_Windows.h | +| | +| RGBController for HP Omen laptop | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "HPOmenLaptopController_Windows.h" +#include "RGBController.h" + +class RGBController_HPOmenLaptopWMI_Windows : public RGBController +{ +public: + RGBController_HPOmenLaptopWMI_Windows(HPOmenLaptopController_Windows *controller); + ~RGBController_HPOmenLaptopWMI_Windows(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + HPOmenLaptopController_Windows *controller; +}; diff --git a/Controllers/HYTEKeyboardController/HYTEKeyboardController.cpp b/Controllers/HYTEKeyboardController/HYTEKeyboardController.cpp new file mode 100644 index 0000000..d298d03 --- /dev/null +++ b/Controllers/HYTEKeyboardController/HYTEKeyboardController.cpp @@ -0,0 +1,202 @@ +/*---------------------------------------------------------*\ +| HYTEKeyboardController.cpp | +| | +| Driver for HYTE keyboard | +| | +| Adam Honse (calcprogrammer1@gmail.com) 30 Oct 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HYTEKeyboardController.h" + +HYTEKeyboardController::HYTEKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HYTEKeyboardController::~HYTEKeyboardController() +{ + +} + +std::string HYTEKeyboardController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string HYTEKeyboardController::GetDeviceName() +{ + return(name); +} + +void HYTEKeyboardController::LEDStreaming(unsigned int zone, RGBColor* colors) +{ + /*-----------------------------------------------------*\ + | Call the appropriate LEDStreaming function for the | + | given zone | + \*-----------------------------------------------------*/ + switch(zone) + { + case HYTE_KEYBOARD_ZONE_KEYBOARD: + LEDStreaming_Keyboard(colors); + break; + + case HYTE_KEYBOARD_ZONE_SURROUND: + LEDStreaming_Surround(colors); + break; + } +} + +void HYTEKeyboardController::LEDStreaming_Keyboard(RGBColor* colors) +{ + /*-----------------------------------------------------*\ + | LED Streaming - Keyboard RGB | + | | + | Set Feature (9 bytes, the first byte is 0x00) | + | 0x04 0xF0 0x00 0x00 0x00 0x00 0x00 0x00 | + | | + | EP6 Write (6 pages, 65 bytes/page) | + | 0xRR 0xGG 0xBB 0xRR 0xGG 0xBB ... | + \*-----------------------------------------------------*/ + unsigned char usb_feature_buf[9]; + unsigned char usb_buf[6][65]; + + /*-----------------------------------------------------*\ + | Set up feature report | + \*-----------------------------------------------------*/ + memset(usb_feature_buf, 0, sizeof(usb_feature_buf)); + + usb_feature_buf[0] = 0x00; + usb_feature_buf[1] = 0x04; + usb_feature_buf[2] = 0xF0; + + /*-----------------------------------------------------*\ + | Set up data packets | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0][0] = 0x00; + usb_buf[1][0] = 0x00; + usb_buf[2][0] = 0x00; + usb_buf[3][0] = 0x00; + usb_buf[4][0] = 0x00; + usb_buf[5][0] = 0x00; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + unsigned int color_idx = 0; + unsigned int channel_idx = 0; + + for(unsigned int pkt_idx = 0; pkt_idx < 6; pkt_idx++) + { + for(unsigned int byte_idx = 0; byte_idx < 64; byte_idx++) + { + switch(channel_idx) + { + case 0: + usb_buf[pkt_idx][1 + byte_idx] = RGBGetRValue(colors[color_idx]); + break; + + case 1: + usb_buf[pkt_idx][1 + byte_idx] = RGBGetGValue(colors[color_idx]); + break; + + case 2: + usb_buf[pkt_idx][1 + byte_idx] = RGBGetBValue(colors[color_idx]); + color_idx++; + break; + } + + channel_idx = ( channel_idx + 1 ) % 3; + } + } + + /*-----------------------------------------------------*\ + | Send the data | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_feature_buf, sizeof(usb_feature_buf)); + hid_write(dev, usb_buf[0], sizeof(usb_buf[0])); + hid_write(dev, usb_buf[1], sizeof(usb_buf[1])); + hid_write(dev, usb_buf[2], sizeof(usb_buf[2])); + hid_write(dev, usb_buf[3], sizeof(usb_buf[3])); + hid_write(dev, usb_buf[4], sizeof(usb_buf[4])); + hid_write(dev, usb_buf[5], sizeof(usb_buf[5])); +} + +void HYTEKeyboardController::LEDStreaming_Surround(RGBColor* colors) +{ + /*-----------------------------------------------------*\ + | LED Streaming - Surround RGB | + | | + | Set Feature (9 bytes, the first byte is 0x00) | + | 0x04 0xF1 0x00 0x00 0x00 0x00 0x00 0x00 | + | | + | EP6 Write (3 pages, 65 bytes/page) | + | 0xRR 0xGG 0xBB 0xRR 0xGG 0xBB ... | + \*-----------------------------------------------------*/ + unsigned char usb_feature_buf[9]; + unsigned char usb_buf[3][65]; + + /*-----------------------------------------------------*\ + | Set up feature report | + \*-----------------------------------------------------*/ + memset(usb_feature_buf, 0, sizeof(usb_feature_buf)); + + usb_feature_buf[0] = 0x00; + usb_feature_buf[1] = 0x04; + usb_feature_buf[2] = 0xF1; + + /*-----------------------------------------------------*\ + | Set up data packets | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0][0] = 0x00; + usb_buf[1][0] = 0x00; + usb_buf[2][0] = 0x00; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + unsigned int color_idx = 0; + unsigned int channel_idx = 0; + + for(unsigned int pkt_idx = 0; pkt_idx < 3; pkt_idx++) + { + for(unsigned int byte_idx = 0; byte_idx < 64; byte_idx++) + { + switch(channel_idx) + { + case 0: + usb_buf[pkt_idx][1 + byte_idx] = RGBGetRValue(colors[color_idx]); + break; + + case 1: + usb_buf[pkt_idx][1 + byte_idx] = RGBGetGValue(colors[color_idx]); + break; + + case 2: + usb_buf[pkt_idx][1 + byte_idx] = RGBGetBValue(colors[color_idx]); + color_idx++; + break; + } + + channel_idx = ( channel_idx + 1 ) % 3; + } + } + + /*-----------------------------------------------------*\ + | Send the data | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_feature_buf, sizeof(usb_feature_buf)); + hid_write(dev, usb_buf[0], sizeof(usb_buf[0])); + hid_write(dev, usb_buf[1], sizeof(usb_buf[1])); + hid_write(dev, usb_buf[2], sizeof(usb_buf[2])); +} + diff --git a/Controllers/HYTEKeyboardController/HYTEKeyboardController.h b/Controllers/HYTEKeyboardController/HYTEKeyboardController.h new file mode 100644 index 0000000..8168813 --- /dev/null +++ b/Controllers/HYTEKeyboardController/HYTEKeyboardController.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| HYTEKeyboardController.h | +| | +| Driver for HYTE keyboard | +| | +| Adam Honse (calcprogrammer1@gmail.com) 30 Oct 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + HYTE_KEYBOARD_ZONE_KEYBOARD, + HYTE_KEYBOARD_ZONE_SURROUND +}; + +class HYTEKeyboardController +{ +public: + HYTEKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name); + ~HYTEKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void LEDStreaming(unsigned int zone, RGBColor* colors); + +private: + hid_device* dev; + std::string location; + std::string name; + + void LEDStreaming_Keyboard(RGBColor* colors); + void LEDStreaming_Surround(RGBColor* colors); +}; diff --git a/Controllers/HYTEKeyboardController/HYTEKeyboardControllerDetect.cpp b/Controllers/HYTEKeyboardController/HYTEKeyboardControllerDetect.cpp new file mode 100644 index 0000000..d268748 --- /dev/null +++ b/Controllers/HYTEKeyboardController/HYTEKeyboardControllerDetect.cpp @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| HYTEKeyboardControllerDetect.cpp | +| | +| Detector for HYTE keyboard | +| | +| Adam Honse (calcprogrammer1@gmail.com) 30 Oct 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "HYTEKeyboardController.h" +#include "RGBController_HYTEKeyboard.h" + +/*---------------------------------------------------------*\ +| HYTE vendor ID | +\*---------------------------------------------------------*/ +#define HYTE_VID 0x3402 + +/*---------------------------------------------------------*\ +| HYTE keyboard product IDs | +\*---------------------------------------------------------*/ +#define HYTE_KEEB_TKL_PID 0x0300 + +void DetectHYTEKeyboard(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HYTEKeyboardController* controller = new HYTEKeyboardController(dev, info->path, name); + RGBController_HYTEKeyboard* rgb_controller = new RGBController_HYTEKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("HYTE Keeb TKL", DetectHYTEKeyboard, HYTE_VID, HYTE_KEEB_TKL_PID, 0xFF11, 0xF0); diff --git a/Controllers/HYTEKeyboardController/RGBController_HYTEKeyboard.cpp b/Controllers/HYTEKeyboardController/RGBController_HYTEKeyboard.cpp new file mode 100644 index 0000000..7294134 --- /dev/null +++ b/Controllers/HYTEKeyboardController/RGBController_HYTEKeyboard.cpp @@ -0,0 +1,200 @@ +/*---------------------------------------------------------*\ +| RGBController_HYTEKeyboard.cpp | +| | +| RGBController for HYTE keyboard | +| | +| Adam Honse (calcprogrammer1@gmail.com) 30 Oct 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "KeyboardLayoutManager.h" +#include "RGBController_HYTEKeyboard.h" + +/*---------------------------------------------------------------------*\ +| HYTE Keeb TKL KLM Layout | +\*---------------------------------------------------------------------*/ +const std::vector hyte_keeb_tkl_values = +{ + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC INS HOME PGUP */ + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + /* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN */ + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + /* CPLK A S D F G H J K L ; " # ENTR */ + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + /* LSFT \ Z X C V B N M , . / RSFT ARWU */ + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 97, 99, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR */ + 105, 106, 107, 111, 115, 116, 117, 118, 119, 120, 121, +}; + +keyboard_keymap_overlay_values hyte_keeb_tkl_layout = +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + hyte_keeb_tkl_values, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys - Add additional LEDs for space bar underglow and media keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 5, 4, 109, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 5, 5, 110, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 5, 7, 112, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 5, 8, 113, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 0, 0, 77, KEY_EN_MEDIA_STOP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 0, 1, 78, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 0, 2, 79, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 0, 3, 98, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 0, 4, 100, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + } +}; + +RGBController_HYTEKeyboard::RGBController_HYTEKeyboard(HYTEKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "HYTE"; + type = DEVICE_TYPE_KEYBOARD; + description = "HYTE Keyboard Device"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_HYTEKeyboard::~RGBController_HYTEKeyboard() +{ + delete controller; +} + +void RGBController_HYTEKeyboard::SetupZones() +{ + KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_DEFAULT, hyte_keeb_tkl_layout.base_size, hyte_keeb_tkl_layout.key_values); + new_kb.ChangeKeys(hyte_keeb_tkl_layout.edit_keys); + + zone keyboard_zone; + + keyboard_zone.name = "Keyboard"; + keyboard_zone.type = ZONE_TYPE_MATRIX; + + matrix_map_type * keyboard_map = new matrix_map_type; + keyboard_zone.matrix_map = keyboard_map; + keyboard_zone.matrix_map->height = new_kb.GetRowCount(); + keyboard_zone.matrix_map->width = new_kb.GetColumnCount(); + + keyboard_zone.matrix_map->map = new unsigned int[keyboard_map->height * keyboard_map->width]; + keyboard_zone.leds_count = new_kb.GetKeyCount(); + keyboard_zone.leds_min = keyboard_zone.leds_count; + keyboard_zone.leds_max = keyboard_zone.leds_count; + + zones.push_back(keyboard_zone); + + /*---------------------------------------------------------*\ + | Matrix map still uses declared zone rows and columns | + | as the packet structure depends on the matrix map | + \*---------------------------------------------------------*/ + new_kb.GetKeyMap(keyboard_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT, keyboard_map->height, keyboard_map->width); + + /*---------------------------------------------------------*\ + | Create LEDs for the Matrix zone | + | Place keys in the layout to populate the matrix | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < keyboard_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + + leds.push_back(new_led); + } + + zone surround_zone; + + surround_zone.name = "Underglow"; + surround_zone.type = ZONE_TYPE_LINEAR; + surround_zone.leds_min = 63; + surround_zone.leds_max = 63; + surround_zone.leds_count = 63; + surround_zone.matrix_map = NULL; + + zones.push_back(surround_zone); + + for(unsigned int led_idx = 0; led_idx < surround_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = surround_zone.name; + + leds.push_back(new_led); + } + + + SetupColors(); +} + +void RGBController_HYTEKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HYTEKeyboard::DeviceUpdateLEDs() +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + UpdateZoneLEDs(zone_idx); + } +} + +void RGBController_HYTEKeyboard::UpdateZoneLEDs(int zone) +{ + if(zone == HYTE_KEYBOARD_ZONE_KEYBOARD) + { + RGBColor color_buf[127]; + + for(unsigned int led_idx = 0; led_idx < zones[HYTE_KEYBOARD_ZONE_KEYBOARD].leds_count; led_idx++) + { + color_buf[leds[led_idx].value] = colors[led_idx]; + } + + controller->LEDStreaming(zone, &color_buf[0]); + } + else + { + controller->LEDStreaming(zone, zones[zone].colors); + } +} + +void RGBController_HYTEKeyboard::UpdateSingleLED(int led) +{ + if(led < (int)zones[0].leds_count) + { + UpdateZoneLEDs(0); + } + else + { + UpdateZoneLEDs(1); + } +} + +void RGBController_HYTEKeyboard::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/HYTEKeyboardController/RGBController_HYTEKeyboard.h b/Controllers/HYTEKeyboardController/RGBController_HYTEKeyboard.h new file mode 100644 index 0000000..d9411d2 --- /dev/null +++ b/Controllers/HYTEKeyboardController/RGBController_HYTEKeyboard.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_HYTEKeyboard.h | +| | +| RGBController for HYTE keyboard | +| | +| Adam Honse (calcprogrammer1@gmail.com) 30 Oct 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "HYTEKeyboardController.h" +#include "RGBController.h" + +class RGBController_HYTEKeyboard : public RGBController +{ +public: + RGBController_HYTEKeyboard(HYTEKeyboardController* controller_ptr); + ~RGBController_HYTEKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + HYTEKeyboardController* controller; +}; diff --git a/Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematControllerDetect_FreeBSD_Linux.cpp b/Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematControllerDetect_FreeBSD_Linux.cpp new file mode 100644 index 0000000..76fe20c --- /dev/null +++ b/Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematControllerDetect_FreeBSD_Linux.cpp @@ -0,0 +1,88 @@ +/*---------------------------------------------------------*\ +| HYTEMousematControllerDetect_FreeBSD_Linux.cpp | +| | +| Detector for HYTE mousemat (libusb implementation for | +| FreeBSD / Linux) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 18 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "RGBController_HYTEMousemat.h" + +/*-----------------------------------------------------*\ +| HYTE vendor ID | +\*-----------------------------------------------------*/ +#define HYTE_VID 0x3402 + +/*-----------------------------------------------------*\ +| HYTE CNVS product IDs | +\*-----------------------------------------------------*/ +#define HYTE_CNVS_HW_VER_1_PID 0x0B00 +#define HYTE_CNVS_HW_VER_2_PID 0x0B01 + +typedef struct +{ + unsigned short usb_vid; + unsigned short usb_pid; + unsigned char usb_interface; + const char * name; +} hyte_mousemat_device; + +#define HYTE_MOUSEMAT_NUM_DEVICES (sizeof(device_list) / sizeof(device_list[ 0 ])) + +static const hyte_mousemat_device device_list[] = +{ + /*-----------------------------------------------------------------------------------------------------*\ + | Mousemats | + \*-----------------------------------------------------------------------------------------------------*/ + { HYTE_VID, HYTE_CNVS_HW_VER_1_PID, 0, "HYTE CNVS" }, + { HYTE_VID, HYTE_CNVS_HW_VER_2_PID, 0, "HYTE CNVS" }, +}; + +/******************************************************************************************\ +* * +* DetectHYTEMousematControllers * +* * +* Detect devices supported by the HyteMousemat driver * +* * +\******************************************************************************************/ + +void DetectHYTEMousematControllers() +{ + libusb_init(NULL); + + #ifdef _WIN32 + libusb_set_option(NULL, LIBUSB_OPTION_USE_USBDK); + #endif + + for(std::size_t device_idx = 0; device_idx < HYTE_MOUSEMAT_NUM_DEVICES; device_idx++) + { + libusb_device_handle * dev = libusb_open_device_with_vid_pid(NULL, device_list[device_idx].usb_vid, device_list[device_idx].usb_pid); + + //Look for HYTE CNVS + if(dev) + { + libusb_detach_kernel_driver(dev, 0); + libusb_claim_interface(dev, 0); + + HYTEMousematController * controller = new HYTEMousematController(dev, device_list[device_idx].name); + RGBController_HYTEMousemat * rgb_controller = new RGBController_HYTEMousemat(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + +} /* DetectHYTEMousematControllers() */ + +REGISTER_DETECTOR("HYTE Mousemat", DetectHYTEMousematControllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("HYTE Mousemat", DetectHYTEMousematControllers, 0x3402, 0x0B00 ) | +| DUMMY_DEVICE_DETECTOR("HYTE Mousemat", DetectHYTEMousematControllers, 0x3402, 0x0B01 ) | +\*---------------------------------------------------------------------------------------------------------*/ diff --git a/Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematController_FreeBSD_Linux.cpp b/Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematController_FreeBSD_Linux.cpp new file mode 100644 index 0000000..390d948 --- /dev/null +++ b/Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematController_FreeBSD_Linux.cpp @@ -0,0 +1,109 @@ +/*---------------------------------------------------------*\ +| HYTEMousematController_FreeBSD_Linux.cpp | +| | +| Driver for HYTE mousemat (libusb implementation for | +| FreeBSD / Linux) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 18 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "HYTEMousematController_FreeBSD_Linux.h" + +HYTEMousematController::HYTEMousematController(libusb_device_handle* dev_handle, std::string dev_name) +{ + dev = dev_handle; + name = dev_name; + + /*-----------------------------------------------------*\ + | Fill in location string with USB ID | + \*-----------------------------------------------------*/ + libusb_device_descriptor descriptor; + libusb_get_device_descriptor(libusb_get_device(dev_handle), &descriptor); + + std::stringstream location_stream; + location_stream << std::hex << std::setfill('0') << std::setw(4) << descriptor.idVendor << ":" << std::hex << std::setfill('0') << std::setw(4) << descriptor.idProduct; + location = location_stream.str(); +} + +HYTEMousematController::~HYTEMousematController() +{ + libusb_release_interface(dev, 0); + libusb_attach_kernel_driver(dev, 0); + libusb_close(dev); +} + +std::string HYTEMousematController::GetLocation() +{ + return(location); +} + +std::string HYTEMousematController::GetName() +{ + return(name); +} + +void HYTEMousematController::FirmwareAnimationControl(bool enabled) +{ + unsigned char serial_buf[4]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(serial_buf, 0, sizeof(serial_buf)); + + /*-----------------------------------------------------*\ + | Set up Firmware Animation Control packet | + \*-----------------------------------------------------*/ + serial_buf[0] = 0xFF; + serial_buf[1] = 0xDC; + serial_buf[2] = 0x05; + serial_buf[3] = enabled; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + libusb_bulk_transfer(dev, HYTE_CNVS_EP_OUT, serial_buf, sizeof(serial_buf), NULL, 1000); +} + +void HYTEMousematController::StreamingCommand(RGBColor* colors) +{ + unsigned char serial_buf[157]; + unsigned int max_brightness = 72; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(serial_buf, 0, sizeof(serial_buf)); + + /*-----------------------------------------------------*\ + | Set up Streaming packet | + \*-----------------------------------------------------*/ + serial_buf[0] = 0xFF; + serial_buf[1] = 0xEE; + serial_buf[2] = 0x02; + serial_buf[3] = 0x01; + serial_buf[4] = 0x00; + serial_buf[5] = 0x32; + serial_buf[6] = 0x00; + + /*-----------------------------------------------------*\ + | Copy in colors | + \*-----------------------------------------------------*/ + for(unsigned int color_idx = 0; color_idx < 50; color_idx++) + { + serial_buf[7 + (color_idx * 3)] = ( max_brightness * RGBGetGValue(colors[color_idx]) ) / 100; + serial_buf[8 + (color_idx * 3)] = ( max_brightness * RGBGetRValue(colors[color_idx]) ) / 100; + serial_buf[9 + (color_idx * 3)] = ( max_brightness * RGBGetBValue(colors[color_idx]) ) / 100; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + libusb_bulk_transfer(dev, HYTE_CNVS_EP_OUT, serial_buf, sizeof(serial_buf), NULL, 1000); +} diff --git a/Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematController_FreeBSD_Linux.h b/Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematController_FreeBSD_Linux.h new file mode 100644 index 0000000..62205e4 --- /dev/null +++ b/Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematController_FreeBSD_Linux.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| HYTEMousematController_FreeBSD_Linux.h | +| | +| Driver for HYTE mousemat (libusb implementation for | +| FreeBSD / Linux) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 18 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +/*---------------------------------------------------------*\ +| HYTE CNVS endpoint values | +\*---------------------------------------------------------*/ +#define HYTE_CNVS_EP_IN 0x81 +#define HYTE_CNVS_EP_OUT 0x01 + +class HYTEMousematController +{ +public: + HYTEMousematController(libusb_device_handle* dev_handle, std::string dev_name); + ~HYTEMousematController(); + + std::string GetLocation(); + std::string GetName(); + + void FirmwareAnimationControl(bool enabled); + void StreamingCommand(RGBColor* colors); + +private: + libusb_device_handle* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematControllerDetect_Windows_MacOS.cpp b/Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematControllerDetect_Windows_MacOS.cpp new file mode 100644 index 0000000..733df2c --- /dev/null +++ b/Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematControllerDetect_Windows_MacOS.cpp @@ -0,0 +1,72 @@ +/*---------------------------------------------------------*\ +| HYTEMousematControllerDetect_Windows_MacOS.cpp | +| | +| Detector for HYTE mousemat (Serial implementation for | +| Windows and MacOS) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 18 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "HYTEMousematController_Windows_MacOS.h" +#include "RGBController_HYTEMousemat.h" +#include "find_usb_serial_port.h" + +#define HYTE_VID 0x3402 + +#define HYTE_CNVS_HW_VER_1_PID 0x0B00 +#define HYTE_CNVS_HW_VER_2_PID 0x0B01 + +struct hyte_mousemat_type +{ + unsigned short vid; + unsigned short pid; + const char * name; +}; + +#define HYTE_MOUSEMAT_NUM_DEVICES 2 + +static const hyte_mousemat_type hyte_mousemat_devices[] = +{ + { HYTE_VID, HYTE_CNVS_HW_VER_1_PID, "HYTE CNVS" }, + { HYTE_VID, HYTE_CNVS_HW_VER_2_PID, "HYTE CNVS" }, +}; + +/******************************************************************************************\ +* * +* DetectHYTEMousematControllers * +* * +* Detect devices supported by the HyteMousemat driver * +* * +\******************************************************************************************/ + +void DetectHYTEMousematControllers() +{ + for(unsigned int device_id = 0; device_id < HYTE_MOUSEMAT_NUM_DEVICES; device_id++) + { + std::vector ports = find_usb_serial_port(hyte_mousemat_devices[device_id].vid, hyte_mousemat_devices[device_id].pid); + + for(unsigned int i = 0; i < ports.size(); i++) + { + if(*ports[i] != "") + { + HYTEMousematController * controller = new HYTEMousematController((char *)ports[i]->c_str(), hyte_mousemat_devices[device_id].name); + RGBController_HYTEMousemat * rgb_controller = new RGBController_HYTEMousemat(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } +} /* DetectHYTEMousematControllers() */ + +REGISTER_DETECTOR("HYTE Mousemat", DetectHYTEMousematControllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("HYTE Mousemat", DetectHYTEMousematControllers, 0x3402, 0x0B00 ) | +| DUMMY_DEVICE_DETECTOR("HYTE Mousemat", DetectHYTEMousematControllers, 0x3402, 0x0B01 ) | +\*---------------------------------------------------------------------------------------------------------*/ diff --git a/Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematController_Windows_MacOS.cpp b/Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematController_Windows_MacOS.cpp new file mode 100644 index 0000000..8bf0291 --- /dev/null +++ b/Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematController_Windows_MacOS.cpp @@ -0,0 +1,100 @@ +/*---------------------------------------------------------*\ +| HYTEMousematController_Windows_MacOS.cpp | +| | +| Driver for HYTE mousemat (Serial implementation for | +| Windows and MacOS) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 18 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "HYTEMousematController_Windows_MacOS.h" + +HYTEMousematController::HYTEMousematController(char* port, std::string dev_name) +{ + name = dev_name; + port_name = port; + + /*-----------------------------------------------------*\ + | Open the port | + | Baud rate doesn't matter for ACM device | + \*-----------------------------------------------------*/ + serialport = new serial_port(port_name.c_str(), 2000000); +} + +HYTEMousematController::~HYTEMousematController() +{ + serialport->serial_close(); +} + +std::string HYTEMousematController::GetLocation() +{ + return(port_name); +} + +std::string HYTEMousematController::GetName() +{ + return(name); +} + +void HYTEMousematController::FirmwareAnimationControl(bool enabled) +{ + unsigned char serial_buf[4]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(serial_buf, 0, sizeof(serial_buf)); + + /*-----------------------------------------------------*\ + | Set up Firmware Animation Control packet | + \*-----------------------------------------------------*/ + serial_buf[0] = 0xFF; + serial_buf[1] = 0xDC; + serial_buf[2] = 0x05; + serial_buf[3] = enabled; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + serialport->serial_write((char *)serial_buf, sizeof(serial_buf)); +} + +void HYTEMousematController::StreamingCommand(RGBColor* colors) +{ + unsigned char serial_buf[157]; + unsigned int max_brightness = 72; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(serial_buf, 0, sizeof(serial_buf)); + + /*-----------------------------------------------------*\ + | Set up Streaming packet | + \*-----------------------------------------------------*/ + serial_buf[0] = 0xFF; + serial_buf[1] = 0xEE; + serial_buf[2] = 0x02; + serial_buf[3] = 0x01; + serial_buf[4] = 0x00; + serial_buf[5] = 0x32; + serial_buf[6] = 0x00; + + /*-----------------------------------------------------*\ + | Copy in colors | + \*-----------------------------------------------------*/ + for(unsigned int color_idx = 0; color_idx < 50; color_idx++) + { + serial_buf[7 + (color_idx * 3)] = ( max_brightness * RGBGetGValue(colors[color_idx]) ) / 100; + serial_buf[8 + (color_idx * 3)] = ( max_brightness * RGBGetRValue(colors[color_idx]) ) / 100; + serial_buf[9 + (color_idx * 3)] = ( max_brightness * RGBGetBValue(colors[color_idx]) ) / 100; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + serialport->serial_write((char *)serial_buf, sizeof(serial_buf)); +} diff --git a/Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematController_Windows_MacOS.h b/Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematController_Windows_MacOS.h new file mode 100644 index 0000000..771d694 --- /dev/null +++ b/Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematController_Windows_MacOS.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| HYTEMousematController_Windows_MacOS.h | +| | +| Driver for HYTE mousemat (Serial implementation for | +| Windows and MacOS) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 18 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "serial_port.h" + +class HYTEMousematController +{ +public: + HYTEMousematController(char* port, std::string dev_name); + ~HYTEMousematController(); + + std::string GetLocation(); + std::string GetName(); + + void FirmwareAnimationControl(bool enabled); + void StreamingCommand(RGBColor* colors); + +private: + std::string name; + std::string port_name; + serial_port * serialport = nullptr; +}; diff --git a/Controllers/HYTEMousematController/RGBController_HYTEMousemat.cpp b/Controllers/HYTEMousematController/RGBController_HYTEMousemat.cpp new file mode 100644 index 0000000..db04588 --- /dev/null +++ b/Controllers/HYTEMousematController/RGBController_HYTEMousemat.cpp @@ -0,0 +1,117 @@ +/*---------------------------------------------------------*\ +| RGBController_HYTEMousemat.cpp | +| | +| RGBController for HYTE mousemat | +| | +| Adam Honse (calcprogrammer1@gmail.com) 18 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HYTEMousemat.h" + +/**------------------------------------------------------------------*\ + @name HYTE Mousemat + @category Mousemat + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHYTEMousematControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HYTEMousemat::RGBController_HYTEMousemat(HYTEMousematController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "HYTE"; + description = "HYTE Mousemat Device"; + type = DEVICE_TYPE_MOUSEMAT; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = HYTE_CNVS_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + // HYTE CNVS does not seem to be able to transfer back into firmware animation + // after streaming command has been used + //mode Rainbow; + //Rainbow.name = "Rainbow Wave"; + //Rainbow.value = HYTE_CNVS_MODE_RAINBOW; + //Rainbow.flags = MODE_FLAG_HAS_RANDOM_COLOR; + //Rainbow.color_mode = MODE_COLORS_RANDOM; + //modes.push_back(Rainbow); + + SetupZones(); +} + +RGBController_HYTEMousemat::~RGBController_HYTEMousemat() +{ + delete controller; +} + +void RGBController_HYTEMousemat::SetupZones() +{ + zone mousemat_zone; + + mousemat_zone.name = "Mousemat"; + mousemat_zone.type = ZONE_TYPE_LINEAR; + mousemat_zone.leds_min = 50; + mousemat_zone.leds_max = 50; + mousemat_zone.leds_count = 50; + mousemat_zone.matrix_map = NULL; + + zones.push_back(mousemat_zone); + + for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led mousemat_led; + + mousemat_led.name = "Mousemat LED "; + mousemat_led.name.append(std::to_string(led_idx)); + + leds.push_back(mousemat_led); + } + + SetupColors(); +} + +void RGBController_HYTEMousemat::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_HYTEMousemat::DeviceUpdateLEDs() +{ + controller->StreamingCommand(&colors[0]); +} + +void RGBController_HYTEMousemat::UpdateZoneLEDs(int /*zone*/) +{ + +} + +void RGBController_HYTEMousemat::UpdateSingleLED(int /*led*/) +{ + +} + +void RGBController_HYTEMousemat::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case HYTE_CNVS_MODE_DIRECT: + controller->FirmwareAnimationControl(false); + break; + + case HYTE_CNVS_MODE_RAINBOW: + controller->FirmwareAnimationControl(true); + break; + } +} diff --git a/Controllers/HYTEMousematController/RGBController_HYTEMousemat.h b/Controllers/HYTEMousematController/RGBController_HYTEMousemat.h new file mode 100644 index 0000000..c57d188 --- /dev/null +++ b/Controllers/HYTEMousematController/RGBController_HYTEMousemat.h @@ -0,0 +1,48 @@ +/*---------------------------------------------------------*\ +| RGBController_HYTEMousemat.h | +| | +| RGBController for HYTE mousemat | +| | +| Adam Honse (calcprogrammer1@gmail.com) 18 Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" + +#if defined(_WIN32) || defined(__APPLE__) +#include "HYTEMousematController_Windows_MacOS.h" +#endif + +#if defined(__FreeBSD__) || defined(__linux__) +#include "HYTEMousematController_FreeBSD_Linux.h" +#endif + + +enum +{ + HYTE_CNVS_MODE_DIRECT = 0, /* Direct (streaming) mode */ + HYTE_CNVS_MODE_RAINBOW = 1, /* Rainbow wave (firmware animation) mode */ +}; + +class RGBController_HYTEMousemat : public RGBController +{ +public: + RGBController_HYTEMousemat(HYTEMousematController* controller_ptr); + ~RGBController_HYTEMousemat(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + HYTEMousematController* controller; +}; diff --git a/Controllers/HYTENexusController/HYTENexusController.cpp b/Controllers/HYTENexusController/HYTENexusController.cpp new file mode 100644 index 0000000..69e68c4 --- /dev/null +++ b/Controllers/HYTENexusController/HYTENexusController.cpp @@ -0,0 +1,407 @@ +/*---------------------------------------------------------*\ +| HYTENexusController.cpp | +| | +| Driver for HYTE Nexus | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "HYTENexusController.h" +#include "LogManager.h" + +using namespace std::chrono_literals; + +/*---------------------------------------------------------*\ +| The protocol for the HYTE NP50 and Q60 are documented | +| on hackmd.io: | +| NP50: https://hackmd.io/3X_ojT77Sr-sLt5Fo2CYMQ | +| Q60: https://hackmd.io/7qUhUQfIQReQYNhdGLqO6g | +| | +| More information on the HYTE Nexus Playground site: | +| https://hyte.com/nexus/nexus-playground | +\*---------------------------------------------------------*/ + +HYTENexusController::HYTENexusController(char* port, unsigned short pid, std::string dev_name) +{ + port_name = port; + device_pid = pid; + name = dev_name; + + /*-----------------------------------------------------*\ + | Initialize channels based on PID | + \*-----------------------------------------------------*/ + memset(channels, 0, sizeof(channels)); + + switch(pid) + { + case HYTE_NEXUS_PORTAL_NP50_PID: + num_channels = 3; + + channels[0].is_nexus_channel = true; + channels[1].is_nexus_channel = true; + channels[2].is_nexus_channel = true; + + channels[0].has_6_led_logo = true; + break; + + case HYTE_THICC_Q60_PID: + num_channels = 4; + + channels[0].is_nexus_channel = true; + channels[1].is_nexus_channel = true; + channels[2].is_nexus_channel = false; + channels[3].is_nexus_channel = false; + + channels[2].has_lcd_leds = true; + channels[3].has_4_led_logo = true; + break; + + default: + num_channels = 0; + break; + } + + /*-----------------------------------------------------*\ + | Open the port | + | Baud rate doesn't matter for ACM device | + \*-----------------------------------------------------*/ + serialport = new serial_port(port_name.c_str(), 2000000); + + /*-----------------------------------------------------*\ + | Get controller information and firmware version | + \*-----------------------------------------------------*/ + ReadDeviceInfo(); + ReadFirmwareVersion(); + + /*-----------------------------------------------------*\ + | Get attached device information for all channels | + \*-----------------------------------------------------*/ + for(unsigned int channel = 0; channel < num_channels; channel++) + { + if(channels[channel].is_nexus_channel) + { + ReadChannelInfo(channel); + } + } + + keepalive_thread_run = true; + keepalive_thread = std::thread(&HYTENexusController::KeepaliveThreadFunction, this); +} + +HYTENexusController::~HYTENexusController() +{ + keepalive_thread_run = false; + keepalive_thread.join(); + + serialport->serial_close(); +} + +std::string HYTENexusController::GetFirmwareVersion() +{ + return(firmware_version); +} + +std::string HYTENexusController::GetLocation() +{ + return(port_name); +} + +std::string HYTENexusController::GetName() +{ + return(name); +} + +void HYTENexusController::KeepaliveThreadFunction() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_update_time) > 2500ms) + { + ReadDeviceInfo(); + } + std::this_thread::sleep_for(1s); + } +} + +std::string HYTENexusController::GetDeviceName(unsigned int device_type) +{ + std::string device_name = ""; + + switch(device_type) + { + case HYTE_NEXUS_DEVICE_TYPE_LS10: + device_name = "LS10"; + break; + + case HYTE_NEXUS_DEVICE_TYPE_LS30: + device_name = "LS30"; + break; + + case HYTE_NEXUS_DEVICE_TYPE_FP12: + device_name = "FP12"; + break; + + case HYTE_NEXUS_DEVICE_TYPE_FP12_DUO: + device_name = "FP12 Duo"; + break; + + case HYTE_NEXUS_DEVICE_TYPE_FP12_TRIO: + device_name = "FP12 Trio"; + break; + + case HYTE_NEXUS_DEVICE_TYPE_LN4060: + device_name = "LN4060"; + break; + + case HYTE_NEXUS_DEVICE_TYPE_LN70: + device_name = "LN70"; + break; + } + + return(device_name); +} + +void HYTENexusController::LEDStreaming(unsigned char channel, unsigned short led_count, RGBColor* colors) +{ + /*-----------------------------------------------------*\ + | Send LED Streaming command | + | Byte 0: FF | + | Byte 1: EE | + | Byte 2: 01 | + | Byte 3: Channel (Port1, Port2, or Port3) | + | Byte 4: LEDCount_H | + | Byte 5: LEDCount_L | + | Byte 6: Reserved | + | Byte 7: G | + | Byte 8: R | + | Byte 9: B | + | Repeat GRB pattern for remaining bytes | + \*-----------------------------------------------------*/ + unsigned char command_buf[750]; + + memset(command_buf, 0, sizeof(command_buf)); + + command_buf[0] = 0xFF; + command_buf[1] = 0xEE; + command_buf[2] = 0x01; + command_buf[3] = (channel + 1); + command_buf[4] = (led_count >> 8); + command_buf[5] = (led_count & 0xFF); + + for(unsigned int led_idx = 0; led_idx < led_count; led_idx++) + { + unsigned int offset = (led_idx * 3); + + command_buf[7 + offset] = RGBGetGValue(colors[led_idx]); + command_buf[8 + offset] = RGBGetRValue(colors[led_idx]); + command_buf[9 + offset] = RGBGetBValue(colors[led_idx]); + } + + /*-----------------------------------------------------*\ + | The default data length is (led_count * 3) + 7 | + \*-----------------------------------------------------*/ + unsigned int bytes_to_send = ((led_count * 3) + 7); + + /*-----------------------------------------------------*\ + | The HYTE THICC Q60 requires 90 bytes to be sent for | + | the 4th channel (logo) even though it only has 4 LEDs | + \*-----------------------------------------------------*/ + if((device_pid == HYTE_THICC_Q60_PID) + && (channel == 3) + && (bytes_to_send < 90)) + { + bytes_to_send = 90; + } + + /*-----------------------------------------------------*\ + | The HYTE Nexus Portal NP50 requires 750 bytes to be | + | sent for the 3rd channel, no matter how many LEDs it | + | has connected | + \*-----------------------------------------------------*/ + if((device_pid == HYTE_NEXUS_PORTAL_NP50_PID) + && (channel == 2) + && (bytes_to_send < 750)) + { + bytes_to_send = 750; + } + + port_mutex.lock(); + serialport->serial_write((char *)command_buf, bytes_to_send); + serialport->serial_flush_tx(); + serialport->serial_flush_rx(); + port_mutex.unlock(); +} + +void HYTENexusController::ReadChannelInfo(unsigned char channel) +{ + /*-----------------------------------------------------*\ + | Send Get Channel Info command | + | Byte 0: FF | + | Byte 1: CC | + | Byte 2: 01 (Get Status) | + | Byte 3: Channel (Port1, Port2, or Port3) | + \*-----------------------------------------------------*/ + unsigned char command_buf[4]; + + command_buf[0] = 0xFF; + command_buf[1] = 0xCC; + command_buf[2] = 0x01; + command_buf[3] = (channel + 1); + + port_mutex.lock(); + serialport->serial_write((char *)command_buf, sizeof(command_buf)); + serialport->serial_flush_tx(); + + /*-----------------------------------------------------*\ + | Wait 50ms for device to send response | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(50ms); + + /*-----------------------------------------------------*\ + | Receive Channel Info | + | First Device Additional Devices | + | Byte 0: FF 00 | + | Byte 1: CC 00 | + | Byte 2: Device Count | + | Byte 3: Device Type | + | Byte 4: Hardware Version | + | Byte 5: LED Count | + | Byte 6: Fan Temp_H | + | Byte 7: Fan Temp_L | + | Byte 8: Fan RPM_H | + | Byte 9: Fan RPM_L | + | Byte 10: Fan Orientation | + | Byte 11: Touch | + | | + | Format repeats with offset of 12 * n for nth device | + | in the list, except for the first two bytes being | + | zero. | + \*-----------------------------------------------------*/ + unsigned char receive_buf[240]; + + serialport->serial_read((char *)receive_buf, sizeof(receive_buf)); + serialport->serial_flush_rx(); + port_mutex.unlock(); + + channels[channel].num_devices = 0; + memset(&channels[channel].devices, 0, sizeof(channels[channel].devices)); + + for(unsigned int device = 0; device < 19; device++) + { + unsigned int offset = 12 * device; + + if(receive_buf[offset + 2] > 0) + { + channels[channel].num_devices++; + + channels[channel].devices[device].device_type = receive_buf[offset + 3]; + channels[channel].devices[device].hardware_version = receive_buf[offset + 4]; + channels[channel].devices[device].led_count = receive_buf[offset + 5]; + } + } +} + +void HYTENexusController::ReadDeviceInfo() +{ + /*-----------------------------------------------------*\ + | Send Get Device Info command | + | Byte 0: FF | + | Byte 1: CC | + | Byte 2: 01 (Get Status) | + | Byte 3: 00 (NP50/Pump) | + \*-----------------------------------------------------*/ + unsigned char command_buf[4]; + + command_buf[0] = 0xFF; + command_buf[1] = 0xCC; + command_buf[2] = 0x01; + command_buf[3] = 0x00; + + port_mutex.lock(); + serialport->serial_write((char *)command_buf, sizeof(command_buf)); + serialport->serial_flush_tx(); + + /*-----------------------------------------------------*\ + | Wait 50ms for device to send response | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(50ms); + + /*-----------------------------------------------------*\ + | Receive Device Info | + | NP50 Q60 | + | Byte 0: FF FF | + | Byte 1: CC CC | + | Byte 2: 00 00 | + | Byte 3: Noise_H Reserve | + | Byte 4: Noise_L Reserve | + | Byte 5: Reserve In Liquid Temp_H | + | Byte 6: Reserve In Liquid Temp_L | + | Byte 7: Temp_H Out Liquid Temp_H | + | Byte 8: Temp_L Out Liquid Temp_L | + | Byte 9: RPM_H Pump RPM_H | + | Byte 10: RPM_L Pump RPM_L | + | Byte 11: Reserve Fan Exhaust/Intake | + | Byte 12: Current Cooling Mode Current Cooling Mode| + | Byte 13: Warning Warnings | + \*-----------------------------------------------------*/ + unsigned char receive_buf[14]; + + serialport->serial_read((char *)receive_buf, sizeof(receive_buf)); + serialport->serial_flush_rx(); + port_mutex.unlock(); + + /*-----------------------------------------------------*\ + | Update last update time | + \*-----------------------------------------------------*/ + last_update_time = std::chrono::steady_clock::now(); +} + +void HYTENexusController::ReadFirmwareVersion() +{ + /*-----------------------------------------------------*\ + | Send Get Firmware Version command | + | Byte 0: FF | + | Byte 1: DD | + | Byte 2: 02 | + | Byte 3: 00 (Reserve) | + \*-----------------------------------------------------*/ + unsigned char command_buf[4]; + + command_buf[0] = 0xFF; + command_buf[1] = 0xDD; + command_buf[2] = 0x02; + command_buf[3] = 0x00; + + port_mutex.lock(); + serialport->serial_write((char *)command_buf, sizeof(command_buf)); + serialport->serial_flush_tx(); + + /*-----------------------------------------------------*\ + | Wait 50ms for device to send response | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(50ms); + + /*-----------------------------------------------------*\ + | Receive Firmware Version | + | Byte 0: FF | + | Byte 1: DD | + | Byte 2: 02 | + | Byte 3: Large Version | + | Byte 4: Mid Version | + | Byte 5: Small Version | + | Byte 6: Hardware Version | + \*-----------------------------------------------------*/ + unsigned char receive_buf[7]; + + serialport->serial_read((char *)receive_buf, sizeof(receive_buf)); + serialport->serial_flush_rx(); + port_mutex.unlock(); + + /*-----------------------------------------------------*\ + | Format Firmware Version string | + \*-----------------------------------------------------*/ + firmware_version = "FW: " + std::to_string(receive_buf[3]) + "." + std::to_string(receive_buf[4]) + "." + std::to_string(receive_buf[5]) + ", HW: " + std::to_string(receive_buf[6]); +} diff --git a/Controllers/HYTENexusController/HYTENexusController.h b/Controllers/HYTENexusController/HYTENexusController.h new file mode 100644 index 0000000..b7ab4d3 --- /dev/null +++ b/Controllers/HYTENexusController/HYTENexusController.h @@ -0,0 +1,87 @@ +/*---------------------------------------------------------*\ +| HYTENexusController.h | +| | +| Driver for HYTE Nexus | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include "RGBController.h" +#include "serial_port.h" + +#define HYTE_THICC_Q60_PID 0x0400 +#define HYTE_NEXUS_PORTAL_NP50_PID 0x0901 + +typedef struct +{ + unsigned int device_type; + unsigned int hardware_version; + unsigned char led_count; +} hyte_nexus_device; + +typedef struct +{ + bool is_nexus_channel; + bool has_4_led_logo; + bool has_6_led_logo; + bool has_lcd_leds; + unsigned int num_devices; + hyte_nexus_device devices[19]; +} hyte_nexus_channel; + +enum +{ + HYTE_NEXUS_DEVICE_TYPE_LS10 = 0x01, + HYTE_NEXUS_DEVICE_TYPE_LS30 = 0x02, + HYTE_NEXUS_DEVICE_TYPE_FP12 = 0x03, + HYTE_NEXUS_DEVICE_TYPE_FP12_DUO = 0x04, + HYTE_NEXUS_DEVICE_TYPE_FP12_TRIO = 0x05, + HYTE_NEXUS_DEVICE_TYPE_LN4060 = 0x06, + HYTE_NEXUS_DEVICE_TYPE_LN70 = 0x07, +}; + +class HYTENexusController +{ +public: + HYTENexusController(char* port, unsigned short pid, std::string dev_name); + ~HYTENexusController(); + + std::string GetFirmwareVersion(); + std::string GetLocation(); + std::string GetName(); + std::string GetDeviceName(unsigned int device_type); + + void LEDStreaming(unsigned char channel, unsigned short num_leds, RGBColor* colors); + + hyte_nexus_channel channels[4]; + unsigned int num_channels; + unsigned short device_pid; + +private: + std::string firmware_version; + std::string name; + std::string port_name; + std::mutex port_mutex; + serial_port * serialport = nullptr; + std::chrono::time_point last_update_time; + std::atomic keepalive_thread_run; + std::thread keepalive_thread; + + void KeepaliveThreadFunction(); + + void ReadChannelInfo(unsigned char channel); + void ReadDeviceInfo(); + void ReadFirmwareVersion(); + + void SetStartupAnimation(bool enable); +}; diff --git a/Controllers/HYTENexusController/HYTENexusControllerDetect.cpp b/Controllers/HYTENexusController/HYTENexusControllerDetect.cpp new file mode 100644 index 0000000..f669877 --- /dev/null +++ b/Controllers/HYTENexusController/HYTENexusControllerDetect.cpp @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| HYTENexusControllerDetect.cpp | +| | +| Detector for HYTE Nexus | +| | +| Adam Honse (calcprogrammer1@gmail.com) 19 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "HYTENexusController.h" +#include "RGBController_HYTENexus.h" +#include "find_usb_serial_port.h" + +#define HYTE_VID 0x3402 + +struct hyte_nexus_type +{ + unsigned short vid; + unsigned short pid; + const char * name; +}; + +#define HYTE_NEXUS_NUM_DEVICES 2 + +static const hyte_nexus_type hyte_nexus_devices[] = +{ + { HYTE_VID, HYTE_THICC_Q60_PID, "HYTE THICC Q60", }, + { HYTE_VID, HYTE_NEXUS_PORTAL_NP50_PID, "HYTE Nexus Portal NP50" }, +}; + +/******************************************************************************************\ +* * +* DetectHYTENexusControllers * +* * +* Detect devices supported by the HYTENexus driver * +* * +\******************************************************************************************/ + +void DetectHYTENexusControllers() +{ + for(unsigned int device_id = 0; device_id < HYTE_NEXUS_NUM_DEVICES; device_id++) + { + std::vector ports = find_usb_serial_port(hyte_nexus_devices[device_id].vid, hyte_nexus_devices[device_id].pid); + + for(unsigned int i = 0; i < ports.size(); i++) + { + if(*ports[i] != "") + { + HYTENexusController * controller = new HYTENexusController((char *)ports[i]->c_str(), hyte_nexus_devices[device_id].pid, hyte_nexus_devices[device_id].name); + RGBController_HYTENexus * rgb_controller = new RGBController_HYTENexus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } +} /* DetectHYTENexusControllers() */ + +REGISTER_DETECTOR("HYTE Nexus", DetectHYTENexusControllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("HYTE THICC Q60", DetectHYTENexusControllers, 0x3402, 0x0400 ) | +| DUMMY_DEVICE_DETECTOR("HYTE Nexus Portal NP50", DetectHYTENexusControllers, 0x3402, 0x0901 ) | +\*---------------------------------------------------------------------------------------------------------*/ diff --git a/Controllers/HYTENexusController/RGBController_HYTENexus.cpp b/Controllers/HYTENexusController/RGBController_HYTENexus.cpp new file mode 100644 index 0000000..090dfc2 --- /dev/null +++ b/Controllers/HYTENexusController/RGBController_HYTENexus.cpp @@ -0,0 +1,195 @@ +/*---------------------------------------------------------*\ +| RGBController_HYTENexus.cpp | +| | +| RGBController for HYTE Nexus | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HYTENexus.h" + +using namespace std::chrono_literals; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int thicc_q60_matrix_map[9][5] = +{ + { 33, 32, NA, 17, 0 }, + { 34, 31, NA, 16, 1 }, + { 35, 30, NA, 15, 2 }, + { 36, 29, 18, 14, 3 }, + { 37, 28, 19, 13, 4 }, + { 38, 27, 20, 12, 5 }, + { 39, 26, 21, 11, 6 }, + { 40, 25, 22, 10, 7 }, + { 41, 24, 23, 9, 8 }, +}; + +RGBController_HYTENexus::RGBController_HYTENexus(HYTENexusController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "HYTE"; + description = "HYTE Nexus Device"; + type = DEVICE_TYPE_LEDSTRIP; + location = controller->GetLocation(); + version = controller->GetFirmwareVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_HYTENexus::~RGBController_HYTENexus() +{ + delete controller; +} + +void RGBController_HYTENexus::SetupZones() +{ + for(unsigned int channel = 0; channel < controller->num_channels; channel++) + { + unsigned int channel_leds = 0; + unsigned int logo_leds = 0; + + for(unsigned int device = 0; device < controller->channels[channel].num_devices; device++) + { + channel_leds += controller->channels[channel].devices[device].led_count; + } + + if(controller->channels[channel].has_4_led_logo == true || controller->channels[channel].has_6_led_logo == true) + { + if(controller->channels[channel].has_4_led_logo == true) + { + logo_leds = 4; + } + else if(controller->channels[channel].has_6_led_logo == true) + { + logo_leds = 6; + } + + channel_leds += logo_leds; + } + + if(controller->channels[channel].has_lcd_leds == true) + { + channel_leds += 42; + } + + zone channel_zone; + + channel_zone.name = "Channel " + std::to_string(channel); + channel_zone.type = ZONE_TYPE_LINEAR; + channel_zone.leds_min = channel_leds; + channel_zone.leds_max = channel_leds; + channel_zone.leds_count = channel_leds; + channel_zone.matrix_map = NULL; + + for(unsigned int led_idx = 0; led_idx < channel_leds; led_idx++) + { + led channel_led; + + channel_led.name = "Channel " + std::to_string(channel) + " LED " + std::to_string(led_idx); + channel_led.value = channel; + + leds.push_back(channel_led); + } + + unsigned int start_idx = 0; + + if(controller->channels[channel].has_4_led_logo == true || controller->channels[channel].has_6_led_logo == true) + { + segment logo_segment; + + logo_segment.name = "Logo"; + logo_segment.leds_count = logo_leds; + logo_segment.start_idx = start_idx; + logo_segment.type = ZONE_TYPE_SINGLE; + + channel_zone.segments.push_back(logo_segment); + + start_idx += logo_segment.leds_count; + } + + if(controller->channels[channel].has_lcd_leds == true) + { + segment lcd_leds_segment; + + lcd_leds_segment.name = "LCD LED Matrix"; + lcd_leds_segment.leds_count = 42; + lcd_leds_segment.start_idx = start_idx; + lcd_leds_segment.type = ZONE_TYPE_MATRIX; + + channel_zone.type = ZONE_TYPE_MATRIX; + channel_zone.matrix_map = new matrix_map_type; + channel_zone.matrix_map->height = 9; + channel_zone.matrix_map->width = 5; + channel_zone.matrix_map->map = (unsigned int *)&thicc_q60_matrix_map; + + channel_zone.segments.push_back(lcd_leds_segment); + + start_idx += lcd_leds_segment.leds_count; + } + + for(unsigned int device = 0; device < controller->channels[channel].num_devices; device++) + { + if(controller->channels[channel].devices[device].led_count > 0) + { + segment device_segment; + + device_segment.name = controller->GetDeviceName(controller->channels[channel].devices[device].device_type); + device_segment.leds_count = controller->channels[channel].devices[device].led_count; + device_segment.start_idx = start_idx; + device_segment.type = ZONE_TYPE_LINEAR; + + channel_zone.segments.push_back(device_segment); + + start_idx += device_segment.leds_count; + } + } + + zones.push_back(channel_zone); + } + + SetupColors(); +} + +void RGBController_HYTENexus::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HYTENexus::DeviceUpdateLEDs() +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + UpdateZoneLEDs(zone_idx); + } +} + +void RGBController_HYTENexus::UpdateZoneLEDs(int zone) +{ + controller->LEDStreaming(zone, zones[zone].leds_count, zones[zone].colors); +} + +void RGBController_HYTENexus::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(leds[led].value); +} + +void RGBController_HYTENexus::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/HYTENexusController/RGBController_HYTENexus.h b/Controllers/HYTENexusController/RGBController_HYTENexus.h new file mode 100644 index 0000000..e09eb22 --- /dev/null +++ b/Controllers/HYTENexusController/RGBController_HYTENexus.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_HYTENexus.h | +| | +| RGBController for HYTE Nexus | +| | +| Adam Honse (calcprogrammer1@gmail.com) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "HYTENexusController.h" +#include "RGBController.h" + +class RGBController_HYTENexus : public RGBController +{ +public: + RGBController_HYTENexus(HYTENexusController* controller_ptr); + ~RGBController_HYTENexus(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + HYTENexusController* controller; +}; diff --git a/Controllers/HoltekController/HoltekA070Controller/HoltekA070Controller.cpp b/Controllers/HoltekController/HoltekA070Controller/HoltekA070Controller.cpp new file mode 100644 index 0000000..bc74a90 --- /dev/null +++ b/Controllers/HoltekController/HoltekA070Controller/HoltekA070Controller.cpp @@ -0,0 +1,120 @@ +/*---------------------------------------------------------*\ +| HoltekA070Controller.cpp | +| | +| Driver for Holtek mouse | +| | +| Santeri Pikarinen (santeri3700) 01 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HoltekA070Controller.h" +#include "StringUtils.h" + +HoltekA070Controller::HoltekA070Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HoltekA070Controller::~HoltekA070Controller() +{ + hid_close(dev); +} + +std::string HoltekA070Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string HoltekA070Controller::GetNameString() +{ + return(name); +} + +std::string HoltekA070Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void HoltekA070Controller::SendCustomColor + ( + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + char usb_buf[8]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up RGB Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x07; // PACKET SIZE? + usb_buf[0x01] = 0x0a; // SET RGB + usb_buf[0x02] = 0x00; // SAVE (does not work with SET RGB) + usb_buf[0x03] = red; // RED + usb_buf[0x04] = green; // GREEN + usb_buf[0x05] = blue; // BLUE + usb_buf[0x06] = 0x00; // PADDING? + usb_buf[0x07] = 0x00; // PADDING? + + // So far no saving function has been discovered for the "SET RGB" command. + // Such functionality might not even exist for the A070 series. + // The chosen RGB color will therefore reset after a power cycle. + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, (unsigned char *)usb_buf, sizeof(usb_buf)); +} + +void HoltekA070Controller::SendMode + ( + unsigned char mode + ) +{ + + char usb_buf[8]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up lighting mode control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x07; // PACKET SIZE? + usb_buf[0x01] = 0x0b; // SET LIGHTING MODE + usb_buf[0x02] = 0x01; // SAVE + usb_buf[0x03] = mode; // MODE 01-04 + usb_buf[0x04] = 0x00; // PADDING? + usb_buf[0x05] = 0x00; // PADDING? + usb_buf[0x06] = 0x00; // PADDING? + usb_buf[0x07] = 0x00; // PADDING? + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, (unsigned char *)usb_buf, sizeof(usb_buf)); +} diff --git a/Controllers/HoltekController/HoltekA070Controller/HoltekA070Controller.h b/Controllers/HoltekController/HoltekA070Controller/HoltekA070Controller.h new file mode 100644 index 0000000..fc92662 --- /dev/null +++ b/Controllers/HoltekController/HoltekA070Controller/HoltekA070Controller.h @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| HoltekA070Controller.h | +| | +| Driver for Holtek mouse | +| | +| Santeri Pikarinen (santeri3700) 01 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +enum +{ + HOLTEK_A070_MODE_STATIC = 0x01, + HOLTEK_A070_MODE_BREATHING_SLOW = 0x02, + HOLTEK_A070_MODE_BREATHING_MEDIUM = 0x03, + HOLTEK_A070_MODE_BREATHING_FAST = 0x04 +}; + +class HoltekA070Controller +{ +public: + HoltekA070Controller(hid_device* dev_handle, const char* path, std::string dev_name); + ~HoltekA070Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendCustomColor + ( + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SendMode + ( + unsigned char mode + ); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/HoltekController/HoltekA070Controller/RGBController_HoltekA070.cpp b/Controllers/HoltekController/HoltekA070Controller/RGBController_HoltekA070.cpp new file mode 100644 index 0000000..57dd703 --- /dev/null +++ b/Controllers/HoltekController/HoltekA070Controller/RGBController_HoltekA070.cpp @@ -0,0 +1,107 @@ +/*---------------------------------------------------------*\ +| RGBController_HoltekA070.cpp | +| | +| RGBController for Holtek mouse | +| | +| Santeri Pikarinen (santeri3700) 01 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HoltekA070.h" + +/**------------------------------------------------------------------*\ + @name Holtek A070 + @category Mouse + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectHoltekControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HoltekA070::RGBController_HoltekA070(HoltekA070Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Holtek"; + type = DEVICE_TYPE_MOUSE; + description = "Holtek USB Gaming Mouse Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.speed = HOLTEK_A070_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = HOLTEK_A070_MODE_BREATHING_SLOW; + Breathing.speed_max = HOLTEK_A070_MODE_BREATHING_FAST; + Breathing.speed = HOLTEK_A070_MODE_BREATHING_MEDIUM; + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_HoltekA070::~RGBController_HoltekA070() +{ + delete controller; +} + +void RGBController_HoltekA070::SetupZones() +{ + zone mouse_zone; + mouse_zone.name = "Mouse"; + mouse_zone.type = ZONE_TYPE_SINGLE; + mouse_zone.leds_min = 1; + mouse_zone.leds_max = 1; + mouse_zone.leds_count = 1; + mouse_zone.matrix_map = NULL; + zones.push_back(mouse_zone); + + led mouse_led; + mouse_led.name = "Mouse"; + leds.push_back(mouse_led); + + SetupColors(); +} + +void RGBController_HoltekA070::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HoltekA070::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char green = RGBGetGValue(colors[0]); + unsigned char blue = RGBGetBValue(colors[0]); + + controller->SendCustomColor(red, green, blue); +} + +void RGBController_HoltekA070::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HoltekA070::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HoltekA070::DeviceUpdateMode() +{ + controller->SendMode(modes[active_mode].speed); +} diff --git a/Controllers/HoltekController/HoltekA070Controller/RGBController_HoltekA070.h b/Controllers/HoltekController/HoltekA070Controller/RGBController_HoltekA070.h new file mode 100644 index 0000000..03b9327 --- /dev/null +++ b/Controllers/HoltekController/HoltekA070Controller/RGBController_HoltekA070.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_HoltekA070.h | +| | +| RGBController for Holtek mouse | +| | +| Santeri Pikarinen (santeri3700) 01 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "HoltekA070Controller.h" + +class RGBController_HoltekA070 : public RGBController +{ +public: + RGBController_HoltekA070(HoltekA070Controller* controller_ptr); + ~RGBController_HoltekA070(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + HoltekA070Controller* controller; +}; diff --git a/Controllers/HoltekController/HoltekA1FAController/HoltekA1FAController.cpp b/Controllers/HoltekController/HoltekA1FAController/HoltekA1FAController.cpp new file mode 100644 index 0000000..6d5bfa0 --- /dev/null +++ b/Controllers/HoltekController/HoltekA1FAController/HoltekA1FAController.cpp @@ -0,0 +1,75 @@ +/*---------------------------------------------------------*\ +| HoltekA1FAController.cpp | +| | +| Driver for Holtek mousemat | +| | +| Edoardo Ridolfi (edo2313) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HoltekA1FAController.h" +#include "StringUtils.h" + +HoltekA1FAController::HoltekA1FAController(hid_device *dev_handle, const char *path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HoltekA1FAController::~HoltekA1FAController() +{ + hid_close(dev); +} + +std::string HoltekA1FAController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string HoltekA1FAController::GetNameString() +{ + return(name); +} + +std::string HoltekA1FAController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void HoltekA1FAController::SendData(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char preset, unsigned char red, unsigned char green, unsigned char blue) +{ + char usb_buf[9] = {0x00}; + + /*-----------------------------------------------------*\ + | Set up RGB Control packet | + \*-----------------------------------------------------*/ + usb_buf[HOLTEK_A1FA_BYTE_COMMAND] = 0x08; + usb_buf[HOLTEK_A1FA_BYTE_MODE] = mode; + usb_buf[HOLTEK_A1FA_BYTE_BRIGHTNESS] = brightness; + usb_buf[HOLTEK_A1FA_BYTE_SPEED] = speed; + usb_buf[HOLTEK_A1FA_BYTE_PRESET] = preset; + usb_buf[HOLTEK_A1FA_BYTE_RED] = red; + usb_buf[HOLTEK_A1FA_BYTE_GREEN] = green; + usb_buf[HOLTEK_A1FA_BYTE_BLUE] = blue; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, (unsigned char *)usb_buf, sizeof(usb_buf)); +} diff --git a/Controllers/HoltekController/HoltekA1FAController/HoltekA1FAController.h b/Controllers/HoltekController/HoltekA1FAController/HoltekA1FAController.h new file mode 100644 index 0000000..8b194f8 --- /dev/null +++ b/Controllers/HoltekController/HoltekA1FAController/HoltekA1FAController.h @@ -0,0 +1,65 @@ +/*---------------------------------------------------------*\ +| HoltekA1FAController.h | +| | +| Driver for Holtek mousemat | +| | +| Edoardo Ridolfi (edo2313) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + HOLTEK_A1FA_BYTE_COMMAND = 1, + HOLTEK_A1FA_BYTE_MODE = 2, + HOLTEK_A1FA_BYTE_BRIGHTNESS = 3, + HOLTEK_A1FA_BYTE_SPEED = 4, + HOLTEK_A1FA_BYTE_PRESET = 5, + HOLTEK_A1FA_BYTE_RED = 6, + HOLTEK_A1FA_BYTE_GREEN = 7, + HOLTEK_A1FA_BYTE_BLUE = 8 + +}; + +enum +{ + HOLTEK_A1FA_MODE_STATIC = 0x00, + HOLTEK_A1FA_MODE_BREATHING = 0x01, + HOLTEK_A1FA_MODE_NEON = 0x02, + HOLTEK_A1FA_MODE_RAINBOW = 0x03 +}; + +enum +{ + HOLTEK_A1FA_SPEED_SLOWEST = 0x00, /* Slowest speed */ + HOLTEK_A1FA_SPEED_SLOWER = 0x01, /* Slower speed */ + HOLTEK_A1FA_SPEED_SLOW = 0x02, /* Slow speed */ + HOLTEK_A1FA_SPEED_NORMAL = 0x03, /* Normal speed */ + HOLTEK_A1FA_SPEED_FAST = 0x04, /* Fast speed */ + HOLTEK_A1FA_SPEED_FASTEST = 0x05 /* Fastest speed */ +}; + +class HoltekA1FAController +{ +public: + HoltekA1FAController(hid_device *dev_handle, const char *path, std::string dev_name); + ~HoltekA1FAController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendData(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char preset, unsigned char red, unsigned char green, unsigned char blue); + +private: + hid_device *dev; + std::string location; + std::string name; +}; diff --git a/Controllers/HoltekController/HoltekA1FAController/RGBController_HoltekA1FA.cpp b/Controllers/HoltekController/HoltekA1FAController/RGBController_HoltekA1FA.cpp new file mode 100644 index 0000000..a313e24 --- /dev/null +++ b/Controllers/HoltekController/HoltekA1FAController/RGBController_HoltekA1FA.cpp @@ -0,0 +1,150 @@ +/*---------------------------------------------------------*\ +| RGBController_HoltekA1FA.cpp | +| | +| RGBController for Holtek mousemat | +| | +| Edoardo Ridolfi (edo2313) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HoltekA1FA.h" + +/**------------------------------------------------------------------*\ + @name Holtek A1FA + @category Mouse + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectHoltekMousemats + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HoltekA1FA::RGBController_HoltekA1FA(HoltekA1FAController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Holtek"; + type = DEVICE_TYPE_MOUSEMAT; + description = "Holtek Mousemat Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.value = HOLTEK_A1FA_MODE_STATIC; + Static.speed = HOLTEK_A1FA_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + Static.colors_min = 1; + Static.colors_max = 7; + Static.colors.resize(7); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = HOLTEK_A1FA_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = HOLTEK_A1FA_SPEED_SLOWEST; + Breathing.speed_max = HOLTEK_A1FA_SPEED_FASTEST; + Breathing.speed = HOLTEK_A1FA_SPEED_NORMAL; + Breathing.colors_min = 1; + Breathing.colors_max = 7; + Breathing.colors.resize(7); + modes.push_back(Breathing); + + mode Neon; + Neon.name = "Neon"; + Neon.value = HOLTEK_A1FA_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Neon.color_mode = MODE_COLORS_NONE; + Neon.speed_min = HOLTEK_A1FA_SPEED_SLOWEST; + Neon.speed_max = HOLTEK_A1FA_SPEED_FASTEST; + Neon.speed = HOLTEK_A1FA_SPEED_NORMAL; + modes.push_back(Neon); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = HOLTEK_A1FA_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed_min = HOLTEK_A1FA_SPEED_SLOWEST; + Rainbow.speed_max = HOLTEK_A1FA_SPEED_FASTEST; + Rainbow.speed = HOLTEK_A1FA_SPEED_NORMAL; + modes.push_back(Rainbow); + + SetupZones(); +} + +RGBController_HoltekA1FA::~RGBController_HoltekA1FA() +{ + delete controller; +} + +void RGBController_HoltekA1FA::SetupZones() +{ + zone mouse_zone; + mouse_zone.name = "Mousemat"; + mouse_zone.type = ZONE_TYPE_SINGLE; + mouse_zone.leds_min = 1; + mouse_zone.leds_max = 1; + mouse_zone.leds_count = 1; + mouse_zone.matrix_map = NULL; + zones.push_back(mouse_zone); + + led mouse_led; + mouse_led.name = "Mousemat"; + leds.push_back(mouse_led); + + SetupColors(); +} + +void RGBController_HoltekA1FA::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HoltekA1FA::DeviceUpdateLEDs() +{ + unsigned char mode = modes[active_mode].value; + unsigned char brightness = 0x20; /*When brightness support is added, change this */ + unsigned char speed = modes[active_mode].speed; + unsigned char preset = (modes[active_mode].color_mode == MODE_COLORS_RANDOM) ? 0x70 : 0x00; + unsigned char red = RGBGetRValue(colors[0]); + unsigned char green = RGBGetGValue(colors[0]); + unsigned char blue = RGBGetBValue(colors[0]); + + controller->SendData(mode, brightness, speed, preset, red, green, blue); +} + +void RGBController_HoltekA1FA::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HoltekA1FA::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HoltekA1FA::DeviceUpdateMode() +{ + if((active_mode < HOLTEK_A1FA_MODE_NEON) && (previous_mode < HOLTEK_A1FA_MODE_NEON)) + { + //If we're switching from and to static and breathing then sync the mode colors + for(unsigned int i = 0; i < modes[active_mode].colors_max; i++) + { + modes[active_mode].colors[i] = modes[previous_mode].colors[i]; + } + } + + previous_mode = active_mode; + + DeviceUpdateLEDs(); +} diff --git a/Controllers/HoltekController/HoltekA1FAController/RGBController_HoltekA1FA.h b/Controllers/HoltekController/HoltekA1FAController/RGBController_HoltekA1FA.h new file mode 100644 index 0000000..868ba9b --- /dev/null +++ b/Controllers/HoltekController/HoltekA1FAController/RGBController_HoltekA1FA.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_HoltekA1FA.h | +| | +| RGBController for Holtek mousemat | +| | +| Edoardo Ridolfi (edo2313) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "HoltekA1FAController.h" + +class RGBController_HoltekA1FA : public RGBController +{ +public: + RGBController_HoltekA1FA(HoltekA1FAController* controller_ptr); + ~RGBController_HoltekA1FA(); + + int previous_mode = 0; /* previous mode */ + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + HoltekA1FAController* controller; +}; diff --git a/Controllers/HoltekController/HoltekControllerDetect.cpp b/Controllers/HoltekController/HoltekControllerDetect.cpp new file mode 100644 index 0000000..e8471ec --- /dev/null +++ b/Controllers/HoltekController/HoltekControllerDetect.cpp @@ -0,0 +1,57 @@ +/*---------------------------------------------------------*\ +| HoltekControllerDetect.cpp | +| | +| Detector for Holtek devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "HoltekA070Controller.h" +#include "RGBController_HoltekA070.h" +#include "HoltekA1FAController.h" +#include "RGBController_HoltekA1FA.h" + +/*-----------------------------------------------------*\ +| Holtek Semiconductor Inc. vendor ID | +\*-----------------------------------------------------*/ +#define HOLTEK_VID 0x04D9 +/*-----------------------------------------------------*\ +| Mouse product IDs | +\*-----------------------------------------------------*/ +#define HOLTEK_A070_PID 0xA070 +/*-----------------------------------------------------*\ +| Mousemats product IDs | +\*-----------------------------------------------------*/ +#define HOLTEK_A1FA_PID 0xA1FA + +void DetectHoltekControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HoltekA070Controller* controller = new HoltekA070Controller(dev, info->path, name); + RGBController_HoltekA070* rgb_controller = new RGBController_HoltekA070(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectHoltekControllers() */ + +void DetectHoltekMousemats(hid_device_info *info, const std::string &name) +{ + hid_device *dev = hid_open_path(info->path); + + if(dev) + { + HoltekA1FAController* controller = new HoltekA1FAController(dev, info->path, name); + RGBController_HoltekA1FA* rgb_controller = new RGBController_HoltekA1FA(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectHoltekMousemats() */ + +REGISTER_HID_DETECTOR_IPU("Holtek USB Gaming Mouse", DetectHoltekControllers, HOLTEK_VID, HOLTEK_A070_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Holtek Mousemat", DetectHoltekMousemats, HOLTEK_VID, HOLTEK_A1FA_PID, 2, 0xFF00, 0xFF00); diff --git a/Controllers/HyperXDRAMController/HyperXDRAMController.cpp b/Controllers/HyperXDRAMController/HyperXDRAMController.cpp new file mode 100644 index 0000000..214beea --- /dev/null +++ b/Controllers/HyperXDRAMController/HyperXDRAMController.cpp @@ -0,0 +1,285 @@ +/*---------------------------------------------------------*\ +| HyperXDRAMController.cpp | +| | +| Driver for HyperX/Kingston Fury RAM | +| | +| Adam Honse (CalcProgrammer1) 19 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXDRAMController.h" + +HyperXDRAMController::HyperXDRAMController(i2c_smbus_interface* bus, hyperx_dev_id dev, unsigned char slots, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; + slots_valid = slots; + + led_count = 0; + + for(unsigned int slot = 0; slot < 4; slot++) + { + if(((slots_valid & ( 0x01 << slot)) != 0) + ||((slots_valid & ( 0x10 << slot)) != 0)) + { + led_count += 5; + } + } + + mode = HYPERX_MODE_DIRECT; +} + +HyperXDRAMController::~HyperXDRAMController() +{ + +} + +std::string HyperXDRAMController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string HyperXDRAMController::GetDeviceName() +{ + return(name); +} + +unsigned int HyperXDRAMController::GetLEDCount() +{ + return(led_count); +} + +unsigned int HyperXDRAMController::GetSlotCount() +{ + unsigned int slot_count = 0; + + for(int slot = 0; slot < 4; slot++) + { + if(((slots_valid & ( 0x01 << slot)) != 0) + ||((slots_valid & ( 0x10 << slot)) != 0)) + { + slot_count++; + } + } + + return(slot_count); +} + +unsigned int HyperXDRAMController::GetMode() +{ + return(mode); +} + +void HyperXDRAMController::SendApply() +{ + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x02); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x03); +} + +void HyperXDRAMController::SetEffectColor(unsigned char red, unsigned char green, unsigned char blue) +{ + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x01); + + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_EFFECT_RED, red ); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_EFFECT_GREEN, green); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_EFFECT_BLUE, blue ); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_EFFECT_BRIGHTNESS, 0x64 ); + + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x02); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x03); +} + +void HyperXDRAMController::SetAllColors(unsigned char red, unsigned char green, unsigned char blue) +{ + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x01); + + /*-----------------------------------------------------*\ + | Loop through all slots and only set those which are | + | active. | + \*-----------------------------------------------------*/ + for(unsigned int slot_idx = 0; slot_idx < 4; slot_idx++) + { + unsigned char slot = slot_map[slot_idx]; + + if(((slots_valid & ( 0x01 << slot)) != 0) + ||((slots_valid & ( 0x10 << slot)) != 0)) + { + unsigned char base = slot_base[slot]; + unsigned char red_base = base + 0x00; + unsigned char green_base = base + 0x01; + unsigned char blue_base = base + 0x02; + unsigned char bright_base = base + 0x10; + + if(mode == HYPERX_MODE_DIRECT) + { + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_MODE_INDEPENDENT, HYPERX_MODE3_DIRECT); + } + + for(int led = 0; led < 5; led++) + { + bus->i2c_smbus_write_byte_data(dev, red_base + (3 * led), red ); + bus->i2c_smbus_write_byte_data(dev, green_base + (3 * led), green); + bus->i2c_smbus_write_byte_data(dev, blue_base + (3 * led), blue ); + bus->i2c_smbus_write_byte_data(dev, bright_base + (3 * led), 0x64 ); + } + } + } + + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x02); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x03); +} + +void HyperXDRAMController::SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + /*-----------------------------------------------------*\ + | led_slot - the unmapped slot ID for the given LED | + | led - the LED ID within that slot | + | slot_id - counts enabled slots | + | slot - the mapped slot ID for the given LED | + \*-----------------------------------------------------*/ + int led_slot = led / 5; + int slot_id = -1; + unsigned char slot; + + led -= (led_slot * 5); + + /*-----------------------------------------------------*\ + | Loop through all possible slots and only count those | + | which are active. | + \*-----------------------------------------------------*/ + for(unsigned int slot_idx = 0; slot_idx < 4; slot_idx++) + { + slot = slot_map[slot_idx]; + + if(((slots_valid & ( 0x01 << slot)) != 0) + ||((slots_valid & ( 0x10 << slot)) != 0)) + { + slot_id++; + } + + if(slot_id == led_slot) + { + break; + } + } + + SetLEDColor(slot, led, red, green, blue); +} + + +void HyperXDRAMController::SetLEDColor(unsigned int slot, unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char base = slot_base[slot]; + unsigned char red_base = base + 0x00; + unsigned char green_base = base + 0x01; + unsigned char blue_base = base + 0x02; + unsigned char bright_base = base + 0x10; + + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x01); + + bus->i2c_smbus_write_byte_data(dev, red_base + (3 * led), red ); + bus->i2c_smbus_write_byte_data(dev, green_base + (3 * led), green); + bus->i2c_smbus_write_byte_data(dev, blue_base + (3 * led), blue ); + bus->i2c_smbus_write_byte_data(dev, bright_base + (3 * led), 0x64 ); +} + +void HyperXDRAMController::SetMode(unsigned char new_mode, bool random, unsigned short new_speed) +{ + mode = new_mode; + speed = new_speed; + + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x01); + + /*-----------------------------------------------------*\ + | Determine which mode register to use. | + | If set to random color mode, use Mode1. | + | If set to fixed color mode, use Mode2. | + \*-----------------------------------------------------*/ + unsigned char mode_reg; + + if(random) + { + mode_reg = HYPERX_REG_MODE_RANDOM; + } + else + { + mode_reg = HYPERX_REG_MODE_CUSTOM; + } + + switch (mode) + { + case HYPERX_MODE_DIRECT: + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_MODE_INDEPENDENT, HYPERX_MODE3_DIRECT); + break; + + case HYPERX_MODE_STATIC: + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_MODE_CUSTOM, HYPERX_MODE2_STATIC); + break; + + case HYPERX_MODE_RAINBOW: + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_MODE_RANDOM, HYPERX_MODE1_RAINBOW); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_TIMER_MSB, speed >> 8); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_TIMER_LSB, speed & 0xFF); + break; + + case HYPERX_MODE_COMET: + bus->i2c_smbus_write_byte_data(dev, mode_reg, HYPERX_MODE2_COMET); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_TIMER_MSB, speed >> 8); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_TIMER_LSB, speed & 0xFF); + break; + + case HYPERX_MODE_HEARTBEAT: + bus->i2c_smbus_write_byte_data(dev, mode_reg, HYPERX_MODE2_HEARTBEAT); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_OFF_TIME_MSB, speed >> 8); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_OFF_TIME_LSB, speed & 0xFF); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_ON_TIME_MSB, speed >> 8); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_ON_TIME_LSB, speed & 0xFF); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_DELAY_TIME_MSB, 0x03); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_DELAY_TIME_LSB, 0xE8); + break; + + case HYPERX_MODE_CYCLE: + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_MODE_RANDOM, HYPERX_MODE1_CYCLE); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_ON_TIME_MSB, speed >> 8); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_ON_TIME_LSB, speed & 0xFF); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_CHANGE_TIME_MSB, speed >> 8); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_CHANGE_TIME_LSB, speed & 0xFF); + break; + + case HYPERX_MODE_BREATHING: + bus->i2c_smbus_write_byte_data(dev, mode_reg, HYPERX_MODE2_BREATHING); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_FADE_IN_TIME_MSB, speed >> 8); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_FADE_IN_TIME_LSB, speed & 0xFF); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_FADE_OUT_TIME_MSB, speed >> 8); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_FADE_OUT_TIME_LSB, speed & 0xFF); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_OFF_TIME_MSB, 0x00); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_OFF_TIME_LSB, 0x00); + break; + + case HYPERX_MODE_BOUNCE: + bus->i2c_smbus_write_byte_data(dev, mode_reg, HYPERX_MODE2_BOUNCE); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_TIMER_MSB, speed >> 8); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_TIMER_LSB, speed & 0xFF); + break; + + case HYPERX_MODE_BLINK: + bus->i2c_smbus_write_byte_data(dev, mode_reg, HYPERX_MODE2_BLINK); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_OFF_TIME_MSB, speed >> 8); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_OFF_TIME_LSB, speed & 0xFF); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_ON_TIME_MSB, 0x07); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_ON_TIME_LSB, 0xD4); + break; + } + + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x02); + bus->i2c_smbus_write_byte_data(dev, HYPERX_REG_APPLY, 0x03); +} diff --git a/Controllers/HyperXDRAMController/HyperXDRAMController.h b/Controllers/HyperXDRAMController/HyperXDRAMController.h new file mode 100644 index 0000000..0410b3c --- /dev/null +++ b/Controllers/HyperXDRAMController/HyperXDRAMController.h @@ -0,0 +1,236 @@ +/*---------------------------------------------------------*\ +| HyperXDRAMController.h | +| | +| Driver for HyperX/Kingston Fury RAM | +| | +| Adam Honse (CalcProgrammer1) 19 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char hyperx_dev_id; +typedef unsigned short hyperx_register; + +enum +{ + HYPERX_REG_SLOT0_LED0_RED = 0x11, /* R color register for LED 0, Slot 0 */ + HYPERX_REG_SLOT0_LED0_GREEN = 0x12, /* G color register for LED 0, Slot 0 */ + HYPERX_REG_SLOT0_LED0_BLUE = 0x13, /* B color register for LED 0, Slot 0 */ + HYPERX_REG_SLOT0_LED1_RED = 0x14, /* R color register for LED 1, Slot 0 */ + HYPERX_REG_SLOT0_LED1_GREEN = 0x15, /* G color register for LED 1, Slot 0 */ + HYPERX_REG_SLOT0_LED1_BLUE = 0x16, /* B color register for LED 1, Slot 0 */ + HYPERX_REG_SLOT0_LED2_RED = 0x17, /* R color register for LED 2, Slot 0 */ + HYPERX_REG_SLOT0_LED2_GREEN = 0x18, /* G color register for LED 2, Slot 0 */ + HYPERX_REG_SLOT0_LED2_BLUE = 0x19, /* B color register for LED 2, Slot 0 */ + HYPERX_REG_SLOT0_LED3_RED = 0x1A, /* R color register for LED 3, Slot 0 */ + HYPERX_REG_SLOT0_LED3_GREEN = 0x1B, /* G color register for LED 3, Slot 0 */ + HYPERX_REG_SLOT0_LED3_BLUE = 0x1C, /* B color register for LED 3, Slot 0 */ + HYPERX_REG_SLOT0_LED4_RED = 0x1D, /* R color register for LED 4, Slot 0 */ + HYPERX_REG_SLOT0_LED4_GREEN = 0x1E, /* G color register for LED 4, Slot 0 */ + HYPERX_REG_SLOT0_LED4_BLUE = 0x1F, /* B color register for LED 4, Slot 0 */ + HYPERX_REG_SLOT0_LED0_BRIGHTNESS = 0x21, /* Brightness for LED 0, Slot 0 (0-100) */ + HYPERX_REG_SLOT0_LED1_BRIGHTNESS = 0x24, /* Brightness for LED 1, Slot 0 (0-100) */ + HYPERX_REG_SLOT0_LED2_BRIGHTNESS = 0x27, /* Brightness for LED 2, Slot 0 (0-100) */ + HYPERX_REG_SLOT0_LED3_BRIGHTNESS = 0x2A, /* Brightness for LED 3, Slot 0 (0-100) */ + HYPERX_REG_SLOT0_LED4_BRIGHTNESS = 0x2D, /* Brightness for LED 4, Slot 0 (0-100) */ + + HYPERX_REG_SLOT1_LED0_RED = 0x41, /* R color register for LED 0, Slot 1 */ + HYPERX_REG_SLOT1_LED0_GREEN = 0x42, /* G color register for LED 0, Slot 1 */ + HYPERX_REG_SLOT1_LED0_BLUE = 0x43, /* B color register for LED 0, Slot 1 */ + HYPERX_REG_SLOT1_LED1_RED = 0x44, /* R color register for LED 1, Slot 1 */ + HYPERX_REG_SLOT1_LED1_GREEN = 0x45, /* G color register for LED 1, Slot 1 */ + HYPERX_REG_SLOT1_LED1_BLUE = 0x46, /* B color register for LED 1, Slot 1 */ + HYPERX_REG_SLOT1_LED2_RED = 0x47, /* R color register for LED 2, Slot 1 */ + HYPERX_REG_SLOT1_LED2_GREEN = 0x48, /* G color register for LED 2, Slot 1 */ + HYPERX_REG_SLOT1_LED2_BLUE = 0x49, /* B color register for LED 2, Slot 1 */ + HYPERX_REG_SLOT1_LED3_RED = 0x4A, /* R color register for LED 3, Slot 1 */ + HYPERX_REG_SLOT1_LED3_GREEN = 0x4B, /* G color register for LED 3, Slot 1 */ + HYPERX_REG_SLOT1_LED3_BLUE = 0x4C, /* B color register for LED 3, Slot 1 */ + HYPERX_REG_SLOT1_LED4_RED = 0x4D, /* R color register for LED 4, Slot 1 */ + HYPERX_REG_SLOT1_LED4_GREEN = 0x4E, /* G color register for LED 4, Slot 1 */ + HYPERX_REG_SLOT1_LED4_BLUE = 0x4F, /* B color register for LED 4, Slot 1 */ + HYPERX_REG_SLOT1_LED0_BRIGHTNESS = 0x51, /* Brightness for LED 0, Slot 1 (0-100) */ + HYPERX_REG_SLOT1_LED1_BRIGHTNESS = 0x54, /* Brightness for LED 1, Slot 1 (0-100) */ + HYPERX_REG_SLOT1_LED2_BRIGHTNESS = 0x57, /* Brightness for LED 2, Slot 1 (0-100) */ + HYPERX_REG_SLOT1_LED3_BRIGHTNESS = 0x5A, /* Brightness for LED 3, Slot 1 (0-100) */ + HYPERX_REG_SLOT1_LED4_BRIGHTNESS = 0x5D, /* Brightness for LED 4, Slot 1 (0-100) */ + + HYPERX_REG_SLOT2_LED0_RED = 0x71, /* R color register for LED 0, Slot 2 */ + HYPERX_REG_SLOT2_LED0_GREEN = 0x72, /* G color register for LED 0, Slot 2 */ + HYPERX_REG_SLOT2_LED0_BLUE = 0x73, /* B color register for LED 0, Slot 2 */ + HYPERX_REG_SLOT2_LED1_RED = 0x74, /* R color register for LED 1, Slot 2 */ + HYPERX_REG_SLOT2_LED1_GREEN = 0x75, /* G color register for LED 1, Slot 2 */ + HYPERX_REG_SLOT2_LED1_BLUE = 0x76, /* B color register for LED 1, Slot 2 */ + HYPERX_REG_SLOT2_LED2_RED = 0x77, /* R color register for LED 2, Slot 2 */ + HYPERX_REG_SLOT2_LED2_GREEN = 0x78, /* G color register for LED 2, Slot 2 */ + HYPERX_REG_SLOT2_LED2_BLUE = 0x79, /* B color register for LED 2, Slot 2 */ + HYPERX_REG_SLOT2_LED3_RED = 0x7A, /* R color register for LED 3, Slot 2 */ + HYPERX_REG_SLOT2_LED3_GREEN = 0x7B, /* G color register for LED 3, Slot 2 */ + HYPERX_REG_SLOT2_LED3_BLUE = 0x7C, /* B color register for LED 3, Slot 2 */ + HYPERX_REG_SLOT2_LED4_RED = 0x7D, /* R color register for LED 4, Slot 2 */ + HYPERX_REG_SLOT2_LED4_GREEN = 0x7E, /* G color register for LED 4, Slot 2 */ + HYPERX_REG_SLOT2_LED4_BLUE = 0x7F, /* B color register for LED 4, Slot 2 */ + HYPERX_REG_SLOT2_LED0_BRIGHTNESS = 0x81, /* Brightness for LED 0, Slot 2 (0-100) */ + HYPERX_REG_SLOT2_LED1_BRIGHTNESS = 0x84, /* Brightness for LED 1, Slot 2 (0-100) */ + HYPERX_REG_SLOT2_LED2_BRIGHTNESS = 0x87, /* Brightness for LED 2, Slot 2 (0-100) */ + HYPERX_REG_SLOT2_LED3_BRIGHTNESS = 0x8A, /* Brightness for LED 3, Slot 2 (0-100) */ + HYPERX_REG_SLOT2_LED4_BRIGHTNESS = 0x8D, /* Brightness for LED 4, Slot 2 (0-100) */ + + HYPERX_REG_SLOT3_LED0_RED = 0xA1, /* R color register for LED 0, Slot 3 */ + HYPERX_REG_SLOT3_LED0_GREEN = 0xA2, /* G color register for LED 0, Slot 3 */ + HYPERX_REG_SLOT3_LED0_BLUE = 0xA3, /* B color register for LED 0, Slot 3 */ + HYPERX_REG_SLOT3_LED1_RED = 0xA4, /* R color register for LED 1, Slot 3 */ + HYPERX_REG_SLOT3_LED1_GREEN = 0xA5, /* G color register for LED 1, Slot 3 */ + HYPERX_REG_SLOT3_LED1_BLUE = 0xA6, /* B color register for LED 1, Slot 3 */ + HYPERX_REG_SLOT3_LED2_RED = 0xA7, /* R color register for LED 2, Slot 3 */ + HYPERX_REG_SLOT3_LED2_GREEN = 0xA8, /* G color register for LED 2, Slot 3 */ + HYPERX_REG_SLOT3_LED2_BLUE = 0xA9, /* B color register for LED 2, Slot 3 */ + HYPERX_REG_SLOT3_LED3_RED = 0xAA, /* R color register for LED 3, Slot 3 */ + HYPERX_REG_SLOT3_LED3_GREEN = 0xAB, /* G color register for LED 3, Slot 3 */ + HYPERX_REG_SLOT3_LED3_BLUE = 0xAC, /* B color register for LED 3, Slot 3 */ + HYPERX_REG_SLOT3_LED4_RED = 0xAD, /* R color register for LED 4, Slot 3 */ + HYPERX_REG_SLOT3_LED4_GREEN = 0xAE, /* G color register for LED 4, Slot 3 */ + HYPERX_REG_SLOT3_LED4_BLUE = 0xAF, /* B color register for LED 4, Slot 3 */ + HYPERX_REG_SLOT3_LED0_BRIGHTNESS = 0xB1, /* Brightness for LED 0, Slot 3 (0-100) */ + HYPERX_REG_SLOT3_LED1_BRIGHTNESS = 0xB4, /* Brightness for LED 1, Slot 3 (0-100) */ + HYPERX_REG_SLOT3_LED2_BRIGHTNESS = 0xB7, /* Brightness for LED 2, Slot 3 (0-100) */ + HYPERX_REG_SLOT3_LED3_BRIGHTNESS = 0xBA, /* Brightness for LED 3, Slot 3 (0-100) */ + HYPERX_REG_SLOT3_LED4_BRIGHTNESS = 0xBD, /* Brightness for LED 4, Slot 3 (0-100) */ + + HYPERX_REG_TIMER_MSB = 0xD1, /* Timer MSB */ + HYPERX_REG_TIMER_LSB = 0xD2, /* Timer LSB */ + HYPERX_REG_ON_TIME_MSB = 0xD3, /* Effect on time MSB */ + HYPERX_REG_ON_TIME_LSB = 0xD4, /* Effect on time LSB */ + HYPERX_REG_CHANGE_TIME_MSB = 0xD5, /* Change time MSB */ + HYPERX_REG_CHANGE_TIME_LSB = 0xD6, /* Change time LSB */ + HYPERX_REG_FADE_IN_TIME_MSB = 0xD7, /* Fade in time MSB */ + HYPERX_REG_FADE_IN_TIME_LSB = 0xD8, /* Fade in time LSB */ + HYPERX_REG_FADE_OUT_TIME_MSB = 0xD9, /* Fade out time MSB */ + HYPERX_REG_FADE_OUT_TIME_LSB = 0xDA, /* Fade out time LSB */ + HYPERX_REG_OFF_TIME_MSB = 0xDB, /* Effect off time MSB */ + HYPERX_REG_OFF_TIME_LSB = 0xDC, /* Effect off time LSB */ + HYPERX_REG_EFFECT_BRIGHTNESS = 0xDD, /* Brightness for effects (0-100) */ + HYPERX_REG_APPLY = 0xE1, /* Apply changes register */ + HYPERX_REG_MODE_RANDOM = 0xE3, /* Mode control register, random colors */ + HYPERX_REG_MODE_CUSTOM = 0xE4, /* Mode control register, custom colors */ + HYPERX_REG_MODE_INDEPENDENT = 0xE5, /* Mode control register, independent */ + HYPERX_REG_DELAY_TIME_MSB = 0xEA, /* Delay time MSB */ + HYPERX_REG_DELAY_TIME_LSB = 0xEB, /* Delay time LSB */ + HYPERX_REG_EFFECT_RED = 0xEC, /* Red color register for effects */ + HYPERX_REG_EFFECT_GREEN = 0xED, /* Green color register for effects */ + HYPERX_REG_EFFECT_BLUE = 0xEE, /* Blue color register for effects */ +}; + +enum +{ + HYPERX_MODE1_RAINBOW = 0x05, /* Mode 1 rainbow effect */ + HYPERX_MODE1_CYCLE = 0x04, /* Mode 1 cycle effect */ +}; + +enum +{ + HYPERX_MODE2_BOUNCE = 0x02, /* Mode 2 bounce effect */ + HYPERX_MODE2_BREATHING = 0x03, /* Mode 2 breathing effect */ + HYPERX_MODE2_BLINK = 0x06, /* Mode 2 blink effect */ + HYPERX_MODE2_HEARTBEAT = 0x07, /* Mode 2 heartbeat effect */ + HYPERX_MODE2_COMET = 0x08, /* Mode 2 comet effect */ + HYPERX_MODE2_STATIC = 0x09, /* Mode 2 static effect */ +}; + +enum +{ + HYPERX_MODE3_DIRECT = 0x21, /* Mode 3 direct control */ +}; + +enum +{ + HYPERX_MODE_DIRECT = 0, /* Direct control mode */ + HYPERX_MODE_STATIC = 1, /* Static color mode */ + HYPERX_MODE_RAINBOW = 2, /* Rainbow wave mode */ + HYPERX_MODE_COMET = 3, /* Comet (chase) mode */ + HYPERX_MODE_HEARTBEAT = 4, /* Heartbeat (pulsing) mode */ + HYPERX_MODE_CYCLE = 5, /* Spectrum cycle mode */ + HYPERX_MODE_BREATHING = 6, /* Breathing mode */ + HYPERX_MODE_BOUNCE = 7, /* Bounce mode */ + HYPERX_MODE_BLINK = 8, /* Blinking mode */ + HYPERX_NUMBER_MODES /* Number of HyperX modes */ +}; + +enum +{ + HYPERX_SPEED_BOUNCE_SLOW = 0x07D0, /* Slowest speed for bounce mode */ + HYPERX_SPEED_BOUNCE_NORMAL = 0x07D0, /* Normal speed for bounce mode */ + HYPERX_SPEED_BOUNCE_FAST = 0x0064, /* Fastest speed for bounce mode */ + HYPERX_SPEED_BREATHING_SLOW = 0x07D0, /* Slowest speed for breathing mode */ + HYPERX_SPEED_BREATHING_NORMAL = 0x07D0, /* Normal speed for breathing mode */ + HYPERX_SPEED_BREATHING_FAST = 0x0064, /* Fastest speed for breathing mode */ + HYPERX_SPEED_CYCLE_SLOW = 0x05DC, /* Slowest speed for cycle mode */ + HYPERX_SPEED_CYCLE_NORMAL = 0x05DC, /* Normal speed for cycle mode */ + HYPERX_SPEED_CYCLE_FAST = 0x00FA, /* Fastest speed for cycle mode */ + HYPERX_SPEED_RAINBOW_SLOW = 0x07D0, /* Slowest speed for rainbow mode */ + HYPERX_SPEED_RAINBOW_NORMAL = 0x07D0, /* Normal speed for rainbow mode */ + HYPERX_SPEED_RAINBOW_FAST = 0x0064, /* Fastest speed for rainbow mode */ + HYPERX_SPEED_BLINK_SLOW = 0x07D0, /* Slowest speed for blink mode */ + HYPERX_SPEED_BLINK_NORMAL = 0x07D0, /* Normal speed for blink mode */ + HYPERX_SPEED_BLINK_FAST = 0x01F4, /* Fastest speed for blink mode */ + HYPERX_SPEED_HEARTBEAT_SLOW = 0x07D0, /* Slowest speed for heartbeat mode */ + HYPERX_SPEED_HEARTBEAT_NORMAL = 0x07D0, /* Normal speed for heartbeat mode */ + HYPERX_SPEED_HEARTBEAT_FAST = 0x01F4, /* Fastest speed for heartbeat mode */ + HYPERX_SPEED_COMET_SLOW = 0x07D0, /* Slowest speed for comet mode */ + HYPERX_SPEED_COMET_NORMAL = 0x07D0, /* Normal speed for comet mode */ + HYPERX_SPEED_COMET_FAST = 0x0064, /* Fastest speed for comet mode */ +}; + +static const unsigned char slot_base[4] = +{ + HYPERX_REG_SLOT0_LED0_RED, /* SPD 0x50 maps to slot 0 */ + HYPERX_REG_SLOT1_LED0_RED, /* SPD 0x52 maps to slot 1 */ + HYPERX_REG_SLOT2_LED0_RED, /* SPD 0x51 maps to slot 2 */ + HYPERX_REG_SLOT3_LED0_RED /* SPD 0x53 maps to slot 3 */ +}; + +static const unsigned char slot_map[4] = +{ + 0, + 2, + 1, + 3 +}; + +class HyperXDRAMController +{ +public: + HyperXDRAMController(i2c_smbus_interface* bus, hyperx_dev_id dev, unsigned char slots, std::string dev_name); + ~HyperXDRAMController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + unsigned int GetLEDCount(); + unsigned int GetSlotCount(); + unsigned int GetMode(); + + void SendApply(); + + void SetMode(unsigned char new_mode, bool random, unsigned short new_speed); + + void SetAllColors(unsigned char red, unsigned char green, unsigned char blue); + void SetEffectColor(unsigned char red, unsigned char green, unsigned char blue); + void SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void SetLEDColor(unsigned int slot, unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + +private: + unsigned int led_count; + unsigned char slots_valid; + i2c_smbus_interface* bus; + hyperx_dev_id dev; + unsigned int mode; + unsigned short speed; + std::string name; +}; diff --git a/Controllers/HyperXDRAMController/HyperXDRAMControllerDetect.cpp b/Controllers/HyperXDRAMController/HyperXDRAMControllerDetect.cpp new file mode 100644 index 0000000..51d43bc --- /dev/null +++ b/Controllers/HyperXDRAMController/HyperXDRAMControllerDetect.cpp @@ -0,0 +1,104 @@ +/*---------------------------------------------------------*\ +| HyperXDRAMControllerDetect.cpp | +| | +| Driver for HyperX/Kingston Fury RAM | +| | +| Adam Honse (CalcProgrammer1) 19 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "HyperXDRAMController.h" +#include "LogManager.h" +#include "RGBController_HyperXDRAM.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +using namespace std::chrono_literals; + +/******************************************************************************************\ +* * +* TestForHyperXDRAMController * +* * +* Tests the given address to see if a HyperX controller exists there. * +* * +\******************************************************************************************/ +#define HYPERX_CONTROLLER_NAME "HyperX DRAM" + +bool TestForHyperXDRAMController(i2c_smbus_interface* bus, unsigned char address) +{ + int res = bus->i2c_smbus_read_byte(address); + + return(res >= 0); + +} /* TestForHyperXDRAMController() */ + + +/******************************************************************************************\ +* * +* DetectHyperXDRAMControllers * +* * +* Detect HyperX DRAM controllers on the enumerated I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where Aura device is connected * +* slots - accessors to SPD information of the occupied slots * +* * +\******************************************************************************************/ + +void DetectHyperXDRAMControllers(i2c_smbus_interface* bus, std::vector &slots, const std::string &/*name*/) +{ + unsigned char slots_valid = 0x00; + bool fury_detected = false; + bool pred_detected = false; + + // Check for HyperX controller at 0x27 + LOG_DEBUG("[%s] Testing bus %d at address 0x27", HYPERX_CONTROLLER_NAME, bus->port_id); + + if(TestForHyperXDRAMController(bus, 0x27)) + { + for(SPDWrapper *slot : slots) + { + LOG_DEBUG("[%s] SPD check success", HYPERX_CONTROLLER_NAME); + + slots_valid |= (1 << (slot->index())); + + if(slot->manufacturer_data(0x06) == 0x01) + { + fury_detected = true; + } + else + { + pred_detected = true; + } + + std::this_thread::sleep_for(1ms); + } + + LOG_DEBUG("[%s] slots_valid=%d fury_detected=%d pred_detected=%d", + HYPERX_CONTROLLER_NAME, slots_valid, fury_detected, pred_detected); + + if(slots_valid != 0) + { + std::string name = "HyperX DRAM"; + + if(fury_detected && !pred_detected) + { + name = "HyperX Fury RGB"; + } + else if(!fury_detected && pred_detected) + { + name = "HyperX Predator RGB"; + } + + HyperXDRAMController* controller = new HyperXDRAMController(bus, 0x27, slots_valid, name); + RGBController_HyperXDRAM* rgb_controller = new RGBController_HyperXDRAM(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} /* DetectHyperXDRAMControllers() */ + +REGISTER_I2C_DIMM_DETECTOR("HyperX DRAM", DetectHyperXDRAMControllers, JEDEC_KINGSTON, SPD_DDR4_SDRAM); diff --git a/Controllers/HyperXDRAMController/RGBController_HyperXDRAM.cpp b/Controllers/HyperXDRAMController/RGBController_HyperXDRAM.cpp new file mode 100644 index 0000000..29d4159 --- /dev/null +++ b/Controllers/HyperXDRAMController/RGBController_HyperXDRAM.cpp @@ -0,0 +1,273 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXDRAM.cpp | +| | +| RGBController for HyperX/Kingston Fury RAM | +| | +| Adam Honse (CalcProgrammer1) 29 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HyperXDRAM.h" + +/**------------------------------------------------------------------*\ + @name HyperX DRAM + @category RAM + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectHyperXDRAMControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXDRAM::RGBController_HyperXDRAM(HyperXDRAMController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "HyperX"; + type = DEVICE_TYPE_DRAM; + description = "HyperX DRAM Device"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = HYPERX_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = HYPERX_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = HYPERX_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED; + Rainbow.speed_min = HYPERX_SPEED_RAINBOW_SLOW; + Rainbow.speed_max = HYPERX_SPEED_RAINBOW_FAST; + Rainbow.speed = HYPERX_SPEED_RAINBOW_NORMAL; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Comet; + Comet.name = "Comet"; + Comet.value = HYPERX_MODE_COMET; + Comet.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Comet.speed_min = HYPERX_SPEED_COMET_SLOW; + Comet.speed_max = HYPERX_SPEED_COMET_FAST; + Comet.colors_min = 1; + Comet.colors_max = 1; + Comet.speed = HYPERX_SPEED_COMET_NORMAL; + Comet.color_mode = MODE_COLORS_MODE_SPECIFIC; + Comet.colors.resize(1); + modes.push_back(Comet); + + mode Heartbeat; + Heartbeat.name = "Heartbeat"; + Heartbeat.value = HYPERX_MODE_HEARTBEAT; + Heartbeat.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Heartbeat.speed_min = HYPERX_SPEED_COMET_SLOW; + Heartbeat.speed_max = HYPERX_SPEED_COMET_FAST; + Heartbeat.colors_min = 1; + Heartbeat.colors_max = 1; + Heartbeat.speed = HYPERX_SPEED_COMET_NORMAL; + Heartbeat.color_mode = MODE_COLORS_MODE_SPECIFIC; + Heartbeat.colors.resize(1); + modes.push_back(Heartbeat); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = HYPERX_MODE_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED; + SpectrumCycle.speed_min = HYPERX_SPEED_CYCLE_SLOW; + SpectrumCycle.speed_max = HYPERX_SPEED_CYCLE_FAST; + SpectrumCycle.speed = HYPERX_SPEED_CYCLE_NORMAL; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = HYPERX_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Breathing.speed_min = HYPERX_SPEED_BREATHING_SLOW; + Breathing.speed_max = HYPERX_SPEED_BREATHING_FAST; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.speed = HYPERX_SPEED_BREATHING_NORMAL; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Bounce; + Bounce.name = "Bounce"; + Bounce.value = HYPERX_MODE_BOUNCE; + Bounce.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Bounce.speed_min = HYPERX_SPEED_BOUNCE_SLOW; + Bounce.speed_max = HYPERX_SPEED_BOUNCE_FAST; + Bounce.colors_min = 1; + Bounce.colors_max = 1; + Bounce.speed = HYPERX_SPEED_BOUNCE_NORMAL; + Bounce.color_mode = MODE_COLORS_MODE_SPECIFIC; + Bounce.colors.resize(1); + modes.push_back(Bounce); + + mode Blink; + Blink.name = "Blink"; + Blink.value = HYPERX_MODE_BLINK; + Blink.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Blink.speed_min = HYPERX_SPEED_BLINK_SLOW; + Blink.speed_max = HYPERX_SPEED_BLINK_FAST; + Blink.colors_min = 1; + Blink.colors_max = 1; + Blink.speed = HYPERX_SPEED_BLINK_NORMAL; + Blink.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blink.colors.resize(1); + modes.push_back(Blink); + + SetupZones(); +} + +RGBController_HyperXDRAM::~RGBController_HyperXDRAM() +{ + delete controller; +} + +void RGBController_HyperXDRAM::SetupZones() +{ + for(unsigned int slot = 0; slot < controller->GetSlotCount(); slot++) + { + zone* new_zone = new zone; + + new_zone->name = "HyperX Slot "; + new_zone->name.append(std::to_string(slot + 1)); + new_zone->type = ZONE_TYPE_LINEAR; + new_zone->leds_min = 5; + new_zone->leds_max = 5; + new_zone->leds_count = 5; + new_zone->matrix_map = NULL; + + zones.push_back(*new_zone); + } + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led* new_led = new led(); + + new_led->name = "HyperX Slot "; + new_led->name.append(std::to_string(zone_idx + 1)); + new_led->name.append(", LED "); + new_led->name.append(std::to_string(led_idx + 1)); + + new_led->value = (unsigned int)leds.size(); + + leds.push_back(*new_led); + } + } + + SetupColors(); +} + +void RGBController_HyperXDRAM::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXDRAM::DeviceUpdateLEDs() +{ + if(controller->GetMode() == HYPERX_MODE_DIRECT) + { + for(unsigned int led_idx = 0; led_idx < (unsigned int)colors.size(); led_idx++ ) + { + RGBColor color = colors[led_idx]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetLEDColor(led_idx, red, grn, blu); + } + controller->SendApply(); + } + else + { + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetEffectColor(red, grn, blu); + } +} + +void RGBController_HyperXDRAM::UpdateZoneLEDs(int zone) +{ + if(controller->GetMode() == HYPERX_MODE_DIRECT) + { + for(std::size_t led_idx = 0; led_idx < zones[zone].leds_count; led_idx++ ) + { + unsigned int led = zones[zone].leds[led_idx].value; + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetLEDColor(led, red, grn, blu); + } + controller->SendApply(); + } + else + { + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetEffectColor(red, grn, blu); + } +} + +void RGBController_HyperXDRAM::UpdateSingleLED(int led) +{ + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + if(controller->GetMode() == HYPERX_MODE_DIRECT) + { + controller->SetLEDColor(led, red, grn, blu); + } + else + { + controller->SetEffectColor(red, grn, blu); + } + controller->SendApply(); +} + +void RGBController_HyperXDRAM::DeviceUpdateMode() +{ + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + + controller->SetMode(modes[active_mode].value, random, modes[active_mode].speed); + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu = RGBGetBValue(modes[active_mode].colors[0]); + + controller->SetEffectColor(red, grn, blu); + } +} + diff --git a/Controllers/HyperXDRAMController/RGBController_HyperXDRAM.h b/Controllers/HyperXDRAMController/RGBController_HyperXDRAM.h new file mode 100644 index 0000000..41dc58a --- /dev/null +++ b/Controllers/HyperXDRAMController/RGBController_HyperXDRAM.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXDRAM.h | +| | +| RGBController for HyperX/Kingston Fury RAM | +| | +| Adam Honse (CalcProgrammer1) 29 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "HyperXDRAMController.h" + +class RGBController_HyperXDRAM : public RGBController +{ +public: + RGBController_HyperXDRAM(HyperXDRAMController* controller_ptr); + ~RGBController_HyperXDRAM(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + HyperXDRAMController* controller; +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.cpp new file mode 100644 index 0000000..0ef726a --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.cpp @@ -0,0 +1,183 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyElite2Controller.cpp | +| | +| Driver for HyperX Alloy Elite 2 keyboard | +| | +| KundaPanda (vojdo) 02 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXAlloyElite2Controller.h" +#include "StringUtils.h" + +/*-----------------------------------------*\ +| Skip these indices in the color output | +\*-----------------------------------------*/ +static const unsigned int SKIP_INDICES[] = { 23, 29, 41, 47, 70, 71, 76, 77, 87, 88, 93, 99, 100, 102, 108, 113 }; + +HyperXAlloyElite2Controller::HyperXAlloyElite2Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXAlloyElite2Controller::~HyperXAlloyElite2Controller() +{ + hid_close(dev); +} + +std::string HyperXAlloyElite2Controller::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string HyperXAlloyElite2Controller::GetNameString() +{ + return(name); +} + +std::string HyperXAlloyElite2Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void HyperXAlloyElite2Controller::SetLEDsDirect(const std::vector& colors) +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Variables to keep track of color sending and skipping | + \*-----------------------------------------------------*/ + size_t buf_idx = 1; + size_t color_idx = 0; + size_t packets_sent = 0; + size_t skipped = 0; + const unsigned int* skip_idx = &SKIP_INDICES[0]; + + /*-----------------------------------------------------*\ + | Initialize direct control | + \*-----------------------------------------------------*/ + SendDirectInitialization(); + + /*-----------------------------------------------------*\ + | Continue filling and sending packets while color data | + | remains | + \*-----------------------------------------------------*/ + while(color_idx < colors.size()) + { + /*-------------------------------------------------*\ + | Packets have colors in groups of 4 bytes, with | + | the first byte being 0x81 and then R, G, B. | + \*-------------------------------------------------*/ + buf[buf_idx] = 0x81; + + /*-------------------------------------------------*\ + | If at a skipped index, add null data to packet | + | and increment skipped count and index | + | Otherwise, copy color data to buffer and increment| + | color index | + \*-------------------------------------------------*/ + if(*skip_idx == color_idx + skipped) + { + buf[buf_idx + 1] = 0; + buf[buf_idx + 2] = 0; + buf[buf_idx + 3] = 0; + + skip_idx++; + + if(skip_idx >= SKIP_INDICES + sizeof(SKIP_INDICES) / sizeof(unsigned int)) + { + skip_idx = SKIP_INDICES; + } + + skipped++; + } + else + { + buf[buf_idx + 1] = RGBGetRValue(colors[color_idx]); + buf[buf_idx + 2] = RGBGetGValue(colors[color_idx]); + buf[buf_idx + 3] = RGBGetBValue(colors[color_idx]); + + color_idx++; + } + + /*-------------------------------------------------*\ + | Increment packet buffer index by 4 bytes | + \*-------------------------------------------------*/ + buf_idx += 4; + + /*-------------------------------------------------*\ + | If the packet buffer is full, send it and reset | + | buffer indexing | + | OR | + | If all colors have been filled into the buffer, | + | send the packet | + \*-------------------------------------------------*/ + if((buf_idx >= sizeof(buf)) || (color_idx == colors.size())) + { + /*---------------------------------------------*\ + | Send packet | + \*---------------------------------------------*/ + hid_send_feature_report(dev, buf, sizeof(buf)); + + /*---------------------------------------------*\ + | Zero out buffer and reset index | + \*---------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + buf_idx = 1; + + /*---------------------------------------------*\ + | Increment packet counter | + \*---------------------------------------------*/ + packets_sent++; + } + } + + /*-----------------------------------------------------*\ + | Send empty packets until 9 total packets have been | + | sent | + \*-----------------------------------------------------*/ + for(size_t remaining_packets = 0; packets_sent + remaining_packets < 9; remaining_packets++) + { + hid_send_feature_report(dev, buf, sizeof(buf)); + } +} + +void HyperXAlloyElite2Controller::SendDirectInitialization() +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Select Profile packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + buf[0x01] = 0x04; + buf[0x02] = 0xF2; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 65); +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.h b/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.h new file mode 100644 index 0000000..2550617 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyElite2Controller.h | +| | +| Driver for HyperX Alloy Elite 2 keyboard | +| | +| KundaPanda (vojdo) 02 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class HyperXAlloyElite2Controller +{ +public: + HyperXAlloyElite2Controller(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXAlloyElite2Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetLEDsDirect(const std::vector& colors); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendDirectInitialization(); +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/RGBController_HyperXAlloyElite2.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/RGBController_HyperXAlloyElite2.cpp new file mode 100644 index 0000000..b308434 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/RGBController_HyperXAlloyElite2.cpp @@ -0,0 +1,346 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyElite2.cpp | +| | +| RGBController for HyperX Alloy Elite 2 keyboard | +| | +| KundaPanda (vojdo) 02 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_HyperXAlloyElite2.h" + +using namespace std::chrono_literals; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[8][22] = +{ + { NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 104, 107, 108, 98, NA, NA }, + { 110, 111, 112, 113, NA, 114, 115, 116, NA, 117, 118, 119, 120, NA, 121, 122, 123, NA, 124, 125, 126, 127 }, + { 0, 12, 18, 23, 28, NA, 34, 39, 44, 50, 56, 62, 66, 70, NA, 76, 80, 85, NA, NA, NA, NA }, + { 1, 7, 13, 19, 24, 29, 35, 40, 45, 51, 57, 63, 67, 71, NA, 77, 81, 86, 89, 94, 99, 105 }, + { 2, NA, 8, 14, 20, 25, 30, 36, 41, 46, 52, 58, 64, 68, 72, 78, 82, 87, 90, 95, 100, 106 }, + { 3, NA, 9, 15, 21, 26, 31, 37, 42, 47, 53, 59, 65, 69, 73, NA, NA, NA, 91, 96, 101, NA }, + { 4, 6, NA, 10, 16, 22, 27, 32, 38, 43, 48, 54, 60, 74, NA, NA, 83, NA, 92, 97, 102, 109 }, + { 5, 11, 17, NA, NA, NA, NA, 33, NA, NA, NA, 49, 61, 55, 75, 79, 84, 88, 93, NA, 103, NA } +}; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 128, +}; + + +// ISO 6, 75, !80 +// ANSI !6, !75, 80 + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, + KEY_EN_BACK_TICK, + KEY_EN_TAB, + KEY_EN_CAPS_LOCK, + KEY_EN_LEFT_SHIFT, + KEY_EN_LEFT_CONTROL, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_1, + KEY_EN_Q, + KEY_EN_A, + KEY_EN_Z, + KEY_EN_LEFT_WINDOWS, + KEY_EN_F1, + KEY_EN_2, + KEY_EN_W, + KEY_EN_S, + KEY_EN_X, + KEY_EN_LEFT_ALT, + KEY_EN_F2, + KEY_EN_3, + KEY_EN_E, + KEY_EN_D, + KEY_EN_C, + // Skip index 23 + KEY_EN_F3, + KEY_EN_4, + KEY_EN_R, + KEY_EN_F, + KEY_EN_V, + // Skip index 29 + KEY_EN_F4, + KEY_EN_5, + KEY_EN_T, + KEY_EN_G, + KEY_EN_B, + KEY_EN_SPACE, + KEY_EN_F5, + KEY_EN_6, + KEY_EN_Y, + KEY_EN_H, + KEY_EN_N, + // Skip index 41 + KEY_EN_F6, + KEY_EN_7, + KEY_EN_U, + KEY_EN_J, + KEY_EN_M, + // Skip index 47 + KEY_EN_F7, + KEY_EN_8, + KEY_EN_I, + KEY_EN_K, + KEY_EN_COMMA, + KEY_EN_RIGHT_ALT, + KEY_EN_F8, + KEY_EN_9, + KEY_EN_O, + KEY_EN_L, + KEY_EN_PERIOD, + KEY_EN_MENU, + KEY_EN_F9, + KEY_EN_0, + KEY_EN_P, + KEY_EN_SEMICOLON, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_F10, + KEY_EN_MINUS, + KEY_EN_LEFT_BRACKET, + KEY_EN_QUOTE, + // Skip index 70 + // Skip index 71 + KEY_EN_F11, + KEY_EN_EQUALS, + KEY_EN_RIGHT_BRACKET, + KEY_EN_POUND, + // Skip index 76 + KEY_EN_F12, + KEY_EN_BACKSPACE, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_ANSI_ENTER, + KEY_EN_RIGHT_SHIFT, + KEY_EN_RIGHT_CONTROL, + KEY_EN_PRINT_SCREEN, + KEY_EN_INSERT, + KEY_EN_DELETE, + // Skip index 87 + // Skip index 88 + KEY_EN_LEFT_ARROW, + KEY_EN_SCROLL_LOCK, + KEY_EN_HOME, + KEY_EN_END, + // Skip index 93 + KEY_EN_UP_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_PAUSE_BREAK, + KEY_EN_PAGE_UP, + KEY_EN_PAGE_DOWN, + // Skip index 99 + // Skip index 100 + KEY_EN_RIGHT_ARROW, + // Skip index 102 + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_0, + // Skip index 108 + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_2, + // Skip index 113 + KEY_EN_MEDIA_MUTE, // Last multimedia key + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_PERIOD, + KEY_EN_MEDIA_PREVIOUS, // First multimedia key + KEY_EN_NUMPAD_MINUS, + KEY_EN_NUMPAD_PLUS, + KEY_EN_MEDIA_PLAY_PAUSE, // Second multimedia key + KEY_EN_MEDIA_NEXT, // Third multimedia key + KEY_EN_NUMPAD_ENTER, + "RGB Strip 1", + "RGB Strip 2", + "RGB Strip 3", + "RGB Strip 4", + "RGB Strip 5", + "RGB Strip 6", + "RGB Strip 7", + "RGB Strip 8", + "RGB Strip 9", + "RGB Strip 10", + "RGB Strip 11", + "RGB Strip 12", + "RGB Strip 13", + "RGB Strip 14", + "RGB Strip 15", + "RGB Strip 16", + "RGB Strip 17", + "RGB Strip 18", +}; + +/**------------------------------------------------------------------*\ + @name HyperX Alloy Elite 2 + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectHyperXAlloyElite2 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXAlloyElite2::RGBController_HyperXAlloyElite2(HyperXAlloyElite2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_KEYBOARD; + description = "HyperX Alloy Elite 2 Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + /*-----------------------------------------------------*\ + | The Corsair Lighting Node Pro requires a packet within| + | 20 seconds of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 5s | + \*-----------------------------------------------------*/ + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RGBController_HyperXAlloyElite2::KeepaliveThreadFunction, this); +} + +RGBController_HyperXAlloyElite2::~RGBController_HyperXAlloyElite2() +{ + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != nullptr) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_HyperXAlloyElite2::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 8; + new_zone.matrix_map->width = 22; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = nullptr; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_HyperXAlloyElite2::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXAlloyElite2::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + if(active_mode == 0) + { + controller->SetLEDsDirect(colors); + } +} + +void RGBController_HyperXAlloyElite2::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyElite2::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyElite2::DeviceUpdateMode() +{ + +} + +void RGBController_HyperXAlloyElite2::KeepaliveThreadFunction() +{ + while(keepalive_thread_run) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(1000)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(50ms); + } +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/RGBController_HyperXAlloyElite2.h b/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/RGBController_HyperXAlloyElite2.h new file mode 100644 index 0000000..eab0328 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/RGBController_HyperXAlloyElite2.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyElite2.h | +| | +| RGBController for HyperX Alloy Elite 2 keyboard | +| | +| KundaPanda (vojdo) 02 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "HyperXAlloyElite2Controller.h" + +class RGBController_HyperXAlloyElite2 : public RGBController +{ +public: + RGBController_HyperXAlloyElite2(HyperXAlloyElite2Controller* controller_ptr); + ~RGBController_HyperXAlloyElite2(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThreadFunction(); + +private: + HyperXAlloyElite2Controller* controller; + std::atomic keepalive_thread_run; + std::thread* keepalive_thread; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/HyperXAlloyEliteController.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/HyperXAlloyEliteController.cpp new file mode 100644 index 0000000..0fba5b0 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/HyperXAlloyEliteController.cpp @@ -0,0 +1,509 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyEliteController.cpp | +| | +| Driver for HyperX Alloy Elite keyboard | +| | +| Adam Honse (CalcProgrammer1) 30 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXAlloyEliteController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +static unsigned int keys[] = {0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x41, + 0x44, 0x45, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x51, 0x54, 0x55, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5E, 0x5F, 0x61, 0x64, 0x65, 0x68, 0x69, 0x6A, + 0x6B, 0x6C, 0x6E, 0x6F, 0x74, 0x75, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, + 0x7F, 0x81, 0x84, 0x85, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x91, + 0x94, 0x95 }; + +static unsigned int extended_red[] = {0x08, 0x48, 0x88, 0x09, 0x89, 0x0A, 0x8A, 0x0B, 0x8B, 0x0C, 0x8C, 0x0D, 0x8D, 0x0E, 0x8F, 0x8E, 0x0F, 0x4F, 0x92, 0x13, 0x93, 0x12 }; +static unsigned int extended_grn[] = {0x29, 0x28, 0x78, 0x19, 0x79, 0x1A, 0x7A, 0x1B, 0x7B, 0x1C, 0x7C, 0x1D, 0x7D, 0x1E, 0x6E, 0x7E, 0x1F, 0x6F, 0x82, 0x23, 0x83, 0x22 }; +static unsigned int extended_blu[] = {0x39, 0x38, 0x68, 0x3A, 0x69, 0x2A, 0x6A, 0x2B, 0x6B, 0x2C, 0x6C, 0x2D, 0x6D, 0x2E, 0x5E, 0x5D, 0x2F, 0x5F, 0x72, 0x33, 0x73, 0x32 }; + +HyperXAlloyEliteController::HyperXAlloyEliteController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXAlloyEliteController::~HyperXAlloyEliteController() +{ + hid_close(dev); +} + +std::string HyperXAlloyEliteController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string HyperXAlloyEliteController::GetNameString() +{ + return(name); +} + +std::string HyperXAlloyEliteController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void HyperXAlloyEliteController::SetMode + ( + unsigned char mode, + unsigned char direction, + unsigned char speed, + std::vector colors + ) +{ + unsigned char color_mode; + unsigned char mode_colors[9]; + + active_mode = mode; + active_direction = direction; + active_speed = speed; + + memset(mode_colors, 0x00, sizeof(mode_colors)); + + switch(colors.size()) + { + default: + case 0: + color_mode = HYPERX_ALLOY_ELITE_COLOR_MODE_SPECTRUM; + break; + + case 1: + color_mode = HYPERX_ALLOY_ELITE_COLOR_MODE_SINGLE; + mode_colors[0] = RGBGetRValue(colors[0]); + mode_colors[1] = RGBGetGValue(colors[0]); + mode_colors[2] = RGBGetBValue(colors[0]); + break; + + case 2: + color_mode = HYPERX_ALLOY_ELITE_COLOR_MODE_DUAL; + mode_colors[3] = RGBGetRValue(colors[0]); + mode_colors[4] = RGBGetGValue(colors[0]); + mode_colors[5] = RGBGetBValue(colors[0]); + mode_colors[6] = RGBGetRValue(colors[1]); + mode_colors[7] = RGBGetGValue(colors[1]); + mode_colors[8] = RGBGetBValue(colors[1]); + break; + } + + SendEffect + ( + 0x01, + active_mode, + active_direction, + HYPERX_ALLOY_ELITE_REACTIVE_MODE_NONE, + active_speed, + color_mode, + mode_colors[0], + mode_colors[1], + mode_colors[2], + mode_colors[3], + mode_colors[4], + mode_colors[5], + mode_colors[6], + mode_colors[7], + mode_colors[8] + ); + + std::this_thread::sleep_for(100ms); +} + +void HyperXAlloyEliteController::SetLEDsDirect(std::vector colors) +{ + unsigned char red_color_data[106]; + unsigned char grn_color_data[106]; + unsigned char blu_color_data[106]; + unsigned char ext_color_data[150]; + + for(std::size_t i = 0; i < 106; i++) + { + red_color_data[i] = RGBGetRValue(colors[i]); + grn_color_data[i] = RGBGetGValue(colors[i]); + blu_color_data[i] = RGBGetBValue(colors[i]); + } + + for(std::size_t i = 0; i < 22; i++) + { + ext_color_data[extended_red[i]] = RGBGetRValue(colors[i + 106]); + ext_color_data[extended_grn[i]] = RGBGetGValue(colors[i + 106]); + ext_color_data[extended_blu[i]] = RGBGetBValue(colors[i + 106]); + } + + SendDirect + ( + HYPERX_ALLOY_ELITE_COLOR_CHANNEL_RED, + red_color_data + ); + + std::this_thread::sleep_for(5ms); + + SendDirect + ( + HYPERX_ALLOY_ELITE_COLOR_CHANNEL_GREEN, + grn_color_data + ); + + std::this_thread::sleep_for(5ms); + + SendDirect + ( + HYPERX_ALLOY_ELITE_COLOR_CHANNEL_BLUE, + blu_color_data + ); + + std::this_thread::sleep_for(5ms); + + SendDirectExtended + ( + ext_color_data + ); +} + +void HyperXAlloyEliteController::SetLEDs(std::vector colors) +{ + unsigned char red_color_data[106]; + unsigned char grn_color_data[106]; + unsigned char blu_color_data[106]; + unsigned char ext_color_data[150]; + + for(std::size_t i = 0; i < 106; i++) + { + red_color_data[i] = RGBGetRValue(colors[i]); + grn_color_data[i] = RGBGetGValue(colors[i]); + blu_color_data[i] = RGBGetBValue(colors[i]); + } + + for(std::size_t i = 0; i < 22; i++) + { + ext_color_data[extended_red[i]] = RGBGetRValue(colors[i + 106]); + ext_color_data[extended_grn[i]] = RGBGetGValue(colors[i + 106]); + ext_color_data[extended_blu[i]] = RGBGetBValue(colors[i + 106]); + } + + SendColor + ( + 0x01, + HYPERX_ALLOY_ELITE_COLOR_CHANNEL_RED, + red_color_data + ); + + std::this_thread::sleep_for(5ms); + + SendColor + ( + 0x01, + HYPERX_ALLOY_ELITE_COLOR_CHANNEL_GREEN, + grn_color_data + ); + + std::this_thread::sleep_for(5ms); + + SendColor + ( + 0x01, + HYPERX_ALLOY_ELITE_COLOR_CHANNEL_BLUE, + blu_color_data + ); + + std::this_thread::sleep_for(5ms); + + SendExtendedColor + ( + 0x01, + ext_color_data + ); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void HyperXAlloyEliteController::SelectProfile + ( + unsigned char profile + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Select Profile packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = 0x01; + buf[0x02] = profile; + + buf[0x06] = 0x03; + buf[0x07] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} + +void HyperXAlloyEliteController::SendEffect + ( + unsigned char profile, + unsigned char mode, + unsigned char direction, + unsigned char reactive_mode, + unsigned char speed, + unsigned char color_mode, + unsigned char red_single, + unsigned char grn_single, + unsigned char blu_single, + unsigned char red_dual_1, + unsigned char grn_dual_1, + unsigned char blu_dual_1, + unsigned char red_dual_2, + unsigned char grn_dual_2, + unsigned char blu_dual_2 + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Effect packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = HYPERX_ALLOY_ELITE_PACKET_ID_SET_EFFECT; + buf[0x02] = profile; + + /*-----------------------------------------------------*\ + | Set mode | + \*-----------------------------------------------------*/ + buf[0x09] = 0x01; + buf[0x0A] = mode; + + /*-----------------------------------------------------*\ + | Set direction | + \*-----------------------------------------------------*/ + buf[0x0D] = direction; + buf[0x0E] = direction; + + /*-----------------------------------------------------*\ + | Set reactive mode | + \*-----------------------------------------------------*/ + buf[0x1B] = reactive_mode; + buf[0x1C] = reactive_mode; + buf[0x1D] = reactive_mode; + buf[0x1E] = reactive_mode; + buf[0x1F] = reactive_mode; + buf[0x20] = reactive_mode; + buf[0x21] = reactive_mode; + buf[0x22] = reactive_mode; + + /*-----------------------------------------------------*\ + | Set mode-specific colors | + \*-----------------------------------------------------*/ + buf[0x29] = red_single; + buf[0x41] = grn_single; + buf[0x59] = blu_single; + buf[0x2A] = red_dual_1; + buf[0x42] = grn_dual_1; + buf[0x5A] = blu_dual_1; + buf[0x2B] = red_dual_2; + buf[0x43] = grn_dual_2; + buf[0x5B] = blu_dual_2; + + buf[0x6B] = 0x09; + buf[0x6C] = 0x09; + buf[0x6D] = 0x05; + buf[0x6E] = 0x05; + buf[0x6F] = 0x06; + buf[0x70] = 0x05; + + /*-----------------------------------------------------*\ + | Set speed | + \*-----------------------------------------------------*/ + buf[0x71] = speed; + + buf[0x72] = 0x09; + + /*-----------------------------------------------------*\ + | Set color mode | + \*-----------------------------------------------------*/ + buf[0x73] = color_mode; + buf[0x74] = color_mode; + buf[0x75] = color_mode; + buf[0x76] = color_mode; + buf[0x77] = color_mode; + buf[0x78] = color_mode; + buf[0x79] = color_mode; + buf[0x7A] = color_mode; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} + +void HyperXAlloyEliteController::SendColor + ( + unsigned char profile, + unsigned char color_channel, + unsigned char* color_data + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Color packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = HYPERX_ALLOY_ELITE_PACKET_ID_SET_COLOR; + buf[0x02] = profile; + buf[0x03] = color_channel; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for(int i = 0; i < 106; i++) + { + buf[keys[i]] = color_data[i]; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} + +void HyperXAlloyEliteController::SendExtendedColor + ( + unsigned char profile, + unsigned char* color_data + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Color packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = HYPERX_ALLOY_ELITE_PACKET_ID_SET_COLOR; + buf[0x02] = profile; + buf[0x03] = HYPERX_ALLOY_ELITE_COLOR_CHANNEL_EXTENDED; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for(int i = 0x08; i < 0x94; i++) + { + buf[i] = color_data[i]; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} + +void HyperXAlloyEliteController::SendDirect + ( + unsigned char color_channel, + unsigned char* color_data + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = HYPERX_ALLOY_ELITE_PACKET_ID_DIRECT; + buf[0x02] = color_channel; + buf[0x03] = 0xA0; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for(int i = 0; i < 106; i++) + { + buf[keys[i]] = color_data[i]; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} + +void HyperXAlloyEliteController::SendDirectExtended + ( + unsigned char* color_data + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = HYPERX_ALLOY_ELITE_PACKET_ID_DIRECT; + buf[0x02] = HYPERX_ALLOY_ELITE_COLOR_CHANNEL_EXTENDED; + buf[0x03] = 0xA0; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for(int i = 0x08; i < 0x94; i++) + { + buf[i] = color_data[i]; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/HyperXAlloyEliteController.h b/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/HyperXAlloyEliteController.h new file mode 100644 index 0000000..76dd29b --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/HyperXAlloyEliteController.h @@ -0,0 +1,142 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyEliteController.h | +| | +| Driver for HyperX Alloy Elite keyboard | +| | +| Adam Honse (CalcProgrammer1) 30 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + HYPERX_ALLOY_ELITE_PACKET_ID_SET_EFFECT = 0x02, /* Set profile effect packet */ + HYPERX_ALLOY_ELITE_PACKET_ID_SET_COLOR = 0x06, /* Set profile color packet */ + HYPERX_ALLOY_ELITE_PACKET_ID_DIRECT = 0x16, /* Direct control packet */ +}; + + +enum +{ + HYPERX_ALLOY_ELITE_DIRECTION_RIGHT = 0x00, + HYPERX_ALLOY_ELITE_DIRECTION_LEFT = 0x01, + HYPERX_ALLOY_ELITE_DIRECTION_UP = 0x02, + HYPERX_ALLOY_ELITE_DIRECTION_DOWN = 0x03, + HYPERX_ALLOY_ELITE_DIRECTION_IN = 0x04, + HYPERX_ALLOY_ELITE_DIRECTION_OUT = 0x05 +}; + +enum +{ + HYPERX_ALLOY_ELITE_MODE_WAVE = 0x00, + HYPERX_ALLOY_ELITE_MODE_STATIC = 0x01, + HYPERX_ALLOY_ELITE_MODE_BREATHING = 0x02, +}; + +enum +{ + HYPERX_ALLOY_ELITE_REACTIVE_MODE_TRIGGER = 0x03, + HYPERX_ALLOY_ELITE_REACTIVE_MODE_EXPLOSION = 0x04, + HYPERX_ALLOY_ELITE_REACTIVE_MODE_HYPERX_FLAME = 0x05, + HYPERX_ALLOY_ELITE_REACTIVE_MODE_NONE = 0xFF +}; + +enum +{ + HYPERX_ALLOY_ELITE_COLOR_MODE_SINGLE = 0x00, + HYPERX_ALLOY_ELITE_COLOR_MODE_DUAL = 0x01, + HYPERX_ALLOY_ELITE_COLOR_MODE_SPECTRUM = 0x02 +}; + +enum +{ + HYPERX_ALLOY_ELITE_COLOR_CHANNEL_RED = 0x01, + HYPERX_ALLOY_ELITE_COLOR_CHANNEL_GREEN = 0x02, + HYPERX_ALLOY_ELITE_COLOR_CHANNEL_BLUE = 0x03, + HYPERX_ALLOY_ELITE_COLOR_CHANNEL_EXTENDED = 0x04 +}; + +class HyperXAlloyEliteController +{ +public: + HyperXAlloyEliteController(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXAlloyEliteController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetMode + ( + unsigned char mode, + unsigned char direction, + unsigned char speed, + std::vector colors + ); + + void SetLEDsDirect(std::vector colors); + void SetLEDs(std::vector colors); + +private: + hid_device* dev; + unsigned char active_mode; + unsigned char active_direction; + unsigned char active_speed; + std::string location; + std::string name; + + void SelectProfile + ( + unsigned char profile + ); + + void SendEffect + ( + unsigned char profile, + unsigned char mode, + unsigned char direction, + unsigned char reactive_mode, + unsigned char speed, + unsigned char color_mode, + unsigned char red_single, + unsigned char grn_single, + unsigned char blu_single, + unsigned char red_dual_1, + unsigned char grn_dual_1, + unsigned char blu_dual_1, + unsigned char red_dual_2, + unsigned char grn_dual_2, + unsigned char blu_dual_2 + ); + + void SendColor + ( + unsigned char profile, + unsigned char color_channel, + unsigned char* color_data + ); + + void SendExtendedColor + ( + unsigned char profile, + unsigned char* color_data + ); + + void SendDirect + ( + unsigned char color_channel, + unsigned char* color_data + ); + + void SendDirectExtended + ( + unsigned char* color_data + ); +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/RGBController_HyperXAlloyElite.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/RGBController_HyperXAlloyElite.cpp new file mode 100644 index 0000000..66e2f05 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/RGBController_HyperXAlloyElite.cpp @@ -0,0 +1,371 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyElite.cpp | +| | +| RGBController for HyperX Alloy Elite keyboard | +| | +| Adam Honse (CalcProgrammer1) 02 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_HyperXAlloyElite.h" + +using namespace std::chrono_literals; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[6][23] = + { { 0, NA, 16, 30, 44, 54, NA, 65, 75, 84, 95, NA, 8, 23 , 38, 6 , 22, 36, 49, NA, NA, NA, NA }, + { 1, 17, 31, 45, 55, 66, 76, 85, 96, 9, 24, NA, 39, 7 , 37, NA , 60, 70, 80, 52, 63, 73, 82 }, + { 2, NA, 18, 32, 46, 56, NA, 67, 77, 86, 97, 10, 25, 40 , 90, 101, 50, 61, 71, 51, 62, 72, 93 }, + { 3, NA, 19, 33, 47, 57, NA, 68, 78, 87, 98, 11, 26, 41 , 28, 14 , NA, NA, NA, 92, 103, 53, NA }, + { 4, 20, 34, 48, 58, 69, NA, 79, NA, 88, 99, 12, 27, 42 , 81, NA , NA, 102, NA, 64, 74, 83, 104 }, + { 5, 21, 35, NA, NA, NA, NA, 59, NA, NA, NA, NA, 89, 100, 13, 91 , 15, 29, 43, 94, NA, 105, NA } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, + "RGB Strip", + "Media Keys" +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, + ZONE_TYPE_LINEAR, + ZONE_TYPE_SINGLE +}; + +static const unsigned int zone_sizes[] = +{ + 106, + 18, + 4 +}; + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, + KEY_EN_BACK_TICK, + KEY_EN_TAB, + KEY_EN_CAPS_LOCK, + KEY_EN_LEFT_SHIFT, + KEY_EN_LEFT_CONTROL, + KEY_EN_F12, + KEY_EN_EQUALS, + KEY_EN_F9, + KEY_EN_9, + KEY_EN_O, + KEY_EN_L, + KEY_EN_COMMA, + KEY_EN_MENU, + KEY_EN_ISO_ENTER, + KEY_EN_LEFT_ARROW, + KEY_EN_F1, + KEY_EN_1, + KEY_EN_Q, + KEY_EN_A, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_LEFT_WINDOWS, + KEY_EN_PRINT_SCREEN, + KEY_EN_F10, + KEY_EN_0, + KEY_EN_P, + KEY_EN_SEMICOLON, + KEY_EN_PERIOD, + KEY_EN_ANSI_ENTER, + KEY_EN_DOWN_ARROW, + KEY_EN_F2, + KEY_EN_2, + KEY_EN_W, + KEY_EN_S, + KEY_EN_Z, + KEY_EN_LEFT_ALT, + KEY_EN_SCROLL_LOCK, + KEY_EN_BACKSPACE, + KEY_EN_F11, + KEY_EN_MINUS, + KEY_EN_LEFT_BRACKET, + KEY_EN_QUOTE, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_ARROW, + KEY_EN_F3, + KEY_EN_3, + KEY_EN_E, + KEY_EN_D, + KEY_EN_X, + KEY_EN_PAUSE_BREAK, + KEY_EN_DELETE, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_6, + KEY_EN_F4, + KEY_EN_4, + KEY_EN_R, + KEY_EN_F, + KEY_EN_C, + KEY_EN_SPACE, + KEY_EN_INSERT, + KEY_EN_END, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_1, + KEY_EN_F5, + KEY_EN_5, + KEY_EN_T, + KEY_EN_G, + KEY_EN_V, + KEY_EN_HOME, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_2, + KEY_EN_F6, + KEY_EN_6, + KEY_EN_Y, + KEY_EN_H, + KEY_EN_B, + KEY_EN_PAGE_UP, + KEY_EN_RIGHT_SHIFT, + KEY_EN_NUMPAD_MINUS, + KEY_EN_NUMPAD_3, + KEY_EN_F7, + KEY_EN_7, + KEY_EN_U, + KEY_EN_J, + KEY_EN_N, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_BRACKET, + KEY_EN_RIGHT_CONTROL, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_PLUS, + KEY_EN_NUMPAD_0, + KEY_EN_F8, + KEY_EN_8, + KEY_EN_I, + KEY_EN_K, + KEY_EN_M, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_UP_ARROW, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_ENTER, + KEY_EN_NUMPAD_PERIOD, + "RGB Strip 1", + "RGB Strip 2", + "RGB Strip 3", + "RGB Strip 4", + "RGB Strip 5", + "RGB Strip 6", + "RGB Strip 7", + "RGB Strip 8", + "RGB Strip 9", + "RGB Strip 10", + "RGB Strip 11", + "RGB Strip 12", + "RGB Strip 13", + "RGB Strip 14", + "RGB Strip 15", + "RGB Strip 16", + "RGB Strip 17", + "RGB Strip 18", + KEY_EN_MEDIA_PREVIOUS, + KEY_EN_MEDIA_PLAY_PAUSE, + KEY_EN_MEDIA_NEXT, + KEY_EN_MEDIA_MUTE +}; + +/**------------------------------------------------------------------*\ + @name HyperX Alloy Elite + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectHyperXAlloyElite + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXAlloyElite::RGBController_HyperXAlloyElite(HyperXAlloyEliteController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_KEYBOARD; + description = "HyperX Alloy Elite Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = HYPERX_ALLOY_ELITE_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = HYPERX_ALLOY_ELITE_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Wave; + Wave.name = "Wave"; + Wave.value = HYPERX_ALLOY_ELITE_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE; + Wave.speed_min = 0x00; + Wave.speed_max = 0x09; + Wave.color_mode = MODE_COLORS_NONE; + Wave.speed = 0x09; + Wave.direction = MODE_DIRECTION_LEFT; + modes.push_back(Wave); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = HYPERX_ALLOY_ELITE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = 0x00; + Breathing.speed_max = 0x09; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = 0x09; + Breathing.colors.resize(2); + modes.push_back(Breathing); + + SetupZones(); + + /*-----------------------------------------------------*\ + | The HyperX Alloy Elite requires a steady stream of | + | packets in order to not revert out of direct mode. | + | Start a thread to continuously refresh the device | + \*-----------------------------------------------------*/ + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RGBController_HyperXAlloyElite::KeepaliveThreadFunction, this); +} + +RGBController_HyperXAlloyElite::~RGBController_HyperXAlloyElite() +{ + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_HyperXAlloyElite::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 3; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 23; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_HyperXAlloyElite::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXAlloyElite::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + if(active_mode == 0) + { + controller->SetLEDsDirect(colors); + } + else + { + controller->SetLEDs(colors); + } +} + +void RGBController_HyperXAlloyElite::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyElite::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyElite::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + controller->SetMode(modes[active_mode].value, modes[active_mode].direction, modes[active_mode].speed, modes[active_mode].colors); + } + else + { + std::vector temp_colors; + controller->SetMode(modes[active_mode].value, modes[active_mode].direction, modes[active_mode].speed, temp_colors); + } +} + +void RGBController_HyperXAlloyElite::KeepaliveThreadFunction() +{ + while(keepalive_thread_run) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(10ms);; + } +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/RGBController_HyperXAlloyElite.h b/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/RGBController_HyperXAlloyElite.h new file mode 100644 index 0000000..d3b32ba --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyEliteController/RGBController_HyperXAlloyElite.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyElite.h | +| | +| RGBController for HyperX Alloy Elite keyboard | +| | +| Adam Honse (CalcProgrammer1) 02 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "HyperXAlloyEliteController.h" + +class RGBController_HyperXAlloyElite : public RGBController +{ +public: + RGBController_HyperXAlloyElite(HyperXAlloyEliteController* controller_ptr); + ~RGBController_HyperXAlloyElite(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThreadFunction(); + +private: + HyperXAlloyEliteController* controller; + std::atomic keepalive_thread_run; + std::thread* keepalive_thread; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/HyperXAlloyFPSController.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/HyperXAlloyFPSController.cpp new file mode 100644 index 0000000..06f26f7 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/HyperXAlloyFPSController.cpp @@ -0,0 +1,136 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyFPSController.cpp | +| | +| Driver for HyperX Alloy FPS keyboard | +| | +| Adam Honse (CalcProgrammer1) 30 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXAlloyFPSController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +static unsigned int keys[] = {0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x41, + 0x44, 0x45, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x51, 0x54, 0x55, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5E, 0x5F, 0x61, 0x64, 0x65, 0x68, 0x69, 0x6A, + 0x6B, 0x6C, 0x6E, 0x6F, 0x74, 0x75, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, + 0x7F, 0x81, 0x84, 0x85, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x91, + 0x94, 0x95 }; + +HyperXAlloyFPSController::HyperXAlloyFPSController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXAlloyFPSController::~HyperXAlloyFPSController() +{ + hid_close(dev); +} + +std::string HyperXAlloyFPSController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string HyperXAlloyFPSController::GetNameString() +{ + return(name); +} + +std::string HyperXAlloyFPSController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void HyperXAlloyFPSController::SetLEDsDirect(std::vector colors) +{ + unsigned char red_color_data[106]; + unsigned char grn_color_data[106]; + unsigned char blu_color_data[106]; + + for(std::size_t i = 0; i < 106; i++) + { + red_color_data[i] = RGBGetRValue(colors[i]); + grn_color_data[i] = RGBGetGValue(colors[i]); + blu_color_data[i] = RGBGetBValue(colors[i]); + } + + SendDirect + ( + HYPERX_ALLOY_FPS_COLOR_CHANNEL_RED, + red_color_data + ); + + std::this_thread::sleep_for(10ms); + + SendDirect + ( + HYPERX_ALLOY_FPS_COLOR_CHANNEL_GREEN, + grn_color_data + ); + + std::this_thread::sleep_for(10ms); + + SendDirect + ( + HYPERX_ALLOY_FPS_COLOR_CHANNEL_BLUE, + blu_color_data + ); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void HyperXAlloyFPSController::SendDirect + ( + unsigned char color_channel, + unsigned char* color_data + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = HYPERX_ALLOY_FPS_PACKET_ID_DIRECT; + buf[0x02] = color_channel; + buf[0x03] = 0xA0; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for(int i = 0; i < 106; i++) + { + buf[keys[i]] = color_data[i]; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/HyperXAlloyFPSController.h b/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/HyperXAlloyFPSController.h new file mode 100644 index 0000000..66ffec9 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/HyperXAlloyFPSController.h @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyFPSController.h | +| | +| Driver for HyperX Alloy FPS keyboard | +| | +| Adam Honse (CalcProgrammer1) 30 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + HYPERX_ALLOY_FPS_PACKET_ID_DIRECT = 0x16, /* Direct control packet */ +}; + +enum +{ + HYPERX_ALLOY_FPS_COLOR_CHANNEL_RED = 0x01, + HYPERX_ALLOY_FPS_COLOR_CHANNEL_GREEN = 0x02, + HYPERX_ALLOY_FPS_COLOR_CHANNEL_BLUE = 0x03 +}; + +class HyperXAlloyFPSController +{ +public: + HyperXAlloyFPSController(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXAlloyFPSController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetLEDsDirect(std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendDirect + ( + unsigned char color_channel, + unsigned char* color_data + ); +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/RGBController_HyperXAlloyFPS.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/RGBController_HyperXAlloyFPS.cpp new file mode 100644 index 0000000..6e171b8 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/RGBController_HyperXAlloyFPS.cpp @@ -0,0 +1,300 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyFPS.cpp | +| | +| RGBController for HyperX Alloy FPS keyboard | +| | +| Adam Honse (CalcProgrammer1) 02 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_HyperXAlloyFPS.h" + +using namespace std::chrono_literals; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[6][23] = + { { 0, NA, 16, 30, 44, 54, NA, 65, 75, 84, 95, NA, 8, 23 , 38, 6 , 22, 36, 49, NA, NA, NA, NA }, + { 1, 17, 31, 45, 55, 66, 76, 85, 96, 9, 24, NA, 39, 7 , 37, NA , 60, 70, 80, 52, 63, 73, 82 }, + { 2, NA, 18, 32, 46, 56, NA, 67, 77, 86, 97, 10, 25, 40 , 90, 101, 50, 61, 71, 51, 62, 72, 93 }, + { 3, NA, 19, 33, 47, 57, NA, 68, 78, 87, 98, 11, 26, 41 , 28, 14 , NA, NA, NA, 92, 103, 53, NA }, + { 4, 20, 34, 48, 58, 69, NA, 79, NA, 88, 99, 12, 27, 42 , 81, NA , NA, 102, NA, 64, 74, 83, 104 }, + { 5, 21, 35, NA, NA, NA, NA, 59, NA, NA, NA, NA, 89, 100, 13, 91 , 15, 29, 43, 94, NA, 105, NA } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX +}; + +static const unsigned int zone_sizes[] = +{ + 106 +}; + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, + KEY_EN_BACK_TICK, + KEY_EN_TAB, + KEY_EN_CAPS_LOCK, + KEY_EN_LEFT_SHIFT, + KEY_EN_LEFT_CONTROL, + KEY_EN_F12, + KEY_EN_EQUALS, + KEY_EN_F9, + KEY_EN_9, + KEY_EN_O, + KEY_EN_L, + KEY_EN_COMMA, + KEY_EN_MENU, + KEY_EN_ISO_ENTER, + KEY_EN_LEFT_ARROW, + KEY_EN_F1, + KEY_EN_1, + KEY_EN_Q, + KEY_EN_A, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_LEFT_WINDOWS, + KEY_EN_PRINT_SCREEN, + KEY_EN_F10, + KEY_EN_0, + KEY_EN_P, + KEY_EN_SEMICOLON, + KEY_EN_PERIOD, + KEY_EN_ANSI_ENTER, + KEY_EN_DOWN_ARROW, + KEY_EN_F2, + KEY_EN_2, + KEY_EN_W, + KEY_EN_S, + KEY_EN_Z, + KEY_EN_LEFT_ALT, + KEY_EN_SCROLL_LOCK, + KEY_EN_BACKSPACE, + KEY_EN_F11, + KEY_EN_MINUS, + KEY_EN_LEFT_BRACKET, + KEY_EN_QUOTE, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_ARROW, + KEY_EN_F3, + KEY_EN_3, + KEY_EN_E, + KEY_EN_D, + KEY_EN_X, + KEY_EN_PAUSE_BREAK, + KEY_EN_DELETE, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_6, + KEY_EN_F4, + KEY_EN_4, + KEY_EN_R, + KEY_EN_F, + KEY_EN_C, + KEY_EN_SPACE, + KEY_EN_INSERT, + KEY_EN_END, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_1, + KEY_EN_F5, + KEY_EN_5, + KEY_EN_T, + KEY_EN_G, + KEY_EN_V, + KEY_EN_HOME, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_2, + KEY_EN_F6, + KEY_EN_6, + KEY_EN_Y, + KEY_EN_H, + KEY_EN_B, + KEY_EN_PAGE_UP, + KEY_EN_RIGHT_SHIFT, + KEY_EN_NUMPAD_MINUS, + KEY_EN_NUMPAD_3, + KEY_EN_F7, + KEY_EN_7, + KEY_EN_U, + KEY_EN_J, + KEY_EN_N, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_BRACKET, + KEY_EN_RIGHT_CONTROL, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_PLUS, + KEY_EN_NUMPAD_0, + KEY_EN_F8, + KEY_EN_8, + KEY_EN_I, + KEY_EN_K, + KEY_EN_M, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_UP_ARROW, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_ENTER, + KEY_EN_NUMPAD_PERIOD +}; + +/**------------------------------------------------------------------*\ + @name HyperX Alloy FPS + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXAlloyFPS + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXAlloyFPS::RGBController_HyperXAlloyFPS(HyperXAlloyFPSController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_KEYBOARD; + description = "HyperX Alloy FPS Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + /*-----------------------------------------------------*\ + | The HyperX Alloy FPS requires a steady stream of | + | packets in order to not revert out of direct mode. | + | Start a thread to continuously refresh the device | + \*-----------------------------------------------------*/ + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RGBController_HyperXAlloyFPS::KeepaliveThreadFunction, this); +} + +RGBController_HyperXAlloyFPS::~RGBController_HyperXAlloyFPS() +{ + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_HyperXAlloyFPS::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 23; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_HyperXAlloyFPS::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXAlloyFPS::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + if(active_mode == 0) + { + controller->SetLEDsDirect(colors); + } +} + +void RGBController_HyperXAlloyFPS::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyFPS::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyFPS::DeviceUpdateMode() +{ + +} + +void RGBController_HyperXAlloyFPS::KeepaliveThreadFunction() +{ + while(keepalive_thread_run) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(10ms);; + } +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/RGBController_HyperXAlloyFPS.h b/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/RGBController_HyperXAlloyFPS.h new file mode 100644 index 0000000..4eb6345 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyFPSController/RGBController_HyperXAlloyFPS.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyFPS.h | +| | +| RGBController for HyperX Alloy FPS keyboard | +| | +| Adam Honse (CalcProgrammer1) 02 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "HyperXAlloyFPSController.h" + +class RGBController_HyperXAlloyFPS : public RGBController +{ +public: + RGBController_HyperXAlloyFPS(HyperXAlloyFPSController* controller_ptr); + ~RGBController_HyperXAlloyFPS(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThreadFunction(); + +private: + HyperXAlloyFPSController* controller; + std::atomic keepalive_thread_run; + std::thread* keepalive_thread; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.cpp new file mode 100644 index 0000000..c382874 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.cpp @@ -0,0 +1,148 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyOrigins60and65Controller.cpp | +| | +| Driver for HyperX Alloy Origins 60 and 65 keyboard | +| | +| Derek Huber 18 Mar 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXAlloyOrigins60and65Controller.h" +#include "StringUtils.h" + +HyperXAlloyOrigins60and65Controller::HyperXAlloyOrigins60and65Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXAlloyOrigins60and65Controller::~HyperXAlloyOrigins60and65Controller() +{ + hid_close(dev); +} + +std::string HyperXAlloyOrigins60and65Controller::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string HyperXAlloyOrigins60and65Controller::GetNameString() +{ + return(name); +} + +std::string HyperXAlloyOrigins60and65Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void HyperXAlloyOrigins60and65Controller::SetLEDsDirect(std::vector colors) +{ + /*-----------------------------------------------------*\ + | Set up variables to track progress of color transmit | + | Do this after inserting blanks | + \*-----------------------------------------------------*/ + int colors_to_send = (int)colors.size(); + int colors_sent = 0; + + SendDirectInitialization(); + + for(int pkt_idx = 0; pkt_idx < 5; pkt_idx++) + { + if(colors_to_send > 16) + { + SendDirectColorPacket(&colors[colors_sent], 16); + colors_sent += 16; + colors_to_send -= 16; + } + else if(colors_to_send > 0) + { + SendDirectColorPacket(&colors[colors_sent], colors_to_send); + colors_sent += colors_to_send; + colors_to_send -= colors_to_send; + } + else + { + RGBColor temp = 0x00000000; + SendDirectColorPacket(&temp, 1); + } + } +} + +void HyperXAlloyOrigins60and65Controller::SendDirectInitialization() +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Initialization packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + buf[0x01] = 0x04; + buf[0x02] = 0xF2; + buf[0x09] = 0x05; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 65); +} + +void HyperXAlloyOrigins60and65Controller::SendDirectColorPacket + ( + RGBColor* color_data, + unsigned int color_count + ) +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Initialization packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + + /*-----------------------------------------------------*\ + | The maximum number of colors per packet is 16 | + \*-----------------------------------------------------*/ + if(color_count > 16) + { + color_count = 16; + } + + /*-----------------------------------------------------*\ + | Copy in color data | + \*-----------------------------------------------------*/ + for(unsigned int color_idx = 0; color_idx < color_count; color_idx++) + { + buf[(color_idx * 4) + 1] = 0x81; + buf[(color_idx * 4) + 2] = RGBGetRValue(color_data[color_idx]); + buf[(color_idx * 4) + 3] = RGBGetGValue(color_data[color_idx]); + buf[(color_idx * 4) + 4] = RGBGetBValue(color_data[color_idx]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 65); +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.h b/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.h new file mode 100644 index 0000000..bbb78a3 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyOrigins60and65Controller.h | +| | +| Driver for HyperX Alloy Origins 60 and 65 keyboard | +| | +| Derek Huber 18 Mar 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class HyperXAlloyOrigins60and65Controller +{ +public: + HyperXAlloyOrigins60and65Controller(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXAlloyOrigins60and65Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetLEDsDirect(std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendDirectInitialization(); + void SendDirectColorPacket + ( + RGBColor* color_data, + unsigned int color_count + ); +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/RGBController_HyperXAlloyOrigins60and65.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/RGBController_HyperXAlloyOrigins60and65.cpp new file mode 100644 index 0000000..b79b81f --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/RGBController_HyperXAlloyOrigins60and65.cpp @@ -0,0 +1,362 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyOrigins60and65.cpp | +| | +| RGBController for HyperX Alloy Origins 60 and 65 | +| keyboard | +| | +| Derek Huber 18 Mar 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_HyperXAlloyOrigins60and65.h" + +using namespace std::chrono_literals; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map_60[5][14] = + { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15 }, + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 }, + { 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, NA, 43 }, + { 44, NA, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, NA, 57 }, + { 58, 59, 60, 61, NA, NA, 62, NA, NA, 63, 64, 65, 66, 70 } }; + +static unsigned int matrix_map_65[5][15] = + { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 69 }, + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 70 }, + { 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, NA, 43, 71 }, + { 44, NA, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 57, 73, 72 }, + { 58, 59, 60, 61, NA, NA, 62, NA, NA, 63, 64, 68, 74, 75, 76 } }; + +static const char *led_names_60[] = +{ + // First row + KEY_EN_UNUSED, + KEY_EN_ESCAPE, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_UNUSED, + KEY_EN_BACKSPACE, + + // Second row + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + + // Third row + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_UNUSED, + KEY_EN_ANSI_ENTER, + + // Fourth row + KEY_EN_LEFT_SHIFT, + KEY_EN_UNUSED, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_UNUSED, + KEY_EN_RIGHT_SHIFT, + + // Fifth row + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + "Left Space", + KEY_EN_SPACE, + "Right Space", + KEY_EN_RIGHT_ALT, + KEY_EN_MENU, + KEY_EN_RIGHT_CONTROL, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_RIGHT_FUNCTION +}; + +static const char *led_names_65[] = +{ + KEY_EN_UNUSED, + KEY_EN_ESCAPE, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_UNUSED, + KEY_EN_BACKSPACE, + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_UNUSED, + KEY_EN_ANSI_ENTER, + KEY_EN_LEFT_SHIFT, + KEY_EN_UNUSED, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_UNUSED, + KEY_EN_RIGHT_SHIFT, + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + "Left Space", + KEY_EN_SPACE, + "Right Space", + KEY_EN_RIGHT_ALT, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_HOME, + KEY_EN_DELETE, + KEY_EN_PAGE_UP, + KEY_EN_PAGE_DOWN, + KEY_EN_UP_ARROW, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW +}; + +/**------------------------------------------------------------------*\ + @name HyperX Alloy Origins 60 and 65 + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXAlloyOrigins60and65 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXAlloyOrigins60and65::RGBController_HyperXAlloyOrigins60and65(HyperXAlloyOrigins60and65Controller* controller_ptr, AlloyOrigins60and65MappingLayoutType keyboard_layout) +{ + controller = controller_ptr; + layout = keyboard_layout; + + switch(layout) + { + case ALLOY_ORIGINS_60_LAYOUT: + description = "HyperX Alloy Origins 60 Keyboard Device"; + break; + + case ALLOY_ORIGINS_65_LAYOUT: + description = "HyperX Alloy Origins 65 Keyboard Device"; + break; + } + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXAlloyOrigins60and65::KeepaliveThread, this); +} + +RGBController_HyperXAlloyOrigins60and65::~RGBController_HyperXAlloyOrigins60and65() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_HyperXAlloyOrigins60and65::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + + std::vector led_zones; + const char* const *led_names; + + switch(layout) + { + case ALLOY_ORIGINS_60_LAYOUT: + default: + led_names = led_names_60; + led_zones.push_back({ZONE_EN_KEYBOARD, ZONE_TYPE_MATRIX, 71, new matrix_map_type{5, 14, (unsigned int *)&matrix_map_60}}); + break; + case ALLOY_ORIGINS_65_LAYOUT: + led_names = led_names_65; + led_zones.push_back({ZONE_EN_KEYBOARD, ZONE_TYPE_MATRIX, 77, new matrix_map_type{5, 15, (unsigned int *)&matrix_map_65}}); + break; + } + + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = led_zones[zone_idx].name; + new_zone.type = led_zones[zone_idx].type; + new_zone.leds_min = led_zones[zone_idx].size; + new_zone.leds_max = led_zones[zone_idx].size; + new_zone.leds_count = led_zones[zone_idx].size; + + if(led_zones[zone_idx].type == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = led_zones[zone_idx].matrix; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += led_zones[zone_idx].size; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_HyperXAlloyOrigins60and65::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXAlloyOrigins60and65::DeviceUpdateLEDs() +{ + controller->SetLEDsDirect(colors); +} + +void RGBController_HyperXAlloyOrigins60and65::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyOrigins60and65::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyOrigins60and65::DeviceUpdateMode() +{ + +} + +void RGBController_HyperXAlloyOrigins60and65::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(10ms);; + } +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/RGBController_HyperXAlloyOrigins60and65.h b/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/RGBController_HyperXAlloyOrigins60and65.h new file mode 100644 index 0000000..359a465 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/RGBController_HyperXAlloyOrigins60and65.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyOrigins60and65.h | +| | +| RGBController for HyperX Alloy Origins 60 and 65 | +| keyboard | +| | +| Derek Huber 18 Mar 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXAlloyOrigins60and65Controller.h" + +enum AlloyOrigins60and65MappingLayoutType +{ + ALLOY_ORIGINS_60_LAYOUT, + ALLOY_ORIGINS_65_LAYOUT +}; + +typedef struct +{ + const char* name; + const zone_type type; + const unsigned int size; + matrix_map_type* matrix; +} led_zone; + +class RGBController_HyperXAlloyOrigins60and65 : public RGBController +{ +public: + RGBController_HyperXAlloyOrigins60and65(HyperXAlloyOrigins60and65Controller* controller_ptr, AlloyOrigins60and65MappingLayoutType keyboard_layout); + ~RGBController_HyperXAlloyOrigins60and65(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + HyperXAlloyOrigins60and65Controller* controller; + AlloyOrigins60and65MappingLayoutType layout; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.cpp new file mode 100644 index 0000000..f715f73 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.cpp @@ -0,0 +1,159 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyOriginsController.cpp | +| | +| Driver for HyperX Alloy Origins keyboard | +| | +| Adam Honse (CalcProgrammer1) 11 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXAlloyOriginsController.h" +#include "StringUtils.h" + +// Skip these indices in the color output +static unsigned int skip_idx[] = { 23, 29, 41, 47, 59, 70, 71, 87, 88, 93, 99, 100, 102, 108, 113, 114, 120, 123, 124 }; + +HyperXAlloyOriginsController::HyperXAlloyOriginsController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXAlloyOriginsController::~HyperXAlloyOriginsController() +{ + hid_close(dev); +} + +std::string HyperXAlloyOriginsController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string HyperXAlloyOriginsController::GetNameString() +{ + return(name); +} + +std::string HyperXAlloyOriginsController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void HyperXAlloyOriginsController::SetLEDsDirect(std::vector colors) +{ + /*-----------------------------------------------------*\ + | Insert color data for unused positions | + \*-----------------------------------------------------*/ + for(unsigned int skip_cnt = 0; skip_cnt < (sizeof(skip_idx) / sizeof(skip_idx[0])); skip_cnt++) + { + colors.insert(colors.begin() + skip_idx[skip_cnt], 0x00000000); + } + + /*-----------------------------------------------------*\ + | Set up variables to track progress of color transmit | + | Do this after inserting blanks | + \*-----------------------------------------------------*/ + int colors_to_send = (int)colors.size(); + int colors_sent = 0; + + SendDirectInitialization(); + + for(int pkt_idx = 0; pkt_idx < 9; pkt_idx++) + { + if(colors_to_send > 16) + { + SendDirectColorPacket(&colors[colors_sent], 16); + colors_sent += 16; + colors_to_send -= 16; + } + else if(colors_to_send > 0) + { + SendDirectColorPacket(&colors[colors_sent], colors_to_send); + colors_sent += colors_to_send; + colors_to_send -= colors_to_send; + } + else + { + RGBColor temp = 0x00000000; + SendDirectColorPacket(&temp, 1); + } + } +} + +void HyperXAlloyOriginsController::SendDirectInitialization() +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Initialization packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + buf[0x01] = 0x04; + buf[0x02] = 0xF2; + buf[0x09] = 0x09; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 65); +} + +void HyperXAlloyOriginsController::SendDirectColorPacket + ( + RGBColor* color_data, + unsigned int color_count + ) +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Initialization packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + + /*-----------------------------------------------------*\ + | The maximum number of colors per packet is 16 | + \*-----------------------------------------------------*/ + if(color_count > 16) + { + color_count = 16; + } + + /*-----------------------------------------------------*\ + | Copy in color data | + \*-----------------------------------------------------*/ + for(unsigned int color_idx = 0; color_idx < color_count; color_idx++) + { + buf[(color_idx * 4) + 1] = 0x81; + buf[(color_idx * 4) + 2] = RGBGetRValue(color_data[color_idx]); + buf[(color_idx * 4) + 3] = RGBGetGValue(color_data[color_idx]); + buf[(color_idx * 4) + 4] = RGBGetBValue(color_data[color_idx]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 65); +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.h b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.h new file mode 100644 index 0000000..f7991b9 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyOriginsController.h | +| | +| Driver for HyperX Alloy Origins keyboard | +| | +| Adam Honse (CalcProgrammer1) 11 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class HyperXAlloyOriginsController +{ +public: + HyperXAlloyOriginsController(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXAlloyOriginsController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetLEDsDirect(std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendDirectInitialization(); + void SendDirectColorPacket + ( + RGBColor* color_data, + unsigned int color_count + ); +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/RGBController_HyperXAlloyOrigins.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/RGBController_HyperXAlloyOrigins.cpp new file mode 100644 index 0000000..3794777 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/RGBController_HyperXAlloyOrigins.cpp @@ -0,0 +1,316 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyOrigins.cpp | +| | +| RGBController for HyperX Alloy Origins keyboard | +| | +| Adam Honse (CalcProgrammer1) 11 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_HyperXAlloyOrigins.h" + +using namespace std::chrono_literals; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[6][23] = + { { 0, NA, 12, 18, 23, 28, NA, 34, 39, 44, 50, NA, 55, 61, 65, 71, 77, 81, 86, NA, NA, NA, NA }, + { 1, 7, 13, 19, 24, 29, 35, 40, 45, 51, 56, NA, 62, 66, 71, 72, 78, 82, 87, 90, 95, 99, 104 }, + { 2, NA, 8, 14, 20, 25, NA, 30, 36, 41, 46, 52, 57, 63, 67, 73, 79, 83, 88, 91, 96, 100, 105 }, + { 3, NA, 9, 15, 21, 26, NA, 31, 37, 42, 47, 53, 58, 64, 68, 74, NA, NA, NA, 92, 97, 101, NA }, + { 4, 6, 10, 16, 22, 27, NA, 32, NA, 38, 43, 48, 54, 59, 69, 75, NA, 84, NA, 93, 98, 102, 106 }, + { 5, 11, 17, NA, NA, NA, NA, 33, NA, NA, NA, NA, 49, 60, 70, 76, 80, 85, 89, 94, NA, 103, NA } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 107, +}; + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, + KEY_EN_BACK_TICK, + KEY_EN_TAB, + KEY_EN_CAPS_LOCK, + KEY_EN_LEFT_SHIFT, + KEY_EN_LEFT_CONTROL, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_1, + KEY_EN_Q, + KEY_EN_A, + KEY_EN_Z, + KEY_EN_LEFT_WINDOWS, + KEY_EN_F1, + KEY_EN_2, + KEY_EN_W, + KEY_EN_S, + KEY_EN_X, + KEY_EN_LEFT_ALT, + KEY_EN_F2, + KEY_EN_3, + KEY_EN_E, + KEY_EN_D, + KEY_EN_C, + // Skip index 23 + KEY_EN_F3, + KEY_EN_4, + KEY_EN_R, + KEY_EN_F, + KEY_EN_V, + // Skip index 29 + KEY_EN_F4, + KEY_EN_5, + KEY_EN_T, + KEY_EN_G, + KEY_EN_B, + KEY_EN_SPACE, + KEY_EN_F5, + KEY_EN_6, + KEY_EN_Y, + KEY_EN_H, + KEY_EN_N, + // Skip index 41 + KEY_EN_F6, + KEY_EN_7, + KEY_EN_U, + KEY_EN_J, + KEY_EN_M, + // Skip index 47 + KEY_EN_F7, + KEY_EN_8, + KEY_EN_I, + KEY_EN_K, + KEY_EN_COMMA, + KEY_EN_RIGHT_ALT, + KEY_EN_F8, + KEY_EN_9, + KEY_EN_O, + KEY_EN_L, + KEY_EN_PERIOD, + // Skip index 59 + KEY_EN_F9, + KEY_EN_0, + KEY_EN_P, + KEY_EN_SEMICOLON, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_F10, + KEY_EN_MINUS, + KEY_EN_LEFT_BRACKET, + KEY_EN_QUOTE, + // Skip index 70 + // Skip index 71 + KEY_EN_F11, + KEY_EN_EQUALS, + KEY_EN_RIGHT_BRACKET, + KEY_EN_POUND, + "Key: / (ABNT)", + KEY_EN_MENU, + KEY_EN_F12, + KEY_EN_BACKSPACE, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_ANSI_ENTER, + KEY_EN_RIGHT_SHIFT, + KEY_EN_RIGHT_CONTROL, + KEY_EN_PRINT_SCREEN, + KEY_EN_INSERT, + KEY_EN_DELETE, + // Skip index 87 + // Skip index 88 + KEY_EN_LEFT_ARROW, + KEY_EN_SCROLL_LOCK, + KEY_EN_HOME, + KEY_EN_END, + // Skip index 93 + KEY_EN_UP_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_PAUSE_BREAK, + KEY_EN_PAGE_UP, + KEY_EN_PAGE_DOWN, + // Skip index 99 + // Skip index 100 + KEY_EN_RIGHT_ARROW, + // Skip index 102 + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_0, + // Skip index 108 + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_2, + // Skip index 113 + // Skip index 114 + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_PERIOD, + // Skip index 120 + KEY_EN_NUMPAD_MINUS, + KEY_EN_NUMPAD_PLUS, + // Skip index 123 + // Skip index 124 + KEY_EN_NUMPAD_ENTER, +}; + +/**------------------------------------------------------------------*\ + @name HyperX Alloy Origins + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXAlloyOrigins + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXAlloyOrigins::RGBController_HyperXAlloyOrigins(HyperXAlloyOriginsController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_KEYBOARD; + description = "HyperX Alloy Origins Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + /*-----------------------------------------------------*\ + | The Corsair Lighting Node Pro requires a packet within| + | 20 seconds of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 5s | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXAlloyOrigins::KeepaliveThread, this); +} + +RGBController_HyperXAlloyOrigins::~RGBController_HyperXAlloyOrigins() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_HyperXAlloyOrigins::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 23; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_HyperXAlloyOrigins::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXAlloyOrigins::DeviceUpdateLEDs() +{ + controller->SetLEDsDirect(colors); +} + +void RGBController_HyperXAlloyOrigins::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyOrigins::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyOrigins::DeviceUpdateMode() +{ + +} + +void RGBController_HyperXAlloyOrigins::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(10ms);; + } +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/RGBController_HyperXAlloyOrigins.h b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/RGBController_HyperXAlloyOrigins.h new file mode 100644 index 0000000..29b7d45 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/RGBController_HyperXAlloyOrigins.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyOrigins.h | +| | +| RGBController for HyperX Alloy Origins keyboard | +| | +| Adam Honse (CalcProgrammer1) 11 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXAlloyOriginsController.h" + +class RGBController_HyperXAlloyOrigins : public RGBController +{ +public: + RGBController_HyperXAlloyOrigins(HyperXAlloyOriginsController* controller_ptr); + ~RGBController_HyperXAlloyOrigins(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + HyperXAlloyOriginsController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.cpp new file mode 100644 index 0000000..c713280 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.cpp @@ -0,0 +1,191 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyOriginsCoreController.cpp | +| | +| Driver for HyperX Alloy Origins Core keyboard | +| | +| Volodymyr Nazarchuk (Vavooon) 28 Apr 2021 | +| Mike White (kamaaina) 09 Jun 2021 | +| carlos jordao 15 Mar 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXAlloyOriginsCoreController.h" +#include "StringUtils.h" +#include "LogManager.h" + + +HyperXAlloyOriginsCoreController::HyperXAlloyOriginsCoreController(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name) +{ + dev = dev_handle; + location = dev_info->path; + name = dev_name; + + /*-----------------------------------------------------*\ + | Get the firmware version from the device info | + \*-----------------------------------------------------*/ + char fw_version_buf[8]; + memset(fw_version_buf, '\0', sizeof(fw_version_buf)); + + unsigned short version = dev_info->release_number; + snprintf(fw_version_buf, 8, "%.2X.%.2X", (version & 0xFF00) >> 8, version & 0x00FF); + + firmware_version = fw_version_buf; +} + +HyperXAlloyOriginsCoreController::~HyperXAlloyOriginsCoreController() +{ + hid_close(dev); +} + +std::string HyperXAlloyOriginsCoreController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string HyperXAlloyOriginsCoreController::GetNameString() +{ + return(name); +} + +std::string HyperXAlloyOriginsCoreController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string HyperXAlloyOriginsCoreController::GetFirmwareVersion() +{ + return(firmware_version); +} + +unsigned int HyperXAlloyOriginsCoreController::GetVariant() +{ + unsigned char packet[65]; + unsigned int variant = 0; + int actual = 0; + + memset(packet, 0x00, sizeof(packet)); + + /*---------------------------------------*\ + | Command 10 asks some data from keyboard | + | The answer looks like: | + | * command answer header (bytes 0-4) | + | * data length: byte 4 | + | * version (bytes 5-6) | + | * Product string (bytes 9-33) | + | * Layout variant (byte 56) | + \*---------------------------------------*/ + packet[1] = 0x10; + hid_write(dev, packet, 65); + memset(packet, 0x00, sizeof(packet)); + actual = hid_read(dev, packet, 65); + + if(actual > 0) + variant = packet[56]; + else + variant = 0; + + LOG_DEBUG("[HyperX Alloy Origins Core] variant: 0x%02X", variant); + return variant; +} + + +void HyperXAlloyOriginsCoreController::SetBrightness(unsigned int brightness) +{ + unsigned char packet[65]; + memset(packet, 0x00, sizeof(packet)); + + packet[1] = 0xA7; + packet[4] = 0x01; + packet[5] = brightness; + + hid_write(dev, packet, 65); +} + +void HyperXAlloyOriginsCoreController::SetLEDsDirect(std::vector leds, std::vector colors) +{ + /*------------------------------------------------------------------------------*\ + | * Always send 380 bytes to the keyboard and a total of 94 led indexes. | + | The colors are grouped into segments of 48 bytes. | + | Each one is divided into: | + | 6 Green + 2 zeroes + 6 Green + 2 zeroes + | + | 6 Red + 2 zeroes + 6 Red + 2 zeroes + | + | 6 Blue + 2 zeroes + 6 Blue + 2 zeroes | + | \=---> sector 0 \=--> sector 1 | + | Every 6 colors form a sector. The names are arbitrary, just to make clear how | + | to set the colors into the buffer. | + | So each segment has 2 sectors and 12 colors. | + | The last 10 colors don't fill completely the last segment. | + | * All 94 colors can be sent even if some of them aren't used by the physical | + | keyboard. This allows to lit every key, even if not mapped directly. | + \*------------------------------------------------------------------------------*/ + unsigned int segment = 0, sector = 0, sequence = 0; + unsigned int total_colors = 0; + memset(color_buf, 0x00, sizeof(color_buf)); + + /*---------------------------------------------------------------------------*\ + | transfer the colors to the buffer. Max 94 colors to avoid buffer overflow. | + \*---------------------------------------------------------------------------*/ + if(colors.size() > 94) + { + total_colors = 94; + } + else + { + total_colors = (unsigned int)colors.size(); + } + + unsigned int pos = 0, color_idx = 0; + for(unsigned int i = 0; i < total_colors; i++) + { + color_idx = leds[i].value; + segment = (color_idx / 12) * 48; + sector = ((color_idx / 6) & 1) * 8; + sequence = color_idx % 6; + + pos = segment + sector + sequence; + + color_buf[pos ] = RGBGetGValue(colors[i]); + color_buf[pos + 16] = RGBGetRValue(colors[i]); + color_buf[pos + 32] = RGBGetBValue(colors[i]); + } +} + + +void HyperXAlloyOriginsCoreController::SendRGBToDevice() +{ + unsigned int sentBytes = 0; + unsigned int bytesToSend = sizeof(color_buf); + unsigned int payloadSize = 60; + unsigned int seq = 0; + + while(sentBytes < bytesToSend) + { + if (bytesToSend - sentBytes < payloadSize) + { + payloadSize = bytesToSend - sentBytes; + } + + unsigned char packet[65]; + memset(packet, 0x00, sizeof(packet)); + + packet[1] = 0xA2; + packet[2] = seq++; + packet[4] = payloadSize; + + memcpy(&packet[5], &color_buf[sentBytes], payloadSize); + hid_write(dev, packet, payloadSize + 5); + + sentBytes += payloadSize; + } +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.h b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.h new file mode 100644 index 0000000..0c7aca1 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| HyperXAlloyOriginsCoreController.h | +| | +| Driver for HyperX Alloy Origins Core keyboard | +| | +| Volodymyr Nazarchuk (Vavooon) 28 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define HYPERX_ALLOY_ORIGINS_CORE_ANSI 0x09 +#define HYPERX_ALLOY_ORIGINS_CORE_ABNT2 0x10 + +class HyperXAlloyOriginsCoreController +{ +public: + HyperXAlloyOriginsCoreController(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name); + ~HyperXAlloyOriginsCoreController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + std::string GetFirmwareVersion(); + unsigned int GetVariant(); + + void SetLEDsDirect(std::vector leds, std::vector colors); + void SendRGBToDevice(); + void SetBrightness(unsigned int brightness); + +private: + hid_device* dev; + std::string location; + std::string firmware_version; + std::string name; + unsigned char color_buf[380]; +}; diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/RGBController_HyperXAlloyOriginsCore.cpp b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/RGBController_HyperXAlloyOriginsCore.cpp new file mode 100644 index 0000000..8571797 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/RGBController_HyperXAlloyOriginsCore.cpp @@ -0,0 +1,253 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyOriginsCore.cpp | +| | +| RGBController for HyperX Alloy Origins Core keyboard | +| | +| Volodymyr Nazarchuk (Vavooon) 28 Apr 2021 | +| carlos jordao 15 Mar 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_HyperXAlloyOriginsCore.h" +#include "KeyboardLayoutManager.h" + +using namespace std::chrono_literals; + +#define HYPERX_MIN_BRIGHTNESS 0 +#define HYPERX_MAX_BRIGHTNESS 255 + +#define NA 0xFFFFFFFF + +/*----------------------------------*\ +| Maps LED position number to keys | +| * based on ANSI QWERTY | +\*----------------------------------*/ +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + + +/*--------------------------------------------------------------------------------*\ +| This keyboard (TKL) always receives 94 led colors. | +| * Some indexes are just blank (unused). | +| * Regional layouts have a few different enabled or disabled led indexes. | +| * Below there is the association of the led indexes and the keyboard keys for | +| DEFAULT layout. | +\*--------------------------------------------------------------------------------*/ + +std::vector hyperx_core_default_values +{ + 0, 1, 2, 3, 4, 5, 6, 7, 48, 49, 50, 51, 52, 53, 54, 55, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 56, 57, 58, 59, 60, 61, 62, 63, + 17, 18, 19, 20, 21, 22, 23, 24, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 25, 26, 27, 28, 29, 30, 31, 32, 73, 74, 75, 76, 77, 78, + 33, 34, 35, 36, 37, 38, 39, 40, 79, 80, 81, 82, 84, 85, + 41, 42, 43, 45, 86, 87, 88, 89, 90, 91, 92, +}; + +layout_values hyperx_core_layout +{ + hyperx_core_default_values, + { + }, +}; + +/*--------------------------------------------*\ +| Provide values to keys that has been changed | +| in DEFAULT layout. | +\*--------------------------------------------*/ +std::map regional_overlay_abnt2 +{ + { + KEYBOARD_LAYOUT_ABNT2, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 4, 11, 82, KEY_EN_SEMICOLON, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 4, 12, 83, KEY_EN_FORWARD_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + }, + } +}; + +/**------------------------------------------------------------------*\ + @name HyperX Alloy Origins Core + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXAlloyOriginsCore + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXAlloyOriginsCore::RGBController_HyperXAlloyOriginsCore(HyperXAlloyOriginsCoreController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_KEYBOARD; + description = "HyperX Alloy Origins Core Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + variant = controller->GetVariant(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = HYPERX_MIN_BRIGHTNESS; + Direct.brightness_max = HYPERX_MAX_BRIGHTNESS; + Direct.brightness = HYPERX_MAX_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + /*-----------------------------------------------------*\ + | The HyperX Origins Core requires a packet within few | + | seconds of sending the lighting change in order to | + | not revert back into current profile. Start a thread | + | to continuously send color values each 10ms | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXAlloyOriginsCore::KeepaliveThread, this); +} + +RGBController_HyperXAlloyOriginsCore::~RGBController_HyperXAlloyOriginsCore() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_HyperXAlloyOriginsCore::SetupZones() +{ + unsigned int total_leds = 0; + zone new_zone; + KEYBOARD_LAYOUT layout_name; + + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + + /*-----------------------------------------------------*\ + | Regional configuration | + | * variant is extracted from keyboard info | + \*-----------------------------------------------------*/ + switch(variant) + { + case HYPERX_ALLOY_ORIGINS_CORE_ABNT2: + layout_name = KEYBOARD_LAYOUT_ABNT2; + hyperx_core_layout.regional_overlay = regional_overlay_abnt2; + break; + + case HYPERX_ALLOY_ORIGINS_CORE_ANSI: + default: + layout_name = KEYBOARD_LAYOUT_ANSI_QWERTY; + break; + } + KeyboardLayoutManager new_kb(layout_name, KEYBOARD_SIZE_TKL, hyperx_core_layout); + + total_leds = new_kb.GetKeyCount(); + + matrix_map_type * keyboard_map = new matrix_map_type; + new_zone.leds_count = total_leds; + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = keyboard_map; + keyboard_map->height = new_kb.GetRowCount(); + keyboard_map->width = new_kb.GetColumnCount(); + keyboard_map->map = new unsigned int[keyboard_map->height * keyboard_map->width]; + + new_kb.GetKeyMap(keyboard_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT); + } + else + { + new_zone.matrix_map = NULL; + } + zones.push_back(new_zone); + + for(unsigned int led_idx = 0; led_idx < total_leds; led_idx++) + { + led new_led; + new_led.name = new_kb.GetKeyAltNameAt(led_idx); + if(new_led.name == KEY_EN_UNUSED) + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + leds.push_back(new_led); + } + } + SetupColors(); +} + +void RGBController_HyperXAlloyOriginsCore::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXAlloyOriginsCore::DeviceUpdateLEDs() +{ + controller->SetLEDsDirect(leds, colors); +} + +void RGBController_HyperXAlloyOriginsCore::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyOriginsCore::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXAlloyOriginsCore::DeviceUpdateMode() +{ + controller->SetBrightness(modes[active_mode].brightness); +} + +void RGBController_HyperXAlloyOriginsCore::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + controller->SendRGBToDevice(); + } + } + std::this_thread::sleep_for(10ms); + } +} diff --git a/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/RGBController_HyperXAlloyOriginsCore.h b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/RGBController_HyperXAlloyOriginsCore.h new file mode 100644 index 0000000..c354557 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/RGBController_HyperXAlloyOriginsCore.h @@ -0,0 +1,45 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXAlloyOriginsCore.h | +| | +| RGBController for HyperX Alloy Origins Core keyboard | +| | +| Volodymyr Nazarchuk (Vavooon) 28 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXAlloyOriginsCoreController.h" + +#define HYPERX_ALLOY_ORIGINS_CORE_ANSI 0x09 +#define HYPERX_ALLOY_ORIGINS_CORE_ABNT2 0x10 + +class RGBController_HyperXAlloyOriginsCore : public RGBController +{ +public: + RGBController_HyperXAlloyOriginsCore(HyperXAlloyOriginsCoreController* controller_ptr); + ~RGBController_HyperXAlloyOriginsCore(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + HyperXAlloyOriginsCoreController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; + unsigned int variant; +}; diff --git a/Controllers/HyperXKeyboardController/HyperXEve1800Controller/HyperXEve1800Controller.cpp b/Controllers/HyperXKeyboardController/HyperXEve1800Controller/HyperXEve1800Controller.cpp new file mode 100644 index 0000000..c838448 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXEve1800Controller/HyperXEve1800Controller.cpp @@ -0,0 +1,121 @@ +/*---------------------------------------------------------*\ +| HyperXEve1800Controller.cpp | +| | +| Driver for HyperX Eve 1800 keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXEve1800Controller.h" +#include "StringUtils.h" + +HyperXEve1800Controller::HyperXEve1800Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXEve1800Controller::~HyperXEve1800Controller() +{ + hid_close(dev); +} + +std::string HyperXEve1800Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string HyperXEve1800Controller::GetNameString() +{ + return(name); +} + +std::string HyperXEve1800Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void HyperXEve1800Controller::SetBrightness(unsigned int brightness) +{ + unsigned char buf[65]; + + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = 0x40; + buf[0x01] = 0x01; + buf[0x02] = 0x00; + buf[0x03] = 0x00; + buf[0x04] = brightness & 0xFF; + + hid_write(dev, buf, 65); +} + +void HyperXEve1800Controller::SetLEDsDirect(std::vector colors) +{ + SendDirectInitialization(); + + if(colors.empty()) + { + RGBColor temp = 0x00000000; + SendDirectColorPacket(&temp, 1); + } + else + { + SendDirectColorPacket(&colors[0], (unsigned int)colors.size()); + } +} + +void HyperXEve1800Controller::SendDirectInitialization() +{ + unsigned char buf[65]; + + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = 0x44; + buf[0x01] = 0x01; + buf[0x02] = 0x04; + buf[0x03] = 0x00; + + hid_write(dev, buf, 65); +} + +void HyperXEve1800Controller::SendDirectColorPacket(RGBColor* color_data, unsigned int color_count) +{ + unsigned char buf[65]; + + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = 0x44; + buf[0x01] = 0x02; + buf[0x02] = 0x00; + buf[0x03] = 0x00; + + /*-----------------------------------------------------*\ + | The Eve 1800 exposes 10 lighting zones, but this | + | report format can carry up to 20 RGB triplets. | + \*-----------------------------------------------------*/ + if(color_count > 20) + { + color_count = 20; + } + + for(unsigned int color_idx = 0; color_idx < color_count; color_idx++) + { + buf[4 + (color_idx * 3)] = RGBGetRValue(color_data[color_idx]); + buf[4 + (color_idx * 3) + 1] = RGBGetGValue(color_data[color_idx]); + buf[4 + (color_idx * 3) + 2] = RGBGetBValue(color_data[color_idx]); + } + + hid_write(dev, buf, 65); +} diff --git a/Controllers/HyperXKeyboardController/HyperXEve1800Controller/HyperXEve1800Controller.h b/Controllers/HyperXKeyboardController/HyperXEve1800Controller/HyperXEve1800Controller.h new file mode 100644 index 0000000..0c204bd --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXEve1800Controller/HyperXEve1800Controller.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| HyperXEve1800Controller.h | +| | +| Driver for HyperX Eve 1800 keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class HyperXEve1800Controller +{ +public: + HyperXEve1800Controller(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXEve1800Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetBrightness(unsigned int brightness); + void SetLEDsDirect(std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendDirectInitialization(); + void SendDirectColorPacket(RGBColor* color_data, unsigned int color_count); +}; diff --git a/Controllers/HyperXKeyboardController/HyperXEve1800Controller/RGBController_HyperXEve1800.cpp b/Controllers/HyperXKeyboardController/HyperXEve1800Controller/RGBController_HyperXEve1800.cpp new file mode 100644 index 0000000..538f2c0 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXEve1800Controller/RGBController_HyperXEve1800.cpp @@ -0,0 +1,127 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXEve1800.cpp | +| | +| RGBController for HyperX Eve 1800 keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HyperXEve1800.h" + +using namespace std::chrono_literals; + +#define HYPERX_EVE_1800_BRIGHTNESS_MIN 0x00 +#define HYPERX_EVE_1800_BRIGHTNESS_MAX 0xFF +#define HYPERX_EVE_1800_ZONE_COUNT 10 + +/**------------------------------------------------------------------*\ + @name HyperX Eve 1800 + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXEve1800 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXEve1800::RGBController_HyperXEve1800(HyperXEve1800Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_KEYBOARD; + description = "HyperX Eve 1800 Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = HYPERX_EVE_1800_BRIGHTNESS_MIN; + Direct.brightness_max = HYPERX_EVE_1800_BRIGHTNESS_MAX; + Direct.brightness = HYPERX_EVE_1800_BRIGHTNESS_MAX; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXEve1800::KeepaliveThread, this); +} + +RGBController_HyperXEve1800::~RGBController_HyperXEve1800() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_HyperXEve1800::SetupZones() +{ + zone new_zone; + new_zone.name = "Keyboard"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = HYPERX_EVE_1800_ZONE_COUNT; + new_zone.leds_max = HYPERX_EVE_1800_ZONE_COUNT; + new_zone.leds_count = HYPERX_EVE_1800_ZONE_COUNT; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led new_led; + new_led.name = "Zone "; + new_led.name.append(std::to_string(led_idx + 1)); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_HyperXEve1800::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_HyperXEve1800::DeviceUpdateLEDs() +{ + controller->SetLEDsDirect(colors); + last_update_time = std::chrono::steady_clock::now(); +} + +void RGBController_HyperXEve1800::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXEve1800::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXEve1800::DeviceUpdateMode() +{ + controller->SetBrightness(modes[active_mode].brightness); + DeviceUpdateLEDs(); +} + +void RGBController_HyperXEve1800::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(10ms); + } +} diff --git a/Controllers/HyperXKeyboardController/HyperXEve1800Controller/RGBController_HyperXEve1800.h b/Controllers/HyperXKeyboardController/HyperXEve1800Controller/RGBController_HyperXEve1800.h new file mode 100644 index 0000000..cdee0b0 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXEve1800Controller/RGBController_HyperXEve1800.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXEve1800.h | +| | +| RGBController for HyperX Eve 1800 keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXEve1800Controller.h" + +class RGBController_HyperXEve1800 : public RGBController +{ +public: + RGBController_HyperXEve1800(HyperXEve1800Controller* controller_ptr); + ~RGBController_HyperXEve1800(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + HyperXEve1800Controller* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXKeyboardController/HyperXKeyboardControllerDetect.cpp b/Controllers/HyperXKeyboardController/HyperXKeyboardControllerDetect.cpp new file mode 100644 index 0000000..a7bb17a --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXKeyboardControllerDetect.cpp @@ -0,0 +1,202 @@ +/*---------------------------------------------------------*\ +| HyperXKeyboardControllerDetect.cpp | +| | +| Driver for HyperX keyboards | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "HyperXAlloyEliteController.h" +#include "HyperXAlloyElite2Controller.h" +#include "HyperXAlloyFPSController.h" +#include "HyperXAlloyOriginsController.h" +#include "HyperXAlloyOriginsCoreController.h" +#include "HyperXAlloyOrigins60and65Controller.h" +#include "HyperXEve1800Controller.h" +#include "HyperXOrigins2_65Controller.h" +#include "RGBController_HyperXAlloyElite.h" +#include "RGBController_HyperXAlloyElite2.h" +#include "RGBController_HyperXAlloyFPS.h" +#include "RGBController_HyperXAlloyOrigins.h" +#include "RGBController_HyperXAlloyOriginsCore.h" +#include "RGBController_HyperXAlloyOrigins60and65.h" +#include "RGBController_HyperXEve1800.h" +#include "RGBController_HyperXOrigins2_65.h" + +/*-----------------------------------------------------*\ +| HyperX keyboard vendor and product IDs | +\*-----------------------------------------------------*/ +#define HYPERX_KEYBOARD_VID 0x0951 + +#define HYPERX_ALLOY_ELITE_PID 0x16BE +#define HYPERX_ALLOY_ELITE_2_PID 0x1711 +#define HYPERX_ALLOY_FPS_RGB_PID 0x16DC +#define HYPERX_ALLOY_ORIGINS_PID 0x16E5 +#define HYPERX_ALLOY_ORIGINS_CORE_PID 0x16E6 +#define HYPERX_ALLOY_ORIGINS_60_PID 0x1734 + +/*-----------------------------------------------------*\ +| HyperX keyboard vendor and product IDs (HP) | +\*-----------------------------------------------------*/ +#define HP_KEYBOARD_VID 0x03F0 + +#define HYPERX_ALLOY_ELITE_2_HP_PID 0x058F +#define HYPERX_ALLOY_ORIGINS_60_HP_PID 0x0C8E +#define HYPERX_ALLOY_ORIGINS_65_HP_PID 0x038F +#define HYPERX_ALLOY_ORIGINS_CORE_HP_PID 0x098F +#define HYPERX_ALLOY_ORIGINS_HP_PID 0x0591 +#define HYPERX_EVE_1800_HP_PID 0x08C2 +#define HYPERX_ORIGINS_2_65_HP_PID 0x0CC2 + +AlloyOrigins60and65MappingLayoutType GetAlloyOrigins60and65MappingLayoutType(int pid) +{ + switch(pid) + { + case HYPERX_ALLOY_ORIGINS_60_PID: + case HYPERX_ALLOY_ORIGINS_60_HP_PID: + return ALLOY_ORIGINS_60_LAYOUT; + + case HYPERX_ALLOY_ORIGINS_65_HP_PID: + return ALLOY_ORIGINS_65_LAYOUT; + + default: + return ALLOY_ORIGINS_60_LAYOUT; + } +} + +void DetectHyperXAlloyElite(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXAlloyEliteController* controller = new HyperXAlloyEliteController(dev, info->path, name); + RGBController_HyperXAlloyElite* rgb_controller = new RGBController_HyperXAlloyElite(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectHyperXAlloyElite2(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXAlloyElite2Controller* controller = new HyperXAlloyElite2Controller(dev, info->path, name); + RGBController_HyperXAlloyElite2* rgb_controller = new RGBController_HyperXAlloyElite2(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectHyperXAlloyFPS(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXAlloyFPSController* controller = new HyperXAlloyFPSController(dev, info->path, name); + RGBController_HyperXAlloyFPS* rgb_controller = new RGBController_HyperXAlloyFPS(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectHyperXAlloyOrigins(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXAlloyOriginsController* controller = new HyperXAlloyOriginsController(dev, info->path, name); + RGBController_HyperXAlloyOrigins* rgb_controller = new RGBController_HyperXAlloyOrigins(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectHyperXAlloyOriginsCore(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXAlloyOriginsCoreController* controller = new HyperXAlloyOriginsCoreController(dev, info, name); + RGBController_HyperXAlloyOriginsCore* rgb_controller = new RGBController_HyperXAlloyOriginsCore(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectHyperXAlloyOrigins60and65(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXAlloyOrigins60and65Controller* controller = new HyperXAlloyOrigins60and65Controller(dev, info->path, name); + AlloyOrigins60and65MappingLayoutType layout = GetAlloyOrigins60and65MappingLayoutType(info->product_id); + RGBController_HyperXAlloyOrigins60and65* rgb_controller = new RGBController_HyperXAlloyOrigins60and65(controller, layout); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectHyperXOrigins2_65(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXOrigins2_65Controller* controller = new HyperXOrigins2_65Controller(dev, info->path, name); + RGBController_HyperXOrigins2_65* rgb_controller = new RGBController_HyperXOrigins2_65(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectHyperXEve1800(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXEve1800Controller* controller = new HyperXEve1800Controller(dev, info->path, name); + RGBController_HyperXEve1800* rgb_controller = new RGBController_HyperXEve1800(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite RGB", DetectHyperXAlloyElite, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_PID, 2, 0xFF01); +REGISTER_HID_DETECTOR_IP("HyperX Alloy FPS RGB", DetectHyperXAlloyFPS, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_FPS_RGB_PID, 2, 0xFF01); +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core", DetectHyperXAlloyOriginsCore, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_PID, 2); + +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins Core (HP)", DetectHyperXAlloyOriginsCore, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_CORE_HP_PID, 2); + +REGISTER_HID_DETECTOR_I("HyperX Origins 2 65 (HP)", DetectHyperXOrigins2_65, HP_KEYBOARD_VID, HYPERX_ORIGINS_2_65_HP_PID, 3); +REGISTER_HID_DETECTOR_I("HyperX Eve 1800 (HP)", DetectHyperXEve1800, HP_KEYBOARD_VID, HYPERX_EVE_1800_HP_PID, 2); + +#ifdef _WIN32 +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 3); +REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite 2", DetectHyperXAlloyElite2, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_PID, 3, 0xFF90); +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60", DetectHyperXAlloyOrigins60and65, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_PID, 3); + +REGISTER_HID_DETECTOR_IP("HyperX Alloy Elite 2 (HP)", DetectHyperXAlloyElite2, HP_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_HP_PID, 3, 0xFF90); +REGISTER_HID_DETECTOR_IP("HyperX Alloy Origins (HP)", DetectHyperXAlloyOrigins, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_HP_PID, 3, 0xFF90); +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_HP_PID, 3); +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 65 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_65_HP_PID, 3); +#else +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins", DetectHyperXAlloyOrigins, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_PID, 0); +REGISTER_HID_DETECTOR_I("HyperX Alloy Elite 2", DetectHyperXAlloyElite2, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_PID, 0); +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60", DetectHyperXAlloyOrigins60and65, HYPERX_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_PID, 0); + +REGISTER_HID_DETECTOR_I("HyperX Alloy Elite 2 (HP)", DetectHyperXAlloyElite2, HP_KEYBOARD_VID, HYPERX_ALLOY_ELITE_2_HP_PID, 0); +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins (HP)", DetectHyperXAlloyOrigins, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_HP_PID, 0); +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 60 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_60_HP_PID, 0); +REGISTER_HID_DETECTOR_I("HyperX Alloy Origins 65 (HP)", DetectHyperXAlloyOrigins60and65, HP_KEYBOARD_VID, HYPERX_ALLOY_ORIGINS_65_HP_PID, 0); +#endif diff --git a/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.cpp b/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.cpp new file mode 100644 index 0000000..87b3a15 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.cpp @@ -0,0 +1,151 @@ +/*---------------------------------------------------------*\ +| HyperXOrigins2_65Controller.cpp | +| | +| Driver for HyperX Origins 2 65 keyboard | +| | +| Ricardo Amorim 28 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXOrigins2_65Controller.h" +#include "StringUtils.h" + +HyperXOrigins2_65Controller::HyperXOrigins2_65Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXOrigins2_65Controller::~HyperXOrigins2_65Controller() +{ + hid_close(dev); +} + +std::string HyperXOrigins2_65Controller::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string HyperXOrigins2_65Controller::GetNameString() +{ + return(name); +} + +std::string HyperXOrigins2_65Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return (""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void HyperXOrigins2_65Controller::SetLEDsDirect(std::vector colors) +{ + /*-----------------------------------------------------*\ + | Set up variables to track progress of color transmit | + | Do this after inserting blanks | + \*-----------------------------------------------------*/ + int colors_to_send = (int)colors.size(); + int colors_sent = 0; + + SendDirectInitialization(); + + for(int pkt_idx = 0; pkt_idx < 4; pkt_idx++) + { + if(colors_to_send > 20) + { + SendDirectColorPacket(&colors[colors_sent], 20, pkt_idx); + colors_sent += 20; + colors_to_send -= 20; + } + else if(colors_to_send > 0) + { + SendDirectColorPacket(&colors[colors_sent], colors_to_send, pkt_idx); + colors_sent += colors_to_send; + colors_to_send -= colors_to_send; + } + else + { + RGBColor temp = 0x00000000; + SendDirectColorPacket(&temp, 1, pkt_idx); + } + } +} + +void HyperXOrigins2_65Controller::SendDirectInitialization() +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Initialization packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x44; + buf[0x01] = 0x01; + buf[0x02] = 0x04; + buf[0x03] = 0x00; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, buf, 65); +} + +void HyperXOrigins2_65Controller::SendDirectColorPacket + ( + RGBColor* color_data, + unsigned int color_count, + unsigned int seq + ) +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Initialization packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x44; + buf[0x01] = 0x02; + buf[0x02] = (unsigned char)seq; + buf[0x03] = 0x00; + + /*-----------------------------------------------------*\ + | The maximum number of colors per packet is 20 | + \*-----------------------------------------------------*/ + if(color_count > 20) + { + color_count = 20; + } + + /*-----------------------------------------------------*\ + | Copy in color data | + \*-----------------------------------------------------*/ + for(unsigned int color_idx = 0; color_idx < color_count; color_idx++) + { + buf[4 + (color_idx * 3)] = RGBGetRValue(color_data[color_idx]); + buf[4 + (color_idx * 3) + 1] = RGBGetGValue(color_data[color_idx]); + buf[4 + (color_idx * 3) + 2] = RGBGetBValue(color_data[color_idx]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, buf, 65); +} diff --git a/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.h b/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.h new file mode 100644 index 0000000..db74d38 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| HyperXOrigins2_65Controller.h | +| | +| Driver for HyperX Origins 2 65 keyboard | +| | +| Ricardo Amorim 28 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class HyperXOrigins2_65Controller +{ +public: + HyperXOrigins2_65Controller(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXOrigins2_65Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetLEDsDirect(std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendDirectInitialization(); + void SendDirectColorPacket + ( + RGBColor* color_data, + unsigned int color_count, + unsigned int seq + ); +}; + diff --git a/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/RGBController_HyperXOrigins2_65.cpp b/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/RGBController_HyperXOrigins2_65.cpp new file mode 100644 index 0000000..307bc9c --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/RGBController_HyperXOrigins2_65.cpp @@ -0,0 +1,257 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXOrigins2_65.cpp | +| | +| RGBController for HyperX Origins 2 65 keyboard | +| | +| Ricardo Amorim 28 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_HyperXOrigins2_65.h" + +using namespace std::chrono_literals; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[5][15] = + { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, + { 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, NA, 29 }, + { 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44 }, + { 45, 61, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 59 }, + { 60, 62, 63, 65, NA, NA, 66, NA, NA, 68, 69, 70, 71, 72, 73 } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 74, +}; + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_DELETE, + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_UNUSED, + KEY_EN_HOME, + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_POUND, + KEY_EN_ISO_ENTER, + KEY_EN_PAGE_UP, + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + KEY_EN_UNUSED, + KEY_EN_PAGE_DOWN, + KEY_EN_LEFT_CONTROL, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_UNUSED, + "Left Space", + KEY_EN_SPACE, + "Right Space", + KEY_EN_UNUSED, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW +}; + +/**------------------------------------------------------------------*\ + @name HyperX Origins 2 65 + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXOrigins2_65 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXOrigins2_65::RGBController_HyperXOrigins2_65(HyperXOrigins2_65Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXOrigins2_65::KeepaliveThread, this); +} + +RGBController_HyperXOrigins2_65::~RGBController_HyperXOrigins2_65() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_HyperXOrigins2_65::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 5; + new_zone.matrix_map->width = 15; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_HyperXOrigins2_65::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXOrigins2_65::DeviceUpdateLEDs() +{ + controller->SetLEDsDirect(colors); +} + +void RGBController_HyperXOrigins2_65::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXOrigins2_65::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXOrigins2_65::DeviceUpdateMode() +{ + +} + +void RGBController_HyperXOrigins2_65::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(10ms);; + } +} + diff --git a/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/RGBController_HyperXOrigins2_65.h b/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/RGBController_HyperXOrigins2_65.h new file mode 100644 index 0000000..1fadf03 --- /dev/null +++ b/Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/RGBController_HyperXOrigins2_65.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXOrigins2_65.h | +| | +| RGBController for HyperX Origins 2 65 keyboard | +| | +| Ricardo Amorim 28 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXOrigins2_65Controller.h" + +class RGBController_HyperXOrigins2_65 : public RGBController +{ +public: + RGBController_HyperXOrigins2_65(HyperXOrigins2_65Controller* controller_ptr); + ~RGBController_HyperXOrigins2_65(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + HyperXOrigins2_65Controller* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; + diff --git a/Controllers/HyperXMicrophoneController/HyperXMicrophoneController.cpp b/Controllers/HyperXMicrophoneController/HyperXMicrophoneController.cpp new file mode 100644 index 0000000..ad4bd1e --- /dev/null +++ b/Controllers/HyperXMicrophoneController/HyperXMicrophoneController.cpp @@ -0,0 +1,206 @@ +/*---------------------------------------------------------*\ +| HyperXMicrophoneController.cpp | +| | +| Driver for HyperX microphone | +| | +| Matt Silva (thesilvanator) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXMicrophoneController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +HyperXMicrophoneController::HyperXMicrophoneController(hidapi_wrapper hid_wrapper, hid_device* dev_handle, std::string path, std::string dev_name) +{ + wrapper = hid_wrapper; + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXMicrophoneController::~HyperXMicrophoneController() +{ + lock.lock(); + + if(dev) + { + wrapper.hid_close(dev); + } + + lock.unlock(); +} + +std::string HyperXMicrophoneController::GetDeviceLocation() +{ + return(location); +} + +std::string HyperXMicrophoneController::GetNameString() +{ + return(name); +} + +std::string HyperXMicrophoneController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = wrapper.hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void HyperXMicrophoneController::SaveColors(std::vector colors, unsigned int num_frames) +{ + unsigned int num_color_packets = 0; + unsigned int frame = 0; + unsigned char color[HYPERX_QUADCAST_S_PACKET_SIZE] = {0}; + + num_color_packets = num_frames/8; + if(num_frames % 8) + { + num_color_packets++; + } + + lock.lock(); + + /*---------------------------------------------------------*\ + | Start Save Transaction | + | 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | + \*---------------------------------------------------------*/ + SendToRegister(0x53, (uint8_t)num_color_packets, 0); + + while(frame < num_frames) + { + memset(color, 0, HYPERX_QUADCAST_S_PACKET_SIZE); + + unsigned int i = 0; + while(i < 8 && frame < num_frames) + { + int index = HYPERX_QUADCAST_S_FRAME_SIZE * i; + RGBColor top = colors[frame*2]; + RGBColor bot = colors[frame*2 + 1]; + + color[index + 1] = 0x81; + color[index + 2] = RGBGetRValue(top); + color[index + 3] = RGBGetGValue(top); + color[index + 4] = RGBGetBValue(top); + color[index + 5] = 0x81; + color[index + 6] = RGBGetRValue(bot); + color[index + 7] = RGBGetGValue(bot); + color[index + 8] = RGBGetBValue(bot); + + i++; + frame++; + } + + std::this_thread::sleep_for(15ms); + wrapper.hid_send_feature_report(dev, color, HYPERX_QUADCAST_S_PACKET_SIZE); + } + + /*---------------------------------------------------------*\ + | Post Save Transaction | + | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | + \*---------------------------------------------------------*/ + SendToRegister(0x02, 0, 0); + + /*---------------------------------------------------------*\ + | Stop Save Transaction | + | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | + \*---------------------------------------------------------*/ + SendToRegister(0x23, 1, 0); + + SendEOT((uint8_t)num_frames); + + /*---------------------------------------------------------*\ + | Post Save Transaction | + | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | + \*---------------------------------------------------------*/ + SendToRegister(0x02, 0, 0); + + lock.unlock(); + + /*---------------------------------------------------------*\ + | Likes to have one temporary direct packet after the save | + | for some reason | + \*---------------------------------------------------------*/ + SendDirect(colors); +} + +void HyperXMicrophoneController::SendDirect(std::vector colors) +{ + /*---------------------------------------------------------*\ + | Verify colors size | + \*---------------------------------------------------------*/ + if(colors.size() != 2) + { + return; + } + + RGBColor c1 = colors[0]; + RGBColor c2 = colors[1]; + uint8_t buffer[HYPERX_QUADCAST_S_PACKET_SIZE]; + + /*---------------------------------------------------------*\ + | Colour packet | + \*---------------------------------------------------------*/ + memset(buffer, 0, HYPERX_QUADCAST_S_PACKET_SIZE); + + buffer[0x01] = 0x81; + buffer[0x02] = RGBGetRValue(c1); + buffer[0x03] = RGBGetGValue(c1); + buffer[0x04] = RGBGetBValue(c1); + buffer[0x05] = 0x81; + buffer[0x06] = RGBGetRValue(c2); + buffer[0x07] = RGBGetGValue(c2); + buffer[0x08] = RGBGetBValue(c2); + + lock.lock(); + + wrapper.hid_send_feature_report(dev, buffer, HYPERX_QUADCAST_S_PACKET_SIZE); + std::this_thread::sleep_for(15ms); + + SendToRegister(0xF2, 0, 1); + + lock.unlock(); +} + +void HyperXMicrophoneController::SendEOT(uint8_t frame_count) +{ + uint8_t buffer[HYPERX_QUADCAST_S_PACKET_SIZE]; + + memset(buffer, 0, HYPERX_QUADCAST_S_PACKET_SIZE); + + buffer[0x01] = 0x08; + buffer[0x3C] = 0x28; + buffer[0x3D] = frame_count; + buffer[0x3E] = 0x00; + buffer[0x3F] = 0xAA; + buffer[0x40] = 0x55; + + wrapper.hid_send_feature_report(dev, buffer, HYPERX_QUADCAST_S_PACKET_SIZE); + std::this_thread::sleep_for(15ms); +} + +void HyperXMicrophoneController::SendToRegister(uint8_t reg, uint8_t param1, uint8_t param2) +{ + uint8_t buffer[HYPERX_QUADCAST_S_PACKET_SIZE]; + + memset(buffer, 0, HYPERX_QUADCAST_S_PACKET_SIZE); + + buffer[0x01] = 0x04; + buffer[0x02] = reg; // 0xF2 Apply, 0x53 Save + buffer[0x08] = param1; + buffer[0x09] = param2; + + wrapper.hid_send_feature_report(dev, buffer, HYPERX_QUADCAST_S_PACKET_SIZE); + std::this_thread::sleep_for(15ms); +} diff --git a/Controllers/HyperXMicrophoneController/HyperXMicrophoneController.h b/Controllers/HyperXMicrophoneController/HyperXMicrophoneController.h new file mode 100644 index 0000000..5643d9d --- /dev/null +++ b/Controllers/HyperXMicrophoneController/HyperXMicrophoneController.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| HyperXMicrophoneController.cpp | +| | +| Driver for HyperX microphone | +| | +| Matt Silva (thesilvanator) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "hidapi_wrapper.h" +#include "RGBController.h" + +#define HYPERX_QUADCAST_S_PACKET_SIZE 64 + 1 +#define HYPERX_QUADCAST_S_FRAME_SIZE 8 + +class HyperXMicrophoneController +{ +public: + HyperXMicrophoneController(hidapi_wrapper hid_wrapper, hid_device* dev, std::string path, std::string dev_name); + ~HyperXMicrophoneController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendDirect(std::vector color_data); + void SaveColors(std::vector colors, unsigned int num_frames); + +private: + hidapi_wrapper wrapper; + hid_device* dev; + std::string location; + std::mutex lock; + std::string name; + + void SendEOT(uint8_t frame_count); + void SendToRegister(uint8_t reg, uint8_t param1, uint8_t param2); +}; diff --git a/Controllers/HyperXMicrophoneController/HyperXMicrophoneControllerDetect.cpp b/Controllers/HyperXMicrophoneController/HyperXMicrophoneControllerDetect.cpp new file mode 100644 index 0000000..1deb099 --- /dev/null +++ b/Controllers/HyperXMicrophoneController/HyperXMicrophoneControllerDetect.cpp @@ -0,0 +1,54 @@ +/*---------------------------------------------------------*\ +| HyperXMicrophoneControllerDetect.cpp | +| | +| Detector for HyperX microphone | +| | +| Matt Silva (thesilvanator) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "HyperXMicrophoneController.h" +#include "RGBController_HyperXMicrophone.h" +#include "hidapi_wrapper.h" + +/*-----------------------------------------------------*\ +| HyperX microphone vendor and product IDs | +\*-----------------------------------------------------*/ +#define HYPERX_VID 0x0951 +#define HYPERX_HP_VID 0x03F0 + +#define HYPERX_QS_PID 0x171F + +#define HYPERX_QS_PID_HP_1 0x0F8B +#define HYPERX_QS_PID_HP_2 0x068C +#define HYPERX_QS_PID_HP_3 0x0294 +#define HYPERX_QS_PID_HP_4 0x028C +#define HYPERX_QS_PID_HP_5 0x048C +#define HYPERX_QS_PID_HP_6 0x0D8B + +#define HYPERX_DUOCAST_PID 0x098C + +void DetectHyperXMicrophoneControllers(hidapi_wrapper wrapper, hid_device_info* info, const std::string& name) +{ + hid_device* dev = wrapper.hid_open_path(info->path); + + if(dev) + { + HyperXMicrophoneController* controller = new HyperXMicrophoneController(wrapper, dev, info->path, name); + RGBController_HyperXMicrophone *rgb_controller = new RGBController_HyperXMicrophone(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_WRAPPED_DETECTOR_I("HyperX Quadcast S", DetectHyperXMicrophoneControllers, HYPERX_VID, HYPERX_QS_PID, 0);//, 0xFF90, 0xFF00); +REGISTER_HID_WRAPPED_DETECTOR_I("HyperX Quadcast S", DetectHyperXMicrophoneControllers, HYPERX_HP_VID, HYPERX_QS_PID_HP_1, 0);//, 0xFF90, 0xFF00); +REGISTER_HID_WRAPPED_DETECTOR_I("HyperX Quadcast S", DetectHyperXMicrophoneControllers, HYPERX_HP_VID, HYPERX_QS_PID_HP_2, 0);//, 0xFF90, 0xFF00); +REGISTER_HID_WRAPPED_DETECTOR_I("HyperX Quadcast S", DetectHyperXMicrophoneControllers, HYPERX_HP_VID, HYPERX_QS_PID_HP_3, 0);//, 0xFF90, 0xFF00); +REGISTER_HID_WRAPPED_DETECTOR_I("HyperX Quadcast S", DetectHyperXMicrophoneControllers, HYPERX_HP_VID, HYPERX_QS_PID_HP_4, 0);//, 0xFF90, 0xFF00); +REGISTER_HID_WRAPPED_DETECTOR_I("HyperX Quadcast S", DetectHyperXMicrophoneControllers, HYPERX_HP_VID, HYPERX_QS_PID_HP_5, 0);//, 0xFF90, 0xFF00); +REGISTER_HID_WRAPPED_DETECTOR_I("HyperX Quadcast S", DetectHyperXMicrophoneControllers, HYPERX_HP_VID, HYPERX_QS_PID_HP_6, 0);//, 0xFF90, 0xFF00); +REGISTER_HID_WRAPPED_DETECTOR_I("HyperX DuoCast", DetectHyperXMicrophoneControllers, HYPERX_HP_VID, HYPERX_DUOCAST_PID, 0);//, 0xFF90, 0xFF00); diff --git a/Controllers/HyperXMicrophoneController/RGBController_HyperXMicrophone.cpp b/Controllers/HyperXMicrophoneController/RGBController_HyperXMicrophone.cpp new file mode 100644 index 0000000..dca79e2 --- /dev/null +++ b/Controllers/HyperXMicrophoneController/RGBController_HyperXMicrophone.cpp @@ -0,0 +1,155 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXMicrophone.cpp | +| | +| RGBController for HyperX microphone | +| | +| Matt Silva (thesilvanator) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +/**------------------------------------------------------------------*\ + @name HyperX Quadcast S + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXMicrophoneControllers + @comment The HyperX Quadcast S has a manufacturer issue + with the interface it uses (0) for controlling its RGB. + HID requires that any HID interface have at least one + Interrupt IN endpoint; however, the HXQS does not, + even though its interface reports itelf as hid and + responds to hid requests. As such Linux doesn't bind + to the usbhid driver and it goes undetected by + hidapi-hidraw. Windows does detect it as hid and hidapi + finds and interacts with it just fine. To work around + the Linux issue, hidapi-libusb is loaded dynamically + using dlopen/dlsym as hidapi using a libusb backend is + able to find the device and interact with it. This + requires that you have support for dlopen/dlsym on your + Linux platform as well as hidapi-libusb (and libusb) + libraries installed in the standard dynamic library + path. + + The controller for this device has a wrapper for hidapi + functions so that the controller can be the same across + all platforms, but call the correct underlying functions + that are defined in the detector under an #ifdef + for that platform. + + Additionally, hidapi-libusb has an error that causes + hid_close() to hang on this device, see: + https://github.com/libusb/hidapi/issues/456 + This will be fixed on newer versions of hidapi-libusb, + but until then, OpenRGB will hang/crash if you try to + rescan devices once a HXQS has been detected during + program session. +\*-------------------------------------------------------------------*/ + +#include "RGBController_HyperXMicrophone.h" +#include + +using namespace std::chrono_literals; + +RGBController_HyperXMicrophone::RGBController_HyperXMicrophone(HyperXMicrophoneController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_MICROPHONE; + description = "HyperX Microphone Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXMicrophone::KeepaliveThread, this); +}; + +RGBController_HyperXMicrophone::~RGBController_HyperXMicrophone() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_HyperXMicrophone::SetupZones() +{ + led Top; + Top.name = "Top"; + Top.value = 0; + + led Bot; + Bot.name = "Bottom"; + Bot.value = 1; + + leds.push_back(Top); + leds.push_back(Bot); + + zone Mic; + Mic.name = "Microphone"; + Mic.type = ZONE_TYPE_SINGLE; + Mic.leds_min = 2; + Mic.leds_max = 2; + Mic.leds_count = 2; + Mic.matrix_map = nullptr; + + zones.push_back(Mic); + + SetupColors(); +} + +void RGBController_HyperXMicrophone::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXMicrophone::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + controller->SendDirect(colors); +} +void RGBController_HyperXMicrophone::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} +void RGBController_HyperXMicrophone::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} +void RGBController_HyperXMicrophone::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXMicrophone::DeviceSaveMode() +{ + LOG_DEBUG("[%s] Saving current direct colors to device", name.c_str()); + controller->SaveColors(colors, 1); +} + +void RGBController_HyperXMicrophone::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + std::this_thread::sleep_for(15ms); + } +} diff --git a/Controllers/HyperXMicrophoneController/RGBController_HyperXMicrophone.h b/Controllers/HyperXMicrophoneController/RGBController_HyperXMicrophone.h new file mode 100644 index 0000000..1a7a15a --- /dev/null +++ b/Controllers/HyperXMicrophoneController/RGBController_HyperXMicrophone.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXMicrophone.h | +| | +| RGBController for HyperX microphone | +| | +| Matt Silva (thesilvanator) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXMicrophoneController.h" + +class RGBController_HyperXMicrophone : public RGBController +{ +public: + RGBController_HyperXMicrophone(HyperXMicrophoneController* controller_ptr); + ~RGBController_HyperXMicrophone(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + + void KeepaliveThread(); + +private: + HyperXMicrophoneController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.cpp b/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.cpp new file mode 100644 index 0000000..20b7354 --- /dev/null +++ b/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.cpp @@ -0,0 +1,271 @@ +/*---------------------------------------------------------*\ +| HyperXMicrophoneV2Controller.cpp | +| | +| Driver for HyperX QuadCast 2 S Microphone | +| | +| Morgan Guimard (morg) | +| Logan Phillips (Eclipse) 23 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "HyperXMicrophoneV2Controller.h" +#include "StringUtils.h" +#include "LogManager.h" + +HyperXMicrophoneV2Controller::HyperXMicrophoneV2Controller(hid_device* dev_handle, std::string path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + errors = 0; + last_error_time = std::chrono::steady_clock::now(); + pause_until = std::chrono::steady_clock::now(); +} + +HyperXMicrophoneV2Controller::~HyperXMicrophoneV2Controller() +{ + if(dev) + { + hid_close(dev); + } +} + +std::string HyperXMicrophoneV2Controller::GetDeviceLocation() +{ + return(location); +} + +std::string HyperXMicrophoneV2Controller::GetNameString() +{ + return(name); +} + +std::string HyperXMicrophoneV2Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +bool HyperXMicrophoneV2Controller::ShouldPauseUpdates() +{ + return std::chrono::steady_clock::now() < pause_until; +} + +void HyperXMicrophoneV2Controller::FlushInputBuffer() +{ + uint8_t discard[HYPERX_QUADCAST_2S_PACKET_SIZE]; + int flushed_count = 0; + + /*---------------------------------------------------------*\ + | Read and discard all pending responses in the buffer | + \*---------------------------------------------------------*/ + while(hid_read_timeout(dev, discard, HYPERX_QUADCAST_2S_PACKET_SIZE, 10) > 0) + { + flushed_count++; + } + + if(flushed_count > 0) + { + LOG_DEBUG("[%s] Flushed %d stale response(s) from input buffer", name.c_str(), flushed_count); + } +} + +void HyperXMicrophoneV2Controller::TrackCommunicationError() +{ + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + long long time_since_last_error = std::chrono::duration_cast(now - last_error_time).count(); + + if(time_since_last_error < 10) + { + errors++; + if(errors >= 5) + { + LOG_WARNING("[%s] Multiple communication errors detected (%d). Flushing input buffer to clear stale responses.", name.c_str(), errors); + FlushInputBuffer(); + } + if(errors >= 10) + { + LOG_ERROR("[%s] Multiple consecutive communication errors detected. Another program (such as HyperX NGENUITY) may be controlling this device. Pausing updates for 5 seconds.", name.c_str()); + pause_until = std::chrono::steady_clock::now() + std::chrono::seconds(5); + errors = 0; + } + } + else + { + errors = 1; + } + + last_error_time = now; +} + +bool HyperXMicrophoneV2Controller::WaitForResponse(const uint8_t* sent_packet, int timeout_ms) +{ + uint8_t response[HYPERX_QUADCAST_2S_PACKET_SIZE]; + memset(response, 0, HYPERX_QUADCAST_2S_PACKET_SIZE); + + int bytes_read = hid_read_timeout(dev, response, HYPERX_QUADCAST_2S_PACKET_SIZE, timeout_ms); + + if(bytes_read <= 0) + { + LOG_WARNING("[%s] No response received from device (timeout: %d ms)", name.c_str(), timeout_ms); + TrackCommunicationError(); + return false; + } + + /*---------------------------------------------------------*\ + | Verify the response echoes back the command bytes | + | Response bytes 14-15 should match sent bytes 0-1 | + \*---------------------------------------------------------*/ + if(response[14] == sent_packet[0] && response[15] == sent_packet[1]) + { + return true; + } + + /*---------------------------------------------------------*\ + | Log validation failure with full response payload | + \*---------------------------------------------------------*/ + std::stringstream response_hex; + for(int i = 0; i < bytes_read; i++) + { + response_hex << std::hex << std::setw(2) << std::setfill('0') << (int)response[i]; + } + + LOG_WARNING("[%s] Invalid response from device. Expected echo of bytes [0x%02X 0x%02X] at positions 14-15, but got [0x%02X 0x%02X]. Full response: %s", + name.c_str(), sent_packet[0], sent_packet[1], response[14], response[15], response_hex.str().c_str()); + + TrackCommunicationError(); + return false; +} + +void HyperXMicrophoneV2Controller::SendColorPackets(std::vector colors, uint8_t command_byte) +{ + uint8_t buf[HYPERX_QUADCAST_2S_PACKET_SIZE]; + unsigned int total_leds_sent = 0; + + for(unsigned int packet = 0; packet < 6; packet++) + { + memset(buf, 0, HYPERX_QUADCAST_2S_PACKET_SIZE); + + buf[0] = HYPERX_QUADCAST_2S_REPORT_ID; + buf[1] = command_byte; + buf[2] = packet; + + unsigned int c = 0; + + while(c < HYPERX_QUADCAST_2S_LEDS_PER_PACKET && total_leds_sent < HYPERX_QUADCAST_2S_TOTAL_LEDS) + { + buf[4 + (3 * c)] = RGBGetRValue(colors[total_leds_sent]); + buf[5 + (3 * c)] = RGBGetGValue(colors[total_leds_sent]); + buf[6 + (3 * c)] = RGBGetBValue(colors[total_leds_sent]); + + c++; + total_leds_sent++; + } + + hid_write(dev, buf, HYPERX_QUADCAST_2S_PACKET_SIZE); + WaitForResponse(buf); + } +} + +void HyperXMicrophoneV2Controller::SendDirect(std::vector colors) +{ + lock.lock(); + + /*---------------------------------------------------------*\ + | Skip sending if we're in pause mode | + \*---------------------------------------------------------*/ + if(ShouldPauseUpdates()) + { + lock.unlock(); + return; + } + + uint8_t buf[HYPERX_QUADCAST_2S_PACKET_SIZE]; + + /*---------------------------------------------------------*\ + | Send header packet for direct mode | + \*---------------------------------------------------------*/ + memset(buf, 0, HYPERX_QUADCAST_2S_PACKET_SIZE); + buf[0] = HYPERX_QUADCAST_2S_REPORT_ID; + buf[1] = 0x01; + buf[2] = 0x06; + hid_write(dev, buf, HYPERX_QUADCAST_2S_PACKET_SIZE); + WaitForResponse(buf); + + /*---------------------------------------------------------*\ + | Send color data packets | + \*---------------------------------------------------------*/ + SendColorPackets(colors, 0x02); + + lock.unlock(); +} + +void HyperXMicrophoneV2Controller::SaveColors(std::vector colors) +{ + lock.lock(); + + /*---------------------------------------------------------*\ + | Skip sending if we're in pause mode | + \*---------------------------------------------------------*/ + if(ShouldPauseUpdates()) + { + lock.unlock(); + return; + } + + uint8_t buf[HYPERX_QUADCAST_2S_PACKET_SIZE]; + + /*---------------------------------------------------------*\ + | Initiate save to device | + \*---------------------------------------------------------*/ + memset(buf, 0, HYPERX_QUADCAST_2S_PACKET_SIZE); + buf[0] = HYPERX_QUADCAST_2S_REPORT_ID; + buf[1] = 0x03; + buf[2] = 0x01; + buf[3] = 0x06; + hid_write(dev, buf, HYPERX_QUADCAST_2S_PACKET_SIZE); + WaitForResponse(buf); + + /*---------------------------------------------------------*\ + | Send 6 color data packets | + \*---------------------------------------------------------*/ + SendColorPackets(colors, 0x04); + + /*---------------------------------------------------------*\ + | Send "Framerate" packet | + | If someone ever wanted to try and replicate the effects, | + | apparently this is the packet to try and change. | + | I believe currently this is setting a "static" frame | + \*---------------------------------------------------------*/ + memset(buf, 0, HYPERX_QUADCAST_2S_PACKET_SIZE); + buf[0] = 0x42; + buf[1] = 0x02; + buf[5] = 0xE8; + buf[6] = 0x03; + hid_write(dev, buf, HYPERX_QUADCAST_2S_PACKET_SIZE); + WaitForResponse(buf); + + /*---------------------------------------------------------*\ + | Send final packet | + \*---------------------------------------------------------*/ + memset(buf, 0, HYPERX_QUADCAST_2S_PACKET_SIZE); + buf[0] = 0x40; + buf[1] = 0x01; + buf[4] = 0xFF; + hid_write(dev, buf, HYPERX_QUADCAST_2S_PACKET_SIZE); + WaitForResponse(buf); + + lock.unlock(); +} diff --git a/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.h b/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.h new file mode 100644 index 0000000..684468c --- /dev/null +++ b/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.h @@ -0,0 +1,55 @@ +/*---------------------------------------------------------*\ +| HyperXMicrophoneV2Controller.h | +| | +| Driver for HyperX QuadCast 2 S Microphone | +| | +| Morgan Guimard (morg) | +| Logan Phillips (Eclipse) 23 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define HYPERX_QUADCAST_2S_PACKET_SIZE 64 +#define HYPERX_QUADCAST_2S_REPORT_ID 0x44 +#define HYPERX_QUADCAST_2S_LEDS_PER_PACKET 20 +#define HYPERX_QUADCAST_2S_MATRIX_WIDTH 12 +#define HYPERX_QUADCAST_2S_MATRIX_HEIGHT 9 +#define HYPERX_QUADCAST_2S_TOTAL_LEDS 108 + +class HyperXMicrophoneV2Controller +{ +public: + HyperXMicrophoneV2Controller(hid_device* dev, std::string path, std::string dev_name); + ~HyperXMicrophoneV2Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendDirect(std::vector color_data); + void SaveColors(std::vector color_data); + + bool ShouldPauseUpdates(); + +private: + hid_device* dev; + std::string location; + std::string name; + std::mutex lock; + + bool WaitForResponse(const uint8_t* sent_packet, int timeout_ms = 2000); + void SendColorPackets(std::vector colors, uint8_t command_byte); + void TrackCommunicationError(); + void FlushInputBuffer(); + + unsigned int errors; + std::chrono::steady_clock::time_point last_error_time; + std::chrono::steady_clock::time_point pause_until; +}; diff --git a/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2ControllerDetect.cpp b/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2ControllerDetect.cpp new file mode 100644 index 0000000..f79b78b --- /dev/null +++ b/Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2ControllerDetect.cpp @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| HyperXMicrophoneV2ControllerDetect.cpp | +| | +| Detector for HyperX QuadCast 2 S Microphone | +| | +| Morgan Guimard (morg) | +| Logan Phillips (Eclipse) 23 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "HyperXMicrophoneV2Controller.h" +#include "RGBController_HyperXMicrophoneV2.h" + +/*-----------------------------------------------------*\ +| HyperX microphone vendor and product IDs | +\*-----------------------------------------------------*/ +#define HYPERX_HP_VID 0x03F0 +#define HYPERX_QUADCAST_2S_PID 0x02B5 + +void DetectHyperXMicrophoneV2Controllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXMicrophoneV2Controller* controller = new HyperXMicrophoneV2Controller(dev, info->path, name); + RGBController_HyperXMicrophoneV2 *rgb_controller = new RGBController_HyperXMicrophoneV2(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("HyperX QuadCast 2 S", DetectHyperXMicrophoneV2Controllers, HYPERX_HP_VID, HYPERX_QUADCAST_2S_PID, 1, 0xFF13, 0xFF00); diff --git a/Controllers/HyperXMicrophoneV2Controller/RGBController_HyperXMicrophoneV2.cpp b/Controllers/HyperXMicrophoneV2Controller/RGBController_HyperXMicrophoneV2.cpp new file mode 100644 index 0000000..a244d52 --- /dev/null +++ b/Controllers/HyperXMicrophoneV2Controller/RGBController_HyperXMicrophoneV2.cpp @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXMicrophoneV2.cpp | +| | +| RGBController for HyperX QuadCast 2 S Microphone | +| | +| Morgan Guimard (morg) | +| Logan Phillips (Eclipse) 23 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +/**------------------------------------------------------------------*\ + @name HyperX Quadcast 2S + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXMicrophoneV2Controllers + @comment +\*-------------------------------------------------------------------*/ + +#include "RGBController_HyperXMicrophoneV2.h" +#include + +using namespace std::chrono_literals; + +RGBController_HyperXMicrophoneV2::RGBController_HyperXMicrophoneV2(HyperXMicrophoneV2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_MICROPHONE; + description = "HyperX Microphone Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.colors_min = HYPERX_QUADCAST_2S_TOTAL_LEDS; + Direct.colors_max = HYPERX_QUADCAST_2S_TOTAL_LEDS; + + modes.push_back(Direct); + + SetupZones(); + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXMicrophoneV2::KeepaliveThread, this); +}; + +RGBController_HyperXMicrophoneV2::~RGBController_HyperXMicrophoneV2() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_HyperXMicrophoneV2::SetupZones() +{ + zone Mic; + + Mic.name = "Microphone"; + Mic.type = ZONE_TYPE_MATRIX; + Mic.leds_min = HYPERX_QUADCAST_2S_TOTAL_LEDS; + Mic.leds_max = HYPERX_QUADCAST_2S_TOTAL_LEDS; + Mic.leds_count = HYPERX_QUADCAST_2S_TOTAL_LEDS; + Mic.matrix_map = new matrix_map_type; + Mic.matrix_map->width = HYPERX_QUADCAST_2S_MATRIX_WIDTH; + Mic.matrix_map->height = HYPERX_QUADCAST_2S_MATRIX_HEIGHT; + Mic.matrix_map->map = new unsigned int[HYPERX_QUADCAST_2S_TOTAL_LEDS]; + + unsigned int led_mapping[HYPERX_QUADCAST_2S_TOTAL_LEDS] = + { + /* front */ /* rear */ + 26, 27, 44, 45, 62, 63, 80, 81, 98, 99, 8, 9, + 25, 28, 43, 46, 61, 64, 79, 82, 97, 100, 7, 10, + 24, 29, 42, 47, 60, 65, 78, 83, 96, 101, 6, 11, + 23, 30, 41, 48, 59, 66, 77, 84, 95, 102, 5, 12, + 22, 31, 40, 49, 58, 67, 76, 85, 94, 103, 4, 13, + 21, 32, 39, 50, 57, 68, 75, 86, 93, 104, 3, 14, + 20, 33, 38, 51, 56, 69, 74, 87, 92, 105, 2, 15, + 19, 34, 37, 52, 55, 70, 73, 88, 91, 106, 1, 16, + 18, 35, 36, 53, 54, 71, 72, 89, 90, 107, 0, 17 + }; + + for(unsigned int i = 0; i < HYPERX_QUADCAST_2S_TOTAL_LEDS; i ++) + { + led l; + l.name = "LED " + std::to_string(i); + l.value = led_mapping[i]; + leds.push_back(l); + + Mic.matrix_map->map[i] = led_mapping[i]; + } + + zones.push_back(Mic); + + SetupColors(); +} + +void RGBController_HyperXMicrophoneV2::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXMicrophoneV2::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + controller->SendDirect(colors); +} +void RGBController_HyperXMicrophoneV2::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} +void RGBController_HyperXMicrophoneV2::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} +void RGBController_HyperXMicrophoneV2::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXMicrophoneV2::DeviceSaveMode() +{ + LOG_DEBUG("[%s] Saving current direct colors to device", name.c_str()); + controller->SaveColors(colors); +} + +void RGBController_HyperXMicrophoneV2::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(!controller->ShouldPauseUpdates() && (std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(1000)) + { + UpdateLEDs(); + } + std::this_thread::sleep_for(250ms); + } +} diff --git a/Controllers/HyperXMicrophoneV2Controller/RGBController_HyperXMicrophoneV2.h b/Controllers/HyperXMicrophoneV2Controller/RGBController_HyperXMicrophoneV2.h new file mode 100644 index 0000000..1a5a838 --- /dev/null +++ b/Controllers/HyperXMicrophoneV2Controller/RGBController_HyperXMicrophoneV2.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXMicrophoneV2.h | +| | +| RGBController for HyperX QuadCast 2 S Microphone | +| | +| Morgan Guimard (morg) | +| Logan Phillips (Eclipse) 23 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXMicrophoneV2Controller.h" + +class RGBController_HyperXMicrophoneV2 : public RGBController +{ +public: + RGBController_HyperXMicrophoneV2(HyperXMicrophoneV2Controller* controller_ptr); + ~RGBController_HyperXMicrophoneV2(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + + void KeepaliveThread(); + +private: + HyperXMicrophoneV2Controller* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXMouseController/HyperXMouseControllerDetect.cpp b/Controllers/HyperXMouseController/HyperXMouseControllerDetect.cpp new file mode 100644 index 0000000..25d5850 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXMouseControllerDetect.cpp @@ -0,0 +1,124 @@ +/*---------------------------------------------------------*\ +| HyperXMouseControllerDetect.cpp | +| | +| Detector for HyperX mouse | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "HyperXPulsefireFPSProController.h" +#include "HyperXPulsefireSurgeController.h" +#include "HyperXPulsefireDartController.h" +#include "HyperXPulsefireRaidController.h" +#include "RGBController_HyperXPulsefireFPSPro.h" +#include "RGBController_HyperXPulsefireHaste.h" +#include "RGBController_HyperXPulsefireSurge.h" +#include "RGBController_HyperXPulsefireDart.h" +#include "RGBController_HyperXPulsefireRaid.h" + +/*-----------------------------------------------------*\ +| HyperX mouse vendor IDs | +\*-----------------------------------------------------*/ +#define HYPERX_VID 0x0951 //Kingston Technology +#define HYPERX_VID_2 0x03F0 //HP, Hewlett-Packard Company +#define HYPERX_PULSEFIRE_SURGE_PID 0x16D3 +#define HYPERX_PULSEFIRE_SURGE_PID_2 0x0490 +#define HYPERX_PULSEFIRE_FPS_PRO_PID 0x16D7 +#define HYPERX_PULSEFIRE_CORE_PID 0x16DE +#define HYPERX_PULSEFIRE_CORE_PID_2 0x0D8F +#define HYPERX_PULSEFIRE_DART_WIRELESS_PID 0x16E1 +#define HYPERX_PULSEFIRE_DART_WIRELESS_PID_2 0x068E +#define HYPERX_PULSEFIRE_DART_WIRED_PID 0x16E2 +#define HYPERX_PULSEFIRE_DART_WIRED_PID_2 0x088E +#define HYPERX_PULSEFIRE_RAID_PID 0x16E4 +#define HYPERX_PULSEFIRE_HASTE_PID 0x1727 +#define HYPERX_PULSEFIRE_HASTE_PID_2 0x0F8F + +void DetectHyperXPulsefireSurgeControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXPulsefireSurgeController* controller = new HyperXPulsefireSurgeController(dev, info->path, name); + RGBController_HyperXPulsefireSurge* rgb_controller = new RGBController_HyperXPulsefireSurge(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectHyperXPulsefireSurgeControllers() */ + +void DetectHyperXPulsefireFPSProControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXPulsefireFPSProController* controller = new HyperXPulsefireFPSProController(dev, info->path, name); + RGBController_HyperXPulsefireFPSPro* rgb_controller = new RGBController_HyperXPulsefireFPSPro(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectHyperXPulsefireFPSProControllers() */ + +void DetectHyperXPulsefireHasteControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXPulsefireHasteController* controller = new HyperXPulsefireHasteController(dev, info->path, name); + RGBController_HyperXPulsefireHaste* rgb_controller = new RGBController_HyperXPulsefireHaste(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectHyperXPulsefireFPSProControllers() */ + +void DetectHyperXPulsefireDartControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXPulsefireDartController* controller = new HyperXPulsefireDartController(dev, info->path, name); + RGBController_HyperXPulsefireDart* rgb_controller = new RGBController_HyperXPulsefireDart(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectHyperXPulsefireDartControllers() */ + +void DetectHyperXPulsefireRaidControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + HyperXPulsefireRaidController* controller = new HyperXPulsefireRaidController(dev, *info, name); + RGBController_HyperXPulsefireRaid* rgb_controller = new RGBController_HyperXPulsefireRaid(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectHyperXPulsefireRaidControllers() */ + +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire Surge", DetectHyperXPulsefireSurgeControllers, HYPERX_VID, HYPERX_PULSEFIRE_SURGE_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire Surge (HP)", DetectHyperXPulsefireSurgeControllers, HYPERX_VID_2, HYPERX_PULSEFIRE_SURGE_PID_2, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire FPS Pro", DetectHyperXPulsefireFPSProControllers, HYPERX_VID, HYPERX_PULSEFIRE_FPS_PRO_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire Core", DetectHyperXPulsefireFPSProControllers, HYPERX_VID, HYPERX_PULSEFIRE_CORE_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire Core (HP)", DetectHyperXPulsefireFPSProControllers, HYPERX_VID_2, HYPERX_PULSEFIRE_CORE_PID_2, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire Dart (Wireless)", DetectHyperXPulsefireDartControllers, HYPERX_VID, HYPERX_PULSEFIRE_DART_WIRELESS_PID, 2, 0xFF00); +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire Dart (Wireless)", DetectHyperXPulsefireDartControllers, HYPERX_VID_2, HYPERX_PULSEFIRE_DART_WIRELESS_PID_2, 2, 0xFF00); +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire Dart (Wired)", DetectHyperXPulsefireDartControllers, HYPERX_VID, HYPERX_PULSEFIRE_DART_WIRED_PID, 1, 0xFF13); +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire Dart (Wired)", DetectHyperXPulsefireDartControllers, HYPERX_VID_2, HYPERX_PULSEFIRE_DART_WIRED_PID_2, 1, 0xFF13); + +REGISTER_HID_DETECTOR_IPU("HyperX Pulsefire Raid", DetectHyperXPulsefireRaidControllers, HYPERX_VID, HYPERX_PULSEFIRE_RAID_PID, 1, 0xFF01, 0x01); + +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire Haste", DetectHyperXPulsefireHasteControllers, HYPERX_VID_2, HYPERX_PULSEFIRE_HASTE_PID_2, 3, 0xFF90); + +#ifdef _WIN32 +REGISTER_HID_DETECTOR_IP("HyperX Pulsefire Haste", DetectHyperXPulsefireHasteControllers, HYPERX_VID, HYPERX_PULSEFIRE_HASTE_PID, 3, 0xFF90); +#else +REGISTER_HID_DETECTOR_IPU("HyperX Pulsefire Haste", DetectHyperXPulsefireHasteControllers, HYPERX_VID_2, HYPERX_PULSEFIRE_HASTE_PID_2, 0, 0x0001, 0x01); +REGISTER_HID_DETECTOR_PU("HyperX Pulsefire Haste", DetectHyperXPulsefireHasteControllers, HYPERX_VID, HYPERX_PULSEFIRE_HASTE_PID, 1, 2); +#endif diff --git a/Controllers/HyperXMouseController/HyperXPulsefireDartController/HyperXPulsefireDartController.cpp b/Controllers/HyperXMouseController/HyperXPulsefireDartController/HyperXPulsefireDartController.cpp new file mode 100644 index 0000000..ba68ac0 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireDartController/HyperXPulsefireDartController.cpp @@ -0,0 +1,122 @@ +/*---------------------------------------------------------*\ +| HyperXPulsefireDartController.cpp | +| | +| Driver for HyperX Pulsefire Dart | +| | +| Santeri Pikarinen (santeri3700) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXPulsefireDartController.h" +#include "StringUtils.h" + +HyperXPulsefireDartController::HyperXPulsefireDartController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXPulsefireDartController::~HyperXPulsefireDartController() +{ + hid_close(dev); +} + +std::string HyperXPulsefireDartController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string HyperXPulsefireDartController::GetNameString() +{ + return(name); +} + +std::string HyperXPulsefireDartController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void HyperXPulsefireDartController::SendDirect + ( + RGBColor color, + int led, + int mode, + int brightness, + int speed + ) +{ + unsigned char buf[HYPERX_PULSEFIRE_DART_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Mode packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + buf[0x01] = HYPERX_PULSEFIRE_DART_PACKET_ID_DIRECT; + buf[0x02] = led; + buf[0x03] = mode; + buf[0x04] = 0x08; // 8 bytes after buffer index 0x04 + + buf[0x05] = RGBGetRValue(color); + buf[0x06] = RGBGetGValue(color); + buf[0x07] = RGBGetBValue(color); + + buf[0x08] = RGBGetRValue(color); + buf[0x09] = RGBGetGValue(color); + buf[0x0a] = RGBGetBValue(color); + + buf[0x0b] = brightness; + buf[0x0c] = speed; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)buf, sizeof(buf)); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void HyperXPulsefireDartController::Save() +{ + /*-----------------------------------------------------*\ + | Save current settings to the on-board memory | + \*-----------------------------------------------------*/ + unsigned char buf[HYPERX_PULSEFIRE_DART_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Save packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + buf[0x01] = 0xde; + buf[0x02] = 0xff; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)buf, sizeof(buf)); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} diff --git a/Controllers/HyperXMouseController/HyperXPulsefireDartController/HyperXPulsefireDartController.h b/Controllers/HyperXMouseController/HyperXPulsefireDartController/HyperXPulsefireDartController.h new file mode 100644 index 0000000..c95cc55 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireDartController/HyperXPulsefireDartController.h @@ -0,0 +1,66 @@ +/*---------------------------------------------------------*\ +| HyperXPulsefireDartController.h | +| | +| Driver for HyperX Pulsefire Dart | +| | +| Santeri Pikarinen (santeri3700) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + HYPERX_PULSEFIRE_DART_PACKET_ID_DIRECT = 0xd2, /* Direct control packet */ + HYPERX_PULSEFIRE_DART_PACKET_SIZE = 65, /* Report ID padding + 64 byte payload */ + + HYPERX_PULSEFIRE_DART_MODE_STATIC = 0x00, /* Static color mode */ + HYPERX_PULSEFIRE_DART_MODE_CYCLE = 0x12, /* Spectrum cycle mode */ + HYPERX_PULSEFIRE_DART_MODE_BREATHING = 0x20, /* Single color breathing mode */ + HYPERX_PULSEFIRE_DART_MODE_REACTIVE = 0x30, /* Reactive/Trigger fade mode */ + + HYPERX_PULSEFIRE_DART_SPEED_MIN = 0x64, + HYPERX_PULSEFIRE_DART_SPEED_MAX = 0x00, + HYPERX_PULSEFIRE_DART_SPEED_MED = 0x32, + HYPERX_PULSEFIRE_DART_SPEED_NONE = 0x00, /* For static color mode */ + + HYPERX_PULSEFIRE_DART_BRIGHTNESS_MIN = 0x00, + HYPERX_PULSEFIRE_DART_BRIGHTNESS_MAX = 0x64, + + HYPERX_PULSEFIRE_DART_LED_LOGO = 0x00, + HYPERX_PULSEFIRE_DART_LED_SCROLL = 0x10, + HYPERX_PULSEFIRE_DART_LED_ALL = 0x20 +}; + +class HyperXPulsefireDartController +{ +public: + HyperXPulsefireDartController(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXPulsefireDartController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendDirect + ( + RGBColor color_data, + int led, + int mode, + int brightness, + int speed + ); + + void Save(); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/HyperXMouseController/HyperXPulsefireDartController/RGBController_HyperXPulsefireDart.cpp b/Controllers/HyperXMouseController/HyperXPulsefireDartController/RGBController_HyperXPulsefireDart.cpp new file mode 100644 index 0000000..2a434c3 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireDartController/RGBController_HyperXPulsefireDart.cpp @@ -0,0 +1,170 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXPulsefireDart.cpp | +| | +| RGBController for HyperX Pulsefire Dart | +| | +| Santeri Pikarinen (santeri3700) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HyperXPulsefireDart.h" + +/**------------------------------------------------------------------*\ + @name HyperX Pulsefire Dart + @category Mouse + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectHyperXPulsefireDartControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXPulsefireDart::RGBController_HyperXPulsefireDart(HyperXPulsefireDartController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_MOUSE; + description = "HyperX Pulsefire Dart Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = HYPERX_PULSEFIRE_DART_MODE_STATIC; + Direct.speed = HYPERX_PULSEFIRE_DART_SPEED_NONE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MIN; + Direct.brightness_max = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MAX; + Direct.brightness = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MAX; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = HYPERX_PULSEFIRE_DART_MODE_BREATHING; + Breathing.speed = HYPERX_PULSEFIRE_DART_SPEED_MED; + Breathing.speed_min = HYPERX_PULSEFIRE_DART_SPEED_MIN; + Breathing.speed_max = HYPERX_PULSEFIRE_DART_SPEED_MAX; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MIN; + Breathing.brightness_max = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MAX; + Breathing.brightness = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MAX; + modes.push_back(Breathing); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = HYPERX_PULSEFIRE_DART_MODE_CYCLE; + SpectrumCycle.speed = HYPERX_PULSEFIRE_DART_SPEED_MED; + SpectrumCycle.speed_min = HYPERX_PULSEFIRE_DART_SPEED_MIN; + SpectrumCycle.speed_max = HYPERX_PULSEFIRE_DART_SPEED_MAX; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MIN; + SpectrumCycle.brightness_max = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MAX; + SpectrumCycle.brightness = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MAX; + modes.push_back(SpectrumCycle); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = HYPERX_PULSEFIRE_DART_MODE_REACTIVE; + Reactive.speed = HYPERX_PULSEFIRE_DART_SPEED_MED; + Reactive.speed_min = HYPERX_PULSEFIRE_DART_SPEED_MIN; + Reactive.speed_max = HYPERX_PULSEFIRE_DART_SPEED_MAX; + Reactive.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Reactive.color_mode = MODE_COLORS_PER_LED; + Reactive.brightness_min = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MIN; + Reactive.brightness_max = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MAX; + Reactive.brightness = HYPERX_PULSEFIRE_DART_BRIGHTNESS_MAX; + modes.push_back(Reactive); + + SetupZones(); +} + +RGBController_HyperXPulsefireDart::~RGBController_HyperXPulsefireDart() +{ + +} + +void RGBController_HyperXPulsefireDart::SetupZones() +{ + zone scroll_zone; + scroll_zone.name = "Scroll Wheel"; + scroll_zone.type = ZONE_TYPE_SINGLE; + scroll_zone.leds_min = 1; + scroll_zone.leds_max = 1; + scroll_zone.leds_count = 1; + scroll_zone.matrix_map = NULL; + zones.push_back(scroll_zone); + + led scroll_led; + scroll_led.name = "Scroll Wheel"; + scroll_led.value = HYPERX_PULSEFIRE_DART_LED_SCROLL; + leds.push_back(scroll_led); + + zone logo_zone; + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + led logo_led; + logo_led.name = "Logo"; + logo_led.value = HYPERX_PULSEFIRE_DART_LED_LOGO; + leds.push_back(logo_led); + + SetupColors(); +} + +void RGBController_HyperXPulsefireDart::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_HyperXPulsefireDart::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_HyperXPulsefireDart::UpdateZoneLEDs(int zone) +{ + UpdateSingleLED(zone); +} + +void RGBController_HyperXPulsefireDart::UpdateSingleLED(int led) +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SendDirect(colors[led], leds[led].value, modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed); + } + else + { + controller->SendDirect(colors[led], HYPERX_PULSEFIRE_DART_LED_ALL, modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed); + } +} + +void RGBController_HyperXPulsefireDart::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SendDirect(colors[0], HYPERX_PULSEFIRE_DART_LED_SCROLL, modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed); + controller->SendDirect(colors[1], HYPERX_PULSEFIRE_DART_LED_LOGO, modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed); + } + else + { + controller->SendDirect(colors[0], HYPERX_PULSEFIRE_DART_LED_ALL, modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed); + } +} + +void RGBController_HyperXPulsefireDart::DeviceSaveMode() +{ + controller->Save(); +} diff --git a/Controllers/HyperXMouseController/HyperXPulsefireDartController/RGBController_HyperXPulsefireDart.h b/Controllers/HyperXMouseController/HyperXPulsefireDartController/RGBController_HyperXPulsefireDart.h new file mode 100644 index 0000000..e60423c --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireDartController/RGBController_HyperXPulsefireDart.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXPulsefireDart.h | +| | +| RGBController for HyperX Pulsefire Dart | +| | +| Santeri Pikarinen (santeri3700) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXPulsefireDartController.h" + +class RGBController_HyperXPulsefireDart : public RGBController +{ +public: + RGBController_HyperXPulsefireDart(HyperXPulsefireDartController* controller_ptr); + ~RGBController_HyperXPulsefireDart(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + HyperXPulsefireDartController* controller; +}; diff --git a/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.cpp b/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.cpp new file mode 100644 index 0000000..59faebf --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.cpp @@ -0,0 +1,83 @@ +/*---------------------------------------------------------*\ +| HyperXPulsefireFPSProController.cpp | +| | +| Driver for HyperX Pulsefire FPS Pro | +| | +| Adam Honse (CalcProgrammer1) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXPulsefireFPSProController.h" +#include "StringUtils.h" + +HyperXPulsefireFPSProController::HyperXPulsefireFPSProController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXPulsefireFPSProController::~HyperXPulsefireFPSProController() +{ + hid_close(dev); +} + +std::string HyperXPulsefireFPSProController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string HyperXPulsefireFPSProController::GetNameString() +{ + return(name); +} + +std::string HyperXPulsefireFPSProController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void HyperXPulsefireFPSProController::SendDirect + ( + RGBColor* color_data + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Mode packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = HYPERX_PULSEFIRE_FPS_PRO_PACKET_ID_DIRECT; + + buf[0x02] = RGBGetRValue(color_data[0]); + buf[0x03] = RGBGetGValue(color_data[0]); + buf[0x04] = RGBGetBValue(color_data[0]); + + buf[0x08] = 0xA0; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} diff --git a/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.h b/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.h new file mode 100644 index 0000000..68b0c44 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| HyperXPulsefireFPSProController.h | +| | +| Driver for HyperX Pulsefire FPS Pro | +| | +| Adam Honse (CalcProgrammer1) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + HYPERX_PULSEFIRE_FPS_PRO_PACKET_ID_DIRECT = 0x0A, /* Direct control packet */ +}; + +class HyperXPulsefireFPSProController +{ +public: + HyperXPulsefireFPSProController(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXPulsefireFPSProController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendDirect + ( + RGBColor* color_data + ); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/RGBController_HyperXPulsefireFPSPro.cpp b/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/RGBController_HyperXPulsefireFPSPro.cpp new file mode 100644 index 0000000..2170149 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/RGBController_HyperXPulsefireFPSPro.cpp @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXPulsefireFPSPro.cpp | +| | +| RGBController for HyperX Pulsefire FPS Pro | +| | +| Adam Honse (CalcProgrammer1) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HyperXPulsefireFPSPro.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name HyperX Pulsefire FPS + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXPulsefireFPSProControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXPulsefireFPSPro::RGBController_HyperXPulsefireFPSPro(HyperXPulsefireFPSProController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_MOUSE; + description = "HyperX Pulsefire FPS Pro Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + /*-----------------------------------------------------*\ + | The Corsair Lighting Node Pro requires a packet within| + | 20 seconds of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 5s | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXPulsefireFPSPro::KeepaliveThread, this); +}; + +RGBController_HyperXPulsefireFPSPro::~RGBController_HyperXPulsefireFPSPro() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_HyperXPulsefireFPSPro::SetupZones() +{ + zone logo; + logo.name = "Logo"; + logo.type = ZONE_TYPE_SINGLE; + logo.leds_min = 1; + logo.leds_max = 1; + logo.leds_count = 1; + logo.matrix_map = NULL; + zones.push_back(logo); + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + + new_led.name = zones[zone_idx].name; + + if(zones[zone_idx].leds_count > 1) + { + new_led.name.append(" LED "); + new_led.name.append(std::to_string(led_idx + 1)); + } + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_HyperXPulsefireFPSPro::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXPulsefireFPSPro::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + if(active_mode == 0) + { + controller->SendDirect(&colors[0]); + } + else + { + } + +} + +void RGBController_HyperXPulsefireFPSPro::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXPulsefireFPSPro::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXPulsefireFPSPro::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXPulsefireFPSPro::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(10ms); + } +} diff --git a/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/RGBController_HyperXPulsefireFPSPro.h b/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/RGBController_HyperXPulsefireFPSPro.h new file mode 100644 index 0000000..b12dfae --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireFPSProController/RGBController_HyperXPulsefireFPSPro.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXPulsefireFPSPro.h | +| | +| RGBController for HyperX Pulsefire FPS Pro | +| | +| Adam Honse (CalcProgrammer1) 26 Dec 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXPulsefireFPSProController.h" + +class RGBController_HyperXPulsefireFPSPro : public RGBController +{ +public: + RGBController_HyperXPulsefireFPSPro(HyperXPulsefireFPSProController* controller_ptr); + ~RGBController_HyperXPulsefireFPSPro(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + HyperXPulsefireFPSProController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.cpp b/Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.cpp new file mode 100644 index 0000000..e31e68d --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.cpp @@ -0,0 +1,116 @@ +/*---------------------------------------------------------*\ +| HyperXPulsefireHasteController.cpp | +| | +| Driver for HyperX Pulsefire Haste | +| | +| Adam Honse (CalcProgrammer1) 19 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXPulsefireHasteController.h" +#include "StringUtils.h" + +HyperXPulsefireHasteController::HyperXPulsefireHasteController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXPulsefireHasteController::~HyperXPulsefireHasteController() +{ + hid_close(dev); +} + +std::string HyperXPulsefireHasteController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string HyperXPulsefireHasteController::GetNameString() +{ + return(name); +} + +std::string HyperXPulsefireHasteController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void HyperXPulsefireHasteController::SendDirect + ( + RGBColor* color_data + ) +{ + SendDirectSetup(); + SendDirectColor(color_data); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void HyperXPulsefireHasteController::SendDirectSetup() +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Mode Setup packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + buf[0x01] = HYPERX_PULSEFIRE_HASTE_PACKET_ID_SETUP; + buf[0x02] = 0xF2; + + buf[0x08] = 0x02; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 65); +} + +void HyperXPulsefireHasteController::SendDirectColor + ( + RGBColor* color_data + ) +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Mode packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + buf[0x01] = HYPERX_PULSEFIRE_HASTE_PACKET_ID_COLOR; + + buf[0x02] = RGBGetRValue(color_data[0]); + buf[0x03] = RGBGetGValue(color_data[0]); + buf[0x04] = RGBGetBValue(color_data[0]); + + buf[0x08] = 0x02; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 65); +} diff --git a/Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.h b/Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.h new file mode 100644 index 0000000..cecc042 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.h @@ -0,0 +1,49 @@ +/*---------------------------------------------------------*\ +| HyperXPulsefireHasteController.h | +| | +| Driver for HyperX Pulsefire Haste | +| | +| Adam Honse (CalcProgrammer1) 19 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + HYPERX_PULSEFIRE_HASTE_PACKET_ID_SETUP = 0x04, /* Direct setup packet */ + HYPERX_PULSEFIRE_HASTE_PACKET_ID_COLOR = 0x81, /* Direct color packet */ +}; + +class HyperXPulsefireHasteController +{ +public: + HyperXPulsefireHasteController(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXPulsefireHasteController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendDirect + ( + RGBColor* color_data + ); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendDirectSetup(); + void SendDirectColor + ( + RGBColor* color_data + ); +}; diff --git a/Controllers/HyperXMouseController/HyperXPulsefireHasteController/RGBController_HyperXPulsefireHaste.cpp b/Controllers/HyperXMouseController/HyperXPulsefireHasteController/RGBController_HyperXPulsefireHaste.cpp new file mode 100644 index 0000000..8090db2 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireHasteController/RGBController_HyperXPulsefireHaste.cpp @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXPulsefireHaste.cpp | +| | +| RGBController for HyperX Pulsefire Haste | +| | +| Adam Honse (CalcProgrammer1) 19 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HyperXPulsefireHaste.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name HyperX Pulsefire Haste + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXPulsefireHasteControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXPulsefireHaste::RGBController_HyperXPulsefireHaste(HyperXPulsefireHasteController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_MOUSE; + description = "HyperX Pulsefire Haste Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + /*-----------------------------------------------------*\ + | The Corsair Lighting Node Pro requires a packet within| + | 20 seconds of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 5s | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXPulsefireHaste::KeepaliveThread, this); +}; + +RGBController_HyperXPulsefireHaste::~RGBController_HyperXPulsefireHaste() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_HyperXPulsefireHaste::SetupZones() +{ + zone logo; + logo.name = "Logo"; + logo.type = ZONE_TYPE_SINGLE; + logo.leds_min = 1; + logo.leds_max = 1; + logo.leds_count = 1; + logo.matrix_map = NULL; + zones.push_back(logo); + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + + new_led.name = zones[zone_idx].name; + + if(zones[zone_idx].leds_count > 1) + { + new_led.name.append(" LED "); + new_led.name.append(std::to_string(led_idx + 1)); + } + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_HyperXPulsefireHaste::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXPulsefireHaste::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + if(active_mode == 0) + { + controller->SendDirect(&colors[0]); + } + else + { + } + +} + +void RGBController_HyperXPulsefireHaste::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXPulsefireHaste::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXPulsefireHaste::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXPulsefireHaste::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(10ms); + } +} diff --git a/Controllers/HyperXMouseController/HyperXPulsefireHasteController/RGBController_HyperXPulsefireHaste.h b/Controllers/HyperXMouseController/HyperXPulsefireHasteController/RGBController_HyperXPulsefireHaste.h new file mode 100644 index 0000000..4d94b9f --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireHasteController/RGBController_HyperXPulsefireHaste.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXPulsefireHaste.h | +| | +| RGBController for HyperX Pulsefire Haste | +| | +| Adam Honse (CalcProgrammer1) 19 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXPulsefireHasteController.h" + +class RGBController_HyperXPulsefireHaste : public RGBController +{ +public: + RGBController_HyperXPulsefireHaste(HyperXPulsefireHasteController* controller_ptr); + ~RGBController_HyperXPulsefireHaste(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + HyperXPulsefireHasteController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXMouseController/HyperXPulsefireRaidController/HyperXPulsefireRaidController.cpp b/Controllers/HyperXMouseController/HyperXPulsefireRaidController/HyperXPulsefireRaidController.cpp new file mode 100644 index 0000000..5c82d89 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireRaidController/HyperXPulsefireRaidController.cpp @@ -0,0 +1,78 @@ +/*---------------------------------------------------------*\ +| HyperXPulsefireRaidController.cpp | +| | +| Driver for HyperX Pulsefire Raid | +| | +| Morgan Guimard (morg) 06 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXPulsefireRaidController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +HyperXPulsefireRaidController::HyperXPulsefireRaidController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +HyperXPulsefireRaidController::~HyperXPulsefireRaidController() +{ + hid_close(dev); +} + +std::string HyperXPulsefireRaidController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string HyperXPulsefireRaidController::GetNameString() +{ + return(name); +} + +std::string HyperXPulsefireRaidController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void HyperXPulsefireRaidController::SendColors(std::vector colors) +{ + unsigned char usb_buf[HYPERX_PULSFIRE_RAID_PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, HYPERX_PULSFIRE_RAID_PACKET_DATA_LENGTH); + + usb_buf[0] = HYPERX_PULSFIRE_RAID_REPORT_ID; + usb_buf[1] = HYPERX_PULSFIRE_RAID_DIRECT_MODE_START_PACKET; + + for(unsigned int i = 0; i < 2; i++) + { + usb_buf[3 * i + 2] = RGBGetRValue(colors[i]); + usb_buf[3 * i + 3] = RGBGetGValue(colors[i]); + usb_buf[3 * i + 4] = RGBGetBValue(colors[i]); + } + + usb_buf[8] = HYPERX_PULSFIRE_RAID_DIRECT_MODE_END_PACKET; + + Send(usb_buf); +} + + +void HyperXPulsefireRaidController::Send(unsigned char* packet) +{ + hid_send_feature_report(dev, packet, HYPERX_PULSFIRE_RAID_PACKET_DATA_LENGTH); + std::this_thread::sleep_for(10ms); +} diff --git a/Controllers/HyperXMouseController/HyperXPulsefireRaidController/HyperXPulsefireRaidController.h b/Controllers/HyperXMouseController/HyperXPulsefireRaidController/HyperXPulsefireRaidController.h new file mode 100644 index 0000000..343e14b --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireRaidController/HyperXPulsefireRaidController.h @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| HyperXPulsefireRaidController.h | +| | +| Driver for HyperX Pulsefire Raid | +| | +| Morgan Guimard (morg) 06 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define HYPERX_PULSFIRE_RAID_PACKET_DATA_LENGTH 264 +#define HYPERX_PULSFIRE_RAID_REPORT_ID 0x07 +#define HYPERX_PULSFIRE_RAID_LEDS_COUNT 2 +#define HYPERX_PULSFIRE_RAID_DIRECT_MODE_START_PACKET 0x0A +#define HYPERX_PULSFIRE_RAID_DIRECT_MODE_END_PACKET 0xA0 + +enum +{ + HYPERX_PULSFIRE_RAID_BRIGHTNESS_MIN = 0x00, + HYPERX_PULSFIRE_RAID_BRIGHTNESS_MAX = 0x64 +}; + +class HyperXPulsefireRaidController +{ +public: + HyperXPulsefireRaidController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~HyperXPulsefireRaidController(); + + std::string GetNameString(); + std::string GetSerialString(); + std::string GetDeviceLocation(); + + void SendColors(std::vector colors); + void SetBrightness(unsigned char brightness); + +protected: + hid_device* dev; + +private: + std::string location; + std::string name; + + void Send(unsigned char* packet); +}; diff --git a/Controllers/HyperXMouseController/HyperXPulsefireRaidController/RGBController_HyperXPulsefireRaid.cpp b/Controllers/HyperXMouseController/HyperXPulsefireRaidController/RGBController_HyperXPulsefireRaid.cpp new file mode 100644 index 0000000..abf9e61 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireRaidController/RGBController_HyperXPulsefireRaid.cpp @@ -0,0 +1,135 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXPulsefireRaid.cpp | +| | +| RGBController for HyperX Pulsefire Raid | +| | +| Morgan Guimard (morg) 06 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HyperXPulsefireRaid.h" + +/**------------------------------------------------------------------*\ + @name HyperX Pulsefire Raid + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXPulsefireRaidControllers + @comment +\*-------------------------------------------------------------------*/ + +using namespace std::chrono_literals; + +RGBController_HyperXPulsefireRaid::RGBController_HyperXPulsefireRaid(HyperXPulsefireRaidController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_MOUSE; + description = "HyperX Pulsefire Raid Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0x00; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness = HYPERX_PULSFIRE_RAID_BRIGHTNESS_MAX; + Direct.brightness_min = HYPERX_PULSFIRE_RAID_BRIGHTNESS_MIN; + Direct.brightness_max = HYPERX_PULSFIRE_RAID_BRIGHTNESS_MAX; + + modes.push_back(Direct); + + SetupZones(); + + /*-----------------------------------------------------*\ + | This devices requires a keepalive thread or it will | + | reset to default (flash) | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXPulsefireRaid::KeepaliveThread, this); +} + +RGBController_HyperXPulsefireRaid::~RGBController_HyperXPulsefireRaid() +{ + +} + +void RGBController_HyperXPulsefireRaid::SetupZones() +{ + std::string led_names[HYPERX_PULSFIRE_RAID_LEDS_COUNT] = + { + "Scroll Wheel", "Logo" + }; + + for(unsigned int i = 0; i < HYPERX_PULSFIRE_RAID_LEDS_COUNT; i++) + { + zone new_zone; + new_zone.name = led_names[i]; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + led new_led; + new_led.name = led_names[i]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_HyperXPulsefireRaid::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_HyperXPulsefireRaid::DeviceUpdateLEDs() +{ + UpdateSingleLED(0); +} + +void RGBController_HyperXPulsefireRaid::UpdateZoneLEDs(int zone) +{ + UpdateSingleLED(zone); +} + +void RGBController_HyperXPulsefireRaid::UpdateSingleLED(int /*led*/) +{ + last_update_time = std::chrono::steady_clock::now(); + controller->SendColors(colors); +} + +void RGBController_HyperXPulsefireRaid::DeviceUpdateMode() +{ + +} + +void RGBController_HyperXPulsefireRaid::DeviceSaveMode() +{ + +} + +void RGBController_HyperXPulsefireRaid::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > 1s) + { + UpdateLEDs(); + } + } + + std::this_thread::sleep_for(10ms); + } +} diff --git a/Controllers/HyperXMouseController/HyperXPulsefireRaidController/RGBController_HyperXPulsefireRaid.h b/Controllers/HyperXMouseController/HyperXPulsefireRaidController/RGBController_HyperXPulsefireRaid.h new file mode 100644 index 0000000..3947b05 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireRaidController/RGBController_HyperXPulsefireRaid.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXPulsefireRaid.h | +| | +| RGBController for HyperX Pulsefire Raid | +| | +| Morgan Guimard (morg) 06 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXPulsefireRaidController.h" + +class RGBController_HyperXPulsefireRaid : public RGBController +{ +public: + RGBController_HyperXPulsefireRaid(HyperXPulsefireRaidController* controller_ptr); + ~RGBController_HyperXPulsefireRaid(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + HyperXPulsefireRaidController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; + void KeepaliveThread(); +}; diff --git a/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.cpp b/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.cpp new file mode 100644 index 0000000..0e8c6e7 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.cpp @@ -0,0 +1,152 @@ +/*---------------------------------------------------------*\ +| HyperXPulsefireSurgeController.cpp | +| | +| Driver for HyperX Pulsefire Surge | +| | +| Adam Honse (CalcProgrammer1) 25 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXPulsefireSurgeController.h" +#include "StringUtils.h" + +HyperXPulsefireSurgeController::HyperXPulsefireSurgeController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXPulsefireSurgeController::~HyperXPulsefireSurgeController() +{ + hid_close(dev); +} + +std::string HyperXPulsefireSurgeController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string HyperXPulsefireSurgeController::GetNameString() +{ + return(name); +} + +std::string HyperXPulsefireSurgeController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void HyperXPulsefireSurgeController::SelectProfile + ( + unsigned char profile + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Select Profile packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = HYPERX_PULSEFIRE_SURGE_PACKET_ID_SELECT_PROFILE; + buf[0x02] = profile; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} + +void HyperXPulsefireSurgeController::SetProfileBrightness + ( + unsigned char profile, + unsigned char brightness + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Select Profile packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = HYPERX_PULSEFIRE_SURGE_PACKET_ID_SET_BRIGHTNESS; + buf[0x02] = profile; + buf[0x03] = 0x01; + buf[0x04] = 0x01; + buf[0x05] = 0x01; + buf[0x06] = brightness; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} + +void HyperXPulsefireSurgeController::SendDirect + ( + RGBColor* color_data + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Select Profile packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = HYPERX_PULSEFIRE_SURGE_PACKET_ID_DIRECT; + buf[0x03] = 0xA0; + + for(int red_idx = 0; red_idx < 32; red_idx++) + { + buf[0x08 + red_idx] = RGBGetRValue(color_data[red_idx]); + } + + for(int grn_idx = 0; grn_idx < 32; grn_idx++) + { + buf[0x28 + grn_idx] = RGBGetGValue(color_data[grn_idx]); + } + + for(int blu_idx = 0; blu_idx < 32; blu_idx++) + { + buf[0x48 + blu_idx] = RGBGetBValue(color_data[blu_idx]); + } + + buf[0x6C] = RGBGetRValue(color_data[32]); + buf[0x6D] = RGBGetGValue(color_data[32]); + buf[0x6E] = RGBGetBValue(color_data[32]); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} diff --git a/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.h b/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.h new file mode 100644 index 0000000..12e2fb2 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.h @@ -0,0 +1,65 @@ +/*---------------------------------------------------------*\ +| HyperXPulsefireSurgeController.h | +| | +| Driver for HyperX Pulsefire Surge | +| | +| Adam Honse (CalcProgrammer1) 25 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + HYPERX_PULSEFIRE_SURGE_PACKET_ID_SET_CONFIGURATION = 0x01, /* Set profile configuration packet */ + HYPERX_PULSEFIRE_SURGE_PACKET_ID_SET_BRIGHTNESS = 0x03, /* Set profile settings and brightness */ + HYPERX_PULSEFIRE_SURGE_PACKET_ID_SELECT_PROFILE = 0x07, /* Select profile */ + HYPERX_PULSEFIRE_SURGE_PACKET_ID_DIRECT = 0x14, /* Direct control packet */ +}; + +enum +{ + HYPERX_PULSEFIRE_SURGE_MODE_SOLID = 0x00, /* Solid color mode */ + HYPERX_PULSEFIRE_SURGE_MODE_CYCLE = 0x01, /* Spectrum cycle mode */ + HYPERX_PULSEFIRE_SURGE_MODE_BREATHING = 0x02, /* Breathing mode */ + HYPERX_PULSEFIRE_SURGE_MODE_WAVE = 0x03, /* Wave mode */ + HYPERX_PULSEFIRE_SURGE_MODE_TRIGGER = 0x04, /* Trigger mode */ +}; + +class HyperXPulsefireSurgeController +{ +public: + HyperXPulsefireSurgeController(hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXPulsefireSurgeController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SelectProfile + ( + unsigned char profile + ); + + void SetProfileBrightness + ( + unsigned char profile, + unsigned char brightness + ); + + void SendDirect + ( + RGBColor* color_data + ); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/RGBController_HyperXPulsefireSurge.cpp b/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/RGBController_HyperXPulsefireSurge.cpp new file mode 100644 index 0000000..e652e3d --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/RGBController_HyperXPulsefireSurge.cpp @@ -0,0 +1,156 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXPulsefireSurge.cpp | +| | +| RGBController for HyperX Pulsefire Surge | +| | +| Adam Honse (CalcProgrammer1) 25 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HyperXPulsefireSurge.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name HyperX Pulsefire Surge + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXPulsefireSurgeControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXPulsefireSurge::RGBController_HyperXPulsefireSurge(HyperXPulsefireSurgeController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_MOUSE; + description = "HyperX Pulsefire Surge Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + /*-----------------------------------------------------*\ + | The Corsair Lighting Node Pro requires a packet within| + | 20 seconds of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 5s | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXPulsefireSurge::KeepaliveThread, this); +}; + +RGBController_HyperXPulsefireSurge::~RGBController_HyperXPulsefireSurge() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_HyperXPulsefireSurge::SetupZones() +{ + zone led_strip; + led_strip.name = "LED Strip"; + led_strip.type = ZONE_TYPE_LINEAR; + led_strip.leds_min = 32; + led_strip.leds_max = 32; + led_strip.leds_count = 32; + led_strip.matrix_map = NULL; + zones.push_back(led_strip); + + zone logo; + logo.name = "Logo"; + logo.type = ZONE_TYPE_SINGLE; + logo.leds_min = 1; + logo.leds_max = 1; + logo.leds_count = 1; + logo.matrix_map = NULL; + zones.push_back(logo); + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + + new_led.name = zones[zone_idx].name; + + if(zones[zone_idx].leds_count > 1) + { + new_led.name.append(" LED "); + new_led.name.append(std::to_string(led_idx + 1)); + } + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_HyperXPulsefireSurge::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXPulsefireSurge::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + if(active_mode == 0) + { + controller->SendDirect(&colors[0]); + } + else + { + } + +} + +void RGBController_HyperXPulsefireSurge::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXPulsefireSurge::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXPulsefireSurge::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXPulsefireSurge::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(10ms); + } +} diff --git a/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/RGBController_HyperXPulsefireSurge.h b/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/RGBController_HyperXPulsefireSurge.h new file mode 100644 index 0000000..fc01118 --- /dev/null +++ b/Controllers/HyperXMouseController/HyperXPulsefireSurgeController/RGBController_HyperXPulsefireSurge.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXPulsefireSurge.h | +| | +| RGBController for HyperX Pulsefire Surge | +| | +| Adam Honse (CalcProgrammer1) 25 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXPulsefireSurgeController.h" + +class RGBController_HyperXPulsefireSurge : public RGBController +{ +public: + RGBController_HyperXPulsefireSurge(HyperXPulsefireSurgeController* controller_ptr); + ~RGBController_HyperXPulsefireSurge(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + HyperXPulsefireSurgeController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/HyperXMousematController/HyperXMousematController.cpp b/Controllers/HyperXMousematController/HyperXMousematController.cpp new file mode 100644 index 0000000..e73933b --- /dev/null +++ b/Controllers/HyperXMousematController/HyperXMousematController.cpp @@ -0,0 +1,124 @@ +/*---------------------------------------------------------*\ +| HyperXMousematController.cpp | +| | +| Driver for HyperX mousemat | +| | +| Adam Honse (CalcProgrammer1) 25 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "HyperXMousematController.h" +#include "StringUtils.h" + +HyperXMousematController::HyperXMousematController(hidapi_wrapper hid_wrapper, hid_device* dev_handle, const char* path, std::string dev_name) +{ + wrapper = hid_wrapper; + dev = dev_handle; + location = path; + name = dev_name; +} + +HyperXMousematController::~HyperXMousematController() +{ + wrapper.hid_close(dev); +} + +std::string HyperXMousematController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string HyperXMousematController::GetNameString() +{ + return(name); +} + +std::string HyperXMousematController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = wrapper.hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void HyperXMousematController::SendDirect + ( + RGBColor* color_data + ) +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Select Profile packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + buf[0x01] = 0x04; + buf[0x02] = 0xF2; + + buf[0x09] = 0x02; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + wrapper.hid_send_feature_report(dev, buf, 65); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Select Profile packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + + for(int i = 0; i < 16; i++) + { + buf[(i * 4) + 1] = 0x81; + buf[(i * 4) + 2] = RGBGetRValue(color_data[i]); + buf[(i * 4) + 3] = RGBGetGValue(color_data[i]); + buf[(i * 4) + 4] = RGBGetBValue(color_data[i]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + wrapper.hid_send_feature_report(dev, buf, 65); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Select Profile packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + + for(int i = 0; i < 16; i++) + { + buf[(i * 4) + 1] = 0x81; + buf[(i * 4) + 2] = RGBGetRValue(color_data[16 + i]); + buf[(i * 4) + 3] = RGBGetGValue(color_data[16 + i]); + buf[(i * 4) + 4] = RGBGetBValue(color_data[16 + i]); + } + + wrapper.hid_send_feature_report(dev, buf, 65); +} diff --git a/Controllers/HyperXMousematController/HyperXMousematController.h b/Controllers/HyperXMousematController/HyperXMousematController.h new file mode 100644 index 0000000..9db9056 --- /dev/null +++ b/Controllers/HyperXMousematController/HyperXMousematController.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| HyperXMousematController.h | +| | +| Driver for HyperX mousemat | +| | +| Adam Honse (CalcProgrammer1) 25 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "hidapi_wrapper.h" + +class HyperXMousematController +{ +public: + HyperXMousematController(hidapi_wrapper hid_wrapper, hid_device* dev_handle, const char* path, std::string dev_name); + ~HyperXMousematController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendDirect + ( + RGBColor* color_data + ); + +private: + hidapi_wrapper wrapper; + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/HyperXMousematController/HyperXMousematControllerDetect.cpp b/Controllers/HyperXMousematController/HyperXMousematControllerDetect.cpp new file mode 100644 index 0000000..c153415 --- /dev/null +++ b/Controllers/HyperXMousematController/HyperXMousematControllerDetect.cpp @@ -0,0 +1,58 @@ +/*---------------------------------------------------------*\ +| HyperXMousematControllerDetect.cpp | +| | +| Detector for HyperX mousemat | +| | +| Adam Honse (CalcProgrammer1) 25 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "HyperXMousematController.h" +#include "RGBController_HyperXMousemat.h" +#include "hidapi_wrapper.h" + +/*-----------------------------------------------------*\ +| HyperX mousemat vendor IDs | +\*-----------------------------------------------------*/ +#define HYPERX_VID 0x0951 +#define HYPERX_FURY_ULTRA_PID 0x1705 +#define HYPERX_FURY_A_XL_PID 0x1741 + +#define HYPERX_VID_2 0x03F0 +#define HYPERX_PULSEFIRE_PID 0x0F8D + +/******************************************************************************************\ +* * +* DetectHyperXMousematControllers * +* * +* Tests the USB address to see if a HyperX Mousemat controller exists there. * +* * +\******************************************************************************************/ + +void DetectHyperXMousematControllers(hidapi_wrapper wrapper, hid_device_info* info, const std::string& name) +{ + hid_device* dev = wrapper.hid_open_path(info->path); + + if(dev) + { + int first_zone_leds_count = info->product_id == HYPERX_FURY_A_XL_PID ? 2 : 15; + int second_zone_leds_count = info->product_id == HYPERX_FURY_A_XL_PID ? 0 : 5; + + HyperXMousematController* controller = new HyperXMousematController(wrapper, dev, info->path, name); + RGBController_HyperXMousemat* rgb_controller = new RGBController_HyperXMousemat(controller, first_zone_leds_count, second_zone_leds_count); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectHyperXMousematControllers() */ + +REGISTER_HID_WRAPPED_DETECTOR_I("HyperX Fury Ultra", DetectHyperXMousematControllers, HYPERX_VID, HYPERX_FURY_ULTRA_PID, 0); +REGISTER_HID_WRAPPED_DETECTOR_IPU("HyperX Pulsefire Mat", DetectHyperXMousematControllers, HYPERX_VID_2, HYPERX_PULSEFIRE_PID, 1, 0xFF90, 0xFF00); + +#ifdef _WIN32 +REGISTER_HID_WRAPPED_DETECTOR_IPU("HyperX Pulsefire Mat RGB Mouse Pad XL", DetectHyperXMousematControllers, HYPERX_VID, HYPERX_FURY_A_XL_PID, 1, 0xFF90, 0xFF00); +#else +REGISTER_HID_WRAPPED_DETECTOR_IPU("HyperX Pulsefire Mat RGB Mouse Pad XL", DetectHyperXMousematControllers, HYPERX_VID, HYPERX_FURY_A_XL_PID, 0, 0x0C, 0x01); +#endif diff --git a/Controllers/HyperXMousematController/RGBController_HyperXMousemat.cpp b/Controllers/HyperXMousematController/RGBController_HyperXMousemat.cpp new file mode 100644 index 0000000..a859071 --- /dev/null +++ b/Controllers/HyperXMousematController/RGBController_HyperXMousemat.cpp @@ -0,0 +1,158 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXMousemat.cpp | +| | +| RGBController for HyperX mousemat | +| | +| Adam Honse (CalcProgrammer1) 25 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_HyperXMousemat.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name HyperX Mousemat + @category Mousemat + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXMousematControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HyperXMousemat::RGBController_HyperXMousemat(HyperXMousematController* controller_ptr, unsigned int first_zone_leds_count_arg, unsigned int second_zone_leds_count_arg) +{ + controller = controller_ptr; + first_zone_leds_count = first_zone_leds_count_arg; + second_zone_leds_count = second_zone_leds_count_arg; + + name = controller->GetNameString(); + vendor = "HyperX"; + type = DEVICE_TYPE_MOUSEMAT; + description = "HyperX Mousemat Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + /*-----------------------------------------------------*\ + | The Corsair Lighting Node Pro requires a packet within| + | 20 seconds of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 5s | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXMousemat::KeepaliveThread, this); +}; + +RGBController_HyperXMousemat::~RGBController_HyperXMousemat() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_HyperXMousemat::SetupZones() +{ + if(first_zone_leds_count > 0) + { + zone underglow; + underglow.name = "Underglow"; + underglow.type = ZONE_TYPE_LINEAR; + underglow.leds_min = first_zone_leds_count; + underglow.leds_max = first_zone_leds_count; + underglow.leds_count = first_zone_leds_count; + underglow.matrix_map = NULL; + zones.push_back(underglow); + } + + if(second_zone_leds_count > 0) + { + zone led_strip; + led_strip.name = "LED Strip"; + led_strip.type = ZONE_TYPE_LINEAR; + led_strip.leds_min = second_zone_leds_count; + led_strip.leds_max = second_zone_leds_count; + led_strip.leds_count = second_zone_leds_count; + led_strip.matrix_map = NULL; + zones.push_back(led_strip); + } + + + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + + new_led.name = zones[zone_idx].name; + + if(zones[zone_idx].leds_count > 1) + { + new_led.name.append(" LED "); + new_led.name.append(std::to_string(led_idx + 1)); + } + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_HyperXMousemat::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_HyperXMousemat::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + controller->SendDirect(&colors[0]); +} + +void RGBController_HyperXMousemat::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXMousemat::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXMousemat::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_HyperXMousemat::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(10ms); + } +} diff --git a/Controllers/HyperXMousematController/RGBController_HyperXMousemat.h b/Controllers/HyperXMousematController/RGBController_HyperXMousemat.h new file mode 100644 index 0000000..c61d020 --- /dev/null +++ b/Controllers/HyperXMousematController/RGBController_HyperXMousemat.h @@ -0,0 +1,44 @@ +/*---------------------------------------------------------*\ +| RGBController_HyperXMousemat.h | +| | +| RGBController for HyperX mousemat | +| | +| Adam Honse (CalcProgrammer1) 25 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "HyperXMousematController.h" + +class RGBController_HyperXMousemat : public RGBController +{ +public: + RGBController_HyperXMousemat(HyperXMousematController* controller_ptr, unsigned int first_zone_leds_count_arg, unsigned int second_zone_leds_count_arg); + ~RGBController_HyperXMousemat(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThread(); + +private: + HyperXMousematController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; + + unsigned int first_zone_leds_count; + unsigned int second_zone_leds_count; +}; diff --git a/Controllers/InstantMouseController/InstantMouseController.cpp b/Controllers/InstantMouseController/InstantMouseController.cpp new file mode 100644 index 0000000..64a8cf7 --- /dev/null +++ b/Controllers/InstantMouseController/InstantMouseController.cpp @@ -0,0 +1,116 @@ +/*---------------------------------------------------------*\ +| InstantMouseController.cpp | +| | +| Driver for Instant mouse | +| | +| Morgan Guimard (morg) 19 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "InstantMouseController.h" +#include "StringUtils.h" + +InstantMouseController::InstantMouseController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + pid = info.product_id; + name = dev_name; +} + +InstantMouseController::~InstantMouseController() +{ + hid_close(dev); +} + +std::string InstantMouseController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string InstantMouseController::GetNameString() +{ + return(name); +} + +std::string InstantMouseController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +uint16_t InstantMouseController::GetPID() +{ + return pid; +} + +void InstantMouseController::SendColor(RGBColor color) +{ + /*---------------------------------------------------------*\ + | Packet details | + | 07 14 XG RB 00 00 00 00 | + | where X is the DPI slot | + \*---------------------------------------------------------*/ + + uint8_t red = 0xF - RGBGetRValue(color) / 16; + uint8_t grn = 0xF - RGBGetGValue(color) / 16; + uint8_t blu = 0xF - RGBGetBValue(color) / 16; + + uint8_t pkt[INSTANT_MOUSE_REPORT_SIZE]; + memset(pkt, 0 , INSTANT_MOUSE_REPORT_SIZE); + + pkt[0] = INSTANT_MOUSE_REPORT_ID; + pkt[1] = INSTANT_MOUSE_SET_COLOR; + pkt[3] = (red << 4) | (blu & 0xF); + + for(unsigned int i = 0; i <= 0xA; i+= 2) + { + pkt[2] = (i << 4) | (grn & 0xF); + hid_send_feature_report(dev, pkt, INSTANT_MOUSE_REPORT_SIZE); + } +} + +void InstantMouseController::SetMode(uint8_t mode_value, uint8_t speed, uint8_t brightness, uint8_t direction) +{ + /*---------------------------------------------------------*\ + | Packet details: | + | 07 13 FF MS DN -B M- -- | + | | + | 07 = report id | + | 13 = set mode function | + | FF = constant | + | - = still undiscovered/useless | + | M = mode | + | S = speed 0 -> 5 (6 and above makes the effect static) | + | N = number of leds | + | D = direction (0/1) | + | B = brightness (0 full, 7 off) | + \*---------------------------------------------------------*/ + + uint8_t led_mask = 0xB; + + uint8_t pkt[INSTANT_MOUSE_REPORT_SIZE]; + + pkt[0] = INSTANT_MOUSE_REPORT_ID; + pkt[1] = INSTANT_MOUSE_SET_MODE; + pkt[2] = 0xFF; + + pkt[3] = (mode_value << 4) | (speed & 0xF); + pkt[4] = (direction << 4) | led_mask; + pkt[5] = 0xF - (brightness & 0xF); + + pkt[6] = mode_value & 0xF0; + pkt[7] = 0x00; + + hid_send_feature_report(dev, pkt, INSTANT_MOUSE_REPORT_SIZE); +} diff --git a/Controllers/InstantMouseController/InstantMouseController.h b/Controllers/InstantMouseController/InstantMouseController.h new file mode 100644 index 0000000..4b81d6d --- /dev/null +++ b/Controllers/InstantMouseController/InstantMouseController.h @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| InstantMouseController.h | +| | +| Driver for Instant mouse | +| | +| Morgan Guimard (morg) 19 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define INSTANT_MOUSE_REPORT_ID 0x07 +#define INSTANT_MOUSE_REPORT_SIZE 8 +#define INSTANT_MOUSE_SET_MODE 0x13 +#define INSTANT_MOUSE_SET_COLOR 0x14 + +enum +{ + INSTANT_MOUSE_DIRECT_MODE = 0x0A, + INSTANT_MOUSE_OFF_MODE = 0xFF, + INSTANT_MOUSE_MULTICOLOR_BREATHING_MODE = 0x01, + INSTANT_MOUSE_FILL_DRAIN_MODE = 0x03, + INSTANT_MOUSE_LOOP_MODE = 0x04, + INSTANT_MOUSE_SPECTRUM_CYCLE_MODE = 0x06, + INSTANT_MOUSE_RAINBOW_WAVE_MODE = 0x07, + INSTANT_MOUSE_BREATHING_MODE = 0x08, + ANT_MOUSE_BREATHING_MODE = 0x09, + INSTANT_MOUSE_ENRAPTURED_MODE = 0xBB, + INSTANT_MOUSE_FLICKER_MODE = 0xB8, + INSTANT_MOUSE_RIPPLE_MODE = 0xBA, + INSTANT_MOUSE_STARTRECK_MODE = 0xB9, + +}; + +enum +{ + INSTANT_MOUSE_SPEED_MIN = 0x00, + INSTANT_MOUSE_SPEED_MAX = 0x05, + INSTANT_MOUSE_BRIGHTNESS_MIN = 0x00, + INSTANT_MOUSE_BRIGHTNESS_MAX = 0x07 +}; + +class InstantMouseController +{ +public: + InstantMouseController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~InstantMouseController(); + + std::string GetNameString(); + std::string GetSerialString(); + std::string GetDeviceLocation(); + uint16_t GetPID(); + + void SetMode(uint8_t mode_value, uint8_t speed, uint8_t brightness, uint8_t direction); + void SendColor(RGBColor color); + +private: + hid_device* dev; + std::string location; + std::string name; + uint16_t pid; +}; diff --git a/Controllers/InstantMouseController/InstantMouseControllerDetect.cpp b/Controllers/InstantMouseController/InstantMouseControllerDetect.cpp new file mode 100644 index 0000000..a363892 --- /dev/null +++ b/Controllers/InstantMouseController/InstantMouseControllerDetect.cpp @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| InstantMouseControllerDetect.cpp | +| | +| Detector for Instant mouse | +| | +| Morgan Guimard (morg) 19 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "InstantMouseController.h" +#include "RGBController_InstantMouse.h" +#include "InstantMouseDevices.h" + + +void DetectInstantMouseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + InstantMouseController* controller = new InstantMouseController(dev, *info, name); + RGBController_InstantMouse* rgb_controller = new RGBController_InstantMouse(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Advanced GTA 250 USB Gaming Mouse", DetectInstantMouseControllers, INSTANT_MICROELECTRONICS_VID, ADVANCED_GTA_250_PID, 1, 0xFF01, 0x01); +REGISTER_HID_DETECTOR_IPU("Anko KM43243952 USB Gaming Mouse", DetectInstantMouseControllers, INSTANT_MICROELECTRONICS_VID, ANKO_KM43243952_VID, 1, 0xFF01, 0x01); +REGISTER_HID_DETECTOR_IPU("Anko KM43277483 USB Gaming Mouse", DetectInstantMouseControllers, INSTANT_MICROELECTRONICS_VID, ANKO_KM43277483_VID, 1, 0xFF01, 0x01); +REGISTER_HID_DETECTOR_IPU("AntEsports GM600 USB Gaming Mouse", DetectInstantMouseControllers, INSTANT_MICROELECTRONICS_VID, ANTESPORTS_GM600_PID, 1, 0xFF01, 0001); + diff --git a/Controllers/InstantMouseController/InstantMouseDevices.h b/Controllers/InstantMouseController/InstantMouseDevices.h new file mode 100644 index 0000000..b6da7ae --- /dev/null +++ b/Controllers/InstantMouseController/InstantMouseDevices.h @@ -0,0 +1,19 @@ +/*---------------------------------------------------------*\ +| InstantMouseController.h | +| | +| Driver for Instant mouse | +| | +| shafiahaz2478 29 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ +#pragma once + +#define INSTANTMOUSEDEVICES_H + +#define INSTANT_MICROELECTRONICS_VID 0x30FA +#define ADVANCED_GTA_250_PID 0x1030 +#define ANKO_KM43243952_VID 0x1440 +#define ANKO_KM43277483_VID 0x1540 +#define ANTESPORTS_GM600_PID 0x1040 diff --git a/Controllers/InstantMouseController/RGBController_InstantMouse.cpp b/Controllers/InstantMouseController/RGBController_InstantMouse.cpp new file mode 100644 index 0000000..25f4799 --- /dev/null +++ b/Controllers/InstantMouseController/RGBController_InstantMouse.cpp @@ -0,0 +1,223 @@ +/*---------------------------------------------------------*\ +| RGBController_InstantMouse.cpp | +| | +| RGBController for Instant mouse | +| | +| Morgan Guimard (morg) 19 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_InstantMouse.h" +#include "InstantMouseDevices.h" + +/**------------------------------------------------------------------*\ + @name Instant mouse + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectInstantMouseControllers + @comment This controller should work with all mouse with this chip. + Identified devices that work with this controller: Advance Gaming + GTA 250 (GX72-A725), Anko KM43243952 (GM8-A825), Anko KM43277483, + Ant Esports GM600 +\*-------------------------------------------------------------------*/ + +RGBController_InstantMouse::RGBController_InstantMouse(InstantMouseController* controller_ptr) +{ + controller = controller_ptr; + + vendor = controller->GetNameString(); + type = DEVICE_TYPE_MOUSE; + description = "Instant USB Gaming Mouse"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode direct; + direct.name = "Direct"; + direct.value = INSTANT_MOUSE_DIRECT_MODE; + direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + direct.color_mode = MODE_COLORS_PER_LED; + direct.brightness = INSTANT_MOUSE_BRIGHTNESS_MAX; + direct.brightness_min = INSTANT_MOUSE_BRIGHTNESS_MIN; + direct.brightness_max = INSTANT_MOUSE_BRIGHTNESS_MAX; + modes.push_back(direct); + + mode rainbow; + rainbow.name = "Rainbow wave"; + rainbow.value = INSTANT_MOUSE_RAINBOW_WAVE_MODE; + rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + rainbow.color_mode = MODE_COLORS_NONE; + rainbow.speed_min = INSTANT_MOUSE_SPEED_MIN; + rainbow.speed_max = INSTANT_MOUSE_SPEED_MAX; + rainbow.speed = INSTANT_MOUSE_SPEED_MAX/2; + modes.push_back(rainbow); + + mode spectrum; + spectrum.name = "Spectrum cycle"; + spectrum.value = INSTANT_MOUSE_SPECTRUM_CYCLE_MODE; + spectrum.flags = MODE_FLAG_HAS_SPEED; + spectrum.color_mode = MODE_COLORS_NONE; + spectrum.speed_min = INSTANT_MOUSE_SPEED_MIN; + spectrum.speed_max = INSTANT_MOUSE_SPEED_MAX; + spectrum.speed = INSTANT_MOUSE_SPEED_MAX/2; + modes.push_back(spectrum); + + mode breathing; + breathing.name = "Breathing"; + /*------------------------------------------------------------------*\ + | ANT ESPORTS GM600 has different mode id for breathing mode. | + \*------------------------------------------------------------------*/ + breathing.value = (controller->GetPID() == ANTESPORTS_GM600_PID ) ? ANT_MOUSE_BREATHING_MODE : INSTANT_MOUSE_BREATHING_MODE; + breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + breathing.colors.resize(1); + breathing.colors_min = 1; + breathing.colors_max = 1; + breathing.speed_min = INSTANT_MOUSE_SPEED_MIN; + breathing.speed_max = INSTANT_MOUSE_SPEED_MAX; + breathing.speed = INSTANT_MOUSE_SPEED_MAX/2; + modes.push_back(breathing); + + mode fill; + fill.name = "Fill"; + fill.value = INSTANT_MOUSE_FILL_DRAIN_MODE; + fill.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + fill.color_mode = MODE_COLORS_NONE; + fill.speed_min = INSTANT_MOUSE_SPEED_MIN; + fill.speed_max = INSTANT_MOUSE_SPEED_MAX; + fill.speed = INSTANT_MOUSE_SPEED_MAX/2; + fill.brightness = INSTANT_MOUSE_BRIGHTNESS_MAX; + fill.brightness_min = INSTANT_MOUSE_BRIGHTNESS_MIN; + fill.brightness_max = INSTANT_MOUSE_BRIGHTNESS_MAX; + modes.push_back(fill); + + mode loop; + loop.name = "Loop"; + loop.value = INSTANT_MOUSE_LOOP_MODE; + loop.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + loop.color_mode = MODE_COLORS_NONE; + loop.speed_min = INSTANT_MOUSE_SPEED_MIN; + loop.speed_max = INSTANT_MOUSE_SPEED_MAX; + loop.speed = INSTANT_MOUSE_SPEED_MAX/2; + loop.brightness = INSTANT_MOUSE_BRIGHTNESS_MAX; + loop.brightness_min = INSTANT_MOUSE_BRIGHTNESS_MIN; + loop.brightness_max = INSTANT_MOUSE_BRIGHTNESS_MAX; + modes.push_back(loop); + /*------------------------------------------------------------------*\ + | Extra modes for Ant Esports GM600. | + \*------------------------------------------------------------------*/ + if(controller->GetPID() == ANTESPORTS_GM600_PID ) + { + mode enraptured; + enraptured.name = "Enrpatured"; + enraptured.value = INSTANT_MOUSE_ENRAPTURED_MODE; + enraptured.flags = MODE_FLAG_HAS_SPEED; + enraptured.color_mode = MODE_COLORS_NONE; + enraptured.speed_min = INSTANT_MOUSE_SPEED_MIN; + enraptured.speed_max = INSTANT_MOUSE_SPEED_MAX; + enraptured.speed = INSTANT_MOUSE_SPEED_MAX/2; + modes.push_back(enraptured); + + mode flicker; + flicker.name = "Flicker"; + flicker.value = INSTANT_MOUSE_FLICKER_MODE; + flicker.flags = MODE_FLAG_HAS_SPEED; + flicker.color_mode = MODE_COLORS_NONE; + flicker.speed_min = INSTANT_MOUSE_SPEED_MIN; + flicker.speed_max = INSTANT_MOUSE_SPEED_MAX; + flicker.speed = INSTANT_MOUSE_SPEED_MAX/2; + modes.push_back(flicker); + + mode ripple; + ripple.name = "Ripple"; + ripple.value = INSTANT_MOUSE_RIPPLE_MODE; + ripple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + ripple.color_mode = MODE_COLORS_NONE; + ripple.speed_min = INSTANT_MOUSE_SPEED_MIN; + ripple.speed_max = INSTANT_MOUSE_SPEED_MAX; + ripple.speed = INSTANT_MOUSE_SPEED_MAX/2; + modes.push_back(ripple); + + mode startreck; + startreck.name = "Star treck"; + startreck.value = INSTANT_MOUSE_STARTRECK_MODE; + + modes.push_back(startreck); + } + + mode off; + off.name = "Off"; + off.value = INSTANT_MOUSE_OFF_MODE; + + modes.push_back(off); + + SetupZones(); +} + +RGBController_InstantMouse::~RGBController_InstantMouse() +{ + delete controller; +} + +void RGBController_InstantMouse::SetupZones() +{ + zone new_zone; + + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(1); + leds[0].name = "Mouse"; + + SetupColors(); +} + +void RGBController_InstantMouse::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_InstantMouse::DeviceUpdateLEDs() +{ + controller->SendColor(colors[0]); +} + +void RGBController_InstantMouse::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_InstantMouse::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_InstantMouse::DeviceUpdateMode() +{ + if(modes[active_mode].value == INSTANT_MOUSE_OFF_MODE) + { + controller->SetMode(INSTANT_MOUSE_DIRECT_MODE, 0, 0, 0); + controller->SendColor(0); + } + else + { + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, modes[active_mode].direction); + + if(modes[active_mode].colors.size() == 1) + { + controller->SendColor(modes[active_mode].colors[0]); + } + } +} diff --git a/Controllers/InstantMouseController/RGBController_InstantMouse.h b/Controllers/InstantMouseController/RGBController_InstantMouse.h new file mode 100644 index 0000000..c359d29 --- /dev/null +++ b/Controllers/InstantMouseController/RGBController_InstantMouse.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_InstantMouse.h | +| | +| RGBController for Instant mouse | +| | +| Morgan Guimard (morg) 19 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "InstantMouseController.h" + +class RGBController_InstantMouse : public RGBController +{ +public: + RGBController_InstantMouse(InstantMouseController* controller_ptr); + ~RGBController_InstantMouse(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + InstantMouseController* controller; +}; diff --git a/Controllers/IntelArcA770LEController/IntelArcA770LEController.cpp b/Controllers/IntelArcA770LEController/IntelArcA770LEController.cpp new file mode 100644 index 0000000..77fb1e7 --- /dev/null +++ b/Controllers/IntelArcA770LEController/IntelArcA770LEController.cpp @@ -0,0 +1,205 @@ +/*---------------------------------------------------------*\ +| IntelArcA770LEController.cpp | +| | +| Driver for Intel Arc A770 LE | +| | +| Adam Honse (CalcProgrammer1) 01 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "IntelArcA770LEController.h" +#include "StringUtils.h" + +IntelArcA770LEController::IntelArcA770LEController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; +} + +IntelArcA770LEController::~IntelArcA770LEController() +{ + hid_close(dev); +} + +std::string IntelArcA770LEController::GetLocationString() +{ + return("HID: " + location); +} + +std::string IntelArcA770LEController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string IntelArcA770LEController::GetFirmwareVersionString() +{ + std::string ret_string = ""; + + unsigned char usb_buf[] = + { + 0x00, + 0x12, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + unsigned char fw_buf[16] = {0x00}; + + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); + + for(int char_idx = 0; char_idx < 16; char_idx+=2) + { + if(usb_buf[char_idx + 0x08] != 0) + { + fw_buf[char_idx / 2] = usb_buf[char_idx + 0x08]; + } + else + { + break; + } + } + + ret_string.append((char *)fw_buf); + + return(ret_string); +} + +void IntelArcA770LEController::SendEnableCommand() +{ + unsigned char usb_buf[] = + { + 0x00, + 0x41, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +} + +void IntelArcA770LEController::SendApplyCommand() +{ + unsigned char usb_buf[] = + { + 0x00, + 0x51, 0x28, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +} + +void IntelArcA770LEController::SendDirectPacket + ( + unsigned char size, + unsigned char * led_ids, + RGBColor * colors + ) +{ + unsigned char usb_buf[] = + { + 0x00, + 0xC0, 0x01, size, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + for(unsigned int led_idx = 0; led_idx < size; led_idx++) + { + unsigned int index = led_idx * 4; + + /*-----------------------------------------*\ + | Special handling for Logo LED (0x96) | + | Use the maximum channel value as this is | + | a white LED using the red channel | + \*-----------------------------------------*/ + if(led_ids[led_idx] == 0x96) + { + usb_buf[index + 5] = led_ids[led_idx]; + + usb_buf[index + 6] = std::max(RGBGetRValue(colors[led_idx]), std::max(RGBGetGValue(colors[led_idx]), RGBGetBValue(colors[led_idx]))); + usb_buf[index + 7] = 0; + usb_buf[index + 8] = 0; + } + else + { + usb_buf[index + 5] = led_ids[led_idx]; + usb_buf[index + 6] = RGBGetRValue(colors[led_idx]); + usb_buf[index + 7] = RGBGetGValue(colors[led_idx]); + usb_buf[index + 8] = RGBGetBValue(colors[led_idx]); + } + } + + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 64); +} diff --git a/Controllers/IntelArcA770LEController/IntelArcA770LEController.h b/Controllers/IntelArcA770LEController/IntelArcA770LEController.h new file mode 100644 index 0000000..13079fd --- /dev/null +++ b/Controllers/IntelArcA770LEController/IntelArcA770LEController.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| IntelArcA770LEController.h | +| | +| Driver for Intel Arc A770 LE | +| | +| Adam Honse (CalcProgrammer1) 01 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class IntelArcA770LEController +{ +public: + IntelArcA770LEController(hid_device* dev_handle, const char* path); + ~IntelArcA770LEController(); + + std::string GetEffectChannelString(unsigned char channel); + std::string GetFirmwareVersionString(); + std::string GetLocationString(); + std::string GetSerialString(); + + void SendDirectPacket + ( + unsigned char size, + unsigned char * led_ids, + RGBColor * colors + ); + + void SendEnableCommand(); + + void SendApplyCommand(); + +private: + hid_device* dev; + std::string location; +}; diff --git a/Controllers/IntelArcA770LEController/IntelArcA770LEControllerDetect.cpp b/Controllers/IntelArcA770LEController/IntelArcA770LEControllerDetect.cpp new file mode 100644 index 0000000..09274ad --- /dev/null +++ b/Controllers/IntelArcA770LEController/IntelArcA770LEControllerDetect.cpp @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| IntelArcA770LEControllerDetect.cpp | +| | +| Detector for Intel Arc A770 LE | +| | +| Adam Honse (CalcProgrammer1) 01 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "IntelArcA770LEController.h" +#include "RGBController_IntelArcA770LE.h" + +#define INTEL_ARC_A770_LIMITED_EDITION_VID 0x2516 +#define INTEL_ARC_A770_LIMITED_EDITION_PID 0x01B5 + +/******************************************************************************************\ +* * +* DetectIntelArcA770LEControllers * +* * +* Tests the USB address to see if an Intel Arc A770 LE controller exists there. * +* * +\******************************************************************************************/ + +void DetectIntelArcA770LEControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + if( dev ) + { + IntelArcA770LEController* controller = new IntelArcA770LEController(dev, info->path); + RGBController_IntelArcA770LE* rgb_controller = new RGBController_IntelArcA770LE(controller); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IP("Intel Arc A770 Limited Edition", DetectIntelArcA770LEControllers, INTEL_ARC_A770_LIMITED_EDITION_VID, INTEL_ARC_A770_LIMITED_EDITION_PID, 1, 0xFF00); diff --git a/Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.cpp b/Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.cpp new file mode 100644 index 0000000..f96baf9 --- /dev/null +++ b/Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.cpp @@ -0,0 +1,205 @@ +/*---------------------------------------------------------*\ +| RGBController_IntelArcA770LE.cpp | +| | +| RGBController for Intel Arc A770 LE | +| | +| Adam Honse (CalcProgrammer1) 01 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_IntelArcA770LE.h" + +/**------------------------------------------------------------------*\ + @name Intel Arc A770 Limited Edition + @category GPU + @type USB + @save :o: + @direct :white_check_mark: + @effects :tools: + @detectors DetectIntelArcA770LEControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_IntelArcA770LE::RGBController_IntelArcA770LE(IntelArcA770LEController* controller_ptr) +{ + controller = controller_ptr; + + name = "Intel Arc A770 Limited Edition"; + vendor = "Cooler Master"; + type = DEVICE_TYPE_GPU; + description = "Intel Arc A770 Limited Edition"; + version = controller->GetFirmwareVersionString(); + location = controller->GetLocationString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = 0; + Direct.brightness_max = 0; + Direct.brightness = 0; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + controller->SendEnableCommand(); + controller->SendApplyCommand(); + + SetupZones(); +} + +RGBController_IntelArcA770LE::~RGBController_IntelArcA770LE() +{ + delete controller; +} + +void RGBController_IntelArcA770LE::SetupZones() +{ + const unsigned int fan_1_leds[16] = { 0x01, 0x04, 0x07, 0x0A, 0x0D, 0x10, 0x13, 0x16, + 0x19, 0x1C, 0x1F, 0x22, 0x25, 0x28, 0x2B, 0x2E }; + const unsigned int fan_2_leds[16] = { 0x31, 0x34, 0x37, 0x3A, 0x3D, 0x40, 0x43, 0x46, + 0x49, 0x4C, 0x4F, 0x52, 0x55, 0x58, 0x5B, 0x5E }; + const unsigned int back_leds[8] = { 0x02, 0x05, 0x08, 0x0B, 0x0E, 0x11, 0x14, 0x17 }; + const unsigned int ring_leds[50] = { 0x00, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x12, 0x15, + 0x18, 0x1B, 0x1E, 0x21, 0x24, 0x27, 0x2A, 0x2D, + 0x30, 0x33, 0x36, 0x39, 0x3C, 0x3F, 0x42, 0x45, + 0x48, 0x4B, 0x4E, 0x51, 0x54, 0x57, 0x5A, 0x5D, + 0x60, 0x63, 0x66, 0x69, 0x6C, 0x6F, 0x72, 0x75, + 0x78, 0x7B, 0x7E, 0x81, 0x84, 0x87, 0x8A, 0x8D, + 0x90, 0x93 }; + const unsigned int logo_leds[1] = { 0x96 }; + + zone fan_1_zone; + fan_1_zone.name = "Fan 1"; + fan_1_zone.type = ZONE_TYPE_LINEAR; + fan_1_zone.leds_min = 16; + fan_1_zone.leds_max = 16; + fan_1_zone.leds_count = 16; + fan_1_zone.matrix_map = NULL; + zones.push_back(fan_1_zone); + + zone fan_2_zone; + fan_2_zone.name = "Fan 2"; + fan_2_zone.type = ZONE_TYPE_LINEAR; + fan_2_zone.leds_min = 16; + fan_2_zone.leds_max = 16; + fan_2_zone.leds_count = 16; + fan_2_zone.matrix_map = NULL; + zones.push_back(fan_2_zone); + + zone back; + back.name = "Back"; + back.type = ZONE_TYPE_LINEAR; + back.leds_min = 8; + back.leds_max = 8; + back.leds_count = 8; + back.matrix_map = NULL; + zones.push_back(back); + + zone ring; + ring.name = "Ring"; + ring.type = ZONE_TYPE_LINEAR; + ring.leds_min = 50; + ring.leds_max = 50; + ring.leds_count = 50; + ring.matrix_map = NULL; + zones.push_back(ring); + + zone logo; + logo.name = "Logo"; + logo.type = ZONE_TYPE_SINGLE; + logo.leds_min = 1; + logo.leds_max = 1; + logo.leds_count = 1; + logo.matrix_map = NULL; + zones.push_back(logo); + + for(unsigned int led_idx = 0; led_idx < 16; led_idx++) + { + led fan_1_led; + fan_1_led.name = "Fan 1 LED " + std::to_string(led_idx + 1); + fan_1_led.value = fan_1_leds[led_idx]; + leds.push_back(fan_1_led); + } + + for(unsigned int led_idx = 0; led_idx < 16; led_idx++) + { + led fan_2_led; + fan_2_led.name = "Fan 2 LED " + std::to_string(led_idx + 1); + fan_2_led.value = fan_2_leds[led_idx]; + leds.push_back(fan_2_led); + } + + for(unsigned int led_idx = 0; led_idx < 8; led_idx++) + { + led back_led; + back_led.name = "Back LED " + std::to_string(led_idx + 1); + back_led.value = back_leds[led_idx]; + leds.push_back(back_led); + } + + for(unsigned int led_idx = 0; led_idx < 50; led_idx++) + { + led ring_led; + ring_led.name = "Ring LED " + std::to_string(led_idx + 1); + ring_led.value = ring_leds[led_idx]; + leds.push_back(ring_led); + } + + for(unsigned int led_idx = 0; led_idx < 1; led_idx++) + { + led logo_led; + logo_led.name = "Logo LED " + std::to_string(led_idx + 1); + logo_led.value = logo_leds[led_idx]; + leds.push_back(logo_led); + } + + SetupColors(); +} + +void RGBController_IntelArcA770LE::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_IntelArcA770LE::DeviceUpdateLEDs() +{ + unsigned char led_ids[15]; + RGBColor color_buf[15]; + unsigned int leds_count = 0; + + for(unsigned int led_idx = 0; led_idx < colors.size(); led_idx++) + { + led_ids[leds_count] = (unsigned char)leds[led_idx].value; + color_buf[leds_count] = colors[led_idx]; + + leds_count++; + + if(leds_count >= 15) + { + controller->SendDirectPacket(15, led_ids, color_buf); + leds_count = 0; + } + } + + if(leds_count > 0) + { + controller->SendDirectPacket(leds_count, led_ids, color_buf); + } +} + +void RGBController_IntelArcA770LE::UpdateZoneLEDs(int /*zone*/) +{ + +} + +void RGBController_IntelArcA770LE::UpdateSingleLED(int /*led*/) +{ +} + +void RGBController_IntelArcA770LE::DeviceUpdateMode() +{ +} diff --git a/Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.h b/Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.h new file mode 100644 index 0000000..05f8be2 --- /dev/null +++ b/Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_IntelArcA770LE.h | +| | +| RGBController for Intel Arc A770 LE | +| | +| Adam Honse (CalcProgrammer1) 01 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "IntelArcA770LEController.h" + +class RGBController_IntelArcA770LE : public RGBController +{ +public: + RGBController_IntelArcA770LE(IntelArcA770LEController* controller_ptr); + ~RGBController_IntelArcA770LE(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + IntelArcA770LEController* controller; +}; diff --git a/Controllers/IonicoController/IonicoController.cpp b/Controllers/IonicoController/IonicoController.cpp new file mode 100644 index 0000000..1358975 --- /dev/null +++ b/Controllers/IonicoController/IonicoController.cpp @@ -0,0 +1,121 @@ +/*---------------------------------------------------------*\ +| IonicoController.cpp | +| | +| Driver for Ionico-II-17 | +| | +| Lucas Strafe 31 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "IonicoController.h" + +IonicoController::IonicoController(hid_device* dev_handle, const hid_device_info& info, const unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + usb_pid = pid; + name = dev_name; +} + +IonicoController::~IonicoController() +{ + hid_close(dev); +} + +std::string IonicoController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string IonicoController::GetDeviceName() +{ + return(name); +} + +uint16_t IonicoController::GetUSBPID() +{ + return(usb_pid); +} + +void IonicoController::TurnOff() +{ + uint8_t usb_buf[IONICO_REPORT_SIZE]; + memset(usb_buf, 0x00, IONICO_REPORT_SIZE); + usb_buf[1] = 0x09; + usb_buf[2] = 0x02; + hid_send_feature_report(dev, usb_buf, IONICO_REPORT_SIZE); +} + +void IonicoController::SaveBios() +{ + uint8_t usb_buf[IONICO_REPORT_SIZE]; + memset(usb_buf, 0x00, IONICO_REPORT_SIZE); + usb_buf[1] = 0x1A; + usb_buf[3] = 0x01; + usb_buf[4] = 0x04; + usb_buf[8] = 0x01; + hid_send_feature_report(dev, usb_buf, IONICO_REPORT_SIZE); +} + +void IonicoController::SetMode(uint8_t mode_value, uint8_t brightness, uint8_t speed) +{ + uint8_t usb_buf[IONICO_REPORT_SIZE]; + memset(usb_buf, 0x00, IONICO_REPORT_SIZE); + usb_buf[1] = 0x08; + usb_buf[2] = 0x02; + usb_buf[3] = mode_value; + usb_buf[4] = speed; + usb_buf[5] = brightness; + usb_buf[6] = 0x08; + hid_send_feature_report(dev, usb_buf, IONICO_REPORT_SIZE); +} + +void IonicoController::SetColors(int device, std::vector array_colors, bool is_mode) +{ + /*---------------------------------------------------------*\ + | Direct mode and effects | + \*---------------------------------------------------------*/ + if(device == DEVICE_TYPE_KEYBOARD || (device == DEVICE_TYPE_LEDSTRIP && is_mode)) + { + uint8_t usb_buf[IONICO_REPORT_SIZE]; + memset(usb_buf, 0x00, IONICO_REPORT_SIZE); + for(size_t i = 0; i < array_colors.size(); i++) + { + usb_buf[1] = IONICO_DIRECT_CMD; + usb_buf[3] = (uint8_t)(i + 1); + usb_buf[4] = RGBGetRValue(array_colors[i]); + usb_buf[5] = RGBGetGValue(array_colors[i]); + usb_buf[6] = RGBGetBValue(array_colors[i]); + hid_send_feature_report(dev, usb_buf, IONICO_REPORT_SIZE); + } + } + /*---------------------------------------------------------*\ + | LIGHT BAR LED PER LED | + \*---------------------------------------------------------*/ + else if(device == DEVICE_TYPE_LEDSTRIP && !is_mode) + { + uint8_t usb_buf[IONICO_REPORT_SIZE]; + memset(usb_buf, 0x00, IONICO_REPORT_SIZE); + usb_buf[1] = 0x12; + hid_send_feature_report(dev, usb_buf, IONICO_REPORT_SIZE); + + uint8_t usb_buf_led[IONICO_DIRECT_REPORT_SIZE]; + memset(usb_buf_led, 0x00, IONICO_DIRECT_REPORT_SIZE); + + for(size_t i = 0; i < array_colors.size(); i++) + { + usb_buf_led[1 + 3 * i] = RGBGetRValue(array_colors[i]); + usb_buf_led[2 + 3 * i] = RGBGetBValue(array_colors[i]); + usb_buf_led[3 + 3 * i] = RGBGetGValue(array_colors[i]); + } + hid_write(dev, usb_buf_led, IONICO_DIRECT_REPORT_SIZE); + + memset(usb_buf, 0x00, IONICO_REPORT_SIZE); + usb_buf[1] = 0x12; + usb_buf[3] = 0x01; + hid_send_feature_report(dev, usb_buf, IONICO_REPORT_SIZE); + } +} diff --git a/Controllers/IonicoController/IonicoController.h b/Controllers/IonicoController/IonicoController.h new file mode 100644 index 0000000..3b27c35 --- /dev/null +++ b/Controllers/IonicoController/IonicoController.h @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| IonicoController.h | +| | +| Driver for Ionico-II-17 | +| | +| Lucas Strafe 31 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define IONICO_REPORT_SIZE 9 +#define IONICO_DIRECT_REPORT_SIZE 65 +#define IONICO_DIRECT_CMD 0x14 +#define IONICO_KEYBOARD_LED_COUNT 4 +#define IONICO_BAR_LED_COUNT 22 +#define IONICO_DIRECT_BRIGHTNESS_MIN 0 +#define IONICO_DIRECT_BRIGHTNESS_MAX 50 +#define IONICO_DIRECT_SPEED_MIN 0 +#define IONICO_DIRECT_SPEED_MAX 10 +#define IONICO_DIRECT_SPEED_DEFAULT 5 + +enum +{ + IONICO_MODE_OFF = 0, + IONICO_MODE_DIRECT = 1, + IONICO_MODE_BREATHING = 2, + IONICO_MODE_WAVE = 3, + IONICO_MODE_RAIN = 10, + IONICO_MODE_FLASH = 18, + IONICO_FB_MODE_WAVE = 32 +}; + + +class IonicoController +{ + public: + IonicoController(hid_device* dev_handle, const hid_device_info& info, const unsigned short pid, std::string dev_name); + ~IonicoController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetMode(uint8_t mode_value, uint8_t brightness, uint8_t speed); + void SetColors(int device, std::vector array_colors, bool is_mode); + void SaveBios(); + void TurnOff(); + uint16_t GetUSBPID(); + + private: + hid_device* dev; + std::string location; + std::string name; + std::string serial_number; + uint16_t usb_pid; +}; diff --git a/Controllers/IonicoController/IonicoControllerDetect.cpp b/Controllers/IonicoController/IonicoControllerDetect.cpp new file mode 100644 index 0000000..4aca4ba --- /dev/null +++ b/Controllers/IonicoController/IonicoControllerDetect.cpp @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| IonicoControllerDetect.cpp | +| | +| Detector for Ionico-II-17 | +| | +| Lucas Strafe 31 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBController.h" +#include "hidapi.h" +#include "IonicoController.h" +#include "RGBController_Ionico.h" + +/*-----------------------------------------------------*\ +| FRONT BAR | +\*-----------------------------------------------------*/ +#define IONICO_FB_VID 0x048D +#define IONICO_FB_PID 0x6005 + +/*-----------------------------------------------------*\ +| KEYBOARD | +\*-----------------------------------------------------*/ +#define IONICO_KB_VID 0x048D +#define IONICO_KB_PID 0xCE00 + + +void DetectIonicoControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + IonicoController* controller = new IonicoController(dev, *info, info->product_id, name); + RGBController_Ionico* rgb_controller = new RGBController_Ionico(controller); + + if(info->product_id == IONICO_KB_PID) + { + rgb_controller->type = DEVICE_TYPE_KEYBOARD; + } + else if(info->product_id == IONICO_FB_PID) + { + rgb_controller->type = DEVICE_TYPE_LEDSTRIP; + } + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("Ionico Light Bar", DetectIonicoControllers, IONICO_FB_VID, IONICO_FB_PID, 0xFF03, 0x01); +REGISTER_HID_DETECTOR_PU("Ionico Keyboard", DetectIonicoControllers, IONICO_KB_VID, IONICO_KB_PID, 0xFF12, 0x01); diff --git a/Controllers/IonicoController/RGBController_Ionico.cpp b/Controllers/IonicoController/RGBController_Ionico.cpp new file mode 100644 index 0000000..c261b18 --- /dev/null +++ b/Controllers/IonicoController/RGBController_Ionico.cpp @@ -0,0 +1,225 @@ +/*---------------------------------------------------------*\ +| RGBController_Ionico.cpp | +| | +| RGBController for Ionico-II-17 | +| | +| Lucas Strafe 31 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Ionico.h" + +/**------------------------------------------------------------------*\ + @name Ionico-II 17 + @category Keyboard,LEDStrip + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectIonicoControllers + @comment +\*-------------------------------------------------------------------*/ + + +RGBController_Ionico::RGBController_Ionico(IonicoController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Pcspecialist"; + description = name; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = IONICO_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = IONICO_DIRECT_BRIGHTNESS_MIN; + Direct.brightness_max = IONICO_DIRECT_BRIGHTNESS_MAX; + Direct.brightness = IONICO_DIRECT_BRIGHTNESS_MAX; + Direct.colors.resize(4); + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = IONICO_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed_min = IONICO_DIRECT_SPEED_MIN; + Breathing.speed_max = IONICO_DIRECT_SPEED_MAX; + Breathing.speed = IONICO_DIRECT_SPEED_DEFAULT; + Breathing.brightness_min = IONICO_DIRECT_BRIGHTNESS_MIN; + Breathing.brightness_max = IONICO_DIRECT_BRIGHTNESS_MAX; + Breathing.brightness = IONICO_DIRECT_BRIGHTNESS_MAX; + Breathing.colors.resize(7); + modes.push_back(Breathing); + + mode Wave; + Wave.name = "Wave"; + if(controller->GetUSBPID() == IONICO_KB_PID) + { + Wave.value = IONICO_MODE_WAVE; + } + else if(controller->GetUSBPID() == IONICO_FB_PID) + { + Wave.value = IONICO_FB_MODE_WAVE; + } + Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.speed_min = IONICO_DIRECT_SPEED_MIN; + Wave.speed_max = IONICO_DIRECT_SPEED_MAX; + Wave.speed = IONICO_DIRECT_SPEED_DEFAULT; + Wave.brightness_min = IONICO_DIRECT_BRIGHTNESS_MIN; + Wave.brightness_max = IONICO_DIRECT_BRIGHTNESS_MAX; + Wave.brightness = IONICO_DIRECT_BRIGHTNESS_MAX; + Wave.colors.resize(7); + modes.push_back(Wave); + + if(controller->GetUSBPID() == IONICO_KB_PID) + { + mode Flash; + Flash.name = "Flashing"; + Flash.value = IONICO_MODE_FLASH; + Flash.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Flash.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flash.speed_min = IONICO_DIRECT_SPEED_MIN; + Flash.speed_max = IONICO_DIRECT_SPEED_MAX; + Flash.speed = IONICO_DIRECT_SPEED_DEFAULT; + Flash.brightness_min = IONICO_DIRECT_BRIGHTNESS_MIN; + Flash.brightness_max = IONICO_DIRECT_BRIGHTNESS_MAX; + Flash.brightness = IONICO_DIRECT_BRIGHTNESS_MAX; + Flash.colors.resize(7); + modes.push_back(Flash); + } + + if(controller->GetUSBPID() == IONICO_FB_PID) + { + mode Raindrops; + Raindrops.name = "Raindrops"; + Raindrops.value = IONICO_MODE_RAIN; + Raindrops.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Raindrops.color_mode = MODE_COLORS_MODE_SPECIFIC; + Raindrops.speed_min = IONICO_DIRECT_SPEED_MIN; + Raindrops.speed_max = IONICO_DIRECT_SPEED_MAX; + Raindrops.speed = IONICO_DIRECT_SPEED_DEFAULT; + Raindrops.brightness_min = IONICO_DIRECT_BRIGHTNESS_MIN; + Raindrops.brightness_max = IONICO_DIRECT_BRIGHTNESS_MAX; + Raindrops.brightness = IONICO_DIRECT_BRIGHTNESS_MAX; + Raindrops.colors.resize(7); + modes.push_back(Raindrops); + } + + mode Off; + Off.name = "Off"; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_Ionico::~RGBController_Ionico() +{ + delete controller; +} + +void RGBController_Ionico::SetupZones() +{ + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + if(controller->GetUSBPID() == IONICO_KB_PID) + { + leds.resize(IONICO_KEYBOARD_LED_COUNT); + zone zone_keyboard; + zone_keyboard.name = "Keyboard"; + zone_keyboard.type = ZONE_TYPE_LINEAR; + zone_keyboard.leds_min = (unsigned int)leds.size(); + zone_keyboard.leds_max = (unsigned int)leds.size(); + zone_keyboard.leds_count = (unsigned int)leds.size(); + zone_keyboard.matrix_map = nullptr; + zones.emplace_back(zone_keyboard); + for(size_t i = 0; i < leds.size(); ++i) + { + leds[i].name = "Keyboard Zone " + std::to_string(i+1); + } + } + else if(controller->GetUSBPID() == IONICO_FB_PID) + { + leds.resize(IONICO_BAR_LED_COUNT); + zone zone_bar; + zone_bar.name = "Front Bar"; + zone_bar.type = ZONE_TYPE_LINEAR; + zone_bar.leds_min = (unsigned int)leds.size(); + zone_bar.leds_max = (unsigned int)leds.size(); + zone_bar.leds_count = (unsigned int)leds.size(); + zone_bar.matrix_map = nullptr; + zones.emplace_back(zone_bar); + for(size_t i = 0; i < leds.size(); ++i) + { + leds[i].name = "Bar Led " + std::to_string(i+1); + } + } + SetupColors(); +} + +void RGBController_Ionico::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Ionico::DeviceUpdateLEDs() +{ + /*---------------------------------------------------------*\ + | MODE_COLORS_PER_LED | + \*---------------------------------------------------------*/ + controller->SetColors(type, colors, false); +} + +void RGBController_Ionico::DeviceSaveMode() +{ + controller->SaveBios(); +} + +void RGBController_Ionico::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Ionico::UpdateSingleLED(int /*led*/) +{ + // +} + +void RGBController_Ionico::DeviceUpdateMode() +{ + switch (modes[active_mode].value) + { + case IONICO_MODE_OFF: + controller->TurnOff(); + break; + case IONICO_MODE_DIRECT: + if(type == DEVICE_TYPE_LEDSTRIP) + { + controller->SetMode(0x33, modes[active_mode].brightness, 0); + } + else + { + controller->SetMode(modes[active_mode].value, modes[active_mode].brightness, 0); + } + break; + default: + controller->SetMode(modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed); + controller->SetColors(type, modes[active_mode].colors, true); + break; + } +} diff --git a/Controllers/IonicoController/RGBController_Ionico.h b/Controllers/IonicoController/RGBController_Ionico.h new file mode 100644 index 0000000..24c9003 --- /dev/null +++ b/Controllers/IonicoController/RGBController_Ionico.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| RGBController_Ionico.h | +| | +| RGBController for Ionico-II-17 | +| | +| Lucas Strafe 31 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "IonicoController.h" + +#define IONICO_KB_PID 0xCE00 +#define IONICO_FB_PID 0x6005 + +class RGBController_Ionico : public RGBController +{ +public: + RGBController_Ionico(IonicoController* controller_ptr); + ~RGBController_Ionico(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void SetSingleLED(); + void UpdateSingleLED(int led); + void DeviceSaveMode(); + + void DeviceUpdateMode(); + + +private: + IonicoController* controller; +}; diff --git a/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.cpp b/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.cpp new file mode 100644 index 0000000..75f3740 --- /dev/null +++ b/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.cpp @@ -0,0 +1,270 @@ +/*---------------------------------------------------------*\ +| JGINYUEInternalUSBController.cpp | +| | +| Driver for JGINYUE USB motherboard | +| | +| Tong R (tcr020) 09 Aug 2023 | +| Liu ShiMeng(Moon dream stars) 09 Aug 2023 | +| Dongguan Yonghang Electronic Technology Co., Ltd | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController.h" +#include "JGINYUEInternalUSBController.h" +#include "dmiinfo.h" + +#define JGINYUE_USB_GENERAL_COMMAND_HEADER 0x01 +#define JGINYUE_USB_LED_STRIPE_SET_COMMAND_HEADER 0x05 +#define JGINYUE_USB_MODE_SET_COMMAND_HEADER 0x06 +#define JGINYUE_USB_PER_LED_SET_COMMAND_HEADER 0x04 + +#define JGINYUE_USB_GET_FW_VERSION 0xA0 +#define JGINYUE_USB_GET_FW_REPLY 0x5A +#define JGINYUE_RG_DEFAULT 0x01 +#define JGINYUE_RG_SWAP 0x00 + +using namespace std::chrono_literals; + +JGINYUEInternalUSBController::JGINYUEInternalUSBController(hid_device* dev_handle, const char* path) +{ + DMIInfo dmi; + + dev = dev_handle; + location = path; + device_name = "JGINYUE " + dmi.getMainboard(); + + memset(&device_config, 0x00, sizeof(device_config)); + + Init_device(device_config); +} + +JGINYUEInternalUSBController::~JGINYUEInternalUSBController() +{ + hid_close(dev); +} + +unsigned int JGINYUEInternalUSBController::GetZoneCount() +{ + return(JGINYUE_MAX_ZONES); +} + +std::string JGINYUEInternalUSBController::GetDeviceLocation() +{ + return("HID:" + location); +} + +std::string JGINYUEInternalUSBController::GetDeviceName() +{ + return(device_name); +} + +std::string JGINYUEInternalUSBController::GetSerialString() +{ + return(""); +} + +std::string JGINYUEInternalUSBController::GetDeviceFWVersion() +{ + unsigned char usb_buf[16]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = JGINYUE_USB_GENERAL_COMMAND_HEADER; + usb_buf[0x01] = JGINYUE_USB_GET_FW_VERSION; + + hid_write(dev, usb_buf, 16); + hid_read(dev, usb_buf, 16); + + if((usb_buf[0x00] != JGINYUE_USB_GENERAL_COMMAND_HEADER) || (usb_buf[0x01] != JGINYUE_USB_GET_FW_REPLY)) + { + return(""); + } + + std::string Major_version = std::to_string(usb_buf[0x02]); + std::string Minor_version = std::to_string(usb_buf[0x03]); + + return(Major_version + "." + Minor_version); +} + +void JGINYUEInternalUSBController::Init_device(AreaConfiguration* ptr_device_cfg) +{ + for(int index_config = 1; index_config <= JGINYUE_MAX_ZONES; index_config++) + { + ptr_device_cfg[index_config].Brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + ptr_device_cfg[index_config].Color_B = 0xFF; + ptr_device_cfg[index_config].Color_G = 0xFF; + ptr_device_cfg[index_config].Color_R = 0xFF; + ptr_device_cfg[index_config].RG_Swap = JGINYUE_RG_DEFAULT; + ptr_device_cfg[index_config].Speed = JGINYUE_USB_SPEED_DEFAULT; + ptr_device_cfg[index_config].LED_numbers = 0; + ptr_device_cfg[index_config].Mode_active = JGINYUE_USB_MODE_STATIC; + } +} + +void JGINYUEInternalUSBController::WriteZoneMode + ( + unsigned char zone, + unsigned char mode, + RGBColor rgb, + unsigned char speed, + unsigned char brightness, + unsigned char direction + ) +{ + int Active_zone; + unsigned char usb_buf[65]; + + switch(zone) + { + case 0x01: + Active_zone = 1; + break; + case 0x02: + Active_zone = 2; + break; + default: + Active_zone = 1; + return; + break; + } + + device_config[Active_zone].Mode_active = mode; + device_config[Active_zone].Direct_Mode_control = 0x00; + device_config[Active_zone].Speed = speed; + device_config[Active_zone].Brightness = brightness; + device_config[Active_zone].Direction = direction; + device_config[Active_zone].Color_B = RGBGetBValue(rgb); + device_config[Active_zone].Color_G = RGBGetGValue(rgb); + device_config[Active_zone].Color_R = RGBGetRValue(rgb); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = JGINYUE_USB_LED_STRIPE_SET_COMMAND_HEADER; + usb_buf[0x01] = zone; + usb_buf[0x02] = device_config[Active_zone].LED_numbers; + usb_buf[0x03] = device_config[Active_zone].RG_Swap; + usb_buf[0x04] = device_config[Active_zone].Direction; + usb_buf[0x05] = device_config[Active_zone].Direct_Mode_control; + + hid_write(dev, usb_buf, 16); + + std::this_thread::sleep_for(20ms); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = JGINYUE_USB_MODE_SET_COMMAND_HEADER; + usb_buf[0x01] = zone; + usb_buf[0x02] = device_config[Active_zone].Mode_active; + usb_buf[0x03] = device_config[Active_zone].Color_R; + usb_buf[0x04] = device_config[Active_zone].Color_G; + usb_buf[0x05] = device_config[Active_zone].Color_B; + usb_buf[0x06] = device_config[Active_zone].Brightness; + usb_buf[0x07] = device_config[Active_zone].Speed; + + hid_write(dev, usb_buf, 16); +} + +void JGINYUEInternalUSBController::DirectLEDControl + ( + RGBColor* colors, + unsigned char zone + ) +{ + int Active_zone; + unsigned char usb_buf[302]; + + switch(zone) + { + case 0x01: + Active_zone = 1; + break; + case 0x02: + Active_zone = 2; + break; + default: + Active_zone = 1; + return; + break; + } + + if(device_config[Active_zone].Mode_active != JGINYUE_USB_MODE_DIRECT) + { + device_config[Active_zone].Mode_active =JGINYUE_USB_MODE_DIRECT; + device_config[Active_zone].Direct_Mode_control =0x01; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = JGINYUE_USB_LED_STRIPE_SET_COMMAND_HEADER; + usb_buf[0x01] = zone; + usb_buf[0x02] = device_config[Active_zone].LED_numbers; + usb_buf[0x03] = device_config[Active_zone].RG_Swap; + usb_buf[0x04] = device_config[Active_zone].Direction; + usb_buf[0x05] = device_config[Active_zone].Direct_Mode_control; + + hid_write(dev, usb_buf, 16); + } + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = JGINYUE_USB_PER_LED_SET_COMMAND_HEADER; + usb_buf[0x01] = zone; + + for(unsigned int color_idx = 0; color_idx < device_config[Active_zone].LED_numbers; color_idx++) + { + usb_buf[color_idx * 3 + 2] = RGBGetRValue(colors[color_idx]); + usb_buf[color_idx * 3 + 3] = RGBGetGValue(colors[color_idx]); + usb_buf[color_idx * 3 + 4] = RGBGetBValue(colors[color_idx]); + } + + hid_send_feature_report(dev, usb_buf, 302); +} + +void JGINYUEInternalUSBController::Area_resize(unsigned char led_numbers, unsigned char zone) +{ + unsigned char usb_buf[65]; + int Active_zone; + + switch(zone) + { + case 0x01: + Active_zone = 1; + break; + case 0x02: + Active_zone = 2; + break; + default: + Active_zone = 1; + return; + break; + } + + device_config[Active_zone].LED_numbers = led_numbers; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = JGINYUE_USB_LED_STRIPE_SET_COMMAND_HEADER; + usb_buf[0x01] = zone; + usb_buf[0x02] = device_config[Active_zone].LED_numbers; + usb_buf[0x03] = device_config[Active_zone].RG_Swap; + usb_buf[0x04] = device_config[Active_zone].Direction; + usb_buf[0x05] = device_config[Active_zone].Direct_Mode_control; + + hid_write(dev, usb_buf, 16); +} + +void JGINYUEInternalUSBController::SetRGSwap(unsigned char RGSwap) +{ + if((RGSwap != 0x00) && (RGSwap != 0x01)) + { + return; + } + + for(int index_config=1; index_config <=JGINYUE_MAX_ZONES; index_config++) + { + device_config[index_config].RG_Swap = RGSwap; + } +} diff --git a/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.h b/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.h new file mode 100644 index 0000000..71c09d5 --- /dev/null +++ b/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.h @@ -0,0 +1,110 @@ +/*---------------------------------------------------------*\ +| JGINYUEInternalUSBController.h | +| | +| Driver for JGINYUE USB motherboard | +| | +| Tong R (tcr020) 09 Aug 2023 | +| Liu ShiMeng(Moon dream stars) 09 Aug 2023 | +| Dongguan Yonghang Electronic Technology Co., Ltd | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define JGINYUE_MAX_ZONES 2 +#define JGINYUE_ADDRESSABLE_MAX_LEDS 100 + +enum +{ + JGINYUE_USB_MODE_OFF = 0x10, + JGINYUE_USB_MODE_STATIC = 0x11, + JGINYUE_USB_MODE_BREATHING = 0x12, + JGINYUE_USB_MODE_STROBE = 0x13, + JGINYUE_USB_MODE_CYCLING = 0x14, + JGINYUE_USB_MODE_RANDOM = 0x15, + JGINYUE_USB_MODE_MUSIC = 0x16, /* music mode,not support yet */ + JGINYUE_USB_MODE_WAVE = 0x17, + JGINYUE_USB_MODE_SPRING = 0x18, /* spring mode,not support yet */ + JGINYUE_USB_MODE_WATER = 0x19, + JGINYUE_USB_MODE_RAINBOW = 0x1A, /* rainbow mode,not support yet */ + JGINYUE_USB_MODE_DIRECT = 0x20, /* Not the exact USB protcol - but need a way to differentiate */ +}; + +enum +{ + JGINYUE_USB_SPEED_MAX = 0xFF, + JGINYUE_USB_SPEED_MIN = 0x00, + JGINYUE_USB_SPEED_DEFAULT = 0x80 +}; + +enum +{ + JGINYUE_DIRECTION_RIGHT = 0x00, + JGINYUE_DIRECTION_LEFT = 0x01 +}; + +enum +{ + JGINYUE_USB_BRIGHTNESS_MAX = 0xFF, + JGINYUE_USB_BRIGHTNESS_MIN = 0x00, + JGINYUE_USB_BRIGHTNESS_DEFAULT = 0xFF +}; + +struct AreaConfiguration +{ + unsigned char LED_numbers; + unsigned char RG_Swap; + unsigned char Direction; + unsigned char Direct_Mode_control; /* 0x00 = Disabled, 0x01 = Enabled */ + unsigned char Mode_active; + unsigned char Color_R; + unsigned char Color_G; + unsigned char Color_B; + unsigned char Brightness; + unsigned char Speed; +}; + +class JGINYUEInternalUSBController +{ +public: + JGINYUEInternalUSBController(hid_device* dev_handle, const char* path); + ~JGINYUEInternalUSBController(); + + unsigned int GetZoneCount(); + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + std::string GetDeviceFWVersion(); + + void WriteZoneMode + ( + unsigned char zone, + unsigned char mode, + RGBColor rgb, + unsigned char speed, + unsigned char brightness, + unsigned char direction + ); + + void DirectLEDControl + ( + RGBColor* colors, + unsigned char zone + ); + + void SetRGSwap(unsigned char RGSwap); + void Init_device(AreaConfiguration* ptr_device_cfg); + void Area_resize(unsigned char led_numbers,unsigned char zone); + +private: + AreaConfiguration device_config[8]; + hid_device* dev; + std::string location; + std::string device_name; +}; diff --git a/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBControllerDetect.cpp b/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBControllerDetect.cpp new file mode 100644 index 0000000..d093eb2 --- /dev/null +++ b/Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBControllerDetect.cpp @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| JGINYUEInternalUSBControllerDetect.cpp | +| | +| Detector for JGINYUE USB motherboard | +| | +| Tong R (tcr020) 09 Aug 2023 | +| Liu ShiMeng(Moon dream stars) 09 Aug 2023 | +| Dongguan Yonghang Electronic Technology Co., Ltd | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_JGINYUEInternalUSB.h" +#include "JGINYUEInternalUSBController.h" +#include "Detector.h" + +/*---------------------------------------------------------*\ +| JGINYUE vendor ID | +\*---------------------------------------------------------*/ +#define JGINYUE_VID 0x0416 + +/*---------------------------------------------------------*\ +| JGINYUE product ID | +\*---------------------------------------------------------*/ +#define JGINYUE_MOTHERBOARD_PID 0xA125 + +void DetectJGINYUEInternalUSBController(hid_device_info* info,const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + JGINYUEInternalUSBController* controller =new JGINYUEInternalUSBController(dev,info->path); + RGBController_JGINYUEInternalUSB* rgb_controller =new RGBController_JGINYUEInternalUSB(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("JGINYUE Internal USB Controller", DetectJGINYUEInternalUSBController, JGINYUE_VID, JGINYUE_MOTHERBOARD_PID); diff --git a/Controllers/JGINYUEInternalUSBController/RGBController_JGINYUEInternalUSB.cpp b/Controllers/JGINYUEInternalUSBController/RGBController_JGINYUEInternalUSB.cpp new file mode 100644 index 0000000..684bd24 --- /dev/null +++ b/Controllers/JGINYUEInternalUSBController/RGBController_JGINYUEInternalUSB.cpp @@ -0,0 +1,350 @@ +/*---------------------------------------------------------*\ +| RGBController_JGINYUEInternalUSB.cpp | +| | +| RGBController for JGINYUE USB motherboard | +| | +| Tong R (tcr020) 09 Aug 2023 | +| Liu ShiMeng(Moon dream stars) 09 Aug 2023 | +| Dongguan Yonghang Electronic Technology Co., Ltd | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_JGINYUEInternalUSB.h" + +#define JGINYUE_MAX_ZONES 2 +#define JGINYUE_ADDRESSABLE_MAX_LEDS 100 + +/**------------------------------------------------------------------*\ + @name JGINYUEInternalUSB + @category MotherBoard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectJGINYUEInternalUSB + @comment Insert multiline JGINYUEInternalUSB comment here +\*--------------------------------------------------------------------*/ + +RGBController_JGINYUEInternalUSB::RGBController_JGINYUEInternalUSB(JGINYUEInternalUSBController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + description = "JGINYUE USB ARGB Device"; + vendor = "JGINYUE"; + type = DEVICE_TYPE_MOTHERBOARD; + location = controller->GetDeviceLocation(); + version = controller->GetDeviceFWVersion(); + + mode Off; + Off.name = "Off"; + Off.value = JGINYUE_USB_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = JGINYUE_USB_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_max = 1; + Static.colors_min = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = JGINYUE_USB_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_max = 1; + Breathing.colors_min = 1; + Breathing.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Breathing.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Breathing.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Breathing.speed = JGINYUE_USB_SPEED_DEFAULT; + Breathing.speed_max = JGINYUE_USB_SPEED_MAX; + Breathing.speed_min = JGINYUE_USB_SPEED_MIN; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Strobe; + Strobe.name = "Strobe"; + Strobe.value = JGINYUE_USB_MODE_STROBE; + Strobe.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Strobe.color_mode = MODE_COLORS_MODE_SPECIFIC; + Strobe.colors_max = 1; + Strobe.colors_min = 1; + Strobe.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Strobe.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Strobe.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Strobe.speed = JGINYUE_USB_SPEED_DEFAULT; + Strobe.speed_max = JGINYUE_USB_SPEED_MAX; + Strobe.speed_min = JGINYUE_USB_SPEED_MIN; + Strobe.colors.resize(1); + modes.push_back(Strobe); + + mode Cycling; + Cycling.name = "Cycling"; + Cycling.value = JGINYUE_USB_MODE_CYCLING; + Cycling.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycling.color_mode = MODE_COLORS_NONE; + Cycling.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Cycling.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Cycling.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Cycling.speed = JGINYUE_USB_SPEED_DEFAULT; + Cycling.speed_max = JGINYUE_USB_SPEED_MAX; + Cycling.speed_min = JGINYUE_USB_SPEED_MIN; + modes.push_back(Cycling); + + mode Random; + Random.name = "Random"; + Random.value = JGINYUE_USB_MODE_RANDOM; + Random.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Random.color_mode = MODE_COLORS_NONE; + Random.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Random.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Random.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Random.speed = JGINYUE_USB_SPEED_DEFAULT; + Random.speed_max = JGINYUE_USB_SPEED_MAX; + Random.speed_min = JGINYUE_USB_SPEED_MIN; + modes.push_back(Random); + + mode Wave; + Wave.name = "Wave"; + Wave.value = JGINYUE_USB_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED; + Wave.color_mode = MODE_COLORS_NONE; + Wave.speed = JGINYUE_USB_SPEED_DEFAULT; + Wave.speed_max = JGINYUE_USB_SPEED_MAX; + Wave.speed_min = JGINYUE_USB_SPEED_MIN; + modes.push_back(Wave); + + //mode Spring; + //Spring.name = "Spring"; + //Spring.value = JGINYUE_USB_MODE_SPRING; + //Spring.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + //Spring.color_mode = MODE_COLORS_NONE; + //Spring.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + //Spring.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + //Spring.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + //Spring.speed = JGINYUE_USB_SPEED_DEFAULT; + //Spring.speed_max = JGINYUE_USB_SPEED_MAX; + //Spring.speed_min = JGINYUE_USB_SPEED_MIN; + //Spring.direction = 0x00; + //modes.push_back(Spring); + + mode Water; + Water.name = "Water"; + Water.value = JGINYUE_USB_MODE_WATER; + Water.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Water.color_mode = MODE_COLORS_NONE; + Water.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Water.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Water.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Water.speed = JGINYUE_USB_SPEED_DEFAULT; + Water.speed_max = JGINYUE_USB_SPEED_MAX; + Water.speed_min = JGINYUE_USB_SPEED_MIN; + Water.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Water); + + //mode Rainbow; + //Rainbow.name = "Rainbow"; + //Rainbow.value = JGINYUE_USB_MODE_RAINBOW; + //Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + //Rainbow.color_mode = MODE_COLORS_NONE; + //Rainbow.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + //Rainbow.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + //Rainbow.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + //Rainbow.speed = JGINYUE_USB_SPEED_DEFAULT; + //Rainbow.speed_max = JGINYUE_USB_SPEED_MAX; + //Rainbow.speed_min = JGINYUE_USB_SPEED_MIN; + //Rainbow.direction = MODE_DIRECTION_RIGHT; + //modes.push_back(Rainbow); + + mode Direct; + Direct.name = "Direct"; + Direct.value = JGINYUE_USB_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_JGINYUEInternalUSB::~RGBController_JGINYUEInternalUSB() +{ + delete controller; +} + +void RGBController_JGINYUEInternalUSB::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(JGINYUE_MAX_ZONES); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + zones[0].name = "ARGB Header 1"; + zones[0].type = ZONE_TYPE_LINEAR; + zones[0].leds_min = 0; + zones[0].leds_max = 100; + zones[0].matrix_map = NULL; + + zones[1].name = "ARGB Header 2"; + zones[1].type = ZONE_TYPE_LINEAR; + zones[1].leds_min = 0; + zones[1].leds_max = 100; + zones[1].matrix_map = NULL; + + if(first_run) + { + zones[0].leds_count = 0; + zones[1].leds_count = 0; + } + + for(unsigned int zone_idx = 0; zone_idx < JGINYUE_MAX_ZONES; zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + new_led.name = "ARGB Header " + std::to_string(zone_idx + 1) + " LED " + std::to_string(led_idx + 1); + new_led.value = led_idx; + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_JGINYUEInternalUSB::ResizeZone(int zone, int new_size) +{ + unsigned char area; + + switch(zone) + { + case 0: + area = 0x01; + break; + case 1: + area = 0x02; + break; + default: + area = 0x01; + break; + } + + zones[zone].leds_count = new_size; + + SetupZones(); + + controller->Area_resize(new_size, area); +} + +void RGBController_JGINYUEInternalUSB::DeviceUpdateLEDs() +{ + for(int i = 0; i < JGINYUE_MAX_ZONES; i++) + { + UpdateZoneLEDs(i); + } +} + +void RGBController_JGINYUEInternalUSB::UpdateZoneLEDs(int zone) +{ + unsigned char area; + + switch(zone) + { + case 0: + area = 0x01; + break; + case 1: + area = 0x02; + break; + default: + area = 0x01; + break; + } + + controller->DirectLEDControl(zones[zone].colors,area); +} + +void RGBController_JGINYUEInternalUSB::UpdateSingleLED(int led) +{ + int zone; + zone = leds[led].value; + UpdateZoneLEDs(zone); +} + +void RGBController_JGINYUEInternalUSB::DeviceUpdateMode() +{ + unsigned char area; + + if(modes[active_mode].value == JGINYUE_USB_MODE_DIRECT) + { + DeviceUpdateLEDs(); + } + else + { + unsigned char aim_direction = JGINYUE_DIRECTION_RIGHT; + unsigned char aim_speed = JGINYUE_USB_SPEED_DEFAULT; + unsigned char aim_brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + RGBColor aim_rgb = 0x00FFFFFF; + + if(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + aim_direction = modes[active_mode].direction; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + aim_speed = modes[active_mode].speed; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + aim_brightness = modes[active_mode].brightness; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + aim_rgb = modes[active_mode].colors[0]; + } + + for(int zone_index = 0; zone_index < JGINYUE_MAX_ZONES; zone_index++) + { + switch(zone_index) + { + case 0: + area = 0x01; + break; + case 1: + area = 0x02; + break; + default: + area = 0x01; + break; + } + + controller->WriteZoneMode(area, modes[active_mode].value, aim_rgb, aim_speed, aim_brightness, aim_direction); + } + } +} diff --git a/Controllers/JGINYUEInternalUSBController/RGBController_JGINYUEInternalUSB.h b/Controllers/JGINYUEInternalUSBController/RGBController_JGINYUEInternalUSB.h new file mode 100644 index 0000000..bb72812 --- /dev/null +++ b/Controllers/JGINYUEInternalUSBController/RGBController_JGINYUEInternalUSB.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_JGINYUEInternalUSB.h | +| | +| RGBController for JGINYUE USB motherboard | +| | +| Tong R (tcr020) 09 Aug 2023 | +| Liu ShiMeng(Moon dream stars) 09 Aug 2023 | +| Dongguan Yonghang Electronic Technology Co., Ltd | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "JGINYUEInternalUSBController.h" + +class RGBController_JGINYUEInternalUSB : public RGBController +{ +public: + RGBController_JGINYUEInternalUSB(JGINYUEInternalUSBController* controller_ptr); + ~RGBController_JGINYUEInternalUSB(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + JGINYUEInternalUSBController* controller; +}; diff --git a/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.cpp b/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.cpp new file mode 100644 index 0000000..6e042da --- /dev/null +++ b/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.cpp @@ -0,0 +1,260 @@ +/*---------------------------------------------------------*\ +| JGINYUEInternalUSBV2Controller.cpp | +| | +| Driver for JGINYUE USB motherboard V2 | +| | +| Tong R (tcr020) 08 Aug 2024 | +| Liu ShiMeng(Moon dream stars) 09 Aug 2023 | +| Dongguan Yonghang Electronic Technology Co., Ltd | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "RGBController.h" +#include "ResourceManager.h" +#include "SettingsManager.h" +#include "JGINYUEInternalUSBV2Controller.h" +#include "LogManager.h" +#include "dmiinfo.h" + +#define JGINYUE_V2_HID_GENERAL_COMMAND_HEADER 0x01 +#define JGINYUE_V2_CDC_COMMAND_HEADER 0x10 + +#define JGINYUE_V2_HID_REQUEST_MCU_STATUS 0x21 +#define JGINYUE_V2_HID_REQUEST_MCUID 0x22 +#define JGINYUE_V2_HID_REQUEST_UNLOCK 0x23 +#define JGINYUE_V2_HID_DOWNLOAD_SKU 0x24 +#define JGINYUE_V2_HID_REQUEST_MCU_STATUS_EX 0x0F +#define JGINYUE_V2_HID_DOWNLOAD_ARGB_SETTING 0x01 +#define JGINYUE_V2_HID_DOWNLOAD_GLOBAL_SETTING 0x02 +#define JGINYUE_V2_HID_REQUEST_ARGB_SETTING 0x11 +#define JGINYUE_V2_HID_REQUEST_GLOBAL_SETTING 0x12 + + + +#define FUNCTION_ID_CDC_ARGB 0x01 + +using namespace std::chrono_literals; + +JGINYUEInternalUSBV2Controller::JGINYUEInternalUSBV2Controller(hid_device* jy_hid_device, const char* path,serial_port* jy_cdc_device) +{ + DMIInfo dmi; + jy_hid_interface = jy_hid_device; + jy_cdc_interface = jy_cdc_device; + location = path; + device_name = "JGINYUE " + dmi.getMainboard() + " Internal USB Controller V2"; + ZoneCount = 0; + support_Global_zone = false; + Init_device(); +} + +JGINYUEInternalUSBV2Controller::~JGINYUEInternalUSBV2Controller() +{ + hid_close(jy_hid_interface); + if(jy_cdc_interface != nullptr) + { + delete jy_cdc_interface; + } +} + +unsigned int JGINYUEInternalUSBV2Controller::GetZoneCount() +{ + return(ZoneCount); +} + +std::string JGINYUEInternalUSBV2Controller::GetDeviceLocation() +{ + return("HID:" + location); +} + +std::string JGINYUEInternalUSBV2Controller::GetDeviceName() +{ + return(device_name); +} + +std::string JGINYUEInternalUSBV2Controller::GetSerialString() +{ + return(""); +} + +std::string JGINYUEInternalUSBV2Controller::GetDeviceFWVersion() +{ + return(""); +} + +void JGINYUEInternalUSBV2Controller::Init_device() +{ + unsigned char usb_buf[64]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0] = JGINYUE_V2_HID_GENERAL_COMMAND_HEADER; + usb_buf[1] = 0x0F; + + int write_result = hid_write(jy_hid_interface, usb_buf, 64); + if(write_result < 0) + { + LOG_ERROR("[JGINYUEInternalUSBV2Controller] Failed to write to JGINYUE device during initialization"); + ZoneCount = 0x00; + memset(device_config, 0x00, sizeof(device_config)); + memset(&device_config_Global, 0x00, sizeof(device_config_Global)); + return; + } + + std::this_thread::sleep_for(20ms); + + int bytes_read = hid_read_timeout(jy_hid_interface, usb_buf, 64, 1000); + if(bytes_read <= 0 || usb_buf[1] != 0x0F) + { + LOG_ERROR("[JGINYUEInternalUSBV2Controller] JGINYUE device did not respond or invalid response (bytes read: %d)", bytes_read); + ZoneCount = 0x00; + memset(device_config, 0x00, sizeof(device_config)); + memset(&device_config_Global, 0x00, sizeof(device_config_Global)); + return; + } + unsigned char Zone_Info = usb_buf[4]; + for(unsigned char i = 0; i < 8; i ++) + { + if(Zone_Info & (1<>4; + device_config[zone].Speed = usb_buf[6]; + device_config[zone].Brightness = usb_buf[7]; + memcpy(&(device_config[zone].Color_Array[0]),&(usb_buf[8]),Color_num*3); + return; +} + +void JGINYUEInternalUSBV2Controller::WriteZoneMode + ( + unsigned char Area, + unsigned char Mode, + unsigned char Num_LED, + std::vector rgb, + unsigned char Speed, + unsigned char Brightness, + unsigned char Direction + ) +{ + unsigned char usb_buf[64]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + unsigned char num_color = (unsigned char)rgb.size(); + num_color = (num_color < 8) ? num_color : 8; + + usb_buf[0] = JGINYUE_V2_HID_GENERAL_COMMAND_HEADER; + usb_buf[1] = JGINYUE_V2_HID_DOWNLOAD_ARGB_SETTING; + usb_buf[2] = Area; + usb_buf[3] = Mode; + usb_buf[4] = Num_LED; + usb_buf[5] = (num_color&0x0F)|((Direction&0x01)<<4); + usb_buf[6] = Speed; + usb_buf[7] = Brightness; + + for(unsigned char i = 0; i < num_color; i++) + { + RGBColor color = rgb[i]; + usb_buf[8+i*3] = RGBGetRValue(color); + usb_buf[9+i*3] = RGBGetGValue(color); + usb_buf[10+i*3] = RGBGetBValue(color); + } + + hid_write(jy_hid_interface, usb_buf, 64); + std::this_thread::sleep_for(10ms); +} + +void JGINYUEInternalUSBV2Controller::DirectLEDControl + ( + RGBColor* colors, + unsigned char num_LEDs, + unsigned char Area + ) +{ + /*-----------------------------------------------------*\ + | Direct mode requires CDC interface | + | If CDC is not available, log error and return | + \*-----------------------------------------------------*/ + if(jy_cdc_interface == nullptr) + { + LOG_WARNING("[JGINYUEInternalUSBV2Controller] Direct mode requires serial port (CDC) which is not available. Use other modes instead."); + return; + } + + unsigned char cdc_buf[512]; + memset(cdc_buf, 0x00, sizeof(cdc_buf)); + cdc_buf[0] = JGINYUE_V2_CDC_COMMAND_HEADER; + cdc_buf[1] = FUNCTION_ID_CDC_ARGB; + cdc_buf[2] = Area; + cdc_buf[3] = 0x20; + cdc_buf[4] = num_LEDs; + + for(unsigned char i = 0; i < num_LEDs; i++) + { + cdc_buf[8+i*3] = RGBGetRValue(colors[i]); + cdc_buf[9+i*3] = RGBGetGValue(colors[i]); + cdc_buf[10+i*3] = RGBGetBValue(colors[i]); + } + int TX_len = 10 + num_LEDs*3; + if(TX_len%64 == 0) + { + TX_len = TX_len+2; + } + + jy_cdc_interface->serial_write((char*)cdc_buf,TX_len); +} diff --git a/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.h b/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.h new file mode 100644 index 0000000..66e627b --- /dev/null +++ b/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.h @@ -0,0 +1,139 @@ +/*---------------------------------------------------------*\ +| JGINYUEInternalUSBV2Controller.h | +| | +| Driver for JGINYUE USB motherboard V2 | +| | +| Tong R (tcr020) 09 Aug 2023 | +| Liu ShiMeng(Moon dream stars) 06 Aug 2024 | +| Dongguan Yonghang Electronic Technology Co., Ltd | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "serial_port.h" +#include "RGBController.h" + +#define JGINYUE_MAX_ZONES 2 +#define JGINYUE_ADDRESSABLE_MAX_LEDS 100 + +enum +{ + JGINYUE_USB_V2_MODE_SYNC = 0x0F, + JGINYUE_USB_V2_MODE_OFF = 0x10, + JGINYUE_USB_V2_MODE_STATIC = 0x11, + JGINYUE_USB_V2_MODE_BREATHING = 0x12, + JGINYUE_USB_V2_MODE_CYCLING = 0x14, + JGINYUE_USB_V2_MODE_RANDOM = 0x15, + JGINYUE_USB_V2_MODE_SPRING = 0x16, + JGINYUE_USB_V2_MODE_WAVE = 0x17, + JGINYUE_USB_V2_MODE_WATER = 0x19, + JGINYUE_USB_V2_MODE_RAINBOW = 0x1A, + JGINYUE_USB_V2_MODE_MULTICOLOR_WAVE = 0x1B, + JGINYUE_USB_V2_MODE_MULTICOLOR_CYCLING = 0x1C, + JGINYUE_USB_V2_MODE_SUNRISE = 0x1D, + JGINYUE_USB_V2_MODE_ROTATE_STAR = 0x1E, + JGINYUE_USB_V2_MODE_METEOR = 0x1F, + JGINYUE_USB_V2_MODE_DIRECT = 0x20, + JGINYUE_USB_V2_MODE_CYCLING_BREATHING = 0x21, + JGINYUE_USB_V2_MODE_CYCLING_RAINING = 0x22, + JGINYUE_USB_V2_MODE_MULTICOLOR_WATER_2 = 0x23, + JGINYUE_USB_V2_MODE_MULTICOLOR_WATER_1 = 0x24, + JGINYUE_USB_V2_MODE_HOURGLASS = 0x25 +}; + +enum +{ + JGINYUE_USB_SPEED_MAX = 0xFF, + JGINYUE_USB_SPEED_MIN = 0x00, + JGINYUE_USB_SPEED_DEFAULT = 0x80 +}; + +enum +{ + JGINYUE_DIRECTION_RIGHT = 0x00, + JGINYUE_DIRECTION_LEFT = 0x01 +}; + +enum +{ + JGINYUE_USB_BRIGHTNESS_MAX = 0xFF, + JGINYUE_USB_BRIGHTNESS_MIN = 0x00, + JGINYUE_USB_BRIGHTNESS_DEFAULT = 0xFF +}; + +enum +{ + JGINYUE_USB_V2_ARGB_STRIP_1 = 0x01, + JGINYUE_USB_V2_ARGB_STRIP_2 = 0x02, + JGINYUE_USB_V2_ARGB_FAN_1 = 0x04, + JGINYUE_USB_V2_ARGB_FAN_2 = 0x08, + JGINYUE_USB_V2_ARGB_FAN_3 = 0x10, + JGINYUE_USB_V2_ARGB_FAN_4 = 0x20, + JGINYUE_USB_V2_ARGB_FAN_5 = 0x40, +}; + +struct AreaConfigurationV2 +{ + unsigned char Area_ID; + unsigned char Max_LED_numbers; + unsigned char User_LED_numbers; + unsigned char Direction; + unsigned char Direct_Mode_control; /* 0x00 = Disabled, 0x01 = Enabled */ + unsigned char Mode_active; + unsigned char Color_num; + unsigned char Color_Array[30]; + unsigned char Brightness; + unsigned char Speed; +}; + +class JGINYUEInternalUSBV2Controller +{ +public: + JGINYUEInternalUSBV2Controller(hid_device* jy_hid_device, const char* path,serial_port* jy_cdc_device); + ~JGINYUEInternalUSBV2Controller(); + + unsigned int GetZoneCount(); + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + std::string GetDeviceFWVersion(); + + void WriteZoneMode + ( + unsigned char Area, + unsigned char Mode, + unsigned char Num_LED, + std::vector rgb, + unsigned char Speed, + unsigned char Brightness, + unsigned char Direction + ); + + void DirectLEDControl + ( + RGBColor* colors, + unsigned char num_LEDs, + unsigned char Area + ); + + AreaConfigurationV2 device_config[8]; + //TODO,When the perzone mode is supported, these parameters will be used to download device configuartion from the device + AreaConfigurationV2 device_config_Global; + //TODO,Can sync its data to other zones,will be used once the perzone mode is supported + bool support_Global_zone; + +private: + void Init_device(); + void Init_Zone(int zone); + unsigned char ZoneCount; + + hid_device* jy_hid_interface; + serial_port* jy_cdc_interface; + std::string location; + std::string device_name; +}; diff --git a/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2ControllerDetect.cpp b/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2ControllerDetect.cpp new file mode 100644 index 0000000..9530e4f --- /dev/null +++ b/Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2ControllerDetect.cpp @@ -0,0 +1,99 @@ +/*---------------------------------------------------------*\ +| JGINYUEInternalUSBV2ControllerDetect.cpp | +| | +| Detector for JGINYUE USB motherboard V2 | +| | +| Tong R (tcr020) 06 Aug 2024 | +| Liu ShiMeng(Moon dream stars) 09 Aug 2023 | +| Dongguan Yonghang Electronic Technology Co., Ltd | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "serial_port.h" +#include "find_usb_serial_port.h" +#include "RGBController_JGINYUEInternalUSBV2.h" +#include "JGINYUEInternalUSBV2Controller.h" +#include "RGBController.h" +#include "Detector.h" +#include "dmiinfo.h" +#include "LogManager.h" +/*---------------------------------------------------------*\ +| JGINYUE vendor ID | +\*---------------------------------------------------------*/ +#define JGINYUE_VID_V2 0x1A86 + +/*---------------------------------------------------------*\ +| JGINYUE product ID | +\*---------------------------------------------------------*/ +#define JGINYUE_MOTHERBOARD_PID_V2 0xE30B + +void DetectJGINYUEInternalUSBV2Controller(hid_device_info* info,const std::string& /*name*/) +{ + hid_device* hid_dev = hid_open_path(info->path); + if(hid_dev == nullptr ) + { + return; + } + + DMIInfo dmi_info; + std::string manufacturer = dmi_info.getManufacturer(); + std::transform(manufacturer.begin(), manufacturer.end(), manufacturer.begin(), ::toupper); + if(manufacturer.find("JGINYUE") == std::string::npos) + { + LOG_INFO("[JGINYUEInternalUSBV2ControllerDetect] JGINYUE Internal USB ControllerV2 not found,error manufacturer name:%s",manufacturer.c_str()); + hid_close(hid_dev); + return; + } + LOG_INFO("[JGINYUEInternalUSBV2ControllerDetect] Pass manufacture name check.Start to init HID and CDC interface"); + + + if(hid_dev != nullptr ) + { + serial_port *port = nullptr; + std::vector serial_ports = find_usb_serial_port(JGINYUE_VID_V2, JGINYUE_MOTHERBOARD_PID_V2); + + if(serial_ports.size() == 0) + { + LOG_WARNING("[JGINYUEInternalUSBV2ControllerDetect] JGINYUE device found but no serial port detected - Direct mode will be unavailable"); + } + else if(serial_ports.size() > 1) + { + LOG_WARNING("[JGINYUEInternalUSBV2ControllerDetect] Multiple serial ports found for JGINYUE device, using first one"); + } + + if(serial_ports.size() >= 1) + { + port = new serial_port(); + if(!port->serial_open(serial_ports[0]->c_str(), 115200)) + { + LOG_WARNING("[JGINYUEInternalUSBV2ControllerDetect] Failed to open serial port %s - Direct mode will be unavailable. HID modes will still work.", serial_ports[0]->c_str()); + delete port; + port = nullptr; + } + } + + /*-----------------------------------------------------*\ + | Clean up serial port string vector | + \*-----------------------------------------------------*/ + for(std::string* str_ptr : serial_ports) + { + delete str_ptr; + } + + JGINYUEInternalUSBV2Controller *controller = new JGINYUEInternalUSBV2Controller(hid_dev, info->path, port); + RGBController_JGINYUEInternalUSBV2 *rgb_controller = new RGBController_JGINYUEInternalUSBV2(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +#ifdef _WIN32 +REGISTER_HID_DETECTOR("JGINYUE Internal USB ControllerV2", DetectJGINYUEInternalUSBV2Controller, JGINYUE_VID_V2, JGINYUE_MOTHERBOARD_PID_V2); +#else +REGISTER_HID_DETECTOR_IPU("JGINYUE Internal USB ControllerV2", DetectJGINYUEInternalUSBV2Controller, JGINYUE_VID_V2, JGINYUE_MOTHERBOARD_PID_V2, 0, 0xFF00, 1); +#endif diff --git a/Controllers/JGINYUEInternalUSBV2Controller/RGBController_JGINYUEInternalUSBV2.cpp b/Controllers/JGINYUEInternalUSBV2Controller/RGBController_JGINYUEInternalUSBV2.cpp new file mode 100644 index 0000000..217e708 --- /dev/null +++ b/Controllers/JGINYUEInternalUSBV2Controller/RGBController_JGINYUEInternalUSBV2.cpp @@ -0,0 +1,511 @@ +/*---------------------------------------------------------*\ +| RGBController_JGINYUEInternalUSBV2.cpp | +| | +| RGBController for JGINYUE USB motherboard V2 | +| | +| Tong R (tcr020) 03 July 2024 | +| Liu ShiMeng(Moon dream stars) 09 Aug 2023 | +| Dongguan Yonghang Electronic Technology Co., Ltd | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_JGINYUEInternalUSBV2.h" + +#define JGINYUE_MAX_ZONES 2 +#define JGINYUE_ADDRESSABLE_MAX_LEDS 100 + +/**------------------------------------------------------------------*\ + @name JGINYUEInternalUSBV2 + @category MotherBoard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectJGINYUEInternalUSBV2 + @comment Insert multiline JGINYUEInternalUSBV2 comment here +\*--------------------------------------------------------------------*/ + +RGBController_JGINYUEInternalUSBV2::RGBController_JGINYUEInternalUSBV2(JGINYUEInternalUSBV2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + description = "JGINYUE USB ARGB Device"; + vendor = "JGINYUE"; + type = DEVICE_TYPE_MOTHERBOARD; + location = controller->GetDeviceLocation(); + version = controller->GetDeviceFWVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = JGINYUE_USB_V2_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = JGINYUE_USB_V2_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = JGINYUE_USB_V2_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR|MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_max = 1; + Static.colors_min = 1; + Static.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Static.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Static.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = JGINYUE_USB_V2_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_max = 1; + Breathing.colors_min = 1; + Breathing.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Breathing.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Breathing.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Breathing.speed = JGINYUE_USB_SPEED_DEFAULT; + Breathing.speed_max = JGINYUE_USB_SPEED_MAX; + Breathing.speed_min = JGINYUE_USB_SPEED_MIN; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Cycling; + Cycling.name = "Cycling"; + Cycling.value = JGINYUE_USB_V2_MODE_CYCLING; + Cycling.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Cycling.color_mode = MODE_COLORS_NONE; + Cycling.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Cycling.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Cycling.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Cycling.speed = JGINYUE_USB_SPEED_DEFAULT; + Cycling.speed_max = JGINYUE_USB_SPEED_MAX; + Cycling.speed_min = JGINYUE_USB_SPEED_MIN; + Cycling.direction = JGINYUE_DIRECTION_RIGHT; + modes.push_back(Cycling); + + mode Random; + Random.name = "Random"; + Random.value = JGINYUE_USB_V2_MODE_RANDOM; + Random.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Random.color_mode = MODE_COLORS_NONE; + Random.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Random.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Random.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Random.speed = JGINYUE_USB_SPEED_DEFAULT; + Random.speed_max = JGINYUE_USB_SPEED_MAX; + Random.speed_min = JGINYUE_USB_SPEED_MIN; + modes.push_back(Random); + + mode Wave; + Wave.name = "Wave"; + Wave.value = JGINYUE_USB_V2_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS| MODE_FLAG_HAS_DIRECTION_LR; + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wave.colors_max = 1; + Wave.colors_min = 1; + Wave.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Wave.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Wave.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Wave.speed = JGINYUE_USB_SPEED_DEFAULT; + Wave.speed_max = JGINYUE_USB_SPEED_MAX; + Wave.speed_min = JGINYUE_USB_SPEED_MIN; + Wave.direction = JGINYUE_DIRECTION_LEFT; + Wave.colors.resize(1); + modes.push_back(Wave); + + mode Spring; + Spring.name = "Spring"; + Spring.value = JGINYUE_USB_V2_MODE_SPRING; + Spring.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS| MODE_FLAG_HAS_DIRECTION_LR; + Spring.color_mode = MODE_COLORS_MODE_SPECIFIC; + Spring.colors_max = 1; + Spring.colors_min = 1; + Spring.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Spring.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Spring.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Spring.speed = JGINYUE_USB_SPEED_DEFAULT; + Spring.speed_max = JGINYUE_USB_SPEED_MAX; + Spring.speed_min = JGINYUE_USB_SPEED_MIN; + Spring.direction = JGINYUE_DIRECTION_RIGHT; + Spring.colors.resize(1); + //modes.push_back(Spring); + + mode Water; + Water.name = "Water"; + Water.value = JGINYUE_USB_V2_MODE_WATER; + Water.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR |MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Water.color_mode = MODE_COLORS_MODE_SPECIFIC; + Water.colors_max = 1; + Water.colors_min = 1; + Water.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Water.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Water.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Water.speed = JGINYUE_USB_SPEED_DEFAULT; + Water.speed_max = JGINYUE_USB_SPEED_MAX; + Water.speed_min = JGINYUE_USB_SPEED_MIN; + Water.direction = MODE_DIRECTION_RIGHT; + Water.colors.resize(1); + modes.push_back(Water); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = JGINYUE_USB_V2_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Rainbow.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Rainbow.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Rainbow.speed = JGINYUE_USB_SPEED_DEFAULT; + Rainbow.speed_max = JGINYUE_USB_SPEED_MAX; + Rainbow.speed_min = JGINYUE_USB_SPEED_MIN; + Rainbow.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Rainbow); + + mode MulticolorCycling; + MulticolorCycling.name = "Multicolor CYCLING"; + MulticolorCycling.value = JGINYUE_USB_V2_MODE_MULTICOLOR_CYCLING; + MulticolorCycling.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + MulticolorCycling.color_mode = MODE_COLORS_MODE_SPECIFIC; + MulticolorCycling.colors_max = 8; + MulticolorCycling.colors_min = 1; + MulticolorCycling.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + MulticolorCycling.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + MulticolorCycling.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + MulticolorCycling.speed = JGINYUE_USB_SPEED_DEFAULT; + MulticolorCycling.speed_max = JGINYUE_USB_SPEED_MAX; + MulticolorCycling.speed_min = JGINYUE_USB_SPEED_MIN; + MulticolorCycling.direction = MODE_DIRECTION_RIGHT; + MulticolorCycling.colors.resize(8); + modes.push_back(MulticolorCycling); + + mode Sunrise; + Sunrise.name = "Sunrise"; + Sunrise.value = JGINYUE_USB_V2_MODE_SUNRISE; + Sunrise.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Sunrise.color_mode = MODE_COLORS_MODE_SPECIFIC; + Sunrise.colors_max = 8; + Sunrise.colors_min = 1; + Sunrise.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Sunrise.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Sunrise.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Sunrise.speed = JGINYUE_USB_SPEED_DEFAULT; + Sunrise.speed_max = JGINYUE_USB_SPEED_MAX; + Sunrise.speed_min = JGINYUE_USB_SPEED_MIN; + Sunrise.direction = MODE_DIRECTION_RIGHT; + Sunrise.colors.resize(8); + modes.push_back(Sunrise); + + mode Rotate_star; + Rotate_star.name = "Rotate Star"; + Rotate_star.value = JGINYUE_USB_V2_MODE_ROTATE_STAR; + Rotate_star.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Rotate_star.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rotate_star.colors_max = 8; + Rotate_star.colors_min = 1; + Rotate_star.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Rotate_star.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Rotate_star.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Rotate_star.speed = JGINYUE_USB_SPEED_DEFAULT; + Rotate_star.speed_max = JGINYUE_USB_SPEED_MAX; + Rotate_star.speed_min = JGINYUE_USB_SPEED_MIN; + Rotate_star.direction = MODE_DIRECTION_RIGHT; + Rotate_star.colors.resize(8); + modes.push_back(Rotate_star); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = JGINYUE_USB_V2_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors_max = 1; + Meteor.colors_min = 1; + Meteor.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Meteor.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Meteor.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Meteor.speed = JGINYUE_USB_SPEED_DEFAULT; + Meteor.speed_max = JGINYUE_USB_SPEED_MAX; + Meteor.speed_min = JGINYUE_USB_SPEED_MIN; + Meteor.direction = MODE_DIRECTION_RIGHT; + Meteor.colors.resize(8); + modes.push_back(Meteor); + + mode Cycling_Breathing; + Cycling_Breathing.name = "Cycling Breathing"; + Cycling_Breathing.value = JGINYUE_USB_V2_MODE_CYCLING_BREATHING; + Cycling_Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Cycling_Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Cycling_Breathing.colors_max = 8; + Cycling_Breathing.colors_min = 1; + Cycling_Breathing.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Cycling_Breathing.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Cycling_Breathing.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Cycling_Breathing.speed = JGINYUE_USB_SPEED_DEFAULT; + Cycling_Breathing.speed_max = JGINYUE_USB_SPEED_MAX; + Cycling_Breathing.speed_min = JGINYUE_USB_SPEED_MIN; + Cycling_Breathing.direction = MODE_DIRECTION_RIGHT; + Cycling_Breathing.colors.resize(8); + modes.push_back(Cycling_Breathing); + + mode Raining; + Raining.name = "Raining"; + Raining.value = JGINYUE_USB_V2_MODE_CYCLING_RAINING; + Raining.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Raining.color_mode = MODE_COLORS_MODE_SPECIFIC; + Raining.colors_max = 1; + Raining.colors_min = 1; + Raining.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Raining.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Raining.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Raining.speed = JGINYUE_USB_SPEED_DEFAULT; + Raining.speed_max = JGINYUE_USB_SPEED_MAX; + Raining.speed_min = JGINYUE_USB_SPEED_MIN; + Raining.direction = MODE_DIRECTION_RIGHT; + Raining.colors.resize(8); + modes.push_back(Raining); + + mode MulticolorWater1; + MulticolorWater1.name = "Multicolor Water 1"; + MulticolorWater1.value = JGINYUE_USB_V2_MODE_MULTICOLOR_WATER_1; + MulticolorWater1.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + MulticolorWater1.color_mode = MODE_COLORS_MODE_SPECIFIC; + MulticolorWater1.colors_max = 8; + MulticolorWater1.colors_min = 1; + MulticolorWater1.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + MulticolorWater1.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + MulticolorWater1.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + MulticolorWater1.speed = JGINYUE_USB_SPEED_DEFAULT; + MulticolorWater1.speed_max = JGINYUE_USB_SPEED_MAX; + MulticolorWater1.speed_min = JGINYUE_USB_SPEED_MIN; + MulticolorWater1.direction = JGINYUE_DIRECTION_LEFT; + MulticolorWater1.colors.resize(8); + modes.push_back(MulticolorWater1); + + + mode MulticolorWater2; + MulticolorWater2.name = "Multicolor Water 2"; + MulticolorWater2.value = JGINYUE_USB_V2_MODE_MULTICOLOR_WATER_2; + MulticolorWater2.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + MulticolorWater2.color_mode = MODE_COLORS_MODE_SPECIFIC; + MulticolorWater2.colors_max = 8; + MulticolorWater2.colors_min = 1; + MulticolorWater2.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + MulticolorWater2.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + MulticolorWater2.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + MulticolorWater2.speed = JGINYUE_USB_SPEED_DEFAULT; + MulticolorWater2.speed_max = JGINYUE_USB_SPEED_MAX; + MulticolorWater2.speed_min = JGINYUE_USB_SPEED_MIN; + MulticolorWater2.direction = JGINYUE_DIRECTION_LEFT; + MulticolorWater2.colors.resize(8); + modes.push_back(MulticolorWater2); + + mode Hourglass; + Hourglass.name = "Hourglass"; + Hourglass.value = JGINYUE_USB_V2_MODE_HOURGLASS; + Hourglass.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Hourglass.color_mode = MODE_COLORS_MODE_SPECIFIC; + Hourglass.colors_max = 3; + Hourglass.colors_min = 3; + Hourglass.brightness = JGINYUE_USB_BRIGHTNESS_DEFAULT; + Hourglass.brightness_max = JGINYUE_USB_BRIGHTNESS_MAX; + Hourglass.brightness_min = JGINYUE_USB_BRIGHTNESS_MIN; + Hourglass.speed = JGINYUE_USB_SPEED_DEFAULT; + Hourglass.speed_max = JGINYUE_USB_SPEED_MAX; + Hourglass.speed_min = JGINYUE_USB_SPEED_MIN; + Hourglass.direction = MODE_DIRECTION_RIGHT; + Hourglass.colors.resize(8); + //modes.push_back(Hourglass); + + InitZones(); +} + +RGBController_JGINYUEInternalUSBV2::~RGBController_JGINYUEInternalUSBV2() +{ + delete controller; +} + +void RGBController_JGINYUEInternalUSBV2::SetupZones() +{ + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + unsigned char normal_zone_count = controller->GetZoneCount(); + if((controller->support_Global_zone == true) && (normal_zone_count > 1)) + { + normal_zone_count--; + //TODO support_Global_zone + } + + for(unsigned int zone_idx = 0; zone_idx < normal_zone_count; zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + new_led.name = zones[zone_idx].name + " LED#" + std::to_string(led_idx + 1); + new_led.value = 0; + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_JGINYUEInternalUSBV2::ResizeZone(int zone, int new_size) +{ + unsigned char area; + + area = controller->device_config[zone].Area_ID; + + zones[zone].leds_count = new_size; + + SetupZones(); + + if(modes[active_mode].value == JGINYUE_USB_V2_MODE_DIRECT) + { + controller->DirectLEDControl(zones[zone].colors, new_size, area); + } + else + { + controller->WriteZoneMode(area,modes[active_mode].value, new_size,modes[active_mode].colors, modes[active_mode].speed, modes[active_mode].brightness, modes[active_mode].direction); + } +} + +void RGBController_JGINYUEInternalUSBV2::DeviceUpdateLEDs() +{ + unsigned char normal_zone_count = controller->GetZoneCount(); + + if((controller->support_Global_zone == true) && (normal_zone_count > 1)) + { + normal_zone_count--; + //TODO support_Global_zone + } + + for(int i = 0; i < normal_zone_count; i++) + { + UpdateZoneLEDs(i); + } +} + +void RGBController_JGINYUEInternalUSBV2::UpdateZoneLEDs(int zone) +{ + unsigned char area; + area = controller->device_config[zone].Area_ID; + + controller->DirectLEDControl(zones[zone].colors, zones[zone].leds_count, area); +} + +void RGBController_JGINYUEInternalUSBV2::UpdateSingleLED(int led) +{ + int zone; + zone = leds[led].value; + + UpdateZoneLEDs(zone); +} + +void RGBController_JGINYUEInternalUSBV2::DeviceUpdateMode() +{ + if(modes[active_mode].value == JGINYUE_USB_V2_MODE_DIRECT) + { + DeviceUpdateLEDs(); + return; + } + + unsigned int Area_num = 0; + if(controller->support_Global_zone == true) + { + Area_num = controller->GetZoneCount() - 1; + } + else + { + Area_num = controller->GetZoneCount(); + } + for(unsigned int i = 0; i < Area_num; i++) + { + DeviceUpdateZoneMode((int)i); + } +} + +void RGBController_JGINYUEInternalUSBV2::DeviceUpdateZoneMode(int zone) +{ + unsigned char Area_ID = controller->device_config[zone].Area_ID; + controller->WriteZoneMode( + Area_ID, + modes[active_mode].value, + zones[zone].leds_count, + modes[active_mode].colors, + modes[active_mode].speed, + modes[active_mode].brightness, + modes[active_mode].direction); +} + +void RGBController_JGINYUEInternalUSBV2::InitZones() +{ + unsigned char normal_zone_count = controller->GetZoneCount(); + zones.clear(); + zones.resize(normal_zone_count); + + if((controller->support_Global_zone == true) && (normal_zone_count > 1)) + { + normal_zone_count--; + //TODO support_Global_zone + } + + for(size_t i = 0; i < normal_zone_count; i++) + { + zone * zone_to_init = &(zones[i]); + AreaConfigurationV2 * cfg = &(controller->device_config[i]); + + zone_to_init->leds_min = 0; + zone_to_init->leds_max = cfg->Max_LED_numbers; + zone_to_init->leds_count = 0; + zone_to_init->type = ZONE_TYPE_LINEAR; + zone_to_init->matrix_map = NULL; + + switch(cfg->Area_ID) + { + case JGINYUE_USB_V2_ARGB_STRIP_1: + zone_to_init->name = "ARGB Strip Header 1"; + break; + case JGINYUE_USB_V2_ARGB_STRIP_2: + zone_to_init->name = "ARGB Strip Header 2"; + break; + case JGINYUE_USB_V2_ARGB_FAN_1: + zone_to_init->name = "ARGB Fan Header 1"; + break; + case JGINYUE_USB_V2_ARGB_FAN_2: + zone_to_init->name = "ARGB Fan Header 2"; + break; + case JGINYUE_USB_V2_ARGB_FAN_3: + zone_to_init->name = "ARGB Fan Header 3"; + break; + case JGINYUE_USB_V2_ARGB_FAN_4: + zone_to_init->name = "ARGB Fan Header 4"; + break; + case JGINYUE_USB_V2_ARGB_FAN_5: + zone_to_init->name = "ARGB Fan Header 5"; + break; + default: + zone_to_init->name = "Unknow Device"; + break; + } + } + SetupZones(); +} diff --git a/Controllers/JGINYUEInternalUSBV2Controller/RGBController_JGINYUEInternalUSBV2.h b/Controllers/JGINYUEInternalUSBV2Controller/RGBController_JGINYUEInternalUSBV2.h new file mode 100644 index 0000000..9d3cbc5 --- /dev/null +++ b/Controllers/JGINYUEInternalUSBV2Controller/RGBController_JGINYUEInternalUSBV2.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_JGINYUEInternalUSBV2.h | +| | +| RGBController for JGINYUE USB motherboard V2 | +| | +| Tong R (tcr020) 03 July 2023 | +| Liu ShiMeng(Moon dream stars) 06 Aug 2024 | +| Dongguan Yonghang Electronic Technology Co., Ltd | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "JGINYUEInternalUSBV2Controller.h" + +class RGBController_JGINYUEInternalUSBV2 : public RGBController +{ +public: + RGBController_JGINYUEInternalUSBV2(JGINYUEInternalUSBV2Controller* controller_ptr); + ~RGBController_JGINYUEInternalUSBV2(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceUpdateZoneMode(int zone); + +private: + JGINYUEInternalUSBV2Controller* controller; + void InitZones(); +}; diff --git a/Controllers/KasaSmartController/KasaSmartController.cpp b/Controllers/KasaSmartController/KasaSmartController.cpp new file mode 100644 index 0000000..70520ca --- /dev/null +++ b/Controllers/KasaSmartController/KasaSmartController.cpp @@ -0,0 +1,403 @@ +/*---------------------------------------------------------*\ +| KasaSmartController.cpp | +| | +| Driver for Kasa Smart bulbs | +| | +| Devin Wendt (umbreon222@gmail.com) 16 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "KasaSmartController.h" +#include +#include "hsv.h" + +using json = nlohmann::json; + +KasaSmartController::KasaSmartController(std::string ipAddress, std::string name) +{ + this->name = name; + + /*------------------------------------------------*\ + | Fill in location string with device's IP address | + \*------------------------------------------------*/ + location = "IP: " + ipAddress; + + /*---------------------------------------------------------*\ + | Create a TCP client sending to the device's IP, port 9999 | + \*---------------------------------------------------------*/ + port.tcp_client(ipAddress.c_str(), "9999"); +} + +bool KasaSmartController::Initialize() +{ + is_initialized = false; + retry_count = 0; + + /*--------------*\ + | Try to connect | + \*--------------*/ + while(!port.connected && !port.tcp_client_connect() && retry_count < KASA_SMART_MAX_CONNECTION_ATTEMPTS) + { + ++retry_count; + } + if(!port.connected) + { + /*----------------*\ + | Couldn't connect | + \*----------------*/ + return is_initialized; + } + + const std::string system_info_query(KASA_SMART_SYSTEM_INFO_QUERY); + std::string system_info_json; + bool command_sent = KasaSmartController::SendCommand(system_info_query, system_info_json); + port.tcp_close(); + if(!command_sent || system_info_json.empty()) + { + /*---------------------------------------*\ + | Send command failed or no data returned | + \*---------------------------------------*/ + return is_initialized; + } + + json system_information; + try + { + system_information = json::parse(system_info_json); + } + catch (json::parse_error&) + { + /*-----------------------*\ + | Can't parse system info | + \*-----------------------*/ + return is_initialized; + } + + std::string device_type; + if(system_information["system"]["get_sysinfo"].contains("type")) + { + device_type = system_information["system"]["get_sysinfo"]["type"]; + } + else if(system_information["system"]["get_sysinfo"].contains("mic_type")) + { + device_type = system_information["system"]["get_sysinfo"]["mic_type"]; + } + else + { + /*----------------------*\ + | Can't find device type | + \*----------------------*/ + return is_initialized; + } + + std::transform(device_type.begin(), device_type.end(), device_type.begin(), + [](unsigned char c){ return std::tolower(c); }); + if(device_type.find("smartbulb") == std::string::npos) + { + /*----------------------------*\ + | Device type not a smart bulb | + \*----------------------------*/ + return is_initialized; + } + + if(system_information["system"]["get_sysinfo"].contains("is_color") && system_information["system"]["get_sysinfo"]["is_color"] != 1) + { + /*--------------------------------*\ + | Smart bulb doesn't support color | + \*--------------------------------*/ + return is_initialized; + } + + std::string model; + if(system_information["system"]["get_sysinfo"].contains("model")) + { + model = system_information["system"]["get_sysinfo"]["model"]; + } + else + { + /*-----------------------*\ + | Can't find device model | + \*-----------------------*/ + return is_initialized; + } + + if(model.find("KL420") != std::string::npos) + { + kasa_type = KASA_SMART_TYPE_KL420; + } + else if(model.find("KL4") != std::string::npos) + { + kasa_type = KASA_SMART_TYPE_OTHER_LEDSTRIP; + } + else + { + kasa_type = KASA_SMART_TYPE_LIGHT; + } + + firmware_version = system_information["system"]["get_sysinfo"]["sw_ver"]; + module_name = system_information["system"]["get_sysinfo"]["model"]; + device_id = system_information["system"]["get_sysinfo"]["deviceId"]; + + is_initialized = true; + return is_initialized; +} + +KasaSmartController::~KasaSmartController() +{ + if(port.connected) + { + port.tcp_close(); + } +} + +std::string KasaSmartController::GetLocation() +{ + return(location); +} + +std::string KasaSmartController::GetName() +{ + return(name); +} + +std::string KasaSmartController::GetVersion() +{ + return(module_name + " " + firmware_version); +} + +std::string KasaSmartController::GetManufacturer() +{ + return("Kasa Smart"); +} + +std::string KasaSmartController::GetUniqueID() +{ + return(device_id); +} + +int KasaSmartController::GetKasaType() +{ + return(kasa_type); +} + +void KasaSmartController::SetColor(unsigned char red, unsigned char green, unsigned char blue, int device_type) +{ + if(!is_initialized) + { + return; + } + + RGBColor color = ToRGBColor(red, green, blue); + hsv_t hsv; + rgb2hsv(color, &hsv); + + /*------------------------------------------*\ + | Normalize case where hue is "-1" undefined | + \*------------------------------------------*/ + unsigned int normalized_hue = hsv.hue; + if(hsv.hue == (unsigned int)-1) + { + normalized_hue = 0; + } + /*--------------------------------------------------*\ + | Kasa smart lights take values out of 100 for these | + \*--------------------------------------------------*/ + unsigned int normalized_saturation = hsv.saturation * 100 / 255; + unsigned int normalized_value = hsv.value * 100 / 255; + + + /*-------------------*\ + | Open TCP connection | + \*-------------------*/ + if(!port.connected && !port.tcp_client_connect() && ++retry_count >= KASA_SMART_MAX_CONNECTION_ATTEMPTS) + { + is_initialized = false; + return; + } + + /*----------------------------*\ + | Hack to handle/emulate black | + \*----------------------------*/ + if(normalized_saturation == 0 && normalized_value == 0) + { + TurnOff(device_type); + return; + } + + /*------------------------------*\ + | Format set light state command | + \*------------------------------*/ + std::string set_lightstate_command_format; + if(device_type == DEVICE_TYPE_LIGHT) + { + set_lightstate_command_format = KASA_SMART_LIGHT_SET_LIGHT_STATE_COMMAND_FORMAT; + } + else if(device_type == DEVICE_TYPE_LEDSTRIP) + { + set_lightstate_command_format = KASA_SMART_LEDSTRIP_SET_LIGHT_STATE_COMMAND_FORMAT; + } + int size = std::snprintf(nullptr, 0, set_lightstate_command_format.c_str(), normalized_hue, normalized_saturation, normalized_value) + 1; + if(size <= 0) + { + port.tcp_close(); + return; + } + char* buf = new char[size]; + std::snprintf(buf, size, set_lightstate_command_format.c_str(), normalized_hue, normalized_saturation, normalized_value); + std::string set_lightstate_command(buf, buf + size - 1); + delete[] buf; + + /*-----------------------------*\ + | Send command, ignore response | + \*-----------------------------*/ + std::string response; + KasaSmartController::SendCommand(set_lightstate_command, response); + port.tcp_close(); +} + +void KasaSmartController::SetEffect(std::string effect) +{ + if(!is_initialized) + { + return; + } + + /*-------------------*\ + | Open TCP connection | + \*-------------------*/ + if(!port.connected && !port.tcp_client_connect() && ++retry_count >= KASA_SMART_MAX_CONNECTION_ATTEMPTS) + { + is_initialized = false; + return; + } + + std::string response; + KasaSmartController::SendCommand(effect, response); + port.tcp_close(); +} + +void KasaSmartController::TurnOff(int device_type) +{ + if(!is_initialized) + { + return; + } + + std::string turn_off_command; + if(device_type == DEVICE_TYPE_LIGHT) + { + turn_off_command = KASA_SMART_LIGHT_OFF_COMMAND; + } + else if(device_type == DEVICE_TYPE_LEDSTRIP) + { + turn_off_command = KASA_SMART_LEDSTRIP_OFF_COMMAND; + } + + if(!port.connected && !port.tcp_client_connect() && ++retry_count >= KASA_SMART_MAX_CONNECTION_ATTEMPTS) + { + is_initialized = false; + return; + } + std::string response; + KasaSmartController::SendCommand(turn_off_command, response); + port.tcp_close(); +} + +bool KasaSmartController::SendCommand(std::string command, std::string &response) +{ + const unsigned char* encrypted_payload = KasaSmartController::Encrypt(command); + port.tcp_client_write((char*)encrypted_payload, (int)(command.length() + sizeof(unsigned long))); + delete[] encrypted_payload; + + unsigned char* receive_buffer = new unsigned char[KASA_SMART_RECEIVE_BUFFER_SIZE]; + int response_length = port.tcp_listen((char*)receive_buffer, KASA_SMART_RECEIVE_BUFFER_SIZE); + if(response_length > KASA_SMART_RECEIVE_BUFFER_SIZE || response_length <= 0) { + /*-------------------------------------------------------------*\ + | Small fail safes to prevent decrypting bad or empty responses | + \*-------------------------------------------------------------*/ + return false; + } + + unsigned long received_length = response_length; + unsigned long response_full_length = 0; + if(response_length > 0) + { + response_full_length = ntohl(*(uint32_t*)receive_buffer); + } + + if(response_full_length > KASA_SMART_RECEIVE_BUFFER_SIZE) { + return false; + } + + /*--------------------------*\ + | Fetch entirety of response | + \*--------------------------*/ + while(received_length < response_full_length) + { + received_length += port.tcp_listen((char*)receive_buffer + received_length, KASA_SMART_RECEIVE_BUFFER_SIZE - received_length); + } + + if(received_length > 0) + { + /*------------------------------------------------*\ + | Decrypt payload data preceeding the payload size | + \*------------------------------------------------*/ + KasaSmartController::Decrypt(receive_buffer + sizeof(uint32_t), received_length - sizeof(uint32_t), response); + } + delete[] receive_buffer; + return true; +} + +unsigned char* KasaSmartController::Encrypt(const std::string request) +{ + /*----------------------------------------------------------------*\ + | "Encrypted" payload consists of size as a uint32 + XOR'd payload | + \*----------------------------------------------------------------*/ + uint32_t size = htonl((uint32_t)request.length()); + int payload_size = (int)(request.length() + sizeof(size)); + unsigned char* payload = new unsigned char[payload_size]; + memcpy(payload, &size, sizeof(size)); + unsigned char* request_data = new unsigned char[request.length()]; + memcpy(request_data, request.data(), request.length()); + KasaSmartController::XorPayload(request_data, (int)request.length()); + memcpy(payload + sizeof(size), request_data, request.length()); + delete[] request_data; + return payload; +} + +std::string KasaSmartController::Decrypt(const unsigned char* encrypted, int length, std::string &response) +{ + unsigned char* temp_encrypted = new unsigned char[length]; + memcpy(temp_encrypted, encrypted, length); + KasaSmartController::XorEncryptedPayload(temp_encrypted, length); + for(int i = 0; i < length; ++i) + { + response += temp_encrypted[i]; + } + delete[] temp_encrypted; + return response; +} + +void KasaSmartController::XorPayload(unsigned char* encrypted, int length) +{ + unsigned char key = KASA_SMART_INITIALIZATION_VECTOR; + for(int i = 0; i < length; ++i) + { + key ^= encrypted[i]; + encrypted[i] = key; + } +} + +void KasaSmartController::XorEncryptedPayload(unsigned char* encrypted, int length) +{ + unsigned char key = KASA_SMART_INITIALIZATION_VECTOR; + for(int i = 0; i < length; ++i) + { + unsigned char plain_byte = key ^ encrypted[i]; + key = encrypted[i]; + encrypted[i] = plain_byte; + } +} diff --git a/Controllers/KasaSmartController/KasaSmartController.h b/Controllers/KasaSmartController/KasaSmartController.h new file mode 100644 index 0000000..5bbcefc --- /dev/null +++ b/Controllers/KasaSmartController/KasaSmartController.h @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| KasaSmartController.h | +| | +| Driver for Kasa Smart bulbs | +| | +| Devin Wendt (umbreon222@gmail.com) 16 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "net_port.h" + +enum +{ + KASA_SMART_MODE_DIRECT = 0x00, + KASA_SMART_MODE_OFF = 0x01, + KASA_SMART_MODE_RAINBOW = 0x02 +}; + +enum +{ + KASA_SMART_TYPE_KL420 = 0x00, + KASA_SMART_TYPE_OTHER_LEDSTRIP = 0x01, + KASA_SMART_TYPE_LIGHT = 0x02 +}; + +#define KASA_SMART_INITIALIZATION_VECTOR 0xAB +#define KASA_SMART_RECEIVE_BUFFER_SIZE 4096 +#define KASA_SMART_MAX_CONNECTION_ATTEMPTS 3 + +/*-------------------------*\ +| Kasa Smart Light Commands | +\*-------------------------*/ +#define KASA_SMART_SYSTEM_INFO_QUERY "{\"system\": {\"get_sysinfo\": {}}}" +#define KASA_SMART_LIGHT_OFF_COMMAND "{\"smartlife.iot.smartbulb.lightingservice\": {\"transition_light_state\": {\"transition_period\": 0, \"on_off\":0, \"mode\":\"normal\"}}}" +const char KASA_SMART_LIGHT_SET_LIGHT_STATE_COMMAND_FORMAT[] = "{\"smartlife.iot.smartbulb.lightingservice\": {\"transition_light_state\": {\"transition_period\": 0, \"on_off\"" + ":1, \"mode\":\"normal\", \"hue\": %u, \"saturation\": %u, \"brightness\": %u, \"color_temp\": 0}}}"; +#define KASA_SMART_LEDSTRIP_OFF_COMMAND "{\"smartlife.iot.lightStrip\": {\"set_light_state\": {\"transition\": 0, \"on_off\":0, \"mode\":\"normal\"}}}" +const char KASA_SMART_LEDSTRIP_SET_LIGHT_STATE_COMMAND_FORMAT[] = "{\"smartlife.iot.lightStrip\": {\"set_light_state\": {\"transition\": 0, \"on_off\"" + ":1, \"mode\":\"normal\", \"hue\": %u, \"saturation\": %u, \"brightness\": %u, \"color_temp\": 0}}}"; +#define KASA_SMART_EFFECT_RAINBOW_COMMAND "{\"smartlife.iot.lighting_effect\":{\"set_lighting_effect\":{\"custom\":0,\"direction\":1,\"duration\":0,\"enable\":1,\"expansion_strategy\":1,\"name\":\"Rainbow\",\"repeat_times\":0,\"segments\":[0],\"sequence\":[[0,100,100],[100,100,100],[200,100,100],[300,100,100]],\"spread\":12,\"transition\":1500,\"type\":\"sequence\"}}}}" + + +class KasaSmartController +{ +public: + KasaSmartController(std::string ipAddress, std::string name); + ~KasaSmartController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetVersion(); + std::string GetManufacturer(); + std::string GetUniqueID(); + int GetKasaType(); + + bool Initialize(); + void SetColor(unsigned char red, unsigned char green, unsigned char blue, int device_type); + void SetEffect(std::string effect); + void TurnOff(int device_type); + +private: + net_port port; + std::string name; + bool is_initialized; + unsigned int retry_count; + std::string firmware_version; + std::string module_name; + std::string device_id; + std::string location; + int kasa_type; + bool SendCommand(std::string command, std::string &response); + static unsigned char* Encrypt(const std::string request); + static std::string Decrypt(const unsigned char*, int length, std::string &response); + static void XorPayload(unsigned char* encrypted, int length); + static void XorEncryptedPayload(unsigned char* encrypted, int length); +}; diff --git a/Controllers/KasaSmartController/KasaSmartControllerDetect.cpp b/Controllers/KasaSmartController/KasaSmartControllerDetect.cpp new file mode 100644 index 0000000..76d7f38 --- /dev/null +++ b/Controllers/KasaSmartController/KasaSmartControllerDetect.cpp @@ -0,0 +1,60 @@ +/*---------------------------------------------------------*\ +| KasaSmartControllerDetect.cpp | +| | +| Detector for Kasa Smart bulbs | +| | +| Devin Wendt (umbreon222@gmail.com) 16 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "KasaSmartController.h" +#include "RGBController_KasaSmart.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectKasaSmartControllers * +* * +* Detect Kasa Smart devices * +* * +\******************************************************************************************/ + +void DetectKasaSmartControllers() +{ + json kasa_smart_settings; + + /*---------------------------------------------*\ + | Get Kasa Smart settings from settings manager | + \*---------------------------------------------*/ + kasa_smart_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("KasaSmartDevices"); + + /*---------------------------------------------*\ + | If the Wiz settings contains devices, process | + \*---------------------------------------------*/ + if(kasa_smart_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < kasa_smart_settings["devices"].size(); device_idx++) + { + if(kasa_smart_settings["devices"][device_idx].contains("ip")) + { + std::string kasa_smart_ip = kasa_smart_settings["devices"][device_idx]["ip"]; + std::string name = kasa_smart_settings["devices"][device_idx]["name"]; + + KasaSmartController* controller = new KasaSmartController(kasa_smart_ip, name); + if(!controller->Initialize()) + { + continue; + } + + RGBController_KasaSmart* rgb_controller = new RGBController_KasaSmart(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } + +} /* DetectKasaSmartControllers() */ + +REGISTER_DETECTOR("KasaSmart", DetectKasaSmartControllers); diff --git a/Controllers/KasaSmartController/RGBController_KasaSmart.cpp b/Controllers/KasaSmartController/RGBController_KasaSmart.cpp new file mode 100644 index 0000000..9f9a261 --- /dev/null +++ b/Controllers/KasaSmartController/RGBController_KasaSmart.cpp @@ -0,0 +1,139 @@ +/*---------------------------------------------------------*\ +| RGBController_KasaSmart.cpp | +| | +| RGBController for Kasa Smart bulbs | +| | +| Devin Wendt (umbreon222) 16 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_KasaSmart.h" + +/**------------------------------------------------------------------*\ + @name Kasa Smart Bulbs + @category Light + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectKasaSmartControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_KasaSmart::RGBController_KasaSmart(KasaSmartController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetManufacturer() + " " + controller->GetName(); + vendor = controller->GetManufacturer(); + version = controller->GetVersion(); + description = "Kasa Smart Device"; + serial = controller->GetUniqueID(); + location = controller->GetLocation(); + + if(controller->GetKasaType() == KASA_SMART_TYPE_LIGHT) + { + type = DEVICE_TYPE_LIGHT; + } + else if(controller->GetKasaType() == KASA_SMART_TYPE_OTHER_LEDSTRIP + || controller->GetKasaType() == KASA_SMART_TYPE_KL420) + { + type = DEVICE_TYPE_LEDSTRIP; + } + + mode Direct; + Direct.name = "Direct"; + Direct.value = KASA_SMART_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + if(controller->GetKasaType() == KASA_SMART_TYPE_KL420) + { + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = KASA_SMART_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Rainbow.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Rainbow); + } + + mode Off; + Off.name = "Off"; + Off.value = KASA_SMART_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_KasaSmart::~RGBController_KasaSmart() +{ + delete controller; +} + +void RGBController_KasaSmart::SetupZones() +{ + zone led_zone; + led_zone.name = "RGB Light"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + led new_led; + new_led.name = "RGB Light"; + + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_KasaSmart::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*-------------------------------------------*\ + | This device does not support resizing zones | + \*-------------------------------------------*/ +} + +void RGBController_KasaSmart::DeviceUpdateLEDs() +{ + if(modes[active_mode].value != KASA_SMART_MODE_DIRECT) + { + return; + } + + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetColor(red, grn, blu, type); +} + +void RGBController_KasaSmart::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_KasaSmart::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_KasaSmart::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case KASA_SMART_MODE_OFF: + controller->TurnOff(type); + break; + case KASA_SMART_MODE_RAINBOW: + controller->SetEffect(KASA_SMART_EFFECT_RAINBOW_COMMAND); + break; + } +} diff --git a/Controllers/KasaSmartController/RGBController_KasaSmart.h b/Controllers/KasaSmartController/RGBController_KasaSmart.h new file mode 100644 index 0000000..14fa467 --- /dev/null +++ b/Controllers/KasaSmartController/RGBController_KasaSmart.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_KasaSmart.h | +| | +| RGBController for Kasa Smart bulbs | +| | +| Devin Wendt (umbreon222) 16 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "KasaSmartController.h" + +class RGBController_KasaSmart : public RGBController +{ +public: + RGBController_KasaSmart(KasaSmartController* controller_ptr); + ~RGBController_KasaSmart(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + KasaSmartController* controller; +}; diff --git a/Controllers/KeychronKeyboardController/KeychronKeyboardController.cpp b/Controllers/KeychronKeyboardController/KeychronKeyboardController.cpp new file mode 100644 index 0000000..512825b --- /dev/null +++ b/Controllers/KeychronKeyboardController/KeychronKeyboardController.cpp @@ -0,0 +1,297 @@ +/*---------------------------------------------------------*\ +| KeychronKeyboardController.cpp | +| | +| Driver for Keychron keyboard | +| | +| Morgan Guimard (morg) 20 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "KeychronKeyboardController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +KeychronKeyboardController::KeychronKeyboardController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +KeychronKeyboardController::~KeychronKeyboardController() +{ + hid_close(dev); +} + +std::string KeychronKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string KeychronKeyboardController::GetNameString() +{ + return(name); +} + +std::string KeychronKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void KeychronKeyboardController:: SetLedSequencePositions(std::vector positions) +{ + led_sequence_positions = positions; +} + +void KeychronKeyboardController::SetMode(std::vector modes, int active_mode, std::vector colors) +{ + /*-----------------------------------------*\ + | Turn customization on/off | + | Custom mode needs to turn it on | + \*-----------------------------------------*/ + SetCustomization(modes[active_mode].value == CUSTOM_MODE_VALUE); + + /*-----------------------------------------*\ + | Tells the device we're about to send the | + | pages (18 pages) | + \*-----------------------------------------*/ + StartEffectPage(); + + unsigned char usb_buf[PACKET_DATA_LENGTH]; + + /*-----------------------------------------*\ + | Configure the modes | + | LED Effect Page structure: | + | | + | OK.. this was from the original PDF | + | which appears to not be exact/up to date | + |-------------------------------------------| + | [0] Specialeffects mode1-32 | + | [1] colorFull color: 0x00 Monochrome:0x01 | + | [2] R Color ratio 0x00-0xFF | + | [3] G Color Ratio0x00-0xFF | + | [4] B Colour ratio0x00-0xFF | + | full color is 0,invalid | + | [5] dynamicdirection | + | left to right: 0x00 | + | right to left: 0x01 | + | down to up: 0x02 | + | up to down: 0x03 | + | [6] brightnesscontrol 0x00-0x0F | + | 0x0F brightest | + | [7] Periodiccontrol0x00-0x0F | + | 0x0F longest cycle | + | [8:13] Reserved | + | [14] Checkcode_L0xAA | + | [15] Checkcode_H0x55 | + |-------------------------------------------| + | Fixes: | + | color mode is 8th byte | + | brightness is 9th byte | + | speed is 10th byte | + | direction is 11th byte | + \*-----------------------------------------*/ + unsigned char selected_mode[EFFECT_PAGE_LENGTH]; + + for(unsigned int i = 0; i < 5; i++) // 5 packets + { + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + for(unsigned int j = 0; j < 4; j++) // of 4 effects + { + const mode& m = modes[1 + j + i * 4]; // skip 1 first mode (Custom) + + int offset = j * EFFECT_PAGE_LENGTH; + + usb_buf[offset + 0] = m.value; // mode value + + if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + usb_buf[offset + 1] = RGBGetRValue(m.colors[0]); + usb_buf[offset + 2] = RGBGetGValue(m.colors[0]); + usb_buf[offset + 3] = RGBGetBValue(m.colors[0]); + } + + usb_buf[offset + 8] = m.color_mode == MODE_COLORS_RANDOM; // random switch + usb_buf[offset + 9] = m.brightness; + usb_buf[offset + 10] = m.speed; + usb_buf[offset + 11] = m.direction; + + usb_buf[offset + 14] = EFFECT_PAGE_CHECK_CODE_L; + usb_buf[offset + 15] = EFFECT_PAGE_CHECK_CODE_H; + + /*-----------------------------------------*\ + | Backup active mode values for later use | + | Custom and off share the same mode value | + \*-----------------------------------------*/ + if(m.value == modes[active_mode].value || (m.value == LIGHTS_OFF_MODE_VALUE && modes[active_mode].value == CUSTOM_MODE_VALUE)) + { + usb_buf[offset + 9] = modes[active_mode].brightness; + + for(unsigned int x = 0; x < EFFECT_PAGE_LENGTH; x++) + { + selected_mode[x] = usb_buf[offset+x]; + } + } + } + + Send(usb_buf); // Sends the packet + } + + // packets count sent: 5 + + /*-----------------------------------------*\ + | 3 times an empty packet - guess why... | + \*-----------------------------------------*/ + for(unsigned int i = 0; i < 3; i++) + { + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + Send(usb_buf); + } + + // packets count sent: 8 + + /*-----------------------------------------*\ + | Customization stuff | + | 9 times * 16 blocks 80 RR GG BB | + \*-----------------------------------------*/ + unsigned char color_buf[COLOR_BUF_SIZE]; + memset(color_buf, 0x00, COLOR_BUF_SIZE); + + for(unsigned int i = 0; i < COLOR_BUF_SIZE; i += 4) + { + color_buf[i] = 0x80; + } + + for(unsigned int c = 0; c < colors.size(); c++) + { + int offset = led_sequence_positions[c] * 4; + + color_buf[offset + 1] = RGBGetRValue(colors[c]); + color_buf[offset + 2] = RGBGetGValue(colors[c]); + color_buf[offset + 3] = RGBGetBValue(colors[c]); + } + + for(unsigned int p = 0; p < 9; p++) + { + memcpy(usb_buf, &color_buf[p * PACKET_DATA_LENGTH], PACKET_DATA_LENGTH); + Send(usb_buf); + } + + // packets count sent: 17 + + /*-----------------------------------------*\ + | Tells the device what the active mode is | + | This is the last packet | + \*-----------------------------------------*/ + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + memcpy(usb_buf, &selected_mode[0], EFFECT_PAGE_LENGTH); + Send(usb_buf); + + // packets count sent: 18 - let's hope the keyboard ACK in next frame + + /*-----------------------------------------*\ + | Tells the device that the pages are sent | + \*-----------------------------------------*/ + EndCommunication(); + + /*-----------------------------------------*\ + | Tells the device to apply what we've sent | + \*-----------------------------------------*/ + StartEffectCommand(); +} + +void KeychronKeyboardController::StartEffectCommand() +{ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x00] = PACKET_HEADER; + usb_buf[0x01] = LED_EFFECT_START_COMMAND; + + Send(usb_buf); +} + +void KeychronKeyboardController::StartEffectPage() +{ + /*-----------------------------------------*\ + | LED_SPECIAL_EFFECT_PACKETS: | + | Packet amount that will be sent in this | + | transaction | + \*-----------------------------------------*/ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x00] = PACKET_HEADER; + usb_buf[0x01] = WRITE_LED_SPECIAL_EFFECT_AREA_COMMAND; + usb_buf[0x08] = LED_SPECIAL_EFFECT_PACKETS; + + Send(usb_buf); + + Read(); +} + +void KeychronKeyboardController::SetCustomization(bool state) +{ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + usb_buf[0x00] = PACKET_HEADER; + usb_buf[0x01] = state ? TURN_ON_CUSTOMIZATION_COMMAND : TURN_OFF_CUSTOMIZATION_COMMAND; + Send(usb_buf); + + Read(); +} + +void KeychronKeyboardController::EndCommunication() +{ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x00] = PACKET_HEADER; + usb_buf[0x01] = COMMUNICATION_END_COMMAND; + + Send(usb_buf); + + Read(); +} + +void KeychronKeyboardController::Read() +{ + unsigned char usb_buf[PACKET_DATA_LENGTH+1]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH+1); + + usb_buf[0x00] = REPORT_ID; + + hid_get_feature_report(dev, usb_buf, PACKET_DATA_LENGTH+1); + + std::this_thread::sleep_for(10ms); +} + +void KeychronKeyboardController::Send(unsigned char data[PACKET_DATA_LENGTH]) +{ + unsigned char usb_buf[PACKET_DATA_LENGTH+1]; + + usb_buf[0] = REPORT_ID; + + for(unsigned int x = 0; x < PACKET_DATA_LENGTH; x++) + { + usb_buf[x+1] = data[x]; + } + + hid_send_feature_report(dev, usb_buf, PACKET_DATA_LENGTH+1); + + std::this_thread::sleep_for(10ms); +} diff --git a/Controllers/KeychronKeyboardController/KeychronKeyboardController.h b/Controllers/KeychronKeyboardController/KeychronKeyboardController.h new file mode 100644 index 0000000..8b05e3a --- /dev/null +++ b/Controllers/KeychronKeyboardController/KeychronKeyboardController.h @@ -0,0 +1,120 @@ +/*---------------------------------------------------------*\ +| KeychronKeyboardController.h | +| | +| Driver for Keychron keyboard | +| | +| Morgan Guimard (morg) 20 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include +#include + +#define REPORT_ID 0x00 +#define PACKET_DATA_LENGTH 64 +#define COLOR_BUF_SIZE 576 +#define EFFECT_PAGE_LENGTH 16 +#define LED_SPECIAL_EFFECT_PACKETS 0x12 +#define PACKET_HEADER 0x04 +#define EFFECT_PAGE_CHECK_CODE_L 0xAA +#define EFFECT_PAGE_CHECK_CODE_H 0x55 + +/*-----------------------------------------*\ +| Commands | +\*-----------------------------------------*/ +enum +{ + COMMUNICATION_END_COMMAND = 0x02, + GET_BASIC_INFO_COMMAND = 0x05, + READ_KEY_DEFINITION_AREA_COMMAND = 0x10, + WRITE_KEY_DEFINITION_AREA_COMMAND = 0x11, + READ_LED_EFFECT_DEFINITION_AREA_COMMAND = 0x12, + WRITE_LED_SPECIAL_EFFECT_AREA_COMMAND = 0x13, + READ_MACRO_DEFINITION_AREA_COMMAND = 0x14, + WRITE_MACRO_DEFINITION_AREA_COMMAND = 0x15, + READ_GAME_MODE_AREA_COMMAND = 0x16, + WRITE_GAME_MODE_AREA_COMMAND = 0x17, + TURN_ON_CUSTOMIZATION_COMMAND = 0x18, + TURN_OFF_CUSTOMIZATION_COMMAND = 0x19, + LED_EFFECT_START_COMMAND = 0xF0, + LED_SYNC_INITIAL_COMMAND = 0xF1, + LED_SYNC_START_COMMAND = 0xF2, + LED_SYNC_STOP_COMMAND = 0xF3, + RANDOM_PACKET_START_COMMAND = 0xAB, +}; + +/*-----------------------------------------*\ +| Modes | +\*-----------------------------------------*/ +enum +{ + CUSTOM_MODE_VALUE = 0x00, + STATIC_MODE_VALUE = 0x01, + KEYSTROKE_LIGHT_UP_MODE_VALUE = 0x02, + KEYSTROKE_DIM_MODE_VALUE = 0x03, + SPARKLE_MODE_VALUE = 0x04, + RAIN_MODE_VALUE = 0x05, + RANDOM_COLORS_MODE_VALUE = 0x06, + BREATHING_MODE_VALUE = 0x07, + SPECTRUM_CYCLE_MODE_VALUE = 0x08, + RING_GRADIENT_MODE_VALUE = 0x09, + VERTICAL_GRADIENT_MODE_VALUE = 0x0A, + HORIZONTAL_GRADIENT_WAVE_MODE_VALUE = 0x0B, + AROUND_EDGES_MODE_VALUE = 0x0C, + KEYSTROKE_HORIZONTAL_LINES_VALUE = 0x0D, + KEYSTROKE_TITLED_LINES_MODE_VALUE = 0x0E, + KEYSTROKE_RIPPLES_MODE_VALUE = 0x0F, + SEQUENCE_MODE_VALUE = 0x10, + WAVE_LINE_MODE_VALUE = 0x11, + TILTED_LINES_MODE_VALUE = 0x12, + BACK_AND_FORTH_MODE_VALUE = 0x13, + LIGHTS_OFF_MODE_VALUE = 0x80, +}; + +/*-----------------------------------------*\ +| Other settings | +\*-----------------------------------------*/ +enum +{ + KEYCHRON_MIN_SPEED = 0x00, + KEYCHRON_MAX_SPEED = 0x0F, + KEYCHRON_MIN_BRIGHTNESS = 0x00, + KEYCHRON_MAX_BRIGHTNESS = 0x0F, +}; + + +class KeychronKeyboardController +{ +public: + KeychronKeyboardController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~KeychronKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetLedSequencePositions(std::vector positions); + void SetMode(std::vector modes, int active_mode, std::vector colors); + +protected: + hid_device* dev; + +private: + std::string location; + std::string name; + std::string version; + std::vector led_sequence_positions; + + void SetCustomization(bool state); + void StartEffectPage(); + void StartEffectCommand(); + void EndCommunication(); + + void Read(); + void Send(unsigned char data[PACKET_DATA_LENGTH]); +}; diff --git a/Controllers/KeychronKeyboardController/KeychronKeyboardControllerDetect.cpp b/Controllers/KeychronKeyboardController/KeychronKeyboardControllerDetect.cpp new file mode 100644 index 0000000..a590049 --- /dev/null +++ b/Controllers/KeychronKeyboardController/KeychronKeyboardControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| KeychronKeyboardControllerDetect.cpp | +| | +| Detector for Keychron keyboard | +| | +| Morgan Guimard (morg) 20 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "KeychronKeyboardController.h" +#include "RGBController_KeychronKeyboard.h" + +/*---------------------------------------------------------*\ +| KeychronKeyboard vendor ID | +\*---------------------------------------------------------*/ +#define KEYCHRON_KEYBOARD_VID 0x05AC + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define KEYCHRON_K3_V2_OPTICAL_RGB_PID 0x024F + +void DetectKeychronKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + KeychronKeyboardController* controller = new KeychronKeyboardController(dev, *info, name); + RGBController_KeychronKeyboard* rgb_controller = new RGBController_KeychronKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Keychron Gaming Keyboard 1", DetectKeychronKeyboardControllers, KEYCHRON_KEYBOARD_VID, KEYCHRON_K3_V2_OPTICAL_RGB_PID, 0, 0x0001, 0x06); diff --git a/Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.cpp b/Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.cpp new file mode 100644 index 0000000..b0e6990 --- /dev/null +++ b/Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.cpp @@ -0,0 +1,678 @@ +/*---------------------------------------------------------*\ +| RGBController_KeychronKeyboard.cpp | +| | +| RGBController for Keychron keyboard | +| | +| Morgan Guimard (morg) 20 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBControllerKeyNames.h" +#include "RGBController_KeychronKeyboard.h" + +#define NA 0xFFFFFFFF + +typedef struct +{ + const unsigned int width; /* matrix width */ + const unsigned int height; /* matrix height */ + std::vector> matrix_map; /* matrix map */ + std::vector led_names; /* led names */ + std::vector led_sequence_positions; /* position in buffers */ +} keychron; + +/*-----------------------------------------*\ +| The one that is showed in the original | +| issue | +\*-----------------------------------------*/ +static keychron default_keychron = +{ + 17, + 5, + { + { 0, 5, 7, 12, 16, 20, 24, 29, 33, 37, 41, 46, 51, 54, NA, 61, 66}, + { 1, NA, 8, 13, 17, 21, 25, 30, 34, 38, 42, 47, 52, 55, 59, 62, 67}, + { 2, NA, 9, 14, 18, 22, 26, 31, 35, 39, 43, 48, 53, 56, NA, 63, 68}, + { 3, NA, 10, 15, 19, 23, 27, 32, 36, 40, 44, 49, NA, 57, NA, 64, 69}, + { 4, 6, 11, NA, NA, NA, 28, NA, NA, NA, 45, 50, NA, 58, 60, 65, 70} + }, + { + KEY_EN_ESCAPE, //0 + KEY_EN_TAB, //1 + KEY_EN_CAPS_LOCK, //2 + KEY_EN_LEFT_SHIFT, //3 + KEY_EN_LEFT_CONTROL, //4 + + KEY_EN_1, //5 + KEY_EN_LEFT_WINDOWS, //6 + + KEY_EN_2, //7 + KEY_EN_Q, //8 + KEY_EN_A, //9 + KEY_EN_Z, //10 + KEY_EN_LEFT_ALT, //11 + + KEY_EN_3, //12 + KEY_EN_W, //13 + KEY_EN_S, //14 + KEY_EN_X, //15 + + KEY_EN_4, //16 + KEY_EN_E, //17 + KEY_EN_D, //18 + KEY_EN_C, //19 + + KEY_EN_5, //20 + KEY_EN_R, //21 + KEY_EN_F, //22 + KEY_EN_V, //23 + + KEY_EN_6, //24 + KEY_EN_T, //25 + KEY_EN_G, //26 + KEY_EN_B, //27 + KEY_EN_SPACE, //28 + + KEY_EN_7, //29 + KEY_EN_Y, //30 + KEY_EN_H, //31 + KEY_EN_N, //32 + + KEY_EN_8, //33 + KEY_EN_U, //34 + KEY_EN_J, //35 + KEY_EN_M, //36 + + KEY_EN_9, //37 + KEY_EN_I, //38 + KEY_EN_K, //39 + KEY_EN_COMMA, //40 + + KEY_EN_0, //41 + KEY_EN_O, //42 + KEY_EN_L, //43 + KEY_EN_PERIOD, //44 + KEY_EN_RIGHT_ALT, //45 + + KEY_EN_MINUS, //46 + KEY_EN_P, //47 + KEY_EN_SEMICOLON, //48 + KEY_EN_FORWARD_SLASH, //49 + KEY_EN_RIGHT_FUNCTION, //50 + + + KEY_EN_EQUALS, //51 + KEY_EN_LEFT_BRACKET, //52 + KEY_EN_QUOTE, //53 + + KEY_EN_BACKSPACE, //54 + KEY_EN_RIGHT_BRACKET, //55 + KEY_EN_ISO_ENTER, //56 + KEY_EN_RIGHT_SHIFT, //57 + KEY_EN_RIGHT_CONTROL, //58 + + KEY_EN_ISO_BACK_SLASH, //59 + KEY_EN_LEFT_ARROW, //60 + + KEY_EN_INSERT, //61 + KEY_EN_DELETE, //62 + KEY_EN_PAUSE_BREAK, //63 + KEY_EN_UP_ARROW, //64 + KEY_EN_DOWN_ARROW, //65 + + KEY_EN_HOME, //66 + KEY_EN_END, //67 + KEY_EN_PAGE_UP, //68 + KEY_EN_PAGE_DOWN, //69 + KEY_EN_RIGHT_ARROW //70 + }, + { + 0, //KEY_EN_ESCAPE, //0 + 1, //KEY_EN_TAB, //1 + 2, //KEY_EN_CAPS_LOCK, //2 + 3, //KEY_EN_LEFT_SHIFT, //3 + 4, //KEY_EN_LEFT_CONTROL, //4 + 5, //KEY_EN_1, //5 + 6, //KEY_EN_LEFT_WINDOWS, //6 + 7, //KEY_EN_2, //7 + 8, //KEY_EN_Q, //8 + 9, //KEY_EN_A, //9 + 10, //KEY_EN_Z, //10 + 11, //KEY_EN_LEFT_ALT, //11 + 12, //KEY_EN_3, //12 + 13, //KEY_EN_W, //13 + 14, //KEY_EN_S, //14 + 15, //KEY_EN_X, //15 + 16, //KEY_EN_4, //16 + 17, //KEY_EN_E, //17 + 18, //KEY_EN_D, //18 + 19, //KEY_EN_C, //19 + 20, //KEY_EN_5, //20 + 21, //KEY_EN_R, //21 + 22, //KEY_EN_F, //22 + 23, //KEY_EN_V, //23 + 24, //KEY_EN_6, //24 + 25, //KEY_EN_T, //25 + 26, //KEY_EN_G, //26 + 27, //KEY_EN_B, //27 + 28, //KEY_EN_SPACE, //28 + 29, //KEY_EN_7, //29 + 30, //KEY_EN_Y, //30 + 31, //KEY_EN_H, //31 + 32, //KEY_EN_N, //32 + 33, //KEY_EN_8, //33 + 34, //KEY_EN_U, //34 + 35, //KEY_EN_J, //35 + 36, //KEY_EN_M, //36 + 37, //KEY_EN_9, //37 + 38, //KEY_EN_I, //38 + 39, //KEY_EN_K, //39 + 40, //KEY_EN_COMMA, //40 + 41, //KEY_EN_0, //41 + 42, //KEY_EN_O, //42 + 43, //KEY_EN_L, //43 + 44, //KEY_EN_PERIOD, //44 + 45, //KEY_EN_RIGHT_ALT, //45 + 46, //KEY_EN_MINUS, //46 + 47, //KEY_EN_P, //47 + 48, //KEY_EN_SEMICOLON, //48 + 49, //KEY_EN_FORWARD_SLASH, //49 + 50, //KEY_EN_RIGHT_FUNCTION, //50 + 51, //KEY_EN_EQUALS, //51 + 52, //KEY_EN_LEFT_BRACKET, //52 + 53, //KEY_EN_QUOTE, //53 + 54, //KEY_EN_BACKSPACE, //54 + 55, //KEY_EN_RIGHT_BRACKET, //55 + 56, //KEY_EN_ISO_ENTER, //56 + 57, //KEY_EN_RIGHT_SHIFT, //57 + 58, //KEY_EN_RIGHT_CONTROL, //58 + 59, //KEY_EN_ISO_BACK_SLASH, //59 + 60, //KEY_EN_LEFT_ARROW, //60 + 61, //KEY_EN_INSERT, //61 + 62, //KEY_EN_DELETE, //62 + 63, //KEY_EN_PAUSE_BREAK, //63 + 64, //KEY_EN_UP_ARROW, //64 + 65, //KEY_EN_DOWN_ARROW, //65 + 66, //KEY_EN_HOME, //66 + 67, //KEY_EN_END, //67 + 68, //KEY_EN_PAGE_UP, //68 + 69, //KEY_EN_PAGE_DOWN, //69 + 70 //KEY_EN_RIGHT_ARROW //70 + } +}; + +/*-----------------------------------------*\ +| K3 V2 VERSION | +\*-----------------------------------------*/ +static keychron k3_keychron = +{ + 16, + 6, + { + { 0, 6, 9, 15, 20, 25, 30, 36, 41, 46, 51, 57, 63, 69, 73, 78}, + { 1, 7, 10, 16, 21, 26, 31, 37, 42, 47, 52, 58, 64, 70, NA, 79}, + { 2, NA, 11, 17, 22, 27, 32, 38, 43, 48, 53, 59, 65, 71, 74, 80}, + { 3, NA, 12, 18, 23, 28, 33, 39, 44, 49, 54, 60, 66, NA, 75, 81}, + { 4, NA, 13, 19, 24, 29, 34, 40, 45, 50, 55, 61, 67, NA, 76, 82}, + { 5, 8, 14, NA, NA, NA, 35, NA, NA, NA, 56, 62, 68, 72, 77, 83} + }, + { + KEY_EN_ESCAPE, //0 + KEY_EN_BACK_TICK, //1 + KEY_EN_TAB, //2 + KEY_EN_CAPS_LOCK, //3 + KEY_EN_LEFT_SHIFT, //4 + KEY_EN_LEFT_CONTROL, //5 + + KEY_EN_F1, //6 + KEY_EN_1, //7 + KEY_EN_LEFT_WINDOWS, //8 + + KEY_EN_F2, //9 + KEY_EN_2, //10 + KEY_EN_Q, //11 + KEY_EN_A, //12 + KEY_EN_Z, //13 + KEY_EN_LEFT_ALT, //14 + + KEY_EN_F3, //15 + KEY_EN_3, //16 + KEY_EN_W, //17 + KEY_EN_S, //18 + KEY_EN_X, //19 + + KEY_EN_F4, //20 + KEY_EN_4, //21 + KEY_EN_E, //22 + KEY_EN_D, //23 + KEY_EN_C, //24 + + KEY_EN_F5, //25 + KEY_EN_5, //26 + KEY_EN_R, //27 + KEY_EN_F, //28 + KEY_EN_V, //29 + + KEY_EN_F6, //30 + KEY_EN_6, //31 + KEY_EN_T, //32 + KEY_EN_G, //33 + KEY_EN_B, //34 + KEY_EN_SPACE, //35 + + KEY_EN_F7, //36 + KEY_EN_7, //37 + KEY_EN_Y, //38 + KEY_EN_H, //39 + KEY_EN_N, //40 + + KEY_EN_F8, //41 + KEY_EN_8, //42 + KEY_EN_U, //43 + KEY_EN_J, //44 + KEY_EN_M, //45 + + KEY_EN_F9, //46 + KEY_EN_9, //47 + KEY_EN_I, //48 + KEY_EN_K, //49 + KEY_EN_COMMA, //50 + + KEY_EN_F10, //51 + KEY_EN_0, //52 + KEY_EN_O, //53 + KEY_EN_L, //54 + KEY_EN_PERIOD, //55 + KEY_EN_RIGHT_ALT, //56 + + KEY_EN_F11, //57 + KEY_EN_MINUS, //58 + KEY_EN_P, //59 + KEY_EN_SEMICOLON, //60 + KEY_EN_FORWARD_SLASH, //61 + KEY_EN_RIGHT_FUNCTION, //62 + + KEY_EN_F12, //63 + KEY_EN_NUMPAD_PLUS, //64 + KEY_EN_LEFT_BRACKET, //65 + KEY_EN_QUOTE, //66 + KEY_EN_LEFT_SHIFT, //67 + KEY_EN_RIGHT_CONTROL, //68 + + KEY_EN_PRINT_SCREEN, //69 + KEY_EN_BACKSPACE, //70 + KEY_EN_RIGHT_BRACKET, //71 + KEY_EN_LEFT_ARROW, //72 + + KEY_EN_DELETE, //73 + KEY_EN_ANSI_BACK_SLASH, //74 + KEY_EN_ISO_ENTER, //75 + KEY_EN_UP_ARROW, //76 + KEY_EN_DOWN_ARROW, //77 + + "Key: Light", //78 + KEY_EN_PAGE_UP, //79 + KEY_EN_PAGE_DOWN, //80 + KEY_EN_HOME, //81 + KEY_EN_END, //82 + KEY_EN_RIGHT_ARROW //83 + }, + { + 143, + 18, + 34, + 50, + 66, + 81, + 2, + 19, + 82, + 3, + 20, + 35, + 51, + 67, + 83, + 4, + 21, + 36, + 52, + 68, + 5, + 22, + 37, + 53, + 69, + 6, + 23, + 38, + 54, + 70, + 7, + 24, + 39, + 55, + 71, + 84, + 8, + 25, + 40, + 56, + 72, + 9, + 26, + 41, + 57, + 73, + 10, + 27, + 42, + 58, + 74, + 11, + 28, + 43, + 59, + 75, + 85, + 12, + 29, + 44, + 60, + 76, + 86, + 13, + 30, + 45, + 61, + 77, + 87, + 14, + 31, + 46, + 88, + 15, + 47, + 63, + 78, + 89, + 16, + 32, + 48, + 64, + 79, + 90 + } +}; + +typedef struct +{ + std::string name; + int value; + int flags; +} keychron_effect; + +/**------------------------------------------------------------------*\ + @name Keychron Keyboard + @category Keyboard + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectKeychronKeyboardControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_KeychronKeyboard::RGBController_KeychronKeyboard(KeychronKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Keychron"; + type = DEVICE_TYPE_KEYBOARD; + description = name; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Custom; + Custom.name = "Custom"; + Custom.value = CUSTOM_MODE_VALUE; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.brightness_min = KEYCHRON_MIN_BRIGHTNESS; + Custom.brightness_max = KEYCHRON_MAX_BRIGHTNESS; + Custom.brightness = KEYCHRON_MAX_BRIGHTNESS; + modes.push_back(Custom); + + keychron_effect keychron_effects[20] = + { + { + "Static", + STATIC_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Keystroke light up", + KEYSTROKE_LIGHT_UP_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Keystroke dim", + KEYSTROKE_DIM_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Sparkle", + SPARKLE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Rain", + RAIN_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Random colors", + RANDOM_COLORS_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Breathing", + BREATHING_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Spectrum cycle", + SPECTRUM_CYCLE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Ring gradient", + RING_GRADIENT_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Vertical gradient", + VERTICAL_GRADIENT_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Horizontal gradient / Rainbow wave", + HORIZONTAL_GRADIENT_WAVE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Around edges", + AROUND_EDGES_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Keystroke horizontal lines", + KEYSTROKE_HORIZONTAL_LINES_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Keystroke tilted lines", + KEYSTROKE_TITLED_LINES_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Keystroke ripples", + KEYSTROKE_RIPPLES_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Sequence", + SEQUENCE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Wave line", + WAVE_LINE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Tilted lines", + TILTED_LINES_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Back and forth", + BACK_AND_FORTH_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Off", + LIGHTS_OFF_MODE_VALUE, + MODE_FLAG_AUTOMATIC_SAVE + } + }; + + for(const keychron_effect& effect : keychron_effects) + { + mode m; + m.name = effect.name; + m.value = effect.value; + m.flags = effect.flags; + + if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + m.color_mode = MODE_COLORS_MODE_SPECIFIC; + m.colors_min = 1; + m.colors_max = 1; + m.colors.resize(1); + } + else + { + m.color_mode = MODE_COLORS_NONE; + m.colors_min = 0; + m.colors_max = 0; + m.colors.resize(0); + } + + if(m.flags & MODE_FLAG_HAS_SPEED) + { + m.speed_min = KEYCHRON_MIN_SPEED; + m.speed_max = KEYCHRON_MAX_SPEED; + m.speed = m.speed_min; + } + + if(m.flags & MODE_FLAG_HAS_BRIGHTNESS) + { + m.brightness_min = KEYCHRON_MIN_BRIGHTNESS; + m.brightness_max = KEYCHRON_MAX_BRIGHTNESS; + m.brightness = m.brightness_max; + } + + modes.push_back(m); + } + + SetupZones(); +} + +RGBController_KeychronKeyboard::~RGBController_KeychronKeyboard() +{ + delete controller; +} + +void RGBController_KeychronKeyboard::SetupZones() +{ + /*-----------------------------------------*\ + | TODO: add logical switch here when we | + | will have to add different layouts | + \*-----------------------------------------*/ + keychron keyboard = k3_keychron; + + controller->SetLedSequencePositions(keyboard.led_sequence_positions); + + /*-----------------------------------------*\ + | Create the zone | + \*-----------------------------------------*/ + unsigned int zone_size = 0; + + zone keyboard_zone; + keyboard_zone.name = ZONE_EN_KEYBOARD; + keyboard_zone.type = ZONE_TYPE_MATRIX; + + keyboard_zone.matrix_map = new matrix_map_type; + keyboard_zone.matrix_map->height = keyboard.height; + keyboard_zone.matrix_map->width = keyboard.width; + + keyboard_zone.matrix_map->map = new unsigned int[keyboard.height * keyboard.width]; + + for(unsigned int w = 0; w < keyboard.width; w++) + { + for(unsigned int h = 0; h < keyboard.height; h++) + { + unsigned int key = keyboard.matrix_map[h][w]; + keyboard_zone.matrix_map->map[h * keyboard.width + w] = key; + + if(key != NA) + { + led new_led; + new_led.name = keyboard.led_names[key]; + leds.push_back(new_led); + zone_size++; + } + } + } + + keyboard_zone.leds_min = zone_size; + keyboard_zone.leds_max = zone_size; + keyboard_zone.leds_count = zone_size; + + zones.push_back(keyboard_zone); + + SetupColors(); +} + +void RGBController_KeychronKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_KeychronKeyboard::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_KeychronKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetMode(modes, active_mode, colors); +} + +void RGBController_KeychronKeyboard::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_KeychronKeyboard::DeviceUpdateMode() +{ + UpdateZoneLEDs(0); +} diff --git a/Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.h b/Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.h new file mode 100644 index 0000000..e8c02e1 --- /dev/null +++ b/Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_KeychronKeyboard.h | +| | +| RGBController for Keychron keyboard | +| | +| Morgan Guimard (morg) 20 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "KeychronKeyboardController.h" + +class RGBController_KeychronKeyboard : public RGBController +{ +public: + RGBController_KeychronKeyboard(KeychronKeyboardController* controller_ptr); + ~RGBController_KeychronKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + KeychronKeyboardController* controller; +}; diff --git a/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.cpp b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.cpp new file mode 100644 index 0000000..2459e89 --- /dev/null +++ b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.cpp @@ -0,0 +1,356 @@ +/*---------------------------------------------------------*\ +| KingstonFuryDRAMController.cpp | +| | +| Driver for Kingston Fury DDR4/5 RAM modules | +| | +| Geofrey Mon (geofbot) 14 Jul 2024 | +| Milan Cermak (krysmanta) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "KingstonFuryDRAMController.h" +#include "RGBController.h" +#include "LogManager.h" + +KingstonFuryDRAMController::KingstonFuryDRAMController(i2c_smbus_interface* bus, unsigned char base_addr, std::vector slots, std::string dev_name) +{ + this->bus = bus; + this->base_addr = base_addr; + this->slots = slots; + this->name = dev_name; + + reg_cache.resize(slots.size()); +} + +std::string KingstonFuryDRAMController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + return_string.append(", addresses ["); + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + char addr[5]; + snprintf(addr, 5, "0x%02X", base_addr + slots[idx]); + return_string.append(addr); + if(idx < slots.size() - 1) + { + return_string.append(","); + } + else + { + return_string.append("]"); + } + } + return("I2C: " + return_string); +} + +std::string KingstonFuryDRAMController::GetDeviceName() +{ + return(name); +} + +unsigned int KingstonFuryDRAMController::GetLEDCount() +{ + return(GetLEDPerDIMM() * (unsigned int)slots.size()); +} + +unsigned int KingstonFuryDRAMController::GetSlotCount() +{ + return((unsigned int)slots.size()); +} + +unsigned char KingstonFuryDRAMController::GetMode() +{ + unsigned char mode = 0; + CachedRead(0, FURY_REG_MODE, &mode); + return mode; +} + +bool KingstonFuryDRAMController::SmbusRead(int slot_idx, unsigned char reg, unsigned char *val) +{ + if(val == NULL) + { + return false; + } + + unsigned char device_addr = base_addr + slots[slot_idx]; + int res; + + for(int retries = 1; retries <= 5; retries++) + { + res = bus->i2c_smbus_read_word_data(device_addr, reg); + if(res >= 0) + { + *val = (res >> 8) & 0xFF; + LOG_DEBUG("[%s] %02X reading register &%02X=%02X; res=%02X", + FURY_CONTROLLER_NAME, device_addr, reg, *val, res); + return true; + } + else + { + std::this_thread::sleep_for(3 * retries * FURY_DELAY); + } + } + return false; +} + +bool KingstonFuryDRAMController::SmbusWrite(int slot_idx, unsigned char reg, unsigned char val) +{ + unsigned char device_addr = base_addr + slots[slot_idx]; + int res; + + for(int retries = 1; retries <= 5; retries++) + { + res = bus->i2c_smbus_write_byte_data(device_addr, + reg, val); + LOG_DEBUG("[%s] %02X setting register &%02X=%02X; res=%02X", + FURY_CONTROLLER_NAME, device_addr, reg, val, res); + if(res >= 0) + { + return true; + } + else + { + std::this_thread::sleep_for(3 * retries * FURY_DELAY); + } + } + return false; +} + +// returns whether a read was successful +bool KingstonFuryDRAMController::CachedRead(int slot_idx, unsigned char reg, unsigned char *val) +{ + if(val == NULL) + { + return false; + } + + unsigned char device_addr = base_addr + slots[slot_idx]; + if(reg_cache[slot_idx].find(reg) == reg_cache[slot_idx].end()) + { + if(SmbusRead(slot_idx, reg, val)) + { + reg_cache[slot_idx][reg] = *val; + return true; + } + LOG_ERROR("[%s] %02X failed to get register &%02X", + FURY_CONTROLLER_NAME, device_addr, reg); + return false; + } + else + { + *val = reg_cache[slot_idx][reg]; + return true; + } +} + +// returns whether a write was actually performed +bool KingstonFuryDRAMController::CachedWrite(int slot_idx, unsigned char reg, unsigned char val) +{ + unsigned char device_addr = base_addr + slots[slot_idx]; + if(reg_cache[slot_idx].find(reg) == reg_cache[slot_idx].end() || + reg_cache[slot_idx][reg] != val) + { + if(SmbusWrite(slot_idx, reg, val)) + { + reg_cache[slot_idx][reg] = val; + return true; + } + LOG_ERROR("[%s] %02X failed to set register &%02X=%02X", + FURY_CONTROLLER_NAME, device_addr, reg, val); + return false; + } + else + { + LOG_DEBUG("[%s] %02X register already set &%02X=%02X", + FURY_CONTROLLER_NAME, device_addr, reg, val); + return false; + } +} + +void KingstonFuryDRAMController::SendPreamble(bool /*synchronize*/) +{ + SendBegin(); + + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + char written_index = 0; +#ifdef FURY_SYNC + if(!synchronize) + { + // some modes set all indices to 0 so that the + // individual sticks don't sync with each other + written_index = 0; + } + else + { + /*--------------------------------------------------------------*\ + | The index tells physical location of the RAM slot | + | from the border to the CPU slot. On most motherboards, | + | the address relates to the slot location, | + | but there are exceptions. | + | The official software writes the indices in decreasing order. | + | | + | Hardware effects seem to support only up to 4 sticks. | + | So we give the first 4 and last 4 sticks separate numbering. | + \*--------------------------------------------------------------*/ + written_index = idx % 4; + } +#endif + LOG_DEBUG("[%s] %02X writing index %d", + FURY_CONTROLLER_NAME, base_addr + slots[idx], + written_index); + SmbusWrite((int)idx, FURY_REG_INDEX, written_index); + } + // The RGB controller is a bit slow and requires delay; + // however, we can delay once for all of the sticks instead of + // delaying individually for each stick. + std::this_thread::sleep_for(FURY_DELAY); + + SendApply(); +} + +void KingstonFuryDRAMController::SendBegin() +{ + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + LOG_DEBUG("[%s] %02X beginning transaction", + FURY_CONTROLLER_NAME, base_addr + slots[idx]); + SmbusWrite((int)idx, FURY_REG_APPLY, FURY_BEGIN_TRNSFER); + } + std::this_thread::sleep_for(FURY_DELAY); +} + +void KingstonFuryDRAMController::SendApply() +{ + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + LOG_DEBUG("[%s] %02X ending transaction", + FURY_CONTROLLER_NAME, base_addr + slots[idx]); + SmbusWrite((int)idx, FURY_REG_APPLY, FURY_END_TRNSFER); + } + std::this_thread::sleep_for(FURY_DELAY); +} + +void KingstonFuryDRAMController::SetMode(unsigned char val) +{ + SetRegister(FURY_REG_MODE, val); +} + +void KingstonFuryDRAMController::SetNumSlots() +{ + if(slots.size() <= 4) + { + SetRegister(FURY_REG_NUM_SLOTS, (unsigned char)slots.size()); + } + else + { + // hardware effects seem to only support at most 4 slots; + // if there are >= 4 slots, then essentially the first 4 slots + // run their effects independent of the last 4 slots + SetRegister(FURY_REG_NUM_SLOTS, 4); + } +} + +void KingstonFuryDRAMController::SetRegister(int reg, unsigned char val) +{ + bool write_occurred = false; + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + write_occurred = CachedWrite((int)idx, reg, val) || write_occurred; + } + if(write_occurred) + { + std::this_thread::sleep_for(FURY_DELAY); + } +} + +void KingstonFuryDRAMController::SetRegister(int reg, std::vector vals) +{ + bool write_occurred = false; + if(vals.size() < slots.size()) + { + LOG_ERROR("[%s] vector of values has wrong size when setting register &%02X", + FURY_CONTROLLER_NAME, reg); + return; + } + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + write_occurred = CachedWrite((int)idx, reg, vals[idx]) || write_occurred; + } + if(write_occurred) + { + std::this_thread::sleep_for(FURY_DELAY); + } +} + +void KingstonFuryDRAMController::SetModeColors(std::vector colors) +{ + if(colors.empty() || (colors.size() > FURY_MAX_MODE_COLORS)) + { + return; + } + + SetRegister(FURY_REG_NUM_COLORS, (unsigned char)colors.size()); + + for(std::size_t idx = 0; idx < colors.size(); idx++) + { + RGBColor color = colors[idx]; + unsigned char red = RGBGetRValue(color); + unsigned char green = RGBGetGValue(color); + unsigned char blue = RGBGetBValue(color); + + int red_idx = FURY_REG_MODE_BASE_RED + (int)idx * 3; + int green_idx = FURY_REG_MODE_BASE_GREEN + (int)idx * 3; + int blue_idx = FURY_REG_MODE_BASE_BLUE + (int)idx * 3; + + SetRegister(red_idx, red); + SetRegister(green_idx, green); + SetRegister(blue_idx, blue); + } +} + +void KingstonFuryDRAMController::SetLEDColors(std::vector colors) +{ + if(colors.size() != GetLEDCount()) + { + return; + } + + unsigned int led_per_dimm = GetLEDPerDIMM(); + for(unsigned int led_idx = 0; led_idx < led_per_dimm; led_idx++) + { + int red_register = FURY_REG_BASE_RED + 3 * led_idx; + int green_register = FURY_REG_BASE_GREEN + 3 * led_idx; + int blue_register = FURY_REG_BASE_BLUE + 3 * led_idx; + + std::vector reds, greens, blues; + for(std::size_t slot_idx = 0; slot_idx < GetSlotCount(); slot_idx++) + { + RGBColor color = colors[slot_idx * led_per_dimm + led_idx]; + unsigned char red = RGBGetRValue(color); + unsigned char green = RGBGetGValue(color); + unsigned char blue = RGBGetBValue(color); + + reds.push_back(red); + greens.push_back(green); + blues.push_back(blue); + } + + SetRegister(red_register, reds); + SetRegister(blue_register, blues); + SetRegister(green_register, greens); + } +} + +unsigned int KingstonFuryDRAMController::GetLEDPerDIMM() +{ + if(base_addr == FURY_BASE_ADDR_DDR4) + { + return(FURY_LEDS_PER_DIMM_DDR4); + } + return(FURY_LEDS_PER_DIMM_DDR5); +} diff --git a/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.h b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.h new file mode 100644 index 0000000..41d79bc --- /dev/null +++ b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.h @@ -0,0 +1,159 @@ +/*---------------------------------------------------------*\ +| KingstonFuryDRAMController.h | +| | +| Driver for Kingston Fury DDR4/5 RAM modules | +| | +| Geofrey Mon (geofbot) 14 Jul 2024 | +| Milan Cermak (krysmanta) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +#define FURY_CONTROLLER_NAME "Kingston Fury DDR4/5 DRAM" +#define FURY_BASE_ADDR_DDR4 0x58 +#define FURY_BASE_ADDR_DDR5 0x60 +#define FURY_DELAY std::chrono::milliseconds(10) +#define FURY_LEDS_PER_DIMM_DDR4 10 +#define FURY_LEDS_PER_DIMM_DDR5 12 +#define FURY_MAX_MODE_COLORS 10 +#define FURY_DEFAULT_BG_COLOR ToRGBColor(16,16,16) +#define FURY_ALT_DIRECTIONS {\ + FURY_DIR_BOTTOM_TO_TOP,\ + FURY_DIR_TOP_TO_BOTTOM,\ + FURY_DIR_BOTTOM_TO_TOP,\ + FURY_DIR_TOP_TO_BOTTOM,\ + FURY_DIR_BOTTOM_TO_TOP,\ + FURY_DIR_TOP_TO_BOTTOM,\ + FURY_DIR_BOTTOM_TO_TOP,\ + FURY_DIR_TOP_TO_BOTTOM} + +enum +{ + FURY_MODEL_BEAST_DDR5 = 0x10, + FURY_MODEL_RENEGADE_DDR5 = 0x11, + FURY_MODEL_BEAST_RGB_WHITE_DDR5 = 0x12, + FURY_MODEL_BEAST2_DDR5 = 0x15, + FURY_MODEL_BEAST_WHITE_DDR4 = 0x21, + FURY_MODEL_BEAST_DDR4 = 0x23, +}; + +enum +{ + FURY_REG_MODEL = 0x06, + FURY_REG_APPLY = 0x08, + FURY_REG_MODE = 0x09, + FURY_REG_INDEX = 0x0B, + FURY_REG_DIRECTION = 0x0C, + FURY_REG_DELAY = 0x0D, + FURY_REG_SPEED = 0x0E, + FURY_REG_DYNAMIC_HOLD_A = 0x12, + FURY_REG_DYNAMIC_HOLD_B = 0x13, + FURY_REG_DYNAMIC_FADE_A = 0x14, + FURY_REG_DYNAMIC_FADE_B = 0x15, + FURY_REG_BREATH_MIN_TO_MID = 0x16, + FURY_REG_BREATH_MID_TO_MAX = 0x17, + FURY_REG_BREATH_MAX_TO_MID = 0x18, + FURY_REG_BREATH_MID_TO_MIN = 0x19, + FURY_REG_BREATH_MIN_HOLD = 0x1A, + FURY_REG_BREATH_MAX_BRIGHTNESS = 0x1B, + FURY_REG_BREATH_MID_BRIGHTNESS = 0x1C, + FURY_REG_BREATH_MIN_BRIGHTNESS = 0x1D, + FURY_REG_BRIGHTNESS = 0x20, + FURY_REG_BG_RED = 0x23, + FURY_REG_BG_GREEN = 0x24, + FURY_REG_BG_BLUE = 0x25, + FURY_REG_LENGTH = 0x26, + FURY_REG_NUM_SLOTS = 0x27, + FURY_REG_NUM_COLORS = 0x30, + FURY_REG_MODE_BASE_RED = 0x31, + FURY_REG_MODE_BASE_GREEN = 0x32, + FURY_REG_MODE_BASE_BLUE = 0x33, + FURY_REG_BASE_RED = 0x50, + FURY_REG_BASE_GREEN = 0x51, + FURY_REG_BASE_BLUE = 0x52, +}; + +enum +{ + FURY_BEGIN_TRNSFER = 0x53, + FURY_END_TRNSFER = 0x44, +}; + +// Differentiate modes which use the same written value using the upper bytes. +// The lowest order byte is generally the value written to the mode register. +enum +{ + FURY_MODE_STATIC = 0x00, + FURY_MODE_RAINBOW = 0x001, + FURY_MODE_SPECTRUM = 0x101, + FURY_MODE_RHYTHM = 0x02, + FURY_MODE_BREATH = 0x03, + FURY_MODE_DYNAMIC = 0x04, + FURY_MODE_SLIDE = 0x005, + FURY_MODE_SLITHER = 0x105, + FURY_MODE_TELEPORT = 0x205, + FURY_MODE_WIND = 0x305, + FURY_MODE_COMET = 0x006, + FURY_MODE_RAIN = 0x106, + FURY_MODE_FIREWORK = 0x206, + FURY_MODE_VOLTAGE = 0x07, + FURY_MODE_COUNTDOWN = 0x08, + FURY_MODE_FLAME = 0x09, + FURY_MODE_TWILIGHT = 0x0A, + FURY_MODE_FURY = 0x0B, + FURY_MODE_DIRECT = 0x10, + FURY_MODE_PRISM = 0x11, + FURY_MODE_BREATH_DIRECT = 0x13, +}; + +enum +{ + FURY_DIR_BOTTOM_TO_TOP = 0x01, + FURY_DIR_TOP_TO_BOTTOM = 0x02, +}; + +class KingstonFuryDRAMController +{ +public: + KingstonFuryDRAMController(i2c_smbus_interface* bus, unsigned char base_addr, std::vector slots, std::string dev_name); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + unsigned int GetLEDCount(); + unsigned int GetLEDPerDIMM(); + unsigned int GetSlotCount(); + unsigned char GetMode(); + + void SendPreamble(bool synchronize); + void SendBegin(); + void SendApply(); + void SetMode(unsigned char val); + void SetNumSlots(); + + void SetRegister(int reg, unsigned char val); + void SetRegister(int reg, std::vector vals); + void SetModeColors(std::vector colors); + void SetLEDColors(std::vector colors); + +private: + bool CachedRead(int slot_idx, unsigned char reg, unsigned char *val); + bool CachedWrite(int slot_idx, unsigned char reg, unsigned char val); + bool SmbusRead(int slot_idx, unsigned char reg, unsigned char *val); + bool SmbusWrite(int slot_idx, unsigned char reg, unsigned char val); + + i2c_smbus_interface* bus; + std::vector slots; + unsigned char base_addr; + std::string name; + std::vector> reg_cache; +}; diff --git a/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMControllerDetect.cpp b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMControllerDetect.cpp new file mode 100644 index 0000000..d818fc0 --- /dev/null +++ b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMControllerDetect.cpp @@ -0,0 +1,201 @@ +/*---------------------------------------------------------*\ +| KingstonFuryDRAMControllerDetect.cpp | +| | +| Detection of Kingston Fury DDR4/5 RAM modules | +| | +| Geofrey Mon (geofbot) 14 Jul 2024 | +| Milan Cermak (krysmanta) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "KingstonFuryDRAMController.h" +#include "LogManager.h" +#include "RGBController_KingstonFuryDRAM.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +using namespace std::chrono_literals; + +typedef enum +{ + RESULT_PASS = 0, + RESULT_FAIL = 1, + RESULT_ERROR = 2 +} TestResult; + +bool TestDDR4Models(char code) +{ + return (code == FURY_MODEL_BEAST_WHITE_DDR4 || + code == FURY_MODEL_BEAST_DDR4); +} + +bool TestDDR5Models(char code) +{ + return (code == FURY_MODEL_BEAST_DDR5 || + code == FURY_MODEL_BEAST2_DDR5 || + code == FURY_MODEL_RENEGADE_DDR5 || + code == FURY_MODEL_BEAST_RGB_WHITE_DDR5); +} + +// Checking Fury signature in the RGB address space +TestResult TestForFurySignature(i2c_smbus_interface *bus, unsigned int slot_addr, bool (*modelChecker)(char)) +{ + bool passed = true; + char test_str[] = "FURY"; + int res; + + LOG_DEBUG("[%s] looking at 0x%02X", + FURY_CONTROLLER_NAME, slot_addr); + + // Start transaction + res = bus->i2c_smbus_write_byte_data(slot_addr, FURY_REG_APPLY, FURY_BEGIN_TRNSFER); + if(res < 0) + { + LOG_DEBUG("[%s] DIMM not present at 0x%02X", + FURY_CONTROLLER_NAME, slot_addr); + return RESULT_ERROR; + } + + std::this_thread::sleep_for(FURY_DELAY); + LOG_DEBUG("[%s] %02X beginning transaction; res=%02X", + FURY_CONTROLLER_NAME, slot_addr, res); + + // Read and check the signature + for(int i = 1; i <= 4; i++) + { + for(int retry = 3; retry > 0; retry--) + { + res = bus->i2c_smbus_read_word_data(slot_addr, i); + std::this_thread::sleep_for(FURY_DELAY); + LOG_DEBUG("[%s] Testing address %02X register %02X, res=%04X", + FURY_CONTROLLER_NAME, slot_addr, i, res); + // retry when there is an error or the returned value is 0xFFFF + if((res >= 0) && (res < 0xFFFF)) + { + break; + } + } + if(res < 0) + { + return RESULT_ERROR; + } + + char shifted = (res >> 8) & 0xFF; + if(shifted != test_str[i-1]) + { + passed = false; + break; + } + } + + if(passed) + { + // Get the model code + res = bus->i2c_smbus_read_word_data(slot_addr, FURY_REG_MODEL); + int model_code = res >> 8; + std::this_thread::sleep_for(FURY_DELAY); + LOG_DEBUG("[%s] Reading model code at address %02X register %02X, res=%02X", + FURY_CONTROLLER_NAME, slot_addr, FURY_REG_MODEL, res); + + if(!modelChecker(model_code)) + { + LOG_INFO("[%s] Unknown model code 0x%02X", FURY_CONTROLLER_NAME, model_code); + passed = false; + } + } + + // Close transaction + res = bus->i2c_smbus_write_byte_data(slot_addr, FURY_REG_APPLY, FURY_END_TRNSFER); + if(res < 0) + { + return RESULT_ERROR; + } + std::this_thread::sleep_for(FURY_DELAY); + LOG_DEBUG("[%s] %02X ending transaction; res=%02X", + FURY_CONTROLLER_NAME, slot_addr, res); + + return passed ? RESULT_PASS : RESULT_FAIL; +} + +void DetectKingstonFuryDRAMControllers(i2c_smbus_interface* bus, std::vector &slots, + uint8_t fury_base_addr, bool (*modelChecker)(char), std::vector &fury_slots) +{ + // Are these the Kingston Fury DRAMs + for(SPDWrapper *slot : slots) + { + TestResult result; + int retries = 0; + + result = RESULT_ERROR; + while(retries < 3 && result == RESULT_ERROR) + { + result = TestForFurySignature(bus, fury_base_addr + slot->index(), modelChecker); + if(result == RESULT_PASS) + { + break; + } + if(result == RESULT_ERROR) + { + // I/O error - wait for a bit and retry + retries++; + std::this_thread::sleep_for(FURY_DELAY); + } + } + + // RAM module successfully detected in the slot 'slot_index' + if(result == RESULT_PASS) + { + LOG_DEBUG("[%s] detected at slot index %d", + FURY_CONTROLLER_NAME, slot->index()); + fury_slots.push_back(slot->index()); + } + } +} + +/******************************************************************************************\ +* * +* DetectKingstonFuryDRAMControllers * +* * +* Detect Kingston Fury DDR4/5 DRAM controllers on the enumerated I2C busses. * +* * +\******************************************************************************************/ + +void DetectKingstonFuryDDR4Controllers(i2c_smbus_interface* bus, std::vector &slots, const std::string &name) +{ + std::vector fury_slots; + + DetectKingstonFuryDRAMControllers(bus, slots, FURY_BASE_ADDR_DDR4, TestDDR4Models, fury_slots); + + if(!fury_slots.empty()) + { + KingstonFuryDRAMController* controller = new KingstonFuryDRAMController(bus, FURY_BASE_ADDR_DDR4, fury_slots, name); + RGBController_KingstonFuryDRAM* rgb_controller = new RGBController_KingstonFuryDRAM(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectKingstonFuryDDR5Controllers(i2c_smbus_interface* bus, std::vector &slots, const std::string &name) +{ + std::vector fury_slots; + + DetectKingstonFuryDRAMControllers(bus, slots, FURY_BASE_ADDR_DDR5, TestDDR5Models, fury_slots); + + if(!fury_slots.empty()) + { + KingstonFuryDRAMController* controller = new KingstonFuryDRAMController(bus, FURY_BASE_ADDR_DDR5, fury_slots, name); + RGBController_KingstonFuryDRAM* rgb_controller = new RGBController_KingstonFuryDRAM(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_I2C_DIMM_DETECTOR("Kingston Fury DDR4 DRAM", DetectKingstonFuryDDR4Controllers, JEDEC_KINGSTON, SPD_DDR4_SDRAM); +REGISTER_I2C_DIMM_DETECTOR("Kingston Fury DDR4 DRAM", DetectKingstonFuryDDR4Controllers, JEDEC_KINGSTON_2, SPD_DDR4_SDRAM); +REGISTER_I2C_DIMM_DETECTOR("Kingston Fury DDR5 DRAM", DetectKingstonFuryDDR5Controllers, JEDEC_KINGSTON, SPD_DDR5_SDRAM); +REGISTER_I2C_DIMM_DETECTOR("Kingston Fury DDR5 DRAM", DetectKingstonFuryDDR5Controllers, JEDEC_KINGSTON_2, SPD_DDR5_SDRAM); +REGISTER_I2C_DIMM_DETECTOR("Kingston Fury DDR5 DRAM", DetectKingstonFuryDDR5Controllers, JEDEC_KINGSTON_3, SPD_DDR5_SDRAM); diff --git a/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.cpp b/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.cpp new file mode 100644 index 0000000..426a36f --- /dev/null +++ b/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.cpp @@ -0,0 +1,723 @@ +/*---------------------------------------------------------*\ +| RGBController_KingstonFuryDRAM.cpp | +| | +| Driver for Kingston Fury DDR4/5 RAM modules | +| | +| Geofrey Mon (geofbot) 14 Jul 2024 | +| Milan Cermak (krysmanta) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_KingstonFuryDRAM.h" +#include "KingstonFuryDRAMController.h" +#include "LogManager.h" + +const RGBColor default_colors[] = +{ + ToRGBColor(0xFF, 0x00, 0x00), + ToRGBColor(0x00, 0xFF, 0x00), + ToRGBColor(0xFF, 0x64, 0x00), + ToRGBColor(0x00, 0x00, 0xFF), + ToRGBColor(0xEF, 0xEF, 0x00), + ToRGBColor(0x80, 0x00, 0x80), + ToRGBColor(0x00, 0x6D, 0x77), + ToRGBColor(0xFF, 0xC8, 0x00), + ToRGBColor(0xFF, 0x55, 0xFF), + ToRGBColor(0x3C, 0x7D, 0xFF), +}; + + +/**------------------------------------------------------------------*\ + @name Kingston Fury DDR4/5 DRAM + @category RAM + @type SMBus + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectKingstonFuryDRAMControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_KingstonFuryDRAM::RGBController_KingstonFuryDRAM(KingstonFuryDRAMController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Kingston"; + type = DEVICE_TYPE_DRAM; + description = "Kingston Fury Beast/Renegade DDR4/5 DRAM Device"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = FURY_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.brightness = 80; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = FURY_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.assign(default_colors, default_colors + 1); + Static.brightness_min = 0; + Static.brightness_max = 100; + Static.brightness = 80; + modes.push_back(Static); + + // All speed values are inverted + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = FURY_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Rainbow.speed_min = 60; + Rainbow.speed_max = 0; + Rainbow.speed = 25; + Rainbow.direction = MODE_DIRECTION_UP; + Rainbow.brightness_min = 0; + Rainbow.brightness_max = 100; + Rainbow.brightness = 80; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Spectrum; + Spectrum.name = "Spectrum"; + Spectrum.value = FURY_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Spectrum.speed_min = 60; + Spectrum.speed_max = 0; + Spectrum.speed = 25; + Spectrum.direction = MODE_DIRECTION_UP; + Spectrum.brightness_min = 0; + Spectrum.brightness_max = 100; + Spectrum.brightness = 80; + Spectrum.color_mode = MODE_COLORS_NONE; + modes.push_back(Spectrum); + + mode Rhythm; + Rhythm.name = "Rhythm"; + Rhythm.value = FURY_MODE_RHYTHM; + Rhythm.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rhythm.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rhythm.colors_min = 2; + Rhythm.colors_max = 11; + Rhythm.colors.assign(default_colors, default_colors + 10); + Rhythm.colors.push_back(FURY_DEFAULT_BG_COLOR); + Rhythm.speed_min = 10; + Rhythm.speed_max = 0; + Rhythm.speed = 0; + Rhythm.brightness_min = 0; + Rhythm.brightness_max = 100; + Rhythm.brightness = 80; + modes.push_back(Rhythm); + + mode Breath; + Breath.name = "Breath"; + Breath.value = FURY_MODE_BREATH; + Breath.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breath.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breath.colors_min = 1; + Breath.colors_max = 10; + Breath.colors.assign(default_colors, default_colors + 10); + Breath.speed_min = 10; + Breath.speed_max = 1; + Breath.speed = 5; + Breath.brightness_min = 0; + Breath.brightness_max = 100; + Breath.brightness = 80; + modes.push_back(Breath); + + mode Dynamic; + Dynamic.name = "Dynamic"; + Dynamic.value = FURY_MODE_DYNAMIC; + Dynamic.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Dynamic.color_mode = MODE_COLORS_MODE_SPECIFIC; + Dynamic.colors_min = 1; + Dynamic.colors_max = 10; + Dynamic.colors.assign(default_colors, default_colors + 10); + Dynamic.speed_min = 1000; + Dynamic.speed_max = 100; + Dynamic.speed = 300; + Dynamic.brightness_min = 0; + Dynamic.brightness_max = 100; + Dynamic.brightness = 80; + modes.push_back(Dynamic); + + mode Slide; + Slide.name = "Slide"; + Slide.value = FURY_MODE_SLIDE; + Slide.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Slide.color_mode = MODE_COLORS_MODE_SPECIFIC; + Slide.colors_min = 2; + Slide.colors_max = 11; + Slide.colors.assign(default_colors, default_colors + 10); + Slide.colors.push_back(FURY_DEFAULT_BG_COLOR); + Slide.speed_min = 255; + Slide.speed_max = 0; + Slide.speed = 8; + Slide.direction = MODE_DIRECTION_UP; + Slide.brightness_min = 0; + Slide.brightness_max = 100; + Slide.brightness = 80; + modes.push_back(Slide); + + mode Slither; + Slither.name = "Slither"; + Slither.value = FURY_MODE_SLITHER; + Slither.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Slither.color_mode = MODE_COLORS_MODE_SPECIFIC; + Slither.colors_min = 2; + Slither.colors_max = 11; + Slither.colors.assign(default_colors, default_colors + 10); + Slither.colors.push_back(FURY_DEFAULT_BG_COLOR); + Slither.speed_min = 255; + Slither.speed_max = 0; + Slither.speed = 40; + Slither.brightness_min = 0; + Slither.brightness_max = 100; + Slither.brightness = 80; + modes.push_back(Slither); + + mode Teleport; + Teleport.name = "Teleport"; + Teleport.value = FURY_MODE_TELEPORT; + Teleport.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Teleport.color_mode = MODE_COLORS_MODE_SPECIFIC; + Teleport.colors_min = 2; + Teleport.colors_max = 11; + Teleport.colors.assign(default_colors, default_colors + 10); + Teleport.colors.push_back(FURY_DEFAULT_BG_COLOR); + Teleport.speed_min = 255; + Teleport.speed_max = 0; + Teleport.speed = 8; + Teleport.brightness_min = 0; + Teleport.brightness_max = 100; + Teleport.brightness = 80; + modes.push_back(Teleport); + + mode Wind; + Wind.name = "Wind"; + Wind.value = FURY_MODE_WIND; + Wind.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Wind.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wind.colors_min = 2; + Wind.colors_max = 11; + Wind.colors.assign(default_colors, default_colors + 10); + Wind.colors.push_back(FURY_DEFAULT_BG_COLOR); + Wind.speed_min = 255; + Wind.speed_max = 0; + Wind.speed = 8; + Wind.direction = MODE_DIRECTION_UP; + Wind.brightness_min = 0; + Wind.brightness_max = 100; + Wind.brightness = 80; + modes.push_back(Wind); + + mode Comet; + Comet.name = "Comet"; + Comet.value = FURY_MODE_COMET; + Comet.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Comet.color_mode = MODE_COLORS_MODE_SPECIFIC; + Comet.colors_min = 1; + Comet.colors_max = 10; + Comet.colors.assign(default_colors, default_colors + 10); + Comet.speed_min = 255; + Comet.speed_max = 0; + Comet.speed = 25; + Comet.direction = MODE_DIRECTION_UP; + Comet.brightness_min = 0; + Comet.brightness_max = 100; + Comet.brightness = 80; + modes.push_back(Comet); + + mode Rain; + Rain.name = "Rain"; + Rain.value = FURY_MODE_RAIN; + Rain.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Rain.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rain.colors_min = 1; + Rain.colors_max = 10; + Rain.colors.assign(default_colors, default_colors + 10); + Rain.speed_min = 28; + Rain.speed_max = 8; + Rain.speed = 25; + Rain.direction = MODE_DIRECTION_DOWN; + Rain.brightness_min = 0; + Rain.brightness_max = 100; + Rain.brightness = 80; + modes.push_back(Rain); + + mode Firework; + Firework.name = "Firework"; + Firework.value = FURY_MODE_FIREWORK; + Firework.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Firework.color_mode = MODE_COLORS_MODE_SPECIFIC; + Firework.colors_min = 1; + Firework.colors_max = 10; + Firework.colors.assign(default_colors, default_colors + 10); + Firework.speed_min = 83; + Firework.speed_max = 33; + Firework.speed = 33; + Firework.direction = MODE_DIRECTION_UP; + Firework.brightness_min = 0; + Firework.brightness_max = 100; + Firework.brightness = 80; + modes.push_back(Firework); + + mode Voltage; + Voltage.name = "Voltage"; + Voltage.value = FURY_MODE_VOLTAGE; + Voltage.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Voltage.color_mode = MODE_COLORS_MODE_SPECIFIC; + Voltage.colors_min = 2; + Voltage.colors_max = 11; + Voltage.colors.assign(default_colors, default_colors + 10); + Voltage.colors.push_back(FURY_DEFAULT_BG_COLOR); + Voltage.speed_min = 18; + Voltage.speed_max = 5; + Voltage.speed = 16; + Voltage.direction = MODE_DIRECTION_UP; + Voltage.brightness_min = 0; + Voltage.brightness_max = 100; + Voltage.brightness = 80; + modes.push_back(Voltage); + +#ifdef FURY_SYNC + mode Countdown; + Countdown.name = "Countdown"; + Countdown.value = FURY_MODE_COUNTDOWN; + Countdown.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Countdown.color_mode = MODE_COLORS_MODE_SPECIFIC; + Countdown.colors_min = 2; + Countdown.colors_max = 11; + Countdown.colors.assign(default_colors, default_colors + 10); + Countdown.colors.push_back(FURY_DEFAULT_BG_COLOR); + Countdown.speed_min = 76; + Countdown.speed_max = 20; + Countdown.speed = 76; + Countdown.direction = MODE_DIRECTION_UP; + Countdown.brightness_min = 0; + Countdown.brightness_max = 100; + Countdown.brightness = 80; + modes.push_back(Countdown); +#endif + + mode Flame; + Flame.name = "Flame"; + Flame.value = FURY_MODE_FLAME; + Flame.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Flame.speed_min = 64; + Flame.speed_max = 40; + Flame.speed = 64; + Flame.direction = MODE_DIRECTION_UP; + Flame.brightness_min = 0; + Flame.brightness_max = 100; + Flame.brightness = 80; + Flame.color_mode = MODE_COLORS_NONE; + modes.push_back(Flame); + + mode Twilight; + Twilight.name = "Twilight"; + Twilight.value = FURY_MODE_TWILIGHT; + Twilight.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Twilight.speed_min = 255; + Twilight.speed_max = 0; + Twilight.speed = 64; + Twilight.brightness_min = 0; + Twilight.brightness_max = 100; + Twilight.brightness = 80; + Twilight.color_mode = MODE_COLORS_NONE; + modes.push_back(Twilight); + + mode Fury; + Fury.name = "Fury"; + Fury.value = FURY_MODE_FURY; + Fury.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Fury.color_mode = MODE_COLORS_MODE_SPECIFIC; + Fury.colors_min = 2; + Fury.colors_max = 11; + Fury.colors.assign(default_colors, default_colors + 10); + Fury.colors.push_back(FURY_DEFAULT_BG_COLOR); + Fury.speed_min = 255; + Fury.speed_max = 0; + Fury.speed = 76; + Fury.direction = MODE_DIRECTION_UP; + Fury.brightness_min = 0; + Fury.brightness_max = 100; + Fury.brightness = 80; + modes.push_back(Fury); + + mode Prism; + Prism.name = "Prism"; + Prism.value = FURY_MODE_PRISM; + Prism.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Prism.speed_min = 60; + Prism.speed_max = 0; + Prism.speed = 40; + Prism.brightness_min = 0; + Prism.brightness_max = 100; + Prism.brightness = 80; + Prism.color_mode = MODE_COLORS_NONE; + modes.push_back(Prism); + + SetupZones(); + + // default per-LED color is red + colors.assign(colors.size(), default_colors[0]); +} + +RGBController_KingstonFuryDRAM::~RGBController_KingstonFuryDRAM() +{ + delete controller; +} + +void RGBController_KingstonFuryDRAM::SetupZones() +{ + for(unsigned int slot = 0; slot < controller->GetSlotCount(); slot++) + { + zone* new_zone = new zone; + + new_zone->name = "Fury Slot "; + new_zone->name.append(std::to_string(slot + 1)); + new_zone->type = ZONE_TYPE_LINEAR; + new_zone->leds_min = controller->GetLEDPerDIMM(); + new_zone->leds_max = new_zone->leds_min; + new_zone->leds_count = new_zone->leds_min; + new_zone->matrix_map = NULL; + + zones.push_back(*new_zone); + } + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led* new_led = new led(); + + new_led->name = "Fury Slot "; + new_led->name.append(std::to_string(zone_idx + 1)); + new_led->name.append(", LED "); + new_led->name.append(std::to_string(led_idx + 1)); + + new_led->value = (unsigned int)leds.size(); + + leds.push_back(*new_led); + } + } + + SetupColors(); +} + +void RGBController_KingstonFuryDRAM::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ + LOG_DEBUG("[%s] resize zone", + FURY_CONTROLLER_NAME); +} + +// some modes have different actual values to be written, depending on the color mode +unsigned char RGBController_KingstonFuryDRAM::GetRealModeValue() +{ + int mode_value = modes[active_mode].value; + switch(mode_value) + { + case FURY_MODE_BREATH: + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + return FURY_MODE_BREATH; + } + else + { + return FURY_MODE_BREATH_DIRECT; + } + } + return mode_value; +} + +void RGBController_KingstonFuryDRAM::DeviceUpdateLEDs() +{ + controller->SendBegin(); + controller->SetMode(GetRealModeValue()); + + // Fixed mode specific parameters + switch(modes[active_mode].value) + { + case FURY_MODE_STATIC: + controller->SetRegister(FURY_REG_DIRECTION, FURY_DIR_BOTTOM_TO_TOP); + controller->SetRegister(FURY_REG_DELAY, 0); + controller->SetRegister(FURY_REG_SPEED, 0); + break; + + case FURY_MODE_RAINBOW: + case FURY_MODE_VOLTAGE: + case FURY_MODE_COUNTDOWN: + case FURY_MODE_FLAME: + case FURY_MODE_TWILIGHT: + case FURY_MODE_FURY: + controller->SetRegister(FURY_REG_DELAY, 0); + break; + + case FURY_MODE_RHYTHM: + controller->SetRegister(FURY_REG_DIRECTION, + FURY_DIR_BOTTOM_TO_TOP); + break; + + case FURY_MODE_BREATH: + controller->SetRegister(FURY_REG_DIRECTION, + FURY_DIR_BOTTOM_TO_TOP); + controller->SetRegister(FURY_REG_DELAY, 0); + break; + + case FURY_MODE_DYNAMIC: + controller->SetRegister(FURY_REG_DIRECTION, + FURY_DIR_BOTTOM_TO_TOP); + controller->SetRegister(FURY_REG_DELAY, 0); + break; + + case FURY_MODE_SLITHER: + controller->SetRegister(FURY_REG_DELAY, 12); + controller->SetRegister(FURY_REG_DIRECTION, + FURY_ALT_DIRECTIONS); + break; + + case FURY_MODE_TELEPORT: + controller->SetRegister(FURY_REG_DELAY, 0); + controller->SetRegister(FURY_REG_DIRECTION, + FURY_ALT_DIRECTIONS); + break; + + case FURY_MODE_RAIN: + controller->SetRegister(FURY_REG_DELAY, 0); + controller->SetRegister(FURY_REG_LENGTH, 3); + break; + + case FURY_MODE_FIREWORK: + controller->SetRegister(FURY_REG_DELAY, 0); + controller->SetRegister(FURY_REG_LENGTH, 7); + break; + } + + // Mode-specific parameters that are customizable in Kingston's software + // but which are not yet available in the OpenRGB interface. + // Default values are used here and the parameter ranges are annotated + switch(modes[active_mode].value) + { + case FURY_MODE_RHYTHM: + // between 2 and 5 + controller->SetRegister(FURY_REG_DELAY, 3); + break; + + + case FURY_MODE_SLIDE: + // between 1 and 4 + controller->SetRegister(FURY_REG_DELAY, 3); + // between 1 and 12 + controller->SetRegister(FURY_REG_LENGTH, 4); + break; + + case FURY_MODE_SLITHER: + // between 1 and 32 + controller->SetRegister(FURY_REG_LENGTH, 12); + break; + + case FURY_MODE_TELEPORT: + // between 1 and 12 + controller->SetRegister(FURY_REG_LENGTH, 3); + break; + + case FURY_MODE_WIND: + // between 0 and 32 + controller->SetRegister(FURY_REG_DELAY, 0); + // between 1 and 32 + controller->SetRegister(FURY_REG_LENGTH, 12); + break; + + case FURY_MODE_COMET: + // between 0 and 20 + controller->SetRegister(FURY_REG_DELAY, 0); + // between 1 and 18 + controller->SetRegister(FURY_REG_LENGTH, 7); + break; + + case FURY_MODE_PRISM: + // between 2 and 4 + controller->SetRegister(FURY_REG_DELAY, 2); + break; + + case FURY_MODE_SPECTRUM: + // between 2 and 6 + controller->SetRegister(FURY_REG_DELAY, 4); + break; + } + + switch(modes[active_mode].color_mode) + { + case MODE_COLORS_PER_LED: + controller->SetLEDColors(colors); + break; + + case MODE_COLORS_MODE_SPECIFIC: + switch(modes[active_mode].value) + { + case FURY_MODE_RHYTHM: + case FURY_MODE_SLIDE: + case FURY_MODE_SLITHER: + case FURY_MODE_TELEPORT: + case FURY_MODE_WIND: + case FURY_MODE_VOLTAGE: + case FURY_MODE_COUNTDOWN: + case FURY_MODE_FURY: + { + std::vector mode_colors(modes[active_mode].colors.begin(), + modes[active_mode].colors.end() - 1); + controller->SetModeColors(mode_colors); + // handle background color + RGBColor color = modes[active_mode].colors[mode_colors.size()]; + unsigned char red = RGBGetRValue(color); + unsigned char green = RGBGetGValue(color); + unsigned char blue = RGBGetBValue(color); + + controller->SetRegister(FURY_REG_BG_RED, red); + controller->SetRegister(FURY_REG_BG_GREEN, green); + controller->SetRegister(FURY_REG_BG_BLUE, blue); + break; + } + default: + controller->SetModeColors(modes[active_mode].colors); + break; + } + break; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_UD) + { + if(modes[active_mode].direction == MODE_DIRECTION_UP) + { + controller->SetRegister(FURY_REG_DIRECTION, + FURY_DIR_BOTTOM_TO_TOP); + } + else + { + controller->SetRegister(FURY_REG_DIRECTION, + FURY_DIR_TOP_TO_BOTTOM); + } + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + switch(modes[active_mode].value) + { + case FURY_MODE_DYNAMIC: + controller->SetRegister(FURY_REG_SPEED, 0); + + // time spent holding a color + controller->SetRegister(FURY_REG_DYNAMIC_HOLD_A, + modes[active_mode].speed >> 8); + // set to 1 as long as the time above is nonzero + controller->SetRegister(FURY_REG_DYNAMIC_HOLD_B, 1); + + // time spent fading to next color + controller->SetRegister(FURY_REG_DYNAMIC_FADE_A, + (modes[active_mode].speed * 5) >> 8); + // set to 1 as long as the time above is nonzero + controller->SetRegister(FURY_REG_DYNAMIC_FADE_B, 1); + break; + + case FURY_MODE_BREATH: + controller->SetRegister(FURY_REG_SPEED, 0); + + // These are the speed values used by Kingston's software, + // representing the time spent fading between two brightness levels + controller->SetRegister(FURY_REG_BREATH_MIN_TO_MID, + modes[active_mode].speed * 3); + controller->SetRegister(FURY_REG_BREATH_MID_TO_MAX, + modes[active_mode].speed); + controller->SetRegister(FURY_REG_BREATH_MAX_TO_MID, + modes[active_mode].speed); + controller->SetRegister(FURY_REG_BREATH_MID_TO_MIN, + modes[active_mode].speed * 3); + + // Time spent holding min brightness + controller->SetRegister(FURY_REG_BREATH_MIN_HOLD, 1); + + // Brightness values (relative to overall brightness) + controller->SetRegister(FURY_REG_BREATH_MAX_BRIGHTNESS, 100); + controller->SetRegister(FURY_REG_BREATH_MID_BRIGHTNESS, 64); + controller->SetRegister(FURY_REG_BREATH_MIN_BRIGHTNESS, 0); + // Kingston software uses 1 for min brightness, + // but 0 seems to look better. + + break; + + case FURY_MODE_RAIN: + { + // speed offsets taken from Kingston software + unsigned char offsets[4] = {11, 0, 15, 9}; + std::vector speeds; + for (std::size_t idx = 0; idx < controller->GetSlotCount(); idx++) + { + speeds.push_back(modes[active_mode].speed + offsets[idx % 4]); + } + controller->SetRegister(FURY_REG_SPEED, speeds); + break; + } + + case FURY_MODE_FIREWORK: + { + // speed offsets taken from Kingston software + unsigned char offsets[4] = {15, 0, 19, 4}; + std::vector speeds; + for (std::size_t idx = 0; idx < controller->GetSlotCount(); idx++) + { + speeds.push_back(modes[active_mode].speed + offsets[idx % 4]); + } + controller->SetRegister(FURY_REG_SPEED, speeds); + break; + } + + default: + controller->SetRegister(FURY_REG_SPEED, modes[active_mode].speed); + break; + } + } + + controller->SetRegister(FURY_REG_BRIGHTNESS, + modes[active_mode].brightness); + controller->SetNumSlots(); + controller->SendApply(); +} + +void RGBController_KingstonFuryDRAM::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_KingstonFuryDRAM::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_KingstonFuryDRAM::DeviceUpdateMode() +{ + LOG_DEBUG("[%s] device update mode", + FURY_CONTROLLER_NAME); + // Preamble only necessary when changing modes. + if(GetRealModeValue() != controller->GetMode()) + { + controller->SendPreamble(modes[active_mode].value != FURY_MODE_RAIN && + modes[active_mode].value != FURY_MODE_FIREWORK && + modes[active_mode].value != FURY_MODE_DIRECT); + } + DeviceUpdateLEDs(); +} diff --git a/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.h b/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.h new file mode 100644 index 0000000..fcb74bd --- /dev/null +++ b/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_KingstonFuryDRAM.h | +| | +| Driver for Kingston Fury DDR4/5 RAM modules | +| | +| Geofrey Mon (geofbot) 14 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "KingstonFuryDRAMController.h" + +class RGBController_KingstonFuryDRAM : public RGBController +{ +public: + RGBController_KingstonFuryDRAM(KingstonFuryDRAMController* controller_ptr); + ~RGBController_KingstonFuryDRAM(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + unsigned char GetRealModeValue(); + KingstonFuryDRAMController* controller; +}; diff --git a/Controllers/LEDStripController/LEDStripController.cpp b/Controllers/LEDStripController/LEDStripController.cpp new file mode 100644 index 0000000..ab26030 --- /dev/null +++ b/Controllers/LEDStripController/LEDStripController.cpp @@ -0,0 +1,415 @@ +/*---------------------------------------------------------*\ +| LEDStripController.cpp | +| | +| Driver for serial LED strips | +| | +| Adam Honse (calcprogrammer1@gmail.com) 11 Dec 2016 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "LEDStripController.h" +#include "ResourceManager.h" + +LEDStripController::LEDStripController(std::string dev_name) +{ + name = dev_name; +} + + +LEDStripController::~LEDStripController() +{ +} + +void LEDStripController::Initialize(char* ledstring, led_protocol proto) +{ + LPSTR numleds = NULL; + LPSTR source = NULL; + LPSTR udpport_baud = NULL; + LPSTR next = NULL; + + //Set the protocol + protocol = proto; + + //Assume serial device unless a different protocol is specified + bool serial = TRUE; + + //Default i2c address out of range + i2c_addr = 255; + + source = strtok_s(ledstring, ",", &next); + + //Check if we are setting up a Keyboard Visualizer UDP protocol device + if (strncmp(source, "udp:", 4) == 0) + { + source = source + 4; + serial = FALSE; + } + + //Check for either the UDP port or the serial baud rate + if (strlen(next)) + { + udpport_baud = strtok_s(next, ",", &next); + } + + //Check for the number of LEDs + if (strlen(next)) + { + numleds = strtok_s(next, ",", &next); + } + + if (serial) + { + if (protocol == LED_PROTOCOL_BASIC_I2C) + { + //I2C uses the baud field for address + i2c_addr = atoi(udpport_baud); + InitializeI2C(source); + } + else if (udpport_baud == NULL) + { + //Initialize with default baud rate + InitializeSerial(source, 115200); + } + else + { + //Initialize with custom baud rate + InitializeSerial(source, atoi(udpport_baud)); + } + } + else + { + if (udpport_baud == NULL) + { + //Do something + } + else + { + //Initialize UDP port + InitializeUDP(source, udpport_baud); + } + } + + if (numleds != NULL && strlen(numleds)) + { + num_leds = atoi(numleds); + } +} + +void LEDStripController::InitializeI2C(char* i2cname) +{ + for(unsigned int i2c_idx = 0; i2c_idx < ResourceManager::get()->GetI2CBusses().size(); i2c_idx++) + { + if(ResourceManager::get()->GetI2CBusses()[i2c_idx]->device_name == std::string(i2cname)) + { + if(i2c_addr < 128) + { + i2cport = ResourceManager::get()->GetI2CBusses()[i2c_idx]; + break; + } + } + } + + serialport = NULL; + udpport = NULL; +} + +void LEDStripController::InitializeSerial(char* portname, int baud) +{ + portname = strtok(portname, "\r"); + port_name = portname; + baud_rate = baud; + serialport = new serial_port(port_name.c_str(), baud_rate); + udpport = NULL; + i2cport = NULL; +} + +void LEDStripController::InitializeUDP(char * clientname, char * port) +{ + client_name = clientname; + port_name = port; + + udpport = new net_port(client_name.c_str(), port_name.c_str()); + serialport = NULL; + i2cport = NULL; +} + +char* LEDStripController::GetLEDString() +{ + return(led_string); +} + +std::string LEDStripController::GetLocation() +{ + if(serialport != NULL) + { + return("COM: " + port_name); + } + else if(udpport != NULL) + { + return("UDP: " + client_name + ":" + port_name); + } + else if(i2cport != NULL) + { + return("I2C: " + std::string(i2cport->device_name) + ", Address " + std::to_string(i2c_addr)); + } + else + { + return(""); + } +} + +std::string LEDStripController::GetName() +{ + return(name); +} + +void LEDStripController::SetLEDs(std::vector colors) +{ + switch(protocol) + { + case LED_PROTOCOL_KEYBOARD_VISUALIZER: + SetLEDsKeyboardVisualizer(colors); + break; + + case LED_PROTOCOL_ADALIGHT: + SetLEDsAdalight(colors); + break; + + case LED_PROTOCOL_TPM2: + SetLEDsTPM2(colors); + break; + + case LED_PROTOCOL_BASIC_I2C: + SetLEDsBasicI2C(colors); + break; + } +} + +void LEDStripController::SetLEDsKeyboardVisualizer(std::vector colors) +{ + unsigned char *serial_buf; + + /*-------------------------------------------------------------*\ + | Keyboard Visualizer Arduino Protocol | + | | + | Packet size: Number of data bytes + 3 | + | | + | 0: Packet Start Byte (0xAA) | + | 1-n: Data bytes | + | n+1: Checksum MSB | + | n+2: Checksum LSB | + \*-------------------------------------------------------------*/ + unsigned int payload_size = (unsigned int)(colors.size() * 3); + unsigned int packet_size = payload_size + 3; + + serial_buf = new unsigned char[packet_size]; + + /*-------------------------------------------------------------*\ + | Set up header | + \*-------------------------------------------------------------*/ + serial_buf[0x00] = 0xAA; + + /*-------------------------------------------------------------*\ + | Copy in color data in RGB order | + \*-------------------------------------------------------------*/ + for(unsigned int color_idx = 0; color_idx < colors.size(); color_idx++) + { + unsigned int color_offset = color_idx * 3; + + serial_buf[0x01 + color_offset] = RGBGetRValue(colors[color_idx]); + serial_buf[0x02 + color_offset] = RGBGetGValue(colors[color_idx]); + serial_buf[0x03 + color_offset] = RGBGetBValue(colors[color_idx]); + } + + /*-------------------------------------------------------------*\ + | Calculate the checksum | + \*-------------------------------------------------------------*/ + unsigned short sum = 0; + + for(unsigned int i = 0; i < (payload_size + 1); i++) + { + sum += serial_buf[i]; + } + + /*-------------------------------------------------------------*\ + | Fill in the checksum bytes | + \*-------------------------------------------------------------*/ + serial_buf[(num_leds * 3) + 1] = sum >> 8; + serial_buf[(num_leds * 3) + 2] = sum & 0x00FF; + + /*-------------------------------------------------------------*\ + | Send the packet | + \*-------------------------------------------------------------*/ + if (serialport != NULL) + { + serialport->serial_write((char *)serial_buf, packet_size); + } + else if (udpport != NULL) + { + udpport->udp_write((char *)serial_buf, packet_size); + } + + delete[] serial_buf; +} + +void LEDStripController::SetLEDsAdalight(std::vector colors) +{ + unsigned char *serial_buf; + + /*-------------------------------------------------------------*\ + | Adalight Protocol | + | | + | Packet size: Number of data bytes + 6 | + | | + | 0: 'A' (0x41) | + | 1: 'd' (0x64) | + | 2: 'a' (0x61) | + | 3: LED count MSB | + | 4: LED count LSB | + | 5: Checksum (MSB xor LSB xor 0x55) | + | 6-n: Data Bytes | + \*-------------------------------------------------------------*/ + unsigned int led_count = (unsigned int)colors.size(); + unsigned int payload_size = (led_count * 3); + unsigned int packet_size = payload_size + 6; + + serial_buf = new unsigned char[packet_size]; + + /*-------------------------------------------------------------*\ + | Set up header | + \*-------------------------------------------------------------*/ + serial_buf[0x00] = 0x41; + serial_buf[0x01] = 0x64; + serial_buf[0x02] = 0x61; + serial_buf[0x03] = (led_count >> 8); + serial_buf[0x04] = (led_count & 0xFF); + serial_buf[0x05] = (serial_buf[0x03] ^ serial_buf[0x04] ^ 0x55); + + /*-------------------------------------------------------------*\ + | Copy in color data in RGB order | + \*-------------------------------------------------------------*/ + for(unsigned int color_idx = 0; color_idx < led_count; color_idx++) + { + unsigned int color_offset = color_idx * 3; + + serial_buf[0x06 + color_offset] = RGBGetRValue(colors[color_idx]); + serial_buf[0x07 + color_offset] = RGBGetGValue(colors[color_idx]); + serial_buf[0x08 + color_offset] = RGBGetBValue(colors[color_idx]); + } + + /*-------------------------------------------------------------*\ + | Send the packet | + \*-------------------------------------------------------------*/ + if (serialport != NULL) + { + serialport->serial_write((char *)serial_buf, packet_size); + } + + delete[] serial_buf; +} + +void LEDStripController::SetLEDsTPM2(std::vector colors) +{ + unsigned char *serial_buf; + + /*-------------------------------------------------------------*\ + | TPM2 Protocol | + | | + | Packet size: Number of data bytes + 5 | + | | + | 0: Packet Start Byte (0xC9) | + | 1: Packet Type (0xDA - Data, 0xC0 - Command, 0xAA - Read) | + | 2: Payload Size MSB | + | 3: Payload Size LSB | + | 4-n: Data Bytes | + | n+1: Packet End Byte (0x36) | + \*-------------------------------------------------------------*/ + unsigned int payload_size = (unsigned int)(colors.size() * 3); + unsigned int packet_size = payload_size + 5; + + serial_buf = new unsigned char[packet_size]; + + /*-------------------------------------------------------------*\ + | Set up header and end byte | + \*-------------------------------------------------------------*/ + serial_buf[0x00] = 0xC9; + serial_buf[0x01] = 0xDA; + serial_buf[0x02] = (payload_size >> 8); + serial_buf[0x03] = (payload_size & 0xFF); + serial_buf[packet_size - 1] = 0x36; + + /*-------------------------------------------------------------*\ + | Copy in color data in RGB order | + \*-------------------------------------------------------------*/ + for(unsigned int color_idx = 0; color_idx < colors.size(); color_idx++) + { + unsigned int color_offset = color_idx * 3; + + serial_buf[0x04 + color_offset] = RGBGetRValue(colors[color_idx]); + serial_buf[0x05 + color_offset] = RGBGetGValue(colors[color_idx]); + serial_buf[0x06 + color_offset] = RGBGetBValue(colors[color_idx]); + } + + /*-------------------------------------------------------------*\ + | Send the packet | + \*-------------------------------------------------------------*/ + if (serialport != NULL) + { + serialport->serial_write((char *)serial_buf, packet_size); + } + + delete[] serial_buf; +} + +void LEDStripController::SetLEDsBasicI2C(std::vector colors) +{ + unsigned char serial_buf[30]; + + /*-------------------------------------------------------------*\ + | Basic I2C Protocol | + | | + | Packet size: At most 32 bytes (SMBus block size) | + | | + | Packet is in RGBRGBRGB... format, also provide start index | + \*-------------------------------------------------------------*/ + + unsigned char index = 0; + unsigned char offset = 0; + + for(unsigned int color_idx = 0; color_idx < colors.size(); color_idx++) + { + serial_buf[index + 0] = RGBGetRValue(colors[color_idx]); + serial_buf[index + 1] = RGBGetGValue(colors[color_idx]); + serial_buf[index + 2] = RGBGetBValue(colors[color_idx]); + + index += 3; + + if(index >= 30) + { + if(i2cport != NULL) + { + i2cport->i2c_smbus_write_i2c_block_data(i2c_addr, offset, 30, serial_buf); + offset += 30; + index = 0; + } + } + } + + if(index > 0) + { + if(i2cport != NULL) + { + i2cport->i2c_smbus_write_i2c_block_data(i2c_addr, offset, index, serial_buf); + } + } + + if(i2cport != NULL) + { + i2cport->i2c_smbus_write_byte(i2c_addr, 0xFF); + } +} diff --git a/Controllers/LEDStripController/LEDStripController.h b/Controllers/LEDStripController/LEDStripController.h new file mode 100644 index 0000000..b701e7c --- /dev/null +++ b/Controllers/LEDStripController/LEDStripController.h @@ -0,0 +1,86 @@ +/*---------------------------------------------------------*\ +| LEDStripController.h | +| | +| Driver for serial LED strips | +| | +| Adam Honse (calcprogrammer1@gmail.com) Dec 11 2016 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "i2c_smbus.h" +#include "serial_port.h" +#include "net_port.h" + +#ifndef TRUE +#define TRUE true +#define FALSE false +#endif + +#ifndef WIN32 +#define LPSTR char * +#define strtok_s strtok_r +#endif + +typedef unsigned int led_protocol; + +enum +{ + LED_PROTOCOL_KEYBOARD_VISUALIZER, + LED_PROTOCOL_ADALIGHT, + LED_PROTOCOL_TPM2, + LED_PROTOCOL_BASIC_I2C +}; + +struct LEDStripDevice +{ + std::string name; + std::string port; + unsigned int baud = 0; + unsigned int num_leds = 0; + led_protocol protocol; +}; + +class LEDStripController +{ +public: + LEDStripController(std::string dev_name); + ~LEDStripController(); + + void Initialize(char* ledstring, led_protocol proto); + + void InitializeI2C(char* i2cname); + void InitializeSerial(char* portname, int baud); + void InitializeUDP(char* clientname, char* port); + + char* GetLEDString(); + std::string GetLocation(); + std::string GetName(); + + void SetLEDs(std::vector colors); + + void SetLEDsKeyboardVisualizer(std::vector colors); + void SetLEDsAdalight(std::vector colors); + void SetLEDsTPM2(std::vector colors); + void SetLEDsBasicI2C(std::vector colors); + + int num_leds; + +private: + int baud_rate; + + char led_string[1024]; + std::string port_name; + std::string client_name; + std::string name; + serial_port *serialport; + net_port *udpport; + i2c_smbus_interface *i2cport; + unsigned char i2c_addr; + led_protocol protocol; +}; diff --git a/Controllers/LEDStripController/LEDStripControllerDetect.cpp b/Controllers/LEDStripController/LEDStripControllerDetect.cpp new file mode 100644 index 0000000..8d502d7 --- /dev/null +++ b/Controllers/LEDStripController/LEDStripControllerDetect.cpp @@ -0,0 +1,135 @@ +/*---------------------------------------------------------*\ +| LEDStripControllerDetect.cpp | +| | +| Detector for serial LED strips | +| | +| Adam Honse (calcprogrammer1@gmail.com) 11 Dec 2016 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LEDStripController.h" +#include "RGBController_LEDStrip.h" +#include "SettingsManager.h" +#include "LogManager.h" + +/******************************************************************************************\ +* * +* DetectLEDStripControllers * +* * +* Detect devices supported by the LEDStrip driver * +* * +\******************************************************************************************/ + +void DetectLEDStripControllers() +{ + json ledstrip_settings; + LEDStripDevice dev; + + /*-------------------------------------------------*\ + | Get LED Strip settings from settings manager | + \*-------------------------------------------------*/ + ledstrip_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("LEDStripDevices"); + + /*-------------------------------------------------*\ + | If the LEDStrip settings contains devices, process| + \*-------------------------------------------------*/ + if(ledstrip_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < ledstrip_settings["devices"].size(); device_idx++) + { + if(ledstrip_settings["devices"][device_idx].contains("name")) + { + dev.name = ledstrip_settings["devices"][device_idx]["name"]; + } + else + { + /*-------------------------------------------------*\ + | Default name | + \*-------------------------------------------------*/ + dev.name = "LED Strip"; + } + + if(ledstrip_settings["devices"][device_idx].contains("port")) + { + dev.port = ledstrip_settings["devices"][device_idx]["port"]; + } + + if(ledstrip_settings["devices"][device_idx].contains("baud")) + { + dev.baud = ledstrip_settings["devices"][device_idx]["baud"]; + } + + if(ledstrip_settings["devices"][device_idx].contains("num_leds")) + { + dev.num_leds = ledstrip_settings["devices"][device_idx]["num_leds"]; + } + + if(ledstrip_settings["devices"][device_idx].contains("protocol")) + { + std::string protocol_string = ledstrip_settings["devices"][device_idx]["protocol"]; + + if(protocol_string == "keyboard_visualizer") + { + dev.protocol = LED_PROTOCOL_KEYBOARD_VISUALIZER; + } + else if(protocol_string == "adalight") + { + dev.protocol = LED_PROTOCOL_ADALIGHT; + } + else if(protocol_string == "tpm2") + { + dev.protocol = LED_PROTOCOL_TPM2; + } + else if(protocol_string == "basic_i2c") + { + dev.protocol = LED_PROTOCOL_BASIC_I2C; + } + else + { + LOG_WARNING("[LEDStripController] '%s' is not a valid value for protocol", protocol_string.c_str()); + return; + } + } + else + { + /*-------------------------------------------------*\ + | Default to the Keyboard Visualizer protocol | + \*-------------------------------------------------*/ + dev.protocol = LED_PROTOCOL_KEYBOARD_VISUALIZER; + } + + if(dev.port.empty()) + { + LOG_WARNING("[LEDStripController] port value cannot be left empty."); + return; + } + + if(dev.baud <= 0) + { + LOG_WARNING("[LEDStripController] baud value cannot be left empty."); + return; + } + + if(dev.num_leds <= 0) + { + LOG_WARNING("[LEDStripController] num_leds value cannot be left empty."); + return; + } + + std::string value = dev.port + "," + std::to_string(dev.baud) + "," + std::to_string(dev.num_leds); + + LEDStripController* controller = new LEDStripController(dev.name); + controller->Initialize((char *)value.c_str(), dev.protocol); + + RGBController_LEDStrip* rgb_controller = new RGBController_LEDStrip(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + +} /* DetectLEDStripControllers() */ + +REGISTER_DETECTOR("LED Strip", DetectLEDStripControllers); diff --git a/Controllers/LEDStripController/RGBController_LEDStrip.cpp b/Controllers/LEDStripController/RGBController_LEDStrip.cpp new file mode 100644 index 0000000..5d6c8d9 --- /dev/null +++ b/Controllers/LEDStripController/RGBController_LEDStrip.cpp @@ -0,0 +1,97 @@ +/*---------------------------------------------------------*\ +| RGBController_LEDStrip.cpp | +| | +| RGBController for serial LED strips | +| | +| Adam Honse (calcprogrammer1@gmail.com) 20 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LEDStrip.h" + +/**------------------------------------------------------------------*\ + @name Serial LED Strip + @category LEDStrip + @type Serial + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectLEDStripControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LEDStrip::RGBController_LEDStrip(LEDStripController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + type = DEVICE_TYPE_LEDSTRIP; + description = "Serial LED Strip Device"; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_LEDStrip::~RGBController_LEDStrip() +{ + delete controller; +} + +void RGBController_LEDStrip::SetupZones() +{ + zone led_zone; + led_zone.name = "LED Strip"; + led_zone.type = ZONE_TYPE_LINEAR; + led_zone.leds_min = controller->num_leds; + led_zone.leds_max = controller->num_leds; + led_zone.leds_count = controller->num_leds; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + for(int led_idx = 0; led_idx < controller->num_leds; led_idx++) + { + led new_led; + new_led.name = "LED "; + new_led.name.append(std::to_string(led_idx)); + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_LEDStrip::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LEDStrip::DeviceUpdateLEDs() +{ + controller->SetLEDs(colors); +} + +void RGBController_LEDStrip::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_LEDStrip::UpdateSingleLED(int /*led*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_LEDStrip::DeviceUpdateMode() +{ + +} diff --git a/Controllers/LEDStripController/RGBController_LEDStrip.h b/Controllers/LEDStripController/RGBController_LEDStrip.h new file mode 100644 index 0000000..c5acf18 --- /dev/null +++ b/Controllers/LEDStripController/RGBController_LEDStrip.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_LEDStrip.h | +| | +| RGBController for serial LED strips | +| | +| Adam Honse (calcprogrammer1@gmail.com) 20 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "serial_port.h" +#include "LEDStripController.h" + +class RGBController_LEDStrip : public RGBController +{ +public: + RGBController_LEDStrip(LEDStripController* controller_ptr); + ~RGBController_LEDStrip(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LEDStripController* controller; +}; diff --git a/Controllers/LGMonitorController/LGMonitorController.cpp b/Controllers/LGMonitorController/LGMonitorController.cpp new file mode 100644 index 0000000..cfa072f --- /dev/null +++ b/Controllers/LGMonitorController/LGMonitorController.cpp @@ -0,0 +1,249 @@ +/*---------------------------------------------------------*\ +| LGMonitorController.cpp | +| | +| Driver for LG monitor | +| | +| Morgan Guimard (morg) 11 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LGMonitorController.h" +#include "StringUtils.h" + +LGMonitorController::LGMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +LGMonitorController::~LGMonitorController() +{ + hid_close(dev); +} + +std::string LGMonitorController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LGMonitorController::GetNameString() +{ + return(name); +} + +std::string LGMonitorController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LGMonitorController::SetDirect(const std::vector colors) +{ + /*---------------------------------------------------------*\ + | Make sure the device is set to on | + \*---------------------------------------------------------*/ + if(!on) + { + TurnOn(true); + } + + /*---------------------------------------------------------*\ + | Make sure the direct mode is enabled | + \*---------------------------------------------------------*/ + if(!direct_mode_enabled) + { + EnableDirectMode(); + } + + /*---------------------------------------------------------*\ + | Prepare the colors data | + \*---------------------------------------------------------*/ + uint8_t data[192]; + memset(data, 0x00, 192); + + unsigned int offset = 0; + + data[offset++] = LG_MONITOR_START_CMD_1; + data[offset++] = LG_MONITOR_START_CMD_2; + data[offset++] = LG_MONITOR_DIRECT_CTL; + data[offset++] = 0x02; + data[offset++] = 0x91; + data[offset++] = 0x00; + + for(const RGBColor color: colors) + { + data[offset++] = RGBGetRValue(color); + data[offset++] = RGBGetGValue(color); + data[offset++] = RGBGetBValue(color); + } + + data[offset] = crc(data, 0, offset); + offset++; + + data[offset++] = LG_MONITOR_END_CMD_1; + data[offset] = LG_MONITOR_END_CMD_2; + + /*---------------------------------------------------------*\ + | Send the data (3 packets of 64 bytes) | + \*---------------------------------------------------------*/ + uint8_t buf[LG_MONITOR_PACKET_SIZE]; + memset(buf, 0x00, LG_MONITOR_PACKET_SIZE); + + for(unsigned int i = 0; i < 3; i++) + { + memcpy(&buf[1], &data[64 * i], 64); + hid_write(dev, buf, LG_MONITOR_PACKET_SIZE); + } +} + +void LGMonitorController::SetMode(uint8_t mode_value, uint8_t brightness, const std::vector colors) +{ + switch(mode_value) + { + case LG_MONITOR_OFF_MODE_VALUE: + /*---------------------------------------------------------*\ + | Turn off lighting | + \*---------------------------------------------------------*/ + TurnOn(false); + break; + + case LG_MONITOR_STATIC_SLOT_1_MODE_VALUE: + /*---------------------------------------------------------*\ + | Set slot 1 active | + \*---------------------------------------------------------*/ + EnableMode(LG_MONITOR_STATIC_SLOT_1_MODE_VALUE); + + SetBrightness(brightness); + /*---------------------------------------------------------*\ + | Send color in slot 1 | + \*---------------------------------------------------------*/ + SetSlotColor(LG_MONITOR_STATIC_SLOT_1_MODE_VALUE, colors[0]); + + break; + + case LG_MONITOR_SPECTRUM_CYCLE_MODE_VALUE: + case LG_MONITOR_RAINBOW_MODE_VALUE: + /*---------------------------------------------------------*\ + | Enable given mode | + \*---------------------------------------------------------*/ + EnableMode(mode_value); + SetBrightness(brightness); + break; + + default: + break; + } + + direct_mode_enabled = false; +} + +void LGMonitorController::EnableDirectMode() +{ + EnableMode(LG_MONITOR_DIRECT_MODE_VALUE); + direct_mode_enabled = true; +} + +void LGMonitorController::EnableMode(uint8_t mode_value) +{ + uint8_t buf[LG_MONITOR_PACKET_SIZE]; + memset(buf, 0x00, LG_MONITOR_PACKET_SIZE); + + buf[1] = LG_MONITOR_START_CMD_1; + buf[2] = LG_MONITOR_START_CMD_2; + buf[3] = LG_MONITOR_SET_MODE; + buf[4] = 0x02; + buf[5] = 0x02; + buf[6] = LG_MONITOR_MODE_CTL; + buf[7] = mode_value; + buf[8] = crc(buf, 1, 8); + buf[9] = LG_MONITOR_END_CMD_1; + buf[10] = LG_MONITOR_END_CMD_2; + + hid_write(dev, buf, LG_MONITOR_PACKET_SIZE); +} + +void LGMonitorController::SetBrightness(uint8_t brightness) +{ + uint8_t buf[LG_MONITOR_PACKET_SIZE]; + memset(buf, 0x00, LG_MONITOR_PACKET_SIZE); + + buf[1] = LG_MONITOR_START_CMD_1; + buf[2] = LG_MONITOR_START_CMD_2; + buf[3] = LG_MONITOR_SET_POWER_STATE; + buf[4] = 0x02; + buf[5] = 0x02; + buf[6] = LG_MONITOR_BRIGHTNESS_CTL; + buf[7] = brightness; + buf[8] = crc(buf, 1, 8); + buf[9] = LG_MONITOR_END_CMD_1; + buf[10] = LG_MONITOR_END_CMD_2; + + hid_write(dev, buf, LG_MONITOR_PACKET_SIZE); +} + +void LGMonitorController::TurnOn(bool value) +{ + uint8_t buf[LG_MONITOR_PACKET_SIZE]; + memset(buf, 0x00, LG_MONITOR_PACKET_SIZE); + + buf[1] = LG_MONITOR_START_CMD_1; + buf[2] = LG_MONITOR_START_CMD_2; + buf[3] = LG_MONITOR_SET_POWER_STATE; + buf[4] = 0x02; + buf[5] = 0x02; + buf[6] = value ? LG_MONITOR_POWER_ON : LG_MONITOR_POWER_OFF; + buf[8] = crc(buf, 1, 8); + buf[9] = LG_MONITOR_END_CMD_1; + buf[10] = LG_MONITOR_END_CMD_2; + + hid_write(dev, buf, LG_MONITOR_PACKET_SIZE); + + on = value; +} + +void LGMonitorController::SetSlotColor(uint8_t slot, const RGBColor color) +{ + uint8_t buf[LG_MONITOR_PACKET_SIZE]; + memset(buf, 0x00, LG_MONITOR_PACKET_SIZE); + + buf[1] = LG_MONITOR_START_CMD_1; + buf[2] = LG_MONITOR_START_CMD_2; + buf[3] = LG_MONITOR_SET_COLOR; + + buf[4] = 0x02; + buf[5] = 0x04; + buf[6] = slot; + + buf[7] = RGBGetRValue(color); + buf[8] = RGBGetGValue(color); + buf[9] = RGBGetBValue(color); + + buf[10] = crc(buf, 1, 10); + buf[11] = LG_MONITOR_END_CMD_1; + buf[12] = LG_MONITOR_END_CMD_2; + + hid_write(dev, buf, LG_MONITOR_PACKET_SIZE); +} + +uint8_t LGMonitorController::crc(const uint8_t data[], uint8_t start, uint8_t end) +{ + uint8_t crc = 0; + + for(unsigned int i = start; i < end; i++) + { + crc = crc ^ data[i]; + } + + return crc; +} diff --git a/Controllers/LGMonitorController/LGMonitorController.h b/Controllers/LGMonitorController/LGMonitorController.h new file mode 100644 index 0000000..2fc656b --- /dev/null +++ b/Controllers/LGMonitorController/LGMonitorController.h @@ -0,0 +1,73 @@ +/*---------------------------------------------------------*\ +| LGMonitorController.h | +| | +| Driver for LG monitor | +| | +| Morgan Guimard (morg) 11 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define LG_MONITOR_LEDS 48 +#define LG_MONITOR_PACKET_SIZE 65 +#define LG_MONITOR_READ_CMD 0x52 // R (Read) +#define LG_MONITOR_START_CMD_1 0x53 // S (Send) +#define LG_MONITOR_START_CMD_2 0x43 // C (Command) +#define LG_MONITOR_END_CMD_1 0x45 // E (End) +#define LG_MONITOR_END_CMD_2 0x44 // D (Data) +#define LG_MONITOR_READ_POWER_STATE 0xCE +#define LG_MONITOR_SET_POWER_STATE 0xCF +#define LG_MONITOR_SET_COLOR 0xCD +#define LG_MONITOR_SET_MODE 0xCA +#define LG_MONITOR_DIRECT_CTL 0xC1 +#define LG_MONITOR_POWER_ON 0x01 +#define LG_MONITOR_POWER_OFF 0x02 +#define LG_MONITOR_MODE_CTL 0x03 +#define LG_MONITOR_BRIGHTNESS_CTL 0x01 + +enum +{ + LG_MONITOR_DIRECT_MODE_VALUE = 0x08, + LG_MONITOR_STATIC_SLOT_1_MODE_VALUE = 0x01, + LG_MONITOR_SPECTRUM_CYCLE_MODE_VALUE = 0x05, + LG_MONITOR_RAINBOW_MODE_VALUE = 0x06, + LG_MONITOR_OFF_MODE_VALUE = 0x00 +}; + +class LGMonitorController +{ +public: + LGMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~LGMonitorController(); + + std::string GetDeviceLocation(); + std::string GetFirmwareVersion(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetDirect(const std::vector colors); + void SetMode(uint8_t mode_value, uint8_t brightness, const std::vector colors); + +private: + hid_device* dev; + std::string description; + std::string location; + std::string name; + std::string version; + bool on = false; + bool direct_mode_enabled = false; + + static uint8_t crc(const uint8_t data[], uint8_t start, uint8_t end); + void SetBrightness(uint8_t value); + void TurnOn(bool value); + void EnableDirectMode(); + void EnableMode(uint8_t mode_value); + void SetSlotColor(uint8_t slot, const RGBColor color); +}; diff --git a/Controllers/LGMonitorController/LGMonitorControllerDetect.cpp b/Controllers/LGMonitorController/LGMonitorControllerDetect.cpp new file mode 100644 index 0000000..602e079 --- /dev/null +++ b/Controllers/LGMonitorController/LGMonitorControllerDetect.cpp @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| LGMonitorControllerDetect.cpp | +| | +| Detector for LG monitor | +| | +| Morgan Guimard (morg) 11 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LGMonitorController.h" +#include "RGBController_LGMonitor.h" + +/*---------------------------------------------------------*\ +| vendor ID | +\*---------------------------------------------------------*/ +#define LG_MONITOR_VID 0x043E + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define LG_27GN950_B_PID 0x9A8A +#define LG_38GL950G_PID 0x9A57 + +static void DetectLGMonitorControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LGMonitorController* controller = new LGMonitorController(dev, *info, name); + RGBController_LGMonitor* rgb_controller = new RGBController_LGMonitor(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("LG 27GN950-B Monitor", DetectLGMonitorControllers, LG_MONITOR_VID, LG_27GN950_B_PID, 1, 0xFF01, 0x01); + +// Untested +//REGISTER_HID_DETECTOR("LG 38GL950G Monitor", DetectLGMonitorControllers, LG_MONITOR_VID, LG_38GL950G_PID); diff --git a/Controllers/LGMonitorController/RGBController_LGMonitor.cpp b/Controllers/LGMonitorController/RGBController_LGMonitor.cpp new file mode 100644 index 0000000..a615421 --- /dev/null +++ b/Controllers/LGMonitorController/RGBController_LGMonitor.cpp @@ -0,0 +1,163 @@ +/*---------------------------------------------------------*\ +| RGBController_LGMonitor.cpp | +| | +| RGBController for LG monitor | +| | +| Morgan Guimard (morg) 11 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_LGMonitor.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name LGMonitor + @category Accessory + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLGMonitorControllers + @comment +\*-------------------------------------------------------------------*/ +RGBController_LGMonitor::RGBController_LGMonitor(LGMonitorController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "LG"; + type = DEVICE_TYPE_MONITOR; + description = "LG Monitor"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LG_MONITOR_DIRECT_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LG_MONITOR_STATIC_SLOT_1_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness_min = 1; + Static.brightness_max = 12; + Static.brightness = 12; + modes.push_back(Static); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = LG_MONITOR_SPECTRUM_CYCLE_MODE_VALUE; + SpectrumCycle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = 1; + SpectrumCycle.brightness_max = 12; + SpectrumCycle.brightness = 12; + modes.push_back(SpectrumCycle); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = LG_MONITOR_RAINBOW_MODE_VALUE; + RainbowWave.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.brightness_min = 1; + RainbowWave.brightness_max = 12; + RainbowWave.brightness = 12; + modes.push_back(RainbowWave); + + mode Off; + Off.name = "Off"; + Off.value = LG_MONITOR_OFF_MODE_VALUE; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_LGMonitor::KeepaliveThread, this); +} + +RGBController_LGMonitor::~RGBController_LGMonitor() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_LGMonitor::SetupZones() +{ + zone new_zone; + + new_zone.name = "Screen"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 48; + new_zone.leds_max = 48; + new_zone.leds_count = 48; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + for(unsigned int i = 0 ; i < 48; i ++) + { + led new_led; + new_led.name = "LED " + std::to_string(i + 1); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_LGMonitor::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LGMonitor::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + controller->SetDirect(colors); +} + +void RGBController_LGMonitor::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LGMonitor::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LGMonitor::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].colors); +} + +void RGBController_LGMonitor::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((modes[active_mode].value == LG_MONITOR_DIRECT_MODE_VALUE) && (std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(500)) + { + UpdateLEDs(); + } + + std::this_thread::sleep_for(15ms); + } +} diff --git a/Controllers/LGMonitorController/RGBController_LGMonitor.h b/Controllers/LGMonitorController/RGBController_LGMonitor.h new file mode 100644 index 0000000..1eb80c6 --- /dev/null +++ b/Controllers/LGMonitorController/RGBController_LGMonitor.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_LGMonitor.h | +| | +| RGBController for LG monitor | +| | +| Morgan Guimard (morg) 11 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "LGMonitorController.h" + +class RGBController_LGMonitor : public RGBController +{ +public: + RGBController_LGMonitor(LGMonitorController* controller_ptr); + ~RGBController_LGMonitor(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + +private: + LGMonitorController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; + + void KeepaliveThread(); +}; diff --git a/Controllers/LIFXController/LIFXController.cpp b/Controllers/LIFXController/LIFXController.cpp new file mode 100644 index 0000000..a2e3a9a --- /dev/null +++ b/Controllers/LIFXController/LIFXController.cpp @@ -0,0 +1,480 @@ +/*---------------------------------------------------------*\ +| LIFXController.cpp | +| | +| Driver for LIFX | +| | +| Adam Honse (calcprogrammer1@gmail.com) 05 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LIFXController.h" +#include +#include "hsv.h" + +using json = nlohmann::json; +using namespace std::chrono_literals; + +LIFXController::LIFXController(std::string ip, std::string name, bool multizone, bool extended_multizone) +{ + this->name = name; + zone_count = 1; + this->multizone = multizone; + this->extended_multizone = extended_multizone; + + /*-----------------------------------------------------------------*\ + | Fill in location string with device's IP address | + \*-----------------------------------------------------------------*/ + location = "IP: " + ip; + + /*-----------------------------------------------------------------*\ + | Open a UDP client sending to the device's IP, port 56700 | + \*-----------------------------------------------------------------*/ + port.udp_client(ip.c_str(), LIFX_UDP_PORT); +} + +LIFXController::~LIFXController() +{ + +} + +std::string LIFXController::GetLocation() +{ + return(location); +} + +std::string LIFXController::GetName() +{ + return(name); +} + +std::string LIFXController::GetVersion() +{ + return(module_name + " " + firmware_version); +} + +std::string LIFXController::GetManufacturer() +{ + return(LIFX_MANUFACTURER); +} + +std::string LIFXController::GetUniqueID() +{ + return(module_mac); +} + +unsigned int LIFXController::GetZoneCount() +{ + return(zone_count); +} + +void LIFXController::SetColors(std::vector colors) +{ + /*-------------------------*\ + | Non-multizone lifx device | + \*-------------------------*/ + if(!multizone) + { + SetColor(colors[0]); + + return; + } + + /*-------------------------------------------*\ + | Multizone lifx device with extended support | + \*-------------------------------------------*/ + if(extended_multizone) + { + SetZoneColors(colors); + + return; + } + + /*----------------------------------------------*\ + | Multizone lifx device without extended support | + \*----------------------------------------------*/ + for(size_t i = 0; i < zone_count; i++) + { + /*-----------------------------------------------------------------*\ + | Utilize caching to avoid setting all zones when 1 zone is changed | + \*-----------------------------------------------------------------*/ + if(cached_colors[i] == colors[i]) + { + continue; + } + + SetZoneColor(colors[i], (unsigned int)i); + cached_colors[i] = colors[i]; + } +} + +void LIFXController::FetchZoneCount() +{ + if(!multizone) + { + return; + } + + /*---------------------------*\ + | Send get color zones packet | + \*---------------------------*/ + data_buf_size = LIFX_PACKET_HEADER_LENGTH + LIFX_GET_COLOR_ZONES_PACKET_LENGTH; + data = new unsigned char[data_buf_size]; + memset(data, 0, data_buf_size); + + HeaderPacketSetDefaults(LIFX_PACKET_TYPE_GET_COLOR_ZONES); + + GetColorZonesPacketSetStartIndex(0); + GetColorZonesPacketSetEndIndex(0); + + port.udp_write((char*)data, (int)data_buf_size); + delete[] data; + + /*----------------------------*\ + | Listen for state zone packet | + \*----------------------------*/ + data_buf_size = LIFX_PACKET_HEADER_LENGTH + LIFX_STATE_ZONE_PACKET_LENGTH; + data = new unsigned char[data_buf_size]; + memset(data, 0, data_buf_size); + + port.set_receive_timeout(5, 0); + port.udp_listen((char*)data, (int)data_buf_size); + + /*-----------------*\ + | Validate response | + \*-----------------*/ + if(HeaderPacketGetSize() != data_buf_size || HeaderPacketGetProtocol() != LIFX_PROTOCOL || HeaderPacketGetPacketType() != LIFX_PACKET_TYPE_STATE_ZONE) + { + return; + } + + zone_count = StateZonePacketGetZonesCount(); + delete[] data; +} + +void LIFXController::SetColor(RGBColor color) +{ + /*---------------------*\ + | Send set color packet | + \*---------------------*/ + data_buf_size = LIFX_PACKET_HEADER_LENGTH + LIFX_SET_COLOR_PACKET_LENGTH; + data = new unsigned char[data_buf_size]; + memset(data, 0, data_buf_size); + + HeaderPacketSetDefaults(LIFX_PACKET_TYPE_SET_COLOR); + + hsbk_t hsbk; + RGBColorToHSBK(color, &hsbk); + + SetColorPacketSetHSBK(&hsbk); + SetColorPacketSetDuration(0); + + port.udp_write((char*)data, (int)data_buf_size); + delete[] data; +} + +void LIFXController::SetZoneColor(RGBColor color, unsigned int zone) +{ + /*---------------------------*\ + | Send set color zones packet | + \*---------------------------*/ + data_buf_size = LIFX_PACKET_HEADER_LENGTH + LIFX_SET_COLOR_ZONES_PACKET_LENGTH; + data = new unsigned char[data_buf_size]; + memset(data, 0, data_buf_size); + + HeaderPacketSetDefaults(LIFX_PACKET_TYPE_SET_COLOR_ZONES); + + SetColorZonesPacketSetStartIndex(zone); + SetColorZonesPacketSetEndIndex(zone); + + hsbk_t hsbk; + RGBColorToHSBK(color, &hsbk); + + SetColorZonesPacketSetHSBK(&hsbk); + SetColorZonesPacketSetDuration(0); + SetColorZonesPacketSetApply(LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY); + + port.udp_write((char*)data, (int)data_buf_size); + delete[] data; +} + +void LIFXController::SetZoneColors(std::vector colors) +{ + /*------------------------------------*\ + | Send set extended color zones packet | + \*------------------------------------*/ + data_buf_size = LIFX_PACKET_HEADER_LENGTH + LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_LENGTH; + data = new unsigned char[data_buf_size]; + memset(data, 0, data_buf_size); + + HeaderPacketSetDefaults(LIFX_PACKET_TYPE_SET_EXTENDED_COLOR_ZONES); + + SetExtendedColorZonesPacketSetDuration(0); + SetExtendedColorZonesPacketSetApply(LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY); + SetExtendedColorZonesPacketSetZoneIndex(0); + SetExtendedColorZonesPacketSetColors(colors); + + port.udp_write((char*)data, (int)data_buf_size); + delete[] data; +} + +void LIFXController::RGBColorToHSBK(RGBColor color, hsbk_t* hsbk) +{ + hsv_t hsv; + rgb2hsv(color, &hsv); + + hsbk->hue = hsv.hue * (USHRT_MAX/360); + hsbk->saturation = hsv.saturation * (USHRT_MAX/256); + hsbk->brightness = hsv.value * (USHRT_MAX/256); + hsbk->kelvin = DEFAULT_KELVIN; +} + +/*----------------------------*\ +| Header packet helper methods | +\*----------------------------*/ +void LIFXController::HeaderPacketSetDefaults(unsigned short packet_type) +{ + /*-----*\ + | Frame | + \*-----*/ + HeaderPacketSetSize((unsigned short)data_buf_size); + HeaderPacketSetProtocol(); + HeaderPacketSetAddressable(true); + HeaderPacketSetTagged(false); + HeaderPacketSetOrigin(0); + HeaderPacketSetSource(2); + + /*-------------*\ + | Frame address | + \*-------------*/ + unsigned char target[TARGET_LENGTH] = {0}; + HeaderPacketSetTarget(target); + HeaderPacketSetResponseRequired(false); + HeaderPacketSetAcknowledgeRequired(false); + HeaderPacketSetSequence(++sequence); + + /*---------------*\ + | Protocol header | + \*---------------*/ + HeaderPacketSetPacketType(packet_type); +} + +unsigned short LIFXController::HeaderPacketGetSize() +{ + return data[LIFX_HEADER_PACKET_OFFSET_SIZE]; +} + +void LIFXController::HeaderPacketSetSize(unsigned short size) +{ + memcpy(&data[LIFX_HEADER_PACKET_OFFSET_SIZE], &size, sizeof(unsigned short)); +} + +unsigned short LIFXController::HeaderPacketGetProtocol() +{ + unsigned short protocol; + memcpy(&protocol, &data[LIFX_HEADER_PACKET_OFFSET_PROTOCOL], sizeof(unsigned short)); + return protocol & 0x0FFF; +} + +void LIFXController::HeaderPacketSetProtocol(unsigned short protocol) +{ + data[LIFX_HEADER_PACKET_OFFSET_PROTOCOL] = protocol & 0xFF; + unsigned char current = data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN]; + data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] = (current & 0xF0) | ((protocol >> 8) & 0x0F); +} + +void LIFXController::HeaderPacketSetAddressable(bool addressable) +{ + if(addressable) + { + data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] |= 0x10; + } + else + { + data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] &= ~0x10; + } +} + +void LIFXController::HeaderPacketSetTagged(bool tagged) +{ + if(tagged) + { + data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] |= 0x20; + } + else + { + data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] &= ~0x20; + } +} + +void LIFXController::HeaderPacketSetOrigin(unsigned char origin) +{ + data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] = + (data[LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN] & 0xFC) | (origin & 0x03); +} + +void LIFXController::HeaderPacketSetSource(unsigned int source) +{ + memcpy(&data[LIFX_HEADER_PACKET_OFFSET_SOURCE], &source, sizeof(unsigned int)); +} + +void LIFXController::HeaderPacketSetTarget(unsigned char* target) +{ + memcpy(&data[LIFX_HEADER_PACKET_OFFSET_TARGET], target, TARGET_LENGTH); +} + +void LIFXController::HeaderPacketSetResponseRequired(bool response_required) +{ + if(response_required) + { + data[LIFX_HEADER_PACKET_OFFSET_RESPONSE_REQUIRED_ACKNOWLEDGE_REQUIRED] |= 0x01; + } + else + { + data[LIFX_HEADER_PACKET_OFFSET_RESPONSE_REQUIRED_ACKNOWLEDGE_REQUIRED] &= ~0x01; + } +} + +void LIFXController::HeaderPacketSetAcknowledgeRequired(bool acknowledge_required) +{ + if(acknowledge_required) + { + data[LIFX_HEADER_PACKET_OFFSET_RESPONSE_REQUIRED_ACKNOWLEDGE_REQUIRED] |= 0x02; + } + else + { + data[LIFX_HEADER_PACKET_OFFSET_RESPONSE_REQUIRED_ACKNOWLEDGE_REQUIRED] &= ~0x02; + } +} + +void LIFXController::HeaderPacketSetSequence(unsigned char sequence) +{ + data[LIFX_HEADER_PACKET_OFFSET_SEQUENCE] = sequence; +} + +unsigned short LIFXController::HeaderPacketGetPacketType() +{ + unsigned short packet_type_value; + memcpy(&packet_type_value, &data[LIFX_HEADER_PACKET_OFFSET_PACKET_TYPE], sizeof(unsigned short)); + + return packet_type_value; +} + +void LIFXController::HeaderPacketSetPacketType(unsigned short packet_type) +{ + memcpy(&data[LIFX_HEADER_PACKET_OFFSET_PACKET_TYPE], &packet_type, sizeof(unsigned short)); +} + +/*-------------------------------*\ +| Set color packet helper methods | +\*-------------------------------*/ +void LIFXController::SetColorPacketSetHSBK(hsbk_t* hsbk) +{ + memcpy(&data[LIFX_SET_COLOR_PACKET_OFFSET_HUE], &hsbk->hue, sizeof(unsigned short)); + memcpy(&data[LIFX_SET_COLOR_PACKET_OFFSET_SATURATION], &hsbk->saturation, sizeof(unsigned short)); + memcpy(&data[LIFX_SET_COLOR_PACKET_OFFSET_BRIGHTNESS], &hsbk->brightness, sizeof(unsigned short)); + memcpy(&data[LIFX_SET_COLOR_PACKET_OFFSET_KELVIN], &hsbk->kelvin, sizeof(unsigned short)); +} + +void LIFXController::SetColorPacketSetDuration(unsigned int duration) +{ + memcpy(&data[LIFX_SET_COLOR_PACKET_OFFSET_DURATION], &duration, sizeof(unsigned int)); +} + +/*-------------------------------------*\ +| Set color zones packet helper methods | +\*-------------------------------------*/ +void LIFXController::SetColorZonesPacketSetStartIndex(unsigned char start_index) +{ + memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_START_INDEX], &start_index, sizeof(unsigned char)); +} + +void LIFXController::SetColorZonesPacketSetEndIndex(unsigned char end_index) +{ + memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_END_INDEX], &end_index, sizeof(unsigned char)); +} + +void LIFXController::SetColorZonesPacketSetHSBK(hsbk_t* hsbk) +{ + memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_HUE], &hsbk->hue, sizeof(unsigned short)); + memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_SATURATION], &hsbk->saturation, sizeof(unsigned short)); + memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_BRIGHTNESS], &hsbk->brightness, sizeof(unsigned short)); + memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_KELVIN], &hsbk->kelvin, sizeof(unsigned short)); +} + +void LIFXController::SetColorZonesPacketSetDuration(unsigned int duration) +{ + memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_DURATION], &duration, sizeof(unsigned int)); +} + +void LIFXController::SetColorZonesPacketSetApply(unsigned char apply) +{ + memcpy(&data[LIFX_SET_COLOR_ZONES_PACKET_OFFSET_APPLY], &apply, sizeof(unsigned char)); +} + +/*-------------------------------------*\ +| Get color zones packet helper methods | +\*-------------------------------------*/ +void LIFXController::GetColorZonesPacketSetStartIndex(unsigned char start_index) +{ + memcpy(&data[LIFX_GET_COLOR_ZONES_PACKET_OFFSET_START_INDEX], &start_index, sizeof(unsigned char)); +} + +void LIFXController::GetColorZonesPacketSetEndIndex(unsigned char end_index) +{ + memcpy(&data[LIFX_GET_COLOR_ZONES_PACKET_OFFSET_END_INDEX], &end_index, sizeof(unsigned char)); +} + +/*--------------------------------*\ +| State zone packet helper methods | +\*--------------------------------*/ +unsigned char LIFXController::StateZonePacketGetZonesCount() +{ + unsigned char zones_count; + memcpy(&zones_count, &data[LIFX_STATE_ZONE_PACKET_OFFSET_ZONES_COUNT], sizeof(unsigned char)); + + return zones_count; +} + +/*----------------------------------------------*\ +| Set extended color zones packet helper methods | +\*----------------------------------------------*/ +void LIFXController::SetExtendedColorZonesPacketSetDuration(unsigned int duration) +{ + memcpy(&data[LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_DURATION], &duration, sizeof(unsigned int)); +} + +void LIFXController::SetExtendedColorZonesPacketSetApply(unsigned char apply) +{ + memcpy(&data[LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_APPLY], &apply, sizeof(unsigned char)); +} + +void LIFXController::SetExtendedColorZonesPacketSetZoneIndex(unsigned short zone_index) +{ + memcpy(&data[LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_ZONE_INDEX], &zone_index, sizeof(unsigned short)); +} + +void LIFXController::SetExtendedColorZonesPacketSetColors(std::vector colors) +{ + unsigned char colors_count = (unsigned char)colors.size(); + memcpy(&data[LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_COLORS_COUNT], &colors_count, sizeof(unsigned char)); + + for(size_t i = 0; i < colors.size(); i++) + { + hsbk_t hsbk; + RGBColorToHSBK(colors[i], &hsbk); + + size_t current_color_offset = LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_COLORS + (i * HSBK_LENGTH); + + size_t hue_offset = current_color_offset; + size_t saturation_offset = hue_offset + sizeof(unsigned short); + size_t brightness_offset = saturation_offset + sizeof(unsigned short); + size_t kelvin_offset = brightness_offset + sizeof(unsigned short); + + memcpy(&data[hue_offset], &hsbk.hue, sizeof(unsigned short)); + memcpy(&data[saturation_offset], &hsbk.saturation, sizeof(unsigned short)); + memcpy(&data[brightness_offset], &hsbk.brightness, sizeof(unsigned short)); + memcpy(&data[kelvin_offset], &hsbk.kelvin, sizeof(unsigned short)); + } +} diff --git a/Controllers/LIFXController/LIFXController.h b/Controllers/LIFXController/LIFXController.h new file mode 100644 index 0000000..dba67fd --- /dev/null +++ b/Controllers/LIFXController/LIFXController.h @@ -0,0 +1,240 @@ +/*---------------------------------------------------------*\ +| LIFXController.h | +| | +| Driver for LIFX | +| | +| Adam Honse (calcprogrammer1@gmail.com) 05 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "net_port.h" + +#define LIFX_MANUFACTURER "LIFX" +#define LIFX_UDP_PORT "56700" +#define LIFX_PROTOCOL 1024 +#define TARGET_LENGTH 8 +#define DEFAULT_KELVIN 3500 +#define HSBK_LENGTH 8 + +/*---------------------*\ +| Packet size constants | +\*---------------------*/ +#define LIFX_PACKET_HEADER_LENGTH 36 +#define LIFX_SET_COLOR_PACKET_LENGTH 13 +#define LIFX_SET_COLOR_ZONES_PACKET_LENGTH 15 +#define LIFX_GET_COLOR_ZONES_PACKET_LENGTH 2 +#define LIFX_STATE_ZONE_PACKET_LENGTH 10 +#define LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_LENGTH 664 + +/*---------------------------------------------------------------------------*\ +| https://lan.developer.lifx.com/docs/field-types#multizoneapplicationrequest | +\*---------------------------------------------------------------------------*/ +enum +{ + LIFX_MULTIZONE_APPLICATION_REQUEST_NO_APPLY = 0, + LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY = 1, + LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY_ONLY = 2 +}; + + +/*----------------------------------------------------------------*\ +| https://lan.developer.lifx.com/docs/representing-color-with-hsbk | +\*----------------------------------------------------------------*/ +typedef struct +{ + unsigned short hue; /* 0-360 value normalized to 0-65535 */ + unsigned short saturation; /* 0-1 value normalized to 0-65535 */ + unsigned short brightness; /* 0-1 value normalized to 0-65535 */ + unsigned short kelvin; /* 0-65535 value */ + /* Note: Devices may only support a subset of the full range. */ +} hsbk_t; + +/*-----------------*\ +| LIFX packet types | +\*-----------------*/ +enum +{ + LIFX_PACKET_TYPE_SET_COLOR = 102, + LIFX_PACKET_TYPE_SET_COLOR_ZONES = 501, + LIFX_PACKET_TYPE_GET_COLOR_ZONES = 502, + LIFX_PACKET_TYPE_STATE_ZONE = 503, + LIFX_PACKET_TYPE_SET_EXTENDED_COLOR_ZONES = 510 +}; + +/*-----------------------------------------------------*\ +| LIFX header packet offsets | +| https://lan.developer.lifx.com/docs/encoding-a-packet | +\*-----------------------------------------------------*/ +enum +{ + LIFX_HEADER_PACKET_OFFSET_SIZE = 0, /* 2 bytes, size of the entire message in bytes */ + LIFX_HEADER_PACKET_OFFSET_PROTOCOL = 2, /* Protocol number, must be 1024 */ + LIFX_HEADER_PACKET_OFFSET_ADDRESSABLE_TAGGED_ORIGIN = 3, /* Bits 0-3 are part of Protocol */ + /* Bit 4, addressable flag */ + /* Bit 5, tagged flag */ + /* Bit 6/7, origin value */ + LIFX_HEADER_PACKET_OFFSET_SOURCE = 4, /* Source identifier, unique value set by client */ + LIFX_HEADER_PACKET_OFFSET_TARGET = 8, /* 6 byte device address (MAC) or zero */ + /* Last two bytes should be 0 */ + LIFX_HEADER_PACKET_OFFSET_RESPONSE_REQUIRED_ACKNOWLEDGE_REQUIRED = 22, /* Bit 0, res_required flag */ + /* Bit 1, ack_required flag */ + LIFX_HEADER_PACKET_OFFSET_SEQUENCE = 23, /* Wrap around message sequence number */ + LIFX_HEADER_PACKET_OFFSET_PACKET_TYPE = 32 /* Message type determines the payload used */ +}; + +/*---------------------------------------------------------------------------*\ +| LIFX set color packet offsets | +| https://lan.developer.lifx.com/docs/changing-a-device#setcolor---packet-102 | +\*---------------------------------------------------------------------------*/ +enum +{ + /* 1 byte, reserved */ + LIFX_SET_COLOR_PACKET_OFFSET_HUE = LIFX_PACKET_HEADER_LENGTH + 1, /* 2 bytes, hue as a 0-65535 value */ + LIFX_SET_COLOR_PACKET_OFFSET_SATURATION = LIFX_PACKET_HEADER_LENGTH + 3, /* 2 bytes, saturation as a 0-65535 value */ + LIFX_SET_COLOR_PACKET_OFFSET_BRIGHTNESS = LIFX_PACKET_HEADER_LENGTH + 5, /* 2 bytes, brightness as a 0-65535 value */ + LIFX_SET_COLOR_PACKET_OFFSET_KELVIN = LIFX_PACKET_HEADER_LENGTH + 7, /* 2 bytes, kelvin as a 0-65535 value. */ + /* Note: The actual max for this is device specific */ + LIFX_SET_COLOR_PACKET_OFFSET_DURATION = LIFX_PACKET_HEADER_LENGTH + 9, /* 4 bytes, transition time in ms */ +}; + +/*--------------------------------------------------------------------------------*\ +| LIFX set color zones packet offsets | +| https://lan.developer.lifx.com/docs/changing-a-device#setcolorzones---packet-501 | +\*--------------------------------------------------------------------------------*/ +enum +{ + LIFX_SET_COLOR_ZONES_PACKET_OFFSET_START_INDEX = LIFX_PACKET_HEADER_LENGTH + 0, /* 1 byte, the first zone in the segment we are changing */ + LIFX_SET_COLOR_ZONES_PACKET_OFFSET_END_INDEX = LIFX_PACKET_HEADER_LENGTH + 1, /* 1 byte, the last zone in the segment we are changing */ + LIFX_SET_COLOR_ZONES_PACKET_OFFSET_HUE = LIFX_PACKET_HEADER_LENGTH + 2, /* 2 bytes, hue as a 0-65535 value */ + LIFX_SET_COLOR_ZONES_PACKET_OFFSET_SATURATION = LIFX_PACKET_HEADER_LENGTH + 4, /* 2 bytes, saturation as a 0-65535 value */ + LIFX_SET_COLOR_ZONES_PACKET_OFFSET_BRIGHTNESS = LIFX_PACKET_HEADER_LENGTH + 6, /* 2 bytes, brightness as a 0-65535 value */ + LIFX_SET_COLOR_ZONES_PACKET_OFFSET_KELVIN = LIFX_PACKET_HEADER_LENGTH + 8, /* 2 bytes, kelvin as a 0-65535 value. */ + /* Note: The actual max for this is device specific */ + LIFX_SET_COLOR_ZONES_PACKET_OFFSET_DURATION = LIFX_PACKET_HEADER_LENGTH + 10, /* 4 bytes, transition time in ms */ + LIFX_SET_COLOR_ZONES_PACKET_OFFSET_APPLY = LIFX_PACKET_HEADER_LENGTH + 14 /* 1 byte, multizone application request */ +}; + +/*-------------------------------------------------------------------------------------------*\ +| LIFX get color zones packet offsets | +| https://lan.developer.lifx.com/docs/querying-the-device-for-data#getcolorzones---packet-502 | +\*-------------------------------------------------------------------------------------------*/ +enum +{ + LIFX_GET_COLOR_ZONES_PACKET_OFFSET_START_INDEX = LIFX_PACKET_HEADER_LENGTH + 0, /* 1 byte, The first zone you want to get information from */ + LIFX_GET_COLOR_ZONES_PACKET_OFFSET_END_INDEX = LIFX_PACKET_HEADER_LENGTH + 1, /* 1 byte, The second zone you want to get information from */ +}; + +/*-------------------------------------------------------------------------------*\ +| LIFX state zone packet offsets | +| https://lan.developer.lifx.com/docs/information-messages#statezone---packet-503 | +\*-------------------------------------------------------------------------------*/ +enum +{ + LIFX_STATE_ZONE_PACKET_OFFSET_ZONES_COUNT = LIFX_PACKET_HEADER_LENGTH + 0, /* 1 byte, the total number of zones on the strip. */ + LIFX_STATE_ZONE_PACKET_OFFSET_ZONE_INDEX = LIFX_PACKET_HEADER_LENGTH + 1, /* 1 byte, the zone this packet refers to. */ + LIFX_STATE_ZONE_PACKET_OFFSET_HUE = LIFX_PACKET_HEADER_LENGTH + 2, /* 2 bytes, hue as a 0-65535 value */ + LIFX_STATE_ZONE_PACKET_OFFSET_SATURATION = LIFX_PACKET_HEADER_LENGTH + 4, /* 2 bytes, saturation as a 0-65535 value */ + LIFX_STATE_ZONE_PACKET_OFFSET_BRIGHTNESS = LIFX_PACKET_HEADER_LENGTH + 6, /* 2 bytes, brightness as a 0-65535 value */ + LIFX_STATE_ZONE_PACKET_OFFSET_KELVIN = LIFX_PACKET_HEADER_LENGTH + 8, /* 2 bytes, kelvin as a 0-65535 value. */ + /* Note: The actual max for this is device specific */ +}; + +/*----------------------------------------------------------------------------------------*\ +| LIFX set extended color zones packet offsets | +| https://lan.developer.lifx.com/docs/changing-a-device#setextendedcolorzones---packet-510 | +\*----------------------------------------------------------------------------------------*/ +enum +{ + LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_DURATION = LIFX_PACKET_HEADER_LENGTH + 0, /* 4 bytes, transition time in ms */ + LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_APPLY = LIFX_PACKET_HEADER_LENGTH + 4, /* 1 byte, multizone application request */ + LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_ZONE_INDEX = LIFX_PACKET_HEADER_LENGTH + 5, /* 2 bytes, The first zone to apply colors from. */ + /* If the light has more than 82 zones, then */ + /* send multiple messages with different indices */ + /* to update the whole device. */ + LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_COLORS_COUNT = LIFX_PACKET_HEADER_LENGTH + 7, /* 1 byte, The number of colors in the colors field */ + LIFX_SET_EXTENDED_COLOR_ZONES_PACKET_OFFSET_COLORS = LIFX_PACKET_HEADER_LENGTH + 8, /* 656 bytes (82 * 4 * 2), 82 HSBK values to change */ + /* the device with */ +}; + +class LIFXController +{ +public: + LIFXController(std::string ip, std::string name, bool multizone, bool extended_multizone); + ~LIFXController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetVersion(); + std::string GetManufacturer(); + std::string GetUniqueID(); + unsigned int GetZoneCount(); + + void FetchZoneCount(); + void SetColors(std::vector colors); + +private: + RGBColor cached_colors[UCHAR_MAX]; + unsigned int zone_count; + size_t data_buf_size; + unsigned char* data; + unsigned char sequence; + std::string name; + std::string firmware_version; + std::string module_name; + std::string module_mac; + std::string location; + net_port port; + bool multizone; + bool extended_multizone; + + void SetColor(RGBColor color); + void SetZoneColor(RGBColor color, unsigned int zone); + void SetZoneColors(std::vector colors); + void RGBColorToHSBK(RGBColor color, hsbk_t* hsbk); + + /*---------------------*\ + | Packet helper methods | + \*---------------------*/ + void HeaderPacketSetDefaults(unsigned short packet_type); + unsigned short HeaderPacketGetSize(); + void HeaderPacketSetSize(unsigned short size); + unsigned short HeaderPacketGetProtocol(); + void HeaderPacketSetProtocol(unsigned short protocol=LIFX_PROTOCOL); + void HeaderPacketSetAddressable(bool addressable=true); + void HeaderPacketSetTagged(bool tagged=false); + void HeaderPacketSetOrigin(unsigned char origin=0); + void HeaderPacketSetSource(unsigned int source=2); + void HeaderPacketSetTarget(unsigned char* target); + void HeaderPacketSetResponseRequired(bool response_required=false); + void HeaderPacketSetAcknowledgeRequired(bool acknowledge_required=false); + void HeaderPacketSetSequence(unsigned char sequence); + unsigned short HeaderPacketGetPacketType(); + void HeaderPacketSetPacketType(unsigned short packet_type); + + void SetColorPacketSetDuration(unsigned int duration=0); + void SetColorPacketSetHSBK(hsbk_t* hsbk); + + void SetColorZonesPacketSetStartIndex(unsigned char start_index); + void SetColorZonesPacketSetEndIndex(unsigned char end_index); + void SetColorZonesPacketSetHSBK(hsbk_t* hsbk); + void SetColorZonesPacketSetDuration(unsigned int duration=0); + void SetColorZonesPacketSetApply(unsigned char apply=LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY); + + void GetColorZonesPacketSetStartIndex(unsigned char start_index=0); + void GetColorZonesPacketSetEndIndex(unsigned char end_index=0); + + unsigned char StateZonePacketGetZonesCount(); + + void SetExtendedColorZonesPacketSetDuration(unsigned int duration=0); + void SetExtendedColorZonesPacketSetApply(unsigned char apply=LIFX_MULTIZONE_APPLICATION_REQUEST_APPLY); + void SetExtendedColorZonesPacketSetZoneIndex(unsigned short zone_index=0); + void SetExtendedColorZonesPacketSetColors(std::vector colors); +}; diff --git a/Controllers/LIFXController/LIFXControllerDetect.cpp b/Controllers/LIFXController/LIFXControllerDetect.cpp new file mode 100644 index 0000000..e2cef44 --- /dev/null +++ b/Controllers/LIFXController/LIFXControllerDetect.cpp @@ -0,0 +1,60 @@ +/*---------------------------------------------------------*\ +| LIFXControllerDetect.cpp | +| | +| Detector for LIFX | +| | +| Adam Honse (calcprogrammer1@gmail.com) 05 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LIFXController.h" +#include "RGBController_LIFX.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectLIFXControllers * +* * +* Detect LIFX devices * +* * +\******************************************************************************************/ + +void DetectLIFXControllers() +{ + json lifx_settings; + + /*-------------------------------------------------*\ + | Get LIFX settings from settings manager | + \*-------------------------------------------------*/ + lifx_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("LIFXDevices"); + + /*-------------------------------------------------*\ + | If the Wiz settings contains devices, process | + \*-------------------------------------------------*/ + if(lifx_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < lifx_settings["devices"].size(); device_idx++) + { + if(lifx_settings["devices"][device_idx].contains("ip")) + { + std::string lifx_ip = lifx_settings["devices"][device_idx]["ip"]; + std::string name = lifx_settings["devices"][device_idx]["name"]; + bool multizone = lifx_settings["devices"][device_idx]["multizone"]; + bool extended_multizone = lifx_settings["devices"][device_idx]["extended_multizone"]; + + LIFXController* controller = new LIFXController(lifx_ip, name, multizone, extended_multizone); + controller->FetchZoneCount(); + + RGBController_LIFX* rgb_controller = new RGBController_LIFX(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } + +} /* DetectLIFXControllers() */ + +REGISTER_DETECTOR("LIFX", DetectLIFXControllers); diff --git a/Controllers/LIFXController/RGBController_LIFX.cpp b/Controllers/LIFXController/RGBController_LIFX.cpp new file mode 100644 index 0000000..274b001 --- /dev/null +++ b/Controllers/LIFXController/RGBController_LIFX.cpp @@ -0,0 +1,132 @@ +/*---------------------------------------------------------*\ +| RGBController_LIFX.cpp | +| | +| RGBController for LIFX | +| | +| Adam Honse (calcprogrammer1@gmail.com) 05 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LIFX.h" + +/**------------------------------------------------------------------*\ + @name LIFX Globes + @category Light + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectLIFXControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LIFX::RGBController_LIFX(LIFXController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetManufacturer() + " " + controller->GetName(); + vendor = controller->GetManufacturer(); + type = DEVICE_TYPE_LIGHT; + version = controller->GetVersion(); + description = "LIFX Device"; + serial = controller->GetUniqueID(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_LIFX::~RGBController_LIFX() +{ + delete controller; +} + +void RGBController_LIFX::SetupZones() +{ + zone led_zone; + + unsigned int zone_count = controller->GetZoneCount(); + + /*---------------------------------------------------------*\ + | If there is only one zone, set up a single LED | + \*---------------------------------------------------------*/ + if(zone_count <= 1) + { + led_zone.name = "RGB Light"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + led new_led; + new_led.name = "RGB Light"; + + leds.push_back(new_led); + } + else + { + /*---------------------------------------------------------*\ + | Set up multiple LEDs | + \*---------------------------------------------------------*/ + led_zone.name = "RGB Light Strip"; + led_zone.type = ZONE_TYPE_LINEAR; + led_zone.leds_min = 1; + led_zone.leds_max = zone_count; + led_zone.leds_count = zone_count; + led_zone.matrix_map = NULL; + + zones.push_back(led_zone); + + for(size_t zone_idx = 0; zone_idx < zone_count; zone_idx++) + { + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + led new_led; + + new_led.name = "LED " + std::to_string(zone_idx); + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_LIFX::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LIFX::DeviceUpdateLEDs() +{ + controller->SetColors(colors); +} + +void RGBController_LIFX::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LIFX::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LIFX::DeviceUpdateMode() +{ + +} diff --git a/Controllers/LIFXController/RGBController_LIFX.h b/Controllers/LIFXController/RGBController_LIFX.h new file mode 100644 index 0000000..88e9972 --- /dev/null +++ b/Controllers/LIFXController/RGBController_LIFX.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LIFX.cpp | +| | +| RGBController for LIFX | +| | +| Adam Honse (calcprogrammer1@gmail.com) 05 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LIFXController.h" + +class RGBController_LIFX : public RGBController +{ +public: + RGBController_LIFX(LIFXController* controller_ptr); + ~RGBController_LIFX(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LIFXController* controller; +}; diff --git a/Controllers/LaviewTechnologyController/LaviewTechnologyController/LaviewTechnologyController.cpp b/Controllers/LaviewTechnologyController/LaviewTechnologyController/LaviewTechnologyController.cpp new file mode 100644 index 0000000..03bb1ea --- /dev/null +++ b/Controllers/LaviewTechnologyController/LaviewTechnologyController/LaviewTechnologyController.cpp @@ -0,0 +1,102 @@ +/*---------------------------------------------------------*\ +| LaviewTechnologyController.cpp | +| | +| Driver for Laview Tech. mice, including Glorious | +| | +| Kosta A (kostaarvanitis) 27 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "LogManager.h" +#include "LaviewTechnologyController.h" +#include "StringUtils.h" + +LaviewTechnologyController::LaviewTechnologyController(hid_device* dev_handle, hid_device_info* dev_info, std::string dev_name) +{ + device = dev_handle; + location = dev_info->path; + name = dev_name; + version = dev_info->release_number; + vendor = StringUtils::wstring_to_string(dev_info->manufacturer_string); + serial = StringUtils::wstring_to_string(dev_info->serial_number); +} + +LaviewTechnologyController::~LaviewTechnologyController() +{ + hid_close(device); +} + +std::string LaviewTechnologyController::GetLocation() +{ + return("HID: " + location); +} + +std::string LaviewTechnologyController::GetName() +{ + return(name); +} + +std::string LaviewTechnologyController::GetVendor() +{ + return(vendor); +} + +std::string LaviewTechnologyController::GetSerialNumber() +{ + return(serial); +} + +std::string LaviewTechnologyController::GetFirmwareVersion() +{ + return std::to_string(version); +} + +void LaviewTechnologyController::SetMode(unsigned int mode, unsigned int brightness, unsigned int speed, RGBColor* color) +{ + uint8_t buf[LAVIEW_TECHNOLOGY_REPORT_SIZE]; + memset(buf, 0x00, sizeof(buf)); + + buf[0] = 0xA1; + buf[1] = 0x0C; + buf[5] = 0x01; // Profile + + buf[16] = mode; + buf[56] = std::clamp(0u, brightness, 64u); + buf[58] = std::clamp(0u, speed, 64u); + buf[60] = 0; // Set for some modes??? + + switch (mode) + { + case LAVIEW_TECHNOLOGY_MODE_STATIC: + case LAVIEW_TECHNOLOGY_MODE_BREATHING: + buf[17] = RGBGetRValue(color[0]); + buf[18] = RGBGetGValue(color[0]); + buf[19] = RGBGetBValue(color[0]); + break; + default: + /*----------------------------------------------------------------*\ + | For these reports we inject the default color scheme into the | + | availble rgb values which in the official control application | + | are not present in some modes causing them to not function. | + | There is no mechanism in the control software for manipulating | + | these values, as such this functionality is also omitted here. | + \*----------------------------------------------------------------*/ + const unsigned char bytes[] = { + 0xFF, 0x00, 0x00, 0xFF, 0xA5, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0xFF, + 0x8B, 0x00, 0xFF + }; + memcpy(&buf[17], bytes, sizeof(bytes)); + break; + }; + + int ret = hid_send_feature_report(device, buf, sizeof(buf)); + if(ret < 0) + { + LOG_ERROR("[%s] Failure to send report (%ls)!", name.c_str(), hid_error(device)); + } +} \ No newline at end of file diff --git a/Controllers/LaviewTechnologyController/LaviewTechnologyController/LaviewTechnologyController.h b/Controllers/LaviewTechnologyController/LaviewTechnologyController/LaviewTechnologyController.h new file mode 100644 index 0000000..20f40c1 --- /dev/null +++ b/Controllers/LaviewTechnologyController/LaviewTechnologyController/LaviewTechnologyController.h @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| LaviewTechnologyController.h | +| | +| Driver for Laview Tech. mice, including Glorious | +| | +| Kosta A (kostaarvanitis) 27 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define LAVIEW_TECHNOLOGY_REPORT_SIZE 64 + +enum +{ + LAVIEW_TECHNOLOGY_MODE_OFF = 0x00, // Off + LAVIEW_TECHNOLOGY_MODE_STATIC = 0x01, // Normally on + LAVIEW_TECHNOLOGY_MODE_FLASHING = 0x02, // Wave + LAVIEW_TECHNOLOGY_MODE_BREATHING = 0x04, // Breathing single color + LAVIEW_TECHNOLOGY_MODE_SPECTRUM_CYCLE = 0x06, // Breating does not work with factory Core software + LAVIEW_TECHNOLOGY_MODE_RAINBOW_WAVE = 0x10, // Glorious Mode + LAVIEW_TECHNOLOGY_MODE_CHASE = 0x11, // Tail + LAVIEW_TECHNOLOGY_MODE_WAVE = 0x14, // Rave (Same issue as breathing) + LAVIEW_TECHNOLOGY_MODE_SPECTRUM_BREATHING = 0x15, // Seamless Breathing +}; + +enum +{ + LAVIEW_TECHNOLOGY_SPEED_SLOW = 1, + LAVIEW_TECHNOLOGY_SPEED_NORMAL = 50, + LAVIEW_TECHNOLOGY_SPEED_FAST = 100, +}; + +enum +{ + LAVIEW_TECHNOLOGY_BRIGHTNESS_SLOW = 0, + LAVIEW_TECHNOLOGY_BRIGHTNESS_NORMAL = 50, + LAVIEW_TECHNOLOGY_BRIGHTNESS_FAST = 100, +}; + +class LaviewTechnologyController +{ +public: + LaviewTechnologyController(hid_device* dev_handle, hid_device_info* dev_info, std::string name); + ~LaviewTechnologyController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetVendor(); + std::string GetSerialNumber(); + std::string GetFirmwareVersion(); + + void SetMode(unsigned int mode, unsigned int brightness, unsigned int speed, RGBColor* color); + +private: + hid_device* device; + std::string location; + std::string name; + std::string vendor; + std::string serial; + unsigned int version; +}; diff --git a/Controllers/LaviewTechnologyController/LaviewTechnologyController/RGBController_LaviewTechnology.cpp b/Controllers/LaviewTechnologyController/LaviewTechnologyController/RGBController_LaviewTechnology.cpp new file mode 100644 index 0000000..ff00858 --- /dev/null +++ b/Controllers/LaviewTechnologyController/LaviewTechnologyController/RGBController_LaviewTechnology.cpp @@ -0,0 +1,213 @@ +/*---------------------------------------------------------*\ +| RGBController_LaviewTechnology.cpp | +| | +| RGBController for Laview Tech. mice, including Glorious | +| | +| Kosta A (kostaarvanitis) 27 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LaviewTechnology.h" + +/**------------------------------------------------------------------*\ + @name Laview Technology Mice + @category Mouse + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectLaviewTechnologyMouse + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LaviewTechnology::RGBController_LaviewTechnology(LaviewTechnologyController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + type = DEVICE_TYPE_MOUSE; + description = "Glorious Device"; + vendor = controller->GetVendor(); + location = controller->GetLocation(); + serial = controller->GetSerialNumber(); + version = controller->GetFirmwareVersion(); + + mode Custom; + Custom.name = "Custom"; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Custom.brightness_min = LAVIEW_TECHNOLOGY_BRIGHTNESS_SLOW; + Custom.brightness = LAVIEW_TECHNOLOGY_BRIGHTNESS_NORMAL; + Custom.brightness_max = LAVIEW_TECHNOLOGY_BRIGHTNESS_FAST; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.value = LAVIEW_TECHNOLOGY_MODE_STATIC; + modes.push_back(Custom); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Flashing.brightness_min = LAVIEW_TECHNOLOGY_BRIGHTNESS_SLOW; + Flashing.brightness = LAVIEW_TECHNOLOGY_BRIGHTNESS_NORMAL; + Flashing.brightness_max = LAVIEW_TECHNOLOGY_BRIGHTNESS_FAST; + Flashing.speed_min = LAVIEW_TECHNOLOGY_SPEED_SLOW; + Flashing.speed = LAVIEW_TECHNOLOGY_SPEED_NORMAL; + Flashing.speed_max = LAVIEW_TECHNOLOGY_SPEED_FAST; + Flashing.color_mode = MODE_COLORS_NONE; + Flashing.value = LAVIEW_TECHNOLOGY_MODE_FLASHING; + modes.push_back(Flashing); + + mode Chase; + Chase.name = "Chase"; + Chase.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Chase.brightness_min = LAVIEW_TECHNOLOGY_BRIGHTNESS_SLOW; + Chase.brightness = LAVIEW_TECHNOLOGY_BRIGHTNESS_NORMAL; + Chase.brightness_max = LAVIEW_TECHNOLOGY_BRIGHTNESS_FAST; + Chase.speed_min = LAVIEW_TECHNOLOGY_SPEED_SLOW; + Chase.speed = LAVIEW_TECHNOLOGY_SPEED_NORMAL; + Chase.speed_max = LAVIEW_TECHNOLOGY_SPEED_FAST; + Chase.color_mode = MODE_COLORS_NONE; + Chase.value = LAVIEW_TECHNOLOGY_MODE_CHASE; + modes.push_back(Chase); + + mode Wave; + Wave.name = "Wave"; + Wave.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Wave.brightness_min = LAVIEW_TECHNOLOGY_BRIGHTNESS_SLOW; + Wave.brightness = LAVIEW_TECHNOLOGY_BRIGHTNESS_NORMAL; + Wave.brightness_max = LAVIEW_TECHNOLOGY_BRIGHTNESS_FAST; + Wave.speed_min = LAVIEW_TECHNOLOGY_SPEED_SLOW; + Wave.speed = LAVIEW_TECHNOLOGY_SPEED_NORMAL; + Wave.speed_max = LAVIEW_TECHNOLOGY_SPEED_FAST; + Wave.color_mode = MODE_COLORS_NONE; + Wave.value = LAVIEW_TECHNOLOGY_MODE_WAVE; + modes.push_back(Wave); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + SpectrumCycle.brightness_min = LAVIEW_TECHNOLOGY_BRIGHTNESS_SLOW; + SpectrumCycle.brightness = LAVIEW_TECHNOLOGY_BRIGHTNESS_NORMAL; + SpectrumCycle.brightness_max = LAVIEW_TECHNOLOGY_BRIGHTNESS_FAST; + SpectrumCycle.speed_min = LAVIEW_TECHNOLOGY_SPEED_SLOW; + SpectrumCycle.speed = LAVIEW_TECHNOLOGY_SPEED_NORMAL; + SpectrumCycle.speed_max = LAVIEW_TECHNOLOGY_SPEED_FAST; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.value = LAVIEW_TECHNOLOGY_MODE_SPECTRUM_CYCLE; + modes.push_back(SpectrumCycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.brightness_min = LAVIEW_TECHNOLOGY_BRIGHTNESS_SLOW; + Breathing.brightness = LAVIEW_TECHNOLOGY_BRIGHTNESS_NORMAL; + Breathing.brightness_max = LAVIEW_TECHNOLOGY_BRIGHTNESS_FAST; + Breathing.speed_min = LAVIEW_TECHNOLOGY_SPEED_SLOW; + Breathing.speed = LAVIEW_TECHNOLOGY_SPEED_NORMAL; + Breathing.speed_max = LAVIEW_TECHNOLOGY_SPEED_FAST; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1, ToRGBColor(255, 0, 0)); + Breathing.value = LAVIEW_TECHNOLOGY_MODE_BREATHING; + modes.push_back(Breathing); + + mode SpectrumBreathing; + SpectrumBreathing.name = "Spectrum Breathing"; + SpectrumBreathing.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + SpectrumBreathing.brightness_min = LAVIEW_TECHNOLOGY_BRIGHTNESS_SLOW; + SpectrumBreathing.brightness = LAVIEW_TECHNOLOGY_BRIGHTNESS_NORMAL; + SpectrumBreathing.brightness_max = LAVIEW_TECHNOLOGY_BRIGHTNESS_FAST; + SpectrumBreathing.speed_min = LAVIEW_TECHNOLOGY_SPEED_SLOW; + SpectrumBreathing.speed = LAVIEW_TECHNOLOGY_SPEED_NORMAL; + SpectrumBreathing.speed_max = LAVIEW_TECHNOLOGY_SPEED_FAST; + SpectrumBreathing.color_mode = MODE_COLORS_NONE; + SpectrumBreathing.value = LAVIEW_TECHNOLOGY_MODE_SPECTRUM_BREATHING; + modes.push_back(SpectrumBreathing); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + RainbowWave.brightness_min = LAVIEW_TECHNOLOGY_BRIGHTNESS_SLOW; + RainbowWave.brightness = LAVIEW_TECHNOLOGY_BRIGHTNESS_NORMAL; + RainbowWave.brightness_max = LAVIEW_TECHNOLOGY_BRIGHTNESS_FAST; + RainbowWave.speed_min = LAVIEW_TECHNOLOGY_SPEED_SLOW; + RainbowWave.speed = LAVIEW_TECHNOLOGY_SPEED_NORMAL; + RainbowWave.speed_max = LAVIEW_TECHNOLOGY_SPEED_FAST; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.value = LAVIEW_TECHNOLOGY_MODE_RAINBOW_WAVE; + modes.push_back(RainbowWave); + + mode Off; + Off.name = "Off"; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + Off.value = LAVIEW_TECHNOLOGY_MODE_OFF; + modes.push_back(Off); + + SetupZones(); +} + +void RGBController_LaviewTechnology::SetupZones() +{ + /*---------------------------------------------------------*\ + | Create a single zone | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + led new_led; + new_led.name = "LED"; + leds.push_back(new_led); + + SetupColors(); +} + +RGBController_LaviewTechnology::~RGBController_LaviewTechnology() +{ + delete controller; +} + +void RGBController_LaviewTechnology::ResizeZone(int /*zone*/, int /*new_size*/) +{ +} + +void RGBController_LaviewTechnology::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_LaviewTechnology::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_LaviewTechnology::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_LaviewTechnology::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SetMode(modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed, &colors[0]); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + controller->SetMode(modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed, &modes[active_mode].colors[0]); + } + else // MODE_COLORS_NONE + { + controller->SetMode(modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed, 0); + } +} diff --git a/Controllers/LaviewTechnologyController/LaviewTechnologyController/RGBController_LaviewTechnology.h b/Controllers/LaviewTechnologyController/LaviewTechnologyController/RGBController_LaviewTechnology.h new file mode 100644 index 0000000..4cf2b99 --- /dev/null +++ b/Controllers/LaviewTechnologyController/LaviewTechnologyController/RGBController_LaviewTechnology.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_LaviewTechnology.h | +| | +| RGBController for Laview Tech. mice, including Glorious | +| | +| Kosta A (kostaarvanitis) 27 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LaviewTechnologyController.h" + +class RGBController_LaviewTechnology : public RGBController +{ +public: + RGBController_LaviewTechnology(LaviewTechnologyController* controller_ptr); + ~RGBController_LaviewTechnology(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LaviewTechnologyController* controller; +}; diff --git a/Controllers/LaviewTechnologyController/LaviewTechnologyDetector.cpp b/Controllers/LaviewTechnologyController/LaviewTechnologyDetector.cpp new file mode 100644 index 0000000..5c04f3f --- /dev/null +++ b/Controllers/LaviewTechnologyController/LaviewTechnologyDetector.cpp @@ -0,0 +1,45 @@ +/*---------------------------------------------------------*\ +| LaviewTechnologyDetect.cpp | +| | +| Detector for Laview Technology brand Mice (Glorious) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "Detector.h" +#include "LaviewTechnologyController.h" +#include "RGBController.h" +#include "RGBController_LaviewTechnology.h" +#include "LogManager.h" + +#define LAVIEW_TECHNOLOGY_VID 0x22D4 + +#define GLORIOUS_MODEL_I_PID 0x1503 // Wired + +/******************************************************************************************\ +* * +* DetectLaviewTechnologyMouse * +* * +* Tests the USB address to see if a Laview Technology controller exists there. * +* * +\******************************************************************************************/ + +static void DetectLaviewTechnologyMouse(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LaviewTechnologyController* controller = new LaviewTechnologyController(dev, info, name); + RGBController_LaviewTechnology* rgb_controller = new RGBController_LaviewTechnology(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Glorious Model I", DetectLaviewTechnologyMouse, LAVIEW_TECHNOLOGY_VID, GLORIOUS_MODEL_I_PID, 1, 0xFF01, 0x02); diff --git a/Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseController.cpp b/Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseController.cpp new file mode 100644 index 0000000..bdd9437 --- /dev/null +++ b/Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseController.cpp @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| LegoDimensionsToypadBaseController.cpp | +| | +| Driver for Lego Dimensions Toypad Base | +| | +| Morgan Guimard (morg) 02 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LegoDimensionsToypadBaseController.h" +#include "StringUtils.h" + +LegoDimensionsToypadBaseController::LegoDimensionsToypadBaseController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + + Activate(); +} + +LegoDimensionsToypadBaseController::~LegoDimensionsToypadBaseController() +{ + hid_close(dev); +} + +std::string LegoDimensionsToypadBaseController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LegoDimensionsToypadBaseController::GetNameString() +{ + return(name); +} + +std::string LegoDimensionsToypadBaseController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LegoDimensionsToypadBaseController::Activate() +{ + unsigned char usb_buf[LEGO_DIMENSIONS_TOYPAD_BASE_PACKET_LENGTH]; + + memset(usb_buf, 0x00, LEGO_DIMENSIONS_TOYPAD_BASE_PACKET_LENGTH); + + usb_buf[1] = LEGO_DIMENSIONS_TOYPAD_BASE_REPORT_ID; + usb_buf[2] = 0x0F; // command length + usb_buf[3] = LEGO_DIMENSIONS_TOYPAD_BASE_ACTIVATE_VALUE; + usb_buf[4] = 0x01; + usb_buf[5] = 0x28; //'(' + usb_buf[6] = 0x63; //'c' + usb_buf[7] = 0x29; //')' + usb_buf[8] = 0x20; //' ' + usb_buf[9] = 0x4C; //'L' + usb_buf[10] = 0x45; //'E' + usb_buf[11] = 0x47; //'G' + usb_buf[12] = 0x4F; //'O' + usb_buf[13] = 0x20; //' ' + usb_buf[14] = 0x32; //'2' + usb_buf[15] = 0x30; //'0' + usb_buf[16] = 0x31; //'1' + usb_buf[17] = 0x34; //'4' + usb_buf[18] = 0xF7; // checksum + + hid_write(dev, usb_buf, LEGO_DIMENSIONS_TOYPAD_BASE_PACKET_LENGTH); +} + +void LegoDimensionsToypadBaseController::SetDirect(unsigned char zone, RGBColor color) +{ + unsigned char usb_buf[LEGO_DIMENSIONS_TOYPAD_BASE_PACKET_LENGTH]; + + memset(usb_buf, 0x00, LEGO_DIMENSIONS_TOYPAD_BASE_PACKET_LENGTH); + + usb_buf[1] = LEGO_DIMENSIONS_TOYPAD_BASE_REPORT_ID; + usb_buf[2] = 0x06; // command length + usb_buf[3] = LEGO_DIMENSIONS_TOYPAD_BASE_DIRECT_MODE_VALUE; + usb_buf[4] = 0x02; // constant value + usb_buf[5] = zone; + usb_buf[6] = RGBGetRValue(color); + usb_buf[7] = RGBGetGValue(color); + usb_buf[8] = RGBGetBValue(color); + + for(unsigned int i = 1; i < 9; i ++) + { + usb_buf[9] += usb_buf[i]; // checksum + } + + hid_write(dev, usb_buf, LEGO_DIMENSIONS_TOYPAD_BASE_PACKET_LENGTH); +} + +void LegoDimensionsToypadBaseController::SetMode(unsigned char zone, unsigned char mode_value, uint8_t speed, RGBColor color) +{ + unsigned char usb_buf[LEGO_DIMENSIONS_TOYPAD_BASE_PACKET_LENGTH]; + + memset(usb_buf, 0x00, LEGO_DIMENSIONS_TOYPAD_BASE_PACKET_LENGTH); + + usb_buf[1] = LEGO_DIMENSIONS_TOYPAD_BASE_REPORT_ID; + usb_buf[3] = mode_value; + usb_buf[5] = zone; + + if(mode_value == LEGO_DIMENSIONS_TOYPAD_BASE_FLASH_MODE_VALUE) + { + usb_buf[2] = 0x09; // command length + usb_buf[4] = 0x1F; // constant value + usb_buf[6] = speed; // light on length + usb_buf[7] = speed; // light off length + usb_buf[8] = 10; // number of pulses + usb_buf[9] = RGBGetRValue(color); + usb_buf[10] = RGBGetGValue(color); + usb_buf[11] = RGBGetBValue(color); + + for(unsigned int i = 1; i < 12; i ++) + { + usb_buf[12] += usb_buf[i]; // checksum + } + } + else if (mode_value == LEGO_DIMENSIONS_TOYPAD_BASE_FADE_MODE_VALUE) + { + usb_buf[2] = 0x08; // command length + usb_buf[4] = 0x0F; // constant value + usb_buf[6] = speed; // light on length + usb_buf[7] = 10; // number of pulses + usb_buf[8] = RGBGetRValue(color); + usb_buf[9] = RGBGetGValue(color); + usb_buf[10] = RGBGetBValue(color); + + for(unsigned int i = 1; i < 11; i ++) + { + usb_buf[11] += usb_buf[i]; // checksum + } + } + + hid_write(dev, usb_buf, LEGO_DIMENSIONS_TOYPAD_BASE_PACKET_LENGTH); +} diff --git a/Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseController.h b/Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseController.h new file mode 100644 index 0000000..0082f34 --- /dev/null +++ b/Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseController.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------*\ +| LegoDimensionsToypadBaseController.h | +| | +| Driver for Lego Dimensions Toypad Base | +| | +| Morgan Guimard (morg) 02 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define LEGO_DIMENSIONS_TOYPAD_BASE_REPORT_ID 0x55 +#define LEGO_DIMENSIONS_TOYPAD_BASE_PACKET_LENGTH 32 + +enum +{ + LEGO_DIMENSIONS_TOYPAD_BASE_ACTIVATE_VALUE = 0xB0, + LEGO_DIMENSIONS_TOYPAD_BASE_DIRECT_MODE_VALUE = 0xC0, + LEGO_DIMENSIONS_TOYPAD_BASE_FLASH_MODE_VALUE = 0xC3, + LEGO_DIMENSIONS_TOYPAD_BASE_FADE_MODE_VALUE = 0xC2 +}; + +enum +{ + LEGO_DIMENSIONS_TOYPAD_BASE_SPEED_MIN = 0x00, + LEGO_DIMENSIONS_TOYPAD_BASE_SPEED_MAX = 0xFF +}; + +class LegoDimensionsToypadBaseController +{ +public: + LegoDimensionsToypadBaseController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~LegoDimensionsToypadBaseController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetDirect(unsigned char zone, RGBColor color); + void SetMode(unsigned char zone, unsigned char mode_value, uint8_t speed, RGBColor color); + +protected: + hid_device* dev; + +private: + std::string location; + std::string name; + std::string version; + + void Activate(); +}; diff --git a/Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseControllerDetect.cpp b/Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseControllerDetect.cpp new file mode 100644 index 0000000..930727d --- /dev/null +++ b/Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| LegoDimensionsToypadBaseControllerDetect.cpp | +| | +| Detector for Lego Dimensions Toypad Base | +| | +| Morgan Guimard (morg) 02 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LegoDimensionsToypadBaseController.h" +#include "RGBController_LegoDimensionsToypadBase.h" + +/*---------------------------------------------------------*\ +| Logic3 vendor ID | +\*---------------------------------------------------------*/ +#define LOGIC_3_VID 0x0E6F + +/*---------------------------------------------------------*\ +| Lego Dimensions Toypad Base product ID | +\*---------------------------------------------------------*/ +#define LEGO_DIMENSIONS_TOYPAD_BASE_PID 0x0241 + +void DetectLegoDimensionsToypadBaseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LegoDimensionsToypadBaseController* controller = new LegoDimensionsToypadBaseController(dev, *info, name); + RGBController_LegoDimensionsToypadBase* rgb_controller = new RGBController_LegoDimensionsToypadBase(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("Lego Dimensions Toypad Base", DetectLegoDimensionsToypadBaseControllers, LOGIC_3_VID, LEGO_DIMENSIONS_TOYPAD_BASE_PID); diff --git a/Controllers/LegoDimensionsToypadBaseController/RGBController_LegoDimensionsToypadBase.cpp b/Controllers/LegoDimensionsToypadBaseController/RGBController_LegoDimensionsToypadBase.cpp new file mode 100644 index 0000000..8f311ce --- /dev/null +++ b/Controllers/LegoDimensionsToypadBaseController/RGBController_LegoDimensionsToypadBase.cpp @@ -0,0 +1,139 @@ +/*---------------------------------------------------------*\ +| RGBController_LegoDimensionsToypadBase.cpp | +| | +| RGBController for Lego Dimensions Toypad Base | +| | +| Morgan Guimard (morg) 02 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_LegoDimensionsToypadBase.h" + +/**------------------------------------------------------------------*\ + @name Lego Dimensions Toypad Base + @category Case + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLegoDimensionsToypadBaseControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LegoDimensionsToypadBase::RGBController_LegoDimensionsToypadBase(LegoDimensionsToypadBaseController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logic3"; + type = DEVICE_TYPE_LEDSTRIP; + description = "Lego Dimensions Toypad Base"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Flash; + Flash.name = "Flash"; + Flash.value = LEGO_DIMENSIONS_TOYPAD_BASE_FLASH_MODE_VALUE; + Flash.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Flash.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flash.colors.resize(1); + Flash.colors_max = 1; + Flash.colors_min = 1; + Flash.speed = LEGO_DIMENSIONS_TOYPAD_BASE_SPEED_MAX / 2; + Flash.speed_max = LEGO_DIMENSIONS_TOYPAD_BASE_SPEED_MAX; + Flash.speed_min = LEGO_DIMENSIONS_TOYPAD_BASE_SPEED_MIN; + modes.push_back(Flash); + + mode Fade; + Fade.name = "Fade"; + Fade.value = LEGO_DIMENSIONS_TOYPAD_BASE_FADE_MODE_VALUE; + Fade.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Fade.color_mode = MODE_COLORS_MODE_SPECIFIC; + Fade.colors.resize(1); + Fade.colors_max = 1; + Fade.colors_min = 1; + Fade.speed = LEGO_DIMENSIONS_TOYPAD_BASE_SPEED_MAX / 2; + Fade.speed_max = LEGO_DIMENSIONS_TOYPAD_BASE_SPEED_MAX; + Fade.speed_min = LEGO_DIMENSIONS_TOYPAD_BASE_SPEED_MIN; + modes.push_back(Fade); + + SetupZones(); +} + +RGBController_LegoDimensionsToypadBase::~RGBController_LegoDimensionsToypadBase() +{ + delete controller; +} + +void RGBController_LegoDimensionsToypadBase::SetupZones() +{ + std::vector zone_names = + { + "Center", + "Left", + "Right" + }; + + for(const std::string& zone_name: zone_names) + { + zone new_zone; + + new_zone.name = zone_name; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + led new_led; + new_led.name = "LED"; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_LegoDimensionsToypadBase::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LegoDimensionsToypadBase::DeviceUpdateLEDs() +{ + for(unsigned int zone = 0; zone < zones.size(); zone++) + { + UpdateZoneLEDs(zone); + } +} + +void RGBController_LegoDimensionsToypadBase::UpdateZoneLEDs(int zone) +{ + controller->SetDirect(zone + 1, zones[zone].colors[0]); +} + +void RGBController_LegoDimensionsToypadBase::UpdateSingleLED(int /*led*/) +{ + UpdateZoneLEDs(0); +} + +void RGBController_LegoDimensionsToypadBase::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + controller->SetMode(0, modes[active_mode].value, modes[active_mode].speed, modes[active_mode].colors[0]); + } +} diff --git a/Controllers/LegoDimensionsToypadBaseController/RGBController_LegoDimensionsToypadBase.h b/Controllers/LegoDimensionsToypadBaseController/RGBController_LegoDimensionsToypadBase.h new file mode 100644 index 0000000..934a480 --- /dev/null +++ b/Controllers/LegoDimensionsToypadBaseController/RGBController_LegoDimensionsToypadBase.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_LegoDimensionsToypadBase.h | +| | +| RGBController for Lego Dimensions Toypad Base | +| | +| Morgan Guimard (morg) 02 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LegoDimensionsToypadBaseController.h" + +class RGBController_LegoDimensionsToypadBase : public RGBController +{ +public: + RGBController_LegoDimensionsToypadBase(LegoDimensionsToypadBaseController* controller_ptr); + ~RGBController_LegoDimensionsToypadBase(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LegoDimensionsToypadBaseController* controller; +}; diff --git a/Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBController.cpp b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBController.cpp new file mode 100644 index 0000000..4f4cf71 --- /dev/null +++ b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBController.cpp @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| Lenovo4ZoneUSBController.cpp | +| | +| Driver for Lenovo 4-Zone devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Lenovo4ZoneUSBController.h" +#include "LogManager.h" +#include "StringUtils.h" + +Lenovo4ZoneUSBController::Lenovo4ZoneUSBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + pid = in_pid; + name = dev_name; +} + +Lenovo4ZoneUSBController::~Lenovo4ZoneUSBController() +{ + hid_close(dev); +} + +void Lenovo4ZoneUSBController::setMode(const KeyboardState &in_mode) +{ + uint8_t buffer[LENOVO_4_ZONE_HID_PACKET_SIZE] = + { + in_mode.header[0], in_mode.header[1], + in_mode.effect, + in_mode.speed, + in_mode.brightness, + in_mode.zone0_rgb[0], in_mode.zone0_rgb[1], in_mode.zone0_rgb[2], + in_mode.zone1_rgb[0], in_mode.zone1_rgb[1], in_mode.zone1_rgb[2], + in_mode.zone2_rgb[0], in_mode.zone2_rgb[1], in_mode.zone2_rgb[2], + in_mode.zone3_rgb[0], in_mode.zone3_rgb[1], in_mode.zone3_rgb[2], + 0x00, + in_mode.wave_ltr, in_mode.wave_rtl + }; + hid_send_feature_report(dev, buffer, LENOVO_4_ZONE_HID_PACKET_SIZE); +} + +uint16_t Lenovo4ZoneUSBController::getPid() +{ + return pid; +} + +std::string Lenovo4ZoneUSBController::getName() +{ + return name; +} + +std::string Lenovo4ZoneUSBController::getLocation() +{ + return location; +} diff --git a/Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBController.h b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBController.h new file mode 100644 index 0000000..580f148 --- /dev/null +++ b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBController.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| Lenovo4ZoneUSBController.h | +| | +| Driver for Lenovo 4-Zone devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include "RGBController.h" +#include "LogManager.h" +#include "LenovoDevices4Zone.h" + +#ifndef HID_MAX_STR +#define HID_MAX_STR 255 +#endif + +#define LENOVO_4_ZONE_HID_PACKET_SIZE 33 + +class Lenovo4ZoneUSBController +{ + public: + /*--------------*\ + |ctor(s) and dtor| + \*--------------*/ + Lenovo4ZoneUSBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name); + ~Lenovo4ZoneUSBController(); + + void setMode(const KeyboardState &in_mode); + + /*--------------*\ + |device functions| + \*--------------*/ + uint16_t getPid(); + std::string getName(); + std::string getLocation(); + + private: + /*--------------*\ + |data members | + \*--------------*/ + std::string name; + hid_device *dev; + std::string location; + uint16_t pid; + KeyboardState mode; + + /*--------------*\ + |device functions| + \*--------------*/ + void sendBasicInstruction(uint8_t instruction); +}; diff --git a/Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBControllerDetect.cpp b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBControllerDetect.cpp new file mode 100644 index 0000000..662e3f7 --- /dev/null +++ b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBControllerDetect.cpp @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| Lenovo4ZoneUSBControllerDetect.cpp | +| | +| Detector for Lenovo 4-Zone devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "Lenovo4ZoneUSBController.h" +#include "LenovoDevices4Zone.h" +#include "RGBController_Lenovo4ZoneUSB.h" + +/*-----------------------------------------------------*\ +| vendor IDs | +\*-----------------------------------------------------*/ +#define ITE_VID 0x048D + +/*-----------------------------------------------------*\ +| Interface, Usage, and Usage Page | +\*-----------------------------------------------------*/ +enum +{ + LENOVO_PAGE = 0xFF89, + LENOVO_USAGE = 0xCC +}; + +void DetectLenovo4ZoneUSBControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + Lenovo4ZoneUSBController* controller = new Lenovo4ZoneUSBController(dev, info->path, info->product_id, name); + RGBController_Lenovo4ZoneUSB* rgb_controller = new RGBController_Lenovo4ZoneUSB(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("Lenovo Ideapad 3-15ach6", DetectLenovo4ZoneUSBControllers, ITE_VID, IDEAPAD_315ACH6, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo 5 2023", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2023_PID, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo 5 2023 Ideapad", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2023_IDEAPAD_PID, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo 5 2022", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2022_PID, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo 5 2022 Ideapad", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2022_IDEAPAD_PID, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo 5 2021", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2021_PID, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo 5 2021 Ideapad", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2021_IDEAPAD_PID, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo 5 2020", DetectLenovo4ZoneUSBControllers, ITE_VID, LEGION_5_2020_PID, LENOVO_PAGE, LENOVO_USAGE); diff --git a/Controllers/LenovoControllers/Lenovo4ZoneUSBController/LenovoDevices4Zone.h b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/LenovoDevices4Zone.h new file mode 100644 index 0000000..8c5a377 --- /dev/null +++ b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/LenovoDevices4Zone.h @@ -0,0 +1,107 @@ +/*---------------------------------------------------------*\ +| LenovoDevices4Zone.h | +| | +| Device list for Lenovo 4-Zone devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBControllerKeyNames.h" +#include "RGBController.h" +#include "LenovoDevices.h" + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define IDEAPAD_315ACH6 0xC963 +#define LEGION_5_2023_PID 0xC985 +#define LEGION_5_2023_IDEAPAD_PID 0xC984 +#define LEGION_5_2022_PID 0xC975 +#define LEGION_5_2022_IDEAPAD_PID 0xC973 +#define LEGION_5_2021_PID 0xC965 +#define LEGION_5_2021_IDEAPAD_PID 0xC963 +#define LEGION_5_2020_PID 0xC955 + +enum LENOVO_4_ZONE_EFFECT +{ + LENOVO_4_ZONE_EFFECT_STATIC = 1, + LENOVO_4_ZONE_EFFECT_BREATH = 3, + LENOVO_4_ZONE_EFFECT_WAVE = 4, + LENOVO_4_ZONE_EFFECT_SMOOTH = 6, +}; + +enum LENOVO_4_ZONE_BRIGHTNESS +{ + LENOVO_4_ZONE_BRIGHTNESS_LOW = 1, + LENOVO_4_ZONE_BRIGHTNESS_HIGH = 2, +}; + +enum LENOVO_4_ZONE_SPEED +{ + LENOVO_4_ZONE_SPEED_SLOWEST = 1, + LENOVO_4_ZONE_SPEED_SLOW = 2, + LENOVO_4_ZONE_SPEED_FAST = 3, + LENOVO_4_ZONE_SPEED_FASTEST = 4, +}; + +/// struct a USB packet for set the keyboard LEDs +class KeyboardState +{ +public: + uint8_t header[2] = {0xCC, 0x16}; + uint8_t effect = LENOVO_4_ZONE_EFFECT_STATIC; + uint8_t speed = LENOVO_4_ZONE_SPEED_SLOWEST; + uint8_t brightness = LENOVO_4_ZONE_BRIGHTNESS_LOW; + uint8_t zone0_rgb[3] = {0xFF, 0xFF, 0xFF}; + uint8_t zone1_rgb[3] = {0xFF, 0xFF, 0xFF}; + uint8_t zone2_rgb[3] = {0xFF, 0xFF, 0xFF}; + uint8_t zone3_rgb[3] = {0xFF, 0xFF, 0xFF}; + uint8_t wave_ltr = 0; + uint8_t wave_rtl = 0; + + void Reset() + { + header[0] = 0xCC, header[1] = 0x16; + effect = LENOVO_4_ZONE_EFFECT_STATIC; + speed = LENOVO_4_ZONE_SPEED_SLOWEST; + brightness = LENOVO_4_ZONE_BRIGHTNESS_LOW; + zone0_rgb[0] = 0xFF, zone0_rgb[1] = 0xFF, zone0_rgb[2] = 0xFF; + zone1_rgb[0] = 0xFF, zone1_rgb[1] = 0xFF, zone1_rgb[2] = 0xFF; + zone2_rgb[0] = 0xFF, zone2_rgb[1] = 0xFF, zone2_rgb[2] = 0xFF; + zone3_rgb[0] = 0xFF, zone3_rgb[1] = 0xFF, zone3_rgb[2] = 0xFF; + wave_ltr = 0; + wave_rtl = 0; + } + + void SetColors(std::vector group_colors) + { + zone0_rgb[0] = RGBGetRValue(group_colors[0]); + zone0_rgb[1] = RGBGetGValue(group_colors[0]); + zone0_rgb[2] = RGBGetBValue(group_colors[0]); + zone1_rgb[0] = RGBGetRValue(group_colors[1]); + zone1_rgb[1] = RGBGetGValue(group_colors[1]); + zone1_rgb[2] = RGBGetBValue(group_colors[1]); + zone2_rgb[0] = RGBGetRValue(group_colors[2]); + zone2_rgb[1] = RGBGetGValue(group_colors[2]); + zone2_rgb[2] = RGBGetBValue(group_colors[2]); + zone3_rgb[0] = RGBGetRValue(group_colors[3]); + zone3_rgb[1] = RGBGetGValue(group_colors[3]); + zone3_rgb[2] = RGBGetBValue(group_colors[3]); + } +}; + +/*-------------------------*\ +| 4-Zone keyboard | +\*-------------------------*/ + +static const lenovo_led lenovo_4_zone_leds[] +{ + {0x00, "Left side"}, + {0x01, "Left center"}, + {0x02, "Right center"}, + {0x03, "Right side"}, +}; diff --git a/Controllers/LenovoControllers/Lenovo4ZoneUSBController/RGBController_Lenovo4ZoneUSB.cpp b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/RGBController_Lenovo4ZoneUSB.cpp new file mode 100644 index 0000000..07f25b1 --- /dev/null +++ b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/RGBController_Lenovo4ZoneUSB.cpp @@ -0,0 +1,176 @@ +/*---------------------------------------------------------*\ +| RGBController_Lenovo4ZoneUSB.cpp | +| | +| Device list for Lenovo 4-Zone devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include "Lenovo4ZoneUSBController.h" +#include "LenovoDevices4Zone.h" +#include "RGBController_Lenovo4ZoneUSB.h" +#include "LogManager.h" + +/**------------------------------------------------------------------*\ + @name Lenovo 4 Zone USB + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectLenovo4ZoneUSBControllers + @comment Tested on Lenovo Legion 5 2021 +\*-------------------------------------------------------------------*/ + + +#define LENOVO_4_ZONE_NUM_LEDS 4 + +RGBController_Lenovo4ZoneUSB::RGBController_Lenovo4ZoneUSB(Lenovo4ZoneUSBController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->getName(); + type = DEVICE_TYPE_LAPTOP; + vendor = "Lenovo"; + description = "Lenovo 4-Zone device"; + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 1; + Direct.brightness_max = 2; + + modes.push_back(Direct); + + mode Breath; + Breath.name = "Breathing"; + Breath.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breath.color_mode = MODE_COLORS_PER_LED; + Breath.brightness_min = 1; + Breath.brightness_max = 2; + Breath.speed_min = 1; + Breath.speed_max = 4; + + modes.push_back(Breath); + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Wave.color_mode = MODE_COLORS_RANDOM; + Wave.brightness_min = 1; + Wave.brightness_max = 2; + Wave.speed_min = 1; + Wave.speed_max = 4; + Wave.direction = MODE_DIRECTION_LEFT | MODE_DIRECTION_RIGHT; + modes.push_back(Wave); + + mode Smooth; + Smooth.name = "Spectrum Cycle"; + Smooth.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Smooth.color_mode = MODE_COLORS_RANDOM; + Smooth.brightness_min = 1; + Smooth.brightness_max = 2; + Smooth.speed_min = 1; + Smooth.speed_max = 4; + modes.push_back(Smooth); + + SetupZones(); +} + +RGBController_Lenovo4ZoneUSB::~RGBController_Lenovo4ZoneUSB() +{ + delete controller; +} + +void RGBController_Lenovo4ZoneUSB::SetupZones() +{ + zone new_zone; + new_zone.name = ZONE_EN_KEYBOARD; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_count = LENOVO_4_ZONE_NUM_LEDS; + new_zone.leds_max = new_zone.leds_count; + new_zone.leds_min = new_zone.leds_count; + + new_zone.matrix_map = NULL; + + + zones.push_back(new_zone); + + for(unsigned int led_idx = 0; led_idx < LENOVO_4_ZONE_NUM_LEDS; led_idx++ ) + { + led new_led; + new_led.name = lenovo_4_zone_leds[led_idx].name; + new_led.value = lenovo_4_zone_leds[led_idx].led_num; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_Lenovo4ZoneUSB::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Lenovo4ZoneUSB::UpdateSingleLED(int /*led*/) +{ +} + +void RGBController_Lenovo4ZoneUSB::UpdateZoneLEDs(int /*zone*/) +{ +} + +void RGBController_Lenovo4ZoneUSB::DeviceUpdateLEDs() +{ + state.SetColors(colors); + controller->setMode(state); +} + +void RGBController_Lenovo4ZoneUSB::DeviceUpdateMode() +{ + state.Reset(); + state.SetColors(colors); + + switch (active_mode) + { + case 0: + state.effect = LENOVO_4_ZONE_EFFECT_STATIC; + break; + case 1: + state.effect = LENOVO_4_ZONE_EFFECT_BREATH; + break; + case 2: + state.effect = LENOVO_4_ZONE_EFFECT_WAVE; + state.wave_ltr = modes[active_mode].direction?0:1; + state.wave_rtl = modes[active_mode].direction?1:0; + break; + case 3: + state.effect = LENOVO_4_ZONE_EFFECT_SMOOTH; + break; + } + + if(active_mode != (LENOVO_4_ZONE_EFFECT_STATIC - 1)) // mode number from 0, but in mode from 1 + { + state.speed = modes[active_mode].speed; + } + state.brightness = modes[active_mode].brightness; + + controller->setMode(state); +} + +void RGBController_Lenovo4ZoneUSB::DeviceSaveMode() +{ + /*---------------------------------------------------------*\ + | This device does not support saving or multiple modes | + \*---------------------------------------------------------*/ +} diff --git a/Controllers/LenovoControllers/Lenovo4ZoneUSBController/RGBController_Lenovo4ZoneUSB.h b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/RGBController_Lenovo4ZoneUSB.h new file mode 100644 index 0000000..79eef4f --- /dev/null +++ b/Controllers/LenovoControllers/Lenovo4ZoneUSBController/RGBController_Lenovo4ZoneUSB.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_Lenovo4ZoneUSB.h | +| | +| RGBController for Lenovo 4-Zone devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "LenovoDevices.h" +#include "Lenovo4ZoneUSBController.h" +#include "RGBController.h" + +#define NA 0xFFFFFFFF + +class RGBController_Lenovo4ZoneUSB : public RGBController +{ +public: + RGBController_Lenovo4ZoneUSB(Lenovo4ZoneUSBController* controller_ptr); + ~RGBController_Lenovo4ZoneUSB(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + KeyboardState state; + + Lenovo4ZoneUSBController *controller; +}; diff --git a/Controllers/LenovoControllers/LenovoDevices.h b/Controllers/LenovoControllers/LenovoDevices.h new file mode 100644 index 0000000..89dd4b0 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoDevices.h @@ -0,0 +1,1985 @@ +/*---------------------------------------------------------*\ +| LenovoDevices.h | +| | +| Device list for Lenovo USB devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBControllerKeyNames.h" +#include "RGBController.h" + +#define NA 0xFFFFFFFF + +/*-------------------------------------------------------------------*\ +| Note: additions here must be adeed to RGBController_LenovoUSB.cpp in| +| the switch statements which are on lines 28 and 60 at time of | +| writing | +\*-------------------------------------------------------------------*/ + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define LEGION_Y740 0xC935 +#define LEGION_Y750 0xC956 +#define LEGION_Y750S 0xC957 +#define LEGION_Y760 0xC968 +#define LEGION_Y760S 0xC967 +#define LEGION_S7GEN7 0xC977 +#define LEGION_7GEN7 0xC978 +#define LEGION_7GEN8 0xC988 +#define LEGION_S7GEN8 0xC987 +#define LEGION_7GEN9 0xC997 +#define LEGION_7GEN9_H 0xC998 +#define LEGION_7GEN10 0xC197 +#define LEGION_5GEN10 0xC195 + +enum LENOVO_KEYBOARD +{ + ISO, + ANSI, + JAPAN +}; + +enum LENOVO_SIZE +{ + SEVENTEEN, + FIFTEEN, + UNKNOWN +}; + +struct lenovo_led +{ + uint8_t led_num; + std::string name; +}; + +struct lenovo_zone +{ + std::string name; + zone_type type; + unsigned char id; + unsigned int height; + unsigned int width; + const unsigned int* matrix_map; + const lenovo_led* leds; + unsigned int start; //index to start reading the list of leds + unsigned int end; //end index +}; + +/*---------*\ +| LED MAPS | +\*---------*/ + +static const unsigned int legion_Y760_ansi_leds_map[] = + { 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, NA, 21, 22, 23, 24, 25, 26, 27, 28, 29, NA, 30, 31, 32, NA, 52, 33, 34, 35, 36, + 37, NA, 38, 39, 40, 41, 42, 43, 44, 45, 46, NA, 47, 48, 49, 50, 51, 53, NA, NA, NA, + 54, NA, 55, 56, 57, NA, 58, 59, 60, 61, 62, 63, 64, 65, 66, NA, 67, 68, 69, 70, NA, + 72, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 90, 91, NA, 92, 93, 94, 95, 71, + 73, 74, 75, NA, 76, 77, 78, 79, 80, 81, 82, 83, 84, NA, 86, 88, 89, 111, 113, 114, 96, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 85, NA, 87, NA, NA, 112, NA, 115, NA, + 97, 98, NA, 99, 100, 101, NA, 102, 103, 104, 105, 106, 107, 108, NA, 109, 110, 119, 121, 123, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 116, 117, 118, NA, NA, NA, 120, 122, 124, 125, + 126, NA, 127, 128, 129, 130, NA, NA, NA, NA, 131, 132, 133, NA, 135, NA, NA, 140, NA, 142, 144, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 136, NA, NA, NA, 141, 143, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 134, 137, NA, 139, NA, NA, NA, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 138, NA, NA, NA, NA, NA, NA }; + +static const unsigned int legion_Y760_iso_leds_map[] = + { 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, NA, 21, 22, 23, 24, 25, 26, 27, 28, 29, NA, 30, 31, 32, NA, NA, 33, 34, 35, 36, + 37, NA, 38, 39, 40, 41, 42, 43, 44, 45, 46, NA, 47, 48, 49, NA, 50, 51, NA, NA, NA, + 52, NA, 53, 54, 55, NA, 56, 57, 58, 59, 60, 61, 62, 63, 64, NA, 65, 66, 67, 68, NA, + 70, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 88, 89, NA, NA, 90, 91, 92, 69, + 71, 72, 73, NA, 74, 75, 76, 77, 78, 79, 80, 81, 82, NA, 84, 86, NA, 109, 111, 112, 93, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 83, NA, 85, 87, NA, 110, NA, 113, NA, + 94, NA, 95, 97, 98, 99, NA, 100, 101, 102, 103, 104, 105, 106, NA, 107, 108, 117, 119, 121, NA, + NA, NA, 96, NA, NA, NA, NA, NA, NA, NA, NA, 114, 115, 116, NA, NA, NA, 118, 120, 122, 123, + 124, NA, 125, 126, 127, 128, NA, NA, NA, NA, 129, 130, 131, NA, 133, NA, NA, 138, NA, 140, 142, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 134, NA, NA, NA, 139, 141, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 132, 135, NA, 137, NA, NA, NA, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 136, NA, NA, NA, NA, NA, NA }; + +static const unsigned int legion_Y760_jp_leds_map[] = + { 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, NA, 21, 22, 23, 24, 25, 26, 27, 28, 29, NA, 30, 31, 32, 33, 53, 34, 35, 36, 37, + 38, NA, 39, 40, 41, 42, 43, 44, 45, 46, 47, NA, 48, 49, 50, 51, 52, 54, NA, NA, NA, + 55, NA, 56, 57, 58, NA, 59, 60, 61, 62, 63, 64, 65, 66, 68, NA, 70, 71, 72, 73, NA, + 75, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 67, 69, NA, 93, 94, 95, 96, 74, + 76, 77, 78, NA, 79, 80, 81, 82, 83, 84, 85, 86, 87, NA, 89, 91, NA, 116, 118, 119, 97, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 88, NA, 90, 92, NA, 117, NA, 120, NA, + 98, 99, NA, 100, 101, 102, NA, 103, 104, 105, 106, 107, 109, 111, 113, 114, 115, 121, 123, 125, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 108, 110, 112, NA, NA, NA, 122, 124, 126, 127, + 128, NA, 129, 130, 131, 132, 133, NA, NA, 134, 135, 136, 137, NA, 139, NA, NA, 144, NA, 146, 148, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 140, NA, NA, NA, 145, 147, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 138, 141, NA, 143, NA, NA, NA, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 142, NA, NA, NA, NA, NA, NA }; + +static const unsigned int legion_Y740_17_ansi_leds_map[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, NA, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, NA, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, NA, 89, NA, 88, NA, + 90, 91, 92, 93, 94, NA, NA, 95, NA, 96, 97, 98, 99, 100, NA, NA, 101, 102, 103 }; + +static const unsigned int legion_Y740_17_iso_leds_map[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, NA, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, NA, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, NA, NA, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, NA, 88, NA, 87, NA, + 89, 90, 91, 92, 93, NA, NA, 94, NA, 95, 96, 97, 98, 99, NA, NA, 100, 101, 102 }; + +static const unsigned int legion_Y740_15_ansi_leds_map[] = + { 84, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, NA, + 85, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 86, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, NA, + 87, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 88, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, NA, + 89, 72, 73, 74, 75, 76, NA, NA, NA, 77, 78, 79, NA, 80, NA, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 81, 82, 83, NA }; + +static const unsigned int legion_Y740_15_iso_leds_map[] = + { 83, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, NA, + 84, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 85, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, NA, + 86, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, NA, + 87, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, NA, + 88, 71, 72, 73, 74, 75, NA, NA, NA, 76, 77, 78, NA, 79, NA, NA, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 80, 81, 82, NA }; + +/*---------------------*\ +| zone 1, keyboard ANSI | +\*---------------------*/ + +const lenovo_led legion_Y760_ansi_leds[] +{ + //row 1 + {0x01, KEY_EN_ESCAPE},//0 + {0x02, KEY_EN_F1},//1 + {0x03, KEY_EN_F2},//2 + {0x04, KEY_EN_F3},//3 + {0x05, KEY_EN_F4},//4 + {0x06, KEY_EN_F5},//5 + {0x07, KEY_EN_F6},//6 + {0x08, KEY_EN_F7},//7 + {0x09, KEY_EN_F8},//8 + {0x0A, KEY_EN_F9},//9 + {0x0B, KEY_EN_F10},//10 + {0x0C, KEY_EN_F11},//11 + {0x0D, KEY_EN_F12},//12 + {0x0E, KEY_EN_INSERT},//13 + {0x0F, KEY_EN_PRINT_SCREEN},//14 + {0x10, KEY_EN_DELETE},//15 + {0x11, KEY_EN_HOME},//16 + {0x12, KEY_EN_END},//17 + {0x13, KEY_EN_PAGE_UP},//18 + {0x14, KEY_EN_PAGE_DOWN},//19 + + //row 2 + {0x16, "Key: ~"},//20 + {0x17, "Key: !"},//21 + {0x18, "Key: @"},//22 + {0x19, KEY_EN_POUND},//23 + {0x1A, "Key: $"},//24 + {0x1B, "Key: %"},//25 + {0x1C, "Key: ^"},//26 + {0x1D, "Key: &"},//27 + {0x1E, "Key: *"},//28 + {0x1F, "Key: ("},//29 + {0x20, "Key: )"},//30 + {0x21, "Key: _"},//31 + {0x22, "Key: +"},//32 + {0x26, KEY_EN_NUMPAD_LOCK},//33 + {0x27, KEY_EN_NUMPAD_DIVIDE},//34 + {0x28, KEY_EN_NUMPAD_TIMES},//35 + {0x29, KEY_EN_NUMPAD_MINUS},//36 + + //row 3 + {0x2B, KEY_EN_BACK_TICK},//37 + {0x2C, KEY_EN_1},//38 + {0x2D, KEY_EN_2},//39 + {0x2E, KEY_EN_3},//40 + {0x2F, KEY_EN_4},//41 + {0x30, KEY_EN_5},//42 + {0x31, KEY_EN_6},//43 + {0x32, KEY_EN_7},//44 + {0x33, KEY_EN_8},//45 + {0x34, KEY_EN_9},//46 + {0x35, KEY_EN_0},//47 + {0x36, KEY_EN_MINUS},//48 + {0x37, KEY_EN_EQUALS},//49 + {0x38, KEY_EN_BACKSPACE},//50 + {0x39, KEY_EN_BACKSPACE},//51 + {0x3A, KEY_EN_BACKSPACE},//52 + {0x3B, KEY_EN_NUMPAD_LOCK},//53 + + //row 4 + {0x40, KEY_EN_TAB},//54 + {0x42, KEY_EN_Q},//55 + {0x43, KEY_EN_W},//56 + {0x44, KEY_EN_E},//57 + {0x45, KEY_EN_R},//58 + {0x46, KEY_EN_T},//59 + {0x47, KEY_EN_Y},//60 + {0x48, KEY_EN_U},//61 + {0x49, KEY_EN_I},//62 + {0x4A, KEY_EN_O},//63 + {0x4B, KEY_EN_P},//64 + {0x4C, "Key: {"},//65 + {0x4D, "Key: }"},//66 + {0x4E, "Key: |"},//67 + {0x4F, KEY_EN_NUMPAD_7},//68 + {0x50, KEY_EN_NUMPAD_8},//69 + {0x51, KEY_EN_NUMPAD_9},//70 + {0x67, KEY_EN_NUMPAD_PLUS},//71 + + //row 5 + {0x55, KEY_EN_CAPS_LOCK},//72 + {0x56, KEY_EN_CAPS_LOCK},//73 + {0x57, KEY_EN_CAPS_LOCK},//74 + {0x6D, KEY_EN_A},//75 + {0x6E, KEY_EN_S},//76 + {0x58, KEY_EN_D},//77 + {0x59, KEY_EN_F},//78 + {0x5A, KEY_EN_G},//79 + {0x71, KEY_EN_H},//80 + {0x72, KEY_EN_J},//81 + {0x5B, KEY_EN_K},//82 + {0x5C, KEY_EN_L},//83 + {0x5D, "Key: :"},//84 + {0x5E, KEY_EN_SEMICOLON},//85 + {0x5F, "Key: \""},//86 + {0x60, KEY_EN_QUOTE},//87 + {0x77, KEY_EN_ANSI_ENTER},//88 + {0x78, KEY_EN_ANSI_ENTER},//89 + {0x61, KEY_EN_LEFT_BRACKET},//90 + {0x62, KEY_EN_RIGHT_BRACKET},//91 + {0x63, KEY_EN_ANSI_BACK_SLASH},//92 + {0x64, "Key: Number Pad Home"},//93 + {0x65, "Key: Number Pad Up Arrow"},//94 + {0x66, "Key: Number Pad Page Up"},//95 + {0x68, KEY_EN_NUMPAD_PLUS},//96 + + //row 6 + {0x6A, KEY_EN_LEFT_SHIFT},//97 + {0x6B, KEY_EN_LEFT_SHIFT},//98 + {0x82, KEY_EN_Z},//99 + {0x83, KEY_EN_X},//100 + {0x6F, KEY_EN_C},//101 + {0x70, KEY_EN_V},//102 + {0x87, KEY_EN_B},//103 + {0x88, KEY_EN_N},//104 + {0x73, KEY_EN_M},//105 + {0x74, "Key: <"},//106 + {0x75, "Key: >"},//107 + {0x76, "Key: ?"},//108 + {0x8D, KEY_EN_RIGHT_SHIFT},//109 + {0xA2, KEY_EN_RIGHT_SHIFT},//110 + {0x79, KEY_EN_NUMPAD_4},//111 + {0x7A, "Key: Number Pad Right Arrow"},//112 + {0x7B, KEY_EN_NUMPAD_5},//113 + {0x7C, KEY_EN_NUMPAD_6},//114 + {0x7D, "Key: Number Pad Left Arrow"},//115 + + //row 7 + {0x89, KEY_EN_COMMA},//116 + {0x8A, KEY_EN_PERIOD},//117 + {0x8B, KEY_EN_FORWARD_SLASH},//118 + {0x8E, KEY_EN_NUMPAD_1},//119 + {0x8F, "Key: Number Pad End"},//120 + {0x90, KEY_EN_NUMPAD_2},//121 + {0x91, "Key: Number Pad Down Arrow"},//122 + {0x92, KEY_EN_NUMPAD_3},//123 + {0x93, "Key: Number Pad Page Down"},//124 + {0xA7, KEY_EN_NUMPAD_ENTER},//125 + + //row 8 + {0x7F, KEY_EN_LEFT_CONTROL},//126 + {0x80, KEY_EN_LEFT_FUNCTION},//127 + {0x96, KEY_EN_LEFT_WINDOWS},//128 + {0x97, KEY_EN_LEFT_ALT},//129 + {0x98, KEY_EN_SPACE},//130 + {0x99, KEY_EN_SPACE},//131 + {0x9A, KEY_EN_RIGHT_ALT},//132 + {0x9B, KEY_EN_RIGHT_CONTROL},//133 + {0x9C, KEY_EN_LEFT_ARROW},//134 + {0x9D, KEY_EN_UP_ARROW},//135 + {0x9E, "Key: Brightness +"},//136 + {0x9F, KEY_EN_DOWN_ARROW},//137 + {0xA0, "Key: Brightness -"},//138 + {0xA1, KEY_EN_RIGHT_ARROW},//139 + {0xA3, KEY_EN_NUMPAD_0},//140 + {0xA4, "Key: Number Pad Insert"},//141 + {0xA5, KEY_EN_NUMPAD_PERIOD},//142 + {0xA6, "Key: Number Pad Delete"},//143 + {0xA8, KEY_EN_NUMPAD_ENTER},//144 +}; + +/*--------------------*\ +| zone 1, keyboard ISO | +\*--------------------*/ + +const lenovo_led legion_Y760_iso_leds[] +{ + //row 1 + {0x01, KEY_EN_ESCAPE},//0 + {0x02, KEY_EN_F1},//1 + {0x03, KEY_EN_F2},//2 + {0x04, KEY_EN_F3},//3 + {0x05, KEY_EN_F4},//4 + {0x06, KEY_EN_F5},//5 + {0x07, KEY_EN_F6},//6 + {0x08, KEY_EN_F7},//7 + {0x09, KEY_EN_F8},//8 + {0x0A, KEY_EN_F9},//9 + {0x0B, KEY_EN_F10},//10 + {0x0C, KEY_EN_F11},//11 + {0x0D, KEY_EN_F12},//12 + {0x0E, KEY_EN_INSERT},//13 + {0x0F, KEY_EN_PRINT_SCREEN},//14 + {0x10, KEY_EN_DELETE},//15 + {0x11, KEY_EN_HOME},//16 + {0x12, KEY_EN_END},//17 + {0x13, KEY_EN_PAGE_UP},//18 + {0x14, KEY_EN_PAGE_DOWN},//19 + + //row 2 + {0x16, "Key: ¬"},//20 + {0x17, "Key: !"},//21 + {0x18, "Key: \""},//22 + {0x19, "Key: £"},//23 + {0x1A, "Key: $"},//24 + {0x1B, "Key: %"},//25 + {0x1C, "Key: ^"},//26 + {0x1D, "Key: &"},//27 + {0x1E, "Key: *"},//28 + {0x1F, "Key: ("},//29 + {0x20, "Key: )"},//30 + {0x21, "Key: _"},//31 + {0x22, "Key: +"},//32 + {0x26, KEY_EN_NUMPAD_LOCK},//33 + {0x27, KEY_EN_NUMPAD_DIVIDE},//34 + {0x28, KEY_EN_NUMPAD_TIMES},//35 + {0x29, KEY_EN_NUMPAD_MINUS},//36 + + //row 3 + {0x2B, KEY_EN_BACK_TICK},//37 + {0x2C, KEY_EN_1},//38 + {0x2D, KEY_EN_2},//39 + {0x2E, KEY_EN_3},//40 + {0x2F, KEY_EN_4},//41 + {0x30, KEY_EN_5},//42 + {0x31, KEY_EN_6},//43 + {0x32, KEY_EN_7},//44 + {0x33, KEY_EN_8},//45 + {0x34, KEY_EN_9},//46 + {0x35, KEY_EN_0},//47 + {0x36, KEY_EN_MINUS},//48 + {0x37, KEY_EN_EQUALS},//49 + {0x3A, KEY_EN_BACKSPACE},//50 + {0x3B, KEY_EN_NUMPAD_LOCK},//51 + + //row 4 + {0x40, KEY_EN_TAB},//52 + {0x42, KEY_EN_Q},//53 + {0x43, KEY_EN_W},//54 + {0x44, KEY_EN_E},//55 + {0x45, KEY_EN_R},//56 + {0x46, KEY_EN_T},//57 + {0x47, KEY_EN_Y},//58 + {0x48, KEY_EN_U},//59 + {0x49, KEY_EN_I},//60 + {0x4A, KEY_EN_O},//61 + {0x4B, KEY_EN_P},//62 + {0x4C, "Key: {"},//63 + {0x4D, "Key: }"},//64 + {0x4E, KEY_EN_ISO_ENTER},//65 + {0x4F, KEY_EN_NUMPAD_7},//66 + {0x50, KEY_EN_NUMPAD_8},//67 + {0x51, KEY_EN_NUMPAD_9},//68 + {0x67, KEY_EN_NUMPAD_PLUS},//69 + + //row 5 + {0x55, KEY_EN_CAPS_LOCK},//70 + {0x56, KEY_EN_CAPS_LOCK},//71 + {0x57, KEY_EN_CAPS_LOCK},//72 + {0x6D, KEY_EN_A},//73 + {0x6E, KEY_EN_S},//74 + {0x58, KEY_EN_D},//75 + {0x59, KEY_EN_F},//76 + {0x5A, KEY_EN_G},//77 + {0x71, KEY_EN_H},//78 + {0x72, KEY_EN_J},//79 + {0x5B, KEY_EN_K},//80 + {0x5C, KEY_EN_L},//81 + {0x5D, "Key: :"},//82 + {0x5E, KEY_EN_SEMICOLON},//83 + {0x5F, "Key: @"},//84 + {0x60, KEY_EN_QUOTE},//85 + {0x77, "Key: ~"},//86 + {0x78, KEY_EN_POUND},//87 + {0x61, KEY_EN_LEFT_BRACKET},//88 + {0x62, KEY_EN_RIGHT_BRACKET},//89 + {0x64, "Key: Number Pad Home"},//90 + {0x65, "Key: Number Pad Up Arrow"},//91 + {0x66, "Key: Number Pad Page Up"},//92 + {0x68, KEY_EN_NUMPAD_PLUS},//93 + + //row 6 + {0x6A, KEY_EN_LEFT_SHIFT},//94 + {0x6C, "Key: |"},//95 + {0x81, KEY_EN_ISO_BACK_SLASH},//96 + {0x82, KEY_EN_Z},//97 + {0x83, KEY_EN_X},//98 + {0x6F, KEY_EN_C},//99 + {0x70, KEY_EN_V},//100 + {0x87, KEY_EN_B},//101 + {0x88, KEY_EN_N},//102 + {0x73, KEY_EN_M},//103 + {0x74, "Key: <"},//104 + {0x75, "Key: >"},//105 + {0x76, "Key: ?"},//106 + {0x8D, KEY_EN_RIGHT_SHIFT},//107 + {0xA2, KEY_EN_RIGHT_SHIFT},//108 + {0x79, KEY_EN_NUMPAD_4},//109 + {0x7A, "Key: Number Pad Right Arrow"},//110 + {0x7B, KEY_EN_NUMPAD_5},//111 + {0x7C, KEY_EN_NUMPAD_6},//112 + {0x7D, "Key: Number Pad Left Arrow"},//113 + + //row 7 + {0x89, KEY_EN_COMMA},//114 + {0x8A, KEY_EN_PERIOD},//115 + {0x8B, KEY_EN_FORWARD_SLASH},//116 + {0x8E, KEY_EN_NUMPAD_1},//117 + {0x8F, "Key: Number Pad End"},//118 + {0x90, KEY_EN_NUMPAD_2},//119 + {0x91, "Key: Number Pad Down Arrow"},//120 + {0x92, KEY_EN_NUMPAD_3},//121 + {0x93, "Key: Number Pad Page Down"},//122 + {0xA7, KEY_EN_NUMPAD_ENTER},//123 + + //row 8 + {0x7F, KEY_EN_LEFT_CONTROL},//124 + {0x80, KEY_EN_LEFT_FUNCTION},//125 + {0x96, KEY_EN_LEFT_WINDOWS},//126 + {0x97, KEY_EN_LEFT_ALT},//127 + {0x98, KEY_EN_SPACE},//128 + {0x99, KEY_EN_SPACE},//129 + {0x9A, KEY_EN_RIGHT_ALT},//130 + {0x9B, KEY_EN_RIGHT_CONTROL},//131 + {0x9C, KEY_EN_LEFT_ARROW},//132 + {0x9D, KEY_EN_UP_ARROW},//133 + {0x9E, "Key: Brightness +"},//134 + {0x9F, KEY_EN_DOWN_ARROW},//135 + {0xA0, "Key: Brightness -"},//136 + {0xA1, KEY_EN_RIGHT_ARROW},//137 + {0xA3, KEY_EN_NUMPAD_0},//138 + {0xA4, "Key: Number Pad Insert"},//139 + {0xA5, KEY_EN_NUMPAD_PERIOD},//140 + {0xA6, "Key: Number Pad Delete"},//141 + {0xA8, KEY_EN_NUMPAD_ENTER},//142 +}; + +/*----------------------*\ +| zone 1, keyboard Japan | +\*----------------------*/ + +const lenovo_led legion_Y760_jp_leds[] +{ + //row 1 + {0x01, KEY_EN_ESCAPE}, //0 + {0x02, KEY_EN_F1}, //1 + {0x03, KEY_EN_F2}, //2 + {0x04, KEY_EN_F3}, //3 + {0x05, KEY_EN_F4}, //4 + {0x06, KEY_EN_F5}, //5 + {0x07, KEY_EN_F6}, //6 + {0x08, KEY_EN_F7}, //7 + {0x09, KEY_EN_F8}, //8 + {0x0A, KEY_EN_F9}, //9 + {0x0B, KEY_EN_F10}, //10 + {0x0C, KEY_EN_F11}, //11 + {0x0D, KEY_EN_F12}, //12 + {0x0E, KEY_EN_INSERT}, //13 + {0x0F, KEY_EN_PRINT_SCREEN}, //14 + {0x10, KEY_EN_DELETE}, //15 + {0x11, KEY_EN_HOME}, //16 + {0x12, KEY_EN_END}, //17 + {0x13, KEY_EN_PAGE_UP}, //18 + {0x14, KEY_EN_PAGE_DOWN}, //19 + + //row 2 + {0x16, KEY_JP_ZENKAKU}, //20 + {0x17, "Key: !"}, //21 + {0x18, "Key: \""}, //22 + {0x19, KEY_EN_POUND}, //23 + {0x1A, "Key: $"}, //24 + {0x1B, "Key: %"}, //25 + {0x1C, "Key: &"}, //26 + {0x1D, "Key: '"}, //27 + {0x1E, "Key: ("}, //28 + {0x1F, "Key: )"}, //29 + {0x20, "Key: wo"}, //30 + {0x21, KEY_EN_EQUALS}, //31 + {0x22, "Key: ~"}, //32 + {0x23, "Key: |"}, //33 + {0x26, KEY_EN_NUMPAD_LOCK}, //34 + {0x27, KEY_EN_NUMPAD_DIVIDE}, //35 + {0x28, KEY_EN_NUMPAD_TIMES}, //36 + {0x29, KEY_EN_NUMPAD_MINUS}, //37 + + //row 3 + {0x2B, "Key: kanji"}, //38 + {0x2C, KEY_EN_1}, //39 + {0x2D, KEY_EN_2}, //40 + {0x2E, KEY_EN_3}, //41 + {0x2F, KEY_EN_4}, //42 + {0x30, KEY_EN_5}, //43 + {0x31, KEY_EN_6}, //44 + {0x32, KEY_EN_7}, //45 + {0x33, KEY_EN_8}, //46 + {0x34, KEY_EN_9}, //47 + {0x35, KEY_EN_0}, //48 + {0x36, KEY_EN_MINUS}, //49 + {0x37, KEY_JP_CHEVRON}, //50 + {0x38, KEY_JP_YEN}, //51 + {0x25, KEY_EN_BACKSPACE}, //52 + {0x3A, KEY_EN_BACKSPACE}, //53 + {0x3B, KEY_EN_NUMPAD_LOCK}, //54 + + //row 4 + {0x40, KEY_EN_TAB}, //55 + {0x42, KEY_EN_Q}, //56 + {0x43, KEY_EN_W}, //57 + {0x44, KEY_EN_E}, //58 + {0x45, KEY_EN_R}, //59 + {0x46, KEY_EN_T}, //60 + {0x47, KEY_EN_Y}, //61 + {0x48, KEY_EN_U}, //62 + {0x49, KEY_EN_I}, //63 + {0x4A, KEY_EN_O}, //64 + {0x4B, KEY_EN_P}, //65 + {0x4C, KEY_EN_BACK_TICK}, //66 + {0x61, KEY_JP_AT}, //67 + {0x4D, "Key: {"}, //68 + {0x62, KEY_EN_LEFT_BRACKET}, //69 + {0x4E, KEY_EN_ISO_ENTER}, //70 + {0x4F, KEY_EN_NUMPAD_7}, //71 + {0x50, KEY_EN_NUMPAD_8}, //72 + {0x51, KEY_EN_NUMPAD_9}, //73 + {0x67, KEY_EN_NUMPAD_PLUS}, //74 + + //row 5 + {0x55, KEY_EN_CAPS_LOCK}, //75 + {0x56, KEY_EN_CAPS_LOCK}, //76 + {0x57, KEY_EN_CAPS_LOCK}, //77 + {0x6D, KEY_EN_A}, //78 + {0x6E, KEY_EN_S}, //79 + {0x58, KEY_EN_D}, //80 + {0x59, KEY_EN_F}, //81 + {0x5A, KEY_EN_G}, //82 + {0x71, KEY_EN_H}, //83 + {0x72, KEY_EN_J}, //84 + {0x5B, KEY_EN_K}, //85 + {0x5C, KEY_EN_L}, //86 + {0x5D, "Key: +"}, //87 + {0x5E, KEY_EN_SEMICOLON}, //88 + {0x5F, "Key: *"}, //89 + {0x60, KEY_JP_COLON}, //90 + {0x77, "Key: }"}, //91 + {0x78, KEY_EN_RIGHT_BRACKET}, //92 + {0x63, KEY_EN_ISO_ENTER}, //93 + {0x64, "Key: Number Pad Home"}, //94 + {0x65, "Key: Number Pad Up Arrow"}, //95 + {0x66, "Key: Number Pad Page Up"}, //96 + {0x68, KEY_EN_NUMPAD_PLUS}, //97 + + //row 6 + {0x6A, KEY_EN_LEFT_SHIFT}, //98 + {0x6B, KEY_EN_LEFT_SHIFT}, //99 + {0x82, KEY_EN_Z}, //100 + {0x83, KEY_EN_X}, //101 + {0x6F, KEY_EN_C}, //102 + {0x70, KEY_EN_V}, //103 + {0x87, KEY_EN_B}, //104 + {0x88, KEY_EN_N}, //105 + {0x73, KEY_EN_M}, //106 + {0x74, "Key: <"}, //107 + {0x89, KEY_EN_COMMA}, //108 + {0x75, "Key: >"}, //109 + {0x8A, KEY_EN_PERIOD}, //110 + {0x76, "Key: ?"}, //111 + {0x8B, KEY_EN_FORWARD_SLASH}, //112 + {0x8C, KEY_JP_RO}, //113 + {0x8D, KEY_EN_RIGHT_SHIFT}, //114 + {0xA2, KEY_EN_RIGHT_SHIFT}, //115 + {0x79, KEY_EN_NUMPAD_4}, //116 + {0x7A, "Key: Number Pad Right Arrow"}, //117 + {0x7B, KEY_EN_NUMPAD_5}, //118 + {0x7C, KEY_EN_NUMPAD_6}, //119 + {0x7D, "Key: Number Pad Left Arrow"}, //120 + + //row 7 + {0x8E, KEY_EN_NUMPAD_1}, //121 + {0x8F, "Key: Number Pad End"}, //122 + {0x90, KEY_EN_NUMPAD_2}, //123 + {0x91, "Key: Number Pad Down Arrow"}, //124 + {0x92, KEY_EN_NUMPAD_3}, //125 + {0x93, "Key: Number Pad Page Down"}, //126 + {0xA7, KEY_EN_NUMPAD_ENTER}, //127 + + //row 8 + {0x7F, KEY_EN_LEFT_CONTROL}, //128 + {0x80, KEY_EN_LEFT_FUNCTION}, //129 + {0x96, KEY_EN_LEFT_WINDOWS}, //130 + {0x97, KEY_EN_LEFT_ALT}, //131 + {0x98, KEY_JP_MUHENKAN}, //132 + {0x85, KEY_EN_SPACE}, //133 + {0x86, KEY_EN_SPACE}, //134 + {0x99, KEY_JP_HENKAN}, //135 + {0x9A, KEY_JP_KANA}, //136 + {0x9B, KEY_EN_RIGHT_CONTROL}, //137 + {0x9C, KEY_EN_LEFT_ARROW}, //138 + {0x9D, KEY_EN_UP_ARROW}, //139 + {0x9E, "Key: Brightness +"}, //140 + {0x9F, KEY_EN_DOWN_ARROW}, //141 + {0xA0, "Key: Brightness -"}, //142 + {0xA1, KEY_EN_RIGHT_ARROW}, //143 + {0xA3, KEY_EN_NUMPAD_0}, //144 + {0xA4, "Key: Number Pad Insert"}, //145 + {0xA5, KEY_EN_NUMPAD_PERIOD}, //146 + {0xA6, "Key: Number Pad Delete"}, //147 + {0xA8, KEY_EN_NUMPAD_ENTER}, //148 +}; + + /*----------------*\ + | zone 2, logo | + \*----------------*/ +const lenovo_led legion_Y760_logo[] +{ + {0x01, "Logo Bottom Left"},//0 + {0x02, "Logo LED 2"}, + {0x03, "Logo LED 3"}, + {0x04, "Logo LED 4"}, + {0x05, "Logo LED Top Left"}, + {0x06, "Logo LED 6"}, + {0x07, "Logo LED 7"}, + {0x08, "Logo LED 8"}, + {0x09, "Logo LED Top Right"}, + {0x0A, "Logo LED 10"}, + {0x0B, "Logo LED 11"}, + {0x0C, "Logo LED 12"}, + {0x0D, "Logo Bottom Right"},//12 +}; + + /*----------------*\ + | zone 3, vents | + \*----------------*/ +const lenovo_led legion_Y760_vents[] +{ + //left + {0x01, "Left Vent Front"},//0 + {0x02, "Left Vent LED 2"}, + {0x03, "Left Vent LED 3"}, + {0x04, "Left Vent LED 4"}, + {0x05, "Left Vent LED 5"}, + {0x06, "Left Vent LED 6"}, + {0x07, "Left Vent LED 7"}, + {0x08, "Left Vent LED 8"}, + {0x09, "Left Vent LED 9"}, + {0x0A, "Left Vent LED 10"}, + {0x0B, "Left Vent LED 11"}, + {0x0C, "Left Vent LED 12"}, + {0x0D, "Left Vent LED 13"}, + {0x0E, "Left Vent LED 14"}, + {0x0F, "Left Vent LED 15"}, + {0x10, "Left Vent LED 16"}, + {0x11, "Left Vent LED 17"}, + {0x12, "Left Vent LED 18"}, + {0x13, "Left Vent LED 19"}, + {0x14, "Left Vent LED 20"}, + {0x15, "Left Vent LED 21"}, + {0x16, "Left Vent LED 22"}, + {0x17, "Left Vent LED 23"}, + {0x18, "Left Vent LED 24"}, + {0x19, "Left Vent LED 25"}, + {0x1A, "Left Vent LED 26"}, + {0x1B, "Left Vent Back"},//26 + + //right + {0x1C, "Right Vent Front"},//27 + {0x1D, "Right Vent LED 2"}, + {0x1E, "Right Vent LED 3"}, + {0x1F, "Right Vent LED 4"}, + {0x20, "Right Vent LED 5"}, + {0x21, "Right Vent LED 6"}, + {0x22, "Right Vent LED 7"}, + {0x23, "Right Vent LED 8"}, + {0x24, "Right Vent LED 9"}, + {0x25, "Right Vent LED 10"}, + {0x26, "Right Vent LED 11"}, + {0x27, "Right Vent LED 12"}, + {0x28, "Right Vent LED 13"}, + {0x29, "Right Vent LED 14"}, + {0x2A, "Right Vent LED 15"}, + {0x2B, "Right Vent LED 16"}, + {0x2C, "Right Vent LED 17"}, + {0x2D, "Right Vent LED 18"}, + {0x2E, "Right Vent LED 19"}, + {0x2F, "Right Vent LED 20"}, + {0x30, "Right Vent LED 21"}, + {0x31, "Right Vent LED 22"}, + {0x32, "Right Vent LED 23"}, + {0x33, "Right Vent LED 24"}, + {0x34, "Right Vent LED 25"}, + {0x35, "Right Vent LED 26"}, + {0x36, "Right Vent Back"},//53 + + //back right vent + {0x37, "Back Right Vent Left"},//54 + {0x38, "Back Right Vent 2"}, + {0x39, "Back Right Vent 3"}, + {0x3A, "Back Right Vent 4"}, + {0x3B, "Back Right Vent 5"}, + {0x3C, "Back Right Vent 6"}, + {0x3D, "Back Right Vent 7"}, + {0x3E, "Back Right Vent 8"}, + {0x3F, "Back Right Vent 10"}, + {0x40, "Back Right Vent 11"}, + {0x41, "Back Right Vent 12"}, + {0x42, "Back Right Vent 13"}, + {0x43, "Back Right Vent 14"}, + {0x44, "Back Right Vent 15"}, + {0x45, "Back Right Vent 16"}, + {0x46, "Back Right Vent 17"}, + {0x47, "Back Right Vent 18"}, + {0x48, "Back Right Vent 19"}, + {0x49, "Back Right Vent 20"}, + {0x4A, "Back Right Vent 21"}, + {0x4B, "Back Right Vent 22"}, + {0x4C, "Back Right Vent 23"}, + {0x4D, "Back Right Vent 24"}, + {0x4E, "Back Right Vent 25"}, + {0x4F, "Back Right Vent 26"}, + {0x50, "Back Right Vent Right"},//79 + + //back left vent + {0x51, "Back Left Vent Right"},//80 + {0x52, "Back Left Vent 2"}, + {0x53, "Back Left Vent 3"}, + {0x54, "Back Left Vent 4"}, + {0x55, "Back Left Vent 5"}, + {0x56, "Back Left Vent 6"}, + {0x57, "Back Left Vent 7"}, + {0x58, "Back Left Vent 8"}, + {0x59, "Back Left Vent 10"}, + {0x5A, "Back Left Vent 11"}, + {0x5B, "Back Left Vent 12"}, + {0x5C, "Back Left Vent 13"}, + {0x5D, "Back Left Vent 14"}, + {0x5E, "Back Left Vent 15"}, + {0x5F, "Back Left Vent 16"}, + {0x60, "Back Left Vent 17"}, + {0x61, "Back Left Vent 18"}, + {0x62, "Back Left Vent 19"}, + {0x63, "Back Left Vent 20"}, + {0x64, "Back Left Vent 21"}, + {0x65, "Back Left Vent 22"}, + {0x66, "Back Left Vent 23"}, + {0x67, "Back Left Vent 24"}, + {0x68, "Back Left Vent 25"}, + {0x69, "Back Left Vent 26"}, + {0x6A, "Back Left Vent Left"},//105 +}; + /*-----------------*\ + | zone 4, neon | + \*-----------------*/ +const lenovo_led legion_Y760_neon[] +{ + //left side + {0x01, "Neon LED 1 Top Left"},//0 + {0x02, "Neon LED 2"}, + {0x03, "Neon LED 3"}, + {0x04, "Neon LED 4"}, + {0x05, "Neon LED 5"}, + {0x06, "Neon LED 6"}, + {0x07, "Neon LED 7"}, + {0x08, "Neon LED 8"}, + {0x09, "Neon LED 9"}, + {0x0A, "Neon LED 10"}, + {0x0B, "Neon LED 11"}, + {0x0C, "Neon LED 12"}, + {0x0D, "Neon LED 13"}, + {0x0E, "Neon LED 14"}, + {0x0F, "Neon LED 15"}, + {0x10, "Neon LED 16"}, + {0x11, "Neon LED 17"}, + {0x12, "Neon LED 18"}, + {0x13, "Neon LED 19"}, + {0x14, "Neon LED 20"}, + {0x15, "Neon LED 21 Left Corner"},//20 + + //front + {0x16, "Neon LED 22 Left Corner"},//21 + {0x17, "Neon LED 23"}, + {0x18, "Neon LED 24"}, + {0x19, "Neon LED 25"}, + {0x1A, "Neon LED 26"}, + {0x1B, "Neon LED 27"}, + {0x1C, "Neon LED 28"}, + {0x1D, "Neon LED 29"}, + {0x1E, "Neon LED 30"}, + {0x1F, "Neon LED 31"}, + {0x20, "Neon LED 32"}, + {0x21, "Neon LED 33"}, + {0x22, "Neon LED 34"}, + {0x23, "Neon LED 35"}, + {0x24, "Neon LED 36"}, + {0x25, "Neon LED 37"}, + {0x26, "Neon LED 38"}, + {0x27, "Neon LED 39"}, + {0x28, "Neon LED 40"}, + {0x29, "Neon LED 41"}, + {0x2A, "Neon LED 42"}, + {0x2B, "Neon LED 43"}, + {0x2C, "Neon LED 44"}, + {0x2D, "Neon LED 45"}, + {0x2E, "Neon LED 46"}, + {0x2F, "Neon LED 47"}, + {0x30, "Neon LED 48"}, + {0x31, "Neon LED 49"}, + {0x32, "Neon LED 50"}, + {0x33, "Neon LED 51"}, + {0x34, "Neon LED 52"}, + {0x35, "Neon LED 53"}, + {0x36, "Neon LED 54"}, + {0x37, "Neon LED 55"}, + {0x38, "Neon LED 56"}, + {0x39, "Neon LED 57"}, + {0x3A, "Neon LED 58"}, + {0x3B, "Neon LED 59"}, + {0x3C, "Neon LED 60"}, + {0x3D, "Neon LED 61"}, + {0x3E, "Neon LED 62"}, + {0x3F, "Neon LED 63"}, + {0x40, "Neon LED 64"}, + {0x41, "Neon LED 65"}, + {0x42, "Neon LED 66"}, + {0x43, "Neon LED 67"}, + {0x44, "Neon LED 68"}, + {0x45, "Neon LED 69"}, + {0x46, "Neon LED 70"}, + {0x47, "Neon LED 71"}, + {0x48, "Neon LED 72"}, + {0x49, "Neon LED 73"}, + {0x4A, "Neon LED 74"}, + {0x4B, "Neon LED 75"}, + {0x4C, "Neon LED 76"}, + {0x4D, "Neon LED 77"}, + {0x4E, "Neon LED 78 Right Corner"},//77 + + //right side + {0x4F, "Neon LED 79 Right Corner"},//78 + {0x50, "Neon LED 80"}, + {0x51, "Neon LED 81"}, + {0x52, "Neon LED 82"}, + {0x53, "Neon LED 83"}, + {0x54, "Neon LED 84"}, + {0x55, "Neon LED 85"}, + {0x56, "Neon LED 86"}, + {0x57, "Neon LED 87"}, + {0x58, "Neon LED 88"}, + {0x59, "Neon LED 89"}, + {0x5A, "Neon LED 90"}, + {0x5B, "Neon LED 91"}, + {0x5C, "Neon LED 92"}, + {0x5D, "Neon LED 93"}, + {0x5E, "Neon LED 94"}, + {0x5F, "Neon LED 95"}, + {0x60, "Neon LED 96"}, + {0x61, "Neon LED 97"}, + {0x62, "Neon LED 98"}, + {0x63, "Neon LED 99 Top Right"},//98 +}; + + +/*--------------------------------------------------------*\ +| Additional LEDs for Legion Y750, Y750S and Y760S | +\*--------------------------------------------------------*/ +const lenovo_led legion_legion_Y750_additional_leds[] +{ + {0xAA, "Logo"}, + {0xAD, "Vents"}, + {0xAC, "Neon"} +}; + +/*--------------------------------------------------------*\ +| LEDs for Legion Y740 17" | +\*--------------------------------------------------------*/ + +const lenovo_led legion_Y740_17_ansi_leds[] +{ + //Row 1 + {0x6E, KEY_EN_ESCAPE}, //0 + {0x70, KEY_EN_F1}, //1 + {0x71, KEY_EN_F2}, //2 + {0x72, KEY_EN_F3}, //3 + {0x73, KEY_EN_F4}, //4 + {0x74, KEY_EN_F5}, //5 + {0x75, KEY_EN_F6}, //6 + {0x76, KEY_EN_F7}, //7 + {0x77, KEY_EN_F8}, //8 + {0x78, KEY_EN_F9}, //9 + {0x79, KEY_EN_F10}, //10 + {0x7A, KEY_EN_F11}, //11 + {0x7B, KEY_EN_F12}, //12 + {0x7D, KEY_EN_DELETE}, //13 + {0x5B, KEY_EN_NUMPAD_7}, //14 + {0x60, KEY_EN_NUMPAD_8}, //15 + {0x65, KEY_EN_NUMPAD_9}, //16 + {0x5F, KEY_EN_NUMPAD_DIVIDE}, //17 + + //Row 2 + {0x01, KEY_EN_BACK_TICK}, //18 + {0x02, KEY_EN_1}, //19 + {0x03, KEY_EN_2}, //20 + {0x04, KEY_EN_3}, //21 + {0x05, KEY_EN_4}, //22 + {0x06, KEY_EN_5}, //23 + {0x07, KEY_EN_6}, //24 + {0x08, KEY_EN_7}, //25 + {0x09, KEY_EN_8}, //26 + {0x0A, KEY_EN_9}, //27 + {0x0B, KEY_EN_0}, //28 + {0x0C, KEY_EN_MINUS}, //29 + {0x0D, KEY_EN_EQUALS}, //30 + {0x0F, KEY_EN_BACKSPACE}, //31 left led + {0x8C, KEY_EN_BACKSPACE}, //32 right led + {0x5C, KEY_EN_NUMPAD_4}, //33 + {0x61, KEY_EN_NUMPAD_5}, //34 + {0x66, KEY_EN_NUMPAD_6}, //35 + {0x64, KEY_EN_NUMPAD_TIMES}, //36 + + //Row 3 + {0x10, KEY_EN_TAB}, //37 + {0x11, KEY_EN_Q}, //38 + {0x12, KEY_EN_W}, //39 + {0x13, KEY_EN_E}, //40 + {0x14, KEY_EN_R}, //41 + {0x15, KEY_EN_T}, //42 + {0x16, KEY_EN_Y}, //43 + {0x17, KEY_EN_U}, //44 + {0x18, KEY_EN_I}, //45 + {0x19, KEY_EN_O}, //46 + {0x1A, KEY_EN_P}, //47 + {0x1B, KEY_EN_LEFT_BRACKET}, //48 + {0x1C, KEY_EN_RIGHT_BRACKET}, //49 + {0x1D, KEY_EN_ANSI_BACK_SLASH}, //50 + {0x5D, KEY_EN_NUMPAD_1}, //51 + {0x62, KEY_EN_NUMPAD_2}, //52 + {0x67, KEY_EN_NUMPAD_3}, //53 + {0x69, KEY_EN_NUMPAD_MINUS}, //54 + + //Row 4 + {0x1E, KEY_EN_CAPS_LOCK}, //55 left led + {0x8D, KEY_EN_CAPS_LOCK}, //56 right led + {0x1F, KEY_EN_A}, //57 + {0x20, KEY_EN_S}, //58 + {0x21, KEY_EN_D}, //59 + {0x22, KEY_EN_F}, //60 + {0x23, KEY_EN_G}, //61 + {0x24, KEY_EN_H}, //62 + {0x25, KEY_EN_J}, //63 + {0x26, KEY_EN_K}, //64 + {0x27, KEY_EN_L}, //65 + {0x28, KEY_EN_SEMICOLON}, //66 + {0x29, KEY_EN_QUOTE}, //67 + {0x2B, KEY_EN_ANSI_ENTER}, //68 left led + {0x8F, KEY_EN_ANSI_ENTER}, //69 right led + {0x5A, KEY_EN_NUMPAD_LOCK}, //70 top led + {0x63, KEY_EN_NUMPAD_0}, //71 + {0x68, KEY_EN_NUMPAD_PERIOD}, //72 + {0x6A, KEY_EN_NUMPAD_PLUS}, //73 + + //Row 5 + {0x2C, KEY_EN_LEFT_SHIFT}, //74 + {0x8E, KEY_EN_LEFT_SHIFT}, //75 + {0x2E, KEY_EN_Z}, //76 + {0x2F, KEY_EN_X}, //77 + {0x30, KEY_EN_C}, //78 + {0x31, KEY_EN_V}, //79 + {0x32, KEY_EN_B}, //80 + {0x33, KEY_EN_N}, //81 + {0x34, KEY_EN_M}, //82 + {0x35, KEY_EN_COMMA}, //83 + {0x36, KEY_EN_PERIOD}, //84 + {0x37, KEY_EN_FORWARD_SLASH}, //85 + {0x39, KEY_EN_RIGHT_SHIFT}, //86 left led + {0x90, KEY_EN_RIGHT_SHIFT}, //87 right led + {0x53, KEY_EN_UP_ARROW}, //88 + {0x93, KEY_EN_NUMPAD_LOCK}, //89 bottom led + + //Row 6 + {0x3A, KEY_EN_LEFT_CONTROL}, //90 + {0x3B, KEY_EN_LEFT_FUNCTION}, //91 + {0x7F, KEY_EN_LEFT_WINDOWS}, //92 + {0x3C, KEY_EN_LEFT_ALT}, //93 + {0x3D, KEY_EN_SPACE}, //94 left led + {0x91, KEY_EN_SPACE}, //95 right led + {0x3E, KEY_EN_RIGHT_ALT}, //96 + {0x40, KEY_EN_PRINT_SCREEN}, //97 left led + {0x92, KEY_EN_PRINT_SCREEN}, //98 left led + {0x86, "Record"}, //99 + {0x81, KEY_EN_RIGHT_CONTROL}, //100 + {0x4F, KEY_EN_LEFT_ARROW}, //101 + {0x54, KEY_EN_DOWN_ARROW}, //102 + {0x59, KEY_EN_RIGHT_ARROW}, //103 +}; + +const lenovo_led legion_Y740_17_iso_leds[] +{ + //Row 1 + {0x6E, KEY_EN_ESCAPE}, //0 + {0x70, KEY_EN_F1}, //1 + {0x71, KEY_EN_F2}, //2 + {0x72, KEY_EN_F3}, //3 + {0x73, KEY_EN_F4}, //4 + {0x74, KEY_EN_F5}, //5 + {0x75, KEY_EN_F6}, //6 + {0x76, KEY_EN_F7}, //7 + {0x77, KEY_EN_F8}, //8 + {0x78, KEY_EN_F9}, //9 + {0x79, KEY_EN_F10}, //10 + {0x7A, KEY_EN_F11}, //11 + {0x7B, KEY_EN_F12}, //12 + {0x7D, KEY_EN_DELETE}, //13 + {0x5B, KEY_EN_NUMPAD_7}, //14 + {0x60, KEY_EN_NUMPAD_8}, //15 + {0x65, KEY_EN_NUMPAD_9}, //16 + {0x5F, KEY_EN_NUMPAD_DIVIDE}, //17 + + //Row 2 + {0x01, KEY_EN_BACK_TICK}, //18 + {0x02, KEY_EN_1}, //19 + {0x03, KEY_EN_2}, //20 + {0x04, KEY_EN_3}, //21 + {0x05, KEY_EN_4}, //22 + {0x06, KEY_EN_5}, //23 + {0x07, KEY_EN_6}, //24 + {0x08, KEY_EN_7}, //25 + {0x09, KEY_EN_8}, //26 + {0x0A, KEY_EN_9}, //27 + {0x0B, KEY_EN_0}, //28 + {0x0C, KEY_EN_MINUS}, //29 + {0x0D, KEY_EN_EQUALS}, //30 + {0x0F, KEY_EN_BACKSPACE}, //31 left led + {0x8C, KEY_EN_BACKSPACE}, //32 right led + {0x5C, KEY_EN_NUMPAD_4}, //33 + {0x61, KEY_EN_NUMPAD_5}, //34 + {0x66, KEY_EN_NUMPAD_6}, //35 + {0x64, KEY_EN_NUMPAD_TIMES}, //36 + + //Row 3 + {0x10, KEY_EN_TAB}, //37 + {0x11, KEY_EN_Q}, //38 + {0x12, KEY_EN_W}, //39 + {0x13, KEY_EN_E}, //40 + {0x14, KEY_EN_R}, //41 + {0x15, KEY_EN_T}, //42 + {0x16, KEY_EN_Y}, //43 + {0x17, KEY_EN_U}, //44 + {0x18, KEY_EN_I}, //45 + {0x19, KEY_EN_O}, //46 + {0x1A, KEY_EN_P}, //47 + {0x1B, KEY_EN_LEFT_BRACKET}, //48 + {0x1C, KEY_EN_RIGHT_BRACKET}, //49 + {0x94, KEY_EN_ISO_ENTER}, //50 + {0x5D, KEY_EN_NUMPAD_1}, //51 + {0x62, KEY_EN_NUMPAD_2}, //52 + {0x67, KEY_EN_NUMPAD_3}, //53 + {0x69, KEY_EN_NUMPAD_MINUS}, //54 + + //Row 4 + {0x1E, KEY_EN_CAPS_LOCK}, //55 left led + {0x8D, KEY_EN_CAPS_LOCK}, //56 right led + {0x1F, KEY_EN_A}, //57 + {0x20, KEY_EN_S}, //58 + {0x21, KEY_EN_D}, //59 + {0x22, KEY_EN_F}, //60 + {0x23, KEY_EN_G}, //61 + {0x24, KEY_EN_H}, //62 + {0x25, KEY_EN_J}, //63 + {0x26, KEY_EN_K}, //64 + {0x27, KEY_EN_L}, //65 + {0x28, KEY_EN_SEMICOLON}, //66 + {0x29, KEY_EN_QUOTE}, //67 + {0x2B, KEY_EN_POUND}, //68 + {0x5A, KEY_EN_NUMPAD_LOCK}, //69 top led + {0x63, KEY_EN_NUMPAD_0}, //70 + {0x68, KEY_EN_NUMPAD_PERIOD}, //71 + {0x6A, KEY_EN_NUMPAD_PLUS}, //72 + + //Row 5 + {0x2C, KEY_EN_LEFT_SHIFT}, //73 + {0x2D, KEY_EN_ISO_BACK_SLASH}, //74 + {0x2E, KEY_EN_Z}, //75 + {0x2F, KEY_EN_X}, //76 + {0x30, KEY_EN_C}, //77 + {0x31, KEY_EN_V}, //78 + {0x32, KEY_EN_B}, //79 + {0x33, KEY_EN_N}, //80 + {0x34, KEY_EN_M}, //81 + {0x35, KEY_EN_COMMA}, //82 + {0x36, KEY_EN_PERIOD}, //83 + {0x37, KEY_EN_FORWARD_SLASH}, //84 + {0x39, KEY_EN_RIGHT_SHIFT}, //85 left led + {0x90, KEY_EN_RIGHT_SHIFT}, //86 right led + {0x53, KEY_EN_UP_ARROW}, //87 + {0x93, KEY_EN_NUMPAD_LOCK}, //88 bottom led + + //Row 6 + {0x3A, KEY_EN_LEFT_CONTROL}, //89 + {0x3B, KEY_EN_LEFT_FUNCTION}, //90 + {0x7F, KEY_EN_LEFT_WINDOWS}, //91 + {0x3C, KEY_EN_LEFT_ALT}, //92 + {0x3D, KEY_EN_SPACE}, //93 left led + {0x91, KEY_EN_SPACE}, //94 right led + {0x3E, KEY_EN_RIGHT_ALT}, //95 + {0x40, KEY_EN_PRINT_SCREEN}, //96 left led + {0x92, KEY_EN_PRINT_SCREEN}, //97 left led + {0x86, "Record"}, //98 + {0x81, KEY_EN_RIGHT_CONTROL}, //99 + {0x4F, KEY_EN_LEFT_ARROW}, //100 + {0x54, KEY_EN_DOWN_ARROW}, //101 + {0x59, KEY_EN_RIGHT_ARROW}, //102 +}; + +/*--------------------------------------------------------*\ +| LEDs for Legion Y740 15" | +\*--------------------------------------------------------*/ + +const lenovo_led legion_Y740_15_ansi_leds[] +{ + {0x6E, KEY_EN_ESCAPE}, //0 + {0x70, KEY_EN_F1}, //1 + {0x71, KEY_EN_F2}, //2 + {0x72, KEY_EN_F3}, //3 + {0x73, KEY_EN_F4}, //4 + {0x74, KEY_EN_F5}, //5 + {0x75, KEY_EN_F6}, //6 + {0x76, KEY_EN_F7}, //7 + {0x77, KEY_EN_F8}, //8 + {0x78, KEY_EN_F9}, //9 + {0x79, KEY_EN_F10}, //10 + {0x7A, KEY_EN_F11}, //11 + {0x7B, KEY_EN_F12}, //12 + {0x4C, KEY_EN_DELETE}, //13 + + //Row 2 + {0x01, KEY_EN_BACK_TICK}, //14 + {0x02, KEY_EN_1}, //15 + {0x03, KEY_EN_2}, //16 + {0x04, KEY_EN_3}, //17 + {0x05, KEY_EN_4}, //18 + {0x06, KEY_EN_5}, //19 + {0x07, KEY_EN_6}, //20 + {0x08, KEY_EN_7}, //21 + {0x09, KEY_EN_8}, //22 + {0x0A, KEY_EN_9}, //23 + {0x0B, KEY_EN_0}, //24 + {0x0C, KEY_EN_MINUS}, //25 + {0x0D, KEY_EN_EQUALS}, //26 + {0x0F, KEY_EN_BACKSPACE}, //27 + {0x8C, KEY_EN_BACKSPACE}, //28 + + //Row 3 + {0x10, KEY_EN_TAB}, //29 + {0x11, KEY_EN_Q}, //30 + {0x12, KEY_EN_W}, //31 + {0x13, KEY_EN_E}, //32 + {0x14, KEY_EN_R}, //33 + {0x15, KEY_EN_T}, //34 + {0x16, KEY_EN_Y}, //35 + {0x17, KEY_EN_U}, //36 + {0x18, KEY_EN_I}, //37 + {0x19, KEY_EN_O}, //38 + {0x1A, KEY_EN_P}, //39 + {0x1B, KEY_EN_LEFT_BRACKET}, //40 + {0x1C, KEY_EN_RIGHT_BRACKET}, //41 + {0x1D, KEY_EN_ANSI_BACK_SLASH}, //42 + + //Row 4 + {0x1E, KEY_EN_CAPS_LOCK}, //43 + {0x8D, KEY_EN_CAPS_LOCK}, //44 + {0x1F, KEY_EN_A}, //45 + {0x20, KEY_EN_S}, //46 + {0x21, KEY_EN_D}, //47 + {0x22, KEY_EN_F}, //48 + {0x23, KEY_EN_G}, //49 + {0x24, KEY_EN_H}, //50 + {0x25, KEY_EN_J}, //51 + {0x26, KEY_EN_K}, //52 + {0x27, KEY_EN_L}, //53 + {0x28, KEY_EN_SEMICOLON}, //54 + {0x29, KEY_EN_QUOTE}, //55 + {0x2B, KEY_EN_ANSI_ENTER}, //56 + {0x8F, KEY_EN_ANSI_ENTER}, //57 + + //Row 5 + {0x2C, KEY_EN_LEFT_SHIFT}, //58 + {0x8E, KEY_EN_LEFT_SHIFT}, //59 + {0x2E, KEY_EN_Z}, //60 + {0x2F, KEY_EN_X}, //61 + {0x30, KEY_EN_C}, //62 + {0x31, KEY_EN_V}, //63 + {0x32, KEY_EN_B}, //64 + {0x33, KEY_EN_N}, //65 + {0x34, KEY_EN_M}, //66 + {0x35, KEY_EN_COMMA}, //67 + {0x36, KEY_EN_PERIOD}, //68 + {0x37, KEY_EN_FORWARD_SLASH}, //69 + {0x39, KEY_EN_RIGHT_SHIFT}, //70 + {0x90, KEY_EN_RIGHT_SHIFT}, //71 + + //Row 6 + {0x3A, KEY_EN_LEFT_CONTROL}, //72 + {0x3B, KEY_EN_LEFT_FUNCTION}, //73 + {0x7F, KEY_EN_LEFT_WINDOWS}, //74 + {0x3C, KEY_EN_LEFT_ALT}, //75 + {0x3D, KEY_EN_SPACE}, //76 + {0x91, KEY_EN_SPACE}, //77 + {0x3E, KEY_EN_RIGHT_ALT}, //78 + {0x40, KEY_EN_RIGHT_CONTROL}, //79 + {0x53, KEY_EN_UP_ARROW}, //80 + + //Row 7 + {0x4F, KEY_EN_LEFT_ARROW}, //81 + {0x54, KEY_EN_DOWN_ARROW}, //82 + {0x59, KEY_EN_RIGHT_ARROW}, //83 + + //Left Column + {0xC1, "Key: Vantage"}, //84 + {0xC2, "Key: Capture"}, //85 + {0xC3, "Key: M1"}, //86 + {0xC4, "Key: M2"}, //87 + {0xC5, "Key: Kb Brightness Up"}, //88 + {0xC6, "Key: Kb Brightness Down"}, //89 +}; + +const lenovo_led legion_Y740_15_iso_leds[] +{ + {0x6E, KEY_EN_ESCAPE}, //0 + {0x70, KEY_EN_F1}, //1 + {0x71, KEY_EN_F2}, //2 + {0x72, KEY_EN_F3}, //3 + {0x73, KEY_EN_F4}, //4 + {0x74, KEY_EN_F5}, //5 + {0x75, KEY_EN_F6}, //6 + {0x76, KEY_EN_F7}, //7 + {0x77, KEY_EN_F8}, //8 + {0x78, KEY_EN_F9}, //9 + {0x79, KEY_EN_F10}, //10 + {0x7A, KEY_EN_F11}, //11 + {0x7B, KEY_EN_F12}, //12 + {0x7D, KEY_EN_DELETE}, //13 + + //Row 2 + {0x01, KEY_EN_BACK_TICK}, //14 + {0x02, KEY_EN_1}, //15 + {0x03, KEY_EN_2}, //16 + {0x04, KEY_EN_3}, //17 + {0x05, KEY_EN_4}, //18 + {0x06, KEY_EN_5}, //19 + {0x07, KEY_EN_6}, //20 + {0x08, KEY_EN_7}, //21 + {0x09, KEY_EN_8}, //22 + {0x0A, KEY_EN_9}, //23 + {0x0B, KEY_EN_0}, //24 + {0x0C, KEY_EN_MINUS}, //25 + {0x0D, KEY_EN_EQUALS}, //26 + {0x0F, KEY_EN_BACKSPACE}, //27 + {0x8C, KEY_EN_BACKSPACE}, //28 + + //Row 3 + {0x10, KEY_EN_TAB}, //29 + {0x11, KEY_EN_Q}, //30 + {0x12, KEY_EN_W}, //31 + {0x13, KEY_EN_E}, //32 + {0x14, KEY_EN_R}, //33 + {0x15, KEY_EN_T}, //34 + {0x16, KEY_EN_Y}, //35 + {0x17, KEY_EN_U}, //36 + {0x18, KEY_EN_I}, //37 + {0x19, KEY_EN_O}, //38 + {0x1A, KEY_EN_P}, //39 + {0x1B, KEY_EN_LEFT_BRACKET}, //40 + {0x1C, KEY_EN_RIGHT_BRACKET}, //41 + {0x94, KEY_EN_ISO_ENTER}, //42 + + //Row 4 + {0x1E, KEY_EN_CAPS_LOCK}, //43 + {0x8D, KEY_EN_CAPS_LOCK}, //44 + {0x1F, KEY_EN_A}, //45 + {0x20, KEY_EN_S}, //46 + {0x21, KEY_EN_D}, //47 + {0x22, KEY_EN_F}, //48 + {0x23, KEY_EN_G}, //49 + {0x24, KEY_EN_H}, //50 + {0x25, KEY_EN_J}, //51 + {0x26, KEY_EN_K}, //52 + {0x27, KEY_EN_L}, //53 + {0x28, KEY_EN_SEMICOLON}, //54 + {0x29, KEY_EN_QUOTE}, //55 + {0x2B, KEY_EN_POUND}, //56 + + //Row 5 + {0x2C, KEY_EN_LEFT_SHIFT}, //57 + {0x2D, KEY_EN_ISO_BACK_SLASH}, //58 + {0x2E, KEY_EN_Z}, //59 + {0x2F, KEY_EN_X}, //60 + {0x30, KEY_EN_C}, //61 + {0x31, KEY_EN_V}, //62 + {0x32, KEY_EN_B}, //63 + {0x33, KEY_EN_N}, //64 + {0x34, KEY_EN_M}, //65 + {0x35, KEY_EN_COMMA}, //66 + {0x36, KEY_EN_PERIOD}, //67 + {0x37, KEY_EN_FORWARD_SLASH}, //68 + {0x39, KEY_EN_RIGHT_SHIFT}, //69 + {0x90, KEY_EN_RIGHT_SHIFT}, //70 + + //Row 6 + {0x3A, KEY_EN_LEFT_CONTROL}, //71 + {0x3B, KEY_EN_LEFT_FUNCTION}, //72 + {0x7F, KEY_EN_LEFT_WINDOWS}, //73 + {0x3C, KEY_EN_LEFT_ALT}, //74 + {0x3D, KEY_EN_SPACE}, //75 + {0x91, KEY_EN_SPACE}, //76 + {0x3E, KEY_EN_RIGHT_ALT}, //77 + {0x40, KEY_EN_RIGHT_CONTROL}, //78 + {0x53, KEY_EN_UP_ARROW}, //79 + + //Row 7 + {0x4F, KEY_EN_LEFT_ARROW}, //80 + {0x54, KEY_EN_DOWN_ARROW}, //81 + {0x59, KEY_EN_RIGHT_ARROW}, //82 + + //Left Column + {0xC1, "Key: Vantage"}, //83 + {0xC2, "Key: Capture"}, //84 + {0xC3, "Key: M1"}, //85 + {0xC4, "Key: M2"}, //86 + {0xC5, "Key: Kb Brightness Up"}, //87 + {0xC6, "Key: Kb Brightness Down"}, //88 +}; + +/*--------------------------------------------------------*\ +| Additional LEDs for Legion Y740 | +\*--------------------------------------------------------*/ +const lenovo_led legion_legion_Y740_additional_leds[] +{ + {0x97, "Power button"}, //104 + {0x99, "Vents"}, //105 + {0x98, "USB ports"}, //106 + {0x96, "Legion Y Logo"} //107 +}; + + +/*--------------------------------------------------------*\ +| Legion 7 gen 6: 7 zones | +| | +|Note: the device has 4 zones in the protocol however, the | +|vent lights have been split into 4 zones to improve ease | +|of use | +\*--------------------------------------------------------*/ + +/*------*\ +|keyboard| +\*------*/ +static lenovo_zone lenovo_legion_Y760_kbd_ansi +{ + "Keyboard", + ZONE_TYPE_MATRIX, + 1, + 13, + 21, + legion_Y760_ansi_leds_map, + legion_Y760_ansi_leds, + 0, + 144, +}; + +static lenovo_zone lenovo_legion_Y760_kbd_iso +{ + "Keyboard", + ZONE_TYPE_MATRIX, + 1, + 13, + 21, + legion_Y760_iso_leds_map, + legion_Y760_iso_leds, + 0, + 142, +}; + +static lenovo_zone lenovo_legion_Y760_kbd_jp +{ + "Keyboard", + ZONE_TYPE_MATRIX, + 1, + 13, + 21, + legion_Y760_jp_leds_map, + legion_Y760_jp_leds, + 0, + 148, +}; + +/*------*\ +|logo | +\*------*/ +static lenovo_zone lenovo_legion_Y760_logo +{ + "Logo", + ZONE_TYPE_LINEAR, + 2, + 1, + 13, + NULL, + legion_Y760_logo, + 0, + 12, +}; + +/*------*\ +|vents | +\*------*/ +static lenovo_zone lenovo_legion_Y760_vent_left +{ + "Left vent", + ZONE_TYPE_LINEAR, + 3, + 1, + 26, + NULL, + legion_Y760_vents, + 0, + 26, +}; + +static lenovo_zone lenovo_legion_Y760_vent_right +{ + "Right vent", + ZONE_TYPE_LINEAR, + 3, + 1, + 26, + NULL, + legion_Y760_vents, + 27, + 53, +}; + +static lenovo_zone lenovo_legion_Y760_vent_back_right +{ + "Back Right vent", + ZONE_TYPE_LINEAR, + 3, + 1, + 25, + NULL, + legion_Y760_vents, + 54, + 79, +}; + +static lenovo_zone lenovo_legion_Y760_vent_back_left +{ + "Back Left vent", + ZONE_TYPE_LINEAR, + 3, + 1, + 25, + NULL, + legion_Y760_vents, + 80, + 105, +}; + +/*------*\ +|neon | +\*------*/ +static lenovo_zone lenovo_legion_Y760_neon +{ + "Neon", + ZONE_TYPE_LINEAR, + 4, + 1, + 99, + NULL, + legion_Y760_neon, + 0, + 98, +}; + +/*--------------------------------------------------------*\ +| Legion Y750, Y750S and Y760S: 4 zones | +\*--------------------------------------------------------*/ + +/*----------------------------------*\ +|keyboard | +| | +| Note: keyboard is shared with Y760 | +\*----------------------------------*/ + +/*------*\ +|logo | +\*------*/ +static lenovo_zone lenovo_legion_Y750_logo +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1, + 1, + NULL, + legion_legion_Y750_additional_leds, + 0, + 0, +}; + +/*------*\ +|vents | +\*------*/ +static lenovo_zone lenovo_legion_Y750_vents +{ + "Vents", + ZONE_TYPE_SINGLE, + 1, + 1, + 1, + NULL, + legion_legion_Y750_additional_leds, + 1, + 1, +}; + +/*------*\ +|neon | +\*------*/ +static lenovo_zone lenovo_legion_Y750_neon +{ + "Neon", + ZONE_TYPE_SINGLE, + 1, + 1, + 1, + NULL, + legion_legion_Y750_additional_leds, + 2, + 2, +}; + +/*--------------------------------------------------------*\ +| Legion Y740 17": 5 zones | +\*--------------------------------------------------------*/ + +/*------*\ +|keyboard| +\*------*/ +static lenovo_zone lenovo_legion_Y740_17_kbd_ansi +{ + "Keyboard", + ZONE_TYPE_MATRIX, + 1, + 6, + 19, + legion_Y740_17_ansi_leds_map, + legion_Y740_17_ansi_leds, + 0, + 103, +}; + +static lenovo_zone lenovo_legion_Y740_17_kbd_iso +{ + "Keyboard", + ZONE_TYPE_MATRIX, + 1, + 6, + 19, + legion_Y740_17_iso_leds_map, + legion_Y740_17_iso_leds, + 0, + 102, +}; + +/*------*\ +|logo | +\*------*/ +static lenovo_zone lenovo_legion_Y740_logo +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1, + 1, + NULL, + legion_legion_Y740_additional_leds, + 3, + 3, +}; + +/*-----------*\ +|Power button | +\*-----------*/ +static lenovo_zone lenovo_legion_Y740_pwrbtn +{ + "Power Button", + ZONE_TYPE_SINGLE, + 1, + 1, + 1, + NULL, + legion_legion_Y740_additional_leds, + 0, + 0, +}; + +/*------*\ +|vents | +\*------*/ +static lenovo_zone lenovo_legion_Y740_vents +{ + "Vents", + ZONE_TYPE_SINGLE, + 1, + 1, + 1, + NULL, + legion_legion_Y740_additional_leds, + 1, + 1, +}; + +/*--------*\ +|USB Ports | +\*--------*/ +static lenovo_zone lenovo_legion_Y740_ports +{ + "USB Ports", + ZONE_TYPE_SINGLE, + 1, + 1, + 1, + NULL, + legion_legion_Y740_additional_leds, + 2, + 2, +}; + +/*--------------------------------------------------------*\ +| Legion Y740 15": 5 zones | +\*--------------------------------------------------------*/ + +/*------*\ +|keyboard| +\*------*/ +static lenovo_zone lenovo_legion_Y740_15_kbd_ansi +{ + "Keyboard", + ZONE_TYPE_MATRIX, + 1, + 7, + 16, + legion_Y740_15_ansi_leds_map, + legion_Y740_15_ansi_leds, + 0, + 89, +}; + +static lenovo_zone lenovo_legion_Y740_15_kbd_iso +{ + "Keyboard", + ZONE_TYPE_MATRIX, + 1, + 7, + 16, + legion_Y740_15_iso_leds_map, + legion_Y740_15_iso_leds, + 0, + 88, +}; + +/*--------------------------------------------------------*\ +| Legion 7 gen7: 4 zones | +\*--------------------------------------------------------*/ + +static const unsigned int legion7_gen7and8_ansi_leds_map[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, NA, 30, 31, 32, 33, NA, 34, 35, 36, 37, + 38, 39, 40, 41, NA, 42, 43, 44, 45, 46, 47, 48, 49, 50, NA, 51, 52, 53, 54, NA, + 55, 56, NA, 57, 58, 59, 60, 61, 62, 63, 64, 65, NA, 66, 67, NA, 68, 69, 70, 71, + 72, NA, 73, 74, 75, NA, 76, 77, 78, 79, 80, 81, 82, NA, 83, NA, 84, 85, 86, NA, + 87, 88, 89, 90, 91, NA, NA, NA, NA, NA, 92, 93, NA, 94, NA, NA, 95, NA, 96, 97, + NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 98, 99, NA, 100, NA, NA, NA, NA}; + +/*---------------------*\ +| zone 1, keyboard ANSI | +\*---------------------*/ +const lenovo_led legion7_gen7and8_ansi_leds[] +{ + //row 1 + {0x01, KEY_EN_ESCAPE},//0 + {0x02, KEY_EN_F1},//1 + {0x03, KEY_EN_F2},//2 + {0x04, KEY_EN_F3},//3 + {0x05, KEY_EN_F4},//4 + {0x06, KEY_EN_F5},//5 + {0x07, KEY_EN_F6},//6 + {0x08, KEY_EN_F7},//7 + {0x09, KEY_EN_F8},//8 + {0x0A, KEY_EN_F9},//9 + {0x0B, KEY_EN_F10},//10 + {0x0C, KEY_EN_F11},//11 + {0x0D, KEY_EN_F12},//12 + {0x0E, KEY_EN_INSERT},//13 + {0x0F, KEY_EN_PRINT_SCREEN},//14 + {0x10, KEY_EN_DELETE},//15 + {0x11, KEY_EN_HOME},//16 + {0x12, KEY_EN_END},//17 + {0x13, KEY_EN_PAGE_UP},//18 + {0x14, KEY_EN_PAGE_DOWN},//19 + + //row 2 + {0x16, KEY_EN_BACK_TICK},//20 + {0x17, KEY_EN_1},//21 + {0x18, KEY_EN_2},//22 + {0x19, KEY_EN_3},//23 + {0x1A, KEY_EN_4},//24 + {0x1B, KEY_EN_5},//25 + {0x1C, KEY_EN_6},//26 + {0x1D, KEY_EN_7},//27 + {0x1E, KEY_EN_8},//28 + {0x1F, KEY_EN_9},//29 + {0x20, KEY_EN_0},//30 + {0x21, KEY_EN_MINUS},//31 + {0x22, KEY_EN_EQUALS},//32 + {0x38, KEY_EN_BACKSPACE},//33 + {0x26, KEY_EN_NUMPAD_LOCK},//34 + {0x27, KEY_EN_NUMPAD_DIVIDE},//35 + {0x28, KEY_EN_NUMPAD_TIMES},//36 + {0x29, KEY_EN_NUMPAD_MINUS},//37 + + //row 3 + {0x40, KEY_EN_TAB},//38 + {0x42, KEY_EN_Q},//39 + {0x43, KEY_EN_W},//40 + {0x44, KEY_EN_E},//41 + {0x45, KEY_EN_R},//42 + {0x46, KEY_EN_T},//43 + {0x47, KEY_EN_Y},//44 + {0x48, KEY_EN_U},//45 + {0x49, KEY_EN_I},//46 + {0x4A, KEY_EN_O},//47 + {0x4B, KEY_EN_P},//48 + {0x4C, KEY_EN_LEFT_BRACKET},//49 + {0x4D, KEY_EN_RIGHT_BRACKET},//50 + {0x4E, KEY_EN_ANSI_BACK_SLASH},//51 + {0x4F, KEY_EN_NUMPAD_7},//52 + {0x50, KEY_EN_NUMPAD_8},//53 + {0x51, KEY_EN_NUMPAD_9},//54 + + //row 4 + {0x55, KEY_EN_CAPS_LOCK},//55 + {0x6D, KEY_EN_A},//56 + {0x6E, KEY_EN_S},//57 + {0x58, KEY_EN_D},//58 + {0x59, KEY_EN_F},//59 + {0x5A, KEY_EN_G},//60 + {0x71, KEY_EN_H},//61 + {0x72, KEY_EN_J},//62 + {0x5B, KEY_EN_K},//63 + {0x5C, KEY_EN_L},//64 + {0x5D, KEY_EN_SEMICOLON},//65 + {0x5F, KEY_EN_QUOTE},//66 + {0x77, KEY_EN_ANSI_ENTER},//67 + {0x79, KEY_EN_NUMPAD_4},//68 + {0x7B, KEY_EN_NUMPAD_5},//69 + {0x7C, KEY_EN_NUMPAD_6},//70 + {0x68, KEY_EN_NUMPAD_PLUS},//71 + + //row 5 + {0x6A, KEY_EN_LEFT_SHIFT},//72 + {0x82, KEY_EN_Z},//73 + {0x83, KEY_EN_X},//74 + {0x6F, KEY_EN_C},//75 + {0x70, KEY_EN_V},//76 + {0x87, KEY_EN_B},//77 + {0x88, KEY_EN_N},//78 + {0x73, KEY_EN_M},//79 + {0x74, KEY_EN_COMMA},//80 + {0x75, KEY_EN_PERIOD},//81 + {0x76, KEY_EN_FORWARD_SLASH},//82 + {0x8D, KEY_EN_RIGHT_SHIFT},//83 + {0x8E, KEY_EN_NUMPAD_1},//84 + {0x90, KEY_EN_NUMPAD_2},//85 + {0x92, KEY_EN_NUMPAD_3},//86 + + //row 6 + {0x7F, KEY_EN_LEFT_CONTROL},//87 + {0x80, KEY_EN_LEFT_FUNCTION},//88 + {0x96, KEY_EN_LEFT_WINDOWS},//89 + {0x97, KEY_EN_LEFT_ALT},//90 + {0x98, KEY_EN_SPACE},//91 + {0x9A, KEY_EN_RIGHT_ALT},//92 + {0x9B, KEY_EN_RIGHT_CONTROL},//93 + {0x9D, KEY_EN_UP_ARROW},//94 + {0xA3, KEY_EN_NUMPAD_0},//95 + {0xA5, KEY_EN_NUMPAD_PERIOD},//96 + {0xA7, KEY_EN_NUMPAD_ENTER},//97 + + //row 7 + {0x9C, KEY_EN_LEFT_ARROW},//98 + {0x9F, KEY_EN_DOWN_ARROW},//99 + {0xA1, KEY_EN_RIGHT_ARROW},//100 +}; + +const lenovo_led legion7_gen7and8_neon_leds[] +{ + {0xF5, "Neon group 1"},//0 + {0xF6, "Neon group 2"},//1 + {0xF7, "Neon group 3"},//2 + {0xF8, "Neon group 4"},//3 + {0xF9, "Neon group 5"},//4 + {0xFA, "Neon group 6"},//5 + {0xFB, "Neon group 7"},//6 + {0xFC, "Neon group 8"},//7 + {0xFD, "Neon group 9"},//8 + {0xFE, "Neon group 10"},//9 +}; + +const lenovo_led legion_7gen7_vents_leds[] +{ + {0xE9, "Vent group 1"},//0 + {0xEA, "Vent group 2"},//1 + {0xEB, "Vent group 3"},//2 + {0xEC, "Vent group 4"},//3 + {0xED, "Vent group 5"},//4 + {0xEE, "Vent group 6"},//5 + {0xEF, "Vent group 7"},//6 + {0xF0, "Vent group 8"},//7 +}; + +const lenovo_led legion_7gen10_vents_leds[] +{ + {0xE9, "Vent group 1"},//0 + {0xEA, "Vent group 2"},//1 + {0xEB, "Vent group 3"},//2 + {0xEC, "Vent group 4"},//3 + {0xED, "Vent group 5"},//4 + {0xEE, "Vent group 6"},//5 + {0xEF, "Vent group 7"},//6 + {0xF0, "Vent group 8"},//7 + {0xF1, "Vent group 9"},//8 + {0xF2, "Vent group 10"},//9 + {0xF3, "Vent group 11"},//10 + {0xF4, "Vent group 12"},//11 + {0xF5, "Vent group 13"},//12 + {0xF6, "Vent group 14"},//13 + {0xF7, "Vent group 15"},//14 + {0xF8, "Vent group 16"},//15 + {0xF9, "Vent group 17"},//16 + {0xFA, "Vent group 18"},//17 +}; + +const lenovo_led legion_7gen7_logo_leds[] +{ + {0xDD, "Logo"},//0 +}; + +/*------*\ +|keyboard| +\*------*/ +static lenovo_zone legion7_gen7and8_kbd_ansi +{ + "Keyboard", + ZONE_TYPE_MATRIX, + 0, + 7, + 20, + legion7_gen7and8_ansi_leds_map, + legion7_gen7and8_ansi_leds, + 0, + 100, +}; + +/*------*\ +|logo | +\*------*/ +static lenovo_zone lenovo_legion_7gen7_logo +{ + "Logo", + ZONE_TYPE_LINEAR, + 5, + 1, + 1, + NULL, + legion_7gen7_logo_leds, + 0, + 0, +}; + +/*------*\ +|vents | +\*------*/ +static lenovo_zone lenovo_legion_7gen7_vents +{ + "Vents", + ZONE_TYPE_LINEAR, + 3, + 1, + 8, + NULL, + legion_7gen7_vents_leds, + 0, + 7, +}; + +static lenovo_zone lenovo_legion_7gen10_vents +{ + "Vents", + ZONE_TYPE_LINEAR, + 3, + 1, + 18, + NULL, + legion_7gen10_vents_leds, + 0, + 17, +}; + +/*------*\ +|neon | +\*------*/ +static lenovo_zone legion7_gen7and8_neon +{ + "Neon", + ZONE_TYPE_LINEAR, + 1, + 1, + 10, + NULL, + legion7_gen7and8_neon_leds, + 0, + 9, +}; diff --git a/Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510Controller.cpp b/Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510Controller.cpp new file mode 100644 index 0000000..e0cbabd --- /dev/null +++ b/Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510Controller.cpp @@ -0,0 +1,101 @@ +/*---------------------------------------------------------*\ +| LenovoK510Controller.cpp | +| | +| Driver for Lenovo Legion K510 keyboard | +| | +| Bnyro 27 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "LenovoK510Controller.h" + +using namespace std; + +LenovoK510Controller::LenovoK510Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + device = dev_handle; + location = info.path; + name = dev_name; +} + +LenovoK510Controller::~LenovoK510Controller() +{ + hid_close(device); +} + +std::string LenovoK510Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LenovoK510Controller::GetDeviceName() +{ + return(name); +} + +void LenovoK510Controller::SetMode(unsigned int color_mode, RGBColor color, unsigned char mode_value, unsigned int brigthness, unsigned int speed, unsigned int direction) +{ + unsigned char usb_buf[K510_DATA_SIZE]; + memset(usb_buf, 0x00, K510_DATA_SIZE); + usb_buf[0x00] = 0x04; // ReportID + + // magic bytes to trigger an LED update + usb_buf[0x03] = 0x06; + usb_buf[0x04] = 0x38; + + usb_buf[0x09] = mode_value; + usb_buf[0x0A] = static_cast(brigthness); + // speed behaves contrary to normal expectations: the lower the value, the higher the speed + usb_buf[0x0B] = static_cast(speed); + + if(direction == MODE_DIRECTION_UP || direction == MODE_DIRECTION_LEFT) + { + // 0x01 reverses the direction of the animation + usb_buf[0x0C] = 0x01; + } + + if(color_mode == MODE_COLORS_MODE_SPECIFIC) + { + usb_buf[0x0D] = 0x00; + usb_buf[0x0E] = static_cast(RGBGetRValue(color)); + usb_buf[0x0F] = static_cast(RGBGetGValue(color)); + usb_buf[0x10] = static_cast(RGBGetBValue(color)); + } + else + { + // sets color to automatic + usb_buf[0x0D] = 0x01; + } + + hid_write(device, usb_buf, K510_DATA_SIZE); +} + +mode LenovoK510Controller::GetCurrentState() +{ + unsigned char usb_buf[K510_DATA_SIZE]; + memset(usb_buf, 0x00, K510_DATA_SIZE); + usb_buf[0x00] = 0x04; // ReportID + + // magic bytes to get a response containing the current configuration + usb_buf[0x03] = 0x05; + usb_buf[0x04] = 0x38; + + hid_write(device, usb_buf, K510_DATA_SIZE); + + unsigned char res_buf[K510_DATA_SIZE]; + hid_read_timeout(device, res_buf, K510_DATA_SIZE, 50); + + mode current_mode; + current_mode.value = res_buf[0x09]; + current_mode.brightness = res_buf[0x0A]; + current_mode.speed = res_buf[0x0B]; + current_mode.direction = res_buf[0x0C]; + current_mode.color_mode = res_buf[0x0D] ? MODE_COLORS_RANDOM : MODE_COLORS_MODE_SPECIFIC; + current_mode.colors.push_back(ToRGBColor(res_buf[0x0E], res_buf[0x0F], res_buf[0x10])); + + return(current_mode); +} diff --git a/Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510Controller.h b/Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510Controller.h new file mode 100644 index 0000000..4ab6d45 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510Controller.h @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| LenovoK510Controller.h | +| | +| Driver for Lenovo Legion K510 keyboard | +| | +| Bnyro 27 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define K510_DATA_SIZE 64 + +#define K510_BRIGHTNESS_DEFAULT 2 +#define K510_BRIGHTNESS_MIN 0 +#define K510_BRIGHTNESS_MAX 2 + +// the lower the speed value, the faster the animation +#define K510_SPEED_DEFAULT 2 +#define K510_SPEED_MIN 4 +#define K510_SPEED_MAX 0 + +enum +{ + K510_MODE_CORRUGATED = 0x01, + K510_MODE_CLOUD = 0x02, + K510_MODE_SERPENTINE = 0x03, + K510_MODE_SPECTRUM = 0x04, + K510_MODE_BREATH = 0x05, + K510_MODE_NORMAL = 0x06, + K510_MODE_REACTION = 0x07, + K510_MODE_RIPPLES = 0x08, + K510_MODE_TRAVERSE = 0x09, + K510_MODE_STARS = 0x0A, + K510_MODE_FLOWERS = 0x0B, + K510_MODE_ROLL = 0x0C, + K510_MODE_WAVE = 0x0D, + K510_MODE_CARTOON = 0x0E, + K510_MODE_RAIN = 0x0F, + K510_MODE_SCAN = 0x10, + K510_MODE_SURMOUNT = 0x11, + K510_MODE_SPEED = 0x12, +}; + +class LenovoK510Controller +{ +public: + LenovoK510Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~LenovoK510Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetMode(unsigned int color_mode, RGBColor color, unsigned char mode_value, unsigned int brightness, unsigned int speed, unsigned int direction); + mode GetCurrentState(); +; + +private: + hid_device* device; + std::string location; + std::string name; +}; diff --git a/Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510ControllerDetect.cpp b/Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510ControllerDetect.cpp new file mode 100644 index 0000000..2301685 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510ControllerDetect.cpp @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| LenovoK510ControllerDetect.cpp | +| | +| Detector for Lenovo Legion K510 keyboard | +| | +| Bnyro 27 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBController_LenovoK510.h" +#include "LenovoK510Controller.h" + +/*---------------------------------------------------------*\ +| Lenovo vendor, product, usage and page IDs | +\*---------------------------------------------------------*/ +#define LENOVO_VID 0x17EF +#define LEGION_K510_PID 0x619A +#define LENOVO_IFACE_NUM 0x01 +#define LENOVO_PAGE 0xFF1C +#define LENOVO_USAGE 0x0092 + +void DetectLenovoLegionK510Controllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LenovoK510Controller* controller = new LenovoK510Controller(dev, *info, name); + RGBController_LenovoK510* rgb_controller = new RGBController_LenovoK510(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Lenovo Legion K510 Mini Pro", DetectLenovoLegionK510Controllers, LENOVO_VID, LEGION_K510_PID, LENOVO_IFACE_NUM, LENOVO_PAGE, LENOVO_USAGE); diff --git a/Controllers/LenovoControllers/LenovoLegionK510Controller/RGBController_LenovoK510.cpp b/Controllers/LenovoControllers/LenovoLegionK510Controller/RGBController_LenovoK510.cpp new file mode 100644 index 0000000..2d97733 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoLegionK510Controller/RGBController_LenovoK510.cpp @@ -0,0 +1,352 @@ +/*---------------------------------------------------------*\ +| RGBController_LenovoM300.cpp | +| | +| RGBController for Lenovo Legion K510 keyboard | +| | +| Bnyro 27 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LenovoK510.h" + +/**------------------------------------------------------------------*\ + @name Lenovo Legion K510 + @category Keyboard + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectLenovoLegionK510Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LenovoK510::RGBController_LenovoK510(LenovoK510Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Lenovo"; + type = DEVICE_TYPE_KEYBOARD; + description = "Lenovo Legion K510 Mini Pro"; + location = controller->GetDeviceLocation(); + + mode Corrugated; + Corrugated.name = "Corrugated"; + Corrugated.value = K510_MODE_CORRUGATED; + Corrugated.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_LR; + Corrugated.color_mode = MODE_COLORS_RANDOM; + Corrugated.brightness = K510_BRIGHTNESS_DEFAULT; + Corrugated.brightness_min = K510_BRIGHTNESS_MIN; + Corrugated.brightness_max = K510_BRIGHTNESS_MAX; + Corrugated.speed = K510_SPEED_DEFAULT; + Corrugated.speed_min = K510_SPEED_MIN; + Corrugated.speed_max = K510_SPEED_MAX; + Corrugated.colors.resize(1); + modes.push_back(Corrugated); + + mode Cloud; + Cloud.name = "Cloud"; + Cloud.value = K510_MODE_CLOUD; + Cloud.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_LR; + Cloud.color_mode = MODE_COLORS_RANDOM; + Cloud.brightness = K510_BRIGHTNESS_DEFAULT; + Cloud.brightness_min = K510_BRIGHTNESS_MIN; + Cloud.brightness_max = K510_BRIGHTNESS_MAX; + Cloud.speed = K510_SPEED_DEFAULT; + Cloud.speed_min = K510_SPEED_MIN; + Cloud.speed_max = K510_SPEED_MAX; + Cloud.colors.resize(1); + modes.push_back(Cloud); + + mode Serpentine; + Serpentine.name = "Serpentine"; + Serpentine.value = K510_MODE_SERPENTINE; + Serpentine.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_LR; + Serpentine.color_mode = MODE_COLORS_RANDOM; + Serpentine.brightness = K510_BRIGHTNESS_DEFAULT; + Serpentine.brightness_min = K510_BRIGHTNESS_MIN; + Serpentine.brightness_max = K510_BRIGHTNESS_MAX; + Serpentine.speed = K510_SPEED_DEFAULT; + Serpentine.speed_min = K510_SPEED_MIN; + Serpentine.speed_max = K510_SPEED_MAX; + Serpentine.colors.resize(1); + modes.push_back(Serpentine); + + mode Spectrum; + Spectrum.name = "Spectrum Cycle"; + Spectrum.value = K510_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.brightness = K510_BRIGHTNESS_DEFAULT; + Spectrum.brightness_min = K510_BRIGHTNESS_MIN; + Spectrum.brightness_max = K510_BRIGHTNESS_MAX; + Spectrum.speed = K510_SPEED_DEFAULT; + Spectrum.speed_min = K510_SPEED_MIN; + Spectrum.speed_max = K510_SPEED_MAX; + modes.push_back(Spectrum); + + mode Breath; + Breath.name = "Breathing"; + Breath.value = K510_MODE_BREATH; + Breath.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Breath.color_mode = MODE_COLORS_RANDOM; + Breath.brightness = K510_BRIGHTNESS_DEFAULT; + Breath.brightness_min = K510_BRIGHTNESS_MIN; + Breath.brightness_max = K510_BRIGHTNESS_MAX; + Breath.speed = K510_SPEED_DEFAULT; + Breath.speed_min = K510_SPEED_MIN; + Breath.speed_max = K510_SPEED_MAX; + Breath.colors.resize(1); + modes.push_back(Breath); + + mode Normal; + Normal.name = "Static"; + Normal.value = K510_MODE_NORMAL; + Normal.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Normal.color_mode = MODE_COLORS_RANDOM; + Normal.brightness = K510_BRIGHTNESS_DEFAULT; + Normal.brightness_min = K510_BRIGHTNESS_MIN; + Normal.brightness_max = K510_BRIGHTNESS_MAX; + Normal.colors.resize(1); + modes.push_back(Normal); + + mode Reaction; + Reaction.name = "Reaction"; + Reaction.value = K510_MODE_REACTION; + Reaction.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Reaction.color_mode = MODE_COLORS_RANDOM; + Reaction.brightness = K510_BRIGHTNESS_DEFAULT; + Reaction.brightness_min = K510_BRIGHTNESS_MIN; + Reaction.brightness_max = K510_BRIGHTNESS_MAX; + Reaction.speed = K510_SPEED_DEFAULT; + Reaction.speed_min = K510_SPEED_MIN; + Reaction.speed_max = K510_SPEED_MAX; + Reaction.colors.resize(1); + modes.push_back(Reaction); + + mode Ripples; + Ripples.name = "Ripples"; + Ripples.value = K510_MODE_RIPPLES; + Ripples.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Ripples.color_mode = MODE_COLORS_RANDOM; + Ripples.brightness = K510_BRIGHTNESS_DEFAULT; + Ripples.brightness_min = K510_BRIGHTNESS_MIN; + Ripples.brightness_max = K510_BRIGHTNESS_MAX; + Ripples.speed = K510_SPEED_DEFAULT; + Ripples.speed_min = K510_SPEED_MIN; + Ripples.speed_max = K510_SPEED_MAX; + Ripples.colors.resize(1); + modes.push_back(Ripples); + + mode Traverse; + Traverse.name = "Traverse"; + Traverse.value = K510_MODE_TRAVERSE; + Traverse.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Traverse.color_mode = MODE_COLORS_RANDOM; + Traverse.brightness = K510_BRIGHTNESS_DEFAULT; + Traverse.brightness_min = K510_BRIGHTNESS_MIN; + Traverse.brightness_max = K510_BRIGHTNESS_MAX; + Traverse.speed = K510_SPEED_DEFAULT; + Traverse.speed_min = K510_SPEED_MIN; + Traverse.speed_max = K510_SPEED_MAX; + Traverse.colors.resize(1); + modes.push_back(Traverse); + + mode Stars; + Stars.name = "Stars"; + Stars.value = K510_MODE_STARS; + Stars.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Stars.color_mode = MODE_COLORS_RANDOM; + Stars.brightness = K510_BRIGHTNESS_DEFAULT; + Stars.brightness_min = K510_BRIGHTNESS_MIN; + Stars.brightness_max = K510_BRIGHTNESS_MAX; + Stars.speed = K510_SPEED_DEFAULT; + Stars.speed_min = K510_SPEED_MIN; + Stars.speed_max = K510_SPEED_MAX; + Stars.colors.resize(1); + modes.push_back(Stars); + + mode Flowers; + Flowers.name = "Flowers"; + Flowers.value = K510_MODE_FLOWERS; + Flowers.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Flowers.color_mode = MODE_COLORS_RANDOM; + Flowers.brightness = K510_BRIGHTNESS_DEFAULT; + Flowers.brightness_min = K510_BRIGHTNESS_MIN; + Flowers.brightness_max = K510_BRIGHTNESS_MAX; + Flowers.speed = K510_SPEED_DEFAULT; + Flowers.speed_min = K510_SPEED_MIN; + Flowers.speed_max = K510_SPEED_MAX; + modes.push_back(Flowers); + + mode Roll; + Roll.name = "Rainbow Wave"; + Roll.value = K510_MODE_ROLL; + Roll.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_UD; + Roll.color_mode = MODE_COLORS_NONE; + Roll.brightness = K510_BRIGHTNESS_DEFAULT; + Roll.brightness_min = K510_BRIGHTNESS_MIN; + Roll.brightness_max = K510_BRIGHTNESS_MAX; + Roll.speed = K510_SPEED_DEFAULT; + Roll.speed_min = K510_SPEED_MIN; + Roll.speed_max = K510_SPEED_MAX; + modes.push_back(Roll); + + mode Wave; + Wave.name = "Wave"; + Wave.value = K510_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Wave.color_mode = MODE_COLORS_RANDOM; + Wave.brightness = K510_BRIGHTNESS_DEFAULT; + Wave.brightness_min = K510_BRIGHTNESS_MIN; + Wave.brightness_max = K510_BRIGHTNESS_MAX; + Wave.speed = K510_SPEED_DEFAULT; + Wave.speed_min = K510_SPEED_MIN; + Wave.speed_max = K510_SPEED_MAX; + Wave.colors.resize(1); + modes.push_back(Wave); + + mode Cartoon; + Cartoon.name = "Cartoon"; + Cartoon.value = K510_MODE_CARTOON; + Cartoon.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Cartoon.color_mode = MODE_COLORS_RANDOM; + Cartoon.brightness = K510_BRIGHTNESS_DEFAULT; + Cartoon.brightness_min = K510_BRIGHTNESS_MIN; + Cartoon.brightness_max = K510_BRIGHTNESS_MAX; + Cartoon.speed = K510_SPEED_DEFAULT; + Cartoon.speed_min = K510_SPEED_MIN; + Cartoon.speed_max = K510_SPEED_MAX; + Cartoon.colors.resize(1); + modes.push_back(Cartoon); + + mode Rain; + Rain.name = "Rain"; + Rain.value = K510_MODE_RAIN; + Rain.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Rain.color_mode = MODE_COLORS_RANDOM; + Rain.brightness = K510_BRIGHTNESS_DEFAULT; + Rain.brightness_min = K510_BRIGHTNESS_MIN; + Rain.brightness_max = K510_BRIGHTNESS_MAX; + Rain.speed = K510_SPEED_DEFAULT; + Rain.speed_min = K510_SPEED_MIN; + Rain.speed_max = K510_SPEED_MAX; + Rain.colors.resize(1); + modes.push_back(Rain); + + mode Scan; + Scan.name = "Scan"; + Scan.value = K510_MODE_SCAN; + Scan.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Scan.color_mode = MODE_COLORS_NONE; + Scan.brightness = K510_BRIGHTNESS_DEFAULT; + Scan.brightness_min = K510_BRIGHTNESS_MIN; + Scan.brightness_max = K510_BRIGHTNESS_MAX; + modes.push_back(Scan); + + mode Surmount; + Surmount.name = "Surmount"; + Surmount.value = K510_MODE_SURMOUNT; + Surmount.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Surmount.color_mode = MODE_COLORS_NONE; + Surmount.brightness = K510_BRIGHTNESS_DEFAULT; + Surmount.brightness_min = K510_BRIGHTNESS_MIN; + Surmount.brightness_max = K510_BRIGHTNESS_MAX; + modes.push_back(Surmount); + + mode Speed; + Speed.name = "Speed"; + Speed.value = K510_MODE_SPEED; + Speed.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_LR; + Speed.color_mode = MODE_COLORS_NONE; + Speed.brightness = K510_BRIGHTNESS_DEFAULT; + Speed.brightness_min = K510_BRIGHTNESS_MIN; + Speed.brightness_max = K510_BRIGHTNESS_MAX; + Speed.speed = K510_SPEED_DEFAULT; + Speed.speed_min = K510_SPEED_MIN; + Speed.speed_max = K510_SPEED_MAX; + modes.push_back(Speed); + + SetupZones(); + ReadAndUpdateCurrentDeviceState(); +} + +RGBController_LenovoK510::~RGBController_LenovoK510() +{ + delete controller; +} + +void RGBController_LenovoK510::SetupZones() +{ + zone default_zone; + default_zone.name = "Keyboard"; + default_zone.type = ZONE_TYPE_SINGLE; + default_zone.leds_min = 1; + default_zone.leds_max = 1; + default_zone.leds_count = 1; + default_zone.matrix_map = nullptr; + zones.emplace_back(default_zone); + + leds.resize(1); + leds[0].name = "Keyboard"; + + SetupColors(); +} + +void RGBController_LenovoK510::ReadAndUpdateCurrentDeviceState() +{ + mode current_active_mode = controller->GetCurrentState(); + + for(std::vector::size_type i = 0; i < modes.size(); ++i) + { + if(modes[i].value == current_active_mode.value) + { + // override the default config of the mode with the current one + modes[i].brightness = current_active_mode.brightness; + modes[i].speed = current_active_mode.speed; + modes[i].color_mode = current_active_mode.color_mode; + zones[0].colors[0] = current_active_mode.colors[0]; + + if(modes[i].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + current_active_mode.direction = current_active_mode.direction ? MODE_DIRECTION_LEFT : MODE_DIRECTION_RIGHT; + } + else if(modes[i].flags & MODE_FLAG_HAS_DIRECTION_UD) + { + current_active_mode.direction = current_active_mode.direction ? MODE_DIRECTION_UP : MODE_DIRECTION_DOWN; + } + + active_mode = (int)i; + break; + } + } +} + +void RGBController_LenovoK510::ResizeZone(int /*zone*/, int /*new_size*/) +{ + // Not Supported +} + +void RGBController_LenovoK510::DeviceUpdateLEDs() +{ + // Not Supported +} + +void RGBController_LenovoK510::UpdateZoneLEDs(int /*zone*/) +{ + // Not Supported +} + +void RGBController_LenovoK510::UpdateSingleLED(int /*led*/) +{ + // Not Supported +} + +void RGBController_LenovoK510::DeviceUpdateMode() +{ + const mode& active = modes[active_mode]; + RGBColor color = active.colors.size() > 0 ? active.colors[0] : 0x00; + controller->SetMode(active.color_mode, color, active.value, active.brightness, active.speed, active.direction); +} diff --git a/Controllers/LenovoControllers/LenovoLegionK510Controller/RGBController_LenovoK510.h b/Controllers/LenovoControllers/LenovoLegionK510Controller/RGBController_LenovoK510.h new file mode 100644 index 0000000..3ae2e1c --- /dev/null +++ b/Controllers/LenovoControllers/LenovoLegionK510Controller/RGBController_LenovoK510.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_LenovoK510.h | +| | +| RGBController for Lenovo Legion K510 keyboard | +| | +| Bnyro 27 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LenovoK510Controller.h" + +class RGBController_LenovoK510 : public RGBController +{ +public: + RGBController_LenovoK510(LenovoK510Controller* controller_ptr); + ~RGBController_LenovoK510(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LenovoK510Controller* controller; + + void ReadAndUpdateCurrentDeviceState(); +}; diff --git a/Controllers/LenovoControllers/LenovoM300Controller/LenovoM300Controller.cpp b/Controllers/LenovoControllers/LenovoM300Controller/LenovoM300Controller.cpp new file mode 100644 index 0000000..37fc832 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoM300Controller/LenovoM300Controller.cpp @@ -0,0 +1,94 @@ +/*---------------------------------------------------------*\ +| LenovoM300Controller.cpp | +| | +| Driver for Lenovo Legion M300 mouse | +| | +| Wayne Riordan 09 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LenovoM300Controller.h" + +using namespace std; + +LenovoM300Controller::LenovoM300Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + device = dev_handle; + location = info.path; + name = dev_name; +} + +LenovoM300Controller::~LenovoM300Controller() +{ + hid_close(device); +} + +std::string LenovoM300Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LenovoM300Controller::GetDeviceName() +{ + return(name); +} + +void LenovoM300Controller::SetMode(std::vector colors, unsigned char mode_value, unsigned int brigthness) +{ + unsigned char usb_buf[M300_DATA_SIZE]; + memset(usb_buf, 0x00, M300_DATA_SIZE); + + usb_buf[0x01] = 0x25; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = 0x0C; + + switch(mode_value) + { + case M300_MODE_RAINBOW: + usb_buf[0x06] = 0x01; + usb_buf[0x07] = 0x64; + usb_buf[0x09] = 0x0A; + usb_buf[0x40] = CalculateFinalByte(usb_buf, 0x0A); + break; + case M300_MODE_BREATHING: + usb_buf[0x06] = 0x02; + usb_buf[0x07] = 0x64; + usb_buf[0x09] = 0x02; + usb_buf[0x0A] = 0x03; + usb_buf[0x0C] = RGBGetRValue(colors[0]); + usb_buf[0x0D] = RGBGetGValue(colors[0]); + usb_buf[0x0E] = RGBGetBValue(colors[0]); + usb_buf[0x0F] = RGBGetRValue(colors[1]); + usb_buf[0x10] = RGBGetGValue(colors[1]); + usb_buf[0x11] = RGBGetBValue(colors[1]); + usb_buf[0x40] = CalculateFinalByte(usb_buf, 0x12); + break; + case M300_MODE_STATIC: + usb_buf[0x06] = 0x03; + usb_buf[0x07] = brigthness; + usb_buf[0x09] = RGBGetRValue(colors[0]); + usb_buf[0x0A] = RGBGetGValue(colors[0]); + usb_buf[0x0B] = RGBGetBValue(colors[0]); + usb_buf[0x40] = CalculateFinalByte(usb_buf, 0x0C); + break; + case M300_MODE_OFF: + default: + usb_buf[0x06] = 0x03; + usb_buf[0x40] = 0x36; + } + hid_write(device, usb_buf, M300_DATA_SIZE); +} + +unsigned char LenovoM300Controller::CalculateFinalByte(unsigned char* ptr, int count) +{ + unsigned char final_byte = 0; + for(int i = 0; i < count; i++) + { + final_byte += ptr[i]; + } + return final_byte; +} diff --git a/Controllers/LenovoControllers/LenovoM300Controller/LenovoM300Controller.h b/Controllers/LenovoControllers/LenovoM300Controller/LenovoM300Controller.h new file mode 100644 index 0000000..13e51c5 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoM300Controller/LenovoM300Controller.h @@ -0,0 +1,49 @@ +/*---------------------------------------------------------*\ +| LenovoM300Controller.h | +| | +| Driver for Lenovo Legion M300 mouse | +| | +| Wayne Riordan 09 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define M300_DATA_SIZE 0x41 +#define M300_MAX_BRIGTH 0x64 +#define M300_MIN_BRIGHT 0x01 + +enum +{ + M300_MODE_OFF = 0x00, + M300_MODE_RAINBOW = 0x01, + M300_MODE_BREATHING = 0x02, + M300_MODE_STATIC = 0X03 +}; + +class LenovoM300Controller +{ +public: + LenovoM300Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~LenovoM300Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetMode(std::vector colors, unsigned char mode_value, unsigned int brightness); + +protected: + hid_device* device; + +private: + std::string location; + std::string name; + + unsigned char CalculateFinalByte(unsigned char* ptr, int count); +}; diff --git a/Controllers/LenovoControllers/LenovoM300Controller/LenovoM300ControllerDetect.cpp b/Controllers/LenovoControllers/LenovoM300Controller/LenovoM300ControllerDetect.cpp new file mode 100644 index 0000000..f32d9e0 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoM300Controller/LenovoM300ControllerDetect.cpp @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| LenovoM300ControllerDetect.cpp | +| | +| Detector for Lenovo Legion M300 mouse | +| | +| Wayne Riordan 09 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBController_LenovoM300.h" +#include "LenovoM300Controller.h" + +/*---------------------------------------------------------*\ +| Lenovo vendor, product, usage and page IDs | +\*---------------------------------------------------------*/ +#define LENOVO_VID 0x17EF +#define LEGION_M300_PID 0x60E4 +#define LENOVO_USAGE 0X01 +#define LENOVO_PAGE 0XFF01 + +void DetectLenovoLegionM300Controllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LenovoM300Controller* controller = new LenovoM300Controller(dev, *info, name); + RGBController_LenovoM300* rgb_controller = new RGBController_LenovoM300(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("Lenovo Legion M300", DetectLenovoLegionM300Controllers, LENOVO_VID, LEGION_M300_PID, LENOVO_PAGE, LENOVO_USAGE); diff --git a/Controllers/LenovoControllers/LenovoM300Controller/RGBController_LenovoM300.cpp b/Controllers/LenovoControllers/LenovoM300Controller/RGBController_LenovoM300.cpp new file mode 100644 index 0000000..8e9a336 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoM300Controller/RGBController_LenovoM300.cpp @@ -0,0 +1,120 @@ +/*---------------------------------------------------------*\ +| RGBController_LenovoM300.cpp | +| | +| RGBController for Lenovo Legion M300 mouse | +| | +| Wayne Riordan 09 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LenovoM300.h" + +/**------------------------------------------------------------------*\ + @name Lenovo Legion M300 + @category Mouse + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectLenovoLegionM300Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LenovoM300::RGBController_LenovoM300(LenovoM300Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Lenovo"; + type = DEVICE_TYPE_MOUSE; + description = "Lenovo M300 Device"; + location = controller->GetDeviceLocation(); + + mode Static; + Static.name = "Static"; + Static.value = M300_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.brightness_max = M300_MAX_BRIGTH; + Static.brightness_min = M300_MIN_BRIGHT; + Static.brightness = M300_MAX_BRIGTH; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = M300_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = M300_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.brightness_max = M300_MAX_BRIGTH; + Breathing.brightness_min = M300_MIN_BRIGHT; + Breathing.brightness = M300_MAX_BRIGTH; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(2); + modes.push_back(Breathing); + + mode Spectrum; + Spectrum.name = "Spectrum Cycle"; + Spectrum.value = M300_MODE_RAINBOW; + Spectrum.flags = MODE_FLAG_AUTOMATIC_SAVE; + Spectrum.color_mode = MODE_COLORS_NONE; + modes.push_back(Spectrum); + + SetupZones(); +} + +RGBController_LenovoM300::~RGBController_LenovoM300() +{ + delete controller; +} + +void RGBController_LenovoM300::SetupZones() +{ + zone default_zone; + default_zone.name = "Mouse"; + default_zone.type = ZONE_TYPE_SINGLE; + default_zone.leds_min = 1; + default_zone.leds_max = 1; + default_zone.leds_count = 1; + default_zone.matrix_map = nullptr; + zones.emplace_back(default_zone); + + leds.resize(1); + leds[0].name = "LED 1"; + + SetupColors(); +} + +void RGBController_LenovoM300::ResizeZone(int /*zone*/, int /*new_size*/) +{ + // Not Supported +} + +void RGBController_LenovoM300::DeviceUpdateLEDs() +{ + const mode& active = modes[active_mode]; + controller->SetMode(active.colors, active.value, active.brightness); +} + +void RGBController_LenovoM300::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LenovoM300::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LenovoM300::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/LenovoControllers/LenovoM300Controller/RGBController_LenovoM300.h b/Controllers/LenovoControllers/LenovoM300Controller/RGBController_LenovoM300.h new file mode 100644 index 0000000..846684d --- /dev/null +++ b/Controllers/LenovoControllers/LenovoM300Controller/RGBController_LenovoM300.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_LenovoM300.h | +| | +| RGBController for Lenovo Legion M300 mouse | +| | +| Wayne Riordan 09 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LenovoM300Controller.h" + +class RGBController_LenovoM300 : public RGBController +{ +public: + RGBController_LenovoM300(LenovoM300Controller* controller_ptr); + ~RGBController_LenovoM300(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LenovoM300Controller* controller; +}; diff --git a/Controllers/LenovoControllers/LenovoUSBController/LenovoUSBController.cpp b/Controllers/LenovoControllers/LenovoUSBController/LenovoUSBController.cpp new file mode 100644 index 0000000..6f0cc57 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBController/LenovoUSBController.cpp @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| LenovoUSBController.cpp | +| | +| Driver for Lenovo USB devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LenovoUSBController.h" +#include "LogManager.h" +#include "StringUtils.h" + +using namespace std; + +LenovoUSBController::LenovoUSBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + pid = in_pid; + name = dev_name; + + setDeviceSoftwareMode(); +} + +LenovoUSBController::~LenovoUSBController() +{ + hid_close(dev); +} + +uint16_t LenovoUSBController::getPid() +{ + return pid; +} + +string LenovoUSBController::getName() +{ + return name; +} + +string LenovoUSBController::getLocation() +{ + return location; +} + +void LenovoUSBController::setZoneLeds(uint8_t zone_num, vector> &led_colors) +{ + for(size_t curr = 0; curr < led_colors.size();) + { + uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, (uint8_t) (LENOVO_ZONE_ID_0+zone_num), 0, 0}; + + /*---------------------------------------------------------*\ + | Set the colour bytes in the packet | + | buffer[2] is required to know the amount of leds in packet| + | so it is set here as well | + \*---------------------------------------------------------*/ + for(; buffer[2] < LENOVO_MAX_LEDS_PER_PACKET && curr < led_colors.size(); ++buffer[2]) + { + uint8_t offset = (buffer[2] * 4) + 4; + + /*------------------*\ + |write the led number| + \*------------------*/ + buffer[offset] = led_colors[curr].first; + /*--------------*\ + |write the colors| + \*--------------*/ + buffer[offset + 1] = RGBGetRValue(led_colors[curr].second); + buffer[offset + 2] = RGBGetGValue(led_colors[curr].second); + buffer[offset + 3] = RGBGetBValue(led_colors[curr].second); + + ++curr; + } + + hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE); + } +} + +void LenovoUSBController::setSingleLED(uint8_t zone_num, uint8_t led_num, RGBColor color) +{ + uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, (uint8_t) (LENOVO_ZONE_ID_0+zone_num), 1, 0, led_num, (uint8_t) (RGBGetRValue(color)), (uint8_t) (RGBGetGValue(color)), (uint8_t) (RGBGetBValue(color))}; + hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE); +} + +void LenovoUSBController::sendBasicInstruction(uint8_t instruction) +{ + uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, instruction}; + hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE); +} + +vector LenovoUSBController::getInformation(uint8_t information_id) +{ + uint8_t buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START, information_id}; + hid_send_feature_report(dev, buffer, LENOVO_HID_PACKET_SIZE); + uint8_t read_buffer[LENOVO_HID_PACKET_SIZE] = {LENOVO_INSTRUCTION_START}; + int num_bytes = hid_get_feature_report(dev, read_buffer, LENOVO_HID_PACKET_SIZE); + if(num_bytes > 0) + { + vector response(&read_buffer[0], &read_buffer[num_bytes]); + return response; + } + return vector(); +} + +void LenovoUSBController::setDeviceSoftwareMode() +{ + /*---------------------------------------*\ + | this is required for the device listen | + | to the software protocol | + \*---------------------------------------*/ + sendBasicInstruction(0xB2); +} + +void LenovoUSBController::setDeviceHardwareMode() +{ + /*---------------------------------------*\ + |releases the device from sofware mode so | + |that onboard controlls can be used | + |this has not been shown to happen between| + |reboots | + \*---------------------------------------*/ + sendBasicInstruction(0xB1); +} diff --git a/Controllers/LenovoControllers/LenovoUSBController/LenovoUSBController.h b/Controllers/LenovoControllers/LenovoUSBController/LenovoUSBController.h new file mode 100644 index 0000000..bb28de8 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBController/LenovoUSBController.h @@ -0,0 +1,69 @@ +/*---------------------------------------------------------*\ +| LenovoUSBController.h | +| | +| Driver for Lenovo USB devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "RGBController.h" +#include "LogManager.h" + +#ifndef HID_MAX_STR +#define HID_MAX_STR 255 +#endif + +#ifndef LENOVOUSBCONTROLLER_H +#define LENOVOUSBCONTROLLER_H + +#define LENOVO_INSTRUCTION_START 0x07 +#define LENOVO_ZONE_ID_0 0xA0 + +#define LENOVO_HID_PACKET_SIZE 192 +#define LENOVO_MAX_LEDS_PER_PACKET 0x2F + +class LenovoUSBController +{ + public: + /*--------------*\ + |ctor(s) and dtor| + \*--------------*/ + LenovoUSBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name); + ~LenovoUSBController(); + + /*--------------*\ + |device functions| + \*--------------*/ + void setZoneLeds(uint8_t zone_num, std::vector> &led_colors); + void setSingleLED(uint8_t zone_num, uint8_t led_num, RGBColor color); + uint16_t getPid(); + std::string getName(); + std::string getLocation(); + std::vector getInformation(uint8_t information_id); + void setDeviceSoftwareMode(); + void setDeviceHardwareMode(); + + private: + /*--------------*\ + |data members | + \*--------------*/ + std::string name; + hid_device *dev; + std::string location; + uint16_t pid; + + /*--------------*\ + |device functions| + \*--------------*/ + void sendBasicInstruction(uint8_t instruction); +}; + +#endif diff --git a/Controllers/LenovoControllers/LenovoUSBController/RGBController_LenovoUSB.cpp b/Controllers/LenovoControllers/LenovoUSBController/RGBController_LenovoUSB.cpp new file mode 100644 index 0000000..e173fc3 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBController/RGBController_LenovoUSB.cpp @@ -0,0 +1,477 @@ +/*---------------------------------------------------------*\ +| RGBController_Lenovo_USB.cpp | +| | +| RGBController for Lenovo USB devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "LenovoDevices.h" +#include "RGBController_LenovoUSB.h" +#include "LogManager.h" + +using namespace std; + +/*--------------------------------------------------------------------------------------*\ +|note: the RGBController_LenovoUSB constructor determines which list of leds to pull from| +\*--------------------------------------------------------------------------------------*/ + +/**------------------------------------------------------------------*\ + @name Lenovo USB + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectLenovoLegionUSBControllers + @comment Tested on Lenovo Legion 7/7i gen. 6 ANSI and ISO models + Hardware modes are not implented + PLEASE UPDATE YOUR BIOS IF YOU HAVE ISSUES WITH HARDWARE MODES AFTER CLOSING OPENRGB + + If you have other models beside the Legion 7 gen 6 and want to test if RGB can be added to OpenRGB, + you can do so if you're on Windows by running + [this Powershell script](https://gitlab.com/pvazny/legion7-rgb-ps) with the `-test` parameter: + + ```powershell + legion7-rgb.ps1 -test -v 0x048D -p 0xC935 -up 0xFF89 -u 0x07 -c 0xa1 -start 0x15 -stop 0xa1 + ``` + + This script will iterate through each LED one by one, in bank `$c = 0xa1`, between IDs + `-start 0x15` and `-stop 0xa1`, on the HID device with VID `-v 0x048D` PID `-p 0xC935` usage page + `-up 0xFF89` and usage `-u 0x07`. It will default to the Legion 7 Gen 6 description, however you can + enter a new description for each LED and at the end it will generate the differences. + + To check if you have an eligible HID device that could potentially be iterated by the script please + open the powershell command prompt and executre the follow: + + ```powershell + Get-PnpDevice -Class 'HIDClass' | ForEach-Object { [PSCustomObject]@{Name = $_.FriendlyName; InstanceId = $_.InstanceId; HardwareId = ($_.HardwareId | Where-Object {$_ -like 'HID_DEVICE_UP:*' })}} | Where-Object { $_.HardwareId -ne $null } | Sort-Object InstanceId + ``` + + If the script is successful please create a [new device issue](https://gitlab.com/CalcProgrammer1/OpenRGB/-/issues/new?issuable_template=New%20Device#) + and attach the relevant details to request support for your device. +\*-------------------------------------------------------------------*/ + +RGBController_LenovoUSB::RGBController_LenovoUSB(LenovoUSBController* controller_ptr) +{ + controller = controller_ptr; + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + name = controller->getName(); + type = DEVICE_TYPE_KEYBOARD; + vendor = "Lenovo"; + + if(LogManager::get()->getLoglevel() >= LL_TRACE) + { + DumpControllerInformation(); + } + + std::vector response; + + /*-----------------------*\ + |Default to ANSI keyboard | + \*-----------------------*/ + keyboard_type = ANSI; + + switch(controller->getPid()) + { + case LEGION_Y740: + response = controller->getInformation(0x01); + if(response.size() > 4 && response[4] <= 100) + { + chasis_size = FIFTEEN; + } + else + { + chasis_size = SEVENTEEN; + } + + response = controller->getInformation(0x04); + if(response.size() > 4) + { + if(response[4] >= 16 && response[4] <=48) + { + keyboard_type = ISO; + } + } + + description = "Lenovo Y740 " + sizeToString(chasis_size) + " " + keyboardToString(keyboard_type); + + break; + + case LEGION_Y750: + response = controller->getInformation(0x04); + if(response.size() > 4) + { + if(response[4] == 41) + { + keyboard_type = JAPAN; + } + else if(response[4] >= 16 && response[4] <=40) + { + keyboard_type = ISO; + } + } + + description = "Lenovo Y750 " + keyboardToString(keyboard_type); + + break; + + case LEGION_Y750S: + response = controller->getInformation(0x01); + if(response.size() > 4) + { + if(response[4] == 0x97) + { + keyboard_type = JAPAN; + } + else if(response[4] == 0x91) + { + keyboard_type = ISO; + } + } + + description = "Lenovo Y750S " + keyboardToString(keyboard_type); + + break; + + case LEGION_Y760: + response = controller->getInformation(0x07); + if(response.size() > 4) + { + if(response[4] == 41) + { + keyboard_type = JAPAN; + } + else if(response[4] >= 16 && response[4] <=40) + { + keyboard_type = ISO; + } + } + + description = "Lenovo Y760 " + keyboardToString(keyboard_type); + + break; + + case LEGION_Y760S: + response = controller->getInformation(0x01); + if(response.size() > 4) + { + if(response[4] == 0x97) + { + keyboard_type = JAPAN; + } + else if(response[4] == 0x8F) + { + keyboard_type = ISO; + } + } + + description = "Lenovo Y760S " + keyboardToString(keyboard_type); + } + + LOG_DEBUG("[Lenovo Controller] detected: %s", description.c_str()); + + SetupZones(); +} + +RGBController_LenovoUSB::~RGBController_LenovoUSB() +{ + /*--------------------------------*\ + | see LenovoUSBController.cpp for | + | details | + \*--------------------------------*/ + controller->setDeviceHardwareMode(); + + delete controller; +} + +void RGBController_LenovoUSB::SetupZones() +{ + vector lenovo_zones; + + switch(controller->getPid()) + { + case LEGION_Y740: + switch(chasis_size) + { + case FIFTEEN: + switch(keyboard_type) + { + case ISO: + lenovo_zones.push_back(lenovo_legion_Y740_15_kbd_iso); + break; + + default: + lenovo_zones.push_back(lenovo_legion_Y740_15_kbd_ansi); + break; + } + break; + + case SEVENTEEN: + default: + switch(keyboard_type) + { + case ISO: + lenovo_zones.push_back(lenovo_legion_Y740_17_kbd_iso); + break; + + default: + lenovo_zones.push_back(lenovo_legion_Y740_17_kbd_ansi); + break; + } + break; + } + lenovo_zones.push_back(lenovo_legion_Y740_logo); + lenovo_zones.push_back(lenovo_legion_Y740_pwrbtn); + lenovo_zones.push_back(lenovo_legion_Y740_vents); + lenovo_zones.push_back(lenovo_legion_Y740_ports); + break; + + case LEGION_Y750: + switch(keyboard_type) + { + case JAPAN: + lenovo_zones.push_back(lenovo_legion_Y760_kbd_jp); + break; + + case ISO: + lenovo_zones.push_back(lenovo_legion_Y760_kbd_iso); + break; + + default: + lenovo_zones.push_back(lenovo_legion_Y760_kbd_ansi); + break; + } + lenovo_zones.push_back(lenovo_legion_Y750_logo); + lenovo_zones.push_back(lenovo_legion_Y750_vents); + lenovo_zones.push_back(lenovo_legion_Y750_neon); + break; + case LEGION_Y750S: + case LEGION_Y760S: + switch(keyboard_type) + { + case JAPAN: + lenovo_zones.push_back(lenovo_legion_Y760_kbd_jp); + break; + + case ISO: + lenovo_zones.push_back(lenovo_legion_Y760_kbd_iso); + break; + + default: + lenovo_zones.push_back(lenovo_legion_Y760_kbd_ansi); + break; + } + break; + case LEGION_Y760: + switch(keyboard_type) + { + case JAPAN: + lenovo_zones.push_back(lenovo_legion_Y760_kbd_jp); + break; + + case ISO: + lenovo_zones.push_back(lenovo_legion_Y760_kbd_iso); + break; + + default: + lenovo_zones.push_back(lenovo_legion_Y760_kbd_ansi); + break; + } + lenovo_zones.push_back(lenovo_legion_Y760_logo); + lenovo_zones.push_back(lenovo_legion_Y760_vent_left); + lenovo_zones.push_back(lenovo_legion_Y760_vent_right); + lenovo_zones.push_back(lenovo_legion_Y760_vent_back_right); + lenovo_zones.push_back(lenovo_legion_Y760_vent_back_left); + lenovo_zones.push_back(lenovo_legion_Y760_neon); + break; + case LEGION_S7GEN7: + lenovo_zones.push_back(legion7_gen7and8_kbd_ansi); + break; + case LEGION_7GEN7: + lenovo_zones.push_back(legion7_gen7and8_kbd_ansi); + lenovo_zones.push_back(lenovo_legion_7gen7_logo); + lenovo_zones.push_back(lenovo_legion_7gen7_vents); + lenovo_zones.push_back(legion7_gen7and8_neon); + break; + case LEGION_7GEN8: + case LEGION_7GEN9: + case LEGION_7GEN9_H: + lenovo_zones.push_back(legion7_gen7and8_kbd_ansi); + lenovo_zones.push_back(legion7_gen7and8_neon); + break; + case LEGION_S7GEN8: + lenovo_zones.push_back(legion7_gen7and8_kbd_ansi); + break; + } + + for(unsigned int i = 0; i < lenovo_zones.size(); i++) + { + zone new_zone; + new_zone.name = lenovo_zones[i].name; + new_zone.type = lenovo_zones[i].type; + new_zone.leds_count = lenovo_zones[i].end - lenovo_zones[i].start + 1; + new_zone.leds_max = new_zone.leds_count; + new_zone.leds_min = new_zone.leds_count; + + LOG_DEBUG("[Lenovo Controller] adding zone: %s with %u LEDs", new_zone.name.c_str(), new_zone.leds_count); + + if(lenovo_zones[i].type == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = lenovo_zones[i].height; + new_zone.matrix_map->width = lenovo_zones[i].width; + new_zone.matrix_map->map = (unsigned int *) lenovo_zones[i].matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + for(unsigned int led_idx = lenovo_zones[i].start; led_idx <= lenovo_zones[i].end; led_idx++ ) + { + led new_led; + new_led.name = lenovo_zones[i].leds[led_idx].name; + new_led.value = ( lenovo_zones[i].id << 8 ) + lenovo_zones[i].leds[led_idx].led_num; + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_LenovoUSB::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LenovoUSB::UpdateSingleLED(int led) +{ + if(led != (int)NA) + { + controller->setSingleLED(leds[led].value >> 8, leds[led].value & 0xFF, colors[led]); + } +} + +void RGBController_LenovoUSB::UpdateZoneLEDs(int zone) +{ + uint8_t zone_id = zones[zone].leds_count > 0 ? leds[zones[zone].start_idx].value >> 8 : 0; + vector> color_map; + + for(unsigned int i = 0; i < zones[zone].leds_count; i++) + { + int index = zones[zone].start_idx+i; + + color_map.push_back({(uint8_t)leds[index].value & 0xFF, colors[index]}); + } + + color_map.shrink_to_fit(); + + controller->setZoneLeds(zone_id, color_map); +} + +void RGBController_LenovoUSB::DeviceUpdateLEDs() +{ + uint8_t zone_id = 0; + uint8_t prev_zone_id = 0; + vector> curr_color_map; + + for(unsigned int i = 0; i < leds.size(); i++) + { + zone_id = leds[i].value >> 8; + + if((zone_id != prev_zone_id) && (prev_zone_id != 0)) + { + controller->setZoneLeds(prev_zone_id, curr_color_map); + curr_color_map.clear(); + } + + prev_zone_id = zone_id; + + curr_color_map.push_back({(uint8_t)(leds[i].value & 0xFF), colors[i]}); + } + + if(curr_color_map.size() > 0) + { + controller->setZoneLeds(prev_zone_id, curr_color_map); + } +} + +void RGBController_LenovoUSB::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | This device does not support multiple modes | + \*---------------------------------------------------------*/ +} + +void RGBController_LenovoUSB::DeviceSaveMode() +{ + /*---------------------------------------------------------*\ + | This device does not support saving or multiple modes | + \*---------------------------------------------------------*/ +} + +std::string RGBController_LenovoUSB::ConvertBytesToHex(const std::vector &input) +{ + std::ostringstream temp_stream; + for(const uint8_t &oneInputByte : input) + { + temp_stream << (temp_stream.tellp()==0 ? "" : " ") << std::setw(2) << std::setfill('0') << std::hex << (int)oneInputByte; + } + return temp_stream.str(); +} + +std::string RGBController_LenovoUSB::keyboardToString(LENOVO_KEYBOARD kb) +{ + switch(kb) + { + case LENOVO_KEYBOARD::ANSI: + return "ANSI"; + case LENOVO_KEYBOARD::ISO: + return "ISO"; + case LENOVO_KEYBOARD::JAPAN: + return "JAPAN"; + default: + return "Unknown"; + } +} + +std::string RGBController_LenovoUSB::sizeToString(LENOVO_SIZE size) +{ + switch(size) + { + case LENOVO_SIZE::FIFTEEN: + return "15\""; + case LENOVO_SIZE::SEVENTEEN: + return "17\""; + default: + return "Unknown"; + } +} + +void RGBController_LenovoUSB::DumpControllerInformation() +{ + for(uint8_t i=1;i<=7;i++) + { + std::vector response = controller->getInformation(i); + LOG_TRACE("[Lenovo Controller] Read values [%02x]: %s", i, ConvertBytesToHex(response).c_str()); + } +} diff --git a/Controllers/LenovoControllers/LenovoUSBController/RGBController_LenovoUSB.h b/Controllers/LenovoControllers/LenovoUSBController/RGBController_LenovoUSB.h new file mode 100644 index 0000000..7a83dd8 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBController/RGBController_LenovoUSB.h @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| RGBController_Lenovo_USB.h | +| | +| RGBController for Lenovo USB devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "LenovoDevices.h" +#include "LenovoUSBController.h" +#include "RGBController.h" + +#define NA 0xFFFFFFFF + +class RGBController_LenovoUSB : public RGBController +{ +public: + RGBController_LenovoUSB(LenovoUSBController* controller_ptr); + ~RGBController_LenovoUSB(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + std::string ConvertBytesToHex(const std::vector &input); + std::string keyboardToString(LENOVO_KEYBOARD kb); + std::string sizeToString(LENOVO_SIZE size); + void DumpControllerInformation(); + + LENOVO_KEYBOARD keyboard_type; + LENOVO_SIZE chasis_size; + + LenovoUSBController *controller; +}; diff --git a/Controllers/LenovoControllers/LenovoUSBControllerDetect.cpp b/Controllers/LenovoControllers/LenovoUSBControllerDetect.cpp new file mode 100644 index 0000000..2d236bc --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBControllerDetect.cpp @@ -0,0 +1,70 @@ +/*---------------------------------------------------------*\ +| LenovoUSBControllerDetect.cpp | +| | +| Detector for Lenovo USB devices | +| | +| Cooper Hall (geobot19) 17 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "LenovoDevices.h" +#include "RGBController_LenovoUSB.h" +#include "RGBController_Lenovo_Gen7_8.h" + +/*-----------------------------------------------------*\ +| vendor IDs | +\*-----------------------------------------------------*/ +#define ITE_VID 0x048D + +/*-----------------------------------------------------*\ +| Interface, Usage, and Usage Page | +\*-----------------------------------------------------*/ +enum +{ + LENOVO_PAGE = 0xFF89, + LENOVO_USAGE = 0x07 +}; + +void DetectLenovoLegionUSBControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LenovoUSBController* controller = new LenovoUSBController(dev, info->path, info->product_id, name); + RGBController_LenovoUSB* rgb_controller = new RGBController_LenovoUSB(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectLenovoLegionUSBControllersGen7And8(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LenovoGen7And8USBController* controller = new LenovoGen7And8USBController(dev, info->path, info->product_id, name); + LenovoRGBController_Gen7_8* rgb_controller = new LenovoRGBController_Gen7_8(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("Lenovo Legion Y740", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_Y740, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 5", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_Y750, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7S Gen 5", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_Y750S, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 6", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_Y760, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7S Gen 6", DetectLenovoLegionUSBControllers, ITE_VID, LEGION_Y760S, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7S Gen 7", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_S7GEN7, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 7", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN7, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 8", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN8, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7S Gen 8", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_S7GEN8, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 9", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN9, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 9", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN9_H, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 7 Gen 10", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_7GEN10, LENOVO_PAGE, LENOVO_USAGE); +REGISTER_HID_DETECTOR_PU("Lenovo Legion 5 Gen 10", DetectLenovoLegionUSBControllersGen7And8, ITE_VID, LEGION_5GEN10, LENOVO_PAGE, LENOVO_USAGE); diff --git a/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/LenovoUSBController_Gen7_8.cpp b/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/LenovoUSBController_Gen7_8.cpp new file mode 100644 index 0000000..09a3ba0 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/LenovoUSBController_Gen7_8.cpp @@ -0,0 +1,376 @@ +/*---------------------------------------------------------*\ +| LenovoUSBController_Gen7_8.cpp | +| | +| Driver for Lenovo Gen7 and Gen8 devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "LenovoDevices.h" +#include "LenovoUSBController_Gen7_8.h" +#include "StringUtils.h" + +using namespace std; + +static void SetGen10PayloadLength(uint16_t pid, uint8_t* buffer, uint16_t payload_length) +{ + if(pid != LEGION_7GEN10 && pid != LEGION_5GEN10) + { + return; + } + + buffer[2] = payload_length & 0xFF; + buffer[3] = (payload_length >> 8) & 0xFF; +} + +static bool UsesGen10PacketFormat(uint16_t pid) +{ + return pid == LEGION_7GEN10 || pid == LEGION_5GEN10; +} + +LenovoGen7And8USBController::LenovoGen7And8USBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + pid = in_pid; + name = dev_name; +} + +LenovoGen7And8USBController::~LenovoGen7And8USBController() +{ + hid_close(dev); +} + +uint16_t LenovoGen7And8USBController::getPid() +{ + return pid; +} + +string LenovoGen7And8USBController::getName() +{ + return name; +} + +string LenovoGen7And8USBController::getLocation() +{ + return location; +} + +void LenovoGen7And8USBController::setLedsByGroup(uint8_t profile_id, vector led_groups) +{ + if(led_groups.empty()) + { + return; + } + + /*---------------------------------------------------------*\ + | Some devices require many groups for per-key updates. | + | Send as many groups as fit in one report, then continue | + | in additional reports. | + \*---------------------------------------------------------*/ + size_t group = 0; + while(group < led_groups.size()) + { + uint8_t buffer[PACKET_SIZE]; + memset(buffer, 0x00, PACKET_SIZE); + + size_t i = 0; + buffer[i++] = REPORT_ID; + buffer[i++] = SAVE_PROFILE; + buffer[i++] = 0xC0; + buffer[i++] = 0x03; + buffer[i++] = profile_id; + buffer[i++] = 0x01; + buffer[i++] = 0x01; + + for(; group < led_groups.size() && i < PACKET_SIZE - 21; group++) + { + buffer[i++] = (uint8_t)group + 1; //Group index + buffer[i++] = 0x06; + buffer[i++] = 0x01; + buffer[i++] = led_groups[group].mode; + buffer[i++] = 0x02; + buffer[i++] = led_groups[group].speed; + buffer[i++] = 0x03; + buffer[i++] = led_groups[group].spin; + buffer[i++] = 0x04; + buffer[i++] = led_groups[group].direction; + buffer[i++] = 0x05; + buffer[i++] = led_groups[group].color_mode; + buffer[i++] = 0x06; + buffer[i++] = 0x00; + + buffer[i++] = (uint8_t)led_groups[group].colors.size(); + for(RGBColor c : led_groups[group].colors) + { + buffer[i++] = RGBGetRValue(c); + buffer[i++] = RGBGetGValue(c); + buffer[i++] = RGBGetBValue(c); + } + + vector leds = led_groups[group].leds; + size_t led_count = min(leds.size(), (PACKET_SIZE - i)/2); + buffer[i++] = (uint8_t)led_count; + uint8_t* byte_ptr = reinterpret_cast(leds.data()); + std::copy(byte_ptr, byte_ptr + led_count * sizeof(uint16_t), buffer + i); + i+= led_count * sizeof(uint16_t); + } + + if(UsesGen10PacketFormat(pid)) + { + SetGen10PayloadLength(pid, buffer, static_cast(i - 4)); + } + else + { + buffer[2] = (uint8_t)i; + } + sendFeatureReport(buffer, PACKET_SIZE); + } +} + +void LenovoGen7And8USBController::setLedsDirectOn(uint8_t profile_id) +{ + uint8_t buffer[PACKET_SIZE]; + memset(buffer, 0x00, PACKET_SIZE); + + size_t i = 0; + buffer[i++] = REPORT_ID; + buffer[i++] = SET_DIRECT_MODE; + buffer[i++] = 0xC0; + buffer[i++] = 0x03; + buffer[i++] = 0x01; + buffer[i++] = profile_id; + + SetGen10PayloadLength(pid, buffer, 2); + sendFeatureReport(buffer, PACKET_SIZE); +} + +void LenovoGen7And8USBController::setLedsDirectOff(uint8_t profile_id) +{ + uint8_t buffer[PACKET_SIZE]; + memset(buffer, 0x00, PACKET_SIZE); + + size_t i = 0; + buffer[i++] = REPORT_ID; + buffer[i++] = SET_DIRECT_MODE; + buffer[i++] = 0xC0; + buffer[i++] = 0x03; + buffer[i++] = 0x02; + buffer[i++] = profile_id; + + SetGen10PayloadLength(pid, buffer, 2); + sendFeatureReport(buffer, PACKET_SIZE); +} + +void LenovoGen7And8USBController::setLedsDirect(std::vector &leds, std::vector &colors) +{ + if(UsesGen10PacketFormat(pid)) + { + /*---------------------------------------------------------*\ + | Gen10 uses 0x07/A1 direct updates, with payload length | + | stored in bytes 2-3. | + \*---------------------------------------------------------*/ + uint8_t buffer[PACKET_SIZE]; + memset(buffer, 0x00, PACKET_SIZE); + + size_t i = 0; + buffer[i++] = REPORT_ID; + buffer[i++] = DIRECT_MODE; + buffer[i++] = 0x00; + buffer[i++] = 0x00; + + size_t count = 0; + for(size_t index = 0; index < leds.size() && index < colors.size() && i + 5 <= PACKET_SIZE; index++) + { + buffer[i++] = leds[index].value & 0xFF; + buffer[i++] = leds[index].value >> 8 & 0xFF; + buffer[i++] = RGBGetRValue(colors[index]); + buffer[i++] = RGBGetGValue(colors[index]); + buffer[i++] = RGBGetBValue(colors[index]); + count++; + } + + SetGen10PayloadLength(pid, buffer, static_cast(count * 5)); + sendFeatureReport(buffer, PACKET_SIZE); + return; + } + + uint8_t buffer[PACKET_SIZE]; + memset(buffer, 0x00, PACKET_SIZE); + + size_t i = 0; + buffer[i++] = REPORT_ID; + buffer[i++] = DIRECT_MODE; + buffer[i++] = 0xC0; + buffer[i++] = 0x03; + + for(size_t index = 0; index < leds.size() && i < PACKET_SIZE; index++) + { + buffer[i++] = leds[index].value & 0xFF; + buffer[i++] = leds[index].value >> 8 & 0xFF; + buffer[i++] = RGBGetRValue(colors[index]); + buffer[i++] = RGBGetGValue(colors[index]); + buffer[i++] = RGBGetBValue(colors[index]); + } + + sendFeatureReport(buffer, PACKET_SIZE); +} + +void LenovoGen7And8USBController::setLedsAllOff(uint8_t profile_id) +{ + uint8_t buffer[PACKET_SIZE] = {REPORT_ID, SAVE_PROFILE, 0xC0, 0x03, profile_id, 0x01, 0x01}; + + SetGen10PayloadLength(pid, buffer, 3); + sendFeatureReport(buffer, PACKET_SIZE); +} + +uint8_t LenovoGen7And8USBController::getCurrentProfileId() +{ + uint8_t buffer[PACKET_SIZE] = {REPORT_ID, GET_ACTIVE_PROFILE, 0xC0, 0x03}; + + SetGen10PayloadLength(pid, buffer, 1); + vector response = getFeatureReport(buffer, PACKET_SIZE); + + return response.size()>4?response[4]:0x01; +} + +uint8_t LenovoGen7And8USBController::getCurrentBrightness() +{ + uint8_t buffer[PACKET_SIZE] = {REPORT_ID, GET_BRIGHTNESS, 0xC0, 0x03}; + + SetGen10PayloadLength(pid, buffer, 1); + vector response = getFeatureReport(buffer, PACKET_SIZE); + + return response.size()>4?response[4]:0x00; +} + + +void LenovoGen7And8USBController::setBrightness(uint8_t brightness) +{ + uint8_t buffer[PACKET_SIZE] = {REPORT_ID, SET_BRIGHTNESS, 0xC0, 0x03, brightness}; + + SetGen10PayloadLength(pid, buffer, 1); + sendFeatureReport(buffer, PACKET_SIZE); +} + +void LenovoGen7And8USBController::switchProfileTo(uint8_t profile_id) +{ + uint8_t buffer[PACKET_SIZE] = {REPORT_ID, SWITCH_PROFILE, 0xC0, 0x03, profile_id}; + + SetGen10PayloadLength(pid, buffer, 1); + sendFeatureReport(buffer, PACKET_SIZE); +} + +std::vector LenovoGen7And8USBController::getProfileSettings(uint8_t profile_id) +{ + uint8_t buffer[PACKET_SIZE] = {REPORT_ID, GET_PROFILE, 0xC0, 0x03, profile_id}; + + SetGen10PayloadLength(pid, buffer, PACKET_SIZE - 4); + vector response = getFeatureReport(buffer, PACKET_SIZE); + + vector groups; + + size_t i = 7; + while(i < response.size() && response[i] != 0x00){ + i++; + + led_group group; + + /*-----------------*\ + |read group settings| + \*-----------------*/ + + size_t cnt = response[i++]; + for(size_t j = 0; j < cnt && i < response.size(); j++, i+=2) + { + switch(response[i]) + { + case 0x01: + group.mode = response[i+1]; + break; + case 0x02: + group.speed = response[i+1]; + break; + case 0x03: + group.spin = response[i+1]; + break; + case 0x04: + group.direction = response[i+1]; + break; + case 0x05: + group.color_mode = response[i+1]; + break; + case 0x06: + //group.mode = response[i+1]; + break; + } + } + + /*-----------------*\ + |read group colors | + \*-----------------*/ + + cnt = response[i++]; + for(size_t j = 0; j < cnt && i < response.size(); j++, i+=3) + { + group.colors.push_back(ToRGBColor(response[i],response[i+1],response[i+2])); + } + + /*-----------------*\ + |read group LEDs | + \*-----------------*/ + + cnt = response[i++]; + for(size_t j = 0; j < cnt && i < response.size(); j++, i+=2) + { + group.leds.push_back(response[i] | response[i+1] << 8); + } + + groups.push_back(group); + } + + return groups; + +} + +void LenovoGen7And8USBController::sendFeatureReport(uint8_t packet[], size_t packet_size) +{ + hid_send_feature_report(dev, packet, packet_size); + LOG_TRACE("[Lenovo Gen 7 Controller] Buffer: %s", ConvertBytesToHex(packet, packet_size).c_str()); +} + +std::vector LenovoGen7And8USBController::getFeatureReport(uint8_t packet[], size_t packet_size) +{ + sendFeatureReport(packet, packet_size); + + uint8_t read_buffer[PACKET_SIZE] = {REPORT_ID}; + int num_bytes = 0; + num_bytes = hid_get_feature_report(dev, read_buffer, sizeof(read_buffer)); + + vector response = {}; + if(num_bytes > 0) + { + response.insert(response.begin(), read_buffer, read_buffer + num_bytes); + } + + LOG_TRACE("[Lenovo Gen 7 Controller] Read Buffer: %s", ConvertBytesToHex(response).c_str()); + return response; +} + +std::string LenovoGen7And8USBController::ConvertBytesToHex(uint8_t packet[], size_t packet_size) +{ + return ConvertBytesToHex(std::vector(packet, packet + packet_size)); +} + +std::string LenovoGen7And8USBController::ConvertBytesToHex(const std::vector &input) +{ + std::ostringstream temp_stream; + for(const uint8_t &oneInputByte : input) + { + temp_stream << (temp_stream.tellp()==0 ? "" : " ") << std::setw(2) << std::setfill('0') << std::hex << (int)oneInputByte; + } + return temp_stream.str(); +} diff --git a/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/LenovoUSBController_Gen7_8.h b/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/LenovoUSBController_Gen7_8.h new file mode 100644 index 0000000..4ebda58 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/LenovoUSBController_Gen7_8.h @@ -0,0 +1,92 @@ +/*---------------------------------------------------------*\ +| LenovoUSBController_Gen7_8.h | +| | +| Driver for Lenovo Gen7 and Gen8 devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include "RGBController.h" +#include "LogManager.h" + +#ifndef HID_MAX_STR +#define HID_MAX_STR 255 +#endif + +#define PACKET_SIZE 960 +#define REPORT_ID 0x07 +#define DIRECT_MODE 0xA1 +#define SWITCH_PROFILE 0xC8 +#define GET_ACTIVE_PROFILE 0xCA +#define SAVE_PROFILE 0xCB +#define GET_PROFILE 0xCC +#define GET_BRIGHTNESS 0xCD +#define SET_BRIGHTNESS 0xCE +#define SET_DIRECT_MODE 0xD0 +#define GET_DIRECT_MODE_PROFILE 0xD1 + +struct led_group +{ + uint8_t mode; + uint8_t speed; + uint8_t spin; + uint8_t direction; + uint8_t color_mode; + std::vector colors; + std::vector leds; +}; + +class LenovoGen7And8USBController +{ + + public: + /*--------------*\ + |ctor(s) and dtor| + \*--------------*/ + LenovoGen7And8USBController(hid_device* dev_handle, const char* path, uint16_t in_pid, std::string dev_name); + ~LenovoGen7And8USBController(); + + /*--------------*\ + |device functions| + \*--------------*/ + void setLedsByGroup(uint8_t profile_id, std::vector led_groups); + void setLedsDirect(std::vector &leds, std::vector &colors); + void setLedsAllOff(uint8_t profile_id); + void setLedsDirectOn(uint8_t profile_id); + void setLedsDirectOff(uint8_t profile_id); + uint16_t getPid(); + std::string getName(); + std::string getLocation(); + uint8_t getCurrentProfileId(); + uint8_t getCurrentBrightness(); + void setBrightness(uint8_t brightness); + uint8_t getKeyboardId(); + void switchProfileTo(uint8_t profile_id); + std::vector getProfileSettings(uint8_t profile_id); + + + private: + /*--------------*\ + |data members | + \*--------------*/ + std::string name; + hid_device *dev; + std::string location; + uint16_t pid; + + /*--------------*\ + |device functions| + \*--------------*/ + void sendFeatureReport(uint8_t packet[], size_t packet_size); + std::vector getFeatureReport(uint8_t packet[], size_t packet_size); + std::string ConvertBytesToHex(uint8_t packet[], size_t packet_size); + std::string ConvertBytesToHex(const std::vector &input); +}; diff --git a/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/RGBController_Lenovo_Gen7_8.cpp b/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/RGBController_Lenovo_Gen7_8.cpp new file mode 100644 index 0000000..f037bff --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/RGBController_Lenovo_Gen7_8.cpp @@ -0,0 +1,820 @@ +/*---------------------------------------------------------*\ +| RGBController_Lenovo_Gen7_8.cpp | +| | +| RGBController for Lenovo Gen7 and Gen8 devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_Lenovo_Gen7_8.h" +#include "LenovoDevices.h" + +using namespace std; + +static bool UsesGen10PacketFormat(uint16_t pid) +{ + return pid == LEGION_7GEN10 || pid == LEGION_5GEN10; +} + +static bool Is24ZoneDevice(uint16_t pid) +{ + return pid == LEGION_5GEN10; +} + +static bool IsKeyboardOnlyDevice(uint16_t pid) +{ + return pid == LEGION_5GEN10; +} + +static const lenovo_led legion_5gen10_24zone_leds[] = +{ + {0x01, "Zone R1C1"}, + {0x02, "Zone R1C2"}, + {0x03, "Zone R1C3"}, + {0x04, "Zone R1C4"}, + {0x05, "Zone R1C5"}, + {0x06, "Zone R1C6"}, + {0x07, "Zone R2C1"}, + {0x08, "Zone R2C2"}, + {0x09, "Zone R2C3"}, + {0x0A, "Zone R2C4"}, + {0x0B, "Zone R2C5"}, + {0x0C, "Zone R2C6"}, + {0x0D, "Zone R3C1"}, + {0x0E, "Zone R3C2"}, + {0x0F, "Zone R3C3"}, + {0x10, "Zone R3C4"}, + {0x11, "Zone R3C5"}, + {0x12, "Zone R3C6"}, + {0x13, "Zone R4C1"}, + {0x14, "Zone R4C2"}, + {0x15, "Zone R4C3"}, + {0x16, "Zone R4C4"}, + {0x17, "Zone R4C5"}, + {0x18, "Zone R4C6"}, +}; + +static const unsigned int legion_5gen10_24zone_matrix_map[] = +{ + 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, +}; + +static const lenovo_zone legion_5gen10_kbd_24zone = +{ + "Keyboard (24-zone)", + ZONE_TYPE_MATRIX, + 0, + 4, + 6, + legion_5gen10_24zone_matrix_map, + legion_5gen10_24zone_leds, + 0, + 23, +}; + +static const RGBColor legion_5gen10_zone_visualization_colors[24] = +{ + 0xFF0000, 0x00FFFF, 0xFFFF00, 0x0000FF, 0x00FF00, 0xFF00FF, + 0xFF7F00, 0x007FFF, 0x7FFF00, 0x7F00FF, 0xFF007F, 0x00FF7F, + 0xFFD700, 0x0055FF, 0x55FF00, 0xFF0055, 0x00BFFF, 0xBF00FF, + 0xFF3B00, 0x00FF3B, 0x3B00FF, 0xFF1493, 0x14FF93, 0x9314FF, +}; + +LenovoRGBController_Gen7_8::LenovoRGBController_Gen7_8(LenovoGen7And8USBController* controller_ptr) +{ + controller = controller_ptr; + + mode Screw; + Screw.name = "Screw Rainbow"; + Screw.value = LENOVO_LEGION_GEN7_8_MODE_SCREW_RAINBOW; + Screw.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_DIRECTION_LR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_AUTOMATIC_SAVE; + Screw.speed_min = 0x01; + Screw.speed_max = 0x03; + Screw.speed = 2; + Screw.color_mode = MODE_COLORS_NONE; + Screw.brightness_min = 0; + Screw.brightness_max = 9; + Screw.brightness = brightness; + Screw.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Screw); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = LENOVO_LEGION_GEN7_8_MODE_RAINBOW_WAVE; + Rainbow.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_DIRECTION_LR | + MODE_FLAG_HAS_DIRECTION_UD | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.speed_min = 0x01; + Rainbow.speed_max = 0x03; + Rainbow.speed = 2; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness_min = 0; + Rainbow.brightness_max = 9; + Rainbow.brightness = brightness; + Rainbow.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Rainbow); + + mode ColorChange; + ColorChange.name = "Color Change"; + ColorChange.value = LENOVO_LEGION_GEN7_8_MODE_COLOR_CHANGE; + ColorChange.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_AUTOMATIC_SAVE; + ColorChange.speed_min = 0x01; + ColorChange.speed_max = 0x03; + ColorChange.speed = 2; + ColorChange.colors_min = 1; + ColorChange.colors_max = 4; + ColorChange.colors.resize(4); + ColorChange.colors[0] = 0xFFF500; + ColorChange.color_mode = MODE_COLORS_RANDOM; + ColorChange.brightness_min = 0; + ColorChange.brightness_max = 9; + ColorChange.brightness = brightness; + modes.push_back(ColorChange); + + mode ColorPulse; + ColorPulse.name = "Color Pulse"; + ColorPulse.value = LENOVO_LEGION_GEN7_8_MODE_COLOR_PULSE; + ColorPulse.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_AUTOMATIC_SAVE; + ColorPulse.speed_min = 0x01; + ColorPulse.speed_max = 0x03; + ColorPulse.speed = 2; + ColorPulse.colors_min = 1; + ColorPulse.colors_max = 4; + ColorPulse.colors.resize(4); + ColorPulse.colors[0] = 0xFFF500; + ColorPulse.color_mode = MODE_COLORS_RANDOM; + ColorPulse.brightness_min = 0; + ColorPulse.brightness_max = 9; + ColorPulse.brightness = brightness; + modes.push_back(ColorPulse); + + mode ColorWave; + ColorWave.name = "Color Wave"; + ColorWave.value = LENOVO_LEGION_GEN7_8_MODE_COLOR_WAVE; + ColorWave.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_DIRECTION_LR | + MODE_FLAG_HAS_DIRECTION_UD | + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_AUTOMATIC_SAVE; + ColorWave.speed_min = 0x01; + ColorWave.speed_max = 0x03; + ColorWave.speed = 2; + ColorWave.colors_min = 1; + ColorWave.colors_max = 4; + ColorWave.colors.resize(4); + ColorWave.colors[0] = 0xFFF500; + ColorWave.color_mode = MODE_COLORS_RANDOM; + ColorWave.brightness_min = 0; + ColorWave.brightness_max = 9; + ColorWave.brightness = brightness; + ColorWave.direction = MODE_DIRECTION_RIGHT; + modes.push_back(ColorWave); + + mode Smooth; + Smooth.name = "Smooth"; + Smooth.value = LENOVO_LEGION_GEN7_8_MODE_SMOOTH; + Smooth.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_AUTOMATIC_SAVE; + Smooth.speed_min = 0x01; + Smooth.speed_max = 0x03; + Smooth.speed = 2; + Smooth.colors_min = 1; + Smooth.colors_max = 4; + Smooth.colors.resize(4); + Smooth.colors[0] = 0xFFF500; + Smooth.color_mode = MODE_COLORS_RANDOM; + Smooth.brightness_min = 0; + Smooth.brightness_max = 9; + Smooth.brightness = brightness; + modes.push_back(Smooth); + + if(!Is24ZoneDevice(controller->getPid())) + { + mode Rain; + Rain.name = "Rain"; + Rain.value = LENOVO_LEGION_GEN7_8_MODE_RAIN; + Rain.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_AUTOMATIC_SAVE; + Rain.speed_min = 0x01; + Rain.speed_max = 0x03; + Rain.speed = 2; + Rain.colors_min = 1; + Rain.colors_max = 4; + Rain.colors.resize(4); + Rain.colors[0] = 0xFFF500; + Rain.color_mode = MODE_COLORS_RANDOM; + Rain.brightness_min = 0; + Rain.brightness_max = 9; + Rain.brightness = brightness; + modes.push_back(Rain); + } + + if(!IsKeyboardOnlyDevice(controller->getPid())) + { + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = LENOVO_LEGION_GEN7_8_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_AUTOMATIC_SAVE; + Ripple.speed_min = 0x01; + Ripple.speed_max = 0x03; + Ripple.speed = 2; + Ripple.colors_min = 1; + Ripple.colors_max = 4; + Ripple.colors.resize(4); + Ripple.colors[0] = 0xFFF500; + Ripple.color_mode = MODE_COLORS_RANDOM; + Ripple.brightness_min = 0; + Ripple.brightness_max = 9; + Ripple.brightness = brightness; + modes.push_back(Ripple); + + mode AudioBounce; + AudioBounce.name = "Audio Bounce Lighting"; + AudioBounce.value = LENOVO_LEGION_GEN7_8_MODE_AUDIO_BOUNCE; + AudioBounce.flags = MODE_FLAG_HAS_BRIGHTNESS; + AudioBounce.color_mode = MODE_COLORS_NONE; + AudioBounce.brightness_min = 0; + AudioBounce.brightness_max = 9; + AudioBounce.brightness = brightness; + modes.push_back(AudioBounce); + + mode AudioRipple; + AudioRipple.name = "Audio Ripple Lighting"; + AudioRipple.value = LENOVO_LEGION_GEN7_8_MODE_AUDIO_RIPPLE; + AudioRipple.flags = MODE_FLAG_HAS_BRIGHTNESS; + AudioRipple.color_mode = MODE_COLORS_NONE; + AudioRipple.brightness_min = 0; + AudioRipple.brightness_max = 9; + AudioRipple.brightness = brightness; + modes.push_back(AudioRipple); + } + + mode Static; + Static.name = "Static"; + Static.value = LENOVO_LEGION_GEN7_8_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | + MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = 0; + Static.brightness_max = 9; + Static.brightness = brightness; + modes.push_back(Static); + + if(!Is24ZoneDevice(controller->getPid())) + { + mode Type; + Type.name = "Type Lighting"; + Type.value = LENOVO_LEGION_GEN7_8_MODE_TYPE; + Type.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_AUTOMATIC_SAVE; + Type.speed_min = 0x01; + Type.speed_max = 0x03; + Type.speed = 2; + Type.colors_min = 1; + Type.colors_max = 4; + Type.colors.resize(4); + Type.colors[0] = 0xFFF500; + Type.color_mode = MODE_COLORS_RANDOM; + Type.brightness_min = 0; + Type.brightness_max = 9; + Type.brightness = brightness; + modes.push_back(Type); + } + + if(!IsKeyboardOnlyDevice(controller->getPid())) + { + mode Direct; + Direct.name = "Direct"; + Direct.value = LENOVO_LEGION_GEN7_8_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | + MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 9; + Direct.brightness = brightness; + modes.push_back(Direct); + } + + name = controller->getName(); + type = DEVICE_TYPE_KEYBOARD; + vendor = "Lenovo"; + + switch (controller->getPid()) + { + case LEGION_S7GEN7: + description = "Lenovo Legion 7 Slim Generation 7"; + break; + + case LEGION_7GEN7: + description = "Lenovo Legion 7 Generation 7"; + break; + + case LEGION_S7GEN8: + description = "Lenovo Legion 7 Slim Generation 8"; + break; + + case LEGION_7GEN8: + description = "Lenovo Legion 7 Generation 8"; + break; + + case LEGION_7GEN9: + case LEGION_7GEN9_H: + description = "Lenovo Legion 7 Generation 9"; + break; + + case LEGION_7GEN10: + description = "Lenovo Legion 7 Generation 10"; + break; + + case LEGION_5GEN10: + description = "Lenovo Legion 5 Gen 10"; + break; + } + + brightness = controller->getCurrentBrightness(); + profile_id = controller->getCurrentProfileId(); + for(mode &m : modes) + { + m.brightness = brightness; + } + + SetupZones(); + + /*-----------------------------------------------------*\ + | Initiliaze Static | + \*-----------------------------------------------------*/ + active_mode = 0; + for(size_t i = 0; i < modes.size(); i++) + { + if(modes[i].value == LENOVO_LEGION_GEN7_8_MODE_STATIC) + { + active_mode = (int)i; + break; + } + } + ReadDeviceSettings(); + last_mode = active_mode; +} + +LenovoRGBController_Gen7_8::~LenovoRGBController_Gen7_8() +{ + delete controller; +} + +void LenovoRGBController_Gen7_8::SetupZones() +{ + vector lenovo_zones; + if(Is24ZoneDevice(controller->getPid())) + { + lenovo_zones.push_back(legion_5gen10_kbd_24zone); + } + else + { + lenovo_zones.push_back(legion7_gen7and8_kbd_ansi); + } + + if(!IsKeyboardOnlyDevice(controller->getPid())) + { + lenovo_zones.push_back(legion7_gen7and8_neon); + } + + if (controller->getPid() == LEGION_7GEN7) + { + lenovo_zones.push_back(lenovo_legion_7gen7_logo); + lenovo_zones.push_back(lenovo_legion_7gen7_vents); + } + + if (controller->getPid() == LEGION_7GEN10) + { + lenovo_zones.push_back(lenovo_legion_7gen7_logo); + lenovo_zones.push_back(lenovo_legion_7gen10_vents); + } + + for(unsigned int i = 0; i < lenovo_zones.size(); i++) + { + zone new_zone; + new_zone.name = lenovo_zones[i].name; + new_zone.type = lenovo_zones[i].type; + new_zone.leds_count = lenovo_zones[i].end - lenovo_zones[i].start + 1; + new_zone.leds_max = new_zone.leds_count; + new_zone.leds_min = new_zone.leds_count; + + LOG_DEBUG("[Lenovo Gen7/8 Controller] adding zone: %s with %u LEDs", new_zone.name.c_str(), new_zone.leds_count); + + if(lenovo_zones[i].type == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = lenovo_zones[i].height; + new_zone.matrix_map->width = lenovo_zones[i].width; + new_zone.matrix_map->map = new unsigned int[new_zone.matrix_map->height * new_zone.matrix_map->width]; + + if(lenovo_zones[i].matrix_map != NULL) + { + new_zone.matrix_map->map = (unsigned int *) lenovo_zones[i].matrix_map; + } + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + for(unsigned int led_idx = lenovo_zones[i].start; led_idx <= lenovo_zones[i].end; led_idx++ ) + { + led new_led; + new_led.name = lenovo_zones[i].leds[led_idx].name; + new_led.value = lenovo_zones[i].id << 8 | lenovo_zones[i].leds[led_idx].led_num ; + leds.push_back(new_led); + + /*---------------------------------------------*\ + | create led id to index map for fast look up | + \*---------------------------------------------*/ + led_id_to_index[new_led.value]=leds.size() - 1; + } + } + + SetupColors(); +} + +void LenovoRGBController_Gen7_8::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void LenovoRGBController_Gen7_8::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void LenovoRGBController_Gen7_8::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void LenovoRGBController_Gen7_8::DeviceUpdateMode() +{ + uint8_t hw_profile_id = controller->getCurrentProfileId(); + if(hw_profile_id != profile_id) + { + profile_id = hw_profile_id; + ReadDeviceSettings(); + last_mode = active_mode; + direct_enabled = false; + } + + if(brightness != modes[active_mode].brightness) + { + brightness = modes[active_mode].brightness; + controller->setBrightness(brightness); + for(mode &m : modes) + { + m.brightness = brightness; + } + } + + if(last_mode != active_mode) + { + if(modes[last_mode].value == LENOVO_LEGION_GEN7_8_MODE_DIRECT) + { + controller->setLedsDirectOff(profile_id); + direct_enabled = false; + } + + if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_DIRECT) + { + controller->setLedsDirectOn(profile_id); + direct_enabled = true; + if(!UsesGen10PacketFormat(controller->getPid())) + { + controller->setLedsByGroup(profile_id, GetLedGroups()); + } + } + + last_mode = active_mode; + } + else if((modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_DIRECT) && !direct_enabled) + { + controller->setLedsDirectOn(profile_id); + direct_enabled = true; + } + + if(modes[active_mode].value != LENOVO_LEGION_GEN7_8_MODE_DIRECT) + { + DeviceUpdateLEDs(); + } +} + +void LenovoRGBController_Gen7_8::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_DIRECT) + { + if(UsesGen10PacketFormat(controller->getPid())) + { + /*---------------------------------------------*\ + | Gen10 may ignore A1 updates unless D0 is | + | reasserted. | + \*---------------------------------------------*/ + controller->setLedsDirectOn(profile_id); + direct_enabled = true; + } + else if(!direct_enabled) + { + controller->setLedsDirectOn(profile_id); + direct_enabled = true; + } + controller->setLedsDirect(leds, colors); + } + else + { + controller->setLedsByGroup(profile_id, GetLedGroups()); + } + +} + +void LenovoRGBController_Gen7_8::ReadDeviceSettings() +{ + vector current_settings = controller->getProfileSettings(profile_id); + if(current_settings.size() > 0) + { + for(unsigned int i = 0; i < modes.size(); i++) + { + if(modes[i].value == current_settings[0].mode) + { + switch(current_settings[0].color_mode) + { + case 0x02: + if(modes[i].flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + modes[i].color_mode = MODE_COLORS_PER_LED; + } + else + { + modes[i].color_mode = MODE_COLORS_MODE_SPECIFIC; + } + break; + + case 0x01: + modes[i].color_mode = MODE_COLORS_RANDOM; + break; + + default: + modes[i].color_mode = MODE_COLORS_NONE; + } + + switch(modes[i].color_mode) + { + case MODE_COLORS_PER_LED: + for(size_t j=0; j < colors.size(); j++) + { + colors[j]=0x00; + } + for(const led_group &lg : current_settings) + { + if(lg.colors.size() > 0) + { + for(uint16_t led_id : lg.leds) + { + if(auto search = led_id_to_index.find(led_id); search != led_id_to_index.end()) + { + colors[search->second] = lg.colors[0]; + } + } + } + } + break; + + case MODE_COLORS_MODE_SPECIFIC: + for(size_t j=0; j < modes[i].colors.size(); j++) + { + if(j < current_settings[0].colors.size()) + { + modes[i].colors[j] = current_settings[0].colors[j]; + } + else + { + modes[i].colors[j] = 0x00; + } + } + } + + switch(current_settings[0].direction) + { + case 0x01: + modes[i].direction = MODE_DIRECTION_UP; + break; + case 0x02: + modes[i].direction = MODE_DIRECTION_DOWN; + break; + case 0x03: + modes[i].direction = MODE_DIRECTION_LEFT; + break; + case 0x04: + modes[i].direction = MODE_DIRECTION_RIGHT; + break; + } + + switch(current_settings[0].spin) + { + case 0x01: + modes[i].direction = MODE_DIRECTION_RIGHT; + break; + case 0x02: + modes[i].direction = MODE_DIRECTION_LEFT; + break; + } + + active_mode = i; + break; //stop for loop + } + } + } +} + +std::vector LenovoRGBController_Gen7_8::GetLedGroups() +{ + if(Is24ZoneDevice(controller->getPid()) && + modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_STATIC && + modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + vector led_groups; + led_groups.reserve(leds.size()); + + for(size_t i = 0; i < leds.size(); i++) + { + led_group group; + group.mode = modes[active_mode].value; + group.speed = modes[active_mode].speed; + group.spin = 0x00; + group.direction = 0x00; + group.color_mode = 0x02; + group.colors.push_back(legion_5gen10_zone_visualization_colors[i % 24]); + group.leds.push_back(leds[i].value); + led_groups.push_back(group); + } + + return led_groups; + } + + std::unordered_map> led_map; + + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED && + modes[active_mode].value != LENOVO_LEGION_GEN7_8_MODE_DIRECT) + { + for(size_t i = 0; i < leds.size(); i++) + { + led_map[colors[i]].push_back(leds[i].value); + } + } + else + { + size_t start = 0; + size_t end = leds.size(); + + /*-------------------------------------------------*\ + | Riplle and Type only apply to keyboard | + \*-------------------------------------------------*/ + if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_RIPPLE || + modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_TYPE) + { + for(const zone &z : zones) + { + if(z.name == "Keyboard") + { + start = z.start_idx; + end = start + z.leds_count; + } + } + + } + + for(size_t i = start; i < end; i++) + { + led_map[0x00].push_back(leds[i].value); + } + } + + vector led_groups; + for(const auto &pair : led_map) + { + if(pair.first == 0x00 && led_map.size() != 1) + { + continue; + } + + led_group group; + group.mode = modes[active_mode].value; + group.speed = modes[active_mode].speed; + group.spin = 0x00; + group.direction = 0x00; + + switch(modes[active_mode].direction) + { + case MODE_DIRECTION_UP: + group.direction = 0x01; + break; + + case MODE_DIRECTION_DOWN: + group.direction = 0x02; + break; + + case MODE_DIRECTION_LEFT: + if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_SCREW_RAINBOW) + { + group.spin = 0x02; + } + else + { + group.direction = 0x03; + } + break; + + case MODE_DIRECTION_RIGHT: + if(modes[active_mode].value == LENOVO_LEGION_GEN7_8_MODE_SCREW_RAINBOW) + { + group.spin = 0x01; + } + else + { + group.direction = 0x04; + } + break; + } + + switch(modes[active_mode].color_mode) + { + default: + case MODE_COLORS_NONE: + group.color_mode = 0x00; + break; + + case MODE_COLORS_RANDOM: + group.color_mode = 0x01; + break; + + case MODE_COLORS_MODE_SPECIFIC: + group.color_mode = 0x02; + for(RGBColor c : modes[active_mode].colors) + { + if(c) + { + group.colors.push_back(c); + } + } + + if(group.colors.size() == 0) + { + group.colors.push_back(0xFFF500); + } + break; + + case MODE_COLORS_PER_LED: + group.color_mode = 0x02; + group.colors.push_back(pair.first); + break; + } + + group.leds = pair.second; + + led_groups.push_back(group); + } + + return led_groups; +} diff --git a/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/RGBController_Lenovo_Gen7_8.h b/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/RGBController_Lenovo_Gen7_8.h new file mode 100644 index 0000000..5af9304 --- /dev/null +++ b/Controllers/LenovoControllers/LenovoUSBController_Gen7_8/RGBController_Lenovo_Gen7_8.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------*\ +| RGBController_Lenovo_Gen7_8.h | +| | +| RGBController for Lenovo Gen7 and Gen8 devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LenovoUSBController_Gen7_8.h" + +enum +{ + LENOVO_LEGION_GEN7_8_MODE_SCREW_RAINBOW = 0x01, + LENOVO_LEGION_GEN7_8_MODE_RAINBOW_WAVE = 0x02, + LENOVO_LEGION_GEN7_8_MODE_COLOR_CHANGE = 0x03, + LENOVO_LEGION_GEN7_8_MODE_COLOR_PULSE = 0x04, + LENOVO_LEGION_GEN7_8_MODE_COLOR_WAVE = 0x05, + LENOVO_LEGION_GEN7_8_MODE_SMOOTH = 0x06, + LENOVO_LEGION_GEN7_8_MODE_RAIN = 0x07, + LENOVO_LEGION_GEN7_8_MODE_RIPPLE = 0x08, + LENOVO_LEGION_GEN7_8_MODE_AUDIO_BOUNCE = 0x09, + LENOVO_LEGION_GEN7_8_MODE_AUDIO_RIPPLE = 0x0A, + LENOVO_LEGION_GEN7_8_MODE_STATIC = 0x0B, + LENOVO_LEGION_GEN7_8_MODE_TYPE = 0x0C, + LENOVO_LEGION_GEN7_8_MODE_DIRECT = 0x0D, +}; + +class LenovoRGBController_Gen7_8 : public RGBController +{ +public: + LenovoRGBController_Gen7_8(LenovoGen7And8USBController* controller_ptr); + ~LenovoRGBController_Gen7_8(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LenovoGen7And8USBController* controller; + std::vector GetLedGroups(); + void ReadDeviceSettings(); + std::unordered_map led_id_to_index; + int last_mode = 0; + bool direct_enabled = false; + uint8_t brightness = 0x00; + uint8_t profile_id = 0x01; +}; diff --git a/Controllers/LenovoMotherboardController/LenovoMotherboardController.cpp b/Controllers/LenovoMotherboardController/LenovoMotherboardController.cpp new file mode 100644 index 0000000..2585890 --- /dev/null +++ b/Controllers/LenovoMotherboardController/LenovoMotherboardController.cpp @@ -0,0 +1,89 @@ +/*---------------------------------------------------------*\ +| LenovoMotherboardController.cpp | +| | +| Driver for Lenovo motherboard | +| | +| Morgan Guimard (morg) 26 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LenovoMotherboardController.h" +#include "StringUtils.h" + +LenovoMotherboardController::LenovoMotherboardController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +LenovoMotherboardController::~LenovoMotherboardController() +{ + hid_close(dev); +} + +std::string LenovoMotherboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LenovoMotherboardController::GetNameString() +{ + return(name); +} + +std::string LenovoMotherboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LenovoMotherboardController::SetMode(uint8_t zone, uint8_t mode, uint8_t brightness, uint8_t speed, RGBColor color) +{ + uint8_t r = RGBGetRValue(color); + uint8_t g = RGBGetGValue(color); + uint8_t b = RGBGetBValue(color); + + uint8_t buffer[LENOVO_MB_PACKET_LENGTH]; + memset(buffer, 0x00, LENOVO_MB_PACKET_LENGTH); + + buffer[0] = LENOVO_MB_REPORT_ID; + + buffer[1] = zone; + buffer[2] = mode; + buffer[3] = speed; + buffer[4] = brightness; + + buffer[5] = r; + buffer[6] = g; + buffer[7] = b; + + hid_send_feature_report(dev, buffer, LENOVO_MB_PACKET_LENGTH); + + memset(buffer, 0x00, LENOVO_MB_PACKET_LENGTH); + + buffer[0] = LENOVO_MB_REPORT_ID; + buffer[1] = 0x28; + buffer[2] = 0x06; + + buffer[33] = zone; + buffer[34] = mode; + buffer[35] = speed; + buffer[36] = brightness; + + buffer[37] = r; + buffer[38] = g; + buffer[39] = b; + + hid_send_feature_report(dev, buffer, LENOVO_MB_PACKET_LENGTH); +} diff --git a/Controllers/LenovoMotherboardController/LenovoMotherboardController.h b/Controllers/LenovoMotherboardController/LenovoMotherboardController.h new file mode 100644 index 0000000..6d7e7d3 --- /dev/null +++ b/Controllers/LenovoMotherboardController/LenovoMotherboardController.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| LenovoMotherboardController.h | +| | +| Driver for Lenovo motherboard | +| | +| Morgan Guimard (morg) 26 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define LENOVO_MB_PACKET_LENGTH 64 +#define LENOVO_MB_REPORT_ID 0xCC +#define LENOVO_MB_NUMBER_OF_LEDS 2 +#define LENOVO_MB_ZONE_1_VALUE 0x12 +#define LENOVO_MB_ZONE_2_VALUE 0x11 + +enum +{ + LENOVO_MB_STATIC_MODE = 0x01, + LENOVO_MB_SPARKLE_MODE = 0x02, + LENOVO_MB_BREATHING_MODE = 0x03, + LENOVO_MB_WAVE_MODE = 0x04, + LENOVO_MB_SPECTER_MODE = 0x06, + LENOVO_MB_RAINBOW_WAVE_MODE = 0x09, + LENOVO_MB_RANDOM_MODE = 0x0A +}; + +enum +{ + LENOVO_MB_BRIGHTNESS_MIN = 1, + LENOVO_MB_BRIGHTNESS_MAX = 4, + LENOVO_MB_SPEED_MIN = 1, + LENOVO_MB_SPEED_MAX = 4, +}; + +class LenovoMotherboardController +{ +public: + LenovoMotherboardController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~LenovoMotherboardController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetMode(uint8_t zone, uint8_t mode, uint8_t brightness, uint8_t speed, RGBColor color); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/LenovoMotherboardController/LenovoMotherboardControllerDetect.cpp b/Controllers/LenovoMotherboardController/LenovoMotherboardControllerDetect.cpp new file mode 100644 index 0000000..84827d8 --- /dev/null +++ b/Controllers/LenovoMotherboardController/LenovoMotherboardControllerDetect.cpp @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| LenovoMotherboardControllerDetect.cpp | +| | +| Detector for Lenovo motherboard | +| | +| Morgan Guimard (morg) 26 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LenovoMotherboardController.h" +#include "RGBController_LenovoMotherboard.h" +#include "dmiinfo.h" + +/*---------------------------------------------------------*\ +| vendor ID | +\*---------------------------------------------------------*/ +#define LENOVO_MB_VID 0x17EF + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define LENOVO_MB_PID 0xC955 + +void DetectLenovoMotherboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + DMIInfo dmi; + + LenovoMotherboardController* controller = new LenovoMotherboardController(dev, *info, name + " " + dmi.getMainboard()); + RGBController_LenovoMotherboard* rgb_controller = new RGBController_LenovoMotherboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("Lenovo", DetectLenovoMotherboardControllers, LENOVO_MB_VID, LENOVO_MB_PID, 0xFF89, 0xCC); diff --git a/Controllers/LenovoMotherboardController/RGBController_LenovoMotherboard.cpp b/Controllers/LenovoMotherboardController/RGBController_LenovoMotherboard.cpp new file mode 100644 index 0000000..8926179 --- /dev/null +++ b/Controllers/LenovoMotherboardController/RGBController_LenovoMotherboard.cpp @@ -0,0 +1,204 @@ +/*---------------------------------------------------------*\ +| RGBController_LenovoMotherboard.cpp | +| | +| RGBController for Lenovo motherboard | +| | +| Morgan Guimard (morg) 26 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_LenovoMotherboard.h" + +/**------------------------------------------------------------------*\ + @name LenovoMotherboard mouse + @category Motherboard + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectLenovoMotherboardControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LenovoMotherboard::RGBController_LenovoMotherboard(LenovoMotherboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Lenovo"; + type = DEVICE_TYPE_MOTHERBOARD; + description = name; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.value = LENOVO_MB_STATIC_MODE; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = LENOVO_MB_BRIGHTNESS_MIN; + Static.brightness_max = LENOVO_MB_BRIGHTNESS_MAX; + Static.brightness = LENOVO_MB_BRIGHTNESS_MAX; + Static.speed = LENOVO_MB_SPEED_MIN; + modes.push_back(Static); + + mode Sparkle; + Sparkle.name = "Sparkle"; + Sparkle.value = LENOVO_MB_SPARKLE_MODE; + Sparkle.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Sparkle.color_mode = MODE_COLORS_PER_LED; + Sparkle.brightness_min = LENOVO_MB_BRIGHTNESS_MIN; + Sparkle.brightness_max = LENOVO_MB_BRIGHTNESS_MAX; + Sparkle.brightness = LENOVO_MB_BRIGHTNESS_MAX; + Sparkle.speed = LENOVO_MB_SPEED_MIN; + Sparkle.speed_max = LENOVO_MB_SPEED_MAX; + Sparkle.speed_min = LENOVO_MB_SPEED_MIN; + modes.push_back(Sparkle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LENOVO_MB_BREATHING_MODE; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = LENOVO_MB_BRIGHTNESS_MIN; + Breathing.brightness_max = LENOVO_MB_BRIGHTNESS_MAX; + Breathing.brightness = LENOVO_MB_BRIGHTNESS_MAX; + Breathing.speed = LENOVO_MB_SPEED_MIN; + Breathing.speed_max = LENOVO_MB_SPEED_MAX; + Breathing.speed_min = LENOVO_MB_SPEED_MIN; + modes.push_back(Breathing); + + mode Wave; + Wave.name = "Wave"; + Wave.value = LENOVO_MB_WAVE_MODE; + Wave.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Wave.color_mode = MODE_COLORS_NONE; + Wave.brightness_min = LENOVO_MB_BRIGHTNESS_MIN; + Wave.brightness_max = LENOVO_MB_BRIGHTNESS_MAX; + Wave.brightness = LENOVO_MB_BRIGHTNESS_MAX; + Wave.speed = LENOVO_MB_SPEED_MIN; + Wave.speed_max = LENOVO_MB_SPEED_MAX; + Wave.speed_min = LENOVO_MB_SPEED_MIN; + modes.push_back(Wave); + + mode Specter; + Specter.name = "Specter"; + Specter.value = LENOVO_MB_SPECTER_MODE; + Specter.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Specter.color_mode = MODE_COLORS_PER_LED; + Specter.brightness_min = LENOVO_MB_BRIGHTNESS_MIN; + Specter.brightness_max = LENOVO_MB_BRIGHTNESS_MAX; + Specter.brightness = LENOVO_MB_BRIGHTNESS_MAX; + Specter.speed = LENOVO_MB_SPEED_MIN; + Specter.speed_max = LENOVO_MB_SPEED_MAX; + Specter.speed_min = LENOVO_MB_SPEED_MIN; + modes.push_back(Specter); + + mode Rainbow; + Rainbow.name = "Rainbow wave"; + Rainbow.value = LENOVO_MB_RAINBOW_WAVE_MODE; + Rainbow.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness_min = LENOVO_MB_BRIGHTNESS_MIN; + Rainbow.brightness_max = LENOVO_MB_BRIGHTNESS_MAX; + Rainbow.brightness = LENOVO_MB_BRIGHTNESS_MAX; + Rainbow.speed = LENOVO_MB_SPEED_MIN; + Rainbow.speed_max = LENOVO_MB_SPEED_MAX; + Rainbow.speed_min = LENOVO_MB_SPEED_MIN; + modes.push_back(Rainbow); + + mode Random; + Random.name = "Random"; + Random.value = LENOVO_MB_RANDOM_MODE; + Random.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Random.color_mode = MODE_COLORS_NONE; + Random.brightness_min = LENOVO_MB_BRIGHTNESS_MIN; + Random.brightness_max = LENOVO_MB_BRIGHTNESS_MAX; + Random.brightness = LENOVO_MB_BRIGHTNESS_MAX; + Random.speed = LENOVO_MB_SPEED_MIN; + Random.speed_max = LENOVO_MB_SPEED_MAX; + Random.speed_min = LENOVO_MB_SPEED_MIN; + modes.push_back(Random); + + SetupZones(); +} + +RGBController_LenovoMotherboard::~RGBController_LenovoMotherboard() +{ + delete controller; +} + +void RGBController_LenovoMotherboard::SetupZones() +{ + zone cpu_fan_zone; + + cpu_fan_zone.name = "CPU FAN Zone"; + cpu_fan_zone.type = ZONE_TYPE_SINGLE; + cpu_fan_zone.leds_min = 1; + cpu_fan_zone.leds_max = 1; + cpu_fan_zone.leds_count = 1; + cpu_fan_zone.matrix_map = nullptr; + + zones.emplace_back(cpu_fan_zone); + + zone rear_fan_zone; + + rear_fan_zone.name = "Rear FAN Zone"; + rear_fan_zone.type = ZONE_TYPE_SINGLE; + rear_fan_zone.leds_min = 1; + rear_fan_zone.leds_max = 1; + rear_fan_zone.leds_count = 1; + rear_fan_zone.matrix_map = nullptr; + + zones.emplace_back(rear_fan_zone); + + leds.resize(LENOVO_MB_NUMBER_OF_LEDS); + + leds[0].name = "LED 1"; + leds[0].value = LENOVO_MB_ZONE_1_VALUE; + + leds[1].name = "LED 2"; + leds[1].value = LENOVO_MB_ZONE_2_VALUE; + + SetupColors(); +} + +void RGBController_LenovoMotherboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LenovoMotherboard::DeviceUpdateLEDs() +{ + for(uint8_t i = 0; i < leds.size(); i++) + { + controller->SetMode( + leds[i].value, + modes[active_mode].value, + modes[active_mode].brightness, + modes[active_mode].speed, + colors[i] + ); + } +} + +void RGBController_LenovoMotherboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LenovoMotherboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LenovoMotherboard::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/LenovoMotherboardController/RGBController_LenovoMotherboard.h b/Controllers/LenovoMotherboardController/RGBController_LenovoMotherboard.h new file mode 100644 index 0000000..df5a89b --- /dev/null +++ b/Controllers/LenovoMotherboardController/RGBController_LenovoMotherboard.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_LenovoMotherboard.h | +| | +| RGBController for Lenovo motherboard | +| | +| Morgan Guimard (morg) 26 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LenovoMotherboardController.h" + +class RGBController_LenovoMotherboard : public RGBController +{ +public: + RGBController_LenovoMotherboard(LenovoMotherboardController* controller_ptr); + ~RGBController_LenovoMotherboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LenovoMotherboardController* controller; +}; diff --git a/Controllers/LexipMouseController/LexipMouseController.cpp b/Controllers/LexipMouseController/LexipMouseController.cpp new file mode 100644 index 0000000..03affde --- /dev/null +++ b/Controllers/LexipMouseController/LexipMouseController.cpp @@ -0,0 +1,75 @@ +/*---------------------------------------------------------*\ +| LexipMouseController.cpp | +| | +| Driver for Lexip mouse | +| | +| Morgan Guimard (morg) 21 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LexipMouseController.h" +#include "StringUtils.h" + +LexipMouseController::LexipMouseController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +LexipMouseController::~LexipMouseController() +{ + hid_close(dev); +} + +std::string LexipMouseController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LexipMouseController::GetNameString() +{ + return(name); +} + +std::string LexipMouseController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LexipMouseController::SetDirect(RGBColor color) +{ + /*-----------------------------------------*\ + | Send a change color packet | + | | + | URB INTERRUPT OUT, pad a leading zero | + | | + | 00 24 01 RR GG BB 00 64 80 00 .... 00 | + \*-----------------------------------------*/ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x01] = 0x24; // constant data + usb_buf[0x02] = 0x01; // constant data + + usb_buf[0x03] = RGBGetRValue(color); // red channel + usb_buf[0x04] = RGBGetGValue(color); // green channel + usb_buf[0x05] = RGBGetBValue(color); // blue channel + + usb_buf[0x07] = 0x64; // constant data + usb_buf[0x08] = 0x80; // constant data + + hid_write(dev, usb_buf, PACKET_DATA_LENGTH); +} diff --git a/Controllers/LexipMouseController/LexipMouseController.h b/Controllers/LexipMouseController/LexipMouseController.h new file mode 100644 index 0000000..7584519 --- /dev/null +++ b/Controllers/LexipMouseController/LexipMouseController.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| LexipMouseController.h | +| | +| Driver for Lexip mouse | +| | +| Morgan Guimard (morg) 21 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define PACKET_DATA_LENGTH 64 + +class LexipMouseController +{ +public: + LexipMouseController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~LexipMouseController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetDirect(RGBColor color); +protected: + hid_device* dev; + +private: + std::string location; + std::string name; + std::string version; +}; diff --git a/Controllers/LexipMouseController/LexipMouseControllerDetect.cpp b/Controllers/LexipMouseController/LexipMouseControllerDetect.cpp new file mode 100644 index 0000000..e656d68 --- /dev/null +++ b/Controllers/LexipMouseController/LexipMouseControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| LexipMouseControllerDetect.cpp | +| | +| Detector for Lexip mouse | +| | +| Morgan Guimard (morg) 21 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LexipMouseController.h" +#include "RGBController_LexipMouse.h" + +/*---------------------------------------------------------*\ +| Lexip vendor ID | +\*---------------------------------------------------------*/ +#define LEXIP_VID 0x04D8 + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define LEXIP_NP93_ALPHA_PID 0xFD0A + +void DetectLexipMouseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LexipMouseController* controller = new LexipMouseController(dev, *info, name); + RGBController_LexipMouse* rgb_controller = new RGBController_LexipMouse(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Np93 ALPHA - Gaming Mouse", DetectLexipMouseControllers, LEXIP_VID, LEXIP_NP93_ALPHA_PID, 0, 0x0001, 2); diff --git a/Controllers/LexipMouseController/RGBController_LexipMouse.cpp b/Controllers/LexipMouseController/RGBController_LexipMouse.cpp new file mode 100644 index 0000000..c5afccb --- /dev/null +++ b/Controllers/LexipMouseController/RGBController_LexipMouse.cpp @@ -0,0 +1,97 @@ +/*---------------------------------------------------------*\ +| RGBController_LexipMouse.cpp | +| | +| RGBController for Lexip mouse | +| | +| Morgan Guimard (morg) 21 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_LexipMouse.h" + +/**------------------------------------------------------------------*\ + @name Lexip Mouse + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectLexipMouseControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LexipMouse::RGBController_LexipMouse(LexipMouseController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Lexip"; + type = DEVICE_TYPE_MOUSE; + description = name; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0x00; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_LexipMouse::~RGBController_LexipMouse() +{ + delete controller; +} + +void RGBController_LexipMouse::SetupZones() +{ + zone new_zone; + + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(1); + leds[0].name = "LED 1"; + + SetupColors(); +} + +void RGBController_LexipMouse::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LexipMouse::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_LexipMouse::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetDirect(colors[0]); +} + +void RGBController_LexipMouse::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_LexipMouse::DeviceUpdateMode() +{ + UpdateZoneLEDs(0); +} diff --git a/Controllers/LexipMouseController/RGBController_LexipMouse.h b/Controllers/LexipMouseController/RGBController_LexipMouse.h new file mode 100644 index 0000000..88aaa68 --- /dev/null +++ b/Controllers/LexipMouseController/RGBController_LexipMouse.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_LexipMouse.h | +| | +| RGBController for Lexip mouse | +| | +| Morgan Guimard (morg) 21 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LexipMouseController.h" + +class RGBController_LexipMouse : public RGBController +{ +public: + RGBController_LexipMouse(LexipMouseController* controller_ptr); + ~RGBController_LexipMouse(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LexipMouseController* controller; +}; diff --git a/Controllers/LianLiController/LianLiControllerDetect.cpp b/Controllers/LianLiController/LianLiControllerDetect.cpp new file mode 100644 index 0000000..3bff90b --- /dev/null +++ b/Controllers/LianLiController/LianLiControllerDetect.cpp @@ -0,0 +1,306 @@ +/*---------------------------------------------------------*\ +| LianLiControllerDetect.cpp | +| | +| Detector for Lian Li devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "Detector.h" +#include "ResourceManager.h" + +/*-----------------------------------------------------*\ +| LianLi USB Controller specific includes | +\*-----------------------------------------------------*/ +#include "RGBController_LianLiUniHub.h" +#include "RGBController_LianLiStrimerLConnect.h" +#include "LianLiUniHubController.h" +#include "RGBController_LianLiUniHub.h" +#include "LianLiUniHubSLController.h" +#include "RGBController_LianLiUniHubSL.h" +#include "LianLiUniHubALController.h" +#include "RGBController_LianLiUniHubAL.h" +#include "LianLiUniHub_AL10Controller.h" +#include "RGBController_LianLiUniHub_AL10.h" +#include "LianLiUniHubSLV2Controller.h" +#include "RGBController_LianLiUniHubSLV2.h" +#include "LianLiUniHubSLInfinityController.h" +#include "RGBController_LianLiUniHubSLInfinity.h" +#include "LianLiGAIITrinityController.h" +#include "RGBController_LianLiGAIITrinity.h" +#include "LianLiUniversalScreenController.h" +#include "RGBController_LianLiUniversalScreen.h" + +/*-----------------------------------------------------*\ +| USB vendor IDs | +\*-----------------------------------------------------*/ +#define ENE_USB_VID 0x0CF2 +#define NUVOTON_USB_VID 0x0416 + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define STRIMER_L_CONNECT_PID 0xA200 + +/*-----------------------------------------------------*\ +| Fan controller product IDs | +\*-----------------------------------------------------*/ +#define UNI_HUB_PID 0x7750 +#define UNI_HUB_SL_PID 0xA100 +#define UNI_HUB_AL_PID 0xA101 +#define UNI_HUB_SLINF_PID 0xA102 +#define UNI_HUB_SLV2_PID 0xA103 +#define UNI_HUB_ALV2_PID 0xA104 +#define UNI_HUB_SLV2_V05_PID 0xA105 +#define GAII_USB_PID 0x7373 +#define GAII_Perf_USB_PID 0x7371 + +/*-----------------------------------------------------*\ +| Screen product IDs | +\*-----------------------------------------------------*/ +#define UNIVERSAL_SCREEN_LED_PID 0x8050 + +/*----------------------------------------------------------------------------*\ +| The Uni Hub is controlled by sending control transfers to various wIndex | +| addresses, allthough it announces some kind of hid interface. Hence it | +| requires libusb as hidapi provides no wIndex customization. | +\*----------------------------------------------------------------------------*/ + +void DetectLianLiUniHub() +{ + libusb_device** devices = nullptr; + + ssize_t ret; + + ret = libusb_init(NULL); + if(ret < 0) + { + return; + } + + ret = libusb_get_device_list(NULL, &devices); + if(ret < 0) + { + return; + } + + ssize_t deviceCount = ret; + + for(int i = 0; i < deviceCount; i++) + { + libusb_device* device = devices[i]; + libusb_device_descriptor descriptor; + ret = libusb_get_device_descriptor(device, &descriptor); + + if(ret < 0) + { + continue; + } + + if( descriptor.idVendor == ENE_USB_VID + && descriptor.idProduct == UNI_HUB_PID) + { + LianLiUniHubController* controller = new LianLiUniHubController(device, &descriptor); + RGBController_LianLiUniHub* rgb_controller = new RGBController_LianLiUniHub(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + + if(devices != nullptr) + { + libusb_free_device_list(devices, 1); + } +} + +void DetectLianLiUniHub_AL10() +{ + libusb_device** devices = nullptr; + + ssize_t ret; + + ret = libusb_init(NULL); + if(ret < 0) + { + return; + } + + ret = libusb_get_device_list(NULL, &devices); + if(ret < 0) + { + return; + } + + ssize_t deviceCount = ret; + + for(int i = 0; i < deviceCount; i++) + { + libusb_device* device = devices[i]; + libusb_device_descriptor descriptor; + ret = libusb_get_device_descriptor(device, &descriptor); + + if(ret < 0) + { + continue; + } + + if( descriptor.idVendor == ENE_USB_VID + && descriptor.idProduct == UNI_HUB_AL_PID) + { + LianLiUniHub_AL10Controller* controller = new LianLiUniHub_AL10Controller(device, &descriptor); + RGBController_LianLiUniHub_AL10* rgb_controller = new RGBController_LianLiUniHub_AL10(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + + if(devices != nullptr) + { + libusb_free_device_list(devices, 1); + } +} /* DetectLianLiUniHub_AL10() */ + +void DetectLianLiUniHubSL(hid_device_info* info, const std::string& name) +{ + hid_device* device = hid_open_path(info->path); + if (!device) + { + return; + } + + LianLiUniHubSLController* controller = new LianLiUniHubSLController(device, info->path); + std::string version = controller->ReadVersion(); + + if (version != "v1.8") + { + delete controller; + return; + } + + RGBController_LianLiUniHubSL* rgb_controller = new RGBController_LianLiUniHubSL(controller, name); + ResourceManager::get()->RegisterRGBController(rgb_controller); +} /* DetectLianLiUniHubSL() */ + +void DetectLianLiUniHubAL(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LianLiUniHubALController* controller = new LianLiUniHubALController(dev, info->path, info->product_id, name); + + std::string firmwareVersion = controller->GetFirmwareVersionString(); + + if(firmwareVersion == "v1.7") + { + RGBController_LianLiUniHubAL* rgb_controller = new RGBController_LianLiUniHubAL(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else if(firmwareVersion == "v1.0") + { + delete controller; + REGISTER_DETECTOR("Lian Li Uni Hub - AL", DetectLianLiUniHub_AL10); + } + else + { + delete controller; + return; + } + + } +} /* DetectLianLiUniHubAL() */ + +void DetectLianLiUniHubSLV2(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LianLiUniHubSLV2Controller* controller = new LianLiUniHubSLV2Controller(dev, info->path, name); + + RGBController_LianLiUniHubSLV2* rgb_controller = new RGBController_LianLiUniHubSLV2(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectLianLiUniHubSLV2() */ + +void DetectLianLiUniHubSLInfinity(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LianLiUniHubSLInfinityController* controller = new LianLiUniHubSLInfinityController(dev, info->path, name); + + RGBController_LianLiUniHubSLInfinity* rgb_controller = new RGBController_LianLiUniHubSLInfinity(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectLianLiUniHubSLInfinity() */ + +void DetectLianLiStrimerControllers(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LianLiStrimerLConnectController* controller = new LianLiStrimerLConnectController(dev, info->path); + RGBController_LianLiStrimerLConnect* rgb_controller = new RGBController_LianLiStrimerLConnect(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectLianLiGAIITrinity(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LianLiGAIITrinityController* controller = new LianLiGAIITrinityController(dev, info->path); + RGBController_LianLiGAIITrinity* rgb_controller = new RGBController_LianLiGAIITrinity(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectLianLiUniversalScreen() +{ + libusb_init(NULL); + + #ifdef _WIN32 + libusb_set_option(NULL, LIBUSB_OPTION_USE_USBDK); + #endif + + libusb_device_handle * dev = libusb_open_device_with_vid_pid(NULL, NUVOTON_USB_VID, UNIVERSAL_SCREEN_LED_PID); + + if(dev) + { + libusb_detach_kernel_driver(dev, 0); + libusb_claim_interface(dev, 0); + + LianLiUniversalScreenController* controller = new LianLiUniversalScreenController(dev); + RGBController_LianLiUniversalScreen* rgb_controller = new RGBController_LianLiUniversalScreen(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules for libusb devices | +| | +| DUMMY_DEVICE_DETECTOR("Lian Li Uni Hub", DetectLianLiUniHub, 0x0CF2, 0x7750 ) | +| DUMMY_DEVICE_DETECTOR("Lian Li Universal Screen", DetectLianLiUniversalScreen, 0x0416, 0x8050 ) | +\*---------------------------------------------------------------------------------------------------------*/ +REGISTER_DETECTOR("Lian Li Uni Hub", DetectLianLiUniHub); +REGISTER_DETECTOR("Lian Li Universal Screen", DetectLianLiUniversalScreen); + +REGISTER_HID_DETECTOR_IPU("Lian Li Uni Hub - SL", DetectLianLiUniHubSL, ENE_USB_VID, UNI_HUB_SL_PID, 0x01, 0xFF72, 0xA1); +REGISTER_HID_DETECTOR_IPU("Lian Li Uni Hub - AL", DetectLianLiUniHubAL, ENE_USB_VID, UNI_HUB_AL_PID, 0x01, 0xFF72, 0xA1); +REGISTER_HID_DETECTOR_IPU("Lian Li Uni Hub - SL V2", DetectLianLiUniHubSLV2, ENE_USB_VID, UNI_HUB_SLV2_PID, 0x01, 0xFF72, 0xA1); +REGISTER_HID_DETECTOR_IPU("Lian Li Uni Hub - AL V2", DetectLianLiUniHubSLV2, ENE_USB_VID, UNI_HUB_ALV2_PID, 0x01, 0xFF72, 0xA1); +REGISTER_HID_DETECTOR_IPU("Lian Li Uni Hub - SL V2 v0.5", DetectLianLiUniHubSLV2, ENE_USB_VID, UNI_HUB_SLV2_V05_PID, 0x01, 0xFF72, 0xA1); +REGISTER_HID_DETECTOR_IPU("Lian Li Uni Hub - SL Infinity", DetectLianLiUniHubSLInfinity, ENE_USB_VID, UNI_HUB_SLINF_PID, 0x01, 0xFF72, 0xA1); +REGISTER_HID_DETECTOR_IPU("Lian Li Strimer L Connect", DetectLianLiStrimerControllers, ENE_USB_VID, STRIMER_L_CONNECT_PID, 1, 0xFF72, 0xA1); +REGISTER_HID_DETECTOR_I("Lian Li GA II Trinity", DetectLianLiGAIITrinity, NUVOTON_USB_VID, GAII_USB_PID, 0x02); +REGISTER_HID_DETECTOR_I("Lian Li GA II Trinity Performance", DetectLianLiGAIITrinity, NUVOTON_USB_VID, GAII_Perf_USB_PID, 0x02); diff --git a/Controllers/LianLiController/LianLiGAIITrinityController/LianLiGAIITrinityController.cpp b/Controllers/LianLiController/LianLiGAIITrinityController/LianLiGAIITrinityController.cpp new file mode 100644 index 0000000..4aae0bd --- /dev/null +++ b/Controllers/LianLiController/LianLiGAIITrinityController/LianLiGAIITrinityController.cpp @@ -0,0 +1,306 @@ +/*---------------------------------------------------------*\ +| LianLiGAIITrinityController.cpp | +| | +| Driver for Lian Li GAII Trinity | +| | +| Michael Losert 27 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "LianLiGAIITrinityController.h" +#include "StringUtils.h" + +LianLiGAIITrinityController::LianLiGAIITrinityController(hid_device* dev_handle, char* path) +{ + dev = dev_handle; + location = path; +} + +LianLiGAIITrinityController::~LianLiGAIITrinityController() +{ + if(dev) + { + hid_close(dev); + } +} + +std::string LianLiGAIITrinityController::GetLocation() +{ + return("HID: " + location); +} + +LianLiGAIITrinityController::GAII_Info LianLiGAIITrinityController::GetControllerInfo() +{ + GAII_Info controllerInfo; + + // get serial number + const uint8_t sz = 255; + wchar_t tmp[sz]; + + hid_get_serial_number_string(dev, tmp, sz); + controllerInfo.serial = StringUtils::wstring_to_string(tmp); + + // get firmware version + unsigned char data[64] = ""; + data[0x00] = 0x01; + data[GAII_ByteAddress::BA_PACKET_TYPE] = GAII_PacketType::PT_FIRMWARE_INFO; + hid_write(dev, data, sizeof(data)); + memset(data, 0, sizeof(data)); + hid_read(dev, data, sizeof(data)); + data[sizeof(data) - 1] = 0; + std::string response(reinterpret_cast(&data[0x06])); + memset(data, 0, sizeof(data)); + hid_read(dev, data, sizeof(data)); + data[sizeof(data) - 1] = 0; + response += " (" + std::string(reinterpret_cast(&data[0x06])) + ")"; + std::replace( response.begin(), response.end(), ',', ' '); + controllerInfo.version = response.substr(0, 100); + + return controllerInfo; +} + +unsigned char* LianLiGAIITrinityController::GetRGBControlPacketTemplate() +{ + static unsigned char usb_buf[64]; + memset(usb_buf, 0, sizeof(usb_buf)); + + usb_buf[0x00] = 0x01; + usb_buf[GAII_ByteAddress::BA_PACKET_TYPE] = GAII_PacketType::PT_RGB_CONTROL; + usb_buf[0x05] = 0x13; + + return usb_buf; +} + +void LianLiGAIITrinityController::SetRGB(unsigned char* usb_buf, RGBColor* rgb0, RGBColor* rgb1, RGBColor* rgb2, RGBColor* rgb3) +{ + if(rgb0) + { + usb_buf[GAII_ByteAddress::BA_R0] = RGBGetRValue(*rgb0); + usb_buf[GAII_ByteAddress::BA_G0] = RGBGetGValue(*rgb0); + usb_buf[GAII_ByteAddress::BA_B0] = RGBGetBValue(*rgb0); + } + + if(rgb1) + { + usb_buf[GAII_ByteAddress::BA_R1] = RGBGetRValue(*rgb1); + usb_buf[GAII_ByteAddress::BA_G1] = RGBGetGValue(*rgb1); + usb_buf[GAII_ByteAddress::BA_B1] = RGBGetBValue(*rgb1); + } + + if(rgb2) + { + usb_buf[GAII_ByteAddress::BA_R2] = RGBGetRValue(*rgb2); + usb_buf[GAII_ByteAddress::BA_G2] = RGBGetGValue(*rgb2); + usb_buf[GAII_ByteAddress::BA_B2] = RGBGetBValue(*rgb2); + } + + if(rgb3) + { + usb_buf[GAII_ByteAddress::BA_R3] = RGBGetRValue(*rgb3); + usb_buf[GAII_ByteAddress::BA_G3] = RGBGetGValue(*rgb3); + usb_buf[GAII_ByteAddress::BA_B3] = RGBGetBValue(*rgb3); + } +} + +void LianLiGAIITrinityController::SetMode_Rainbow(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_RAINBOW; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + usb_buf[GAII_ByteAddress::BA_DIRECTION] = direction; + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_RainbowMorph(GAII_Brightness brightness, GAII_Speed speed) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_RAINBOW_MORPH; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_StaticColor(GAII_Brightness brightness, RGBColor rgb0, RGBColor rgb1) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_STATIC_COLOR; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + SetRGB(usb_buf, rgb0, rgb1); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_BreathingColor(GAII_Brightness brightness, GAII_Speed speed, RGBColor rgb0, RGBColor rgb1) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_BREATHING_COLOR; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + SetRGB(usb_buf, rgb0, rgb1); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_Runway(GAII_Brightness brightness, GAII_Speed speed, RGBColor rgb0, RGBColor rgb1) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_RUNWAY; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + SetRGB(usb_buf, rgb0, rgb1); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_Meteor(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction, RGBColor rgb0, RGBColor rgb1, RGBColor rgb2, RGBColor rgb3) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_METEOR; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + usb_buf[GAII_ByteAddress::BA_DIRECTION] = direction; + SetRGB(usb_buf, rgb0, rgb1, rgb2, rgb3); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_Vortex(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction, RGBColor rgb0, RGBColor rgb1, RGBColor rgb2, RGBColor rgb3) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_VORTEX; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + usb_buf[GAII_ByteAddress::BA_DIRECTION] = direction; + SetRGB(usb_buf, rgb0, rgb1, rgb2, rgb3); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_CrossingOver(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction, RGBColor rgb0, RGBColor rgb1, RGBColor rgb2, RGBColor rgb3) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_CROSSING_OVER; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + usb_buf[GAII_ByteAddress::BA_DIRECTION] = direction; + SetRGB(usb_buf, rgb0, rgb1, rgb2, rgb3); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_TaiChi(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction, RGBColor rgb0, RGBColor rgb1) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_TAI_CHI; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + usb_buf[GAII_ByteAddress::BA_DIRECTION] = direction; + SetRGB(usb_buf, rgb0, rgb1); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_ColorfulStarryNight(GAII_Brightness brightness, GAII_Speed speed) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_COLORFUL_STARRY_NIGHT; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_StaticStarryNight(GAII_Brightness brightness, GAII_Speed speed, RGBColor rgb0) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_STATIC_STARRY_NIGHT; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + SetRGB(usb_buf, rgb0); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_Voice(GAII_Brightness brightness, GAII_Speed speed, RGBColor rgb0, RGBColor rgb1) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_VOICE; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + SetRGB(usb_buf, rgb0, rgb1); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_BigBang(GAII_Brightness brightness, GAII_Speed speed, RGBColor rgb0, RGBColor rgb1, RGBColor rgb2, RGBColor rgb3) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_BIG_BANG; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + SetRGB(usb_buf, rgb0, rgb1, rgb2, rgb3); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_Pump(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction, RGBColor rgb0, RGBColor rgb1) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_PUMP; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + usb_buf[GAII_ByteAddress::BA_DIRECTION] = direction; + SetRGB(usb_buf, rgb0, rgb1); + + hid_write(dev, usb_buf, 64); +} + +void LianLiGAIITrinityController::SetMode_ColorsMorph(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction) +{ + unsigned char *usb_buf = GetRGBControlPacketTemplate(); + + usb_buf[GAII_ByteAddress::BA_MODE] = GAII_Modes::M_COLORS_MORPH; + usb_buf[GAII_ByteAddress::BA_RING] = GAII_Ring::R_BOTH; + usb_buf[GAII_ByteAddress::BA_BRIGHTNESS] = brightness; + usb_buf[GAII_ByteAddress::BA_SPEED] = speed; + usb_buf[GAII_ByteAddress::BA_DIRECTION] = direction; + + hid_write(dev, usb_buf, 64); +} diff --git a/Controllers/LianLiController/LianLiGAIITrinityController/LianLiGAIITrinityController.h b/Controllers/LianLiController/LianLiGAIITrinityController/LianLiGAIITrinityController.h new file mode 100644 index 0000000..8d422ae --- /dev/null +++ b/Controllers/LianLiController/LianLiGAIITrinityController/LianLiGAIITrinityController.h @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| LianLiGAIITrinityController.h | +| | +| Driver for Lian Li GAII Trinity | +| | +| Michael Losert 27 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +/*----------------------------------------------------------------------------*\ +| Definitions related to LED configuration. | +\*----------------------------------------------------------------------------*/ + +class LianLiGAIITrinityController +{ +public: + struct GAII_Info + { + std::string serial; + std::string version; + }; + + enum GAII_PacketType : unsigned char + { + PT_RGB_CONTROL = 0x83, + PT_FIRMWARE_INFO = 0x86 + }; + + enum GAII_Modes : unsigned char + { + M_RAINBOW = 0x01, + M_RAINBOW_MORPH, + M_STATIC_COLOR, + M_BREATHING_COLOR, + M_RUNWAY, + M_METEOR, + M_VORTEX, + M_CROSSING_OVER, + M_TAI_CHI, + M_COLORFUL_STARRY_NIGHT, + M_STATIC_STARRY_NIGHT, + M_VOICE, + M_BIG_BANG, + M_PUMP, + M_COLORS_MORPH, + /* M_BOUNCE, */ // TODO: requires zone-specific modes + }; + + enum GAII_Ring : unsigned char + { + R_INNER, + R_OUTER, + R_BOTH, + }; + + enum GAII_Brightness : unsigned char + { + B_OFF, + B_25, + B_50, + B_75, + B_100, + }; + + enum GAII_Speed : unsigned char + { + S_VERY_SLOW, + S_SLOW, + S_MODERATE, + S_FAST, + S_VERY_FAST, + }; + + enum GAII_Direction : unsigned char + { + D_RIGHT, + D_LEFT + }; + + enum GAII_ByteAddress : unsigned char + { + BA_PACKET_TYPE = 0x01, + + BA_RING = 0x06, + BA_MODE, + BA_BRIGHTNESS, + BA_SPEED, + + BA_R0 = 0x0A, + BA_G0, + BA_B0, + BA_R1, + BA_G1, + BA_B1, + BA_R2, + BA_G2, + BA_B2, + BA_R3, + BA_G3, + BA_B3, + + BA_DIRECTION = 0x16, + }; + + LianLiGAIITrinityController(hid_device* dev_handle, char* path); + ~LianLiGAIITrinityController(); + + std::string GetLocation(); + + GAII_Info GetControllerInfo(); + + void SetMode_Rainbow(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction); + void SetMode_RainbowMorph(GAII_Brightness brightness, GAII_Speed speed); + void SetMode_StaticColor(GAII_Brightness brightness, RGBColor rgb0, RGBColor rgb1); + void SetMode_BreathingColor(GAII_Brightness brightness, GAII_Speed speed, RGBColor rgb0, RGBColor rgb1); + void SetMode_Runway(GAII_Brightness brightness, GAII_Speed speed, RGBColor rgb0, RGBColor rgb1); + void SetMode_Meteor(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction, RGBColor rgb0, RGBColor rgb1, RGBColor rgb2, RGBColor rgb3); + void SetMode_Vortex(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction, RGBColor rgb0, RGBColor rgb1, RGBColor rgb2, RGBColor rgb3); + void SetMode_CrossingOver(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction, RGBColor rgb0, RGBColor rgb1, RGBColor rgb2, RGBColor rgb3); + void SetMode_TaiChi(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction, RGBColor rgb0, RGBColor rgb1); + void SetMode_ColorfulStarryNight(GAII_Brightness brightness, GAII_Speed speed); + void SetMode_StaticStarryNight(GAII_Brightness brightness, GAII_Speed speed, RGBColor rgb0); + void SetMode_Voice(GAII_Brightness brightness, GAII_Speed speed, RGBColor rgb0, RGBColor rgb1); + void SetMode_BigBang(GAII_Brightness brightness, GAII_Speed speed, RGBColor rgb0, RGBColor rgb1, RGBColor rgb2, RGBColor rgb3); + void SetMode_Pump(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction, RGBColor rgb0, RGBColor rgb1); + void SetMode_ColorsMorph(GAII_Brightness brightness, GAII_Speed speed, GAII_Direction direction); + +private: + std::string location; + unsigned char* GetRGBControlPacketTemplate(); + void SetRGB(unsigned char* usb_buf, RGBColor rgb0) { SetRGB(usb_buf, &rgb0, nullptr, nullptr, nullptr); }; + void SetRGB(unsigned char* usb_buf, RGBColor rgb0, RGBColor rgb1) { SetRGB(usb_buf, &rgb0, &rgb1, nullptr, nullptr); }; + void SetRGB(unsigned char* usb_buf, RGBColor rgb0, RGBColor rgb1, RGBColor rgb2) { SetRGB(usb_buf, &rgb0, &rgb1, &rgb2, nullptr); }; + void SetRGB(unsigned char* usb_buf, RGBColor rgb0, RGBColor rgb1, RGBColor rgb2, RGBColor rgb3) { SetRGB(usb_buf, &rgb0, &rgb1, &rgb2, &rgb3); }; + void SetRGB(unsigned char* usb_buf, RGBColor* rgb0, RGBColor* rgb1, RGBColor* rgb2, RGBColor* rgb3); + + hid_device* dev; +}; diff --git a/Controllers/LianLiController/LianLiGAIITrinityController/RGBController_LianLiGAIITrinity.cpp b/Controllers/LianLiController/LianLiGAIITrinityController/RGBController_LianLiGAIITrinity.cpp new file mode 100644 index 0000000..3b36c51 --- /dev/null +++ b/Controllers/LianLiController/LianLiGAIITrinityController/RGBController_LianLiGAIITrinity.cpp @@ -0,0 +1,418 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiGAIITrinity.cpp | +| | +| RGBController for Lian Li GAII Trinity | +| | +| Michael Losert 27 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_LianLiGAIITrinity.h" + +/**------------------------------------------------------------------*\ + @name Lian Li GAII Trinity + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLianLiGAIITrinity + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LianLiGAIITrinity::RGBController_LianLiGAIITrinity(LianLiGAIITrinityController* controller_ptr) +{ + controller = controller_ptr; + + name = "Lian Li GAII Trinity"; + vendor = "Lian Li"; + type = DEVICE_TYPE_COOLER; + description = "Lian Li Galahad II Trinity AIO"; + location = controller->GetLocation(); + + LianLiGAIITrinityController::GAII_Info controllerInfo = controller->GetControllerInfo(); + version = controllerInfo.version; + serial = controllerInfo.serial; + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = LianLiGAIITrinityController::GAII_Modes::M_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.color_mode = MODE_COLORS_RANDOM; + Rainbow.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + Rainbow.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + Rainbow.brightness = LianLiGAIITrinityController::GAII_Brightness::B_75; + Rainbow.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + Rainbow.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + Rainbow.speed = LianLiGAIITrinityController::GAII_Speed::S_FAST; + Rainbow.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Rainbow); + + mode RainbowMorph; + RainbowMorph.name = "Rainbow Morph"; + RainbowMorph.value = LianLiGAIITrinityController::GAII_Modes::M_RAINBOW_MORPH; + RainbowMorph.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + RainbowMorph.color_mode = MODE_COLORS_RANDOM; + RainbowMorph.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + RainbowMorph.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + RainbowMorph.brightness = LianLiGAIITrinityController::GAII_Brightness::B_75; + RainbowMorph.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + RainbowMorph.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + RainbowMorph.speed = LianLiGAIITrinityController::GAII_Speed::S_SLOW; + modes.push_back(RainbowMorph); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LianLiGAIITrinityController::GAII_Modes::M_STATIC_COLOR; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + Direct.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + Direct.brightness = LianLiGAIITrinityController::GAII_Brightness::B_50; + modes.push_back(Direct); + + mode BreathingColor; + BreathingColor.name = "Breathing Color"; + BreathingColor.value = LianLiGAIITrinityController::GAII_Modes::M_BREATHING_COLOR; + BreathingColor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + BreathingColor.color_mode = MODE_COLORS_MODE_SPECIFIC; + BreathingColor.colors.resize(2); + BreathingColor.colors[0] = ToRGBColor(255, 255, 255); + BreathingColor.colors[1] = ToRGBColor(255, 0, 0); + BreathingColor.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + BreathingColor.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + BreathingColor.brightness = LianLiGAIITrinityController::GAII_Brightness::B_100; + BreathingColor.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + BreathingColor.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + BreathingColor.speed = LianLiGAIITrinityController::GAII_Speed::S_MODERATE; + modes.push_back(BreathingColor); + + mode Runway; + Runway.name = "Runway"; + Runway.value = LianLiGAIITrinityController::GAII_Modes::M_RUNWAY; + Runway.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Runway.color_mode = MODE_COLORS_MODE_SPECIFIC; + Runway.colors.resize(2); + Runway.colors[0] = ToRGBColor(0, 0, 0); + Runway.colors[1] = ToRGBColor(255, 255, 255); + Runway.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + Runway.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + Runway.brightness = LianLiGAIITrinityController::GAII_Brightness::B_50; + Runway.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + Runway.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + Runway.speed = LianLiGAIITrinityController::GAII_Speed::S_FAST; + modes.push_back(Runway); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = LianLiGAIITrinityController::GAII_Modes::M_METEOR; + Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors.resize(4); + Meteor.colors[0] = ToRGBColor(50, 50, 50); + Meteor.colors[1] = ToRGBColor(100, 100, 100); + Meteor.colors[2] = ToRGBColor(180, 180, 180); + Meteor.colors[3] = ToRGBColor(255, 0, 0); + Meteor.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + Meteor.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + Meteor.brightness = LianLiGAIITrinityController::GAII_Brightness::B_100; + Meteor.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + Meteor.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + Meteor.speed = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + Meteor.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Meteor); + + mode Vortex; + Vortex.name = "Vortex"; + Vortex.value = LianLiGAIITrinityController::GAII_Modes::M_VORTEX; + Vortex.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Vortex.color_mode = MODE_COLORS_MODE_SPECIFIC; + Vortex.colors.resize(4); + Vortex.colors[0] = ToRGBColor(100, 100, 100); + Vortex.colors[1] = ToRGBColor(0, 100, 0); + Vortex.colors[2] = ToRGBColor(255, 255, 255); + Vortex.colors[3] = ToRGBColor(255, 0, 0); + Vortex.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + Vortex.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + Vortex.brightness = LianLiGAIITrinityController::GAII_Brightness::B_75; + Vortex.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + Vortex.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + Vortex.speed = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + Vortex.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Vortex); + + mode CrossingOver; + CrossingOver.name = "Crossing Over"; + CrossingOver.value = LianLiGAIITrinityController::GAII_Modes::M_CROSSING_OVER; + CrossingOver.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + CrossingOver.color_mode = MODE_COLORS_MODE_SPECIFIC; + CrossingOver.colors.resize(4); + CrossingOver.colors[0] = ToRGBColor(255, 0, 0); + CrossingOver.colors[1] = ToRGBColor(0, 255, 0); + CrossingOver.colors[2] = ToRGBColor(0, 0, 255); + CrossingOver.colors[3] = ToRGBColor(255, 255, 0); + CrossingOver.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + CrossingOver.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + CrossingOver.brightness = LianLiGAIITrinityController::GAII_Brightness::B_75; + CrossingOver.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + CrossingOver.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + CrossingOver.speed = LianLiGAIITrinityController::GAII_Speed::S_FAST; + CrossingOver.direction = MODE_DIRECTION_RIGHT; + modes.push_back(CrossingOver); + + mode TaiChi; + TaiChi.name = "Tai Chi"; + TaiChi.value = LianLiGAIITrinityController::GAII_Modes::M_TAI_CHI; + TaiChi.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + TaiChi.color_mode = MODE_COLORS_MODE_SPECIFIC; + TaiChi.colors.resize(2); + TaiChi.colors[0] = ToRGBColor(255, 0, 0); + TaiChi.colors[1] = ToRGBColor(0, 255, 0); + TaiChi.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + TaiChi.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + TaiChi.brightness = LianLiGAIITrinityController::GAII_Brightness::B_75; + TaiChi.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + TaiChi.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + TaiChi.speed = LianLiGAIITrinityController::GAII_Speed::S_MODERATE; + TaiChi.direction = MODE_DIRECTION_RIGHT; + modes.push_back(TaiChi); + + mode ColorfulStarryNight; + ColorfulStarryNight.name = "Colorful Starry Night"; + ColorfulStarryNight.value = LianLiGAIITrinityController::GAII_Modes::M_COLORFUL_STARRY_NIGHT; + ColorfulStarryNight.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + ColorfulStarryNight.color_mode = MODE_COLORS_RANDOM; + ColorfulStarryNight.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + ColorfulStarryNight.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + ColorfulStarryNight.brightness = LianLiGAIITrinityController::GAII_Brightness::B_50; + ColorfulStarryNight.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + ColorfulStarryNight.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + ColorfulStarryNight.speed = LianLiGAIITrinityController::GAII_Speed::S_SLOW; + modes.push_back(ColorfulStarryNight); + + mode StaticStarryNight; + StaticStarryNight.name = "Static Starry Night"; + StaticStarryNight.value = LianLiGAIITrinityController::GAII_Modes::M_STATIC_STARRY_NIGHT; + StaticStarryNight.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + StaticStarryNight.color_mode = MODE_COLORS_MODE_SPECIFIC; + StaticStarryNight.colors.resize(1); + StaticStarryNight.colors[0] = ToRGBColor(255, 255, 0); + StaticStarryNight.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + StaticStarryNight.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + StaticStarryNight.brightness = LianLiGAIITrinityController::GAII_Brightness::B_50; + StaticStarryNight.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + StaticStarryNight.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + StaticStarryNight.speed = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + modes.push_back(StaticStarryNight); + + mode Voice; + Voice.name = "Voice"; + Voice.value = LianLiGAIITrinityController::GAII_Modes::M_VOICE; + Voice.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Voice.color_mode = MODE_COLORS_MODE_SPECIFIC; + Voice.colors.resize(2); + Voice.colors[0] = ToRGBColor(255, 255, 255); + Voice.colors[1] = ToRGBColor(130, 130, 130); + Voice.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + Voice.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + Voice.brightness = LianLiGAIITrinityController::GAII_Brightness::B_100; + Voice.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + Voice.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + Voice.speed = LianLiGAIITrinityController::GAII_Speed::S_SLOW; + modes.push_back(Voice); + + + mode BigBang; + BigBang.name = "Big Bang"; + BigBang.value = LianLiGAIITrinityController::GAII_Modes::M_BIG_BANG; + BigBang.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + BigBang.color_mode = MODE_COLORS_MODE_SPECIFIC; + BigBang.colors.resize(4); + BigBang.colors[0] = ToRGBColor(255, 255, 255); + BigBang.colors[1] = ToRGBColor(255, 0, 0); + BigBang.colors[2] = ToRGBColor(255, 255, 255); + BigBang.colors[3] = ToRGBColor(0, 255, 0); + BigBang.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + BigBang.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + BigBang.brightness = LianLiGAIITrinityController::GAII_Brightness::B_75; + BigBang.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + BigBang.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + BigBang.speed = LianLiGAIITrinityController::GAII_Speed::S_FAST; + modes.push_back(BigBang); + + mode Pump; + Pump.name = "Pump"; + Pump.value = LianLiGAIITrinityController::GAII_Modes::M_PUMP; + Pump.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Pump.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pump.colors.resize(2); + Pump.colors[0] = ToRGBColor(0, 255, 0); + Pump.colors[1] = ToRGBColor(150, 150, 150); + Pump.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + Pump.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + Pump.brightness = LianLiGAIITrinityController::GAII_Brightness::B_75; + Pump.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + Pump.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + Pump.speed = LianLiGAIITrinityController::GAII_Speed::S_FAST; + Pump.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Pump); + + mode ColorsMorph; + ColorsMorph.name = "Colors Morph"; + ColorsMorph.value = LianLiGAIITrinityController::GAII_Modes::M_COLORS_MORPH; + ColorsMorph.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + ColorsMorph.color_mode = MODE_COLORS_RANDOM; + ColorsMorph.brightness_min = LianLiGAIITrinityController::GAII_Brightness::B_OFF; + ColorsMorph.brightness_max = LianLiGAIITrinityController::GAII_Brightness::B_100; + ColorsMorph.brightness = LianLiGAIITrinityController::GAII_Brightness::B_100; + ColorsMorph.speed_min = LianLiGAIITrinityController::GAII_Speed::S_VERY_SLOW; + ColorsMorph.speed_max = LianLiGAIITrinityController::GAII_Speed::S_VERY_FAST; + ColorsMorph.speed = LianLiGAIITrinityController::GAII_Speed::S_MODERATE; + ColorsMorph.direction = MODE_DIRECTION_RIGHT; + modes.push_back(ColorsMorph); + + SetupZones(); +} + +RGBController_LianLiGAIITrinity::~RGBController_LianLiGAIITrinity() +{ + delete controller; +} + +void RGBController_LianLiGAIITrinity::SetupZones() +{ + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + + zone gaii_trinity; + gaii_trinity.name = "GAII Trinity"; + gaii_trinity.type = ZONE_TYPE_SINGLE; + gaii_trinity.leds_min = 2; + gaii_trinity.leds_max = 2; + gaii_trinity.leds_count = 2; + gaii_trinity.matrix_map = NULL; + zones.push_back(gaii_trinity); + + led inner_led; + inner_led.name = "Inner Ring LEDs"; + leds.push_back(inner_led); + + led outer_led; + outer_led.name = "Outer Ring LEDs"; + leds.push_back(outer_led); + + SetupColors(); + + // set default color values + zones[0].colors[0] = ToRGBColor(255, 255, 255); + zones[0].colors[1] = ToRGBColor(0, 0, 255); +} + +void RGBController_LianLiGAIITrinity::ResizeZone(int /* zone */, int /* new_size */) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LianLiGAIITrinity::DeviceUpdateLEDs() +{ + switch(modes[active_mode].value) + { + case LianLiGAIITrinityController::GAII_Modes::M_RAINBOW: + controller->SetMode_Rainbow(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + OpenRGBDirection2GAIIDirection(modes[active_mode].direction)); + break; + case LianLiGAIITrinityController::GAII_Modes::M_RAINBOW_MORPH: + controller->SetMode_RainbowMorph(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed)); + break; + case LianLiGAIITrinityController::GAII_Modes::M_STATIC_COLOR: + controller->SetMode_StaticColor(static_cast(modes[active_mode].brightness), + zones[0].colors[0], zones[0].colors[1]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_BREATHING_COLOR: + controller->SetMode_BreathingColor(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + modes[active_mode].colors[0], modes[active_mode].colors[1]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_RUNWAY: + controller->SetMode_Runway(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + modes[active_mode].colors[0], modes[active_mode].colors[1]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_METEOR: + controller->SetMode_Meteor(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + OpenRGBDirection2GAIIDirection(modes[active_mode].direction), + modes[active_mode].colors[0], modes[active_mode].colors[1], modes[active_mode].colors[2], modes[active_mode].colors[3]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_VORTEX: + controller->SetMode_Vortex(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + OpenRGBDirection2GAIIDirection(modes[active_mode].direction), + modes[active_mode].colors[0], modes[active_mode].colors[1], modes[active_mode].colors[2], modes[active_mode].colors[3]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_CROSSING_OVER: + controller->SetMode_CrossingOver(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + OpenRGBDirection2GAIIDirection(modes[active_mode].direction), + modes[active_mode].colors[0], modes[active_mode].colors[1], modes[active_mode].colors[2], modes[active_mode].colors[3]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_TAI_CHI: + controller->SetMode_TaiChi(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + OpenRGBDirection2GAIIDirection(modes[active_mode].direction), + modes[active_mode].colors[0], modes[active_mode].colors[1]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_COLORFUL_STARRY_NIGHT: + controller->SetMode_ColorfulStarryNight(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed)); + break; + case LianLiGAIITrinityController::GAII_Modes::M_STATIC_STARRY_NIGHT: + controller->SetMode_StaticStarryNight(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + modes[active_mode].colors[0]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_VOICE: + controller->SetMode_Voice(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + modes[active_mode].colors[0], modes[active_mode].colors[1]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_BIG_BANG: + controller->SetMode_BigBang(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + modes[active_mode].colors[0], modes[active_mode].colors[1], modes[active_mode].colors[2], modes[active_mode].colors[3]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_PUMP: + controller->SetMode_Pump(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + OpenRGBDirection2GAIIDirection(modes[active_mode].direction), + modes[active_mode].colors[0], modes[active_mode].colors[1]); + break; + case LianLiGAIITrinityController::GAII_Modes::M_COLORS_MORPH: + controller->SetMode_ColorsMorph(static_cast(modes[active_mode].brightness), + static_cast(modes[active_mode].speed), + OpenRGBDirection2GAIIDirection(modes[active_mode].direction)); + break; + } +} + +void RGBController_LianLiGAIITrinity::UpdateZoneLEDs(int /* zone */) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LianLiGAIITrinity::UpdateSingleLED(int /* led */) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LianLiGAIITrinity::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/LianLiController/LianLiGAIITrinityController/RGBController_LianLiGAIITrinity.h b/Controllers/LianLiController/LianLiGAIITrinityController/RGBController_LianLiGAIITrinity.h new file mode 100644 index 0000000..8432950 --- /dev/null +++ b/Controllers/LianLiController/LianLiGAIITrinityController/RGBController_LianLiGAIITrinity.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiGAIITrinity.h | +| | +| RGBController for Lian Li GAII Trinity | +| | +| Michael Losert 27 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "LianLiGAIITrinityController.h" +#include "RGBController.h" + +class RGBController_LianLiGAIITrinity : public RGBController +{ +public: + RGBController_LianLiGAIITrinity(LianLiGAIITrinityController* controller_ptr); + ~RGBController_LianLiGAIITrinity(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + LianLiGAIITrinityController::GAII_Direction OpenRGBDirection2GAIIDirection(unsigned int openrgb_direction) + { + if(openrgb_direction == MODE_DIRECTION_LEFT) + return LianLiGAIITrinityController::GAII_Direction::D_LEFT; + + return LianLiGAIITrinityController::GAII_Direction::D_RIGHT; + } + +private: + LianLiGAIITrinityController* controller; +}; diff --git a/Controllers/LianLiController/LianLiStrimerLConnectController/LianLiStrimerLConnectController.cpp b/Controllers/LianLiController/LianLiStrimerLConnectController/LianLiStrimerLConnectController.cpp new file mode 100644 index 0000000..b71d088 --- /dev/null +++ b/Controllers/LianLiController/LianLiStrimerLConnectController/LianLiStrimerLConnectController.cpp @@ -0,0 +1,107 @@ +/*---------------------------------------------------------*\ +| LianLiStrimerLConnectController.cpp | +| | +| Driver for Lian Li Strimer L Connect | +| | +| Chris M (Dr_No) 03 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LianLiStrimerLConnectController.h" +#include "StringUtils.h" + +static uint8_t speed_data[5] = +{ + 0x02, 0x01, 0x00, 0xFE, 0xFF /* Slow to fast */ +}; + +static uint8_t brightness_data[5] = +{ + 0x08, 0x03, 0x02, 0x01, 0x00 /* 0%, 25%, 50%, 75%, 100% */ +}; + +LianLiStrimerLConnectController::LianLiStrimerLConnectController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); +} + +LianLiStrimerLConnectController::~LianLiStrimerLConnectController() +{ + hid_close(dev); +} + +std::string LianLiStrimerLConnectController::GetDeviceName() +{ + return device_name; +} + +std::string LianLiStrimerLConnectController::GetSerial() +{ + wchar_t serial_string[HID_MAX_STR]; + int ret = hid_get_serial_number_string(dev, serial_string, HID_MAX_STR); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string LianLiStrimerLConnectController::GetLocation() +{ + return("HID: " + location); +} + +void LianLiStrimerLConnectController::SendApply() +{ + uint8_t buffer[STRIMERLCONNECT_PACKET_SIZE] = { STRIMERLCONNECT_REPORT_ID, 0x2C, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00 }; + + hid_write(dev, buffer, STRIMERLCONNECT_PACKET_SIZE); +} + +void LianLiStrimerLConnectController::SetMode(uint8_t mode, uint8_t zone, uint8_t speed, uint8_t brightness, uint8_t direction, bool /*random_colours*/) +{ + uint8_t buffer[STRIMERLCONNECT_PACKET_SIZE] = { STRIMERLCONNECT_REPORT_ID, STRIMERLCONNECT_MODE_COMMAND, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + buffer[STRIMERLCONNECT_COMMAND_BYTE] |= zone; + + buffer[STRIMERLCONNECT_DATA_BYTE] = mode; + buffer[STRIMERLCONNECT_SPEED_BYTE] = speed_data[speed]; + buffer[STRIMERLCONNECT_DIRECTION_BYTE] = (direction == 0) ? 1 : 0; + buffer[STRIMERLCONNECT_BRIGHTNESS_BYTE] = brightness_data[brightness]; + + hid_write(dev, buffer, STRIMERLCONNECT_PACKET_SIZE); +} + +void LianLiStrimerLConnectController::SetLedsDirect(uint8_t zone, RGBColor * led_colours, uint8_t led_count) +{ + uint8_t buffer[STRIMERLCONNECT_PACKET_SIZE] = { STRIMERLCONNECT_REPORT_ID, STRIMERLCONNECT_COLOUR_COMMAND, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + buffer[STRIMERLCONNECT_COMMAND_BYTE] |= zone; + + for(size_t i = 0; i < led_count; i++) + { + uint8_t offset = (3 * (uint8_t)i) + STRIMERLCONNECT_DATA_BYTE; + + buffer[offset] = RGBGetRValue(led_colours[i]); + buffer[offset + 1] = RGBGetBValue(led_colours[i]); + buffer[offset + 2] = RGBGetGValue(led_colours[i]); + } + + hid_write(dev, buffer, STRIMERLCONNECT_PACKET_SIZE); +} diff --git a/Controllers/LianLiController/LianLiStrimerLConnectController/LianLiStrimerLConnectController.h b/Controllers/LianLiController/LianLiStrimerLConnectController/LianLiStrimerLConnectController.h new file mode 100644 index 0000000..3187450 --- /dev/null +++ b/Controllers/LianLiController/LianLiStrimerLConnectController/LianLiStrimerLConnectController.h @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| LianLiStrimerLConnectController.h | +| | +| Driver for Lian Li Strimer L Connect | +| | +| Chris M (Dr_No) 03 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "LogManager.h" +#include "RGBController.h" + +#define HID_MAX_STR 255 +#define STRIMERLCONNECT_PACKET_SIZE 255 //Buffer requires a prepended ReportID hence + 1 + +#define STRIMERLCONNECT_BRIGHTNESS_MIN 0 //Brightness indexes not values +#define STRIMERLCONNECT_BRIGHTNESS_MAX 4 +#define STRIMERLCONNECT_STRIP_COUNT 12 + +enum +{ + STRIMERLCONNECT_MODE_OFF = 0x00, //Turn off - All leds off + STRIMERLCONNECT_MODE_DIRECT = 0x01, //Direct Led Control - Independently set LEDs in zone + STRIMERLCONNECT_MODE_BREATHING = 0x02, //Breathing Mode - Fades between fully off and fully on. + STRIMERLCONNECT_MODE_FLASHING = 0x03, //Flashing Mode - Abruptly changing between fully off and fully on. + STRIMERLCONNECT_MODE_RAINBOWMORPH = 0x04, //Rainbow Morph Mode + STRIMERLCONNECT_MODE_RAINBOW = 0x05, //Rainbow Wave Mode - Cycle thru the color spectrum as a wave across all LEDs + STRIMERLCONNECT_MODE_BREATHCYCLE = 0x06, //Spectrum Cycle Mode - Cycles through the color spectrum on all lights on the device + + STRIMERLCONNECT_MODE_SNOOKER = 0x19, //Snooker Mode + STRIMERLCONNECT_MODE_MIXING = 0x1A, //Mixing Mode + STRIMERLCONNECT_MODE_PINGPONG = 0x1B, //Ping Pong Mode + STRIMERLCONNECT_MODE_RUNWAY = 0x1C, //Runway Mode + STRIMERLCONNECT_MODE_PAINTING = 0x1D, //Painting Mode + STRIMERLCONNECT_MODE_TIDE = 0x1E, //Tide Mode + STRIMERLCONNECT_MODE_BLOWUP = 0x1F, //Blow Up Mode + STRIMERLCONNECT_MODE_METEOR = 0x20, //Meteor Mode + + STRIMERLCONNECT_MODE_SHOCKWAVE = 0x21, //Shock Wave Mode + STRIMERLCONNECT_MODE_RIPPLE = 0x22, //Ripple Mode + STRIMERLCONNECT_MODE_VOICE = 0x23, //Voice Mode + STRIMERLCONNECT_MODE_BULLETSTACK = 0x24, //Bullet Stack Mode + STRIMERLCONNECT_MODE_DRIZZLING = 0x25, //Drizzling Mode + STRIMERLCONNECT_MODE_FADEOUT = 0x26, //Fade Out Mode + STRIMERLCONNECT_MODE_COLORTRANSFER = 0x27, //Color Transfer Mode + STRIMERLCONNECT_MODE_CROSSOVER = 0x28, //Cross Over Mode + STRIMERLCONNECT_MODE_TWINKLE = 0x29, //Twinkle Mode + STRIMERLCONNECT_MODE_CONTEST = 0x2A, //Contest Mode + STRIMERLCONNECT_MODE_PARALLEL = 0x2B, //Parallel Mode +}; + +enum +{ + STRIMERLCONNECT_COMMAND_BYTE = 1, + STRIMERLCONNECT_DATA_BYTE = 2, + STRIMERLCONNECT_SPEED_BYTE = 3, + STRIMERLCONNECT_DIRECTION_BYTE = 4, + STRIMERLCONNECT_BRIGHTNESS_BYTE = 5, + + STRIMERLCONNECT_MODE_COMMAND = 0x10, + STRIMERLCONNECT_COLOUR_COMMAND = 0x30, + STRIMERLCONNECT_REPORT_ID = 0xE0, +}; + +enum +{ + STRIMERLCONNECT_SPEED_SLOWEST = 0, + STRIMERLCONNECT_SPEED_NORMAL = 2, + STRIMERLCONNECT_SPEED_FASTEST = 4, +}; + +class LianLiStrimerLConnectController +{ +public: + LianLiStrimerLConnectController(hid_device* dev_handle, const char* path); + ~LianLiStrimerLConnectController(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + void SendApply(); + void SetMode(uint8_t mode, uint8_t zone, uint8_t speed, uint8_t brightness, uint8_t direction, bool random_colours); + void SetLedsDirect(uint8_t zone, RGBColor *led_colours, uint8_t led_count); +private: + std::string device_name; + std::string location; + hid_device* dev; +}; diff --git a/Controllers/LianLiController/LianLiStrimerLConnectController/RGBController_LianLiStrimerLConnect.cpp b/Controllers/LianLiController/LianLiStrimerLConnectController/RGBController_LianLiStrimerLConnect.cpp new file mode 100644 index 0000000..04cefdd --- /dev/null +++ b/Controllers/LianLiController/LianLiStrimerLConnectController/RGBController_LianLiStrimerLConnect.cpp @@ -0,0 +1,336 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiStrimerLConnect.cpp | +| | +| RGBController for Lian Li Strimer L Connect | +| | +| Chris M (Dr_No) 03 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LianLiStrimerLConnect.h" + +/**------------------------------------------------------------------*\ + @name Lian Li Strimer L Connect + @category LEDStrip + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectLianLiStrimerControllers + @comment The Lian Li Strimer L Connect `Direct` mode stutters at high frame rates and + and has been rate limited to ~10FPS. +\*-------------------------------------------------------------------*/ + +RGBController_LianLiStrimerLConnect::RGBController_LianLiStrimerLConnect(LianLiStrimerLConnectController *controller_ptr) +{ + controller = controller_ptr; + + name = "Lian Li Strimer L Connect"; + vendor = "Lian Li"; + type = DEVICE_TYPE_LEDSTRIP; + description = controller->GetDeviceName(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Off; + Off.name = "Off"; + Off.value = STRIMERLCONNECT_MODE_DIRECT; + Off.brightness = STRIMERLCONNECT_BRIGHTNESS_MIN; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = STRIMERLCONNECT_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Breathing = CreateMode("Breathing", STRIMERLCONNECT_MODE_BREATHING, 0, MODE_COLORS_PER_LED); + Breathing.flags |= MODE_FLAG_HAS_PER_LED_COLOR; + modes.push_back(Breathing); + + mode Flashing = CreateMode("Flashing", STRIMERLCONNECT_MODE_FLASHING, 0, MODE_COLORS_PER_LED); + Flashing.flags |= MODE_FLAG_HAS_PER_LED_COLOR; + modes.push_back(Flashing); + + mode BreathCycle = CreateMode("Breathing Cycle", STRIMERLCONNECT_MODE_BREATHCYCLE, 0, MODE_COLORS_NONE); + modes.push_back(BreathCycle); + + mode Rainbow = CreateMode("Rainbow", STRIMERLCONNECT_MODE_RAINBOW, 0, MODE_COLORS_NONE); + Rainbow.flags |= MODE_FLAG_HAS_DIRECTION_LR; + modes.push_back(Rainbow); + + mode RainbowMorph = CreateMode("Rainbow Morph", STRIMERLCONNECT_MODE_RAINBOWMORPH, 0, MODE_COLORS_NONE); + modes.push_back(RainbowMorph); + + mode Snooker = CreateMode("Snooker", STRIMERLCONNECT_MODE_SNOOKER, 6, MODE_COLORS_MODE_SPECIFIC); + Snooker.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + modes.push_back(Snooker); + + mode Mixing = CreateMode("Mixing", STRIMERLCONNECT_MODE_MIXING, 2, MODE_COLORS_MODE_SPECIFIC); + Mixing.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + modes.push_back(Mixing); + + mode PingPong = CreateMode("Ping Pong", STRIMERLCONNECT_MODE_PINGPONG, 6, MODE_COLORS_MODE_SPECIFIC); + PingPong.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + modes.push_back(PingPong); + + mode Runway = CreateMode("Runway", STRIMERLCONNECT_MODE_RUNWAY, 2, MODE_COLORS_MODE_SPECIFIC); + Runway.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + modes.push_back(Runway); + + mode Painting = CreateMode("Painting", STRIMERLCONNECT_MODE_PAINTING, 6, MODE_COLORS_MODE_SPECIFIC); + Painting.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + modes.push_back(Painting); + + mode Tide = CreateMode("Tide", STRIMERLCONNECT_MODE_TIDE, 6, MODE_COLORS_MODE_SPECIFIC); + Tide.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + modes.push_back(Tide); + + mode BlowUp = CreateMode("Blow Up", STRIMERLCONNECT_MODE_BLOWUP, 6, MODE_COLORS_MODE_SPECIFIC); + BlowUp.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + modes.push_back(BlowUp); + + mode Meteor = CreateMode("Meteor", STRIMERLCONNECT_MODE_METEOR, 6, MODE_COLORS_MODE_SPECIFIC); + Meteor.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + modes.push_back(Meteor); + + mode ColorTransfer = CreateMode("Color Transfer", STRIMERLCONNECT_MODE_COLORTRANSFER, 6, MODE_COLORS_MODE_SPECIFIC); + ColorTransfer.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + modes.push_back(ColorTransfer); + + mode FadeOut = CreateMode("Fade Out", STRIMERLCONNECT_MODE_FADEOUT, 6, MODE_COLORS_MODE_SPECIFIC); + FadeOut.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + modes.push_back(FadeOut); + + mode Contest = CreateMode("Contest", STRIMERLCONNECT_MODE_CONTEST, 6, MODE_COLORS_MODE_SPECIFIC); + Contest.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + modes.push_back(Contest); + + mode CrossOver = CreateMode("Cross Over", STRIMERLCONNECT_MODE_CROSSOVER, 6, MODE_COLORS_MODE_SPECIFIC); + CrossOver.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + modes.push_back(CrossOver); + + mode BulletStack = CreateMode("Bullet Stack", STRIMERLCONNECT_MODE_BULLETSTACK, 0, MODE_COLORS_NONE); + BulletStack.flags |= MODE_FLAG_HAS_DIRECTION_LR; + modes.push_back(BulletStack); + + mode Twinkle = CreateMode("Twinkle", STRIMERLCONNECT_MODE_TWINKLE, 0, MODE_COLORS_NONE); + modes.push_back(Twinkle); + + mode Parallel = CreateMode("Parallel", STRIMERLCONNECT_MODE_PARALLEL, 6, MODE_COLORS_MODE_SPECIFIC); + Parallel.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + modes.push_back(Parallel); + + mode ShockWave = CreateMode("Shock Wave", STRIMERLCONNECT_MODE_SHOCKWAVE, 6, MODE_COLORS_MODE_SPECIFIC); + ShockWave.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + modes.push_back(ShockWave); + + mode Ripple = CreateMode("Ripple", STRIMERLCONNECT_MODE_RIPPLE, 6, MODE_COLORS_MODE_SPECIFIC); + Ripple.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + modes.push_back(Ripple); + + mode Voice = CreateMode("Voice", STRIMERLCONNECT_MODE_VOICE, 6, MODE_COLORS_MODE_SPECIFIC); + Voice.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + modes.push_back(Voice); + + mode Drizzling = CreateMode("Drizzling", STRIMERLCONNECT_MODE_DRIZZLING, 6, MODE_COLORS_MODE_SPECIFIC); + Drizzling.flags |= MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + modes.push_back(Drizzling); + + Init_Controller(); + SetupZones(); +} + +RGBController_LianLiStrimerLConnect::~RGBController_LianLiStrimerLConnect() +{ + delete controller; +} + +void RGBController_LianLiStrimerLConnect::Init_Controller() +{ + const uint8_t zone_split = STRIMERLCONNECT_STRIP_COUNT / 2; + + /*-------------------------------------------------*\ + | Create the device's controllable zones | + \*-------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zone_split; zone_idx++) + { + zone new_zone; + new_zone.name = "24 Pin ATX Strip "; + new_zone.name.append(std::to_string(zone_idx)); + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 20; + new_zone.leds_max = 20; + new_zone.leds_count = new_zone.leds_max; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + } + + for(std::size_t zone_idx = zone_split; zone_idx < STRIMERLCONNECT_STRIP_COUNT; zone_idx++) + { + zone new_zone; + new_zone.name = "8 Pin GPU Strip "; + new_zone.name.append(std::to_string(zone_idx - zone_split)); + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 27; + new_zone.leds_max = 27; + new_zone.leds_count = new_zone.leds_max; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + } +} + +void RGBController_LianLiStrimerLConnect::SetupZones() +{ + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int lp_idx = 0; lp_idx < zones[zone_idx].leds_count; lp_idx++) + { + led new_led; + + new_led.name = zones[zone_idx].name; + new_led.name.append(" LED " + std::to_string(lp_idx)); + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_LianLiStrimerLConnect::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +bool RGBController_LianLiStrimerLConnect::TimeToSend() +{ + /*-----------------------------------------------------*\ + | Rate limit is 1000(ms) / wait_time in Frames Per Sec | + \*-----------------------------------------------------*/ + const uint8_t wait_time = 90; + + return (std::chrono::steady_clock::now() - last_commit_time) > std::chrono::milliseconds(wait_time); +} + +void RGBController_LianLiStrimerLConnect::DeviceUpdateLEDs() +{ + if(TimeToSend()) + { + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + UpdateZoneLEDs((int)zone_idx); + } + + controller->SendApply(); + + /*-----------------------------------------------------*\ + | Update last commit time | + \*-----------------------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); + } +} + +void RGBController_LianLiStrimerLConnect::UpdateZoneLEDs(int zone) +{ + mode current_mode = modes[active_mode]; + + controller->SetLedsDirect(zone, zones[zone].colors, zones[zone].leds_count); + controller->SetMode(current_mode.value, zone, current_mode.speed, current_mode.brightness, current_mode.direction, false); +} + +void RGBController_LianLiStrimerLConnect::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(GetLED_Zone(led)); + controller->SendApply(); +} + +void RGBController_LianLiStrimerLConnect::DeviceUpdateMode() +{ + if(TimeToSend()) + { + mode current_mode = modes[active_mode]; + + if(current_mode.color_mode == MODE_COLORS_PER_LED) + { + return; + } + + bool random_colours = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + + if(current_mode.color_mode == MODE_COLORS_NONE) + { + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetMode((uint8_t)current_mode.value, (uint8_t)zone_idx, current_mode.speed, current_mode.brightness, current_mode.direction, random_colours); + } + } + else + { + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetLedsDirect((uint8_t)zone_idx, ¤t_mode.colors[0], (uint8_t)current_mode.colors.size()); + controller->SetMode((uint8_t)current_mode.value, (uint8_t)zone_idx, current_mode.speed, current_mode.brightness, current_mode.direction, random_colours); + } + } + + controller->SendApply(); + + /*-----------------------------------------------------*\ + | Update last commit time | + \*-----------------------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); + } +} + +int RGBController_LianLiStrimerLConnect::GetLED_Zone(int led_idx) +{ + for(size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + int zone_start = zones[zone_idx].start_idx; + int zone_end = zone_start + zones[zone_idx].leds_count - 1; + + if( zone_start <= led_idx && zone_end >= led_idx) + { + return((int)zone_idx); + } + } + + return(-1); +} + +mode RGBController_LianLiStrimerLConnect::CreateMode(std::string name, int value, uint8_t colour_count, uint8_t colour_mode) +{ + mode new_mode; + new_mode.name = name; + new_mode.value = value; + new_mode.colors_min = colour_count; + new_mode.colors_max = colour_count; + new_mode.colors.resize(colour_count); + new_mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + new_mode.brightness_min = STRIMERLCONNECT_BRIGHTNESS_MIN; + new_mode.brightness_max = STRIMERLCONNECT_BRIGHTNESS_MAX; + new_mode.brightness = STRIMERLCONNECT_BRIGHTNESS_MAX; + new_mode.speed_min = STRIMERLCONNECT_SPEED_SLOWEST; + new_mode.speed_max = STRIMERLCONNECT_SPEED_FASTEST; + new_mode.speed = STRIMERLCONNECT_SPEED_NORMAL; + new_mode.color_mode = colour_mode; + + return new_mode; +} diff --git a/Controllers/LianLiController/LianLiStrimerLConnectController/RGBController_LianLiStrimerLConnect.h b/Controllers/LianLiController/LianLiStrimerLConnectController/RGBController_LianLiStrimerLConnect.h new file mode 100644 index 0000000..fa48f65 --- /dev/null +++ b/Controllers/LianLiController/LianLiStrimerLConnectController/RGBController_LianLiStrimerLConnect.h @@ -0,0 +1,44 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiStrimerLConnect.h | +| | +| RGBController for Lian Li Strimer L Connect | +| | +| Chris M (Dr_No) 03 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "LogManager.h" +#include "RGBController.h" +#include "LianLiStrimerLConnectController.h" + +class RGBController_LianLiStrimerLConnect : public RGBController +{ +public: + RGBController_LianLiStrimerLConnect(LianLiStrimerLConnectController* controller_ptr); + ~RGBController_LianLiStrimerLConnect(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + void Init_Controller(); + int GetDeviceMode(); + int GetLED_Zone(int led_idx); + + mode CreateMode(std::string name, int value, uint8_t colour_count, uint8_t colour_mode); + bool TimeToSend(); + + LianLiStrimerLConnectController* controller; + std::chrono::time_point last_commit_time; +}; diff --git a/Controllers/LianLiController/LianLiUniHubALController/LianLiUniHubALController.cpp b/Controllers/LianLiController/LianLiUniHubALController/LianLiUniHubALController.cpp new file mode 100644 index 0000000..549ad4a --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubALController/LianLiUniHubALController.cpp @@ -0,0 +1,402 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubALController.cpp | +| | +| Driver for Lian Li AL Uni Hub | +| | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LianLiUniHubALController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +LianLiUniHubALController::LianLiUniHubALController(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + dev_pid = pid; + location = path; + name = dev_name; +} + +LianLiUniHubALController::~LianLiUniHubALController() +{ + hid_close(dev); +} + +std::string LianLiUniHubALController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LianLiUniHubALController::GetFirmwareVersionString() +{ + wchar_t product_string[40]; + int ret = hid_get_product_string(dev, product_string, 40); + + if (ret != 0) + { + return (""); + } + + std::string return_string = StringUtils::wstring_to_string(product_string); + + return(return_string.substr(return_string.find_last_of("-")+1,4).c_str()); +} + +std::string LianLiUniHubALController::GetName() +{ + return(name); +} + +std::string LianLiUniHubALController::GetSerialString() +{ + wchar_t serial_string[20]; + int ret = hid_get_serial_number_string(dev, serial_string, 20); + + if (ret != 0) + { + return (""); + } + + std::string return_string = StringUtils::wstring_to_string(serial_string); + + return(return_string); + +} + +void LianLiUniHubALController::SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors, float brightness) +{ + unsigned char fan_led_data[96]; + unsigned char edge_led_data[144]; + int fan_idx = 0; + int mod_led_idx; + int cur_led_idx; + + if(num_colors == 0) + { + return; // Do nothing, channel isn't in use + } + + for(unsigned int led_idx = 0; led_idx < num_colors; led_idx++) + { + mod_led_idx = (led_idx % 20); + + if((mod_led_idx == 0) && (led_idx != 0)) + { + fan_idx++; + } + + /*---------------------------------------------------------*\ + | Limiter to protect LEDs | + \*---------------------------------------------------------*/ + if(UNIHUB_AL_LED_LIMITER && RGBGetRValue(colors[led_idx]) > 153 && (RGBGetRValue(colors[led_idx]) == RGBGetBValue(colors[led_idx])) && (RGBGetRValue(colors[led_idx]) == RGBGetGValue(colors[led_idx])) ) + { + colors[led_idx] = ToRGBColor(153,153,153); + } + + if(mod_led_idx < 8) // Fan LEDs, 8 LEDs per fan + { + //Determine current position of led_data array from colors array + cur_led_idx = ((mod_led_idx + (fan_idx * 8)) * 3); + + fan_led_data[cur_led_idx + 0] = (unsigned char)(RGBGetRValue(colors[led_idx]) * brightness); + fan_led_data[cur_led_idx + 1] = (unsigned char)(RGBGetBValue(colors[led_idx]) * brightness); + fan_led_data[cur_led_idx + 2] = (unsigned char)(RGBGetGValue(colors[led_idx]) * brightness); + } + else // Edge LEDs, 12 LEDs per fan + { + //Determine current position of led_data array from colors array + cur_led_idx = (((mod_led_idx - 8) + (fan_idx * 12)) * 3); + + edge_led_data[cur_led_idx + 0] = (unsigned char)(RGBGetRValue(colors[led_idx]) * brightness); + edge_led_data[cur_led_idx + 1] = (unsigned char)(RGBGetBValue(colors[led_idx]) * brightness); + edge_led_data[cur_led_idx + 2] = (unsigned char)(RGBGetGValue(colors[led_idx]) * brightness); + } + } + + /*---------------------------------------------------------*\ + | Send fan LED data | + \*---------------------------------------------------------*/ + + SendStartAction + ( + channel, // Current channel + (fan_idx + 1) // Number of fans + ); + + SendColorData + ( + channel, // Channel + 0, // 0 = Fan, 1 = Edge + (fan_idx + 1)*8, + fan_led_data + ); + + SendCommitAction + ( + channel, // Channel + 0, // 0 = Fan, 1 = Edge + UNIHUB_AL_LED_MODE_STATIC_COLOR, // Effect + UNIHUB_AL_LED_SPEED_000, // Speed + UNIHUB_AL_LED_DIRECTION_LTR, // Direction + UNIHUB_AL_LED_BRIGHTNESS_100 // Brightness + ); + + /*---------------------------------------------------------*\ + | Send edge LED data | + \*---------------------------------------------------------*/ + SendStartAction + ( + channel, // Current channel + (fan_idx + 1) // Number of fans + ); + + SendColorData + ( + channel, // Channel + 1, // 0 = Fan, 1 = Edge + (fan_idx + 1)*12, + edge_led_data + ); + + SendCommitAction + ( + channel, // Channel + 1, // 0 = Fan, 1 = Edge + UNIHUB_AL_LED_MODE_STATIC_COLOR, // Effect + UNIHUB_AL_LED_SPEED_000, // Speed + UNIHUB_AL_LED_DIRECTION_LTR, // Direction + UNIHUB_AL_LED_BRIGHTNESS_100 // Brightness + ); + +} + +void LianLiUniHubALController::SetChannelMode(unsigned char channel, unsigned int mode_value, std::vector colors, unsigned int num_colors, unsigned int num_fans, bool upd_both_fan_edge, unsigned int brightness, unsigned int speed, unsigned int direction) +{ + static unsigned int brightness_code[5] = + { + UNIHUB_AL_LED_BRIGHTNESS_000, + UNIHUB_AL_LED_BRIGHTNESS_025, + UNIHUB_AL_LED_BRIGHTNESS_050, + UNIHUB_AL_LED_BRIGHTNESS_075, + UNIHUB_AL_LED_BRIGHTNESS_100 + }; + + static unsigned int speed_code[5] = + { + UNIHUB_AL_LED_SPEED_000, + UNIHUB_AL_LED_SPEED_025, + UNIHUB_AL_LED_SPEED_050, + UNIHUB_AL_LED_SPEED_075, + UNIHUB_AL_LED_SPEED_100 + }; + + unsigned char fan_led_data[96]; + unsigned char edge_led_data[144]; + int cur_led_idx; + float brightness_scale; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(fan_led_data, 0x00, sizeof(fan_led_data)); + memset(edge_led_data, 0x00, sizeof(edge_led_data)); + + if(num_colors) // Update led_data if there's colors + { + switch(mode_value) + { + case UNIHUB_AL_LED_MODE_STATIC_COLOR: // Static mode requires a full data array + case UNIHUB_AL_LED_MODE_BREATHING: + brightness_scale = static_cast(brightness)/4; + for(unsigned int i = 0; i < 4; i++) + { + /*---------------------------------------------------------*\ + | Limiter to protect LEDs | + \*---------------------------------------------------------*/ + if(UNIHUB_AL_LED_LIMITER && RGBGetRValue(colors[i]) > 153 && (RGBGetRValue(colors[i]) == RGBGetBValue(colors[i])) && (RGBGetRValue(colors[i]) == RGBGetGValue(colors[i])) ) + { + colors[i] = ToRGBColor(153,153,153); + } + + for(unsigned int led_idx = 0; led_idx < 22; led_idx += 3) + { + cur_led_idx = (i * 8 * 3) + led_idx; + fan_led_data[cur_led_idx + 0] = (unsigned char)(RGBGetRValue(colors[i]) * brightness_scale); + fan_led_data[cur_led_idx + 1] = (unsigned char)(RGBGetBValue(colors[i]) * brightness_scale); + fan_led_data[cur_led_idx + 2] = (unsigned char)(RGBGetGValue(colors[i]) * brightness_scale); + } + + for(unsigned int led_idx = 0; led_idx < 34; led_idx += 3) + { + cur_led_idx = (i * 12 * 3) + led_idx; + edge_led_data[cur_led_idx + 0] = (unsigned char)(RGBGetRValue(colors[i]) * brightness_scale); + edge_led_data[cur_led_idx + 1] = (unsigned char)(RGBGetBValue(colors[i]) * brightness_scale); + edge_led_data[cur_led_idx + 2] = (unsigned char)(RGBGetGValue(colors[i]) * brightness_scale); + } + } + break; + + default: + colors.resize(4); + for(unsigned int i = num_colors; i < 4; i++) + { + colors[i] = 0x00; + } + + // needs a 48 length array of 4 colors, even if less are defined + for(unsigned int i = 0; i < 4; i++) + { + for(unsigned int j = 0; j < 4; j++) + { + cur_led_idx = (i * 12) + (j * 3); + fan_led_data[cur_led_idx + 0] = RGBGetRValue(colors[j]); + fan_led_data[cur_led_idx + 1] = RGBGetBValue(colors[j]); + fan_led_data[cur_led_idx + 2] = RGBGetGValue(colors[j]); + } + } + break; + } + + } + + SendStartAction + ( + channel, // Current channel + (num_fans + 1) // Number of fans + ); + + SendColorData + ( + channel, // Channel + 0, // 0 = Fan, 1 = Edge + (num_fans + 1)*8, + fan_led_data // Data + ); + + SendCommitAction + ( + channel, // Channel + 0, // 0 = Fan, 1 = Edge + mode_value, // Effect + speed_code[speed], // Speed + direction, // Direction + brightness_code[brightness] // Brightness + ); + + if(upd_both_fan_edge) + { + SendStartAction + ( + channel, // Current channel + (num_fans + 1) // Number of fans + ); + + SendColorData + ( + channel, // Channel + 1, // 0 = Fan, 1 = Edge + (num_fans + 1)*12, + edge_led_data + ); + + SendCommitAction + ( + channel, // Channel + 1, // 0 = Fan, 1 = Edge + mode_value, // Effect + speed_code[speed], // Speed + direction, // Direction + brightness_code[brightness] // Brightness + ); + } +} + +void LianLiUniHubALController::SendStartAction(unsigned char channel, unsigned int num_fans) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = UNIHUB_AL_TRANSACTION_ID; + usb_buf[0x01] = 0x10; + usb_buf[0x02] = 0x40; + usb_buf[0x03] = channel + 1; + usb_buf[0x04] = num_fans; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + std::this_thread::sleep_for(5ms); + +} + +void LianLiUniHubALController::SendColorData(unsigned char channel, unsigned int fan_or_edge, unsigned int num_leds, unsigned char* led_data) +{ + /*---------------------------------------------------------*\ + | Send edge LED data | + \*---------------------------------------------------------*/ + + unsigned char usb_buf[146]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = UNIHUB_AL_TRANSACTION_ID; + usb_buf[0x01] = 0x30 + fan_or_edge + (channel * 2); // Channel+device (30 = channel 1 edge, 31 = channel 1 edge, 32 = channel 2 fan, 33 = channel 2 edge, etc.) + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x02], led_data, num_leds * 3); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 146); + std::this_thread::sleep_for(5ms); +} + +void LianLiUniHubALController::SendCommitAction(unsigned char channel, unsigned int fan_or_edge, unsigned char effect, unsigned char speed, unsigned int direction, unsigned int brightness) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = UNIHUB_AL_TRANSACTION_ID; + usb_buf[0x01] = 0x10 + fan_or_edge + (channel*2); // Channel+device (10 = channel 1 fan, 11 = channel 1 edge, 12 = channel 2 fan, 13 = chanell 2 edge, etc.) + usb_buf[0x02] = effect; // Effect + usb_buf[0x03] = speed; // Speed, 02=0%, 01=25%, 00=50%, ff=75%, fe=100% + usb_buf[0x04] = direction; // Direction, right=00, left=01 + usb_buf[0x05] = brightness; // Brightness, 0=100%, 1= 75%, 2 = 50%, 3 = 25%, 8 = 0% + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + std::this_thread::sleep_for(5ms); +} diff --git a/Controllers/LianLiController/LianLiUniHubALController/LianLiUniHubALController.h b/Controllers/LianLiController/LianLiUniHubALController/LianLiUniHubALController.h new file mode 100644 index 0000000..c1fd318 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubALController/LianLiUniHubALController.h @@ -0,0 +1,217 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubALController.h | +| | +| Driver for Lian Li AL Uni Hub | +| | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*----------------------------------------------------------------------------*\ +| Global definitions. | +\*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*\ +| Definitions related to zone Sizes | +\*----------------------------------------------------------------------------*/ + + +enum +{ + UNIHUB_AL_CHANNEL_COUNT = 0x04, /* Channel count */ + UNIHUB_AL_CHAN_FANLED_COUNT = 0x20, /* Max-LED per channel count - 32 */ + UNIHUB_AL_CHAN_EDGELED_COUNT = 0x30, /* Max-LED per channel count - 48 */ + UNIHUB_AL_CHAN_LED_COUNT = 0x50, /* Max-LED per channel count - 80 */ +}; + +/*----------------------------------------------------------------------------*\ +| Definitions related to LED configuration. | +\*----------------------------------------------------------------------------*/ + +// Used for sync'd mode between Fan and Edge + +enum +{ + UNIHUB_AL_LED_MODE_RAINBOW = 0x28, /* Rainbow mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_RAINBOW_MORPH = 0x35, /* Rainbow Morph mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_STATIC_COLOR = 0x01, /* Static Color mode */ + UNIHUB_AL_LED_MODE_BREATHING = 0x02, /* Breathing mode */ + UNIHUB_AL_LED_MODE_TAICHI = 0x2C, /* Neon mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_COLOR_CYCLE = 0x2B, /* Color Cycle mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_RUNWAY = 0x1A, /* Runway mode */ + UNIHUB_AL_LED_MODE_METEOR = 0x19, /* Meteor mode */ + UNIHUB_AL_LED_MODE_WARNING = 0x2D, /* Warning mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_VOICE = 0x2E, /* Voice mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_SPINNING_TEACUP = 0x38, /* Spinning Teacup mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_TORNADO = 0x36, /* Tornado mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_MIXING = 0x2F, /* Mixing mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_STACK = 0x30, /* Stack mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_STAGGGERED = 0x37, /* Stagggered mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_TIDE = 0x31, /* Tide mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_SCAN = 0x32, /* Scan mode - Calls Fan only */ + UNIHUB_AL_LED_MODE_CONTEST = 0x33, /* Contest mode - Calls Fan only */ + +}; + +enum +{ + UNIHUB_AL_LED_SPEED_000 = 0x02, /* Very slow speed */ + UNIHUB_AL_LED_SPEED_025 = 0x01, /* Rather slow speed */ + UNIHUB_AL_LED_SPEED_050 = 0x00, /* Medium speed */ + UNIHUB_AL_LED_SPEED_075 = 0xFF, /* Rather fast speed */ + UNIHUB_AL_LED_SPEED_100 = 0xFE, /* Very fast speed */ +}; + +enum +{ + UNIHUB_AL_LED_DIRECTION_LTR = 0x00, /* Left-to-Right direction */ + UNIHUB_AL_LED_DIRECTION_RTL = 0x01, /* Right-to-Left direction */ +}; + +enum +{ + UNIHUB_AL_LED_BRIGHTNESS_000 = 0x08, /* Very dark (off) */ + UNIHUB_AL_LED_BRIGHTNESS_025 = 0x03, /* Rather dark */ + UNIHUB_AL_LED_BRIGHTNESS_050 = 0x02, /* Medium bright */ + UNIHUB_AL_LED_BRIGHTNESS_075 = 0x01, /* Rather bright */ + UNIHUB_AL_LED_BRIGHTNESS_100 = 0x00, /* Very bright */ +}; + +enum +{ + UNIHUB_AL_LED_LIMITER = 0x01 /* Limit the color white to 999999 as per manufacturer limits */ +}; + + +/*----------------------------------------------------------------------------*\ +| Definitions related to packet configuration. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_AL_TRANSACTION_ID = 0xE0, /* Command value to start all packets */ +}; + +/*----------------------------------------------------------------------------*\ +| Uni Hub AL controller. | +\*----------------------------------------------------------------------------*/ + +class LianLiUniHubALController +{ + + +public: + LianLiUniHubALController(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name); + ~LianLiUniHubALController(); + + std::string GetDeviceLocation(); + std::string GetFirmwareVersionString(); + std::string GetName(); + std::string GetSerialString(); + + void SetChannelMode + ( + unsigned char channel, + unsigned int mode_value, + std::vector colors, // Not a pointer because the copy gets resized + unsigned int num_colors, + unsigned int num_fans, + bool upd_both_fan_edge, + unsigned int brightness, + unsigned int speed, + unsigned int direction + ); + + void SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors, + float brightness + ); + + void SendStartAction + ( + unsigned char channel, + unsigned int num_fans + ); + + void SendColorData + ( + unsigned char channel, // Zone index + unsigned int fan_or_edge, // 1 (Fan) or 0 (Edge) modifer to channel + unsigned int num_leds, + unsigned char* led_data // Color data payload + ); + + void SendCommitAction + ( + unsigned char channel, // Zone index + unsigned int fan_or_edge, // 1 (Fan) or 0 (Edge) modifer to channel + unsigned char effect, + unsigned char speed, + unsigned int direction, + unsigned int brightness + ); + +private: + /* The Uni Hub requires colors in RBG order */ + struct Color + { + uint8_t r; + uint8_t b; + uint8_t g; + }; + + /* The values correspond to the definitions above */ + struct Channel + { + uint8_t index; + + uint8_t anyFanCountOffset; + uint8_t anyFanCount; + + uint16_t ledActionAddress; + uint16_t ledCommitAddress; + uint16_t ledModeAddress; + uint16_t ledSpeedAddress; + uint16_t ledDirectionAddress; + uint16_t ledBrightnessAddress; + + Color colors[UNIHUB_AL_CHAN_FANLED_COUNT]; + + uint8_t ledMode; + uint8_t ledSpeed; + uint8_t ledDirection; + uint8_t ledBrightness; + + uint16_t fanHubActionAddress; + uint16_t fanHubCommitAddress; + + uint16_t fanPwmActionAddress; + uint16_t fanPwmCommitAddress; + uint16_t fanRpmActionAddress; + + uint16_t fanSpeed; + }; + +private: + hid_device* dev; + unsigned short dev_pid; + + /*---------------------------------------------------------*\ + | Device information strings | + \*---------------------------------------------------------*/ + std::string firmware_version; + std::string location; + std::string name; +}; diff --git a/Controllers/LianLiController/LianLiUniHubALController/RGBController_LianLiUniHubAL.cpp b/Controllers/LianLiController/LianLiUniHubALController/RGBController_LianLiUniHubAL.cpp new file mode 100644 index 0000000..cb6ce17 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubALController/RGBController_LianLiUniHubAL.cpp @@ -0,0 +1,524 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHubAL.cpp | +| | +| RGBController for Lian Li AL Uni Hub | +| | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_LianLiUniHubAL.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[8][35] = + { { NA, NA, 10, NA, NA, 11, NA, NA, NA, NA, NA, 30, NA, NA, 31, NA, NA, NA, NA, NA, 50, NA, NA, 51, NA, NA, NA, NA, NA, 70, NA, NA, 71, NA, NA}, + { NA, 9, NA, NA, NA, NA, 12, NA, NA, NA, 29, NA, NA, NA, NA, 32, NA, NA, NA, 49, NA, NA, NA, NA, 52, NA, NA, NA, 69, NA, NA, NA, NA, 72, NA}, + { 8, NA, NA, 1, 2, NA, NA, 13, NA, 28, NA, NA, 21, 22, NA, NA, 33, NA, 48, NA, NA, 41, 42, NA, NA, 53, NA, 68, NA, NA, 61, 62, NA, NA, 73}, + { NA, NA, 0, NA, NA, 3, NA, NA, NA, NA, NA, 20, NA, NA, 23, NA, NA, NA, NA, NA, 40, NA, NA, 43, NA, NA, NA, NA, NA, 60, NA, NA, 63, NA, NA}, + { NA, NA, 7, NA, NA, 4, NA, NA, NA, NA, NA, 27, NA, NA, 24, NA, NA, NA, NA, NA, 47, NA, NA, 44, NA, NA, NA, NA, NA, 67, NA, NA, 64, NA, NA}, + { 19, NA, NA, 6, 5, NA, NA, 14, NA, 39, NA, NA, 26, 25, NA, NA, 34, NA, 59, NA, NA, 46, 45, NA, NA, 54, NA, 79, NA, NA, 66, 65, NA, NA, 74}, + { NA, 18, NA, NA, NA, NA, 15, NA, NA, NA, 38, NA, NA, NA, NA, 35, NA, NA, NA, 58, NA, NA, NA, NA, 55, NA, NA, NA, 78, NA, NA, NA, NA, 75, NA}, + { NA, NA, 17, NA, NA, 16, NA, NA, NA, NA, NA, 37, NA, NA, 36, NA, NA, NA, NA, NA, 57, NA, NA, 56, NA, NA, NA, NA, NA, 77, NA, NA, 76, NA, NA} + }; + +/**------------------------------------------------------------------*\ + @name Lian Li Uni Hub AL + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectLianLiUniHubAL + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LianLiUniHubAL::RGBController_LianLiUniHubAL(LianLiUniHubALController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Lian Li"; + type = DEVICE_TYPE_COOLER; + description = "Lian Li Uni Hub - AL"; + version = controller->GetFirmwareVersionString(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + + initializedMode = false; + + mode Custom; + Custom.name = "Custom"; + Custom.value = UNIHUB_AL_LED_MODE_STATIC_COLOR; + Custom.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Custom.brightness_min = 0; + Custom.brightness_max = 50; + Custom.brightness = 37; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = UNIHUB_AL_LED_MODE_RAINBOW; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + RainbowWave.speed_min = 0; + RainbowWave.speed_max = 4; + RainbowWave.brightness_min = 0; + RainbowWave.brightness_max = 4; + RainbowWave.speed = 3; + RainbowWave.brightness = 3; + RainbowWave.direction = UNIHUB_AL_LED_DIRECTION_LTR; + RainbowWave.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowWave); + + mode RainbowMorph; + RainbowMorph.name = "Rainbow Morph"; + RainbowMorph.value = UNIHUB_AL_LED_MODE_RAINBOW_MORPH; + RainbowMorph.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + RainbowMorph.speed_min = 0; + RainbowMorph.speed_max = 4; + RainbowMorph.brightness_min = 0; + RainbowMorph.brightness_max = 4; + RainbowMorph.speed = 3; + RainbowMorph.brightness = 3; + RainbowMorph.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowMorph); + + mode StaticColor; + StaticColor.name = "Static Color"; + StaticColor.value = UNIHUB_AL_LED_MODE_STATIC_COLOR; + StaticColor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + StaticColor.brightness_min = 0; + StaticColor.brightness_max = 4; + StaticColor.colors_min = 0; + StaticColor.colors_max = 4; + StaticColor.brightness = 3; + StaticColor.color_mode = MODE_COLORS_MODE_SPECIFIC; + StaticColor.colors.resize(4); + modes.push_back(StaticColor); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = UNIHUB_AL_LED_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.speed_min = 0; + Breathing.speed_max = 4; + Breathing.brightness_min = 0; + Breathing.brightness_max = 4; + Breathing.colors_min = 0; + Breathing.colors_max = 4; + Breathing.speed = 3; + Breathing.brightness = 3; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(4); + modes.push_back(Breathing); + + mode Taichi; + Taichi.name = "Taichi"; + Taichi.value = UNIHUB_AL_LED_MODE_TAICHI; + Taichi.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Taichi.speed_min = 0; + Taichi.speed_max = 4; + Taichi.brightness_min = 0; + Taichi.brightness_max = 4; + Taichi.colors_min = 0; + Taichi.colors_max = 2; + Taichi.speed = 3; + Taichi.brightness = 3; + Taichi.direction = UNIHUB_AL_LED_DIRECTION_LTR; + Taichi.color_mode = MODE_COLORS_MODE_SPECIFIC; + Taichi.colors.resize(2); + modes.push_back(Taichi); + + mode ColorCycle; + ColorCycle.name = "ColorCycle"; + ColorCycle.value = UNIHUB_AL_LED_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + ColorCycle.speed_min = 0; + ColorCycle.speed_max = 4; + ColorCycle.brightness_min = 0; + ColorCycle.brightness_max = 4; + ColorCycle.colors_min = 0; + ColorCycle.colors_max = 4; + ColorCycle.speed = 3; + ColorCycle.brightness = 3; + ColorCycle.direction = UNIHUB_AL_LED_DIRECTION_LTR; + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorCycle.colors.resize(4); + modes.push_back(ColorCycle); + + mode Runway; + Runway.name = "Runway"; + Runway.value = UNIHUB_AL_LED_MODE_RUNWAY; + Runway.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Runway.speed_min = 0; + Runway.speed_max = 4; + Runway.brightness_min = 0; + Runway.brightness_max = 4; + Runway.colors_min = 0; + Runway.colors_max = 2; + Runway.speed = 3; + Runway.brightness = 3; + Runway.color_mode = MODE_COLORS_MODE_SPECIFIC; + Runway.colors.resize(2); + modes.push_back(Runway); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = UNIHUB_AL_LED_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Meteor.speed_min = 0; + Meteor.speed_max = 4; + Meteor.brightness_min = 0; + Meteor.brightness_max = 4; + Meteor.colors_min = 0; + Meteor.colors_max = 4; + Meteor.speed = 3; + Meteor.brightness = 3; + Meteor.direction = UNIHUB_AL_LED_DIRECTION_LTR; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors.resize(4); + modes.push_back(Meteor); + + mode Warning; + Warning.name = "Warning"; + Warning.value = UNIHUB_AL_LED_MODE_WARNING; + Warning.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Warning.speed_min = 0; + Warning.speed_max = 4; + Warning.brightness_min = 0; + Warning.brightness_max = 4; + Warning.colors_min = 0; + Warning.colors_max = 4; + Warning.speed = 3; + Warning.brightness = 3; + Warning.color_mode = MODE_COLORS_MODE_SPECIFIC; + Warning.colors.resize(4); + modes.push_back(Warning); + + mode Voice; + Voice.name = "Voice"; + Voice.value = UNIHUB_AL_LED_MODE_VOICE; + Voice.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Voice.speed_min = 0; + Voice.speed_max = 4; + Voice.brightness_min = 0; + Voice.brightness_max = 4; + Voice.colors_min = 0; + Voice.colors_max = 4; + Voice.speed = 3; + Voice.brightness = 3; + Voice.direction = UNIHUB_AL_LED_DIRECTION_LTR; + Voice.color_mode = MODE_COLORS_MODE_SPECIFIC; + Voice.colors.resize(4); + modes.push_back(Voice); + + mode SpinningTeacup; + SpinningTeacup.name = "SpinningTeacup"; + SpinningTeacup.value = UNIHUB_AL_LED_MODE_SPINNING_TEACUP; + SpinningTeacup.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + SpinningTeacup.speed_min = 0; + SpinningTeacup.speed_max = 4; + SpinningTeacup.brightness_min = 0; + SpinningTeacup.brightness_max = 4; + SpinningTeacup.colors_min = 0; + SpinningTeacup.colors_max = 4; + SpinningTeacup.speed = 3; + SpinningTeacup.brightness = 3; + SpinningTeacup.direction = UNIHUB_AL_LED_DIRECTION_LTR; + SpinningTeacup.color_mode = MODE_COLORS_MODE_SPECIFIC; + SpinningTeacup.colors.resize(4); + modes.push_back(SpinningTeacup); + + mode Tornado; + Tornado.name = "Tornado"; + Tornado.value = UNIHUB_AL_LED_MODE_TORNADO; + Tornado.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Tornado.speed_min = 0; + Tornado.speed_max = 4; + Tornado.brightness_min = 0; + Tornado.brightness_max = 4; + Tornado.colors_min = 0; + Tornado.colors_max = 4; + Tornado.speed = 3; + Tornado.brightness = 3; + Tornado.direction = UNIHUB_AL_LED_DIRECTION_LTR; + Tornado.color_mode = MODE_COLORS_MODE_SPECIFIC; + Tornado.colors.resize(4); + modes.push_back(Tornado); + + mode Mixing; + Mixing.name = "Mixing"; + Mixing.value = UNIHUB_AL_LED_MODE_MIXING; + Mixing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Mixing.speed_min = 0; + Mixing.speed_max = 4; + Mixing.brightness_min = 0; + Mixing.brightness_max = 4; + Mixing.colors_min = 0; + Mixing.colors_max = 2; + Mixing.speed = 3; + Mixing.brightness = 3; + Mixing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Mixing.colors.resize(2); + modes.push_back(Mixing); + + mode Stack; + Stack.name = "Stack"; + Stack.value = UNIHUB_AL_LED_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Stack.speed_min = 0; + Stack.speed_max = 4; + Stack.brightness_min = 0; + Stack.brightness_max = 4; + Stack.colors_min = 0; + Stack.colors_max = 2; + Stack.speed = 3; + Stack.brightness = 3; + Stack.direction = UNIHUB_AL_LED_DIRECTION_LTR; + Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stack.colors.resize(2); + modes.push_back(Stack); + + mode Staggered; + Staggered.name = "Staggered"; + Staggered.value = UNIHUB_AL_LED_MODE_STAGGGERED; + Staggered.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Staggered.speed_min = 0; + Staggered.speed_max = 4; + Staggered.brightness_min = 0; + Staggered.brightness_max = 4; + Staggered.colors_min = 0; + Staggered.colors_max = 4; + Staggered.speed = 3; + Staggered.brightness = 3; + Staggered.color_mode = MODE_COLORS_MODE_SPECIFIC; + Staggered.colors.resize(4); + modes.push_back(Staggered); + + mode Tide; + Tide.name = "Tide"; + Tide.value = UNIHUB_AL_LED_MODE_TIDE; + Tide.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Tide.speed_min = 0; + Tide.speed_max = 4; + Tide.brightness_min = 0; + Tide.brightness_max = 4; + Tide.colors_min = 0; + Tide.colors_max = 4; + Tide.speed = 3; + Tide.brightness = 3; + Tide.color_mode = MODE_COLORS_MODE_SPECIFIC; + Tide.colors.resize(4); + modes.push_back(Tide); + + mode Scan; + Scan.name = "Scan"; + Scan.value = UNIHUB_AL_LED_MODE_SCAN; + Scan.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Scan.speed_min = 0; + Scan.speed_max = 4; + Scan.brightness_min = 0; + Scan.brightness_max = 4; + Scan.colors_min = 0; + Scan.colors_max = 2; + Scan.speed = 3; + Scan.brightness = 3; + Scan.color_mode = MODE_COLORS_MODE_SPECIFIC; + Scan.colors.resize(2); + modes.push_back(Scan); + + mode Contest; + Contest.name = "Contest"; + Contest.value = UNIHUB_AL_LED_MODE_CONTEST; + Contest.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Contest.speed_min = 0; + Contest.speed_max = 4; + Contest.brightness_min = 0; + Contest.brightness_max = 4; + Contest.colors_min = 0; + Contest.colors_max = 2; + Contest.speed = 3; + Contest.brightness = 3; + Contest.direction = UNIHUB_AL_LED_DIRECTION_LTR; + Contest.color_mode = MODE_COLORS_MODE_SPECIFIC; + Contest.colors.resize(3); + modes.push_back(Contest); + + RGBController_LianLiUniHubAL::SetupZones(); +} + +RGBController_LianLiUniHubAL::~RGBController_LianLiUniHubAL() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_LianLiUniHubAL::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + zones.resize(UNIHUB_AL_CHANNEL_COUNT); + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for(unsigned int channel_idx = 0; channel_idx < zones.size(); channel_idx++) + { + zones[channel_idx].name = "Channel "; + zones[channel_idx].name.append(std::to_string(channel_idx + 1)); + + // Note: Matrix types won't get loaded from the sizes.ors as the default zone type in this RGBController is ZONE_TYPE_LINEAR + // This will require augmentation on the ProfileManager.cpp to be able to override zone types but this is probably not wanted in general + if (zones[channel_idx].leds_count == 60 || zones[channel_idx].leds_count == 40 || zones[channel_idx].leds_count == 20 || zones[channel_idx].leds_count == 80) // Assume they're AL120 Fans + { + zones[channel_idx].type = ZONE_TYPE_MATRIX; + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = UNIHUB_AL_CHAN_LED_COUNT; + zones[channel_idx].matrix_map = new matrix_map_type; + zones[channel_idx].matrix_map->height = 8; + zones[channel_idx].matrix_map->width = 35; + zones[channel_idx].matrix_map->map = (unsigned int *)&matrix_map; + } + else // Treat as regular LED strip + { + zones[channel_idx].type = ZONE_TYPE_LINEAR; + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = UNIHUB_AL_CHAN_LED_COUNT; + } + + if(first_run) + { + zones[channel_idx].leds_count = zones[channel_idx].leds_min; + } + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + led new_led; + new_led.name = zones[channel_idx].name; + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_ch_idx + 1)); + new_led.value = channel_idx; + + leds.push_back(new_led); + } + + } + + SetupColors(); +} + +void RGBController_LianLiUniHubAL::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_LianLiUniHubAL::DeviceUpdateLEDs() +{ + + if(!initializedMode) + { + DeviceUpdateMode(); + } + + float brightness_scale = static_cast(modes[active_mode].brightness)/modes[active_mode].brightness_max; + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count, brightness_scale); + } +} + +void RGBController_LianLiUniHubAL::UpdateZoneLEDs(int zone) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + + float brightness_scale = static_cast(modes[active_mode].brightness)/modes[active_mode].brightness_max; + + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count, brightness_scale); +} + +void RGBController_LianLiUniHubAL::UpdateSingleLED(int /* led */) +{ + DeviceUpdateMode(); + +} + +void RGBController_LianLiUniHubAL::DeviceUpdateMode() +{ + if(!active_mode) + { + return; // Do nothing, custom mode should go through DeviceUpdateLEDs() to avoid flooding controller + } + + initializedMode = true; + + int fan_idx = 0; + bool upd_both_fan_edge = false; + + /*-----------------------------------------------------*\ + | Check modes that requires updating both arrays | + \*-----------------------------------------------------*/ + + switch(modes[active_mode].value) + { + case UNIHUB_AL_LED_MODE_STATIC_COLOR: + case UNIHUB_AL_LED_MODE_BREATHING: + case UNIHUB_AL_LED_MODE_RUNWAY: + case UNIHUB_AL_LED_MODE_METEOR: + upd_both_fan_edge = true; + break; + } + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].leds_count == 0) + { + return; // Do nothing, channel isn't in use + } + fan_idx = ((zones[zone_idx].leds_count / 20) - 1); // Indexes start at 0 + + controller->SetChannelMode((unsigned char)zone_idx, modes[active_mode].value,modes[active_mode].colors, (unsigned int)modes[active_mode].colors.size(), (fan_idx >= 0 ? fan_idx : 0), upd_both_fan_edge, modes[active_mode].brightness, modes[active_mode].speed, modes[active_mode].direction); + } +} diff --git a/Controllers/LianLiController/LianLiUniHubALController/RGBController_LianLiUniHubAL.h b/Controllers/LianLiController/LianLiUniHubALController/RGBController_LianLiUniHubAL.h new file mode 100644 index 0000000..16ae87c --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubALController/RGBController_LianLiUniHubAL.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHub_AL.h | +| | +| RGBController for Lian Li AL Uni Hub | +| | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "LianLiUniHubALController.h" +#include "RGBController.h" + +class RGBController_LianLiUniHubAL : public RGBController +{ +public: + RGBController_LianLiUniHubAL(LianLiUniHubALController* controller_ptr); + ~RGBController_LianLiUniHubAL(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LianLiUniHubALController* controller; + bool initializedMode; +}; diff --git a/Controllers/LianLiController/LianLiUniHubController/LianLiUniHubController.cpp b/Controllers/LianLiController/LianLiUniHubController/LianLiUniHubController.cpp new file mode 100644 index 0000000..cc19093 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubController/LianLiUniHubController.cpp @@ -0,0 +1,701 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubController.cpp | +| | +| Driver for Lian Li Uni Hub | +| | +| Luca Lovisa 20 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LianLiUniHubController.h" + +using namespace std::chrono_literals; + +/*----------------------------------------------------------------------------*\ +| The Uni Hub is controlled by sending control transfers to various wIndex | +| addresses, allthough it announces some kind of hid interface. Hence it | +| requires libusb as hidapi provides no wIndex customization. | +\*----------------------------------------------------------------------------*/ + +LianLiUniHubController::LianLiUniHubController + ( + libusb_device* device, + libusb_device_descriptor* descriptor + ) +{ + int ret; + + /*--------------------------------------------------------------------*\ + | Open the libusb device. | + \*--------------------------------------------------------------------*/ + ret = libusb_open(device, &handle); + + if(ret < 0) + { + return; + } + + /*--------------------------------------------------------------------*\ + | Fill in the location string from USB port numbers. | + \*--------------------------------------------------------------------*/ + uint8_t ports[7]; + + ret = libusb_get_port_numbers(device, ports, sizeof(ports)); + + if(ret > 0) + { + location = "USB: "; + + for (int i = 0; i < ret; i ++) + { + location += std::to_string(ports[i]); + location.push_back(':'); + } + + location.pop_back(); + } + + /*--------------------------------------------------------------------*\ + | Fill in the serial string from the string descriptor | + \*--------------------------------------------------------------------*/ + char serialStr[64]; + + ret = libusb_get_string_descriptor_ascii(handle, descriptor->iSerialNumber, reinterpret_cast(serialStr), sizeof(serialStr)); + + if(ret > 0) + { + serial = std::string(serialStr, ret); + } + + /*--------------------------------------------------------------------*\ + | Fill in the version string by reading version from device. | + \*--------------------------------------------------------------------*/ + version = ReadVersion(); + + /*--------------------------------------------------------------------*\ + | Create channels with their static configuration and "sane" defaults. | + \*--------------------------------------------------------------------*/ + Channel channel1; + channel1.index = 0; + channel1.anyFanCountOffset = UNIHUB_ANY_C1_FAN_COUNT_OFFSET; + channel1.anyFanCount = UNIHUB_ANY_FAN_COUNT_001; + channel1.ledActionAddress = UNIHUB_LED_C1_ACTION_ADDRESS; + channel1.ledCommitAddress = UNIHUB_LED_C1_COMMIT_ADDRESS; + channel1.ledModeAddress = UNIHUB_LED_C1_MODE_ADDRESS; + channel1.ledSpeedAddress = UNIHUB_LED_C1_SPEED_ADDRESS; + channel1.ledDirectionAddress = UNIHUB_LED_C1_DIRECTION_ADDRESS; + channel1.ledBrightnessAddress = UNIHUB_LED_C1_BRIGHTNESS_ADDRESS; + channel1.ledMode = UNIHUB_LED_MODE_RAINBOW; + channel1.ledSpeed = UNIHUB_LED_SPEED_100; + channel1.ledDirection = UNIHUB_LED_DIRECTION_LTR; + channel1.ledBrightness = UNIHUB_LED_BRIGHTNESS_100; + channel1.fanHubActionAddress = UNIHUB_FAN_C1_HUB_ACTION_ADDRESS; + channel1.fanHubCommitAddress = UNIHUB_FAN_C1_HUB_COMMIT_ADDRESS; + channel1.fanPwmActionAddress = UNIHUB_FAN_C1_PWM_ACTION_ADDRESS; + channel1.fanPwmCommitAddress = UNIHUB_FAN_C1_PWM_COMMIT_ADDRESS; + channel1.fanRpmActionAddress = UNIHUB_FAN_C1_RPM_ACTION_ADDRESS; + channel1.fanSpeed = UNIHUB_FAN_SPEED_QUIET; + channels[0] = channel1; + + Channel channel2; + channel2.index = 1; + channel2.anyFanCountOffset = UNIHUB_ANY_C2_FAN_COUNT_OFFSET; + channel2.anyFanCount = UNIHUB_ANY_FAN_COUNT_001; + channel2.ledActionAddress = UNIHUB_LED_C2_ACTION_ADDRESS; + channel2.ledCommitAddress = UNIHUB_LED_C2_COMMIT_ADDRESS; + channel2.ledModeAddress = UNIHUB_LED_C2_MODE_ADDRESS; + channel2.ledSpeedAddress = UNIHUB_LED_C2_SPEED_ADDRESS; + channel2.ledDirectionAddress = UNIHUB_LED_C2_DIRECTION_ADDRESS; + channel2.ledBrightnessAddress = UNIHUB_LED_C2_BRIGHTNESS_ADDRESS; + channel2.ledMode = UNIHUB_LED_MODE_RAINBOW; + channel2.ledSpeed = UNIHUB_LED_SPEED_100; + channel2.ledDirection = UNIHUB_LED_DIRECTION_LTR; + channel2.ledBrightness = UNIHUB_LED_BRIGHTNESS_100; + channel2.fanHubActionAddress = UNIHUB_FAN_C2_HUB_ACTION_ADDRESS; + channel2.fanHubCommitAddress = UNIHUB_FAN_C2_HUB_COMMIT_ADDRESS; + channel2.fanPwmActionAddress = UNIHUB_FAN_C2_PWM_ACTION_ADDRESS; + channel2.fanPwmCommitAddress = UNIHUB_FAN_C2_PWM_COMMIT_ADDRESS; + channel2.fanRpmActionAddress = UNIHUB_FAN_C2_RPM_ACTION_ADDRESS; + channel2.fanSpeed = UNIHUB_FAN_SPEED_QUIET; + channels[1] = channel2; + + Channel channel3; + channel3.index = 2; + channel3.anyFanCountOffset = UNIHUB_ANY_C3_FAN_COUNT_OFFSET; + channel3.anyFanCount = UNIHUB_ANY_FAN_COUNT_001; + channel3.ledActionAddress = UNIHUB_LED_C3_ACTION_ADDRESS; + channel3.ledCommitAddress = UNIHUB_LED_C3_COMMIT_ADDRESS; + channel3.ledModeAddress = UNIHUB_LED_C3_MODE_ADDRESS; + channel3.ledSpeedAddress = UNIHUB_LED_C3_SPEED_ADDRESS; + channel3.ledDirectionAddress = UNIHUB_LED_C3_DIRECTION_ADDRESS; + channel3.ledBrightnessAddress = UNIHUB_LED_C3_BRIGHTNESS_ADDRESS; + channel3.ledMode = UNIHUB_LED_MODE_RAINBOW; + channel3.ledSpeed = UNIHUB_LED_SPEED_100; + channel3.ledDirection = UNIHUB_LED_DIRECTION_LTR; + channel3.ledBrightness = UNIHUB_LED_BRIGHTNESS_100; + channel3.fanHubActionAddress = UNIHUB_FAN_C3_HUB_ACTION_ADDRESS; + channel3.fanHubCommitAddress = UNIHUB_FAN_C3_HUB_COMMIT_ADDRESS; + channel3.fanPwmActionAddress = UNIHUB_FAN_C3_PWM_ACTION_ADDRESS; + channel3.fanPwmCommitAddress = UNIHUB_FAN_C3_PWM_COMMIT_ADDRESS; + channel3.fanRpmActionAddress = UNIHUB_FAN_C3_RPM_ACTION_ADDRESS; + channel3.fanSpeed = UNIHUB_FAN_SPEED_QUIET; + channels[2] = channel3; + + Channel channel4; + channel4.index = 3; + channel4.anyFanCountOffset = UNIHUB_ANY_C4_FAN_COUNT_OFFSET; + channel4.anyFanCount = UNIHUB_ANY_FAN_COUNT_001; + channel4.ledActionAddress = UNIHUB_LED_C4_ACTION_ADDRESS; + channel4.ledCommitAddress = UNIHUB_LED_C4_COMMIT_ADDRESS; + channel4.ledModeAddress = UNIHUB_LED_C4_MODE_ADDRESS; + channel4.ledSpeedAddress = UNIHUB_LED_C4_SPEED_ADDRESS; + channel4.ledDirectionAddress = UNIHUB_LED_C4_DIRECTION_ADDRESS; + channel4.ledBrightnessAddress = UNIHUB_LED_C4_BRIGHTNESS_ADDRESS; + channel4.ledMode = UNIHUB_LED_MODE_RAINBOW; + channel4.ledSpeed = UNIHUB_LED_SPEED_100; + channel4.ledDirection = UNIHUB_LED_DIRECTION_LTR; + channel4.ledBrightness = UNIHUB_LED_BRIGHTNESS_100; + channel4.fanHubActionAddress = UNIHUB_FAN_C4_HUB_ACTION_ADDRESS; + channel4.fanHubCommitAddress = UNIHUB_FAN_C4_HUB_COMMIT_ADDRESS; + channel4.fanPwmActionAddress = UNIHUB_FAN_C4_PWM_ACTION_ADDRESS; + channel4.fanPwmCommitAddress = UNIHUB_FAN_C4_PWM_COMMIT_ADDRESS; + channel4.fanRpmActionAddress = UNIHUB_FAN_C4_RPM_ACTION_ADDRESS; + channel4.fanSpeed = UNIHUB_FAN_SPEED_QUIET; + channels[3] = channel4; +} + +LianLiUniHubController::~LianLiUniHubController() +{ + CloseLibusb(); +} + +std::string LianLiUniHubController::GetVersion() +{ + return version; +} + +std::string LianLiUniHubController::GetLocation() +{ + return location; +} + +std::string LianLiUniHubController::GetSerial() +{ + return serial; +} + +void LianLiUniHubController::SetAnyFanCount(size_t channel, uint8_t count) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].anyFanCount = count; +} + +void LianLiUniHubController::SetLedColors(size_t channel, RGBColor* colors, size_t count) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + /*-------------------------------------*\ + | Check for invalid count | + \*-------------------------------------*/ + if(count > UNIHUB_CHANLED_COUNT) + { + count = UNIHUB_CHANLED_COUNT; + } + + size_t i = 0; + for(; i < count; i++) + { + channels[channel].colors[i].r = RGBGetRValue(colors[i]); + channels[channel].colors[i].b = RGBGetBValue(colors[i]); + channels[channel].colors[i].g = RGBGetGValue(colors[i]); + } + + /* Set all remaining leds to black */ + for(; i < UNIHUB_CHANLED_COUNT; i++) + { + channels[channel].colors[i].r = 0x00; + channels[channel].colors[i].b = 0x00; + channels[channel].colors[i].g = 0x00; + } +} + +void LianLiUniHubController::SetLedMode(size_t channel, uint8_t mode) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledMode = mode; +} + +void LianLiUniHubController::SetLedSpeed(size_t channel, uint8_t speed) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledSpeed = speed; +} + +void LianLiUniHubController::SetLedDirection(size_t channel, uint8_t direction) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledDirection = direction; +} + +void LianLiUniHubController::SetLedBrightness(size_t channel, uint8_t brightness) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledBrightness = brightness; +} + +uint16_t LianLiUniHubController::GetFanSpeed(size_t channel) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return 0; + } + + return channels[channel].fanSpeed; +} + +void LianLiUniHubController::SetFanSpeed(size_t channel, uint16_t speed) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_CHANNEL_COUNT) + { + return; + } + + channels[channel].fanSpeed = speed; +} + +void LianLiUniHubController::EnableRgbhMode() +{ + rgbhModeEnabled = true; +} + +void LianLiUniHubController::DisableRgbhMode() +{ + rgbhModeEnabled = false; +} + +void LianLiUniHubController::EnableSyncMode() +{ + syncModeEnabled = true; +} + +void LianLiUniHubController::DisableSyncMode() +{ + syncModeEnabled = false; +} + +/*----------------------------------------------------------------------------*\ +| The Uni Hub is a PWM and LED controller designed specifically for the Lian | +| Li Uni Fans. It can control them by itself using the built-in effect engine | +| can also be connected to the mainboard via 4-pin PWM and 3-pin RGB cables | +| and forward these signals. The protocol implementation below was build as | +| close a possible to the Lian Li L-Connect software. | +| | +| The commands to control the fan speeds and to switch between controller and | +| mainboard control is already included, but currently deactivated as OpenRGB | +| had no fan control module or controller specific configuration at the time | +| of writing. | +\*----------------------------------------------------------------------------*/ +void LianLiUniHubController::Synchronize() +{ + /*---------------------------------------------------------------------*\ + | Configure common settings. | + \*---------------------------------------------------------------------*/ + + /*---------------------------------------------------------------------*\ + | Still unsure about this. Probably some sort of configuration | + | initialization | + \*---------------------------------------------------------------------*/ + uint8_t config_initialization[1] = { 0x34 }; + + SendConfig(UNIHUB_ACTION_ADDRESS, config_initialization, sizeof(config_initialization)); + SendCommit(UNIHUB_COMMIT_ADDRESS); + + for(const Channel& channel : channels) + { + /*-------------------------------------*\ + | The Uni Hub doesn't know zero fans | + \*-------------------------------------*/ + uint8_t anyFanCount = channel.anyFanCount; + + if(anyFanCount == UNIHUB_ANY_FAN_COUNT_000) + { + anyFanCount = UNIHUB_ANY_FAN_COUNT_001; + } + + /*-------------------------------------*\ + | Configure the physical fan count | + \*-------------------------------------*/ + uint8_t config_fan_count[2] = { 0x32, (uint8_t)(channel.anyFanCountOffset | anyFanCount) }; + + SendConfig(UNIHUB_ACTION_ADDRESS, config_fan_count, sizeof(config_fan_count)); + SendCommit(UNIHUB_COMMIT_ADDRESS); + } + + /*--------------------------------------------------------------------*\ + | Configure channels for sync effects | + \*--------------------------------------------------------------------*/ + if(syncModeEnabled) + { + uint8_t config_sync[6]; + uint8_t config_sync_index = 0; + + config_sync[config_sync_index++] = 0x33; + + for(const Channel& channel : channels) + { + if(channel.anyFanCount != UNIHUB_ANY_FAN_COUNT_000) + { + config_sync[config_sync_index++] = channel.index; + } + } + + config_sync[config_sync_index++] = 0x08; + + SendConfig(UNIHUB_ACTION_ADDRESS, config_sync, config_sync_index); + SendCommit(UNIHUB_COMMIT_ADDRESS); + } + + /*--------------------------------------------------------------------*\ + | Configure led settings. | + \*--------------------------------------------------------------------*/ + for(const Channel& channel : channels) + { + if(channel.anyFanCount != UNIHUB_ANY_FAN_COUNT_000) + { + /*-----------------------------*\ + | Configure led colors | + \*-----------------------------*/ + uint8_t config_colors[192]; + memcpy(config_colors, channel.colors, sizeof(config_colors)); + + /*-----------------------------*\ + | No idea what this does ... | + \*-----------------------------*/ + if (syncModeEnabled) + { + config_colors[0x06] = 0x66; + config_colors[0x07] = 0x33; + config_colors[0x08] = 0xCC; + + memset(config_colors + 0x09, 0x00, sizeof(config_colors) - 0x09); + } + + SendConfig(channel.ledActionAddress, config_colors, sizeof(config_colors)); + + /*-----------------------------*\ + | Configure led mode | + \*-----------------------------*/ + uint8_t config_mode[1] = { channel.ledMode }; + + SendConfig(channel.ledModeAddress, config_mode, sizeof(config_mode)); + } + /*-----------------------------------------------------------------*\ + | The Uni Hub doesn't know zero fans so we set them to black | + \*-----------------------------------------------------------------*/ + else + { + /*-----------------------------*\ + | Configure led colors | + \*-----------------------------*/ + uint8_t config_colors[192]; + memset(config_colors, 0x00, sizeof(config_colors)); + + SendConfig(channel.ledActionAddress, config_colors, sizeof(config_colors)); + + /*-----------------------------*\ + | Configure led mode | + \*-----------------------------*/ + uint8_t config_mode[1] = { UNIHUB_LED_MODE_STATIC_COLOR }; + + SendConfig(channel.ledModeAddress, config_mode, sizeof(config_mode)); + } + + /*---------------------------------*\ + | Configure led speed | + \*---------------------------------*/ + uint8_t config_speed[1] = { channel.ledSpeed }; + + SendConfig(channel.ledSpeedAddress, config_speed, sizeof(config_speed)); + + /*---------------------------------*\ + | Configure led direction | + \*---------------------------------*/ + uint8_t config_direction[1] = { channel.ledDirection }; + + SendConfig(channel.ledDirectionAddress, config_direction, sizeof(config_direction)); + + /*---------------------------------*\ + | Configure led brightness | + \*---------------------------------*/ + uint8_t config_brightness[1] = { channel.ledBrightness }; + + SendConfig(channel.ledBrightnessAddress, config_brightness, sizeof(config_brightness)); + + /*---------------------------------*\ + | Commit only once for all led | + | settings | + \*---------------------------------*/ + SendCommit(channel.ledCommitAddress); + } + + /*--------------------------------------------------------------------*\ + | Configure fan settings. Comment out until enabling fan control | + \*--------------------------------------------------------------------*/ +// uint8_t control = 0; + + /*-------------------------------------*\ + | Configure fan settings | + \*-------------------------------------*/ +// for(const Channel& channel : channels) +// { +// if(channel.fanSpeed == UNIHUB_FAN_SPEED_PWM) +// { + /*-----------------------------*\ + | Configure the fan to pwm | + | control | + \*-----------------------------*/ +// uint8_t config_pwm[1] = { 0x00 }; + +// control |= (0x01 << channel.index); + +// SendConfig(channel.fanPwmActionAddress, config_pwm, sizeof(config_pwm)); +// SendCommit(channel.fanPwmCommitAddress); +// } +// else +// { + /*-----------------------------*\ + | Configure the fan to hub | + | control and set speed | + \*-----------------------------*/ +// uint8_t config_hub[2] = { (uint8_t)(channel.fanSpeed >> 0x08), (uint8_t)(channel.fanSpeed & 0xFF) }; + +// SendConfig(channel.fanHubActionAddress, config_hub, sizeof(config_hub)); +// SendCommit(channel.fanHubCommitAddress); +// } +// } + + /*-------------------------------------*\ + | Configure fan control modes | + \*-------------------------------------*/ +// uint8_t config_fan_mode[2] = { 0x31, (uint8_t)(0xF0 | control) }; + +// SendConfig(UNIHUB_ACTION_ADDRESS, config_fan_mode, sizeof(config_fan_mode)); +// SendCommit(UNIHUB_COMMIT_ADDRESS); + + /*--------------------------------------------------------------------*\ + | Configure led settings. | + \*--------------------------------------------------------------------*/ + if(rgbhModeEnabled) + { + /*-------------------------------------*\ + | Configure the leds to hdr control. | + \*-------------------------------------*/ + uint8_t config_hdr[2] = { 0x30, 0x01 }; + + SendConfig(UNIHUB_ACTION_ADDRESS, config_hdr, sizeof(config_hdr)); + SendCommit(UNIHUB_COMMIT_ADDRESS); + } + else + { + /*-------------------------------------*\ + | Configure the leds to hub control | + \*-------------------------------------*/ + uint8_t config_hub[2] = { 0x30, 0x00 }; + + SendConfig(UNIHUB_ACTION_ADDRESS, config_hub, sizeof(config_hub)); + SendCommit(UNIHUB_COMMIT_ADDRESS); + } +} + +uint16_t LianLiUniHubController::ReadFanSpeed(size_t channel) +{ + /*-------------------------------------*\ + | Check for invalid handle | + \*-------------------------------------*/ + if(handle == nullptr) + { + return(0); + } + + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel > UNIHUB_CHANNEL_COUNT) + { + return(0); + } + + uint8_t buffer[2]; + uint8_t length = sizeof(buffer); + + uint16_t wIndex = channels[channel].fanRpmActionAddress; + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + size_t ret = libusb_control_transfer(handle, /* dev_handle */ + 0xC0, /* bmRequestType */ + 0x81, /* bRequest */ + 0x00, /* wValue */ + wIndex, /* wIndex */ + buffer, /* data */ + length, /* wLength */ + 1000); /* timeout */ + + /*-------------------------------------*\ + | Check for communication error | + \*-------------------------------------*/ + if(ret != length) + { + return(0); + } + + return(*(uint16_t*)buffer); +} + +void LianLiUniHubController::CloseLibusb() +{ + if (handle != nullptr) + { + libusb_close(handle); + handle = nullptr; + } +} + +std::string LianLiUniHubController::ReadVersion() +{ + /*-------------------------------------*\ + | Check for invalid handle | + \*-------------------------------------*/ + if(handle == nullptr) + { + return(""); + } + + uint8_t buffer[5]; + uint8_t length = sizeof(buffer); + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + size_t ret = libusb_control_transfer(handle, /* dev_handle */ + 0xC0, /* bmRequestType */ + 0x81, /* bRequest */ + 0x00, /* wValue */ + 0xB500, /* wIndex */ + buffer, /* data */ + length, /* wLength */ + 1000); /* timeout */ + + /*-------------------------------------*\ + | Check for communication error | + \*-------------------------------------*/ + if(ret != length) + { + return(""); + } + + /*-------------------------------------*\ + | Format version string | + \*-------------------------------------*/ + char version[15]; + int vlength = std::snprintf(version, sizeof(version), "%x.%x.%x.%x.%x", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]); + + return(std::string(version, vlength)); +} + +void LianLiUniHubController::SendConfig(uint16_t wIndex, uint8_t *config, size_t length) +{ + /*-------------------------------------*\ + | Check for invalid handle | + \*-------------------------------------*/ + if(handle == nullptr) + { + return; + } + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + size_t ret = libusb_control_transfer(handle, /* dev_handle */ + 0x40, /* bmRequestType */ + 0x80, /* bRequest */ + 0x00, /* wValue */ + wIndex, /* wIndex */ + config, /* data */ + (uint16_t)length, /* wLength */ + 1000); /* timeout */ + + /*-------------------------------------*\ + | Check for communication error | + \*-------------------------------------*/ + if(ret != length) + { + return; + } +} + +void LianLiUniHubController::SendCommit(uint16_t wIndex) +{ + /*-------------------------------------*\ + | Set up config packet | + \*-------------------------------------*/ + uint8_t config[1] = { 0x01 }; + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + SendConfig(wIndex, config, sizeof(config)); + + std::this_thread::sleep_for(5ms); +} diff --git a/Controllers/LianLiController/LianLiUniHubController/LianLiUniHubController.h b/Controllers/LianLiController/LianLiUniHubController/LianLiUniHubController.h new file mode 100644 index 0000000..9063fa3 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubController/LianLiUniHubController.h @@ -0,0 +1,263 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubController.h | +| | +| Driver for Lian Li Uni Hub | +| | +| Luca Lovisa 20 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBController.h" + +/*----------------------------------------------------------------------------*\ +| Global definitions. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_CHANNEL_COUNT = 0x04, /* Channel count */ + UNIHUB_CHANLED_COUNT = 0x40, /* Max-LED per channel count */ +}; + +enum +{ + UNIHUB_ACTION_ADDRESS = 0xe021, /* Global action address */ + UNIHUB_COMMIT_ADDRESS = 0xe02f, /* Global commit address */ +}; + +enum +{ + UNIHUB_ANY_C1_FAN_COUNT_OFFSET = 0x00, /* Channel 1 fan count offset */ + UNIHUB_ANY_C2_FAN_COUNT_OFFSET = 0x10, /* Channel 2 fan count offset */ + UNIHUB_ANY_C3_FAN_COUNT_OFFSET = 0x20, /* Channel 3 fan count offset */ + UNIHUB_ANY_C4_FAN_COUNT_OFFSET = 0x30, /* Channel 4 fan count offset */ +}; + +enum +{ + UNIHUB_ANY_FAN_COUNT_000 = 0xFF, /* Fan count for 0 fans (dummy value) */ + UNIHUB_ANY_FAN_COUNT_001 = 0x00, /* Fan count for 1 fan */ + UNIHUB_ANY_FAN_COUNT_002 = 0x01, /* Fan count for 2 fans */ + UNIHUB_ANY_FAN_COUNT_003 = 0x02, /* Fan count for 3 fans */ + UNIHUB_ANY_FAN_COUNT_004 = 0x03, /* Fan count for 4 fans */ +}; + +/*----------------------------------------------------------------------------*\ +| Definitions related to led configuration. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_LED_C1_ACTION_ADDRESS = 0xe300, /* Channel 1 led action address */ + UNIHUB_LED_C1_COMMIT_ADDRESS = 0xe02f, /* Channel 1 led commit address */ + UNIHUB_LED_C1_MODE_ADDRESS = 0xe021, /* Channel 1 led mode address */ + UNIHUB_LED_C1_SPEED_ADDRESS = 0xe022, /* Channel 1 led speed address */ + UNIHUB_LED_C1_DIRECTION_ADDRESS = 0xe023, /* Channel 1 led direction address */ + UNIHUB_LED_C1_BRIGHTNESS_ADDRESS = 0xe029, /* Channel 1 led brightness address */ + + UNIHUB_LED_C2_ACTION_ADDRESS = 0xe3c0, /* Channel 2 led action address */ + UNIHUB_LED_C2_COMMIT_ADDRESS = 0xe03f, /* Channel 2 led commit address */ + UNIHUB_LED_C2_MODE_ADDRESS = 0xe031, /* Channel 2 led mode address */ + UNIHUB_LED_C2_SPEED_ADDRESS = 0xe032, /* Channel 2 led speed address */ + UNIHUB_LED_C2_DIRECTION_ADDRESS = 0xe033, /* Channel 2 led direction address */ + UNIHUB_LED_C2_BRIGHTNESS_ADDRESS = 0xe039, /* Channel 2 led brightness address */ + + UNIHUB_LED_C3_ACTION_ADDRESS = 0xe480, /* Channel 3 led action address */ + UNIHUB_LED_C3_COMMIT_ADDRESS = 0xe04f, /* Channel 3 led commit address */ + UNIHUB_LED_C3_MODE_ADDRESS = 0xe041, /* Channel 3 led mode address */ + UNIHUB_LED_C3_SPEED_ADDRESS = 0xe042, /* Channel 3 led speed address */ + UNIHUB_LED_C3_DIRECTION_ADDRESS = 0xe043, /* Channel 3 led direction address */ + UNIHUB_LED_C3_BRIGHTNESS_ADDRESS = 0xe049, /* Channel 3 led brightness address */ + + UNIHUB_LED_C4_ACTION_ADDRESS = 0xe540, /* Channel 4 led action address */ + UNIHUB_LED_C4_COMMIT_ADDRESS = 0xe05f, /* Channel 4 led commit address */ + UNIHUB_LED_C4_MODE_ADDRESS = 0xe051, /* Channel 4 led mode address */ + UNIHUB_LED_C4_SPEED_ADDRESS = 0xe052, /* Channel 4 led speed address */ + UNIHUB_LED_C4_DIRECTION_ADDRESS = 0xe053, /* Channel 4 led direction address */ + UNIHUB_LED_C4_BRIGHTNESS_ADDRESS = 0xe059, /* Channel 4 led brightness address */ +}; + +enum +{ + UNIHUB_LED_MODE_RAINBOW = 0x05, /* Rainbow mode */ + UNIHUB_LED_MODE_STATIC_COLOR = 0x01, /* Static Color mode */ + UNIHUB_LED_MODE_BREATHING = 0x02, /* Breathing mode */ + UNIHUB_LED_MODE_COLOR_CYCLE = 0x04, /* Color Cycle mode */ + UNIHUB_LED_MODE_RUNWAY = 0x1c, /* Runway mode */ + UNIHUB_LED_MODE_RUNWAY_SYNC = 0x1c, /* Runway Sync mode */ + UNIHUB_LED_MODE_STAGGGERED = 0x18, /* Stagggered mode */ + UNIHUB_LED_MODE_MIXING = 0x1a, /* Mixing mode */ + UNIHUB_LED_MODE_METEOR = 0x07, /* Meteor mode */ + UNIHUB_LED_MODE_METEOR_SYNC = 0x07, /* Meteor Sync mode */ + UNIHUB_LED_MODE_FIREWORK = 0x1f, /* Firework mode */ + UNIHUB_LED_MODE_STACK = 0x21, /* Stack mode */ + UNIHUB_LED_MODE_STACK_MULTI_COLOR = 0x22, /* Stack Multi Color mode */ + UNIHUB_LED_MODE_NEON = 0x23, /* Neon mode */ +}; + +enum +{ + UNIHUB_LED_SPEED_000 = 0x04, /* Very slow speed */ + UNIHUB_LED_SPEED_025 = 0x03, /* Rather slow speed */ + UNIHUB_LED_SPEED_050 = 0x02, /* Medium speed */ + UNIHUB_LED_SPEED_075 = 0x01, /* Rather fast speed */ + UNIHUB_LED_SPEED_100 = 0x00, /* Very fast speed */ +}; + +enum +{ + UNIHUB_LED_DIRECTION_LTR = 0x00, /* Left-to-Right direction */ + UNIHUB_LED_DIRECTION_RTL = 0x01, /* Right-to-Left direction */ +}; + +enum +{ + UNIHUB_LED_BRIGHTNESS_000 = 0x08, /* Very dark (off) */ + UNIHUB_LED_BRIGHTNESS_025 = 0x03, /* Rather dark */ + UNIHUB_LED_BRIGHTNESS_050 = 0x02, /* Medium bright */ + UNIHUB_LED_BRIGHTNESS_075 = 0x01, /* Rather bright */ + UNIHUB_LED_BRIGHTNESS_100 = 0x00, /* Very bright */ +}; + +/*----------------------------------------------------------------------------*\ +| Definitions related to fan configuration. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_FAN_C1_HUB_ACTION_ADDRESS = 0xe8a0, /* Channel 1 fan action address for hub control */ + UNIHUB_FAN_C1_HUB_COMMIT_ADDRESS = 0xe890, /* Channel 1 fan commit address for hub control */ + UNIHUB_FAN_C1_PWM_ACTION_ADDRESS = 0xe890, /* Channel 1 fan action address for pwm control */ + UNIHUB_FAN_C1_PWM_COMMIT_ADDRESS = 0xe818, /* Channel 1 fan commit address for pwm control */ + UNIHUB_FAN_C1_RPM_ACTION_ADDRESS = 0xe800, /* Channel 1 fan pwm read address */ + + UNIHUB_FAN_C2_HUB_ACTION_ADDRESS = 0xe8a2, /* Channel 2 fan action address for hub control */ + UNIHUB_FAN_C2_HUB_COMMIT_ADDRESS = 0xe891, /* Channel 2 fan commit address for hub control */ + UNIHUB_FAN_C2_PWM_ACTION_ADDRESS = 0xe891, /* Channel 2 fan action address for pwm control */ + UNIHUB_FAN_C2_PWM_COMMIT_ADDRESS = 0xe81a, /* Channel 2 fan commit address for pwm control */ + UNIHUB_FAN_C2_RPM_ACTION_ADDRESS = 0xe802, /* Channel 1 fan pwm read address */ + + UNIHUB_FAN_C3_HUB_ACTION_ADDRESS = 0xe8a4, /* Channel 3 fan action address for hub control */ + UNIHUB_FAN_C3_HUB_COMMIT_ADDRESS = 0xe892, /* Channel 3 fan commit address for hub control */ + UNIHUB_FAN_C3_PWM_ACTION_ADDRESS = 0xe892, /* Channel 3 fan action address for pwm control */ + UNIHUB_FAN_C3_PWM_COMMIT_ADDRESS = 0xe81c, /* Channel 3 fan commit address for pwm control */ + UNIHUB_FAN_C3_RPM_ACTION_ADDRESS = 0xe804, /* Channel 1 fan pwm read address */ + + UNIHUB_FAN_C4_HUB_ACTION_ADDRESS = 0xe8a6, /* Channel 4 fan action address for hub control */ + UNIHUB_FAN_C4_HUB_COMMIT_ADDRESS = 0xe893, /* Channel 4 fan commit address for hub control */ + UNIHUB_FAN_C4_PWM_ACTION_ADDRESS = 0xe893, /* Channel 4 fan action address for pwm control */ + UNIHUB_FAN_C4_PWM_COMMIT_ADDRESS = 0xe81e, /* Channel 4 fan commit address for pwm control */ + UNIHUB_FAN_C4_RPM_ACTION_ADDRESS = 0xe806, /* Channel 1 fan pwm read address */ +}; + +enum +{ + UNIHUB_FAN_SPEED_QUIET = 0x2003, /* Rather slow */ + UNIHUB_FAN_SPEED_HIGH_SPEED = 0x2206, /* Rather fast */ + UNIHUB_FAN_SPEED_FULL_SPEED = 0x6c07, /* BRRRRRRRRRR */ + UNIHUB_FAN_SPEED_PWM = 0xffff, /* PWM Control */ +}; + +/*----------------------------------------------------------------------------*\ +| Uni Hub controller. | +\*----------------------------------------------------------------------------*/ + +class LianLiUniHubController +{ +private: + /* The Uni Hub requires colors in RBG order */ + struct Color + { + uint8_t r; + uint8_t b; + uint8_t g; + }; + + /* The values correspond to the definitions above */ + struct Channel + { + uint8_t index; + + uint8_t anyFanCountOffset; + uint8_t anyFanCount; + + uint16_t ledActionAddress; + uint16_t ledCommitAddress; + uint16_t ledModeAddress; + uint16_t ledSpeedAddress; + uint16_t ledDirectionAddress; + uint16_t ledBrightnessAddress; + + Color colors[UNIHUB_CHANLED_COUNT]; + + uint8_t ledMode; + uint8_t ledSpeed; + uint8_t ledDirection; + uint8_t ledBrightness; + + uint16_t fanHubActionAddress; + uint16_t fanHubCommitAddress; + + uint16_t fanPwmActionAddress; + uint16_t fanPwmCommitAddress; + uint16_t fanRpmActionAddress; + + uint16_t fanSpeed; + }; + +public: + LianLiUniHubController + ( + libusb_device* device, + libusb_device_descriptor* descriptor + ); + ~LianLiUniHubController(); + + std::string GetVersion(); + std::string GetLocation(); + std::string GetSerial(); + + void SetAnyFanCount(size_t channel, uint8_t count); + void SetLedColors(size_t channel, RGBColor* colors, size_t count); + void SetLedMode(size_t channel, uint8_t mode); + void SetLedSpeed(size_t channel, uint8_t speed); + void SetLedDirection(size_t channel, uint8_t direction); + void SetLedBrightness(size_t channel, uint8_t brightness); + uint16_t GetFanSpeed(size_t channel); + void SetFanSpeed(size_t channel, uint16_t speed); + void EnableRgbhMode(); + void DisableRgbhMode(); + void EnableSyncMode(); + void DisableSyncMode(); + uint16_t ReadFanSpeed(size_t channel); + + /*-----------------------------------------------------*\ + | Synchronize the current configuration to the Uni Hub. | + \*-----------------------------------------------------*/ + void Synchronize(); + +private: + libusb_device_handle* handle = nullptr; + + std::string version; + std::string location; + std::string serial; + + bool rgbhModeEnabled = false; + bool syncModeEnabled = false; + + Channel channels[UNIHUB_CHANNEL_COUNT]; + + void CloseLibusb(); + std::string ReadVersion(); + void SendConfig(uint16_t wIndex, uint8_t *config, size_t length); + void SendCommit(uint16_t wIndex); +}; diff --git a/Controllers/LianLiController/LianLiUniHubController/RGBController_LianLiUniHub.cpp b/Controllers/LianLiController/LianLiUniHubController/RGBController_LianLiUniHub.cpp new file mode 100644 index 0000000..f7a7779 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubController/RGBController_LianLiUniHub.cpp @@ -0,0 +1,479 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHub.cpp | +| | +| RGBController for Lian Li Uni Hub | +| | +| Luca Lovisa 20 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_LianLiUniHub.h" + +mode makeMode() +{ + mode Mode; + + Mode.value = 0; + Mode.flags = 0; + Mode.speed_min = 0; + Mode.speed_max = 0; + Mode.colors_min = 0; + Mode.colors_max = 0; + Mode.speed = 0; + Mode.direction = 0; + Mode.color_mode = 0; + + return Mode; +} + +/**------------------------------------------------------------------*\ + @name Lian Li Uni Hub + @category Cooler + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectLianLiUniHub + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LianLiUniHub::RGBController_LianLiUniHub(LianLiUniHubController* controller_ptr) +{ + controller = controller_ptr; + + name = "Lian Li Uni Hub"; + vendor = "Lian Li"; + version = controller->GetVersion(); + type = DEVICE_TYPE_COOLER; + description = "Lian Li Uni Hub"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + + initializedMode = false; + + mode StaticColor = makeMode(); + StaticColor.name = "Custom"; + StaticColor.value = UNIHUB_LED_MODE_STATIC_COLOR; + StaticColor.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + StaticColor.color_mode = MODE_COLORS_PER_LED; + modes.push_back(StaticColor); + + mode Rainbow = makeMode(); + Rainbow.name = "Rainbow Wave"; + Rainbow.value = UNIHUB_LED_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.speed_min = 1; + Rainbow.speed_max = 5; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Breathing = makeMode(); + Breathing.name = "Breathing"; + Breathing.value = UNIHUB_LED_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = 1; + Breathing.speed_max = 5; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode ColorCycle = makeMode(); + ColorCycle.name = "Color Cycle"; + ColorCycle.value = UNIHUB_LED_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + ColorCycle.speed_min = 1; + ColorCycle.speed_max = 5; + ColorCycle.colors_min = 3; + ColorCycle.colors_max = 3; + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorCycle.colors.resize(3); + modes.push_back(ColorCycle); + + mode Runway = makeMode(); + Runway.name = "Runway"; + Runway.value = UNIHUB_LED_MODE_RUNWAY; + Runway.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Runway.speed_min = 1; + Runway.speed_max = 5; + Runway.colors_min = 2; + Runway.colors_max = 2; + Runway.color_mode = MODE_COLORS_MODE_SPECIFIC; + Runway.colors.resize(2); + modes.push_back(Runway); + + mode RunwaySync = makeMode(); + RunwaySync.name = "Runway Sync"; + RunwaySync.value = UNIHUB_LED_MODE_RUNWAY_SYNC | 0x0100; + RunwaySync.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + RunwaySync.speed_min = 1; + RunwaySync.speed_max = 5; + RunwaySync.colors_min = 2; + RunwaySync.colors_max = 2; + RunwaySync.color_mode = MODE_COLORS_MODE_SPECIFIC; + RunwaySync.colors.resize(2); + modes.push_back(RunwaySync); + + mode Staggered = makeMode(); + Staggered.name = "Staggered"; + Staggered.value = UNIHUB_LED_MODE_STAGGGERED; + Staggered.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Staggered.speed_min = 1; + Staggered.speed_max = 5; + Staggered.colors_min = 2; + Staggered.colors_max = 2; + Staggered.color_mode = MODE_COLORS_MODE_SPECIFIC; + Staggered.colors.resize(2); + modes.push_back(Staggered); + + mode Mixing = makeMode(); + Mixing.name = "Mixing"; + Mixing.value = UNIHUB_LED_MODE_MIXING; + Mixing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Mixing.speed_min = 1; + Mixing.speed_max = 5; + Mixing.colors_min = 2; + Mixing.colors_max = 2; + Mixing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Mixing.colors.resize(2); + modes.push_back(Mixing); + + mode Meteor = makeMode(); + Meteor.name = "Meteor"; + Meteor.value = UNIHUB_LED_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Meteor.speed_min = 1; + Meteor.speed_max = 5; + Meteor.colors_min = 2; + Meteor.colors_max = 2; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors.resize(2); + modes.push_back(Meteor); + + mode MeteorSync = makeMode(); + MeteorSync.name = "Meteor Sync"; + MeteorSync.value = UNIHUB_LED_MODE_METEOR_SYNC | 0x0100; + MeteorSync.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + MeteorSync.speed_min = 1; + MeteorSync.speed_max = 5; + MeteorSync.colors_min = 2; + MeteorSync.colors_max = 2; + MeteorSync.color_mode = MODE_COLORS_MODE_SPECIFIC; + MeteorSync.colors.resize(2); + modes.push_back(MeteorSync); + + mode Firework = makeMode(); + Firework.name = "Firework"; + Firework.value = UNIHUB_LED_MODE_FIREWORK; + Firework.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Firework.speed_min = 1; + Firework.speed_max = 5; + Firework.colors_min = 2; + Firework.colors_max = 2; + Firework.color_mode = MODE_COLORS_MODE_SPECIFIC; + Firework.colors.resize(2); + modes.push_back(Firework); + + mode Stack = makeMode(); + Stack.name = "Stack"; + Stack.value = UNIHUB_LED_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Stack.speed_min = 1; + Stack.speed_max = 5; + Stack.colors_min = 1; + Stack.colors_max = 1; + Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stack.colors.resize(1); + modes.push_back(Stack); + + mode StackMultiColor = makeMode(); + StackMultiColor.name = "Stack Multi Color"; + StackMultiColor.value = UNIHUB_LED_MODE_STACK_MULTI_COLOR; + StackMultiColor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + StackMultiColor.speed_min = 1; + StackMultiColor.speed_max = 5; + StackMultiColor.color_mode = MODE_COLORS_NONE; + modes.push_back(StackMultiColor); + + mode Neon = makeMode(); + Neon.name = "Neon"; + Neon.value = UNIHUB_LED_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Neon.speed_min = 1; + Neon.speed_max = 5; + Neon.color_mode = MODE_COLORS_NONE; + modes.push_back(Neon); + + mode Rgbh = makeMode(); + Rgbh.name = "RGB Header"; + Rgbh.value = UNIHUB_LED_MODE_STATIC_COLOR | 0x0200; + Rgbh.flags = 0; + Rgbh.color_mode = MODE_COLORS_NONE; + modes.push_back(Rgbh); + + RGBController_LianLiUniHub::SetupZones(); +} + +void RGBController_LianLiUniHub::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(UNIHUB_CHANNEL_COUNT); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + int addressableCounter = 1; + for(unsigned int channel_idx = 0; channel_idx < zones.size(); channel_idx++) + { + zones[channel_idx].name = "Channel "; + zones[channel_idx].name.append(std::to_string(addressableCounter)); + + addressableCounter++; + + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = UNIHUB_CHANLED_COUNT; + + if(first_run) + { + zones[channel_idx].leds_count = zones[channel_idx].leds_min; + } + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + led new_led; + new_led.name = zones[channel_idx].name; + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_ch_idx + 1)); + new_led.value = channel_idx; + + leds.push_back(new_led); + } + + zones[channel_idx].matrix_map = NULL; + } + + SetupColors(); +} + +void RGBController_LianLiUniHub::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_LianLiUniHub::DeviceUpdateLEDs() +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + for(size_t channel = 0; channel < zones.size(); channel++) + { + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + controller->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + controller->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + } + + controller->Synchronize(); +} + +void RGBController_LianLiUniHub::UpdateZoneLEDs(int zone) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + unsigned int channel = zone; + + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + controller->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + controller->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + + controller->Synchronize(); +} + +void RGBController_LianLiUniHub::UpdateSingleLED(int led) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + unsigned int channel = leds[led].value; + + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + controller->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + controller->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + + controller->Synchronize(); +} + +void RGBController_LianLiUniHub::DeviceUpdateMode() +{ + initializedMode = true; + + for (size_t channel = 0; channel < zones.size(); channel++) + { + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + controller->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + + switch (modes[active_mode].color_mode) + { + case MODE_COLORS_PER_LED: + controller->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + break; + + case MODE_COLORS_MODE_SPECIFIC: + controller->SetLedColors(channel, modes[active_mode].colors.data(), modes[active_mode].colors.size()); + break; + + default: + controller->SetLedColors(channel, nullptr, 0); + break; + } + + controller->SetLedMode(channel, modes[active_mode].value); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->SetLedSpeed(channel, convertLedSpeed(modes[active_mode].speed)); + } + else + { + controller->SetLedSpeed(channel, UNIHUB_LED_SPEED_000); + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + controller->SetLedDirection(channel, convertLedDirection(modes[active_mode].direction)); + } + else + { + controller->SetLedDirection(channel, UNIHUB_LED_DIRECTION_LTR); + } + } + + if(modes[active_mode].value & 0x0200) + { + controller->EnableRgbhMode(); + controller->DisableSyncMode(); + } + else if (modes[active_mode].value & 0x0100) + { + controller->DisableRgbhMode(); + controller->EnableSyncMode(); + } + else + { + controller->DisableRgbhMode(); + controller->DisableSyncMode(); + } + + controller->Synchronize(); +} + +uint8_t RGBController_LianLiUniHub::convertAnyFanCount(uint8_t count) +{ + switch (count) + { + case 0: + return UNIHUB_ANY_FAN_COUNT_000; + + case 1: + return UNIHUB_ANY_FAN_COUNT_001; + + case 2: + return UNIHUB_ANY_FAN_COUNT_002; + + case 3: + return UNIHUB_ANY_FAN_COUNT_003; + + case 4: + return UNIHUB_ANY_FAN_COUNT_004; + + default: + return UNIHUB_ANY_FAN_COUNT_001; + } +} + +uint8_t RGBController_LianLiUniHub::convertLedSpeed(uint8_t speed) +{ + switch (speed) + { + case 1: + return UNIHUB_LED_SPEED_000; + + case 2: + return UNIHUB_LED_SPEED_025; + + case 3: + return UNIHUB_LED_SPEED_050; + + case 4: + return UNIHUB_LED_SPEED_075; + + case 5: + return UNIHUB_LED_SPEED_100; + + default: + return UNIHUB_LED_SPEED_050; + } +} + +uint8_t RGBController_LianLiUniHub::convertLedDirection(uint8_t direction) +{ + switch (direction) + { + case 0: + return UNIHUB_LED_DIRECTION_LTR; + + case 1: + return UNIHUB_LED_DIRECTION_RTL; + + default: + return UNIHUB_LED_DIRECTION_LTR; + } +} + +uint8_t RGBController_LianLiUniHub::convertLedCountToFanCount(uint8_t count) +{ + /*-------------------------------------------------*\ + | Converts 0 to 0, 1-16 to 1, 17-32 to 2, 33-48 to | + | 3 and 49-64+ to 4 | + \*-------------------------------------------------*/ + if (count == 0x00) + { + return 0x00; + } + if (count >= 0x40) + { + count = 0x40; + } + + return((count -1) / 16 + 1); +} diff --git a/Controllers/LianLiController/LianLiUniHubController/RGBController_LianLiUniHub.h b/Controllers/LianLiController/LianLiUniHubController/RGBController_LianLiUniHub.h new file mode 100644 index 0000000..fae6d96 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubController/RGBController_LianLiUniHub.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHub.h | +| | +| RGBController for Lian Li Uni Hub | +| | +| Luca Lovisa 20 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "LianLiUniHubController.h" +#include "RGBController.h" + +class RGBController_LianLiUniHub : public RGBController +{ +public: + RGBController_LianLiUniHub(LianLiUniHubController* controller_ptr); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + uint8_t convertAnyFanCount(uint8_t count); + uint8_t convertLedSpeed(uint8_t speed); + uint8_t convertLedDirection(uint8_t direction); + + uint8_t convertLedCountToFanCount(uint8_t count); + +private: + LianLiUniHubController* controller; + bool initializedMode; +}; diff --git a/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.cpp b/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.cpp new file mode 100644 index 0000000..e53684c --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.cpp @@ -0,0 +1,375 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubSLController.cpp | +| | +| Driver for Lian Li Uni Hub - SL | +| | +| Muhamad Visat 26 Jul 2025 | +| Original work by Luca Lovisa & Oliver P | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "StringUtils.h" +#include "LianLiUniHubSLController.h" + +using namespace std::chrono_literals; + +LianLiUniHubSLController::LianLiUniHubSLController(hid_device *dev, const char *path) +{ + if(dev == nullptr) + { + return; + } + + device = dev; + location = "HID: " + std::string(path); + is_merged_mode = false; +} + +LianLiUniHubSLController::~LianLiUniHubSLController() +{ + if(device != nullptr) + { + hid_close(device); + device = nullptr; + } +} + +std::string LianLiUniHubSLController::ReadVersion() +{ + wchar_t buf[40]; + int ret = hid_get_product_string(this->device, buf, 40); + if(ret != 0) + { + return ""; + } + + /*------------------------------ -*\ + | Example: LianLi-UNI FAN-SL-v1.8 | + | We just want the v1.8 part | + | without trailing spaces | + \*--------------------------------*/ + std::string version = StringUtils::wstring_to_string(buf); + version = version.substr(version.find_last_of('-') + 1); + return version.substr(0, version.find_last_not_of(' ') + 1); +} + +std::string LianLiUniHubSLController::ReadSerial() +{ + wchar_t buf[20]; + int ret = hid_get_serial_number_string(this->device, buf, 20); + if(ret != 0) + { + return ""; + } + + std::string serial = StringUtils::wstring_to_string(buf); + return serial; +} + +void LianLiUniHubSLController::UpdateMode(const std::vector &zones, const mode &active) +{ + /*---------------------------------*\ + | Activate all channels | + \*---------------------------------*/ + for(size_t channel = 0; channel < zones.size(); channel++) + { + this->SendActivate(channel, zones[channel].leds_count); + } + + /*---------------------------------*\ + | Set merge mode if requested | + \*---------------------------------*/ + is_merged_mode = active.name.find("Merged") != std::string::npos; + this->SendMerge(); + + for (size_t channel = 0; channel < zones.size(); channel++) + { + UpdateZoneLEDs(channel, zones[channel], active); + } +} + +void LianLiUniHubSLController::UpdateZoneLEDs(size_t channel, const zone &z, const mode &active) +{ + /*---------------------------------*\ + | Handle per-LED color mode | + \*---------------------------------*/ + if(active.color_mode == MODE_COLORS_PER_LED) + { + this->SetPerLEDColor(channel, z, active); + return; + } + + /*-------------------------------------------------*\ + | In merged mode, only first channel takes control | + \*-------------------------------------------------*/ + if(is_merged_mode && channel > 0) + { + return; + } + + /*---------------------------------*\ + | Handle mode-specific color | + \*---------------------------------*/ + this->SetModeSpecificColor(channel, active); +} + + +void LianLiUniHubSLController::SetPerLEDColor(size_t channel, const zone &z, const mode &active) +{ + unsigned char color_buf[UNIHUB_SL_MAX_LED_PER_CHANNEL * 3]; + float brightness_scale = (float)(active.brightness) / active.brightness_max; + + memset(color_buf, 0, sizeof(color_buf)); + this->FillStaticColorBuffer(color_buf, z.colors, z.leds_count, brightness_scale); + this->SendColor(channel, color_buf, sizeof(color_buf)); + this->SendMode(channel, active); +} + +void LianLiUniHubSLController::SetModeSpecificColor(size_t channel, const mode &active) +{ + unsigned char color_buf[UNIHUB_SL_MAX_LED_PER_CHANNEL * 3]; + memset(color_buf, 0, sizeof(color_buf)); + + float brightness_scale = (float)(active.brightness) / active.brightness_max; + + switch(active.value) + { + case UNIHUB_SL_LED_MODE_RAINBOW: + case UNIHUB_SL_LED_MODE_RAINBOW_MORPH: + case UNIHUB_SL_LED_MODE_STACK_MULTI_COLOR: + case UNIHUB_SL_LED_MODE_NEON: + /*-------------------------*\ + | No need to set any value | + \*-------------------------*/ + break; + + case UNIHUB_SL_LED_MODE_STATIC: + case UNIHUB_SL_LED_MODE_BREATHING: + this->FillStaticColorBuffer(color_buf, active.colors.data(), active.colors.size(), brightness_scale); + break; + + case UNIHUB_SL_LED_MODE_COLOR_CYCLE: + case UNIHUB_SL_LED_MODE_RUNWAY: + case UNIHUB_SL_LED_MODE_STAGGERED: + case UNIHUB_SL_LED_MODE_TIDE: + case UNIHUB_SL_LED_MODE_METEOR: + case UNIHUB_SL_LED_MODE_MIXING: + case UNIHUB_SL_LED_MODE_STACK: + this->FillDynamicColorBuffer(color_buf, active.colors.data(), active.colors.size(), brightness_scale); + break; + + default: + LOG_WARNING("[Lian Li Uni Hub - SL] Unknown mode value: %d", active.value); + break; + } + + this->SendColor(channel, color_buf, sizeof(color_buf)); + this->SendMode(channel, active); +} + +void LianLiUniHubSLController::FillStaticColorBuffer(unsigned char *color_buf, const RGBColor *colors, size_t num_colors, float brightness_scale) +{ + size_t max_fans = (size_t)UNIHUB_SL_MAX_FAN_PER_CHANNEL; + size_t max_idx = num_colors < max_fans ? num_colors : max_fans; + + for(size_t fan_idx = 0; fan_idx < max_idx; fan_idx++) + { + RGBColor color = colors[fan_idx]; + unsigned char r = (unsigned char)(RGBGetRValue(color) * brightness_scale); + unsigned char g = (unsigned char)(RGBGetGValue(color) * brightness_scale); + unsigned char b = (unsigned char)(RGBGetBValue(color) * brightness_scale); + + for(size_t led_idx = 0; led_idx < UNIHUB_SL_LED_PER_FAN; led_idx++) + { + size_t absolute_led_idx = fan_idx * UNIHUB_SL_LED_PER_FAN + led_idx; + size_t idx = absolute_led_idx * 3; + + /*------------------------*\ + | The protocol uses R B G | + \*------------------------*/ + color_buf[idx + 0] = r; + color_buf[idx + 1] = b; + color_buf[idx + 2] = g; + } + } +} + +void LianLiUniHubSLController::FillDynamicColorBuffer(unsigned char *color_buf, const RGBColor *colors, size_t num_colors, float brightness_scale) +{ + size_t max_fans = (size_t)UNIHUB_SL_MAX_FAN_PER_CHANNEL; + size_t max_idx = num_colors < max_fans ? num_colors : max_fans; + + for(size_t color_idx = 0; color_idx < max_idx; color_idx++) + { + RGBColor color = colors[color_idx]; + unsigned char r = (unsigned char)(RGBGetRValue(color) * brightness_scale); + unsigned char g = (unsigned char)(RGBGetGValue(color) * brightness_scale); + unsigned char b = (unsigned char)(RGBGetBValue(color) * brightness_scale); + + size_t idx = (color_idx * 3); + + /*------------------------*\ + | The protocol uses R B G | + \*------------------------*/ + color_buf[idx + 0] = r; + color_buf[idx + 1] = b; + color_buf[idx + 2] = g; + } +} + +unsigned char LianLiUniHubSLController::ConvertBrightness(unsigned int brightness) +{ + switch(brightness) + { + case 0: + return UNIHUB_SL_LED_BRIGHTNESS_000; + + case 1: + return UNIHUB_SL_LED_BRIGHTNESS_025; + + case 2: + return UNIHUB_SL_LED_BRIGHTNESS_050; + + case 3: + return UNIHUB_SL_LED_BRIGHTNESS_075; + + case 4: + return UNIHUB_SL_LED_BRIGHTNESS_100; + + default: + return UNIHUB_SL_LED_BRIGHTNESS_100; + } +} + +unsigned char LianLiUniHubSLController::ConvertSpeed(unsigned int speed) +{ + switch(speed) + { + case 0: + return UNIHUB_SL_LED_SPEED_000; + + case 1: + return UNIHUB_SL_LED_SPEED_025; + + case 2: + return UNIHUB_SL_LED_SPEED_050; + + case 3: + return UNIHUB_SL_LED_SPEED_075; + + case 4: + return UNIHUB_SL_LED_SPEED_100; + + default: + return UNIHUB_SL_LED_SPEED_050; + } +} + +unsigned char LianLiUniHubSLController::ConvertDirection(unsigned int direction) +{ + switch(direction) + { + case MODE_DIRECTION_LEFT: + return UNIHUB_SL_LED_DIRECTION_LTR; + + case MODE_DIRECTION_RIGHT: + return UNIHUB_SL_LED_DIRECTION_RTL; + + default: + return UNIHUB_SL_LED_DIRECTION_LTR; + } +} + +void LianLiUniHubSLController::SendActivate(size_t channel, unsigned char num_fans) +{ + unsigned char buf[11]; + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = UNIHUB_SL_REPORT_ID; + buf[0x01] = 0x10; + buf[0x02] = 0x32; + buf[0x03] = 0x10 * (unsigned char)channel + num_fans; + + this->LogBuffer("SendActivate", buf, sizeof(buf)); + + hid_write(this->device, buf, sizeof(buf)); + std::this_thread::sleep_for(5ms); +} + +void LianLiUniHubSLController::SendMerge() +{ + unsigned char buf[11]; + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = UNIHUB_SL_REPORT_ID; + buf[0x01] = 0x10; + if(is_merged_mode) + { + buf[0x02] = 0x33; + buf[0x03] = 0x00; + buf[0x04] = 0x01; + buf[0x05] = 0x02; + buf[0x06] = 0x03; + buf[0x07] = 0x08; + } + else + { + buf[0x02] = 0x34; + } + + this->LogBuffer("SendMerge", buf, sizeof(buf)); + + hid_write(this->device, buf, sizeof(buf)); + std::this_thread::sleep_for(5ms); +} + +void LianLiUniHubSLController::SendColor(size_t channel, const unsigned char *colors, size_t num_colors) +{ + unsigned char* buf = new unsigned char[2 + num_colors]; + memset(buf, 0x00, (2 + num_colors)); + + buf[0x00] = UNIHUB_SL_REPORT_ID; + buf[0x01] = 0x30 + (unsigned char)channel; // Channel 1: 0x30, Channel 2: 0x31, etc. + + memcpy(&buf[0x02], colors, num_colors); + + this->LogBuffer("SendColor", buf, sizeof(buf)); + + hid_write(this->device, buf, sizeof(buf)); + std::this_thread::sleep_for(5ms); + + delete[] buf; +} + +void LianLiUniHubSLController::SendMode(size_t channel, const mode &active) +{ + unsigned char buf[11]; + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = UNIHUB_SL_REPORT_ID; + buf[0x01] = 0x10 + (unsigned char)channel; // Channel 1: 0x10, Channel 2: 0x11, etc. + buf[0x02] = active.value; + buf[0x03] = this->ConvertSpeed(active.speed); + buf[0x04] = this->ConvertDirection(active.direction); + buf[0x05] = this->ConvertBrightness(active.brightness); + + this->LogBuffer("SendMode", buf, sizeof(buf)); + + hid_write(this->device, buf, sizeof(buf)); + std::this_thread::sleep_for(5ms); +} + +void LianLiUniHubSLController::LogBuffer(const char *operation, const unsigned char *buf, size_t buf_len) +{ + std::string hex_string; + for(size_t i = 0; i < buf_len; i++) + { + char hex_byte[3]; + snprintf(hex_byte, sizeof(hex_byte), "%02X", buf[i]); + hex_string += hex_byte; + } + LOG_DEBUG("[Lian Li Uni Hub - SL] %s buffer: %s", operation, hex_string.c_str()); +} diff --git a/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.h b/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.h new file mode 100644 index 0000000..7ef50f8 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.h @@ -0,0 +1,119 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubSLController.h | +| | +| Driver for Lian Li Uni Hub - SL | +| | +| Muhamad Visat 26 Jul 2025 | +| Original work by Luca Lovisa & Oliver P | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +enum +{ + UNIHUB_SL_REPORT_ID = 0xE0, +}; + +enum +{ + UNIHUB_SL_MAX_CHANNEL = 4, + UNIHUB_SL_MAX_FAN_PER_CHANNEL = 4, + UNIHUB_SL_LED_PER_FAN = 16, + UNIHUB_SL_MAX_LED_PER_CHANNEL = 64, +}; + +enum +{ + UNIHUB_SL_LED_BRIGHTNESS_MIN = 0x00, // 0% + UNIHUB_SL_LED_BRIGHTNESS_MAX = 0x04, // 100% + UNIHUB_SL_LED_BRIGHTNESS_DEFAULT = 0x04, // 100% + + UNIHUB_SL_LED_BRIGHTNESS_000 = 0x08, // Black (turned off) + UNIHUB_SL_LED_BRIGHTNESS_025 = 0x03, // Dark + UNIHUB_SL_LED_BRIGHTNESS_050 = 0x02, // Medium + UNIHUB_SL_LED_BRIGHTNESS_075 = 0x01, // Bright + UNIHUB_SL_LED_BRIGHTNESS_100 = 0x00, // Brightest +}; + +enum +{ + UNIHUB_SL_LED_SPEED_MIN = 0x00, // Slowest + UNIHUB_SL_LED_SPEED_MAX = 0x04, // Fastest + UNIHUB_SL_LED_SPEED_DEFAULT = 0x00, // Slowest + + UNIHUB_SL_LED_SPEED_000 = 0x02, // Slowest + UNIHUB_SL_LED_SPEED_025 = 0x01, // Slow + UNIHUB_SL_LED_SPEED_050 = 0x00, // Medium + UNIHUB_SL_LED_SPEED_075 = 0xff, // Fast + UNIHUB_SL_LED_SPEED_100 = 0xfe, // Fastest +}; + +enum +{ + UNIHUB_SL_LED_DIRECTION_LTR = 0x00, // Left to right + UNIHUB_SL_LED_DIRECTION_RTL = 0x01, // Right to left +}; + +enum +{ + UNIHUB_SL_LED_MODE_RAINBOW = 0x05, + UNIHUB_SL_LED_MODE_RAINBOW_MORPH = 0x04, + UNIHUB_SL_LED_MODE_STATIC = 0x01, + UNIHUB_SL_LED_MODE_BREATHING = 0x02, + UNIHUB_SL_LED_MODE_COLOR_CYCLE = 0x23, + UNIHUB_SL_LED_MODE_RUNWAY = 0x1c, + UNIHUB_SL_LED_MODE_RUNWAY_MERGED = 0x1c, + UNIHUB_SL_LED_MODE_STAGGERED = 0x18, + UNIHUB_SL_LED_MODE_TIDE = 0x1a, + UNIHUB_SL_LED_MODE_METEOR = 0x24, + UNIHUB_SL_LED_MODE_METEOR_MERGED = 0x24, + UNIHUB_SL_LED_MODE_MIXING = 0x1e, + UNIHUB_SL_LED_MODE_STACK = 0x20, + UNIHUB_SL_LED_MODE_STACK_MULTI_COLOR = 0x21, + UNIHUB_SL_LED_MODE_NEON = 0x22, +}; + +class LianLiUniHubSLController +{ +public: + LianLiUniHubSLController(hid_device *device, const char *path); + ~LianLiUniHubSLController(); + + std::string ReadVersion(); + std::string ReadSerial(); + + std::string GetLocation() { return this->location; }; + + void UpdateMode(const std::vector &zones, const mode &active); + void UpdateZoneLEDs(size_t channel, const zone &z, const mode &active); + +private: + hid_device *device; + std::string location; + + bool is_merged_mode; + + void SetPerLEDColor(size_t channel, const zone &z, const mode &active); + void SetModeSpecificColor(size_t channel, const mode &active); + void FillStaticColorBuffer(unsigned char *color_buf, const RGBColor *colors, size_t num_colors, float brightness_scale); + void FillDynamicColorBuffer(unsigned char *color_buf, const RGBColor *colors, size_t num_colors, float brightness_scale); + + unsigned char ConvertBrightness(unsigned int brightness); + unsigned char ConvertSpeed(unsigned int speed); + unsigned char ConvertDirection(unsigned int direction); + + void SendActivate(size_t channel, unsigned char num_fans); + void SendMerge(); + void SendColor(size_t channel, const unsigned char *color_buf, size_t color_buf_len); + void SendMode(size_t channel, const mode &active); + + void LogBuffer(const char *operation, const unsigned char *buf, size_t len); +}; diff --git a/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.cpp b/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.cpp new file mode 100644 index 0000000..15315f6 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.cpp @@ -0,0 +1,381 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHubSL.cpp | +| | +| RGBController for Lian Li Uni Hub - SL | +| | +| Muhamad Visat 26 Jul 2025 | +| Original work by Luca Lovisa & Oliver P | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "RGBController_LianLiUniHubSL.h" + +/**------------------------------------------------------------------*\ + @name Lian Li Uni Hub - SL + @category Cooler + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectLianLiUniHubSL + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LianLiUniHubSL::RGBController_LianLiUniHubSL(LianLiUniHubSLController *controller, std::string name) +{ + this->controller = controller; + this->name = name; + this->vendor = "Lian Li"; + this->description = "Lian Li Uni Hub - SL"; + this->version = controller->ReadVersion(); + this->serial = controller->ReadSerial(); + this->location = controller->GetLocation(); + this->type = DEVICE_TYPE_COOLER; + + initialized = false; + + mode Custom; + Custom.name = "Custom"; + Custom.value = UNIHUB_SL_LED_MODE_STATIC; + Custom.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Custom.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Custom.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Custom.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = UNIHUB_SL_LED_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Rainbow.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Rainbow.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Rainbow.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Rainbow.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Rainbow.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Rainbow.direction = UNIHUB_SL_LED_DIRECTION_LTR; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode RainbowMorph; + RainbowMorph.name = "Rainbow Morph"; + RainbowMorph.value = UNIHUB_SL_LED_MODE_RAINBOW_MORPH; + RainbowMorph.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RainbowMorph.speed_min = UNIHUB_SL_LED_SPEED_MIN; + RainbowMorph.speed_max = UNIHUB_SL_LED_SPEED_MAX; + RainbowMorph.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + RainbowMorph.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + RainbowMorph.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + RainbowMorph.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + RainbowMorph.direction = UNIHUB_SL_LED_DIRECTION_LTR; + RainbowMorph.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowMorph); + + mode Static; + Static.name = "Static"; + Static.value = UNIHUB_SL_LED_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Static.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Static.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Static.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = UNIHUB_SL_LED_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Breathing.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Breathing.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Breathing.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Breathing.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Breathing.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = UNIHUB_SL_LED_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + ColorCycle.speed_min = UNIHUB_SL_LED_SPEED_MIN; + ColorCycle.speed_max = UNIHUB_SL_LED_SPEED_MAX; + ColorCycle.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + ColorCycle.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + ColorCycle.colors_min = 3; + ColorCycle.colors_max = 3; + ColorCycle.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + ColorCycle.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + ColorCycle.direction = UNIHUB_SL_LED_DIRECTION_LTR; + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(ColorCycle); + + mode Runway; + Runway.name = "Runway"; + Runway.value = UNIHUB_SL_LED_MODE_RUNWAY; + Runway.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Runway.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Runway.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Runway.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Runway.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Runway.colors_min = 2; + Runway.colors_max = 2; + Runway.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Runway.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Runway.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Runway); + + mode RunwayMerged; + RunwayMerged.name = "Runway Merged"; + RunwayMerged.value = UNIHUB_SL_LED_MODE_RUNWAY; + RunwayMerged.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + RunwayMerged.speed_min = UNIHUB_SL_LED_SPEED_MIN; + RunwayMerged.speed_max = UNIHUB_SL_LED_SPEED_MAX; + RunwayMerged.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + RunwayMerged.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + RunwayMerged.colors_min = 2; + RunwayMerged.colors_max = 2; + RunwayMerged.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + RunwayMerged.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + RunwayMerged.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(RunwayMerged); + + mode Staggered; + Staggered.name = "Staggered"; + Staggered.value = UNIHUB_SL_LED_MODE_STAGGERED; + Staggered.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Staggered.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Staggered.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Staggered.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Staggered.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Staggered.colors_min = 2; + Staggered.colors_max = 2; + Staggered.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Staggered.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Staggered.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Staggered); + + mode Tide; + Tide.name = "Tide"; + Tide.value = UNIHUB_SL_LED_MODE_TIDE; + Tide.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Tide.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Tide.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Tide.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Tide.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Tide.colors_min = 2; + Tide.colors_max = 2; + Tide.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Tide.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Tide.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Tide); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = UNIHUB_SL_LED_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Meteor.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Meteor.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Meteor.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Meteor.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Meteor.colors_min = 2; + Meteor.colors_max = 2; + Meteor.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Meteor.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Meteor.direction = UNIHUB_SL_LED_DIRECTION_LTR; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Meteor); + + mode MeteorMerged; + MeteorMerged.name = "Meteor Merged"; + MeteorMerged.value = UNIHUB_SL_LED_MODE_METEOR_MERGED; + MeteorMerged.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + MeteorMerged.speed_min = UNIHUB_SL_LED_SPEED_MIN; + MeteorMerged.speed_max = UNIHUB_SL_LED_SPEED_MAX; + MeteorMerged.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + MeteorMerged.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + MeteorMerged.colors_min = 2; + MeteorMerged.colors_max = 2; + MeteorMerged.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + MeteorMerged.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + MeteorMerged.direction = UNIHUB_SL_LED_DIRECTION_LTR; + MeteorMerged.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(MeteorMerged); + + mode Mixing; + Mixing.name = "Mixing"; + Mixing.value = UNIHUB_SL_LED_MODE_MIXING; + Mixing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Mixing.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Mixing.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Mixing.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Mixing.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Mixing.colors_min = 2; + Mixing.colors_max = 2; + Mixing.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Mixing.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Mixing.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Mixing); + + mode Stack; + Stack.name = "Stack"; + Stack.value = UNIHUB_SL_LED_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Stack.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Stack.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Stack.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Stack.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Stack.colors_min = 1; + Stack.colors_max = 1; + Stack.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Stack.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Stack.direction = UNIHUB_SL_LED_DIRECTION_LTR; + Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Stack); + + mode StackMultiColor; + StackMultiColor.name = "Stack Multi Color"; + StackMultiColor.value = UNIHUB_SL_LED_MODE_STACK_MULTI_COLOR; + StackMultiColor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + StackMultiColor.speed_min = UNIHUB_SL_LED_SPEED_MIN; + StackMultiColor.speed_max = UNIHUB_SL_LED_SPEED_MAX; + StackMultiColor.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + StackMultiColor.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + StackMultiColor.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + StackMultiColor.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + StackMultiColor.direction = UNIHUB_SL_LED_DIRECTION_LTR; + StackMultiColor.color_mode = MODE_COLORS_NONE; + modes.push_back(StackMultiColor); + + mode Neon; + Neon.name = "Neon"; + Neon.value = UNIHUB_SL_LED_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Neon.speed_min = UNIHUB_SL_LED_SPEED_MIN; + Neon.speed_max = UNIHUB_SL_LED_SPEED_MAX; + Neon.brightness_min = UNIHUB_SL_LED_BRIGHTNESS_MIN; + Neon.brightness_max = UNIHUB_SL_LED_BRIGHTNESS_MAX; + Neon.speed = UNIHUB_SL_LED_SPEED_DEFAULT; + Neon.brightness = UNIHUB_SL_LED_BRIGHTNESS_DEFAULT; + Neon.color_mode = MODE_COLORS_NONE; + modes.push_back(Neon); + + RGBColor default_colors[] = + { + ToRGBColor(255, 0, 0), // Red + ToRGBColor(0, 255, 0), // Green + ToRGBColor(0, 0, 255), // Blue + ToRGBColor(255, 255, 255), // White + }; + + for(size_t mode_idx = 0; mode_idx < modes.size(); mode_idx++) + { + mode &m = modes[mode_idx]; + m.colors.resize(m.colors_max); + for (unsigned int color_idx = 0; color_idx < m.colors_max; color_idx++) + { + m.colors[color_idx] = default_colors[color_idx % sizeof(default_colors)]; + } + } + + RGBController_LianLiUniHubSL::SetupZones(); +} + +RGBController_LianLiUniHubSL::~RGBController_LianLiUniHubSL() +{ + delete this->controller; +} + +void RGBController_LianLiUniHubSL::SetupZones() +{ + bool first_run = zones.size() == 0; + + leds.clear(); + colors.clear(); + if(first_run) + { + zones.resize(UNIHUB_SL_MAX_CHANNEL); + } + + for(size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + zones[zone_idx].name = "Channel "; + zones[zone_idx].name.append(std::to_string(zone_idx + 1)); + zones[zone_idx].type = ZONE_TYPE_LINEAR; + zones[zone_idx].matrix_map = NULL; + + zones[zone_idx].leds_min = 0; + zones[zone_idx].leds_max = UNIHUB_SL_MAX_FAN_PER_CHANNEL; + + if(first_run) + { + zones[zone_idx].leds_count = zones[zone_idx].leds_min; + } + + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + new_led.name = zones[zone_idx].name; + new_led.name.append(", Fan "); + new_led.name.append(std::to_string(led_idx + 1)); + new_led.value = (unsigned int)zone_idx; + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_LianLiUniHubSL::ResizeZone(int zone, int new_size) +{ + if((size_t)zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_LianLiUniHubSL::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_LianLiUniHubSL::UpdateZoneLEDs(int zone) +{ + if(!initialized) + { + return DeviceUpdateMode(); + } + controller->UpdateZoneLEDs(zone, zones[zone], modes[active_mode]); +} + +void RGBController_LianLiUniHubSL::UpdateSingleLED(int /* led */) +{ + DeviceUpdateMode(); +} + +void RGBController_LianLiUniHubSL::DeviceUpdateMode() +{ + if(active_mode == 0) + { + return; + } + + controller->UpdateMode(zones, modes[active_mode]); + initialized = true; +} + +void RGBController_LianLiUniHubSL::SetCustomMode() +{ + active_mode = 0; +} diff --git a/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.h b/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.h new file mode 100644 index 0000000..9728916 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHubSL.h | +| | +| RGBController for Lian Li Uni Hub - SL | +| | +| Muhamad Visat 26 Jul 2025 | +| Original work by Luca Lovisa & Oliver P | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LianLiUniHubSLController.h" + +class RGBController_LianLiUniHubSL : public RGBController +{ +public: + RGBController_LianLiUniHubSL(LianLiUniHubSLController *controller, std::string name); + ~RGBController_LianLiUniHubSL(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void SetCustomMode(); + +private: + LianLiUniHubSLController *controller; + bool initialized; +}; diff --git a/Controllers/LianLiController/LianLiUniHubSLInfinityController/LianLiUniHubSLInfinityController.cpp b/Controllers/LianLiController/LianLiUniHubSLInfinityController/LianLiUniHubSLInfinityController.cpp new file mode 100644 index 0000000..93ca16c --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLInfinityController/LianLiUniHubSLInfinityController.cpp @@ -0,0 +1,319 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubSLInfinityController.cpp | +| | +| Driver for Lian Li SL Infinity Uni Hub | +| | +| Simon McKenna 21 Oct 2023 | +| Will Kennedy 17 Jan 2023 | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LianLiUniHubSLInfinityController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +LianLiUniHubSLInfinityController::LianLiUniHubSLInfinityController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +LianLiUniHubSLInfinityController::~LianLiUniHubSLInfinityController() +{ + hid_close(dev); +} + +std::string LianLiUniHubSLInfinityController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LianLiUniHubSLInfinityController::GetFirmwareVersionString() +{ + wchar_t product_string[40]; + int ret = hid_get_product_string(dev, product_string, 40); + + if (ret != 0) + { + return (""); + } + + std::string return_string = StringUtils::wstring_to_string(product_string); + + return(return_string.substr(return_string.find_last_of("-")+1,4).c_str()); +} + +std::string LianLiUniHubSLInfinityController::GetName() +{ + return(name); +} + +std::string LianLiUniHubSLInfinityController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +float infinityBrightnessLimit(RGBColor color) +{ + /*---------------------------------------------------------*\ + | Limiter to protect LEDs | + \*---------------------------------------------------------*/ + if(UNIHUB_SLINF_LED_LIMITER && (RGBGetRValue(color) + RGBGetBValue(color) + RGBGetGValue(color) > 460)) + { + return 460.f / (RGBGetRValue(color) + RGBGetBValue(color) + RGBGetGValue(color)); + } + return 1; +} + +void LianLiUniHubSLInfinityController::SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors, float brightness) +{ + unsigned char led_data[16 * 6 * 3]; + int fan_idx = 0; + int mod_led_idx; + int cur_led_idx; + + if(num_colors == 0) + { + return; // Do nothing, channel isn't in use + } + + for(unsigned int led_idx = 0; led_idx < num_colors; led_idx++) + { + mod_led_idx = (led_idx % 16); + + if((mod_led_idx == 0) && (led_idx != 0)) + { + fan_idx++; + } + + float brightness_scale = brightness * infinityBrightnessLimit(colors[led_idx]); + + //Determine current position of led_data array from colors array + cur_led_idx = ((mod_led_idx + (fan_idx * 16)) * 3); + + led_data[cur_led_idx + 0] = (unsigned char)(RGBGetRValue(colors[led_idx]) * brightness_scale); + led_data[cur_led_idx + 1] = (unsigned char)(RGBGetBValue(colors[led_idx]) * brightness_scale); + led_data[cur_led_idx + 2] = (unsigned char)(RGBGetGValue(colors[led_idx]) * brightness_scale); + } + + /*---------------------------------------------------------*\ + | Send fan LED data | + \*---------------------------------------------------------*/ + + SendStartAction + ( + channel, // Current channel + (fan_idx + 1) // Number of fans + ); + + SendColorData + ( + channel, // Channel + (fan_idx + 1)*16, + led_data + ); + + SendCommitAction + ( + channel, // Channel + UNIHUB_SLINF_LED_MODE_STATIC_COLOR, // Effect + UNIHUB_SLINF_LED_SPEED_000, // Speed + UNIHUB_SLINF_LED_DIRECTION_LTR, // Direction + UNIHUB_SLINF_LED_BRIGHTNESS_100 // Brightness + ); + +} + +void LianLiUniHubSLInfinityController::SetChannelMode(unsigned char channel, const mode active_mode, unsigned int num_fans) +{ + static unsigned int brightness_code[5] = + { + UNIHUB_SLINF_LED_BRIGHTNESS_000, + UNIHUB_SLINF_LED_BRIGHTNESS_025, + UNIHUB_SLINF_LED_BRIGHTNESS_050, + UNIHUB_SLINF_LED_BRIGHTNESS_075, + UNIHUB_SLINF_LED_BRIGHTNESS_100 + }; + + static unsigned int speed_code[5] = + { + UNIHUB_SLINF_LED_SPEED_000, + UNIHUB_SLINF_LED_SPEED_025, + UNIHUB_SLINF_LED_SPEED_050, + UNIHUB_SLINF_LED_SPEED_075, + UNIHUB_SLINF_LED_SPEED_100 + }; + + unsigned char fan_led_data[16 * 6 * 3]; + int cur_led_idx; + float brightness; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(fan_led_data, 0x00, sizeof(fan_led_data)); + + std::vector colors = active_mode.colors; + unsigned int num_colors = (unsigned int)colors.size(); + + if(!colors.empty()) // Update led_data if there's colors + { + brightness = static_cast(active_mode.brightness)/4; + if(num_colors == 6) + { + for(unsigned int i = 0; i < 6; i++) + { + float brightness_scale = brightness * infinityBrightnessLimit(colors[i]); + for(unsigned int led_idx = 0; led_idx < 16 * 3; led_idx += 3) + { + cur_led_idx = (i * 16 * 3) + led_idx; + fan_led_data[cur_led_idx + 0] = (unsigned char)(RGBGetRValue(colors[i]) * brightness_scale); + fan_led_data[cur_led_idx + 1] = (unsigned char)(RGBGetBValue(colors[i]) * brightness_scale); + fan_led_data[cur_led_idx + 2] = (unsigned char)(RGBGetGValue(colors[i]) * brightness_scale); + } + } + } + else + { + colors.resize(4); + for(unsigned int i = num_colors; i < 4; i++) + { + colors[i] = 0x00; + } + + // needs a 72 length array of 4 colors, even if less are defined + for(unsigned int j = 0; j < 4; j++) + { + float brightness_scale = brightness * infinityBrightnessLimit(colors[j]); + for(unsigned int i = 0; i < 6; i++) + { + cur_led_idx = (i * 12) + (j * 3); + fan_led_data[cur_led_idx + 0] = (unsigned char)(RGBGetRValue(colors[j]) * brightness_scale); + fan_led_data[cur_led_idx + 1] = (unsigned char)(RGBGetBValue(colors[j]) * brightness_scale); + fan_led_data[cur_led_idx + 2] = (unsigned char)(RGBGetGValue(colors[j]) * brightness_scale); + } + } + } + + } + + SendStartAction + ( + channel, // Current channel + (num_fans + 1) // Number of fans + ); + + SendColorData + ( + channel, // Channel + (num_fans + 1)*16, + fan_led_data // Data + ); + + SendCommitAction + ( + channel, // Channel + active_mode.value, // Effect + speed_code[active_mode.speed], // Speed + active_mode.direction, // Direction + brightness_code[active_mode.brightness] // Brightness + ); +} + +void LianLiUniHubSLInfinityController::SendStartAction(unsigned char channel, unsigned int /*num_fans*/) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = UNIHUB_SLINF_TRANSACTION_ID; + usb_buf[0x01] = 0x10; + usb_buf[0x02] = 0x60; + usb_buf[0x03] = 1 + (channel / 2); // every fan-array uses two channels (one for the spinner and one for the led-band on the side) + usb_buf[0x04] = 0x04; // TODO: number of fans (1-4) on this channel, hardcoding this to 4 for now + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, sizeof(usb_buf)); + std::this_thread::sleep_for(5ms); + +} + +void LianLiUniHubSLInfinityController::SendColorData(unsigned char channel, unsigned int num_leds, unsigned char* led_data) +{ + /*---------------------------------------------------------*\ + | Send LED data | + \*---------------------------------------------------------*/ + + unsigned char usb_buf[353]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = UNIHUB_SLINF_TRANSACTION_ID; + usb_buf[0x01] = 0x30 + channel; // action + channel(30 = channel 1, 31 = channel 2, etc.) + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x02], led_data, num_leds * 3); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, sizeof(usb_buf)); + std::this_thread::sleep_for(5ms); +} + +void LianLiUniHubSLInfinityController::SendCommitAction(unsigned char channel, unsigned char effect, unsigned char speed, unsigned int direction, unsigned int brightness) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = UNIHUB_SLINF_TRANSACTION_ID; + usb_buf[0x01] = 0x10 + channel; // Channel+device (10 = channel 1, 11 = channel 2, etc.) + usb_buf[0x02] = effect; // Effect + usb_buf[0x03] = speed; // Speed, 02=0%, 01=25%, 00=50%, ff=75%, fe=100% + usb_buf[0x04] = direction; // Direction, right=00, left=01 + usb_buf[0x05] = brightness; // Brightness, 0=100%, 1= 75%, 2 = 50%, 3 = 25%, 8 = 0% + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, sizeof(usb_buf)); + std::this_thread::sleep_for(5ms); +} diff --git a/Controllers/LianLiController/LianLiUniHubSLInfinityController/LianLiUniHubSLInfinityController.h b/Controllers/LianLiController/LianLiUniHubSLInfinityController/LianLiUniHubSLInfinityController.h new file mode 100644 index 0000000..fb57cb4 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLInfinityController/LianLiUniHubSLInfinityController.h @@ -0,0 +1,211 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubSLInfinityController.h | +| | +| Driver for Lian Li SL Infinity Uni Hub | +| | +| Simon McKenna 21 Oct 2023 | +| Will Kennedy 17 Jan 2023 | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*----------------------------------------------------------------------------*\ +| Global definitions. | +\*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*\ +| Definitions related to zone Sizes | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_SLINF_CHANNEL_COUNT = 0x08, /* Channel count */ + UNIHUB_SLINF_CHAN_LED_COUNT = 0x10 * 6, /* Max-LED per channel count - 96 */ +}; + +/*----------------------------------------------------------------------------*\ +| Definitions related to LED configuration. | +\*----------------------------------------------------------------------------*/ + +// Used for sync'd mode between Fan and Edge + +enum +{ + UNIHUB_SLINF_LED_MODE_STATIC_COLOR = 0x01, // full data array + UNIHUB_SLINF_LED_MODE_BREATHING = 0x02, // full data array + UNIHUB_SLINF_LED_MODE_RAINBOW_MORPH = 0x04, // no array + UNIHUB_SLINF_LED_MODE_RAINBOW = 0x05, // no array + UNIHUB_SLINF_LED_MODE_STAGGERED = 0x18, // size 2 + UNIHUB_SLINF_LED_MODE_TIDE = 0x1A, // size 2 + UNIHUB_SLINF_LED_MODE_RUNWAY = 0x1C, // size 2 + UNIHUB_SLINF_LED_MODE_MIXING = 0x1E, // size 2 + UNIHUB_SLINF_LED_MODE_STACK = 0x20, // size 1 + UNIHUB_SLINF_LED_MODE_STACK_MULTI_COLOR = 0x21, // no array + UNIHUB_SLINF_LED_MODE_NEON = 0x22, // no array + UNIHUB_SLINF_LED_MODE_COLOR_CYCLE = 0x23, // size 3 + UNIHUB_SLINF_LED_MODE_METEOR = 0x24, // size 2 + UNIHUB_SLINF_LED_MODE_VOICE = 0x26, // no array + UNIHUB_SLINF_LED_MODE_GROOVE = 0x27, // size 2 + UNIHUB_SLINF_LED_MODE_RENDER = 0x28, // size 4 + UNIHUB_SLINF_LED_MODE_TUNNEL = 0x29, // size 4 + // merged modes + UNIHUB_SLINF_LED_MODE_METEOR_MERGED = 0x2A, + UNIHUB_SLINF_LED_MODE_RUNWAY_MERGED = 0x2B, + UNIHUB_SLINF_LED_MODE_TIDE_MERGED = 0x2C, + UNIHUB_SLINF_LED_MODE_MIXING_MERGED = 0x2D, + UNIHUB_SLINF_LED_MODE_STACK_MULTI_COLOR_MERGED = 0x2E +}; + +enum +{ + UNIHUB_SLINF_LED_SPEED_000 = 0x02, /* Very slow speed */ + UNIHUB_SLINF_LED_SPEED_025 = 0x01, /* Rather slow speed */ + UNIHUB_SLINF_LED_SPEED_050 = 0x00, /* Medium speed */ + UNIHUB_SLINF_LED_SPEED_075 = 0xFF, /* Rather fast speed */ + UNIHUB_SLINF_LED_SPEED_100 = 0xFE, /* Very fast speed */ +}; + +enum +{ + UNIHUB_SLINF_LED_DIRECTION_LTR = 0x00, /* Left-to-Right direction */ + UNIHUB_SLINF_LED_DIRECTION_RTL = 0x01, /* Right-to-Left direction */ +}; + +enum +{ + UNIHUB_SLINF_LED_BRIGHTNESS_000 = 0x08, /* Very dark (off) */ + UNIHUB_SLINF_LED_BRIGHTNESS_025 = 0x03, /* Rather dark */ + UNIHUB_SLINF_LED_BRIGHTNESS_050 = 0x02, /* Medium bright */ + UNIHUB_SLINF_LED_BRIGHTNESS_075 = 0x01, /* Rather bright */ + UNIHUB_SLINF_LED_BRIGHTNESS_100 = 0x00, /* Very bright */ +}; + +enum +{ + UNIHUB_SLINF_LED_LIMITER = 0x01 /* Limit the color white to 999999 as per manufacturer limits */ +}; + + +/*----------------------------------------------------------------------------*\ +| Definitions related to packet configuration. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_SLINF_TRANSACTION_ID = 0xE0, /* Command value to start all packets */ +}; + +/*----------------------------------------------------------------------------*\ +| Uni Hub SL Infinity controller. | +\*----------------------------------------------------------------------------*/ + +class LianLiUniHubSLInfinityController +{ + + +public: + LianLiUniHubSLInfinityController(hid_device* dev_handle, const char* path, std::string dev_name); + ~LianLiUniHubSLInfinityController(); + + std::string GetDeviceLocation(); + std::string GetFirmwareVersionString(); + std::string GetName(); + std::string GetSerialString(); + + void SetChannelMode + ( + unsigned char channel, + const mode active_mode, + unsigned int num_fans + ); + + void SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors, + float brightness + ); + + void SendStartAction + ( + unsigned char channel, + unsigned int num_fans + ); + + void SendColorData + ( + unsigned char channel, // Zone index + unsigned int num_leds, + unsigned char* led_data // Color data payload + ); + + void SendCommitAction + ( + unsigned char channel, // Zone index + unsigned char effect, + unsigned char speed, + unsigned int direction, + unsigned int brightness + ); + +private: + /* The Uni Hub requires colors in RBG order */ + struct Color + { + uint8_t r; + uint8_t b; + uint8_t g; + }; + + /* The values correspond to the definitions above */ + struct Channel + { + uint8_t index; + + uint8_t anyFanCountOffset; + uint8_t anyFanCount; + + uint16_t ledActionAddress; + uint16_t ledCommitAddress; + uint16_t ledModeAddress; + uint16_t ledSpeedAddress; + uint16_t ledDirectionAddress; + uint16_t ledBrightnessAddress; + + Color colors[UNIHUB_SLINF_CHAN_LED_COUNT]; + + uint8_t ledMode; + uint8_t ledSpeed; + uint8_t ledDirection; + uint8_t ledBrightness; + + uint16_t fanHubActionAddress; + uint16_t fanHubCommitAddress; + + uint16_t fanPwmActionAddress; + uint16_t fanPwmCommitAddress; + uint16_t fanRpmActionAddress; + + uint16_t fanSpeed; + }; + +private: + hid_device* dev; + + /*---------------------------------------------------------*\ + | Device information strings | + \*---------------------------------------------------------*/ + std::string firmware_version; + std::string location; + std::string name; +}; diff --git a/Controllers/LianLiController/LianLiUniHubSLInfinityController/RGBController_LianLiUniHubSLInfinity.cpp b/Controllers/LianLiController/LianLiUniHubSLInfinityController/RGBController_LianLiUniHubSLInfinity.cpp new file mode 100644 index 0000000..e33e763 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLInfinityController/RGBController_LianLiUniHubSLInfinity.cpp @@ -0,0 +1,445 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHubSLInfinity.cpp | +| | +| RGBController for Lian Li SL Infinity Uni Hub | +| | +| Simon McKenna 21 Oct 2023 | +| Will Kennedy 17 Jan 2023 | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_LianLiUniHubSLInfinity.h" + +/**------------------------------------------------------------------*\ + @name Lian Li Uni Hub SL Infinity + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectLianLiUniHubSLInfinity + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LianLiUniHubSLInfinity::RGBController_LianLiUniHubSLInfinity(LianLiUniHubSLInfinityController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Lian Li"; + type = DEVICE_TYPE_COOLER; + description = "Lian Li Uni Hub - SL Infinity"; + version = controller->GetFirmwareVersionString(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + initializedMode = false; + + mode Custom; + Custom.name = "Custom"; + Custom.value = UNIHUB_SLINF_LED_MODE_STATIC_COLOR; + Custom.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Custom.brightness_min = 0; + Custom.brightness_max = 50; + Custom.brightness = 37; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode StaticColor; + StaticColor.name = "Static"; + StaticColor.value = UNIHUB_SLINF_LED_MODE_STATIC_COLOR; + StaticColor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + StaticColor.brightness_min = 0; + StaticColor.brightness_max = 4; + StaticColor.colors_min = 0; + StaticColor.colors_max = 6; + StaticColor.brightness = 4; + StaticColor.color_mode = MODE_COLORS_MODE_SPECIFIC; + StaticColor.colors.resize(6); + modes.push_back(StaticColor); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = UNIHUB_SLINF_LED_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.speed_min = 0; + Breathing.speed_max = 4; + Breathing.brightness_min = 0; + Breathing.brightness_max = 4; + Breathing.colors_min = 0; + Breathing.colors_max = 6; + Breathing.speed = 2; + Breathing.brightness = 4; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(6); + modes.push_back(Breathing); + + mode RainbowMorph; + RainbowMorph.name = "Spectrum Cycle"; + RainbowMorph.value = UNIHUB_SLINF_LED_MODE_RAINBOW_MORPH; + RainbowMorph.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + RainbowMorph.speed_min = 0; + RainbowMorph.speed_max = 4; + RainbowMorph.brightness_min = 0; + RainbowMorph.brightness_max = 4; + RainbowMorph.speed = 2; + RainbowMorph.brightness = 4; + RainbowMorph.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowMorph); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = UNIHUB_SLINF_LED_MODE_RAINBOW; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + RainbowWave.speed_min = 0; + RainbowWave.speed_max = 4; + RainbowWave.brightness_min = 0; + RainbowWave.brightness_max = 4; + RainbowWave.speed = 2; + RainbowWave.brightness = 4; + RainbowWave.direction = UNIHUB_SLINF_LED_DIRECTION_LTR; + RainbowWave.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowWave); + + mode Staggered; + Staggered.name = "Staggered"; + Staggered.value = UNIHUB_SLINF_LED_MODE_STAGGERED; + Staggered.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Staggered.speed_min = 0; + Staggered.speed_max = 4; + Staggered.brightness_min = 0; + Staggered.brightness_max = 4; + Staggered.colors_min = 0; + Staggered.colors_max = 2; + Staggered.speed = 2; + Staggered.brightness = 4; + Staggered.color_mode = MODE_COLORS_MODE_SPECIFIC; + Staggered.colors.resize(2); + modes.push_back(Staggered); + + mode Tide; // TODO: Has merge + Tide.name = "Tide"; + Tide.value = UNIHUB_SLINF_LED_MODE_TIDE; + Tide.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Tide.speed_min = 0; + Tide.speed_max = 4; + Tide.brightness_min = 0; + Tide.brightness_max = 4; + Tide.colors_min = 0; + Tide.colors_max = 2; + Tide.speed = 2; + Tide.brightness = 4; + Tide.color_mode = MODE_COLORS_MODE_SPECIFIC; + Tide.colors.resize(2); + modes.push_back(Tide); + + mode Runway; //TODO: Has merge + Runway.name = "Runway"; + Runway.value = UNIHUB_SLINF_LED_MODE_RUNWAY; + Runway.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Runway.speed_min = 0; + Runway.speed_max = 4; + Runway.brightness_min = 0; + Runway.brightness_max = 4; + Runway.colors_min = 0; + Runway.colors_max = 2; + Runway.speed = 2; + Runway.brightness = 4; + Runway.color_mode = MODE_COLORS_MODE_SPECIFIC; + Runway.colors.resize(2); + modes.push_back(Runway); + + mode Mixing; //TODO: Has merge + Mixing.name = "Mixing"; + Mixing.value = UNIHUB_SLINF_LED_MODE_MIXING; + Mixing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Mixing.speed_min = 0; + Mixing.speed_max = 4; + Mixing.brightness_min = 0; + Mixing.brightness_max = 4; + Mixing.colors_min = 0; + Mixing.colors_max = 2; + Mixing.speed = 2; + Mixing.brightness = 4; + Mixing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Mixing.colors.resize(2); + modes.push_back(Mixing); + + mode Stack; + Stack.name = "Stack"; + Stack.value = UNIHUB_SLINF_LED_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Stack.speed_min = 0; + Stack.speed_max = 4; + Stack.brightness_min = 0; + Stack.brightness_max = 4; + Stack.colors_min = 0; + Stack.colors_max = 1; + Stack.speed = 2; + Stack.brightness = 4; + Stack.direction = UNIHUB_SLINF_LED_DIRECTION_LTR; + Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stack.colors.resize(1); + modes.push_back(Stack); + + mode StackMultiColor; //TODO: Has merge + Stack.name = "Stack Multi Color"; + Stack.value = UNIHUB_SLINF_LED_MODE_STACK_MULTI_COLOR; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Stack.speed_min = 0; + Stack.speed_max = 4; + Stack.brightness_min = 0; + Stack.brightness_max = 4; + Stack.speed = 2; + Stack.brightness = 4; + Stack.direction = UNIHUB_SLINF_LED_DIRECTION_LTR; + Stack.color_mode = MODE_COLORS_NONE; + modes.push_back(Stack); + + mode Neon; + Neon.name = "Neon"; + Neon.value = UNIHUB_SLINF_LED_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Neon.speed_min = 0; + Neon.speed_max = 4; + Neon.brightness_min = 0; + Neon.brightness_max = 4; + Neon.speed = 2; + Neon.brightness = 4; + Neon.color_mode = MODE_COLORS_NONE; + modes.push_back(Neon); + + mode ColorCycle; + ColorCycle.name = "ColorCycle"; + ColorCycle.value = UNIHUB_SLINF_LED_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + ColorCycle.speed_min = 0; + ColorCycle.speed_max = 4; + ColorCycle.brightness_min = 0; + ColorCycle.brightness_max = 4; + ColorCycle.colors_min = 0; + ColorCycle.colors_max = 3; + ColorCycle.speed = 2; + ColorCycle.brightness = 4; + ColorCycle.direction = UNIHUB_SLINF_LED_DIRECTION_LTR; + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorCycle.colors.resize(3); + modes.push_back(ColorCycle); + + mode Meteor; //TODO: Has merge + Meteor.name = "Meteor"; + Meteor.value = UNIHUB_SLINF_LED_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Meteor.speed_min = 0; + Meteor.speed_max = 4; + Meteor.brightness_min = 0; + Meteor.brightness_max = 4; + Meteor.colors_min = 0; + Meteor.colors_max = 2; + Meteor.speed = 2; + Meteor.brightness = 4; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors.resize(2); + modes.push_back(Meteor); + + mode Voice; + Voice.name = "Voice"; + Voice.value = UNIHUB_SLINF_LED_MODE_VOICE; + Voice.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Voice.speed_min = 0; + Voice.speed_max = 4; + Voice.brightness_min = 0; + Voice.brightness_max = 4; + Voice.speed = 2; + Voice.brightness = 4; + modes.push_back(Voice); + + mode Groove; + Groove.name = "Groove"; + Groove.value = UNIHUB_SLINF_LED_MODE_GROOVE; + Groove.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Groove.speed_min = 0; + Groove.speed_max = 4; + Groove.brightness_min = 0; + Groove.brightness_max = 4; + Groove.colors_min = 0; + Groove.colors_max = 1; + Groove.speed = 2; + Groove.brightness = 4; + Groove.direction = UNIHUB_SLINF_LED_DIRECTION_LTR; + Groove.color_mode = MODE_COLORS_MODE_SPECIFIC; + Groove.colors.resize(1); + modes.push_back(Groove); + + mode Render; + Render.name = "Render"; + Render.value = UNIHUB_SLINF_LED_MODE_RENDER; + Render.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Render.speed_min = 0; + Render.speed_max = 4; + Render.brightness_min = 0; + Render.brightness_max = 4; + Render.colors_min = 0; + Render.colors_max = 4; + Render.speed = 2; + Render.brightness = 4; + Render.direction = UNIHUB_SLINF_LED_DIRECTION_LTR; + Render.color_mode = MODE_COLORS_MODE_SPECIFIC; + Render.colors.resize(4); + modes.push_back(Render); + + mode Tunnel; + Tunnel.name = "Tunnel"; + Tunnel.value = UNIHUB_SLINF_LED_MODE_TUNNEL; + Tunnel.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Tunnel.speed_min = 0; + Tunnel.speed_max = 4; + Tunnel.brightness_min = 0; + Tunnel.brightness_max = 4; + Tunnel.colors_min = 0; + Tunnel.colors_max = 4; + Tunnel.speed = 2; + Tunnel.brightness = 4; + Tunnel.direction = UNIHUB_SLINF_LED_DIRECTION_LTR; + Tunnel.color_mode = MODE_COLORS_MODE_SPECIFIC; + Tunnel.colors.resize(4); + modes.push_back(Tunnel); + + RGBController_LianLiUniHubSLInfinity::SetupZones(); +} + +RGBController_LianLiUniHubSLInfinity::~RGBController_LianLiUniHubSLInfinity() +{ + delete controller; +} + +void RGBController_LianLiUniHubSLInfinity::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + zones.resize(UNIHUB_SLINF_CHANNEL_COUNT); + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for(unsigned int channel_idx = 0; channel_idx < zones.size(); channel_idx++) + { + zones[channel_idx].name = "Channel "; + zones[channel_idx].name.append(std::to_string(channel_idx + 1)); + + zones[channel_idx].type = ZONE_TYPE_LINEAR; + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = UNIHUB_SLINF_CHAN_LED_COUNT; + + if(first_run) + { + zones[channel_idx].leds_count = zones[channel_idx].leds_min; + } + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + led new_led; + new_led.name = zones[channel_idx].name; + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_ch_idx + 1)); + new_led.value = channel_idx; + + leds.push_back(new_led); + } + + } + + SetupColors(); +} + +void RGBController_LianLiUniHubSLInfinity::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_LianLiUniHubSLInfinity::DeviceUpdateLEDs() +{ + + if(!initializedMode) + { + DeviceUpdateMode(); + } + + float brightness_scale = static_cast(modes[active_mode].brightness)/modes[active_mode].brightness_max; + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count, brightness_scale); + } +} + +void RGBController_LianLiUniHubSLInfinity::UpdateZoneLEDs(int zone) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + + float brightness_scale = static_cast(modes[active_mode].brightness)/modes[active_mode].brightness_max; + + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count, brightness_scale); +} + +void RGBController_LianLiUniHubSLInfinity::UpdateSingleLED(int /* led */) +{ + DeviceUpdateMode(); + +} + +void RGBController_LianLiUniHubSLInfinity::DeviceUpdateMode() +{ + if(!active_mode) + { + return; // Do nothing, custom mode should go through DeviceUpdateLEDs() to avoid flooding controller + } + + initializedMode = true; + + int fan_idx = 0; + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].leds_count == 0) + { + return; // Do nothing, channel isn't in use + } + fan_idx = ((zones[zone_idx].leds_count / 16) - 1); // Indexes start at 0 + + controller->SetChannelMode((unsigned char)zone_idx, + modes[active_mode], + fan_idx); + + } +} diff --git a/Controllers/LianLiController/LianLiUniHubSLInfinityController/RGBController_LianLiUniHubSLInfinity.h b/Controllers/LianLiController/LianLiUniHubSLInfinityController/RGBController_LianLiUniHubSLInfinity.h new file mode 100644 index 0000000..d206c2b --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLInfinityController/RGBController_LianLiUniHubSLInfinity.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHubSLInfinity.h | +| | +| RGBController for Lian Li SL Infinity Uni Hub | +| | +| Simon McKenna 21 Oct 2023 | +| Will Kennedy 17 Jan 2023 | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "LianLiUniHubSLInfinityController.h" +#include "RGBController.h" + +class RGBController_LianLiUniHubSLInfinity : public RGBController +{ +public: + RGBController_LianLiUniHubSLInfinity(LianLiUniHubSLInfinityController* controller_ptr); + ~RGBController_LianLiUniHubSLInfinity(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LianLiUniHubSLInfinityController* controller; + bool initializedMode; +}; diff --git a/Controllers/LianLiController/LianLiUniHubSLV2Controller/LianLiUniHubSLV2Controller.cpp b/Controllers/LianLiController/LianLiUniHubSLV2Controller/LianLiUniHubSLV2Controller.cpp new file mode 100644 index 0000000..a87fb31 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLV2Controller/LianLiUniHubSLV2Controller.cpp @@ -0,0 +1,316 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubSLV2Controller.cpp | +| | +| Driver for Lian Li SLV2 Uni Hub | +| | +| Will Kennedy 17 Jan 2023 | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LianLiUniHubSLV2Controller.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +LianLiUniHubSLV2Controller::LianLiUniHubSLV2Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +LianLiUniHubSLV2Controller::~LianLiUniHubSLV2Controller() +{ + hid_close(dev); +} + +std::string LianLiUniHubSLV2Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LianLiUniHubSLV2Controller::GetFirmwareVersionString() +{ + wchar_t product_string[40]; + int ret = hid_get_product_string(dev, product_string, 40); + + if (ret != 0) + { + return (""); + } + + std::string return_string = StringUtils::wstring_to_string(product_string); + + return(return_string.substr(return_string.find_last_of("-")+1,4).c_str()); +} + +std::string LianLiUniHubSLV2Controller::GetName() +{ + return(name); +} + +std::string LianLiUniHubSLV2Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +float brightnessLimit(RGBColor color) +{ + /*---------------------------------------------------------*\ + | Limiter to protect LEDs | + \*---------------------------------------------------------*/ + if(UNIHUB_SLV2_LED_LIMITER && (RGBGetRValue(color) + RGBGetBValue(color) + RGBGetGValue(color) > 460)) + { + return 460.f / (RGBGetRValue(color) + RGBGetBValue(color) + RGBGetGValue(color)); + } + return 1; +} + +void LianLiUniHubSLV2Controller::SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors, float brightness) +{ + unsigned char led_data[16 * 6 * 3]; + int fan_idx = 0; + int mod_led_idx; + int cur_led_idx; + + if(num_colors == 0) + { + return; // Do nothing, channel isn't in use + } + + for(unsigned int led_idx = 0; led_idx < num_colors; led_idx++) + { + mod_led_idx = (led_idx % 16); + + if((mod_led_idx == 0) && (led_idx != 0)) + { + fan_idx++; + } + + float brightness_scale = brightness * brightnessLimit(colors[led_idx]); + + //Determine current position of led_data array from colors array + cur_led_idx = ((mod_led_idx + (fan_idx * 16)) * 3); + + led_data[cur_led_idx + 0] = (unsigned char)(RGBGetRValue(colors[led_idx]) * brightness_scale); + led_data[cur_led_idx + 1] = (unsigned char)(RGBGetBValue(colors[led_idx]) * brightness_scale); + led_data[cur_led_idx + 2] = (unsigned char)(RGBGetGValue(colors[led_idx]) * brightness_scale); + } + + /*---------------------------------------------------------*\ + | Send fan LED data | + \*---------------------------------------------------------*/ + + SendStartAction + ( + channel, // Current channel + (fan_idx + 1) // Number of fans + ); + + SendColorData + ( + channel, // Channel + (fan_idx + 1)*16, + led_data + ); + + SendCommitAction + ( + channel, // Channel + UNIHUB_SLV2_LED_MODE_STATIC_COLOR, // Effect + UNIHUB_SLV2_LED_SPEED_000, // Speed + UNIHUB_SLV2_LED_DIRECTION_LTR, // Direction + UNIHUB_SLV2_LED_BRIGHTNESS_100 // Brightness + ); + +} + +void LianLiUniHubSLV2Controller::SetChannelMode(unsigned char channel, const mode active_mode, unsigned int num_fans) +{ + static unsigned int brightness_code[5] = + { + UNIHUB_SLV2_LED_BRIGHTNESS_000, + UNIHUB_SLV2_LED_BRIGHTNESS_025, + UNIHUB_SLV2_LED_BRIGHTNESS_050, + UNIHUB_SLV2_LED_BRIGHTNESS_075, + UNIHUB_SLV2_LED_BRIGHTNESS_100 + }; + + static unsigned int speed_code[5] = + { + UNIHUB_SLV2_LED_SPEED_000, + UNIHUB_SLV2_LED_SPEED_025, + UNIHUB_SLV2_LED_SPEED_050, + UNIHUB_SLV2_LED_SPEED_075, + UNIHUB_SLV2_LED_SPEED_100 + }; + + unsigned char fan_led_data[16 * 6 * 3]; + int cur_led_idx; + float brightness; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(fan_led_data, 0x00, sizeof(fan_led_data)); + + std::vector colors = active_mode.colors; + unsigned int num_colors = (unsigned int)colors.size(); + + if(!colors.empty()) // Update led_data if there's colors + { + brightness = static_cast(active_mode.brightness)/4; + if (num_colors == 6) + { + for(unsigned int i = 0; i < 6; i++) + { + float brightness_scale = brightness * brightnessLimit(colors[i]); + for(unsigned int led_idx = 0; led_idx < 16 * 3; led_idx += 3) + { + cur_led_idx = (i * 16 * 3) + led_idx; + fan_led_data[cur_led_idx + 0] = (unsigned char)(RGBGetRValue(colors[i]) * brightness_scale); + fan_led_data[cur_led_idx + 1] = (unsigned char)(RGBGetBValue(colors[i]) * brightness_scale); + fan_led_data[cur_led_idx + 2] = (unsigned char)(RGBGetGValue(colors[i]) * brightness_scale); + } + } + } + else + { + colors.resize(4); + for(unsigned int i = num_colors; i < 4; i++) + { + colors[i] = 0x00; + } + + // needs a 72 length array of 4 colors, even if less are defined + for(unsigned int j = 0; j < 4; j++) + { + float brightness_scale = brightness * brightnessLimit(colors[j]); + for(unsigned int i = 0; i < 6; i++) + { + cur_led_idx = (i * 12) + (j * 3); + fan_led_data[cur_led_idx + 0] = (unsigned char)(RGBGetRValue(colors[j]) * brightness_scale); + fan_led_data[cur_led_idx + 1] = (unsigned char)(RGBGetBValue(colors[j]) * brightness_scale); + fan_led_data[cur_led_idx + 2] = (unsigned char)(RGBGetGValue(colors[j]) * brightness_scale); + } + } + } + } + + SendStartAction + ( + channel, // Current channel + (num_fans + 1) // Number of fans + ); + + SendColorData + ( + channel, // Channel + (num_fans + 1)*16, + fan_led_data // Data + ); + + SendCommitAction + ( + channel, // Channel + active_mode.value, // Effect + speed_code[active_mode.speed], // Speed + active_mode.direction, // Direction + brightness_code[active_mode.brightness] // Brightness + ); +} + +void LianLiUniHubSLV2Controller::SendStartAction(unsigned char channel, unsigned int num_fans) +{ + unsigned char usb_buf[353]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = UNIHUB_SLV2_TRANSACTION_ID; + usb_buf[0x01] = 0x10; + usb_buf[0x02] = 0x60; + usb_buf[0x03] = (channel << 4) + num_fans; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, sizeof(usb_buf)); + std::this_thread::sleep_for(5ms); + +} + +void LianLiUniHubSLV2Controller::SendColorData(unsigned char channel, unsigned int num_leds, unsigned char* led_data) +{ + /*---------------------------------------------------------*\ + | Send LED data | + \*---------------------------------------------------------*/ + + unsigned char usb_buf[353]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = UNIHUB_SLV2_TRANSACTION_ID; + usb_buf[0x01] = 0x30 + channel; // action + channel(30 = channel 1, 31 = channel 2, etc.) + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x02], led_data, num_leds * 3); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, sizeof(usb_buf)); + std::this_thread::sleep_for(5ms); +} + +void LianLiUniHubSLV2Controller::SendCommitAction(unsigned char channel, unsigned char effect, unsigned char speed, unsigned int direction, unsigned int brightness) +{ + unsigned char usb_buf[353]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up message packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = UNIHUB_SLV2_TRANSACTION_ID; + usb_buf[0x01] = 0x10 + channel; // Channel+device (10 = channel 1, 11 = channel 2, etc.) + usb_buf[0x02] = effect; // Effect + usb_buf[0x03] = speed; // Speed, 02=0%, 01=25%, 00=50%, ff=75%, fe=100% + usb_buf[0x04] = direction; // Direction, right=00, left=01 + usb_buf[0x05] = brightness; // Brightness, 0=100%, 1= 75%, 2 = 50%, 3 = 25%, 8 = 0% + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, sizeof(usb_buf)); + std::this_thread::sleep_for(5ms); +} diff --git a/Controllers/LianLiController/LianLiUniHubSLV2Controller/LianLiUniHubSLV2Controller.h b/Controllers/LianLiController/LianLiUniHubSLV2Controller/LianLiUniHubSLV2Controller.h new file mode 100644 index 0000000..e748270 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLV2Controller/LianLiUniHubSLV2Controller.h @@ -0,0 +1,211 @@ +/*---------------------------------------------------------*\ +| LianLiUniHubSLV2Controller.h | +| | +| Driver for Lian Li SLV2 Uni Hub | +| | +| Will Kennedy 17 Jan 2023 | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*----------------------------------------------------------------------------*\ +| Global definitions. | +\*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*\ +| Definitions related to zone Sizes | +\*----------------------------------------------------------------------------*/ + + +enum +{ + UNIHUB_SLV2_CHANNEL_COUNT = 0x04, /* Channel count */ + UNIHUB_SLV2_CHAN_LED_COUNT = 0x10 * 6, /* Max-LED per channel count - 96 */ +}; + +/*----------------------------------------------------------------------------*\ +| Definitions related to LED configuration. | +\*----------------------------------------------------------------------------*/ + +// Used for sync'd mode between Fan and Edge + +enum +{ + UNIHUB_SLV2_LED_MODE_STATIC_COLOR = 0x01, // full data array + UNIHUB_SLV2_LED_MODE_BREATHING = 0x02, // full data array + UNIHUB_SLV2_LED_MODE_RAINBOW_MORPH = 0x04, // no array + UNIHUB_SLV2_LED_MODE_RAINBOW = 0x05, // no array + UNIHUB_SLV2_LED_MODE_STAGGERED = 0x18, // size 2 + UNIHUB_SLV2_LED_MODE_TIDE = 0x1A, // size 2 + UNIHUB_SLV2_LED_MODE_RUNWAY = 0x1C, // size 2 + UNIHUB_SLV2_LED_MODE_MIXING = 0x1E, // size 2 + UNIHUB_SLV2_LED_MODE_STACK = 0x20, // size 1 + UNIHUB_SLV2_LED_MODE_STACK_MULTI_COLOR = 0x21, // no array + UNIHUB_SLV2_LED_MODE_NEON = 0x22, // no array + UNIHUB_SLV2_LED_MODE_COLOR_CYCLE = 0x23, // size 3 + UNIHUB_SLV2_LED_MODE_METEOR = 0x24, // size 2 + UNIHUB_SLV2_LED_MODE_VOICE = 0x26, // no array + UNIHUB_SLV2_LED_MODE_GROOVE = 0x27, // size 2 + UNIHUB_SLV2_LED_MODE_RENDER = 0x28, // size 4 + UNIHUB_SLV2_LED_MODE_TUNNEL = 0x29, // size 4 + // merged modes + UNIHUB_SLV2_LED_MODE_METEOR_MERGED = 0x2A, + UNIHUB_SLV2_LED_MODE_RUNWAY_MERGED = 0x2B, + UNIHUB_SLV2_LED_MODE_TIDE_MERGED = 0x2C, + UNIHUB_SLV2_LED_MODE_MIXING_MERGED = 0x2D, + UNIHUB_SLV2_LED_MODE_STACK_MULTI_COLOR_MERGED = 0x2E +}; + +enum +{ + UNIHUB_SLV2_LED_SPEED_000 = 0x02, /* Very slow speed */ + UNIHUB_SLV2_LED_SPEED_025 = 0x01, /* Rather slow speed */ + UNIHUB_SLV2_LED_SPEED_050 = 0x00, /* Medium speed */ + UNIHUB_SLV2_LED_SPEED_075 = 0xFF, /* Rather fast speed */ + UNIHUB_SLV2_LED_SPEED_100 = 0xFE, /* Very fast speed */ +}; + +enum +{ + UNIHUB_SLV2_LED_DIRECTION_LTR = 0x00, /* Left-to-Right direction */ + UNIHUB_SLV2_LED_DIRECTION_RTL = 0x01, /* Right-to-Left direction */ +}; + +enum +{ + UNIHUB_SLV2_LED_BRIGHTNESS_000 = 0x08, /* Very dark (off) */ + UNIHUB_SLV2_LED_BRIGHTNESS_025 = 0x03, /* Rather dark */ + UNIHUB_SLV2_LED_BRIGHTNESS_050 = 0x02, /* Medium bright */ + UNIHUB_SLV2_LED_BRIGHTNESS_075 = 0x01, /* Rather bright */ + UNIHUB_SLV2_LED_BRIGHTNESS_100 = 0x00, /* Very bright */ +}; + +enum +{ + UNIHUB_SLV2_LED_LIMITER = 0x01 /* Limit the color white to 999999 as per manufacturer limits */ +}; + + +/*----------------------------------------------------------------------------*\ +| Definitions related to packet configuration. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_SLV2_TRANSACTION_ID = 0xE0, /* Command value to start all packets */ +}; + +/*----------------------------------------------------------------------------*\ +| Uni Hub SLV2 controller. | +\*----------------------------------------------------------------------------*/ + +class LianLiUniHubSLV2Controller +{ + + +public: + LianLiUniHubSLV2Controller(hid_device* dev_handle, const char* path, std::string dev_name); + ~LianLiUniHubSLV2Controller(); + + std::string GetDeviceLocation(); + std::string GetFirmwareVersionString(); + std::string GetName(); + std::string GetSerialString(); + + void SetChannelMode + ( + unsigned char channel, + const mode active_mode, + unsigned int num_fans + ); + + void SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors, + float brightness + ); + + void SendStartAction + ( + unsigned char channel, + unsigned int num_fans + ); + + void SendColorData + ( + unsigned char channel, // Zone index + unsigned int num_leds, + unsigned char* led_data // Color data payload + ); + + void SendCommitAction + ( + unsigned char channel, // Zone index + unsigned char effect, + unsigned char speed, + unsigned int direction, + unsigned int brightness + ); + +private: + /* The Uni Hub requires colors in RBG order */ + struct Color + { + uint8_t r; + uint8_t b; + uint8_t g; + }; + + /* The values correspond to the definitions above */ + struct Channel + { + uint8_t index; + + uint8_t anyFanCountOffset; + uint8_t anyFanCount; + + uint16_t ledActionAddress; + uint16_t ledCommitAddress; + uint16_t ledModeAddress; + uint16_t ledSpeedAddress; + uint16_t ledDirectionAddress; + uint16_t ledBrightnessAddress; + + Color colors[UNIHUB_SLV2_CHAN_LED_COUNT]; + + uint8_t ledMode; + uint8_t ledSpeed; + uint8_t ledDirection; + uint8_t ledBrightness; + + uint16_t fanHubActionAddress; + uint16_t fanHubCommitAddress; + + uint16_t fanPwmActionAddress; + uint16_t fanPwmCommitAddress; + uint16_t fanRpmActionAddress; + + uint16_t fanSpeed; + }; + +private: + hid_device* dev; + + /*---------------------------------------------------------*\ + | Device information strings | + \*---------------------------------------------------------*/ + std::string firmware_version; + std::string location; + std::string name; +}; diff --git a/Controllers/LianLiController/LianLiUniHubSLV2Controller/RGBController_LianLiUniHubSLV2.cpp b/Controllers/LianLiController/LianLiUniHubSLV2Controller/RGBController_LianLiUniHubSLV2.cpp new file mode 100644 index 0000000..42e2267 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLV2Controller/RGBController_LianLiUniHubSLV2.cpp @@ -0,0 +1,444 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHubSLV2.cpp | +| | +| RGBController for Lian Li SLV2 Uni Hub | +| | +| Will Kennedy 17 Jan 2023 | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_LianLiUniHubSLV2.h" + +/**------------------------------------------------------------------*\ + @name Lian Li Uni Hub SLV2 + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectLianLiUniHubSLV2 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LianLiUniHubSLV2::RGBController_LianLiUniHubSLV2(LianLiUniHubSLV2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Lian Li"; + type = DEVICE_TYPE_COOLER; + description = "Lian Li Uni Hub - SL V2"; + version = controller->GetFirmwareVersionString(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + initializedMode = false; + + mode Custom; + Custom.name = "Custom"; + Custom.value = UNIHUB_SLV2_LED_MODE_STATIC_COLOR; + Custom.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Custom.brightness_min = 0; + Custom.brightness_max = 50; + Custom.brightness = 50; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode StaticColor; + StaticColor.name = "Static"; + StaticColor.value = UNIHUB_SLV2_LED_MODE_STATIC_COLOR; + StaticColor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + StaticColor.brightness_min = 0; + StaticColor.brightness_max = 4; + StaticColor.colors_min = 0; + StaticColor.colors_max = 6; + StaticColor.brightness = 4; + StaticColor.color_mode = MODE_COLORS_MODE_SPECIFIC; + StaticColor.colors.resize(6); + modes.push_back(StaticColor); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = UNIHUB_SLV2_LED_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.speed_min = 0; + Breathing.speed_max = 4; + Breathing.brightness_min = 0; + Breathing.brightness_max = 4; + Breathing.colors_min = 0; + Breathing.colors_max = 6; + Breathing.speed = 2; + Breathing.brightness = 4; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(6); + modes.push_back(Breathing); + + mode RainbowMorph; + RainbowMorph.name = "Spectrum Cycle"; + RainbowMorph.value = UNIHUB_SLV2_LED_MODE_RAINBOW_MORPH; + RainbowMorph.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + RainbowMorph.speed_min = 0; + RainbowMorph.speed_max = 4; + RainbowMorph.brightness_min = 0; + RainbowMorph.brightness_max = 4; + RainbowMorph.speed = 2; + RainbowMorph.brightness = 4; + RainbowMorph.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowMorph); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = UNIHUB_SLV2_LED_MODE_RAINBOW; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + RainbowWave.speed_min = 0; + RainbowWave.speed_max = 4; + RainbowWave.brightness_min = 0; + RainbowWave.brightness_max = 4; + RainbowWave.speed = 2; + RainbowWave.brightness = 4; + RainbowWave.direction = UNIHUB_SLV2_LED_DIRECTION_LTR; + RainbowWave.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowWave); + + mode Staggered; + Staggered.name = "Staggered"; + Staggered.value = UNIHUB_SLV2_LED_MODE_STAGGERED; + Staggered.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Staggered.speed_min = 0; + Staggered.speed_max = 4; + Staggered.brightness_min = 0; + Staggered.brightness_max = 4; + Staggered.colors_min = 0; + Staggered.colors_max = 2; + Staggered.speed = 2; + Staggered.brightness = 4; + Staggered.color_mode = MODE_COLORS_MODE_SPECIFIC; + Staggered.colors.resize(2); + modes.push_back(Staggered); + + mode Tide; // TODO: Has merge + Tide.name = "Tide"; + Tide.value = UNIHUB_SLV2_LED_MODE_TIDE; + Tide.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Tide.speed_min = 0; + Tide.speed_max = 4; + Tide.brightness_min = 0; + Tide.brightness_max = 4; + Tide.colors_min = 0; + Tide.colors_max = 2; + Tide.speed = 2; + Tide.brightness = 4; + Tide.color_mode = MODE_COLORS_MODE_SPECIFIC; + Tide.colors.resize(2); + modes.push_back(Tide); + + mode Runway; //TODO: Has merge + Runway.name = "Runway"; + Runway.value = UNIHUB_SLV2_LED_MODE_RUNWAY; + Runway.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Runway.speed_min = 0; + Runway.speed_max = 4; + Runway.brightness_min = 0; + Runway.brightness_max = 4; + Runway.colors_min = 0; + Runway.colors_max = 2; + Runway.speed = 2; + Runway.brightness = 4; + Runway.color_mode = MODE_COLORS_MODE_SPECIFIC; + Runway.colors.resize(2); + modes.push_back(Runway); + + mode Mixing; //TODO: Has merge + Mixing.name = "Mixing"; + Mixing.value = UNIHUB_SLV2_LED_MODE_MIXING; + Mixing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Mixing.speed_min = 0; + Mixing.speed_max = 4; + Mixing.brightness_min = 0; + Mixing.brightness_max = 4; + Mixing.colors_min = 0; + Mixing.colors_max = 2; + Mixing.speed = 2; + Mixing.brightness = 4; + Mixing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Mixing.colors.resize(2); + modes.push_back(Mixing); + + mode Stack; + Stack.name = "Stack"; + Stack.value = UNIHUB_SLV2_LED_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Stack.speed_min = 0; + Stack.speed_max = 4; + Stack.brightness_min = 0; + Stack.brightness_max = 4; + Stack.colors_min = 0; + Stack.colors_max = 1; + Stack.speed = 2; + Stack.brightness = 4; + Stack.direction = UNIHUB_SLV2_LED_DIRECTION_LTR; + Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stack.colors.resize(1); + modes.push_back(Stack); + + mode StackMultiColor; //TODO: Has merge + Stack.name = "Stack Multi Color"; + Stack.value = UNIHUB_SLV2_LED_MODE_STACK_MULTI_COLOR; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Stack.speed_min = 0; + Stack.speed_max = 4; + Stack.brightness_min = 0; + Stack.brightness_max = 4; + Stack.speed = 2; + Stack.brightness = 4; + Stack.direction = UNIHUB_SLV2_LED_DIRECTION_LTR; + Stack.color_mode = MODE_COLORS_NONE; + modes.push_back(Stack); + + mode Neon; + Neon.name = "Neon"; + Neon.value = UNIHUB_SLV2_LED_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Neon.speed_min = 0; + Neon.speed_max = 4; + Neon.brightness_min = 0; + Neon.brightness_max = 4; + Neon.speed = 2; + Neon.brightness = 4; + Neon.color_mode = MODE_COLORS_NONE; + modes.push_back(Neon); + + mode ColorCycle; + ColorCycle.name = "ColorCycle"; + ColorCycle.value = UNIHUB_SLV2_LED_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + ColorCycle.speed_min = 0; + ColorCycle.speed_max = 4; + ColorCycle.brightness_min = 0; + ColorCycle.brightness_max = 4; + ColorCycle.colors_min = 0; + ColorCycle.colors_max = 3; + ColorCycle.speed = 2; + ColorCycle.brightness = 4; + ColorCycle.direction = UNIHUB_SLV2_LED_DIRECTION_LTR; + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorCycle.colors.resize(3); + modes.push_back(ColorCycle); + + mode Meteor; //TODO: Has merge + Meteor.name = "Meteor"; + Meteor.value = UNIHUB_SLV2_LED_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Meteor.speed_min = 0; + Meteor.speed_max = 4; + Meteor.brightness_min = 0; + Meteor.brightness_max = 4; + Meteor.colors_min = 0; + Meteor.colors_max = 2; + Meteor.speed = 2; + Meteor.brightness = 4; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors.resize(2); + modes.push_back(Meteor); + + mode Voice; + Voice.name = "Voice"; + Voice.value = UNIHUB_SLV2_LED_MODE_VOICE; + Voice.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Voice.speed_min = 0; + Voice.speed_max = 4; + Voice.brightness_min = 0; + Voice.brightness_max = 4; + Voice.speed = 2; + Voice.brightness = 4; + modes.push_back(Voice); + + mode Groove; + Groove.name = "Groove"; + Groove.value = UNIHUB_SLV2_LED_MODE_GROOVE; + Groove.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Groove.speed_min = 0; + Groove.speed_max = 4; + Groove.brightness_min = 0; + Groove.brightness_max = 4; + Groove.colors_min = 0; + Groove.colors_max = 1; + Groove.speed = 2; + Groove.brightness = 4; + Groove.direction = UNIHUB_SLV2_LED_DIRECTION_LTR; + Groove.color_mode = MODE_COLORS_MODE_SPECIFIC; + Groove.colors.resize(1); + modes.push_back(Groove); + + mode Render; + Render.name = "Render"; + Render.value = UNIHUB_SLV2_LED_MODE_RENDER; + Render.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Render.speed_min = 0; + Render.speed_max = 4; + Render.brightness_min = 0; + Render.brightness_max = 4; + Render.colors_min = 0; + Render.colors_max = 4; + Render.speed = 2; + Render.brightness = 4; + Render.direction = UNIHUB_SLV2_LED_DIRECTION_LTR; + Render.color_mode = MODE_COLORS_MODE_SPECIFIC; + Render.colors.resize(4); + modes.push_back(Render); + + mode Tunnel; + Tunnel.name = "Tunnel"; + Tunnel.value = UNIHUB_SLV2_LED_MODE_TUNNEL; + Tunnel.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Tunnel.speed_min = 0; + Tunnel.speed_max = 4; + Tunnel.brightness_min = 0; + Tunnel.brightness_max = 4; + Tunnel.colors_min = 0; + Tunnel.colors_max = 4; + Tunnel.speed = 2; + Tunnel.brightness = 4; + Tunnel.direction = UNIHUB_SLV2_LED_DIRECTION_LTR; + Tunnel.color_mode = MODE_COLORS_MODE_SPECIFIC; + Tunnel.colors.resize(4); + modes.push_back(Tunnel); + + RGBController_LianLiUniHubSLV2::SetupZones(); +} + +RGBController_LianLiUniHubSLV2::~RGBController_LianLiUniHubSLV2() +{ + delete controller; +} + +void RGBController_LianLiUniHubSLV2::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + zones.resize(UNIHUB_SLV2_CHANNEL_COUNT); + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for(unsigned int channel_idx = 0; channel_idx < zones.size(); channel_idx++) + { + zones[channel_idx].name = "Channel "; + zones[channel_idx].name.append(std::to_string(channel_idx + 1)); + + zones[channel_idx].type = ZONE_TYPE_LINEAR; + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = UNIHUB_SLV2_CHAN_LED_COUNT; + + if(first_run) + { + zones[channel_idx].leds_count = zones[channel_idx].leds_min; + } + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + led new_led; + new_led.name = zones[channel_idx].name; + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_ch_idx + 1)); + new_led.value = channel_idx; + + leds.push_back(new_led); + } + + } + + SetupColors(); +} + +void RGBController_LianLiUniHubSLV2::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_LianLiUniHubSLV2::DeviceUpdateLEDs() +{ + + if(!initializedMode) + { + DeviceUpdateMode(); + } + + float brightness_scale = static_cast(modes[active_mode].brightness)/modes[active_mode].brightness_max; + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count, brightness_scale); + } +} + +void RGBController_LianLiUniHubSLV2::UpdateZoneLEDs(int zone) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + + float brightness_scale = static_cast(modes[active_mode].brightness)/modes[active_mode].brightness_max; + + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count, brightness_scale); +} + +void RGBController_LianLiUniHubSLV2::UpdateSingleLED(int /* led */) +{ + DeviceUpdateMode(); + +} + +void RGBController_LianLiUniHubSLV2::DeviceUpdateMode() +{ + if(!active_mode) + { + return; // Do nothing, custom mode should go through DeviceUpdateLEDs() to avoid flooding controller + } + + initializedMode = true; + + int fan_idx = 0; + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].leds_count == 0) + { + return; // Do nothing, channel isn't in use + } + fan_idx = ((zones[zone_idx].leds_count / 16) - 1); // Indexes start at 0 + + controller->SetChannelMode((unsigned char)zone_idx, + modes[active_mode], + fan_idx); + + } +} diff --git a/Controllers/LianLiController/LianLiUniHubSLV2Controller/RGBController_LianLiUniHubSLV2.h b/Controllers/LianLiController/LianLiUniHubSLV2Controller/RGBController_LianLiUniHubSLV2.h new file mode 100644 index 0000000..4d82022 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHubSLV2Controller/RGBController_LianLiUniHubSLV2.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHubSLV2.h | +| | +| RGBController for Lian Li SLV2 Uni Hub | +| | +| Will Kennedy 17 Jan 2023 | +| Oliver P 26 Apr 2022 | +| Credit to Luca Lovisa for original work. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "LianLiUniHubSLV2Controller.h" +#include "RGBController.h" + +class RGBController_LianLiUniHubSLV2 : public RGBController +{ +public: + RGBController_LianLiUniHubSLV2(LianLiUniHubSLV2Controller* controller_ptr); + ~RGBController_LianLiUniHubSLV2(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LianLiUniHubSLV2Controller* controller; + bool initializedMode; +}; diff --git a/Controllers/LianLiController/LianLiUniHub_AL10Controller/LianLiUniHub_AL10Controller.cpp b/Controllers/LianLiController/LianLiUniHub_AL10Controller/LianLiUniHub_AL10Controller.cpp new file mode 100644 index 0000000..368f6a9 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHub_AL10Controller/LianLiUniHub_AL10Controller.cpp @@ -0,0 +1,789 @@ +/*---------------------------------------------------------*\ +| LianLiUniHub_AL10Controller.cpp | +| | +| Driver for Lian Li AL10 Uni Hub | +| | +| Oliver P 05 May 2022 | +| Credit to Luca Lovisa for original work | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LianLiUniHub_AL10Controller.h" + +using namespace std::chrono_literals; + +/*----------------------------------------------------------------------------*\ +| The Uni Hub is controlled by sending control transfers to various wIndex | +| addresses, allthough it announces some kind of hid interface. Hence it | +| requires libusb as hidapi provides no wIndex customization. | +\*----------------------------------------------------------------------------*/ + +LianLiUniHub_AL10Controller::LianLiUniHub_AL10Controller + ( + libusb_device* device, + libusb_device_descriptor* descriptor + ) +{ + int ret; + + /*--------------------------------------------------------------------*\ + | Open the libusb device. | + \*--------------------------------------------------------------------*/ + ret = libusb_open(device, &handle); + + if(ret < 0) + { + return; + } + + /*--------------------------------------------------------------------*\ + | Fill in the location string from USB port numbers. | + \*--------------------------------------------------------------------*/ + uint8_t ports[7]; + + ret = libusb_get_port_numbers(device, ports, sizeof(ports)); + + if(ret > 0) + { + location = "USB: "; + + for (int i = 0; i < ret; i ++) + { + location += std::to_string(ports[i]); + location.push_back(':'); + } + + location.pop_back(); + } + + /*--------------------------------------------------------------------*\ + | Fill in the serial string from the string descriptor | + \*--------------------------------------------------------------------*/ + char serialStr[64]; + + ret = libusb_get_string_descriptor_ascii(handle, descriptor->iSerialNumber, reinterpret_cast(serialStr), sizeof(serialStr)); + + if(ret > 0) + { + serial = std::string(serialStr, ret); + } + + /*--------------------------------------------------------------------*\ + | Fill in the version string by reading version from device. | + \*--------------------------------------------------------------------*/ + version = ReadVersion(); + + /*--------------------------------------------------------------------*\ + | Create channels with their static configuration and "sane" defaults. | + \*--------------------------------------------------------------------*/ + Channel channel1; + channel1.index = 0; + channel1.anyFanCountOffset = UNIHUB_AL10_ANY_C1_FAN_COUNT_OFFSET; + channel1.anyFanCount = UNIHUB_AL10_ANY_FAN_COUNT_001; + channel1.ledActionAddress = UNIHUB_AL10_LED_C1_ACTION_ADDRESS; + channel1.ledCommitAddress = UNIHUB_AL10_LED_C1_COMMIT_ADDRESS; + channel1.ledModeAddress = UNIHUB_AL10_LED_C1_MODE_ADDRESS; + channel1.ledSpeedAddress = UNIHUB_AL10_LED_C1_SPEED_ADDRESS; + channel1.ledDirectionAddress = UNIHUB_AL10_LED_C1_DIRECTION_ADDRESS; + channel1.ledBrightnessAddress = UNIHUB_AL10_LED_C1_BRIGHTNESS_ADDRESS; + channel1.ledMode = UNIHUB_AL10_LED_MODE_RAINBOW; + channel1.ledSpeed = UNIHUB_AL10_LED_SPEED_100; + channel1.ledDirection = UNIHUB_AL10_LED_DIRECTION_LTR; + channel1.ledBrightness = UNIHUB_AL10_LED_BRIGHTNESS_100; + channel1.fanHubActionAddress = UNIHUB_AL10_FAN_C1_HUB_ACTION_ADDRESS; + channel1.fanHubCommitAddress = UNIHUB_AL10_FAN_C1_HUB_COMMIT_ADDRESS; + channel1.fanPwmActionAddress = UNIHUB_AL10_FAN_C1_PWM_ACTION_ADDRESS; + channel1.fanPwmCommitAddress = UNIHUB_AL10_FAN_C1_PWM_COMMIT_ADDRESS; + channel1.fanRpmActionAddress = UNIHUB_AL10_FAN_C1_RPM_ACTION_ADDRESS; + channel1.fanSpeed = UNIHUB_AL10_FAN_SPEED_QUIET; + channels[0] = channel1; + + Channel channel2; + channel2.index = 1; + channel2.anyFanCountOffset = UNIHUB_AL10_ANY_C2_FAN_COUNT_OFFSET; + channel2.anyFanCount = UNIHUB_AL10_ANY_FAN_COUNT_001; + channel2.ledActionAddress = UNIHUB_AL10_LED_C2_ACTION_ADDRESS; + channel2.ledCommitAddress = UNIHUB_AL10_LED_C2_COMMIT_ADDRESS; + channel2.ledModeAddress = UNIHUB_AL10_LED_C2_MODE_ADDRESS; + channel2.ledSpeedAddress = UNIHUB_AL10_LED_C2_SPEED_ADDRESS; + channel2.ledDirectionAddress = UNIHUB_AL10_LED_C2_DIRECTION_ADDRESS; + channel2.ledBrightnessAddress = UNIHUB_AL10_LED_C2_BRIGHTNESS_ADDRESS; + channel2.ledMode = UNIHUB_AL10_LED_MODE_RAINBOW; + channel2.ledSpeed = UNIHUB_AL10_LED_SPEED_100; + channel2.ledDirection = UNIHUB_AL10_LED_DIRECTION_LTR; + channel2.ledBrightness = UNIHUB_AL10_LED_BRIGHTNESS_100; + channel2.fanHubActionAddress = UNIHUB_AL10_FAN_C2_HUB_ACTION_ADDRESS; + channel2.fanHubCommitAddress = UNIHUB_AL10_FAN_C2_HUB_COMMIT_ADDRESS; + channel2.fanPwmActionAddress = UNIHUB_AL10_FAN_C2_PWM_ACTION_ADDRESS; + channel2.fanPwmCommitAddress = UNIHUB_AL10_FAN_C2_PWM_COMMIT_ADDRESS; + channel2.fanRpmActionAddress = UNIHUB_AL10_FAN_C2_RPM_ACTION_ADDRESS; + channel2.fanSpeed = UNIHUB_AL10_FAN_SPEED_QUIET; + channels[1] = channel2; + + Channel channel3; + channel3.index = 2; + channel3.anyFanCountOffset = UNIHUB_AL10_ANY_C3_FAN_COUNT_OFFSET; + channel3.anyFanCount = UNIHUB_AL10_ANY_FAN_COUNT_001; + channel3.ledActionAddress = UNIHUB_AL10_LED_C3_ACTION_ADDRESS; + channel3.ledCommitAddress = UNIHUB_AL10_LED_C3_COMMIT_ADDRESS; + channel3.ledModeAddress = UNIHUB_AL10_LED_C3_MODE_ADDRESS; + channel3.ledSpeedAddress = UNIHUB_AL10_LED_C3_SPEED_ADDRESS; + channel3.ledDirectionAddress = UNIHUB_AL10_LED_C3_DIRECTION_ADDRESS; + channel3.ledBrightnessAddress = UNIHUB_AL10_LED_C3_BRIGHTNESS_ADDRESS; + channel3.ledMode = UNIHUB_AL10_LED_MODE_RAINBOW; + channel3.ledSpeed = UNIHUB_AL10_LED_SPEED_100; + channel3.ledDirection = UNIHUB_AL10_LED_DIRECTION_LTR; + channel3.ledBrightness = UNIHUB_AL10_LED_BRIGHTNESS_100; + channel3.fanHubActionAddress = UNIHUB_AL10_FAN_C3_HUB_ACTION_ADDRESS; + channel3.fanHubCommitAddress = UNIHUB_AL10_FAN_C3_HUB_COMMIT_ADDRESS; + channel3.fanPwmActionAddress = UNIHUB_AL10_FAN_C3_PWM_ACTION_ADDRESS; + channel3.fanPwmCommitAddress = UNIHUB_AL10_FAN_C3_PWM_COMMIT_ADDRESS; + channel3.fanRpmActionAddress = UNIHUB_AL10_FAN_C3_RPM_ACTION_ADDRESS; + channel3.fanSpeed = UNIHUB_AL10_FAN_SPEED_QUIET; + channels[2] = channel3; + + Channel channel4; + channel4.index = 3; + channel4.anyFanCountOffset = UNIHUB_AL10_ANY_C4_FAN_COUNT_OFFSET; + channel4.anyFanCount = UNIHUB_AL10_ANY_FAN_COUNT_001; + channel4.ledActionAddress = UNIHUB_AL10_LED_C4_ACTION_ADDRESS; + channel4.ledCommitAddress = UNIHUB_AL10_LED_C4_COMMIT_ADDRESS; + channel4.ledModeAddress = UNIHUB_AL10_LED_C4_MODE_ADDRESS; + channel4.ledSpeedAddress = UNIHUB_AL10_LED_C4_SPEED_ADDRESS; + channel4.ledDirectionAddress = UNIHUB_AL10_LED_C4_DIRECTION_ADDRESS; + channel4.ledBrightnessAddress = UNIHUB_AL10_LED_C4_BRIGHTNESS_ADDRESS; + channel4.ledMode = UNIHUB_AL10_LED_MODE_RAINBOW; + channel4.ledSpeed = UNIHUB_AL10_LED_SPEED_100; + channel4.ledDirection = UNIHUB_AL10_LED_DIRECTION_LTR; + channel4.ledBrightness = UNIHUB_AL10_LED_BRIGHTNESS_100; + channel4.fanHubActionAddress = UNIHUB_AL10_FAN_C4_HUB_ACTION_ADDRESS; + channel4.fanHubCommitAddress = UNIHUB_AL10_FAN_C4_HUB_COMMIT_ADDRESS; + channel4.fanPwmActionAddress = UNIHUB_AL10_FAN_C4_PWM_ACTION_ADDRESS; + channel4.fanPwmCommitAddress = UNIHUB_AL10_FAN_C4_PWM_COMMIT_ADDRESS; + channel4.fanRpmActionAddress = UNIHUB_AL10_FAN_C4_RPM_ACTION_ADDRESS; + channel4.fanSpeed = UNIHUB_AL10_FAN_SPEED_QUIET; + channels[3] = channel4; +} + +LianLiUniHub_AL10Controller::~LianLiUniHub_AL10Controller() +{ + CloseLibusb(); +} + +std::string LianLiUniHub_AL10Controller::GetVersion() +{ + return version; +} + +std::string LianLiUniHub_AL10Controller::GetLocation() +{ + return location; +} + +std::string LianLiUniHub_AL10Controller::GetSerial() +{ + return serial; +} + +void LianLiUniHub_AL10Controller::SetAnyFanCount(size_t channel, uint8_t count) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_AL10_CHANNEL_COUNT) + { + return; + } + + channels[channel].anyFanCount = count; +} + +void LianLiUniHub_AL10Controller::SetLedColors(size_t channel, RGBColor* colors, size_t count) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_AL10_CHANNEL_COUNT) + { + return; + } + + /*-------------------------------------*\ + | Check for invalid count | + \*-------------------------------------*/ + if(count > UNIHUB_AL10_CHANLED_COUNT) + { + count = UNIHUB_AL10_CHANLED_COUNT; + } + + /*-------------------------------------*\ + | Check for mode colors | + \*-------------------------------------*/ + size_t i = 0; + switch(channels[channel].ledMode) + { + case UNIHUB_AL10_LED_MODE_RAINBOW: + channels[channel].colors[0].r = 0xFC; + channels[channel].colors[0].b = 0xFC; + channels[channel].colors[0].g = 0xFC; + i = 1; + break; + case UNIHUB_AL10_LED_MODE_BREATHING: + for(unsigned int color_idx = 0; color_idx < count; color_idx++) + { + for(unsigned int led_idx = 0; led_idx < 20; led_idx++) + { + channels[channel].colors[led_idx + (color_idx * 20)].r = RGBGetRValue(colors[color_idx]); + channels[channel].colors[led_idx + (color_idx * 20)].b = RGBGetBValue(colors[color_idx]); + channels[channel].colors[led_idx + (color_idx * 20)].g = RGBGetGValue(colors[color_idx]); + i++; + } + } + break; + case UNIHUB_AL10_LED_MODE_SCAN: + for(; i < 20; i++) + { + channels[channel].colors[i].r = 0x00; + channels[channel].colors[i].b = 0x00; + channels[channel].colors[i].g = 0x00; + } + + channels[channel].colors[0x00].r = RGBGetRValue(colors[0]); + channels[channel].colors[0x00].b = RGBGetBValue(colors[0]); + channels[channel].colors[0x00].g = RGBGetGValue(colors[0]); + + channels[channel].colors[0x08].r = RGBGetRValue(colors[1]); + channels[channel].colors[0x08].b = RGBGetBValue(colors[1]); + channels[channel].colors[0x08].g = RGBGetGValue(colors[1]); + + break; + default: + for(; i < count; i++) + { + channels[channel].colors[i].r = (RGBGetRValue(colors[i])); + channels[channel].colors[i].b = (RGBGetBValue(colors[i])); + channels[channel].colors[i].g = (RGBGetGValue(colors[i])); + + // Limiter to protect LEDs + if(UNIHUB_AL10_LED_LIMITER && (channels[channel].colors[i].r > 153) && (channels[channel].colors[i].r == channels[channel].colors[i].b) && (channels[channel].colors[i].r == channels[channel].colors[i].g)) + { + channels[channel].colors[i].r = 153; + channels[channel].colors[i].b = 153; + channels[channel].colors[i].g = 153; + } + } + } + /* Set all remaining leds to black */ + for(; i < UNIHUB_AL10_CHANLED_COUNT; i++) + { + channels[channel].colors[i].r = 0x00; + channels[channel].colors[i].b = 0x00; + channels[channel].colors[i].g = 0x00; + } + + + +} + +void LianLiUniHub_AL10Controller::SetLedMode(size_t channel, uint8_t mode) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_AL10_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledMode = mode; +} + +void LianLiUniHub_AL10Controller::SetLedSpeed(size_t channel, uint8_t speed) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_AL10_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledSpeed = speed; +} + +void LianLiUniHub_AL10Controller::SetLedDirection(size_t channel, uint8_t direction) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_AL10_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledDirection = direction; +} + +void LianLiUniHub_AL10Controller::SetLedBrightness(size_t channel, uint8_t brightness) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_AL10_CHANNEL_COUNT) + { + return; + } + + channels[channel].ledBrightness = brightness; +} + +uint16_t LianLiUniHub_AL10Controller::GetFanSpeed(size_t channel) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_AL10_CHANNEL_COUNT) + { + return 0; + } + + return channels[channel].fanSpeed; +} + +void LianLiUniHub_AL10Controller::SetFanSpeed(size_t channel, uint16_t speed) +{ + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel >= UNIHUB_AL10_CHANNEL_COUNT) + { + return; + } + + channels[channel].fanSpeed = speed; +} + +void LianLiUniHub_AL10Controller::EnableRgbhMode() +{ + rgbhModeEnabled = true; +} + +void LianLiUniHub_AL10Controller::DisableRgbhMode() +{ + rgbhModeEnabled = false; +} + +void LianLiUniHub_AL10Controller::EnableSyncMode() +{ + syncModeEnabled = true; +} + +void LianLiUniHub_AL10Controller::DisableSyncMode() +{ + syncModeEnabled = false; +} + +/*----------------------------------------------------------------------------*\ +| The Uni Hub is a PWM and LED controller designed specifically for the Lian | +| Li Uni Fans. It can control them by itself using the built-in effect engine | +| can also be connected to the mainboard via 4-pin PWM and 3-pin RGB cables | +| and forward these signals. The protocol implementation below was build as | +| close a possible to the Lian Li L-Connect software. | +| | +| The commands to control the fan speeds and to switch between controller and | +| mainboard control is already included, but currently deactivated as OpenRGB | +| had no fan control module or controller specific configuration at the time | +| of writing. | +\*----------------------------------------------------------------------------*/ +void LianLiUniHub_AL10Controller::Synchronize() +{ + /*---------------------------------------------------------------------*\ + | Configure common settings. | + \*---------------------------------------------------------------------*/ + + /*---------------------------------------------------------------------*\ + | Still unsure about this. Probably some sort of configuration | + | initialization | + \*---------------------------------------------------------------------*/ + uint8_t config_initialization[16]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(config_initialization, 0x00, sizeof(config_initialization)); + + config_initialization[0x0F] = 0x43; // Control data + config_initialization[0x0F] = 0x01; // Ending data + + SendConfig(UNIHUB_AL10_ACTION_ADDRESS, config_initialization, sizeof(config_initialization)); + + for(const Channel& channel : channels) + { + /*-------------------------------------*\ + | The Uni Hub doesn't know zero fans | + \*-------------------------------------*/ + uint8_t anyFanCount = channel.anyFanCount; + + if(anyFanCount == UNIHUB_AL10_ANY_FAN_COUNT_000) + { + anyFanCount = UNIHUB_AL10_ANY_FAN_COUNT_001; + } + + /*-------------------------------------*\ + | Configure the physical fan count | + \*-------------------------------------*/ + uint8_t config_fan_count[16]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(config_fan_count, 0x00, sizeof(config_fan_count)); + + config_fan_count[0x01] = 0x40; // Control data + config_fan_count[0x02] = channel.index + 1; // Channel + config_fan_count[0x03] = anyFanCount + 1; // Number of fans + config_fan_count[0x0F] = 0x01; // Ending data + + SendConfig(UNIHUB_AL10_ACTION_ADDRESS, config_fan_count, sizeof(config_fan_count)); + //SendCommit(UNIHUB_AL10_COMMIT_ADDRESS); + } + + /*--------------------------------------------------------------------*\ + | Configure channels for sync effects | + \*--------------------------------------------------------------------*/ + + /* + if(syncModeEnabled) + { + uint8_t config_sync[6]; + uint8_t config_sync_index = 0; + + config_sync[config_sync_index++] = 0x33; + + for(const Channel& channel : channels) + { + if(channel.anyFanCount != UNIHUB_AL10_ANY_FAN_COUNT_000) + { + config_sync[config_sync_index++] = channel.index; + } + } + + config_sync[config_sync_index++] = 0x08; + + SendConfig(UNIHUB_AL10_ACTION_ADDRESS, config_sync, config_sync_index); + SendCommit(UNIHUB_AL10_COMMIT_ADDRESS); + } + + */ + + /*--------------------------------------------------------------------*\ + | Configure led settings. | + \*--------------------------------------------------------------------*/ + for(const Channel& channel : channels) + { + if(channel.anyFanCount != UNIHUB_AL10_ANY_FAN_COUNT_000) + { + for(unsigned int fan_idx = 0; fan_idx <= UNIHUB_AL10_ANY_FAN_COUNT_004; fan_idx++) + { + /*-----------------------------*\ + | Configure fan colors | + \*-----------------------------*/ + uint8_t fan_config_colors[24]; + memcpy(fan_config_colors, channel.colors + (fan_idx * 20), sizeof(fan_config_colors)); + + SendConfig(channel.ledActionAddress + (60 * fan_idx), fan_config_colors, sizeof(fan_config_colors)); + } + + /*-----------------------------*\ + | Configure led mode | + \*-----------------------------*/ + uint8_t led_config[16]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(led_config, 0x00, sizeof(led_config)); + + led_config[0x01] = channel.ledMode; // Effect + led_config[0x02] = channel.ledSpeed; // Speed + led_config[0x03] = channel.ledDirection; // Direction + led_config[0x09] = channel.ledBrightness; // Brightness? + led_config[0x0F] = 0x01; // Ending data + + SendConfig(UNIHUB_AL10_ACTION_ADDRESS + (channel.index * 32 ), led_config, sizeof(led_config)); + + for(unsigned int fan_idx = 0; fan_idx <= UNIHUB_AL10_ANY_FAN_COUNT_004; fan_idx++) + { + /*-----------------------------*\ + | Configure rim colors | + \*-----------------------------*/ + uint8_t rim_config_colors[36]; + memcpy(rim_config_colors, channel.colors + (fan_idx * 20) + 8, sizeof(rim_config_colors)); + + SendConfig(channel.ledActionAddress + 24 + (60 * fan_idx), rim_config_colors, sizeof(rim_config_colors)); + } + + SendConfig(UNIHUB_AL10_ACTION_ADDRESS + 16 + (channel.index * 32 ), led_config, sizeof(led_config)); + } + /*-----------------------------------------------------------------*\ + | The Uni Hub doesn't know zero fans so we set them to black | + \*-----------------------------------------------------------------*/ + else + { + for(unsigned int fan_idx = 0; fan_idx <= UNIHUB_AL10_ANY_FAN_COUNT_004; fan_idx++) + { + /*-----------------------------*\ + | Configure fan colors | + \*-----------------------------*/ + uint8_t fan_config_colors[24]; + memset(fan_config_colors, 0x00, sizeof(fan_config_colors)); + + SendConfig(channel.ledActionAddress + (60 * fan_idx), fan_config_colors, sizeof(fan_config_colors)); + } + + /*-----------------------------*\ + | Configure led mode | + \*-----------------------------*/ + uint8_t led_config[16]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(led_config, 0x00, sizeof(led_config)); + + led_config[0x01] = UNIHUB_AL10_LED_MODE_STATIC_COLOR; // Effect + led_config[0x02] = channel.ledSpeed; // Speed? + led_config[0x03] = channel.ledDirection; // direction + led_config[0x0F] = 0x01; // Ending data + + SendConfig(UNIHUB_AL10_ACTION_ADDRESS + (channel.index * 32 ), led_config, sizeof(led_config)); + + for(unsigned int fan_idx = 0; fan_idx <= UNIHUB_AL10_ANY_FAN_COUNT_004; fan_idx++) + { + /*-----------------------------*\ + | Configure rim colors | + \*-----------------------------*/ + uint8_t rim_config_colors[36]; + memset(rim_config_colors, 0x00, sizeof(rim_config_colors)); + + SendConfig(channel.ledActionAddress + 24 + (60 * fan_idx), rim_config_colors, sizeof(rim_config_colors)); + } + + SendConfig(UNIHUB_AL10_ACTION_ADDRESS + 16 + (channel.index * 32 ), led_config, sizeof(led_config)); + } + } + + /*--------------------------------------------------------------------*\ + | Configure fan settings. Comment out until enabling fan control | + \*--------------------------------------------------------------------*/ +// uint8_t control = 0; + + /*-------------------------------------*\ + | Configure fan settings | + \*-------------------------------------*/ +// for(const Channel& channel : channels) +// { +// if(channel.fanSpeed == UNIHUB_AL10_FAN_SPEED_PWM) +// { + /*-----------------------------*\ + | Configure the fan to pwm | + | control | + \*-----------------------------*/ +// uint8_t config_pwm[1] = { 0x00 }; + +// control |= (0x01 << channel.index); + +// SendConfig(channel.fanPwmActionAddress, config_pwm, sizeof(config_pwm)); +// SendCommit(channel.fanPwmCommitAddress); +// } +// else +// { + /*-----------------------------*\ + | Configure the fan to hub | + | control and set speed | + \*-----------------------------*/ +// uint8_t config_hub[2] = { (uint8_t)(channel.fanSpeed >> 0x08), (uint8_t)(channel.fanSpeed & 0xFF) }; + +// SendConfig(channel.fanHubActionAddress, config_hub, sizeof(config_hub)); +// SendCommit(channel.fanHubCommitAddress); +// } +// } + + /*-------------------------------------*\ + | Configure fan control modes | + \*-------------------------------------*/ +// uint8_t config_fan_mode[2] = { 0x31, (uint8_t)(0xF0 | control) }; + +// SendConfig(UNIHUB_AL10_ACTION_ADDRESS, config_fan_mode, sizeof(config_fan_mode)); +// SendCommit(UNIHUB_AL10_COMMIT_ADDRESS); + + /*--------------------------------------------------------------------*\ + | Configure led settings. | + \*--------------------------------------------------------------------*/ +// if(rgbhModeEnabled) +// { + /*-------------------------------------*\ + | Configure the leds to hdr control. | + \*-------------------------------------*/ +// uint8_t config_hdr[2] = { 0x30, 0x01 }; + +// SendConfig(UNIHUB_AL10_ACTION_ADDRESS, config_hdr, sizeof(config_hdr)); +// SendCommit(UNIHUB_AL10_COMMIT_ADDRESS); +// } +// else +// { + /*-------------------------------------*\ + | Configure the leds to hub control | + \*-------------------------------------*/ +// uint8_t config_hub[2] = { 0x30, 0x00 }; + +// SendConfig(UNIHUB_AL10_ACTION_ADDRESS, config_hub, sizeof(config_hub)); +// SendCommit(UNIHUB_AL10_COMMIT_ADDRESS); +// } +} + +uint16_t LianLiUniHub_AL10Controller::ReadFanSpeed(size_t channel) +{ + /*-------------------------------------*\ + | Check for invalid handle | + \*-------------------------------------*/ + if(handle == nullptr) + { + return(0); + } + + /*-------------------------------------*\ + | Check for invalid channel | + \*-------------------------------------*/ + if(channel > UNIHUB_AL10_CHANNEL_COUNT) + { + return(0); + } + + uint8_t buffer[2]; + uint8_t length = sizeof(buffer); + + uint16_t wIndex = channels[channel].fanRpmActionAddress; + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + size_t ret = libusb_control_transfer(handle, /* dev_handle */ + 0xC0, /* bmRequestType */ + 0x81, /* bRequest */ + 0x00, /* wValue */ + wIndex, /* wIndex */ + buffer, /* data */ + length, /* wLength */ + 1000); /* timeout */ + + /*-------------------------------------*\ + | Check for communication error | + \*-------------------------------------*/ + if(ret != length) + { + return(0); + } + + return(*(uint16_t*)buffer); +} + +void LianLiUniHub_AL10Controller::CloseLibusb() +{ + if (handle != nullptr) + { + libusb_close(handle); + handle = nullptr; + } +} + +std::string LianLiUniHub_AL10Controller::ReadVersion() +{ + /*-------------------------------------*\ + | Check for invalid handle | + \*-------------------------------------*/ + if(handle == nullptr) + { + return(""); + } + + uint8_t buffer[5]; + uint8_t length = sizeof(buffer); + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + size_t ret = libusb_control_transfer(handle, /* dev_handle */ + 0xC0, /* bmRequestType */ + 0x81, /* bRequest */ + 0x00, /* wValue */ + 0xB500, /* wIndex */ + buffer, /* data */ + length, /* wLength */ + 1000); /* timeout */ + + /*-------------------------------------*\ + | Check for communication error | + \*-------------------------------------*/ + if(ret != length) + { + return(""); + } + + /*-------------------------------------*\ + | Format version string | + \*-------------------------------------*/ + char version[15]; + int vlength = std::snprintf(version, sizeof(version), "%x.%x.%x.%x.%x", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]); + + return(std::string(version, vlength)); +} + +void LianLiUniHub_AL10Controller::SendConfig(uint16_t wIndex, uint8_t *config, size_t length) +{ + /*-------------------------------------*\ + | Check for invalid handle | + \*-------------------------------------*/ + if(handle == nullptr) + { + return; + } + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + size_t ret = libusb_control_transfer(handle, /* dev_handle */ + 0x40, /* bmRequestType */ + 0x80, /* bRequest */ + 0x00, /* wValue */ + wIndex, /* wIndex */ + config, /* data */ + (uint16_t)length, /* wLength */ + 1000); /* timeout */ + std::this_thread::sleep_for(5ms); + /*-------------------------------------*\ + | Check for communication error | + \*-------------------------------------*/ + if(ret != length) + { + return; + } +} + +void LianLiUniHub_AL10Controller::SendCommit(uint16_t wIndex) +{ + /*-------------------------------------*\ + | Set up config packet | + \*-------------------------------------*/ + uint8_t config[1] = { 0x01 }; + + /*-------------------------------------*\ + | Send packet | + \*-------------------------------------*/ + SendConfig(wIndex, config, sizeof(config)); + + std::this_thread::sleep_for(5ms); +} diff --git a/Controllers/LianLiController/LianLiUniHub_AL10Controller/LianLiUniHub_AL10Controller.h b/Controllers/LianLiController/LianLiUniHub_AL10Controller/LianLiUniHub_AL10Controller.h new file mode 100644 index 0000000..d122af1 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHub_AL10Controller/LianLiUniHub_AL10Controller.h @@ -0,0 +1,273 @@ +/*---------------------------------------------------------*\ +| LianLiUniHub_AL10Controller.h | +| | +| Driver for Lian Li AL10 Uni Hub | +| | +| Oliver P 05 May 2022 | +| Credit to Luca Lovisa for original work | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBController.h" + +/*----------------------------------------------------------------------------*\ +| Global definitions. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_AL10_CHANNEL_COUNT = 0x04, /* Channel count */ + UNIHUB_AL10_CHANLED_COUNT = 0x50, /* Max-LED per channel count */ +}; + +enum +{ + UNIHUB_AL10_ACTION_ADDRESS = 0xE020, /* Global action address */ + UNIHUB_AL10_COMMIT_ADDRESS = 0xE02F, /* Global commit address */ +}; + +enum +{ + UNIHUB_AL10_ANY_C1_FAN_COUNT_OFFSET = 0x00, /* Channel 1 fan count offset */ + UNIHUB_AL10_ANY_C2_FAN_COUNT_OFFSET = 0x14, /* Channel 2 fan count offset */ + UNIHUB_AL10_ANY_C3_FAN_COUNT_OFFSET = 0x28, /* Channel 3 fan count offset */ + UNIHUB_AL10_ANY_C4_FAN_COUNT_OFFSET = 0x3C, /* Channel 4 fan count offset */ +}; + +enum +{ + UNIHUB_AL10_ANY_FAN_COUNT_000 = 0xFF, /* Fan count for 0 fans (dummy value) */ + UNIHUB_AL10_ANY_FAN_COUNT_001 = 0x00, /* Fan count for 1 fan */ + UNIHUB_AL10_ANY_FAN_COUNT_002 = 0x01, /* Fan count for 2 fans */ + UNIHUB_AL10_ANY_FAN_COUNT_003 = 0x02, /* Fan count for 3 fans */ + UNIHUB_AL10_ANY_FAN_COUNT_004 = 0x03, /* Fan count for 4 fans */ +}; + +enum +{ + UNIHUB_AL10_LED_LIMITER = 1, /* Limit the color white to 999999 as per manufacturer limits in v1.7 */ +}; + +/*----------------------------------------------------------------------------*\ +| Definitions related to led configuration. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_AL10_LED_C1_ACTION_ADDRESS = 0xE500, /* Channel 1 led action address */ + UNIHUB_AL10_LED_C1_COMMIT_ADDRESS = 0xE02F, /* Channel 1 led commit address */ + UNIHUB_AL10_LED_C1_MODE_ADDRESS = 0xE021, /* Channel 1 led mode address */ + UNIHUB_AL10_LED_C1_SPEED_ADDRESS = 0xE022, /* Channel 1 led speed address */ + UNIHUB_AL10_LED_C1_DIRECTION_ADDRESS = 0xE023, /* Channel 1 led direction address */ + UNIHUB_AL10_LED_C1_BRIGHTNESS_ADDRESS = 0xE029, /* Channel 1 led brightness address */ + + UNIHUB_AL10_LED_C2_ACTION_ADDRESS = 0xE5F0, /* Channel 2 led action address */ + UNIHUB_AL10_LED_C2_COMMIT_ADDRESS = 0xE03F, /* Channel 2 led commit address */ + UNIHUB_AL10_LED_C2_MODE_ADDRESS = 0xE031, /* Channel 2 led mode address */ + UNIHUB_AL10_LED_C2_SPEED_ADDRESS = 0xE032, /* Channel 2 led speed address */ + UNIHUB_AL10_LED_C2_DIRECTION_ADDRESS = 0xE033, /* Channel 2 led direction address */ + UNIHUB_AL10_LED_C2_BRIGHTNESS_ADDRESS = 0xE039, /* Channel 2 led brightness address */ + + UNIHUB_AL10_LED_C3_ACTION_ADDRESS = 0xE6E0, /* Channel 3 led action address */ + UNIHUB_AL10_LED_C3_COMMIT_ADDRESS = 0xE04F, /* Channel 3 led commit address */ + UNIHUB_AL10_LED_C3_MODE_ADDRESS = 0xE041, /* Channel 3 led mode address */ + UNIHUB_AL10_LED_C3_SPEED_ADDRESS = 0xE042, /* Channel 3 led speed address */ + UNIHUB_AL10_LED_C3_DIRECTION_ADDRESS = 0xE043, /* Channel 3 led direction address */ + UNIHUB_AL10_LED_C3_BRIGHTNESS_ADDRESS = 0xE049, /* Channel 3 led brightness address */ + + UNIHUB_AL10_LED_C4_ACTION_ADDRESS = 0xE7D0, /* Channel 4 led action address */ + UNIHUB_AL10_LED_C4_COMMIT_ADDRESS = 0xE05F, /* Channel 4 led commit address */ + UNIHUB_AL10_LED_C4_MODE_ADDRESS = 0xE051, /* Channel 4 led mode address */ + UNIHUB_AL10_LED_C4_SPEED_ADDRESS = 0xE052, /* Channel 4 led speed address */ + UNIHUB_AL10_LED_C4_DIRECTION_ADDRESS = 0xE053, /* Channel 4 led direction address */ + UNIHUB_AL10_LED_C4_BRIGHTNESS_ADDRESS = 0xE059, /* Channel 4 led brightness address */ +}; + +enum +{ + UNIHUB_AL10_LED_MODE_RAINBOW = 0x05, /* Rainbow mode */ + UNIHUB_AL10_LED_MODE_RAINBOW_MORPH = 0xFF, /* Runway mode - Needs updated code */ + UNIHUB_AL10_LED_MODE_STATIC_COLOR = 0x01, /* Static Color mode */ + UNIHUB_AL10_LED_MODE_BREATHING = 0x02, /* Breathing mode */ + UNIHUB_AL10_LED_MODE_TAICHI = 0x2C, /* Neon mode */ + UNIHUB_AL10_LED_MODE_COLOR_CYCLE = 0x2B, /* Color Cycle mode */ + UNIHUB_AL10_LED_MODE_RUNWAY = 0xFF, /* Runway mode - Needs updated code */ + UNIHUB_AL10_LED_MODE_METEOR = 0xFF, /* Meteor mode - Needs updated code */ + UNIHUB_AL10_LED_MODE_WARNING = 0x2D, /* Warning mode */ + UNIHUB_AL10_LED_MODE_VOICE = 0x22, /* Voice mode */ + UNIHUB_AL10_LED_MODE_SPINNING_TEACUP = 0x36, /* Spinning Teacup mode */ + UNIHUB_AL10_LED_MODE_TORNADO = 0x34, /* Tornado mode */ + UNIHUB_AL10_LED_MODE_MIXING = 0x23, /* Mixing mode */ + UNIHUB_AL10_LED_MODE_STACK = 0xFF, /* Stack mode - Needs updated code */ + UNIHUB_AL10_LED_MODE_STAGGGERED = 0x35, /* Stagggered mode */ + UNIHUB_AL10_LED_MODE_TIDE = 0x25, /* Tide mode */ + UNIHUB_AL10_LED_MODE_SCAN = 0x26, /* Scan mode */ + UNIHUB_AL10_LED_MODE_CONTEST = 0x33, /* Contest mode */ +}; + +enum +{ + UNIHUB_AL10_LED_SPEED_000 = 0x02, /* Very slow speed */ + UNIHUB_AL10_LED_SPEED_025 = 0x01, /* Rather slow speed */ + UNIHUB_AL10_LED_SPEED_050 = 0x00, /* Medium speed */ + UNIHUB_AL10_LED_SPEED_075 = 0xFF, /* Rather fast speed */ + UNIHUB_AL10_LED_SPEED_100 = 0xFE, /* Very fast speed */ +}; + +enum +{ + UNIHUB_AL10_LED_DIRECTION_LTR = 0x00, /* Left-to-Right direction */ + UNIHUB_AL10_LED_DIRECTION_RTL = 0x01, /* Right-to-Left direction */ +}; + +enum +{ + UNIHUB_AL10_LED_BRIGHTNESS_000 = 0x08, /* Very dark (off) */ + UNIHUB_AL10_LED_BRIGHTNESS_025 = 0x03, /* Rather dark */ + UNIHUB_AL10_LED_BRIGHTNESS_050 = 0x02, /* Medium bright */ + UNIHUB_AL10_LED_BRIGHTNESS_075 = 0x01, /* Rather bright */ + UNIHUB_AL10_LED_BRIGHTNESS_100 = 0x00, /* Very bright */ +}; + +/*----------------------------------------------------------------------------*\ +| Definitions related to fan configuration. | +\*----------------------------------------------------------------------------*/ + +enum +{ + UNIHUB_AL10_FAN_C1_HUB_ACTION_ADDRESS = 0xE8A0, /* Channel 1 fan action address for hub control */ + UNIHUB_AL10_FAN_C1_HUB_COMMIT_ADDRESS = 0xE890, /* Channel 1 fan commit address for hub control */ + UNIHUB_AL10_FAN_C1_PWM_ACTION_ADDRESS = 0xE890, /* Channel 1 fan action address for pwm control */ + UNIHUB_AL10_FAN_C1_PWM_COMMIT_ADDRESS = 0xE818, /* Channel 1 fan commit address for pwm control */ + UNIHUB_AL10_FAN_C1_RPM_ACTION_ADDRESS = 0xE800, /* Channel 1 fan pwm read address */ + + UNIHUB_AL10_FAN_C2_HUB_ACTION_ADDRESS = 0xE8A2, /* Channel 2 fan action address for hub control */ + UNIHUB_AL10_FAN_C2_HUB_COMMIT_ADDRESS = 0xE891, /* Channel 2 fan commit address for hub control */ + UNIHUB_AL10_FAN_C2_PWM_ACTION_ADDRESS = 0xE891, /* Channel 2 fan action address for pwm control */ + UNIHUB_AL10_FAN_C2_PWM_COMMIT_ADDRESS = 0xE81A, /* Channel 2 fan commit address for pwm control */ + UNIHUB_AL10_FAN_C2_RPM_ACTION_ADDRESS = 0xE802, /* Channel 1 fan pwm read address */ + + UNIHUB_AL10_FAN_C3_HUB_ACTION_ADDRESS = 0xE8A4, /* Channel 3 fan action address for hub control */ + UNIHUB_AL10_FAN_C3_HUB_COMMIT_ADDRESS = 0xE892, /* Channel 3 fan commit address for hub control */ + UNIHUB_AL10_FAN_C3_PWM_ACTION_ADDRESS = 0xE892, /* Channel 3 fan action address for pwm control */ + UNIHUB_AL10_FAN_C3_PWM_COMMIT_ADDRESS = 0xE81C, /* Channel 3 fan commit address for pwm control */ + UNIHUB_AL10_FAN_C3_RPM_ACTION_ADDRESS = 0xE804, /* Channel 1 fan pwm read address */ + + UNIHUB_AL10_FAN_C4_HUB_ACTION_ADDRESS = 0xE8A6, /* Channel 4 fan action address for hub control */ + UNIHUB_AL10_FAN_C4_HUB_COMMIT_ADDRESS = 0xE893, /* Channel 4 fan commit address for hub control */ + UNIHUB_AL10_FAN_C4_PWM_ACTION_ADDRESS = 0xE893, /* Channel 4 fan action address for pwm control */ + UNIHUB_AL10_FAN_C4_PWM_COMMIT_ADDRESS = 0xE81E, /* Channel 4 fan commit address for pwm control */ + UNIHUB_AL10_FAN_C4_RPM_ACTION_ADDRESS = 0xE806, /* Channel 1 fan pwm read address */ +}; + +enum +{ + UNIHUB_AL10_FAN_SPEED_QUIET = 0x2003, /* Rather slow */ + UNIHUB_AL10_FAN_SPEED_HIGH_SPEED = 0x2206, /* Rather fast */ + UNIHUB_AL10_FAN_SPEED_FULL_SPEED = 0x6C07, /* BRRRRRRRRRR */ + UNIHUB_AL10_FAN_SPEED_PWM = 0xFFFF, /* PWM Control */ +}; + +/*----------------------------------------------------------------------------*\ +| Uni Hub controller. | +\*----------------------------------------------------------------------------*/ + +class LianLiUniHub_AL10Controller +{ +private: + /* The Uni Hub requires colors in RBG order */ + struct Color + { + uint8_t r; + uint8_t b; + uint8_t g; + }; + + /* The values correspond to the definitions above */ + struct Channel + { + uint8_t index; + + uint8_t anyFanCountOffset; + uint8_t anyFanCount; + + uint16_t ledActionAddress; + uint16_t ledCommitAddress; + uint16_t ledModeAddress; + uint16_t ledSpeedAddress; + uint16_t ledDirectionAddress; + uint16_t ledBrightnessAddress; + + Color colors[UNIHUB_AL10_CHANLED_COUNT]; + + uint8_t ledMode; + uint8_t ledSpeed; + uint8_t ledDirection; + uint8_t ledBrightness; + + uint16_t fanHubActionAddress; + uint16_t fanHubCommitAddress; + + uint16_t fanPwmActionAddress; + uint16_t fanPwmCommitAddress; + uint16_t fanRpmActionAddress; + + uint16_t fanSpeed; + }; + +public: + LianLiUniHub_AL10Controller + ( + libusb_device* device, + libusb_device_descriptor* descriptor + ); + ~LianLiUniHub_AL10Controller(); + + std::string GetVersion(); + std::string GetLocation(); + std::string GetSerial(); + + void SetAnyFanCount(size_t channel, uint8_t count); + void SetLedColors(size_t channel, RGBColor* colors, size_t count); + void SetLedMode(size_t channel, uint8_t mode); + void SetLedSpeed(size_t channel, uint8_t speed); + void SetLedDirection(size_t channel, uint8_t direction); + void SetLedBrightness(size_t channel, uint8_t brightness); + uint16_t GetFanSpeed(size_t channel); + void SetFanSpeed(size_t channel, uint16_t speed); + void EnableRgbhMode(); + void DisableRgbhMode(); + void EnableSyncMode(); + void DisableSyncMode(); + uint16_t ReadFanSpeed(size_t channel); + + /*-----------------------------------------------------*\ + | Synchronize the current configuration to the Uni Hub. | + \*-----------------------------------------------------*/ + void Synchronize(); + +private: + libusb_device_handle* handle = nullptr; + + std::string version; + std::string location; + std::string serial; + + bool rgbhModeEnabled = false; + bool syncModeEnabled = false; + + Channel channels[UNIHUB_AL10_CHANNEL_COUNT]; + + void CloseLibusb(); + std::string ReadVersion(); + void SendConfig(uint16_t wIndex, uint8_t *config, size_t length); + void SendCommit(uint16_t wIndex); +}; diff --git a/Controllers/LianLiController/LianLiUniHub_AL10Controller/RGBController_LianLiUniHub_AL10.cpp b/Controllers/LianLiController/LianLiUniHub_AL10Controller/RGBController_LianLiUniHub_AL10.cpp new file mode 100644 index 0000000..dd87f3c --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHub_AL10Controller/RGBController_LianLiUniHub_AL10.cpp @@ -0,0 +1,661 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHub_AL10.cpp | +| | +| RGBController for Lian Li AL10 Uni Hub | +| | +| Oliver P 05 May 2022 | +| Credit to Luca Lovisa for original work | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_LianLiUniHub_AL10.h" + +/**------------------------------------------------------------------*\ + @name Lian Li Uni Hub + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectLianLiUniHub + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LianLiUniHub_AL10::RGBController_LianLiUniHub_AL10(LianLiUniHub_AL10Controller* controller_ptr) +{ + controller = controller_ptr; + + name = "Lian Li Uni Hub - AL"; + vendor = "Lian Li"; + version = controller->GetVersion(); + type = DEVICE_TYPE_COOLER; + description = "Lian Li Uni Hub - AL v1.0"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + + initializedMode = false; + + mode Custom; + Custom.name = "Custom"; + Custom.value = UNIHUB_AL10_LED_MODE_STATIC_COLOR; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = UNIHUB_AL10_LED_MODE_RAINBOW; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + RainbowWave.speed_min = 0; + RainbowWave.speed_max = 4; + RainbowWave.brightness_min = 0; + RainbowWave.brightness_max = 4; + RainbowWave.speed = 3; + RainbowWave.brightness = 3; + RainbowWave.direction = UNIHUB_AL10_LED_DIRECTION_LTR; + RainbowWave.color_mode = MODE_COLORS_NONE; + //RainbowWave.colors[0] = ToRGBColor(253,253,253); + modes.push_back(RainbowWave); + + /* Needs updated code + mode RainbowMorph; + RainbowMorph.name = "Rainbow Morph"; + RainbowMorph.value = UNIHUB_AL10_LED_MODE_RAINBOW_MORPH; + RainbowMorph.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + RainbowMorph.speed_min = 0; + RainbowMorph.speed_max = 4; + RainbowMorph.brightness_min = 0; + RainbowMorph.brightness_max = 4; + RainbowMorph.speed = 3; + RainbowMorph.brightness = 3; + RainbowMorph.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowMorph); + */ + + mode StaticColor; + StaticColor.name = "Static Color"; + StaticColor.value = UNIHUB_AL10_LED_MODE_STATIC_COLOR; + StaticColor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + StaticColor.brightness_min = 0; + StaticColor.brightness_max = 4; + StaticColor.colors_min = 0; + StaticColor.colors_max = 4; + StaticColor.brightness = 3; + StaticColor.color_mode = MODE_COLORS_MODE_SPECIFIC; + StaticColor.colors.resize(4); + modes.push_back(StaticColor); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = UNIHUB_AL10_LED_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.speed_min = 0; + Breathing.speed_max = 4; + Breathing.brightness_min = 0; + Breathing.brightness_max = 4; + Breathing.colors_min = 0; + Breathing.colors_max = 4; + Breathing.speed = 3; + Breathing.brightness = 3; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(4); + modes.push_back(Breathing); + + mode Taichi; + Taichi.name = "Taichi"; + Taichi.value = UNIHUB_AL10_LED_MODE_TAICHI; + Taichi.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Taichi.speed_min = 0; + Taichi.speed_max = 4; + Taichi.brightness_min = 0; + Taichi.brightness_max = 4; + Taichi.colors_min = 0; + Taichi.colors_max = 2; + Taichi.speed = 3; + Taichi.brightness = 3; + Taichi.direction = UNIHUB_AL10_LED_DIRECTION_LTR; + Taichi.color_mode = MODE_COLORS_MODE_SPECIFIC; + Taichi.colors.resize(2); + modes.push_back(Taichi); + + mode ColorCycle; + ColorCycle.name = "ColorCycle"; + ColorCycle.value = UNIHUB_AL10_LED_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + ColorCycle.speed_min = 0; + ColorCycle.speed_max = 4; + ColorCycle.brightness_min = 0; + ColorCycle.brightness_max = 4; + ColorCycle.colors_min = 0; + ColorCycle.colors_max = 4; + ColorCycle.speed = 3; + ColorCycle.brightness = 3; + ColorCycle.direction = UNIHUB_AL10_LED_DIRECTION_LTR; + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorCycle.colors.resize(4); + modes.push_back(ColorCycle); + + /* Needs updated code + mode Runway; + Runway.name = "Runway"; + Runway.value = UNIHUB_AL10_LED_MODE_RUNWAY; + Runway.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Runway.speed_min = 0; + Runway.speed_max = 4; + Runway.brightness_min = 0; + Runway.brightness_max = 4; + Runway.colors_min = 0; + Runway.colors_max = 2; + Runway.speed = 3; + Runway.brightness = 3; + Runway.color_mode = MODE_COLORS_MODE_SPECIFIC; + Runway.colors.resize(2); + modes.push_back(Runway); + */ + + /* Needs updated code + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = UNIHUB_AL10_LED_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Meteor.speed_min = 0; + Meteor.speed_max = 4; + Meteor.brightness_min = 0; + Meteor.brightness_max = 4; + Meteor.colors_min = 0; + Meteor.colors_max = 4; + Meteor.speed = 3; + Meteor.brightness = 3; + Meteor.direction = UNIHUB_AL10_LED_DIRECTION_LTR; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors.resize(4); + modes.push_back(Meteor); + */ + + mode Warning; + Warning.name = "Warning"; + Warning.value = UNIHUB_AL10_LED_MODE_WARNING; + Warning.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Warning.speed_min = 0; + Warning.speed_max = 4; + Warning.brightness_min = 0; + Warning.brightness_max = 4; + Warning.colors_min = 0; + Warning.colors_max = 4; + Warning.speed = 3; + Warning.brightness = 3; + Warning.color_mode = MODE_COLORS_MODE_SPECIFIC; + Warning.colors.resize(4); + modes.push_back(Warning); + + mode Voice; + Voice.name = "Voice"; + Voice.value = UNIHUB_AL10_LED_MODE_VOICE; + Voice.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Voice.speed_min = 0; + Voice.speed_max = 4; + Voice.brightness_min = 0; + Voice.brightness_max = 4; + Voice.colors_min = 0; + Voice.colors_max = 4; + Voice.speed = 3; + Voice.brightness = 3; + Voice.direction = UNIHUB_AL10_LED_DIRECTION_LTR; + Voice.color_mode = MODE_COLORS_MODE_SPECIFIC; + Voice.colors.resize(4); + modes.push_back(Voice); + + mode SpinningTeacup; + SpinningTeacup.name = "SpinningTeacup"; + SpinningTeacup.value = UNIHUB_AL10_LED_MODE_SPINNING_TEACUP; + SpinningTeacup.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + SpinningTeacup.speed_min = 0; + SpinningTeacup.speed_max = 4; + SpinningTeacup.brightness_min = 0; + SpinningTeacup.brightness_max = 4; + SpinningTeacup.colors_min = 0; + SpinningTeacup.colors_max = 4; + SpinningTeacup.speed = 3; + SpinningTeacup.brightness = 3; + SpinningTeacup.direction = UNIHUB_AL10_LED_DIRECTION_LTR; + SpinningTeacup.color_mode = MODE_COLORS_MODE_SPECIFIC; + SpinningTeacup.colors.resize(4); + modes.push_back(SpinningTeacup); + + mode Tornado; + Tornado.name = "Tornado"; + Tornado.value = UNIHUB_AL10_LED_MODE_TORNADO; + Tornado.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Tornado.speed_min = 0; + Tornado.speed_max = 4; + Tornado.brightness_min = 0; + Tornado.brightness_max = 4; + Tornado.colors_min = 0; + Tornado.colors_max = 4; + Tornado.speed = 3; + Tornado.brightness = 3; + Tornado.direction = UNIHUB_AL10_LED_DIRECTION_LTR; + Tornado.color_mode = MODE_COLORS_MODE_SPECIFIC; + Tornado.colors.resize(4); + modes.push_back(Tornado); + + mode Mixing; + Mixing.name = "Mixing"; + Mixing.value = UNIHUB_AL10_LED_MODE_MIXING; + Mixing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Mixing.speed_min = 0; + Mixing.speed_max = 4; + Mixing.brightness_min = 0; + Mixing.brightness_max = 4; + Mixing.colors_min = 0; + Mixing.colors_max = 2; + Mixing.speed = 3; + Mixing.brightness = 3; + Mixing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Mixing.colors.resize(2); + modes.push_back(Mixing); + + /* Needs updated code + mode Stack; + Stack.name = "Stack"; + Stack.value = UNIHUB_AL10_LED_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Stack.speed_min = 0; + Stack.speed_max = 4; + Stack.brightness_min = 0; + Stack.brightness_max = 4; + Stack.colors_min = 0; + Stack.colors_max = 2; + Stack.speed = 3; + Stack.brightness = 3; + Stack.direction = UNIHUB_AL10_LED_DIRECTION_LTR; + Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stack.colors.resize(2); + modes.push_back(Stack); + */ + + mode Staggered; + Staggered.name = "Staggered"; + Staggered.value = UNIHUB_AL10_LED_MODE_STAGGGERED; + Staggered.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Staggered.speed_min = 0; + Staggered.speed_max = 4; + Staggered.brightness_min = 0; + Staggered.brightness_max = 4; + Staggered.colors_min = 0; + Staggered.colors_max = 4; + Staggered.speed = 3; + Staggered.brightness = 3; + Staggered.color_mode = MODE_COLORS_MODE_SPECIFIC; + Staggered.colors.resize(4); + modes.push_back(Staggered); + + mode Tide; + Tide.name = "Tide"; + Tide.value = UNIHUB_AL10_LED_MODE_TIDE; + Tide.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Tide.speed_min = 0; + Tide.speed_max = 4; + Tide.brightness_min = 0; + Tide.brightness_max = 4; + Tide.colors_min = 0; + Tide.colors_max = 4; + Tide.speed = 3; + Tide.brightness = 3; + Tide.color_mode = MODE_COLORS_MODE_SPECIFIC; + Tide.colors.resize(4); + modes.push_back(Tide); + + mode Scan; + Scan.name = "Scan"; + Scan.value = UNIHUB_AL10_LED_MODE_SCAN; + Scan.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Scan.speed_min = 0; + Scan.speed_max = 4; + Scan.brightness_min = 0; + Scan.brightness_max = 4; + Scan.colors_min = 0; + Scan.colors_max = 2; + Scan.speed = 3; + Scan.brightness = 3; + Scan.color_mode = MODE_COLORS_MODE_SPECIFIC; + Scan.colors.resize(2); + modes.push_back(Scan); + + mode Contest; + Contest.name = "Contest"; + Contest.value = UNIHUB_AL10_LED_MODE_CONTEST; + Contest.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + Contest.speed_min = 0; + Contest.speed_max = 4; + Contest.brightness_min = 0; + Contest.brightness_max = 4; + Contest.colors_min = 0; + Contest.colors_max = 2; + Contest.speed = 3; + Contest.brightness = 3; + Contest.direction = UNIHUB_AL10_LED_DIRECTION_LTR; + Contest.color_mode = MODE_COLORS_MODE_SPECIFIC; + Contest.colors.resize(3); + modes.push_back(Contest); + + /* Motherboard header mode? Not implemented yet + mode Rgbh = makeModeAL(); + Rgbh.name = "RGB Header"; + Rgbh.value = UNIHUB_AL10_LED_MODE_STATIC_COLOR | 0x0200; + Rgbh.flags = 0; + Rgbh.color_mode = MODE_COLORS_NONE; + modes.push_back(Rgbh); + */ + + RGBController_LianLiUniHub_AL10::SetupZones(); +} + +void RGBController_LianLiUniHub_AL10::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(UNIHUB_AL10_CHANNEL_COUNT); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + int addressableCounter = 1; + for(unsigned int channel_idx = 0; channel_idx < zones.size(); channel_idx++) + { + zones[channel_idx].name = "Channel "; + zones[channel_idx].name.append(std::to_string(addressableCounter)); + + addressableCounter++; + + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = UNIHUB_AL10_CHANLED_COUNT; + + if(first_run) + { + zones[channel_idx].leds_count = zones[channel_idx].leds_min; + } + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + led new_led; + new_led.name = zones[channel_idx].name; + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_ch_idx + 1)); + new_led.value = channel_idx; + + leds.push_back(new_led); + } + + zones[channel_idx].matrix_map = NULL; + } + + SetupColors(); +} + +void RGBController_LianLiUniHub_AL10::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_LianLiUniHub_AL10::DeviceUpdateLEDs() +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + for(size_t channel = 0; channel < zones.size(); channel++) + { + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + controller->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + controller->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + } + + controller->Synchronize(); +} + +void RGBController_LianLiUniHub_AL10::UpdateZoneLEDs(int zone) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + unsigned int channel = zone; + + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + controller->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + controller->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + + controller->Synchronize(); +} + +void RGBController_LianLiUniHub_AL10::UpdateSingleLED(int led) +{ + if(!initializedMode) + { + DeviceUpdateMode(); + } + unsigned int channel = leds[led].value; + + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + controller->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + controller->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + + controller->Synchronize(); +} + +void RGBController_LianLiUniHub_AL10::DeviceUpdateMode() +{ + initializedMode = true; + + for (size_t channel = 0; channel < zones.size(); channel++) + { + uint8_t fanCount = convertLedCountToFanCount(zones[channel].leds_count); + controller->SetAnyFanCount(channel, convertAnyFanCount(fanCount)); + + switch (modes[active_mode].color_mode) + { + case MODE_COLORS_PER_LED: + controller->SetLedColors(channel, zones[channel].colors, zones[channel].leds_count); + break; + + case MODE_COLORS_MODE_SPECIFIC: + controller->SetLedColors(channel, modes[active_mode].colors.data(), modes[active_mode].colors.size()); + break; + + default: + controller->SetLedColors(channel, nullptr, 0); + break; + } + + controller->SetLedMode(channel, modes[active_mode].value); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->SetLedSpeed(channel, convertLedSpeed(modes[active_mode].speed)); + } + else + { + controller->SetLedSpeed(channel, UNIHUB_AL10_LED_SPEED_075); + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + controller->SetLedDirection(channel, convertLedDirection(modes[active_mode].direction)); + } + else + { + controller->SetLedDirection(channel, UNIHUB_AL10_LED_DIRECTION_LTR); + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + controller->SetLedBrightness(channel, convertLedBrightness(modes[active_mode].brightness)); + } + else + { + controller->SetLedBrightness(channel, UNIHUB_AL10_LED_BRIGHTNESS_100); + } + } + + if(modes[active_mode].value & 0x0200) + { + controller->EnableRgbhMode(); + controller->DisableSyncMode(); + } + else if (modes[active_mode].value & 0x0100) + { + controller->DisableRgbhMode(); + controller->EnableSyncMode(); + } + else + { + controller->DisableRgbhMode(); + controller->DisableSyncMode(); + } + + controller->Synchronize(); +} + +uint8_t RGBController_LianLiUniHub_AL10::convertAnyFanCount(uint8_t count) +{ + switch (count) + { + case 0: + return UNIHUB_AL10_ANY_FAN_COUNT_000; + + case 1: + return UNIHUB_AL10_ANY_FAN_COUNT_001; + + case 2: + return UNIHUB_AL10_ANY_FAN_COUNT_002; + + case 3: + return UNIHUB_AL10_ANY_FAN_COUNT_003; + + case 4: + return UNIHUB_AL10_ANY_FAN_COUNT_004; + + default: + return UNIHUB_AL10_ANY_FAN_COUNT_001; + } +} + +uint8_t RGBController_LianLiUniHub_AL10::convertLedSpeed(uint8_t speed) +{ + switch (speed) + { + case 0: + return UNIHUB_AL10_LED_SPEED_000; + + case 1: + return UNIHUB_AL10_LED_SPEED_025; + + case 2: + return UNIHUB_AL10_LED_SPEED_050; + + case 3: + return UNIHUB_AL10_LED_SPEED_075; + + case 4: + return UNIHUB_AL10_LED_SPEED_100; + + default: + return UNIHUB_AL10_LED_SPEED_050; + } +} + +uint8_t RGBController_LianLiUniHub_AL10::convertLedDirection(uint8_t direction) +{ + switch (direction) + { + case 0: + return UNIHUB_AL10_LED_DIRECTION_LTR; + + case 1: + return UNIHUB_AL10_LED_DIRECTION_RTL; + + default: + return UNIHUB_AL10_LED_DIRECTION_LTR; + } +} + +uint8_t RGBController_LianLiUniHub_AL10::convertLedBrightness(uint8_t brightness) +{ + switch (brightness) + { + case 0: + return UNIHUB_AL10_LED_BRIGHTNESS_000; + + case 1: + return UNIHUB_AL10_LED_BRIGHTNESS_025; + + case 2: + return UNIHUB_AL10_LED_BRIGHTNESS_050; + + case 3: + return UNIHUB_AL10_LED_BRIGHTNESS_075; + + case 4: + return UNIHUB_AL10_LED_BRIGHTNESS_100; + + default: + return UNIHUB_AL10_LED_BRIGHTNESS_100; + } +} +uint8_t RGBController_LianLiUniHub_AL10::convertLedCountToFanCount(uint8_t count) +{ + /*-------------------------------------------------*\ + | Converts <20 to 0, 20-39 to 1, 40-59 to 2, 60=79 | + | to 3 and 80+ to 4 | + \*-------------------------------------------------*/ + // Sets lower and upper limits + if (count == 0x00) + { + return 0x00; + } + if (count >= 0x50) + { + count = 0x50; + } + + /*---------------------------------------------------------*\ + | Returns regular count if it's not in multiples of 20s | + | (AL120 has 20 LEDs per fan, LED strip scenario) | + \*---------------------------------------------------------*/ + if (count % 20) + { + return(count); + } + else + { + return(count / 20); + } +} diff --git a/Controllers/LianLiController/LianLiUniHub_AL10Controller/RGBController_LianLiUniHub_AL10.h b/Controllers/LianLiController/LianLiUniHub_AL10Controller/RGBController_LianLiUniHub_AL10.h new file mode 100644 index 0000000..35d7959 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniHub_AL10Controller/RGBController_LianLiUniHub_AL10.h @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniHub_AL10.h | +| | +| RGBController for Lian Li AL Uni Hub | +| | +| Oliver P 05 May 2022 | +| Credit to Luca Lovisa for original work | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "LianLiUniHub_AL10Controller.h" +#include "RGBController.h" + +class RGBController_LianLiUniHub_AL10 : public RGBController +{ +public: + RGBController_LianLiUniHub_AL10(LianLiUniHub_AL10Controller* controller_ptr); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + uint8_t convertAnyFanCount(uint8_t count); + uint8_t convertLedSpeed(uint8_t speed); + uint8_t convertLedDirection(uint8_t direction); + uint8_t convertLedBrightness(uint8_t brightness); + + uint8_t convertLedCountToFanCount(uint8_t count); + +private: + LianLiUniHub_AL10Controller* controller; + bool initializedMode; +}; diff --git a/Controllers/LianLiController/LianLiUniversalScreenController/LianLiUniversalScreenController.cpp b/Controllers/LianLiController/LianLiUniversalScreenController/LianLiUniversalScreenController.cpp new file mode 100644 index 0000000..3fc74b1 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniversalScreenController/LianLiUniversalScreenController.cpp @@ -0,0 +1,99 @@ +/*---------------------------------------------------------*\ +| LianLiUniversalScreenController.cpp | +| | +| Driver for Lian Li 8.8" Universal Screen LEDs | +| | +| Adam Honse 17 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "LianLiUniversalScreenController.h" + +LianLiUniversalScreenController::LianLiUniversalScreenController(libusb_device_handle* device) +{ + dev = device; + + /*-----------------------------------------------------*\ + | Fill in location string with USB ID | + \*-----------------------------------------------------*/ + libusb_device_descriptor descriptor; + libusb_get_device_descriptor(libusb_get_device(dev), &descriptor); + + std::stringstream location_stream; + location_stream << std::hex << std::setfill('0') << std::setw(4) << descriptor.idVendor << ":" << std::hex << std::setfill('0') << std::setw(4) << descriptor.idProduct; + location = location_stream.str(); + + /*-----------------------------------------------------*\ + | Fill in the serial string from the string descriptor | + \*-----------------------------------------------------*/ + char serialStr[64]; + + int ret = libusb_get_string_descriptor_ascii(dev, descriptor.iSerialNumber, reinterpret_cast(serialStr), sizeof(serialStr)); + + if(ret > 0) + { + serial = std::string(serialStr, ret); + } +} + +LianLiUniversalScreenController::~LianLiUniversalScreenController() +{ + if(dev) + { + libusb_close(dev); + } +} + +std::string LianLiUniversalScreenController::GetLocation() +{ + return("USB: " + location); +} + +std::string LianLiUniversalScreenController::GetSerial() +{ + return(serial); +} + +std::string LianLiUniversalScreenController::GetVersion() +{ + return(""); +} + +void LianLiUniversalScreenController::SetLedColors(RGBColor* colors, size_t count) +{ + std::size_t leds_in_packet = 20; + unsigned char offset = 0; + unsigned char usb_buf[64]; + + memset(usb_buf, 0, sizeof(usb_buf)); + + do + { + usb_buf[0] = 0x11; + usb_buf[1] = offset; + usb_buf[2] = 0; + usb_buf[3] = 0; + + if((count - offset) < leds_in_packet) + { + leds_in_packet = count - offset; + } + + for(std::size_t led_idx = 0; led_idx < leds_in_packet; led_idx++) + { + usb_buf[4 + (led_idx * 3)] = RGBGetRValue(colors[offset + led_idx]); + usb_buf[5 + (led_idx * 3)] = RGBGetGValue(colors[offset + led_idx]); + usb_buf[6 + (led_idx * 3)] = RGBGetBValue(colors[offset + led_idx]); + } + + offset += (unsigned char)leds_in_packet; + + int actual_length = sizeof(usb_buf); + libusb_bulk_transfer(dev, 1, usb_buf, sizeof(usb_buf), &actual_length, 25); + } while( offset < count ); +} diff --git a/Controllers/LianLiController/LianLiUniversalScreenController/LianLiUniversalScreenController.h b/Controllers/LianLiController/LianLiUniversalScreenController/LianLiUniversalScreenController.h new file mode 100644 index 0000000..ce71b0c --- /dev/null +++ b/Controllers/LianLiController/LianLiUniversalScreenController/LianLiUniversalScreenController.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| LianLiUniversalScreenController.h | +| | +| Driver for Lian Li 8.8" Universal Screen LEDs | +| | +| Adam Honse 17 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class LianLiUniversalScreenController +{ +public: + LianLiUniversalScreenController(libusb_device_handle* device); + ~LianLiUniversalScreenController(); + + std::string GetVersion(); + std::string GetLocation(); + std::string GetSerial(); + + void SetLedColors(RGBColor* colors, size_t count); + +private: + libusb_device_handle* dev; + + std::string version; + std::string location; + std::string serial; +}; diff --git a/Controllers/LianLiController/LianLiUniversalScreenController/RGBController_LianLiUniversalScreen.cpp b/Controllers/LianLiController/LianLiUniversalScreenController/RGBController_LianLiUniversalScreen.cpp new file mode 100644 index 0000000..253b51f --- /dev/null +++ b/Controllers/LianLiController/LianLiUniversalScreenController/RGBController_LianLiUniversalScreen.cpp @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniversalScreen.cpp | +| | +| RGBController for Lian Li 8.8" Universal Screen LEDs | +| | +| Adam Honse 17 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LianLiUniversalScreen.h" + +/**------------------------------------------------------------------*\ + @name Lian Li Universal Screen + @category Monitor + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectLianLiUniversalScreenControllers + @comment Only controls the LEDs around the screen, not the screen + itself. +\*-------------------------------------------------------------------*/ + +RGBController_LianLiUniversalScreen::RGBController_LianLiUniversalScreen(LianLiUniversalScreenController* controller_ptr) +{ + controller = controller_ptr; + + name = "Lian Li Universal Screen"; + type = DEVICE_TYPE_MONITOR; + vendor = "Lian Li"; + description = "Lian Li Universal Screen Device"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + version = controller->GetVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +void RGBController_LianLiUniversalScreen::SetupZones() +{ + zone Screen; + Screen.name = "Screen Lighting"; + Screen.type = ZONE_TYPE_LINEAR; + Screen.leds_min = 60; + Screen.leds_max = 60; + Screen.leds_count = 60; + Screen.matrix_map = NULL; + zones.push_back(Screen); + + for(std::size_t led_idx = 0; led_idx < Screen.leds_count; led_idx++) + { + led ScreenLED; + + ScreenLED.name = "Screen Lighting LED " + std::to_string(led_idx); + ScreenLED.value = 0; + + leds.push_back(ScreenLED); + } + + SetupColors(); +} + +void RGBController_LianLiUniversalScreen::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_LianLiUniversalScreen::DeviceUpdateLEDs() +{ + controller->SetLedColors(colors.data(), colors.size()); +} + +void RGBController_LianLiUniversalScreen::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LianLiUniversalScreen::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LianLiUniversalScreen::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/LianLiController/LianLiUniversalScreenController/RGBController_LianLiUniversalScreen.h b/Controllers/LianLiController/LianLiUniversalScreenController/RGBController_LianLiUniversalScreen.h new file mode 100644 index 0000000..0719649 --- /dev/null +++ b/Controllers/LianLiController/LianLiUniversalScreenController/RGBController_LianLiUniversalScreen.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_LianLiUniversalScreen.h | +| | +| RGBController for Lian Li 8.8" Universal Screen LEDs | +| | +| Adam Honse 17 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "LianLiUniversalScreenController.h" +#include "RGBController.h" + +class RGBController_LianLiUniversalScreen : public RGBController +{ +public: + RGBController_LianLiUniversalScreen(LianLiUniversalScreenController* controller_ptr); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LianLiUniversalScreenController* controller; +}; diff --git a/Controllers/LightSaltController/LightSaltController.cpp b/Controllers/LightSaltController/LightSaltController.cpp new file mode 100644 index 0000000..d6fffa0 --- /dev/null +++ b/Controllers/LightSaltController/LightSaltController.cpp @@ -0,0 +1,311 @@ +/*---------------------------------------------------------*\ +| LightSaltController.cpp | +| | +| Driver for LightSalt Peripherals | +| | +| James Buren (braewoods) 23 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LightSaltController.h" +#include "StringUtils.h" + +static const int mode_map[LIGHTSALT_MODE_MAXIMUM][6] = +{ + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0400, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0700, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0A00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0D00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0E00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0F00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x1100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x3301, 0x3302}, + {0x3402, 0x3401, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x3502, 0x3501, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x3602, 0x3601, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x3802, 0x3801, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x3901, 0x3902, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x3A01, 0x3A02, 0x0000, 0x0000}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x3702, 0x3701}, + {0x3B02, 0x3B01, 0x0000, 0x0000, 0x0000, 0x0000} +}; + +LightSaltController::LightSaltController(hid_device* dev_handle, const hid_device_info& info) +{ + wchar_t usb_string[128]; + + dev = dev_handle; + device_location = info.path; + + if(hid_get_manufacturer_string(dev, usb_string, 128) == 0) + { + manufacturer = StringUtils::wstring_to_string(usb_string); + } + else + { + manufacturer = ""; + } + + if(hid_get_product_string(dev, usb_string, 128) == 0) + { + product = StringUtils::wstring_to_string(usb_string); + } + else + { + product = ""; + } + + if(strstr(manufacturer.c_str(), "Light&Salt") && strstr(product.c_str(), "Light&Salt")) + { + QueryDeviceModel(); + QueryDeviceClass(); + SetDeviceType(); + } + else + { + device_model = ""; + device_class = ""; + firmware_version = ""; + device_type = LIGHTSALT_TYPE_MAXIMUM; + } +} + +LightSaltController::~LightSaltController() +{ + hid_close(dev); +} + +std::string LightSaltController::GetDeviceLocation() +{ + return("HID: " + device_location); +} + +std::string LightSaltController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string LightSaltController::GetManufacturer() +{ + return(manufacturer); +} + +std::string LightSaltController::GetProduct() +{ + return(product); +} + +std::string LightSaltController::GetDeviceModel() +{ + return(device_model); +} + +std::string LightSaltController::GetDeviceClass() +{ + return(device_class); +} + +std::string LightSaltController::GetFirmwareVersion() +{ + return(firmware_version); +} + +int LightSaltController::GetDeviceType() +{ + return(device_type); +} + +void LightSaltController::SetColors(RGBColor* colors, int sets, int rows, int columns) +{ + uint8_t write_buffer[LIGHTSALT_WRITE_LENGTH]; + int color_idx = 0; + + write_buffer[0] = 0x00; + + for(int set_idx = 0; set_idx < sets; set_idx++) + { + write_buffer[1] = 0x03 + set_idx; + + for(int row_idx = 0; row_idx < rows; row_idx++) + { + int write_idx = 2; + + write_buffer[write_idx++] = 0x01 + row_idx * columns; + + for(int column_idx = 0; column_idx < columns; column_idx++) + { + RGBColor color = colors[color_idx++]; + write_buffer[write_idx++] = RGBGetRValue(color); + write_buffer[write_idx++] = RGBGetGValue(color); + write_buffer[write_idx++] = RGBGetBValue(color); + } + + hid_write(dev, write_buffer, sizeof(write_buffer)); + } + } +} + +void LightSaltController::ApplyColors(int sets) +{ + uint8_t write_buffer[LIGHTSALT_WRITE_LENGTH]; + int write_idx = 1; + + memset(write_buffer, 0x00, sizeof(write_buffer)); + write_buffer[write_idx++] = 0xFE; + write_buffer[write_idx++] = 0x01; + + for(int set_idx = 0; set_idx < sets; set_idx++) + { + write_buffer[write_idx++] = 0x03 + set_idx; + } + + hid_write(dev, write_buffer, sizeof(write_buffer)); +} + +void LightSaltController::SaveColors(int sets) +{ + uint8_t write_buffer[LIGHTSALT_WRITE_LENGTH]; + int write_idx = 1; + + memset(write_buffer, 0x00, sizeof(write_buffer)); + write_buffer[write_idx++] = 0xFE; + write_buffer[write_idx++] = 0xF0; + + for(int set_idx = 0; set_idx < sets; set_idx++) + { + write_buffer[write_idx++] = 0x03 + set_idx; + } + + hid_write(dev, write_buffer, sizeof(write_buffer)); +} + +void LightSaltController::SetMode(int mode, int direction, int speed) +{ + uint8_t write_buffer[LIGHTSALT_WRITE_LENGTH]; + int actual_mode = mode_map[mode][direction]; + + memset(write_buffer, 0x00, sizeof(write_buffer)); + write_buffer[1] = 0xFE; + write_buffer[2] = 0x03; + write_buffer[3] = (actual_mode & 0xFF00) >> 8; + write_buffer[4] = (actual_mode & 0x00FF) >> 0; + write_buffer[5] = speed; + + hid_write(dev, write_buffer, sizeof(write_buffer)); +} + +void LightSaltController::SetFilter(RGBColor color) +{ + uint8_t write_buffer[LIGHTSALT_WRITE_LENGTH]; + + memset(write_buffer, 0x00, sizeof(write_buffer)); + write_buffer[1] = 0xFE; + write_buffer[2] = 0x04; + write_buffer[3] = RGBGetRValue(color); + write_buffer[4] = RGBGetGValue(color); + write_buffer[5] = RGBGetBValue(color); + + hid_write(dev, write_buffer, sizeof(write_buffer)); +} + +void LightSaltController::SetBrightness(uint8_t brightness) +{ + uint8_t write_buffer[LIGHTSALT_WRITE_LENGTH]; + + memset(write_buffer, 0x00, sizeof(write_buffer)); + write_buffer[1] = 0xFE; + write_buffer[2] = 0x05; + write_buffer[3] = brightness; + + hid_write(dev, write_buffer, sizeof(write_buffer)); +} + +void LightSaltController::QueryDeviceModel() +{ + uint8_t write_buffer[LIGHTSALT_WRITE_LENGTH]; + uint8_t read_buffer[LIGHTSALT_READ_LENGTH]; + + memset(write_buffer, 0x00, sizeof(write_buffer)); + write_buffer[1] = 0xFE; + write_buffer[2] = 0xF5; + write_buffer[3] = 0x06; + + if(hid_write(dev, write_buffer, sizeof(write_buffer)) != sizeof(write_buffer)) + { + device_model = ""; + return; + } + + if(hid_read_timeout(dev, read_buffer, sizeof(read_buffer), 100) != sizeof(read_buffer)) + { + device_model = ""; + return; + } + + device_model = (char *)(read_buffer + 3); +} + +void LightSaltController::QueryDeviceClass() +{ + uint8_t write_buffer[LIGHTSALT_WRITE_LENGTH]; + uint8_t read_buffer[LIGHTSALT_READ_LENGTH]; + + memset(write_buffer, 0x00, sizeof(write_buffer)); + write_buffer[1] = 0xFE; + write_buffer[2] = 0xFF; + + if(hid_write(dev, write_buffer, sizeof(write_buffer)) != sizeof(write_buffer)) + { + device_class = ""; + firmware_version = ""; + return; + } + + if(hid_read_timeout(dev, read_buffer, sizeof(read_buffer), 100) != sizeof(read_buffer)) + { + device_class = ""; + firmware_version = ""; + return; + } + + device_class = (char *)(read_buffer ); + firmware_version = (char *)(read_buffer + device_class.size() + 1); +} + +void LightSaltController::SetDeviceType() +{ + struct device_class_table + { + std::string device_class; + int device_type; + }; + static const device_class_table table[] = + { + {"Light&Salt_keyboard1", LIGHTSALT_TYPE_KEYBOARD}, + {"Light&Salt_KEY_PAD", LIGHTSALT_TYPE_KEYPAD } + }; + + device_type = LIGHTSALT_TYPE_MAXIMUM; + + for(const device_class_table& match : table) + { + if(match.device_class == device_class) + { + device_type = match.device_type; + break; + } + } +} diff --git a/Controllers/LightSaltController/LightSaltController.h b/Controllers/LightSaltController/LightSaltController.h new file mode 100644 index 0000000..0d1ad90 --- /dev/null +++ b/Controllers/LightSaltController/LightSaltController.h @@ -0,0 +1,109 @@ +/*---------------------------------------------------------*\ +| LightSaltController.h | +| | +| Driver for LightSalt Peripherals | +| | +| James Buren (braewoods) 23 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define LIGHTSALT_PACKET_LENGTH 32 +#define LIGHTSALT_WRITE_LENGTH (LIGHTSALT_PACKET_LENGTH + 1) +#define LIGHTSALT_READ_LENGTH (LIGHTSALT_PACKET_LENGTH) + +enum +{ + LIGHTSALT_TYPE_KEYBOARD, + LIGHTSALT_TYPE_KEYPAD, + LIGHTSALT_TYPE_MAXIMUM +}; + +enum +{ + LIGHTSALT_MODE_CUSTOM, + LIGHTSALT_MODE_POINT, + LIGHTSALT_MODE_AREA, + LIGHTSALT_MODE_COLLAPSE, + LIGHTSALT_MODE_EXPAND, + LIGHTSALT_MODE_EXPLODE, + LIGHTSALT_MODE_DART, + LIGHTSALT_MODE_FLAME, + LIGHTSALT_MODE_LASER, + LIGHTSALT_MODE_BREATHING, + LIGHTSALT_MODE_TRICOLOR_RADAR, + LIGHTSALT_MODE_WHEEL_1, + LIGHTSALT_MODE_WHEEL_2, + LIGHTSALT_MODE_WAVE_1, + LIGHTSALT_MODE_WAVE_2, + LIGHTSALT_MODE_WAVE_3, + LIGHTSALT_MODE_RAINBOW_1, + LIGHTSALT_MODE_RAINBOW_2, + LIGHTSALT_MODE_MAXIMUM +}; + +enum +{ + LIGHTSALT_SETS_MAX = 4, + LIGHTSALT_ROWS_MAX = 9, + LIGHTSALT_COLUMNS_MAX = 10 +}; + +enum +{ + LIGHTSALT_SPEED_MINIMUM = 0x01, + LIGHTSALT_SPEED_MAXIMUM = 0x32, + LIGHTSALT_SPEED_DEFAULT = 0x19 +}; + +enum +{ + LIGHTSALT_BRIGHTNESS_MINIMUM = 0x00, + LIGHTSALT_BRIGHTNESS_MAXIMUM = 0xFF, + LIGHTSALT_BRIGHTNESS_DEFAULT = 0x80 +}; + +class LightSaltController +{ +public: + LightSaltController(hid_device* dev_handle, const hid_device_info& info); + ~LightSaltController(); + + std::string GetDeviceLocation(); + std::string GetSerial(); + std::string GetManufacturer(); + std::string GetProduct(); + std::string GetDeviceModel(); + std::string GetDeviceClass(); + std::string GetFirmwareVersion(); + int GetDeviceType(); + + void SetColors(RGBColor* colors, int sets, int rows, int columns); + void ApplyColors(int sets); + void SaveColors(int sets); + void SetMode(int mode, int direction, int speed); + void SetFilter(RGBColor color); + void SetBrightness(uint8_t brightness); + +private: + hid_device* dev; + + std::string device_location; + std::string manufacturer; + std::string product; + std::string device_model; + std::string device_class; + std::string firmware_version; + int device_type; + + void QueryDeviceModel(); + void QueryDeviceClass(); + void SetDeviceType(); +}; diff --git a/Controllers/LightSaltController/LightSaltControllerDetect.cpp b/Controllers/LightSaltController/LightSaltControllerDetect.cpp new file mode 100644 index 0000000..e2d7420 --- /dev/null +++ b/Controllers/LightSaltController/LightSaltControllerDetect.cpp @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| LightSaltControllerDetect.cpp | +| | +| Detector for LightSalt Peripherals | +| | +| James Buren (braewoods) 23 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "LightSaltController.h" +#include "RGBController_LightSaltKeyboard.h" +#include "RGBController_LightSaltKeypad.h" + +#define LIGHTSALT_VID 0x0483 +#define LIGHTSALT_PID 0x5750 + +void DetectLightSaltControllers(hid_device_info* info, const std::string &) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LightSaltController* controller = new LightSaltController(dev, *info); + RGBController* rgb_controller = nullptr; + + switch(controller->GetDeviceType()) + { + case LIGHTSALT_TYPE_KEYBOARD: + rgb_controller = new RGBController_LightSaltKeyboard(controller); + break; + + case LIGHTSALT_TYPE_KEYPAD: + rgb_controller = new RGBController_LightSaltKeypad(controller); + break; + + default: + delete controller; + break; + } + + if(rgb_controller != nullptr) + { + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} /* DetectLightSaltControllers() */ + +REGISTER_HID_DETECTOR_IPU("LightSalt Peripherals", DetectLightSaltControllers, LIGHTSALT_VID, LIGHTSALT_PID, 1, 1, 0); diff --git a/Controllers/LightSaltController/RGBController_LightSalt.cpp b/Controllers/LightSaltController/RGBController_LightSalt.cpp new file mode 100644 index 0000000..2bcac33 --- /dev/null +++ b/Controllers/LightSaltController/RGBController_LightSalt.cpp @@ -0,0 +1,452 @@ +/*---------------------------------------------------------*\ +| RGBController_LightSalt.cpp | +| | +| RGBController for LightSalt Peripherals | +| | +| James Buren (braewoods) 23 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBControllerKeyNames.h" +#include "RGBController_LightSalt.h" + +static RGBColor DeflectColor(bool deflection, RGBColor color) +{ + if(deflection) + { + int old_red = RGBGetRValue(color); + int old_grn = RGBGetGValue(color); + int old_blu = RGBGetBValue(color); + int new_red = old_red + ((255 - old_red) * 50 / 100); + int new_grn = old_grn + ((255 - old_grn) * 50 / 100); + int new_blu = old_blu + ((255 - old_blu) * 50 / 100); + return ToRGBColor(new_red, new_grn, new_blu); + } + else + { + return color; + } +} + +RGBController_LightSalt::~RGBController_LightSalt() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != nullptr) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_LightSalt::SetupData(LightSaltController* controller_ptr) +{ + controller = controller_ptr; + name = "LightSalt " + table.device.name; + vendor = "LightSalt"; + type = table.device.type; + description = "LightSalt " + table.device.name + " (" + controller->GetDeviceModel() + ")"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + version = controller->GetFirmwareVersion(); +} + +void RGBController_LightSalt::SetupModes() +{ + { + mode mode; + mode.name = "Custom"; + mode.value = LIGHTSALT_MODE_CUSTOM; + mode.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_PER_LED; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Reactive Point"; + mode.value = LIGHTSALT_MODE_POINT; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Reactive Area"; + mode.value = LIGHTSALT_MODE_AREA; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Reactive Collapse"; + mode.value = LIGHTSALT_MODE_COLLAPSE; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Reactive Expand"; + mode.value = LIGHTSALT_MODE_EXPAND; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Reactive Explode"; + mode.value = LIGHTSALT_MODE_EXPLODE; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Reactive Dart"; + mode.value = LIGHTSALT_MODE_DART; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Reactive Flame"; + mode.value = LIGHTSALT_MODE_FLAME; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Reactive Laser"; + mode.value = LIGHTSALT_MODE_LASER; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Breathing"; + mode.value = LIGHTSALT_MODE_BREATHING; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.direction = MODE_DIRECTION_HORIZONTAL; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Tricolor Radar"; + mode.value = LIGHTSALT_MODE_TRICOLOR_RADAR; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.direction = MODE_DIRECTION_LEFT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Wheel 1"; + mode.value = LIGHTSALT_MODE_WHEEL_1; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.direction = MODE_DIRECTION_LEFT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Wheel 2"; + mode.value = LIGHTSALT_MODE_WHEEL_2; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.direction = MODE_DIRECTION_LEFT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Wave 1"; + mode.value = LIGHTSALT_MODE_WAVE_1; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.direction = MODE_DIRECTION_LEFT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Wave 2"; + mode.value = LIGHTSALT_MODE_WAVE_2; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.direction = MODE_DIRECTION_UP; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Wave 3"; + mode.value = LIGHTSALT_MODE_WAVE_3; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.direction = MODE_DIRECTION_UP; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Rainbow 1"; + mode.value = LIGHTSALT_MODE_RAINBOW_1; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_HV | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.direction = MODE_DIRECTION_HORIZONTAL; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } + + { + mode mode; + mode.name = "Rainbow 2"; + mode.value = LIGHTSALT_MODE_RAINBOW_2; + mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + mode.speed_min = LIGHTSALT_SPEED_MINIMUM; + mode.speed_max = LIGHTSALT_SPEED_MAXIMUM; + mode.speed = LIGHTSALT_SPEED_DEFAULT; + mode.direction = MODE_DIRECTION_LEFT; + mode.brightness_min = LIGHTSALT_BRIGHTNESS_MINIMUM; + mode.brightness_max = LIGHTSALT_BRIGHTNESS_MAXIMUM; + mode.brightness = LIGHTSALT_BRIGHTNESS_DEFAULT; + mode.color_mode = MODE_COLORS_NONE; + modes.push_back(mode); + } +} + +void RGBController_LightSalt::SetupZones() +{ + { + zone zone; + zone.name = table.device.name; + zone.type = ZONE_TYPE_MATRIX; + zone.leds_min = table.led.count; + zone.leds_max = table.led.count; + zone.leds_count = table.led.count; + zone.matrix_map = new matrix_map_type; + zone.matrix_map->height = table.map.height; + zone.matrix_map->width = table.map.width; + zone.matrix_map->map = table.map.matrix; + zones.push_back(zone); + } + + for(int led_idx = 0; led_idx < table.led.count; led_idx++) + { + led led; + led.name = table.led.names[led_idx]; + leds.push_back(led); + } + + { + zone zone; + zone.name = "Color Filter"; + zone.type = ZONE_TYPE_SINGLE; + zone.leds_min = 1; + zone.leds_max = 1; + zone.leds_count = 1; + zone.matrix_map = NULL; + zones.push_back(zone); + } + + { + led led; + led.name = "Color Filter"; + leds.push_back(led); + } + + SetupColors(); + + colors[colors.size() - 1] = ToRGBColor(0xFF, 0xFF, 0xFF); +} + +void RGBController_LightSalt::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LightSalt::DeviceUpdateColors(bool save) +{ + int sets = table.led.sets; + int rows = table.led.rows; + int columns = table.led.columns; + + RGBColor colors_data[LIGHTSALT_SETS_MAX][LIGHTSALT_ROWS_MAX][LIGHTSALT_COLUMNS_MAX]; + memset(colors_data, 0x00, sizeof(colors_data)); + + for(int led_idx = 0; led_idx < table.led.count; led_idx++) + { + int index = table.led.indices[led_idx]; + int deflection = table.led.deflections[led_idx]; + int row = index / columns; + int column = index % columns; + RGBColor color = colors[led_idx]; + + for(int set_idx = 0; set_idx < sets; set_idx++) + { + colors_data[set_idx][row][column] = DeflectColor(deflection & (1 << set_idx), color); + } + } + + controller->SetFilter(colors[colors.size() - 1]); + controller->SetColors((RGBColor*)colors_data, sets, rows, columns); + controller->ApplyColors(sets); + if(save) + { + controller->SaveColors(sets); + } +} + +void RGBController_LightSalt::DeviceUpdateLEDs() +{ + DeviceUpdateColors(false); +} + +void RGBController_LightSalt::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LightSalt::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LightSalt::DeviceUpdateMode() +{ + const mode& mode = modes[active_mode]; + + controller->SetBrightness(mode.brightness); + std::this_thread::sleep_for(std::chrono::milliseconds(25)); + controller->SetMode(mode.value, mode.direction, mode.speed); +} + +void RGBController_LightSalt::DeviceSaveMode() +{ + DeviceUpdateColors(true); +} diff --git a/Controllers/LightSaltController/RGBController_LightSalt.h b/Controllers/LightSaltController/RGBController_LightSalt.h new file mode 100644 index 0000000..b37d3c1 --- /dev/null +++ b/Controllers/LightSaltController/RGBController_LightSalt.h @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| RGBController_LightSalt.h | +| | +| RGBController for LightSalt Peripherals | +| | +| James Buren (braewoods) 23 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LightSaltController.h" + +class RGBController_LightSalt : public RGBController +{ +public: + ~RGBController_LightSalt(); + + void SetupData(LightSaltController* controller_ptr); + void SetupModes(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateColors(bool save); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +protected: + struct + { + struct + { + std::string name; + int type; + } device; + struct + { + int sets; + int rows; + int columns; + int count; + char const * const * names; + const int* indices; + const int* deflections; + } led; + struct + { + unsigned int height; + unsigned int width; + unsigned int* matrix; + } map; + } table; + LightSaltController* controller; +}; diff --git a/Controllers/LightSaltController/RGBController_LightSaltKeyboard.cpp b/Controllers/LightSaltController/RGBController_LightSaltKeyboard.cpp new file mode 100644 index 0000000..4b58993 --- /dev/null +++ b/Controllers/LightSaltController/RGBController_LightSaltKeyboard.cpp @@ -0,0 +1,207 @@ +/*---------------------------------------------------------*\ +| RGBController_LightSaltKeyboard.cpp | +| | +| RGBController for LightSalt Keyboard | +| | +| James Buren (braewoods) 23 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_LightSaltKeyboard.h" + +// 0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +enum +{ + LED_SETS = 4, + LED_ROWS = 9, + LED_COLUMNS = 10, + LED_COUNT = 83, + MAP_HEIGHT = 6, + MAP_WIDTH = 15, +}; + +static char const * const led_names[LED_COUNT] = +{ + /* Row 1 */ + KEY_EN_ESCAPE, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + + /* Row 2 */ + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_DELETE, + + /* Row 3 */ + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_PRINT_SCREEN, + + /* Row 4 */ + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + + /* Row 5 */ + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + + /* Row 6 */ + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_SPACE, + KEY_EN_SPACE, + KEY_EN_SPACE, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW +}; + +static const int led_indices[LED_COUNT] = +{ + /* Row 1 */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + + /* Row 2 */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + + /* Row 3 */ + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + + /* Row 4 */ + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + + /* Row 5 */ + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, + + /* Row 6 */ + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 87, 88, 89 +}; + +static const int led_deflections[LED_COUNT] = +{ + /* Row 1 */ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + + /* Row 2 */ + 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0x0, 0x0, + + /* Row 3 */ + 0x0, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0xA, 0xA, 0xA, 0x0, + + /* Row 4 */ + 0x0, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0xA, 0xA, 0x0, + + /* Row 5 */ + 0x0, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0xA, 0xA, 0xA, 0x0, 0x0, + + /* Row 6 */ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 +}; + +static const unsigned int matrix_map[MAP_HEIGHT][MAP_WIDTH] = +{ + /* Row 1 */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, NA, NA }, + + /* Row 2 */ + { 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }, + + /* Row 3 */ + { 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }, + + /* Row 4 */ + { 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, NA, NA }, + + /* Row 5 */ + { 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, NA, 68, NA }, + + /* Row 6 */ + { 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, NA, 80, 81, 82 } +}; + +RGBController_LightSaltKeyboard::RGBController_LightSaltKeyboard(LightSaltController* controller_ptr) +{ + table.device.name = "Keyboard"; + table.device.type = DEVICE_TYPE_KEYBOARD; + table.led.sets = LED_SETS; + table.led.rows = LED_ROWS; + table.led.columns = LED_COLUMNS; + table.led.count = LED_COUNT; + table.led.names = led_names; + table.led.indices = led_indices; + table.led.deflections = led_deflections; + table.map.height = MAP_HEIGHT; + table.map.width = MAP_WIDTH; + table.map.matrix = (unsigned int *)matrix_map; + + SetupData(controller_ptr); + SetupModes(); + SetupZones(); +} diff --git a/Controllers/LightSaltController/RGBController_LightSaltKeyboard.h b/Controllers/LightSaltController/RGBController_LightSaltKeyboard.h new file mode 100644 index 0000000..38155f2 --- /dev/null +++ b/Controllers/LightSaltController/RGBController_LightSaltKeyboard.h @@ -0,0 +1,20 @@ +/*---------------------------------------------------------*\ +| RGBController_LightSaltKeyboard.h | +| | +| RGBController for LightSalt Keyboard | +| | +| James Buren (braewoods) 23 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController_LightSalt.h" + +class RGBController_LightSaltKeyboard : public RGBController_LightSalt +{ +public: + RGBController_LightSaltKeyboard(LightSaltController* controller_ptr); +}; diff --git a/Controllers/LightSaltController/RGBController_LightSaltKeypad.cpp b/Controllers/LightSaltController/RGBController_LightSaltKeypad.cpp new file mode 100644 index 0000000..0bbde7f --- /dev/null +++ b/Controllers/LightSaltController/RGBController_LightSaltKeypad.cpp @@ -0,0 +1,131 @@ +/*---------------------------------------------------------*\ +| RGBController_LightSaltKeypad.cpp | +| | +| RGBController for LightSalt Keypad | +| | +| James Buren (braewoods) 23 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_LightSaltKeypad.h" + +// 0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +enum +{ + LED_SETS = 1, + LED_ROWS = 5, + LED_COLUMNS = 10, + LED_COUNT = 18, + MAP_HEIGHT = 5, + MAP_WIDTH = 4 +}; + +static char const * const led_names[LED_COUNT] = +{ + /* Row 1 */ + KEY_EN_TAB, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_BACKSPACE, + + /* Row 2 */ + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_MINUS, + + /* Row 3 */ + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + KEY_EN_PLUS, + + /* Row 4 */ + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + + /* Row 5 */ + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD, + KEY_EN_NUMPAD_ENTER +}; + +static const int led_indices[LED_COUNT] = +{ + /* Row 1 */ + 0, 1, 2, 3, + + /* Row 2 */ + 4, 5, 6, 7, + + /* Row 3 */ + 8, 9, 10, 11, + + /* Row 4 */ + 12, 13, 14, + + /* Row 5 */ + 16, 17, 18 +}; + +static const int led_deflections[LED_COUNT] = +{ + /* Row 1 */ + 0x0, 0x0, 0x0, 0x0, + + /* Row 2 */ + 0x0, 0x0, 0x0, 0x0, + + /* Row 3 */ + 0x0, 0x0, 0x0, 0x0, + + /* Row 4 */ + 0x0, 0x0, 0x0, + + /* Row 5 */ + 0x0, 0x0, 0x0 +}; + +static const unsigned int matrix_map[MAP_HEIGHT][MAP_WIDTH] = +{ + /* Row 1 */ + { 0, 1 , 2, 3}, + + /* Row 2 */ + { 4, 5, 6, 7}, + + /* Row 3 */ + { 8, 9, 10, 11}, + + /* Row 4 */ + {12, 13, 14, NA}, + + /* Row 5 */ + {15, NA, 16, 17} +}; + +RGBController_LightSaltKeypad::RGBController_LightSaltKeypad(LightSaltController* controller_ptr) +{ + table.device.name = "Keypad"; + table.device.type = DEVICE_TYPE_KEYPAD; + table.led.sets = LED_SETS; + table.led.rows = LED_ROWS; + table.led.columns = LED_COLUMNS; + table.led.count = LED_COUNT; + table.led.names = led_names; + table.led.indices = led_indices; + table.led.deflections = led_deflections; + table.map.height = MAP_HEIGHT; + table.map.width = MAP_WIDTH; + table.map.matrix = (unsigned int *)matrix_map; + + SetupData(controller_ptr); + SetupModes(); + SetupZones(); +} diff --git a/Controllers/LightSaltController/RGBController_LightSaltKeypad.h b/Controllers/LightSaltController/RGBController_LightSaltKeypad.h new file mode 100644 index 0000000..3828707 --- /dev/null +++ b/Controllers/LightSaltController/RGBController_LightSaltKeypad.h @@ -0,0 +1,20 @@ +/*---------------------------------------------------------*\ +| RGBController_LightSaltKeypad.h | +| | +| RGBController for LightSalt Keypad | +| | +| James Buren (braewoods) 23 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController_LightSalt.h" + +class RGBController_LightSaltKeypad : public RGBController_LightSalt +{ +public: + RGBController_LightSaltKeypad(LightSaltController* controller_ptr); +}; diff --git a/Controllers/LinuxLEDController/LinuxLEDControllerDetect_Linux.cpp b/Controllers/LinuxLEDController/LinuxLEDControllerDetect_Linux.cpp new file mode 100644 index 0000000..268c361 --- /dev/null +++ b/Controllers/LinuxLEDController/LinuxLEDControllerDetect_Linux.cpp @@ -0,0 +1,86 @@ +/*---------------------------------------------------------*\ +| LinuxLEDControllerDetect_Linux.cpp | +| | +| Detector for Linux sysfs LEDs | +| | +| Adam Honse (calcprogrammer1@gmail.com) 25 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "RGBController_LinuxLED_Linux.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectLinuxLEDControllers * +* * +* Detect devices supported by the LinuxLED driver * +* * +\******************************************************************************************/ + +void DetectLinuxLEDControllers() +{ + json linux_led_settings; + + /*-------------------------------------------------*\ + | Get Linux LED settings from settings manager | + \*-------------------------------------------------*/ + linux_led_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("LinuxLEDDevices"); + + /*-------------------------------------------------*\ + | If the LinuxLED settings contains devices, process| + \*-------------------------------------------------*/ + if(linux_led_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < linux_led_settings["devices"].size(); device_idx++) + { + std::string name; + std::string red_path; + std::string green_path; + std::string blue_path; + std::string rgb_path; + + if(linux_led_settings["devices"][device_idx].contains("name")) + { + name = linux_led_settings["devices"][device_idx]["name"]; + } + + if(linux_led_settings["devices"][device_idx].contains("red_path")) + { + red_path = linux_led_settings["devices"][device_idx]["red_path"]; + } + + if(linux_led_settings["devices"][device_idx].contains("green_path")) + { + green_path = linux_led_settings["devices"][device_idx]["green_path"]; + } + + if(linux_led_settings["devices"][device_idx].contains("blue_path")) + { + blue_path = linux_led_settings["devices"][device_idx]["blue_path"]; + } + + if(linux_led_settings["devices"][device_idx].contains("rgb_path")) + { + rgb_path = linux_led_settings["devices"][device_idx]["rgb_path"]; + } + + LinuxLEDController* controller = new LinuxLEDController(name); + controller->OpenRedPath(red_path); + controller->OpenGreenPath(green_path); + controller->OpenBluePath(blue_path); + controller->OpenRgbPath(rgb_path); + + RGBController_LinuxLED* rgb_controller = new RGBController_LinuxLED(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + +} /* DetectLinuxLEDControllers() */ + +REGISTER_DETECTOR("Linux LED", DetectLinuxLEDControllers); diff --git a/Controllers/LinuxLEDController/LinuxLEDController_Linux.cpp b/Controllers/LinuxLEDController/LinuxLEDController_Linux.cpp new file mode 100644 index 0000000..d3ffc57 --- /dev/null +++ b/Controllers/LinuxLEDController/LinuxLEDController_Linux.cpp @@ -0,0 +1,119 @@ +/*---------------------------------------------------------*\ +| LinuxLEDController_Linux.cpp | +| | +| Driver for Linux sysfs LEDs | +| | +| Adam Honse (calcprogrammer1@gmail.com) 25 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LinuxLEDController_Linux.h" + +LinuxLEDController::LinuxLEDController(std::string dev_name) +{ + name = dev_name; +} + +LinuxLEDController::~LinuxLEDController() +{ + +} + +std::string LinuxLEDController::GetName() +{ + return(name); +} + +std::string LinuxLEDController::GetRedPath() +{ + return(led_r_path); +} + +std::string LinuxLEDController::GetGreenPath() +{ + return(led_g_path); +} + +std::string LinuxLEDController::GetBluePath() +{ + return(led_b_path); +} + +std::string LinuxLEDController::GetRgbPath() +{ + return(led_rgb_path); +} + +void LinuxLEDController::OpenRedPath(std::string red_path) +{ + led_r_path = red_path; + led_r_brightness.open(led_r_path + "brightness"); +} + +void LinuxLEDController::OpenGreenPath(std::string green_path) +{ + led_g_path = green_path; + led_g_brightness.open(led_g_path + "brightness"); +} + +void LinuxLEDController::OpenBluePath(std::string blue_path) +{ + led_b_path = blue_path; + led_b_brightness.open(led_b_path + "brightness"); +} + +void LinuxLEDController::OpenRgbPath(std::string rgb_path) +{ + led_rgb_path = rgb_path; + led_rgb_brightness.open(led_rgb_path + "brightness"); + led_rgb_color.open(led_rgb_path + "multi_intensity"); +} + +void LinuxLEDController::SetRGB(unsigned char red, unsigned char grn, unsigned char blu) +{ + std::string brightness_str; + + if(led_rgb_path.empty()) + { + /*-------------------------------------------------*\ + | My phone LED that I tested this on shuts down if | + | you set zero | + \*-------------------------------------------------*/ + if(red == 0) red = 1; + if(grn == 0) grn = 1; + if(blu == 0) blu = 1; + + brightness_str = std::to_string((unsigned int)red); + + led_r_brightness.write(brightness_str.c_str(), brightness_str.length()); + led_r_brightness.flush(); + + brightness_str = std::to_string((unsigned int)grn); + + led_g_brightness.write(brightness_str.c_str(), brightness_str.length()); + led_g_brightness.flush(); + + brightness_str = std::to_string((unsigned int)blu); + + led_b_brightness.write(brightness_str.c_str(), brightness_str.length()); + led_b_brightness.flush(); + } + else + { + /*-------------------------------------------------*\ + | For the led_classdev_mc brightness just applies a | + | coefficient to the multi_intensity. Set brightness| + | to maximum and use the RGB values directly | + | instead. | + \*-------------------------------------------------*/ + brightness_str = std::to_string((unsigned int)255); + led_rgb_brightness.write(brightness_str.c_str(), brightness_str.length()); + led_rgb_brightness.flush(); + + brightness_str = std::to_string((unsigned int)red) + " " + std::to_string((unsigned int)grn) + " " + std::to_string((unsigned int)blu); + led_rgb_color.write(brightness_str.c_str(), brightness_str.length()); + led_rgb_color.flush(); + } +} diff --git a/Controllers/LinuxLEDController/LinuxLEDController_Linux.h b/Controllers/LinuxLEDController/LinuxLEDController_Linux.h new file mode 100644 index 0000000..dc44531 --- /dev/null +++ b/Controllers/LinuxLEDController/LinuxLEDController_Linux.h @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| LinuxLEDController_Linux.h | +| | +| Driver for Linux sysfs LEDs | +| | +| Adam Honse (calcprogrammer1@gmail.com) 25 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +class LinuxLEDController +{ +public: + LinuxLEDController(std::string dev_name); + ~LinuxLEDController(); + + std::string GetName(); + + std::string GetRedPath(); + std::string GetBluePath(); + std::string GetGreenPath(); + std::string GetRgbPath(); + + void OpenRedPath(std::string red_path); + void OpenGreenPath(std::string green_path); + void OpenBluePath(std::string blue_path); + void OpenRgbPath(std::string rgb_path); + + void SetRGB(unsigned char red, unsigned char grn, unsigned char blu); + +private: + std::string led_r_path; + std::string led_g_path; + std::string led_b_path; + std::string led_rgb_path; + std::ofstream led_r_brightness; + std::ofstream led_g_brightness; + std::ofstream led_b_brightness; + std::ofstream led_rgb_brightness; + std::ofstream led_rgb_color; + std::string name; +}; diff --git a/Controllers/LinuxLEDController/RGBController_LinuxLED_Linux.cpp b/Controllers/LinuxLEDController/RGBController_LinuxLED_Linux.cpp new file mode 100644 index 0000000..c9d478a --- /dev/null +++ b/Controllers/LinuxLEDController/RGBController_LinuxLED_Linux.cpp @@ -0,0 +1,107 @@ +/*---------------------------------------------------------*\ +| RGBController_LinuxLED.cpp | +| | +| RGBController for Linux sysfs LEDs | +| | +| Adam Honse (calcprogrammer1@gmail.com) 25 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LinuxLED_Linux.h" + +/**------------------------------------------------------------------*\ + @name Dummy + @category LEDStrip + @type File Stream + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectLinuxLEDControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LinuxLED::RGBController_LinuxLED(LinuxLEDController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + type = DEVICE_TYPE_LEDSTRIP; + description = "Linux Sysfs LED Device"; + + if(controller->GetRgbPath().empty()) + { + location = "R: " + controller->GetRedPath() + "\r\n" + + "G: " + controller->GetGreenPath() + "\r\n" + + "B: " + controller->GetBluePath(); + } + else + { + location = controller->GetRgbPath(); + } + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_LinuxLED::~RGBController_LinuxLED() +{ + delete controller; +} + +void RGBController_LinuxLED::SetupZones() +{ + zone led_zone; + led_zone.name = "RGB Light"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + led new_led; + new_led.name = "RGB Light"; + + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_LinuxLED::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LinuxLED::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetRGB(red, grn, blu); +} + +void RGBController_LinuxLED::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LinuxLED::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LinuxLED::DeviceUpdateMode() +{ + +} diff --git a/Controllers/LinuxLEDController/RGBController_LinuxLED_Linux.h b/Controllers/LinuxLEDController/RGBController_LinuxLED_Linux.h new file mode 100644 index 0000000..732a87c --- /dev/null +++ b/Controllers/LinuxLEDController/RGBController_LinuxLED_Linux.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LinuxLED.h | +| | +| RGBController for Linux sysfs LEDs | +| | +| Adam Honse (calcprogrammer1@gmail.com) 25 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LinuxLEDController_Linux.h" + +class RGBController_LinuxLED : public RGBController +{ +public: + RGBController_LinuxLED(LinuxLEDController* controller_ptr); + ~RGBController_LinuxLED(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LinuxLEDController* controller; +}; diff --git a/Controllers/LogitechController/LogitechControllerDetect.cpp b/Controllers/LogitechController/LogitechControllerDetect.cpp new file mode 100644 index 0000000..0f42734 --- /dev/null +++ b/Controllers/LogitechController/LogitechControllerDetect.cpp @@ -0,0 +1,1253 @@ +/*---------------------------------------------------------*\ +| LogitechControllerDetect.cpp | +| | +| Detector for Logitech devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "ResourceManager.h" +#include "LogManager.h" +#include "LogitechProtocolCommon.h" +#include "LogitechG203LController.h" +#include "LogitechG213Controller.h" +#include "LogitechG560Controller.h" +#include "LogitechG600Controller.h" +#include "LogitechG933Controller.h" +#include "LogitechG810Controller.h" +#include "LogitechGProKeyboardController.h" +#include "LogitechG910Controller.h" +#include "LogitechG815Controller.h" +#include "LogitechG915Controller.h" +#include "LogitechGLightsyncController.h" +#include "LogitechLightspeedController.h" +#include "LogitechX56Controller.h" +#include "RGBController_LogitechG203L.h" +#include "RGBController_LogitechG213.h" +#include "RGBController_LogitechG560.h" +#include "RGBController_LogitechG600.h" +#include "RGBController_LogitechG933.h" +#include "RGBController_LogitechG810.h" +#include "RGBController_LogitechGProKeyboard.h" +#include "RGBController_LogitechG910.h" +#include "RGBController_LogitechG815.h" +#include "RGBController_LogitechG915.h" +#include "RGBController_LogitechGLightsync.h" +#include "RGBController_LogitechGLightsync1zone.h" +#include "RGBController_LogitechLightspeed.h" +#include "RGBController_LogitechGPowerPlay.h" // Linux-only +#include "RGBController_LogitechX56.h" +#include "LogitechHIDPP20Controller.h" +#include "RGBController_LogitechHIDPP20.h" + +using namespace std::chrono_literals; + +/*-----------------------------------------------------*\ +| Logitech vendor ID | +\*-----------------------------------------------------*/ +#define LOGITECH_VID 0x046D +#define LOGITECH_LIGHTSPEED_DETECT_MAX_RETRY 10 + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define LOGITECH_G213_PID 0xC336 +#define LOGITECH_G512_PID 0xC342 +#define LOGITECH_G512_RGB_PID 0xC33C +#define LOGITECH_G610_1_PID 0xC333 +#define LOGITECH_G610_2_PID 0xC338 +#define LOGITECH_G810_1_PID 0xC331 +#define LOGITECH_G810_2_PID 0xC337 +#define LOGITECH_G813_PID 0xC232 +#define LOGITECH_G815_PID 0xC33F +#define LOGITECH_G915_WIRED_PID 0xC33E +#define LOGITECH_G915_RECEIVER_PID 0xC541 +#define LOGITECH_G915_RECEIVER_2_PID 0xC547 +#define LOGITECH_G915TKL_WIRED_PID 0xC343 +#define LOGITECH_G915TKL_RECEIVER_PID 0xC545 +#define LOGITECH_G910_ORION_SPARK_PID 0xC32B +#define LOGITECH_G910_PID 0xC335 +#define LOGITECH_GPRO_KEYBOARD_1_PID 0xC339 + +/*-----------------------------------------------------*\ +| Mouse product IDs | +\*-----------------------------------------------------*/ +#define LOGITECH_G203_PID 0xC084 +#define LOGITECH_G203_LIGHTSYNC_PID 0xC092 +#define LOGITECH_G203_LIGHTSYNC_PID_2 0xC09D +#define LOGITECH_G303_PID 0xC080 +#define LOGITECH_G403_PID 0xC083 +#define LOGITECH_G403_HERO_PID 0xC08F +#define LOGITECH_G403_LIGHTSPEED_PID 0xC082 +#define LOGITECH_G502_PROTEUS_SPECTRUM_PID 0xC332 +#define LOGITECH_G502_HERO_PID 0xC08B +#define LOGITECH_G502_LIGHTSPEED_PID 0xC08D +#define LOGITECH_G502_X_PLUS_PID 0xC095 +#define LOGITECH_G515_LS_TKL_PID 0xC355 +#define LOGITECH_G522_LIGHTSPEED_USB_PID 0x0B19 +#define LOGITECH_G522_LIGHTSPEED_DONGLE_PID 0x0B18 +#define LOGITECH_G600_PID 0xC24A +#define LOGITECH_G703_LIGHTSPEED_PID 0xC087 +#define LOGITECH_G703_HERO_LIGHTSPEED_PID 0xC090 +#define LOGITECH_G900_LIGHTSPEED_PID 0xC081 +#define LOGITECH_G903_LIGHTSPEED_PID 0xC086 +#define LOGITECH_G903_LIGHTSPEED_HERO_PID 0xC091 +#define LOGITECH_G_PRO_PID 0xC085 +#define LOGITECH_G_PRO_HERO_PID 0xC08C +#define LOGITECH_G_PRO_WIRELESS_PID 0xC088 + +/*-----------------------------------------------------*\ +| Mousemat product IDs | +\*-----------------------------------------------------*/ +#define LOGITECH_G_LIGHTSPEED_POWERPLAY_PID 0xC53A + +/*-----------------------------------------------------*\ +| Speaker product IDs | +\*-----------------------------------------------------*/ +#define LOGITECH_G560_PID 0x0A78 + +/*-----------------------------------------------------*\ +| Headset product IDs | +\*-----------------------------------------------------*/ +#define LOGITECH_G633_PID 0x0A5C +#define LOGITECH_G635_PID 0x0A89 +#define LOGITECH_G733_PID 0x0AB5 +#define LOGITECH_G733_2_PID 0x0AFE +#define LOGITECH_G733_3_PID 0x0B1F +#define LOGITECH_G933_PID 0x0A5B +#define LOGITECH_G935_PID 0x0A87 + +/*-----------------------------------------------------*\ +| Unifying Device IDs (Including Lightspeed receivers) | +| NB: Not used but preserved for debugging | +\*-----------------------------------------------------*/ +#define LOGITECH_G_UNIFYING_RECEIVER_1_PID 0xC52B +#define LOGITECH_G_NANO_RECEIVER_PID 0xC52F +#define LOGITECH_G_G700_RECEIVER_PID 0xC531 +#define LOGITECH_G_UNIFYING_RECEIVER_2_PID 0xC532 +#define LOGITECH_G_G602_RECEIVER_PID 0xC537 + +#define LOGITECH_G_LIGHTSPEED_RECEIVER_PID 0xC539 +#define LOGITECH_G403_LIGHTSPEED_VIRTUAL_PID 0x405D +#define LOGITECH_G502_LIGHTSPEED_VIRTUAL_PID 0x407F +#define LOGITECH_G703_LIGHTSPEED_VIRTUAL_PID 0x4070 +#define LOGITECH_G703_HERO_LIGHTSPEED_VIRTUAL_PID 0x4086 +#define LOGITECH_G900_LIGHTSPEED_VIRTUAL_PID 0x4053 +#define LOGITECH_G903_LIGHTSPEED_VIRTUAL_PID 0x4067 +#define LOGITECH_G903_LIGHTSPEED_VIRTUAL_HERO_PID 0x4087 +#define LOGITECH_G_PRO_WIRELESS_VIRTUAL_PID 0x4079 +#define LOGITECH_POWERPLAY_MAT_VIRTUAL_PID 0x405F +#define LOGITECH_G502_X_PLUS_LIGHTSPEED_VIRTUAL_PID 0x4099 +#define LOGITECH_G515_LS_TKL_LIGHTSPEED_VIRTUAL_PID 0x40B4 + +/*-----------------------------------------------------*\ +| Joystick product IDs | +\*-----------------------------------------------------*/ +#define LOGITECH_X56_VID 0x0738 +#define LOGITECH_X56_JOYSTICK_PID 0x2221 +#define LOGITECH_X56_THROTTLE_PID 0xA221 + +/*-----------------------------------------------------*\ +| Logitech Keyboards | +\*-----------------------------------------------------*/ +void DetectLogitechKeyboardG213(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LogitechG213Controller* controller = new LogitechG213Controller(dev, info->path, name); + RGBController_LogitechG213* rgb_controller = new RGBController_LogitechG213(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectLogitechKeyboardG810(hid_device_info* info, const std::string& name) +{ + /*-------------------------------------------------------------------------------------------------*\ + | Logitech keyboards use two different usages, one for 20-byte packets and one for 64-byte packets | + | Usage 0x0602 for 20 byte, usage 0x0604 for 64 byte, both are on usage page 0xFF43 | + \*-------------------------------------------------------------------------------------------------*/ +#ifdef USE_HID_USAGE + hid_device* dev_usage_0x0602 = nullptr; + hid_device* dev_usage_0x0604 = nullptr; + hid_device_info* info_temp = info; + + while(info_temp) + { + if(info_temp->vendor_id == info->vendor_id // constant LOGITECH_VID + && info_temp->product_id == info->product_id // NON-constant + && info_temp->interface_number == info->interface_number // constant 1 + && info_temp->usage_page == info->usage_page) // constant 0xFF43 + { + if(info_temp->usage == 0x0602) + { + dev_usage_0x0602 = hid_open_path(info_temp->path); + } + else if(info_temp->usage == 0x0604) + { + dev_usage_0x0604 = hid_open_path(info_temp->path); + } + } + if(dev_usage_0x0602 && dev_usage_0x0604) + { + break; + } + info_temp = info_temp->next; + } + if(dev_usage_0x0602 && dev_usage_0x0604) + { + LogitechG810Controller* controller = new LogitechG810Controller(dev_usage_0x0602, dev_usage_0x0604, name); + RGBController_LogitechG810* rgb_controller = new RGBController_LogitechG810(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + // Not all of them could be opened, do some cleanup + hid_close(dev_usage_0x0602); + hid_close(dev_usage_0x0604); + } +#else + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LogitechG810Controller* controller = new LogitechG810Controller(dev, dev, name); + RGBController_LogitechG810* rgb_controller = new RGBController_LogitechG810(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +#endif +} + +void DetectLogitechKeyboardG910(hid_device_info* info, const std::string& name) +{ + /*-------------------------------------------------------------------------------------------------*\ + | Logitech keyboards use two different usages, one for 20-byte packets and one for 64-byte packets | + | Usage 0x0602 for 20 byte, usage 0x0604 for 64 byte, both are on usage page 0xFF43 | + \*-------------------------------------------------------------------------------------------------*/ +#ifdef USE_HID_USAGE + hid_device* dev_usage_0x0602 = nullptr; + hid_device* dev_usage_0x0604 = nullptr; + hid_device_info* info_temp = info; + + while(info_temp) + { + if(info_temp->vendor_id == info->vendor_id // constant LOGITECH_VID + && info_temp->product_id == info->product_id // NON-constant + && info_temp->interface_number == info->interface_number // constant 1 + && info_temp->usage_page == info->usage_page) // constant 0xFF43 + { + if(info_temp->usage == 0x0602) + { + dev_usage_0x0602 = hid_open_path(info_temp->path); + } + else if(info_temp->usage == 0x0604) + { + dev_usage_0x0604 = hid_open_path(info_temp->path); + } + } + if(dev_usage_0x0602 && dev_usage_0x0604) + { + break; + } + info_temp = info_temp->next; + } + if(dev_usage_0x0602 && dev_usage_0x0604) + { + LogitechG910Controller* controller = new LogitechG910Controller(dev_usage_0x0602, dev_usage_0x0604, name); + RGBController_LogitechG910* rgb_controller = new RGBController_LogitechG910(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + // Not all of them could be opened, do some cleanup + hid_close(dev_usage_0x0602); + hid_close(dev_usage_0x0604); + } +#else + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LogitechG910Controller* controller = new LogitechG910Controller(dev, dev, name); + RGBController_LogitechG910* rgb_controller = new RGBController_LogitechG910(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +#endif +} + +void DetectLogitechKeyboardG815(hid_device_info* info, const std::string& name) +{ + /*-------------------------------------------------------------------------------------------------*\ + | Logitech keyboards use two different usages, one for 20-byte packets and one for 64-byte packets | + | Usage 0x0602 for 20 byte, usage 0x0604 for 64 byte, both are on usage page 0xFF43 | + \*-------------------------------------------------------------------------------------------------*/ +#ifdef USE_HID_USAGE + hid_device* dev_usage_0x0602 = nullptr; + hid_device* dev_usage_0x0604 = nullptr; + hid_device_info* info_temp = info; + + while(info_temp) + { + if(info_temp->vendor_id == info->vendor_id // constant LOGITECH_VID + && info_temp->product_id == info->product_id // NON-constant + && info_temp->interface_number == info->interface_number // constant 1 + && info_temp->usage_page == info->usage_page) // constant 0xFF43 + { + if(info_temp->usage == 0x0602) + { + dev_usage_0x0602 = hid_open_path(info_temp->path); + } + else if(info_temp->usage == 0x0604) + { + dev_usage_0x0604 = hid_open_path(info_temp->path); + } + } + if(dev_usage_0x0602 && dev_usage_0x0604) + { + break; + } + info_temp = info_temp->next; + } + if(dev_usage_0x0602 && dev_usage_0x0604) + { + LogitechG815Controller* controller = new LogitechG815Controller(dev_usage_0x0602, dev_usage_0x0604, name); + RGBController_LogitechG815* rgb_controller = new RGBController_LogitechG815(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + /*-------------------------------------------------*\ + | Not all of them could be opened, do some cleanup | + \*-------------------------------------------------*/ + if(dev_usage_0x0602) + { + hid_close(dev_usage_0x0602); + } + if(dev_usage_0x0604) + { + hid_close(dev_usage_0x0604); + } + } +#else + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LogitechG815Controller* controller = new LogitechG815Controller(dev, dev, name); + RGBController_LogitechG815* rgb_controller = new RGBController_LogitechG815(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +#endif +} + +void DetectLogitechKeyboardG915(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + bool is_tkl = info->product_id == LOGITECH_G915TKL_RECEIVER_PID; + + if(dev) + { + LogitechG915Controller* controller = new LogitechG915Controller(dev, false, name); + RGBController_LogitechG915* rgb_controller = new RGBController_LogitechG915(controller, is_tkl); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +static bool ProbeG915ReceiverName(hid_device* dev, std::string& out_name) +{ + /*---------------------------------------------------------*\n | HID++ short message name probe. | + | Request: 10 01 03 0E 00 00 00 (get name length) | + | Request: 10 01 03 1E 00 00 00 (get name string) | + | Response: 11 01 03 1E | + | Verified against G915 TKL (PID 0xC547) which returns | + | "G915 TKL LIGHTSP" (truncated, full: G915 TKL LIGHTSPEED) | + \*---------------------------------------------------------*/ + const unsigned char req_len[7] = { 0x10, 0x01, 0x03, 0x0E, 0x00, 0x00, 0x00 }; + const unsigned char req_name[7] = { 0x10, 0x01, 0x03, 0x1E, 0x00, 0x00, 0x00 }; + unsigned char resp[64] = { 0 }; + + hid_write(dev, req_len, sizeof(req_len)); + hid_read_timeout(dev, resp, sizeof(resp), 100); + + hid_write(dev, req_name, sizeof(req_name)); + for(int attempt = 0; attempt < 3; attempt++) + { + int rd = hid_read_timeout(dev, resp, sizeof(resp), 200); + if(rd < 8) + { + continue; + } + if(resp[0] == 0x11 && resp[1] == 0x01 && resp[2] == 0x03 && resp[3] == 0x1E) + { + std::string name_str; + for(int i = 4; i < rd; i++) + { + if(resp[i] == 0x00) + { + break; + } + name_str.push_back(static_cast(resp[i])); + } + out_name = name_str; + return true; + } + } + return false; +} + +void DetectLogitechKeyboardG915Receiver2(hid_device_info* info, const std::string& name) +{ + /*---------------------------------------------------------*\ + | PID 0xC547 is shared by multiple Logitech keyboards. | + | Use a HID++ name probe to identify the exact device and | + | route to the correct controller. | + | | + | Known devices behind this PID: | + | "G915 TKL LIGHTSP..." -> G915 TKL (is_tkl = true) | + | "G915 LIGHTSP..." -> G915 full-size (is_tkl = false)| + | "G515..." -> G515 (not handled here) | + \*---------------------------------------------------------*/ + hid_device* dev = hid_open_path(info->path); + if(!dev) + { + return; + } + + std::string probed_name; + bool ok = ProbeG915ReceiverName(dev, probed_name); + + if(!ok) + { + LOG_DEBUG("[LogitechControllerDetect] 0xC547 name probe failed, skipping device"); + hid_close(dev); + return; + } + + LOG_DEBUG("[LogitechControllerDetect] 0xC547 probe returned name=\"%s\"", probed_name.c_str()); + + /*---------------------------------------------------------*\ + | Route based on probed name. Check for TKL before full | + | G915 since both contain "G915". | + \*---------------------------------------------------------*/ + if(probed_name.find("G915 TKL") != std::string::npos) + { + LogitechG915Controller* controller = new LogitechG915Controller(dev, false, name); + RGBController_LogitechG915* rgb_controller = new RGBController_LogitechG915(controller, true); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else if(probed_name.find("G915") != std::string::npos) + { + LogitechG915Controller* controller = new LogitechG915Controller(dev, false, name); + RGBController_LogitechG915* rgb_controller = new RGBController_LogitechG915(controller, false); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + /*-----------------------------------------------------*\ + | Unknown device (e.g. G515 or future hardware). | + | Close and leave it for another detector to claim. | + \*-----------------------------------------------------*/ + LOG_DEBUG("[LogitechControllerDetect] 0xC547 unrecognised device name \"%s\", skipping", + probed_name.c_str()); + hid_close(dev); + } +} + +void DetectLogitechKeyboardG915Wired(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + bool is_tkl = info->product_id == LOGITECH_G915TKL_WIRED_PID; + + if(dev) + { + LogitechG915Controller* controller = new LogitechG915Controller(dev, true, name); + RGBController_LogitechG915* rgb_controller = new RGBController_LogitechG915(controller, is_tkl); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectLogitechKeyboardGPro(hid_device_info* info, const std::string& name) +{ + /*-------------------------------------------------------------------------------------------------*\ + | Logitech keyboards use two different usages, one for 20-byte packets and one for 64-byte packets | + | Usage 0x0602 for 20 byte, usage 0x0604 for 64 byte, both are on usage page 0xFF43 | + \*-------------------------------------------------------------------------------------------------*/ +#ifdef USE_HID_USAGE + hid_device* dev_usage_0x0602 = nullptr; + hid_device* dev_usage_0x0604 = nullptr; + hid_device_info* info_temp = info; + + while(info_temp) + { + if(info_temp->vendor_id == info->vendor_id // constant LOGITECH_VID + && info_temp->product_id == info->product_id // NON-constant + && info_temp->interface_number == info->interface_number // constant 1 + && info_temp->usage_page == info->usage_page) // constant 0xFF43 + { + if(info_temp->usage == 0x0602) + { + dev_usage_0x0602 = hid_open_path(info_temp->path); + } + else if(info_temp->usage == 0x0604) + { + dev_usage_0x0604 = hid_open_path(info_temp->path); + } + } + if(dev_usage_0x0602 && dev_usage_0x0604) + { + break; + } + info_temp = info_temp->next; + } + if(dev_usage_0x0602 && dev_usage_0x0604) + { + LogitechGProKeyboardController* controller = new LogitechGProKeyboardController(dev_usage_0x0602, dev_usage_0x0604, name); + RGBController_LogitechGProKeyboard* rgb_controller = new RGBController_LogitechGProKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + // Not all of them could be opened, do some cleanup + hid_close(dev_usage_0x0602); + hid_close(dev_usage_0x0604); + } +#else + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LogitechGProKeyboardController* controller = new LogitechGProKeyboardController(dev, dev, name); + RGBController_LogitechGProKeyboard* rgb_controller = new RGBController_LogitechGProKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +#endif +} + +/*-----------------------------------------------------*\ +| Logitech Mice | +\*-----------------------------------------------------*/ +static void addLogitechLightsyncMouse1zone(hid_device_info* info, const std::string& name, unsigned char hid_dev_index, unsigned char hid_feature_index, unsigned char hid_fctn_ase_id) +{ +#ifdef USE_HID_USAGE + { + hid_device* dev_usage_1 = nullptr; + hid_device* dev_usage_2 = nullptr; + hid_device_info* info_temp = info; + + while(info_temp) + { + if(info_temp->vendor_id == info->vendor_id // constant LOGITECH_VID + && info_temp->product_id == info->product_id // NON-constant + && info_temp->interface_number == info->interface_number // constant 1 + && info_temp->usage_page == info->usage_page) // constant 0x00FF + { + if (info_temp->usage == 1) + { + dev_usage_1 = hid_open_path(info_temp->path); + } + else if (info_temp->usage == 2) + { + dev_usage_2 = hid_open_path(info_temp->path); + } + } + if (dev_usage_1 && dev_usage_2) + { + break; + } + info_temp = info_temp->next; + } + if(dev_usage_1 && dev_usage_2) + { + LogitechGLightsyncController* controller = new LogitechGLightsyncController(dev_usage_1, dev_usage_2, info->path, hid_dev_index, hid_feature_index, hid_fctn_ase_id, name); + RGBController_LogitechGLightsync1zone* rgb_controller = new RGBController_LogitechGLightsync1zone (controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + LOG_INFO("Unable to open all device report endpoints, unable to add device"); + hid_close(dev_usage_1); + hid_close(dev_usage_2); + } + } + +#else + { + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LogitechGLightsyncController* controller = new LogitechGLightsyncController(dev, dev, info->path, hid_dev_index, hid_feature_index, hid_fctn_ase_id, name); + RGBController_LogitechGLightsync1zone* rgb_controller = new RGBController_LogitechGLightsync1zone(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +#endif +} + +static void addLogitechLightsyncMouse2zone(hid_device_info* info, const std::string& name, unsigned char hid_dev_index, unsigned char hid_feature_index, unsigned char hid_fctn_ase_id) +{ +#ifdef USE_HID_USAGE + { + hid_device* dev_usage_1 = nullptr; + hid_device* dev_usage_2 = nullptr; + hid_device_info* info_temp = info; + + while(info_temp) + { + if(info_temp->vendor_id == info->vendor_id // constant LOGITECH_VID + && info_temp->product_id == info->product_id // NON-constant + && info_temp->interface_number == info->interface_number // constant 1 + && info_temp->usage_page == info->usage_page) // constant 0x00FF + { + if(info_temp->usage == 1) + { + dev_usage_1 = hid_open_path(info_temp->path); + } + else if(info_temp->usage == 2) + { + dev_usage_2 = hid_open_path(info_temp->path); + } + } + if(dev_usage_1 && dev_usage_2) + { + break; + } + info_temp = info_temp->next; + } + if(dev_usage_1 && dev_usage_2) + { + LogitechGLightsyncController* controller = new LogitechGLightsyncController(dev_usage_1, dev_usage_2, info->path, hid_dev_index, hid_feature_index, hid_fctn_ase_id, name); + RGBController_LogitechGLightsync* rgb_controller = new RGBController_LogitechGLightsync (controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + LOG_INFO("Unable to open all device report endpoints, unable to add device"); + hid_close(dev_usage_1); + hid_close(dev_usage_2); + } + } +#else + { + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LogitechGLightsyncController* controller = new LogitechGLightsyncController(dev, dev, info->path, hid_dev_index, hid_feature_index, hid_fctn_ase_id, name); + RGBController_LogitechGLightsync* rgb_controller = new RGBController_LogitechGLightsync(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +#endif +} + +void DetectLogitechMouseG203(hid_device_info* info, const std::string& name) +{ + addLogitechLightsyncMouse1zone(info, name, 0xFF, 0x0E, 0x3A); +} + +void DetectLogitechMouseG203L(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LogitechG203LController* controller = new LogitechG203LController(dev, info->path, name); + RGBController_LogitechG203L* rgb_controller = new RGBController_LogitechG203L(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectLogitechMouseG303(hid_device_info* info, const std::string& name) +{ + addLogitechLightsyncMouse2zone(info, name, 0xFF, 0x0E, 0x3A); +} + +void DetectLogitechMouseG403(hid_device_info* info, const std::string& name) +{ + addLogitechLightsyncMouse2zone(info, name, 0xFF, 0x0E, 0x3A); +} + +void DetectLogitechMouseG600(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LogitechG600Controller* controller = new LogitechG600Controller(dev, info->path, name); + RGBController_LogitechG600* rgb_controller = new RGBController_LogitechG600(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectLogitechMouseGPRO(hid_device_info* info, const std::string& name) +{ + addLogitechLightsyncMouse1zone(info, name, 0xFF, 0x0E, 0x3C); +} + +/*-----------------------------------------------------*\ +| Other Logitech Devices | +\*-----------------------------------------------------*/ +void DetectLogitechG560(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + /*---------------------------------------------*\ + | Add G560 Speaker | + \*---------------------------------------------*/ + LogitechG560Controller* controller = new LogitechG560Controller(dev, info->path, name); + RGBController_LogitechG560* rgb_controller = new RGBController_LogitechG560(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectLogitechG933(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + /*---------------------------------------------*\ + | Add G933 Headset | + \*---------------------------------------------*/ + LogitechG933Controller* controller = new LogitechG933Controller(dev, info->path, name); + RGBController_LogitechG933* rgb_controller = new RGBController_LogitechG933(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectLogitechX56(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + /*---------------------------------------------*\ + | Add X56 Devices | + \*---------------------------------------------*/ + LogitechX56Controller* controller = new LogitechX56Controller(dev, info->path, name); + RGBController_LogitechX56* rgb_controller = new RGBController_LogitechX56(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +/*------------------------------------------------------------------------------*\ +| Unified HID++ 2.0 Detection | +| Probes IRoot (feature 0x0000) to determine if the device speaks HID++ 2.0. | +| If it does and has RGB features, the unified controller handles it. | +| If not, the device is released for legacy controllers. | +\*------------------------------------------------------------------------------*/ +void DetectLogitechHIDPP20(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(!dev) + { + return; + } + + LogitechHIDPP20Controller* controller = new LogitechHIDPP20Controller( + dev, info->path, LOGITECH_DEFAULT_DEVICE_INDEX, false, nullptr, + info->usage_page); + + if(controller->Probe()) + { + controller->Initialize(); + + const HIDPP20DeviceCapabilities& caps = controller->GetCapabilities(); + + if(caps.has_zone_effects || caps.has_perkey) + { + /*-------------------------------------------------*\ + | Device has RGB features — create and register | + | RGBController for the UI. | + \*-------------------------------------------------*/ + RGBController_LogitechHIDPP20* rgb_controller = new RGBController_LogitechHIDPP20(controller); + + LOG_INFO("[%s] Registering RGB controller", caps.device_name.c_str()); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + + /*--------------------------------------------------*\ + | Start reader + power threads immediately so we | + | detect connection events and handle power mgmt | + | from the start — not deferred to DeviceUpdateMode. | + \*--------------------------------------------------*/ + if(caps.has_power_mgmt || caps.idx_wireless_status != 0) + { + controller->StartPowerManager(); + + if(!caps.has_power_mgmt && caps.idx_wireless_status != 0) + { + controller->StartEventWatcher(); + } + } + } + else if(controller->HasBridge()) + { + /*--------------------------------------------------*\ + | Centurion dongle with no sub-device — keep the | + | controller alive and start reader thread to watch | + | for sub-device connection events. | + \*--------------------------------------------------*/ + LOG_INFO("[%s] Dongle registered, watching for sub-device", + caps.device_name.c_str()); + + controller->SetRegisterCallback([](RGBController* rgb) + { + ResourceManager::get()->RegisterRGBController(rgb); + }); + + controller->StartEventWatcher(); + } + else + { + /*--------------------------------------------------*\ + | Device probed successfully but has no RGB and no | + | bridge — nothing to do (e.g., headset without RGB) | + \*--------------------------------------------------*/ + LOG_INFO("[%s] No RGB features, skipping", caps.device_name.c_str()); + delete controller; + } + } + else + { + /*--------------------------------------------------*\ + | Probe failed. Could be an offline paired device, | + | a stale pairing slot, or a receiver itself. | + | | + | Only skip if the name explicitly says "Receiver". | + | Everything else gets a watcher — devices can come | + | back at any time (power cycle, dongle swap, etc.) | + \*--------------------------------------------------*/ + std::string hid_name; + + if(info->product_string) + { + std::wstring ws(info->product_string); + hid_name = std::string(ws.begin(), ws.end()); + } + + if(hid_name.find("Receiver") != std::string::npos || + hid_name.find("receiver") != std::string::npos) + { + delete controller; + } + else + { + LOG_INFO("[HID++2.0 %s] Probe failed — watching for device (name='%s')", + info->path, hid_name.c_str()); + + controller->SetRegisterCallback([](RGBController* rgb) + { + ResourceManager::get()->RegisterRGBController(rgb); + }); + + controller->StartProbeWatcher(); + } + } +} + +#if defined(_WIN32) || defined(__APPLE__) +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Unified HID++ 2.0 Lightspeed Receiver Detection (Windows / macOS) | +| | +| On Linux, hid-logitech-dj splits receiver traffic into per-slot virtual child hidraw nodes with their own 0x40XX PIDs, so Linux detection can | +| use DetectLogitechHIDPP20 directly against the virtual PIDs. Windows and macOS have no DJ driver — the receiver appears as a single HID device | +| and we must probe each paired slot by hand, addressing it via the HID++ device_index header byte. | +| | +| Iterates device indices 0x01..0x06. c547 is dual-pair, but the loop covers Unifying-style receivers and any future wider-pair variants. Each | +| responding slot gets its own hid_device handle and a shared std::mutex so sibling slots serialize HID writes — matching the pattern in the | +| legacy LogitechLightspeedController (see CreateLogitechLightspeedDevice around line 860). | +| | +| TODO (untested on Windows): runtime reader-thread coordination. Each slot controller starts its own reader thread; on a shared receiver both | +| threads will see both slots' incoming packets. The reader needs to drop frames whose device_index doesn't match its own, or dispatch across | +| sibling controllers. Safe during probe (mutex serializes writes, reads are direct); becomes an issue post-StartPowerManager. | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +void DetectLogitechHIDPP20LightspeedReceiver(hid_device_info* info, const std::string& /*name*/) +{ + std::shared_ptr receiver_mutex = std::make_shared(); + + for(uint8_t idx = 0x01; idx <= 0x06; idx++) + { + hid_device* dev = hid_open_path(info->path); + + if(!dev) + { + continue; + } + + LogitechHIDPP20Controller* controller = new LogitechHIDPP20Controller( + dev, info->path, idx, true, receiver_mutex, info->usage_page); + + if(!controller->Probe()) + { + /*--------------------------------------------------*\ + | Slot is empty, stale, or not HID++ 2.0. Destructor | + | closes the per-slot handle we opened above. | + \*--------------------------------------------------*/ + delete controller; + continue; + } + + controller->Initialize(); + + const HIDPP20DeviceCapabilities& caps = controller->GetCapabilities(); + + if(caps.has_zone_effects || caps.has_perkey) + { + RGBController_LogitechHIDPP20* rgb_controller = new RGBController_LogitechHIDPP20(controller); + + LOG_INFO("[%s slot=%u] Registering RGB controller", caps.device_name.c_str(), idx); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + + if(caps.has_power_mgmt || caps.idx_wireless_status != 0) + { + controller->StartPowerManager(); + + if(!caps.has_power_mgmt && caps.idx_wireless_status != 0) + { + controller->StartEventWatcher(); + } + } + } + else + { + LOG_INFO("[%s slot=%u] No RGB features, skipping", caps.device_name.c_str(), idx); + delete controller; + } + } +} +#endif + +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Unified HID++ 2.0 Devices | +| PID-specific registrations for devices tested with the unified controller. | +| Wired paths use the device's own USB PID; wireless paths on Linux use the hid-logitech-dj virtual child PIDs (0x40XX range). | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Logitech HID++ 2.0 G502 X Plus (wired)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G502_X_PLUS_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech HID++ 2.0 G515 LS TKL (wired)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G515_LS_TKL_PID, 2, 0xFF00, 2); +#ifdef __linux__ +REGISTER_HID_DETECTOR_IPU("Logitech HID++ 2.0 G502 X Plus (wireless)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G502_X_PLUS_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech HID++ 2.0 G515 LS TKL (wireless)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G515_LS_TKL_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +#endif +#if defined(_WIN32) || defined(__APPLE__) +REGISTER_HID_DETECTOR_IPU("Logitech HID++ 2.0 Lightspeed Receiver (C547)", DetectLogitechHIDPP20LightspeedReceiver, LOGITECH_VID, 0xC547, 2, 0xFF00, 2); +#endif + +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Centurion-transport devices (63-byte reports on usage page 0xFFA0, 0x50 addressed or 0x51 direct). | +| Centurion receivers are not DJ-style Lightspeed receivers and are not split by hid-logitech-dj — the dongle PID enumerates as a single hidraw | +| on all platforms. The controller's DiscoverTransport parses the report descriptor to pick the 0x50/0x51 variant and runs a 0x00..0xFF address | +| sweep for the 0x50 (addressed) variant, so the detector only needs VID/PID + usage page 0xFFA0. | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_P("Logitech HID++ 2.0 G522 Lightspeed (wired)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G522_LIGHTSPEED_USB_PID, 0xFFA0); +REGISTER_HID_DETECTOR_P("Logitech HID++ 2.0 G522 Lightspeed (dongle)", DetectLogitechHIDPP20, LOGITECH_VID, LOGITECH_G522_LIGHTSPEED_DONGLE_PID, 0xFFA0); + +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Keyboards | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Logitech G213", DetectLogitechKeyboardG213, LOGITECH_VID, LOGITECH_G213_PID, 1, 0xFF43, 0x0602); +REGISTER_HID_DETECTOR_IP ("Logitech G512", DetectLogitechKeyboardG810, LOGITECH_VID, LOGITECH_G512_PID, 1, 0xFF43); +REGISTER_HID_DETECTOR_IP ("Logitech G512 RGB", DetectLogitechKeyboardG810, LOGITECH_VID, LOGITECH_G512_RGB_PID, 1, 0xFF43); +REGISTER_HID_DETECTOR_IP ("Logitech G610 Orion", DetectLogitechKeyboardG810, LOGITECH_VID, LOGITECH_G610_1_PID, 1, 0xFF43); +REGISTER_HID_DETECTOR_IP ("Logitech G610 Orion", DetectLogitechKeyboardG810, LOGITECH_VID, LOGITECH_G610_2_PID, 1, 0xFF43); +REGISTER_HID_DETECTOR_IP ("Logitech G810 Orion Spectrum", DetectLogitechKeyboardG810, LOGITECH_VID, LOGITECH_G810_1_PID, 1, 0xFF43); +REGISTER_HID_DETECTOR_IP ("Logitech G810 Orion Spectrum", DetectLogitechKeyboardG810, LOGITECH_VID, LOGITECH_G810_2_PID, 1, 0xFF43); +REGISTER_HID_DETECTOR_IP ("Logitech G813 RGB Mechanical Gaming Keyboard", DetectLogitechKeyboardG815, LOGITECH_VID, LOGITECH_G813_PID, 1, 0xFF43); +REGISTER_HID_DETECTOR_IP ("Logitech G815 RGB Mechanical Gaming Keyboard", DetectLogitechKeyboardG815, LOGITECH_VID, LOGITECH_G815_PID, 1, 0xFF43); +REGISTER_HID_DETECTOR_IP ("Logitech G910 Orion Spark", DetectLogitechKeyboardG910, LOGITECH_VID, LOGITECH_G910_ORION_SPARK_PID, 1, 0xFF43); +REGISTER_HID_DETECTOR_IP ("Logitech G910 Orion Spectrum", DetectLogitechKeyboardG910, LOGITECH_VID, LOGITECH_G910_PID, 1, 0xFF43); +REGISTER_HID_DETECTOR_IP ("Logitech G Pro RGB Mechanical Gaming Keyboard", DetectLogitechKeyboardGPro, LOGITECH_VID, LOGITECH_GPRO_KEYBOARD_1_PID, 1, 0xFF43); + +REGISTER_HID_DETECTOR_IPU("Logitech G915 Wireless RGB Mechanical Gaming Keyboard", DetectLogitechKeyboardG915, LOGITECH_VID, LOGITECH_G915_RECEIVER_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G915 Wireless RGB Mechanical Gaming Keyboard (Receiver 2)", DetectLogitechKeyboardG915Receiver2, LOGITECH_VID, LOGITECH_G915_RECEIVER_2_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G915 Wireless RGB Mechanical Gaming Keyboard (Wired)", DetectLogitechKeyboardG915Wired, LOGITECH_VID, LOGITECH_G915_WIRED_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G915TKL Wireless RGB Mechanical Gaming Keyboard", DetectLogitechKeyboardG915, LOGITECH_VID, LOGITECH_G915TKL_RECEIVER_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G915TKL Wireless RGB Mechanical Gaming Keyboard (Wired)", DetectLogitechKeyboardG915Wired, LOGITECH_VID, LOGITECH_G915TKL_WIRED_PID, 2, 0xFF00, 2); +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Mice | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP ("Logitech G203 Prodigy", DetectLogitechMouseG203, LOGITECH_VID, LOGITECH_G203_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IPU("Logitech G203 Lightsync", DetectLogitechMouseG203L, LOGITECH_VID, LOGITECH_G203_LIGHTSYNC_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G203 Lightsync", DetectLogitechMouseG203L, LOGITECH_VID, LOGITECH_G203_LIGHTSYNC_PID_2, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IP ("Logitech G303 Daedalus Apex", DetectLogitechMouseG303, LOGITECH_VID, LOGITECH_G303_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP ("Logitech G403 HERO", DetectLogitechMouseG403, LOGITECH_VID, LOGITECH_G403_HERO_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP ("Logitech G600 Gaming Mouse", DetectLogitechMouseG600, LOGITECH_VID, LOGITECH_G600_PID, 1, 0xFF80); +REGISTER_HID_DETECTOR_IP ("Logitech G Pro Gaming Mouse", DetectLogitechMouseGPRO, LOGITECH_VID, LOGITECH_G_PRO_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP ("Logitech G Pro HERO Gaming Mouse", DetectLogitechMouseGPRO, LOGITECH_VID, LOGITECH_G_PRO_HERO_PID, 1, 0xFF00); +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Speakers | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Logitech G560 Lightsync Speaker", DetectLogitechG560, LOGITECH_VID, LOGITECH_G560_PID, 2, 0xFF43, 514); +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Headsets | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Logitech G933 Lightsync Headset", DetectLogitechG933, LOGITECH_VID, LOGITECH_G933_PID, 3, 0xFF43, 514); +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Joysticks | +| Older versions of the HOTAS have the controller on usage 1 however registering a IP detector resulted in duplicate detections on Linux | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Logitech X56 Rhino Hotas Joystick", DetectLogitechX56, LOGITECH_X56_VID, LOGITECH_X56_JOYSTICK_PID, 2, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Logitech X56 Rhino Hotas Throttle", DetectLogitechX56, LOGITECH_X56_VID, LOGITECH_X56_THROTTLE_PID, 2, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Logitech X56 Rhino Hotas Joystick", DetectLogitechX56, LOGITECH_X56_VID, LOGITECH_X56_JOYSTICK_PID, 2, 0xFF00, 3); +REGISTER_HID_DETECTOR_IPU("Logitech X56 Rhino Hotas Throttle", DetectLogitechX56, LOGITECH_X56_VID, LOGITECH_X56_THROTTLE_PID, 2, 0xFF00, 3); + + + + + +/*---------------------------------------------------------------------------------------------------------*\ +| Common Lightspeed Detection | +| | +\*---------------------------------------------------------------------------------------------------------*/ + +void CreateLogitechLightspeedDevice(char *path, usages device_usages, uint8_t device_index, uint16_t pid, bool wireless, std::shared_ptr mutex_ptr) +{ + LogitechLightspeedController* controller = new LogitechLightspeedController(device_usages.find(2)->second, path); + bool lightspeedDeviceIsValid = false; + int retryCount = 0; + + while (!lightspeedDeviceIsValid && retryCount < LOGITECH_LIGHTSPEED_DETECT_MAX_RETRY) + { + std::this_thread::sleep_for(50ms); + controller->lightspeed = new logitech_device(path, device_usages, device_index, wireless, mutex_ptr); + lightspeedDeviceIsValid = controller->lightspeed->is_valid(); + retryCount++; + } + + if (retryCount < LOGITECH_LIGHTSPEED_DETECT_MAX_RETRY) + { + RGBController_LogitechLightspeed* rgb_controller = new RGBController_LogitechLightspeed(controller); + rgb_controller->pid = pid; + ResourceManager::get()->RegisterRGBController(rgb_controller); + LOG_DEBUG("Added controller in %i retries", retryCount); + } + else + { + delete controller; + LOG_DEBUG("Failed to set up device - exceeded retries"); + } +} + +void DetectLogitechWired(hid_device_info* info, const std::string& /*name*/) +{ + /*-----------------------------------------------------------------*\ + | Wired lightspeed devices don't use the FAP short message | + | Be sure to specify a Page AND Usage when using this detector | + | i.e. REGISTER_HID_DETECTOR_IPU | + \*-----------------------------------------------------------------*/ + //char *path = info->path; + usages device_usages; + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LOG_DEBUG("Adding Usage %i for device @ path %s", info->usage, info->path); + device_usages.emplace((uint8_t)info->usage, dev); + } + else + { + LOG_DEBUG("Error opening Usage %i for device @ path %s", info->usage, info->path); + } + + if(device_usages.size() > 0) + { + CreateLogitechLightspeedDevice(info->path, device_usages, LOGITECH_DEFAULT_DEVICE_INDEX, info->product_id, false, nullptr); + } +} + +/*---------------------------------------------------------------------------------------------------------*\ +| Windows and MacOS Lightspeed Detection | +| | +| The Lightspeed receiver is a unifying receiver that will only accept 1 connection | +| We must probe the receiver to check what is currently connected | +| | +| Hat tip - kernel driver https://github.com/torvalds/linux/blob/master/drivers/hid/hid-logitech-dj.c | +| - ltunify https://github.com/Lekensteyn/ltunify/ | +\*---------------------------------------------------------------------------------------------------------*/ +#if defined(_WIN32) || defined(__APPLE__) + +usages BundleLogitechUsages(hid_device_info* info) +{ + /*-----------------------------------------------------------------*\ + | Need a unique ID to group usages for 1 device if multiple exist | + | Grab all usages that you can open. For normal Logitech FAP | + | devices this will be usage 1, 2 and 4 | + \*-----------------------------------------------------------------*/ + usages temp_usages; + + hid_device_info* temp_info = hid_enumerate(info->vendor_id, info->product_id); + while(temp_info) + { + /*-----------------------------------------------------------------*\ + | Only bundle the device that triggered this callback | + \*-----------------------------------------------------------------*/ + if(temp_info->interface_number == 2) + { + LOG_DEBUG("Attempting to open dev path: %s", info->path); + hid_device* dev = hid_open_path(temp_info->path); + + if(dev) + { + LOG_DEBUG("Success! Adding Usage %i for device @ path %s", temp_info->usage, temp_info->path); + temp_usages.emplace((uint8_t)temp_info->usage, dev); + } + else + { + LOG_INFO("FAILED! Can not add Usage %i for device @ path %s", temp_info->usage, temp_info->path); + } + } + temp_info = temp_info->next; + } + + return temp_usages; +} + +void DetectLogitechLightspeedReceiver(hid_device_info* info, const std::string& /*name*/) +{ + /*-----------------------------------------------------------------*\ + | Need to save the PID and the device path before iterating | + | over "info" in BundleLogitechUsages() | + \*-----------------------------------------------------------------*/ + char *path = info->path; + uint16_t dev_pid = info->product_id; + usages device_usages = BundleLogitechUsages(info); + + wireless_map wireless_devices; + unsigned int device_count = getWirelessDevice(device_usages, dev_pid, &wireless_devices); + + /*-----------------------------------------------------------------*\ + | Lightspeed Receivers will only have one paired /connected device | + | Unifying Receivers can have up to 6 devices paired / connected | + \*-----------------------------------------------------------------*/ + if(device_count > 0) + { + /*-------------------------------------------------*\ + | Create mutex to prevent the controllers sharing a | + | receiver from interfering with each other | + \*-------------------------------------------------*/ + std::shared_ptr logitech_mutex = std::make_shared(); + + for(wireless_map::iterator wd = wireless_devices.begin(); wd != wireless_devices.end(); wd++) + { + CreateLogitechLightspeedDevice(path, device_usages, wd->second, dev_pid, true, logitech_mutex); + } + } +} + +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Lightspeed Receivers (Windows Wireless) | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Logitech Lightspeed Receiver", DetectLogitechLightspeedReceiver, LOGITECH_VID, LOGITECH_G_LIGHTSPEED_RECEIVER_PID, 2, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Logitech G Powerplay Mousepad", DetectLogitechLightspeedReceiver, LOGITECH_VID, LOGITECH_G_LIGHTSPEED_POWERPLAY_PID, 2, 0xFF00, 1); + +#endif + +/*---------------------------------------------------------------------------------------------------------*\ +| Linux Lightspeed Detection | +| | +| The Linux kernel handles detecting wireless devices connected to a Unifying Receiver. | +\*---------------------------------------------------------------------------------------------------------*/ +#ifdef __linux__ + +void DetectLogitechWireless(hid_device_info* info, const std::string& /*name*/) +{ + /*-----------------------------------------------------------------*\ + | Wireless lightspeed devices on Linux are handled by the Kernel | + | and as such can largely be treated as Wired with the caveat | + | that they may not be connected | + \*-----------------------------------------------------------------*/ + //char *path = info->path; + usages device_usages; + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LOG_DEBUG("Adding Usage %i for device @ path %s", info->usage, info->path); + device_usages.emplace((uint8_t)info->usage, dev); + } + else + { + LOG_DEBUG("Error opening Usage %i for device @ path %s", info->usage, info->path); + } + + if(device_usages.size() > 0) + { + /*-------------------------------------------------*\ + | Create mutex to prevent the controllers sharing a | + | receiver from interfering with each other | + \*-------------------------------------------------*/ + std::shared_ptr logitech_mutex = std::make_shared(); + + CreateLogitechLightspeedDevice(info->path, device_usages, LOGITECH_DEFAULT_DEVICE_INDEX, info->product_id, true, logitech_mutex); + } +} + +/*--------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Lightspeed Devices (Linux Wireless) | +| | +| DUMMY_DEVICE_DETECTOR("Logitech G Lightspeed Receiver", DetectLogitechWireless, 0x046D, 0xC539 ) | +| DUMMY_DEVICE_DETECTOR("Logitech Powerplay Mat Receiver", DetectLogitechWireless, 0x046D, 0xC53A ) | +\*--------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Logitech G403 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G403_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G502 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G502_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G703 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G703_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G703 HERO Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G703_HERO_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G900 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G900_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G903 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G903_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G903 HERO Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G903_LIGHTSPEED_VIRTUAL_HERO_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G Pro Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G_PRO_WIRELESS_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech Powerplay Mat", DetectLogitechWireless, LOGITECH_VID, LOGITECH_POWERPLAY_MAT_VIRTUAL_PID, 2, 0xFF00, 2); + +#endif + +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Lightspeed Wireless Devices (Common Wired) | +| G502 changed to PU to accomodate old and new firmware. Other devices may require similar update #4627 | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_PU("Logitech G502 Wireless Gaming Mouse (wired)", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_LIGHTSPEED_PID, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G502 Proteus Spectrum Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_PROTEUS_SPECTRUM_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G502 HERO Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_HERO_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G403 Prodigy Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G403_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G403 Wireless Gaming Mouse (wired)", DetectLogitechWired, LOGITECH_VID, LOGITECH_G403_LIGHTSPEED_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G703 Wireless Gaming Mouse (wired)", DetectLogitechWired, LOGITECH_VID, LOGITECH_G703_LIGHTSPEED_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_PU("Logitech G703 HERO Wireless Gaming Mouse (wired)", DetectLogitechWired, LOGITECH_VID, LOGITECH_G703_HERO_LIGHTSPEED_PID, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G900 Wireless Gaming Mouse (wired)", DetectLogitechWired, LOGITECH_VID, LOGITECH_G900_LIGHTSPEED_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G903 Wireless Gaming Mouse (wired)", DetectLogitechWired, LOGITECH_VID, LOGITECH_G903_LIGHTSPEED_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G903 HERO Wireless Gaming Mouse (wired)", DetectLogitechWired, LOGITECH_VID, LOGITECH_G903_LIGHTSPEED_HERO_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G Pro Wireless Gaming Mouse (wired)", DetectLogitechWired, LOGITECH_VID, LOGITECH_G_PRO_WIRELESS_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G633 Gaming Headset", DetectLogitechWired, LOGITECH_VID, LOGITECH_G633_PID, 3, 0xFF43, 514); +REGISTER_HID_DETECTOR_IPU("Logitech G635 Gaming Headset", DetectLogitechWired, LOGITECH_VID, LOGITECH_G635_PID, 3, 0xFF43, 514); +REGISTER_HID_DETECTOR_IPU("Logitech G733 Gaming Headset", DetectLogitechWired, LOGITECH_VID, LOGITECH_G733_PID, 3, 0xFF43, 514); +REGISTER_HID_DETECTOR_IPU("Logitech G733 Gaming Headset", DetectLogitechWired, LOGITECH_VID, LOGITECH_G733_2_PID, 3, 0xFF43, 514); +REGISTER_HID_DETECTOR_IPU("Logitech G733 Gaming Headset", DetectLogitechWired, LOGITECH_VID, LOGITECH_G733_3_PID, 3, 0xFF43, 514); +REGISTER_HID_DETECTOR_IPU("Logitech G935 Gaming Headset", DetectLogitechWired, LOGITECH_VID, LOGITECH_G935_PID, 3, 0xFF43, 514); diff --git a/Controllers/LogitechController/LogitechG203LController/LogitechG203LController.cpp b/Controllers/LogitechController/LogitechG203LController/LogitechG203LController.cpp new file mode 100644 index 0000000..6710705 --- /dev/null +++ b/Controllers/LogitechController/LogitechG203LController/LogitechG203LController.cpp @@ -0,0 +1,224 @@ +/*---------------------------------------------------------*\ +| LogitechG203LController.cpp | +| | +| Driver for Logitech G203L | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechG203LController.h" +#include "StringUtils.h" + +#define PACKET_SIZE 20 + +LogitechG203LController::LogitechG203LController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + // enable software control + unsigned char usb_buf[PACKET_SIZE]; + + memset(usb_buf, 0x00, PACKET_SIZE); + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0E; + usb_buf[0x03] = 0x50; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = 0x03; + usb_buf[0x06] = 0x07; + + SendPacket(usb_buf); +} + +LogitechG203LController::~LogitechG203LController() +{ + if(dev != nullptr) + { + hid_close(dev); + } +} + +std::string LogitechG203LController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LogitechG203LController::GetNameString() +{ + return(name); +} + +std::string LogitechG203LController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LogitechG203LController::SendApply() +{ + unsigned char usb_buf[PACKET_SIZE]; + + memset(usb_buf, 0x00, PACKET_SIZE); + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x12; + usb_buf[0x03] = 0x70; + + SendPacket(usb_buf); +} + +void LogitechG203LController::SetSingleLED(int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char usb_buf[PACKET_SIZE]; + + memset(usb_buf, 0x00, PACKET_SIZE); + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x12; + usb_buf[0x03] = 0x10; + + usb_buf[0x04] = (unsigned char)led; + usb_buf[0x05] = red; + usb_buf[0x06] = green; + usb_buf[0x07] = blue; + + usb_buf[0x08] = 0xFF; + + SendPacket(usb_buf); + + SendApply(); +} + +void LogitechG203LController::SetMode( + int mode, + int speed, + unsigned char bright, + unsigned char dir, + unsigned char red, + unsigned char green, + unsigned char blue) +{ + unsigned char usb_buf[PACKET_SIZE]; + unsigned char brightness = bright * 5; + + if(brightness == 0) + { + brightness = 1; + } + + memset(usb_buf, 0x00, PACKET_SIZE); + + //Header + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0E; + usb_buf[0x03] = 0x10; + //Common Data + usb_buf[0x04] = 0x00; + usb_buf[0x05] = (unsigned char)mode; + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + //mode specific Data and position + if(mode == LOGITECH_G203L_MODE_STATIC) usb_buf[0x09] = 0x02; + if(mode == LOGITECH_G203L_MODE_CYCLE) + { + usb_buf[0x0B] = (unsigned char)((speed>>8) & 0x000000FF); + usb_buf[0x0C] = (unsigned char)(speed & 0x000000FF); + usb_buf[0x0D] = brightness; + } + if(mode == LOGITECH_G203L_MODE_BREATHING) + { + usb_buf[0x09] = (unsigned char)((speed>>8) & 0x000000FF); + usb_buf[0x0A] = (unsigned char)(speed & 0x000000FF); + usb_buf[0x0C] = brightness; + } + if(mode == LOGITECH_G203L_MODE_WAVE) + { + usb_buf[0x0C] = (unsigned char)(speed & 0x000000FF); + usb_buf[0x0D] = dir ? 0x01 : 0x06; //0x01: Left->Right 0x06: Right->Left + usb_buf[0x0E] = brightness; + usb_buf[0x0F] = (unsigned char)((speed>>8) & 0x000000FF); + } + if(mode == LOGITECH_G203L_MODE_COLORMIXING) + { + usb_buf[0x0C] = (unsigned char)(speed & 0x000000FF); + usb_buf[0x0D] = (unsigned char)((speed>>8) & 0x000000FF); + usb_buf[0x0E] = brightness; + } + + //END BYTE + usb_buf[0x10] = 0x01; + + SendPacket(usb_buf); +} + +void LogitechG203LController::SetDevice(std::vector colors) +{ + unsigned char usb_buf[PACKET_SIZE]; + + memset(usb_buf, 0x00, PACKET_SIZE); + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x12; + usb_buf[0x03] = 0x10; + + usb_buf[0x04] = 0x01; + usb_buf[0x05] = RGBGetRValue(colors[0]); + usb_buf[0x06] = RGBGetGValue(colors[0]); + usb_buf[0x07] = RGBGetBValue(colors[0]); + + usb_buf[0x08] = 0x02; + usb_buf[0x09] = RGBGetRValue(colors[1]); + usb_buf[0x0A] = RGBGetGValue(colors[1]); + usb_buf[0x0B] = RGBGetBValue(colors[1]); + + usb_buf[0x0C] = 0x03; + usb_buf[0x0D] = RGBGetRValue(colors[2]); + usb_buf[0x0E] = RGBGetGValue(colors[2]); + usb_buf[0x0F] = RGBGetBValue(colors[2]); + + usb_buf[0x10] = 0xFF; + + SendPacket(usb_buf); + + SendApply(); +} + +void LogitechG203LController::SendPacket(unsigned char* buffer) +{ + if(dev != nullptr) + { + if(hid_write(dev, buffer, PACKET_SIZE) == -1) + { + hid_close(dev); + dev = hid_open_path(location.c_str()); + return; + } + } + + if(dev != nullptr) + { + if(hid_read_timeout(dev, buffer, PACKET_SIZE, 10) <= 0) + { + hid_close(dev); + dev = hid_open_path(location.c_str()); + return; + } + } +} diff --git a/Controllers/LogitechController/LogitechG203LController/LogitechG203LController.h b/Controllers/LogitechController/LogitechG203LController/LogitechG203LController.h new file mode 100644 index 0000000..cea966f --- /dev/null +++ b/Controllers/LogitechController/LogitechG203LController/LogitechG203LController.h @@ -0,0 +1,48 @@ +/*---------------------------------------------------------*\ +| LogitechG203LController.h | +| | +| Driver for Logitech G203L | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + LOGITECH_G203L_MODE_DIRECT = 0x07, + LOGITECH_G203L_MODE_OFF = 0x00, + LOGITECH_G203L_MODE_STATIC = 0x01, + LOGITECH_G203L_MODE_CYCLE = 0x02, + LOGITECH_G203L_MODE_WAVE = 0x03, + LOGITECH_G203L_MODE_BREATHING = 0x04, + LOGITECH_G203L_MODE_COLORMIXING = 0x06, +}; + +class LogitechG203LController +{ +public: + LogitechG203LController(hid_device* dev_handle, const char* path, std::string dev_name); + ~LogitechG203LController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetSingleLED(int led, unsigned char red, unsigned char green, unsigned char blue); + void SetMode(int mode, int speed, unsigned char brightness, unsigned char dir, unsigned char red, unsigned char green, unsigned char blue); + void SetDevice(std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendApply(); + void SendPacket(unsigned char* buffer); +}; diff --git a/Controllers/LogitechController/LogitechG203LController/RGBController_LogitechG203L.cpp b/Controllers/LogitechController/LogitechG203LController/RGBController_LogitechG203L.cpp new file mode 100644 index 0000000..e5d838d --- /dev/null +++ b/Controllers/LogitechController/LogitechG203LController/RGBController_LogitechG203L.cpp @@ -0,0 +1,202 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG203L.cpp | +| | +| Driver for Logitech G203L | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LogitechG203L.h" + +/**------------------------------------------------------------------*\ + @name Logitech G203L + @category Mouse + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechMouseG203L + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechG203L::RGBController_LogitechG203L(LogitechG203LController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_MOUSE; + description = "Logitech Mouse Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LOGITECH_G203L_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G203L_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = LOGITECH_G203L_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Cycle; + Cycle.name = "Cycle"; + Cycle.value = LOGITECH_G203L_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = 0x4E20; + Cycle.speed_max = 0x03E8; + Cycle.brightness = 20; + Cycle.brightness_min = 0; + Cycle.brightness_max = 20; + modes.push_back(Cycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G203L_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed_min = 0x4E20; + Breathing.speed_max = 0x03E8; + Breathing.brightness = 20; + Breathing.brightness_min = 0; + Breathing.brightness_max = 20; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Wave; + Wave.name = "Wave"; + Wave.value = LOGITECH_G203L_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Wave.color_mode = MODE_COLORS_NONE; + Wave.speed_min = 0x4E20; + Wave.speed_max = 0x03E8; + Wave.brightness = 20; + Wave.brightness_min = 0; + Wave.brightness_max = 20; + modes.push_back(Wave); + + mode Colormixing; + Colormixing.name = "Colormixing"; + Colormixing.value = LOGITECH_G203L_MODE_COLORMIXING; + Colormixing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Colormixing.color_mode = MODE_COLORS_NONE; + Colormixing.speed_min = 0x4E20; + Colormixing.speed_max = 0x03E8; + Colormixing.brightness = 20; + Colormixing.brightness_min = 0; + Colormixing.brightness_max = 20; + modes.push_back(Colormixing); + + SetupZones(); +} + +RGBController_LogitechG203L::~RGBController_LogitechG203L() +{ + delete controller; +} + +void RGBController_LogitechG203L::SetupZones() +{ + zone g203L_zone; + g203L_zone.name = "Mouse Zone"; + g203L_zone.type = ZONE_TYPE_LINEAR; + g203L_zone.leds_min = 3; + g203L_zone.leds_max = 3; + g203L_zone.leds_count = 3; + g203L_zone.matrix_map = NULL; + zones.push_back(g203L_zone); + + led g203L_led_l; + g203L_led_l.name = "Mouse Left"; + g203L_led_l.value = 1; + leds.push_back(g203L_led_l); + + led g203L_led_c; + g203L_led_c.name = "Mouse Center"; + g203L_led_c.value = 2; + leds.push_back(g203L_led_c); + + led g203L_led_r; + g203L_led_r.name = "Mouse Right"; + g203L_led_r.value = 3; + leds.push_back(g203L_led_r); + + SetupColors(); +} + +void RGBController_LogitechG203L::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechG203L::DeviceUpdateLEDs() +{ + controller->SetDevice(colors); + controller->SetDevice(colors); //dirty workaround for color lag +} + +void RGBController_LogitechG203L::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG203L::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + controller->SetSingleLED(leds[led].value, red, grn, blu); + controller->SetSingleLED(leds[led].value, red, grn, blu); //dirty workaround for color lag +} + +void RGBController_LogitechG203L::DeviceUpdateMode() +{ + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + unsigned char dir = 0; + + if(modes[active_mode].color_mode & MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + dir = (unsigned char)modes[active_mode].direction; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + //dunno where brightness is + } + + if(modes[active_mode].value == LOGITECH_G203L_MODE_DIRECT) + { + controller->SetDevice(colors); + } + else + { + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, dir, red, grn, blu); + } +} diff --git a/Controllers/LogitechController/LogitechG203LController/RGBController_LogitechG203L.h b/Controllers/LogitechController/LogitechG203LController/RGBController_LogitechG203L.h new file mode 100644 index 0000000..29539d6 --- /dev/null +++ b/Controllers/LogitechController/LogitechG203LController/RGBController_LogitechG203L.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG203L.h | +| | +| Driver for Logitech G203L | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechG203LController.h" + +class RGBController_LogitechG203L : public RGBController +{ +public: + RGBController_LogitechG203L(LogitechG203LController* controller_ptr); + ~RGBController_LogitechG203L(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechG203LController* controller; +}; diff --git a/Controllers/LogitechController/LogitechG213Controller/LogitechG213Controller.cpp b/Controllers/LogitechController/LogitechG213Controller/LogitechG213Controller.cpp new file mode 100644 index 0000000..8005a87 --- /dev/null +++ b/Controllers/LogitechController/LogitechG213Controller/LogitechG213Controller.cpp @@ -0,0 +1,160 @@ +/*---------------------------------------------------------*\ +| LogitechG203LController.cpp | +| | +| Driver for Logitech G203L | +| | +| Eric Samuelson (edbgon) 06 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechG213Controller.h" +#include "StringUtils.h" + +LogitechG213Controller::LogitechG213Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +LogitechG213Controller::~LogitechG213Controller() +{ + hid_close(dev); +} + +std::string LogitechG213Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LogitechG213Controller::GetNameString() +{ + return(name); +} + +std::string LogitechG213Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LogitechG213Controller::SetDirect + ( + unsigned char zone, + unsigned char r, + unsigned char g, + unsigned char b + ) +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0C; + usb_buf[0x03] = 0x3A; + usb_buf[0x04] = zone; + usb_buf[0x05] = 0x01; + usb_buf[0x06] = r; + usb_buf[0x07] = g; + usb_buf[0x08] = b; + usb_buf[0x09] = 0x02; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 20); + hid_read(dev, usb_buf, 20); +} + +void LogitechG213Controller::SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned char direction, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SendMode(LOGITECH_G213_ZONE_MODE_KEYBOARD, mode, speed, direction, red, green, blue); +} + +void LogitechG213Controller::SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned char direction, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0C; + usb_buf[0x03] = 0x3C; + usb_buf[0x04] = zone; + + usb_buf[0x05] = mode; + + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + + speed = 100 * speed; + + if(mode == LOGITECH_G213_MODE_BREATHING) + { + usb_buf[0x09] = speed >> 8; + usb_buf[0x0A] = speed & 0xFF; + usb_buf[0x0C] = 0x64; + } + else if(mode == LOGITECH_G213_MODE_CYCLE) + { + usb_buf[0x0B] = speed >> 8; + usb_buf[0x0C] = speed & 0xFF; + usb_buf[0x0D] = 0x64; + } + else if(mode == LOGITECH_G213_MODE_WAVE) + { + usb_buf[0x0B] = speed >> 8; + usb_buf[0x0C] = speed & 0xFF; + usb_buf[0x0D] = direction & 0xFF; + usb_buf[0x0F] = speed >> 8; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 20); + hid_read(dev, usb_buf, 20); +} diff --git a/Controllers/LogitechController/LogitechG213Controller/LogitechG213Controller.h b/Controllers/LogitechController/LogitechG213Controller/LogitechG213Controller.h new file mode 100644 index 0000000..4a9f3b9 --- /dev/null +++ b/Controllers/LogitechController/LogitechG213Controller/LogitechG213Controller.h @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| LogitechG203LController.h | +| | +| Driver for Logitech G203L | +| | +| Eric Samuelson (edbgon) 06 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController.h" + +#include +#include + +#pragma once + +enum +{ + LOGITECH_G213_ZONE_MODE_KEYBOARD = 0x00 +}; + +enum +{ + LOGITECH_G213_MODE_OFF = 0x00, + LOGITECH_G213_MODE_STATIC = 0x01, + LOGITECH_G213_MODE_BREATHING = 0x02, + LOGITECH_G213_MODE_CYCLE = 0x03, + LOGITECH_G213_MODE_WAVE = 0x04, +}; + +enum +{ + LOGITECH_G213_WAVE_MODE_LEFT = 0x06, + LOGITECH_G213_WAVE_MODE_RIGHT = 0x01, + LOGITECH_G213_WAVE_MODE_CENTER_EDGE = 0x03, + LOGITECH_G213_WAVE_MODE_EDGE_CENTER = 0x08, +}; + +/*---------------------------------------------------------------------------------------------*\ +| Speed is 1000 for fast and 20000 for slow. | +| Values are multiplied by 100 later to give lots of GUI steps. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_G213_SPEED_SLOWEST = 0xC8, /* Slowest speed */ + LOGITECH_G213_SPEED_NORMAL = 0x32, /* Normal speed */ + LOGITECH_G213_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +class LogitechG213Controller +{ +public: + LogitechG213Controller(hid_device* dev_handle, const char* path, std::string dev_name); + ~LogitechG213Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetDirect + ( + unsigned char zone, + unsigned char r, + unsigned char g, + unsigned char b + ); + + void SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned char direction, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned char direction, + unsigned char red, + unsigned char green, + unsigned char blue + ); +}; diff --git a/Controllers/LogitechController/LogitechG213Controller/RGBController_LogitechG213.cpp b/Controllers/LogitechController/LogitechG213Controller/RGBController_LogitechG213.cpp new file mode 100644 index 0000000..7ff3f0a --- /dev/null +++ b/Controllers/LogitechController/LogitechG213Controller/RGBController_LogitechG213.cpp @@ -0,0 +1,209 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG213.cpp | +| | +| RGBController for Logitech G203L | +| | +| Eric Samuelson (edbgon) 06 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LogitechG213.h" + +static const char* led_names[] = +{ + "Left Area", + "Middle Area", + "Right Area", + "Arrow and Homekeys", + "Numpad", +}; + +static const unsigned char led_values[] = +{ + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, +}; + +#define LOGITECH_G213_ZONES (sizeof(led_values) / sizeof(led_values[ 0 ])) + +/**------------------------------------------------------------------*\ + @name Logitech G213 + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechKeyboardG213 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechG213::RGBController_LogitechG213(LogitechG213Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_KEYBOARD; + description = "Logitech G213 Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G213_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Cycle; + Cycle.name = "Cycle"; + Cycle.value = LOGITECH_G213_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_G213_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G213_SPEED_FASTEST; + Cycle.speed = LOGITECH_G213_SPEED_NORMAL; + modes.push_back(Cycle); + + mode Wave; + Wave.name = "Wave"; + Wave.value = LOGITECH_G213_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD; + Wave.color_mode = MODE_COLORS_NONE; + Wave.speed_min = LOGITECH_G213_SPEED_SLOWEST; + Wave.speed_max = LOGITECH_G213_SPEED_FASTEST; + Wave.speed = LOGITECH_G213_SPEED_NORMAL; + Wave.direction = MODE_DIRECTION_LEFT; + modes.push_back(Wave); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G213_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + Breathing.speed_min = LOGITECH_G213_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G213_SPEED_FASTEST; + Breathing.speed = LOGITECH_G213_SPEED_NORMAL; + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_LogitechG213::~RGBController_LogitechG213() +{ + delete controller; +} + +void RGBController_LogitechG213::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = "Keyboard"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 5; + new_zone.leds_max = 5; + new_zone.leds_count = 5; + + new_zone.matrix_map = NULL; + + zones.push_back(new_zone); + + for(unsigned int led_idx = 0; led_idx < LOGITECH_G213_ZONES; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + new_led.value = led_values[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_LogitechG213::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechG213::DeviceUpdateLEDs() +{ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + controller->SetDirect((unsigned char)leds[led_idx].value, RGBGetRValue(colors[led_idx]), RGBGetGValue(colors[led_idx]), RGBGetBValue(colors[led_idx])); + } +} + +void RGBController_LogitechG213::UpdateZoneLEDs(int zone) +{ + controller->SetDirect((unsigned char) zone, RGBGetRValue(zones[zone].colors[0]), RGBGetGValue(zones[zone].colors[0]), RGBGetBValue(zones[zone].colors[0])); +} + +void RGBController_LogitechG213::UpdateSingleLED(int led) +{ + controller->SetDirect(leds[led].value, RGBGetRValue(colors[led]), RGBGetGValue(colors[led]), RGBGetBValue(colors[led])); +} + +void RGBController_LogitechG213::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Direct mode does not send a mode packet | + | Call UpdateLEDs to send direct packet | + \*---------------------------------------------------------*/ + if(active_mode == 0xFFFF) + { + UpdateLEDs(); + return; + } + + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + unsigned char direction = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + switch (modes[active_mode].direction) + { + case MODE_DIRECTION_LEFT: + // Right to left + direction = LOGITECH_G213_WAVE_MODE_LEFT; + break; + case MODE_DIRECTION_RIGHT: + // Left to right + direction = LOGITECH_G213_WAVE_MODE_RIGHT; + break; + case MODE_DIRECTION_UP: + // Edge to center + direction = LOGITECH_G213_WAVE_MODE_EDGE_CENTER; + break; + case MODE_DIRECTION_DOWN: + // Center to edge + direction = LOGITECH_G213_WAVE_MODE_CENTER_EDGE; + break; + } + + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, direction, red, grn, blu); +} diff --git a/Controllers/LogitechController/LogitechG213Controller/RGBController_LogitechG213.h b/Controllers/LogitechController/LogitechG213Controller/RGBController_LogitechG213.h new file mode 100644 index 0000000..c84b044 --- /dev/null +++ b/Controllers/LogitechController/LogitechG213Controller/RGBController_LogitechG213.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG213.h | +| | +| RGBController for Logitech G203L | +| | +| Eric Samuelson (edbgon) 06 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechG213Controller.h" + +class RGBController_LogitechG213 : public RGBController +{ +public: + RGBController_LogitechG213(LogitechG213Controller* controller_ptr); + ~RGBController_LogitechG213(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechG213Controller* controller; +}; diff --git a/Controllers/LogitechController/LogitechG560Controller/LogitechG560Controller.cpp b/Controllers/LogitechController/LogitechG560Controller/LogitechG560Controller.cpp new file mode 100644 index 0000000..09f43f5 --- /dev/null +++ b/Controllers/LogitechController/LogitechG560Controller/LogitechG560Controller.cpp @@ -0,0 +1,154 @@ +/*---------------------------------------------------------*\ +| LogitechG560Controller.cpp | +| | +| Driver for Logitech G560 | +| | +| Cheerpipe 28 Oct 2020 | +| based on TheRogueZeta 31 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "LogitechG560Controller.h" + +using namespace std::chrono_literals; + +LogitechG560Controller::LogitechG560Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +LogitechG560Controller::~LogitechG560Controller() +{ + hid_close(dev); +} + +std::string LogitechG560Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LogitechG560Controller::GetDeviceName() +{ + return(name); +} + +void LogitechG560Controller::SetDirectMode(uint8_t zone) +{ + unsigned char usb_buf[LOGI_G560_LED_PACKET_SIZE]; + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x04; + usb_buf[0x03] = 0xCA; + usb_buf[0x04] = zone; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + fail_retry_write(dev, usb_buf, LOGI_G560_LED_PACKET_SIZE); +} + +void LogitechG560Controller::SetOffMode(uint8_t zone) +{ + unsigned char usb_buf[LOGI_G560_LED_PACKET_SIZE]; + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x04; + usb_buf[0x03] = 0x3F; + usb_buf[0x04] = zone; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + fail_retry_write(dev, usb_buf, LOGI_G560_LED_PACKET_SIZE); +} + +void LogitechG560Controller::SendSpeakerMode + ( + unsigned char zone, + unsigned char mode, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[LOGI_G560_LED_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x04; + + /*-----------------------------------------------------*\ + | This packet sets speaker into direct mode. This mode | + | is used by Lightsync Ambilight and Music Visualizer | + | realtime effect. | + \*-----------------------------------------------------*/ + usb_buf[0x03] = 0x3A; + + /*-----------------------------------------------------*\ + | Set up mode and speed | + \*-----------------------------------------------------*/ + usb_buf[0x04] = zone; + usb_buf[0x05] = mode; + + /*-----------------------------------------------------*\ + | And set up the colors | + \*-----------------------------------------------------*/ + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + + if(mode == LOGITECH_G560_MODE_DIRECT) //G560 only has Direct Mode. + { + usb_buf[0x09] = 0x02; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + fail_retry_write(dev, usb_buf, LOGI_G560_LED_PACKET_SIZE); +} + +void LogitechG560Controller::fail_retry_write(hid_device *device, const unsigned char *data, size_t length) +{ + unsigned char usb_buf_out[LOGI_G560_LED_PACKET_SIZE]; + unsigned int write_max_retry = LOGI_G560_LED_COMMAND_SEND_RETRIES; + do + { + std::this_thread::sleep_for(1ms); + int ret = hid_write(device, data, length); + + /*-------------------------------------------------------------------------------------*\ + | HID write fails if a change led color and set volume command are sent at | + | the same time because RGB controller and volume control shares the same interface. | + \*-------------------------------------------------------------------------------------*/ + if(ret == 20) + { + std::this_thread::sleep_for(1ms); + hid_read_timeout(dev, usb_buf_out, LOGI_G560_LED_PACKET_SIZE, 20); + break; + } + else + { + write_max_retry--; + std::this_thread::sleep_for(10ms); + } + + }while (write_max_retry > 0); +} diff --git a/Controllers/LogitechController/LogitechG560Controller/LogitechG560Controller.h b/Controllers/LogitechController/LogitechG560Controller/LogitechG560Controller.h new file mode 100644 index 0000000..dbc34e3 --- /dev/null +++ b/Controllers/LogitechController/LogitechG560Controller/LogitechG560Controller.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| LogitechG560Controller.h | +| | +| Driver for Logitech G560 | +| | +| Cheerpipe 28 Oct 2020 | +| based on TheRogueZeta 31 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define LOGI_G560_LED_PACKET_SIZE 20 +#define LOGI_G560_LED_COMMAND_SEND_RETRIES 3 + +enum +{ + LOGITECH_G560_MODE_OFF = 0x00, + LOGITECH_G560_MODE_DIRECT = 0x01, + LOGITECH_G560_MODE_CYCLE = 0x02, + LOGITECH_G560_MODE_BREATHING = 0x03, +}; + +class LogitechG560Controller +{ +public: + LogitechG560Controller(hid_device* dev_handle, const char* path, std::string dev_name); + ~LogitechG560Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetDirectMode(uint8_t zone); + void SetOffMode(uint8_t zone); + + void SendSpeakerMode + ( + unsigned char zone, + unsigned char mode, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev; + std::string location; + std::string name; + + void fail_retry_write(hid_device *device, const unsigned char *data, size_t length); +}; + + diff --git a/Controllers/LogitechController/LogitechG560Controller/RGBController_LogitechG560.cpp b/Controllers/LogitechController/LogitechG560Controller/RGBController_LogitechG560.cpp new file mode 100644 index 0000000..2906336 --- /dev/null +++ b/Controllers/LogitechController/LogitechG560Controller/RGBController_LogitechG560.cpp @@ -0,0 +1,164 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG560.cpp | +| | +| RGBController for Logitech G560 | +| | +| Cheerpipe 28 Oct 2020 | +| based on TheRogueZeta 31 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LogitechG560.h" + +/**------------------------------------------------------------------*\ + @name Logitech G560 + @category Speaker + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectLogitechG560 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechG560::RGBController_LogitechG560(LogitechG560Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Logitech"; + type = DEVICE_TYPE_SPEAKER; + description = "Logitech G560 Lightsync Speaker"; + location = controller->GetDeviceLocation(); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G560_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LOGITECH_G560_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + SetupZones(); +} + +void RGBController_LogitechG560::SetupZones() +{ + zone G560_left_front; + G560_left_front.name = "Left Front"; + G560_left_front.type = ZONE_TYPE_SINGLE; + G560_left_front.leds_min = 1; + G560_left_front.leds_max = 1; + G560_left_front.leds_count = 1; + G560_left_front.matrix_map = NULL; + zones.push_back(G560_left_front); + + led G560_left_front_led; + G560_left_front_led.name = "Left Front"; + G560_left_front_led.value = 0x00; + leds.push_back(G560_left_front_led); + + + zone G560_right_front; + G560_right_front.name = "Right Front"; + G560_right_front.type = ZONE_TYPE_SINGLE; + G560_right_front.leds_min = 1; + G560_right_front.leds_max = 1; + G560_right_front.leds_count = 1; + G560_right_front.matrix_map = NULL; + zones.push_back(G560_right_front); + + led G560_right_front_led; + G560_right_front_led.name = "Right Front"; + G560_right_front_led.value = 0x01; + leds.push_back(G560_right_front_led); + + + zone G560_left_rear; + G560_left_rear.name = "Left Rear"; + G560_left_rear.type = ZONE_TYPE_SINGLE; + G560_left_rear.leds_min = 1; + G560_left_rear.leds_max = 1; + G560_left_rear.leds_count = 1; + G560_left_rear.matrix_map = NULL; + zones.push_back(G560_left_rear); + + led G560_left_read_led; + G560_left_read_led.name = "Left Rear"; + G560_left_read_led.value = 0x02; + leds.push_back(G560_left_read_led); + + + zone G560_right_rear; + G560_right_rear.name = "Right Rear"; + G560_right_rear.type = ZONE_TYPE_SINGLE; + G560_right_rear.leds_min = 1; + G560_right_rear.leds_max = 1; + G560_right_rear.leds_count = 1; + G560_right_rear.matrix_map = NULL; + zones.push_back(G560_right_rear); + + led G560_right_rear_led; + G560_right_rear_led.name = "Right Rear"; + G560_right_rear_led.value = 0x03; + leds.push_back(G560_right_rear_led); + + SetupColors(); +} + +void RGBController_LogitechG560::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechG560::DeviceUpdateLEDs() +{ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned char red = RGBGetRValue(colors[led_idx]); + unsigned char grn = RGBGetGValue(colors[led_idx]); + unsigned char blu = RGBGetBValue(colors[led_idx]); + + controller->SendSpeakerMode((unsigned char)leds[led_idx].value, modes[active_mode].value, red, grn, blu); + } +} + +void RGBController_LogitechG560::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG560::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG560::DeviceUpdateMode() +{ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + if(modes[active_mode].value == LOGITECH_G560_MODE_OFF) + { + controller->SetOffMode(leds[led_idx].value); + } + else + { + /*---------------------------------------------------------*\ + | Required to "reset" RGB controller and start receiving | + | color in direct mode | + \*---------------------------------------------------------*/ + controller->SetDirectMode(leds[led_idx].value); + } + + } + DeviceUpdateLEDs(); +} diff --git a/Controllers/LogitechController/LogitechG560Controller/RGBController_LogitechG560.h b/Controllers/LogitechController/LogitechG560Controller/RGBController_LogitechG560.h new file mode 100644 index 0000000..3f99b2c --- /dev/null +++ b/Controllers/LogitechController/LogitechG560Controller/RGBController_LogitechG560.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG560.h | +| | +| RGBController for Logitech G560 | +| | +| Cheerpipe 28 Oct 2020 | +| based on TheRogueZeta 31 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechG560Controller.h" + +class RGBController_LogitechG560 : public RGBController +{ +public: + RGBController_LogitechG560(LogitechG560Controller* controller_ptr); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechG560Controller* controller; +}; diff --git a/Controllers/LogitechController/LogitechG600Controller/LogitechG600Controller.cpp b/Controllers/LogitechController/LogitechG600Controller/LogitechG600Controller.cpp new file mode 100644 index 0000000..3ed1fc4 --- /dev/null +++ b/Controllers/LogitechController/LogitechG600Controller/LogitechG600Controller.cpp @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| LogitechG600Controller.cpp | +| | +| Driver for Logitech G600 Gaming Mouse | +| | +| Austin B (austinleroy) 11 Sep 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechG600Controller.h" +#include "StringUtils.h" + +LogitechG600Controller::LogitechG600Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +LogitechG600Controller::~LogitechG600Controller() +{ + if(dev != nullptr) + { + hid_close(dev); + } +} + +std::string LogitechG600Controller::GetNameString() +{ + return(name); +} + +std::string LogitechG600Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LogitechG600Controller::SetMode + ( + unsigned char mode, + unsigned short speed, + RGBColor color + ) +{ + unsigned char usb_buf[8]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0xF1; + usb_buf[0x01] = RGBGetRValue(color); + usb_buf[0x02] = RGBGetGValue(color); + usb_buf[0x03] = RGBGetBValue(color); + usb_buf[0x04] = mode; + usb_buf[0x05] = speed & 0xFF; + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} diff --git a/Controllers/LogitechController/LogitechG600Controller/LogitechG600Controller.h b/Controllers/LogitechController/LogitechG600Controller/LogitechG600Controller.h new file mode 100644 index 0000000..8fd85f5 --- /dev/null +++ b/Controllers/LogitechController/LogitechG600Controller/LogitechG600Controller.h @@ -0,0 +1,56 @@ +/*---------------------------------------------------------*\ +| LogitechG600Controller.h | +| | +| Driver for Logitech G600 Gaming Mouse | +| | +| Austin B (austinleroy) 11 Sep 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + + +enum +{ + LOGITECH_G600_MODE_DIRECT = 0x00, + LOGITECH_G600_MODE_BREATHING = 0x01, + LOGITECH_G600_MODE_CYCLE = 0x02 +}; + +/*---------------------------------------------------------------------------------------------*\ +| Speed is number of seconds for cycle to complete. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_G600_SPEED_SLOWEST = 0x0F, /* Slowest speed */ + LOGITECH_G600_SPEED_NORMAL = 0x03, /* Normal speed */ + LOGITECH_G600_SPEED_FASTEST = 0x01, /* Fastest speed */ +}; + +class LogitechG600Controller +{ +public: + LogitechG600Controller(hid_device* dev, const char* path, std::string dev_name); + ~LogitechG600Controller(); + + std::string GetNameString(); + std::string GetSerialString(); + + void SetMode + ( + unsigned char mode, + unsigned short speed, + RGBColor color + ); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/LogitechController/LogitechG600Controller/RGBController_LogitechG600.cpp b/Controllers/LogitechController/LogitechG600Controller/RGBController_LogitechG600.cpp new file mode 100644 index 0000000..a9cefe8 --- /dev/null +++ b/Controllers/LogitechController/LogitechG600Controller/RGBController_LogitechG600.cpp @@ -0,0 +1,114 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG600.cpp | +| | +| RGBController for Logitech G600 Gaming Mouse | +| | +| Austin B (austinleroy) 11 Sep 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LogitechG600.h" + +/**------------------------------------------------------------------*\ + @name Logitech G600 + @category Mouse + @type USB + @save :o: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechMouseG600 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechG600::RGBController_LogitechG600(LogitechG600Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_MOUSE; + description = "Logitech Mouse Device"; + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LOGITECH_G600_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G600_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = LOGITECH_G600_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G600_SPEED_FASTEST; + Breathing.speed = LOGITECH_G600_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = LOGITECH_G600_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_G600_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G600_SPEED_FASTEST; + Cycle.speed = LOGITECH_G600_SPEED_NORMAL; + modes.push_back(Cycle); + + SetupZones(); +} + +RGBController_LogitechG600::~RGBController_LogitechG600() +{ + delete controller; +} + +void RGBController_LogitechG600::SetupZones() +{ + zone side_lights_zone; + side_lights_zone.name = "Side Lights"; + side_lights_zone.type = ZONE_TYPE_SINGLE; + side_lights_zone.leds_min = 1; + side_lights_zone.leds_max = 1; + side_lights_zone.leds_count = 1; + side_lights_zone.matrix_map = NULL; + zones.push_back(side_lights_zone); + + // Set up LED + led g600_led; + g600_led.name = "All"; + leds.push_back(g600_led); + + SetupColors(); +} + +void RGBController_LogitechG600::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | Currently does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechG600::DeviceUpdateLEDs() +{ + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, GetLED(0)); +} + +void RGBController_LogitechG600::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG600::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG600::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, GetLED(0)); +} diff --git a/Controllers/LogitechController/LogitechG600Controller/RGBController_LogitechG600.h b/Controllers/LogitechController/LogitechG600Controller/RGBController_LogitechG600.h new file mode 100644 index 0000000..0f47809 --- /dev/null +++ b/Controllers/LogitechController/LogitechG600Controller/RGBController_LogitechG600.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG600.h | +| | +| RGBController for Logitech G600 Gaming Mouse | +| | +| Austin B (austinleroy) 11 Sep 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechG600Controller.h" + +class RGBController_LogitechG600 : public RGBController +{ +public: + RGBController_LogitechG600(LogitechG600Controller* controller_ptr); + ~RGBController_LogitechG600(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechG600Controller* controller; +}; diff --git a/Controllers/LogitechController/LogitechG810Controller/LogitechG810Controller.cpp b/Controllers/LogitechController/LogitechG810Controller/LogitechG810Controller.cpp new file mode 100644 index 0000000..bed001f --- /dev/null +++ b/Controllers/LogitechController/LogitechG810Controller/LogitechG810Controller.cpp @@ -0,0 +1,192 @@ +/*---------------------------------------------------------*\ +| LogitechG810Controller.cpp | +| | +| Driver for Logitech G810 Orion Spectrum | +| | +| Adam Honse (CalcProgrammer1) 11 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechG810Controller.h" +#include "StringUtils.h" + +LogitechG810Controller::LogitechG810Controller(hid_device* dev_handle_0x11, hid_device* dev_handle_0x12, std::string dev_name) +{ + dev_pkt_0x11 = dev_handle_0x11; + dev_pkt_0x12 = dev_handle_0x12; + name = dev_name; +} + +LogitechG810Controller::~LogitechG810Controller() +{ + hid_close(dev_pkt_0x11); + hid_close(dev_pkt_0x12); +} + +std::string LogitechG810Controller::GetNameString() +{ + return(name); +} + +std::string LogitechG810Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev_pkt_0x11, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LogitechG810Controller::Commit() +{ + SendCommit(); +} + +void LogitechG810Controller::SetDirect + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ) +{ + SendDirectFrame(zone, frame_count, frame_data); +} + +void LogitechG810Controller::SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SendMode(LOGITECH_G810_ZONE_MODE_KEYBOARD, mode, speed, red, green, blue); + SendMode(LOGITECH_G810_ZONE_MODE_LOGO, mode, speed, red, green, blue); + + SendCommit(); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void LogitechG810Controller::SendCommit() +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0C; + usb_buf[0x03] = 0x5D; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); +} + +void LogitechG810Controller::SendDirectFrame + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ) +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x12; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0C; + usb_buf[0x03] = 0x3D; + usb_buf[0x05] = zone; + usb_buf[0x07] = frame_count; + + /*-----------------------------------------------------*\ + | Copy in frame data | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x08], frame_data, frame_count * 4); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x12, usb_buf, 64); + hid_read(dev_pkt_0x11, usb_buf, 20); +} + +void LogitechG810Controller::SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0D; + usb_buf[0x03] = 0x3D; + usb_buf[0x04] = zone; + + usb_buf[0x05] = mode; + + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + + speed = 100 * speed; + if(mode == LOGITECH_G810_MODE_CYCLE) + { + usb_buf[0x0B] = speed >> 8; + usb_buf[0x0C] = speed & 0xFF; + usb_buf[0x0D] = 0x64; + } + else if(mode == LOGITECH_G810_MODE_BREATHING) + { + usb_buf[0x09] = speed >> 8; + usb_buf[0x0A] = speed & 0xFF; + usb_buf[0x0C] = 0x64; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); +} diff --git a/Controllers/LogitechController/LogitechG810Controller/LogitechG810Controller.h b/Controllers/LogitechController/LogitechG810Controller/LogitechG810Controller.h new file mode 100644 index 0000000..6424a10 --- /dev/null +++ b/Controllers/LogitechController/LogitechG810Controller/LogitechG810Controller.h @@ -0,0 +1,102 @@ +/*---------------------------------------------------------*\ +| LogitechG810Controller.h | +| | +| Driver for Logitech G810 Orion Spectrum | +| | +| Adam Honse (CalcProgrammer1) 11 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + LOGITECH_G810_ZONE_MODE_KEYBOARD = 0x00, + LOGITECH_G810_ZONE_MODE_LOGO = 0x01, +}; + +enum +{ + LOGITECH_G810_ZONE_DIRECT_KEYBOARD = 0x01, + LOGITECH_G810_ZONE_DIRECT_MEDIA = 0x02, + LOGITECH_G810_ZONE_DIRECT_LOGO = 0x10, + LOGITECH_G810_ZONE_DIRECT_INDICATORS = 0x40, +}; + +enum +{ + LOGITECH_G810_MODE_OFF = 0x00, + LOGITECH_G810_MODE_STATIC = 0x01, + LOGITECH_G810_MODE_BREATHING = 0x02, + LOGITECH_G810_MODE_CYCLE = 0x03, + LOGITECH_G810_MODE_WAVE = 0x04, +}; + +/*---------------------------------------------------------------------------------------------*\ +| Speed is 1000 for fast and 20000 for slow. | +| Values are multiplied by 100 later to give lots of GUI steps. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_G810_SPEED_SLOWEST = 0xC8, /* Slowest speed */ + LOGITECH_G810_SPEED_NORMAL = 0x32, /* Normal speed */ + LOGITECH_G810_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +class LogitechG810Controller +{ +public: + LogitechG810Controller(hid_device* dev_handle_0x11, hid_device* dev_handle_0x12, std::string dev_name); + ~LogitechG810Controller(); + + std::string GetNameString(); + std::string GetSerialString(); + + void Commit(); + + void SetDirect + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ); + + void SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev_pkt_0x11; + hid_device* dev_pkt_0x12; + std::string name; + + void SendDirectFrame + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ); + + void SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SendCommit(); +}; diff --git a/Controllers/LogitechController/LogitechG810Controller/RGBController_LogitechG810.cpp b/Controllers/LogitechController/LogitechG810Controller/RGBController_LogitechG810.cpp new file mode 100644 index 0000000..81cf744 --- /dev/null +++ b/Controllers/LogitechController/LogitechG810Controller/RGBController_LogitechG810.cpp @@ -0,0 +1,402 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG810.cpp | +| | +| RGBController for Logitech G810 Orion Spectrum | +| | +| Adam Honse (CalcProgrammer1) 12 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_LogitechG810.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[7][23] = + { { 111, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 116, 114, 115, NA, 113, NA, 112, 110, NA, NA, NA }, + { 37, NA, 54, 55, 56, 57, NA, 58, 59, 60, 61, NA, 62, 63, 64, 65, 66, 67, 68, 109, 108, 107, 106 }, + { 49, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NA, 41, 42, 38, NA, 69, 70, 71, 79, 80, 81, 82 }, + { 39, NA, 16, 22, 4, 17, NA, 19, 24, 20, 8, 14, 15, 43, 44, 45, 72, 73, 74, 91, 92, 93, 83 }, + { 53, NA, 0, 18, 3, 5, NA, 6, 7, 9, 10, 11, 47, 48, 46, 36, NA, NA, NA, 88, 89, 90, NA }, + { 99, 96, 25, 23, 2, 21, NA, 1, NA, 13, 12, 50, 51, 52, 103, NA, NA, 78, NA, 85, 86, 87, 84 }, + { 98, 101, 100, NA, NA, NA, NA, 40, NA, NA, NA, NA, 104, 105, 97, 102, 76, 77, 75, 94, NA, 95, NA } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 117, +}; + +typedef struct +{ + const char * name; + const unsigned char zone; + const unsigned char idx; +} led_type; + +static const led_type led_names[] = +{ + /* Key Label Zone, Index */ + { KEY_EN_A, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x04 }, + { KEY_EN_B, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x05 }, + { KEY_EN_C, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x06 }, + { KEY_EN_D, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x07 }, + { KEY_EN_E, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x08 }, + { KEY_EN_F, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x09 }, + { KEY_EN_G, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x0A }, + { KEY_EN_H, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x0B }, + { KEY_EN_I, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x0C }, + { KEY_EN_J, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x0D }, + { KEY_EN_K, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x0E }, + { KEY_EN_L, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x0F }, + { KEY_EN_M, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x10 }, + { KEY_EN_N, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x11 }, + { KEY_EN_O, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x12 }, + { KEY_EN_P, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x13 }, + { KEY_EN_Q, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x14 }, + { KEY_EN_R, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x15 }, + { KEY_EN_S, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x16 }, + { KEY_EN_T, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x17 }, + { KEY_EN_U, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x18 }, + { KEY_EN_V, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x19 }, + { KEY_EN_W, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x1A }, + { KEY_EN_X, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x1B }, + { KEY_EN_Y, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x1C }, + { KEY_EN_Z, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x1D }, + { KEY_EN_1, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x1E }, + { KEY_EN_2, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x1F }, + { KEY_EN_3, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x20 }, + { KEY_EN_4, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x21 }, + { KEY_EN_5, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x22 }, + { KEY_EN_6, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x23 }, + { KEY_EN_7, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x24 }, + { KEY_EN_8, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x25 }, + { KEY_EN_9, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x26 }, + { KEY_EN_0, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x27 }, + { KEY_EN_ANSI_ENTER, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x28 }, + { KEY_EN_ESCAPE, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x29 }, + { KEY_EN_BACKSPACE, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x2A }, + { KEY_EN_TAB, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x2B }, + { KEY_EN_SPACE, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x2C }, + { KEY_EN_MINUS, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x2D }, + { KEY_EN_EQUALS, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x2E }, + { KEY_EN_LEFT_BRACKET, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x2F }, + { KEY_EN_RIGHT_BRACKET, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x30 }, + { KEY_EN_ANSI_BACK_SLASH, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x31 },//ANSI only + { KEY_EN_POUND, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x32 },//ISO only + { KEY_EN_SEMICOLON, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x33 }, + { KEY_EN_QUOTE, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x34 }, + { KEY_EN_BACK_TICK, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x35 }, + { KEY_EN_COMMA, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x36 }, + { KEY_EN_PERIOD, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x37 }, + { KEY_EN_FORWARD_SLASH, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x38 }, + { KEY_EN_CAPS_LOCK, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x39 }, + { KEY_EN_F1, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x3A }, + { KEY_EN_F2, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x3B }, + { KEY_EN_F3, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x3C }, + { KEY_EN_F4, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x3D }, + { KEY_EN_F5, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x3E }, + { KEY_EN_F6, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x3F }, + { KEY_EN_F7, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x40 }, + { KEY_EN_F8, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x41 }, + { KEY_EN_F9, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x42 }, + { KEY_EN_F10, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x43 }, + { KEY_EN_F11, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x44 }, + { KEY_EN_F12, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x45 }, + { KEY_EN_PRINT_SCREEN, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x46 }, + { KEY_EN_SCROLL_LOCK, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x47 }, + { KEY_EN_PAUSE_BREAK, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x48 }, + { KEY_EN_INSERT, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x49 }, + { KEY_EN_HOME, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x4A }, + { KEY_EN_PAGE_UP, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x4B }, + { KEY_EN_DELETE, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x4C }, + { KEY_EN_END, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x4D }, + { KEY_EN_PAGE_DOWN, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x4E }, + { KEY_EN_RIGHT_ARROW, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x4F }, + { KEY_EN_LEFT_ARROW, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x50 }, + { KEY_EN_DOWN_ARROW, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x51 }, + { KEY_EN_UP_ARROW, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x52 }, + { KEY_EN_NUMPAD_LOCK, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x53 }, + { KEY_EN_NUMPAD_DIVIDE, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x54 }, + { KEY_EN_NUMPAD_TIMES, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x55 }, + { KEY_EN_NUMPAD_MINUS, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x56 }, + { KEY_EN_NUMPAD_PLUS, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x57 }, + { KEY_EN_NUMPAD_ENTER, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x58 }, + { KEY_EN_NUMPAD_1, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x59 }, + { KEY_EN_NUMPAD_2, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x5A }, + { KEY_EN_NUMPAD_3, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x5B }, + { KEY_EN_NUMPAD_4, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x5C }, + { KEY_EN_NUMPAD_5, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x5D }, + { KEY_EN_NUMPAD_6, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x5E }, + { KEY_EN_NUMPAD_7, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x5F }, + { KEY_EN_NUMPAD_8, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x60 }, + { KEY_EN_NUMPAD_9, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x61 }, + { KEY_EN_NUMPAD_0, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x62 }, + { KEY_EN_NUMPAD_PERIOD, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x63 }, + { KEY_EN_ISO_BACK_SLASH, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x64 },//ISO only + { KEY_EN_MENU, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0x65 }, + { KEY_EN_LEFT_CONTROL, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0xE0 }, + { KEY_EN_LEFT_SHIFT, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0xE1 }, + { KEY_EN_LEFT_ALT, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0xE2 }, + { KEY_EN_LEFT_WINDOWS, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0xE3 }, + { KEY_EN_RIGHT_CONTROL, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0xE4 }, + { KEY_EN_RIGHT_SHIFT, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0xE5 }, + { KEY_EN_RIGHT_ALT, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0xE6 }, + { KEY_EN_RIGHT_WINDOWS, LOGITECH_G810_ZONE_DIRECT_KEYBOARD, 0xE7 }, + { KEY_EN_MEDIA_NEXT, LOGITECH_G810_ZONE_DIRECT_MEDIA, 0xB5 }, + { KEY_EN_MEDIA_PREVIOUS, LOGITECH_G810_ZONE_DIRECT_MEDIA, 0xB6 }, + { KEY_EN_MEDIA_STOP, LOGITECH_G810_ZONE_DIRECT_MEDIA, 0xB7 }, + { KEY_EN_MEDIA_PLAY_PAUSE, LOGITECH_G810_ZONE_DIRECT_MEDIA, 0xCD }, + { KEY_EN_MEDIA_MUTE, LOGITECH_G810_ZONE_DIRECT_MEDIA, 0xE2 }, + { "Logo", LOGITECH_G810_ZONE_DIRECT_LOGO, 0x01 }, + { "Lighting", LOGITECH_G810_ZONE_DIRECT_INDICATORS, 0x01 }, + { "Game Mode", LOGITECH_G810_ZONE_DIRECT_INDICATORS, 0x02 }, + { "Caps Lock Indicator", LOGITECH_G810_ZONE_DIRECT_INDICATORS, 0x03 }, + { "Scroll Lock Indicator", LOGITECH_G810_ZONE_DIRECT_INDICATORS, 0x04 }, + { "Num Lock Indicator", LOGITECH_G810_ZONE_DIRECT_INDICATORS, 0x05 }, +}; + +/**------------------------------------------------------------------*\ + @name Logitech G810 + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechKeyboardG810 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechG810::RGBController_LogitechG810(LogitechG810Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_KEYBOARD; + description = "Logitech Keyboard Device"; + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LOGITECH_G810_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G810_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Cycle; + Cycle.name = "Cycle"; + Cycle.value = LOGITECH_G810_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_G810_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G810_SPEED_FASTEST; + Cycle.speed = LOGITECH_G810_SPEED_NORMAL; + modes.push_back(Cycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G810_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + Breathing.speed_min = LOGITECH_G810_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G810_SPEED_FASTEST; + Breathing.speed = LOGITECH_G810_SPEED_NORMAL; + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_LogitechG810::~RGBController_LogitechG810() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_LogitechG810::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 7; + new_zone.matrix_map->width = 23; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx].name; + new_led.value = ( led_names[led_idx].zone << 8 ) + led_names[led_idx].idx; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_LogitechG810::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechG810::DeviceUpdateLEDs() +{ + #define MAX_FRAMES_PER_PACKET 0x0E + + unsigned char frame_buf[MAX_FRAMES_PER_PACKET * 4]; + unsigned char frame_cnt = 0; + unsigned char prev_zone = 0; + unsigned char zone = 0; + unsigned char idx = 0; + + /*---------------------------------------------------------*\ + | TODO: Send packets with multiple LED frames | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + zone = ( leds[led_idx].value >> 8 ); + idx = ( leds[led_idx].value & 0xFF ); + + if((zone != prev_zone) && (frame_cnt != 0)) + { + controller->SetDirect(prev_zone, frame_cnt, frame_buf); + frame_cnt = 0; + } + + frame_buf[(frame_cnt * 4) + 0] = idx; + frame_buf[(frame_cnt * 4) + 1] = RGBGetRValue(colors[led_idx]); + frame_buf[(frame_cnt * 4) + 2] = RGBGetGValue(colors[led_idx]); + frame_buf[(frame_cnt * 4) + 3] = RGBGetBValue(colors[led_idx]); + + frame_cnt++; + prev_zone = zone; + + if(frame_cnt == MAX_FRAMES_PER_PACKET) + { + controller->SetDirect(prev_zone, frame_cnt, frame_buf); + frame_cnt = 0; + } + } + + if(frame_cnt != 0) + { + controller->SetDirect(prev_zone, frame_cnt, frame_buf); + } + + controller->Commit(); +} + +void RGBController_LogitechG810::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG810::UpdateSingleLED(int led) +{ + unsigned char frame[4]; + unsigned char zone; + unsigned char idx; + + zone = ( leds[led].value >> 8 ); + idx = ( leds[led].value & 0xFF ); + + frame[0] = idx; + frame[1] = RGBGetRValue(colors[led]); + frame[2] = RGBGetGValue(colors[led]); + frame[3] = RGBGetBValue(colors[led]); + + controller->SetDirect(zone, 1, frame); + controller->Commit(); +} + +void RGBController_LogitechG810::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Direct mode does not send a mode packet | + | Call UpdateLEDs to send direct packet | + \*---------------------------------------------------------*/ + if(active_mode == 0xFFFF) + { + UpdateLEDs(); + return; + } + + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, red, grn, blu); +} diff --git a/Controllers/LogitechController/LogitechG810Controller/RGBController_LogitechG810.h b/Controllers/LogitechController/LogitechG810Controller/RGBController_LogitechG810.h new file mode 100644 index 0000000..bc3f32f --- /dev/null +++ b/Controllers/LogitechController/LogitechG810Controller/RGBController_LogitechG810.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG810.h | +| | +| RGBController for Logitech G810 Orion Spectrum | +| | +| Adam Honse (CalcProgrammer1) 12 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechG810Controller.h" + +class RGBController_LogitechG810 : public RGBController +{ +public: + RGBController_LogitechG810(LogitechG810Controller* controller_ptr); + ~RGBController_LogitechG810(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechG810Controller* controller; +}; diff --git a/Controllers/LogitechController/LogitechG815Controller/LogitechG815Controller.cpp b/Controllers/LogitechController/LogitechG815Controller/LogitechG815Controller.cpp new file mode 100644 index 0000000..b1f8000 --- /dev/null +++ b/Controllers/LogitechController/LogitechG815Controller/LogitechG815Controller.cpp @@ -0,0 +1,311 @@ +/*---------------------------------------------------------*\ +| LogitechG815Controller.cpp | +| | +| Driver for Logitech G815 | +| | +| Cheerpipe 20 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechG815Controller.h" +#include "StringUtils.h" + +LogitechG815Controller::LogitechG815Controller(hid_device* dev_handle_0x11, hid_device* dev_handle_0x12, std::string dev_name) +{ + dev_pkt_0x11 = dev_handle_0x11; + dev_pkt_0x12 = dev_handle_0x12; + name = dev_name; +} + +LogitechG815Controller::~LogitechG815Controller() +{ + +} + +std::string LogitechG815Controller::GetNameString() +{ + return(name); +} + +std::string LogitechG815Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev_pkt_0x11, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LogitechG815Controller::Commit() +{ + SendCommit(); +} + +void LogitechG815Controller::SetDirect + ( + unsigned char frame_type, + unsigned char * frame_data + ) +{ + SendDirectFrame(frame_type, frame_data); +} + +void LogitechG815Controller::SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SendMode(LOGITECH_G815_ZONE_MODE_KEYBOARD, mode, speed, red, green, blue); + SendMode(LOGITECH_G815_ZONE_MODE_LOGO, mode, speed, red, green, blue); + SendCommit(); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void LogitechG815Controller::SendCommit() +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x10; + usb_buf[0x03] = LOGITECH_G815_COMMIT_BYTE; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read_timeout(dev_pkt_0x11, usb_buf, 20, LOGITECH_READ_TIMEOUT); +} + +void LogitechG815Controller::InitializeDirect() +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x08; + usb_buf[0x03] = 0x3E; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x08; + usb_buf[0x03] = 0x1E; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0F; + usb_buf[0x03] = 0x1E; + usb_buf[0x10] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0F; + usb_buf[0x03] = 0x1E; + usb_buf[0x04] = 0x01; + usb_buf[0x10] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); +} + +void LogitechG815Controller::SendSingleLed + ( + unsigned char keyCode, + unsigned char r, + unsigned char g, + unsigned char b + ) +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up a 6F packet with a single color | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x10; + usb_buf[0x03] = LOGITECH_G815_ZONE_FRAME_TYPE_LITTLE; + + usb_buf[0x04] = keyCode; + + usb_buf[0x05] = r; + usb_buf[0x06] = g; + usb_buf[0x07] = b; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); +} + +void LogitechG815Controller::SendDirectFrame + ( + unsigned char frame_type, + unsigned char * frame_data + ) +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x10; + usb_buf[0x03] = frame_type; + + /*-----------------------------------------------------*\ + | Copy in frame data | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x04], frame_data, 16); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read_timeout(dev_pkt_0x11, usb_buf, 20, LOGITECH_READ_TIMEOUT); +} + +void LogitechG815Controller::SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0D; + usb_buf[0x03] = 0x3D; //TODO: Check if it is the correct value for G815 + usb_buf[0x04] = zone; + + usb_buf[0x05] = mode; + + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + + speed = 100 * speed; + + if(mode == LOGITECH_G815_MODE_CYCLE) + { + usb_buf[0x0B] = speed >> 8; + usb_buf[0x0C] = speed & 0xFF; + usb_buf[0x0D] = 0x64; + } + else if(mode == LOGITECH_G815_MODE_BREATHING) + { + usb_buf[0x09] = speed >> 8; + usb_buf[0x0A] = speed & 0xFF; + usb_buf[0x0C] = 0x64; + } + else + { + return; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); +} diff --git a/Controllers/LogitechController/LogitechG815Controller/LogitechG815Controller.h b/Controllers/LogitechController/LogitechG815Controller/LogitechG815Controller.h new file mode 100644 index 0000000..b133c51 --- /dev/null +++ b/Controllers/LogitechController/LogitechG815Controller/LogitechG815Controller.h @@ -0,0 +1,118 @@ +/*---------------------------------------------------------*\ +| LogitechG815Controller.h | +| | +| Driver for Logitech G815 | +| | +| Cheerpipe 20 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define LOGITECH_G815_COMMIT_BYTE 0x7F +#define LOGITECH_READ_TIMEOUT 300 //Timeout in ms + +enum +{ + LOGITECH_G815_ZONE_MODE_KEYBOARD = 0x00, + LOGITECH_G815_ZONE_MODE_LOGO = 0x01, + LOGITECH_G815_ZONE_MODE_MULTIMEDIA = 0X02, + LOGITECH_G815_ZONE_MODE_GKEYS = 0x03, + LOGITECH_G815_ZONE_MODE_MODIFIERS = 0x04 +}; + +enum +{ + LOGITECH_G815_ZONE_FRAME_TYPE_LITTLE = 0x1F, + LOGITECH_G815_ZONE_FRAME_TYPE_BIG = 0x6F +}; + +enum +{ + LOGITECH_G815_ZONE_DIRECT_KEYBOARD = 0x01, + LOGITECH_G815_ZONE_DIRECT_MEDIA = 0x02, + LOGITECH_G815_ZONE_DIRECT_LOGO = 0x10, + LOGITECH_G815_ZONE_DIRECT_INDICATORS = 0x40, +}; + +enum +{ + LOGITECH_G815_MODE_OFF = 0x00, + LOGITECH_G815_MODE_STATIC = 0x01, + LOGITECH_G815_MODE_BREATHING = 0x02, + LOGITECH_G815_MODE_CYCLE = 0x03, + LOGITECH_G815_MODE_WAVE = 0x04, + LOGITECH_G815_MODE_DIRECT = 0x05, +}; + +/*---------------------------------------------------------------------------------------------*\ +| Speed is 1000 for fast and 20000 for slow. | +| Values are multiplied by 100 later to give lots of GUI steps. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_G815_SPEED_SLOWEST = 0xC8, /* Slowest speed */ + LOGITECH_G815_SPEED_NORMAL = 0x32, /* Normal speed */ + LOGITECH_G815_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +class LogitechG815Controller +{ +public: + LogitechG815Controller(hid_device* dev_handle_0x11, hid_device* dev_handle_0x12, std::string dev_name); + ~LogitechG815Controller(); + + std::string GetNameString(); + std::string GetSerialString(); + + void Commit(); + void InitializeDirect(); + void SetDirect + ( + unsigned char frame_type, + unsigned char * frame_data + ); + void SendSingleLed + ( + unsigned char keyCode, + unsigned char r, + unsigned char g, + unsigned char b + ); + void SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev_pkt_0x11; + hid_device* dev_pkt_0x12; + std::string name; + + void SendDirectFrame + ( + unsigned char frame_type, + unsigned char * frame_data + ); + + void SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + void SendCommit(); +}; diff --git a/Controllers/LogitechController/LogitechG815Controller/RGBController_LogitechG815.cpp b/Controllers/LogitechController/LogitechG815Controller/RGBController_LogitechG815.cpp new file mode 100644 index 0000000..161a484 --- /dev/null +++ b/Controllers/LogitechController/LogitechG815Controller/RGBController_LogitechG815.cpp @@ -0,0 +1,537 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG815.cpp | +| | +| RGBController for Logitech G815 | +| | +| Cheerpipe 20 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "RGBControllerKeyNames.h" +#include "RGBController_LogitechG815.h" + +#define NA 0xFFFFFFFF +const size_t max_key_per_color = 13; +const size_t data_size = 16; + +static unsigned int matrix_map[7][27] = + { { 110, NA, NA, NA, NA, NA, NA, NA, NA, NA, 111, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA }, + { NA, NA, 37, NA, 54, 55, 56, 57, NA, 58, 59, 60, 61, NA, 62, 63, 64, 65, NA, 66, 67, 68, NA, 106, 107, 108, 109 }, + { 112, NA, 49, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NA, 41, 42, 38, NA, NA, 69, 70, 71, NA, 79, 80, 81, 82 }, + { 113, NA, 39, NA, 16, 22, 4, 17, NA, 19, 24, 20, 8, 14, 15, 43, 44, 45, NA, 72, 73, 74, NA, 91, 92, 93, 83 }, + { 114, NA, 53, NA, 0, 18, 3, 5, NA, 6, 7, 9, 10, 11, 47, 48, 46, 36, NA, NA, NA, NA, NA, 88, 89, 90, NA }, + { 115, NA, 99, 96, 25, 23, 2, 21, NA, 1, NA, 13, 12, 50, 51, 52, 103, NA, NA, NA, 78, NA, NA, 85, 86, 87, 84 }, + { 116, NA, 98, 101, 100, NA, NA, NA, NA, 40, NA, NA, NA, NA, 104, 105, 97, 102, NA, 76, 77, 75, NA, 94, NA, 95, NA } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 117, +}; + +typedef struct +{ + const char * name; + const unsigned char zone; + const unsigned char idx; +} logitech_g815_led; + +static const logitech_g815_led led_names[] = +{ + /* Key Label Zone, Index */ + { KEY_EN_A, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x04 }, + { KEY_EN_B, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x05 }, + { KEY_EN_C, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x06 }, + { KEY_EN_D, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x07 }, + { KEY_EN_E, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x08 }, + { KEY_EN_F, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x09 }, + { KEY_EN_G, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x0A }, + { KEY_EN_H, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x0B }, + { KEY_EN_I, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x0C }, + { KEY_EN_J, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x0D }, + { KEY_EN_K, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x0E }, + { KEY_EN_L, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x0F }, + { KEY_EN_M, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x10 }, + { KEY_EN_N, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x11 }, + { KEY_EN_O, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x12 }, + { KEY_EN_P, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x13 }, + { KEY_EN_Q, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x14 }, + { KEY_EN_R, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x15 }, + { KEY_EN_S, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x16 }, + { KEY_EN_T, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x17 }, + { KEY_EN_U, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x18 }, + { KEY_EN_V, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x19 }, + { KEY_EN_W, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x1A }, + { KEY_EN_X, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x1B }, + { KEY_EN_Y, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x1C }, + { KEY_EN_Z, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x1D }, + { KEY_EN_1, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x1E }, + { KEY_EN_2, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x1F }, + { KEY_EN_3, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x20 }, + { KEY_EN_4, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x21 }, + { KEY_EN_5, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x22 }, + { KEY_EN_6, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x23 }, + { KEY_EN_7, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x24 }, + { KEY_EN_8, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x25 }, + { KEY_EN_9, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x26 }, + { KEY_EN_0, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x27 }, + { KEY_EN_ANSI_ENTER, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x28 }, + { KEY_EN_ESCAPE, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x29 }, + { KEY_EN_BACKSPACE, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x2A }, + { KEY_EN_TAB, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x2B }, + { KEY_EN_SPACE, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x2C }, + { KEY_EN_MINUS, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x2D }, + { KEY_EN_EQUALS, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x2E }, + { KEY_EN_LEFT_BRACKET, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x2F }, + { KEY_EN_RIGHT_BRACKET, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x30 }, + { KEY_EN_ANSI_BACK_SLASH, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x31 },//ANSI only + { KEY_EN_POUND, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x32 },//ISO only + { KEY_EN_SEMICOLON, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x33 }, + { KEY_EN_QUOTE, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x34 }, + { KEY_EN_BACK_TICK, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x35 }, + { KEY_EN_COMMA, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x36 }, + { KEY_EN_PERIOD, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x37 }, + { KEY_EN_FORWARD_SLASH, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x38 }, + { KEY_EN_CAPS_LOCK, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x39 }, + { KEY_EN_F1, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x3A }, + { KEY_EN_F2, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x3B }, + { KEY_EN_F3, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x3C }, + { KEY_EN_F4, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x3D }, + { KEY_EN_F5, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x3E }, + { KEY_EN_F6, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x3F }, + { KEY_EN_F7, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x40 }, + { KEY_EN_F8, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x41 }, + { KEY_EN_F9, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x42 }, + { KEY_EN_F10, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x43 }, + { KEY_EN_F11, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x44 }, + { KEY_EN_F12, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x45 }, + { KEY_EN_PRINT_SCREEN, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x46 }, + { KEY_EN_SCROLL_LOCK, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x47 }, + { KEY_EN_PAUSE_BREAK, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x48 }, + { KEY_EN_INSERT, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x49 }, + { KEY_EN_HOME, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x4A }, + { KEY_EN_PAGE_UP, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x4B }, + { KEY_EN_DELETE, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x4C }, + { KEY_EN_END, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x4D }, + { KEY_EN_PAGE_DOWN, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x4E }, + { KEY_EN_RIGHT_ARROW, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x4F }, + { KEY_EN_LEFT_ARROW, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x50 }, + { KEY_EN_DOWN_ARROW, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x51 }, + { KEY_EN_UP_ARROW, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x52 }, + { KEY_EN_NUMPAD_LOCK, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x53 }, + { KEY_EN_NUMPAD_DIVIDE, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x54 }, + { KEY_EN_NUMPAD_TIMES, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x55 }, + { KEY_EN_NUMPAD_MINUS, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x56 }, + { KEY_EN_NUMPAD_PLUS, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x57 }, + { KEY_EN_NUMPAD_ENTER, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x58 }, + { KEY_EN_NUMPAD_1, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x59 }, + { KEY_EN_NUMPAD_2, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x5A }, + { KEY_EN_NUMPAD_3, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x5B }, + { KEY_EN_NUMPAD_4, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x5C }, + { KEY_EN_NUMPAD_5, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x5D }, + { KEY_EN_NUMPAD_6, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x5E }, + { KEY_EN_NUMPAD_7, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x5F }, + { KEY_EN_NUMPAD_8, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x60 }, + { KEY_EN_NUMPAD_9, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x61 }, + { KEY_EN_NUMPAD_0, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x62 }, + { KEY_EN_NUMPAD_PERIOD, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x63 }, + { KEY_EN_ISO_BACK_SLASH, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x64 },//ISO only + { KEY_EN_MENU, LOGITECH_G815_ZONE_DIRECT_KEYBOARD, 0x65 }, + { KEY_EN_LEFT_CONTROL, LOGITECH_G815_ZONE_MODE_MODIFIERS, 0xE0 }, + { KEY_EN_LEFT_SHIFT, LOGITECH_G815_ZONE_MODE_MODIFIERS, 0xE1 }, + { KEY_EN_LEFT_ALT, LOGITECH_G815_ZONE_MODE_MODIFIERS, 0xE2 }, + { KEY_EN_LEFT_WINDOWS, LOGITECH_G815_ZONE_MODE_MODIFIERS, 0xE3 }, + { KEY_EN_RIGHT_CONTROL, LOGITECH_G815_ZONE_MODE_MODIFIERS, 0xE4 }, + { KEY_EN_RIGHT_SHIFT, LOGITECH_G815_ZONE_MODE_MODIFIERS, 0xE5 }, + { KEY_EN_RIGHT_ALT, LOGITECH_G815_ZONE_MODE_MODIFIERS, 0xE6 }, + { KEY_EN_RIGHT_WINDOWS, LOGITECH_G815_ZONE_MODE_MODIFIERS, 0xE7 }, + { KEY_EN_MEDIA_PREVIOUS, LOGITECH_G815_ZONE_DIRECT_MEDIA, 0x9E }, + { KEY_EN_MEDIA_PLAY_PAUSE, LOGITECH_G815_ZONE_DIRECT_MEDIA, 0x9B }, + { KEY_EN_MEDIA_NEXT, LOGITECH_G815_ZONE_DIRECT_MEDIA, 0x9D }, + { KEY_EN_MEDIA_MUTE, LOGITECH_G815_ZONE_DIRECT_MEDIA, 0x9C }, + { "Logo", LOGITECH_G815_ZONE_DIRECT_LOGO, 0x01 }, + { "Lighting", LOGITECH_G815_ZONE_DIRECT_INDICATORS, 0x99 }, + { "Key: G1", LOGITECH_G815_ZONE_MODE_GKEYS, 0x01 }, + { "Key: G2", LOGITECH_G815_ZONE_MODE_GKEYS, 0x02 }, + { "Key: G3", LOGITECH_G815_ZONE_MODE_GKEYS, 0x03 }, + { "Key: G4", LOGITECH_G815_ZONE_MODE_GKEYS, 0x04 }, + { "Key: G5", LOGITECH_G815_ZONE_MODE_GKEYS, 0x05 }, +}; + +/**------------------------------------------------------------------*\ + @name Logitech G815 + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechKeyboardG815 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechG815::RGBController_LogitechG815(LogitechG815Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_KEYBOARD; + description = "Logitech G815 Keyboard Device"; + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LOGITECH_G815_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LOGITECH_G815_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G815_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Cycle; + Cycle.name = "Cycle"; + Cycle.value = LOGITECH_G815_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_G815_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G815_SPEED_FASTEST; + Cycle.speed = LOGITECH_G815_SPEED_NORMAL; + modes.push_back(Cycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G815_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + Breathing.speed_min = LOGITECH_G815_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G815_SPEED_FASTEST; + Breathing.speed = LOGITECH_G815_SPEED_NORMAL; + modes.push_back(Breathing); + + SetupZones(); + std::copy(colors.begin(), colors.end(),std::back_inserter(current_colors)); +} + +RGBController_LogitechG815::~RGBController_LogitechG815() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_LogitechG815::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 7; + new_zone.matrix_map->width = 27; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx].name; + new_led.value = ( led_names[led_idx].zone << 8 ) + led_names[led_idx].idx; + leds.push_back(new_led); + } + SetupColors(); +} + +void RGBController_LogitechG815::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechG815::DeviceUpdateLEDs() +{ + std::map> ledsByColors; + std::vector new_colors; + unsigned char zone = 0; + unsigned char idx = 0; + unsigned char frame_buffer_big_mode[data_size]; + unsigned char frame_buffer_little_mode[data_size]; + RGBColor colorkey; + + /*---------------------------------------------------------*\ + | Freeze colors array because prepare framebuffers | + | may take some time. | + \*---------------------------------------------------------*/ + std::copy(colors.begin(), colors.end(),std::back_inserter(new_colors)); + + /*---------------------------------------------------------*\ + | Get unique colors to create mode 1F and 6F frame_buffers | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + zone = ( leds[led_idx].value >> 8 ); + idx = ( leds[led_idx].value ); + + if(current_colors[led_idx]==new_colors[led_idx]) + { + /*-------------------------------------------------*\ + | Don't send if key color is not changed | + \*-------------------------------------------------*/ + continue; + } + + switch (zone) + { + case LOGITECH_G815_ZONE_MODE_GKEYS: + idx = ((idx & 0x00ff) + 0xb3); + break; + + case LOGITECH_G815_ZONE_MODE_MODIFIERS: + idx = ((idx & 0x00ff) - 0x78); + break; + + case LOGITECH_G815_ZONE_DIRECT_KEYBOARD: + idx = ((idx & 0x00ff) - 0x03); + break; + + case LOGITECH_G815_ZONE_DIRECT_LOGO: + idx = ((idx & 0x00ff) + 0xd1); + break; + + default: + idx = (idx & 0x00ff); + break; + } + + colorkey = new_colors[led_idx]; + + if(ledsByColors.count(colorkey) == 0) + { + ledsByColors.insert(std::pair>(colorkey, {})); + } + + ledsByColors[colorkey].push_back(idx); + } + + uint8_t led_in_little_frame = 0; + uint8_t bi = 0; + size_t frame_pos = 3; + uint8_t li = 0; + + /*---------------------------------------------------------*\ + | Create frame_buffers of type 1F (Little, up to 4 leds | + | per packet) and 6F (big, up to 13 leds per packet). | + \*---------------------------------------------------------*/ + for(std::pair>& x: ledsByColors) + { + /*-----------------------------------------------------*\ + | For colors with more than 4 keys. Better to use big | + | (6F) packets to save USB transfers. | + \*-----------------------------------------------------*/ + if(x.second.size() > 4) + { + bi = 0; + + while(bi < x.second.size()) + { + frame_buffer_big_mode[0] = RGBGetRValue(x.first); + frame_buffer_big_mode[1] = RGBGetGValue(x.first); + frame_buffer_big_mode[2] = RGBGetBValue(x.first); + frame_pos = 3; + + for(uint8_t i = 0; i < (uint8_t)max_key_per_color; i++) + { + if((bi + i) < (uint8_t)x.second.size()) + { + frame_buffer_big_mode[frame_pos] = x.second[bi+i]; + frame_pos++; + } + } + + if(frame_pos < data_size) + { + /*-----------------------------------------*\ + | Zeroing just what is needed and if needed | + \*-----------------------------------------*/ + memset(frame_buffer_big_mode + frame_pos, 0x00, sizeof(frame_buffer_big_mode) - frame_pos); + + /*-----------------------------------------*\ + | End of Data byte | + \*-----------------------------------------*/ + frame_buffer_big_mode[frame_pos] = 0xFF; + } + + /*-----------------------------------------------------*\ + | Zeroing just what is needed | + \*-----------------------------------------------------*/ + controller->SetDirect(LOGITECH_G815_ZONE_FRAME_TYPE_BIG, frame_buffer_big_mode); + bi = bi + max_key_per_color; + } + } + /*-----------------------------------------------------*\ + | For colors with up to 4 keys. Use 1F packet to send | + | up to 4 colors-keys combinations per packet. | + \*-----------------------------------------------------*/ + else + { + li = 0; + + while(li < x.second.size()) + { + frame_buffer_little_mode[led_in_little_frame*4 + 0] = x.second[li]; + frame_buffer_little_mode[led_in_little_frame*4 + 1] = RGBGetRValue(x.first); + frame_buffer_little_mode[led_in_little_frame*4 + 2] = RGBGetGValue(x.first); + frame_buffer_little_mode[led_in_little_frame*4 + 3] = RGBGetBValue(x.first); + li++; + led_in_little_frame++; + + if(led_in_little_frame == 4) + { + /*-----------------------------------------*\ + | No End of Data byte if the packet is full | + \*-----------------------------------------*/ + controller->SetDirect(LOGITECH_G815_ZONE_FRAME_TYPE_LITTLE, frame_buffer_little_mode); + led_in_little_frame=0; + } + } + } + } + + /*---------------------------------------------------------*\ + | If there is a left 1F packet with less than 4 keys, send | + | it and add an End of Data byte. | + \*---------------------------------------------------------*/ + if(led_in_little_frame > 0) + { + /*-----------------------------------------------------*\ + | Zeroing just what is needed | + \*-----------------------------------------------------*/ + memset(frame_buffer_little_mode + (led_in_little_frame * 4 - 1), 0x00, sizeof(frame_buffer_little_mode) - led_in_little_frame * 4); + + /*-----------------------------------------------------*\ + | Data byte | + \*-----------------------------------------------------*/ + frame_buffer_little_mode[led_in_little_frame*4 + 0] = 0xFF; + + /*-----------------------------------------------------*\ + | Send little frame and clear little frame buffer | + \*-----------------------------------------------------*/ + controller->SetDirect(LOGITECH_G815_ZONE_FRAME_TYPE_LITTLE, frame_buffer_little_mode); + } + if(ledsByColors.size() > 0) + { + /*-----------------------------------------------------*\ + | Copy the current color vector to avoid set keys that | + | has not being | + \*-----------------------------------------------------*/ + controller->Commit(); + std::copy(new_colors.begin(), new_colors.end(),current_colors.begin()); + } +} + +void RGBController_LogitechG815::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG815::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG815::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Direct mode does not send a mode packet | + | Call UpdateLEDs to send direct packet | + \*---------------------------------------------------------*/ + if(modes[active_mode].value == LOGITECH_G815_MODE_DIRECT) + { + /*-----------------------------------------------------*\ + | Send real direct mode initialization. I used same | + | sequence as GHUB for screen capture. | + \*-----------------------------------------------------*/ + controller->InitializeDirect(); + + /*-----------------------------------------------------*\ + | Set one key to get direct mode engaged. | + \*-----------------------------------------------------*/ + controller->SendSingleLed(0x29,0,0,0); + controller->Commit(); + return; + } + + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, red, grn, blu); +} diff --git a/Controllers/LogitechController/LogitechG815Controller/RGBController_LogitechG815.h b/Controllers/LogitechController/LogitechG815Controller/RGBController_LogitechG815.h new file mode 100644 index 0000000..b8db464 --- /dev/null +++ b/Controllers/LogitechController/LogitechG815Controller/RGBController_LogitechG815.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG815.h | +| | +| RGBController for Logitech G815 | +| | +| Cheerpipe 20 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechG815Controller.h" + +class RGBController_LogitechG815 : public RGBController +{ +public: + RGBController_LogitechG815(LogitechG815Controller* controller_ptr); + ~RGBController_LogitechG815(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechG815Controller* controller; + std::vector current_colors; +}; diff --git a/Controllers/LogitechController/LogitechG910Controller/LogitechG910Controller.cpp b/Controllers/LogitechController/LogitechG910Controller/LogitechG910Controller.cpp new file mode 100644 index 0000000..08b4f9b --- /dev/null +++ b/Controllers/LogitechController/LogitechG910Controller/LogitechG910Controller.cpp @@ -0,0 +1,192 @@ +/*---------------------------------------------------------*\ +| LogitechG910Controller.cpp | +| | +| Driver for Logitech G910 Orion Spectrum | +| | +| Adam Honse (CalcProgrammer1) 11 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechG910Controller.h" +#include "StringUtils.h" + +LogitechG910Controller::LogitechG910Controller(hid_device* dev_handle_0x11, hid_device* dev_handle_0x12, std::string dev_name) +{ + dev_pkt_0x11 = dev_handle_0x11; + dev_pkt_0x12 = dev_handle_0x12; + name = dev_name; +} + +LogitechG910Controller::~LogitechG910Controller() +{ + hid_close(dev_pkt_0x11); + hid_close(dev_pkt_0x12); +} + +std::string LogitechG910Controller::GetNameString() +{ + return(name); +} + +std::string LogitechG910Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev_pkt_0x11, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LogitechG910Controller::Commit() +{ + SendCommit(); +} + +void LogitechG910Controller::SetDirect + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ) +{ + SendDirectFrame(zone, frame_count, frame_data); +} + +void LogitechG910Controller::SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SendMode(LOGITECH_G910_ZONE_MODE_KEYBOARD, mode, speed, red, green, blue); + SendMode(LOGITECH_G910_ZONE_MODE_LOGO, mode, speed, red, green, blue); + + SendCommit(); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void LogitechG910Controller::SendCommit() +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0F; + usb_buf[0x03] = 0x5F; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); +} + +void LogitechG910Controller::SendDirectFrame + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ) +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x12; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0F; + usb_buf[0x03] = 0x3F; + usb_buf[0x05] = zone; + usb_buf[0x07] = frame_count; + + /*-----------------------------------------------------*\ + | Copy in frame data | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x08], frame_data, frame_count * 4); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x12, usb_buf, 64); + hid_read(dev_pkt_0x11, usb_buf, 20); +} + +void LogitechG910Controller::SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x10; + usb_buf[0x03] = 0x3B; + usb_buf[0x04] = zone; + + usb_buf[0x05] = mode; + + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + + speed = 100 * speed; + if(mode == LOGITECH_G910_MODE_CYCLE) + { + usb_buf[0x0B] = speed >> 8; + usb_buf[0x0C] = speed & 0xFF; + usb_buf[0x0D] = 0x64; + } + else if(mode == LOGITECH_G910_MODE_BREATHING) + { + usb_buf[0x09] = speed >> 8; + usb_buf[0x0A] = speed & 0xFF; + usb_buf[0x0C] = 0x64; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); +} diff --git a/Controllers/LogitechController/LogitechG910Controller/LogitechG910Controller.h b/Controllers/LogitechController/LogitechG910Controller/LogitechG910Controller.h new file mode 100644 index 0000000..e3491bf --- /dev/null +++ b/Controllers/LogitechController/LogitechG910Controller/LogitechG910Controller.h @@ -0,0 +1,102 @@ +/*---------------------------------------------------------*\ +| LogitechG910Controller.h | +| | +| Driver for Logitech G910 Orion Spectrum | +| | +| Adam Honse (CalcProgrammer1) 11 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + LOGITECH_G910_ZONE_MODE_KEYBOARD = 0x00, + LOGITECH_G910_ZONE_MODE_LOGO = 0x01, +}; + +enum +{ + LOGITECH_G910_ZONE_DIRECT_KEYBOARD = 0x01, + LOGITECH_G910_ZONE_DIRECT_GKEYS = 0x04, + LOGITECH_G910_ZONE_DIRECT_LOGO = 0x10, + LOGITECH_G910_ZONE_DIRECT_INDICATORS = 0x40, +}; + +enum +{ + LOGITECH_G910_MODE_OFF = 0x00, + LOGITECH_G910_MODE_STATIC = 0x01, + LOGITECH_G910_MODE_BREATHING = 0x02, + LOGITECH_G910_MODE_CYCLE = 0x03, + LOGITECH_G910_MODE_WAVE = 0x04, +}; + +/*---------------------------------------------------------------------------------------------*\ +| Speed is 1000 for fast and 20000 for slow. | +| Values are multiplied by 100 later to give lots of GUI steps. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_G910_SPEED_SLOWEST = 0xC8, /* Slowest speed */ + LOGITECH_G910_SPEED_NORMAL = 0x32, /* Normal speed */ + LOGITECH_G910_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +class LogitechG910Controller +{ +public: + LogitechG910Controller(hid_device* dev_handle_0x11, hid_device* dev_handle_0x12, std::string dev_name); + ~LogitechG910Controller(); + + std::string GetNameString(); + std::string GetSerialString(); + + void Commit(); + + void SetDirect + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ); + + void SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev_pkt_0x11; + hid_device* dev_pkt_0x12; + std::string name; + + void SendDirectFrame + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ); + + void SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SendCommit(); +}; diff --git a/Controllers/LogitechController/LogitechG910Controller/RGBController_LogitechG910.cpp b/Controllers/LogitechController/LogitechG910Controller/RGBController_LogitechG910.cpp new file mode 100644 index 0000000..c8a46f6 --- /dev/null +++ b/Controllers/LogitechController/LogitechG910Controller/RGBController_LogitechG910.cpp @@ -0,0 +1,403 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG910.cpp | +| | +| RGBController for Logitech G910 Orion Spectrum | +| | +| Adam Honse (CalcProgrammer1) 12 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_LogitechG910.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[8][24] = + { { NA, 111, 112, 113, 114, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA }, + { 115, 37, NA, 54, 55, 56, 57, NA, 58, 59, 60, 61, NA, 62, 63, 64, 65, 66, 67, 68, NA, NA, NA, NA }, + { 106, 49, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NA, 41, 42, 38, NA, 69, 70, 71, 79, 80, 81, 82 }, + { 107, 39, NA, 16, 22, 4, 17, NA, 19, 24, 20, 8, 14, 15, 43, 44, 45, 72, 73, 74, 91, 92, 93, 83 }, + { 108, 53, NA, 0, 18, 3, 5, NA, 6, 7, 9, 10, 11, 47, 48, 46, 36, NA, NA, NA, 88, 89, 90, NA }, + { 109, 99, 96, 25, 23, 2, 21, NA, 1, NA, 13, 12, 50, 51, 52, 103, NA, NA, 78, NA, 85, 86, 87, 84 }, + { 110, 98, 101, 100, NA, NA, NA, NA, 40, NA, NA, NA, NA, 104, 105, 97, 102, 76, 77, 75, 94, NA, 95, NA }, + { NA, NA, NA, 116, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 117, +}; + +typedef struct +{ + const char * name; + const unsigned char zone; + const unsigned char idx; +} led_type; + +static const led_type led_names[] = +{ + /* Key Label Zone, Index */ + { KEY_EN_A, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x04 }, + { KEY_EN_B, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x05 }, + { KEY_EN_C, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x06 }, + { KEY_EN_D, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x07 }, + { KEY_EN_E, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x08 }, + { KEY_EN_F, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x09 }, + { KEY_EN_G, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x0A }, + { KEY_EN_H, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x0B }, + { KEY_EN_I, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x0C }, + { KEY_EN_J, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x0D }, + { KEY_EN_K, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x0E }, + { KEY_EN_L, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x0F }, + { KEY_EN_M, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x10 }, + { KEY_EN_N, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x11 }, + { KEY_EN_O, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x12 }, + { KEY_EN_P, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x13 }, + { KEY_EN_Q, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x14 }, + { KEY_EN_R, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x15 }, + { KEY_EN_S, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x16 }, + { KEY_EN_T, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x17 }, + { KEY_EN_U, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x18 }, + { KEY_EN_V, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x19 }, + { KEY_EN_W, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x1A }, + { KEY_EN_X, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x1B }, + { KEY_EN_Y, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x1C }, + { KEY_EN_Z, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x1D }, + { KEY_EN_1, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x1E }, + { KEY_EN_2, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x1F }, + { KEY_EN_3, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x20 }, + { KEY_EN_4, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x21 }, + { KEY_EN_5, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x22 }, + { KEY_EN_6, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x23 }, + { KEY_EN_7, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x24 }, + { KEY_EN_8, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x25 }, + { KEY_EN_9, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x26 }, + { KEY_EN_0, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x27 }, + { KEY_EN_ANSI_ENTER, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x28 }, + { KEY_EN_ESCAPE, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x29 }, + { KEY_EN_BACKSPACE, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x2A }, + { KEY_EN_TAB, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x2B }, + { KEY_EN_SPACE, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x2C }, + { KEY_EN_MINUS, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x2D }, + { KEY_EN_EQUALS, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x2E }, + { KEY_EN_LEFT_BRACKET, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x2F }, + { KEY_EN_RIGHT_BRACKET, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x30 }, + { KEY_EN_ANSI_BACK_SLASH, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x31 },//ANSI only + { KEY_EN_POUND, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x32 },//ISO only + { KEY_EN_SEMICOLON, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x33 }, + { KEY_EN_QUOTE, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x34 }, + { KEY_EN_BACK_TICK, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x35 }, + { KEY_EN_COMMA, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x36 }, + { KEY_EN_PERIOD, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x37 }, + { KEY_EN_FORWARD_SLASH, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x38 }, + { KEY_EN_CAPS_LOCK, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x39 }, + { KEY_EN_F1, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x3A }, + { KEY_EN_F2, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x3B }, + { KEY_EN_F3, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x3C }, + { KEY_EN_F4, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x3D }, + { KEY_EN_F5, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x3E }, + { KEY_EN_F6, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x3F }, + { KEY_EN_F7, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x40 }, + { KEY_EN_F8, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x41 }, + { KEY_EN_F9, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x42 }, + { KEY_EN_F10, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x43 }, + { KEY_EN_F11, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x44 }, + { KEY_EN_F12, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x45 }, + { KEY_EN_PRINT_SCREEN, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x46 }, + { KEY_EN_SCROLL_LOCK, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x47 }, + { KEY_EN_PAUSE_BREAK, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x48 }, + { KEY_EN_INSERT, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x49 }, + { KEY_EN_HOME, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x4A }, + { KEY_EN_PAGE_UP, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x4B }, + { KEY_EN_DELETE, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x4C }, + { KEY_EN_END, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x4D }, + { KEY_EN_PAGE_DOWN, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x4E }, + { KEY_EN_RIGHT_ARROW, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x4F }, + { KEY_EN_LEFT_ARROW, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x50 }, + { KEY_EN_DOWN_ARROW, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x51 }, + { KEY_EN_UP_ARROW, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x52 }, + { KEY_EN_NUMPAD_LOCK, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x53 }, + { KEY_EN_NUMPAD_DIVIDE, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x54 }, + { KEY_EN_NUMPAD_TIMES, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x55 }, + { KEY_EN_NUMPAD_MINUS, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x56 }, + { KEY_EN_NUMPAD_PLUS, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x57 }, + { KEY_EN_NUMPAD_ENTER, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x58 }, + { KEY_EN_NUMPAD_1, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x59 }, + { KEY_EN_NUMPAD_2, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x5A }, + { KEY_EN_NUMPAD_3, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x5B }, + { KEY_EN_NUMPAD_4, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x5C }, + { KEY_EN_NUMPAD_5, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x5D }, + { KEY_EN_NUMPAD_6, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x5E }, + { KEY_EN_NUMPAD_7, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x5F }, + { KEY_EN_NUMPAD_8, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x60 }, + { KEY_EN_NUMPAD_9, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x61 }, + { KEY_EN_NUMPAD_0, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x62 }, + { KEY_EN_NUMPAD_PERIOD, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x63 }, + { KEY_EN_ISO_BACK_SLASH, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x64 },//ISO only + { KEY_EN_MENU, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0x65 }, + { KEY_EN_LEFT_CONTROL, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0xE0 }, + { KEY_EN_LEFT_SHIFT, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0xE1 }, + { KEY_EN_LEFT_ALT, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0xE2 }, + { KEY_EN_LEFT_WINDOWS, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0xE3 }, + { KEY_EN_RIGHT_CONTROL, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0xE4 }, + { KEY_EN_RIGHT_SHIFT, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0xE5 }, + { KEY_EN_RIGHT_ALT, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0xE6 }, + { KEY_EN_RIGHT_WINDOWS, LOGITECH_G910_ZONE_DIRECT_KEYBOARD, 0xE7 }, + { "Key: G1", LOGITECH_G910_ZONE_DIRECT_GKEYS, 0x01 }, + { "Key: G2", LOGITECH_G910_ZONE_DIRECT_GKEYS, 0x02 }, + { "Key: G3", LOGITECH_G910_ZONE_DIRECT_GKEYS, 0x03 }, + { "Key: G4", LOGITECH_G910_ZONE_DIRECT_GKEYS, 0x04 }, + { "Key: G5", LOGITECH_G910_ZONE_DIRECT_GKEYS, 0x05 }, + { "Key: G6", LOGITECH_G910_ZONE_DIRECT_GKEYS, 0x06 }, + { "Key: G7", LOGITECH_G910_ZONE_DIRECT_GKEYS, 0x07 }, + { "Key: G8", LOGITECH_G910_ZONE_DIRECT_GKEYS, 0x08 }, + { "Key: G9", LOGITECH_G910_ZONE_DIRECT_GKEYS, 0x09 }, + { "Logo", LOGITECH_G910_ZONE_DIRECT_LOGO, 0x01 }, + { "Nameplate", LOGITECH_G910_ZONE_DIRECT_LOGO, 0x02 }, +}; + +/**------------------------------------------------------------------*\ + @name Logitech G910 + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechKeyboardG910 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechG910::RGBController_LogitechG910(LogitechG910Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_KEYBOARD; + description = "Logitech Keyboard Device"; + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LOGITECH_G910_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G910_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Cycle; + Cycle.name = "Cycle"; + Cycle.value = LOGITECH_G910_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_G910_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G910_SPEED_FASTEST; + Cycle.speed = LOGITECH_G910_SPEED_NORMAL; + modes.push_back(Cycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G910_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + Breathing.speed_min = LOGITECH_G910_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G910_SPEED_FASTEST; + Breathing.speed = LOGITECH_G910_SPEED_NORMAL; + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_LogitechG910::~RGBController_LogitechG910() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_LogitechG910::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 8; + new_zone.matrix_map->width = 24; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx].name; + new_led.value = ( led_names[led_idx].zone << 8 ) + led_names[led_idx].idx; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_LogitechG910::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechG910::DeviceUpdateLEDs() +{ + #define MAX_FRAMES_PER_PACKET 0x0E + + unsigned char frame_buf[MAX_FRAMES_PER_PACKET * 4]; + unsigned char frame_cnt = 0; + unsigned char prev_zone = 0; + unsigned char zone = 0; + unsigned char idx = 0; + + /*---------------------------------------------------------*\ + | TODO: Send packets with multiple LED frames | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + zone = ( leds[led_idx].value >> 8 ); + idx = ( leds[led_idx].value & 0xFF ); + + if((zone != prev_zone) && (frame_cnt != 0)) + { + controller->SetDirect(prev_zone, frame_cnt, frame_buf); + frame_cnt = 0; + } + + frame_buf[(frame_cnt * 4) + 0] = idx; + frame_buf[(frame_cnt * 4) + 1] = RGBGetRValue(colors[led_idx]); + frame_buf[(frame_cnt * 4) + 2] = RGBGetGValue(colors[led_idx]); + frame_buf[(frame_cnt * 4) + 3] = RGBGetBValue(colors[led_idx]); + + frame_cnt++; + prev_zone = zone; + + if(frame_cnt == MAX_FRAMES_PER_PACKET) + { + controller->SetDirect(prev_zone, frame_cnt, frame_buf); + frame_cnt = 0; + } + } + + if(frame_cnt != 0) + { + controller->SetDirect(prev_zone, frame_cnt, frame_buf); + } + + controller->Commit(); +} + +void RGBController_LogitechG910::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG910::UpdateSingleLED(int led) +{ + unsigned char frame[4]; + unsigned char zone; + unsigned char idx; + + zone = ( leds[led].value >> 8 ); + idx = ( leds[led].value & 0xFF ); + + frame[0] = idx; + frame[1] = RGBGetRValue(colors[led]); + frame[2] = RGBGetGValue(colors[led]); + frame[3] = RGBGetBValue(colors[led]); + + controller->SetDirect(zone, 1, frame); + controller->Commit(); +} + +void RGBController_LogitechG910::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Direct mode does not send a mode packet | + | Call UpdateLEDs to send direct packet | + \*---------------------------------------------------------*/ + if(active_mode == 0xFFFF) + { + UpdateLEDs(); + return; + } + + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, red, grn, blu); +} diff --git a/Controllers/LogitechController/LogitechG910Controller/RGBController_LogitechG910.h b/Controllers/LogitechController/LogitechG910Controller/RGBController_LogitechG910.h new file mode 100644 index 0000000..5f80364 --- /dev/null +++ b/Controllers/LogitechController/LogitechG910Controller/RGBController_LogitechG910.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG910.h | +| | +| RGBController for Logitech G910 Orion Spectrum | +| | +| Adam Honse (CalcProgrammer1) 12 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechG910Controller.h" + +class RGBController_LogitechG910 : public RGBController +{ +public: + RGBController_LogitechG910(LogitechG910Controller* controller_ptr); + ~RGBController_LogitechG910(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechG910Controller* controller; +}; diff --git a/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.cpp b/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.cpp new file mode 100644 index 0000000..30659bb --- /dev/null +++ b/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.cpp @@ -0,0 +1,438 @@ +/*---------------------------------------------------------*\ +| LogitechG915Controller.cpp | +| | +| Driver for Logitech G915 | +| | +| Cheerpipe 20 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechG915Controller.h" +#include "StringUtils.h" + +const size_t MIN_DATA_FRAME_SIZE = 4; +const size_t MAX_DATA_FRAME_SIZE = 16; +const size_t HEADER_SIZE = 4; +const size_t MESSAGE_LEN = 20; +const size_t RESPONSE_LEN = 20; + +LogitechG915Controller::LogitechG915Controller(hid_device* dev_handle, bool wired, std::string dev_name) +{ + this->dev_handle = dev_handle; + this->name = dev_name; + + if(wired) + { + device_index = 0xFF; + feature_4522_idx = 0x0E; + feature_8040_idx = 0x13; + feature_8071_idx = 0x09; + feature_8081_idx = 0x0A; + } + else + { + device_index = 0x01; + feature_4522_idx = 0x0F; + feature_8040_idx = 0x14; + feature_8071_idx = 0x0A; + feature_8081_idx = 0x0B; + } +} + +LogitechG915Controller::~LogitechG915Controller() +{ + +} + +std::string LogitechG915Controller::GetNameString() +{ + return(name); +} + +std::string LogitechG915Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev_handle, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LogitechG915Controller::Commit() +{ + SendCommit(); +} + +void LogitechG915Controller::SetDirect + ( + unsigned char frame_type, + unsigned char * frame_data, + size_t length + ) +{ + SendDirectFrame(frame_type, frame_data, length); +} + +void LogitechG915Controller::SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned short brightness, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + BeginModeSet(); + uint8_t logo_mode = mode; + switch(mode) + { + case LOGITECH_G915_MODE_OFF: + case LOGITECH_G915_MODE_STATIC: + { + logo_mode = mode; // static and off match + break; + } + case LOGITECH_G915_MODE_BREATHING: + { + logo_mode = LOGITECH_G915_LOGO_MODE_BREATHING; //0x03 + break; + } + case LOGITECH_G915_MODE_CYCLE: + case LOGITECH_G915_MODE_WAVE: + { + logo_mode = LOGITECH_G915_LOGO_MODE_CYCLE; //0x02 + break; + } + case LOGITECH_G915_MODE_RIPPLE: + { + logo_mode = LOGITECH_G915_LOGO_MODE_STATIC; + break; + } + } + + SendMode(LOGITECH_G915_ZONE_MODE_KEYBOARD, mode, speed, brightness, red, green, blue); + SendMode(LOGITECH_G915_ZONE_MODE_LOGO, logo_mode, speed, brightness, red, green, blue); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void LogitechG915Controller::SendCommit() +{ + unsigned char usb_buf[MESSAGE_LEN]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = device_index; + usb_buf[0x02] = feature_8081_idx; + usb_buf[0x03] = LOGITECH_G915_COMMIT_BYTE; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_handle, usb_buf, MESSAGE_LEN); + hid_read_timeout(dev_handle, usb_buf, RESPONSE_LEN, LOGITECH_READ_TIMEOUT); +} + +void LogitechG915Controller::BeginModeSet() +{ + unsigned char usb_buf[MESSAGE_LEN]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = device_index; + usb_buf[0x02] = feature_4522_idx; + usb_buf[0x03] = 0x3E; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_handle, usb_buf, MESSAGE_LEN); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = device_index; + usb_buf[0x02] = feature_4522_idx; + usb_buf[0x03] = 0x1E; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_handle, usb_buf, MESSAGE_LEN); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); +} + +void LogitechG915Controller::InitializeModeSet() +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = device_index; + usb_buf[0x02] = feature_8071_idx; + usb_buf[0x03] = 0x5E; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = 0x03; + usb_buf[0x06] = 0x07; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_handle, usb_buf, MESSAGE_LEN); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); +} + +void LogitechG915Controller::InitializeDirect() +{ + unsigned char usb_buf[MESSAGE_LEN]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = device_index; + usb_buf[0x02] = feature_4522_idx; + usb_buf[0x03] = 0x3E; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_handle, usb_buf, MESSAGE_LEN); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = device_index; + usb_buf[0x02] = feature_4522_idx; + usb_buf[0x03] = 0x1E; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_handle, usb_buf, MESSAGE_LEN); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = device_index; + usb_buf[0x02] = feature_8071_idx; + usb_buf[0x03] = 0x1E; + usb_buf[0x10] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_handle, usb_buf, MESSAGE_LEN); + hid_read(dev_handle, usb_buf, MESSAGE_LEN); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = device_index; + usb_buf[0x02] = feature_8071_idx; + usb_buf[0x03] = 0x1E; + usb_buf[0x04] = 0x01; + usb_buf[0x10] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_handle, usb_buf, MESSAGE_LEN); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); +} + +void LogitechG915Controller::SendSingleLed + ( + unsigned char keyCode, + unsigned char r, + unsigned char g, + unsigned char b + ) +{ + unsigned char little_frame[4] = { keyCode, r, g, b }; + SendDirectFrame(LOGITECH_G915_ZONE_FRAME_TYPE_LITTLE, little_frame, 4); +} + +void LogitechG915Controller::SendDirectFrame + ( + unsigned char frame_type, + unsigned char * frame_data, + size_t length + ) +{ + if(length < MIN_DATA_FRAME_SIZE) + { + return; + } + else if(length > MAX_DATA_FRAME_SIZE) + { + length = MAX_DATA_FRAME_SIZE; + } + + unsigned char usb_buf[MESSAGE_LEN]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = device_index; + usb_buf[0x02] = feature_8081_idx; + usb_buf[0x03] = frame_type; + + /*-----------------------------------------------------*\ + | Copy in frame data | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x04], frame_data, length); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_handle, usb_buf, MESSAGE_LEN); + hid_read_timeout(dev_handle, usb_buf, RESPONSE_LEN, LOGITECH_READ_TIMEOUT); +} + +void LogitechG915Controller::SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned short brightness, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[MESSAGE_LEN]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = device_index; + usb_buf[0x02] = feature_8071_idx; + usb_buf[0x03] = LOGITECH_G915_ZONE_FRAME_TYPE_MODE; + + usb_buf[0x04] = zone; + usb_buf[0x05] = mode; + + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + + if(mode != LOGITECH_G915_MODE_RIPPLE) + { + speed = 100 * speed; + } + + // mode == LOGITECH_G915_MODE_OFF; No data to set + if(mode == LOGITECH_G915_MODE_STATIC) + { + usb_buf[0x09] = 0x02; + } + else if((mode == LOGITECH_G915_MODE_BREATHING && zone == LOGITECH_G915_ZONE_MODE_KEYBOARD) \ + || (mode == LOGITECH_G915_LOGO_MODE_BREATHING && zone == LOGITECH_G915_ZONE_MODE_LOGO)) + { + usb_buf[0x09] = speed >> 8; + usb_buf[0x0A] = speed & 0xFF; + usb_buf[0x0C] = brightness & 0xFF; + } + else if((mode == LOGITECH_G915_MODE_CYCLE && zone == LOGITECH_G915_ZONE_MODE_KEYBOARD) \ + || (mode == LOGITECH_G915_LOGO_MODE_CYCLE && zone == LOGITECH_G915_ZONE_MODE_LOGO)) + { + usb_buf[0x0B] = speed >> 8; + usb_buf[0x0C] = speed & 0xFF; + usb_buf[0x0D] = brightness & 0xFF; + } + else if(mode == LOGITECH_G915_MODE_WAVE) + { + usb_buf[0x0C] = speed & 0xFF; + usb_buf[0x0D] = 0x01; // Direction control 0x01 is horizontal + usb_buf[0x0E] = brightness & 0xFF; + usb_buf[0x0F] = speed >> 8; + } + else if(mode == LOGITECH_G915_MODE_RIPPLE) + { + usb_buf[0x0B] = speed & 0xFF; + } + usb_buf[0x10] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_handle, usb_buf, MESSAGE_LEN); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); +} diff --git a/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.h b/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.h new file mode 100644 index 0000000..d7f1cd2 --- /dev/null +++ b/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.h @@ -0,0 +1,146 @@ +/*---------------------------------------------------------*\ +| LogitechG915Controller.h | +| | +| Driver for Logitech G915 | +| | +| Cheerpipe 20 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define LOGITECH_G915_COMMIT_BYTE 0x7F +#define LOGITECH_READ_TIMEOUT 300 //Timeout in ms + +enum +{ + LOGITECH_G915_ZONE_MODE_KEYBOARD = 0x01, + LOGITECH_G915_ZONE_MODE_LOGO = 0x00, + LOGITECH_G915_ZONE_MODE_MULTIMEDIA = 0x02, + LOGITECH_G915_ZONE_MODE_GKEYS = 0x03, + LOGITECH_G915_ZONE_MODE_MODIFIERS = 0x04 +}; + +enum +{ + LOGITECH_G915_ZONE_FRAME_TYPE_LITTLE = 0x1F, + LOGITECH_G915_ZONE_FRAME_TYPE_BIG = 0x6F, + LOGITECH_G915_ZONE_FRAME_TYPE_MODE = 0x1E +}; + +enum +{ + LOGITECH_G915_ZONE_DIRECT_KEYBOARD = 0x01, + LOGITECH_G915_ZONE_DIRECT_MEDIA = 0x02, + LOGITECH_G915_ZONE_DIRECT_LOGO = 0x10, + LOGITECH_G915_ZONE_DIRECT_INDICATORS = 0x40, +}; + +enum +{ + LOGITECH_G915_MODE_OFF = 0x00, + LOGITECH_G915_MODE_STATIC = 0x01, + LOGITECH_G915_MODE_BREATHING = 0x02, + LOGITECH_G915_MODE_CYCLE = 0x03, + LOGITECH_G915_MODE_WAVE = 0x04, + LOGITECH_G915_MODE_RIPPLE = 0x05, + LOGITECH_G915_MODE_DIRECT = 0xFF, +}; + +enum +{ + LOGITECH_G915_LOGO_MODE_OFF = 0x00, + LOGITECH_G915_LOGO_MODE_STATIC = 0x01, + LOGITECH_G915_LOGO_MODE_CYCLE = 0x02, + LOGITECH_G915_LOGO_MODE_BREATHING = 0x03, +}; + +/*---------------------------------------------------------------------------------------------*\ +| Speed is 1000 for fast and 20000 for slow. | +| Values are multiplied by 100 later to give lots of GUI steps. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_G915_SPEED_SLOWEST = 0xC8, /* Slowest speed */ + LOGITECH_G915_SPEED_NORMAL = 0x32, /* Normal speed */ + LOGITECH_G915_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +/* Ripple speeds are in ms directly. */ +enum +{ + LOGITECH_G915_SPEED_RIPPLE_SLOW = 200, + LOGITECH_G915_SPEED_RIPPLE_NORMAL = 20, + LOGITECH_G915_SPEED_RIPPLE_FAST = 2, +}; + +class LogitechG915Controller +{ +public: + LogitechG915Controller(hid_device* dev_handle, bool wired, std::string dev_name); + ~LogitechG915Controller(); + + std::string GetNameString(); + std::string GetSerialString(); + + void Commit(); + void InitializeDirect(); + void InitializeModeSet(); + void BeginModeSet(); + void SetDirect + ( + unsigned char frame_type, + unsigned char * frame_data, + size_t length + ); + void SendSingleLed + ( + unsigned char keyCode, + unsigned char r, + unsigned char g, + unsigned char b + ); + void SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned short brightness, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev_handle; + unsigned char feature_4522_idx; + unsigned char device_index; + unsigned char feature_8040_idx; + unsigned char feature_8071_idx; + unsigned char feature_8081_idx; + std::string name; + + void SendDirectFrame + ( + unsigned char frame_type, + unsigned char * frame_data, + size_t length + ); + + void SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned short brightness, + unsigned char red, + unsigned char green, + unsigned char blue + ); + void SendCommit(); +}; diff --git a/Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.cpp b/Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.cpp new file mode 100644 index 0000000..a744479 --- /dev/null +++ b/Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.cpp @@ -0,0 +1,655 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG915.cpp | +| | +| RGBController for Logitech G915 | +| | +| Cheerpipe 20 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "RGBControllerKeyNames.h" +#include "RGBController_LogitechG915.h" + +#define NA 0xFFFFFFFF +const size_t DATA_FRAME_SIZE = 16; +const size_t BIG_FRAME_MAX_KEYS = 13; +const size_t LITTLE_FRAME_MAX_KEYS = 4; + +static unsigned int matrix_map[7][27] = + { { 93, NA, NA, NA, NA, NA, NA, NA, NA, NA, 94, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA }, + { NA, NA, 37, NA, 54, 55, 56, 57, NA, 58, 59, 60, 61, NA, 62, 63, 64, 65, NA, 66, 67, 68, NA, 89, 90, 91, 92 }, + { 112, NA, 49, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NA, 41, 42, 38, NA, NA, 69, 70, 71, NA, 95, 96, 97, 98 }, + { 113, NA, 39, NA, 16, 22, 4, 17, NA, 19, 24, 20, 8, 14, 15, 43, 44, 45, NA, 72, 73, 74, NA, 107, 108, 109, 99 }, + { 114, NA, 53, NA, 0, 18, 3, 5, NA, 6, 7, 9, 10, 11, 47, 48, 46, 36, NA, NA, NA, NA, NA, 104, 105, 106, NA }, + { 115, NA, 82, 79, 25, 23, 2, 21, NA, 1, NA, 13, 12, 50, 51, 52, 86, NA, NA, NA, 78, NA, NA, 101, 102, 103, 100 }, + { 116, NA, 81, 84, 83, NA, NA, NA, NA, 40, NA, NA, NA, NA, 87, 88, 80, 85, NA, 76, 77, 75, NA, 110, NA, 111, NA } }; + +static unsigned int matrix_map_tkl[7][20] = + { { 93, NA, NA, NA, NA, 94, NA, NA, NA, NA, NA, NA, 89, 90, 91, 92, NA, NA, NA, NA }, + { 37, NA, 54, 55, 56, 57, NA, 58, 59, 60, 61, NA, 62, 63, 64, 65, NA, 66, 67, 68 }, + { 49, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NA, 41, 42, 38, NA, NA, 69, 70, 71 }, + { 39, NA, 16, 22, 4, 17, NA, 19, 24, 20, 8, 14, 15, 43, 44, 45, NA, 72, 73, 74 }, + { 53, NA, 0, 18, 3, 5, NA, 6, 7, 9, 10, 11, 47, 48, 46, 36, NA, NA, NA, NA }, + { 82, 79, 25, 23, 2, 21, NA, 1, NA, 13, 12, 50, 51, 52, 86, NA, NA, NA, 78, NA }, + { 81, 84, 83, NA, NA, NA, NA, 40, NA, NA, NA, NA, 87, 88, 80, 85, NA, 76, 77, 75 } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int tkl_led_count = 95; +static const unsigned int full_led_count = 117; + +typedef struct +{ + const char * name; + const unsigned char zone; + const unsigned char idx; +} led_type; + +static const led_type led_names[] = +{ + /* Key Label Zone, Index */ + { KEY_EN_A, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x04 }, + { KEY_EN_B, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x05 }, + { KEY_EN_C, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x06 }, + { KEY_EN_D, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x07 }, + { KEY_EN_E, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x08 }, + { KEY_EN_F, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x09 }, + { KEY_EN_G, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x0A }, + { KEY_EN_H, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x0B }, + { KEY_EN_I, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x0C }, + { KEY_EN_J, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x0D }, + { KEY_EN_K, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x0E }, + { KEY_EN_L, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x0F }, + { KEY_EN_M, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x10 }, + { KEY_EN_N, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x11 }, + { KEY_EN_O, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x12 }, + { KEY_EN_P, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x13 }, + { KEY_EN_Q, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x14 }, + { KEY_EN_R, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x15 }, + { KEY_EN_S, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x16 }, + { KEY_EN_T, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x17 }, + { KEY_EN_U, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x18 }, + { KEY_EN_V, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x19 }, + { KEY_EN_W, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x1A }, + { KEY_EN_X, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x1B }, + { KEY_EN_Y, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x1C }, + { KEY_EN_Z, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x1D }, + { KEY_EN_1, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x1E }, + { KEY_EN_2, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x1F }, + { KEY_EN_3, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x20 }, + { KEY_EN_4, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x21 }, + { KEY_EN_5, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x22 }, + { KEY_EN_6, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x23 }, + { KEY_EN_7, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x24 }, + { KEY_EN_8, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x25 }, + { KEY_EN_9, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x26 }, + { KEY_EN_0, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x27 }, + { KEY_EN_ANSI_ENTER, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x28 }, + { KEY_EN_ESCAPE, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x29 }, + { KEY_EN_BACKSPACE, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x2A }, + { KEY_EN_TAB, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x2B }, + { KEY_EN_SPACE, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x2C }, + { KEY_EN_MINUS, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x2D }, + { KEY_EN_EQUALS, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x2E }, + { KEY_EN_LEFT_BRACKET, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x2F }, + { KEY_EN_RIGHT_BRACKET, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x30 }, + { KEY_EN_ANSI_BACK_SLASH, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x31 },//ANSI only + { KEY_EN_POUND, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x32 },//ISO only + { KEY_EN_SEMICOLON, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x33 }, + { KEY_EN_QUOTE, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x34 }, + { KEY_EN_BACK_TICK, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x35 }, + { KEY_EN_COMMA, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x36 }, + { KEY_EN_PERIOD, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x37 }, + { KEY_EN_FORWARD_SLASH, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x38 }, + { KEY_EN_CAPS_LOCK, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x39 }, + { KEY_EN_F1, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x3A }, + { KEY_EN_F2, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x3B }, + { KEY_EN_F3, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x3C }, + { KEY_EN_F4, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x3D }, + { KEY_EN_F5, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x3E }, + { KEY_EN_F6, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x3F }, + { KEY_EN_F7, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x40 }, + { KEY_EN_F8, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x41 }, + { KEY_EN_F9, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x42 }, + { KEY_EN_F10, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x43 }, + { KEY_EN_F11, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x44 }, + { KEY_EN_F12, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x45 }, + { KEY_EN_PRINT_SCREEN, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x46 }, + { KEY_EN_SCROLL_LOCK, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x47 }, + { KEY_EN_PAUSE_BREAK, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x48 }, + { KEY_EN_INSERT, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x49 }, + { KEY_EN_HOME, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x4A }, + { KEY_EN_PAGE_UP, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x4B }, + { KEY_EN_DELETE, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x4C }, + { KEY_EN_END, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x4D }, + { KEY_EN_PAGE_DOWN, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x4E }, + { KEY_EN_RIGHT_ARROW, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x4F }, + { KEY_EN_LEFT_ARROW, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x50 }, + { KEY_EN_DOWN_ARROW, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x51 }, + { KEY_EN_UP_ARROW, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x52 }, + { KEY_EN_ISO_BACK_SLASH, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x64 },//ISO only + { KEY_EN_MENU, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x65 }, + { KEY_EN_LEFT_CONTROL, LOGITECH_G915_ZONE_MODE_MODIFIERS, 0xE0 }, + { KEY_EN_LEFT_SHIFT, LOGITECH_G915_ZONE_MODE_MODIFIERS, 0xE1 }, + { KEY_EN_LEFT_ALT, LOGITECH_G915_ZONE_MODE_MODIFIERS, 0xE2 }, + { KEY_EN_LEFT_WINDOWS, LOGITECH_G915_ZONE_MODE_MODIFIERS, 0xE3 }, + { KEY_EN_RIGHT_CONTROL, LOGITECH_G915_ZONE_MODE_MODIFIERS, 0xE4 }, + { KEY_EN_RIGHT_SHIFT, LOGITECH_G915_ZONE_MODE_MODIFIERS, 0xE5 }, + { KEY_EN_RIGHT_ALT, LOGITECH_G915_ZONE_MODE_MODIFIERS, 0xE6 }, + { KEY_EN_RIGHT_WINDOWS, LOGITECH_G915_ZONE_MODE_MODIFIERS, 0xE7 }, + { KEY_EN_MEDIA_PREVIOUS, LOGITECH_G915_ZONE_DIRECT_MEDIA, 0x9E }, + { KEY_EN_MEDIA_PLAY_PAUSE, LOGITECH_G915_ZONE_DIRECT_MEDIA, 0x9B }, + { KEY_EN_MEDIA_NEXT, LOGITECH_G915_ZONE_DIRECT_MEDIA, 0x9D }, + { KEY_EN_MEDIA_MUTE, LOGITECH_G915_ZONE_DIRECT_MEDIA, 0x9C }, + { "Logo", LOGITECH_G915_ZONE_DIRECT_LOGO, 0x01 }, + { "Key: Brightness", LOGITECH_G915_ZONE_DIRECT_INDICATORS, 0x99 }, + { KEY_EN_NUMPAD_LOCK, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x53 }, // First Non-TKL Key + { KEY_EN_NUMPAD_DIVIDE, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x54 }, + { KEY_EN_NUMPAD_TIMES, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x55 }, + { KEY_EN_NUMPAD_MINUS, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x56 }, + { KEY_EN_NUMPAD_PLUS, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x57 }, + { KEY_EN_NUMPAD_ENTER, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x58 }, + { KEY_EN_NUMPAD_1, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x59 }, + { KEY_EN_NUMPAD_2, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x5A }, + { KEY_EN_NUMPAD_3, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x5B }, + { KEY_EN_NUMPAD_4, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x5C }, + { KEY_EN_NUMPAD_5, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x5D }, + { KEY_EN_NUMPAD_6, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x5E }, + { KEY_EN_NUMPAD_7, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x5F }, + { KEY_EN_NUMPAD_8, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x60 }, + { KEY_EN_NUMPAD_9, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x61 }, + { KEY_EN_NUMPAD_0, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x62 }, + { KEY_EN_NUMPAD_PERIOD, LOGITECH_G915_ZONE_DIRECT_KEYBOARD, 0x63 }, + { "Key: G1", LOGITECH_G915_ZONE_MODE_GKEYS, 0x01 }, + { "Key: G2", LOGITECH_G915_ZONE_MODE_GKEYS, 0x02 }, + { "Key: G3", LOGITECH_G915_ZONE_MODE_GKEYS, 0x03 }, + { "Key: G4", LOGITECH_G915_ZONE_MODE_GKEYS, 0x04 }, + { "Key: G5", LOGITECH_G915_ZONE_MODE_GKEYS, 0x05 }, +}; + +/*--------------------------------------*\ +| Small dataframe | +| Contains a max of 4 pairs | +\*--------------------------------------*/ +struct LittleFrame +{ + std::pair color_key[LITTLE_FRAME_MAX_KEYS]; + size_t len = 0; +}; + +/*--------------------------------------*\ +| Small dataframe | +| Contains 1 color for max 13 keys | +\*--------------------------------------*/ +struct BigFrame +{ + RGBColor color; + char keys[BIG_FRAME_MAX_KEYS]; + size_t len = 0; +}; + +/*-------------------------------------------*\ +| Add termination byte and zero out rest | +\*-------------------------------------------*/ +void terminate_buffer(unsigned char buf[DATA_FRAME_SIZE], size_t idx) +{ + memset(&buf[idx], 0x00, DATA_FRAME_SIZE - idx); + buf[idx] = 0xFF; +} + +/*-------------------------------------------*\ +| small frame: [KEY, R, G, B] | +| If less than 4 keys, terminate using 0xFF | +\*-------------------------------------------*/ +size_t populate_little_frame_data(unsigned char buf[DATA_FRAME_SIZE], const LittleFrame& frame) +{ + if(frame.len == 0) + { + return 0; + } + + for(size_t i = 0; i < frame.len && i < LITTLE_FRAME_MAX_KEYS; i++) + { + buf[4 * i + 0] = frame.color_key[i].second; + buf[4 * i + 1] = RGBGetRValue(frame.color_key[i].first); + buf[4 * i + 2] = RGBGetGValue(frame.color_key[i].first); + buf[4 * i + 3] = RGBGetBValue(frame.color_key[i].first); + } + + if(frame.len < LITTLE_FRAME_MAX_KEYS) + { + terminate_buffer(buf, 4 * frame.len); + return 4 * frame.len + 1; // termination byte + } + return DATA_FRAME_SIZE; +} + +/*-------------------------------------------------*\ +| Large frame: [R, G, B, Key0, Key1, ..., Key12] | +| If less than 13 keys, terminate using 0xFF | +\*--------------------------------------------------*/ +size_t populate_big_frame_data(unsigned char buf[DATA_FRAME_SIZE], const BigFrame& frame) +{ + if(frame.len == 0) + { + return 0; + } + + buf[0] = RGBGetRValue(frame.color); + buf[1] = RGBGetGValue(frame.color); + buf[2] = RGBGetBValue(frame.color); + for(size_t i = 0; i < frame.len && i < BIG_FRAME_MAX_KEYS; i++) + { + buf[i + 3] = frame.keys[i]; + } + + if(frame.len < BIG_FRAME_MAX_KEYS) + { + terminate_buffer(buf, frame.len + 3); + return frame.len + 4; // color + termination byte + } + return DATA_FRAME_SIZE; +} + +/**------------------------------------------------------------------*\ + @name Logitech G915 + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechKeyboardG915,DetectLogitechKeyboardG915Wired + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechG915::RGBController_LogitechG915(LogitechG915Controller* controller_ptr, bool tkl) +{ + controller = controller_ptr; + is_tkl = tkl; + + if(is_tkl) + { + description = "Logitech G915TKL Keyboard Device"; + } + else + { + description = "Logitech G915 Keyboard Device"; + } + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_KEYBOARD; + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LOGITECH_G915_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LOGITECH_G915_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G915_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G915_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + Breathing.brightness_min = 1; + Breathing.brightness_max = 100; + Breathing.brightness = 100; + Breathing.speed_min = LOGITECH_G915_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G915_SPEED_FASTEST; + Breathing.speed = LOGITECH_G915_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = LOGITECH_G915_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_G915_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G915_SPEED_FASTEST; + Cycle.speed = LOGITECH_G915_SPEED_NORMAL; + Cycle.brightness_min = 1; + Cycle.brightness_max = 100; + Cycle.brightness = 100; + modes.push_back(Cycle); + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = LOGITECH_G915_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + //Wave.flags |= MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_DIRECTION_HV; + Wave.brightness_min = 1; + Wave.brightness_max = 100; + Wave.brightness = 100; + Wave.color_mode = MODE_COLORS_NONE; + Wave.direction = MODE_DIRECTION_HORIZONTAL | MODE_DIRECTION_RIGHT; + Wave.speed_min = LOGITECH_G915_SPEED_SLOWEST; + Wave.speed_max = LOGITECH_G915_SPEED_FASTEST; + Wave.speed = LOGITECH_G915_SPEED_NORMAL; + modes.push_back(Wave); + + mode Ripple; + Ripple.name = "Reactive (Ripple)"; + Ripple.value = LOGITECH_G915_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Ripple.colors_min = 1; + Ripple.colors_max = 1; + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripple.colors.resize(1); + Ripple.speed_min = LOGITECH_G915_SPEED_RIPPLE_SLOW; + Ripple.speed_max = LOGITECH_G915_SPEED_RIPPLE_FAST; + Ripple.speed = LOGITECH_G915_SPEED_RIPPLE_NORMAL; + modes.push_back(Ripple); + + SetupZones(); + std::copy(colors.begin(), colors.end(),std::back_inserter(current_colors)); +} + +RGBController_LogitechG915::~RGBController_LogitechG915() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_LogitechG915::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_count = (is_tkl) ? tkl_led_count : full_led_count; + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + if(is_tkl) + { + new_zone.matrix_map->map = (unsigned int *)&matrix_map_tkl; + new_zone.matrix_map->height = 7; + new_zone.matrix_map->width = 20; + } + else + { + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + new_zone.matrix_map->height = 7; + new_zone.matrix_map->width = 27; + } + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += new_zone.leds_count; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx].name; + new_led.value = ( led_names[led_idx].zone << 8 ) + led_names[led_idx].idx; + leds.push_back(new_led); + } + SetupColors(); +} + +void RGBController_LogitechG915::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechG915::DeviceUpdateLEDs() +{ + std::map> ledsByColors; + std::vector new_colors; + unsigned char zone = 0; + unsigned char idx = 0; + RGBColor colorkey; + + /*---------------------------------------------------------*\ + | Freeze colors array because prepare framebuffers | + | may take some time. | + \*---------------------------------------------------------*/ + std::copy(colors.begin(), colors.end(),std::back_inserter(new_colors)); + + /*---------------------------------------------------------*\ + | Get unique colors to create mode 1F and 6F frame_buffers | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + zone = ( leds[led_idx].value >> 8 ); + idx = ( leds[led_idx].value ); + + if(current_colors[led_idx]==new_colors[led_idx]) + { + /*-------------------------------------------------*\ + | Don't send if key color is not changed | + \*-------------------------------------------------*/ + continue; + } + + switch(zone) + { + case LOGITECH_G915_ZONE_MODE_GKEYS: + idx = ((idx & 0x00ff) + 0xb3); + break; + + case LOGITECH_G915_ZONE_MODE_MODIFIERS: + idx = ((idx & 0x00ff) - 0x78); + break; + + case LOGITECH_G915_ZONE_DIRECT_KEYBOARD: + idx = ((idx & 0x00ff) - 0x03); + break; + + case LOGITECH_G915_ZONE_DIRECT_LOGO: + idx = ((idx & 0x00ff) + 0xd1); + break; + + default: + idx = (idx & 0x00ff); + break; + } + + colorkey = new_colors[led_idx]; + + if(ledsByColors.count(colorkey) == 0) + { + ledsByColors.insert(std::pair>(colorkey, {})); + } + + ledsByColors[colorkey].push_back(idx); + } + + /*-------------------------------------------------*\ + | Nothing to do, we can skip rest of work | + \*-------------------------------------------------*/ + if(ledsByColors.size() == 0) + { + return; + } + + /*-----------------------------------------------------*\ + | Copy the current color vector to avoid set keys that | + | have not changed | + \*-----------------------------------------------------*/ + std::copy(new_colors.begin(), new_colors.end(),current_colors.begin()); + + std::vector little_frames; + std::vector big_frames; + LittleFrame cur_small; + + /*---------------------------------------------------------*\ + | Create frame_buffers of type 1F (Little, up to 4 leds | + | per packet) and 6F (big, up to 13 leds per packet). | + \*---------------------------------------------------------*/ + for(std::pair>& x: ledsByColors) + { + for(size_t bi = 0; bi < x.second.size(); bi += BIG_FRAME_MAX_KEYS) + { + size_t n_colors_left = x.second.size() - bi; + + /*-----------------------------------------------------*\ + | For colors with more than 4 keys. Better to use big | + | (6F) packets to save USB transfers. | + \*-----------------------------------------------------*/ + if(n_colors_left > 4) + { + BigFrame b_frame; + b_frame.color = x.first; + + for(size_t i = 0; i < BIG_FRAME_MAX_KEYS && i < n_colors_left; i++) + { + b_frame.keys[i] = x.second[bi + i]; + b_frame.len++; + } + big_frames.push_back(b_frame); + } + /*-----------------------------------------------------*\ + | For colors with up to 4 keys. Use 1F packet to send | + | up to 4 colors-keys combinations per packet. | + \*-----------------------------------------------------*/ + else + { + for(size_t li = 0; li < n_colors_left; li++) + { + cur_small.color_key[cur_small.len] = std::make_pair(x.first, x.second[bi + li]); + cur_small.len++; + /*-------------------------------*\ + | Frame is full, create a new one | + \*-------------------------------*/ + if(cur_small.len >= LITTLE_FRAME_MAX_KEYS) + { + little_frames.push_back(std::move(cur_small)); + cur_small = LittleFrame(); + } + } + } + } + } + + /*-------------------------------*\ + | Move leftover small frame | + \*-------------------------------*/ + if(cur_small.len > 0) + { + little_frames.push_back(std::move(cur_small)); + } + + unsigned char frame_buffer[DATA_FRAME_SIZE]; + for(const BigFrame& frame : big_frames) + { + size_t length = populate_big_frame_data(frame_buffer, frame); + controller->SetDirect(LOGITECH_G915_ZONE_FRAME_TYPE_BIG, frame_buffer, length); + } + + for(const LittleFrame& frame : little_frames) + { + size_t length = populate_little_frame_data(frame_buffer, frame); + controller->SetDirect(LOGITECH_G915_ZONE_FRAME_TYPE_LITTLE, frame_buffer, length); + } + + controller->Commit(); +} + +void RGBController_LogitechG915::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG915::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG915::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Direct mode does not send a mode packet | + | Call UpdateLEDs to send direct packet | + \*---------------------------------------------------------*/ + if(modes[active_mode].value == LOGITECH_G915_MODE_DIRECT) + { + /*-----------------------------------------------------*\ + | Send real direct mode initialization. I used same | + | sequence as GHUB for screen capture. | + \*-----------------------------------------------------*/ + controller->InitializeDirect(); + + /*-----------------------------------------------------*\ + | Set one key to get direct mode engaged. | + \*-----------------------------------------------------*/ + controller->SendSingleLed(0x29,0,0,0); + controller->Commit(); + return; + } + controller->InitializeModeSet(); + + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, red, grn, blu); +} diff --git a/Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.h b/Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.h new file mode 100644 index 0000000..ba37ccf --- /dev/null +++ b/Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG915.h | +| | +| RGBController for Logitech G915 | +| | +| Cheerpipe 20 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechG915Controller.h" + +class RGBController_LogitechG915 : public RGBController +{ +public: + RGBController_LogitechG915(LogitechG915Controller* controller_ptr, bool tkl); + ~RGBController_LogitechG915(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + bool is_tkl; + + LogitechG915Controller* controller; + std::vector current_colors; +}; diff --git a/Controllers/LogitechController/LogitechG933Controller/LogitechG933Controller.cpp b/Controllers/LogitechController/LogitechG933Controller/LogitechG933Controller.cpp new file mode 100644 index 0000000..2d8144b --- /dev/null +++ b/Controllers/LogitechController/LogitechG933Controller/LogitechG933Controller.cpp @@ -0,0 +1,154 @@ +/*---------------------------------------------------------*\ +| LogitechG933Controller.cpp | +| | +| Driver for Logitech G933 | +| | +| Edbgon 21 Jun 2021 | +| Based on TheRogueZeta 31 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "LogitechG933Controller.h" + +using namespace std::chrono_literals; + +LogitechG933Controller::LogitechG933Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +LogitechG933Controller::~LogitechG933Controller() +{ + hid_close(dev); +} + +std::string LogitechG933Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LogitechG933Controller::GetDeviceName() +{ + return(name); +} + +void LogitechG933Controller::SetDirectMode(uint8_t zone) +{ + unsigned char usb_buf[LOGI_G933_LED_PACKET_SIZE]; + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x04; + usb_buf[0x03] = 0xCA; + usb_buf[0x04] = zone; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + fail_retry_write(dev, usb_buf, LOGI_G933_LED_PACKET_SIZE); +} + +void LogitechG933Controller::SetOffMode(uint8_t zone) +{ + unsigned char usb_buf[LOGI_G933_LED_PACKET_SIZE]; + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x04; + usb_buf[0x03] = 0x3F; + usb_buf[0x04] = zone; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + fail_retry_write(dev, usb_buf, LOGI_G933_LED_PACKET_SIZE); +} + +void LogitechG933Controller::SendHeadsetMode + ( + unsigned char zone, + unsigned char mode, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[LOGI_G933_LED_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x04; + + /*-----------------------------------------------------*\ + | This packet sets speaker into direct mode. This mode | + | is used by Lightsync Ambilight and Music Visualizer | + | realtime effect. | + \*-----------------------------------------------------*/ + usb_buf[0x03] = 0x3A; + + /*-----------------------------------------------------*\ + | Set up mode and speed | + \*-----------------------------------------------------*/ + usb_buf[0x04] = zone; + usb_buf[0x05] = mode; + + /*-----------------------------------------------------*\ + | And set up the colors | + \*-----------------------------------------------------*/ + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + + if(mode == LOGITECH_G933_MODE_DIRECT) //G933 only has Direct Mode. + { + usb_buf[0x09] = 0x02; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + fail_retry_write(dev, usb_buf, LOGI_G933_LED_PACKET_SIZE); +} + +void LogitechG933Controller::fail_retry_write(hid_device *device, const unsigned char *data, size_t length) +{ + unsigned char usb_buf_out[LOGI_G933_LED_PACKET_SIZE]; + unsigned int write_max_retry = LOGI_G933_LED_COMMAND_SEND_RETRIES; + do + { + std::this_thread::sleep_for(1ms); + int ret = hid_write(device, data, length); + + /*-------------------------------------------------------------------------------------*\ + | HID write fails if a change led color and set volume command are sent at | + | the same time because RGB controller and volume control shares the same interface. | + \*-------------------------------------------------------------------------------------*/ + if(ret == 20) + { + std::this_thread::sleep_for(1ms); + hid_read_timeout(dev, usb_buf_out, LOGI_G933_LED_PACKET_SIZE, 20); + break; + } + else + { + write_max_retry--; + std::this_thread::sleep_for(10ms); + } + + }while (write_max_retry > 0); +} diff --git a/Controllers/LogitechController/LogitechG933Controller/LogitechG933Controller.h b/Controllers/LogitechController/LogitechG933Controller/LogitechG933Controller.h new file mode 100644 index 0000000..8772e3b --- /dev/null +++ b/Controllers/LogitechController/LogitechG933Controller/LogitechG933Controller.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| LogitechG933Controller.h | +| | +| Driver for Logitech G933 | +| | +| Edbgon 21 Jun 2021 | +| Based on TheRogueZeta 31 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define LOGI_G933_LED_PACKET_SIZE 20 +#define LOGI_G933_LED_COMMAND_SEND_RETRIES 3 + +enum +{ + LOGITECH_G933_MODE_OFF = 0x00, + LOGITECH_G933_MODE_DIRECT = 0x01, + LOGITECH_G933_MODE_CYCLE = 0x02, + LOGITECH_G933_MODE_BREATHING = 0x03, +}; + +class LogitechG933Controller +{ +public: + LogitechG933Controller(hid_device* dev_handle, const char* path, std::string dev_name); + ~LogitechG933Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetDirectMode(uint8_t zone); + void SetOffMode(uint8_t zone); + + void SendHeadsetMode + ( + unsigned char zone, + unsigned char mode, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev; + std::string location; + std::string name; + + void fail_retry_write(hid_device *device, const unsigned char *data, size_t length); +}; + + diff --git a/Controllers/LogitechController/LogitechG933Controller/RGBController_LogitechG933.cpp b/Controllers/LogitechController/LogitechG933Controller/RGBController_LogitechG933.cpp new file mode 100644 index 0000000..ea294c0 --- /dev/null +++ b/Controllers/LogitechController/LogitechG933Controller/RGBController_LogitechG933.cpp @@ -0,0 +1,134 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG933.cpp | +| | +| RGBController for Logitech G933 | +| | +| Edbgon 21 Jun 2021 | +| Based on TheRogueZeta 31 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LogitechG933.h" + +/**------------------------------------------------------------------*\ + @name Logitech G933 + @category Headset + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectLogitechG933 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechG933::RGBController_LogitechG933(LogitechG933Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Logitech"; + type = DEVICE_TYPE_HEADSET; + description = "Logitech G933 Lightsync Headset"; + location = controller->GetDeviceLocation(); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G933_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LOGITECH_G933_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +void RGBController_LogitechG933::SetupZones() +{ + zone G933_logo; + G933_logo.name = "Logo"; + G933_logo.type = ZONE_TYPE_SINGLE; + G933_logo.leds_min = 1; + G933_logo.leds_max = 1; + G933_logo.leds_count = 1; + G933_logo.matrix_map = NULL; + zones.push_back(G933_logo); + + led G933_logo_led; + G933_logo_led.name = "Logo"; + G933_logo_led.value = 0x00; + leds.push_back(G933_logo_led); + + zone G933_strip; + G933_strip.name = "LED Strip"; + G933_strip.type = ZONE_TYPE_SINGLE; + G933_strip.leds_min = 1; + G933_strip.leds_max = 1; + G933_strip.leds_count = 1; + G933_strip.matrix_map = NULL; + zones.push_back(G933_strip); + + led G933_strip_led; + G933_strip_led.name = "Led Strip"; + G933_strip_led.value = 0x01; + leds.push_back(G933_strip_led); + + SetupColors(); +} + +void RGBController_LogitechG933::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechG933::DeviceUpdateLEDs() +{ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned char red = RGBGetRValue(colors[led_idx]); + unsigned char grn = RGBGetGValue(colors[led_idx]); + unsigned char blu = RGBGetBValue(colors[led_idx]); + + controller->SendHeadsetMode((unsigned char)leds[led_idx].value, modes[active_mode].value, red, grn, blu); + } +} + +void RGBController_LogitechG933::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG933::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG933::DeviceUpdateMode() +{ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + if(modes[active_mode].value == LOGITECH_G933_MODE_OFF) + { + controller->SetOffMode(leds[led_idx].value); + } + else + { + /*---------------------------------------------------------*\ + | Required to "reset" RGB controller and start receiving | + | color in direct mode | + \*---------------------------------------------------------*/ + controller->SetDirectMode(leds[led_idx].value); + } + + } + DeviceUpdateLEDs(); +} diff --git a/Controllers/LogitechController/LogitechG933Controller/RGBController_LogitechG933.h b/Controllers/LogitechController/LogitechG933Controller/RGBController_LogitechG933.h new file mode 100644 index 0000000..1dabedd --- /dev/null +++ b/Controllers/LogitechController/LogitechG933Controller/RGBController_LogitechG933.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechG933.h | +| | +| RGBController for Logitech G933 | +| | +| Edbgon 21 Jun 2021 | +| Based on TheRogueZeta 31 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechG933Controller.h" + +class RGBController_LogitechG933 : public RGBController +{ +public: + RGBController_LogitechG933(LogitechG933Controller* controller_ptr); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechG933Controller* controller; +}; diff --git a/Controllers/LogitechController/LogitechGLightsyncController/LogitechGLightsyncController.cpp b/Controllers/LogitechController/LogitechGLightsyncController/LogitechGLightsyncController.cpp new file mode 100644 index 0000000..df87885 --- /dev/null +++ b/Controllers/LogitechController/LogitechGLightsyncController/LogitechGLightsyncController.cpp @@ -0,0 +1,187 @@ +/*---------------------------------------------------------*\ +| LogitechGLightsyncController.cpp | +| | +| Driver for Logitech Lightsync | +| | +| TheRogueZeta 21 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechGLightsyncController.h" +#include "StringUtils.h" + +LogitechGLightsyncController::LogitechGLightsyncController(hid_device* dev_cmd_handle, hid_device *dev_handle, const char *path, unsigned char hid_dev_index, unsigned char hid_feature_index, unsigned char hid_fctn_ase_id, std::string dev_name) +{ + dev = dev_handle; + cmd_dev = dev_cmd_handle; + location = path; + dev_index = hid_dev_index; + feature_index = hid_feature_index; + fctn_ase_id = hid_fctn_ase_id; + mutex = nullptr; + name = dev_name; +} + +LogitechGLightsyncController::LogitechGLightsyncController(hid_device* dev_cmd_handle, hid_device *dev_handle, const char *path, unsigned char hid_dev_index, unsigned char hid_feature_index, unsigned char hid_fctn_ase_id, std::shared_ptr mutex_ptr, std::string dev_name) +{ + dev = dev_handle; + cmd_dev = dev_cmd_handle; + location = path; + dev_index = hid_dev_index; + feature_index = hid_feature_index; + fctn_ase_id = hid_fctn_ase_id; + mutex = mutex_ptr; + name = dev_name; +} + +LogitechGLightsyncController::~LogitechGLightsyncController() +{ + hid_close(dev); +} + +std::string LogitechGLightsyncController::GetDeviceLocation() +{ + return ("HID: " + location); +} + +std::string LogitechGLightsyncController::GetNameString() +{ + return(name); +} + +std::string LogitechGLightsyncController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LogitechGLightsyncController::UpdateMouseLED( + unsigned char mode, + std::uint16_t speed, + unsigned char zone, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness +) +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = dev_index; + usb_buf[0x02] = feature_index; + usb_buf[0x03] = fctn_ase_id; + + usb_buf[0x04] = zone; + usb_buf[0x05] = mode; + + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + + speed = 100 * speed; + if (mode == LOGITECH_G_LIGHTSYNC_MODE_STATIC) + { + usb_buf[0x09] = 0x02; + } + if (mode == LOGITECH_G_LIGHTSYNC_MODE_CYCLE) + { + usb_buf[0x0B] = speed >> 8; + usb_buf[0x0C] = speed & 0xFF; + usb_buf[0x0D] = brightness; + } + else if (mode == LOGITECH_G_LIGHTSYNC_MODE_BREATHING) + { + usb_buf[0x09] = speed >> 8; + usb_buf[0x0A] = speed & 0xFF; + usb_buf[0x0C] = brightness; + } + + /*-----------------------------------------------------*\ + | Send packet | + | This code has to be protected to avoid crashes when | + | this is called at the same time to change a powerplay | + | mat and its paired wireless mouse leds. It will | + | happen when using effects engines with high framerate | + \*-----------------------------------------------------*/ + if(mutex) + { + std::lock_guard guard(*mutex); + + hid_write(dev, usb_buf, 20); + hid_read(dev, usb_buf, 20); + } + else + { + hid_write(dev, usb_buf, 20); + hid_read(dev, usb_buf, 20); + } +} + +void LogitechGLightsyncController::SetDirectMode(bool direct) +{ + unsigned char cmd_buf[7]; + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(cmd_buf, 0x00, sizeof(cmd_buf)); + + /*-----------------------------------------------------*\ + | Set up Command Control packet | + \*-----------------------------------------------------*/ + cmd_buf[0x00] = 0x10; + cmd_buf[0x01] = dev_index; + cmd_buf[0x02] = feature_index; + cmd_buf[0x03] = 0x8A; + cmd_buf[0x04] = 0x00; + cmd_buf[0x05] = 0x00; + + /*-----------------------------------------------------*\ + | If direct, disable save to flash | + \*-----------------------------------------------------*/ + if(direct) + { + cmd_buf[0x04] = 0x01; + cmd_buf[0x05] = 0x01; + } + + /*-----------------------------------------------------*\ + | Send packet | + | This code has to be protected to avoid crashes when | + | this is called at the same time to change a powerplay | + | mat and its paired wireless mouse leds. It will | + | happen when using effects engines with high framerate | + \*-----------------------------------------------------*/ + if(mutex) + { + std::lock_guard guard(*mutex); + + hid_write(cmd_dev, cmd_buf, 7); + hid_read(dev, usb_buf, 20); + } + else + { + hid_write(cmd_dev, cmd_buf, 7); + hid_read(dev, usb_buf, 20); + } +} diff --git a/Controllers/LogitechController/LogitechGLightsyncController/LogitechGLightsyncController.h b/Controllers/LogitechController/LogitechGLightsyncController/LogitechGLightsyncController.h new file mode 100644 index 0000000..818a169 --- /dev/null +++ b/Controllers/LogitechController/LogitechGLightsyncController/LogitechGLightsyncController.h @@ -0,0 +1,91 @@ +/*---------------------------------------------------------*\ +| LogitechGLightsyncController.h | +| | +| Driver for Logitech Lightsync | +| | +| TheRogueZeta 21 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +enum +{ + LOGITECH_G_LIGHTSYNC_MODE_OFF = 0x00, + LOGITECH_G_LIGHTSYNC_MODE_STATIC = 0x01, + LOGITECH_G_LIGHTSYNC_MODE_CYCLE = 0x02, + LOGITECH_G_LIGHTSYNC_MODE_BREATHING = 0x03, +}; + +/*---------------------------------------------------------------------------------------------*\ +| Speed is 1000 for fast and 20000 for slow. | +| Values are multiplied by 100 later to give lots of GUI steps. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_G_LIGHTSYNC_SPEED_SLOWEST = 0xC8, /* Slowest speed */ + LOGITECH_G_LIGHTSYNC_SPEED_NORMAL = 0x32, /* Normal speed */ + LOGITECH_G_LIGHTSYNC_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +class LogitechGLightsyncController +{ +public: + LogitechGLightsyncController + ( + hid_device* ev_cmd_handle, + hid_device* ev_handle, + const char* ath, + unsigned char id_dev_index, + unsigned char id_feature_index, + unsigned char id_fctn_ase_id, + std::string ev_name + ); + + LogitechGLightsyncController + ( + hid_device* dev_cmd_handle, + hid_device* dev_handle, + const char* path, + unsigned char hid_dev_index, + unsigned char hid_feature_index, + unsigned char hid_fctn_ase_id, + std::shared_ptr mutex_ptr, + std::string dev_name + ); + + ~LogitechGLightsyncController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void UpdateMouseLED + ( + unsigned char mode, + unsigned short speed, + unsigned char zone, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness + ); + void SetDirectMode(bool direct); + +private: + hid_device* dev; + hid_device* cmd_dev; + std::string location; + std::string name; + unsigned char dev_index; + unsigned char feature_index; + unsigned char fctn_ase_id; + std::shared_ptr mutex; +}; diff --git a/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync.cpp b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync.cpp new file mode 100644 index 0000000..bb0fbff --- /dev/null +++ b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync.cpp @@ -0,0 +1,163 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechGLightsync.cpp | +| | +| RGBController for Logitech Lightsync | +| | +| TheRogueZeta 21 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LogitechGLightsync.h" + +/**------------------------------------------------------------------*\ + @name Logitech Lightsync Mouse + @category Mouse + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechMouseG303, DetectLogitechMouseG403 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechGLightsync::RGBController_LogitechGLightsync(LogitechGLightsyncController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_MOUSE; + description = "Logitech G Lightsync Mouse"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G_LIGHTSYNC_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LOGITECH_G_LIGHTSYNC_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = LOGITECH_G_LIGHTSYNC_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_G_LIGHTSYNC_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G_LIGHTSYNC_SPEED_FASTEST; + Cycle.speed = LOGITECH_G_LIGHTSYNC_SPEED_NORMAL; + Cycle.brightness_min = 0; + Cycle.brightness_max = 100; + Cycle.brightness = 100; + modes.push_back(Cycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G_LIGHTSYNC_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = LOGITECH_G_LIGHTSYNC_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G_LIGHTSYNC_SPEED_FASTEST; + Breathing.speed = LOGITECH_G_LIGHTSYNC_SPEED_NORMAL; + Breathing.brightness_min = 0; + Breathing.brightness_max = 100; + Breathing.brightness = 100; + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_LogitechGLightsync::~RGBController_LogitechGLightsync() +{ + delete controller; +} + +void RGBController_LogitechGLightsync::SetupZones() +{ + zone GLightsync_primary_zone; + GLightsync_primary_zone.name = "DPI"; + GLightsync_primary_zone.type = ZONE_TYPE_SINGLE; + GLightsync_primary_zone.leds_min = 1; + GLightsync_primary_zone.leds_max = 1; + GLightsync_primary_zone.leds_count = 1; + GLightsync_primary_zone.matrix_map = NULL; + zones.push_back(GLightsync_primary_zone); + + led GLightsync_primary_led; + GLightsync_primary_led.name = "DPI"; + leds.push_back(GLightsync_primary_led); + + zone GLightsync_logo_zone; + GLightsync_logo_zone.name = "Logo"; + GLightsync_logo_zone.type = ZONE_TYPE_SINGLE; + GLightsync_logo_zone.leds_min = 1; + GLightsync_logo_zone.leds_max = 1; + GLightsync_logo_zone.leds_count = 1; + GLightsync_logo_zone.matrix_map = NULL; + zones.push_back(GLightsync_logo_zone); + + led GLightsync_logo_led; + GLightsync_logo_led.name = "Logo"; + leds.push_back(GLightsync_logo_led); + + SetupColors(); +} + +void RGBController_LogitechGLightsync::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechGLightsync::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); + UpdateZoneLEDs(1); +} + +void RGBController_LogitechGLightsync::UpdateZoneLEDs(int zone) +{ + unsigned char red = RGBGetRValue(colors[zone]); + unsigned char grn = RGBGetGValue(colors[zone]); + unsigned char blu = RGBGetBValue(colors[zone]); + + /*---------------------------------------------------------*\ + | Replace direct mode with static when sending to controller| + \*---------------------------------------------------------*/ + unsigned char temp_mode = (modes[active_mode].value != 0xFF) ? modes[active_mode].value : LOGITECH_G_LIGHTSYNC_MODE_STATIC; + + controller->UpdateMouseLED(temp_mode, modes[active_mode].speed, zone, red, grn, blu, modes[active_mode].brightness); +} + +void RGBController_LogitechGLightsync::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_LogitechGLightsync::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | If direct mode is true, then sent the packet to put the | + | mouse in direct mode. This code will only be called when | + | we change modes as to not spam the device. | + \*---------------------------------------------------------*/ + controller->SetDirectMode(modes[active_mode].value == 0xFF); + DeviceUpdateLEDs(); +} diff --git a/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync.h b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync.h new file mode 100644 index 0000000..98105b5 --- /dev/null +++ b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechGLightsync.h | +| | +| RGBController for Logitech Lightsync | +| | +| TheRogueZeta 21 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechGLightsyncController.h" + +class RGBController_LogitechGLightsync : public RGBController +{ +public: + RGBController_LogitechGLightsync(LogitechGLightsyncController* controller_ptr); + ~RGBController_LogitechGLightsync(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechGLightsyncController* controller; +}; diff --git a/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync1zone.cpp b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync1zone.cpp new file mode 100644 index 0000000..19c8f8a --- /dev/null +++ b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync1zone.cpp @@ -0,0 +1,149 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechGLightsync1zone.cpp | +| | +| RGBController for single zone Logitech Lightsync | +| | +| TheRogueZeta 21 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LogitechGLightsync1zone.h" + +/**------------------------------------------------------------------*\ + @name Logitech Lightsync Mouse (1 Zone) + @category Mouse + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechMouseG203, DetectLogitechMouseGPRO + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechGLightsync1zone::RGBController_LogitechGLightsync1zone(LogitechGLightsyncController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_MOUSE; + description = "Logitech G Lightsync Mouse Single Zone"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G_LIGHTSYNC_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LOGITECH_G_LIGHTSYNC_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = LOGITECH_G_LIGHTSYNC_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_G_LIGHTSYNC_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G_LIGHTSYNC_SPEED_FASTEST; + Cycle.speed = LOGITECH_G_LIGHTSYNC_SPEED_NORMAL; + Cycle.brightness_min = 0; + Cycle.brightness_max = 100; + Cycle.brightness = 100; + modes.push_back(Cycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G_LIGHTSYNC_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = LOGITECH_G_LIGHTSYNC_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G_LIGHTSYNC_SPEED_FASTEST; + Breathing.speed = LOGITECH_G_LIGHTSYNC_SPEED_NORMAL; + Breathing.brightness_min = 0; + Breathing.brightness_max = 100; + Breathing.brightness = 100; + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_LogitechGLightsync1zone::~RGBController_LogitechGLightsync1zone() +{ + delete controller; +} + +void RGBController_LogitechGLightsync1zone::SetupZones() +{ + zone GLightsync_logo_zone; + GLightsync_logo_zone.name = "Logo"; + GLightsync_logo_zone.type = ZONE_TYPE_SINGLE; + GLightsync_logo_zone.leds_min = 1; + GLightsync_logo_zone.leds_max = 1; + GLightsync_logo_zone.leds_count = 1; + GLightsync_logo_zone.matrix_map = NULL; + zones.push_back(GLightsync_logo_zone); + + led GLightsync_logo_led; + GLightsync_logo_led.name = "Logo"; + leds.push_back(GLightsync_logo_led); + + SetupColors(); +} + +void RGBController_LogitechGLightsync1zone::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechGLightsync1zone::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_LogitechGLightsync1zone::UpdateZoneLEDs(int zone) +{ + unsigned char red = RGBGetRValue(colors[zone]); + unsigned char grn = RGBGetGValue(colors[zone]); + unsigned char blu = RGBGetBValue(colors[zone]); + + /*---------------------------------------------------------*\ + | Replace direct mode with static when sending to controller| + \*---------------------------------------------------------*/ + unsigned char temp_mode = (modes[active_mode].value != 0xFF) ? modes[active_mode].value : LOGITECH_G_LIGHTSYNC_MODE_STATIC; + + controller->UpdateMouseLED(temp_mode, modes[active_mode].speed, zone, red, grn, blu, modes[active_mode].brightness); +} + +void RGBController_LogitechGLightsync1zone::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_LogitechGLightsync1zone::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | If direct mode is true, then sent the packet to put the | + | mouse in direct mode. This code will only be called when | + | we change modes as to not spam the device. | + \*---------------------------------------------------------*/ + controller->SetDirectMode(modes[active_mode].value == 0xFF); + DeviceUpdateLEDs(); +} diff --git a/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync1zone.h b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync1zone.h new file mode 100644 index 0000000..c625abc --- /dev/null +++ b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync1zone.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechGLightsync1zone.h | +| | +| RGBController for single zone Logitech Lightsync | +| | +| TheRogueZeta 21 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechGLightsyncController.h" + +class RGBController_LogitechGLightsync1zone : public RGBController +{ +public: + RGBController_LogitechGLightsync1zone(LogitechGLightsyncController* controller_ptr); + ~RGBController_LogitechGLightsync1zone(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechGLightsyncController* controller; +}; diff --git a/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGPowerPlay.cpp b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGPowerPlay.cpp new file mode 100644 index 0000000..7d89320 --- /dev/null +++ b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGPowerPlay.cpp @@ -0,0 +1,143 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechGPowerPlay.cpp | +| | +| RGBController for Logitech G PowerPlay | +| | +| TheRogueZeta 31 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LogitechGPowerPlay.h" + +/**------------------------------------------------------------------*\ + @name Logitech Powerplay Mat + @category Mousemat + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechGPowerPlay::RGBController_LogitechGPowerPlay(LogitechGLightsyncController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_MOUSEMAT; + description = "Logitech G PowerPlay Wireless Charging System"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G_LIGHTSYNC_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LOGITECH_G_LIGHTSYNC_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = LOGITECH_G_LIGHTSYNC_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_G_LIGHTSYNC_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G_LIGHTSYNC_SPEED_FASTEST; + Cycle.speed = LOGITECH_G_LIGHTSYNC_SPEED_NORMAL; + modes.push_back(Cycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G_LIGHTSYNC_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = LOGITECH_G_LIGHTSYNC_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G_LIGHTSYNC_SPEED_FASTEST; + Breathing.speed = LOGITECH_G_LIGHTSYNC_SPEED_NORMAL; + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_LogitechGPowerPlay::~RGBController_LogitechGPowerPlay() +{ + delete controller; +} + +void RGBController_LogitechGPowerPlay::SetupZones() +{ + zone GPowerPlay_logo_zone; + GPowerPlay_logo_zone.name = "Logo"; + GPowerPlay_logo_zone.type = ZONE_TYPE_SINGLE; + GPowerPlay_logo_zone.leds_min = 1; + GPowerPlay_logo_zone.leds_max = 1; + GPowerPlay_logo_zone.leds_count = 1; + GPowerPlay_logo_zone.matrix_map = NULL; + zones.push_back(GPowerPlay_logo_zone); + + led GPowerPlay_logo_led; + GPowerPlay_logo_led.name = "Logo"; + leds.push_back(GPowerPlay_logo_led); + + SetupColors(); +} + +void RGBController_LogitechGPowerPlay::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechGPowerPlay::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_LogitechGPowerPlay::UpdateZoneLEDs(int zone) +{ + unsigned char red = RGBGetRValue(colors[zone]); + unsigned char grn = RGBGetGValue(colors[zone]); + unsigned char blu = RGBGetBValue(colors[zone]); + + /*---------------------------------------------------------*\ + | Replace direct mode with static when sending to controller| + \*---------------------------------------------------------*/ + unsigned char temp_mode = (modes[active_mode].value != 0xFF) ? modes[active_mode].value : LOGITECH_G_LIGHTSYNC_MODE_STATIC; + + controller->UpdateMouseLED(temp_mode, modes[active_mode].speed, zone, red, grn, blu, /* Brightness */ 0x64); +} + +void RGBController_LogitechGPowerPlay::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_LogitechGPowerPlay::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | If direct mode is true, then sent the packet to put the | + | mouse in direct mode. This code will only be called when | + | we change modes as to not spam the device. | + \*---------------------------------------------------------*/ + controller->SetDirectMode(modes[active_mode].value == 0xFF); + DeviceUpdateLEDs(); +} diff --git a/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGPowerPlay.h b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGPowerPlay.h new file mode 100644 index 0000000..e9acd05 --- /dev/null +++ b/Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGPowerPlay.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechGPowerPlay.h | +| | +| RGBController for Logitech G PowerPlay | +| | +| TheRogueZeta 31 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechGLightsyncController.h" + +class RGBController_LogitechGPowerPlay : public RGBController +{ +public: + RGBController_LogitechGPowerPlay(LogitechGLightsyncController* controller_ptr); + ~RGBController_LogitechGPowerPlay(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechGLightsyncController* controller; +}; diff --git a/Controllers/LogitechController/LogitechGProKeyboardController/LogitechGProKeyboardController.cpp b/Controllers/LogitechController/LogitechGProKeyboardController/LogitechGProKeyboardController.cpp new file mode 100644 index 0000000..f47f192 --- /dev/null +++ b/Controllers/LogitechController/LogitechGProKeyboardController/LogitechGProKeyboardController.cpp @@ -0,0 +1,192 @@ +/*---------------------------------------------------------*\ +| LogitechGProController.cpp | +| | +| Driver for Logitech G Pro keyboard | +| | +| sanchezzzs 20 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechGProKeyboardController.h" +#include "StringUtils.h" + +LogitechGProKeyboardController::LogitechGProKeyboardController(hid_device* dev_handle_0x11, hid_device* dev_handle_0x12, std::string dev_name) +{ + dev_pkt_0x11 = dev_handle_0x11; + dev_pkt_0x12 = dev_handle_0x12; + name = dev_name; +} + +LogitechGProKeyboardController::~LogitechGProKeyboardController() +{ + hid_close(dev_pkt_0x11); + hid_close(dev_pkt_0x12); +} + +std::string LogitechGProKeyboardController::GetNameString() +{ + return(name); +} + +std::string LogitechGProKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev_pkt_0x11, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LogitechGProKeyboardController::Commit() +{ + SendCommit(); +} + +void LogitechGProKeyboardController::SetDirect + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ) +{ + SendDirectFrame(zone, frame_count, frame_data); +} + +void LogitechGProKeyboardController::SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SendMode(LOGITECH_GPRO_ZONE_MODE_KEYBOARD, mode, speed, red, green, blue); + SendMode(LOGITECH_GPRO_ZONE_MODE_LOGO, mode, speed, red, green, blue); + + SendCommit(); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void LogitechGProKeyboardController::SendCommit() +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0C; + usb_buf[0x03] = 0x5D; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); +} + +void LogitechGProKeyboardController::SendDirectFrame + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ) +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x12; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0C; + usb_buf[0x03] = 0x3D; + usb_buf[0x05] = zone; + usb_buf[0x07] = frame_count; + + /*-----------------------------------------------------*\ + | Copy in frame data | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x08], frame_data, frame_count * 4); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x12, usb_buf, 64); + hid_read(dev_pkt_0x11, usb_buf, 20); +} + +void LogitechGProKeyboardController::SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x0D; + usb_buf[0x03] = 0x3D; + usb_buf[0x04] = zone; + + usb_buf[0x05] = mode; + + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + + speed = 100 * speed; + if(mode == LOGITECH_GPRO_MODE_CYCLE) + { + usb_buf[0x0B] = speed >> 8; + usb_buf[0x0C] = speed & 0xFF; + usb_buf[0x0D] = 0x64; + } + else if(mode == LOGITECH_GPRO_MODE_BREATHING) + { + usb_buf[0x09] = speed >> 8; + usb_buf[0x0A] = speed & 0xFF; + usb_buf[0x0C] = 0x64; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev_pkt_0x11, usb_buf, 20); + hid_read(dev_pkt_0x11, usb_buf, 20); +} diff --git a/Controllers/LogitechController/LogitechGProKeyboardController/LogitechGProKeyboardController.h b/Controllers/LogitechController/LogitechGProKeyboardController/LogitechGProKeyboardController.h new file mode 100644 index 0000000..79d8600 --- /dev/null +++ b/Controllers/LogitechController/LogitechGProKeyboardController/LogitechGProKeyboardController.h @@ -0,0 +1,102 @@ +/*---------------------------------------------------------*\ +| LogitechGProController.h | +| | +| Driver for Logitech G Pro keyboard | +| | +| sanchezzzs 20 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + LOGITECH_GPRO_ZONE_MODE_KEYBOARD = 0x00, + LOGITECH_GPRO_ZONE_MODE_LOGO = 0x01, +}; + +enum +{ + LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD = 0x01, + LOGITECH_GPRO_ZONE_DIRECT_MEDIA = 0x02, + LOGITECH_GPRO_ZONE_DIRECT_LOGO = 0x10, + LOGITECH_GPRO_ZONE_DIRECT_INDICATORS = 0x40, +}; + +enum +{ + LOGITECH_GPRO_MODE_OFF = 0x00, + LOGITECH_GPRO_MODE_STATIC = 0x01, + LOGITECH_GPRO_MODE_BREATHING = 0x02, + LOGITECH_GPRO_MODE_CYCLE = 0x03, + LOGITECH_GPRO_MODE_WAVE = 0x04, +}; + +/*---------------------------------------------------------------------------------------------*\ +| Speed is 1000 for fast and 20000 for slow. | +| Values are multiplied by 100 later to give lots of GUI steps. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_GPRO_SPEED_SLOWEST = 0xC8, /* Slowest speed */ + LOGITECH_GPRO_SPEED_NORMAL = 0x32, /* Normal speed */ + LOGITECH_GPRO_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +class LogitechGProKeyboardController +{ +public: + LogitechGProKeyboardController(hid_device* dev_handle_0x11, hid_device* dev_handle_0x12, std::string dev_name); + ~LogitechGProKeyboardController(); + + std::string GetNameString(); + std::string GetSerialString(); + + void Commit(); + + void SetDirect + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ); + + void SetMode + ( + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev_pkt_0x11; + hid_device* dev_pkt_0x12; + std::string name; + + void SendDirectFrame + ( + unsigned char zone, + unsigned char frame_count, + unsigned char * frame_data + ); + + void SendMode + ( + unsigned char zone, + unsigned char mode, + unsigned short speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SendCommit(); +}; diff --git a/Controllers/LogitechController/LogitechGProKeyboardController/RGBController_LogitechGProKeyboard.cpp b/Controllers/LogitechController/LogitechGProKeyboardController/RGBController_LogitechGProKeyboard.cpp new file mode 100644 index 0000000..7749b05 --- /dev/null +++ b/Controllers/LogitechController/LogitechGProKeyboardController/RGBController_LogitechGProKeyboard.cpp @@ -0,0 +1,379 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechGPro.cpp | +| | +| RGBController for Logitech G Pro keyboard | +| | +| sanchezzzs 20 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_LogitechGProKeyboard.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[7][19] = + { { 89, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 93, 92, NA, NA, 91, NA, 90 }, + { 37, NA, 54, 55, 56, 57, NA, 58, 59, 60, 61, NA, 62, 63, 64, 65, 66, 67, 68 }, + { 49, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NA, 41, 42, 38, NA, 69, 70, 71 }, + { 39, NA, 16, 22, 4, 17, NA, 19, 24, 20, 8, 14, 15, 43, 44, 45, 72, 73, 74 }, + { 53, NA, 0, 18, 3, 5, NA, 6, 7, 9, 10, 11, 47, 48, 46, 36, NA, NA, NA }, + { 82, 79, 25, 23, 2, 21, NA, 1, NA, 13, 12, 50, 51, 52, 86, NA, NA, 78, NA }, + { 81, 84, 83, NA, NA, NA, NA, 40, NA, NA, NA, NA, 87, 88, 80, 85, 76, 77, 75 } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 94, +}; + +typedef struct +{ + const char * name; + const unsigned char zone; + const unsigned char idx; +} led_type; + +static const led_type led_names[] = +{ + /* Key Label Zone, Index */ + { KEY_EN_A, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x04 },//00 + { KEY_EN_B, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x05 }, + { KEY_EN_C, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x06 }, + { KEY_EN_D, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x07 }, + { KEY_EN_E, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x08 }, + { KEY_EN_F, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x09 }, + { KEY_EN_G, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x0A }, + { KEY_EN_H, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x0B }, + { KEY_EN_I, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x0C }, + { KEY_EN_J, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x0D }, + { KEY_EN_K, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x0E },//10 + { KEY_EN_L, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x0F }, + { KEY_EN_M, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x10 }, + { KEY_EN_N, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x11 }, + { KEY_EN_O, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x12 }, + { KEY_EN_P, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x13 }, + { KEY_EN_Q, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x14 }, + { KEY_EN_R, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x15 }, + { KEY_EN_S, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x16 }, + { KEY_EN_T, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x17 }, + { KEY_EN_U, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x18 },//20 + { KEY_EN_V, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x19 }, + { KEY_EN_W, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x1A }, + { KEY_EN_X, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x1B }, + { KEY_EN_Y, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x1C }, + { KEY_EN_Z, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x1D }, + { KEY_EN_1, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x1E }, + { KEY_EN_2, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x1F }, + { KEY_EN_3, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x20 }, + { KEY_EN_4, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x21 }, + { KEY_EN_5, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x22 },//30 + { KEY_EN_6, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x23 }, + { KEY_EN_7, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x24 }, + { KEY_EN_8, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x25 }, + { KEY_EN_9, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x26 }, + { KEY_EN_0, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x27 }, + { KEY_EN_ANSI_ENTER, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x28 }, + { KEY_EN_ESCAPE, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x29 }, + { KEY_EN_BACKSPACE, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x2A }, + { KEY_EN_TAB, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x2B }, + { KEY_EN_SPACE, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x2C },//40 + { KEY_EN_MINUS, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x2D }, + { KEY_EN_EQUALS, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x2E }, + { KEY_EN_LEFT_BRACKET, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x2F }, + { KEY_EN_RIGHT_BRACKET, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x30 }, + { KEY_EN_ANSI_BACK_SLASH, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x31 },//ANSI only + { KEY_EN_POUND, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x32 },//ISO only + { KEY_EN_SEMICOLON, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x33 }, + { KEY_EN_QUOTE, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x34 }, + { KEY_EN_BACK_TICK, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x35 }, + { KEY_EN_COMMA, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x36 },//50 + { KEY_EN_PERIOD, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x37 }, + { KEY_EN_FORWARD_SLASH, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x38 }, + { KEY_EN_CAPS_LOCK, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x39 }, + { KEY_EN_F1, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x3A }, + { KEY_EN_F2, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x3B }, + { KEY_EN_F3, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x3C }, + { KEY_EN_F4, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x3D }, + { KEY_EN_F5, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x3E }, + { KEY_EN_F6, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x3F }, + { KEY_EN_F7, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x40 },//60 + { KEY_EN_F8, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x41 }, + { KEY_EN_F9, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x42 }, + { KEY_EN_F10, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x43 }, + { KEY_EN_F11, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x44 }, + { KEY_EN_F12, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x45 }, + { KEY_EN_PRINT_SCREEN, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x46 }, + { KEY_EN_SCROLL_LOCK, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x47 }, + { KEY_EN_PAUSE_BREAK, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x48 }, + { KEY_EN_INSERT, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x49 }, + { KEY_EN_HOME, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x4A },//70 + { KEY_EN_PAGE_UP, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x4B }, + { KEY_EN_DELETE, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x4C }, + { KEY_EN_END, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x4D }, + { KEY_EN_PAGE_DOWN, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x4E }, + { KEY_EN_RIGHT_ARROW, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x4F }, + { KEY_EN_LEFT_ARROW, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x50 }, + { KEY_EN_DOWN_ARROW, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x51 }, + { KEY_EN_UP_ARROW, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x52 }, + { KEY_EN_ISO_BACK_SLASH, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x64 },//ISO only + { KEY_EN_MENU, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0x65 },//80 + { KEY_EN_LEFT_CONTROL, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0xE0 }, + { KEY_EN_LEFT_SHIFT, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0xE1 }, + { KEY_EN_LEFT_ALT, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0xE2 }, + { KEY_EN_LEFT_WINDOWS, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0xE3 }, + { KEY_EN_RIGHT_CONTROL, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0xE4 }, + { KEY_EN_RIGHT_SHIFT, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0xE5 }, + { KEY_EN_RIGHT_ALT, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0xE6 }, + { KEY_EN_RIGHT_FUNCTION, LOGITECH_GPRO_ZONE_DIRECT_KEYBOARD, 0xE7 }, + { "Logo", LOGITECH_GPRO_ZONE_DIRECT_LOGO, 0x01 }, + { "Lighting", LOGITECH_GPRO_ZONE_DIRECT_INDICATORS, 0x01 },//90 + { "Game Mode", LOGITECH_GPRO_ZONE_DIRECT_INDICATORS, 0x02 }, + { "Caps Lock Indicator", LOGITECH_GPRO_ZONE_DIRECT_INDICATORS, 0x03 }, + { "Scroll Lock Indicator", LOGITECH_GPRO_ZONE_DIRECT_INDICATORS, 0x04 },//93 +}; + +/**------------------------------------------------------------------*\ + @name Logitech G Pro + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechKeyboardGPro + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechGProKeyboard::RGBController_LogitechGProKeyboard(LogitechGProKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Logitech"; + type = DEVICE_TYPE_KEYBOARD; + description = "Logitech Keyboard Device"; + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LOGITECH_GPRO_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_GPRO_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Cycle; + Cycle.name = "Cycle"; + Cycle.value = LOGITECH_GPRO_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_GPRO_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_GPRO_SPEED_FASTEST; + Cycle.speed = LOGITECH_GPRO_SPEED_NORMAL; + modes.push_back(Cycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_GPRO_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + Breathing.speed_min = LOGITECH_GPRO_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_GPRO_SPEED_FASTEST; + Breathing.speed = LOGITECH_GPRO_SPEED_NORMAL; + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_LogitechGProKeyboard::~RGBController_LogitechGProKeyboard() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_LogitechGProKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 7; + new_zone.matrix_map->width = 19; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx].name; + new_led.value = ( led_names[led_idx].zone << 8 ) + led_names[led_idx].idx; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_LogitechGProKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechGProKeyboard::DeviceUpdateLEDs() +{ + #define MAX_FRAMES_PER_PACKET 0x0E + + unsigned char frame_buf[MAX_FRAMES_PER_PACKET * 4]; + unsigned char frame_cnt = 0; + unsigned char prev_zone = 0; + unsigned char zone = 0; + unsigned char idx = 0; + + /*---------------------------------------------------------*\ + | TODO: Send packets with multiple LED frames | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + zone = ( leds[led_idx].value >> 8 ); + idx = ( leds[led_idx].value & 0xFF ); + + if((zone != prev_zone) && (frame_cnt != 0)) + { + controller->SetDirect(prev_zone, frame_cnt, frame_buf); + frame_cnt = 0; + } + + frame_buf[(frame_cnt * 4) + 0] = idx; + frame_buf[(frame_cnt * 4) + 1] = RGBGetRValue(colors[led_idx]); + frame_buf[(frame_cnt * 4) + 2] = RGBGetGValue(colors[led_idx]); + frame_buf[(frame_cnt * 4) + 3] = RGBGetBValue(colors[led_idx]); + + frame_cnt++; + prev_zone = zone; + + if(frame_cnt == MAX_FRAMES_PER_PACKET) + { + controller->SetDirect(prev_zone, frame_cnt, frame_buf); + frame_cnt = 0; + } + } + + if(frame_cnt != 0) + { + controller->SetDirect(prev_zone, frame_cnt, frame_buf); + } + + controller->Commit(); +} + +void RGBController_LogitechGProKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechGProKeyboard::UpdateSingleLED(int led) +{ + unsigned char frame[4]; + unsigned char zone; + unsigned char idx; + + zone = ( leds[led].value >> 8 ); + idx = ( leds[led].value & 0xFF ); + + frame[0] = idx; + frame[1] = RGBGetRValue(colors[led]); + frame[2] = RGBGetGValue(colors[led]); + frame[3] = RGBGetBValue(colors[led]); + + controller->SetDirect(zone, 1, frame); + controller->Commit(); +} + +void RGBController_LogitechGProKeyboard::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Direct mode does not send a mode packet | + | Call UpdateLEDs to send direct packet | + \*---------------------------------------------------------*/ + if(active_mode == 0xFFFF) + { + UpdateLEDs(); + return; + } + + unsigned char red = 0; + unsigned char grn = 0; + unsigned char blu = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, red, grn, blu); +} diff --git a/Controllers/LogitechController/LogitechGProKeyboardController/RGBController_LogitechGProKeyboard.h b/Controllers/LogitechController/LogitechGProKeyboardController/RGBController_LogitechGProKeyboard.h new file mode 100644 index 0000000..fedddb4 --- /dev/null +++ b/Controllers/LogitechController/LogitechGProKeyboardController/RGBController_LogitechGProKeyboard.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechGPro.h | +| | +| RGBController for Logitech G Pro keyboard | +| | +| sanchezzzs 20 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechGProKeyboardController.h" + +class RGBController_LogitechGProKeyboard : public RGBController +{ +public: + RGBController_LogitechGProKeyboard(LogitechGProKeyboardController* controller_ptr); + ~RGBController_LogitechGProKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + LogitechGProKeyboardController* controller; +}; diff --git a/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller.cpp b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller.cpp new file mode 100644 index 0000000..8c41ee5 --- /dev/null +++ b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller.cpp @@ -0,0 +1,5709 @@ +/*---------------------------------------------------------*\ +| LogitechHIDPP20Controller.cpp | +| | +| Unified Logitech HID++ 2.0 controller implementation | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "LogitechHIDPP20Controller.h" +#include "RGBController_LogitechHIDPP20.h" +#include "LogManager.h" + +#include "LogitechHIDPP20IdleSettings.h" + + +#define LOG_TAG log_tag.c_str() + +/*----------------------------------------------------------*\ +| Hard cap on per-call non-HID++ drains in the read loop. | +| A high-polling-rate mouse can put 50+ input reports in the | +| buffer between our reads; this cap prevents pathological | +| input-flood scenarios from locking up a single read call. | +| 64 is enough headroom for normal congestion at 1 kHz. | +\*----------------------------------------------------------*/ +static const int HIDPP20_READ_DRAIN_BUDGET = 64; + +/*----------------------------------------------------------*\ +| Per-candidate read timeout (ms) for the Centurion 0x50 | +| device-address probe. USB round-trip is <1ms; 5ms gives | +| 5x margin. Worst case (no device responds) 256 × 5 = | +| ~1.3s; typical G522 at addr 0x23 is ~180ms. Matches | +| Solaar's probe_centurion_device_addr constant. | +\*----------------------------------------------------------*/ +static const int CENTURION_PROBE_PER_ADDR_TIMEOUT_MS = 5; + +/*----------------------------------------------------------*\ +| Observed HID++ 2.0 feature versions. Each row is a feature | +| ID plus the versions we've empirically verified working. | +| When feature discovery reports a version outside this set, | +| we log a one-shot INFO tripwire so a tester with new | +| hardware immediately surfaces unknown firmware revs. | +| | +| Purely observational — no behavior branches on version. | +| Solaar has effectively zero version gating for the RGB | +| features we implement, so we don't either; the table is a | +| "have we seen this combination work" ledger, not a | +| compatibility matrix. Add versions as devices report them. | +| | +| A feature_id absent from this table is silent (no | +| tripwire). Only features we actually exercise are worth | +| flagging. | +\*----------------------------------------------------------*/ +struct HIDPP20FeatureVersionSet +{ + uint16_t feature_id; + uint8_t versions[8]; /* approved versions; first `count` valid */ + uint8_t count; +}; + +static constexpr HIDPP20FeatureVersionSet HIDPP20_FEATURE_OBSERVED_VERSIONS[] = +{ + { 0x0620, { 1 }, 1 }, + { 0x1D4B, { 0 }, 1 }, + { 0x4540, { 1 }, 1 }, + { 0x8071, { 4 }, 1 }, + { 0x8081, { 0, 2 }, 2 }, +}; + +/*----------------------------------------------------------*\ +| Returns true if feature_id is not tracked (silent) or if | +| version appears in the tracked feature's approved set. | +\*----------------------------------------------------------*/ +static bool FeatureVersionIsObserved(uint16_t feature_id, uint8_t version) +{ + size_t table_len = sizeof(HIDPP20_FEATURE_OBSERVED_VERSIONS) + / sizeof(HIDPP20_FEATURE_OBSERVED_VERSIONS[0]); + + for(size_t r = 0; r < table_len; r++) + { + const HIDPP20FeatureVersionSet& row = HIDPP20_FEATURE_OBSERVED_VERSIONS[r]; + + if(row.feature_id != feature_id) + { + continue; + } + + for(uint8_t i = 0; i < row.count; i++) + { + if(row.versions[i] == version) + { + return true; + } + } + + return false; /* tracked feature, unknown version */ + } + + return true; /* feature not tracked — silent */ +} + +LogitechHIDPP20Controller::LogitechHIDPP20Controller + ( + hid_device* dev, + const char* path, + uint8_t device_index, + bool wireless, + std::shared_ptr mutex_ptr, + uint16_t usage_page + ) +{ + this->dev = dev; + this->location = path; + this->device_index = device_index; + this->wireless = wireless; + this->mutex = mutex_ptr; + this->initialized = false; + this->sw_control_claimed = false; + this->sw_control_needs_upgrade_to_5 = false; + this->frame_counter = 0; + this->retry_paint_deadline_.store(std::chrono::steady_clock::time_point{}); + this->retry_paint_attempt_.store(0); + this->wake_full_repaint_pending_.store(false); + this->init_generation = 0; + this->log_tag = "[LogitechHID++ " + std::string(path) + "]"; + this->reader_thread = nullptr; + this->reader_running = false; + this->power_thread = nullptr; + this->power_thread_running = false; + this->pending_activity = -1; + this->pending_connection = 0; + this->pending_path_check = 0; + this->device_online = true; + this->consecutive_timeouts = 0; + this->watcher_mode = false; + this->power_state = HIDPP20_POWER_ACTIVE; + this->deep_sleep = false; + this->consecutive_frame_end_failures = 0; + this->dim_brightness_pct = 100; + this->dim_step = 0; + this->idle_timeout_s = 60; + this->sleep_timeout_s = 300; + + caps = {}; + + /*---------------------------------------------------------*\ + | Default to standard HID++ transport; DiscoverTransport() | + | may change this during Probe() if Centurion is detected. | + \*---------------------------------------------------------*/ + transport.type = HIDPP20_TRANSPORT_STANDARD; + transport.usage_page = usage_page; + transport.report_id = LOGITECH_LONG_MESSAGE; + transport.addressed = false; + transport.device_address = 0x00; + transport.bridge_feat_idx = 0; + transport.sub_device_id = 0; + transport.bridge_mtu = 0; +} + +LogitechHIDPP20Controller::~LogitechHIDPP20Controller() +{ + if(initialized) + { + Shutdown(); + } + + if(dev) + { + hid_close(dev); + } +} + +/*---------------------------------------------------------*\ +| Transport-layer I/O | +| | +| SendMessage/ReadMessage dispatch to the appropriate | +| transport implementation based on transport.type. | +| SendAndReceive is a convenience wrapper. | +\*---------------------------------------------------------*/ + +int LogitechHIDPP20Controller::SendMessage + ( + uint8_t feat_idx, + uint8_t function, + const uint8_t* data, + size_t len + ) +{ + switch(transport.type) + { + case HIDPP20_TRANSPORT_CENTURION: + return SendCenturion(feat_idx, function, data, len); + + case HIDPP20_TRANSPORT_STANDARD: + default: + return SendStandard(feat_idx, function, data, len); + } +} + +int LogitechHIDPP20Controller::ReadMessage + ( + uint8_t* feat_idx_out, + uint8_t* function_out, + uint8_t* data_out, + size_t data_max, + int timeout_ms + ) +{ + /*---------------------------------------------------------*\ + | When the reader thread is running, it is the sole caller | + | of hid_read_timeout. All other reads come from the queue. | + | Before the reader starts (during Probe/Initialize), read | + | directly from HID. | + \*---------------------------------------------------------*/ + if(reader_running.load()) + { + return ReadFromQueue(feat_idx_out, function_out, data_out, data_max, timeout_ms); + } + + return ReadHIDDirect(feat_idx_out, function_out, data_out, data_max, timeout_ms); +} + +int LogitechHIDPP20Controller::ReadHIDDirect + ( + uint8_t* feat_idx_out, + uint8_t* function_out, + uint8_t* data_out, + size_t data_max, + int timeout_ms + ) +{ + switch(transport.type) + { + case HIDPP20_TRANSPORT_CENTURION: + return ReadCenturionDirect(feat_idx_out, function_out, data_out, data_max, timeout_ms); + + case HIDPP20_TRANSPORT_STANDARD: + default: + return ReadStandardDirect(feat_idx_out, function_out, data_out, data_max, timeout_ms); + } +} + +int LogitechHIDPP20Controller::ReadFromQueue + ( + uint8_t* feat_idx_out, + uint8_t* function_out, + uint8_t* data_out, + size_t data_max, + int timeout_ms + ) +{ + std::unique_lock lock(response_mutex); + + std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(timeout_ms); + + while(response_queue.empty()) + { + if(response_cv.wait_until(lock, deadline) == std::cv_status::timeout) + { + /*-------------------------------------------------*\ + | Offline detection lives at the SendAcked layer | + | now: one tick per fully-failed call, not per | + | per-attempt read window. Streaming policies that | + | retry several times don't artificially accelerate | + | the offline declaration. | + \*-------------------------------------------------*/ + return 0; + } + + if(!reader_running.load()) + { + return 0; + } + } + + HIDPP20RawMessage msg = response_queue.front(); + response_queue.pop_front(); + + if(feat_idx_out) + { + *feat_idx_out = msg.feat; + } + + if(function_out) + { + *function_out = msg.func; + } + + if(data_out && data_max > 0) + { + size_t copy_len = (data_max > sizeof(msg.data)) ? sizeof(msg.data) : data_max; + memcpy(data_out, msg.data, copy_len); + } + + return msg.result; +} + +int LogitechHIDPP20Controller::SendAndReceive + ( + uint8_t feat_idx, + uint8_t function, + const uint8_t* send_data, + size_t send_len, + uint8_t* recv_data, + size_t recv_max + ) +{ + /*---------------------------------------------------------*\ + | Thin wrapper around SendAcked with the reliable policy. | + | Preserved as a named entry point so existing call sites | + | don't need to be touched. | + \*---------------------------------------------------------*/ + return SendAcked(feat_idx, function, + send_data, send_len, + recv_data, recv_max, + HIDPP20_POLICY_RELIABLE); +} + +int LogitechHIDPP20Controller::SendAcked + ( + uint8_t feat_idx, + uint8_t function, + const uint8_t* send_data, + size_t send_len, + uint8_t* recv_data, + size_t recv_max, + const HIDPP20RetryPolicy& policy, + uint8_t* hidpp20_error_out + ) +{ + /*----------------------------------------------------------*\ + | Universal send-and-ack with policy-driven retry. Mirrors | + | the firmware's own event burst pattern: 7-attempt | + | exponential backoff for reliable one-shot commands, tight | + | 2-attempt for streaming animation frames. | + | | + | Loop semantics per attempt: | + | 1. Sleep backoff_ms[i] (0 on first attempt) | + | 2. Bail if device went offline | + | 3. SendMessage; on wire error, mark and retry | + | 4. Read loop bounded by read_window_ms: | + | - matching response -> success | + | - HID++ error 0xFF for our request: | + | BUSY (0x08) + retry_on_busy -> retry the send | + | other code -> hard fail (-1) | + | - HID++ error for different request -> discard | + | - non-matching, non-error frame -> discard | + | - read timeout (0) -> retry the send | + \*----------------------------------------------------------*/ + if(hidpp20_error_out) + { + *hidpp20_error_out = 0; + } + + if(policy.flush_before) + { + FlushResponseQueue(); + } + + int last_result = 0; + uint8_t last_error = 0; + + for(uint8_t attempt = 0; attempt < policy.attempts; attempt++) + { + /*-----------------------------------------------------*\ + | Backoff before each attempt (0 on first) | + \*-----------------------------------------------------*/ + uint16_t delay_ms = policy.backoff_ms[attempt]; + + if(delay_ms > 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); + } + + /*-----------------------------------------------------*\ + | Bail early if device went offline mid-retry | + \*-----------------------------------------------------*/ + if(!device_online.load()) + { + return 0; + } + + int send_result = SendMessage(feat_idx, function, send_data, send_len); + + if(send_result < 0) + { + LOG_DEBUG("%s SendAcked[%s] wire send failed (attempt %d, result=%d) " + "feat=0x%02X func=0x%02X", + LOG_TAG, policy.name, attempt, send_result, feat_idx, function); + last_result = -2; + continue; + } + + /*-----------------------------------------------------*\ + | Read loop bounded by per-attempt window. Drain | + | non-matching HID++ frames within this window — they | + | are stale responses or unrelated events from prior | + | commands. Only retry the send if the window expires | + | with no match (lost on wire) or we got BUSY. | + \*-----------------------------------------------------*/ + std::chrono::steady_clock::time_point window_deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(policy.read_window_ms); + bool need_resend = false; + + while(!need_resend) + { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + + if(now >= window_deadline) + { + LOG_TRACE("%s SendAcked[%s] window expired (attempt %d)", + LOG_TAG, policy.name, attempt); + last_result = 0; + break; + } + + int remaining = (int)std::chrono::duration_cast( + window_deadline - now).count(); + + if(remaining <= 0) + { + last_result = 0; + break; + } + + uint8_t resp_feat = 0; + uint8_t resp_func = 0; + uint8_t resp_data[60] = {}; + + int rd = ReadMessage(&resp_feat, &resp_func, + resp_data, sizeof(resp_data), + remaining); + + if(rd < 0) + { + /* Wire error — propagate, don't retry */ + return -2; + } + + if(rd == 0) + { + /* Window drained with nothing matching — retry the send */ + last_result = 0; + break; + } + + /*-------------------------------------------------*\ + | HID++ error frame | + | feat=0xFF, func=err_feat, data[0]=err_func, | + | data[1]=err_code | + \*-------------------------------------------------*/ + if(resp_feat == 0xFF) + { + uint8_t err_feat = resp_func; + uint8_t err_func = resp_data[0]; + uint8_t err_code = resp_data[1]; + + /*-----------------------------------------------*\ + | Match: either a direct error for our request, | + | or a Centurion bridge error attributed to the | + | bridge feature index when we're routing through | + | it. The bridge swallows the sub-device feat in | + | the error response, so all bridge-routed | + | failures look like errors from the bridge. | + \*-----------------------------------------------*/ + bool is_our_error = + (err_feat == feat_idx && + (err_func & 0xF0) == (function & 0xF0)) + || (transport.bridge_feat_idx != 0 && + err_feat == transport.bridge_feat_idx); + + if(is_our_error) + { + if(err_code == 0x08 && policy.retry_on_busy) + { + /* BUSY: retry the send after backoff */ + LOG_TRACE("%s SendAcked[%s] BUSY (attempt %d) feat=0x%02X func=0x%02X", + LOG_TAG, policy.name, attempt, feat_idx, function); + last_error = 0x08; + last_result = 0; + need_resend = true; + continue; + } + + /* Non-BUSY HID++ error: hard fail */ + LOG_DEBUG("%s SendAcked[%s] LogitechHID++ error 0x%02X " + "feat=0x%02X func=0x%02X", + LOG_TAG, policy.name, err_code, feat_idx, function); + + if(hidpp20_error_out) + { + *hidpp20_error_out = err_code; + } + + return -1; + } + + /* Error for a different request — stale, discard and keep reading */ + continue; + } + + /*-------------------------------------------------*\ + | Match our expected response | + \*-------------------------------------------------*/ + if(resp_feat == feat_idx && + (resp_func & 0xF0) == (function & 0xF0)) + { + if(recv_data && recv_max > 0) + { + size_t copy = (recv_max > sizeof(resp_data)) + ? sizeof(resp_data) : recv_max; + memcpy(recv_data, resp_data, copy); + } + + if(attempt > 0) + { + LOG_DEBUG("%s SendAcked[%s] succeeded on attempt %d " + "feat=0x%02X func=0x%02X", + LOG_TAG, policy.name, attempt, feat_idx, function); + } + + consecutive_timeouts.store(0); + return rd; + } + + /* Non-matching, non-error: stale unrelated frame, keep reading */ + } + } + + LOG_DEBUG("%s SendAcked[%s] exhausted %d attempts feat=0x%02X func=0x%02X " + "(last_error=0x%02X)", + LOG_TAG, policy.name, (int)policy.attempts, + feat_idx, function, last_error); + + if(hidpp20_error_out) + { + *hidpp20_error_out = last_error; + } + + /*----------------------------------------------------------*\ + | Offline detection: tick once per fully-failed call (all | + | retry attempts exhausted with no response). At a threshold | + | of 10 we declare the device gone. Reset to 0 happens on | + | any successful call (above) — single delayed responses | + | don't push us toward offline. | + \*----------------------------------------------------------*/ + if(last_result == 0) + { + int timeouts = ++consecutive_timeouts; + + if(timeouts >= 10 && device_online.load()) + { + LOG_DEBUG("%s Device appears offline (%d consecutive failed calls)", + LOG_TAG, timeouts); + device_online.store(false); + } + } + + return last_result; +} + +int LogitechHIDPP20Controller::SendAckedIntoFAP + ( + uint8_t feat_idx, + uint8_t function, + const uint8_t* send_data, + size_t send_len, + blankFAPmessage& response, + const HIDPP20RetryPolicy& policy + ) +{ + /*---------------------------------------------------------*\ + | Compatibility shim for callers that inherited the | + | SendLong+ReadResponse interface and inspect | + | response.data[] downstream. Calls SendAcked into a local | + | buffer, then reconstructs a blankFAPmessage on success. | + \*---------------------------------------------------------*/ + response.init(); + + uint8_t recv[60] = {}; + int result = SendAcked(feat_idx, function, + send_data, send_len, + recv, sizeof(recv), + policy); + + if(result > 0) + { + response.report_id = LOGITECH_LONG_MESSAGE; + response.device_index = device_index; + response.feature_index = feat_idx; + response.feature_command = function; + memcpy(response.data, recv, sizeof(response.data)); + } + + return result; +} + +/*---------------------------------------------------------*\ +| Standard HID++ transport (0xFF00 / 0xFF43) | +| Report IDs 0x10 (7 bytes) / 0x11 (20 bytes) | +\*---------------------------------------------------------*/ + +int LogitechHIDPP20Controller::SendStandard + ( + uint8_t feat_idx, + uint8_t function, + const uint8_t* data, + size_t len + ) +{ + /*-----------------------------------------------------------*\ + | Auto-select short (0x10, 7 bytes) vs long (0x11, 20 bytes) | + | based on data length. Upper layers just provide data; | + | transport picks the smallest frame that fits. | + | | + | Windows exception: HIDClass splits the HID++ short and long | + | message Top-Level Collections into separate virtual HID | + | devices (page 0xFF00 usage 1 vs usage 2). We open the long- | + | message TLC, which rejects 7-byte writes. Force long format | + | on Windows so every outgoing frame matches the collection | + | we opened — Linux hidraw and macOS IOHIDManager expose both | + | TLCs through one handle and keep the size-based heuristic. | + \*-----------------------------------------------------------*/ + uint8_t buf[LOGITECH_LONG_MESSAGE_LEN]; + size_t msg_len; + +#if defined(_WIN32) + const bool prefer_short = false; +#else + const bool prefer_short = (len <= 3); +#endif + + if(prefer_short) + { + memset(buf, 0, LOGITECH_SHORT_MESSAGE_LEN); + buf[0] = LOGITECH_SHORT_MESSAGE; + buf[1] = device_index; + buf[2] = feat_idx; + buf[3] = function | HIDPP20_SW_ID; + + if(data && len > 0) + { + memcpy(buf + 4, data, len); + } + + msg_len = LOGITECH_SHORT_MESSAGE_LEN; + } + else + { + memset(buf, 0, LOGITECH_LONG_MESSAGE_LEN); + buf[0] = LOGITECH_LONG_MESSAGE; + buf[1] = device_index; + buf[2] = feat_idx; + buf[3] = function | HIDPP20_SW_ID; + + if(data && len > 0) + { + size_t copy_len = (len > 16) ? 16 : len; + memcpy(buf + 4, data, copy_len); + } + + msg_len = LOGITECH_LONG_MESSAGE_LEN; + } + + int result; + + if(mutex) + { + std::lock_guard lock(*mutex); + result = hid_write(dev, buf, msg_len); + } + else + { + result = hid_write(dev, buf, msg_len); + } + + return result; +} + +int LogitechHIDPP20Controller::ReadStandardDirect + ( + uint8_t* feat_idx_out, + uint8_t* function_out, + uint8_t* data_out, + size_t data_max, + int timeout_ms + ) +{ + /*---------------------------------------------------------*\ + | No mutex needed for reads — when the reader thread is | + | running, it is the sole caller. Before the reader starts, | + | all access is single-threaded. | + | | + | Loop within the timeout window draining non-HID++ reports | + | (mouse motion, keystrokes, media keys, DJ events) until | + | we either find a HID++ short/long frame or actually time | + | out. A high-polling-rate device can put 50+ input reports | + | in the hidraw buffer between our calls; without the drain | + | loop the synchronous probe path can never get past them | + | to find its response. | + \*---------------------------------------------------------*/ + std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(timeout_ms); + int drained = 0; + + while(true) + { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + + if(now >= deadline) + { + return 0; + } + + int remaining_ms = (int)std::chrono::duration_cast( + deadline - now).count(); + + if(remaining_ms <= 0) + { + return 0; + } + + blankFAPmessage response; + response.init(); + + int result = hid_read_timeout(dev, response.buffer, response.size(), remaining_ms); + + if(result < 0) + { + /* Real wire error (e.g. device removed). */ + return result; + } + + if(result == 0) + { + /* hidapi timeout — window expired with nothing pending. */ + return 0; + } + + /*-----------------------------------------------------*\ + | Validate report ID. The hidraw can carry HID input | + | reports (keyboard, mouse, media keys) in addition to | + | HID++. Drop anything that isn't a HID++ short (0x10) | + | or long (0x11) message and keep draining within the | + | remaining window — otherwise the parser would read | + | buf[2]/buf[3] as feat/func and misinterpret | + | keystrokes/motion as HID++ events. | + \*-----------------------------------------------------*/ + if(response.buffer[0] != LOGITECH_SHORT_MESSAGE && + response.buffer[0] != LOGITECH_LONG_MESSAGE) + { + if(++drained > HIDPP20_READ_DRAIN_BUDGET) + { + LOG_DEBUG("%s ReadStandardDirect: drain budget (%d) exceeded", + LOG_TAG, HIDPP20_READ_DRAIN_BUDGET); + return 0; + } + continue; + } + + if(feat_idx_out) + { + *feat_idx_out = response.feature_index; + } + + if(function_out) + { + *function_out = response.feature_command; + } + + if(data_out && data_max > 0) + { + size_t copy_len = (data_max > sizeof(response.data)) ? sizeof(response.data) : data_max; + memcpy(data_out, response.data, copy_len); + } + + return result; + } +} + +/*---------------------------------------------------------*\ +| Centurion transport (0xFFA0) | +| | +| Wire format per protocol doc: | +| 0x51 (direct): [reportId] [cplLen] [flags] [featIdx] | +| [func|swid] [params...] | +| 0x50 (addressed): [reportId] [devAddr] [cplLen] [flags] | +| [featIdx] [func|swid] [params...] | +| | +| For sub-device access, the parent CentPPBridge wraps | +| sub-device messages: | +| params = [devId<<4|lenHi, lenLo, subCPL, subFeatIdx, | +| subFunc|swid, subParams...] | +| | +| Selects direct (0x50/0x51) or bridge-wrapped framing | +| based on transport; routes sub-devices via CentPPBridge. | +\*---------------------------------------------------------*/ + +int LogitechHIDPP20Controller::SendCenturion + ( + uint8_t feat_idx, + uint8_t function, + const uint8_t* data, + size_t len + ) +{ + uint8_t buf[64]; + memset(buf, 0, sizeof(buf)); + + if(transport.bridge_feat_idx != 0) + { + /*-----------------------------------------------------*\ + | Sub-device message routed through CentPPBridge | + | Parent message: feat=bridge, func=sendFragment(0x10) | + | Payload: [devId<<4|lenHi, lenLo, subCPL=0x00, | + | subFeatIdx, subFunc|swid, subParams...] | + \*-----------------------------------------------------*/ + uint16_t sub_msg_len = 3 + (uint16_t)len; // subCPL + featIdx + func + data + + if(transport.addressed) + { + buf[0] = transport.report_id; + buf[1] = transport.device_address; + buf[2] = 5 + sub_msg_len; // cplLen + buf[3] = 0x00; // flags (single fragment) + buf[4] = transport.bridge_feat_idx; + buf[5] = 0x10 | HIDPP20_SW_ID; // sendFragment (func 1) + buf[6] = (transport.sub_device_id << 4) | ((sub_msg_len >> 8) & 0x0F); + buf[7] = sub_msg_len & 0xFF; + buf[8] = 0x00; // sub-CPL (single fragment) + buf[9] = feat_idx; + buf[10] = function | HIDPP20_SW_ID; + + if(data && len > 0) + { + memcpy(buf + 11, data, len); + } + } + else + { + buf[0] = transport.report_id; + buf[1] = 5 + sub_msg_len; // cplLen: flags(1) + feat(1) + func(1) + hdr(2) + sub + buf[2] = 0x00; // flags + buf[3] = transport.bridge_feat_idx; + buf[4] = 0x10 | HIDPP20_SW_ID; // sendFragment (func 1) + buf[5] = (transport.sub_device_id << 4) | ((sub_msg_len >> 8) & 0x0F); + buf[6] = sub_msg_len & 0xFF; + buf[7] = 0x00; // sub-CPL + buf[8] = feat_idx; + buf[9] = function | HIDPP20_SW_ID; + + if(data && len > 0) + { + memcpy(buf + 10, data, len); + } + } + } + else + { + /*-----------------------------------------------------*\ + | Direct parent device message (no bridge) | + \*-----------------------------------------------------*/ + if(transport.addressed) + { + buf[0] = transport.report_id; + buf[1] = transport.device_address; + buf[2] = 3 + (uint8_t)len; // cplLen + buf[3] = 0x00; // flags + buf[4] = feat_idx; + buf[5] = function | HIDPP20_SW_ID; + + if(data && len > 0) + { + memcpy(buf + 6, data, len); + } + } + else + { + buf[0] = transport.report_id; + buf[1] = 3 + (uint8_t)len; // cplLen: flags(1) + feat(1) + func(1) + data + buf[2] = 0x00; // flags + buf[3] = feat_idx; + buf[4] = function | HIDPP20_SW_ID; + + if(data && len > 0) + { + memcpy(buf + 5, data, len); + } + } + } + + int result; + + if(mutex) + { + std::lock_guard lock(*mutex); + result = hid_write(dev, buf, 64); + } + else + { + result = hid_write(dev, buf, 64); + } + + return result; +} + +int LogitechHIDPP20Controller::ReadCenturionDirect + ( + uint8_t* feat_idx_out, + uint8_t* function_out, + uint8_t* data_out, + size_t data_max, + int timeout_ms + ) +{ + uint8_t buf[64]; + memset(buf, 0, sizeof(buf)); + + /*----------------------------------------------------------*\ + | Track an overall deadline so the bridge ACK + MessageEvent | + | two-read sequence stays within timeout_ms total — without | + | this each read could eat the full budget independently. | + | Drain non-Centurion report IDs within the remaining window | + | rather than bailing on the first non-matching frame. | + \*----------------------------------------------------------*/ + std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(timeout_ms); + int drained = 0; + int result = 0; + + while(true) + { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + + if(now >= deadline) + { + return 0; + } + + int remaining_ms = (int)std::chrono::duration_cast( + deadline - now).count(); + + if(remaining_ms <= 0) + { + return 0; + } + + result = hid_read_timeout(dev, buf, sizeof(buf), remaining_ms); + + if(result < 0) + { + return result; + } + + if(result == 0) + { + return 0; + } + + if(buf[0] == transport.report_id) + { + break; + } + + if(++drained > HIDPP20_READ_DRAIN_BUDGET) + { + LOG_DEBUG("%s ReadCenturionDirect: drain budget (%d) exceeded", + LOG_TAG, HIDPP20_READ_DRAIN_BUDGET); + return 0; + } + } + + /*---------------------------------------------------------*\ + | Parse based on transport variant | + \*---------------------------------------------------------*/ + int hdr_offset = transport.addressed ? 1 : 0; // skip device address byte + + uint8_t cpl_len = buf[1 + hdr_offset]; + // uint8_t cpl_flags = buf[2 + hdr_offset]; // for fragmentation support + uint8_t resp_feat = buf[3 + hdr_offset]; + uint8_t resp_func = buf[4 + hdr_offset]; + + if(transport.bridge_feat_idx != 0 && resp_feat == transport.bridge_feat_idx) + { + /*--------------------------------------------------------*\ + | CentPPBridge — distinguish events from command responses | + | | + | Bridge events (e.g. ConnectionStateChangedEvent) have | + | func high nibble = 0x00 (event index 0) and swid = 0. | + | These are NOT wrapped sub-device responses — they are | + | bridge-level notifications. Return as-is so the reader | + | thread can detect them. | + | | + | Command responses follow a two-response pattern: | + | 1. ACK: bridge echoes feat+func with our swid | + | 2. MessageEvent: func=1x, swid=0, wrapped sub-device | + \*--------------------------------------------------------*/ + if((resp_func & 0xF0) == 0x00 && (resp_func & 0x0F) != HIDPP20_SW_ID) + { + /*-------------------------------------------------*\ + | Bridge event — return feat/func/data as-is | + \*-------------------------------------------------*/ + if(feat_idx_out) *feat_idx_out = resp_feat; + if(function_out) *function_out = resp_func; + + if(data_out && data_max > 0) + { + size_t avail = (size_t)(cpl_len > 2 ? cpl_len - 2 : 0); + size_t copy = (avail < data_max) ? avail : data_max; + memcpy(data_out, buf + 5 + hdr_offset, copy); + } + + return result; + } + + if((resp_func & 0x0F) == HIDPP20_SW_ID) + { + /*---------------------------------------------------*\ + | This is the ACK — discard and read the MessageEvent | + | Use the *remaining* window from the overall | + | deadline so the two-read sequence stays bounded, | + | and drain non-Centurion frames within that window. | + \*---------------------------------------------------*/ + while(true) + { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + + if(now >= deadline) + { + return 0; + } + + int remaining_ms = (int)std::chrono::duration_cast( + deadline - now).count(); + + if(remaining_ms <= 0) + { + return 0; + } + + memset(buf, 0, sizeof(buf)); + result = hid_read_timeout(dev, buf, sizeof(buf), remaining_ms); + + if(result < 0) + { + return result; + } + + if(result == 0) + { + return 0; + } + + if(buf[0] == transport.report_id) + { + break; + } + + if(++drained > HIDPP20_READ_DRAIN_BUDGET) + { + LOG_DEBUG("%s ReadCenturionDirect: drain budget (%d) exceeded on bridge MessageEvent", + LOG_TAG, HIDPP20_READ_DRAIN_BUDGET); + return 0; + } + } + + resp_feat = buf[3 + hdr_offset]; + resp_func = buf[4 + hdr_offset]; + + if(resp_feat != transport.bridge_feat_idx) + { + /*---------------------------------------------*\ + | Not a bridge response — return as-is | + \*---------------------------------------------*/ + if(feat_idx_out) *feat_idx_out = resp_feat; + if(function_out) *function_out = resp_func; + + if(data_out && data_max > 0) + { + size_t avail = (size_t)(buf[1 + hdr_offset] > 2 ? buf[1 + hdr_offset] - 2 : 0); + size_t copy = (avail < data_max) ? avail : data_max; + memcpy(data_out, buf + 5 + hdr_offset, copy); + } + + return result; + } + } + + /*-----------------------------------------------------*\ + | MessageEvent — unwrap sub-device response. | + | Bridge params: [devId<<4|lenHi, lenLo, subCPL, | + | subFeatIdx, subFunc|swid, subData...] | + \*-----------------------------------------------------*/ + int sub_offset = 5 + hdr_offset + 3; // past bridge header + resp_feat = buf[sub_offset]; + resp_func = buf[sub_offset + 1]; + + if(feat_idx_out) *feat_idx_out = resp_feat; + if(function_out) *function_out = resp_func; + + if(data_out && data_max > 0) + { + size_t avail = (size_t)(result - sub_offset - 2); + size_t copy = (avail < data_max) ? avail : data_max; + memcpy(data_out, buf + sub_offset + 2, copy); + } + } + else + { + /*-----------------------------------------------------*\ + | Direct response | + \*-----------------------------------------------------*/ + if(feat_idx_out) *feat_idx_out = resp_feat; + if(function_out) *function_out = resp_func; + + if(data_out && data_max > 0) + { + size_t avail = (size_t)(cpl_len > 2 ? cpl_len - 2 : 0); + size_t copy = (avail < data_max) ? avail : data_max; + memcpy(data_out, buf + 5 + hdr_offset, copy); + } + } + + (void)cpl_len; + + return result; +} + +/*---------------------------------------------------------*\ +| Feature Discovery | +\*---------------------------------------------------------*/ + +uint8_t LogitechHIDPP20Controller::GetFeatureIndex(uint16_t feature_page, + const HIDPP20RetryPolicy& policy) +{ + /*-----------------------------------------------------------*\ + | Check cache first — both Centurion bulk and HID++ on-demand | + | lookups store results here. | + \*-----------------------------------------------------------*/ + std::map::const_iterator it = caps.feature_map.find(feature_page); + + if(it != caps.feature_map.end()) + { + return it->second; + } + + /*---------------------------------------------------------*\ + | Centurion bulk enumeration is complete — if a feature | + | isn't in the map, it doesn't exist. No wire query needed. | + \*---------------------------------------------------------*/ + if(caps.feature_map_complete) + { + return 0; + } + + /*---------------------------------------------------------*\ + | Standard HID++: on-demand IRoot query, cache the result. | + \*---------------------------------------------------------*/ + uint8_t send_data[2]; + send_data[0] = (feature_page >> 8) & 0xFF; + send_data[1] = feature_page & 0xFF; + + uint8_t recv_data[16] = {}; + int result = SendAcked(LOGITECH_HIDPP_PAGE_ROOT_IDX, FN_8071_GET_INFO, + send_data, 2, recv_data, sizeof(recv_data), + policy); + + if(result > 0) + { + uint8_t index = recv_data[0]; + uint8_t version = recv_data[2]; + + if(index != 0) + { + caps.feature_map[feature_page] = index; + caps.feature_versions[feature_page] = version; + } + + if(index != 0) + { + LOG_DEBUG("%s Feature 0x%04X V%u -> index 0x%02X", + LOG_TAG, feature_page, version, index); + + if(!FeatureVersionIsObserved(feature_page, version)) + { + LOG_INFO("%s Feature 0x%04X V%u not previously observed — " + "tripwire for version-gated behavior", + LOG_TAG, feature_page, version); + } + } + else + { + LOG_DEBUG("%s Feature 0x%04X not present", LOG_TAG, feature_page); + } + + return index; + } + + /*---------------------------------------------------------*\ + | Cache the miss too so we don't re-query failed lookups | + \*---------------------------------------------------------*/ + caps.feature_map[feature_page] = 0; + + LOG_DEBUG("%s Feature 0x%04X not found", LOG_TAG, feature_page); + return 0; +} + +/*---------------------------------------------------------*\ +| Return the protocol version byte for a feature, or 0 if | +| the feature isn't present in this device's feature set. | +| Populated alongside feature_map during EnumerateFeatures | +| (Centurion bulk) or GetFeatureIndex (standard HID++ | +| on-demand IRoot.GetFeature). | +\*---------------------------------------------------------*/ +uint8_t LogitechHIDPP20Controller::GetFeatureVersion(uint16_t feature_page) const +{ + std::map::const_iterator it = caps.feature_versions.find(feature_page); + + if(it != caps.feature_versions.end()) + { + return it->second; + } + + return 0; +} + +void LogitechHIDPP20Controller::DiscoverDeviceName() +{ + /*---------------------------------------------------------*\ + | Centurion sub-devices use 0x0101 (DeviceName). | + | Standard HID++ uses 0x0005 (DeviceNameType). | + \*---------------------------------------------------------*/ + if(transport.type == HIDPP20_TRANSPORT_CENTURION) + { + /*------------------------------------------------------*\ + | Centurion sub-device: 0x0101 getName returns firmware | + | data, not a readable name. Fall back to a platform- | + | specific lookup that reads the friendly name from the | + | OS's HID enumeration (sysfs HID_NAME on Linux, | + | hid_device_info::product_string on Windows). | + \*------------------------------------------------------*/ + std::string friendly = GetCenturionSubDeviceName(location); + + if(!friendly.empty()) + { + caps.device_name = friendly; + } + else + { + caps.device_name = "Logitech Centurion Device"; + } + + LOG_VERBOSE("%s Device name (Centurion): %s", LOG_TAG, caps.device_name.c_str()); + return; + } + + uint8_t feat_idx = GetFeatureIndex(HIDPP20_FEAT_DEVICE_NAME_TYPE); + + if(feat_idx == 0) + { + caps.device_name = "Logitech HID++ Device"; + return; + } + + uint8_t recv[16] = {}; + int result = SendAcked(feat_idx, LOTITECH_CMD_DEVICE_NAME_TYPE_GET_COUNT, + nullptr, 0, recv, sizeof(recv)); + + if(result <= 0) + { + caps.device_name = "Logitech HID++ Device"; + return; + } + + unsigned int name_length = recv[0]; + caps.device_name.clear(); + + for(unsigned int offset = 0; offset < name_length; offset += 16) + { + uint8_t send_data[1] = { (uint8_t)offset }; + result = SendAcked(feat_idx, LOGITECH_CMD_DEVICE_NAME_TYPE_GET_DEVICE_NAME, + send_data, 1, recv, sizeof(recv)); + + if(result <= 0) + { + break; + } + + unsigned int chunk_len = name_length - offset; + if(chunk_len > 16) + { + chunk_len = 16; + } + + caps.device_name.append((char*)recv, chunk_len); + } + + LOG_VERBOSE("%s Device name: %s", LOG_TAG, caps.device_name.c_str()); +} + +void LogitechHIDPP20Controller::DiscoverDeviceType() +{ + /*----------------------------------------------------------*\ + | Centurion sub-devices don't have 0x0005 (DeviceNameType). | + | Default to unknown — don't assume device type from | + | transport, as Centurion may be used for future devices. | + \*----------------------------------------------------------*/ + if(transport.type == HIDPP20_TRANSPORT_CENTURION) + { + caps.device_type = 0; + return; + } + + uint8_t feat_idx = GetFeatureIndex(HIDPP20_FEAT_DEVICE_NAME_TYPE); + + if(feat_idx == 0) + { + caps.device_type = LOGITECH_DEVICE_TYPE_MOUSE; + return; + } + + uint8_t recv[16] = {}; + int result = SendAcked(feat_idx, LOGITECH_CMD_DEVICE_NAME_TYPE_GET_TYPE, + nullptr, 0, recv, sizeof(recv)); + + if(result > 0) + { + caps.device_type = recv[0]; + LOG_VERBOSE("%s Device type: %d", LOG_TAG, caps.device_type); + } + else + { + caps.device_type = LOGITECH_DEVICE_TYPE_MOUSE; + } +} + +void LogitechHIDPP20Controller::DiscoverTransport() +{ + /*---------------------------------------------------------*\ + | Detect transport type from usage page. | + | 0xFF00/0xFF43: Standard HID++ (0x10/0x11 reports) | + | 0xFFA0+: Centurion (64-byte CPL framing) | + \*---------------------------------------------------------*/ + if(transport.usage_page == 0xFF00 || transport.usage_page == 0xFF43) + { + transport.type = HIDPP20_TRANSPORT_STANDARD; + return; + } + + /*---------------------------------------------------------*\ + | Centurion transport — determine variant by probing. | + | 0x51 = direct (PRO X 2), 0x50 = addressed (G522). The | + | report descriptor would tell us which report IDs exist, | + | but hid_get_report_descriptor is hidapi 0.14.0+ only, so | + | we probe instead: try 0x51 direct first, then fall back | + | to the robust 0x50 device-address sweep. | + \*---------------------------------------------------------*/ + transport.type = HIDPP20_TRANSPORT_CENTURION; + + /*---------------------------------------------------------*\ + | Probe 0x51 (direct). If the device answers a 0x51 frame | + | it speaks the direct variant — no device address needed. | + \*---------------------------------------------------------*/ + transport.report_id = 0x51; + transport.addressed = false; + + uint8_t probe_buf[64] = {}; + probe_buf[0] = 0x51; + probe_buf[1] = 3; + probe_buf[2] = 0x00; + probe_buf[3] = 0x00; + probe_buf[4] = 0x00 | HIDPP20_SW_ID; + + int wr = hid_write(dev, probe_buf, 64); + + if(wr > 0) + { + uint8_t resp_buf[64] = {}; + int rd = hid_read_timeout(dev, resp_buf, sizeof(resp_buf), 500); + + if(rd > 0 && resp_buf[0] == 0x51) + { + LOG_DEBUG("%s Centurion 0x51 (direct) from probe", LOG_TAG); + return; + } + } + + /*---------------------------------------------------------*\ + | No 0x51 reply — assume 0x50 (addressed) and find the | + | device address. | + \*---------------------------------------------------------*/ + transport.report_id = 0x50; + transport.addressed = true; + transport.device_address = 0x00; + + /*-----------------------------------------------------*\ + | Device-address sweep. 0x50 frames carry a device | + | address byte; the device silently drops frames | + | addressed to the wrong ID, so we brute-force probe | + | every candidate with an IRoot fn1 GetProtocolVersion | + | ping. First response wins — real address lives in | + | resp_buf[1] of the reply. Mirrors Solaar's | + | probe_centurion_device_addr; see | + | CENTURION_PROBE_PER_ADDR_TIMEOUT_MS above for timing. | + | | + | Wire format per candidate: | + | [0x50, addr, 0x06, 0x00, 0x00, 0x10, 0x00, 0x00, | + | 0x00, zero-pad to 64] | + | where 0x06 = cpl_length (flags+payload), 0x10 = fn1 | + | GetProtocolVersion with sw_id=0. | + \*-----------------------------------------------------*/ + bool addr_found = false; + unsigned probe_count = 0; + unsigned write_errors = 0; + + for(unsigned addr = 0; addr < 256; addr++) + { + uint8_t sweep_buf[64] = {}; + sweep_buf[0] = 0x50; + sweep_buf[1] = (uint8_t)addr; + sweep_buf[2] = 0x06; + sweep_buf[3] = 0x00; + sweep_buf[4] = 0x00; + sweep_buf[5] = 0x10; + + int swr = hid_write(dev, sweep_buf, 64); + probe_count++; + + if(swr <= 0) + { + write_errors++; + if(write_errors > 3) + { + LOG_DEBUG("%s Centurion 0x50 probe: too many write failures, aborting", LOG_TAG); + break; + } + continue; + } + + uint8_t resp_buf[64] = {}; + int rd = hid_read_timeout(dev, resp_buf, sizeof(resp_buf), + CENTURION_PROBE_PER_ADDR_TIMEOUT_MS); + + if(rd >= 2 && resp_buf[0] == 0x50) + { + transport.device_address = resp_buf[1]; + addr_found = true; + break; + } + } + + if(addr_found) + { + LOG_INFO("%s Centurion 0x50 device_addr=0x%02X (after %u candidates)", + LOG_TAG, transport.device_address, probe_count); + } + else + { + LOG_DEBUG("%s Centurion 0x50 probe: no response from any of 256 candidates", + LOG_TAG); + } +} + +void LogitechHIDPP20Controller::EnumerateFeatures(uint8_t feature_set_idx) +{ + caps.feature_map.clear(); + caps.feature_map_complete = false; + + /*---------------------------------------------------------*\ + | Root (0x0000) is always at index 0 | + \*---------------------------------------------------------*/ + caps.feature_map[0x0000] = 0; + + if(transport.type == HIDPP20_TRANSPORT_CENTURION) + { + /*-----------------------------------------------------*\ + | Centurion sub-device: CenturionFeatureSet fn1 returns | + | ALL features in a single bulk response. | + | [count, (feat_hi, feat_lo, type, version) × count] | + \*-----------------------------------------------------*/ + uint8_t send_data[1] = { 0x00 }; + uint8_t recv_data[60] = {}; + + int result = SendAcked(feature_set_idx, 0x10, + send_data, 1, recv_data, sizeof(recv_data)); + + if(result > 0) + { + uint8_t count = recv_data[0]; + + LOG_DEBUG("%s CenturionFeatureSet: %d features", LOG_TAG, count); + + for(uint8_t i = 0; i < count && (1 + i * 4 + 3) < (int)sizeof(recv_data); i++) + { + int offset = 1 + i * 4; + uint16_t feat_id = ((uint16_t)recv_data[offset] << 8) | recv_data[offset + 1]; + uint8_t feat_type = recv_data[offset + 2]; + uint8_t feat_version = recv_data[offset + 3]; + uint8_t feat_idx = i; // 0-based: bulk includes root at 0 + + caps.feature_map[feat_id] = feat_idx; + caps.feature_versions[feat_id] = feat_version; + + LOG_DEBUG("%s [%2d] Feature 0x%04X V%u type=0x%02X", + LOG_TAG, feat_idx, feat_id, feat_version, feat_type); + + if(!FeatureVersionIsObserved(feat_id, feat_version)) + { + LOG_INFO("%s Feature 0x%04X V%u not previously observed — " + "tripwire for version-gated behavior", + LOG_TAG, feat_id, feat_version); + } + } + + caps.feature_map_complete = true; + } + } + else + { + /*-----------------------------------------------------*\ + | Standard HID++: no bulk query available. Features are | + | looked up on-demand via GetFeatureIndex (IRoot) and | + | cached in the feature map. Nothing to do here. | + \*-----------------------------------------------------*/ + return; + } +} + +void LogitechHIDPP20Controller::DiscoverFirmwareInfo() +{ + /*--------------------------------------------------------------*\ + | Centurion sub-devices use 0x0100 (DeviceInfo) for firmware | + | version and serial. Standard HID++ uses 0x0003 (FirmwareInfo). | + \*--------------------------------------------------------------*/ + if(transport.type == HIDPP20_TRANSPORT_CENTURION) + { + uint8_t dev_info_idx = GetFeatureIndex(HIDPP20_FEAT_CENTURION_DEVICE_INFO); + + if(dev_info_idx == 0) + { + return; + } + + /*------------------------------------------------------*\ + | fn1 getFirmwareVersion(entityIndex=0) — main firmware | + | Response: [fwType, additional, version_hi, version_lo] | + \*------------------------------------------------------*/ + { + uint8_t send_data[1] = { 0x00 }; + uint8_t recv_data[16] = {}; + + int result = SendAcked(dev_info_idx, 0x10, + send_data, 1, recv_data, sizeof(recv_data)); + + if(result > 0) + { + uint16_t version = ((uint16_t)recv_data[2] << 8) | recv_data[3]; + + char ver_str[32]; + snprintf(ver_str, sizeof(ver_str), "%d.%d", + (version >> 8) & 0xFF, version & 0xFF); + + caps.firmware_version = ver_str; + + LOG_DEBUG("%s Firmware (Centurion): %s", LOG_TAG, caps.firmware_version.c_str()); + } + } + + /*-----------------------------------------------------*\ + | fn2 getSerialNumber on 0x0100 (DeviceInfo) | + | Response: [stringLen, serial...] | + \*-----------------------------------------------------*/ + { + uint8_t recv_data[16] = {}; + + int result = SendAcked(dev_info_idx, 0x20, + nullptr, 0, recv_data, sizeof(recv_data)); + + if(result > 0) + { + uint8_t slen = recv_data[0]; + if(slen > 15) slen = 15; + + caps.serial_number = std::string((char*)&recv_data[1], slen); + + LOG_DEBUG("%s Serial (Centurion): %s", LOG_TAG, caps.serial_number.c_str()); + } + } + + return; + } + + uint8_t fw_idx = GetFeatureIndex(HIDPP20_FEAT_FIRMWARE_INFO); + + if(fw_idx == 0) + { + return; + } + + /*------------------------------------------------------------*\ + | fn0 GetEntityCount — entity count, unitId, transport PIDs | + | Response: [count, unitId(4), transport(2), PID1(2), PID2(2)] | + \*------------------------------------------------------------*/ + uint8_t entity_count = 1; + + { + uint8_t recv_data[16] = {}; + int result = SendAcked(fw_idx, 0x00, + nullptr, 0, recv_data, sizeof(recv_data)); + + if(result > 0) + { + entity_count = recv_data[0]; + + /*-------------------------------------------------*\ + | Extract unitId — stable hardware identity across | + | all connection paths (USB, wireless, dongle). | + \*-------------------------------------------------*/ + char uid[16]; + snprintf(uid, sizeof(uid), "%02X%02X%02X%02X", + recv_data[1], recv_data[2], recv_data[3], recv_data[4]); + caps.unit_id = uid; + + caps.pid_wireless = ((uint16_t)recv_data[7] << 8) | recv_data[8]; + caps.pid_wired = ((uint16_t)recv_data[9] << 8) | recv_data[10]; + + /*-------------------------------------------------*\ + | Use unitId as serial if device doesn't report one | + \*-------------------------------------------------*/ + if(caps.serial_number.empty() && caps.unit_id != "00000000") + { + caps.serial_number = caps.unit_id; + } + + LOG_DEBUG("%s unitId=%s PID1=0x%04X PID2=0x%04X", + LOG_TAG, caps.unit_id.c_str(), caps.pid_wireless, caps.pid_wired); + + /*-------------------------------------------------*\ + | Resolve per-model quirks from the PID pair. | + \*-------------------------------------------------*/ + caps.quirks = 0; + + size_t quirk_table_len = sizeof(HIDPP20_DEVICE_QUIRK_TABLE) + / sizeof(HIDPP20_DEVICE_QUIRK_TABLE[0]); + + for(size_t q = 0; q < quirk_table_len; q++) + { + const HIDPP20DeviceQuirkEntry& entry = HIDPP20_DEVICE_QUIRK_TABLE[q]; + + if((entry.pid_wireless != 0 && entry.pid_wireless == caps.pid_wireless) || + (entry.pid_wired != 0 && entry.pid_wired == caps.pid_wired)) + { + caps.quirks |= entry.quirks; + } + } + + if(caps.quirks != 0) + { + LOG_DEBUG("%s Device quirks: 0x%08X", LOG_TAG, caps.quirks); + } + } + } + + /*----------------------------------------------------------*\ + | fn1 GetFwInfo — iterate entities to find main FW (type 0) | + | fwType lower nibble: 0=main, 1=bootloader, 2=HW rev | + | Response: fwType(1), prefix(3), bcdVersion(2), bcdBuild(2) | + \*----------------------------------------------------------*/ + for(uint8_t entity = 0; entity < entity_count && entity < 8; entity++) + { + uint8_t send_data[1] = { entity }; + uint8_t recv_data[16] = {}; + + int result = SendAcked(fw_idx, 0x10, + send_data, 1, recv_data, sizeof(recv_data)); + + if(result <= 0) + { + continue; + } + + uint8_t fw_type = recv_data[0] & 0x0F; + char prefix[4] = { (char)recv_data[1], (char)recv_data[2], (char)recv_data[3], '\0' }; + uint8_t ver_major = recv_data[4]; + uint8_t ver_minor = recv_data[5]; + uint16_t build = ((uint16_t)recv_data[6] << 8) | recv_data[7]; + + char ver_str[64]; + snprintf(ver_str, sizeof(ver_str), "%s %d.%d.%05u", + prefix, ver_major, ver_minor, build); + + LOG_DEBUG("%s Firmware entity %d: type=%d %s", LOG_TAG, entity, fw_type, ver_str); + + if(fw_type == 0) + { + caps.firmware_version = ver_str; + } + } + + if(caps.firmware_version.empty()) + { + LOG_DEBUG("%s No main firmware entity found", LOG_TAG); + } + + /*---------------------------------------------------------*\ + | fn2 GetDeviceSerialNumber — ASCII serial up to 16 bytes | + \*---------------------------------------------------------*/ + { + uint8_t recv_data[16] = {}; + + int result = SendAcked(fw_idx, 0x20, + nullptr, 0, recv_data, sizeof(recv_data)); + + if(result > 0) + { + char serial[17] = {}; + memcpy(serial, recv_data, 16); + serial[16] = '\0'; + + /*-------------------------------------------------*\ + | Trim trailing nulls/spaces | + \*-------------------------------------------------*/ + for(int i = 15; i >= 0; i--) + { + if(serial[i] == '\0' || serial[i] == ' ') + { + serial[i] = '\0'; + } + else + { + break; + } + } + + if(serial[0] != '\0') + { + caps.serial_number = serial; + } + + LOG_DEBUG("%s Serial: %s", LOG_TAG, caps.serial_number.c_str()); + } + } +} + +void LogitechHIDPP20Controller::DiscoverRGBEffects() +{ + /*---------------------------------------------------------*\ + | Try 0x8071 first, then 0x0600 (Centurion), then 0x8070 | + \*---------------------------------------------------------*/ + caps.idx_rgb_effects = GetFeatureIndex(HIDPP20_FEAT_RGB_EFFECTS); + caps.rgb_feature_page = HIDPP20_FEAT_RGB_EFFECTS; + + if(caps.idx_rgb_effects == 0) + { + caps.idx_rgb_effects = GetFeatureIndex(HIDPP20_FEAT_CENTURION_RGB); + caps.rgb_feature_page = HIDPP20_FEAT_CENTURION_RGB; + } + + if(caps.idx_rgb_effects == 0) + { + caps.idx_rgb_effects = GetFeatureIndex(HIDPP20_FEAT_COLOR_LED_EFFECTS); + caps.rgb_feature_page = HIDPP20_FEAT_COLOR_LED_EFFECTS; + } + + if(caps.idx_rgb_effects == 0) + { + caps.has_zone_effects = false; + return; + } + + /*------------------------------------------------------------*\ + | Resolve function IDs based on which feature was found. | + | 0x8071 and 0x0600 share the same function layout. | + | 0x8070 has different function numbers and SW control format. | + \*------------------------------------------------------------*/ + if(caps.rgb_feature_page == HIDPP20_FEAT_COLOR_LED_EFFECTS) + { + caps.fn_set_effect = 0x30; + caps.fn_sw_control = 0x80; + caps.fn_pwr_config = 0; + caps.fn_pwr_mode = 0; + caps.has_power_mgmt = false; + caps.sw_control_simple = true; + } + else + { + caps.fn_set_effect = 0x10; + caps.fn_sw_control = 0x50; + caps.fn_pwr_config = 0x70; + caps.fn_pwr_mode = 0x80; + caps.has_power_mgmt = true; + caps.sw_control_simple = false; + } + + /*---------------------------------------------------------*\ + | GetInfo: discover cluster count | + | 0x8071: data = [0xFF, 0xFF, 0x00] | + | 0x8070: data = [] (empty) | + \*---------------------------------------------------------*/ + uint8_t data[3] = { 0xFF, 0xFF, 0x00 }; + size_t data_len = (caps.rgb_feature_page == HIDPP20_FEAT_RGB_EFFECTS) ? 3 : 0; + + blankFAPmessage response; + int result = SendAckedIntoFAP(caps.idx_rgb_effects, FN_8071_GET_INFO, + data, data_len, response); + + if(result <= 0) + { + caps.has_zone_effects = false; + return; + } + + unsigned int cluster_count; + + if(caps.rgb_feature_page == HIDPP20_FEAT_RGB_EFFECTS) + { + cluster_count = response.data[2]; + + /*-----------------------------------------------------*\ + | 0x8071 GetInfo response layout: | + | byte 2 numRgbZones | + | bytes 3-4 extendedCapabilities (BE16) | + | bytes 5-6 effectBlockCount (BE16) | + | byte 7 supportedClusterIndex | + | Logging the extra fields makes it easy to spot a | + | device whose enumerated effect list looks too short | + | relative to what it claims it can do. | + \*-----------------------------------------------------*/ + uint16_t ext_caps = ((uint16_t)response.data[3] << 8) | response.data[4]; + uint16_t effect_blocks = ((uint16_t)response.data[5] << 8) | response.data[6]; + uint8_t supported_idx = response.data[7]; + + LOG_INFO("%s RGBEffects 0x8071 V%u GetInfo: zones=%u extCaps=0x%04X effectBlocks=%u supportedClusterIdx=%u", + LOG_TAG, GetFeatureVersion(caps.rgb_feature_page), + cluster_count, ext_caps, effect_blocks, supported_idx); + } + else + { + cluster_count = response.data[0]; + LOG_INFO("%s RGB feature page=0x%04X V%u cluster_count=%u", + LOG_TAG, caps.rgb_feature_page, + GetFeatureVersion(caps.rgb_feature_page), cluster_count); + } + + /*---------------------------------------------------------*\ + | GetRgbClusterInfo for each cluster | + \*---------------------------------------------------------*/ + for(unsigned int i = 0; i < cluster_count; i++) + { + HIDPP20ZoneCluster cluster; + cluster.index = i; + + if(caps.rgb_feature_page == HIDPP20_FEAT_RGB_EFFECTS) + { + uint8_t query[2] = { (uint8_t)i, 0xFF }; + result = SendAckedIntoFAP(caps.idx_rgb_effects, FN_8071_GET_INFO, + query, 2, response); + } + else + { + uint8_t query[2] = { (uint8_t)i, 0x00 }; + result = SendAckedIntoFAP(caps.idx_rgb_effects, LOGITECH_CMD_RGB_EFFECTS_GET_INFO, + query, 2, response); + } + + if(result <= 0) + { + continue; + } + + if(caps.rgb_feature_page == HIDPP20_FEAT_RGB_EFFECTS) + { + cluster.location = (response.data[2] << 8) | response.data[3]; + cluster.effect_count = response.data[4]; + } + else + { + cluster.location = (response.data[1] << 8) | response.data[2]; + cluster.effect_count = response.data[3]; + } + + LOG_INFO("%s Cluster %d: location=0x%04X effects=%d", + LOG_TAG, i, cluster.location, cluster.effect_count); + + /*------------------------------------------------------*\ + | GetEffectInfo for each effect in this cluster | + \*------------------------------------------------------*/ + for(unsigned int j = 0; j < cluster.effect_count; j++) + { + HIDPP20Effect effect; + effect.index = j; + + uint8_t eff_query[4] = { (uint8_t)i, (uint8_t)j, 0x00, 0x00 }; + uint8_t eff_fn = (caps.rgb_feature_page == HIDPP20_FEAT_RGB_EFFECTS) + ? FN_8071_GET_INFO : LOGITECH_FP8070_EFFECT_INFO; + result = SendAckedIntoFAP(caps.idx_rgb_effects, eff_fn, + eff_query, 4, response); + + if(result <= 0) + { + continue; + } + + effect.effect_id = (response.data[2] << 8) | response.data[3]; + effect.capabilities = (response.data[4] << 8) | response.data[5]; + effect.default_period = (response.data[6] << 8) | response.data[7]; + + LOG_INFO("%s Effect %d: id=0x%04X caps=0x%04X default_period=%dms", + LOG_TAG, j, effect.effect_id, effect.capabilities, effect.default_period); + + cluster.effects.push_back(effect); + } + + caps.zone_clusters.push_back(cluster); + } + + caps.has_zone_effects = !caps.zone_clusters.empty(); + + /*---------------------------------------------------------*\ + | Probe for device-firmware effect cards. Only defined on | + | the 0x8071 RGBEffects path — 0x8070 and 0x0600 don't | + | expose GetEffectSpecificInfo in the same form. | + \*---------------------------------------------------------*/ + DiscoverEffectCards(); +} + +void LogitechHIDPP20Controller::DiscoverEffectCards() +{ + /*---------------------------------------------------------*\ + | Probes the device for the presence of firmware-resident | + | effect cards via 0x8071 fn0 GetEffectSpecificInfo. On | + | devices that have them (observed on G502 X PLUS), every | + | valid card returns a device-wide template byte pair at a | + | fixed position in page 1 of the response — the vendor app | + | reads those bytes and echoes them into the per-key prep | + | call's `SetEffectByIndex` params[6..7]. Our | + | implementation does the same. | + | | + | Request format for GetEffectSpecificInfo (0x8071 fn0): | + | [0xFF, effectIdHi, 0x01, effectIdLo, pageIndex] | + | | + | Response layout in blankFAPmessage::data[] terms (i.e. | + | starting AFTER the 4-byte HID++ header | + | report_id/dev_idx/feat_idx/func_byte): | + | | + | data[0..4] 5-byte prefix | + | [0] 0xFF echo of subfn marker | + | [1] echo of effectIdHi | + | [2] 0x01 echo of static constant | + | [3] 0x00 static zero (NOT an echo of effectIdLo) | + | [4] 0x00 static zero (NOT an echo of pageIndex) | + | data[5..15] 11-byte page payload | + | [5..6] header (0x00 0x00) | + | [7..8] firmware card ID (BE16; differs per card) | + | [9] pad | + | [10..11] device-wide template bytes (our target) | + | [12..15] trailing zeros | + | | + | Devices without effect cards return InvalidArgument for | + | any effectIdLo; we detect that as a non-positive result | + | and leave caps.has_effect_cards = false so the per-key | + | prep falls back to the Static-pass-through path. | + \*---------------------------------------------------------*/ + caps.has_effect_cards = false; + caps.effect_card_template[0] = 0; + caps.effect_card_template[1] = 0; + + if(caps.idx_rgb_effects == 0 || + caps.rgb_feature_page != HIDPP20_FEAT_RGB_EFFECTS || + !device_online.load()) + { + return; + } + + /*----------------------------------------------------------*\ + | Query card at effectIdLo=0, page 1. Any valid card works — | + | the template bytes are device-wide and identical across | + | every card on the device — so using card 0 is simplest. | + \*----------------------------------------------------------*/ + uint8_t query[5] = { 0xFF, 0x00, 0x01, 0x00, 0x01 }; + blankFAPmessage response; + int result = SendAckedIntoFAP( + caps.idx_rgb_effects, + FN_8071_GET_INFO, + query, sizeof(query), + response, + HIDPP20_POLICY_PROBE); + + if(result <= 0) + { + LOG_DEBUG("%s DiscoverEffectCards: no effect cards on this device " + "(result=%d)", LOG_TAG, result); + return; + } + + caps.has_effect_cards = true; + caps.effect_card_template[0] = response.data[10]; + caps.effect_card_template[1] = response.data[11]; + + LOG_INFO("%s Effect cards present: template bytes = 0x%02X 0x%02X " + "(card firmware_id=0x%02X%02X, full data[0..15] = " + "%02X %02X %02X %02X %02X %02X %02X %02X " + "%02X %02X %02X %02X %02X %02X %02X %02X)", + LOG_TAG, + caps.effect_card_template[0], caps.effect_card_template[1], + response.data[7], response.data[8], + response.data[0], response.data[1], response.data[2], + response.data[3], response.data[4], response.data[5], + response.data[6], response.data[7], response.data[8], + response.data[9], response.data[10], response.data[11], + response.data[12], response.data[13], response.data[14], + response.data[15]); +} + +/*---------------------------------------------------------*\ +| Feature 0x0620 Headset RGB Hostmode (Centurion G522 / | +| PRO X 2). Separate feature from 0x8071/0x0600/0x8070 — | +| no effect cards, no SetSWControl, no power management. | +| | +| Zone enumeration is best-effort from fn1 GetRGBZoneInfo. | +| Falls back to {0x00, 0x01} (two earcups) if decode fails. | +\*---------------------------------------------------------*/ +void LogitechHIDPP20Controller::DiscoverHeadsetRGBHostmode() +{ + caps.idx_headset_rgb_hostmode = GetFeatureIndex(HIDPP20_FEAT_HEADSET_RGB_HOSTMODE); + + if(caps.idx_headset_rgb_hostmode == 0) + { + return; + } + + LOG_INFO("%s 0x0620 V%u Headset RGB Hostmode present at feature index %u", + LOG_TAG, + GetFeatureVersion(HIDPP20_FEAT_HEADSET_RGB_HOSTMODE), + caps.idx_headset_rgb_hostmode); + + /*---------------------------------------------------------*\ + | fn1 GetRGBZoneInfo — empty request, returns a zone list. | + | Exact packing is not fully pinned down by the protocol | + | doc; log the raw response so a tester's log is enough to | + | refine the decoder. | + \*---------------------------------------------------------*/ + blankFAPmessage response; + int result = SendAckedIntoFAP(caps.idx_headset_rgb_hostmode, + FN_0620_GET_RGB_ZONE_INFO, + nullptr, 0, response); + + caps.headset_rgb_hostmode_zone_ids.clear(); + + if(result > 0) + { + LOG_INFO("%s 0x0620 fn1 GetRGBZoneInfo raw: " + "%02X %02X %02X %02X %02X %02X %02X %02X " + "%02X %02X %02X %02X %02X %02X %02X %02X", + LOG_TAG, + response.data[0], response.data[1], response.data[2], + response.data[3], response.data[4], response.data[5], + response.data[6], response.data[7], response.data[8], + response.data[9], response.data[10], response.data[11], + response.data[12], response.data[13], response.data[14], + response.data[15]); + + /*------------------------------------------------------*\ + | First-pass decode: byte 0 = zone count, bytes 1..N = | + | zone IDs. Bounds-check against the 16-byte data | + | window. Refine once we see real G522 output. | + \*------------------------------------------------------*/ + uint8_t zone_count = response.data[0]; + + if(zone_count > 0 && zone_count <= 15) + { + for(uint8_t i = 0; i < zone_count; i++) + { + caps.headset_rgb_hostmode_zone_ids.push_back(response.data[1 + i]); + } + } + } + else + { + LOG_DEBUG("%s 0x0620 fn1 GetRGBZoneInfo failed (result=%d)", + LOG_TAG, result); + } + + if(caps.headset_rgb_hostmode_zone_ids.empty()) + { + LOG_INFO("%s 0x0620 zone decode produced 0 zones — falling back to " + "{0x00, 0x01} (two-earcup layout)", LOG_TAG); + caps.headset_rgb_hostmode_zone_ids.push_back(0x00); + caps.headset_rgb_hostmode_zone_ids.push_back(0x01); + } + + /*---------------------------------------------------------*\ + | Synthesize a single zone cluster so the existing | + | RGBController zone UI lights up with no special-casing. | + | The 0x0620 path is static-color-only; no effect cards, no | + | per-key. One cluster, one LED per discovered zone. | + \*---------------------------------------------------------*/ + HIDPP20ZoneCluster cluster; + cluster.index = 0; + cluster.location = 0; + cluster.effect_count = 0; + caps.zone_clusters.clear(); + caps.zone_clusters.push_back(cluster); + + caps.is_headset_rgb_hostmode = true; + caps.has_zone_effects = true; + caps.rgb_feature_page = HIDPP20_FEAT_HEADSET_RGB_HOSTMODE; + + /*---------------------------------------------------------*\ + | Pin device type to HEADSET. 0x0620 presence is a headset | + | signal and Centurion sub-devices otherwise show type=0. | + | DiscoverDeviceType ran earlier in the probe sequence, so | + | pin it here where we have the evidence. | + \*---------------------------------------------------------*/ + caps.device_type = LOGITECH_DEVICE_TYPE_HEADSET; + + LOG_INFO("%s 0x0620 ready: %zu zone(s), transient (FrameEnd 0x01) mode", + LOG_TAG, caps.headset_rgb_hostmode_zone_ids.size()); +} + +void LogitechHIDPP20Controller::DiscoverPerKeyZones() +{ + /*---------------------------------------------------------*\ + | Try 0x8081 first, fall back to 0x8080 | + \*---------------------------------------------------------*/ + caps.idx_perkey_v2 = GetFeatureIndex(HIDPP20_FEAT_PER_KEY_LIGHTING_V2); + + if(caps.idx_perkey_v2 == 0) + { + caps.idx_perkey_v1 = GetFeatureIndex(HIDPP20_FEAT_PER_KEY_LIGHTING_V1); + } + + uint8_t perkey_idx = (caps.idx_perkey_v2 != 0) ? caps.idx_perkey_v2 : caps.idx_perkey_v1; + + if(perkey_idx == 0) + { + caps.has_perkey = false; + return; + } + + /*----------------------------------------------------------*\ + | Paginated GetInfo enumeration. | + | | + | typeOfInfo is a page index, not a redundant probe. Per | + | the 0x8081 spec the device's zone space is up to 336 IDs | + | organized as three pages of 112 bits each: | + | | + | zone_id = (page * 112) + (byte * 8) + bit | + | | + | An earlier version of this code only queried page 0 on | + | the assumption that all pages echoed the same data. That | + | was wrong — G515 TKL happened to concentrate its zones | + | in page 0 so the bug was invisible, but devices with | + | G-keys, lightbars, media, or logo LEDs report those | + | zones in pages 1 and 2 and were being silently dropped. | + \*----------------------------------------------------------*/ + caps.perkey_zone_ids.clear(); + + size_t page_counts[3] = { 0, 0, 0 }; + + for(uint8_t page = 0; page < 3; page++) + { + /*------------------------------------------------------*\ + | Request body: uint16 BE typeOfInfo + 1 pad byte. | + | Short report carries the 3 bytes at buf[4..6], so | + | { 0x00, page, 0x00 } places page in the low byte of | + | the BE field. | + \*------------------------------------------------------*/ + uint8_t query[3] = { 0x00, page, 0x00 }; + blankFAPmessage response; + int result = SendAckedIntoFAP(perkey_idx, FN_8081_GET_INFO, + query, 3, response); + + if(result <= 0) + { + continue; + } + + /*------------------------------------------------------*\ + | Parse 14-byte bitmap (bytes 2..15 of the response). | + | LSB-first bit order within each byte. Skip zone 0 on | + | page 0 (matches prior behavior; zone 0 is not used). | + \*------------------------------------------------------*/ + const uint8_t* bitmap = response.data + 2; + int start_bit = (page == 0) ? 1 : 0; + + for(int bit_in_page = start_bit; bit_in_page < 112; bit_in_page++) + { + int byte_idx = bit_in_page / 8; + int bit_idx = bit_in_page % 8; + + if(bitmap[byte_idx] & (1 << bit_idx)) + { + uint16_t zone_id = (uint16_t)(page * 112 + bit_in_page); + + /*----------------------------------------------*\ + | Wire protocol 0x8081 Set* functions take a | + | uint8_t zone ID. Zones >255 from the bitmap | + | formula can't actually be addressed — drop | + | them so we don't expose phantom LEDs. | + \*----------------------------------------------*/ + if(zone_id > 255) + { + LOG_WARNING("%s Per-key GetInfo page %u reported " + "unreachable zone %u (wire protocol " + "caps zones at 255); ignoring", + LOG_TAG, page, zone_id); + continue; + } + + caps.perkey_zone_ids.push_back(zone_id); + page_counts[page]++; + } + } + } + + caps.has_perkey = !caps.perkey_zone_ids.empty(); + + /*----------------------------------------------------------*\ + | Detect numpad presence from zone bitmask. | + | Numpad zones are 80-96 in Solaar's KEYCODES numbering. | + \*----------------------------------------------------------*/ + caps.has_numpad = false; + + for(uint16_t zid : caps.perkey_zone_ids) + { + if(zid >= 80 && zid <= 96) + { + caps.has_numpad = true; + break; + } + } + + LOG_VERBOSE("%s Per-key zones discovered: %zu total " + "(page0=%zu, page1=%zu, page2=%zu, numpad=%s)", + LOG_TAG, caps.perkey_zone_ids.size(), + page_counts[0], page_counts[1], page_counts[2], + caps.has_numpad ? "yes" : "no"); +} + +void LogitechHIDPP20Controller::DiscoverKeyboardLayout() +{ + uint8_t idx = GetFeatureIndex(HIDPP20_FEAT_KEYBOARD_LAYOUT); + + if(idx == 0) + { + caps.keyboard_layout_code = 0; + return; + } + + uint8_t recv_data[16] = {}; + int result = SendAcked(idx, 0x00, nullptr, 0, recv_data, sizeof(recv_data)); + + if(result > 0) + { + caps.keyboard_layout_code = recv_data[0]; + LOG_DEBUG("%s Keyboard layout code: %d", LOG_TAG, caps.keyboard_layout_code); + } + else + { + caps.keyboard_layout_code = 0; + } +} + +/*---------------------------------------------------------*\ +| Probe / Initialize / Shutdown | +\*---------------------------------------------------------*/ + +bool LogitechHIDPP20Controller::Probe() +{ + LOG_DEBUG("%s Probing device at %s (index=0x%02X)", + LOG_TAG, location.c_str(), device_index); + + /*-----------------------------------------------------------*\ + | Detect transport type from usage page before anything else. | + | Centurion devices need different framing for all commands. | + \*-----------------------------------------------------------*/ + DiscoverTransport(); + + /*----------------------------------------------------------*\ + | Flush any queued HID reports before probing. | + | The device may have unsolicited notifications (battery, | + | button events, etc.) sitting in the read buffer. | + \*----------------------------------------------------------*/ + { + uint8_t flush_buf[64]; + int flushed = 0; + + while(flushed < 20) + { + int r = hid_read_timeout(dev, flush_buf, sizeof(flush_buf), 0); + + if(r <= 0) + { + break; + } + + flushed++; + } + + if(flushed > 0) + { + LOG_DEBUG("%s Flushed %d queued reports", LOG_TAG, flushed); + } + } + + /*----------------------------------------------------------*\ + | Test IRoot by looking up a known feature. | + | | + | Standard HID++: look up FeatureSet (0x0001) — must exist. | + | Centurion dongle: look up CentPPBridge (0x0003) — the | + | dongle doesn't have FeatureSet, but must have the | + | bridge to reach the sub-device. | + | | + | Retry up to 3 times — wireless devices behind a shared | + | receiver can return stale responses. | + \*----------------------------------------------------------*/ + uint8_t test_idx = 0; + + if(transport.type == HIDPP20_TRANSPORT_CENTURION) + { + /*------------------------------------------------------*\ + | Centurion: try CentPPBridge (0x0003) first for dongle. | + | If not found, try FeatureSet (0x0001) for wired/direct | + | connection where the device IS the endpoint. | + | | + | This is the "is anyone there?" check — use the fast- | + | fail probe policy so non-Centurion or unreachable | + | devices bail in ~500ms instead of ~6s. Once we have a | + | positive response, subsequent discovery uses reliable. | + \*------------------------------------------------------*/ + test_idx = GetFeatureIndex(HIDPP20_FEAT_CENTPPBRIDGE, HIDPP20_POLICY_PROBE); + + if(test_idx != 0) + { + transport.bridge_feat_idx = test_idx; + transport.sub_device_id = 0; + + LOG_DEBUG("%s CentPPBridge at index %d — routing to sub-device", + LOG_TAG, test_idx); + + /*---------------------------------------------------*\ + | Pre-check sub-device availability via | + | getConnectionInfo (CentPPBridge fn0). The vendor | + | app does this and refuses to call sendFragment | + | when MTU=0. | + | | + | Response format (from protocol doc line 910-917): | + | Byte 0: high nibble = connection type/state | + | low nibble + Byte 1 = sub-device data | + | length / MTU | + | Bytes 2+: sub-device descriptors | + | | + | If MTU == 0, no sub-device is connected. Calling | + | sendFragment in that state triggers an undocumented | + | error code 0x0B and wastes the full retry budget. | + | Skip enumeration and let the dongle-watcher path | + | take over until ConnectionStateChangedEvent fires. | + | | + | Bridge is confirmed responsive at this point — | + | use reliable policy for the MTU check. | + \*---------------------------------------------------*/ + uint8_t mtu_recv[16] = {}; + int mtu_result = SendAcked(test_idx, 0x00, + nullptr, 0, + mtu_recv, sizeof(mtu_recv)); + + if(mtu_result > 0) + { + transport.bridge_mtu = + ((uint16_t)(mtu_recv[0] & 0x0F) << 8) | mtu_recv[1]; + + LOG_DEBUG("%s CentPPBridge MTU=%u (%s)", + LOG_TAG, transport.bridge_mtu, + transport.bridge_mtu > 0 ? "sub-device present" + : "no sub-device"); + + if(transport.bridge_mtu == 0) + { + /*----------------------------------------*\ + | No sub-device — skip enumeration. Mark | + | the feature map complete so on-demand | + | lookups don't hit the wire. The dongle | + | will be registered as a watcher and the | + | sub-device will be probed when | + | ConnectionStateChangedEvent fires. | + \*----------------------------------------*/ + caps.feature_map_complete = true; + DiscoverDeviceName(); + return true; + } + } + else + { + LOG_DEBUG("%s CentPPBridge getConnectionInfo failed (result=%d)", + LOG_TAG, mtu_result); + } + } + else + { + LOG_DEBUG("%s No CentPPBridge — Centurion direct connection", LOG_TAG); + test_idx = GetFeatureIndex(HIDPP20_FEAT_FEATURE_SET, HIDPP20_POLICY_PROBE); + } + } + else + { + /*-------------------------------------------------------*\ + | Standard HID++: probe FeatureSet (0x0001) — fast-fail. | + | The probe policy already includes its own retry; the | + | outer loop is preserved for buffer-flushing behavior | + | between attempts. | + \*-------------------------------------------------------*/ + for(int attempt = 0; attempt < 3 && test_idx == 0; attempt++) + { + if(attempt > 0) + { + uint8_t retry_buf[64]; + + while(hid_read_timeout(dev, retry_buf, sizeof(retry_buf), 10) > 0) + { + } + + LOG_DEBUG("%s IRoot retry %d at %s", LOG_TAG, attempt + 1, location.c_str()); + } + + test_idx = GetFeatureIndex(HIDPP20_FEAT_FEATURE_SET, HIDPP20_POLICY_PROBE); + } + } + + if(test_idx == 0) + { + LOG_DEBUG("%s IRoot probe failed at %s — device does not respond", + LOG_TAG, location.c_str()); + return false; + } + + /*----------------------------------------------------------*\ + | If retries were needed, flush delayed responses from | + | failed attempts before continuing with discovery. | + \*----------------------------------------------------------*/ + { + uint8_t post_buf[64]; + + while(hid_read_timeout(dev, post_buf, sizeof(post_buf), 10) > 0) + { + } + } + + /*-----------------------------------------------------------*\ + | Enumerate all features in bulk. For standard HID++, uses | + | FeatureSet GetCount + GetFeatureId loop. For Centurion | + | sub-devices, uses bulk GetFeatureId (single response). | + | After this, GetFeatureIndex uses the map — no wire traffic. | + \*-----------------------------------------------------------*/ + { + /*------------------------------------------------------*\ + | For Centurion bridged, FeatureSet is at index 1 on the | + | sub-device. For standard HID++, test_idx is the | + | FeatureSet index from the IRoot probe. | + \*------------------------------------------------------*/ + uint8_t fs_idx = (transport.type == HIDPP20_TRANSPORT_CENTURION && + transport.bridge_feat_idx != 0) + ? 1 // CenturionFeatureSet always at index 1 on sub-device + : test_idx; + + EnumerateFeatures(fs_idx); + + /*------------------------------------------------------*\ + | If bridged and bulk enumeration failed, the sub-device | + | isn't reachable (e.g., headset off or on USB cable). | + | Mark map as complete so lookups don't hit the wire. | + | The device will be discovered with no features — it | + | can be re-probed when the sub-device comes online. | + \*------------------------------------------------------*/ + if(transport.bridge_feat_idx != 0 && !caps.feature_map_complete) + { + LOG_DEBUG("%s Sub-device not reachable through bridge — dongle only", + LOG_TAG); + caps.feature_map_complete = true; + } + } + + /*----------------------------------------------------------*\ + | Discover device identity. | + | On Centurion with bridge, this now queries the sub-device | + | (headset) through the bridge, not the dongle. | + \*----------------------------------------------------------*/ + DiscoverDeviceName(); + log_tag = "[LogitechHID++ " + caps.device_name + "]"; + DiscoverDeviceType(); + DiscoverFirmwareInfo(); + + /*---------------------------------------------------------*\ + | Discover profile management features | + \*---------------------------------------------------------*/ + caps.idx_profile_management = GetFeatureIndex(HIDPP20_FEAT_PROFILE_MANAGEMENT); + caps.idx_onboard_profiles = GetFeatureIndex(HIDPP20_FEAT_ONBOARD_PROFILES); + caps.idx_disable_keys_by_usage = GetFeatureIndex(HIDPP20_FEAT_DISABLE_KEYS_BY_USAGE); + + /*---------------------------------------------------------*\ + | Discover RGB capabilities | + \*---------------------------------------------------------*/ + DiscoverRGBEffects(); + if(caps.idx_rgb_effects == 0) + { + DiscoverHeadsetRGBHostmode(); + } + DiscoverPerKeyZones(); + DiscoverKeyboardLayout(); + + /*---------------------------------------------------------*\ + | Probe WirelessStatus (0x1D4B) for reconnect detection. | + | Lightspeed devices behind kernel-managed receivers send | + | WirelessStatus events when they reconnect after power | + | cycle. Cache the feature index so the reader thread can | + | detect these events without sending commands. | + \*---------------------------------------------------------*/ + caps.idx_wireless_status = GetFeatureIndex(HIDPP20_FEAT_WIRELESS_STATUS); + + if(!caps.has_zone_effects && !caps.has_perkey) + { + LOG_DEBUG("%s %s: no RGB features found", LOG_TAG, caps.device_name.c_str()); + + /*------------------------------------------------------*\ + | Centurion dongles with bridge stay alive to watch for | + | sub-device connection events, even without RGB. | + \*------------------------------------------------------*/ + if(transport.bridge_feat_idx != 0) + { + return true; + } + + return false; + } + + LOG_VERBOSE("%s %s: zones=%zu perkey=%zu", + LOG_TAG, caps.device_name.c_str(), + caps.zone_clusters.size(), caps.perkey_zone_ids.size()); + + return true; +} + +void LogitechHIDPP20Controller::Initialize() +{ + /*-----------------------------------------------------------*\ + | No device state changes here — let firmware effects keep | + | running until DeviceUpdateMode claims control with real | + | colors ready via ClaimSWControlIfNeeded(). | + \*-----------------------------------------------------------*/ + init_generation++; + initialized = true; +} + +void LogitechHIDPP20Controller::Shutdown() +{ + if(!initialized) + { + return; + } + + StopPowerManager(); + + /*---------------------------------------------------------*\ + | Release SW control | + \*---------------------------------------------------------*/ + if(caps.idx_rgb_effects != 0) + { + SetSWControl(0, 0); + } + + /*---------------------------------------------------------*\ + | Restore firmware mode | + \*---------------------------------------------------------*/ + if(caps.idx_profile_management != 0) + { + uint8_t data[1] = { 0x03 }; + blankFAPmessage response; + SendAckedIntoFAP(caps.idx_profile_management, FN_8101_GET_SET_MODE, + data, 1, response); + } + else if(caps.idx_onboard_profiles != 0) + { + uint8_t data[1] = { 0x01 }; + blankFAPmessage response; + SendAckedIntoFAP(caps.idx_onboard_profiles, FN_8100_SET_ONBOARD_MODE, + data, 1, response); + } + + /*---------------------------------------------------------*\ + | Release 0x0620 Headset RGB hostmode claim. Best-effort; | + | mirrors the SetHostMode() additive branch. | + \*---------------------------------------------------------*/ + if(caps.idx_headset_rgb_hostmode != 0) + { + uint8_t off = 0x00; + blankFAPmessage release_response; + SendAckedIntoFAP(caps.idx_headset_rgb_hostmode, + FN_0620_SET_HOST_MODE_STATE, + &off, 1, release_response); + } + + initialized = false; +} + +/*---------------------------------------------------------*\ +| Accessors | +\*---------------------------------------------------------*/ + +const HIDPP20DeviceCapabilities& LogitechHIDPP20Controller::GetCapabilities() const +{ + return caps; +} + +std::string LogitechHIDPP20Controller::GetDeviceLocation() +{ + return "HID: " + location; +} + +std::string LogitechHIDPP20Controller::GetSerialString() +{ + return caps.serial_number; +} + +uint32_t LogitechHIDPP20Controller::GetInitGeneration() const +{ + return init_generation; +} + +/*---------------------------------------------------------*\ +| SW Control and Power | +\*---------------------------------------------------------*/ + +int LogitechHIDPP20Controller::SetSWControl(uint8_t mode, uint8_t flags) +{ + if(caps.idx_rgb_effects == 0) + { + return 0; + } + + blankFAPmessage response; + int result; + + if(caps.sw_control_simple) + { + /*------------------------------------------------------*\ + | 0x8070: simple [enabled(bool), persist(bool)] | + \*------------------------------------------------------*/ + uint8_t data[2] = { (uint8_t)(mode > 0 ? 0x01 : 0x00), 0x00 }; + result = SendAckedIntoFAP(caps.idx_rgb_effects, caps.fn_sw_control, + data, 2, response); + } + else + { + /*------------------------------------------------------*\ + | 0x8071/0x0600: [0x01(set), mode, flags] | + \*------------------------------------------------------*/ + uint8_t data[3] = { 0x01, mode, flags }; + result = SendAckedIntoFAP(caps.idx_rgb_effects, caps.fn_sw_control, + data, 3, response); + } + + LOG_DEBUG("%s SetSWControl mode=%d flags=0x%02X result=%d", + LOG_TAG, mode, flags, result); + + return result; +} + +void LogitechHIDPP20Controller::SetRGBPowerMode(uint8_t mode) +{ + if(caps.idx_rgb_effects == 0 || !caps.has_power_mgmt) + { + return; + } + + uint8_t data[2] = { 0x01, mode }; + blankFAPmessage response; + SendAckedIntoFAP(caps.idx_rgb_effects, caps.fn_pwr_mode, + data, 2, response); + + LOG_DEBUG("%s SetRGBPowerMode mode=%d", LOG_TAG, mode); +} + +void LogitechHIDPP20Controller::SetHostMode() +{ + if(caps.idx_profile_management != 0) + { + uint8_t data[1] = { 0x05 }; + blankFAPmessage response; + SendAckedIntoFAP(caps.idx_profile_management, FN_8101_GET_SET_MODE, + data, 1, response); + + LOG_DEBUG("%s ProfileManagement set to host mode", LOG_TAG); + } + else if(caps.idx_onboard_profiles != 0) + { + /*-------------------------------------------------------*\ + | Observed vendor-app wire pattern on G502 X PLUS: | + | unconditional SetOnboardMode(host) immediately | + | followed by a GetOnboardMode verify read. The vendor | + | app never reads first — it writes fn1 with 0x02 then | + | re-queries fn2, ignoring the response value (no retry | + | logic, no branching on it). The verify appears to be a | + | state-settle / sync point rather than a check, but | + | since we don't know its firmware-side effect, we mirror | + | it. | + | | + | An earlier revision of this function added a pre-Set | + | GetOnboardMode guard to skip the write when already | + | in host mode. That deviated from the observed wire | + | behavior, so it has been removed. | + \*-------------------------------------------------------*/ + uint8_t set_data[1] = { 0x02 }; + blankFAPmessage set_response; + SendAckedIntoFAP(caps.idx_onboard_profiles, FN_8100_SET_ONBOARD_MODE, + set_data, 1, set_response); + + blankFAPmessage verify_response; + SendAckedIntoFAP(caps.idx_onboard_profiles, FN_8100_GET_ONBOARD_MODE, + nullptr, 0, verify_response); + + LOG_DEBUG("%s OnboardProfiles set to host mode, verify=0x%02X", + LOG_TAG, verify_response.data[0]); + } + + /*-------------------------------------------------------*\ + | 0x0620 Headset RGB hostmode claim. Additive — a headset | + | exposing 0x0620 typically won't also have 0x8100/0x8101 | + | but we don't assume mutual exclusion. Sticky claim, not | + | re-issued per write; wake path re-enters SetHostMode | + | after reconnect which reinstates it for free. | + \*-------------------------------------------------------*/ + if(caps.idx_headset_rgb_hostmode != 0) + { + uint8_t on = 0x01; + blankFAPmessage claim_response; + SendAckedIntoFAP(caps.idx_headset_rgb_hostmode, + FN_0620_SET_HOST_MODE_STATE, + &on, 1, claim_response); + + LOG_DEBUG("%s 0x0620 SetHostModeState(1) sent", LOG_TAG); + } +} + +bool LogitechHIDPP20Controller::ClaimSWControlIfNeeded() +{ + if(sw_control_claimed) + { + return true; + } + + if(caps.idx_rgb_effects == 0 || !device_online.load()) + { + return false; + } + + /*----------------------------------------------------------*\ + | Two-phase claim to avoid the onboard→host transition | + | flash (warm-white ~3000K, ~50ms) visible on G502 X PLUS. | + | | + | The SW Control `flags` bits are not "Zone/Power/Effect" | + | as the overview labels them — derived from a G502 wire | + | capture, bit 0 = effect control, bit 1 = power management, | + | bit 2 = NV config. Setting the effect bit suspends the | + | firmware's autonomous effect engine, and anything the host | + | hasn't explicitly painted since that moment shows as the | + | firmware's default LED buffer — on the G502 X PLUS that | + | default is warm-white. | + | | + | The observed vendor-app behavior paints the G502 with | + | flags=6 (power+NV, NOT effect) the entire time, so the | + | firmware effect engine keeps rendering the onboard | + | profile's output right up to the moment SetEffectByIndex | + | replaces it — no visible gap. | + | | + | We can't just stay on flags=6 forever: the idle/wake | + | state machine in OnUserActivity uses flags=5/3 as its | + | active/idle signals and needs those specific values for | + | the firmware to generate the right onUserActivity events. | + | So we claim at flags=6, let the first per-key frame paint | + | through the transition, then upgrade to flags=5 only | + | after the per-key layer is active — at that point per-key | + | masks any zone output anyway, so the 6→5 transition is | + | invisible. | + | | + | The previous sequence wrote `(3,7)` then `(3,5)` to mimic | + | the vendor app's *keyboard first-contact* behavior. That | + | was right for the initial G515 bring-up but wrong for the | + | mouse — on the G502 X PLUS the effect bit at claim time | + | is the root cause of the startup flash. | + | | + | The vendor-app claim sequence does two WritePowerConfig | + | calls around SetHostMode, writing (a) its profile's sleep | + | value then (b) that value minus the firmware off-ramp. | + | We don't write timers at all: we don't have a profile we | + | want to impose on the device, and our host-side StartSleep | + | trigger already fires SetRgbPowerMode(3) explicitly at the | + | moment we want the fade to begin. | + \*----------------------------------------------------------*/ + int claim_result = SetSWControl(3, 6); + + if(claim_result <= 0) + { + LOG_DEBUG("%s SW control claim failed (SetSWControl(3,6) result=%d)", + LOG_TAG, claim_result); + return false; + } + + /*---------------------------------------------------------*\ + | Keyboard-family handshake on feature 0x4522 | + | (DisableKeysByUsage). G815 / G915 / G Pro send this fn3 + | + | fn1 empty-payload pair between SetSWControl and the first | + | mode write. Feature-gated inside — no-op on G502 / G515. | + \*---------------------------------------------------------*/ + DoDisableKeysByUsageHandshake(); + + SetRGBPowerMode(1); + WritePowerConfig(idle_timeout_s, sleep_timeout_s); + SetHostMode(); + WritePowerConfig(idle_timeout_s, sleep_timeout_s); + + written_idle_s = idle_timeout_s; + written_sleep_s = sleep_timeout_s; + + sw_control_claimed = true; + sw_control_needs_upgrade_to_5 = true; + + LOG_DEBUG("%s Claimed SW control at flags=6 " + "(effect engine still autonomous until first per-key frame)", + LOG_TAG); + return true; +} + +void LogitechHIDPP20Controller::UpgradeSwControlAfterFirstPaint() +{ + /*---------------------------------------------------------*\ + | Called by RGBController_LogitechHIDPP20::DeviceUpdateLEDs | + | immediately after the first successful PerKeyFrameEnd of | + | a newly-claimed session. At this point the per-key buffer | + | is populated with real host colors, so the per-key layer | + | masks the zone layer and the 6→5 transition no longer | + | exposes the firmware's default LED buffer. Upgrading to | + | flags=5 puts the device into the "active steady state" | + | that OnUserActivity expects for idle detection events. | + \*---------------------------------------------------------*/ + if(!sw_control_needs_upgrade_to_5) + { + return; + } + + if(caps.idx_rgb_effects == 0 || !device_online.load()) + { + sw_control_needs_upgrade_to_5 = false; + return; + } + + int result = SetSWControl(3, 5); + + if(result > 0) + { + sw_control_needs_upgrade_to_5 = false; + LOG_DEBUG("%s Upgraded SW control to flags=5 " + "(per-key layer now masks zone layer)", LOG_TAG); + } + else + { + LOG_DEBUG("%s SW control upgrade to flags=5 failed (result=%d)", + LOG_TAG, result); + /* Leave the flag set so the next frame will retry. */ + } +} + +void LogitechHIDPP20Controller::DoDisableKeysByUsageHandshake() +{ + /*----------------------------------------------------------*\ + | G815 / G915 / G Pro keyboards send this two-call | + | handshake on feature 0x4522 (DisableKeysByUsage) before | + | any mode change or per-key write. The original OpenRGB | + | G815 + G915 controllers both do it in their BeginModeSet | + | and InitializeDirect paths. Both payloads are empty — | + | bare function calls — suggesting they're state reads | + | used as a firmware sync point, not actual disable-keys | + | writes (those would require a keyset in the payload). | + | | + | Feature-gated: caps.idx_disable_keys_by_usage is only | + | non-zero on devices that enumerate 0x4522. G502 and G515 | + | do not enumerate it, so this is a no-op on those. | + \*----------------------------------------------------------*/ + if(caps.idx_disable_keys_by_usage == 0 || !device_online.load()) + { + return; + } + + blankFAPmessage response; + SendAckedIntoFAP(caps.idx_disable_keys_by_usage, 0x30, + nullptr, 0, response, HIDPP20_POLICY_PROBE); + SendAckedIntoFAP(caps.idx_disable_keys_by_usage, 0x10, + nullptr, 0, response, HIDPP20_POLICY_PROBE); + + LOG_DEBUG("%s 0x4522 DisableKeysByUsage handshake sent (fn3 + fn1)", LOG_TAG); +} + +/*---------------------------------------------------------*\ +| Observed per-key prep sequence | +| | +| Two SetEffectByIndex calls cloned byte-for-byte from a | +| wire capture of the vendor app talking to a G502 X PLUS | +| (wired-ish connection via Lightspeed receiver). The two | +| frames are: | +| | +| Frame 2297 (17.348s, ~262ms after SetOnboardMode(02)): | +| 1101091a ff 02 00 00 00 00 00 00 20 64 00 00 01 … | +| RgbEffects.SetEffectByIndex | +| cluster=0xFF (all clusters) | +| effectIdx=0x02 (Breathing on G502's enumerated set) | +| params=[00 00 00 00 00 00 20 64 00 00] (10 bytes) | +| — positions [6]=0x20, [7]=0x64 are non-zero. The | +| Breathing effect parameter layout documented in | +| the protocol reference has period/brightness in | +| those slots, but the exact meaning of these two | +| values in this context is NOT understood. The | +| vendor app sends them verbatim on every claim; we | +| mirror. | +| persist=0x01 | +| | +| Frame 2321 (17.443s, ~95ms after frame 2297): | +| 1101091a ff 04 00 00 00 00 00 00 00 00 00 00 01 … | +| RgbEffects.SetEffectByIndex | +| cluster=0xFF | +| effectIdx=0x04 — OUT OF RANGE on G502 X PLUS (the | +| device only enumerates effects 0..3 via | +| GetEffectInfo). Likely a "custom / direct mode" | +| slot the firmware accepts but does not advertise | +| through the normal enumeration. | +| params=[00 × 10] | +| persist=0x01 | +| | +| The function we call is caps.fn_set_effect (0x10 on | +| 0x8071, 0x30 on 0x8070, same as SetZoneEffect uses). | +| | +| We do NOT attempt to derive these values from the effect | +| param layout tables because we don't understand what | +| they mean. They're observed-working bytes from the wire | +| capture and that's the contract. If this prep sequence | +| later turns out to work on other devices, the gating in | +| DeviceUpdateLEDs can be loosened. | +\*---------------------------------------------------------*/ + +void LogitechHIDPP20Controller::DoObservedPerKeyPrep() +{ + if(caps.idx_rgb_effects == 0 || !device_online.load()) + { + return; + } + + /*----------------------------------------------------------*\ + | Prep1: SetEffectByIndex(cluster=0xFF, effectIdx=2, params) | + | with the device-wide template bytes at params[6..7]. | + | | + | The template bytes are discovered at feature-discovery | + | time via GetEffectSpecificInfo on any firmware effect | + | card; the vendor app does the same read-then-echo | + | pattern, and on a G502 X PLUS the read value is 0x20 0x64 | + | across every card. We don't know what those bytes mean | + | semantically — just that the device expects to see them | + | echoed back verbatim in this position when priming the | + | firmware effect engine for per-key takeover. | + \*----------------------------------------------------------*/ + uint8_t prep1[16] = + { + 0xFF, 0x02, /* cluster, effectIdx */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* params[0..5] */ + caps.effect_card_template[0], /* params[6] — device */ + caps.effect_card_template[1], /* params[7] — device */ + 0x00, 0x00, /* params[8..9] */ + 0x01, /* persist */ + 0x00, 0x00, 0x00 /* padding */ + }; + blankFAPmessage prep1_resp; + SendAckedIntoFAP(caps.idx_rgb_effects, caps.fn_set_effect, + prep1, 16, prep1_resp); + + /*---------------------------------------------------------*\ + | Prep2: SetEffectByIndex with effectIdx set to the first | + | out-of-range slot above the last enumerated effect, all | + | params zero. | + | | + | On the G502 X PLUS (4 enumerated effects: 0..3) this | + | means effectIdx=4 — matches the value in pcap frame 2321. | + | On other devices, effectIdx is parameterized by effect | + | count so the same "first OOR slot" semantic holds. | + | | + | The RE thread's working theory is that this is a firmware | + | "custom/direct mode" slot the effect engine accepts but | + | doesn't advertise through GetEffectInfo. Without that | + | slot being written, the per-key pipeline doesn't enter | + | cleanly and the firmware exposes its default LED state | + | during the claim→paint window (the 3000K warm-white flash | + | we previously observed on cold starts). | + \*---------------------------------------------------------*/ + uint8_t num_effects = 0; + + if(!caps.zone_clusters.empty()) + { + size_t count = caps.zone_clusters[0].effects.size(); + num_effects = (count > 0xFFu) ? 0xFFu : (uint8_t)count; + } + + uint8_t prep2[16] = + { + 0xFF, num_effects, /* cluster, first OOR slot */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* params[0..5] */ + 0x00, 0x00, 0x00, 0x00, /* params[6..9] */ + 0x01, /* persist */ + 0x00, 0x00, 0x00 /* padding */ + }; + blankFAPmessage prep2_resp; + SendAckedIntoFAP(caps.idx_rgb_effects, caps.fn_set_effect, + prep2, 16, prep2_resp); + + LOG_DEBUG("%s DoObservedPerKeyPrep: prep1 template=0x%02X%02X " + "prep2 idx=%u (OOR slot above %u enumerated effects)", + LOG_TAG, + caps.effect_card_template[0], caps.effect_card_template[1], + num_effects, num_effects); +} + +void LogitechHIDPP20Controller::DoKeyboardFamilyPerKeyPrep() +{ + /*---------------------------------------------------------*\ + | G815 / G915 / G Pro per-key takeover prep, cloned from | + | the InitializeDirect sequence in their legacy OpenRGB | + | controllers. Three steps after the claim-time 0x4522 | + | handshake (which fires from ClaimSWControlIfNeeded): | + | | + | 1. For each enumerated cluster, SetEffectByIndex with | + | effectIdx=0 (Off) and persist=1. This deactivates | + | the firmware effect engine per-cluster — different | + | from the G515 static-black fallback, which leaves | + | the effect engine running with a black static color. | + | | + | 2. Send a primer SetIndividualRgbZones write covering | + | one zone (the first enumerated) at black. G915 uses | + | Escape specifically; we use the first enumerated | + | zone for portability. | + | | + | 3. FrameEnd, so the primer write commits and the | + | per-key layer becomes the visible output. | + | | + | Gate (caller's responsibility): feature 0x4522 present | + | AND per-key V2 feature present. G502 / G515 fail the | + | 0x4522 side; older keyboards without 0x8081 fail the | + | per-key side. | + \*---------------------------------------------------------*/ + if(caps.idx_rgb_effects == 0 || caps.idx_perkey_v2 == 0 || !device_online.load()) + { + return; + } + + for(size_t i = 0; i < caps.zone_clusters.size(); i++) + { + uint8_t cluster_off[16] = + { + caps.zone_clusters[i].index, 0x00, /* cluster, effectIdx=0 (Off) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, /* persist */ + 0x00, 0x00, 0x00 + }; + blankFAPmessage cluster_resp; + SendAckedIntoFAP(caps.idx_rgb_effects, caps.fn_set_effect, + cluster_off, 16, cluster_resp); + } + + if(caps.perkey_zone_ids.empty()) + { + LOG_DEBUG("%s DoKeyboardFamilyPerKeyPrep: no per-key zones enumerated, " + "skipping primer key", LOG_TAG); + return; + } + + uint8_t primer_zone = (uint8_t)(caps.perkey_zone_ids[0] & 0xFF); + uint8_t primer[4] = { primer_zone, 0x00, 0x00, 0x00 }; + + std::vector primer_zones; + primer_zones.push_back(primer_zone); + SendPerKeyData(caps.idx_perkey_v2, FN_8081_SET_INDIVIDUAL, + primer, 4, primer_zones); + + PerKeyFrameEnd(); + + LOG_DEBUG("%s DoKeyboardFamilyPerKeyPrep: %zu clusters -> Off, " + "primer zone=0x%02X, FrameEnd committed", + LOG_TAG, caps.zone_clusters.size(), primer_zone); +} + +/*---------------------------------------------------------*\ +| Retry-paint scheduling | +| | +| Called by RGBController_LogitechHIDPP20::DeviceUpdateLEDs | +| when a full pass completes with `acked_zones.size() != | +| attempted_zones.size()` (partial commit). The retry | +| re-runs a whole DeviceUpdateLEDs cycle from the power | +| thread so the uncommitted zones (marked | +| HIDPP20_UNCOMMITTED in sent_colors) get another shot. | +| | +| Streaming animation frames also call ScheduleRetryPaint | +| on partial commit, but the next animation frame almost | +| always CancelRetryPaint()s before the deadline fires, | +| so the retry is a free no-op in the streaming path. | +| The retry only actually fires when no follow-up frame | +| arrives — which matches our two problem cases: | +| 1. First frame after a reconnect-transient claim | +| (Direct mode, no animation timer). | +| 2. Last frame of an animation that then stops. | +\*---------------------------------------------------------*/ + +void LogitechHIDPP20Controller::ScheduleRetryPaint() +{ + size_t max_attempts = + sizeof(HIDPP20_REPAINT_RETRY_BACKOFF_MS) / sizeof(uint16_t); + + uint8_t attempt = retry_paint_attempt_.load(); + + if(attempt >= max_attempts) + { + /*-----------------------------------------------------*\ + | Retry budget exhausted. Give up for this sequence — | + | the next fresh failure (after a full_commit clears | + | the attempt counter) will start from attempt 0. | + \*-----------------------------------------------------*/ + retry_paint_deadline_.store(std::chrono::steady_clock::time_point{}); + LOG_DEBUG("%s retry paint budget exhausted (%zu attempts)", + LOG_TAG, max_attempts); + return; + } + + uint16_t delay_ms = HIDPP20_REPAINT_RETRY_BACKOFF_MS[attempt]; + std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(delay_ms); + + retry_paint_deadline_.store(deadline); + + LOG_DEBUG("%s retry paint scheduled attempt=%u delay=%ums", + LOG_TAG, attempt, delay_ms); +} + +void LogitechHIDPP20Controller::CancelRetryPaint() +{ + retry_paint_deadline_.store(std::chrono::steady_clock::time_point{}); + retry_paint_attempt_.store(0); +} + +void LogitechHIDPP20Controller::TickRetryPaintIfPending() +{ + /*---------------------------------------------------------*\ + | Called from the power thread's main loop each tick. | + | Checks the retry deadline and fires the repaint callback | + | when it expires. The callback runs DeviceUpdateLEDs on | + | the power thread's context — not recursively from inside | + | another DeviceUpdateLEDs call. | + \*---------------------------------------------------------*/ + std::chrono::steady_clock::time_point deadline = retry_paint_deadline_.load(); + + if(deadline == std::chrono::steady_clock::time_point{}) + { + return; + } + + if(std::chrono::steady_clock::now() < deadline) + { + return; + } + + /*---------------------------------------------------------*\ + | Clear the deadline before firing so a concurrent | + | ScheduleRetryPaint (from a different thread) doesn't | + | double-fire on the same tick. Advance the attempt counter | + | so the next ScheduleRetryPaint (if this retry also fails) | + | picks the next backoff slot. | + \*---------------------------------------------------------*/ + retry_paint_deadline_.store(std::chrono::steady_clock::time_point{}); + retry_paint_attempt_.fetch_add(1); + + LOG_DEBUG("%s retry paint firing", LOG_TAG); + + if(request_repaint_fn) + { + request_repaint_fn(); + } +} + +/*---------------------------------------------------------*\ +| Per-key lighting (0x8081) | +\*---------------------------------------------------------*/ + +void LogitechHIDPP20Controller::SetPerKeyColors + ( + const std::vector>& zone_colors + ) +{ + if(!device_online.load()) return; + + uint8_t perkey_idx = (caps.idx_perkey_v2 != 0) ? caps.idx_perkey_v2 : caps.idx_perkey_v1; + + if(perkey_idx == 0) + { + return; + } + + /*---------------------------------------------------------*\ + | Batch into SetIndividualRgbZones (fn1): 4 entries/packet | + | Each entry = [zone_id, R, G, B]. Track the zones in each | + | batch so PerKeyFrameEnd can report which committed. | + \*---------------------------------------------------------*/ + uint8_t data[16]; + std::vector batch_zones; + int count = 0; + + for(size_t i = 0; i < zone_colors.size(); i++) + { + int offset = count * 4; + data[offset + 0] = (uint8_t)zone_colors[i].first; + data[offset + 1] = RGBGetRValue(zone_colors[i].second); + data[offset + 2] = RGBGetGValue(zone_colors[i].second); + data[offset + 3] = RGBGetBValue(zone_colors[i].second); + batch_zones.push_back((uint8_t)zone_colors[i].first); + count++; + + if(count == 4 || i == zone_colors.size() - 1) + { + SendPerKeyData(perkey_idx, FN_8081_SET_INDIVIDUAL, + data, count * 4, batch_zones); + memset(data, 0, sizeof(data)); + batch_zones.clear(); + count = 0; + } + } +} + +void LogitechHIDPP20Controller::SetAllPerKeyColor(RGBColor color) +{ + if(!device_online.load()) return; + + uint8_t perkey_idx = (caps.idx_perkey_v2 != 0) ? caps.idx_perkey_v2 : caps.idx_perkey_v1; + + if(perkey_idx == 0) + { + return; + } + + uint8_t r = RGBGetRValue(color); + uint8_t g = RGBGetGValue(color); + uint8_t b = RGBGetBValue(color); + + /*----------------------------------------------------------*\ + | Use SetRangeRgbZones (fn5): [start, end, R, G, B] × 3 | + | per packet. Sets all zones in a contiguous range to one | + | color. Gaps in zone IDs are silently ignored by firmware. | + | For uniform color this is far more efficient than fn6: | + | 1-2 packets vs 8 packets for 94 zones. | + \*----------------------------------------------------------*/ + uint8_t min_zone = 255, max_zone = 0; + + for(uint16_t zid : caps.perkey_zone_ids) + { + if(zid > 0 && zid <= 255) + { + if((uint8_t)zid < min_zone) min_zone = (uint8_t)zid; + if((uint8_t)zid > max_zone) max_zone = (uint8_t)zid; + } + } + + if(min_zone <= max_zone) + { + uint8_t data[5] = { min_zone, max_zone, r, g, b }; + std::vector batch_zones; + + for(uint16_t zid : caps.perkey_zone_ids) + { + if(zid >= min_zone && zid <= max_zone) + { + batch_zones.push_back((uint8_t)zid); + } + } + + SendPerKeyData(perkey_idx, FN_8081_SET_RANGE, data, 5, batch_zones); + } +} + +void LogitechHIDPP20Controller::SendPerKeyData + ( + uint8_t perkey_idx, + uint8_t function, + const uint8_t* data, + size_t len, + const std::vector& zone_ids + ) +{ + /*-----------------------------------------------------------*\ + | Truly fire-and-forget. Push the packet onto the wire, | + | record the zones it covers in outstanding_writes, and | + | return. PerKeyFrameEnd will drain the response queue at | + | end-of-frame and FIFO-match each ACK back to the | + | corresponding outstanding entry. | + | | + | The retry/backoff machinery is intentionally NOT used | + | here — when a streaming frame fails, we don't want to | + | delay the next frame retrying old data. The carry-over | + | of uncommitted zones via sent_colors[i]=HIDPP20_UNCOMMITTED | + | naturally ensures missed keys land in the next frame. | + \*-----------------------------------------------------------*/ + int send_result = SendMessage(perkey_idx, function, data, len); + + if(send_result < 0) + { + LOG_DEBUG("%s SendPerKeyData wire send failed (result=%d) func=0x%02X", + LOG_TAG, send_result, function); + /* Still record the outstanding entry — its zones will */ + /* be reported as unacked, which is correct. */ + } + + OutstandingPerKeyWrite entry; + entry.function = function; + entry.zone_ids = zone_ids; + outstanding_writes.push_back(std::move(entry)); +} + +PerKeyFrameResult LogitechHIDPP20Controller::PerKeyFrameEnd() +{ + PerKeyFrameResult result; + result.frame_end_acked = false; + + /*---------------------------------------------------------*\ + | Build attempted_zones from the outstanding writes list | + | up front so the caller can use it for both the success | + | and failure paths. | + \*---------------------------------------------------------*/ + for(size_t w = 0; w < outstanding_writes.size(); w++) + { + const std::vector& zone_ids = outstanding_writes[w].zone_ids; + + for(size_t z = 0; z < zone_ids.size(); z++) + { + result.attempted_zones.push_back(zone_ids[z]); + } + } + + if(!device_online.load()) + { + outstanding_writes.clear(); + return result; + } + + uint8_t perkey_idx = (caps.idx_perkey_v2 != 0) ? caps.idx_perkey_v2 : caps.idx_perkey_v1; + + if(perkey_idx == 0) + { + outstanding_writes.clear(); + return result; + } + + /*---------------------------------------------------------*\ + | Send FrameEnd directly. No retry, no backoff: a streaming | + | frame failure means the next frame's delta will pick up | + | the missed keys, and we don't want to delay that frame. | + | | + | Format (matches observed wire capture): LONG message | + | (0x11), 16 bytes of zeros. Firmware expects long-format | + | FrameEnd | + | — short-format hits intermittent BUSY. | + \*---------------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + + uint8_t data[16] = {}; + int send_result = SendMessage(perkey_idx, FN_8081_FRAME_END, data, sizeof(data)); + + if(send_result < 0) + { + LOG_DEBUG("%s FrameEnd wire send failed (result=%d)", LOG_TAG, send_result); + outstanding_writes.clear(); + return result; + } + + /*---------------------------------------------------------*\ + | Drain responses in FIFO order until we either see the | + | FrameEnd ACK or run out the wait budget. Each per-key | + | write response is matched (by feature + function high | + | nibble) to the head of outstanding_writes; matched zones | + | go into acked_zones. The FrameEnd response itself is the | + | terminating event. | + | | + | Wait budget: 300ms. Generous enough to absorb the slow | + | batch-ACK behavior we've seen on G515 (~700ms p99) for | + | dense per-key frames, but won't actually consume that | + | much time on healthy devices — the loop exits the moment | + | the FrameEnd response shows up. | + \*---------------------------------------------------------*/ + std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(300); + size_t outstanding_idx = 0; + int busy_retries = 0; + + while(true) + { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + if(now >= deadline) + { + LOG_DEBUG("%s FrameEnd timed out waiting for ACK (matched %zu/%zu writes)", + LOG_TAG, outstanding_idx, outstanding_writes.size()); + break; + } + + int remaining = (int)std::chrono::duration_cast( + deadline - now).count(); + if(remaining <= 0) + { + break; + } + + uint8_t resp_feat = 0; + uint8_t resp_func = 0; + uint8_t resp_data[60] = {}; + + int rd = ReadMessage(&resp_feat, &resp_func, + resp_data, sizeof(resp_data), + remaining); + + if(rd < 0) + { + LOG_DEBUG("%s FrameEnd read error (result=%d)", LOG_TAG, rd); + break; + } + + if(rd == 0) + { + /* timeout */ + LOG_DEBUG("%s FrameEnd timed out waiting for ACK (matched %zu/%zu writes)", + LOG_TAG, outstanding_idx, outstanding_writes.size()); + break; + } + + /*-----------------------------------------------------*\ + | HID++ error frame: feat=0xFF, func=err_feat, | + | data[0]=err_func, data[1]=err_code. | + | | + | The case we care about is BUSY (0x08) for our | + | FrameEnd: the firmware is still draining the per-key | + | write queue and asks us to re-send. Without this we | + | hang on the deadline waiting for an ACK that never | + | comes, since BUSY-rejected commands are not queued. | + | | + | Re-send with a tight budget — 3 retries, 30ms gap. | + | If BUSY persists past that, give up for this frame | + | and let delta carry-over handle it next frame. | + \*-----------------------------------------------------*/ + if(resp_feat == 0xFF) + { + uint8_t err_feat = resp_func; + uint8_t err_func_byte = resp_data[0]; + uint8_t err_code = resp_data[1]; + + bool is_our_frame_end = + (err_feat == perkey_idx) && + ((err_func_byte & 0xF0) == FN_8081_FRAME_END); + + if(is_our_frame_end) + { + size_t max_busy_retries = + sizeof(HIDPP20_FRAME_END_BUSY_BACKOFF_MS) / sizeof(uint16_t); + + if(err_code == 0x08 && (size_t)busy_retries < max_busy_retries) + { + uint16_t delay_ms = HIDPP20_FRAME_END_BUSY_BACKOFF_MS[busy_retries]; + busy_retries++; + LOG_TRACE("%s FrameEnd BUSY, re-sending (retry %d, delay %ums)", + LOG_TAG, busy_retries, delay_ms); + std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms)); + SendMessage(perkey_idx, FN_8081_FRAME_END, data, sizeof(data)); + continue; + } + + /* Non-BUSY error or out of retries — frame committed=false */ + LOG_DEBUG("%s FrameEnd error 0x%02X (retries=%d)", + LOG_TAG, err_code, busy_retries); + break; + } + + /* Error for an unrelated request — discard and keep reading */ + continue; + } + + /*-----------------------------------------------------*\ + | Discard frames that aren't from our perkey feature. | + \*-----------------------------------------------------*/ + if(resp_feat != perkey_idx) + { + continue; + } + + uint8_t resp_func_hi = resp_func & 0xF0; + + /*-----------------------------------------------------*\ + | FrameEnd response — terminator. | + \*-----------------------------------------------------*/ + if(resp_func_hi == FN_8081_FRAME_END) + { + result.frame_end_acked = true; + break; + } + + /*-----------------------------------------------------*\ + | Per-key write response. Match against the next | + | outstanding entry by function high nibble. If the | + | head doesn't match (a write was dropped on the wire | + | or the firmware is responding out of order), skip | + | unmatched heads — those entries' zones will be left | + | out of acked_zones and treated as uncommitted. | + \*-----------------------------------------------------*/ + while(outstanding_idx < outstanding_writes.size() && + outstanding_writes[outstanding_idx].function != resp_func_hi) + { + outstanding_idx++; + } + + if(outstanding_idx >= outstanding_writes.size()) + { + /* No matching outstanding write — stale or unexpected response */ + continue; + } + + for(uint8_t z : outstanding_writes[outstanding_idx].zone_ids) + { + result.acked_zones.push_back(z); + } + outstanding_idx++; + } + + /*---------------------------------------------------------*\ + | Deep-sleep detection. If FrameEnd failed (no ACK) while | + | we're in the SLEEPING state, the device may have finished | + | its firmware fade and entered deep sleep. Track | + | consecutive failures; once we hit the threshold, suppress | + | further frame sends until Wake() clears the flag. | + | | + | A successful ACK resets the counter — transient BUSY | + | bursts during the fade don't accumulate. | + \*---------------------------------------------------------*/ + if(result.frame_end_acked) + { + consecutive_frame_end_failures.store(0); + } + else if(power_state == HIDPP20_POWER_SLEEPING) + { + int failures = consecutive_frame_end_failures.fetch_add(1) + 1; + + if(failures >= HIDPP20_DEEP_SLEEP_FAILURE_THRESHOLD && !deep_sleep.load()) + { + deep_sleep.store(true); + LOG_DEBUG("%s Device entered deep sleep (%d consecutive FrameEnd failures)", + LOG_TAG, failures); + } + } + + outstanding_writes.clear(); + return result; +} + +/*---------------------------------------------------------*\ +| Zone effects (0x8071 / 0x8070) | +\*---------------------------------------------------------*/ + +void LogitechHIDPP20Controller::SetZoneEffect + ( + uint8_t cluster_idx, + uint8_t effect_idx, + uint16_t effect_id, + unsigned char r, + unsigned char g, + unsigned char b, + uint16_t period, + unsigned char brightness, + unsigned char direction, + bool persist + ) +{ + if(caps.idx_rgb_effects == 0 || !device_online.load()) + { + return; + } + + /*---------------------------------------------------------*\ + | SetEffectByIndex (fn1 on 0x8071, fn3 on 0x8070) | + | 0x8071/0x0600: [cluster, effect_idx, 10-byte params, | + | persist at [12]] | + | 0x8070: [zone, effect_idx, 10-byte params, | + | persist at [12] (Bit 2-3 Power, | + | Bit 1-0 Persistence)] | + \*---------------------------------------------------------*/ + uint8_t data[16]; + memset(data, 0, sizeof(data)); + + data[0] = cluster_idx; + data[1] = effect_idx; + + /*----------------------------------------------------------*\ + | Build 10-byte params (data[2..11]) per effect type | + | Layouts from protocol docs and observed wire captures | + \*----------------------------------------------------------*/ + switch(effect_id) + { + case 0x0001: // Static + data[2] = r; + data[3] = g; + data[4] = b; + /*-----------------------------------------------------*\ + | "Fixed color" marker — only set when there's an | + | actual color. All-black means "Off / pass-through to | + | per-key buffer", which uses byte 5 = 0x00 instead. | + \*-----------------------------------------------------*/ + if(r != 0 || g != 0 || b != 0) + { + data[5] = 0x02; + } + break; + + case 0x000A: // Breathing + /*------------------------------------------------------*\ + | Effect param layout (10 bytes, indices into data[]): | + | data[2..4] = R, G, B | + | data[5..6] = periodHi, periodLo (BE16 milliseconds) | + | data[7] = 0 | + | data[8] = brightness 0..100 | + \*------------------------------------------------------*/ + data[2] = r; + data[3] = g; + data[4] = b; + data[5] = (period >> 8) & 0xFF; + data[6] = period & 0xFF; + data[8] = brightness; + break; + + case 0x0003: // Color Cycle / Spectrum + /*------------------------------------------------------*\ + | Effect param layout (10 bytes, indices into data[]): | + | data[7..8] = periodHi, periodLo (BE16 milliseconds) | + | data[9] = brightness 0..100 | + \*------------------------------------------------------*/ + data[7] = (period >> 8) & 0xFF; + data[8] = period & 0xFF; + data[9] = brightness; + break; + + case 0x0004: // Color Wave + data[3] = (period > 0) ? (uint8_t)(period / 100) : 50; + data[8] = 0x01; // pattern + data[9] = 0x00; // waveform + data[11] = 0x01; // direction + break; + + case 0x000B: // Ripple + data[2] = r; + data[3] = g; + data[4] = b; + data[6] = (period >> 8) & 0xFF; + data[7] = period & 0xFF; + break; + + case 0x0015: // Cycle (saturation variant) + /*------------------------------------------------------*\ + | Saturation-bearing variant of 0x0003. Param block | + | (10 bytes, indices into data[]): | + | data[3] = saturation 0..255 (hardcoded full) | + | data[8..9] = periodHi, periodLo (BE16 milliseconds) | + | data[10] = intensity 0..100 | + | Layout from Solaar LEDEffects 0x15 (saturation@1, | + | period@6, intensity@8 in the param block). | + \*------------------------------------------------------*/ + data[3] = 0xFF; + data[8] = (period >> 8) & 0xFF; + data[9] = period & 0xFF; + data[10] = brightness; + break; + + case 0x0016: // Wave (saturation variant) + /*------------------------------------------------------*\ + | Saturation-bearing variant of 0x0004. Param block: | + | data[3] = saturation 0..255 (hardcoded full) | + | data[8..9] = periodHi, periodLo (BE16 milliseconds) | + | data[10] = intensity 0..100 | + | data[11] = direction (Logitech wire value) | + | Layout from Solaar LEDEffects 0x16 (saturation@1, | + | period@6, intensity@8, direction@9). The caller maps | + | OpenRGB's 6 direction slots to the wire values. | + \*------------------------------------------------------*/ + data[3] = 0xFF; + data[8] = (period >> 8) & 0xFF; + data[9] = period & 0xFF; + data[10] = brightness; + data[11] = direction; + break; + + case 0x0017: // Ripple (saturation variant) + /*------------------------------------------------------*\ + | Saturation-bearing variant of 0x000B. Param block: | + | data[2..4] = R, G, B | + | data[5] = saturation 0..255 (hardcoded full) | + | data[8..9] = periodHi, periodLo (BE16 milliseconds) | + | Layout from Solaar LEDEffects 0x17 (color@0, | + | saturation@3, period@6). No intensity param. | + \*------------------------------------------------------*/ + data[2] = r; + data[3] = g; + data[4] = b; + data[5] = 0xFF; + data[8] = (period >> 8) & 0xFF; + data[9] = period & 0xFF; + break; + + default: // Unknown — best-effort + data[2] = r; + data[3] = g; + data[4] = b; + data[5] = (period >> 8) & 0xFF; + data[6] = period & 0xFF; + break; + } + + /*------------------------------------------------------*\ + | 16-byte payload for all pages; persist at byte[12]. | + | 0x8070 and 0x8071/0x0600 share the same byte position | + | per LogitechProtocolCommon setMode convention. | + \*------------------------------------------------------*/ + data[12] = persist ? 0x01 : 0x00; + + LOG_DEBUG("%s SetEffect cluster=%u idx=%u id=0x%04X " + "data=[%02X %02X %02X %02X %02X %02X %02X %02X " + "%02X %02X %02X %02X %02X %02X %02X %02X]", + LOG_TAG, cluster_idx, effect_idx, effect_id, + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7], + data[8], data[9], data[10], data[11], + data[12], data[13], data[14], data[15]); + + blankFAPmessage response; + SendAckedIntoFAP(caps.idx_rgb_effects, caps.fn_set_effect, + data, 16, response); +} + +/*---------------------------------------------------------*\ +| Feature 0x0620 Headset RGB Hostmode — static color write. | +| | +| Claim is sticky from SetHostMode(); this function only | +| writes colors + FrameEnd. Picks fn5 SetRgbZonesSingleValue| +| when all zones share a color, else fn2 | +| SetIndividualRgbZones. FrameEnd byte 0 is always 0x01 | +| (transient) — 0x02 was tested and does not work on G522 | +| firmware. | +\*---------------------------------------------------------*/ +void LogitechHIDPP20Controller::SetHeadsetRGBHostmodeColors + ( + const std::vector& zone_colors + ) +{ + if(caps.idx_headset_rgb_hostmode == 0 || !device_online.load()) + { + return; + } + + const std::vector& zones = caps.headset_rgb_hostmode_zone_ids; + if(zones.empty()) + { + return; + } + + /*---------------------------------------------------------*\ + | If fewer colors than zones, fill the tail with the last | + | provided color. If zero colors, nothing to write. | + \*---------------------------------------------------------*/ + if(zone_colors.empty()) + { + return; + } + + /*---------------------------------------------------------*\ + | Uniformity check: same color across every zone? | + \*---------------------------------------------------------*/ + RGBColor first = zone_colors[0]; + bool all_same = true; + for(size_t i = 1; i < zones.size(); i++) + { + RGBColor c = (i < zone_colors.size()) ? zone_colors[i] : zone_colors.back(); + if(c != first) + { + all_same = false; + break; + } + } + + uint8_t payload[16]; + size_t payload_len = 0; + uint8_t function = 0; + blankFAPmessage response; + + if(all_same) + { + /*------------------------------------------------------*\ + | fn5 SetRgbZonesSingleValue: [R, G, B, count, zones...] | + \*------------------------------------------------------*/ + function = FN_0620_SET_RGB_ZONES_SINGLE_VALUE; + payload[0] = RGBGetRValue(first); + payload[1] = RGBGetGValue(first); + payload[2] = RGBGetBValue(first); + payload[3] = (uint8_t)zones.size(); + + size_t n = zones.size(); + if(n > sizeof(payload) - 4) n = sizeof(payload) - 4; + for(size_t i = 0; i < n; i++) + { + payload[4 + i] = zones[i]; + } + payload_len = 4 + n; + } + else + { + /*------------------------------------------------------*\ + | fn2 SetIndividualRgbZones: [zone, R, G, B] × N | + | Each entry is 4 bytes; 16-byte payload fits 4 entries. | + \*------------------------------------------------------*/ + function = FN_0620_SET_INDIVIDUAL_RGB_ZONES; + size_t n = zones.size(); + if(n > sizeof(payload) / 4) n = sizeof(payload) / 4; + for(size_t i = 0; i < n; i++) + { + RGBColor c = (i < zone_colors.size()) ? zone_colors[i] : zone_colors.back(); + payload[i * 4 + 0] = zones[i]; + payload[i * 4 + 1] = RGBGetRValue(c); + payload[i * 4 + 2] = RGBGetGValue(c); + payload[i * 4 + 3] = RGBGetBValue(c); + } + payload_len = n * 4; + } + + SendAckedIntoFAP(caps.idx_headset_rgb_hostmode, function, + payload, payload_len, response); + + /*---------------------------------------------------------*\ + | fn6 FrameEnd — byte 0 = 0x01 (transient commit). Never | + | 0x00 (silently discarded) and never 0x02 (tested broken | + | on G522 firmware). | + \*---------------------------------------------------------*/ + uint8_t frame_end[4] = { 0x01, 0x00, 0x00, 0x00 }; + SendAckedIntoFAP(caps.idx_headset_rgb_hostmode, FN_0620_FRAME_END, + frame_end, sizeof(frame_end), response); + + LOG_TRACE("%s 0x0620 wrote %zu zone(s), fn=0x%02X, FrameEnd[0x01]", + LOG_TAG, zones.size(), function); +} + +/*---------------------------------------------------------*\ +| Power management (idle/dim/sleep/wake) | +| | +| Matches Solaar's RGBPowerManager state machine: | +| ACTIVE → DIMMING → IDLE → SLEEPING | +| | +| Uses firmware onUserActivity events from 0x8071 for | +| idle/active detection. SW control flags cycle: | +| 7 (init) → 5 (active, monitor idle) → | +| 3 (idle, monitor active) → 5 (wake) | +\*---------------------------------------------------------*/ + +void LogitechHIDPP20Controller::SetRepaintCallback(std::function repaint) +{ + request_repaint_fn = repaint; +} + +void LogitechHIDPP20Controller::SetReapplyActiveModeCallback(std::function cb) +{ + reapply_active_mode_fn = cb; +} + +void LogitechHIDPP20Controller::SetRegisterCallback(std::function cb) +{ + register_controller_fn = cb; +} + +HIDPP20PowerState LogitechHIDPP20Controller::GetPowerState() const +{ + return power_state; +} + +int LogitechHIDPP20Controller::GetDimBrightness() const +{ + return dim_brightness_pct.load(); +} + +bool LogitechHIDPP20Controller::HasBridge() const +{ + return transport.bridge_feat_idx != 0; +} + +bool LogitechHIDPP20Controller::IsOnline() const +{ + return device_online.load(); +} + +bool LogitechHIDPP20Controller::IsDeepSleep() const +{ + return deep_sleep.load(); +} + +void LogitechHIDPP20Controller::ReprobeSubDevice() +{ + /*----------------------------------------------------------*\ + | Called by power thread when a sub-device connects through | + | the Centurion bridge. The reader thread is running, so all | + | commands go through SendAndRead → ReadFromQueue. | + | | + | We clear the sub-device feature cache and re-discover | + | everything. The bridge_feat_idx and dongle name are kept. | + \*----------------------------------------------------------*/ + LOG_DEBUG("%s Re-probing sub-device through bridge", LOG_TAG); + + /*----------------------------------------------------------*\ + | Give the sub-device a moment to settle after connection | + | before sending commands through the bridge. | + \*----------------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + FlushResponseQueue(); + + /*----------------------------------------------------------*\ + | Clear sub-device feature map but keep bridge index. | + | This forces fresh lookups through the bridge. | + \*----------------------------------------------------------*/ + caps.feature_map.clear(); + caps.feature_map_complete = false; + caps.has_zone_effects = false; + caps.has_perkey = false; + caps.has_effect_cards = false; + caps.effect_card_template[0] = 0; + caps.effect_card_template[1] = 0; + caps.zone_clusters.clear(); + caps.perkey_zone_ids.clear(); + caps.idx_rgb_effects = 0; + caps.idx_perkey_v2 = 0; + caps.idx_perkey_v1 = 0; + caps.idx_profile_management = 0; + caps.idx_onboard_profiles = 0; + caps.idx_disable_keys_by_usage = 0; + caps.fn_set_effect = 0; + caps.fn_sw_control = 0; + caps.fn_pwr_config = 0; + caps.fn_pwr_mode = 0; + caps.has_power_mgmt = false; + caps.sw_control_simple = false; + caps.nv_sleep_ramp_known = false; + caps.nv_sleep_ramp_enabled = false; + caps.nv_sleep_ramp_seconds = 0; + + /*---------------------------------------------------------*\ + | Re-populate feature map. CenturionFeatureSet is always at | + | index 1 on the sub-device. | + \*---------------------------------------------------------*/ + EnumerateFeatures(1); + + if(!caps.feature_map_complete) + { + LOG_DEBUG("%s Sub-device not reachable after connect event", LOG_TAG); + caps.feature_map_complete = true; + return; + } + + /*---------------------------------------------------------*\ + | Discover sub-device identity if not already known. | + \*---------------------------------------------------------*/ + if(caps.device_name.empty() || caps.device_name.find("PRO X 2") == std::string::npos) + { + /*------------------------------------------------------*\ + | Dongle may have a sysfs-derived name; get real name | + | from the sub-device now that it's reachable. | + \*------------------------------------------------------*/ + std::string old_name = caps.device_name; + DiscoverDeviceName(); + + if(caps.device_name != old_name) + { + log_tag = "[LogitechHID++ " + caps.device_name + "]"; + } + } + + DiscoverDeviceType(); + DiscoverFirmwareInfo(); + + /*---------------------------------------------------------*\ + | Discover RGB features | + \*---------------------------------------------------------*/ + caps.idx_profile_management = GetFeatureIndex(HIDPP20_FEAT_PROFILE_MANAGEMENT); + caps.idx_onboard_profiles = GetFeatureIndex(HIDPP20_FEAT_ONBOARD_PROFILES); + caps.idx_disable_keys_by_usage = GetFeatureIndex(HIDPP20_FEAT_DISABLE_KEYS_BY_USAGE); + + DiscoverRGBEffects(); + if(caps.idx_rgb_effects == 0) + { + DiscoverHeadsetRGBHostmode(); + } + DiscoverPerKeyZones(); + DiscoverKeyboardLayout(); + + if(!caps.has_zone_effects && !caps.has_perkey) + { + LOG_DEBUG("%s Sub-device has no RGB features", LOG_TAG); + return; + } + + LOG_INFO("%s Sub-device probed: zones=%zu perkey=%zu", + LOG_TAG, caps.zone_clusters.size(), caps.perkey_zone_ids.size()); + + /*---------------------------------------------------------*\ + | Create and register RGBController for the sub-device. | + \*---------------------------------------------------------*/ + Initialize(); + + if(register_controller_fn) + { + RGBController_LogitechHIDPP20* rgb = new RGBController_LogitechHIDPP20(this); + register_controller_fn(rgb); + + LOG_INFO("%s Registered RGB controller for sub-device", LOG_TAG); + } +} + +void LogitechHIDPP20Controller::ReconnectDevice() +{ + /*----------------------------------------------------------*\ + | Called by power thread when a WirelessStatus reconnect | + | event arrives. Race the firmware boot animation: hammer | + | the SW-control claim + per-key push on a fast-backoff | + | schedule until the claim ACKs (matches the vendor app, | + | which lands control in ~50ms). | + | | + | Previously this code split work across the two firmware | + | events (reconnect=1/config_needed=1 then config_needed=0) | + | and only pushed once per event. The first push raced the | + | boot, the second only fired after the animation finished, | + | and a failed claim left sw_control_claimed=true so the | + | 10s firmware watchdog dropped us back to onboard mode. | + \*----------------------------------------------------------*/ + LOG_DEBUG("%s Reconnecting device", LOG_TAG); + + FlushResponseQueue(); + + bool first_event = !device_online.load(); + + if(first_event) + { + device_online.store(true); + consecutive_timeouts.store(0); + frame_counter = 0; + + { + std::lock_guard lock(power_mutex); + dim_brightness_pct.store(100); + power_state = HIDPP20_POWER_ACTIVE; + } + } + + /*----------------------------------------------------------*\ + | Always invalidate any stale claim so the loop below runs | + | the full claim sequence on each attempt until it sticks. | + \*----------------------------------------------------------*/ + sw_control_claimed = false; + sw_control_needs_upgrade_to_5 = false; + retry_paint_deadline_.store(std::chrono::steady_clock::time_point{}); + retry_paint_attempt_.store(0); + + bool claimed = false; + size_t attempt_count = sizeof(HIDPP20_RECLAIM_BACKOFF_MS) / sizeof(uint16_t); + + for(size_t i = 0; i < attempt_count; i++) + { + if(HIDPP20_RECLAIM_BACKOFF_MS[i] > 0) + { + std::this_thread::sleep_for( + std::chrono::milliseconds(HIDPP20_RECLAIM_BACKOFF_MS[i])); + } + + if(!device_online.load()) + { + return; + } + + if(reapply_active_mode_fn && reapply_active_mode_fn()) + { + claimed = true; + LOG_INFO("%s Device reconnected — SW control reclaimed (attempt %zu/%zu)", + LOG_TAG, i + 1, attempt_count); + break; + } + } + + if(!claimed) + { + LOG_WARNING("%s Device reconnected — SW control reclaim failed after %zu attempts", + LOG_TAG, attempt_count); + } + + if(first_event && caps.has_power_mgmt) + { + ReadFirmwareTimers(); + ReadNvSleepRampConfig(); + } +} + +void LogitechHIDPP20Controller::RediscoverFeatures() +{ + /*---------------------------------------------------------*\ + | Clear the cached HID++ feature map and all index/ | + | function-byte derivations, then re-run the standard | + | discovery sequence on the current hid_device handle. | + | | + | Used in two situations: | + | - FullReprobe (a previously-unreachable device just | + | became reachable; reader thread is already running) | + | - SwapHIDHandle (USB/wireless path migration; the new | + | path's HID++ feature map may have completely | + | different feature indices than the old one — observed | + | on the G515 LS TKL where wireless RGBEffects sits at | + | idx 0x09 but the USB path puts it elsewhere, causing | + | INVALID_FEATURE_INDEX (0x07) errors on every cached | + | idx_rgb_effects/fn_set_effect/etc. lookup). | + | | + | Caller is responsible for state that lives outside the | + | feature map (sw_control_claimed, frame counters, online | + | flag, threads). | + \*---------------------------------------------------------*/ + caps.feature_map.clear(); + caps.feature_map_complete = false; + caps.has_zone_effects = false; + caps.has_perkey = false; + caps.has_effect_cards = false; + caps.effect_card_template[0] = 0; + caps.effect_card_template[1] = 0; + caps.zone_clusters.clear(); + caps.perkey_zone_ids.clear(); + caps.idx_rgb_effects = 0; + caps.idx_perkey_v2 = 0; + caps.idx_perkey_v1 = 0; + caps.idx_wireless_status = 0; + caps.idx_profile_management = 0; + caps.idx_onboard_profiles = 0; + caps.idx_disable_keys_by_usage = 0; + caps.fn_set_effect = 0; + caps.fn_sw_control = 0; + caps.fn_pwr_config = 0; + caps.fn_pwr_mode = 0; + caps.has_power_mgmt = false; + caps.sw_control_simple = false; + caps.nv_sleep_ramp_known = false; + caps.nv_sleep_ramp_enabled = false; + caps.nv_sleep_ramp_seconds = 0; + + /*---------------------------------------------------------*\ + | idx_unified_battery lives outside caps (discovered lazily | + | by QueryExternalPower on first use) so it isn't cleared | + | by the caps reset above. Clear it here too so the next | + | QueryExternalPower call re-probes on the new path — the | + | old path's feature index may not exist, or may map to a | + | different feature entirely, on the new map. | + \*---------------------------------------------------------*/ + idx_unified_battery = 0; + last_power_raw = 0xFFFF; + + /*---------------------------------------------------------*\ + | Force ApplyPowerSavingProfile's dedup to re-emit its | + | "Idle management: ..." line on the next call so a path | + | transition always produces a full state confirmation in | + | the log, symmetric with the QueryExternalPower re-log. | + | Inverting ps_last_logged_external guarantees the boolean | + | comparison trips regardless of the current power state. | + \*---------------------------------------------------------*/ + ps_last_logged_pct = -1; + ps_last_logged_idle = -1; + ps_last_logged_sleep = -1; + ps_last_logged_external = !ps_on_external_power; + + /*---------------------------------------------------------*\ + | Standard HID++ features are looked up on-demand — no bulk | + | enumeration needed. Just re-discover everything. | + \*---------------------------------------------------------*/ + DiscoverDeviceName(); + log_tag = "[LogitechHID++ " + caps.device_name + "]"; + DiscoverDeviceType(); + DiscoverFirmwareInfo(); + + caps.idx_profile_management = GetFeatureIndex(HIDPP20_FEAT_PROFILE_MANAGEMENT); + caps.idx_onboard_profiles = GetFeatureIndex(HIDPP20_FEAT_ONBOARD_PROFILES); + caps.idx_wireless_status = GetFeatureIndex(HIDPP20_FEAT_WIRELESS_STATUS); + caps.idx_disable_keys_by_usage = GetFeatureIndex(HIDPP20_FEAT_DISABLE_KEYS_BY_USAGE); + + DiscoverRGBEffects(); + if(caps.idx_rgb_effects == 0) + { + DiscoverHeadsetRGBHostmode(); + } + DiscoverPerKeyZones(); + DiscoverKeyboardLayout(); +} + +void LogitechHIDPP20Controller::FullReprobe() +{ + /*----------------------------------------------------------*\ + | Called by power thread when a failed-probe device becomes | + | reachable. Like ReprobeSubDevice but for non-bridge | + | standard HID++ devices. Reader thread is running. | + \*----------------------------------------------------------*/ + LOG_DEBUG("%s Full re-probe of previously unreachable device", LOG_TAG); + + FlushResponseQueue(); + RediscoverFeatures(); + + if(!caps.has_zone_effects && !caps.has_perkey) + { + LOG_DEBUG("%s Device has no RGB features after re-probe", LOG_TAG); + return; + } + + LOG_INFO("%s Re-probe complete: zones=%zu perkey=%zu", + LOG_TAG, caps.zone_clusters.size(), caps.perkey_zone_ids.size()); + + device_online.store(true); + consecutive_timeouts.store(0); + watcher_mode.store(false); + Initialize(); + + if(register_controller_fn) + { + RGBController_LogitechHIDPP20* rgb = new RGBController_LogitechHIDPP20(this); + register_controller_fn(rgb); + LOG_INFO("%s Registered RGB controller after re-probe", LOG_TAG); + } +} + +void LogitechHIDPP20Controller::StartProbeWatcher() +{ + /*---------------------------------------------------------*\ + | Start reader + power threads in watcher mode for a device | + | that failed initial probe. The power thread periodically | + | retries IRoot until the device becomes reachable. | + \*---------------------------------------------------------*/ + if(reader_running) + { + return; + } + + watcher_mode.store(true); + device_online.store(false); + + pending_connection = 0; + reader_running = true; + reader_thread = new std::thread(&LogitechHIDPP20Controller::ReaderThreadFunc, this); + + power_thread_running = true; + power_thread = new std::thread(&LogitechHIDPP20Controller::PowerThreadFunc, this); + + LOG_DEBUG("%s Probe watcher started (retrying every 5s)", LOG_TAG); +} + +/*---------------------------------------------------------*\ +| ScanForDevice and GetCenturionSubDeviceName live in | +| LogitechHIDPP20Controller_Linux.cpp / | +| LogitechHIDPP20Controller_Windows_MacOS.cpp. Both are | +| the only parts of this controller that touch platform- | +| specific HID enumeration details (sysfs vs hidapi | +| serial_number). | +\*---------------------------------------------------------*/ + +void LogitechHIDPP20Controller::SwapHIDHandle + ( + hid_device* new_dev, + const std::string& new_path + ) +{ + /*----------------------------------------------------------*\ + | Stop reader/power threads, swap HID handle, restart. | + | Called from the power thread — we can't join ourselves, | + | so we stop the reader, swap, and flag for restart. | + | | + | Actually, we're called from the power thread's scan loop. | + | The reader thread is running. We need to: | + | 1. Stop the reader thread | + | 2. Close old handle, set new one | + | 3. Restart reader thread | + | 4. Reinit device | + | The power thread keeps running throughout. | + \*----------------------------------------------------------*/ + + /*----------------------------------------------------------*\ + | Stop reader thread | + \*----------------------------------------------------------*/ + reader_running = false; + + if(reader_thread && reader_thread->joinable()) + { + reader_thread->join(); + } + + delete reader_thread; + reader_thread = nullptr; + + /*---------------------------------------------------------*\ + | Swap HID handle | + \*---------------------------------------------------------*/ + hid_close(dev); + dev = new_dev; + location = new_path; + log_tag = "[LogitechHID++ " + caps.device_name + "]"; + + /*----------------------------------------------------------*\ + | Re-discover transport (might change between Centurion and | + | standard HID++ if device switches connection types). | + \*----------------------------------------------------------*/ + DiscoverTransport(); + + /*---------------------------------------------------------*\ + | Reset state for new connection | + \*---------------------------------------------------------*/ + FlushResponseQueue(); + device_online.store(true); + consecutive_timeouts.store(0); + sw_control_claimed = false; + sw_control_needs_upgrade_to_5 = false; + retry_paint_deadline_.store(std::chrono::steady_clock::time_point{}); + retry_paint_attempt_.store(0); + frame_counter = 0; + + { + std::lock_guard lock(power_mutex); + dim_brightness_pct.store(100); + power_state = HIDPP20_POWER_ACTIVE; + } + + /*----------------------------------------------------------*\ + | Restart reader thread on new handle | + \*----------------------------------------------------------*/ + reader_running = true; + reader_thread = new std::thread(&LogitechHIDPP20Controller::ReaderThreadFunc, this); + + /*--------------------------------------------------------------*\ + | Re-discover the HID++ feature map on the new path. The | + | wireless dongle path and the USB-direct path expose | + | DIFFERENT feature index assignments for the same logical | + | features — observed on the G515 LS TKL where wireless | + | RGBEffects sits at idx 0x09 but the USB path puts it | + | elsewhere. Without this rediscovery, the cached idx_* | + | values from the old path point at the wrong features on | + | the new one and every reclaim call returns HID++ error | + | 0x07 INVALID_FEATURE_INDEX. | + | | + | Must run after the reader thread is restarted (the | + | discovery uses queue-backed reads) and before the | + | reapply_active_mode_fn callback (so the SW control claim sees | + | correct indices). | + \*--------------------------------------------------------------*/ + RediscoverFeatures(); + + /*---------------------------------------------------------*\ + | Reinit device with colors | + \*---------------------------------------------------------*/ + if(reapply_active_mode_fn) + { + reapply_active_mode_fn(); + } + + if(caps.has_power_mgmt) + { + ReadFirmwareTimers(); + ReadNvSleepRampConfig(); + QueryExternalPower(); + ApplyPowerSavingProfile(); + } + + LOG_INFO("%s Device reconnected on new path — colors restored", LOG_TAG); +} + +void LogitechHIDPP20Controller::StartEventWatcher() +{ + /*------------------------------------------------------------*\ + | Start reader thread only (no power thread) to watch for | + | connection events on Centurion dongles without sub-devices. | + | When ConnectionStateChangedEvent arrives, pending_connection | + | is set for the power thread — but since there's no power | + | thread, we need a minimal processing loop. | + \*------------------------------------------------------------*/ + if(reader_running) + { + return; + } + + pending_connection = 0; + reader_running = true; + reader_thread = new std::thread(&LogitechHIDPP20Controller::ReaderThreadFunc, this); + + /*----------------------------------------------------------*\ + | Start power thread to process connection events. | + | It won't do dim/sleep (no RGB) but it handles | + | pending_connection for sub-device re-probe. | + \*----------------------------------------------------------*/ + power_thread_running = true; + power_thread = new std::thread(&LogitechHIDPP20Controller::PowerThreadFunc, this); + + LOG_DEBUG("%s Event watcher started (watching for sub-device)", LOG_TAG); +} + +void LogitechHIDPP20Controller::StartPowerManager() +{ + if(caps.idx_rgb_effects == 0 || !caps.has_power_mgmt) + { + return; + } + + if(reader_running) + { + return; + } + + ReadFirmwareTimers(); + ReadNvSleepRampConfig(); + ReadActiveProfileSector(); + + LogitechHIDPP20IdleSettings::instance()->load(); + QueryExternalPower(); + ApplyPowerSavingProfile(); + + /*-----------------------------------------------------------*\ + | Seed the periodic idle-settings poll clock so the first | + | tick of the power thread's 500ms re-read happens one | + | interval from now, not immediately (we just applied above). | + \*-----------------------------------------------------------*/ + last_idle_poll = std::chrono::steady_clock::now(); + + /*----------------------------------------------------------*\ + | Don't claim SW control here. The device runs its firmware | + | effect (or saved hardware profile) until DeviceUpdateLEDs | + | is called for the first time, at which point claim + push | + | happen atomically. | + | | + | Reader and power threads still start so we can detect | + | migration events (USB plug-in) and process activity events | + | once SW control is eventually claimed. | + \*----------------------------------------------------------*/ + power_state = HIDPP20_POWER_ACTIVE; + pending_activity = -1; + + reader_running = true; + reader_thread = new std::thread(&LogitechHIDPP20Controller::ReaderThreadFunc, this); + + power_thread_running = true; + power_thread = new std::thread(&LogitechHIDPP20Controller::PowerThreadFunc, this); + + LOG_DEBUG("%s Power manager started (idle=%us sleep=%us)", + LOG_TAG, idle_timeout_s, sleep_timeout_s); +} + +void LogitechHIDPP20Controller::StopPowerManager() +{ + if(!reader_running && !power_thread_running) + { + return; + } + + /*----------------------------------------------------------*\ + | Stop power thread first (it may be waiting on the queue) | + \*----------------------------------------------------------*/ + power_thread_running = false; + response_cv.notify_all(); + + if(power_thread && power_thread->joinable()) + { + power_thread->join(); + } + + delete power_thread; + power_thread = nullptr; + + /*----------------------------------------------------------*\ + | Then stop reader thread | + \*----------------------------------------------------------*/ + reader_running = false; + + if(reader_thread && reader_thread->joinable()) + { + reader_thread->join(); + } + + delete reader_thread; + reader_thread = nullptr; + + /*----------------------------------------------------------*\ + | Wake if we were dimmed/sleeping so Shutdown() can | + | cleanly release SW control. | + \*----------------------------------------------------------*/ + if(power_state != HIDPP20_POWER_ACTIVE) + { + Wake(); + } + + LOG_DEBUG("%s Power manager stopped", LOG_TAG); +} + +void LogitechHIDPP20Controller::ReaderThreadFunc() +{ + /*----------------------------------------------------------*\ + | Sole HID reader. NEVER sends commands — that would | + | deadlock (we'd wait on our own queue for the response). | + | Events are flagged via atomic for the power thread. | + \*----------------------------------------------------------*/ + while(reader_running.load()) + { + uint8_t feat = 0, func = 0; + uint8_t data[60] = {}; + int result = ReadHIDDirect(&feat, &func, data, sizeof(data), 50); + + if(result < 0) + { + /*--------------------------------------------------*\ + | HID read error — device handle is invalid (device | + | physically removed). Mark offline and sleep to | + | avoid spinning. | + \*--------------------------------------------------*/ + if(device_online.load()) + { + LOG_DEBUG("%s HID read error — device removed", LOG_TAG); + device_online.store(false); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + if(result > 0) + { + /*--------------------------------------------------*\ + | Check for firmware events first. | + | Events are flagged for the power thread and NOT | + | added to the response queue — they aren't command | + | responses and would pollute the queue. | + \*--------------------------------------------------*/ + if(caps.idx_rgb_effects != 0 && + feat == caps.idx_rgb_effects && + (func & 0xF0) == 0x10 && + (func & 0x0F) != HIDPP20_SW_ID) + { + pending_activity.store((int)data[0]); + continue; + } + + /*--------------------------------------------------*\ + | CentPPBridge event 0: ConnectionStateChangedEvent | + | Sub-device connected or disconnected from dongle. | + \*--------------------------------------------------*/ + if(transport.bridge_feat_idx != 0 && + feat == transport.bridge_feat_idx && + (func & 0xF0) == 0x00 && + (func & 0x0F) != HIDPP20_SW_ID) + { + /*--------------------------------------------------*\ + | ConnectionStateChangedEvent payload: | + | data[0] = ? (always 0 in observed packets) | + | data[1] = number of connected sub-devices | + | data[2] = ? | + | Connect: [00 01 00], Disconnect: [00 00 00] | + \*--------------------------------------------------*/ + uint8_t num_devices = data[1]; + LOG_DEBUG("%s Bridge ConnectionStateChanged: %d sub-device(s) (data: %02X %02X %02X)", + LOG_TAG, num_devices, data[0], data[1], data[2]); + /*-------------------------------------------------*\ + | Store +1 for connected, -1 for disconnected. | + | Power thread checks sign to decide action. | + \*-------------------------------------------------*/ + pending_connection.store(num_devices > 0 ? 1 : -1); + continue; + } + + /*---------------------------------------------------*\ + | Feature 0x1D4B event 0: WirelessStatus | + | Device reconnected after power cycle. | + | Use cached map lookup only — reader thread must | + | never send commands (deadlock risk). | + \*---------------------------------------------------*/ + { + std::map::const_iterator it = caps.feature_map.find(0x1D4B); + uint8_t ws_idx = (it != caps.feature_map.end()) ? it->second : 0; + + if(ws_idx != 0 && feat == ws_idx && + (func & 0xF0) == 0x00 && + (func & 0x0F) != HIDPP20_SW_ID) + { + uint8_t reconnect = data[0]; + uint8_t config_needed = data[1]; + + LOG_DEBUG("%s WirelessStatus event: reconnect=%d config_needed=%d", + LOG_TAG, reconnect, config_needed); + + /*--------------------------------------------------*\ + | Forward both events to the power thread. Each | + | call into ReconnectDevice runs the fast-backoff | + | reclaim loop, so the second event acts as a | + | belt-and-suspenders re-claim once the firmware | + | boot fully settles. | + \*--------------------------------------------------*/ + pending_connection.store(1); + + continue; + } + } + + /*---------------------------------------------------*\ + | HID++1.0 Device Connection notifications from the | + | Lightspeed receiver. sub_id 0x40 = Device | + | Disconnection, 0x41 = Device Connection Status. | + | Either one is a signal that the paired device's | + | preferred path just changed — typically because | + | the user plugged in (or unplugged) the USB cable | + | on the device itself. | + | | + | These arrive on the paired device's hidraw with | + | device_index=0x01 (not the receiver's own | + | endpoint) because the kernel dj-receiver driver | + | routes them to the device's virtual hidraw. We | + | see them because our reader is attached to that | + | hidraw. | + | | + | Flag a force path-check for the power thread. It | + | will run ScanForDevice(true) which bypasses the | + | device_online gate so the scan can find a | + | different-PID migration candidate even while the | + | current path hasn't failed yet. If no such | + | candidate exists (same path, false alarm), the | + | scan is a no-op. | + | | + | This is how we catch the wireless→USB transition: | + | the notification fires BEFORE the firmware fully | + | switches its data flow, giving us a window to | + | migrate proactively. The reverse direction | + | (USB→wireless) is already handled via the USB fd | + | becoming invalid on cable unplug. | + \*---------------------------------------------------*/ + if(feat == 0x40 || feat == 0x41) + { + /*--------------------------------------------------*\ + | Set 75 retries × 200ms = ~15 second window. The | + | keyboard's USB HID++ interface (page 0xFF00) can | + | take 10+ seconds to appear after the boot HID | + | interface — the first DJ notification fires when | + | the boot interface comes up, but hid_enumerate | + | won't return the HID++ interface until the kernel | + | finishes setting up all three interfaces. Until | + | the path-check clears, the device is dark/ | + | uncontrolled, so we poll at the same fast cadence | + | as offline recovery (200ms) to minimize how long | + | the user sees default firmware behavior. | + | | + | A subsequent DJ notification before timeout resets | + | the counter to 75, extending the retry window. | + \*--------------------------------------------------*/ + LOG_DEBUG("%s LogitechHID++1.0 connection notification sub_id=0x%02X " + "flags=0x%02X (path change — forcing scan retries)", + LOG_TAG, feat, func); + pending_path_check.store(75); + continue; + } + + /*----------------------------------------------------*\ + | Only queue responses to OUR commands. | + | Our commands use HIDPP20_SW_ID (0x0A) in the low | + | nibble. Firmware-generated messages (battery, | + | sync, etc.) use SW_ID 0 — drop those silently. | + | Error responses (feat=0xFF) are always queued. | + \*----------------------------------------------------*/ + if(feat != 0xFF && (func & 0x0F) != HIDPP20_SW_ID) + { + continue; + } + { + std::lock_guard lock(response_mutex); + HIDPP20RawMessage msg; + msg.feat = feat; + msg.func = func; + msg.result = result; + memcpy(msg.data, data, sizeof(msg.data)); + response_queue.push_back(msg); + } + response_cv.notify_all(); + } + } +} + +void LogitechHIDPP20Controller::PowerThreadFunc() +{ + /*----------------------------------------------------------*\ + | Handles power state machine and sends commands. | + | Reads responses from the queue (filled by reader thread). | + \*----------------------------------------------------------*/ + std::chrono::steady_clock::time_point last_probe_time = std::chrono::steady_clock::now(); + + while(power_thread_running.load()) + { + /*------------------------------------------------------*\ + | Watcher mode: device failed initial probe. Retry | + | IRoot every 5 seconds until device becomes reachable. | + \*------------------------------------------------------*/ + if(watcher_mode.load()) + { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + + if(now - last_probe_time >= std::chrono::seconds(5)) + { + last_probe_time = now; + + uint8_t test_idx = GetFeatureIndex(HIDPP20_FEAT_FEATURE_SET); + + if(test_idx != 0) + { + LOG_INFO("%s Device became reachable — initiating full probe", LOG_TAG); + FullReprobe(); + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + continue; + } + + /*------------------------------------------------------*\ + | 1. Check for pending firmware events | + \*------------------------------------------------------*/ + int activity = pending_activity.exchange(-1); + + if(activity >= 0) + { + std::lock_guard lock(power_mutex); + OnUserActivity((uint8_t)activity); + } + + /*------------------------------------------------------*\ + | 1b. Check for connection state changes | + \*------------------------------------------------------*/ + int connection = pending_connection.exchange(0); + + if(connection > 0) + { + if(HasBridge()) + { + ReprobeSubDevice(); + } + else + { + ReconnectDevice(); + } + } + else if(connection < 0) + { + LOG_DEBUG("%s Device disconnected", LOG_TAG); + device_online.store(false); + } + + /*-------------------------------------------------------*\ + | 1c. Reactive scan for connection migration. | + | | + | Three modes feed this loop: | + | a) device_online == true, no path-check pending | + | -> 2s idle interval, scan is a no-op | + | b) device_online == false | + | -> 200ms fast interval, scan tries to reclaim | + | c) pending_path_check > 0 (DJ notification fired) | + | -> 200ms fast interval, force-scan bypasses | + | the online gate to find a different-PID | + | migration candidate; counter decrements per | + | failed attempt and clears on success | + | | + | The retry counter exists because the keyboard's USB | + | HID++ interface can take 10+ seconds to enumerate | + | after the boot HID interface comes up — the first DJ | + | notification fires too early to find anything. 75 * | + | 200ms = ~15s window, plenty for the slowest observed | + | enumeration. | + \*-------------------------------------------------------*/ + if(!caps.unit_id.empty()) + { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + int path_retries = pending_path_check.load(); + bool online = device_online.load(); + + std::chrono::milliseconds interval = (online && path_retries == 0) + ? std::chrono::milliseconds(2000) + : std::chrono::milliseconds(200); + + if(now - last_probe_time >= interval) + { + last_probe_time = now; + + bool force = (path_retries > 0) || !online; + bool success = ScanForDevice(force); + + if(path_retries > 0) + { + if(success) + { + pending_path_check.store(0); + } + else + { + pending_path_check.fetch_sub(1); + } + } + } + } + + /*------------------------------------------------------*\ + | 2. Power management timing | + \*------------------------------------------------------*/ + { + std::lock_guard lock(power_mutex); + + switch(power_state) + { + case HIDPP20_POWER_DIMMING: + { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + if(now >= next_dim_time) + { + DimRampStep(); + next_dim_time = now + std::chrono::milliseconds(DIM_INTERVAL_MS); + } + break; + } + + case HIDPP20_POWER_IDLE: + /*---------------------------------------------------*\ + | Poll dim brightness target — if the user is | + | dragging the slider, ps_dim_target_pct updates | + | in-memory and we pick it up here on the next 50ms | + | tick without any callback/repaint chain. | + | | + | Gated on ps_dim_enabled: a profile (or the default | + | unconfigured fallback) can enter IDLE state via | + | the skip-dim path in OnUserActivity, and we must | + | not dim in that case — only sleep when the deadline | + | hits. | + \*---------------------------------------------------*/ + if(ps_dim_enabled && + dim_brightness_pct.load() != ps_dim_target_pct) + { + dim_brightness_pct.store(ps_dim_target_pct); + + if(request_repaint_fn) + { + request_repaint_fn(); + } + } + + if(sleep_timeout_s > 0 && ps_sleep_enabled && + std::chrono::steady_clock::now() >= sleep_deadline) + { + StartSleep(); + } + break; + + default: + break; + } + } + + /*------------------------------------------------------*\ + | Fast poll of idle settings + external-power flag. | + | QueryExternalPower is a single HID++ 0x1004 GetStatus | + | call — cheap on wire and lets ApplyPowerSavingProfile | + | pick between the on_battery and plugged_in profiles | + | within half a second of a power-source transition. | + | The idle-settings reload itself is purely in-memory. | + \*------------------------------------------------------*/ + if(caps.has_power_mgmt) + { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + if(now - last_idle_poll >= std::chrono::milliseconds(500)) + { + last_idle_poll = now; + QueryExternalPower(); + ApplyPowerSavingProfile(); + } + } + + /*------------------------------------------------------*\ + | Fire any pending retry-paint whose deadline has come | + | due. The callback runs DeviceUpdateLEDs on this | + | thread's context, not recursively inside another call. | + \*------------------------------------------------------*/ + TickRetryPaintIfPending(); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } +} + +bool LogitechHIDPP20Controller::IsCurrentlyWireless() const +{ + return wireless; +} + +bool LogitechHIDPP20Controller::QueryWirelessStatus() +{ + if(caps.idx_wireless_status == 0) + { + LOG_DEBUG("%s QueryWirelessStatus: feature not present", LOG_TAG); + return false; + } + + uint8_t send_data[1] = {0}; + uint8_t recv_data[16] = {}; + uint8_t hidpp_err = 0; + + int result = SendAcked(caps.idx_wireless_status, 0, + send_data, 0, recv_data, sizeof(recv_data), + HIDPP20_POLICY_PROBE, &hidpp_err); + + LOG_DEBUG("%s QueryWirelessStatus: result=%d err=0x%02X " + "data=[%02X %02X %02X %02X %02X %02X]", + LOG_TAG, result, hidpp_err, + recv_data[0], recv_data[1], recv_data[2], + recv_data[3], recv_data[4], recv_data[5]); + + if(result <= 0) + { + return false; + } + + return true; +} + +bool LogitechHIDPP20Controller::QueryExternalPower() +{ + /*----------------------------------------------------------*\ + | Query HID++ 2.0 feature 0x1004 (UnifiedBattery) fn1 | + | GetStatus and determine whether the device is drawing | + | external power. | + | | + | Response layout: | + | byte 2: Charging Status | + | 0 = Discharging | + | 1 = Charging (wired) | + | 2 = Charging (slow) | + | 3 = Complete | + | 4 = Error | + | 5 = Wireless Charging | + | byte 3: External Power Status | + | 0 = no external power | + | non-zero = external power present | + | | + | We consider the device externally powered if EITHER byte | + | is non-zero: some devices leave byte 3 at 0 whenever they | + | are actively charging and rely on byte 2 alone to signal | + | the wired state. The pre-refactor QueryOnBattery used the | + | same OR semantic (expressed from the on-battery side) and | + | was known to work across the Logitech lineup. | + | | + | Updates ps_on_external_power and returns the new value. | + | On failure returns the cached value without touching it. | + \*----------------------------------------------------------*/ + if(caps.idx_rgb_effects == 0 || !caps.has_power_mgmt) + { + return ps_on_external_power; + } + + if(idx_unified_battery == 0) + { + idx_unified_battery = GetFeatureIndex(HIDPP20_FEAT_UNIFIED_BATTERY, + HIDPP20_POLICY_PROBE); + + if(idx_unified_battery == 0) + { + /*-----------------------------------------------------*\ + | Device doesn't expose UnifiedBattery. Wired-only | + | devices (no battery) report the feature absent; we | + | treat them as permanently externally powered. | + \*-----------------------------------------------------*/ + ps_on_external_power = true; + return ps_on_external_power; + } + } + + uint8_t send_data[1] = {0}; + uint8_t recv_data[16] = {}; + + int result = SendAcked(idx_unified_battery, 0x10, + send_data, 0, recv_data, sizeof(recv_data), + HIDPP20_POLICY_PROBE); + + if(result <= 0) + { + LOG_TRACE("%s QueryExternalPower: GetStatus failed (result=%d) — using cached", + LOG_TAG, result); + return ps_on_external_power; + } + + uint8_t charge_status = recv_data[2]; + uint8_t external_power = recv_data[3]; + + ps_on_external_power = (charge_status != 0) || (external_power != 0); + + uint16_t raw = ((uint16_t)charge_status << 8) | external_power; + if(raw != last_power_raw) + { + last_power_raw = raw; + LOG_TRACE("%s QueryExternalPower: charge_status=%u external_power=%u -> %s", + LOG_TAG, charge_status, external_power, + ps_on_external_power ? "external" : "battery"); + } + + return ps_on_external_power; +} + +void LogitechHIDPP20Controller::ApplyPowerSavingProfile() +{ + /*----------------------------------------------------------*\ + | Re-read the JSON every invocation. This is a cheap | + | in-memory SettingsManager hash lookup + a handful of | + | field copies — safe to do on every 500ms power-thread | + | tick. Any write from the plugin (or a manual JSON edit) | + | therefore applies within one poll interval without any | + | cross-boundary signalling. | + \*----------------------------------------------------------*/ + LogitechHIDPP20IdleSettings* settings = LogitechHIDPP20IdleSettings::instance(); + settings->load(); + + bool prev_dim = ps_dim_enabled; + bool prev_sleep = ps_sleep_enabled; + + /*----------------------------------------------------------*\ + | Start from the firmware-timer baseline. Both the | + | configured and unconfigured paths return to these if | + | they don't explicitly override, so a profile that sets | + | idle_timeout_s does not leave a stale value behind after | + | the user resets to an empty config. | + \*----------------------------------------------------------*/ + idle_timeout_s = fw_idle_timeout_s; + sleep_timeout_s = fw_sleep_timeout_s; + + if(!settings->isConfigured()) + { + /*---------------------------------------------------------*\ + | Unconfigured: no plugin in use. We still hold SW control | + | so firmware will NOT dim or sleep autonomously — it only | + | emits idle events and expects the host to act. Run a | + | basic default profile ourselves: no dim on idle (OpenRGB | + | users generally expect lights to stay on), but still go | + | to sleep at the firmware-configured timeout. | + \*---------------------------------------------------------*/ + ps_dim_enabled = false; + ps_dim_target_pct = DIM_TARGET_PCT; + ps_sleep_enabled = true; + + /*------------------------------------------------------*\ + | Restore firmware defaults on the device if we | + | previously wrote custom values from a plugin profile. | + \*------------------------------------------------------*/ + if(written_idle_s != fw_idle_timeout_s || written_sleep_s != fw_sleep_timeout_s) + { + WritePowerConfig(fw_idle_timeout_s, fw_sleep_timeout_s); + written_idle_s = fw_idle_timeout_s; + written_sleep_s = fw_sleep_timeout_s; + } + + if(prev_dim != ps_dim_enabled || prev_sleep != ps_sleep_enabled || + ps_last_logged_pct != ps_dim_target_pct || + ps_last_logged_idle != (int)idle_timeout_s || + ps_last_logged_sleep != (int)sleep_timeout_s || + ps_last_logged_external != ps_on_external_power) + { + ps_last_logged_pct = ps_dim_target_pct; + ps_last_logged_idle = idle_timeout_s; + ps_last_logged_sleep = sleep_timeout_s; + ps_last_logged_external = ps_on_external_power; + LOG_DEBUG("%s Idle management: defaults (dim=off, firmware sleep=%us)", + LOG_TAG, sleep_timeout_s); + } + return; + } + + /*---------------------------------------------------------*\ + | Configured: pick the active profile based on whether the | + | device is currently externally powered. ps_on_external_ | + | power is refreshed by QueryExternalPower() on the same | + | 500 ms power-thread poll that calls us. | + \*---------------------------------------------------------*/ + const LogitechHIDPP20IdleProfile& profile = ps_on_external_power + ? settings->pluggedIn() + : settings->onBattery(); + + ps_dim_enabled = profile.dim_when_idle; + ps_dim_target_pct = profile.dim_when_idle ? profile.dim_brightness : DIM_TARGET_PCT; + ps_sleep_enabled = profile.allow_sleep; + + if(profile.dim_when_idle) + { + idle_timeout_s = (uint16_t)profile.idle_timeout_s; + } + /* else: idle_timeout_s stays at fw_idle_timeout_s from above */ + + if(profile.allow_sleep) + { + sleep_timeout_s = (uint16_t)profile.sleep_timeout_s; + } + else + { + /*------------------------------------------------------*\ + | Signal "don't sleep" to the state machine. The IDLE | + | branch of PowerThreadFunc gates on sleep_timeout_s>0. | + \*------------------------------------------------------*/ + sleep_timeout_s = 0; + } + + /*----------------------------------------------------------*\ + | Write our timer values to the device RAM so the firmware's | + | idle detection aligns with our host-side state machine. | + | Only writes when values actually change to avoid spamming | + | the bus on every 500ms poll tick. | + \*----------------------------------------------------------*/ + if(idle_timeout_s != written_idle_s || sleep_timeout_s != written_sleep_s) + { + WritePowerConfig(idle_timeout_s, sleep_timeout_s); + written_idle_s = idle_timeout_s; + written_sleep_s = sleep_timeout_s; + } + + if(prev_dim != ps_dim_enabled || prev_sleep != ps_sleep_enabled || + ps_last_logged_pct != ps_dim_target_pct || + ps_last_logged_idle != (int)idle_timeout_s || + ps_last_logged_sleep != (int)sleep_timeout_s || + ps_last_logged_external != ps_on_external_power) + { + ps_last_logged_pct = ps_dim_target_pct; + ps_last_logged_idle = idle_timeout_s; + ps_last_logged_sleep = sleep_timeout_s; + ps_last_logged_external = ps_on_external_power; + LOG_DEBUG("%s Idle management: power=%s dim=%s(%d%%) idle=%us sleep=%s(%us)", + LOG_TAG, + ps_on_external_power ? "external" : "battery", + ps_dim_enabled ? "on" : "off", ps_dim_target_pct, + idle_timeout_s, + ps_sleep_enabled ? "on" : "off", sleep_timeout_s); + } +} + +void LogitechHIDPP20Controller::FlushResponseQueue() +{ + std::lock_guard lock(response_mutex); + response_queue.clear(); +} + +void LogitechHIDPP20Controller::DispatchEvent + ( + uint8_t feat, + uint8_t func, + const uint8_t* data + ) +{ + if(caps.idx_rgb_effects == 0 || data == nullptr) + { + return; + } + + /*------------------------------------------------------------*\ + | onUserActivity = event 1 on RGB Effects (0x8071) | + | Event function byte: (1 << 4) | fw_swid | + | Our commands use HIDPP20_SW_ID (0x0A); firmware events use | + | a different sw_id (typically 0). | + \*------------------------------------------------------------*/ + if(feat == caps.idx_rgb_effects && + (func & 0xF0) == 0x10 && + (func & 0x0F) != HIDPP20_SW_ID) + { + OnUserActivity(data[0]); + } +} + +void LogitechHIDPP20Controller::OnUserActivity(uint8_t activity_type) +{ + /*----------------------------------------------------------*\ + | power_mutex must already be held by the caller. | + \*----------------------------------------------------------*/ + + if(activity_type == 0) + { + /*------------------------------------------------------*\ + | IDLE event — firmware detected inactivity. | + | Only act if we're currently ACTIVE. | + | Firmware sends a burst of ~8 events; ignore repeats. | + \*------------------------------------------------------*/ + if(power_state != HIDPP20_POWER_ACTIVE) + { + return; + } + + if(!ps_dim_enabled && !ps_sleep_enabled) + { + return; + } + + LOG_DEBUG("%s onUserActivity: IDLE — starting dim", LOG_TAG); + + /*------------------------------------------------------*\ + | Flush stale per-key ACKs before sending commands | + \*------------------------------------------------------*/ + FlushResponseQueue(); + + /*------------------------------------------------------*\ + | Switch to flags=3 (ZONE|POWER): release EFFECT to | + | firmware, monitor for user activity (keypress). | + \*------------------------------------------------------*/ + SetSWControl(3, 3); + + if(!ps_dim_enabled) + { + power_state = HIDPP20_POWER_IDLE; + + uint16_t sleep_delay = (sleep_timeout_s > idle_timeout_s) + ? (sleep_timeout_s - idle_timeout_s) : 0; + sleep_deadline = std::chrono::steady_clock::now() + + std::chrono::seconds(sleep_delay); + + LOG_DEBUG("%s Dim disabled — skipping to IDLE (sleep in %us)", + LOG_TAG, sleep_delay); + } + else + { + StartDimRamp(); + } + } + else + { + /*------------------------------------------------------*\ + | ACTIVE event — user resumed typing. | + | Only act if we're NOT already active. | + \*------------------------------------------------------*/ + if(power_state == HIDPP20_POWER_ACTIVE) + { + return; + } + + LOG_DEBUG("%s onUserActivity: ACTIVE — waking", LOG_TAG); + Wake(); + } +} + +void LogitechHIDPP20Controller::StartDimRamp() +{ + /*----------------------------------------------------------*\ + | Start the brightness ramp from 100% to DIM_TARGET_PCT. | + | The actual dimming happens in DeviceUpdateLEDs — it reads | + | dim_brightness_pct and scales the color buffer output. | + | This is our own host-side animation, independent of the | + | firmware's sleep-ramp timer. | + \*----------------------------------------------------------*/ + dim_step = 0; + next_dim_time = std::chrono::steady_clock::now(); + power_state = HIDPP20_POWER_DIMMING; + + LOG_DEBUG("%s Dim ramp started (100%% -> %d%%)", LOG_TAG, ps_dim_target_pct); +} + +void LogitechHIDPP20Controller::DimRampStep() +{ + /*----------------------------------------------------------*\ + | power_mutex must already be held by the caller. | + | Adjusts brightness and requests a repaint so | + | DeviceUpdateLEDs pushes the dimmed colors. | + \*----------------------------------------------------------*/ + if(power_state != HIDPP20_POWER_DIMMING) + { + return; + } + + dim_step++; + + int target = ps_dim_target_pct; + int brightness = 100 - ((100 - target) * dim_step / DIM_STEPS); + + if(brightness < target) + { + brightness = target; + } + + dim_brightness_pct.store(brightness); + + /*----------------------------------------------------------*\ + | Request repaint so DeviceUpdateLEDs applies the new | + | brightness. For animations this is redundant (the | + | animation loop already calls it), but for static colors | + | this is the only way to push the dimmed output. | + | | + | Do NOT bump init_generation here — that would clear | + | sent_colors and make the next DeviceUpdateLEDs treat the | + | frame as a first-push, firing the SetZoneEffect(0xFF, | + | static black, persist=true) prep call. On mice that flash | + | as a brief black-out per dim step. Delta tracking already | + | handles the changed brightness correctly: snapshot is the | + | scaled output, sent_colors holds the previously scaled | + | frame, and the diff catches every pixel that moved. | + \*----------------------------------------------------------*/ + if(request_repaint_fn) + { + request_repaint_fn(); + } + + /*----------------------------------------------------------*\ + | Check if dim ramp is complete | + \*----------------------------------------------------------*/ + if(dim_step >= DIM_STEPS) + { + power_state = HIDPP20_POWER_IDLE; + + /*------------------------------------------------------*\ + | Pull the sleep deadline forward by the firmware's | + | off-ramp duration so the firmware fade *ends* at the | + | user-configured sleep_timeout_s. Without this we'd | + | be late by nv_sleep_ramp_seconds (30s on G515). | + \*------------------------------------------------------*/ + uint16_t effective_sleep = sleep_timeout_s; + + if(caps.nv_sleep_ramp_known && caps.nv_sleep_ramp_enabled + && caps.nv_sleep_ramp_seconds < sleep_timeout_s) + { + effective_sleep -= caps.nv_sleep_ramp_seconds; + } + + uint16_t sleep_delay = (effective_sleep > idle_timeout_s) + ? (effective_sleep - idle_timeout_s) : 0; + + sleep_deadline = std::chrono::steady_clock::now() + + std::chrono::seconds(sleep_delay); + + LOG_DEBUG("%s Dim complete — IDLE (sleep in %us, effective_sleep=%us)", + LOG_TAG, sleep_delay, effective_sleep); + } +} + +void LogitechHIDPP20Controller::StartSleep() +{ + /*----------------------------------------------------------*\ + | SetRgbPowerMode(3) = firmware-managed fade to off. | + | The firmware handles the fade internally. | + | | + | Set power_state BEFORE sending the command so that | + | DeviceUpdateLEDs sees SLEEPING and stops pushing frames | + | before the sleep command hits the wire. Suppression is | + | the safe default: a write arriving after SetRgbPowerMode | + | (3) can otherwise wake the device and cancel the sleep. | + | Devices carrying FADE_ACCEPTS_WRITES opt out of | + | suppression — their firmware tolerates writes during the | + | fade. | + \*----------------------------------------------------------*/ + LOG_DEBUG("%s Entering sleep (SetRgbPowerMode 3)", LOG_TAG); + + power_state = HIDPP20_POWER_SLEEPING; + + uint8_t data[3] = { 0x01, 0x03, 0x00 }; + blankFAPmessage response; + int result = SendAckedIntoFAP(caps.idx_rgb_effects, caps.fn_pwr_mode, + data, 3, response); + + if(result <= 0) + { + LOG_DEBUG("%s SetRgbPowerMode(3) failed after retries (result=%d), " + "reverting to IDLE", LOG_TAG, result); + power_state = HIDPP20_POWER_IDLE; + } +} + +void LogitechHIDPP20Controller::Wake() +{ + /*----------------------------------------------------------*\ + | Called from OnUserActivity(1) when the firmware tells us | + | the device has seen user input. Works for DIMMING, IDLE, | + | and SLEEPING states uniformly — the only wrinkle is that | + | SLEEPING means we previously sent SetRgbPowerMode(3) to | + | initiate the fade, so we have to explicitly cancel it | + | with SetRgbPowerMode(1) first. | + | | + | Per the 0x8071 protocol lifecycle, a proper wake is: power | + | mode 1 (if we were sleeping), then SetSWControl(3,5) to | + | re-claim | + | rendering from the firmware's idle-monitor mode, then | + | re-push the current lighting state at full brightness. | + | | + | The re-push uses request_repaint_fn (DeviceUpdateLEDs) | + | NOT reapply_active_mode_fn (which re-runs the full claim | + | + per-key prep sequence). Wake is NOT a reconnect — the | + | device handle, feature map, SW control claim, and per-key | + | prep are all still intact. Re-running the claim would | + | briefly reset the zone effect layer and flash the firmware | + | default colors for ~50ms before per-key takes back over. | + | | + | power_mutex must already be held by the caller. | + \*----------------------------------------------------------*/ + HIDPP20PowerState prev = power_state; + + FlushResponseQueue(); + + if(prev == HIDPP20_POWER_SLEEPING) + { + /*------------------------------------------------------*\ + | Cancel the firmware's fade-to-off. SW control is still | + | ours; this is not a reconnect. The device stays on the | + | same hidraw handle, same feature map, same claim. | + \*------------------------------------------------------*/ + SetRGBPowerMode(1); + } + + SetSWControl(3, 5); + dim_brightness_pct.store(100); + deep_sleep.store(false); + consecutive_frame_end_failures.store(0); + power_state = HIDPP20_POWER_ACTIVE; + + LOG_DEBUG("%s Woke from state %d", LOG_TAG, prev); + + /*----------------------------------------------------------*\ + | Re-push the current lighting state at full brightness. | + | | + | We use request_repaint_fn (lightweight: just calls | + | DeviceUpdateLEDs) NOT reapply_active_mode_fn (heavyweight: | + | re-runs ClaimSWControlIfNeeded → SetOnboardMode → per-key | + | prep sequence → DeviceUpdateMode). On a wake-from-dim/idle | + | the device is still in host mode, SW control is still | + | claimed, and the per-key prep has already been established | + | — all we need is a fresh paint at restored brightness. | + | | + | The brightness was restored to 100% above | + | (dim_brightness_pct.store(100)), so DeviceUpdateLEDs will | + | apply the full-brightness multiplier to the snapshot. | + | Since sent_colors was recorded at the previous (dimmed) | + | brightness, the delta detects a change on every zone and | + | pushes a full frame naturally — no sent_colors.clear() | + | needed. | + | | + | ReapplyActiveMode (the heavyweight path) is reserved for | + | reconnects where the device was fully re-enumerated and | + | needs the complete claim + prep + mode re-establishment. | + \*----------------------------------------------------------*/ + wake_full_repaint_pending_.store(true); + + if(request_repaint_fn) + { + request_repaint_fn(); + } +} + +bool LogitechHIDPP20Controller::ConsumeWakeFullRepaint() +{ + return wake_full_repaint_pending_.exchange(false); +} + +void LogitechHIDPP20Controller::ReadFirmwareTimers() +{ + if(caps.idx_rgb_effects == 0 || !caps.has_power_mgmt) + { + return; + } + + /*----------------------------------------------------------*\ + | GetRgbPowerModeConfig (fn7, sub-function 0x00 = get) | + | Response: [echo], idle_hi, idle_lo, sleep_hi, sleep_lo | + \*----------------------------------------------------------*/ + uint8_t send_data[1] = { 0x00 }; + uint8_t recv_data[16] = {}; + + int result = SendAndReceive(caps.idx_rgb_effects, caps.fn_pwr_config, + send_data, 1, recv_data, sizeof(recv_data)); + + if(result > 0) + { + uint16_t idle = ((uint16_t)recv_data[3] << 8) | recv_data[4]; + uint16_t sleep = ((uint16_t)recv_data[5] << 8) | recv_data[6]; + + if(idle > 0) + { + idle_timeout_s = idle; + fw_idle_timeout_s = idle; + } + + if(sleep > 0) + { + sleep_timeout_s = sleep; + fw_sleep_timeout_s = sleep; + } + + written_idle_s = idle; + written_sleep_s = sleep; + + LOG_TRACE("%s Firmware timers: idle=%us sleep=%us", LOG_TAG, idle_timeout_s, sleep_timeout_s); + } + else + { + written_idle_s = idle_timeout_s; + written_sleep_s = sleep_timeout_s; + + LOG_DEBUG("%s Failed to read firmware timers, using defaults (idle=%us sleep=%us)", + LOG_TAG, idle_timeout_s, sleep_timeout_s); + } +} + +void LogitechHIDPP20Controller::ReadNvSleepRampConfig() +{ + /*----------------------------------------------------------*\ + | RGBEffects fn3 NV_CONFIG (0x30) read of capability 0x0020 | + | (Off Ramp / Sleep Transition). | + | | + | Wire format (matches observed wire capture): | + | request: short msg, data = [0x00, cap_hi, cap_lo] | + | where 0x00 = sub-function GET | + | response: long msg, data = [echo (3 bytes), enabled, | + | ramp_seconds, ...] | + | | + | G515 default observed from vendor app: enabled=0x01, | + | seconds=0x1E (= 30 seconds dim ramp before sleep). | + \*----------------------------------------------------------*/ + if(caps.idx_rgb_effects == 0 || !caps.has_power_mgmt) + { + return; + } + + uint8_t send_data[3] = { 0x00, 0x00, 0x20 }; + uint8_t recv_data[16] = {}; + + int result = SendAndReceive(caps.idx_rgb_effects, FN_8071_NV_CONFIG, + send_data, sizeof(send_data), + recv_data, sizeof(recv_data)); + + if(result <= 0) + { + LOG_DEBUG("%s NvConfig 0x0020 read failed (result=%d)", LOG_TAG, result); + return; + } + + if(recv_data[0] != 0x00 || recv_data[1] != 0x00 || recv_data[2] != 0x20) + { + LOG_DEBUG("%s NvConfig 0x0020 read: unexpected echo %02X %02X %02X", + LOG_TAG, recv_data[0], recv_data[1], recv_data[2]); + return; + } + + caps.nv_sleep_ramp_enabled = (recv_data[3] != 0); + caps.nv_sleep_ramp_seconds = recv_data[4]; + caps.nv_sleep_ramp_known = true; + + LOG_DEBUG("%s NvConfig 0x0020 (sleep ramp): enabled=%d ramp=%us " + "raw=[%02X %02X %02X %02X %02X %02X %02X %02X]", + LOG_TAG, + (int)caps.nv_sleep_ramp_enabled, + (unsigned)caps.nv_sleep_ramp_seconds, + recv_data[3], recv_data[4], recv_data[5], recv_data[6], + recv_data[7], recv_data[8], recv_data[9], recv_data[10]); +} + +void LogitechHIDPP20Controller::WritePowerConfig(uint16_t idle_s, uint16_t sleep_s) +{ + /*----------------------------------------------------------*\ + | SetRgbPowerModeConfig (fn7, sub-function 0x01 = set) | + | Wire format (long message, 16 bytes payload, matches the | + | GET response layout at the same offsets): | + | [0x01, 0x00, 0x00, idle_hi, idle_lo, sleep_hi, sleep_lo, | + | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] | + | | + | These are the firmware's *runtime* power timers — the | + | values reset on power cycle but persist across SW control | + | release/reclaim, so we need to write them ourselves on | + | every claim to be safe. | + \*----------------------------------------------------------*/ + if(caps.idx_rgb_effects == 0 || !caps.has_power_mgmt) + { + return; + } + + uint8_t data[16] = {}; + data[0] = 0x01; // sub-function: SET + data[3] = (uint8_t)((idle_s >> 8) & 0xFF); + data[4] = (uint8_t)( idle_s & 0xFF); + data[5] = (uint8_t)((sleep_s >> 8) & 0xFF); + data[6] = (uint8_t)( sleep_s & 0xFF); + + blankFAPmessage response; + SendAckedIntoFAP(caps.idx_rgb_effects, caps.fn_pwr_config, + data, sizeof(data), response); + + LOG_DEBUG("%s WritePowerConfig: idle=%us sleep=%us", LOG_TAG, idle_s, sleep_s); +} + +void LogitechHIDPP20Controller::ReadActiveProfileSector() +{ + /*----------------------------------------------------------*\ + | Diagnostic-only read of the active profile sector via | + | ProfileManagement (0x8101) load + paged readBuffer. | + | | + | This sector is the canonical storage for persisted device | + | state on G-series devices: idle/sleep timers, baseline | + | RGB effect, FKC enable, and more. The HID++ feature | + | endpoints (0x8071, 0x8081, 0x1B05, ...) are mostly status | + | hooks; the configuration database lives here. We don't | + | act on the contents — just log them so we can see what | + | the device thinks its persisted state is. | + | | + | Wire format mirrors observed wire capture (load followed | + | by 7× readBuffer): | + | load: long msg, [partition=0x01, sector=0x01, | + | size_hi=0x00, size_lo=0x63, | + | padding to 16 bytes] | + | readBuffer: short msg, [offset_hi, offset_lo, 0] | + | returns long msg with 16 bytes of data | + | | + | Sector size 0x63 = 99 bytes is what the vendor app | + | requested for the G515 active profile. Other devices may | + | differ — we | + | hardcode it for now since this is diagnostic-only. | + \*----------------------------------------------------------*/ + if(caps.idx_profile_management == 0) + { + return; + } + + constexpr uint16_t SECTOR_SIZE = 0x63; // 99 bytes + constexpr uint16_t PAGE_SIZE = 16; + + /*---------------------------------------------------------*\ + | Step 1: load the sector into the device's read buffer | + \*---------------------------------------------------------*/ + uint8_t load_data[16] = {}; + load_data[0] = 0x01; // partition: NVS/flash + load_data[1] = 0x01; // sector: active profile + load_data[2] = (uint8_t)((SECTOR_SIZE >> 8) & 0xFF); // size hi + load_data[3] = (uint8_t)( SECTOR_SIZE & 0xFF); // size lo + + blankFAPmessage load_resp; + int load_result = SendAckedIntoFAP(caps.idx_profile_management, FN_8101_LOAD, + load_data, sizeof(load_data), load_resp); + + if(load_result <= 0) + { + LOG_DEBUG("%s ProfileSector load failed (result=%d)", LOG_TAG, load_result); + return; + } + + /*---------------------------------------------------------*\ + | Step 2: page the sector out 16 bytes at a time | + \*---------------------------------------------------------*/ + uint8_t sector_buf[SECTOR_SIZE] = {}; + + for(uint16_t offset = 0; offset < SECTOR_SIZE; offset += PAGE_SIZE) + { + uint8_t read_req[3] = { + (uint8_t)((offset >> 8) & 0xFF), + (uint8_t)( offset & 0xFF), + 0x00 + }; + uint8_t page_resp[20] = {}; + + int result = SendAndReceive(caps.idx_profile_management, FN_8101_READBUFFER, + read_req, sizeof(read_req), + page_resp, sizeof(page_resp)); + + if(result <= 0) + { + LOG_DEBUG("%s ProfileSector readBuffer offset=0x%04X failed (result=%d)", + LOG_TAG, (unsigned)offset, result); + return; + } + + size_t copy_len = (offset + PAGE_SIZE > SECTOR_SIZE) + ? (size_t)(SECTOR_SIZE - offset) + : PAGE_SIZE; + memcpy(sector_buf + offset, page_resp, copy_len); + } + + /*----------------------------------------------------------*\ + | Step 3: log as a hexdump, one row per 16 bytes | + \*----------------------------------------------------------*/ + LOG_DEBUG("%s ProfileSector partition=NVS sector=1 size=%u bytes:", + LOG_TAG, (unsigned)SECTOR_SIZE); + + for(uint16_t row = 0; row < SECTOR_SIZE; row += PAGE_SIZE) + { + size_t row_len = (row + PAGE_SIZE > SECTOR_SIZE) + ? (size_t)(SECTOR_SIZE - row) + : PAGE_SIZE; + + char hex[64] = {}; + char* p = hex; + for(size_t i = 0; i < row_len; i++) + { + snprintf(p, 4, "%02X ", sector_buf[row + i]); + p += 3; + } + + LOG_DEBUG("%s %04X: %s", LOG_TAG, (unsigned)row, hex); + } +} + diff --git a/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller.h b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller.h new file mode 100644 index 0000000..65f5108 --- /dev/null +++ b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller.h @@ -0,0 +1,874 @@ +/*---------------------------------------------------------*\ +| LogitechHIDPP20Controller.h | +| | +| Unified Logitech HID++ 2.0 controller | +| | +| Uses feature discovery (IRoot 0x0000) to dynamically | +| determine device capabilities and adapt to any HID++ | +| 2.0 device with RGB lighting support. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "RGBController.h" +#include "LogitechProtocolCommon.h" + +/*-----------------------------------------------------*\ +| HID++ 2.0 Feature Page IDs | +\*-----------------------------------------------------*/ +#define HIDPP20_FEAT_IROOT 0x0000 +#define HIDPP20_FEAT_FEATURE_SET 0x0001 +#define HIDPP20_FEAT_DEVICE_NAME_TYPE 0x0005 +#define HIDPP20_FEAT_ONBOARD_PROFILES 0x8100 +#define HIDPP20_FEAT_PROFILE_MANAGEMENT 0x8101 +#define HIDPP20_FEAT_FIRMWARE_INFO 0x0003 +#define HIDPP20_FEAT_CENTPPBRIDGE 0x0003 /* same ID, different meaning on Centurion */ +#define HIDPP20_FEAT_COLOR_LED_EFFECTS 0x8070 +#define HIDPP20_FEAT_RGB_EFFECTS 0x8071 +#define HIDPP20_FEAT_PER_KEY_LIGHTING_V1 0x8080 +#define HIDPP20_FEAT_PER_KEY_LIGHTING_V2 0x8081 +#define HIDPP20_FEAT_KEYBOARD_LAYOUT 0x4540 +#define HIDPP20_FEAT_DISABLE_KEYS_BY_USAGE 0x4522 +#define HIDPP20_FEAT_CENTURION_RGB 0x0600 +#define HIDPP20_FEAT_HEADSET_RGB_HOSTMODE 0x0620 +#define HIDPP20_FEAT_CENTURION_DEVICE_INFO 0x0100 +#define HIDPP20_FEAT_CENTURION_DEVICE_NAME 0x0101 +#define HIDPP20_FEAT_UNIFIED_BATTERY 0x1004 +#define HIDPP20_FEAT_WIRELESS_STATUS 0x1D4B + +/*-----------------------------------------------------*\ +| HID++ 2.0 Function IDs (byte 3 high nibble) | +| Function ID is shifted left 4 bits, low nibble = swID | +\*-----------------------------------------------------*/ +/*-----------------------------------------------------*\ +| HID++ Software ID — identifies our responses. | +| Must avoid: 0x00 (firmware), 0x01 (vendor app), | +| 0x02-0x0F (Solaar cycles these). | +| There are only 16 values (4-bit field), and all are | +| claimed. We pick a fixed value and will coordinate | +| with Solaar to exclude it from its cycle. | +\*-----------------------------------------------------*/ +#define HIDPP20_SW_ID 0x07 + +/*-----------------------------------------------------*\ +| Feature 0x8071 functions | +\*-----------------------------------------------------*/ +#define FN_8071_GET_INFO 0x00 +#define FN_8071_SET_EFFECT 0x10 +#define FN_8071_SET_PATTERN 0x20 +#define FN_8071_NV_CONFIG 0x30 +#define FN_8071_BIN_INFO 0x40 +#define FN_8071_SW_CONTROL 0x50 +#define FN_8071_SYNC 0x60 +#define FN_8071_PWR_CONFIG 0x70 +#define FN_8071_PWR_MODE 0x80 + +/*-----------------------------------------------------*\ +| Feature 0x8081 functions | +\*-----------------------------------------------------*/ +#define FN_8081_GET_INFO 0x00 +#define FN_8081_SET_INDIVIDUAL 0x10 +#define FN_8081_SET_CONSECUTIVE 0x20 +#define FN_8081_SET_DELTA_5BIT 0x30 +#define FN_8081_SET_DELTA_4BIT 0x40 +#define FN_8081_SET_RANGE 0x50 +#define FN_8081_SET_SINGLE_VALUE 0x60 +#define FN_8081_FRAME_END 0x70 + +/*-----------------------------------------------------*\ +| Feature 0x0620 functions (headset RGB hostmode) | +\*-----------------------------------------------------*/ +#define FN_0620_GET_INFO 0x00 +#define FN_0620_GET_RGB_ZONE_INFO 0x10 +#define FN_0620_SET_INDIVIDUAL_RGB_ZONES 0x20 +#define FN_0620_SET_CONSECUTIVE_RGB_ZONES 0x30 +#define FN_0620_SET_RANGE_RGB_ZONES 0x40 +#define FN_0620_SET_RGB_ZONES_SINGLE_VALUE 0x50 +#define FN_0620_FRAME_END 0x60 +#define FN_0620_GET_HOST_MODE_STATE 0x70 +#define FN_0620_SET_HOST_MODE_STATE 0x80 + +/*-----------------------------------------------------*\ +| Feature 0x8100 functions | +\*-----------------------------------------------------*/ +#define FN_8100_SET_ONBOARD_MODE 0x10 +#define FN_8100_GET_ONBOARD_MODE 0x20 + +/*-----------------------------------------------------*\ +| Feature 0x8101 functions | +\*-----------------------------------------------------*/ +#define FN_8101_GET_SET_MODE 0x60 +#define FN_8101_LOAD 0x80 // load(partition, sector, size) +#define FN_8101_READBUFFER 0xC0 // readBuffer(offset) + +/*-----------------------------------------------------*\ +| Zone cluster effect entry | +\*-----------------------------------------------------*/ +struct HIDPP20Effect +{ + uint8_t index; + uint16_t effect_id; + uint16_t capabilities; + uint16_t default_period; +}; + +/*-----------------------------------------------------*\ +| Zone cluster info from 0x8071 GetRgbClusterInfo | +\*-----------------------------------------------------*/ +struct HIDPP20ZoneCluster +{ + uint8_t index; + uint16_t location; + uint8_t effect_count; + std::vector effects; +}; + +/*-----------------------------------------------------*\ +| Per-model device quirks — behavioral differences that | +| can't be detected via feature probing. | +\*-----------------------------------------------------*/ +enum HIDPP20DeviceQuirks : uint32_t +{ + HIDPP20_QUIRK_NONE = 0, + HIDPP20_QUIRK_FADE_ACCEPTS_WRITES = (1 << 0), // firmware accepts host frames during sleep fade without waking +}; + +struct HIDPP20DeviceQuirkEntry +{ + uint16_t pid_wireless; + uint16_t pid_wired; + uint32_t quirks; +}; + +/*---------------------------------------------------------*\ +| Default: suppress frames while SLEEPING. Safe everywhere | +| — it cannot wake a device that treats writes as activity. | +| Devices listed here opt out of suppression because their | +| firmware accepts writes during the fade without | +| cancelling sleep. | +\*---------------------------------------------------------*/ +static constexpr HIDPP20DeviceQuirkEntry HIDPP20_DEVICE_QUIRK_TABLE[] = +{ + { 0x40B4, 0xC355, HIDPP20_QUIRK_FADE_ACCEPTS_WRITES }, // G515 LS TKL +}; + +/*-----------------------------------------------------*\ +| Device capabilities discovered via feature probing | +\*-----------------------------------------------------*/ +struct HIDPP20DeviceCapabilities +{ + std::string device_name; + uint8_t device_type; + std::string firmware_version; + std::string serial_number; + std::string unit_id; // stable hardware ID (from FirmwareInfo fn0) + uint16_t pid_wireless; // wireless virtual PID (from FirmwareInfo fn0) + uint16_t pid_wired; // wired USB PID (from FirmwareInfo fn0) + uint32_t quirks; // resolved from HIDPP20_DEVICE_QUIRK_TABLE after PID discovery + + /*--------------------------------------------------*\ + | Complete feature map (feature_id → runtime index) | + | Built once by EnumerateFeatures, used by all | + | subsequent GetFeatureIndex lookups (no wire). | + \*--------------------------------------------------*/ + std::map feature_map; + std::map feature_versions; /* feature_id -> version byte */ + bool feature_map_complete; // true after bulk enumeration + + /*-------------------------------------------------*\ + | Feature indices (0 = not supported) | + \*-------------------------------------------------*/ + uint8_t idx_onboard_profiles; + uint8_t idx_profile_management; + uint8_t idx_rgb_effects; + uint8_t idx_headset_rgb_hostmode; /* 0x0620 — Centurion headset RGB */ + uint8_t idx_perkey_v2; + uint8_t idx_perkey_v1; + uint8_t idx_wireless_status; + uint8_t idx_disable_keys_by_usage; /* 0x4522 — keyboard-family handshake */ + uint16_t rgb_feature_page; + + /*--------------------------------------------------*\ + | Resolved function IDs for the RGB effects feature | + | Varies between 0x8070, 0x8071, 0x0600 | + \*--------------------------------------------------*/ + uint8_t fn_set_effect; + uint8_t fn_sw_control; + uint8_t fn_pwr_config; + uint8_t fn_pwr_mode; + bool has_power_mgmt; + bool sw_control_simple; + + /*--------------------------------------------------*\ + | Persistent NV settings read from RGBEffects fn3 | + | (FN_8071_NV_CONFIG). Capability 0x0020 is the | + | sleep ramp / off-ramp transition (enabled + | + | ramp_seconds). | + \*--------------------------------------------------*/ + bool nv_sleep_ramp_known; + bool nv_sleep_ramp_enabled; + uint8_t nv_sleep_ramp_seconds; + + /*---------------------------------------------------*\ + | Device-firmware effect cards (0x8071 fn0 | + | GetEffectSpecificInfo). Populated by | + | DiscoverEffectCards at feature-discovery time. | + | | + | has_effect_cards — probe returned a valid response | + | for firmware card 0 page 1 (no InvalidArgument). | + | effect_card_template[0..1] — device-wide constant | + | bytes read from that response at data[10..11]. | + | Echoed back into prep1 of DoObservedPerKeyPrep | + | at SetEffectByIndex params[6..7]. | + | | + | Gate for the observed per-key prep is now | + | has_effect_cards — replaces the earlier | + | "effects.size() < 5" heuristic, which was a proxy | + | that correlated with "has cards" on the devices we | + | happened to know but had no principled meaning. | + \*---------------------------------------------------*/ + bool has_effect_cards; + uint8_t effect_card_template[2]; + + /*-------------------------------------------------*\ + | Discovered zone and LED data | + \*-------------------------------------------------*/ + std::vector zone_clusters; + std::vector perkey_zone_ids; + std::vector headset_rgb_hostmode_zone_ids; /* 0x0620 fn1 result */ + bool has_perkey; + bool has_zone_effects; + bool is_headset_rgb_hostmode; /* 0x0620 path selected */ + bool has_numpad; + uint8_t keyboard_layout_code; +}; + +/*------------------------------------------------------*\ +| Transport type — determines wire framing | +\*------------------------------------------------------*/ +enum HIDPP20TransportType +{ + HIDPP20_TRANSPORT_STANDARD, // 0xFF00/0xFF43: report IDs 0x10/0x11, 7/20 bytes + HIDPP20_TRANSPORT_CENTURION // 0xFFA0: report ID 0x51 or 0x50, 64 bytes, + // with CPL framing and CentPPBridge sub-device routing +}; + +/*------------------------------------------------------*\ +| Transport layer — abstracts wire format differences | +| | +| Standard HID++ and Centurion both carry the same | +| feature/function/data payload, but with different | +| report framing. This struct holds transport state | +| so SendMessage/ReadMessage can adapt. | +\*------------------------------------------------------*/ +struct HIDPP20Transport +{ + HIDPP20TransportType type; + uint16_t usage_page; // 0xFF00, 0xFF43, or 0xFFA0 + uint8_t report_id; // 0x10/0x11 for standard, 0x51/0x50 for Centurion + bool addressed; // Centurion 0x50 has device address byte + uint8_t device_address; // Centurion 0x50: device address (e.g., 0x23) + uint8_t bridge_feat_idx; // CentPPBridge feature index on parent (0 if N/A) + uint8_t sub_device_id; // CentPPBridge sub-device ID (typically 0) + uint16_t bridge_mtu; // CentPPBridge MTU from getConnectionInfo: + // 0 = no sub-device, sendFragment will fail + // >0 = sub-device present, payload size in bytes +}; + +/*-----------------------------------------------------*\ +| Power management state machine | +| Matches Solaar's RGBPowerManager states | +\*-----------------------------------------------------*/ +enum HIDPP20PowerState +{ + HIDPP20_POWER_ACTIVE = 0, + HIDPP20_POWER_DIMMING = 1, + HIDPP20_POWER_IDLE = 2, + HIDPP20_POWER_SLEEPING = 3, +}; + +/*-----------------------------------------------------*\ +| Parsed HID++ message for the response queue | +\*-----------------------------------------------------*/ +struct HIDPP20RawMessage +{ + uint8_t feat; + uint8_t func; + uint8_t data[60]; + int result; +}; + +/*------------------------------------------------------*\ +| Per-key write tracking. SendPerKeyData is fire-and- | +| forget at the wire layer; we track which zones each | +| outstanding write covers so PerKeyFrameEnd can match | +| ACKs back by FIFO order and report which zones the | +| firmware actually committed. | +\*------------------------------------------------------*/ +struct OutstandingPerKeyWrite +{ + uint8_t function; // FN_8081_* (high nibble carries the type) + std::vector zone_ids; // zones covered by this packet +}; + +/*-----------------------------------------------------*\ +| Result of a per-key frame commit. The caller uses | +| these to update its delta-tracking state: | +| - frame_end_acked: did the FrameEnd packet ACK? | +| - acked_zones: zones whose write packet ACKed | +| - attempted_zones: every zone written this frame | +\*-----------------------------------------------------*/ +struct PerKeyFrameResult +{ + bool frame_end_acked; + std::vector acked_zones; + std::vector attempted_zones; +}; + +/*-------------------------------------------------------*\ +| Retry policy for SendAcked. | +| | +| Controls the send/read/retry loop for a single HID++ | +| request. Three canned policies cover all use cases: | +| | +| Reliable: probe/discovery/set/get (~6s worst case) | +| FrameEnd: per-key commit gate (~230ms worst) | +| Streaming: per-key write inside the animation loop | +| (~80ms worst) | +| | +| backoff_ms[i] is the delay applied BEFORE the i-th | +| attempt. backoff_ms[0] is normally 0 (no delay before | +| the first send). The schedule mirrors the firmware's | +| own event burst pattern (63→125→250→500→1000→2000ms, | +| "catch at least one of N"). | +\*-------------------------------------------------------*/ +struct HIDPP20RetryPolicy +{ + const uint16_t* backoff_ms; // schedule[i] = delay before attempt i + uint8_t attempts; // length of backoff_ms (>=1) + uint16_t read_window_ms; // per-attempt read budget + bool flush_before; // flush response queue at call start + bool retry_on_busy; // BUSY (0x08) -> retry the send + const char* name; // for logging ("reliable", etc.) +}; + +/*------------------------------------------------------*\ +| Canned backoff schedules. | +\*------------------------------------------------------*/ +static constexpr uint16_t HIDPP20_BACKOFF_RELIABLE[] = + { 0, 63, 125, 250, 500, 1000, 2000 }; +static constexpr uint16_t HIDPP20_BACKOFF_PROBE[] = + { 0, 100 }; + +/*------------------------------------------------------*\ +| SW-control reclaim backoff. Used by ReconnectDevice | +| to retry the claim+push sequence after a wireless | +| reconnect, racing the firmware boot animation. The | +| vendor app typically lands control in ~50ms; this | +| schedule fits | +| 6 attempts inside ~620ms so the animation never gets | +| a chance to become visible. | +\*------------------------------------------------------*/ +static constexpr uint16_t HIDPP20_RECLAIM_BACKOFF_MS[] = + { 0, 20, 40, 80, 160, 320 }; + +/*------------------------------------------------------*\ +| FrameEnd BUSY retry backoff. Used by PerKeyFrameEnd | +| when the firmware returns HID++ error 0x08 (BUSY) | +| because it's still draining the per-key write queue. | +| First retry is fast (~2 USB round trips) for the | +| common case where BUSY was transient; subsequent | +| retries give actual drain headroom. Total worst case | +| ~180ms, fits inside the PerKeyFrameEnd 300ms deadline | +| with margin for the eventual ACK to land. | +\*------------------------------------------------------*/ +static constexpr uint16_t HIDPP20_FRAME_END_BUSY_BACKOFF_MS[] = + { 30, 60, 90 }; + +/*-----------------------------------------------------*\ +| Deep-sleep detection threshold. After StartSleep() | +| commands the firmware fade, the device eventually | +| enters deep sleep and returns BUSY to every FrameEnd. | +| Once this many consecutive FrameEnd attempts exhaust | +| all BUSY retries while power_state == SLEEPING, we | +| suppress further frame sends until Wake() fires. | +\*-----------------------------------------------------*/ +static constexpr int HIDPP20_DEEP_SLEEP_FAILURE_THRESHOLD = 5; + +/*------------------------------------------------------*\ +| Per-key frame retry backoff. Used when a full | +| DeviceUpdateLEDs pass completes with some zones | +| unacked (partial commit). The retry re-runs a whole | +| frame from the power thread, so the backoff is | +| between full frames, not individual packets. | +| | +| First value aligned to the power thread's 50ms poll | +| cadence — anything shorter rounds up anyway, and | +| matching the tick makes latency predictable. | +| | +| Worst case: 5 retries, cumulative ~1550ms. This | +| covers the reconnect-transient window where the G502 | +| firmware silently drops per-key writes for several | +| hundred ms after the wireless link re-establishes. | +\*------------------------------------------------------*/ +static constexpr uint16_t HIDPP20_REPAINT_RETRY_BACKOFF_MS[] = + { 50, 100, 200, 400, 800 }; + +static constexpr HIDPP20RetryPolicy HIDPP20_POLICY_RELIABLE = { + HIDPP20_BACKOFF_RELIABLE, + sizeof(HIDPP20_BACKOFF_RELIABLE) / sizeof(uint16_t), + 300, // read window + true, // flush_before + true, // retry_on_busy + "reliable" +}; + +/*------------------------------------------------------*\ +| Probe policy: tight budget for is-this-HID++ checks | +| during initial discovery. ~500ms worst case for dead | +| devices, vs ~6s for reliable. One retry handles a | +| transient hiccup on the first IRoot call (e.g. on a | +| busy mouse), but we bail fast on truly non-responsive | +| or non-HID++ hidraws so probe latency stays bounded. | +\*------------------------------------------------------*/ +static constexpr HIDPP20RetryPolicy HIDPP20_POLICY_PROBE = { + HIDPP20_BACKOFF_PROBE, + sizeof(HIDPP20_BACKOFF_PROBE) / sizeof(uint16_t), + 200, // read window + true, // flush_before + true, // retry_on_busy + "probe" +}; + +class LogitechHIDPP20Controller +{ +public: + LogitechHIDPP20Controller(hid_device* dev, const char* path, + uint8_t device_index, bool wireless, + std::shared_ptr mutex_ptr, + uint16_t usage_page = 0xFF00); + ~LogitechHIDPP20Controller(); + + /*-------------------------------------------------*\ + | Lifecycle | + \*-------------------------------------------------*/ + bool Probe(); + void Initialize(); + void Shutdown(); + + /*-------------------------------------------------*\ + | Accessors | + \*-------------------------------------------------*/ + const HIDPP20DeviceCapabilities& GetCapabilities() const; + std::string GetDeviceLocation(); + std::string GetSerialString(); + uint32_t GetInitGeneration() const; + + /*--------------------------------------------------*\ + | Per-key lighting (0x8081) | + | | + | Per-key writes are fire-and-forget at the wire | + | layer. SendPerKeyData enqueues an outstanding | + | write entry; PerKeyFrameEnd drains the response | + | queue, matches ACKs by FIFO, and returns which | + | zones the firmware actually committed plus | + | whether the FrameEnd itself ACKed. | + \*--------------------------------------------------*/ + void SetPerKeyColors(const std::vector>& zone_colors); + void SetAllPerKeyColor(RGBColor color); + void SendPerKeyData(uint8_t perkey_idx, uint8_t function, + const uint8_t* data, size_t len, + const std::vector& zone_ids); + PerKeyFrameResult PerKeyFrameEnd(); + + /*-------------------------------------------------*\ + | Zone effects (0x8071 / 0x8070) | + \*-------------------------------------------------*/ + void SetZoneEffect(uint8_t cluster_idx, uint8_t effect_idx, + uint16_t effect_id, + unsigned char r, unsigned char g, unsigned char b, + uint16_t period, unsigned char brightness, + unsigned char direction, bool persist); + + /*---------------------------------------------------*\ + | Headset RGB hostmode (0x0620). | + | Sticky-claim model: SetHostMode() claims once via | + | fn8, then each write is fn5 (single-value) or fn2 | + | (individual) + fn6 FrameEnd[0x01]. 0x02 persist was | + | tested and does not work on G522 firmware. | + \*---------------------------------------------------*/ + void SetHeadsetRGBHostmodeColors(const std::vector& zone_colors); + + /*-------------------------------------------------*\ + | SW control management | + \*-------------------------------------------------*/ + int SetSWControl(uint8_t mode, uint8_t flags); + void SetRGBPowerMode(uint8_t mode); + void SetHostMode(); + bool ClaimSWControlIfNeeded(); + void UpgradeSwControlAfterFirstPaint(); + + /*-------------------------------------------------*\ + | Keyboard-family handshake (0x4522 fn3 + fn1). | + | G815 / G915 / G Pro send this before any mode | + | write. Feature-gated no-op on devices (G502 / | + | G515) that don't enumerate 0x4522. | + \*-------------------------------------------------*/ + void DoDisableKeysByUsageHandshake(); + + /*--------------------------------------------------*\ + | Keyboard-family per-key takeover prep. | + | Per-cluster SetEffectByIndex(effectIdx=0=Off, | + | persist=1) + primer key via SetIndividualRgbZones | + | + FrameEnd. Matches G815 / G915 InitializeDirect. | + | Gated on 0x4522 + per-key V2 presence. | + \*--------------------------------------------------*/ + void DoKeyboardFamilyPerKeyPrep(); + + /*--------------------------------------------------*\ + | Wake-repaint flag. Set by Wake() before calling | + | request_repaint_fn so the repaint callback knows | + | to invalidate sent_colors (force a full per-key | + | push) without triggering the claim/prep sequence. | + \*--------------------------------------------------*/ + bool ConsumeWakeFullRepaint(); + bool NeedsPrepSequence() const { return sw_control_needs_upgrade_to_5; } + + /*--------------------------------------------------*\ + | Per-key retry scheduling. Called by the RGB | + | controller's DeviceUpdateLEDs on partial-commit | + | frames; the power thread polls and fires | + | request_repaint_fn when a retry deadline expires. | + \*--------------------------------------------------*/ + void ScheduleRetryPaint(); + void CancelRetryPaint(); + void TickRetryPaintIfPending(); + + /*--------------------------------------------------*\ + | Observed per-key prep sequence. Two | + | SetEffectByIndex calls cloned byte-for-byte from a | + | wire capture on a G502 X PLUS. | + | Used in place of the Static-pass-through prep when | + | the device's RGBEffects enumeration matches the | + | G502 shape — see DeviceUpdateLEDs for gating. | + \*--------------------------------------------------*/ + void DoObservedPerKeyPrep(); + + /*--------------------------------------------------*\ + | Power management (idle/dim/sleep/wake) | + \*--------------------------------------------------*/ + void StartPowerManager(); + void StopPowerManager(); + void StartEventWatcher(); + void StartProbeWatcher(); + bool HasBridge() const; + void SetRepaintCallback(std::function repaint); + void SetReapplyActiveModeCallback(std::function cb); + void SetRegisterCallback(std::function cb); + HIDPP20PowerState GetPowerState() const; + int GetDimBrightness() const; + bool IsOnline() const; + bool IsDeepSleep() const; + void SetWireless(bool w) { wireless = w; } + bool QueryWirelessStatus(); + bool QueryExternalPower(); + void FlushResponseQueue(); + +private: + hid_device* dev; + std::string location; + uint8_t device_index; + bool wireless; + std::shared_ptr mutex; + HIDPP20DeviceCapabilities caps; + HIDPP20Transport transport; + bool initialized; + bool sw_control_claimed; + bool sw_control_needs_upgrade_to_5; + uint32_t frame_counter; + + /*--------------------------------------------------*\ + | Retry-paint state (partial-commit recovery). | + | retry_paint_deadline_ zero = no retry pending. | + | retry_paint_attempt_ indexes into | + | HIDPP20_REPAINT_RETRY_BACKOFF_MS; once it reaches | + | the array length, we give up for this sequence. | + | Atomic so both the paint thread (RGB controller) | + | and the power thread can access without locks. | + \*--------------------------------------------------*/ + std::atomic retry_paint_deadline_; + std::atomic retry_paint_attempt_; + std::atomic wake_full_repaint_pending_; + uint32_t init_generation; + std::string log_tag; + + /*---------------------------------------------------*\ + | Transport-layer I/O | + | | + | SendMessage/ReadMessage handle wire framing based | + | on transport.type. Upper layers pass feature index, | + | function ID, and payload — the transport layer | + | wraps them in the correct report format. | + \*---------------------------------------------------*/ + int SendMessage(uint8_t feat_idx, uint8_t function, + const uint8_t* data, size_t len); + int ReadMessage(uint8_t* feat_idx_out, uint8_t* function_out, + uint8_t* data_out, size_t data_max, + int timeout_ms = LOGITECH_PROTOCOL_TIMEOUT); + + /*--------------------------------------------------*\ + | Standard HID++ transport (0xFF00/0xFF43) | + \*--------------------------------------------------*/ + int SendStandard(uint8_t feat_idx, uint8_t function, + const uint8_t* data, size_t len); + int ReadStandardDirect(uint8_t* feat_idx_out, uint8_t* function_out, + uint8_t* data_out, size_t data_max, + int timeout_ms); + + /*--------------------------------------------------*\ + | Centurion transport (0xFFA0) | + | Wraps messages in CPL framing, routes through | + | CentPPBridge for sub-device access. | + \*--------------------------------------------------*/ + int SendCenturion(uint8_t feat_idx, uint8_t function, + const uint8_t* data, size_t len); + int ReadCenturionDirect(uint8_t* feat_idx_out, uint8_t* function_out, + uint8_t* data_out, size_t data_max, + int timeout_ms); + + /*--------------------------------------------------*\ + | Reader thread dispatch layer | + | ReadHIDDirect: raw HID read (used by reader thread | + | and during Probe before reader starts). | + | ReadFromQueue: waits on response queue filled by | + | the reader thread. | + \*--------------------------------------------------*/ + int ReadHIDDirect(uint8_t* feat_idx_out, uint8_t* function_out, + uint8_t* data_out, size_t data_max, + int timeout_ms); + int ReadFromQueue(uint8_t* feat_idx_out, uint8_t* function_out, + uint8_t* data_out, size_t data_max, + int timeout_ms); + + /*---------------------------------------------------*\ + | High-level helpers | + | SendAndReceive is retained as a thin wrapper | + | around SendAcked with the reliable policy, for | + | call-site stability. | + \*---------------------------------------------------*/ + int SendAndReceive(uint8_t feat_idx, uint8_t function, + const uint8_t* send_data, size_t send_len, + uint8_t* recv_data, size_t recv_max); + + /*---------------------------------------------------*\ + | Unified send-and-ack primitive with retry policy. | + | All command paths converge here. Returns: | + | >0 : bytes copied into recv_data | + | 0 : timeout / BUSY exhaustion | + | -1 : non-BUSY HID++ error | + | -2 : wire error (SendMessage failed) | + | If hidpp20_error_out is non-null and return is -1, | + | the HID++ error code is stored there. | + \*---------------------------------------------------*/ + int SendAcked(uint8_t feat_idx, uint8_t function, + const uint8_t* send_data, size_t send_len, + uint8_t* recv_data, size_t recv_max, + const HIDPP20RetryPolicy& policy = HIDPP20_POLICY_RELIABLE, + uint8_t* hidpp20_error_out = nullptr); + + /*--------------------------------------------------*\ + | Compatibility shim: same as SendAcked but writes | + | the response into a blankFAPmessage. Used by | + | callers that inherited the SendLong+ReadResponse | + | interface and inspect response.data[] downstream. | + \*--------------------------------------------------*/ + int SendAckedIntoFAP(uint8_t feat_idx, uint8_t function, + const uint8_t* send_data, size_t send_len, + blankFAPmessage& response, + const HIDPP20RetryPolicy& policy = HIDPP20_POLICY_RELIABLE); + + /*-------------------------------------------------*\ + | Feature discovery | + \*-------------------------------------------------*/ + uint8_t GetFeatureIndex(uint16_t feature_page, + const HIDPP20RetryPolicy& policy = HIDPP20_POLICY_RELIABLE); + uint8_t GetFeatureVersion(uint16_t feature_page) const; + void DiscoverTransport(); + void DiscoverDeviceName(); + void DiscoverDeviceType(); + void EnumerateFeatures(uint8_t feature_set_idx); + void DiscoverFirmwareInfo(); + void DiscoverRGBEffects(); + void DiscoverEffectCards(); + void DiscoverHeadsetRGBHostmode(); + void DiscoverPerKeyZones(); + void DiscoverKeyboardLayout(); + + /*-------------------------------------------------*\ + | Power management internals | + \*-------------------------------------------------*/ + void ReaderThreadFunc(); + void PowerThreadFunc(); + void DispatchEvent(uint8_t feat, uint8_t func, const uint8_t* data); + void OnUserActivity(uint8_t activity_type); + void StartDimRamp(); + void DimRampStep(); + void StartSleep(); + void Wake(); + void ReadFirmwareTimers(); + void ReadNvSleepRampConfig(); + void WritePowerConfig(uint16_t idle_s, uint16_t sleep_s); + void ReadActiveProfileSector(); + void ReprobeSubDevice(); + void ReconnectDevice(); + void FullReprobe(); + void RediscoverFeatures(); + /*----------------------------------------------------*\ + | Platform-specific. ScanForDevice walks the OS-level | + | HID enumeration to find the same physical device on | + | a new path (USB<->wireless transitions). Linux uses | + | sysfs; Windows and macOS use hidapi + serial_number | + | matching. Bodies live in | + | LogitechHIDPP20Controller_Linux.cpp and | + | LogitechHIDPP20Controller_Windows_MacOS.cpp. | + \*----------------------------------------------------*/ + bool ScanForDevice(bool force = false); + + /*----------------------------------------------------*\ + | Platform-specific. Returns the friendly name for a | + | Centurion sub-device at the given hidapi path, or "" | + | if no name is available. Linux reads HID_NAME from | + | sysfs; Windows uses hid_device_info::product_string. | + \*----------------------------------------------------*/ + std::string GetCenturionSubDeviceName(const std::string& path); + + void SwapHIDHandle(hid_device* new_dev, const std::string& new_path); + + /*-------------------------------------------------*\ + | Reader thread + response queue | + \*-------------------------------------------------*/ + std::thread* reader_thread; + std::atomic reader_running; + std::mutex response_mutex; + std::condition_variable response_cv; + std::deque response_queue; + + /*-------------------------------------------------*\ + | Power thread (state machine + command sender) | + \*-------------------------------------------------*/ + std::thread* power_thread; + std::atomic power_thread_running; + std::atomic pending_activity; // -1=none, 0=idle, 1+=active + std::atomic pending_connection; // 0=none, +1=connected, -1=disconnected + std::atomic pending_path_check; // HID++1.0 DJ notification → force-scan retries remaining (0=idle) + std::atomic device_online; // false when device is unreachable + std::atomic consecutive_timeouts; // reset on successful response + std::atomic watcher_mode; // true when retrying failed probe + + /*-------------------------------------------------*\ + | Power management state | + \*-------------------------------------------------*/ + HIDPP20PowerState power_state; + std::mutex power_mutex; + std::atomic deep_sleep; // true once device stops responding after StartSleep() + std::atomic consecutive_frame_end_failures; // FrameEnd BUSY exhaustions while SLEEPING + + /*-------------------------------------------------*\ + | Dim ramp state | + | dim_brightness_pct is applied by DeviceUpdateLEDs | + | to scale colors before pushing to device. | + | This is our own host-side animation, independent | + | of any firmware dim/sleep timers. | + \*-------------------------------------------------*/ + #define DIM_STEPS 25 + #define DIM_INTERVAL_MS 200 + #define DIM_TARGET_PCT 50 + std::atomic dim_brightness_pct; // 100=full, 50=dimmed + int dim_step; + std::chrono::steady_clock::time_point next_dim_time; + + /*-------------------------------------------------*\ + | Sleep timer | + \*-------------------------------------------------*/ + std::chrono::steady_clock::time_point sleep_deadline; + + /*--------------------------------------------------*\ + | Last idle-settings re-read timestamp. Drives the | + | 500ms poll in PowerThreadFunc that re-reads the | + | LogitechHIDPP20IdleSettings JSON key so updates | + | from the plugin (or manual edits) apply within | + | about half a second without any callback plumbing. | + \*--------------------------------------------------*/ + std::chrono::steady_clock::time_point last_idle_poll; + + /*-------------------------------------------------*\ + | Effective idle/sleep timers used by the state | + | machine. Populated from the firmware snapshot by | + | default, then possibly overridden by profile | + | values in ApplyPowerSavingProfile. | + \*-------------------------------------------------*/ + uint16_t idle_timeout_s; + uint16_t sleep_timeout_s; + + /*--------------------------------------------------*\ + | Firmware-configured timer snapshot. Read at init | + | (and on reconnect) by ReadFirmwareTimers and | + | never overwritten by profile application, so | + | that the unconfigured fallback path and transition | + | back from a user profile both have a clean set | + | of defaults to return to. | + \*--------------------------------------------------*/ + uint16_t fw_idle_timeout_s = 60; + uint16_t fw_sleep_timeout_s = 300; + uint16_t written_idle_s = 0; // last value written to device RAM (0 = not written yet) + uint16_t written_sleep_s = 0; + + /*--------------------------------------------------*\ + | Host-side idle/dim/sleep state. | + | Populated from LogitechHIDPP20IdleSettings on each | + | ApplyPowerSavingProfile() invocation. | + \*--------------------------------------------------*/ + bool ps_dim_enabled = false; + int ps_dim_target_pct = DIM_TARGET_PCT; + bool ps_sleep_enabled = false; + bool ps_on_external_power = false; + bool ps_last_logged_external = false; + int ps_last_logged_pct = -1; + int ps_last_logged_idle = -1; + int ps_last_logged_sleep = -1; + uint16_t last_power_raw = 0xFFFF; // dedup for QueryExternalPower TRACE + uint8_t idx_unified_battery = 0; + + void ApplyPowerSavingProfile(); + bool IsCurrentlyWireless() const; + + /*-------------------------------------------------*\ + | Per-key write tracking (per active frame) | + | Populated by SendPerKeyData, drained by | + | PerKeyFrameEnd. Single-threaded — only the RGB | + | controller thread touches per-key state. | + \*-------------------------------------------------*/ + std::vector outstanding_writes; + + /*-------------------------------------------------*\ + | Callbacks | + \*-------------------------------------------------*/ + std::function request_repaint_fn; + std::function reapply_active_mode_fn; + std::function register_controller_fn; +}; diff --git a/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller_Linux.cpp b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller_Linux.cpp new file mode 100644 index 0000000..ca6ead2 --- /dev/null +++ b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller_Linux.cpp @@ -0,0 +1,433 @@ +/*---------------------------------------------------------*\ +| LogitechHIDPP20Controller_Linux.cpp | +| | +| Linux-specific path-migration and device-name lookup | +| for the unified Logitech HID++ 2.0 controller. | +| | +| Uses sysfs (/sys/class/hidraw) to find the same | +| physical device on a new hidraw path after a USB <-> | +| wireless transition, and to read Centurion sub-device | +| friendly names from HID_NAME uevent fields. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "LogitechHIDPP20Controller.h" +#include "LogManager.h" + +#define LOG_TAG log_tag.c_str() + +std::string LogitechHIDPP20Controller::GetCenturionSubDeviceName(const std::string& path) +{ + /*--------------------------------------------------------*\ + | Centurion sub-device friendly name comes from sysfs | + | HID_NAME=, same field Solaar reads. The `path` arg is a | + | hidraw dev path like /dev/hidraw5; extract the hidrawN | + | basename and read /sys/class/hidraw//device/ | + | uevent. | + \*--------------------------------------------------------*/ + std::string sysfs_name; + size_t pos = path.rfind("hidraw"); + + if(pos == std::string::npos) + { + return sysfs_name; + } + + std::string uevent_path = "/sys/class/hidraw/" + path.substr(pos) + "/device/uevent"; + FILE* f = fopen(uevent_path.c_str(), "r"); + + if(!f) + { + return sysfs_name; + } + + char line[256]; + + while(fgets(line, sizeof(line), f)) + { + if(strncmp(line, "HID_NAME=", 9) == 0) + { + sysfs_name = line + 9; + + while(!sysfs_name.empty() && (sysfs_name.back() == '\n' || sysfs_name.back() == '\r')) + { + sysfs_name.pop_back(); + } + + break; + } + } + + fclose(f); + return sysfs_name; +} + +bool LogitechHIDPP20Controller::ScanForDevice(bool force) +{ + /*---------------------------------------------------------*\ + | Scan sysfs for a hidraw with matching unitId. Called by | + | the power thread either periodically (normal reactive | + | mode — only when device_online==false, USB/wireless | + | transition or physical unplug) or on demand from the | + | reader thread after a HID++1.0 Device Connection | + | notification arrives (force=true, bypasses the online | + | gate). Finds the device on its new connection path. | + | | + | Reactive-only: we never migrate while the current path | + | still works. On devices like the G502 X PLUS that expose | + | both a USB-direct hidraw AND a wireless-via-dongle hidraw | + | simultaneously (when paired to the receiver and plugged | + | in at the same time), eager migration would bounce us off | + | a working path onto one the firmware has actively muted, | + | breaking control. The device signals which path is | + | active by returning errors/going silent on the inactive | + | path — our reader thread picks that up as a hid_read | + | failure and flips device_online=false, at which point the | + | power thread calls this function to pick the new path. | + | | + | Linux implementation: walks /sys/class/hidraw and matches | + | on HID_UNIQ. The Windows counterpart (same class method, | + | different .cpp file) uses hid_enumerate + serial_number. | + | | + | Match criteria: | + | - device_online == false (caller should already ensure) | + | - HID_UNIQ matches our unitId | + | - Logitech VID (046D) | + | - HID++ interface (usage_page 0xFF00, usage 2) | + | - Different from our current path (device moved) | + \*---------------------------------------------------------*/ + if(caps.unit_id.empty() || caps.unit_id == "00000000") + { + return false; + } + + /*---------------------------------------------------------*\ + | Online guard: never migrate off a working path unless | + | the caller explicitly forces a re-check. The normal | + | periodic scan path stays gated on device_online==false | + | so it's a no-op when everything is healthy. | + | | + | The reader thread bypasses this guard (force=true) after | + | seeing a HID++1.0 Device Connection Status notification | + | from the Lightspeed receiver, which fires BEFORE the | + | firmware fully switches its data flow from wireless to | + | USB. At that moment device_online is still true (the | + | current path hasn't failed yet), but we want the scan to | + | run so we can migrate to the new path proactively. | + \*---------------------------------------------------------*/ + if(!force && device_online.load()) + { + return false; + } + + /*---------------------------------------------------------*\ + | Normalize our unitId to lowercase hex without dashes for | + | comparison. Sysfs HID_UNIQ varies between drivers: | + | - Lightspeed virtual: "0d-12-5d-47" (dashes) | + | - USB direct: "0D125D47" (no dashes) | + | We strip dashes and lowercase both sides for matching. | + \*---------------------------------------------------------*/ + std::string target_norm; + + for(char c : caps.unit_id) + { + if(c != '-') + { + target_norm += (char)tolower(c); + } + } + + /*---------------------------------------------------------*\ + | Read our current PID from sysfs. Only migrate to paths | + | with a DIFFERENT PID — same PID means same receiver, | + | just a different pairing slot (e.g., stale pairing). | + \*---------------------------------------------------------*/ + unsigned int current_pid = 0; + { + std::string cur_hidraw = location.substr(location.rfind('/') + 1); + std::string cur_uevent = "/sys/class/hidraw/" + cur_hidraw + "/device/uevent"; + std::ifstream cur_file(cur_uevent); + std::string line; + + while(std::getline(cur_file, line)) + { + if(line.compare(0, 7, "HID_ID=") == 0) + { + size_t lc = line.rfind(':'); + + if(lc != std::string::npos) + { + sscanf(line.c_str() + lc + 1, "%x", ¤t_pid); + } + + break; + } + } + } + + /*----------------------------------------------------------*\ + | Collect ALL sysfs hidraws with matching unit_id + Logitech | + | VID + different PID. With multiple Lightspeed dongles in | + | the system, several hidraws can share the same HID_UNIQ: | + | one is the live virt-slot on the connected dongle, others | + | are stale virt-slots on dongles where our device is not | + | actually present. We have to probe each one to find out | + | which slot is real — sysfs alone can't tell them apart. | + \*----------------------------------------------------------*/ + struct Candidate + { + std::string dev_path; + unsigned int pid; + }; + + std::vector candidates; + + DIR* dir = opendir("/sys/class/hidraw"); + + if(!dir) + { + return false; + } + + struct dirent* entry; + + while((entry = readdir(dir)) != nullptr) + { + if(strncmp(entry->d_name, "hidraw", 6) != 0) + { + continue; + } + + std::string uevent_path = "/sys/class/hidraw/" + + std::string(entry->d_name) + "/device/uevent"; + std::ifstream uevent(uevent_path); + + if(!uevent.is_open()) + { + continue; + } + + std::string line; + std::string hid_uniq; + std::string hid_id; + + while(std::getline(uevent, line)) + { + if(line.compare(0, 9, "HID_UNIQ=") == 0) + { + hid_uniq = line.substr(9); + } + else if(line.compare(0, 7, "HID_ID=") == 0) + { + hid_id = line.substr(7); + } + } + + std::string uniq_norm; + + for(char c : hid_uniq) + { + if(c != '-') + { + uniq_norm += (char)tolower(c); + } + } + + if(uniq_norm != target_norm) + { + continue; + } + + if(hid_id.find("0000046D") == std::string::npos && + hid_id.find("0000046d") == std::string::npos) + { + continue; + } + + std::string dev_path = "/dev/" + std::string(entry->d_name); + + if(dev_path == location) + { + continue; + } + + size_t last_colon = hid_id.rfind(':'); + + if(last_colon == std::string::npos) + { + continue; + } + + unsigned int pid = 0; + sscanf(hid_id.c_str() + last_colon + 1, "%x", &pid); + + if(pid == current_pid) + { + continue; + } + + candidates.push_back({dev_path, pid}); + } + + closedir(dir); + + if(candidates.empty()) + { + return false; + } + + /*----------------------------------------------------------*\ + | Probe each candidate before committing. Two-dongle systems | + | can expose multiple sysfs hidraws with the same HID_UNIQ: | + | one is the live slot, others are stale pairings that | + | respond with short-format UNKNOWN_DEVICE errors. Issue a | + | cheap IRoot GetFeature (feat 0x0001) to distinguish them. | + | A live slot returns a long-form response with feat_idx=0 | + | and feat_byte matching the sub-index we asked for. A stale | + | slot returns r[10 xx 8F 00 00 08 ...] (short error, code | + | 0x08 UNKNOWN_DEVICE). We accept the first candidate that | + | passes; the overall pending_path_check retry loop will | + | naturally re-probe all candidates on subsequent scans if | + | none pass on the current pass (e.g., mid-transition). | + \*----------------------------------------------------------*/ + std::string found_path; + hid_device* found_dev = nullptr; + + for(size_t c = 0; c < candidates.size(); c++) + { + const Candidate& cand = candidates[c]; + + LOG_DEBUG("%s Scan: migration candidate at %s (pid=0x%04X, current=%s pid=0x%04X)", + LOG_TAG, cand.dev_path.c_str(), cand.pid, + location.c_str(), current_pid); + + /*-----------------------------------------------------*\ + | Multi-dongle caveat: hid_enumerate returns entries | + | for ALL dongles with this PID (046D:4099 Lightspeed | + | can appear several times in a single machine). Match | + | strictly on the sysfs candidate's dev_path so each | + | candidate resolves to its OWN dongle's HID++ | + | interface — not the first match we stumble across. | + \*-----------------------------------------------------*/ + hid_device_info* devs = hid_enumerate(0x046D, (uint16_t)cand.pid); + std::string hidpp20_path; + + for(hid_device_info* d = devs; d != nullptr; d = d->next) + { + LOG_TRACE("%s Scan: enumerate PID=0x%04X path=%s page=0x%04X usage=%d", + LOG_TAG, cand.pid, d->path, d->usage_page, d->usage); + + if(std::string(d->path) == cand.dev_path && + d->usage_page == 0xFF00 && d->usage == 2 && + std::string(d->path) != location) + { + hidpp20_path = d->path; + break; + } + } + + hid_free_enumeration(devs); + + if(hidpp20_path.empty()) + { + LOG_DEBUG("%s Scan: %s no matching LogitechHID++ interface in enum", + LOG_TAG, cand.dev_path.c_str()); + continue; + } + + hid_device* test_dev = hid_open_path(hidpp20_path.c_str()); + + if(!test_dev) + { + LOG_DEBUG("%s Scan: %s failed to open", LOG_TAG, hidpp20_path.c_str()); + continue; + } + + /*------------------------------------------------------*\ + | Probe: HID++2.0 IRoot GetFeature for feat 0x0001 | + | (IFeatureSet). Wire: w[10 FF 0000 000100] | + | - report_id 0x10 (short) | + | - device_index 0xFF | + | - feat_idx 0x00 (IRoot) | + | - address 0x00 (func 0 GetFeature, sw_id 0) | + | - payload 00 01 00 (feat_id 0x0001) | + | | + | A live device returns r[11 xx 00 0X ...] — long form, | + | feat_idx 0x00, the address byte we sent back, and the | + | feature index in the payload. | + | A stale slot returns r[10 xx 8F 00 00 08 ...] — short | + | error form. Reject anything that starts with 0x10. | + \*------------------------------------------------------*/ + uint8_t probe[7] = {0x10, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00}; + uint8_t reply[20] = {}; + int write_rc = hid_write(test_dev, probe, sizeof(probe)); + bool probe_ok = false; + + if(write_rc >= 0) + { + /*-----------------------------------------------------*\ + | Drain up to ~100ms, looking for a long-form response | + | matching our probe. Skip stray reports from unrelated | + | firmware events that might be queued on the hidraw. | + \*-----------------------------------------------------*/ + std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(100); + + while(std::chrono::steady_clock::now() < deadline) + { + int read_rc = hid_read_timeout(test_dev, reply, sizeof(reply), 50); + + if(read_rc <= 0) + { + continue; + } + + if(reply[0] == 0x11 && reply[2] == 0x00 && reply[3] == 0x00) + { + probe_ok = true; + break; + } + + if(reply[0] == 0x10 && reply[2] == 0x8F) + { + LOG_DEBUG("%s Scan: %s probe rejected — err=0x%02X (stale slot)", + LOG_TAG, hidpp20_path.c_str(), reply[5]); + break; + } + } + } + + if(!probe_ok) + { + hid_close(test_dev); + continue; + } + + LOG_DEBUG("%s Scan: %s probe accepted (feat_idx=0x%02X)", + LOG_TAG, hidpp20_path.c_str(), reply[4]); + + found_path = hidpp20_path; + found_dev = test_dev; + break; + } + + if(!found_dev) + { + return false; + } + + LOG_INFO("%s Device migrated: %s -> %s", LOG_TAG, location.c_str(), found_path.c_str()); + + SwapHIDHandle(found_dev, found_path); + return true; +} diff --git a/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller_Windows_MacOS.cpp b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller_Windows_MacOS.cpp new file mode 100644 index 0000000..f609c26 --- /dev/null +++ b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller_Windows_MacOS.cpp @@ -0,0 +1,302 @@ +/*---------------------------------------------------------*\ +| LogitechHIDPP20Controller_Windows_MacOS.cpp | +| | +| Path-migration and device-name lookup for the unified | +| Logitech HID++ 2.0 controller on Windows and macOS. | +| | +| Uses hidapi's hid_enumerate + hid_device_info fields | +| (serial_number, product_string, product_id, usage_page) | +| on platforms with no /sys/class/hidraw equivalent. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "LogitechHIDPP20Controller.h" +#include "LogManager.h" +#include "StringUtils.h" + +#define LOG_TAG log_tag.c_str() + +std::string LogitechHIDPP20Controller::GetCenturionSubDeviceName(const std::string& path) +{ + /*---------------------------------------------------------*\ + | On Windows, hidapi's hid_device_info carries a | + | product_string field (wchar_t*). Enumerate all Logitech | + | devices and find the one whose path matches `path`. | + | | + | Caveat: Windows hidapi typically returns the parent | + | product string on every (interface, usage_page, usage) | + | entry that maps to the same USB device, so Centurion | + | sub-devices may share a name with the parent dongle. | + | That's a less specific name than Linux's HID_NAME, but | + | still better than "Logitech Centurion Device". | + \*---------------------------------------------------------*/ + std::string friendly; + + hid_device_info* devs = hid_enumerate(0x046D, 0x0000); + + for(hid_device_info* d = devs; d != nullptr; d = d->next) + { + if(d->path == nullptr) + { + continue; + } + + if(std::string(d->path) != path) + { + continue; + } + + friendly = StringUtils::wchar_to_string(d->product_string); + break; + } + + hid_free_enumeration(devs); + return friendly; +} + +bool LogitechHIDPP20Controller::ScanForDevice(bool force) +{ + /*---------------------------------------------------------*\ + | Scan hidapi for a Logitech device with matching unitId. | + | Called by the power thread either periodically (normal | + | reactive mode — only when device_online==false, USB/ | + | wireless transition or physical unplug) or on demand | + | from the reader thread after a HID++1.0 Device | + | Connection notification arrives (force=true, bypasses | + | the online gate). Finds the device on its new | + | connection path. | + | | + | Reactive-only: we never migrate while the current path | + | still works (same rationale as Linux — see the Linux | + | companion file for the full explanation). | + | | + | Windows implementation: walks hid_enumerate(046D, *) and | + | matches on hid_device_info::serial_number, which for | + | Logitech devices generally corresponds to the same | + | stable identity as the HID++-reported unit_id. If | + | serial_number matching yields nothing (hidapi may not | + | populate it for some Logitech devices on Windows), we | + | fall through to IRoot probing every candidate on the | + | matching usage_page/usage so we can still find the | + | device albeit more slowly. | + \*---------------------------------------------------------*/ + if(caps.unit_id.empty() || caps.unit_id == "00000000") + { + return false; + } + + if(!force && device_online.load()) + { + return false; + } + + std::string target_norm = StringUtils::normalize_hex_id(caps.unit_id); + + /*---------------------------------------------------------*\ + | Candidate = {path, pid, has_serial_match}. Serial matches | + | sort first so we try the cheapest/most-likely candidate. | + \*---------------------------------------------------------*/ + struct Candidate + { + std::string dev_path; + unsigned int pid; + bool serial_match; + }; + + std::vector candidates; + unsigned int current_pid = 0; + + hid_device_info* devs = hid_enumerate(0x046D, 0x0000); + + /*---------------------------------------------------------*\ + | First pass: find the entry matching our current path and | + | record its product_id so we can skip same-PID candidates. | + \*---------------------------------------------------------*/ + for(hid_device_info* d = devs; d != nullptr; d = d->next) + { + if(d->path != nullptr && std::string(d->path) == location) + { + current_pid = d->product_id; + break; + } + } + + /*---------------------------------------------------------*\ + | Second pass: collect candidates that (a) aren't our | + | current path, (b) speak the HID++ interface, and (c) have | + | a plausible identity match — either the serial_number | + | matches our unit_id, or at minimum their PID differs from | + | ours so they can't be another slot on the same dongle. | + \*---------------------------------------------------------*/ + for(hid_device_info* d = devs; d != nullptr; d = d->next) + { + if(d->path == nullptr) + { + continue; + } + + if(std::string(d->path) == location) + { + continue; + } + + /*-----------------------------------------------------*\ + | Only HID++ interface (usage_page 0xFF00, usage 2). | + | Note: on Windows hidapi may or may not populate | + | usage / usage_page consistently. If either is zero, | + | fall through — we'll probe unconditionally in that | + | case rather than dropping a potential candidate. | + \*-----------------------------------------------------*/ + bool usage_known = (d->usage_page != 0 || d->usage != 0); + + if(usage_known && (d->usage_page != 0xFF00 || d->usage != 2)) + { + continue; + } + + /*-----------------------------------------------------*\ + | Compare serial_number (wchar_t*) against our unit_id. | + | Normalize both and do an exact-match comparison. | + | Empty/missing serial is allowed — falls through as a | + | serial_match=false candidate so the probe path can | + | still find it via a different-PID filter. | + \*-----------------------------------------------------*/ + bool serial_match = false; + + if(d->serial_number != nullptr && d->serial_number[0] != L'\0') + { + std::string sn = StringUtils::wchar_to_string(d->serial_number); + std::string sn_norm = StringUtils::normalize_hex_id(sn); + + if(!sn_norm.empty() && sn_norm == target_norm) + { + serial_match = true; + } + } + + /*-----------------------------------------------------*\ + | If we have no serial match AND this path shares the | + | current PID, skip. Same PID with no identity evidence | + | is probably a sibling slot on the same dongle, not a | + | valid migration target. | + \*-----------------------------------------------------*/ + if(!serial_match && d->product_id == current_pid && current_pid != 0) + { + continue; + } + + Candidate c; + c.dev_path = d->path; + c.pid = d->product_id; + c.serial_match = serial_match; + candidates.push_back(c); + } + + hid_free_enumeration(devs); + + if(candidates.empty()) + { + return false; + } + + /*---------------------------------------------------------*\ + | Sort so serial-matched candidates get probed first. | + \*---------------------------------------------------------*/ + std::stable_sort(candidates.begin(), candidates.end(), + [](const Candidate& a, const Candidate& b) + { + return a.serial_match && !b.serial_match; + }); + + /*---------------------------------------------------------*\ + | Probe each candidate with IRoot GetFeature feat 0x0001. | + | A live slot returns a long-form (0x11) response; a stale | + | slot returns a short-form (0x10) error. See the Linux | + | companion file for the wire-level explanation. | + \*---------------------------------------------------------*/ + std::string found_path; + hid_device* found_dev = nullptr; + + for(size_t c = 0; c < candidates.size(); c++) + { + const Candidate& cand = candidates[c]; + + LOG_DEBUG("%s Scan: migration candidate at %s (pid=0x%04X, serial_match=%d)", + LOG_TAG, cand.dev_path.c_str(), cand.pid, + cand.serial_match ? 1 : 0); + + hid_device* test_dev = hid_open_path(cand.dev_path.c_str()); + + if(!test_dev) + { + LOG_DEBUG("%s Scan: %s failed to open", + LOG_TAG, cand.dev_path.c_str()); + continue; + } + + uint8_t probe[7] = {0x10, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00}; + uint8_t reply[20] = {}; + int write_rc = hid_write(test_dev, probe, sizeof(probe)); + bool probe_ok = false; + + if(write_rc >= 0) + { + std::chrono::steady_clock::time_point deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(100); + + while(std::chrono::steady_clock::now() < deadline) + { + int read_rc = hid_read_timeout(test_dev, reply, sizeof(reply), 50); + + if(read_rc <= 0) + { + continue; + } + + if(reply[0] == 0x11 && reply[2] == 0x00 && reply[3] == 0x00) + { + probe_ok = true; + break; + } + + if(reply[0] == 0x10 && reply[2] == 0x8F) + { + LOG_DEBUG("%s Scan: %s probe rejected — err=0x%02X (stale slot)", + LOG_TAG, cand.dev_path.c_str(), reply[5]); + break; + } + } + } + + if(!probe_ok) + { + hid_close(test_dev); + continue; + } + + LOG_DEBUG("%s Scan: %s probe accepted (feat_idx=0x%02X)", + LOG_TAG, cand.dev_path.c_str(), reply[4]); + + found_path = cand.dev_path; + found_dev = test_dev; + break; + } + + if(!found_dev) + { + return false; + } + + LOG_INFO("%s Device migrated: %s -> %s", + LOG_TAG, location.c_str(), found_path.c_str()); + + SwapHIDHandle(found_dev, found_path); + return true; +} diff --git a/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20IdleSettings.cpp b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20IdleSettings.cpp new file mode 100644 index 0000000..40db645 --- /dev/null +++ b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20IdleSettings.cpp @@ -0,0 +1,146 @@ +/*---------------------------------------------------------*\ +| LogitechHIDPP20IdleSettings.cpp | +| | +| Host-side idle/dim/sleep settings storage helper for | +| Logitech HID++ 2.0 devices. Loads the configuration | +| block from SettingsManager JSON, caches it, and writes | +| changes back through SettingsManager::SetSettings(). | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogitechHIDPP20IdleSettings.h" +#include "ResourceManager.h" +#include "SettingsManager.h" + +static const char* SETTINGS_KEY = "LogitechHIDPP20IdleSettings"; + +LogitechHIDPP20IdleSettings* LogitechHIDPP20IdleSettings::instance() +{ + static LogitechHIDPP20IdleSettings inst; + return &inst; +} + +/*---------------------------------------------------------*\ +| Minimum idle / sleep values the controller will honor. | +| Matches what Logitech firmware typically clamps to on | +| HID++ 2.0 devices, and guarantees the skip-dim path's | +| sleep_delay math always produces a positive window. | +\*---------------------------------------------------------*/ +static const int MIN_IDLE_S = 15; +static const int MIN_SLEEP_S = 45; +static const int MIN_DIM_WINDOW = 30; // sleep must exceed idle by at least this + +static void ClampProfile(LogitechHIDPP20IdleProfile& p) +{ + if(p.dim_brightness < 0) p.dim_brightness = 0; + if(p.dim_brightness > 100) p.dim_brightness = 100; + + if(p.idle_timeout_s < MIN_IDLE_S) + { + p.idle_timeout_s = MIN_IDLE_S; + } + + if(p.sleep_timeout_s < MIN_SLEEP_S) + { + p.sleep_timeout_s = MIN_SLEEP_S; + } + + if(p.sleep_timeout_s < p.idle_timeout_s + MIN_DIM_WINDOW) + { + p.sleep_timeout_s = p.idle_timeout_s + MIN_DIM_WINDOW; + } +} + +static LogitechHIDPP20IdleProfile ProfileFromJson(const json& j) +{ + LogitechHIDPP20IdleProfile p; + + if(j.contains("dim_when_idle")) p.dim_when_idle = j["dim_when_idle"]; + if(j.contains("dim_brightness")) p.dim_brightness = j["dim_brightness"]; + if(j.contains("idle_timeout_s")) p.idle_timeout_s = j["idle_timeout_s"]; + if(j.contains("allow_sleep")) p.allow_sleep = j["allow_sleep"]; + if(j.contains("sleep_timeout_s")) p.sleep_timeout_s = j["sleep_timeout_s"]; + + ClampProfile(p); + + return p; +} + +static json ProfileToJson(const LogitechHIDPP20IdleProfile& p) +{ + json j; + + j["dim_when_idle"] = p.dim_when_idle; + j["dim_brightness"] = p.dim_brightness; + j["idle_timeout_s"] = p.idle_timeout_s; + j["allow_sleep"] = p.allow_sleep; + j["sleep_timeout_s"] = p.sleep_timeout_s; + + return j; +} + +void LogitechHIDPP20IdleSettings::load() +{ + json settings = ResourceManager::get()->GetSettingsManager()->GetSettings(SETTINGS_KEY); + + /*---------------------------------------------------------*\ + | Empty / missing key means the plugin is not in use. | + | Reset both profiles to defaults with configured=false so | + | the controller defers to firmware. | + \*---------------------------------------------------------*/ + if(!settings.is_object() || settings.empty()) + { + configured = false; + on_battery = LogitechHIDPP20IdleProfile{}; + plugged_in = LogitechHIDPP20IdleProfile{}; + return; + } + + configured = true; + + if(settings.contains("on_battery")) + { + on_battery = ProfileFromJson(settings["on_battery"]); + } + else + { + on_battery = LogitechHIDPP20IdleProfile{}; + } + + if(settings.contains("plugged_in")) + { + plugged_in = ProfileFromJson(settings["plugged_in"]); + } + else + { + plugged_in = LogitechHIDPP20IdleProfile{}; + } +} + +void LogitechHIDPP20IdleSettings::save() +{ + json settings; + + settings["on_battery"] = ProfileToJson(on_battery); + settings["plugged_in"] = ProfileToJson(plugged_in); + + SettingsManager* mgr = ResourceManager::get()->GetSettingsManager(); + mgr->SetSettings(SETTINGS_KEY, settings); + mgr->SaveSettings(); + + configured = true; +} + +void LogitechHIDPP20IdleSettings::setOnBattery(const LogitechHIDPP20IdleProfile& p) +{ + on_battery = p; + configured = true; +} + +void LogitechHIDPP20IdleSettings::setPluggedIn(const LogitechHIDPP20IdleProfile& p) +{ + plugged_in = p; + configured = true; +} diff --git a/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20IdleSettings.h b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20IdleSettings.h new file mode 100644 index 0000000..399526b --- /dev/null +++ b/Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20IdleSettings.h @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| LogitechHIDPP20IdleSettings.h | +| | +| Host-side idle/dim/sleep configuration for Logitech | +| HID++ 2.0 devices. Two profiles (on_battery, plugged_in)| +| selected at runtime based on the device's external- | +| power flag. `configured == false` means the JSON key | +| is absent entirely — the controller defers to firmware. | +| Qt-free so the controller can consume it directly. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +struct LogitechHIDPP20IdleProfile +{ + bool dim_when_idle = false; + int dim_brightness = 50; + int idle_timeout_s = 60; + bool allow_sleep = false; + int sleep_timeout_s = 300; +}; + +class LogitechHIDPP20IdleSettings +{ +public: + static LogitechHIDPP20IdleSettings* instance(); + + void load(); + void save(); + + bool isConfigured() const { return configured; } + const LogitechHIDPP20IdleProfile& onBattery() const { return on_battery; } + const LogitechHIDPP20IdleProfile& pluggedIn() const { return plugged_in; } + + void setOnBattery(const LogitechHIDPP20IdleProfile& p); + void setPluggedIn(const LogitechHIDPP20IdleProfile& p); + +private: + LogitechHIDPP20IdleSettings() = default; + + bool configured = false; + LogitechHIDPP20IdleProfile on_battery; + LogitechHIDPP20IdleProfile plugged_in; +}; diff --git a/Controllers/LogitechController/LogitechHIDPP20Controller/RGBController_LogitechHIDPP20.cpp b/Controllers/LogitechController/LogitechHIDPP20Controller/RGBController_LogitechHIDPP20.cpp new file mode 100644 index 0000000..ccb5d0b --- /dev/null +++ b/Controllers/LogitechController/LogitechHIDPP20Controller/RGBController_LogitechHIDPP20.cpp @@ -0,0 +1,1796 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechHIDPP20.cpp | +| | +| RGBController for unified Logitech HID++ 2.0 devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "RGBController_LogitechHIDPP20.h" +#include "RGBControllerKeyNames.h" +#include "KeyboardLayoutManager.h" +#include "LogManager.h" + +/*----------------------------------------------------------*\ +| Sentinel for "this LED's last write didn't ACK". | +| Stored in sent_colors[i] to force the next frame to | +| re-push the LED regardless of color delta. The high byte | +| 0xFF is unreachable from any ToRGBColor(r,g,b) value | +| (those have high byte 0), so the sentinel never collides | +| with a real color including black (0x00000000) or | +| white (0x00FFFFFF). | +\*----------------------------------------------------------*/ +static constexpr RGBColor HIDPP20_UNCOMMITTED = 0xFF000000; + +/*---------------------------------------------------------*\ +| Effect period range, in milliseconds. Matches the | +| 1000..20000ms range Logitech firmware tests against and | +| the vendor app clamps to in observed wire captures. | +| Out-of-band values can | +| produce flashy / invisible animations on real hardware, | +| so the slider stays clamped here on our side. | +\*---------------------------------------------------------*/ +static const uint16_t HIDPP20_PERIOD_MIN_MS = 1000; +static const uint16_t HIDPP20_PERIOD_MAX_MS = 20000; + +/*---------------------------------------------------------*\ +| Ripple has its own narrower, much faster period range. | +| Values taken from G915's LOGITECH_G915_SPEED_RIPPLE_* | +| constants: 2ms..200ms. A ripple feels right when quick — | +| using the breathing range (1..20s) makes it invisible. | +\*---------------------------------------------------------*/ +static const uint16_t HIDPP20_RIPPLE_PERIOD_MIN_MS = 2; +static const uint16_t HIDPP20_RIPPLE_PERIOD_MAX_MS = 200; + +/*---------------------------------------------------------*\ +| Speed slider range presented to the user. 1..100 matches | +| our brightness convention. Higher = faster animation, | +| inverted on the wire because lower period = faster cycle. | +\*---------------------------------------------------------*/ +static const int HIDPP20_SPEED_SLIDER_MIN = 1; +static const int HIDPP20_SPEED_SLIDER_MAX = 100; + +static uint16_t SliderToPeriodMs(int slider, uint16_t period_min_ms, uint16_t period_max_ms) +{ + if(slider <= HIDPP20_SPEED_SLIDER_MIN) return period_max_ms; + if(slider >= HIDPP20_SPEED_SLIDER_MAX) return period_min_ms; + + const int period_range = period_max_ms - period_min_ms; + const int slider_range = HIDPP20_SPEED_SLIDER_MAX - HIDPP20_SPEED_SLIDER_MIN; + + return (uint16_t)(period_max_ms + - ((slider - HIDPP20_SPEED_SLIDER_MIN) * period_range) / slider_range); +} + +static uint16_t SpeedSliderToPeriodMs(int slider) +{ + return SliderToPeriodMs(slider, HIDPP20_PERIOD_MIN_MS, HIDPP20_PERIOD_MAX_MS); +} + +static uint16_t RippleSpeedSliderToPeriodMs(int slider) +{ + return SliderToPeriodMs(slider, HIDPP20_RIPPLE_PERIOD_MIN_MS, HIDPP20_RIPPLE_PERIOD_MAX_MS); +} + +/*---------------------------------------------------------*\ +| Color Wave 0x0016 carries a direction byte. Map OpenRGB's | +| 6 direction slots onto the Logitech wire values (Solaar | +| LedDirectionChoices). Logitech defines 8 directions; the | +| G515 uses 6 of them, which line up 1:1 with OpenRGB's set | +| (In / Out are not exposed on this keyboard). | +\*---------------------------------------------------------*/ +static uint8_t WaveDirectionToWire(unsigned int dir) +{ + switch(dir) + { + case MODE_DIRECTION_LEFT: return 6; /* Left */ + case MODE_DIRECTION_RIGHT: return 1; /* Right */ + case MODE_DIRECTION_UP: return 7; /* Up */ + case MODE_DIRECTION_DOWN: return 2; /* Down */ + case MODE_DIRECTION_HORIZONTAL: return 3; /* Center Out */ + case MODE_DIRECTION_VERTICAL: return 8; /* Center In */ + default: return 1; /* Right */ + } +} + +/**------------------------------------------------------------------*\ + @name Logitech HID++ 2.0 + @category Keyboard,Mouse,Headset + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechHIDPP20 + @comment + Unified HID++ 2.0 controller that dynamically discovers device + capabilities via feature probing. Supports per-key lighting + (0x8081/0x8080) and zone-based effects (0x8071/0x8070). +\*-------------------------------------------------------------------*/ + +static const char* zone_location_name(uint16_t location) +{ + switch(location) + { + case 0x0001: return "All"; + case 0x0002: return "Primary"; + case 0x0003: return "Combined"; + case 0x0004: return "Logo"; + case 0x0005: return "Left"; + case 0x0006: return "Right"; + case 0x0007: return "Group 1"; + case 0x0008: return "Group 2"; + case 0x0009: return "Group 3"; + case 0x000A: return "Group 4"; + case 0x000B: return "Group 5"; + case 0x2000: return "Top"; + case 0x4000: return "Bottom"; + default: + { + static char buf[16]; + snprintf(buf, sizeof(buf), "Zone 0x%04X", location); + return buf; + } + } +} + +/*---------------------------------------------------------*\ +| HID++ per-key zone ID to OpenRGB key name mapping | +| Zone IDs follow Solaar's KEYCODES (special_keys.py) | +| Used to look up zone IDs by key name after KLM builds | +| the keymap in its own sorted order. | +\*---------------------------------------------------------*/ +static const std::map hidpp20_key_name_to_zone = +{ + { KEY_EN_A, 1 }, + { KEY_EN_B, 2 }, + { KEY_EN_C, 3 }, + { KEY_EN_D, 4 }, + { KEY_EN_E, 5 }, + { KEY_EN_F, 6 }, + { KEY_EN_G, 7 }, + { KEY_EN_H, 8 }, + { KEY_EN_I, 9 }, + { KEY_EN_J, 10 }, + { KEY_EN_K, 11 }, + { KEY_EN_L, 12 }, + { KEY_EN_M, 13 }, + { KEY_EN_N, 14 }, + { KEY_EN_O, 15 }, + { KEY_EN_P, 16 }, + { KEY_EN_Q, 17 }, + { KEY_EN_R, 18 }, + { KEY_EN_S, 19 }, + { KEY_EN_T, 20 }, + { KEY_EN_U, 21 }, + { KEY_EN_V, 22 }, + { KEY_EN_W, 23 }, + { KEY_EN_X, 24 }, + { KEY_EN_Y, 25 }, + { KEY_EN_Z, 26 }, + { KEY_EN_1, 27 }, + { KEY_EN_2, 28 }, + { KEY_EN_3, 29 }, + { KEY_EN_4, 30 }, + { KEY_EN_5, 31 }, + { KEY_EN_6, 32 }, + { KEY_EN_7, 33 }, + { KEY_EN_8, 34 }, + { KEY_EN_9, 35 }, + { KEY_EN_0, 36 }, + { KEY_EN_ANSI_ENTER, 37 }, + { KEY_EN_ESCAPE, 38 }, + { KEY_EN_BACKSPACE, 39 }, + { KEY_EN_TAB, 40 }, + { KEY_EN_SPACE, 41 }, + { KEY_EN_MINUS, 42 }, + { KEY_EN_EQUALS, 43 }, + { KEY_EN_LEFT_BRACKET, 44 }, + { KEY_EN_RIGHT_BRACKET, 45 }, + { KEY_EN_ANSI_BACK_SLASH, 46 }, + { KEY_EN_SEMICOLON, 48 }, + { KEY_EN_QUOTE, 49 }, + { KEY_EN_BACK_TICK, 50 }, + { KEY_EN_COMMA, 51 }, + { KEY_EN_PERIOD, 52 }, + { KEY_EN_FORWARD_SLASH, 53 }, + { KEY_EN_CAPS_LOCK, 54 }, + { KEY_EN_F1, 55 }, + { KEY_EN_F2, 56 }, + { KEY_EN_F3, 57 }, + { KEY_EN_F4, 58 }, + { KEY_EN_F5, 59 }, + { KEY_EN_F6, 60 }, + { KEY_EN_F7, 61 }, + { KEY_EN_F8, 62 }, + { KEY_EN_F9, 63 }, + { KEY_EN_F10, 64 }, + { KEY_EN_F11, 65 }, + { KEY_EN_F12, 66 }, + { KEY_EN_PRINT_SCREEN, 67 }, + { KEY_EN_SCROLL_LOCK, 68 }, + { KEY_EN_PAUSE_BREAK, 69 }, + { KEY_EN_INSERT, 70 }, + { KEY_EN_HOME, 71 }, + { KEY_EN_PAGE_UP, 72 }, + { KEY_EN_DELETE, 73 }, + { KEY_EN_END, 74 }, + { KEY_EN_PAGE_DOWN, 75 }, + { KEY_EN_RIGHT_ARROW, 76 }, + { KEY_EN_LEFT_ARROW, 77 }, + { KEY_EN_DOWN_ARROW, 78 }, + { KEY_EN_UP_ARROW, 79 }, + { KEY_EN_RIGHT_FUNCTION, 111 }, + { KEY_EN_MENU, 98 }, + + /*------------------------------------------------------*\ + | Numpad zones (Solaar KEYCODES 80-96). | + | Required for any full-size HID++ keyboard. | + \*------------------------------------------------------*/ + { KEY_EN_NUMPAD_LOCK, 80 }, + { KEY_EN_NUMPAD_DIVIDE, 81 }, + { KEY_EN_NUMPAD_TIMES, 82 }, + { KEY_EN_NUMPAD_MINUS, 83 }, + { KEY_EN_NUMPAD_PLUS, 84 }, + { KEY_EN_NUMPAD_ENTER, 85 }, + { KEY_EN_NUMPAD_1, 86 }, + { KEY_EN_NUMPAD_2, 87 }, + { KEY_EN_NUMPAD_3, 88 }, + { KEY_EN_NUMPAD_4, 89 }, + { KEY_EN_NUMPAD_5, 90 }, + { KEY_EN_NUMPAD_6, 91 }, + { KEY_EN_NUMPAD_7, 92 }, + { KEY_EN_NUMPAD_8, 93 }, + { KEY_EN_NUMPAD_9, 94 }, + { KEY_EN_NUMPAD_0, 95 }, + { KEY_EN_NUMPAD_PERIOD, 96 }, + + { KEY_EN_LEFT_CONTROL, 104 }, + { KEY_EN_LEFT_SHIFT, 105 }, + { KEY_EN_LEFT_ALT, 106 }, + { KEY_EN_LEFT_WINDOWS, 107 }, + { KEY_EN_RIGHT_CONTROL, 108 }, + { KEY_EN_RIGHT_SHIFT, 109 }, + { KEY_EN_RIGHT_ALT, 110 }, + { KEY_EN_RIGHT_WINDOWS, 111 }, + + /*------------------------------------------------------*\ + | G915 (and similar) out-of-KLM LEDs. | + | Zone IDs from Solaar KEYCODES. Names match the legacy | + | G915 controller so existing users don't see their LED | + | labels change when they move onto the unified driver. | + \*------------------------------------------------------*/ + { "Key: Brightness", 153 }, + { KEY_EN_MEDIA_PLAY_PAUSE, 155 }, + { KEY_EN_MEDIA_MUTE, 156 }, + { KEY_EN_MEDIA_NEXT, 157 }, + { KEY_EN_MEDIA_PREVIOUS, 158 }, + { "Key: G1", 180 }, + { "Key: G2", 181 }, + { "Key: G3", 182 }, + { "Key: G4", 183 }, + { "Key: G5", 184 }, + { "Logo", 210 }, +}; + +/*---------------------------------------------------------*\ +| Mouse LED layout table | +| Each entry defines a matrix layout for a known mouse. | +| Looked up by substring match on device name. | +| To add a new mouse: add an entry with name pattern, | +| grid dimensions, LED count, map, and LED names. | +\*---------------------------------------------------------*/ +#define ML_NA 0xFFFFFFFF + +struct MouseLayout +{ + const char* name_match; + unsigned int rows; + unsigned int cols; + unsigned int led_count; + const unsigned int* map; + const char* const* led_names; +}; + +static const unsigned int g502x_map[3 * 7] = +{ + /* C . . . . . B */ + 2, ML_NA, ML_NA, ML_NA, ML_NA, ML_NA, 1, + /* . D H G F E . */ + ML_NA, 3, 7, 6, 5, 4, ML_NA, + /* . . . . . . A */ + ML_NA, ML_NA, ML_NA, ML_NA, ML_NA, ML_NA, 0, +}; + +static const char* g502x_led_names[] = +{ + "LED A", "LED B", "LED C", "LED D", + "LED E", "LED F", "LED G", "LED H", +}; + +static const MouseLayout known_mouse_layouts[] = +{ + { "G502 X", 3, 7, 8, g502x_map, g502x_led_names }, + /*-------------------------------------------------------*\ + | Add new mice here: | + | { "G PRO X", rows, cols, count, map_ptr, names_ptr }, | + \*-------------------------------------------------------*/ + { nullptr, 0, 0, 0, nullptr, nullptr } +}; + +static const MouseLayout* FindMouseLayout(const std::string& device_name) +{ + for(const MouseLayout* ml = known_mouse_layouts; ml->name_match != nullptr; ml++) + { + if(device_name.find(ml->name_match) != std::string::npos) + { + return ml; + } + } + + return nullptr; +} + +RGBController_LogitechHIDPP20::RGBController_LogitechHIDPP20(LogitechHIDPP20Controller* controller_ptr) +{ + controller = controller_ptr; + + const HIDPP20DeviceCapabilities& caps = controller->GetCapabilities(); + + name = caps.device_name; + vendor = "Logitech"; + description = "Logitech HID++ 2.0 Device"; + version = caps.firmware_version; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + switch(caps.device_type) + { + case LOGITECH_DEVICE_TYPE_KEYBOARD: + type = DEVICE_TYPE_KEYBOARD; + break; + case LOGITECH_DEVICE_TYPE_MOUSE: + case LOGITECH_DEVICE_TYPE_TRACKBALL: + type = DEVICE_TYPE_MOUSE; + break; + case LOGITECH_DEVICE_TYPE_HEADSET: + type = DEVICE_TYPE_HEADSET; + break; + case LOGITECH_DEVICE_TYPE_MOUSEPAD: + type = DEVICE_TYPE_MOUSEMAT; + break; + default: + type = DEVICE_TYPE_UNKNOWN; + break; + } + + /*----------------------------------------------------------*\ + | Build mode list from discovered capabilities | + \*----------------------------------------------------------*/ + + /*----------------------------------------------------------*\ + | Direct mode: per-key control via 0x8081 | + \*----------------------------------------------------------*/ + if(caps.has_perkey) + { + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + } + + /*----------------------------------------------------------*\ + | Off mode: always available | + \*----------------------------------------------------------*/ + { + mode Off; + Off.name = "Off"; + Off.value = 0xFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + } + + /*----------------------------------------------------------*\ + | 0x0620 Headset RGB Hostmode has no effect cards. Provide | + | a single Direct mode that maps every LED to the frame | + | buffer; SetHeadsetRGBHostmodeColors writes them straight | + | to the earcup zones. | + \*----------------------------------------------------------*/ + if(caps.is_headset_rgb_hostmode) + { + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + } + + /*----------------------------------------------------------*\ + | Effect modes from zone cluster discovery | + | Scan effects from the first cluster (effects are usually | + | the same across clusters) | + \*----------------------------------------------------------*/ + if(caps.has_zone_effects && !caps.zone_clusters.empty() && !caps.is_headset_rgb_hostmode) + { + const HIDPP20ZoneCluster& cluster = caps.zone_clusters[0]; + + for(size_t i = 0; i < cluster.effects.size(); i++) + { + const HIDPP20Effect& fx = cluster.effects[i]; + + switch(fx.effect_id) + { + case 0x0001: // Static / Fixed Color + { + mode Static; + Static.name = "Static"; + Static.value = fx.index; + + /*-----------------------------------------------------*\ + | Multi-cluster devices (mice with logo/scroll/DPI) | + | get per-LED colors so each zone can be painted | + | independently in Static. Single-cluster devices | + | (keyboards, single-zone mice) keep the single-color | + | MODE_COLORS_MODE_SPECIFIC UX. | + \*-----------------------------------------------------*/ + if(caps.zone_clusters.size() > 1) + { + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + } + else + { + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + } + modes.push_back(Static); + break; + } + + case 0x0003: // Color Cycle / Spectrum + { + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = fx.index; + Cycle.flags = MODE_FLAG_HAS_SPEED + | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.speed_min = HIDPP20_SPEED_SLIDER_MIN; + Cycle.speed_max = HIDPP20_SPEED_SLIDER_MAX; + Cycle.speed = 80; /* ~4.9s, lively medium */ + Cycle.brightness_min = 1; + Cycle.brightness_max = 100; + Cycle.brightness = 100; + Cycle.color_mode = MODE_COLORS_NONE; + modes.push_back(Cycle); + break; + } + + case 0x000A: // Breathing + { + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = fx.index; + Breathing.speed_min = HIDPP20_SPEED_SLIDER_MIN; + Breathing.speed_max = HIDPP20_SPEED_SLIDER_MAX; + Breathing.speed = 70; /* ~6.8s, calm medium */ + Breathing.brightness_min = 1; + Breathing.brightness_max = 100; + Breathing.brightness = 100; + + /*-----------------------------------------------------*\ + | See Static above — multi-cluster gets per-LED colors. | + \*-----------------------------------------------------*/ + if(caps.zone_clusters.size() > 1) + { + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR + | MODE_FLAG_HAS_SPEED + | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_PER_LED; + } + else + { + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR + | MODE_FLAG_HAS_SPEED + | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + } + modes.push_back(Breathing); + break; + } + + case 0x0004: // Color Wave + { + mode Wave; + Wave.name = "Color Wave"; + Wave.value = fx.index; + Wave.flags = MODE_FLAG_HAS_SPEED + | MODE_FLAG_HAS_BRIGHTNESS; + Wave.speed_min = HIDPP20_SPEED_SLIDER_MIN; + Wave.speed_max = HIDPP20_SPEED_SLIDER_MAX; + Wave.speed = 80; /* ~4.9s, lively medium */ + Wave.brightness_min = 1; + Wave.brightness_max = 100; + Wave.brightness = 100; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + break; + } + + case 0x000B: // Ripple + { + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = fx.index; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR + | MODE_FLAG_HAS_SPEED + | MODE_FLAG_HAS_BRIGHTNESS; + Ripple.speed_min = HIDPP20_SPEED_SLIDER_MIN; + Ripple.speed_max = HIDPP20_SPEED_SLIDER_MAX; + Ripple.speed = 70; /* ~6.8s, calm medium */ + Ripple.brightness_min = 1; + Ripple.brightness_max = 100; + Ripple.brightness = 100; + Ripple.colors_min = 1; + Ripple.colors_max = 1; + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripple.colors.resize(1); + modes.push_back(Ripple); + break; + } + + case 0x0015: // Cycle (saturation variant) + { + /*-----------------------------------------------*\ + | Saturation-bearing variant of 0x0003. Same UI | + | (speed = period, brightness = intensity); the | + | saturation byte is hardcoded full on the wire. | + \*-----------------------------------------------*/ + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = fx.index; + Cycle.flags = MODE_FLAG_HAS_SPEED + | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.speed_min = HIDPP20_SPEED_SLIDER_MIN; + Cycle.speed_max = HIDPP20_SPEED_SLIDER_MAX; + Cycle.speed = 80; /* ~4.9s, lively medium */ + Cycle.brightness_min = 1; + Cycle.brightness_max = 100; + Cycle.brightness = 100; + Cycle.color_mode = MODE_COLORS_NONE; + modes.push_back(Cycle); + break; + } + + case 0x0016: // Wave (saturation variant) + { + /*-----------------------------------------------*\ + | Saturation-bearing variant of 0x0004. Period | + | is a BE16 ms value on the standard 1..20s range | + | (Solaar's LEDEffects table has no period range | + | override for Wave); saturation is hardcoded on | + | the wire. | + \*-----------------------------------------------*/ + mode Wave; + Wave.name = "Color Wave"; + Wave.value = fx.index; + Wave.flags = MODE_FLAG_HAS_SPEED + | MODE_FLAG_HAS_BRIGHTNESS + | MODE_FLAG_HAS_DIRECTION_LR + | MODE_FLAG_HAS_DIRECTION_UD + | MODE_FLAG_HAS_DIRECTION_HV; + Wave.speed_min = HIDPP20_SPEED_SLIDER_MIN; + Wave.speed_max = HIDPP20_SPEED_SLIDER_MAX; + Wave.speed = 80; /* ~4.9s, lively medium */ + Wave.brightness_min = 1; + Wave.brightness_max = 100; + Wave.brightness = 100; + Wave.direction = MODE_DIRECTION_RIGHT; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + break; + } + + case 0x0017: // Ripple (saturation variant) + { + /*-----------------------------------------------*\ + | Saturation-bearing variant of 0x000B. Carries | + | color + period only — no intensity param, so | + | no brightness slider. Saturation is hardcoded | + | full on the wire. | + \*-----------------------------------------------*/ + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = fx.index; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR + | MODE_FLAG_HAS_SPEED; + Ripple.speed_min = HIDPP20_SPEED_SLIDER_MIN; + Ripple.speed_max = HIDPP20_SPEED_SLIDER_MAX; + Ripple.speed = 70; /* mid, fast ripple range */ + Ripple.colors_min = 1; + Ripple.colors_max = 1; + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripple.colors.resize(1); + modes.push_back(Ripple); + break; + } + + default: + break; + } + } + } + + /*---------------------------------------------------------*\ + | On 0x8070 devices every effect write is ephemeral by | + | default (see DeviceUpdateMode persist branch below). Add | + | a Save button on firmware-effect modes so users can | + | explicitly commit the active mode to NVM. Direct is | + | excluded because per-key framebuffer writes don't map to | + | a savable firmware effect on 0x8070. 0x8071/0x0600 | + | already persist on every write, so no Save button is | + | exposed there pending further research. | + \*---------------------------------------------------------*/ + if(caps.rgb_feature_page == HIDPP20_FEAT_COLOR_LED_EFFECTS) + { + for(size_t i = 0; i < modes.size(); i++) + { + if(modes[i].name != "Direct") + { + modes[i].flags |= MODE_FLAG_MANUAL_SAVE; + } + } + } + + SetupZones(); + + /*----------------------------------------------------------*\ + | Register repaint callback and start power manager. | + | The callback triggers DeviceUpdateLEDs from the power | + | thread for dim/wake when no animation is driving updates. | + \*----------------------------------------------------------*/ + controller->SetRepaintCallback( + std::bind(&RGBController_LogitechHIDPP20::OnRepaintRequest, this)); + + controller->SetReapplyActiveModeCallback( + std::bind(&RGBController_LogitechHIDPP20::ReapplyActiveMode, this)); +} + +/*---------------------------------------------------------------*\ +| Repaint callback handler (request_repaint_fn). Invoked from the | +| power thread for dim/wake when no animation is driving updates. | +\*---------------------------------------------------------------*/ +void RGBController_LogitechHIDPP20::OnRepaintRequest() +{ + /*-------------------------------------------------*\ + | If Wake() signaled a full repaint, invalidate | + | sent_colors so DeviceUpdateLEDs pushes every zone | + | regardless of delta. Uses HIDPP20_UNCOMMITTED | + | rather than clear() so sent_colors is non-empty — | + | that avoids the first_frame / prep trigger while | + | still forcing a full push. | + \*-------------------------------------------------*/ + if(controller->ConsumeWakeFullRepaint()) + { + for(size_t i = 0; i < sent_colors.size(); i++) + { + sent_colors[i] = HIDPP20_UNCOMMITTED; + } + } + DeviceUpdateLEDs(); +} + +RGBController_LogitechHIDPP20::~RGBController_LogitechHIDPP20() +{ + controller->StopPowerManager(); + delete controller; +} + +void RGBController_LogitechHIDPP20::SetupZones() +{ + const HIDPP20DeviceCapabilities& caps = controller->GetCapabilities(); + + led_to_zone_id.clear(); + sent_colors.clear(); + + if(caps.has_perkey) + { + if(caps.device_type == LOGITECH_DEVICE_TYPE_KEYBOARD) + { + /*--------------------------------------------------*\ + | Keyboard: use KeyboardLayoutManager for matrix | + | layout. Derive size from numpad presence, layout | + | from 0x4540 KeyboardLayout feature. | + \*--------------------------------------------------*/ + KEYBOARD_SIZE kb_size = caps.has_numpad + ? KEYBOARD_SIZE_FULL + : KEYBOARD_SIZE_TKL; + + KEYBOARD_LAYOUT kb_layout; + + switch(caps.keyboard_layout_code) + { + case 3: // German + case 7: // Swiss + kb_layout = KEYBOARD_LAYOUT_ISO_QWERTZ; + break; + + case 4: // French + kb_layout = KEYBOARD_LAYOUT_ISO_AZERTY; + break; + + case 2: // UK + case 5: // Spanish + case 0x0B: // Italian + case 0x0D: // Portuguese + case 0x0E: // Belgian + case 0x0F: // Scandinavian + case 8: // Nordic + case 0x16: // Nordic + case 0x1D: // Nordic + case 0x21: // Nordic + case 0x24: // Belgian + kb_layout = KEYBOARD_LAYOUT_ISO_QWERTY; + break; + + case 9: // Japanese + case 0x3E: // Japanese + kb_layout = KEYBOARD_LAYOUT_JIS; + break; + + case 1: // US + default: + kb_layout = KEYBOARD_LAYOUT_ANSI_QWERTY; + break; + } + + KeyboardLayoutManager klm(kb_layout, kb_size); + + zone perkey_zone; + perkey_zone.name = ZONE_EN_KEYBOARD; + perkey_zone.type = ZONE_TYPE_MATRIX; + perkey_zone.leds_min = klm.GetKeyCount(); + perkey_zone.leds_max = klm.GetKeyCount(); + perkey_zone.leds_count = klm.GetKeyCount(); + + matrix_map_type* new_map = new matrix_map_type; + new_map->height = klm.GetRowCount(); + new_map->width = klm.GetColumnCount(); + new_map->map = new unsigned int[new_map->height * new_map->width]; + klm.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT, + new_map->height, new_map->width); + perkey_zone.matrix_map = new_map; + zones.push_back(perkey_zone); + + for(unsigned int i = 0; i < klm.GetKeyCount(); i++) + { + led new_led; + std::string key_name = klm.GetKeyNameAt(i); + new_led.name = key_name; + + /*---------------------------------------------*\ + | Look up zone ID by key name | + \*---------------------------------------------*/ + std::map::const_iterator it = hidpp20_key_name_to_zone.find(key_name); + unsigned int zone_id = (it != hidpp20_key_name_to_zone.end()) ? it->second : 0; + new_led.value = zone_id; + leds.push_back(new_led); + + led_to_zone_id.push_back((uint16_t)zone_id); + } + + /*-------------------------------------------------*\ + | Extras: zones the device reported via paginated | + | 0x8081 GetInfo that aren't covered by KLM. On a | + | G915 this is media keys, G1-G5, brightness, and | + | logo. Append them as a separate linear zone so | + | users can still address them. | + \*-------------------------------------------------*/ + std::set klm_claimed_zones; + for(uint16_t zid : led_to_zone_id) + { + if(zid != 0) + { + klm_claimed_zones.insert(zid); + } + } + + /*---------------------------------------------------*\ + | Extras candidates: zones reported by the device | + | that aren't claimed by KLM AND have a known name | + | in hidpp20_key_name_to_zone. We deliberately DROP | + | unnamed zones — firmware-side GetInfo bitmaps | + | enumerate phantom/reserved slots (G515 reports | + | 47, 97, 99-103, 254 among others) that aren't | + | wired to physical LEDs. Exposing them as "LED N" | + | created ghost entries in the GUI. Treat | + | hidpp20_key_name_to_zone as the curated allowlist. | + \*---------------------------------------------------*/ + std::vector> extras; + for(uint16_t zid : caps.perkey_zone_ids) + { + if(klm_claimed_zones.count(zid) != 0) + { + continue; + } + + std::string label; + for(const std::pair& kv : hidpp20_key_name_to_zone) + { + if(kv.second == zid) + { + label = kv.first; + break; + } + } + + if(label.empty()) + { + LOG_DEBUG("[LogitechHID++2.0 %s] Dropping unnamed per-key zone %u " + "(not in hidpp20_key_name_to_zone)", + name.c_str(), (unsigned)zid); + continue; + } + + extras.emplace_back(zid, label); + } + + if(!extras.empty()) + { + zone extras_zone; + extras_zone.name = "Extras"; + extras_zone.type = ZONE_TYPE_LINEAR; + extras_zone.leds_min = (unsigned int)extras.size(); + extras_zone.leds_max = (unsigned int)extras.size(); + extras_zone.leds_count = (unsigned int)extras.size(); + extras_zone.matrix_map = nullptr; + zones.push_back(extras_zone); + + for(size_t i = 0; i < extras.size(); i++) + { + led new_led; + new_led.name = extras[i].second; + new_led.value = extras[i].first; + leds.push_back(new_led); + led_to_zone_id.push_back(extras[i].first); + } + } + } + else if(const MouseLayout* ml = FindMouseLayout(caps.device_name)) + { + /*--------------------------------------------------*\ + | Known mouse: use table-defined matrix layout | + \*--------------------------------------------------*/ + zone perkey_zone; + perkey_zone.name = "Mouse LEDs"; + perkey_zone.type = ZONE_TYPE_MATRIX; + perkey_zone.leds_min = ml->led_count; + perkey_zone.leds_max = ml->led_count; + perkey_zone.leds_count = ml->led_count; + + matrix_map_type* new_map = new matrix_map_type; + new_map->height = ml->rows; + new_map->width = ml->cols; + new_map->map = new unsigned int[ml->rows * ml->cols]; + memcpy(new_map->map, ml->map, ml->rows * ml->cols * sizeof(unsigned int)); + perkey_zone.matrix_map = new_map; + zones.push_back(perkey_zone); + + for(unsigned int i = 0; i < ml->led_count && i < caps.perkey_zone_ids.size(); i++) + { + led new_led; + new_led.name = ml->led_names[i]; + new_led.value = caps.perkey_zone_ids[i]; + leds.push_back(new_led); + + led_to_zone_id.push_back(caps.perkey_zone_ids[i]); + } + } + else + { + /*-------------------------------------------------*\ + | Other devices: linear zone with auto-named LEDs | + \*-------------------------------------------------*/ + zone perkey_zone; + perkey_zone.name = "LEDs"; + perkey_zone.type = ZONE_TYPE_LINEAR; + perkey_zone.leds_min = (unsigned int)caps.perkey_zone_ids.size(); + perkey_zone.leds_max = (unsigned int)caps.perkey_zone_ids.size(); + perkey_zone.leds_count = (unsigned int)caps.perkey_zone_ids.size(); + perkey_zone.matrix_map = nullptr; + zones.push_back(perkey_zone); + + for(size_t i = 0; i < caps.perkey_zone_ids.size(); i++) + { + led new_led; + new_led.name = "LED " + std::to_string(caps.perkey_zone_ids[i]); + new_led.value = caps.perkey_zone_ids[i]; + leds.push_back(new_led); + + led_to_zone_id.push_back(caps.perkey_zone_ids[i]); + } + } + } + else if(caps.is_headset_rgb_hostmode) + { + /*------------------------------------------------------*\ + | Headset RGB hostmode (0x0620): single linear zone with | + | one LED per discovered earcup zone ID. | + \*------------------------------------------------------*/ + size_t led_count = caps.headset_rgb_hostmode_zone_ids.size(); + + zone headset_zone; + headset_zone.name = "Headset"; + headset_zone.type = ZONE_TYPE_LINEAR; + headset_zone.leds_min = (unsigned int)led_count; + headset_zone.leds_max = (unsigned int)led_count; + headset_zone.leds_count = (unsigned int)led_count; + headset_zone.matrix_map = nullptr; + zones.push_back(headset_zone); + + for(size_t i = 0; i < led_count; i++) + { + led new_led; + new_led.name = (i == 0) ? "Left Earcup" + : (i == 1) ? "Right Earcup" + : "Zone " + std::to_string(i); + new_led.value = caps.headset_rgb_hostmode_zone_ids[i]; + leds.push_back(new_led); + + led_to_zone_id.push_back(caps.headset_rgb_hostmode_zone_ids[i]); + } + } + else if(caps.has_zone_effects) + { + /*------------------------------------------------------*\ + | No per-key: create one zone per cluster | + \*------------------------------------------------------*/ + for(size_t i = 0; i < caps.zone_clusters.size(); i++) + { + const HIDPP20ZoneCluster& cluster = caps.zone_clusters[i]; + + zone new_zone; + new_zone.name = zone_location_name(cluster.location); + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + zones.push_back(new_zone); + + led new_led; + new_led.name = new_zone.name; + new_led.value = cluster.index; + leds.push_back(new_led); + + led_to_zone_id.push_back(cluster.index); + } + } + + /*---------------------------------------------------------*\ + | Build the zone_id -> LED index reverse map. Indexed | + | 0..255 (zone IDs are bytes); -1 marks "no LED for this | + | zone". Used by the FrameEnd commit step to translate the | + | acked_zones list back into LED indices for sent_colors. | + \*---------------------------------------------------------*/ + zone_id_to_led_idx.assign(256, -1); + + for(size_t i = 0; i < led_to_zone_id.size(); i++) + { + uint16_t zid = led_to_zone_id[i]; + if(zid > 0 && zid < 256) + { + zone_id_to_led_idx[zid] = (int)i; + } + } + + SetupColors(); +} + +void RGBController_LogitechHIDPP20::ResizeZone(int /*zone*/, int /*new_size*/) +{ +} + +void RGBController_LogitechHIDPP20::DeviceUpdateLEDs() +{ + if(!controller->IsOnline()) + { + return; + } + + /*----------------------------------------------------------*\ + | Ensure SW control is claimed on first actual color push. | + | Safe here because we have real colors in the buffer. | + \*----------------------------------------------------------*/ + controller->ClaimSWControlIfNeeded(); + + const HIDPP20DeviceCapabilities& caps = controller->GetCapabilities(); + + /*----------------------------------------------------------*\ + | Frame handling during SLEEPING: | + | | + | Default — suppress frames. A suppressed frame cannot | + | wake a device that treats writes as activity, so this is | + | the safe choice when we don't know how a particular | + | firmware handles host traffic during its fade. | + | | + | Quirk-gated — devices flagged FADE_ACCEPTS_WRITES opt out | + | of suppression because their firmware accepts writes | + | without cancelling sleep. Frames flow through SLEEPING | + | until deep sleep starts BUSY-NACKing every FrameEnd; | + | consecutive-failure tracking then sets deep_sleep and the | + | top IsDeepSleep() check takes over. | + | | + | Both paths suppress until Wake() clears the state. | + \*----------------------------------------------------------*/ + if(controller->IsDeepSleep()) + { + return; + } + + if(controller->GetPowerState() == HIDPP20_POWER_SLEEPING + && !(caps.quirks & HIDPP20_QUIRK_FADE_ACCEPTS_WRITES)) + { + return; + } + + /*----------------------------------------------------------*\ + | Feature 0x0620 Headset RGB Hostmode (Centurion G522 / | + | PRO X 2). Static-color only, two earcup zones. Bypasses | + | per-key, SetZoneEffect, and effect-card paths entirely — | + | 0x0620 has none of that. Claim was made once in | + | SetHostMode(); we just write colors + FrameEnd[0x01]. | + \*----------------------------------------------------------*/ + if(caps.is_headset_rgb_hostmode) + { + controller->SetHeadsetRGBHostmodeColors(colors); + return; + } + + if(caps.has_perkey && (unsigned int)active_mode < modes.size() && + modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + uint8_t perkey_idx = (caps.idx_perkey_v2 != 0) ? caps.idx_perkey_v2 : caps.idx_perkey_v1; + + /*------------------------------------------------------*\ + | Detect re-initialization (reconnect, wake from sleep). | + | Device state is unknown — force full resend. | + \*------------------------------------------------------*/ + uint32_t gen = controller->GetInitGeneration(); + + if(gen != last_init_gen) + { + sent_colors.clear(); + last_init_gen = gen; + } + + /*-------------------------------------------------------*\ + | Per-key prep call. Two paths, selected by a runtime | + | capability probe at feature-discovery time: | + | | + | (A) Observed prep via DoObservedPerKeyPrep — two | + | SetEffectByIndex calls on 0x8071 cloned from | + | the observed vendor-app wire behavior. The | + | template bytes at | + | prep1 params[6..7] and the effectIdx at prep2 are | + | parameterized from device-discovery results | + | (caps.effect_card_template[], and | + | caps.zone_clusters[0].effects.size() respectively) | + | so the same code adapts to any device that shares | + | the G502-family prep pattern. | + | | + | Gated on caps.has_effect_cards, which is set by | + | DiscoverEffectCards iff the device responds | + | successfully to GetEffectSpecificInfo. Devices | + | without firmware effect cards leave this false | + | and fall through to path (B). | + | | + | (B) Static-pass-through prep (original fork behavior, | + | doc-verified on G515) — applied when the device | + | has no effect cards or uses 0x8070 / 0x0600 | + | instead of 0x8071. SetEffect cluster=0xFF, | + | effect=Static, RGB=(0,0,0), no fixed-color marker, | + | persist=1. | + | | + | An earlier revision used `effects.size() < 5` as a | + | heuristic proxy for "G502-shaped" devices. The proxy | + | accidentally correlated with "has effect cards" on | + | the two devices we knew about but had no principled | + | meaning — it's been replaced with the direct capability | + | probe. | + \*-------------------------------------------------------*/ + bool needs_prep = controller->NeedsPrepSequence(); + + if(needs_prep && caps.has_zone_effects) + { + bool shape_matches_keyboard_family = + caps.idx_disable_keys_by_usage != 0 + && caps.idx_perkey_v2 != 0 + && caps.rgb_feature_page == HIDPP20_FEAT_RGB_EFFECTS; + + bool shape_matches_observed_prep = + caps.has_effect_cards + && caps.rgb_feature_page == HIDPP20_FEAT_RGB_EFFECTS; + + if(shape_matches_keyboard_family) + { + /*---------------------------------------------*\ + | G815 / G915 / G Pro: per-cluster Off + primer | + | key + FrameEnd. Matches their legacy | + | InitializeDirect wire sequence. | + \*---------------------------------------------*/ + controller->DoKeyboardFamilyPerKeyPrep(); + } + else if(shape_matches_observed_prep) + { + controller->DoObservedPerKeyPrep(); + } + else + { + uint8_t static_effect_idx = 0; + + for(size_t j = 0; j < caps.zone_clusters[0].effects.size(); j++) + { + if(caps.zone_clusters[0].effects[j].effect_id == 0x0001) + { + static_effect_idx = caps.zone_clusters[0].effects[j].index; + break; + } + } + + controller->SetZoneEffect( + 0xFF, /* all clusters */ + static_effect_idx, + 0x0001, /* static effect */ + 0, 0, 0, /* black — no fixed-color marker */ + 0, + 100, /* brightness — unused for static */ + 0, /* direction — unused for static */ + true /* persist=true */); + } + } + + /*------------------------------------------------------*\ + | Snapshot colors to avoid races with effects updating | + | the colors array while we're sending. | + \*------------------------------------------------------*/ + std::vector snapshot(colors.begin(), colors.end()); + + /*-------------------------------------------------------*\ + | Apply dim brightness scaling if not at full brightness. | + | This modifies the OUTPUT only — the internal colors[] | + | buffer stays at full brightness for the animation. | + \*-------------------------------------------------------*/ + int brightness = controller->GetDimBrightness(); + + if(brightness < 100) + { + for(size_t i = 0; i < snapshot.size(); i++) + { + uint8_t r = RGBGetRValue(snapshot[i]) * brightness / 100; + uint8_t g = RGBGetGValue(snapshot[i]) * brightness / 100; + uint8_t b = RGBGetBValue(snapshot[i]) * brightness / 100; + snapshot[i] = ToRGBColor(r, g, b); + } + } + + /*------------------------------------------------------*\ + | Compute delta against last committed state. | + | First call (sent_colors empty) sends everything. | + \*------------------------------------------------------*/ + bool full_update = (sent_colors.size() != snapshot.size()); + + std::map> color_to_zones; + + for(size_t i = 0; i < snapshot.size() && i < led_to_zone_id.size(); i++) + { + if(led_to_zone_id[i] == 0 || led_to_zone_id[i] > 255) + { + continue; + } + + if(full_update || snapshot[i] != sent_colors[i]) + { + color_to_zones[snapshot[i]].push_back((uint8_t)led_to_zone_id[i]); + } + } + + if(color_to_zones.empty()) + { + return; + } + + /*-----------------------------------------------------*\ + | Drain stale ACKs from previous frames before sending. | + | Without this, SendPerKeyData reads a stale ACK, | + | mistakes it for the current write's ACK, returns | + | early, and FrameEnd then races with the actual ACK. | + \*-----------------------------------------------------*/ + controller->FlushResponseQueue(); + + /*------------------------------------------------------*\ + | Batch changed keys for efficient wire encoding. | + | | + | For same-color groups (>= 2 keys): | + | Sort zone IDs and find contiguous runs. | + | fn5 (SET_RANGE) for runs of 3+: | + | [start, end, R, G, B] × 3 per packet | + | fn6 (SET_SINGLE_VALUE) for scattered remainder: | + | [R, G, B, zid, zid, ...] up to 13 per packet | + | | + | For single-occurrence colors: | + | fn1 (SET_INDIVIDUAL): [zid,R,G,B] × 4 per packet | + \*------------------------------------------------------*/ + std::vector> individual_pairs; + + for(std::pair>& entry : color_to_zones) + { + RGBColor color = entry.first; + std::vector& zone_ids = entry.second; + + if(zone_ids.size() >= 2) + { + uint8_t r = RGBGetRValue(color); + uint8_t g = RGBGetGValue(color); + uint8_t b = RGBGetBValue(color); + + /*--------------------------------------------------*\ + | Sort zone IDs and extract contiguous runs for fn5 | + \*--------------------------------------------------*/ + std::sort(zone_ids.begin(), zone_ids.end()); + + std::vector> ranges; + std::vector scattered; + size_t run_start = 0; + + for(size_t i = 1; i <= zone_ids.size(); i++) + { + if(i < zone_ids.size() && zone_ids[i] == zone_ids[i - 1] + 1) + { + continue; + } + + size_t run_len = i - run_start; + + if(run_len >= 3) + { + ranges.push_back({zone_ids[run_start], zone_ids[i - 1]}); + } + else + { + for(size_t j = run_start; j < i; j++) + { + scattered.push_back(zone_ids[j]); + } + } + + run_start = i; + } + + /*--------------------------------------------------*\ + | fn5 (SET_RANGE): 3 range entries per packet. | + | Track every zone in each packet's ranges so the | + | FrameEnd ACK matcher can mark them committed. | + \*--------------------------------------------------*/ + for(size_t i = 0; i < ranges.size(); i += 3) + { + uint8_t data[16] = {}; + std::vector packet_zones; + size_t batch = ranges.size() - i; + if(batch > 3) batch = 3; + + for(size_t j = 0; j < batch; j++) + { + data[j * 5 + 0] = ranges[i + j].first; + data[j * 5 + 1] = ranges[i + j].second; + data[j * 5 + 2] = r; + data[j * 5 + 3] = g; + data[j * 5 + 4] = b; + + for(uint8_t z = ranges[i + j].first; + z <= ranges[i + j].second; z++) + { + packet_zones.push_back(z); + } + } + + controller->SendPerKeyData(perkey_idx, FN_8081_SET_RANGE, + data, batch * 5, packet_zones); + } + + /*--------------------------------------------------*\ + | fn6 (SET_SINGLE_VALUE) for remaining scattered. | + | Track the listed zone IDs in each packet. | + \*--------------------------------------------------*/ + for(size_t i = 0; i < scattered.size(); i += 13) + { + uint8_t data[16] = {}; + std::vector packet_zones; + data[0] = r; + data[1] = g; + data[2] = b; + + size_t batch = scattered.size() - i; + if(batch > 13) batch = 13; + + for(size_t j = 0; j < batch; j++) + { + data[3 + j] = scattered[i + j]; + packet_zones.push_back(scattered[i + j]); + } + + controller->SendPerKeyData(perkey_idx, FN_8081_SET_SINGLE_VALUE, + data, 3 + batch, packet_zones); + } + } + else + { + for(uint8_t zid : zone_ids) + { + individual_pairs.push_back({zid, color}); + } + } + } + + if(!individual_pairs.empty()) + { + controller->SetPerKeyColors(individual_pairs); + } + + PerKeyFrameResult commit = controller->PerKeyFrameEnd(); + + /*-----------------------------------------------------*\ + | A frame is "fully committed" only if FrameEnd ACKed | + | AND every attempted zone also ACKed. FrameEnd alone | + | is not enough — the firmware happily ACKs FrameEnd | + | even when prior per-key writes were silently dropped | + | (observed on G502 X PLUS during wireless reconnect | + | transients, where the Set* writes return no response | + | but FrameEnd still lands cleanly). | + \*-----------------------------------------------------*/ + bool full_commit = commit.frame_end_acked + && (commit.acked_zones.size() + == commit.attempted_zones.size()); + + /*-----------------------------------------------------*\ + | Upgrade SW control flags from 6 → 5 once the per-key | + | layer is populated. ClaimSWControlIfNeeded leaves the | + | device at flags=6 (effect engine still autonomous) to | + | avoid the onboard→host transition flash; now that the | + | per-key layer is masking zone output, it's safe (and | + | required for idle/wake event generation) to claim the | + | effect bit. No-op if the upgrade has already happened | + | or if claim itself hasn't occurred. | + | | + | Gated on full_commit: upgrading into flags=5 with an | + | empty per-key buffer would leave the firmware with | + | nothing to render and expose its default LED buffer | + | (warm-white on the G502 X PLUS). | + \*-----------------------------------------------------*/ + if(full_commit) + { + controller->UpgradeSwControlAfterFirstPaint(); + } + + /*------------------------------------------------------*\ + | Retry scheduling — ONLY on the critical first paint | + | after a fresh claim (needs_prep == true). Streaming | + | animation frames regularly partial-commit due to | + | fire-and-forget timing, and the delta carry-over | + | (HIDPP20_UNCOMMITTED) already handles missed zones on | + | the next animation tick. Scheduling retries on every | + | partial streaming frame causes the power thread's | + | TickRetryPaintIfPending to fire request_repaint_fn | + | between animation frames, colliding with the animation | + | loop and producing visible stalls. | + | | + | For the first-paint-after-claim case (needs_prep), | + | there IS no "next animation frame" guaranteed, so the | + | retry is the only mechanism to recover from a partial | + | commit during the reconnect-transient window. | + \*------------------------------------------------------*/ + if(full_commit || !needs_prep) + { + controller->CancelRetryPaint(); + } + else + { + controller->ScheduleRetryPaint(); + } + + /*-------------------------------------------------------*\ + | Ensure sent_colors is sized to the snapshot before | + | the commit loop writes by index. On the first frame | + | (or after a reinit clear) sent_colors is empty, and | + | the per-zone writes below would silently no-op, | + | leaving sent_colors empty and re-firing the prep call | + | on every subsequent frame. | + | | + | Initial fill is HIDPP20_UNCOMMITTED so any LED that we | + | did not touch this frame stays "uncommitted" and gets | + | scheduled for the next delta. | + \*-------------------------------------------------------*/ + if(sent_colors.size() != snapshot.size()) + { + sent_colors.assign(snapshot.size(), HIDPP20_UNCOMMITTED); + } + + /*-----------------------------------------------------*\ + | Build a fast lookup of acked zones for this frame. | + \*-----------------------------------------------------*/ + std::set acked_set(commit.acked_zones.begin(), + commit.acked_zones.end()); + + if(commit.frame_end_acked) + { + /*----------------------------------------------------*\ + | Frame end ACKed: any zone whose write packet also | + | ACKed is now committed — advance sent_colors for | + | that LED. Any zone we attempted but never saw an | + | ACK for goes to HIDPP20_UNCOMMITTED so the next | + | frame's delta picks it up. | + \*----------------------------------------------------*/ + for(uint8_t zid : commit.attempted_zones) + { + int led_idx = zone_id_to_led_idx[zid]; + if(led_idx < 0 || (size_t)led_idx >= sent_colors.size()) + { + continue; + } + + if(acked_set.count(zid)) + { + sent_colors[led_idx] = snapshot[led_idx]; + } + else + { + sent_colors[led_idx] = HIDPP20_UNCOMMITTED; + } + } + } + else + { + /*---------------------------------------------------*\ + | Frame end timed out: we don't know what the device | + | committed. Mark every attempted LED uncommitted so | + | the next frame re-pushes them all. Don't bother | + | with the per-zone ACK info here — if FrameEnd | + | didn't land, the per-key writes that did ACK still | + | sit in the staging buffer un-swapped. | + \*---------------------------------------------------*/ + for(uint8_t zid : commit.attempted_zones) + { + int led_idx = zone_id_to_led_idx[zid]; + if(led_idx >= 0 && (size_t)led_idx < sent_colors.size()) + { + sent_colors[led_idx] = HIDPP20_UNCOMMITTED; + } + } + } + + } +} + +void RGBController_LogitechHIDPP20::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechHIDPP20::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechHIDPP20::DeviceUpdateMode() +{ + if(!controller->IsOnline()) + { + return; + } + + /*----------------------------------------------------------*\ + | Drop mode changes while the firmware is fading to off. | + | The device owns its own power state — we don't force-wake | + | it from software. active_mode stays tracked framework- | + | side, and the next wake (firmware onUserActivity) or | + | reconnect will re-apply it through the reinit callback. | + \*----------------------------------------------------------*/ + if(controller->GetPowerState() == HIDPP20_POWER_SLEEPING) + { + return; + } + + /*----------------------------------------------------------*\ + | Claim SW control on first mode set (deferred from init). | + \*----------------------------------------------------------*/ + controller->ClaimSWControlIfNeeded(); + + const HIDPP20DeviceCapabilities& caps = controller->GetCapabilities(); + + if((unsigned int)active_mode >= modes.size()) + { + return; + } + + const mode& current = modes[active_mode]; + + /*----------------------------------------------------------*\ + | Direct mode: invalidate delta tracking so the next | + | DeviceUpdateLEDs sends a full frame with actual colors. | + \*----------------------------------------------------------*/ + if(current.name == "Direct") + { + sent_colors.clear(); + DeviceUpdateLEDs(); + + /*------------------------------------------------------*\ + | Start power manager (reader + power threads) if not | + | already running. | + \*------------------------------------------------------*/ + controller->StartPowerManager(); + + if(caps.idx_wireless_status != 0 && !caps.has_power_mgmt) + { + controller->StartEventWatcher(); + } + + return; + } + + sent_colors.clear(); + + /*----------------------------------------------------------*\ + | On per-key devices, single-color non-animated modes (Off, | + | Static) are applied through the per-key path so the LEDs | + | track the mode color cleanly. Animated effects (anything | + | with HAS_SPEED — Breathing, Cycle, Wave, Ripple) fall | + | through to the zone-effect path below; in practice zone | + | effects render correctly alongside per-key on the devices | + | we have data for. | + \*----------------------------------------------------------*/ + if(caps.has_perkey) + { + /*------------------------------------------------------*\ + | Off mode via per-key: set all LEDs to black | + \*------------------------------------------------------*/ + if(current.value == 0xFF) + { + controller->SetAllPerKeyColor(ToRGBColor(0, 0, 0)); + controller->PerKeyFrameEnd(); + return; + } + + /*------------------------------------------------------*\ + | Static mode via per-key: only used as a fallback when | + | the device exposes per-key but no zone effects. When | + | both are available we prefer the zone-effect path | + | because per-key writes alone don't fully claim against | + | the firmware effect engine on some devices (G502), | + | leaving the firmware fade fighting our per-key colors | + | until something else (e.g. Cycle) force-claims. | + | Gated on !HAS_SPEED so animated colored effects like | + | Breathing don't get clipped to a static color. | + \*------------------------------------------------------*/ + if(!caps.has_zone_effects + && current.color_mode == MODE_COLORS_MODE_SPECIFIC + && current.colors.size() > 0 + && !(current.flags & MODE_FLAG_HAS_SPEED)) + { + controller->SetAllPerKeyColor(current.colors[0]); + controller->PerKeyFrameEnd(); + return; + } + + /*------------------------------------------------------*\ + | Animated effects (Breathing, Cycle, Wave, Ripple) on | + | per-key devices fall through to the zone effect path. | + \*------------------------------------------------------*/ + } + + /*----------------------------------------------------------*\ + | Zone effect modes (devices without per-key, or animated | + | effects that can't be done via per-key) | + | | + | persist branching: | + | 0x8070: ephemeral by default; becomes persist=true | + | only when DeviceSaveMode has set save_pending. | + | 0x8071/0x0600: keeps the pre-existing per-branch | + | hardcoded values (Off=false, Effect=true) | + | pending 0x8071 save research. | + \*----------------------------------------------------------*/ + const bool is_8070 = (caps.rgb_feature_page == HIDPP20_FEAT_COLOR_LED_EFFECTS); + + /*----------------------------------------------------------*\ + | Off mode: set static black on all clusters | + \*----------------------------------------------------------*/ + if(current.value == 0xFF) + { + const bool off_persist = is_8070 ? save_pending : false; + + for(size_t i = 0; i < caps.zone_clusters.size(); i++) + { + for(size_t j = 0; j < caps.zone_clusters[i].effects.size(); j++) + { + if(caps.zone_clusters[i].effects[j].effect_id == 0x0001) + { + controller->SetZoneEffect( + caps.zone_clusters[i].index, + caps.zone_clusters[i].effects[j].index, + 0x0001, 0, 0, 0, 0, 100, 0, off_persist); + break; + } + } + } + controller->UpgradeSwControlAfterFirstPaint(); + return; + } + + /*----------------------------------------------------------*\ + | Effect mode: apply to all clusters. | + | | + | Color source per cluster: | + | MODE_COLORS_PER_LED — current.colors[i] maps to | + | caps.zone_clusters[i] | + | (one LED per cluster on the | + | 0x8070 zone path) | + | MODE_COLORS_MODE_SPECIFIC — current.colors[0] for all | + | MODE_COLORS_NONE — zero (effect ignores RGB) | + \*----------------------------------------------------------*/ + uint16_t period = SpeedSliderToPeriodMs(current.speed); + + /*---------------------------------------------------------*\ + | Brightness defaults to 100 for modes that don't expose | + | a brightness slider — those modes ignore the value at the | + | wire level anyway. Modes flagged HAS_BRIGHTNESS take the | + | user-set value from current.brightness. | + \*---------------------------------------------------------*/ + unsigned char brightness = (current.flags & MODE_FLAG_HAS_BRIGHTNESS) + ? (unsigned char)current.brightness + : 100; + + for(size_t i = 0; i < caps.zone_clusters.size(); i++) + { + uint16_t eff_id = 0; + + const std::vector& cluster_effects = caps.zone_clusters[i].effects; + for(size_t j = 0; j < cluster_effects.size(); j++) + { + if(cluster_effects[j].index == (uint8_t)current.value) + { + eff_id = cluster_effects[j].effect_id; + break; + } + } + + unsigned char r = 0, g = 0, b = 0; + + if(current.color_mode == MODE_COLORS_PER_LED && i < current.colors.size()) + { + r = RGBGetRValue(current.colors[i]); + g = RGBGetGValue(current.colors[i]); + b = RGBGetBValue(current.colors[i]); + } + else if(current.color_mode == MODE_COLORS_MODE_SPECIFIC && !current.colors.empty()) + { + r = RGBGetRValue(current.colors[0]); + g = RGBGetGValue(current.colors[0]); + b = RGBGetBValue(current.colors[0]); + } + + /*------------------------------------------------------*\ + | Ripple wants a narrower, much faster period range | + | (2..200ms) than the breathing/wave baseline of 1..20s. | + | Both Ripple variants — 0x000B and the saturation | + | 0x0017 — use the fast range; this mirrors Solaar's | + | LEDEffects table, where only Ripple carries a period | + | range override. Cycle (0x0003/0x0015) and Wave | + | (0x0004/0x0016) stay on the standard 1..20s range. | + \*------------------------------------------------------*/ + uint16_t cluster_period = (eff_id == 0x000B + || eff_id == 0x0017) + ? RippleSpeedSliderToPeriodMs(current.speed) + : period; + + /*-----------------------------------------------------*\ + | 0x8071/0x0600: persist=true matches what the observed | + | vendor-app wire capture does for every mode-set on | + | these | + | devices. With persist=false the firmware appears to | + | accept the command without actually committing the | + | new effect, which is consistent with Static (which | + | gets prepped with persist=true at startup) being the | + | only effect that visibly works. | + | | + | 0x8070: ephemeral (persist=false) on live writes; | + | DeviceSaveMode flips save_pending true to replay the | + | active mode with persist=true and commit to NVM. | + \*-----------------------------------------------------*/ + const bool effect_persist = is_8070 ? save_pending : true; + + controller->SetZoneEffect( + caps.zone_clusters[i].index, + current.value, + eff_id, r, g, b, cluster_period, brightness, + WaveDirectionToWire(current.direction), effect_persist); + } + + /*-----------------------------------------------------------*\ + | The zone effects are now committed — safe to upgrade from | + | flags=6 to flags=5. Without this, devices that only use | + | zone effects (no per-key Direct path) would stay at | + | flags=6 forever and the firmware would never send | + | onUserActivity events for idle/sleep. | + \*-----------------------------------------------------------*/ + controller->UpgradeSwControlAfterFirstPaint(); +} + +void RGBController_LogitechHIDPP20::DeviceSaveMode() +{ + /*----------------------------------------------------------*\ + | 0x8071/0x0600 already write persist=true on every mode | + | change, so the Save button isn't exposed on those pages | + | (MODE_FLAG_MANUAL_SAVE is only set for 0x8070 modes in | + | the constructor). If a save ever lands here from those | + | pages anyway, nothing needs doing — the active mode is | + | already committed to NVM. | + | | + | 0x8070: replay the active mode through DeviceUpdateMode | + | with save_pending true so the zone effect writes go out | + | with persist=true, committing the currently-live effect | + | to flash. | + \*----------------------------------------------------------*/ + const HIDPP20DeviceCapabilities& caps = controller->GetCapabilities(); + if(caps.rgb_feature_page != HIDPP20_FEAT_COLOR_LED_EFFECTS) + { + return; + } + + save_pending = true; + DeviceUpdateMode(); + save_pending = false; +} + +bool RGBController_LogitechHIDPP20::ReapplyActiveMode() +{ + /*-----------------------------------------------------------*\ + | Re-establish the current active_mode on the device. Used | + | by the wake path (after SetRgbPowerMode(1) cancels the | + | firmware fade) and by the reconnect path (after a wireless | + | or USB reconnect). Handles both per-key Direct and zone | + | effect modes: | + | | + | 1. Claim SW control (host mode + flags + power mode). | + | Retried internally; returns true iff the final claim | + | ACKed. ReconnectDevice's fast-backoff loop uses this | + | as the accept signal. | + | 2. Clear sent_colors so the next frame is full-push. | + | Per the 0x8071 lifecycle, the device's LED buffer may | + | not survive mode 3→1 or a full reconnect, so we don't | + | trust it to remember any prior state. | + | 3. Route through DeviceUpdateMode so both per-key Direct | + | (full per-key frame via DeviceUpdateLEDs) and zone | + | effects (SetEffect per cluster) re-establish | + | correctly. Covers the case where the active_mode was | + | changed in the GUI while the device was fading — that | + | mode change was dropped at the time and needs to land | + | here on wake. | + \*-----------------------------------------------------------*/ + bool claimed = controller->ClaimSWControlIfNeeded(); + sent_colors.clear(); + DeviceUpdateMode(); + return claimed; +} diff --git a/Controllers/LogitechController/LogitechHIDPP20Controller/RGBController_LogitechHIDPP20.h b/Controllers/LogitechController/LogitechHIDPP20Controller/RGBController_LogitechHIDPP20.h new file mode 100644 index 0000000..c5d5ba0 --- /dev/null +++ b/Controllers/LogitechController/LogitechHIDPP20Controller/RGBController_LogitechHIDPP20.h @@ -0,0 +1,72 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechHIDPP20.h | +| | +| RGBController for unified Logitech HID++ 2.0 devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechHIDPP20Controller.h" + +class RGBController_LogitechHIDPP20 : public RGBController +{ +public: + RGBController_LogitechHIDPP20(LogitechHIDPP20Controller* controller_ptr); + ~RGBController_LogitechHIDPP20(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + void DeviceSaveMode(); + bool ReapplyActiveMode(); + +private: + LogitechHIDPP20Controller* controller; + + /*---------------------------------------------------------*\ + | Repaint callback handler. Registered with the controller | + | as request_repaint_fn and invoked from the power thread | + | for dim/wake when no animation is driving updates. | + \*---------------------------------------------------------*/ + void OnRepaintRequest(); + + /*---------------------------------------------------------*\ + | When true, the next DeviceUpdateMode cycle sends its | + | SetZoneEffect calls with persist=true instead of the | + | default ephemeral write. Used by DeviceSaveMode to replay | + | the active mode as a NVM-committed effect on 0x8070 | + | devices, which default to non-persistent live writes. | + \*---------------------------------------------------------*/ + bool save_pending = false; + + /*---------------------------------------------------------*\ + | Maps OpenRGB LED index -> HID++ per-key zone ID | + \*---------------------------------------------------------*/ + std::vector led_to_zone_id; + + /*---------------------------------------------------------*\ + | Reverse map: zone_id -> LED index (-1 if no LED). | + | Indexed 0..255 (zone IDs are bytes). Built once in | + | SetupZones to avoid scanning led_to_zone_id at commit | + | time, which would be O(N) per acked zone. | + \*---------------------------------------------------------*/ + std::vector zone_id_to_led_idx; + + /*---------------------------------------------------------*\ + | Last successfully committed colors for delta updates. | + | An entry of HIDPP20_UNCOMMITTED (0xFF000000) marks an LED | + | whose last write didn't ACK and which therefore needs to | + | be re-pushed in the next frame regardless of color delta. | + | The high byte (0xFF) is impossible for any value produced | + | by ToRGBColor() so it never collides with a real color. | + \*---------------------------------------------------------*/ + std::vector sent_colors; + uint32_t last_init_gen = 0; +}; diff --git a/Controllers/LogitechController/LogitechLightspeedController/LogitechLightspeedController.cpp b/Controllers/LogitechController/LogitechLightspeedController/LogitechLightspeedController.cpp new file mode 100644 index 0000000..54db155 --- /dev/null +++ b/Controllers/LogitechController/LogitechLightspeedController/LogitechLightspeedController.cpp @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| LogitechLightspeedController.cpp | +| | +| Driver for Logitech Lightspeed | +| | +| TheRogueZeta 05 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechLightspeedController.h" +#include "StringUtils.h" + +LogitechLightspeedController::LogitechLightspeedController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; +} + +LogitechLightspeedController::~LogitechLightspeedController() +{ + delete lightspeed; +} + +std::string LogitechLightspeedController::GetDeviceLocation() +{ + return("HID: " + location + " (Receiver) \r\nWireless Index: " + std::to_string(lightspeed->device_index)); +} + +std::string LogitechLightspeedController::GetSerialString() +{ + if (lightspeed->device_index == 255 && lightspeed->wireless) + { + LOG_DEBUG("[%s] Skipped get serial number as this is the reciever", lightspeed->device_name.c_str()); + return(""); + } + else + { + wchar_t serial_string[128]; + //int ret = hid_get_serial_number_string(dev, serial_string, 128); + //LOG_DEBUG("[%s] hid_get_serial_number_string Returned status - %i : %s", lightspeed->device_name.c_str(), ret, ((ret == 0) ? "SUCCESS" : "FAILED")); + + //if(ret != 0) + { + return(""); + } + + std::string return_string(StringUtils::wstring_to_string(serial_string)); + + return(return_string); + } +} + +void LogitechLightspeedController::SendMouseMode + ( + uint8_t mode, + uint16_t speed, + uint8_t zone, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t brightness + ) +{ + lightspeed->setMode(mode, speed, zone, red, green, blue, brightness); +} diff --git a/Controllers/LogitechController/LogitechLightspeedController/LogitechLightspeedController.h b/Controllers/LogitechController/LogitechLightspeedController/LogitechLightspeedController.h new file mode 100644 index 0000000..14e3239 --- /dev/null +++ b/Controllers/LogitechController/LogitechLightspeedController/LogitechLightspeedController.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| LogitechLightspeedController.h | +| | +| Driver for Logitech Lightspeed | +| | +| TheRogueZeta 05 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "LogManager.h" +#include "LogitechProtocolCommon.h" + +#define LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MIN 0x01 +#define LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MAX 0x64 + +/*---------------------------------------------------------------------------------------------*\ +| Speed is 1000 for fast and 20000 for slow. | +| Values are multiplied by 100 later to give lots of GUI steps. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_G_PRO_WIRELESS_SPEED_SLOWEST = 0xC8, /* Slowest speed */ + LOGITECH_G_PRO_WIRELESS_SPEED_NORMAL = 0x32, /* Normal speed */ + LOGITECH_G_PRO_WIRELESS_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +class LogitechLightspeedController +{ +public: + LogitechLightspeedController(hid_device* dev_handle, const char* path); + ~LogitechLightspeedController(); + + logitech_device* lightspeed; + + std::string GetDeviceLocation(); + std::string GetSerialString(); + + void SendMouseMode + ( + uint8_t mode, + uint16_t speed, + uint8_t zone, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t brightness + ); + +private: + hid_device* dev; + std::string location; +}; diff --git a/Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.cpp b/Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.cpp new file mode 100644 index 0000000..e21d448 --- /dev/null +++ b/Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.cpp @@ -0,0 +1,248 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechLightspeed.cpp | +| | +| RGBController for Logitech Lightspeed | +| | +| TheRogueZeta 05 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LogitechLightspeed.h" + +/**------------------------------------------------------------------*\ + @name Logitech Lightspeed + @category Keyboard,Mouse,Mousemat,Headset + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectLogitechWireless,DetectLogitechWired + @comment The Lightspeed controller is the generic RGB Controller + for all Logitech HID++ devices that support feature page 8070. +\*-------------------------------------------------------------------*/ + +RGBController_LogitechLightspeed::RGBController_LogitechLightspeed(LogitechLightspeedController* controller_ptr) +{ + controller = controller_ptr; + bool connected = controller->lightspeed->connected(); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_DEVICE_LED_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + if(connected) + { + name = controller->lightspeed->device_name; + vendor = "Logitech"; + description = "Logitech Wireless Lightspeed Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + switch(controller->lightspeed->logitech_device_type) + { + case LOGITECH_DEVICE_TYPE_KEYBOARD: + type = DEVICE_TYPE_KEYBOARD; + break; + + case LOGITECH_DEVICE_TYPE_MOUSE: + type = DEVICE_TYPE_MOUSE; + break; + + case LOGITECH_DEVICE_TYPE_MOUSEPAD: + type = DEVICE_TYPE_MOUSEMAT; + break; + + case LOGITECH_DEVICE_TYPE_HEADSET: + type = DEVICE_TYPE_HEADSET; + break; + + default: + type = DEVICE_TYPE_UNKNOWN; + LOG_INFO("Logitech device type not known: %i", controller->lightspeed->logitech_device_type); + } + + logitech_led fx = controller->lightspeed->getLED_info(0); + + for(uint8_t i = 0; i < fx.fx.size(); i++) + { + /*---------------------------------------------------------*\ + | Logitech devices don't have a set order for effects and | + | each device needs to have the effect index mapped | + \*---------------------------------------------------------*/ + switch(fx.fx[i].mode) + { + case LOGITECH_DEVICE_LED_OFF: + /* Do nothing as it's already added */ + break; + + case LOGITECH_DEVICE_LED_ON: + { + mode Direct; + Direct.name = "Direct"; + Direct.value = i; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = i; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MIN; + Static.brightness_max = LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MAX; + Static.brightness = LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MAX; + modes.push_back(Static); + LOG_DEBUG("[%s] Adding %s & %s modes at index - %02i", name.c_str(), Direct.name.c_str(), Static.name.c_str(), i); + break; + } + + case LOGITECH_DEVICE_LED_SPECTRUM: + { + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = i; + Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.brightness_min = LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MIN; + Cycle.brightness_max = LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MAX; + Cycle.brightness = LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MAX; + Cycle.speed_min = LOGITECH_G_PRO_WIRELESS_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G_PRO_WIRELESS_SPEED_FASTEST; + Cycle.speed = LOGITECH_G_PRO_WIRELESS_SPEED_NORMAL; + modes.push_back(Cycle); + LOG_DEBUG("[%s] Adding %s mode at index - %02i", name.c_str(), Cycle.name.c_str(), i); + break; + } + + case LOGITECH_DEVICE_LED_BREATHING: + { + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = i; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MIN; + Breathing.brightness_max = LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MAX; + Breathing.brightness = LOGITECH_G_PRO_WIRELESS_BRIGHTNESS_MAX; + Breathing.speed_min = LOGITECH_G_PRO_WIRELESS_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G_PRO_WIRELESS_SPEED_FASTEST; + Breathing.speed = LOGITECH_G_PRO_WIRELESS_SPEED_NORMAL; + modes.push_back(Breathing); + LOG_DEBUG("[%s] Adding %s mode at index - %02i", name.c_str(), Breathing.name.c_str(), i); + break; + } + + default: + LOG_WARNING("[%s] Effect at index - %02i not added: Value %04X unrecognised", name.c_str(), i, fx.fx[i].mode); + break; + } + } + + SetupZones(); + } + else + { + name = "Idle Lightspeed Device"; + vendor = "Logitech"; + type = DEVICE_TYPE_UNKNOWN; + description = "Idle Logitech Wireless Lightspeed Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + } +} + +RGBController_LogitechLightspeed::~RGBController_LogitechLightspeed() +{ + delete controller; +} + +void RGBController_LogitechLightspeed::SetupZones() +{ + const std::string zone_string = "Zone"; + const std::string led_string = "LED"; + uint8_t led_count = controller->lightspeed->getLED_count(); + + LOG_DEBUG("[%s] Setting up %d LEDs", name.c_str(), led_count); + if(led_count > 0) + { + for(size_t i = 0; i < led_count; i++) + { + zone Lightspeed_logo_zone; + led Lightspeed_logo_led; + logitech_led new_led = controller->lightspeed->getLED_info((uint8_t)i); + + if(new_led.location < NUM_LOGITECH_LED_LOCATIONS ) + { + Lightspeed_logo_zone.name = logitech_led_locations[new_led.location]; + Lightspeed_logo_zone.name.append(" "); + Lightspeed_logo_led.name = Lightspeed_logo_zone.name; + Lightspeed_logo_led.name.append(led_string); + Lightspeed_logo_zone.name.append(zone_string); + } + else + { + std::string name = " " + std::to_string(i); + Lightspeed_logo_zone.name = zone_string + name; + Lightspeed_logo_led.name = led_string + name; + } + + Lightspeed_logo_zone.type = ZONE_TYPE_SINGLE; + Lightspeed_logo_zone.leds_min = 1; + Lightspeed_logo_zone.leds_max = 1; + Lightspeed_logo_zone.leds_count = 1; + Lightspeed_logo_zone.matrix_map = NULL; + zones.push_back(Lightspeed_logo_zone); + + Lightspeed_logo_led.value = (unsigned int)i; + leds.push_back(Lightspeed_logo_led); + } + } + + SetupColors(); +} + +void RGBController_LogitechLightspeed::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechLightspeed::DeviceUpdateLEDs() +{ + for(std::vector::iterator led_index = leds.begin(); led_index != leds.end(); led_index++) + { + UpdateZoneLEDs(led_index->value); + } +} + +void RGBController_LogitechLightspeed::UpdateZoneLEDs(int zone) +{ + unsigned char red = RGBGetRValue(colors[zone]); + unsigned char grn = RGBGetGValue(colors[zone]); + unsigned char blu = RGBGetBValue(colors[zone]); + + controller->SendMouseMode(modes[active_mode].value, modes[active_mode].speed, zone, red, grn, blu, modes[active_mode].brightness); +} + +void RGBController_LogitechLightspeed::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_LogitechLightspeed::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | If direct mode is true, then sent the packet to put the | + | mouse in direct mode. This code will only be called when | + | we change modes as to not spam the device. | + \*---------------------------------------------------------*/ + controller->lightspeed->setDirectMode(modes[active_mode].name == "Direct"); + DeviceUpdateLEDs(); +} diff --git a/Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.h b/Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.h new file mode 100644 index 0000000..90296b9 --- /dev/null +++ b/Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechLightspeed.h | +| | +| RGBController for Logitech Lightspeed | +| | +| TheRogueZeta 05 Aug 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechLightspeedController.h" + +class RGBController_LogitechLightspeed : public RGBController +{ +public: + RGBController_LogitechLightspeed(LogitechLightspeedController* controller_ptr); + ~RGBController_LogitechLightspeed(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + uint16_t pid; //This is a workaround fix for G502 mode breathing / spectrum cycle swap +private: + LogitechLightspeedController* controller; +}; diff --git a/Controllers/LogitechController/LogitechProtocolCommon.cpp b/Controllers/LogitechController/LogitechProtocolCommon.cpp new file mode 100644 index 0000000..2f02f3b --- /dev/null +++ b/Controllers/LogitechController/LogitechProtocolCommon.cpp @@ -0,0 +1,929 @@ +/*---------------------------------------------------------*\ +| LogitechProtocolCommon.cpp | +| | +| Common functionality for Logitech RAP and FAP protocols | +| | +| Chris M (Dr_No) 04 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include + +const char* logitech_led_locations[] = +{ + "Unknown", + "Primary", + "Logo", + "Left", + "Right", + "Combined", + "Group One", + "Group Two", + "Group Three", + "Group Four", + "Group Five", + "Group Six" +}; + +const int NUM_LOGITECH_LED_LOCATIONS = sizeof(logitech_led_locations); + +static std::vector logitech_RGB_pages = +{ + LOGITECH_HIDPP_PAGE_RGB_EFFECTS1, + LOGITECH_HIDPP_PAGE_RGB_EFFECTS2 +}; + +int getWirelessDevice(usages device_usages, uint16_t pid, wireless_map *wireless_devices) +{ + hid_device* dev_use1; + usages::iterator find_usage = device_usages.find(1); + if (find_usage == device_usages.end()) + { + LOG_INFO("Unable get_Wireless_Device due to missing FAP Short Message (0x10) usage"); + LOG_DEBUG("Dumping device usages:"); + for(usages::iterator dev = device_usages.begin(); dev != device_usages.end(); dev++) + { + LOG_DEBUG("Usage index:\t%i", dev->first); + } + } + else + { + dev_use1 = find_usage->second; + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + shortFAPrequest get_connected_devices; + get_connected_devices.init(LOGITECH_RECEIVER_DEVICE_INDEX, LOGITECH_GET_REGISTER_REQUEST); + + hid_write(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + hid_read_timeout(dev_use1, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + bool wireless_notifications = response.data[1] & 1; //Connected devices is a flag + + if (!wireless_notifications) + { + response.init(); //zero out the response + get_connected_devices.init(LOGITECH_RECEIVER_DEVICE_INDEX, LOGITECH_SET_REGISTER_REQUEST); + get_connected_devices.data[1] = 1; + hid_write(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + hid_read_timeout(dev_use1, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + + if(get_connected_devices.feature_index == 0x8F) + { + LOG_ERROR("Logitech Protocol error: %02X %02X %02X %02X %02X %02X %02X", get_connected_devices.report_id, get_connected_devices.device_index, get_connected_devices.feature_index, get_connected_devices.feature_command, get_connected_devices.data[0], get_connected_devices.data[1], get_connected_devices.data[2]); + } + } + + response.init(); //zero out the response + get_connected_devices.init(LOGITECH_RECEIVER_DEVICE_INDEX, LOGITECH_GET_REGISTER_REQUEST); + get_connected_devices.feature_command = 0x02; //0x02 Connection State register. Essentially asking for count of paired devices + hid_write(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + hid_read_timeout(dev_use1, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + + unsigned int device_count = response.data[1]; + LOG_INFO("Count of connected devices to %4X: %i", pid, device_count); + + if (device_count > 0) + { + LOG_INFO("Faking a reconnect to get device list"); + device_count++; //Add 1 to the device_count to include the receiver + + response.init(); + get_connected_devices.init(LOGITECH_RECEIVER_DEVICE_INDEX, LOGITECH_SET_REGISTER_REQUEST); + get_connected_devices.feature_index = LOGITECH_SET_REGISTER_REQUEST; + get_connected_devices.feature_command = 0x02; //0x02 Connection State register + get_connected_devices.data[0] = 0x02; //Writting 0x02 to the connection state register will ask the receiver to fake a reconnect of paired devices + hid_write(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + + for(size_t i = 0; i < device_count; i++) + { + blankFAPmessage devices; + devices.init(); + + hid_read_timeout(dev_use1, devices.buffer, devices.size(), LOGITECH_PROTOCOL_TIMEOUT); + unsigned int wireless_PID = (devices.data[2] << 8) | devices.data[1]; + LOG_INFO("Connected Device Index %i:\tVirtualID=%04X\t\t%02X %02X %02X %02X %02X %02X %02X", i, wireless_PID, devices.buffer[0], devices.buffer[1], devices.buffer[2], devices.buffer[3], devices.buffer[4], devices.buffer[5], devices.buffer[6]); + + /*-----------------------------------------------------------------*\ + | We need to read the receiver from the HID device queue but | + | there is no need to add it as it's own device | + \*-----------------------------------------------------------------*/ + if(devices.device_index != LOGITECH_RECEIVER_DEVICE_INDEX) + { + wireless_devices->emplace(wireless_PID, devices.device_index); + } + } + } + else + { + LOG_WARNING("No devices were found connected to receiver!"); + } + } + + return((int)wireless_devices->size()); +} + +logitech_device::logitech_device(char *path, usages _usages, uint8_t _device_index, bool _wireless) +{ + device_index = _device_index; + location = path; + device_usages = _usages; + wireless = _wireless; + RGB_feature_index = 0; + mutex = nullptr; + + initialiseDevice(); +} + +logitech_device::logitech_device(char *path, usages _usages, uint8_t _device_index, bool _wireless, std::shared_ptr mutex_ptr) +{ + device_index = _device_index; + location = path; + device_usages = _usages; + wireless = _wireless; + RGB_feature_index = 0; + mutex = mutex_ptr; + + initialiseDevice(); +} + +logitech_device::~logitech_device() +{ + for(usages::iterator dev = device_usages.begin(); dev != device_usages.end(); dev++) + { + hid_close(dev->second); + } +} + +void logitech_device::initialiseDevice() +{ + bool is_connected = connected(); + flushReadQueue(); + + if(is_connected) + { + getDeviceName(); + + /*-----------------------------------------------------------------*\ + | If this is running with DEBUG or higher loglevel then | + | dump the entire Feature list to log | + \*-----------------------------------------------------------------*/ + if(LogManager::get()->getLoglevel() > 4) + { + getDeviceFeatureList(); //This will populate the feature list + } + + /*-----------------------------------------------------------------*\ + | Check device for known RGB Effects Feature pages & save the index | + \*-----------------------------------------------------------------*/ + for(std::vector::iterator page = logitech_RGB_pages.begin(); page != logitech_RGB_pages.end(); page++) + { + int feature_index = getFeatureIndex(*page); + if(feature_index > 0) + { + feature_list.emplace(*page, feature_index); + RGB_feature_index = feature_index; + break; + } + } + + /*-----------------------------------------------------------------*\ + | If there was no RGB Effect Feature page found | + | dump the entire Feature list to log | + \*-----------------------------------------------------------------*/ + if (RGB_feature_index == 0) + { + LOG_INFO("[%s] Unable add this device due to missing RGB Effects Feature", device_name.c_str()); + } + else + { + getRGBconfig(); + } + } +} + +bool logitech_device::is_valid() +{ + bool is_connected = connected(); + bool valid_test = false; + + if(is_connected) + { + LOG_DEBUG("[%s] valid_test - type %i led_count - %i RGB_index - %i", device_name.c_str(), logitech_device_type, leds.size(), RGB_feature_index); + + valid_test = !device_name.empty() // Check if device name exists + && logitech_device_type <= 8 // Check if device type has a valid index + && (device_name[0] >= 32 && device_name[0] < 122) // Check for non valid characters in device name + && device_name.length() > 3 // Check for valid device names lenght + && leds.size() > 0 // Check if a device has at least 1 led + && RGB_feature_index > 0; // Check if a feature index is "valid" + } + else + { + LOG_INFO("Unable add this Logitech device: Not Connected"); + } + + return(valid_test); +} + +bool logitech_device::connected() +{ + /*-----------------------------------------------------------------*\ + | If this is a wireless device test that it's connected | + | Wired devices will always be connected. | + \*-----------------------------------------------------------------*/ + if(wireless) + { + bool test = false; + hid_device* dev_use1 = getDevice(1); + + if(dev_use1) + { + shortFAPrequest get_connected_devices; + get_connected_devices.init(device_index, LOGITECH_GET_REGISTER_REQUEST); + get_connected_devices.feature_command = 0x02; //0x02 Connection State register. Essentially asking for count of paired devices + + hid_write(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + //This hid_read will not timeout as we need to be sure the wireless device is connected + hid_read(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + test = (get_connected_devices.data[1] != 0x09); //ERR_RESOURCE_ERROR i.e. not currently connected + LOG_DEBUG("Wireless device index %i is %s - %02X %02X %02X", get_connected_devices.device_index, + (test ? "connected" : "disconnected"), get_connected_devices.data[0], get_connected_devices.data[1], get_connected_devices.data[2]); + } + return(test); + } + else + { + return(true); + } +} + +uint8_t logitech_device::getLED_count() +{ + return((uint8_t)leds.size()); +} + +logitech_led logitech_device::getLED_info(uint8_t LED_num) +{ + /*-----------------------------------------------------------------*\ + | Get all info about the LEDs and Zones | + \*-----------------------------------------------------------------*/ + if(!(LED_num > leds.size())) + { + return(leds[LED_num]); + } + else + { + return(leds[0]); + } +} + +void logitech_device::flushReadQueue() +{ + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + for(usages::iterator dev = device_usages.begin(); dev != device_usages.end(); dev++) + { + //Flush the buffer + int flushed = 0; + int result = 1; + + while( result > 0 ) + { + result = hid_read_timeout(dev->second, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + if (result > 0) + { + flushed++; + } + } + //device_name has not yet been set so can not use it in the log + LOG_DEBUG("Preparing read queue for device %i - flushed %i packet%s", dev->first, flushed, ((flushed == 1) ? "" : "s")); + } +} + +hid_device* logitech_device::getDevice(uint8_t usage_index) +{ + /*-----------------------------------------------------------------*\ + | Check the usage map for usage_index | + | Return the associated device if found otherwise a nullptr | + \*-----------------------------------------------------------------*/ +#ifdef WIN32 + usages::iterator find_usage = device_usages.find(usage_index); +#else + //Linux does not need bundle the device usages hence .begin() + usages::iterator find_usage = device_usages.begin(); +#endif //WIN32 + + if (find_usage == device_usages.end()) + { + LOG_INFO("Unable add this device due to missing FAP Message usage %i", usage_index); + return(nullptr); + } + else + { + return(find_usage->second); + } +} + +uint8_t logitech_device::getFeatureIndex(uint16_t feature_page) +{ + /*-----------------------------------------------------------------*\ + | Get the feature index from the Root Index | + | Return the mapped feature_index of the given feature page | + | for this device or else return 0 | + \*-----------------------------------------------------------------*/ + uint8_t feature_index = 0; + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + blankFAPmessage response; + longFAPrequest get_index; + get_index.init(device_index, LOGITECH_HIDPP_PAGE_ROOT_IDX, LOGITECH_CMD_ROOT_GET_FEATURE); + get_index.data[0] = feature_page >> 8; + get_index.data[1] = feature_page & 0xFF; + + hid_write(dev_use2, get_index.buffer, get_index.size()); + hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + + feature_index = response.data[0]; + + LOG_DEBUG("[%s] Feature Page %04X found @ index %02X - %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), feature_page, feature_index, + response.data[0], response.data[1], response.data[2], response.data[3], response.data[4], response.data[5], response.data[6], response.data[7]); + } + + return(feature_index); +} + +uint16_t logitech_device::getFeaturePage(uint8_t feature_index) +{ + /*-----------------------------------------------------------------*\ + | Get the feature page from the feature_list | + | Return the mapped feature page given the feature index | + | for this device or else return 0 | + \*-----------------------------------------------------------------*/ + rvrse_features rvrse_feature_list = reverse_map(feature_list); + + rvrse_features::iterator find_page = rvrse_feature_list.find(feature_index); + if (find_page == rvrse_feature_list.end()) + { + LOG_DEBUG("[%s] Feature index %02X not found!", device_name.c_str(), feature_index); + + //TODO: Handle cache miss + return(0); + } + else + { + return(find_page->second); + } +} + +int logitech_device::getDeviceFeatureList() +{ + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) | + | then list all features for device | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + /*-----------------------------------------------------------------*\ + | Query the root index for the index of the feature list | + | This is done for safety as it is generaly at feature index 0x01 | + \*-----------------------------------------------------------------*/ + int feature_index = getFeatureIndex(LOGITECH_HIDPP_PAGE_FEATURE_SET); + + /*-----------------------------------------------------------------*\ + | Get the count of Features | + \*-----------------------------------------------------------------*/ + longFAPrequest get_count; + get_count.init(device_index, feature_index, LOGITECH_CMD_FEATURE_SET_GET_COUNT); + + hid_write(dev_use2, get_count.buffer, get_count.size()); + hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + unsigned int feature_count = response.data[0]; + + longFAPrequest get_features; + get_features.init(device_index, feature_index, LOGITECH_CMD_FEATURE_SET_GET_ID); + for(std::size_t i = 1; feature_list.size() < feature_count; i++ ) + { + get_features.data[0] = (uint8_t)i; + hid_write(dev_use2, get_features.buffer, get_features.size()); + hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + LOG_DEBUG("[%s] Feature %04X @ index: %02X", device_name.c_str(), (response.data[0] << 8) | response.data[1], i); + feature_list.emplace((uint16_t)((response.data[0] << 8) | response.data[1]), (uint8_t)i); + } + } + else + { + LOG_INFO("[%s] Unable get the feature index list - missing FAP Long Message (0x11) usage", device_name.c_str()); + } + + return((int)feature_list.size()); +} + +int logitech_device::getDeviceName() +{ + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) | + | Then use it to get the name for this device | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + /*-----------------------------------------------------------------*\ + | Query the root index for the index of the name feature | + \*-----------------------------------------------------------------*/ + int feature_index = getFeatureIndex(LOGITECH_HIDPP_PAGE_DEVICE_NAME_TYPE); + + /*-----------------------------------------------------------------*\ + | Get the device name length | + \*-----------------------------------------------------------------*/ + if(feature_index > 0) + { + longFAPrequest get_length; + get_length.init(device_index, feature_index, LOTITECH_CMD_DEVICE_NAME_TYPE_GET_COUNT); + hid_write(dev_use2, get_length.buffer, get_length.size()); + hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + unsigned int name_length = response.data[0]; + LOG_DEBUG("[%s] Name Length %02i - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), name_length, + response.data[0], response.data[1], response.data[2], response.data[3], response.data[4], response.data[5], response.data[6], response.data[7], + response.data[8], response.data[9], response.data[10], response.data[11], response.data[12], response.data[13], response.data[14], response.data[15]); + + longFAPrequest get_name; + get_name.init(device_index, feature_index, LOGITECH_CMD_DEVICE_NAME_TYPE_GET_DEVICE_NAME); + while(device_name.length() < name_length) + { + get_name.data[0] = (uint8_t)device_name.length(); //This sets the character index to get from the device + hid_write(dev_use2, get_name.buffer, get_name.size()); + hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + std::string temp = (char *)&response.data; + device_name.append(temp); + LOG_DEBUG("[%s] Get Name %02i - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), device_name.length(), + response.data[0], response.data[1], response.data[2], response.data[3], response.data[4], response.data[5], response.data[6], response.data[7], + response.data[8], response.data[9], response.data[10], response.data[11], response.data[12], response.data[13], response.data[14], response.data[15]); + } + + get_name.init(device_index, feature_index, LOGITECH_CMD_DEVICE_NAME_TYPE_GET_TYPE); + hid_write(dev_use2, get_name.buffer, get_name.size()); + hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + logitech_device_type = response.data[0]; + LOG_DEBUG("[%s] Get Type %02i - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), logitech_device_type, + response.data[0], response.data[1], response.data[2], response.data[3], response.data[4], response.data[5], response.data[6], response.data[7], + response.data[8], response.data[9], response.data[10], response.data[11], response.data[12], response.data[13], response.data[14], response.data[15]); + } + } + + return((int)device_name.length()); +} + +void logitech_device::getRGBconfig() +{ + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) | + | Then use it to get the name for this device | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + uint16_t feature_page = getFeaturePage(RGB_feature_index); + uint8_t led_response = 0; + uint8_t led_counter = 0; + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + int result; + + longFAPrequest get_count; + get_count.init(device_index, RGB_feature_index, LOGITECH_CMD_RGB_EFFECTS_GET_COUNT); + + if(feature_page == LOGITECH_HIDPP_PAGE_RGB_EFFECTS1) + { + result = hid_write(dev_use2, get_count.buffer, get_count.size()); + do + { + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + LOG_DEBUG("[%s] FP8070 - LED Count - %02X : %04X %04X %04X %04X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), + response.data[0], (response.data[1] << 8 | response.data[2]), (response.data[3] << 8 | response.data[4]), (response.data[5] << 8 | response.data[6]), + (response.data[7] << 8 | response.data[8]), response.data[9], response.data[10], response.data[11], response.data[12], response.data[13], response.data[14], response.data[15]); + } while ((result == 20) && (get_count.feature_index != response.feature_index) && (get_count.feature_command != response.feature_command)); + + led_response = response.data[0]; + + get_count.feature_command = LOGITECH_CMD_RGB_EFFECTS_GET_INFO; + for(size_t i = 0; i < led_response; i++) + { + get_count.data[0] = (uint8_t)i; + result = hid_write(dev_use2, get_count.buffer, get_count.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + LOG_DEBUG("[%s] FP8070 - LED %02i - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), get_count.data[0], + response.data[0], response.data[1], response.data[2], response.data[3], response.data[4], response.data[5], response.data[6], response.data[7], + response.data[8], response.data[9], response.data[10], response.data[11], response.data[12], response.data[13], response.data[14], response.data[15]); + if( result == 20 && + get_count.feature_index == response.feature_index && + get_count.feature_command == response.feature_command && + response.data[0] != 0x10 && + response.data[1] != 0x02 + ) + { + //If the response is the correct length (i.e. no USB error) and is for the RGB_feature_index and LOGITECH_CMD_RGB_EFFECTS_GET_INFO and no error occured with the led_counter then bump the counter + logitech_led new_led; + + new_led.location = response.data[1] << 8 | response.data[2]; + new_led.fx_count = response.data[3]; + + for(uint8_t i = 0; i < new_led.fx_count; i++) + { + blankFAPmessage fx_response; + fx_response.init(); + + longFAPrequest get_effect; + get_effect.init(device_index, RGB_feature_index, LOGITECH_CMD_RGB_EFFECTS_GET_CONTROL); + + get_effect.data[0] = get_count.data[0]; + get_effect.data[1] = i; + result = hid_write(dev_use2, get_effect.buffer, get_effect.size()); + result = hid_read_timeout(dev_use2, fx_response.buffer, fx_response.size(), LOGITECH_PROTOCOL_TIMEOUT); + LOG_DEBUG("[%s] FP8070 - LED %02i Effect %02X - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), get_count.data[0], i, + fx_response.data[0], fx_response.data[1], fx_response.data[2], fx_response.data[3], fx_response.data[4], fx_response.data[5], fx_response.data[6], fx_response.data[7], + fx_response.data[8], fx_response.data[9], fx_response.data[10], fx_response.data[11], fx_response.data[12], fx_response.data[13], fx_response.data[14], fx_response.data[15]); + + logitech_fx new_fx; + + new_fx.index = i; + new_fx.mode = static_cast(fx_response.data[2] << 8 | fx_response.data[3]); + new_fx.speed = fx_response.data[6] << 8 | fx_response.data[7]; + + new_led.fx.push_back(new_fx); + } + + leds.emplace(response.data[0], new_led); + } + } + } + else if(feature_page == LOGITECH_HIDPP_PAGE_RGB_EFFECTS2) + { + get_count.data[0] = 0xFF; + get_count.data[1] = 0xFF; + get_count.data[2] = 0; + get_count.data[3] = 0; + get_count.data[4] = 0; + + result = hid_write(dev_use2, get_count.buffer, get_count.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + LOG_DEBUG("[%s] FP8071 - LED Count - %04X : %02X %04X %04X %04X %04X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), + (response.data[1] << 8 | response.data[2]), response.data[0], (response.data[1] << 8 | response.data[2]), (response.data[3] << 8 | response.data[4]), (response.data[5] << 8 | response.data[6]), + (response.data[7] << 8 | response.data[8]), response.data[9], response.data[10], response.data[11], response.data[12], response.data[13], response.data[14], response.data[15]); + + led_response = (response.data[1] << 8 | response.data[2]); + for(size_t i = 0; i < led_response; i++) + { + get_count.data[0] = (uint8_t)i; + get_count.data[1] = 0xFF; + get_count.data[2] = 0; + + result = hid_write(dev_use2, get_count.buffer, get_count.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + LOG_DEBUG("[%s] FP8071 - LED %02i - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), i, + response.data[0], response.data[1], response.data[2], response.data[3], response.data[4], response.data[5], response.data[6], response.data[7], + response.data[8], response.data[9], response.data[10], response.data[11], response.data[12], response.data[13], response.data[14], response.data[15]); + + logitech_led new_led; + + new_led.location = response.data[2] << 8 | response.data[3]; + new_led.fx_count = response.data[4]; + + for(uint8_t i = 0; i < new_led.fx_count; i++) + { + blankFAPmessage fx_response; + fx_response.init(); + + longFAPrequest get_effect; + get_effect.init(device_index, RGB_feature_index, LOGITECH_CMD_RGB_EFFECTS_GET_COUNT); + + get_effect.data[0] = get_count.data[0]; + get_effect.data[1] = i; + result = hid_write(dev_use2, get_effect.buffer, get_effect.size()); + result = hid_read_timeout(dev_use2, fx_response.buffer, fx_response.size(), LOGITECH_PROTOCOL_TIMEOUT); + LOG_DEBUG("[%s] FP8071 - LED %02i Effect %02X - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), get_count.data[0], i, + fx_response.data[0], fx_response.data[1], fx_response.data[2], fx_response.data[3], fx_response.data[4], fx_response.data[5], fx_response.data[6], fx_response.data[7], + fx_response.data[8], fx_response.data[9], fx_response.data[10], fx_response.data[11], fx_response.data[12], fx_response.data[13], fx_response.data[14], fx_response.data[15]); + + logitech_fx new_fx; + + new_fx.index = i; + new_fx.mode = static_cast(fx_response.data[2] << 8 | fx_response.data[3]); + new_fx.speed = fx_response.data[6] << 8 | fx_response.data[7]; + + new_led.fx.push_back(new_fx); + } + + leds.emplace(response.data[0], new_led); + } + /*-----------------------------------------------------------------*\ + | Set the config to SW control mode | + \*-----------------------------------------------------------------*/ + set8071Effects(5); + } + + /*get_count.feature_command = LOGITECH_CMD_RGB_EFFECTS_GET_STATE; + for(std::size_t i = 0; i < feature_count; i++ ) + { + get_count.data[0] = i; + result = hid_write(dev_use2, get_count.buffer, get_count.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + + get_count.feature_command = LOGITECH_CMD_RGB_EFFECTS_GET_CONFIG; + for(std::size_t i = 0; i < feature_count; i++ ) + { + get_count.data[0] = i; + result = hid_write(dev_use2, get_count.buffer, get_count.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + }*/ + } + + LOG_DEBUG("[%s] led_response returned %i led_counter returned %i : setting controller to %i LED%s", device_name.c_str(), led_response, led_counter, leds.size(), ((leds.size() == 1) ? "" : "s")); +} + +uint8_t logitech_device::setDirectMode(bool direct) +{ + int result = 0; + + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) | + | then set the device into direct mode via register 0x80 | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + /*-----------------------------------------------------------------*\ + | Turn the direct mode on or off via the RGB_feature_index | + \*-----------------------------------------------------------------*/ + longFAPrequest set_direct; + set_direct.init(device_index, RGB_feature_index, LOGITECH_FP8070_SET_SW_CTL); + set_direct.data[0] = (direct) ? 1 : 0; + set_direct.data[1] = set_direct.data[0]; + + /*-----------------------------------------------------*\ + | Send packet | + | This code has to be protected to avoid crashes when | + | this is called at the same time to change a powerplay | + | mat and its paired wireless mouse leds. It will | + | happen when using effects engines with high framerate | + \*-----------------------------------------------------*/ + if(mutex) + { + std::lock_guard guard(*mutex); + + result = hid_write(dev_use2, set_direct.buffer, set_direct.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + else + { + result = hid_write(dev_use2, set_direct.buffer, set_direct.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + } + + return(result); +} + +uint8_t logitech_device::setMode(uint8_t mode, uint16_t speed, uint8_t zone, uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness) +{ + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) then | + | set the device mode via LOGITECH_CMD_RGB_EFFECTS_SET_CONTROL | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + uint16_t feature_page = getFeaturePage(RGB_feature_index); + int result = 0; + LOGITECH_DEVICE_MODE fx = leds[zone].fx[mode].mode; + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + /*-----------------------------------------------------------------*\ + | Set the mode via the RGB_feature_index | + \*-----------------------------------------------------------------*/ + longFAPrequest set_mode; + bool fp8070 = (feature_page == LOGITECH_HIDPP_PAGE_RGB_EFFECTS1); + + set_mode.init( + device_index, + RGB_feature_index, + (fp8070 ? (uint8_t)LOGITECH_FP8070_SET_EFFECT : (uint8_t)LOGITECH_FP8071_SET_LED_EFFECT) + ); + set_mode.data[0] = zone; + set_mode.data[1] = mode; + + set_mode.data[2] = red; + set_mode.data[3] = green; + set_mode.data[4] = blue; + + set_mode.data[12] = fp8070 ? 0x00 : 0x01; //Bit 2-3 Power Mode : Bit 1-0 Persistence + + speed *= 100; + switch(fx) + { + case LOGITECH_DEVICE_LED_ON: + //set_mode.data[5] = 0x02; //zone; + break; + + case LOGITECH_DEVICE_LED_SPECTRUM: + set_mode.data[7] = speed >> 8; + set_mode.data[8] = speed & 0xFF; + set_mode.data[9] = brightness; + break; + + case LOGITECH_DEVICE_LED_BREATHING: + set_mode.data[5] = speed >> 8; + set_mode.data[6] = speed & 0xFF; + //set_mode.data[7] = curve_type; //Value 0-6: Default, Sine, Square, Triangle, Sawtooth, Reverse_Sawtooth, Exponent + set_mode.data[8] = brightness; + break; + + /*-----------------------------------------------------*\ + | Place holders for later implementation | + \*-----------------------------------------------------*/ + case LOGITECH_DEVICE_LED_OFF: + case LOGITECH_DEVICE_LED_WAVE: + case LOGITECH_DEVICE_LED_STAR: + case LOGITECH_DEVICE_LED_RIPPLE: + case LOGITECH_DEVICE_LED_CUSTOM: + default: + break; + } + + /*-----------------------------------------------------*\ + | Send packet | + | This code has to be protected to avoid crashes when | + | this is called at the same time to change a powerplay | + | mat and its paired wireless mouse leds. It will | + | happen when using effects engines with high framerate | + \*-----------------------------------------------------*/ + if(mutex) + { + std::lock_guard guard(*mutex); + + result = hid_write(dev_use2, set_mode.buffer, set_mode.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + else + { + result = hid_write(dev_use2, set_mode.buffer, set_mode.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + } + + return result; +} + +uint8_t logitech_device::set8071Effects(uint8_t control) +{ + int result = 0; + + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) | + | then set the device into direct mode via register 0x80 | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + /*-----------------------------------------------------------------*\ + | Use longFAPrequest (20 bytes) for FP8071 CONTROL command | + | Short messages (7 bytes) are not supported by some devices | + \*-----------------------------------------------------------------*/ + longFAPrequest set_effects; + set_effects.init(device_index, RGB_feature_index, LOGITECH_FP8071_CONTROL); + set_effects.data[0] = 1; + set_effects.data[1] = 3; //Disables all FW control for PWR (0x02) and RGB (0x01) + set_effects.data[2] = control; + + /*-----------------------------------------------------*\ + | Send packet | + | This code has to be protected to avoid crashes when | + | this is called at the same time to change a powerplay | + | mat and its paired wireless mouse leds. It will | + | happen when using effects engines with high framerate | + \*-----------------------------------------------------*/ + if(mutex) + { + std::lock_guard guard(*mutex); + + hid_write(dev_use2, set_effects.buffer, set_effects.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + else + { + hid_write(dev_use2, set_effects.buffer, set_effects.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + + /*-----------------------------------------------------*\ + | Check for HID++ error response (0x8F in feature_index)| + \*-----------------------------------------------------*/ + if(response.feature_index == 0x8F) + { + LOG_WARNING("[%s] set8071Effects: HID++ ERROR! ErrCode=%02X", + device_name.c_str(), response.data[2]); + } + } + return result; +} + +uint8_t logitech_device::set8071TimeoutControl(uint8_t /*control*/) +{ + int result = 0; + + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) | + | then set the device into direct mode via register 0x80 | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + /*-----------------------------------------------------------------*\ + | Turn the direct mode on or off via the RGB_feature_index | + \*-----------------------------------------------------------------*/ + longFAPrequest set_control; + set_control.init(device_index, RGB_feature_index, LOGITECH_FP8071_PWR_CFG); + set_control.data[0] = 1; //1; + set_control.data[3] = 0; //0x3C; //Inactive Lighting timeout MSB + set_control.data[4] = 5; //0x3C; //Inactive Lighting timeout LSB + set_control.data[5] = 0; //1; //Lights off timeout MSB + set_control.data[6] = 20; //0x2C; //Lights off timeout LSB + + /*-----------------------------------------------------*\ + | Send packet | + | This code has to be protected to avoid crashes when | + | this is called at the same time to change a powerplay | + | mat and its paired wireless mouse leds. It will | + | happen when using effects engines with high framerate | + \*-----------------------------------------------------*/ + if(mutex) + { + std::lock_guard guard(*mutex); + + result = hid_write(dev_use2, set_control.buffer, set_control.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + else + { + result = hid_write(dev_use2, set_control.buffer, set_control.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + } + return(result); +} diff --git a/Controllers/LogitechController/LogitechProtocolCommon.h b/Controllers/LogitechController/LogitechProtocolCommon.h new file mode 100644 index 0000000..2314fee --- /dev/null +++ b/Controllers/LogitechController/LogitechProtocolCommon.h @@ -0,0 +1,317 @@ +/*---------------------------------------------------------*\ +| LogitechProtocolCommon.h | +| | +| Common functionality for Logitech RAP and FAP protocols | +| | +| Chris M (Dr_No) 04 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "LogManager.h" + +#define LOGITECH_PROTOCOL_TIMEOUT 300 //Timeout in ms +#define LOGITECH_HEADER_SIZE 3 +#define LOGITECH_SHORT_MESSAGE 0x10 +#define LOGITECH_SHORT_MESSAGE_LEN 7 +#define LOGITECH_LONG_MESSAGE 0x11 +#define LOGITECH_LONG_MESSAGE_LEN 20 +#define LOGITECH_FAP_RESPONSE_LEN 64 //Define a universal response buffer and allow the hidapi to determine the size + +#define LOGITECH_DEFAULT_DEVICE_INDEX 0xFF +#define LOGITECH_RECEIVER_DEVICE_INDEX 0xFF //The Unifying receiver uses RAP or register access protocol +#define LOGITECH_SET_REGISTER_REQUEST 0x80 +#define LOGITECH_GET_REGISTER_REQUEST 0x81 + +#define LOGITECH_HIDPP_PAGE_ROOT_IDX 0x00 //Used for querying the feature index +#define LOGITECH_CMD_ROOT_GET_FEATURE 0x01 +#define LOGITECH_CMD_ROOT_GET_PROTOCOL 0x11 + +#define LOGITECH_HIDPP_PAGE_FEATURE_SET 0x0001 +#define LOGITECH_CMD_FEATURE_SET_GET_COUNT 0x01 +#define LOGITECH_CMD_FEATURE_SET_GET_ID 0x11 + +#define LOGITECH_HIDPP_PAGE_DEVICE_NAME_TYPE 0x0005 +#define LOTITECH_CMD_DEVICE_NAME_TYPE_GET_COUNT 0x01 +#define LOGITECH_CMD_DEVICE_NAME_TYPE_GET_DEVICE_NAME 0x11 +#define LOGITECH_CMD_DEVICE_NAME_TYPE_GET_TYPE 0x21 + +#define LOGITECH_HIDPP_PAGE_RGB_EFFECTS1 0x8070 +#define LOGITECH_HIDPP_PAGE_RGB_EFFECTS2 0x8071 +#define LOGITECH_CMD_RGB_EFFECTS_GET_COUNT 0x00 +#define LOGITECH_CMD_RGB_EFFECTS_GET_INFO 0x10 +#define LOGITECH_CMD_RGB_EFFECTS_GET_CONTROL 0x20 +#define LOGITECH_CMD_RGB_EFFECTS_SET_CONTROL 0x30 +#define LOGITECH_CMD_RGB_EFFECTS_GET_STATE 0x40 +#define LOGITECH_CMD_RGB_EFFECTS_SET_STATE 0x50 +#define LOGITECH_CMD_RGB_EFFECTS_GET_CONFIG 0x60 +#define LOGITECH_CMD_RGB_EFFECTS_SET_CONFIG 0x70 + +enum LOGITECH_DEVICE_TYPE +{ + LOGITECH_DEVICE_TYPE_KEYBOARD = 0, + LOGITECH_DEVICE_TYPE_REMOTECONTROL = 1, + LOGITECH_DEVICE_TYPE_NUMPAD = 2, + LOGITECH_DEVICE_TYPE_MOUSE = 3, + LOGITECH_DEVICE_TYPE_MOUSEPAD = 4, + LOGITECH_DEVICE_TYPE_TRACKBALL = 5, + LOGITECH_DEVICE_TYPE_PRESENTER = 6, + LOGITECH_DEVICE_TYPE_RECEIVER = 7, + LOGITECH_DEVICE_TYPE_HEADSET = 8 +}; + +enum LOGITECH_DEVICE_MODE +{ + LOGITECH_DEVICE_LED_OFF = 0x0000, + LOGITECH_DEVICE_LED_ON = 0x0001, + LOGITECH_DEVICE_LED_SPECTRUM = 0x0003, + LOGITECH_DEVICE_LED_WAVE = 0x0004, + LOGITECH_DEVICE_LED_STAR = 0x0005, + LOGITECH_DEVICE_LED_BREATHING = 0x000A, + LOGITECH_DEVICE_LED_RIPPLE = 0x000B, + LOGITECH_DEVICE_LED_CUSTOM = 0x000C +}; + +enum LOGITECH_FP8070 +{ + LOGITECH_FP8070_INFO = 0x00, + LOGITECH_FP8070_ZONE_INFO = 0x10, + LOGITECH_FP8070_EFFECT_INFO = 0x20, + LOGITECH_FP8070_SET_EFFECT = 0x30, + LOGITECH_FP8070_SET_CFG = 0x40, + LOGITECH_FP8070_GET_CFG = 0x50, + LOGITECH_FP8070_GET_BIN_INFO = 0x60, + LOGITECH_FP8070_GET_SW_CTL = 0x70, + LOGITECH_FP8070_SET_SW_CTL = 0x80, + LOGITECH_FP8070_GET_STATUS = 0x90, + LOGITECH_FP8070_CLEAR_EFFECT = 0xA0, + LOGITECH_FP8070_SET_DIR = 0xB0, + LOGITECH_FP8070_GET_COLOUR = 0xC0, + LOGITECH_FP8070_SYNC_CFG = 0xD0, + LOGITECH_FP8070_GET_EFFECT = 0xE0, + LOGITECH_FP8070_SET_BIN_INFO = 0xF0, +}; + +enum LOGITECH_FP8071 +{ + LOGITECH_FP8071_INFO = 0x00, + LOGITECH_FP8071_SET_LED_EFFECT = 0x10, + LOGITECH_FP8071_ZONE_PATTERN = 0x20, + LOGITECH_FP8071_CONFIG = 0x30, + LOGITECH_FP8070_BIN_INFO = 0x40, + LOGITECH_FP8071_CONTROL = 0x50, + LOGITECH_FP8071_SYNC_CFG = 0x60, + LOGITECH_FP8071_PWR_CFG = 0x70, + LOGITECH_FP8071_PWR_MODE = 0x80, + LOGITECH_FP8071_SHUTDOWN = 0x90 +}; + +enum LOGITECH_FP8071_FLAGS +{ + FP8071_SUPPORTS_GET_STATUS = 0x01, + FP8071_RESERVED = 0x02, + FP8071_SUPPORTS_SET_BIN_INFO = 0x04, + FP8071_MONOCHROME_ONLY = 0x08, + FP8071_NO_SYNC_SUPPORT = 0x10, + FP8071_SUPPORTS_SHUTDOWN = 0x20, + FP8071_SUPPORTS_CLUSTER_CHANGED = 0x40, +}; + +extern const char* logitech_led_locations[]; +extern const int NUM_LOGITECH_LED_LOCATIONS; + +// Used for: {GET,SET}_REGISTER_{REQ,RSP}, SET_LONG_REGISTER_RSP, GET_LONG_REGISTER_REQ +struct message_short +{ + unsigned char address; + unsigned char data[3]; +}; + +// Used for: SET_LONG_REGISTER_REQ, GET_LONG_REGISTER_RSP +struct message_long +{ + unsigned char address; + unsigned char data[16]; +}; + +// Used for: ERROR_MSG +struct message_error +{ + unsigned char sub_id; + unsigned char address; + unsigned char error_code; + unsigned char padding; /* set to 0 */ +}; + +union shortFAPrequest +{ + uint8_t buffer[LOGITECH_SHORT_MESSAGE_LEN]; + struct + { + uint8_t report_id; + uint8_t device_index; + uint8_t feature_index; + uint8_t feature_command; + uint8_t data[LOGITECH_SHORT_MESSAGE_LEN - 4]; + }; + + void init(uint8_t device_index, uint8_t feature_index) + { + this->report_id = LOGITECH_SHORT_MESSAGE; + this->device_index = device_index; + this->feature_index = feature_index; + this->feature_command = feature_command; + for(size_t i = 0; i < sizeof(data); i++) + { + this->data[i] = 0; + } + }; + + int size() + { + return LOGITECH_SHORT_MESSAGE_LEN; + }; +}; + +union longFAPrequest +{ + uint8_t buffer[LOGITECH_LONG_MESSAGE_LEN]; + struct + { + uint8_t report_id; + uint8_t device_index; + uint8_t feature_index; + uint8_t feature_command; + uint8_t data[LOGITECH_LONG_MESSAGE_LEN - 4]; + }; + + void init(uint8_t device_index, uint8_t feature_index, uint8_t feature_command) + { + this->report_id = LOGITECH_LONG_MESSAGE; + this->device_index = device_index; + this->feature_index = feature_index; + this->feature_command = feature_command; + for(size_t i = 0; i < sizeof(data); i++) + { + this->data[i] = 0; + } + }; + + int size() + { + return LOGITECH_LONG_MESSAGE_LEN; + }; +}; + +union blankFAPmessage +{ + uint8_t buffer[LOGITECH_FAP_RESPONSE_LEN]; + struct + { + uint8_t report_id; + uint8_t device_index; + uint8_t feature_index; + uint8_t feature_command; + uint8_t data[LOGITECH_FAP_RESPONSE_LEN - 4]; + }; + + //blank this buffer entirely + void init() + { + for(size_t i = 0; i < sizeof(buffer); i++) + { + this->buffer[i] = 0; + } + }; + + int size() + { + return LOGITECH_FAP_RESPONSE_LEN; + }; +}; + +template +static std::map reverse_map(const std::map& map) +{ + std::map reversed_map; + + for(const std::pair& entry : map) + { + reversed_map[entry.second] = entry.first; + } + + return reversed_map; +} + +struct logitech_fx +{ + uint8_t index; + uint16_t speed; //period + LOGITECH_DEVICE_MODE mode; +}; + +typedef std::map usages; +typedef std::map features; +typedef std::map rvrse_features; +typedef std::map wireless_map; + +typedef std::vector leds_fx; + +struct logitech_led +{ + uint16_t location; + uint8_t fx_count; + leds_fx fx; +}; + +int getWirelessDevice(usages _usages, uint16_t pid, wireless_map *wireless_devices); //Helper function needed outside of class + +class logitech_device +{ +public: + logitech_device(char *path, usages _usages, uint8_t _device_index, bool _wireless); + logitech_device(char *path, usages _usages, uint8_t _device_index, bool _wireless, std::shared_ptr mutex_ptr); + + ~logitech_device(); + + /*-----------------------------------------------------------------*\ + | usages is a std::map that stores all the devices HID usages | + | This is to ensure that we can communicate to all windows usages | + \*-----------------------------------------------------------------*/ + usages device_usages; + features feature_list; + uint8_t device_index; + uint8_t RGB_feature_index; //Stored for quick use + uint8_t logitech_device_type; + bool wireless; + std::string device_name; + std::string location; + std::string protocol_version; + + bool connected(); + bool is_valid(); + void flushReadQueue(); + uint8_t getFeatureIndex(uint16_t feature_page); + uint8_t getLED_count(); + logitech_led getLED_info(uint8_t LED_num); + uint8_t setDirectMode(bool direct); + uint8_t setMode(uint8_t mode, uint16_t speed, uint8_t zone, uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness); + uint8_t set8071Effects(uint8_t control); + uint8_t set8071TimeoutControl(uint8_t control); + int getDeviceName(); +private: + std::map leds; + std::shared_ptr mutex; + + hid_device* getDevice(uint8_t usage_index); + uint16_t getFeaturePage(uint8_t feature_index); + int getDeviceFeatureList(); + void getRGBconfig(); + void initialiseDevice(); +}; diff --git a/Controllers/LogitechController/LogitechX56Controller/LogitechX56Controller.cpp b/Controllers/LogitechController/LogitechX56Controller/LogitechX56Controller.cpp new file mode 100644 index 0000000..0f50601 --- /dev/null +++ b/Controllers/LogitechController/LogitechX56Controller/LogitechX56Controller.cpp @@ -0,0 +1,102 @@ +/*---------------------------------------------------------*\ +| LogitechX56Controller.cpp | +| | +| Driver for Logitech X56 | +| | +| Edbgon 11 Jun 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogitechX56Controller.h" +#include "StringUtils.h" + +LogitechX56Controller::LogitechX56Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +LogitechX56Controller::~LogitechX56Controller() +{ + hid_close(dev); +} + +std::string LogitechX56Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LogitechX56Controller::GetDeviceName() +{ + return(name); +} + +std::string LogitechX56Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LogitechX56Controller::SetColor(RGBColor color, uint8_t brightness) +{ + unsigned char buf[X56_CONTROLLER_PACKET_SIZE]; + unsigned char cbuf[X56_CONTROLLER_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, X56_CONTROLLER_PACKET_SIZE); + memset(cbuf, 0x00, X56_CONTROLLER_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up init packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x09; + buf[0x02] = 0x02; + buf[0x03] = brightness; + + /*-----------------------------------------------------*\ + | Set up color packet | + \*-----------------------------------------------------*/ + cbuf[0x00] = 0x09; + cbuf[0x02] = 0x03; + cbuf[0x03] = RGBGetRValue(color); + cbuf[0x04] = RGBGetGValue(color); + cbuf[0x05] = RGBGetBValue(color); + + /*-----------------------------------------------------*\ + | Send packets | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, X56_CONTROLLER_PACKET_SIZE); + hid_send_feature_report(dev, cbuf, X56_CONTROLLER_PACKET_SIZE); + +} + +void LogitechX56Controller::Save() +{ + uint8_t buffer[X56_CONTROLLER_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buffer, 0x00, X56_CONTROLLER_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up init packet | + \*-----------------------------------------------------*/ + buffer[0x00] = 0x01; + buffer[0x01] = 0x01; + + hid_send_feature_report(dev, buffer, X56_CONTROLLER_PACKET_SIZE); +} diff --git a/Controllers/LogitechController/LogitechX56Controller/LogitechX56Controller.h b/Controllers/LogitechController/LogitechX56Controller/LogitechX56Controller.h new file mode 100644 index 0000000..af10c14 --- /dev/null +++ b/Controllers/LogitechController/LogitechX56Controller/LogitechX56Controller.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| LogitechX56Controller.h | +| | +| Driver for Logitech X56 | +| | +| Edbgon 11 Jun 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define X56_CONTROLLER_PACKET_SIZE 64 + +class LogitechX56Controller +{ +public: + LogitechX56Controller(hid_device* dev_handle, const char* path, std::string dev_name); + + ~LogitechX56Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + void SetColor(RGBColor colors, uint8_t brightness); + void Save(); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/LogitechController/LogitechX56Controller/RGBController_LogitechX56.cpp b/Controllers/LogitechController/LogitechX56Controller/RGBController_LogitechX56.cpp new file mode 100644 index 0000000..6f3971a --- /dev/null +++ b/Controllers/LogitechController/LogitechX56Controller/RGBController_LogitechX56.cpp @@ -0,0 +1,110 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechX56.cpp | +| | +| RGBController for Logitech X56 | +| | +| Edbgon 11 Jun 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_LogitechX56.h" + +/**------------------------------------------------------------------*\ + @name Logitech X56 + @category Gamepad + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :x: + @detectors DetectLogitechX56 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_LogitechX56::RGBController_LogitechX56(LogitechX56Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Logitech"; + type = DEVICE_TYPE_GAMEPAD; + description = "Logitech X56 Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0x00; + Direct.brightness_max = 0x64; + Direct.brightness = 0x64; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_LogitechX56::~RGBController_LogitechX56() +{ + delete controller; +} + +void RGBController_LogitechX56::SetupZones() +{ + /*---------------------------------------------------------*\ + | Each device has only one zone and LED | + \*---------------------------------------------------------*/ + zone x56_zone; + x56_zone.name = "X56"; + x56_zone.type = ZONE_TYPE_SINGLE; + x56_zone.leds_min = 1; + x56_zone.leds_max = 1; + x56_zone.leds_count = 1; + x56_zone.matrix_map = NULL; + zones.push_back(x56_zone); + + led x56_led; + x56_led.name = "X56"; + leds.push_back(x56_led); + + SetupColors(); +} + +void RGBController_LogitechX56::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechX56::DeviceUpdateLEDs() +{ + controller->SetColor(colors[0], modes[active_mode].brightness); +} + +void RGBController_LogitechX56::UpdateZoneLEDs(int /*zone*/) +{ + /*---------------------------------------------------------*\ + | Packet expects both LEDs | + \*---------------------------------------------------------*/ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechX56::UpdateSingleLED(int /*led*/) +{ + /*---------------------------------------------------------*\ + | Packet expects both LEDs | + \*---------------------------------------------------------*/ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechX56::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechX56::DeviceSaveMode() +{ + controller->Save(); +} diff --git a/Controllers/LogitechController/LogitechX56Controller/RGBController_LogitechX56.h b/Controllers/LogitechController/LogitechX56Controller/RGBController_LogitechX56.h new file mode 100644 index 0000000..1f758d5 --- /dev/null +++ b/Controllers/LogitechController/LogitechX56Controller/RGBController_LogitechX56.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_LogitechX56.h | +| | +| RGBController for Logitech X56 | +| | +| Edbgon 11 Jun 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "LogitechX56Controller.h" + +class RGBController_LogitechX56 : public RGBController +{ +public: + RGBController_LogitechX56(LogitechX56Controller* controller_ptr); + ~RGBController_LogitechX56(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + LogitechX56Controller* controller; +}; diff --git a/Controllers/LuxaforController/LuxaforController.cpp b/Controllers/LuxaforController/LuxaforController.cpp new file mode 100644 index 0000000..6170ac1 --- /dev/null +++ b/Controllers/LuxaforController/LuxaforController.cpp @@ -0,0 +1,135 @@ +/*---------------------------------------------------------*\ +| LuxaforController.cpp | +| | +| Driver for Luxafor devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 05 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LuxaforController.h" +#include "StringUtils.h" + +LuxaforController::LuxaforController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +LuxaforController::~LuxaforController() +{ + hid_close(dev); +} + +std::string LuxaforController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string LuxaforController::GetNameString() +{ + return(name); +} + +std::string LuxaforController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void LuxaforController::SendPacket(unsigned char mode, unsigned char led, unsigned char red, unsigned char grn, unsigned char blu, unsigned char type) +{ + unsigned char usb_buf[9]; + + memset(usb_buf, 0, sizeof(usb_buf)); + + switch(mode) + { + /*-------------------------------------------------*\ + | For Direct, Fade, and Strobe, the packet format: | + | 0: Report ID (Always 0) | + | 1: Mode (1: Direct, 2: Fade, 3: Strobe) | + | 2: LED Index | + | 3: Red | + | 4: Green | + | 5: Blue | + | 6: Changing Time (Fade) / Speed (Strobe) | + | 7: Unused | + | 8: Repeat (Strobe) | + \*-------------------------------------------------*/ + case LUXAFOR_MODE_DIRECT: + case LUXAFOR_MODE_FADE: + case LUXAFOR_MODE_STROBE: + usb_buf[0] = 0x00; + usb_buf[1] = mode; + usb_buf[2] = led; + usb_buf[3] = red; + usb_buf[4] = grn; + usb_buf[5] = blu; + usb_buf[6] = 100; + usb_buf[7] = 0; + usb_buf[8] = (mode == LUXAFOR_MODE_STROBE) ? 255 : 0; + break; + + /*-------------------------------------------------*\ + | For Wave, the packet format: | + | 0: Report ID (Always 0) | + | 1: Mode (4: Wave) | + | 2: Wave Type (1-5) | + | 3: Red | + | 4: Green | + | 5: Blue | + | 6: Unused | + | 7: Repeat | + | 8: Speed | + \*-------------------------------------------------*/ + case LUXAFOR_MODE_WAVE: + usb_buf[0] = 0x00; + usb_buf[1] = mode; + usb_buf[2] = type; + usb_buf[3] = red; + usb_buf[4] = grn; + usb_buf[5] = blu; + usb_buf[6] = 0; + usb_buf[7] = 255; + usb_buf[8] = 100; + break; + + /*-------------------------------------------------*\ + | For Pattern, the packet format: | + | 0: Report ID (Always 0) | + | 1: Mode (6: Pattern) | + | 2: Pattern Number (1-8) | + | 3: Repeat | + | 4: Unused | + | 5: Unused | + | 6: Unused | + | 7: Unused | + | 8: Unused | + \*-------------------------------------------------*/ + case LUXAFOR_MODE_PATTERN: + usb_buf[0] = 0x00; + usb_buf[1] = mode; + usb_buf[2] = type; + usb_buf[3] = 255; + usb_buf[4] = 0; + usb_buf[5] = 0; + usb_buf[6] = 0; + usb_buf[7] = 0; + usb_buf[8] = 0; + break; + } + + hid_write(dev, usb_buf, sizeof(usb_buf)); +} diff --git a/Controllers/LuxaforController/LuxaforController.h b/Controllers/LuxaforController/LuxaforController.h new file mode 100644 index 0000000..7c1067d --- /dev/null +++ b/Controllers/LuxaforController/LuxaforController.h @@ -0,0 +1,60 @@ +/*---------------------------------------------------------*\ +| LuxaforController.h | +| | +| Driver for Luxafor devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 05 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +enum +{ + LUXAFOR_LED_FIRST = 1, + LUXAFOR_LED_ALL = 255, +}; + +enum +{ + LUXAFOR_MODE_DIRECT = 1, + LUXAFOR_MODE_FADE = 2, + LUXAFOR_MODE_STROBE = 3, + LUXAFOR_MODE_WAVE = 4, + LUXAFOR_MODE_PATTERN = 6, +}; + +enum +{ + LUXAFOR_PATTERN_TRAFFIC_LIGHTS = 1, + LUXAFOR_PATTERN_2 = 2, + LUXAFOR_PATTERN_3 = 3, + LUXAFOR_PATTERN_4 = 4, + LUXAFOR_PATTERN_POLICE = 5, + LUXAFOR_PATTERN_6 = 6, + LUXAFOR_PATTERN_7 = 7, + LUXAFOR_PATTERN_8 = 8, +}; + +class LuxaforController +{ +public: + LuxaforController(hid_device* dev_handle, const char* path, std::string dev_name); + ~LuxaforController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendPacket(unsigned char mode, unsigned char led, unsigned char red, unsigned char grn, unsigned char blu, unsigned char type); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/LuxaforController/LuxaforControllerDetect.cpp b/Controllers/LuxaforController/LuxaforControllerDetect.cpp new file mode 100644 index 0000000..328ee9c --- /dev/null +++ b/Controllers/LuxaforController/LuxaforControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| LuxaforControllerDetect.cpp | +| | +| Detector for Luxafor devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 05 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LuxaforController.h" +#include "RGBController_Luxafor.h" + +/*---------------------------------------------------------*\ +| Luxafor USB Vendor ID | +\*---------------------------------------------------------*/ +#define LUXAFOR_VID 0x04D8 + +/*---------------------------------------------------------*\ +| Luxafor USB Product ID | +\*---------------------------------------------------------*/ +#define LUXAFOR_FLAG_PID 0xF372 + +void DetectLuxaforControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + LuxaforController* controller = new LuxaforController(dev, info->path, name); + RGBController_Luxafor* rgb_controller = new RGBController_Luxafor(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR( "Luxafor Flag", DetectLuxaforControllers, LUXAFOR_VID, LUXAFOR_FLAG_PID ); diff --git a/Controllers/LuxaforController/RGBController_Luxafor.cpp b/Controllers/LuxaforController/RGBController_Luxafor.cpp new file mode 100644 index 0000000..5315659 --- /dev/null +++ b/Controllers/LuxaforController/RGBController_Luxafor.cpp @@ -0,0 +1,239 @@ +/*---------------------------------------------------------*\ +| RGBController_Luxafor.cpp | +| | +| RGBController for Luxafor devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 05 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Luxafor.h" + +RGBController_Luxafor::RGBController_Luxafor(LuxaforController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + type = DEVICE_TYPE_ACCESSORY; + vendor = "Luxafor"; + description = "Luxafor Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LUXAFOR_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + // mode Fade; + // Fade.name = "Fade"; + // Fade.value = LUXAFOR_MODE_FADE; + // Fade.flags = MODE_FLAG_HAS_PER_LED_COLOR; + // Fade.color_mode = MODE_COLORS_PER_LED; + // modes.push_back(Fade); + + // mode Strobe; + // Strobe.name = "Strobe"; + // Strobe.value = LUXAFOR_MODE_STROBE; + // Strobe.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + // Strobe.color_mode = MODE_COLORS_MODE_SPECIFIC; + // Strobe.colors_min = 1; + // Strobe.colors_max = 1; + // Strobe.colors.resize(1); + // modes.push_back(Strobe); + + // mode Wave; + // Wave.name = "Wave"; + // Wave.value = LUXAFOR_MODE_WAVE; + // Wave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + // Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + // Wave.colors_min = 1; + // Wave.colors_max = 1; + // Wave.colors.resize(1); + // modes.push_back(Wave); + + mode TrafficLights; + TrafficLights.name = "Traffic Lights"; + TrafficLights.value = LUXAFOR_MODE_PATTERN_TRAFFIC_LIGHTS; + TrafficLights.flags = 0; + TrafficLights.color_mode = MODE_COLORS_NONE; + modes.push_back(TrafficLights); + + mode Pattern2; + Pattern2.name = "Pattern 2"; + Pattern2.value = LUXAFOR_MODE_PATTERN_2; + Pattern2.flags = 0; + Pattern2.color_mode = MODE_COLORS_NONE; + modes.push_back(Pattern2); + + mode Pattern3; + Pattern3.name = "Pattern 3"; + Pattern3.value = LUXAFOR_MODE_PATTERN_3; + Pattern3.flags = 0; + Pattern3.color_mode = MODE_COLORS_NONE; + modes.push_back(Pattern3); + + mode Pattern4; + Pattern4.name = "Pattern 4"; + Pattern4.value = LUXAFOR_MODE_PATTERN_4; + Pattern4.flags = 0; + Pattern4.color_mode = MODE_COLORS_NONE; + modes.push_back(Pattern4); + + mode Police; + Police.name = "Police"; + Police.value = LUXAFOR_MODE_PATTERN_POLICE; + Police.flags = 0; + Police.color_mode = MODE_COLORS_NONE; + modes.push_back(Police); + + mode Pattern6; + Pattern6.name = "Pattern 6"; + Pattern6.value = LUXAFOR_MODE_PATTERN_6; + Pattern6.flags = 0; + Pattern6.color_mode = MODE_COLORS_NONE; + modes.push_back(Pattern6); + + mode Pattern7; + Pattern7.name = "Pattern 7"; + Pattern7.value = LUXAFOR_MODE_PATTERN_7; + Pattern7.flags = 0; + Pattern7.color_mode = MODE_COLORS_NONE; + modes.push_back(Pattern7); + + mode Pattern8; + Pattern8.name = "Pattern 8"; + Pattern8.value = LUXAFOR_MODE_PATTERN_8; + Pattern8.flags = 0; + Pattern8.color_mode = MODE_COLORS_NONE; + modes.push_back(Pattern8); + + SetupZones(); +} + +RGBController_Luxafor::~RGBController_Luxafor() +{ + +} + +void RGBController_Luxafor::SetupZones() +{ + /*-----------------------------------------------------*\ + | The Luxafor Flag has 2 zones | + | * Flag (3 LEDs) | + | * Rear (3 LEDs) | + | The LED index starts at 1. Sending 255 for the LED ID | + | sets all LEDs at once. | + \*-----------------------------------------------------*/ + unsigned int led_value = LUXAFOR_LED_FIRST; + + zone flag_zone; + flag_zone.name = "Flag"; + flag_zone.type = ZONE_TYPE_SINGLE; + flag_zone.leds_min = 3; + flag_zone.leds_max = 3; + flag_zone.leds_count = 3; + flag_zone.matrix_map = NULL; + zones.push_back(flag_zone); + + for(std::size_t led_idx = 0; led_idx < flag_zone.leds_count; led_idx++) + { + led luxafor_led; + luxafor_led.name = "Flag LED"; + luxafor_led.value = led_value; + leds.push_back(luxafor_led); + + led_value++; + } + + zone rear_zone; + rear_zone.name = "Rear"; + rear_zone.type = ZONE_TYPE_SINGLE; + rear_zone.leds_min = 3; + rear_zone.leds_max = 3; + rear_zone.leds_count = 3; + rear_zone.matrix_map = NULL; + zones.push_back(rear_zone); + + for(std::size_t led_idx = 0; led_idx < rear_zone.leds_count; led_idx++) + { + led luxafor_led; + luxafor_led.name = "Rear LED"; + luxafor_led.value = led_value; + leds.push_back(luxafor_led); + + led_value++; + } + + SetupColors(); +} + +void RGBController_Luxafor::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*-----------------------------------------------------*\ + | This device does not support resizing zones | + \*-----------------------------------------------------*/ +} + +void RGBController_Luxafor::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + UpdateZoneLEDs((int)zone_idx); + } +} + +void RGBController_Luxafor::UpdateZoneLEDs(int zone) +{ + for(unsigned int led_idx = 0; led_idx < zones[zone].leds_count; led_idx++) + { + UpdateSingleLED((int)(zones[zone].start_idx + led_idx)); + } +} + +void RGBController_Luxafor::UpdateSingleLED(int led) +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + controller->SendPacket((modes[active_mode].value & 0xFF), leds[led].value, red, grn, blu, 0); + } +} + +void RGBController_Luxafor::DeviceUpdateMode() +{ + switch(modes[active_mode].color_mode) + { + case MODE_COLORS_PER_LED: + DeviceUpdateLEDs(); + break; + + case MODE_COLORS_MODE_SPECIFIC: + { + unsigned char red = RGBGetRValue(colors[modes[active_mode].colors[0]]); + unsigned char grn = RGBGetGValue(colors[modes[active_mode].colors[0]]); + unsigned char blu = RGBGetBValue(colors[modes[active_mode].colors[0]]); + + controller->SendPacket((modes[active_mode].value & 0xFF), LUXAFOR_LED_ALL, red, grn, blu, 0); + } + break; + + case MODE_COLORS_NONE: + controller->SendPacket((modes[active_mode].value & 0xFF), LUXAFOR_LED_ALL, 0, 0, 0, (modes[active_mode].value >> 8)); + break; + } +} + +void RGBController_Luxafor::DeviceSaveMode() +{ + /*-----------------------------------------------------*\ + | This device does not support saving | + \*-----------------------------------------------------*/ +} diff --git a/Controllers/LuxaforController/RGBController_Luxafor.h b/Controllers/LuxaforController/RGBController_Luxafor.h new file mode 100644 index 0000000..c05aafd --- /dev/null +++ b/Controllers/LuxaforController/RGBController_Luxafor.h @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| RGBController_Luxafor.h | +| | +| RGBController for Luxafor devices | +| | +| Adam Honse (calcprogrammer1@gmail.com) 05 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "LuxaforController.h" +#include "RGBController.h" + +/*---------------------------------------------------------*\ +| Additional "pseudo-modes" which combine pattern mode with | +| the pattern to use. | +\*---------------------------------------------------------*/ +enum +{ + LUXAFOR_MODE_PATTERN_TRAFFIC_LIGHTS = LUXAFOR_MODE_PATTERN + (LUXAFOR_PATTERN_TRAFFIC_LIGHTS << 8), + LUXAFOR_MODE_PATTERN_2 = LUXAFOR_MODE_PATTERN + (LUXAFOR_PATTERN_2 << 8), + LUXAFOR_MODE_PATTERN_3 = LUXAFOR_MODE_PATTERN + (LUXAFOR_PATTERN_3 << 8), + LUXAFOR_MODE_PATTERN_4 = LUXAFOR_MODE_PATTERN + (LUXAFOR_PATTERN_4 << 8), + LUXAFOR_MODE_PATTERN_POLICE = LUXAFOR_MODE_PATTERN + (LUXAFOR_PATTERN_POLICE << 8), + LUXAFOR_MODE_PATTERN_6 = LUXAFOR_MODE_PATTERN + (LUXAFOR_PATTERN_6 << 8), + LUXAFOR_MODE_PATTERN_7 = LUXAFOR_MODE_PATTERN + (LUXAFOR_PATTERN_7 << 8), + LUXAFOR_MODE_PATTERN_8 = LUXAFOR_MODE_PATTERN + (LUXAFOR_PATTERN_8 << 8), +}; + +class RGBController_Luxafor : public RGBController +{ +public: + RGBController_Luxafor(LuxaforController* controller_ptr); + ~RGBController_Luxafor(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + LuxaforController* controller; +}; diff --git a/Controllers/MNTKeyboardController/MNTKeyboardController.cpp b/Controllers/MNTKeyboardController/MNTKeyboardController.cpp new file mode 100644 index 0000000..764c2e4 --- /dev/null +++ b/Controllers/MNTKeyboardController/MNTKeyboardController.cpp @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| MNTKeyboardController.cpp | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "MNTKeyboardController.h" + +MNTKeyboardController::~MNTKeyboardController() +{ + hid_close(dev); +} + +void MNTKeyboardController::SendColorMatrix(unsigned char *color_map) +{ + unsigned char row_size = kbd_cols * KBD_COLOR_SIZE; + unsigned char cmdbuf_size = CMD_OFFSET + row_size; + unsigned char *usb_buf = new unsigned char[cmdbuf_size]; + memcpy(usb_buf, CMD_PREFIX, CMD_PREFIX_LEN); + for(unsigned int row_idx = 0; row_idx < KBD_ROWS; row_idx++) + { + usb_buf[CMD_PREFIX_LEN] = row_idx; + memcpy(usb_buf + CMD_OFFSET, color_map + row_idx * row_size, row_size); + hid_write(dev, usb_buf, cmdbuf_size); + } + delete[] usb_buf; +} diff --git a/Controllers/MNTKeyboardController/MNTKeyboardController.h b/Controllers/MNTKeyboardController/MNTKeyboardController.h new file mode 100644 index 0000000..8484dcf --- /dev/null +++ b/Controllers/MNTKeyboardController/MNTKeyboardController.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| MNTKeyboardController.h | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +#include "Detector.h" +#include "LogManager.h" + +#define KBD_ROWS 6 +#define KBD_COLOR_SIZE 3 + +#define CMD_PREFIX "xXRGB" +#define CMD_OFFSET (sizeof("xXRGB")) +#define CMD_PREFIX_LEN CMD_OFFSET - 1 + +#define KBD_VID 0x1209 +#define KBD_INTERFACE 0 +#define HID_USAGE_PAGE_DESKTOP 0x01 +#define HID_USAGE_DESKTOP_KEYBOARD 0x06 + +class MNTKeyboardController +{ + public: + ~MNTKeyboardController(); + void SendColorMatrix(unsigned char *color_map); + std::string location; + unsigned char kbd_cols; + + protected: + hid_device *dev; +}; diff --git a/Controllers/MNTKeyboardController/MNTKeyboardControllerDetect.cpp b/Controllers/MNTKeyboardController/MNTKeyboardControllerDetect.cpp new file mode 100644 index 0000000..db0954a --- /dev/null +++ b/Controllers/MNTKeyboardController/MNTKeyboardControllerDetect.cpp @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| MNTKeyboardControllerDetect.cpp | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LogManager.h" +#include +#include "MNTReformKeyboardController.h" +#include "MNTPocketReformKeyboardController.h" +#include "RGBController_MNTReformKeyboard.h" +#include "RGBController_MNTPocketReformKeyboard.h" + +#define PID_KBD_REFORM 0x6D02 +#define PID_KBD_POCKET_REFORM 0x6D06 + +void DetectMNTKeyboardControllers(hid_device_info *info, const std::string &name) +{ + LOG_DEBUG("[%s] trying to detect … ", name.c_str()); + hid_device *dev = hid_open_path(info->path); + if(dev) + { + LOG_DEBUG("[%s] found at %s", name.c_str(), info->path); + if(info->product_id == PID_KBD_REFORM) + { + MNTReformKeyboardController *controller = new MNTReformKeyboardController(dev, info->path); + RGBController_MNTReformKeyboard *rgb_controller = new RGBController_MNTReformKeyboard(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else if(info->product_id == PID_KBD_POCKET_REFORM) + { + MNTPocketReformKeyboardController *controller = new MNTPocketReformKeyboardController(dev, info->path); + RGBController_MNTPocketReformKeyboard *rgb_controller = new RGBController_MNTPocketReformKeyboard(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + return; + } + LOG_DEBUG("[%s] successfully registered", name.c_str()); + } +} + +REGISTER_HID_DETECTOR_IPU("MNT Reform Keyboard", DetectMNTKeyboardControllers, KBD_VID, PID_KBD_REFORM, KBD_INTERFACE, HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_KEYBOARD); +REGISTER_HID_DETECTOR_IPU("MNT Pocket Reform Keyboard", DetectMNTKeyboardControllers, KBD_VID, PID_KBD_POCKET_REFORM, KBD_INTERFACE, HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_KEYBOARD); diff --git a/Controllers/MNTKeyboardController/MNTPocketReformKeyboardController.cpp b/Controllers/MNTKeyboardController/MNTPocketReformKeyboardController.cpp new file mode 100644 index 0000000..5401a60 --- /dev/null +++ b/Controllers/MNTKeyboardController/MNTPocketReformKeyboardController.cpp @@ -0,0 +1,19 @@ +/*---------------------------------------------------------*\ +| MNTPocketReformKeyboardController.cpp | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "MNTPocketReformKeyboardController.h" + +MNTPocketReformKeyboardController::MNTPocketReformKeyboardController(hid_device *dev_handle, const char *path) +{ + dev = dev_handle; + location = path; + kbd_cols = KBD_COLS_POCKET_REFORM; +} diff --git a/Controllers/MNTKeyboardController/MNTPocketReformKeyboardController.h b/Controllers/MNTKeyboardController/MNTPocketReformKeyboardController.h new file mode 100644 index 0000000..55d0f3d --- /dev/null +++ b/Controllers/MNTKeyboardController/MNTPocketReformKeyboardController.h @@ -0,0 +1,22 @@ +/*---------------------------------------------------------*\ +| MNTPocketReformKeyboardController.cpp | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "MNTKeyboardController.h" + +#define KBD_COLS_POCKET_REFORM 12 + +class MNTPocketReformKeyboardController : public MNTKeyboardController +{ + public: + MNTPocketReformKeyboardController(hid_device *dev_handle, const char *path); +}; diff --git a/Controllers/MNTKeyboardController/MNTReformKeyboardController.cpp b/Controllers/MNTKeyboardController/MNTReformKeyboardController.cpp new file mode 100644 index 0000000..c824056 --- /dev/null +++ b/Controllers/MNTKeyboardController/MNTReformKeyboardController.cpp @@ -0,0 +1,19 @@ +/*---------------------------------------------------------*\ +| MNTReformKeyboardController.cpp | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "MNTReformKeyboardController.h" + +MNTReformKeyboardController::MNTReformKeyboardController(hid_device *dev_handle, const char *path) +{ + dev = dev_handle; + location = path; + kbd_cols = KBD_COLS_REFORM; +} diff --git a/Controllers/MNTKeyboardController/MNTReformKeyboardController.h b/Controllers/MNTKeyboardController/MNTReformKeyboardController.h new file mode 100644 index 0000000..6bd8273 --- /dev/null +++ b/Controllers/MNTKeyboardController/MNTReformKeyboardController.h @@ -0,0 +1,22 @@ +/*---------------------------------------------------------*\ +| MNTReformKeyboardController.h | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "MNTKeyboardController.h" + +#define KBD_COLS_REFORM 14 + +class MNTReformKeyboardController : public MNTKeyboardController +{ + public: + MNTReformKeyboardController(hid_device *dev_handle, const char *path); +}; diff --git a/Controllers/MNTKeyboardController/RGBController_MNTKeyboard.cpp b/Controllers/MNTKeyboardController/RGBController_MNTKeyboard.cpp new file mode 100644 index 0000000..cbbda49 --- /dev/null +++ b/Controllers/MNTKeyboardController/RGBController_MNTKeyboard.cpp @@ -0,0 +1,81 @@ +/*---------------------------------------------------------*\ +| RGBController_MNTKeyboard.cpp | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MNTKeyboard.h" + +void RGBController_MNTKeyboard::CommonInit() +{ + vendor = "MNT Research"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->location; + modes.resize(1); + modes[0].name = "Direct"; + modes[0].flags = MODE_FLAG_HAS_PER_LED_COLOR; + modes[0].color_mode = MODE_COLORS_PER_LED; + SetupZones(); + SetAllLEDs(ToRGBColor(255, 255, 255)); + DeviceUpdateLEDs(); +} + +RGBController_MNTKeyboard::~RGBController_MNTKeyboard() +{ + delete zones[0].matrix_map; + delete controller; +} + +void RGBController_MNTKeyboard::SetupZones() +{ + zone new_zone; + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_count = KBD_ROWS * controller->kbd_cols; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = KBD_ROWS; + new_zone.matrix_map->width = controller->kbd_cols; + new_zone.matrix_map->map = (unsigned int *)matrix_keys; + zones.push_back(new_zone); + for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + SetupColors(); +} + +void RGBController_MNTKeyboard::DeviceUpdateLEDs() +{ + unsigned char *color_map = new unsigned char[zones[0].leds_count * KBD_COLOR_SIZE]; + for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + RGBColor color = colors[led_idx]; + int offset = led_idx * KBD_COLOR_SIZE; + color_map[offset + 2] = RGBGetRValue(color); + color_map[offset + 1] = RGBGetGValue(color); + color_map[offset + 0] = RGBGetBValue(color); + } + controller->SendColorMatrix(color_map); + delete[] color_map; +} + +void RGBController_MNTKeyboard::ResizeZone(int, int) +{ +} +void RGBController_MNTKeyboard::UpdateZoneLEDs(int) +{ + DeviceUpdateLEDs(); +} +void RGBController_MNTKeyboard::UpdateSingleLED(int) +{ + DeviceUpdateLEDs(); +} +void RGBController_MNTKeyboard::DeviceUpdateMode() +{ +} diff --git a/Controllers/MNTKeyboardController/RGBController_MNTKeyboard.h b/Controllers/MNTKeyboardController/RGBController_MNTKeyboard.h new file mode 100644 index 0000000..e785714 --- /dev/null +++ b/Controllers/MNTKeyboardController/RGBController_MNTKeyboard.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_MNTKeyboard.h | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RGBControllerKeyNames.h" +#include "MNTKeyboardController.h" + +#define NA 0xFFFFFFFF + +class RGBController_MNTKeyboard : public RGBController +{ + public: + ~RGBController_MNTKeyboard(); + void SetupZones(); + void DeviceUpdateLEDs(); + + void ResizeZone(int, int); + void UpdateZoneLEDs(int); + void UpdateSingleLED(int); + void DeviceUpdateMode(); + + protected: + const char **led_names; + unsigned int *matrix_keys; + void CommonInit(); + MNTKeyboardController *controller; +}; diff --git a/Controllers/MNTKeyboardController/RGBController_MNTPocketReformKeyboard.cpp b/Controllers/MNTKeyboardController/RGBController_MNTPocketReformKeyboard.cpp new file mode 100644 index 0000000..f99d62a --- /dev/null +++ b/Controllers/MNTKeyboardController/RGBController_MNTPocketReformKeyboard.cpp @@ -0,0 +1,121 @@ +/*---------------------------------------------------------*\ +| RGBController_MNTPocketReformKeyboard.cpp | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MNTPocketReformKeyboard.h" + +/**----------------------------------------------*\ + @name MNT Pocket Reform Keyboard + @category Keyboard + @type USB + @detectors DetectMNTKeyboardControllers +\*----------------------------------------------**/ + +static unsigned int matrix_keys_MNTPocketReform[KBD_ROWS][KBD_COLS_POCKET_REFORM] = + { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, + {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, + {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}, + {36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, + {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59}, + {NA, NA, NA, 63, 64, NA, NA, 69, 70, NA, NA, NA}, +}; + +static const char *led_names_MNTPocketReform[] = + { + // row 0 + KEY_EN_ESCAPE, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_BACKSPACE, + // row 1 + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_SEMICOLON, + // row 2 + KEY_EN_LEFT_CONTROL, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + // row 3 + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_UP_ARROW, + KEY_EN_RIGHT_ALT, + // row 4 + KEY_EN_LEFT_FUNCTION, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_BACK_SLASH, + KEY_EN_EQUALS, + KEY_EN_SPACE, + KEY_EN_SPACE, + KEY_EN_MINUS, + KEY_EN_FORWARD_SLASH, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + // row 5 + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + "Key: Left Button", + "Key: Scroll Mode", + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + "Key: Middle Button", + "Key: Right Button", + KEY_EN_UNUSED, +}; + +RGBController_MNTPocketReformKeyboard::RGBController_MNTPocketReformKeyboard(MNTPocketReformKeyboardController *controller_ptr) +{ + led_names = led_names_MNTPocketReform; + matrix_keys = matrix_keys_MNTPocketReform[0]; + controller = controller_ptr; + name = "MNT Pocket Reform Keyboard"; + description = "MNT Pocket Reform Keyboard"; + CommonInit(); +} diff --git a/Controllers/MNTKeyboardController/RGBController_MNTPocketReformKeyboard.h b/Controllers/MNTKeyboardController/RGBController_MNTPocketReformKeyboard.h new file mode 100644 index 0000000..73d02b6 --- /dev/null +++ b/Controllers/MNTKeyboardController/RGBController_MNTPocketReformKeyboard.h @@ -0,0 +1,21 @@ +/*---------------------------------------------------------*\ +| RGBController_MNTPocketReformKeyboard.h | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController_MNTKeyboard.h" +#include "MNTPocketReformKeyboardController.h" + +class RGBController_MNTPocketReformKeyboard : public RGBController_MNTKeyboard +{ + public: + RGBController_MNTPocketReformKeyboard(MNTPocketReformKeyboardController *controller_ptr); +}; diff --git a/Controllers/MNTKeyboardController/RGBController_MNTReformKeyboard.cpp b/Controllers/MNTKeyboardController/RGBController_MNTReformKeyboard.cpp new file mode 100644 index 0000000..52de11d --- /dev/null +++ b/Controllers/MNTKeyboardController/RGBController_MNTReformKeyboard.cpp @@ -0,0 +1,131 @@ +/*---------------------------------------------------------*\ +| RGBController_MNTReformKeyboard.cpp | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MNTReformKeyboard.h" + +/**----------------------------------------------*\ + @name MNT Reform Keyboard + @category Keyboard + @type USB + @detectors DetectMNTKeyboardControllers +\*----------------------------------------------**/ + +static unsigned int matrix_keys_MNTReform[KBD_ROWS][KBD_COLS_REFORM] = + { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + {14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}, + {28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41}, + {42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, NA}, + {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69}, + {70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, NA, NA, NA}}; + +static const char *led_names_MNTReform[] = + { + // row 0 + KEY_EN_ESCAPE, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_POWER, + // row 1 + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + // row 2 + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + // row 3 + KEY_EN_LEFT_CONTROL, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + KEY_EN_UNUSED, + // row 4 + KEY_EN_LEFT_SHIFT, + KEY_EN_DELETE, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_UP_ARROW, + KEY_EN_RIGHT_SHIFT, + // row 5 + KEY_EN_LEFT_FUNCTION, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_SPACE, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED}; + +RGBController_MNTReformKeyboard::RGBController_MNTReformKeyboard(MNTReformKeyboardController *controller_ptr) +{ + led_names = led_names_MNTReform; + matrix_keys = matrix_keys_MNTReform[0]; + controller = controller_ptr; + name = "MNT Reform Keyboard"; + description = "MNT Reform Keyboard"; + CommonInit(); +} diff --git a/Controllers/MNTKeyboardController/RGBController_MNTReformKeyboard.h b/Controllers/MNTKeyboardController/RGBController_MNTReformKeyboard.h new file mode 100644 index 0000000..6b4b3b7 --- /dev/null +++ b/Controllers/MNTKeyboardController/RGBController_MNTReformKeyboard.h @@ -0,0 +1,21 @@ +/*---------------------------------------------------------*\ +| RGBController_MNTReformKeyboard.h | +| | +| Driver for the MNT Reform keyboards | +| | +| Christian Heller 7 Aug 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController_MNTKeyboard.h" +#include "MNTReformKeyboardController.h" + +class RGBController_MNTReformKeyboard : public RGBController_MNTKeyboard +{ + public: + RGBController_MNTReformKeyboard(MNTReformKeyboardController *controller_ptr); +}; diff --git a/Controllers/MSI3ZoneController/MSI3ZoneController.cpp b/Controllers/MSI3ZoneController/MSI3ZoneController.cpp new file mode 100644 index 0000000..19b03cf --- /dev/null +++ b/Controllers/MSI3ZoneController/MSI3ZoneController.cpp @@ -0,0 +1,100 @@ +/*---------------------------------------------------------*\ +| MSI3ZoneController.cpp | +| | +| Driver for MSI/SteelSeries 3-Zone keyboard | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "MSI3ZoneController.h" +#include "StringUtils.h" + +MSI3ZoneController::MSI3ZoneController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + //strcpy(device_name, "MSI 3-Zone Keyboard"); +} + +MSI3ZoneController::~MSI3ZoneController() +{ + hid_close(dev); +} + +char* MSI3ZoneController::GetDeviceName() +{ + return device_name; +} + +std::string MSI3ZoneController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MSI3ZoneController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void MSI3ZoneController::SetLEDs(std::vector colors) +{ + //Shout out to bparker06 for reverse engineering the MSI keyboard USB protocol! + // https://github.com/bparker06/msi-keyboard/blob/master/keyboard.cpp for original implementation + unsigned char buf[8] = { 0 }; + + buf[0] = 1; + buf[1] = 2; + buf[2] = 64; + buf[3] = 1; + buf[4] = RGBGetRValue(colors[0]); + buf[5] = RGBGetGValue(colors[0]); + buf[6] = RGBGetBValue(colors[0]); + buf[7] = 236; + + hid_send_feature_report(dev, buf, 8); + + buf[3] = 2; + buf[4] = RGBGetRValue(colors[1]); + buf[5] = RGBGetGValue(colors[1]); + buf[6] = RGBGetBValue(colors[1]); + + hid_send_feature_report(dev, buf, 8); + + buf[3] = 3; + buf[4] = RGBGetRValue(colors[2]); + buf[5] = RGBGetGValue(colors[2]); + buf[6] = RGBGetBValue(colors[2]); + + hid_send_feature_report(dev, buf, 8); + + buf[3] = 4; + buf[4] = RGBGetRValue(colors[3]); + buf[5] = RGBGetGValue(colors[3]); + buf[6] = RGBGetBValue(colors[3]); + + hid_send_feature_report(dev, buf, 8); + + buf[3] = 5; + + hid_send_feature_report(dev, buf, 8); + + buf[3] = 6; + + hid_send_feature_report(dev, buf, 8); + + buf[3] = 7; + + hid_send_feature_report(dev, buf, 8); +} diff --git a/Controllers/MSI3ZoneController/MSI3ZoneController.h b/Controllers/MSI3ZoneController/MSI3ZoneController.h new file mode 100644 index 0000000..3b38fe3 --- /dev/null +++ b/Controllers/MSI3ZoneController/MSI3ZoneController.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| MSI3ZoneController.h | +| | +| Driver for MSI/SteelSeries 3-Zone keyboard | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class MSI3ZoneController +{ +public: + MSI3ZoneController(hid_device* dev_handle, const char* path); + ~MSI3ZoneController(); + + char* GetDeviceName(); + std::string GetDeviceLocation(); + std::string GetSerialString(); + + void SetLEDs(std::vector colors); + +private: + char device_name[32]; + hid_device* dev; + std::string location; +}; diff --git a/Controllers/MSI3ZoneController/MSI3ZoneControllerDetect.cpp b/Controllers/MSI3ZoneController/MSI3ZoneControllerDetect.cpp new file mode 100644 index 0000000..10313dd --- /dev/null +++ b/Controllers/MSI3ZoneController/MSI3ZoneControllerDetect.cpp @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| MSI3ZoneControllerDetect.cpp | +| | +| Detector for MSI/SteelSeries 3-Zone keyboard | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "MSI3ZoneController.h" +#include "RGBController_MSI3Zone.h" + +#define MSI_3_ZONE_KEYBOARD_VID 0x1770 +#define MSI_3_ZONE_KEYBOARD_PID 0xFF00 + +/******************************************************************************************\ +* * +* DetectMSI3ZoneControllers * +* * +* Tests the USB address to see if an MSI/SteelSeries 3-zone Keyboard controller * +* exists there. * +* * +\******************************************************************************************/ + +void DetectMSI3ZoneControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + MSI3ZoneController* controller = new MSI3ZoneController(dev, info->path); + RGBController_MSI3Zone* rgb_controller = new RGBController_MSI3Zone(controller); + // Constructor sets the name + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectMSI3ZoneControllers() */ + +REGISTER_HID_DETECTOR("MSI 3-Zone Laptop", DetectMSI3ZoneControllers, MSI_3_ZONE_KEYBOARD_VID, MSI_3_ZONE_KEYBOARD_PID); diff --git a/Controllers/MSI3ZoneController/RGBController_MSI3Zone.cpp b/Controllers/MSI3ZoneController/RGBController_MSI3Zone.cpp new file mode 100644 index 0000000..dac237d --- /dev/null +++ b/Controllers/MSI3ZoneController/RGBController_MSI3Zone.cpp @@ -0,0 +1,121 @@ +/*---------------------------------------------------------*\ +| RGBController_MSI3Zone.cpp | +| | +| RGBController for MSI/SteelSeries 3-Zone keyboard | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MSI3Zone.h" + +/**------------------------------------------------------------------*\ + @name MSI 3 Zone Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectMSI3ZoneControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSI3Zone::RGBController_MSI3Zone(MSI3ZoneController* controller_ptr) +{ + controller = controller_ptr; + + name = "MSI 3-Zone Keyboard"; + vendor = "MSI"; + type = DEVICE_TYPE_LAPTOP; + description = "MSI 3-Zone Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_MSI3Zone::~RGBController_MSI3Zone() +{ + delete controller; +} + +void RGBController_MSI3Zone::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up Keyboard zone and Keyboard LEDs | + \*---------------------------------------------------------*/ + zone keyboard_zone; + keyboard_zone.name = "Keyboard"; + keyboard_zone.type = ZONE_TYPE_LINEAR; + keyboard_zone.leds_min = 3; + keyboard_zone.leds_max = 3; + keyboard_zone.leds_count = 3; + keyboard_zone.matrix_map = NULL; + zones.push_back(keyboard_zone); + + led left_led; + left_led.name = "Keyboard Left"; + leds.push_back(left_led); + + led mid_led; + mid_led.name = "Keyboard Middle"; + leds.push_back(mid_led); + + led right_led; + right_led.name = "Keyboard Right"; + leds.push_back(right_led); + + /*---------------------------------------------------------*\ + | Set up Aux zone and Aux LED | + \*---------------------------------------------------------*/ + zone aux_zone; + aux_zone.name = "Aux"; + aux_zone.type = ZONE_TYPE_SINGLE; + aux_zone.leds_min = 1; + aux_zone.leds_max = 1; + aux_zone.leds_count = 1; + aux_zone.matrix_map = NULL; + zones.push_back(aux_zone); + + led aux_led; + aux_led.name = "Aux"; + leds.push_back(aux_led); + + SetupColors(); +} + +void RGBController_MSI3Zone::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_MSI3Zone::DeviceUpdateLEDs() +{ + controller->SetLEDs(colors); +} + +void RGBController_MSI3Zone::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_MSI3Zone::UpdateSingleLED(int /*led*/) +{ + controller->SetLEDs(colors); +} + +void RGBController_MSI3Zone::DeviceUpdateMode() +{ + +} diff --git a/Controllers/MSI3ZoneController/RGBController_MSI3Zone.h b/Controllers/MSI3ZoneController/RGBController_MSI3Zone.h new file mode 100644 index 0000000..c71df36 --- /dev/null +++ b/Controllers/MSI3ZoneController/RGBController_MSI3Zone.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_MSI3Zone.h | +| | +| RGBController for MSI/SteelSeries 3-Zone keyboard | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "MSI3ZoneController.h" + +class RGBController_MSI3Zone : public RGBController +{ +public: + RGBController_MSI3Zone(MSI3ZoneController* controller_ptr); + ~RGBController_MSI3Zone(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + MSI3ZoneController* controller; +}; diff --git a/Controllers/MSIGPUController/MSIGPUController/MSIGPUController.cpp b/Controllers/MSIGPUController/MSIGPUController/MSIGPUController.cpp new file mode 100644 index 0000000..437695e --- /dev/null +++ b/Controllers/MSIGPUController/MSIGPUController/MSIGPUController.cpp @@ -0,0 +1,79 @@ +/*---------------------------------------------------------*\ +| MSIGPUController.cpp | +| | +| Driver for MSI GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "MSIGPUController.h" +#include + +MSIGPUController::MSIGPUController(i2c_smbus_interface* bus, msi_gpu_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +MSIGPUController::~MSIGPUController() +{ + +} + +std::string MSIGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string MSIGPUController::GetDeviceName() +{ + return(name); +} + +void MSIGPUController::SetRGB1(unsigned char red, unsigned char green, unsigned char blue) +{ + MSIGPURegisterWrite(MSI_GPU_REG_R1, red); + MSIGPURegisterWrite(MSI_GPU_REG_G1, green); + MSIGPURegisterWrite(MSI_GPU_REG_B1, blue); +} + +void MSIGPUController::SetRGB2(unsigned char red, unsigned char green, unsigned char blue) +{ + MSIGPURegisterWrite(MSI_GPU_REG_R2, red); + MSIGPURegisterWrite(MSI_GPU_REG_G2, green); + MSIGPURegisterWrite(MSI_GPU_REG_B2, blue); +} + +void MSIGPUController::SetRGB3(unsigned char red, unsigned char green, unsigned char blue) +{ + MSIGPURegisterWrite(MSI_GPU_REG_R3, red); + MSIGPURegisterWrite(MSI_GPU_REG_G3, green); + MSIGPURegisterWrite(MSI_GPU_REG_B3, blue); +} + +void MSIGPUController::SetMode(unsigned char mode) +{ + MSIGPURegisterWrite(MSI_GPU_REG_MODE, mode); +} + +void MSIGPUController::Save() +{ + MSIGPURegisterWrite(MSI_GPU_REG_SAVE, 0x01); +} + +unsigned char MSIGPUController::MSIGPURegisterRead(unsigned char reg) +{ + return(bus->i2c_smbus_read_byte_data(dev, reg)); +} + +void MSIGPUController::MSIGPURegisterWrite(unsigned char reg, unsigned char val) +{ + bus->i2c_smbus_write_byte_data(dev, reg, val); +} diff --git a/Controllers/MSIGPUController/MSIGPUController/MSIGPUController.h b/Controllers/MSIGPUController/MSIGPUController/MSIGPUController.h new file mode 100644 index 0000000..4c667b9 --- /dev/null +++ b/Controllers/MSIGPUController/MSIGPUController/MSIGPUController.h @@ -0,0 +1,89 @@ +/*---------------------------------------------------------*\ +| MSIGPUController.h | +| | +| Driver for MSI GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char msi_gpu_dev_id; + +#define MSI_GPU_SPEED_MIN 0 +#define MSI_GPU_SPEED_MID 1 +#define MSI_GPU_SPEED_MAX 2 +#define MSI_GPU_BRIGHTNESS_MIN 0 +#define MSI_GPU_BRIGHTNESS_MAX 5 +#define MSI_GPU_BRIGHTNESS_MULTI 20 + +enum +{ + MSI_GPU_REG_BRIGHTNESS = 0x36, /* MSI GPU Brightness Register */ + MSI_GPU_REG_SPEED = 0x38, /* MSI GPU Speed Register */ + MSI_GPU_REG_UNKNOWN = 0x26, /* MSI GPU Unknown Register */ + MSI_GPU_REG_R1 = 0x30, /* MSI GPU R1 Register */ + MSI_GPU_REG_G1 = 0x31, /* MSI GPU G1 Register */ + MSI_GPU_REG_B1 = 0x32, /* MSI GPU B1 Register */ + MSI_GPU_REG_R2 = 0x27, /* MSI GPU R2 Register */ + MSI_GPU_REG_G2 = 0x28, /* MSI GPU G2 Register */ + MSI_GPU_REG_B2 = 0x29, /* MSI GPU B2 Register */ + MSI_GPU_REG_R3 = 0x2A, /* MSI GPU R3 Register */ + MSI_GPU_REG_G3 = 0x2B, /* MSI GPU G3 Register */ + MSI_GPU_REG_B3 = 0x2C, /* MSI GPU B3 Register */ + MSI_GPU_REG_MODE = 0x22, /* MSI GPU Mode Selection Register */ + MSI_GPU_REG_SAVE = 0x3F, /* MSI GPU Save Changes Register */ +}; + +enum +{ + MSI_GPU_MODE_OFF = 0x01, /* OFF mode */ + MSI_GPU_MODE_RAINBOW = 0x08, /* Rainbow effect mode */ + MSI_GPU_MODE_STATIC = 0x13, /* Static color mode */ + MSI_GPU_MODE_RAINDROP = 0x1A, /* Raindrop effect mode */ + MSI_GPU_MODE_MAGIC = 0x07, /* Magic effect mode */ + MSI_GPU_MODE_PATROLLING = 0x05, /* Patrolling effect mode */ + MSI_GPU_MODE_STREAMING = 0x06, /* Streaming effect mode */ + MSI_GPU_MODE_LIGHTNING = 0x15, /* Lightning effect mode */ + MSI_GPU_MODE_WAVE = 0x1F, /* Wave effect mode */ + MSI_GPU_MODE_METEOR = 0x16, /* Meteor effect mode */ + MSI_GPU_MODE_STACK = 0x0D, /* Stack effect mode */ + MSI_GPU_MODE_RHYTHM = 0x0B, /* Rhythm effect mode */ + MSI_GPU_MODE_FLOWING = 0x09, /* Flowing effect mode */ + MSI_GPU_MODE_WHIRLING = 0x0F, /* Whirling effect mode */ + MSI_GPU_MODE_TWISTING = 0x11, /* Twisting effect mode */ + MSI_GPU_MODE_LAMINATING = 0x1D, /* Laminating effect mode */ + MSI_GPU_MODE_FADEIN = 0x14, /* Fadein effect mode */ + MSI_GPU_MODE_BREATHING = 0x04, /* Breathing effect mode */ + MSI_GPU_MODE_FLASHING = 0x02, /* Flashing effect mode */ + MSI_GPU_MODE_DOUBLEFLASHING = 0x03, /* Doubleflashing effect mode */ +}; + +class MSIGPUController +{ +public: + MSIGPUController(i2c_smbus_interface* bus, msi_gpu_dev_id dev, std::string dev_name); + ~MSIGPUController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetRGB1(unsigned char red, unsigned char green, unsigned char blue); + void SetRGB2(unsigned char red, unsigned char green, unsigned char blue); + void SetRGB3(unsigned char red, unsigned char green, unsigned char blue); + + void SetMode(unsigned char mode); + void Save(); + + unsigned char MSIGPURegisterRead(unsigned char reg); + void MSIGPURegisterWrite(unsigned char reg, unsigned char val); + +private: + i2c_smbus_interface * bus; + msi_gpu_dev_id dev; + std::string name; +}; diff --git a/Controllers/MSIGPUController/MSIGPUController/MSIGPUControllerDetect.cpp b/Controllers/MSIGPUController/MSIGPUController/MSIGPUControllerDetect.cpp new file mode 100644 index 0000000..7d100a2 --- /dev/null +++ b/Controllers/MSIGPUController/MSIGPUController/MSIGPUControllerDetect.cpp @@ -0,0 +1,133 @@ +/*---------------------------------------------------------*\ +| MSIGPUControllerDetect.cpp | +| | +| Detector for MSI GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "MSIGPUController.h" +#include "RGBController_MSIGPU.h" +#include "i2c_amd_gpu.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* DetectMSIGPUControllers * +* * +* Detect MSI GPU controllers on the enumerated I2C busses. * +* * +\******************************************************************************************/ + +void DetectMSIGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(bus->pci_vendor == NVIDIA_VEN && bus->port_id != 1) + { + return; + } + if(bus->pci_vendor == AMD_GPU_VEN && !is_amd_gpu_i2c_bus(bus)) + { + return; + } + + MSIGPUController* controller = new MSIGPUController(bus, i2c_addr, name); + RGBController_MSIGPU* rgb_controller = new RGBController_MSIGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + +} /* DetectMSIGPUControllers() */ + +/*-----------------------------------------*\ +| Nvidia GPUs | +\*-----------------------------------------*/ + +REGISTER_I2C_PCI_DETECTOR("MSI GeForce GTX 1070 Gaming X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, MSI_SUB_VEN, MSI_GTX1070_GAMING_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce GTX 1660 Gaming X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660_DEV, MSI_SUB_VEN, MSI_GTX1660_GAMING_X_6G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce GTX 1660 Ti Gaming", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660TI_DEV, MSI_SUB_VEN, MSI_GTX1660TI_GAMING_6G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce GTX 1660 Ti Gaming X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660TI_DEV, MSI_SUB_VEN, MSI_GTX1660TI_GAMING_X_6G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce GTX 1660 SUPER Gaming", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660S_DEV, MSI_SUB_VEN, MSI_GTX1660S_GAMING_6G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce GTX 1660 SUPER Gaming X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_GTX1660S_DEV, MSI_SUB_VEN, MSI_GTX1660S_GAMING_X_6G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2060 Gaming Z", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU104_DEV, MSI_SUB_VEN, MSI_RTX2060_GAMING_Z_6G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2060 Gaming Z", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, MSI_SUB_VEN, MSI_RTX2060_GAMING_Z_6G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2060 Gaming Z", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, MSI_SUB_VEN, MSI_RTX2060_GAMING_Z_6G_SUB_DEV_2, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2060 SUPER Gaming X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, MSI_SUB_VEN, MSI_RTX2060S_GAMING_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2060 SUPER ARMOR OC", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, MSI_SUB_VEN, MSI_RTX2060S_ARMOR_OC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2070 Gaming Z", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, MSI_SUB_VEN, MSI_RTX2070_GAMING_Z_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2070 Gaming", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_DEV, MSI_SUB_VEN, MSI_RTX2070_GAMING_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2070 ARMOR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_DEV, MSI_SUB_VEN, MSI_RTX2070_ARMOR_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2070 ARMOR OC", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, MSI_SUB_VEN, MSI_RTX2070_ARMOR_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2070 SUPER ARMOR OC", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, MSI_SUB_VEN, MSI_RTX2070S_ARMOR_OC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2070 SUPER Gaming", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, MSI_SUB_VEN, MSI_RTX2070S_GAMING_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2070 SUPER Gaming Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, MSI_SUB_VEN, MSI_RTX2070S_GAMING_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2070 SUPER Gaming X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, MSI_SUB_VEN, MSI_RTX2070S_GAMING_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2070 SUPER Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, MSI_SUB_VEN, MSI_RTX2070S_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2070 SUPER Gaming Z Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, MSI_SUB_VEN, MSI_RTX2070S_GAMING_Z_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2080 Gaming Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_DEV, MSI_SUB_VEN, MSI_RTX2080_GAMING_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2080 Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, MSI_SUB_VEN, MSI_RTX2080_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2080 Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, MSI_SUB_VEN, MSI_RTX2080_GAMING_X_TRIO_SUB_DEV_2, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2080 Sea Hawk EK X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, MSI_SUB_VEN, MSI_RTX2080_SEA_HAWK_EK_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2080 Duke OC", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, MSI_SUB_VEN, MSI_RTX2080_DUKE_OC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2080 SUPER Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, MSI_SUB_VEN, MSI_RTX2080S_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2080 Ti Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, MSI_SUB_VEN, MSI_RTX2080TI_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2080 Ti Gaming Z Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, MSI_SUB_VEN, MSI_RTX2080TI_GAMING_Z_TRIO_SUB_DEV, 0X68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2080 Ti Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, MSI_SUB_VEN, MSI_RTX2080TI_11G_GAMING_X_TRIO_SUB_DEV,0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 2080 Ti Sea Hawk EK X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, MSI_SUB_VEN, MSI_RTX2080TI_SEA_HAWK_EK_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3050 Gaming X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3050_DEV, MSI_SUB_VEN, MSI_RTX3060_GAMING_X_8G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Gaming X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, MSI_SUB_VEN, MSI_RTX3060_GAMING_X_12G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Gaming X LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, MSI_SUB_VEN, MSI_RTX3060_GAMING_X_12G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Gaming X (GA104)", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA104_DEV, MSI_SUB_VEN, MSI_RTX3060_GAMING_X_12G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Gaming X Trio LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, MSI_SUB_VEN, MSI_RTX3070_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, MSI_SUB_VEN, MSI_RTX3070_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Gaming Z Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_GA104_DEV, MSI_SUB_VEN, MSI_RTX3070_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Ti Gaming X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, MSI_SUB_VEN, MSI_RTX3060TI_GAMING_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Ti Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, MSI_SUB_VEN, MSI_RTX3070_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Ti Gaming X LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, MSI_SUB_VEN, MSI_RTX3060TI_GAMING_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Ti Gaming X Trio LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, MSI_SUB_VEN, MSI_RTX3070_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Ti SUPER 3X OC", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_GDDR6X_DEV,MSI_SUB_VEN, MSI_RTX3060TI_SUPER_3X_OC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3060 Ti Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_GDDR6X_DEV,MSI_SUB_VEN, MSI_RTX3060TI_GAMING_X_TRIO_8G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3070 Gaming Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, MSI_SUB_VEN, MSI_RTX3070_GAMING_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3070 Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, MSI_SUB_VEN, MSI_RTX3070_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3070 Suprim", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, MSI_SUB_VEN, MSI_RTX3070_SUPRIM_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3070 Suprim LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, MSI_SUB_VEN, MSI_RTX3070_SUPRIM_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3070 Suprim X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, MSI_SUB_VEN, MSI_RTX3070_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3070 Suprim X LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, MSI_SUB_VEN, MSI_RTX3070_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3070 Suprim X GODZILLA LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, MSI_SUB_VEN, MSI_RTX3070_SUPRIM_X_GODZILLA_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3070 Ti Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, MSI_SUB_VEN, MSI_RTX3070TI_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3070 Ti Suprim X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, MSI_SUB_VEN, MSI_RTX3070TI_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3080 Gaming Z Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, MSI_SUB_VEN, MSI_RTX3080_GAMING_Z_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3080 Gaming Z Trio LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, MSI_SUB_VEN, MSI_RTX3080_GAMING_Z_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3080 Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, MSI_SUB_VEN, MSI_RTX3080_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3080 Suprim X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, MSI_SUB_VEN, MSI_RTX3080_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3080 Suprim X LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, MSI_SUB_VEN, MSI_RTX3080_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3080 12GB Suprim X LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, MSI_SUB_VEN, MSI_RTX3080_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3080 12GB Gaming Z Trio LHR", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, MSI_SUB_VEN, MSI_RTX3080_12G_GAMING_Z_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3080 Ti Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, MSI_SUB_VEN, MSI_RTX3080TI_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3080 Ti Suprim X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, MSI_SUB_VEN, MSI_RTX3080TI_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3090 Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, MSI_SUB_VEN, MSI_RTX3090_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3090 Suprim", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, MSI_SUB_VEN, MSI_RTX3090_SUPRIM_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3090 Suprim X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, MSI_SUB_VEN, MSI_RTX3090_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3090 Ti Suprim X", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, MSI_SUB_VEN, MSI_RTX3090TI_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 3090 Ti Gaming X Trio", DetectMSIGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, MSI_SUB_VEN, MSI_RTX3090TI_GAMING_X_TRIO_SUB_DEV, 0x68); + +/*-----------------------------------------*\ +| AMD GPUs | +\*-----------------------------------------*/ + +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 5600 XT Gaming X", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI10_DEV, MSI_SUB_VEN, MSI_RX5600XT_GAMING_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6600 XT Gaming X", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI23_DEV, MSI_SUB_VEN, MSI_RX6600XT_GAMING_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6650 XT Gaming X", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI23_DEV1, MSI_SUB_VEN, MSI_RX6650XT_GAMING_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6700 XT Gaming X", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, MSI_SUB_VEN, MSI_RX6700XT_GAMING_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6750 XT Gaming X Trio", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI22_DEV, MSI_SUB_VEN, MSI_RX6750XT_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6800 Gaming Z Trio v1", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, MSI_SUB_VEN, MSI_RX6800_GAMING_Z_TRIO_V1_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6800 Gaming X Trio", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, MSI_SUB_VEN, MSI_RX6800_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6800 XT Gaming X Trio", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, MSI_SUB_VEN, MSI_RX6800XT_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6800 XT Gaming Z Trio", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, MSI_SUB_VEN, MSI_RX6800XT_GAMING_Z_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6900 XT Gaming X Trio", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, MSI_SUB_VEN, MSI_RX6900XT_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6900 XT Gaming Z Trio", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV2, MSI_SUB_VEN, MSI_RX6950XT_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6950 XT Gaming X Trio", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, MSI_SUB_VEN, MSI_RX6950XT_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 6950 XT Gaming X Trio", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI21_DEV3, MSI_SUB_VEN, MSI_RX6950XT_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 7900 XTX Gaming X Trio", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, MSI_SUB_VEN, MSI_RX7900XTX_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI Radeon RX 7900 XT Gaming Trio Classic", DetectMSIGPUControllers, AMD_GPU_VEN, AMD_NAVI31_DEV, MSI_SUB_VEN, MSI_RX7900XT_GAMING_TRIO_CLASSIC_SUB_DEV,0x68); diff --git a/Controllers/MSIGPUController/MSIGPUController/RGBController_MSIGPU.cpp b/Controllers/MSIGPUController/MSIGPUController/RGBController_MSIGPU.cpp new file mode 100644 index 0000000..442a9e4 --- /dev/null +++ b/Controllers/MSIGPUController/MSIGPUController/RGBController_MSIGPU.cpp @@ -0,0 +1,444 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIGPU.cpp | +| | +| RGBController for MSI GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_MSIGPU.h" + +static const std::array speed_values = { 0x04, 0x02, 0x01 }; + +int RGBController_MSIGPU::GetDeviceMode() +{ + unsigned char dev_mode = controller->MSIGPURegisterRead(MSI_GPU_REG_MODE); + + for(std::size_t mode = 0; mode < modes.size(); mode++) + { + if(modes[mode].value == dev_mode) + { + active_mode = (int)mode; + break; + } + } + + return(active_mode); +} + +int RGBController_MSIGPU::GetModeSpeed() +{ + unsigned char mode_speed = controller->MSIGPURegisterRead(MSI_GPU_REG_SPEED); + + for(std::size_t speed = 0; speed < speed_values.size(); speed++) + { + if(speed_values[speed] == mode_speed) + { + return((int)speed); + } + } + + return(0); +} + +/**------------------------------------------------------------------*\ + @name MSI GPU + @category GPU + @type I2C + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectMSIGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSIGPU::RGBController_MSIGPU(MSIGPUController * controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "MSI"; + type = DEVICE_TYPE_GPU; + description = name; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = MSI_GPU_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Direct.brightness = MSI_GPU_BRIGHTNESS_MAX; + Direct.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + modes.push_back(Direct); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = MSI_GPU_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Rainbow.speed_min = MSI_GPU_SPEED_MIN; + Rainbow.speed = MSI_GPU_SPEED_MID; + Rainbow.speed_max = MSI_GPU_SPEED_MAX; + Rainbow.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Rainbow.brightness = MSI_GPU_BRIGHTNESS_MAX; + Rainbow.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Raindrop; + Raindrop.name = "Raindrop"; + Raindrop.value = MSI_GPU_MODE_RAINDROP; + Raindrop.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Raindrop.speed_min = MSI_GPU_SPEED_MIN; + Raindrop.speed = MSI_GPU_SPEED_MID; + Raindrop.speed_max = MSI_GPU_SPEED_MAX; + Raindrop.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Raindrop.brightness = MSI_GPU_BRIGHTNESS_MAX; + Raindrop.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Raindrop.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Raindrop); + + mode Magic; + Magic.name = "Magic"; + Magic.value = MSI_GPU_MODE_MAGIC; + Magic.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Magic.speed_min = MSI_GPU_SPEED_MIN; + Magic.speed = MSI_GPU_SPEED_MID; + Magic.speed_max = MSI_GPU_SPEED_MAX; + Magic.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Magic.brightness = MSI_GPU_BRIGHTNESS_MAX; + Magic.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Magic.color_mode = MODE_COLORS_NONE; + modes.push_back(Magic); + + mode Patrolling; + Patrolling.name = "Patrolling"; + Patrolling.value = MSI_GPU_MODE_PATROLLING; + Patrolling.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Patrolling.speed_min = MSI_GPU_SPEED_MIN; + Patrolling.speed = MSI_GPU_SPEED_MID; + Patrolling.speed_max = MSI_GPU_SPEED_MAX; + Patrolling.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Patrolling.brightness = MSI_GPU_BRIGHTNESS_MAX; + Patrolling.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Patrolling.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Patrolling); + + mode Streaming; + Streaming.name = "Streaming"; + Streaming.value = MSI_GPU_MODE_STREAMING; + Streaming.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Streaming.speed_min = MSI_GPU_SPEED_MIN; + Streaming.speed = MSI_GPU_SPEED_MID; + Streaming.speed_max = MSI_GPU_SPEED_MAX; + Streaming.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Streaming.brightness = MSI_GPU_BRIGHTNESS_MAX; + Streaming.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Streaming.color_mode = MODE_COLORS_NONE; + modes.push_back(Streaming); + + mode Lightning; + Lightning.name = "Lightning"; + Lightning.value = MSI_GPU_MODE_LIGHTNING; + Lightning.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Lightning.speed_min = MSI_GPU_SPEED_MIN; + Lightning.speed = MSI_GPU_SPEED_MID; + Lightning.speed_max = MSI_GPU_SPEED_MAX; + Lightning.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Lightning.brightness = MSI_GPU_BRIGHTNESS_MAX; + Lightning.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Lightning.color_mode = MODE_COLORS_NONE; + modes.push_back(Lightning); + + mode Wave; + Wave.name = "Wave"; + Wave.value = MSI_GPU_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Wave.speed_min = MSI_GPU_SPEED_MIN; + Wave.speed = MSI_GPU_SPEED_MID; + Wave.speed_max = MSI_GPU_SPEED_MAX; + Wave.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Wave.brightness = MSI_GPU_BRIGHTNESS_MAX; + Wave.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Wave.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Wave); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = MSI_GPU_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Meteor.speed_min = MSI_GPU_SPEED_MIN; + Meteor.speed = MSI_GPU_SPEED_MID; + Meteor.speed_max = MSI_GPU_SPEED_MAX; + Meteor.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Meteor.brightness = MSI_GPU_BRIGHTNESS_MAX; + Meteor.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Meteor.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Meteor); + + mode Stack; + Stack.name = "Stack"; + Stack.value = MSI_GPU_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Stack.speed_min = MSI_GPU_SPEED_MIN; + Stack.speed = MSI_GPU_SPEED_MID; + Stack.speed_max = MSI_GPU_SPEED_MAX; + Stack.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Stack.brightness = MSI_GPU_BRIGHTNESS_MAX; + Stack.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Stack.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Stack); + + mode Rhythm; + Rhythm.name = "Rhythm"; + Rhythm.value = MSI_GPU_MODE_RHYTHM; + Rhythm.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Rhythm.speed_min = MSI_GPU_SPEED_MIN; + Rhythm.speed = MSI_GPU_SPEED_MID; + Rhythm.speed_max = MSI_GPU_SPEED_MAX; + Rhythm.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Rhythm.brightness = MSI_GPU_BRIGHTNESS_MAX; + Rhythm.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Rhythm.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Rhythm); + + mode Flowing; + Flowing.name = "Flowing"; + Flowing.value = MSI_GPU_MODE_FLOWING; + Flowing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Flowing.speed_min = MSI_GPU_SPEED_MIN; + Flowing.speed = MSI_GPU_SPEED_MID; + Flowing.speed_max = MSI_GPU_SPEED_MAX; + Flowing.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Flowing.brightness = MSI_GPU_BRIGHTNESS_MAX; + Flowing.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Flowing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flowing); + + mode Whirling; + Whirling.name = "Whirling"; + Whirling.value = MSI_GPU_MODE_WHIRLING; + Whirling.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Whirling.speed_min = MSI_GPU_SPEED_MIN; + Whirling.speed = MSI_GPU_SPEED_MID; + Whirling.speed_max = MSI_GPU_SPEED_MAX; + Whirling.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Whirling.brightness = MSI_GPU_BRIGHTNESS_MAX; + Whirling.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Whirling.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Whirling); + + mode Twisting; + Twisting.name = "Twisting"; + Twisting.value = MSI_GPU_MODE_TWISTING; + Twisting.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Twisting.speed_min = MSI_GPU_SPEED_MIN; + Twisting.speed = MSI_GPU_SPEED_MID; + Twisting.speed_max = MSI_GPU_SPEED_MAX; + Twisting.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Twisting.brightness = MSI_GPU_BRIGHTNESS_MAX; + Twisting.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Twisting.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Twisting); + + mode Laminating; + Laminating.name = "Laminating"; + Laminating.value = MSI_GPU_MODE_LAMINATING; + Laminating.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Laminating.speed_min = MSI_GPU_SPEED_MIN; + Laminating.speed = MSI_GPU_SPEED_MID; + Laminating.speed_max = MSI_GPU_SPEED_MAX; + Laminating.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Laminating.brightness = MSI_GPU_BRIGHTNESS_MAX; + Laminating.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Laminating.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Laminating); + + mode Fadein; + Fadein.name = "Fadein"; + Fadein.value = MSI_GPU_MODE_FADEIN; + Fadein.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Fadein.speed_min = MSI_GPU_SPEED_MIN; + Fadein.speed = MSI_GPU_SPEED_MID; + Fadein.speed_max = MSI_GPU_SPEED_MAX; + Fadein.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Fadein.brightness = MSI_GPU_BRIGHTNESS_MAX; + Fadein.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Fadein.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Fadein); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = MSI_GPU_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Breathing.speed_min = MSI_GPU_SPEED_MIN; + Breathing.speed = MSI_GPU_SPEED_MID; + Breathing.speed_max = MSI_GPU_SPEED_MAX; + Breathing.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + Breathing.brightness = MSI_GPU_BRIGHTNESS_MAX; + Breathing.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode flashing; + flashing.name = "Flashing"; + flashing.value = MSI_GPU_MODE_FLASHING; + flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + flashing.speed_min = MSI_GPU_SPEED_MIN; + flashing.speed = MSI_GPU_SPEED_MID; + flashing.speed_max = MSI_GPU_SPEED_MAX; + flashing.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + flashing.brightness = MSI_GPU_BRIGHTNESS_MAX; + flashing.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(flashing); + + mode doubleflashing; + doubleflashing.name = "Doubleflashing"; + doubleflashing.value = MSI_GPU_MODE_DOUBLEFLASHING; + doubleflashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + doubleflashing.speed_min = 0; + doubleflashing.speed = 0; + doubleflashing.speed_max = 2; + doubleflashing.brightness_min = MSI_GPU_BRIGHTNESS_MIN; + doubleflashing.brightness = MSI_GPU_BRIGHTNESS_MAX; + doubleflashing.brightness_max = MSI_GPU_BRIGHTNESS_MAX; + doubleflashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(doubleflashing); + + mode Off; + Off.name = "Off"; + Off.value = MSI_GPU_MODE_OFF; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + + active_mode = GetDeviceMode(); + modes[active_mode].speed = GetModeSpeed(); + modes[active_mode].brightness = controller->MSIGPURegisterRead(MSI_GPU_REG_BRIGHTNESS) / MSI_GPU_BRIGHTNESS_MULTI; +} + +RGBController_MSIGPU::~RGBController_MSIGPU() +{ + delete controller; +} + +void RGBController_MSIGPU::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone msi_gpu_zone; + msi_gpu_zone.name = "GPU"; + msi_gpu_zone.type = ZONE_TYPE_SINGLE; + msi_gpu_zone.leds_min = 1; + msi_gpu_zone.leds_max = 1; + msi_gpu_zone.leds_count = 3; + msi_gpu_zone.matrix_map = NULL; + zones.push_back(msi_gpu_zone); + + /*---------------------------------------------------------*\ + | Set up LED | + \*---------------------------------------------------------*/ + led led1; + led1.name = "Color 1"; + leds.push_back(led1); + led led2; + led2.name = "Color 2"; + leds.push_back(led2); + led led3; + led3.name = "Color 3"; + leds.push_back(led3); + + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize color | + \*---------------------------------------------------------*/ + unsigned char r1 = controller->MSIGPURegisterRead(MSI_GPU_REG_R1); + unsigned char g1 = controller->MSIGPURegisterRead(MSI_GPU_REG_G1); + unsigned char b1 = controller->MSIGPURegisterRead(MSI_GPU_REG_B1); + unsigned char r2 = controller->MSIGPURegisterRead(MSI_GPU_REG_R2); + unsigned char g2 = controller->MSIGPURegisterRead(MSI_GPU_REG_G2); + unsigned char b2 = controller->MSIGPURegisterRead(MSI_GPU_REG_B2); + unsigned char r3 = controller->MSIGPURegisterRead(MSI_GPU_REG_R3); + unsigned char g3 = controller->MSIGPURegisterRead(MSI_GPU_REG_G3); + unsigned char b3 = controller->MSIGPURegisterRead(MSI_GPU_REG_B3); + + colors[0] = ToRGBColor(r1, g1, b1); + colors[1] = ToRGBColor(r2, g2, b2); + colors[2] = ToRGBColor(r3, g3, b3); +} + +void RGBController_MSIGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +bool RGBController_MSIGPU::TimeToSend() +{ + /*-----------------------------------------------------*\ + | Rate limit is 1000(ms) / wait_time in Frames Per Sec | + \*-----------------------------------------------------*/ + const uint8_t wait_time = 33; + return (std::chrono::steady_clock::now() - last_commit_time) > std::chrono::milliseconds(wait_time); +} + +void RGBController_MSIGPU::DeviceUpdateLEDs() +{ + if(TimeToSend()) + { + controller->MSIGPURegisterWrite(MSI_GPU_REG_UNKNOWN, 0x00); + + if(modes[active_mode].value == MSI_GPU_MODE_FADEIN) + { + controller->SetRGB2(RGBGetRValue(colors[1]), RGBGetGValue(colors[1]), RGBGetBValue(colors[1])); + controller->SetRGB3(RGBGetRValue(colors[2]), RGBGetGValue(colors[2]), RGBGetBValue(colors[2])); + } + else + { + controller->SetRGB1(RGBGetRValue(colors[0]), RGBGetGValue(colors[0]), RGBGetBValue(colors[0])); + } + + /*-----------------------------------------------------*\ + | Update last commit time | + \*-----------------------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); + } +} + +void RGBController_MSIGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIGPU::DeviceUpdateMode() +{ + if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + controller->MSIGPURegisterWrite(MSI_GPU_REG_BRIGHTNESS, modes[active_mode].brightness * MSI_GPU_BRIGHTNESS_MULTI); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->MSIGPURegisterWrite(MSI_GPU_REG_SPEED, speed_values[modes[active_mode].speed]); + } + + controller->SetMode(modes[active_mode].value); + } +} + +void RGBController_MSIGPU::DeviceSaveMode() +{ + controller->Save(); +} diff --git a/Controllers/MSIGPUController/MSIGPUController/RGBController_MSIGPU.h b/Controllers/MSIGPUController/MSIGPUController/RGBController_MSIGPU.h new file mode 100644 index 0000000..d789c18 --- /dev/null +++ b/Controllers/MSIGPUController/MSIGPUController/RGBController_MSIGPU.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIGPU.h | +| | +| RGBController for MSI GPU | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "MSIGPUController.h" + +class RGBController_MSIGPU : public RGBController +{ +public: + RGBController_MSIGPU(MSIGPUController* controller_ptr); + ~RGBController_MSIGPU(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + MSIGPUController* controller; + std::chrono::time_point last_commit_time; + + bool TimeToSend(); + int GetDeviceMode(); + int GetModeSpeed(); +}; diff --git a/Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2Controller.cpp b/Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2Controller.cpp new file mode 100644 index 0000000..7a19f09 --- /dev/null +++ b/Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2Controller.cpp @@ -0,0 +1,104 @@ +/*---------------------------------------------------------*\ +| MSIGPUv2Controller.cpp | +| | +| Driver for MSI V2 GPU (ITE9) | +| | +| Wojciech Lazarski 03 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "MSIGPUv2Controller.h" + +using namespace std::chrono_literals; + +MSIGPUv2Controller::MSIGPUv2Controller(i2c_smbus_interface* bus, msi_gpu_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +MSIGPUv2Controller::~MSIGPUv2Controller() +{ + +} + +std::string MSIGPUv2Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string MSIGPUv2Controller::GetDeviceName() +{ + return(name); +} + +void MSIGPUv2Controller::SetRGB1(unsigned char red, unsigned char green, unsigned char blue) +{ + MSIGPURegisterWrite(MSI_GPU_V2_REG_R1, red); + MSIGPURegisterWrite(MSI_GPU_V2_REG_G1, green); + MSIGPURegisterWrite(MSI_GPU_V2_REG_B1, blue); +} + +void MSIGPUv2Controller::SetRGB1V2(unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char buffer[3]; + buffer[2]=red; + buffer[1]=green; + buffer[0]=blue; + MSIGPUBlockWrite(MSI_GPU_V2_REG_COLOR_BLOCK1_BASE, &buffer[0], sizeof(buffer)); +} + +void MSIGPUv2Controller::SetRGB2V2(unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char buffer[3]; + buffer[2]=red; + buffer[1]=green; + buffer[0]=blue; + MSIGPUBlockWrite(MSI_GPU_V2_REG_COLOR_BLOCK2_BASE, &buffer[0], sizeof(buffer)); +} + +void MSIGPUv2Controller::SetRGB3V2(unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char buffer[3]; + buffer[2]=red; + buffer[1]=green; + buffer[0]=blue; + MSIGPUBlockWrite(MSI_GPU_V2_REG_COLOR_BLOCK3_BASE, &buffer[0], sizeof(buffer)); +} + +void MSIGPUv2Controller::SetMode(unsigned char mode) +{ + MSIGPURegisterWrite(MSI_GPU_V2_REG_MODE, mode); +} + +void MSIGPUv2Controller::Save() +{ + MSIGPURegisterWrite(MSI_GPU_V2_REG_SAVE, 0x00); +} + +unsigned char MSIGPUv2Controller::MSIGPURegisterRead(unsigned char reg) +{ + return bus->i2c_smbus_read_byte_data(dev, reg); +} + +void MSIGPUv2Controller::MSIGPURegisterWrite(unsigned char reg, unsigned char val) +{ + bus->i2c_smbus_write_byte_data(dev, reg, val); + std::this_thread::sleep_for(20ms); +} + +void MSIGPUv2Controller::MSIGPUBlockWrite(unsigned char reg, unsigned char *val, unsigned char len) +{ + bus->i2c_smbus_interface::i2c_smbus_write_i2c_block_data(dev, reg, len, val); + std::this_thread::sleep_for(20ms); +} diff --git a/Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2Controller.h b/Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2Controller.h new file mode 100644 index 0000000..fb379fd --- /dev/null +++ b/Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2Controller.h @@ -0,0 +1,100 @@ +/*---------------------------------------------------------*\ +| MSIGPUv2Controller.h | +| | +| Driver for MSI V2 GPU (ITE9) | +| | +| Wojciech Lazarski 03 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char msi_gpu_dev_id; + +#define MSI_GPU_V2_SPEED_MIN 0 +#define MSI_GPU_V2_SPEED_MID 1 +#define MSI_GPU_V2_SPEED_MAX 2 +#define MSI_GPU_V2_BRIGHTNESS_MIN 1 +#define MSI_GPU_V2_BRIGHTNESS_MAX 5 +#define MSI_GPU_V2_BRIGHTNESS_MULTI 20 + +enum +{ + MSI_GPU_V2_REG_BRIGHTNESS = 0x36, /* MSI GPU Brightness Register */ + MSI_GPU_V2_REG_SPEED = 0x38, /* MSI GPU Speed Register */ + MSI_GPU_V2_REG_UNKNOWN = 0x2E, /* MSI GPU Unknown Register */ + MSI_GPU_V2_REG_R1 = 0x30, /* MSI GPU R1 Register */ + MSI_GPU_V2_REG_G1 = 0x31, /* MSI GPU G1 Register */ + MSI_GPU_V2_REG_B1 = 0x32, /* MSI GPU B1 Register */ + MSI_GPU_V2_REG_COLOR_BLOCK1_BASE = 0x27, /* MSI GPU 1 Color block register */ + MSI_GPU_V2_REG_COLOR_BLOCK2_BASE = 0x28, /* MSI GPU 2 Color block register */ + MSI_GPU_V2_REG_COLOR_BLOCK3_BASE = 0x29, /* MSI GPU 3 Color block register */ + MSI_GPU_V2_REG_MODE = 0x22, /* MSI GPU Mode Selection Register */ + MSI_GPU_V2_REG_SAVE = 0x3F, /* MSI GPU Commit Changes Register */ + MSI_GPU_V2_REG_CONTROL = 0x46, /* MSI GPU Direction Register */ +}; + +enum +{ + MSI_GPU_V2_CONTROL_DIRECTION_RIGHT = 0x00, /* Right direction light effects */ + MSI_GPU_V2_CONTROL_DIRECTION_LEFT = 0x02, /* Left direction light effects */ + MSI_GPU_V2_CONTROL_NON_RGBMODE = 0x01, /* Non RGB Mode - programming colors */ +}; + +enum +{ + MSI_GPU_V2_MODE_IDLE = 0x1C, /* Idle mode for programing ? */ + MSI_GPU_V2_MODE_OFF = 0x01, /* OFF mode */ + MSI_GPU_V2_MODE_RAINBOW = 0x08, /* Rainbow effect mode */ + MSI_GPU_V2_MODE_STATIC = 0x13, /* Static color mode */ + MSI_GPU_V2_MODE_RAINDROP = 0x1A, /* Raindrop effect mode */ + MSI_GPU_V2_MODE_MAGIC = 0x07, /* Magic effect mode */ + MSI_GPU_V2_MODE_PATROLLING = 0x05, /* Patrolling effect mode */ + MSI_GPU_V2_MODE_STREAMING = 0x06, /* Streaming effect mode */ + MSI_GPU_V2_MODE_LIGHTNING = 0x15, /* Lightning effect mode */ + MSI_GPU_V2_MODE_WAVE = 0x1F, /* Wave effect mode */ + MSI_GPU_V2_MODE_METEOR = 0x16, /* Meteor effect mode */ + MSI_GPU_V2_MODE_STACK = 0x0D, /* Stack effect mode */ + MSI_GPU_V2_MODE_RHYTHM = 0x0C, /* Rhythm effect mode */ + MSI_GPU_V2_MODE_FLOWING = 0x09, /* Flowing effect mode */ + MSI_GPU_V2_MODE_FLOWING2 = 0x0A, /* Flowing effect mode V2 */ + MSI_GPU_V2_MODE_WHIRLING = 0x0F, /* Whirling effect mode */ + MSI_GPU_V2_MODE_TWISTING = 0x11, /* Twisting effect mode */ + MSI_GPU_V2_MODE_LAMINATING = 0x1D, /* Laminating effect mode */ + MSI_GPU_V2_MODE_FADEIN = 0x14, /* Fadein effect mode */ + MSI_GPU_V2_MODE_BREATHING = 0x04, /* Breathing effect mode */ + MSI_GPU_V2_MODE_FLASHING = 0x02, /* Flashing effect mode */ + MSI_GPU_V2_MODE_DOUBLEFLASHING = 0x03, /* Doubleflashing effect mode */ +}; + +class MSIGPUv2Controller +{ +public: + MSIGPUv2Controller(i2c_smbus_interface* bus, msi_gpu_dev_id dev, std::string dev_name); + ~MSIGPUv2Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetRGB1(unsigned char red, unsigned char green, unsigned char blue); + void SetRGB1V2(unsigned char red, unsigned char green, unsigned char blue); + void SetRGB2V2(unsigned char red, unsigned char green, unsigned char blue); + void SetRGB3V2(unsigned char red, unsigned char green, unsigned char blue); + + void SetMode(unsigned char mode); + void Save(); + + unsigned char MSIGPURegisterRead(unsigned char reg); + void MSIGPURegisterWrite(unsigned char reg, unsigned char val); + void MSIGPUBlockWrite(unsigned char reg,unsigned char *val, unsigned char len); + +private: + i2c_smbus_interface * bus; + msi_gpu_dev_id dev; + std::string name; +}; diff --git a/Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2ControllerDetect.cpp b/Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2ControllerDetect.cpp new file mode 100644 index 0000000..fbe45bb --- /dev/null +++ b/Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2ControllerDetect.cpp @@ -0,0 +1,81 @@ +/*---------------------------------------------------------*\ +| MSIGPUv2ControllerDetect.cpp | +| | +| Detector for MSI V2 GPU (ITE9) | +| | +| Wojciech Lazarski 03 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LogManager.h" +#include "i2c_smbus.h" +#include "RGBController_MSIGPUv2.h" +#include "MSIGPUv2Controller.h" + +/*-----------------------------------------------------------------------------------------*\ +| | +| DetectMSI GPU V2 Controllers | +| | +| Detect MSI GPU v2 controllers on the enumerated I2C busses. | +| | +\*-----------------------------------------------------------------------------------------*/ + +void DetectMSIGPUv2Controllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(bus->pci_vendor == NVIDIA_VEN && bus->port_id != 1) + { + return; + } + + int msi_gpu_id = bus->pci_subsystem_device | bus->pci_device << 16; + MSIGPUv2Controller* controller = new MSIGPUv2Controller(bus, i2c_addr, name); + RGBController_MSIGPUv2* rgb_controller = new RGBController_MSIGPUv2(controller, msi_gpu_id); + + ResourceManager::get()->RegisterRGBController(rgb_controller); +} /* DetectMSIGPUv2Controllers() */ + +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4060 Gaming X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4060_DEV, MSI_SUB_VEN, MSI_RTX4060_GAMING_X_8G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4060 Gaming X NV Edition", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4060_DEV, MSI_SUB_VEN, MSI_RTX4060_GAMING_X_NV_EDITION_8G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4060 Ti Gaming X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4060TI_DEV, MSI_SUB_VEN, MSI_RTX4060TI_GAMING_X_8G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4060 Ti 16GB Gaming X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4060TI_16G_DEV, MSI_SUB_VEN, MSI_RTX4060TI_GAMING_X_16G_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4060 Ti 16GB Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4060TI_16G_DEV, MSI_SUB_VEN, MSI_RTX4060TI_GAMING_X_16G_SLIM_WHITE_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Gaming X Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, MSI_SUB_VEN, MSI_RTX4070_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Gaming X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 SUPER Gaming X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 SUPER Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 SUPER Gaming X Slim MLG", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070S_DEV, MSI_SUB_VEN, MSI_RTX4070S_GAMING_X_SLIM_MLG_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti Gaming X Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, MSI_SUB_VEN, MSI_RTX4070TI_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti Gaming X Trio White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, MSI_SUB_VEN, MSI_RTX4070TI_GAMING_X_TRIO_WHITE_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, MSI_SUB_VEN, MSI_RTX4070TI_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti Suprim X Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, MSI_SUB_VEN, MSI_RTX4070TI_SUPRIM_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti SUPER Gaming X Trio White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, MSI_SUB_VEN, MSI_RTX4070TI_GAMING_X_TRIO_WHITE_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti SUPER Gaming Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, MSI_SUB_VEN, MSI_RTX4070TIS_GAMING_SLIM_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti SUPER Gaming X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, MSI_SUB_VEN, MSI_RTX4070TIS_GAMING_X_SLIM_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4070 Ti SUPER Gaming White X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, MSI_SUB_VEN, MSI_RTX4070TIS_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, MSI_SUB_VEN, MSI_RTX4080S_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 Gaming X Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, MSI_SUB_VEN, MSI_RTX4080_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 Gaming X Trio White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, MSI_SUB_VEN, MSI_RTX4080_GAMING_X_TRIO_WHITE_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 Suprim X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, MSI_SUB_VEN, MSI_RTX4080_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 SUPER Gaming X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, MSI_SUB_VEN, MSI_RTX4080S_GAMING_X_SLIM_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 SUPER Gaming X Slim White", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, MSI_SUB_VEN, MSI_RTX4080S_GAMING_X_SLIM_WHITE_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 SUPER Suprim X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, MSI_SUB_VEN, MSI_RTX4080_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4080 SUPER Gaming X Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, MSI_SUB_VEN, MSI_RTX4080S_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Gaming X Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, MSI_SUB_VEN, MSI_RTX4090_GAMING_X_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Gaming X Slim", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, MSI_SUB_VEN, MSI_RTX4090_GAMING_X_SLIM_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Suprim Liquid X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, MSI_SUB_VEN, MSI_RTX4090_SUPRIM_LIQUID_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 4090 Suprim X", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, MSI_SUB_VEN, MSI_RTX4090_SUPRIM_X_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5070 Gaming Trio", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, MSI_SUB_VEN, MSI_RTX5070_GAMING_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5070 Ti Gaming Trio OC Plus", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, MSI_SUB_VEN, MSI_RTX5070TI_GAMING_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5070 Ti VANGUARD SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, MSI_SUB_VEN, MSI_RTX5070TI_VANGUARD_SOC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 Gaming Trio OC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_GAMING_TRIO_OC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 VANGUARD OC SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_VANGUARD_SOC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 SUPRIM SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_SUPRIM_SOC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5080 SUPRIM LIQUID SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, MSI_SUB_VEN, MSI_RTX5080_SUPRIM_LIQUID_SOC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 Gaming Trio OC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_GAMING_TRIO_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 VANGUARD SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_VANGUARD_SOC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 SUPRIM SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_SUPRIM_SOC_SUB_DEV, 0x68); +REGISTER_I2C_PCI_DETECTOR("MSI GeForce RTX 5090 SUPRIM LIQUID SOC", DetectMSIGPUv2Controllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, MSI_SUB_VEN, MSI_RTX5090_SUPRIM_LIQUID_SOC_SUB_DEV, 0x68); diff --git a/Controllers/MSIGPUController/MSIGPUv2Controller/RGBController_MSIGPUv2.cpp b/Controllers/MSIGPUController/MSIGPUv2Controller/RGBController_MSIGPUv2.cpp new file mode 100644 index 0000000..dd6b567 --- /dev/null +++ b/Controllers/MSIGPUController/MSIGPUv2Controller/RGBController_MSIGPUv2.cpp @@ -0,0 +1,488 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIGPUv2.cpp | +| | +| RGBController for MSI V2 GPU (ITE9) | +| | +| Wojciech Lazarski 03 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_MSIGPUv2.h" + +static const unsigned char speed_values[3] = { 0x04, 0x02, 0x01 }; + +/**------------------------------------------------------------------*\ + @name MSI GPU v2 + @category GPU + @type I2C + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectMSIGPUv2Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSIGPUv2::RGBController_MSIGPUv2(MSIGPUv2Controller * controller_ptr, int msi_gpu_id) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "MSI"; + type = DEVICE_TYPE_GPU; + description = "MSI GPU V2 Device"; + location = controller->GetDeviceLocation(); + + mode Off; + Off.name = "Off"; + Off.value = MSI_GPU_V2_MODE_OFF; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = MSI_GPU_V2_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Direct.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Direct.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Direct.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = MSI_GPU_V2_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Rainbow.speed_min = MSI_GPU_V2_SPEED_MIN; + Rainbow.speed = MSI_GPU_V2_SPEED_MID; + Rainbow.speed_max = MSI_GPU_V2_SPEED_MAX; + Rainbow.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Rainbow.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Rainbow.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Rainbow.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Rainbow); + + mode Magic; + Magic.name = "Magic"; + Magic.value = MSI_GPU_V2_MODE_MAGIC; + Magic.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Magic.speed_min = MSI_GPU_V2_SPEED_MIN; + Magic.speed = MSI_GPU_V2_SPEED_MID; + Magic.speed_max = MSI_GPU_V2_SPEED_MAX; + Magic.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Magic.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Magic.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Magic.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Magic); + + mode ColorCycle; + ColorCycle.name = "Color Cycle"; + ColorCycle.value = MSI_GPU_V2_MODE_MAGIC; + ColorCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE; + ColorCycle.speed_min = MSI_GPU_V2_SPEED_MIN; + ColorCycle.speed = MSI_GPU_V2_SPEED_MID; + ColorCycle.speed_max = MSI_GPU_V2_SPEED_MAX; + ColorCycle.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + ColorCycle.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + ColorCycle.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + ColorCycle.colors_min = 1; + ColorCycle.colors_max = 3; + ColorCycle.colors.resize(3); + ColorCycle.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(ColorCycle); + + + mode Patrolling; + Patrolling.name = "Patrolling"; + Patrolling.value = MSI_GPU_V2_MODE_PATROLLING; + Patrolling.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Patrolling.speed_min = MSI_GPU_V2_SPEED_MIN; + Patrolling.speed = MSI_GPU_V2_SPEED_MID; + Patrolling.speed_max = MSI_GPU_V2_SPEED_MAX; + Patrolling.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Patrolling.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Patrolling.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Patrolling.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Patrolling); + + mode Streaming; + Streaming.name = "Streaming"; + Streaming.value = MSI_GPU_V2_MODE_STREAMING; + Streaming.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Streaming.speed_min = MSI_GPU_V2_SPEED_MIN; + Streaming.speed = MSI_GPU_V2_SPEED_MID; + Streaming.speed_max = MSI_GPU_V2_SPEED_MAX; + Streaming.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Streaming.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Streaming.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Streaming.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Streaming); + + mode Lightning; + Lightning.name = "Lightning"; + Lightning.value = MSI_GPU_V2_MODE_LIGHTNING; + Lightning.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Lightning.speed_min = MSI_GPU_V2_SPEED_MIN; + Lightning.speed = MSI_GPU_V2_SPEED_MID; + Lightning.speed_max = MSI_GPU_V2_SPEED_MAX; + Lightning.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Lightning.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Lightning.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Lightning.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Lightning); + + mode Wave; + Wave.name = "Wave"; + Wave.value = MSI_GPU_V2_MODE_RAINBOW; //Rainbow has two modes now + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_MANUAL_SAVE; + Wave.speed_min = MSI_GPU_V2_SPEED_MIN; + Wave.speed = MSI_GPU_V2_SPEED_MID; + Wave.speed_max = MSI_GPU_V2_SPEED_MAX; + Wave.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Wave.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Wave.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Wave.colors_min = 1; + Wave.colors_max = 3; + Wave.colors.resize(3); + Wave.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Wave); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = MSI_GPU_V2_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Meteor.speed_min = MSI_GPU_V2_SPEED_MIN; + Meteor.speed = MSI_GPU_V2_SPEED_MID; + Meteor.speed_max = MSI_GPU_V2_SPEED_MAX; + Meteor.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Meteor.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Meteor.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Meteor.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Meteor); + + switch(msi_gpu_id) + { + case MSI_RTX4060TI_GAMING_X_16G_SLIM_WHITE_SUB_DEV | NVIDIA_RTX4060TI_16G_DEV << 16: + case MSI_RTX4070S_GAMING_X_SLIM_SUB_DEV | NVIDIA_RTX4070S_DEV << 16: + case MSI_RTX4070TI_GAMING_X_TRIO_WHITE_SUB_DEV | NVIDIA_RTX4070TIS_DEV << 16: + case MSI_RTX4080S_GAMING_X_TRIO_SUB_DEV | NVIDIA_RTX4080S_DEV << 16: + case MSI_RTX4080S_GAMING_X_SLIM_WHITE_SUB_DEV | NVIDIA_RTX4080_DEV << 16: + case MSI_RTX4080S_GAMING_X_SLIM_WHITE_SUB_DEV | NVIDIA_RTX4080S_DEV << 16: + break; + + default: + mode Stack; + Stack.name = "Stack"; + Stack.value = MSI_GPU_V2_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Stack.speed_min = MSI_GPU_V2_SPEED_MIN; + Stack.speed = MSI_GPU_V2_SPEED_MID; + Stack.speed_max = MSI_GPU_V2_SPEED_MAX; + Stack.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Stack.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Stack.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Stack.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Stack); + + mode Rhythm; + Rhythm.name = "Rhythm"; + Rhythm.value = MSI_GPU_V2_MODE_RHYTHM; + Rhythm.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Rhythm.speed_min = MSI_GPU_V2_SPEED_MIN; + Rhythm.speed = MSI_GPU_V2_SPEED_MID; + Rhythm.speed_max = MSI_GPU_V2_SPEED_MAX; + Rhythm.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Rhythm.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Rhythm.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Rhythm.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Rhythm); + } + + mode Flowing; + Flowing.name = "Flowing"; + Flowing.value = MSI_GPU_V2_MODE_FLOWING; + Flowing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Flowing.speed_min = MSI_GPU_V2_SPEED_MIN; + Flowing.speed = MSI_GPU_V2_SPEED_MID; + Flowing.speed_max = MSI_GPU_V2_SPEED_MAX; + Flowing.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Flowing.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Flowing.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Flowing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flowing); + + mode Whirling; + Whirling.name = "Whirling"; + Whirling.value = MSI_GPU_V2_MODE_WHIRLING; + Whirling.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Whirling.speed_min = MSI_GPU_V2_SPEED_MIN; + Whirling.speed = MSI_GPU_V2_SPEED_MID; + Whirling.speed_max = MSI_GPU_V2_SPEED_MAX; + Whirling.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Whirling.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Whirling.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Whirling.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Whirling); + + mode Fadein; + Fadein.name = "Fade In"; + Fadein.value = MSI_GPU_V2_MODE_FADEIN; + Fadein.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE; + Fadein.speed_min = MSI_GPU_V2_SPEED_MIN; + Fadein.speed = MSI_GPU_V2_SPEED_MID; + Fadein.speed_max = MSI_GPU_V2_SPEED_MAX; + Fadein.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Fadein.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Fadein.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Fadein.colors_min = 1; + Fadein.colors_max = 2; + Fadein.colors.resize(2); + Fadein.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Fadein); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = MSI_GPU_V2_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE; + Breathing.speed_min = MSI_GPU_V2_SPEED_MIN; + Breathing.speed = MSI_GPU_V2_SPEED_MID; + Breathing.speed_max = MSI_GPU_V2_SPEED_MAX; + Breathing.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + Breathing.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + Breathing.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(2); + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Breathing); + + mode flashing; + flashing.name = "Flashing"; + flashing.value = MSI_GPU_V2_MODE_FLASHING; + flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + flashing.speed_min = MSI_GPU_V2_SPEED_MIN; + flashing.speed = MSI_GPU_V2_SPEED_MID; + flashing.speed_max = MSI_GPU_V2_SPEED_MAX; + flashing.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + flashing.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + flashing.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(flashing); + + mode doubleflashing; + doubleflashing.name = "Double Flashing"; + doubleflashing.value = MSI_GPU_V2_MODE_DOUBLEFLASHING; + doubleflashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + doubleflashing.speed_min = MSI_GPU_V2_SPEED_MIN; + doubleflashing.speed = MSI_GPU_V2_SPEED_MID; + doubleflashing.speed_max = MSI_GPU_V2_SPEED_MAX; + doubleflashing.brightness_min = MSI_GPU_V2_BRIGHTNESS_MIN; + doubleflashing.brightness = MSI_GPU_V2_BRIGHTNESS_MAX; + doubleflashing.brightness_max = MSI_GPU_V2_BRIGHTNESS_MAX; + doubleflashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(doubleflashing); + + SetupZones(); + + modes[active_mode].speed = MSI_GPU_V2_SPEED_MID; + modes[active_mode].brightness = MSI_GPU_V2_BRIGHTNESS_MAX; +} + +RGBController_MSIGPUv2::~RGBController_MSIGPUv2() +{ + delete controller; +} + +void RGBController_MSIGPUv2::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone msi_gpu_zone; + msi_gpu_zone.name = "GPU"; + msi_gpu_zone.type = ZONE_TYPE_SINGLE; + msi_gpu_zone.leds_min = 1; + msi_gpu_zone.leds_max = 1; + msi_gpu_zone.leds_count = 1; + msi_gpu_zone.matrix_map = NULL; + zones.push_back(msi_gpu_zone); + + /*---------------------------------------------------------*\ + | Set up LED | + \*---------------------------------------------------------*/ + led led1; + led1.name = "Color"; + leds.push_back(led1); + + SetupColors(); + + /*-------------------------------------------------------------*\ + | Initialize colors | + | This controller doesn't support reading colors from device | + \*-------------------------------------------------------------*/ + colors[0] = ToRGBColor(0xFF, 0, 0); + + for(unsigned int mode_idx = 0; mode_idx < modes.size(); mode_idx++) + { + if(modes[mode_idx].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + if(modes[mode_idx].colors.size()>2) + { + modes[mode_idx].colors[2] = ToRGBColor(0, 0, 0xFF); + } + if(modes[mode_idx].colors.size()>1) + { + modes[mode_idx].colors[1] = ToRGBColor(0, 0xFF, 0); + } + modes[mode_idx].colors[0] = ToRGBColor(0xFF, 0, 0); + } + } + +} + +void RGBController_MSIGPUv2::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_MSIGPUv2::DeviceUpdateAll(const mode& current_mode) +{ + switch(current_mode.value) + { + case MSI_GPU_V2_MODE_RAINBOW: + if(current_mode.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + if(current_mode.direction == MODE_DIRECTION_LEFT) + { + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_CONTROL, MSI_GPU_V2_CONTROL_DIRECTION_LEFT | MSI_GPU_V2_CONTROL_NON_RGBMODE); + } + else + { + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_CONTROL, MSI_GPU_V2_CONTROL_DIRECTION_RIGHT | MSI_GPU_V2_CONTROL_NON_RGBMODE); + } + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_UNKNOWN, 0x00); + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_MODE, MSI_GPU_V2_MODE_IDLE); + + controller->SetRGB1V2(RGBGetRValue(current_mode.colors[0]), RGBGetGValue(current_mode.colors[0]), RGBGetBValue(current_mode.colors[0])); + controller->SetRGB2V2(RGBGetRValue(current_mode.colors[1]), RGBGetGValue(current_mode.colors[1]), RGBGetBValue(current_mode.colors[1])); + controller->SetRGB3V2(RGBGetRValue(current_mode.colors[2]), RGBGetGValue(current_mode.colors[2]), RGBGetBValue(current_mode.colors[2])); + } + else + { + if(current_mode.direction == MODE_DIRECTION_LEFT) + { + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_CONTROL, MSI_GPU_V2_CONTROL_DIRECTION_LEFT); + } + else + { + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_CONTROL, MSI_GPU_V2_CONTROL_DIRECTION_RIGHT); + } + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_UNKNOWN, 0x00); + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_MODE, MSI_GPU_V2_MODE_IDLE); + } + break; + + case MSI_GPU_V2_MODE_MAGIC: + if(current_mode.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_UNKNOWN, 0x00); + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_MODE, MSI_GPU_V2_MODE_IDLE); + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_CONTROL, MSI_GPU_V2_CONTROL_NON_RGBMODE); + + controller->SetRGB1V2(RGBGetRValue(current_mode.colors[0]), RGBGetGValue(current_mode.colors[0]), RGBGetBValue(current_mode.colors[0])); + controller->SetRGB2V2(RGBGetRValue(current_mode.colors[1]), RGBGetGValue(current_mode.colors[1]), RGBGetBValue(current_mode.colors[1])); + controller->SetRGB3V2(RGBGetRValue(current_mode.colors[2]), RGBGetGValue(current_mode.colors[2]), RGBGetBValue(current_mode.colors[2])); + } + else + { + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_UNKNOWN, 0x00); + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_MODE, MSI_GPU_V2_MODE_IDLE); + + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_CONTROL, MSI_GPU_V2_CONTROL_DIRECTION_RIGHT); + } + break; + + + case MSI_GPU_V2_MODE_BREATHING: + case MSI_GPU_V2_MODE_FADEIN: + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_UNKNOWN, 0x00); + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_MODE, MSI_GPU_V2_MODE_IDLE); + + controller->SetRGB1V2(RGBGetRValue(current_mode.colors[0]), RGBGetGValue(current_mode.colors[0]), RGBGetBValue(current_mode.colors[0])); + controller->SetRGB2V2(RGBGetRValue(current_mode.colors[1]), RGBGetGValue(current_mode.colors[1]), RGBGetBValue(current_mode.colors[1])); + break; + + case MSI_GPU_V2_MODE_FLOWING: + case MSI_GPU_V2_MODE_WHIRLING: + case MSI_GPU_V2_MODE_PATROLLING: + case MSI_GPU_V2_MODE_FLASHING: + case MSI_GPU_V2_MODE_DOUBLEFLASHING: + case MSI_GPU_V2_MODE_STATIC: + case MSI_GPU_V2_MODE_RHYTHM: + case MSI_GPU_V2_MODE_STACK: + case MSI_GPU_V2_MODE_METEOR: + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_UNKNOWN, 0x00); + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_MODE, MSI_GPU_V2_MODE_IDLE); + controller->SetRGB1(RGBGetRValue(colors[0]), RGBGetGValue(colors[0]), RGBGetBValue(colors[0])); + break; + + case MSI_GPU_V2_MODE_STREAMING: + case MSI_GPU_V2_MODE_LIGHTNING: + case MSI_GPU_V2_MODE_OFF: + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_UNKNOWN, 0x00); + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_MODE, MSI_GPU_V2_MODE_IDLE); + break; + + default: + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_UNKNOWN, 0x00); + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_MODE, MSI_GPU_V2_MODE_IDLE); + controller->SetMode(MSI_GPU_V2_MODE_OFF); + } + + + if(current_mode.flags & MODE_FLAG_HAS_BRIGHTNESS) + { + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_BRIGHTNESS, MSI_GPU_V2_BRIGHTNESS_MULTI * modes[active_mode].brightness); + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->MSIGPURegisterWrite(MSI_GPU_V2_REG_SPEED, speed_values[current_mode.speed]); + } + + } + + controller->SetMode(current_mode.value); +} + + +void RGBController_MSIGPUv2::DeviceUpdateLEDs() +{ + DeviceUpdateAll(modes[active_mode]); +} + +void RGBController_MSIGPUv2::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateAll(modes[active_mode]); +} + + +void RGBController_MSIGPUv2::UpdateSingleLED(int /*led*/) +{ + /*---------------------------------------------------------*\ + | This device does not support updating single LEDs | + \*---------------------------------------------------------*/ +} + +void RGBController_MSIGPUv2::DeviceUpdateMode() +{ + DeviceUpdateAll(modes[active_mode]); +} + +void RGBController_MSIGPUv2::DeviceSaveMode() +{ + controller->Save(); +} diff --git a/Controllers/MSIGPUController/MSIGPUv2Controller/RGBController_MSIGPUv2.h b/Controllers/MSIGPUController/MSIGPUv2Controller/RGBController_MSIGPUv2.h new file mode 100644 index 0000000..f313d94 --- /dev/null +++ b/Controllers/MSIGPUController/MSIGPUv2Controller/RGBController_MSIGPUv2.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIGPUv2.h | +| | +| RGBController for MSI V2 GPU (ITE9) | +| | +| Wojciech Lazarski 03 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "pci_ids.h" +#include "RGBController.h" +#include "MSIGPUv2Controller.h" + +class RGBController_MSIGPUv2 : public RGBController +{ +public: + RGBController_MSIGPUv2(MSIGPUv2Controller* controller_ptr, int msi_gpu_id); + ~RGBController_MSIGPUv2(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + MSIGPUv2Controller* controller; + + void DeviceUpdateAll(const mode& current_mode); +}; diff --git a/Controllers/MSIKeyboardController/MSIKeyboardControllerDetect.cpp b/Controllers/MSIKeyboardController/MSIKeyboardControllerDetect.cpp new file mode 100644 index 0000000..a0f5054 --- /dev/null +++ b/Controllers/MSIKeyboardController/MSIKeyboardControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| MSIKeyboardControllerDetect.cpp | +| | +| Detector for MSI Mystic Light MS-1565 Keyboard | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "MSIMysticLightKBController.h" +#include "RGBController_MSIMysticLightKB.h" + +#define MSI_USB_VID 0x1462 + +/*----------------------------------------------------------*\ +| | +| DetectMSIKeyboardController | +| | +| Detect MSI Mystic Light MS-1565 keyboard | +| | +\*----------------------------------------------------------*/ + +void DetectMSIKeyboardController + ( + hid_device_info* info, + const std::string& /*name*/ + ) +{ + hid_device* dev = hid_open_path(info->path); + if(dev != nullptr) + { + MSIKeyboardController* controller = new MSIKeyboardController(dev, info->path); + RGBController_MSIKeyboard* rgb_controller = new RGBController_MSIKeyboard(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("MSI Keyboard MS_1565", DetectMSIKeyboardController, MSI_USB_VID, 0x1601, 0x00FF, 0x01); \ No newline at end of file diff --git a/Controllers/MSIKeyboardController/MSIMysticLightKBController/MSIMysticLightKBController.cpp b/Controllers/MSIKeyboardController/MSIMysticLightKBController/MSIMysticLightKBController.cpp new file mode 100644 index 0000000..61e7e2c --- /dev/null +++ b/Controllers/MSIKeyboardController/MSIMysticLightKBController/MSIMysticLightKBController.cpp @@ -0,0 +1,128 @@ +/*---------------------------------------------------------*\ +| MSIMysticLightKBController.cpp | +| | +| Driver for MSI Mystic Light MS-1565 keyboard leds | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "MSIMysticLightKBController.h" + +#include "StringUtils.h" +#include "hidapi.h" +#include + +std::map zone_map = +{ + { MS_1565_ZONE_1, 1 }, + { MS_1565_ZONE_2, 2 }, + { MS_1565_ZONE_3, 4 }, + { MS_1565_ZONE_4, 8 }, + { MS_1565_ZONE_DEVICE, 15} +}; + +MSIKeyboardController::MSIKeyboardController +( + hid_device *handle, + const char *path +) +{ + dev = handle; + if(dev) + { + location = path; + } +} + +MSIKeyboardController::~MSIKeyboardController() +{ + hid_close(dev); +} + +void MSIKeyboardController::SetMode +( + MS_1565_MODE mode, + MS_1565_SPEED speed1, + MS_1565_SPEED speed2, + MS_1565_WAVE_DIRECTION wave_dir, + MS_1565_ZONE zone, + ColorKeyFrame color_keyframes[] +) +{ + unsigned char buf[64] = {}; + buf[0] = 0x02; + buf[1] = 0x01; + buf[2] = zone_map[zone]; + hid_send_feature_report(dev, buf, sizeof(buf)); + + FeaturePacket_MS1565 data; + data.mode = (unsigned char)(mode); + for(int i = 0; i < MAX_MS_1565_KEYFRAMES; i++) + { + data.color_keyframes[i] = color_keyframes[i]; + } + data.speed2 = speed2; + data.speed1 = speed1; + + data.wave_dir = (unsigned char)(wave_dir); + /*-----------------------------------------------------*\ + | Send packet to hardware, return true if successful | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, (unsigned char *)&data, sizeof(data)); + return; +} + +std::string MSIKeyboardController::GetDeviceName() +{ + wchar_t tname[256]; + + /*-----------------------------------------------------*\ + | Get the manufacturer string from HID | + \*-----------------------------------------------------*/ + hid_get_manufacturer_string(dev, tname, 256); + + /*-----------------------------------------------------*\ + | Convert to std::string | + \*-----------------------------------------------------*/ + std::string name = StringUtils::wstring_to_string(tname); + + /*-----------------------------------------------------*\ + | Get the product string from HID | + \*-----------------------------------------------------*/ + hid_get_product_string(dev, tname, 256); + + /*-----------------------------------------------------*\ + | Append the product string to the manufacturer string | + \*-----------------------------------------------------*/ + name.append(" ").append(StringUtils::wstring_to_string(tname)); + + return(name); +} + +std::string MSIKeyboardController::GetFWVersion() +{ + /*-----------------------------------------------------*\ + | This device doesn't support firmware version | + \*-----------------------------------------------------*/ + std::string firmware_version = ""; + return firmware_version; +} + +std::string MSIKeyboardController::GetDeviceLocation() +{ + return ("HID: " + location); +} + +std::string MSIKeyboardController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} diff --git a/Controllers/MSIKeyboardController/MSIMysticLightKBController/MSIMysticLightKBController.h b/Controllers/MSIKeyboardController/MSIMysticLightKBController/MSIMysticLightKBController.h new file mode 100644 index 0000000..04677aa --- /dev/null +++ b/Controllers/MSIKeyboardController/MSIMysticLightKBController/MSIMysticLightKBController.h @@ -0,0 +1,111 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight1565Controller.h | +| | +| Driver for MSI Mystic Light MS-1565 keyboard leds | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#ifndef MSIMYSTICLIGHTKBCONTROLLER_H +#define MSIMYSTICLIGHTKBCONTROLLER_H + +#include +#include +#include +#include + +struct Color +{ + unsigned char R; + unsigned char G; + unsigned char B; +}; + +struct ColorKeyFrame +{ + unsigned char time_frame = 0x00; + Color color; +}; + +#define MAX_MS_1565_KEYFRAMES 10 + +typedef unsigned char MS_1565_SPEED; + +// 64 bytes long feature +struct FeaturePacket_MS1565 +{ + unsigned char report_id = 0x02; // Report ID + unsigned char packet_id = 0x02; + unsigned char mode = 0x00; + unsigned char speed2 = 0x00; // Seconds X 100 = duration of animation cycle + unsigned char speed1 = 0x00; // In little endian + // 1 second => 100 = 0x0064, speed2 = 0x64, speed1 = 0x00 + const unsigned char unused = 0x00; + const unsigned char unused2 = 0x00; + const unsigned char unused3 = 0x0F; + const unsigned char unused4 = 0x01; + unsigned char wave_dir = 0x00; + ColorKeyFrame color_keyframes[MAX_MS_1565_KEYFRAMES] = {}; + const unsigned char padding[14] = {}; //pad to make the packet size 64 bytes +}; + +enum MS_1565_MODE +{ + MS_1565_OFF = 0, + MS_1565_STEADY = 1, + MS_1565_BREATHING = 2, + MS_1565_CYCLE = 3, + MS_1565_WAVE = 4, +}; + +enum MS_1565_WAVE_DIRECTION +{ + MS_1565_WAVE_DIRECTION_RIGHT_TO_LEFT = 0, + MS_1565_WAVE_DIRECTION_LEFT_TO_RIGHT = 1 +}; + +enum MS_1565_ZONE +{ + MS_1565_ZONE_1 = 1, + MS_1565_ZONE_2, + MS_1565_ZONE_3, + MS_1565_ZONE_4, + MS_1565_ZONE_DEVICE +}; + +class MSIKeyboardController +{ +public: + MSIKeyboardController + ( + hid_device* handle, + const char* path + ); + ~MSIKeyboardController(); + + void SetMode + ( + MS_1565_MODE mode, + MS_1565_SPEED speed1, + MS_1565_SPEED speed2, + MS_1565_WAVE_DIRECTION wave_dir, + MS_1565_ZONE zone, + ColorKeyFrame color_keyframes[] + ); + + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + std::string GetFWVersion(); + std::string GetSerial(); + + std::vector mode_zones; + +private: + hid_device* dev; + std::string location; +}; + + +#endif // MSIMYSTICLIGHTKBCONTROLLER_H diff --git a/Controllers/MSIKeyboardController/MSIMysticLightKBController/RGBController_MSIMysticLightKB.cpp b/Controllers/MSIKeyboardController/MSIMysticLightKBController/RGBController_MSIMysticLightKB.cpp new file mode 100644 index 0000000..594abe5 --- /dev/null +++ b/Controllers/MSIKeyboardController/MSIMysticLightKBController/RGBController_MSIMysticLightKB.cpp @@ -0,0 +1,204 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLightKB.cpp | +| | +| Driver for MSI Mystic Light MS-1565 keyboard leds | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MSIMysticLightKB.h" +#include "MSIMysticLightKBController.h" +#include "RGBController.h" + +#include + +/**------------------------------------------------------------------*\ + @name MSI MS-1565 Mystic Light Keyboard (64 Byte) + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectMSIKeyboardController + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSIKeyboard::RGBController_MSIKeyboard +( + MSIKeyboardController *controller_ptr +) +{ + controller = controller_ptr; + name = controller->GetDeviceName(); + vendor = "MSI"; + type = DEVICE_TYPE_KEYBOARD; + description = "MSI Mystic Light MS-1565"; + version = controller->GetFWVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + SetupModes(); + SetupColors(); +} + +RGBController_MSIKeyboard::~RGBController_MSIKeyboard() +{ + delete controller; +} + +void RGBController_MSIKeyboard::ResizeZone +( + int /*zone*/, + int /*new_size*/ +) +{ +} + +void RGBController_MSIKeyboard::SetupZones() +{ +} + +void RGBController_MSIKeyboard::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIKeyboard::DeviceUpdateLEDs() +{ + mode &Mode = modes[active_mode]; + MS_1565_MODE msi_mode = (MS_1565_MODE)Mode.value; + MS_1565_ZONE zone = controller->mode_zones[active_mode]; + + /*----------------------------------*\ + | speed is cycle duration in 1/100s | + | Mode.speed = 0 % => speed = 12.00s | + | Mode.speed = 50 % => speed = 7.50s | + | Mode.speed = 100 % => speed = 3.00 | + \*--------------------------------- */ + + unsigned int speed = 1200 - 9 * Mode.speed; + unsigned char speed2 = (unsigned char)(speed & 0xFF); + unsigned char speed1 = (unsigned char)((speed & 0xFF00) >> 8); + + MS_1565_WAVE_DIRECTION wave_direction = (MS_1565_WAVE_DIRECTION)(Mode.direction); + + const size_t colors_size = Mode.colors.size(); + + ColorKeyFrame ck[MAX_MS_1565_KEYFRAMES] = {}; + + for(size_t idx = 0; idx < colors_size; idx++) + { + ck[idx].time_frame = (unsigned char)(idx * 100 / colors_size); + ck[idx].color.R = RGBGetRValue(Mode.colors[idx]) * Mode.brightness / 100; + ck[idx].color.G = RGBGetGValue(Mode.colors[idx]) * Mode.brightness / 100; + ck[idx].color.B = RGBGetBValue(Mode.colors[idx]) * Mode.brightness / 100; + } + + ck[colors_size].time_frame = 100; + ck[colors_size].color.R = RGBGetRValue(Mode.colors[0]) * Mode.brightness / 100; + ck[colors_size].color.G = RGBGetGValue(Mode.colors[0]) * Mode.brightness / 100; + ck[colors_size].color.B = RGBGetBValue(Mode.colors[0]) * Mode.brightness / 100; + + controller->SetMode(msi_mode, speed1, speed2, wave_direction, zone, ck); +} + +void RGBController_MSIKeyboard::UpdateZoneLEDs(int /*zone*/) +{ +} + +void RGBController_MSIKeyboard::UpdateSingleLED(int /*led*/) +{ +} + +void RGBController_MSIKeyboard::SetupModes() +{ + SetupZonesMode("Off", MS_1565_MODE::MS_1565_OFF, 0); + SetupZonesMode("Static", MS_1565_MODE::MS_1565_STEADY, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_BRIGHTNESS); + SetupZonesMode("Breathing", MS_1565_MODE::MS_1565_BREATHING, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_BRIGHTNESS); + SetupZonesMode("Color Cycle", MS_1565_MODE::MS_1565_CYCLE, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_BRIGHTNESS); + SetupZonesMode("Wave", MS_1565_MODE::MS_1565_WAVE, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_DIRECTION_LR); +} + +void RGBController_MSIKeyboard::SetupMode +( + const std::string name, + MS_1565_MODE mod, + unsigned int flags, + MS_1565_ZONE zone +) +{ + mode Mode; + Mode.name = name; + Mode.value = mod; + Mode.flags = flags; + + if(Mode.value == MS_1565_MODE::MS_1565_OFF) + { + Mode.color_mode= MODE_COLORS_NONE; + Mode.colors_min = 0; + Mode.colors_max = 0; + Mode.colors.resize(1); + modes.push_back(Mode); + controller->mode_zones.push_back(zone); + return; + } + + if(flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + Mode.color_mode= MODE_COLORS_MODE_SPECIFIC; + Mode.colors_min = 1; + if(Mode.value == MS_1565_MODE::MS_1565_STEADY) + { + Mode.colors_max = 1; + } + else + { + Mode.colors_max = MAX_MS_1565_KEYFRAMES - 1; + } + Mode.colors.resize(Mode.colors_max); + } + + if(flags & MODE_FLAG_HAS_SPEED) + { + Mode.speed_min = 0; + Mode.speed_max = 100; + Mode.speed = 50; + } + + if(flags & MODE_FLAG_HAS_BRIGHTNESS) + { + Mode.brightness_min = 0; + Mode.brightness_max = 100; + } + Mode.brightness = 100; + + if(flags & MODE_FLAG_HAS_DIRECTION_LR) + { + Mode.direction = 0; + } + + modes.push_back(Mode); + controller->mode_zones.push_back(zone); +} + +void RGBController_MSIKeyboard::SetupZonesMode +( + const std::string name, + MS_1565_MODE mod, + unsigned int flags +) +{ + SetupMode(name, mod, flags, MS_1565_ZONE_DEVICE); + for(int idx = 0; idx < 4; idx++) + { + SetupMode(name + " zone " + std::to_string(idx + 1), mod, flags, MS_1565_ZONE(idx + 1)); + } +} diff --git a/Controllers/MSIKeyboardController/MSIMysticLightKBController/RGBController_MSIMysticLightKB.h b/Controllers/MSIKeyboardController/MSIMysticLightKBController/RGBController_MSIMysticLightKB.h new file mode 100644 index 0000000..64fd88e --- /dev/null +++ b/Controllers/MSIKeyboardController/MSIMysticLightKBController/RGBController_MSIMysticLightKB.h @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight1565.h | +| | +| Driver for MSI Mystic Light MS-1565 keyboard leds | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#ifndef RGBCONTROLLER_MSIMYSTICLIGHTKB_H +#define RGBCONTROLLER_MSIMYSTICLIGHTKB_H + +#include "RGBController.h" +#include "MSIMysticLightKBController.h" + +class RGBController_MSIKeyboard : public RGBController +{ +public: + RGBController_MSIKeyboard(MSIKeyboardController* controller_ptr); + ~RGBController_MSIKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + MSIKeyboardController* controller; + + void SetupModes(); + void SetupMode + ( + const std::string name, + MS_1565_MODE mode, + unsigned int flags, + MS_1565_ZONE zone + ); + void SetupZonesMode + ( + const std::string name, + MS_1565_MODE mod, + unsigned int flags + ); +}; + +#endif // RGBCONTROLLER_MSIMYSTICLIGHTKB_H diff --git a/Controllers/MSILaptopController/MSILaptopController.cpp b/Controllers/MSILaptopController/MSILaptopController.cpp new file mode 100644 index 0000000..29caa04 --- /dev/null +++ b/Controllers/MSILaptopController/MSILaptopController.cpp @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| MSILaptopController.cpp | +| | +| Driver for MSI laptop SteelSeries KLC/ALC RGB devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "MSILaptopController.h" +#include "StringUtils.h" + +#define MSI_LAPTOP_REPORT_ID 0x00 +#define MSI_LAPTOP_COMMAND 0x0C +#define MSI_LAPTOP_KLC_PACKET_ID 0x66 +#define MSI_LAPTOP_ALC_PACKET_ID 0x06 +#define MSI_LAPTOP_PACKET_SIZE 525 +#define MSI_LAPTOP_PAYLOAD_OFFSET 5 + +MSILaptopController::MSILaptopController(hid_device* dev_handle, const char* path, std::string dev_name, msi_laptop_device device_type) +{ + dev = dev_handle; + location = path; + name = dev_name; + type = device_type; +} + +MSILaptopController::~MSILaptopController() +{ + hid_close(dev); +} + +std::string MSILaptopController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MSILaptopController::GetDeviceName() +{ + return(name); +} + +std::string MSILaptopController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +msi_laptop_device MSILaptopController::GetDeviceType() +{ + return(type); +} + +void MSILaptopController::SetLEDs(std::vector leds, std::vector colors) +{ + unsigned char buf[MSI_LAPTOP_PACKET_SIZE]; + unsigned int led_count = (leds.size() < colors.size()) ? leds.size() : colors.size(); + + memset(buf, 0x00, sizeof(buf)); + + buf[0x00] = MSI_LAPTOP_REPORT_ID; + buf[0x01] = MSI_LAPTOP_COMMAND; + buf[0x03] = (type == MSI_LAPTOP_KLC) ? MSI_LAPTOP_KLC_PACKET_ID : MSI_LAPTOP_ALC_PACKET_ID; + + // Fill unused LED IDs with 0xFF so they are ignored by the controller + for(int i = 0; i < (MSI_LAPTOP_PACKET_SIZE - MSI_LAPTOP_PAYLOAD_OFFSET) / 4; i++) + { + buf[MSI_LAPTOP_PAYLOAD_OFFSET + (i * 4)] = 0xFF; + } + + for(unsigned int led_idx = 0; led_idx < led_count; led_idx++) + { + unsigned int offset = MSI_LAPTOP_PAYLOAD_OFFSET + (led_idx * 4); + + if((offset + 3) >= sizeof(buf)) + { + break; + } + + buf[offset + 0] = leds[led_idx].value; + buf[offset + 1] = RGBGetRValue(colors[led_idx]); + buf[offset + 2] = RGBGetGValue(colors[led_idx]); + buf[offset + 3] = RGBGetBValue(colors[led_idx]); + } + + hid_send_feature_report(dev, buf, sizeof(buf)); +} diff --git a/Controllers/MSILaptopController/MSILaptopController.h b/Controllers/MSILaptopController/MSILaptopController.h new file mode 100644 index 0000000..59b9955 --- /dev/null +++ b/Controllers/MSILaptopController/MSILaptopController.h @@ -0,0 +1,67 @@ +/*---------------------------------------------------------*\ +| MSILaptopController.h | +| | +| Driver for MSI laptop SteelSeries KLC/ALC RGB devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +typedef enum +{ + MSI_LAPTOP_KLC, + MSI_LAPTOP_ALC, + +} msi_laptop_device; + +typedef struct +{ + const char* name; + unsigned char id; + +} msi_laptop_led; + +struct MSILaptopModel +{ + const char* sys_vendor; + const char* product_name; + + /* Keyboard layout */ + const msi_laptop_led* klc_leds; + unsigned int klc_leds_count; + unsigned int klc_matrix_height; + unsigned int klc_matrix_width; + const unsigned int* klc_matrix_map; + + /* Lightbar layout */ + const msi_laptop_led* alc_leds; + unsigned int alc_leds_count; + unsigned int alc_lightbar_leds; +}; + +class MSILaptopController +{ +public: + MSILaptopController(hid_device* dev_handle, const char* path, std::string dev_name, msi_laptop_device device_type); + ~MSILaptopController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + msi_laptop_device GetDeviceType(); + + void SetLEDs(std::vector leds, std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; + msi_laptop_device type; +}; diff --git a/Controllers/MSILaptopController/MSILaptopControllerDetect.cpp b/Controllers/MSILaptopController/MSILaptopControllerDetect.cpp new file mode 100644 index 0000000..b76bbbe --- /dev/null +++ b/Controllers/MSILaptopController/MSILaptopControllerDetect.cpp @@ -0,0 +1,235 @@ +/*---------------------------------------------------------*\ +| MSILaptopControllerDetect.cpp | +| | +| Detector for MSI laptop SteelSeries RGB devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBControllerKeyNames.h" +#include "RGBController_MSILaptop.h" +#include "MSILaptopController.h" +#include "dmiinfo.h" + +#define MSI_LAPTOP_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define STEELSERIES_VID 0x1038 +#define STEELSERIES_MSI_RAIDER_A18_KLC_PID 0x1122 +#define STEELSERIES_MSI_RAIDER_A18_ALC_PID 0x1161 + +#define NA 0xFFFFFFFF +#define MSI_LAPTOP_KLC_MATRIX_HEIGHT 6 +#define MSI_LAPTOP_KLC_MATRIX_WIDTH 23 +#define MSI_LAPTOP_ALC_LIGHTBAR_LEDS 3 + +static const msi_laptop_led msi_raider_a18_klc_leds[] = +{ + { KEY_EN_A, 0x04 }, + { KEY_EN_B, 0x05 }, + { KEY_EN_C, 0x06 }, + { KEY_EN_D, 0x07 }, + { KEY_EN_E, 0x08 }, + { KEY_EN_F, 0x09 }, + { KEY_EN_G, 0x0A }, + { KEY_EN_H, 0x0B }, + { KEY_EN_I, 0x0C }, + { KEY_EN_J, 0x0D }, + { KEY_EN_K, 0x0E }, + { KEY_EN_L, 0x0F }, + { KEY_EN_M, 0x10 }, + { KEY_EN_N, 0x11 }, + { KEY_EN_O, 0x12 }, + { KEY_EN_P, 0x13 }, + { KEY_EN_Q, 0x14 }, + { KEY_EN_R, 0x15 }, + { KEY_EN_S, 0x16 }, + { KEY_EN_T, 0x17 }, + { KEY_EN_U, 0x18 }, + { KEY_EN_V, 0x19 }, + { KEY_EN_W, 0x1A }, + { KEY_EN_X, 0x1B }, + { KEY_EN_Y, 0x1C }, + { KEY_EN_Z, 0x1D }, + { KEY_EN_1, 0x1E }, + { KEY_EN_2, 0x1F }, + { KEY_EN_3, 0x20 }, + { KEY_EN_4, 0x21 }, + { KEY_EN_5, 0x22 }, + { KEY_EN_6, 0x23 }, + { KEY_EN_7, 0x24 }, + { KEY_EN_8, 0x25 }, + { KEY_EN_9, 0x26 }, + { KEY_EN_0, 0x27 }, + { KEY_EN_ESCAPE, 0x29 }, + { KEY_EN_TAB, 0x2B }, + { KEY_EN_SPACE, 0x2C }, + { KEY_EN_MINUS, 0x2D }, + { KEY_EN_EQUALS, 0x2E }, + { KEY_EN_LEFT_BRACKET, 0x2F }, + { KEY_EN_RIGHT_BRACKET, 0x30 }, + { KEY_EN_SEMICOLON, 0x33 }, + { KEY_EN_QUOTE, 0x34 }, + { KEY_EN_BACK_TICK, 0x35 }, + { KEY_EN_COMMA, 0x36 }, + { KEY_EN_PERIOD, 0x37 }, + { KEY_EN_FORWARD_SLASH, 0x38 }, + { KEY_EN_CAPS_LOCK, 0x39 }, + { KEY_EN_F1, 0x3A }, + { KEY_EN_F2, 0x3B }, + { KEY_EN_F3, 0x3C }, + { KEY_EN_F4, 0x3D }, + { KEY_EN_F5, 0x3E }, + { KEY_EN_F6, 0x3F }, + { KEY_EN_F7, 0x40 }, + { KEY_EN_F8, 0x41 }, + { KEY_EN_F9, 0x42 }, + { KEY_EN_F10, 0x43 }, + { KEY_EN_F11, 0x44 }, + { KEY_EN_F12, 0x45 }, + { KEY_EN_PRINT_SCREEN, 0x46 }, + { KEY_EN_SCROLL_LOCK, 0x47 }, + { KEY_EN_INSERT, 0x49 }, + { "Home/Page Up", 0x4B }, + { KEY_EN_DELETE, 0x4C }, + { KEY_EN_PAGE_DOWN, 0x4E }, + { KEY_EN_RIGHT_ARROW, 0x4F }, + { KEY_EN_LEFT_ARROW, 0x50 }, + { KEY_EN_DOWN_ARROW, 0x51 }, + { KEY_EN_UP_ARROW, 0x52 }, + { KEY_EN_NUMPAD_LOCK, 0x53 }, + { KEY_EN_NUMPAD_DIVIDE, 0x54 }, + { KEY_EN_NUMPAD_TIMES, 0x55 }, + { KEY_EN_NUMPAD_MINUS, 0x56 }, + { KEY_EN_NUMPAD_PLUS, 0x57 }, + { KEY_EN_NUMPAD_ENTER, 0x58 }, + { KEY_EN_NUMPAD_1, 0x59 }, + { KEY_EN_NUMPAD_2, 0x5A }, + { KEY_EN_NUMPAD_3, 0x5B }, + { KEY_EN_NUMPAD_4, 0x5C }, + { KEY_EN_NUMPAD_5, 0x5D }, + { KEY_EN_NUMPAD_6, 0x5E }, + { KEY_EN_NUMPAD_7, 0x5F }, + { KEY_EN_NUMPAD_8, 0x60 }, + { KEY_EN_NUMPAD_9, 0x61 }, + { KEY_EN_NUMPAD_0, 0x62 }, + { KEY_EN_NUMPAD_PERIOD, 0x63 }, + { KEY_EN_POWER, 0x66 }, + { KEY_EN_LEFT_CONTROL, 0xE0 }, + { KEY_EN_LEFT_SHIFT, 0xE1 }, + { KEY_EN_LEFT_ALT, 0xE2 }, + { KEY_EN_LEFT_WINDOWS, 0xE3 }, + { KEY_EN_RIGHT_WINDOWS, 0xE4 }, + { KEY_EN_RIGHT_FUNCTION, 0xF0 }, + { KEY_EN_ANSI_ENTER, 0x28 }, + { KEY_EN_BACKSPACE, 0x2A }, + { KEY_EN_BACK_SLASH, 0x31 }, + { KEY_EN_ISO_BACK_SLASH, 0x64 }, + { KEY_EN_RIGHT_SHIFT, 0xE5 }, + { KEY_EN_RIGHT_ALT, 0xE6 }, +}; + +static unsigned int msi_raider_a18_klc_matrix_map[MSI_LAPTOP_KLC_MATRIX_HEIGHT][MSI_LAPTOP_KLC_MATRIX_WIDTH] = +{ + { 36, NA, 50, 51, 52, 53, NA, 54, 55, 56, 57, NA, 58, 59, 60, 61, 62, 63, NA, NA, NA, NA, 89 }, + { 45, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 39, 40, 97, NA, 64, 65, NA, 72, 73, 74, 75, NA }, + { 37, 16, 22, 4, 17, 19, 24, 20, 8, 14, 15, 41, 42, 98, NA, 66, 67, NA, 84, 85, 86, 76, NA }, + { 49, 0, 18, 3, 5, 6, 7, 9, 10, 11, 43, 44, 96, NA, NA, NA, NA, NA, 81, 82, 83, NA, NA }, + { 91, 25, 23, 2, 21, 1, 13, 12, 46, 47, 48,100, NA, NA, NA, NA, 71, NA, 78, 79, 80, 77, NA }, + { 90, 93, 92, NA, NA, NA, 38, NA, NA, NA,101, 94, 95, NA, 69, 70, 68, NA, 87, NA, 88, NA, NA }, +}; + +/*---------------------------------------------------------*\ +| Note on Raider A18 HX ALC (Lightbar) LED mappings: | +| Initial reverse engineering packet captures showed 6 zones| +| being updated (indexes 0x00 to 0x05). | +| However, indexes 0x04 (L4) and 0x05 (L5) are "dummy" zones| +| on this specific model and do not correspond to physical | +| LEDs. To avoid user confusion, they are omitted from the | +| UI profile here. | +| The SetLEDs function handles zero-padding the unused | +| payload slots with 0xFF so that omitting these zones | +| doesn't accidentally overwrite the 0x00 index (L1). | +\*---------------------------------------------------------*/ +static const msi_laptop_led msi_raider_a18_alc_leds[] = +{ + { "Lightbar 1", 0x00 }, + { "Lightbar 2", 0x01 }, + { "Lightbar 3", 0x02 }, + { "Logo", 0x03 }, +}; + +static const MSILaptopModel msi_laptop_models[] = +{ + { + "Micro-Star International Co., Ltd.", + "Raider A18 HX A9WJG", + + /* Keyboard layout */ + msi_raider_a18_klc_leds, + MSI_LAPTOP_ARRAY_SIZE(msi_raider_a18_klc_leds), + MSI_LAPTOP_KLC_MATRIX_HEIGHT, + MSI_LAPTOP_KLC_MATRIX_WIDTH, + (const unsigned int*)msi_raider_a18_klc_matrix_map, + + /* Lightbar layout */ + msi_raider_a18_alc_leds, + MSI_LAPTOP_ARRAY_SIZE(msi_raider_a18_alc_leds), + MSI_LAPTOP_ALC_LIGHTBAR_LEDS, + }, +}; + +static const MSILaptopModel* GetMSILaptopModelDMI() +{ + DMIInfo dmi; + + for(unsigned int i = 0; i < MSI_LAPTOP_ARRAY_SIZE(msi_laptop_models); i++) + { + if((dmi.getManufacturer() == msi_laptop_models[i].sys_vendor) && + (dmi.getProductName() == msi_laptop_models[i].product_name)) + { + return &msi_laptop_models[i]; + } + } + + return nullptr; +} + +void DetectMSILaptop(hid_device_info* info, const std::string& name) +{ + const MSILaptopModel* model = GetMSILaptopModelDMI(); + if(!model) + { + return; + } + + msi_laptop_device device_type; + + if(info->product_id == STEELSERIES_MSI_RAIDER_A18_KLC_PID) + { + device_type = MSI_LAPTOP_KLC; + } + else if(info->product_id == STEELSERIES_MSI_RAIDER_A18_ALC_PID) + { + device_type = MSI_LAPTOP_ALC; + } + else + { + return; + } + + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + MSILaptopController* controller = new MSILaptopController(dev, info->path, name, device_type); + RGBController_MSILaptop* rgb_controller = new RGBController_MSILaptop(controller, model); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("MSI Laptop Keyboard", DetectMSILaptop, STEELSERIES_VID, STEELSERIES_MSI_RAIDER_A18_KLC_PID); +REGISTER_HID_DETECTOR_I("MSI Laptop Lightbar", DetectMSILaptop, STEELSERIES_VID, STEELSERIES_MSI_RAIDER_A18_ALC_PID, 0); + diff --git a/Controllers/MSILaptopController/RGBController_MSILaptop.cpp b/Controllers/MSILaptopController/RGBController_MSILaptop.cpp new file mode 100644 index 0000000..d2f9cfd --- /dev/null +++ b/Controllers/MSILaptopController/RGBController_MSILaptop.cpp @@ -0,0 +1,141 @@ +/*---------------------------------------------------------*\ +| RGBController_MSILaptop.cpp | +| | +| RGBController for MSI laptop SteelSeries RGB devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_MSILaptop.h" + +#define NA 0xFFFFFFFF +/**------------------------------------------------------------------*\ + @name MSI Laptop SteelSeries RGB + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectMSILaptop + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSILaptop::RGBController_MSILaptop(MSILaptopController* controller_ptr, const MSILaptopModel* model_ptr) +{ + controller = controller_ptr; + model = model_ptr; + + name = controller->GetDeviceName(); + vendor = "SteelSeries"; + description = std::string(model->sys_vendor) + " " + std::string(model->product_name) + " RGB Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + type = (controller->GetDeviceType() == MSI_LAPTOP_KLC) ? DEVICE_TYPE_KEYBOARD : DEVICE_TYPE_LEDSTRIP; + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_MSILaptop::~RGBController_MSILaptop() +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].matrix_map != NULL) + { + delete zones[zone_idx].matrix_map; + } + } + + delete controller; +} + +void RGBController_MSILaptop::SetupZones() +{ + if(controller->GetDeviceType() == MSI_LAPTOP_KLC) + { + zone keyboard_zone; + + keyboard_zone.name = ZONE_EN_KEYBOARD; + keyboard_zone.type = ZONE_TYPE_MATRIX; + keyboard_zone.leds_min = model->klc_leds_count; + keyboard_zone.leds_max = model->klc_leds_count; + keyboard_zone.leds_count = model->klc_leds_count; + keyboard_zone.matrix_map = new matrix_map_type; + keyboard_zone.matrix_map->height = model->klc_matrix_height; + keyboard_zone.matrix_map->width = model->klc_matrix_width; + keyboard_zone.matrix_map->map = (unsigned int *)model->klc_matrix_map; + + zones.push_back(keyboard_zone); + + for(unsigned int led_idx = 0; led_idx < model->klc_leds_count; led_idx++) + { + led new_led; + new_led.name = model->klc_leds[led_idx].name; + new_led.value = model->klc_leds[led_idx].id; + leds.push_back(new_led); + } + } + else + { + zone lightbar_zone; + lightbar_zone.name = "Lightbar"; + lightbar_zone.type = ZONE_TYPE_LINEAR; + lightbar_zone.start_idx = 0; + lightbar_zone.leds_min = model->alc_lightbar_leds; + lightbar_zone.leds_max = model->alc_lightbar_leds; + lightbar_zone.leds_count = model->alc_lightbar_leds; + lightbar_zone.matrix_map = NULL; + zones.push_back(lightbar_zone); + + zone logo_zone; + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.start_idx = model->alc_lightbar_leds; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + for(unsigned int led_idx = 0; led_idx < model->alc_leds_count; led_idx++) + { + led new_led; + new_led.name = model->alc_leds[led_idx].name; + new_led.value = model->alc_leds[led_idx].id; + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_MSILaptop::ResizeZone(int /*zone*/, int /*new_size*/) +{ +} + +void RGBController_MSILaptop::DeviceUpdateLEDs() +{ + controller->SetLEDs(leds, colors); +} + +void RGBController_MSILaptop::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSILaptop::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSILaptop::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/MSILaptopController/RGBController_MSILaptop.h b/Controllers/MSILaptopController/RGBController_MSILaptop.h new file mode 100644 index 0000000..a75c6f4 --- /dev/null +++ b/Controllers/MSILaptopController/RGBController_MSILaptop.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_MSILaptop.h | +| | +| RGBController for MSI laptop SteelSeries RGB devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "MSILaptopController.h" + +class RGBController_MSILaptop : public RGBController +{ +public: + RGBController_MSILaptop(MSILaptopController* controller_ptr, const MSILaptopModel* model_ptr); + ~RGBController_MSILaptop(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + MSILaptopController* controller; + const MSILaptopModel* model; +}; diff --git a/Controllers/MSIMonitorController/MSIMonitorController.cpp b/Controllers/MSIMonitorController/MSIMonitorController.cpp new file mode 100644 index 0000000..8219367 --- /dev/null +++ b/Controllers/MSIMonitorController/MSIMonitorController.cpp @@ -0,0 +1,121 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMonitor.cpp | +| | +| RGBController for MSI monitor (gaming controller) | +| | +| Andy Herbert 2026 June 4 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "MSIMonitorController.h" +#include "StringUtils.h" + +MSIMonitorController::MSIMonitorController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +MSIMonitorController::~MSIMonitorController() +{ + hid_close(dev); +} + +std::string MSIMonitorController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MSIMonitorController::GetNameString() +{ + return(name); +} + +std::string MSIMonitorController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void MSIMonitorController::Set(uint8_t mode_value, const std::vector colors, uint8_t last_bit) +{ + /*---------------------------------------------------------*\ + | Prepare color data | + \*---------------------------------------------------------*/ + uint8_t data[MSI_MONITOR_PACKET_SIZE]; + memset(data, 0x00, MSI_MONITOR_PACKET_SIZE); + + unsigned int offset = 0; + + data[offset++] = 0x71; + data[offset++] = 0x01; + for(int i = 0; i < 3; i++) + { + data[offset++] = 0x00; + } + + data[offset++] = 0x01; + data[offset++] = 0x64; + for(int i = 0; i < 5; i++) + { + data[offset++] = 0x00; + } + + /*---------------------------------------------------------*\ + | put mode_value | + \*---------------------------------------------------------*/ + data[offset++] = mode_value; + + for(int i = 0; i < 3; i++) + { + data[offset++] = 0x00; + } + data[offset++] = 0x01; + data[offset++] = 0x64; + for(int i = 0; i < 5; i++) + { + data[offset++] = 0x00; + } + + /*-----------------------------------------------------------------------*\ + | this data looks like placeholder for additional LEDs in other monitors | + \*-----------------------------------------------------------------------*/ + for(int i = 0; i < 9; i++) + { + data[offset++] = 0xff; + data[offset++] = 0x00; + data[offset++] = 0x00; + } + + //RGB values begin + for(const RGBColor color: colors) + { + data[offset++] = RGBGetRValue(color); + data[offset++] = RGBGetGValue(color); + data[offset++] = RGBGetBValue(color); + } + + /*-------------------------------------------------------------------------------*\ + | last bit is probably a write to device bit- 0x01 saves to device, 0x00 doesn't. | + | For direct mode, bit is set to 0x00, otherwise lights will flicker | + \*-------------------------------------------------------------------------------*/ + + data[offset++] = last_bit; + + /*---------------------------------------------------------*\ + | Send the data (1 packet) | + \*---------------------------------------------------------*/ + + hid_send_feature_report(dev, data, MSI_MONITOR_PACKET_SIZE); +} diff --git a/Controllers/MSIMonitorController/MSIMonitorController.h b/Controllers/MSIMonitorController/MSIMonitorController.h new file mode 100644 index 0000000..1f53d8a --- /dev/null +++ b/Controllers/MSIMonitorController/MSIMonitorController.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMonitor.cpp | +| | +| RGBController for MSI monitor (gaming controller) | +| | +| Andy Herbert 2026 June 1 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define MSI_MONITOR_LEDS 9 +#define MSI_MONITOR_PACKET_SIZE 78 + +enum +{ + MSI_MONITOR_OFF_MODE_VALUE = 0x00, + MSI_MONITOR_STATIC_MODE_VALUE = 0x01, + MSI_MONITOR_BREATHING_MODE_VALUE = 0x02, + MSI_MONITOR_FLASHING_MODE_VALUE = 0x03, + MSI_MONITOR_LIGHTNING_MODE_VALUE = 0x05, + MSI_MONITOR_MARQUEE_MODE_VALUE = 0x06, + MSI_MONITOR_METEOR_MODE_VALUE = 0x08, + MSI_MONITOR_RAINBOW_MODE_VALUE = 0x1A, + MSI_MONITOR_RANDOM_MODE_VALUE = 0x1F +}; + +class MSIMonitorController +{ +public: + MSIMonitorController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name); + ~MSIMonitorController(); + + std::string GetDeviceLocation(); + std::string GetFirmwareVersion(); + std::string GetNameString(); + std::string GetSerialString(); + + void Set(uint8_t mode_value, const std::vector colors, uint8_t last_bit); + +private: + hid_device *dev; + std::string description; + std::string location; + std::string name; + std::string version; +}; diff --git a/Controllers/MSIMonitorController/MSIMonitorControllerDetect.cpp b/Controllers/MSIMonitorController/MSIMonitorControllerDetect.cpp new file mode 100644 index 0000000..f804a36 --- /dev/null +++ b/Controllers/MSIMonitorController/MSIMonitorControllerDetect.cpp @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| MSIKeyboardControllerDetect.cpp | +| | +| Detector for MSI monitor (MSI Gaming Controller) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "MSIMonitorController.h" +#include "RGBController_MSIMonitor.h" + +#define MSI_USB_VID 0x1462 +#define MSI_USB_PID 0x3FA4 + +/*----------------------------------------------------------*\ +| | +| DetectMSIMonitorController | +| | +| Detect MSI monitor and maybe others | +| | +\*----------------------------------------------------------*/ + +static void DetectMSIMonitorController(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + MSIMonitorController* controller = new MSIMonitorController(dev, *info, name); + RGBController_MSIMonitor* rgb_controller = new RGBController_MSIMonitor(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("MSI Monitor (Gaming Controller)", DetectMSIMonitorController, MSI_USB_VID, MSI_USB_PID, 0, 0x01, 0); diff --git a/Controllers/MSIMonitorController/RGBController_MSIMonitor.cpp b/Controllers/MSIMonitorController/RGBController_MSIMonitor.cpp new file mode 100644 index 0000000..3215a65 --- /dev/null +++ b/Controllers/MSIMonitorController/RGBController_MSIMonitor.cpp @@ -0,0 +1,171 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMonitor.cpp | +| | +| RGBController for MSI monitor (gaming controller) | +| | +| Andy Herbert 2026 June 1 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_MSIMonitor.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name MSIMonitor + @category Accessory + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectMSIMonitorController + @comment Developed with MSI MAG272CQR +\*-------------------------------------------------------------------*/ +RGBController_MSIMonitor::RGBController_MSIMonitor(MSIMonitorController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "MSI"; + type = DEVICE_TYPE_MONITOR; + description = "MSI Monitor (Gaming Controller)"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = MSI_MONITOR_STATIC_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = MSI_MONITOR_STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = MSI_MONITOR_BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = MSI_MONITOR_FLASHING_MODE_VALUE; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flashing); + + mode Lightning; + Lightning.name = "Lightning"; + Lightning.value = MSI_MONITOR_LIGHTNING_MODE_VALUE; + Lightning.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Lightning.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Lightning); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = MSI_MONITOR_MARQUEE_MODE_VALUE; + Marquee.flags = MODE_FLAG_AUTOMATIC_SAVE; + Marquee.color_mode = MODE_COLORS_NONE; + modes.push_back(Marquee); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = MSI_MONITOR_METEOR_MODE_VALUE; + Meteor.flags = MODE_FLAG_AUTOMATIC_SAVE; + Meteor.color_mode = MODE_COLORS_NONE; + modes.push_back(Meteor); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = MSI_MONITOR_RAINBOW_MODE_VALUE; + Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Random; + Random.name = "Random"; + Random.value = MSI_MONITOR_RANDOM_MODE_VALUE; + Random.flags = MODE_FLAG_AUTOMATIC_SAVE; + Random.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Random); + + mode Off; + Off.name = "Off"; + Off.value = MSI_MONITOR_OFF_MODE_VALUE; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_MSIMonitor::~RGBController_MSIMonitor() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_MSIMonitor::SetupZones() +{ + zone new_zone; + + new_zone.name = "Rear"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 9; + new_zone.leds_max = 9; + new_zone.leds_count = 9; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + for(unsigned int i = 0 ; i < 9; i ++) + { + led new_led; + new_led.name = "LED " + std::to_string(i + 1); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_MSIMonitor::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_MSIMonitor::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + controller->Set(modes[active_mode].value, colors, active_mode == 0 ? 0x00 : 0x01); +} + +void RGBController_MSIMonitor::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIMonitor::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIMonitor::DeviceUpdateMode() +{ + controller->Set(modes[active_mode].value, colors, 0x01); +} diff --git a/Controllers/MSIMonitorController/RGBController_MSIMonitor.h b/Controllers/MSIMonitorController/RGBController_MSIMonitor.h new file mode 100644 index 0000000..1380e0d --- /dev/null +++ b/Controllers/MSIMonitorController/RGBController_MSIMonitor.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMonitor.cpp | +| | +| RGBController for MSI monitor (gaming controller) | +| | +| Andy Herbert 2026 May 16 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "MSIMonitorController.h" + +class RGBController_MSIMonitor : public RGBController +{ +public: + RGBController_MSIMonitor(MSIMonitorController* controller_ptr); + ~RGBController_MSIMonitor(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + +private: + MSIMonitorController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; + + void KeepaliveThread(); +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLight112Controller/MSIMysticLight112Controller.cpp b/Controllers/MSIMysticLightController/MSIMysticLight112Controller/MSIMysticLight112Controller.cpp new file mode 100644 index 0000000..6cf2c4e --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight112Controller/MSIMysticLight112Controller.cpp @@ -0,0 +1,436 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight112Controller.cpp | +| | +| Driver for MSI Mystic Light 112-byte motherboard | +| | +| thombo 17 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "MSIMysticLight112Controller.h" +#include "StringUtils.h" + +#define BITSET(val, bit, pos) ((unsigned char)std::bitset<8>(val).set((pos), (bit)).to_ulong()) + +struct Config +{ + unsigned short pid; // PID of the board + size_t numof_onboard_leds; // number of onboard leds + const std::vector* supported_zones; // pointer to vector of supported zones +}; + +const std::vector zones_set = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_CORSAIR, + MSI_ZONE_ON_BOARD_LED_0 +}; + +MSIMysticLight112Controller::MSIMysticLight112Controller + ( + hid_device* handle, + const char* path, + std::string dev_name + ) +{ + dev = handle; + location = path; + name = dev_name; + + if(dev) + { + ReadFwVersion(); + ReadSettings(); + } + + /*-----------------------------------------*\ + | Initialize save flag | + \*-----------------------------------------*/ + data.save_data = 0; + data.on_board_led.colorFlags = 0x81; // force MS bit of color flags to 1 to have expected zone control + + /*-----------------------------------------*\ + | Initialize zone based per LED data | + \*-----------------------------------------*/ + numof_onboard_leds = 7; + supported_zones = &zones_set; + + zone_based_per_led_data.j_rgb_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.j_rgb_1.colorFlags = BITSET(zone_based_per_led_data.j_rgb_1.colorFlags, true, 7u); + zone_based_per_led_data.j_rainbow_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.j_rainbow_1.colorFlags = BITSET(zone_based_per_led_data.j_rainbow_1.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led.colorFlags = BITSET(zone_based_per_led_data.on_board_led.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_1.colorFlags = BITSET(zone_based_per_led_data.on_board_led_1.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_2.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_2.colorFlags = BITSET(zone_based_per_led_data.on_board_led_2.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_3.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_3.colorFlags = BITSET(zone_based_per_led_data.on_board_led_3.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_4.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_4.colorFlags = BITSET(zone_based_per_led_data.on_board_led_4.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_5.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_5.colorFlags = BITSET(zone_based_per_led_data.on_board_led_5.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_6.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2 << 2; + zone_based_per_led_data.on_board_led_6.colorFlags = BITSET(zone_based_per_led_data.on_board_led_6.colorFlags, true, 7u); + zone_based_per_led_data.save_data = 0; + + direct_mode = false; +} + +MSIMysticLight112Controller::~MSIMysticLight112Controller() +{ + hid_close(dev); +} + +void MSIMysticLight112Controller::SetMode + ( + MSI_ZONE zone, + MSI_MODE mode, + MSI_SPEED speed, + MSI_BRIGHTNESS brightness, + bool rainbow_color + ) +{ + ZoneData* zone_data = GetZoneData(data, zone); + + if(zone_data == nullptr) + { + return; + } + + if (zone <= MSI_ZONE_ON_BOARD_LED_0) + { + zone_data->effect = mode; + zone_data->speedAndBrightnessFlags = (brightness << 2) | (speed & 0x03); + zone_data->colorFlags = BITSET(zone_data->colorFlags, !rainbow_color, 7u); + zone_data->padding = 0x00; + + if(mode > MSI_MODE_DOUBLE_FLASHING) + { + zone_data->speedAndBrightnessFlags |= SYNC_SETTING_JRGB; + zone_data->colorFlags |= SYNC_SETTING_ONBOARD; + } + else + { + zone_data->speedAndBrightnessFlags &= ~SYNC_SETTING_JRGB; + zone_data->colorFlags &= ~SYNC_SETTING_ONBOARD; + } + } + + if((zone >= MSI_ZONE_ON_BOARD_LED_0) && (mode <= MSI_MODE_DOUBLE_FLASHING)) + { + zone_data = GetZoneData(data, (MSI_ZONE)((int)zone + 1)); + + if(zone_data != nullptr) + { + zone_data->effect = mode; + zone_data->speedAndBrightnessFlags = (brightness << 2) | (speed & 0x03); + zone_data->colorFlags = BITSET(zone_data->colorFlags, !rainbow_color, 7u); + zone_data->padding = 0x00; + } + } +} + +std::string MSIMysticLight112Controller::GetDeviceName() +{ + return name; +} + +std::string MSIMysticLight112Controller::GetFWVersion() +{ + std::string firmware_version; + firmware_version = "APROM: " + version_APROM + ", LDROM: " + version_LDROM; + return firmware_version; +} + +std::string MSIMysticLight112Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MSIMysticLight112Controller::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +bool MSIMysticLight112Controller::ReadSettings() +{ + /*-----------------------------------------------------*\ + | Read packet from hardware, return true if successful | + \*-----------------------------------------------------*/ + return(hid_get_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof data); +} + +bool MSIMysticLight112Controller::Update + ( + bool save + ) +{ + /*-----------------------------------------------------*\ + | Send packet to hardware, return true if successful | + \*-----------------------------------------------------*/ + if(direct_mode) + { + return (hid_send_feature_report(dev, (unsigned char*)&zone_based_per_led_data, sizeof(zone_based_per_led_data)) == sizeof(zone_based_per_led_data)); + } + else + { + data.save_data = save; + return (hid_send_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof(data)); + } +} + +void MSIMysticLight112Controller::SetZoneColor + ( + MSI_ZONE zone, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2 + ) +{ + ZoneData* zone_data = GetZoneData(data, zone); + + if(zone_data == nullptr) + { + return; + } + + if (zone <= MSI_ZONE_ON_BOARD_LED_0) + { + zone_data->color.R = red1; + zone_data->color.G = grn1; + zone_data->color.B = blu1; + zone_data->color2.R = red2; + zone_data->color2.G = grn2; + zone_data->color2.B = blu2; + } + + if(zone >= MSI_ZONE_ON_BOARD_LED_0) + { + zone_data = GetZoneData(data, (MSI_ZONE)((int)zone + 1)); + + if(zone_data != nullptr) + { + zone_data->color.R = red1; + zone_data->color.G = grn1; + zone_data->color.B = blu1; + zone_data->color2.R = red2; + zone_data->color2.G = grn2; + zone_data->color2.B = blu2; + } + } +} + +void MSIMysticLight112Controller::SetLedColor + ( + MSI_ZONE zone, + unsigned char red, + unsigned char grn, + unsigned char blu + ) +{ + if(zone >= MSI_ZONE_ON_BOARD_LED_0) + { + zone = (MSI_ZONE)((int)zone + 1); + } + + ZoneData *zone_data = GetZoneData(zone_based_per_led_data, zone); + + if(zone_data == nullptr) + { + return; + } + + zone_data->color.R = red; + zone_data->color.G = grn; + zone_data->color.B = blu; + zone_data->color2.R = red; + zone_data->color2.G = grn; + zone_data->color2.B = blu; +} + +ZoneData *MSIMysticLight112Controller::GetZoneData + ( + FeaturePacket_112& data_packet, + MSI_ZONE zone + ) +{ + switch(zone) + { + case MSI_ZONE_J_RGB_1: + return &data_packet.j_rgb_1; + case MSI_ZONE_J_RAINBOW_1: + return &data_packet.j_rainbow_1; + case MSI_ZONE_ON_BOARD_LED_0: + return &data_packet.on_board_led; + case MSI_ZONE_ON_BOARD_LED_1: + return &data_packet.on_board_led_1; + case MSI_ZONE_ON_BOARD_LED_2: + return &data_packet.on_board_led_2; + case MSI_ZONE_ON_BOARD_LED_3: + return &data_packet.on_board_led_3; + case MSI_ZONE_ON_BOARD_LED_4: + return &data_packet.on_board_led_4; + case MSI_ZONE_ON_BOARD_LED_5: + return &data_packet.on_board_led_5; + case MSI_ZONE_ON_BOARD_LED_6: + return &data_packet.on_board_led_6; + case MSI_ZONE_J_CORSAIR: + return &data_packet.j_corsair_1; + default: + break; + } + + return nullptr; +} + +bool MSIMysticLight112Controller::ReadFwVersion() +{ + unsigned char request[64]; + unsigned char response[64]; + int ret_val = 64; + + /*-----------------------------------------------------*\ + | First read the APROM | + | Checksum also available at report ID 180, with MSB | + | stored at index 0x08 and LSB at 0x09 | + \*-----------------------------------------------------*/ + + /*-----------------------------------------------------*\ + | Zero out buffers | + \*-----------------------------------------------------*/ + memset(request, 0x00, sizeof(request)); + memset(response, 0x00, sizeof(response)); + + /*-----------------------------------------------------*\ + | Set up APROM Firmware Version Request packet | + \*-----------------------------------------------------*/ + request[0x00] = 0x01; + request[0x01] = 0xB0; + + /*-----------------------------------------------------*\ + | Fill request from 0x02 to 0x61 with 0xCC | + \*-----------------------------------------------------*/ + memset(&request[0x02], 0xCC, sizeof(request) - 2); + + /*-----------------------------------------------------*\ + | Send request and receive response packets | + \*-----------------------------------------------------*/ + ret_val &= hid_write(dev, request, 64); + ret_val &= hid_read(dev, response, 64); + + /*-----------------------------------------------------*\ + | Extract high and low values from response | + \*-----------------------------------------------------*/ + unsigned char highValue = response[2] >> 4; + unsigned char lowValue = response[2] & 0x0F; + + /*-----------------------------------------------------*\ + | Build firmware string . | + \*-----------------------------------------------------*/ + version_APROM = std::to_string((int)highValue).append(".").append(std::to_string((int)lowValue)); + + /*-----------------------------------------------------*\ + | First read the LDROM | + | Checksum also available at report ID 184, with MSB | + | stored at index 0x08 and LSB at 0x09 | + \*-----------------------------------------------------*/ + + /*-----------------------------------------------------*\ + | Set up LDROM Firmware Version Request packet | + \*-----------------------------------------------------*/ + request[0x00] = 0x01; + request[0x01] = 0xB6; + + /*-----------------------------------------------------*\ + | Send request and receive response packets | + \*-----------------------------------------------------*/ + ret_val &= hid_write(dev, request, 64); + ret_val &= hid_read(dev, response, 64); + + /*-----------------------------------------------------*\ + | Extract high and low values from response | + \*-----------------------------------------------------*/ + highValue = response[2] >> 4; + lowValue = response[2] & 0x0F; + + /*-----------------------------------------------------*\ + | Build firmware string . | + \*-----------------------------------------------------*/ + version_LDROM = std::to_string((int)highValue).append(".").append(std::to_string((int)lowValue)); + + /*-----------------------------------------------------*\ + | If return value is zero it means an HID transfer | + | failed | + \*-----------------------------------------------------*/ + return(ret_val > 0); +} + +MSI_MODE MSIMysticLight112Controller::GetMode() +{ + return (MSI_MODE)data.on_board_led.effect; +} + +void MSIMysticLight112Controller::GetMode + ( + MSI_ZONE zone, + MSI_MODE &mode, + MSI_SPEED &speed, + MSI_BRIGHTNESS &brightness, + bool &rainbow_color, + unsigned int &color + ) +{ + /*-----------------------------------------------------*\ + | Get data for given zone | + \*-----------------------------------------------------*/ + ZoneData *zone_data = GetZoneData(data, zone); + + /*-----------------------------------------------------*\ + | Return if zone is invalid | + \*-----------------------------------------------------*/ + if(zone_data == nullptr) + { + return; + } + + /*-----------------------------------------------------*\ + | Update pointers with data | + \*-----------------------------------------------------*/ + mode = (MSI_MODE)zone_data->effect; + speed = (MSI_SPEED)(zone_data->speedAndBrightnessFlags & 0x03); + brightness = (MSI_BRIGHTNESS)((zone_data->speedAndBrightnessFlags >> 2) & 0x1F); + rainbow_color = (zone_data->colorFlags & 0x80) == 0 ? true : false; + color = ToRGBColor(zone_data->color.R, zone_data->color.G, zone_data->color.B); +} + +void MSIMysticLight112Controller::SetDirectMode + ( + bool mode + ) +{ + direct_mode = mode; +} + +size_t MSIMysticLight112Controller::GetMaxOnboardLeds() +{ + return numof_onboard_leds; +} diff --git a/Controllers/MSIMysticLightController/MSIMysticLight112Controller/MSIMysticLight112Controller.h b/Controllers/MSIMysticLightController/MSIMysticLight112Controller/MSIMysticLight112Controller.h new file mode 100644 index 0000000..01b2ebf --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight112Controller/MSIMysticLight112Controller.h @@ -0,0 +1,112 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight112Controller.h | +| | +| Driver for MSI Mystic Light 112-byte motherboard | +| | +| thombo 17 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "MSIMysticLightCommon.h" +#include "RGBController.h" + +class MSIMysticLight112Controller +{ +public: + MSIMysticLight112Controller + ( + hid_device* handle, + const char* path, + std::string dev_name + ); + + ~MSIMysticLight112Controller(); + + void SetMode + ( + MSI_ZONE zone, + MSI_MODE mode, + MSI_SPEED speed, + MSI_BRIGHTNESS brightness, + bool rainbow_color + ); + + MSI_MODE GetMode(); + + void GetMode + ( + MSI_ZONE zone, + MSI_MODE &mode, + MSI_SPEED &speed, + MSI_BRIGHTNESS &brightness, + bool &rainbow_color, + unsigned int &color + ); + + void SetZoneColor + ( + MSI_ZONE zone, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2 + ); + + void SetLedColor + ( + MSI_ZONE zone, + unsigned char red, + unsigned char grn, + unsigned char blu + ); + + bool Update + ( + bool save + ); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + std::string GetFWVersion(); + std::string GetSerial(); + + void SetDirectMode + ( + bool mode + ); + + bool IsDirectModeActive() { return direct_mode; } + size_t GetMaxOnboardLeds(); + const std::vector* + GetSupportedZones() { return supported_zones; } + +private: + hid_device* dev; + std::string name; + std::string location; + std::string version_APROM; + std::string version_LDROM; + + FeaturePacket_112 data; + FeaturePacket_112 zone_based_per_led_data; + bool direct_mode; + size_t numof_onboard_leds; + const std::vector* supported_zones; + + bool ReadSettings(); + bool ReadFwVersion(); + ZoneData* GetZoneData + ( + FeaturePacket_112& dataPacket, + MSI_ZONE zone + ); +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLight112Controller/RGBController_MSIMysticLight112.cpp b/Controllers/MSIMysticLightController/MSIMysticLight112Controller/RGBController_MSIMysticLight112.cpp new file mode 100644 index 0000000..062643f --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight112Controller/RGBController_MSIMysticLight112.cpp @@ -0,0 +1,386 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight112.cpp | +| | +| RGBController for MSI Mystic Light 112-byte motherboard | +| | +| thombo 17 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MSIMysticLight112.h" + +struct ZoneDescription +{ + std::string name; + MSI_ZONE zone_type; +}; + +#define NUMOF_ZONES (sizeof(led_zones) / sizeof(ZoneDescription)) + +const ZoneDescription led_zones[] = +{ + ZoneDescription{ "JRGB1", MSI_ZONE_J_RGB_1 }, + ZoneDescription{ "JRAINBOW1", MSI_ZONE_J_RAINBOW_1 }, + ZoneDescription{ "JCORSAIR", MSI_ZONE_J_CORSAIR }, + ZoneDescription{ "Onboard LEDs", MSI_ZONE_ON_BOARD_LED_0 } +}; + +static std::vector zone_description; + +/**------------------------------------------------------------------*\ + @name MSI Mystic Light (112 Byte) + @category Motherboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectMSIMysticLightControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSIMysticLight112::RGBController_MSIMysticLight112 + ( + MSIMysticLight112Controller* controller_ptr + ) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "MSI"; + type = DEVICE_TYPE_MOTHERBOARD; + description = "MSI Mystic Light Device (112-byte)"; + version = controller->GetFWVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + + const std::vector* supported_zones = controller->GetSupportedZones(); + + for(std::size_t i = 0; i < supported_zones->size(); ++i) + { + for(std::size_t j = 0; j < NUMOF_ZONES; ++j) + { + if(led_zones[j].zone_type == (*supported_zones)[i]) + { + zone_description.push_back(&led_zones[j]); + break; + } + } + } + + SetupModes(); + SetupZones(); + SetupColors(); + active_mode = GetDeviceMode(); + GetDeviceConfig(); +} + +RGBController_MSIMysticLight112::~RGBController_MSIMysticLight112() +{ + zone_description.clear(); + delete controller; +} + +int RGBController_MSIMysticLight112::GetDeviceMode() +{ + MSI_MODE mode = controller->GetMode(); + + for(unsigned int i = 0; i < modes.size(); ++i) + { + if(mode == modes[i].value) + { + return i; + } + } + + return 0; +} + +void RGBController_MSIMysticLight112::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zone_description.size(); ++zone_idx) + { + const ZoneDescription* zd = zone_description[zone_idx]; + + zone new_zone; + + new_zone.name = zd->name; + + /*--------------------------------------------------\ + | 112-byte MSI does not have resizable zones, but | + | onboard LED zones have multiple LEDs | + \*-------------------------------------------------*/ + if(zd->zone_type == MSI_ZONE_ON_BOARD_LED_0) + { + new_zone.leds_max = (int)controller->GetMaxOnboardLeds(); + } + else + { + new_zone.leds_max = 1; + } + + new_zone.leds_min = new_zone.leds_max; + new_zone.leds_count = new_zone.leds_max; + + /*-------------------------------------------------*\ + | Determine zone type based on max number of LEDs | + \*-------------------------------------------------*/ + if(new_zone.leds_max == 1) + { + new_zone.type = ZONE_TYPE_SINGLE; + } + else + { + new_zone.type = ZONE_TYPE_LINEAR; + } + + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + } + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zone_description.size(); ++zone_idx) + { + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; ++led_idx) + { + led new_led; + + new_led.name = zones[zone_idx].name + " LED "; + + if(zones[zone_idx].leds_count > 1) + { + new_led.name.append(std::to_string(led_idx + 1)); + } + + new_led.value = (unsigned int)(zone_description[zone_idx]->zone_type + led_idx); + leds.push_back(new_led); + } + } +} + +void RGBController_MSIMysticLight112::ResizeZone + ( + int /*zone*/, + int /*new_size*/ + ) +{ +} + +void RGBController_MSIMysticLight112::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); ++zone_idx) + { + for(int led_idx = zones[zone_idx].leds_count - 1; led_idx >= 0; led_idx--) + { + UpdateLed((int)zone_idx, led_idx); + } + } + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight112::UpdateZoneLEDs + ( + int zone + ) +{ + for(int led_idx = zones[zone].leds_count - 1; led_idx >= 0; led_idx--) + { + UpdateLed(zone, led_idx); + } + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight112::UpdateSingleLED + ( + int led + ) +{ + UpdateLed(leds[led].value, led); + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight112::DeviceUpdateMode() +{ + if(modes[active_mode].value == MSI_MODE_DIRECT_DUMMY) + { + controller->SetDirectMode(true); + } + else + { + controller->SetDirectMode(false); + DeviceUpdateLEDs(); + } +} + +void RGBController_MSIMysticLight112::DeviceSaveMode() +{ + controller->Update(true); +} + +void RGBController_MSIMysticLight112::SetupModes() +{ + constexpr unsigned int PER_LED_ONLY = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + constexpr unsigned int RANDOM_ONLY = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + constexpr unsigned int COMMON = RANDOM_ONLY | MODE_FLAG_HAS_PER_LED_COLOR; + + SetupMode("Direct", MSI_MODE_DIRECT_DUMMY, MODE_FLAG_HAS_PER_LED_COLOR); + SetupMode("Static", MSI_MODE_STATIC, MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE); + SetupMode("Breathing", MSI_MODE_BREATHING, PER_LED_ONLY); + SetupMode("Flashing", MSI_MODE_FLASHING, COMMON); + SetupMode("Double flashing", MSI_MODE_DOUBLE_FLASHING, COMMON); + SetupMode("Lightning", MSI_MODE_LIGHTNING, PER_LED_ONLY); + SetupMode("Meteor", MSI_MODE_METEOR, COMMON); + SetupMode("Stack", MSI_MODE_WATER_DROP, COMMON); + SetupMode("Rainbow", MSI_MODE_COLOR_RING, COMMON); + SetupMode("Planetary", MSI_MODE_PLANETARY, RANDOM_ONLY); + SetupMode("Double meteor", MSI_MODE_DOUBLE_METEOR, RANDOM_ONLY); + SetupMode("Energy", MSI_MODE_ENERGY, RANDOM_ONLY); + SetupMode("Blink", MSI_MODE_BLINK, COMMON); + SetupMode("Clock", MSI_MODE_CLOCK, RANDOM_ONLY); + SetupMode("Color pulse", MSI_MODE_COLOR_PULSE, COMMON); + SetupMode("Color shift", MSI_MODE_COLOR_SHIFT, RANDOM_ONLY); + SetupMode("Color wave", MSI_MODE_COLOR_WAVE, COMMON); + SetupMode("Marquee", MSI_MODE_MARQUEE, PER_LED_ONLY); + SetupMode("Rainbow wave", MSI_MODE_RAINBOW_WAVE, RANDOM_ONLY); + SetupMode("Visor", MSI_MODE_VISOR, COMMON); + SetupMode("Rainbow flashing", MSI_MODE_RAINBOW_FLASHING, RANDOM_ONLY); + SetupMode("Rainbow double flashing", MSI_MODE_RAINBOW_DOUBLE_FLASHING, RANDOM_ONLY); +} + +void RGBController_MSIMysticLight112::UpdateLed + ( + int zone, + int led + ) +{ + unsigned char red = RGBGetRValue(zones[zone].colors[led]); + unsigned char grn = RGBGetGValue(zones[zone].colors[led]); + unsigned char blu = RGBGetBValue(zones[zone].colors[led]); + + if(controller->IsDirectModeActive()) + { + controller->SetLedColor((MSI_ZONE)zones[zone].leds[led].value, red, grn, blu); + } + else + { + bool random = modes[active_mode].color_mode == MODE_COLORS_RANDOM; + MSI_MODE mode = (MSI_MODE)modes[active_mode].value; + MSI_SPEED speed = (MSI_SPEED)modes[active_mode].speed; + MSI_BRIGHTNESS brightness = (MSI_BRIGHTNESS)modes[active_mode].brightness; + + controller->SetMode((MSI_ZONE)zones[zone].leds[led].value, mode, speed, brightness, random); + controller->SetZoneColor((MSI_ZONE)zones[zone].leds[led].value, red, grn, blu, red, grn, blu); + } +} + +void RGBController_MSIMysticLight112::SetupMode + ( + const char *name, + MSI_MODE mod, + unsigned int flags + ) +{ + mode Mode; + Mode.name = name; + Mode.value = mod; + Mode.flags = flags; + + if(flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + Mode.color_mode = MODE_COLORS_PER_LED; + } + else + { + Mode.color_mode = MODE_COLORS_RANDOM; + } + + if(flags & MODE_FLAG_HAS_SPEED) + { + Mode.speed = MSI_SPEED_MEDIUM; + Mode.speed_max = MSI_SPEED_HIGH; + Mode.speed_min = MSI_SPEED_LOW; + } + else + { + /*---------------------------------------------------------*\ + | For modes without speed this needs to be set to avoid | + | bad values in the saved profile which in turn corrupts | + | the brightness calculation when loading the profile | + \*---------------------------------------------------------*/ + Mode.speed = 0; + Mode.speed_max = 0; + Mode.speed_min = 0; + } + + if(flags & MODE_FLAG_HAS_BRIGHTNESS) + { + Mode.brightness = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_max = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_min = MSI_BRIGHTNESS_OFF; + } + else + { + Mode.brightness = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_max = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_min = MSI_BRIGHTNESS_LEVEL_100; + } + + modes.push_back(Mode); +} + +void RGBController_MSIMysticLight112::GetDeviceConfig() +{ + MSI_MODE mode; + MSI_SPEED speed; + MSI_BRIGHTNESS brightness; + bool rainbow; + unsigned int color; + + for(size_t i = 0; i < zone_description.size(); ++i) + { + controller->GetMode(zone_description[i]->zone_type, mode, speed, brightness, rainbow, color); + + for(size_t j = 0; j < zones[i].leds_count; ++j) + { + zones[i].colors[j] = color; + } + } + + controller->GetMode(zone_description[0]->zone_type, mode, speed, brightness, rainbow, color); + + for(size_t i = 0; i < modes.size(); ++i) + { + if(mode == modes[i].value) + { + if(modes[i].flags & MODE_FLAG_HAS_SPEED) + { + modes[i].speed = speed; + } + if(modes[i].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + modes[i].brightness = brightness; + } + if(rainbow) + { + if(modes[i].flags & (MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR)) + { + if(rainbow) + { + modes[i].color_mode = MODE_COLORS_RANDOM; + } + else + { + modes[i].color_mode = MODE_COLORS_PER_LED; + } + } + } + break; + } + } +} diff --git a/Controllers/MSIMysticLightController/MSIMysticLight112Controller/RGBController_MSIMysticLight112.h b/Controllers/MSIMysticLightController/MSIMysticLight112Controller/RGBController_MSIMysticLight112.h new file mode 100644 index 0000000..de0e17e --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight112Controller/RGBController_MSIMysticLight112.h @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight112.h | +| | +| RGBController for MSI Mystic Light 112-byte motherboard | +| | +| thombo 17 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "MSIMysticLight112Controller.h" + +class RGBController_MSIMysticLight112: public RGBController +{ +public: + RGBController_MSIMysticLight112(MSIMysticLight112Controller* controller_ptr); + ~RGBController_MSIMysticLight112(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + MSIMysticLight112Controller* controller; + + void SetupModes(); + void UpdateLed + ( + int zone, + int led + ); + void SetupMode + ( + const char *name, + MSI_MODE mode, + unsigned int flags + ); + int GetDeviceMode(); + void GetDeviceConfig(); +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLight162Controller/MSIMysticLight162Controller.cpp b/Controllers/MSIMysticLightController/MSIMysticLight162Controller/MSIMysticLight162Controller.cpp new file mode 100644 index 0000000..00c8783 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight162Controller/MSIMysticLight162Controller.cpp @@ -0,0 +1,452 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight162Controller.cpp | +| | +| Driver for MSI Mystic Light 162-byte motherboard | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "MSIMysticLight162Controller.h" +#include "StringUtils.h" + +#define BITSET(val, bit, pos) ((unsigned char)std::bitset<8>(val).set((pos), (bit)).to_ulong()) + +struct mystic_light_162_config +{ + unsigned short pid; // PID of the board + size_t numof_onboard_leds; // number of onboard leds + const std::vector* supported_zones; // pointer to vector of supported zones +}; + +const std::vector zones_set0 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RGB_2, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_CORSAIR, + MSI_ZONE_ON_BOARD_LED_0 +}; + +const std::vector zones_set1 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RGB_2, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_ON_BOARD_LED_0 +}; + +const std::vector zones_set2 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RGB_2, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_ON_BOARD_LED_0 +}; + +const std::vector zones_set3 = +{ + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_ON_BOARD_LED_0 +}; + +/*-----------------------------------------------------------------------------------------------------------------------------*\ +| Definition of the board sepcific configurations (number of onboard LEDs and supported zones). | +| | +| Only tested boards are listed here (refer to MSIMysticLightControllerDetect.cpp). If more boards | +| are tested the list must be extended here. Otherwise the default settings will be used (7 onboard LEDs, all zones supported). | +| Boards with yet unknown supported zones are configured to support all zones. | +\*-----------------------------------------------------------------------------------------------------------------------------*/ + +#define NUMOF_CONFIGS (sizeof(board_configs) / sizeof(mystic_light_162_config)) + +static const mystic_light_162_config board_configs[] = +{ + { 0x1720, 10, &zones_set0 }, // MPG Z390 GAMING EDGE AC + { 0x7B12, 10, &zones_set0 }, // MEG Z390 ACE + { 0x7B17, 10, &zones_set0 }, // MPG Z390 GAMING PRO CARBON + { 0x7B18, 6, &zones_set1 }, // MAG Z390 TOMAHAWK + { 0x7B50, 6, &zones_set2 }, // MPG Z390M GAMING EDGE AC + { 0x7B85, 7, &zones_set0 }, // B450 GAMING PRO CARBON + { 0x7B92, 10, &zones_set0 }, // MEG X399 CREATION + { 0xB926, 3, &zones_set3 }, // MPG B460 TRIDENT AS +}; + + +MSIMysticLight162Controller::MSIMysticLight162Controller + ( + hid_device* handle, + const char* path, + unsigned short pid, + std::string dev_name + ) +{ + dev = handle; + location = path; + name = dev_name; + + if(dev) + { + ReadFwVersion(); + ReadSettings(); + } + + /*-----------------------------------------*\ + | Initialize save flag | + \*-----------------------------------------*/ + data.save_data = 0; + data.on_board_led.colorFlags = 0x81; // force MS bit of color flags to 1 to have expectd zone control + + /*-----------------------------------------*\ + | Initialize zone based per LED data | + \*-----------------------------------------*/ + const mystic_light_162_config* board_config = nullptr; + + for(std::size_t i = 0; i < NUMOF_CONFIGS; ++i) + { + if (board_configs[i].pid == pid) + { + board_config = &board_configs[i]; + break; + } + } + + if(board_config != nullptr) + { + numof_onboard_leds = board_config->numof_onboard_leds; + supported_zones = board_config->supported_zones; + } + else + { + numof_onboard_leds = 10; + supported_zones = &zones_set0; + } + +} + +MSIMysticLight162Controller::~MSIMysticLight162Controller() +{ + hid_close(dev); +} + +void MSIMysticLight162Controller::SetMode + ( + MSI_ZONE zone, + MSI_MODE mode, + MSI_SPEED speed, + MSI_BRIGHTNESS brightness, + bool rainbow_color + ) +{ + ZoneData* zone_data = GetZoneData(data, zone); + + if(zone_data == nullptr) + { + return; + } + + if (zone <= MSI_ZONE_ON_BOARD_LED_0) + { + zone_data->effect = mode; + zone_data->speedAndBrightnessFlags = (brightness << 2) | (speed & 0x03); + zone_data->colorFlags = BITSET(zone_data->colorFlags, !rainbow_color, 7u); + zone_data->padding = 0x00; + + if(mode > MSI_MODE_DOUBLE_FLASHING) + { + zone_data->speedAndBrightnessFlags |= SYNC_SETTING_JRGB; + zone_data->colorFlags |= SYNC_SETTING_ONBOARD; + } + else + { + zone_data->speedAndBrightnessFlags &= ~SYNC_SETTING_JRGB; + zone_data->colorFlags &= ~SYNC_SETTING_ONBOARD; + } + } + + if((zone >= MSI_ZONE_ON_BOARD_LED_0) && (mode <= MSI_MODE_DOUBLE_FLASHING)) + { + zone_data = GetZoneData(data, (MSI_ZONE)((int)zone + 1)); + + if(zone_data != nullptr) + { + zone_data->effect = mode; + zone_data->speedAndBrightnessFlags = (brightness << 2) | (speed & 0x03); + zone_data->colorFlags = BITSET(zone_data->colorFlags, !rainbow_color, 7u); + zone_data->padding = 0x00; + } + } +} + +std::string MSIMysticLight162Controller::GetDeviceName() +{ + return name; +} + +std::string MSIMysticLight162Controller::GetFWVersion() +{ + std::string firmware_version; + firmware_version = "APROM: " + version_APROM + ", LDROM: " + version_LDROM; + return firmware_version; +} + +std::string MSIMysticLight162Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MSIMysticLight162Controller::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +bool MSIMysticLight162Controller::ReadSettings() +{ + /*-----------------------------------------------------*\ + | Read packet from hardware, return true if successful | + \*-----------------------------------------------------*/ + return(hid_get_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof data); +} + +bool MSIMysticLight162Controller::Update + ( + bool save + ) +{ + /*-----------------------------------------------------*\ + | Send packet to hardware, return true if successful | + \*-----------------------------------------------------*/ + data.save_data = save; + return (hid_send_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof(data)); +} + +void MSIMysticLight162Controller::SetZoneColor + ( + MSI_ZONE zone, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2 + ) +{ + ZoneData* zone_data = GetZoneData(data, zone); + + if(zone_data == nullptr) + { + return; + } + + if (zone <= MSI_ZONE_ON_BOARD_LED_0) + { + zone_data->color.R = red1; + zone_data->color.G = grn1; + zone_data->color.B = blu1; + zone_data->color2.R = red2; + zone_data->color2.G = grn2; + zone_data->color2.B = blu2; + } + + if(zone >= MSI_ZONE_ON_BOARD_LED_0) + { + zone_data = GetZoneData(data, (MSI_ZONE)((int)zone + 1)); + + if(zone_data != nullptr) + { + zone_data->color.R = red1; + zone_data->color.G = grn1; + zone_data->color.B = blu1; + zone_data->color2.R = red2; + zone_data->color2.G = grn2; + zone_data->color2.B = blu2; + } + } +} + +ZoneData *MSIMysticLight162Controller::GetZoneData + ( + FeaturePacket_162& data_packet, + MSI_ZONE zone + ) +{ + switch(zone) + { + case MSI_ZONE_J_RGB_1: + return &data_packet.j_rgb_1; + case MSI_ZONE_J_RGB_2: + return &data_packet.j_rgb_2; + case MSI_ZONE_J_RAINBOW_1: + return &data_packet.j_rainbow_1; + case MSI_ZONE_J_RAINBOW_2: + return &data_packet.on_board_led_10; + case MSI_ZONE_ON_BOARD_LED_0: + return &data_packet.on_board_led; + case MSI_ZONE_ON_BOARD_LED_1: + return &data_packet.on_board_led_1; + case MSI_ZONE_ON_BOARD_LED_2: + return &data_packet.on_board_led_2; + case MSI_ZONE_ON_BOARD_LED_3: + return &data_packet.on_board_led_3; + case MSI_ZONE_ON_BOARD_LED_4: + return &data_packet.on_board_led_4; + case MSI_ZONE_ON_BOARD_LED_5: + return &data_packet.on_board_led_5; + case MSI_ZONE_ON_BOARD_LED_6: + return &data_packet.on_board_led_6; + case MSI_ZONE_ON_BOARD_LED_7: + return &data_packet.on_board_led_7; + case MSI_ZONE_ON_BOARD_LED_8: + return &data_packet.on_board_led_8; + case MSI_ZONE_ON_BOARD_LED_9: + return &data_packet.on_board_led_9; + case MSI_ZONE_ON_BOARD_LED_10: + return &data_packet.on_board_led_10; + case MSI_ZONE_J_CORSAIR: + return &data_packet.j_corsair_1; + default: + break; + } + + return nullptr; +} + +bool MSIMysticLight162Controller::ReadFwVersion() +{ + unsigned char request[64]; + unsigned char response[64]; + int ret_val = 64; + + /*-----------------------------------------------------*\ + | First read the APROM | + | Checksum also available at report ID 180, with MSB | + | stored at index 0x08 and LSB at 0x09 | + \*-----------------------------------------------------*/ + + /*-----------------------------------------------------*\ + | Zero out buffers | + \*-----------------------------------------------------*/ + memset(request, 0x00, sizeof(request)); + memset(response, 0x00, sizeof(response)); + + /*-----------------------------------------------------*\ + | Set up APROM Firmware Version Request packet | + \*-----------------------------------------------------*/ + request[0x00] = 0x01; + request[0x01] = 0xB0; + + /*-----------------------------------------------------*\ + | Fill request from 0x02 to 0x61 with 0xCC | + \*-----------------------------------------------------*/ + memset(&request[0x02], 0xCC, sizeof(request) - 2); + + /*-----------------------------------------------------*\ + | Send request and receive response packets | + \*-----------------------------------------------------*/ + ret_val &= hid_write(dev, request, 64); + ret_val &= hid_read(dev, response, 64); + + /*-----------------------------------------------------*\ + | Extract high and low values from response | + \*-----------------------------------------------------*/ + unsigned char highValue = response[2] >> 4; + unsigned char lowValue = response[2] & 0x0F; + + /*-----------------------------------------------------*\ + | Build firmware string . | + \*-----------------------------------------------------*/ + version_APROM = std::to_string((int)highValue).append(".").append(std::to_string((int)lowValue)); + + /*-----------------------------------------------------*\ + | First read the LDROM | + | Checksum also available at report ID 184, with MSB | + | stored at index 0x08 and LSB at 0x09 | + \*-----------------------------------------------------*/ + + /*-----------------------------------------------------*\ + | Set up LDROM Firmware Version Request packet | + \*-----------------------------------------------------*/ + request[0x00] = 0x01; + request[0x01] = 0xB6; + + /*-----------------------------------------------------*\ + | Send request and receive response packets | + \*-----------------------------------------------------*/ + ret_val &= hid_write(dev, request, 64); + ret_val &= hid_read(dev, response, 64); + + /*-----------------------------------------------------*\ + | Extract high and low values from response | + \*-----------------------------------------------------*/ + highValue = response[2] >> 4; + lowValue = response[2] & 0x0F; + + /*-----------------------------------------------------*\ + | Build firmware string . | + \*-----------------------------------------------------*/ + version_LDROM = std::to_string((int)highValue).append(".").append(std::to_string((int)lowValue)); + + /*-----------------------------------------------------*\ + | If return value is zero it means an HID transfer | + | failed | + \*-----------------------------------------------------*/ + return(ret_val > 0); +} + +MSI_MODE MSIMysticLight162Controller::GetMode() +{ + return (MSI_MODE)data.on_board_led.effect; +} + +void MSIMysticLight162Controller::GetMode + ( + MSI_ZONE zone, + MSI_MODE &mode, + MSI_SPEED &speed, + MSI_BRIGHTNESS &brightness, + bool &rainbow_color, + unsigned int &color + ) +{ + /*-----------------------------------------------------*\ + | Get data for given zone | + \*-----------------------------------------------------*/ + ZoneData *zone_data = GetZoneData(data, zone); + + /*-----------------------------------------------------*\ + | Return if zone is invalid | + \*-----------------------------------------------------*/ + if(zone_data == nullptr) + { + return; + } + + /*-----------------------------------------------------*\ + | Update pointers with data | + \*-----------------------------------------------------*/ + mode = (MSI_MODE)zone_data->effect; + speed = (MSI_SPEED)(zone_data->speedAndBrightnessFlags & 0x03); + brightness = (MSI_BRIGHTNESS)((zone_data->speedAndBrightnessFlags >> 2) & 0x1F); + rainbow_color = (zone_data->colorFlags & 0x80) == 0 ? true : false; + color = ToRGBColor(zone_data->color.R, zone_data->color.G, zone_data->color.B); +} + +size_t MSIMysticLight162Controller::GetMaxOnboardLeds() +{ + return numof_onboard_leds; +} diff --git a/Controllers/MSIMysticLightController/MSIMysticLight162Controller/MSIMysticLight162Controller.h b/Controllers/MSIMysticLightController/MSIMysticLight162Controller/MSIMysticLight162Controller.h new file mode 100644 index 0000000..8c6acef --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight162Controller/MSIMysticLight162Controller.h @@ -0,0 +1,98 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight162Controller.h | +| | +| Driver for MSI Mystic Light 162-byte motherboard | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "MSIMysticLightCommon.h" +#include "RGBController.h" + +class MSIMysticLight162Controller +{ +public: + MSIMysticLight162Controller + ( + hid_device* handle, + const char* path, + unsigned short pid, + std::string dev_name + ); + + ~MSIMysticLight162Controller(); + + void SetMode + ( + MSI_ZONE zone, + MSI_MODE mode, + MSI_SPEED speed, + MSI_BRIGHTNESS brightness, + bool rainbow_color + ); + + MSI_MODE GetMode(); + + void GetMode + ( + MSI_ZONE zone, + MSI_MODE &mode, + MSI_SPEED &speed, + MSI_BRIGHTNESS &brightness, + bool &rainbow_color, + unsigned int &color + ); + + void SetZoneColor + ( + MSI_ZONE zone, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2 + ); + + bool Update + ( + bool save + ); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + std::string GetFWVersion(); + std::string GetSerial(); + + size_t GetMaxOnboardLeds(); + const std::vector* + GetSupportedZones() { return supported_zones; } + +private: + hid_device* dev; + std::string name; + std::string location; + std::string version_APROM; + std::string version_LDROM; + + FeaturePacket_162 data; + size_t numof_onboard_leds; + const std::vector* supported_zones; + + bool ReadSettings(); + bool ReadFwVersion(); + ZoneData* GetZoneData + ( + FeaturePacket_162& dataPacket, + MSI_ZONE zone + ); +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLight162Controller/RGBController_MSIMysticLight162.cpp b/Controllers/MSIMysticLightController/MSIMysticLight162Controller/RGBController_MSIMysticLight162.cpp new file mode 100644 index 0000000..c42ab82 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight162Controller/RGBController_MSIMysticLight162.cpp @@ -0,0 +1,392 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight162.cpp | +| | +| RGBController for MSI Mystic Light 162-byte motherboard | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MSIMysticLight162.h" + +struct ZoneDescription +{ + std::string name; + MSI_ZONE zone_type; +}; + +#define NUMOF_ZONES (sizeof(led_zones) / sizeof(ZoneDescription)) + +const ZoneDescription led_zones[] = +{ + ZoneDescription{ "JRGB1", MSI_ZONE_J_RGB_1 }, + ZoneDescription{ "JRGB2", MSI_ZONE_J_RGB_2 }, + ZoneDescription{ "JRAINBOW1", MSI_ZONE_J_RAINBOW_1 }, + ZoneDescription{ "JRAINBOW2", MSI_ZONE_J_RAINBOW_2 }, + ZoneDescription{ "JCORSAIR", MSI_ZONE_J_CORSAIR }, + ZoneDescription{ "Onboard LEDs", MSI_ZONE_ON_BOARD_LED_0 } +}; + +static std::vector zone_description; + +/**------------------------------------------------------------------*\ + @name MSI Mystic Light (162 Byte) + @category Motherboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectMSIMysticLightControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSIMysticLight162::RGBController_MSIMysticLight162 + ( + MSIMysticLight162Controller* controller_ptr + ) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "MSI"; + type = DEVICE_TYPE_MOTHERBOARD; + description = "MSI Mystic Light Device (162-byte)"; + version = controller->GetFWVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + + const std::vector* supported_zones = controller->GetSupportedZones(); + + for(std::size_t i = 0; i < supported_zones->size(); ++i) + { + for(std::size_t j = 0; j < NUMOF_ZONES; ++j) + { + if(led_zones[j].zone_type == (*supported_zones)[i]) + { + zone_description.push_back(&led_zones[j]); + break; + } + } + } + + SetupModes(); + SetupZones(); + SetupColors(); + active_mode = GetDeviceMode(); + GetDeviceConfig(); +} + +RGBController_MSIMysticLight162::~RGBController_MSIMysticLight162() +{ + zone_description.clear(); + delete controller; +} + +int RGBController_MSIMysticLight162::GetDeviceMode() +{ + MSI_MODE mode = controller->GetMode(); + + for(unsigned int i = 0; i < modes.size(); ++i) + { + if(mode == modes[i].value) + { + return i; + } + } + + return 0; +} + +void RGBController_MSIMysticLight162::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zone_description.size(); ++zone_idx) + { + const ZoneDescription* zd = zone_description[zone_idx]; + + zone new_zone; + + new_zone.name = zd->name; + + /*--------------------------------------------------\ + | 162-byte MSI does not have resizable zones, but | + | onboard LED zones have multiple LEDs | + \*-------------------------------------------------*/ + if(zd->zone_type == MSI_ZONE_ON_BOARD_LED_0) + { + new_zone.leds_max = (int)controller->GetMaxOnboardLeds(); + } + else + { + new_zone.leds_max = 1; + } + + new_zone.leds_min = new_zone.leds_max; + new_zone.leds_count = new_zone.leds_max; + + /*-------------------------------------------------*\ + | Determine zone type based on max number of LEDs | + \*-------------------------------------------------*/ + if(new_zone.leds_max == 1) + { + new_zone.type = ZONE_TYPE_SINGLE; + } + else + { + new_zone.type = ZONE_TYPE_LINEAR; + } + + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + } + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zone_description.size(); ++zone_idx) + { + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; ++led_idx) + { + led new_led; + + new_led.name = zones[zone_idx].name + " LED "; + + if(zones[zone_idx].leds_count > 1) + { + new_led.name.append(std::to_string(led_idx + 1)); + } + + new_led.value = (unsigned int)(zone_description[zone_idx]->zone_type + led_idx); + leds.push_back(new_led); + } + } +} + +void RGBController_MSIMysticLight162::ResizeZone + ( + int /*zone*/, + int /*new_size*/ + ) +{ +} + +void RGBController_MSIMysticLight162::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); ++zone_idx) + { + for(int led_idx = zones[zone_idx].leds_count - 1; led_idx >= 0; led_idx--) + { + UpdateLed((int)zone_idx, led_idx); + } + } + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight162::UpdateZoneLEDs + ( + int zone + ) +{ + for(int led_idx = zones[zone].leds_count - 1; led_idx >= 0; led_idx--) + { + UpdateLed(zone, led_idx); + } + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight162::UpdateSingleLED + ( + int led + ) +{ + UpdateLed(leds[led].value, led); + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight162::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIMysticLight162::DeviceSaveMode() +{ + controller->Update(true); +} + +void RGBController_MSIMysticLight162::SetupModes() +{ + constexpr unsigned int PER_LED_ONLY = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + constexpr unsigned int RANDOM_ONLY = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + constexpr unsigned int COMMON = RANDOM_ONLY | MODE_FLAG_HAS_PER_LED_COLOR; + + SetupMode("Direct", MSI_MODE_STATIC, MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE); + // SetupMode("Off", MSI_MODE_DISABLE, 0); + SetupMode("Breathing", MSI_MODE_BREATHING, PER_LED_ONLY); + SetupMode("Flashing", MSI_MODE_FLASHING, COMMON); + SetupMode("Double flashing", MSI_MODE_DOUBLE_FLASHING, COMMON); + SetupMode("Lightning", MSI_MODE_LIGHTNING, PER_LED_ONLY); + // SetupMode("MSI Marquee", MSI_MODE_MSI_MARQUEE, COMMON); + SetupMode("Meteor", MSI_MODE_METEOR, COMMON); + SetupMode("Stack", MSI_MODE_WATER_DROP, COMMON); + // SetupMode("MSI Rainbow", MSI_MODE_MSI_RAINBOW, RANDOM_ONLY); + // SetupMode("Pop", MSI_MODE_POP, COMMON); + // SetupMode("Rap", MSI_MODE_RAP, COMMON); + // SetupMode("Jazz", MSI_MODE_JAZZ, COMMON); + // SetupMode("Play", MSI_MODE_PLAY, COMMON); + // SetupMode("Movie", MSI_MODE_MOVIE, COMMON); + SetupMode("Rainbow", MSI_MODE_COLOR_RING, COMMON); + SetupMode("Planetary", MSI_MODE_PLANETARY, RANDOM_ONLY); + SetupMode("Double meteor", MSI_MODE_DOUBLE_METEOR, RANDOM_ONLY); + SetupMode("Energy", MSI_MODE_ENERGY, RANDOM_ONLY); + SetupMode("Blink", MSI_MODE_BLINK, COMMON); + SetupMode("Clock", MSI_MODE_CLOCK, RANDOM_ONLY); + SetupMode("Color pulse", MSI_MODE_COLOR_PULSE, COMMON); + SetupMode("Color shift", MSI_MODE_COLOR_SHIFT, RANDOM_ONLY); + SetupMode("Color wave", MSI_MODE_COLOR_WAVE, COMMON); + SetupMode("Marquee", MSI_MODE_MARQUEE, PER_LED_ONLY); + // SetupMode("Rainbow", MSI_MODE_RAINBOW, COMMON); + SetupMode("Rainbow wave", MSI_MODE_RAINBOW_WAVE, RANDOM_ONLY); + SetupMode("Visor", MSI_MODE_VISOR, COMMON); + // SetupMode("JRainbow", MSI_MODE_JRAINBOW, COMMON); + SetupMode("Rainbow flashing", MSI_MODE_RAINBOW_FLASHING, RANDOM_ONLY); + SetupMode("Rainbow double flashing", MSI_MODE_RAINBOW_DOUBLE_FLASHING, RANDOM_ONLY); + // SetupMode("Random", MSI_MODE_RANDOM, COMMON); + // SetupMode("Fan control", MSI_MODE_FAN_CONTROL, COMMON); + // SetupMode("Off 2", MSI_MODE_DISABLE_2, COMMON); + // SetupMode("Color ring flashing", MSI_MODE_COLOR_RING_FLASHING, COMMON); + // SetupMode("Color ring double flashing", MSI_MODE_COLOR_RING_DOUBLE_FLASHING, COMMON); + // SetupMode("Stack", MSI_MODE_STACK, COMMON); + // SetupMode("Corsair Que", MSI_MODE_CORSAIR_QUE, COMMON); + // SetupMode("Fire", MSI_MODE_FIRE, COMMON); + // SetupMode("Lava", MSI_MODE_LAVA, COMMON); +} + +void RGBController_MSIMysticLight162::UpdateLed + ( + int zone, + int led + ) +{ + unsigned char red = RGBGetRValue(zones[zone].colors[led]); + unsigned char grn = RGBGetGValue(zones[zone].colors[led]); + unsigned char blu = RGBGetBValue(zones[zone].colors[led]); + + bool random = modes[active_mode].color_mode == MODE_COLORS_RANDOM; + MSI_MODE mode = (MSI_MODE)modes[active_mode].value; + MSI_SPEED speed = (MSI_SPEED)modes[active_mode].speed; + MSI_BRIGHTNESS brightness = (MSI_BRIGHTNESS)modes[active_mode].brightness; + + controller->SetMode((MSI_ZONE)zones[zone].leds[led].value, mode, speed, brightness, random); + controller->SetZoneColor((MSI_ZONE)zones[zone].leds[led].value, red, grn, blu, red, grn, blu); +} + +void RGBController_MSIMysticLight162::SetupMode + ( + const char *name, + MSI_MODE mod, + unsigned int flags + ) +{ + mode Mode; + Mode.name = name; + Mode.value = mod; + Mode.flags = flags; + + if(flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + Mode.color_mode = MODE_COLORS_PER_LED; + } + else + { + Mode.color_mode = MODE_COLORS_RANDOM; + } + + if(flags & MODE_FLAG_HAS_SPEED) + { + Mode.speed = MSI_SPEED_MEDIUM; + Mode.speed_max = MSI_SPEED_HIGH; + Mode.speed_min = MSI_SPEED_LOW; + } + else + { + /*---------------------------------------------------------*\ + | For modes without speed this needs to be set to avoid | + | bad values in the saved profile which in turn corrupts | + | the brightness calculation when loading the profile | + \*---------------------------------------------------------*/ + Mode.speed = 0; + Mode.speed_max = 0; + Mode.speed_min = 0; + } + + if(flags & MODE_FLAG_HAS_BRIGHTNESS) + { + Mode.brightness = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_max = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_min = MSI_BRIGHTNESS_OFF; + } + else + { + Mode.brightness = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_max = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_min = MSI_BRIGHTNESS_LEVEL_100; + } + + modes.push_back(Mode); +} + +void RGBController_MSIMysticLight162::GetDeviceConfig() +{ + MSI_MODE mode; + MSI_SPEED speed; + MSI_BRIGHTNESS brightness; + bool rainbow; + unsigned int color; + + for(size_t i = 0; i < zone_description.size(); ++i) + { + controller->GetMode(zone_description[i]->zone_type, mode, speed, brightness, rainbow, color); + + for(size_t j = 0; j < zones[i].leds_count; ++j) + { + zones[i].colors[j] = color; + } + } + + controller->GetMode(zone_description[0]->zone_type, mode, speed, brightness, rainbow, color); + + for(size_t i = 0; i < modes.size(); ++i) + { + if(mode == modes[i].value) + { + if(modes[i].flags & MODE_FLAG_HAS_SPEED) + { + modes[i].speed = speed; + } + if(modes[i].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + modes[i].brightness = brightness; + } + if(rainbow) + { + if(modes[i].flags & (MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR)) + { + if(rainbow) + { + modes[i].color_mode = MODE_COLORS_RANDOM; + } + else + { + modes[i].color_mode = MODE_COLORS_PER_LED; + } + } + } + break; + } + } +} diff --git a/Controllers/MSIMysticLightController/MSIMysticLight162Controller/RGBController_MSIMysticLight162.h b/Controllers/MSIMysticLightController/MSIMysticLight162Controller/RGBController_MSIMysticLight162.h new file mode 100644 index 0000000..6afdfb9 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight162Controller/RGBController_MSIMysticLight162.h @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight162.h | +| | +| RGBController for MSI Mystic Light 162-byte motherboard | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "MSIMysticLight162Controller.h" + +class RGBController_MSIMysticLight162: public RGBController +{ +public: + RGBController_MSIMysticLight162(MSIMysticLight162Controller* controller_ptr); + ~RGBController_MSIMysticLight162(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + void SetupModes(); + void UpdateLed + ( + int zone, + int led + ); + void SetupMode + ( + const char *name, + MSI_MODE mode, + unsigned int flags + ); + int GetDeviceMode(); + void GetDeviceConfig(); + + MSIMysticLight162Controller* controller; +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLight185Controller/MSIMysticLight185Controller.cpp b/Controllers/MSIMysticLightController/MSIMysticLight185Controller/MSIMysticLight185Controller.cpp new file mode 100644 index 0000000..01ff45c --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight185Controller/MSIMysticLight185Controller.cpp @@ -0,0 +1,1294 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight185Controller.cpp | +| | +| Driver for MSI Mystic Light 185-byte motherboard | +| | +| Direct mode functionality has been implemented based on | +| the mystic-why project provided by Aleksandr | +| Garashchenko | +| (https://github.com/garashchenko/mystic-why) | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "MSIMysticLight185Controller.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + + +#define BITSET(val, bit, pos) ((unsigned char)std::bitset<8>(val).set((pos), (bit)).to_ulong()) + +#define SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT 40 +#define SYNC_PER_LED_MODE_CORSAIR_LED_COUNT 120 +#define JRAINBOW1_MAX_LED_COUNT 200 +#define JRAINBOW2_MAX_LED_COUNT 240 +#define JCORSAIR_MAX_LED_COUNT 240 + +#define MSI_DIRECT_MODE 0x25 +#define PER_LED_BASIC_SYNC_MODE (0x80 | SYNC_SETTING_ONBOARD | SYNC_SETTING_JPIPE1 | SYNC_SETTING_JPIPE2) +#define PER_LED_FULL_SYNC_MODE (PER_LED_BASIC_SYNC_MODE | SYNC_SETTING_JRAINBOW1 | SYNC_SETTING_JRAINBOW2 | SYNC_SETTING_JCORSAIR) + +struct mystic_light_185_config +{ + unsigned short pid; // PID of the board + int numof_onboard_leds; // number of onboard leds + int numof_pipe1_leds; // number of pipe 1 leds (used in per LED mode only) + int numof_pipe2_leds; // number of pipe 2 leds (used in per LED mode only) + int numof_JRGBs; // number of supported JRGB headers (used in per LED mode only) + const std::vector* supported_zones; // pointer to vector of supported zones + MSIMysticLight185Controller::DIRECT_MODE per_led_mode; // type of direct mode support +}; + +const std::vector all_zones = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RGB_2, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_CORSAIR, + MSI_ZONE_J_PIPE_1, + MSI_ZONE_J_PIPE_2, + MSI_ZONE_ON_BOARD_LED_0 +}; + +const std::vector zones_set0 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RGB_2, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_ON_BOARD_LED_0 +}; + +const std::vector zones_set1 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_CORSAIR +}; + +const std::vector zones_set2 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_ON_BOARD_LED_0 +}; + +const std::vector zones_set3 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_CORSAIR, + MSI_ZONE_J_PIPE_1, + MSI_ZONE_ON_BOARD_LED_0 +}; + +const std::vector zones_set4 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_PIPE_1 +}; + +const std::vector zones_set5 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1 +}; + +const std::vector zones_set6 = +{ + MSI_ZONE_J_RAINBOW_1 +}; + +const std::vector zones_set7 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RGB_2, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_PIPE_1, + MSI_ZONE_ON_BOARD_LED_0 +}; + +const std::vector zones_set8 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_CORSAIR, + MSI_ZONE_J_PIPE_1, + MSI_ZONE_J_PIPE_2 +}; + +const std::vector zones_set9 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_CORSAIR, + MSI_ZONE_J_PIPE_1 +}; + +const std::vector zones_set10 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_RAINBOW_3 +}; + +const std::vector zones_set11 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2 +}; + +const std::vector zones_set12 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_RAINBOW_3, + MSI_ZONE_J_PIPE_1, + MSI_ZONE_ON_BOARD_LED_0 +}; + +const std::vector zones_set13 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RGB_2, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2 +}; + +const std::vector zones_set14 = +{ + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2 +}; + +const std::vector zones_set15 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_RAINBOW_3, + MSI_ZONE_J_PIPE_1, + MSI_ZONE_J_PIPE_2, + MSI_ZONE_ON_BOARD_LED_0 +}; + +const std::vector zones_set16 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_RAINBOW_3, + MSI_ZONE_J_PIPE_1, + MSI_ZONE_J_PIPE_2 +}; + +const std::vector zones_set17 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RGB_2, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_PIPE_1 +}; + +const std::vector zones_set18 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_RAINBOW_3, + MSI_ZONE_J_PIPE_1 +}; + +const std::vector zones_set19 = +{ + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_RAINBOW_3 +}; + +const std::vector zones_set20 = +{ + MSI_ZONE_J_RGB_1, +}; + +const std::vector zones_set21 = +{ + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RGB_2, + MSI_ZONE_J_RAINBOW_1, + MSI_ZONE_J_RAINBOW_2, + MSI_ZONE_J_CORSAIR, + MSI_ZONE_J_PIPE_1, + MSI_ZONE_J_PIPE_2, + MSI_ZONE_ON_BOARD_LED_0 +}; + + +/*---------------------------------------------------------------------------------------------------------------------------------*\ +| Definition of the board sepcific configurations (number of onboard LEDs and supported zones). | +| | +| Only tested boards are listed here (refer to MSIMysticLightControllerDetect.cpp). If more boards | +| are tested the list must be extended here. Otherwise the default settings will be used (6 onboard LEDs, 2 JRGB, no direct mode). | +| Boards with yet unknown supported zones are configured to support all zones. | +\*---------------------------------------------------------------------------------------------------------------------------------*/ + +#define NUMOF_CONFIGS (sizeof(board_configs) / sizeof(mystic_light_185_config)) + +static const mystic_light_185_config board_configs[] = +{ + { 0x7B93, 6, 1, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MPG X570 GAMING PRO CARBON WIFI + { 0x7C34, 0, 1, 1, 1, &zones_set8, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MEG X570 GODLIKE + { 0x7C35, 0, 1, 0, 1, &zones_set9, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MEG X570 ACE + { 0x7C36, 6, 1, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // PRESTIGE X570 CREATION + { 0x7C37, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MPG X570 GAMING PLUS + { 0x7C56, 6, 0, 0, 1, &zones_set2, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B550 GAMING PLUS + { 0x7C59, 0, 8, 0, 1, &zones_set9, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // CREATOR TRX40 + { 0x7C60, 6, 0, 0, 1, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // TRX40-A PRO + { 0x7C67, 6, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B365M MORTAR + { 0x7C71, 6, 6, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z490 ACE + { 0x7C73, 6, 4, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z490 GAMING CARBON WIFI + { 0x7C75, 6, 0, 0, 1, &zones_set2, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z490 GAMING PLUS + { 0x7C76, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // MPG Z490M GAMING EDGE + { 0x7C77, 0, 0, 0, 0, &zones_set14, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z490I UNIFY + { 0x7C79, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z490 GAMING EDGE WIFI + { 0x7C80, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG Z490 TOMAHAWK + { 0x7C02, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B450 TOMAHAWK MAX + { 0x7C81, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B460 TOMAHAWK + { 0x7C82, 6, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B460M MORTAR WIFI + { 0x7C83, 6, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // B460M PRO-VDH WIFI + { 0x7C84, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MAG X570 TOMAHAWK WIFI + { 0x7C86, 6, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_DISABLED }, // MPG B460I GAMING EDGE + { 0x7C87, 6, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // B450M BAZOOKA MAX WIFI + { 0x7C90, 6, 4, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B550 GAMING CARBON WIFI + { 0x7C91, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B550 TOMAHAWK + { 0x7C92, 6, 0, 0, 0, &zones_set6, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B550I GAMING EDGE WIFI + { 0x7C94, 6, 0, 0, 1, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B550M MORTAR + { 0x7C95, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // B550M PRO-VDH WIFI + { 0x7C98, 6, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // Z490 PLUS + { 0x7D03, 0, 15, 18, 1, &zones_set8, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z590 GODLIKE + { 0x7D06, 4, 4, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z590 GAMING FORCE + { 0x7D07, 4, 5, 0, 2, &zones_set7, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z590 GAMING EDGE WIFI + { 0x7D08, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG Z590 TOMAHAWK + { 0x7D09, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // Z590-A PRO WIFI + { 0x7D13, 6, 0, 0, 1, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG B550 UNIFY + { 0x7D14, 6, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // A520M PRO + { 0x7D15, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B560 TOMAHAWK WIFI + { 0x7D17, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B560M MORTAR + { 0x7D18, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B560M PRO-VDH + { 0x7D19, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B560I GAMIING EDGE WIFI + { 0x7D20, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B560M PRO + { 0x7D25, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO Z690-A WIFI DDR4 + { 0x7D27, 6, 0, 0, 2, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z690 ACE + { 0x7D28, 6, 0, 0, 1, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z690 UNIFY-X + { 0x7D29, 6, 0, 0, 0, &zones_set6, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z690I UNIFY + { 0x7D30, 6, 6, 0, 2, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z690 CARBON WIFI + { 0x7D31, 4, 8, 0, 2, &zones_set12, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG EDGE WIFI DDR4 + { 0x7D32, 1, 0, 0, 1, &zones_set10, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG Z690 TOMAHAWK WIFI DDR4 + { 0x7D33, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO Z790-VC WIFI + { 0x7D36, 6, 0, 0, 2, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO Z690-P DDR4 + { 0x7D37, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B760M-VC WIFI (MS-7D37) - fans on JRAINBOW2 + { 0x7D38, 0, 0, 0, 1, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z590 UNIFY-X + { 0x7D40, 0, 0, 0, 1, &zones_set5, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B760i EDGE WIFI DDR4 + { 0x7D41, 6, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B660M TOMAHAWK WIFI DDR4 + { 0x7D42, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B660 MORTAR WIFI DDR4 + { 0x7D43, 0, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B660M-A WIFI DDR4 + { 0x7D46, 0, 1, 1, 0, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO H610M-G DDR4 + { 0x7D50, 6, 12, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG X570S ACE MAX + { 0x7D51, 6, 0, 0, 2, &zones_set1, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG X570S UNIFY-X MAX + { 0x7D52, 6, 14, 0, 1, &zones_set3, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG X570S CARBON EK X + { 0x7D53, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG X570S EDGE MAX WIFI + { 0x7D54, 6, 0, 0, 2, &zones_set0, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG X570S TOMAHAWK MAX WIFI + { 0x7D59, 0, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B660-A DDR4 + { 0x7D67, 0, 0, 0, 1, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO X670-P WIFI + { 0x7D69, 9, 2, 4, 1, &zones_set15, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG X670E ACE + { 0x7D70, 0, 6, 0, 1, &zones_set4, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG X670E Carbon WIFI + { 0x7D73, 1, 0, 0, 0, &zones_set6, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B650I EDGE WIFI + { 0x7D74, 0, 6, 0, 1, &zones_set18, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B650 CARBON WIFI + { 0x7D75, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B650 TOMAHAWK WIFI + { 0x7D76, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B650M MORTAR WIFI + { 0x7D77, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B650M-A WIFI + { 0x7D78, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B650-P WIFI + { 0x7D86, 0, 18, 4, 1, &zones_set16, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MEG Z790 ACE + { 0x7D88, 0, 0, 0, 1, &zones_set20, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z790-S WIFI + { 0x7D89, 0, 6, 0, 1, &zones_set18, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z790 CARBON WIFI + { 0x7D90, 0, 0, 0, 1, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // B760M BOMBER DDR4 + { 0x7D91, 1, 0, 0, 1, &zones_set10, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG Z790 TOMAHAWK WIFI + { 0x7D93, 6, 0, 0, 1, &zones_set2, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // Z790 GAMING PRO WIFI + { 0x7D96, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B760 TOMAHAWK WIFI DDR5 + { 0x7D97, 6, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B660 MORTAR MAX WIFI DDR4 + { 0x7D98, 0, 0, 0, 1, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B760-P WIFI DDR4 + { 0x7D99, 6, 0, 0, 1, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B760M-A WIFI DDR4 + { 0x7E01, 0, 0, 0, 1, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG B760M MORTAR MAX + { 0x7E03, 6, 0, 0, 0, &zones_set6, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG Z790I EDGE WIFI + { 0x7E06, 0, 0, 0, 2, &zones_set11, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO Z790-P WIFI DDR4 + { 0x7E07, 0, 0, 0, 2, &zones_set10, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO Z790-A WIFI DDR4 + { 0x7E09, 0, 0, 0, 0, &zones_set19, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // B650M PROJECT ZERO + { 0x7E10, 0, 6, 0, 2, &zones_set17, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MPG B650 EDGE WIFI + { 0x7E12, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG X670E TOMAHAWK WIFI + { 0x0076, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // MAG X670E TOMAHAWK WIFI (Common PID) + { 0x7E16, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // X670E GAMING PLUS WIFI + { 0x7E24, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // B650M GAMING PLUS WIFI + { 0x7E26, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // B650 GAMING PLUS WIFI + { 0x7E27, 0, 0, 0, 2, &zones_set13, MSIMysticLight185Controller::DIRECT_MODE_PER_LED }, // PRO B650M-P + { 0x7E28, 6, 0, 0, 2, &zones_set21, MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO A620M-B (MS-7E28) +}; + + +FeaturePacket_185 enable_per_led_msg; + +Color* per_led_onboard_leds; +Color* per_led_jpipe1; +Color* per_led_jpipe2; +Color* per_led_jrgb; +Color* per_led_jrainbow1_sync; +Color* per_led_jrainbow2_sync; +Color* per_led_jcorsair_sync; + +MSIMysticLight185Controller::MSIMysticLight185Controller + ( + hid_device* handle, + const char* path, + unsigned short pid, + std::string dev_name + ) +{ + dev = handle; + location = path; + name = dev_name; + + if(dev) + { + ReadFwVersion(); + ReadSettings(); + } + + if(pid == MSI_USB_PID_COMMON) + { + std::string pidStr(GetSerial().substr(0, 4)); + pid = std::stoi(pidStr, nullptr, 16); + } + + mixed_mode_support = false; + + if(pid >= 0x7D03) + { + mixed_mode_support = true; + } + + /*---------------------------------------------*\ + | Initialize save flag and some static settings | + \*---------------------------------------------*/ + data.save_data = 0; + data.on_board_led.colorFlags = 0x80 | SYNC_SETTING_ONBOARD; // always enable onboard sync flag to have expected zone control + + const mystic_light_185_config* board_config = nullptr; + + for(std::size_t i = 0; i < NUMOF_CONFIGS; ++i) + { + if(board_configs[i].pid == pid) + { + board_config = &board_configs[i]; + break; + } + } + + if(board_config != nullptr) + { + numof_onboard_leds = board_config->numof_onboard_leds; + numof_pipe1_leds = board_config->numof_pipe1_leds; + numof_pipe2_leds = board_config->numof_pipe2_leds; + numof_JRGBs = board_config->numof_JRGBs; + supported_zones = board_config->supported_zones; + per_led_mode = board_config->per_led_mode; + } + else + { + numof_onboard_leds = 6; + numof_pipe1_leds = 1; + numof_pipe2_leds = 1; + numof_JRGBs = 2; + supported_zones = &all_zones; + per_led_mode = DIRECT_MODE_DISABLED; + } + + /*-----------------------------------------*\ + | Initialize per-LED data | + \*-----------------------------------------*/ + std::memset(per_led_data_onboard_and_sync.leds, 0, sizeof(Color) * NUMOF_PER_LED_MODE_LEDS); + std::memset(per_led_data_jrainbow1.leds, 0, sizeof(Color) * NUMOF_PER_LED_MODE_LEDS); + per_led_data_jrainbow1.hdr1 = 4; + std::memset(per_led_data_jrainbow2.leds, 0, sizeof(Color) * NUMOF_PER_LED_MODE_LEDS); + per_led_data_jrainbow2.hdr1 = 4; + per_led_data_jrainbow2.hdr2 = 1; + std::memset(per_led_data_jcorsair.leds, 0, sizeof(Color) * NUMOF_PER_LED_MODE_LEDS); + per_led_data_jcorsair.hdr1 = 5; + + per_led_onboard_leds = per_led_data_onboard_and_sync.leds; + per_led_jpipe1 = per_led_onboard_leds + numof_onboard_leds; + per_led_jpipe2 = per_led_jpipe1 + numof_pipe1_leds; + per_led_jrgb = per_led_jpipe2 + numof_pipe2_leds; + per_led_jrainbow1_sync = per_led_jrgb + numof_JRGBs; + per_led_jrainbow2_sync = per_led_jrainbow1_sync + SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT; + per_led_jcorsair_sync = per_led_jrainbow2_sync + SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT; + + no_onboards = true; + no_jrainbow1 = true; + no_jrainbow2 = true; + no_jcorsair = true; + + for(std::size_t i = 0; i < supported_zones->size(); ++i) + { + switch((*supported_zones)[i]) + { + case MSI_ZONE_ON_BOARD_LED_0: + no_onboards = false; + break; + + case MSI_ZONE_J_RAINBOW_1: + no_jrainbow1 = false; + break; + + case MSI_ZONE_J_RAINBOW_2: + no_jrainbow2 = false; + break; + + case MSI_ZONE_J_CORSAIR: + case MSI_ZONE_J_RAINBOW_3: + no_jcorsair = false; + break; + + default: + break; + } + } + + /*---------------------------------------------------------------------------------------------------------*\ + | Set up per-LED switching message for synchronized mode. | + | Static initialization made problems with some compilers. Therefore this is done programmatically here. | + \*---------------------------------------------------------------------------------------------------------*/ + enable_per_led_msg.j_rgb_1.speedAndBrightnessFlags = 0x08; + enable_per_led_msg.j_rgb_1.colorFlags = 0x80; + enable_per_led_msg.j_pipe_1.speedAndBrightnessFlags = 0x2A; + enable_per_led_msg.j_pipe_1.colorFlags = 0x80; + enable_per_led_msg.j_pipe_2.speedAndBrightnessFlags = 0x2A; + enable_per_led_msg.j_pipe_2.colorFlags = 0x80; + enable_per_led_msg.j_rainbow_1.speedAndBrightnessFlags = 0x29; + enable_per_led_msg.j_rainbow_1.colorFlags = 0x80; + enable_per_led_msg.j_rainbow_1.cycle_or_led_num = 0x28; + enable_per_led_msg.j_rainbow_2.speedAndBrightnessFlags = 0x29; + enable_per_led_msg.j_rainbow_2.colorFlags = 0x80; + enable_per_led_msg.j_rainbow_2.cycle_or_led_num = 0x28; + enable_per_led_msg.j_corsair.fan_flags = 0x29; + enable_per_led_msg.j_corsair.corsair_quantity = 0x00; + enable_per_led_msg.j_corsair.padding[2] = 0x82; + enable_per_led_msg.j_corsair.is_individual = 0x78; + enable_per_led_msg.j_corsair_outerll120.speedAndBrightnessFlags = 0x28; + enable_per_led_msg.j_corsair_outerll120.colorFlags = 0x80; + enable_per_led_msg.on_board_led.effect = MSI_DIRECT_MODE; + enable_per_led_msg.on_board_led.speedAndBrightnessFlags = 0x29 | SYNC_SETTING_JRGB; + enable_per_led_msg.on_board_led.colorFlags = PER_LED_FULL_SYNC_MODE; + enable_per_led_msg.on_board_led_1.speedAndBrightnessFlags = 0x28; + enable_per_led_msg.on_board_led_1.colorFlags = 0x80; + enable_per_led_msg.on_board_led_2.speedAndBrightnessFlags = 0x28; + enable_per_led_msg.on_board_led_2.colorFlags = 0x80; + enable_per_led_msg.on_board_led_3.speedAndBrightnessFlags = 0x28; + enable_per_led_msg.on_board_led_3.colorFlags = 0x80; + enable_per_led_msg.on_board_led_4.speedAndBrightnessFlags = 0x28; + enable_per_led_msg.on_board_led_4.colorFlags = 0x80; + enable_per_led_msg.on_board_led_5.speedAndBrightnessFlags = 0x28; + enable_per_led_msg.on_board_led_5.colorFlags = 0x80; + enable_per_led_msg.on_board_led_6.speedAndBrightnessFlags = 0x28; + enable_per_led_msg.on_board_led_6.colorFlags = 0x80; + enable_per_led_msg.on_board_led_7.speedAndBrightnessFlags = 0x28; + enable_per_led_msg.on_board_led_7.colorFlags = 0x80; + enable_per_led_msg.on_board_led_8.speedAndBrightnessFlags = 0x28; + enable_per_led_msg.on_board_led_8.colorFlags = 0x80; + enable_per_led_msg.on_board_led_9.speedAndBrightnessFlags = 0x28; + enable_per_led_msg.on_board_led_9.colorFlags = 0x80; + enable_per_led_msg.j_rgb_2.speedAndBrightnessFlags = 0x2A; + enable_per_led_msg.j_rgb_2.colorFlags = 0x80; + + /*-----------------------------------------*\ + | Initialize zone based per LED data | + \*-----------------------------------------*/ + zone_based_per_led_data.j_rgb_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.j_rgb_1.colorFlags = BITSET(zone_based_per_led_data.j_rgb_1.colorFlags, true, 7u); + zone_based_per_led_data.j_pipe_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.j_pipe_1.colorFlags = BITSET(zone_based_per_led_data.j_pipe_1.colorFlags, true, 7u); + zone_based_per_led_data.j_pipe_2.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.j_pipe_2.colorFlags = BITSET(zone_based_per_led_data.j_pipe_2.colorFlags, true, 7u); + zone_based_per_led_data.j_rainbow_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.j_rainbow_1.colorFlags = BITSET(zone_based_per_led_data.j_rainbow_1.colorFlags, true, 7u); + zone_based_per_led_data.j_rainbow_2.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.j_rainbow_2.colorFlags = BITSET(zone_based_per_led_data.j_rainbow_2.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led.colorFlags = BITSET(zone_based_per_led_data.on_board_led.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_1.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_1.colorFlags = BITSET(zone_based_per_led_data.on_board_led_1.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_2.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_2.colorFlags = BITSET(zone_based_per_led_data.on_board_led_2.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_3.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_3.colorFlags = BITSET(zone_based_per_led_data.on_board_led_3.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_4.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_4.colorFlags = BITSET(zone_based_per_led_data.on_board_led_4.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_5.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_5.colorFlags = BITSET(zone_based_per_led_data.on_board_led_5.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_6.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2 << 2; + zone_based_per_led_data.on_board_led_6.colorFlags = BITSET(zone_based_per_led_data.on_board_led_6.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_7.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100; + zone_based_per_led_data.on_board_led_7.colorFlags = BITSET(zone_based_per_led_data.on_board_led_7.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_8.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_8.colorFlags = BITSET(zone_based_per_led_data.on_board_led_8.colorFlags, true, 7u); + zone_based_per_led_data.on_board_led_9.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.on_board_led_9.colorFlags = BITSET(zone_based_per_led_data.on_board_led_9.colorFlags, true, 7u); + zone_based_per_led_data.j_rgb_2.speedAndBrightnessFlags = MSI_BRIGHTNESS_LEVEL_100 << 2; + zone_based_per_led_data.j_rgb_2.colorFlags = BITSET(zone_based_per_led_data.j_rgb_2.colorFlags, true, 7u); + zone_based_per_led_data.save_data = 0; + + direct_mode = false; + sync_direct_mode = true; +} + +MSIMysticLight185Controller::~MSIMysticLight185Controller() +{ + hid_close(dev); +} + +void MSIMysticLight185Controller::SetMode + ( + MSI_ZONE zone, + MSI_MODE mode, + MSI_SPEED speed, + MSI_BRIGHTNESS brightness, + bool rainbow_color + ) +{ + if((per_led_mode == DIRECT_MODE_ZONE_BASED) && (zone > MSI_ZONE_ON_BOARD_LED_0)) + { + return; + } + + ZoneData* zone_data = GetZoneData(data, zone); + + if(zone_data == nullptr) + { + return; + } + + zone_data->effect = mode; + zone_data->speedAndBrightnessFlags = (brightness << 2) | (speed & 0x03); + zone_data->colorFlags = BITSET(zone_data->colorFlags, !rainbow_color, 7u); + zone == MSI_ZONE_J_RAINBOW_3 ? zone_data->padding = 4 : zone_data->padding = 0; + + ZoneData* on_board_zone = GetZoneData(data, MSI_ZONE_ON_BOARD_LED_0); + + if(no_onboards && ((zone == MSI_ZONE_J_RGB_1) || (zone == MSI_ZONE_J_RGB_2) || (zone == MSI_ZONE_J_PIPE_1) || (zone == MSI_ZONE_J_PIPE_2))) + { + on_board_zone->effect = zone_data->effect; + on_board_zone->speedAndBrightnessFlags = zone_data->speedAndBrightnessFlags; + on_board_zone->colorFlags = zone_data->colorFlags; + on_board_zone->colorFlags |= SYNC_SETTING_ONBOARD; + on_board_zone->padding = 0x00; + } + + if(mode > MSI_MODE_LIGHTNING) + { + on_board_zone->speedAndBrightnessFlags |= SYNC_SETTING_JRGB; + on_board_zone->colorFlags |= (SYNC_SETTING_JPIPE1 | SYNC_SETTING_JPIPE2); + } + else + { + on_board_zone->speedAndBrightnessFlags &= ~SYNC_SETTING_JRGB; + on_board_zone->colorFlags &= ~(SYNC_SETTING_JPIPE1 | SYNC_SETTING_JPIPE2); + } + + if((zone == MSI_ZONE_ON_BOARD_LED_0) && (mode <= MSI_MODE_LIGHTNING)) + { + for(int i = 0; i < numof_onboard_leds; ++i) + { + zone_data = GetZoneData(data, (MSI_ZONE)((int)MSI_ZONE_ON_BOARD_LED_1 + i)); + + if(zone_data != nullptr) + { + zone_data->effect = mode; + zone_data->speedAndBrightnessFlags = (brightness << 2) | (speed & 0x03); + zone_data->colorFlags = BITSET(zone_data->colorFlags, !rainbow_color, 7u); + zone_data->padding = 0x00; + } + } + } +} + +std::string MSIMysticLight185Controller::GetDeviceName() +{ + return name; +} + +std::string MSIMysticLight185Controller::GetFWVersion() +{ + return std::string("AP/LD ").append(version_APROM).append(" / ").append(version_LDROM); +} + +std::string MSIMysticLight185Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MSIMysticLight185Controller::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +bool MSIMysticLight185Controller::ReadSettings() +{ + /*-----------------------------------------------------*\ + | Read packet from hardware, return true if successful | + \*-----------------------------------------------------*/ + return (hid_get_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof data); +} + +bool MSIMysticLight185Controller::Update + ( + bool save + ) +{ + /*-----------------------------------------------------*\ + | Send packet to hardware, return true if successful | + \*-----------------------------------------------------*/ + if(direct_mode) + { + if(per_led_mode == DIRECT_MODE_PER_LED) + { + if(sync_direct_mode) + { + return (hid_send_feature_report(dev, (unsigned char*)&per_led_data_onboard_and_sync, sizeof(per_led_data_onboard_and_sync)) == sizeof(per_led_data_onboard_and_sync)); + } + else + { + if(!no_jrainbow1) + { + (void)hid_send_feature_report(dev, (unsigned char*)&per_led_data_jrainbow1, sizeof(per_led_data_jrainbow1)); + std::this_thread::sleep_for(13ms); + } + if(!no_jrainbow2) + { + (void)hid_send_feature_report(dev, (unsigned char*)&per_led_data_jrainbow2, sizeof(per_led_data_jrainbow2)); + std::this_thread::sleep_for(13ms); + } + if(!no_jcorsair) + { + (void)hid_send_feature_report(dev, (unsigned char*)&per_led_data_jcorsair, sizeof(per_led_data_jcorsair)); + std::this_thread::sleep_for(13ms); + } + return (hid_send_feature_report(dev, (unsigned char*)&per_led_data_onboard_and_sync, sizeof(per_led_data_onboard_and_sync)) == sizeof(per_led_data_onboard_and_sync)); + } + } + else + { + return (hid_send_feature_report(dev, (unsigned char*)&zone_based_per_led_data, sizeof(zone_based_per_led_data)) == sizeof(zone_based_per_led_data)); + } + } + else + { + /*-----------------------------------------------------*\ + | Save new state, read current state from board, | + | send old state first, then new state. | + | Windows Mystic Light sends two consecutive reports. | + \*-----------------------------------------------------*/ + FeaturePacket_185 new_data = data; + memcpy((unsigned char*)&new_data, (unsigned char*)&data, sizeof(data)); + + ReadSettings(); + data.save_data = save; + hid_send_feature_report(dev, (unsigned char*)&data, sizeof(data)); + + memcpy((unsigned char*)&data, (unsigned char*)&new_data, sizeof(data)); + data.save_data = save; + return (hid_send_feature_report(dev, (unsigned char*)&data, sizeof(data)) == sizeof(data)); + } +} + +void MSIMysticLight185Controller::SetZoneColor + ( + MSI_ZONE zone, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2 + ) +{ + if((per_led_mode == DIRECT_MODE_ZONE_BASED) && (zone > MSI_ZONE_ON_BOARD_LED_0)) + { + return; + } + + ZoneData* zone_data = GetZoneData(data, zone); + + if(zone_data == nullptr) + { + return; + } + + zone_data->color.R = red1; + zone_data->color.G = grn1; + zone_data->color.B = blu1; + zone_data->color2.R = red2; + zone_data->color2.G = grn2; + zone_data->color2.B = blu2; + + if(no_onboards && ((zone == MSI_ZONE_J_RGB_1) || (zone == MSI_ZONE_J_RGB_2) || (zone == MSI_ZONE_J_PIPE_1) || (zone == MSI_ZONE_J_PIPE_2))) + { + ZoneData* on_board_zone = GetZoneData(data, MSI_ZONE_ON_BOARD_LED_0); + + on_board_zone->color.R = red1; + on_board_zone->color.G = grn1; + on_board_zone->color.B = blu1; + on_board_zone->color2.R = red2; + on_board_zone->color2.G = grn2; + on_board_zone->color2.B = blu2; + } + + if(zone == MSI_ZONE_ON_BOARD_LED_0) + { + for(int i = 0; i < numof_onboard_leds; ++i) + { + zone_data = GetZoneData(data, (MSI_ZONE)((int)MSI_ZONE_ON_BOARD_LED_1 + i)); + + if(zone_data != nullptr) + { + zone_data->color.R = red1; + zone_data->color.G = grn1; + zone_data->color.B = blu1; + zone_data->color2.R = red2; + zone_data->color2.G = grn2; + zone_data->color2.B = blu2; + } + } + } +} + +void MSIMysticLight185Controller::SetLedColor + ( + MSI_ZONE zone, + int index, + unsigned char red, + unsigned char grn, + unsigned char blu + ) +{ + if(per_led_mode == DIRECT_MODE_PER_LED) + { + Color* zone_data = GetPerLedZoneData(zone); + + if(zone_data != nullptr) + { + int maxSize = (int)GetMaxDirectLeds(zone); + + if(sync_direct_mode) + { + switch(zone) + { + case MSI_ZONE_J_RAINBOW_1: + case MSI_ZONE_J_RAINBOW_2: + maxSize = SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT; + break; + + case MSI_ZONE_J_RAINBOW_3: + case MSI_ZONE_J_CORSAIR: + maxSize = SYNC_PER_LED_MODE_CORSAIR_LED_COUNT; + break; + + default: + break; + } + } + + if(index < maxSize) + { + zone_data[index].R = red; + zone_data[index].G = grn; + zone_data[index].B = blu; + } + } + } + else + { + if(((zone == MSI_ZONE_J_RAINBOW_1) || (zone == MSI_ZONE_J_RAINBOW_2) || (zone == MSI_ZONE_J_PIPE_1) || (zone == MSI_ZONE_J_PIPE_2)) && (index != 0)) + { + return; + } + + if(zone >= MSI_ZONE_ON_BOARD_LED_0) + { + zone = (MSI_ZONE)((int)zone + index + 1); + } + + ZoneData *zone_data = GetZoneData(zone_based_per_led_data, zone); + + if(zone_data == nullptr) + { + return; + } + + zone_data->color.R = red; + zone_data->color.G = grn; + zone_data->color.B = blu; + zone_data->color2.R = red; + zone_data->color2.G = grn; + zone_data->color2.B = blu; + } +} + +ZoneData *MSIMysticLight185Controller::GetZoneData + ( + FeaturePacket_185& data_packet, + MSI_ZONE zone + ) +{ + switch(zone) + { + case MSI_ZONE_J_RGB_1: + return &data_packet.j_rgb_1; + case MSI_ZONE_J_RGB_2: + if(mixed_mode_support) + { + return &data_packet.on_board_led_6; + } + else + { + return &data_packet.j_rgb_2; + } + case MSI_ZONE_J_RAINBOW_1: + return &data_packet.j_rainbow_1; + case MSI_ZONE_J_RAINBOW_2: + return &data_packet.j_rainbow_2; + case MSI_ZONE_J_RAINBOW_3: + return (ZoneData*)&data_packet.j_corsair; + case MSI_ZONE_J_PIPE_1: + return &data_packet.j_pipe_1; + case MSI_ZONE_J_PIPE_2: + return &data_packet.j_pipe_2; + case MSI_ZONE_ON_BOARD_LED_0: + return &data_packet.on_board_led; + case MSI_ZONE_ON_BOARD_LED_1: + return &data_packet.on_board_led_1; + case MSI_ZONE_ON_BOARD_LED_2: + return &data_packet.on_board_led_2; + case MSI_ZONE_ON_BOARD_LED_3: + return &data_packet.on_board_led_3; + case MSI_ZONE_ON_BOARD_LED_4: + return &data_packet.on_board_led_4; + case MSI_ZONE_ON_BOARD_LED_5: + return &data_packet.on_board_led_5; + case MSI_ZONE_ON_BOARD_LED_6: + if(mixed_mode_support) + { + return &data_packet.j_corsair_outerll120; + } + else + { + return &data_packet.on_board_led_6; + } + case MSI_ZONE_ON_BOARD_LED_7: + return &data_packet.on_board_led_7; + case MSI_ZONE_ON_BOARD_LED_8: + return &data_packet.on_board_led_8; + case MSI_ZONE_ON_BOARD_LED_9: + return &data_packet.on_board_led_9; + case MSI_ZONE_J_CORSAIR_OUTERLL120: + return &data_packet.j_corsair_outerll120; + case MSI_ZONE_J_CORSAIR: + return (ZoneData*)&data_packet.j_corsair; + default: + break; + } + + return nullptr; +} + +Color *MSIMysticLight185Controller::GetPerLedZoneData + ( + MSI_ZONE zone + ) +{ + switch(zone) + { + case MSI_ZONE_J_RAINBOW_1: + if(sync_direct_mode) + { + return per_led_jrainbow1_sync; + } + else + { + return per_led_data_jrainbow1.leds; + } + case MSI_ZONE_J_RAINBOW_2: + if(sync_direct_mode) + { + return per_led_jrainbow2_sync; + } + else + { + return per_led_data_jrainbow2.leds; + } + case MSI_ZONE_ON_BOARD_LED_0: + return per_led_onboard_leds; + case MSI_ZONE_J_RAINBOW_3: + case MSI_ZONE_J_CORSAIR: + if(sync_direct_mode) + { + return per_led_jcorsair_sync; + } + else + { + return per_led_data_jcorsair.leds; + } + case MSI_ZONE_J_RGB_1: + return per_led_jrgb; + case MSI_ZONE_J_RGB_2: + return per_led_jrgb + 1; + case MSI_ZONE_J_PIPE_1: + return per_led_jpipe1; + case MSI_ZONE_J_PIPE_2: + return per_led_jpipe2; + default: + break; + } + + return nullptr; +} + +RainbowZoneData *MSIMysticLight185Controller::GetRainbowZoneData + ( + MSI_ZONE zone + ) +{ + switch(zone) + { + case MSI_ZONE_J_RAINBOW_1: + return &data.j_rainbow_1; + case MSI_ZONE_J_RAINBOW_2: + return &data.j_rainbow_2; + case MSI_ZONE_J_RAINBOW_3: + return (RainbowZoneData*)&data.j_corsair; + case MSI_ZONE_J_CORSAIR: + default: + return nullptr; + } +} + +bool MSIMysticLight185Controller::ReadFwVersion() +{ + unsigned char request[64]; + unsigned char response[64]; + int ret_val = 64; + + /*-----------------------------------------------------*\ + | First read the APROM | + | Checksum also available at report ID 180, with MSB | + | stored at index 0x08 and LSB at 0x09 | + \*-----------------------------------------------------*/ + + /*-----------------------------------------------------*\ + | Zero out buffers | + \*-----------------------------------------------------*/ + memset(request, 0x00, sizeof(request)); + memset(response, 0x00, sizeof(response)); + + /*-----------------------------------------------------*\ + | Set up APROM Firmware Version Request packet | + \*-----------------------------------------------------*/ + request[0x00] = 0x01; + request[0x01] = 0xB0; + + /*-----------------------------------------------------*\ + | Fill request from 0x02 to 0x61 with 0xCC | + \*-----------------------------------------------------*/ + memset(&request[0x02], 0xCC, sizeof(request) - 2); + + /*-----------------------------------------------------*\ + | Send request and receive response packets | + \*-----------------------------------------------------*/ + ret_val &= hid_write(dev, request, 64); + ret_val &= hid_read(dev, response, 64); + + /*-----------------------------------------------------*\ + | Extract high and low values from response | + \*-----------------------------------------------------*/ + unsigned char highValue = response[2] >> 4; + unsigned char lowValue = response[2] & 0x0F; + + /*-----------------------------------------------------*\ + | Build firmware string . | + \*-----------------------------------------------------*/ + version_APROM = std::to_string((int)highValue).append(".").append(std::to_string((int)lowValue)); + + /*-----------------------------------------------------*\ + | First read the LDROM | + | Checksum also available at report ID 184, with MSB | + | stored at index 0x08 and LSB at 0x09 | + \*-----------------------------------------------------*/ + + /*-----------------------------------------------------*\ + | Set up LDROM Firmware Version Request packet | + \*-----------------------------------------------------*/ + request[0x00] = 0x01; + request[0x01] = 0xB6; + + /*-----------------------------------------------------*\ + | Send request and receive response packets | + \*-----------------------------------------------------*/ + ret_val &= hid_write(dev, request, 64); + ret_val &= hid_read(dev, response, 64); + + /*-----------------------------------------------------*\ + | Extract high and low values from response | + \*-----------------------------------------------------*/ + highValue = response[2] >> 4; + lowValue = response[2] & 0x0F; + + /*-----------------------------------------------------*\ + | Build firmware string . | + \*-----------------------------------------------------*/ + version_LDROM = std::to_string((int)highValue).append(".").append(std::to_string((int)lowValue)); + + /*-----------------------------------------------------*\ + | If return value is zero it means an HID transfer | + | failed | + \*-----------------------------------------------------*/ + return (ret_val > 0); +} + +MSI_MODE MSIMysticLight185Controller::GetMode() +{ + if(data.on_board_led.effect == MSI_DIRECT_MODE) + { + direct_mode = true; + return MSI_MODE_DIRECT_DUMMY; + } + else + { + return (MSI_MODE)data.j_rainbow_1.effect; + } +} + +void MSIMysticLight185Controller::GetMode + ( + MSI_ZONE zone, + MSI_MODE &mode, + MSI_SPEED &speed, + MSI_BRIGHTNESS &brightness, + bool &rainbow_color, + unsigned int &color + ) +{ + /*-----------------------------------------------------*\ + | Get data for given zone | + \*-----------------------------------------------------*/ + ZoneData *zone_data = GetZoneData(data, zone); + + /*-----------------------------------------------------*\ + | Return if zone is invalid | + \*-----------------------------------------------------*/ + if(!zone_data) + { + return; + } + + /*-----------------------------------------------------*\ + | Update pointers with data | + \*-----------------------------------------------------*/ + mode = (MSI_MODE)zone_data->effect; + speed = (MSI_SPEED)(zone_data->speedAndBrightnessFlags & 0x03); + brightness = (MSI_BRIGHTNESS)((zone_data->speedAndBrightnessFlags >> 2) & 0x1F); + rainbow_color = (zone_data->colorFlags & 0x80) == 0 ? true : false; + color = ToRGBColor(zone_data->color.R, zone_data->color.G, zone_data->color.B); +} + +void MSIMysticLight185Controller::SetCycleCount + ( + MSI_ZONE zone, + unsigned char cycle_num + ) +{ + RainbowZoneData *requested_zone = GetRainbowZoneData(zone); + + if(!requested_zone) + { + return; + } + + requested_zone->cycle_or_led_num = cycle_num; + SelectPerLedProtocol(); +} + +void MSIMysticLight185Controller::SetDirectMode + ( + bool mode + ) +{ + direct_mode = mode; + SelectPerLedProtocol(); +} + +size_t MSIMysticLight185Controller::GetMaxDirectLeds + ( + MSI_ZONE zone + ) +{ + switch(zone) + { + case MSI_ZONE_J_RGB_1: + case MSI_ZONE_J_RGB_2: + return 1; + + case MSI_ZONE_J_PIPE_1: + return numof_pipe1_leds; + + case MSI_ZONE_J_PIPE_2: + return numof_pipe2_leds; + + case MSI_ZONE_J_RAINBOW_1: + if(per_led_mode == DIRECT_MODE_PER_LED) + { + return JRAINBOW1_MAX_LED_COUNT; + } + else + { + return 1; + } + case MSI_ZONE_J_RAINBOW_2: + case MSI_ZONE_J_RAINBOW_3: + if(per_led_mode == DIRECT_MODE_PER_LED) + { + return JRAINBOW2_MAX_LED_COUNT; + } + else + { + return 1; + } + + case MSI_ZONE_J_CORSAIR: + if(per_led_mode == DIRECT_MODE_PER_LED) + { + return JCORSAIR_MAX_LED_COUNT; + } + else + { + return 1; + } + + case MSI_ZONE_ON_BOARD_LED_0: + return numof_onboard_leds; + + default: + return 1; + } +} + +void MSIMysticLight185Controller::SelectPerLedProtocol() +{ + + unsigned char jrainbow1_size = 0; + unsigned char jrainbow2_size = 0; + unsigned char jrainbow3_size = 0; + + RainbowZoneData* zone_data = GetRainbowZoneData(MSI_ZONE_J_RAINBOW_1); + + if(zone_data != nullptr) + { + jrainbow1_size = zone_data->cycle_or_led_num; + } + + zone_data = GetRainbowZoneData(MSI_ZONE_J_RAINBOW_2); + + if(zone_data != nullptr) + { + jrainbow2_size = zone_data->cycle_or_led_num; + } + + zone_data = GetRainbowZoneData(MSI_ZONE_J_RAINBOW_3); + + if(zone_data != nullptr) + { + jrainbow3_size = zone_data->cycle_or_led_num; + } + + sync_direct_mode = true; + + if((jrainbow1_size > SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT) || + (jrainbow2_size > SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT) || + (jrainbow3_size > SYNC_PER_LED_MODE_CORSAIR_LED_COUNT)) + { + sync_direct_mode = false; + } + + if(sync_direct_mode) + { + enable_per_led_msg.j_rainbow_1.effect = MSI_MODE_STATIC; + enable_per_led_msg.j_rainbow_1.cycle_or_led_num = SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT; + enable_per_led_msg.j_rainbow_2.effect = MSI_MODE_STATIC; + enable_per_led_msg.j_rainbow_2.cycle_or_led_num = SYNC_PER_LED_MODE_JRAINBOW_LED_COUNT; + enable_per_led_msg.j_corsair.effect = MSI_MODE_STATIC; + enable_per_led_msg.j_corsair.is_individual = SYNC_PER_LED_MODE_CORSAIR_LED_COUNT; + enable_per_led_msg.on_board_led.colorFlags = PER_LED_FULL_SYNC_MODE; + } + else + { + if(!no_jrainbow1) + { + enable_per_led_msg.j_rainbow_1.effect = MSI_DIRECT_MODE; + enable_per_led_msg.j_rainbow_1.cycle_or_led_num = JRAINBOW1_MAX_LED_COUNT; + } + if(!no_jrainbow2) + { + enable_per_led_msg.j_rainbow_2.effect = MSI_DIRECT_MODE; + enable_per_led_msg.j_rainbow_2.cycle_or_led_num = JRAINBOW2_MAX_LED_COUNT; + } + if(!no_jcorsair) + { + enable_per_led_msg.j_corsair.effect = MSI_DIRECT_MODE; + enable_per_led_msg.j_corsair.is_individual = JCORSAIR_MAX_LED_COUNT; + } + enable_per_led_msg.on_board_led.colorFlags = PER_LED_BASIC_SYNC_MODE; + } + + if(direct_mode) + { + if(per_led_mode == DIRECT_MODE_PER_LED) + { + hid_send_feature_report(dev, (unsigned char*)&enable_per_led_msg, sizeof(enable_per_led_msg)); + } + } +} diff --git a/Controllers/MSIMysticLightController/MSIMysticLight185Controller/MSIMysticLight185Controller.h b/Controllers/MSIMysticLightController/MSIMysticLight185Controller/MSIMysticLight185Controller.h new file mode 100644 index 0000000..16aed11 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight185Controller/MSIMysticLight185Controller.h @@ -0,0 +1,157 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight185Controller.h | +| | +| Driver for MSI Mystic Light 185-byte motherboard | +| | +| Direct mode functionality has been implemented based on | +| the mystic-why project provided by Aleksandr | +| Garashchenko | +| (https://github.com/garashchenko/mystic-why) | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "MSIMysticLightCommon.h" +#include "RGBController.h" + +class MSIMysticLight185Controller +{ +public: + MSIMysticLight185Controller + ( + hid_device* handle, + const char* path, + unsigned short pid, + std::string dev_name + ); + + ~MSIMysticLight185Controller(); + + void SetMode + ( + MSI_ZONE zone, + MSI_MODE mode, + MSI_SPEED speed, + MSI_BRIGHTNESS brightness, + bool rainbow_color + ); + + MSI_MODE GetMode(); + + void GetMode + ( + MSI_ZONE zone, + MSI_MODE &mode, + MSI_SPEED &speed, + MSI_BRIGHTNESS &brightness, + bool &rainbow_color, + unsigned int &color + ); + + void SetZoneColor + ( + MSI_ZONE zone, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2 + ); + + void SetLedColor + ( + MSI_ZONE zone, + int index, + unsigned char red, + unsigned char grn, + unsigned char blu + ); + + void SetCycleCount + ( + MSI_ZONE zone, + unsigned char cycle_num + ); + + bool Update + ( + bool save + ); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + std::string GetFWVersion(); + std::string GetSerial(); + + void SetDirectMode + ( + bool mode + ); + bool IsDirectModeActive() { return direct_mode; } + size_t GetMaxDirectLeds + ( + MSI_ZONE zone + ); + const std::vector* + GetSupportedZones() { return supported_zones; } + + enum DIRECT_MODE + { + DIRECT_MODE_DISABLED, + DIRECT_MODE_PER_LED, + DIRECT_MODE_ZONE_BASED + }; + + DIRECT_MODE GetSupportedDirectMode() { return per_led_mode; } + +private: + hid_device* dev; + std::string name; + std::string location; + std::string version_APROM; + std::string version_LDROM; + + FeaturePacket_185 data; + FeaturePacket_PerLED_185 per_led_data_onboard_and_sync; + FeaturePacket_PerLED_185 per_led_data_jrainbow1; + FeaturePacket_PerLED_185 per_led_data_jrainbow2; + FeaturePacket_PerLED_185 per_led_data_jcorsair; + FeaturePacket_185 zone_based_per_led_data; + bool direct_mode; + bool sync_direct_mode; + bool no_onboards; + bool no_jrainbow1; + bool no_jrainbow2; + bool no_jcorsair; + bool mixed_mode_support; + int numof_onboard_leds; + int numof_pipe1_leds; + int numof_pipe2_leds; + int numof_JRGBs; + const std::vector* supported_zones; + DIRECT_MODE per_led_mode; + + bool ReadSettings(); + bool ReadFwVersion(); + ZoneData* GetZoneData + ( + FeaturePacket_185& data_packet, + MSI_ZONE zone + ); + RainbowZoneData* GetRainbowZoneData(MSI_ZONE zone); + Color* GetPerLedZoneData + ( + MSI_ZONE zone + ); + void SelectPerLedProtocol(); +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLight185Controller/RGBController_MSIMysticLight185.cpp b/Controllers/MSIMysticLightController/MSIMysticLight185Controller/RGBController_MSIMysticLight185.cpp new file mode 100644 index 0000000..223945b --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight185Controller/RGBController_MSIMysticLight185.cpp @@ -0,0 +1,520 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight185.cpp | +| | +| RGBController for MSI Mystic Light 185-byte motherboard | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MSIMysticLight185.h" +#include "LogManager.h" + +struct ZoneDescription +{ + std::string name; + MSI_ZONE zone_type; +}; + +#define NUMOF_ZONES (sizeof(led_zones) / sizeof(ZoneDescription)) + +const ZoneDescription led_zones[] = +{ + ZoneDescription{ "JRGB1", MSI_ZONE_J_RGB_1 }, + ZoneDescription{ "JRGB2", MSI_ZONE_J_RGB_2 }, + ZoneDescription{ "JRAINBOW1", MSI_ZONE_J_RAINBOW_1 }, + ZoneDescription{ "JRAINBOW2", MSI_ZONE_J_RAINBOW_2 }, + ZoneDescription{ "JRAINBOW3", MSI_ZONE_J_RAINBOW_3 }, + ZoneDescription{ "JCORSAIR", MSI_ZONE_J_CORSAIR }, + ZoneDescription{ "PIPE1", MSI_ZONE_J_PIPE_1 }, + ZoneDescription{ "PIPE2", MSI_ZONE_J_PIPE_2 }, + ZoneDescription{ "ONBOARD", MSI_ZONE_ON_BOARD_LED_0 } +}; + +static std::vector zone_description; + +/*---------------------------------------------------------------------------------------------------------*\ +| Returns the index of the zone_description in led_zones which has zone_type equal to the given zone_type. | +| Returns -1 if no such zone_description exists. | +\*---------------------------------------------------------------------------------------------------------*/ +static int IndexOfZoneForType(MSI_ZONE zone_type) +{ + for(size_t i = 0; i < zone_description.size(); ++i) + { + if(zone_description[i]->zone_type == zone_type) + { + return (int)i; + } + } + + return -1; +} + +/**------------------------------------------------------------------*\ + @name MSI Mystic Light (185 Byte) + @category Motherboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectMSIMysticLightControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSIMysticLight185::RGBController_MSIMysticLight185 + ( + MSIMysticLight185Controller* controller_ptr + ) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "MSI"; + type = DEVICE_TYPE_MOTHERBOARD; + description = "MSI Mystic Light Device (185-byte)"; + version = controller->GetFWVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + + const std::vector* supported_zones = controller->GetSupportedZones(); + + for(std::size_t i = 0; i < supported_zones->size(); ++i) + { + for(std::size_t j = 0; j < NUMOF_ZONES; ++j) + { + if(led_zones[j].zone_type == (*supported_zones)[i]) + { + zone_description.push_back(&led_zones[j]); + break; + } + } + } + + last_resizable_zone = MSI_ZONE_NONE; + SetupModes(); + SetupZones(); + active_mode = GetDeviceMode(); +} + +RGBController_MSIMysticLight185::~RGBController_MSIMysticLight185() +{ + zone_description.clear(); + delete controller; +} + +int RGBController_MSIMysticLight185::GetDeviceMode() +{ + MSI_MODE mode = controller->GetMode(); + + for(unsigned int i = 0; i < modes.size(); ++i) + { + if(mode == modes[i].value) + { + return i; + } + } + + return 0; +} + +void RGBController_MSIMysticLight185::SetupZones() +{ + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + if(first_run) + { + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zone_description.size(); ++zone_idx) + { + const ZoneDescription* zd = zone_description[zone_idx]; + + zone new_zone; + + new_zone.name = zd->name; + new_zone.flags = 0; + + int maxLeds = (int)controller->GetMaxDirectLeds(zd->zone_type); + + /*-------------------------------------------------*\ + | This is a fixed size zone | + \*-------------------------------------------------*/ + if(((zd->zone_type != MSI_ZONE_J_RAINBOW_1) + && (zd->zone_type != MSI_ZONE_J_RAINBOW_2) + && (zd->zone_type != MSI_ZONE_J_RAINBOW_3) + && (zd->zone_type != MSI_ZONE_J_CORSAIR))) + { + new_zone.leds_min = maxLeds; + new_zone.leds_max = maxLeds; + new_zone.leds_count = maxLeds; + } + /*--------------------------------------------------\ + | This is a resizable zone on a board that does not | + | support per-LED direct mode | + \*-------------------------------------------------*/ + else if(controller->GetSupportedDirectMode() == MSIMysticLight185Controller::DIRECT_MODE_ZONE_BASED) + { + new_zone.leds_min = 0; + new_zone.leds_max = 30;//maxLeds; + new_zone.leds_count = 0; + last_resizable_zone = zd->zone_type; + new_zone.flags |= ZONE_FLAG_RESIZE_EFFECTS_ONLY; + } + /*--------------------------------------------------\ + | This is a resizable zone on a board that does | + | support per-LED direct mode | + \*-------------------------------------------------*/ + else + { + new_zone.leds_min = 0; + new_zone.leds_max = maxLeds; + new_zone.leds_count = 0; + last_resizable_zone = zd->zone_type; + } + + /*-------------------------------------------------*\ + | Determine zone type based on max number of LEDs | + \*-------------------------------------------------*/ + if((new_zone.leds_max == 1) || (new_zone.flags & ZONE_FLAG_RESIZE_EFFECTS_ONLY)) + { + new_zone.type = ZONE_TYPE_SINGLE; + } + else + { + new_zone.type = ZONE_TYPE_LINEAR; + } + + new_zone.matrix_map = NULL; + + zones.push_back(new_zone); + } + } + + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zone_description.size(); ++zone_idx) + { + controller->SetCycleCount(zone_description[zone_idx]->zone_type, zones[zone_idx].leds_count); + + if((zones[zone_idx].flags & ZONE_FLAG_RESIZE_EFFECTS_ONLY) == 0) + { + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; ++led_idx) + { + led new_led; + + new_led.name = zones[zone_idx].name; + + if(zones[zone_idx].leds_count > 1) + { + new_led.name.append(" LED " + std::to_string(led_idx + 1)); + } + + new_led.value = zone_description[zone_idx]->zone_type; + leds.push_back(new_led); + } + } + else if(zones[zone_idx].leds_count > 0) + { + led new_led; + + new_led.name = zones[zone_idx].name; + + new_led.value = zone_description[zone_idx]->zone_type; + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_MSIMysticLight185::ResizeZone + ( + int zone, + int new_size + ) +{ + if((size_t)zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + SetupZones(); + + if(zone_description[zone]->zone_type == last_resizable_zone) + { + GetDeviceConfig(); + last_resizable_zone = MSI_ZONE_NONE; + } + } +} + +void RGBController_MSIMysticLight185::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); ++zone_idx) + { + for(int led_idx = zones[zone_idx].leds_count - 1; led_idx >= 0; led_idx--) + { + UpdateLed((int)zone_idx, led_idx); + } + } + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight185::UpdateZoneLEDs(int zone) +{ + for(int led_idx = zones[zone].leds_count - 1; led_idx >= 0; led_idx--) + { + UpdateLed(zone, led_idx); + } + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight185::UpdateSingleLED + ( + int led + ) +{ + int zone_index = IndexOfZoneForType((MSI_ZONE)leds[led].value); + + if(zone_index == -1) + { + LOG_DEBUG("[%s]: could not find zone for type %d", controller->GetDeviceName().c_str(), leds[led].value); + return; + } + + int led_index = led - zones[zone_index].start_idx; + UpdateLed(zone_index, led_index); + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight185::DeviceUpdateMode() +{ + if(modes[active_mode].value == MSI_MODE_DIRECT_DUMMY) + { + controller->SetDirectMode(true); + } + else + { + controller->SetDirectMode(false); + DeviceUpdateLEDs(); + } +} + +void RGBController_MSIMysticLight185::DeviceSaveMode() +{ + controller->Update(true); +} + +void RGBController_MSIMysticLight185::SetupModes() +{ + constexpr unsigned int PER_LED_ONLY = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + constexpr unsigned int RANDOM_ONLY = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + constexpr unsigned int COMMON = RANDOM_ONLY | MODE_FLAG_HAS_PER_LED_COLOR; + + if(controller->GetSupportedDirectMode() != MSIMysticLight185Controller::DIRECT_MODE_DISABLED) + { + SetupMode("Direct", MSI_MODE_DIRECT_DUMMY, MODE_FLAG_HAS_PER_LED_COLOR); + } + + SetupMode("Static", MSI_MODE_STATIC, MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE); + // SetupMode("Off", MSI_MODE_DISABLE, 0); + SetupMode("Breathing", MSI_MODE_BREATHING, PER_LED_ONLY); + SetupMode("Flashing", MSI_MODE_FLASHING, COMMON); + SetupMode("Double flashing", MSI_MODE_DOUBLE_FLASHING, COMMON); + SetupMode("Lightning", MSI_MODE_LIGHTNING, PER_LED_ONLY); + // SetupMode("MSI Marquee", MSI_MODE_MSI_MARQUEE, COMMON); + SetupMode("Meteor", MSI_MODE_METEOR, COMMON); + // SetupMode("Water drop", MSI_MODE_WATER_DROP, COMMON); + // SetupMode("MSI Rainbow", MSI_MODE_MSI_RAINBOW, RANDOM_ONLY); + // SetupMode("Pop", MSI_MODE_POP, COMMON); + // SetupMode("Rap", MSI_MODE_RAP, COMMON); + // SetupMode("Jazz", MSI_MODE_JAZZ, COMMON); + // SetupMode("Play", MSI_MODE_PLAY, COMMON); + // SetupMode("Movie", MSI_MODE_MOVIE, COMMON); + SetupMode("Color ring", MSI_MODE_COLOR_RING, RANDOM_ONLY); + SetupMode("Planetary", MSI_MODE_PLANETARY, RANDOM_ONLY); + SetupMode("Double meteor", MSI_MODE_DOUBLE_METEOR, RANDOM_ONLY); + SetupMode("Energy", MSI_MODE_ENERGY, RANDOM_ONLY); + SetupMode("Blink", MSI_MODE_BLINK, COMMON); + SetupMode("Clock", MSI_MODE_CLOCK, RANDOM_ONLY); + SetupMode("Color pulse", MSI_MODE_COLOR_PULSE, COMMON); + SetupMode("Color shift", MSI_MODE_COLOR_SHIFT, RANDOM_ONLY); + SetupMode("Color wave", MSI_MODE_COLOR_WAVE, COMMON); + SetupMode("Marquee", MSI_MODE_MARQUEE, PER_LED_ONLY); + // SetupMode("Rainbow", MSI_MODE_RAINBOW, COMMON); + SetupMode("Rainbow wave", MSI_MODE_RAINBOW_WAVE, RANDOM_ONLY); + SetupMode("Visor", MSI_MODE_VISOR, COMMON); + // SetupMode("JRainbow", MSI_MODE_JRAINBOW, COMMON); + SetupMode("Rainbow flashing", MSI_MODE_RAINBOW_FLASHING, RANDOM_ONLY); + // SetupMode("Rainbow double flashing", MSI_MODE_RAINBOW_DOUBLE_FLASHING, COMMON); + // SetupMode("Random", MSI_MODE_RANDOM, COMMON); + // SetupMode("Fan control", MSI_MODE_FAN_CONTROL, COMMON); + // SetupMode("Off 2", MSI_MODE_DISABLE_2, COMMON); + // SetupMode("Color ring flashing", MSI_MODE_COLOR_RING_FLASHING, COMMON); + SetupMode("Color ring double flashing", MSI_MODE_COLOR_RING_DOUBLE_FLASHING, RANDOM_ONLY); + SetupMode("Stack", MSI_MODE_STACK, COMMON); + // SetupMode("Corsair Que", MSI_MODE_CORSAIR_QUE, COMMON); + SetupMode("Fire", MSI_MODE_FIRE, RANDOM_ONLY); + // SetupMode("Lava", MSI_MODE_LAVA, COMMON); +} + +void RGBController_MSIMysticLight185::UpdateLed + ( + int zone, + int led + ) +{ + unsigned char red = RGBGetRValue(zones[zone].colors[led]); + unsigned char grn = RGBGetGValue(zones[zone].colors[led]); + unsigned char blu = RGBGetBValue(zones[zone].colors[led]); + + if(controller->IsDirectModeActive()) + { + controller->SetLedColor((MSI_ZONE)(zones[zone].leds[led].value), led, red, grn, blu); + } + else + { + if(led == 0) + { + bool random = modes[active_mode].color_mode == MODE_COLORS_RANDOM; + MSI_MODE mode = (MSI_MODE)modes[active_mode].value; + MSI_SPEED speed = (MSI_SPEED)modes[active_mode].speed; + MSI_BRIGHTNESS brightness = (MSI_BRIGHTNESS)modes[active_mode].brightness; + + controller->SetMode((MSI_ZONE)zones[zone].leds[led].value, mode, speed, brightness, random); + controller->SetZoneColor((MSI_ZONE)zones[zone].leds[led].value, red, grn, blu, red, grn, blu); + } + } +} + +void RGBController_MSIMysticLight185::SetupMode + ( + const char *name, + MSI_MODE mod, + unsigned int flags + ) +{ + mode Mode; + Mode.name = name; + Mode.value = mod; + Mode.flags = flags; + + if(flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + Mode.color_mode = MODE_COLORS_PER_LED; + } + else + { + Mode.color_mode = MODE_COLORS_RANDOM; + } + + if(flags & MODE_FLAG_HAS_SPEED) + { + Mode.speed = MSI_SPEED_MEDIUM; + Mode.speed_max = MSI_SPEED_HIGH; + Mode.speed_min = MSI_SPEED_LOW; + } + else + { + /*---------------------------------------------------------*\ + | For modes without speed this needs to be set to avoid | + | bad values in the saved profile which in turn corrupts | + | the brightness calculation when loading the profile | + \*---------------------------------------------------------*/ + Mode.speed = 0; + Mode.speed_max = 0; + Mode.speed_min = 0; + } + + if(flags & MODE_FLAG_HAS_BRIGHTNESS) + { + Mode.brightness = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_max = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_min = MSI_BRIGHTNESS_OFF; + } + else + { + Mode.brightness = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_max = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_min = MSI_BRIGHTNESS_LEVEL_100; + } + + modes.push_back(Mode); +} + +void RGBController_MSIMysticLight185::GetDeviceConfig() +{ + if(controller->GetMode() != MSI_MODE_DIRECT_DUMMY) + { + MSI_MODE mode; + MSI_SPEED speed; + MSI_BRIGHTNESS brightness; + bool rainbow; + unsigned int color; + + for(size_t i = 0; i < zone_description.size(); ++i) + { + controller->GetMode(zone_description[i]->zone_type, mode, speed, brightness, rainbow, color); + + if(zones[i].colors != nullptr) + { + for(size_t j = 0; j < GetLEDsInZone((unsigned int)i); ++j) + { + zones[i].colors[j] = color; + } + } + } + + controller->GetMode(zone_description[0]->zone_type, mode, speed, brightness, rainbow, color); + + for(size_t i = 0; i < modes.size(); ++i) + { + if(mode == modes[i].value) + { + if(modes[i].flags & MODE_FLAG_HAS_SPEED) + { + modes[i].speed = speed; + } + if(modes[i].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + modes[i].brightness = brightness; + } + if(rainbow) + { + if(modes[i].flags & (MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR)) + { + if(rainbow) + { + modes[i].color_mode = MODE_COLORS_RANDOM; + } + else + { + modes[i].color_mode = MODE_COLORS_PER_LED; + } + } + } + break; + } + } + } +} diff --git a/Controllers/MSIMysticLightController/MSIMysticLight185Controller/RGBController_MSIMysticLight185.h b/Controllers/MSIMysticLightController/MSIMysticLight185Controller/RGBController_MSIMysticLight185.h new file mode 100644 index 0000000..3a2c754 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight185Controller/RGBController_MSIMysticLight185.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight185.h | +| | +| RGBController for MSI Mystic Light 185-byte motherboard | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "MSIMysticLight185Controller.h" + +class RGBController_MSIMysticLight185: public RGBController +{ +public: + RGBController_MSIMysticLight185(MSIMysticLight185Controller* controller_ptr); + ~RGBController_MSIMysticLight185(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + void SetupModes(); + void UpdateLed + ( + int zone, + int led + ); + void SetupMode + ( + const char *name, + MSI_MODE mode, + unsigned int flags + ); + int GetDeviceMode(); + void GetDeviceConfig(); + + MSIMysticLight185Controller* controller; + MSI_ZONE last_resizable_zone; +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLight64Controller/MSIMysticLight64Controller.cpp b/Controllers/MSIMysticLightController/MSIMysticLight64Controller/MSIMysticLight64Controller.cpp new file mode 100644 index 0000000..49c792b --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight64Controller/MSIMysticLight64Controller.cpp @@ -0,0 +1,115 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight64Controller.cpp | +| | +| Driver for MSI Mystic Light 64-byte motherboard | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| Elchanan Haas 23 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "MSIMysticLight64Controller.h" +#include "StringUtils.h" + +MSIMysticLight64Controller::MSIMysticLight64Controller +( + hid_device *handle, + const char *path +) +{ + dev = handle; + if(dev) + { + location = path; + } +} + +MSIMysticLight64Controller::~MSIMysticLight64Controller() +{ + hid_close(dev); +} + +void MSIMysticLight64Controller::SetMode +( + MSI_64_MODE mode, + MSI_SPEED speed, + MSI_BRIGHTNESS brightness, + unsigned int num_colors, + Color colors[] +) +{ + FeaturePacket_64 data; + for(int i = 0; i < MSI_64_MAX_COLORS; i++) + { + data.colors[i] = colors[i]; + } + data.speed = speed; + data.brightness = brightness; + data.num_colors = num_colors; + data.mode = mode; + /*-----------------------------------------------------*\ + | Send packet to hardware, return true if successful | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, (unsigned char *)&data, sizeof(data)); + return; +} + +std::string MSIMysticLight64Controller::GetDeviceName() +{ + wchar_t tname[256]; + + /*-----------------------------------------------------*\ + | Get the manufacturer string from HID | + \*-----------------------------------------------------*/ + hid_get_manufacturer_string(dev, tname, 256); + + /*-----------------------------------------------------*\ + | Convert to std::string | + \*-----------------------------------------------------*/ + std::string name = StringUtils::wstring_to_string(tname); + + /*-----------------------------------------------------*\ + | Get the product string from HID | + \*-----------------------------------------------------*/ + hid_get_product_string(dev, tname, 256); + + /*-----------------------------------------------------*\ + | Append the product string to the manufacturer string | + \*-----------------------------------------------------*/ + name.append(" ").append(StringUtils::wstring_to_string(tname)); + + return(name); +} + +std::string MSIMysticLight64Controller::GetFWVersion() +{ + /*-----------------------------------------------------*\ + | This device doesn't support firmware version | + \*-----------------------------------------------------*/ + std::string firmware_version = ""; + return firmware_version; +} + +std::string MSIMysticLight64Controller::GetDeviceLocation() +{ + return ("HID: " + location); +} + +std::string MSIMysticLight64Controller::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} diff --git a/Controllers/MSIMysticLightController/MSIMysticLight64Controller/MSIMysticLight64Controller.h b/Controllers/MSIMysticLightController/MSIMysticLight64Controller/MSIMysticLight64Controller.h new file mode 100644 index 0000000..3c83bfd --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight64Controller/MSIMysticLight64Controller.h @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight64Controller.h | +| | +| Driver for MSI Mystic Light 64-byte motherboard | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| Elchanan Haas 23 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "MSIMysticLightCommon.h" +#include "RGBController.h" + +enum MSI_64_MODE +{ + MSI_64_OFF = 0, + MSI_64_STEADY = 1, + MSI_64_BREATHING = 2, + MSI_64_PULSE = 3, + MSI_64_DOUBLE_PULSE = 4, + MSI_64_CYCLE = 5, + MSI_64_SMOOTH_CYCLE = 6, +}; + +class MSIMysticLight64Controller +{ +public: + MSIMysticLight64Controller + ( + hid_device* handle, + const char *path + ); + ~MSIMysticLight64Controller(); + + void SetMode + ( + MSI_64_MODE mode, + MSI_SPEED speed, + MSI_BRIGHTNESS brightness, + unsigned int num_colors, + Color colors[] + ); + + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + std::string GetFWVersion(); + std::string GetSerial(); + +private: + + hid_device* dev; + std::string location; +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLight64Controller/RGBController_MSIMysticLight64.cpp b/Controllers/MSIMysticLightController/MSIMysticLight64Controller/RGBController_MSIMysticLight64.cpp new file mode 100644 index 0000000..7fbdbd9 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight64Controller/RGBController_MSIMysticLight64.cpp @@ -0,0 +1,164 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight64.cpp | +| | +| RGBController for MSI Mystic Light 64-byte motherboard | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| Elchanan Haas 23 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MSIMysticLight64.h" + +/**------------------------------------------------------------------*\ + @name MSI GL66 Mystic Light Keyboard (64 Byte) + @category Keyboard + @type USB + @save :robot: + @effects :white_check_mark: + @detectors DetectMSIMysticLight64Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSIMysticLight64::RGBController_MSIMysticLight64 +( + MSIMysticLight64Controller *controller_ptr +) +{ + controller = controller_ptr; + name = controller->GetDeviceName(); + vendor = "MSI"; + type = DEVICE_TYPE_KEYBOARD; + description = "MSI Mystic Light Device (64-byte)"; + version = controller->GetFWVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + SetupZones(); +} + +RGBController_MSIMysticLight64::~RGBController_MSIMysticLight64() +{ + delete controller; +} + +void RGBController_MSIMysticLight64::ResizeZone +( + int /*zone*/, + int /*new_size*/ +) +{ +} + +void RGBController_MSIMysticLight64::SetupZones() +{ + zone msi_zone; + msi_zone.name = "MSI Zone"; + msi_zone.type = ZONE_TYPE_SINGLE; + msi_zone.leds_min = 1; + msi_zone.leds_max = 1; + msi_zone.leds_count = 1; + msi_zone.matrix_map = NULL; + zones.push_back(msi_zone); + + led msi_led; + msi_led.name = "MSI LED"; + leds.push_back(msi_led); + SetupModes(); + SetupColors(); +} + +void RGBController_MSIMysticLight64::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIMysticLight64::DeviceUpdateLEDs() +{ + mode &Mode = modes[active_mode]; + MSI_64_MODE msi_mode = (MSI_64_MODE)Mode.value; + MSI_SPEED speed = (MSI_SPEED)Mode.speed; + MSI_BRIGHTNESS brightness = (MSI_BRIGHTNESS)(Mode.brightness); + Color led_colors[MSI_64_MAX_COLORS] = {}; + unsigned int num_colors = 0; + if(Mode.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + num_colors = (unsigned int)Mode.colors.size(); + for(unsigned int i = 0; i < num_colors; i++) + { + led_colors[i].R = RGBGetRValue(Mode.colors[i]); + led_colors[i].G = RGBGetGValue(Mode.colors[i]); + led_colors[i].B = RGBGetBValue(Mode.colors[i]); + } + } + controller->SetMode(msi_mode, speed, brightness, num_colors, led_colors); +} + +void RGBController_MSIMysticLight64::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIMysticLight64::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIMysticLight64::SetupModes() +{ + unsigned int TRANSITION=MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + SetupMode("Off", MSI_64_MODE::MSI_64_OFF, 0); + SetupMode("Static", MSI_64_MODE::MSI_64_STEADY, MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR); + SetupMode("Breathing", MSI_64_MODE::MSI_64_BREATHING, TRANSITION); + SetupMode("Flashing", MSI_64_MODE::MSI_64_PULSE, TRANSITION); + SetupMode("Double Flashing", MSI_64_MODE::MSI_64_DOUBLE_PULSE, TRANSITION); + SetupMode("Spectrum Cycle", MSI_64_MODE::MSI_64_CYCLE, TRANSITION); + SetupMode("Smooth Spectrum Cycle", MSI_64_MODE::MSI_64_SMOOTH_CYCLE, TRANSITION); +} +void RGBController_MSIMysticLight64::SetupMode +( + const char *name, + MSI_64_MODE mod, + unsigned int flags +) +{ + mode Mode; + Mode.name = name; + Mode.value = mod; + Mode.flags = flags; + if(flags & MODE_FLAG_HAS_BRIGHTNESS) + { + Mode.brightness_min = MSI_BRIGHTNESS_LEVEL_10; + Mode.brightness_max = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness = MSI_BRIGHTNESS_LEVEL_100; + } + if(flags & MODE_FLAG_HAS_SPEED) + { + Mode.speed_min = MSI_SPEED_LOW; + Mode.speed_max = MSI_SPEED_HIGH; + Mode.speed = MSI_SPEED_LOW; + } + if(flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + Mode.color_mode= MODE_COLORS_MODE_SPECIFIC; + Mode.colors_min = 1; + Mode.colors_max = 1; + if (flags & MODE_FLAG_HAS_SPEED) + { + Mode.colors_max = MSI_64_MAX_COLORS; + } + /*-------------------------------------------------*\ + | Set up colors for rainbow cycle | + \*-------------------------------------------------*/ + Mode.colors.push_back(0x000000FF); + Mode.colors.push_back(0x000050FF); + Mode.colors.push_back(0x0000FFFF); + Mode.colors.push_back(0x0000FF00); + Mode.colors.push_back(0x00FF0000); + Mode.colors.push_back(0x00FF0096); + Mode.colors.push_back(0x00FF00FF); + } + modes.push_back(Mode); +} diff --git a/Controllers/MSIMysticLightController/MSIMysticLight64Controller/RGBController_MSIMysticLight64.h b/Controllers/MSIMysticLightController/MSIMysticLight64Controller/RGBController_MSIMysticLight64.h new file mode 100644 index 0000000..fc5f309 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight64Controller/RGBController_MSIMysticLight64.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight64.h | +| | +| RGBController for MSI Mystic Light 64-byte motherboard | +| | +| T-bond 03 Apr 2020 | +| Adam Honse 06 Mar 2021 | +| Elchanan Haas 23 Aug 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "MSIMysticLight64Controller.h" + +class RGBController_MSIMysticLight64 : public RGBController +{ +public: + RGBController_MSIMysticLight64(MSIMysticLight64Controller* controller_ptr); + ~RGBController_MSIMysticLight64(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + MSIMysticLight64Controller* controller; + void SetupModes(); + void SetupMode + ( + const char *name, + MSI_64_MODE mode, + unsigned int flags + ); +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLight761Controller/MSIMysticLight761Controller.cpp b/Controllers/MSIMysticLightController/MSIMysticLight761Controller/MSIMysticLight761Controller.cpp new file mode 100644 index 0000000..d42d33e --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight761Controller/MSIMysticLight761Controller.cpp @@ -0,0 +1,633 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight761Controller.cpp | +| | +| Driver for MSI Mystic Light 761-byte motherboard | +| | +| Direct mode functionality has been implemented based on | +| the SignalRGB project | +| (https://signalrgb.com/) | +| | +| rom4ster 11 Jun 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "MSIMysticLight761Controller.h" +#include "StringUtils.h" + +#define NUM_CONFS sizeof(board_configs) / sizeof(mystic_light_761_config) +#define COLOR_BLACK {0, 0, 0} +#define IS_JARGB(X) (X == MSI_ZONE_JARGB_1 || X == MSI_ZONE_JARGB_2 || X == MSI_ZONE_JARGB_3) +#define GET_CHAR_PTR_REF(X) (unsigned char *) &(X) +#define ARRAY_ROW(X, Y) get_zone_setup_index(X)*16 + Y +#define ARRAY_ROW_RAW(X, Y) X*16 + Y +#define SETUP_ARRAY_SIZE 290 + +struct mystic_light_761_config +{ + const std::string * name; // Name of the board + int numof_onboard_leds; // number of onboard leds + int numof_pipe1_leds; // number of pipe 1 leds (used in per LED mode only) + int numof_pipe2_leds; // number of pipe 2 leds (used in per LED mode only) + int numof_JRGBs; // number of supported JRGB headers (used in per LED mode only) + const std::vector* supported_zones; // pointer to vector of supported zones + MSIMysticLight761Controller::DIRECT_MODE per_led_mode; // type of direct mode support +}; + +static const std::vector zone_set1 = +{ + MSI_ZONE_JAF, + MSI_ZONE_JARGB_1, + MSI_ZONE_JARGB_2, + MSI_ZONE_JARGB_3, +}; + +static const std::string board_names[] = +{ + "MSI MAG X870 TOMAHAWK WIFI (MS-7E51)", + "MSI MAG B850M MORTAR WIFI (MS-7E61)", + "MSI MPG B850I EDGE TI WIFI (MS-7E79)", + "MSI X870 GAMING PLUS WIFI (MS-7E47)", + "MSI B850 GAMING PLUS WIFI6E (MS-7E80)", + "MSI B850M GAMING PLUS WIFI6E (MS-7E81)", + "MSI MPG X870E CARBON WIFI (MS-7E49)", + "MSI Z890 GAMING PLUS WIFI (MS-7E34)", + "MSI X870E GAMING PLUS WIFI (MS-7E70)", + "MSI MAG X870E TOMAHAWK WIFI (MS-7E59)", + "MSI PRO B850-P WIFI (MS-7E56)", + "MSI B850M GAMING PLUS WIFI (MS-7E66)", + "MSI PRO X870E-P WIFI (MS-7E70)", + "MSI MPG X870I EDGE TI EVO WIFI (MS-7E50)", + "MSI B850 GAMING PLUS WIFI (MS-7E56)", + "MSI PRO X870-P WIFI (MS-7E47)", + "MSI MPG X870E EDGE TI WIFI (MS-7E59)", + "MSI MAG B850 TOMAHAWK MAX WIFI (MS-7E62)", + "MSI PRO B850M-P WIFI (MS-7E71)", + "MSI MAG Z890 TOMAHAWK WIFI (MS-7E32)", + "MSI MPG B850 EDGE TI WIFI (MS-7E62)", + "MSI PRO B850M-VC WIFI6E (MS-7E71)", + "MSI MAG B850 TOMAHAWK WIFI (MS-7E53)", + "MSI MEG Z890 UNIFY-X (MS-7E20)", + "MSI PRO X870E-S EVO WIFI (MS-7E86)", + "MSI PRO Z890-P WIFI (MS-7E34)", +}; + +static const mystic_light_761_config board_configs[] = +{ + { &(board_names[0]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI X870 TOMAHAWK WIFI + { &(board_names[1]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG B850M MORTAR WIFI + { &(board_names[2]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG B850I EDGE TI WIFI + { &(board_names[3]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI X870 GAMING PLUS WIFI + { &(board_names[4]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850 GAMING PLUS WIFI6E + { &(board_names[5]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850M GAMING PLUS WIFI6E + { &(board_names[6]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG X870E CARBON WIFI + { &(board_names[7]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI Z890 GAMING PLUS WIFI + { &(board_names[8]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI X870E GAMING PLUS WIFI + { &(board_names[9]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG X870E TOMAHAWK WIFI + { &(board_names[10]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850-P WIFI (MS-7E56) + { &(board_names[11]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850M GAMING PLUS WIFI + { &(board_names[12]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO X870E-P WIFI + { &(board_names[13]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG X870I EDGE TI EVO WIFI (MS-7E50) + { &(board_names[14]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI B850 GAMING PLUS WIFI (MS-7E56) + { &(board_names[15]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO X870-P WIFI + { &(board_names[16]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG X870E EDGE TI WIFI + { &(board_names[17]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG B850 TOMAHAWK MAX WIFI + { &(board_names[18]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850M-P WIFI (MS-7E71) + { &(board_names[19]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG Z890 TOMAHAWK WIFI (MS-7E32) + { &(board_names[20]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MPG B850 EDGE TI WIFI (MS-7E62) + { &(board_names[21]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO B850M-VC WIFI6E (MS-7E71) + { &(board_names[22]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MAG B850 TOMAHAWK WIFI (MS-7E53) + { &(board_names[23]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI MEG Z890 UNIFY-X (MS-7E20) + { &(board_names[24]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO X870E-S EVO WIFI (MS-7E86) + { &(board_names[25]), 0, 0, 0, 1, &zone_set1, MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED }, // MSI PRO Z890-P WIFI (MS-7E34) +}; + +enum MSI_ZONE setup_map [] = + { + MSI_ZONE_JARGB_1, + MSI_ZONE_JARGB_2, + MSI_ZONE_JARGB_3, + MSI_ZONE_JAF, + MSI_ZONE_J_PIPE_1, + MSI_ZONE_J_PIPE_2, + MSI_ZONE_J_PIPE_3, + MSI_ZONE_J_PIPE_4, + MSI_ZONE_J_PIPE_5, + MSI_ZONE_J_RGB_1, + MSI_ZONE_J_RGB_2, + MSI_ZONE_ON_BOARD_LED_0, + MSI_ZONE_ON_BOARD_LED_1, + MSI_ZONE_ON_BOARD_LED_2, + MSI_ZONE_ON_BOARD_LED_3, + MSI_ZONE_ON_BOARD_LED_4, + MSI_ZONE_ON_BOARD_LED_5, + +}; + +int get_zone_setup_index(MSI_ZONE index) +{ + int size = sizeof(setup_map) / sizeof(setup_map[0]); + for(int i = 0; i < size; i++) + { + if(setup_map[i] == index) + { + return i; + } + } + return -1; +} + +// Copying from signal plugin +// DO NOT MODIFY THIS ARRAY DIRECTLY, MAKE COPY +unsigned char initial_setup_array[] = +{ + 0x50, + 0x09, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x15, 0x78, //JARGB 1 + 0x09, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x15, 0x78, //JARGB 2 + 0x09, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x15, 0x78, //JARGB 3 + 0x09, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x15, 0x78, //JAF //Fans go here with the weird connector + 0x09, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x95, 0x1E, //JPIPE1 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x1E, //JPIPE2 //95 for active zones 94 for inactive + 0x09, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x95, 0x1E, //JPIPE3 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x1E, //JPIPE4 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x1E, //JPIPE5 + 0x09, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x95, 0x1E, //JRGB1 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x1E, //JRGB2 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x1E, //Onboard1 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x1E, //Onboard2 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x1E, //Onboard3 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x1E, //Onboard4 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x1E, //Onboard5 + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x1E, //Onboard6 + 0x09, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x95, 0x1E, //Select all? + 0x00 +}; + +unsigned char * initializer_array() +{ + unsigned char * arr = (unsigned char *) malloc(SETUP_ARRAY_SIZE); + for(int i = 0; i < SETUP_ARRAY_SIZE; i++) + { + arr[i] = initial_setup_array[i]; + } + return arr; +} + +void init_packet(FeaturePacket_Zone_761 * packet) +{ + packet->packet.fixed1 = 0x09; + packet->packet.fixed2 = 0x00; + packet->packet.fixed3 = 0x00; + packet->packet.hdr2 = 240; + + for(int i = 0; i < NUM_LEDS_761; i++) + { + packet->packet.colors[i] = 0x0; + } + +} + +MSIMysticLight761Controller::MSIMysticLight761Controller + ( + hid_device* handle, + const char* path, + std::string dev_name + ) +{ + dev = handle; + location = path; + name = dev_name; + + const mystic_light_761_config * board_config = nullptr; + for(std::size_t i = 0; i < NUM_CONFS; i++) + { + if(*(board_configs[i].name) == name) + { + board_config = &board_configs[i]; + break; + } + } + + if(board_config != nullptr) + { + supported_zones = (std::vector*) board_config->supported_zones; + unsigned int max = 0; + + for(std::size_t i = 0; i < board_config->supported_zones[0].size(); i++) + { + unsigned int curr_val = (unsigned int) (board_config->supported_zones[0][i]); + + if(curr_val > max) + { + max = curr_val; + } + } + + // Need to send configuration to board + unsigned char * conf_arr = initializer_array(); + + // First set everything off + for(int i = 4; i < 17; i++) + { + conf_arr[ARRAY_ROW_RAW(i, 1)] = 0x00; + conf_arr[ARRAY_ROW_RAW(i, 2)] = 0x00; + conf_arr[ARRAY_ROW_RAW(i, 6)] = 0x00; + conf_arr[ARRAY_ROW_RAW(i, 10)] = 0x00; + conf_arr[ARRAY_ROW_RAW(i, 11)] = 0x00; + conf_arr[ARRAY_ROW_RAW(i, 12)] = 0x00; + conf_arr[ARRAY_ROW_RAW(i, 13)] = 0x00; + conf_arr[ARRAY_ROW_RAW(i, 14)] = 0x00; + conf_arr[ARRAY_ROW_RAW(i, 15)] = 0x00; + } + + for(std::size_t i = 0; i < supported_zones->size(); i++) + { + MSI_ZONE supp_zone = (*supported_zones)[i]; + ZoneConfig conf; + conf.msi_zone = supp_zone; + // Turn on relevant zones (0-3 are always active) + if(get_zone_setup_index(supp_zone) > 3) + { + conf_arr[ARRAY_ROW(supp_zone,1 )] = 0x09; + conf_arr[ARRAY_ROW(supp_zone,2 )] = 0xFF; + conf_arr[ARRAY_ROW(supp_zone,6 )] = 0xFF; + conf_arr[ARRAY_ROW(supp_zone,10)] = 0xFF; + conf_arr[ARRAY_ROW(supp_zone,11)] = 0xFF; + conf_arr[ARRAY_ROW(supp_zone,12)] = 0xFF; + conf_arr[ARRAY_ROW(supp_zone,13)] = 0xFF; + conf_arr[ARRAY_ROW(supp_zone,14)] = 0x03; + conf_arr[ARRAY_ROW(supp_zone,15)] = 0x95; + } + ZoneData * dat = new ZoneData; + conf.zone_data = dat; + zone_configs.push_back(conf); + } + + if(dev) + { + location = path; + + ReadName(); + ReadFwVersion(); + ReadSettings(); + + // Push config so setting colors works + int res = hid_send_feature_report(dev, conf_arr, SETUP_ARRAY_SIZE); + LOG_INFO("Sending configuration resulted in %i\n", res); + + data = new FeaturePacket_761; + + data->jaf.zone = MSI_ZONE_JAF; + data->jargb1.zone = MSI_ZONE_JARGB_1; + data->jargb2.zone = MSI_ZONE_JARGB_2; + data->jargb3.zone = MSI_ZONE_JARGB_3; + + data->jaf.packet.hdr0 = 0x08; + data->jargb1.packet.hdr0 = 0x04; + data->jargb2.packet.hdr0 = 0x04; + data->jargb3.packet.hdr0 = 0x04; + + data->jaf.packet.hdr1 = 0x00; + data->jargb1.packet.hdr1 = 0x00; + data->jargb2.packet.hdr1 = 0x01; + data->jargb3.packet.hdr1 = 0x02; + + init_packet(&data->jaf); + init_packet(&data->jargb1); + init_packet(&data->jargb2); + init_packet(&data->jargb3); + } + + free(conf_arr); + } + else + { + throw std::runtime_error(BOARD_UNSUPPORTED_ERROR); + } + +} + +MSIMysticLight761Controller::~MSIMysticLight761Controller() +{ + hid_close(dev); + + if(data) + { + delete data; + data = nullptr; + } + + for(ZoneConfig& zone : zone_configs) + { + if(zone.zone_data) + { + delete zone.zone_data; + zone.zone_data = nullptr; + } + } + zone_configs.clear(); +} + +void MSIMysticLight761Controller::SetMode + ( + MSI_ZONE /*zone*/, + MSI_MODE /*mode*/, + MSI_SPEED /*speed*/, + MSI_BRIGHTNESS /*brightness*/, + bool /*rainbow_color*/ + ) +{ + return; // Only supporting direct for now +} + +std::string MSIMysticLight761Controller::GetDeviceName() +{ + return name; +} + +std::string MSIMysticLight761Controller::GetFWVersion() +{ + return std::string("AP/LD ").append(version_aprom).append(" / ").append(version_ldrom); +} + +std::string MSIMysticLight761Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MSIMysticLight761Controller::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +bool MSIMysticLight761Controller::ReadSettings() +{ + /*-----------------------------------------------------*\ + | Read packet from hardware, return true if successful | + \*-----------------------------------------------------*/ + unsigned char buffer [500]; + buffer[0] = 0x51; + return (hid_get_feature_report(dev, buffer, 500)) > 0 ; +} + +bool MSIMysticLight761Controller::Update + ( + bool /*save*/ + ) +{ + int ret = 0; + bool flag = true; + ret = hid_send_feature_report(dev, GET_CHAR_PTR_REF(data->jaf.packet) , sizeof(FeaturePacket_PerLED_761)); + if(ret < 0) + { + flag = false; + } + ret = hid_send_feature_report(dev, GET_CHAR_PTR_REF(data->jargb1.packet) , sizeof(FeaturePacket_PerLED_761)); + if(ret < 0) + { + flag = false; + } + ret = hid_send_feature_report(dev, GET_CHAR_PTR_REF(data->jargb2.packet) , sizeof(FeaturePacket_PerLED_761)); + if(ret < 0) + { + flag = false; + } + ret = hid_send_feature_report(dev, GET_CHAR_PTR_REF(data->jargb3.packet) , sizeof(FeaturePacket_PerLED_761)); + if(ret < 0) + { + flag = false; + } + + return flag; +} + +void MSIMysticLight761Controller::SetZoneColor + ( + MSI_ZONE zone, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2 + ) +{ + for(std::size_t i = 0; i < zone_configs.size(); i++) + { + if(zone_configs[i].msi_zone == zone) + { + zone_configs[i].zone_data->color.R = red1; + zone_configs[i].zone_data->color.G = grn1; + zone_configs[i].zone_data->color.B = blu1; + zone_configs[i].zone_data->color2.R = red2; + zone_configs[i].zone_data->color2.G = grn2; + zone_configs[i].zone_data->color2.B = blu2; + } + } +} + +void set_data_color(FeaturePacket_Zone_761 * packet, std::size_t index, unsigned char color_val ) +{ + if(packet == nullptr) + { + return; + } + packet->packet.colors[index] = color_val; +} + +void MSIMysticLight761Controller::SetLedColor + ( + MSI_ZONE zone, + std::size_t index, + unsigned char red, + unsigned char grn, + unsigned char blu + ) +{ + FeaturePacket_Zone_761 * ptr = nullptr; + switch(zone) + { + case MSI_ZONE_JAF: + ptr = &data->jaf; + break; + case MSI_ZONE_JARGB_1: + ptr = &data->jargb1; + break; + case MSI_ZONE_JARGB_2: + ptr = &data->jargb2; + break; + case MSI_ZONE_JARGB_3: + ptr = &data->jargb3; + break; + default: + break; + } + + std::size_t candidate_index = (index * 3); + + if((candidate_index + 2) <= GetMaxDirectLeds(zone)) + { + set_data_color(ptr, candidate_index, red); + set_data_color(ptr, candidate_index + 1, grn); + set_data_color(ptr, candidate_index + 2, blu); + } +} + +ZoneData *MSIMysticLight761Controller::GetZoneData + ( + FeaturePacket_761& /*data_packet*/, + MSI_ZONE zone + ) +{ + for(std::size_t i = 0; i < zone_configs.size(); i++) + { + if(zone_configs[i].msi_zone == zone) + { + return zone_configs[i].zone_data; + } + } + + return nullptr; +} + +Color *MSIMysticLight761Controller::GetPerLedZoneData + ( + MSI_ZONE zone + ) +{ + return &(GetZoneData(*data, zone)->color); +} + +RainbowZoneData *MSIMysticLight761Controller::GetRainbowZoneData + ( + MSI_ZONE /*zone*/ + ) +{ + return nullptr; +} + +bool MSIMysticLight761Controller::ReadFwVersion() +{ + return true; +} +void MSIMysticLight761Controller::ReadName() +{ + wchar_t tname[256]; + + /*-----------------------------------------------------*\ + | Get the manufacturer string from HID | + \*-----------------------------------------------------*/ + hid_get_manufacturer_string(dev, tname, 256); + + /*-----------------------------------------------------*\ + | Convert to std::string | + \*-----------------------------------------------------*/ + name = StringUtils::wstring_to_string(tname); + + /*-----------------------------------------------------*\ + | Get the product string from HID | + \*-----------------------------------------------------*/ + hid_get_product_string(dev, tname, 256); + + /*-----------------------------------------------------*\ + | Append the product string to the manufacturer string | + \*-----------------------------------------------------*/ + name.append(" ").append(StringUtils::wstring_to_string(tname)); +} + +MSI_MODE MSIMysticLight761Controller::GetMode() +{ + return MSI_MODE_DIRECT_DUMMY; +} + +void MSIMysticLight761Controller::GetMode + ( + MSI_ZONE zone, + MSI_MODE & mode, + MSI_SPEED & speed, + MSI_BRIGHTNESS & brightness, + bool & rainbow_color, + unsigned int & color + ) +{ + /*-----------------------------------------------------*\ + | Get data for given zone | + \*-----------------------------------------------------*/ + ZoneData *zone_data = GetZoneData(*data, zone); + + /*-----------------------------------------------------*\ + | Return if zone is invalid | + \*-----------------------------------------------------*/ + if(!zone_data) + { + return; + } + + /*-----------------------------------------------------*\ + | Update pointers with data | + \*-----------------------------------------------------*/ + + // Actual support of non direct modes needs to be further investigated + mode = (MSI_MODE)zone_data->effect; + speed = (MSI_SPEED)(zone_data->speedAndBrightnessFlags & 0x03); + brightness = (MSI_BRIGHTNESS)((zone_data->speedAndBrightnessFlags >> 2) & 0x1F); + rainbow_color = (zone_data->colorFlags & 0x80) == 0 ? true : false; + color = ToRGBColor(zone_data->color.R, zone_data->color.G, zone_data->color.B); +} + +void MSIMysticLight761Controller::SetCycleCount + ( + MSI_ZONE /*zone*/, + unsigned char /*cycle_num*/ + ) +{ + return; +} + +void MSIMysticLight761Controller::SetDirectMode + ( + bool /*mode*/ + ) +{ + SelectPerLedProtocol(); +} + +bool MSIMysticLight761Controller::IsDirectModeActive() +{ + return true; +} + +size_t MSIMysticLight761Controller::GetMaxDirectLeds + ( + MSI_ZONE zone + ) +{ + switch(zone) + { + case MSI_ZONE_JAF: + case MSI_ZONE_JARGB_1: + case MSI_ZONE_JARGB_2: + case MSI_ZONE_JARGB_3: + return 240; + break; + default: + return 1; + } +} + + +void MSIMysticLight761Controller::SelectPerLedProtocol() +{ + return; +} diff --git a/Controllers/MSIMysticLightController/MSIMysticLight761Controller/MSIMysticLight761Controller.h b/Controllers/MSIMysticLightController/MSIMysticLight761Controller/MSIMysticLight761Controller.h new file mode 100644 index 0000000..c12df42 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight761Controller/MSIMysticLight761Controller.h @@ -0,0 +1,155 @@ +/*---------------------------------------------------------*\ +| MSIMysticLight761Controller.h | +| | +| Driver for MSI Mystic Light 761-byte motherboard | +| | +| Direct mode functionality has been implemented based on | +| the SignalRGB project | +| (https://signalrgb.com/) | +| | +| rom4ster 11 Jun 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "MSIMysticLightCommon.h" +#include "RGBController.h" + +inline constexpr const char * BOARD_UNSUPPORTED_ERROR = "No Config Found For Board"; + +class MSIMysticLight761Controller +{ +public: + MSIMysticLight761Controller + ( + hid_device* handle, + const char* path, + std::string dev_name + ); + + ~MSIMysticLight761Controller(); + + void SetMode + ( + MSI_ZONE zone, + MSI_MODE mode, + MSI_SPEED speed, + MSI_BRIGHTNESS brightness, + bool rainbow_color + ); + + MSI_MODE GetMode(); + + void GetMode + ( + MSI_ZONE zone, + MSI_MODE &mode, + MSI_SPEED &speed, + MSI_BRIGHTNESS &brightness, + bool &rainbow_color, + unsigned int &color + ); + + void SetZoneColor + ( + MSI_ZONE zone, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2 + ); + + void SetLedColor + ( + MSI_ZONE zone, + std::size_t index, + unsigned char red, + unsigned char grn, + unsigned char blu + ); + + void SetCycleCount + ( + MSI_ZONE zone, + unsigned char cycle_num + ); + + bool Update + ( + bool save + ); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + std::string GetFWVersion(); + std::string GetSerial(); + + void SetDirectMode + ( + bool mode + ); + + bool IsDirectModeActive(); + + size_t GetMaxDirectLeds + ( + MSI_ZONE zone + ); + + const std::vector* + GetSupportedZones() + { + return supported_zones; + } + + enum DIRECT_MODE + { + DIRECT_MODE_DISABLED, + DIRECT_MODE_PER_LED, + DIRECT_MODE_ZONE_BASED + }; + + DIRECT_MODE GetSupportedDirectMode() + { + return DIRECT_MODE_PER_LED; + } + + struct ZoneConfig + { + MSI_ZONE msi_zone; + ZoneData * zone_data; + }; + +private: + std::string name; + std::vector zone_configs; + const std::vector* supported_zones; + hid_device* dev; + std::string location; + std::string version_aprom; + std::string version_ldrom; + FeaturePacket_761* data; + + bool ReadSettings(); + bool ReadFwVersion(); + void ReadName(); + + ZoneData* GetZoneData + ( + FeaturePacket_761& data_packet, + MSI_ZONE zone + ); + RainbowZoneData* GetRainbowZoneData(MSI_ZONE zone); + Color* GetPerLedZoneData + ( + MSI_ZONE zone + ); + void SelectPerLedProtocol(); +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLight761Controller/RGBController_MSIMysticLight761.cpp b/Controllers/MSIMysticLightController/MSIMysticLight761Controller/RGBController_MSIMysticLight761.cpp new file mode 100644 index 0000000..787c1ba --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight761Controller/RGBController_MSIMysticLight761.cpp @@ -0,0 +1,459 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight761.cpp | +| | +| RGBController for MSI Mystic Light 761-byte motherboard | +| | +| | +| rom4ster 11 Jun 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MSIMysticLight761.h" +#include "LogManager.h" + +struct ZoneDescription +{ + std::string name; + MSI_ZONE zone_type; +}; + +#define NUMOF_ZONES (sizeof(led_zones) / sizeof(ZoneDescription)) + +const ZoneDescription led_zones[] = +{ + ZoneDescription{ "JAF", MSI_ZONE_JAF }, + ZoneDescription{ "JARGB 1", MSI_ZONE_JARGB_1 }, + ZoneDescription{ "JARGB 2", MSI_ZONE_JARGB_2 }, + ZoneDescription{ "JARGB 3", MSI_ZONE_JARGB_3 }, +}; + +static std::vector zone_description; + +static int IndexOfZoneForType(MSI_ZONE zone_type) +{ + for(std::size_t i = 0; i < zone_description.size(); ++i) + { + if(zone_description[i]->zone_type == zone_type) + { + return (int) i; + } + } + + return -1; +} + +RGBController_MSIMysticLight761::RGBController_MSIMysticLight761 + ( + MSIMysticLight761Controller* controller_ptr + ) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "MSI"; + type = DEVICE_TYPE_MOTHERBOARD; + description = "MSI Mystic Light Device (761-byte)"; + version = controller->GetFWVersion(); + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + + const std::vector* supported_zones = controller->GetSupportedZones(); + + for(std::size_t i = 0; i < supported_zones->size(); ++i) + { + for(std::size_t j = 0; j < NUMOF_ZONES; ++j) + { + if(led_zones[j].zone_type == (*supported_zones)[i]) + { + zone_description.push_back(&led_zones[j]); + break; + } + } + } + + last_resizable_zone = MSI_ZONE_NONE; + SetupModes(); + SetupZones(); + active_mode = GetDeviceMode(); +} + +RGBController_MSIMysticLight761::~RGBController_MSIMysticLight761() +{ + zone_description.clear(); + delete controller; +} + +int RGBController_MSIMysticLight761::GetDeviceMode() +{ + MSI_MODE mode = controller->GetMode(); + + for(std::size_t i = 0; i < modes.size(); ++i) + { + if(mode == modes[i].value) + { + return (int)i; + } + } + + return 0; +} + +void RGBController_MSIMysticLight761::SetupZones() +{ + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + if(first_run) + { + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zone_description.size(); ++zone_idx) + { + const ZoneDescription* zd = zone_description[zone_idx]; + + zone new_zone; + + new_zone.name = zd->name; + new_zone.flags = 0; + + int maxLeds = (int)controller->GetMaxDirectLeds(zd->zone_type); + + /*-------------------------------------------------*\ + | This is a fixed size zone | + \*-------------------------------------------------*/ + if(((zd->zone_type != MSI_ZONE_J_RAINBOW_1) + && (zd->zone_type != MSI_ZONE_J_RAINBOW_2) + && (zd->zone_type != MSI_ZONE_J_RAINBOW_3) + && (zd->zone_type != MSI_ZONE_JAF) + && (zd->zone_type != MSI_ZONE_JARGB_1) + && (zd->zone_type != MSI_ZONE_JARGB_2) + && (zd->zone_type != MSI_ZONE_JARGB_3) + && (zd->zone_type != MSI_ZONE_J_CORSAIR))) + { + new_zone.leds_min = maxLeds; + new_zone.leds_max = maxLeds; + new_zone.leds_count = maxLeds; + } + /*--------------------------------------------------*\ + | This is a resizable zone on a board that does not | + | support per-LED direct mode | + \*--------------------------------------------------*/ + else if(controller->GetSupportedDirectMode() == MSIMysticLight761Controller::DIRECT_MODE_ZONE_BASED) + { + new_zone.leds_min = 0; + new_zone.leds_max = 30;//maxLeds; + new_zone.leds_count = 0; + last_resizable_zone = zd->zone_type; + new_zone.flags |= ZONE_FLAG_RESIZE_EFFECTS_ONLY; + } + /*--------------------------------------------------*\ + | This is a resizable zone on a board that does | + | support per-LED direct mode | + \*--------------------------------------------------*/ + else + { + new_zone.leds_min = 0; + new_zone.leds_max = maxLeds; + new_zone.leds_count = 0; + last_resizable_zone = zd->zone_type; + } + + /*-------------------------------------------------*\ + | Determine zone type based on max number of LEDs | + \*-------------------------------------------------*/ + if((new_zone.leds_max == 1) || (new_zone.flags & ZONE_FLAG_RESIZE_EFFECTS_ONLY)) + { + new_zone.type = ZONE_TYPE_SINGLE; + } + else + { + new_zone.type = ZONE_TYPE_LINEAR; + } + + new_zone.matrix_map = NULL; + + zones.push_back(new_zone); + } + } + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zone_description.size(); ++zone_idx) + { + controller->SetCycleCount(zone_description[zone_idx]->zone_type, zones[zone_idx].leds_count); + + if((zones[zone_idx].flags & ZONE_FLAG_RESIZE_EFFECTS_ONLY) == 0) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; ++led_idx) + { + led new_led; + + new_led.name = zones[zone_idx].name; + + if(zones[zone_idx].leds_count > 1) + { + new_led.name.append(" LED " + std::to_string(led_idx + 1)); + } + + new_led.value = zone_description[zone_idx]->zone_type; + leds.push_back(new_led); + } + } + else if(zones[zone_idx].leds_count > 0) + { + led new_led; + + new_led.name = zones[zone_idx].name; + + new_led.value = zone_description[zone_idx]->zone_type; + leds.push_back(new_led); + } + } + + SetupColors(); +} + + +void RGBController_MSIMysticLight761::ResizeZone + ( + int zone, + int new_size + ) +{ + if((std::size_t)zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + SetupZones(); + + if(zone_description[zone]->zone_type == last_resizable_zone) + { + GetDeviceConfig(); + last_resizable_zone = MSI_ZONE_NONE; + } + } +} + +void RGBController_MSIMysticLight761::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); ++zone_idx) + { + for(int led_idx = zones[zone_idx].leds_count - 1; led_idx >= 0; led_idx--) + { + UpdateLed((int)zone_idx, led_idx); + } + } + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight761::UpdateZoneLEDs(int zone) +{ + for(int led_idx = zones[zone].leds_count - 1; led_idx >= 0; led_idx--) + { + UpdateLed(zone, led_idx); + } + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight761::UpdateSingleLED + ( + int led + ) +{ + int zone_index = IndexOfZoneForType((MSI_ZONE)leds[led].value); + + if(zone_index == -1) + { + LOG_DEBUG("[%s]: could not find zone for type %d", controller->GetDeviceName().c_str(), leds[led].value); + return; + } + + int led_index = led - zones[zone_index].start_idx; + UpdateLed(zone_index, led_index); + controller->Update((modes[active_mode].flags & MODE_FLAG_AUTOMATIC_SAVE) != 0); +} + +void RGBController_MSIMysticLight761::DeviceUpdateMode() +{ + if(modes[active_mode].value == MSI_MODE_DIRECT_DUMMY) + { + controller->SetDirectMode(true); + } + else + { + controller->SetDirectMode(false); + DeviceUpdateLEDs(); + } +} + +void RGBController_MSIMysticLight761::DeviceSaveMode() +{ + controller->Update(true); +} + +void RGBController_MSIMysticLight761::SetupModes() +{ + if(controller->GetSupportedDirectMode() != MSIMysticLight761Controller::DIRECT_MODE_DISABLED) + { + SetupMode("Direct", MSI_MODE_DIRECT_DUMMY, MODE_FLAG_HAS_PER_LED_COLOR); + } +} + +void RGBController_MSIMysticLight761::UpdateLed + ( + int zone, + int led + ) +{ + unsigned char red = RGBGetRValue(zones[zone].colors[led]); + unsigned char grn = RGBGetGValue(zones[zone].colors[led]); + unsigned char blu = RGBGetBValue(zones[zone].colors[led]); + + if(controller->IsDirectModeActive()) + { + controller->SetLedColor((MSI_ZONE)(zones[zone].leds[led].value), led, red, grn, blu); + } + else + { + if(led == 0) + { + bool random = modes[active_mode].color_mode == MODE_COLORS_RANDOM; + MSI_MODE mode = (MSI_MODE)modes[active_mode].value; + MSI_SPEED speed = (MSI_SPEED)modes[active_mode].speed; + MSI_BRIGHTNESS brightness = (MSI_BRIGHTNESS)modes[active_mode].brightness; + + controller->SetMode((MSI_ZONE)zones[zone].leds[led].value, mode, speed, brightness, random); + controller->SetZoneColor((MSI_ZONE)zones[zone].leds[led].value, red, grn, blu, red, grn, blu); + } + } +} + +void RGBController_MSIMysticLight761::SetupMode + ( + const char *name, + MSI_MODE mod, + unsigned int flags + ) +{ + mode Mode; + Mode.name = name; + Mode.value = mod; + Mode.flags = flags; + + if(flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + Mode.color_mode = MODE_COLORS_PER_LED; + } + else + { + Mode.color_mode = MODE_COLORS_RANDOM; + } + + if(flags & MODE_FLAG_HAS_SPEED) + { + Mode.speed = MSI_SPEED_MEDIUM; + Mode.speed_max = MSI_SPEED_HIGH; + Mode.speed_min = MSI_SPEED_LOW; + } + else + { + /*---------------------------------------------------------*\ + | For modes without speed this needs to be set to avoid | + | bad values in the saved profile which in turn corrupts | + | the brightness calculation when loading the profile | + \*---------------------------------------------------------*/ + Mode.speed = 0; + Mode.speed_max = 0; + Mode.speed_min = 0; + } + + if(flags & MODE_FLAG_HAS_BRIGHTNESS) + { + Mode.brightness = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_max = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_min = MSI_BRIGHTNESS_OFF; + } + else + { + Mode.brightness = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_max = MSI_BRIGHTNESS_LEVEL_100; + Mode.brightness_min = MSI_BRIGHTNESS_LEVEL_100; + } + + modes.push_back(Mode); +} + +void RGBController_MSIMysticLight761::GetDeviceConfig() +{ + if(controller->GetMode() != MSI_MODE_DIRECT_DUMMY) + { + MSI_MODE mode; + MSI_SPEED speed; + MSI_BRIGHTNESS brightness; + bool rainbow; + unsigned int color; + + for(std::size_t i = 0; i < zone_description.size(); ++i) + { + controller->GetMode(zone_description[i]->zone_type, mode, speed, brightness, rainbow, color); + + if(zones[i].colors != nullptr) + { + for(size_t j = 0; j < GetLEDsInZone((unsigned int)i); ++j) + { + zones[i].colors[j] = color; + } + } + } + + controller->GetMode(zone_description[0]->zone_type, mode, speed, brightness, rainbow, color); + + for(std::size_t i = 0; i < modes.size(); ++i) + { + if(mode == modes[i].value) + { + if(modes[i].flags & MODE_FLAG_HAS_SPEED) + { + modes[i].speed = speed; + } + if(modes[i].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + modes[i].brightness = brightness; + } + if(rainbow) + { + if(modes[i].flags & (MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR)) + { + if(rainbow) + { + modes[i].color_mode = MODE_COLORS_RANDOM; + } + else + { + modes[i].color_mode = MODE_COLORS_PER_LED; + } + } + } + break; + } + } + } +} diff --git a/Controllers/MSIMysticLightController/MSIMysticLight761Controller/RGBController_MSIMysticLight761.h b/Controllers/MSIMysticLightController/MSIMysticLight761Controller/RGBController_MSIMysticLight761.h new file mode 100644 index 0000000..743ca3a --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLight761Controller/RGBController_MSIMysticLight761.h @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIMysticLight761.h | +| | +| RGBController for MSI Mystic Light 761-byte motherboard | +| | +| | +| rom4ster 11 Jun 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "MSIMysticLight761Controller.h" + +class RGBController_MSIMysticLight761: public RGBController +{ +public: + RGBController_MSIMysticLight761(MSIMysticLight761Controller* controller_ptr); + ~RGBController_MSIMysticLight761(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + void SetupModes(); + void UpdateLed + ( + int zone, + int led + ); + void SetupMode + ( + const char *name, + MSI_MODE mode, + unsigned int flags + ); + int GetDeviceMode(); + void GetDeviceConfig(); + + MSIMysticLight761Controller* controller; + MSI_ZONE last_resizable_zone; +}; diff --git a/Controllers/MSIMysticLightController/MSIMysticLightCommon.h b/Controllers/MSIMysticLightController/MSIMysticLightCommon.h new file mode 100644 index 0000000..130991f --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLightCommon.h @@ -0,0 +1,278 @@ +/*---------------------------------------------------------*\ +| MSIMysticLightCommon.h | +| | +| Common definitions for MSI Mystic Light motherboards | +| | +| Adam Honse 06 Mar 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +enum MSI_ZONE +{ + MSI_ZONE_NONE = 0, + MSI_ZONE_J_RGB_1 = 1, + MSI_ZONE_J_RGB_2 = 2, + MSI_ZONE_J_PIPE_1 = 3, + MSI_ZONE_J_PIPE_2 = 4, + MSI_ZONE_J_PIPE_3 = 5, + MSI_ZONE_J_PIPE_4 = 6, + MSI_ZONE_J_PIPE_5 = 7, + MSI_ZONE_J_RAINBOW_1 = 8, + MSI_ZONE_J_RAINBOW_2 = 9, + MSI_ZONE_J_RAINBOW_3 = 10, + MSI_ZONE_J_CORSAIR = 11, + MSI_ZONE_J_CORSAIR_OUTERLL120 = 12, + MSI_ZONE_ON_BOARD_LED_0 = 13, + MSI_ZONE_ON_BOARD_LED_1 = 14, + MSI_ZONE_ON_BOARD_LED_2 = 15, + MSI_ZONE_ON_BOARD_LED_3 = 16, + MSI_ZONE_ON_BOARD_LED_4 = 17, + MSI_ZONE_ON_BOARD_LED_5 = 18, + MSI_ZONE_ON_BOARD_LED_6 = 19, + MSI_ZONE_ON_BOARD_LED_7 = 20, + MSI_ZONE_ON_BOARD_LED_8 = 21, + MSI_ZONE_ON_BOARD_LED_9 = 22, + MSI_ZONE_ON_BOARD_LED_10 = 23, + MSI_ZONE_JAF = 24, + MSI_ZONE_JARGB_1 = 25, + MSI_ZONE_JARGB_2 = 26, + MSI_ZONE_JARGB_3 = 27, +}; + +enum MSI_MODE +{ + MSI_MODE_DISABLE = 0, + MSI_MODE_STATIC = 1, + MSI_MODE_BREATHING = 2, + MSI_MODE_FLASHING = 3, + MSI_MODE_DOUBLE_FLASHING = 4, + MSI_MODE_LIGHTNING = 5, + MSI_MODE_MSI_MARQUEE = 6, + MSI_MODE_METEOR = 7, + MSI_MODE_WATER_DROP = 8, + MSI_MODE_MSI_RAINBOW = 9, + MSI_MODE_POP = 10, + MSI_MODE_RAP = 11, + MSI_MODE_JAZZ = 12, + MSI_MODE_PLAY = 13, + MSI_MODE_MOVIE = 14, + MSI_MODE_COLOR_RING = 15, + MSI_MODE_PLANETARY = 16, + MSI_MODE_DOUBLE_METEOR = 17, + MSI_MODE_ENERGY = 18, + MSI_MODE_BLINK = 19, + MSI_MODE_CLOCK = 20, + MSI_MODE_COLOR_PULSE = 21, + MSI_MODE_COLOR_SHIFT = 22, + MSI_MODE_COLOR_WAVE = 23, + MSI_MODE_MARQUEE = 24, + MSI_MODE_RAINBOW = 25, + MSI_MODE_RAINBOW_WAVE = 26, + MSI_MODE_VISOR = 27, + MSI_MODE_JRAINBOW = 28, + MSI_MODE_RAINBOW_FLASHING = 29, + MSI_MODE_RAINBOW_DOUBLE_FLASHING = 30, + MSI_MODE_RANDOM = 31, + MSI_MODE_FAN_CONTROL = 32, + MSI_MODE_DISABLE_2 = 33, + MSI_MODE_COLOR_RING_FLASHING = 34, + MSI_MODE_COLOR_RING_DOUBLE_FLASHING = 35, + MSI_MODE_STACK = 36, + MSI_MODE_CORSAIR_QUE = 37, + MSI_MODE_FIRE = 38, + MSI_MODE_LAVA = 39, + MSI_MODE_DIRECT_DUMMY = 100 +}; + +enum MSI_SPEED +{ + MSI_SPEED_LOW = 0, + MSI_SPEED_MEDIUM = 1, + MSI_SPEED_HIGH = 2, +}; + +enum MSI_FAN_TYPE +{ + MSI_FAN_TYPE_SP = 0, + MSI_FAN_TYPE_HD = 1, + MSI_FAN_TYPE_LL = 2, +}; + +enum MSI_BRIGHTNESS +{ + MSI_BRIGHTNESS_OFF = 0, + MSI_BRIGHTNESS_LEVEL_10 = 1, + MSI_BRIGHTNESS_LEVEL_20 = 2, + MSI_BRIGHTNESS_LEVEL_30 = 3, + MSI_BRIGHTNESS_LEVEL_40 = 4, + MSI_BRIGHTNESS_LEVEL_50 = 5, + MSI_BRIGHTNESS_LEVEL_60 = 6, + MSI_BRIGHTNESS_LEVEL_70 = 7, + MSI_BRIGHTNESS_LEVEL_80 = 8, + MSI_BRIGHTNESS_LEVEL_90 = 9, + MSI_BRIGHTNESS_LEVEL_100 = 10, +}; + +#define NUMOF_PER_LED_MODE_LEDS 240 +#define NUM_LEDS_761 720 + +#define SYNC_SETTING_ONBOARD 0x01 +#define SYNC_SETTING_JRAINBOW1 0x02 +#define SYNC_SETTING_JRAINBOW2 0x04 +#define SYNC_SETTING_JCORSAIR 0x08 +#define SYNC_SETTING_JPIPE1 0x10 +#define SYNC_SETTING_JPIPE2 0x20 +#define SYNC_SETTING_JRGB 0x80 + +#define MSI_64_MAX_COLORS 7 + +struct Color +{ + unsigned char R; + unsigned char G; + unsigned char B; +}; + +struct CorsairZoneData +{ + unsigned char effect = MSI_MODE_STATIC; + Color color { 0, 0, 0 }; + unsigned char fan_flags = 40; + unsigned char corsair_quantity = 0; + unsigned char padding[4] = { 0, 0, 0, 0 }; + unsigned char is_individual = 0; +}; + +struct ZoneData +{ + unsigned char effect = MSI_MODE_STATIC; + Color color { 0, 0, 0 }; + unsigned char speedAndBrightnessFlags = 0; + Color color2 { 0, 0, 0 }; + unsigned char colorFlags = 0; + unsigned char padding = 0; +}; + +struct RainbowZoneData : ZoneData +{ + unsigned char cycle_or_led_num = 100; +}; + +struct FeaturePacket_64 +{ + const unsigned char report_id = 0x02; // Report ID + const unsigned char second_byte = 0x00; + unsigned char mode = 0x00; + unsigned char speed = 0x00; + unsigned char brightness = 0x00; + unsigned char num_colors = 0x00; + Color colors[MSI_64_MAX_COLORS] = {}; + const unsigned char padding[37] = {}; //pad to make the packet size 64 bytes +}; + +struct FeaturePacket_112 +{ + const unsigned char report_id = 0x52; // Report ID + ZoneData j_rgb_1; // 1 + ZoneData j_rainbow_1; // 11 + ZoneData j_corsair_1; // 21 + ZoneData j_corsair_outerll120; // 31 + ZoneData on_board_led; // 41 + ZoneData on_board_led_1; // 51 + ZoneData on_board_led_2; // 61 + ZoneData on_board_led_3; // 71 + ZoneData on_board_led_4; // 81 + ZoneData on_board_led_5; // 91 + ZoneData on_board_led_6; // 101 + unsigned char save_data = 0; // 111 +}; + +struct FeaturePacket_162 +{ + const unsigned char report_id = 0x52; // Report ID + ZoneData j_rgb_1; // 1 + ZoneData j_rainbow_1; // 11 + ZoneData j_corsair_1; // 21 + ZoneData j_corsair_outerll120; // 31 + ZoneData on_board_led; // 41 + ZoneData on_board_led_1; // 51 + ZoneData on_board_led_2; // 61 + ZoneData on_board_led_3; // 71 + ZoneData on_board_led_4; // 81 + ZoneData on_board_led_5; // 91 + ZoneData on_board_led_6; // 101 + ZoneData on_board_led_7; // 111 + ZoneData on_board_led_8; // 121 + ZoneData on_board_led_9; // 131 + ZoneData on_board_led_10; // 141 + ZoneData j_rgb_2; // 151 + unsigned char save_data = 0; // 161 +}; + +struct FeaturePacket_185 +{ + const unsigned char report_id = 0x52; // Report ID + ZoneData j_rgb_1; // 1 + ZoneData j_pipe_1; // 11 + ZoneData j_pipe_2; // 21 + RainbowZoneData j_rainbow_1; // 31 + RainbowZoneData j_rainbow_2; // 42 + CorsairZoneData j_corsair; // 53 + ZoneData j_corsair_outerll120; // 64 + ZoneData on_board_led; // 74 + ZoneData on_board_led_1; // 84 + ZoneData on_board_led_2; // 94 + ZoneData on_board_led_3; // 104 + ZoneData on_board_led_4; // 114 + ZoneData on_board_led_5; // 124 + ZoneData on_board_led_6; // 134 + ZoneData on_board_led_7; // 144 + ZoneData on_board_led_8; // 154 + ZoneData on_board_led_9; // 164 + ZoneData j_rgb_2; // 174 + unsigned char save_data = 0; // 184 +}; + +struct FeaturePacket_PerLED_185 +{ + unsigned char report_id = 0x53; // Report ID + unsigned char hdr0 = 0x25; // header byte 0 + unsigned char hdr1 = 0x06; // header byte 1 + unsigned char hdr2 = 0x00; // header byte 2 + unsigned char hdr3 = 0x00; // header byte 3 + Color leds[NUMOF_PER_LED_MODE_LEDS]; +}; + +struct FeaturePacket_PerLED_761 +{ + unsigned char report_id = 0x51; // Report ID + unsigned char fixed1 = 0x09; // Always 9? + unsigned char hdr0; // 0x08 for ez 0x04 for ARGB 0x06 for LED + unsigned char hdr1; // 0 for LED/EZ and argb num for ARGB? + unsigned char fixed2 = 0x00; // IDK what this is + unsigned char fixed3 = 0x00; // IDK what this is + unsigned char hdr2; // Led Count + unsigned char colors [NUM_LEDS_761]; +}; + +struct FeaturePacket_Zone_761 +{ + MSI_ZONE zone; + FeaturePacket_PerLED_761 packet; +}; + +struct FeaturePacket_761 +{ + FeaturePacket_Zone_761 jargb1; + FeaturePacket_Zone_761 jargb2; + FeaturePacket_Zone_761 jargb3; + FeaturePacket_Zone_761 jaf; +}; + +#define MSI_USB_PID_COMMON 0x0076 // Common PID for a certain set of 185-byte boards diff --git a/Controllers/MSIMysticLightController/MSIMysticLightControllerDetect.cpp b/Controllers/MSIMysticLightController/MSIMysticLightControllerDetect.cpp new file mode 100644 index 0000000..5fbb843 --- /dev/null +++ b/Controllers/MSIMysticLightController/MSIMysticLightControllerDetect.cpp @@ -0,0 +1,287 @@ +/*---------------------------------------------------------*\ +| MSIMysticLightControllerDetect.cpp | +| | +| Detector for MSI Mystic Light motherboards | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "MSIMysticLight64Controller.h" +#include "MSIMysticLight112Controller.h" +#include "MSIMysticLight162Controller.h" +#include "MSIMysticLight185Controller.h" +#include "MSIMysticLight761Controller.h" +#include "RGBController_MSIMysticLight64.h" +#include "RGBController_MSIMysticLight112.h" +#include "RGBController_MSIMysticLight162.h" +#include "RGBController_MSIMysticLight185.h" +#include "RGBController_MSIMysticLight761.h" +#include "dmiinfo.h" +#include "LogManager.h" + +#define MSI_USB_VID 0x1462 +#define MSI_USB_VID_COMMON 0x0DB0 + +/*---------------------------------------------------------------------------------*\ +| WARNING! | +| | +| The MSI Mystic Light controller had a bricking risk in the past. | +| The code has been tested on a few boards and the bricking issue has been fixed. | +| Uncomment this line to enable for untested boards. Do so at your own risk. | +\*---------------------------------------------------------------------------------*/ +//#define ENABLE_UNTESTED_MYSTIC_LIGHT + +/*----------------------------------------------------------------------------------------*\ +| | +| DetectMSIMysticLightControllers | +| | +| Detect MSI Mystic Light devices | +| | +\*----------------------------------------------------------------------------------------*/ +void DetectMSIMysticLightControllers + ( + hid_device_info* info, + const std::string& /*name*/ + ) +{ + hid_device* dev = hid_open_path(info->path); + if(dev != nullptr) + { + unsigned char temp_buffer[200]; + temp_buffer[0] = 0x52; + + size_t packet_length = hid_get_feature_report(dev, temp_buffer, 200); + + DMIInfo dmi; + std::string dmi_name = "MSI " + dmi.getMainboard(); + + if((packet_length >= sizeof(FeaturePacket_185)) && (packet_length <= (sizeof(FeaturePacket_185) + 1))) //WHY r we doing this ? why not == + { + MSIMysticLight185Controller* controller = new MSIMysticLight185Controller(dev, info->path, info->product_id, dmi_name); + RGBController_MSIMysticLight185* rgb_controller = new RGBController_MSIMysticLight185(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else if((packet_length >= sizeof(FeaturePacket_162)) && (packet_length <= (sizeof(FeaturePacket_162) + 1))) + { + MSIMysticLight162Controller* controller = new MSIMysticLight162Controller(dev, info->path, info->product_id, dmi_name); + RGBController_MSIMysticLight162* rgb_controller = new RGBController_MSIMysticLight162(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else if((packet_length >= sizeof(FeaturePacket_112)) && (packet_length <= (sizeof(FeaturePacket_112) + 1))) + { + MSIMysticLight112Controller* controller = new MSIMysticLight112Controller(dev, info->path, dmi_name); + RGBController_MSIMysticLight112* rgb_controller = new RGBController_MSIMysticLight112(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else // no supported length returned + { + + unsigned char second_buffer [761]; + second_buffer[0] = 0x50; + + memset(second_buffer + sizeof(unsigned char), 0x0, sizeof(second_buffer) - sizeof(unsigned char)); + + //Using this enables subsequent reads to work for some reason + size_t enable_reading_packet = hid_get_feature_report(dev, second_buffer, 290); + LOG_INFO("Read %i bytes from read enable packet, subsequent get reports should work", enable_reading_packet); + + memset(second_buffer + sizeof(unsigned char), 0x0, sizeof(second_buffer) - sizeof(unsigned char)); + + + second_buffer[0] = 0x51; + + size_t packet_length_new_attempt = hid_send_feature_report(dev, second_buffer, 761); + + if(packet_length_new_attempt > 0) + { + + try + { + MSIMysticLight761Controller* controller = new MSIMysticLight761Controller(dev, (const char *) info->path, dmi_name); + RGBController_MSIMysticLight761* rgb_controller = new RGBController_MSIMysticLight761(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + catch(const std::runtime_error& e) + { + if (strcmp(e.what(), BOARD_UNSUPPORTED_ERROR) != 0) + { + throw e; + } + else + { + LOG_INFO("Found Board %s but does not have valid config", dmi_name.c_str()); + } + } + + + } + else + { + LOG_INFO("No matching driver found for %s, packet length = %d", dmi_name.c_str(), packet_length); + return; + } + } + } +} + +void DetectMSIMysticLight64Controllers + ( + hid_device_info* info, + const std::string& /*name*/ + ) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev != nullptr) + { + MSIMysticLight64Controller* controller = new MSIMysticLight64Controller(dev, info->path); + RGBController_MSIMysticLight64* rgb_controller = new RGBController_MSIMysticLight64(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + + +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_1562", DetectMSIMysticLight64Controllers, MSI_USB_VID, 0x1562, 0x00FF, 0x01); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_1563", DetectMSIMysticLight64Controllers, MSI_USB_VID, 0x1563, 0x00FF, 0x01); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_1564", DetectMSIMysticLight64Controllers, MSI_USB_VID, 0x1564, 0x00FF, 0x01); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_1720", DetectMSIMysticLightControllers, MSI_USB_VID, 0x1720, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B12", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B12, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B16", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B16, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B17", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B17, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B18", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B18, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B50", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B50, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B85", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B85, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B92", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B92, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B93", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B93, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C02", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C02, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C34", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C34, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C35", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C35, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C36", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C36, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C37", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C37, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C56", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C56, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C59", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C59, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C60", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C60, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C67", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C67, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C71", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C71, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C73", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C73, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C75", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C75, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C76", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C76, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C77", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C77, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C79", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C79, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C80", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C80, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C81", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C81, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C82", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C82, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C83", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C83, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C84", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C84, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C86", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C86, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C87", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C87, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C90", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C90, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C91", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C91, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C92", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C92, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C94", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C94, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C95", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C95, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C98", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C98, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D03", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D03, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D04", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D04, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D06", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D06, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D07", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D07, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D08", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D08, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D09", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D09, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D13", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D13, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D14", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D14, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D15", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D15, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D17", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D17, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D18", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D18, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D19", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D19, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D20", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D20, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D25", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D25, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D27", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D27, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D28", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D28, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D29", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D29, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D30", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D30, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D31", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D31, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D32", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D32, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D33", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D33, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D36", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D36, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D37", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D37, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D38", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D38, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D40", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D40, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D41", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D41, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D42", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D42, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D43", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D43, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D46", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D46, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D50", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D50, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D51", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D51, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D52", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D52, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D53", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D53, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D54", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D54, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D59", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D59, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D67", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D67, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D69", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D69, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D70", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D70, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D73", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D73, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D74", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D74, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D75", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D75, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D76", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D76, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D77", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D77, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D78", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D78, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D86", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D86, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D88", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D88, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D89", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D89, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D90", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D90, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D91", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D91, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D93", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D93, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D96", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D96, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D97", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D97, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D98", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D98, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7D99", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7D99, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E01", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E01, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E03", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E03, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E06", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E06, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E07", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E07, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E09", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E09, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E10", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E10, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_B926", DetectMSIMysticLightControllers, MSI_USB_VID, 0xB926, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E70", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E70, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E59", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E59, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E80", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E80, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E81", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E81, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E34", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E34, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E32", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E32, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7E20", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7E20, 0x0001, 0x00); +// Detector for the set of common boards +REGISTER_HID_DETECTOR_PU("MSI Mystic Light Common", DetectMSIMysticLightControllers, MSI_USB_VID_COMMON, MSI_USB_PID_COMMON, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light X870", DetectMSIMysticLightControllers, MSI_USB_VID_COMMON, MSI_USB_PID_COMMON, 0xFF00, 0x01); +/*---------------------------------------------------------------------------------------------------------*\ +| Dummy entries for boards using common VID and PID | +| | +| DUMMY_DEVICE_DETECTOR("MSI Mystic Light MS_7E12", DetectMSIMysticLightControllers, 0x1462, 0x7E12 ) | +| DUMMY_DEVICE_DETECTOR("MSI Mystic Light MS_7E16", DetectMSIMysticLightControllers, 0x1462, 0x7E16 ) | +| DUMMY_DEVICE_DETECTOR("MSI Mystic Light MS_7E24", DetectMSIMysticLightControllers, 0x1462, 0x7E24 ) | +| DUMMY_DEVICE_DETECTOR("MSI Mystic Light MS_7E26", DetectMSIMysticLightControllers, 0x1462, 0x7E26 ) | +| DUMMY_DEVICE_DETECTOR("MSI Mystic Light MS_7E27", DetectMSIMysticLightControllers, 0x1462, 0x7E27 ) | +| DUMMY_DEVICE_DETECTOR("MSI Mystic Light MS_7E49", DetectMSIMysticLightControllers, 0x1462, 0x7E49 ) | +\*---------------------------------------------------------------------------------------------------------*/ + + +#ifdef ENABLE_UNTESTED_MYSTIC_LIGHT +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_3EA4", DetectMSIMysticLightControllers, MSI_USB_VID, 0x3EA4, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_4459", DetectMSIMysticLightControllers, MSI_USB_VID, 0x4459, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B10", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B10, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B94", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B94, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7B96", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7B96, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C42", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C42, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C70", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C70, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C85", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C85, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C88", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C88, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C89", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C89, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C96", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C96, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_7C99", DetectMSIMysticLightControllers, MSI_USB_VID, 0x7C99, 0x0001, 0x00); +REGISTER_HID_DETECTOR_PU("MSI Mystic Light MS_905D", DetectMSIMysticLightControllers, MSI_USB_VID, 0x905D, 0x0001, 0x00); +#endif diff --git a/Controllers/MSIOptixController/MSIOptixController.cpp b/Controllers/MSIOptixController/MSIOptixController.cpp new file mode 100644 index 0000000..9da309d --- /dev/null +++ b/Controllers/MSIOptixController/MSIOptixController.cpp @@ -0,0 +1,216 @@ +/*---------------------------------------------------------*\ +| MSIOptixController.cpp | +| | +| Driver for MSI Optix | +| | +| Morgan Guimard (morg) 10 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "MSIOptixController.h" +#include "StringUtils.h" + +MSIOptixController::MSIOptixController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +MSIOptixController::~MSIOptixController() +{ + hid_close(dev); +} + +std::string MSIOptixController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MSIOptixController::GetNameString() +{ + return(name); +} + +std::string MSIOptixController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned char MSIOptixController::GetMysteriousFlag(unsigned char mode_value) +{ + switch(mode_value) + { + case RAINBOW_MODE_VALUE: + case STACK_MODE_VALUE: + case BREATHING_MODE_VALUE: + case FLASHING_MODE_VALUE: + case DOUBLE_FLASHING_MODE_VALUE: + case STATIC_MODE_VALUE: + case METEOR_MODE_VALUE: + case LIGHNING_MODE_VALUE: + case PLANETARY_MODE_VALUE: + case DOUBLE_METEOR_MODE_VALUE: + case ENERGY_MODE_VALUE: + case MARQUEE_MODE_VALUE: + return MSI_OPTIX_MYSTERIOUS_FLAG; + + default: return 0x00; + } +} + +void MSIOptixController::SetDirect(std::vector colors, unsigned char brightness) +{ + unsigned char usb_buf[MSI_OPTIX_REPORT_SIZE]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = MSI_OPTIC_REPORT_ID; + + unsigned char offset = 0x00; + + usb_buf[offset + 0x01] = DIRECT_MODE_VALUE; // mode + usb_buf[offset + 0x02] = 0xff; // color r value + usb_buf[offset + 0x03] = 0xff; // color g value + usb_buf[offset + 0x04] = 0xff; // color b value + usb_buf[offset + 0x05] = 0x00; // speed + usb_buf[offset + 0x06] = brightness; // Brightness + usb_buf[offset + 0x07] = 0x00; // always 00 + usb_buf[offset + 0x08] = 0xff; // always ff + usb_buf[offset + 0x09] = 0x00; // always 00 + usb_buf[offset + 0x0A] = MSI_OPTIX_MYSTERIOUS_FLAG; // enigma + usb_buf[offset + 0x0B] = 0x00; // always 00 + + /*-----------------------------------------*\ + | Duplicate the block - enigma | + \*-----------------------------------------*/ + offset = 0x0B; + + usb_buf[offset + 0x01] = DIRECT_MODE_VALUE; // mode + usb_buf[offset + 0x02] = 0xff; // color r value + usb_buf[offset + 0x03] = 0xff; // color g value + usb_buf[offset + 0x04] = 0xff; // color b value + usb_buf[offset + 0x05] = 0x00; // speed + usb_buf[offset + 0x06] = brightness; // Brightness + usb_buf[offset + 0x07] = 0x00; // always 00 + usb_buf[offset + 0x08] = 0xff; // always ff + usb_buf[offset + 0x09] = 0x00; // always 00 + usb_buf[offset + 0x0A] = MSI_OPTIX_MYSTERIOUS_FLAG; // enigma + usb_buf[offset + 0x0B] = 0x00; // always 00 + + + /*-----------------------------------------*\ + | Colors block position | + \*-----------------------------------------*/ + offset += 0x0B; + + /*-----------------------------------------*\ + | Start at index 25 in the colors block | + | multiplied by the 3 channels (rgb) | + \*-----------------------------------------*/ + offset += MSI_OPTIX_DIRECT_COLOR_OFFSET; + + for(unsigned int i = 0; i < MSI_OPTIX_NUMBER_OF_LEDS; i++) + { + usb_buf[++offset] = RGBGetRValue(colors[i]); + usb_buf[++offset] = RGBGetGValue(colors[i]); + usb_buf[++offset] = RGBGetBValue(colors[i]); + } + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +void MSIOptixController::SetMode(std::vector colors, unsigned char brightness, unsigned char speed, unsigned char mode_value, unsigned int mode_flags) +{ + unsigned char red = 0xff; + unsigned char grn = 0xff; + unsigned char blu = 0xff; + + if(mode_flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + red = RGBGetRValue(colors[0]); + grn = RGBGetGValue(colors[0]); + blu = RGBGetBValue(colors[0]); + } + + unsigned char usb_buf[MSI_OPTIX_REPORT_SIZE]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = MSI_OPTIC_REPORT_ID; + + unsigned char offset = 0x00; + + usb_buf[offset + 0x01] = mode_value; // mode + usb_buf[offset + 0x02] = red; // color r value + usb_buf[offset + 0x03] = grn; // color g value + usb_buf[offset + 0x04] = blu; // color b value + usb_buf[offset + 0x05] = speed; // speed + usb_buf[offset + 0x06] = brightness; // Brightness + usb_buf[offset + 0x07] = 0x00; // always 00 + usb_buf[offset + 0x08] = 0xff; // always ff + usb_buf[offset + 0x09] = 0x00; // always 00 + usb_buf[offset + 0x0A] = GetMysteriousFlag(mode_value); // enigma + usb_buf[offset + 0x0B] = 0x00; // always 00 + + /*-----------------------------------------*\ + | Duplicate the block - enigma | + \*-----------------------------------------*/ + offset = 0x0B; + + usb_buf[offset + 0x01] = mode_value; // mode + usb_buf[offset + 0x02] = red; // color r value + usb_buf[offset + 0x03] = grn; // color g value + usb_buf[offset + 0x04] = blu; // color b value + usb_buf[offset + 0x05] = speed; // speed + usb_buf[offset + 0x06] = brightness; // Brightness + usb_buf[offset + 0x07] = 0x00; // always 00 + usb_buf[offset + 0x08] = 0xff; // always ff + usb_buf[offset + 0x09] = 0x00; // always 00 + usb_buf[offset + 0x0A] = GetMysteriousFlag(mode_value); // enigma + usb_buf[offset + 0x0B] = 0x00; // always 00 + + /*-----------------------------------------*\ + | Colors block position | + \*-----------------------------------------*/ + offset += 0x0B; + + if(mode_flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + /*-----------------------------------------*\ + | Start at index 25 in the colors block | + | multiplied by the 3 channels (rgb) | + \*-----------------------------------------*/ + offset += MSI_OPTIX_DIRECT_COLOR_OFFSET; + + for(unsigned int i = 0; i < MSI_OPTIX_NUMBER_OF_LEDS; i++) + { + usb_buf[++offset] = RGBGetRValue(colors[i]); + usb_buf[++offset] = RGBGetGValue(colors[i]); + usb_buf[++offset] = RGBGetBValue(colors[i]); + } + } + + else if(mode_flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + for(unsigned int i = 0; i < MSI_OPTIX_COLOR_PACKET_SIZE; i++) + { + usb_buf[++offset] = red; + usb_buf[++offset] = grn; + usb_buf[++offset] = blu; + } + } + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} diff --git a/Controllers/MSIOptixController/MSIOptixController.h b/Controllers/MSIOptixController/MSIOptixController.h new file mode 100644 index 0000000..08ecfa1 --- /dev/null +++ b/Controllers/MSIOptixController/MSIOptixController.h @@ -0,0 +1,88 @@ +/*---------------------------------------------------------*\ +| MSIOptixController.h | +| | +| Driver for MSI Optix | +| | +| Morgan Guimard (morg) 10 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define MSI_OPTIX_REPORT_SIZE 168 +#define MSI_OPTIX_COLOR_PACKET_SIZE 48 +#define MSI_OPTIX_NUMBER_OF_LEDS 12 +#define MSI_OPTIX_DIRECT_COLOR_OFFSET 24 * 3; +#define MSI_OPTIC_REPORT_ID 0x72 +#define MSI_OPTIX_DEFAULT_MODE_COLOR ToRGBColor(255,0,0) + +enum +{ + OFF_MODE_VALUE = 0x00, + RAINBOW_MODE_VALUE = 0x0f, + METEOR_MODE_VALUE = 0x07, + STACK_MODE_VALUE = 0x08, + BREATHING_MODE_VALUE = 0x02, + FLASHING_MODE_VALUE = 0x03, + DOUBLE_FLASHING_MODE_VALUE = 0x04, + DIRECT_MODE_VALUE = 0x01, + STATIC_MODE_VALUE = 0x01, + LIGHNING_MODE_VALUE = 0x05, + PLANETARY_MODE_VALUE = 0x10, + DOUBLE_METEOR_MODE_VALUE = 0x11, + ENERGY_MODE_VALUE = 0x12, + BLINK_MODE_VALUE = 0x13, + CLOCK_MODE_VALUE = 0x14, + COLOR_PULSE_MODE_VALUE = 0x15, + COLOR_SHIFT_MODE_VALUE = 0x16, + COLOR_WAVE_MODE_VALUE = 0x17, + MARQUEE_MODE_VALUE = 0x18, + RAINBOW_WAVE_MODE_VALUE = 0x1a, + VISOR_MODE_VALUE = 0x1b +}; + +enum +{ + MSI_OPTIX_BRIGHTNESS_MIN = 0x00, + MSI_OPTIX_BRIGHTNESS_MAX = 0x64 +}; + +enum +{ + MSI_OPTIX_SPEED_MIN = 0x00, + MSI_OPTIX_SPEED_MAX = 0x02 +}; + +enum +{ + MSI_OPTIX_MYSTERIOUS_FLAG = 0x80 +}; + +class MSIOptixController +{ +public: + MSIOptixController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~MSIOptixController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetDirect(std::vector colors, unsigned char brightness); + void SetMode(std::vector colors, unsigned char brightness, unsigned char speed, unsigned char mode_value, unsigned int mode_flags); + +protected: + hid_device* dev; + +private: + std::string location; + std::string name; + + unsigned char GetMysteriousFlag(unsigned char mode_value); +}; diff --git a/Controllers/MSIOptixController/MSIOptixControllerDetect.cpp b/Controllers/MSIOptixController/MSIOptixControllerDetect.cpp new file mode 100644 index 0000000..df89642 --- /dev/null +++ b/Controllers/MSIOptixController/MSIOptixControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| MSIOptixControllerDetect.cpp | +| | +| Detector for MSI Optix | +| | +| Morgan Guimard (morg) 10 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "MSIOptixController.h" +#include "RGBController_MSIOptix.h" + +/*---------------------------------------------------------*\ +| MSI vendor ID | +\*---------------------------------------------------------*/ +#define MSI_VID 0x1462 + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define MSI_OPTIX_MAG274QRF_PID 0x3FA4 + +void DetectMSIOptixControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + MSIOptixController* controller = new MSIOptixController(dev, *info, name); + RGBController_MSIOptix* rgb_controller = new RGBController_MSIOptix(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("MSI Optix controller", DetectMSIOptixControllers, MSI_VID, MSI_OPTIX_MAG274QRF_PID, 0, 0xFF00, 1); diff --git a/Controllers/MSIOptixController/RGBController_MSIOptix.cpp b/Controllers/MSIOptixController/RGBController_MSIOptix.cpp new file mode 100644 index 0000000..63cefca --- /dev/null +++ b/Controllers/MSIOptixController/RGBController_MSIOptix.cpp @@ -0,0 +1,395 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIOptix.cpp | +| | +| RGBController for MSI Optix | +| | +| Morgan Guimard (morg) 10 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_MSIOptix.h" + +/**------------------------------------------------------------------*\ + @name MSI Optix + @category LEDStrip + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectMSIOptixControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSIOptix::RGBController_MSIOptix(MSIOptixController* controller_ptr) +{ + controller = controller_ptr; + name = controller->GetNameString(); + vendor = "MSI"; + type = DEVICE_TYPE_LEDSTRIP; + description = "MSI Optix USB Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = DIRECT_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Direct.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Direct.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Static.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Static.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Static.colors.resize(1); + Static.colors[0] = MSI_OPTIX_DEFAULT_MODE_COLOR; + modes.push_back(Static); + + mode OFF; + OFF.name = "Off"; + OFF.value = OFF_MODE_VALUE; + OFF.flags = 0; + OFF.color_mode = MODE_COLORS_NONE; + modes.push_back(OFF); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = RAINBOW_MODE_VALUE; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Rainbow.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Rainbow.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Rainbow.speed_min = MSI_OPTIX_SPEED_MIN; + Rainbow.speed_max = MSI_OPTIX_SPEED_MAX; + Rainbow.speed = MSI_OPTIX_SPEED_MIN; + modes.push_back(Rainbow); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = METEOR_MODE_VALUE; + Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors_min = 1; + Meteor.colors_max = 1; + Meteor.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Meteor.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Meteor.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Meteor.speed_min = MSI_OPTIX_SPEED_MIN; + Meteor.speed_max = MSI_OPTIX_SPEED_MAX; + Meteor.speed = MSI_OPTIX_SPEED_MIN; + Meteor.colors.resize(1); + Meteor.colors[0] = MSI_OPTIX_DEFAULT_MODE_COLOR; + modes.push_back(Meteor); + + mode Stack; + Stack.name = "Stack"; + Stack.value = STACK_MODE_VALUE; + Stack.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stack.colors_min = 1; + Stack.colors_max = 1; + Stack.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Stack.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Stack.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Stack.speed_min = MSI_OPTIX_SPEED_MIN; + Stack.speed_max = MSI_OPTIX_SPEED_MAX; + Stack.speed = MSI_OPTIX_SPEED_MIN; + Stack.colors.resize(1); + Stack.colors[0] = MSI_OPTIX_DEFAULT_MODE_COLOR; + modes.push_back(Stack); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Breathing.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Breathing.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Breathing.speed_min = MSI_OPTIX_SPEED_MIN; + Breathing.speed_max = MSI_OPTIX_SPEED_MAX; + Breathing.speed = MSI_OPTIX_SPEED_MIN; + Breathing.colors.resize(1); + Breathing.colors[0] = MSI_OPTIX_DEFAULT_MODE_COLOR; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = FLASHING_MODE_VALUE; + Flashing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flashing.colors_min = 1; + Flashing.colors_max = 1; + Flashing.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Flashing.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Flashing.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Flashing.speed_min = MSI_OPTIX_SPEED_MIN; + Flashing.speed_max = MSI_OPTIX_SPEED_MAX; + Flashing.speed = MSI_OPTIX_SPEED_MIN; + Flashing.colors.resize(1); + Flashing.colors[0] = MSI_OPTIX_DEFAULT_MODE_COLOR; + modes.push_back(Flashing); + + mode Double_Flashing; + Double_Flashing.name = "Double Flashing"; + Double_Flashing.value = DOUBLE_FLASHING_MODE_VALUE; + Double_Flashing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Double_Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Double_Flashing.colors_min = 1; + Double_Flashing.colors_max = 1; + Double_Flashing.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Double_Flashing.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Double_Flashing.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Double_Flashing.speed_min = MSI_OPTIX_SPEED_MIN; + Double_Flashing.speed_max = MSI_OPTIX_SPEED_MAX; + Double_Flashing.speed = MSI_OPTIX_SPEED_MIN; + Double_Flashing.colors.resize(1); + Double_Flashing.colors[0] = MSI_OPTIX_DEFAULT_MODE_COLOR; + modes.push_back(Double_Flashing); + + mode Lightning; + Lightning.name = "Lightning"; + Lightning.value = LIGHNING_MODE_VALUE; + Lightning.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Lightning.color_mode = MODE_COLORS_MODE_SPECIFIC; + Lightning.colors_min = 1; + Lightning.colors_max = 1; + Lightning.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Lightning.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Lightning.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Lightning.speed_min = MSI_OPTIX_SPEED_MIN; + Lightning.speed_max = MSI_OPTIX_SPEED_MAX; + Lightning.speed = MSI_OPTIX_SPEED_MIN; + Lightning.colors.resize(1); + Lightning.colors[0] = MSI_OPTIX_DEFAULT_MODE_COLOR; + modes.push_back(Lightning); + + mode Planetary; + Planetary.name = "Planetary"; + Planetary.value = PLANETARY_MODE_VALUE; + Planetary.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Planetary.color_mode = MODE_COLORS_NONE; + Planetary.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Planetary.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Planetary.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Planetary.speed_min = MSI_OPTIX_SPEED_MIN; + Planetary.speed_max = MSI_OPTIX_SPEED_MAX; + Planetary.speed = MSI_OPTIX_SPEED_MIN; + modes.push_back(Planetary); + + mode Double_Meteor; + Double_Meteor.name = "Double_Meteor"; + Double_Meteor.value = DOUBLE_METEOR_MODE_VALUE; + Double_Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Double_Meteor.color_mode = MODE_COLORS_NONE; + Double_Meteor.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Double_Meteor.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Double_Meteor.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Double_Meteor.speed_min = MSI_OPTIX_SPEED_MIN; + Double_Meteor.speed_max = MSI_OPTIX_SPEED_MAX; + Double_Meteor.speed = MSI_OPTIX_SPEED_MIN; + modes.push_back(Double_Meteor); + + mode Energy; + Energy.name = "Energy"; + Energy.value = ENERGY_MODE_VALUE; + Energy.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Energy.color_mode = MODE_COLORS_MODE_SPECIFIC; + Energy.colors_min = 1; + Energy.colors_max = 1; + Energy.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Energy.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Energy.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Energy.speed_min = MSI_OPTIX_SPEED_MIN; + Energy.speed_max = MSI_OPTIX_SPEED_MAX; + Energy.speed = MSI_OPTIX_SPEED_MIN; + Energy.colors.resize(1); + Energy.colors[0] = MSI_OPTIX_DEFAULT_MODE_COLOR; + modes.push_back(Energy); + + mode Blink; + Blink.name = "Blink"; + Blink.value = BLINK_MODE_VALUE; + Blink.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Blink.color_mode = MODE_COLORS_NONE; + Blink.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Blink.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Blink.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Blink.speed_min = MSI_OPTIX_SPEED_MIN; + Blink.speed_max = MSI_OPTIX_SPEED_MAX; + Blink.speed = MSI_OPTIX_SPEED_MIN; + modes.push_back(Blink); + + mode Clock; + Clock.name = "Clock"; + Clock.value = CLOCK_MODE_VALUE; + Clock.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Clock.color_mode = MODE_COLORS_NONE; + Clock.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Clock.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Clock.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Clock.speed_min = MSI_OPTIX_SPEED_MIN; + Clock.speed_max = MSI_OPTIX_SPEED_MAX; + Clock.speed = MSI_OPTIX_SPEED_MIN; + modes.push_back(Clock); + + mode Color_Pulse; + Color_Pulse.name = "Color Pulse"; + Color_Pulse.value = COLOR_PULSE_MODE_VALUE; + Color_Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Color_Pulse.color_mode = MODE_COLORS_NONE; + Color_Pulse.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Color_Pulse.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Color_Pulse.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Color_Pulse.speed_min = MSI_OPTIX_SPEED_MIN; + Color_Pulse.speed_max = MSI_OPTIX_SPEED_MAX; + Color_Pulse.speed = MSI_OPTIX_SPEED_MIN; + modes.push_back(Color_Pulse); + + mode Color_Shift; + Color_Shift.name = "Color Shift"; + Color_Shift.value = COLOR_SHIFT_MODE_VALUE; + Color_Shift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Color_Shift.color_mode = MODE_COLORS_NONE; + Color_Shift.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Color_Shift.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Color_Shift.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Color_Shift.speed_min = MSI_OPTIX_SPEED_MIN; + Color_Shift.speed_max = MSI_OPTIX_SPEED_MAX; + Color_Shift.speed = MSI_OPTIX_SPEED_MIN; + modes.push_back(Color_Shift); + + mode Color_wave; + Color_wave.name = "Color Wave"; + Color_wave.value = COLOR_WAVE_MODE_VALUE; + Color_wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Color_wave.color_mode = MODE_COLORS_NONE; + Color_wave.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Color_wave.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Color_wave.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Color_wave.speed_min = MSI_OPTIX_SPEED_MIN; + Color_wave.speed_max = MSI_OPTIX_SPEED_MAX; + Color_wave.speed = MSI_OPTIX_SPEED_MIN; + modes.push_back(Color_wave); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = MARQUEE_MODE_VALUE; + Marquee.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Marquee.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Marquee.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Marquee.speed_min = MSI_OPTIX_SPEED_MIN; + Marquee.speed_max = MSI_OPTIX_SPEED_MAX; + Marquee.speed = MSI_OPTIX_SPEED_MIN; + Marquee.colors.resize(1); + Marquee.colors[0] = MSI_OPTIX_DEFAULT_MODE_COLOR; + modes.push_back(Marquee); + + mode Rainbow_Wave; + Rainbow_Wave.name = "Rainbow Wave"; + Rainbow_Wave.value = RAINBOW_WAVE_MODE_VALUE; + Rainbow_Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rainbow_Wave.color_mode = MODE_COLORS_NONE; + Rainbow_Wave.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Rainbow_Wave.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Rainbow_Wave.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Rainbow_Wave.speed_min = MSI_OPTIX_SPEED_MIN; + Rainbow_Wave.speed_max = MSI_OPTIX_SPEED_MAX; + Rainbow_Wave.speed = MSI_OPTIX_SPEED_MIN; + modes.push_back(Rainbow_Wave); + + mode Visor; + Visor.name = "Visor"; + Visor.value = VISOR_MODE_VALUE; + Visor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Visor.color_mode = MODE_COLORS_NONE; + Visor.brightness_min = MSI_OPTIX_BRIGHTNESS_MIN; + Visor.brightness_max = MSI_OPTIX_BRIGHTNESS_MAX; + Visor.brightness = MSI_OPTIX_BRIGHTNESS_MAX; + Visor.speed_min = MSI_OPTIX_SPEED_MIN; + Visor.speed_max = MSI_OPTIX_SPEED_MAX; + Visor.speed = MSI_OPTIX_SPEED_MIN; + modes.push_back(Visor); + + SetupZones(); +} + +RGBController_MSIOptix::~RGBController_MSIOptix() +{ + delete controller; +} + +void RGBController_MSIOptix::SetupZones() +{ + zone new_zone; + + new_zone.name = "Backside"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = MSI_OPTIX_NUMBER_OF_LEDS; + new_zone.leds_max = MSI_OPTIX_NUMBER_OF_LEDS; + new_zone.leds_count = MSI_OPTIX_NUMBER_OF_LEDS; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(new_zone.leds_count); + + for(unsigned int i = 0; i < MSI_OPTIX_NUMBER_OF_LEDS; i++) + { + leds[i].name = "LED " + std::to_string(i); + } + + SetupColors(); +} + +void RGBController_MSIOptix::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_MSIOptix::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_MSIOptix::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetDirect(colors, modes[active_mode].brightness); +} + +void RGBController_MSIOptix::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_MSIOptix::DeviceUpdateMode() +{ + if(modes[active_mode].flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + controller->SetMode(colors, modes[active_mode].brightness, modes[active_mode].speed, modes[active_mode].value, modes[active_mode].flags); + } + else + { + controller->SetMode(modes[active_mode].colors, modes[active_mode].brightness, modes[active_mode].speed, modes[active_mode].value, modes[active_mode].flags); + } +} diff --git a/Controllers/MSIOptixController/RGBController_MSIOptix.h b/Controllers/MSIOptixController/RGBController_MSIOptix.h new file mode 100644 index 0000000..ecd8acd --- /dev/null +++ b/Controllers/MSIOptixController/RGBController_MSIOptix.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIOptix.h | +| | +| RGBController for MSI Optix | +| | +| Morgan Guimard (morg) 10 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "MSIOptixController.h" + +class RGBController_MSIOptix : public RGBController +{ +public: + RGBController_MSIOptix(MSIOptixController* controller_ptr); + ~RGBController_MSIOptix(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + MSIOptixController* controller; +}; diff --git a/Controllers/MSIRGBController/MSIRGBController.cpp b/Controllers/MSIRGBController/MSIRGBController.cpp new file mode 100644 index 0000000..5392ff9 --- /dev/null +++ b/Controllers/MSIRGBController/MSIRGBController.cpp @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| MSIRGBController.cpp | +| | +| Driver for MSI-RGB motherboard | +| | +| Logic adapted from https://github.com/nagisa/msi-rgb | +| | +| Adam Honse (CalcProgrammer1) 11 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "MSIRGBController.h" +#include "dmiinfo.h" +#include "super_io.h" + +MSIRGBController::MSIRGBController(int sioaddr, bool invert, std::string dev_name) +{ + msi_sioaddr = sioaddr; + name = dev_name; + + /*-----------------------------------------------------*\ + | This setup step isn't well documented | + | Without this, pulsing does not work | + \*-----------------------------------------------------*/ + superio_outb(msi_sioaddr, SIO_REG_LOGDEV, 0x09); + + int val_at_2c = superio_inb(msi_sioaddr, 0x2C); + + val_at_2c &= 0b11110111; + val_at_2c |= 0b00010000; + + superio_outb(msi_sioaddr, 0x2C, val_at_2c); + + /*-----------------------------------------------------*\ + | Set logical device register to RGB controller | + \*-----------------------------------------------------*/ + superio_outb(msi_sioaddr, SIO_REG_LOGDEV, MSI_SIO_LOGDEV_RGB); + + /*-----------------------------------------------------*\ + | Test if RGB is enabled. If it is not, enable it. | + \*-----------------------------------------------------*/ + int enable = superio_inb(msi_sioaddr, MSI_SIO_RGB_REG_ENABLE); + + if((enable & MSI_SIO_RGB_ENABLE_MASK) != MSI_SIO_RGB_ENABLE_MASK) + { + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_ENABLE, 0xE0); + } + + /*-----------------------------------------------------*\ + | Lighting enabled, no pulsing or blinking | + \*-----------------------------------------------------*/ + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_CFG_1, 0x00); + + /*--------------------------------------------------------------*\ + | Header on, pulse deactivated, colors inverted depending on DMI | + \*--------------------------------------------------------------*/ + unsigned char ff_val = 0b11100010; + + if (invert) + { + ff_val |= 0b00011100; + } + + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_CFG_3, ff_val); + /*-----------------------------------------------------------*\ + | This seems to be related to some rainbow mode. Deactivated | + \*-----------------------------------------------------------*/ + superio_outb(msi_sioaddr, 0xFD, 0x00); +} + +MSIRGBController::~MSIRGBController() +{ + +} + +std::string MSIRGBController::GetDeviceLocation() +{ + char hex[12]; + snprintf(hex, sizeof(hex), "0x%X", msi_sioaddr); + return("SIO: " + std::string(hex)); +} + +std::string MSIRGBController::GetDeviceName() +{ + return(name); +} + +void MSIRGBController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + /*-----------------------------------------------------*\ + | The MSI RGB controller uses 4 bits per color rather | + | than 8. Shift the values by 4 so that the 4 most | + | significant bits of each color are the new color value| + \*-----------------------------------------------------*/ + red = red >> 4; + green = green >> 4; + blue = blue >> 4; + + /*-----------------------------------------------------*\ + | Set logical device register to RGB controller | + \*-----------------------------------------------------*/ + superio_outb(msi_sioaddr, SIO_REG_LOGDEV, MSI_SIO_LOGDEV_RGB); + + /*-----------------------------------------------------*\ + | Write the colors to the color sequence registers | + | Only static mode is supported right now - all colors | + | are the same. | + \*-----------------------------------------------------*/ + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_RED_1_0, (red | (red << 4))); + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_RED_3_2, (red | (red << 4))); + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_RED_5_4, (red | (red << 4))); + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_RED_7_6, (red | (red << 4))); + + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_GREEN_1_0, (green | (green << 4))); + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_GREEN_3_2, (green | (green << 4))); + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_GREEN_5_4, (green | (green << 4))); + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_GREEN_7_6, (green | (green << 4))); + + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_BLUE_1_0, (blue | (blue << 4))); + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_BLUE_3_2, (blue | (blue << 4))); + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_BLUE_5_4, (blue | (blue << 4))); + superio_outb(msi_sioaddr, MSI_SIO_RGB_REG_BLUE_7_6, (blue | (blue << 4))); +} diff --git a/Controllers/MSIRGBController/MSIRGBController.h b/Controllers/MSIRGBController/MSIRGBController.h new file mode 100644 index 0000000..b448525 --- /dev/null +++ b/Controllers/MSIRGBController/MSIRGBController.h @@ -0,0 +1,58 @@ +/*---------------------------------------------------------*\ +| MSIRGBController.h | +| | +| Driver for MSI-RGB motherboard | +| | +| Logic adapted from https://github.com/nagisa/msi-rgb | +| | +| Adam Honse (CalcProgrammer1) 11 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +#define MSI_SIO_LOGDEV_RGB 0x12 + +enum +{ + MSI_SIO_RGB_REG_ENABLE = 0xE0, + MSI_SIO_RGB_REG_CFG_1 = 0xE4, + MSI_SIO_RGB_REG_RED_1_0 = 0xF0, + MSI_SIO_RGB_REG_RED_3_2 = 0xF1, + MSI_SIO_RGB_REG_RED_5_4 = 0xF2, + MSI_SIO_RGB_REG_RED_7_6 = 0xF3, + MSI_SIO_RGB_REG_GREEN_1_0 = 0xF4, + MSI_SIO_RGB_REG_GREEN_3_2 = 0xF5, + MSI_SIO_RGB_REG_GREEN_5_4 = 0xF6, + MSI_SIO_RGB_REG_GREEN_7_6 = 0xF7, + MSI_SIO_RGB_REG_BLUE_1_0 = 0xF8, + MSI_SIO_RGB_REG_BLUE_3_2 = 0xF9, + MSI_SIO_RGB_REG_BLUE_5_4 = 0xFA, + MSI_SIO_RGB_REG_BLUE_7_6 = 0xFB, + MSI_SIO_RGB_REG_CFG_2 = 0xFE, + MSI_SIO_RGB_REG_CFG_3 = 0xFF, +}; + +#define MSI_SIO_RGB_ENABLE_MASK 0xE0 + +class MSIRGBController +{ +public: + MSIRGBController(int sioaddr, bool invert, std::string dev_name); + ~MSIRGBController(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + + unsigned int GetMode(); + void SetMode(unsigned char new_mode, unsigned char new_speed); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue); +private: + int msi_sioaddr; + std::string name; +}; diff --git a/Controllers/MSIRGBController/MSIRGBControllerDetect.cpp b/Controllers/MSIRGBController/MSIRGBControllerDetect.cpp new file mode 100644 index 0000000..ca41418 --- /dev/null +++ b/Controllers/MSIRGBController/MSIRGBControllerDetect.cpp @@ -0,0 +1,129 @@ +/*---------------------------------------------------------*\ +| MSIRGBControllerDetect.cpp | +| | +| Detector for MSI-RGB motherboard | +| | +| Adam Honse (CalcProgrammer1) 11 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "MSIRGBController.h" +#include "RGBController_MSIRGB.h" +#include "super_io.h" +#include "dmiinfo.h" + +/******************************************************************************************\ +* * +* DetectMSIRGBControllers * +* * +* Detect MSI-RGB compatible Super-IO chips. * +* * +\******************************************************************************************/ + +#define NUM_COMPATIBLE_DEVICES (sizeof(compatible_devices) / sizeof(compatible_devices[0])) + +typedef struct +{ + const char* name; + bool invert; +} msi_device; + +static msi_device compatible_devices[] = +{ + {"7A40", false}, + {"7A34", false}, + {"7A39", false}, + {"7A38", false}, + {"7B79", false}, + {"7B73", false}, + {"7B61", false}, + {"7B54", false}, + {"7B49", false}, + {"7B48", false}, + {"7B45", false}, + {"7B44", false}, + {"7A59", false}, + {"7A57", false}, + {"7A68", false}, + {"7B40", false}, + {"7A94", false}, + {"7B06", false}, + {"7B08", false}, // B350 KRAIT GAMING (MS-7B08) + {"7B09", false}, + {"7A58", false}, + {"7A62", false}, + {"7A69", false}, + {"7A70", false}, + {"7A72", false}, + {"7A78", false}, + {"7A79", false}, + {"7A37", false}, + {"7B89", true }, + {"7B90", true }, + {"7B19", true }, + {"7C02", true }, + {"7B75", true }, + {"7B22", true }, + {"7B23", true }, + {"7B24", true }, + {"7B27", true }, + {"7B30", true }, + {"7B31", true }, + {"7B51", true }, + {"7C04", true }, + {"7C00", true }, + {"7B98", true }, + {"7C22", true }, + {"7C24", true }, + {"7C01", true }, + {"7C39", true }, + {"7B86", true }, + {"7B87", true }, + {"7D95", false} +}; + +void DetectMSIRGBControllers() +{ + int sio_addrs[2] = {0x2E, 0x4E}; + + DMIInfo board; + std::string board_dmi = board.getMainboard(); + std::string manufacturer = board.getManufacturer(); + + if (manufacturer != "Micro-Star International Co., Ltd." && manufacturer != "Micro-Star International Co., Ltd" && manufacturer != "MSI") + { + return; + } + + for(int sioaddr_idx = 0; sioaddr_idx < 2; sioaddr_idx++) + { + int sioaddr = sio_addrs[sioaddr_idx]; + + superio_enter(sioaddr); + + int val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) | superio_inb(sioaddr, SIO_REG_DEVID + 1); + + switch (val & SIO_ID_MASK) + { + case SIO_NCT6795_ID: + case SIO_NCT6797_ID: + for(unsigned int i = 0; i < NUM_COMPATIBLE_DEVICES; i++) + { + if (board_dmi.find(std::string(compatible_devices[i].name)) != std::string::npos) + { + MSIRGBController* controller = new MSIRGBController(sioaddr, compatible_devices[i].invert, "MSI " + board_dmi); + RGBController_MSIRGB* rgb_controller = new RGBController_MSIRGB(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + break; + } + } + break; + } + } +} /* DetectMSIRGBControllers() */ + +REGISTER_DETECTOR("MSI-RGB", DetectMSIRGBControllers); diff --git a/Controllers/MSIRGBController/RGBController_MSIRGB.cpp b/Controllers/MSIRGBController/RGBController_MSIRGB.cpp new file mode 100644 index 0000000..08dfd3a --- /dev/null +++ b/Controllers/MSIRGBController/RGBController_MSIRGB.cpp @@ -0,0 +1,96 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIRGB.cpp | +| | +| RGBController for MSI-RGB motherboard | +| | +| Adam Honse (CalcProgrammer1) 14 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MSIRGB.h" + +/**------------------------------------------------------------------*\ + @name MSI RGB + @category Motherboard + @type SuperIO + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectMSIRGBControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MSIRGB::RGBController_MSIRGB(MSIRGBController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "MSI"; + type = DEVICE_TYPE_MOTHERBOARD; + description = "MSI-RGB Device"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_MSIRGB::~RGBController_MSIRGB() +{ + delete controller; +} + +void RGBController_MSIRGB::SetupZones() +{ + zone msi_zone; + msi_zone.name = "MSI Zone"; + msi_zone.type = ZONE_TYPE_SINGLE; + msi_zone.leds_min = 1; + msi_zone.leds_max = 1; + msi_zone.leds_count = 1; + msi_zone.matrix_map = NULL; + zones.push_back(msi_zone); + + led msi_led; + msi_led.name = "MSI LED"; + leds.push_back(msi_led); + + SetupColors(); +} + +void RGBController_MSIRGB::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_MSIRGB::DeviceUpdateLEDs() +{ + RGBColor color = colors[0]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetColor(red, grn, blu); +} + +void RGBController_MSIRGB::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIRGB::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MSIRGB::DeviceUpdateMode() +{ + +} diff --git a/Controllers/MSIRGBController/RGBController_MSIRGB.h b/Controllers/MSIRGBController/RGBController_MSIRGB.h new file mode 100644 index 0000000..d8fce53 --- /dev/null +++ b/Controllers/MSIRGBController/RGBController_MSIRGB.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIRGB.h | +| | +| RGBController for MSI-RGB motherboard | +| | +| Adam Honse (CalcProgrammer1) 14 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "MSIRGBController.h" + +class RGBController_MSIRGB : public RGBController +{ +public: + RGBController_MSIRGB(MSIRGBController* controller_ptr); + ~RGBController_MSIRGB(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + MSIRGBController* controller; +}; diff --git a/Controllers/MSIVigorController/MSIVigorControllerDetect.cpp b/Controllers/MSIVigorController/MSIVigorControllerDetect.cpp new file mode 100644 index 0000000..f63d329 --- /dev/null +++ b/Controllers/MSIVigorController/MSIVigorControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| MSIVigorControllerDetect.cpp | +| | +| Detector for MSI Vigor | +| | +| Morgan Guimard (morg) 01 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "MSIVigorGK30Controller.h" +#include "RGBController_MSIVigorGK30.h" + +/*---------------------------------------------------------*\ +| MSI vendor ID | +\*---------------------------------------------------------*/ +#define MSI_VID 0x0DB0 + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define MSI_VIGOR_GK30_PID 0x0B30 + +void DetectMSIVigorGK30Controllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + MSIVigorGK30Controller* controller = new MSIVigorGK30Controller(dev, *info, name); + RGBController_MSIVigorGK30* rgb_controller = new RGBController_MSIVigorGK30(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("MSI Vigor GK30 controller", DetectMSIVigorGK30Controllers, MSI_VID, MSI_VIGOR_GK30_PID, 1, 0xFF01, 1); diff --git a/Controllers/MSIVigorController/MSIVigorGK30Controller.cpp b/Controllers/MSIVigorController/MSIVigorGK30Controller.cpp new file mode 100644 index 0000000..b00db06 --- /dev/null +++ b/Controllers/MSIVigorController/MSIVigorGK30Controller.cpp @@ -0,0 +1,227 @@ +/*---------------------------------------------------------*\ +| MSIVigorGK30Controller.cpp | +| | +| Driver for MSI Vigor GK30 | +| | +| Morgan Guimard (morg) 01 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "MSIVigorGK30Controller.h" +#include "StringUtils.h" + +static unsigned char argb_colour_index_data[2][2][2] = +{ //B0 B1 + { { 0x00, 0x04 }, //G0 R0 + { 0x02, 0x03 }, }, //G1 R0 + { { 0x00, 0x05 }, //G0 R1 + { 0x01, 0x06 }, } //G1 R1 +}; + +MSIVigorGK30Controller::MSIVigorGK30Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +MSIVigorGK30Controller::~MSIVigorGK30Controller() +{ + hid_close(dev); +} + +std::string MSIVigorGK30Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MSIVigorGK30Controller::GetNameString() +{ + return(name); +} + +std::string MSIVigorGK30Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned int MSIVigorGK30Controller::GetLargestColour(unsigned int red, unsigned int green, unsigned int blue) +{ + unsigned int largest; + + if ( red > green ) + { + ( red > blue ) ? largest = red : largest = blue; + } + else + { + ( green > blue ) ? largest = green : largest = blue; + } + + return (largest == 0) ? 1 : largest; +} + +unsigned char MSIVigorGK30Controller::GetColourIndex(unsigned char red, unsigned char green, unsigned char blue) +{ + /*-----------------------------------------------------*\ + | This device uses a limited colour pallette referenced | + | by an index | + | 0x00 red | + | 0x01 yellow | + | 0x02 green | + | 0x03 cyan | + | 0x04 blue | + | 0x05 magen | + | 0x06 white | + \*-----------------------------------------------------*/ + unsigned int divisor = GetLargestColour( red, green, blue); + unsigned int r = (unsigned int)round( red / divisor ); + unsigned int g = (unsigned int)round( green / divisor ); + unsigned int b = (unsigned int)round( blue / divisor ); + unsigned char idx = argb_colour_index_data[r][g][b]; + return idx; +} + +void MSIVigorGK30Controller::SetMode(std::vector colors, unsigned char brightness, unsigned char speed, unsigned char mode_value, unsigned int mode_flags, unsigned int color_mode, unsigned char direction) +{ + unsigned char usb_buf[MSI_VIGOR_GK30_REPORT_SIZE]; + + memset(usb_buf, 0x00, MSI_VIGOR_GK30_REPORT_SIZE); + + usb_buf[0x00] = MSI_VIGOR_GK30_REPORT_ID; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = mode_value; + + /*-----------------------------------------------------*\ + | Parse one color for specific color mode | + \*-----------------------------------------------------*/ + if(color_mode == MODE_COLORS_MODE_SPECIFIC) + { + /*-----------------------------------------------------*\ + | Use OFF mode if color is black, black isnt an indexed | + | color | + \*-----------------------------------------------------*/ + if(colors[0] == 0) + { + usb_buf[0x02] = MSI_VIGOR_GK30_OFF_MODE_VALUE; + + hid_send_feature_report(dev, usb_buf, MSI_VIGOR_GK30_REPORT_SIZE); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + return; + } + + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + usb_buf[0x03] = GetColourIndex(red, grn, blu); + } + else if(color_mode == MODE_COLORS_PER_LED) + { + /*-----------------------------------------------------*\ + | Use OFF mode if color is black, black isnt an indexed | + | color | + \*-----------------------------------------------------*/ + unsigned int black_count = 0; + + for(unsigned int i = 0; i < MSI_VIGOR_GK30_LEDS_COUNT; i ++) + { + if(colors[i] == 0) + { + black_count++; + } + } + + if( black_count == MSI_VIGOR_GK30_LEDS_COUNT ) + { + usb_buf[0x02] = MSI_VIGOR_GK30_OFF_MODE_VALUE; + + hid_send_feature_report(dev, usb_buf, MSI_VIGOR_GK30_REPORT_SIZE); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + return; + } + + /*-----------------------------------------------------*\ + | Parse and set the 6 colors in the buffer | + \*-----------------------------------------------------*/ + unsigned int offset = 0; + + for(unsigned int i = 0; i < MSI_VIGOR_GK30_LEDS_COUNT; i ++) + { + unsigned char red = RGBGetRValue(colors[i]); + unsigned char grn = RGBGetGValue(colors[i]); + unsigned char blu = RGBGetBValue(colors[i]); + + unsigned char index = GetColourIndex(red, grn, blu); + + if(offset % 2 == 0) + { + index <<= 4; + } + + usb_buf[0x03 + offset/2] += index; + + offset++; + } + } + + /*-----------------------------------------------------*\ + | color index is shifted in breathing mode | + | 0x00 becomes random | + \*-----------------------------------------------------*/ + if(mode_value == MSI_VIGOR_GK30_BREATHING_MODE_VALUE) + { + if(color_mode == MODE_COLORS_RANDOM) + { + usb_buf[0x03] = 0x00; + } + else + { + usb_buf[0x03]++; + } + } + + /*-----------------------------------------------------*\ + | Direction byte | + \*-----------------------------------------------------*/ + if(mode_flags & MODE_FLAG_HAS_DIRECTION_LR) + { + usb_buf[0x04] = direction; + } + + /*-----------------------------------------------------*\ + | Speed byte | + \*-----------------------------------------------------*/ + if(mode_flags & MODE_FLAG_HAS_SPEED) + { + usb_buf[0x05] = speed; + } + + /*-----------------------------------------------------*\ + | Brightness byte | + \*-----------------------------------------------------*/ + if(mode_flags & MODE_FLAG_HAS_BRIGHTNESS) + { + usb_buf[0x06] = brightness; + } + + hid_send_feature_report(dev, usb_buf, MSI_VIGOR_GK30_REPORT_SIZE); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); +} diff --git a/Controllers/MSIVigorController/MSIVigorGK30Controller.h b/Controllers/MSIVigorController/MSIVigorGK30Controller.h new file mode 100644 index 0000000..95075b5 --- /dev/null +++ b/Controllers/MSIVigorController/MSIVigorGK30Controller.h @@ -0,0 +1,76 @@ +/*---------------------------------------------------------*\ +| MSIVigorGK30Controller.h | +| | +| Driver for MSI Vigor GK30 | +| | +| Morgan Guimard (morg) 01 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define MSI_VIGOR_GK30_REPORT_SIZE 8 +#define MSI_VIGOR_GK30_LEDS_COUNT 6 +#define MSI_VIGOR_GK30_REPORT_ID 0x07 + +enum +{ + MSI_VIGOR_GK30_OFF_MODE_VALUE = 0x00, + MSI_VIGOR_GK30_STATIC_MODE_VALUE = 0x10, + MSI_VIGOR_GK30_BREATHING_MODE_VALUE = 0x20, + MSI_VIGOR_GK30_RAINBOW_MODE_VALUE = 0x30, + MSI_VIGOR_GK30_METEOR_MODE_VALUE = 0x40, + MSI_VIGOR_GK30_RIPPLE_MODE_VALUE = 0x50, + MSI_VIGOR_GK30_DIMMING_MODE_VALUE = 0x60, + MSI_VIGOR_GK30_CUSTOM_MODE_VALUE = 0x70 +}; + +enum +{ + MSI_VIGOR_GK30_BRIGHTNESS_MIN = 0x01, + MSI_VIGOR_GK30_BRIGHTNESS_MAX = 0x03 +}; + +enum +{ + MSI_VIGOR_GK30_SPEED_MIN = 0x01, + MSI_VIGOR_GK30_SPEED_MAX = 0x03 +}; + +class MSIVigorGK30Controller +{ +public: + MSIVigorGK30Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~MSIVigorGK30Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetMode(std::vector colors, + unsigned char brightness, + unsigned char speed, + unsigned char mode_value, + unsigned int mode_flags, + unsigned int color_mode, + unsigned char direction + ); + +protected: + hid_device* dev; + +private: + + unsigned int GetLargestColour(unsigned int red, unsigned int green, unsigned int blue); + unsigned char GetColourIndex(unsigned char red, unsigned char green, unsigned char blue); + + std::string location; + std::string name; + std::string serial_number; +}; diff --git a/Controllers/MSIVigorController/RGBController_MSIVigorGK30.cpp b/Controllers/MSIVigorController/RGBController_MSIVigorGK30.cpp new file mode 100644 index 0000000..af21761 --- /dev/null +++ b/Controllers/MSIVigorController/RGBController_MSIVigorGK30.cpp @@ -0,0 +1,202 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIVigorGK30.cpp | +| | +| RGBController for MSI Vigor GK30 | +| | +| Morgan Guimard (morg) 01 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_MSIVigorGK30.h" + +/**------------------------------------------------------------------*\ + @name MSI Vigor GK30 + @category Keyboard + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectMSIVigorGK30Controllers + @comment This device does only support 7 different colors +\*-------------------------------------------------------------------*/ + +RGBController_MSIVigorGK30::RGBController_MSIVigorGK30(MSIVigorGK30Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "MSI"; + type = DEVICE_TYPE_KEYBOARD; + description = "MSI VigorGK30 USB Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Custom; + Custom.name = "Custom"; + Custom.value = MSI_VIGOR_GK30_CUSTOM_MODE_VALUE; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Custom.brightness = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Custom.brightness_min = MSI_VIGOR_GK30_BRIGHTNESS_MIN; + Custom.brightness_max = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode Static; + Static.name = "Static"; + Static.value = MSI_VIGOR_GK30_STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.brightness = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Static.brightness_min = MSI_VIGOR_GK30_BRIGHTNESS_MIN; + Static.brightness_max = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = MSI_VIGOR_GK30_OFF_MODE_VALUE; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = MSI_VIGOR_GK30_BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.brightness = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Breathing.brightness_min = MSI_VIGOR_GK30_BRIGHTNESS_MIN; + Breathing.brightness_max = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Breathing.speed = MSI_VIGOR_GK30_SPEED_MIN; + Breathing.speed_min = MSI_VIGOR_GK30_SPEED_MIN; + Breathing.speed_max = MSI_VIGOR_GK30_SPEED_MAX; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = MSI_VIGOR_GK30_RAINBOW_MODE_VALUE; + Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Rainbow.brightness_min = MSI_VIGOR_GK30_BRIGHTNESS_MIN; + Rainbow.brightness_max = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Rainbow.speed = MSI_VIGOR_GK30_SPEED_MIN; + Rainbow.speed_min = MSI_VIGOR_GK30_SPEED_MIN; + Rainbow.speed_max = MSI_VIGOR_GK30_SPEED_MAX; + Rainbow.direction = MODE_DIRECTION_LEFT; + modes.push_back(Rainbow); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = MSI_VIGOR_GK30_METEOR_MODE_VALUE; + Meteor.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_DIRECTION_LR; + Meteor.color_mode = MODE_COLORS_NONE; + Meteor.brightness = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Meteor.brightness_min = MSI_VIGOR_GK30_BRIGHTNESS_MIN; + Meteor.brightness_max = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Meteor.speed = MSI_VIGOR_GK30_SPEED_MIN; + Meteor.speed_min = MSI_VIGOR_GK30_SPEED_MIN; + Meteor.speed_max = MSI_VIGOR_GK30_SPEED_MAX; + Meteor.direction = MODE_DIRECTION_LEFT; + modes.push_back(Meteor); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = MSI_VIGOR_GK30_RIPPLE_MODE_VALUE; + Ripple.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Ripple.brightness = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Ripple.brightness_min = MSI_VIGOR_GK30_BRIGHTNESS_MIN; + Ripple.brightness_max = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Ripple.speed = MSI_VIGOR_GK30_SPEED_MIN; + Ripple.speed_min = MSI_VIGOR_GK30_SPEED_MIN; + Ripple.speed_max = MSI_VIGOR_GK30_SPEED_MAX; + Ripple.color_mode = MODE_COLORS_NONE; + modes.push_back(Ripple); + + + mode Dimming; + Dimming.name = "Dimming"; + Dimming.value = MSI_VIGOR_GK30_DIMMING_MODE_VALUE; + Dimming.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Dimming.brightness = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Dimming.brightness_min = MSI_VIGOR_GK30_BRIGHTNESS_MIN; + Dimming.brightness_max = MSI_VIGOR_GK30_BRIGHTNESS_MAX; + Dimming.speed = MSI_VIGOR_GK30_SPEED_MIN; + Dimming.speed_min = MSI_VIGOR_GK30_SPEED_MIN; + Dimming.speed_max = MSI_VIGOR_GK30_SPEED_MAX; + Dimming.color_mode = MODE_COLORS_MODE_SPECIFIC; + Dimming.colors.resize(1); + modes.push_back(Dimming); + + SetupZones(); +} + +RGBController_MSIVigorGK30::~RGBController_MSIVigorGK30() +{ + delete controller; +} + +void RGBController_MSIVigorGK30::SetupZones() +{ + zone new_zone; + + new_zone.name = "Keyboard"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = MSI_VIGOR_GK30_LEDS_COUNT; + new_zone.leds_max = MSI_VIGOR_GK30_LEDS_COUNT; + new_zone.leds_count = MSI_VIGOR_GK30_LEDS_COUNT; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(new_zone.leds_count); + + for(unsigned int i = 0; i < MSI_VIGOR_GK30_LEDS_COUNT; i++) + { + leds[i].name = "LED " + std::to_string(i); + } + + SetupColors(); +} + +void RGBController_MSIVigorGK30::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_MSIVigorGK30::DeviceUpdateLEDs() +{ + UpdateSingleLED(0); +} + +void RGBController_MSIVigorGK30::UpdateZoneLEDs(int /*zone*/) +{ + UpdateSingleLED(0); +} + +void RGBController_MSIVigorGK30::UpdateSingleLED(int /*led*/) +{ + const mode& active = modes[active_mode]; + + controller->SetMode( + active.color_mode == MODE_COLORS_MODE_SPECIFIC ? active.colors : colors, + active.brightness, + active.speed, + active.value, + active.flags, + active.color_mode, + active.direction + ); +} + +void RGBController_MSIVigorGK30::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/MSIVigorController/RGBController_MSIVigorGK30.h b/Controllers/MSIVigorController/RGBController_MSIVigorGK30.h new file mode 100644 index 0000000..a82c10e --- /dev/null +++ b/Controllers/MSIVigorController/RGBController_MSIVigorGK30.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_MSIVigorGK30.h | +| | +| RGBController for MSI Vigor GK30 | +| | +| Morgan Guimard (morg) 01 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "MSIVigorGK30Controller.h" + +class RGBController_MSIVigorGK30 : public RGBController +{ +public: + RGBController_MSIVigorGK30(MSIVigorGK30Controller* controller_ptr); + ~RGBController_MSIVigorGK30(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + MSIVigorGK30Controller* controller; +}; diff --git a/Controllers/MadCatzCyborgController/MadCatzCyborgController.cpp b/Controllers/MadCatzCyborgController/MadCatzCyborgController.cpp new file mode 100644 index 0000000..a5c83cc --- /dev/null +++ b/Controllers/MadCatzCyborgController/MadCatzCyborgController.cpp @@ -0,0 +1,99 @@ +/*---------------------------------------------------------*\ +| MadCatzCyborgController.cpp | +| | +| Driver for MadCatz Cyborg Gaming Light | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "MadCatzCyborgController.h" +#include "StringUtils.h" +#include + +MadCatzCyborgController::MadCatzCyborgController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; +} + +MadCatzCyborgController::~MadCatzCyborgController() +{ + if(dev != nullptr) + { + hid_close(dev); + dev = nullptr; + } +} + +std::string MadCatzCyborgController::GetDeviceLocation() +{ + return(location); +} + +std::string MadCatzCyborgController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void MadCatzCyborgController::Initialize() +{ + if(dev == nullptr) + { + return; + } + + // Enable the device + unsigned char enable_buf[2] = { CMD_ENABLE, 0x00 }; + hid_send_feature_report(dev, enable_buf, 2); +} + +void MadCatzCyborgController::SetLEDColor(unsigned char red, unsigned char green, unsigned char blue) +{ + if(dev == nullptr) + { + return; + } + + // Format: [CMD_COLOR][0x00][R][G][B][0x00][0x00][0x00][0x00] + unsigned char usb_buf[9] = + { + CMD_COLOR, + 0x00, + red, + green, + blue, + 0x00, + 0x00, + 0x00, + 0x00 + }; + + hid_send_feature_report(dev, usb_buf, 9); +} + +void MadCatzCyborgController::SetIntensity(unsigned char intensity) +{ + if(dev == nullptr) + { + return; + } + + // Clamp intensity to 0-100 + if(intensity > 100) + { + intensity = 100; + } + + // Format: [CMD_INTENSITY][0x00][intensity_value] + unsigned char usb_buf[3] = { CMD_INTENSITY, 0x00, intensity }; + hid_send_feature_report(dev, usb_buf, 3); +} diff --git a/Controllers/MadCatzCyborgController/MadCatzCyborgController.h b/Controllers/MadCatzCyborgController/MadCatzCyborgController.h new file mode 100644 index 0000000..e9e4150 --- /dev/null +++ b/Controllers/MadCatzCyborgController/MadCatzCyborgController.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| MadCatzCyborgController.h | +| | +| Driver for MadCatz Cyborg Gaming Light | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +class MadCatzCyborgController +{ +public: + MadCatzCyborgController(hid_device* dev_handle, const char* path); + ~MadCatzCyborgController(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + + void Initialize(); + void SetLEDColor(unsigned char red, unsigned char green, unsigned char blue); + void SetIntensity(unsigned char intensity); + +private: + hid_device* dev; + std::string location; + + // Protocol constants + enum Commands + { + CMD_ENABLE = 0xA1, + CMD_COLOR = 0xA2, + CMD_INTENSITY = 0xA6 + }; +}; diff --git a/Controllers/MadCatzCyborgController/MadCatzCyborgControllerDetect.cpp b/Controllers/MadCatzCyborgController/MadCatzCyborgControllerDetect.cpp new file mode 100644 index 0000000..59129c3 --- /dev/null +++ b/Controllers/MadCatzCyborgController/MadCatzCyborgControllerDetect.cpp @@ -0,0 +1,44 @@ +/*---------------------------------------------------------*\ +| MadCatzCyborgControllerDetect.cpp | +| | +| Detector for MadCatz Cyborg Gaming Light | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "MadCatzCyborgController.h" +#include "RGBController_MadCatzCyborg.h" +#include + +/*-----------------------------------------------------*\ +| MadCatz Cyborg VID/PID | +\*-----------------------------------------------------*/ +#define MADCATZ_VID 0x06A3 +#define MADCATZ_CYBORG_PID 0x0DC5 + +/******************************************************************************************\ +* * +* DetectMadCatzCyborgControllers * +* * +* Tests the USB address to find MadCatz Cyborg Gaming Light devices * +* * +\******************************************************************************************/ + +void DetectMadCatzCyborgControllers(hid_device_info* info, const std::string& /*name*/) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + MadCatzCyborgController* controller = new MadCatzCyborgController(dev, info->path); + controller->Initialize(); + + RGBController_MadCatzCyborg* rgb_controller = new RGBController_MadCatzCyborg(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("MadCatz Cyborg Gaming Light", DetectMadCatzCyborgControllers, MADCATZ_VID, MADCATZ_CYBORG_PID); diff --git a/Controllers/MadCatzCyborgController/RGBController_MadCatzCyborg.cpp b/Controllers/MadCatzCyborgController/RGBController_MadCatzCyborg.cpp new file mode 100644 index 0000000..7c1bb4e --- /dev/null +++ b/Controllers/MadCatzCyborgController/RGBController_MadCatzCyborg.cpp @@ -0,0 +1,104 @@ +/*---------------------------------------------------------*\ +| RGBController_MadCatzCyborg.cpp | +| | +| RGB Controller for MadCatz Cyborg Gaming Light | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_MadCatzCyborg.h" + +/**--------------------------------------------------------*\ + @name MadCatz Cyborg Gaming Light + @category Accessory + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectMadCatzCyborgControllers + @comment The MadCatz Cyborg Gaming Light is an ambient lighting device. +\*---------------------------------------------------------*/ + +RGBController_MadCatzCyborg::RGBController_MadCatzCyborg(MadCatzCyborgController* controller_ptr) +{ + controller = controller_ptr; + + name = "MadCatz Cyborg Gaming Light"; + vendor = "MadCatz"; + type = DEVICE_TYPE_ACCESSORY; + description = "MadCatz Cyborg Gaming Light"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.brightness = 100; + modes.push_back(Direct); + + SetupZones(); + + controller->SetIntensity(modes[active_mode].brightness); +} + +RGBController_MadCatzCyborg::~RGBController_MadCatzCyborg() +{ + delete controller; +} + +void RGBController_MadCatzCyborg::SetupZones() +{ + zone cyborg_zone; + + cyborg_zone.name = "Cyborg"; + cyborg_zone.type = ZONE_TYPE_SINGLE; + cyborg_zone.leds_min = 1; + cyborg_zone.leds_max = 1; + cyborg_zone.leds_count = 1; + cyborg_zone.matrix_map = NULL; + + zones.push_back(cyborg_zone); + + led cyborg_led; + cyborg_led.name = "LED"; + leds.push_back(cyborg_led); + + SetupColors(); +} + +void RGBController_MadCatzCyborg::ResizeZone(int /*zone*/, int /*new_size*/) +{ + // Single LED device - nothing to resize +} + +void RGBController_MadCatzCyborg::DeviceUpdateLEDs() +{ + if(colors.size() > 0) + { + RGBColor color = colors[0]; + controller->SetLEDColor(RGBGetRValue(color), RGBGetGValue(color), RGBGetBValue(color)); + } +} + +void RGBController_MadCatzCyborg::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MadCatzCyborg::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MadCatzCyborg::DeviceUpdateMode() +{ + if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + controller->SetIntensity(modes[active_mode].brightness); + } +} diff --git a/Controllers/MadCatzCyborgController/RGBController_MadCatzCyborg.h b/Controllers/MadCatzCyborgController/RGBController_MadCatzCyborg.h new file mode 100644 index 0000000..7a3510c --- /dev/null +++ b/Controllers/MadCatzCyborgController/RGBController_MadCatzCyborg.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| RGBController_MadCatzCyborg.h | +| | +| RGB Controller for MadCatz Cyborg Gaming Light | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "MadCatzCyborgController.h" + +class RGBController_MadCatzCyborg : public RGBController +{ +public: + RGBController_MadCatzCyborg(MadCatzCyborgController* controller_ptr); + ~RGBController_MadCatzCyborg(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + MadCatzCyborgController* controller; +}; diff --git a/Controllers/ManliGPUController/ManliGPUController.cpp b/Controllers/ManliGPUController/ManliGPUController.cpp new file mode 100644 index 0000000..8ed74a5 --- /dev/null +++ b/Controllers/ManliGPUController/ManliGPUController.cpp @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| ManliGPUController.cpp | +| | +| Driver for Manli GPU RGB controllers | +| | +| Based on ZotacV2GPUController | +| Adapted for Manli RTX 4090 Gallardo | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "ManliGPUController.h" + +ManliGPUController::ManliGPUController(i2c_smbus_interface* bus, u8 dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; + + if(dev) + { + ReadVersion(); + } +} + +ManliGPUController::~ManliGPUController() +{ +} + +std::string ManliGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return ("I2C: " + return_string); +} + +std::string ManliGPUController::GetName() +{ + return(name); +} + +std::string ManliGPUController::GetVersion() +{ + return(version); +} + +bool ManliGPUController::ReadVersion() +{ + u8 data_pkt[] = { MANLI_GPU_REG_RGB, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + if(bus->i2c_write_block(dev, sizeof(data_pkt), data_pkt) < 0) + { + return false; + } + + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + if(bus->i2c_read_block(dev, &rdata_len, rdata_pkt) < 0) + { + return false; + } + + version = std::string((char*)rdata_pkt); + return true; +} + +bool ManliGPUController::TurnOnOff(bool on) +{ + ManliGPUZone zoneConfig; + return SendCommand(on, zoneConfig); +} + +bool ManliGPUController::SetMode(ManliGPUZone zoneConfig) +{ + return SendCommand(true, zoneConfig); +} + +bool ManliGPUController::SendCommand(bool on, ManliGPUZone zoneConfig) +{ + /*---------------------------------------------------------*\ + | Color Cycle: Uses breathing mode (0x01) with flag 0x07 | + | This cycles through colors with brightness and speed | + \*---------------------------------------------------------*/ + if(zoneConfig.mode == MANLI_GPU_MODE_COLOR_CYCLE) + { + u8 data_pkt[30] = { 0x00 }; + data_pkt[0] = on ? (u8)0x01 : (u8)0x00; + data_pkt[6] = 0x01; // mode = breathing + data_pkt[7] = 0x00; // R + data_pkt[8] = 0x00; // G + data_pkt[9] = 0x00; // B + data_pkt[10] = (u8)zoneConfig.speed; + data_pkt[11] = (u8)zoneConfig.brightness; + data_pkt[13] = 0x07; // color cycle flag + + if(bus->i2c_smbus_write_i2c_block_data(dev, MANLI_GPU_REG_RGB, sizeof(data_pkt), data_pkt) < 0) + { + return false; + } + return true; + } + /*---------------------------------------------------------*\ + | Standard modes (Static, Breathing, Wave, Strobing, Rainbow)| + \*---------------------------------------------------------*/ + else + { + u8 data_pkt[30] = { 0x00 }; + data_pkt[0] = on ? (u8)0x01 : (u8)0x00; + data_pkt[6] = (u8)zoneConfig.mode; + data_pkt[7] = (u8)RGBGetRValue(zoneConfig.color1); + data_pkt[8] = (u8)RGBGetGValue(zoneConfig.color1); + data_pkt[9] = (u8)RGBGetBValue(zoneConfig.color1); + data_pkt[10] = (u8)zoneConfig.speed; + data_pkt[11] = (u8)zoneConfig.brightness; + + if(bus->i2c_smbus_write_i2c_block_data(dev, MANLI_GPU_REG_RGB, sizeof(data_pkt), data_pkt) < 0) + { + return false; + } + return true; + } +} diff --git a/Controllers/ManliGPUController/ManliGPUController.h b/Controllers/ManliGPUController/ManliGPUController.h new file mode 100644 index 0000000..3fe029a --- /dev/null +++ b/Controllers/ManliGPUController/ManliGPUController.h @@ -0,0 +1,70 @@ +/*---------------------------------------------------------*\ +| ManliGPUController.h | +| | +| Driver for Manli GPU RGB controllers | +| | +| Based on ZotacV2GPUController | +| Adapted for Manli RTX 4090 Gallardo | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +enum +{ + MANLI_GPU_REG_RGB = 0xA0, +}; + +enum +{ + MANLI_GPU_MODE_STATIC = 0x00, // Static color + MANLI_GPU_MODE_BREATHING = 0x01, // Breathing effect + MANLI_GPU_MODE_WAVE = 0x02, // Wave effect (no brightness - HW limitation) + MANLI_GPU_MODE_STROBING = 0x03, // Strobing effect + MANLI_GPU_MODE_RAINBOW = 0x08, // Rainbow effect (no brightness - HW limitation) + MANLI_GPU_MODE_COLOR_CYCLE = 0x10, // Color Cycle (breathing + flag 0x07) +}; + +struct ManliGPUConfig +{ + int numberOfZones = 0; + bool supportsExternalLEDStrip = false; +}; + +struct ManliGPUZone +{ + int mode = 0; + RGBColor color1 = ToRGBColor(0, 0, 0); + unsigned int speed = 0; + unsigned int brightness = 0; +}; + +class ManliGPUController +{ +public: + ManliGPUController(i2c_smbus_interface* bus, u8 dev, std::string dev_name); + ~ManliGPUController(); + + std::string GetDeviceLocation(); + std::string GetName(); + std::string GetVersion(); + + bool TurnOnOff(bool on); + bool SetMode(ManliGPUZone zoneConfig); + +private: + i2c_smbus_interface* bus; + u8 dev; + std::string name; + std::string version; + + bool ReadVersion(); + bool SendCommand(bool on, ManliGPUZone zoneConfig); +}; + diff --git a/Controllers/ManliGPUController/ManliGPUControllerDetect.cpp b/Controllers/ManliGPUController/ManliGPUControllerDetect.cpp new file mode 100644 index 0000000..f7b7189 --- /dev/null +++ b/Controllers/ManliGPUController/ManliGPUControllerDetect.cpp @@ -0,0 +1,61 @@ +/*---------------------------------------------------------*\ +| ManliGPUControllerDetect.cpp | +| | +| Detector for Manli GPU | +| | +| Based on ZotacV2GPUControllerDetect | +| Adapted for Manli RTX 4090 Gallardo | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ManliGPUController.h" +#include "RGBController_ManliGPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" +#include "LogManager.h" + +/******************************************************************************************\ +* * +* DetectManliGPUControllers * +* * +* Detect Manli GPU RGB controllers on the enumerated I2C busses * +* at address 0x49. * +* * +* bus - pointer to i2c_smbus_interface where RGB device is connected * +* dev - I2C address of RGB device * +* * +\******************************************************************************************/ + +void DetectManliGPUControllers(i2c_smbus_interface* bus, u8 i2c_addr, const std::string& name) +{ + u8 data_pkt[] = { MANLI_GPU_REG_RGB, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + if(bus->i2c_write_block(i2c_addr, sizeof(data_pkt), data_pkt) < 0) + { + return; + } + + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + + if(bus->i2c_read_block(i2c_addr, &rdata_len, rdata_pkt) >= 0) + { + ManliGPUController* controller = new ManliGPUController(bus, i2c_addr, name); + RGBController_ManliGPU* rgb_controller = new RGBController_ManliGPU(controller); + + if(rgb_controller->config.numberOfZones > 0) + { + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + LOG_ERROR("[%s] RGB controller not registered - invalid zone count: %d", name.c_str(), rgb_controller->config.numberOfZones); + delete rgb_controller; + } + } +} + +REGISTER_I2C_PCI_DETECTOR("MANLI GeForce RTX 4090 Gallardo", DetectManliGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, NVIDIA_SUB_VEN, MANLI_RTX4090_GALLARDO_SUB_DEV, 0x49); + diff --git a/Controllers/ManliGPUController/RGBController_ManliGPU.cpp b/Controllers/ManliGPUController/RGBController_ManliGPU.cpp new file mode 100644 index 0000000..6535bc9 --- /dev/null +++ b/Controllers/ManliGPUController/RGBController_ManliGPU.cpp @@ -0,0 +1,201 @@ +/*---------------------------------------------------------*\ +| RGBController_ManliGPU.cpp | +| | +| RGBController for Manli GPU | +| | +| Based on RGBController_ZotacV2GPU | +| Adapted for Manli RTX 4090 Gallardo | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_ManliGPU.h" + +std::map MANLI_GPU_CONFIG = +{ + { "N675M-1018", { 1, false } }, // MANLI GeForce RTX 4090 Gallardo +}; + +/**------------------------------------------------------------------*\ + @name Manli GPU + @category GPU + @type I2C + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectManliGPUControllers + @comment + Manli GPU RGB controllers use I2C communication at address 0x49. + Supported modes: Static, Breathing, Wave, Strobing, Rainbow, Color Cycle. +\*-------------------------------------------------------------------*/ + +RGBController_ManliGPU::RGBController_ManliGPU(ManliGPUController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "MANLI"; + description = "MANLI RGB GPU Device (" + controller->GetVersion() + ")"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + if(MANLI_GPU_CONFIG.count(controller->GetVersion()) > 0) + { + config = MANLI_GPU_CONFIG.at(controller->GetVersion()); + } + else + { + config = { 0, false }; + } + + version += std::to_string(config.numberOfZones) + " zones"; + + mode STATIC; + STATIC.name = "Static"; + STATIC.value = MANLI_GPU_MODE_STATIC; + STATIC.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + STATIC.brightness_min = 0; + STATIC.brightness_max = 100; + STATIC.brightness = 100; + STATIC.color_mode = MODE_COLORS_MODE_SPECIFIC; + STATIC.colors_min = 1; + STATIC.colors_max = 1; + STATIC.colors.resize(1); + STATIC.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(STATIC); + + mode BREATHING; + BREATHING.name = "Breathing"; + BREATHING.value = MANLI_GPU_MODE_BREATHING; + BREATHING.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + BREATHING.brightness_min = 0; + BREATHING.brightness_max = 100; + BREATHING.brightness = 100; + BREATHING.speed_min = 0; + BREATHING.speed_max = 100; + BREATHING.speed = 50; + BREATHING.color_mode = MODE_COLORS_MODE_SPECIFIC; + BREATHING.colors_min = 1; + BREATHING.colors_max = 1; + BREATHING.colors.resize(1); + BREATHING.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(BREATHING); + + mode WAVE; + WAVE.name = "Wave"; + WAVE.value = MANLI_GPU_MODE_WAVE; + WAVE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; // No brightness - HW limitation + WAVE.speed_min = 0; + WAVE.speed_max = 100; + WAVE.speed = 50; + WAVE.color_mode = MODE_COLORS_NONE; + modes.push_back(WAVE); + + mode STROBING; + STROBING.name = "Strobing"; + STROBING.value = MANLI_GPU_MODE_STROBING; + STROBING.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + STROBING.brightness_min = 0; + STROBING.brightness_max = 100; + STROBING.brightness = 100; + STROBING.speed_min = 0; + STROBING.speed_max = 100; + STROBING.speed = 50; + STROBING.color_mode = MODE_COLORS_MODE_SPECIFIC; + STROBING.colors_min = 1; + STROBING.colors_max = 1; + STROBING.colors.resize(1); + STROBING.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(STROBING); + + mode RAINBOW; + RAINBOW.name = "Rainbow"; + RAINBOW.value = MANLI_GPU_MODE_RAINBOW; + RAINBOW.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + RAINBOW.speed_min = 0; + RAINBOW.speed_max = 100; + RAINBOW.speed = 50; + RAINBOW.color_mode = MODE_COLORS_NONE; + modes.push_back(RAINBOW); + + mode COLOR_CYCLE; + COLOR_CYCLE.name = "Color Cycle"; + COLOR_CYCLE.value = MANLI_GPU_MODE_COLOR_CYCLE; + COLOR_CYCLE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + COLOR_CYCLE.brightness_min = 0; + COLOR_CYCLE.brightness_max = 100; + COLOR_CYCLE.brightness = 100; + COLOR_CYCLE.speed_min = 0; + COLOR_CYCLE.speed_max = 100; + COLOR_CYCLE.speed = 50; + COLOR_CYCLE.color_mode = MODE_COLORS_NONE; + modes.push_back(COLOR_CYCLE); + + SetupZones(); +} + +RGBController_ManliGPU::~RGBController_ManliGPU() +{ + delete controller; +} + +void RGBController_ManliGPU::SetupZones() +{ + led new_led; + new_led.name = "GPU LED"; + leds.push_back(new_led); + + zone new_zone; + new_zone.name = "GPU Zone"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + SetupColors(); +} + +void RGBController_ManliGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ManliGPU::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_ManliGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_ManliGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_ManliGPU::DeviceUpdateMode() +{ + ManliGPUZone zoneConfig; + zoneConfig.mode = modes[active_mode].value; + zoneConfig.speed = modes[active_mode].speed; + zoneConfig.brightness = modes[active_mode].brightness; + + if(modes[active_mode].colors.size() >= 1) + { + zoneConfig.color1 = modes[active_mode].colors[0]; + } + else + { + zoneConfig.color1 = ToRGBColor(0, 0, 0); + } + + controller->SetMode(zoneConfig); +} diff --git a/Controllers/ManliGPUController/RGBController_ManliGPU.h b/Controllers/ManliGPUController/RGBController_ManliGPU.h new file mode 100644 index 0000000..fb98d83 --- /dev/null +++ b/Controllers/ManliGPUController/RGBController_ManliGPU.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_ManliGPU.h | +| | +| RGBController for Manli GPU | +| | +| Based on RGBController_ZotacV2GPU | +| Adapted for Manli RTX 4090 Gallardo | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ManliGPUController.h" + +class RGBController_ManliGPU : public RGBController +{ +public: + RGBController_ManliGPU(ManliGPUController* controller_ptr); + ~RGBController_ManliGPU(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + ManliGPUConfig config; + +private: + ManliGPUController* controller; +}; + diff --git a/Controllers/MintakaKeyboardController/MintakaKeyboardController.cpp b/Controllers/MintakaKeyboardController/MintakaKeyboardController.cpp new file mode 100644 index 0000000..a635dde --- /dev/null +++ b/Controllers/MintakaKeyboardController/MintakaKeyboardController.cpp @@ -0,0 +1,302 @@ +/*---------------------------------------------------------*\ +| MintakaKeyboardController.cpp | +| | +| Driver for VSG Mintaka Devices keyboard lighting | +| Based on KeychronKeyboardController | +| | +| Federico Scodelaro (pudymody) 08 Oct 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "MintakaKeyboardController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +MintakaKeyboardController::MintakaKeyboardController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + serial_number = ""; + } + else + { + serial_number = StringUtils::wstring_to_string(serial_string); + } +} + +MintakaKeyboardController::~MintakaKeyboardController() +{ + hid_close(dev); +} + +std::string MintakaKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MintakaKeyboardController::GetNameString() +{ + return(name); +} + +std::string MintakaKeyboardController::GetSerialString() +{ + return(serial_number); +} + +void MintakaKeyboardController:: SetLedSequencePositions(std::vector positions) +{ + led_sequence_positions = positions; +} + +void MintakaKeyboardController::SetMode(std::vector modes, int active_mode, std::vector colors) +{ + /*-----------------------------------------*\ + | Turn customization on/off | + | Custom mode needs to turn it on | + \*-----------------------------------------*/ + SetCustomization(modes[active_mode].value == CUSTOM_MODE_VALUE); + + /*-----------------------------------------*\ + | Tells the device we're about to send the | + | pages (18 pages) | + \*-----------------------------------------*/ + StartEffectPage(); + + unsigned char usb_buf[PACKET_DATA_LENGTH]; + + /*-----------------------------------------*\ + | Configure the modes | + | LED Effect Page structure: | + | | + | OK.. this was from the original PDF | + | which appears to not be exact/up to date | + |-------------------------------------------| + | [0] Specialeffects mode1-32 | + | [1] colorFull color: 0x00 Monochrome:0x01 | + | [2] R Color ratio 0x00-0xFF | + | [3] G Color Ratio0x00-0xFF | + | [4] B Colour ratio0x00-0xFF | + | full color is 0,invalid | + | [5] dynamicdirection | + | left to right: 0x00 | + | right to left: 0x01 | + | down to up: 0x02 | + | up to down: 0x03 | + | [6] brightnesscontrol 0x00-0x0F | + | 0x0F brightest | + | [7] Periodiccontrol0x00-0x0F | + | 0x0F longest cycle | + | [8:13] Reserved | + | [14] Checkcode_L0xAA | + | [15] Checkcode_H0x55 | + |-------------------------------------------| + | Fixes: | + | color mode is 8th byte | + | brightness is 9th byte | + | speed is 10th byte | + | direction is 11th byte | + \*-----------------------------------------*/ + unsigned char selected_mode[EFFECT_PAGE_LENGTH]; + + for(unsigned int i = 0; i < 5; i++) // 5 packets + { + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + for(unsigned int j = 0; j < 4; j++) // of 4 effects + { + const mode& m = modes[1 + j + i * 4]; // skip 1 first mode (Custom) + + int offset = j * EFFECT_PAGE_LENGTH; + + usb_buf[offset + 0] = m.value; // mode value + + if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + usb_buf[offset + 1] = RGBGetRValue(m.colors[0]); + usb_buf[offset + 2] = RGBGetGValue(m.colors[0]); + usb_buf[offset + 3] = RGBGetBValue(m.colors[0]); + } + + usb_buf[offset + 8] = m.color_mode == MODE_COLORS_RANDOM; // random switch + usb_buf[offset + 9] = m.brightness; + usb_buf[offset + 10] = m.speed; + usb_buf[offset + 11] = m.direction; + + usb_buf[offset + 14] = EFFECT_PAGE_CHECK_CODE_L; + usb_buf[offset + 15] = EFFECT_PAGE_CHECK_CODE_H; + + /*-----------------------------------------*\ + | Backup active mode values for later use | + | Custom and off share the same mode value | + \*-----------------------------------------*/ + if(m.value == modes[active_mode].value || (m.value == LIGHTS_OFF_MODE_VALUE && modes[active_mode].value == CUSTOM_MODE_VALUE)) + { + usb_buf[offset + 9] = modes[active_mode].brightness; + + for(unsigned int x = 0; x < EFFECT_PAGE_LENGTH; x++) + { + selected_mode[x] = usb_buf[offset+x]; + } + } + } + + Send(usb_buf); // Sends the packet + } + + // packets count sent: 5 + + /*-----------------------------------------*\ + | 3 times an empty packet - guess why... | + \*-----------------------------------------*/ + for(unsigned int i = 0; i < 3; i++) + { + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + Send(usb_buf); + } + + // packets count sent: 8 + + /*-----------------------------------------*\ + | Customization stuff | + | 9 times * 16 blocks 80 RR GG BB | + \*-----------------------------------------*/ + unsigned char color_buf[COLOR_BUF_SIZE]; + memset(color_buf, 0x00, COLOR_BUF_SIZE); + + for(unsigned int i = 0; i < COLOR_BUF_SIZE; i += 4) + { + color_buf[i] = 0x80; + } + + for(unsigned int c = 0; c < colors.size(); c++) + { + int offset = led_sequence_positions[c] * 4; + + color_buf[offset + 1] = RGBGetRValue(colors[c]); + color_buf[offset + 2] = RGBGetGValue(colors[c]); + color_buf[offset + 3] = RGBGetBValue(colors[c]); + } + + for(unsigned int p = 0; p < 9; p++) + { + memcpy(usb_buf, &color_buf[p * PACKET_DATA_LENGTH], PACKET_DATA_LENGTH); + Send(usb_buf); + } + + // packets count sent: 17 + + /*-----------------------------------------*\ + | Tells the device what the active mode is | + | This is the last packet | + \*-----------------------------------------*/ + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + memcpy(usb_buf, &selected_mode[0], EFFECT_PAGE_LENGTH); + Send(usb_buf); + + // packets count sent: 18 - let's hope the keyboard ACK in next frame + + /*-----------------------------------------*\ + | Tells the device that the pages are sent | + \*-----------------------------------------*/ + EndCommunication(); + + /*-----------------------------------------*\ + | Tells the device to apply what we've sent | + \*-----------------------------------------*/ + StartEffectCommand(); +} + +void MintakaKeyboardController::StartEffectCommand() +{ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x00] = PACKET_HEADER; + usb_buf[0x01] = LED_EFFECT_START_COMMAND; + + Send(usb_buf); +} + +void MintakaKeyboardController::StartEffectPage() +{ + /*-----------------------------------------*\ + | LED_SPECIAL_EFFECT_PACKETS: | + | Packet amount that will be sent in this | + | transaction | + \*-----------------------------------------*/ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x00] = PACKET_HEADER; + usb_buf[0x01] = WRITE_LED_SPECIAL_EFFECT_AREA_COMMAND; + usb_buf[0x08] = LED_SPECIAL_EFFECT_PACKETS; + + Send(usb_buf); + + Read(); +} + +void MintakaKeyboardController::SetCustomization(bool state) +{ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + usb_buf[0x00] = PACKET_HEADER; + usb_buf[0x01] = state ? TURN_ON_CUSTOMIZATION_COMMAND : TURN_OFF_CUSTOMIZATION_COMMAND; + Send(usb_buf); + + Read(); +} + +void MintakaKeyboardController::EndCommunication() +{ + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[0x00] = PACKET_HEADER; + usb_buf[0x01] = COMMUNICATION_END_COMMAND; + + Send(usb_buf); + + Read(); +} + +void MintakaKeyboardController::Read() +{ + unsigned char usb_buf[PACKET_DATA_LENGTH+1]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH+1); + + usb_buf[0x00] = REPORT_ID; + + hid_get_feature_report(dev, usb_buf, PACKET_DATA_LENGTH+1); + + std::this_thread::sleep_for(10ms); +} + +void MintakaKeyboardController::Send(unsigned char data[PACKET_DATA_LENGTH]) +{ + unsigned char usb_buf[PACKET_DATA_LENGTH+1]; + + usb_buf[0] = REPORT_ID; + + for(unsigned int x = 0; x < PACKET_DATA_LENGTH; x++) + { + usb_buf[x+1] = data[x]; + } + + hid_send_feature_report(dev, usb_buf, PACKET_DATA_LENGTH+1); + + std::this_thread::sleep_for(10ms); +} diff --git a/Controllers/MintakaKeyboardController/MintakaKeyboardController.h b/Controllers/MintakaKeyboardController/MintakaKeyboardController.h new file mode 100644 index 0000000..c6bf2f9 --- /dev/null +++ b/Controllers/MintakaKeyboardController/MintakaKeyboardController.h @@ -0,0 +1,121 @@ +/*---------------------------------------------------------*\ +| MintakaKeyboardController.h | +| | +| Driver for VSG Mintaka Devices keyboard lighting | +| Based on KeychronKeyboardController | +| | +| Federico Scodelaro (pudymody) 08 Oct 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ +#pragma once + +#include "RGBController.h" +#include +#include + +#define REPORT_ID 0x00 +#define PACKET_DATA_LENGTH 64 +#define COLOR_BUF_SIZE 576 +#define EFFECT_PAGE_LENGTH 16 +#define LED_SPECIAL_EFFECT_PACKETS 0x12 +#define PACKET_HEADER 0x04 +#define EFFECT_PAGE_CHECK_CODE_L 0xAA +#define EFFECT_PAGE_CHECK_CODE_H 0x55 + +/*-----------------------------------------*\ +| Commands | +\*-----------------------------------------*/ +enum +{ + COMMUNICATION_END_COMMAND = 0x02, + GET_BASIC_INFO_COMMAND = 0x05, + READ_KEY_DEFINITION_AREA_COMMAND = 0x10, + WRITE_KEY_DEFINITION_AREA_COMMAND = 0x11, + READ_LED_EFFECT_DEFINITION_AREA_COMMAND = 0x12, + WRITE_LED_SPECIAL_EFFECT_AREA_COMMAND = 0x13, + READ_MACRO_DEFINITION_AREA_COMMAND = 0x14, + WRITE_MACRO_DEFINITION_AREA_COMMAND = 0x15, + READ_GAME_MODE_AREA_COMMAND = 0x16, + WRITE_GAME_MODE_AREA_COMMAND = 0x17, + TURN_ON_CUSTOMIZATION_COMMAND = 0x18, + TURN_OFF_CUSTOMIZATION_COMMAND = 0x19, + LED_EFFECT_START_COMMAND = 0xF0, + LED_SYNC_INITIAL_COMMAND = 0xF1, + LED_SYNC_START_COMMAND = 0xF2, + LED_SYNC_STOP_COMMAND = 0xF3, + RANDOM_PACKET_START_COMMAND = 0xAB, +}; + +/*-----------------------------------------*\ +| Modes | +\*-----------------------------------------*/ +enum +{ + CUSTOM_MODE_VALUE = 0x00, + STATIC_MODE_VALUE = 0x01, + KEYSTROKE_LIGHT_UP_MODE_VALUE = 0x02, + KEYSTROKE_DIM_MODE_VALUE = 0x03, + SPARKLE_MODE_VALUE = 0x04, + RAIN_MODE_VALUE = 0x05, + RANDOM_COLORS_MODE_VALUE = 0x06, + BREATHING_MODE_VALUE = 0x07, + SPECTRUM_CYCLE_MODE_VALUE = 0x08, + RING_GRADIENT_MODE_VALUE = 0x09, + VERTICAL_GRADIENT_MODE_VALUE = 0x0A, + HORIZONTAL_GRADIENT_WAVE_MODE_VALUE = 0x0B, + AROUND_EDGES_MODE_VALUE = 0x0C, + KEYSTROKE_HORIZONTAL_LINES_VALUE = 0x0D, + KEYSTROKE_TITLED_LINES_MODE_VALUE = 0x0E, + KEYSTROKE_RIPPLES_MODE_VALUE = 0x0F, + SEQUENCE_MODE_VALUE = 0x10, + WAVE_LINE_MODE_VALUE = 0x11, + TILTED_LINES_MODE_VALUE = 0x12, + BACK_AND_FORTH_MODE_VALUE = 0x13, + LIGHTS_OFF_MODE_VALUE = 0x80, +}; + +/*-----------------------------------------*\ +| Other settings | +\*-----------------------------------------*/ +enum +{ + MINTAKA_MIN_SPEED = 0x00, + MINTAKA_MAX_SPEED = 0x0F, + MINTAKA_MIN_BRIGHTNESS = 0x00, + MINTAKA_MAX_BRIGHTNESS = 0x0F, +}; + + +class MintakaKeyboardController +{ +public: + MintakaKeyboardController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~MintakaKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetLedSequencePositions(std::vector positions); + void SetMode(std::vector modes, int active_mode, std::vector colors); + +protected: + hid_device* dev; + +private: + std::string location; + std::string name; + std::string serial_number; + std::string version; + std::vector led_sequence_positions; + + void SetCustomization(bool state); + void StartEffectPage(); + void StartEffectCommand(); + void EndCommunication(); + + void Read(); + void Send(unsigned char data[PACKET_DATA_LENGTH]); +}; diff --git a/Controllers/MintakaKeyboardController/MintakaKeyboardControllerDetect.cpp b/Controllers/MintakaKeyboardController/MintakaKeyboardControllerDetect.cpp new file mode 100644 index 0000000..5627aa6 --- /dev/null +++ b/Controllers/MintakaKeyboardController/MintakaKeyboardControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| MintakaKeyboardControllerDetect.cpp | +| | +| Driver for VSG Mintaka Devices keyboard lighting | +| Based on KeychronKeyboardController | +| | +| Federico Scodelaro (pudymody) 08 Oct 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ +#include "Detector.h" +#include "MintakaKeyboardController.h" +#include "RGBController_MintakaKeyboard.h" + +/*---------------------------------------------------------*\ +| MintakaKeyboard vendor ID | +\*---------------------------------------------------------*/ +#define MINTAKA_KEYBOARD_VID 0x05AC + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define VSG_MINTAKA_PID 0x0256 + +void DetectMintakaKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + MintakaKeyboardController* controller = new MintakaKeyboardController(dev, *info, name); + RGBController_MintakaKeyboard* rgb_controller = new RGBController_MintakaKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("VSG Mintaka", DetectMintakaKeyboardControllers, MINTAKA_KEYBOARD_VID, VSG_MINTAKA_PID, 0, 0x0001, 0x06); diff --git a/Controllers/MintakaKeyboardController/RGBController_MintakaKeyboard.cpp b/Controllers/MintakaKeyboardController/RGBController_MintakaKeyboard.cpp new file mode 100644 index 0000000..d731356 --- /dev/null +++ b/Controllers/MintakaKeyboardController/RGBController_MintakaKeyboard.cpp @@ -0,0 +1,323 @@ +/*---------------------------------------------------------*\ +| RGBController_MintakaKeyboard.cpp | +| | +| Driver for VSG Mintaka Devices keyboard lighting | +| Based on KeychronKeyboardController | +| | +| Federico Scodelaro (pudymody) 08 Oct 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ +#include +#include + +#include "KeyboardLayoutManager.h" +#include "RGBControllerKeyNames.h" +#include "RGBController_MintakaKeyboard.h" +/*---------------------------------------------------------------------*\ +| VSG Keyboard Mintaka Layout | +\*---------------------------------------------------------------------*/ +layout_values mintaka_offset_values = +{ + { + /* ESC 1 2 3 4 5 6 7 8 9 0 ' ¿ BSPC */ + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 103, + /* TAB Q W E R T Y U I O P ´ + */ + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + /* CPLK A S D F G H J K L Ñ { } ENTR */ + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 108, 85, + /* LSFT < Z X C V B N M , . - RSFT */ + 73, 109, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + /* LCTL LWIN LALT SPC RALT RMNU RCTL RFNC */ + 91, 92, 93, 94, 95, 96, 97, 98, + }, + { + { KEYBOARD_LAYOUT_ISO_QWERTY, { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 1, 11, 0, KEY_EN_QUOTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 1, 12, 0, KEY_ES_OPEN_QUESTION_MARK, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + + { 0, 2, 11, 0, KEY_ES_TILDE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 2, 12, 0, KEY_EN_PLUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + + { 0, 3, 10, 0, KEY_ES_ENIE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 11, 0, KEY_EN_LEFT_BRACKET, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 12, 0, KEY_EN_RIGHT_BRACKET, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + + { 0, 4, 1, 0, KEY_NORD_ANGLE_BRACKET, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 4, 11, 0, KEY_NORD_HYPHEN, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + + { 0, 5, 11, 0, KEY_EN_MENU, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 5, 12, 0, KEY_EN_RIGHT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 5, 13, 0, KEY_EN_RIGHT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + }} + } +}; + +typedef struct +{ + std::string name; + int value; + int flags; +} mintaka_effect; + +/**------------------------------------------------------------------*\ + @name Mintaka Keyboard + @category Keyboard + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectMintakaKeyboardControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MintakaKeyboard::RGBController_MintakaKeyboard(MintakaKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "VSG"; + type = DEVICE_TYPE_KEYBOARD; + description = name; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Custom; + Custom.name = "Custom"; + Custom.value = CUSTOM_MODE_VALUE; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.brightness_min = MINTAKA_MIN_BRIGHTNESS; + Custom.brightness_max = MINTAKA_MAX_BRIGHTNESS; + Custom.brightness = MINTAKA_MAX_BRIGHTNESS; + modes.push_back(Custom); + + mintaka_effect mintaka_effects[20] = + { + { + "Static", + STATIC_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Keystroke light up", + KEYSTROKE_LIGHT_UP_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Keystroke dim", + KEYSTROKE_DIM_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Sparkle", + SPARKLE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Rain", + RAIN_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Random colors", + RANDOM_COLORS_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Breathing", + BREATHING_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Spectrum cycle", + SPECTRUM_CYCLE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Ring gradient", + RING_GRADIENT_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Vertical gradient", + VERTICAL_GRADIENT_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Horizontal gradient / Rainbow wave", + HORIZONTAL_GRADIENT_WAVE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Around edges", + AROUND_EDGES_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Keystroke horizontal lines", + KEYSTROKE_HORIZONTAL_LINES_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Keystroke tilted lines", + KEYSTROKE_TITLED_LINES_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Keystroke ripples", + KEYSTROKE_RIPPLES_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Sequence", + SEQUENCE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Wave line", + WAVE_LINE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Tilted lines", + TILTED_LINES_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Back and forth", + BACK_AND_FORTH_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE + }, + { + "Off", + LIGHTS_OFF_MODE_VALUE, + MODE_FLAG_AUTOMATIC_SAVE + } + }; + + for(const mintaka_effect& effect : mintaka_effects) + { + mode m; + m.name = effect.name; + m.value = effect.value; + m.flags = effect.flags; + + if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + m.color_mode = MODE_COLORS_MODE_SPECIFIC; + m.colors_min = 1; + m.colors_max = 1; + m.colors.resize(1); + } + else + { + m.color_mode = MODE_COLORS_NONE; + m.colors_min = 0; + m.colors_max = 0; + m.colors.resize(0); + } + + if(m.flags & MODE_FLAG_HAS_SPEED) + { + m.speed_min = MINTAKA_MIN_SPEED; + m.speed_max = MINTAKA_MAX_SPEED; + m.speed = m.speed_min; + } + + if(m.flags & MODE_FLAG_HAS_BRIGHTNESS) + { + m.brightness_min = MINTAKA_MIN_BRIGHTNESS; + m.brightness_max = MINTAKA_MAX_BRIGHTNESS; + m.brightness = m.brightness_max; + } + + modes.push_back(m); + } + + SetupZones(); +} + +RGBController_MintakaKeyboard::~RGBController_MintakaKeyboard() +{ + delete controller; +} + +void RGBController_MintakaKeyboard::SetupZones() +{ + + /*---------------------------------------------------------*\ + | Create the keyboard zone usiung Keyboard Layout Manager | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = ZONE_EN_KEYBOARD; + new_zone.type = ZONE_TYPE_MATRIX; + + KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ISO_QWERTY, KEYBOARD_SIZE_SIXTY, mintaka_offset_values); + + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + new_zone.matrix_map->height = new_kb.GetRowCount(); + new_zone.matrix_map->width = new_kb.GetColumnCount(); + + new_zone.matrix_map->map = new unsigned int[new_map->height * new_map->width]; + new_zone.leds_count = new_kb.GetKeyCount(); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + /*---------------------------------------------------------*\ + | Matrix map still uses declared zone rows and columns | + | as the packet structure depends on the matrix map | + \*---------------------------------------------------------*/ + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT, new_map->height, new_map->width); + controller->SetLedSequencePositions(mintaka_offset_values.default_values); + + /*---------------------------------------------------------*\ + | Create LEDs for the Matrix zone | + | Place keys in the layout to populate the matrix | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + leds.push_back(new_led); + } + + zones.push_back(new_zone); + + SetupColors(); +} + +void RGBController_MintakaKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_MintakaKeyboard::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_MintakaKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetMode(modes, active_mode, colors); +} + +void RGBController_MintakaKeyboard::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_MintakaKeyboard::DeviceUpdateMode() +{ + UpdateZoneLEDs(0); +} diff --git a/Controllers/MintakaKeyboardController/RGBController_MintakaKeyboard.h b/Controllers/MintakaKeyboardController/RGBController_MintakaKeyboard.h new file mode 100644 index 0000000..2e4f9a8 --- /dev/null +++ b/Controllers/MintakaKeyboardController/RGBController_MintakaKeyboard.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_MintakaKeyboard.h | +| | +| Driver for VSG Mintaka Devices keyboard lighting | +| Based on KeychronKeyboardController | +| | +| Federico Scodelaro (pudymody) 08 Oct 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ +#pragma once + +#include "RGBController.h" +#include "MintakaKeyboardController.h" + +class RGBController_MintakaKeyboard : public RGBController +{ +public: + RGBController_MintakaKeyboard(MintakaKeyboardController* controller_ptr); + ~RGBController_MintakaKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + MintakaKeyboardController* controller; +}; diff --git a/Controllers/MountainKeyboardController/Mountain60KeyboardController.cpp b/Controllers/MountainKeyboardController/Mountain60KeyboardController.cpp new file mode 100644 index 0000000..cea2b58 --- /dev/null +++ b/Controllers/MountainKeyboardController/Mountain60KeyboardController.cpp @@ -0,0 +1,283 @@ +/*---------------------------------------------------------*\ +| Mountain60KeyboardController.cpp | +| | +| Driver for Mountain keyboard | +| | +| O'D.Sæzl Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "Mountain60KeyboardController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +Mountain60KeyboardController::Mountain60KeyboardController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + unsigned char usb_buf[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + unsigned char read[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + memset(usb_buf, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + + usb_buf[0x01] = MOUNTAIN60_KEYBOARD_RESET_CMD; + usb_buf[0x02] = 0x46; //constant data + usb_buf[0x03] = 0x23; //constant data + usb_buf[0x04] = 0xEA; //constant data + + hid_send_feature_report(dev, usb_buf, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + memset(read, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + hid_get_feature_report(dev, read, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); +} + +Mountain60KeyboardController::~Mountain60KeyboardController() +{ + hid_close(dev); +} + +std::string Mountain60KeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string Mountain60KeyboardController::GetNameString() +{ + return(name); +} + +std::string Mountain60KeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void Mountain60KeyboardController::UpdateData() +{ + unsigned char usb_buf[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + unsigned char read[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + memset(usb_buf, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + + usb_buf[0x01] = MOUNTAIN60_KEYBOARD_CHECK_NUMPAD; + usb_buf[0x02] = 0x46; //constant data + usb_buf[0x03] = 0x23; //constant data + usb_buf[0x04] = 0xEA; //constant data + + hid_send_feature_report(dev, usb_buf, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + memset(read, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + hid_get_feature_report(dev, read, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); +} + +void Mountain60KeyboardController::SendModeDetails(const mode* current_mode) +{ + unsigned char usb_buf[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + unsigned char color_mode [] = {MOUNTAIN60_KEYBOARD_COLOR_MODE_SINGLE,MOUNTAIN60_KEYBOARD_COLOR_MODE_DUAL}; + memset(usb_buf, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + + usb_buf[0x01] = MOUNTAIN60_KEYBOARD_SEND_CMD; + usb_buf[0x02] = 0x46; //constant data + usb_buf[0x03] = 0x23; //constant data + usb_buf[0x04] = 0xEA; //constant data + + usb_buf[0x05] = current_mode->value; + usb_buf[0x07] = (current_mode->value == MOUNTAIN60_KEYBOARD_MODE_STATIC) ? 0x32 : current_mode->speed * 25; + usb_buf[0x08] = current_mode->brightness * 25; + usb_buf[0x09] = (current_mode->color_mode == MODE_COLORS_RANDOM) ? (unsigned char)MOUNTAIN60_KEYBOARD_COLOR_MODE_RAINBOW : color_mode[current_mode->colors.size() - 1]; + usb_buf[0x0A] = ConvertDirection(current_mode->direction,current_mode->value == MOUNTAIN60_KEYBOARD_MODE_TORNADO); + + for(std::size_t idx = 0; idx < current_mode->colors.size(); idx++) + { + std::size_t offset = (12 + (idx * 3)); + usb_buf[offset] = RGBGetRValue(current_mode->colors[idx]); + usb_buf[offset+1] = RGBGetGValue(current_mode->colors[idx]); + usb_buf[offset+2] = RGBGetBValue(current_mode->colors[idx]); + } + + hid_send_feature_report(dev, usb_buf, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(10ms); + SaveData(current_mode->value); +} + +void Mountain60KeyboardController::SelectMode(unsigned char mode_idx) +{ + unsigned char usb_buf[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + unsigned char read[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + memset(usb_buf, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + + usb_buf[0x01] = MOUNTAIN60_KEYBOARD_SELECT_MODE_CMD; + usb_buf[0x02] = 0x46; //constant data + usb_buf[0x03] = 0x23; //constant data + usb_buf[0x04] = 0xEA; //constant data + usb_buf[0x05] = 0x01; //constant data + + usb_buf[0x09] = mode_idx; + + hid_send_feature_report(dev, usb_buf, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + memset(read, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + hid_get_feature_report(dev, read, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); +} + +void Mountain60KeyboardController::SaveData(unsigned char mode_idx) +{ + unsigned char usb_buf[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + unsigned char read[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + memset(usb_buf, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + + usb_buf[0x01] = MOUNTAIN60_KEYBOARD_SAVE_CMD; + usb_buf[0x02] = 0x46; //constant data + usb_buf[0x03] = 0x23; //constant data + usb_buf[0x04] = 0xEA; //constant data + + usb_buf[0x05] = mode_idx; + + hid_send_feature_report(dev, usb_buf, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + memset(read, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + hid_get_feature_report(dev, read, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); +} + +void Mountain60KeyboardController::SendDirectStartPacketCmd(unsigned int brightness) +{ + unsigned char usb_buf[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + unsigned char read[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + memset(usb_buf, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + + usb_buf[0x01] = MOUNTAIN60_KEYBOARD_START_DIRECT_CMD; + usb_buf[0x02] = 0x46; //constant data + usb_buf[0x03] = 0x23; //constant data + usb_buf[0x04] = 0xEA; //constant data + usb_buf[0x06] = 0xC0; //constant data + + usb_buf[0x05] = brightness * 25; + + hid_send_feature_report(dev, usb_buf, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + memset(read, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + hid_get_feature_report(dev, read, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); +} + +void Mountain60KeyboardController::SendDirectPacketCmd(unsigned char stream_control, unsigned char *data, unsigned int data_size) +{ + unsigned char usb_buf[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + memset(usb_buf, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_HEADER_SIZE); + + usb_buf[0x01] = MOUNTAIN60_KEYBOARD_MAP_DIRECT_CMD; + usb_buf[0x02] = 0x46; //constant data + usb_buf[0x03] = 0x23; //constant data + usb_buf[0x04] = 0xEA; //constant data + + usb_buf[0x05] = stream_control; + + if(data_size <= MOUNTAIN60_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE) + { + unsigned char read[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + memcpy(&usb_buf[MOUNTAIN60_KEYBOARD_USB_BUFFER_HEADER_SIZE],data,data_size); + + if(data_size < MOUNTAIN60_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE) + { + memset(&usb_buf[MOUNTAIN60_KEYBOARD_USB_BUFFER_HEADER_SIZE + data_size],0xFF,MOUNTAIN60_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE-data_size); + } + + hid_send_feature_report(dev, usb_buf, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + memset(read, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + hid_get_feature_report(dev, read, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + } +} + +void Mountain60KeyboardController::SendDirectPacketFinishCmd() +{ + unsigned char usb_buf[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + unsigned char read[MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE]; + memset(usb_buf, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + + usb_buf[0x01] = MOUNTAIN60_KEYBOARD_END_DIRECT_CMD; + usb_buf[0x02] = 0x46; //constant data + usb_buf[0x03] = 0x23; //constant data + usb_buf[0x04] = 0xEA; //constant data + + hid_send_feature_report(dev, usb_buf, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + memset(read, 0x00, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); + hid_get_feature_report(dev, read, MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE); +} + +void Mountain60KeyboardController::SendDirect(unsigned int brightness, unsigned char* color_data, unsigned int data_size) +{ + static unsigned char prv_buffer[MOUNTAIN60_KEYBOARD_TRANSFER_BUFFER_SIZE] = {0xFF}; + unsigned char *data_ptr = color_data; + unsigned char *prv_data_ptr = prv_buffer; + unsigned int data_len = data_size; + unsigned char stream_control_flag = 0x0E; + + SendDirectStartPacketCmd(brightness); + + while(data_len>0) + { + if(data_len >= MOUNTAIN60_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE) + { + Mountain60KeyboardController::SendDirectPacketCmd(stream_control_flag,data_ptr,MOUNTAIN60_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE); + memcpy(prv_data_ptr,data_ptr,MOUNTAIN60_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE); + + data_ptr += MOUNTAIN60_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE; + prv_data_ptr += MOUNTAIN60_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE; + data_len -= MOUNTAIN60_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE; + } + else + { + stream_control_flag = 0x0A; + Mountain60KeyboardController::SendDirectPacketCmd(stream_control_flag,data_ptr,data_len); + memcpy(prv_data_ptr,data_ptr,data_len); + data_len = 0; + } + } + + SendDirectPacketFinishCmd(); +} + +unsigned char Mountain60KeyboardController::ConvertDirection(unsigned int direction, bool rotation) +{ + unsigned char ret; + switch(direction) + { + case MODE_DIRECTION_LEFT: + { + ret = rotation?MOUNTAIN60_KEYBOARD_DIRECTION_ANTICLK:MOUNTAIN60_KEYBOARD_DIRECTION_LEFT; + } + break; + + case MODE_DIRECTION_RIGHT: + { + ret = rotation?MOUNTAIN60_KEYBOARD_DIRECTION_CLK:MOUNTAIN60_KEYBOARD_DIRECTION_RIGHT; + } + break; + + case MODE_DIRECTION_UP: + { + ret = MOUNTAIN60_KEYBOARD_DIRECTION_UP; + } + break; + + case MODE_DIRECTION_DOWN: + { + ret = MOUNTAIN60_KEYBOARD_DIRECTION_DOWN; + } + break; + + default: + { + ret = MOUNTAIN60_KEYBOARD_DIRECTION_LEFT; + } + break; + } + return ret; +} diff --git a/Controllers/MountainKeyboardController/Mountain60KeyboardController.h b/Controllers/MountainKeyboardController/Mountain60KeyboardController.h new file mode 100644 index 0000000..c76f773 --- /dev/null +++ b/Controllers/MountainKeyboardController/Mountain60KeyboardController.h @@ -0,0 +1,96 @@ +/*---------------------------------------------------------*\ +| MountainKeyboardController.h | +| | +| Driver for Mountain keyboard | +| | +| O'D.Sæzl Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define MOUNTAIN60_KEYBOARD_MAX_TRANSFER_COLORS 191 +#define MOUNTAIN60_KEYBOARD_TRANSFER_BUFFER_SIZE (4*MOUNTAIN60_KEYBOARD_MAX_TRANSFER_COLORS) + +#define MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE 65 +#define MOUNTAIN60_KEYBOARD_USB_BUFFER_HEADER_SIZE 9 + +#define MOUNTAIN60_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE \ +(MOUNTAIN60_KEYBOARD_USB_BUFFER_SIZE-MOUNTAIN60_KEYBOARD_USB_BUFFER_HEADER_SIZE) + + enum +{ + MOUNTAIN60_KEYBOARD_RESET_CMD = 0x03, + MOUNTAIN60_KEYBOARD_CHECK_NUMPAD = 0x08, + MOUNTAIN60_KEYBOARD_SELECT_MODE_CMD = 0x16, + MOUNTAIN60_KEYBOARD_SEND_CMD = 0x17, + MOUNTAIN60_KEYBOARD_START_DIRECT_CMD = 0x34, + MOUNTAIN60_KEYBOARD_MAP_DIRECT_CMD = 0x35, + MOUNTAIN60_KEYBOARD_END_DIRECT_CMD = 0x36, + MOUNTAIN60_KEYBOARD_SAVE_CMD = 0x1A, +}; + +enum +{ + MOUNTAIN60_KEYBOARD_MODE_STATIC = 0x01, + MOUNTAIN60_KEYBOARD_MODE_COLOR_WAVE = 0x02, + MOUNTAIN60_KEYBOARD_MODE_TORNADO = 0x03, + MOUNTAIN60_KEYBOARD_MODE_BREATHING = 0x04, + MOUNTAIN60_KEYBOARD_MODE_REACTIVE = 0x05, + MOUNTAIN60_KEYBOARD_MODE_MATRIX = 0x06, + MOUNTAIN60_KEYBOARD_MODE_CUSTOM = 0x07, + MOUNTAIN60_KEYBOARD_MODE_YETI = 0x08, + MOUNTAIN60_KEYBOARD_MODE_OFF = 0x09, + MOUNTAIN60_KEYBOARD_MODE_INVALID = 0xFF, +}; + +enum +{ + MOUNTAIN60_KEYBOARD_COLOR_MODE_RAINBOW = 0x02, + MOUNTAIN60_KEYBOARD_COLOR_MODE_SINGLE = 0x00, + MOUNTAIN60_KEYBOARD_COLOR_MODE_DUAL = 0x10 +}; + +enum +{ + MOUNTAIN60_KEYBOARD_DIRECTION_UP = 0x06, + MOUNTAIN60_KEYBOARD_DIRECTION_DOWN = 0x02, + MOUNTAIN60_KEYBOARD_DIRECTION_LEFT = 0x04, + MOUNTAIN60_KEYBOARD_DIRECTION_RIGHT = 0x00, + MOUNTAIN60_KEYBOARD_DIRECTION_ANTICLK = 0x0A, + MOUNTAIN60_KEYBOARD_DIRECTION_CLK = 0x09, +}; + +class Mountain60KeyboardController +{ +public: + Mountain60KeyboardController(hid_device* dev_handle, const char* path, std::string dev_name); + ~Mountain60KeyboardController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void UpdateData(); + void SaveData(unsigned char mode_idx); + void SelectMode(unsigned char mode_idx); + void SendModeDetails(const mode* current_mode); + void SendDirect(unsigned int brightness,unsigned char* color_data, unsigned int color_count); + +private: + hid_device* dev; + std::string location; + std::string name; + + unsigned char ConvertDirection(unsigned int direction, bool rotation); + + void SendDirectStartPacketCmd(unsigned int brightness); + void SendDirectPacketCmd(unsigned char stream_control, unsigned char *data, unsigned int data_size); + void SendDirectPacketFinishCmd(); +}; diff --git a/Controllers/MountainKeyboardController/MountainKeyboardController.cpp b/Controllers/MountainKeyboardController/MountainKeyboardController.cpp new file mode 100644 index 0000000..ae009fd --- /dev/null +++ b/Controllers/MountainKeyboardController/MountainKeyboardController.cpp @@ -0,0 +1,580 @@ +/*---------------------------------------------------------*\ +| MountainKeyboardController.cpp | +| | +| Driver for Mountain keyboard | +| | +| Wojciech Lazarski Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "MountainKeyboardController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +MountainKeyboardController::MountainKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +MountainKeyboardController::~MountainKeyboardController() +{ + hid_close(dev); +} + +std::string MountainKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string MountainKeyboardController::GetNameString() +{ + return(name); +} + +std::string MountainKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void MountainKeyboardController::SelectMode(unsigned char mode_idx) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SELECT_MODE_CMD; + usb_buf[0x05] = 0x01; //constant data + usb_buf[0x06] = mode_idx; + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(200ms); +} + +void MountainKeyboardController::SaveData(unsigned char mode_idx) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SAVE_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SAVE_MAGIC1; + usb_buf[0x05] = mode_idx; + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(200ms); +} + +void MountainKeyboardController::SendOffCmd() +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SEND_COLOR_DATA_CMD; + usb_buf[0x03] = MOUNTAIN_KEYBOARD_OFF_MSG; + + usb_buf[0x05] = 0xFF; //constant data + usb_buf[0x06] = 0x64; // constant data + + usb_buf[0x07] = 0xFF; // constant data + usb_buf[0x08] = 0xFF; // constant data + usb_buf[0x09] = 0xFF; // constant data + + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(10ms); +} + + +void MountainKeyboardController::SendColorStaticCmd(color_setup setup) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SEND_COLOR_DATA_CMD; + usb_buf[0x03] = MOUNTAIN_KEYBOARD_STATIC_MSG; + usb_buf[0x05] = 0xFF; // constant data + usb_buf[0x06] = setup.brightness; + + usb_buf[0x07] = 0x00; // constant data + usb_buf[0x08] = 0xFF; // constant data + usb_buf[0x09] = 0xFF; // constant data + + usb_buf[0x0A] = setup.mode.one_color.r; + usb_buf[0x0B] = setup.mode.one_color.g; + usb_buf[0x0C] = setup.mode.one_color.b; + + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(10ms); +} + +void MountainKeyboardController::SendColorWaveCmd(color_setup setup) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SEND_COLOR_DATA_CMD; + usb_buf[0x03] = MOUNTAIN_KEYBOARD_COLOR_WAVE_MSG; + + usb_buf[0x05] = setup.speed; + usb_buf[0x06] = setup.brightness; + usb_buf[0x08] = setup.direction; + + switch(setup.color_mode) + { + case MOUNTAIN_KEYBOARD_COLOR_MODE_DUAL: + { + usb_buf[0x07] = 0x00; // constant data + usb_buf[0x09] = 0x02; // constant data + + usb_buf[0x0A] = 0x04; // constant data + usb_buf[0x0B] = 0x19; // constant data + + usb_buf[0x0C] = setup.mode.two_colors.r1; + usb_buf[0x0D] = setup.mode.two_colors.g1; + usb_buf[0x0E] = setup.mode.two_colors.b1; + usb_buf[0x0F] = 0x32; // constant data + usb_buf[0x10] = setup.mode.two_colors.r2; + usb_buf[0x11] = setup.mode.two_colors.g2; + usb_buf[0x12] = setup.mode.two_colors.b2; + usb_buf[0x13] = 0x4B; // constant data + usb_buf[0x14] = setup.mode.two_colors.r1; + usb_buf[0x15] = setup.mode.two_colors.g1; + usb_buf[0x16] = setup.mode.two_colors.b1; + usb_buf[0x17] = 0x64; // constant data + usb_buf[0x18] = setup.mode.two_colors.r2; + usb_buf[0x19] = setup.mode.two_colors.g2; + usb_buf[0x1A] = setup.mode.two_colors.b2; + } + break; + + case MOUNTAIN_KEYBOARD_COLOR_MODE_SINGLE: + { + usb_buf[0x07] = 0x00; // constant data + usb_buf[0x09] = 0x00; // constant data + + usb_buf[0x0A] = 0x01; // constant data + usb_buf[0x0B] = 0x64; // constant data + + usb_buf[0x0C] = setup.mode.one_color.r; + usb_buf[0x0D] = setup.mode.one_color.g; + usb_buf[0x0E] = setup.mode.one_color.b; + usb_buf[0x0F] = 0xFF; // constant data + } + break; + + case MOUNTAIN_KEYBOARD_COLOR_MODE_RAINBOW: + { + usb_buf[0x07] = 0x02; // constant data + usb_buf[0x09] = 0x02; // constant data + + usb_buf[0x0B] = 0xFF; // constant data + usb_buf[0x0F] = 0xFF; // constant data + } + break; + + default: + break; + } + + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(10ms); +} + +void MountainKeyboardController::SendColorTornadoCmd(color_setup setup) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SEND_COLOR_DATA_CMD; + usb_buf[0x03] = MOUNTAIN_KEYBOARD_TORNADO_MSG; + + usb_buf[0x05] = setup.speed; + usb_buf[0x06] = setup.brightness; + usb_buf[0x08] = setup.direction; + + switch(setup.color_mode) + { + case MOUNTAIN_KEYBOARD_COLOR_MODE_SINGLE: + { + usb_buf[0x07] = 0x00; // constant data + usb_buf[0x09] = 0x00; // constant data + + usb_buf[0x0A] = 0x01; // constant data + usb_buf[0x0B] = 0x64; // constant data + + usb_buf[0x0C] = setup.mode.one_color.r; + usb_buf[0x0D] = setup.mode.one_color.g; + usb_buf[0x0E] = setup.mode.one_color.b; + usb_buf[0x0F] = 0xFF; // constant data + } + break; + + case MOUNTAIN_KEYBOARD_COLOR_MODE_RAINBOW: + { + usb_buf[0x07] = 0x02; // constant data + usb_buf[0x09] = 0x02; // constant data + + usb_buf[0x0B] = 0xFF; // constant data + usb_buf[0x0F] = 0xFF; // constant data + } + break; + + default: + break; + } + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(10ms); +} + +void MountainKeyboardController::SendColorBreathingCmd( color_setup setup) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SEND_COLOR_DATA_CMD; + usb_buf[0x03] = MOUNTAIN_KEYBOARD_BREATHING_MSG; + usb_buf[0x05] = setup.speed; + usb_buf[0x06] = setup.brightness; + + switch(setup.color_mode) + { + case MOUNTAIN_KEYBOARD_COLOR_MODE_DUAL: + { + usb_buf[0x07] = 0x10; // constant data + usb_buf[0x08] = 0xFF; // constant data + usb_buf[0x09] = 0xFF; // constant data + + usb_buf[0x0A] = setup.mode.two_colors.r1; + usb_buf[0x0B] = setup.mode.two_colors.g1; + usb_buf[0x0C] = setup.mode.two_colors.b1; + usb_buf[0x0D] = setup.mode.two_colors.r2; + usb_buf[0x0E] = setup.mode.two_colors.g2; + usb_buf[0x0F] = setup.mode.two_colors.b2; + } + break; + + case MOUNTAIN_KEYBOARD_COLOR_MODE_SINGLE: + { + usb_buf[0x07] = 0x00; // constant data + usb_buf[0x08] = 0xFF; // constant data + usb_buf[0x09] = 0xFF; // constant data + usb_buf[0x0A] = setup.mode.one_color.r; + usb_buf[0x0B] = setup.mode.one_color.g; + usb_buf[0x0C] = setup.mode.one_color.b; + } + break; + + case MOUNTAIN_KEYBOARD_COLOR_MODE_RAINBOW: + { + usb_buf[0x07] = 0x02; // constant data + usb_buf[0x08] = 0xFF; // constant data + usb_buf[0x09] = 0xFF; // constant data + } + break; + + default: + break; + } + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(10ms); +} + +void MountainKeyboardController::SendColorMatrixCmd(color_setup setup) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SEND_COLOR_DATA_CMD; + usb_buf[0x03] = MOUNTAIN_KEYBOARD_MATRIX_MSG; + usb_buf[0x05] = setup.speed; + usb_buf[0x06] = setup.brightness; + + usb_buf[0x07] = 0x00; // constant data + usb_buf[0x08] = 0xFF; // constant data + usb_buf[0x09] = 0xFF; // constant data + + usb_buf[0x0A] = setup.mode.two_colors.r1; + usb_buf[0x0B] = setup.mode.two_colors.g1; + usb_buf[0x0C] = setup.mode.two_colors.b1; + + usb_buf[0x13] = setup.mode.two_colors.r2; + usb_buf[0x14] = setup.mode.two_colors.g2; + usb_buf[0x15] = setup.mode.two_colors.b2; + + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(10ms); +} + +void MountainKeyboardController::SendColorReactiveCmd(color_setup setup) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SEND_COLOR_DATA_CMD; + usb_buf[0x03] = MOUNTAIN_KEYBOARD_REACTIVE_MSG; + usb_buf[0x05] = setup.speed; + usb_buf[0x06] = setup.brightness; + + usb_buf[0x07] = 0x00; // constant data + usb_buf[0x08] = 0xFF; // constant data + usb_buf[0x09] = 0xFF; // constant data + + usb_buf[0x0A] = setup.mode.two_colors.r1; + usb_buf[0x0B] = setup.mode.two_colors.g1; + usb_buf[0x0C] = setup.mode.two_colors.b1; + + usb_buf[0x13] = setup.mode.two_colors.r2; + usb_buf[0x14] = setup.mode.two_colors.g2; + usb_buf[0x15] = setup.mode.two_colors.b2; + + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(10ms); +} + +void MountainKeyboardController::SendColorStartPacketCmd(unsigned char brightness) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0xFF, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x00] = 0x00; + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SEND_COLOR_DATA_CMD; + usb_buf[0x03] = MOUNTAIN_KEYBOARD_CUSTOM_MSG; // constant data + usb_buf[0x04] = 0x00; // constant data + usb_buf[0x06] = brightness; + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(10ms); +} + +void MountainKeyboardController::SendColorPacketCmd(unsigned char pkt_no,unsigned char brightness, unsigned char *data, unsigned int data_size) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_HEADER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SEND_COLOR_DATA_CMD; + usb_buf[0x04] = 0x01; // constant data + usb_buf[0x05] = pkt_no; + usb_buf[0x06] = brightness; + + if(data_size <= MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE) + { + memcpy(&usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_HEADER_SIZE],data,data_size); + if(data_size < MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE) + { + memset(&usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_HEADER_SIZE + data_size],0x00,MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE-data_size); + } + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(5ms); + } +} + +void MountainKeyboardController::SendColorEdgePacketCmd(unsigned char pkt_no, unsigned char *data, unsigned int data_size) +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_HEADER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_SEND_COLOR_EDGE_CMD; + usb_buf[0x03] = 0x0A; // constant data + usb_buf[0x05] = pkt_no; + usb_buf[0x06] = 0xFF; + + if(data_size <= MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE) + { + memcpy(&usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_HEADER_SIZE],data,data_size); + if(data_size < MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE) + { + memset(&usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_HEADER_SIZE + data_size],0x00,MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE-data_size); + } + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(5ms); + } +} + +void MountainKeyboardController::SendColorPacketFinishCmd() +{ + unsigned char usb_buf[MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE]; + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + usb_buf[0x01] = MOUNTAIN_KEYBOARD_SEND_CMD; + usb_buf[0x02] = MOUNTAIN_KEYBOARD_CONFIRM_CMD; + + for(unsigned char i=0;i<3;i++) + { + usb_buf[0x03] = i; + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE); + std::this_thread::sleep_for(10ms); + } +} + +void MountainKeyboardController::SendWheelColorChange(unsigned char color_data [3]) +{ + + wheel_config * usb_buf = GetWheelConfig(); + if (usb_buf != nullptr) + { + usb_buf->fixed_byte_1 = 0x01; + usb_buf->fixed_byte_2 = 0x02; + usb_buf->zero_byte = 0x00; + usb_buf->r = color_data[0]; + usb_buf->g = color_data[1]; + usb_buf->b = color_data[2]; + hid_write(dev, (unsigned char *) usb_buf, MOUNTAIN_KEYBOARD_WHEEL_CONFIG_BUFFER_SIZE); + } + +} + +wheel_config * MountainKeyboardController::GetWheelConfig() +{ + + unsigned char * usb_buf = new unsigned char [MOUNTAIN_KEYBOARD_WHEEL_CONFIG_BUFFER_SIZE+1]; + unsigned char * recv_buf = &usb_buf[1]; + + + for (int i =0; i < 1000; i++) + { + + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_WHEEL_CONFIG_BUFFER_SIZE+1); + wheel_config * usb_conf = (wheel_config *) usb_buf; + usb_conf->config_start_first = 0x11; + usb_conf->config_start_second = 0x14; + hid_write(dev, usb_buf, MOUNTAIN_KEYBOARD_WHEEL_CONFIG_BUFFER_SIZE); + memset(usb_buf, 0x00, MOUNTAIN_KEYBOARD_WHEEL_CONFIG_BUFFER_SIZE+1); + hid_read_timeout(dev, recv_buf, MOUNTAIN_KEYBOARD_WHEEL_CONFIG_BUFFER_SIZE, 1); + if (usb_conf->config_start_first == MOUNTAIN_KEYBOARD_WHEEL_CONFIG_FIRST_BYTE && + usb_conf->config_start_second == MOUNTAIN_KEYBOARD_WHEEL_CONFIG_SECOND_BYTE) + { + return usb_conf; + } + + } + + delete[] usb_buf; + return nullptr; + +} + + +void MountainKeyboardController::SendDirectColorEdgeCmd(bool quick_mode, unsigned char brightness, unsigned char *color_data, unsigned int data_size) +{ + static bool first_call = true; + static unsigned char prv_buffer[MOUNTAIN_KEYBOARD_TRANSFER_EDGE_BUFFER_SIZE] = {0}; + + unsigned char pkt_no = 0; + unsigned char *data_ptr = color_data; + unsigned char *prv_data_ptr = prv_buffer; + unsigned int data_len = data_size; + + if(MOUNTAIN_KEYBOARD_TRANSFER_EDGE_BUFFER_SIZE >= data_len) + { + if(!quick_mode) + { + SendColorStartPacketCmd(brightness); + } + while(data_len>0) + { + if(data_len >= MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE) + { + if(first_call || !quick_mode || memcmp(data_ptr,prv_data_ptr,MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE)) + { + MountainKeyboardController::SendColorEdgePacketCmd(pkt_no,data_ptr,MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE); + memcpy(prv_data_ptr,data_ptr,MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE); + } + pkt_no++; + data_ptr += MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE; + prv_data_ptr += MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE; + data_len -= MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE; + } + else + { + if(first_call || !quick_mode || memcmp(data_ptr,prv_data_ptr,data_len)) + { + MountainKeyboardController::SendColorEdgePacketCmd(pkt_no,data_ptr,data_len); + memcpy(prv_data_ptr,data_ptr,data_len); + } + data_len = 0; + } + } + if(!quick_mode) + { + SendColorPacketFinishCmd(); + } + + if (first_call) + { + first_call = false; + } + } +} + +void MountainKeyboardController::SendDirectColorCmd(bool quick_mode, unsigned char brightness, unsigned char *color_data, unsigned int data_size) +{ + static bool first_call = true; + static unsigned char prv_buffer[MOUNTAIN_KEYBOARD_TRANSFER_BUFFER_SIZE] = {0}; + + unsigned char pkt_no = 0; + unsigned char *data_ptr = color_data; + unsigned char *prv_data_ptr = prv_buffer; + unsigned int data_len = data_size; + + if(MOUNTAIN_KEYBOARD_TRANSFER_BUFFER_SIZE >= data_len) + { + if(!quick_mode) + { + SendColorStartPacketCmd(brightness); + } + while(data_len>0) + { + if(data_len >= MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE) + { + if(first_call || !quick_mode || memcmp(data_ptr,prv_data_ptr,MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE)) + { + MountainKeyboardController::SendColorPacketCmd(pkt_no,brightness,data_ptr,MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE); + memcpy(prv_data_ptr,data_ptr,MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE); + } + pkt_no++; + data_ptr += MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE; + prv_data_ptr += MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE; + data_len -= MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE; + } + else + { + if(first_call || !quick_mode || memcmp(data_ptr,prv_data_ptr,data_len)) + { + MountainKeyboardController::SendColorPacketCmd(pkt_no,brightness,data_ptr,data_len); + memcpy(prv_data_ptr,data_ptr,data_len); + } + data_len = 0; + } + } + if(!quick_mode) + { + SendColorPacketFinishCmd(); + } + + if (first_call) + { + first_call = false; + } + } +} diff --git a/Controllers/MountainKeyboardController/MountainKeyboardController.h b/Controllers/MountainKeyboardController/MountainKeyboardController.h new file mode 100644 index 0000000..e76a00e --- /dev/null +++ b/Controllers/MountainKeyboardController/MountainKeyboardController.h @@ -0,0 +1,182 @@ +/*---------------------------------------------------------*\ +| MountainKeyboardController.h | +| | +| Driver for Mountain keyboard | +| | +| Wojciech Lazarski Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +/*-----------------------------------------------------*\ +| Mountain vendor ID | +\*-----------------------------------------------------*/ +#define MOUNTAIN_VID 0x3282 +/*-----------------------------------------------------*\ +| Everest keyboard product IDs | +\*-----------------------------------------------------*/ +#define MOUNTAIN_EVEREST_PID 0x0001 + + +#define MOUNTAIN_KEYBOARD_MAX_TRANSFER_COLORS 126 +#define MOUNTAIN_KEYBOARD_TRANSFER_BUFFER_SIZE (3*MOUNTAIN_KEYBOARD_MAX_TRANSFER_COLORS) + +#define MOUNTAIN_KEYBOARD_MAX_TRANSFER_EDGE_COLORS 46 +#define MOUNTAIN_KEYBOARD_TRANSFER_EDGE_BUFFER_SIZE (3*MOUNTAIN_KEYBOARD_MAX_TRANSFER_EDGE_COLORS) + +#define MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE 65 +#define MOUNTAIN_KEYBOARD_USB_BUFFER_HEADER_SIZE 8 +#define MOUNTAIN_KEYBOARD_USB_MAX_DIRECT_PAYLOAD_SIZE \ + (MOUNTAIN_KEYBOARD_USB_BUFFER_SIZE-MOUNTAIN_KEYBOARD_USB_BUFFER_HEADER_SIZE) +#define MOUNTAIN_KEYBOARD_WHEEL_CONFIG_BUFFER_SIZE 65 +#define MOUNTAIN_KEYBOARD_WHEEL_CONFIG_FIRST_BYTE 0x11 +#define MOUNTAIN_KEYBOARD_WHEEL_CONFIG_SECOND_BYTE 0x14 +#define MOUNTAIN_KEYBOARD_WHEEL_CONFIG_FIXED_BYTE_1 0x01 +#define MOUNTAIN_KEYBOARD_WHEEL_CONFIG_FIXED_BYTE_2 0x02 + +enum +{ + MOUNTAIN_KEYBOARD_SAVE_CMD = 0x13, + MOUNTAIN_KEYBOARD_SEND_CMD = 0x14 +}; + +enum +{ + MOUNTAIN_KEYBOARD_SEND_COLOR_DATA_CMD = 0x2C, + MOUNTAIN_KEYBOARD_SEND_COLOR_EDGE_CMD = 0x2D, + MOUNTAIN_KEYBOARD_SELECT_MODE_CMD = 0x00, + MOUNTAIN_KEYBOARD_CONFIRM_CMD = 0xA0 +}; + +enum +{ + MOUNTAIN_KEYBOARD_STATIC_MSG = 0x00, + MOUNTAIN_KEYBOARD_COLOR_WAVE_MSG = 0x04, + MOUNTAIN_KEYBOARD_TORNADO_MSG = 0x07, + MOUNTAIN_KEYBOARD_BREATHING_MSG = 0x01, + MOUNTAIN_KEYBOARD_REACTIVE_MSG = 0x03, + MOUNTAIN_KEYBOARD_MATRIX_MSG = 0x09, + MOUNTAIN_KEYBOARD_CUSTOM_MSG = 0x0A, + MOUNTAIN_KEYBOARD_OFF_MSG = 0x0C +}; + +enum +{ + MOUNTAIN_KEYBOARD_IDX_STATIC = 0x00, + MOUNTAIN_KEYBOARD_IDX_COLOR_WAVE = 0x01, + MOUNTAIN_KEYBOARD_IDX_TORNADO = 0x02, + MOUNTAIN_KEYBOARD_IDX_BREATHING = 0x03, + MOUNTAIN_KEYBOARD_IDX_REACTIVE = 0x04, + MOUNTAIN_KEYBOARD_IDX_MATRIX = 0x05, + MOUNTAIN_KEYBOARD_IDX_CUSTOM = 0x06, + MOUNTAIN_KEYBOARD_IDX_OFF = 0x08, + MOUNTAIN_KEYBOARD_IDX_INVALID = 0xFF, +}; + +enum +{ + MOUNTAIN_KEYBOARD_COLOR_MODE_RAINBOW = 0x00, + MOUNTAIN_KEYBOARD_COLOR_MODE_SINGLE = 0x01, + MOUNTAIN_KEYBOARD_COLOR_MODE_DUAL = 0x02 +}; + +enum +{ + MOUNTAIN_KEYBOARD_DIRECTION_UP = 0x06, + MOUNTAIN_KEYBOARD_DIRECTION_DOWN = 0x02, + MOUNTAIN_KEYBOARD_DIRECTION_LEFT = 0x04, + MOUNTAIN_KEYBOARD_DIRECTION_RIGHT = 0x00, + MOUNTAIN_KEYBOARD_DIRECTION_ANTICLK = 0x0A, + MOUNTAIN_KEYBOARD_DIRECTION_CLK = 0x09, +}; + +#define MOUNTAIN_KEYBOARD_SAVE_MAGIC1 0x55 + +typedef struct +{ + unsigned char color_mode; + unsigned char brightness; + unsigned char speed; + unsigned char direction; + + union + { + struct + { + unsigned char r; + unsigned char g; + unsigned char b; + } one_color; + struct + { + unsigned char r1; + unsigned char g1; + unsigned char b1; + unsigned char r2; + unsigned char g2; + unsigned char b2; + } two_colors; + } mode; +} color_setup; + + +typedef struct +{ + unsigned char report_size; + unsigned char config_start_first; + unsigned char config_start_second; + unsigned char zero_byte; + unsigned char fixed_byte_1; + unsigned char fixed_byte_2; + unsigned char config_intermediate_values [2]; + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char config_end_values [54]; + +} wheel_config; + +class MountainKeyboardController +{ +public: + MountainKeyboardController(hid_device* dev_handle, const char* path, std::string dev_name); + ~MountainKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendOffCmd(); + void SendColorStaticCmd(color_setup setup); + void SendColorWaveCmd(color_setup setup); + void SendColorTornadoCmd(color_setup setup); + void SendColorBreathingCmd(color_setup setup); + void SendColorMatrixCmd(color_setup setup); + void SendColorReactiveCmd(color_setup setup); + + void SendDirectColorCmd(bool quick_mode, unsigned char brightness, unsigned char *color_data, unsigned int color_count); + void SendDirectColorEdgeCmd(bool quick_mode, unsigned char brightness, unsigned char *color_data, unsigned int data_size); + + + void SendWheelColorChange(unsigned char color_data [3]); + wheel_config * GetWheelConfig(); + + void SaveData(unsigned char mode_idx); + void SelectMode(unsigned char mode_idx); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendColorStartPacketCmd(unsigned char brightness); + void SendColorPacketCmd(unsigned char pkt_no,unsigned char brightness, unsigned char *data, unsigned int data_size); + void SendColorEdgePacketCmd(unsigned char pkt_no, unsigned char *data, unsigned int data_size); + void SendColorPacketFinishCmd(); +}; diff --git a/Controllers/MountainKeyboardController/MountainKeyboardControllerDetect.cpp b/Controllers/MountainKeyboardController/MountainKeyboardControllerDetect.cpp new file mode 100644 index 0000000..ad6adf5 --- /dev/null +++ b/Controllers/MountainKeyboardController/MountainKeyboardControllerDetect.cpp @@ -0,0 +1,69 @@ +/*---------------------------------------------------------*\ +| MountainKeyboardControllerDetect.cpp | +| | +| Detector for Mountain keyboard | +| | +| Wojciech Lazarski / O'D.Sæzl Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "MountainKeyboardController.h" +#include "RGBController_MountainKeyboard.h" +#include "Mountain60KeyboardController.h" +#include "RGBController_Mountain60Keyboard.h" + +/*---------------------------------------------------------------*\ +| Mountain vendor ID | +\*---------------------------------------------------------------*/ +#define MOUNTAIN_VID 0x3282 + +/*----------------------------------------------------------------*\ +| Everest 60 keyboard Connection IDs | +\*----------------------------------------------------------------*/ +#define MOUNTAIN60_EVEREST_60_PID_ANSII 0x0005 +#define MOUNTAIN60_EVEREST_60_PID_ISO 0x0006 +#define MOUNTAIN60_EVEREST_60_INTERFACE 2 +#define MOUNTAIN60_EVEREST_60_U 0x01 +#define MOUNTAIN60_EVEREST_60_UP 0xFFFF + +/*----------------------------------------------------------------------------------------*\ +| | +| DetectMountainKeyboardControllers | +| | +| Tests the USB address to see if a Mountain RGB Keyboard controller exists there. | +| | +\*----------------------------------------------------------------------------------------*/ + +void DetectMountain60KeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + Mountain60KeyboardController* controller = new Mountain60KeyboardController(dev, info->path, name); + RGBController_Mountain60Keyboard* rgb_controller = new RGBController_Mountain60Keyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectMountainKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + MountainKeyboardController* controller = new MountainKeyboardController(dev, info->path, name); + RGBController_MountainKeyboard* rgb_controller = new RGBController_MountainKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Mountain Everest", DetectMountainKeyboardControllers, MOUNTAIN_VID, MOUNTAIN_EVEREST_PID, 3, 0xFF00, 0x01); +REGISTER_HID_DETECTOR_IPU("Mountain Everest 60", DetectMountain60KeyboardControllers, MOUNTAIN_VID, MOUNTAIN60_EVEREST_60_PID_ANSII, MOUNTAIN60_EVEREST_60_INTERFACE, MOUNTAIN60_EVEREST_60_UP, MOUNTAIN60_EVEREST_60_U); +REGISTER_HID_DETECTOR_IPU("Mountain Everest 60", DetectMountain60KeyboardControllers, MOUNTAIN_VID, MOUNTAIN60_EVEREST_60_PID_ISO, MOUNTAIN60_EVEREST_60_INTERFACE, MOUNTAIN60_EVEREST_60_UP, MOUNTAIN60_EVEREST_60_U); diff --git a/Controllers/MountainKeyboardController/RGBController_Mountain60Keyboard.cpp b/Controllers/MountainKeyboardController/RGBController_Mountain60Keyboard.cpp new file mode 100644 index 0000000..e66ccaa --- /dev/null +++ b/Controllers/MountainKeyboardController/RGBController_Mountain60Keyboard.cpp @@ -0,0 +1,463 @@ +/*---------------------------------------------------------*\ +| RGBController_MountainKeyboard.cpp | +| | +| RGBController for Mountain keyboard | +| | +| O'D.Sæzl Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "RGBController_Mountain60Keyboard.h" +#include "RGBControllerKeyNames.h" +#include "KeyboardLayoutManager.h" + +using namespace std::chrono_literals; + +/*---------------------------------------------------------*\ +| TODO: Detect detached keypad | +\*---------------------------------------------------------*/ + +std::vector mountain60_keyboard_key_id_values = + { + /* ESC 1 2 3 4 5 6 7 8 9 0 - = BSPC */ + 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + /* TAB Q W E R T Y U I O P [ ] \ */ + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* CPLK A S D F G H J K L ; " ENTR */ + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, + /* LSFT Z X C V B N M , . / RSFT ARWU DEL */ + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 97, 99, 56, + /* LCTL LWIN LALT SPC RALT RFNC FNC ARWL ARWD ARWR */ + 105, 106, 107, 110, 113, 115, 119, 120, 121, +}; + +layout_values mountain60_layout = + { + mountain60_keyboard_key_id_values, + { + /*---------------------------------------------*\ + | No regional layout fix for the moment | + \*---------------------------------------------*/ + }, +}; + +keyboard_keymap_overlay_values mountain60_keyboard_overlay_no_numpad = + { + KEYBOARD_SIZE::KEYBOARD_SIZE_SIXTY, + mountain60_layout, + { + /*--------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alt name, OpCode, | + \*---------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 4, 13, 120, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 4, 12, 119, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 4, 14, 121, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 4, 14, 121, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 13, 99, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 12, 97, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 2, 85, KEY_EN_Z, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 3, 86, KEY_EN_X, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 4, 87, KEY_EN_C, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 5, 88, KEY_EN_B, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 6, 89, KEY_EN_V, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 7, 90, KEY_EN_N, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 8, 91, KEY_EN_M, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 9, 92, KEY_EN_COMMA, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 10, 93, KEY_EN_PERIOD, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 14, 56, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 2, 13, 76, KEY_EN_ANSI_ENTER, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 0, 84, KEY_EN_LEFT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + //upper edge + { 0, 0, 0, 126, "Edge 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 0, 1, 127, "Edge 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 2, 128, "Edge 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 3, 129, "Edge 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 4, 130, "Edge 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 5, 131, "Edge 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 6, 132, "Edge 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 7, 133, "Edge 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 8, 134, "Edge 9", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 9, 135, "Edge 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 10, 136, "Edge 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 11, 137, "Edge 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 12, 138, "Edge 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 13, 139, "Edge 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 14, 140, "Edge 15", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 15, 141, "Edge 16", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //left edge + { 0, 0, 0, 169, "Edge 44", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 168, "Edge 43", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 167, "Edge 42", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 166, "Edge 41", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 165, "Edge 40", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 164, "Edge 39", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //down edge + { 0, 6, 0, 1, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_ROW, }, + { 0, 6, 0, 163, "Edge 38", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 1, 162, "Edge 37", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 2, 161, "Edge 36", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 3, 160, "Edge 35", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 4, 159, "Edge 34", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 5, 158, "Edge 33", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 6, 157, "Edge 32", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 7, 156, "Edge 31", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 8, 155, "Edge 30", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 9, 154, "Edge 29", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 10, 153, "Edge 28", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 11, 152, "Edge 27", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 12, 151, "Edge 26", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 13, 150, "Edge 25", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 14, 149, "Edge 24", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 15, 148, "Edge 23", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //right edge + { 0, 1, 16, 142, "Edge 17", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 16, 143, "Edge 18", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 16, 144, "Edge 19", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 16, 145, "Edge 20", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 16, 146, "Edge 21", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 16, 147, "Edge 22", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 16, 147, "Edge 22", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + //numpad left edge + { 0, 0, 17, 191, "Numpad Edge 22", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 17, 190, "Numpad Edge 21", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 17, 189, "Numpad Edge 20", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 17, 188, "Numpad Edge 19", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 17, 187, "Numpad Edge 18", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 17, 186, "Numpad Edge 17", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //numpad upper edge + { 0, 0, 18, 170, "Numpad Edge 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 171, "Numpad Edge 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 172, "Numpad Edge 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 21, 173, "Numpad Edge 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 22, 174, "Numpad Edge 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //numpad down edge + { 0, 6, 17, 185, "Numpad Edge 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 6, 18, 184, "Numpad Edge 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 19, 183, "Numpad Edge 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 20, 182, "Numpad Edge 15", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 21, 181, "Numpad Edge 16", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + //numpad right edge + { 0, 1, 22, 175, "Numpad Edge 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 22, 176, "Numpad Edge 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 22, 177, "Numpad Edge 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 22, 178, "Numpad Edge 9", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 22, 179, "Numpad Edge 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 22, 180, "Numpad Edge 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 6, 22, 180, "Numpad Edge 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + //numpad keys + { 0, 1, 18, 38, KEY_EN_NUMPAD_LOCK, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 1, 19, 39, KEY_EN_NUMPAD_DIVIDE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 1, 20, 40, KEY_EN_NUMPAD_TIMES, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 1, 21, 41, KEY_EN_NUMPAD_MINUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 2, 18, 59, KEY_EN_NUMPAD_7, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 2, 19, 60, KEY_EN_NUMPAD_8, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 2, 20, 61, KEY_EN_NUMPAD_9, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 2, 21, 62, KEY_EN_NUMPAD_PLUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 18, 80, KEY_EN_NUMPAD_4, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 19, 81, KEY_EN_NUMPAD_5, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 3, 20, 82, KEY_EN_NUMPAD_6, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 4, 18, 101, KEY_EN_NUMPAD_1, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 4, 19, 102, KEY_EN_NUMPAD_2, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 4, 20, 103, KEY_EN_NUMPAD_3, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 5, 18, 122, KEY_EN_NUMPAD_0, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 5, 20, 124, KEY_EN_NUMPAD_PERIOD, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 5, 21, 125, KEY_EN_NUMPAD_ENTER, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + } +}; + +/**------------------------------------------------------------------*\ + @name Mountain Keyboard + @category Keyboard + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectMountainKeyboardControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Mountain60Keyboard::RGBController_Mountain60Keyboard(Mountain60KeyboardController* controller_ptr) +{ + controller = controller_ptr; + name = controller->GetNameString(); + vendor = "Mountain"; + type = DEVICE_TYPE_KEYBOARD; + description = "Mountain Everest Keyboard 60% Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = MOUNTAIN60_KEYBOARD_MODE_CUSTOM; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Direct.brightness_min = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MIN; + Direct.brightness = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Direct.brightness_max = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = MOUNTAIN60_KEYBOARD_MODE_OFF; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = MOUNTAIN60_KEYBOARD_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.brightness_min = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MIN; + Static.brightness = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Static.brightness_max = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode ColorWaveRainbow; + ColorWaveRainbow.name = "Rainbow Wave"; + ColorWaveRainbow.value = MOUNTAIN60_KEYBOARD_MODE_COLOR_WAVE; + ColorWaveRainbow.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_MANUAL_SAVE; + ColorWaveRainbow.brightness_min = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MIN; + ColorWaveRainbow.brightness = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + ColorWaveRainbow.brightness_max = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + ColorWaveRainbow.speed_min = MOUNTAIN60_KEYBOARD_SPEED_MIN; + ColorWaveRainbow.speed = MOUNTAIN60_KEYBOARD_SPEED_DEFAULT; + ColorWaveRainbow.speed_max = MOUNTAIN60_KEYBOARD_SPEED_MAX; + ColorWaveRainbow.color_mode = MODE_COLORS_RANDOM; + + modes.push_back(ColorWaveRainbow); + + mode ColorWave; + ColorWave.name = "ColorWave"; + ColorWave.value = MOUNTAIN60_KEYBOARD_MODE_COLOR_WAVE; + ColorWave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_MANUAL_SAVE; + ColorWave.brightness_min = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MIN; + ColorWave.brightness = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + ColorWave.brightness_max = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + ColorWave.speed_min = MOUNTAIN60_KEYBOARD_SPEED_MIN; + ColorWave.speed = MOUNTAIN60_KEYBOARD_SPEED_DEFAULT; + ColorWave.speed_max = MOUNTAIN60_KEYBOARD_SPEED_MAX; + ColorWave.colors_min = 1; + ColorWave.colors_max = 2; + ColorWave.colors.resize(2); + ColorWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(ColorWave); + + mode Tornado; + Tornado.name = "Tornado"; + Tornado.value = MOUNTAIN60_KEYBOARD_MODE_TORNADO; + Tornado.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_MANUAL_SAVE; + Tornado.brightness_min = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MIN; + Tornado.brightness = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Tornado.brightness_max = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Tornado.speed_min = MOUNTAIN60_KEYBOARD_SPEED_MIN; + Tornado.speed = MOUNTAIN60_KEYBOARD_SPEED_DEFAULT; + Tornado.speed_max = MOUNTAIN60_KEYBOARD_SPEED_MAX; + Tornado.colors_min = 1; + Tornado.colors_max = 1; + Tornado.colors.resize(1); + Tornado.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Tornado); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = MOUNTAIN60_KEYBOARD_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Breathing.brightness_min = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MIN; + Breathing.brightness = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Breathing.brightness_max = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Breathing.speed_min = MOUNTAIN60_KEYBOARD_SPEED_MIN; + Breathing.speed = MOUNTAIN60_KEYBOARD_SPEED_DEFAULT; + Breathing.speed_max = MOUNTAIN60_KEYBOARD_SPEED_MAX; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(2); + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Breathing); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = MOUNTAIN60_KEYBOARD_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Reactive.brightness_min = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MIN; + Reactive.brightness = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Reactive.brightness_max = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Reactive.speed_min = MOUNTAIN60_KEYBOARD_SPEED_MIN; + Reactive.speed = MOUNTAIN60_KEYBOARD_SPEED_DEFAULT; + Reactive.speed_max = MOUNTAIN60_KEYBOARD_SPEED_MAX; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.colors_min = 1; + Reactive.colors_max = 2; + Reactive.colors.resize(2); + modes.push_back(Reactive); + + mode Matrix; + Matrix.name = "Matrix"; + Matrix.value = MOUNTAIN60_KEYBOARD_MODE_MATRIX; + Matrix.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Matrix.brightness_min = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MIN; + Matrix.brightness = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Matrix.brightness_max = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Matrix.speed_min = MOUNTAIN60_KEYBOARD_SPEED_MIN; + Matrix.speed = MOUNTAIN60_KEYBOARD_SPEED_DEFAULT; + Matrix.speed_max = MOUNTAIN60_KEYBOARD_SPEED_MAX; + Matrix.color_mode = MODE_COLORS_MODE_SPECIFIC; + Matrix.colors_min = 1; + Matrix.colors_max = 2; + Matrix.colors.resize(2); + modes.push_back(Matrix); + + mode Yeti; + Yeti.name = "Yeti"; + Yeti.value = MOUNTAIN60_KEYBOARD_MODE_YETI; + Yeti.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Yeti.brightness_min = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MIN; + Yeti.brightness = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Yeti.brightness_max = MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX; + Yeti.speed_min = MOUNTAIN60_KEYBOARD_SPEED_MIN; + Yeti.speed = MOUNTAIN60_KEYBOARD_SPEED_DEFAULT; + Yeti.speed_max = MOUNTAIN60_KEYBOARD_SPEED_MAX; + Yeti.color_mode = MODE_COLORS_MODE_SPECIFIC; + Yeti.colors_min = 1; + Yeti.colors_max = 2; + Yeti.colors.resize(2); + modes.push_back(Yeti); + + active_mode = 0; + current_mode_value = -1; + + SetupZones(); + + /*-----------------------------------------------------*\ + | The Mountain Everest 60 keyboard need to send a | + | specific packet frequently so that leds get updated | + \*-----------------------------------------------------*/ + mountain_thread = new std::thread(&RGBController_Mountain60Keyboard::UpdateMountain, this); + mountain_thread_running = true; +} + +RGBController_Mountain60Keyboard::~RGBController_Mountain60Keyboard() +{ + mountain_thread_running = false; + mountain_thread->join(); + delete mountain_thread; + + /*-----------------------------------------------------*\ + | Delete the matrix map | + \*-----------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].type == ZONE_TYPE_MATRIX) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_Mountain60Keyboard::SetupZones() +{ + KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ANSI_QWERTY, KEYBOARD_SIZE_SIXTY, mountain60_layout); + new_kb.ChangeKeys(mountain60_keyboard_overlay_no_numpad); + + zone new_zone; + matrix_map_type * new_map = new matrix_map_type; + + new_zone.name = "Mountain Everest 60"; + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.matrix_map = new_map; + new_zone.matrix_map->height = new_kb.GetRowCount(); + new_zone.matrix_map->width = new_kb.GetColumnCount(); + new_zone.matrix_map->map = new unsigned int[new_map->height * new_map->width]; + new_zone.leds_count = new_kb.GetKeyCount(); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT); + + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + leds.push_back(new_led); + } + + zones.push_back(new_zone); + + SetupColors(); +} + +void RGBController_Mountain60Keyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Mountain60Keyboard::DeviceUpdateLEDs() +{ + unsigned char* color_data = new unsigned char[(leds.size()*4)]; + + /*-----------------------------------------------------*\ + | Filling the color_data vector with progressive index | + | leaving space for RGB data | + \*-----------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + const unsigned int idx = led_idx * 4; + color_data[idx] = leds[led_idx].value; + color_data[idx + 1] = RGBGetRValue(colors[led_idx]); + color_data[idx + 2] = RGBGetGValue(colors[led_idx]); + color_data[idx + 3] = RGBGetBValue(colors[led_idx]); + } + + controller->SendDirect(modes[active_mode].brightness, color_data, ((unsigned int)leds.size() * 4)); + delete[] color_data; +} + +void RGBController_Mountain60Keyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Mountain60Keyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Mountain60Keyboard::DeviceUpdateMode() +{ + if(modes[active_mode].value != current_mode_value) + { + current_mode_value = modes[active_mode].value; + controller->SelectMode(modes[active_mode].value); + } + + if(modes[active_mode].color_mode != MODE_FLAG_HAS_PER_LED_COLOR) + { + controller->SendModeDetails(&modes[active_mode]); + } +} + +void RGBController_Mountain60Keyboard::DeviceSaveMode() +{ + controller->SaveData(modes[active_mode].value); +} + +void RGBController_Mountain60Keyboard::UpdateMountain() +{ + while(mountain_thread_running.load()) + { + std::this_thread::sleep_for(MOUNTAIN60_KEEP_LIVE_PERIOD); + + controller->UpdateData(); + } +} diff --git a/Controllers/MountainKeyboardController/RGBController_Mountain60Keyboard.h b/Controllers/MountainKeyboardController/RGBController_Mountain60Keyboard.h new file mode 100644 index 0000000..94ebd25 --- /dev/null +++ b/Controllers/MountainKeyboardController/RGBController_Mountain60Keyboard.h @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| RGBController_MountainKeyboard.h | +| | +| RGBController for Mountain keyboard | +| | +| O'D.Sæzl Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "Mountain60KeyboardController.h" + +#define MOUNTAIN60_KEYBOARD_BRIGHTNESS_MIN 0 +#define MOUNTAIN60_KEYBOARD_BRIGHTNESS_MAX 4 + +#define MOUNTAIN60_KEYBOARD_SPEED_MIN 0 +#define MOUNTAIN60_KEYBOARD_SPEED_MAX 4 +#define MOUNTAIN60_KEYBOARD_SPEED_DEFAULT 2 +#define MOUNTAIN60_KEEP_LIVE_PERIOD 500ms + + +class RGBController_Mountain60Keyboard : public RGBController +{ +public: + RGBController_Mountain60Keyboard(Mountain60KeyboardController* controller_ptr); + ~RGBController_Mountain60Keyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + void UpdateMountain(); + +private: + Mountain60KeyboardController* controller; + int current_mode_value; + std::thread* mountain_thread; + std::atomic mountain_thread_running; +}; diff --git a/Controllers/MountainKeyboardController/RGBController_MountainKeyboard.cpp b/Controllers/MountainKeyboardController/RGBController_MountainKeyboard.cpp new file mode 100644 index 0000000..e4f54d0 --- /dev/null +++ b/Controllers/MountainKeyboardController/RGBController_MountainKeyboard.cpp @@ -0,0 +1,1084 @@ +/*---------------------------------------------------------*\ +| RGBController_MountainKeyboard.cpp | +| | +| RGBController for Mountain keyboard | +| | +| Wojciech Lazarski Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_MountainKeyboard.h" + +static const unsigned char colorwave_speed_values [MOUNTAIN_KEYBOARD_SPEED_MAX+1] = { 10, 9, 8, 7, 6}; +static const unsigned char tornado_speed_values [MOUNTAIN_KEYBOARD_SPEED_MAX+1] = { 10, 9, 8, 7, 6}; +static const unsigned char breathing_speed_values [MOUNTAIN_KEYBOARD_SPEED_MAX+1] = { 5, 4, 3, 1, 0}; +static const unsigned char matrix_speed_values [MOUNTAIN_KEYBOARD_SPEED_MAX+1] = { 20, 15, 10, 5, 0}; +static const unsigned char reactive_speed_values [MOUNTAIN_KEYBOARD_SPEED_MAX+1] = { 5, 4, 3, 1, 0}; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +#define KEYBOARD_MATRIX_TKL_HEIGHT 6 +#define KEYBOARD_MATRIX_TKL_WIDTH 19 +#define KEYBOARD_MATRIX_TKL_KEYS_NO 87 + +#define KEYBOARD_MATRIX_NUM_HEIGHT 6 +#define KEYBOARD_MATRIX_NUM_WIDTH 4 +#define KEYBOARD_MATRIX_NUM_KEYS_NO 17 + +#define KEYBOARD_MATRIX_EDGE_TKL_HEIGHT 6 +#define KEYBOARD_MATRIX_EDGE_TKL_WIDTH 14 +#define KEYBOARD_MATRIX_EDGE_TKL_KEYS_NO 32 + +#define KEYBOARD_MATRIX_EDGE_NUMPAD_HEIGHT 6 +#define KEYBOARD_MATRIX_EDGE_NUMPAD_WIDTH 5 +#define KEYBOARD_MATRIX_EDGE_NUMPAD_KEYS_NO 14 + +#define KEYBOARD_MATRIX_KEYS_NO (KEYBOARD_MATRIX_TKL_KEYS_NO + KEYBOARD_MATRIX_NUM_KEYS_NO) +#define KEYBOARD_MATRIX_EDGE_KEYS_NO (KEYBOARD_MATRIX_EDGE_TKL_KEYS_NO + KEYBOARD_MATRIX_EDGE_NUMPAD_KEYS_NO) + +/*-------------------------------*\ +| TODO: Detect detached keypad | +\*-------------------------------*/ +enum +{ + IDX_EDGE_00, + IDX_EDGE_01, + IDX_EDGE_02, + IDX_EDGE_03, + IDX_EDGE_04, + IDX_EDGE_05, + IDX_EDGE_06, + IDX_EDGE_07, + IDX_EDGE_08, + IDX_EDGE_09, + IDX_EDGE_10, + IDX_EDGE_11, + IDX_EDGE_12, + IDX_EDGE_13, + IDX_EDGE_14, + IDX_EDGE_15, + IDX_EDGE_16, + IDX_EDGE_17, + IDX_EDGE_18, + IDX_EDGE_19, + IDX_EDGE_20, + IDX_EDGE_21, + IDX_EDGE_22, + IDX_EDGE_23, + IDX_EDGE_24, + IDX_EDGE_25, + IDX_EDGE_26, + IDX_EDGE_27, + IDX_EDGE_28, + IDX_EDGE_29, + IDX_EDGE_30, + IDX_EDGE_31, + IDX_EDGE_32, + IDX_EDGE_33, + IDX_EDGE_34, + IDX_EDGE_35, + IDX_EDGE_36, + IDX_EDGE_37, + IDX_EDGE_38, + IDX_EDGE_39, + IDX_EDGE_40, + IDX_EDGE_41, + IDX_EDGE_42, + IDX_EDGE_43, + IDX_EDGE_44, + IDX_EDGE_45 +}; + +enum +{ + IDX_KEY_EN_ESCAPE = 0, + IDX_KEY_EN_BACK_TICK, + IDX_KEY_EN_TAB, + IDX_KEY_EN_CAPS_LOCK, + IDX_KEY_EN_LEFT_SHIFT, + IDX_KEY_EN_LEFT_CONTROL, + IDX_KEY_EN_NUMPAD_LOCK, + IDX_KEY_EN_NUMPAD_PLUS, + IDX_KEY_EN_UNUSED_8, + IDX_KEY_EN_F1, + IDX_KEY_EN_1, + IDX_KEY_EN_Q, + IDX_KEY_EN_A, + IDX_KEY_EN_UNUSED_13, + IDX_KEY_EN_LEFT_WINDOWS, + IDX_KEY_EN_NUMPAD_TIMES, + IDX_KEY_EN_NUMPAD_MINUS, + IDX_KEY_EN_UNUSED_17, + IDX_KEY_EN_F2, + IDX_KEY_EN_2, + IDX_KEY_EN_W, + IDX_KEY_EN_S, + IDX_KEY_EN_Z, + IDX_KEY_EN_LEFT_ALT, + IDX_KEY_EN_NUMPAD_DIVIDE, + IDX_KEY_EN_UNUSED_25, + IDX_KEY_EN_UNUSED_26, + IDX_KEY_EN_F3, + IDX_KEY_EN_3, + IDX_KEY_EN_E, + IDX_KEY_EN_D, + IDX_KEY_EN_X, + IDX_KEY_EN_UNUSED_32, + IDX_KEY_EN_NUMPAD_ENTER, + IDX_KEY_EN_NUMPAD_1, + IDX_KEY_EN_UNUSED_35, + IDX_KEY_EN_F4, + IDX_KEY_EN_4, + IDX_KEY_EN_R, + IDX_KEY_EN_F, + IDX_KEY_EN_C, + IDX_KEY_EN_SPACE, + IDX_KEY_EN_NUMPAD_2, + IDX_KEY_EN_NUMPAD_3, + IDX_KEY_EN_UNUSED_44, + IDX_KEY_EN_F5, + IDX_KEY_EN_5, + IDX_KEY_EN_T, + IDX_KEY_EN_G, + IDX_KEY_EN_V, + IDX_KEY_EN_UNUSED_50, + IDX_KEY_EN_NUMPAD_4, + IDX_KEY_EN_NUMPAD_5, + IDX_KEY_EN_UNUSED_53, + IDX_KEY_EN_F6, + IDX_KEY_EN_6, + IDX_KEY_EN_Y, + IDX_KEY_EN_H, + IDX_KEY_EN_B, + IDX_KEY_EN_UNUSED_59, + IDX_KEY_EN_NUMPAD_6, + IDX_KEY_EN_NUMPAD_7, + IDX_KEY_EN_UNUSED_62, + IDX_KEY_EN_F7, + IDX_KEY_EN_7, + IDX_KEY_EN_U, + IDX_KEY_EN_J, + IDX_KEY_EN_N, + IDX_KEY_EN_RIGHT_ALT, + IDX_KEY_EN_NUMPAD_8, + IDX_KEY_EN_NUMPAD_9, + IDX_KEY_EN_UNUSED_71, + IDX_KEY_EN_F8, + IDX_KEY_EN_8, + IDX_KEY_EN_I, + IDX_KEY_EN_K, + IDX_KEY_EN_M, + IDX_KEY_EN_RIGHT_WINDOWS, + IDX_KEY_EN_NUMPAD_0, + IDX_KEY_EN_NUMPAD_PERIOD, + IDX_KEY_EN_UNUSED_80, + IDX_KEY_EN_F9, + IDX_KEY_EN_9, + IDX_KEY_EN_O, + IDX_KEY_EN_L, + IDX_KEY_EN_COMMA, + IDX_KEY_EN_RIGHT_FUNCTION, + IDX_KEY_EN_BACKSPACE, + IDX_KEY_EN_DELETE, + IDX_KEY_EN_UNUSED_89, + IDX_KEY_EN_F10, + IDX_KEY_EN_0, + IDX_KEY_EN_P, + IDX_KEY_EN_SEMICOLON, + IDX_KEY_EN_PERIOD, + IDX_KEY_EN_RIGHT_CONTROL, + IDX_KEY_EN_INSERT, + IDX_KEY_EN_END, + IDX_KEY_EN_UNUSED_98, + IDX_KEY_EN_F11, + IDX_KEY_EN_MINUS, + IDX_KEY_EN_LEFT_BRACKET, + IDX_KEY_EN_QUOTE, + IDX_KEY_EN_FORWARD_SLASH, + IDX_KEY_EN_LEFT_ARROW, + IDX_KEY_EN_HOME, + IDX_KEY_EN_PAGE_DOWN, + IDX_KEY_EN_UNUSED_107, + IDX_KEY_EN_F12, + IDX_KEY_EN_EQUALS, + IDX_KEY_EN_RIGHT_BRACKET, + IDX_KEY_EN_UNUSED_111, + IDX_KEY_EN_UNUSED_112, + IDX_KEY_EN_DOWN_ARROW, + IDX_KEY_EN_SCROLL_LOCK, + IDX_KEY_EN_PAGE_UP, + IDX_KEY_EN_UNUSED_116, + IDX_KEY_EN_PRINT_SCREEN, + IDX_KEY_EN_UNUSED_118, + IDX_KEY_EN_ANSI_BACK_SLASH, + IDX_KEY_EN_ANSI_ENTER, + IDX_KEY_EN_RIGHT_SHIFT, + IDX_KEY_EN_RIGHT_ARROW, + IDX_KEY_EN_PAUSE_BREAK, + IDX_KEY_EN_UP_ARROW, +}; + +enum +{ + IDX_WHEEL +}; + +static unsigned int matrix_tkl_map[KEYBOARD_MATRIX_TKL_HEIGHT][KEYBOARD_MATRIX_TKL_WIDTH] = + { { 0, NA, 8, 14, 19, 24, NA, 30, 36, 40, 45, NA, 53, 59, 65, 70, 74, 78, 83}, + { 1, 6, 9, 15, 20, 25, 29, 31, 37, 41, 46, NA, 54, 60, 66, NA, 75, 79, 84}, + { 2, NA, 10, 16, 21, 26, NA, 32, 38, 42, 47, 50, 55, 61, 67, 71, 76, 80, 85}, + { 3, NA, 11, 17, 22, 27, NA, 33, 39, 43, 48, 51, 56, 62, NA, 72, NA, NA, NA}, + { 4, NA, 12, 18, 23, 28, NA, 34, NA, 44, 49, 52, 57, 63, 68, NA, NA, 81, NA}, + { 5, 7, 13, NA, NA, NA, NA, 35, NA, NA, NA, NA, 58, 64, 69, 73, 77, 82, 86} }; + +static unsigned int matrix_num_map[KEYBOARD_MATRIX_NUM_HEIGHT][KEYBOARD_MATRIX_NUM_WIDTH] = + { { NA, NA, NA, NA }, + { 0, 5, 9, 14 }, + { 1, 6, 10, 15 }, + { 2, 7, 11, NA }, + { 3, 8, 12, 16 }, + { 4, NA, 13, NA } }; + +static unsigned int matrix_tkl_map_lut[KEYBOARD_MATRIX_TKL_KEYS_NO] = +{ + IDX_KEY_EN_ESCAPE, IDX_KEY_EN_BACK_TICK, IDX_KEY_EN_TAB, IDX_KEY_EN_CAPS_LOCK, IDX_KEY_EN_LEFT_SHIFT, IDX_KEY_EN_LEFT_CONTROL, + IDX_KEY_EN_1, IDX_KEY_EN_LEFT_WINDOWS, + IDX_KEY_EN_F1, IDX_KEY_EN_2, IDX_KEY_EN_Q, IDX_KEY_EN_A, IDX_KEY_EN_Z, IDX_KEY_EN_LEFT_ALT, + IDX_KEY_EN_F2, IDX_KEY_EN_3, IDX_KEY_EN_W, IDX_KEY_EN_S, IDX_KEY_EN_X, + IDX_KEY_EN_F3, IDX_KEY_EN_4, IDX_KEY_EN_E, IDX_KEY_EN_D, IDX_KEY_EN_C, + IDX_KEY_EN_F4, IDX_KEY_EN_5, IDX_KEY_EN_R, IDX_KEY_EN_F, IDX_KEY_EN_V, + IDX_KEY_EN_6, + IDX_KEY_EN_F5, IDX_KEY_EN_7, IDX_KEY_EN_T, IDX_KEY_EN_G, IDX_KEY_EN_B, IDX_KEY_EN_SPACE, + IDX_KEY_EN_F6, IDX_KEY_EN_8, IDX_KEY_EN_Y, IDX_KEY_EN_H, + IDX_KEY_EN_F7, IDX_KEY_EN_9, IDX_KEY_EN_U, IDX_KEY_EN_J, IDX_KEY_EN_N, + IDX_KEY_EN_F8, IDX_KEY_EN_0, IDX_KEY_EN_I, IDX_KEY_EN_K, IDX_KEY_EN_M, + IDX_KEY_EN_O, IDX_KEY_EN_L, IDX_KEY_EN_COMMA, + IDX_KEY_EN_F9, IDX_KEY_EN_MINUS, IDX_KEY_EN_P, IDX_KEY_EN_SEMICOLON, IDX_KEY_EN_PERIOD, IDX_KEY_EN_RIGHT_ALT, + IDX_KEY_EN_F10, IDX_KEY_EN_EQUALS, IDX_KEY_EN_LEFT_BRACKET, IDX_KEY_EN_QUOTE, IDX_KEY_EN_FORWARD_SLASH, IDX_KEY_EN_RIGHT_WINDOWS, + IDX_KEY_EN_F11, IDX_KEY_EN_BACKSPACE, IDX_KEY_EN_RIGHT_BRACKET, IDX_KEY_EN_RIGHT_SHIFT, IDX_KEY_EN_RIGHT_FUNCTION, + IDX_KEY_EN_F12, IDX_KEY_EN_ANSI_BACK_SLASH, IDX_KEY_EN_ANSI_ENTER, IDX_KEY_EN_RIGHT_CONTROL, + IDX_KEY_EN_PRINT_SCREEN, IDX_KEY_EN_INSERT, IDX_KEY_EN_DELETE, IDX_KEY_EN_LEFT_ARROW, + IDX_KEY_EN_SCROLL_LOCK, IDX_KEY_EN_HOME, IDX_KEY_EN_END, IDX_KEY_EN_UP_ARROW, IDX_KEY_EN_DOWN_ARROW, + IDX_KEY_EN_PAUSE_BREAK, IDX_KEY_EN_PAGE_UP, IDX_KEY_EN_PAGE_DOWN, IDX_KEY_EN_RIGHT_ARROW +}; + +static unsigned int matrix_num_map_lut[KEYBOARD_MATRIX_NUM_KEYS_NO] = +{ + IDX_KEY_EN_NUMPAD_LOCK, IDX_KEY_EN_NUMPAD_7, IDX_KEY_EN_NUMPAD_4, IDX_KEY_EN_NUMPAD_1, IDX_KEY_EN_NUMPAD_0, + IDX_KEY_EN_NUMPAD_DIVIDE, IDX_KEY_EN_NUMPAD_8, IDX_KEY_EN_NUMPAD_5, IDX_KEY_EN_NUMPAD_2, + IDX_KEY_EN_NUMPAD_TIMES, IDX_KEY_EN_NUMPAD_9, IDX_KEY_EN_NUMPAD_6, IDX_KEY_EN_NUMPAD_3, IDX_KEY_EN_NUMPAD_PERIOD, + IDX_KEY_EN_NUMPAD_MINUS, IDX_KEY_EN_NUMPAD_PLUS, IDX_KEY_EN_NUMPAD_ENTER +}; + +static unsigned int matrix_edge_tkl_map_lut[KEYBOARD_MATRIX_EDGE_TKL_KEYS_NO] = +{ + IDX_EDGE_16, IDX_EDGE_17, IDX_EDGE_18, IDX_EDGE_19, + IDX_EDGE_13, IDX_EDGE_20, + IDX_EDGE_14, IDX_EDGE_21, + IDX_EDGE_15, IDX_EDGE_22, + IDX_EDGE_07, IDX_EDGE_23, + IDX_EDGE_06, IDX_EDGE_24, + IDX_EDGE_05, IDX_EDGE_25, + IDX_EDGE_04, IDX_EDGE_26, + IDX_EDGE_03, IDX_EDGE_27, + IDX_EDGE_02, IDX_EDGE_28, + IDX_EDGE_01, IDX_EDGE_29, + IDX_EDGE_00, IDX_EDGE_30, + IDX_EDGE_45, IDX_EDGE_12, + IDX_EDGE_09, IDX_EDGE_08, IDX_EDGE_10, IDX_EDGE_11 +}; + +static unsigned int matrix_edge_numpad_map_lut[KEYBOARD_MATRIX_EDGE_NUMPAD_KEYS_NO]= +{ + IDX_EDGE_31, IDX_EDGE_32, IDX_EDGE_33, IDX_EDGE_34, + IDX_EDGE_44, IDX_EDGE_35, + IDX_EDGE_43, IDX_EDGE_36, + IDX_EDGE_42, IDX_EDGE_37, + IDX_EDGE_41, IDX_EDGE_40, IDX_EDGE_39, IDX_EDGE_38 +}; + +static unsigned int single_wheel_lut[1]= +{ + IDX_WHEEL +}; + +static unsigned int matrix_edge_tkl_map[KEYBOARD_MATRIX_EDGE_TKL_HEIGHT][KEYBOARD_MATRIX_EDGE_TKL_WIDTH] = + { { NA, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, NA }, + { 0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 28 }, + { 1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 29 }, + { 2, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 30 }, + { 3, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 31 }, + { NA, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, NA } }; + +static unsigned int matrix_edge_numpad_map[KEYBOARD_MATRIX_EDGE_NUMPAD_HEIGHT][KEYBOARD_MATRIX_EDGE_NUMPAD_WIDTH] = + { { NA, 4, 6, 8, NA }, + { 0, NA, NA, NA, 10 }, + { 1, NA, NA, NA, 11 }, + { 2, NA, NA, NA, 12 }, + { 3, NA, NA, NA, 13 }, + { NA, 5, 7, 9, NA } }; + +/*---------------------------------------------------*\ +| TODO: Figure out how to read RGB state | +| TODO: separatee display brughness from backlight | +\*---------------------------------------------------*/ + +typedef struct +{ + const char* name; + zone_type type; + unsigned int* ptr; + unsigned int* ptr_lut; + unsigned int size; + unsigned int height; + unsigned int width; +} mountain_zone_t; + +static const mountain_zone_t zone_definitions[] = +{ + { + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + (unsigned int*)&matrix_tkl_map, + (unsigned int*)&matrix_tkl_map_lut, + KEYBOARD_MATRIX_TKL_KEYS_NO, + KEYBOARD_MATRIX_TKL_HEIGHT, + KEYBOARD_MATRIX_TKL_WIDTH + }, + { + "Numpad", + ZONE_TYPE_MATRIX, + (unsigned int*)&matrix_num_map, + (unsigned int*)&matrix_num_map_lut, + KEYBOARD_MATRIX_NUM_KEYS_NO, + KEYBOARD_MATRIX_NUM_HEIGHT, + KEYBOARD_MATRIX_NUM_WIDTH + }, + { + "Keyboard Edge", + ZONE_TYPE_MATRIX, + (unsigned int*)&matrix_edge_tkl_map, + (unsigned int*)&matrix_edge_tkl_map_lut, + KEYBOARD_MATRIX_EDGE_TKL_KEYS_NO, + KEYBOARD_MATRIX_EDGE_TKL_HEIGHT, + KEYBOARD_MATRIX_EDGE_TKL_WIDTH + }, + { + "Numpad Edge", + ZONE_TYPE_MATRIX, + (unsigned int*)&matrix_edge_numpad_map, + (unsigned int*)&matrix_edge_numpad_map_lut, + KEYBOARD_MATRIX_EDGE_NUMPAD_KEYS_NO, + KEYBOARD_MATRIX_EDGE_NUMPAD_HEIGHT, + KEYBOARD_MATRIX_EDGE_NUMPAD_WIDTH + }, + { + "Wheel Selector", + ZONE_TYPE_SINGLE, + NULL, + (unsigned int *) &single_wheel_lut, + 1, + 1, + 1 + } +}; + + +static const char *led_names[MOUNTAIN_KEYBOARD_MAX_TRANSFER_COLORS] = +{ + KEY_EN_ESCAPE, + KEY_EN_BACK_TICK, + KEY_EN_TAB, + KEY_EN_CAPS_LOCK, + KEY_EN_LEFT_SHIFT, + KEY_EN_LEFT_CONTROL, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_PLUS, + KEY_EN_UNUSED,/*8*/ + KEY_EN_F1, + KEY_EN_1, /*10*/ + KEY_EN_Q, + KEY_EN_A, + KEY_EN_UNUSED,/*13*/ + KEY_EN_LEFT_WINDOWS, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, + KEY_EN_UNUSED,/*17*/ + KEY_EN_F2, + KEY_EN_2, + KEY_EN_W, + KEY_EN_S, + KEY_EN_Z, + KEY_EN_LEFT_ALT, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_UNUSED,/*25*/ + KEY_EN_UNUSED,/*26*/ + KEY_EN_F3, + KEY_EN_3, + KEY_EN_E, + KEY_EN_D, + KEY_EN_X, + KEY_EN_UNUSED,/*32*/ + KEY_EN_NUMPAD_ENTER, + KEY_EN_NUMPAD_1, + KEY_EN_UNUSED,/*35*/ + KEY_EN_F4, + KEY_EN_4, + KEY_EN_R, + KEY_EN_F, + KEY_EN_C, + KEY_EN_SPACE, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + KEY_EN_UNUSED, /*44*/ + KEY_EN_F5, + KEY_EN_5, + KEY_EN_T, + KEY_EN_G, + KEY_EN_V, + KEY_EN_UNUSED, /*50*/ + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_UNUSED, /*53*/ + KEY_EN_F6, + KEY_EN_6, + KEY_EN_Y, + KEY_EN_H, + KEY_EN_B, + KEY_EN_UNUSED, /*59*/ + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_7, + KEY_EN_UNUSED, /*62*/ + KEY_EN_F7, + KEY_EN_7, + KEY_EN_U, + KEY_EN_J, + KEY_EN_N, + KEY_EN_RIGHT_ALT, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_UNUSED, /*71*/ + KEY_EN_F8, + KEY_EN_8, + KEY_EN_I, + KEY_EN_K, + KEY_EN_M, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD, + KEY_EN_UNUSED, /*80*/ + KEY_EN_F9, + KEY_EN_9, + KEY_EN_O, + KEY_EN_L, + KEY_EN_COMMA, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_BACKSPACE, + KEY_EN_DELETE, + KEY_EN_UNUSED,/*89*/ + KEY_EN_F10, + KEY_EN_0, + KEY_EN_P, + KEY_EN_SEMICOLON, + KEY_EN_PERIOD, + KEY_EN_RIGHT_CONTROL, + KEY_EN_INSERT, + KEY_EN_END, + KEY_EN_UNUSED,/*98*/ + KEY_EN_F11, + KEY_EN_MINUS, + KEY_EN_LEFT_BRACKET, + KEY_EN_QUOTE, + KEY_EN_FORWARD_SLASH, + KEY_EN_LEFT_ARROW, + KEY_EN_HOME, + KEY_EN_PAGE_DOWN, + KEY_EN_UNUSED,/*107*/ + KEY_EN_F12, + KEY_EN_EQUALS, + KEY_EN_RIGHT_BRACKET, + KEY_EN_UNUSED,/*111*/ + KEY_EN_UNUSED,/*112*/ + KEY_EN_DOWN_ARROW, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAGE_UP, + KEY_EN_UNUSED,/*116*/ + KEY_EN_PRINT_SCREEN, + KEY_EN_UNUSED,/*118*/ + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_ANSI_ENTER, + KEY_EN_RIGHT_SHIFT, + KEY_EN_RIGHT_ARROW, + KEY_EN_PAUSE_BREAK, + KEY_EN_UP_ARROW, +}; + +/**------------------------------------------------------------------*\ + @name Mountain Keyboard + @category Keyboard + @type USB + @save :white_check_mark: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectMountainKeyboardControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_MountainKeyboard::RGBController_MountainKeyboard(MountainKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + memset(wheel_color, 0, 3); + + wheel_config * wheel_conf = controller->GetWheelConfig(); + + if(wheel_conf != nullptr) + { + wheel_color[0] = wheel_conf->r; + wheel_color[1] = wheel_conf->g; + wheel_color[2] = wheel_conf->b; + } + + name = controller->GetNameString(); + vendor = "Mountain"; + type = DEVICE_TYPE_KEYBOARD; + description = "Mountain Everest Keyboard"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = MOUNTAIN_KEYBOARD_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Custom; + Custom.name = "Custom"; + Custom.value = MOUNTAIN_KEYBOARD_MODE_DIRECT; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Custom.brightness_min = MOUNTAIN_KEYBOARD_BRIGHTNESS_MIN; + Custom.brightness = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Custom.brightness_max = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode Off; + Off.name = "Off"; + Off.value = MOUNTAIN_KEYBOARD_MODE_OFF; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = MOUNTAIN_KEYBOARD_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Static.brightness_min = MOUNTAIN_KEYBOARD_BRIGHTNESS_MIN; + Static.brightness = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Static.brightness_max = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode ColorWaveRainbow; + ColorWaveRainbow.name = "Rainbow Wave"; + ColorWaveRainbow.value = MOUNTAIN_KEYBOARD_MODE_COLOR_WAVE; + ColorWaveRainbow.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_MANUAL_SAVE; + ColorWaveRainbow.brightness_min = MOUNTAIN_KEYBOARD_BRIGHTNESS_MIN; + ColorWaveRainbow.brightness = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + ColorWaveRainbow.brightness_max = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + ColorWaveRainbow.speed_min = MOUNTAIN_KEYBOARD_SPEED_MIN; + ColorWaveRainbow.speed = MOUNTAIN_KEYBOARD_SPEED_DEFAULT; + ColorWaveRainbow.speed_max = MOUNTAIN_KEYBOARD_SPEED_MAX; + ColorWaveRainbow.color_mode = MODE_COLORS_RANDOM; + modes.push_back(ColorWaveRainbow); + + mode ColorWave; + ColorWave.name = "ColorWave"; + ColorWave.value = MOUNTAIN_KEYBOARD_MODE_COLOR_WAVE; + ColorWave.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_MANUAL_SAVE; + ColorWave.brightness_min = MOUNTAIN_KEYBOARD_BRIGHTNESS_MIN; + ColorWave.brightness = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + ColorWave.brightness_max = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + ColorWave.speed_min = MOUNTAIN_KEYBOARD_SPEED_MIN; + ColorWave.speed = MOUNTAIN_KEYBOARD_SPEED_DEFAULT; + ColorWave.speed_max = MOUNTAIN_KEYBOARD_SPEED_MAX; + ColorWave.colors_min = 1; + ColorWave.colors_max = 2; + ColorWave.colors.resize(2); + ColorWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(ColorWave); + + mode Tornado; + Tornado.name = "Tornado"; + Tornado.value = MOUNTAIN_KEYBOARD_MODE_TORNADO; + Tornado.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_MANUAL_SAVE; + Tornado.brightness_min = MOUNTAIN_KEYBOARD_BRIGHTNESS_MIN; + Tornado.brightness = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Tornado.brightness_max = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Tornado.speed_min = MOUNTAIN_KEYBOARD_SPEED_MIN; + Tornado.speed = MOUNTAIN_KEYBOARD_SPEED_DEFAULT; + Tornado.speed_max = MOUNTAIN_KEYBOARD_SPEED_MAX; + Tornado.colors_min = 1; + Tornado.colors_max = 1; + Tornado.colors.resize(1); + Tornado.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Tornado); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = MOUNTAIN_KEYBOARD_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Breathing.brightness_min = MOUNTAIN_KEYBOARD_BRIGHTNESS_MIN; + Breathing.brightness = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Breathing.brightness_max = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Breathing.speed_min = MOUNTAIN_KEYBOARD_SPEED_MIN; + Breathing.speed = MOUNTAIN_KEYBOARD_SPEED_DEFAULT; + Breathing.speed_max = MOUNTAIN_KEYBOARD_SPEED_MAX; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(2); + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Breathing); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = MOUNTAIN_KEYBOARD_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Reactive.brightness_min = MOUNTAIN_KEYBOARD_BRIGHTNESS_MIN; + Reactive.brightness = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Reactive.brightness_max = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Reactive.speed_min = MOUNTAIN_KEYBOARD_SPEED_MIN; + Reactive.speed = MOUNTAIN_KEYBOARD_SPEED_DEFAULT; + Reactive.speed_max = MOUNTAIN_KEYBOARD_SPEED_MAX; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.colors_min = 1; + Reactive.colors_max = 2; + Reactive.colors.resize(2); + modes.push_back(Reactive); + + mode Matrix; + Matrix.name = "Matrix"; + Matrix.value = MOUNTAIN_KEYBOARD_MODE_MATRIX; + Matrix.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Matrix.brightness_min = MOUNTAIN_KEYBOARD_BRIGHTNESS_MIN; + Matrix.brightness = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Matrix.brightness_max = MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX; + Matrix.speed_min = MOUNTAIN_KEYBOARD_SPEED_MIN; + Matrix.speed = MOUNTAIN_KEYBOARD_SPEED_DEFAULT; + Matrix.speed_max = MOUNTAIN_KEYBOARD_SPEED_MAX; + Matrix.color_mode = MODE_COLORS_MODE_SPECIFIC; + Matrix.colors_min = 1; + Matrix.colors_max = 2; + Matrix.colors.resize(2); + modes.push_back(Matrix); + + prv_mode = -1; + active_mode = 0; + + SetupZones(); +} + +RGBController_MountainKeyboard::~RGBController_MountainKeyboard() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_MountainKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < sizeof(zone_definitions)/sizeof(zone_definitions[0]); zone_idx++) + { + zone new_zone; + + new_zone.name = zone_definitions[zone_idx].name; + new_zone.type = zone_definitions[zone_idx].type; + new_zone.leds_min = zone_definitions[zone_idx].size; + new_zone.leds_max = zone_definitions[zone_idx].size; + new_zone.leds_count = zone_definitions[zone_idx].size; + new_zone.matrix_map = NULL; + if (zone_definitions[zone_idx].type == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = zone_definitions[zone_idx].height; + new_zone.matrix_map->width = zone_definitions[zone_idx].width; + new_zone.matrix_map->map = zone_definitions[zone_idx].ptr; + } + zones.push_back(new_zone); + } + + for(unsigned int zone_idx = 0; zone_idx < sizeof(zone_definitions)/sizeof(zone_definitions[0]); zone_idx++) + { + if (zone_definitions[zone_idx].ptr_lut) + { + for(unsigned int led_idx=0;led_idxname = led_names[zone_definitions[zone_idx].ptr_lut[led_idx]]; + } + break; + + case 2: + case 3: + { + new_led->name = zones[zone_idx].name + " LED:"; + new_led->name.append(std::to_string(led_idx + 1)); + } + break; + + case 4: + { + new_led->name = zones[zone_idx].name + " LED"; + } + break; + default: + break; + } + leds.push_back(*new_led); + } + } + } + + SetupColors(); +} + +void RGBController_MountainKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_MountainKeyboard::DeviceUpdate(const mode& current_mode) +{ + switch(current_mode.value) + { + case MOUNTAIN_KEYBOARD_MODE_DIRECT: + { + unsigned char color_data[MOUNTAIN_KEYBOARD_TRANSFER_BUFFER_SIZE] = {0}; + unsigned char color_edge_data[MOUNTAIN_KEYBOARD_TRANSFER_EDGE_BUFFER_SIZE] = {0}; + + for(unsigned int led_idx = 0; led_idx < colors.size(); led_idx++) + { + if(led_idx < zones[0].leds_count) + { + unsigned int zone_led_idx = led_idx; + unsigned int idx = zone_definitions[0].ptr_lut[zone_led_idx]; + color_data[(3 * idx)] = RGBGetRValue(colors[led_idx]); + color_data[(3 * idx)+1] = RGBGetGValue(colors[led_idx]); + color_data[(3 * idx)+2] = RGBGetBValue(colors[led_idx]); + } + else if (led_idx < zones[0].leds_count + zones[1].leds_count) + { + unsigned int zone_led_idx = led_idx - zones[0].leds_count; + unsigned int idx = zone_definitions[1].ptr_lut[zone_led_idx]; + color_data[(3 * idx)] = RGBGetRValue(colors[led_idx]); + color_data[(3 * idx)+1] = RGBGetGValue(colors[led_idx]); + color_data[(3 * idx)+2] = RGBGetBValue(colors[led_idx]); + + } + else if (led_idx < zones[0].leds_count + zones[1].leds_count + zones[2].leds_count) + { + unsigned int zone_led_idx = led_idx - zones[0].leds_count - zones[1].leds_count; + unsigned int idx = zone_definitions[2].ptr_lut[zone_led_idx]; + color_edge_data[(3 * idx)] = RGBGetRValue(colors[led_idx]); + color_edge_data[(3 * idx)+1] = RGBGetGValue(colors[led_idx]); + color_edge_data[(3 * idx)+2] = RGBGetBValue(colors[led_idx]); + } + else if (led_idx < zones[0].leds_count + zones[1].leds_count + zones[2].leds_count + zones[3].leds_count) + { + unsigned int zone_led_idx = led_idx - zones[0].leds_count - zones[1].leds_count - zones[2].leds_count; + unsigned int idx = zone_definitions[3].ptr_lut[zone_led_idx]; + color_edge_data[(3 * idx)] = RGBGetRValue(colors[led_idx]); + color_edge_data[(3 * idx)+1] = RGBGetGValue(colors[led_idx]); + color_edge_data[(3 * idx)+2] = RGBGetBValue(colors[led_idx]); + } + else + { + wheel_color[0] = RGBGetRValue(colors[led_idx]); + wheel_color[1] = RGBGetGValue(colors[led_idx]); + wheel_color[2] = RGBGetBValue(colors[led_idx]); + } + + + } + + /*---------------------------------------------------------*\ + | Check if we running pseud DIRECT mode or CUSTOM one | + \*---------------------------------------------------------*/ + if(current_mode.flags & MODE_FLAG_MANUAL_SAVE) + { + controller->SendDirectColorCmd(false, current_mode.brightness, color_data,MOUNTAIN_KEYBOARD_TRANSFER_BUFFER_SIZE); + controller->SendDirectColorEdgeCmd(false, current_mode.brightness, color_edge_data,MOUNTAIN_KEYBOARD_TRANSFER_EDGE_BUFFER_SIZE); + } + else + { + controller->SendDirectColorCmd(true, current_mode.brightness, color_data,MOUNTAIN_KEYBOARD_TRANSFER_BUFFER_SIZE); + controller->SendDirectColorEdgeCmd(true, current_mode.brightness, color_edge_data,MOUNTAIN_KEYBOARD_TRANSFER_EDGE_BUFFER_SIZE); + } + } + break; + + case MOUNTAIN_KEYBOARD_MODE_OFF: + { + controller->SendOffCmd(); + } + break; + + case MOUNTAIN_KEYBOARD_MODE_STATIC: + { + color_setup setup; + + setup.brightness = current_mode.brightness; + setup.mode.one_color.r = RGBGetRValue(current_mode.colors[0]); + setup.mode.one_color.g = RGBGetGValue(current_mode.colors[0]); + setup.mode.one_color.b = RGBGetBValue(current_mode.colors[0]); + + unsigned char colors [3] = {setup.mode.one_color.r, setup.mode.one_color.g, setup.mode.one_color.b}; + controller->SendColorStaticCmd(setup); + wheel_color[0] = colors[0]; + wheel_color[1] = colors[1]; + wheel_color[2] = colors[2]; + + } + break; + + case MOUNTAIN_KEYBOARD_MODE_COLOR_WAVE: + { + color_setup setup; + + setup.speed = colorwave_speed_values[current_mode.speed]; + setup.direction = ConvertDirection(current_mode.direction,false); + setup.brightness = current_mode.brightness; + + if(current_mode.color_mode == MODE_COLORS_MODE_SPECIFIC) + { + if(current_mode.colors.size() == 2) + { + setup.color_mode = MOUNTAIN_KEYBOARD_COLOR_MODE_DUAL; + setup.mode.two_colors.r1 = RGBGetRValue(current_mode.colors[0]); + setup.mode.two_colors.g1 = RGBGetGValue(current_mode.colors[0]); + setup.mode.two_colors.b1 = RGBGetBValue(current_mode.colors[0]); + setup.mode.two_colors.r2 = RGBGetRValue(current_mode.colors[1]); + setup.mode.two_colors.g2 = RGBGetGValue(current_mode.colors[1]); + setup.mode.two_colors.b2 = RGBGetBValue(current_mode.colors[1]); + } + else + { + if(current_mode.colors.size() == 1) + { + setup.color_mode = MOUNTAIN_KEYBOARD_COLOR_MODE_SINGLE; + setup.mode.one_color.r = RGBGetRValue(current_mode.colors[0]); + setup.mode.one_color.g = RGBGetGValue(current_mode.colors[0]); + setup.mode.one_color.b = RGBGetBValue(current_mode.colors[0]); + } + } + } + else + { + setup.color_mode = MOUNTAIN_KEYBOARD_COLOR_MODE_RAINBOW; + } + + controller->SendColorWaveCmd(setup); + } + break; + + case MOUNTAIN_KEYBOARD_MODE_TORNADO: + { + color_setup setup; + + setup.speed = tornado_speed_values[current_mode.speed]; + setup.direction = ConvertDirection(current_mode.direction,true); + setup.brightness = current_mode.brightness; + + if(current_mode.color_mode == MODE_COLORS_MODE_SPECIFIC) + { + setup.color_mode = MOUNTAIN_KEYBOARD_COLOR_MODE_SINGLE; + setup.mode.one_color.r = RGBGetRValue(current_mode.colors[0]); + setup.mode.one_color.g = RGBGetGValue(current_mode.colors[0]); + setup.mode.one_color.b = RGBGetBValue(current_mode.colors[0]); + } + else + { + setup.color_mode = MOUNTAIN_KEYBOARD_COLOR_MODE_RAINBOW; + } + + controller->SendColorTornadoCmd(setup); + } + break; + + case MOUNTAIN_KEYBOARD_MODE_BREATHING: + { + color_setup setup; + + setup.speed = breathing_speed_values[current_mode.speed]; + setup.brightness = current_mode.brightness; + + if(current_mode.color_mode == MODE_COLORS_MODE_SPECIFIC) + { + if(current_mode.colors.size() == 2) + { + setup.color_mode = MOUNTAIN_KEYBOARD_COLOR_MODE_DUAL; + setup.mode.two_colors.r1 = RGBGetRValue(current_mode.colors[0]); + setup.mode.two_colors.g1 = RGBGetGValue(current_mode.colors[0]); + setup.mode.two_colors.b1 = RGBGetBValue(current_mode.colors[0]); + setup.mode.two_colors.r2 = RGBGetRValue(current_mode.colors[1]); + setup.mode.two_colors.g2 = RGBGetGValue(current_mode.colors[1]); + setup.mode.two_colors.b2 = RGBGetBValue(current_mode.colors[1]); + } + else + { + if(current_mode.colors.size() == 1) + { + setup.color_mode = MOUNTAIN_KEYBOARD_COLOR_MODE_SINGLE; + setup.mode.one_color.r = RGBGetRValue(current_mode.colors[0]); + setup.mode.one_color.g = RGBGetGValue(current_mode.colors[0]); + setup.mode.one_color.b = RGBGetBValue(current_mode.colors[0]); + } + } + } + else + { + setup.color_mode = MOUNTAIN_KEYBOARD_COLOR_MODE_RAINBOW; + } + + controller->SendColorBreathingCmd(setup); + } + break; + + case MOUNTAIN_KEYBOARD_MODE_MATRIX: + { + color_setup setup; + + setup.speed = matrix_speed_values[current_mode.speed]; + setup.brightness = current_mode.brightness; + setup.mode.two_colors.r1 = RGBGetRValue(current_mode.colors[0]); + setup.mode.two_colors.g1 = RGBGetGValue(current_mode.colors[0]); + setup.mode.two_colors.b1 = RGBGetBValue(current_mode.colors[0]); + setup.mode.two_colors.r2 = RGBGetRValue(current_mode.colors[1]); + setup.mode.two_colors.g2 = RGBGetGValue(current_mode.colors[1]); + setup.mode.two_colors.b2 = RGBGetBValue(current_mode.colors[1]); + + controller->SendColorMatrixCmd(setup); + } + break; + + case MOUNTAIN_KEYBOARD_MODE_REACTIVE: + { + color_setup setup; + + setup.speed = reactive_speed_values[current_mode.speed]; + setup.brightness = current_mode.brightness; + setup.mode.two_colors.r1 = RGBGetRValue(current_mode.colors[0]); + setup.mode.two_colors.g1 = RGBGetGValue(current_mode.colors[0]); + setup.mode.two_colors.b1 = RGBGetBValue(current_mode.colors[0]); + setup.mode.two_colors.r2 = RGBGetRValue(current_mode.colors[1]); + setup.mode.two_colors.g2 = RGBGetGValue(current_mode.colors[1]); + setup.mode.two_colors.b2 = RGBGetBValue(current_mode.colors[1]); + + controller->SendColorReactiveCmd(setup); + } + break; + + default: + break; + } +} + +void RGBController_MountainKeyboard::DeviceUpdateLEDs() +{ + mode current_mode = modes[active_mode]; + DeviceUpdate(current_mode); +} + +void RGBController_MountainKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MountainKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_MountainKeyboard::DeviceUpdateMode() +{ + mode current_mode = modes[active_mode]; + + if(prv_mode != current_mode.value) + { + controller->SelectMode(current_mode.value); + prv_mode = current_mode.value; + } + + DeviceUpdate(current_mode); +} + +unsigned char RGBController_MountainKeyboard::ConvertDirection(unsigned int direction, bool rotation) +{ + unsigned char ret; + switch(direction) + { + case MODE_DIRECTION_LEFT: + { + ret = rotation?MOUNTAIN_KEYBOARD_DIRECTION_ANTICLK:MOUNTAIN_KEYBOARD_DIRECTION_LEFT; + } + break; + + case MODE_DIRECTION_RIGHT: + { + ret = rotation?MOUNTAIN_KEYBOARD_DIRECTION_CLK:MOUNTAIN_KEYBOARD_DIRECTION_RIGHT; + } + break; + + case MODE_DIRECTION_UP: + { + ret = MOUNTAIN_KEYBOARD_DIRECTION_UP; + } + break; + + case MODE_DIRECTION_DOWN: + { + ret = MOUNTAIN_KEYBOARD_DIRECTION_DOWN; + } + break; + + default: + { + ret = MOUNTAIN_KEYBOARD_DIRECTION_LEFT; + } + break; + } + return ret; +} + +void RGBController_MountainKeyboard::DeviceSaveMode() +{ + controller->SaveData(modes[active_mode].value); + controller->SendWheelColorChange(wheel_color); +} diff --git a/Controllers/MountainKeyboardController/RGBController_MountainKeyboard.h b/Controllers/MountainKeyboardController/RGBController_MountainKeyboard.h new file mode 100644 index 0000000..ac7d70a --- /dev/null +++ b/Controllers/MountainKeyboardController/RGBController_MountainKeyboard.h @@ -0,0 +1,61 @@ +/*---------------------------------------------------------*\ +| RGBController_MountainKeyboard.h | +| | +| RGBController for Mountain keyboard | +| | +| Wojciech Lazarski Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "MountainKeyboardController.h" + +#define MOUNTAIN_KEYBOARD_BRIGHTNESS_MIN 0 +#define MOUNTAIN_KEYBOARD_BRIGHTNESS_MAX 100 + +#define MOUNTAIN_KEYBOARD_SPEED_MIN 0 +#define MOUNTAIN_KEYBOARD_SPEED_MAX 4 +#define MOUNTAIN_KEYBOARD_SPEED_DEFAULT 3 + +enum +{ + MOUNTAIN_KEYBOARD_MODE_DIRECT = MOUNTAIN_KEYBOARD_IDX_CUSTOM, + MOUNTAIN_KEYBOARD_MODE_STATIC = MOUNTAIN_KEYBOARD_IDX_STATIC, + MOUNTAIN_KEYBOARD_MODE_COLOR_WAVE = MOUNTAIN_KEYBOARD_IDX_COLOR_WAVE, + MOUNTAIN_KEYBOARD_MODE_TORNADO = MOUNTAIN_KEYBOARD_IDX_TORNADO, + MOUNTAIN_KEYBOARD_MODE_BREATHING = MOUNTAIN_KEYBOARD_IDX_BREATHING, + MOUNTAIN_KEYBOARD_MODE_REACTIVE = MOUNTAIN_KEYBOARD_IDX_REACTIVE, + MOUNTAIN_KEYBOARD_MODE_MATRIX = MOUNTAIN_KEYBOARD_IDX_MATRIX, + MOUNTAIN_KEYBOARD_MODE_OFF = MOUNTAIN_KEYBOARD_IDX_OFF +}; + +class RGBController_MountainKeyboard : public RGBController +{ +public: + RGBController_MountainKeyboard(MountainKeyboardController* controller_ptr); + ~RGBController_MountainKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + MountainKeyboardController* controller; + int prv_mode; + unsigned char wheel_color[3]; + + unsigned char ConvertDirection(unsigned int direction, bool rotation); + void DeviceUpdate(const mode& current_mode); +}; diff --git a/Controllers/N5312AController/N5312AController.cpp b/Controllers/N5312AController/N5312AController.cpp new file mode 100644 index 0000000..5b4a41a --- /dev/null +++ b/Controllers/N5312AController/N5312AController.cpp @@ -0,0 +1,104 @@ +/*---------------------------------------------------------*\ +| N5312AController.cpp | +| | +| Driver for N5312A | +| | +| Morgan Guimard (morg) 02 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "N5312AController.h" +#include "StringUtils.h" + +N5312AController::N5312AController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + + SendInit(); +} + +N5312AController::~N5312AController() +{ + hid_close(dev); +} + +std::string N5312AController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string N5312AController::GetNameString() +{ + return(name); +} + +std::string N5312AController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void N5312AController::SendInit() +{ + unsigned char usb_buf[N5312A_PACKET_DATA_LENGTH]; + + memset(usb_buf, 0x00, N5312A_PACKET_DATA_LENGTH); + + usb_buf[0x00] = N5312A_REPORT_ID; + usb_buf[0x01] = N5312A_INIT_BYTE; + + hid_send_feature_report(dev, usb_buf, N5312A_PACKET_DATA_LENGTH); +} + +void N5312AController::SetColor(RGBColor color) +{ + unsigned char usb_buf[N5312A_PACKET_DATA_LENGTH]; + + memset(usb_buf, 0x00, N5312A_PACKET_DATA_LENGTH); + + usb_buf[0x00] = N5312A_REPORT_ID; + usb_buf[0x01] = N5312A_SET_COLOR_BYTE; + usb_buf[0x02] = 1; + usb_buf[0x03] = RGBGetRValue(color); + usb_buf[0x04] = RGBGetGValue(color); + usb_buf[0x05] = RGBGetBValue(color); + + hid_send_feature_report(dev, usb_buf, N5312A_PACKET_DATA_LENGTH); + +} + +void N5312AController::SetMode(RGBColor color, unsigned char mode_value, unsigned char brightness, unsigned char speed) +{ + SetColor(color); + + unsigned char usb_buf[N5312A_PACKET_DATA_LENGTH]; + + memset(usb_buf, 0x00, N5312A_PACKET_DATA_LENGTH); + + usb_buf[0x00] = N5312A_REPORT_ID; + usb_buf[0x01] = N5312A_SET_MODE_BYTE; + + usb_buf[0x02] = mode_value; + + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x01; + usb_buf[0x05] = 0x01; + + usb_buf[0x06] = speed; + usb_buf[0x07] = brightness; + + hid_send_feature_report(dev, usb_buf, N5312A_PACKET_DATA_LENGTH); +} diff --git a/Controllers/N5312AController/N5312AController.h b/Controllers/N5312AController/N5312AController.h new file mode 100644 index 0000000..bcc3f56 --- /dev/null +++ b/Controllers/N5312AController/N5312AController.h @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| N5312AController.h | +| | +| Driver for N5312A | +| | +| Morgan Guimard (morg) 02 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define N5312A_REPORT_ID 0x07 +#define N5312A_PACKET_DATA_LENGTH 8 +#define N5312A_NUMBER_OF_LEDS 1 +#define N5312A_INIT_BYTE 0xA0 +#define N5312A_SET_MODE_BYTE 0x0A +#define N5312A_SET_COLOR_BYTE 0x0B + +enum +{ + N5312A_BREATHING_MODE_VALUE = 0x00, + N5312A_SINGLE_BREATH_MODE_VALUE = 0x01, + N5312A_DIRECT_MODE_VALUE = 0x02, + N5312A_OFF_MODE_VALUE = 0x03 +}; + +enum +{ + N5312A_BRIGHTNESS_MIN = 0x0A, + N5312A_BRIGHTNESS_MAX = 0x64, + N5312A_SPEED_MIN = 0x01, + N5312A_SPEED_MAX = 0x0A +}; + +class N5312AController +{ +public: + N5312AController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~N5312AController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetColor(RGBColor color); + void SetMode(RGBColor color, unsigned char mode_value, unsigned char brightness, unsigned char speed); + +private: + hid_device* dev; + + std::string location; + std::string name; + std::string version; + + void SendInit(); +}; diff --git a/Controllers/N5312AController/N5312AControllerDetect.cpp b/Controllers/N5312AController/N5312AControllerDetect.cpp new file mode 100644 index 0000000..9c2b811 --- /dev/null +++ b/Controllers/N5312AController/N5312AControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| N5312AControllerDetect.cpp | +| | +| Detector for N5312A | +| | +| Morgan Guimard (morg) 02 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "N5312AController.h" +#include "RGBController_N5312A.h" + +/*---------------------------------------------------------*\ +| N5312A vendor ID | +\*---------------------------------------------------------*/ +#define N5312A_VID 0x4E53 + +/*---------------------------------------------------------*\ +| Product ID | +\*---------------------------------------------------------*/ +#define N5312A_PID 0x5406 + +void DetectN5312AControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + N5312AController* controller = new N5312AController(dev, *info, name); + RGBController_N5312A* rgb_controller = new RGBController_N5312A(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("N5312A USB Optical Mouse", DetectN5312AControllers, N5312A_VID, N5312A_PID, 1, 0xFF01, 0x01); diff --git a/Controllers/N5312AController/RGBController_N5312A.cpp b/Controllers/N5312AController/RGBController_N5312A.cpp new file mode 100644 index 0000000..b806543 --- /dev/null +++ b/Controllers/N5312AController/RGBController_N5312A.cpp @@ -0,0 +1,140 @@ +/*---------------------------------------------------------*\ +| RGBController_N5312A.cpp | +| | +| RGBController for N5312A | +| | +| Morgan Guimard (morg) 02 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_N5312A.h" + +/**------------------------------------------------------------------*\ + @name N5312A mouse + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectN5312AControllers + @comment This controller should work with all mouse with this chip. + Identified devices that work with this controller: ANT Esports KM540 Mouse, + Marvo M115 +\*-------------------------------------------------------------------*/ + +RGBController_N5312A::RGBController_N5312A(N5312AController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Unknown"; + type = DEVICE_TYPE_MOUSE; + description = "N5312A Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Direct"; + Static.value = N5312A_DIRECT_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = N5312A_BRIGHTNESS_MIN; + Static.brightness_max = N5312A_BRIGHTNESS_MAX; + Static.brightness = N5312A_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = N5312A_BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = N5312A_BRIGHTNESS_MIN; + Breathing.brightness_max = N5312A_BRIGHTNESS_MAX; + Breathing.brightness = N5312A_BRIGHTNESS_MAX; + Breathing.speed = N5312A_SPEED_MIN; + Breathing.speed_min = N5312A_SPEED_MIN; + Breathing.speed_max = N5312A_SPEED_MAX; + modes.push_back(Breathing); + + mode SingleBreath; + SingleBreath.name = "Single Breath"; + SingleBreath.value = N5312A_SINGLE_BREATH_MODE_VALUE; + SingleBreath.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + SingleBreath.color_mode = MODE_COLORS_PER_LED; + SingleBreath.brightness_min = N5312A_BRIGHTNESS_MIN; + SingleBreath.brightness_max = N5312A_BRIGHTNESS_MAX; + SingleBreath.brightness = N5312A_BRIGHTNESS_MAX; + SingleBreath.speed = N5312A_SPEED_MIN; + SingleBreath.speed_min = N5312A_SPEED_MIN; + SingleBreath.speed_max = N5312A_SPEED_MAX; + modes.push_back(SingleBreath); + + mode Off; + Off.name = "Off"; + Off.value = N5312A_OFF_MODE_VALUE; + Off.flags = 0x00; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_N5312A::~RGBController_N5312A() +{ + delete controller; +} + +void RGBController_N5312A::SetupZones() +{ + zone new_zone; + + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = N5312A_NUMBER_OF_LEDS; + new_zone.leds_max = N5312A_NUMBER_OF_LEDS; + new_zone.leds_count = N5312A_NUMBER_OF_LEDS; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(new_zone.leds_count); + + for(unsigned int i = 0; i < N5312A_NUMBER_OF_LEDS; i++) + { + leds[i].name = "LED " + std::to_string(i + 1); + } + + SetupColors(); +} + +void RGBController_N5312A::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_N5312A::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_N5312A::UpdateZoneLEDs(int /*zone*/) +{ + controller->SetColor(colors[0]); +} + +void RGBController_N5312A::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_N5312A::DeviceUpdateMode() +{ + const RGBColor& color = modes[active_mode].value == N5312A_OFF_MODE_VALUE ? 0 : colors[0]; + controller->SetMode(color, modes[active_mode].value, modes[active_mode].brightness, modes[active_mode].speed); +} diff --git a/Controllers/N5312AController/RGBController_N5312A.h b/Controllers/N5312AController/RGBController_N5312A.h new file mode 100644 index 0000000..534167c --- /dev/null +++ b/Controllers/N5312AController/RGBController_N5312A.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_N5312A.h | +| | +| RGBController for N5312A | +| | +| Morgan Guimard (morg) 02 Apr 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "N5312AController.h" + +class RGBController_N5312A : public RGBController +{ +public: + RGBController_N5312A(N5312AController* controller_ptr); + ~RGBController_N5312A(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + N5312AController* controller; +}; diff --git a/Controllers/NVIDIAIlluminationController/NVIDIAIlluminationControllerDetect_Windows_Linux.cpp b/Controllers/NVIDIAIlluminationController/NVIDIAIlluminationControllerDetect_Windows_Linux.cpp new file mode 100644 index 0000000..150844a --- /dev/null +++ b/Controllers/NVIDIAIlluminationController/NVIDIAIlluminationControllerDetect_Windows_Linux.cpp @@ -0,0 +1,132 @@ +/*---------------------------------------------------------*\ +| NVIDIAIlluminationControllerDetect_Windows_Linux.cpp | +| | +| Detector for NVIDIA Illumination GPU | +| | +| Carter Miller (GingerRunner) 04 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "LogManager.h" +#include "RGBController_NVIDIAIllumination_Windows_Linux.h" +#include "pci_ids.h" + +enum +{ + NVIDIA_ILLUMINATION_V1 +}; + +typedef struct +{ + int pci_vendor; + int pci_device; + int pci_subsystem_vendor; + int pci_subsystem_device; + int gpu_rgb_version; + bool treats_rgbw_as_rgb; + const char * name; +} nv_gpu_pci_device; + + +#define GPU_NUM_DEVICES (sizeof(device_list) / sizeof(device_list[ 0 ])) + +/*-----------------------------------------------------------------------------------------------------*\ +| Certain devices seem to ignore the white value entirely, despite the zone being reported back by the | +| API as RGBW, so this boolean is passed at detection time via constructor inform the controller logic. | +\*-----------------------------------------------------------------------------------------------------*/ +#define TREATS_RGBW_AS_RGB true +#define TREATS_RGBW_AS_RGBW false + +static const nv_gpu_pci_device device_list[] = +{ + {NVIDIA_VEN, NVIDIA_RTX2060_TU104_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2060_TU104_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "Palit GeForce RTX 2060" }, + {NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2060_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2060 FE" }, + {NVIDIA_VEN, NVIDIA_RTX2060S_OC_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2060S_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2060 SUPER FE" }, + {NVIDIA_VEN, NVIDIA_RTX2070_OC_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2070_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2070 FE" }, + {NVIDIA_VEN, NVIDIA_RTX2070S_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2070_FE_SUPER_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2070 SUPER FE" }, + {NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2080_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2080 FE" }, + {NVIDIA_VEN, NVIDIA_RTX2080S_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2080S_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2080 SUPER FE" }, + {NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2080TI_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 2080 Ti FE" }, + {NVIDIA_VEN, NVIDIA_TITANRTX_DEV, NVIDIA_SUB_VEN, NVIDIA_TITANRTX_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA TITAN RTX" }, + {NVIDIA_VEN, NVIDIA_RTX3050_DEV, NVIDIA_SUB_VEN, GAINWARD_RTX3050_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "Gainward GeForce RTX 3050 LHR" }, + {NVIDIA_VEN, NVIDIA_RTX3060_8G_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3060_8G_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "Gainward GeForce RTX 3060 Pegasus" }, + {NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3060_LHR_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "Palit GeForce RTX 3060 LHR" }, + {NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, PNY_SUB_VEN, PNY_RTX_3060_XLR8_REVEL_EPIC_X_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "PNY GeForce RTX 3060 XLR8 REVEL EPIC-X" }, + {NVIDIA_VEN, NVIDIA_RTX3060_GA104_DEV, PNY_SUB_VEN, PNY_RTX_3060_XLR8_REVEL_EPIC_X_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "PNY GeForce RTX 3060 XLR8 REVEL EPIC-X" }, + {NVIDIA_VEN, NVIDIA_RTX3060_GA104_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3060_GA104_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "Palit GeForce RTX 3060 LHR" }, + {NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, PNY_SUB_VEN, PNY_RTX_3060TI_XLR8_REVEL_EPIC_X_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "PNY GeForce RTX 3060 Ti XLR8 REVEL EPIC-X" }, + {NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, NVIDIA_SUB_VEN, PNY_RTX_3060TI_XLR8_REVEL_EPIC_X_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "PNY GeForce RTX 3060 Ti XLR8 REVEL EPIC-X" }, + {NVIDIA_VEN, NVIDIA_RTX3060TI_V1_LHR_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3060TI_V1_LHR_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "NVIDIA GeForce RTX 3060 Ti V1 LHR" }, + {NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3060TI_LHR_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "NVIDIA GeForce RTX 3060 Ti LHR" }, + {NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, PNY_SUB_VEN, PNY_RTX_3070TI_XLR8_UPRISING_EPIC_X_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "PNY GeForce RTX 3070 Ti XLR8 Uprising EPIC-X"}, + {NVIDIA_VEN, NVIDIA_RTX3080_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3080_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 3080 FE" }, + {NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3080TI_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 3080 Ti FE" }, + {NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, NVIDIA_SUB_VEN, MANLI_RTX3080TI_GALLARDO_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "MANLI GeForce RTX 3080 Ti GALLARDO" }, + {NVIDIA_VEN, NVIDIA_RTX3090_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3090_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 3090 FE" }, + {NVIDIA_VEN, NVIDIA_RTX3090TI_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX3090TI_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 3090 Ti FE" }, + {NVIDIA_VEN, NVIDIA_RTX4060_DEV, PALIT_SUB_VEN, PALIT_RTX4060_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "Palit GeForce RTX 4060 Dual" }, + {NVIDIA_VEN, NVIDIA_RTX4070_DEV, PALIT_SUB_VEN, PALIT_RTX4070_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "Palit GeForce RTX 4070" }, + {NVIDIA_VEN, NVIDIA_RTX4070S_DEV, PALIT_SUB_VEN, PALIT_RTX4070S_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGB, "Palit GeForce RTX 4070 SUPER Dual" }, + {NVIDIA_VEN, NVIDIA_RTX4070_DEV, GAINWARD_SUB_VEN, GAINWARD_RTX_4070_GHOST_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "Gainward GeForce RTX 4070 Ghost" }, + {NVIDIA_VEN, NVIDIA_RTX4080_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX4080_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 4080 FE" }, + {NVIDIA_VEN, NVIDIA_RTX4080_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX4080_FE_SUB_DEV2, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 4080 FE" }, + {NVIDIA_VEN, NVIDIA_RTX4080S_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX4080S_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 4080 SUPER FE" }, + {NVIDIA_VEN, NVIDIA_RTX4090_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX4090_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 4090 FE" }, + {NVIDIA_VEN, NVIDIA_RTX4090_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX4090_FE_SUB_DEV2, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 4090 FE" }, + {NVIDIA_VEN, NVIDIA_RTX5080_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX5080_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 5080 FE" }, + {NVIDIA_VEN, NVIDIA_RTX5090_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX5090_FE_SUB_DEV, NVIDIA_ILLUMINATION_V1, TREATS_RGBW_AS_RGBW, "NVIDIA GeForce RTX 5090 FE" }, +}; + +void DetectNVIDIAIllumGPUs() +{ + static NV_PHYSICAL_GPU_HANDLE gpu_handles[64]; + static NV_S32 gpu_count = 0; + NV_U32 device_id; + NV_U32 ext_device_id; + NV_STATUS res; + NV_U32 revision_id; + NV_U32 sub_system_id; + + NvAPI_Initialize(); + + NvAPI_EnumPhysicalGPUs(gpu_handles, &gpu_count); + + for(NV_S32 gpu_idx = 0; gpu_idx < gpu_count; gpu_idx++) + { + res = NvAPI_GPU_GetPCIIdentifiers(gpu_handles[gpu_idx], &device_id, &sub_system_id, &revision_id, &ext_device_id); + if (res == 0) + { + uint16_t pci_device = device_id >> 16; + uint16_t pci_vendor = device_id & 0xffff; + uint16_t pci_subsystem_device = sub_system_id >> 16; + uint16_t pci_subsystem_vendor = sub_system_id & 0xffff; + for(unsigned int dev_idx = 0; dev_idx < GPU_NUM_DEVICES; dev_idx++) + { + if(pci_vendor == device_list[dev_idx].pci_vendor && + pci_device == device_list[dev_idx].pci_device && + pci_subsystem_vendor == device_list[dev_idx].pci_subsystem_vendor && + pci_subsystem_device == device_list[dev_idx].pci_subsystem_device) + { + LOG_DEBUG("[%s] Nvidia NvAPI Illumination GPU found", device_list[dev_idx].name); + switch(device_list[dev_idx].gpu_rgb_version) + { + case NVIDIA_ILLUMINATION_V1: + { + nvapi_accessor* new_nvapi = new nvapi_accessor(gpu_handles[gpu_idx]); + NVIDIAIlluminationV1Controller* controller = new NVIDIAIlluminationV1Controller(new_nvapi, device_list[dev_idx].treats_rgbw_as_rgb, device_list[dev_idx].name); + RGBController_NVIDIAIlluminationV1* rgb_controller = new RGBController_NVIDIAIlluminationV1(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + } + } + } + } + } +} + +REGISTER_DETECTOR("Nvidia NvAPI Illumination", DetectNVIDIAIllumGPUs); diff --git a/Controllers/NVIDIAIlluminationController/NVIDIAIlluminationV1Controller_Windows_Linux.cpp b/Controllers/NVIDIAIlluminationController/NVIDIAIlluminationV1Controller_Windows_Linux.cpp new file mode 100644 index 0000000..6b924e6 --- /dev/null +++ b/Controllers/NVIDIAIlluminationController/NVIDIAIlluminationV1Controller_Windows_Linux.cpp @@ -0,0 +1,190 @@ +/*---------------------------------------------------------*\ +| NVIDIAIlluminationV1Controller_Windows_Linux.cpp | +| | +| Driver for NVIDIA Illumination V1 GPU | +| | +| Carter Miller (GingerRunner) 05 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "NVIDIAIlluminationV1Controller_Windows_Linux.h" + +NVIDIAIlluminationV1Controller::NVIDIAIlluminationV1Controller(nvapi_accessor* nvapi_ptr, bool treats_rgbw_as_rgb, std::string dev_name) +{ + nvapi = nvapi_ptr; + _treats_rgbw_as_rgb = treats_rgbw_as_rgb; + name = dev_name; +} + +NVIDIAIlluminationV1Controller::~NVIDIAIlluminationV1Controller() +{ + +} + +std::string NVIDIAIlluminationV1Controller::GetName() +{ + return(name); +} + +void NVIDIAIlluminationV1Controller::checkNVAPIreturn() +{ + if (nvapi_return != NVAPI_OK) + { + LOG_DEBUG("NVAPI return code not NVAPI_OK: %d", nvapi_return); + } +} + +void NVIDIAIlluminationV1Controller::getControl() +{ + /*------------------------------------------------------------------------------------------------*\ + | This was previously memset(&zone_params, 0, sizeof(zone_params)) | + | But this kind of zero initialization is more up-to-date and safer in the event of non-primitive | + | data types | + \*------------------------------------------------------------------------------------------------*/ + zone_params = {}; + /*---------------------------------------------------------------------------------------------------*\ + | Hardcoded value found via sniffing, this may be different for other cards, once that is | + | found, may be best to simply if/else this based on the card detected or map it out in the detector | + | and then pass via constructor to here. | + \*---------------------------------------------------------------------------------------------------*/ + zone_params.version = 72012; + zone_params.bDefault = 0; + /*---------------------------------------------------------------------------------------------------*\ + | As far as I can tell, this pre-populates the zone type value, as well as the number of zones | + | able to be controlled, and their existing settings, very useful for extending this controller. | + \*---------------------------------------------------------------------------------------------------*/ + nvapi_return = nvapi->nvapi_zone_control(NVAPI_ZONE_GET_CONTROL, &zone_params); + checkNVAPIreturn(); +} + +void NVIDIAIlluminationV1Controller::setControl() +{ + nvapi_return = nvapi->nvapi_zone_control(NVAPI_ZONE_SET_CONTROL, &zone_params); + checkNVAPIreturn(); +} + +/*----------------------------------------------------------------------------------------------------*\ +| This function exists to check if RGB colors are all set to zero, and if so, to take the brightness | +| down to zero. This was done to comply with functionality in OpenRGB such as "Lights Off" which | +| sends RGB values of all zeroes, but doesn't seem to send a brightness of zero at this time (6/2022). | +\*----------------------------------------------------------------------------------------------------*/ +bool NVIDIAIlluminationV1Controller::allZero(std::array colors) +{ + return colors == all_zeros; +} + +void NVIDIAIlluminationV1Controller::setZoneRGBW(uint8_t zone, uint8_t red, uint8_t green, uint8_t blue, uint8_t white, uint8_t brightness) +{ + zone_params.zones[zone].data.rgbw.data.manualRGBW.rgbwParams.colorR = red; + zone_params.zones[zone].data.rgbw.data.manualRGBW.rgbwParams.colorG = green; + zone_params.zones[zone].data.rgbw.data.manualRGBW.rgbwParams.colorB = blue; + zone_params.zones[zone].data.rgbw.data.manualRGBW.rgbwParams.colorW = white; + zone_params.zones[zone].data.rgbw.data.manualRGBW.rgbwParams.brightnessPct = brightness; +} + +void NVIDIAIlluminationV1Controller::setZoneRGB(uint8_t zone, uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness) +{ + zone_params.zones[zone].data.rgb.data.manualRGB.rgbParams.colorR = red; + zone_params.zones[zone].data.rgb.data.manualRGB.rgbParams.colorG = green; + zone_params.zones[zone].data.rgb.data.manualRGB.rgbParams.colorB = blue; + zone_params.zones[zone].data.rgb.data.manualRGB.rgbParams.brightnessPct = brightness; +} + + +void NVIDIAIlluminationV1Controller::setZone(uint8_t zone, uint8_t mode, NVIDIAIllumination_Config zone_config) +{ + getControl(); + uint8_t red = RGBGetRValue(zone_config.colors[0]); + uint8_t green = RGBGetGValue(zone_config.colors[0]); + uint8_t blue = RGBGetBValue(zone_config.colors[0]); + uint8_t white = 0; + switch(mode) + { + case NVIDIA_ILLUMINATION_OFF: + zone_params.zones[zone].ctrlMode = NV_GPU_CLIENT_ILLUM_CTRL_MODE_MANUAL_RGB; + if(zone_params.zones[zone].type == NV_GPU_CLIENT_ILLUM_ZONE_TYPE_RGB) + { + setZoneRGB(zone, 0, 0, 0, 0); + } + else if(zone_params.zones[zone].type == NV_GPU_CLIENT_ILLUM_ZONE_TYPE_RGBW) + { + setZoneRGBW(zone, 0, 0, 0, 0, 0); + } + else if(zone_params.zones[zone].type == NV_GPU_CLIENT_ILLUM_ZONE_TYPE_SINGLE_COLOR) + { + zone_params.zones[zone].data.singleColor.data.manualSingleColor.singleColorParams.brightnessPct = 0; + } + else if(zone_params.zones[zone].type == NV_GPU_CLIENT_ILLUM_ZONE_TYPE_COLOR_FIXED) + { + zone_params.zones[zone].data.colorFixed.data.manualColorFixed.colorFixedParams.brightnessPct = 0; + } + break; + case NVIDIA_ILLUMINATION_DIRECT: + zone_params.zones[zone].ctrlMode = NV_GPU_CLIENT_ILLUM_CTRL_MODE_MANUAL_RGB; + if(zone_params.zones[zone].type == NV_GPU_CLIENT_ILLUM_ZONE_TYPE_RGB) + { + setZoneRGB(zone, red, green, blue, zone_config.brightness); + } + else if(zone_params.zones[zone].type == NV_GPU_CLIENT_ILLUM_ZONE_TYPE_RGBW) + { + /*----------------------------------------------------------------------------------------------------*\ + | Certain devices seem to ignore the white value entirely, despite the zone being reported back by the | + | API as RGBW, as such, this if statement was added to conduct a different course of action based | + | on definitions placed in the controller page | + \*----------------------------------------------------------------------------------------------------*/ + if(!_treats_rgbw_as_rgb) + { + uint8_t min_rgb_value = 0xFF; + uint8_t max_rgb_value = 0; + min_rgb_value = ((red < 0xFF) ? red : min_rgb_value); + min_rgb_value = ((green < min_rgb_value) ? green : min_rgb_value); + min_rgb_value = ((blue < min_rgb_value) ? blue : min_rgb_value); + max_rgb_value = ((red > 0) ? red : max_rgb_value); + max_rgb_value = ((green > max_rgb_value) ? green : max_rgb_value); + max_rgb_value = ((blue > max_rgb_value) ? blue : max_rgb_value); + /*---------------------------------------------------------------------------------------------------*\ + | If difference between the highest and lowest RGB values is 10 or lower, set the white value only, | + | zero out the rest, this logic was found via tedious examination | + \*---------------------------------------------------------------------------------------------------*/ + if (max_rgb_value - min_rgb_value <= 10) + { + red = 0; + green = 0; + blue = 0; + white = (max_rgb_value + min_rgb_value)/2; + } + } + setZoneRGBW(zone, red, green, blue, white, zone_config.brightness); + } + else if(zone_params.zones[zone].type == NV_GPU_CLIENT_ILLUM_ZONE_TYPE_SINGLE_COLOR) + { + zone_params.zones[zone].data.singleColor.data.manualSingleColor.singleColorParams.brightnessPct = allZero({red, green, blue, white}) ? 0 : zone_config.brightness; + } + else if(zone_params.zones[zone].type == NV_GPU_CLIENT_ILLUM_ZONE_TYPE_COLOR_FIXED) + { + zone_params.zones[zone].data.colorFixed.data.manualColorFixed.colorFixedParams.brightnessPct = allZero({red, green, blue, white}) ? 0 : zone_config.brightness; + } + break; + } + setControl(); +} + +int NVIDIAIlluminationV1Controller::getZoneColor(uint8_t zone_index) +{ + return ToRGBColor(zone_params.zones[zone_index].data.rgb.data.manualRGB.rgbParams.colorR, + zone_params.zones[zone_index].data.rgb.data.manualRGB.rgbParams.colorG, + zone_params.zones[zone_index].data.rgb.data.manualRGB.rgbParams.colorB); +} + +std::vector NVIDIAIlluminationV1Controller::getInfo() +{ + std::vector zone_types; + getControl(); + for(unsigned int i = 0; i < zone_params.numIllumZonesControl; i++) + { + zone_types.push_back(zone_params.zones[i].type); + } + return zone_types; +} diff --git a/Controllers/NVIDIAIlluminationController/NVIDIAIlluminationV1Controller_Windows_Linux.h b/Controllers/NVIDIAIlluminationController/NVIDIAIlluminationV1Controller_Windows_Linux.h new file mode 100644 index 0000000..f35d0e8 --- /dev/null +++ b/Controllers/NVIDIAIlluminationController/NVIDIAIlluminationV1Controller_Windows_Linux.h @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| NVIDIAIlluminationV1Controller_Windows_Linux.h | +| | +| Driver for NVIDIA Illumination V1 GPU | +| | +| Carter Miller (GingerRunner) 04 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "nvapi_accessor_Windows_Linux.h" +#include "RGBController.h" +#include "LogManager.h" + +#define NVIDIA_ILLUMINATION_V1_CONTROLLER_NAME "NVIDIA_ILLUMINATION_V1" +#define NVAPI_OK 0 + +struct NVIDIAIllumination_Config +{ + uint8_t brightness; + RGBColor colors[7]; +}; + +enum +{ + NVIDIA_ILLUMINATION_OFF = 0, + NVIDIA_ILLUMINATION_DIRECT = 1 +}; + +class NVIDIAIlluminationV1Controller +{ +public: + NVIDIAIlluminationV1Controller(nvapi_accessor* nvapi_ptr, bool treats_rgbw_as_rgb, std::string dev_name); + ~NVIDIAIlluminationV1Controller(); + + std::string GetName(); + + void getControl(); + void setControl(); + bool allZero(std::array colors); + void setZoneRGBW(uint8_t zone, uint8_t red, uint8_t green, uint8_t blue, uint8_t white, uint8_t brightness); + void setZoneRGB(uint8_t zone, uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness); + void setZone(uint8_t zone, uint8_t mode, NVIDIAIllumination_Config zone_config); + int getZoneColor(uint8_t zone_index); + std::vector getInfo(); + +private: + nvapi_accessor* nvapi; + bool _treats_rgbw_as_rgb; + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS zone_params; + NV_STATUS nvapi_return = 0; + const std::array all_zeros = {0, 0, 0, 0}; + std::string name; + + void checkNVAPIreturn(); +}; diff --git a/Controllers/NVIDIAIlluminationController/RGBController_NVIDIAIllumination_Windows_Linux.cpp b/Controllers/NVIDIAIlluminationController/RGBController_NVIDIAIllumination_Windows_Linux.cpp new file mode 100644 index 0000000..f810ee3 --- /dev/null +++ b/Controllers/NVIDIAIlluminationController/RGBController_NVIDIAIllumination_Windows_Linux.cpp @@ -0,0 +1,168 @@ +/*---------------------------------------------------------*\ +| RGBController_NVIDIAIllumination_Windows_Linux.cpp | +| | +| RGBController for NVIDIA Illumination GPU | +| | +| Carter Miller (GingerRunner) 04 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_NVIDIAIllumination_Windows_Linux.h" + +/**------------------------------------------------------------------*\ + @name NVIDIA Illumination + @category GPU + @type PCI + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectNVIDIAIllumGPUs + @comment Tested on various 30 series GPUs and a Founders Edition 2070 Super + If you want to see if your card should also use this controller, download the DLLs from the release of [this](https://gitlab.com/OpenRGBDevelopers/NvAPISpy). + + Perform the global replacement technique, which is specified in the README of NvAPI spy. Once this is complete, use an RGB program of your choice and make some basic + lighting changes. + + Check the C:\NvAPISpy\ folder and see if the logs created are filled with calls like this: + ``` + NvAPI_GPU_ClientIllumZonesGetControl: version: 72012 numIllumZones: 2 bDefault: 0 rsvdField: 0 + ZoneIdx: 0 ---------------------------------------- + **ZoneType: RGBW ControlMode: MANUAL + **DATA_RGBW:: Red: 255 Green: 0 Blue: 0 White: 0 Brightness%: 36 + ZoneIdx: 1 ---------------------------------------- + **ZoneType: SINGLE_COLOR ControlMode: MANUAL + **DATA_SINGLE_COLOR:: Brightness% 100 + NvAPI_GPU_ClientIllumZonesSetControl: version: 72012 numIllumZones: 2 bDefault: 0 rsvdField: 0 + ZoneIdx: 0 ---------------------------------------- + **ZoneType: RGBW ControlMode: MANUAL + **DATA_RGBW:: Red: 255 Green: 0 Blue: 0 White: 0 Brightness%: 36 + ZoneIdx: 1 ---------------------------------------- + **ZoneType: SINGLE_COLOR ControlMode: MANUAL + **DATA_SINGLE_COLOR:: Brightness% 44 + ``` + If you see Get/Set Calls above for zone control, please create a [new device issue](https://gitlab.com/CalcProgrammer1/OpenRGB/-/issues/new?issuable_template=New%20Device#) + and attach the relevant details to request support for your device (try various modes in each color, especially white and shades around it, since some cards treat RGBW as + standard RGB). +\*-------------------------------------------------------------------*/ + +RGBController_NVIDIAIlluminationV1::RGBController_NVIDIAIlluminationV1(NVIDIAIlluminationV1Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "NVIDIA"; + description = "NVIDIA Illumination RGB GPU Device"; + type = DEVICE_TYPE_GPU; + + mode Off; + Off.name = "Off"; + Off.value = NVIDIA_ILLUMINATION_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Direct"; + Static.value = NVIDIA_ILLUMINATION_DIRECT; + Static.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness_min = 0; + Static.brightness = 100; + Static.brightness_max = 100; + modes.push_back(Static); + + SetupZones(); + + for(unsigned int i = 0; i < zones.size(); i++) + { + zones[i].colors[0] = controller->getZoneColor(i); + } +} + +RGBController_NVIDIAIlluminationV1::~RGBController_NVIDIAIlluminationV1() +{ + delete controller; +} + +void RGBController_NVIDIAIlluminationV1::UpdateSingleLED(int) +{ + DeviceUpdateLEDs(); +} + +void RGBController_NVIDIAIlluminationV1::SetupZones() +{ + /*--------------------------------------------------------------------------*\ + | Use the NvAPI to gather existing zones on the card and their capabilities, | + | populate available zones accordingly. | + \*--------------------------------------------------------------------------*/ + zoneTypes = controller->getInfo(); + nvidia_illum_zone_names[NV_GPU_CLIENT_ILLUM_ZONE_TYPE_RGB] = "RGB"; + nvidia_illum_zone_names[NV_GPU_CLIENT_ILLUM_ZONE_TYPE_RGBW] = "RGBW"; + nvidia_illum_zone_names[NV_GPU_CLIENT_ILLUM_ZONE_TYPE_COLOR_FIXED] = "FIXED COLOR"; + nvidia_illum_zone_names[NV_GPU_CLIENT_ILLUM_ZONE_TYPE_SINGLE_COLOR] = "SINGLE COLOR"; + for(uint8_t zone_idx = 0; zone_idx < zoneTypes.size(); zone_idx++) + { + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = std::to_string(zone_idx) + " - " + (std::string)nvidia_illum_zone_names[zoneTypes[zone_idx]]; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + new_led->name = "Entire Zone"; + leds.push_back(*new_led); + zones.push_back(*new_zone); + zoneIndexMap.push_back(zone_idx); + } + SetupColors(); + +} + +void RGBController_NVIDIAIlluminationV1::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_NVIDIAIlluminationV1::DeviceUpdateLEDs() +{ + NVIDIAIllumination_Config nv_zone_config; + for(uint8_t zone_idx = 0; zone_idx < zoneIndexMap.size(); zone_idx++) + { + nv_zone_config.colors[0] = colors[zone_idx]; + nv_zone_config.brightness = modes[active_mode].brightness; + controller->setZone(zone_idx, modes[active_mode].value, nv_zone_config); + } +} + +void RGBController_NVIDIAIlluminationV1::UpdateZoneLEDs(int zone) +{ + NVIDIAIllumination_Config nv_zone_config; + nv_zone_config.colors[0] = colors[zone]; + nv_zone_config.brightness = modes[active_mode].brightness; + controller->setZone(zone, modes[active_mode].value, nv_zone_config); +} + +uint8_t RGBController_NVIDIAIlluminationV1::getModeIndex(uint8_t mode_value) +{ + for(uint8_t mode_index = 0; mode_index < modes.size(); mode_index++) + { + if(modes[mode_index].value == mode_value) + { + return mode_index; + } + } + return 0; +} + +void RGBController_NVIDIAIlluminationV1::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/NVIDIAIlluminationController/RGBController_NVIDIAIllumination_Windows_Linux.h b/Controllers/NVIDIAIlluminationController/RGBController_NVIDIAIllumination_Windows_Linux.h new file mode 100644 index 0000000..8985124 --- /dev/null +++ b/Controllers/NVIDIAIlluminationController/RGBController_NVIDIAIllumination_Windows_Linux.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_NVIDIAIllumination_Windows_Linux.h | +| | +| RGBController for NVIDIA Illumination GPU | +| | +| Carter Miller (GingerRunner) 04 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "NVIDIAIlluminationV1Controller_Windows_Linux.h" + +#define NVIDIA_FOUNDERS_V1_CONTROLLER_NAME "NVIDIA_FOUNDERS_V1" + +class RGBController_NVIDIAIlluminationV1 : public RGBController +{ + public: + RGBController_NVIDIAIlluminationV1(NVIDIAIlluminationV1Controller* nvidia_founders_ptr); + ~RGBController_NVIDIAIlluminationV1(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + private: + uint8_t getModeIndex(uint8_t mode_value); + NVIDIAIlluminationV1Controller* controller; + std::vector zoneIndexMap; + std::vector zoneTypes; + std::map nvidia_illum_zone_names; +}; diff --git a/Controllers/NVIDIAIlluminationController/nvapi_accessor_Windows_Linux.cpp b/Controllers/NVIDIAIlluminationController/nvapi_accessor_Windows_Linux.cpp new file mode 100644 index 0000000..0570a52 --- /dev/null +++ b/Controllers/NVIDIAIlluminationController/nvapi_accessor_Windows_Linux.cpp @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| nvapi_accessor_Windows_Linux.cpp | +| | +| NVAPI accessor for NVIDIA NVAPI illumination API | +| | +| Carter Miller (GingerRunner) 20 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "nvapi_accessor_Windows_Linux.h" + +nvapi_accessor::nvapi_accessor(NV_PHYSICAL_GPU_HANDLE handle) +{ + this->handle = handle; +} + +NV_STATUS nvapi_accessor::nvapi_zone_control(char nvapi_call, NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* zone_control_struct) +{ + NV_STATUS ret = -1; + + if(nvapi_call == NVAPI_ZONE_SET_CONTROL) + { + ret = NvAPI_GPU_ClientIllumZonesSetControl(handle, zone_control_struct); + } + else if(nvapi_call == NVAPI_ZONE_GET_CONTROL) + { + ret = NvAPI_GPU_ClientIllumZonesGetControl(handle, zone_control_struct); + } + /*----------------------------------------------------------------------------------*\ + | Based off experimentation, the NvAPI doesn't like to be spammed calls | + | or else it just ignores them, this applies to both get/set control (GingerRunner) | + \*----------------------------------------------------------------------------------*/ + std::this_thread::sleep_for(std::chrono::milliseconds(NVAPI_CONTROL_BUFFER_TIME_MS)); + + return(ret); +} diff --git a/Controllers/NVIDIAIlluminationController/nvapi_accessor_Windows_Linux.h b/Controllers/NVIDIAIlluminationController/nvapi_accessor_Windows_Linux.h new file mode 100644 index 0000000..abf721d --- /dev/null +++ b/Controllers/NVIDIAIlluminationController/nvapi_accessor_Windows_Linux.h @@ -0,0 +1,27 @@ +/*---------------------------------------------------------*\ +| nvapi_accessor_Windows_Linux.h | +| | +| NVAPI accessor for NVIDIA NVAPI illumination API | +| | +| Carter Miller (GingerRunner) 20 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "nvapi.h" + +// NVAPI Direct Calls +#define NVAPI_ZONE_GET_CONTROL 0 +#define NVAPI_ZONE_SET_CONTROL 1 +#define NVAPI_CONTROL_BUFFER_TIME_MS 30 + +class nvapi_accessor +{ +public: + nvapi_accessor(NV_PHYSICAL_GPU_HANDLE handle); + NV_STATUS nvapi_zone_control(char nvapi_call, NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* zone_control_struct); + +private: + NV_PHYSICAL_GPU_HANDLE handle; +}; diff --git a/Controllers/NZXTHue1Controller/NZXTHue1Controller.cpp b/Controllers/NZXTHue1Controller/NZXTHue1Controller.cpp new file mode 100644 index 0000000..504d260 --- /dev/null +++ b/Controllers/NZXTHue1Controller/NZXTHue1Controller.cpp @@ -0,0 +1,302 @@ +/*---------------------------------------------------------*\ +| NZXTHue1Controller.cpp | +| | +| Driver for NZXT Hue 1 (Smart Device V1) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 16 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "NZXTHue1Controller.h" +#include "StringUtils.h" + +NZXTHue1Controller::NZXTHue1Controller(hid_device* dev_handle, unsigned int /*fan_channels*/, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + Initialize(); +} + +NZXTHue1Controller::~NZXTHue1Controller() +{ + +} + +std::string NZXTHue1Controller::GetFirmwareVersion() +{ + return(firmware_version); +} + +std::string NZXTHue1Controller::GetLocation() +{ + return("HID: " + location); +} + +std::string NZXTHue1Controller::GetName() +{ + return(name); +} + +std::string NZXTHue1Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned int NZXTHue1Controller::GetAccessoryType() +{ + return(accessory_type); +} + +void NZXTHue1Controller::SetEffect + ( + unsigned char mode, + unsigned char speed, + bool direction, + RGBColor * colors, + unsigned int num_colors + ) +{ + unsigned char color_data[120]; + + /*-----------------------------------------------------*\ + | If mode requires no colors, send packet | + \*-----------------------------------------------------*/ + if(num_colors == 0) + { + /*-----------------------------------------------------*\ + | Send mode without color data | + \*-----------------------------------------------------*/ + SendPacket(mode, direction, 0, speed, 0, NULL); + } + /*-----------------------------------------------------*\ + | If mode requires indexed colors, send color index | + | packets for each mode color | + \*-----------------------------------------------------*/ + else if(num_colors <= 8) + { + for(std::size_t color_idx = 0; color_idx < num_colors; color_idx++) + { + /*-----------------------------------------------------*\ + | Fill in color data (40 entries per color) | + \*-----------------------------------------------------*/ + for (std::size_t idx = 0; idx < 40; idx++) + { + int pixel_idx = (int)idx * 3; + RGBColor color = colors[color_idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send mode and color data | + \*-----------------------------------------------------*/ + SendPacket(mode, direction, (unsigned char)color_idx, speed, 40, &color_data[0]); + } + } + /*-----------------------------------------------------*\ + | If mode requires per-LED colors, fill colors array | + \*-----------------------------------------------------*/ + else + { + /*-----------------------------------------------------*\ + | Fill in color data (up to 40 colors) | + \*-----------------------------------------------------*/ + for (std::size_t idx = 0; idx < num_colors; idx++) + { + int pixel_idx = (int)idx * 3; + RGBColor color = colors[idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send mode and color data | + \*-----------------------------------------------------*/ + SendPacket(mode, direction, 0, speed, num_colors, &color_data[0]); + } +} + +void NZXTHue1Controller::SetLEDs + ( + RGBColor * colors, + unsigned int num_colors + ) +{ + unsigned char color_data[120]; + + /*-----------------------------------------------------*\ + | Fill in color data (up to 40 colors) | + \*-----------------------------------------------------*/ + for (std::size_t idx = 0; idx < num_colors; idx++) + { + int pixel_idx = (int)idx * 3; + RGBColor color = colors[idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send color data | + \*-----------------------------------------------------*/ + SendPacket(HUE_1_MODE_FIXED, false, 0, 0, num_colors, &color_data[0]); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void NZXTHue1Controller::Initialize() +{ + unsigned char usb_buf[65]; + unsigned int ret_val = 0; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Send Initialize command | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x01; + usb_buf[0x01] = 0x5C; + + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Send Start Reporting command | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x01; + usb_buf[0x01] = 0x5D; + + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Receive packets until a valid read has occurred | + \*-----------------------------------------------------*/ + do + { + ret_val = hid_read(dev, usb_buf, 21); + } while( (ret_val != 21) ); + + /*-----------------------------------------------------*\ + | Determine firmware version | + | | + | 0x0B is the major value | + | 0x0E is the minor value | + \*-----------------------------------------------------*/ + snprintf(firmware_version, 16, "%u.%u", usb_buf[0x0B], usb_buf[0x0E]); + + /*-----------------------------------------------------*\ + | Determine device count and type | + | | + | 0x11 defines the number of devices (strip, fan) | + | 0x10 defines the type (strip or fan) | + | 0: Hue+ RGB Strip (10 LEDs) | + | 1: Aer RGB Fan (8 LEDs) | + | All connected devices must be of the same type | + \*-----------------------------------------------------*/ + unsigned char dev_count = usb_buf[0x11]; + accessory_type = (usb_buf[0x10] >> 3); + + if(accessory_type == HUE_1_ACCESSORY_STRIP) + { + + num_leds = dev_count * 10; + } + else + { + num_leds = dev_count * 8; + } +} + +void NZXTHue1Controller::SendPacket + ( + unsigned char mode, + bool direction, + unsigned char color_idx, + unsigned char speed, + unsigned char color_count, + unsigned char* color_data + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up RGB packet 1 | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x02; + usb_buf[0x01] = 0x4B; + + /*-----------------------------------------------------*\ + | Set mode in RGB packet | + \*-----------------------------------------------------*/ + usb_buf[0x02] = mode; + + /*-----------------------------------------------------*\ + | Set options bitfield in RGB packet | + \*-----------------------------------------------------*/ + usb_buf[0x03] = direction ? ( 1 << 4 ) : 0; + + /*-----------------------------------------------------*\ + | Set color index and speed in RGB packet | + \*-----------------------------------------------------*/ + usb_buf[0x04] = ( color_idx << 5 ) | speed; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + unsigned int colors_in_packet = 20; + + if(color_count < 20) + { + colors_in_packet = color_count; + } + + memcpy(&usb_buf[0x05], color_data, colors_in_packet * 3); + + /*-----------------------------------------------------*\ + | Write RGB packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up RGB packet 2 | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x03; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + colors_in_packet = color_count - colors_in_packet; + + memcpy(&usb_buf[0x01], color_data, colors_in_packet * 3); + + hid_write(dev, usb_buf, 65); +} diff --git a/Controllers/NZXTHue1Controller/NZXTHue1Controller.h b/Controllers/NZXTHue1Controller/NZXTHue1Controller.h new file mode 100644 index 0000000..d080c3f --- /dev/null +++ b/Controllers/NZXTHue1Controller/NZXTHue1Controller.h @@ -0,0 +1,97 @@ +/*---------------------------------------------------------*\ +| NZXTHue1Controller.h | +| | +| Driver for NZXT Hue 1 (Smart Device V1) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 16 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +enum +{ + HUE_1_ACCESSORY_STRIP = 0x00, /* NZXT Hue+ LED Strip (10 LEDs)*/ + HUE_1_ACCESSORY_FAN = 0x01 /* NZXT Aer RGB Fan (8 LEDs) */ +}; + +enum +{ + HUE_1_SPEED_SLOWEST = 0x00, /* Slowest speed */ + HUE_1_SPEED_SLOW = 0x01, /* Slow speed */ + HUE_1_SPEED_NORMAL = 0x02, /* Normal speed */ + HUE_1_SPEED_FAST = 0x03, /* Fast speed */ + HUE_1_SPEED_FASTEST = 0x04, /* Fastest speed */ +}; + +enum +{ + HUE_1_MODE_FIXED = 0x00, /* Fixed colors mode */ + HUE_1_MODE_FADING = 0x01, /* Fading mode */ + HUE_1_MODE_SPECTRUM = 0x02, /* Spectrum cycle mode */ + HUE_1_MODE_MARQUEE = 0x03, /* Marquee mode */ + HUE_1_MODE_COVER_MARQUEE = 0x04, /* Cover marquee mode */ + HUE_1_MODE_ALTERNATING = 0x05, /* Alternating mode */ + HUE_1_MODE_PULSING = 0x06, /* Pulsing mode */ + HUE_1_MODE_BREATHING = 0x07, /* Breathing mode */ + HUE_1_MODE_ALERT = 0x08, /* Alert mode */ + HUE_1_MODE_CANDLELIGHT = 0x09, /* Candlelight mode */ + HUE_1_MODE_WINGS = 0x0C, /* Wings mode */ + HUE_1_MODE_WAVE = 0x0D, /* Wave mode */ +}; + +class NZXTHue1Controller +{ +public: + NZXTHue1Controller(hid_device* dev_handle, unsigned int fan_channels, const char* path, std::string dev_name); + ~NZXTHue1Controller(); + + std::string GetFirmwareVersion(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + + unsigned int GetAccessoryType(); + + void SetEffect + ( + unsigned char mode, + unsigned char speed, + bool direction, + RGBColor * colors, + unsigned int num_colors + ); + + void SetLEDs + ( + RGBColor * colors, + unsigned int num_colors + ); + + unsigned int num_leds; + +private: + hid_device* dev; + + char firmware_version[16]; + std::string location; + std::string name; + unsigned int accessory_type; + + void Initialize(); + + void SendPacket + ( + unsigned char mode, + bool direction, + unsigned char color_idx, + unsigned char speed, + unsigned char color_count, + unsigned char* color_data + ); +}; diff --git a/Controllers/NZXTHue1Controller/NZXTHue1ControllerDetect.cpp b/Controllers/NZXTHue1Controller/NZXTHue1ControllerDetect.cpp new file mode 100644 index 0000000..20135f8 --- /dev/null +++ b/Controllers/NZXTHue1Controller/NZXTHue1ControllerDetect.cpp @@ -0,0 +1,44 @@ +/*---------------------------------------------------------*\ +| NZXTHue1ControllerDetect.cpp | +| | +| Detector for NZXT Hue 1 (Smart Device V1) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 16 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "NZXTHue1Controller.h" +#include "RGBController_NZXTHue1.h" + +/*-----------------------------------------------------*\ +| NZXT USB IDs | +\*-----------------------------------------------------*/ +#define NZXT_VID 0x1E71 +#define NZXT_SMART_DEVICE_V1_PID 0x1714 + +/******************************************************************************************\ +* * +* DetectNZXTHue1Controllers * +* * +* Detect devices supported by the NZXTHue1 driver * +* * +\******************************************************************************************/ + +void DetectNZXTHue1Controllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + NZXTHue1Controller* controller = new NZXTHue1Controller(dev, 3, info->path, name); + RGBController_NZXTHue1* rgb_controller = new RGBController_NZXTHue1(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectNZXTHue1Controllers() */ + +REGISTER_HID_DETECTOR("NZXT Smart Device V1", DetectNZXTHue1Controllers, NZXT_VID, NZXT_SMART_DEVICE_V1_PID); diff --git a/Controllers/NZXTHue1Controller/RGBController_NZXTHue1.cpp b/Controllers/NZXTHue1Controller/RGBController_NZXTHue1.cpp new file mode 100644 index 0000000..8dd853a --- /dev/null +++ b/Controllers/NZXTHue1Controller/RGBController_NZXTHue1.cpp @@ -0,0 +1,301 @@ +/*---------------------------------------------------------*\ +| RGBController_NZXTHue1.cpp | +| | +| RGBController for NZXT Hue 1 (Smart Device V1) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 16 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_NZXTHue1.h" + +/**------------------------------------------------------------------*\ + @name NZXT Hue 1 + @category LEDStrip + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectNZXTHue1Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_NZXTHue1::RGBController_NZXTHue1(NZXTHue1Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "NZXT"; + type = DEVICE_TYPE_LEDSTRIP; + description = "NZXT Hue 1 Device"; + version = controller->GetFirmwareVersion(); + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = HUE_1_MODE_FIXED; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Fading; + Fading.name = "Fading"; + Fading.value = HUE_1_MODE_FADING; + Fading.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Fading.speed_min = HUE_1_SPEED_SLOWEST; + Fading.speed_max = HUE_1_SPEED_FASTEST; + Fading.colors_min = 1; + Fading.colors_max = 8; + Fading.speed = HUE_1_SPEED_NORMAL; + Fading.color_mode = MODE_COLORS_MODE_SPECIFIC; + Fading.colors.resize(2); + modes.push_back(Fading); + + mode SpectrumCycle; + SpectrumCycle.name = "Rainbow Wave"; + SpectrumCycle.value = HUE_1_MODE_SPECTRUM; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + SpectrumCycle.speed_min = HUE_1_SPEED_SLOWEST; + SpectrumCycle.speed_max = HUE_1_SPEED_FASTEST; + SpectrumCycle.speed = HUE_1_SPEED_NORMAL; + SpectrumCycle.direction = MODE_DIRECTION_RIGHT; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = HUE_1_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Marquee.speed_min = HUE_1_SPEED_SLOWEST; + Marquee.speed_max = HUE_1_SPEED_FASTEST; + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.speed = HUE_1_SPEED_NORMAL; + Marquee.direction = MODE_DIRECTION_RIGHT; + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + Marquee.colors.resize(1); + modes.push_back(Marquee); + + mode CoverMarquee; + CoverMarquee.name = "Cover Marquee"; + CoverMarquee.value = HUE_1_MODE_COVER_MARQUEE; + CoverMarquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + CoverMarquee.speed_min = HUE_1_SPEED_SLOWEST; + CoverMarquee.speed_max = HUE_1_SPEED_FASTEST; + CoverMarquee.colors_min = 1; + CoverMarquee.colors_max = 8; + CoverMarquee.speed = HUE_1_SPEED_NORMAL; + CoverMarquee.direction = MODE_DIRECTION_RIGHT; + CoverMarquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + CoverMarquee.colors.resize(2); + modes.push_back(CoverMarquee); + + mode Alternating; + Alternating.name = "Alternating"; + Alternating.value = HUE_1_MODE_ALTERNATING; + Alternating.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Alternating.speed_min = HUE_1_SPEED_SLOWEST; + Alternating.speed_max = HUE_1_SPEED_FASTEST; + Alternating.colors_min = 1; + Alternating.colors_max = 2; + Alternating.speed = HUE_1_SPEED_NORMAL; + Alternating.direction = MODE_DIRECTION_RIGHT; + Alternating.color_mode = MODE_COLORS_MODE_SPECIFIC; + Alternating.colors.resize(2); + modes.push_back(Alternating); + + mode Pulsing; + Pulsing.name = "Pulsing"; + Pulsing.value = HUE_1_MODE_PULSING; + Pulsing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Pulsing.speed_min = HUE_1_SPEED_SLOWEST; + Pulsing.speed_max = HUE_1_SPEED_FASTEST; + Pulsing.colors_min = 1; + Pulsing.colors_max = 8; + Pulsing.speed = HUE_1_SPEED_NORMAL; + Pulsing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulsing.colors.resize(2); + modes.push_back(Pulsing); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = HUE_1_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = HUE_1_SPEED_SLOWEST; + Breathing.speed_max = HUE_1_SPEED_FASTEST; + Breathing.colors_min = 1; + Breathing.colors_max = 8; + Breathing.speed = HUE_1_SPEED_NORMAL; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(2); + modes.push_back(Breathing); + + mode Alert; + Alert.name = "Alert"; + Alert.value = HUE_1_MODE_ALERT; + Alert.flags = 0; + Alert.color_mode = MODE_COLORS_NONE; + modes.push_back(Alert); + + mode Candlelight; + Candlelight.name = "Candlelight"; + Candlelight.value = HUE_1_MODE_CANDLELIGHT; + Candlelight.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Candlelight.colors_min = 1; + Candlelight.colors_max = 1; + Candlelight.color_mode = MODE_COLORS_MODE_SPECIFIC; + Candlelight.colors.resize(1); + modes.push_back(Candlelight); + + mode Wings; + Wings.name = "Wings"; + Wings.value = HUE_1_MODE_WINGS; + Wings.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Wings.speed_min = HUE_1_SPEED_SLOWEST; + Wings.speed_max = HUE_1_SPEED_FASTEST; + Wings.colors_min = 1; + Wings.colors_max = 1; + Wings.speed = HUE_1_SPEED_NORMAL; + Wings.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wings.colors.resize(1); + modes.push_back(Wings); + + mode Wave; + Wave.name = "Wave"; + Wave.value = HUE_1_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Wave.speed_min = HUE_1_SPEED_SLOWEST; + Wave.speed_max = HUE_1_SPEED_FASTEST; + Wave.speed = HUE_1_SPEED_NORMAL; + Wave.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Wave); + + SetupZones(); +} + +RGBController_NZXTHue1::~RGBController_NZXTHue1() +{ + delete controller; +} + +void RGBController_NZXTHue1::SetupZones() +{ + /*-------------------------------------------------*\ + | Set up zone | + \*-------------------------------------------------*/ + zone* new_zone = new zone; + + new_zone->name = "Hue 1 Channel"; + new_zone->type = ZONE_TYPE_LINEAR; + new_zone->leds_min = 0; + new_zone->leds_max = 40; + new_zone->leds_count = controller->num_leds; + new_zone->matrix_map = NULL; + + zones.push_back(*new_zone); + + /*-------------------------------------------------*\ + | Set up LEDs | + \*-------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led new_led; + new_led.name = "Hue 1 Channel"; + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_idx + 1)); + + leds.push_back(new_led); + } + + /*-------------------------------------------------*\ + | Set up Segments | + \*-------------------------------------------------*/ + unsigned int num_segments = 0; + unsigned int segment_size = 0; + std::string segment_name = ""; + + switch(controller->GetAccessoryType()) + { + case HUE_1_ACCESSORY_STRIP: + segment_size = 10; + num_segments = zones[0].leds_count / segment_size; + segment_name = "Hue+ Strip"; + break; + + case HUE_1_ACCESSORY_FAN: + segment_size = 8; + num_segments = zones[0].leds_count / segment_size; + segment_name = "Aer RGB Fan"; + break; + } + + if(segment_name != "") + { + for(unsigned int segment_idx = 0; segment_idx < num_segments; segment_idx++) + { + segment new_segment; + new_segment.name = segment_name; + new_segment.type = ZONE_TYPE_LINEAR; + new_segment.start_idx = segment_idx * segment_size; + new_segment.leds_count = segment_size; + + zones[0].segments.push_back(new_segment); + } + } + + SetupColors(); +} + +void RGBController_NZXTHue1::ResizeZone(int /*zone*/, int /*new_size*/) +{ +} + +void RGBController_NZXTHue1::DeviceUpdateLEDs() +{ + controller->SetLEDs(zones[0].colors, zones[0].leds_count); +} + +void RGBController_NZXTHue1::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_NZXTHue1::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_NZXTHue1::DeviceUpdateMode() +{ + if(modes[active_mode].value == HUE_1_MODE_FIXED) + { + DeviceUpdateLEDs(); + } + else + { + RGBColor* colors = NULL; + bool direction = false; + + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + direction = true; + } + + if(modes[active_mode].colors.size() > 0) + { + colors = &modes[active_mode].colors[0]; + } + + controller->SetEffect + ( + modes[active_mode].value, + modes[active_mode].speed, + direction, + colors, + (unsigned int)modes[active_mode].colors.size() + ); + } +} diff --git a/Controllers/NZXTHue1Controller/RGBController_NZXTHue1.h b/Controllers/NZXTHue1Controller/RGBController_NZXTHue1.h new file mode 100644 index 0000000..042af3a --- /dev/null +++ b/Controllers/NZXTHue1Controller/RGBController_NZXTHue1.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_NZXTHue1.h | +| | +| RGBController for NZXT Hue 1 (Smart Device V1) | +| | +| Adam Honse (calcprogrammer1@gmail.com) 16 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "NZXTHue1Controller.h" + +class RGBController_NZXTHue1 : public RGBController +{ +public: + RGBController_NZXTHue1(NZXTHue1Controller* controller_ptr); + ~RGBController_NZXTHue1(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + NZXTHue1Controller* controller; +}; diff --git a/Controllers/NZXTHue2Controller/NZXTHue2Controller.cpp b/Controllers/NZXTHue2Controller/NZXTHue2Controller.cpp new file mode 100644 index 0000000..328f174 --- /dev/null +++ b/Controllers/NZXTHue2Controller/NZXTHue2Controller.cpp @@ -0,0 +1,634 @@ +/*---------------------------------------------------------*\ +| NZXTHue2Controller.cpp | +| | +| Driver for NZXT Hue 2 | +| | +| Adam Honse (calcprogrammer1@gmail.com) 29 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "LogManager.h" +#include "NZXTHue2Controller.h" +#include "StringUtils.h" + +NZXTHue2Controller::NZXTHue2Controller(hid_device* dev_handle, unsigned int rgb_channels, unsigned int fan_channels, const char* path, std::string dev_name, bool use_2023_effects_val) +{ + dev = dev_handle; + location = path; + name = dev_name; + + use_2023_effects = use_2023_effects_val; + num_fan_channels = fan_channels; + num_rgb_channels = rgb_channels; + + SendFirmwareRequest(); + UpdateDeviceList(); + + fan_cmd.resize(num_fan_channels); + fan_rpm.resize(num_fan_channels); + UpdateStatus(); +} + +NZXTHue2Controller::~NZXTHue2Controller() +{ + hid_close(dev); +} + +unsigned char NZXTHue2Controller::GetFanCommand + ( + unsigned char fan_channel + ) +{ + return(fan_cmd[fan_channel]); +} + +unsigned short NZXTHue2Controller::GetFanRPM + ( + unsigned char fan_channel + ) +{ + return(fan_rpm[fan_channel]); +} + +std::string NZXTHue2Controller::GetLocation() +{ + return("HID: " + location); +} + +std::string NZXTHue2Controller::GetName() +{ + return(name); +} + +unsigned int NZXTHue2Controller::GetNumFanChannels() +{ + return(num_fan_channels); +} + +unsigned int NZXTHue2Controller::GetNumRGBChannels() +{ + return(num_rgb_channels); +} + +std::string NZXTHue2Controller::GetFirmwareVersion() +{ + return(firmware_version); +} + +std::string NZXTHue2Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void NZXTHue2Controller::SendFan + ( + unsigned char port, + unsigned char /*mode*/, + unsigned char speed + ) +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up RGB packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x62; + usb_buf[0x01] = 0x01; + usb_buf[0x02] = 1 << port; + usb_buf[port + 3] = speed; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 64); + hid_read(dev, usb_buf, 64); +} + +void NZXTHue2Controller::UpdateDeviceList() +{ + unsigned char usb_buf[64]; + unsigned int ret_val = 0; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Device Information Request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x20; + usb_buf[0x01] = 0x03; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 64); + + /*-----------------------------------------------------*\ + | Receive packets until 0x21 0x03 is received | + \*-----------------------------------------------------*/ + do + { + ret_val = hid_read(dev, usb_buf, sizeof(usb_buf)); + } while( (ret_val != 64) || (usb_buf[0] != 0x21) || (usb_buf[1] != 0x03) ); + + for(unsigned int chan = 0; chan < num_rgb_channels; chan++) + { + unsigned int start = 0x0F + (6 * chan); + unsigned int num_leds_on_channel = 0; + + for(int dev = 0; dev < 6; dev++) + { + unsigned int num_leds_in_device = 0; + + switch(usb_buf[start + dev]) + { + case 0x01: //Hue 1 strip + num_leds_in_device = 10; + break; + + case 0x02: //Aer 1 fan + num_leds_in_device = 8; + break; + + case 0x04: //Hue 2 strip (10 LEDs) + num_leds_in_device = 10; + break; + + case 0x05: //Hue 2 strip (8 LEDs) + num_leds_in_device = 8; + break; + + case 0x06: //Hue 2 strip (6 LEDs) + num_leds_in_device = 6; + break; + + case 0x08: //Hue 2 Cable Comb (14 LEDs) + num_leds_in_device = 14; + break; + + case 0x09: //Hue 2 Underglow (300mm) (15 LEDs) + num_leds_in_device = 15; + break; + + case 0x0A: //Hue 2 Underglow (200mm) (10 LEDs) + num_leds_in_device = 10; + break; + + case 0x0B: //Aer 2 fan (120mm) + num_leds_in_device = 8; + break; + + case 0x0C: //Aer 2 fan (140mm) + num_leds_in_device = 8; + break; + + case 0x10: //Kraken X3 ring + num_leds_in_device = 8; + break; + + case 0x11: //Kraken X3 logo + num_leds_in_device = 1; + break; + + case 0x13: //F120 RGB fan (120mm) + num_leds_in_device = 18; + break; + + case 0x14: //F140 RGB fan (140mm) + num_leds_in_device = 18; + break; + + case 0x15: //F120 RGB Duo fan (120mm) + num_leds_in_device = 20; + break; + + case 0x16: //F140 RGB Duo fan (140mm) + num_leds_in_device = 20; + break; + + case 0x17: //F120 RGB Core fan (120mm) + num_leds_in_device = 8; + break; + + case 0x18: //F140 RGB Core fan (140mm) + num_leds_in_device = 8; + break; + + case 0x19: //F120 RGB Core fan case version (120mm) + num_leds_in_device = 8; + break; + + case 0x1D: //F360 RGB Core Fan Case Version (360mm) + num_leds_in_device = 24; + break; + + case 0x1E: //Kraken Elite Ring + num_leds_in_device = 24; + break; + + case 0x1F: //F420 RGB + num_leds_in_device = 24; + break; + + default: + break; + } + + channel_dev_ids[chan][dev] = usb_buf[start + dev]; + channel_dev_szs[chan][dev] = num_leds_in_device; + + LOG_DEBUG("[NZXT Hue 2] %d: Device ID: %02X LEDs: %d", dev, usb_buf[start + dev], num_leds_in_device); + + num_leds_on_channel += num_leds_in_device; + } + + channel_leds[chan] = num_leds_on_channel; + } +} + +void NZXTHue2Controller::UpdateStatus() +{ + unsigned char usb_buf[64]; + unsigned int ret_val = 0; + + if(false)//num_fan_channels > 0) + { + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Read packet | + \*-----------------------------------------------------*/ + do + { + ret_val = hid_read(dev, usb_buf, sizeof(usb_buf)); + } while( (ret_val != 64) || (usb_buf[0] != 0x67) || (usb_buf[1] != 0x02) ); + + /*-----------------------------------------------------*\ + | Extract fan information | + \*-----------------------------------------------------*/ + for(unsigned int fan_idx = 0; fan_idx < num_fan_channels; fan_idx++) + { + unsigned char cmd; + unsigned short rpm; + + cmd = usb_buf[40 + fan_idx]; + rpm = ( usb_buf[25 + (2 * fan_idx)] << 8 ) | usb_buf[24 + (2 * fan_idx)]; + + fan_cmd[fan_idx] = cmd; + fan_rpm[fan_idx] = rpm; + } + } +} + +void NZXTHue2Controller::SetChannelEffect + ( + unsigned char channel, + unsigned char mode, + unsigned char speed, + bool direction, + RGBColor * colors, + unsigned int num_colors + ) +{ + unsigned char color_data[24]; + + /*-----------------------------------------------------*\ + | Fill in color data (up to 8 colors) | + \*-----------------------------------------------------*/ + for (std::size_t idx = 0; idx < num_colors; idx++) + { + int pixel_idx = (int)idx * 3; + RGBColor color = colors[idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send effect packet | + \*-----------------------------------------------------*/ + if(use_2023_effects) + { + SendEffect2023(channel, mode, speed, direction, num_colors, &color_data[0]); + } + else + { + SendEffect(channel, mode, speed, direction, num_colors, &color_data[0]); + } +} + +void NZXTHue2Controller::SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors + ) +{ + unsigned char color_data[120]; + + /*-----------------------------------------------------*\ + | Fill in color data (up to 40 colors) | + \*-----------------------------------------------------*/ + for (std::size_t idx = 0; idx < num_colors; idx++) + { + int pixel_idx = (int)idx * 3; + RGBColor color = colors[idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send first group of color data | + \*-----------------------------------------------------*/ + unsigned char first_color_count = (num_colors > 20) ? 20 : (unsigned char)num_colors; + SendDirect(channel, 0, first_color_count, &color_data[0]); + + /*-----------------------------------------------------*\ + | Send second group of color data if necessary | + \*-----------------------------------------------------*/ + if(num_colors > 20) + { + SendDirect(channel, 1, (unsigned char)(num_colors - 20), &color_data[60]); + } + + /*-----------------------------------------------------*\ + | Send apply packet | + \*-----------------------------------------------------*/ + SendApply(channel); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void NZXTHue2Controller::SendApply + ( + unsigned char channel + ) +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Apply packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x22; + usb_buf[0x01] = 0xA0; + usb_buf[0x02] = (unsigned char)(1 << channel); + usb_buf[0x04] = 0x01; + usb_buf[0x07] = 0x28; + usb_buf[0x0A] = 0x80; + usb_buf[0x0C] = 0x32; + usb_buf[0x0F] = 0x01; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 64); + //hid_read(dev, usb_buf, 64); +} + +void NZXTHue2Controller::SendDirect + ( + unsigned char channel, + unsigned char group, + unsigned char color_count, + unsigned char* color_data + ) +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x22; + usb_buf[0x01] = 0x10 | group; + usb_buf[0x02] = (unsigned char)(1 << channel); + usb_buf[0x03] = 0x00; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x04], color_data, color_count * 3); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 64); + //hid_read(dev, usb_buf, 64); +} + +void NZXTHue2Controller::SendEffect + ( + unsigned char channel, + unsigned char mode, + unsigned char speed, + bool direction, + unsigned char color_count, + unsigned char* color_data + ) +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Effect packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x28; + usb_buf[0x01] = 0x03; + usb_buf[0x02] = (unsigned char)(1 << channel); + usb_buf[0x03] = 0x28; + + /*-----------------------------------------------------*\ + | Set mode in USB packet | + \*-----------------------------------------------------*/ + usb_buf[0x04] = mode; + + /*-----------------------------------------------------*\ + | Set speed in USB packet | + \*-----------------------------------------------------*/ + usb_buf[0x05] = speed; + + /*-----------------------------------------------------*\ + | Set moving flag to true in USB packet | + \*-----------------------------------------------------*/ + usb_buf[0x06] = true; + + /*-----------------------------------------------------*\ + | Set direction in USB packet | + \*-----------------------------------------------------*/ + usb_buf[0x07] = direction ? 0x01 : 0x00; + + /*-----------------------------------------------------*\ + | Set color count in USB packet | + \*-----------------------------------------------------*/ + usb_buf[0x08] = color_count; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x0A], color_data, color_count * 3); + + hid_write(dev, usb_buf, 64); + //hid_read(dev, usb_buf, 64); +} + +void NZXTHue2Controller::SendEffect2023 + ( + unsigned char channel, + unsigned char mode, + unsigned char speed, + bool direction, + unsigned char color_count, + unsigned char* color_data + ) +{ + unsigned char usb_buf[64]; + unsigned char speed_data[2] = { 0x32, 0x00 }; + unsigned char mode_modifier = 0x00; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + if(speed > HUE_2_SPEED_FASTEST) + { + speed = HUE_2_SPEED_NORMAL; + } + + switch(mode) + { + case HUE_2_MODE_FADING: + { + const unsigned char values[5][2] = { {0x50, 0x00}, {0x3C, 0x00}, {0x28, 0x00}, {0x14, 0x00}, {0x0A, 0x00} }; + speed_data[0] = values[speed][0]; + speed_data[1] = values[speed][1]; + mode_modifier = 0x08; + } + break; + + case HUE_2_MODE_SPECTRUM: + case HUE_2_MODE_RAINBOW_FLOW: + case HUE_2_MODE_SUPER_RAINBOW: + case HUE_2_MODE_RAINBOW_PULSE: + { + const unsigned char values[5][2] = { {0x5E, 0x01}, {0x2C, 0x01}, {0xFA, 0x00}, {0x96, 0x00}, {0x50, 0x00} }; + speed_data[0] = values[speed][0]; + speed_data[1] = values[speed][1]; + } + break; + + case HUE_2_MODE_ALTERNATING: + { + const unsigned char values[5][2] = { {0x40, 0x06}, {0x14, 0x05}, {0xE8, 0x03}, {0x20, 0x03}, {0x58, 0x02} }; + speed_data[0] = values[speed][0]; + speed_data[1] = values[speed][1]; + } + break; + + case HUE_2_MODE_PULSING: + case HUE_2_MODE_STARRY_NIGHT: + { + const unsigned char values[5][2] = { {0x19, 0x00}, {0x14, 0x00}, {0x0F, 0x00}, {0x07, 0x00}, {0x04, 0x00} }; + speed_data[0] = values[speed][0]; + speed_data[1] = values[speed][1]; + mode_modifier = (mode == HUE_2_MODE_PULSING) ? 0x08 : 0x00; + } + break; + + case HUE_2_MODE_BREATHING: + { + const unsigned char values[5][2] = { {0x28, 0x00}, {0x1E, 0x00}, {0x14, 0x00}, {0x0A, 0x00}, {0x04, 0x00} }; + speed_data[0] = values[speed][0]; + speed_data[1] = values[speed][1]; + mode_modifier = 0x08; + } + break; + + default: + break; + } + + usb_buf[0x00] = 0x2A; + usb_buf[0x01] = 0x04; + usb_buf[0x02] = (unsigned char)(1 << channel); + usb_buf[0x03] = usb_buf[0x02]; + usb_buf[0x04] = mode; + usb_buf[0x05] = speed_data[0]; + usb_buf[0x06] = speed_data[1]; + + memcpy(&usb_buf[0x07], color_data, color_count * 3); + + usb_buf[0x37] = direction ? 0x02 : 0x00; + usb_buf[0x38] = color_count; + usb_buf[0x39] = mode_modifier; + usb_buf[0x3A] = 0x08; + usb_buf[0x3B] = 0x03; + + hid_write(dev, usb_buf, 64); +} + +void NZXTHue2Controller::SendFirmwareRequest() +{ + unsigned char usb_buf[64]; + unsigned int ret_val = 0; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Firmware Request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x10; + usb_buf[0x01] = 0x01; + + hid_write(dev, usb_buf, 64); + + /*-----------------------------------------------------*\ + | Receive packets until 0x11 0x01 is received | + \*-----------------------------------------------------*/ + do + { + ret_val = hid_read(dev, usb_buf, sizeof(usb_buf)); + } while( (ret_val != 64) || (usb_buf[0] != 0x11) || (usb_buf[1] != 0x01) ); + + snprintf(firmware_version, 16, "%u.%u.%u", usb_buf[0x11], usb_buf[0x12], usb_buf[0x13]); +} diff --git a/Controllers/NZXTHue2Controller/NZXTHue2Controller.h b/Controllers/NZXTHue2Controller/NZXTHue2Controller.h new file mode 100644 index 0000000..e429054 --- /dev/null +++ b/Controllers/NZXTHue2Controller/NZXTHue2Controller.h @@ -0,0 +1,170 @@ +/*---------------------------------------------------------*\ +| NZXTHue2Controller.h | +| | +| Driver for NZXT Hue 2 | +| | +| Adam Honse (calcprogrammer1@gmail.com) 29 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +enum +{ + HUE_2_CHANNEL_ALL = 0x00, /* All channels */ + HUE_2_CHANNEL_1 = 0x01, /* Channel 1 */ + HUE_2_CHANNEL_2 = 0x02, /* Channel 2 */ + HUE_2_CHANNEL_3 = 0x03, /* Channel 3 */ + HUE_2_CHANNEL_4 = 0x04, /* Channel 4 */ + HUE_2_CHANNEL_5 = 0x05, /* Channel 5 */ + HUE_2_CHANNEL_6 = 0x06, /* Channel 6 */ + HUE_2_NUM_CHANNELS = 0x06 /* Number of channels */ +}; + +enum +{ + HUE_2_CHANNEL_1_IDX = 0x00, /* Channel 1 array index */ + HUE_2_CHANNEL_2_IDX = 0x01, /* Channel 2 array index */ + HUE_2_CHANNEL_3_IDX = 0x01, /* Channel 3 array index */ + HUE_2_CHANNEL_4_IDX = 0x01, /* Channel 4 array index */ +}; + +enum +{ + HUE_2_SPEED_SLOWEST = 0x00, /* Slowest speed */ + HUE_2_SPEED_SLOW = 0x01, /* Slow speed */ + HUE_2_SPEED_NORMAL = 0x02, /* Normal speed */ + HUE_2_SPEED_FAST = 0x03, /* Fast speed */ + HUE_2_SPEED_FASTEST = 0x04, /* Fastest speed */ +}; + +enum +{ + HUE_2_MODE_FIXED = 0x00, /* Fixed colors mode */ + HUE_2_MODE_FADING = 0x01, /* Fading mode */ + HUE_2_MODE_SPECTRUM = 0x02, /* Spectrum cycle mode */ + HUE_2_MODE_MARQUEE = 0x03, /* Marquee mode */ + HUE_2_MODE_COVER_MARQUEE = 0x04, /* Cover marquee mode */ + HUE_2_MODE_ALTERNATING = 0x05, /* Alternating mode */ + HUE_2_MODE_PULSING = 0x06, /* Pulsing mode */ + HUE_2_MODE_BREATHING = 0x07, /* Breathing mode */ + HUE_2_MODE_CANDLE = 0x08, /* Candle Mode */ + HUE_2_MODE_STARRY_NIGHT = 0x09, /* Starry Night mode */ + HUE_2_MODE_RAINBOW_FLOW = 0x0b, /* Rainbow Flow mode */ + HUE_2_MODE_SUPER_RAINBOW = 0x0c, /* Super Rainbow mode */ + HUE_2_MODE_RAINBOW_PULSE = 0x0d, /* Rainbow Pulse mode */ + HUE_2_NUM_MODES /* Number of Hue 2 modes */ +}; + +class NZXTHue2Controller +{ +public: + NZXTHue2Controller(hid_device* dev_handle, unsigned int rgb_channels, unsigned int fan_channels, const char* path, std::string dev_name, bool use_2023_effects = false); + ~NZXTHue2Controller(); + + std::string GetFirmwareVersion(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + + unsigned char GetFanCommand + ( + unsigned char fan_channel + ); + + unsigned short GetFanRPM + ( + unsigned char fan_channel + ); + + unsigned int GetNumFanChannels(); + + unsigned int GetNumRGBChannels(); + + void SendFan + ( + unsigned char port, + unsigned char mode, + unsigned char speed + ); + + void SetChannelEffect + ( + unsigned char channel, + unsigned char mode, + unsigned char speed, + bool direction, + RGBColor * colors, + unsigned int num_colors + ); + + void SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors + ); + + void UpdateDeviceList(); + + void UpdateStatus(); + + unsigned int channel_leds[HUE_2_NUM_CHANNELS]; + unsigned int channel_dev_ids[HUE_2_NUM_CHANNELS][6]; + unsigned int channel_dev_szs[HUE_2_NUM_CHANNELS][6]; + +private: + hid_device* dev; + + std::vector fan_cmd; + std::vector fan_rpm; + + char firmware_version[16]; + std::string location; + std::string name; + bool use_2023_effects; + unsigned int num_fan_channels; + unsigned int num_rgb_channels; + + void SendApply + ( + unsigned char channel + ); + + void SendDirect + ( + unsigned char channel, + unsigned char group, + unsigned char color_count, + unsigned char* color_data + ); + + void SendEffect + ( + unsigned char channel, + unsigned char mode, + unsigned char speed, + bool direction, + unsigned char color_count, + unsigned char* color_data + ); + + void SendEffect2023 + ( + unsigned char channel, + unsigned char mode, + unsigned char speed, + bool direction, + unsigned char color_count, + unsigned char* color_data + ); + + void SendFirmwareRequest(); +}; diff --git a/Controllers/NZXTHue2Controller/NZXTHue2ControllerDetect.cpp b/Controllers/NZXTHue2Controller/NZXTHue2ControllerDetect.cpp new file mode 100644 index 0000000..b256a28 --- /dev/null +++ b/Controllers/NZXTHue2Controller/NZXTHue2ControllerDetect.cpp @@ -0,0 +1,124 @@ +/*---------------------------------------------------------*\ +| NZXTHue2ControllerDetect.cpp | +| | +| Detector for NZXT Hue 2 | +| | +| Adam Honse (calcprogrammer1@gmail.com) 29 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "LogManager.h" +#include "NZXTHue2Controller.h" +#include "RGBController_NZXTHue2.h" + +#define NZXT_VID 0x1E71 +#define NZXT_HUE_2_PID 0x2001 +#define NZXT_HUE_2_AMBIENT_PID 0x2002 +#define NZXT_MOTHERBOARD_DEVICE_PID 0x2005 +#define NZXT_MOTHERBOARD_DEVICE_2_PID 0x200B +#define NZXT_SMART_DEVICE_V2_PID 0x2006 +#define NZXT_KRAKEN_X3_SERIES_PID 0x2007 +#define NZXT_KRAKEN_X3_SERIES_RGB_PID 0x2014 +#define NZXT_KRAKEN_2024_ELITE_SERIES_RGB_PID 0x3012 +#define NZXT_RGB_FAN_CONTROLLER_PID 0x2009 +#define NZXT_RGB_FAN_CONTROLLER2_PID 0x2010 +#define NZXT_RGB_FAN_CONTROLLER3_PID 0x200E +#define NZXT_RGB_FAN_CONTROLLER4_PID 0x2011 +#define NZXT_RGB_FAN_CONTROLLER5_PID 0x2019 +#define NZXT_RGB_FAN_CONTROLLER6_PID 0x2020 +#define NZXT_RGB_FAN_CONTROLLER7_PID 0x201F +#define NZXT_RGB_FAN_CONTROLLER8_PID 0x2022 +#define NZXT_RGB_FAN_CONTROLLER9_PID 0x201B +#define NZXT_RGB_CONTROLLER_1_PID 0x2012 +#define NZXT_RGB_CONTROLLER_2_PID 0x2021 +#define NZXT_SMART_DEVICE_V2_1_PID 0x200D +#define NZXT_SMART_DEVICE_V2_2_PID 0x200F + +static void spawn_hue(hid_device_info* info, const std::string& name, int rgb_channels, int fan_channels, bool use_2023_effects = false) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + NZXTHue2Controller* controller = new NZXTHue2Controller(dev, rgb_channels, fan_channels, info->path, name, use_2023_effects); + RGBController_NZXTHue2* rgb_controller = new RGBController_NZXTHue2(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + LOG_TRACE("[NZXTHue2Controller] NZXT Controller setup: %s", info->path); + } + else + { + LOG_DEBUG("[NZXTHue2Controller] Failed to load device: %s!", info->path); + } +} + +void DetectNZXTHue2(hid_device_info* info, const std::string& name) +{ + spawn_hue(info, name, 4, 0); +} + +void DetectNZXTHue2Ambient(hid_device_info* info, const std::string& name) +{ + spawn_hue(info, name, 2, 0); +} + +void DetectNZXTHue2Motherboard(hid_device_info* info, const std::string& name) +{ + spawn_hue(info, name, 2, 3); +} + +void DetectNZXTSmartDeviceV2(hid_device_info* info, const std::string& name) +{ + spawn_hue(info, name, 2, 3); +} + +void DetectNZXTKrakenX3(hid_device_info* info, const std::string& name) +{ + spawn_hue(info, name, 3, 0); +} + +void DetectNZXTKrakenElite(hid_device_info* info, const std::string& name) +{ + spawn_hue(info, name, 2, 2, true); +} + +void DetectNZXTFanController(hid_device_info* info, const std::string& name) +{ + spawn_hue(info, name, 2, 3); +} + +void DetectNZXTFanController6Channel(hid_device_info* info, const std::string& name) +{ + spawn_hue(info, name, 6, 3); +} + +void DetectNZXTRGBController(hid_device_info* info, const std::string& name) +{ + spawn_hue(info, name, 3, 0); +} + +REGISTER_HID_DETECTOR("NZXT RGB & Fan Controller", DetectNZXTFanController, NZXT_VID, NZXT_RGB_FAN_CONTROLLER_PID); +REGISTER_HID_DETECTOR("NZXT RGB & Fan Controller", DetectNZXTFanController, NZXT_VID, NZXT_RGB_FAN_CONTROLLER2_PID); +REGISTER_HID_DETECTOR("NZXT RGB & Fan Controller", DetectNZXTFanController, NZXT_VID, NZXT_RGB_FAN_CONTROLLER3_PID); +REGISTER_HID_DETECTOR("NZXT RGB & Fan Controller", DetectNZXTFanController6Channel, NZXT_VID, NZXT_RGB_FAN_CONTROLLER4_PID); +REGISTER_HID_DETECTOR("NZXT RGB & Fan Controller", DetectNZXTFanController6Channel, NZXT_VID, NZXT_RGB_FAN_CONTROLLER5_PID); +REGISTER_HID_DETECTOR("NZXT RGB & Fan Controller", DetectNZXTFanController6Channel, NZXT_VID, NZXT_RGB_FAN_CONTROLLER6_PID); +REGISTER_HID_DETECTOR("NZXT RGB & Fan Controller", DetectNZXTFanController6Channel, NZXT_VID, NZXT_RGB_FAN_CONTROLLER7_PID); +REGISTER_HID_DETECTOR("NZXT RGB & Fan Controller 2024", DetectNZXTFanController6Channel, NZXT_VID, NZXT_RGB_FAN_CONTROLLER8_PID); +REGISTER_HID_DETECTOR("NZXT B650E Motherboard", DetectNZXTFanController6Channel, NZXT_VID, NZXT_RGB_FAN_CONTROLLER9_PID); +REGISTER_HID_DETECTOR("NZXT Hue 2", DetectNZXTHue2, NZXT_VID, NZXT_HUE_2_PID); +REGISTER_HID_DETECTOR("NZXT Hue 2 Ambient", DetectNZXTHue2Ambient, NZXT_VID, NZXT_HUE_2_AMBIENT_PID); +REGISTER_HID_DETECTOR("NZXT Hue 2 Motherboard", DetectNZXTHue2Motherboard, NZXT_VID, NZXT_MOTHERBOARD_DEVICE_PID); +REGISTER_HID_DETECTOR("NZXT Hue 2 Motherboard", DetectNZXTHue2Motherboard, NZXT_VID, NZXT_MOTHERBOARD_DEVICE_2_PID); +REGISTER_HID_DETECTOR("NZXT Kraken X3 Series", DetectNZXTKrakenX3, NZXT_VID, NZXT_KRAKEN_X3_SERIES_PID); +REGISTER_HID_DETECTOR("NZXT Kraken X3 Series RGB", DetectNZXTKrakenX3, NZXT_VID, NZXT_KRAKEN_X3_SERIES_RGB_PID); +REGISTER_HID_DETECTOR("NZXT Kraken 2024 ELITE Series RGB", DetectNZXTKrakenElite, NZXT_VID, NZXT_KRAKEN_2024_ELITE_SERIES_RGB_PID); +REGISTER_HID_DETECTOR("NZXT RGB Controller", DetectNZXTRGBController, NZXT_VID, NZXT_RGB_CONTROLLER_1_PID); +REGISTER_HID_DETECTOR("NZXT RGB Controller", DetectNZXTRGBController, NZXT_VID, NZXT_RGB_CONTROLLER_2_PID); +REGISTER_HID_DETECTOR("NZXT Smart Device V2", DetectNZXTSmartDeviceV2, NZXT_VID, NZXT_SMART_DEVICE_V2_PID); +REGISTER_HID_DETECTOR("NZXT Smart Device V2", DetectNZXTSmartDeviceV2, NZXT_VID, NZXT_SMART_DEVICE_V2_1_PID); +REGISTER_HID_DETECTOR("NZXT Smart Device V2", DetectNZXTSmartDeviceV2, NZXT_VID, NZXT_SMART_DEVICE_V2_2_PID); diff --git a/Controllers/NZXTHue2Controller/RGBController_NZXTHue2.cpp b/Controllers/NZXTHue2Controller/RGBController_NZXTHue2.cpp new file mode 100644 index 0000000..14039ed --- /dev/null +++ b/Controllers/NZXTHue2Controller/RGBController_NZXTHue2.cpp @@ -0,0 +1,443 @@ +/*---------------------------------------------------------*\ +| RGBController_NZXTHue2.cpp | +| | +| RGBController for NZXT Hue 2 | +| | +| Adam Honse (calcprogrammer1@gmail.com) 29 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_NZXTHue2.h" + +/**------------------------------------------------------------------*\ + @name NZXT Hue2 + @category LEDStrip + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectNZXTFanController,DetectNZXTFanController6Channel,DetectNZXTHue2,DetectNZXTHue2Ambient,DetectNZXTHue2Motherboard,DetectNZXTKrakenX3,DetectNZXTRGBController,DetectNZXTSmartDeviceV2,DetectNZXTKrakenElite + @comment +\*-------------------------------------------------------------------*/ + +RGBController_NZXTHue2::RGBController_NZXTHue2(NZXTHue2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "NZXT"; + type = DEVICE_TYPE_LEDSTRIP; + description = "NZXT Hue 2 Device"; + version = controller->GetFirmwareVersion(); + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = HUE_2_MODE_FIXED; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Fading; + Fading.name = "Fading"; + Fading.value = HUE_2_MODE_FADING; + Fading.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Fading.speed_min = HUE_2_SPEED_SLOWEST; + Fading.speed_max = HUE_2_SPEED_FASTEST; + Fading.colors_min = 1; + Fading.colors_max = 8; + Fading.speed = HUE_2_SPEED_NORMAL; + Fading.color_mode = MODE_COLORS_MODE_SPECIFIC; + Fading.colors.resize(1); + modes.push_back(Fading); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = HUE_2_MODE_SPECTRUM; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + SpectrumCycle.speed_min = HUE_2_SPEED_SLOWEST; + SpectrumCycle.speed_max = HUE_2_SPEED_FASTEST; + SpectrumCycle.speed = HUE_2_SPEED_NORMAL; + SpectrumCycle.direction = MODE_DIRECTION_RIGHT; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = HUE_2_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Marquee.speed_min = HUE_2_SPEED_SLOWEST; + Marquee.speed_max = HUE_2_SPEED_FASTEST; + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.speed = HUE_2_SPEED_NORMAL; + Marquee.direction = MODE_DIRECTION_RIGHT; + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + Marquee.colors.resize(1); + modes.push_back(Marquee); + + mode CoverMarquee; + CoverMarquee.name = "Cover Marquee"; + CoverMarquee.value = HUE_2_MODE_COVER_MARQUEE; + CoverMarquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + CoverMarquee.speed_min = HUE_2_SPEED_SLOWEST; + CoverMarquee.speed_max = HUE_2_SPEED_FASTEST; + CoverMarquee.colors_min = 1; + CoverMarquee.colors_max = 8; + CoverMarquee.speed = HUE_2_SPEED_NORMAL; + CoverMarquee.direction = MODE_DIRECTION_RIGHT; + CoverMarquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + CoverMarquee.colors.resize(1); + modes.push_back(CoverMarquee); + + mode Alternating; + Alternating.name = "Alternating"; + Alternating.value = HUE_2_MODE_ALTERNATING; + Alternating.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Alternating.speed_min = HUE_2_SPEED_SLOWEST; + Alternating.speed_max = HUE_2_SPEED_FASTEST; + Alternating.colors_min = 1; + Alternating.colors_max = 2; + Alternating.speed = HUE_2_SPEED_NORMAL; + Alternating.direction = MODE_DIRECTION_RIGHT; + Alternating.color_mode = MODE_COLORS_MODE_SPECIFIC; + Alternating.colors.resize(1); + modes.push_back(Alternating); + + mode Pulsing; + Pulsing.name = "Pulsing"; + Pulsing.value = HUE_2_MODE_PULSING; + Pulsing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Pulsing.speed_min = HUE_2_SPEED_SLOWEST; + Pulsing.speed_max = HUE_2_SPEED_FASTEST; + Pulsing.colors_min = 1; + Pulsing.colors_max = 8; + Pulsing.speed = HUE_2_SPEED_NORMAL; + Pulsing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulsing.colors.resize(1) ; + modes.push_back(Pulsing); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = HUE_2_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = HUE_2_SPEED_SLOWEST; + Breathing.speed_max = HUE_2_SPEED_FASTEST; + Breathing.colors_min = 1; + Breathing.colors_max = 8; + Breathing.speed = HUE_2_SPEED_NORMAL; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize( 1); + modes.push_back(Breathing); + + mode Candle; + Candle.name = "Candle"; + Candle.value = HUE_2_MODE_CANDLE; + Candle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Candle.speed_min = HUE_2_SPEED_SLOWEST; + Candle.speed_max = HUE_2_SPEED_FASTEST; + Candle.colors_min = 1; + Candle.colors_max = 8; + Candle.speed = HUE_2_SPEED_NORMAL; + Candle.color_mode = MODE_COLORS_MODE_SPECIFIC; + Candle.colors.resize(1) ; + modes.push_back(Candle); + + mode StarryNight; + StarryNight.name = "Starry Night"; + StarryNight.value = HUE_2_MODE_STARRY_NIGHT; + StarryNight.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + StarryNight.speed_min = HUE_2_SPEED_SLOWEST; + StarryNight.speed_max = HUE_2_SPEED_FASTEST; + StarryNight.colors_min = 1; + StarryNight.colors_max = 1; + StarryNight.speed = HUE_2_SPEED_NORMAL; + StarryNight.color_mode = MODE_COLORS_MODE_SPECIFIC; + StarryNight.colors.resize(1); + modes.push_back(StarryNight); + + mode SuperRainbow; + SuperRainbow.name = "Super Rainbow"; + SuperRainbow.value = HUE_2_MODE_SUPER_RAINBOW; + SuperRainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + SuperRainbow.speed_min = HUE_2_SPEED_SLOWEST; + SuperRainbow.speed_max = HUE_2_SPEED_FASTEST; + SuperRainbow.speed = HUE_2_SPEED_NORMAL; + SuperRainbow.direction = MODE_DIRECTION_RIGHT; + SuperRainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(SuperRainbow); + + mode RainbowPulse; + RainbowPulse.name = "Rainbow Pulse"; + RainbowPulse.value = HUE_2_MODE_RAINBOW_PULSE; + RainbowPulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RainbowPulse.speed_min = HUE_2_SPEED_SLOWEST; + RainbowPulse.speed_max = HUE_2_SPEED_FASTEST; + RainbowPulse.speed = HUE_2_SPEED_NORMAL; + RainbowPulse.direction = MODE_DIRECTION_RIGHT; + RainbowPulse.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowPulse); + + mode RainbowFlow; + RainbowFlow.name = "Rainbow Flow"; + RainbowFlow.value = HUE_2_MODE_RAINBOW_FLOW; + RainbowFlow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RainbowFlow.speed_min = HUE_2_SPEED_SLOWEST; + RainbowFlow.speed_max = HUE_2_SPEED_FASTEST; + RainbowFlow.speed = HUE_2_SPEED_NORMAL; + RainbowFlow.direction = MODE_DIRECTION_RIGHT; + RainbowFlow.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowFlow); + + SetupZones(); +} + +RGBController_NZXTHue2::~RGBController_NZXTHue2() +{ + delete controller; +} + +void RGBController_NZXTHue2::SetupZones() +{ + /*-------------------------------------------------*\ + | Set up zones | + \*-------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < controller->GetNumRGBChannels(); zone_idx++) + { + zone* new_zone = new zone; + + new_zone->name = "Hue 2 Channel "; + new_zone->name.append(std::to_string(zone_idx + 1)); + new_zone->type = ZONE_TYPE_LINEAR; + new_zone->leds_min = 0; + new_zone->leds_max = 40; + new_zone->leds_count = controller->channel_leds[zone_idx]; + new_zone->matrix_map = NULL; + + zones.push_back(*new_zone); + } + + /*-------------------------------------------------*\ + | Set up LEDs | + \*-------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + new_led.name = "Hue 2 Channel "; + new_led.name.append(std::to_string(zone_idx + 1)); + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_idx + 1)); + new_led.value = zone_idx; + + leds.push_back(new_led); + } + } + + /*-------------------------------------------------*\ + | Set up segments | + \*-------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + unsigned int start_idx = 0; + + for(unsigned int dev_idx = 0; dev_idx < 6; dev_idx++) + { + std::string device_name = ""; + switch(controller->channel_dev_ids[zone_idx][dev_idx]) + { + case 0x01: //Hue 1 strip + device_name = "Hue 1 strip"; + break; + + case 0x02: //Aer 1 fan + device_name = "Aer 1 fan"; + break; + + case 0x04: //Hue 2 strip (10 LEDs) + device_name = "Hue 2 strip (10 LEDs)"; + break; + + case 0x05: //Hue 2 strip (8 LEDs) + device_name = "Hue 2 strip (8 LEDs)"; + break; + + case 0x06: //Hue 2 strip (6 LEDs) + device_name = "Hue 2 strip (6 LEDs)"; + break; + + case 0x08: //Hue 2 Cable Comb (14 LEDs) + device_name = "Hue 2 Cable Comb (14 LEDs)"; + break; + + case 0x09: //Hue 2 Underglow (300mm) (15 LEDs) + device_name = "Hue 2 Underglow (300mm) (15 LEDs)"; + break; + + case 0x0A: //Hue 2 Underglow (200mm) (10 LEDs) + device_name = "Hue 2 Underglow (200mm) (10 LEDs)"; + break; + + case 0x0B: //Aer 2 fan (120mm) + device_name = "Aer 2 fan (120mm)"; + break; + + case 0x0C: //Aer 2 fan (140mm) + device_name = "Aer 2 fan (140mm)"; + break; + + case 0x10: //Kraken X3 ring + device_name = "Kraken X3 ring"; + break; + + case 0x11: //Kraken X3 logo + device_name = "Kraken X3 logo"; + break; + + case 0x13: //F120 RGB fan (120mm) + device_name = "F120 fan (120mm)"; + break; + + case 0x14: //F140 RGB fan (140mm) + device_name = "F140 fan (140mm)"; + break; + + case 0x15: //F120 RGB Duo fan (120mm) + device_name = "F120 Duo fan (120mm)"; + break; + + case 0x16: //F140 RGB Duo fan (140mm) + device_name = "F140 Duo fan (140mm)"; + break; + + case 0x17: //F120 RGB Core fan (120mm) + device_name = "F120 Core fan (120mm)"; + break; + + case 0x18: //F140 RGB Core fan (140mm) + device_name = "F140 Core fan (140mm)"; + break; + + case 0x19: //F120 RGB Core fan case version (120mm) + device_name = "F120 Core fan case version (120mm)"; + break; + + case 0x1D: //F360 Core fan case version (360mm) + device_name = "F360 Core fan case version (360mm)"; + break; + + case 0x1E: //Kraken Elite Ring + device_name = "Kraken Elite Ring"; + break; + + case 0x1F: //F420 RGB + device_name = "F420 Core fan case version"; + break; + + default: + break; + } + + if(device_name != "") + { + segment new_segment; + new_segment.name = device_name; + new_segment.type = ZONE_TYPE_LINEAR; + new_segment.start_idx = start_idx; + new_segment.leds_count = controller->channel_dev_szs[zone_idx][dev_idx]; + + zones[zone_idx].segments.push_back(new_segment); + + start_idx += new_segment.leds_count; + } + } + } + + SetupColors(); +} + +void RGBController_NZXTHue2::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } + +} + +void RGBController_NZXTHue2::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} + +void RGBController_NZXTHue2::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_NZXTHue2::UpdateSingleLED(int led) +{ + unsigned int zone_idx = leds[led].value; + + controller->SetChannelLEDs(zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); +} + +void RGBController_NZXTHue2::DeviceUpdateMode() +{ + if(modes[active_mode].value == 0xFFFF) + { + DeviceUpdateLEDs(); + } + else + { + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + RGBColor* colors = NULL; + bool direction = false; + + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + direction = true; + } + + if(modes[active_mode].colors.size() > 0) + { + colors = &modes[active_mode].colors[0]; + } + + controller->SetChannelEffect + ( + (unsigned char)zone_idx, + modes[active_mode].value, + modes[active_mode].speed, + direction, + colors, + (unsigned int)modes[active_mode].colors.size() + ); + } + } +} diff --git a/Controllers/NZXTHue2Controller/RGBController_NZXTHue2.h b/Controllers/NZXTHue2Controller/RGBController_NZXTHue2.h new file mode 100644 index 0000000..a8570bb --- /dev/null +++ b/Controllers/NZXTHue2Controller/RGBController_NZXTHue2.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_NZXTHue2.h | +| | +| RGBController for NZXT Hue 2 | +| | +| Adam Honse (calcprogrammer1@gmail.com) 29 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "NZXTHue2Controller.h" + +class RGBController_NZXTHue2 : public RGBController +{ +public: + RGBController_NZXTHue2(NZXTHue2Controller* controller_ptr); + ~RGBController_NZXTHue2(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + NZXTHue2Controller* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/NZXTHuePlusController/NZXTHuePlusController.cpp b/Controllers/NZXTHuePlusController/NZXTHuePlusController.cpp new file mode 100644 index 0000000..327135b --- /dev/null +++ b/Controllers/NZXTHuePlusController/NZXTHuePlusController.cpp @@ -0,0 +1,242 @@ +/*---------------------------------------------------------*\ +| NZXTHuePlusController.cpp | +| | +| Driver for NZXT Hue Plus | +| | +| Adam Honse (calcprogrammer1@gmail.com) 27 Aug 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "NZXTHuePlusController.h" + +using namespace std::chrono_literals; + +HuePlusController::HuePlusController() +{ + +} + +HuePlusController::~HuePlusController() +{ + delete serialport; +} + +void HuePlusController::Initialize(char* port) +{ + port_name = port; + + serialport = new serial_port(port_name.c_str(), HUE_PLUS_BAUD); + + channel_leds[HUE_PLUS_CHANNEL_1_IDX] = GetLEDsOnChannel(HUE_PLUS_CHANNEL_1); + channel_leds[HUE_PLUS_CHANNEL_2_IDX] = GetLEDsOnChannel(HUE_PLUS_CHANNEL_2); +} + +std::string HuePlusController::GetLocation() +{ + return("COM: " + port_name); +} + +unsigned int HuePlusController::GetLEDsOnChannel(unsigned int channel) +{ + unsigned char serial_buf[] = + { + 0x8D, 0x00, 0x00, 0x00, 0x00 + }; + + unsigned int ret_val = 0; + + /*-----------------------------------------------------*\ + | Set channel in serial packet | + \*-----------------------------------------------------*/ + serial_buf[0x01] = channel; + + serialport->serial_flush_rx(); + serialport->serial_write((char *)serial_buf, 2); + + std::this_thread::sleep_for(50ms); + + int bytes_read = serialport->serial_read((char *)serial_buf, 5); + + if(bytes_read == 5) + { + if(serial_buf[3] == 0x01) + { + ret_val = serial_buf[4] * 8; + } + else + { + ret_val += serial_buf[4] * 10; + } + } + + return(ret_val); +} + +void HuePlusController::SetChannelEffect + ( + unsigned char channel, + unsigned char mode, + unsigned char speed, + bool direction, + RGBColor * colors, + unsigned int num_colors + ) +{ + unsigned char color_data[120]; + + /*-----------------------------------------------------*\ + | If mode requires no colors, send packet | + \*-----------------------------------------------------*/ + if(num_colors == 0) + { + /*-----------------------------------------------------*\ + | Send mode without color data | + \*-----------------------------------------------------*/ + SendPacket(channel, mode, direction, 0, speed, 0, NULL); + } + /*-----------------------------------------------------*\ + | If mode requires indexed colors, send color index | + | packets for each mode color | + \*-----------------------------------------------------*/ + else if(num_colors <= 8) + { + for(std::size_t color_idx = 0; color_idx < num_colors; color_idx++) + { + /*-----------------------------------------------------*\ + | Fill in color data (40 entries per color) | + \*-----------------------------------------------------*/ + for (std::size_t idx = 0; idx < 40; idx++) + { + int pixel_idx = (int)idx * 3; + RGBColor color = colors[color_idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send mode and color data | + \*-----------------------------------------------------*/ + SendPacket(channel, mode, direction, (unsigned char)color_idx, speed, 40, &color_data[0]); + } + } + /*-----------------------------------------------------*\ + | If mode requires per-LED colors, fill colors array | + \*-----------------------------------------------------*/ + else + { + /*-----------------------------------------------------*\ + | Fill in color data (up to 40 colors) | + \*-----------------------------------------------------*/ + for (std::size_t idx = 0; idx < num_colors; idx++) + { + int pixel_idx = (int)idx * 3; + RGBColor color = colors[idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send mode and color data | + \*-----------------------------------------------------*/ + SendPacket(channel, mode, direction, 0, speed, num_colors, &color_data[0]); + } +} + +void HuePlusController::SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors + ) +{ + unsigned char color_data[120]; + + /*-----------------------------------------------------*\ + | Fill in color data (up to 40 colors) | + \*-----------------------------------------------------*/ + for (std::size_t idx = 0; idx < num_colors; idx++) + { + int pixel_idx = (int)idx * 3; + RGBColor color = colors[idx]; + color_data[pixel_idx + 0x00] = RGBGetGValue(color); + color_data[pixel_idx + 0x01] = RGBGetRValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } + + /*-----------------------------------------------------*\ + | Send color data | + \*-----------------------------------------------------*/ + SendPacket(channel, HUE_PLUS_MODE_DIRECT, false, 0, 0, num_colors, &color_data[0]); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void HuePlusController::SendPacket + ( + unsigned char channel, + unsigned char mode, + bool direction, + unsigned char color_idx, + unsigned char speed, + unsigned char color_count, + unsigned char* color_data + ) +{ + unsigned char serial_buf[HUE_PLUS_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(serial_buf, 0x00, sizeof(serial_buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + serial_buf[0x00] = 0x4B; + + /*-----------------------------------------------------*\ + | Set channel in serial packet | + \*-----------------------------------------------------*/ + serial_buf[0x01] = channel + 1; + + /*-----------------------------------------------------*\ + | Set mode in serial packet | + \*-----------------------------------------------------*/ + serial_buf[0x02] = mode; + + /*-----------------------------------------------------*\ + | Set options bitfield in serial packet | + \*-----------------------------------------------------*/ + serial_buf[0x03] = 0; + serial_buf[0x03] |= direction ? ( 1 << 4 ) : 0; + + /*-----------------------------------------------------*\ + | Set color index and speed in serial packet | + \*-----------------------------------------------------*/ + serial_buf[0x04] = ( color_idx << 5 ) | speed; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + memcpy(&serial_buf[0x05], color_data, color_count * 3); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + serialport->serial_write((char *)serial_buf, HUE_PLUS_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Delay to allow Hue+ device to ready for next packet | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(5ms); +} diff --git a/Controllers/NZXTHuePlusController/NZXTHuePlusController.h b/Controllers/NZXTHuePlusController/NZXTHuePlusController.h new file mode 100644 index 0000000..e45c43f --- /dev/null +++ b/Controllers/NZXTHuePlusController/NZXTHuePlusController.h @@ -0,0 +1,114 @@ +/*---------------------------------------------------------*\ +| NZXTHuePlusController.h | +| | +| Driver for NZXT Hue Plus | +| | +| Adam Honse (calcprogrammer1@gmail.com) 27 Aug 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "serial_port.h" + +#ifndef TRUE +#define TRUE true +#define FALSE false +#endif + +#ifndef WIN32 +#define LPSTR char * +#define strtok_s strtok_r +#endif + +#define HUE_PLUS_BAUD 256000 +#define HUE_PLUS_PACKET_SIZE 125 + +enum +{ + HUE_PLUS_CHANNEL_BOTH = 0x00, /* Both channels */ + HUE_PLUS_CHANNEL_1 = 0x01, /* Channel 1 */ + HUE_PLUS_CHANNEL_2 = 0x02, /* Channel 2 */ + HUE_PLUS_NUM_CHANNELS = 0x02 /* Number of channels */ +}; + +enum +{ + HUE_PLUS_CHANNEL_1_IDX = 0x00, /* Channel 1 array index */ + HUE_PLUS_CHANNEL_2_IDX = 0x01, /* Channel 2 array index */ +}; + +enum +{ + HUE_PLUS_SPEED_SLOWEST = 0x00, /* Slowest speed */ + HUE_PLUS_SPEED_SLOW = 0x01, /* Slow speed */ + HUE_PLUS_SPEED_NORMAL = 0x02, /* Normal speed */ + HUE_PLUS_SPEED_FAST = 0x03, /* Fast speed */ + HUE_PLUS_SPEED_FASTEST = 0x04, /* Fastest speed */ +}; + +enum +{ + HUE_PLUS_MODE_FIXED = 0x00, /* Fixed colors mode */ + HUE_PLUS_MODE_FADING = 0x01, /* Fading mode */ + HUE_PLUS_MODE_SPECTRUM = 0x02, /* Spectrum cycle mode */ + HUE_PLUS_MODE_MARQUEE = 0x03, /* Marquee mode */ + HUE_PLUS_MODE_COVER_MARQUEE = 0x04, /* Cover marquee mode */ + HUE_PLUS_MODE_ALTERNATING = 0x05, /* Alternating mode */ + HUE_PLUS_MODE_PULSING = 0x06, /* Pulsing mode */ + HUE_PLUS_MODE_BREATHING = 0x07, /* Breathing mode */ + HUE_PLUS_MODE_ALERT = 0x08, /* Alert mode */ + HUE_PLUS_MODE_CANDLELIGHT = 0x09, /* Candlelight mode */ + HUE_PLUS_MODE_WINGS = 0x0C, /* Wings mode */ + HUE_PLUS_MODE_WAVE = 0x0D, /* Wave mode */ + HUE_PLUS_MODE_DIRECT = 0x0E, /* Direct mode */ +}; + +class HuePlusController +{ +public: + HuePlusController(); + ~HuePlusController(); + + void Initialize(char* port); + std::string GetLocation(); + unsigned int GetLEDsOnChannel(unsigned int channel); + + void SetChannelEffect + ( + unsigned char channel, + unsigned char mode, + unsigned char speed, + bool direction, + RGBColor * colors, + unsigned int num_colors + ); + + void SetChannelLEDs + ( + unsigned char channel, + RGBColor * colors, + unsigned int num_colors + ); + + unsigned int channel_leds[HUE_PLUS_NUM_CHANNELS]; + +private: + std::string port_name; + serial_port *serialport = nullptr; + + void SendPacket + ( + unsigned char channel, + unsigned char mode, + bool direction, + unsigned char color_idx, + unsigned char speed, + unsigned char color_count, + unsigned char* color_data + ); +}; diff --git a/Controllers/NZXTHuePlusController/NZXTHuePlusControllerDetect.cpp b/Controllers/NZXTHuePlusController/NZXTHuePlusControllerDetect.cpp new file mode 100644 index 0000000..b45b93e --- /dev/null +++ b/Controllers/NZXTHuePlusController/NZXTHuePlusControllerDetect.cpp @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| NZXTHuePlusControllerDetect.cpp | +| | +| Detector for NZXT Hue Plus | +| | +| Adam Honse (calcprogrammer1@gmail.com) 27 Aug 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "NZXTHuePlusController.h" +#include "RGBController_NZXTHuePlus.h" +#include "find_usb_serial_port.h" + +#define NZXT_HUE_PLUS_VID 0x04D8 +#define NZXT_HUE_PLUS_PID 0x00DF + +/******************************************************************************************\ +* * +* DetectNZXTHuePlusControllers * +* * +* Detect devices supported by the NZXTHuePlus driver * +* * +\******************************************************************************************/ + +void DetectNZXTHuePlusControllers() +{ + std::vector ports = find_usb_serial_port(NZXT_HUE_PLUS_VID, NZXT_HUE_PLUS_PID); + + for(unsigned int i = 0; i < ports.size(); i++) + { + if(*ports[i] != "") + { + HuePlusController* controller = new HuePlusController(); + controller->Initialize((char *)ports[i]->c_str()); + RGBController_HuePlus* rgb_controller = new RGBController_HuePlus(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} /* DetectHuePlusControllers() */ + +REGISTER_DETECTOR("NZXT Hue+", DetectNZXTHuePlusControllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("NZXT Hue+", DetectNZXTHuePlusControllers, 0x04D8, 0x00DF ) | +\*---------------------------------------------------------------------------------------------------------*/ diff --git a/Controllers/NZXTHuePlusController/RGBController_NZXTHuePlus.cpp b/Controllers/NZXTHuePlusController/RGBController_NZXTHuePlus.cpp new file mode 100644 index 0000000..96856bb --- /dev/null +++ b/Controllers/NZXTHuePlusController/RGBController_NZXTHuePlus.cpp @@ -0,0 +1,309 @@ +/*---------------------------------------------------------*\ +| RGBController_NZXTHuePlus.cpp | +| | +| RGBController for NZXT Hue Plus | +| | +| Adam Honse (calcprogrammer1@gmail.com) 20 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_NZXTHuePlus.h" + +/**------------------------------------------------------------------*\ + @name NZXT Hue+ + @category LEDStrip + @type Serial + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectNZXTHuePlusControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_HuePlus::RGBController_HuePlus(HuePlusController* controller_ptr) +{ + controller = controller_ptr; + + name = "NZXT Hue+"; + vendor = "NZXT"; + type = DEVICE_TYPE_LEDSTRIP; + description = "NZXT Hue+ Device"; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = HUE_PLUS_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Fading; + Fading.name = "Fading"; + Fading.value = HUE_PLUS_MODE_FADING; + Fading.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Fading.speed_min = HUE_PLUS_SPEED_SLOWEST; + Fading.speed_max = HUE_PLUS_SPEED_FASTEST; + Fading.colors_min = 1; + Fading.colors_max = 8; + Fading.speed = HUE_PLUS_SPEED_NORMAL; + Fading.color_mode = MODE_COLORS_MODE_SPECIFIC; + Fading.colors.resize(2); + modes.push_back(Fading); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = HUE_PLUS_MODE_SPECTRUM; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + SpectrumCycle.speed_min = HUE_PLUS_SPEED_SLOWEST; + SpectrumCycle.speed_max = HUE_PLUS_SPEED_FASTEST; + SpectrumCycle.speed = HUE_PLUS_SPEED_NORMAL; + SpectrumCycle.direction = MODE_DIRECTION_RIGHT; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = HUE_PLUS_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Marquee.speed_min = HUE_PLUS_SPEED_SLOWEST; + Marquee.speed_max = HUE_PLUS_SPEED_FASTEST; + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.speed = HUE_PLUS_SPEED_NORMAL; + Marquee.direction = MODE_DIRECTION_RIGHT; + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + Marquee.colors.resize(1); + modes.push_back(Marquee); + + mode CoverMarquee; + CoverMarquee.name = "Cover Marquee"; + CoverMarquee.value = HUE_PLUS_MODE_COVER_MARQUEE; + CoverMarquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + CoverMarquee.speed_min = HUE_PLUS_SPEED_SLOWEST; + CoverMarquee.speed_max = HUE_PLUS_SPEED_FASTEST; + CoverMarquee.colors_min = 1; + CoverMarquee.colors_max = 8; + CoverMarquee.speed = HUE_PLUS_SPEED_NORMAL; + CoverMarquee.direction = MODE_DIRECTION_RIGHT; + CoverMarquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + CoverMarquee.colors.resize(2); + modes.push_back(CoverMarquee); + + mode Alternating; + Alternating.name = "Alternating"; + Alternating.value = HUE_PLUS_MODE_ALTERNATING; + Alternating.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Alternating.speed_min = HUE_PLUS_SPEED_SLOWEST; + Alternating.speed_max = HUE_PLUS_SPEED_FASTEST; + Alternating.colors_min = 1; + Alternating.colors_max = 2; + Alternating.speed = HUE_PLUS_SPEED_NORMAL; + Alternating.direction = MODE_DIRECTION_RIGHT; + Alternating.color_mode = MODE_COLORS_MODE_SPECIFIC; + Alternating.colors.resize(2); + modes.push_back(Alternating); + + mode Pulsing; + Pulsing.name = "Pulsing"; + Pulsing.value = HUE_PLUS_MODE_PULSING; + Pulsing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Pulsing.speed_min = HUE_PLUS_SPEED_SLOWEST; + Pulsing.speed_max = HUE_PLUS_SPEED_FASTEST; + Pulsing.colors_min = 1; + Pulsing.colors_max = 8; + Pulsing.speed = HUE_PLUS_SPEED_NORMAL; + Pulsing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulsing.colors.resize(2); + modes.push_back(Pulsing); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = HUE_PLUS_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = HUE_PLUS_SPEED_SLOWEST; + Breathing.speed_max = HUE_PLUS_SPEED_FASTEST; + Breathing.colors_min = 1; + Breathing.colors_max = 8; + Breathing.speed = HUE_PLUS_SPEED_NORMAL; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(2); + modes.push_back(Breathing); + + mode Alert; + Alert.name = "Alert"; + Alert.value = HUE_PLUS_MODE_ALERT; + Alert.flags = 0; + Alert.color_mode = MODE_COLORS_NONE; + modes.push_back(Alert); + + mode Candlelight; + Candlelight.name = "Candlelight"; + Candlelight.value = HUE_PLUS_MODE_CANDLELIGHT; + Candlelight.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Candlelight.colors_min = 1; + Candlelight.colors_max = 1; + Candlelight.color_mode = MODE_COLORS_MODE_SPECIFIC; + Candlelight.colors.resize(1); + modes.push_back(Candlelight); + + mode Wings; + Wings.name = "Wings"; + Wings.value = HUE_PLUS_MODE_WINGS; + Wings.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Wings.speed_min = HUE_PLUS_SPEED_SLOWEST; + Wings.speed_max = HUE_PLUS_SPEED_FASTEST; + Wings.colors_min = 1; + Wings.colors_max = 1; + Wings.speed = HUE_PLUS_SPEED_NORMAL; + Wings.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wings.colors.resize(1); + modes.push_back(Wings); + + mode Wave; + Wave.name = "Wave"; + Wave.value = HUE_PLUS_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Wave.speed_min = HUE_PLUS_SPEED_SLOWEST; + Wave.speed_max = HUE_PLUS_SPEED_FASTEST; + Wave.speed = HUE_PLUS_SPEED_NORMAL; + Wave.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Wave); + + SetupZones(); +} + +RGBController_HuePlus::~RGBController_HuePlus() +{ + delete controller; +} + +void RGBController_HuePlus::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(HUE_PLUS_NUM_CHANNELS); + + /*-------------------------------------------------*\ + | Set up zones | + \*-------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < HUE_PLUS_NUM_CHANNELS; zone_idx++) + { + zones[zone_idx].name = "Hue+ Channel "; + zones[zone_idx].name.append(std::to_string(zone_idx + 1)); + zones[zone_idx].type = ZONE_TYPE_LINEAR; + zones[zone_idx].leds_min = 0; + zones[zone_idx].leds_max = 40; + zones[zone_idx].matrix_map = NULL; + + if(first_run) + { + zones[zone_idx].leds_count = controller->channel_leds[zone_idx]; + } + } + + /*-------------------------------------------------*\ + | Set up LEDs | + \*-------------------------------------------------*/ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(unsigned int led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led new_led; + new_led.name = "Hue+ Channel "; + new_led.name.append(std::to_string(zone_idx + 1)); + new_led.name.append(", LED "); + new_led.name.append(std::to_string(led_idx + 1)); + new_led.value = zone_idx; + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_HuePlus::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_HuePlus::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} + +void RGBController_HuePlus::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_HuePlus::UpdateSingleLED(int led) +{ + unsigned int zone_idx = leds[led].value; + + controller->SetChannelLEDs(zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); +} + +void RGBController_HuePlus::DeviceUpdateMode() +{ + if(modes[active_mode].value == HUE_PLUS_MODE_FIXED) + { + DeviceUpdateLEDs(); + } + else + { + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + RGBColor* colors = NULL; + bool direction = false; + + if(modes[active_mode].direction == MODE_DIRECTION_LEFT) + { + direction = true; + } + + if(modes[active_mode].colors.size() > 0) + { + colors = &modes[active_mode].colors[0]; + } + + controller->SetChannelEffect + ( + (unsigned char)zone_idx, + modes[active_mode].value, + modes[active_mode].speed, + direction, + colors, + (unsigned int)modes[active_mode].colors.size() + ); + } + } +} diff --git a/Controllers/NZXTHuePlusController/RGBController_NZXTHuePlus.h b/Controllers/NZXTHuePlusController/RGBController_NZXTHuePlus.h new file mode 100644 index 0000000..b015dbb --- /dev/null +++ b/Controllers/NZXTHuePlusController/RGBController_NZXTHuePlus.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_NZXTHuePlus.h | +| | +| RGBController for NZXT Hue Plus | +| | +| Adam Honse (calcprogrammer1@gmail.com) 20 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "serial_port.h" +#include "NZXTHuePlusController.h" + +class RGBController_HuePlus : public RGBController +{ +public: + RGBController_HuePlus(HuePlusController* controller_ptr); + ~RGBController_HuePlus(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + HuePlusController* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/NZXTKrakenController/NZXTKrakenController.cpp b/Controllers/NZXTKrakenController/NZXTKrakenController.cpp new file mode 100644 index 0000000..0186e5a --- /dev/null +++ b/Controllers/NZXTKrakenController/NZXTKrakenController.cpp @@ -0,0 +1,204 @@ +/*---------------------------------------------------------*\ +| NZXTKrakenController.cpp | +| | +| Driver for NZXT Kraken | +| | +| Martin Hartl (inlart) 04 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "NZXTKrakenController.h" +#include "StringUtils.h" + +static void SetColor(const std::vector& colors, unsigned char* color_data) +{ + for(std::size_t idx = 0; idx < colors.size(); idx++) + { + int pixel_idx = (int)idx * 3; + RGBColor color = colors[idx]; + color_data[pixel_idx + 0x00] = RGBGetRValue(color); + color_data[pixel_idx + 0x01] = RGBGetGValue(color); + color_data[pixel_idx + 0x02] = RGBGetBValue(color); + } +} + +static RGBColor ToLogoColor(RGBColor rgb) +{ + return ToRGBColor(RGBGetGValue(rgb), RGBGetRValue(rgb), RGBGetBValue(rgb)); +} + +NZXTKrakenController::NZXTKrakenController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + /*-----------------------------------------------------*\ + | Get the firmware version | + \*-----------------------------------------------------*/ + UpdateStatus(); +} + +NZXTKrakenController::~NZXTKrakenController() +{ + hid_close(dev); +} + +std::string NZXTKrakenController::GetFirmwareVersion() +{ + return firmware_version; +} + +std::string NZXTKrakenController::GetLocation() +{ + return("HID: " + location); +} + +std::string NZXTKrakenController::GetName() +{ + return(name); +} + +std::string NZXTKrakenController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void NZXTKrakenController::UpdateStatus() +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Read packet | + \*-----------------------------------------------------*/ + hid_read(dev, usb_buf, 64); + + /*-----------------------------------------------------*\ + | Extract cooler information | + \*-----------------------------------------------------*/ + liquid_temperature = usb_buf[0x1] + (usb_buf[0x2] * 0.1); + fan_speed = usb_buf[0x3] << 8 | usb_buf[0x4]; + pump_speed = usb_buf[0x5] << 8 | usb_buf[0x6]; + + /*-----------------------------------------------------*\ + | Extract firmware version | + \*-----------------------------------------------------*/ + int major = usb_buf[0xb]; + int minor = usb_buf[0xc] << 8 | usb_buf[0xd]; + int patch = usb_buf[0xe]; + std::stringstream ss; + ss << major << '.' << minor << '.' << patch; + firmware_version = ss.str(); +} + +void NZXTKrakenController::UpdateEffect + ( + NZXTKrakenChannel_t channel, + unsigned char mode, + bool direction, + unsigned char speed, + int seq, + std::vector colors + ) +{ + unsigned char color_data[9 * 3]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(color_data, 0, sizeof(color_data)); + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + if(!colors.empty() && channel != NZXT_KRAKEN_CHANNEL_RING) + { + colors[0] = ToLogoColor(colors[0]); + } + + /*-----------------------------------------------------*\ + | Update color data | + \*-----------------------------------------------------*/ + SetColor(colors, color_data); + + /*-----------------------------------------------------*\ + | Send update packet | + \*-----------------------------------------------------*/ + SendEffect(channel, mode, direction, color_data, speed, false, seq); +} + +void NZXTKrakenController::SendEffect + ( + unsigned char channel, + unsigned char mode, + bool direction, + unsigned char* color_data, + unsigned char speed /* = 0x02 */, + bool movement /* = false */, + int cis /* = 0 */, + int size /* = 0 */ + ) +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set effect mode | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x02; + usb_buf[0x01] = 0x4c; + + /*-----------------------------------------------------*\ + | Set effect channel, movement and direction | + \*-----------------------------------------------------*/ + usb_buf[0x02] = channel; + usb_buf[0x02] |= movement ? ( 1 << 3 ) : 0; + usb_buf[0x02] |= direction ? ( 1 << 4 ) : 0; + + /*-----------------------------------------------------*\ + | Set mode | + \*-----------------------------------------------------*/ + usb_buf[0x03] = mode; + + /*-----------------------------------------------------*\ + | Set effect speed, size and color in set | + \*-----------------------------------------------------*/ + usb_buf[0x04] = speed; + usb_buf[0x04] |= size << 3; + usb_buf[0x04] |= cis << 5; + + /*-----------------------------------------------------*\ + | Copy color data bytes | + \*-----------------------------------------------------*/ + if(color_data) + { + memcpy(usb_buf + 0x05, color_data, 9 * 3); + } + + /*-----------------------------------------------------*\ + | Send effect | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 64); +} diff --git a/Controllers/NZXTKrakenController/NZXTKrakenController.h b/Controllers/NZXTKrakenController/NZXTKrakenController.h new file mode 100644 index 0000000..7d1e22c --- /dev/null +++ b/Controllers/NZXTKrakenController/NZXTKrakenController.h @@ -0,0 +1,96 @@ +/*---------------------------------------------------------*\ +| NZXTKrakenController.h | +| | +| Driver for NZXT Kraken | +| | +| Martin Hartl (inlart) 04 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +enum NZXTKrakenChannel_t +{ + NZXT_KRAKEN_CHANNEL_SYNC = 0x00, /* Sync Channel */ + NZXT_KRAKEN_CHANNEL_LOGO = 0x01, /* Logo Channel */ + NZXT_KRAKEN_CHANNEL_RING = 0x02, /* Ring Channel */ +}; + +enum +{ + NZXT_KRAKEN_MODE_FIXED = 0x00, /* Fixed colors mode */ + NZXT_KRAKEN_MODE_FADING = 0x01, /* Fading mode */ + NZXT_KRAKEN_MODE_SPECTRUM = 0x02, /* Spectrum cycle mode */ + NZXT_KRAKEN_MODE_MARQUEE = 0x03, /* Marquee mode */ + NZXT_KRAKEN_MODE_COVER_MARQUEE = 0x04, /* Cover marquee mode */ + NZXT_KRAKEN_MODE_ALTERNATING = 0x05, /* Alternating mode */ + NZXT_KRAKEN_MODE_BREATHING = 0x06, /* Breathing mode */ + NZXT_KRAKEN_MODE_PULSE = 0x07, /* Pulse mode */ + NZXT_KRAKEN_MODE_TAI_CHI = 0x08, /* Tai Chi mode */ + NZXT_KRAKEN_MODE_WATER_COOLER = 0x09, /* Water color mode */ + NZXT_KRAKEN_MODE_LOADING = 0x0a, /* Loading mode */ + NZXT_KRAKEN_MODE_WINGS = 0x0c, /* Wings mode */ +}; + +enum +{ + NZXT_KRAKEN_SPEED_SLOWEST = 0x00, /* Slowest speed */ + NZXT_KRAKEN_SPEED_SLOW = 0x01, /* Slow speed */ + NZXT_KRAKEN_SPEED_NORMAL = 0x02, /* Normal speed */ + NZXT_KRAKEN_SPEED_FAST = 0x03, /* Fast speed */ + NZXT_KRAKEN_SPEED_FASTEST = 0x04, /* Fastest speed */ +}; + +class NZXTKrakenController +{ +public: + NZXTKrakenController(hid_device* dev_handle, const char* path, std::string dev_name); + ~NZXTKrakenController(); + + std::string GetFirmwareVersion(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + + void UpdateEffect + ( + NZXTKrakenChannel_t channel, + unsigned char mode, + bool direction, + unsigned char speed, + int seq, + std::vector colors + ); + +private: + void UpdateStatus(); + + void SendEffect + ( + unsigned char channel, + unsigned char mode, + bool direction, + unsigned char* color_data, + unsigned char speed = 0x02, + bool movement = false, + int cis = 0 , + int size = 0 + ); + + hid_device* dev; + + // -- status + std::string firmware_version; + double liquid_temperature; + std::string location; + std::string name; + unsigned int fan_speed; + unsigned int pump_speed; +}; diff --git a/Controllers/NZXTKrakenController/NZXTKrakenControllerDetect.cpp b/Controllers/NZXTKrakenController/NZXTKrakenControllerDetect.cpp new file mode 100644 index 0000000..78df3f8 --- /dev/null +++ b/Controllers/NZXTKrakenController/NZXTKrakenControllerDetect.cpp @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| NZXTKrakenControllerDetect.cpp | +| | +| Detector for NZXT Kraken | +| | +| Martin Hartl (inlart) 04 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "NZXTKrakenController.h" +#include "RGBController_NZXTKraken.h" + +#define NZXT_KRAKEN_VID 0x1E71 +#define NZXT_KRAKEN_X2_PID 0x170E +#define NZXT_KRAKEN_M2_PID 0x1715 + +/******************************************************************************************\ +* * +* DetectNZXTKrakenControllers * +* * +* Detect devices supported by the NZXTKraken driver * +* * +\******************************************************************************************/ + +void DetectNZXTKrakenControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + NZXTKrakenController* controller = new NZXTKrakenController(dev, info->path, name); + RGBController_NZXTKraken* rgb_controller = new RGBController_NZXTKraken(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectNZXTKrakenControllers() */ + +REGISTER_HID_DETECTOR("NZXT Kraken X2", DetectNZXTKrakenControllers, NZXT_KRAKEN_VID, NZXT_KRAKEN_X2_PID); +REGISTER_HID_DETECTOR("NZXT Kraken M2", DetectNZXTKrakenControllers, NZXT_KRAKEN_VID, NZXT_KRAKEN_M2_PID); diff --git a/Controllers/NZXTKrakenController/RGBController_NZXTKraken.cpp b/Controllers/NZXTKrakenController/RGBController_NZXTKraken.cpp new file mode 100644 index 0000000..bb14600 --- /dev/null +++ b/Controllers/NZXTKrakenController/RGBController_NZXTKraken.cpp @@ -0,0 +1,362 @@ +/*---------------------------------------------------------*\ +| RGBController_NZXTKraken.cpp | +| | +| RGBController for NZXT Kraken | +| | +| Martin Hartl (inlart) 04 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_NZXTKraken.h" + +/**------------------------------------------------------------------*\ + @name NZXT Kraken + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectNZXTKrakenControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_NZXTKraken::RGBController_NZXTKraken(NZXTKrakenController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "NZXT"; + type = DEVICE_TYPE_COOLER; + description = "NZXT Kraken X42/X52/X62/X72/M22"; + version = controller->GetFirmwareVersion(); + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = NZXT_KRAKEN_MODE_FIXED; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Fading; + Fading.name = "Fading"; + Fading.value = NZXT_KRAKEN_MODE_FADING; + Fading.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Fading.speed_min = NZXT_KRAKEN_SPEED_SLOWEST; + Fading.speed_max = NZXT_KRAKEN_SPEED_FASTEST; + Fading.colors_min = 2; + Fading.colors_max = 8; + Fading.speed = NZXT_KRAKEN_SPEED_NORMAL; + Fading.color_mode = MODE_COLORS_MODE_SPECIFIC; + Fading.colors.resize(2); + modes.push_back(Fading); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = NZXT_KRAKEN_MODE_SPECTRUM; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + SpectrumCycle.speed_min = NZXT_KRAKEN_SPEED_SLOWEST; + SpectrumCycle.speed_max = NZXT_KRAKEN_SPEED_FASTEST; + SpectrumCycle.speed = NZXT_KRAKEN_SPEED_NORMAL; + SpectrumCycle.direction = MODE_DIRECTION_RIGHT; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = NZXT_KRAKEN_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Marquee.speed_min = NZXT_KRAKEN_SPEED_SLOWEST; + Marquee.speed_max = NZXT_KRAKEN_SPEED_FASTEST; + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.speed = NZXT_KRAKEN_SPEED_NORMAL; + Marquee.direction = MODE_DIRECTION_RIGHT; + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + Marquee.colors.resize(1); + modes.push_back(Marquee); + + mode CoverMarquee; + CoverMarquee.name = "Cover Marquee"; + CoverMarquee.value = NZXT_KRAKEN_MODE_COVER_MARQUEE; + CoverMarquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + CoverMarquee.speed_min = NZXT_KRAKEN_SPEED_SLOWEST; + CoverMarquee.speed_max = NZXT_KRAKEN_SPEED_FASTEST; + CoverMarquee.colors_min = 1; + CoverMarquee.colors_max = 8; + CoverMarquee.speed = NZXT_KRAKEN_SPEED_NORMAL; + CoverMarquee.direction = MODE_DIRECTION_RIGHT; + CoverMarquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + CoverMarquee.colors.resize(2); + modes.push_back(CoverMarquee); + + mode Alternating; + Alternating.name = "Alternating"; + Alternating.value = NZXT_KRAKEN_MODE_ALTERNATING; + Alternating.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Alternating.speed_min = NZXT_KRAKEN_SPEED_SLOWEST; + Alternating.speed_max = NZXT_KRAKEN_SPEED_FASTEST; + Alternating.colors_min = 2; + Alternating.colors_max = 2; + Alternating.speed = NZXT_KRAKEN_SPEED_NORMAL; + Alternating.direction = MODE_DIRECTION_RIGHT; + Alternating.color_mode = MODE_COLORS_MODE_SPECIFIC; + Alternating.colors.resize(2); + modes.push_back(Alternating); + + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = NZXT_KRAKEN_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Pulse.speed_min = NZXT_KRAKEN_SPEED_SLOWEST; + Pulse.speed_max = NZXT_KRAKEN_SPEED_FASTEST; + Pulse.colors_min = 1; + Pulse.colors_max = 8; + Pulse.speed = NZXT_KRAKEN_SPEED_NORMAL; + Pulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + Pulse.colors.resize(1); + modes.push_back(Pulse); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = NZXT_KRAKEN_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = NZXT_KRAKEN_SPEED_SLOWEST; + Breathing.speed_max = NZXT_KRAKEN_SPEED_FASTEST; + Breathing.colors_min = 1; + Breathing.colors_max = 8; + Breathing.speed = NZXT_KRAKEN_SPEED_NORMAL; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode ThaiChi; + ThaiChi.name = "Thai Chi"; + ThaiChi.value = NZXT_KRAKEN_MODE_TAI_CHI; + ThaiChi.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + ThaiChi.speed_min = NZXT_KRAKEN_SPEED_SLOWEST; + ThaiChi.speed_max = NZXT_KRAKEN_SPEED_FASTEST; + ThaiChi.speed = NZXT_KRAKEN_SPEED_NORMAL; + ThaiChi.colors_min = 2; + ThaiChi.colors_max = 2; + ThaiChi.color_mode = MODE_COLORS_MODE_SPECIFIC; + ThaiChi.colors.resize(2); + modes.push_back(ThaiChi); + + mode WaterCooler; + WaterCooler.name = "Water Cooler"; + WaterCooler.value = NZXT_KRAKEN_MODE_WATER_COOLER; + WaterCooler.flags = MODE_FLAG_HAS_SPEED; + WaterCooler.speed_min = NZXT_KRAKEN_SPEED_SLOWEST; + WaterCooler.speed_max = NZXT_KRAKEN_SPEED_FASTEST; + WaterCooler.speed = NZXT_KRAKEN_SPEED_NORMAL; + WaterCooler.color_mode = MODE_COLORS_NONE; + modes.push_back(WaterCooler); + + mode Loading; + Loading.name = "Loading"; + Loading.value = NZXT_KRAKEN_MODE_LOADING; + Loading.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Loading.colors_min = 1; + Loading.colors_max = 1; + Loading.color_mode = MODE_COLORS_MODE_SPECIFIC; + Loading.colors.resize(1); + modes.push_back(Loading); + + mode Wings; + Wings.name = "Wings"; + Wings.value = NZXT_KRAKEN_MODE_WINGS; + Wings.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Wings.speed_min = NZXT_KRAKEN_SPEED_SLOWEST; + Wings.speed_max = NZXT_KRAKEN_SPEED_FASTEST; + Wings.speed = NZXT_KRAKEN_SPEED_NORMAL; + Wings.colors_min = 1; + Wings.colors_max = 1; + Wings.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wings.colors.resize(1); + modes.push_back(Wings); + + /*---------------------------------------------------------*\ + | Fixed is the default mode | + \*---------------------------------------------------------*/ + default_mode = 0; + + /*---------------------------------------------------------*\ + | Modes supported by the LOGO LED | + \*---------------------------------------------------------*/ + logo_modes = + { + NZXT_KRAKEN_MODE_FIXED, + NZXT_KRAKEN_MODE_FADING, + NZXT_KRAKEN_MODE_SPECTRUM, + NZXT_KRAKEN_MODE_BREATHING, + NZXT_KRAKEN_MODE_PULSE + }; + + SetupZones(); +} + +RGBController_NZXTKraken::~RGBController_NZXTKraken() +{ + delete controller; +} + +void RGBController_NZXTKraken::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + zone logo_zone; + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + zone ring_zone; + ring_zone.name = "Ring"; + ring_zone.type = ZONE_TYPE_LINEAR; + ring_zone.leds_min = 8; + ring_zone.leds_max = 8; + ring_zone.leds_count = 8; + ring_zone.matrix_map = NULL; + zones.push_back(ring_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + led logo_led; + logo_led.name = "Logo LED"; + leds.push_back(logo_led); + + led ring_led; + for(int i = 1; i < 9; i++) + { + ring_led.name = std::string("Ring LED ") + std::to_string(i); + leds.push_back(ring_led); + } + + SetupColors(); +} + +void RGBController_NZXTKraken::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +std::vector> RGBController_NZXTKraken::GetColors(int zone, const mode& channel_mode) +{ + std::vector> result; + int length = zone < 0 ? (int)leds.size() : (int)zones[zone].leds_count; + + if(channel_mode.color_mode == MODE_COLORS_NONE) + { + result.push_back(std::vector()); + } + else if(channel_mode.color_mode == MODE_COLORS_PER_LED) + { + if(zone < 0) + { + result.push_back(colors); + } + else + { + std::vector led_colors; + for(std::size_t idx = 0; idx < zones[zone].leds_count; ++idx) + { + led_colors.push_back(zones[zone].colors[idx]); + } + result.push_back(led_colors); + } + } + else if(channel_mode.color_mode == MODE_COLORS_MODE_SPECIFIC) + { + for(std::size_t idx = 0; idx < channel_mode.colors.size(); ++idx) + { + result.push_back(std::vector(length, channel_mode.colors[idx])); + } + } + + return result; +} + +void RGBController_NZXTKraken::UpdateChannel(NZXTKrakenChannel_t channel, int zone, const mode& channel_mode) +{ + bool direction = false; + + if((channel_mode.flags & MODE_FLAG_HAS_DIRECTION_LR) + &&(channel_mode.direction == MODE_DIRECTION_LEFT )) + { + direction = true; + } + + unsigned char speed = NZXT_KRAKEN_SPEED_NORMAL; + if(channel_mode.flags & MODE_FLAG_HAS_SPEED) + { + speed = channel_mode.speed; + } + + std::vector> update_colors = GetColors(zone, channel_mode); + for(std::size_t idx = 0; idx < update_colors.size(); ++idx) + { + controller->UpdateEffect( + channel, + channel_mode.value, + direction, + speed, + (int)idx, + update_colors[idx] + ); + } +} + +void RGBController_NZXTKraken::DeviceUpdateLEDs() +{ + if(logo_modes.find(modes[active_mode].value) == logo_modes.end()) + { + UpdateChannel(NZXT_KRAKEN_CHANNEL_LOGO, 0, modes[default_mode]); + UpdateChannel(NZXT_KRAKEN_CHANNEL_RING, 1, modes[active_mode]); + } + else + { + UpdateChannel(NZXT_KRAKEN_CHANNEL_SYNC, -1, modes[active_mode]); + } +} + +void RGBController_NZXTKraken::UpdateZoneLEDs(int zone) +{ + NZXTKrakenChannel_t channel; + mode channel_mode = modes[active_mode]; + if(zone == 0) + { + channel = NZXT_KRAKEN_CHANNEL_LOGO; + if(logo_modes.find(modes[active_mode].value) == logo_modes.end()) + { + channel_mode = modes[default_mode]; + } + } + else + { + channel = NZXT_KRAKEN_CHANNEL_RING; + } + UpdateChannel(channel, zone, channel_mode); +} + +void RGBController_NZXTKraken::UpdateSingleLED(int led) +{ + int zone = (led > 0) ? 1 : 0; + UpdateZoneLEDs(zone); +} + +void RGBController_NZXTKraken::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/NZXTKrakenController/RGBController_NZXTKraken.h b/Controllers/NZXTKrakenController/RGBController_NZXTKraken.h new file mode 100644 index 0000000..ac31b1a --- /dev/null +++ b/Controllers/NZXTKrakenController/RGBController_NZXTKraken.h @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| RGBController_NZXTKraken.h | +| | +| RGBController for NZXT Kraken | +| | +| Martin Hartl (inlart) 04 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "NZXTKrakenController.h" + +class RGBController_NZXTKraken : public RGBController +{ +public: + RGBController_NZXTKraken(NZXTKrakenController* controller_ptr); + ~RGBController_NZXTKraken(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + std::vector> GetColors + ( + int zone, + const mode& channel_mode + ); + + void UpdateChannel + ( + NZXTKrakenChannel_t channel, + int zone, + const mode& channel_mode + ); + + NZXTKrakenController* controller; + std::set logo_modes; + int default_mode; +}; diff --git a/Controllers/NZXTMouseController/NZXTMouseController.cpp b/Controllers/NZXTMouseController/NZXTMouseController.cpp new file mode 100644 index 0000000..9db6606 --- /dev/null +++ b/Controllers/NZXTMouseController/NZXTMouseController.cpp @@ -0,0 +1,144 @@ +/*---------------------------------------------------------*\ +| NZXTMouseController.cpp | +| | +| Driver for NZXT Mouse | +| | +| Adam Honse (calcprogrammer1@gmail.com) 13 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "NZXTMouseController.h" +#include "StringUtils.h" + +NZXTMouseController::NZXTMouseController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + /*-----------------------------------------------------*\ + | Request firmware version | + \*-----------------------------------------------------*/ + SendFirmwareRequest(); +} + +NZXTMouseController::~NZXTMouseController() +{ + hid_close(dev); +} + +std::string NZXTMouseController::GetFirmwareVersion() +{ + return(firmware_version); +} + +std::string NZXTMouseController::GetLocation() +{ + return("HID: " + location); +} + +std::string NZXTMouseController::GetName() +{ + return(name); +} + +std::string NZXTMouseController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void NZXTMouseController::SetLEDs + ( + RGBColor * colors + ) +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x43; + usb_buf[0x01] = 0xAE; + usb_buf[0x03] = 0x10; + usb_buf[0x04] = 0x02; + usb_buf[0x05] = 0x3F; + + usb_buf[24] = 0x06; + + usb_buf[25] = RGBGetRValue(colors[2]); + usb_buf[26] = RGBGetGValue(colors[2]); + usb_buf[27] = RGBGetBValue(colors[2]); + + usb_buf[29] = RGBGetRValue(colors[1]); + usb_buf[30] = RGBGetGValue(colors[1]); + usb_buf[31] = RGBGetBValue(colors[1]); + + usb_buf[33] = RGBGetRValue(colors[0]); + usb_buf[34] = RGBGetGValue(colors[0]); + usb_buf[35] = RGBGetBValue(colors[0]); + + usb_buf[37] = RGBGetRValue(colors[3]); + usb_buf[38] = RGBGetGValue(colors[3]); + usb_buf[39] = RGBGetBValue(colors[3]); + + usb_buf[41] = RGBGetRValue(colors[4]); + usb_buf[42] = RGBGetGValue(colors[4]); + usb_buf[43] = RGBGetBValue(colors[4]); + + usb_buf[45] = RGBGetRValue(colors[5]); + usb_buf[46] = RGBGetGValue(colors[5]); + usb_buf[47] = RGBGetBValue(colors[5]); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, sizeof(usb_buf)); +} + +void NZXTMouseController::SendFirmwareRequest() +{ + unsigned char usb_buf[64]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Firmware Request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x43; + usb_buf[0x01] = 0x81; + usb_buf[0x03] = 0x01; + + hid_write(dev, usb_buf, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Receive packets until 0x43 0x86 is received | + \*-----------------------------------------------------*/ + do + { + hid_read(dev, usb_buf, sizeof(usb_buf)); + } while( (usb_buf[0] != 0x43) || (usb_buf[1] != 0x86) ); + + /*-----------------------------------------------------*\ + | Format firmware version string | + \*-----------------------------------------------------*/ + snprintf(firmware_version, 16, "%u.%u.%u", usb_buf[0x03], usb_buf[0x04], usb_buf[0x05]); +} diff --git a/Controllers/NZXTMouseController/NZXTMouseController.h b/Controllers/NZXTMouseController/NZXTMouseController.h new file mode 100644 index 0000000..b7fb5d3 --- /dev/null +++ b/Controllers/NZXTMouseController/NZXTMouseController.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| NZXTMouseController.h | +| | +| Driver for NZXT Mouse | +| | +| Adam Honse (calcprogrammer1@gmail.com) 13 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +class NZXTMouseController +{ +public: + NZXTMouseController(hid_device* dev_handle, const char* path, std::string dev_name); + ~NZXTMouseController(); + + std::string GetFirmwareVersion(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + + void SetLEDs + ( + RGBColor * colors + ); + +private: + hid_device* dev; + + char firmware_version[16]; + std::string location; + std::string name; + + void SendFirmwareRequest(); +}; diff --git a/Controllers/NZXTMouseController/NZXTMouseControllerDetect.cpp b/Controllers/NZXTMouseController/NZXTMouseControllerDetect.cpp new file mode 100644 index 0000000..4426bc3 --- /dev/null +++ b/Controllers/NZXTMouseController/NZXTMouseControllerDetect.cpp @@ -0,0 +1,44 @@ +/*---------------------------------------------------------*\ +| NZXTMouseControllerDetect.cpp | +| | +| Detector for NZXT Mouse | +| | +| Adam Honse (calcprogrammer1@gmail.com) 13 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "NZXTMouseController.h" +#include "RGBController_NZXTMouse.h" + +/*-----------------------------------------------------*\ +| NZXT USB IDs | +\*-----------------------------------------------------*/ +#define NZXT_VID 0x1E71 +#define NZXT_LIFT_PID 0x2100 + +/******************************************************************************************\ +* * +* DetectNZXTMouseControllers * +* * +* Detect devices supported by the NZXTMouse driver * +* * +\******************************************************************************************/ + +static void DetectNZXTMouseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + NZXTMouseController* controller = new NZXTMouseController(dev, info->path, name); + RGBController_NZXTMouse* rgb_controller = new RGBController_NZXTMouse(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("NZXT Lift", DetectNZXTMouseControllers, NZXT_VID, NZXT_LIFT_PID, 0, 0xFFCA, 1); diff --git a/Controllers/NZXTMouseController/RGBController_NZXTMouse.cpp b/Controllers/NZXTMouseController/RGBController_NZXTMouse.cpp new file mode 100644 index 0000000..b7e90c4 --- /dev/null +++ b/Controllers/NZXTMouseController/RGBController_NZXTMouse.cpp @@ -0,0 +1,118 @@ +/*---------------------------------------------------------*\ +| RGBController_NZXTMouse.cpp | +| | +| RGBController for NZXT Mouse | +| | +| Adam Honse (calcprogrammer1@gmail.com) 16 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_NZXTMouse.h" + +/**------------------------------------------------------------------*\ + @name NZXT Mouse + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :tools: + @detectors DetectNZXTMouseControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_NZXTMouse::RGBController_NZXTMouse(NZXTMouseController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "NZXT"; + type = DEVICE_TYPE_MOUSE; + description = "NZXT Mouse Device"; + version = controller->GetFirmwareVersion(); + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_NZXTMouse::~RGBController_NZXTMouse() +{ + +} + +void RGBController_NZXTMouse::SetupZones() +{ + zone left; + + left.name = "Left"; + left.type = ZONE_TYPE_LINEAR; + left.leds_min = 3; + left.leds_max = 3; + left.leds_count = 3; + left.matrix_map = NULL; + + zones.push_back( left ); + + for(unsigned int led_idx = 0; led_idx < left.leds_count; led_idx++) + { + led left_led; + left_led.name = "Left LED " + std::to_string(led_idx); + + leds.push_back(left_led); + } + + zone right; + + right.name = "Right"; + right.type = ZONE_TYPE_LINEAR; + right.leds_min = 3; + right.leds_max = 3; + right.leds_count = 3; + right.matrix_map = NULL; + + zones.push_back( right ); + + for(unsigned int led_idx = 0; led_idx < right.leds_count; led_idx++) + { + led right_led; + right_led.name = "Right LED " + std::to_string(led_idx); + + leds.push_back(right_led); + } + + SetupColors(); +} + +void RGBController_NZXTMouse::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_NZXTMouse::DeviceUpdateLEDs() +{ +controller->SetLEDs(&colors[0]); +} + +void RGBController_NZXTMouse::UpdateZoneLEDs(int /*zone*/) +{ +DeviceUpdateLEDs(); +} + +void RGBController_NZXTMouse::UpdateSingleLED(int /*led*/) +{ +DeviceUpdateLEDs(); +} + +void RGBController_NZXTMouse::DeviceUpdateMode() +{ +DeviceUpdateLEDs(); +} diff --git a/Controllers/NZXTMouseController/RGBController_NZXTMouse.h b/Controllers/NZXTMouseController/RGBController_NZXTMouse.h new file mode 100644 index 0000000..6bec12f --- /dev/null +++ b/Controllers/NZXTMouseController/RGBController_NZXTMouse.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_NZXTMouse.h | +| | +| RGBController for NZXT Mouse | +| | +| Adam Honse (calcprogrammer1@gmail.com) 16 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "NZXTMouseController.h" + +class RGBController_NZXTMouse : public RGBController +{ +public: + RGBController_NZXTMouse(NZXTMouseController* controller_ptr); + ~RGBController_NZXTMouse(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + NZXTMouseController* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/NanoleafController/NanoleafController.cpp b/Controllers/NanoleafController/NanoleafController.cpp new file mode 100644 index 0000000..28d3082 --- /dev/null +++ b/Controllers/NanoleafController/NanoleafController.cpp @@ -0,0 +1,364 @@ +/*---------------------------------------------------------*\ +| NanoleafController.cpp | +| | +| Driver for Nanoleaf | +| | +| Nikita Rushmanov 13 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "NanoleafController.h" +#include "LogManager.h" +#include "httplib.h" + +long APIRequest(std::string method, std::string location, std::string URI, json* request_data = nullptr, json* response_data = nullptr) +{ + /*-------------------------------------------------------------*\ + | Append http:// to the location field to create the URL | + \*-------------------------------------------------------------*/ + const std::string url("http://" + location); + + /*-------------------------------------------------------------*\ + | Create httplib Client and variables to hold result | + \*-------------------------------------------------------------*/ + httplib::Client client(url.c_str()); + int status = 0; + std::string body = ""; + + /*-------------------------------------------------------------*\ + | Perform the appropriate call for the given method | + \*-------------------------------------------------------------*/ + if(method == "GET") + { + httplib::Result result = client.Get(URI.c_str()); + + if(httplib::Error::Success == result.error()) + { + status = result->status; + body = result->body; + } + } + else if(method == "PUT") + { + if(request_data) + { + httplib::Result result = client.Put(URI.c_str(), request_data->dump(), "application/json"); + + if(httplib::Error::Success == result.error()) + { + status = result->status; + body = result->body; + } + } + else + { + httplib::Result result = client.Put(URI.c_str()); + + if(httplib::Error::Success == result.error()) + { + status = result->status; + body = result->body; + } + } + } + else if(method == "DELETE") + { + httplib::Result result = client.Delete(URI.c_str()); + + if(httplib::Error::Success == result.error()) + { + status = result->status; + body = result->body; + } + } + else if(method == "POST") + { + httplib::Result result = client.Post(URI.c_str()); + + if(httplib::Error::Success == result.error()) + { + status = result->status; + body = result->body; + } + } + + /*-------------------------------------------------------------*\ + | If status is in the 200 range the request was successful | + \*-------------------------------------------------------------*/ + if((status / 100) == 2) + { + if(response_data) + { + *response_data = json::parse(body); + } + } + else + { + LOG_DEBUG("[Nanoleaf] HTTP %i:Could not %s from %s", status, method.c_str(), url.c_str()); + } + + return status; +} + +NanoleafController::NanoleafController(std::string a_address, int a_port, std::string a_auth_token) +{ + address = a_address; + port = a_port; + auth_token = a_auth_token; + location = address + ":" + std::to_string(port); + + json data; + if(APIRequest("GET", location, "/api/v1/"+auth_token, nullptr, &data) == 200) + { + name = data["name"]; + serial = data["serialNo"]; + manufacturer = data["manufacturer"]; + firmware_version = data["firmwareVersion"]; + model = data["model"]; + + brightness = data["state"]["brightness"]["value"]; + selectedEffect = data["effects"]["select"]; + + for(json::const_iterator it = data["effects"]["effectsList"].begin(); it != data["effects"]["effectsList"].end(); ++it) + { + effects.push_back(it.value()); + } + + for(json::const_iterator it = data["panelLayout"]["layout"]["positionData"].begin(); it != data["panelLayout"]["layout"]["positionData"].end(); ++it) + { + panel_ids.push_back(it.value()["panelId"].get()); + } + } + else + { + throw std::exception(); + } +} + +std::string NanoleafController::Pair(std::string address, int port) +{ + const std::string location = address+":"+std::to_string(port); + + json data; + if(APIRequest("POST", location, "/api/v1/new", nullptr, &data) == 200) + { + return data["auth_token"]; + } + else + { + throw std::exception(); + } +} + +void NanoleafController::Unpair(std::string address, int port, std::string auth_token) +{ + const std::string location = address+":"+std::to_string(port); + + /*-------------------------------------------------------------*\ + | We really don't care if this fails. | + \*-------------------------------------------------------------*/ + APIRequest("DELETE", location, "/api/v1/"+auth_token, nullptr, nullptr); +} + +void NanoleafController::UpdateLEDs(std::vector& colors) +{ + /*-------------------------------------------------------------*\ + | Requires StartExternalControl() to have been called prior. | + \*-------------------------------------------------------------*/ + + if(model == NANOLEAF_LIGHT_PANELS_MODEL) + { + /*---------------------------------------------------------*\ + | Protocol V1 - https://forum.nanoleaf.me/docs | + | | + | Size Description | + | --------------------------------------------------------- | + | 1 nPanels Number of panels | + | | + | 1 panelId ID of panel | + | 1 nFrames Number of frames (always 1) | + | 1 R Red channel | + | 1 G Green channel | + | 1 B Blue channel | + | 1 W White channel (ignored) | + | 1 transitionTime Transition time (x 100ms) | + \*---------------------------------------------------------*/ + std::size_t size = panel_ids.size(); + + uint8_t* message = (uint8_t*)malloc((size * 7) + 1); + + message[0] = (uint8_t)size; /* nPanels */ + + for(unsigned int i = 0; i < size; i++) + { + message[(7 * i) + 0 + 1] = (uint8_t)panel_ids[i]; /* panelId */ + message[(7 * i) + 1 + 1] = (uint8_t)1; /* nFrames */ + message[(7 * i) + 2 + 1] = (uint8_t)RGBGetRValue(colors[i]); /* R */ + message[(7 * i) + 3 + 1] = (uint8_t)RGBGetGValue(colors[i]); /* G */ + message[(7 * i) + 4 + 1] = (uint8_t)RGBGetBValue(colors[i]); /* B */ + message[(7 * i) + 5 + 1] = (uint8_t)0; /* W */ + message[(7 * i) + 6 + 1] = (uint8_t)0; /* transitionTime */ + } + + external_control_socket.udp_write((char*)message, ((int)size * 7) + 1); + + free(message); + } + else if((model == NANOLEAF_CANVAS_MODEL) + || (model == NANOLEAF_SHAPES_MODEL)) + { + /*---------------------------------------------------------*\ + | Protocol V2 - https://forum.nanoleaf.me/docs | + | | + | Size Description | + | --------------------------------------------------------- | + | 2 nPanels Number of panels | + | | + | 2 panelId ID of panel | + | 1 R Red channel | + | 1 G Green channel | + | 1 B Blue channel | + | 1 W White channel (ignored) | + | 2 transitionTime Transition time (x 100ms) | + \*---------------------------------------------------------*/ + std::size_t size = panel_ids.size(); + + uint8_t* message = (uint8_t*)malloc((size * 8) + 2); + + message[0] = (uint8_t)(size >> 8); /* nPanels H */ + message[1] = (uint8_t)(size & 0xFF); /* nPanels L */ + + for(unsigned int i = 0; i < size; i++) + { + message[(8 * i) + 0 + 2] = (uint8_t)(panel_ids[i] >> 8); /* panelId H */ + message[(8 * i) + 1 + 2] = (uint8_t)(panel_ids[i] & 0xFF); /* panelId L */ + message[(8 * i) + 2 + 2] = (uint8_t)RGBGetRValue(colors[i]); /* R */ + message[(8 * i) + 3 + 2] = (uint8_t)RGBGetGValue(colors[i]); /* G */ + message[(8 * i) + 4 + 2] = (uint8_t)RGBGetBValue(colors[i]); /* B */ + message[(8 * i) + 5 + 2] = (uint8_t)0; /* W */ + message[(8 * i) + 6 + 2] = (uint8_t)0; /* transitionTime H */ + message[(8 * i) + 7 + 2] = (uint8_t)0; /* transitionTime L */ + } + + external_control_socket.udp_write((char *)message, ((int)size * 8) + 2); + + free(message); + } +} + +void NanoleafController::StartExternalControl() +{ + json request; + request["write"]["command"] = "display"; + request["write"]["animType"] = "extControl"; + + /*-------------------------------------------------------------*\ + | Determine whether to use v1 or v2 extControl protocol based | + | on model string | + \*-------------------------------------------------------------*/ + if(model == NANOLEAF_LIGHT_PANELS_MODEL) + { + /*---------------------------------------------------------*\ + | Protocol v1 returns IP and port for UDP communication | + \*---------------------------------------------------------*/ + request["write"]["extControlVersion"] = "v1"; + + json response; + if((APIRequest("PUT", location, "/api/v1/"+auth_token+"/effects", &request, &response) / 100) == 2) + { + external_control_socket.udp_client(response["streamControlIpAddr"].get().c_str(), std::to_string(response["streamControlPort"].get()).c_str()); + + selectedEffect = NANOLEAF_DIRECT_MODE_EFFECT_NAME; + } + } + else if((model == NANOLEAF_CANVAS_MODEL) + || (model == NANOLEAF_SHAPES_MODEL)) + { + /*---------------------------------------------------------*\ + | Protocol v2 does not return anything, use device IP and | + | port 60222 | + \*---------------------------------------------------------*/ + request["write"]["extControlVersion"] = "v2"; + + if((APIRequest("PUT", location, "/api/v1/"+auth_token+"/effects", &request) / 100) == 2) + { + external_control_socket.udp_client(address.c_str(), "60222"); + + selectedEffect = NANOLEAF_DIRECT_MODE_EFFECT_NAME; + } + } +} + +void NanoleafController::SelectEffect(std::string effect_name) +{ + json request; + request["select"] = effect_name; + + if((APIRequest("PUT", location, "/api/v1/"+auth_token+"/effects", &request) / 100) == 2) + { + selectedEffect = effect_name; + } +} + +void NanoleafController::SetBrightness(int a_brightness) +{ + json request; + request["brightness"]["value"] = a_brightness; + + if((APIRequest("PUT", location, "/api/v1/"+auth_token+"/state", &request) / 100) == 2) + { + brightness = a_brightness; + } +} + +std::string NanoleafController::GetAuthToken() +{ + return auth_token; +}; + +std::string NanoleafController::GetName() +{ + return name; +}; + +std::string NanoleafController::GetSerial() +{ + return serial; +}; + +std::string NanoleafController::GetManufacturer() +{ + return manufacturer; +}; + +std::string NanoleafController::GetFirmwareVersion() +{ + return firmware_version; +}; + +std::string NanoleafController::GetModel() +{ + return model; +}; + +std::vector& NanoleafController::GetEffects() +{ + return effects; +}; + +std::vector& NanoleafController::GetPanelIds() +{ + return panel_ids; +}; + +std::string NanoleafController::GetSelectedEffect() +{ + return selectedEffect; +}; + +int NanoleafController::GetBrightness() +{ + return brightness; +}; diff --git a/Controllers/NanoleafController/NanoleafController.h b/Controllers/NanoleafController/NanoleafController.h new file mode 100644 index 0000000..6fb6081 --- /dev/null +++ b/Controllers/NanoleafController/NanoleafController.h @@ -0,0 +1,65 @@ +/*---------------------------------------------------------*\ +| NanoleafController.h | +| | +| Driver for Nanoleaf | +| | +| Nikita Rushmanov 13 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "net_port.h" + +#define NANOLEAF_DIRECT_MODE_EFFECT_NAME "*Dynamic*" +#define NANOLEAF_LIGHT_PANELS_MODEL "NL22" +#define NANOLEAF_CANVAS_MODEL "NL29" +#define NANOLEAF_SHAPES_MODEL "NL42" + +class NanoleafController +{ +public: + NanoleafController(std::string a_address, int a_port, std::string a_auth_token); + + static std::string Pair(std::string address, int port); + static void Unpair(std::string address, int port, std::string auth_token); + + void SelectEffect(std::string effect_name); + void StartExternalControl(); + void SetBrightness(int a_brightness); + void UpdateLEDs(std::vector& colors); + + std::string GetAuthToken(); + std::string GetName(); + std::string GetSerial(); + std::string GetManufacturer(); + std::string GetFirmwareVersion(); + std::string GetModel(); + std::vector& GetEffects(); + std::vector& GetPanelIds(); + std::string GetSelectedEffect(); + int GetBrightness(); + +private: + net_port external_control_socket; + + std::string address; + int port; + std::string location; + std::string auth_token; + + std::string name; + std::string serial; + std::string manufacturer; + std::string firmware_version; + std::string model; + + std::vector effects; + std::vector panel_ids; + + std::string selectedEffect; + int brightness; +}; diff --git a/Controllers/NanoleafController/NanoleafControllerDetect.cpp b/Controllers/NanoleafController/NanoleafControllerDetect.cpp new file mode 100644 index 0000000..508de11 --- /dev/null +++ b/Controllers/NanoleafController/NanoleafControllerDetect.cpp @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| NanoleafControllerDetect.cpp | +| | +| Detector for Nanoleaf | +| | +| Nikita Rushmanov 13 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBController_Nanoleaf.h" +#include "SettingsManager.h" +#include "LogManager.h" + +/*----------------------------------------------------------------------------------------*\ +| | +| DetectNanoleafControllers | +| | +| Connect to paired Nanoleaf devices | +| | +\*----------------------------------------------------------------------------------------*/ + +void DetectNanoleafControllers() +{ + json nanoleaf_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("NanoleafDevices"); + + if(nanoleaf_settings.contains("devices")) + { + for(json::const_iterator it = nanoleaf_settings["devices"].begin(); it != nanoleaf_settings["devices"].end(); ++it) + { + const json& device = it.value(); + + if(device.contains("ip") && device.contains("port") && device.contains("auth_token")) + { + try + { + RGBController_Nanoleaf* rgb_controller = new RGBController_Nanoleaf(device["ip"], device["port"], device["auth_token"]); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + catch(...) + { + LOG_DEBUG("[Nanoleaf] Could not connect to device at %s:%d using auth_token %s", device["ip"].get().c_str(), device["port"].get(), device["auth_token"].get().c_str()); + } + } + } + } +} /* DetectNanoleafControllers() */ + +REGISTER_DETECTOR("Nanoleaf", DetectNanoleafControllers); diff --git a/Controllers/NanoleafController/RGBController_Nanoleaf.cpp b/Controllers/NanoleafController/RGBController_Nanoleaf.cpp new file mode 100644 index 0000000..ed32d62 --- /dev/null +++ b/Controllers/NanoleafController/RGBController_Nanoleaf.cpp @@ -0,0 +1,151 @@ +/*---------------------------------------------------------*\ +| RGBController_Nanoleaf.cpp | +| | +| RGBController for Nanoleaf | +| | +| Nikita Rushmanov 13 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Nanoleaf.h" +#include "ResourceManager.h" +#include "LogManager.h" +#include + +using json = nlohmann::json; + +/**------------------------------------------------------------------*\ + @name Nanoleaf + @category Light + @type Network + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectNanoleafControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Nanoleaf::RGBController_Nanoleaf(std::string a_address, int a_port, std::string a_auth_token) : + controller(a_address, a_port, a_auth_token) +{ + location = a_address+":"+std::to_string(a_port); + name = controller.GetName(); + serial = controller.GetSerial(); + vendor = controller.GetManufacturer(); + version = controller.GetFirmwareVersion(); + description = controller.GetModel(); + type = DEVICE_TYPE_LIGHT; + + /*-------------------------------------------------------------*\ + | Direct mode uses external control protocol. | + \*-------------------------------------------------------------*/ + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + /*---------------------------------------------------------*\ + | Set this effect as current if the name is selected. | + \*---------------------------------------------------------*/ + if(controller.GetSelectedEffect() == NANOLEAF_DIRECT_MODE_EFFECT_NAME) + { + /*-----------------------------------------------------*\ + | If the direct mode is active, we need to call this | + | method to open the socket. | + \*-----------------------------------------------------*/ + controller.StartExternalControl(); + active_mode = 0; + } + + /*-------------------------------------------------------------*\ + | Create additional modes from device effects list | + \*-------------------------------------------------------------*/ + for(std::vector::const_iterator it = controller.GetEffects().begin(); it != controller.GetEffects().end(); ++it) + { + mode effect; + effect.name = *it; + effect.flags = MODE_FLAG_HAS_BRIGHTNESS; + effect.color_mode = MODE_COLORS_NONE; + effect.brightness_max = 100; + effect.brightness_min = 0; + effect.brightness = 100; + + modes.push_back(effect); + + /*---------------------------------------------------------*\ + | Set this effect as current if the name is selected. | + \*---------------------------------------------------------*/ + if(controller.GetSelectedEffect() == effect.name) + { + active_mode = (int)modes.size() - 1; + } + } + + SetupZones(); +} + +void RGBController_Nanoleaf::SetupZones() +{ + zone led_zone; + led_zone.name = "Nanoleaf Layout"; + led_zone.type = ZONE_TYPE_LINEAR; + led_zone.leds_count = (unsigned int)controller.GetPanelIds().size(); + led_zone.leds_min = led_zone.leds_count; + led_zone.leds_max = led_zone.leds_count; + led_zone.matrix_map = NULL; + + for(std::vector::const_iterator it = controller.GetPanelIds().begin(); it != controller.GetPanelIds().end(); ++it) + { + led new_led; + new_led.name = std::to_string(*it); + leds.push_back(new_led); + } + + zones.push_back(led_zone); + + SetupColors(); +} + +void RGBController_Nanoleaf::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Nanoleaf::DeviceUpdateLEDs() +{ + controller.UpdateLEDs(colors); +} + +void RGBController_Nanoleaf::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Nanoleaf::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Nanoleaf::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Mode 0 is reserved for Direct mode | + \*---------------------------------------------------------*/ + if(active_mode == 0) + { + controller.StartExternalControl(); + } + /*---------------------------------------------------------*\ + | Update normal effects. | + \*---------------------------------------------------------*/ + else + { + controller.SelectEffect(modes[active_mode].name); + controller.SetBrightness(modes[active_mode].brightness); + } +} diff --git a/Controllers/NanoleafController/RGBController_Nanoleaf.h b/Controllers/NanoleafController/RGBController_Nanoleaf.h new file mode 100644 index 0000000..d233b98 --- /dev/null +++ b/Controllers/NanoleafController/RGBController_Nanoleaf.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_Nanoleaf.h | +| | +| RGBController for Nanoleaf | +| | +| Nikita Rushmanov 13 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "NanoleafController.h" + +class RGBController_Nanoleaf : public RGBController +{ + +public: + RGBController_Nanoleaf(std::string a_address, int a_port, std::string a_auth_token); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + NanoleafController controller; +}; diff --git a/Controllers/NollieController/NollieController.cpp b/Controllers/NollieController/NollieController.cpp new file mode 100644 index 0000000..7ceece1 --- /dev/null +++ b/Controllers/NollieController/NollieController.cpp @@ -0,0 +1,192 @@ +/*---------------------------------------------------------*\ +| NollieController.cpp | +| | +| Driver for Nollie | +| | +| Name (cnn1236661) 25 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "NollieController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +NollieController::NollieController(hid_device* dev_handle, const char* path, unsigned short vid, unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + usb_vid = vid; + usb_pid = pid; +} + +std::string NollieController::GetLocationString() +{ + return("HID: " + location); +} + +std::string NollieController::GetNameString() +{ + return(name); +} + +std::string NollieController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned short NollieController::GetUSBPID() +{ + return(usb_pid); +} + +unsigned short NollieController::GetUSBVID() +{ + return(usb_vid); +} + +void NollieController::InitChLEDs(int *led_num_list,int ch_num) +{ + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[1] = 0xFE; + usb_buf[2] = 0x03; + for(int i = 0; i < ch_num; i++) + { + usb_buf[3+(i*2)] = led_num_list[i]& 0xFF; + usb_buf[4+(i*2)] = (led_num_list[i] >> 8) & 0xFF; + } + hid_write(dev, usb_buf, 65); +} + +void NollieController::SetMos(bool mos) +{ + unsigned char usb_buf[65]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x01] = 0x80; + usb_buf[0x02] = mos; + hid_write(dev, usb_buf, 65); +} + +void NollieController::SetChannelLEDs(unsigned char channel, RGBColor* colors, unsigned int num_colors) +{ + if(usb_pid == NOLLIE32_PID || usb_pid == NOLLIE16_PID || usb_pid == NOLLIE32_OS21_PID || usb_pid == NOLLIE16_OS21_PID) + { + SendPacket(channel,&colors[0], num_colors); + } + else + { + unsigned int num_packets = (num_colors / 21) + ((num_colors % 21) > 0); + unsigned int color_idx = 0; + + for(unsigned int packet_idx = 0; packet_idx < num_packets; packet_idx++) + { + unsigned int colors_in_packet = 21; + if(num_colors - color_idx < colors_in_packet) + { + colors_in_packet = num_colors - color_idx; + } + SendPacketFS(channel,packet_idx,&colors[color_idx], colors_in_packet); + color_idx += colors_in_packet; + } + } +} + +void NollieController::SendUpdate() +{ + unsigned char usb_buf[65] ; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[1] = 0xff; + hid_write(dev, usb_buf, 65); +} + +void NollieController::SendPacket(unsigned char channel,RGBColor* colors,unsigned int num_colors) +{ + unsigned char usb_buf[1025]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[1] = channel; + usb_buf[2] = 0; + usb_buf[3] = num_colors / 256; + usb_buf[4] = num_colors % 256; + if(num_colors) + { + for(unsigned int color_idx = 0; color_idx < num_colors; color_idx++) + { + usb_buf[0x05 + (color_idx * 3)] = RGBGetGValue(colors[color_idx]); + usb_buf[0x06 + (color_idx * 3)] = RGBGetRValue(colors[color_idx]); + usb_buf[0x07 + (color_idx * 3)] = RGBGetBValue(colors[color_idx]); + } + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + if(channel == NOLLIE32_FLAG1_CHANNEL) + { + usb_buf[2] = 1; + hid_write(dev, usb_buf, 1025); + std::this_thread::sleep_for(std::chrono::milliseconds(8)); + } + else if(channel == NOLLIE32_FLAG2_CHANNEL) + { + usb_buf[2] = 2; + hid_write(dev, usb_buf, 1025); + std::this_thread::sleep_for(std::chrono::milliseconds(8)); + } + else + { + hid_write(dev, usb_buf, 1025); + } +} + +void NollieController::SendPacketFS(unsigned char channel,unsigned char packet_id,RGBColor* colors,unsigned int num_colors) +{ + unsigned char usb_buf[65]; + unsigned int packet_interval; + unsigned int dev_pid = GetUSBPID(); + switch(dev_pid) + { + case NOLLIE28_12_PID: + packet_interval = 2; + break; + case NOLLIE8_PID: + case NOLLIE8_OS21_PID: + case PRISM8_OS21_PID: + packet_interval = 6; + break; + case NOLLIE1_PID: + case NOLLIE1_OS21_PID: + packet_interval = 30; + break; + default: + packet_interval = 25; + break; + } + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0x00; + usb_buf[0x01] = packet_id + channel * packet_interval; + for(unsigned int color_idx = 0; color_idx < num_colors; color_idx++) + { + usb_buf[0x02 + (color_idx * 3)] = RGBGetRValue(colors[color_idx]); + usb_buf[0x03 + (color_idx * 3)] = RGBGetGValue(colors[color_idx]); + usb_buf[0x04 + (color_idx * 3)] = RGBGetBValue(colors[color_idx]); + if(dev_pid == NOLLIE8_PID || dev_pid == NOLLIE1_PID || dev_pid == NOLLIE8_OS21_PID || dev_pid == PRISM8_OS21_PID || dev_pid == NOLLIE1_OS21_PID) + { + usb_buf[0x02 + (color_idx * 3)] = RGBGetGValue(colors[color_idx]); + usb_buf[0x03 + (color_idx * 3)] = RGBGetRValue(colors[color_idx]); + } + } + hid_write(dev, usb_buf, 65); +} diff --git a/Controllers/NollieController/NollieController.h b/Controllers/NollieController/NollieController.h new file mode 100644 index 0000000..24d0cff --- /dev/null +++ b/Controllers/NollieController/NollieController.h @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| NollieController.h | +| | +| Driver for Nollie | +| | +| Name (cnn1236661) 25 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define NOLLIE_12_CH_LED_NUM 42 +#define NOLLIE_8_CH_LED_NUM 126 +#define NOLLIE_1_CH_LED_NUM 630 +#define NOLLIE_HS_CH_LED_NUM 256 +#define NOLLIE_FS_CH_LED_NUM 525 + +#define NOLLIERGBOS_2_VID 0x16D5 + +#define NOLLIE32_CHANNELS_NUM 32 +#define NOLLIE32_PID 0x4714 +#define NOLLIE32_OS21_PID 0x2A32 +#define NOLLIE32_VID 0x3061 + +#define NOLLIE16_CHANNELS_NUM 16 +#define NOLLIE16_PID 0x4716 +#define NOLLIE16_OS21_PID 0x2A16 +#define NOLLIE16_VID 0x3061 + +#define NOLLIE8_CHANNELS_NUM 8 +#define NOLLIE8_PID 0x1F01 +#define NOLLIE8_OS21_PID 0x2A08 +#define PRISM8_OS21_PID 0x2C08 +#define NOLLIE8_VID 0x16D2 + +#define NOLLIE1_CHANNELS_NUM 1 +#define NOLLIE1_PID 0x1F11 +#define NOLLIE1_OS21_PID 0x2A01 +#define NOLLIE1_VID 0x16D2 + +#define NOLLIE28_12_CHANNELS_NUM 12 +#define NOLLIE28_12_VID 0x16D2 +#define NOLLIE28_12_PID 0x1616 +#define NOLLIE28_L1_PID 0x1617 +#define NOLLIE28_L2_PID 0x1618 + +#define NOLLIE32_MOS_TRIGGER_CH 26 +#define NOLLIE32_MOS_TRIGGER_LED 20 +#define NOLLIE32_FLAG1_CHANNEL 15 +#define NOLLIE32_FLAG2_CHANNEL 31 + +class NollieController +{ +public: + NollieController(hid_device* dev_handle, const char* path, unsigned short vid, unsigned short pid, std::string dev_name); + + std::string GetLocationString(); + std::string GetNameString(); + std::string GetSerialString(); + unsigned short GetUSBVID(); + unsigned short GetUSBPID(); + + void SetMos(bool mos); + void InitChLEDs(int *led_num_list,int ch_num); + void SendUpdate(); + void SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors); + +private: + hid_device* dev; + std::string location; + std::string name; + unsigned short usb_vid; + unsigned short usb_pid; + + void SendPacket(unsigned char channel,RGBColor * colors,unsigned int num_colors); + void SendPacketFS(unsigned char channel,unsigned char packet_id,RGBColor * colors,unsigned int num_colors); +}; diff --git a/Controllers/NollieController/NollieControllerDetect.cpp b/Controllers/NollieController/NollieControllerDetect.cpp new file mode 100644 index 0000000..6043282 --- /dev/null +++ b/Controllers/NollieController/NollieControllerDetect.cpp @@ -0,0 +1,64 @@ +/*---------------------------------------------------------*\ +| NollieControllerDetect.cpp | +| | +| Detector for Nollie | +| | +| Name (cnn1236661) 25 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "NollieController.h" +#include "RGBController_Nollie.h" + +void DetectNollieControllers(hid_device_info* info, const std::string& name) +{ + if((info->product_id == NOLLIE1_OS21_PID || info->product_id == NOLLIE8_OS21_PID || info->product_id == PRISM8_OS21_PID) + && info->interface_number != 2) + { + return; + } + + if((info->product_id == NOLLIE16_OS21_PID || info->product_id == NOLLIE32_OS21_PID) + && info->interface_number != 0) + { + return; + } + + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + wchar_t product[128]; + hid_get_product_string(dev, product, 128); + + std::wstring product_str(product); + + NollieController* controller = new NollieController(dev, info->path, info->vendor_id, info->product_id, name); + RGBController_Nollie* rgb_controller = new RGBController_Nollie(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + + } +} + +REGISTER_HID_DETECTOR("Nollie 32CH", DetectNollieControllers, NOLLIE32_VID, NOLLIE32_PID); +REGISTER_HID_DETECTOR("Nollie 16CH", DetectNollieControllers, NOLLIE16_VID, NOLLIE16_PID); +REGISTER_HID_DETECTOR("Nollie 8CH", DetectNollieControllers, NOLLIE8_VID, NOLLIE8_PID); +REGISTER_HID_DETECTOR("Nollie 1CH", DetectNollieControllers, NOLLIE1_VID, NOLLIE1_PID); +REGISTER_HID_DETECTOR("Nollie 32_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE32_OS21_PID); +REGISTER_HID_DETECTOR("Nollie 16_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE16_OS21_PID); +REGISTER_HID_DETECTOR("Nollie 8_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE8_OS21_PID); +REGISTER_HID_DETECTOR("Prism 8_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, PRISM8_OS21_PID); +REGISTER_HID_DETECTOR("Nollie 1_OS2.1", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE1_OS21_PID); +REGISTER_HID_DETECTOR("Nollie 28 12", DetectNollieControllers, NOLLIE28_12_VID, NOLLIE28_12_PID); +REGISTER_HID_DETECTOR("Nollie 28 L1", DetectNollieControllers, NOLLIE28_12_VID, NOLLIE28_L1_PID); +REGISTER_HID_DETECTOR("Nollie 28 L2", DetectNollieControllers, NOLLIE28_12_VID, NOLLIE28_L2_PID); +//Nollie OS2 Firmware +REGISTER_HID_DETECTOR("Nollie 32_OS2", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE32_PID); +REGISTER_HID_DETECTOR("Nollie 16_OS2", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE16_PID); +REGISTER_HID_DETECTOR("Nollie 8_OS2", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE8_PID); +REGISTER_HID_DETECTOR("Nollie 1_OS2", DetectNollieControllers, NOLLIERGBOS_2_VID, NOLLIE1_PID); diff --git a/Controllers/NollieController/RGBController_Nollie.cpp b/Controllers/NollieController/RGBController_Nollie.cpp new file mode 100644 index 0000000..3892ceb --- /dev/null +++ b/Controllers/NollieController/RGBController_Nollie.cpp @@ -0,0 +1,251 @@ +/*---------------------------------------------------------*\ +| RGBController_Nollie.cpp | +| | +| RGBController for Nollie | +| | +| Name (cnn1236661) 25 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_Nollie.h" + +/**------------------------------------------------------------------*\ + @name Nollie Controller + @category LEDStrip + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectNollieControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Nollie::RGBController_Nollie(NollieController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Nollie"; + description = "Nollie Controller Device"; + type = DEVICE_TYPE_LEDSTRIP; + location = controller->GetLocationString(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_Nollie::~RGBController_Nollie() +{ + delete controller; +} + +void RGBController_Nollie::SetupZones() +{ + bool first_run = false; + unsigned int channels_num = 0; + unsigned int ch_led_num = 0; + if(zones.size() == 0) + { + first_run = true; + } + leds.clear(); + colors.clear(); + switch(controller->GetUSBPID()) + { + case NOLLIE32_PID: + case NOLLIE32_OS21_PID: + channels_num = NOLLIE32_CHANNELS_NUM; + ch_led_num = NOLLIE_HS_CH_LED_NUM; + channel_index = ch32; + break; + case NOLLIE16_PID: + case NOLLIE16_OS21_PID: + channels_num = NOLLIE16_CHANNELS_NUM; + ch_led_num = NOLLIE_HS_CH_LED_NUM; + channel_index = ch16; + if (controller->GetUSBVID() == NOLLIERGBOS_2_VID) + channel_index = n16; + break; + case NOLLIE28_12_PID: + channels_num = NOLLIE28_12_CHANNELS_NUM; + ch_led_num = NOLLIE_12_CH_LED_NUM; + break; + case NOLLIE8_PID: + case NOLLIE8_OS21_PID: + case PRISM8_OS21_PID: + channels_num = NOLLIE8_CHANNELS_NUM; + ch_led_num = NOLLIE_8_CH_LED_NUM; + break; + case NOLLIE1_PID: + case NOLLIE1_OS21_PID: + channels_num = NOLLIE1_CHANNELS_NUM; + ch_led_num = NOLLIE_1_CH_LED_NUM; + break; + default: + channels_num = NOLLIE8_CHANNELS_NUM; + ch_led_num = NOLLIE_FS_CH_LED_NUM; + break; + } + zones.resize(channels_num); + for(unsigned int channel_idx = 0; channel_idx < channels_num; channel_idx++) + { + if(channel_idx > 27 ) + { + char ch_idx_string[4]; + snprintf(ch_idx_string, 4, "%d", channel_idx + 1 - 28); + zones[channel_idx].name = "Channel EXT "; + zones[channel_idx].name.append(ch_idx_string); + } + else if(channel_idx > 21 ) + { + char ch_idx_string[4]; + snprintf(ch_idx_string, 4, "%d", channel_idx + 1 - 22); + zones[channel_idx].name = "Channel GPU "; + zones[channel_idx].name.append(ch_idx_string); + } + else if(channel_idx > 15 ) + { + char ch_idx_string[4]; + snprintf(ch_idx_string, 4, "%d", channel_idx + 1 - 16); + zones[channel_idx].name = "Channel ATX "; + zones[channel_idx].name.append(ch_idx_string); + } + else + { + char ch_idx_string[4]; + snprintf(ch_idx_string, 4, "%d", channel_idx + 1); + zones[channel_idx].name = "Channel "; + zones[channel_idx].name.append(ch_idx_string); + } + zones[channel_idx].type = ZONE_TYPE_LINEAR; + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = ch_led_num; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + + zones[channel_idx].matrix_map = NULL; + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + char led_idx_string[4]; + snprintf(led_idx_string, 4, "%d", led_ch_idx + 1); + + led new_led; + new_led.name = "LED "; + new_led.name.append(led_idx_string); + + leds.push_back(new_led); + leds_channel.push_back(channel_idx); + } + } + + SetupColors(); +} + +void RGBController_Nollie::ResizeZone(int zone, int new_size) +{ + /*-----------------------------------------------------*\ + | Set whether MOS is enabled or not | + \*-----------------------------------------------------*/ + if(controller->GetUSBVID() == NOLLIE32_VID && NOLLIE32_PID == controller->GetUSBPID()) + { + if(zone == NOLLIE32_MOS_TRIGGER_CH && new_size > NOLLIE32_MOS_TRIGGER_LED) + { + controller->SetMos(false); + } + else if(zone == NOLLIE32_MOS_TRIGGER_CH) + { + controller->SetMos(true); + } + } + + /*-----------------------------------------------------*\ + | Nollie1 needs to report the number of LEDs | + \*-----------------------------------------------------*/ + if(controller->GetUSBPID() == NOLLIE1_PID) + { + controller->InitChLEDs(&new_size,NOLLIE1_CHANNELS_NUM); + } + + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_Nollie::DeviceUpdateLEDs() +{ + unsigned int DevPid = controller->GetUSBPID(); + if(DevPid == NOLLIE32_PID || DevPid == NOLLIE16_PID || DevPid == NOLLIE32_OS21_PID || DevPid == NOLLIE16_OS21_PID) + { + std::vector ChSort; + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + unsigned int channel = channel_index[zone_idx]; + if(zones[zone_idx].leds_count > 0) + { + ChSort.push_back(channel); + } + else if(channel == NOLLIE32_FLAG1_CHANNEL || channel == NOLLIE32_FLAG2_CHANNEL) + { + ChSort.push_back(channel); + } + } + std::sort(ChSort.begin(), ChSort.end()); + for(std::size_t i = 0; i < ChSort.size(); i++) + { + int* ptr = std::find(channel_index, channel_index + 32, ChSort[i]); + int zone_idx = (int)(ptr - channel_index); + controller->SetChannelLEDs(ChSort[i], zones[zone_idx].colors, zones[zone_idx].leds_count); + } + } + else + { + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].leds_count > 0) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } + } + controller->SendUpdate(); + } + +} + +void RGBController_Nollie::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(channel_index[zone], zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_Nollie::UpdateSingleLED(int led) +{ + unsigned int channel = leds_channel[led]; + controller->SetChannelLEDs(channel_index[channel], zones[channel].colors, zones[channel].leds_count); +} + +void RGBController_Nollie::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/NollieController/RGBController_Nollie.h b/Controllers/NollieController/RGBController_Nollie.h new file mode 100644 index 0000000..9d09004 --- /dev/null +++ b/Controllers/NollieController/RGBController_Nollie.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_Nollie.h | +| | +| RGBController for Nollie | +| | +| Name (cnn1236661) 25 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "NollieController.h" + +class RGBController_Nollie : public RGBController +{ +public: + RGBController_Nollie(NollieController* controller_ptr); + ~RGBController_Nollie(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + NollieController* controller; + std::vector leds_channel; + std::vector zones_channel; + + int* channel_index; + + int ch32[32] = {5, 4, 3, 2, 1, 0, 15, 14, 26, 27, 28, 29, 30, 31, 8, 9, 19, 18, 17, 16, 7, 6, 25, 24, 23, 22, 21, 20, 13, 12, 11, 10}; + int ch16[32] = {19, 18, 17, 16, 24, 25, 26, 27, 20, 21, 22, 23, 31, 30, 29, 28, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + int n16[16] = {3, 2, 1, 0, 8, 9, 10, 11, 4, 5, 6, 7, 15, 14, 13, 12}; +}; diff --git a/Controllers/NvidiaESAController/NvidiaESAController.cpp b/Controllers/NvidiaESAController/NvidiaESAController.cpp new file mode 100644 index 0000000..87083c5 --- /dev/null +++ b/Controllers/NvidiaESAController/NvidiaESAController.cpp @@ -0,0 +1,66 @@ +/*---------------------------------------------------------*\ +| NvidiaESAController.cpp | +| | +| Driver for NVIDIA ESA | +| | +| Morgan Guimard (morg) 18 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "NvidiaESAController.h" +#include "StringUtils.h" + +NvidiaESAController::NvidiaESAController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +NvidiaESAController::~NvidiaESAController() +{ + hid_close(dev); +} + +std::string NvidiaESAController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string NvidiaESAController::GetNameString() +{ + return(name); +} + +std::string NvidiaESAController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void NvidiaESAController::SetZoneColor(unsigned int zone_idx, RGBColor color) +{ + unsigned char red = (unsigned char)(0x0F - 0x0F * RGBGetRValue(color) / 255.0f); + unsigned char grn = (unsigned char)(0x0F - 0x0F * RGBGetGValue(color) / 255.0f); + unsigned char blu = (unsigned char)(0x0F - 0x0F * RGBGetBValue(color) / 255.0f); + + unsigned char usb_buf[4]; + + usb_buf[0x00] = 0x42 + zone_idx; + + usb_buf[0x01] = red; + usb_buf[0x02] = grn; + usb_buf[0x03] = blu; + + hid_write(dev, usb_buf, 4); +} diff --git a/Controllers/NvidiaESAController/NvidiaESAController.h b/Controllers/NvidiaESAController/NvidiaESAController.h new file mode 100644 index 0000000..5a9136c --- /dev/null +++ b/Controllers/NvidiaESAController/NvidiaESAController.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| NvidiaESAController.h | +| | +| Driver for NVIDIA ESA | +| | +| Morgan Guimard (morg) 18 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class NvidiaESAController +{ +public: + NvidiaESAController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~NvidiaESAController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetZoneColor(unsigned int zone_idx, RGBColor color); + +protected: + hid_device* dev; + +private: + std::string location; + std::string name; +}; diff --git a/Controllers/NvidiaESAController/NvidiaESAControllerDetect.cpp b/Controllers/NvidiaESAController/NvidiaESAControllerDetect.cpp new file mode 100644 index 0000000..156136f --- /dev/null +++ b/Controllers/NvidiaESAController/NvidiaESAControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| NvidiaESAControllerDetect.cpp | +| | +| Detector for NVIDIA ESA | +| | +| Morgan Guimard (morg) 18 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "NvidiaESAController.h" +#include "RGBController_NvidiaESA.h" + +/*---------------------------------------------------------*\ +| NVIDIA ESA vendor ID | +\*---------------------------------------------------------*/ +#define NVIDIA_ESA_VID 0x0955 + +/*---------------------------------------------------------*\ +| NVIDIA ESA product ID | +\*---------------------------------------------------------*/ +#define NVIDIA_ESA_DELL_XPS_730X_PID 0x000A + +void DetectNvidiaESAControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + NvidiaESAController* controller = new NvidiaESAController(dev, *info, name); + RGBController_NvidiaESA* rgb_controller = new RGBController_NvidiaESA(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("Nvidia ESA - Dell XPS 730x", DetectNvidiaESAControllers, NVIDIA_ESA_VID, NVIDIA_ESA_DELL_XPS_730X_PID, 0xFFDE, 0x02); diff --git a/Controllers/NvidiaESAController/RGBController_NvidiaESA.cpp b/Controllers/NvidiaESAController/RGBController_NvidiaESA.cpp new file mode 100644 index 0000000..4329368 --- /dev/null +++ b/Controllers/NvidiaESAController/RGBController_NvidiaESA.cpp @@ -0,0 +1,113 @@ +/*---------------------------------------------------------*\ +| RGBController_NvidiaESA.cpp | +| | +| RGBController for NVIDIA ESA | +| | +| Morgan Guimard (morg) 18 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_NvidiaESA.h" + +/**------------------------------------------------------------------*\ + @name Nvidia ESA + @category Case + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectNvidiaESAControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_NvidiaESA::RGBController_NvidiaESA(NvidiaESAController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "NVIDIA"; + type = DEVICE_TYPE_CASE; + description = "Nvidia ESA USB Device";; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.value = 0x00; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + SetupZones(); +} + +RGBController_NvidiaESA::~RGBController_NvidiaESA() +{ + delete controller; +} + +void RGBController_NvidiaESA::SetupZones() +{ + std::vector zone_names = + { + "Front Drive Bays", // 0x42 + "Front USB", // 0x43 + "Rear", // 0x44 + "Internal", // 0x45 + "Front Audio" // 0x46 + }; + + for(const std::string& zone_name: zone_names) + { + zone new_zone; + + new_zone.name = zone_name; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + led new_led; + new_led.name = "LED"; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_NvidiaESA::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_NvidiaESA::DeviceUpdateLEDs() +{ + for(unsigned int zone = 0; zone < zones.size(); zone++) + { + UpdateZoneLEDs(zone); + } +} + +void RGBController_NvidiaESA::UpdateZoneLEDs(int zone) +{ + controller->SetZoneColor(zone, zones[zone].colors[0]); +} + +void RGBController_NvidiaESA::UpdateSingleLED(int /*led*/) +{ + UpdateZoneLEDs(0); +} + +void RGBController_NvidiaESA::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/NvidiaESAController/RGBController_NvidiaESA.h b/Controllers/NvidiaESAController/RGBController_NvidiaESA.h new file mode 100644 index 0000000..619bb64 --- /dev/null +++ b/Controllers/NvidiaESAController/RGBController_NvidiaESA.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_NvidiaESA.h | +| | +| RGBController for NVIDIA ESA | +| | +| Morgan Guimard (morg) 18 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "NvidiaESAController.h" + +class RGBController_NvidiaESA : public RGBController +{ +public: + RGBController_NvidiaESA(NvidiaESAController* controller_ptr); + ~RGBController_NvidiaESA(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + NvidiaESAController* controller; +}; diff --git a/Controllers/OKSController/OKSKeyboardController.cpp b/Controllers/OKSController/OKSKeyboardController.cpp new file mode 100644 index 0000000..8bd8431 --- /dev/null +++ b/Controllers/OKSController/OKSKeyboardController.cpp @@ -0,0 +1,184 @@ +/*---------------------------------------------------------*\ +| OKSKeyboardController.cpp | +| | +| Driver for OKS keyboard | +| | +| Merafour (OKS) 24 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "OKSKeyboardController.h" +#include "StringUtils.h" + +OKSKeyboardController::OKSKeyboardController(hid_device* dev_handle, const char* path, const unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + usb_pid = pid; + + SendInitialize(); +} + +OKSKeyboardController::~OKSKeyboardController() +{ + hid_close(dev); +} + +std::string OKSKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string OKSKeyboardController::GetNameString() +{ + return(name); +} + +std::string OKSKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned short OKSKeyboardController::GetUSBPID() +{ + return(usb_pid); +} + +void OKSKeyboardController::SendColors(unsigned char* color_data, unsigned int /*color_data_size*/) +{ + char usb_buf[65]; + union kb2_port_t Pack; + uint8_t cnt; + uint8_t red,green,blue; + uint16_t color_idx; + uint32_t irgb[14]; + uint16_t pos=0; + for(color_idx=0; color_idx<(6*21); color_idx += 14) + { + for(cnt=0; cnt<14; cnt++) + { + pos = color_idx+cnt; + red = color_data[pos*3+0]; + green = color_data[pos*3+1]; + blue = color_data[pos*3+2]; + irgb[cnt] = blue&0xFF; + irgb[cnt] <<= 8; + irgb[cnt] |= green&0xFF; + irgb[cnt] <<= 8; + irgb[cnt] |= red&0xFF; + irgb[cnt] <<= 8; + irgb[cnt] |= pos&0xFF; + } + kb2M_wled(&Pack, irgb); + usb_buf[0] = 0x04; + for(uint8_t i=0; i<64; i++) usb_buf[i+1] = Pack.bin[i]; + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, (unsigned char *)usb_buf, 65); + } + std::this_thread::sleep_for(std::chrono::milliseconds(5)); +} + +void OKSKeyboardController::SendKeyboardModeEx(const mode &m, unsigned char /*red*/, unsigned char /*green*/, unsigned char /*blue*/) +{ + union kb2_port_t Pack; + kb2M_wrgb(&Pack, m.brightness, m.value, m.speed, m.direction); + Send(Pack.bin, Pack.length+KB2_HEAD_SIZE); +} + +void OKSKeyboardController::Send(const uint8_t bin[], const uint16_t len) +{ + char usb_buf[65]; + uint16_t Len; + uint16_t pos; + pos=0; + while(pos64) Len=64; + for(uint8_t i=0; ilength; + + checksum += Pack->head; + checksum += len; + checksum += Pack->cmd; + if((len>0) && (len<=KB2_DATA_SIZE)) checksum += Pack->data[len-1]; + return checksum; +} +int OKSKeyboardController::kb2_add_32b(union kb2_port_t* const Pack, const uint32_t value) +{ + union uint32_kb2 Value; + if((Pack->length+4)>KB2_DATA_SIZE) return -1; + Value.data = value; + Pack->data[Pack->length++] = Value.Byte0; + Pack->data[Pack->length++] = Value.Byte1; + Pack->data[Pack->length++] = Value.Byte2; + Pack->data[Pack->length++] = Value.Byte3; + return 4; +} +void OKSKeyboardController::kb2M_init(union kb2_port_t* const Pack, const enum kb2_cmd cmd) +{ + Pack->head = KB2_PACK_HEAD; + Pack->length = 0; + Pack->cmd = cmd; + Pack->checksum = 0; +} +void OKSKeyboardController::kb2M_wrgb(union kb2_port_t* const Pack, const uint8_t bright, const uint8_t mode, const uint8_t speed, const uint8_t dir) +{ + kb2M_init(Pack, KB2_CMD_WRGB); + kb2_add_32b(Pack, bright); + Pack->data[Pack->length++] = mode; + Pack->data[Pack->length++] = speed; + Pack->data[Pack->length++] = dir; + Pack->data[Pack->length++] = 0xFF; + Pack->data[Pack->length++] = 0xFF; + Pack->checksum = kb2_ComputeChecksum(Pack); +} +void OKSKeyboardController::kb2M_wled(union kb2_port_t* const Pack, const uint32_t irgb[14]) +{ + kb2M_init(Pack, KB2_CMD_WLED); + for(uint8_t i=0; i<14; i++) kb2_add_32b(Pack, irgb[i]); + Pack->data[Pack->length++] = 0xFF; + Pack->data[Pack->length++] = 0xFF; + Pack->checksum = kb2_ComputeChecksum(Pack); +} diff --git a/Controllers/OKSController/OKSKeyboardController.h b/Controllers/OKSController/OKSKeyboardController.h new file mode 100644 index 0000000..c8079af --- /dev/null +++ b/Controllers/OKSController/OKSKeyboardController.h @@ -0,0 +1,94 @@ +/*---------------------------------------------------------*\ +| OKSKeyboardController.h | +| | +| Driver for OKS keyboard | +| | +| Merafour (OKS) 24 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +/*-----------------------------------------------------*\ +| OKS vendor ID | +\*-----------------------------------------------------*/ +#define OKS_VID 0x1C4F + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define OKS_OPTICAL_RGB_PID 0xEE88 + +/*-----------------------------------------------------*\ +| Communication protocol | +\*-----------------------------------------------------*/ +#define KB2_PACK_HEAD (0x5C&0xFF) +#define KB2_HEAD_SIZE 4 +#define KB2_PORT_SIZE (64*4) +#define KB2_DATA_SIZE (KB2_PORT_SIZE-KB2_HEAD_SIZE) +union kb2_port_t +{ + uint8_t bin[KB2_PORT_SIZE]; + struct + { + uint8_t head; + uint8_t length; + uint8_t cmd; + uint8_t checksum; + uint8_t data[KB2_DATA_SIZE]; + }; +}; +enum kb2_cmd +{ + KB2_CMD_RRGB = 0x14, + KB2_CMD_WRGB = 0x15, + KB2_CMD_RLED = 0x16, + KB2_CMD_WLED = 0x17, +}; +union uint32_kb2 +{ + uint32_t data; + struct + { + uint8_t Byte0; + uint8_t Byte1; + uint8_t Byte2; + uint8_t Byte3; + }; +}; + +class OKSKeyboardController +{ +public: + OKSKeyboardController(hid_device* dev_handle, const char* path, const unsigned short pid, std::string dev_name); + ~OKSKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + unsigned short GetUSBPID(); + + void SendColors(unsigned char* color_data, unsigned int color_data_size); + void SendKeyboardModeEx(const mode &m, unsigned char red, unsigned char green, unsigned char blue); + +private: + hid_device* dev; + std::string location; + std::string name; + unsigned short usb_pid; + + void Send(const uint8_t bin[64], const uint16_t len); + void SendInitialize(); + uint8_t kb2_ComputeChecksum(const union kb2_port_t* const Pack); + int kb2_add_32b(union kb2_port_t* const Pack, const uint32_t value); + void kb2M_init(union kb2_port_t* const Pack, const enum kb2_cmd cmd); + void kb2M_wrgb(union kb2_port_t* const Pack, const uint8_t bright, const uint8_t mode, const uint8_t speed, const uint8_t dir); + void kb2M_wled(union kb2_port_t* const Pack, const uint32_t irgb[14]); +}; diff --git a/Controllers/OKSController/OKSKeyboardControllerDetect.cpp b/Controllers/OKSController/OKSKeyboardControllerDetect.cpp new file mode 100644 index 0000000..5b3ef90 --- /dev/null +++ b/Controllers/OKSController/OKSKeyboardControllerDetect.cpp @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| OKSKeyboardControllerDetect.cpp | +| | +| Detector for OKS keyboard | +| | +| Merafour (OKS) 24 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "OKSKeyboardController.h" +#include "RGBController_OKSKeyboard.h" + +/******************************************************************************************\ +* DetectOKSKeyboardControllers * +* Reference: DuckyKeyboardController * +* Tests the USB address to see if a OKS Optical Axis RGB Keyboard controller exists there.* +* Reference:DetectDuckyKeyboardControllers * +\******************************************************************************************/ + +void DetectOKSKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + OKSKeyboardController* controller = new OKSKeyboardController(dev, info->path, info->product_id, name); + RGBController_OKSKeyboard* rgb_controller = new RGBController_OKSKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectOKSKeyboardControllers() */ + +REGISTER_HID_DETECTOR_I("OKS Optical Axis RGB", DetectOKSKeyboardControllers, OKS_VID, OKS_OPTICAL_RGB_PID, 1); diff --git a/Controllers/OKSController/RGBController_OKSKeyboard.cpp b/Controllers/OKSController/RGBController_OKSKeyboard.cpp new file mode 100644 index 0000000..d9c1d65 --- /dev/null +++ b/Controllers/OKSController/RGBController_OKSKeyboard.cpp @@ -0,0 +1,299 @@ +/*---------------------------------------------------------*\ +| RGBController_OKSKeyboard.cpp | +| | +| RGBController for OKS keyboard | +| | +| Merafour (OKS) 24 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_OKSKeyboard.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map_optical[6][21] = + { { 0, NA, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, NA, NA, NA, NA }, + { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41 }, + { 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62 }, + { 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, NA, 76, NA, NA, NA, 80, 81, 82, NA }, + { 84, NA, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, NA, 97, NA, 99, NA, 101, 102, 103, 104 }, + { 105, 106, 107, NA, NA, NA, 111, NA, NA, NA, 115, 116, 117, 118, 119, 120, 121, 122, NA, 124, NA } }; + +static unsigned int matrix_kb87_map[6][17] = +{ { 0, NA, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + { 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33}, + { 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50}, + { 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, NA, 64, NA, NA, NA}, + { 68, NA, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, NA, 81, NA, 83, NA}, + { 85, 86, 87, NA, NA, NA, 91, NA, NA, NA, 95, 96, 97, 98, 99, 100, 101} }; + +static const char* zone_names[] = +{ + "Keyboard", +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes_optical[] = +{ + 126 +}; + +static const unsigned int zone_sizes_kb87[] = +{ + 102 +}; + +static const char *led_names[] = +{ + // R0--0 + KEY_EN_ESCAPE, KEY_EN_UNUSED, KEY_EN_F1, KEY_EN_F2, KEY_EN_F3, KEY_EN_F4, KEY_EN_F5, KEY_EN_F6, KEY_EN_F7, KEY_EN_F8, KEY_EN_F9, KEY_EN_F10, KEY_EN_F11, KEY_EN_F12, KEY_EN_PRINT_SCREEN, KEY_EN_SCROLL_LOCK, KEY_EN_PAUSE_BREAK, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_UNUSED, + // R1--21 + KEY_EN_BACK_TICK, KEY_EN_1, KEY_EN_2, KEY_EN_3, KEY_EN_4, KEY_EN_5, KEY_EN_6, KEY_EN_7, KEY_EN_8, KEY_EN_9, KEY_EN_0, KEY_EN_MINUS, KEY_EN_EQUALS, KEY_EN_BACKSPACE, KEY_EN_INSERT, KEY_EN_HOME, KEY_EN_PAGE_UP, KEY_EN_NUMPAD_LOCK, KEY_EN_NUMPAD_DIVIDE, KEY_EN_NUMPAD_TIMES, KEY_EN_NUMPAD_MINUS, + // R2--42 + KEY_EN_TAB, KEY_EN_Q, KEY_EN_W, KEY_EN_E, KEY_EN_R, KEY_EN_T, KEY_EN_Y, KEY_EN_U, KEY_EN_I, KEY_EN_O, KEY_EN_P, KEY_EN_LEFT_BRACKET, KEY_EN_RIGHT_BRACKET, KEY_EN_ANSI_BACK_SLASH, KEY_EN_DELETE, KEY_EN_END, KEY_EN_PAGE_DOWN, KEY_EN_NUMPAD_7, KEY_EN_NUMPAD_8, KEY_EN_NUMPAD_9, KEY_EN_NUMPAD_PLUS, + // R3--63 + KEY_EN_CAPS_LOCK, KEY_EN_A, KEY_EN_S, KEY_EN_D, KEY_EN_F, KEY_EN_G, KEY_EN_H, KEY_EN_J, KEY_EN_K, KEY_EN_L, KEY_EN_SEMICOLON, KEY_EN_QUOTE, KEY_EN_UNUSED, KEY_EN_ANSI_ENTER, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_NUMPAD_4, KEY_EN_NUMPAD_5, KEY_EN_NUMPAD_6, KEY_EN_UNUSED, + // R4--84 + KEY_EN_LEFT_SHIFT, KEY_EN_UNUSED, KEY_EN_Z, KEY_EN_X, KEY_EN_C, KEY_EN_V, KEY_EN_B, KEY_EN_N, KEY_EN_M, KEY_EN_COMMA, KEY_EN_PERIOD, KEY_EN_FORWARD_SLASH, KEY_EN_UNUSED, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEY_EN_NUMPAD_1, KEY_EN_NUMPAD_2, KEY_EN_NUMPAD_3, KEY_EN_NUMPAD_ENTER, + // R5--105 + KEY_EN_LEFT_CONTROL, KEY_EN_LEFT_WINDOWS, KEY_EN_LEFT_ALT, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_SPACE, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_RIGHT_ALT, KEY_EN_RIGHT_FUNCTION, KEY_EN_MENU, KEY_EN_RIGHT_CONTROL, KEY_EN_LEFT_ARROW, KEY_EN_DOWN_ARROW, KEY_EN_RIGHT_ARROW, KEY_EN_NUMPAD_0, KEY_EN_UNUSED, KEY_EN_NUMPAD_PERIOD, KEY_EN_UNUSED, + // "Key: Calculator", +}; + +static const char *led_kb87_names[] = +{ + // R0--0 + KEY_EN_ESCAPE, KEY_EN_UNUSED, KEY_EN_F1, KEY_EN_F2, KEY_EN_F3, KEY_EN_F4, KEY_EN_F5, KEY_EN_F6, KEY_EN_F7, KEY_EN_F8, KEY_EN_F9, KEY_EN_F10, KEY_EN_F11, KEY_EN_F12, KEY_EN_PRINT_SCREEN, KEY_EN_SCROLL_LOCK, KEY_EN_PAUSE_BREAK, + // R1--17 + KEY_EN_BACK_TICK, KEY_EN_1, KEY_EN_2, KEY_EN_3, KEY_EN_4, KEY_EN_5, KEY_EN_6, KEY_EN_7, KEY_EN_8, KEY_EN_9, KEY_EN_0, KEY_EN_MINUS, KEY_EN_EQUALS, KEY_EN_BACKSPACE, KEY_EN_INSERT, KEY_EN_HOME, KEY_EN_PAGE_UP, + // R2--34 + KEY_EN_TAB, KEY_EN_Q, KEY_EN_W, KEY_EN_E, KEY_EN_R, KEY_EN_T, KEY_EN_Y, KEY_EN_U, KEY_EN_I, KEY_EN_O, KEY_EN_P, KEY_EN_LEFT_BRACKET, KEY_EN_RIGHT_BRACKET, KEY_EN_ANSI_BACK_SLASH, KEY_EN_DELETE, KEY_EN_END, KEY_EN_PAGE_DOWN, + // R3--51 + KEY_EN_CAPS_LOCK, KEY_EN_A, KEY_EN_S, KEY_EN_D, KEY_EN_F, KEY_EN_G, KEY_EN_H, KEY_EN_J, KEY_EN_K, KEY_EN_L, KEY_EN_SEMICOLON, KEY_EN_QUOTE, KEY_EN_UNUSED, KEY_EN_ANSI_ENTER, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_UNUSED, + // R4--68 + KEY_EN_LEFT_SHIFT, KEY_EN_UNUSED, KEY_EN_Z, KEY_EN_X, KEY_EN_C, KEY_EN_V, KEY_EN_B, KEY_EN_N, KEY_EN_M, KEY_EN_COMMA, KEY_EN_PERIOD, KEY_EN_FORWARD_SLASH, KEY_EN_UNUSED, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEY_EN_UP_ARROW, KEY_EN_UNUSED, + // R5--85 + KEY_EN_LEFT_CONTROL, KEY_EN_LEFT_WINDOWS, KEY_EN_LEFT_ALT, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_SPACE, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_UNUSED, KEY_EN_RIGHT_ALT, KEY_EN_RIGHT_FUNCTION, KEY_EN_MENU, KEY_EN_RIGHT_CONTROL, KEY_EN_LEFT_ARROW, KEY_EN_DOWN_ARROW, KEY_EN_RIGHT_ARROW, KEY_EN_NUMPAD_0, + // "Key: Calculator", +}; +enum +{ + OKS_SPEED_SLOWEST = 0x00, // Slowest speed + OKS_SPEED_SLOWER = 0x01, // Slower speed + OKS_SPEED_SLOW = 0x02, // Slow speed + OKS_SPEED_SLOWISH = 0x02, // Slowish speed + OKS_SPEED_NORMAL = 0x02, // Normal speed + OKS_SPEED_FASTISH = 0x03, // Fastish speed + OKS_SPEED_FAST = 0x04, // Fast speed + OKS_SPEED_FASTER = 0x05, // Faster speed + OKS_SPEED_FASTEST = 0x06, // Fastest speed +}; +/**------------------------------------------------------------------*\ + @name OKS Keyboard | + @category Keyboard | + @type USB | + @save :x: | + @direct :white_check_mark: | + @effects :x: | + @detectors DetectOKSKeyboardControllers | + @comment | +\*-------------------------------------------------------------------*/ + +RGBController_OKSKeyboard::RGBController_OKSKeyboard(OKSKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "OKS"; + type = DEVICE_TYPE_KEYBOARD; + description = "OKS Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = UP_RGB_MODES_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS ; + Direct.brightness_min = 0; + Direct.brightness_max = 5; + Direct.brightness = 2; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.speed_min = OKS_SPEED_FASTEST; + Direct.speed_max = OKS_SPEED_SLOWEST; + Direct.speed = OKS_SPEED_NORMAL; + Direct.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Direct); + + mode udef = Direct; + udef.name = "User mode1"; + udef.value = UP_RGB_MODES_UDEF1; + udef.direction = MODE_DIRECTION_LEFT; + udef.speed = 0; + modes.push_back(udef); + udef.name = "User mode2"; + udef.value = UP_RGB_MODES_UDEF2; + modes.push_back(udef); + udef.name = "User mode3"; + udef.value = UP_RGB_MODES_UDEF3; + modes.push_back(udef); + udef.name = "User mode4"; + udef.value = UP_RGB_MODES_UDEF4; + modes.push_back(udef); + udef.name = "User mode5"; + udef.value = UP_RGB_MODES_UDEF5; + modes.push_back(udef); + /*---------------------------------------------------------*\ + | Delete the "Horse race lamp","Breathing"... mode | + \*---------------------------------------------------------*/ + SetupZones(); +} + +RGBController_OKSKeyboard::~RGBController_OKSKeyboard() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_OKSKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + unsigned int zone_size = 0; + unsigned int matrix_width = 0; + unsigned int* matrix_map_ptr = NULL; + + switch(serial.c_str()[0]) + { + case 'B': + zone_size = zone_sizes_kb87[zone_idx]; + matrix_width = 17; + matrix_map_ptr = (unsigned int *)&matrix_kb87_map; + break; + default: + zone_size = zone_sizes_optical[zone_idx]; + matrix_width = 21; + matrix_map_ptr = (unsigned int *)&matrix_map_optical; + break; + } + + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_size; + new_zone.leds_max = zone_size; + new_zone.leds_count = zone_size; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = matrix_width; + new_zone.matrix_map->map = matrix_map_ptr; + zones.push_back(new_zone); + + total_led_count += zone_size; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + switch(serial.c_str()[0]) + { + case 'B': + new_led.name = led_kb87_names[led_idx]; + break; + + default: + new_led.name = led_names[led_idx]; + break; + } + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_OKSKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_OKSKeyboard::DeviceUpdateLEDs() +{ + unsigned char colordata[155*3]; + unsigned int width; + unsigned int kb_idx; + unsigned int row_idx; + unsigned int col_idx; + + width = zones[0].matrix_map->width; + for(std::size_t color_idx = 0; color_idx < 155; color_idx++) + { + colordata[(color_idx*3)+0] = 0x00; + colordata[(color_idx*3)+1] = 0x00; + colordata[(color_idx*3)+2] = 0x00; + } + /*---------------------------------------------------------*\ + | send 6x21 matrix | + \*---------------------------------------------------------*/ + for(std::size_t color_idx = 0; color_idx < colors.size(); color_idx++) + { + row_idx = (unsigned int)(color_idx) / width; + col_idx = (unsigned int)(color_idx) % width; + kb_idx = row_idx*21+col_idx; + colordata[(kb_idx*3)+0] = RGBGetRValue(colors[color_idx]); + colordata[(kb_idx*3)+1] = RGBGetGValue(colors[color_idx]); + colordata[(kb_idx*3)+2] = RGBGetBValue(colors[color_idx]); + } + + controller->SendColors(colordata, (unsigned int)colors.size() * 3); +} + +void RGBController_OKSKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_OKSKeyboard::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_OKSKeyboard::DeviceUpdateMode() +{ + mode m = modes[active_mode]; + unsigned char red = 0x00; + unsigned char grn = 0x00; + unsigned char blu = 0x00; + //unsigned char random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + + if(modes[active_mode].colors.size() > 0) + { + red = RGBGetRValue(modes[active_mode].colors[0]); + grn = RGBGetGValue(modes[active_mode].colors[0]); + blu = RGBGetBValue(modes[active_mode].colors[0]); + } + + controller->SendKeyboardModeEx(m, red, grn, blu); +} diff --git a/Controllers/OKSController/RGBController_OKSKeyboard.h b/Controllers/OKSController/RGBController_OKSKeyboard.h new file mode 100644 index 0000000..13a00db --- /dev/null +++ b/Controllers/OKSController/RGBController_OKSKeyboard.h @@ -0,0 +1,73 @@ +/*---------------------------------------------------------*\ +| RGBController_OKSKeyboard.h | +| | +| RGBController for OKS keyboard | +| | +| Merafour (OKS) 24 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "OKSKeyboardController.h" + +/*-----------------------------------------------------*\ +| mode | +\*-----------------------------------------------------*/ +enum user_param_view_mode_t +{ + UP_RGB_MODES_MASK = 0x7F, + UP_RGB_PAUSE__MASK = 0x80, + UP_RGB_PAUSE_ON = 0x80, + UP_RGB_PAUSE_OFF = 0x00, + /*------------------------------------------------------------*\ + | direct mode,eg:OpenRGB+Artemis | + \*------------------------------------------------------------*/ + UP_RGB_MODES_DIRECT = 0x00, + /*------------------------------------------------------------*\ + | user define mode | + \*------------------------------------------------------------*/ + UP_RGB_MODES_UDEF1 = 0x01, + UP_RGB_MODES_UDEF2 = 0x02, + UP_RGB_MODES_UDEF3 = 0x03, + UP_RGB_MODES_UDEF4 = 0x04, + UP_RGB_MODES_UDEF5 = 0x05, + UP_RGB_MODES_RECORD = 0x06, + /*------------------------------------------------------------*\ + | default mode | + \*------------------------------------------------------------*/ + UP_RGB_MODES_RACE = 0x07, + UP_RGB_MODES_BREAT = 0x08, + UP_RGB_MODES_RIPPLE = 0x09, + UP_RGB_MODES_DIFF = 0x0A, + UP_RGB_MODES_WAVE = 0x0B, + UP_RGB_MODES_CLICK = 0x0C, + UP_RGB_MODES_BRIGHT = 0x0D, + UP_RGB_MODES_LUMA_CYCLE = 0x0E, + UP_RGB_MODES_ROTATE = 0x0F, + UP_RGB_MODES_HID_MENU = 0x10, + UP_RGB_MODES_NONE = 0x12, +}; + +class RGBController_OKSKeyboard : public RGBController +{ +public: + RGBController_OKSKeyboard(OKSKeyboardController* controller_ptr); + ~RGBController_OKSKeyboard(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + OKSKeyboardController* controller; +}; diff --git a/Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUController.cpp b/Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUController.cpp new file mode 100644 index 0000000..1444e73 --- /dev/null +++ b/Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUController.cpp @@ -0,0 +1,78 @@ +/*---------------------------------------------------------*\ +| PNYARGBEpicXGPUController.cpp | +| | +| Driver for PNY ARGB Epic-X GPU | +| | +| Peter Berendi 27 Apr 2025 | +| Adam Honse 01 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "PNYARGBEpicXGPUController.h" + +PNYARGBEpicXGPUController::PNYARGBEpicXGPUController(i2c_smbus_interface* bus, unsigned char init_i2c_addr, std::string name, bool large_variant) +{ + this->bus = bus; + this->i2c_addr = init_i2c_addr; + this->name = name; + this->large_variant = large_variant; +} + +PNYARGBEpicXGPUController::~PNYARGBEpicXGPUController() +{ + +} + +std::string PNYARGBEpicXGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", i2c_addr); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string PNYARGBEpicXGPUController::GetDeviceName() +{ + return(name); +} + +bool PNYARGBEpicXGPUController::IsLargeVariant() +{ + return(large_variant); +} + +void PNYARGBEpicXGPUController::SetZoneMode(unsigned char zone, unsigned char mode, unsigned char speed, unsigned char brightness, unsigned char subcmd, RGBColor color) +{ + unsigned char data[7] = + { + mode, + brightness, + speed, + subcmd, + (unsigned char)RGBGetRValue(color), + (unsigned char)RGBGetGValue(color), + (unsigned char)RGBGetBValue(color) + }; + + bus->i2c_smbus_write_i2c_block_data(i2c_addr, zone, sizeof(data), data); +} + +void PNYARGBEpicXGPUController::SetLEDDirect(unsigned char zone, unsigned char led, unsigned char mode, RGBColor color) +{ + unsigned char data[7] = + { + mode, + 0xFF, + led, + 0x00, + (unsigned char)RGBGetRValue(color), + (unsigned char)RGBGetGValue(color), + (unsigned char)RGBGetBValue(color) + }; + + bus->i2c_smbus_write_i2c_block_data(i2c_addr, zone, sizeof(data), data); +} diff --git a/Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUController.h b/Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUController.h new file mode 100644 index 0000000..b14591a --- /dev/null +++ b/Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUController.h @@ -0,0 +1,64 @@ +/*---------------------------------------------------------*\ +| PNYARGBEpicXGPUController.h | +| | +| Driver for PNY ARGB Epic-X GPU | +| | +| Peter Berendi 27 Apr 2025 | +| Adam Honse 01 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +enum +{ + PNY_GPU_MODE_ARGB_OFF = 0x00, + PNY_GPU_MODE_ARGB_BREATH = 0x0302, + PNY_GPU_MODE_ARGB_CYCLE = 0x03, + PNY_GPU_MODE_ARGB_NEON = 0x04, + PNY_GPU_MODE_ARGB_EXPLOSION = 0x05, + PNY_GPU_MODE_ARGB_SUPERNOVA = 0x06, + PNY_GPU_MODE_ARGB_INFINITY = 0x07, + PNY_GPU_MODE_ARGB_STREAMER = 0x08, + PNY_GPU_MODE_ARGB_DIRECT = 0x09, + PNY_GPU_MODE_ARGB_WAVE = 0x0A, +}; + +/*---------------------------------------------------------*\ +| The 5070Ti only uses FRONT and ARROW, the LOGO zone is | +| used by the 5090 | +\*---------------------------------------------------------*/ +enum +{ + PNY_GPU_REG_ZONE_ARROW = 0x02, + PNY_GPU_REG_ZONE_FRONT = 0x04, + PNY_GPU_REG_ZONE_LOGO = 0x0F, + PNY_GPU_REG_DETECT = 0x81, +}; + +class PNYARGBEpicXGPUController +{ +public: + PNYARGBEpicXGPUController(i2c_smbus_interface* bus, unsigned char init_i2c_addr, std::string name, bool large_variant); + ~PNYARGBEpicXGPUController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + bool IsLargeVariant(); + + void SetZoneMode(unsigned char zone, unsigned char mode, unsigned char speed, unsigned char brightness, unsigned char subcmd, RGBColor color); + void SetLEDDirect(unsigned char zone, unsigned char led, unsigned char mode, RGBColor color); + +private: + i2c_smbus_interface* bus; + unsigned char i2c_addr; + std::string name; + bool large_variant; +}; diff --git a/Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUControllerDetect.cpp b/Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUControllerDetect.cpp new file mode 100644 index 0000000..4cf7a87 --- /dev/null +++ b/Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUControllerDetect.cpp @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| PNYARGBEpicXGPUControllerDetect.cpp | +| | +| Detector for PNY ARGB Epic-X GPU | +| | +| Peter Berendi 27 Apr 2025 | +| Adam Honse 01 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "i2c_smbus.h" +#include "LogManager.h" +#include "pci_ids.h" +#include "PNYARGBEpicXGPUController.h" +#include "RGBController_PNYARGBEpicXGPU.h" + +void DetectPNYARGBEpicXGPUSmallControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(bus->port_id == 1) + { + PNYARGBEpicXGPUController* controller = new PNYARGBEpicXGPUController(bus, i2c_addr, name, false); + RGBController_PNYARGBEpicXGPU* rgb_controller = new RGBController_PNYARGBEpicXGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectPNYARGBEpicXGPULargeControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(bus->port_id == 1) + { + PNYARGBEpicXGPUController* controller = new PNYARGBEpicXGPUController(bus, i2c_addr, name, true); + RGBController_PNYARGBEpicXGPU* rgb_controller = new RGBController_PNYARGBEpicXGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5060Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, PNY_SUB_VEN, PNY_RTX_5060TI_ARGB_EPIC_X_OC_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070 ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, PNY_SUB_VEN, PNY_RTX_5070_ARGB_EPIC_X_OC_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5070Ti ARGB Epic-X OC", DetectPNYARGBEpicXGPUSmallControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PNY_SUB_VEN, PNY_RTX_5070TI_ARGB_EPIC_X_OC_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5080 ARGB Epic-X OC", DetectPNYARGBEpicXGPULargeControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, PNY_SUB_VEN, PNY_RTX_5080_ARGB_EPIC_X_OC_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 5090 ARGB Epic-X OC", DetectPNYARGBEpicXGPULargeControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, PNY_SUB_VEN, PNY_RTX_5090_ARGB_EPIC_X_OC_SUB_DEV, 0x60); diff --git a/Controllers/PNYARGBEpicXGPUController/RGBController_PNYARGBEpicXGPU.cpp b/Controllers/PNYARGBEpicXGPUController/RGBController_PNYARGBEpicXGPU.cpp new file mode 100644 index 0000000..279ffd6 --- /dev/null +++ b/Controllers/PNYARGBEpicXGPUController/RGBController_PNYARGBEpicXGPU.cpp @@ -0,0 +1,331 @@ +/*---------------------------------------------------------*\ +| RGBController_PNYARGBEpicXGPU.cpp | +| | +| RGBController for PNY ARGB Epic-X GPU | +| | +| Peter Berendi 27 Apr 2025 | +| Adam Honse 01 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "pci_ids.h" +#include "RGBController_PNYARGBEpicXGPU.h" + +#define __ 0xFFFFFFFF + +/**------------------------------------------------------------------*\ + @name PNY ARGB Epic-X GPU + @category GPU + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectPNYARGBEpicXGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PNYARGBEpicXGPU::RGBController_PNYARGBEpicXGPU(PNYARGBEpicXGPUController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "PNY"; + description = "PNY ARGB Epic-X GPU Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Direct; + Direct.name = "Direct"; + Direct.value = PNY_GPU_MODE_ARGB_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = PNY_GPU_MODE_ARGB_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = PNY_GPU_MODE_ARGB_CYCLE; + Cycle.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.color_mode = MODE_COLORS_RANDOM; + Cycle.speed = 0x09; + Cycle.speed_min = 0x0F; + Cycle.speed_max = 0x00; + Cycle.brightness = 0xFF; + Cycle.brightness_min = 0; + Cycle.brightness_max = 0xFF; + modes.push_back(Cycle); + + mode Neon; + Neon.name = "Neon"; + Neon.value = PNY_GPU_MODE_ARGB_NEON; + Neon.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Neon.color_mode = MODE_COLORS_RANDOM; + Neon.speed = 0x09; + Neon.speed_min = 0x5F; + Neon.speed_max = 0x00; + Neon.brightness = 0xFF; + Neon.brightness_min = 0; + Neon.brightness_max = 0xFF; + modes.push_back(Neon); + +// mode Explosion; +// Explosion.name = "Explosion"; +// Explosion.value = PNY_GPU_MODE_ARGB_EXPLOSION; +// Explosion.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; +// Explosion.color_mode = MODE_COLORS_MODE_SPECIFIC; +// Explosion.brightness = 0xFF; +// Explosion.brightness_min = 0; +// Explosion.brightness_max = 0xFF; +// Explosion.colors_min = 1; +// Explosion.colors_max = 1; +// Explosion.colors.resize(1); +// modes.push_back(Explosion); + +// mode Supernova; +// Supernova.name = "Supernova"; +// Supernova.value = PNY_GPU_MODE_ARGB_SUPERNOVA; +// Supernova.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; +// Supernova.color_mode = MODE_COLORS_RANDOM; +// Supernova.brightness = 0xFF; +// Supernova.brightness_min = 0; +// Supernova.brightness_max = 0xFF; +// modes.push_back(Supernova); + + mode Infinity; + Infinity.name = "Infinity"; + Infinity.value = PNY_GPU_MODE_ARGB_INFINITY; + Infinity.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Infinity.color_mode = MODE_COLORS_RANDOM; + Infinity.speed = 0x09; + Infinity.speed_min = 0x5F; + Infinity.speed_max = 0x00; + Infinity.brightness = 0xFF; + Infinity.brightness_min = 0; + Infinity.brightness_max = 0xFF; + modes.push_back(Infinity); + + mode Streamer; + Streamer.name = "Streamer"; + Streamer.value = PNY_GPU_MODE_ARGB_STREAMER; + Streamer.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Streamer.color_mode = MODE_COLORS_RANDOM; + Streamer.speed = 0x09; + Streamer.speed_min = 0x5F; + Streamer.speed_max = 0x00; + Streamer.brightness = 0xFF; + Streamer.brightness_min = 0; + Streamer.brightness_max = 0xFF; + modes.push_back(Streamer); + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = PNY_GPU_MODE_ARGB_WAVE; + Wave.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Wave.color_mode = MODE_COLORS_RANDOM; + Wave.speed = 0x09; + Wave.speed_min = 0x5F; + Wave.speed_max = 0x00; + Wave.brightness = 0xFF; + Wave.brightness_min = 0; + Wave.brightness_max = 0xFF; + modes.push_back(Wave); + + SetupZones(); + + active_mode = 0; +} + +RGBController_PNYARGBEpicXGPU::~RGBController_PNYARGBEpicXGPU() +{ + delete controller; +} + +void RGBController_PNYARGBEpicXGPU::SetupZones() +{ + /*-----------------------------------------------------*\ + | The side logo zone has 4 LEDs, but they are part of | + | the FRONT register zone | + | | + | This card has two variants, large and small. Large | + | is used on the 5080 and 5090 and has an extra logo | + | zone on the rear of the card as well as additional | + | LEDs in the arrow zone. The small variant is used on | + | the 5070 Ti and lower cards. | + \*-----------------------------------------------------*/ + zone side_logo; + + if(controller->IsLargeVariant()) + { + side_logo.name = "Side Logo"; + } + else + { + side_logo.name = "Logo"; + } + + side_logo.type = ZONE_TYPE_LINEAR; + side_logo.leds_min = 4; + side_logo.leds_max = 4; + side_logo.leds_count = 4; + side_logo.matrix_map = NULL; + zones.push_back(side_logo); + + for(std::size_t led_idx = 0; led_idx < side_logo.leds_count; led_idx++) + { + led side_logo_led; + side_logo_led.name = side_logo.name + " LED " + std::to_string(led_idx); + side_logo_led.value = PNY_GPU_REG_ZONE_FRONT; + leds.push_back(side_logo_led); + zone_led_idx.push_back((unsigned char)(led_idx + 20)); + } + + /*-----------------------------------------------------*\ + | The front zone has 20 LEDs in a figure 8 pattern, the | + | FRONT register has these 20 and then the 4 log LEDs | + \*-----------------------------------------------------*/ + zone front; + front.name = "Front"; + front.type = ZONE_TYPE_LINEAR; + front.leds_min = 20; + front.leds_max = 20; + front.leds_count = 20; + front.matrix_map = NULL; + zones.push_back(front); + + for(std::size_t led_idx = 0; led_idx < front.leds_count; led_idx++) + { + led front_led; + front_led.name = "Front LED " + std::to_string(led_idx); + front_led.value = PNY_GPU_REG_ZONE_FRONT; + leds.push_back(front_led); + zone_led_idx.push_back((unsigned char)led_idx); + } + + /*-----------------------------------------------------*\ + | The arrow zone has 17 LEDs on the small variant, 19 | + | LEDs on the large variant | + \*-----------------------------------------------------*/ + unsigned int arrow_led_count; + + if(controller->IsLargeVariant()) + { + arrow_led_count = 19; + } + else + { + arrow_led_count = 17; + } + + zone arrow; + arrow.name = "Arrow"; + arrow.type = ZONE_TYPE_LINEAR; + arrow.leds_min = arrow_led_count; + arrow.leds_max = arrow_led_count; + arrow.leds_count = arrow_led_count; + arrow.matrix_map = NULL; + zones.push_back(arrow); + + for(std::size_t led_idx = 0; led_idx < arrow.leds_count; led_idx++) + { + led arrow_led; + arrow_led.name = "Arrow LED " + std::to_string(led_idx); + arrow_led.value = PNY_GPU_REG_ZONE_ARROW; + leds.push_back(arrow_led); + zone_led_idx.push_back((unsigned char)led_idx); + } + + /*-----------------------------------------------------*\ + | The rear logo zone is only present on the large | + | variant | + \*-----------------------------------------------------*/ + if(controller->IsLargeVariant()) + { + zone rear_logo; + rear_logo.name = "Rear Logo"; + rear_logo.type = ZONE_TYPE_SINGLE; + rear_logo.leds_min = 1; + rear_logo.leds_max = 1; + rear_logo.leds_count = 1; + rear_logo.matrix_map = NULL; + zones.push_back(rear_logo); + + led rear_logo_led; + rear_logo_led.name = "Rear Logo LED"; + rear_logo_led.value = PNY_GPU_REG_ZONE_LOGO; + leds.push_back(rear_logo_led); + zone_led_idx.push_back(0); + } + + SetupColors(); +} + +void RGBController_PNYARGBEpicXGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PNYARGBEpicXGPU::DeviceUpdateLEDs() +{ + for(std::size_t i = 0; i < leds.size(); i++) + { + UpdateSingleLED((int)i); + } +} + +void RGBController_PNYARGBEpicXGPU::UpdateZoneLEDs(int zone) +{ + for(unsigned int i = 0; i < zones[zone].leds_count; i++) + { + UpdateSingleLED(zones[zone].start_idx + i); + } +} + +void RGBController_PNYARGBEpicXGPU::UpdateSingleLED(int led) +{ + controller->SetLEDDirect(leds[led].value, zone_led_idx[led], PNY_GPU_MODE_ARGB_DIRECT, colors[led]); +} + +void RGBController_PNYARGBEpicXGPU::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_NONE || modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + controller->SetZoneMode(PNY_GPU_REG_ZONE_FRONT, modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, 0, 0); + controller->SetZoneMode(PNY_GPU_REG_ZONE_ARROW, modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, 0, 0); + + if(controller->IsLargeVariant()) + { + controller->SetZoneMode(PNY_GPU_REG_ZONE_LOGO, modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, 0, 0); + } + } + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + controller->SetZoneMode(PNY_GPU_REG_ZONE_FRONT, modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, 0, modes[active_mode].colors[0]); + controller->SetZoneMode(PNY_GPU_REG_ZONE_ARROW, modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, 0, modes[active_mode].colors[0]); + + if(controller->IsLargeVariant()) + { + controller->SetZoneMode(PNY_GPU_REG_ZONE_LOGO, modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, 0, modes[active_mode].colors[0]); + } + } + else if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SetZoneMode(PNY_GPU_REG_ZONE_FRONT, modes[active_mode].value, 0, 0xFF, 0, 0); + controller->SetZoneMode(PNY_GPU_REG_ZONE_ARROW, modes[active_mode].value, 0, 0xFF, 0, 0); + + if(controller->IsLargeVariant()) + { + controller->SetZoneMode(PNY_GPU_REG_ZONE_LOGO, modes[active_mode].value, 0, 0xFF, 0, 0); + } + + DeviceUpdateLEDs(); + } +} diff --git a/Controllers/PNYARGBEpicXGPUController/RGBController_PNYARGBEpicXGPU.h b/Controllers/PNYARGBEpicXGPUController/RGBController_PNYARGBEpicXGPU.h new file mode 100644 index 0000000..e2f02cf --- /dev/null +++ b/Controllers/PNYARGBEpicXGPUController/RGBController_PNYARGBEpicXGPU.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_PNYARGBEpicXGPU.h | +| | +| RGBController for PNY ARGB Epic-X GPU | +| | +| Peter Berendi 27 Apr 2025 | +| Adam Honse 01 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "PNYARGBEpicXGPUController.h" + +class RGBController_PNYARGBEpicXGPU : public RGBController +{ +public: + RGBController_PNYARGBEpicXGPU(PNYARGBEpicXGPUController* controller_ptr); + ~RGBController_PNYARGBEpicXGPU(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +protected: + PNYARGBEpicXGPUController* controller; + std::vector zone_led_idx; +}; diff --git a/Controllers/PNYGPUController/PNYGPUController.cpp b/Controllers/PNYGPUController/PNYGPUController.cpp new file mode 100644 index 0000000..c256a57 --- /dev/null +++ b/Controllers/PNYGPUController/PNYGPUController.cpp @@ -0,0 +1,112 @@ +/*---------------------------------------------------------*\ +| PNYGPUController.cpp | +| | +| Driver for PNY Turing GPU | +| | +| KendallMorgan 17 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "PNYGPUController.h" + +PNYGPUController::PNYGPUController(i2c_smbus_interface* bus, pny_dev_id dev, std::string name) +{ + this->bus = bus; + this->dev = dev; + this->name = name; +} + +PNYGPUController::~PNYGPUController() +{ + +} + +std::string PNYGPUController::GetDeviceName() +{ + return(name); +} + +std::string PNYGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +void PNYGPUController::WriteI2CData(u8 command, u8 length, u8* data) +{ + //Simulating i2c_smbus_write_i2c_block_data(command, length, data); + for (u8 i = 0; i < length; i++) + { + bus->i2c_smbus_write_byte_data(dev, command+i, data[i]); + } +} + +unsigned char PNYGPUController::GetMode() +{ + return(bus->i2c_smbus_read_byte_data(dev, PNY_GPU_MODE_OFF)); +} + +unsigned char PNYGPUController::GetRed() +{ + return(bus->i2c_smbus_read_byte_data(dev, PNY_GPU_REG_COLOR_RED)); +} + +unsigned char PNYGPUController::GetGreen() +{ + return(bus->i2c_smbus_read_byte_data(dev, PNY_GPU_REG_COLOR_GREEN)); +} + +unsigned char PNYGPUController::GetBlue() +{ + return(bus->i2c_smbus_read_byte_data(dev, PNY_GPU_REG_COLOR_BLUE)); +} + +void PNYGPUController::SetOff() +{ + bus->i2c_smbus_write_byte_data(dev, PNY_GPU_REG_CONTROL, 0); + bus->i2c_smbus_write_byte_data(dev, PNY_GPU_REG_MODE, 0x00); +} + +void PNYGPUController::SetCycle(unsigned char speed) +{ + u8 loop[] = { + 0x01, // Direction + 0x00, // ?? + speed, // Speed + 0x1F // Somehow related to speed + }; + loop[2] = speed; + WriteI2CData(PNY_GPU_REG_CONTROL, sizeof(loop), loop); +} + +void PNYGPUController::SetStrobe(unsigned char r, unsigned char g, unsigned char b, unsigned char speed, unsigned char brightness) +{ + u8 strobe[] = { + 0x02, // Strobe + 0x00, // Rise speed + speed, // Cycle length/2 + 0x00, // Off delay + r, // R + g, // G + b, // B + brightness, // Peak brightness + 0x05 // Fade speed + }; + WriteI2CData(PNY_GPU_REG_CONTROL, sizeof(strobe), strobe); +} + +void PNYGPUController::SetDirect(unsigned char red, unsigned char green, unsigned char blue, unsigned char brightness) +{ + bus->i2c_smbus_write_byte_data(dev, PNY_GPU_REG_CONTROL, 0); + bus->i2c_smbus_write_byte_data(dev, PNY_GPU_REG_MODE, 0x01); + bus->i2c_smbus_write_byte_data(dev, PNY_GPU_REG_COLOR_RED, red); + bus->i2c_smbus_write_byte_data(dev, PNY_GPU_REG_COLOR_BLUE, blue); + bus->i2c_smbus_write_byte_data(dev, PNY_GPU_REG_COLOR_GREEN, green); + bus->i2c_smbus_write_byte_data(dev, PNY_GPU_REG_COLOR_BRIGHTNESS, brightness); +} diff --git a/Controllers/PNYGPUController/PNYGPUController.h b/Controllers/PNYGPUController/PNYGPUController.h new file mode 100644 index 0000000..eed65ac --- /dev/null +++ b/Controllers/PNYGPUController/PNYGPUController.h @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| PNYGPUController.h | +| | +| Driver for PNY Turing GPU | +| | +| KendallMorgan 17 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char pny_dev_id; + +enum +{ + PNY_GPU_REG_CONTROL = 0xE0, + PNY_GPU_REG_MODE = 0x60, + PNY_GPU_REG_COLOR_RED = 0x6C, + PNY_GPU_REG_COLOR_GREEN = 0x6D, + PNY_GPU_REG_COLOR_BLUE = 0x6E, + PNY_GPU_REG_COLOR_BRIGHTNESS = 0x6F, +}; + +enum +{ + PNY_GPU_MODE_OFF = 0x00, + PNY_GPU_MODE_DIRECT = 0x01, + PNY_GPU_MODE_CYCLE = 0x02, + PNY_GPU_MODE_STROBE = 0x03, +}; + +class PNYGPUController +{ +public: + PNYGPUController(i2c_smbus_interface* bus, pny_dev_id dev, std::string name); + ~PNYGPUController(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + + void SetOff(); + void SetCycle(unsigned char speed); + void SetStrobe(unsigned char r, unsigned char g, unsigned char b, unsigned char speed, unsigned char brightness); + void SetDirect(unsigned char red, unsigned char green, unsigned char blue, unsigned char brightness); + +private: + i2c_smbus_interface* bus; + pny_dev_id dev; + std::string name; + + void WriteI2CData(u8 command, u8 length, u8* data); + unsigned char GetMode(); + unsigned char GetRed(); + unsigned char GetGreen(); + unsigned char GetBlue(); + +}; diff --git a/Controllers/PNYGPUController/PNYGPUControllerDetect.cpp b/Controllers/PNYGPUController/PNYGPUControllerDetect.cpp new file mode 100644 index 0000000..66c23d7 --- /dev/null +++ b/Controllers/PNYGPUController/PNYGPUControllerDetect.cpp @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| PNYGPUControllerDetect.cpp | +| | +| Detector for PNY Turing GPU | +| | +| KendallMorgan 17 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "PNYGPUController.h" +#include "RGBController_PNYGPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ + * * + * DetectPNYGPUControllers * + * * + * Detect PNY GPU controllers on the enumerated I2C busses at address 0x49. * + * * + * bus - pointer to i2c_smbus_interface where PNY GPU device is connected * + * dev - I2C address of PNY GPU device * + * * +\******************************************************************************************/ + +void DetectPNYGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(bus->port_id != 1) + { + return; + } + + PNYGPUController* controller = new PNYGPUController(bus, i2c_addr, name); + RGBController_PNYGPU* rgb_controller = new RGBController_PNYGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); +} /* DetectPNYGPUControllers() */ + +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 2060 XLR8 OC EDITION", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU104_DEV, PNY_SUB_VEN, PNY_RTX_2060_XLR8_OC_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 3060 XLR8 Revel EPIC-X", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, PNY_SUB_VEN, PNY_RTX_3060_XLR8_REVEL_EPIC_X_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 3070 XLR8 Revel EPIC-X", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, PNY_SUB_VEN, PNY_RTX_3070_XLR8_REVEL_EPIC_X_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 3070 XLR8 Revel EPIC-X LHR", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, PNY_SUB_VEN, PNY_RTX_3070_XLR8_REVEL_EPIC_X_LHR_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 3080 XLR8 Revel EPIC-X", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, PNY_SUB_VEN, PNY_RTX_3080_XLR8_REVEL_EPIC_X_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 3080 Ti XLR8 Revel EPIC-X", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, PNY_SUB_VEN, PNY_RTX_3080TI_XLR8_REVEL_EPIC_X_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 3090 XLR8 Revel EPIC-X", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, PNY_SUB_VEN, PNY_RTX_3090_XLR8_REVEL_EPIC_X_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 2070 SUPER Jetstream", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2070S_OC_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3060", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_DEV, PALIT_SUB_VEN, PALIT_RTX3060_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3060 LHR", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060_LHR_DEV, PALIT_SUB_VEN, PALIT_RTX3060_LHR_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3060 Ti", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, PALIT_SUB_VEN, PALIT_RTX3060TI_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3060 Ti LHR", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_LHR_DEV, PALIT_SUB_VEN, NVIDIA_RTX3060TI_LHR_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3060 Ti Dual", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3060TI_DEV, NVIDIA_SUB_VEN, PALIT_RTX3060TI_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3070", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_DEV, PALIT_SUB_VEN, PALIT_RTX3070_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3070 LHR", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, PALIT_SUB_VEN, PALIT_RTX3070_LHR_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3070 Gamerock LHR", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, PALIT_SUB_VEN, PALIT_RTX3070_GAMEROCK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3070 Ti", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, PALIT_SUB_VEN, PALIT_RTX3070TI_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3070 Ti GamingPro", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, PALIT_SUB_VEN, PALIT_RTX3070TI_GAMING_PRO_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3080", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, PALIT_SUB_VEN, PALIT_RTX3080_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3080 LHR", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, PALIT_SUB_VEN, PALIT_RTX3080_LHR_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3080 Gamerock", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, PALIT_SUB_VEN, PALIT_RTX3080_GAMEROCK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3080 Gamerock LHR", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, PALIT_SUB_VEN, PALIT_RTX3080_GAMEROCK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3080 12GB GamingPro", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, PALIT_SUB_VEN, PALIT_RTX3080_GAMINGPRO_12G_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3080 Ti", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, PALIT_SUB_VEN, PALIT_RTX3080TI_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3080 Ti Gamerock", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, PALIT_SUB_VEN, PALIT_RTX3080TI_GAMEROCK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3090", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, PALIT_SUB_VEN, PALIT_RTX3090_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 3090 Gamerock", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, PALIT_SUB_VEN, PALIT_RTX3090_GAMEROCK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 4070 Ti", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, PALIT_SUB_VEN, PALIT_RTX4070TI_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 4070 Ti Gamerock", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, PALIT_SUB_VEN, PALIT_RTX4070TI_GAMEROCK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 4070 Ti SUPER GamingPro", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, PALIT_SUB_VEN, PALIT_RTX4080_GAMINGPRO_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 4080 GamingPro", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, PALIT_SUB_VEN, PALIT_RTX4080_GAMINGPRO_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 4080 SUPER GamingPro", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, PALIT_SUB_VEN, PALIT_RTX4080_GAMINGPRO_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 4090 Gamerock", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, PALIT_SUB_VEN, PALIT_RTX4090_GAMEROCK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 5060 Ti White OC 16GB", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX5060TI_DEV, PALIT_SUB_VEN, PALIT_RTX5060TI_WHITE_OC_16G_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 5070 GamingPro", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070_DEV, PALIT_SUB_VEN, PALIT_RTX5070_GAMINGPRO_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 5070 Ti GameRock", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PALIT_SUB_VEN, PALIT_RTX5070TI_GAMEROCK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 5070 Ti GamingPro", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PALIT_SUB_VEN, PALIT_RTX5070TI_GAMINGPRO_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 5070 Ti GamingPro-S", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX5070TI_DEV, PALIT_SUB_VEN, PALIT_RTX5070TI_GAMINGPRO_S_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 5080 GameRock", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, PALIT_SUB_VEN, PALIT_RTX5080_GAMEROCK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 5080 GamingPro", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX5080_DEV, PALIT_SUB_VEN, PALIT_RTX5080_GAMINGPRO_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce RTX 5090 Gamerock", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX5090_DEV, PALIT_SUB_VEN, PALIT_RTX5090_GAMEROCK_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("NVIDIA GeForce RTX 2060 SUPER", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX2060_TU106_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2060_TU106_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("NVIDIA GeForce RTX 2080 SUPER", DetectPNYGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, NVIDIA_SUB_VEN, NVIDIA_RTX2080S_DEV, 0x49); diff --git a/Controllers/PNYGPUController/RGBController_PNYGPU.cpp b/Controllers/PNYGPUController/RGBController_PNYGPU.cpp new file mode 100644 index 0000000..04fb1a6 --- /dev/null +++ b/Controllers/PNYGPUController/RGBController_PNYGPU.cpp @@ -0,0 +1,162 @@ +/*---------------------------------------------------------*\ +| RGBController_PNYGPU.cpp | +| | +| RGBController for PNY Turing GPU | +| | +| KendallMorgan 17 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_PNYGPU.h" + +/**------------------------------------------------------------------*\ + @name PNY GPU + @category GPU + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectPNYGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PNYGPU::RGBController_PNYGPU(PNYGPUController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = name.substr(0, name.find(' ')); + description = "PNY/Palit RGB GPU Device"; + location = controller->GetDeviceLocation(); + + type = DEVICE_TYPE_GPU; + + mode Off; + Off.name = "Off"; + Off.value = PNY_GPU_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = PNY_GPU_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR| MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness = 255; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Cycle; + Cycle.name = "Cycle"; + Cycle.value = PNY_GPU_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED; + Cycle.speed = 3; + Cycle.speed_max = 0; + Cycle.speed_max = 100; + Cycle.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Cycle); + + mode Strobe; + Strobe.name = "Strobe"; + Strobe.value = PNY_GPU_MODE_STROBE; + Strobe.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Strobe.speed = 2; + Strobe.speed_max = 0; + Strobe.speed_max = 255; + Strobe.brightness = 255; + Strobe.brightness_min = 0; + Strobe.brightness_max = 100; + Strobe.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Strobe); + + + SetupZones(); + + // Initialize active mode + active_mode = 0; +} + +void RGBController_PNYGPU::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has one LED, so create a single zone and | + | LED for it | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = "GPU Zone"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = "GPU LED"; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zones.push_back(*new_zone); + SetupColors(); +} + +void RGBController_PNYGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PNYGPU::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_PNYGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PNYGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PNYGPU::DeviceUpdateMode() +{ + RGBColor color = colors[0]; + unsigned char r = RGBGetRValue(color); + unsigned char g = RGBGetGValue(color); + unsigned char b = RGBGetBValue(color); + unsigned char speed, brightness; + switch(modes[active_mode].value) + { + case PNY_GPU_MODE_OFF: + controller->SetOff(); + break; + + case PNY_GPU_MODE_DIRECT: + brightness = modes[active_mode].brightness; + controller->SetDirect(r, g, b, brightness); + break; + + case PNY_GPU_MODE_CYCLE: + speed = modes[active_mode].speed; + controller->SetCycle(speed); + break; + + case PNY_GPU_MODE_STROBE: + speed = modes[active_mode].speed; + brightness = modes[active_mode].brightness; + controller->SetStrobe(r, g, b, speed, brightness); + break; + default: + break; + } +} diff --git a/Controllers/PNYGPUController/RGBController_PNYGPU.h b/Controllers/PNYGPUController/RGBController_PNYGPU.h new file mode 100644 index 0000000..da17e81 --- /dev/null +++ b/Controllers/PNYGPUController/RGBController_PNYGPU.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_PNYGPU.h | +| | +| RGBController for PNY Turing GPU | +| | +| KendallMorgan 17 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "PNYGPUController.h" + +class RGBController_PNYGPU : public RGBController +{ +public: + RGBController_PNYGPU(PNYGPUController* controller_ptr); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PNYGPUController* controller; +}; diff --git a/Controllers/PNYLovelaceGPUController/PNYLovelaceGPUController.cpp b/Controllers/PNYLovelaceGPUController/PNYLovelaceGPUController.cpp new file mode 100644 index 0000000..9a00e7d --- /dev/null +++ b/Controllers/PNYLovelaceGPUController/PNYLovelaceGPUController.cpp @@ -0,0 +1,79 @@ +/*---------------------------------------------------------*\ +| PNYLovelaceGPUController.cpp | +| | +| Driver for PNY Lovelace GPU | +| | +| yufan 01 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "PNYLovelaceGPUController.h" + +PNYLovelaceGPUController::PNYLovelaceGPUController(i2c_smbus_interface* bus, pny_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +PNYLovelaceGPUController::~PNYLovelaceGPUController() +{ + +} + +std::string PNYLovelaceGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string PNYLovelaceGPUController::GetDeviceName() +{ + return(name); +} + +void PNYLovelaceGPUController::SetOff() +{ + unsigned char data[7] = {}; + bus->i2c_smbus_write_i2c_block_data(dev, PNY_GPU_REG_LIGHTING, sizeof(data), data); +} + +void PNYLovelaceGPUController::SetDirect(unsigned char led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char data[7] = {PNY_GPU_MODE_STATIC, 0xFF, led, 0x00, red, green, blue}; + bus->i2c_smbus_write_i2c_block_data(dev, PNY_GPU_REG_LIGHTING, sizeof(data), data); +} + +void PNYLovelaceGPUController::SetCycle(unsigned char speed, unsigned char brightness) +{ + speed = 0xB2 - speed; + unsigned char data[7] = {PNY_GPU_MODE_CYCLE, brightness, speed, 0x00, 0xAA, 0x00, 0x00}; + bus->i2c_smbus_write_i2c_block_data(dev, PNY_GPU_REG_LIGHTING, sizeof(data), data); +} + +void PNYLovelaceGPUController::SetBreath(unsigned char speed, unsigned char red, unsigned char green, unsigned char blue) +{ + speed = 0x19 - speed; + unsigned char data[7] = {PNY_GPU_MODE_BREATH, 0xFF, speed, 0x01, red, green, blue}; + bus->i2c_smbus_write_i2c_block_data(dev, PNY_GPU_REG_LIGHTING, sizeof(data), data); +} + +void PNYLovelaceGPUController::SetWave(unsigned char speed, unsigned char brightness) +{ + speed = 0xBF - speed; + unsigned char data[7] = {PNY_GPU_MODE_WAVE, brightness, speed, 0x00, 0xAA, 0x00, 0x00}; + bus->i2c_smbus_write_i2c_block_data(dev, PNY_GPU_REG_LIGHTING, sizeof(data), data); +} + +void PNYLovelaceGPUController::SetFlash(unsigned char speed, unsigned char brightness, unsigned char red, unsigned char green, unsigned char blue) +{ + speed = 0x4D - speed; + unsigned char data[7] = {PNY_GPU_MODE_FLASH, brightness, speed, 0x00, red, green, blue}; + bus->i2c_smbus_write_i2c_block_data(dev, PNY_GPU_REG_LIGHTING, sizeof(data), data); +} diff --git a/Controllers/PNYLovelaceGPUController/PNYLovelaceGPUController.h b/Controllers/PNYLovelaceGPUController/PNYLovelaceGPUController.h new file mode 100644 index 0000000..5458b9c --- /dev/null +++ b/Controllers/PNYLovelaceGPUController/PNYLovelaceGPUController.h @@ -0,0 +1,55 @@ +/*---------------------------------------------------------*\ +| PNYLovelaceGPUController.h | +| | +| Driver for PNY Lovelace GPU | +| | +| yufan 01 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char pny_dev_id; + +enum +{ + PNY_GPU_MODE_OFF = 0x00, + PNY_GPU_MODE_BREATH = 0x02, + PNY_GPU_MODE_CYCLE = 0x03, + PNY_GPU_MODE_WAVE = 0x04, + PNY_GPU_MODE_FLASH = 0x05, + PNY_GPU_MODE_STATIC = 0x06, +}; + +enum +{ + PNY_GPU_REG_LIGHTING = 0x02, +}; + +class PNYLovelaceGPUController +{ +public: + PNYLovelaceGPUController(i2c_smbus_interface* bus, pny_dev_id dev, std::string dev_name); + ~PNYLovelaceGPUController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetOff(); + void SetBreath(unsigned char speed, unsigned char red, unsigned char green, unsigned char blue); + void SetCycle(unsigned char speed, unsigned char brightness); + void SetWave(unsigned char speed, unsigned char brightness); + void SetFlash(unsigned char speed, unsigned char brightness, unsigned char red, unsigned char green, unsigned char blue); + void SetDirect(unsigned char led, unsigned char red, unsigned char green, unsigned char blue); + +private: + i2c_smbus_interface* bus; + pny_dev_id dev; + std::string name; +}; + diff --git a/Controllers/PNYLovelaceGPUController/PNYLovelaceGPUControllerDetect.cpp b/Controllers/PNYLovelaceGPUController/PNYLovelaceGPUControllerDetect.cpp new file mode 100644 index 0000000..4f21d2c --- /dev/null +++ b/Controllers/PNYLovelaceGPUController/PNYLovelaceGPUControllerDetect.cpp @@ -0,0 +1,51 @@ +/*---------------------------------------------------------*\ +| PNYLovelaceGPUControllerDetect.cpp | +| | +| Detector for PNY Lovelace GPU | +| | +| yufan 01 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "PNYLovelaceGPUController.h" +#include "RGBController_PNYLovelaceGPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/*-----------------------------------------------------------------------------------------*\ +| DetectPNYLovelaceGPUControllers | +| | +| Detect PNY 40xx GPU controllers on the enumerated I2C busses at address 0x60. | +| | +| bus - pointer to i2c_smbus_interface where PNY GPU device is connected | +| dev - I2C address of PNY GPU device | +\*-----------------------------------------------------------------------------------------*/ + +void DetectPNYLovelaceGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(bus->port_id != 1) + { + return; + } + + PNYLovelaceGPUController* controller = new PNYLovelaceGPUController(bus, i2c_addr, name); + RGBController_PNYLovelaceGPU* rgb_controller = new RGBController_PNYLovelaceGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); +} /* DetectPNYLovelaceGPUControllers() */ + +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4070 Ti XLR8 VERTO Epic-X", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, PNY_SUB_VEN, PNY_RTX_4070TI_XLR8_VERTO_EPIC_X_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4070 Ti XLR8 VERTO REV1", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, PNY_SUB_VEN, PNY_RTX_4070TI_XLR8_VERTO_REV1_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4070 Ti XLR8 VERTO REV2", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, PNY_SUB_VEN, PNY_RTX_4070TI_XLR8_VERTO_REV2_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4070 Ti XLR8 VERTO OC", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, PNY_SUB_VEN, PNY_RTX_4070TI_XLR8_VERTO_OC_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4070 Ti Super XLR8 VERTO OC", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TIS_DEV, PNY_SUB_VEN, PNY_RTX_4070TIS_XLR8_VERTO_OC_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4080 XLR8 UPRISING", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, PNY_SUB_VEN, PNY_RTX_4080_XLR8_UPRISING_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4080 XLR8 VERTO", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, PNY_SUB_VEN, PNY_RTX_4080_XLR8_VERTO_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4080 SUPER XLR8 VERTO", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080S_DEV, PNY_SUB_VEN, PNY_RTX_4080S_XLR8_VERTO_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4080 XLR8 Verto Epic-X", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, PNY_SUB_VEN, PNY_RTX_4080_XLR8_VERTO_EPIC_X_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4090 XLR8 VERTO", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, PNY_SUB_VEN, PNY_RTX_4090_XLR8_VERTO_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4090 XLR8 Verto Epic-X", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, PNY_SUB_VEN, PNY_RTX_4090_VERTO_EPIC_X_SUB_DEV, 0x60); +REGISTER_I2C_PCI_DETECTOR("PNY GeForce RTX 4090 XLR8 Verto Epic-X OC", DetectPNYLovelaceGPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, PNY_SUB_VEN, PNY_RTX_4090_VERTO_EPIC_X_OC_SUB_DEV, 0x60); diff --git a/Controllers/PNYLovelaceGPUController/RGBController_PNYLovelaceGPU.cpp b/Controllers/PNYLovelaceGPUController/RGBController_PNYLovelaceGPU.cpp new file mode 100644 index 0000000..f1fbd2e --- /dev/null +++ b/Controllers/PNYLovelaceGPUController/RGBController_PNYLovelaceGPU.cpp @@ -0,0 +1,199 @@ +/*---------------------------------------------------------*\ +| RGBController_PNYLovelaceGPU.cpp | +| | +| RGBController for PNY Lovelace GPU | +| | +| yufan 01 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_PNYLovelaceGPU.h" + +/**------------------------------------------------------------------*\ + @name PNY GPU 40xx + @category GPU + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectPNYLovelaceGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PNYLovelaceGPU::RGBController_PNYLovelaceGPU(PNYLovelaceGPUController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "PNY"; + description = "PNY RGB GPU Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Direct; + Direct.name = "Direct"; + Direct.value = PNY_GPU_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Cycle; + Cycle.name = "Cycle"; + Cycle.value = PNY_GPU_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.speed = 0x89; + Cycle.speed_min = 0; + Cycle.speed_max = 0xB2; + Cycle.brightness = 0xFF; + Cycle.brightness_min = 0; + Cycle.brightness_max = 0xFF; + Cycle.color_mode = MODE_COLORS_NONE; + modes.push_back(Cycle); + + mode Breath; + Breath.name = "Breath"; + Breath.value = PNY_GPU_MODE_BREATH; + Breath.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breath.speed = 0x09; + Breath.speed_min = 0; + Breath.speed_max = 0x19; + Breath.colors_min = 1; + Breath.colors_max = 1; + Breath.colors.resize(1); + Breath.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Breath); + + mode Wave; + Wave.name = "Wave"; + Wave.value = PNY_GPU_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Wave.speed = 0x60; + Wave.speed_min = 0; + Wave.speed_max = 0xBF; + Wave.brightness = 0xFF; + Wave.brightness_min = 0; + Wave.brightness_max = 0xFF; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + + mode Flash; + Flash.name = "Flash"; + Flash.value = PNY_GPU_MODE_FLASH; + Flash.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Flash.speed = 0x27; + Flash.speed_min = 0; + Flash.speed_max = 0x4D; + Flash.brightness = 0xFF; + Flash.brightness_min = 0; + Flash.brightness_max = 0xFF; + Flash.colors_min = 1; + Flash.colors_max = 1; + Flash.colors.resize(1); + Flash.color_mode = MODE_COLORS_MODE_SPECIFIC; + modes.push_back(Flash); + + mode Off; + Off.name = "Off"; + Off.value = PNY_GPU_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + + // Initialize active mode + active_mode = 0; +} + +void RGBController_PNYLovelaceGPU::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has 3 LED, so create a single zone. | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + + new_zone->name = "GPU Zone"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 3; + new_zone->leds_max = 3; + new_zone->leds_count = 3; + new_zone->matrix_map = NULL; + + led* new_led = new led(); + new_led->name = "Fan LED"; + leds.push_back(*new_led); + + new_led = new led(); + new_led->name = "Right LED"; + leds.push_back(*new_led); + + new_led = new led(); + new_led->name = "Left LED"; + leds.push_back(*new_led); + + zones.push_back(*new_zone); + SetupColors(); +} + +void RGBController_PNYLovelaceGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PNYLovelaceGPU::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_PNYLovelaceGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_PNYLovelaceGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_PNYLovelaceGPU::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case PNY_GPU_MODE_OFF: + controller->SetOff(); + break; + + case PNY_GPU_MODE_STATIC: + for (int i = 0; i < 3; i++) + { + RGBColor color = GetLED(i); + controller->SetDirect(i, RGBGetRValue(color), RGBGetGValue(color), RGBGetBValue(color)); + } + break; + case PNY_GPU_MODE_CYCLE: + controller->SetCycle(modes[active_mode].speed, modes[active_mode].brightness); + break; + case PNY_GPU_MODE_BREATH: + { + RGBColor color = modes[active_mode].colors[0]; + controller->SetBreath(modes[active_mode].speed, RGBGetRValue(color), RGBGetGValue(color), RGBGetBValue(color)); + break; + } + case PNY_GPU_MODE_WAVE: + controller->SetWave(modes[active_mode].speed, modes[active_mode].brightness); + break; + case PNY_GPU_MODE_FLASH: + { + RGBColor color = modes[active_mode].colors[0]; + controller->SetFlash(modes[active_mode].speed, modes[active_mode].brightness, + RGBGetRValue(color), RGBGetGValue(color), RGBGetBValue(color)); + break; + } + default: + break; + } +} diff --git a/Controllers/PNYLovelaceGPUController/RGBController_PNYLovelaceGPU.h b/Controllers/PNYLovelaceGPUController/RGBController_PNYLovelaceGPU.h new file mode 100644 index 0000000..c001659 --- /dev/null +++ b/Controllers/PNYLovelaceGPUController/RGBController_PNYLovelaceGPU.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_PNYLovelaceGPU.h | +| | +| RGBController for PNY Lovelace GPU | +| | +| yufan 01 Oct 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "PNYLovelaceGPUController.h" + +class RGBController_PNYLovelaceGPU : public RGBController +{ +public: + RGBController_PNYLovelaceGPU(PNYLovelaceGPUController* controller_ptr); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PNYLovelaceGPUController* controller; +}; diff --git a/Controllers/PalitGPUController/PalitGPUController.cpp b/Controllers/PalitGPUController/PalitGPUController.cpp new file mode 100644 index 0000000..2518124 --- /dev/null +++ b/Controllers/PalitGPUController/PalitGPUController.cpp @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| PalitGPUController.cpp | +| | +| Driver for Palit GPU | +| | +| Manatsawin Hanmongkolchai 11 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "PalitGPUController.h" + +PalitGPUController::PalitGPUController(i2c_smbus_interface* bus, palit_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +PalitGPUController::~PalitGPUController() +{ + +} + +std::string PalitGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string PalitGPUController::GetName() +{ + return(name); +} + +void PalitGPUController::SetDirect(unsigned char red, unsigned char green, unsigned char blue) +{ + // NvAPI_I2CWriteEx: Dev: 0x08 RegSize: 0x01 Reg: 0x03 Size: 0x04 Data: 0xFF 0x00 0x00 0xFF + uint8_t values[] = {red, green, blue, 0xFF}; + bus->i2c_smbus_write_i2c_block_data(dev, PALIT_GPU_REG_LED, sizeof(values), values); +} diff --git a/Controllers/PalitGPUController/PalitGPUController.h b/Controllers/PalitGPUController/PalitGPUController.h new file mode 100644 index 0000000..77a3116 --- /dev/null +++ b/Controllers/PalitGPUController/PalitGPUController.h @@ -0,0 +1,44 @@ +/*---------------------------------------------------------*\ +| PalitGPUController.h | +| | +| Driver for Palit GPU | +| | +| Manatsawin Hanmongkolchai 11 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char palit_dev_id; + +enum +{ + PALIT_GPU_MODE_DIRECT = 0x00, +}; + +enum +{ + PALIT_GPU_REG_LED = 0x03, +}; + +class PalitGPUController +{ +public: + PalitGPUController(i2c_smbus_interface* bus, palit_dev_id dev, std::string dev_name); + ~PalitGPUController(); + + std::string GetDeviceLocation(); + std::string GetName(); + + void SetDirect(unsigned char red, unsigned char green, unsigned char blue); + +private: + i2c_smbus_interface* bus; + palit_dev_id dev; + std::string name; +}; diff --git a/Controllers/PalitGPUController/PalitGPUControllerDetect.cpp b/Controllers/PalitGPUController/PalitGPUControllerDetect.cpp new file mode 100644 index 0000000..0e9fa9b --- /dev/null +++ b/Controllers/PalitGPUController/PalitGPUControllerDetect.cpp @@ -0,0 +1,64 @@ +/*---------------------------------------------------------*\ +| PalitGPUControllerDetect.cpp | +| | +| Detector for Palit GPU | +| | +| Manatsawin Hanmongkolchai 11 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "PalitGPUController.h" +#include "RGBController_PalitGPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ + * * + * DetectPalitGPUControllers * + * * + * Detect Palit GPU controllers on the enumerated I2C busses at address 0x49. * + * * + * bus - pointer to i2c_smbus_interface where Palit GPU device is connected * + * dev - I2C address of Palit GPU device * + * * * + * Ligolas Neo Malicdem - Added 1060, 1070, 1070ti support * +\******************************************************************************************/ + +void DetectPalitGPUControllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(bus->port_id != 1) + { + return; + } + + /*-----------------------------------------------------*\ + | Check for PALIT string | + \*-----------------------------------------------------*/ + const uint8_t palit[] = {'P', 'A', 'L', 'I', 'T'}; + + for(size_t i = 0; i < sizeof(palit); i++) + { + int32_t letter = bus->i2c_smbus_read_byte_data(i2c_addr, 0x07 + (u8)i); + + if(palit[i] != letter) + { + return; + } + } + + PalitGPUController* controller = new PalitGPUController(bus, i2c_addr, name); + RGBController_PalitGPU* rgb_controller = new RGBController_PalitGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); +} /* DetectPalitGPUControllers() */ + +REGISTER_I2C_PCI_DETECTOR("Palit GeForce GTX 1060", DetectPalitGPUControllers, NVIDIA_VEN, NVIDIA_GTX1060_DEV, NVIDIA_SUB_VEN, NVIDIA_GTX1060_DEV, 0x08); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce GTX 1070", DetectPalitGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070_DEV, NVIDIA_SUB_VEN, NVIDIA_GTX1070_DEV, 0x08); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce GTX 1070 Ti", DetectPalitGPUControllers, NVIDIA_VEN, NVIDIA_GTX1070TI_DEV, NVIDIA_SUB_VEN, NVIDIA_GTX1070TI_DEV, 0x08); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce GTX 1080", DetectPalitGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080_DEV, NVIDIA_SUB_VEN, NVIDIA_GTX1080_DEV, 0x08); +REGISTER_I2C_PCI_DETECTOR("Palit GeForce GTX 1080 Ti", DetectPalitGPUControllers, NVIDIA_VEN, NVIDIA_GTX1080TI_DEV, NVIDIA_SUB_VEN, NVIDIA_GTX1080TI_DEV, 0x08); diff --git a/Controllers/PalitGPUController/RGBController_PalitGPU.cpp b/Controllers/PalitGPUController/RGBController_PalitGPU.cpp new file mode 100644 index 0000000..368adfb --- /dev/null +++ b/Controllers/PalitGPUController/RGBController_PalitGPU.cpp @@ -0,0 +1,113 @@ +/*---------------------------------------------------------*\ +| RGBController_PalitGPU.cpp | +| | +| RGBController for Palit GPU | +| | +| Manatsawin Hanmongkolchai 11 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_PalitGPU.h" + +/**------------------------------------------------------------------*\ + @name Palit GPU + @category GPU + @type I2C + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectPalitGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PalitGPU::RGBController_PalitGPU(PalitGPUController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Palit"; + description = "Legacy Palit RGB GPU Device"; + location = controller->GetDeviceLocation(); + + type = DEVICE_TYPE_GPU; + + mode Direct; + Direct.name = "Direct"; + Direct.value = PALIT_GPU_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + // Initialize active mode + active_mode = 0; +} + +void RGBController_PalitGPU::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has one LED, so create a single zone and | + | LED for it | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = "GPU Zone"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = "GPU LED"; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zones.push_back(*new_zone); + SetupColors(); +} + +void RGBController_PalitGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PalitGPU::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_PalitGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PalitGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PalitGPU::DeviceUpdateMode() +{ + RGBColor color = colors[0]; + unsigned char r = RGBGetRValue(color); + unsigned char g = RGBGetGValue(color); + unsigned char b = RGBGetBValue(color); + + switch(modes[active_mode].value) + { + case PALIT_GPU_MODE_DIRECT: + controller->SetDirect(r, g, b); + break; + + default: + break; + } +} diff --git a/Controllers/PalitGPUController/RGBController_PalitGPU.h b/Controllers/PalitGPUController/RGBController_PalitGPU.h new file mode 100644 index 0000000..51934d4 --- /dev/null +++ b/Controllers/PalitGPUController/RGBController_PalitGPU.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_PalitGPU.h | +| | +| RGBController for Palit GPU | +| | +| Manatsawin Hanmongkolchai 11 Apr 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "PalitGPUController.h" + +class RGBController_PalitGPU : public RGBController +{ +public: + RGBController_PalitGPU(PalitGPUController* controller_ptr); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PalitGPUController* controller; +}; diff --git a/Controllers/PatriotViperController/PatriotViperController.cpp b/Controllers/PatriotViperController/PatriotViperController.cpp new file mode 100644 index 0000000..cbb273e --- /dev/null +++ b/Controllers/PatriotViperController/PatriotViperController.cpp @@ -0,0 +1,340 @@ +/*---------------------------------------------------------*\ +| PatriotViperController.cpp | +| | +| Driver for Patriot Viper RAM | +| | +| Adam Honse (CalcProgrammer1) 01 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "PatriotViperController.h" + +PatriotViperController::PatriotViperController(i2c_smbus_interface* bus, viper_dev_id dev, unsigned char slots) +{ + this->bus = bus; + this->dev = dev; + slots_valid = slots; + + strcpy(device_name, "Patriot Viper RGB"); + + led_count = 0; + + for(int i = 0; i < 8; i++) + { + if((slots_valid & (1 << i)) != 0) + { + led_count += 5; + } + } + + keepalive_thread = NULL; + keepalive_thread_run = 1; +} + +PatriotViperController::~PatriotViperController() +{ + StopKeepaliveThread(); +} + +std::string PatriotViperController::GetDeviceName() +{ + return(device_name); +} + +std::string PatriotViperController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +unsigned int PatriotViperController::GetLEDCount() +{ + return(led_count); +} + +unsigned int PatriotViperController::GetSlotCount() +{ + unsigned int slot_count = 0; + + for(int slot = 0; slot < 4; slot++) + { + if((slots_valid & (1 << slot)) != 0) + { + slot_count++; + } + } + + return(slot_count); +} + +unsigned int PatriotViperController::GetMode() +{ + return(mode); +} + +void PatriotViperController::SetEffectColor(unsigned char red, unsigned char green, unsigned char blue) +{ + ViperRegisterWrite(VIPER_REG_START, 0xFF, 0xFF, 0xFF); + ViperRegisterWrite(VIPER_REG_STATIC, 0x04, 0x00, 0x00); + + ViperRegisterWrite(VIPER_REG_LED0_EFFECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_REG_LED1_EFFECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_REG_LED2_EFFECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_REG_LED3_EFFECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_REG_LED4_EFFECT_COLOR, red, blue, green); + + ViperRegisterWrite(VIPER_REG_MODE, mode, 0x00, speed); + ViperRegisterWrite(VIPER_REG_MODE, 0xAA, 0x00, 0x00); + ViperRegisterWrite(VIPER_REG_MODE, 0xFA, 0x00, 0x00); +} + +void PatriotViperController::SetAllColors(unsigned char red, unsigned char green, unsigned char blue) +{ + ViperRegisterWrite(VIPER_REG_LED0_EFFECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_REG_LED1_EFFECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_REG_LED2_EFFECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_REG_LED3_EFFECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_REG_LED4_EFFECT_COLOR, red, blue, green); +} + +void PatriotViperController::SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + ViperRegisterWrite(VIPER_REG_LED0_EFFECT_COLOR + led, red, blue, green); +} + +void PatriotViperController::SetLEDEffectColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + ViperRegisterWrite(VIPER_REG_START, 0xFF, 0xFF, 0xFF); + ViperRegisterWrite(VIPER_REG_STATIC, 0x04, 0x00, 0x00); + + ViperRegisterWrite(VIPER_REG_LED0_EFFECT_COLOR + led, red, blue, green); + + ViperRegisterWrite(VIPER_REG_MODE, mode, 0x00, speed); + ViperRegisterWrite(VIPER_REG_MODE, 0xAA, 0x00, 0x00); + ViperRegisterWrite(VIPER_REG_MODE, 0xFA, 0x00, 0x00); +} + +void PatriotViperController::SetLEDColor(unsigned int /*slot*/, unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + ViperRegisterWrite(VIPER_REG_START, 0xFF, 0xFF, 0xFF); + ViperRegisterWrite(VIPER_REG_STATIC, 0x04, 0x00, 0x00); + + ViperRegisterWrite(VIPER_REG_LED0_DIRECT_COLOR + led, red, blue, green); + + ViperRegisterWrite(VIPER_REG_APPLY, 0x01, 0x00, 0x00); +} + +void PatriotViperController::SetLEDEffectColor(unsigned int /*slot*/, unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + ViperRegisterWrite(VIPER_REG_START, 0xFF, 0xFF, 0xFF); + ViperRegisterWrite(VIPER_REG_STATIC, 0x04, 0x00, 0x00); + + ViperRegisterWrite(VIPER_REG_LED0_EFFECT_COLOR + led, red, blue, green); + + ViperRegisterWrite(VIPER_REG_MODE, mode, 0x00, speed); + ViperRegisterWrite(VIPER_REG_MODE, 0xAA, 0x00, 0x00); + ViperRegisterWrite(VIPER_REG_MODE, 0xFA, 0x00, 0x00); +} + +void PatriotViperController::SetMode(unsigned char new_mode, unsigned char new_speed, unsigned int color_mode) +{ + StopKeepaliveThread(); + direct = false; + mode = new_mode; + if(mode_speed[mode] == -1) + { + speed = new_speed; + } + else + { + speed = mode_speed[mode]; + } + + if(color_mode == 3) + { + /*--------------------------------------------------------------------------------------------------*\ + | Reset previously set mode color, because we want RAM sticks to fall-back to automatic viper colors | + | These are not just rainbowey, but they are affected differently with modes. | + \*--------------------------------------------------------------------------------------------------*/ + + ViperRegisterWrite(VIPER_REG_START, 0xFF, 0xFF, 0xFF); + ViperRegisterWrite(VIPER_REG_STATIC, 0x04, 0x00, 0x00); + ViperRegisterWrite(VIPER_REG_MODE, VIPER_MODE_DARK, 0x00, 0x00); + ViperRegisterWrite(VIPER_REG_MODE, 0xAA, 0x00, 0x00); + ViperRegisterWrite(VIPER_REG_MODE, 0xFA, 0x00, 0x00); + } + + if(mode_steps[mode] == -1) + { + /*--------------------------------------------------------------------------------------------------*\ + | Based on a header file, if number of steps in mode is -1 it means mode is not synced, | + | doesn't have steps and the Keepalive thread is not needed. Set the mode directly and leave it. | + \*--------------------------------------------------------------------------------------------------*/ + + ViperRegisterWrite(VIPER_REG_START, 0xFF, 0xFF, 0xFF); + ViperRegisterWrite(VIPER_REG_STATIC, 0x04, 0x00, 0x00); + ViperRegisterWrite(VIPER_REG_MODE, mode, 0x00, speed); + ViperRegisterWrite(VIPER_REG_MODE, 0xAA, 0x00, 0x00); + ViperRegisterWrite(VIPER_REG_MODE, 0xFA, 0x00, 0x00); + } + else + { + /*----------------------------------------------------------------*\ + | Reset step counters and fire up Keepalive thread. | + | Thread will deal with changing the mode and everything else. | + \*----------------------------------------------------------------*/ + + step = 0; + sub_step = 0; + + keepalive_thread = new std::thread(&PatriotViperController::KeepaliveThread, this); + } +} + +void PatriotViperController::SetDirect() +{ + StopKeepaliveThread(); + direct = true; + ViperRegisterWrite(VIPER_REG_START, 0xFF, 0xFF, 0xFF); + ViperRegisterWrite(VIPER_REG_STATIC, 0x04, 0x00, 0x00); + ViperRegisterWrite(VIPER_REG_MODE, VIPER_MODE_DIRECT, 0x00, 0x00); +} + +void PatriotViperController::ViperRegisterWrite(viper_register reg, unsigned char val0, unsigned char val1, unsigned char val2) +{ + bus->i2c_smbus_write_byte_data(dev, reg, val0); + bus->i2c_smbus_write_byte_data(dev, val2, val1); +} + +void PatriotViperController::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + int cur_step = step.load(); + int cur_sub_step = sub_step.load(); + if(cur_sub_step == 0) + { + ViperRegisterWrite(VIPER_REG_MODE, mode, cur_step, speed); + ViperRegisterWrite(VIPER_REG_MODE, 0xAA, 0x00, cur_sub_step); + } + else + { + ViperRegisterWrite(VIPER_REG_MODE, 0xAA, 0x00, cur_sub_step); + } + + if(cur_sub_step == mode_sub_steps[mode]) + { + sub_step.store(0); + + if(cur_step == mode_steps[mode]) + { + step.store(0); + } + else + { + step.store(cur_step+1); + } + } + else + { + sub_step.store(cur_sub_step+1); + } + + /*---------------------------------------------------------------------------------------------------------*\ + | We have to use wait_for with condition_variable since some of the modes will keep the thread waiting for | + | long time and we don't want to make user wait for mode change when in middle of waiting period. | + \*---------------------------------------------------------------------------------------------------------*/ + int delay = GetDelay(mode, cur_step, cur_sub_step, cur_sub_step == mode_sub_steps[mode]); + std::unique_lock l(thread_ctrl_m); + thread_ctrl.wait_for(l, std::chrono::milliseconds(delay)); + } +} + +void PatriotViperController::StopKeepaliveThread() +{ + if(keepalive_thread != NULL) + { + keepalive_thread_run = 0; + thread_ctrl.notify_one(); + keepalive_thread->join(); + keepalive_thread = NULL; + keepalive_thread_run = 1; + } +} + +unsigned int PatriotViperController::GetDelay(unsigned char mode, unsigned int /*step*/, unsigned int sub_step, bool loop_end) +{ + if(loop_end) + { + if(mode == VIPER_MODE_VIPER) + { + return 7000; + } + else if(mode == VIPER_MODE_BREATHING) + { + return 3000; + } + else if(mode == VIPER_MODE_NEON) + { + return 600; + } + else if(mode == VIPER_MODE_AURORA) + { + return 300; + } + else + { + return 0; + } + } + + if(sub_step == 0) + { + if(mode == VIPER_MODE_VIPER) + { + return 3000; + } + else if(mode == VIPER_MODE_BREATHING) + { + return 3000; + } + else + { + return 0; + } + } + else + { + if(sub_step == 4 && mode == VIPER_MODE_VIPER) + { + return 1000; + } + else if(sub_step == 5 && mode == VIPER_MODE_VIPER) + { + return 590; + } + else if(mode == VIPER_MODE_NEON) + { + return 600; + } + else if(mode == VIPER_MODE_HEARTBEAT) + { + return 100; + } + else if(mode == VIPER_MODE_MARQUEE) + { + return 300; + } + else + { + return 0; + } + } +} diff --git a/Controllers/PatriotViperController/PatriotViperController.h b/Controllers/PatriotViperController/PatriotViperController.h new file mode 100644 index 0000000..16fd7a1 --- /dev/null +++ b/Controllers/PatriotViperController/PatriotViperController.h @@ -0,0 +1,148 @@ +/*---------------------------------------------------------*\ +| PatriotViperController.h | +| | +| Driver for Patriot Viper RAM | +| | +| Adam Honse (CalcProgrammer1) 01 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "i2c_smbus.h" + +typedef unsigned char viper_dev_id; +typedef unsigned char viper_register; + +enum +{ + VIPER_REG_STATIC = 0x01, /* Set static mode */ + VIPER_REG_MODE = 0x03, /* Mode register */ + VIPER_REG_LED0_DIRECT_COLOR = 0x30, /* LED 0 Color (R, B, G) */ + VIPER_REG_LED1_DIRECT_COLOR = 0x31, /* LED 1 Color (R, B, G) */ + VIPER_REG_LED2_DIRECT_COLOR = 0x32, /* LED 2 Color (R, B, G) */ + VIPER_REG_LED3_DIRECT_COLOR = 0x33, /* LED 3 Color (R, B, G) */ + VIPER_REG_LED4_DIRECT_COLOR = 0x34, /* LED 4 Color (R, B, G) */ + VIPER_REG_APPLY = 0x35, /* Apply Changes (0x01, 0x00, 0x00) */ + VIPER_REG_LED0_EFFECT_COLOR = 0x3B, /* LED 0 Color (R, B, G) */ + VIPER_REG_LED1_EFFECT_COLOR = 0x3C, /* LED 1 Color (R, B, G) */ + VIPER_REG_LED2_EFFECT_COLOR = 0x3D, /* LED 2 Color (R, B, G) */ + VIPER_REG_LED3_EFFECT_COLOR = 0x3E, /* LED 3 Color (R, B, G) */ + VIPER_REG_LED4_EFFECT_COLOR = 0x3F, /* LED 4 Color (R, B, G) */ + VIPER_REG_START = 0xFF, /* Start Frame (0xFF, 0xFF, 0xFF) */ +}; + +enum +{ + VIPER_MODE_DARK = 0x00, /* Dark mode */ + VIPER_MODE_BREATHING = 0x01, /* Breathing mode */ + VIPER_MODE_VIPER = 0x02, /* Viper mode */ + VIPER_MODE_HEARTBEAT = 0x03, /* Heartbeat mode */ + VIPER_MODE_MARQUEE = 0x04, /* Marquee mode */ + VIPER_MODE_RAINDROP = 0x05, /* Raindrop mode */ + VIPER_MODE_AURORA = 0x06, /* Aurora mode */ + VIPER_MODE_DIRECT = 0x07, /* Direct mode */ + VIPER_MODE_NEON = 0x08, /* Color cycle mode */ +}; + + +enum +{ + VIPER_SPEED_MIN = 0xC8, /* Slowest speed for non-breathing mode */ + VIPER_SPEED_DEFAULT = 0x64, /* Default speed for non-breathing mode */ + VIPER_SPEED_MAX = 0x14, /* Fastest speed for non-breathing mode */ + VIPER_SPEED_BREATHING_MIN = 0xFF, /* Slowest speed for breathing mode */ + VIPER_SPEED_BREATHING_DEFAULT = 0x0C, /* Default speed for breathing mode */ + VIPER_SPEED_BREATHING_MAX = 0x00, /* Fastest speed for breathing mode */ +}; + +class PatriotViperController +{ +public: + PatriotViperController(i2c_smbus_interface* bus, viper_dev_id dev, unsigned char slots); + ~PatriotViperController(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + unsigned int GetLEDCount(); + unsigned int GetSlotCount(); + unsigned int GetMode(); + void SetMode(unsigned char new_mode, unsigned char new_speed, unsigned int color_mode); + void SetDirect(); + + void SetAllColors(unsigned char red, unsigned char green, unsigned char blue); + void SetEffectColor(unsigned char red, unsigned char green, unsigned char blue); + void SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void SetLEDColor(unsigned int slot, unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void SetLEDEffectColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void SetLEDEffectColor(unsigned int slot, unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + + void KeepaliveThread(); + void StopKeepaliveThread(); + unsigned int GetDelay(unsigned char mode, unsigned int step, unsigned int sub_step, bool loop_end); + + void ViperRegisterWrite(viper_register reg, unsigned char val0, unsigned char val1, unsigned char val2); + bool direct; + +private: + char device_name[32]; + unsigned int led_count; + unsigned char slots_valid; + i2c_smbus_interface* bus; + viper_dev_id dev; + unsigned char mode; + unsigned char speed; + + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::atomic step; + std::atomic sub_step; + std::condition_variable thread_ctrl; + std::mutex thread_ctrl_m; + + + /*-------------------------------------------------------*\ + | Value -1 means mode is not synced, doesn't have steps | + | and the Keepalive thread is not needed | + \*-------------------------------------------------------*/ + + std::map mode_steps = + { + {VIPER_MODE_DARK, -1}, + {VIPER_MODE_BREATHING, 4}, + {VIPER_MODE_VIPER, 4}, + {VIPER_MODE_HEARTBEAT, 6}, + {VIPER_MODE_MARQUEE, 3}, + {VIPER_MODE_RAINDROP, -1}, + {VIPER_MODE_AURORA, 4}, + {VIPER_MODE_NEON, 0}, + }; + + std::map mode_sub_steps = + { + {VIPER_MODE_DARK, -1}, + {VIPER_MODE_BREATHING, 1}, + {VIPER_MODE_VIPER, 6}, + {VIPER_MODE_HEARTBEAT, 59}, + {VIPER_MODE_MARQUEE, 30}, + {VIPER_MODE_RAINDROP, -1}, + {VIPER_MODE_AURORA, 0}, + {VIPER_MODE_NEON, 5}, + }; + + std::map mode_speed = + { + {VIPER_MODE_DARK, -1}, + {VIPER_MODE_BREATHING, 0x06}, + {VIPER_MODE_VIPER, 0x3C}, + {VIPER_MODE_HEARTBEAT, 0x3C}, + {VIPER_MODE_MARQUEE, 0x3C}, + {VIPER_MODE_RAINDROP, -1}, + {VIPER_MODE_AURORA, 0x3C}, + {VIPER_MODE_NEON, 0x3C}, + }; +}; diff --git a/Controllers/PatriotViperController/PatriotViperControllerDetect.cpp b/Controllers/PatriotViperController/PatriotViperControllerDetect.cpp new file mode 100644 index 0000000..1e8652c --- /dev/null +++ b/Controllers/PatriotViperController/PatriotViperControllerDetect.cpp @@ -0,0 +1,96 @@ +/*---------------------------------------------------------*\ +| PatriotViperControllerDetect.cpp | +| | +| Detector for Patriot Viper RAM | +| | +| Adam Honse (CalcProgrammer1) 01 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "PatriotViperController.h" +#include "LogManager.h" +#include "RGBController_PatriotViper.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +using namespace std::chrono_literals; +#define PATRIOT_CONTROLLER_NAME "Patriot Viper" + +/******************************************************************************************\ +* * +* TestForPatriotViperController * +* * +* Tests the given address to see if a Patriot Viper controller exists there. * +* * +\******************************************************************************************/ + +bool TestForPatriotViperController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + + int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE); + + LOG_DEBUG("[%s] Writing at address %02X, res=%02X", PATRIOT_CONTROLLER_NAME, address, res); + + if (res >= 0) + { + pass = true; + } + + return(pass); + +} /* TestForPatriotViperController() */ + + +/******************************************************************************************\ +* * +* DetectPatriotViperControllers * +* * +* Detect Patriot Viper RGB controllers on the enumerated I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where Aura device is connected * +* dev - I2C address of Aura device * +* * +\******************************************************************************************/ + +void DetectPatriotViperControllers(i2c_smbus_interface* bus, std::vector &slots, const std::string &/*name*/) +{ + unsigned char slots_valid = 0x00; + + // Check for Patriot Viper controller at 0x77 + LOG_DEBUG("[%s] Testing bus %d at address 0x77", PATRIOT_CONTROLLER_NAME, bus->port_id); + + if(TestForPatriotViperController(bus, 0x77)) + { + for(SPDWrapper *slot : slots) + { + if((slot->manufacturer_data(0x00) == 0x4D) + &&(slot->manufacturer_data(0x01) == 0x49) + &&(slot->manufacturer_data(0x02) == 0x43) + &&(slot->manufacturer_data(0x03) == 0x53) + &&(slot->manufacturer_data(0x04) == 0x59) + &&(slot->manufacturer_data(0x05) == 0x53) + &&(slot->manufacturer_data(0x06) == 0x5f) + &&(slot->manufacturer_data(0x07) == 0x44)) + { + LOG_DEBUG("[%s] The RAM module detected in slot %d", PATRIOT_CONTROLLER_NAME, slot->index()); + slots_valid |= (1 << (slot->index())); + } + } + + if(slots_valid != 0) + { + PatriotViperController* controller = new PatriotViperController(bus, 0x77, slots_valid); + RGBController_PatriotViper* rgb_controller = new RGBController_PatriotViper(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + +} /* DetectPatriotViperControllers() */ + +REGISTER_I2C_DIMM_DETECTOR(PATRIOT_CONTROLLER_NAME, DetectPatriotViperControllers, JEDEC_PATRIOT, SPD_DDR4_SDRAM); + diff --git a/Controllers/PatriotViperController/RGBController_PatriotViper.cpp b/Controllers/PatriotViperController/RGBController_PatriotViper.cpp new file mode 100644 index 0000000..9eabf69 --- /dev/null +++ b/Controllers/PatriotViperController/RGBController_PatriotViper.cpp @@ -0,0 +1,227 @@ +/*---------------------------------------------------------*\ +| RGBController_PatriotViper.cpp | +| | +| RGBController for Patriot Viper RAM | +| | +| Adam Honse (CalcProgrammer1) 01 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_PatriotViper.h" + +/**------------------------------------------------------------------*\ + @name Patriot Viper + @category RAM + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectPatriotViperControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PatriotViper::RGBController_PatriotViper(PatriotViperController* viper_ptr) +{ + viper = viper_ptr; + + name = viper->GetDeviceName(); + vendor = "Patriot"; + type = DEVICE_TYPE_DRAM; + description = "Patriot Viper Device"; + location = viper->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.speed_min = 0; + Direct.speed_max = 0; + Direct.speed = 0; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Dark; + Dark.name = "Dark"; + Dark.value = VIPER_MODE_DARK; + Dark.flags = 0; + Dark.speed_min = 0; + Dark.speed_max = 0; + Dark.speed = 0; + Dark.color_mode = MODE_COLORS_NONE; + modes.push_back(Dark); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = VIPER_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Breathing.speed_min = 0x06; + Breathing.speed_max = 0x06; + Breathing.speed = 0x06; + Breathing.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Breathing); + + mode Viper; + Viper.name = "Viper"; + Viper.value = VIPER_MODE_VIPER; + Viper.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Viper.speed_min = 0x3C; + Viper.speed_max = 0x3C; + Viper.speed = 0x3C; + Viper.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Viper); + + mode Heartbeat; + Heartbeat.name = "Heartbeat"; + Heartbeat.value = VIPER_MODE_HEARTBEAT; + Heartbeat.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Heartbeat.speed_min = 0x3C; + Heartbeat.speed_max = 0x3C; + Heartbeat.speed = 0x3C; + Heartbeat.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Heartbeat); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = VIPER_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Marquee.speed_min = 0x3C; + Marquee.speed_max = 0x3C; + Marquee.speed = 0x3C; + Marquee.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Marquee); + + mode Raindrop; + Raindrop.name = "Raindrop"; + Raindrop.value = VIPER_MODE_RAINDROP; + Raindrop.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Raindrop.speed_min = VIPER_SPEED_MIN; + Raindrop.speed_max = VIPER_SPEED_MAX; + Raindrop.speed = VIPER_SPEED_DEFAULT; + Raindrop.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Raindrop); + + mode Aurora; + Aurora.name = "Aurora"; + Aurora.value = VIPER_MODE_AURORA; + Aurora.flags = MODE_FLAG_HAS_RANDOM_COLOR; + Aurora.speed_min = 0x3C; + Aurora.speed_max = 0x3C; + Aurora.speed = 0x3C; + Aurora.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Aurora); + + mode Neon; + Neon.name = "Neon"; + Neon.value = VIPER_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_RANDOM_COLOR; + Neon.speed_min = 0x3C; + Neon.speed_max = 0x3C; + Neon.speed = 0x3C; + Neon.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Neon); + + SetupZones(); +} + +RGBController_PatriotViper::~RGBController_PatriotViper() +{ + delete viper; +} + +void RGBController_PatriotViper::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + zone* new_zone = new zone; + new_zone->name = "Patriot Viper RGB"; + new_zone->type = ZONE_TYPE_LINEAR; + new_zone->leds_min = 5; + new_zone->leds_max = 5; + new_zone->leds_count = 5; + new_zone->matrix_map = NULL; + zones.push_back(*new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led* new_led = new led(); + + new_led->name = "Patriot Viper RGB LED "; + new_led->name.append(std::to_string(led_idx + 1)); + leds.push_back(*new_led); + } + + SetupColors(); +} + +void RGBController_PatriotViper::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PatriotViper::DeviceUpdateLEDs() +{ + if(viper->direct == true) + { + for(int led = 0; led < 5; led++) + { + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + viper->SetLEDColor(led, red, grn, blu); + } + } + else + { + for(int led = 0; led < 5; led++) + { + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + viper->SetLEDEffectColor(led, red, grn, blu); + } + } +} + +void RGBController_PatriotViper::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PatriotViper::UpdateSingleLED(int led) +{ + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + if(viper->direct == true) + { + viper->SetLEDColor(led, red, grn, blu); + } + else + { + viper->SetLEDEffectColor(led, red, grn, blu); + } +} + +void RGBController_PatriotViper::DeviceUpdateMode() +{ + if(modes[active_mode].value == 0xFFFF) + { + viper->SetDirect(); + } + else + { + viper->SetMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].color_mode); + } +} diff --git a/Controllers/PatriotViperController/RGBController_PatriotViper.h b/Controllers/PatriotViperController/RGBController_PatriotViper.h new file mode 100644 index 0000000..2291734 --- /dev/null +++ b/Controllers/PatriotViperController/RGBController_PatriotViper.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_PatriotViper.h | +| | +| RGBController for Patriot Viper RAM | +| | +| Adam Honse (CalcProgrammer1) 01 Jan 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "PatriotViperController.h" + +class RGBController_PatriotViper : public RGBController +{ +public: + RGBController_PatriotViper(PatriotViperController* viper_ptr); + ~RGBController_PatriotViper(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PatriotViperController* viper; +}; diff --git a/Controllers/PatriotViperMouseController/PatriotViperMouseController.cpp b/Controllers/PatriotViperMouseController/PatriotViperMouseController.cpp new file mode 100644 index 0000000..f4fdbb5 --- /dev/null +++ b/Controllers/PatriotViperMouseController/PatriotViperMouseController.cpp @@ -0,0 +1,85 @@ +/*---------------------------------------------------------*\ +| PatriotViperMouseController.cpp | +| | +| Detector for Patriot Viper Mouse | +| | +| mi4code 23 May 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include + +PatriotViperMouseController::PatriotViperMouseController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + const unsigned char init_packet[64] = {0x01, 0x00, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x50, 0xDE, 0x8D, 0x77, 0x09, 0xDF, 0x8D, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x58, 0x7C, 0x77, 0x78, 0x81, 0x43, 0x00, 0x30, 0x58, 0x7C, 0x77, 0x8C, 0x5D, 0x9B, 0x77, 0x00, 0x00, 0x3D, 0x00, 0x98, 0xF5, 0x19, 0x08, 0x00, 0x00, 0x00, 0xEE}; + hid_send_feature_report(dev, init_packet, 64); +} + +PatriotViperMouseController::~PatriotViperMouseController() +{ + hid_close(dev); +} + +std::string PatriotViperMouseController::GetLocation() +{ + return("HID " + location); +} + +std::string PatriotViperMouseController::GetName() +{ + return(name); +} + +std::string PatriotViperMouseController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + if(ret != 0) + { + serial_string[0] = '\0'; + } + return StringUtils::wstring_to_string(serial_string); +} + +void PatriotViperMouseController::SetRGB(std::vector colors) +{ + /*------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*\ + | led red green blue checksum | + \*------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ + unsigned char buffer[64] = { 0x01, 0x13, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC}; + + for(unsigned char led_index = 0x00; led_index < 0x07; led_index++) + { + buffer[2] = led_index; + buffer[4] = RGBGetRValue(colors[led_index]); + buffer[5] = RGBGetGValue(colors[led_index]); + buffer[6] = RGBGetBValue(colors[led_index]); + + /*--------------------------------------*\ + | calculate the last checksum byte | + \*--------------------------------------*/ + unsigned char xor_value = 0; + + for(int i = 0; i < 63; ++i) + { + xor_value ^= buffer[i]; + } + + if(xor_value % 2 == 0) + { + buffer[63] = (xor_value + 1) % 256; + } + else + { + buffer[63] = (xor_value - 1) % 256; + } + + hid_send_feature_report(dev, buffer, 64); + } +} diff --git a/Controllers/PatriotViperMouseController/PatriotViperMouseController.h b/Controllers/PatriotViperMouseController/PatriotViperMouseController.h new file mode 100644 index 0000000..118d289 --- /dev/null +++ b/Controllers/PatriotViperMouseController/PatriotViperMouseController.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| PatriotViperMouseController.h | +| | +| Detector for Patriot Viper Mouse | +| | +| mi4code 23 May 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "StringUtils.h" +#include +#include "RGBController.h" + +class PatriotViperMouseController +{ +public: + PatriotViperMouseController(hid_device* dev_handle, const char* path, std::string dev_name); + ~PatriotViperMouseController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + + void SetRGB(std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/PatriotViperMouseController/PatriotViperMouseControllerDetect.cpp b/Controllers/PatriotViperMouseController/PatriotViperMouseControllerDetect.cpp new file mode 100644 index 0000000..adc8851 --- /dev/null +++ b/Controllers/PatriotViperMouseController/PatriotViperMouseControllerDetect.cpp @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| PatriotViperMouseControllerDetect.cpp | +| | +| Detector for Patriot Viper Mouse | +| | +| mi4code 07 Apr 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "PatriotViperMouseController.h" +#include "RGBController_PatriotViperMouse.h" + + +/*-----------------------------------------------------*\ +| Patriot Viper Mouse IDs | +\*-----------------------------------------------------*/ + +#define PATRIOT_VID 0x0C45 +#define VIPER_V550_PID 0x7E18 + + +void DetectPatriotViperMouseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + PatriotViperMouseController* controller = new PatriotViperMouseController(dev, info->path, name); + RGBController_PatriotViperMouse* rgb_controller = new RGBController_PatriotViperMouse(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Patriot Viper V550", DetectPatriotViperMouseControllers, PATRIOT_VID, VIPER_V550_PID, 2, 0xFF18, 0x01); diff --git a/Controllers/PatriotViperMouseController/RGBController_PatriotViperMouse.cpp b/Controllers/PatriotViperMouseController/RGBController_PatriotViperMouse.cpp new file mode 100644 index 0000000..f1e09bf --- /dev/null +++ b/Controllers/PatriotViperMouseController/RGBController_PatriotViperMouse.cpp @@ -0,0 +1,121 @@ +/*---------------------------------------------------------*\ +| RGBController_PatriotViperMouse.cpp | +| | +| RGBController for Patriot Viper Mouse | +| | +| mi4code 07 Apr 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_PatriotViperMouse.h" + +/**------------------------------------------------------------------*\ + @name Patriot Viper V550 + @category Mouse + @type USB + @save :o: + @direct :white_check_mark: + @effects :o: + @detectors DetectPatriotViperMouseControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PatriotViperMouse::RGBController_PatriotViperMouse(PatriotViperMouseController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Patriot"; + type = DEVICE_TYPE_MOUSE; + description = "Patriot Viper Mouse"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 1; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +}; + +RGBController_PatriotViperMouse::~RGBController_PatriotViperMouse() +{ + delete controller; +} + +void RGBController_PatriotViperMouse::SetupZones() +{ + zone left_zone; + left_zone.name = "Left"; + left_zone.type = ZONE_TYPE_LINEAR; + left_zone.leds_min = 3; + left_zone.leds_max = 3; + left_zone.leds_count = 3; + left_zone.matrix_map = NULL; + zones.push_back(left_zone); + + zone right_zone; + right_zone.name = "Right"; + right_zone.type = ZONE_TYPE_LINEAR; + right_zone.leds_min = 3; + right_zone.leds_max = 3; + right_zone.leds_count = 3; + right_zone.matrix_map = NULL; + zones.push_back(right_zone); + + zone wheel_zone; + wheel_zone.name = "Mousewheel"; + wheel_zone.type = ZONE_TYPE_SINGLE; + wheel_zone.leds_min = 1; + wheel_zone.leds_max = 1; + wheel_zone.leds_count = 1; + wheel_zone.matrix_map = NULL; + zones.push_back(wheel_zone); + + for(unsigned char i = 0x00; i < 0x07; i++) + { + led new_led; + new_led.name = "LED " + std::to_string(i+1); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_PatriotViperMouse::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PatriotViperMouse::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_PatriotViperMouse::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PatriotViperMouse::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PatriotViperMouse::DeviceUpdateMode() +{ + /*-----------------*\ + | Direct mode | + \*-----------------*/ + if(modes[active_mode].value == 1) + { + controller->SetRGB(colors); + } +} diff --git a/Controllers/PatriotViperMouseController/RGBController_PatriotViperMouse.h b/Controllers/PatriotViperMouseController/RGBController_PatriotViperMouse.h new file mode 100644 index 0000000..93ea45a --- /dev/null +++ b/Controllers/PatriotViperMouseController/RGBController_PatriotViperMouse.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_PatriotViperMouse.h | +| | +| RGBController for Patriot Viper Mouse | +| | +| mi4code 07 Apr 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "PatriotViperMouseController.h" + +class RGBController_PatriotViperMouse : public RGBController +{ +public: + RGBController_PatriotViperMouse(PatriotViperMouseController* controller_ptr); + ~RGBController_PatriotViperMouse(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PatriotViperMouseController* controller; +}; diff --git a/Controllers/PatriotViperSteelController/PatriotViperSteelController.cpp b/Controllers/PatriotViperSteelController/PatriotViperSteelController.cpp new file mode 100644 index 0000000..731e4ba --- /dev/null +++ b/Controllers/PatriotViperSteelController/PatriotViperSteelController.cpp @@ -0,0 +1,90 @@ +/*---------------------------------------------------------*\ +| PatriotViperSteelController.cpp | +| | +| Driver for Patriot Viper Steel RAM | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "PatriotViperSteelController.h" + +PatriotViperSteelController::PatriotViperSteelController(i2c_smbus_interface *bus, viper_dev_id dev) +{ + this->bus = bus; + this->dev = dev; + + strcpy(device_name, "Patriot Viper Steel RGB"); + + led_count = 5; +} + +PatriotViperSteelController::~PatriotViperSteelController() +{ +} + +std::string PatriotViperSteelController::GetDeviceName() +{ + return(device_name); +} + +std::string PatriotViperSteelController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +unsigned int PatriotViperSteelController::GetLEDCount() +{ + return(led_count); +} + +unsigned int PatriotViperSteelController::GetSlotCount() +{ + unsigned int slot_count = 0; + + for(int slot = 0; slot < 4; slot++) + { + if((slots_valid & (1 << slot)) != 0) + { + slot_count++; + } + } + + return(slot_count); +} + +unsigned int PatriotViperSteelController::GetMode() +{ + return(mode); +} + +void PatriotViperSteelController::SetAllColors(unsigned char red, unsigned char green, unsigned char blue) +{ + ViperRegisterWrite(VIPER_STEEL_REG_LED0_DIRECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_STEEL_REG_LED1_DIRECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_STEEL_REG_LED2_DIRECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_STEEL_REG_LED3_DIRECT_COLOR, red, blue, green); + ViperRegisterWrite(VIPER_STEEL_REG_LED4_DIRECT_COLOR, red, blue, green); +} + +void PatriotViperSteelController::SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + ViperRegisterWrite(VIPER_STEEL_REG_LED0_DIRECT_COLOR + led, red, blue, green); +} + +void PatriotViperSteelController::SetLEDColor(unsigned int /*slot*/, unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + ViperRegisterWrite(VIPER_STEEL_REG_LED0_DIRECT_COLOR + led, red, blue, green); +} + +void PatriotViperSteelController::ViperRegisterWrite(viper_register reg, unsigned char val0, unsigned char val1, unsigned char val2) +{ + bus->i2c_smbus_write_byte_data(dev, reg, val0); + bus->i2c_smbus_write_byte_data(dev, val2, val1); +} diff --git a/Controllers/PatriotViperSteelController/PatriotViperSteelController.h b/Controllers/PatriotViperSteelController/PatriotViperSteelController.h new file mode 100644 index 0000000..668a8e9 --- /dev/null +++ b/Controllers/PatriotViperSteelController/PatriotViperSteelController.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| PatriotViperSteelController.h | +| | +| Driver for Patriot Viper Steel RAM | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char viper_dev_id; +typedef unsigned char viper_register; + +enum +{ + VIPER_STEEL_REG_LED0_DIRECT_COLOR = 0x17, /* LED 0 Color (R, B, G) */ + VIPER_STEEL_REG_LED1_DIRECT_COLOR = 0x18, /* LED 1 Color (R, B, G) */ + VIPER_STEEL_REG_LED2_DIRECT_COLOR = 0x19, /* LED 2 Color (R, B, G) */ + VIPER_STEEL_REG_LED3_DIRECT_COLOR = 0x1a, /* LED 3 Color (R, B, G) */ + VIPER_STEEL_REG_LED4_DIRECT_COLOR = 0x1b, /* LED 4 Color (R, B, G) */ +}; + +class PatriotViperSteelController +{ +public: + PatriotViperSteelController(i2c_smbus_interface *bus, viper_dev_id dev); + ~PatriotViperSteelController(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + unsigned int GetLEDCount(); + unsigned int GetSlotCount(); + unsigned int GetMode(); + + void SetAllColors(unsigned char red, unsigned char green, unsigned char blue); + void SetLEDColor(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void SetLEDColor(unsigned int slot, unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + + void ViperRegisterWrite(viper_register reg, unsigned char val0, unsigned char val1, unsigned char val2); + bool direct; + +private: + char device_name[32]; + unsigned int led_count; + unsigned char slots_valid; + i2c_smbus_interface *bus; + viper_dev_id dev; + unsigned char mode; +}; diff --git a/Controllers/PatriotViperSteelController/PatriotViperSteelControllerDetect.cpp b/Controllers/PatriotViperSteelController/PatriotViperSteelControllerDetect.cpp new file mode 100644 index 0000000..7e16a01 --- /dev/null +++ b/Controllers/PatriotViperSteelController/PatriotViperSteelControllerDetect.cpp @@ -0,0 +1,94 @@ +/*---------------------------------------------------------*\ +| PatriotViperSteelControllerDetect.cpp | +| | +| Detector for Patriot Viper Steel RAM | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "PatriotViperSteelController.h" +#include "LogManager.h" +#include "RGBController_PatriotViperSteel.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +using namespace std::chrono_literals; +#define PATRIOT_CONTROLLER_NAME "Patriot Viper Steel" + +/******************************************************************************************\ +* * +* TestForPatriotViperSteelController * +* * +* Tests the given address to see if a Patriot Viper Steel controller exists there. * +* * +\******************************************************************************************/ + +bool TestForPatriotViperSteelController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + + int res = bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE); + + LOG_DEBUG("[%s] Writing at address %02X, res=%02X", PATRIOT_CONTROLLER_NAME, address, res); + + if (res >= 0) + { + pass = true; + } + + return(pass); + +} /* TestForPatriotViperSteelController() */ + + +/******************************************************************************************\ +* * +* DetectPatriotViperSteelControllers * +* * +* Detect Patriot Viper Steel RGB controllers on the enumerated I2C busses. * +* * +* bus - pointer to i2c_smbus_interface where Aura device is connected * +* dev - I2C address of Aura device * +* * +\******************************************************************************************/ + +void DetectPatriotViperSteelControllers(i2c_smbus_interface* bus, std::vector &slots, const std::string &/*name*/) +{ + unsigned char slots_valid = 0x00; + + // Check for Patriot Viper controller at 0x77 + LOG_DEBUG("[%s] Testing bus %d at address 0x77", PATRIOT_CONTROLLER_NAME, bus->port_id); + + if(TestForPatriotViperSteelController(bus, 0x77)) + { + for(SPDWrapper *slot : slots) + { + if((slot->manufacturer_data(0x00) == 0x50) + &&(slot->manufacturer_data(0x01) == 0x44) + &&(slot->manufacturer_data(0x02) == 0x41) + &&(slot->manufacturer_data(0x03) == 0x31) + &&(slot->manufacturer_data(0x04) == 0x00) + &&(slot->manufacturer_data(0x05) == 0x00) + &&(slot->manufacturer_data(0x06) == 0x00) + &&(slot->manufacturer_data(0x07) == 0x00)) + { + LOG_DEBUG("[%s] The RAM module detected in slot %d", PATRIOT_CONTROLLER_NAME, slot->index()); + slots_valid |= (1 << (slot->index())); + } + } + + if(slots_valid != 0) + { + PatriotViperSteelController* controller = new PatriotViperSteelController(bus, 0x77); + RGBController_PatriotViperSteel* rgb_controller = new RGBController_PatriotViperSteel(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + +} /* DetectPatriotViperSteelControllers() */ + +REGISTER_I2C_DIMM_DETECTOR(PATRIOT_CONTROLLER_NAME, DetectPatriotViperSteelControllers, 0xFF7E, SPD_DDR4_SDRAM); + diff --git a/Controllers/PatriotViperSteelController/RGBController_PatriotViperSteel.cpp b/Controllers/PatriotViperSteelController/RGBController_PatriotViperSteel.cpp new file mode 100644 index 0000000..5abb82f --- /dev/null +++ b/Controllers/PatriotViperSteelController/RGBController_PatriotViperSteel.cpp @@ -0,0 +1,117 @@ +/*---------------------------------------------------------*\ +| RGBController_PatriotViperSteel.cpp | +| | +| RGBController for Patriot Viper Steel RAM | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_PatriotViperSteel.h" + +/**------------------------------------------------------------------*\ + @name Patriot Viper Steel + @category RAM + @type I2C + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectPatriotViperSteelControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PatriotViperSteel::RGBController_PatriotViperSteel(PatriotViperSteelController *viper_ptr) +{ + controller = viper_ptr; + + name = controller->GetDeviceName(); + vendor = "Patriot"; + type = DEVICE_TYPE_DRAM; + description = "Patriot Viper Steel Device"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.speed_min = 0; + Direct.speed_max = 0; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.speed = 0; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_PatriotViperSteel::~RGBController_PatriotViperSteel() +{ + delete controller; +} + +void RGBController_PatriotViperSteel::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + zone *new_zone = new zone; + new_zone->name = "Patriot Viper Steel RGB"; + new_zone->type = ZONE_TYPE_LINEAR; + new_zone->leds_min = 5; + new_zone->leds_max = 5; + new_zone->leds_count = 5; + new_zone->matrix_map = NULL; + zones.push_back(*new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led *new_led = new led(); + + new_led->name = "Patriot Viper RGB LED "; + new_led->name.append(std::to_string(led_idx + 1)); + leds.push_back(*new_led); + } + + SetupColors(); +} + +void RGBController_PatriotViperSteel::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PatriotViperSteel::DeviceUpdateLEDs() +{ + for(int led = 0; led < 5; led++) + { + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetLEDColor(led, red, grn, blu); + } +} + +void RGBController_PatriotViperSteel::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PatriotViperSteel::UpdateSingleLED(int led) +{ + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetLEDColor(led, red, grn, blu); +} + +void RGBController_PatriotViperSteel::DeviceUpdateMode() +{ +} diff --git a/Controllers/PatriotViperSteelController/RGBController_PatriotViperSteel.h b/Controllers/PatriotViperSteelController/RGBController_PatriotViperSteel.h new file mode 100644 index 0000000..01940a8 --- /dev/null +++ b/Controllers/PatriotViperSteelController/RGBController_PatriotViperSteel.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| RGBController_PatriotViperSteel.h | +| | +| RGBController for Patriot Viper Steel RAM | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "PatriotViperSteelController.h" + +class RGBController_PatriotViperSteel : public RGBController +{ +public: + RGBController_PatriotViperSteel(PatriotViperSteelController *controller_ptr); + ~RGBController_PatriotViperSteel(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PatriotViperSteelController *controller; +}; diff --git a/Controllers/PhilipsHueController/PhilipsHueController.cpp b/Controllers/PhilipsHueController/PhilipsHueController.cpp new file mode 100644 index 0000000..7e40338 --- /dev/null +++ b/Controllers/PhilipsHueController/PhilipsHueController.cpp @@ -0,0 +1,87 @@ +/*---------------------------------------------------------*\ +| PhilipsHueController.cpp | +| | +| Driver for Philips Hue | +| | +| Adam Honse (calcprogrammer1@gmail.com) 15 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "PhilipsHueController.h" +#include "LogManager.h" + +PhilipsHueController::PhilipsHueController(hueplusplus::Light light_ptr, std::string bridge_ip):light(light_ptr) +{ + dark = false; + location = "IP: " + bridge_ip; +} + +PhilipsHueController::~PhilipsHueController() +{ + +} + +std::string PhilipsHueController::GetLocation() +{ + return(location); +} + +std::string PhilipsHueController::GetName() +{ + return(light.getModelId()); +} + +std::string PhilipsHueController::GetVersion() +{ + return(light.getSwVersion()); +} + +std::string PhilipsHueController::GetManufacturer() +{ + return(light.getManufacturername()); +} + +std::string PhilipsHueController::GetUniqueID() +{ + return(light.getUId()); +} + +void PhilipsHueController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + hueplusplus::RGB rgb; + rgb.r = red; + rgb.g = green; + rgb.b = blue; + + if((red == 0) && (green == 0) && (blue == 0)) + { + if(!dark) + { + try + { + light.setColorRGB(rgb, 0); + } + catch(const std::exception& e) + { + LOG_ERROR("[PhilipsHueController] An error occured while setting the colors: %s", e.what()); + } + } + + dark = true; + } + else + { + dark = false; + + try + { + light.setColorRGB(rgb, 0); + } + catch(std::exception& e) + { + LOG_ERROR("[PhilipsHueController] An error occured while setting the colors: %s", e.what()); + } + } +} diff --git a/Controllers/PhilipsHueController/PhilipsHueController.h b/Controllers/PhilipsHueController/PhilipsHueController.h new file mode 100644 index 0000000..b6ed82e --- /dev/null +++ b/Controllers/PhilipsHueController/PhilipsHueController.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| PhilipsHueController.h | +| | +| Driver for Philips Hue | +| | +| Adam Honse (calcprogrammer1@gmail.com) 15 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "HueDeviceTypes.h" + +class PhilipsHueController +{ +public: + PhilipsHueController(hueplusplus::Light light_ptr, std::string bridge_ip); + ~PhilipsHueController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetVersion(); + std::string GetManufacturer(); + std::string GetUniqueID(); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + +private: + hueplusplus::Light light; + std::string location; + bool dark; +}; diff --git a/Controllers/PhilipsHueController/PhilipsHueControllerDetect.cpp b/Controllers/PhilipsHueController/PhilipsHueControllerDetect.cpp new file mode 100644 index 0000000..4f7fd5d --- /dev/null +++ b/Controllers/PhilipsHueController/PhilipsHueControllerDetect.cpp @@ -0,0 +1,244 @@ +/*---------------------------------------------------------*\ +| PhilipsHueControllerDetect.cpp | +| | +| Detector for Philips Hue | +| | +| Adam Honse (calcprogrammer1@gmail.com) 15 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Bridge.h" +#include "HueDeviceTypes.h" + +#ifdef _WIN32 +#include "WinHttpHandler.h" +#else +#include "LinHttpHandler.h" +#endif + +#include "Detector.h" +#include "LogManager.h" +#include "PhilipsHueController.h" +#include "PhilipsHueEntertainmentController.h" +#include "RGBController_PhilipsHue.h" +#include "RGBController_PhilipsHueEntertainment.h" +#include "PhilipsHueSettingsHandler.h" + +/******************************************************************************************\ +* * +* DetectPhilipsHueControllers * +* * +* Detect Philips Hue lighting devices with RGB control * +* * +\******************************************************************************************/ + +void DetectPhilipsHueControllers() +{ + PhilipsHueSettingsHandler hue_settings; + + /*-------------------------------------------------*\ + | Create an HTTP handler | + \*-------------------------------------------------*/ +#ifdef _WIN32 + using SystemHttpHandler = hueplusplus::WinHttpHandler; +#else + using SystemHttpHandler = hueplusplus::LinHttpHandler; +#endif + + /*-------------------------------------------------*\ + | Create a finder and find bridges | + \*-------------------------------------------------*/ + static hueplusplus::BridgeFinder finder(std::make_shared()); + std::vector bridges;// = finder.findBridges(); + + /*-------------------------------------------------*\ + | If no bridges were detected, manually add bridge | + | IP and MAC (need to get these from file) | + \*-------------------------------------------------*/ + if(hue_settings.GetBridgeCount() > 0) + { + hueplusplus::BridgeFinder::BridgeIdentification ident; + + ident.ip = hue_settings.GetBridgeIP(0); + ident.mac = hue_settings.GetBridgeMAC(0); + + bridges.push_back(ident); + } + + /*-------------------------------------------------*\ + | If no bridges were found, return, otherwise | + | connect to the first bridge | + \*-------------------------------------------------*/ + if(bridges.empty()) + { + return; + } + else + { + /*-------------------------------------------------*\ + | Check if a saved username exists | + \*-------------------------------------------------*/ + if(hue_settings.GetBridgeCount() > 0) + { + /*-------------------------------------------------*\ + | Add the username if it exists | + \*-------------------------------------------------*/ + if(hue_settings.BridgeHasUsername(0)) + { + finder.addUsername(bridges[0].mac, hue_settings.GetBridgeUsername(0)); + } + + /*-------------------------------------------------*\ + | Add the client key if it exists | + \*-------------------------------------------------*/ + if(hue_settings.BridgeHasClientKey(0)) + { + finder.addClientKey(bridges[0].mac, hue_settings.GetBridgeClientKey(0)); + } + } + + /*-------------------------------------------------*\ + | If username was added, this should connect right | + | away. If not, the user will have to push the | + | connect button on the bridge. | + \*-------------------------------------------------*/ + try + { + static hueplusplus::Bridge bridge = finder.getBridge(bridges[0]); + + bridge.refresh(); + + /*-------------------------------------------------*\ + | Check to see if we need to save the settings | + | Settings need to be saved if either username or | + | client key either do not exist or have changed | + \*-------------------------------------------------*/ + bool save_settings = false; + bool use_entertainment = false; + bool auto_connect = false; + + if(hue_settings.GetBridgeCount() > 0) + { + if(hue_settings.BridgeHasUsername(0)) + { + if(hue_settings.GetBridgeUsername(0) != bridge.getUsername()) + { + save_settings = true; + } + } + else + { + save_settings = true; + } + + if(hue_settings.BridgeHasClientKey(0)) + { + if(hue_settings.GetBridgeClientKey(0) != bridge.getClientKey()) + { + use_entertainment = true; + save_settings = true; + } + } + else + { + save_settings = true; + } + } + + /*-------------------------------------------------*\ + | Save the settings if needed | + \*-------------------------------------------------*/ + if(save_settings) + { + hue_settings.SetBridgeUsername(0, bridge.getUsername()); + hue_settings.SetBridgeClientKey(0, bridge.getClientKey()); + hue_settings.SetBridgeUseEntertainment(0, use_entertainment); + hue_settings.SetBridgeAutoconnect(0, auto_connect); + hue_settings.SaveSettings(); + } + + /*-------------------------------------------------*\ + | Get entertainment mode settings | + \*-------------------------------------------------*/ + use_entertainment = hue_settings.GetBridgeUseEntertainment(0); + auto_connect = hue_settings.GetBridgeAutoconnect(0); + + /*-------------------------------------------------*\ + | Get all groups from the bridge | + \*-------------------------------------------------*/ + if(use_entertainment) + { + std::vector groups = bridge.groups().getAll(); + + if(groups.size() > 0) + { + /*-------------------------------------------------*\ + | Loop through all available groups and check to | + | see if any are Entertainment groups | + \*-------------------------------------------------*/ + for(unsigned int group_idx = 0; group_idx < groups.size(); group_idx++) + { + if(groups[group_idx].getType() == "Entertainment") + { + PhilipsHueEntertainmentController* controller = new PhilipsHueEntertainmentController(bridge, groups[group_idx]); + RGBController_PhilipsHueEntertainment* rgb_controller = new RGBController_PhilipsHueEntertainment(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + + /*-------------------------------------------------*\ + | Loop through RGB Controllers to find the first | + | Entertainment group and Set it to "Connect", | + | as only one Stream can be open at a time. | + \*-------------------------------------------------*/ + if(auto_connect) + { + for(unsigned int controller_idx = 0; controller_idx < ResourceManager::get()->GetRGBControllers().size(); controller_idx++) + { + if(ResourceManager::get()->GetRGBControllers()[controller_idx]->GetDescription() == "Philips Hue Entertainment Mode Device") + { + ResourceManager::get()->GetRGBControllers()[controller_idx]->SetMode(0); + break; + } + } + } + } + } + + /*-------------------------------------------------*\ + | Get all lights from the bridge | + \*-------------------------------------------------*/ + else + { + std::vector lights = bridge.lights().getAll(); + + if(lights.size() > 0) + { + /*-------------------------------------------------*\ + | Loop through all available lights and add those | + | that have color (RGB) control capability | + \*-------------------------------------------------*/ + for(unsigned int light_idx = 0; light_idx < lights.size(); light_idx++) + { + if(lights[light_idx].hasColorControl()) + { + PhilipsHueController* controller = new PhilipsHueController(lights[light_idx], bridge.getBridgeIP()); + RGBController_PhilipsHue* rgb_controller = new RGBController_PhilipsHue(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } + } + } + catch(const std::exception &e) + { + LOG_INFO("Exception occurred in Philips Hue detection: %s", e.what()); + } + } +} /* DetectPhilipsHueControllers() */ + +REGISTER_DETECTOR("Philips Hue", DetectPhilipsHueControllers); diff --git a/Controllers/PhilipsHueController/PhilipsHueEntertainmentController.cpp b/Controllers/PhilipsHueController/PhilipsHueEntertainmentController.cpp new file mode 100644 index 0000000..b973cff --- /dev/null +++ b/Controllers/PhilipsHueController/PhilipsHueEntertainmentController.cpp @@ -0,0 +1,110 @@ +/*---------------------------------------------------------*\ +| PhilipsHueEntertainmentController.cpp | +| | +| Detector for Philips Hue Entertainment Mode | +| | +| Adam Honse (calcprogrammer1@gmail.com) 06 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController.h" +#include "PhilipsHueEntertainmentController.h" + +PhilipsHueEntertainmentController::PhilipsHueEntertainmentController(hueplusplus::Bridge& bridge_ptr, hueplusplus::Group group_ptr):bridge(bridge_ptr),group(group_ptr) +{ + /*-------------------------------------------------*\ + | Fill in location string with bridge IP | + \*-------------------------------------------------*/ + location = "IP: " + bridge.getBridgeIP(); + num_leds = (unsigned int)group.getLightIds().size(); + connected = false; +} + +PhilipsHueEntertainmentController::~PhilipsHueEntertainmentController() +{ + +} + +std::string PhilipsHueEntertainmentController::GetLocation() +{ + return(location); +} + +std::string PhilipsHueEntertainmentController::GetName() +{ + return(group.getName()); +} + +std::string PhilipsHueEntertainmentController::GetVersion() +{ + return(""); +} + +std::string PhilipsHueEntertainmentController::GetManufacturer() +{ + return(""); +} + +std::string PhilipsHueEntertainmentController::GetUniqueID() +{ + return(""); +} + +unsigned int PhilipsHueEntertainmentController::GetNumLEDs() +{ + return(num_leds); +} + +void PhilipsHueEntertainmentController::SetColor(RGBColor* colors) +{ + if(connected) + { + /*-------------------------------------------------*\ + | Fill in Entertainment Mode light data | + \*-------------------------------------------------*/ + for(unsigned int light_idx = 0; light_idx < num_leds; light_idx++) + { + RGBColor color = colors[light_idx]; + unsigned char red = RGBGetRValue(color); + unsigned char green = RGBGetGValue(color); + unsigned char blue = RGBGetBValue(color); + + entertainment->setColorRGB(light_idx, red, green, blue); + } + + entertainment->update(); + } +} + +void PhilipsHueEntertainmentController::Connect() +{ + if(!connected) + { + /*-------------------------------------------------*\ + | Create Entertainment Mode from bridge and group | + \*-------------------------------------------------*/ + entertainment = new hueplusplus::EntertainmentMode(bridge, group); + + /*-------------------------------------------------*\ + | Connect Hue Entertainment Mode | + \*-------------------------------------------------*/ + entertainment->connect(); + connected = true; + } +} + +void PhilipsHueEntertainmentController::Disconnect() +{ + if(connected) + { + /*-------------------------------------------------*\ + | Disconnect Hue Entertainment Mode | + \*-------------------------------------------------*/ + entertainment->disconnect(); + connected = false; + + delete entertainment; + } +} diff --git a/Controllers/PhilipsHueController/PhilipsHueEntertainmentController.h b/Controllers/PhilipsHueController/PhilipsHueEntertainmentController.h new file mode 100644 index 0000000..22c7c86 --- /dev/null +++ b/Controllers/PhilipsHueController/PhilipsHueEntertainmentController.h @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| PhilipsHueEntertainmentController.h | +| | +| Detector for Philips Hue Entertainment Mode | +| | +| Adam Honse (calcprogrammer1@gmail.com) 06 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "Bridge.h" +#include "EntertainmentMode.h" +#include "Group.h" +#include "RGBController.h" + +#define HUE_ENTERTAINMENT_HEADER_SIZE 16 +#define HUE_ENTERTAINMENT_LIGHT_SIZE 9 + +class PhilipsHueEntertainmentController +{ +public: + PhilipsHueEntertainmentController(hueplusplus::Bridge& bridge_ptr, hueplusplus::Group group_ptr); + ~PhilipsHueEntertainmentController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetVersion(); + std::string GetManufacturer(); + std::string GetUniqueID(); + unsigned int GetNumLEDs(); + + void SetColor(RGBColor* colors); + + void Connect(); + void Disconnect(); + +private: + hueplusplus::Bridge& bridge; + hueplusplus::Group group; + hueplusplus::EntertainmentMode* entertainment; + + std::string location; + unsigned int num_leds; + bool connected; +}; diff --git a/Controllers/PhilipsHueController/PhilipsHueSettingsHandler.cpp b/Controllers/PhilipsHueController/PhilipsHueSettingsHandler.cpp new file mode 100644 index 0000000..ac1064f --- /dev/null +++ b/Controllers/PhilipsHueController/PhilipsHueSettingsHandler.cpp @@ -0,0 +1,151 @@ +#include "PhilipsHueSettingsHandler.h" +#include "ResourceManager.h" +#include "SettingsManager.h" + +#define HUE_SETTINGS ((hue_settings_type *)hue_settings)->hue_settings + +typedef struct +{ + json hue_settings; +} hue_settings_type; + +PhilipsHueSettingsHandler::PhilipsHueSettingsHandler() +{ + /*-------------------------------------------------*\ + | Create an object to hold the hue settings json | + | This cannot be a class member as json must not | + | be included in the header file, so it is held as | + | a void pointer instead. | + \*-------------------------------------------------*/ + hue_settings = (void *)(new hue_settings_type); + + /*-------------------------------------------------*\ + | Get Philips Hue settings from settings manager | + \*-------------------------------------------------*/ + HUE_SETTINGS = ResourceManager::get()->GetSettingsManager()->GetSettings("PhilipsHueDevices"); +} + +PhilipsHueSettingsHandler::~PhilipsHueSettingsHandler() +{ + delete (hue_settings_type *)hue_settings; +} + +std::size_t PhilipsHueSettingsHandler::GetBridgeCount() +{ + if(HUE_SETTINGS.contains("bridges")) + { + return(HUE_SETTINGS["bridges"].size()); + } + else + { + return(0); + } +} + +std::string PhilipsHueSettingsHandler::GetBridgeIP(unsigned int bridge_idx) +{ + if(HUE_SETTINGS["bridges"][bridge_idx].contains("ip")) + { + return(HUE_SETTINGS["bridges"][bridge_idx]["ip"]); + } + else + { + return(""); + } +} + +std::string PhilipsHueSettingsHandler::GetBridgeMAC(unsigned int bridge_idx) +{ + if(HUE_SETTINGS["bridges"][bridge_idx].contains("mac")) + { + return(HUE_SETTINGS["bridges"][bridge_idx]["mac"]); + } + else + { + return(""); + } +} + +std::string PhilipsHueSettingsHandler::GetBridgeUsername(unsigned int bridge_idx) +{ + if(HUE_SETTINGS["bridges"][bridge_idx].contains("username")) + { + return(HUE_SETTINGS["bridges"][bridge_idx]["username"]); + } + else + { + return(""); + } +} + +std::string PhilipsHueSettingsHandler::GetBridgeClientKey(unsigned int bridge_idx) +{ + if(HUE_SETTINGS["bridges"][bridge_idx].contains("clientkey")) + { + return(HUE_SETTINGS["bridges"][bridge_idx]["clientkey"]); + } + else + { + return(""); + } +} + +bool PhilipsHueSettingsHandler::GetBridgeAutoconnect(unsigned int bridge_idx) +{ + if(HUE_SETTINGS["bridges"][bridge_idx].contains("autoconnect")) + { + return(HUE_SETTINGS["bridges"][bridge_idx]["autoconnect"]); + } + else + { + return(false); + } +} + +bool PhilipsHueSettingsHandler::GetBridgeUseEntertainment(unsigned int bridge_idx) +{ + if(HUE_SETTINGS["bridges"][bridge_idx].contains("entertainment")) + { + return(HUE_SETTINGS["bridges"][bridge_idx]["entertainment"]); + } + else + { + return(false); + } +} + +bool PhilipsHueSettingsHandler::BridgeHasUsername(unsigned int bridge_idx) +{ + return(HUE_SETTINGS["bridges"][bridge_idx].contains("username")); +} + +bool PhilipsHueSettingsHandler::BridgeHasClientKey(unsigned int bridge_idx) +{ + return(HUE_SETTINGS["bridges"][bridge_idx].contains("clientkey")); +} + +void PhilipsHueSettingsHandler::SetBridgeUsername(unsigned int bridge_idx, std::string username) +{ + HUE_SETTINGS["bridges"][bridge_idx]["username"] = username; +} + +void PhilipsHueSettingsHandler::SetBridgeClientKey(unsigned int bridge_idx, std::string clientkey) +{ + HUE_SETTINGS["bridges"][bridge_idx]["clientkey"] = clientkey; +} + +void PhilipsHueSettingsHandler::SetBridgeAutoconnect(unsigned int bridge_idx, bool auto_connect) +{ + HUE_SETTINGS["bridges"][bridge_idx]["autoconnect"] = auto_connect; +} + +void PhilipsHueSettingsHandler::SetBridgeUseEntertainment(unsigned int bridge_idx, bool use_entertainment) +{ + HUE_SETTINGS["bridges"][bridge_idx]["entertainment"] = use_entertainment; +} + +void PhilipsHueSettingsHandler::SaveSettings() +{ + ResourceManager::get()->GetSettingsManager()->SetSettings("PhilipsHueDevices", HUE_SETTINGS); + ResourceManager::get()->GetSettingsManager()->SaveSettings(); +} diff --git a/Controllers/PhilipsHueController/PhilipsHueSettingsHandler.h b/Controllers/PhilipsHueController/PhilipsHueSettingsHandler.h new file mode 100644 index 0000000..04ce1fc --- /dev/null +++ b/Controllers/PhilipsHueController/PhilipsHueSettingsHandler.h @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| PhilipsHueSettingsHandler.h | +| | +| Settings Handler for Philips Hue | +| Due to conflict in jsoh.hpp library, hueplusplus and | +| SettingsManager should not be included in the same file | +| so handle settings in a separate class. | +| | +| Adam Honse (calcprogrammer1@gmail.com) 17 Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +class PhilipsHueSettingsHandler +{ +public: + PhilipsHueSettingsHandler(); + ~PhilipsHueSettingsHandler(); + + std::size_t GetBridgeCount(); + + std::string GetBridgeIP(unsigned int bridge_idx); + std::string GetBridgeMAC(unsigned int bridge_idx); + std::string GetBridgeUsername(unsigned int bridge_idx); + std::string GetBridgeClientKey(unsigned int bridge_idx); + bool GetBridgeAutoconnect(unsigned int bridge_idx); + bool GetBridgeUseEntertainment(unsigned int bridge_idx); + + bool BridgeHasUsername(unsigned int bridge_idx); + bool BridgeHasClientKey(unsigned int bridge_idx); + + void SetBridgeUsername(unsigned int bridge_idx, std::string username); + void SetBridgeClientKey(unsigned int bridge_idx, std::string clientkey); + void SetBridgeAutoconnect(unsigned int bridge_ip, bool auto_connect); + void SetBridgeUseEntertainment(unsigned int bridge_idx, bool use_entertainment); + + void SaveSettings(); + +private: + void * hue_settings; +}; diff --git a/Controllers/PhilipsHueController/RGBController_PhilipsHue.cpp b/Controllers/PhilipsHueController/RGBController_PhilipsHue.cpp new file mode 100644 index 0000000..029908d --- /dev/null +++ b/Controllers/PhilipsHueController/RGBController_PhilipsHue.cpp @@ -0,0 +1,94 @@ +/*---------------------------------------------------------*\ +| RGBController_PhilipsHue.cpp | +| | +| RGBController for Philips Hue | +| | +| Adam Honse (calcprogrammer1@gmail.com) 15 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_PhilipsHue.h" + +/**------------------------------------------------------------------*\ + @name Philips Hue + @category Light + @type Network + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectPhilipsHueControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PhilipsHue::RGBController_PhilipsHue(PhilipsHueController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetManufacturer() + " " + controller->GetName(); + type = DEVICE_TYPE_LIGHT; + version = controller->GetVersion(); + description = "Philips Hue Device"; + serial = controller->GetUniqueID(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +void RGBController_PhilipsHue::SetupZones() +{ + zone led_zone; + led_zone.name = "RGB Light"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + led new_led; + new_led.name = "RGB Light"; + + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_PhilipsHue::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PhilipsHue::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetColor(red, grn, blu); +} + +void RGBController_PhilipsHue::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PhilipsHue::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PhilipsHue::DeviceUpdateMode() +{ + +} diff --git a/Controllers/PhilipsHueController/RGBController_PhilipsHue.h b/Controllers/PhilipsHueController/RGBController_PhilipsHue.h new file mode 100644 index 0000000..0d929dd --- /dev/null +++ b/Controllers/PhilipsHueController/RGBController_PhilipsHue.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_PhilipsHue.h | +| | +| RGBController for Philips Hue | +| | +| Adam Honse (calcprogrammer1@gmail.com) 15 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "PhilipsHueController.h" + +class RGBController_PhilipsHue : public RGBController +{ +public: + RGBController_PhilipsHue(PhilipsHueController* controller_ptr); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PhilipsHueController* controller; +}; diff --git a/Controllers/PhilipsHueController/RGBController_PhilipsHueEntertainment.cpp b/Controllers/PhilipsHueController/RGBController_PhilipsHueEntertainment.cpp new file mode 100644 index 0000000..3f18c88 --- /dev/null +++ b/Controllers/PhilipsHueController/RGBController_PhilipsHueEntertainment.cpp @@ -0,0 +1,156 @@ +/*---------------------------------------------------------*\ +| RGBController_PhilipsHueEntertainment.cpp | +| | +| RGBController for Philips Hue Entertainment Mode | +| | +| Adam Honse (calcprogrammer1@gmail.com) 07 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_PhilipsHueEntertainment.h" +#include "ResourceManager.h" + +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name Philips Hue Entertainment + @category Light + @type Network + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectPhilipsHueControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PhilipsHueEntertainment::RGBController_PhilipsHueEntertainment(PhilipsHueEntertainmentController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetManufacturer() + " " + controller->GetName(); + type = DEVICE_TYPE_LIGHT; + version = controller->GetVersion(); + description = "Philips Hue Entertainment Mode Device"; + serial = controller->GetUniqueID(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Disconnected; + Disconnected.name = "Disconnected"; + Disconnected.value = 1; + Disconnected.flags = 0; + Disconnected.color_mode = MODE_COLORS_NONE; + modes.push_back(Disconnected); + + SetupZones(); + + /*-----------------------------------------------------------------------------------------------------*\ + | The Philips Hue Entertainment Mode only supports one stream at a time. So we must start Disconnected. | + | https://developers.meethue.com/develop/hue-entertainment/philips-hue-entertainment-api/ | + \*-----------------------------------------------------------------------------------------------------*/ + + active_mode = 1; + + /*-----------------------------------------------------*\ + | The Philips Hue Entertainment Mode requires a packet | + | within 10 seconds of sending the lighting change in | + | order to not exit entertainment mode. Start a thread | + | to continuously send a packet every 5s | + \*-----------------------------------------------------*/ + KeepaliveThreadRunning = true; + KeepaliveThread = new std::thread(&RGBController_PhilipsHueEntertainment::KeepaliveThreadFunction, this); +} + +void RGBController_PhilipsHueEntertainment::SetupZones() +{ + zone led_zone; + led_zone.name = "RGB Light"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = controller->GetNumLEDs(); + led_zone.leds_max = controller->GetNumLEDs(); + led_zone.leds_count = controller->GetNumLEDs(); + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + for(unsigned int led_idx = 0; led_idx < controller->GetNumLEDs(); led_idx++) + { + led new_led; + new_led.name = "RGB Light"; + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_PhilipsHueEntertainment::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PhilipsHueEntertainment::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + if(active_mode == 0) + { + controller->SetColor(&colors[0]); + } +} + +void RGBController_PhilipsHueEntertainment::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PhilipsHueEntertainment::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PhilipsHueEntertainment::DeviceUpdateMode() +{ + if(active_mode == 0) + { + std::vector rgb_controllers = ResourceManager::get()->GetRGBControllers(); + + for(unsigned int controller_idx = 0; controller_idx < rgb_controllers.size(); controller_idx++) + { + if(rgb_controllers[controller_idx] != this && rgb_controllers[controller_idx]->GetDescription() == "Philips Hue Entertainment Mode Device" && rgb_controllers[controller_idx]->active_mode == 0) + { + rgb_controllers[controller_idx]->SetMode(1); + } + } + + controller->Connect(); + } + else + { + controller->Disconnect(); + } +} + +void RGBController_PhilipsHueEntertainment::KeepaliveThreadFunction() +{ + while(KeepaliveThreadRunning) + { + if(active_mode == 0) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::seconds(5)) + { + UpdateLEDs(); + } + } + std::this_thread::sleep_for(1s); + } +} diff --git a/Controllers/PhilipsHueController/RGBController_PhilipsHueEntertainment.h b/Controllers/PhilipsHueController/RGBController_PhilipsHueEntertainment.h new file mode 100644 index 0000000..05d13c7 --- /dev/null +++ b/Controllers/PhilipsHueController/RGBController_PhilipsHueEntertainment.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_PhilipsHueEntertainment.h | +| | +| RGBController for Philips Hue Entertainment Mode | +| | +| Adam Honse (calcprogrammer1@gmail.com) 07 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "PhilipsHueEntertainmentController.h" + +class RGBController_PhilipsHueEntertainment : public RGBController +{ +public: + RGBController_PhilipsHueEntertainment(PhilipsHueEntertainmentController* controller_ptr); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void KeepaliveThreadFunction(); + +private: + PhilipsHueEntertainmentController* controller; + + std::atomic KeepaliveThreadRunning; + std::thread* KeepaliveThread; + + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/PhilipsWizController/PhilipsWizController.cpp b/Controllers/PhilipsWizController/PhilipsWizController.cpp new file mode 100644 index 0000000..94944f9 --- /dev/null +++ b/Controllers/PhilipsWizController/PhilipsWizController.cpp @@ -0,0 +1,264 @@ +/*---------------------------------------------------------*\ +| PhilipsWizController.cpp | +| | +| Driver for Philips Wiz | +| | +| Adam Honse (calcprogrammer1@gmail.com) 03 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "PhilipsWizController.h" +#include + +using json = nlohmann::json; +using namespace std::chrono_literals; + +PhilipsWizController::PhilipsWizController(std::string ip, bool use_cool, bool use_warm, std::string selected_white_strategy) +{ + /*-----------------------------------------------------------------*\ + | Fill in location string with device's IP address | + \*-----------------------------------------------------------------*/ + location = "IP: " + ip; + + /*-----------------------------------------------------------------*\ + | Fill in settings | + \*-----------------------------------------------------------------*/ + use_cool_white = use_cool; + use_warm_white = use_warm; + white_strategy = selected_white_strategy; + + /*-----------------------------------------------------------------*\ + | Open a UDP client sending to the device's IP, port 38899 | + \*-----------------------------------------------------------------*/ + port.udp_client(ip.c_str(), "38899"); + + /*-----------------------------------------------------------------*\ + | Start a thread to handle responses received from the Wiz device | + \*-----------------------------------------------------------------*/ + ReceiveThreadRun = 1; + ReceiveThread = new std::thread(&PhilipsWizController::ReceiveThreadFunction, this); + + /*-----------------------------------------------------------------*\ + | Request the system config (name, firmware version, MAC address) | + \*-----------------------------------------------------------------*/ + RequestSystemConfig(); +} + +PhilipsWizController::~PhilipsWizController() +{ + ReceiveThreadRun = 0; + ReceiveThread->join(); + delete ReceiveThread; +} + +std::string PhilipsWizController::GetLocation() +{ + return(location); +} + +std::string PhilipsWizController::GetName() +{ + return("Wiz"); +} + +std::string PhilipsWizController::GetVersion() +{ + return(module_name + " " + firmware_version); +} + +std::string PhilipsWizController::GetModuleName() +{ + return(module_name); +} + +std::string PhilipsWizController::GetManufacturer() +{ + return("Philips"); +} + +std::string PhilipsWizController::GetUniqueID() +{ + return(module_mac); +} + +void PhilipsWizController::SetColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char brightness) +{ + json command; + unsigned char white; + + /*-----------------------------------------------------------------*\ + | The official Wiz app also sends a warm white level with its | + | custom colours. Until we can figure out a way to account for it | + | correctly, set the white level based on selected strategy. | + \*-----------------------------------------------------------------*/ + if(white_strategy == "Average") + { + white = (red + green + blue) / 3; + } + else if(white_strategy == "Minimum") + { + white = std::min(std::min(red, green), blue); + if(use_cool_white || use_warm_white) + { + red = red - white; + green = green - white; + blue = blue - white; + } + } + else + { + white = 0; + } + + if(use_cool_white) + { + command["params"]["c"] = white; + } + else + { + command["params"]["c"] = 0; + } + + if(use_warm_white) + { + command["params"]["w"] = white; + } + else + { + command["params"]["w"] = 0; + } + + + /*-----------------------------------------------------------------*\ + | Fill in the setPilot command with RGB and brightness information. | + | The bulb will not respond to 0, 0, 0, so if all channels are zero,| + | set the state to off. Otherwise, set it to on. As we're also | + | running direct the bulb needs to be set back to max brightness. | + \*-----------------------------------------------------------------*/ + command["method"] = "setPilot"; + command["params"]["r"] = red; + command["params"]["g"] = green; + command["params"]["b"] = blue; + command["params"]["dimming"] = brightness; + command["params"]["state"] = !((red == 0) && (green == 0) && (blue == 0) && (white == 0)); + + /*-----------------------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------------------*/ + std::string command_str = command.dump(); + + port.udp_write((char *)command_str.c_str(), (int)command_str.length() + 1); +} + +void PhilipsWizController::SetScene(int scene, unsigned char brightness) +{ + json command; + + /*------------------------------------------------------------*\ + | Fill in the setPilot command with Scene information. | + \*------------------------------------------------------------*/ + command["method"] = "setPilot"; + command["params"]["sceneId"] = scene; + command["params"]["dimming"] = brightness; + + /*------------------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*------------------------------------------------------------*/ + std::string command_str = command.dump(); + + port.udp_write((char*)command_str.c_str(), (int)command_str.length() + 1); +} + +void PhilipsWizController::ReceiveThreadFunction() +{ + char recv_buf[1025]; + + port.set_receive_timeout(1, 0); + + while(ReceiveThreadRun.load()) + { + /*-----------------------------------------------------------------*\ + | Receive up to 1024 bytes from the device with a 1s timeout | + \*-----------------------------------------------------------------*/ + int size = port.udp_listen(recv_buf, 1024); + + if(size > 0) + { + /*-----------------------------------------------------------------*\ + | Responses are not null-terminated, so add termination | + \*-----------------------------------------------------------------*/ + recv_buf[size] = '\0'; + + /*-----------------------------------------------------------------*\ + | Convert null-terminated response to JSON | + \*-----------------------------------------------------------------*/ + json response = json::parse(recv_buf); + + /*-----------------------------------------------------------------*\ + | Check if the response contains the method name | + \*-----------------------------------------------------------------*/ + if(response.contains("method")) + { + /*-------------------------------------------------------------*\ + | Handle responses for getSystemConfig method | + | This method's response should contain a result object | + | containing fwVersion, moduleName, and mac, among others. | + \*-------------------------------------------------------------*/ + if(response["method"] == "getSystemConfig") + { + if(response.contains("result")) + { + json result = response["result"]; + + if(result.contains("fwVersion")) + { + firmware_version = result["fwVersion"]; + } + + if(result.contains("moduleName")) + { + module_name = result["moduleName"]; + } + + if(result.contains("mac")) + { + module_mac = result["mac"]; + } + } + } + } + } + } +} + +void PhilipsWizController::RequestSystemConfig() +{ + json command; + + /*-----------------------------------------------------------------*\ + | Fill in the getSystemConfig command | + \*-----------------------------------------------------------------*/ + command["method"] = "getSystemConfig"; + + /*-----------------------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------------------*/ + std::string command_str = command.dump(); + + port.udp_write((char *)command_str.c_str(), (int)command_str.length() + 1); + + /*-----------------------------------------------------------------*\ + | Wait up to 1s to give it time to receive and process response | + \*-----------------------------------------------------------------*/ + for(unsigned int wait_count = 0; wait_count < 100; wait_count++) + { + if(firmware_version != "") + { + return; + } + + std::this_thread::sleep_for(10ms); + } +} diff --git a/Controllers/PhilipsWizController/PhilipsWizController.h b/Controllers/PhilipsWizController/PhilipsWizController.h new file mode 100644 index 0000000..ae77486 --- /dev/null +++ b/Controllers/PhilipsWizController/PhilipsWizController.h @@ -0,0 +1,94 @@ +/*---------------------------------------------------------*\ +| PhilipsWizController.h | +| | +| Driver for Philips Wiz | +| | +| Adam Honse (calcprogrammer1@gmail.com) 03 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "net_port.h" + +#define PHILIPSWIZ_BRIGHTNESS_MAX 100 +#define PHILIPSWIZ_BRIGHTNESS_MIN 10 + +enum +{ + PHILLIPSWIZ_MODE_STATIC = 0, + PHILLIPSWIZ_MODE_OCEAN = 1, + PHILLIPSWIZ_MODE_ROMANCE = 2, + PHILLIPSWIZ_MODE_SUNSET = 3, + PHILLIPSWIZ_MODE_PARTY = 4, + PHILLIPSWIZ_MODE_FIREPLACE = 5, + PHILLIPSWIZ_MODE_COZY = 6, + PHILLIPSWIZ_MODE_FOREST = 7, + PHILLIPSWIZ_MODE_PASTEL_COLORS = 8, + PHILLIPSWIZ_MODE_WAKE_UP = 9, + PHILLIPSWIZ_MODE_BEDTIME = 10, + PHILLIPSWIZ_MODE_WARM_WHITE = 11, + PHILLIPSWIZ_MODE_DAYLIGHT = 12, + PHILLIPSWIZ_MODE_COOL_WHITE = 13, + PHILLIPSWIZ_MODE_NIGHT_LIGHT = 14, + PHILLIPSWIZ_MODE_FOCUS = 15, + PHILLIPSWIZ_MODE_RELAX = 16, + PHILLIPSWIZ_MODE_TRUE_COLORS = 17, + PHILLIPSWIZ_MODE_TV_TIME = 18, + PHILLIPSWIZ_MODE_PLANTGROWTH = 19, + PHILLIPSWIZ_MODE_SPRING = 20, + PHILLIPSWIZ_MODE_SUMMER = 21, + PHILLIPSWIZ_MODE_FALL = 22, + PHILLIPSWIZ_MODE_DEEPDIVE = 23, + PHILLIPSWIZ_MODE_JUNGLE = 24, + PHILLIPSWIZ_MODE_MOJITO = 25, + PHILLIPSWIZ_MODE_CLUB = 26, + PHILLIPSWIZ_MODE_CHRISTMAS = 27, + PHILLIPSWIZ_MODE_HALLOWEEN = 28, + PHILLIPSWIZ_MODE_CANDLELIGHT = 29, + PHILLIPSWIZ_MODE_GOLDEN_WHITE = 30, + PHILLIPSWIZ_MODE_PULSE = 31, + PHILLIPSWIZ_MODE_STEAMPUNK = 32 +}; + +class PhilipsWizController +{ +public: + PhilipsWizController(std::string ip, bool use_cool, bool use_warm, std::string selected_white_strategy); + ~PhilipsWizController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetVersion(); + std::string GetModuleName(); + std::string GetManufacturer(); + std::string GetUniqueID(); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char brightness); + + void SetScene(int scene, unsigned char brightness); + + void ReceiveThreadFunction(); + void RequestSystemConfig(); + +private: + std::string firmware_version; + std::string module_name; + std::string module_mac; + std::string location; + net_port port; + std::thread* ReceiveThread; + std::atomic ReceiveThreadRun; + + bool use_cool_white; + bool use_warm_white; + std::string white_strategy; + + void SendSetPilot(); +}; diff --git a/Controllers/PhilipsWizController/PhilipsWizControllerDetect.cpp b/Controllers/PhilipsWizController/PhilipsWizControllerDetect.cpp new file mode 100644 index 0000000..1d79a08 --- /dev/null +++ b/Controllers/PhilipsWizController/PhilipsWizControllerDetect.cpp @@ -0,0 +1,73 @@ +/*---------------------------------------------------------*\ +| PhilipsWizControllerDetect.cpp | +| | +| Detector for Philips Wiz | +| | +| Adam Honse (calcprogrammer1@gmail.com) 03 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "PhilipsWizController.h" +#include "RGBController_PhilipsWiz.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectPhilipsWizControllers * +* * +* Detect Philips Wiz devices * +* * +\******************************************************************************************/ + +void DetectPhilipsWizControllers() +{ + json wiz_settings; + + /*-------------------------------------------------*\ + | Get Philips Wiz settings from settings manager | + \*-------------------------------------------------*/ + wiz_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("PhilipsWizDevices"); + + /*-------------------------------------------------*\ + | If the Wiz settings contains devices, process | + \*-------------------------------------------------*/ + if(wiz_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < wiz_settings["devices"].size(); device_idx++) + { + if(wiz_settings["devices"][device_idx].contains("ip")) + { + std::string wiz_ip = wiz_settings["devices"][device_idx]["ip"]; + + bool wiz_cool = false; + + if(wiz_settings["devices"][device_idx].contains("use_cool_white")) + { + wiz_cool = wiz_settings["devices"][device_idx]["use_cool_white"]; + } + + bool wiz_warm = false; + if(wiz_settings["devices"][device_idx].contains("use_warm_white")) + { + wiz_warm = wiz_settings["devices"][device_idx]["use_warm_white"]; + } + std::string wiz_white_strategy = "Average"; + if(wiz_settings["devices"][device_idx].contains("selected_white_strategy")) + { + wiz_white_strategy = wiz_settings["devices"][device_idx]["selected_white_strategy"]; + } + + PhilipsWizController* controller = new PhilipsWizController(wiz_ip, wiz_cool, wiz_warm, wiz_white_strategy); + RGBController_PhilipsWiz* rgb_controller = new RGBController_PhilipsWiz(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } + +} /* DetectPhilipsWizControllers() */ + +REGISTER_DETECTOR("Philips Wiz", DetectPhilipsWizControllers); diff --git a/Controllers/PhilipsWizController/RGBController_PhilipsWiz.cpp b/Controllers/PhilipsWizController/RGBController_PhilipsWiz.cpp new file mode 100644 index 0000000..7b934b4 --- /dev/null +++ b/Controllers/PhilipsWizController/RGBController_PhilipsWiz.cpp @@ -0,0 +1,434 @@ +/*---------------------------------------------------------*\ +| RGBController_PhilipsWiz.cpp | +| | +| RGBController for Philips Wiz | +| | +| Adam Honse (calcprogrammer1@gmail.com) 03 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_PhilipsWiz.h" + +/**------------------------------------------------------------------*\ + @name Philips Wiz + @category Light + @type Network + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectPhilipsWizControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PhilipsWiz::RGBController_PhilipsWiz(PhilipsWizController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetManufacturer() + " " + controller->GetName(); + vendor = controller->GetManufacturer(); + type = DEVICE_TYPE_LIGHT; + version = controller->GetVersion(); + description = "Philips Wiz Device"; + serial = controller->GetUniqueID(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = PHILLIPSWIZ_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + Direct.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Direct.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Direct); + + std::string model = controller->GetModuleName(); + if (model.find("RGB") != std::string::npos) + { + mode WarmWhite; + WarmWhite.name = "Warm White"; + WarmWhite.value = PHILLIPSWIZ_MODE_WARM_WHITE; + WarmWhite.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + WarmWhite.color_mode = MODE_COLORS_PER_LED; + WarmWhite.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + WarmWhite.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + WarmWhite.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(WarmWhite); + + mode Daylight; + Daylight.name = "Daylight"; + Daylight.value = PHILLIPSWIZ_MODE_DAYLIGHT; + Daylight.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Daylight.color_mode = MODE_COLORS_PER_LED; + Daylight.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Daylight.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Daylight.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Daylight); + + mode CoolWhite; + CoolWhite.name = "Cool White"; + CoolWhite.value = PHILLIPSWIZ_MODE_COOL_WHITE; + CoolWhite.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + CoolWhite.color_mode = MODE_COLORS_PER_LED; + CoolWhite.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + CoolWhite.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + CoolWhite.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(CoolWhite); + + mode Ocean; + Ocean.name = "Ocean"; + Ocean.value = PHILLIPSWIZ_MODE_OCEAN; + Ocean.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Ocean.color_mode = MODE_COLORS_PER_LED; + Ocean.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Ocean.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Ocean.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Ocean); + + mode Romance; + Romance.name = "Romance"; + Romance.value = PHILLIPSWIZ_MODE_ROMANCE; + Romance.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Romance.color_mode = MODE_COLORS_PER_LED; + Romance.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Romance.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Romance.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Romance); + + mode Sunset; + Sunset.name = "Sunset"; + Sunset.value = PHILLIPSWIZ_MODE_SUNSET; + Sunset.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Sunset.color_mode = MODE_COLORS_PER_LED; + Sunset.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Sunset.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Sunset.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Sunset); + + mode Party; + Party.name = "Party"; + Party.value = PHILLIPSWIZ_MODE_PARTY; + Party.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Party.color_mode = MODE_COLORS_PER_LED; + Party.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Party.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Party.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Party); + + mode Fireplace; + Fireplace.name = "Fireplace"; + Fireplace.value = PHILLIPSWIZ_MODE_FIREPLACE; + Fireplace.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Fireplace.color_mode = MODE_COLORS_PER_LED; + Fireplace.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Fireplace.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Fireplace.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Fireplace); + + mode Cozy; + Cozy.name = "Cozy"; + Cozy.value = PHILLIPSWIZ_MODE_COZY; + Cozy.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Cozy.color_mode = MODE_COLORS_PER_LED; + Cozy.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Cozy.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Cozy.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Cozy); + + mode Forest; + Forest.name = "Forest"; + Forest.value = PHILLIPSWIZ_MODE_FOREST; + Forest.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Forest.color_mode = MODE_COLORS_PER_LED; + Forest.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Forest.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Forest.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Forest); + + mode PastelColors; + PastelColors.name = "Pastel Colors"; + PastelColors.value = PHILLIPSWIZ_MODE_PASTEL_COLORS; + PastelColors.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + PastelColors.color_mode = MODE_COLORS_PER_LED; + PastelColors.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + PastelColors.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + PastelColors.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(PastelColors); + + mode WakeUp; + WakeUp.name = "Wake up"; + WakeUp.value = PHILLIPSWIZ_MODE_WAKE_UP; + WakeUp.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + WakeUp.color_mode = MODE_COLORS_PER_LED; + WakeUp.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + WakeUp.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + WakeUp.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(WakeUp); + + mode Bedtime; + Bedtime.name = "Bedtime"; + Bedtime.value = PHILLIPSWIZ_MODE_BEDTIME; + Bedtime.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Bedtime.color_mode = MODE_COLORS_PER_LED; + Bedtime.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Bedtime.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Bedtime.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Bedtime); + + mode NightLight; + NightLight.name = "Night light"; + NightLight.value = PHILLIPSWIZ_MODE_NIGHT_LIGHT; + NightLight.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + NightLight.color_mode = MODE_COLORS_PER_LED; + NightLight.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + NightLight.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + NightLight.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(NightLight); + + mode Focus; + Focus.name = "Focus"; + Focus.value = PHILLIPSWIZ_MODE_FOCUS; + Focus.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Focus.color_mode = MODE_COLORS_PER_LED; + Focus.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Focus.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Focus.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Focus); + + mode Relax; + Relax.name = "Relax"; + Relax.value = PHILLIPSWIZ_MODE_RELAX; + Relax.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Relax.color_mode = MODE_COLORS_PER_LED; + Relax.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Relax.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Relax.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Relax); + + mode TrueColors; + TrueColors.name = "True colors"; + TrueColors.value = PHILLIPSWIZ_MODE_TRUE_COLORS; + TrueColors.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + TrueColors.color_mode = MODE_COLORS_PER_LED; + TrueColors.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + TrueColors.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + TrueColors.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(TrueColors); + + mode TvTime; + TvTime.name = "TV time"; + TvTime.value = PHILLIPSWIZ_MODE_TV_TIME; + TvTime.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + TvTime.color_mode = MODE_COLORS_PER_LED; + TvTime.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + TvTime.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + TvTime.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(TvTime); + + mode Plantgrowth; + Plantgrowth.name = "Plantgrowth"; + Plantgrowth.value = PHILLIPSWIZ_MODE_PLANTGROWTH; + Plantgrowth.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Plantgrowth.color_mode = MODE_COLORS_PER_LED; + Plantgrowth.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Plantgrowth.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Plantgrowth.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Plantgrowth); + + mode Spring; + Spring.name = "Spring"; + Spring.value = PHILLIPSWIZ_MODE_SPRING; + Spring.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Spring.color_mode = MODE_COLORS_PER_LED; + Spring.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Spring.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Spring.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Spring); + + mode Summer; + Summer.name = "Summer"; + Summer.value = PHILLIPSWIZ_MODE_SUMMER; + Summer.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Summer.color_mode = MODE_COLORS_PER_LED; + Summer.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Summer.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Summer.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Summer); + + mode Fall; + Fall.name = "Fall"; + Fall.value = PHILLIPSWIZ_MODE_FALL; + Fall.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Fall.color_mode = MODE_COLORS_PER_LED; + Fall.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Fall.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Fall.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Fall); + + mode Deepdive; + Deepdive.name = "Deepdive"; + Deepdive.value = PHILLIPSWIZ_MODE_DEEPDIVE; + Deepdive.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Deepdive.color_mode = MODE_COLORS_PER_LED; + Deepdive.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Deepdive.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Deepdive.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Deepdive); + + mode Jungle; + Jungle.name = "Jungle"; + Jungle.value = PHILLIPSWIZ_MODE_JUNGLE; + Jungle.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Jungle.color_mode = MODE_COLORS_PER_LED; + Jungle.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Jungle.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Jungle.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Jungle); + + mode Mojito; + Mojito.name = "Mojito"; + Mojito.value = PHILLIPSWIZ_MODE_MOJITO; + Mojito.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Mojito.color_mode = MODE_COLORS_PER_LED; + Mojito.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Mojito.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Mojito.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Mojito); + + mode Club; + Club.name = "Club"; + Club.value = PHILLIPSWIZ_MODE_CLUB; + Club.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Club.color_mode = MODE_COLORS_PER_LED; + Club.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Club.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Club.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Club); + + mode Christmas; + Christmas.name = "Christmas"; + Christmas.value = PHILLIPSWIZ_MODE_CHRISTMAS; + Christmas.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Christmas.color_mode = MODE_COLORS_PER_LED; + Christmas.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Christmas.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Christmas.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Christmas); + + mode Halloween; + Halloween.name = "Halloween"; + Halloween.value = PHILLIPSWIZ_MODE_HALLOWEEN; + Halloween.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Halloween.color_mode = MODE_COLORS_PER_LED; + Halloween.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Halloween.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Halloween.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Halloween); + + mode Candlelight; + Candlelight.name = "Candlelight"; + Candlelight.value = PHILLIPSWIZ_MODE_CANDLELIGHT; + Candlelight.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Candlelight.color_mode = MODE_COLORS_PER_LED; + Candlelight.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Candlelight.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Candlelight.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Candlelight); + + mode GoldenWhite; + GoldenWhite.name = "Golden white"; + GoldenWhite.value = PHILLIPSWIZ_MODE_GOLDEN_WHITE; + GoldenWhite.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + GoldenWhite.color_mode = MODE_COLORS_PER_LED; + GoldenWhite.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + GoldenWhite.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + GoldenWhite.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(GoldenWhite); + + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = PHILLIPSWIZ_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Pulse.color_mode = MODE_COLORS_PER_LED; + Pulse.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Pulse.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Pulse.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Pulse); + + mode Steampunk; + Steampunk.name = "Steampunk"; + Steampunk.value = PHILLIPSWIZ_MODE_STEAMPUNK; + Steampunk.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Steampunk.color_mode = MODE_COLORS_PER_LED; + Steampunk.brightness_min = PHILIPSWIZ_BRIGHTNESS_MIN; + Steampunk.brightness_max = PHILIPSWIZ_BRIGHTNESS_MAX; + Steampunk.brightness = PHILIPSWIZ_BRIGHTNESS_MAX; + modes.push_back(Steampunk); + + } + + SetupZones(); +} + +RGBController_PhilipsWiz::~RGBController_PhilipsWiz() +{ + delete controller; +} + +void RGBController_PhilipsWiz::SetupZones() +{ + zone led_zone; + led_zone.name = "RGB Light"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + led new_led; + new_led.name = "RGB Light"; + + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_PhilipsWiz::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PhilipsWiz::DeviceUpdateLEDs() +{ + if (modes[active_mode].value == PHILLIPSWIZ_MODE_STATIC) + { + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetColor(red, grn, blu, modes[active_mode].brightness); + } +} + +void RGBController_PhilipsWiz::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PhilipsWiz::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PhilipsWiz::DeviceUpdateMode() +{ + if (modes[active_mode].value != PHILLIPSWIZ_MODE_STATIC) + { + controller->SetScene(modes[active_mode].value, modes[active_mode].brightness); + } +} diff --git a/Controllers/PhilipsWizController/RGBController_PhilipsWiz.h b/Controllers/PhilipsWizController/RGBController_PhilipsWiz.h new file mode 100644 index 0000000..9d1f463 --- /dev/null +++ b/Controllers/PhilipsWizController/RGBController_PhilipsWiz.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_PhilipsWiz.h | +| | +| RGBController for Philips Wiz | +| | +| Adam Honse (calcprogrammer1@gmail.com) 03 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "PhilipsWizController.h" + +class RGBController_PhilipsWiz : public RGBController +{ +public: + RGBController_PhilipsWiz(PhilipsWizController* controller_ptr); + ~RGBController_PhilipsWiz(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PhilipsWizController* controller; +}; diff --git a/Controllers/PowerColorGPUController/PowerColorGPUControllerDetect.cpp b/Controllers/PowerColorGPUController/PowerColorGPUControllerDetect.cpp new file mode 100644 index 0000000..e45a6a2 --- /dev/null +++ b/Controllers/PowerColorGPUController/PowerColorGPUControllerDetect.cpp @@ -0,0 +1,75 @@ +/*---------------------------------------------------------*\ +| PowerColorGPUControllerDetect.cpp | +| | +| Driver for PowerColor GPUs | +| | +| Nexrem 15 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "pci_ids.h" +#include "i2c_amd_gpu.h" +#include "PowerColorRedDevilV1Controller/RGBController_PowerColorRedDevilV1.h" +#include "PowerColorRedDevilV1Controller/PowerColorRedDevilV1Controller.h" +#include "PowerColorRedDevilV2Controller/RGBController_PowerColorRedDevilV2.h" +#include "PowerColorRedDevilV2Controller/PowerColorRedDevilV2Controller.h" + +static const unsigned char magic_v1[3] = {0x01, 0x05, 0x00}; +static const unsigned char magic_v2[3] = {0x01, 0x32, 0x00}; + +/*---------------------------------------------------------*\ +| The controller reports a unique identifier for V1 and V2. | +| Unfortunately they are on different addresses. Read it | +| for good measure anyways. | +| N.B: Some V2 controllers report the V1 magic. | +\*---------------------------------------------------------*/ + +void DetectPowerColorRedDevilGPUControllersV1(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(!is_amd_gpu_i2c_bus(bus)) + { + return; + } + + unsigned char data[3]; + int ret = bus->i2c_smbus_read_i2c_block_data(i2c_addr, RED_DEVIL_V1_REG_MAGIC, 3, data); + if(ret == 3 && memcmp(data, magic_v1, 3) == 0) + { + PowerColorRedDevilV1Controller* controller = new PowerColorRedDevilV1Controller(bus, i2c_addr, name); + RGBController_PowerColorRedDevilV1* rgb_controller = new RGBController_PowerColorRedDevilV1(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + + +void DetectPowerColorRedDevilGPUControllersV2(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(!is_amd_gpu_i2c_bus(bus)) + { + return; + } + + unsigned char data[3]; + int ret = bus->i2c_smbus_read_i2c_block_data(i2c_addr, RED_DEVIL_V2_READ_REG_MAGIC, 3, data); + if(ret == 3 && (memcmp(data, magic_v1, 3) == 0 || memcmp(data, magic_v2, 3) == 0)) + { + PowerColorRedDevilV2Controller* controller = new PowerColorRedDevilV2Controller(bus, i2c_addr, name); + RGBController_PowerColorRedDevilV2* rgb_controller = new RGBController_PowerColorRedDevilV2(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX5700", DetectPowerColorRedDevilGPUControllersV1, AMD_GPU_VEN, AMD_NAVI10_DEV, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX5700_SUB_DEV, 0x22); +REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX5700XT", DetectPowerColorRedDevilGPUControllersV1, AMD_GPU_VEN, AMD_NAVI10_DEV, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX5700XT_SUB_DEV, 0x22); +REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX6750XT", DetectPowerColorRedDevilGPUControllersV1, AMD_GPU_VEN, AMD_NAVI22_DEV, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX6750XT_SUB_DEV, 0x22); +REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX6800XT", DetectPowerColorRedDevilGPUControllersV1, AMD_GPU_VEN, AMD_NAVI21_DEV1, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX6800XT_SUB_DEV, 0x22); +REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX6900XT", DetectPowerColorRedDevilGPUControllersV1, AMD_GPU_VEN, AMD_NAVI21_DEV1, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX6900XT_SUB_DEV, 0x22); +REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX6900XT", DetectPowerColorRedDevilGPUControllersV1, AMD_GPU_VEN, AMD_NAVI21_DEV2, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX6900XT_SUB_DEV, 0x22); +REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX6900XT Ultimate", DetectPowerColorRedDevilGPUControllersV1, AMD_GPU_VEN, AMD_NAVI21_DEV2, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX6900XT_ULTIMATE_SUB_DEV, 0x22); + +REGISTER_I2C_PCI_DETECTOR("PowerColor Red Devil RX9070XT", DetectPowerColorRedDevilGPUControllersV2, AMD_GPU_VEN, AMD_NAVI48_DEV, POWERCOLOR_SUB_VEN, POWERCOLOR_RED_DEVIL_RX9070XT_SUB_DEV, 0x22); diff --git a/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/PowerColorRedDevilV1Controller.cpp b/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/PowerColorRedDevilV1Controller.cpp new file mode 100644 index 0000000..6f78d31 --- /dev/null +++ b/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/PowerColorRedDevilV1Controller.cpp @@ -0,0 +1,149 @@ +/*---------------------------------------------------------*\ +| PowerColorRedDevilV1Controller.cpp | +| | +| Driver for PowerColor Red Devil GPU | +| | +| Jana Rettig (SapphicKitten) 14 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "pci_ids.h" +#include "PowerColorRedDevilV1Controller.h" + +using namespace std::chrono_literals; + +PowerColorRedDevilV1Controller::PowerColorRedDevilV1Controller(i2c_smbus_interface* bus, red_devil_v1_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; + + if(bus->pci_device > AMD_NAVI10_DEV) // Only Navi 2 cards have this mode + { + this->has_sync_mode = true; + } +} + +PowerColorRedDevilV1Controller::~PowerColorRedDevilV1Controller() +{ + +} + +std::string PowerColorRedDevilV1Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C:" + return_string); +} + +std::string PowerColorRedDevilV1Controller::GetDeviceName() +{ + return(name); +} + +void PowerColorRedDevilV1Controller::SetLEDColor(int led, RGBColor color) +{ + if(led > RED_DEVIL_V1_LED_MAX_COUNT) + { + return; + } + + unsigned char data[3] = + { + (unsigned char)RGBGetRValue(color), + (unsigned char)RGBGetGValue(color), + (unsigned char)RGBGetBValue(color), + }; + + RegisterWrite(RED_DEVIL_V1_REG_LED_1 + led, data); +} + +RGBColor PowerColorRedDevilV1Controller::GetLEDColor(int led) +{ + if(led > RED_DEVIL_V1_LED_MAX_COUNT) + { + return RGBColor(0); + } + + unsigned char data[3] = {0}; + RegisterRead(RED_DEVIL_V1_REG_LED_1 + RED_DEVIL_V1_READ_OFFSET, data); + return ToRGBColor(data[0], data[1], data[2]); +} + +void PowerColorRedDevilV1Controller::SetLEDColorAll(RGBColor color) +{ + unsigned char data[3] = + { + (unsigned char)RGBGetRValue(color), + (unsigned char)RGBGetGValue(color), + (unsigned char)RGBGetBValue(color), + }; + + RegisterWrite(RED_DEVIL_V1_REG_LED_ALL, data); +} + +void PowerColorRedDevilV1Controller::SetModeColor(RGBColor color) +{ + unsigned char data[3] = + { + (unsigned char)RGBGetRValue(color), + (unsigned char)RGBGetGValue(color), + (unsigned char)RGBGetBValue(color), + }; + + RegisterWrite(RED_DEVIL_V1_REG_MODE_COLOR, data); +} + +RGBColor PowerColorRedDevilV1Controller::GetModeColor() +{ + unsigned char data[3] = {0}; + RegisterRead(RED_DEVIL_V1_REG_MODE_COLOR + RED_DEVIL_V1_READ_OFFSET, data); + return ToRGBColor(data[0], data[1], data[2]); +} + +void PowerColorRedDevilV1Controller::SetMode(red_devil_v1_mode config) +{ + if(config.mode == RED_DEVIL_V1_MODE_MB_SYNC) + { + unsigned char data[3] = {1, 0, 1}; + RegisterWrite(RED_DEVIL_V1_REG_MB_SYNC, data); + } + else + { + unsigned char data[3] = {0}; + RegisterWrite(RED_DEVIL_V1_REG_MB_SYNC, data); + RegisterWrite(RED_DEVIL_V1_REG_MODE, (unsigned char *)&config); + } +} + +red_devil_v1_mode PowerColorRedDevilV1Controller::GetMode() +{ + unsigned char data[3] = {0}; + RegisterRead(RED_DEVIL_V1_REG_MB_SYNC + RED_DEVIL_V1_READ_OFFSET, data); + if(data[0] != 0 && this->has_sync_mode) + { + return red_devil_v1_mode{RED_DEVIL_V1_MODE_MB_SYNC, 0, 0}; + } + + RegisterRead(RED_DEVIL_V1_REG_MODE + RED_DEVIL_V1_READ_OFFSET, data); + return red_devil_v1_mode{data[0], data[1], data[2]}; +} + +int PowerColorRedDevilV1Controller::RegisterRead(unsigned char reg, unsigned char *data) +{ + int ret = bus->i2c_smbus_read_i2c_block_data(dev, reg, 3, data); + std::this_thread::sleep_for(32ms); + return ret; +} + +int PowerColorRedDevilV1Controller::RegisterWrite(unsigned char reg, unsigned char *data) +{ + int ret = bus->i2c_smbus_write_i2c_block_data(dev, reg, 3, data); + std::this_thread::sleep_for(32ms); + return ret; +} diff --git a/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/PowerColorRedDevilV1Controller.h b/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/PowerColorRedDevilV1Controller.h new file mode 100644 index 0000000..606a7ba --- /dev/null +++ b/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/PowerColorRedDevilV1Controller.h @@ -0,0 +1,110 @@ +/*---------------------------------------------------------*\ +| PowerColorRedDevilV1Controller.h | +| | +| Driver for PowerColor Red Devil GPU | +| | +| Jana Rettig (SapphicKitten) 14 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +#pragma once + +#define RED_DEVIL_V1_READ_OFFSET 0x80 +#define RED_DEVIL_V1_LED_MAX_COUNT 12 + +typedef unsigned char red_devil_v1_dev_id; + +struct red_devil_v1_mode +{ + unsigned char mode; + unsigned char brightness; + unsigned char speed; +}; + +enum +{ + RED_DEVIL_V1_REG_MODE = 0x01, + RED_DEVIL_V1_REG_LED_1 = 0x02, + RED_DEVIL_V1_REG_LED_2 = 0x03, + RED_DEVIL_V1_REG_LED_3 = 0x04, + RED_DEVIL_V1_REG_LED_4 = 0x05, + RED_DEVIL_V1_REG_LED_5 = 0x06, + RED_DEVIL_V1_REG_LED_6 = 0x07, + RED_DEVIL_V1_REG_LED_7 = 0x08, + RED_DEVIL_V1_REG_LED_8 = 0x09, + RED_DEVIL_V1_REG_LED_9 = 0x0A, + RED_DEVIL_V1_REG_LED_10 = 0x0B, + RED_DEVIL_V1_REG_LED_11 = 0x0C, + RED_DEVIL_V1_REG_LED_12 = 0x0D, // Unused for now, acts like any other led reg + RED_DEVIL_V1_REG_LED_ALL = 0x0E, + RED_DEVIL_V1_REG_MODE_COLOR = 0x0F, + RED_DEVIL_V1_REG_UNKNOWN_1 = 0x10, // Never seen writes to this, reads 0x01 0x05 0x00. Maybe Version? + RED_DEVIL_V1_REG_UNKNOWN_2 = 0x11, // DevilZone writes to this sometimes. No observable change + RED_DEVIL_V1_REG_MB_SYNC = 0x12, // Unused on NAVI 1X cards. Disables controller and allows LEDs to be controlled by external source via ARGB header + RED_DEVIL_V1_REG_MAGIC = 0x90 +}; + +enum +{ + RED_DEVIL_V1_MODE_OFF = 0x00, + RED_DEVIL_V1_MODE_STATIC = 0x01, + RED_DEVIL_V1_MODE_BREATHING = 0x02, + RED_DEVIL_V1_MODE_NEON = 0x03, + RED_DEVIL_V1_MODE_BLINK = 0x04, + RED_DEVIL_V1_MODE_DOUBLE_BLINK = 0x05, + RED_DEVIL_V1_MODE_COLOR_SHIFT = 0x06, + RED_DEVIL_V1_MODE_METEOR = 0x07, + RED_DEVIL_V1_MODE_RIPPLE = 0x08, + RED_DEVIL_V1_MODE_SEVEN_COLORS = 0x09, + RED_DEVIL_V1_MODE_MB_SYNC = 0xFF +}; + +enum +{ + RED_DEVIL_V1_BRIGHTNESS_MIN = 0x00, + RED_DEVIL_V1_BRIGHTNESS_MAX = 0xFF, +}; + +enum +{ + RED_DEVIL_V1_SPEED_SLOWEST = 0x64, + RED_DEVIL_V1_SPEED_DEFAULT = 0x32, + RED_DEVIL_V1_SPEED_FASTEST = 0x00 +}; + +class PowerColorRedDevilV1Controller +{ +public: + PowerColorRedDevilV1Controller(i2c_smbus_interface* bus, red_devil_v1_dev_id dev, std::string dev_name); + ~PowerColorRedDevilV1Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetLEDColor(int led, RGBColor color); + RGBColor GetLEDColor(int led); + + void SetLEDColorAll(RGBColor color); + + void SetModeColor(RGBColor color); + RGBColor GetModeColor(); + + void SetMode(red_devil_v1_mode config); + red_devil_v1_mode GetMode(); + + int RegisterRead(unsigned char reg, unsigned char *data); + int RegisterWrite(unsigned char reg, unsigned char *data); + + bool has_sync_mode = false; + +private: + i2c_smbus_interface* bus; + red_devil_v1_dev_id dev; + std::string name; +}; diff --git a/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/RGBController_PowerColorRedDevilV1.cpp b/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/RGBController_PowerColorRedDevilV1.cpp new file mode 100644 index 0000000..2000bca --- /dev/null +++ b/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/RGBController_PowerColorRedDevilV1.cpp @@ -0,0 +1,249 @@ +/*---------------------------------------------------------*\ +| RGBController_PowerColorRedDevilV1.cpp | +| | +| Driver for PowerColor Red Devil GPU | +| | +| Jana Rettig (SapphicKitten) 14 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_PowerColorRedDevilV1.h" + +RGBController_PowerColorRedDevilV1::RGBController_PowerColorRedDevilV1(PowerColorRedDevilV1Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "PowerColor"; + description = "PowerColor Red Devil GPU Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Off; + Off.name = "Off"; + Off.value = RED_DEVIL_V1_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = RED_DEVIL_V1_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = RED_DEVIL_V1_BRIGHTNESS_MIN; + Static.brightness_max = RED_DEVIL_V1_BRIGHTNESS_MAX; + Static.brightness = RED_DEVIL_V1_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = RED_DEVIL_V1_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = RED_DEVIL_V1_BRIGHTNESS_MIN; + Breathing.brightness_max = RED_DEVIL_V1_BRIGHTNESS_MAX; + Breathing.brightness = RED_DEVIL_V1_BRIGHTNESS_MAX; + Breathing.speed_min = RED_DEVIL_V1_SPEED_SLOWEST; + Breathing.speed_max = RED_DEVIL_V1_SPEED_FASTEST; + Breathing.speed = RED_DEVIL_V1_SPEED_DEFAULT; + modes.push_back(Breathing); + + mode Neon; + Neon.name = "Neon"; + Neon.value = RED_DEVIL_V1_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Neon.color_mode = MODE_COLORS_NONE; + Neon.brightness_min = RED_DEVIL_V1_BRIGHTNESS_MIN; + Neon.brightness_max = RED_DEVIL_V1_BRIGHTNESS_MAX; + Neon.brightness = RED_DEVIL_V1_BRIGHTNESS_MAX; + Neon.speed_min = RED_DEVIL_V1_SPEED_SLOWEST; + Neon.speed_max = RED_DEVIL_V1_SPEED_FASTEST; + Neon.speed = RED_DEVIL_V1_SPEED_DEFAULT; + modes.push_back(Neon); + + mode Blink; + Blink.name = "Blink"; + Blink.value = RED_DEVIL_V1_MODE_BLINK; + Blink.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Blink.color_mode = MODE_COLORS_PER_LED; + Blink.brightness_min = RED_DEVIL_V1_BRIGHTNESS_MIN; + Blink.brightness_max = RED_DEVIL_V1_BRIGHTNESS_MAX; + Blink.brightness = RED_DEVIL_V1_BRIGHTNESS_MAX; + Blink.speed_min = RED_DEVIL_V1_SPEED_SLOWEST; + Blink.speed_max = RED_DEVIL_V1_SPEED_FASTEST; + Blink.speed = RED_DEVIL_V1_SPEED_DEFAULT; + modes.push_back(Blink); + + mode DoubleBlink; + DoubleBlink.name = "Double Blink"; + DoubleBlink.value = RED_DEVIL_V1_MODE_DOUBLE_BLINK; + DoubleBlink.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + DoubleBlink.color_mode = MODE_COLORS_PER_LED; + DoubleBlink.brightness_min = RED_DEVIL_V1_BRIGHTNESS_MIN; + DoubleBlink.brightness_max = RED_DEVIL_V1_BRIGHTNESS_MAX; + DoubleBlink.brightness = RED_DEVIL_V1_BRIGHTNESS_MAX; + DoubleBlink.speed_min = RED_DEVIL_V1_SPEED_SLOWEST; + DoubleBlink.speed_max = RED_DEVIL_V1_SPEED_FASTEST; + DoubleBlink.speed = RED_DEVIL_V1_SPEED_DEFAULT; + modes.push_back(DoubleBlink); + + mode ColorShift; + ColorShift.name = "Color Shift"; + ColorShift.value = RED_DEVIL_V1_MODE_COLOR_SHIFT; + ColorShift.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + ColorShift.color_mode = MODE_COLORS_NONE; + ColorShift.brightness_min = RED_DEVIL_V1_BRIGHTNESS_MIN; + ColorShift.brightness_max = RED_DEVIL_V1_BRIGHTNESS_MAX; + ColorShift.brightness = RED_DEVIL_V1_BRIGHTNESS_MAX; + ColorShift.speed_min = RED_DEVIL_V1_SPEED_SLOWEST; + ColorShift.speed_max = RED_DEVIL_V1_SPEED_FASTEST; + ColorShift.speed = RED_DEVIL_V1_SPEED_DEFAULT; + modes.push_back(ColorShift); + + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = RED_DEVIL_V1_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors_min = 1; + Meteor.colors_max = 1; + Meteor.colors.resize(1); + Meteor.brightness_min = RED_DEVIL_V1_BRIGHTNESS_MIN; + Meteor.brightness_max = RED_DEVIL_V1_BRIGHTNESS_MAX; + Meteor.brightness = RED_DEVIL_V1_BRIGHTNESS_MAX; + Meteor.speed_min = RED_DEVIL_V1_SPEED_SLOWEST; + Meteor.speed_max = RED_DEVIL_V1_SPEED_FASTEST; + Meteor.speed = RED_DEVIL_V1_SPEED_DEFAULT; + modes.push_back(Meteor); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = RED_DEVIL_V1_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Ripple.color_mode = MODE_COLORS_MODE_SPECIFIC; + Ripple.colors_min = 1; + Ripple.colors_max = 1; + Ripple.colors.resize(1); + Ripple.brightness_min = RED_DEVIL_V1_BRIGHTNESS_MIN; + Ripple.brightness_max = RED_DEVIL_V1_BRIGHTNESS_MAX; + Ripple.brightness = RED_DEVIL_V1_BRIGHTNESS_MAX; + Ripple.speed_min = RED_DEVIL_V1_SPEED_SLOWEST; + Ripple.speed_max = RED_DEVIL_V1_SPEED_FASTEST; + Ripple.speed = RED_DEVIL_V1_SPEED_DEFAULT; + modes.push_back(Ripple); + + mode SevenColors; + SevenColors.name = "Seven Colors"; + SevenColors.value = RED_DEVIL_V1_MODE_SEVEN_COLORS; + SevenColors.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + SevenColors.color_mode = MODE_COLORS_NONE; + SevenColors.brightness_min = RED_DEVIL_V1_BRIGHTNESS_MIN; + SevenColors.brightness_max = RED_DEVIL_V1_BRIGHTNESS_MAX; + SevenColors.brightness = RED_DEVIL_V1_BRIGHTNESS_MAX; + SevenColors.speed_min = RED_DEVIL_V1_SPEED_SLOWEST; + SevenColors.speed_max = RED_DEVIL_V1_SPEED_FASTEST; + SevenColors.speed = RED_DEVIL_V1_SPEED_DEFAULT; + modes.push_back(SevenColors); + + if(controller->has_sync_mode) + { + mode Sync; + Sync.name = "Sync with Motherboard"; + Sync.value = RED_DEVIL_V1_MODE_MB_SYNC; + Sync.flags = MODE_FLAG_AUTOMATIC_SAVE; + Sync.color_mode = MODE_COLORS_NONE; + modes.push_back(Sync); + } + + SetupZones(); + + red_devil_v1_mode config = controller->GetMode(); + active_mode = config.mode; + + if(active_mode != RED_DEVIL_V1_MODE_OFF) + { + modes[active_mode].brightness = config.brightness; + modes[active_mode].speed = config.speed; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + modes[active_mode].colors[0] = controller->GetModeColor(); + } + else + { + colors[0] = controller->GetLEDColor(0); + } + } +} + +RGBController_PowerColorRedDevilV1::~RGBController_PowerColorRedDevilV1() +{ + delete controller; +} + +void RGBController_PowerColorRedDevilV1::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + + new_zone->name = "GPU"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + zones.push_back(*new_zone); + + /*---------------------------------------------------------*\ + | This device can control up to 12 LEDs | + | For now all LEDs show the same color | + \*---------------------------------------------------------*/ + led* new_led = new led(); + + new_led->name = "GPU"; + leds.push_back(*new_led); + + SetupColors(); +} + +void RGBController_PowerColorRedDevilV1::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PowerColorRedDevilV1::DeviceUpdateLEDs() +{ + controller->SetLEDColorAll(colors[0]); +} + +void RGBController_PowerColorRedDevilV1::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PowerColorRedDevilV1::UpdateSingleLED(int led) +{ + controller->SetLEDColor(led, colors[led]); +} + +void RGBController_PowerColorRedDevilV1::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + if(modes[active_mode].colors[0] != 0) + { + controller->SetModeColor(modes[active_mode].colors[0]); + } + } + + red_devil_v1_mode config{(unsigned char)modes[active_mode].value, (unsigned char)modes[active_mode].brightness, (unsigned char)modes[active_mode].speed}; + controller->SetMode(config); +} diff --git a/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/RGBController_PowerColorRedDevilV1.h b/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/RGBController_PowerColorRedDevilV1.h new file mode 100644 index 0000000..47046e5 --- /dev/null +++ b/Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/RGBController_PowerColorRedDevilV1.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_PowerColorRedDevilV1.cpp | +| | +| Driver for PowerColor Red Devil GPU | +| | +| Jana Rettig (SapphicKitten) 14 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "PowerColorRedDevilV1Controller.h" + +class RGBController_PowerColorRedDevilV1 : public RGBController +{ +public: + RGBController_PowerColorRedDevilV1(PowerColorRedDevilV1Controller* controller_ptr); + ~RGBController_PowerColorRedDevilV1(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PowerColorRedDevilV1Controller* controller; +}; diff --git a/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/PowerColorRedDevilV2Controller.cpp b/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/PowerColorRedDevilV2Controller.cpp new file mode 100644 index 0000000..3889d33 --- /dev/null +++ b/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/PowerColorRedDevilV2Controller.cpp @@ -0,0 +1,167 @@ +/*---------------------------------------------------------*\ +| PowerColorRedDevilV2Controller.cpp | +| | +| Driver for PowerColor Red Devil GPU | +| | +| Nexrem 15 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "PowerColorRedDevilV2Controller.h" + + +PowerColorRedDevilV2Controller::PowerColorRedDevilV2Controller(i2c_smbus_interface* bus, red_devil_v2_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +PowerColorRedDevilV2Controller::~PowerColorRedDevilV2Controller() +{ + +} + +std::string PowerColorRedDevilV2Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C:" + return_string); +} + +std::string PowerColorRedDevilV2Controller::GetDeviceName() +{ + return(name); +} + +bool PowerColorRedDevilV2Controller::GetSync() +{ + unsigned char data[3]; + RegisterRead(RED_DEVIL_V2_READ_REG_SYNC, data); + + return data[0] && data[1] && data[2]; +} + +void PowerColorRedDevilV2Controller::SetSync(bool sync) +{ + if(sync) + { + unsigned char data[3] = {0x01, 0x01, 0x01}; + RegisterWrite(RED_DEVIL_V2_WRITE_REG_SYNC, data); + } + else + { + unsigned char data[3] = {0x00, 0x00, 0x00}; + RegisterWrite(RED_DEVIL_V2_WRITE_REG_SYNC, data); + } +} + +/*------------------------------------------------------------------*\ +| Mode returns MMBBSS | +\*------------------------------------------------------------------*/ +red_devil_v2_mode PowerColorRedDevilV2Controller::GetMode() +{ + unsigned char data[3]; + red_devil_v2_mode mode; + + RegisterRead(RED_DEVIL_V2_READ_REG_MODE, data); + + mode.mode = data[0]; + mode.brightness = data[1]; + mode.speed = data[2]; + + return mode; +} + +void PowerColorRedDevilV2Controller::SetMode(red_devil_v2_mode mode) +{ + if(mode.mode == RED_DEVIL_V2_MODE_SYNC) + { + SetSync(true); + return; + } + + SetSync(false); + + unsigned char data[3] = + { + mode.mode, + mode.brightness, + mode.speed + }; + + RegisterWrite(RED_DEVIL_V2_WRITE_REG_MODE, data); +} + +RGBColor PowerColorRedDevilV2Controller::GetLedColor(int led) +{ + /*------------------------------------------------------------------*\ + | On overflow read the first LED | + \*------------------------------------------------------------------*/ + if(led >= RED_DEVIL_V2_NUM_LEDS) + { + led = 0; + } + + unsigned char data[3]; + RegisterRead(RED_DEVIL_V2_READ_REG_RGBX + led, data); + + return ToRGBColor(data[0], data[1], data[2]); +} + +void PowerColorRedDevilV2Controller::SetLedColor(int led, RGBColor color) +{ + /*------------------------------------------------------------------*\ + | Skip writing to invalid LEDs | + \*------------------------------------------------------------------*/ + if(led >= RED_DEVIL_V2_NUM_LEDS) + { + return; + } + + unsigned char data[3] = + { + (unsigned char) RGBGetRValue(color), + (unsigned char) RGBGetGValue(color), + (unsigned char) RGBGetBValue(color) + }; + + RegisterWrite(RED_DEVIL_V2_WRITE_REG_RGBX+led, data); +} + +void PowerColorRedDevilV2Controller::SetLedColorAll(RGBColor color) +{ + unsigned char data[3] = + { + (unsigned char) RGBGetRValue(color), + (unsigned char) RGBGetGValue(color), + (unsigned char) RGBGetBValue(color) + }; + + /*------------------------------------------------------------------*\ + | Factory firmware writes both, but we only need 1 of them. | + | Write both anyways just in case... | + \*------------------------------------------------------------------*/ + RegisterWrite(RED_DEVIL_V2_WRITE_REG_RGB1, data); + RegisterWrite(RED_DEVIL_V2_WRITE_REG_RGB2, data); +} + +int PowerColorRedDevilV2Controller::RegisterRead(unsigned char reg, unsigned char *data) +{ + int ret = bus->i2c_smbus_read_i2c_block_data(dev, reg, 3, data); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + return ret; +} + +int PowerColorRedDevilV2Controller::RegisterWrite(unsigned char reg, unsigned char *data) +{ + int ret = bus->i2c_smbus_write_i2c_block_data(dev, reg, 3, data); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + return ret; +} \ No newline at end of file diff --git a/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/PowerColorRedDevilV2Controller.h b/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/PowerColorRedDevilV2Controller.h new file mode 100644 index 0000000..a182af5 --- /dev/null +++ b/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/PowerColorRedDevilV2Controller.h @@ -0,0 +1,101 @@ +/*---------------------------------------------------------*\ +| PowerColorRedDevilV2Controller.h | +| | +| Driver for PowerColor Red Devil GPU | +| | +| Nexrem 15 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +typedef unsigned char red_devil_v2_dev_id; + +struct red_devil_v2_mode +{ + unsigned char mode; + unsigned char brightness; + unsigned char speed; +}; + +#define RED_DEVIL_V2_NUM_LEDS 24 + +enum +{ + RED_DEVIL_V2_WRITE_REG_MODE = 0x01, + RED_DEVIL_V2_WRITE_REG_SYNC = 0x04, + RED_DEVIL_V2_WRITE_REG_RGBX = 0x10, + RED_DEVIL_V2_WRITE_REG_RGB1 = 0x30, + RED_DEVIL_V2_WRITE_REG_RGB2 = 0x31 +}; + +enum +{ + RED_DEVIL_V2_READ_REG_MODE = 0x81, + RED_DEVIL_V2_READ_REG_MAGIC = 0x82, + RED_DEVIL_V2_READ_REG_SYNC = 0x84, + RED_DEVIL_V2_READ_REG_RGBX = 0x90 +}; + +enum +{ + RED_DEVIL_V2_MODE_OFF = 0x00, + RED_DEVIL_V2_MODE_STATIC = 0x01, + RED_DEVIL_V2_MODE_BREATHING = 0x02, + RED_DEVIL_V2_MODE_SECRET_RAINBOW = 0x03, + RED_DEVIL_V2_MODE_RADIANCE = 0x04, + RED_DEVIL_V2_MODE_DIFFUSE = 0x05, + RED_DEVIL_V2_MODE_COLOR_SHIFT = 0x06, + RED_DEVIL_V2_MODE_METEOR = 0x07, + RED_DEVIL_V2_MODE_RIPPLE = 0x08, + RED_DEVIL_V2_MODE_RAINBOW = 0x09, + RED_DEVIL_V2_MODE_SYNC = 0xFF +}; + +enum +{ + RED_DEVIL_V2_BRIGHTNESS_MIN = 0x00, + RED_DEVIL_V2_BRIGHTNESS_MAX = 0xFF +}; + +enum +{ + RED_DEVIL_V2_SPEED_MIN = 0xFF, + RED_DEVIL_V2_SPEED_DEFAULT = 0x32, + RED_DEVIL_V2_SPEED_MAX = 0x00 +}; + + +class PowerColorRedDevilV2Controller +{ +public: + PowerColorRedDevilV2Controller(i2c_smbus_interface* bus, red_devil_v2_dev_id dev, std::string dev_name); + ~PowerColorRedDevilV2Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + bool GetSync(); + void SetSync(bool sync); + + red_devil_v2_mode GetMode(); + void SetMode(red_devil_v2_mode mode); + + RGBColor GetLedColor(int led); + void SetLedColor(int led, RGBColor color); + void SetLedColorAll(RGBColor color); + +private: + i2c_smbus_interface* bus; + red_devil_v2_dev_id dev; + std::string name; + + int RegisterRead(unsigned char reg, unsigned char *data); + int RegisterWrite(unsigned char reg, unsigned char *data); +}; diff --git a/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/RGBController_PowerColorRedDevilV2.cpp b/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/RGBController_PowerColorRedDevilV2.cpp new file mode 100644 index 0000000..36719a4 --- /dev/null +++ b/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/RGBController_PowerColorRedDevilV2.cpp @@ -0,0 +1,349 @@ +/*---------------------------------------------------------*\ +| RGBController_PowerColorRedDevilV2.cpp | +| | +| Driver for PowerColor Red Devil GPU | +| | +| Nexrem 15 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController.h" +#include "RGBController_PowerColorRedDevilV2.h" + +RGBController_PowerColorRedDevilV2::RGBController_PowerColorRedDevilV2(PowerColorRedDevilV2Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "PowerColor"; + description = "PowerColor Red Devil V2 GPU Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Off; + Off.name = "Off"; + Off.value = RED_DEVIL_V2_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Custom"; + Static.value = RED_DEVIL_V2_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = RED_DEVIL_V2_BRIGHTNESS_MIN; + Static.brightness_max = RED_DEVIL_V2_BRIGHTNESS_MAX; + Static.brightness = RED_DEVIL_V2_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = RED_DEVIL_V2_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness_min = RED_DEVIL_V2_BRIGHTNESS_MIN; + Breathing.brightness_max = RED_DEVIL_V2_BRIGHTNESS_MAX; + Breathing.brightness = RED_DEVIL_V2_BRIGHTNESS_MAX; + Breathing.speed_min = RED_DEVIL_V2_SPEED_MIN; + Breathing.speed_max = RED_DEVIL_V2_SPEED_MAX; + Breathing.speed = RED_DEVIL_V2_SPEED_DEFAULT; + modes.push_back(Breathing); + + mode SecretRainbow; + SecretRainbow.name = "Secret Rainbow"; + SecretRainbow.value = RED_DEVIL_V2_MODE_SECRET_RAINBOW; + SecretRainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SecretRainbow.color_mode = MODE_COLORS_NONE; + SecretRainbow.brightness_min = RED_DEVIL_V2_BRIGHTNESS_MIN; + SecretRainbow.brightness_max = RED_DEVIL_V2_BRIGHTNESS_MAX; + SecretRainbow.brightness = RED_DEVIL_V2_BRIGHTNESS_MAX; + SecretRainbow.speed_min = RED_DEVIL_V2_SPEED_MIN; + SecretRainbow.speed_max = RED_DEVIL_V2_SPEED_MAX; + SecretRainbow.speed = RED_DEVIL_V2_SPEED_DEFAULT; + modes.push_back(SecretRainbow); + + mode Radiance; + Radiance.name = "Radiance"; + Radiance.value = RED_DEVIL_V2_MODE_RADIANCE; + Radiance.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Radiance.color_mode = MODE_COLORS_NONE; + Radiance.brightness_min = RED_DEVIL_V2_BRIGHTNESS_MIN; + Radiance.brightness_max = RED_DEVIL_V2_BRIGHTNESS_MAX; + Radiance.brightness = RED_DEVIL_V2_BRIGHTNESS_MAX; + Radiance.speed_min = RED_DEVIL_V2_SPEED_MIN; + Radiance.speed_max = RED_DEVIL_V2_SPEED_MAX; + Radiance.speed = RED_DEVIL_V2_SPEED_DEFAULT; + modes.push_back(Radiance); + + mode Diffuse; + Diffuse.name = "Diffuse"; + Diffuse.value = RED_DEVIL_V2_MODE_DIFFUSE; + Diffuse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Diffuse.color_mode = MODE_COLORS_NONE; + Diffuse.brightness_min = RED_DEVIL_V2_BRIGHTNESS_MIN; + Diffuse.brightness_max = RED_DEVIL_V2_BRIGHTNESS_MAX; + Diffuse.brightness = RED_DEVIL_V2_BRIGHTNESS_MAX; + Diffuse.speed_min = RED_DEVIL_V2_SPEED_MIN; + Diffuse.speed_max = RED_DEVIL_V2_SPEED_MAX; + Diffuse.speed = RED_DEVIL_V2_SPEED_DEFAULT; + modes.push_back(Diffuse); + + mode ColorShift; + ColorShift.name = "Color Shift"; + ColorShift.value = RED_DEVIL_V2_MODE_COLOR_SHIFT; + ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorShift.color_mode = MODE_COLORS_NONE; + ColorShift.brightness_min = RED_DEVIL_V2_BRIGHTNESS_MIN; + ColorShift.brightness_max = RED_DEVIL_V2_BRIGHTNESS_MAX; + ColorShift.brightness = RED_DEVIL_V2_BRIGHTNESS_MAX; + ColorShift.speed_min = RED_DEVIL_V2_SPEED_MIN; + ColorShift.speed_max = RED_DEVIL_V2_SPEED_MAX; + ColorShift.speed = RED_DEVIL_V2_SPEED_DEFAULT; + modes.push_back(ColorShift); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = RED_DEVIL_V2_MODE_METEOR; + Meteor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Meteor.color_mode = MODE_COLORS_PER_LED; + Meteor.brightness_min = RED_DEVIL_V2_BRIGHTNESS_MIN; + Meteor.brightness_max = RED_DEVIL_V2_BRIGHTNESS_MAX; + Meteor.brightness = RED_DEVIL_V2_BRIGHTNESS_MAX; + Meteor.speed_min = RED_DEVIL_V2_SPEED_MIN; + Meteor.speed_max = RED_DEVIL_V2_SPEED_MAX; + Meteor.speed = RED_DEVIL_V2_SPEED_DEFAULT; + modes.push_back(Meteor); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = RED_DEVIL_V2_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Ripple.color_mode = MODE_COLORS_PER_LED; + Ripple.brightness_min = RED_DEVIL_V2_BRIGHTNESS_MIN; + Ripple.brightness_max = RED_DEVIL_V2_BRIGHTNESS_MAX; + Ripple.brightness = RED_DEVIL_V2_BRIGHTNESS_MAX; + Ripple.speed_min = RED_DEVIL_V2_SPEED_MIN; + Ripple.speed_max = RED_DEVIL_V2_SPEED_MAX; + Ripple.speed = RED_DEVIL_V2_SPEED_DEFAULT; + modes.push_back(Ripple); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = RED_DEVIL_V2_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness_min = RED_DEVIL_V2_BRIGHTNESS_MIN; + Rainbow.brightness_max = RED_DEVIL_V2_BRIGHTNESS_MAX; + Rainbow.brightness = RED_DEVIL_V2_BRIGHTNESS_MAX; + Rainbow.speed_min = RED_DEVIL_V2_SPEED_MIN; + Rainbow.speed_max = RED_DEVIL_V2_SPEED_MAX; + Rainbow.speed = RED_DEVIL_V2_SPEED_DEFAULT; + modes.push_back(Rainbow); + + mode Sync; + Sync.name = "Sync with motherboard"; + Sync.value = RED_DEVIL_V2_MODE_SYNC; + Sync.flags = MODE_FLAG_AUTOMATIC_SAVE; + Sync.color_mode = MODE_COLORS_NONE; + modes.push_back(Sync); + + SetupZones(); + + ReadConfig(); + + /*------------------------------------------------------------------*\ + | Copy the read colors for later delta-ing | + \*------------------------------------------------------------------*/ + colors_copy = colors; +} + +RGBController_PowerColorRedDevilV2::~RGBController_PowerColorRedDevilV2() +{ + delete controller; +} + +void RGBController_PowerColorRedDevilV2::SetupZones() +{ + zone stripe1; + stripe1.name = "Stripe 1"; + stripe1.type = ZONE_TYPE_LINEAR; + stripe1.leds_min = 3; + stripe1.leds_max = 3; + stripe1.leds_count = 3; + stripe1.matrix_map = NULL; + zones.push_back(stripe1); + + zone stripe2; + stripe2.name = "Stripe 2"; + stripe2.type = ZONE_TYPE_LINEAR; + stripe2.leds_min = 3; + stripe2.leds_max = 3; + stripe2.leds_count = 3; + stripe2.matrix_map = NULL; + zones.push_back(stripe2); + + static unsigned int hellstone_map[2][7] = + { + { 0, 1, 2, 3, 4, 5, 6 }, + { 13, 12, 11, 10, 9, 8, 7 } + }; + + zone hellstone; + hellstone.name = "Hellstone"; + hellstone.type = ZONE_TYPE_MATRIX; + hellstone.leds_min = 14; + hellstone.leds_max = 14; + hellstone.leds_count = 14; + hellstone.matrix_map = new matrix_map_type; + hellstone.matrix_map->height = 2; + hellstone.matrix_map->width = 7; + hellstone.matrix_map->map = (unsigned int *)hellstone_map; + zones.push_back(hellstone); + + zone devil; + devil.name = "Devil"; + devil.type = ZONE_TYPE_LINEAR; + devil.leds_min = 4; + devil.leds_max = 4; + devil.leds_count = 4; + devil.matrix_map = NULL; + zones.push_back(devil); + + /*------------------------------------------------------------------*\ + | Create the LEDs for each zone | + \*------------------------------------------------------------------*/ + for(unsigned int i = 0; i < stripe1.leds_count; i++) + { + led new_led; + new_led.name = stripe1.name + " " + std::to_string(i+1); + leds.push_back(new_led); + } + + for(unsigned int i = 0; i < stripe2.leds_count; i++) + { + led new_led; + new_led.name = stripe2.name + " " + std::to_string(i+1); + leds.push_back(new_led); + } + + for(unsigned int i = 0; i < hellstone.leds_count; i++) + { + led new_led; + new_led.name = hellstone.name + " " + std::to_string(i+1); + leds.push_back(new_led); + } + + for(unsigned int i = 0; i < devil.leds_count; i++) + { + led new_led; + new_led.name = devil.name + " " + std::to_string(i+1); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_PowerColorRedDevilV2::ResizeZone(int, int) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PowerColorRedDevilV2::DeviceUpdateLEDs() +{ + /*---------------------------------------------------------*\ + | Check if all colors are identical. If they are do a | + | single register write instead of writing to each LED | + \*---------------------------------------------------------*/ + bool all_same = true; + for(std::size_t i = 1; i < colors.size(); i++) + { + if(colors[i-1] != colors[i]) + { + all_same = false; + break; + } + } + + /*---------------------------------------------------------*\ + | Do single register write to set all | + \*---------------------------------------------------------*/ + if(all_same) + { + RGBColor color = colors[0]; + controller->SetLedColorAll(color); + } + else + { + /*---------------------------------------------------------*\ + | Since writing to each LED is slow check which colors have | + | changed and only write those instead | + \*---------------------------------------------------------*/ + for(std::size_t i = 0; i < colors.size(); i++) + { + if(colors[i] != colors_copy[i]) + { + controller->SetLedColor((int)i, colors[i]); + } + } + } + + /*---------------------------------------------------------*\ + | Store changed colors | + \*---------------------------------------------------------*/ + colors_copy = colors; +} + +void RGBController_PowerColorRedDevilV2::UpdateZoneLEDs(int) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PowerColorRedDevilV2::UpdateSingleLED(int) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PowerColorRedDevilV2::DeviceUpdateMode() +{ + red_devil_v2_mode mode; + mode.mode = (unsigned char)modes[active_mode].value; + mode.brightness = (unsigned char)modes[active_mode].brightness; + mode.speed = (unsigned char)modes[active_mode].speed; + + controller->SetMode(mode); +} + +void RGBController_PowerColorRedDevilV2::ReadConfig() +{ + red_devil_v2_mode mode = controller->GetMode(); + bool sync = controller->GetSync(); + + for(std::size_t i = 0; i < colors.size(); i++) + { + colors[i] = controller->GetLedColor((int)i); + } + + /*---------------------------------------------------------*\ + | Since Sync is not actually "a mode" it needs special | + | handling | + \*---------------------------------------------------------*/ + if(sync) + { + active_mode = 10; + } + else if(mode.mode < modes.size() - 1) + { + /*---------------------------------------------------------*\ + | Mode ordering is important, keep them in order | + \*---------------------------------------------------------*/ + active_mode = mode.mode; + } + + modes[active_mode].brightness = mode.brightness; + modes[active_mode].speed = mode.speed; +} diff --git a/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/RGBController_PowerColorRedDevilV2.h b/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/RGBController_PowerColorRedDevilV2.h new file mode 100644 index 0000000..a05e075 --- /dev/null +++ b/Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/RGBController_PowerColorRedDevilV2.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_PowerColorRedDevilV2.h | +| | +| Driver for PowerColor Red Devil GPU | +| | +| Nexrem 15 Aug 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "PowerColorRedDevilV2Controller.h" + +class RGBController_PowerColorRedDevilV2 : public RGBController +{ +public: + RGBController_PowerColorRedDevilV2(PowerColorRedDevilV2Controller* controller_ptr); + ~RGBController_PowerColorRedDevilV2(); + + void SetupZones(); + + void ResizeZone(int, int); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int); + void UpdateSingleLED(int); + + void DeviceUpdateMode(); + +private: + PowerColorRedDevilV2Controller *controller; + /*------------------------------------------------------------------*\ + | To optimize color writes we store a copy of the colors in order to | + | later only write changed colors | + \*------------------------------------------------------------------*/ + std::vector colors_copy; + + void ReadConfig(); +}; diff --git a/Controllers/QMKController/QMKCommon.h b/Controllers/QMKController/QMKCommon.h new file mode 100644 index 0000000..cd80623 --- /dev/null +++ b/Controllers/QMKController/QMKCommon.h @@ -0,0 +1,22 @@ +/*---------------------------------------------------------*\ +| QMKCommon.h | +| | +| Common QMK definitions | +| | +| Adam Honse 22 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "hsv.h" +#include "QMKKeychronController.h" +#include "QMKViaCommands.h" +#include "StringUtils.h" +#include "LogManager.h" + +using namespace std::chrono_literals; + +QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char *path) +{ + /*-----------------------------------------------------*\ + | Initialize controller fields | + \*-----------------------------------------------------*/ + dev = dev_handle; + location = path; + kc_protocol_version = 0; + supported_features = 0; + via_protocol_version = 0; + + /*-----------------------------------------------------*\ + | Read product string | + \*-----------------------------------------------------*/ + wchar_t product_string[256]; + + int ret = hid_get_product_string(dev, product_string, 256); + + if(ret != 0) + { + name = ""; + } + else + { + name = StringUtils::wstring_to_string(product_string); + } + + /*-----------------------------------------------------*\ + | Read vendor string | + \*-----------------------------------------------------*/ + wchar_t vendor_string[256]; + + ret = hid_get_manufacturer_string(dev, vendor_string, 256); + + if(ret != 0) + { + vendor = ""; + } + else + { + vendor = StringUtils::wstring_to_string(vendor_string); + } + + /*-----------------------------------------------------*\ + | Read serial string | + \*-----------------------------------------------------*/ + wchar_t serial_string[256]; + + ret = hid_get_serial_number_string(dev, serial_string, 256); + + if(ret != 0) + { + serial = ""; + } + else + { + serial = StringUtils::wstring_to_string(serial_string); + } + + /*-----------------------------------------------------*\ + | Get VIA protocol version | + \*-----------------------------------------------------*/ + CmdGetViaProtocolVersion(&via_protocol_version); + + /*-----------------------------------------------------*\ + | Get Keychron protocol version | + \*-----------------------------------------------------*/ + CmdGetKeychronProtocolVersion(&kc_protocol_version); + + /*-----------------------------------------------------*\ + | Get Keychron firmware version | + \*-----------------------------------------------------*/ + kc_firmware_version = CmdGetKeychronFirmwareVersion(); + + /*-----------------------------------------------------*\ + | Get supported Keychron features | + \*-----------------------------------------------------*/ + CmdGetSupportFeature(&supported_features); + + if(!GetSupported()) + { + return; + } + + /*-----------------------------------------------------*\ + | Get Keychron RGB protocol version | + \*-----------------------------------------------------*/ + CmdGetKeychronRGBProtocolVersion(&kc_rgb_protocol_version); + + /*-----------------------------------------------------*\ + | Get count of LEDs | + \*-----------------------------------------------------*/ + CmdGetNumberLEDs(&number_leds); + + led_info.resize(number_leds); + keycodes.resize(number_leds); + + for(std::size_t led_idx = 0; led_idx < led_info.size(); led_idx++) + { + led_info[led_idx].valid = false; + } + + /*-----------------------------------------------------*\ + | Get info and keycode for all LEDs | + \*-----------------------------------------------------*/ + for(unsigned char row = 0; row < 32; row++) + { + std::vector row_leds = CmdGetLEDIndexByRow(row); + + for(unsigned char col = 0; col < row_leds.size(); col++) + { + if(row_leds[col] != 0xFF && row_leds[col] < led_info.size() && led_info[row_leds[col]].valid == false) + { + led_info[row_leds[col]].valid = true; + led_info[row_leds[col]].col = col; + led_info[row_leds[col]].row = row; + } + } + } + + for(unsigned short led_index = 0; led_index < number_leds; led_index++) + { + keycodes[led_index] = CmdGetKeycode(0, led_info[led_index].row, led_info[led_index].col); + } +} + +QMKKeychronController::~QMKKeychronController() +{ + hid_close(dev); +} + +std::string QMKKeychronController::GetLocation() +{ + return("HID: " + location); +} + +std::string QMKKeychronController::GetName() +{ + return(name); +} + +std::string QMKKeychronController::GetSerial() +{ + return(serial); +} + +std::string QMKKeychronController::GetVendor() +{ + return(vendor); +} + +std::string QMKKeychronController::GetVersion() +{ + /*-----------------------------------------------------*\ + | Format multi-line version text | + \*-----------------------------------------------------*/ + return("VIA: " + std::to_string(via_protocol_version) + "\r\n" + + "Keychron: " + std::to_string(kc_protocol_version) + "\r\n" + + "Keychron RGB: " + std::to_string(kc_rgb_protocol_version) + "\r\n" + + "Keychron FW: " + kc_firmware_version); +} + +bool QMKKeychronController::GetSupported() +{ + return(supported_features & KC_FEATURE_KEYCHRON_RGB); +} + +unsigned short QMKKeychronController::GetKeycode(unsigned short led_index) +{ + return(keycodes[led_index]); +} + +unsigned short QMKKeychronController::GetLEDCount() +{ + return(number_leds); +} + +qmk_rgb_matrix_led_info QMKKeychronController::GetLEDInfo(unsigned short led_index) +{ + return(led_info[led_index]); +} + +void QMKKeychronController::SaveMode() +{ + CmdSaveMode(); +} + +void QMKKeychronController::SendLEDs(unsigned short number_leds, RGBColor* color_data) +{ + unsigned short led_start_index = 0; + unsigned char number_packet_leds = 9; + + while(led_start_index < number_leds) + { + if((number_leds - led_start_index) < 9) + { + number_packet_leds = (number_leds - led_start_index); + } + + CmdSendLEDs(led_start_index, number_packet_leds, &color_data[led_start_index]); + + led_start_index += number_packet_leds; + } +} + +void QMKKeychronController::SetMode(unsigned short mode, unsigned char speed, unsigned char hue, unsigned char sat, unsigned char val) +{ + if(mode == 0xFFFF) + { + CmdSetRGBMatrixMode(KEYCHRON_QHE_PER_KEY_RGB_EFFECT); + CmdSetPerKeyRGBType(KEYCHRON_PER_KEY_RGB_SOLID); + } + else + { + CmdSetRGBMatrixMode((unsigned char)mode); + CmdSetColorHS(hue, sat); + CmdSetBrightness(val); + CmdSetSpeed(speed); + } +} + +unsigned short QMKKeychronController::CmdGetKeycode + ( + unsigned char layer, + unsigned char row, + unsigned char col + ) +{ + unsigned char args[3]; + unsigned char response[5]; + unsigned short keycode; + + args[0] = layer; + args[1] = row; + args[2] = col; + + ViaSendCommand(QMK_VIA_CMD_VIA_DYNAMIC_KEYMAP_GET_KEYCODE, args, sizeof(args), response, sizeof(response)); + + keycode = ( response[3] << 8 )| response[4]; + + return(keycode); +} + + +std::string QMKKeychronController::CmdGetKeychronFirmwareVersion() +{ + char response[30]; + + ViaSendCommand(KC_GET_FIRMWARE_VERSION, NULL, 0, (unsigned char*)response, sizeof(response)); + + /*-----------------------------------------------------*\ + | Ensure response null termination | + \*-----------------------------------------------------*/ + response[29] = 0; + + return(std::string(response)); +} + +void QMKKeychronController::CmdGetKeychronProtocolVersion + ( + unsigned char* kc_protocol_version + ) +{ + ViaSendCommand(KC_GET_PROTOCOL_VERSION, NULL, 0, (unsigned char*)kc_protocol_version, sizeof(unsigned char)); +} + +void QMKKeychronController::CmdGetKeychronRGBProtocolVersion(unsigned short* kc_rgb_protocol_version) +{ + ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_PROTOCOL_VER, NULL, 0, (unsigned char*)kc_rgb_protocol_version, sizeof(unsigned short)); + + /*-----------------------------------------------------*\ + | The RGB protocol version byte order is reversed | + \*-----------------------------------------------------*/ + *kc_rgb_protocol_version = ((*kc_rgb_protocol_version & 0x00FF) << 8) | ((*kc_rgb_protocol_version & 0xFF00) >> 8); +} + +std::vector QMKKeychronController::CmdGetLEDIndexByRow(unsigned char row) +{ + unsigned char args[4]; + unsigned char response[KEYCHRON_QHE_PACKET_SIZE - 2]; + + args[0] = row; + args[1] = 0xFF; + args[2] = 0xFF; + args[3] = 0xFF; + + int bytes_read = ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_LED_IDX, args, sizeof(args), response, sizeof(response)); + + std::vector result; + + if(bytes_read > 0) + { + for(int i = 1; i < bytes_read; i++) + { + result.push_back(response[i] == 0xFF ? -1 : response[i]); + } + } + + return result; +} + +void QMKKeychronController::CmdGetNumberLEDs + ( + unsigned short* number_leds + ) +{ + ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_LED_COUNT, NULL, 0, (unsigned char*)number_leds, sizeof(unsigned short)); + + /*-----------------------------------------------------*\ + | The LED count byte order is reversed | + \*-----------------------------------------------------*/ + *number_leds = ((*number_leds & 0x00FF) << 8) | ((*number_leds & 0xFF00) >> 8); +} + +void QMKKeychronController::CmdGetSupportFeature(unsigned short* supported_features) +{ + ViaSendCommand(KC_GET_SUPPORT_FEATURE, NULL, 0, (unsigned char*)supported_features, sizeof(unsigned short)); + + /*-----------------------------------------------------*\ + | The supported features byte order is reversed | + \*-----------------------------------------------------*/ + *supported_features = ((*supported_features & 0x00FF) << 8) | ((*supported_features & 0xFF00) >> 8); +} + +void QMKKeychronController::CmdGetViaProtocolVersion + ( + unsigned short* via_protocol_version + ) +{ + ViaSendCommand(QMK_VIA_CMD_GET_PROTOCOL_VERSION, NULL, 0, (unsigned char*)via_protocol_version, sizeof(unsigned short)); + + /*-----------------------------------------------------*\ + | The protocol version byte order is reversed | + \*-----------------------------------------------------*/ + *via_protocol_version = ((*via_protocol_version & 0x00FF) << 8) | ((*via_protocol_version & 0xFF00) >> 8); +} + +void QMKKeychronController::CmdSaveMode() +{ + ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_SAVE, NULL, 0, NULL, 0); +} + +void QMKKeychronController::CmdSendLEDs(unsigned char start_index, unsigned char number_leds, RGBColor* color_data) +{ + unsigned char args[KEYCHRON_QHE_PACKET_SIZE - 2]; + + args[0] = start_index; + args[1] = number_leds; + + if(number_leds > 9) + { + number_leds = 9; + } + + for(unsigned char led_index = 0; led_index < number_leds; led_index++) + { + /*-------------------------------------------------*\ + | VialRGB sends direct packets in HSV for some | + | inexplicable reason, so do the RGB to HSV | + | conversion before sending | + \*-------------------------------------------------*/ + hsv_t hsv_color; + rgb2hsv(color_data[led_index], &hsv_color); + + args[2 + (led_index * 3)] = (unsigned char)((float)hsv_color.hue * (256.0f / 360.0f)); + args[3 + (led_index * 3)] = hsv_color.saturation; + args[4 + (led_index * 3)] = hsv_color.value; + } + + ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_PER_KEY_SET_COLOR, args, sizeof(args), NULL, 0); +} + +void QMKKeychronController::CmdSetBrightness(unsigned char brightness) +{ + unsigned char args[3]; + + args[0] = QMK_VIA_RGB_MATRIX_CHANNEL; + args[1] = QMK_VIA_RGB_MATRIX_BRIGHTNESS; + args[2] = brightness; + + ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0); +} + +void QMKKeychronController::CmdSetColorHS(unsigned char h, unsigned char s) +{ + unsigned char args[4]; + + args[0] = QMK_VIA_RGB_MATRIX_CHANNEL; + args[1] = QMK_VIA_RGB_MATRIX_COLOR; + args[2] = h; + args[3] = s; + + ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0); +} + +void QMKKeychronController::CmdSetPerKeyRGBType(unsigned char type) +{ + unsigned char args[1]; + + args[0] = type; + + ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_PER_KEY_SET_TYPE, args, sizeof(args), NULL, 0); +} + +void QMKKeychronController::CmdSetRGBMatrixMode(unsigned char mode) +{ + unsigned char args[3]; + + args[0] = QMK_VIA_RGB_MATRIX_CHANNEL; + args[1] = QMK_VIA_RGB_MATRIX_EFFECT; + args[2] = mode; + + ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0); +} + +void QMKKeychronController::CmdSetSpeed(unsigned char speed) +{ + unsigned char args[3]; + + args[0] = QMK_VIA_RGB_MATRIX_CHANNEL; + args[1] = QMK_VIA_RGB_MATRIX_EFFECT_SPEED; + args[2] = speed; + + ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0); +} + +int QMKKeychronController::ViaSendCommand + ( + unsigned char cmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ) +{ + /*-----------------------------------------------------*\ + | Standard VIA command with no sub-command | + | | + | Byte 0: Command | + | Byte 1+: Data | + \*-----------------------------------------------------*/ + unsigned char usb_buf[KEYCHRON_QHE_PACKET_SIZE + 1]; + + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Write command, offsetting by 1 for HID report ID | + \*-----------------------------------------------------*/ + usb_buf[1] = cmd; + memcpy(&usb_buf[2], data_in, data_in_size); + + hid_write(dev, usb_buf, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Read response | + \*-----------------------------------------------------*/ + int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf) - 1, 1000); + + if(usb_buf[0] != cmd) + { + return(-1); + } + + memcpy(data_out, &usb_buf[1], data_out_size); + + return(bytes_received - 1); +} + +int QMKKeychronController::ViaSendCommandSub + ( + unsigned char cmd, + unsigned char subcmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ) +{ + /*-----------------------------------------------------*\ + | Standard VIA command with sub-command | + | | + | Byte 0: Command | + | Byte 1: Sub-Command | + | Byte 2+: Data | + \*-----------------------------------------------------*/ + unsigned char usb_buf[KEYCHRON_QHE_PACKET_SIZE + 1]; + + memset(usb_buf, 0, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Write command, offsetting by 1 for HID report ID | + \*-----------------------------------------------------*/ + usb_buf[1] = cmd; + usb_buf[2] = subcmd; + memcpy(&usb_buf[3], data_in, data_in_size); + + hid_write(dev, usb_buf, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Read response | + \*-----------------------------------------------------*/ + int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf) - 1, 1000); + + if(usb_buf[0] != cmd || usb_buf[1] != subcmd) + { + return(-1); + } + + memcpy(data_out, &usb_buf[2], data_out_size); + + return(bytes_received - 2); +} diff --git a/Controllers/QMKController/QMKKeychronController/QMKKeychronController.h b/Controllers/QMKController/QMKKeychronController/QMKKeychronController.h new file mode 100644 index 0000000..586e3fa --- /dev/null +++ b/Controllers/QMKController/QMKKeychronController/QMKKeychronController.h @@ -0,0 +1,448 @@ +/*---------------------------------------------------------*\ +| QMKKeychronController.h | +| | +| Driver for Keychron QMK-based keyboards | +| | +| Amadej Kastelic 21 Jun 2026 | +| Adam Honse 22 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "QMKCommon.h" +#include "RGBController.h" + +/*---------------------------------------------------------*\ +| Keychron vendor ID | +\*---------------------------------------------------------*/ +#define KEYCHRON_VID 0x3434 + +/*---------------------------------------------------------*\ +| Product IDs | +\*---------------------------------------------------------*/ +#define KEYCHRON_C1_PRO_ANSI_RGB_PID 0x0510 +#define KEYCHRON_C1_PRO_8K_ANSI_PID 0x0521 +#define KEYCHRON_C1_PRO_8K_ISO_PID 0x051D +#define KEYCHRON_C1_PRO_8K_JIS_PID 0x051E +#define KEYCHRON_C1_PRO_V2_ANSI_RGB_PID 0x0516 +#define KEYCHRON_C2_PRO_ANSI_RGB_PID 0x0520 +#define KEYCHRON_C2_PRO_8K_ANSI_PID 0x0522 +#define KEYCHRON_C2_PRO_8K_ISO_PID 0x052D +#define KEYCHRON_C2_PRO_V2_ANSI_RGB_PID 0x0526 +#define KEYCHRON_C3_PRO_ANSI_RGB_PID 0x0433 +#define KEYCHRON_C3_PRO_8K_ANSI_PID 0x0530 +#define KEYCHRON_C3_PRO_8K_ISO_PID 0x0531 +#define KEYCHRON_C3_PRO_8K_JIS_PID 0x0532 +#define KEYCHRON_K0_MAX_PID 0x0A06 +#define KEYCHRON_K1_MAX_ANSI_RGB_PID 0x0A10 +#define KEYCHRON_K1_MAX_ISO_RGB_PID 0x0A11 +#define KEYCHRON_K1_MAX_JIS_RGB_PID 0x0A12 +#define KEYCHRON_K1_V6_ANSI_RGB_PID 0x0D10 +#define KEYCHRON_K1_V6_ISO_RGB_PID 0x0D11 +#define KEYCHRON_K1_V6_JIS_RGB_PID 0x0D12 +#define KEYCHRON_K2_HE_ANSI_PID 0x0E20 +#define KEYCHRON_K2_HE_ISO_PID 0x0E21 +#define KEYCHRON_K2_HE_JIS_PID 0x0E22 +#define KEYCHRON_K2_MAX_ANSI_RGB_PID 0x0A20 +#define KEYCHRON_K2_MAX_ISO_RGB_PID 0x0A21 +#define KEYCHRON_K2_MAX_JIS_RGB_PID 0x0A22 +#define KEYCHRON_K2_V3_ANSI_RGB_PID 0x0D20 +#define KEYCHRON_K2_V3_ISO_RGB_PID 0x0D21 +#define KEYCHRON_K2_V3_JIS_RGB_PID 0x0D22 +#define KEYCHRON_K3_MAX_ANSI_RGB_PID 0x0A30 +#define KEYCHRON_K3_MAX_ISO_RGB_PID 0x0A31 +#define KEYCHRON_K3_MAX_JIS_RGB_PID 0x0A32 +#define KEYCHRON_K3_V3_ANSI_RGB_PID 0x0D30 +#define KEYCHRON_K3_V3_ISO_RGB_PID 0x0D31 +#define KEYCHRON_K3_V3_JIS_RGB_PID 0x0D32 +#define KEYCHRON_K4_HE_ANSI_PID 0x0E40 +#define KEYCHRON_K4_HE_ISO_PID 0x0E41 +#define KEYCHRON_K4_HE_JIS_PID 0x0E42 +#define KEYCHRON_K4_MAX_ANSI_RGB_PID 0x0A40 +#define KEYCHRON_K4_MAX_ISO_RGB_PID 0x0A41 +#define KEYCHRON_K4_MAX_JIS_RGB_PID 0x0A42 +#define KEYCHRON_K4_V3_ANSI_RGB_PID 0x0D40 +#define KEYCHRON_K4_V3_ISO_RGB_PID 0x0D41 +#define KEYCHRON_K4_V3_JIS_RGB_PID 0x0D42 +#define KEYCHRON_K5_MAX_ANSI_RGB_PID 0x0A50 +#define KEYCHRON_K5_MAX_ISO_RGB_PID 0x0A51 +#define KEYCHRON_K5_MAX_JIS_RGB_PID 0x0A52 +#define KEYCHRON_K5_MAX_JIS_V2_RGB_PID 0x0A58 +#define KEYCHRON_K5_V2_ANSI_RGB_PID 0x0D50 +#define KEYCHRON_K5_V2_ISO_RGB_PID 0x0D51 +#define KEYCHRON_K5_V2_JIS_RGB_PID 0x0D52 +#define KEYCHRON_K6_HE_ANSI_PID 0x0E60 +#define KEYCHRON_K7_MAX_ANSI_RGB_PID 0x0A70 +#define KEYCHRON_K7_MAX_ISO_RGB_PID 0x0A71 +#define KEYCHRON_K7_MAX_JIS_RGB_PID 0x0A72 +#define KEYCHRON_K7_MAX_JIS_V2_RGB_PID 0x0A76 +#define KEYCHRON_K8_HE_ANSI_PID 0x0E80 +#define KEYCHRON_K8_HE_ISO_PID 0x0E81 +#define KEYCHRON_K8_HE_JIS_PID 0x0E82 +#define KEYCHRON_K8_MAX_ANSI_RGB_PID 0x0A80 +#define KEYCHRON_K8_MAX_ISO_RGB_PID 0x0A81 +#define KEYCHRON_K8_MAX_JIS_RGB_PID 0x0A82 +#define KEYCHRON_K8_PRO_ANSI_RGB_PID 0x0280 +#define KEYCHRON_K8_PRO_ISO_RGB_PID 0x0281 +#define KEYCHRON_K8_PRO_JIS_RGB_PID 0x0282 +#define KEYCHRON_K8_V2_ANSI_RGB_PID 0x0D80 +#define KEYCHRON_K8_V2_ISO_RGB_PID 0x0D81 +#define KEYCHRON_K8_V2_JIS_RGB_PID 0x0D82 +#define KEYCHRON_K9_MAX_ANSI_RGB_PID 0x0A90 +#define KEYCHRON_K10_HE_ANSI_PID 0x0EA0 +#define KEYCHRON_K10_HE_ISO_PID 0x0EA1 +#define KEYCHRON_K10_MAX_ANSI_RGB_PID 0x0AA0 +#define KEYCHRON_K10_MAX_ISO_RGB_PID 0x0AA1 +#define KEYCHRON_K10_MAX_JIS_RGB_PID 0x0AA2 +#define KEYCHRON_K10_V2_ANSI_RGB_PID 0x0DA0 +#define KEYCHRON_K10_V2_ISO_RGB_PID 0x0DA1 +#define KEYCHRON_K10_V2_JIS_RGB_PID 0x0DA2 +#define KEYCHRON_K11_MAX_ANSI_ENCODER_RGB_PID 0x0AB3 +#define KEYCHRON_K11_MAX_ISO_ENCODER_RGB_PID 0x0AB4 +#define KEYCHRON_K11_MAX_JIS_ENCODER_RGB_PID 0x0AB5 +#define KEYCHRON_K13_MAX_ANSI_RGB_PID 0x0AD0 +#define KEYCHRON_K13_MAX_ISO_RGB_PID 0x0AD1 +#define KEYCHRON_K13_MAX_JIS_RGB_PID 0x0AD2 +#define KEYCHRON_K15_MAX_ANSI_ENCODER_RGB_PID 0x0AF0 +#define KEYCHRON_K15_MAX_ISO_ENCODER_RGB_PID 0x0AF1 +#define KEYCHRON_K17_MAX_ANSI_ENCODER_RGB_PID 0x0A00 +#define KEYCHRON_K17_MAX_ISO_ENCODER_RGB_PID 0x0A01 +#define KEYCHRON_K17_MAX_JIS_ENCODER_RGB_PID 0x0A02 +#define KEYCHRON_Q0_BASE_PID 0x0130 +#define KEYCHRON_Q0_PLUS_PID 0x0131 +#define KEYCHRON_Q0_MAX_ENCODER_PID 0x0800 +#define KEYCHRON_Q1_HE_ANSI_ENCODER_PID 0x0B10 +#define KEYCHRON_Q1_HE_ISO_ENCODER_PID 0x0B11 +#define KEYCHRON_Q1_HE_JIS_ENCODER_PID 0x0B12 +#define KEYCHRON_Q1_MAX_ANSI_ENCODER_PID 0x0810 +#define KEYCHRON_Q1_MAX_ISO_ENCODER_PID 0x0811 +#define KEYCHRON_Q1_MAX_JIS_ENCODER_PID 0x0812 +#define KEYCHRON_Q1_V1_ANSI_PID 0x0100 +#define KEYCHRON_Q1_V1_ANSI_ENCODER_PID 0x0101 +#define KEYCHRON_Q1_V1_ISO_PID 0x0102 +#define KEYCHRON_Q1_V1_ISO_ENCODER_PID 0x0103 +#define KEYCHRON_Q1_V2_ANSI_PID 0x0106 +#define KEYCHRON_Q1_V2_ANSI_ENCODER_PID 0x0107 +#define KEYCHRON_Q1_V2_ISO_PID 0x0108 +#define KEYCHRON_Q1_V2_ISO_ENCODER_PID 0x0109 +#define KEYCHRON_Q1_V2_JIS_PID 0x010A +#define KEYCHRON_Q1_V2_JIS_ENCODER_PID 0x010B +#define KEYCHRON_Q2_ANSI_PID 0x0110 +#define KEYCHRON_Q2_ANSI_ENCODER_PID 0x0111 +#define KEYCHRON_Q2_ISO_PID 0x0112 +#define KEYCHRON_Q2_ISO_ENCODER_PID 0x0113 +#define KEYCHRON_Q2_JIS_PID 0x0114 +#define KEYCHRON_Q2_JIS_ENCODER_PID 0x0115 +#define KEYCHRON_Q2_HE_ANSI_ENCODER_PID 0x0B20 +#define KEYCHRON_Q2_MAX_ANSI_ENCODER_PID 0x0820 +#define KEYCHRON_Q2_MAX_ISO_ENCODER_PID 0x0821 +#define KEYCHRON_Q3_ANSI_PID 0x0120 +#define KEYCHRON_Q3_ANSI_ENCODER_PID 0x0121 +#define KEYCHRON_Q3_ISO_PID 0x0122 +#define KEYCHRON_Q3_ISO_ENCODER_PID 0x0123 +#define KEYCHRON_Q3_JIS_PID 0x0124 +#define KEYCHRON_Q3_JIS_ENCODER_PID 0x0125 +#define KEYCHRON_Q3_HE_ANSI_ENCODER_PID 0x0B30 +#define KEYCHRON_Q3_HE_ISO_ENCODER_PID 0x0B31 +#define KEYCHRON_Q3_HE_JIS_ENCODER_PID 0x0B32 +#define KEYCHRON_Q3_MAX_ANSI_ENCODER_PID 0x0830 +#define KEYCHRON_Q3_MAX_ISO_ENCODER_PID 0x0831 +#define KEYCHRON_Q4_ANSI_PID 0x0140 +#define KEYCHRON_Q4_ISO_PID 0x0142 +#define KEYCHRON_Q4_HE_ANSI_PID 0x0B40 +#define KEYCHRON_Q5_ANSI_PID 0x0150 +#define KEYCHRON_Q5_ANSI_ENCODER_PID 0x0151 +#define KEYCHRON_Q5_ISO_PID 0x0152 +#define KEYCHRON_Q5_ISO_ENCODER_PID 0x0153 +#define KEYCHRON_Q5_HE_ANSI_ENCODER_PID 0x0B50 +#define KEYCHRON_Q5_HE_ISO_ENCODER_PID 0x0B51 +#define KEYCHRON_Q5_HE_JIS_ENCODER_PID 0x0B52 +#define KEYCHRON_Q5_MAX_ANSI_ENCODER_PID 0x0850 +#define KEYCHRON_Q5_MAX_ISO_ENCODER_PID 0x0851 +#define KEYCHRON_Q5_MAX_JIS_ENCODER_PID 0x0852 +#define KEYCHRON_Q6_ANSI_PID 0x0160 +#define KEYCHRON_Q6_ANSI_ENCODER_PID 0x0161 +#define KEYCHRON_Q6_ISO_PID 0x0162 +#define KEYCHRON_Q6_ISO_ENCODER_PID 0x0163 +#define KEYCHRON_Q6_HE_ANSI_ENCODER_PID 0x0B60 +#define KEYCHRON_Q6_HE_ISO_ENCODER_PID 0x0B61 +#define KEYCHRON_Q6_HE_JIS_ENCODER_PID 0x0B62 +#define KEYCHRON_Q6_MAX_ANSI_ENCODER_PID 0x0860 +#define KEYCHRON_Q6_MAX_ISO_ENCODER_PID 0x0861 +#define KEYCHRON_Q7_ANSI_PID 0x0170 +#define KEYCHRON_Q7_ISO_PID 0x0172 +#define KEYCHRON_Q8_ANSI_PID 0x0180 +#define KEYCHRON_Q8_ANSI_ENCODER_PID 0x0181 +#define KEYCHRON_Q8_ISO_PID 0x0182 +#define KEYCHRON_Q8_ISO_ENCODER_PID 0x0183 +#define KEYCHRON_Q8_MAX_ANSI_ENCODER_PID 0x0880 +#define KEYCHRON_Q9_ANSI_PID 0x0190 +#define KEYCHRON_Q9_ANSI_ENCODER_PID 0x0191 +#define KEYCHRON_Q9_ISO_PID 0x0192 +#define KEYCHRON_Q9_ISO_ENCODER_PID 0x0193 +#define KEYCHRON_Q9_PLUS_ANSI_ENCODER_PID 0x0194 +#define KEYCHRON_Q10_ANSI_ENCODER_PID 0x01A1 +#define KEYCHRON_Q10_ISO_ENCODER_PID 0x01A3 +#define KEYCHRON_Q10_MAX_ANSI_ENCODER_PID 0x08A0 +#define KEYCHRON_Q10_MAX_ISO_ENCODER_PID 0x08A1 +#define KEYCHRON_Q11_ANSI_ENCODER_PID 0x01E0 +#define KEYCHRON_Q11_ISO_ENCODER_PID 0x01E1 +#define KEYCHRON_Q12_ANSI_ENCODER_PID 0x01D1 +#define KEYCHRON_Q12_ISO_ENCODER_PID 0x01D3 +#define KEYCHRON_Q12_HE_ANSI_ENCODER_PID 0x0BC0 +#define KEYCHRON_Q12_HE_ISO_ENCODER_PID 0x0BC1 +#define KEYCHRON_Q12_MAX_ANSI_ENCODER_PID 0x08C3 +#define KEYCHRON_Q12_MAX_ISO_ENCODER_PID 0x08C4 +#define KEYCHRON_Q13_MAX_ANSI_ENCODER_PID 0x08D0 +#define KEYCHRON_Q13_MAX_JIS_ENCODER_PID 0x08D2 +#define KEYCHRON_Q14_MAX_ANSI_ENCODER_PID 0x08E0 +#define KEYCHRON_Q15_MAX_ANSI_ENCODER_PID 0x08F0 +#define KEYCHRON_Q60_MAX_ANSI_PID 0x08C0 +#define KEYCHRON_Q65_MAX_ANSI_ENCODER_PID 0x08B0 +#define KEYCHRON_S1_ANSI_RGB_PID 0x0410 +#define KEYCHRON_V1_ABNT2_ENCODER_PID 0x0317 +#define KEYCHRON_V1_ANSI_PID 0x0310 +#define KEYCHRON_V1_ANSI_ENCODER_PID 0x0311 +#define KEYCHRON_V1_ISO_PID 0x0312 +#define KEYCHRON_V1_ISO_ENCODER_PID 0x0313 +#define KEYCHRON_V1_JIS_PID 0x0314 +#define KEYCHRON_V1_JIS_ENCODER_PID 0x0315 +#define KEYCHRON_V1_8K_ANSI_ENCODER_PID 0x0F10 +#define KEYCHRON_V1_MAX_ANSI_ENCODER_PID 0x0913 +#define KEYCHRON_V1_MAX_ISO_ENCODER_PID 0x0914 +#define KEYCHRON_V1_MAX_JIS_ENCODER_PID 0x0915 +#define KEYCHRON_V2_ABNT2_ENCODER_PID 0x0327 +#define KEYCHRON_V2_ANSI_PID 0x0320 +#define KEYCHRON_V2_ANSI_ENCODER_PID 0x0321 +#define KEYCHRON_V2_ISO_PID 0x0322 +#define KEYCHRON_V2_ISO_ENCODER_PID 0x0323 +#define KEYCHRON_V2_JIS_PID 0x0324 +#define KEYCHRON_V2_JIS_ENCODER_PID 0x0325 +#define KEYCHRON_V2_MAX_ANSI_ENCODER_PID 0x0920 +#define KEYCHRON_V2_MAX_ISO_ENCODER_PID 0x0921 +#define KEYCHRON_V2_MAX_JIS_ENCODER_PID 0x0922 +#define KEYCHRON_V3_ABNT2_ENCODER_PID 0x0337 +#define KEYCHRON_V3_ANSI_PID 0x0330 +#define KEYCHRON_V3_ANSI_ENCODER_PID 0x0331 +#define KEYCHRON_V3_ISO_PID 0x0332 +#define KEYCHRON_V3_ISO_ENCODER_PID 0x0333 +#define KEYCHRON_V3_JIS_PID 0x0334 +#define KEYCHRON_V3_JIS_ENCODER_PID 0x0335 +#define KEYCHRON_V3_MAX_ANSI_ENCODER_PID 0x0933 +#define KEYCHRON_V3_MAX_ISO_ENCODER_PID 0x0934 +#define KEYCHRON_V3_MAX_JIS_ENCODER_PID 0x0935 +#define KEYCHRON_V4_ANSI_PID 0x0340 +#define KEYCHRON_V4_ISO_PID 0x0342 +#define KEYCHRON_V4_MAX_ANSI_PID 0x0940 +#define KEYCHRON_V4_MAX_ISO_PID 0x0941 +#define KEYCHRON_V5_ANSI_PID 0x0350 +#define KEYCHRON_V5_ANSI_ENCODER_PID 0x0351 +#define KEYCHRON_V5_ISO_PID 0x0352 +#define KEYCHRON_V5_ISO_ENCODER_PID 0x0353 +#define KEYCHRON_V5_MAX_ANSI_ENCODER_PID 0x0950 +#define KEYCHRON_V5_MAX_ISO_ENCODER_PID 0x0951 +#define KEYCHRON_V5_MAX_JIS_ENCODER_PID 0x0952 +#define KEYCHRON_V6_ABNT2_ENCODER_PID 0x0367 +#define KEYCHRON_V6_ANSI_PID 0x0360 +#define KEYCHRON_V6_ANSI_ENCODER_PID 0x0361 +#define KEYCHRON_V6_ISO_PID 0x0362 +#define KEYCHRON_V6_ISO_ENCODER_PID 0x0363 +#define KEYCHRON_V6_MAX_ANSI_ENCODER_PID 0x0960 +#define KEYCHRON_V6_MAX_ISO_ENCODER_PID 0x0961 +#define KEYCHRON_V6_MAX_JIS_ENCODER_PID 0x0962 +#define KEYCHRON_V6_V2_ISO_ENCODER_PID 0x0368 +#define KEYCHRON_V7_ANSI_PID 0x0370 +#define KEYCHRON_V7_ISO_PID 0x0372 +#define KEYCHRON_V8_ANSI_PID 0x0380 +#define KEYCHRON_V8_ANSI_ENCODER_PID 0x0381 +#define KEYCHRON_V8_ISO_PID 0x0382 +#define KEYCHRON_V8_ISO_ENCODER_PID 0x0383 +#define KEYCHRON_V8_MAX_ANSI_ENCODER_PID 0x0980 +#define KEYCHRON_V8_MAX_ISO_ENCODER_PID 0x0981 +#define KEYCHRON_V10_ANSI_ENCODER_PID 0x03A1 +#define KEYCHRON_V10_ISO_ENCODER_PID 0x03A3 +#define KEYCHRON_V10_MAX_ANSI_ENCODER_PID 0x09A0 +#define KEYCHRON_V10_MAX_ISO_ENCODER_PID 0x09A1 + +/*---------------------------------------------------------*\ +| QMK raw HID usage page/usage | +\*---------------------------------------------------------*/ +#define KEYCHRON_QMK_USAGE_PAGE 0xFF60 +#define KEYCHRON_QMK_USAGE 0x61 + +/*---------------------------------------------------------*\ +| HID packet constants | +\*---------------------------------------------------------*/ +#define KEYCHRON_QHE_PACKET_SIZE 32 +#define KEYCHRON_QHE_HID_READ_TIMEOUT 1000 + +/*---------------------------------------------------------*\ +| Keychron-specific VIA protocol extension | +\*---------------------------------------------------------*/ +enum +{ + KC_GET_PROTOCOL_VERSION = 0xA0, + KC_GET_FIRMWARE_VERSION = 0xA1, + KC_GET_SUPPORT_FEATURE = 0xA2, + KC_GET_DEFAULT_LAYER = 0xA3, + KC_MISC_CMD_GROUP = 0xA7, + KC_KEYCHRON_RGB = 0xA8, + KC_ANALOG_MATRIX = 0xA9, + KC_WIRELESS_DFU = 0xAA, + KC_FACTORY_TEST = 0xAB +}; + +enum KeychronKCRGBCommand +{ + KEYCHRON_RGB_PROTOCOL_VER = 0x01, + KEYCHRON_RGB_SAVE = 0x02, + KEYCHRON_RGB_GET_INDICATORS = 0x03, + KEYCHRON_RGB_SET_INDICATORS = 0x04, + KEYCHRON_RGB_LED_COUNT = 0x05, + KEYCHRON_RGB_LED_IDX = 0x06, + KEYCHRON_RGB_PER_KEY_GET_TYPE = 0x07, + KEYCHRON_RGB_PER_KEY_SET_TYPE = 0x08, + KEYCHRON_RGB_PER_KEY_GET_COLOR = 0x09, + KEYCHRON_RGB_PER_KEY_SET_COLOR = 0x0A, + KEYCHRON_RGB_MIXED_GET_INFO = 0x0B, + KEYCHRON_RGB_MIXED_GET_REGIONS = 0x0C, + KEYCHRON_RGB_MIXED_SET_REGIONS = 0x0D, + KEYCHRON_RGB_MIXED_GET_EFFECTS = 0x0E, + KEYCHRON_RGB_MIXED_SET_EFFECTS = 0x0F, +}; + +enum +{ + KC_FEATURE_DEFAULT_LAYER = ( 1 << 0 ), + KC_FEATURE_BLUETOOTH = ( 1 << 1 ), + KC_FEATURE_P24G = ( 1 << 2 ), + KC_FEATURE_ANALOG_MATRIX = ( 1 << 3 ), + KC_FEATURE_STATE_NOTIFY = ( 1 << 4 ), + KC_FEATURE_DYNAMIC_DEBOUNCE = ( 1 << 5 ), + KC_FEATURE_SNAP_CLICK = ( 1 << 6 ), + KC_FEATURE_KEYCHRON_RGB = ( 1 << 7 ), + KC_FEATURE_QUICK_START = ( 1 << 8 ), + KC_FEATURE_NKRO = ( 1 << 9 ), +}; + +/*---------------------------------------------------------*\ +| Per-key RGB animation types (PER_KEY_RGB_SET_TYPE value) | +\*---------------------------------------------------------*/ +enum KeychronPerKeyRgbType +{ + KEYCHRON_PER_KEY_RGB_SOLID = 0, + KEYCHRON_PER_KEY_RGB_BREATHING = 1, + KEYCHRON_PER_KEY_RGB_REACTIVE_SIMPLE = 2, + KEYCHRON_PER_KEY_RGB_REACTIVE_WIDE = 3, + KEYCHRON_PER_KEY_RGB_REACTIVE_SPLASH = 4, +}; + +/*---------------------------------------------------------*\ +| VIA backlight value IDs | +\*---------------------------------------------------------*/ +#define KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX 0x03 + +enum KeychronVIABacklightValueID +{ + KEYCHRON_VIA_BACKLIGHT_BRIGHTNESS = 0x01, + KEYCHRON_VIA_BACKLIGHT_EFFECT = 0x02, + KEYCHRON_VIA_BACKLIGHT_SPEED = 0x03, + KEYCHRON_VIA_BACKLIGHT_COLOR = 0x04, +}; + +/*---------------------------------------------------------*\ +| Q1 HE effect IDs | +| | +| Determined by the number of standard effects enabled | +| in the Q1 HE firmware (info.json animations) + | +| 2 custom effects (PER_KEY_RGB, MIXED_RGB). | +| May need adjustment for other firmware versions. | +\*---------------------------------------------------------*/ +#define KEYCHRON_QHE_PER_KEY_RGB_EFFECT 23 + +/*---------------------------------------------------------*\ +| Brightness and speed ranges | +\*---------------------------------------------------------*/ +#define KEYCHRON_QHE_MIN_BRIGHTNESS 0x00 +#define KEYCHRON_QHE_MAX_BRIGHTNESS 0xFF +#define KEYCHRON_QHE_MIN_SPEED 0x00 +#define KEYCHRON_QHE_MAX_SPEED 0xFF + +class QMKKeychronController +{ +public: + QMKKeychronController(hid_device* dev_handle, const char *path); + ~QMKKeychronController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + std::string GetVendor(); + std::string GetVersion(); + + bool GetSupported(); + + unsigned short GetKeycode(unsigned short led_index); + unsigned short GetLEDCount(); + qmk_rgb_matrix_led_info GetLEDInfo(unsigned short led_index); + + void SendLEDs(unsigned short number_leds, RGBColor* color_data); + void SetMode(unsigned short mode, unsigned char speed, unsigned char hue, unsigned char sat, unsigned char val); + + void SaveMode(); + +private: + hid_device* dev; + std::string kc_firmware_version; + unsigned char kc_protocol_version; + unsigned short kc_rgb_protocol_version; + std::vector keycodes; + std::vector led_info; + std::string location; + std::string name; + unsigned short number_leds; + std::string serial; + unsigned short supported_features; + std::string vendor; + unsigned short via_protocol_version; + + unsigned short CmdGetKeycode(unsigned char layer, unsigned char row, unsigned char col); + std::string CmdGetKeychronFirmwareVersion(); + void CmdGetKeychronProtocolVersion(unsigned char* kc_protocol_version); + void CmdGetKeychronRGBProtocolVersion(unsigned short* kc_rgb_protocol_version); + std::vector CmdGetLEDIndexByRow(unsigned char row); + void CmdGetNumberLEDs(unsigned short* number_leds); + void CmdGetSupportFeature(unsigned short* supported_features); + void CmdGetViaProtocolVersion(unsigned short* via_protocol_version); + void CmdSaveMode(); + void CmdSendLEDs(unsigned char start_index, unsigned char number_leds, RGBColor* color_data); + void CmdSetBrightness(unsigned char brightness); + void CmdSetColorHS(unsigned char h, unsigned char s); + void CmdSetPerKeyRGBType(unsigned char type); + void CmdSetRGBMatrixMode(unsigned char mode); + void CmdSetSpeed(unsigned char speed); + + int ViaSendCommand + ( + unsigned char cmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ); + + int ViaSendCommandSub + ( + unsigned char cmd, + unsigned char subcmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ); +}; diff --git a/Controllers/QMKController/QMKKeychronController/QMKKeychronControllerDetect.cpp b/Controllers/QMKController/QMKKeychronController/QMKKeychronControllerDetect.cpp new file mode 100644 index 0000000..60828a8 --- /dev/null +++ b/Controllers/QMKController/QMKKeychronController/QMKKeychronControllerDetect.cpp @@ -0,0 +1,279 @@ +/*---------------------------------------------------------*\ +| QMKKeychronControllerDetect.cpp | +| | +| Detector for Keychron QMK-based keyboards | +| | +| Amadej Kastelic 21 Jun 2026 | +| Adam Honse 22 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "QMKKeychronController.h" +#include "RGBController_QMKKeychron.h" + +void DetectQMKKeychronControllers(hid_device_info *info, const std::string&) +{ + hid_device *dev = hid_open_path(info->path); + + if(dev) + { + QMKKeychronController* controller = new QMKKeychronController(dev, info->path); + + if(controller->GetSupported()) + { + RGBController_QMKKeychron* rgb_controller = new RGBController_QMKKeychron(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + delete controller; + } + } +} + +REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_8K_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_8K_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_8K_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C1 Pro V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C1_PRO_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_8K_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_8K_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C2 Pro V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C2_PRO_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_8K_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_8K_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron C3 Pro 8K", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_C3_PRO_8K_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K0 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K0_MAX_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K1 V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_V6_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K1 V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_V6_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K1 V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K1_V6_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_HE_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K2 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_V3_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K2 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_V3_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K2 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K2_V3_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K3 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_V3_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K3 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_V3_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K3 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K3_V3_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_HE_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K4 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_V3_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K4 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_V3_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K4 V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K4_V3_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_MAX_JIS_V2_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K5 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K5 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_V2_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K5 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K5_V2_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K6_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K7 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K7_MAX_JIS_V2_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_HE_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_PRO_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_PRO_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 Pro", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_PRO_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_V2_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K8 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K8_V2_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K9 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K9_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K10 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K10 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_HE_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K10 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_V2_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K10 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_V2_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K10 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K10_V2_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K11 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K11_MAX_ANSI_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K11 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K11_MAX_ISO_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K11 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K11_MAX_JIS_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K13_MAX_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K13_MAX_ISO_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K13_MAX_JIS_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K15 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K15_MAX_ANSI_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K15 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K15_MAX_ISO_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_ANSI_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_ISO_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron K17 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_K17_MAX_JIS_ENCODER_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q0", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_BASE_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q0 Plus", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_PLUS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q0 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q0_MAX_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V1_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q1 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q1_V2_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q2 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q2_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q3_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q4_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q4_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q4 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q4_HE_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q5 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q5 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q5 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q5_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q6 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_HE_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q6_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q7_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q7_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q8_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q9", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q9 Plus", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q9_PLUS_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q10_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q11", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q11_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q11", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q11_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q12", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q12", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q12 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_HE_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q12 HE", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_HE_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q12 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q12 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q12_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q13_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q13 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q13_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q14 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q14_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q15 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q15_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q60 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q60_MAX_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q65 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_Q65_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron S1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_S1_ANSI_RGB_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_8K_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V1 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V1_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V2 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V2_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_JIS_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V3", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V3 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V3_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V4", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_MAX_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V4 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V4_MAX_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V5", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V5 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V5_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ABNT2_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V6", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V6 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_MAX_JIS_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V6 V2", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V6_V2_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V7_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V7", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V7_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ANSI_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ISO_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V8", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V8 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V8_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V10", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_MAX_ANSI_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron V10 Max", DetectQMKKeychronControllers, KEYCHRON_VID, KEYCHRON_V10_MAX_ISO_ENCODER_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); diff --git a/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.cpp b/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.cpp new file mode 100644 index 0000000..d1c2c64 --- /dev/null +++ b/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.cpp @@ -0,0 +1,239 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKKeychron.cpp | +| | +| RGBController for Keychron QMK-based keyboards | +| | +| Amadej Kastelic 21 Jun 2026 | +| Adam Honse 22 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "hsv.h" +#include "RGBController_QMKKeychron.h" +#include "QMKKeycodes.h" +#include "QMKKeychronController.h" + +/**------------------------------------------------------------------*\ + @name QMK Keychron + @category Keyboard + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectQMKKeychronController + @comment +\*-------------------------------------------------------------------*/ + +typedef struct +{ + std::string name; + int value; + int flags; +} kc_effect; + +static const kc_effect kc_effects[] = +{ + { "Direct", 0xFFFF, MODE_FLAG_HAS_PER_LED_COLOR }, + { "Solid Color", 1, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_MANUAL_SAVE }, + { "Breathing", 2, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE }, + { "Band Spiral", 3, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE }, + { "Cycle All", 4, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Cycle Left Right", 5, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Cycle Up Down", 6, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Rainbow Moving Chevron", 7, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Cycle Out In", 8, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Cycle Out In Dual", 9, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Cycle Pinwheel", 10, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Cycle Spiral", 11, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Dual Beacon", 12, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Rainbow Beacon", 13, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Jellybean Raindrops", 14, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Pixel Rain", 15, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Typing Heatmap", 16, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Digital Rain", 17, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE }, + { "Solid Reactive Simple", 18, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE }, + { "Solid Reactive Multiwide", 19, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE }, + { "Solid Reactive Multinexus", 20, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE }, + { "Splash", 21, MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE }, + { "Solid Splash", 22, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE }, +}; + +RGBController_QMKKeychron::RGBController_QMKKeychron(QMKKeychronController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = controller->GetVendor(); + type = DEVICE_TYPE_KEYBOARD; + description = "QMK Keychron Device"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + version = controller->GetVersion(); + + for(const kc_effect& effect : kc_effects) + { + mode m; + m.name = effect.name; + m.value = effect.value; + m.flags = effect.flags; + + if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + m.color_mode = MODE_COLORS_MODE_SPECIFIC; + m.colors_min = 1; + m.colors_max = 1; + m.colors.resize(1); + } + else if(m.flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + m.color_mode = MODE_COLORS_PER_LED; + m.colors_min = 0; + m.colors_max = 0; + m.colors.resize(0); + } + else + { + m.color_mode = MODE_COLORS_NONE; + m.colors_min = 0; + m.colors_max = 0; + m.colors.resize(0); + } + + if(m.flags & MODE_FLAG_HAS_SPEED) + { + m.speed_min = KEYCHRON_QHE_MIN_SPEED; + m.speed_max = KEYCHRON_QHE_MAX_SPEED; + m.speed = KEYCHRON_QHE_MAX_SPEED / 2; + } + + if(m.flags & MODE_FLAG_HAS_BRIGHTNESS) + { + m.brightness_min = KEYCHRON_QHE_MIN_BRIGHTNESS; + m.brightness_max = KEYCHRON_QHE_MAX_BRIGHTNESS; + m.brightness = KEYCHRON_QHE_MAX_BRIGHTNESS; + } + + modes.push_back(m); + } + + SetupZones(); +} + +RGBController_QMKKeychron::~RGBController_QMKKeychron() +{ + delete controller; +} + +void RGBController_QMKKeychron::SetupZones() +{ + /*-----------------------------------------------------*\ + | Build matrix map | + \*-----------------------------------------------------*/ + unsigned char max_col = 0; + unsigned char max_row = 0; + + for(unsigned short led_index = 0; led_index < controller->GetLEDCount(); led_index++) + { + qmk_rgb_matrix_led_info info = controller->GetLEDInfo(led_index); + + if(info.col > max_col) + { + max_col = info.col; + } + + if(info.row > max_row) + { + max_row = info.row; + } + } + + unsigned char height = max_row + 1; + unsigned char width = max_col + 1; + + unsigned int* matrix_map = new unsigned int[width * height]; + + memset(matrix_map, 0xFF, (sizeof(unsigned int) * (width * height))); + + for(unsigned short led_index = 0; led_index < controller->GetLEDCount(); led_index++) + { + qmk_rgb_matrix_led_info info = controller->GetLEDInfo(led_index); + + matrix_map[(width * info.row) + info.col] = (unsigned int)led_index; + } + + /*-----------------------------------------------------*\ + | Create keyboard zone | + \*-----------------------------------------------------*/ + zone keyboard; + + keyboard.name = "Keyboard"; + keyboard.type = ZONE_TYPE_MATRIX; + keyboard.leds_min = controller->GetLEDCount(); + keyboard.leds_max = controller->GetLEDCount(); + keyboard.leds_count = controller->GetLEDCount(); + keyboard.matrix_map = new matrix_map_type; + keyboard.matrix_map->height = height; + keyboard.matrix_map->width = width; + keyboard.matrix_map->map = matrix_map; + + zones.push_back(keyboard); + + /*-----------------------------------------------------*\ + | Create keyboard LEDs | + \*-----------------------------------------------------*/ + for(unsigned short led_idx = 0; led_idx < controller->GetLEDCount(); led_idx++) + { + led new_led; + new_led.name = qmk_keynames[controller->GetKeycode(led_idx)]; + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_QMKKeychron::ResizeZone(int /*zone*/, int /*new_size*/) +{ +} + +void RGBController_QMKKeychron::DeviceUpdateLEDs() +{ + controller->SendLEDs((unsigned short)colors.size(), colors.data()); +} + +void RGBController_QMKKeychron::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_QMKKeychron::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_QMKKeychron::DeviceUpdateMode() +{ + unsigned char hue = 0; + unsigned char sat = 255; + unsigned char val = modes[active_mode].brightness; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + hsv_t hsv_color; + rgb2hsv(modes[active_mode].colors[0], &hsv_color); + + hue = (unsigned char)((float)hsv_color.hue * (256.0f / 360.0f)); + sat = hsv_color.saturation; + val = hsv_color.value; + } + + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, hue, sat, val); +} + +void RGBController_QMKKeychron::DeviceSaveMode() +{ + controller->SaveMode(); +} diff --git a/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.h b/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.h new file mode 100644 index 0000000..625d02f --- /dev/null +++ b/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKKeychron.h | +| | +| RGBController for Keychron QMK-based keyboards | +| | +| Amadej Kastelic 21 Jun 2026 | +| Adam Honse 22 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "QMKKeychronController.h" + +class RGBController_QMKKeychron : public RGBController +{ +public: + RGBController_QMKKeychron(QMKKeychronController* controller_ptr); + ~RGBController_QMKKeychron(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + QMKKeychronController* controller; +}; diff --git a/Controllers/QMKController/QMKKeycodes.cpp b/Controllers/QMKController/QMKKeycodes.cpp new file mode 100644 index 0000000..e8d6c6b --- /dev/null +++ b/Controllers/QMKController/QMKKeycodes.cpp @@ -0,0 +1,153 @@ +/*---------------------------------------------------------*\ +| QMKKeycodes.cpp | +| | +| List of QMK keycode values | +| | +| Adam Honse qmk_keynames = +{ + { QMK_KC_NO, KEY_EN_UNUSED }, + { QMK_KC_TRANSPARENT, KEY_EN_RIGHT_FUNCTION }, + { QMK_KC_A, KEY_EN_A }, + { QMK_KC_B, KEY_EN_B }, + { QMK_KC_C, KEY_EN_C }, + { QMK_KC_D, KEY_EN_D }, + { QMK_KC_E, KEY_EN_E }, + { QMK_KC_F, KEY_EN_F }, + { QMK_KC_G, KEY_EN_G }, + { QMK_KC_H, KEY_EN_H }, + { QMK_KC_I, KEY_EN_I }, + { QMK_KC_J, KEY_EN_J }, + { QMK_KC_K, KEY_EN_K }, + { QMK_KC_L, KEY_EN_L }, + { QMK_KC_M, KEY_EN_M }, + { QMK_KC_N, KEY_EN_N }, + { QMK_KC_O, KEY_EN_O }, + { QMK_KC_P, KEY_EN_P }, + { QMK_KC_Q, KEY_EN_Q }, + { QMK_KC_R, KEY_EN_R }, + { QMK_KC_S, KEY_EN_S }, + { QMK_KC_T, KEY_EN_T }, + { QMK_KC_U, KEY_EN_U }, + { QMK_KC_V, KEY_EN_V }, + { QMK_KC_W, KEY_EN_W }, + { QMK_KC_X, KEY_EN_X }, + { QMK_KC_Y, KEY_EN_Y }, + { QMK_KC_Z, KEY_EN_Z }, + { QMK_KC_1, KEY_EN_1 }, + { QMK_KC_2, KEY_EN_2 }, + { QMK_KC_3, KEY_EN_3 }, + { QMK_KC_4, KEY_EN_4 }, + { QMK_KC_5, KEY_EN_5 }, + { QMK_KC_6, KEY_EN_6 }, + { QMK_KC_7, KEY_EN_7 }, + { QMK_KC_8, KEY_EN_8 }, + { QMK_KC_9, KEY_EN_9 }, + { QMK_KC_0, KEY_EN_0 }, + { QMK_KC_ENTER, KEY_EN_ANSI_ENTER }, + { QMK_KC_ESCAPE, KEY_EN_ESCAPE }, + { QMK_KC_BACKSPACE, KEY_EN_BACKSPACE }, + { QMK_KC_TAB, KEY_EN_TAB }, + { QMK_KC_SPACE, KEY_EN_SPACE }, + { QMK_KC_MINUS, KEY_EN_MINUS }, + { QMK_KC_EQUAL, KEY_EN_EQUALS }, + { QMK_KC_LEFT_BRACKET, KEY_EN_LEFT_BRACKET }, + { QMK_KC_RIGHT_BRACKET, KEY_EN_RIGHT_BRACKET }, + { QMK_KC_BACKSLASH, KEY_EN_ANSI_BACK_SLASH }, + { QMK_KC_NONUS_HASH, KEY_EN_POUND }, + { QMK_KC_SEMICOLON, KEY_EN_SEMICOLON }, + { QMK_KC_QUOTE, KEY_EN_QUOTE }, + { QMK_KC_GRAVE, KEY_EN_BACK_TICK }, + { QMK_KC_COMMA, KEY_EN_COMMA }, + { QMK_KC_DOT, KEY_EN_PERIOD }, + { QMK_KC_SLASH, KEY_EN_FORWARD_SLASH }, + { QMK_KC_CAPS_LOCK, KEY_EN_CAPS_LOCK }, + { QMK_KC_F1, KEY_EN_F1 }, + { QMK_KC_F2, KEY_EN_F2 }, + { QMK_KC_F3, KEY_EN_F3 }, + { QMK_KC_F4, KEY_EN_F4 }, + { QMK_KC_F5, KEY_EN_F5 }, + { QMK_KC_F6, KEY_EN_F6 }, + { QMK_KC_F7, KEY_EN_F7 }, + { QMK_KC_F8, KEY_EN_F8 }, + { QMK_KC_F9, KEY_EN_F9 }, + { QMK_KC_F10, KEY_EN_F10 }, + { QMK_KC_F11, KEY_EN_F11 }, + { QMK_KC_F12, KEY_EN_F12 }, + { QMK_KC_PRINT_SCREEN, KEY_EN_PRINT_SCREEN }, + { QMK_KC_SCROLL_LOCK, KEY_EN_SCROLL_LOCK }, + { QMK_KC_PAUSE, KEY_EN_PAUSE_BREAK }, + { QMK_KC_INSERT, KEY_EN_INSERT }, + { QMK_KC_HOME, KEY_EN_HOME }, + { QMK_KC_PAGE_UP, KEY_EN_PAGE_UP }, + { QMK_KC_DELETE, KEY_EN_DELETE }, + { QMK_KC_END, KEY_EN_END }, + { QMK_KC_PAGE_DOWN, KEY_EN_PAGE_DOWN }, + { QMK_KC_RIGHT, KEY_EN_RIGHT_ARROW }, + { QMK_KC_LEFT, KEY_EN_LEFT_ARROW }, + { QMK_KC_DOWN, KEY_EN_DOWN_ARROW }, + { QMK_KC_UP, KEY_EN_UP_ARROW }, + { QMK_KC_NUM_LOCK, KEY_EN_NUMPAD_LOCK }, + { QMK_KC_KP_SLASH, KEY_EN_NUMPAD_DIVIDE }, + { QMK_KC_KP_ASTERISK, KEY_EN_NUMPAD_TIMES }, + { QMK_KC_KP_MINUS, KEY_EN_NUMPAD_MINUS }, + { QMK_KC_KP_PLUS, KEY_EN_NUMPAD_PLUS }, + { QMK_KC_KP_ENTER, KEY_EN_NUMPAD_ENTER }, + { QMK_KC_KP_1, KEY_EN_NUMPAD_1 }, + { QMK_KC_KP_2, KEY_EN_NUMPAD_2 }, + { QMK_KC_KP_3, KEY_EN_NUMPAD_3 }, + { QMK_KC_KP_4, KEY_EN_NUMPAD_4 }, + { QMK_KC_KP_5, KEY_EN_NUMPAD_5 }, + { QMK_KC_KP_6, KEY_EN_NUMPAD_6 }, + { QMK_KC_KP_7, KEY_EN_NUMPAD_7 }, + { QMK_KC_KP_8, KEY_EN_NUMPAD_8 }, + { QMK_KC_KP_9, KEY_EN_NUMPAD_9 }, + { QMK_KC_KP_0, KEY_EN_NUMPAD_0 }, + { QMK_KC_KP_DOT, KEY_EN_NUMPAD_PERIOD }, + { QMK_KC_NONUS_BACKSLASH, KEY_EN_ISO_BACK_SLASH }, + { QMK_KC_APPLICATION, KEY_EN_MENU }, + { QMK_KC_F13, "Key: F13" }, + { QMK_KC_F14, "Key: F14" }, + { QMK_KC_F15, "Key: F15" }, + { QMK_KC_F16, "Key: F16" }, + { QMK_KC_F17, "Key: F17" }, + { QMK_KC_F18, "Key: F18" }, + { QMK_KC_F19, "Key: F19" }, + { QMK_KC_F20, "Key: F20" }, + { QMK_KC_F21, "Key: F21" }, + { QMK_KC_F22, "Key: F22" }, + { QMK_KC_F23, "Key: F23" }, + { QMK_KC_F24, "Key: F24" }, + { QMK_KC_AUDIO_MUTE, KEY_EN_MEDIA_MUTE }, + { QMK_KC_AUDIO_VOL_UP, KEY_EN_MEDIA_VOLUME_UP }, + { QMK_KC_AUDIO_VOL_DOWN, KEY_EN_MEDIA_VOLUME_DOWN }, + { QMK_KC_MEDIA_NEXT_TRACK, KEY_EN_MEDIA_NEXT }, + { QMK_KC_MEDIA_PREV_TRACK, KEY_EN_MEDIA_PREVIOUS }, + { QMK_KC_MEDIA_STOP, KEY_EN_MEDIA_STOP }, + { QMK_KC_MEDIA_PLAY_PAUSE, KEY_EN_MEDIA_PLAY_PAUSE }, + { QMK_KC_MEDIA_SELECT, "Key: Media Select" }, + { QMK_KC_MEDIA_EJECT, "Key: Media Eject" }, + { QMK_KC_BRIGHTNESS_UP, "Key: Brightness Up" }, + { QMK_KC_BRIGHTNESS_DOWN, "Key: Brightness Down" }, + { 196, "Key: Task Manager" }, /* From OpenRGB QMK */ + { 202, "Key: RGB Brightness Up" }, /* From OpenRGB QMK */ + { 203, "Key: RGB Brightness Down" }, /* From OpenRGB QMK */ + { 216, KEY_EN_LEFT_SHIFT }, /* Space Cadet Left Shift */ + { 217, KEY_EN_RIGHT_SHIFT }, /* Space Cadet Right Shift */ + { QMK_KC_LEFT_CTRL, KEY_EN_LEFT_CONTROL }, + { QMK_KC_LEFT_SHIFT, KEY_EN_LEFT_SHIFT }, + { QMK_KC_LEFT_ALT, KEY_EN_LEFT_ALT }, + { QMK_KC_LEFT_GUI, KEY_EN_LEFT_WINDOWS }, + { QMK_KC_RIGHT_CTRL, KEY_EN_RIGHT_CONTROL }, + { QMK_KC_RIGHT_SHIFT, KEY_EN_RIGHT_SHIFT }, + { QMK_KC_RIGHT_ALT, KEY_EN_RIGHT_ALT }, + { QMK_KC_RIGHT_GUI, KEY_EN_RIGHT_WINDOWS }, +}; diff --git a/Controllers/QMKController/QMKKeycodes.h b/Controllers/QMKController/QMKKeycodes.h new file mode 100644 index 0000000..46d192b --- /dev/null +++ b/Controllers/QMKController/QMKKeycodes.h @@ -0,0 +1,754 @@ +/*---------------------------------------------------------*\ +| QMKKeycodes.h | +| | +| List of QMK keycode values | +| | +| Adam Honse +#include + +typedef unsigned short qmk_keycode; +enum +{ + QMK_KC_NO = 0x0000, + QMK_KC_TRANSPARENT = 0x0001, + QMK_KC_A = 0x0004, + QMK_KC_B = 0x0005, + QMK_KC_C = 0x0006, + QMK_KC_D = 0x0007, + QMK_KC_E = 0x0008, + QMK_KC_F = 0x0009, + QMK_KC_G = 0x000A, + QMK_KC_H = 0x000B, + QMK_KC_I = 0x000C, + QMK_KC_J = 0x000D, + QMK_KC_K = 0x000E, + QMK_KC_L = 0x000F, + QMK_KC_M = 0x0010, + QMK_KC_N = 0x0011, + QMK_KC_O = 0x0012, + QMK_KC_P = 0x0013, + QMK_KC_Q = 0x0014, + QMK_KC_R = 0x0015, + QMK_KC_S = 0x0016, + QMK_KC_T = 0x0017, + QMK_KC_U = 0x0018, + QMK_KC_V = 0x0019, + QMK_KC_W = 0x001A, + QMK_KC_X = 0x001B, + QMK_KC_Y = 0x001C, + QMK_KC_Z = 0x001D, + QMK_KC_1 = 0x001E, + QMK_KC_2 = 0x001F, + QMK_KC_3 = 0x0020, + QMK_KC_4 = 0x0021, + QMK_KC_5 = 0x0022, + QMK_KC_6 = 0x0023, + QMK_KC_7 = 0x0024, + QMK_KC_8 = 0x0025, + QMK_KC_9 = 0x0026, + QMK_KC_0 = 0x0027, + QMK_KC_ENTER = 0x0028, + QMK_KC_ESCAPE = 0x0029, + QMK_KC_BACKSPACE = 0x002A, + QMK_KC_TAB = 0x002B, + QMK_KC_SPACE = 0x002C, + QMK_KC_MINUS = 0x002D, + QMK_KC_EQUAL = 0x002E, + QMK_KC_LEFT_BRACKET = 0x002F, + QMK_KC_RIGHT_BRACKET = 0x0030, + QMK_KC_BACKSLASH = 0x0031, + QMK_KC_NONUS_HASH = 0x0032, + QMK_KC_SEMICOLON = 0x0033, + QMK_KC_QUOTE = 0x0034, + QMK_KC_GRAVE = 0x0035, + QMK_KC_COMMA = 0x0036, + QMK_KC_DOT = 0x0037, + QMK_KC_SLASH = 0x0038, + QMK_KC_CAPS_LOCK = 0x0039, + QMK_KC_F1 = 0x003A, + QMK_KC_F2 = 0x003B, + QMK_KC_F3 = 0x003C, + QMK_KC_F4 = 0x003D, + QMK_KC_F5 = 0x003E, + QMK_KC_F6 = 0x003F, + QMK_KC_F7 = 0x0040, + QMK_KC_F8 = 0x0041, + QMK_KC_F9 = 0x0042, + QMK_KC_F10 = 0x0043, + QMK_KC_F11 = 0x0044, + QMK_KC_F12 = 0x0045, + QMK_KC_PRINT_SCREEN = 0x0046, + QMK_KC_SCROLL_LOCK = 0x0047, + QMK_KC_PAUSE = 0x0048, + QMK_KC_INSERT = 0x0049, + QMK_KC_HOME = 0x004A, + QMK_KC_PAGE_UP = 0x004B, + QMK_KC_DELETE = 0x004C, + QMK_KC_END = 0x004D, + QMK_KC_PAGE_DOWN = 0x004E, + QMK_KC_RIGHT = 0x004F, + QMK_KC_LEFT = 0x0050, + QMK_KC_DOWN = 0x0051, + QMK_KC_UP = 0x0052, + QMK_KC_NUM_LOCK = 0x0053, + QMK_KC_KP_SLASH = 0x0054, + QMK_KC_KP_ASTERISK = 0x0055, + QMK_KC_KP_MINUS = 0x0056, + QMK_KC_KP_PLUS = 0x0057, + QMK_KC_KP_ENTER = 0x0058, + QMK_KC_KP_1 = 0x0059, + QMK_KC_KP_2 = 0x005A, + QMK_KC_KP_3 = 0x005B, + QMK_KC_KP_4 = 0x005C, + QMK_KC_KP_5 = 0x005D, + QMK_KC_KP_6 = 0x005E, + QMK_KC_KP_7 = 0x005F, + QMK_KC_KP_8 = 0x0060, + QMK_KC_KP_9 = 0x0061, + QMK_KC_KP_0 = 0x0062, + QMK_KC_KP_DOT = 0x0063, + QMK_KC_NONUS_BACKSLASH = 0x0064, + QMK_KC_APPLICATION = 0x0065, + QMK_KC_KB_POWER = 0x0066, + QMK_KC_KP_EQUAL = 0x0067, + QMK_KC_F13 = 0x0068, + QMK_KC_F14 = 0x0069, + QMK_KC_F15 = 0x006A, + QMK_KC_F16 = 0x006B, + QMK_KC_F17 = 0x006C, + QMK_KC_F18 = 0x006D, + QMK_KC_F19 = 0x006E, + QMK_KC_F20 = 0x006F, + QMK_KC_F21 = 0x0070, + QMK_KC_F22 = 0x0071, + QMK_KC_F23 = 0x0072, + QMK_KC_F24 = 0x0073, + QMK_KC_EXECUTE = 0x0074, + QMK_KC_HELP = 0x0075, + QMK_KC_MENU = 0x0076, + QMK_KC_SELECT = 0x0077, + QMK_KC_STOP = 0x0078, + QMK_KC_AGAIN = 0x0079, + QMK_KC_UNDO = 0x007A, + QMK_KC_CUT = 0x007B, + QMK_KC_COPY = 0x007C, + QMK_KC_PASTE = 0x007D, + QMK_KC_FIND = 0x007E, + QMK_KC_KB_MUTE = 0x007F, + QMK_KC_KB_VOLUME_UP = 0x0080, + QMK_KC_KB_VOLUME_DOWN = 0x0081, + QMK_KC_LOCKING_CAPS_LOCK = 0x0082, + QMK_KC_LOCKING_NUM_LOCK = 0x0083, + QMK_KC_LOCKING_SCROLL_LOCK = 0x0084, + QMK_KC_KP_COMMA = 0x0085, + QMK_KC_KP_EQUAL_AS400 = 0x0086, + QMK_KC_INTERNATIONAL_1 = 0x0087, + QMK_KC_INTERNATIONAL_2 = 0x0088, + QMK_KC_INTERNATIONAL_3 = 0x0089, + QMK_KC_INTERNATIONAL_4 = 0x008A, + QMK_KC_INTERNATIONAL_5 = 0x008B, + QMK_KC_INTERNATIONAL_6 = 0x008C, + QMK_KC_INTERNATIONAL_7 = 0x008D, + QMK_KC_INTERNATIONAL_8 = 0x008E, + QMK_KC_INTERNATIONAL_9 = 0x008F, + QMK_KC_LANGUAGE_1 = 0x0090, + QMK_KC_LANGUAGE_2 = 0x0091, + QMK_KC_LANGUAGE_3 = 0x0092, + QMK_KC_LANGUAGE_4 = 0x0093, + QMK_KC_LANGUAGE_5 = 0x0094, + QMK_KC_LANGUAGE_6 = 0x0095, + QMK_KC_LANGUAGE_7 = 0x0096, + QMK_KC_LANGUAGE_8 = 0x0097, + QMK_KC_LANGUAGE_9 = 0x0098, + QMK_KC_ALTERNATE_ERASE = 0x0099, + QMK_KC_SYSTEM_REQUEST = 0x009A, + QMK_KC_CANCEL = 0x009B, + QMK_KC_CLEAR = 0x009C, + QMK_KC_PRIOR = 0x009D, + QMK_KC_RETURN = 0x009E, + QMK_KC_SEPARATOR = 0x009F, + QMK_KC_OUT = 0x00A0, + QMK_KC_OPER = 0x00A1, + QMK_KC_CLEAR_AGAIN = 0x00A2, + QMK_KC_CRSEL = 0x00A3, + QMK_KC_EXSEL = 0x00A4, + QMK_KC_SYSTEM_POWER = 0x00A5, + QMK_KC_SYSTEM_SLEEP = 0x00A6, + QMK_KC_SYSTEM_WAKE = 0x00A7, + QMK_KC_AUDIO_MUTE = 0x00A8, + QMK_KC_AUDIO_VOL_UP = 0x00A9, + QMK_KC_AUDIO_VOL_DOWN = 0x00AA, + QMK_KC_MEDIA_NEXT_TRACK = 0x00AB, + QMK_KC_MEDIA_PREV_TRACK = 0x00AC, + QMK_KC_MEDIA_STOP = 0x00AD, + QMK_KC_MEDIA_PLAY_PAUSE = 0x00AE, + QMK_KC_MEDIA_SELECT = 0x00AF, + QMK_KC_MEDIA_EJECT = 0x00B0, + QMK_KC_MAIL = 0x00B1, + QMK_KC_CALCULATOR = 0x00B2, + QMK_KC_MY_COMPUTER = 0x00B3, + QMK_KC_WWW_SEARCH = 0x00B4, + QMK_KC_WWW_HOME = 0x00B5, + QMK_KC_WWW_BACK = 0x00B6, + QMK_KC_WWW_FORWARD = 0x00B7, + QMK_KC_WWW_STOP = 0x00B8, + QMK_KC_WWW_REFRESH = 0x00B9, + QMK_KC_WWW_FAVORITES = 0x00BA, + QMK_KC_MEDIA_FAST_FORWARD = 0x00BB, + QMK_KC_MEDIA_REWIND = 0x00BC, + QMK_KC_BRIGHTNESS_UP = 0x00BD, + QMK_KC_BRIGHTNESS_DOWN = 0x00BE, + QMK_KC_CONTROL_PANEL = 0x00BF, + QMK_KC_ASSISTANT = 0x00C0, + QMK_KC_MISSION_CONTROL = 0x00C1, + QMK_KC_LAUNCHPAD = 0x00C2, + QMK_QK_MOUSE_CURSOR_UP = 0x00CD, + QMK_QK_MOUSE_CURSOR_DOWN = 0x00CE, + QMK_QK_MOUSE_CURSOR_LEFT = 0x00CF, + QMK_QK_MOUSE_CURSOR_RIGHT = 0x00D0, + QMK_QK_MOUSE_BUTTON_1 = 0x00D1, + QMK_QK_MOUSE_BUTTON_2 = 0x00D2, + QMK_QK_MOUSE_BUTTON_3 = 0x00D3, + QMK_QK_MOUSE_BUTTON_4 = 0x00D4, + QMK_QK_MOUSE_BUTTON_5 = 0x00D5, + QMK_QK_MOUSE_BUTTON_6 = 0x00D6, + QMK_QK_MOUSE_BUTTON_7 = 0x00D7, + QMK_QK_MOUSE_BUTTON_8 = 0x00D8, + QMK_QK_MOUSE_WHEEL_UP = 0x00D9, + QMK_QK_MOUSE_WHEEL_DOWN = 0x00DA, + QMK_QK_MOUSE_WHEEL_LEFT = 0x00DB, + QMK_QK_MOUSE_WHEEL_RIGHT = 0x00DC, + QMK_QK_MOUSE_ACCELERATION_0 = 0x00DD, + QMK_QK_MOUSE_ACCELERATION_1 = 0x00DE, + QMK_QK_MOUSE_ACCELERATION_2 = 0x00DF, + QMK_KC_LEFT_CTRL = 0x00E0, + QMK_KC_LEFT_SHIFT = 0x00E1, + QMK_KC_LEFT_ALT = 0x00E2, + QMK_KC_LEFT_GUI = 0x00E3, + QMK_KC_RIGHT_CTRL = 0x00E4, + QMK_KC_RIGHT_SHIFT = 0x00E5, + QMK_KC_RIGHT_ALT = 0x00E6, + QMK_KC_RIGHT_GUI = 0x00E7, + QMK_QK_SWAP_HANDS_TOGGLE = 0x56F0, + QMK_QK_SWAP_HANDS_TAP_TOGGLE = 0x56F1, + QMK_QK_SWAP_HANDS_MOMENTARY_ON = 0x56F2, + QMK_QK_SWAP_HANDS_MOMENTARY_OFF = 0x56F3, + QMK_QK_SWAP_HANDS_OFF = 0x56F4, + QMK_QK_SWAP_HANDS_ON = 0x56F5, + QMK_QK_SWAP_HANDS_ONE_SHOT = 0x56F6, + QMK_QK_MAGIC_SWAP_CONTROL_CAPS_LOCK = 0x7000, + QMK_QK_MAGIC_UNSWAP_CONTROL_CAPS_LOCK = 0x7001, + QMK_QK_MAGIC_TOGGLE_CONTROL_CAPS_LOCK = 0x7002, + QMK_QK_MAGIC_CAPS_LOCK_AS_CONTROL_OFF = 0x7003, + QMK_QK_MAGIC_CAPS_LOCK_AS_CONTROL_ON = 0x7004, + QMK_QK_MAGIC_SWAP_LALT_LGUI = 0x7005, + QMK_QK_MAGIC_UNSWAP_LALT_LGUI = 0x7006, + QMK_QK_MAGIC_SWAP_RALT_RGUI = 0x7007, + QMK_QK_MAGIC_UNSWAP_RALT_RGUI = 0x7008, + QMK_QK_MAGIC_GUI_ON = 0x7009, + QMK_QK_MAGIC_GUI_OFF = 0x700A, + QMK_QK_MAGIC_TOGGLE_GUI = 0x700B, + QMK_QK_MAGIC_SWAP_GRAVE_ESC = 0x700C, + QMK_QK_MAGIC_UNSWAP_GRAVE_ESC = 0x700D, + QMK_QK_MAGIC_SWAP_BACKSLASH_BACKSPACE = 0x700E, + QMK_QK_MAGIC_UNSWAP_BACKSLASH_BACKSPACE = 0x700F, + QMK_QK_MAGIC_TOGGLE_BACKSLASH_BACKSPACE = 0x7010, + QMK_QK_MAGIC_NKRO_ON = 0x7011, + QMK_QK_MAGIC_NKRO_OFF = 0x7012, + QMK_QK_MAGIC_TOGGLE_NKRO = 0x7013, + QMK_QK_MAGIC_SWAP_ALT_GUI = 0x7014, + QMK_QK_MAGIC_UNSWAP_ALT_GUI = 0x7015, + QMK_QK_MAGIC_TOGGLE_ALT_GUI = 0x7016, + QMK_QK_MAGIC_SWAP_LCTL_LGUI = 0x7017, + QMK_QK_MAGIC_UNSWAP_LCTL_LGUI = 0x7018, + QMK_QK_MAGIC_SWAP_RCTL_RGUI = 0x7019, + QMK_QK_MAGIC_UNSWAP_RCTL_RGUI = 0x701A, + QMK_QK_MAGIC_SWAP_CTL_GUI = 0x701B, + QMK_QK_MAGIC_UNSWAP_CTL_GUI = 0x701C, + QMK_QK_MAGIC_TOGGLE_CTL_GUI = 0x701D, + QMK_QK_MAGIC_EE_HANDS_LEFT = 0x701E, + QMK_QK_MAGIC_EE_HANDS_RIGHT = 0x701F, + QMK_QK_MAGIC_SWAP_ESCAPE_CAPS_LOCK = 0x7020, + QMK_QK_MAGIC_UNSWAP_ESCAPE_CAPS_LOCK = 0x7021, + QMK_QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK = 0x7022, + QMK_QK_MIDI_ON = 0x7100, + QMK_QK_MIDI_OFF = 0x7101, + QMK_QK_MIDI_TOGGLE = 0x7102, + QMK_QK_MIDI_NOTE_C_0 = 0x7103, + QMK_QK_MIDI_NOTE_C_SHARP_0 = 0x7104, + QMK_QK_MIDI_NOTE_D_0 = 0x7105, + QMK_QK_MIDI_NOTE_D_SHARP_0 = 0x7106, + QMK_QK_MIDI_NOTE_E_0 = 0x7107, + QMK_QK_MIDI_NOTE_F_0 = 0x7108, + QMK_QK_MIDI_NOTE_F_SHARP_0 = 0x7109, + QMK_QK_MIDI_NOTE_G_0 = 0x710A, + QMK_QK_MIDI_NOTE_G_SHARP_0 = 0x710B, + QMK_QK_MIDI_NOTE_A_0 = 0x710C, + QMK_QK_MIDI_NOTE_A_SHARP_0 = 0x710D, + QMK_QK_MIDI_NOTE_B_0 = 0x710E, + QMK_QK_MIDI_NOTE_C_1 = 0x710F, + QMK_QK_MIDI_NOTE_C_SHARP_1 = 0x7110, + QMK_QK_MIDI_NOTE_D_1 = 0x7111, + QMK_QK_MIDI_NOTE_D_SHARP_1 = 0x7112, + QMK_QK_MIDI_NOTE_E_1 = 0x7113, + QMK_QK_MIDI_NOTE_F_1 = 0x7114, + QMK_QK_MIDI_NOTE_F_SHARP_1 = 0x7115, + QMK_QK_MIDI_NOTE_G_1 = 0x7116, + QMK_QK_MIDI_NOTE_G_SHARP_1 = 0x7117, + QMK_QK_MIDI_NOTE_A_1 = 0x7118, + QMK_QK_MIDI_NOTE_A_SHARP_1 = 0x7119, + QMK_QK_MIDI_NOTE_B_1 = 0x711A, + QMK_QK_MIDI_NOTE_C_2 = 0x711B, + QMK_QK_MIDI_NOTE_C_SHARP_2 = 0x711C, + QMK_QK_MIDI_NOTE_D_2 = 0x711D, + QMK_QK_MIDI_NOTE_D_SHARP_2 = 0x711E, + QMK_QK_MIDI_NOTE_E_2 = 0x711F, + QMK_QK_MIDI_NOTE_F_2 = 0x7120, + QMK_QK_MIDI_NOTE_F_SHARP_2 = 0x7121, + QMK_QK_MIDI_NOTE_G_2 = 0x7122, + QMK_QK_MIDI_NOTE_G_SHARP_2 = 0x7123, + QMK_QK_MIDI_NOTE_A_2 = 0x7124, + QMK_QK_MIDI_NOTE_A_SHARP_2 = 0x7125, + QMK_QK_MIDI_NOTE_B_2 = 0x7126, + QMK_QK_MIDI_NOTE_C_3 = 0x7127, + QMK_QK_MIDI_NOTE_C_SHARP_3 = 0x7128, + QMK_QK_MIDI_NOTE_D_3 = 0x7129, + QMK_QK_MIDI_NOTE_D_SHARP_3 = 0x712A, + QMK_QK_MIDI_NOTE_E_3 = 0x712B, + QMK_QK_MIDI_NOTE_F_3 = 0x712C, + QMK_QK_MIDI_NOTE_F_SHARP_3 = 0x712D, + QMK_QK_MIDI_NOTE_G_3 = 0x712E, + QMK_QK_MIDI_NOTE_G_SHARP_3 = 0x712F, + QMK_QK_MIDI_NOTE_A_3 = 0x7130, + QMK_QK_MIDI_NOTE_A_SHARP_3 = 0x7131, + QMK_QK_MIDI_NOTE_B_3 = 0x7132, + QMK_QK_MIDI_NOTE_C_4 = 0x7133, + QMK_QK_MIDI_NOTE_C_SHARP_4 = 0x7134, + QMK_QK_MIDI_NOTE_D_4 = 0x7135, + QMK_QK_MIDI_NOTE_D_SHARP_4 = 0x7136, + QMK_QK_MIDI_NOTE_E_4 = 0x7137, + QMK_QK_MIDI_NOTE_F_4 = 0x7138, + QMK_QK_MIDI_NOTE_F_SHARP_4 = 0x7139, + QMK_QK_MIDI_NOTE_G_4 = 0x713A, + QMK_QK_MIDI_NOTE_G_SHARP_4 = 0x713B, + QMK_QK_MIDI_NOTE_A_4 = 0x713C, + QMK_QK_MIDI_NOTE_A_SHARP_4 = 0x713D, + QMK_QK_MIDI_NOTE_B_4 = 0x713E, + QMK_QK_MIDI_NOTE_C_5 = 0x713F, + QMK_QK_MIDI_NOTE_C_SHARP_5 = 0x7140, + QMK_QK_MIDI_NOTE_D_5 = 0x7141, + QMK_QK_MIDI_NOTE_D_SHARP_5 = 0x7142, + QMK_QK_MIDI_NOTE_E_5 = 0x7143, + QMK_QK_MIDI_NOTE_F_5 = 0x7144, + QMK_QK_MIDI_NOTE_F_SHARP_5 = 0x7145, + QMK_QK_MIDI_NOTE_G_5 = 0x7146, + QMK_QK_MIDI_NOTE_G_SHARP_5 = 0x7147, + QMK_QK_MIDI_NOTE_A_5 = 0x7148, + QMK_QK_MIDI_NOTE_A_SHARP_5 = 0x7149, + QMK_QK_MIDI_NOTE_B_5 = 0x714A, + QMK_QK_MIDI_OCTAVE_N2 = 0x714B, + QMK_QK_MIDI_OCTAVE_N1 = 0x714C, + QMK_QK_MIDI_OCTAVE_0 = 0x714D, + QMK_QK_MIDI_OCTAVE_1 = 0x714E, + QMK_QK_MIDI_OCTAVE_2 = 0x714F, + QMK_QK_MIDI_OCTAVE_3 = 0x7150, + QMK_QK_MIDI_OCTAVE_4 = 0x7151, + QMK_QK_MIDI_OCTAVE_5 = 0x7152, + QMK_QK_MIDI_OCTAVE_6 = 0x7153, + QMK_QK_MIDI_OCTAVE_7 = 0x7154, + QMK_QK_MIDI_OCTAVE_DOWN = 0x7155, + QMK_QK_MIDI_OCTAVE_UP = 0x7156, + QMK_QK_MIDI_TRANSPOSE_N6 = 0x7157, + QMK_QK_MIDI_TRANSPOSE_N5 = 0x7158, + QMK_QK_MIDI_TRANSPOSE_N4 = 0x7159, + QMK_QK_MIDI_TRANSPOSE_N3 = 0x715A, + QMK_QK_MIDI_TRANSPOSE_N2 = 0x715B, + QMK_QK_MIDI_TRANSPOSE_N1 = 0x715C, + QMK_QK_MIDI_TRANSPOSE_0 = 0x715D, + QMK_QK_MIDI_TRANSPOSE_1 = 0x715E, + QMK_QK_MIDI_TRANSPOSE_2 = 0x715F, + QMK_QK_MIDI_TRANSPOSE_3 = 0x7160, + QMK_QK_MIDI_TRANSPOSE_4 = 0x7161, + QMK_QK_MIDI_TRANSPOSE_5 = 0x7162, + QMK_QK_MIDI_TRANSPOSE_6 = 0x7163, + QMK_QK_MIDI_TRANSPOSE_DOWN = 0x7164, + QMK_QK_MIDI_TRANSPOSE_UP = 0x7165, + QMK_QK_MIDI_VELOCITY_0 = 0x7166, + QMK_QK_MIDI_VELOCITY_1 = 0x7167, + QMK_QK_MIDI_VELOCITY_2 = 0x7168, + QMK_QK_MIDI_VELOCITY_3 = 0x7169, + QMK_QK_MIDI_VELOCITY_4 = 0x716A, + QMK_QK_MIDI_VELOCITY_5 = 0x716B, + QMK_QK_MIDI_VELOCITY_6 = 0x716C, + QMK_QK_MIDI_VELOCITY_7 = 0x716D, + QMK_QK_MIDI_VELOCITY_8 = 0x716E, + QMK_QK_MIDI_VELOCITY_9 = 0x716F, + QMK_QK_MIDI_VELOCITY_10 = 0x7170, + QMK_QK_MIDI_VELOCITY_DOWN = 0x7171, + QMK_QK_MIDI_VELOCITY_UP = 0x7172, + QMK_QK_MIDI_CHANNEL_1 = 0x7173, + QMK_QK_MIDI_CHANNEL_2 = 0x7174, + QMK_QK_MIDI_CHANNEL_3 = 0x7175, + QMK_QK_MIDI_CHANNEL_4 = 0x7176, + QMK_QK_MIDI_CHANNEL_5 = 0x7177, + QMK_QK_MIDI_CHANNEL_6 = 0x7178, + QMK_QK_MIDI_CHANNEL_7 = 0x7179, + QMK_QK_MIDI_CHANNEL_8 = 0x717A, + QMK_QK_MIDI_CHANNEL_9 = 0x717B, + QMK_QK_MIDI_CHANNEL_10 = 0x717C, + QMK_QK_MIDI_CHANNEL_11 = 0x717D, + QMK_QK_MIDI_CHANNEL_12 = 0x717E, + QMK_QK_MIDI_CHANNEL_13 = 0x717F, + QMK_QK_MIDI_CHANNEL_14 = 0x7180, + QMK_QK_MIDI_CHANNEL_15 = 0x7181, + QMK_QK_MIDI_CHANNEL_16 = 0x7182, + QMK_QK_MIDI_CHANNEL_DOWN = 0x7183, + QMK_QK_MIDI_CHANNEL_UP = 0x7184, + QMK_QK_MIDI_ALL_NOTES_OFF = 0x7185, + QMK_QK_MIDI_SUSTAIN = 0x7186, + QMK_QK_MIDI_PORTAMENTO = 0x7187, + QMK_QK_MIDI_SOSTENUTO = 0x7188, + QMK_QK_MIDI_SOFT = 0x7189, + QMK_QK_MIDI_LEGATO = 0x718A, + QMK_QK_MIDI_MODULATION = 0x718B, + QMK_QK_MIDI_MODULATION_SPEED_DOWN = 0x718C, + QMK_QK_MIDI_MODULATION_SPEED_UP = 0x718D, + QMK_QK_MIDI_PITCH_BEND_DOWN = 0x718E, + QMK_QK_MIDI_PITCH_BEND_UP = 0x718F, + QMK_QK_SEQUENCER_ON = 0x7200, + QMK_QK_SEQUENCER_OFF = 0x7201, + QMK_QK_SEQUENCER_TOGGLE = 0x7202, + QMK_QK_SEQUENCER_TEMPO_DOWN = 0x7203, + QMK_QK_SEQUENCER_TEMPO_UP = 0x7204, + QMK_QK_SEQUENCER_RESOLUTION_DOWN = 0x7205, + QMK_QK_SEQUENCER_RESOLUTION_UP = 0x7206, + QMK_QK_SEQUENCER_STEPS_ALL = 0x7207, + QMK_QK_SEQUENCER_STEPS_CLEAR = 0x7208, + QMK_QK_JOYSTICK_BUTTON_0 = 0x7400, + QMK_QK_JOYSTICK_BUTTON_1 = 0x7401, + QMK_QK_JOYSTICK_BUTTON_2 = 0x7402, + QMK_QK_JOYSTICK_BUTTON_3 = 0x7403, + QMK_QK_JOYSTICK_BUTTON_4 = 0x7404, + QMK_QK_JOYSTICK_BUTTON_5 = 0x7405, + QMK_QK_JOYSTICK_BUTTON_6 = 0x7406, + QMK_QK_JOYSTICK_BUTTON_7 = 0x7407, + QMK_QK_JOYSTICK_BUTTON_8 = 0x7408, + QMK_QK_JOYSTICK_BUTTON_9 = 0x7409, + QMK_QK_JOYSTICK_BUTTON_10 = 0x740A, + QMK_QK_JOYSTICK_BUTTON_11 = 0x740B, + QMK_QK_JOYSTICK_BUTTON_12 = 0x740C, + QMK_QK_JOYSTICK_BUTTON_13 = 0x740D, + QMK_QK_JOYSTICK_BUTTON_14 = 0x740E, + QMK_QK_JOYSTICK_BUTTON_15 = 0x740F, + QMK_QK_JOYSTICK_BUTTON_16 = 0x7410, + QMK_QK_JOYSTICK_BUTTON_17 = 0x7411, + QMK_QK_JOYSTICK_BUTTON_18 = 0x7412, + QMK_QK_JOYSTICK_BUTTON_19 = 0x7413, + QMK_QK_JOYSTICK_BUTTON_20 = 0x7414, + QMK_QK_JOYSTICK_BUTTON_21 = 0x7415, + QMK_QK_JOYSTICK_BUTTON_22 = 0x7416, + QMK_QK_JOYSTICK_BUTTON_23 = 0x7417, + QMK_QK_JOYSTICK_BUTTON_24 = 0x7418, + QMK_QK_JOYSTICK_BUTTON_25 = 0x7419, + QMK_QK_JOYSTICK_BUTTON_26 = 0x741A, + QMK_QK_JOYSTICK_BUTTON_27 = 0x741B, + QMK_QK_JOYSTICK_BUTTON_28 = 0x741C, + QMK_QK_JOYSTICK_BUTTON_29 = 0x741D, + QMK_QK_JOYSTICK_BUTTON_30 = 0x741E, + QMK_QK_JOYSTICK_BUTTON_31 = 0x741F, + QMK_QK_PROGRAMMABLE_BUTTON_1 = 0x7440, + QMK_QK_PROGRAMMABLE_BUTTON_2 = 0x7441, + QMK_QK_PROGRAMMABLE_BUTTON_3 = 0x7442, + QMK_QK_PROGRAMMABLE_BUTTON_4 = 0x7443, + QMK_QK_PROGRAMMABLE_BUTTON_5 = 0x7444, + QMK_QK_PROGRAMMABLE_BUTTON_6 = 0x7445, + QMK_QK_PROGRAMMABLE_BUTTON_7 = 0x7446, + QMK_QK_PROGRAMMABLE_BUTTON_8 = 0x7447, + QMK_QK_PROGRAMMABLE_BUTTON_9 = 0x7448, + QMK_QK_PROGRAMMABLE_BUTTON_10 = 0x7449, + QMK_QK_PROGRAMMABLE_BUTTON_11 = 0x744A, + QMK_QK_PROGRAMMABLE_BUTTON_12 = 0x744B, + QMK_QK_PROGRAMMABLE_BUTTON_13 = 0x744C, + QMK_QK_PROGRAMMABLE_BUTTON_14 = 0x744D, + QMK_QK_PROGRAMMABLE_BUTTON_15 = 0x744E, + QMK_QK_PROGRAMMABLE_BUTTON_16 = 0x744F, + QMK_QK_PROGRAMMABLE_BUTTON_17 = 0x7450, + QMK_QK_PROGRAMMABLE_BUTTON_18 = 0x7451, + QMK_QK_PROGRAMMABLE_BUTTON_19 = 0x7452, + QMK_QK_PROGRAMMABLE_BUTTON_20 = 0x7453, + QMK_QK_PROGRAMMABLE_BUTTON_21 = 0x7454, + QMK_QK_PROGRAMMABLE_BUTTON_22 = 0x7455, + QMK_QK_PROGRAMMABLE_BUTTON_23 = 0x7456, + QMK_QK_PROGRAMMABLE_BUTTON_24 = 0x7457, + QMK_QK_PROGRAMMABLE_BUTTON_25 = 0x7458, + QMK_QK_PROGRAMMABLE_BUTTON_26 = 0x7459, + QMK_QK_PROGRAMMABLE_BUTTON_27 = 0x745A, + QMK_QK_PROGRAMMABLE_BUTTON_28 = 0x745B, + QMK_QK_PROGRAMMABLE_BUTTON_29 = 0x745C, + QMK_QK_PROGRAMMABLE_BUTTON_30 = 0x745D, + QMK_QK_PROGRAMMABLE_BUTTON_31 = 0x745E, + QMK_QK_PROGRAMMABLE_BUTTON_32 = 0x745F, + QMK_QK_AUDIO_ON = 0x7480, + QMK_QK_AUDIO_OFF = 0x7481, + QMK_QK_AUDIO_TOGGLE = 0x7482, + QMK_QK_AUDIO_CLICKY_TOGGLE = 0x748A, + QMK_QK_AUDIO_CLICKY_ON = 0x748B, + QMK_QK_AUDIO_CLICKY_OFF = 0x748C, + QMK_QK_AUDIO_CLICKY_UP = 0x748D, + QMK_QK_AUDIO_CLICKY_DOWN = 0x748E, + QMK_QK_AUDIO_CLICKY_RESET = 0x748F, + QMK_QK_MUSIC_ON = 0x7490, + QMK_QK_MUSIC_OFF = 0x7491, + QMK_QK_MUSIC_TOGGLE = 0x7492, + QMK_QK_MUSIC_MODE_NEXT = 0x7493, + QMK_QK_AUDIO_VOICE_NEXT = 0x7494, + QMK_QK_AUDIO_VOICE_PREVIOUS = 0x7495, + QMK_QK_STENO_BOLT = 0x74F0, + QMK_QK_STENO_GEMINI = 0x74F1, + QMK_QK_STENO_COMB = 0x74F2, + QMK_QK_STENO_COMB_MAX = 0x74FC, + QMK_QK_MACRO_0 = 0x7700, + QMK_QK_MACRO_1 = 0x7701, + QMK_QK_MACRO_2 = 0x7702, + QMK_QK_MACRO_3 = 0x7703, + QMK_QK_MACRO_4 = 0x7704, + QMK_QK_MACRO_5 = 0x7705, + QMK_QK_MACRO_6 = 0x7706, + QMK_QK_MACRO_7 = 0x7707, + QMK_QK_MACRO_8 = 0x7708, + QMK_QK_MACRO_9 = 0x7709, + QMK_QK_MACRO_10 = 0x770A, + QMK_QK_MACRO_11 = 0x770B, + QMK_QK_MACRO_12 = 0x770C, + QMK_QK_MACRO_13 = 0x770D, + QMK_QK_MACRO_14 = 0x770E, + QMK_QK_MACRO_15 = 0x770F, + QMK_QK_MACRO_16 = 0x7710, + QMK_QK_MACRO_17 = 0x7711, + QMK_QK_MACRO_18 = 0x7712, + QMK_QK_MACRO_19 = 0x7713, + QMK_QK_MACRO_20 = 0x7714, + QMK_QK_MACRO_21 = 0x7715, + QMK_QK_MACRO_22 = 0x7716, + QMK_QK_MACRO_23 = 0x7717, + QMK_QK_MACRO_24 = 0x7718, + QMK_QK_MACRO_25 = 0x7719, + QMK_QK_MACRO_26 = 0x771A, + QMK_QK_MACRO_27 = 0x771B, + QMK_QK_MACRO_28 = 0x771C, + QMK_QK_MACRO_29 = 0x771D, + QMK_QK_MACRO_30 = 0x771E, + QMK_QK_MACRO_31 = 0x771F, + QMK_QK_OUTPUT_AUTO = 0x7780, + QMK_QK_OUTPUT_NEXT = 0x7781, + QMK_QK_OUTPUT_PREV = 0x7782, + QMK_QK_OUTPUT_NONE = 0x7783, + QMK_QK_OUTPUT_USB = 0x7784, + QMK_QK_OUTPUT_2P4GHZ = 0x7785, + QMK_QK_OUTPUT_BLUETOOTH = 0x7786, + QMK_QK_BLUETOOTH_PROFILE_NEXT = 0x7790, + QMK_QK_BLUETOOTH_PROFILE_PREV = 0x7791, + QMK_QK_BLUETOOTH_UNPAIR = 0x7792, + QMK_QK_BLUETOOTH_PROFILE1 = 0x7793, + QMK_QK_BLUETOOTH_PROFILE2 = 0x7794, + QMK_QK_BLUETOOTH_PROFILE3 = 0x7795, + QMK_QK_BLUETOOTH_PROFILE4 = 0x7796, + QMK_QK_BLUETOOTH_PROFILE5 = 0x7797, + QMK_QK_BACKLIGHT_ON = 0x7800, + QMK_QK_BACKLIGHT_OFF = 0x7801, + QMK_QK_BACKLIGHT_TOGGLE = 0x7802, + QMK_QK_BACKLIGHT_DOWN = 0x7803, + QMK_QK_BACKLIGHT_UP = 0x7804, + QMK_QK_BACKLIGHT_STEP = 0x7805, + QMK_QK_BACKLIGHT_TOGGLE_BREATHING = 0x7806, + QMK_QK_LED_MATRIX_ON = 0x7810, + QMK_QK_LED_MATRIX_OFF = 0x7811, + QMK_QK_LED_MATRIX_TOGGLE = 0x7812, + QMK_QK_LED_MATRIX_MODE_NEXT = 0x7813, + QMK_QK_LED_MATRIX_MODE_PREVIOUS = 0x7814, + QMK_QK_LED_MATRIX_BRIGHTNESS_UP = 0x7815, + QMK_QK_LED_MATRIX_BRIGHTNESS_DOWN = 0x7816, + QMK_QK_LED_MATRIX_SPEED_UP = 0x7817, + QMK_QK_LED_MATRIX_SPEED_DOWN = 0x7818, + QMK_QK_UNDERGLOW_TOGGLE = 0x7820, + QMK_QK_UNDERGLOW_MODE_NEXT = 0x7821, + QMK_QK_UNDERGLOW_MODE_PREVIOUS = 0x7822, + QMK_QK_UNDERGLOW_HUE_UP = 0x7823, + QMK_QK_UNDERGLOW_HUE_DOWN = 0x7824, + QMK_QK_UNDERGLOW_SATURATION_UP = 0x7825, + QMK_QK_UNDERGLOW_SATURATION_DOWN = 0x7826, + QMK_QK_UNDERGLOW_VALUE_UP = 0x7827, + QMK_QK_UNDERGLOW_VALUE_DOWN = 0x7828, + QMK_QK_UNDERGLOW_SPEED_UP = 0x7829, + QMK_QK_UNDERGLOW_SPEED_DOWN = 0x782A, + QMK_RGB_MODE_PLAIN = 0x782B, + QMK_RGB_MODE_BREATHE = 0x782C, + QMK_RGB_MODE_RAINBOW = 0x782D, + QMK_RGB_MODE_SWIRL = 0x782E, + QMK_RGB_MODE_SNAKE = 0x782F, + QMK_RGB_MODE_KNIGHT = 0x7830, + QMK_RGB_MODE_XMAS = 0x7831, + QMK_RGB_MODE_GRADIENT = 0x7832, + QMK_RGB_MODE_RGBTEST = 0x7833, + QMK_RGB_MODE_TWINKLE = 0x7834, + QMK_QK_RGB_MATRIX_ON = 0x7840, + QMK_QK_RGB_MATRIX_OFF = 0x7841, + QMK_QK_RGB_MATRIX_TOGGLE = 0x7842, + QMK_QK_RGB_MATRIX_MODE_NEXT = 0x7843, + QMK_QK_RGB_MATRIX_MODE_PREVIOUS = 0x7844, + QMK_QK_RGB_MATRIX_HUE_UP = 0x7845, + QMK_QK_RGB_MATRIX_HUE_DOWN = 0x7846, + QMK_QK_RGB_MATRIX_SATURATION_UP = 0x7847, + QMK_QK_RGB_MATRIX_SATURATION_DOWN = 0x7848, + QMK_QK_RGB_MATRIX_VALUE_UP = 0x7849, + QMK_QK_RGB_MATRIX_VALUE_DOWN = 0x784A, + QMK_QK_RGB_MATRIX_SPEED_UP = 0x784B, + QMK_QK_RGB_MATRIX_SPEED_DOWN = 0x784C, + QMK_QK_BOOTLOADER = 0x7C00, + QMK_QK_REBOOT = 0x7C01, + QMK_QK_DEBUG_TOGGLE = 0x7C02, + QMK_QK_CLEAR_EEPROM = 0x7C03, + QMK_QK_MAKE = 0x7C04, + QMK_QK_AUTO_SHIFT_DOWN = 0x7C10, + QMK_QK_AUTO_SHIFT_UP = 0x7C11, + QMK_QK_AUTO_SHIFT_REPORT = 0x7C12, + QMK_QK_AUTO_SHIFT_ON = 0x7C13, + QMK_QK_AUTO_SHIFT_OFF = 0x7C14, + QMK_QK_AUTO_SHIFT_TOGGLE = 0x7C15, + QMK_QK_GRAVE_ESCAPE = 0x7C16, + QMK_QK_VELOCIKEY_TOGGLE = 0x7C17, + QMK_QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN = 0x7C18, + QMK_QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE = 0x7C19, + QMK_QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN = 0x7C1A, + QMK_QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE = 0x7C1B, + QMK_QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN = 0x7C1C, + QMK_QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE = 0x7C1D, + QMK_QK_SPACE_CADET_RIGHT_SHIFT_ENTER = 0x7C1E, + QMK_QK_UNICODE_MODE_NEXT = 0x7C30, + QMK_QK_UNICODE_MODE_PREVIOUS = 0x7C31, + QMK_QK_UNICODE_MODE_MACOS = 0x7C32, + QMK_QK_UNICODE_MODE_LINUX = 0x7C33, + QMK_QK_UNICODE_MODE_WINDOWS = 0x7C34, + QMK_QK_UNICODE_MODE_BSD = 0x7C35, + QMK_QK_UNICODE_MODE_WINCOMPOSE = 0x7C36, + QMK_QK_UNICODE_MODE_EMACS = 0x7C37, + QMK_QK_HAPTIC_ON = 0x7C40, + QMK_QK_HAPTIC_OFF = 0x7C41, + QMK_QK_HAPTIC_TOGGLE = 0x7C42, + QMK_QK_HAPTIC_RESET = 0x7C43, + QMK_QK_HAPTIC_FEEDBACK_TOGGLE = 0x7C44, + QMK_QK_HAPTIC_BUZZ_TOGGLE = 0x7C45, + QMK_QK_HAPTIC_MODE_NEXT = 0x7C46, + QMK_QK_HAPTIC_MODE_PREVIOUS = 0x7C47, + QMK_QK_HAPTIC_CONTINUOUS_TOGGLE = 0x7C48, + QMK_QK_HAPTIC_CONTINUOUS_UP = 0x7C49, + QMK_QK_HAPTIC_CONTINUOUS_DOWN = 0x7C4A, + QMK_QK_HAPTIC_DWELL_UP = 0x7C4B, + QMK_QK_HAPTIC_DWELL_DOWN = 0x7C4C, + QMK_QK_COMBO_ON = 0x7C50, + QMK_QK_COMBO_OFF = 0x7C51, + QMK_QK_COMBO_TOGGLE = 0x7C52, + QMK_QK_DYNAMIC_MACRO_RECORD_START_1 = 0x7C53, + QMK_QK_DYNAMIC_MACRO_RECORD_START_2 = 0x7C54, + QMK_QK_DYNAMIC_MACRO_RECORD_STOP = 0x7C55, + QMK_QK_DYNAMIC_MACRO_PLAY_1 = 0x7C56, + QMK_QK_DYNAMIC_MACRO_PLAY_2 = 0x7C57, + QMK_QK_LEADER = 0x7C58, + QMK_QK_LOCK = 0x7C59, + QMK_QK_ONE_SHOT_ON = 0x7C5A, + QMK_QK_ONE_SHOT_OFF = 0x7C5B, + QMK_QK_ONE_SHOT_TOGGLE = 0x7C5C, + QMK_QK_KEY_OVERRIDE_TOGGLE = 0x7C5D, + QMK_QK_KEY_OVERRIDE_ON = 0x7C5E, + QMK_QK_KEY_OVERRIDE_OFF = 0x7C5F, + QMK_QK_SECURE_LOCK = 0x7C60, + QMK_QK_SECURE_UNLOCK = 0x7C61, + QMK_QK_SECURE_TOGGLE = 0x7C62, + QMK_QK_SECURE_REQUEST = 0x7C63, + QMK_QK_DYNAMIC_TAPPING_TERM_PRINT = 0x7C70, + QMK_QK_DYNAMIC_TAPPING_TERM_UP = 0x7C71, + QMK_QK_DYNAMIC_TAPPING_TERM_DOWN = 0x7C72, + QMK_QK_CAPS_WORD_TOGGLE = 0x7C73, + QMK_QK_AUTOCORRECT_ON = 0x7C74, + QMK_QK_AUTOCORRECT_OFF = 0x7C75, + QMK_QK_AUTOCORRECT_TOGGLE = 0x7C76, + QMK_QK_TRI_LAYER_LOWER = 0x7C77, + QMK_QK_TRI_LAYER_UPPER = 0x7C78, + QMK_QK_REPEAT_KEY = 0x7C79, + QMK_QK_ALT_REPEAT_KEY = 0x7C7A, + QMK_QK_LAYER_LOCK = 0x7C7B, + QMK_QK_KB_0 = 0x7E00, + QMK_QK_KB_1 = 0x7E01, + QMK_QK_KB_2 = 0x7E02, + QMK_QK_KB_3 = 0x7E03, + QMK_QK_KB_4 = 0x7E04, + QMK_QK_KB_5 = 0x7E05, + QMK_QK_KB_6 = 0x7E06, + QMK_QK_KB_7 = 0x7E07, + QMK_QK_KB_8 = 0x7E08, + QMK_QK_KB_9 = 0x7E09, + QMK_QK_KB_10 = 0x7E0A, + QMK_QK_KB_11 = 0x7E0B, + QMK_QK_KB_12 = 0x7E0C, + QMK_QK_KB_13 = 0x7E0D, + QMK_QK_KB_14 = 0x7E0E, + QMK_QK_KB_15 = 0x7E0F, + QMK_QK_KB_16 = 0x7E10, + QMK_QK_KB_17 = 0x7E11, + QMK_QK_KB_18 = 0x7E12, + QMK_QK_KB_19 = 0x7E13, + QMK_QK_KB_20 = 0x7E14, + QMK_QK_KB_21 = 0x7E15, + QMK_QK_KB_22 = 0x7E16, + QMK_QK_KB_23 = 0x7E17, + QMK_QK_KB_24 = 0x7E18, + QMK_QK_KB_25 = 0x7E19, + QMK_QK_KB_26 = 0x7E1A, + QMK_QK_KB_27 = 0x7E1B, + QMK_QK_KB_28 = 0x7E1C, + QMK_QK_KB_29 = 0x7E1D, + QMK_QK_KB_30 = 0x7E1E, + QMK_QK_KB_31 = 0x7E1F, + QMK_QK_USER_0 = 0x7E40, + QMK_QK_USER_1 = 0x7E41, + QMK_QK_USER_2 = 0x7E42, + QMK_QK_USER_3 = 0x7E43, + QMK_QK_USER_4 = 0x7E44, + QMK_QK_USER_5 = 0x7E45, + QMK_QK_USER_6 = 0x7E46, + QMK_QK_USER_7 = 0x7E47, + QMK_QK_USER_8 = 0x7E48, + QMK_QK_USER_9 = 0x7E49, + QMK_QK_USER_10 = 0x7E4A, + QMK_QK_USER_11 = 0x7E4B, + QMK_QK_USER_12 = 0x7E4C, + QMK_QK_USER_13 = 0x7E4D, + QMK_QK_USER_14 = 0x7E4E, + QMK_QK_USER_15 = 0x7E4F, + QMK_QK_USER_16 = 0x7E50, + QMK_QK_USER_17 = 0x7E51, + QMK_QK_USER_18 = 0x7E52, + QMK_QK_USER_19 = 0x7E53, + QMK_QK_USER_20 = 0x7E54, + QMK_QK_USER_21 = 0x7E55, + QMK_QK_USER_22 = 0x7E56, + QMK_QK_USER_23 = 0x7E57, + QMK_QK_USER_24 = 0x7E58, + QMK_QK_USER_25 = 0x7E59, + QMK_QK_USER_26 = 0x7E5A, + QMK_QK_USER_27 = 0x7E5B, + QMK_QK_USER_28 = 0x7E5C, + QMK_QK_USER_29 = 0x7E5D, + QMK_QK_USER_30 = 0x7E5E, + QMK_QK_USER_31 = 0x7E5F, +}; + +extern std::map qmk_keynames; diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBBaseController.cpp b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBBaseController.cpp new file mode 100644 index 0000000..6bd4e7f --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBBaseController.cpp @@ -0,0 +1,292 @@ +/*---------------------------------------------------------*\ +| QMKOpenRGBBaseController.cpp | +| | +| Common Driver for OpenRGB QMK Keyboard Protocol | +| | +| ChrisM 20 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "RGBControllerKeyNames.h" +#include "SettingsManager.h" +#include "QMKOpenRGBBaseController.h" + +using namespace std::chrono_literals; + +QMKOpenRGBBaseController::QMKOpenRGBBaseController(hid_device *dev_handle, const char *path, unsigned char max_led_count) +{ + /*-------------------------------------------------*\ + | Get QMKOpenRGB settings | + \*-------------------------------------------------*/ + json qmk_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("QMKOpenRGBDevices"); + if(qmk_settings.contains("leds_per_update")) + { + if(qmk_settings["leds_per_update"] > max_led_count) + { + qmk_settings["leds_per_update"] = max_led_count; + } + else if(qmk_settings["leds_per_update"] < 1) + { + qmk_settings["leds_per_update"] = 1; + } + SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager(); + settings_manager->SetSettings("QMKOpenRGBDevices", qmk_settings); + settings_manager->SaveSettings(); + leds_per_update = qmk_settings["leds_per_update"]; + } + else + { + leds_per_update = max_led_count; + } + + if(qmk_settings.contains("delay")) + { + delay = (unsigned int)qmk_settings["delay"] * 1ms; + } + else + { + delay = 0ms; + } + + dev = dev_handle; + location = path; + + GetDeviceInfo(); + GetModeInfo(); +} + +QMKOpenRGBBaseController::~QMKOpenRGBBaseController() +{ + hid_close(dev); +} + +std::string QMKOpenRGBBaseController::GetLocation() +{ + return("HID: " + location); +} + +std::string QMKOpenRGBBaseController::GetDeviceName() +{ + return device_name; +} + +std::string QMKOpenRGBBaseController::GetDeviceVendor() +{ + return device_vendor; +} + +unsigned int QMKOpenRGBBaseController::GetTotalNumberOfLEDs() +{ + return total_number_of_leds; +} + +unsigned int QMKOpenRGBBaseController::GetTotalNumberOfLEDsWithEmptySpace() +{ + return total_number_of_leds_with_empty_space; +} + +unsigned int QMKOpenRGBBaseController::GetMode() +{ + return mode; +} + +unsigned int QMKOpenRGBBaseController::GetModeSpeed() +{ + return mode_speed; +} + +unsigned int QMKOpenRGBBaseController::GetModeColor() +{ + return mode_color; +} + +std::vector QMKOpenRGBBaseController::GetLEDPoints() +{ + return led_points; +} + +std::vector QMKOpenRGBBaseController::GetLEDFlags() +{ + return led_flags; +} + +std::vector QMKOpenRGBBaseController::GetLEDNames() +{ + return led_names; +} + +std::vector QMKOpenRGBBaseController::GetLEDColors() +{ + return led_colors; +} + +unsigned int QMKOpenRGBBaseController::GetProtocolVersion() +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_PROTOCOL_VERSION; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + return usb_buf[1]; +} + +std::string QMKOpenRGBBaseController::GetQMKVersion() +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_QMK_VERSION; + + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + hid_read(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + + std::string qmk_version; + int i = 1; + while (usb_buf[i] != 0) + { + qmk_version.push_back(usb_buf[i]); + i++; + } + + return qmk_version; +} + +void QMKOpenRGBBaseController::GetDeviceInfo() +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_DEVICE_INFO; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + total_number_of_leds = usb_buf[QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_BYTE]; + total_number_of_leds_with_empty_space = usb_buf[QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE]; + + int i = QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE + 1; + while (usb_buf[i] != 0) + { + device_name.push_back(usb_buf[i]); + i++; + } + + i++; + while (usb_buf[i] != 0) + { + device_vendor.push_back(usb_buf[i]); + i++; + } +} + +void QMKOpenRGBBaseController::GetModeInfo() +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_MODE_INFO; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, 65); + bytes_read = hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + mode = usb_buf[QMK_OPENRGB_MODE_BYTE]; + mode_speed = usb_buf[QMK_OPENRGB_SPEED_BYTE]; + + /*-----------------------------------------------------*\ + | QMK hue range is between 0-255 so hue needs to be | + | converted | + \*-----------------------------------------------------*/ + unsigned int oldRange = 255; + unsigned int newRange = 359; + unsigned int convertedHue = (usb_buf[QMK_OPENRGB_HUE_BYTE] * newRange / oldRange); + + hsv_t hsv; + hsv.hue = convertedHue; + hsv.saturation = usb_buf[QMK_OPENRGB_SATURATION_BYTE]; + hsv.value = usb_buf[QMK_OPENRGB_VALUE_BYTE]; + + mode_color = hsv2rgb(&hsv); +} + +void QMKOpenRGBBaseController::SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed) +{ + SetMode(hsv_color, mode, speed, false); +} + +void QMKOpenRGBBaseController::SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed, bool save) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_SET_MODE; + usb_buf[0x02] = hsv_color.hue * 255 / 359; + usb_buf[0x03] = hsv_color.saturation; + usb_buf[0x04] = hsv_color.value; + usb_buf[0x05] = mode; + usb_buf[0x06] = speed; + usb_buf[0x07] = save; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT); +} diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBBaseController.h b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBBaseController.h new file mode 100644 index 0000000..797bdb3 --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBBaseController.h @@ -0,0 +1,78 @@ +/*---------------------------------------------------------*\ +| QMKOpenRGBBaseController.h | +| | +| Common Driver for OpenRGB QMK Keyboard Protocol | +| | +| ChrisM 20 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "LogManager.h" +#include "RGBController.h" +#include "RGBControllerKeyNames.h" +#include "SettingsManager.h" +#include "QMKOpenRGBController.h" + +class QMKOpenRGBBaseController +{ +public: + QMKOpenRGBBaseController(hid_device *dev_handle, const char *path, unsigned char max_led_count); + virtual ~QMKOpenRGBBaseController(); + + std::string GetLocation(); + std::string GetDeviceName(); + std::string GetDeviceVendor(); + + unsigned int GetTotalNumberOfLEDs(); + unsigned int GetTotalNumberOfLEDsWithEmptySpace(); + unsigned int GetMode(); + unsigned int GetModeSpeed(); + unsigned int GetModeColor(); + + std::vector GetLEDPoints(); + std::vector GetLEDFlags(); + std::vector GetLEDNames(); + std::vector GetLEDColors(); + + unsigned int GetProtocolVersion(); + std::string GetQMKVersion(); + void GetDeviceInfo(); + void GetModeInfo(); + + void SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed); + void SetMode(hsv_t hsv_color, unsigned char mode, unsigned char speed, bool save); + + virtual void GetLEDInfo(unsigned int leds_count) = 0; + virtual void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) = 0; + virtual void DirectModeSetLEDs(std::vector colors, unsigned int leds_count) = 0; + +protected: + hid_device *dev; + + unsigned int leds_per_update; + + std::string location; + + std::string device_name; + std::string device_vendor; + + std::chrono::milliseconds delay; + + unsigned int total_number_of_leds; + unsigned int total_number_of_leds_with_empty_space; + unsigned int mode; + unsigned int mode_speed; + + RGBColor mode_color; + + std::vector led_points; + std::vector led_flags; + std::vector led_names; + std::vector led_colors; + +private: +}; diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBController.h b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBController.h new file mode 100644 index 0000000..60bd4f2 --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBController.h @@ -0,0 +1,133 @@ +/*---------------------------------------------------------*\ +| QMKOpenRGBController.h | +| | +| Driver for OpenRGB QMK Keyboard Protocol | +| | +| Kasper 10 Oct 2020 | +| Jath03 28 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "ResourceManager.h" +#include "RGBController.h" +#include "hsv.h" + +#define QMK_OPENRGB_PACKET_SIZE 65 +#define QMK_OPENRGB_HID_READ_TIMEOUT 50 + +enum CommandsId +{ + QMK_OPENRGB_GET_PROTOCOL_VERSION = 1, + QMK_OPENRGB_GET_QMK_VERSION, + QMK_OPENRGB_GET_DEVICE_INFO, + QMK_OPENRGB_GET_MODE_INFO, + QMK_OPENRGB_GET_LED_INFO, + QMK_OPENRGB_GET_IS_MODE_ENABLED, + QMK_OPENRGB_GET_ENABLED_MODES = QMK_OPENRGB_GET_IS_MODE_ENABLED, + + QMK_OPENRGB_SET_MODE, + QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED, + QMK_OPENRGB_DIRECT_MODE_SET_LEDS, +}; + +enum Modes +{ + QMK_OPENRGB_MODE_OPENRGB_DIRECT = 1, + QMK_OPENRGB_MODE_SOLID_COLOR, + QMK_OPENRGB_MODE_ALPHA_MOD, + QMK_OPENRGB_MODE_GRADIENT_UP_DOWN, + QMK_OPENRGB_MODE_GRADIENT_LEFT_RIGHT, + QMK_OPENRGB_MODE_BREATHING, + QMK_OPENRGB_MODE_BAND_SAT, + QMK_OPENRGB_MODE_BAND_VAL, + QMK_OPENRGB_MODE_BAND_PINWHEEL_SAT, + QMK_OPENRGB_MODE_BAND_PINWHEEL_VAL, + QMK_OPENRGB_MODE_BAND_SPIRAL_SAT, + QMK_OPENRGB_MODE_BAND_SPIRAL_VAL, + QMK_OPENRGB_MODE_CYCLE_ALL, + QMK_OPENRGB_MODE_CYCLE_LEFT_RIGHT, + QMK_OPENRGB_MODE_CYCLE_UP_DOWN, + QMK_OPENRGB_MODE_CYCLE_OUT_IN, + QMK_OPENRGB_MODE_CYCLE_OUT_IN_DUAL, + QMK_OPENRGB_MODE_RAINBOW_MOVING_CHEVRON, + QMK_OPENRGB_MODE_CYCLE_PINWHEEL, + QMK_OPENRGB_MODE_CYCLE_SPIRAL, + QMK_OPENRGB_MODE_DUAL_BEACON, + QMK_OPENRGB_MODE_RAINBOW_BEACON, + QMK_OPENRGB_MODE_RAINBOW_PINWHEELS, + QMK_OPENRGB_MODE_RAINDROPS, + QMK_OPENRGB_MODE_JELLYBEAN_RAINDROPS, + QMK_OPENRGB_MODE_HUE_BREATHING, + QMK_OPENRGB_MODE_HUE_PENDULUM, + QMK_OPENRGB_MODE_HUE_WAVE, + QMK_OPENRGB_MODE_TYPING_HEATMAP, + QMK_OPENRGB_MODE_DIGITAL_RAIN, + QMK_OPENRGB_MODE_SOLID_REACTIVE_SIMPLE, + QMK_OPENRGB_MODE_SOLID_REACTIVE, + QMK_OPENRGB_MODE_SOLID_REACTIVE_WIDE, + QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTIWIDE, + QMK_OPENRGB_MODE_SOLID_REACTIVE_CROSS, + QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTICROSS, + QMK_OPENRGB_MODE_SOLID_REACTIVE_NEXUS, + QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTINEXUS, + QMK_OPENRGB_MODE_SPLASH, + QMK_OPENRGB_MODE_MULTISPLASH, + QMK_OPENRGB_MODE_SOLID_SPLASH, + QMK_OPENRGB_MODE_SOLID_MULTISPLASH, + QMK_OPENRGB_MODE_PIXEL_RAIN, + QMK_OPENRGB_MODE_PIXEL_FLOW, + QMK_OPENRGB_MODE_PIXEL_FRACTAL, +}; + +enum SpeedCommands +{ + QMK_OPENRGB_SPEED_SLOWEST = 0x00, /* Slowest speed */ + QMK_OPENRGB_SPEED_NORMAL = 0x7F, /* Normal speed */ + QMK_OPENRGB_SPEED_FASTEST = 0xFF, /* Fastest speed */ +}; + +enum +{ + QMK_OPENRGB_FAILURE = 25, /* Failure status code */ + QMK_OPENRGB_SUCCESS = 50, /* Success status code */ + QMK_OPENRGB_END_OF_MESSAGE = 100, /* End of Message status code */ +}; + +enum +{ + QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_BYTE = 1, + QMK_OPENRGB_TOTAL_NUMBER_OF_LEDS_WITH_EMPTY_SPACE_BYTE = 2 +}; + +enum +{ + QMK_OPENRGB_MODE_BYTE = 1, + QMK_OPENRGB_SPEED_BYTE = 2, + QMK_OPENRGB_HUE_BYTE = 3, + QMK_OPENRGB_SATURATION_BYTE = 4, + QMK_OPENRGB_VALUE_BYTE = 5, +}; + +enum +{ + QMK_OPENRGB_POINT_X_BYTE = 1, + QMK_OPENRGB_POINT_Y_BYTE = 2, + QMK_OPENRGB_FLAG_BYTE = 3, + QMK_OPENRGB_R_COLOR_BYTE = 4, + QMK_OPENRGB_G_COLOR_BYTE = 5, + QMK_OPENRGB_B_COLOR_BYTE = 6, + QMK_OPENRGB_KEYCODE_BYTE = 7 +}; + +typedef struct +{ + uint8_t x; + uint8_t y; +} point_t; diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBControllerDetect.cpp b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBControllerDetect.cpp new file mode 100644 index 0000000..64f5b3b --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBControllerDetect.cpp @@ -0,0 +1,167 @@ +/*---------------------------------------------------------*\ +| QMKOpenRGBControllerDetect.cpp | +| | +| Detector for OpenRGB QMK Keyboard Protocol | +| | +| Kasper 10 Oct 2020 | +| Jath03 28 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "QMKOpenRGBRev9Controller.h" +#include "QMKOpenRGBRevBController.h" +#include "QMKOpenRGBRevDController.h" +#include "RGBController_QMKOpenRGBRev9.h" +#include "RGBController_QMKOpenRGBRevB.h" +#include "RGBController_QMKOpenRGBRevD.h" +#include "RGBController_QMKOpenRGBRevE.h" +#include "LogManager.h" +#include "SettingsManager.h" + +/*-----------------------------------------------------*\ +| Protocol version | +\*-----------------------------------------------------*/ +#define QMK_OPENRGB_PROTOCOL_VERSION_9 0x09 +#define QMK_OPENRGB_PROTOCOL_VERSION_B 0x0B +#define QMK_OPENRGB_PROTOCOL_VERSION_C 0x0C +#define QMK_OPENRGB_PROTOCOL_VERSION_D 0x0D +#define QMK_OPENRGB_PROTOCOL_VERSION_E 0x0E + +/*-----------------------------------------------------*\ +| Usage and Usage Page | +\*-----------------------------------------------------*/ +#define QMK_USAGE_PAGE 0xFF60 +#define QMK_USAGE 0x61 + +unsigned int GetProtocolVersion(hid_device *dev) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_PROTOCOL_VERSION; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + return usb_buf[1]; +} + +void DetectQMKOpenRGBControllers(hid_device_info *info, const std::string&) +{ + hid_device *dev = hid_open_path(info->path); + + if(dev) + { + /*-----------------------------------------------------*\ + | Use Rev9 controller for getting protocol version. | + | Protocol version request may not change across | + | protocol versions | + \*-----------------------------------------------------*/ + unsigned int version = GetProtocolVersion(dev); + + switch(version) + { + case QMK_OPENRGB_PROTOCOL_VERSION_9: + { + QMKOpenRGBRev9Controller* controller = new QMKOpenRGBRev9Controller(dev, info->path); + RGBController_QMKOpenRGBRev9* rgb_controller = new RGBController_QMKOpenRGBRev9(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + case QMK_OPENRGB_PROTOCOL_VERSION_B: + { + QMKOpenRGBRevBController* controller = new QMKOpenRGBRevBController(dev, info->path); + RGBController_QMKOpenRGBRevB* rgb_controller = new RGBController_QMKOpenRGBRevB(controller, false); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + case QMK_OPENRGB_PROTOCOL_VERSION_C: + { + QMKOpenRGBRevBController* controller = new QMKOpenRGBRevBController(dev, info->path); + RGBController_QMKOpenRGBRevB* rgb_controller = new RGBController_QMKOpenRGBRevB(controller, true); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + case QMK_OPENRGB_PROTOCOL_VERSION_D: + { + QMKOpenRGBRevDController* controller = new QMKOpenRGBRevDController(dev, info->path); + RGBController_QMKOpenRGBRevD* rgb_controller = new RGBController_QMKOpenRGBRevD(controller, true); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + case QMK_OPENRGB_PROTOCOL_VERSION_E: + { + QMKOpenRGBRevDController* controller = new QMKOpenRGBRevDController(dev, info->path); + RGBController_QMKOpenRGBRevE* rgb_controller = new RGBController_QMKOpenRGBRevE(controller, true); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + break; + default: + if (version == 0) + { + LOG_WARNING("[QMK OpenRGB] Detection failed - the detected keyboard does not have the OpenRGB protocol feature enabled! \n" + "Please make sure your keyboard supports RGB Matrix, add OPENRGB_ENABLE = yes to the rules.mk inside your keymap folder, compile and flash again!"); + } + else if (version < QMK_OPENRGB_PROTOCOL_VERSION_9) + { + LOG_WARNING("[QMK OpenRGB] Detection failed - the detected keyboard is using an outdated protocol version %i. Please update to to the update to the latest version of QMK-OpenRGB! \n" + "For officaly supported QMK boards grab url \n" + "For Sonix boards grab url", version); + } + else if (version > QMK_OPENRGB_PROTOCOL_VERSION_E) + { + LOG_WARNING("[QMK OpenRGB] Detection failed - the detected keyboard is using version protocol %i which is not supported by this OpenRGB build. Please update OpenRGB!", version); + } + } + } +} + +void RegisterQMKDetectors() +{ + /*-------------------------------------------------*\ + | Get QMKOpenRGB settings | + \*-------------------------------------------------*/ + json qmk_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("QMKOpenRGBDevices"); + + if(qmk_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < qmk_settings["devices"].size(); device_idx++) + { + if( qmk_settings["devices"][device_idx].contains("usb_pid") + && qmk_settings["devices"][device_idx].contains("usb_vid") + && qmk_settings["devices"][device_idx].contains("name")) + { + std::string usb_pid_str = qmk_settings["devices"][device_idx]["usb_pid"]; + std::string usb_vid_str = qmk_settings["devices"][device_idx]["usb_vid"]; + std::string name = qmk_settings["devices"][device_idx]["name"]; + + /*-------------------------------------*\ + | Parse hex string to integer | + \*-------------------------------------*/ + unsigned short usb_pid = std::stoi(usb_pid_str, 0, 16); + unsigned short usb_vid = std::stoi(usb_vid_str, 0, 16); + + REGISTER_DYNAMIC_HID_DETECTOR_IPU(name, DetectQMKOpenRGBControllers, usb_vid, usb_pid, 1, QMK_USAGE_PAGE, QMK_USAGE); + } + } + } +} + +REGISTER_DYNAMIC_DETECTOR("QMK OpenRGB Devices", RegisterQMKDetectors); diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/QMKOpenRGBRev9Controller.cpp b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/QMKOpenRGBRev9Controller.cpp new file mode 100644 index 0000000..4256c6a --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/QMKOpenRGBRev9Controller.cpp @@ -0,0 +1,170 @@ +/*---------------------------------------------------------*\ +| QMKOpenRGBRev9Controller.cpp | +| | +| Driver for OpenRGB QMK Keyboard Protocol Revision 9 | +| Revision 9 was initially supported by OpenRGB 0.6 | +| | +| Kasper 10 Oct 2020 | +| Jath03 28 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "QMKKeycodes.h" +#include "QMKOpenRGBRev9Controller.h" + +using namespace std::chrono_literals; + +QMKOpenRGBRev9Controller::QMKOpenRGBRev9Controller(hid_device *dev_handle, const char *path) : + QMKOpenRGBBaseController(dev_handle, path, 20) +{ +} + +QMKOpenRGBRev9Controller::~QMKOpenRGBRev9Controller() +{ +} + +void QMKOpenRGBRev9Controller::GetLEDInfo(unsigned int led) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_LED_INFO; + usb_buf[0x02] = led; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + if(usb_buf[62] != QMK_OPENRGB_FAILURE) + { + led_points.push_back(point_t{usb_buf[QMK_OPENRGB_POINT_X_BYTE], usb_buf[QMK_OPENRGB_POINT_Y_BYTE]}); + led_flags.push_back(usb_buf[QMK_OPENRGB_FLAG_BYTE]); + led_colors.push_back(ToRGBColor(usb_buf[QMK_OPENRGB_R_COLOR_BYTE], usb_buf[QMK_OPENRGB_G_COLOR_BYTE], usb_buf[QMK_OPENRGB_B_COLOR_BYTE])); + } + + if(usb_buf[QMK_OPENRGB_KEYCODE_BYTE] != 0) + { + if(qmk_keynames.count(usb_buf[QMK_OPENRGB_KEYCODE_BYTE]) > 0) + { + led_names.push_back(qmk_keynames[usb_buf[QMK_OPENRGB_KEYCODE_BYTE]]); + } + else + { + LOG_DEBUG("[%s] Key code: %d (%02X) @ offset %d was not found in the QMK keyname map", + device_name.c_str(), usb_buf[QMK_OPENRGB_KEYCODE_BYTE], + usb_buf[QMK_OPENRGB_KEYCODE_BYTE], led); + led_names.push_back(KEY_EN_UNUSED); + } + } +} + +bool QMKOpenRGBRev9Controller::GetIsModeEnabled(unsigned int mode) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_IS_MODE_ENABLED; + usb_buf[0x02] = mode; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + return usb_buf[1] == QMK_OPENRGB_SUCCESS ? true : false; +} + +void QMKOpenRGBRev9Controller::DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED; + usb_buf[0x02] = led; + usb_buf[0x03] = red; + usb_buf[0x04] = green; + usb_buf[0x05] = blue; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT); +} + +void QMKOpenRGBRev9Controller::DirectModeSetLEDs(std::vector colors, unsigned int leds_count) +{ + unsigned int leds_sent = 0; + unsigned int tmp_leds_per_update = leds_per_update; + + while (leds_sent < leds_count) + { + if ((leds_count - leds_sent) < tmp_leds_per_update) + { + tmp_leds_per_update = leds_count - leds_sent; + } + + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_LEDS; + usb_buf[0x02] = leds_sent; + usb_buf[0x03] = tmp_leds_per_update; + + for (unsigned int led_idx = 0; led_idx < tmp_leds_per_update; led_idx++) + { + usb_buf[(led_idx * 3) + 4] = RGBGetRValue(colors[led_idx + leds_sent]); + usb_buf[(led_idx * 3) + 5] = RGBGetGValue(colors[led_idx + leds_sent]); + usb_buf[(led_idx * 3) + 6] = RGBGetBValue(colors[led_idx + leds_sent]); + } + + hid_write(dev, usb_buf, 65); + + if(delay > 0ms) + { + std::this_thread::sleep_for(delay); + } + + leds_sent += tmp_leds_per_update; + } +} diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/QMKOpenRGBRev9Controller.h b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/QMKOpenRGBRev9Controller.h new file mode 100644 index 0000000..28aca8a --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/QMKOpenRGBRev9Controller.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| QMKOpenRGBRev9Controller.h | +| | +| Driver for OpenRGB QMK Keyboard Protocol Revision 9 | +| Revision 9 was initially supported by OpenRGB 0.6 | +| | +| Kasper 10 Oct 2020 | +| Jath03 28 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "QMKOpenRGBBaseController.h" + +class QMKOpenRGBRev9Controller : public QMKOpenRGBBaseController +{ +public: + QMKOpenRGBRev9Controller(hid_device *dev_handle, const char *path); + ~QMKOpenRGBRev9Controller(); + + //Virtual function implementations + void GetLEDInfo(unsigned int led); + void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void DirectModeSetLEDs(std::vector colors, unsigned int num_colors); + + //Protocol Specific functions + bool GetIsModeEnabled(unsigned int mode); + +private: +}; diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/RGBController_QMKOpenRGBRev9.cpp b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/RGBController_QMKOpenRGBRev9.cpp new file mode 100644 index 0000000..7c0542f --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/RGBController_QMKOpenRGBRev9.cpp @@ -0,0 +1,762 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKOpenRGBRev9.cpp | +| | +| RGBController for OpenRGB QMK Keyboard Protocol | +| Revision 9 | +| | +| Kasper 10 Oct 2020 | +| Jath03 28 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "hsv.h" +#include "LogManager.h" +#include "RGBController_QMKOpenRGBRev9.h" + +/**------------------------------------------------------------------*\ + @name Quantum Mechanical Keyboard (QMK) + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors + @comment Please see [the github page](https://github.com/qmk/qmk_firmware#supported-keyboards) for the up to date list of + keyboards supported by the QMK controller. +\*-------------------------------------------------------------------*/ + +RGBController_QMKOpenRGBRev9::RGBController_QMKOpenRGBRev9(QMKOpenRGBRev9Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = controller->GetDeviceVendor(); + description = "QMK OpenRGB Device (Protocol Version " + std::to_string(controller->GetProtocolVersion()) + ")"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->GetLocation(); + version = controller->GetQMKVersion(); + + unsigned int current_mode = 1; + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_OPENRGB_DIRECT)) + { + InitializeMode("Direct", current_mode, MODE_FLAG_HAS_PER_LED_COLOR, MODE_COLORS_PER_LED); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_COLOR)) + { + InitializeMode("Static", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_ALPHA_MOD)) + { + InitializeMode("Alpha Mod", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_GRADIENT_UP_DOWN)) + { + InitializeMode("Gradient Up Down", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_GRADIENT_LEFT_RIGHT)) + { + InitializeMode("Gradient Left Right", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BREATHING)) + { + InitializeMode("Breathing", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_SAT)) + { + InitializeMode("Band Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_VAL)) + { + InitializeMode("Band Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_PINWHEEL_SAT)) + { + InitializeMode("Band Pinwheel Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_PINWHEEL_VAL)) + { + InitializeMode("Band Pinwheel Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_SPIRAL_SAT)) + { + InitializeMode("Band Spiral Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_BAND_SPIRAL_VAL)) + { + InitializeMode("Band Spiral Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_ALL)) + { + InitializeMode("Cycle All", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_LEFT_RIGHT)) + { + InitializeMode("Cycle Left Right", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_UP_DOWN)) + { + InitializeMode("Cycle Up Down", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_OUT_IN)) + { + InitializeMode("Cycle Out In", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_OUT_IN_DUAL)) + { + InitializeMode("Cycle Out In Dual", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_RAINBOW_MOVING_CHEVRON)) + { + InitializeMode("Rainbow Moving Chevron", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_PINWHEEL)) + { + InitializeMode("Cycle Pinwheel", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_CYCLE_SPIRAL)) + { + InitializeMode("Cycle Spiral", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_DUAL_BEACON)) + { + InitializeMode("Dual Beacon", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_RAINBOW_BEACON)) + { + InitializeMode("Rainbow Beacon", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_RAINBOW_PINWHEELS)) + { + InitializeMode("Rainbow Pinwheels", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_RAINDROPS)) + { + InitializeMode("Raindrops", current_mode, 0, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_JELLYBEAN_RAINDROPS)) + { + InitializeMode("Jellybean Raindrops", current_mode, 0, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_HUE_BREATHING)) + { + InitializeMode("Hue Breathing", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_HUE_PENDULUM)) + { + InitializeMode("Hue Pendulum", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_HUE_WAVE)) + { + InitializeMode("Hue Wave", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_TYPING_HEATMAP)) + { + InitializeMode("Typing Heatmap", current_mode, 0, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_DIGITAL_RAIN)) + { + InitializeMode("Digital Rain", current_mode, 0, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_SIMPLE)) + { + InitializeMode("Solid Reactive Simple", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE)) + { + InitializeMode("Solid Reactive", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_WIDE)) + { + InitializeMode("Solid Reactive Wide", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTIWIDE)) + { + InitializeMode("Solid Reactive Multi Wide", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_CROSS)) + { + InitializeMode("Solid Reactive Cross", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTICROSS)) + { + InitializeMode("Solid Reactive Multi Cross", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_NEXUS)) + { + InitializeMode("Solid Reactive Nexus", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTINEXUS)) + { + InitializeMode("Solid Reactive Multi Nexus", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SPLASH)) + { + InitializeMode("Rainbow Reactive Splash", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_MULTISPLASH)) + { + InitializeMode("Rainbow Reactive Multi Splash", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_SPLASH)) + { + InitializeMode("Solid Reactive Splash", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + if(controller->GetIsModeEnabled(QMK_OPENRGB_MODE_SOLID_MULTISPLASH)) + { + InitializeMode("Solid Reactive Multi Splash", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC); + } + + active_mode = controller->GetMode() - 1; + + SetupZones(); +} + +RGBController_QMKOpenRGBRev9::~RGBController_QMKOpenRGBRev9() +{ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } +} + +void RGBController_QMKOpenRGBRev9::SetupZones() +{ + /*---------------------------------------------------------*\ + | Get the number of LEDs from the device | + \*---------------------------------------------------------*/ + const unsigned int total_number_of_leds = controller->GetTotalNumberOfLEDs(); + const unsigned int total_number_of_leds_with_empty_space = controller->GetTotalNumberOfLEDsWithEmptySpace(); + + LOG_INFO("[%s] Keyboard has %u LEDs total", name.c_str(), total_number_of_leds); + + /*---------------------------------------------------------*\ + | Get information for each LED | + \*---------------------------------------------------------*/ + for(unsigned int i = 0; i < std::max(total_number_of_leds, total_number_of_leds_with_empty_space); i++) + { + controller->GetLEDInfo(i); + } + + /*---------------------------------------------------------*\ + | Get LED vectors from controller | + \*---------------------------------------------------------*/ + std::vector led_points = controller->GetLEDPoints(); + std::vector led_flags = controller->GetLEDFlags(); + std::vector led_names = controller->GetLEDNames(); + + /*---------------------------------------------------------*\ + | Count key LEDs and underglow LEDs | + \*---------------------------------------------------------*/ + unsigned int number_of_key_leds; + unsigned int number_of_underglow_leds; + + CountKeyTypes(led_flags, total_number_of_leds, number_of_key_leds, number_of_underglow_leds); + + /*---------------------------------------------------------*\ + | Add LED names for underglow zone | + \*---------------------------------------------------------*/ + unsigned int number_of_leds = number_of_key_leds + number_of_underglow_leds; + bool has_underglow = number_of_underglow_leds > 0; + LOG_INFO("[%s] Keyboard has %u underglow LEDs", name.c_str(), number_of_underglow_leds); + + for(unsigned int i = 0; i < number_of_underglow_leds; i++) + { + led_names.push_back("Underglow: " + std::to_string(number_of_key_leds + i)); + } + + /*---------------------------------------------------------*\ + | Create sets for row and column position values | + \*---------------------------------------------------------*/ + std::set rows, columns; + for (unsigned int i = 0; i < number_of_leds; i++) + { + rows.insert(led_points[i].y); + columns.insert(led_points[i].x); + } + + /*---------------------------------------------------------*\ + | Calculate matrix map from QMK positions | + \*---------------------------------------------------------*/ + unsigned int divisor = CalculateDivisor(led_points, rows, columns); + LOG_DEBUG("[%s] Distance between standard keys calculated to be %u", name.c_str(), divisor); + + VectorMatrix matrix_map; + VectorMatrix underglow_map; + + PlaceLEDsInMaps(rows, columns, divisor, led_points, led_flags, matrix_map, underglow_map); + CleanMatrixMaps(matrix_map, underglow_map, (unsigned int)rows.size(), has_underglow); + + /*---------------------------------------------------------*\ + | These vectors are class members because if they go out of | + | scope, the underlying array (used by each zones' | + | matrix_map) is unallocated. | + \*---------------------------------------------------------*/ + flat_matrix_map = FlattenMatrixMap(matrix_map); + flat_underglow_map = FlattenMatrixMap(underglow_map); + + /*---------------------------------------------------------*\ + | Create Keyboard zone | + \*---------------------------------------------------------*/ + zone keys_zone; + keys_zone.name = "Keyboard"; + keys_zone.type = ZONE_TYPE_MATRIX; + keys_zone.leds_min = number_of_key_leds; + keys_zone.leds_max = keys_zone.leds_min; + keys_zone.leds_count = keys_zone.leds_min; + keys_zone.matrix_map = new matrix_map_type; + keys_zone.matrix_map->width = (unsigned int)matrix_map[0].size(); + keys_zone.matrix_map->height = (unsigned int)matrix_map.size(); + keys_zone.matrix_map->map = flat_matrix_map.data(); + zones.push_back(keys_zone); + + /*---------------------------------------------------------*\ + | Create Underglow zone if it exists | + \*---------------------------------------------------------*/ + if(has_underglow) + { + zone underglow_zone; + underglow_zone.name = "Underglow"; + underglow_zone.type = ZONE_TYPE_MATRIX; + underglow_zone.leds_min = number_of_underglow_leds; + underglow_zone.leds_max = underglow_zone.leds_min; + underglow_zone.leds_count = underglow_zone.leds_min; + underglow_zone.matrix_map = new matrix_map_type; + underglow_zone.matrix_map->width = (unsigned int)underglow_map[0].size(); + underglow_zone.matrix_map->height = (unsigned int)underglow_map.size(); + underglow_zone.matrix_map->map = flat_underglow_map.data(); + zones.push_back(underglow_zone); + } + + /*---------------------------------------------------------*\ + | Create LEDs | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < number_of_leds; led_idx++) + { + led keyboard_led; + + if(led_idx < led_names.size()) + { + keyboard_led.name = led_names[led_idx]; + } + keyboard_led.value = led_idx; + + leds.push_back(keyboard_led); + } + + /*---------------------------------------------------------*\ + | Setup Colors | + \*---------------------------------------------------------*/ + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize colors from device values | + \*---------------------------------------------------------*/ + for(unsigned int i = 0; i < leds.size(); i++) + { + colors[i] = controller->GetLEDColors()[i]; + } +} + +void RGBController_QMKOpenRGBRev9::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_QMKOpenRGBRev9::DeviceUpdateLEDs() +{ + controller->DirectModeSetLEDs(colors, controller->GetTotalNumberOfLEDs()); +} + +void RGBController_QMKOpenRGBRev9::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_QMKOpenRGBRev9::UpdateSingleLED(int led) +{ + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->DirectModeSetSingleLED(led, red, grn, blu); +} + +void RGBController_QMKOpenRGBRev9::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, 127); + } + else if(modes[active_mode].color_mode == MODE_COLORS_NONE) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, modes[active_mode].speed); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + RGBColor rgb_color = modes[active_mode].colors[0]; + hsv_t hsv_color; + rgb2hsv(rgb_color, &hsv_color); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->SetMode(hsv_color, modes[active_mode].value, modes[active_mode].speed); + } + else + { + controller->SetMode(hsv_color, modes[active_mode].value, 127); + } + } +} + +void RGBController_QMKOpenRGBRev9::InitializeMode + ( + std::string name, + unsigned int ¤t_mode, + unsigned int flags, + unsigned int color_mode + ) +{ + mode qmk_mode; + qmk_mode.name = name; + qmk_mode.value = current_mode++; + qmk_mode.flags = flags; + qmk_mode.color_mode = color_mode; + + if(flags & MODE_FLAG_HAS_SPEED) + { + qmk_mode.speed_min = QMK_OPENRGB_SPEED_SLOWEST; + qmk_mode.speed_max = QMK_OPENRGB_SPEED_FASTEST; + qmk_mode.speed = QMK_OPENRGB_SPEED_NORMAL; + } + if(flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + qmk_mode.colors_min = 1; + qmk_mode.colors_max = 1; + qmk_mode.colors.resize(1); + qmk_mode.colors[0] = controller->GetModeColor(); + } + + modes.push_back(qmk_mode); +} + +unsigned int RGBController_QMKOpenRGBRev9::CalculateDivisor + ( + std::vector led_points, + std::set rows, + std::set /*columns*/ + ) +{ + std::vector< std::vector > row_points(rows.size()); + for(const point_t &pt : led_points) + { + for(const int &i : rows) + { + if(pt.y == i) + { + row_points[std::distance(rows.begin(), rows.find(i))].push_back(pt); + } + } + } + + int last_pos; + std::vector distances; + for(const std::vector &row : row_points) + { + last_pos = 0; + std::for_each(row.begin(), row.end(), [&distances, &last_pos](const point_t &pt) + { + distances.push_back(std::abs(pt.x - last_pos)); + last_pos = pt.x; + }); + } + std::map counts; + for(const int &i : distances) + { + counts[i]++; + } + + /*---------------------------------------------------------*\ + | Guard against empty distances (malformed LED data) | + \*---------------------------------------------------------*/ + if(distances.empty()) + { + LOG_WARNING("[%s] No valid LED distances found, using default divisor of 1", name.c_str()); + return 1; + } + + unsigned int divisor = distances[0]; + for(const std::pair &i : counts) + { + if(counts[divisor] < i.second) + { + divisor = i.first; + } + } + + if(divisor == 0) + { + LOG_WARNING("[%s] Calculated divisor is 0, using default of 1. This may indicate malformed LED position data.", name.c_str()); + return 1; + } + + return divisor; +} + +void RGBController_QMKOpenRGBRev9::CountKeyTypes + ( + std::vector led_flags, + unsigned int total_led_count, + unsigned int& key_leds, + unsigned int& underglow_leds + ) +{ + underglow_leds = 0; + key_leds = 0; + + for(unsigned int i = 0; i < total_led_count; i++) + { + if(led_flags[i] & 2) + { + underglow_leds++; + } + else if(led_flags[i] != 0) + { + key_leds++; + } + } +} + +void RGBController_QMKOpenRGBRev9::PlaceLEDsInMaps + ( + std::set unique_rows, + std::set /*unique_cols*/, + unsigned int divisor, + std::vector led_points, + std::vector led_flags, + VectorMatrix& matrix_map_xl, + VectorMatrix& underglow_map_xl + ) +{ + matrix_map_xl = MakeEmptyMatrixMap(unique_rows.size(), (std::size_t)(std::round(255 / divisor) + 10)); + underglow_map_xl = MakeEmptyMatrixMap(unique_rows.size(), (std::size_t)(std::round(255 / divisor) + 10)); + + unsigned int x = 0; + unsigned int y = 0; + unsigned int underglow_counter = 0; + + for(unsigned int i = 0; i < controller->GetTotalNumberOfLEDs(); i++) + { + if(led_points[i].x != 255 && led_points[i].y != 255) + { + bool underglow = led_flags[i] & 2; + + x = (unsigned int)(std::round(led_points[i].x / divisor)); + y = (unsigned int)(std::distance(unique_rows.begin(), unique_rows.find(led_points[i].y))); + + if(!underglow) + { + while(matrix_map_xl[y][x] != NO_LED) + { + x++; + } + matrix_map_xl[y][x] = i; + LOG_DEBUG("[%s] Key Matrix LED %u, (%u, %u) being placed into (%u, %u)", name.c_str(), i, led_points[i].x, led_points[i].y, x, y); + } + else + { + while(underglow_map_xl[y][x] != NO_LED) + { + x++; + } + underglow_map_xl[y][x] = underglow_counter; + underglow_counter++; + LOG_DEBUG("[%s] Underglow LED %u, (%u, %u) being placed into (%u, %u)", name.c_str(), i, led_points[i].x, led_points[i].y, x, y); + } + } + } +} + +VectorMatrix RGBController_QMKOpenRGBRev9::MakeEmptyMatrixMap + ( + std::size_t height, + std::size_t width + ) +{ + std::vector > matrix_map(height); + for(std::size_t i = 0; i < height; i++) + { + for(std::size_t j = 0; j < width; j++) + { + matrix_map[i].push_back(NO_LED); + } + } + return matrix_map; +} + +void RGBController_QMKOpenRGBRev9::CleanMatrixMaps + ( + VectorMatrix& matrix_map, + VectorMatrix& underglow_map, + unsigned int height, + bool has_underglow + ) +{ + bool empty_col = true; + bool empty_col_udg = true; + bool empty_row = true; + int width = 0; + int width_udg = 0; + + std::vector empty_rows; + + bool can_break; + bool can_break_udg; + + for(unsigned int i = 0; i < height; i++) + { + empty_row = true; + can_break = false; + can_break_udg = false; + + for(int j = (int)matrix_map[i].size() - 1; j --> 0; ) + { + if(matrix_map[i][j] != NO_LED && width < (j + 1) && !can_break) + { + width = (j + 1); + can_break = true; + empty_row = false; + } + else if(matrix_map[i][j] != NO_LED) + { + empty_row = false; + } + if(underglow_map[i][j] != NO_LED && width_udg < (j + 1) && !can_break_udg) + { + width_udg = (j + 1); + can_break_udg = true; + } + if (can_break && can_break_udg) break; + } + + if(matrix_map[i][0] != NO_LED) + { + empty_col = false; + } + + if(underglow_map[i][0] != NO_LED) + { + empty_col_udg = false; + } + + if(empty_row) + { + empty_rows.push_back(i); + } + } + + unsigned int new_height = height - (unsigned int)empty_rows.size(); + width = empty_col ? width - 1 : width; + width_udg = empty_col_udg && empty_col ? width_udg - 1 : width_udg; + LOG_DEBUG("[%s] Key LED Matrix: %ux%u", name.c_str(), width, new_height); + LOG_DEBUG("[%s] Underglow LED Matrix: %ux%u", name.c_str(), width_udg, new_height); + + for(unsigned int i = (unsigned int)empty_rows.size(); i --> 0; ) + { + matrix_map.erase(matrix_map.begin() + empty_rows[i]); + } + + for(unsigned int i = 0; i < new_height; i++) + { + if(empty_col) + { + matrix_map[i].erase(matrix_map[i].begin(), matrix_map[i].begin() + 1); + } + + if(empty_col_udg && empty_col) + { + underglow_map[i].erase(underglow_map[i].begin(), underglow_map[i].begin() + 1); + } + + matrix_map[i].erase(matrix_map[i].begin()+width, matrix_map[i].end()); + + if(has_underglow) + { + underglow_map[i].erase(underglow_map[i].begin()+width_udg, underglow_map[i].end()); + } + } + + if(has_underglow) + { + for(unsigned int i = new_height; i < height; i++) + { + underglow_map[i].erase(underglow_map[i].begin()+width_udg, underglow_map[i].end()); + } + } +} + +std::vector RGBController_QMKOpenRGBRev9::FlattenMatrixMap + ( + VectorMatrix matrix_map + ) +{ + std::vector flat_map; + + for(const std::vector &row : matrix_map) + { + for(const unsigned int &item : row) + { + flat_map.push_back(item); + } + } + return flat_map; +} diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/RGBController_QMKOpenRGBRev9.h b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/RGBController_QMKOpenRGBRev9.h new file mode 100644 index 0000000..8f4560c --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/RGBController_QMKOpenRGBRev9.h @@ -0,0 +1,99 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKOpenRGBRev9.h | +| | +| RGBController for OpenRGB QMK Keyboard Protocol | +| Revision 9 | +| | +| Kasper 10 Oct 2020 | +| Jath03 28 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBController.h" +#include "QMKOpenRGBRev9Controller.h" + +#define NO_LED 0xFFFFFFFF + +typedef std::vector> VectorMatrix; + +class RGBController_QMKOpenRGBRev9 : public RGBController +{ +public: + RGBController_QMKOpenRGBRev9(QMKOpenRGBRev9Controller* controller_ptr); + ~RGBController_QMKOpenRGBRev9(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + QMKOpenRGBRev9Controller* controller; + std::vector flat_matrix_map; + std::vector flat_underglow_map; + + void InitializeMode + ( + std::string name, + unsigned int ¤t_mode, + unsigned int flags, + unsigned int color_mode + ); + + unsigned int CalculateDivisor + ( + std::vector led_points, + std::set rows, + std::set columns + ); + + void CountKeyTypes + ( + std::vector led_flags, + unsigned int total_led_count, + unsigned int& key_leds, + unsigned int& underglow_leds + ); + + void PlaceLEDsInMaps + ( + std::set unique_rows, + std::set unique_cols, + unsigned int divisor, + std::vector led_points, + std::vector led_flags, + VectorMatrix& matrix_map_xl, + VectorMatrix& underglow_map_xl + ); + + VectorMatrix MakeEmptyMatrixMap + ( + std::size_t height, + std::size_t width + ); + + void CleanMatrixMaps + ( + VectorMatrix& matrix_map, + VectorMatrix& underglow_map, + unsigned int height, + bool has_underglow + ); + + std::vector FlattenMatrixMap + ( + VectorMatrix matrix_map + ); +}; diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/QMKOpenRGBRevBController.cpp b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/QMKOpenRGBRevBController.cpp new file mode 100644 index 0000000..8a6c529 --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/QMKOpenRGBRevBController.cpp @@ -0,0 +1,194 @@ +/*---------------------------------------------------------*\ +| QMKOpenRGBRevBController.cpp | +| | +| Driver for OpenRGB QMK Keyboard Protocol Revision B | +| | +| Kasper 28 Jun 2021 | +| Jath03 28 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "QMKKeycodes.h" +#include "QMKOpenRGBRevBController.h" + +using namespace std::chrono_literals; + +QMKOpenRGBRevBController::QMKOpenRGBRevBController(hid_device *dev_handle, const char *path) : + QMKOpenRGBBaseController(dev_handle, path, 20) +{ +} + +QMKOpenRGBRevBController::~QMKOpenRGBRevBController() +{ +} + +void QMKOpenRGBRevBController::GetLEDInfo(unsigned int leds_count) +{ + unsigned int leds_sent = 0; + unsigned int leds_per_update_info = 8; + + while (leds_sent < leds_count) + { + if ((leds_count - leds_sent) < leds_per_update_info) + { + leds_per_update_info = leds_count - leds_sent; + } + + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_LED_INFO; + usb_buf[0x02] = leds_sent; + usb_buf[0x03] = leds_per_update_info; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + for (unsigned int led_idx = 0; led_idx < leds_per_update_info; led_idx++) + { + unsigned int offset = led_idx * 7; + + if(usb_buf[(offset) + QMK_OPENRGB_FLAG_BYTE] != QMK_OPENRGB_FAILURE) + { + led_points.push_back(point_t{usb_buf[(offset) + QMK_OPENRGB_POINT_X_BYTE], usb_buf[(offset) + QMK_OPENRGB_POINT_Y_BYTE]}); + led_flags.push_back(usb_buf[(offset) + QMK_OPENRGB_FLAG_BYTE]); + led_colors.push_back(ToRGBColor(usb_buf[(offset) + QMK_OPENRGB_R_COLOR_BYTE], usb_buf[(offset) + QMK_OPENRGB_G_COLOR_BYTE], usb_buf[(offset) + QMK_OPENRGB_B_COLOR_BYTE])); + } + + if(usb_buf[(offset) + QMK_OPENRGB_KEYCODE_BYTE] != 0) + { + if(qmk_keynames.count(usb_buf[(offset) + QMK_OPENRGB_KEYCODE_BYTE]) > 0) + { + led_names.push_back(qmk_keynames[usb_buf[(offset) + QMK_OPENRGB_KEYCODE_BYTE]]); + } + else + { + LOG_DEBUG("[%s] Key code: %d (%02X) @ offset %d was not found in the QMK keyname map", + device_name.c_str(), usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE], + usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE], leds_sent + led_idx); + led_names.push_back(KEY_EN_UNUSED); + } + } + } + + leds_sent += leds_per_update_info; + } +} + +std::vector QMKOpenRGBRevBController::GetEnabledModes() +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_ENABLED_MODES; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + std::vector enabled_modes; + int i = 1; + while (usb_buf[i] != 0) + { + enabled_modes.push_back(usb_buf[i]); + i++; + } + return enabled_modes; +} + +void QMKOpenRGBRevBController::DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED; + usb_buf[0x02] = led; + usb_buf[0x03] = red; + usb_buf[0x04] = green; + usb_buf[0x05] = blue; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT); +} + +void QMKOpenRGBRevBController::DirectModeSetLEDs(std::vector colors, unsigned int leds_count) +{ + unsigned int leds_sent = 0; + unsigned int tmp_leds_per_update = leds_per_update; + + while (leds_sent < leds_count) + { + if ((leds_count - leds_sent) < tmp_leds_per_update) + { + tmp_leds_per_update = leds_count - leds_sent; + } + + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_LEDS; + usb_buf[0x02] = leds_sent; + usb_buf[0x03] = tmp_leds_per_update; + + for (unsigned int led_idx = 0; led_idx < tmp_leds_per_update; led_idx++) + { + usb_buf[(led_idx * 3) + 4] = RGBGetRValue(colors[led_idx + leds_sent]); + usb_buf[(led_idx * 3) + 5] = RGBGetGValue(colors[led_idx + leds_sent]); + usb_buf[(led_idx * 3) + 6] = RGBGetBValue(colors[led_idx + leds_sent]); + } + + hid_write(dev, usb_buf, 65); + + if(delay > 0ms) + { + std::this_thread::sleep_for(delay); + } + + leds_sent += tmp_leds_per_update; + } +} diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/QMKOpenRGBRevBController.h b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/QMKOpenRGBRevBController.h new file mode 100644 index 0000000..2cbf8a8 --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/QMKOpenRGBRevBController.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| QMKOpenRGBRevBController.h | +| | +| Driver for OpenRGB QMK Keyboard Protocol Revision B | +| | +| Kasper 28 Jun 2021 | +| Jath03 28 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "QMKOpenRGBBaseController.h" + +class QMKOpenRGBRevBController : public QMKOpenRGBBaseController +{ +public: + QMKOpenRGBRevBController(hid_device *dev_handle, const char *path); + ~QMKOpenRGBRevBController(); + + //Virtual function implementations + void GetLEDInfo(unsigned int leds_count); + void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void DirectModeSetLEDs(std::vector colors, unsigned int num_colors); + + //Protocol Specific functions + std::vector GetEnabledModes(); + +private: +}; diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/RGBController_QMKOpenRGBRevB.cpp b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/RGBController_QMKOpenRGBRevB.cpp new file mode 100644 index 0000000..131ccce --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/RGBController_QMKOpenRGBRevB.cpp @@ -0,0 +1,807 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKOpenRGBRevB.cpp | +| | +| RGBController for OpenRGB QMK Keyboard Protocol | +| Revision B | +| | +| Kasper 28 Jun 2021 | +| Jath03 28 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "hsv.h" +#include "LogManager.h" +#include "RGBController_QMKOpenRGBRevB.h" + +RGBController_QMKOpenRGBRevB::RGBController_QMKOpenRGBRevB(QMKOpenRGBRevBController* controller_ptr, bool save) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = controller->GetDeviceVendor(); + description = "QMK OpenRGB Device (Protocol Version " + std::to_string(controller->GetProtocolVersion()) + ")"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->GetLocation(); + version = controller->GetQMKVersion(); + + unsigned int current_mode = 1; + std::vector enabled_modes = controller->GetEnabledModes(); + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_COLOR) != enabled_modes.end()) + { + InitializeMode("Static", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_ALPHA_MOD) != enabled_modes.end()) + { + InitializeMode("Alpha Mod", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_GRADIENT_UP_DOWN) != enabled_modes.end()) + { + InitializeMode("Gradient Up Down", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_GRADIENT_LEFT_RIGHT) != enabled_modes.end()) + { + InitializeMode("Gradient Left Right", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BREATHING) != enabled_modes.end()) + { + InitializeMode("Breathing", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_SAT) != enabled_modes.end()) + { + InitializeMode("Band Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_VAL) != enabled_modes.end()) + { + InitializeMode("Band Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_PINWHEEL_SAT) != enabled_modes.end()) + { + InitializeMode("Band Pinwheel Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_PINWHEEL_VAL) != enabled_modes.end()) + { + InitializeMode("Band Pinwheel Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_SPIRAL_SAT) != enabled_modes.end()) + { + InitializeMode("Band Spiral Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_SPIRAL_VAL) != enabled_modes.end()) + { + InitializeMode("Band Spiral Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_ALL) != enabled_modes.end()) + { + InitializeMode("Cycle All", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_LEFT_RIGHT) != enabled_modes.end()) + { + InitializeMode("Cycle Left Right", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_UP_DOWN) != enabled_modes.end()) + { + InitializeMode("Cycle Up Down", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_OUT_IN) != enabled_modes.end()) + { + InitializeMode("Cycle Out In", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_OUT_IN_DUAL) != enabled_modes.end()) + { + InitializeMode("Cycle Out In Dual", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINBOW_MOVING_CHEVRON) != enabled_modes.end()) + { + InitializeMode("Rainbow Moving Chevron", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_PINWHEEL) != enabled_modes.end()) + { + InitializeMode("Cycle Pinwheel", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_SPIRAL) != enabled_modes.end()) + { + InitializeMode("Cycle Spiral", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_DUAL_BEACON) != enabled_modes.end()) + { + InitializeMode("Dual Beacon", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINBOW_BEACON) != enabled_modes.end()) + { + InitializeMode("Rainbow Beacon", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINBOW_PINWHEELS) != enabled_modes.end()) + { + InitializeMode("Rainbow Pinwheels", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINDROPS) != enabled_modes.end()) + { + InitializeMode("Raindrops", current_mode, 0, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_JELLYBEAN_RAINDROPS) != enabled_modes.end()) + { + InitializeMode("Jellybean Raindrops", current_mode, 0, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_HUE_BREATHING) != enabled_modes.end()) + { + InitializeMode("Hue Breathing", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_HUE_PENDULUM) != enabled_modes.end()) + { + InitializeMode("Hue Pendulum", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_HUE_WAVE) != enabled_modes.end()) + { + InitializeMode("Hue Wave", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_TYPING_HEATMAP) != enabled_modes.end()) + { + InitializeMode("Typing Heatmap", current_mode, 0, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_DIGITAL_RAIN) != enabled_modes.end()) + { + InitializeMode("Digital Rain", current_mode, 0, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_SIMPLE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Simple", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_WIDE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Wide", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTIWIDE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Wide", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_CROSS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Cross", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTICROSS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Cross", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_NEXUS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Nexus", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTINEXUS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Nexus", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SPLASH) != enabled_modes.end()) + { + InitializeMode("Rainbow Reactive Splash", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_MULTISPLASH) != enabled_modes.end()) + { + InitializeMode("Rainbow Reactive Multi Splash", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_SPLASH) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Splash", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_MULTISPLASH) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Splash", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_OPENRGB_DIRECT) != enabled_modes.end()) + { + InitializeMode("Direct", current_mode, MODE_FLAG_HAS_PER_LED_COLOR, MODE_COLORS_PER_LED, save); + } + + /*-----------------------------------------------------*\ + | As we are insertting direct mode at index 0 | + | for it to be the first mode in the UI there will | + | be a mismatch between the values. QMK has direct | + | mode last in order, while in OpenRGB it's first. | + \*-----------------------------------------------------*/ + if(controller->GetMode() == (current_mode - 1)) + { + active_mode = 0; + } + else + { + active_mode = controller->GetMode(); + } + + SetupZones(); +} + +RGBController_QMKOpenRGBRevB::~RGBController_QMKOpenRGBRevB() +{ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } +} + +void RGBController_QMKOpenRGBRevB::SetupZones() +{ + /*---------------------------------------------------------*\ + | Get the number of LEDs from the device | + \*---------------------------------------------------------*/ + const unsigned int total_number_of_leds = controller->GetTotalNumberOfLEDs(); + const unsigned int total_number_of_leds_with_empty_space = controller->GetTotalNumberOfLEDsWithEmptySpace(); + + LOG_INFO("[%s] Keyboard has %u LEDs total", name.c_str(), total_number_of_leds); + + /*---------------------------------------------------------*\ + | Get information for each LED | + \*---------------------------------------------------------*/ + controller->GetLEDInfo(std::max(total_number_of_leds, total_number_of_leds_with_empty_space)); + + /*---------------------------------------------------------*\ + | Get LED vectors from controller | + \*---------------------------------------------------------*/ + std::vector led_points = controller->GetLEDPoints(); + std::vector led_flags = controller->GetLEDFlags(); + std::vector led_names = controller->GetLEDNames(); + + /*---------------------------------------------------------*\ + | Count key LEDs and underglow LEDs | + \*---------------------------------------------------------*/ + unsigned int number_of_key_leds; + unsigned int number_of_underglow_leds; + + CountKeyTypes(led_flags, total_number_of_leds, number_of_key_leds, number_of_underglow_leds); + + /*---------------------------------------------------------*\ + | Add LED names for underglow zone | + \*---------------------------------------------------------*/ + unsigned int number_of_leds = number_of_key_leds + number_of_underglow_leds; + bool has_underglow = number_of_underglow_leds > 0; + LOG_INFO("[%s] Keyboard has %u underglow LEDs", name.c_str(), number_of_underglow_leds); + + for(unsigned int i = 0; i < number_of_underglow_leds; i++) + { + led_names.push_back("Underglow: " + std::to_string(number_of_key_leds + i)); + } + + /*---------------------------------------------------------*\ + | Create sets for row and column position values | + \*---------------------------------------------------------*/ + std::set rows, columns; + for (unsigned int i = 0; i < number_of_leds; i++) + { + rows.insert(led_points[i].y); + columns.insert(led_points[i].x); + } + + /*---------------------------------------------------------*\ + | Calculate matrix map from QMK positions | + \*---------------------------------------------------------*/ + unsigned int divisor = CalculateDivisor(led_points, rows, columns); + LOG_DEBUG("[%s] Distance between standard keys calculated to be %u", name.c_str(), divisor); + + VectorMatrix matrix_map; + VectorMatrix underglow_map; + + PlaceLEDsInMaps(rows, columns, divisor, led_points, led_flags, matrix_map, underglow_map); + CleanMatrixMaps(matrix_map, underglow_map, (unsigned int)rows.size(), has_underglow); + + /*---------------------------------------------------------*\ + | These vectors are class members because if they go out of | + | scope, the underlying array (used by each zones' | + | matrix_map) is unallocated. | + \*---------------------------------------------------------*/ + flat_matrix_map = FlattenMatrixMap(matrix_map); + flat_underglow_map = FlattenMatrixMap(underglow_map); + + /*---------------------------------------------------------*\ + | Create Keyboard zone | + \*---------------------------------------------------------*/ + zone keys_zone; + keys_zone.name = "Keyboard"; + keys_zone.type = ZONE_TYPE_MATRIX; + keys_zone.leds_min = number_of_key_leds; + keys_zone.leds_max = keys_zone.leds_min; + keys_zone.leds_count = keys_zone.leds_min; + keys_zone.matrix_map = new matrix_map_type; + keys_zone.matrix_map->width = (unsigned int)matrix_map[0].size(); + keys_zone.matrix_map->height = (unsigned int)matrix_map.size(); + keys_zone.matrix_map->map = flat_matrix_map.data(); + zones.push_back(keys_zone); + + /*---------------------------------------------------------*\ + | Create Underglow zone if it exists | + \*---------------------------------------------------------*/ + if(has_underglow) + { + zone underglow_zone; + underglow_zone.name = "Underglow"; + underglow_zone.type = ZONE_TYPE_MATRIX; + underglow_zone.leds_min = number_of_underglow_leds; + underglow_zone.leds_max = underglow_zone.leds_min; + underglow_zone.leds_count = underglow_zone.leds_min; + underglow_zone.matrix_map = new matrix_map_type; + underglow_zone.matrix_map->width = (unsigned int)underglow_map[0].size(); + underglow_zone.matrix_map->height = (unsigned int)underglow_map.size(); + underglow_zone.matrix_map->map = flat_underglow_map.data(); + zones.push_back(underglow_zone); + } + + /*---------------------------------------------------------*\ + | Create LEDs | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < number_of_leds; led_idx++) + { + led keyboard_led; + + if(led_idx < led_names.size()) + { + keyboard_led.name = led_names[led_idx]; + } + keyboard_led.value = led_idx; + + leds.push_back(keyboard_led); + } + + /*---------------------------------------------------------*\ + | Setup Colors | + \*---------------------------------------------------------*/ + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize colors from device values | + \*---------------------------------------------------------*/ + for(unsigned int i = 0; i < leds.size(); i++) + { + colors[i] = controller->GetLEDColors()[i]; + } +} + +void RGBController_QMKOpenRGBRevB::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_QMKOpenRGBRevB::DeviceUpdateLEDs() +{ + controller->DirectModeSetLEDs(colors, controller->GetTotalNumberOfLEDs()); +} + +void RGBController_QMKOpenRGBRevB::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_QMKOpenRGBRevB::UpdateSingleLED(int led) +{ + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->DirectModeSetSingleLED(led, red, grn, blu); +} + +void RGBController_QMKOpenRGBRevB::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, 127, false); + } + else if(modes[active_mode].color_mode == MODE_COLORS_NONE) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, modes[active_mode].speed, false); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + RGBColor rgb_color = modes[active_mode].colors[0]; + hsv_t hsv_color; + rgb2hsv(rgb_color, &hsv_color); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->SetMode(hsv_color, modes[active_mode].value, modes[active_mode].speed, false); + } + else + { + controller->SetMode(hsv_color, modes[active_mode].value, 127, false); + } + } +} + +void RGBController_QMKOpenRGBRevB::DeviceSaveMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_NONE) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, modes[active_mode].speed, true); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + RGBColor rgb_color = modes[active_mode].colors[0]; + hsv_t hsv_color; + rgb2hsv(rgb_color, &hsv_color); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->SetMode(hsv_color, modes[active_mode].value, modes[active_mode].speed, true); + } + else + { + controller->SetMode(hsv_color, modes[active_mode].value, 127, true); + } + } +} + +void RGBController_QMKOpenRGBRevB::InitializeMode + ( + std::string name, + unsigned int ¤t_mode, + unsigned int flags, + unsigned int color_mode, + bool save + ) +{ + mode qmk_mode; + qmk_mode.name = name; + qmk_mode.value = current_mode++; + qmk_mode.flags = flags; + qmk_mode.color_mode = color_mode; + + if(flags & MODE_FLAG_HAS_SPEED) + { + qmk_mode.speed_min = QMK_OPENRGB_SPEED_SLOWEST; + qmk_mode.speed_max = QMK_OPENRGB_SPEED_FASTEST; + qmk_mode.speed = QMK_OPENRGB_SPEED_NORMAL; + } + if(flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + qmk_mode.colors_min = 1; + qmk_mode.colors_max = 1; + qmk_mode.colors.resize(1); + qmk_mode.colors[0] = controller->GetModeColor(); + } + + /*-----------------------------------------------------*\ + | Direct mode it the last mode on the QMK firmware | + | but we still want it to appear first on the UI | + \*-----------------------------------------------------*/ + if(flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + modes.insert(modes.begin(), qmk_mode); + } + else + { + /*-----------------------------------------------------*\ + | Every mode apart from direct is save-able | + \*-----------------------------------------------------*/ + if(save == true) + { + qmk_mode.flags = flags | MODE_FLAG_MANUAL_SAVE; + } + modes.push_back(qmk_mode); + } +} + +unsigned int RGBController_QMKOpenRGBRevB::CalculateDivisor + ( + std::vector led_points, + std::set rows, + std::set /*columns*/ + ) +{ + std::vector< std::vector > row_points(rows.size()); + for(const point_t &pt : led_points) + { + for(const int &i : rows) + { + if(pt.y == i) + { + row_points[std::distance(rows.begin(), rows.find(i))].push_back(pt); + } + } + } + + int last_pos; + std::vector distances; + for(const std::vector &row : row_points) + { + last_pos = 0; + std::for_each(row.begin(), row.end(), [&distances, &last_pos](const point_t &pt) + { + distances.push_back(std::abs(pt.x - last_pos)); + last_pos = pt.x; + }); + } + + /*---------------------------------------------------------*\ + | Guard against empty distances (malformed LED data) | + \*---------------------------------------------------------*/ + if(distances.empty()) + { + LOG_WARNING("[%s] No valid LED distances found, using default divisor of 1", name.c_str()); + return 1; + } + + std::map counts; + for(const int &i : distances) + { + counts[i]++; + } + + unsigned int divisor = distances[0]; + for(const std::pair &i : counts) + { + if(counts[divisor] < i.second) + { + divisor = i.first; + } + } + + /*---------------------------------------------------------*\ + | Guard against zero divisor (prevents division by zero) | + \*---------------------------------------------------------*/ + if(divisor == 0) + { + LOG_WARNING("[%s] Calculated divisor is 0, using default of 1. This may indicate malformed LED position data.", name.c_str()); + return 1; + } + + return divisor; +} + +void RGBController_QMKOpenRGBRevB::CountKeyTypes + ( + std::vector led_flags, + unsigned int total_led_count, + unsigned int& key_leds, + unsigned int& underglow_leds + ) +{ + underglow_leds = 0; + key_leds = 0; + + for(unsigned int i = 0; i < total_led_count; i++) + { + if(led_flags[i] & 2) + { + underglow_leds++; + } + else if(led_flags[i] != 0) + { + key_leds++; + } + } +} + +void RGBController_QMKOpenRGBRevB::PlaceLEDsInMaps + ( + std::set unique_rows, + std::set /*unique_cols*/, + unsigned int divisor, + std::vector led_points, + std::vector led_flags, + VectorMatrix& matrix_map_xl, + VectorMatrix& underglow_map_xl + ) +{ + matrix_map_xl = MakeEmptyMatrixMap(unique_rows.size(), (std::size_t)(std::round(255 / divisor) + 10)); + underglow_map_xl = MakeEmptyMatrixMap(unique_rows.size(), (std::size_t)(std::round(255 / divisor) + 10)); + + unsigned int x = 0; + unsigned int y = 0; + unsigned int underglow_counter = 0; + + for(unsigned int i = 0; i < controller->GetTotalNumberOfLEDs(); i++) + { + if(led_points[i].x != 255 && led_points[i].y != 255) + { + bool underglow = led_flags[i] & 2; + + x = (unsigned int)(std::round(led_points[i].x / divisor)); + y = (unsigned int)(std::distance(unique_rows.begin(), unique_rows.find(led_points[i].y))); + + if(!underglow) + { + while(matrix_map_xl[y][x] != NO_LED) + { + x++; + } + matrix_map_xl[y][x] = i; + LOG_DEBUG("[%s] Key Matrix LED %u, (%u, %u) being placed into (%u, %u)", name.c_str(), i, led_points[i].x, led_points[i].y, x, y); + } + else + { + while(underglow_map_xl[y][x] != NO_LED) + { + x++; + } + underglow_map_xl[y][x] = underglow_counter; + underglow_counter++; + LOG_DEBUG("[%s] Underglow LED %u, (%u, %u) being placed into (%u, %u)", name.c_str(), i, led_points[i].x, led_points[i].y, x, y); + } + } + } +} + +VectorMatrix RGBController_QMKOpenRGBRevB::MakeEmptyMatrixMap + ( + std::size_t height, + std::size_t width + ) +{ + std::vector > matrix_map(height); + for(std::size_t i = 0; i < height; i++) + { + for(std::size_t j = 0; j < width; j++) + { + matrix_map[i].push_back(NO_LED); + } + } + return matrix_map; +} + +void RGBController_QMKOpenRGBRevB::CleanMatrixMaps + ( + VectorMatrix& matrix_map, + VectorMatrix& underglow_map, + unsigned int height, + bool has_underglow + ) +{ + bool empty_col = true; + bool empty_col_udg = true; + bool empty_row = true; + int width = 0; + int width_udg = 0; + + std::vector empty_rows; + + bool can_break; + bool can_break_udg; + + for(unsigned int i = 0; i < height; i++) + { + empty_row = true; + can_break = false; + can_break_udg = false; + + for(int j = (int)matrix_map[i].size() - 1; j --> 0; ) + { + if(matrix_map[i][j] != NO_LED && width < (j + 1) && !can_break) + { + width = (j + 1); + can_break = true; + empty_row = false; + } + else if(matrix_map[i][j] != NO_LED) + { + empty_row = false; + } + if(underglow_map[i][j] != NO_LED && width_udg < (j + 1) && !can_break_udg) + { + width_udg = (j + 1); + can_break_udg = true; + } + if (can_break && can_break_udg) break; + } + + if(matrix_map[i][0] != NO_LED) + { + empty_col = false; + } + + if(underglow_map[i][0] != NO_LED) + { + empty_col_udg = false; + } + + if(empty_row) + { + empty_rows.push_back(i); + } + } + + unsigned int new_height = height - (unsigned int)empty_rows.size(); + width = empty_col ? width - 1 : width; + width_udg = empty_col_udg && empty_col ? width_udg - 1 : width_udg; + LOG_DEBUG("[%s] Key LED Matrix: %ux%u", name.c_str(), width, new_height); + LOG_DEBUG("[%s] Underglow LED Matrix: %ux%u", name.c_str(), width_udg, new_height); + + for(unsigned int i = (unsigned int)empty_rows.size(); i --> 0; ) + { + matrix_map.erase(matrix_map.begin()+empty_rows[i]); + } + + for(unsigned int i = 0; i < new_height; i++) + { + if(empty_col) + { + matrix_map[i].erase(matrix_map[i].begin(), matrix_map[i].begin() + 1); + } + + if(empty_col_udg && empty_col) + { + underglow_map[i].erase(underglow_map[i].begin(), underglow_map[i].begin() + 1); + } + + matrix_map[i].erase(matrix_map[i].begin()+width, matrix_map[i].end()); + + if(has_underglow) + { + underglow_map[i].erase(underglow_map[i].begin()+width_udg, underglow_map[i].end()); + } + } + + if(has_underglow) + { + for(unsigned int i = new_height; i < height; i++) + { + underglow_map[i].erase(underglow_map[i].begin()+width_udg, underglow_map[i].end()); + } + } +} + +std::vector RGBController_QMKOpenRGBRevB::FlattenMatrixMap + ( + VectorMatrix matrix_map + ) +{ + std::vector flat_map; + + for(const std::vector &row : matrix_map) + { + for(const unsigned int &item : row) + { + flat_map.push_back(item); + } + } + return flat_map; +} diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/RGBController_QMKOpenRGBRevB.h b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/RGBController_QMKOpenRGBRevB.h new file mode 100644 index 0000000..e21b2bf --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/RGBController_QMKOpenRGBRevB.h @@ -0,0 +1,101 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKOpenRGBRevB.h | +| | +| RGBController for OpenRGB QMK Keyboard Protocol | +| Revision B | +| | +| Kasper 28 Jun 2021 | +| Jath03 28 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBController.h" +#include "QMKOpenRGBRevBController.h" + +#define NO_LED 0xFFFFFFFF + +typedef std::vector> VectorMatrix; + +class RGBController_QMKOpenRGBRevB : public RGBController +{ +public: + RGBController_QMKOpenRGBRevB(QMKOpenRGBRevBController* controller_ptr, bool save); + ~RGBController_QMKOpenRGBRevB(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + QMKOpenRGBRevBController* controller; + std::vector flat_matrix_map; + std::vector flat_underglow_map; + + void InitializeMode + ( + std::string name, + unsigned int ¤t_mode, + unsigned int flags, + unsigned int color_mode, + bool save + ); + + unsigned int CalculateDivisor + ( + std::vector led_points, + std::set rows, + std::set columns + ); + + void CountKeyTypes + ( + std::vector led_flags, + unsigned int total_led_count, + unsigned int& key_leds, + unsigned int& underglow_leds + ); + + void PlaceLEDsInMaps + ( + std::set unique_rows, + std::set unique_cols, + unsigned int divisor, + std::vector led_points, + std::vector led_flags, + VectorMatrix& matrix_map_xl, + VectorMatrix& underglow_map_xl + ); + + VectorMatrix MakeEmptyMatrixMap + ( + std::size_t height, + std::size_t width + ); + + void CleanMatrixMaps + ( + VectorMatrix& matrix_map, + VectorMatrix& underglow_map, + unsigned int height, + bool has_underglow + ); + + std::vector FlattenMatrixMap + ( + VectorMatrix matrix_map + ); +}; diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/QMKOpenRGBRevDController.cpp b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/QMKOpenRGBRevDController.cpp new file mode 100644 index 0000000..1efe920 --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/QMKOpenRGBRevDController.cpp @@ -0,0 +1,225 @@ +/*---------------------------------------------------------*\ +| QMKOpenRGBRevDController.cpp | +| | +| Driver for OpenRGB QMK Keyboard Protocol Revision D | +| | +| Neneya 26 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "QMKKeycodes.h" +#include "QMKOpenRGBRevDController.h" + +using namespace std::chrono_literals; + +QMKOpenRGBRevDController::QMKOpenRGBRevDController(hid_device *dev_handle, const char *path) : + QMKOpenRGBBaseController(dev_handle, path, 15) +{ +} + +QMKOpenRGBRevDController::~QMKOpenRGBRevDController() +{ +} + +std::vector QMKOpenRGBRevDController::GetLEDValues() +{ + return led_values; +} + +void QMKOpenRGBRevDController::GetLEDInfo(unsigned int leds_count) +{ + unsigned int leds_sent = 0; + unsigned int leds_per_update_info = 8; + + std::vector underglow_points; + std::vector underglow_flags; + std::vector underglow_names; + std::vector underglow_colors; + std::vector underglow_values; + + while (leds_sent < leds_count) + { + if ((leds_count - leds_sent) < leds_per_update_info) + { + leds_per_update_info = leds_count - leds_sent; + } + + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_LED_INFO; + usb_buf[0x02] = leds_sent; + usb_buf[0x03] = leds_per_update_info; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + for (unsigned int led_idx = 0; led_idx < leds_per_update_info; led_idx++) + { + unsigned int offset = led_idx * 7; + + if(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE] != QMK_OPENRGB_FAILURE) + { + if(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE] & 2) + { + underglow_points.push_back(point_t{usb_buf[offset + QMK_OPENRGB_POINT_X_BYTE], usb_buf[offset + QMK_OPENRGB_POINT_Y_BYTE]}); + underglow_flags.push_back(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE]); + underglow_colors.push_back(ToRGBColor(usb_buf[offset + QMK_OPENRGB_R_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_G_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_B_COLOR_BYTE])); + underglow_values.push_back((unsigned int)(underglow_values.size() + led_values.size())); + } + else + { + led_points.push_back(point_t{usb_buf[offset + QMK_OPENRGB_POINT_X_BYTE], usb_buf[offset + QMK_OPENRGB_POINT_Y_BYTE]}); + led_flags.push_back(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE]); + led_colors.push_back(ToRGBColor(usb_buf[offset + QMK_OPENRGB_R_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_G_COLOR_BYTE], usb_buf[offset + QMK_OPENRGB_B_COLOR_BYTE])); + led_values.push_back((unsigned int)(underglow_values.size() + led_values.size())); + } + } + + if(usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE] != 0) + { + if(qmk_keynames.count(usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE]) > 0) + { + led_names.push_back(qmk_keynames[usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE]]); + } + else + { + LOG_DEBUG("[%s] Key code: %d (%02X) @ offset %d was not found in the QMK keyname map", + device_name.c_str(), usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE], + usb_buf[offset + QMK_OPENRGB_KEYCODE_BYTE], leds_sent + led_idx); + led_names.push_back(KEY_EN_UNUSED); + } + } + else if(usb_buf[offset + QMK_OPENRGB_FLAG_BYTE] & 2) + { + underglow_names.push_back("Underglow: " + std::to_string(underglow_names.size() + led_names.size())); + } + } + + leds_sent += leds_per_update_info; + } + + led_points.insert(led_points.end(), underglow_points.begin(), underglow_points.end()); + led_flags.insert(led_flags.end(), underglow_flags.begin(), underglow_flags.end()); + led_colors.insert(led_colors.end(), underglow_colors.begin(), underglow_colors.end()); + led_names.insert(led_names.end(), underglow_names.begin(), underglow_names.end()); + led_values.insert(led_values.end(), underglow_values.begin(), underglow_values.end()); +} + +std::vector QMKOpenRGBRevDController::GetEnabledModes() +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_GET_ENABLED_MODES; + + int bytes_read = 0; + do + { + hid_write(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE); + bytes_read = hid_read_timeout(dev, usb_buf, QMK_OPENRGB_PACKET_SIZE, QMK_OPENRGB_HID_READ_TIMEOUT); + } while(bytes_read <= 0); + + std::vector enabled_modes; + int i = 1; + while (usb_buf[i] != 0) + { + enabled_modes.push_back(usb_buf[i]); + i++; + } + return enabled_modes; +} + +void QMKOpenRGBRevDController::DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_SINGLE_LED; + usb_buf[0x02] = led_values[led]; + usb_buf[0x03] = red; + usb_buf[0x04] = green; + usb_buf[0x05] = blue; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read_timeout(dev, usb_buf, 65, QMK_OPENRGB_HID_READ_TIMEOUT); +} + +void QMKOpenRGBRevDController::DirectModeSetLEDs(std::vector colors, unsigned int leds_count) +{ + unsigned int leds_sent = 0; + unsigned int tmp_leds_per_update = leds_per_update; + + while (leds_sent < leds_count) + { + if ((leds_count - leds_sent) < tmp_leds_per_update) + { + tmp_leds_per_update = leds_count - leds_sent; + } + + unsigned char usb_buf[QMK_OPENRGB_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, QMK_OPENRGB_PACKET_SIZE); + + /*-----------------------------------------------------*\ + | Set up config table request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = QMK_OPENRGB_DIRECT_MODE_SET_LEDS; + usb_buf[0x02] = tmp_leds_per_update; + + for (unsigned int led_idx = 0; led_idx < tmp_leds_per_update; led_idx++) + { + usb_buf[(led_idx * 4) + 3] = led_values[led_idx + leds_sent]; + usb_buf[(led_idx * 4) + 4] = RGBGetRValue(colors[led_idx + leds_sent]); + usb_buf[(led_idx * 4) + 5] = RGBGetGValue(colors[led_idx + leds_sent]); + usb_buf[(led_idx * 4) + 6] = RGBGetBValue(colors[led_idx + leds_sent]); + } + + hid_write(dev, usb_buf, 65); + + if(delay > 0ms) + { + std::this_thread::sleep_for(delay); + } + + leds_sent += tmp_leds_per_update; + } +} diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/QMKOpenRGBRevDController.h b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/QMKOpenRGBRevDController.h new file mode 100644 index 0000000..c5a1a28 --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/QMKOpenRGBRevDController.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| QMKOpenRGBRevDController.h | +| | +| Driver for OpenRGB QMK Keyboard Protocol Revision D | +| | +| Neneya 26 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "QMKOpenRGBBaseController.h" + +class QMKOpenRGBRevDController : public QMKOpenRGBBaseController +{ +public: + QMKOpenRGBRevDController(hid_device *dev_handle, const char *path); + ~QMKOpenRGBRevDController(); + + //Virtual function implementations + void GetLEDInfo(unsigned int leds_count); + void DirectModeSetSingleLED(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void DirectModeSetLEDs(std::vector colors, unsigned int num_colors); + + //Protocol Specific functions + std::vector GetLEDValues(); + std::vector GetEnabledModes(); + +private: + std::vector led_values; +}; diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/RGBController_QMKOpenRGBRevD.cpp b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/RGBController_QMKOpenRGBRevD.cpp new file mode 100644 index 0000000..d23ea73 --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/RGBController_QMKOpenRGBRevD.cpp @@ -0,0 +1,804 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKOpenRGBRevD.cpp | +| | +| RGBController for OpenRGB QMK Keyboard Protocol | +| Revision D | +| | +| Neneya 26 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "hsv.h" +#include "LogManager.h" +#include "RGBController_QMKOpenRGBRevD.h" + +RGBController_QMKOpenRGBRevD::RGBController_QMKOpenRGBRevD(QMKOpenRGBRevDController* controller_ptr, bool save) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = controller->GetDeviceVendor(); + description = "QMK OpenRGB Device (Protocol Version " + std::to_string(controller->GetProtocolVersion()) + ")"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->GetLocation(); + version = controller->GetQMKVersion(); + + unsigned int current_mode = 1; + std::vector enabled_modes = controller->GetEnabledModes(); + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_COLOR) != enabled_modes.end()) + { + InitializeMode("Static", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_ALPHA_MOD) != enabled_modes.end()) + { + InitializeMode("Alpha Mod", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_GRADIENT_UP_DOWN) != enabled_modes.end()) + { + InitializeMode("Gradient Up Down", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_GRADIENT_LEFT_RIGHT) != enabled_modes.end()) + { + InitializeMode("Gradient Left Right", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BREATHING) != enabled_modes.end()) + { + InitializeMode("Breathing", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_SAT) != enabled_modes.end()) + { + InitializeMode("Band Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_VAL) != enabled_modes.end()) + { + InitializeMode("Band Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_PINWHEEL_SAT) != enabled_modes.end()) + { + InitializeMode("Band Pinwheel Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_PINWHEEL_VAL) != enabled_modes.end()) + { + InitializeMode("Band Pinwheel Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_SPIRAL_SAT) != enabled_modes.end()) + { + InitializeMode("Band Spiral Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_SPIRAL_VAL) != enabled_modes.end()) + { + InitializeMode("Band Spiral Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_ALL) != enabled_modes.end()) + { + InitializeMode("Cycle All", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_LEFT_RIGHT) != enabled_modes.end()) + { + InitializeMode("Cycle Left Right", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_UP_DOWN) != enabled_modes.end()) + { + InitializeMode("Cycle Up Down", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_OUT_IN) != enabled_modes.end()) + { + InitializeMode("Cycle Out In", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_OUT_IN_DUAL) != enabled_modes.end()) + { + InitializeMode("Cycle Out In Dual", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINBOW_MOVING_CHEVRON) != enabled_modes.end()) + { + InitializeMode("Rainbow Moving Chevron", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_PINWHEEL) != enabled_modes.end()) + { + InitializeMode("Cycle Pinwheel", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_SPIRAL) != enabled_modes.end()) + { + InitializeMode("Cycle Spiral", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_DUAL_BEACON) != enabled_modes.end()) + { + InitializeMode("Dual Beacon", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINBOW_BEACON) != enabled_modes.end()) + { + InitializeMode("Rainbow Beacon", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINBOW_PINWHEELS) != enabled_modes.end()) + { + InitializeMode("Rainbow Pinwheels", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINDROPS) != enabled_modes.end()) + { + InitializeMode("Raindrops", current_mode, 0, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_JELLYBEAN_RAINDROPS) != enabled_modes.end()) + { + InitializeMode("Jellybean Raindrops", current_mode, 0, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_HUE_BREATHING) != enabled_modes.end()) + { + InitializeMode("Hue Breathing", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_HUE_PENDULUM) != enabled_modes.end()) + { + InitializeMode("Hue Pendulum", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_HUE_WAVE) != enabled_modes.end()) + { + InitializeMode("Hue Wave", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_TYPING_HEATMAP) != enabled_modes.end()) + { + InitializeMode("Typing Heatmap", current_mode, 0, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_DIGITAL_RAIN) != enabled_modes.end()) + { + InitializeMode("Digital Rain", current_mode, 0, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_SIMPLE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Simple", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_WIDE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Wide", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTIWIDE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Wide", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_CROSS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Cross", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTICROSS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Cross", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_NEXUS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Nexus", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTINEXUS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Nexus", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SPLASH) != enabled_modes.end()) + { + InitializeMode("Rainbow Reactive Splash", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_MULTISPLASH) != enabled_modes.end()) + { + InitializeMode("Rainbow Reactive Multi Splash", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_SPLASH) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Splash", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_MULTISPLASH) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Splash", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_OPENRGB_DIRECT) != enabled_modes.end()) + { + InitializeMode("Direct", current_mode, MODE_FLAG_HAS_PER_LED_COLOR, MODE_COLORS_PER_LED, save); + } + + /*-----------------------------------------------------*\ + | As we are insertting direct mode at index 0 | + | for it to be the first mode in the UI there will | + | be a mismatch between the values. QMK has direct | + | mode last in order, while in OpenRGB it's first. | + \*-----------------------------------------------------*/ + if(controller->GetMode() == (current_mode - 1)) + { + active_mode = 0; + } + else + { + active_mode = controller->GetMode(); + } + + SetupZones(); +} + +RGBController_QMKOpenRGBRevD::~RGBController_QMKOpenRGBRevD() +{ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } +} + +void RGBController_QMKOpenRGBRevD::SetupZones() +{ + /*---------------------------------------------------------*\ + | Get the number of LEDs from the device | + \*---------------------------------------------------------*/ + const unsigned int total_number_of_leds = controller->GetTotalNumberOfLEDs(); + const unsigned int total_number_of_leds_with_empty_space = controller->GetTotalNumberOfLEDsWithEmptySpace(); + + LOG_INFO("[%s] Keyboard has %u LEDs total", name.c_str(), total_number_of_leds); + + /*---------------------------------------------------------*\ + | Get information for each LED | + \*---------------------------------------------------------*/ + controller->GetLEDInfo(std::max(total_number_of_leds, total_number_of_leds_with_empty_space)); + + /*---------------------------------------------------------*\ + | Get LED vectors from controller | + \*---------------------------------------------------------*/ + std::vector led_points = controller->GetLEDPoints(); + std::vector led_flags = controller->GetLEDFlags(); + std::vector led_names = controller->GetLEDNames(); + std::vector led_values = controller->GetLEDValues(); + + /*---------------------------------------------------------*\ + | Count key LEDs and underglow LEDs | + \*---------------------------------------------------------*/ + unsigned int number_of_key_leds; + unsigned int number_of_underglow_leds; + + CountKeyTypes(led_flags, total_number_of_leds, number_of_key_leds, number_of_underglow_leds); + + /*---------------------------------------------------------*\ + | Count total LEDs and check if underglow exists | + \*---------------------------------------------------------*/ + unsigned int number_of_leds = number_of_key_leds + number_of_underglow_leds; + bool has_underglow = number_of_underglow_leds > 0; + LOG_INFO("[%s] Keyboard has %u underglow LEDs", name.c_str(), number_of_underglow_leds); + + /*---------------------------------------------------------*\ + | Create sets for row and column position values | + \*---------------------------------------------------------*/ + std::set rows, columns; + for (unsigned int i = 0; i < number_of_leds; i++) + { + rows.insert(led_points[i].y); + columns.insert(led_points[i].x); + } + + /*---------------------------------------------------------*\ + | Calculate matrix map from QMK positions | + \*---------------------------------------------------------*/ + unsigned int divisor = CalculateDivisor(led_points, rows, columns); + LOG_DEBUG("[%s] Distance between standard keys calculated to be %u", name.c_str(), divisor); + + VectorMatrix matrix_map; + VectorMatrix underglow_map; + + PlaceLEDsInMaps(rows, columns, divisor, led_points, led_flags, matrix_map, underglow_map); + CleanMatrixMaps(matrix_map, underglow_map, (unsigned int)rows.size(), has_underglow); + + /*---------------------------------------------------------*\ + | These vectors are class members because if they go out of | + | scope, the underlying array (used by each zones' | + | matrix_map) is unallocated. | + \*---------------------------------------------------------*/ + flat_matrix_map = FlattenMatrixMap(matrix_map); + flat_underglow_map = FlattenMatrixMap(underglow_map); + + /*---------------------------------------------------------*\ + | Create Keyboard zone | + \*---------------------------------------------------------*/ + zone keys_zone; + keys_zone.name = "Keyboard"; + keys_zone.type = ZONE_TYPE_MATRIX; + keys_zone.leds_min = number_of_key_leds; + keys_zone.leds_max = keys_zone.leds_min; + keys_zone.leds_count = keys_zone.leds_min; + keys_zone.matrix_map = new matrix_map_type; + keys_zone.matrix_map->width = (unsigned int)matrix_map[0].size(); + keys_zone.matrix_map->height = (unsigned int)matrix_map.size(); + keys_zone.matrix_map->map = flat_matrix_map.data(); + zones.push_back(keys_zone); + + /*---------------------------------------------------------*\ + | Create Underglow zone if it exists | + \*---------------------------------------------------------*/ + if(has_underglow) + { + zone underglow_zone; + underglow_zone.name = "Underglow"; + underglow_zone.type = ZONE_TYPE_MATRIX; + underglow_zone.leds_min = number_of_underglow_leds; + underglow_zone.leds_max = underglow_zone.leds_min; + underglow_zone.leds_count = underglow_zone.leds_min; + underglow_zone.matrix_map = new matrix_map_type; + underglow_zone.matrix_map->width = (unsigned int)underglow_map[0].size(); + underglow_zone.matrix_map->height = (unsigned int)underglow_map.size(); + underglow_zone.matrix_map->map = flat_underglow_map.data(); + zones.push_back(underglow_zone); + } + + /*---------------------------------------------------------*\ + | Create LEDs | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < number_of_leds; led_idx++) + { + led keyboard_led; + + if(led_idx < led_names.size()) + { + keyboard_led.name = led_names[led_idx]; + } + if(led_idx < led_values.size()){ + keyboard_led.value = led_values[led_idx]; + } + else + { + keyboard_led.value = led_idx; + } + + leds.push_back(keyboard_led); + } + + /*---------------------------------------------------------*\ + | Setup Colors | + \*---------------------------------------------------------*/ + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize colors from device values | + \*---------------------------------------------------------*/ + for(unsigned int i = 0; i < leds.size(); i++) + { + colors[i] = controller->GetLEDColors()[i]; + } +} + +void RGBController_QMKOpenRGBRevD::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_QMKOpenRGBRevD::DeviceUpdateLEDs() +{ + controller->DirectModeSetLEDs(colors, controller->GetTotalNumberOfLEDs()); +} + +void RGBController_QMKOpenRGBRevD::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_QMKOpenRGBRevD::UpdateSingleLED(int led) +{ + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->DirectModeSetSingleLED(led, red, grn, blu); +} + +void RGBController_QMKOpenRGBRevD::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, 127, false); + } + else if(modes[active_mode].color_mode == MODE_COLORS_NONE) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, modes[active_mode].speed, false); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + RGBColor rgb_color = modes[active_mode].colors[0]; + hsv_t hsv_color; + rgb2hsv(rgb_color, &hsv_color); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->SetMode(hsv_color, modes[active_mode].value, modes[active_mode].speed, false); + } + else + { + controller->SetMode(hsv_color, modes[active_mode].value, 127, false); + } + } +} + +void RGBController_QMKOpenRGBRevD::DeviceSaveMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_NONE) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, modes[active_mode].speed, true); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + RGBColor rgb_color = modes[active_mode].colors[0]; + hsv_t hsv_color; + rgb2hsv(rgb_color, &hsv_color); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->SetMode(hsv_color, modes[active_mode].value, modes[active_mode].speed, true); + } + else + { + controller->SetMode(hsv_color, modes[active_mode].value, 127, true); + } + } +} + +void RGBController_QMKOpenRGBRevD::InitializeMode + ( + std::string name, + unsigned int ¤t_mode, + unsigned int flags, + unsigned int color_mode, + bool save + ) +{ + mode qmk_mode; + qmk_mode.name = name; + qmk_mode.value = current_mode++; + qmk_mode.flags = flags; + qmk_mode.color_mode = color_mode; + + if(flags & MODE_FLAG_HAS_SPEED) + { + qmk_mode.speed_min = QMK_OPENRGB_SPEED_SLOWEST; + qmk_mode.speed_max = QMK_OPENRGB_SPEED_FASTEST; + qmk_mode.speed = QMK_OPENRGB_SPEED_NORMAL; + } + if(flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + qmk_mode.colors_min = 1; + qmk_mode.colors_max = 1; + qmk_mode.colors.resize(1); + qmk_mode.colors[0] = controller->GetModeColor(); + } + + /*-----------------------------------------------------*\ + | Direct mode it the last mode on the QMK firmware | + | but we still want it to appear first on the UI | + \*-----------------------------------------------------*/ + if(flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + modes.insert(modes.begin(), qmk_mode); + } + else + { + /*-----------------------------------------------------*\ + | Every mode apart from direct is save-able | + \*-----------------------------------------------------*/ + if(save == true) + { + qmk_mode.flags = flags | MODE_FLAG_MANUAL_SAVE; + } + modes.push_back(qmk_mode); + } +} + +unsigned int RGBController_QMKOpenRGBRevD::CalculateDivisor + ( + std::vector led_points, + std::set rows, + std::set /*columns*/ + ) +{ + std::vector< std::vector > row_points(rows.size()); + for(const point_t &pt : led_points) + { + for(const int &i : rows) + { + if(pt.y == i) + { + row_points[std::distance(rows.begin(), rows.find(i))].push_back(pt); + } + } + } + + int last_pos; + std::vector distances; + for(const std::vector &row : row_points) + { + last_pos = 0; + std::for_each(row.begin(), row.end(), [&distances, &last_pos](const point_t &pt) + { + distances.push_back(std::abs(pt.x - last_pos)); + last_pos = pt.x; + }); + } + std::map counts; + for(const int &i : distances) + { + counts[i]++; + } + + /*---------------------------------------------------------*\ + | Guard against empty distances (malformed LED data) | + \*---------------------------------------------------------*/ + if(distances.empty()) + { + LOG_WARNING("[%s] No valid LED distances found, using default divisor of 1", name.c_str()); + return 1; + } + + unsigned int divisor = distances[0]; + for(const std::pair &i : counts) + { + if(counts[divisor] < i.second) + { + divisor = i.first; + } + } + + if(divisor == 0) + { + LOG_WARNING("[%s] Calculated divisor is 0, using default of 1. This may indicate malformed LED position data.", name.c_str()); + return 1; + } + + return divisor; +} + +void RGBController_QMKOpenRGBRevD::CountKeyTypes + ( + std::vector led_flags, + unsigned int total_led_count, + unsigned int& key_leds, + unsigned int& underglow_leds + ) +{ + underglow_leds = 0; + key_leds = 0; + + for(unsigned int i = 0; i < total_led_count; i++) + { + if(led_flags[i] & 2) + { + underglow_leds++; + } + else if(led_flags[i] != 0) + { + key_leds++; + } + } +} + +void RGBController_QMKOpenRGBRevD::PlaceLEDsInMaps + ( + std::set unique_rows, + std::set /*unique_cols*/, + unsigned int divisor, + std::vector led_points, + std::vector led_flags, + VectorMatrix& matrix_map_xl, + VectorMatrix& underglow_map_xl + ) +{ + matrix_map_xl = MakeEmptyMatrixMap(unique_rows.size(), (std::size_t)(std::round(255 / divisor) + 10)); + underglow_map_xl = MakeEmptyMatrixMap(unique_rows.size(), (std::size_t)(std::round(255 / divisor) + 10)); + + unsigned int x = 0; + unsigned int y = 0; + unsigned int underglow_counter = 0; + + for(unsigned int i = 0; i < controller->GetTotalNumberOfLEDs(); i++) + { + if(led_points[i].x != 255 && led_points[i].y != 255) + { + bool underglow = led_flags[i] & 2; + + x = (unsigned int)(std::round(led_points[i].x / divisor)); + y = (unsigned int)(std::distance(unique_rows.begin(), unique_rows.find(led_points[i].y))); + + if(!underglow) + { + while(matrix_map_xl[y][x] != NO_LED) + { + x++; + } + matrix_map_xl[y][x] = i; + LOG_DEBUG("[%s] Key Matrix LED %u, (%u, %u) being placed into (%u, %u)", name.c_str(), i, led_points[i].x, led_points[i].y, x, y); + } + else + { + while(underglow_map_xl[y][x] != NO_LED) + { + x++; + } + underglow_map_xl[y][x] = underglow_counter; + underglow_counter++; + LOG_DEBUG("[%s] Underglow LED %u, (%u, %u) being placed into (%u, %u)", name.c_str(), i, led_points[i].x, led_points[i].y, x, y); + } + } + } +} + +VectorMatrix RGBController_QMKOpenRGBRevD::MakeEmptyMatrixMap + ( + std::size_t height, + std::size_t width + ) +{ + std::vector > matrix_map(height); + for(std::size_t i = 0; i < height; i++) + { + for(std::size_t j = 0; j < width; j++) + { + matrix_map[i].push_back(NO_LED); + } + } + return matrix_map; +} + +void RGBController_QMKOpenRGBRevD::CleanMatrixMaps + ( + VectorMatrix& matrix_map, + VectorMatrix& underglow_map, + unsigned int height, + bool has_underglow + ) +{ + bool empty_col = true; + bool empty_col_udg = true; + bool empty_row = true; + int width = 0; + int width_udg = 0; + + std::vector empty_rows; + + bool can_break; + bool can_break_udg; + + for(unsigned int i = 0; i < height; i++) + { + empty_row = true; + can_break = false; + can_break_udg = false; + + for(int j = (int)matrix_map[i].size() - 1; j --> 0; ) + { + if(matrix_map[i][j] != NO_LED && width < (j + 1) && !can_break) + { + width = (j + 1); + can_break = true; + empty_row = false; + } + else if(matrix_map[i][j] != NO_LED) + { + empty_row = false; + } + if(underglow_map[i][j] != NO_LED && width_udg < (j + 1) && !can_break_udg) + { + width_udg = (j + 1); + can_break_udg = true; + } + if (can_break && can_break_udg) break; + } + + if(matrix_map[i][0] != NO_LED) + { + empty_col = false; + } + + if(underglow_map[i][0] != NO_LED) + { + empty_col_udg = false; + } + + if(empty_row) + { + empty_rows.push_back(i); + } + } + + unsigned int new_height = height - (unsigned int)empty_rows.size(); + width = empty_col ? width - 1 : width; + width_udg = empty_col_udg && empty_col ? width_udg - 1 : width_udg; + LOG_DEBUG("[%s] Key LED Matrix: %ux%u", name.c_str(), width, new_height); + LOG_DEBUG("[%s] Underglow LED Matrix: %ux%u", name.c_str(), width_udg, new_height); + + for(unsigned int i = (unsigned int)empty_rows.size(); i --> 0; ) + { + matrix_map.erase(matrix_map.begin()+empty_rows[i]); + } + + for(unsigned int i = 0; i < new_height; i++) + { + if(empty_col) + { + matrix_map[i].erase(matrix_map[i].begin(), matrix_map[i].begin() + 1); + } + + if(empty_col_udg && empty_col) + { + underglow_map[i].erase(underglow_map[i].begin(), underglow_map[i].begin() + 1); + } + + matrix_map[i].erase(matrix_map[i].begin()+width, matrix_map[i].end()); + + if(has_underglow) + { + underglow_map[i].erase(underglow_map[i].begin()+width_udg, underglow_map[i].end()); + } + } + + if(has_underglow) + { + for(unsigned int i = new_height; i < height; i++) + { + underglow_map[i].erase(underglow_map[i].begin()+width_udg, underglow_map[i].end()); + } + } +} + +std::vector RGBController_QMKOpenRGBRevD::FlattenMatrixMap + ( + VectorMatrix matrix_map + ) +{ + std::vector flat_map; + + for(const std::vector &row : matrix_map) + { + for(const unsigned int &item : row) + { + flat_map.push_back(item); + } + } + return flat_map; +} diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/RGBController_QMKOpenRGBRevD.h b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/RGBController_QMKOpenRGBRevD.h new file mode 100644 index 0000000..af1e562 --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/RGBController_QMKOpenRGBRevD.h @@ -0,0 +1,100 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKOpenRGBRevD.h | +| | +| RGBController for OpenRGB QMK Keyboard Protocol | +| Revision D | +| | +| Neneya 26 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBController.h" +#include "QMKOpenRGBRevDController.h" + +#define NO_LED 0xFFFFFFFF + +typedef std::vector> VectorMatrix; + +class RGBController_QMKOpenRGBRevD : public RGBController +{ +public: + RGBController_QMKOpenRGBRevD(QMKOpenRGBRevDController* controller_ptr, bool save); + ~RGBController_QMKOpenRGBRevD(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + QMKOpenRGBRevDController* controller; + std::vector flat_matrix_map; + std::vector flat_underglow_map; + + void InitializeMode + ( + std::string name, + unsigned int ¤t_mode, + unsigned int flags, + unsigned int color_mode, + bool save + ); + + unsigned int CalculateDivisor + ( + std::vector led_points, + std::set rows, + std::set columns + ); + + void CountKeyTypes + ( + std::vector led_flags, + unsigned int total_led_count, + unsigned int& key_leds, + unsigned int& underglow_leds + ); + + void PlaceLEDsInMaps + ( + std::set unique_rows, + std::set unique_cols, + unsigned int divisor, + std::vector led_points, + std::vector led_flags, + VectorMatrix& matrix_map_xl, + VectorMatrix& underglow_map_xl + ); + + VectorMatrix MakeEmptyMatrixMap + ( + std::size_t height, + std::size_t width + ); + + void CleanMatrixMaps + ( + VectorMatrix& matrix_map, + VectorMatrix& underglow_map, + unsigned int height, + bool has_underglow + ); + + std::vector FlattenMatrixMap + ( + VectorMatrix matrix_map + ); +}; diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevEController/RGBController_QMKOpenRGBRevE.cpp b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevEController/RGBController_QMKOpenRGBRevE.cpp new file mode 100644 index 0000000..dbabf20 --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevEController/RGBController_QMKOpenRGBRevE.cpp @@ -0,0 +1,820 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKOpenRGBRevE.cpp | +| | +| RGBController for OpenRGB QMK Keyboard Protocol | +| Revision E | +| | +| Neneya 26 Dec 2021 | +| HorrorTroll 11 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "hsv.h" +#include "LogManager.h" +#include "RGBController_QMKOpenRGBRevE.h" + +RGBController_QMKOpenRGBRevE::RGBController_QMKOpenRGBRevE(QMKOpenRGBRevDController* controller_ptr, bool save) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = controller->GetDeviceVendor(); + description = "QMK OpenRGB Device (Protocol Version " + std::to_string(controller->GetProtocolVersion()) + ")"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->GetLocation(); + version = controller->GetQMKVersion(); + + unsigned int current_mode = 1; + std::vector enabled_modes = controller->GetEnabledModes(); + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_COLOR) != enabled_modes.end()) + { + InitializeMode("Static", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_ALPHA_MOD) != enabled_modes.end()) + { + InitializeMode("Alpha Mod", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_GRADIENT_UP_DOWN) != enabled_modes.end()) + { + InitializeMode("Gradient Up Down", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_GRADIENT_LEFT_RIGHT) != enabled_modes.end()) + { + InitializeMode("Gradient Left Right", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BREATHING) != enabled_modes.end()) + { + InitializeMode("Breathing", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_SAT) != enabled_modes.end()) + { + InitializeMode("Band Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_VAL) != enabled_modes.end()) + { + InitializeMode("Band Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_PINWHEEL_SAT) != enabled_modes.end()) + { + InitializeMode("Band Pinwheel Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_PINWHEEL_VAL) != enabled_modes.end()) + { + InitializeMode("Band Pinwheel Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_SPIRAL_SAT) != enabled_modes.end()) + { + InitializeMode("Band Spiral Saturation", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_BAND_SPIRAL_VAL) != enabled_modes.end()) + { + InitializeMode("Band Spiral Value", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_ALL) != enabled_modes.end()) + { + InitializeMode("Cycle All", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_LEFT_RIGHT) != enabled_modes.end()) + { + InitializeMode("Cycle Left Right", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_UP_DOWN) != enabled_modes.end()) + { + InitializeMode("Cycle Up Down", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINBOW_MOVING_CHEVRON) != enabled_modes.end()) + { + InitializeMode("Rainbow Moving Chevron", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_OUT_IN) != enabled_modes.end()) + { + InitializeMode("Cycle Out In", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_OUT_IN_DUAL) != enabled_modes.end()) + { + InitializeMode("Cycle Out In Dual", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_PINWHEEL) != enabled_modes.end()) + { + InitializeMode("Cycle Pinwheel", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_CYCLE_SPIRAL) != enabled_modes.end()) + { + InitializeMode("Cycle Spiral", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_DUAL_BEACON) != enabled_modes.end()) + { + InitializeMode("Dual Beacon", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINBOW_BEACON) != enabled_modes.end()) + { + InitializeMode("Rainbow Beacon", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINBOW_PINWHEELS) != enabled_modes.end()) + { + InitializeMode("Rainbow Pinwheels", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_RAINDROPS) != enabled_modes.end()) + { + InitializeMode("Raindrops", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_JELLYBEAN_RAINDROPS) != enabled_modes.end()) + { + InitializeMode("Jellybean Raindrops", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_HUE_BREATHING) != enabled_modes.end()) + { + InitializeMode("Hue Breathing", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_HUE_PENDULUM) != enabled_modes.end()) + { + InitializeMode("Hue Pendulum", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_HUE_WAVE) != enabled_modes.end()) + { + InitializeMode("Hue Wave", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_TYPING_HEATMAP) != enabled_modes.end()) + { + InitializeMode("Typing Heatmap", current_mode, 0, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_DIGITAL_RAIN) != enabled_modes.end()) + { + InitializeMode("Digital Rain", current_mode, 0, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_SIMPLE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Simple", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_WIDE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Wide", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTIWIDE) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Wide", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_CROSS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Cross", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTICROSS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Cross", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_NEXUS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Nexus", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_REACTIVE_MULTINEXUS) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Nexus", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SPLASH) != enabled_modes.end()) + { + InitializeMode("Rainbow Reactive Splash", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_MULTISPLASH) != enabled_modes.end()) + { + InitializeMode("Rainbow Reactive Multi Splash", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_SPLASH) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Splash", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_SOLID_MULTISPLASH) != enabled_modes.end()) + { + InitializeMode("Solid Reactive Multi Splash", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_PIXEL_RAIN) != enabled_modes.end()) + { + InitializeMode("Pixel Rain", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_PIXEL_FLOW) != enabled_modes.end()) + { + InitializeMode("Pixel Flow", current_mode, MODE_FLAG_HAS_SPEED, MODE_COLORS_NONE, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_PIXEL_FRACTAL) != enabled_modes.end()) + { + InitializeMode("Pixel Fractal", current_mode, MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED, MODE_COLORS_MODE_SPECIFIC, save); + } + + if(std::find(enabled_modes.begin(), enabled_modes.end(), QMK_OPENRGB_MODE_OPENRGB_DIRECT) != enabled_modes.end()) + { + InitializeMode("Direct", current_mode, MODE_FLAG_HAS_PER_LED_COLOR, MODE_COLORS_PER_LED, save); + } + + /*-----------------------------------------------------*\ + | As we are insertting direct mode at index 0 | + | for it to be the first mode in the UI there will | + | be a mismatch between the values. QMK has direct | + | mode last in order, while in OpenRGB it's first. | + \*-----------------------------------------------------*/ + if(controller->GetMode() == (current_mode - 1)) + { + active_mode = 0; + } + else + { + active_mode = controller->GetMode(); + } + + SetupZones(); +} + +RGBController_QMKOpenRGBRevE::~RGBController_QMKOpenRGBRevE() +{ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } +} + +void RGBController_QMKOpenRGBRevE::SetupZones() +{ + /*---------------------------------------------------------*\ + | Get the number of LEDs from the device | + \*---------------------------------------------------------*/ + const unsigned int total_number_of_leds = controller->GetTotalNumberOfLEDs(); + const unsigned int total_number_of_leds_with_empty_space = controller->GetTotalNumberOfLEDsWithEmptySpace(); + + LOG_INFO("[%s] Keyboard has %u LEDs total", name.c_str(), total_number_of_leds); + + /*---------------------------------------------------------*\ + | Get information for each LED | + \*---------------------------------------------------------*/ + controller->GetLEDInfo(std::max(total_number_of_leds, total_number_of_leds_with_empty_space)); + + /*---------------------------------------------------------*\ + | Get LED vectors from controller | + \*---------------------------------------------------------*/ + std::vector led_points = controller->GetLEDPoints(); + std::vector led_flags = controller->GetLEDFlags(); + std::vector led_names = controller->GetLEDNames(); + std::vector led_values = controller->GetLEDValues(); + + /*---------------------------------------------------------*\ + | Count key LEDs and underglow LEDs | + \*---------------------------------------------------------*/ + unsigned int number_of_key_leds; + unsigned int number_of_underglow_leds; + + CountKeyTypes(led_flags, total_number_of_leds, number_of_key_leds, number_of_underglow_leds); + + /*---------------------------------------------------------*\ + | Count total LEDs and check if underglow exists | + \*---------------------------------------------------------*/ + unsigned int number_of_leds = number_of_key_leds + number_of_underglow_leds; + bool has_underglow = number_of_underglow_leds > 0; + LOG_INFO("[%s] Keyboard has %u underglow LEDs", name.c_str(), number_of_underglow_leds); + + /*---------------------------------------------------------*\ + | Create sets for row and column position values | + \*---------------------------------------------------------*/ + std::set rows, columns; + for (unsigned int i = 0; i < number_of_leds; i++) + { + rows.insert(led_points[i].y); + columns.insert(led_points[i].x); + } + + /*---------------------------------------------------------*\ + | Calculate matrix map from QMK positions | + \*---------------------------------------------------------*/ + unsigned int divisor = CalculateDivisor(led_points, rows, columns); + LOG_DEBUG("[%s] Distance between standard keys calculated to be %u", name.c_str(), divisor); + + VectorMatrix matrix_map; + VectorMatrix underglow_map; + + PlaceLEDsInMaps(rows, columns, divisor, led_points, led_flags, matrix_map, underglow_map); + CleanMatrixMaps(matrix_map, underglow_map, (unsigned int)rows.size(), has_underglow); + + /*---------------------------------------------------------*\ + | These vectors are class members because if they go out of | + | scope, the underlying array (used by each zones' | + | matrix_map) is unallocated. | + \*---------------------------------------------------------*/ + flat_matrix_map = FlattenMatrixMap(matrix_map); + flat_underglow_map = FlattenMatrixMap(underglow_map); + + /*---------------------------------------------------------*\ + | Create Keyboard zone | + \*---------------------------------------------------------*/ + zone keys_zone; + keys_zone.name = "Keyboard"; + keys_zone.type = ZONE_TYPE_MATRIX; + keys_zone.leds_min = number_of_key_leds; + keys_zone.leds_max = keys_zone.leds_min; + keys_zone.leds_count = keys_zone.leds_min; + keys_zone.matrix_map = new matrix_map_type; + keys_zone.matrix_map->width = (unsigned int)matrix_map[0].size(); + keys_zone.matrix_map->height = (unsigned int)matrix_map.size(); + keys_zone.matrix_map->map = flat_matrix_map.data(); + zones.push_back(keys_zone); + + /*---------------------------------------------------------*\ + | Create Underglow zone if it exists | + \*---------------------------------------------------------*/ + if(has_underglow) + { + zone underglow_zone; + underglow_zone.name = "Underglow"; + underglow_zone.type = ZONE_TYPE_MATRIX; + underglow_zone.leds_min = number_of_underglow_leds; + underglow_zone.leds_max = underglow_zone.leds_min; + underglow_zone.leds_count = underglow_zone.leds_min; + underglow_zone.matrix_map = new matrix_map_type; + underglow_zone.matrix_map->width = (unsigned int)underglow_map[0].size(); + underglow_zone.matrix_map->height = (unsigned int)underglow_map.size(); + underglow_zone.matrix_map->map = flat_underglow_map.data(); + zones.push_back(underglow_zone); + } + + /*---------------------------------------------------------*\ + | Create LEDs | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < number_of_leds; led_idx++) + { + led keyboard_led; + + if(led_idx < led_names.size()) + { + keyboard_led.name = led_names[led_idx]; + } + if(led_idx < led_values.size()){ + keyboard_led.value = led_values[led_idx]; + } + else + { + keyboard_led.value = led_idx; + } + + leds.push_back(keyboard_led); + } + + /*---------------------------------------------------------*\ + | Setup Colors | + \*---------------------------------------------------------*/ + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize colors from device values | + \*---------------------------------------------------------*/ + for(unsigned int i = 0; i < leds.size(); i++) + { + colors[i] = controller->GetLEDColors()[i]; + } +} + +void RGBController_QMKOpenRGBRevE::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_QMKOpenRGBRevE::DeviceUpdateLEDs() +{ + controller->DirectModeSetLEDs(colors, controller->GetTotalNumberOfLEDs()); +} + +void RGBController_QMKOpenRGBRevE::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_QMKOpenRGBRevE::UpdateSingleLED(int led) +{ + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->DirectModeSetSingleLED(led, red, grn, blu); +} + +void RGBController_QMKOpenRGBRevE::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, 127, false); + } + else if(modes[active_mode].color_mode == MODE_COLORS_NONE) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, modes[active_mode].speed, false); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + RGBColor rgb_color = modes[active_mode].colors[0]; + hsv_t hsv_color; + rgb2hsv(rgb_color, &hsv_color); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->SetMode(hsv_color, modes[active_mode].value, modes[active_mode].speed, false); + } + else + { + controller->SetMode(hsv_color, modes[active_mode].value, 127, false); + } + } +} + +void RGBController_QMKOpenRGBRevE::DeviceSaveMode() +{ + if(modes[active_mode].color_mode == MODE_COLORS_NONE) + { + controller->SetMode({ 0, 255, 255 }, modes[active_mode].value, modes[active_mode].speed, true); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + RGBColor rgb_color = modes[active_mode].colors[0]; + hsv_t hsv_color; + rgb2hsv(rgb_color, &hsv_color); + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + controller->SetMode(hsv_color, modes[active_mode].value, modes[active_mode].speed, true); + } + else + { + controller->SetMode(hsv_color, modes[active_mode].value, 127, true); + } + } +} + +void RGBController_QMKOpenRGBRevE::InitializeMode + ( + std::string name, + unsigned int ¤t_mode, + unsigned int flags, + unsigned int color_mode, + bool save + ) +{ + mode qmk_mode; + qmk_mode.name = name; + qmk_mode.value = current_mode++; + qmk_mode.flags = flags; + qmk_mode.color_mode = color_mode; + + if(flags & MODE_FLAG_HAS_SPEED) + { + qmk_mode.speed_min = QMK_OPENRGB_SPEED_SLOWEST; + qmk_mode.speed_max = QMK_OPENRGB_SPEED_FASTEST; + qmk_mode.speed = QMK_OPENRGB_SPEED_NORMAL; + } + if(flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + qmk_mode.colors_min = 1; + qmk_mode.colors_max = 1; + qmk_mode.colors.resize(1); + qmk_mode.colors[0] = controller->GetModeColor(); + } + + /*-----------------------------------------------------*\ + | Direct mode it the last mode on the QMK firmware | + | but we still want it to appear first on the UI | + \*-----------------------------------------------------*/ + if(flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + modes.insert(modes.begin(), qmk_mode); + } + else + { + /*-----------------------------------------------------*\ + | Every mode apart from direct is save-able | + \*-----------------------------------------------------*/ + if(save == true) + { + qmk_mode.flags = flags | MODE_FLAG_MANUAL_SAVE; + } + modes.push_back(qmk_mode); + } +} + +unsigned int RGBController_QMKOpenRGBRevE::CalculateDivisor + ( + std::vector led_points, + std::set rows, + std::set /*columns*/ + ) +{ + std::vector< std::vector > row_points(rows.size()); + for(const point_t &pt : led_points) + { + for(const int &i : rows) + { + if(pt.y == i) + { + row_points[std::distance(rows.begin(), rows.find(i))].push_back(pt); + } + } + } + + int last_pos; + std::vector distances; + for(const std::vector &row : row_points) + { + last_pos = 0; + std::for_each(row.begin(), row.end(), [&distances, &last_pos](const point_t &pt) + { + distances.push_back(std::abs(pt.x - last_pos)); + last_pos = pt.x; + }); + } + std::map counts; + for(const int &i : distances) + { + counts[i]++; + } + + /*---------------------------------------------------------*\ + | Guard against empty distances (malformed LED data) | + \*---------------------------------------------------------*/ + if(distances.empty()) + { + LOG_WARNING("[%s] No valid LED distances found, using default divisor of 1", name.c_str()); + return 1; + } + + unsigned int divisor = distances[0]; + for(const std::pair &i : counts) + { + if(counts[divisor] < i.second) + { + divisor = i.first; + } + } + + if(divisor == 0) + { + LOG_WARNING("[%s] Calculated divisor is 0, using default of 1. This may indicate malformed LED position data.", name.c_str()); + return 1; + } + + return divisor; +} + +void RGBController_QMKOpenRGBRevE::CountKeyTypes + ( + std::vector led_flags, + unsigned int total_led_count, + unsigned int& key_leds, + unsigned int& underglow_leds + ) +{ + underglow_leds = 0; + key_leds = 0; + + for(unsigned int i = 0; i < total_led_count; i++) + { + if(led_flags[i] & 2) + { + underglow_leds++; + } + else if(led_flags[i] != 0) + { + key_leds++; + } + } +} + +void RGBController_QMKOpenRGBRevE::PlaceLEDsInMaps + ( + std::set unique_rows, + std::set /*unique_cols*/, + unsigned int divisor, + std::vector led_points, + std::vector led_flags, + VectorMatrix& matrix_map_xl, + VectorMatrix& underglow_map_xl + ) +{ + matrix_map_xl = MakeEmptyMatrixMap(unique_rows.size(), (std::size_t)(std::round(255 / divisor) + 10)); + underglow_map_xl = MakeEmptyMatrixMap(unique_rows.size(), (std::size_t)(std::round(255 / divisor) + 10)); + + unsigned int x = 0; + unsigned int y = 0; + unsigned int underglow_counter = 0; + + for(unsigned int i = 0; i < controller->GetTotalNumberOfLEDs(); i++) + { + if(led_points[i].x != 255 && led_points[i].y != 255) + { + bool underglow = led_flags[i] & 2; + + x = (unsigned int)(std::round(led_points[i].x / divisor)); + y = (unsigned int)(std::distance(unique_rows.begin(), unique_rows.find(led_points[i].y))); + + if(!underglow) + { + while(matrix_map_xl[y][x] != NO_LED) + { + x++; + } + matrix_map_xl[y][x] = i; + LOG_DEBUG("[%s] Key Matrix LED %u, (%u, %u) being placed into (%u, %u)", name.c_str(), i, led_points[i].x, led_points[i].y, x, y); + } + else + { + while(underglow_map_xl[y][x] != NO_LED) + { + x++; + } + underglow_map_xl[y][x] = underglow_counter; + underglow_counter++; + LOG_DEBUG("[%s] Underglow LED %u, (%u, %u) being placed into (%u, %u)", name.c_str(), i, led_points[i].x, led_points[i].y, x, y); + } + } + } +} + +VectorMatrix RGBController_QMKOpenRGBRevE::MakeEmptyMatrixMap + ( + std::size_t height, + std::size_t width + ) +{ + std::vector > matrix_map(height); + for(std::size_t i = 0; i < height; i++) + { + for(std::size_t j = 0; j < width; j++) + { + matrix_map[i].push_back(NO_LED); + } + } + return matrix_map; +} + +void RGBController_QMKOpenRGBRevE::CleanMatrixMaps + ( + VectorMatrix& matrix_map, + VectorMatrix& underglow_map, + unsigned int height, + bool has_underglow + ) +{ + bool empty_col = true; + bool empty_col_udg = true; + bool empty_row = true; + int width = 0; + int width_udg = 0; + + std::vector empty_rows; + + bool can_break; + bool can_break_udg; + + for(unsigned int i = 0; i < height; i++) + { + empty_row = true; + can_break = false; + can_break_udg = false; + + for(int j = (int)matrix_map[i].size() - 1; j --> 0; ) + { + if(matrix_map[i][j] != NO_LED && width < (j + 1) && !can_break) + { + width = (j + 1); + can_break = true; + empty_row = false; + } + else if(matrix_map[i][j] != NO_LED) + { + empty_row = false; + } + if(underglow_map[i][j] != NO_LED && width_udg < (j + 1) && !can_break_udg) + { + width_udg = (j + 1); + can_break_udg = true; + } + if (can_break && can_break_udg) break; + } + + if(matrix_map[i][0] != NO_LED) + { + empty_col = false; + } + + if(underglow_map[i][0] != NO_LED) + { + empty_col_udg = false; + } + + if(empty_row) + { + empty_rows.push_back(i); + } + } + + unsigned int new_height = height - (unsigned int)empty_rows.size(); + width = empty_col ? width - 1 : width; + width_udg = empty_col_udg && empty_col ? width_udg - 1 : width_udg; + LOG_DEBUG("[%s] Key LED Matrix: %ux%u", name.c_str(), width, new_height); + LOG_DEBUG("[%s] Underglow LED Matrix: %ux%u", name.c_str(), width_udg, new_height); + + for(unsigned int i = (unsigned int)empty_rows.size(); i --> 0; ) + { + matrix_map.erase(matrix_map.begin()+empty_rows[i]); + } + + for(unsigned int i = 0; i < new_height; i++) + { + if(empty_col) + { + matrix_map[i].erase(matrix_map[i].begin(), matrix_map[i].begin() + 1); + } + + if(empty_col_udg && empty_col) + { + underglow_map[i].erase(underglow_map[i].begin(), underglow_map[i].begin() + 1); + } + + matrix_map[i].erase(matrix_map[i].begin()+width, matrix_map[i].end()); + + if(has_underglow) + { + underglow_map[i].erase(underglow_map[i].begin()+width_udg, underglow_map[i].end()); + } + } + + if(has_underglow) + { + for(unsigned int i = new_height; i < height; i++) + { + underglow_map[i].erase(underglow_map[i].begin()+width_udg, underglow_map[i].end()); + } + } +} + +std::vector RGBController_QMKOpenRGBRevE::FlattenMatrixMap + ( + VectorMatrix matrix_map + ) +{ + std::vector flat_map; + + for(const std::vector &row : matrix_map) + { + for(const unsigned int &item : row) + { + flat_map.push_back(item); + } + } + return flat_map; +} diff --git a/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevEController/RGBController_QMKOpenRGBRevE.h b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevEController/RGBController_QMKOpenRGBRevE.h new file mode 100644 index 0000000..a3b766f --- /dev/null +++ b/Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevEController/RGBController_QMKOpenRGBRevE.h @@ -0,0 +1,101 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKOpenRGBRevE.h | +| | +| RGBController for OpenRGB QMK Keyboard Protocol | +| Revision E | +| | +| Neneya 26 Dec 2021 | +| HorrorTroll 11 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBController.h" +#include "QMKOpenRGBRevDController.h" + +#define NO_LED 0xFFFFFFFF + +typedef std::vector> VectorMatrix; + +class RGBController_QMKOpenRGBRevE : public RGBController +{ +public: + RGBController_QMKOpenRGBRevE(QMKOpenRGBRevDController* controller_ptr, bool save); + ~RGBController_QMKOpenRGBRevE(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + QMKOpenRGBRevDController* controller; + std::vector flat_matrix_map; + std::vector flat_underglow_map; + + void InitializeMode + ( + std::string name, + unsigned int ¤t_mode, + unsigned int flags, + unsigned int color_mode, + bool save + ); + + unsigned int CalculateDivisor + ( + std::vector led_points, + std::set rows, + std::set columns + ); + + void CountKeyTypes + ( + std::vector led_flags, + unsigned int total_led_count, + unsigned int& key_leds, + unsigned int& underglow_leds + ); + + void PlaceLEDsInMaps + ( + std::set unique_rows, + std::set unique_cols, + unsigned int divisor, + std::vector led_points, + std::vector led_flags, + VectorMatrix& matrix_map_xl, + VectorMatrix& underglow_map_xl + ); + + VectorMatrix MakeEmptyMatrixMap + ( + std::size_t height, + std::size_t width + ); + + void CleanMatrixMaps + ( + VectorMatrix& matrix_map, + VectorMatrix& underglow_map, + unsigned int height, + bool has_underglow + ); + + std::vector FlattenMatrixMap + ( + VectorMatrix matrix_map + ); +}; diff --git a/Controllers/QMKController/QMKViaCommands.h b/Controllers/QMKController/QMKViaCommands.h new file mode 100644 index 0000000..16be94b --- /dev/null +++ b/Controllers/QMKController/QMKViaCommands.h @@ -0,0 +1,71 @@ +/*---------------------------------------------------------*\ +| QMKViaCommands.h | +| | +| List of QMK VIA command values | +| | +| Adam Honse +#include "hsv.h" +#include "QMKVialRGBController.h" +#include "StringUtils.h" + +/*---------------------------------------------------------*\ +| Portions of this controller adapted from Raspberry Pi | +| RPiKeyboardConfig utility: | +| https://github.com/raspberrypi/rpi-keyboard-config | +\*---------------------------------------------------------*/ + +QMKVialRGBController::QMKVialRGBController(hid_device *dev_handle, const char *path) +{ + /*-----------------------------------------------------*\ + | Initialize controller fields | + \*-----------------------------------------------------*/ + dev = dev_handle; + location = path; + supported = false; + + /*-----------------------------------------------------*\ + | Read product string | + \*-----------------------------------------------------*/ + wchar_t product_string[256]; + + int ret = hid_get_product_string(dev, product_string, 256); + + if(ret != 0) + { + name = ""; + } + else + { + name = StringUtils::wstring_to_string(product_string); + } + + /*-----------------------------------------------------*\ + | Read vendor string | + \*-----------------------------------------------------*/ + wchar_t vendor_string[256]; + + ret = hid_get_manufacturer_string(dev, vendor_string, 256); + + if(ret != 0) + { + vendor = ""; + } + else + { + vendor = StringUtils::wstring_to_string(vendor_string); + } + + /*-----------------------------------------------------*\ + | Read serial string | + \*-----------------------------------------------------*/ + wchar_t serial_string[256]; + + ret = hid_get_serial_number_string(dev, serial_string, 256); + + if(ret != 0) + { + serial = ""; + } + else + { + serial = StringUtils::wstring_to_string(serial_string); + } + + /*-----------------------------------------------------*\ + | Get VIA, Vial, and VialRGB information | + \*-----------------------------------------------------*/ + CmdGetViaProtocolVersion(&via_protocol_version); + + if(via_protocol_version < 9) + { + return; + } + + CmdGetVialInfo(&vial_protocol_version, &keyboard_uid, &vialrgb_flag); + + if((vial_protocol_version < 4) || ((vialrgb_flag & 1) == 0)) + { + supported = false; + return; + } + + CmdGetVialRGBInfo(&vialrgb_protocol_version, &maximum_brightness); + + /*-----------------------------------------------------*\ + | Get list of supported effects | + \*-----------------------------------------------------*/ + CmdGetSupportedEffects(); + + /*-----------------------------------------------------*\ + | Get count of LEDs | + \*-----------------------------------------------------*/ + CmdGetNumberLEDs(&number_leds); + + /*-----------------------------------------------------*\ + | Get info and keycode for all LEDs | + \*-----------------------------------------------------*/ + for(unsigned short led_index = 0; led_index < number_leds; led_index++) + { + led_info.push_back(CmdGetLEDInfo(led_index)); + keycodes.push_back(CmdGetKeycode(0, led_info[led_index].row, led_info[led_index].col)); + } + + supported = true; +} + +QMKVialRGBController::~QMKVialRGBController() +{ + hid_close(dev); +} + +std::string QMKVialRGBController::GetLocation() +{ + return("HID: " + location); +} + +std::string QMKVialRGBController::GetName() +{ + return(name); +} + +std::string QMKVialRGBController::GetSerial() +{ + return(serial); +} + +std::string QMKVialRGBController::GetVendor() +{ + return(vendor); +} + +std::string QMKVialRGBController::GetVersion() +{ + /*-----------------------------------------------------*\ + | Format UID string | + \*-----------------------------------------------------*/ + char uid_buf[17]; + snprintf(uid_buf, sizeof(uid_buf), "%016llX", keyboard_uid); + + /*-----------------------------------------------------*\ + | Format multi-line version text | + \*-----------------------------------------------------*/ + return("VIA: " + std::to_string(via_protocol_version) + "\r\n" + + "Vial: " + std::to_string(vial_protocol_version) + "\r\n" + + "VialRGB: " + std::to_string(vialrgb_protocol_version) + "\r\n" + + "UID: " + uid_buf); +} + +bool QMKVialRGBController::GetSupported() +{ + return(supported); +} + +unsigned short QMKVialRGBController::GetEffect(std::size_t effect_idx) +{ + return(supported_effects[effect_idx]); +} + +std::size_t QMKVialRGBController::GetEffectCount() +{ + return(supported_effects.size()); +} + +unsigned short QMKVialRGBController::GetKeycode(unsigned short led_index) +{ + return(keycodes[led_index]); +} + +unsigned short QMKVialRGBController::GetLEDCount() +{ + return(number_leds); +} + +qmk_rgb_matrix_led_info QMKVialRGBController::GetLEDInfo(unsigned short led_index) +{ + return(led_info[led_index]); +} + +void QMKVialRGBController::GetMode + ( + unsigned short* mode, + unsigned char* speed, + unsigned char* hue, + unsigned char* sat, + unsigned char* val + ) +{ + CmdGetMode(mode, speed, hue, sat, val); +} + +void QMKVialRGBController::SendLEDs + ( + unsigned short number_leds, + RGBColor* color_data + ) +{ + unsigned short led_start_index = 0; + unsigned char number_packet_leds = 9; + + while(led_start_index < number_leds) + { + if((number_leds - led_start_index) < 9) + { + number_packet_leds = (number_leds - led_start_index); + } + + CmdSendLEDs(led_start_index, number_packet_leds, &color_data[led_start_index]); + + led_start_index += number_packet_leds; + } +} + +void QMKVialRGBController::SetMode + ( + unsigned short mode, + unsigned char speed, + unsigned char hue, + unsigned char sat, + unsigned char val + ) +{ + CmdSetMode(mode, speed, hue, sat, val); +} + +unsigned short QMKVialRGBController::CmdGetKeycode + ( + unsigned char layer, + unsigned char row, + unsigned char col + ) +{ + unsigned char data[5]; + unsigned short keycode; + + data[0] = row; + data[1] = col; + + SendCheckCommand(CMD_VIA_DYNAMIC_KEYMAP_GET_KEYCODE, layer, data, 2, data, 5); + + memcpy(&keycode, &data[3], sizeof(unsigned short)); + + return(keycode); +} + +qmk_rgb_matrix_led_info QMKVialRGBController::CmdGetLEDInfo + ( + unsigned short led_index + ) +{ + qmk_rgb_matrix_led_info data; + + SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_LED_INFO, (unsigned char*)&led_index, sizeof(led_index), (unsigned char*)&data, sizeof(data)); + + return(data); +} + +void QMKVialRGBController::CmdGetMode + ( + unsigned short* mode, + unsigned char* speed, + unsigned char* hue, + unsigned char* sat, + unsigned char* val + ) +{ + unsigned char data[6]; + + SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_MODE, NULL, 0, data, 6); + + memcpy(mode, &data[0], sizeof(unsigned short)); + *speed = data[2]; + *hue = data[3]; + *sat = data[4]; + *val = data[5]; +} + +void QMKVialRGBController::CmdGetNumberLEDs + ( + unsigned short* number_leds + ) +{ + SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_NUMBER_LEDS, NULL, 0, (unsigned char*)number_leds, sizeof(unsigned short)); +} + +void QMKVialRGBController::CmdGetSupportedEffects() +{ + unsigned short packet_effects[15]; + unsigned short max_effect = 0; + + supported_effects.clear(); + + supported_effects.push_back(0); + + while(max_effect < VIALRGB_EFFECT_SKIP) + { + SendCheckCommand(CMD_LIGHTING_GET_VALUE, VIALRGB_GET_SUPPORTED, (unsigned char*)&max_effect, sizeof(max_effect), (unsigned char*)packet_effects, sizeof(packet_effects)); + + for(unsigned int effect_idx = 0; effect_idx < 15; effect_idx++) + { + if(packet_effects[effect_idx] == VIALRGB_EFFECT_SKIP) + { + return; + } + supported_effects.push_back(packet_effects[effect_idx]); + max_effect = packet_effects[effect_idx]; + } + } +} + +void QMKVialRGBController::CmdGetVialInfo + ( + unsigned int* vial_protocol, + unsigned long long* keyboard_uid, + unsigned char* vialrgb_flag + ) +{ + unsigned char data[sizeof(int) + sizeof(unsigned long long) + sizeof(unsigned char)]; + + SendCommand(CMD_VIAL_COMMAND, VIAL_GET_KEYBOARD_ID, NULL, 0, data, sizeof(data)); + + memcpy(vial_protocol, &data[0], sizeof(int)); + memcpy(keyboard_uid, &data[sizeof(int)], sizeof(unsigned long long)); + memcpy(vialrgb_flag, &data[sizeof(int) + sizeof(unsigned long long)], sizeof(unsigned char)); +} + +void QMKVialRGBController::CmdGetVialRGBInfo + ( + unsigned short* vialrgb_protocol_version, + unsigned char* maximum_brightness + ) +{ + unsigned char data[sizeof(unsigned short) + sizeof(unsigned char)]; + + SendCommand(CMD_VIAL_COMMAND, VIAL_GET_KEYBOARD_ID, NULL, 0, data, sizeof(data)); + + memcpy(vialrgb_protocol_version, &data[0], sizeof(unsigned short)); + memcpy(maximum_brightness, &data[sizeof(unsigned char)], sizeof(unsigned short)); +} + +void QMKVialRGBController::CmdGetViaProtocolVersion + ( + unsigned short* via_protocol_version + ) +{ + SendCheckCommand(CMD_GET_PROTOCOL_VERSION, 0, NULL, 0, (unsigned char*)via_protocol_version, sizeof(unsigned int)); +} + +void QMKVialRGBController::CmdSendLEDs + ( + unsigned short start_index, + unsigned char number_leds, + RGBColor* color_data + ) +{ + unsigned char data[30]; + + memcpy(&data[0], &start_index, sizeof(start_index)); + memcpy(&data[2], &number_leds, sizeof(number_leds)); + + if(number_leds > 9) + { + number_leds = 9; + } + + for(unsigned char led_index = 0; led_index < number_leds; led_index++) + { + /*-------------------------------------------------*\ + | VialRGB sends direct packets in HSV for some | + | inexplicable reason, so do the RGB to HSV | + | conversion before sending | + \*-------------------------------------------------*/ + hsv_t hsv_color; + rgb2hsv(color_data[led_index], &hsv_color); + + data[3 + (led_index * 3)] = (unsigned char)((float)hsv_color.hue * (256.0f / 360.0f)); + data[4 + (led_index * 3)] = hsv_color.saturation; + data[5 + (led_index * 3)] = hsv_color.value; + } + + SendCheckCommand(CMD_LIGHTING_SET_VALUE, VIALRGB_DIRECT_FASTSET, data, sizeof(data), NULL, 0); +} + +void QMKVialRGBController::CmdSetMode + ( + unsigned short mode, + unsigned char speed, + unsigned char hue, + unsigned char sat, + unsigned char val + ) +{ + unsigned char data[6]; + + memcpy(&data[0], &mode, sizeof(unsigned short)); + + data[2] = speed; + data[3] = hue; + data[4] = sat; + data[5] = val; + + SendCommand(CMD_LIGHTING_SET_VALUE, VIALRGB_SET_MODE, data, sizeof(data), NULL, 0); +} + +int QMKVialRGBController::SendCommand + ( + unsigned char cmd, + unsigned char subcmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ) +{ + unsigned char usb_buf[MSG_LEN + 1]; + + memset(usb_buf, 0, sizeof(usb_buf)); + + usb_buf[0] = 0x00; + usb_buf[1] = cmd; + usb_buf[2] = subcmd; + + memcpy(&usb_buf[3], data_in, data_in_size); + + hid_write(dev, usb_buf, sizeof(usb_buf)); + int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf)-1, 1000); + + memcpy(data_out, &usb_buf[0], data_out_size); + + return(bytes_received); +} + +int QMKVialRGBController::SendCheckCommand + ( + unsigned char cmd, + unsigned char subcmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ) +{ + unsigned char usb_buf[MSG_LEN + 1]; + + memset(usb_buf, 0, sizeof(usb_buf)); + + usb_buf[0] = 0x00; + usb_buf[1] = cmd; + usb_buf[2] = subcmd; + + memcpy(&usb_buf[3], data_in, data_in_size); + + hid_write(dev, usb_buf, sizeof(usb_buf)); + int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf)-1, 1000); + + memcpy(data_out, &usb_buf[2], data_out_size); + + return(bytes_received); +} diff --git a/Controllers/QMKController/QMKVialRGBController/QMKVialRGBController.h b/Controllers/QMKController/QMKVialRGBController/QMKVialRGBController.h new file mode 100644 index 0000000..8316fc5 --- /dev/null +++ b/Controllers/QMKController/QMKVialRGBController/QMKVialRGBController.h @@ -0,0 +1,245 @@ +/*---------------------------------------------------------*\ +| QMKVialRGBController.h | +| | +| Driver for VialRGB QMK Keyboard Protocol | +| | +| Adam Honse +#include "ResourceManager.h" +#include "QMKCommon.h" +#include "RGBController.h" + +#define MSG_LEN 32 + +enum +{ + CMD_GET_PROTOCOL_VERSION = 0x01, + CMD_GET_KEYBOARD_VALUE = 0x02, + CMD_SET_KEYBOARD_VALUE = 0x03, + CMD_VIA_DYNAMIC_KEYMAP_GET_KEYCODE = 0x04, + CMD_VIA_DYNAMIC_KEYMAP_SET_KEYCODE = 0x05, + CMD_VIA_DYNAMIC_KEYMAP_RESET = 0x06, + CMD_LIGHTING_SET_VALUE = 0x07, + CMD_LIGHTING_GET_VALUE = 0x08, + CMD_VIAL_COMMAND = 0xFE, +}; + +enum +{ + VIAL_GET_KEYBOARD_ID = 0x00, + VIAL_GET_SIZE = 0x01, + VIAL_GET_DEFINITION = 0x02, + VIAL_GET_UNLOCK_STATUS = 0x05, + VIAL_UNLOCK_START = 0x06, + VIAL_UNLOCK_POLL = 0x07, + VIAL_LOCK = 0x08, + VIALRGB_GET_INFO = 0x40, + VIALRGB_GET_MODE = 0x41, + VIALRGB_GET_SUPPORTED = 0x42, + VIALRGB_GET_NUMBER_LEDS = 0x43, + VIALRGB_GET_LED_INFO = 0x44, + VIALRGB_SET_MODE = 0x41, + VIALRGB_DIRECT_FASTSET = 0x42, +}; + +enum +{ + VIALRGB_EFFECT_OFF, + VIALRGB_EFFECT_DIRECT, + VIALRGB_EFFECT_SOLID_COLOR, + VIALRGB_EFFECT_ALPHAS_MODS, + VIALRGB_EFFECT_GRADIENT_UP_DOWN, + VIALRGB_EFFECT_GRADIENT_LEFT_RIGHT, + VIALRGB_EFFECT_BREATHING, + VIALRGB_EFFECT_BAND_SAT, + VIALRGB_EFFECT_BAND_VAL, + VIALRGB_EFFECT_BAND_PINWHEEL_SAT, + VIALRGB_EFFECT_BAND_PINWHEEL_VAL, + VIALRGB_EFFECT_BAND_SPIRAL_SAT, + VIALRGB_EFFECT_BAND_SPIRAL_VAL, + VIALRGB_EFFECT_CYCLE_ALL, + VIALRGB_EFFECT_CYCLE_LEFT_RIGHT, + VIALRGB_EFFECT_CYCLE_UP_DOWN, + VIALRGB_EFFECT_RAINBOW_MOVING_CHEVRON, + VIALRGB_EFFECT_CYCLE_OUT_IN, + VIALRGB_EFFECT_CYCLE_OUT_IN_DUAL, + VIALRGB_EFFECT_CYCLE_PINWHEEL, + VIALRGB_EFFECT_CYCLE_SPIRAL, + VIALRGB_EFFECT_DUAL_BEACON, + VIALRGB_EFFECT_RAINBOW_BEACON, + VIALRGB_EFFECT_RAINBOW_PINWHEELS, + VIALRGB_EFFECT_RAINDROPS, + VIALRGB_EFFECT_JELLYBEAN_RAINDROPS, + VIALRGB_EFFECT_HUE_BREATHING, + VIALRGB_EFFECT_HUE_PENDULUM, + VIALRGB_EFFECT_HUE_WAVE, + VIALRGB_EFFECT_TYPING_HEATMAP, + VIALRGB_EFFECT_DIGITAL_RAIN, + VIALRGB_EFFECT_SOLID_REACTIVE_SIMPLE, + VIALRGB_EFFECT_SOLID_REACTIVE, + VIALRGB_EFFECT_SOLID_REACTIVE_WIDE, + VIALRGB_EFFECT_SOLID_REACTIVE_MULTIWIDE, + VIALRGB_EFFECT_SOLID_REACTIVE_CROSS, + VIALRGB_EFFECT_SOLID_REACTIVE_MULTICROSS, + VIALRGB_EFFECT_SOLID_REACTIVE_NEXUS, + VIALRGB_EFFECT_SOLID_REACTIVE_MULTINEXUS, + VIALRGB_EFFECT_SPLASH, + VIALRGB_EFFECT_MULTISPLASH, + VIALRGB_EFFECT_SOLID_SPLASH, + VIALRGB_EFFECT_SOLID_MULTISPLASH, + VIALRGB_EFFECT_PIXEL_RAIN, + VIALRGB_EFFECT_PIXEL_FRACTAL, + VIALRGB_EFFECT_SKIP = 0xFFFF +}; + +class QMKVialRGBController +{ +public: + QMKVialRGBController(hid_device *dev_handle, const char *path); + ~QMKVialRGBController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + std::string GetVendor(); + std::string GetVersion(); + + bool GetSupported(); + + unsigned short GetEffect(std::size_t effect_idx); + std::size_t GetEffectCount(); + unsigned short GetKeycode(unsigned short led_index); + unsigned short GetLEDCount(); + qmk_rgb_matrix_led_info GetLEDInfo(unsigned short led_index); + + void GetMode + ( + unsigned short* mode, + unsigned char* speed, + unsigned char* hue, + unsigned char* sat, + unsigned char* val + ); + + void SendLEDs + ( + unsigned short number_leds, + RGBColor* color_data + ); + + void SetMode + ( + unsigned short mode, + unsigned char speed, + unsigned char hue, + unsigned char sat, + unsigned char val + ); + +private: + hid_device* dev; + unsigned long long keyboard_uid; + std::vector keycodes; + std::vector led_info; + std::string location; + unsigned char maximum_brightness; + std::string name; + unsigned short number_leds; + std::string serial; + bool supported; + std::vector supported_effects; + std::string vendor; + unsigned short via_protocol_version; + unsigned int vial_protocol_version; + unsigned short vialrgb_protocol_version; + unsigned char vialrgb_flag; + + unsigned short CmdGetKeycode + ( + unsigned char layer, + unsigned char row, + unsigned char col + ); + + qmk_rgb_matrix_led_info CmdGetLEDInfo + ( + unsigned short led_index + ); + + void CmdGetMode + ( + unsigned short* mode, + unsigned char* speed, + unsigned char* hue, + unsigned char* sat, + unsigned char* val + ); + + void CmdGetNumberLEDs + ( + unsigned short* number_leds + ); + + void CmdGetSupportedEffects(); + + void CmdGetVialInfo + ( + unsigned int* vial_protocol_version, + unsigned long long* keyboard_uid, + unsigned char* vialrgb_flag + ); + + void CmdGetVialRGBInfo + ( + unsigned short* vialrgb_protocol_version, + unsigned char* maximum_brightness + ); + + void CmdGetViaProtocolVersion + ( + unsigned short* via_protocol_version + ); + + void CmdSendLEDs + ( + unsigned short start_index, + unsigned char number_leds, + RGBColor* color_data + ); + + void CmdSetMode + ( + unsigned short mode, + unsigned char speed, + unsigned char hue, + unsigned char sat, + unsigned char val + ); + + int SendCommand + ( + unsigned char cmd, + unsigned char subcmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ); + + int SendCheckCommand + ( + unsigned char cmd, + unsigned char subcmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ); +}; diff --git a/Controllers/QMKController/QMKVialRGBController/QMKVialRGBControllerDetect.cpp b/Controllers/QMKController/QMKVialRGBController/QMKVialRGBControllerDetect.cpp new file mode 100644 index 0000000..67b37d4 --- /dev/null +++ b/Controllers/QMKController/QMKVialRGBController/QMKVialRGBControllerDetect.cpp @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| QMKVialRGBControllerDetect.cpp | +| | +| Detector for VialRGB QMK Keyboard Protocol | +| | +| Adam Honse 29 Sep 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "QMKVialRGBController.h" +#include "RGBController_QMKVialRGB.h" +#include "SettingsManager.h" + +/*-----------------------------------------------------*\ +| USB IDs | +\*-----------------------------------------------------*/ +#define RASPBERRY_PI_VID 0x2E8A +#define RASPBERRY_PI_500_PLUS_PID 0x0011 + +/*-----------------------------------------------------*\ +| Usage and Usage Page | +\*-----------------------------------------------------*/ +#define QMK_USAGE_PAGE 0xFF60 +#define QMK_USAGE 0x61 + +void DetectQMKVialRGBControllers(hid_device_info *info, const std::string&) +{ + hid_device *dev = hid_open_path(info->path); + + if(dev) + { + QMKVialRGBController* controller = new QMKVialRGBController(dev, info->path); + + if(controller->GetSupported()) + { + RGBController_QMKVialRGB* rgb_controller = new RGBController_QMKVialRGB(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + delete controller; + } + } +} + +void RegisterQMKVialRGBDetectors() +{ + /*-------------------------------------------------*\ + | Get QMKVialRGB settings | + \*-------------------------------------------------*/ + json vial_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("QMKVialRGBDevices"); + + if(vial_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < vial_settings["devices"].size(); device_idx++) + { + if( vial_settings["devices"][device_idx].contains("usb_pid") + && vial_settings["devices"][device_idx].contains("usb_vid") + && vial_settings["devices"][device_idx].contains("name")) + { + std::string usb_pid_str = vial_settings["devices"][device_idx]["usb_pid"]; + std::string usb_vid_str = vial_settings["devices"][device_idx]["usb_vid"]; + std::string name = vial_settings["devices"][device_idx]["name"]; + + /*-------------------------------------*\ + | Parse hex string to integer | + \*-------------------------------------*/ + unsigned short usb_pid = std::stoi(usb_pid_str, 0, 16); + unsigned short usb_vid = std::stoi(usb_vid_str, 0, 16); + + REGISTER_DYNAMIC_HID_DETECTOR_PU(name, DetectQMKVialRGBControllers, usb_vid, usb_pid, QMK_USAGE_PAGE, QMK_USAGE); + } + } + } +} + +REGISTER_DYNAMIC_DETECTOR("QMK VialRGB Devices", RegisterQMKVialRGBDetectors); + +REGISTER_HID_DETECTOR_PU( "Raspberry Pi 500+", DetectQMKVialRGBControllers, RASPBERRY_PI_VID, RASPBERRY_PI_500_PLUS_PID, QMK_USAGE_PAGE, QMK_USAGE ); diff --git a/Controllers/QMKController/QMKVialRGBController/RGBController_QMKVialRGB.cpp b/Controllers/QMKController/QMKVialRGBController/RGBController_QMKVialRGB.cpp new file mode 100644 index 0000000..bfecb4c --- /dev/null +++ b/Controllers/QMKController/QMKVialRGBController/RGBController_QMKVialRGB.cpp @@ -0,0 +1,268 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKVialRGB.cpp | +| | +| RGBController for VialRGB QMK Keyboard Protocol | +| | +| Adam Honse GetName(); + description = "QMK VialRGB Device"; + vendor = controller->GetVendor(); + location = controller->GetLocation(); + serial = controller->GetSerial(); + version = controller->GetVersion(); + type = DEVICE_TYPE_KEYBOARD; + + /*-----------------------------------------------------*\ + | Read mode list | + \*-----------------------------------------------------*/ + for(std::size_t effect_idx = 0; effect_idx < controller->GetEffectCount(); effect_idx++) + { + unsigned short mode_index = controller->GetEffect(effect_idx); + mode new_mode; + + if(mode_index > VIALRGB_NUM_MODES) + { + continue; + } + + new_mode.name = vialrgb_modes[mode_index].name; + new_mode.value = vialrgb_modes[mode_index].value; + + if(new_mode.value == VIALRGB_EFFECT_DIRECT) + { + new_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR; + new_mode.color_mode = MODE_COLORS_PER_LED; + } + + if(new_mode.value >= VIALRGB_EFFECT_SOLID_COLOR) + { + new_mode.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + new_mode.color_mode = MODE_COLORS_MODE_SPECIFIC; + new_mode.colors_min = 1; + new_mode.colors_max = 1; + new_mode.colors.resize(1); + } + + if(vialrgb_modes[mode_index].has_speed) + { + new_mode.flags |= MODE_FLAG_HAS_SPEED; + new_mode.speed_min = 0; + new_mode.speed_max = 255; + new_mode.speed = 128; + } + + modes.push_back(new_mode); + } + + /*-----------------------------------------------------*\ + | Read current mode | + \*-----------------------------------------------------*/ + unsigned short cur_mode; + unsigned char cur_speed; + unsigned char cur_hue; + unsigned char cur_sat; + unsigned char cur_val; + + controller->GetMode(&cur_mode, &cur_speed, &cur_hue, &cur_sat, &cur_val); + + active_mode = cur_mode; + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + modes[active_mode].speed = cur_speed; + } + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + hsv_t hsv_color; + hsv_color.hue = (unsigned int)((float)cur_hue * (360.0f / 256.0f)); + hsv_color.saturation = cur_sat; + hsv_color.value = cur_val; + + RGBColor rgb_color = hsv2rgb(&hsv_color); + modes[active_mode].colors[0] = rgb_color; + } + + SetupZones(); +} + +RGBController_QMKVialRGB::~RGBController_QMKVialRGB() +{ + delete controller; +} + +void RGBController_QMKVialRGB::SetupZones() +{ + /*-----------------------------------------------------*\ + | Build matrix map | + \*-----------------------------------------------------*/ + unsigned char max_col = 0; + unsigned char max_row = 0; + + for(unsigned short led_index = 0; led_index < controller->GetLEDCount(); led_index++) + { + qmk_rgb_matrix_led_info info = controller->GetLEDInfo(led_index); + + if(info.col > max_col) + { + max_col = info.col; + } + + if(info.row > max_row) + { + max_row = info.row; + } + } + + unsigned char height = max_row + 1; + unsigned char width = max_col + 1; + + unsigned int* matrix_map = new unsigned int[width * height]; + + memset(matrix_map, 0xFF, (sizeof(unsigned int) * (width * height))); + + for(unsigned short led_index = 0; led_index < controller->GetLEDCount(); led_index++) + { + qmk_rgb_matrix_led_info info = controller->GetLEDInfo(led_index); + + matrix_map[(width * info.row) + info.col] = (unsigned int)led_index; + } + + /*-----------------------------------------------------*\ + | Create keyboard zone | + \*-----------------------------------------------------*/ + zone keyboard; + + keyboard.name = "Keyboard"; + keyboard.type = ZONE_TYPE_MATRIX; + keyboard.leds_min = controller->GetLEDCount(); + keyboard.leds_max = controller->GetLEDCount(); + keyboard.leds_count = controller->GetLEDCount(); + keyboard.matrix_map = new matrix_map_type; + keyboard.matrix_map->height = height; + keyboard.matrix_map->width = width; + keyboard.matrix_map->map = matrix_map; + + zones.push_back(keyboard); + + /*-----------------------------------------------------*\ + | Create keyboard LEDs | + \*-----------------------------------------------------*/ + for(unsigned short led_idx = 0; led_idx < controller->GetLEDCount(); led_idx++) + { + led new_led; + new_led.name = qmk_keynames[controller->GetKeycode(led_idx)]; + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_QMKVialRGB::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_QMKVialRGB::DeviceUpdateLEDs() +{ + controller->SendLEDs((unsigned short)colors.size(), colors.data()); +} + +void RGBController_QMKVialRGB::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_QMKVialRGB::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_QMKVialRGB::DeviceUpdateMode() +{ + unsigned char hue = 0; + unsigned char sat = 0; + unsigned char val = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + hsv_t hsv_color; + rgb2hsv(modes[active_mode].colors[0], &hsv_color); + + hue = (unsigned char)((float)hsv_color.hue * (256.0f / 360.0f)); + sat = hsv_color.saturation; + val = hsv_color.value; + } + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, hue, sat, val); +} diff --git a/Controllers/QMKController/QMKVialRGBController/RGBController_QMKVialRGB.h b/Controllers/QMKController/QMKVialRGBController/RGBController_QMKVialRGB.h new file mode 100644 index 0000000..b0028e9 --- /dev/null +++ b/Controllers/QMKController/QMKVialRGBController/RGBController_QMKVialRGB.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_QMKVialRGB.h | +| | +| RGBController for VialRGB QMK Keyboard Protocol | +| | +| Adam Honse GetName(); + vendor = "Razer"; + type = controller->GetDeviceType(); + description = "Razer Device"; + location = controller->GetDeviceLocation(); + version = controller->GetFirmwareString(); + serial = controller->GetSerialString(); + uint8_t max_brightness = controller->GetMaxBrightness(); + + if(type == DEVICE_TYPE_KEYBOARD) + { + LOG_DEBUG("[%s] Checking Keyboard Layout", name.c_str()); + std::string layout = controller->GetKeyboardLayoutString(); + + LOG_DEBUG("[%s] returned: %s", name.c_str(), layout.c_str()); + description.append(", "); + description.append(layout); + } + + LOG_DEBUG("[%s] Checking variant", name.c_str()); + std::string variant = controller->GetVariantName(); + + LOG_DEBUG("[%s] returned: %s", name.c_str(), variant.c_str()); + description.append(", "); + description.append(variant); + + mode Direct; + Direct.name = "Direct"; + Direct.value = RAZER_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = max_brightness; + Direct.brightness = max_brightness; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = RAZER_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = RAZER_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + Static.brightness_min = 0; + Static.brightness_max = max_brightness; + Static.brightness = max_brightness; + modes.push_back(Static); + + if(controller->SupportsBreathing()) + { + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = RAZER_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(1); + Breathing.brightness_min = 0; + Breathing.brightness_max = max_brightness; + Breathing.brightness = max_brightness; + modes.push_back(Breathing); + } + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = RAZER_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_BRIGHTNESS; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = 0; + SpectrumCycle.brightness_max = max_brightness; + SpectrumCycle.brightness = max_brightness; + modes.push_back(SpectrumCycle); + + if(controller->SupportsWave()) + { + mode Wave; + Wave.name = "Wave"; + Wave.value = RAZER_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS; + Wave.direction = MODE_DIRECTION_RIGHT; + Wave.color_mode = MODE_COLORS_NONE; + Wave.brightness_min = 0; + Wave.brightness_max = max_brightness; + Wave.brightness = max_brightness; + modes.push_back(Wave); + } + + if(controller->SupportsReactive()) + { + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = RAZER_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.colors_min = 1; + Reactive.colors_max = 1; + Reactive.colors.resize(1); + Reactive.brightness_min = 0; + Reactive.brightness_max = max_brightness; + Reactive.brightness = max_brightness; + modes.push_back(Reactive); + } + + SetupZones(); +} + +RGBController_Razer::~RGBController_Razer() +{ + delete controller; +} + +void RGBController_Razer::SetupZones() +{ + unsigned int device_index = controller->GetDeviceIndex(); + unsigned char layout_type = controller->GetKeyboardLayoutType(); + + /*---------------------------------------------------------*\ + | Fill in zone information based on device table | + \*---------------------------------------------------------*/ + for(unsigned int zone_id = 0; zone_id < RAZER_MAX_ZONES; zone_id++) + { + if(device_list[device_index]->zones[zone_id] != NULL) + { + zone new_zone; + + new_zone.name = device_list[device_index]->zones[zone_id]->name; + new_zone.type = device_list[device_index]->zones[zone_id]->type; + + new_zone.leds_count = device_list[device_index]->zones[zone_id]->rows * device_list[device_index]->zones[zone_id]->cols; + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + /*---------------------------------------------------------*\ + | If this is a keyboard zone, check if using Keyboard Layout| + | Manager | + \*---------------------------------------------------------*/ + if(new_zone.type == ZONE_TYPE_MATRIX) + { + if(device_list[device_index]->layout != NULL && + (new_zone.name == ZONE_EN_KEYBOARD || new_zone.name == "Keypad")) + { + /*---------------------------------------------------------*\ + | Dynamically generate a keyboard layout | + \*---------------------------------------------------------*/ + KEYBOARD_LAYOUT new_layout; + switch(layout_type) + { + case RAZER_LAYOUT_TYPE_AZERTY: + new_layout = KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ISO_AZERTY; + break; + + case RAZER_LAYOUT_TYPE_ISO: + new_layout = KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ISO_QWERTY; + break; + + case RAZER_LAYOUT_TYPE_JIS: + new_layout = KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ANSI_QWERTY; + break; + + case RAZER_LAYOUT_TYPE_QWERTZ: + new_layout = KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ISO_QWERTZ; + break; + + default: + new_layout = KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ANSI_QWERTY; + } + + KeyboardLayoutManager new_kb(new_layout, device_list[device_index]->layout->base_size, + device_list[device_index]->layout->key_values); + + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + new_map->height = device_list[device_index]->zones[zone_id]->rows; + new_map->width = device_list[device_index]->zones[zone_id]->cols; + new_map->map = new unsigned int[new_map->height * new_map->width]; + + if(device_list[device_index]->layout->base_size != KEYBOARD_SIZE::KEYBOARD_SIZE_EMPTY || + !device_list[device_index]->layout->edit_keys.empty()) + { + /*---------------------------------------------------------*\ + | Minor adjustments to keyboard layout | + \*---------------------------------------------------------*/ + keyboard_keymap_overlay_values* temp = device_list[device_index]->layout; + new_kb.ChangeKeys(*temp); + + /*---------------------------------------------------------*\ + | Matrix map still uses declared zone rows and columns | + | as the packet structure depends on the matrix map | + \*---------------------------------------------------------*/ + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_INDEX, new_map->height, new_map->width); + } + + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Check the dynamic layout | + \*---------------------------------------------------------*/ + if(new_kb.GetKeyCount() > 0) + { + for(std::size_t row = 0; row < zones[zone_id].matrix_map->height; row++) + { + for(std::size_t col = 0; col < zones[zone_id].matrix_map->width; col++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt((unsigned int)row, (unsigned int)col); + + leds.push_back(new_led); + } + } + } + + continue; + } + else + { + /*---------------------------------------------------------*\ + | Handle all other matrix type zones by filling in all | + | entries | + \*---------------------------------------------------------*/ + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + new_map->height = device_list[device_index]->zones[zone_id]->rows; + new_map->width = device_list[device_index]->zones[zone_id]->cols; + new_map->map = new unsigned int[new_map->height * new_map->width]; + + for(unsigned int y = 0; y < new_map->height; y++) + { + for(unsigned int x = 0; x < new_map->width; x++) + { + new_map->map[(y * new_map->width) + x] = (y * new_map->width) + x; + } + } + } + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + + for (unsigned int row_id = 0; row_id < device_list[device_index]->zones[zone_id]->rows; row_id++) + { + for (unsigned int col_id = 0; col_id < device_list[device_index]->zones[zone_id]->cols; col_id++) + { + led* new_led = new led(); + + new_led->name = device_list[device_index]->zones[zone_id]->name; + + if(zones[zone_id].leds_count > 1) + { + new_led->name.append(" LED "); + new_led->name.append(std::to_string(col_id + 1)); + } + + leds.push_back(*new_led); + } + } + } + } + + SetupColors(); +} + +void RGBController_Razer::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Razer::DeviceUpdateLEDs() +{ + controller->SetLEDs(&colors[0]); +} + +void RGBController_Razer::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Razer::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Razer::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case RAZER_MODE_OFF: + controller->SetModeOff(); + break; + + case RAZER_MODE_STATIC: + if(modes[active_mode].colors.size() == 1) + { + unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu = RGBGetBValue(modes[active_mode].colors[0]); + + controller->SetModeStatic(red, grn, blu); + } + break; + + case RAZER_MODE_BREATHING: + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + controller->SetModeBreathingRandom(); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + if(modes[active_mode].colors.size() == 1) + { + unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu = RGBGetBValue(modes[active_mode].colors[0]); + + controller->SetModeBreathingOneColor(red, grn, blu); + } + else if(modes[active_mode].colors.size() == 2) + { + unsigned char red1 = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn1 = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu1 = RGBGetBValue(modes[active_mode].colors[0]); + unsigned char red2 = RGBGetRValue(modes[active_mode].colors[1]); + unsigned char grn2 = RGBGetGValue(modes[active_mode].colors[1]); + unsigned char blu2 = RGBGetBValue(modes[active_mode].colors[1]); + + controller->SetModeBreathingTwoColors(red1, grn1, blu1, red2, grn2, blu2); + } + } + break; + + case RAZER_MODE_SPECTRUM_CYCLE: + controller->SetModeSpectrumCycle(); + break; + + case RAZER_MODE_WAVE: + switch(modes[active_mode].direction) + { + case MODE_DIRECTION_LEFT: + controller->SetModeWave(2); + break; + + default: + controller->SetModeWave(1); + break; + } + break; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + controller->SetBrightness(modes[active_mode].brightness); + } + else + { + controller->SetBrightness(255); + } +} diff --git a/Controllers/RazerController/RazerController/RGBController_Razer.h b/Controllers/RazerController/RazerController/RGBController_Razer.h new file mode 100644 index 0000000..14eefe4 --- /dev/null +++ b/Controllers/RazerController/RazerController/RGBController_Razer.h @@ -0,0 +1,48 @@ +/*---------------------------------------------------------*\ +| RGBController_Razer.h | +| | +| RGBController for Razer devices | +| | +| Adam Honse (CalcProgrammer1) 22 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RazerController.h" + +#define NA 0xFFFFFFFF + +enum +{ + RAZER_MODE_DIRECT, + RAZER_MODE_OFF, + RAZER_MODE_STATIC, + RAZER_MODE_BREATHING, + RAZER_MODE_SPECTRUM_CYCLE, + RAZER_MODE_WAVE, + RAZER_MODE_REACTIVE, +}; + +class RGBController_Razer : public RGBController +{ +public: + RGBController_Razer(RazerController* controller_ptr); + ~RGBController_Razer(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RazerController* controller; +}; diff --git a/Controllers/RazerController/RazerController/RGBController_RazerAddressable.cpp b/Controllers/RazerController/RazerController/RGBController_RazerAddressable.cpp new file mode 100644 index 0000000..e0003d0 --- /dev/null +++ b/Controllers/RazerController/RazerController/RGBController_RazerAddressable.cpp @@ -0,0 +1,357 @@ +/*---------------------------------------------------------*\ +| RGBController_RazerAddressable.cpp | +| | +| RGBController for Razer ARGB Controller | +| | +| Adam Honse (CalcProgrammer1) 11 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_RazerAddressable.h" +#include "RazerDevices.h" + +/**------------------------------------------------------------------*\ + @name Razer ARGB + @category LEDStrip + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRazerARGBControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RazerAddressable::RGBController_RazerAddressable(RazerController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Razer"; + type = controller->GetDeviceType(); + description = "Razer Addressable Device"; + location = controller->GetDeviceLocation(); + version = controller->GetFirmwareString(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = RAZER_ADDRESSABLE_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 255; + Direct.brightness = 255; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = RAZER_ADDRESSABLE_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = RAZER_ADDRESSABLE_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + Static.brightness_min = 0; + Static.brightness_max = 255; + Static.brightness = 255; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = RAZER_ADDRESSABLE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(1); + Breathing.brightness_min = 0; + Breathing.brightness_max = 255; + Breathing.brightness = 255; + modes.push_back(Breathing); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = RAZER_ADDRESSABLE_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_BRIGHTNESS; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = 0; + SpectrumCycle.brightness_max = 255; + SpectrumCycle.brightness = 255; + modes.push_back(SpectrumCycle); + + if(controller->SupportsWave()) + { + mode Wave; + Wave.name = "Wave"; + Wave.value = RAZER_ADDRESSABLE_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_BRIGHTNESS; + Wave.direction = MODE_DIRECTION_RIGHT; + Wave.color_mode = MODE_COLORS_NONE; + Wave.brightness_min = 0; + Wave.brightness_max = 255; + Wave.brightness = 255; + modes.push_back(Wave); + } + + if(controller->SupportsReactive()) + { + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = RAZER_ADDRESSABLE_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Reactive.color_mode = MODE_COLORS_MODE_SPECIFIC; + Reactive.colors_min = 1; + Reactive.colors_max = 1; + Reactive.colors.resize(1); + Reactive.brightness_min = 0; + Reactive.brightness_max = 255; + Reactive.brightness = 255; + modes.push_back(Reactive); + } + + SetupZones(); +} + +RGBController_RazerAddressable::~RGBController_RazerAddressable() +{ + delete controller; +} + +void RGBController_RazerAddressable::SetupZones() +{ + unsigned int device_index = controller->GetDeviceIndex(); + unsigned int zone_count = 0; + + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Count the number of zones for this device | + \*-------------------------------------------------*/ + for(unsigned int zone_id = 0; zone_id < RAZER_MAX_ZONES; zone_id++) + { + if(device_list[device_index]->zones[zone_id] != NULL) + { + zone_count++; + } + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(zone_count); + + /*---------------------------------------------------------*\ + | Fill in zone information based on device table | + \*---------------------------------------------------------*/ + zone_count = 0; + + for(unsigned int zone_id = 0; zone_id < RAZER_MAX_ZONES; zone_id++) + { + if(device_list[device_index]->zones[zone_id] != NULL) + { + zones[zone_count].name = device_list[device_index]->zones[zone_id]->name; + zones[zone_count].type = device_list[device_index]->zones[zone_id]->type; + + zones[zone_count].leds_min = 0; + zones[zone_count].leds_max = device_list[device_index]->zones[zone_id]->rows * device_list[device_index]->zones[zone_id]->cols; + + if(first_run) + { + zones[zone_count].leds_count = 0; + } + + if(zones[zone_count].type == ZONE_TYPE_MATRIX) + { + matrix_map_type * new_map = new matrix_map_type; + zones[zone_count].matrix_map = new_map; + + new_map->height = device_list[device_index]->zones[zone_id]->rows; + new_map->width = device_list[device_index]->zones[zone_id]->cols; + + new_map->map = new unsigned int[new_map->height * new_map->width]; + + for(unsigned int y = 0; y < new_map->height; y++) + { + for(unsigned int x = 0; x < new_map->width; x++) + { + new_map->map[(y * new_map->width) + x] = (y * new_map->width) + x; + } + } + } + else + { + zones[zone_count].matrix_map = NULL; + } + + zone_count++; + } + } + + for(unsigned int zone_id = 0; zone_id < zones.size(); zone_id++) + { + for(unsigned int led_id = 0; led_id < zones[zone_id].leds_count; led_id++) + { + led new_led; + new_led.name = "Channel " + std::to_string(zone_id + 1) + ", LED " + std::to_string(led_id + 1); + + leds.push_back(new_led); + } + } + + SetupColors(); +} + +void RGBController_RazerAddressable::ResizeZone(int zone, int new_size) +{ + /*---------------------------------------------------------*\ + | Only the Razer Chroma Addressable RGB Controller supports | + | zone resizing | + \*---------------------------------------------------------*/ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + controller->SetAddressableZoneSizes(zones[0].leds_count, + zones[1].leds_count, + zones[2].leds_count, + zones[3].leds_count, + zones[4].leds_count, + zones[5].leds_count); + + SetupZones(); + } +} + +void RGBController_RazerAddressable::DeviceUpdateLEDs() +{ + /*---------------------------------------------------------*\ + | Only the Razer Chroma Addressable RGB Controller supports | + | zone resizing | + \*---------------------------------------------------------*/ + RGBColor colors_buf[80 * 6]; + + for(unsigned int zone_id = 0; zone_id < zones.size(); zone_id++) + { + memcpy(&colors_buf[(80 * zone_id)], zones[zone_id].colors, sizeof(RGBColor) * zones[zone_id].leds_count); + } + + controller->SetLEDs(&colors_buf[0]); +} + +void RGBController_RazerAddressable::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RazerAddressable::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RazerAddressable::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case RAZER_ADDRESSABLE_MODE_DIRECT: + /*---------------------------------------------------------*\ + | Controller does not preserve the LEDs for direct mode. | + | We have to restore them. | + \*---------------------------------------------------------*/ + DeviceUpdateLEDs(); + break; + case RAZER_ADDRESSABLE_MODE_OFF: + controller->SetModeOff(); + break; + + case RAZER_ADDRESSABLE_MODE_STATIC: + if(modes[active_mode].colors.size() == 1) + { + unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu = RGBGetBValue(modes[active_mode].colors[0]); + + controller->SetModeStatic(red, grn, blu); + } + break; + + case RAZER_ADDRESSABLE_MODE_BREATHING: + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + controller->SetModeBreathingRandom(); + } + else if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + if(modes[active_mode].colors.size() == 1) + { + unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu = RGBGetBValue(modes[active_mode].colors[0]); + + controller->SetModeBreathingOneColor(red, grn, blu); + } + else if(modes[active_mode].colors.size() == 2) + { + unsigned char red1 = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn1 = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu1 = RGBGetBValue(modes[active_mode].colors[0]); + unsigned char red2 = RGBGetRValue(modes[active_mode].colors[1]); + unsigned char grn2 = RGBGetGValue(modes[active_mode].colors[1]); + unsigned char blu2 = RGBGetBValue(modes[active_mode].colors[1]); + + controller->SetModeBreathingTwoColors(red1, grn1, blu1, red2, grn2, blu2); + } + } + break; + + case RAZER_ADDRESSABLE_MODE_SPECTRUM_CYCLE: + controller->SetModeSpectrumCycle(); + break; + + case RAZER_ADDRESSABLE_MODE_WAVE: + switch(modes[active_mode].direction) + { + case MODE_DIRECTION_LEFT: + controller->SetModeWave(2); + break; + + default: + controller->SetModeWave(1); + break; + } + break; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + controller->SetBrightness(modes[active_mode].brightness); + } + else + { + controller->SetBrightness(255); + } +} diff --git a/Controllers/RazerController/RazerController/RGBController_RazerAddressable.h b/Controllers/RazerController/RazerController/RGBController_RazerAddressable.h new file mode 100644 index 0000000..c89208b --- /dev/null +++ b/Controllers/RazerController/RazerController/RGBController_RazerAddressable.h @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| RGBController_RazerAddressable.h | +| | +| RGBController for Razer ARGB Controller | +| | +| Adam Honse (CalcProgrammer1) 11 Apr 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RazerController.h" + +enum +{ + RAZER_ADDRESSABLE_MODE_DIRECT, + RAZER_ADDRESSABLE_MODE_OFF, + RAZER_ADDRESSABLE_MODE_STATIC, + RAZER_ADDRESSABLE_MODE_BREATHING, + RAZER_ADDRESSABLE_MODE_SPECTRUM_CYCLE, + RAZER_ADDRESSABLE_MODE_WAVE, + RAZER_ADDRESSABLE_MODE_REACTIVE, +}; + +class RGBController_RazerAddressable : public RGBController +{ +public: + RGBController_RazerAddressable(RazerController* controller_ptr); + ~RGBController_RazerAddressable(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RazerController* controller; +}; diff --git a/Controllers/RazerController/RazerController/RazerController.cpp b/Controllers/RazerController/RazerController/RazerController.cpp new file mode 100644 index 0000000..b5aff64 --- /dev/null +++ b/Controllers/RazerController/RazerController/RazerController.cpp @@ -0,0 +1,1907 @@ +/*---------------------------------------------------------*\ +| RazerController.cpp | +| | +| Driver for Razer devices | +| | +| Adam Honse (CalcProgrammer1) 22 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RazerController.h" +#include "RazerDevices.h" +#include "LogManager.h" +#include "RazerDeviceGuard.h" + +using namespace std::chrono_literals; + +RazerController::RazerController(hid_device* dev_handle, hid_device* dev_argb_handle, const char* path, unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + dev_argb = dev_argb_handle; + dev_pid = pid; + location = path; + name = dev_name; + device_index = 0; + guard_manager_ptr = new DeviceGuardManager(new RazerDeviceGuard()); + + /*-----------------------------------------------------------------*\ + | Loop through all known devices to look for a name match | + \*-----------------------------------------------------------------*/ + for (unsigned int i = 0; i < RAZER_NUM_DEVICES; i++) + { + if (device_list[i]->pid == dev_pid) + { + /*---------------------------------------------------------*\ + | Set device ID | + \*---------------------------------------------------------*/ + device_index = i; + } + } + + /*-----------------------------------------------------------------*\ + | Set report index | + \*-----------------------------------------------------------------*/ + switch(dev_pid) + { + case RAZER_LEVIATHAN_V2_PID: + case RAZER_LEVIATHAN_V2X_PID: + report_index = 0x07; + response_index = 0x07; + break; + + default: + report_index = 0; + response_index = 0; + } + + /*-----------------------------------------------------------------*\ + | Determine transaction ID for device | + \*-----------------------------------------------------------------*/ + dev_transaction_id = device_list[device_index]->transaction_id; + + switch(dev_pid) + { + case RAZER_CHARGING_PAD_CHROMA_PID: + case RAZER_CHROMA_MUG_PID: + case RAZER_FIREFLY_HYPERFLUX_PID: + { + razer_set_device_mode(RAZER_DEVICE_MODE_SOFTWARE); + } + break; + } + + /*-----------------------------------------------------------------*\ + | Determine LED ID for device | + \*-----------------------------------------------------------------*/ + switch(dev_pid) + { + case RAZER_BASILISK_ULTIMATE_WIRED_PID: + case RAZER_BASILISK_ULTIMATE_WIRELESS_PID: + case RAZER_BASILISK_V3_PID: + case RAZER_BASILISK_V3_35K_PID: + case RAZER_BASILISK_V3_X_HYPERSPEED_PID: + case RAZER_BASILISK_V3_PRO_WIRED_PID: + case RAZER_BASILISK_V3_PRO_WIRELESS_PID: + case RAZER_BASILISK_V3_PRO_35K_WIRED_PID: + case RAZER_BASILISK_V3_PRO_35K_WIRELESS_PID: + case RAZER_BASILISK_V3_PRO_BLUETOOTH_PID: + case RAZER_BASILISK_V3_PRO_35K_PG_WIRED_PID: + case RAZER_BASILISK_V3_PRO_35K_PG_WIRELESS_PID: + case RAZER_BASILISK_V3_PRO_35K_PG_BLUETOOTH_PID: + case RAZER_BASE_STATION_CHROMA_PID: + case RAZER_BASE_STATION_V2_CHROMA_PID: + case RAZER_BLADE_14_2022_PID: + case RAZER_BLADE_14_2023_PID: + case RAZER_BLADE_15_2022_PID: + case RAZER_CHARGING_PAD_CHROMA_PID: + case RAZER_CHROMA_HDK_PID: + case RAZER_COBRA_PRO_WIRED_PID: + case RAZER_COBRA_PRO_WIRELESS_PID: + case RAZER_CORE_X_PID: + case RAZER_DEATHADDER_ELITE_PID: + case RAZER_DEATHADDER_V2_PID: + case RAZER_DEATHADDER_V2_MINI_PID: + case RAZER_DEATHADDER_ESSENTIAL_V2_PID: + case RAZER_DEATHSTALKER_V2_PRO_TKL_WIRED_PID: + case RAZER_DEATHSTALKER_V2_PRO_TKL_WIRELESS_PID: + case RAZER_DEATHSTALKER_V2_PRO_WIRED_PID: + case RAZER_DEATHSTALKER_V2_PRO_WIRELESS_PID: + case RAZER_FIREFLY_V2_PID: + case RAZER_FIREFLY_V2_PRO_PID: + case RAZER_FIREFLY_HYPERFLUX_PID: + case RAZER_GOLIATHUS_CHROMA_EXTENDED_PID: + case RAZER_GOLIATHUS_CHROMA_PID: + case RAZER_GOLIATHUS_CHROMA_3XL_PID: + case RAZER_LAPTOP_STAND_CHROMA_PID: + case RAZER_LAPTOP_STAND_CHROMA_V2_PID: + case RAZER_LEVIATHAN_V2_PID: + case RAZER_LEVIATHAN_V2X_PID: + case RAZER_MAMBA_ELITE_PID: + case RAZER_MAMBA_HYPERFLUX_PID: + case RAZER_MOUSE_BUNGEE_V3_CHROMA_PID: + case RAZER_MOUSE_DOCK_PRO_PID: + case RAZER_NAGA_CLASSIC_PID: + case RAZER_NAGA_LEFT_HANDED_PID: + case RAZER_NAGA_PRO_V2_WIRED_PID: + case RAZER_NAGA_PRO_V2_WIRELESS_PID: + case RAZER_O11_DYNAMIC_PID: + case RAZER_STRIDER_CHROMA_PID: + case RAZER_TARTARUS_PRO_PID: + case RAZER_TARTARUS_V2_PID: + dev_led_id = RAZER_LED_ID_ZERO; + break; + + case RAZER_BLACKWIDOW_2019_PID: + case RAZER_BLACKWIDOW_ELITE_PID: + case RAZER_BLACKWIDOW_ESSENTIAL_PID: + case RAZER_BLACKWIDOW_LITE_PID: + case RAZER_BLACKWIDOW_V3_PID: + case RAZER_BLACKWIDOW_V3_PRO_WIRED_PID: + case RAZER_BLACKWIDOW_V3_PRO_BLUETOOTH_PID: + case RAZER_BLACKWIDOW_V3_PRO_WIRELESS_PID: + case RAZER_BLACKWIDOW_V3_TKL_PID: + case RAZER_BLACKWIDOW_V3_MINI_WIRED_PID: + case RAZER_BLACKWIDOW_V3_MINI_WIRELESS_PID: + case RAZER_CYNOSA_CHROMA_PID: + case RAZER_CYNOSA_LITE_PID: + case RAZER_CYNOSA_V2_PID: + case RAZER_DEATHSTALKER_V2_PID: + case RAZER_HUNTSMAN_ELITE_PID: + case RAZER_HUNTSMAN_PID: + case RAZER_HUNTSMAN_MINI_PID: + case RAZER_HUNTSMAN_MINI_ANALOG_PID: + case RAZER_HUNTSMAN_TE_PID: + case RAZER_HUNTSMAN_V2_ANALOG_PID: + case RAZER_HUNTSMAN_V2_TKL_PID: + case RAZER_HUNTSMAN_V2_PID: + case RAZER_HUNTSMAN_V3_PRO_PID: + case RAZER_HUNTSMAN_V3_PRO_TKL_WHITE_PID: + case RAZER_ORNATA_CHROMA_PID: + case RAZER_ORNATA_CHROMA_V2_PID: + case RAZER_ORNATA_V3_PID: + case RAZER_ORNATA_V3_REV2_PID: + case RAZER_ORNATA_V3_TKL_PID: + case RAZER_ORNATA_V3_X_PID: + case RAZER_CORE_PID: + case RAZER_FIREFLY_PID: + default: + dev_led_id = RAZER_LED_ID_BACKLIGHT; + break; + } + + /*-----------------------------------------------------------------*\ + | Determine matrix type for device | + \*-----------------------------------------------------------------*/ + matrix_type = device_list[device_index]->matrix_type; + + /*-----------------------------------------------------------------*\ + | Start keepalive thread for devices that need it to prevent RGB | + | from timing out | + \*-----------------------------------------------------------------*/ + switch(dev_pid) + { + case RAZER_BLADE_14_2021_PID: + case RAZER_BLADE_14_2022_PID: + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RazerController::KeepaliveThreadFunction, this); + break; + + default: + keepalive_thread_run = false; + keepalive_thread = NULL; + break; + } +} + +RazerController::~RazerController() +{ + if(keepalive_thread != NULL) + { + keepalive_thread_run = false; + keepalive_thread->join(); + } + + hid_close(dev); + delete guard_manager_ptr; +} + +std::string RazerController::GetName() +{ + return(name); +} + +unsigned int RazerController::GetDeviceIndex() +{ + return(device_index); +} + +device_type RazerController::GetDeviceType() +{ + return(device_list[device_index]->type); +} + +std::string RazerController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RazerController::GetFirmwareString() +{ + return(razer_get_firmware()); +} + +std::string RazerController::GetSerialString() +{ + return(razer_get_serial()); +} + +void RazerController::KeepaliveThreadFunction() +{ + /*-----------------------------------------------------------------*\ + | Performing a get device mode request seems to be enough to keep | + | the lighting active on devices with the lighting timeout, so | + | periodically send a device mode request every 2.5s. | + \*-----------------------------------------------------------------*/ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_update_time) > 2500ms) + { + razer_get_device_mode(); + } + std::this_thread::sleep_for(1s); + } +} + +void RazerController::SetAddressableZoneSizes(unsigned char zone_1_size, unsigned char zone_2_size, unsigned char zone_3_size, unsigned char zone_4_size, unsigned char zone_5_size, unsigned char zone_6_size) +{ + razer_report report = razer_create_addressable_size_report(zone_1_size, zone_2_size, zone_3_size, zone_4_size, zone_5_size, zone_6_size); + + razer_usb_send(&report); +} + +unsigned char RazerController::GetMaxBrightness() +{ + /*-----------------------------------------------------*\ + | Max brightness for most devices is 0xFF (255) | + | Add PIDs only for devices that use 0x64 (100) | + | or any another arbitrary value | + \*-----------------------------------------------------*/ + unsigned char max_brightness = 255; + + switch(dev_pid) + { + /*-----------------------------------------------------*\ + | Mice | + \*-----------------------------------------------------*/ + case RAZER_DEATHADDER_ESSENTIAL_V2_PID: + + max_brightness = 100; + break; + } + + return(max_brightness); +} + +void RazerController::SetBrightness(unsigned char brightness) +{ + razer_set_brightness(brightness); +} + +void RazerController::SetLEDs(RGBColor* colors) +{ + /*---------------------------------------------------------*\ + | Get the matrix layout information from the device list | + \*---------------------------------------------------------*/ + unsigned int matrix_cols = device_list[device_index]->cols; + unsigned int matrix_rows = device_list[device_index]->rows; + + /*---------------------------------------------------------*\ + | Create an output array large enough to hold RGB data for | + | a single row | + \*---------------------------------------------------------*/ + unsigned char* output_array = new unsigned char[matrix_cols * 3]; + + /*---------------------------------------------------------*\ + | Send one row of the custom frame at a time | + \*---------------------------------------------------------*/ + for (unsigned int row = 0; row < matrix_rows; row++) + { + unsigned int row_offset = (row * matrix_cols); + + /*-----------------------------------------------------*\ + | Fill the output array with RGB data | + \*-----------------------------------------------------*/ + for(unsigned int col = 0; col < matrix_cols; col++) + { + unsigned int color_idx = col + row_offset; + output_array[(col * 3) + 0] = (char)RGBGetRValue(colors[color_idx]); + output_array[(col * 3) + 1] = (char)RGBGetGValue(colors[color_idx]); + output_array[(col * 3) + 2] = (char)RGBGetBValue(colors[color_idx]); + } + + /*-----------------------------------------------------*\ + | Send the output array to the device | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(1ms); + + razer_set_custom_frame(row, 0, matrix_cols - 1, output_array); + } + + std::this_thread::sleep_for(1ms); + + /*---------------------------------------------------------*\ + | Set custom mode to apply frame | + \*---------------------------------------------------------*/ + razer_set_mode_custom(); + + /*---------------------------------------------------------*\ + | Delete the output array | + \*---------------------------------------------------------*/ + delete[] output_array; +} + +void RazerController::SetModeBreathingOneColor(unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_set_mode_breathing_one_color(red, grn, blu); +} + +void RazerController::SetModeBreathingRandom() +{ + razer_set_mode_breathing_random(); +} + +void RazerController::SetModeBreathingTwoColors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2) +{ + razer_set_mode_breathing_two_colors(r1, g1, b1, r2, g2, b2); +} + +void RazerController::SetModeOff() +{ + razer_set_mode_none(); +} + +void RazerController::SetModeSpectrumCycle() +{ + razer_set_mode_spectrum_cycle(); +} + +void RazerController::SetModeStatic(unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_set_mode_static(red, grn, blu); +} + +void RazerController::SetModeWave(unsigned char direction) +{ + razer_set_mode_wave(direction); +} + +bool RazerController::SupportsBreathing() +{ + /*-----------------------------------------------------*\ + | Breathing Mode is assumed as supported in hardware | + | Add PIDs only for devices that DO NOT support the | + | hardware breathing mode i.e. Packet captures show | + | software driving the basic `Breathing` mode | + \*-----------------------------------------------------*/ + bool supports_breathing = true; + + switch(dev_pid) + { + /*-----------------------------------------------------*\ + | Mice | + \*-----------------------------------------------------*/ + case RAZER_BASILISK_V3_PID: + case RAZER_BASILISK_V3_35K_PID: + case RAZER_BASILISK_V3_PRO_WIRED_PID: + case RAZER_BASILISK_V3_PRO_WIRELESS_PID: + case RAZER_BASILISK_V3_PRO_35K_WIRED_PID: + case RAZER_BASILISK_V3_PRO_35K_WIRELESS_PID: + case RAZER_BASILISK_V3_PRO_35K_PG_WIRED_PID: + case RAZER_BASILISK_V3_PRO_35K_PG_WIRELESS_PID: + case RAZER_BASILISK_V3_PRO_35K_PG_BLUETOOTH_PID: + case RAZER_BASILISK_V3_PRO_BLUETOOTH_PID: + + supports_breathing = false; + break; + } + + return(supports_breathing); +} + +bool RazerController::SupportsReactive() +{ + return(false); +} + +bool RazerController::SupportsWave() +{ + bool supports_wave = false; + + switch(dev_pid) + { + /*-----------------------------------------------------*\ + | Keyboards, Keypads, and Laptops | + \*-----------------------------------------------------*/ + case RAZER_BLACKWIDOW_CHROMA_PID: + case RAZER_BLACKWIDOW_CHROMA_TE_PID: + case RAZER_BLACKWIDOW_CHROMA_V2_PID: + case RAZER_BLACKWIDOW_OVERWATCH_PID: + case RAZER_BLACKWIDOW_V3_PID: + case RAZER_BLACKWIDOW_V3_PRO_WIRED_PID: + case RAZER_BLACKWIDOW_V3_PRO_BLUETOOTH_PID: + case RAZER_BLACKWIDOW_V3_PRO_WIRELESS_PID: + case RAZER_BLACKWIDOW_V3_TKL_PID: + case RAZER_BLACKWIDOW_V3_MINI_WIRED_PID: + case RAZER_BLACKWIDOW_V3_MINI_WIRELESS_PID: + case RAZER_BLACKWIDOW_V4_PID: + case RAZER_BLACKWIDOW_V4_PRO_PID: + case RAZER_BLACKWIDOW_V4_PRO_75_WIRED_PID: + case RAZER_BLACKWIDOW_V4_75_WIRED_PID: + case RAZER_BLACKWIDOW_V4_X_PID: + case RAZER_BLACKWIDOW_V4_TKL_WIRED_PID: + case RAZER_BLACKWIDOW_V4_TKL_WIRELESS_PID: + case RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRED_PID: + case RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRELESS_PID: + case RAZER_BLACKWIDOW_X_CHROMA_PID: + case RAZER_BLACKWIDOW_X_CHROMA_TE_PID: + case RAZER_BLADE_2016_PID: + case RAZER_BLADE_LATE_2016_PID: + case RAZER_BLADE_2018_ADVANCED_PID: + case RAZER_BLADE_2018_MERCURY_PID: + case RAZER_BLADE_2019_ADVANCED_PID: + case RAZER_BLADE_2019_BASE_PID: + case RAZER_BLADE_2019_MERCURY_PID: + case RAZER_BLADE_2019_STUDIO_PID: + case RAZER_BLADE_2020_ADVANCED_PID: + case RAZER_BLADE_LATE_2020_PID: + case RAZER_BLADE_2020_BASE_PID: + case RAZER_BLADE_2021_ADVANCED_PID: + case RAZER_BLADE_2021_BASE_PID: + case RAZER_BLADE_2021_BASE_V2_PID: + case RAZER_BLADE_LATE_2021_ADVANCED_PID: + case RAZER_BLADE_14_2021_PID: + case RAZER_BLADE_14_2022_PID: + case RAZER_BLADE_14_2023_PID: + case RAZER_BLADE_15_2022_PID: + case RAZER_BLADE_PRO_2016_PID: + case RAZER_BLADE_PRO_2017_PID: + case RAZER_BLADE_PRO_2017_FULLHD_PID: + case RAZER_BLADE_PRO_2019_PID: + case RAZER_BLADE_PRO_LATE_2019_PID: + case RAZER_BLADE_PRO_17_2020_PID: + case RAZER_BLADE_PRO_17_2021_PID: + case RAZER_BLADE_STEALTH_2016_PID: + case RAZER_BLADE_STEALTH_LATE_2016_PID: + case RAZER_BLADE_STEALTH_2017_PID: + case RAZER_BLADE_STEALTH_LATE_2017_PID: + case RAZER_BOOK_13_2020_PID: + case RAZER_CYNOSA_CHROMA_PID: + case RAZER_CYNOSA_V2_PID: + case RAZER_DEATHSTALKER_CHROMA_PID: + case RAZER_DEATHSTALKER_V2_PID: + case RAZER_DEATHSTALKER_V2_PRO_TKL_WIRED_PID: + case RAZER_DEATHSTALKER_V2_PRO_TKL_WIRELESS_PID: + case RAZER_DEATHSTALKER_V2_PRO_WIRED_PID: + case RAZER_DEATHSTALKER_V2_PRO_WIRELESS_PID: + case RAZER_ORNATA_CHROMA_PID: + case RAZER_ORNATA_CHROMA_V2_PID: + case RAZER_ORNATA_V3_PID: + case RAZER_ORNATA_V3_REV2_PID: + case RAZER_ORNATA_V3_TKL_PID: + case RAZER_ORNATA_V3_X_PID: + case RAZER_HUNTSMAN_PID: + case RAZER_HUNTSMAN_ELITE_PID: + case RAZER_HUNTSMAN_MINI_PID: + case RAZER_HUNTSMAN_MINI_ANALOG_PID: + case RAZER_HUNTSMAN_TE_PID: + case RAZER_HUNTSMAN_V2_ANALOG_PID: + case RAZER_HUNTSMAN_V2_TKL_PID: + case RAZER_HUNTSMAN_V2_PID: + case RAZER_HUNTSMAN_V3_PRO_PID: + case RAZER_HUNTSMAN_V3_PRO_TKL_WHITE_PID: + case RAZER_ORBWEAVER_CHROMA_PID: + case RAZER_TARTARUS_PRO_PID: + case RAZER_TARTARUS_V2_PID: + + /*-----------------------------------------------------*\ + | Mice | + \*-----------------------------------------------------*/ + case RAZER_BASILISK_ULTIMATE_WIRED_PID: + case RAZER_BASILISK_ULTIMATE_WIRELESS_PID: + case RAZER_BASILISK_V3_PID: + case RAZER_BASILISK_V3_35K_PID: + case RAZER_BASILISK_V3_PRO_WIRED_PID: + case RAZER_BASILISK_V3_PRO_WIRELESS_PID: + case RAZER_BASILISK_V3_PRO_35K_WIRED_PID: + case RAZER_BASILISK_V3_PRO_35K_WIRELESS_PID: + case RAZER_BASILISK_V3_PRO_35K_PG_WIRED_PID: + case RAZER_BASILISK_V3_PRO_35K_PG_WIRELESS_PID: + case RAZER_BASILISK_V3_PRO_35K_PG_BLUETOOTH_PID: + case RAZER_BASILISK_V3_PRO_BLUETOOTH_PID: + case RAZER_COBRA_PRO_WIRED_PID: + case RAZER_COBRA_PRO_WIRELESS_PID: + case RAZER_DIAMONDBACK_CHROMA_PID: + case RAZER_MAMBA_2015_WIRED_PID: + case RAZER_MAMBA_2015_WIRELESS_PID: + case RAZER_MAMBA_ELITE_PID: + case RAZER_MAMBA_TE_PID: + case RAZER_NAGA_LEFT_HANDED_PID: + + /*-----------------------------------------------------*\ + | Headsets | + \*-----------------------------------------------------*/ + case RAZER_TIAMAT_71_V2_PID: + + /*-----------------------------------------------------*\ + | Accessories | + \*-----------------------------------------------------*/ + case RAZER_BASE_STATION_CHROMA_PID: + case RAZER_BASE_STATION_V2_CHROMA_PID: + case RAZER_CHARGING_PAD_CHROMA_PID: + case RAZER_CHROMA_ADDRESSABLE_RGB_CONTROLLER_PID: + case RAZER_CHROMA_MUG_PID: + case RAZER_CHROMA_HDK_PID: + case RAZER_CHROMA_PC_CASE_LIGHTING_KIT_PID: + case RAZER_CORE_PID: + case RAZER_CORE_X_PID: + case RAZER_FIREFLY_PID: + case RAZER_FIREFLY_V2_PID: + case RAZER_FIREFLY_V2_PRO_PID: + case RAZER_FIREFLY_HYPERFLUX_PID: + case RAZER_LAPTOP_STAND_CHROMA_PID: + case RAZER_LAPTOP_STAND_CHROMA_V2_PID: + case RAZER_LEVIATHAN_V2_PID: + case RAZER_LEVIATHAN_V2X_PID: + case RAZER_MOUSE_BUNGEE_V3_CHROMA_PID: + case RAZER_MOUSE_DOCK_PRO_PID: + case RAZER_NOMMO_CHROMA_PID: + case RAZER_NOMMO_PRO_PID: + case RAZER_O11_DYNAMIC_PID: + case RAZER_STRIDER_CHROMA_PID: + case RAZER_THUNDERBOLT_4_DOCK_CHROMA_PID: + case RAZER_THUNDERBOLT_5_DOCK_CHROMA_PID: + + supports_wave = true; + break; + } + + return(supports_wave); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +unsigned char RazerController::razer_calculate_crc(razer_report* report) +{ + /*---------------------------------------------------------*\ + | The second to last byte of report is a simple checksum | + | Just xor all bytes up with overflow and you are done | + \*---------------------------------------------------------*/ + unsigned char crc = 0; + unsigned char* report_ptr = (unsigned char*)report; + + /*---------------------------------------------------------*\ + | The start and end checks here have been modified compared | + | to the original OpenRazer version. This is due to adding | + | the report ID field to the razer_report structure for | + | compatibility with HIDAPI. | + \*---------------------------------------------------------*/ + for(unsigned int i = 3; i < 89; i++) + { + crc ^= report_ptr[i]; + } + + return crc; +} + +/*---------------------------------------------------------------------------------*\ +| Basic report and response creation functions | +\*---------------------------------------------------------------------------------*/ + +razer_report RazerController::razer_create_report(unsigned char command_class, unsigned char command_id, unsigned char data_size) +{ + razer_report new_report; + + /*---------------------------------------------------------*\ + | Zero out the new report | + \*---------------------------------------------------------*/ + memset(&new_report, 0, sizeof(razer_report)); + + /*---------------------------------------------------------*\ + | Fill in the new report with the given parameters | + \*---------------------------------------------------------*/ + new_report.report_id = report_index; + new_report.status = 0x00; + new_report.transaction_id.id = dev_transaction_id; + new_report.remaining_packets = 0x00; + new_report.protocol_type = 0x00; + new_report.data_size = data_size; + new_report.command_class = command_class; + new_report.command_id.id = command_id; + + return new_report; +} + +razer_report RazerController::razer_create_response() +{ + razer_report new_report; + + /*---------------------------------------------------------*\ + | Zero out the new report | + \*---------------------------------------------------------*/ + memset(&new_report, 0, sizeof(razer_report)); + + /*---------------------------------------------------------*\ + | Fill in the new report with the given parameters | + \*---------------------------------------------------------*/ + new_report.report_id = response_index; + new_report.status = 0x00; + new_report.transaction_id.id = dev_transaction_id; + new_report.remaining_packets = 0x00; + new_report.protocol_type = 0x00; + new_report.command_class = 0x00; + new_report.command_id.id = 0x00; + new_report.data_size = 0x00; + + return new_report; +} + +/*---------------------------------------------------------------------------------*\ +| Command report creation functions | +\*---------------------------------------------------------------------------------*/ + +razer_report RazerController::razer_create_addressable_size_report + ( + unsigned char zone_1_size, + unsigned char zone_2_size, + unsigned char zone_3_size, + unsigned char zone_4_size, + unsigned char zone_5_size, + unsigned char zone_6_size + ) +{ + razer_report report = razer_create_report(0x0F, 0x08, 0x0D); + + report.arguments[0] = 0x06; + report.arguments[1] = (zone_1_size == 0) ? 0x01 : 0x19; + report.arguments[2] = zone_1_size; + report.arguments[3] = (zone_2_size == 0) ? 0x02 : 0x19; + report.arguments[4] = zone_2_size; + report.arguments[5] = (zone_3_size == 0) ? 0x03 : 0x19; + report.arguments[6] = zone_3_size; + report.arguments[7] = (zone_4_size == 0) ? 0x04 : 0x19; + report.arguments[8] = zone_4_size; + report.arguments[9] = (zone_5_size == 0) ? 0x05 : 0x19; + report.arguments[10] = zone_5_size; + report.arguments[11] = (zone_6_size == 0) ? 0x06 : 0x19; + report.arguments[12] = zone_6_size; + + return report; +} + +razer_report RazerController::razer_create_addressable_startup_detect_report(bool enable) +{ + razer_report report = razer_create_report(0x00, 0x44, 0x01); + + report.arguments[0] = enable; + + return report; +} + +razer_report RazerController::razer_create_brightness_extended_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char brightness) +{ + razer_report report = razer_create_report(0x0F, 0x04, 0x03); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + report.arguments[2] = brightness; + + return report; +} + +razer_report RazerController::razer_create_brightness_standard_report(unsigned char variable_storage, unsigned char led_id, unsigned char brightness) +{ + razer_report report = razer_create_report(0x03, 0x03, 0x03); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + report.arguments[2] = brightness; + + return report; +} + +razer_argb_report RazerController::razer_create_custom_frame_argb_report(unsigned char row_index, unsigned char stop_col, unsigned char* rgb_data) +{ + razer_argb_report report; + + /*---------------------------------------------------------*\ + | Zero out the new report | + \*---------------------------------------------------------*/ + memset(&report, 0, sizeof(razer_argb_report)); + + /*---------------------------------------------------------*\ + | Fill in report header | + \*---------------------------------------------------------*/ + report.hid_id = 0; + + if(row_index < 5) + { + report.report_id = 0x04; + } + else + { + report.report_id = 0x84; + } + + report.channel_1 = row_index; + report.channel_2 = row_index; + report.pad = 0; + report.last_idx = stop_col; + + /*---------------------------------------------------------*\ + | Copy in the RGB data | + \*---------------------------------------------------------*/ + memcpy(&report.color_data, rgb_data, (stop_col + 1) * 3); + + return report; +} + +razer_report RazerController::razer_create_custom_frame_linear_report(unsigned char start_col, unsigned char stop_col, unsigned char* rgb_data) +{ + razer_report report = razer_create_report(0x03, 0x0C, 0x32); + size_t row_length = (size_t) (((stop_col + 1) - start_col) * 3); + + report.arguments[0] = start_col; + report.arguments[1] = stop_col; + + /*---------------------------------------------------------*\ + | Copy in the RGB data | + \*---------------------------------------------------------*/ + memcpy(&report.arguments[2], rgb_data, row_length); + + return report; +} + +razer_report RazerController::razer_create_custom_frame_extended_matrix_report(unsigned char row_index, unsigned char start_col, unsigned char stop_col, unsigned char* rgb_data) +{ + const size_t row_length = (size_t)(((stop_col + 1) - start_col) * 3); + const size_t packet_length = row_length + 5; + + razer_report report = razer_create_report(0x0F, 0x03, (unsigned char)packet_length); + + report.arguments[2] = row_index; + report.arguments[3] = start_col; + report.arguments[4] = stop_col; + + /*---------------------------------------------------------*\ + | Copy in the RGB data | + \*---------------------------------------------------------*/ + memcpy(&report.arguments[5], rgb_data, row_length); + + return report; +} + +razer_report RazerController::razer_create_custom_frame_standard_matrix_report(unsigned char row_index, unsigned char start_col, unsigned char stop_col, unsigned char* rgb_data) +{ + const size_t row_length = (size_t)(((stop_col + 1) - start_col) * 3); + const size_t packet_length = row_length + 4; + + razer_report report = razer_create_report(0x03, 0x0B, (unsigned char)packet_length); + + report.arguments[0] = 0xFF; + report.arguments[1] = row_index; + report.arguments[2] = start_col; + report.arguments[3] = stop_col; + + /*---------------------------------------------------------*\ + | Copy in the RGB data | + \*---------------------------------------------------------*/ + memcpy(&report.arguments[4], rgb_data, row_length); + + return report; +} + +razer_report RazerController::razer_create_device_mode_report(unsigned char mode, unsigned char param) +{ + razer_report report = razer_create_report(0x00, 0x04, 0x02); + + report.arguments[0] = mode; + report.arguments[1] = param; + + return report; +} + +razer_report RazerController::razer_create_mode_breathing_one_color_extended_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_report report = razer_create_report(0x0F, 0x02, 0x09); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + report.arguments[2] = 0x02; + + report.arguments[3] = 0x01; + report.arguments[5] = 0x01; + + report.arguments[6] = red; + report.arguments[7] = grn; + report.arguments[8] = blu; + + return report; +} + +razer_report RazerController::razer_create_mode_breathing_one_color_standard_matrix_report(unsigned char /*variable_storage*/, unsigned char /*led_id*/, unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_report report = razer_create_report(0x03, 0x0A, 0x08); + + report.arguments[0] = 0x03; + report.arguments[1] = 0x01; + report.arguments[2] = red; + report.arguments[3] = grn; + report.arguments[4] = blu; + + return report; +} + +razer_report RazerController::razer_create_mode_breathing_random_extended_matrix_report(unsigned char variable_storage, unsigned char led_id) +{ + razer_report report = razer_create_report(0x0F, 0x02, 0x06); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + report.arguments[2] = 0x02; + + return report; +} + +razer_report RazerController::razer_create_mode_breathing_random_standard_matrix_report(unsigned char /*variable_storage*/, unsigned char /*led_id*/) +{ + razer_report report = razer_create_report(0x03, 0x0A, 0x08); + + report.arguments[0] = 0x03; + report.arguments[1] = 0x03; + + return report; +} + +razer_report RazerController::razer_create_mode_breathing_two_colors_extended_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2) +{ + razer_report report = razer_create_report(0x0F, 0x02, 0x0C); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + report.arguments[2] = 0x02; + + report.arguments[3] = 0x02; + report.arguments[5] = 0x02; + + report.arguments[6] = r1; + report.arguments[7] = g1; + report.arguments[8] = b1; + report.arguments[9] = r2; + report.arguments[10] = g2; + report.arguments[11] = b2; + + return report; +} + +razer_report RazerController::razer_create_mode_breathing_two_colors_standard_matrix_report(unsigned char /*variable_storage*/, unsigned char /*led_id*/, unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2) +{ + razer_report report = razer_create_report(0x03, 0x0A, 0x08); + + report.arguments[0] = 0x03; + report.arguments[1] = 0x02; + report.arguments[2] = r1; + report.arguments[3] = g1; + report.arguments[4] = b1; + report.arguments[5] = r2; + report.arguments[6] = g2; + report.arguments[7] = b2; + + return report; +} + +razer_report RazerController::razer_create_mode_custom_extended_matrix_report() +{ + struct razer_report report = razer_create_report(0x0F, 0x02, 0x0C); + + report.arguments[0] = 0x00; + report.arguments[1] = 0x00; + report.arguments[2] = 0x08; + + return report; +} + +razer_report RazerController::razer_create_mode_custom_standard_matrix_report(unsigned char variable_storage) +{ + razer_report report = razer_create_report(0x03, 0x0A, 0x02); + + report.arguments[0] = 0x05; + report.arguments[1] = variable_storage; + + return report; +} + +razer_report RazerController::razer_create_mode_none_extended_matrix_report(unsigned char variable_storage, unsigned char led_id) +{ + struct razer_report report = razer_create_report(0x0F, 0x02, 06); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + report.arguments[2] = 0x00; + + return report; +} + +razer_report RazerController::razer_create_mode_none_standard_matrix_report(unsigned char /*variable_storage*/, unsigned char /*led_id*/) +{ + struct razer_report report = razer_create_report(0x03, 0x0A, 0x01); + + report.arguments[0] = 0x00; + + return report; +} + +razer_report RazerController::razer_create_mode_spectrum_cycle_extended_matrix_report(unsigned char variable_storage, unsigned char led_id) +{ + razer_report report = razer_create_report(0x0F, 0x02, 0x06); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + report.arguments[2] = 0x03; + + return report; +} + +razer_report RazerController::razer_create_mode_spectrum_cycle_standard_matrix_report(unsigned char /*variable_storage*/, unsigned char /*led_id*/) +{ + razer_report report = razer_create_report(0x03, 0x0A, 0x01); + + report.arguments[0] = 0x04; + + return report; +} + +razer_report RazerController::razer_create_mode_static_extended_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_report report = razer_create_report(0x0F, 0x02, 0x09); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + report.arguments[2] = 0x01; + + report.arguments[5] = 0x01; + report.arguments[6] = red; + report.arguments[7] = grn; + report.arguments[8] = blu; + + return report; +} + +razer_report RazerController::razer_create_mode_static_standard_matrix_report(unsigned char /*variable_storage*/, unsigned char /*led_id*/, unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_report report = razer_create_report(0x03, 0x0A, 0x04); + + report.arguments[0] = 0x06; + report.arguments[1] = red; + report.arguments[2] = grn; + report.arguments[3] = blu; + + return report; +} + +razer_report RazerController::razer_create_mode_wave_extended_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char direction) +{ + razer_report report = razer_create_report(0x0F, 0x02, 0x06); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + report.arguments[2] = 0x04; + + report.arguments[3] = direction; + report.arguments[4] = 0x28; + + return report; +} + +razer_report RazerController::razer_create_mode_wave_standard_matrix_report(unsigned char /*variable_storage*/, unsigned char /*led_id*/, unsigned char direction) +{ + razer_report report = razer_create_report(0x03, 0x0A, 0x02); + + report.arguments[0] = 0x01; + report.arguments[1] = direction; + + return report; +} + +razer_report RazerController::razer_create_set_led_rgb_report(unsigned char variable_storage, unsigned char led_id, unsigned char* rgb_data) +{ + razer_report report = razer_create_report(0x03, 0x01, 0x05); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + report.arguments[2] = rgb_data[0]; + report.arguments[3] = rgb_data[1]; + report.arguments[4] = rgb_data[2]; + + return report; +} + +razer_report RazerController::razer_create_set_led_effect_report(unsigned char variable_storage, unsigned char led_id, unsigned char effect) +{ + razer_report report = razer_create_report(0x03, 0x02, 0x03); + + report.arguments[0] = variable_storage; + report.arguments[1] = led_id; + + if(effect > 5) + { + report.arguments[2] = 5; + } + else + { + report.arguments[2] = effect; + } + + return report; +} + +/*---------------------------------------------------------------------------------*\ +| Get functions (request information from device) | +\*---------------------------------------------------------------------------------*/ + +unsigned char RazerController::razer_get_device_mode() +{ + std::string firmware_string = ""; + struct razer_report report = razer_create_report(0x00, RAZER_COMMAND_ID_GET_DEVICE_MODE, 0x02); + struct razer_report response_report = razer_create_response(); + + std::this_thread::sleep_for(2ms); + razer_usb_send(&report); + std::this_thread::sleep_for(5ms); + razer_usb_receive(&response_report); + + return(response_report.arguments[0]); +} + +std::string RazerController::razer_get_firmware() +{ + std::string firmware_string = ""; + struct razer_report report = razer_create_report(0x00, RAZER_COMMAND_ID_GET_FIRMWARE_VERSION, 0x02); + struct razer_report response_report = razer_create_response(); + + std::this_thread::sleep_for(2ms); + razer_usb_send(&report); + std::this_thread::sleep_for(5ms); + razer_usb_receive(&response_report); + + firmware_string = "v" + std::to_string(response_report.arguments[0]) + "." + std::to_string(response_report.arguments[1]); + + return firmware_string; +} + +std::string RazerController::razer_get_serial() +{ + char serial_string[64] = ""; + struct razer_report report = razer_create_report(0x00, RAZER_COMMAND_ID_GET_SERIAL_STRING, 0x16); + struct razer_report response_report = razer_create_response(); + + std::this_thread::sleep_for(2ms); + razer_usb_send(&report); + std::this_thread::sleep_for(5ms); + razer_usb_receive(&response_report); + + memcpy(&serial_string[0], &response_report.arguments[0], 22); + serial_string[22] = '\0'; + + for(size_t i = 0; i < 22; i++) + { + if(serial_string[i] < 30 || serial_string[i] > 126) + { + serial_string[i] = ' '; + } + } + + std::string ret_string = serial_string; + return ret_string; +} + +void RazerController::razer_get_keyboard_info(unsigned char* layout, unsigned char* variant) +{ + struct razer_report report = razer_create_report(0x00, RAZER_COMMAND_ID_GET_KEYBOARD_INFO, 0x00); + struct razer_report response_report = razer_create_response(); + + std::this_thread::sleep_for(1ms); + razer_usb_send(&report); + std::this_thread::sleep_for(1ms); + razer_usb_receive(&response_report); + + *layout = response_report.arguments[0]; + *variant = response_report.arguments[1]; +} + +unsigned char RazerController::GetKeyboardLayoutType() +{ + unsigned char layout; + unsigned char variant; + + RazerController::razer_get_keyboard_info(&layout, &variant); + + switch(layout) + { + case RAZER_KEYBOARD_LAYOUT_US: + case RAZER_KEYBOARD_LAYOUT_RUSSIAN: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_CHT: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_TURKISH: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_THAILAND: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_ARABIC: // Unconfirmed + return RAZER_LAYOUT_TYPE_ANSI; + + case RAZER_KEYBOARD_LAYOUT_GREEK: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_UK: + case RAZER_KEYBOARD_LAYOUT_NORDIC: + case RAZER_KEYBOARD_LAYOUT_KOREAN: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_PORTUGESE_BRAZIL: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_SPANISH_LATIN_AMERICAN: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_SWISS: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_SPANISH_EUR: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_ITALIAN: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_PORTUGESE_PORTUGA: // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_HEBREW: // Unconfirmed + return RAZER_LAYOUT_TYPE_ISO; + + case RAZER_KEYBOARD_LAYOUT_FRENCH: + return RAZER_LAYOUT_TYPE_AZERTY; + + case RAZER_KEYBOARD_LAYOUT_JAPAN: // Unconfirmed + return RAZER_LAYOUT_TYPE_JIS; + + case RAZER_KEYBOARD_LAYOUT_GERMAN: + return RAZER_LAYOUT_TYPE_QWERTZ; + + default: + return RAZER_LAYOUT_TYPE_ALL; + } +} + +std::string RazerController::GetKeyboardLayoutString() +{ + unsigned char layout; + unsigned char variant; + + RazerController::razer_get_keyboard_info(&layout, &variant); + + switch(layout) + { + case RAZER_KEYBOARD_LAYOUT_US: return "US (ANSI)"; + case RAZER_KEYBOARD_LAYOUT_GERMAN: return "German (QWERTZ)"; + case RAZER_KEYBOARD_LAYOUT_GREEK: return "Greek (ISO)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_FRENCH: return "French (ISO)"; + case RAZER_KEYBOARD_LAYOUT_RUSSIAN: return "Russian (ANSI)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_UK: return "UK (ISO)"; + case RAZER_KEYBOARD_LAYOUT_NORDIC: return "Nordic (ISO)"; + case RAZER_KEYBOARD_LAYOUT_CHT: return "Chinese Traditional (ANSI)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_KOREAN: return "Korean (ISO)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_TURKISH: return "Turkish (ANSI)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_THAILAND: return "Thai (ANSI)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_JAPAN: return "Japanese (JIS)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_PORTUGESE_BRAZIL: return "Portugese (Brazil) (ISO)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_SPANISH_LATIN_AMERICAN: return "Spanish (Latin america) (ISO)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_SWISS: return "Swiss (ISO)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_SPANISH_EUR: return "Spanish (Europe) (ISO)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_ITALIAN: return "Italian (ISO)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_PORTUGESE_PORTUGA: return "Portugese (Portugal) (ISO)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_HEBREW: return "Hebrew (ISO)"; // Unconfirmed + case RAZER_KEYBOARD_LAYOUT_ARABIC: return "Arabic (ANSI)"; // Unconfirmed + default: + std::string tmp = "Unknown: "; + tmp.append(std::to_string(layout)); + return tmp; + } +} + +std::string RazerController::GetVariantName() +{ + unsigned char layout; + unsigned char variant; + + RazerController::razer_get_keyboard_info(&layout, &variant); + + switch(variant) + { + case RAZER_KEYBOARD_VARIANT_BLACK: return "Black"; + case RAZER_KEYBOARD_VARIANT_QUARTZ: return "Quartz"; + case RAZER_KEYBOARD_VARIANT_MERCURY: return "Mercury"; + default: return "Unkown Variant"; + } +} + +/*---------------------------------------------------------------------------------*\ +| Set functions (send information to device) | +\*---------------------------------------------------------------------------------*/ + +void RazerController::razer_set_brightness(unsigned char brightness) +{ + razer_report report; + + switch(matrix_type) + { + case RAZER_MATRIX_TYPE_STANDARD: + case RAZER_MATRIX_TYPE_LINEAR: + report = razer_create_brightness_standard_report(RAZER_STORAGE_NO_SAVE, dev_led_id, brightness); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED: + report = razer_create_brightness_extended_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, brightness); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED_ARGB: + report = razer_create_brightness_extended_matrix_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_ARGB_CH_1, brightness); + razer_usb_send(&report); + + report = razer_create_brightness_extended_matrix_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_ARGB_CH_2, brightness); + razer_usb_send(&report); + + report = razer_create_brightness_extended_matrix_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_ARGB_CH_3, brightness); + razer_usb_send(&report); + + report = razer_create_brightness_extended_matrix_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_ARGB_CH_4, brightness); + razer_usb_send(&report); + + report = razer_create_brightness_extended_matrix_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_ARGB_CH_5, brightness); + razer_usb_send(&report); + + report = razer_create_brightness_extended_matrix_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_ARGB_CH_6, brightness); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_CUSTOM: + switch(dev_pid) + { + /*-------------------------------------------------*\ + | These devices use standard matrix reports | + \*-------------------------------------------------*/ + case RAZER_ORBWEAVER_CHROMA_PID: + report = razer_create_brightness_standard_report(RAZER_STORAGE_NO_SAVE, dev_led_id, brightness); + razer_usb_send(&report); + break; + } + break; + } +} + +void RazerController::razer_set_custom_frame(unsigned char row_index, unsigned char start_col, unsigned char stop_col, unsigned char* rgb_data) +{ + razer_argb_report argb_report; + razer_report report; + + switch(matrix_type) + { + case RAZER_MATRIX_TYPE_STANDARD: + report = razer_create_custom_frame_standard_matrix_report(row_index, start_col, stop_col, rgb_data); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED: + report = razer_create_custom_frame_extended_matrix_report(row_index, start_col, stop_col, rgb_data); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_LINEAR: + report = razer_create_custom_frame_linear_report(start_col, stop_col, rgb_data); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED_ARGB: + argb_report = razer_create_custom_frame_argb_report(row_index, stop_col, rgb_data); + razer_usb_send_argb(&argb_report); + break; + + case RAZER_MATRIX_TYPE_CUSTOM: + switch(dev_pid) + { + /*-------------------------------------------------*\ + | These devices use individual LED effect reports | + \*-------------------------------------------------*/ + case RAZER_TARTARUS_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, rgb_data); + razer_usb_send(&report); + break; + + case RAZER_DEATHADDER_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_LOGO, &rgb_data[3]); + razer_usb_send(&report); + break; + + case RAZER_NAGA_EPIC_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, &rgb_data[3]); + razer_usb_send(&report); + break; + + case RAZER_MAMBA_2012_WIRED_PID: + case RAZER_MAMBA_2012_WIRELESS_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + break; + + /*-------------------------------------------------*\ + | The Orbweaver Chroma has an unusual matrix layout | + | and the following code allows it to present as a | + | 5x5 matrix. The hardware layout is: | + | | + | XX XX XX XX XX XX XX | + | XX 01 02 03 04 05 XX | + | XX 06 07 08 09 10 XX | + | XX 11 12 13 14 15 XX | + | XX 16 XX 17 18 19 20 | + | | + | It uses a standard matrix report and transaction | + | ID 0x3F | + \*-------------------------------------------------*/ + case RAZER_ORBWEAVER_CHROMA_PID: + if(row_index != 3) + { + report = razer_create_custom_frame_standard_matrix_report(row_index + 1, start_col + 1, stop_col + 1, rgb_data); + razer_usb_send(&report); + } + else + { + unsigned char rgb_data_adj[6*3]; + + memcpy(&rgb_data_adj[0], &rgb_data[0], 3); + memcpy(&rgb_data_adj[6], &rgb_data[3], 3*4); + + report = razer_create_custom_frame_standard_matrix_report(row_index + 1, start_col + 1, stop_col + 2, rgb_data_adj); + razer_usb_send(&report); + } + break; + } + break; + } +} + +void RazerController::razer_set_device_mode(unsigned char device_mode) +{ + razer_report report = razer_create_device_mode_report(device_mode, 0x00); + razer_usb_send(&report); +} + +void RazerController::razer_set_mode_breathing_one_color(unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_report report; + + switch(matrix_type) + { + case RAZER_MATRIX_TYPE_STANDARD: + case RAZER_MATRIX_TYPE_LINEAR: + report = razer_create_mode_breathing_one_color_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, red, grn, blu); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED: + case RAZER_MATRIX_TYPE_EXTENDED_ARGB: + report = razer_create_mode_breathing_one_color_extended_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, red, grn, blu); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_CUSTOM: + unsigned char rgb_data[6]; + rgb_data[0] = red; + rgb_data[1] = grn; + rgb_data[2] = blu; + rgb_data[3] = red; + rgb_data[4] = grn; + rgb_data[5] = blu; + + switch(dev_pid) + { + /*-------------------------------------------------*\ + | These devices use individual LED effect reports | + \*-------------------------------------------------*/ + case RAZER_TARTARUS_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, rgb_data); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, 2); + razer_usb_send(&report); + break; + + case RAZER_DEATHADDER_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 2); + razer_usb_send(&report); + + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_LOGO, &rgb_data[3]); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_LOGO, 2); + razer_usb_send(&report); + break; + + case RAZER_NAGA_EPIC_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 2); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, &rgb_data[3]); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, 2); + razer_usb_send(&report); + break; + + case RAZER_MAMBA_2012_WIRED_PID: + case RAZER_MAMBA_2012_WIRELESS_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 2); + razer_usb_send(&report); + break; + + /*-------------------------------------------------*\ + | These devices use standard matrix reports | + \*-------------------------------------------------*/ + case RAZER_ORBWEAVER_CHROMA_PID: + report = razer_create_mode_breathing_one_color_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, red, grn, blu); + razer_usb_send(&report); + break; + } + break; + } +} + +void RazerController::razer_set_mode_breathing_random() +{ + razer_report report; + + switch(matrix_type) + { + case RAZER_MATRIX_TYPE_STANDARD: + case RAZER_MATRIX_TYPE_LINEAR: + report = razer_create_mode_breathing_random_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED: + case RAZER_MATRIX_TYPE_EXTENDED_ARGB: + report = razer_create_mode_breathing_random_extended_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_CUSTOM: + switch(dev_pid) + { + /*-------------------------------------------------*\ + | These devices use standard matrix reports | + \*-------------------------------------------------*/ + case RAZER_ORBWEAVER_CHROMA_PID: + report = razer_create_mode_breathing_random_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id); + razer_usb_send(&report); + break; + } + break; + } +} + +void RazerController::razer_set_mode_breathing_two_colors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2) +{ + razer_report report; + + switch(matrix_type) + { + case RAZER_MATRIX_TYPE_STANDARD: + case RAZER_MATRIX_TYPE_LINEAR: + report = razer_create_mode_breathing_two_colors_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, r1, g1, b1, r2, g2, b2); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED: + case RAZER_MATRIX_TYPE_EXTENDED_ARGB: + report = razer_create_mode_breathing_two_colors_extended_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, r1, g1, b1, r2, g2, b2); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_CUSTOM: + switch(dev_pid) + { + /*-------------------------------------------------*\ + | These devices use standard matrix reports | + \*-------------------------------------------------*/ + case RAZER_ORBWEAVER_CHROMA_PID: + report = razer_create_mode_breathing_two_colors_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, r1, g1, b1, r2, g2, b2); + razer_usb_send(&report); + break; + } + break; + } +} + +void RazerController::razer_set_mode_custom() +{ + razer_report report; + + switch(matrix_type) + { + case RAZER_MATRIX_TYPE_STANDARD: + case RAZER_MATRIX_TYPE_LINEAR: + report = razer_create_mode_custom_standard_matrix_report(RAZER_STORAGE_NO_SAVE); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED: + report = razer_create_mode_custom_extended_matrix_report(); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED_ARGB: + break; + + case RAZER_MATRIX_TYPE_CUSTOM: + switch(dev_pid) + { + /*-------------------------------------------------*\ + | These devices use individual LED effect reports | + \*-------------------------------------------------*/ + case RAZER_TARTARUS_CHROMA_PID: + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, 0); + razer_usb_send(&report); + break; + + case RAZER_DEATHADDER_CHROMA_PID: + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 0); + razer_usb_send(&report); + + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_LOGO, 0); + razer_usb_send(&report); + break; + + case RAZER_NAGA_EPIC_CHROMA_PID: + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 0); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, 0); + razer_usb_send(&report); + break; + + case RAZER_MAMBA_2012_WIRED_PID: + case RAZER_MAMBA_2012_WIRELESS_PID: + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 0); + razer_usb_send(&report); + break; + + /*-------------------------------------------------*\ + | These devices use standard matrix reports | + \*-------------------------------------------------*/ + case RAZER_ORBWEAVER_CHROMA_PID: + report = razer_create_mode_custom_standard_matrix_report(RAZER_STORAGE_NO_SAVE); + razer_usb_send(&report); + break; + } + break; + } +} + +void RazerController::razer_set_mode_none() +{ + razer_report report; + + switch(matrix_type) + { + case RAZER_MATRIX_TYPE_STANDARD: + case RAZER_MATRIX_TYPE_LINEAR: + report = razer_create_mode_none_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED: + case RAZER_MATRIX_TYPE_EXTENDED_ARGB: + report = razer_create_mode_none_extended_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_CUSTOM: + unsigned char rgb_data[6]; + rgb_data[0] = 0x00; + rgb_data[1] = 0x00; + rgb_data[2] = 0x00; + rgb_data[3] = 0x00; + rgb_data[4] = 0x00; + rgb_data[5] = 0x00; + + switch(dev_pid) + { + /*-------------------------------------------------*\ + | These devices use individual LED effect reports | + \*-------------------------------------------------*/ + case RAZER_TARTARUS_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, rgb_data); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, 0); + razer_usb_send(&report); + break; + + case RAZER_DEATHADDER_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 0); + razer_usb_send(&report); + + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_LOGO, &rgb_data[3]); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_LOGO, 0); + razer_usb_send(&report); + break; + + case RAZER_NAGA_EPIC_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 0); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, &rgb_data[3]); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, 0); + razer_usb_send(&report); + break; + + case RAZER_MAMBA_2012_WIRED_PID: + case RAZER_MAMBA_2012_WIRELESS_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 0); + razer_usb_send(&report); + break; + + /*-------------------------------------------------*\ + | These devices use standard matrix reports | + \*-------------------------------------------------*/ + case RAZER_ORBWEAVER_CHROMA_PID: + report = razer_create_mode_none_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id); + razer_usb_send(&report); + break; + } + break; + } +} + +void RazerController::razer_set_mode_spectrum_cycle() +{ + razer_report report; + + switch(matrix_type) + { + case RAZER_MATRIX_TYPE_STANDARD: + case RAZER_MATRIX_TYPE_LINEAR: + report = razer_create_mode_spectrum_cycle_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED: + case RAZER_MATRIX_TYPE_EXTENDED_ARGB: + report = razer_create_mode_spectrum_cycle_extended_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_CUSTOM: + switch(dev_pid) + { + /*-------------------------------------------------*\ + | These devices use individual LED effect reports | + \*-------------------------------------------------*/ + case RAZER_TARTARUS_CHROMA_PID: + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, 4); + razer_usb_send(&report); + break; + + case RAZER_DEATHADDER_CHROMA_PID: + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 4); + razer_usb_send(&report); + + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_LOGO, 4); + razer_usb_send(&report); + break; + + case RAZER_NAGA_EPIC_CHROMA_PID: + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 4); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, 4); + razer_usb_send(&report); + break; + + case RAZER_MAMBA_2012_WIRED_PID: + case RAZER_MAMBA_2012_WIRELESS_PID: + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 4); + razer_usb_send(&report); + break; + + /*-------------------------------------------------*\ + | These devices use standard matrix reports | + \*-------------------------------------------------*/ + case RAZER_ORBWEAVER_CHROMA_PID: + report = razer_create_mode_spectrum_cycle_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id); + razer_usb_send(&report); + break; + } + break; + } +} + +void RazerController::razer_set_mode_static(unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_report report; + + switch(matrix_type) + { + case RAZER_MATRIX_TYPE_STANDARD: + case RAZER_MATRIX_TYPE_LINEAR: + report = razer_create_mode_static_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, red, grn, blu); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED: + case RAZER_MATRIX_TYPE_EXTENDED_ARGB: + report = razer_create_mode_static_extended_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, red, grn, blu); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_CUSTOM: + unsigned char rgb_data[6]; + rgb_data[0] = red; + rgb_data[1] = grn; + rgb_data[2] = blu; + rgb_data[3] = red; + rgb_data[4] = grn; + rgb_data[5] = blu; + + switch(dev_pid) + { + /*-------------------------------------------------*\ + | These devices use individual LED effect reports | + \*-------------------------------------------------*/ + case RAZER_TARTARUS_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, rgb_data); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, 0); + razer_usb_send(&report); + break; + + case RAZER_DEATHADDER_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 0); + razer_usb_send(&report); + + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_LOGO, &rgb_data[3]); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_LOGO, 0); + razer_usb_send(&report); + break; + + case RAZER_NAGA_EPIC_CHROMA_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 0); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, &rgb_data[3]); + razer_usb_send(&report); + + std::this_thread::sleep_for(1ms); + + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_BACKLIGHT, 0); + razer_usb_send(&report); + break; + + case RAZER_MAMBA_2012_WIRED_PID: + case RAZER_MAMBA_2012_WIRELESS_PID: + report = razer_create_set_led_rgb_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, rgb_data); + razer_usb_send(&report); + report = razer_create_set_led_effect_report(RAZER_STORAGE_NO_SAVE, RAZER_LED_ID_SCROLL_WHEEL, 0); + razer_usb_send(&report); + break; + + /*-------------------------------------------------*\ + | These devices use standard matrix reports | + \*-------------------------------------------------*/ + case RAZER_ORBWEAVER_CHROMA_PID: + report = razer_create_mode_static_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, red, grn, blu); + razer_usb_send(&report); + break; + } + break; + } +} + +void RazerController::razer_set_mode_wave(unsigned char direction) +{ + razer_report report; + + switch(matrix_type) + { + case RAZER_MATRIX_TYPE_STANDARD: + case RAZER_MATRIX_TYPE_LINEAR: + report = razer_create_mode_wave_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, direction); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_EXTENDED: + case RAZER_MATRIX_TYPE_EXTENDED_ARGB: + report = razer_create_mode_wave_extended_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, direction); + razer_usb_send(&report); + break; + + case RAZER_MATRIX_TYPE_CUSTOM: + switch(dev_pid) + { + /*-------------------------------------------------*\ + | These devices use standard matrix reports | + \*-------------------------------------------------*/ + case RAZER_ORBWEAVER_CHROMA_PID: + report = razer_create_mode_wave_standard_matrix_report(RAZER_STORAGE_NO_SAVE, dev_led_id, direction); + razer_usb_send(&report); + break; + } + break; + } +} + +/*---------------------------------------------------------------------------------*\ +| USB transfer functions | +\*---------------------------------------------------------------------------------*/ + +int RazerController::razer_usb_receive(razer_report* report) +{ + return hid_get_feature_report(dev, (unsigned char*)report, sizeof(*report)); +} + +int RazerController::razer_usb_send(razer_report* report) +{ + report->crc = razer_calculate_crc(report); + + DeviceGuardLock _ = guard_manager_ptr->AwaitExclusiveAccess(); + return hid_send_feature_report(dev, (unsigned char*)report, sizeof(*report)); +} + +int RazerController::razer_usb_send_argb(razer_argb_report* report) +{ + DeviceGuardLock _ = guard_manager_ptr->AwaitExclusiveAccess(); + return hid_send_feature_report(dev_argb, (unsigned char*)report, sizeof(*report)); +} diff --git a/Controllers/RazerController/RazerController/RazerController.h b/Controllers/RazerController/RazerController/RazerController.h new file mode 100644 index 0000000..533af7b --- /dev/null +++ b/Controllers/RazerController/RazerController/RazerController.h @@ -0,0 +1,358 @@ +/*---------------------------------------------------------*\ +| RazerController.h | +| | +| Driver for Razer devices | +| | +| Adam Honse (CalcProgrammer1) 22 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include "RGBController.h" +#include "DeviceGuardManager.h" + +/*---------------------------------------------------------*\ +| Struct packing macro for GCC and MSVC | +\*---------------------------------------------------------*/ +#ifdef __GNUC__ +#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) +#endif + +#ifdef _MSC_VER +#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop)) +#endif + +/*---------------------------------------------------------*\ +| Razer Device Mode IDs | +\*---------------------------------------------------------*/ +enum +{ + RAZER_DEVICE_MODE_HARDWARE = 0x00, + RAZER_DEVICE_MODE_SOFTWARE = 0x03, +}; + +/*---------------------------------------------------------*\ +| Razer Command IDs | +\*---------------------------------------------------------*/ +enum +{ + /*-----------------------------------------------------*\ + | Set Commands | + \*-----------------------------------------------------*/ + RAZER_COMMAND_ID_SET_LED_STATE = 0x00, + RAZER_COMMAND_ID_SET_DEVICE_MODE = 0x04, + + /*-----------------------------------------------------*\ + | Get Commands | + \*-----------------------------------------------------*/ + RAZER_COMMAND_ID_GET_LED_STATE = 0x80, + RAZER_COMMAND_ID_GET_FIRMWARE_VERSION = 0x81, + RAZER_COMMAND_ID_GET_SERIAL_STRING = 0x82, + RAZER_COMMAND_ID_GET_DEVICE_MODE = 0x84, + RAZER_COMMAND_ID_GET_KEYBOARD_INFO = 0x86, +}; + +/*---------------------------------------------------------*\ +| Razer Storage Flags | +\*---------------------------------------------------------*/ +enum +{ + RAZER_STORAGE_NO_SAVE = 0x00, + RAZER_STORAGE_SAVE = 0x01, +}; + +/*---------------------------------------------------------*\ +| Razer LED IDs | +\*---------------------------------------------------------*/ +enum +{ + RAZER_LED_ID_ZERO = 0x00, + RAZER_LED_ID_SCROLL_WHEEL = 0x01, + RAZER_LED_ID_BATTERY = 0x03, + RAZER_LED_ID_LOGO = 0x04, + RAZER_LED_ID_BACKLIGHT = 0x05, + RAZER_LED_ID_MACRO = 0x07, + RAZER_LED_ID_GAME = 0x08, + RAZER_LED_ID_PROFILE_RED = 0x0C, + RAZER_LED_ID_PROFILE_GREEN = 0x0D, + RAZER_LED_ID_PROFILE_BLUE = 0x0E, + RAZER_LED_ID_RIGHT_SIDE = 0x10, + RAZER_LED_ID_LEFT_SIDE = 0x11, + RAZER_LED_ID_ARGB_CH_1 = 0x1A, + RAZER_LED_ID_ARGB_CH_2 = 0x1B, + RAZER_LED_ID_ARGB_CH_3 = 0x1C, + RAZER_LED_ID_ARGB_CH_4 = 0x1D, + RAZER_LED_ID_ARGB_CH_5 = 0x1E, + RAZER_LED_ID_ARGB_CH_6 = 0x1F, +}; + +/*---------------------------------------------------------*\ +| Razer Matrix Type | +\*---------------------------------------------------------*/ +enum +{ + RAZER_MATRIX_TYPE_NONE = 0, + RAZER_MATRIX_TYPE_STANDARD = 1, + RAZER_MATRIX_TYPE_EXTENDED = 2, + RAZER_MATRIX_TYPE_LINEAR = 3, + RAZER_MATRIX_TYPE_EXTENDED_ARGB = 4, + RAZER_MATRIX_TYPE_CUSTOM = 5, +}; + +/*---------------------------------------------------------*\ +| Razer Keyboard Layout | +\*---------------------------------------------------------*/ +enum +{ + RAZER_KEYBOARD_LAYOUT_NONE = 0, + RAZER_KEYBOARD_LAYOUT_US = 1, + RAZER_KEYBOARD_LAYOUT_GREEK = 2, + RAZER_KEYBOARD_LAYOUT_GERMAN = 3, + RAZER_KEYBOARD_LAYOUT_FRENCH = 4, + RAZER_KEYBOARD_LAYOUT_RUSSIAN = 5, + RAZER_KEYBOARD_LAYOUT_UK = 6, + RAZER_KEYBOARD_LAYOUT_NORDIC = 7, + RAZER_KEYBOARD_LAYOUT_CHT = 8, + RAZER_KEYBOARD_LAYOUT_KOREAN = 9, + RAZER_KEYBOARD_LAYOUT_TURKISH = 10, + RAZER_KEYBOARD_LAYOUT_THAILAND = 11, + RAZER_KEYBOARD_LAYOUT_JAPAN = 12, + RAZER_KEYBOARD_LAYOUT_PORTUGESE_BRAZIL = 13, + RAZER_KEYBOARD_LAYOUT_SPANISH_LATIN_AMERICAN = 14, + RAZER_KEYBOARD_LAYOUT_SWISS = 15, + RAZER_KEYBOARD_LAYOUT_SPANISH_EUR = 16, + RAZER_KEYBOARD_LAYOUT_ITALIAN = 17, + RAZER_KEYBOARD_LAYOUT_PORTUGESE_PORTUGA = 18, + RAZER_KEYBOARD_LAYOUT_HEBREW = 19, + RAZER_KEYBOARD_LAYOUT_ARABIC = 20, +}; + +/*---------------------------------------------------------*\ +| Razer Layout Type | +\*---------------------------------------------------------*/ +enum +{ + RAZER_LAYOUT_TYPE_NONE = 0x00, + RAZER_LAYOUT_TYPE_ANSI = 0x01, + RAZER_LAYOUT_TYPE_ISO = 0x02, + RAZER_LAYOUT_TYPE_JIS = 0x04, + RAZER_LAYOUT_TYPE_QWERTZ = 0x08, + RAZER_LAYOUT_TYPE_AZERTY = 0x10, + + RAZER_LAYOUT_TYPE_ALL = RAZER_LAYOUT_TYPE_ANSI | RAZER_LAYOUT_TYPE_ISO + | RAZER_LAYOUT_TYPE_JIS | RAZER_LAYOUT_TYPE_QWERTZ +}; + +/*---------------------------------------------------------*\ +| Razer Keyboard Variant | +\*---------------------------------------------------------*/ +enum +{ + RAZER_KEYBOARD_VARIANT_BLACK = 0x00, + RAZER_KEYBOARD_VARIANT_QUARTZ = 0x80, + RAZER_KEYBOARD_VARIANT_MERCURY = 0x82, +}; + +/*---------------------------------------------------------*\ +| Razer Report Type (taken from OpenRazer) | +\*---------------------------------------------------------*/ +struct razer_rgb +{ + unsigned char r,g,b; +}; + +union transaction_id_union +{ + unsigned char id; + struct transaction_parts + { + unsigned char device : 3; + unsigned char id : 5; + } parts; +}; + +union command_id_union +{ + unsigned char id; + struct command_id_parts + { + unsigned char direction : 1; + unsigned char id : 7; + } parts; +}; + +PACK(struct razer_report +{ + unsigned char report_id; + unsigned char status; + union transaction_id_union transaction_id; + unsigned short remaining_packets; + unsigned char protocol_type; + unsigned char data_size; + unsigned char command_class; + union command_id_union command_id; + unsigned char arguments[80]; + unsigned char crc; + unsigned char reserved; +}); + +/*---------------------------------------------------------*\ +| Razer ARGB Report Type (taken from OpenRazer) | +\*---------------------------------------------------------*/ +PACK(struct razer_argb_report +{ + unsigned char hid_id; + unsigned char report_id; + unsigned char channel_1; + unsigned char channel_2; + unsigned char pad; + unsigned char last_idx; + unsigned char color_data[315]; +}); + +class RazerController +{ +public: + RazerController(hid_device* dev_handle, hid_device* dev_argb_handle, const char* path, unsigned short pid, std::string dev_name); + ~RazerController(); + + unsigned int GetDeviceIndex(); + device_type GetDeviceType(); + std::string GetDeviceLocation(); + std::string GetFirmwareString(); + std::string GetName(); + std::string GetSerialString(); + unsigned char GetMaxBrightness(); + + unsigned char GetKeyboardLayoutType(); + std::string GetKeyboardLayoutString(); + std::string GetVariantName(); + + void SetBrightness(unsigned char brightness); + + void SetLEDs(RGBColor* colors); + void SetAddressableZoneSizes(unsigned char zone_1_size, unsigned char zone_2_size, unsigned char zone_3_size, unsigned char zone_4_size, unsigned char zone_5_size, unsigned char zone_6_size); + + void SetModeBreathingRandom(); + void SetModeBreathingOneColor(unsigned char red, unsigned char grn, unsigned char blu); + void SetModeBreathingTwoColors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2); + void SetModeOff(); + void SetModeSpectrumCycle(); + void SetModeStatic(unsigned char red, unsigned char grn, unsigned char blu); + void SetModeWave(unsigned char direction); + + bool SupportsBreathing(); + bool SupportsReactive(); + bool SupportsWave(); + +private: + hid_device* dev; + hid_device* dev_argb; + unsigned short dev_pid; + + /*---------------------------------------------------------*\ + | Device-specific protocol settings | + \*---------------------------------------------------------*/ + unsigned char dev_transaction_id; + unsigned char dev_led_id; + + /*---------------------------------------------------------*\ + | Device information strings | + \*---------------------------------------------------------*/ + std::string firmware_version; + std::string location; + std::string name; + + /*---------------------------------------------------------*\ + | Index of device in Razer device list | + \*---------------------------------------------------------*/ + unsigned int device_index; + + /*---------------------------------------------------------*\ + | HID report index for request and response | + \*---------------------------------------------------------*/ + unsigned char report_index; + unsigned char response_index; + + /*---------------------------------------------------------*\ + | Matrix type | + \*---------------------------------------------------------*/ + unsigned char matrix_type; + + /*---------------------------------------------------------*\ + | Mutex lock to sync with other softwares | + \*---------------------------------------------------------*/ + DeviceGuardManager* guard_manager_ptr; + + /*---------------------------------------------------------*\ + | Private functions based on OpenRazer | + \*---------------------------------------------------------*/ + unsigned char razer_calculate_crc(razer_report* report); + razer_report razer_create_report(unsigned char command_class, unsigned char command_id, unsigned char data_size); + razer_report razer_create_response(); + + razer_report razer_create_addressable_size_report(unsigned char zone_1_size, unsigned char zone_2_size, unsigned char zone_3_size, unsigned char zone_4_size, unsigned char zone_5_size, unsigned char zone_6_size); + razer_report razer_create_addressable_startup_detect_report(bool enable); + razer_report razer_create_brightness_extended_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char brightness); + razer_report razer_create_brightness_standard_report(unsigned char variable_storage, unsigned char led_id, unsigned char brightness); + razer_argb_report razer_create_custom_frame_argb_report(unsigned char row_index, unsigned char stop_col, unsigned char* rgb_data); + razer_report razer_create_custom_frame_linear_report(unsigned char start_col, unsigned char stop_col, unsigned char* rgb_data); + razer_report razer_create_custom_frame_extended_matrix_report(unsigned char row_index, unsigned char start_col, unsigned char stop_col, unsigned char* rgb_data); + razer_report razer_create_custom_frame_standard_matrix_report(unsigned char row_index, unsigned char start_col, unsigned char stop_col, unsigned char* rgb_data); + razer_report razer_create_device_mode_report(unsigned char mode, unsigned char param); + razer_report razer_create_mode_breathing_one_color_extended_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char red, unsigned char grn, unsigned char blu); + razer_report razer_create_mode_breathing_one_color_standard_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char red, unsigned char grn, unsigned char blu); + razer_report razer_create_mode_breathing_random_extended_matrix_report(unsigned char variable_storage, unsigned char led_id); + razer_report razer_create_mode_breathing_random_standard_matrix_report(unsigned char variable_storage, unsigned char led_id); + razer_report razer_create_mode_breathing_two_colors_extended_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2); + razer_report razer_create_mode_breathing_two_colors_standard_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2); + razer_report razer_create_mode_custom_extended_matrix_report(); + razer_report razer_create_mode_custom_standard_matrix_report(unsigned char variable_storage); + razer_report razer_create_mode_none_extended_matrix_report(unsigned char variable_storage, unsigned char led_id); + razer_report razer_create_mode_none_standard_matrix_report(unsigned char variable_storage, unsigned char led_id); + razer_report razer_create_mode_spectrum_cycle_extended_matrix_report(unsigned char variable_storage, unsigned char led_id); + razer_report razer_create_mode_spectrum_cycle_standard_matrix_report(unsigned char variable_storage, unsigned char led_id); + razer_report razer_create_mode_static_extended_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char red, unsigned char grn, unsigned char blu); + razer_report razer_create_mode_static_standard_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char red, unsigned char grn, unsigned char blu); + razer_report razer_create_mode_wave_extended_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char direction); + razer_report razer_create_mode_wave_standard_matrix_report(unsigned char variable_storage, unsigned char led_id, unsigned char direction); + razer_report razer_create_set_led_effect_report(unsigned char variable_storage, unsigned char led_id, unsigned char effect); + razer_report razer_create_set_led_rgb_report(unsigned char variable_storage, unsigned char led_id, unsigned char* rgb_data); + + unsigned char razer_get_device_mode(); + std::string razer_get_firmware(); + std::string razer_get_serial(); + void razer_get_keyboard_info(unsigned char* layout, unsigned char* variant); + + void razer_set_brightness(unsigned char brightness); + void razer_set_custom_frame(unsigned char row_index, unsigned char start_col, unsigned char stop_col, unsigned char* rgb_data); + + void razer_set_device_mode(unsigned char device_mode); + + void razer_set_mode_breathing_random(); + void razer_set_mode_breathing_one_color(unsigned char red, unsigned char grn, unsigned char blu); + void razer_set_mode_breathing_two_colors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2); + void razer_set_mode_custom(); + void razer_set_mode_none(); + void razer_set_mode_spectrum_cycle(); + void razer_set_mode_static(unsigned char red, unsigned char grn, unsigned char blu); + void razer_set_mode_wave(unsigned char direction); + + int razer_usb_receive(razer_report* report); + int razer_usb_send(razer_report* report); + int razer_usb_send_argb(razer_argb_report* report); + + std::chrono::time_point last_update_time; + std::atomic keepalive_thread_run; + std::thread * keepalive_thread; + + void KeepaliveThreadFunction(); +}; diff --git a/Controllers/RazerController/RazerControllerDetect.cpp b/Controllers/RazerController/RazerControllerDetect.cpp new file mode 100644 index 0000000..b94f95b --- /dev/null +++ b/Controllers/RazerController/RazerControllerDetect.cpp @@ -0,0 +1,464 @@ +/*---------------------------------------------------------*\ +| RazerControllerDetect.cpp | +| | +| Detector for Razer devices | +| | +| Adam Honse (CalcProgrammer1) 22 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "Detector.h" +#include "RazerController.h" +#include "RazerKrakenController.h" +#include "RazerKrakenV3Controller.h" +#include "RazerKrakenV4Controller.h" +#include "RazerHanboController.h" +#include "RazerDevices.h" +#include "ResourceManager.h" +#include "RGBController_Razer.h" +#include "RGBController_RazerAddressable.h" +#include "RGBController_RazerKraken.h" +#include "RGBController_RazerKrakenV3.h" +#include "RGBController_RazerKrakenV4.h" +#include "RGBController_RazerHanbo.h" + +/******************************************************************************************\ +* * +* DetectRazerControllers * +* * +* Tests the USB address to see if a Razer controller exists there. * +* * +\******************************************************************************************/ + +void DetectRazerControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RazerController* controller = new RazerController(dev, dev, info->path, info->product_id, name); + + RGBController_Razer* rgb_controller = new RGBController_Razer(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectRazerControllers() */ + +/******************************************************************************************\ +* * +* DetectRazerARGBControllers * +* * +* Tests the USB address to see if a Razer ARGB controller exists there. * +* * +\******************************************************************************************/ + + +/*---------------------------------------------------------------------*\ +| Tracks the paths used in DetectRazerARGBControllers so multiple Razer | +| devices can be detected without all controlling the same device. | +\*---------------------------------------------------------------------*/ +static std::unordered_set used_paths; + +/*--------------------------------------------------------------------------------*\ +| Removes all entries in used_paths so device discovery does not skip any of them. | +\*--------------------------------------------------------------------------------*/ +void ResetRazerARGBControllersPaths() +{ + used_paths.clear(); +} + +void DetectRazerARGBControllers(hid_device_info* info, const std::string& name) +{ + /*-------------------------------------------------------------------------------------------------*\ + | Razer's ARGB controller uses two different interfaces, one for 90-byte Razer report packets and | + | one for 320-byte ARGB packets. Interface 0 for 90-byte and interface 1 for 320-byte. | + | | + | Create a local copy of the HID enumerations for the Razer ARGB controller VID/PID and iterate | + | through it. This prevents detection from failing if interface 1 comes before interface 0 in the | + | main info list. | + \*-------------------------------------------------------------------------------------------------*/ + hid_device* dev_interface_0 = nullptr; + hid_device* dev_interface_1 = nullptr; + hid_device_info* info_full = hid_enumerate(RAZER_VID, RAZER_CHROMA_ADDRESSABLE_RGB_CONTROLLER_PID); + hid_device_info* info_temp = info_full; + /*--------------------------------------------------------------------------------------------*\ + | Keep track of paths so they can be added to used_paths only if both interfaces can be found. | + \*--------------------------------------------------------------------------------------------*/ + std::string dev_interface_0_path; + std::string dev_interface_1_path; + + while(info_temp) + { + /*----------------------------------------------------------------------------*\ + | Check for paths used on an already registered Razer ARGB controller to avoid | + | registering multiple controllers that refer to the same physical hardware. | + \*----------------------------------------------------------------------------*/ + if(info_temp->vendor_id == info->vendor_id + && info_temp->product_id == info->product_id + && used_paths.find(info_temp->path) == used_paths.end() ) + { + if(info_temp->interface_number == 0) + { + dev_interface_0 = hid_open_path(info_temp->path); + dev_interface_0_path = info_temp->path; + } + else if(info_temp->interface_number == 1) + { + dev_interface_1 = hid_open_path(info_temp->path); + dev_interface_1_path = info_temp->path; + } + } + if(dev_interface_0 && dev_interface_1) + { + break; + } + info_temp = info_temp->next; + } + + hid_free_enumeration(info_full); + + if(dev_interface_0 && dev_interface_1) + { + RazerController* controller = new RazerController(dev_interface_0, dev_interface_1, info->path, info->product_id, name); + RGBController_RazerAddressable* rgb_controller = new RGBController_RazerAddressable(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + used_paths.insert(dev_interface_0_path); + used_paths.insert(dev_interface_1_path); + } + else + { + // Not all of them could be opened, do some cleanup + hid_close(dev_interface_0); + hid_close(dev_interface_1); + } +} + +/******************************************************************************************\ +* * +* DetectRazerKrakenController * +* * +* Tests the USB address to see if a Razer Kraken controller exists there. * +* * +\******************************************************************************************/ + +void DetectRazerKrakenControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RazerKrakenController* controller = new RazerKrakenController(dev, info->path, info->product_id, name); + + RGBController_RazerKraken* rgb_controller = new RGBController_RazerKraken(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectRazerKrakenControllers() */ + +/******************************************************************************************\ +* * +* DetectRazerKrakenV3Controllers * +* * +* Tests the USB address to see if a Razer Kraken V3 controller exists there. * +* * +\******************************************************************************************/ + +void DetectRazerKrakenV3Controllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RazerKrakenV3Controller* controller = new RazerKrakenV3Controller(dev, info->path, info->product_id, name); + + RGBController_RazerKrakenV3* rgb_controller = new RGBController_RazerKrakenV3(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectRazerKrakenV3Controllers() */ + +/******************************************************************************************\ +* * +* DetectRazerKrakenV4Controllers * +* * +* Tests the USB address to see if a Razer Kraken V4 controller exists there. * +* * +\******************************************************************************************/ + +void DetectRazerKrakenV4Controllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RazerKrakenV4Controller* controller = new RazerKrakenV4Controller(dev, info->path, info->product_id, name); + + RGBController_RazerKrakenV4* rgb_controller = new RGBController_RazerKrakenV4(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectRazerKrakenV4Controllers() */ + +/******************************************************************************************\ +* * +* DetectRazerHanboController * +* * +* Tests the USB address to see if a Razer Hanbo controller exists there. * +* * +\******************************************************************************************/ + +void DetectRazerHanboControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RazerHanboController* controller = new RazerHanboController(dev, info->path, info->product_id, name); + + RGBController_RazerHanbo* rgb_controller = new RGBController_RazerHanbo(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectRazerHanboControllers() */ + +/*-----------------------------------------------------------------------------------------------------*\ +| Keyboards | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow 2019", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_2019_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow Chroma", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_CHROMA_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow Chroma Tournament Edition", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_CHROMA_TE_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow Chroma V2", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_CHROMA_V2_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow Elite", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_ELITE_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow Overwatch", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_OVERWATCH_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V3", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V3_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V3 Pro (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V3_PRO_WIRED_PID, 0x02, 0x01, 0x02); +// REGISTER_HID_DETECTOR_PU ("Razer Blackwidow V3 Pro (Bluetooth)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V3_PRO_BLUETOOTH_PID, 0x01, 0x00); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V3 Pro (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V3_PRO_WIRELESS_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V3 TKL", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V3_TKL_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V3 Mini (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V3_MINI_WIRED_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V3 Mini (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V3_MINI_WIRELESS_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PID, 0x03, 0x01, 0x00); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro", /* firmware < 1.5 */ DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PRO_PID, 0x03, 0x01, 0x00); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro", /* firmware >= 1.5 */ DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PRO_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro 75% (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PRO_75_WIRED_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Pro 75% (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_PRO_75_WIRELESS_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 75% (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_75_WIRED_PID, 0x03, 0x01, 0x00); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 X", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_X_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 TKL (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_TKL_WIRED_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 TKL (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_TKL_WIRELESS_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Low Profile TKL (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRED_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow V4 Low Profile TKL (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRELESS_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow X Chroma", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_X_CHROMA_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blackwidow X Chroma Tournament Edition", DetectRazerControllers, RAZER_VID, RAZER_BLACKWIDOW_X_CHROMA_TE_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Cynosa Chroma", DetectRazerControllers, RAZER_VID, RAZER_CYNOSA_CHROMA_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Cynosa Chroma V2", DetectRazerControllers, RAZER_VID, RAZER_CYNOSA_V2_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Cynosa Lite", DetectRazerControllers, RAZER_VID, RAZER_CYNOSA_LITE_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathstalker Chroma", DetectRazerControllers, RAZER_VID, RAZER_DEATHSTALKER_CHROMA_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathstalker V2", DetectRazerControllers, RAZER_VID, RAZER_DEATHSTALKER_V2_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Deathstalker V2 Pro TKL (Wired)", DetectRazerControllers, RAZER_VID, RAZER_DEATHSTALKER_V2_PRO_TKL_WIRED_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Deathstalker V2 Pro TKL (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_DEATHSTALKER_V2_PRO_TKL_WIRELESS_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathstalker V2 Pro (Wired)", DetectRazerControllers, RAZER_VID, RAZER_DEATHSTALKER_V2_PRO_WIRED_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Deathstalker V2 Pro (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_DEATHSTALKER_V2_PRO_WIRELESS_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Huntsman", DetectRazerControllers, RAZER_VID, RAZER_HUNTSMAN_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Huntsman Elite", DetectRazerControllers, RAZER_VID, RAZER_HUNTSMAN_ELITE_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Huntsman Mini", DetectRazerControllers, RAZER_VID, RAZER_HUNTSMAN_MINI_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Huntsman Mini Analog", DetectRazerControllers, RAZER_VID, RAZER_HUNTSMAN_MINI_ANALOG_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Huntsman Tournament Edition", DetectRazerControllers, RAZER_VID, RAZER_HUNTSMAN_TE_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Huntsman V2 Analog", DetectRazerControllers, RAZER_VID, RAZER_HUNTSMAN_V2_ANALOG_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Huntsman V2 TKL", DetectRazerControllers, RAZER_VID, RAZER_HUNTSMAN_V2_TKL_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Huntsman V2", DetectRazerControllers, RAZER_VID, RAZER_HUNTSMAN_V2_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Huntsman V3 Pro", DetectRazerControllers, RAZER_VID, RAZER_HUNTSMAN_V3_PRO_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Huntsman V3 Pro TKL White", DetectRazerControllers, RAZER_VID, RAZER_HUNTSMAN_V3_PRO_TKL_WHITE_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Ornata Chroma", DetectRazerControllers, RAZER_VID, RAZER_ORNATA_CHROMA_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Ornata Chroma V2", DetectRazerControllers, RAZER_VID, RAZER_ORNATA_CHROMA_V2_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Ornata V3", DetectRazerControllers, RAZER_VID, RAZER_ORNATA_V3_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Ornata V3 Rev2", DetectRazerControllers, RAZER_VID, RAZER_ORNATA_V3_REV2_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Ornata V3 TKL", DetectRazerControllers, RAZER_VID, RAZER_ORNATA_V3_TKL_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Ornata V3 X", DetectRazerControllers, RAZER_VID, RAZER_ORNATA_V3_X_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Ornata V3 X Rev2", DetectRazerControllers, RAZER_VID, RAZER_ORNATA_V3_X_REV2_PID, 0x02, 0x01, 0x02); +/*-----------------------------------------------------------------------------------------------------*\ +| Laptops | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Razer Blade (2016)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2016_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade (Late 2016)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_LATE_2016_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 14 (2021)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_14_2021_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 14 (2022)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_14_2022_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 14 (2023)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_14_2023_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2022)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_15_2022_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2018 Advanced)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2018_ADVANCED_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2018 Base)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2018_BASE_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2018 Mercury)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2018_MERCURY_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2019 Advanced)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2019_ADVANCED_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2019 Base)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2019_BASE_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2019 Mercury)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2019_MERCURY_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2019 Studio)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2019_STUDIO_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2020 Advanced)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2020_ADVANCED_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2020 Base)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2020_BASE_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (Late 2020)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_LATE_2020_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2021 Advanced)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2021_ADVANCED_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (Late 2021 Advanced)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_LATE_2021_ADVANCED_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2021 Base)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2021_BASE_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade 15 (2021 Base)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_2021_BASE_V2_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Pro (2016)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_PRO_2016_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Pro (2017)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_PRO_2017_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Pro (2017 FullHD)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_PRO_2017_FULLHD_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Pro (2019)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_PRO_2019_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Pro (Late 2019)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_PRO_LATE_2019_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Pro 17 (2020)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_PRO_17_2020_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Pro 17 (2021)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_PRO_17_2021_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Stealth (2016)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_STEALTH_2016_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Stealth (Late 2016)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_STEALTH_LATE_2016_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Stealth (2017)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_STEALTH_2017_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Stealth (Late 2017)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_STEALTH_LATE_2017_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Stealth (2019)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_STEALTH_2019_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Stealth (Late 2019)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_STEALTH_LATE_2019_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Stealth (2020)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_STEALTH_2020_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Blade Stealth (Late 2020)", DetectRazerControllers, RAZER_VID, RAZER_BLADE_STEALTH_LATE_2020_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Book 13 (2020)", DetectRazerControllers, RAZER_VID, RAZER_BOOK_13_2020_PID, 0x02, 0x01, 0x02); + +/*-----------------------------------------------------------------------------------------------------*\ +| Mice | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Razer Abyssus Elite D.Va Edition", DetectRazerControllers, RAZER_VID, RAZER_ABYSSUS_ELITE_DVA_EDITION_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Abyssus Essential", DetectRazerControllers, RAZER_VID, RAZER_ABYSSUS_ESSENTIAL_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk Essential", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_ESSENTIAL_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk Ultimate (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_ULTIMATE_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk Ultimate (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_ULTIMATE_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk V2", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V2_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk V3", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V3_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk V3 35K", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V3_35K_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk V3 Pro (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V3_PRO_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk V3 Pro (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V3_PRO_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk V3 Pro 35K (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V3_PRO_35K_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk V3 Pro 35K (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V3_PRO_35K_WIRELESS_PID, 0x00, 0x01, 0x02); +// REGISTER_HID_DETECTOR_PU ("Razer Basilisk V3 Pro (Bluetooth)", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V3_PRO_BLUETOOTH_PID, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk V3 Pro 35K Phantom Green (Wired)", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V3_PRO_35K_PG_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk V3 Pro 35K Phantom Green (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V3_PRO_35K_PG_WIRELESS_PID, 0x00, 0x01, 0x02); +// REGISTER_HID_DETECTOR_PU("Razer Basilisk V3 Pro 35K Phantom Green (Bluetooth)",DetectRazerControllers, RAZER_BLUETOOTH_VID, RAZER_BASILISK_V3_PRO_35K_PG_BLUETOOTH_PID, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Basilisk V3 X HyperSpeed", DetectRazerControllers, RAZER_VID, RAZER_BASILISK_V3_X_HYPERSPEED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Cobra", DetectRazerControllers, RAZER_VID, RAZER_COBRA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Cobra Pro (Wired)", DetectRazerControllers, RAZER_VID, RAZER_COBRA_PRO_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Cobra Pro (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_COBRA_PRO_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathadder Chroma", DetectRazerControllers, RAZER_VID, RAZER_DEATHADDER_CHROMA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathadder Elite", DetectRazerControllers, RAZER_VID, RAZER_DEATHADDER_ELITE_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathadder Essential", DetectRazerControllers, RAZER_VID, RAZER_DEATHADDER_ESSENTIAL_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathadder Essential V2", DetectRazerControllers, RAZER_VID, RAZER_DEATHADDER_ESSENTIAL_V2_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathadder Essential White Edition", DetectRazerControllers, RAZER_VID, RAZER_DEATHADDER_ESSENTIAL_WHITE_EDITION_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathadder V2", DetectRazerControllers, RAZER_VID, RAZER_DEATHADDER_V2_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathadder V2 Mini", DetectRazerControllers, RAZER_VID, RAZER_DEATHADDER_V2_MINI_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathadder V2 Pro (Wired)", DetectRazerControllers, RAZER_VID, RAZER_DEATHADDER_V2_PRO_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Deathadder V2 Pro (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_DEATHADDER_V2_PRO_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Diamondback", DetectRazerControllers, RAZER_VID, RAZER_DIAMONDBACK_CHROMA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Lancehead 2017 (Wired)", DetectRazerControllers, RAZER_VID, RAZER_LANCEHEAD_2017_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Lancehead 2017 (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_LANCEHEAD_2017_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Lancehead 2019 (Wired)", DetectRazerControllers, RAZER_VID, RAZER_LANCEHEAD_2019_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Lancehead 2019 (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_LANCEHEAD_2019_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Lancehead Tournament Edition", DetectRazerControllers, RAZER_VID, RAZER_LANCEHEAD_TE_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mamba 2012 (Wired)", DetectRazerControllers, RAZER_VID, RAZER_MAMBA_2012_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mamba 2012 (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_MAMBA_2012_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mamba 2015 (Wired)", DetectRazerControllers, RAZER_VID, RAZER_MAMBA_2015_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mamba 2015 (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_MAMBA_2015_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mamba 2018 (Wired)", DetectRazerControllers, RAZER_VID, RAZER_MAMBA_2018_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mamba 2018 (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_MAMBA_2018_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mamba Elite", DetectRazerControllers, RAZER_VID, RAZER_MAMBA_ELITE_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mamba Hyperflux (Wired)", DetectRazerControllers, RAZER_VID, RAZER_MAMBA_HYPERFLUX_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mamba Tournament Edition", DetectRazerControllers, RAZER_VID, RAZER_MAMBA_TE_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Naga Chroma", DetectRazerControllers, RAZER_VID, RAZER_NAGA_CHROMA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Naga Classic", DetectRazerControllers, RAZER_VID, RAZER_NAGA_CLASSIC_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Naga Epic Chroma", DetectRazerControllers, RAZER_VID, RAZER_NAGA_EPIC_CHROMA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Naga Left Handed", DetectRazerControllers, RAZER_VID, RAZER_NAGA_LEFT_HANDED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Naga Hex V2", DetectRazerControllers, RAZER_VID, RAZER_NAGA_HEX_V2_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Naga Trinity", DetectRazerControllers, RAZER_VID, RAZER_NAGA_TRINITY_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Naga Pro (Wired)", DetectRazerControllers, RAZER_VID, RAZER_NAGA_PRO_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Naga Pro (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_NAGA_PRO_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Naga Pro V2 (Wired)", DetectRazerControllers, RAZER_VID, RAZER_NAGA_PRO_V2_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Naga Pro V2 (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_NAGA_PRO_V2_WIRELESS_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Viper", DetectRazerControllers, RAZER_VID, RAZER_VIPER_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Viper 8kHz", DetectRazerControllers, RAZER_VID, RAZER_VIPER_8KHZ_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Viper Mini", DetectRazerControllers, RAZER_VID, RAZER_VIPER_MINI_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Viper Ultimate (Wired)", DetectRazerControllers, RAZER_VID, RAZER_VIPER_ULTIMATE_WIRED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Viper Ultimate (Wireless)", DetectRazerControllers, RAZER_VID, RAZER_VIPER_ULTIMATE_WIRELESS_PID, 0x00, 0x01, 0x02); + +/*-----------------------------------------------------------------------------------------------------*\ +| Keypads | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Razer Orbweaver Chroma", DetectRazerControllers, RAZER_VID, RAZER_ORBWEAVER_CHROMA_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Tartarus Chroma", DetectRazerControllers, RAZER_VID, RAZER_TARTARUS_CHROMA_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Tartarus Pro", DetectRazerControllers, RAZER_VID, RAZER_TARTARUS_PRO_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Tartarus V2", DetectRazerControllers, RAZER_VID, RAZER_TARTARUS_V2_PID, 0x02, 0x01, 0x02); + +/*-----------------------------------------------------------------------------------------------------*\ +| Headsets | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Razer Kraken 7.1", DetectRazerKrakenControllers, RAZER_VID, RAZER_KRAKEN_CLASSIC_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken 7.1", DetectRazerKrakenControllers, RAZER_VID, RAZER_KRAKEN_CLASSIC_ALT_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken 7.1 Chroma", DetectRazerKrakenControllers, RAZER_VID, RAZER_KRAKEN_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken 7.1 V2", DetectRazerKrakenControllers, RAZER_VID, RAZER_KRAKEN_V2_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken Kitty Edition", DetectRazerControllers, RAZER_VID, RAZER_KRAKEN_KITTY_EDITION_PID, 0x01, 0x01, 0x03); +REGISTER_HID_DETECTOR_IPU("Razer Kraken Kitty Black Edition", DetectRazerControllers, RAZER_VID, RAZER_KRAKEN_KITTY_BLACK_EDITION_PID, 0x01, 0x01, 0x03); +REGISTER_HID_DETECTOR_IPU("Razer Kraken Kitty Black Edition V2", DetectRazerKrakenControllers, RAZER_VID, RAZER_KRAKEN_KITTY_BLACK_EDITION_V2_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken Ultimate", DetectRazerKrakenControllers, RAZER_VID, RAZER_KRAKEN_ULTIMATE_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken V3 HyperSense", DetectRazerKrakenV3Controllers,RAZER_VID, RAZER_KRAKEN_V3_HYPERSENSE_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken V3 X", DetectRazerKrakenV3Controllers,RAZER_VID, RAZER_KRAKEN_V3_X_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken V3", DetectRazerKrakenV3Controllers,RAZER_VID, RAZER_KRAKEN_V3_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken Kitty V2 Pro", DetectRazerKrakenV3Controllers,RAZER_VID, RAZER_KRAKEN_KITTY_V2_PRO_PID, 0x03, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken V4 (Wired)", DetectRazerKrakenV4Controllers,RAZER_VID, RAZER_KRAKEN_V4_WIRED_PID, 0x05, 0xFF14, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken V4 (Wireless)", DetectRazerKrakenV4Controllers,RAZER_VID, RAZER_KRAKEN_V4_WIRELESS_PID, 0x05, 0xFF14, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken Kitty V3 Pro (Wired)", DetectRazerKrakenV4Controllers,RAZER_VID, RAZER_KRAKEN_KITTY_V3_PRO_WIRED_PID, 0x05, 0xFF14, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Kraken Kitty V3 Pro (Wireless)", DetectRazerKrakenV4Controllers,RAZER_VID, RAZER_KRAKEN_KITTY_V3_PRO_WIRELESS_PID, 0x05, 0xFF14, 0x01); +REGISTER_HID_DETECTOR_I( "Razer Tiamat 7.1 V2", DetectRazerControllers, RAZER_VID, RAZER_TIAMAT_71_V2_PID, 0x00 ); + +/*-----------------------------------------------------------------------------------------------------*\ +| Mousemats | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Razer Firefly", DetectRazerControllers, RAZER_VID, RAZER_FIREFLY_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Firefly V2", DetectRazerControllers, RAZER_VID, RAZER_FIREFLY_V2_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Firefly V2 Pro", DetectRazerControllers, RAZER_VID, RAZER_FIREFLY_V2_PRO_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Firefly Hyperflux", DetectRazerControllers, RAZER_VID, RAZER_FIREFLY_HYPERFLUX_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Goliathus", DetectRazerControllers, RAZER_VID, RAZER_GOLIATHUS_CHROMA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Goliathus Chroma 3XL", DetectRazerControllers, RAZER_VID, RAZER_GOLIATHUS_CHROMA_3XL_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Goliathus Extended", DetectRazerControllers, RAZER_VID, RAZER_GOLIATHUS_CHROMA_EXTENDED_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Strider Chroma", DetectRazerControllers, RAZER_VID, RAZER_STRIDER_CHROMA_PID, 0x00, 0x01, 0x02); + +/*-----------------------------------------------------------------------------------------------------*\ +| Accessories | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Razer Base Station Chroma", DetectRazerControllers, RAZER_VID, RAZER_BASE_STATION_CHROMA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Base Station V2 Chroma", DetectRazerControllers, RAZER_VID, RAZER_BASE_STATION_V2_CHROMA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Charging Pad Chroma", DetectRazerControllers, RAZER_VID, RAZER_CHARGING_PAD_CHROMA_PID, 0x00, 0x0C, 0x01); +REGISTER_HID_DETECTOR_I("Razer Chroma Addressable RGB Controller", DetectRazerARGBControllers, RAZER_VID, RAZER_CHROMA_ADDRESSABLE_RGB_CONTROLLER_PID, 0x00 ); +REGISTER_HID_DETECTOR_IPU("Razer Chroma HDK", DetectRazerControllers, RAZER_VID, RAZER_CHROMA_HDK_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Chroma Mug Holder", DetectRazerControllers, RAZER_VID, RAZER_CHROMA_MUG_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Chroma PC Case Lighting Kit", DetectRazerControllers, RAZER_VID, RAZER_CHROMA_PC_CASE_LIGHTING_KIT_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Core", DetectRazerControllers, RAZER_VID, RAZER_CORE_PID, 0x00, 0xFF00, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Core X", DetectRazerControllers, RAZER_VID, RAZER_CORE_X_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Laptop Stand Chroma", DetectRazerControllers, RAZER_VID, RAZER_LAPTOP_STAND_CHROMA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Laptop Stand Chroma V2", DetectRazerControllers, RAZER_VID, RAZER_LAPTOP_STAND_CHROMA_V2_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Leviathan V2", DetectRazerControllers, RAZER_VID, RAZER_LEVIATHAN_V2_PID, 0x02, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Leviathan V2 X", DetectRazerControllers, RAZER_VID, RAZER_LEVIATHAN_V2X_PID, 0x00, 0x0C, 0x01); +REGISTER_HID_DETECTOR_IPU("Razer Mouse Bungee V3 Chroma", DetectRazerControllers, RAZER_VID, RAZER_MOUSE_BUNGEE_V3_CHROMA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mouse Dock Chroma", DetectRazerControllers, RAZER_VID, RAZER_MOUSE_DOCK_CHROMA_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Razer Mouse Dock Pro", DetectRazerControllers, RAZER_VID, RAZER_MOUSE_DOCK_PRO_PID, 0x00, 0x01, 0x02); +REGISTER_HID_DETECTOR_IPU("Lian Li O11 Dynamic - Razer Edition", DetectRazerControllers, RAZER_VID, RAZER_O11_DYNAMIC_PID, 0x02, 0x01, 0x02); +REGISTER_HID_DETECTOR_PU("Razer Seiren Emote", DetectRazerControllers, RAZER_VID, RAZER_SEIREN_EMOTE_PID, 0x0C, 0x01 ); +REGISTER_HID_DETECTOR_PU("Razer Thunderbolt 4 Dock Chroma", DetectRazerControllers, RAZER_VID, RAZER_THUNDERBOLT_4_DOCK_CHROMA_PID, 0x0C, 0x01 ); +REGISTER_HID_DETECTOR_PU("Razer Thunderbolt 5 Dock Chroma", DetectRazerControllers, RAZER_VID, RAZER_THUNDERBOLT_5_DOCK_CHROMA_PID, 0x0C, 0x01 ); +REGISTER_HID_DETECTOR_IPU("Razer Hanbo Chroma", DetectRazerHanboControllers, RAZER_VID, RAZER_HANBO_CHROMA_PID, 0x00, 0xFF00, 0x01); + +/*-----------------------------------------------------------------------------------------------------*\ +| Nommo devices seem to have an issue where interface 1 doesn't show on Linux or MacOS. Due to the way | +| hidapi works on these operating systems, it is acceptable to use interface 0 instead. Interface 1 | +| must be used on Windows. | +\*-----------------------------------------------------------------------------------------------------*/ +#ifdef _WIN32 +REGISTER_HID_DETECTOR_IPU("Razer Nommo Chroma", DetectRazerControllers, RAZER_VID, RAZER_NOMMO_CHROMA_PID, 0x01, 0x01, 0x03); +REGISTER_HID_DETECTOR_IPU("Razer Nommo Pro", DetectRazerControllers, RAZER_VID, RAZER_NOMMO_PRO_PID, 0x01, 0x01, 0x03); +#else +REGISTER_HID_DETECTOR_IPU("Razer Nommo Chroma", DetectRazerControllers, RAZER_VID, RAZER_NOMMO_CHROMA_PID, 0x00, 0x01, 0x00); +REGISTER_HID_DETECTOR_IPU("Razer Nommo Pro", DetectRazerControllers, RAZER_VID, RAZER_NOMMO_PRO_PID, 0x00, 0x01, 0x00); +#endif + +/*-----------------------------------------------------------------------------------------------------*\ +| Need to clean up some stuff before we scan/rescan | +\*-----------------------------------------------------------------------------------------------------*/ +REGISTER_PRE_DETECTION_HOOK(ResetRazerARGBControllersPaths); diff --git a/Controllers/RazerController/RazerDeviceGuard.cpp b/Controllers/RazerController/RazerDeviceGuard.cpp new file mode 100644 index 0000000..63fb713 --- /dev/null +++ b/Controllers/RazerController/RazerDeviceGuard.cpp @@ -0,0 +1,64 @@ +/*---------------------------------------------------------*\ +| RazerDeviceGuard.cpp | +| | +| DeviceGuard for Razer devices | +| | +| Aytac Kayadelen 18 May 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RazerDeviceGuard.h" + +RazerDeviceGuard::RazerDeviceGuard() : DeviceGuard() +{ +#ifdef _WIN32 + mutex_handle = CreateWindowsMutex(); +#endif +} + +void RazerDeviceGuard::Acquire() +{ +#ifdef _WIN32 + while(true) + { + DWORD result = WaitForSingleObject(mutex_handle, INFINITE); + + if(result == WAIT_OBJECT_0) + { + break; + } + + if(result == WAIT_ABANDONED) + { + ReleaseMutex(mutex_handle); + } + } +#endif +} + +void RazerDeviceGuard::Release() +{ +#ifdef _WIN32 + ReleaseMutex(mutex_handle); +#endif +} + +#ifdef _WIN32 + +HANDLE RazerDeviceGuard::CreateWindowsMutex() +{ + SECURITY_DESCRIPTOR sd; + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + return CreateMutex(&sa, FALSE, "Global\\RazerLinkReadWriteGuardMutex"); +} + +#endif diff --git a/Controllers/RazerController/RazerDeviceGuard.h b/Controllers/RazerController/RazerDeviceGuard.h new file mode 100644 index 0000000..5842223 --- /dev/null +++ b/Controllers/RazerController/RazerDeviceGuard.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RazerDeviceGuard.h | +| | +| DeviceGuard for Razer devices | +| | +| Aytac Kayadelen 18 May 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "DeviceGuard.h" + +#ifdef _WIN32 +#include +#endif + +class RazerDeviceGuard : public DeviceGuard +{ +public: + RazerDeviceGuard(); + + void Acquire() override; + void Release() override; + +private: +#ifdef _WIN32 + HANDLE mutex_handle; + + HANDLE CreateWindowsMutex(); +#endif +}; diff --git a/Controllers/RazerController/RazerDevices.cpp b/Controllers/RazerController/RazerDevices.cpp new file mode 100644 index 0000000..75b7235 --- /dev/null +++ b/Controllers/RazerController/RazerDevices.cpp @@ -0,0 +1,9622 @@ +/*---------------------------------------------------------*\ +| RazerDevices.cpp | +| | +| Device list for Razer devices | +| | +| Adam Honse (CalcProgrammer1) 04 Sep 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RazerDevices.h" + +/*-------------------------------------------------------------------------*\ +| KEYMAPS | +\*-------------------------------------------------------------------------*/ +keyboard_keymap_overlay_values razer_blackwidow_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + { + KEYBOARD_LAYOUT_ANSI_QWERTY, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 4, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Move 'Z' 1 right (Account for ISO key) + } + }, + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right + { 0, 0, 2, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'F1' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 1, 14, 0, KEY_EN_EQUALS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts most of row) + { 0, 3, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Enter 1 right + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts most of row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right + { 0, 5, 10, 0, KEY_EN_RIGHT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert (Another) RGT_ALT (Shifts remainder of row) + { 0, 5, 11, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap RGT_ALT for 'Logo' + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_2019_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right (Shifts row) + { 0, 5, 6, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Move Space 1 left (Shifts row) + { 0, 5, 11, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert 'Logo' key + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_x_chroma_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right (Shifts row) + { 0, 0, 20, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert 'Logo' key + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_chroma_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, "Key: M1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Inset Macro key 1 (Shifts row) + { 0, 2, 0, 0, "Key: M2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Inset Macro key 2 (Shifts row) + { 0, 3, 0, 0, "Key: M3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Inset Macro key 3 (Shifts row) + { 0, 4, 0, 0, "Key: M4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Inset Macro key 4 (Shifts row) + { 0, 5, 0, 0, "Key: M5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Inset Macro key 5 (Shifts row) + { 0, 0, 20, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert 'Logo' key + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_chroma_v2_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, "Key: M1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Inset Macro key 1 (Shifts row) + { 0, 2, 0, 0, "Key: M2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Inset Macro key 2 (Shifts row) + { 0, 3, 0, 0, "Key: M3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Inset Macro key 3 (Shifts row) + { 0, 4, 0, 0, "Key: M4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Inset Macro key 4 (Shifts row) + { 0, 5, 0, 0, "Key: M5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Inset Macro key 5 (Shifts row) + { 0, 0, 20, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert 'Logo' key + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_chroma_te_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right (Shifts row) + { 0, 0, 20, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert 'Logo' key + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_elite_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 0, 18, 0, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 0, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 0, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 21, 0, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right (Shifts row) + { 0, 5, 6, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Move Space 1 left (Shifts row) + { 0, 5, 11, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert 'Logo' key + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_v3_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right (Shifts row) + { 0, 5, 6, 0, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Spacebar @ 5,6 + { 0, 5, 7, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Spacebar @ 5,7 + { 0, 5, 10, 0, KEY_EN_RIGHT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert (another) Right Alt + { 0, 5, 11, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap 'Logo' instead of Right ALt + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_v3_mini_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_SIXTY, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_ESCAPE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap Escape in for Backtick + { 0, 0, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backspace 1 right (Shifts row) + { 0, 0, 15, 0, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Delete Key + { 0, 1, 15, 0, KEY_EN_PAGE_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Page Up Key + { 0, 2, 15, 0, KEY_EN_PAGE_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Page Down Key + { 0, 3, 14, 0, KEY_EN_INSERT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Insert Key + { 0, 3, 15, 0, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Up Arrow Key + { 0, 4, 7, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert 'Logo' key + { 0, 4, 12, 0, KEY_EN_RIGHT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap Right Control in for Right Menu + { 0, 4, 13, 0, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Left Arrow + { 0, 4, 14, 0, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Down Arrow + { 0, 4, 15, 0, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Right Arrow + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_v3_pro_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 18, 0, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 0, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 0, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 21, 0, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 14, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert 'Logo' + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_v3_tkl_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right (Shifts row) + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_v4_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Esc key 1 right (Shifts row) + { 0, 0, 1, 0, "Key: M6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M6 macro key (Shifts row) + { 0, 0, 19, 0, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 0, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 21, 0, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 22, 0, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 1, 1, 0, "Key: M5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M5 macro key (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 2, 1, 0, "Key: M4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M4 macro key (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 3, 1, 0, "Key: M3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M3 macro key (Shifts row) + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 4, 1, 0, "Key: M2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M2 macro key (Shifts row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right (Shifts row) + { 0, 5, 1, 0, "Key: M1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M1 macro key (Shifts row) + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_v4_pro_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 0, 1, 0, "Key: Dial", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Command Dial (Shifts row) + { 0, 0, 19, 0, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 0, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 21, 0, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 22, 0, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 1, 1, 0, "Key: M5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M5 macro key (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 2, 1, 0, "Key: M4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M4 macro key (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 3, 1, 0, "Key: M3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M3 macro key (Shifts row) + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 4, 1, 0, "Key: M2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M2 macro key (Shifts row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right (Shifts row) + { 0, 5, 1, 0, "Key: M1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M1 macro key (Shifts row) + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_v4_pro_75_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_SEVENTY_FIVE, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, "Left Underglow 0", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 2, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Delete gap between ESC and F1 + { 0, 0, 14, 0, "Left Underglow 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, + { 0, 0, 15, 0, "Left Underglow 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, + { 0, 0, 16, 0, "Left Underglow 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, + { 0, 0, 17, 0, "Right Underglow 0", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + + { 0, 1, 0, 0, "Left Underglow 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Add gap between = and Backspace + { 0, 1, 16, 0, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 17, 0, "Right Underglow 2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 0, "Left Underglow 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 16, 0, KEY_EN_PAGE_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 17, 0, "Right Underglow 3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 0, "Left Underglow 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 16, 0, KEY_EN_PAGE_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 17, 0, "Right Underglow 4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 0, "Left Underglow 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 15, 0, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 16, 0, KEY_EN_INSERT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 17, 0, "Right Underglow 6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 0, "Left Underglow 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 5, 0, "Right Underglow 1", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, + { 0, 5, 6, 0, "Right Underglow 5", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, + { 0, 5, 8, 0, "Right Underglow 8", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, + { 0, 5, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Menu key between Fn and RCtrl + { 0, 5, 14, 0, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 15, 0, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 16, 0, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 17, 0, "Right Underglow 7", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_v4_x_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, "Key: M6", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M6 macro key (Shifts row) + { 0, 1, 0, 0, "Key: M5", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M5 macro key (Shifts row) + { 0, 2, 0, 0, "Key: M4", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M4 macro key (Shifts row) + { 0, 3, 0, 0, "Key: M3", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M3 macro key (Shifts row) + { 0, 4, 0, 0, "Key: M2", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M2 macro key (Shifts row) + { 0, 5, 0, 0, "Key: M1", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert M1 macro key (Shifts row) + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_v4_tkl_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 10, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move F9 1 right (Shifts row) + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Insert 1 right (Shifts row) + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Del 1 right (Shifts row) + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Up Arrow 1 right (Shifts row) + { 0, 5, 5, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Right Alt 1 right (Shifts row) + + } +}; + +keyboard_keymap_overlay_values razer_blackwidow_x_chroma_te_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right (Shifts row) + { 0, 0, 20, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert 'Logo' key + } +}; + +keyboard_keymap_overlay_values razer_blade_pro_2017_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_SEVENTY_FIVE, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Escape 1 right (Shifts row) + { 0, 0, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Escape @ 0,1 + { 0, 0, 2, 0, KEY_EN_ESCAPE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Escape @ 0,2 + { 0, 0, 15, 0, KEY_EN_INSERT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert 'Insert' key + { 0, 0, 17, 0, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Delete key + { 0, 0, 19, 0, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Previous track key + { 0, 0, 20, 0, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Next track key + { 0, 0, 21, 0, "Key: Media Volume", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Volume key + { 0, 0, 23, 0, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Play / Pause key + { 0, 0, 24, 0, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Volume Mute key + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 1, 19, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 1, 20, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 1, 21, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 1, 22, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 1, 23, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 1, 24, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 2, 3, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Q' 1 right (Shifts row) + { 0, 2, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Back slash 1 right -> 2,17 + { 0, 2, 19, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 2, 24, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 3, 2, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'A' 1 right (Shifts row) + { 0, 3, 3, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'A' 1 right (Shifts row) + { 0, 3, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move ANSI Enter 1 right -> 3,18 + { 0, 3, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move ANSI Enter 1 right -> 3,18 + { 0, 3, 19, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 3, 24, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 4, 2, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Z' 1 right (Shifts row) + { 0, 4, 3, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Z' 1 right (Shifts row) + { 0, 4, 14, 0, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Arrow Up and shift row 1 right + { 0, 4, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Right Shift 1 right -> 4,17 + { 0, 4, 17, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Right Shift 1 right -> 4,18 + { 0, 4, 19, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 4, 24, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Trackpad + { 0, 5, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Left Windows @ 5,1 + { 0, 5, 2, 0, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Left Function @ 5,2 + { 0, 5, 3, 0, KEY_EN_LEFT_WINDOWS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Left Windows @ 5,3 + { 0, 5, 5, 0, KEY_EN_LEFT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Left Alt @ 5,5 + { 0, 5, 6, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Spacebar @ 5,6 + { 0, 5, 7, 0, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Spacebar @ 5,7 + { 0, 5, 11, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Right Function @ 5,11 + { 0, 5, 12, 0, KEY_EN_RIGHT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap Right Control for Right Menu @ 5,12 + { 0, 5, 13, 0, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap Arrow Left for Right Control @ 5,13 + { 0, 5, 14, 0, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Arrow Down @ 5,14 + { 0, 5, 15, 0, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Arrow Right @ 5,15 + { 0, 5, 16, 0, KEY_EN_RIGHT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Right Function @ 5,16 + { 0, 5, 19, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Trackpad + { 0, 5, 20, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Trackpad + { 0, 5, 21, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Trackpad + { 0, 5, 22, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Trackpad + { 0, 5, 23, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Trackpad + { 0, 5, 24, 0, "Trackpad", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Trackpad + } +}; + +keyboard_keymap_overlay_values razer_blade_15_2021_advanced_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + { /* ANSI Value set not used */ }, + { + { + KEYBOARD_LAYOUT_ISO_QWERTY, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 2, 13, 0, KEY_EN_ISO_ENTER, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove ANSI_BACKSLASH + { 0, 3, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove ANSI_ENTER + } + }, + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 1, 0, KEY_EN_ESCAPE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backspace 1 right + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 5, 0, KEY_EN_LEFT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 9, 0, KEY_EN_RIGHT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove ESC + { 0, 0, 14, 0, KEY_EN_INSERT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap PRTSCN for INS + { 0, 0, 15, 0, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap SCRLCK for DEL + { 0, 0, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove PSE_BRK + { 0, 1, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove INSERT + { 0, 1, 17, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove HOME + { 0, 1, 18, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove PGUP + { 0, 2, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove DEL + { 0, 2, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove END + { 0, 2, 17, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove PGDN + { 0, 4, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove RGT_SHFT + { 0, 4, 15, 0, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWUP for RGT_SHFT + { 0, 5, 2, 0, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap LFT_WIN for LFT_FNC + { 0, 5, 3, 0, KEY_EN_LEFT_WINDOWS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap LFT_ALT for LFT_WIN + { 0, 5, 6, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove SPACE + { 0, 5, 10, 0, KEY_EN_RIGHT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap RGT_ALT for RGT_FNC + { 0, 5, 11, 0, KEY_EN_RIGHT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap RGT_FNC for RGT_CTL + { 0, 5, 12, 0, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWLFT for RGT_MNU + { 0, 5, 13, 0, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWUP for RGT_CTL + { 0, 5, 14, 0, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWRGT for ARWLFT + { 0, 5, 15, 0, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWDWN for ARWDWN + { 0, 5, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove ARWRGT + } +}; + +keyboard_keymap_overlay_values razer_blade_15_2022_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + { /* ANSI Value set not used */ }, + { + { + KEYBOARD_LAYOUT_ANSI_QWERTY, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 4, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Move 'Z' 1 right (Account for ISO key) + } + }, + { + KEYBOARD_LAYOUT_ISO_QWERTY, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 2, 13, 0, KEY_EN_ISO_ENTER, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove ANSI_BACKSLASH + { 0, 3, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove ANSI_ENTER + } + }, + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Escape 1 right + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backspace 1 right + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Back slash 1 right + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move ANSI Enter 1 right + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move ANSI Enter 1 right + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 5, 0, KEY_EN_LEFT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 6, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Space 1 right + { 0, 5, 9, 0, KEY_EN_RIGHT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 14, 0, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap PRTSCN for DEL + { 0, 0, 15, 0, KEY_EN_POWER, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap SCRLCK for POWER + { 0, 0, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove PSE_BRK + { 0, 1, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove INSERT + { 0, 1, 17, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove HOME + { 0, 1, 18, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove PGUP + { 0, 2, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove DEL + { 0, 2, 17, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove END + { 0, 2, 18, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove PGDN + { 0, 4, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove RGT_SHFT + { 0, 4, 15, 0, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWUP for RGT_SHFT + { 0, 5, 2, 0, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap LFT_WIN for LFT_FNC + { 0, 5, 3, 0, KEY_EN_LEFT_WINDOWS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap LFT_ALT for LFT_WIN + { 0, 5, 10, 0, KEY_EN_RIGHT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap RGT_ALT for RGT_FNC + { 0, 5, 11, 0, KEY_EN_RIGHT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap RGT_FNC for RGT_CTL + { 0, 5, 12, 0, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWLFT for RGT_MNU + { 0, 5, 13, 0, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWUP for RGT_CTL + { 0, 5, 14, 0, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWRGT for ARWLFT + { 0, 5, 15, 0, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWDWN for ARWDWN + { 0, 5, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove ARWRGT + } +}; + +keyboard_keymap_overlay_values razer_blade_17_pro_2021_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_SEVENTY_FIVE, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Shift all rows right by one */ + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Rows 1-4 have an empty spot in */ + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* 14th column */ + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 0, 2, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove empty spot between Esc and F1 */ + { 0, 0, 14, 0, KEY_EN_INSERT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, /* Swap in Delete after F12 */ + { 0, 0, 15, 0, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, /* Swap in Power after Delete */ + { 0, 5, 2, 0, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Insert left Fn between Ctrl and Win */ + { 0, 5, 4, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add empty spot between Win and Alt */ + { 0, 5, 9, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove Space */ + { 0, 5, 9, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove unused */ + { 0, 5, 9, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove unused */ + { 0, 5, 9, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove unused */ + { 0, 5, 11, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove Menu */ + { 0, 5, 12, 0, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Left Arrow at the end of row 5 */ + { 0, 5, 13, 0, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Up Arrow at the end of row 5 */ + { 0, 5, 14, 0, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Right Arrow at the end of row 5 */ + { 0, 5, 15, 0, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Down Arrow at the end of row 5 */ + } +}; + +keyboard_keymap_overlay_values razer_blade_stealth_2016_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_SEVENTY_FIVE, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Shift rows right by one */ + { 0, 0, 2, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove empty between Esc and F1 */ + { 0, 0, 14, 0, KEY_EN_INSERT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Insert at end of row */ + { 0, 0, 15, 0, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Delete at end of row */ + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Shift rows right by one */ + { 0, 1, 15, 0, KEY_EN_BACKSPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add second Backspace at end of row */ + { 0, 2, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add empty between tab and Q */ + { 0, 2, 15, 0, KEY_EN_ANSI_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add second Backslash at end of row */ + { 0, 3, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add empty between caps and A */ + { 0, 3, 15, 0, KEY_EN_ANSI_ENTER, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add second Enter at end of row */ + { 0, 4, 12, 0, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, /* Insert 1st Right Shift */ + { 0, 4, 13, 0, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, /* Insert 2nd Right Shift */ + { 0, 4, 14, 0, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Insert 3rd Right Shift */ + { 0, 5, 1, 0, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Insert left Fn between Ctrl and Win */ + { 0, 5, 5, 0, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, /* Insert 1st Space */ + { 0, 5, 6, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove empty between Space 1 and 2 */ + { 0, 5, 8, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove empty between Space 2 and 3 */ + { 0, 5, 8, 0, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, /* Insert 3rd Space */ + { 0, 5, 11, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove Menu */ + { 0, 5, 12, 0, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Left Arrow at the end of row 5 */ + { 0, 5, 13, 0, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Up Arrow at the end of row 5 */ + { 0, 5, 14, 0, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Right Arrow at the end of row 5 */ + { 0, 5, 15, 0, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Down Arrow at the end of row 5 */ + } +}; + +keyboard_keymap_overlay_values razer_cynosa_chroma_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 1 + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 2 + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 3 + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 4 + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 5 + { 0, 0, 20, 0, "Logo", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert 'Logo' key + } +}; + +keyboard_keymap_overlay_values razer_cynosa_chroma_v2_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 1 + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 2 + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 3 + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 4 + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 5 + { 0, 0, 18, 0, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Previous track key + { 0, 0, 19, 0, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Play / Pause key + { 0, 0, 20, 0, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Next track key + { 0, 0, 21, 0, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Volume Mute key + { 0, 4, 15, 0, KEY_EN_MEDIA_VOLUME_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Volume Up + { 0, 4, 17, 0, KEY_EN_MEDIA_VOLUME_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Volume Down + } +}; + +keyboard_keymap_overlay_values razer_deathstalker_v2_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + { + KEYBOARD_LAYOUT_ANSI_QWERTY, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 4, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Move 'Z' 1 right (Account for ISO key) + } + }, + { + KEYBOARD_LAYOUT_ISO_QWERTY, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 2, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove ANSI_BACKSLASH + { 0, 4, 1, 0, KEY_EN_ISO_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Add ISO_BACK_SLASH + } + }, + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 1, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backspace 1 right + } +}; + +keyboard_keymap_overlay_values razer_deathstalker_v2_pro_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + { + KEYBOARD_LAYOUT_ANSI_QWERTY, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 4, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Move 'Z' 1 right (Account for ISO key) + } + }, + { + KEYBOARD_LAYOUT_ISO_QWERTY, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 2, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove ANSI_BACKSLASH + { 0, 4, 1, 0, KEY_EN_ISO_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Add ISO_BACK_SLASH + } + }, + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + } +}; + +keyboard_keymap_overlay_values razer_deathstalker_v2_pro_tkl_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + { /* ANSI Value set not used */ }, + { + { + KEYBOARD_LAYOUT_ANSI_QWERTY, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 4, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Move 'Z' 1 right (Account for ISO key) + } + }, + { + KEYBOARD_LAYOUT_ISO_QWERTY, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 2, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove ANSI_BACKSLASH + { 0, 4, 1, 0, KEY_EN_ISO_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Add ISO_BACK_SLASH + } + }, + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove PRINT_SCREEN + { 0, 0, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove SCROLL_LOCK + { 0, 0, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove PAUSE_BREAK + } +}; + +keyboard_keymap_overlay_values razer_full_size_shifted_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 1 + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 2 + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 3 + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 4 + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shifts row 5 + } +}; + +keyboard_keymap_overlay_values razer_huntsman_common_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 0, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 0, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 0, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 21, 0, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +keyboard_keymap_overlay_values razer_huntsman_mini_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_SIXTY, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shift row 0 + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shift row 1 + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shift row 2 + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shift row 3 + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Shift row 4 + } +}; + +keyboard_keymap_overlay_values razer_huntsman_te_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move 'Esc' 1 right (Shifts row) + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_CTRL 1 right (Shifts row) + } +}; + +keyboard_keymap_overlay_values razer_huntsman_v2_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 17, 0, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 0, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 0, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 0, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +keyboard_keymap_overlay_values razer_huntsman_v2_tkl_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + } +}; + +keyboard_keymap_overlay_values razer_huntsman_v3_pro_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 0, "Media group", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 0, "Media: Volume Dial", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 17, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + } +}; + +keyboard_keymap_overlay_values razer_huntsman_v3_pro_tkl_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_TKL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 15, 0, "Xbox Game Bar", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 0, 16, 0, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 0, 17, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 0, "Media: Volume Dial", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +keyboard_keymap_overlay_values razer_laptop_common_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_SEVENTY_FIVE, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Shift all rows right by one */ + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Rows 1-4 have an empty spot in */ + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* 14th column */ + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* */ + { 0, 0, 2, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove empty spot between Esc and F1 */ + { 0, 0, 14, 0, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, /* Swap in Delete after F12 */ + { 0, 0, 15, 0, KEY_EN_POWER, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, /* Swap in Power after Delete */ + { 0, 5, 2, 0, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Insert left Fn between Ctrl and Win */ + { 0, 5, 4, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add empty spot between Win and Alt */ + { 0, 5, 9, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove Space */ + { 0, 5, 9, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove unused */ + { 0, 5, 9, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove unused */ + { 0, 5, 9, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove unused */ + { 0, 5, 10, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, /* Remove Right Fn */ + { 0, 5, 10, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, /* Remove Menu */ + { 0, 5, 12, 0, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Left Arrow at the end of row 5 */ + { 0, 5, 13, 0, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Up Arrow at the end of row 5 */ + { 0, 5, 14, 0, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Right Arrow at the end of row 5 */ + { 0, 5, 15, 0, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, /* Add Down Arrow at the end of row 5 */ + } +}; + +keyboard_keymap_overlay_values razer_laptop_with_spacebar_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_SEVENTY_FIVE, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Escape @ 0,0 + { 0, 0, 1, 0, KEY_EN_ESCAPE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Escape @ 0,1 + { 0, 0, 15, 0, KEY_EN_POWER, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Power key + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backtick 1 right (Shifts row) + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Backspace 1 right + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Tab 1 right (Shifts row) + { 0, 2, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Back slash 1 right + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Caps 1 right (Shifts row) + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move ANSI Enter 1 right + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move LFT_SHFT 1 right (Shifts row) + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Move Right Shift 1 right + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Left Control @ 5,0 + { 0, 5, 1, 0, KEY_EN_LEFT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Left Control @ 5,1 + { 0, 5, 2, 0, KEY_EN_LEFT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Left Function @ 5,2 + { 0, 5, 3, 0, KEY_EN_LEFT_WINDOWS, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Left Windows @ 5,3 + { 0, 5, 5, 0, KEY_EN_LEFT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Left Alt @ 5,5 + { 0, 5, 6, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Spacebar @ 5,6 + { 0, 5, 7, 0, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Spacebar @ 5,7 + { 0, 5, 9, 0, KEY_EN_RIGHT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert Right Alt @ 5,9 + { 0, 5, 10, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Right Alt @ 5,10 + { 0, 5, 11, 0, KEY_EN_RIGHT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap RGT_FNC for RGT_CTL + { 0, 5, 12, 0, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWLFT for RGT_MNU + { 0, 5, 13, 0, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Swap ARWUP for RGT_CTL + { 0, 5, 14, 0, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert ARWRGT + { 0, 5, 15, 0, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert ARWDWN + } +}; + +keyboard_keymap_overlay_values razer_ornata_chroma_v2_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_FULL, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 18, 0, KEY_EN_MEDIA_PREVIOUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 19, 0, KEY_EN_MEDIA_PLAY_PAUSE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 20, 0, KEY_EN_MEDIA_NEXT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 21, 0, KEY_EN_MEDIA_MUTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +keyboard_keymap_overlay_values razer_tartarus_v2_layout +{ + KEYBOARD_SIZE::KEYBOARD_SIZE_EMPTY, + { + { /* ANSI Value set not used */ }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, "Key: 01", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 1, 0, "Key: 02", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 2, 0, "Key: 03", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 3, 0, "Key: 04", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 4, 0, "Key: 05", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 0, 0, "Key: 06", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 1, 0, "Key: 07", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 2, 0, "Key: 08", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 3, 0, "Key: 09", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 4, 0, "Key: 10", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 0, "Key: 11", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 1, 0, "Key: 12", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 2, 0, "Key: 13", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 3, 0, "Key: 14", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 4, 0, "Key: 15", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 0, "Key: 16", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 1, 0, "Key: 17", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 2, 0, "Key: 18", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 3, 0, "Key: 19", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 4, 0, "Key: Scroll Wheel", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 5, 0, "Key: 20", KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + } +}; + +/*-------------------------------------------------------------------------*\ +| KEYBOARDS | +\*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------*\ +| Razer Blackwidow 2019 1532:0241 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_2019_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_2019_device = +{ + "Razer BlackWidow 2019", + RAZER_BLACKWIDOW_2019_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 22, + { + &blackwidow_2019_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_2019_layout +}; + +/*-------------------------------------------------------------*\ +| Razer BlackWidow Chroma | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_chroma_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_chroma_device = +{ + "Razer BlackWidow Chroma", + RAZER_BLACKWIDOW_CHROMA_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 22, + { + &blackwidow_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_chroma_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow Chroma Overwatch 1532:0211 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_chroma_overwatch_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_chroma_overwatch_device = +{ + "Razer Blackwidow Chroma Overwatch", + RAZER_BLACKWIDOW_OVERWATCH_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 22, + { + &blackwidow_chroma_overwatch_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer BlackWidow Chroma Tournament Edition | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_chroma_te_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_chroma_te_device = +{ + "Razer BlackWidow Chroma Tournament Edition", + RAZER_BLACKWIDOW_CHROMA_TE_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 22, + { + &blackwidow_chroma_te_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_chroma_te_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow Chroma V2 1532:0221 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_chroma_v2_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_chroma_v2_device = +{ + "Razer BlackWidow Chroma V2", + RAZER_BLACKWIDOW_CHROMA_V2_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 22, + { + &blackwidow_chroma_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_chroma_v2_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow Elite 1532:0228 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_elite_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_elite_device = +{ + "Razer BlackWidow Elite", + RAZER_BLACKWIDOW_ELITE_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 22, + { + &blackwidow_elite_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_elite_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V3 1532:024E | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v3_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_v3_device = +{ + "Razer Blackwidow V3", + RAZER_BLACKWIDOW_V3_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 22, + { + &blackwidow_v3_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v3_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V3 Pro (Wired) 1532:025A | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v3_pro_wired_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_v3_pro_wired_device = +{ + "Razer BlackWidow V3 Pro (Wired)", + RAZER_BLACKWIDOW_V3_PRO_WIRED_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 22, + { + &blackwidow_v3_pro_wired_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v3_pro_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V3 Pro (Bluetooth) 1532:025B | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v3_pro_bluetooth_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_v3_pro_bluetooth_device = +{ + "Razer BlackWidow V3 Pro (Bluetooth)", + RAZER_BLACKWIDOW_V3_PRO_BLUETOOTH_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 22, + { + &blackwidow_v3_pro_bluetooth_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v3_pro_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V3 Pro (Wireless) 1532:025C | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v3_pro_wireless_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_v3_pro_wireless_device = +{ + "Razer BlackWidow V3 Pro (Wireless)", + RAZER_BLACKWIDOW_V3_PRO_WIRELESS_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 22, + { + &blackwidow_v3_pro_wireless_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v3_pro_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V3 TKL 1532:0A24 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 18 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v3_tkl_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 18 +}; + +static const razer_device blackwidow_v3_tkl_device = +{ + "Razer BlackWidow V3 TKL", + RAZER_BLACKWIDOW_V3_TKL_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 18, + { + &blackwidow_v3_tkl_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v3_tkl_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V3 Mini (Wired) 1532:0258 | +| | +| Zone "Keyboard" | +| Matrix | +| 5 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v3_mini_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 5, + 16 +}; + +static const razer_device blackwidow_v3_mini_wired_device = +{ + "Razer BlackWidow V3 Mini (Wired)", + RAZER_BLACKWIDOW_V3_MINI_WIRED_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 5, + 16, + { + &blackwidow_v3_mini_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v3_mini_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V3 Mini (Wireless) 1532:0271 | +| | +| Zone "Keyboard" | +| Matrix | +| 5 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_device blackwidow_v3_mini_wireless_device = +{ + "Razer BlackWidow V3 Mini (Wireless)", + RAZER_BLACKWIDOW_V3_MINI_WIRELESS_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x9F, + 5, + 16, + { + &blackwidow_v3_mini_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v3_mini_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V4 1532:0287 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 23 Columns | +| | +| Zone "Underglow Left" | +| Linear | +| 1 Row, 9 Columns | +| | +| Zone "Underglow right" | +| Linear | +| 1 Row, 9 Columns | +| | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v4_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 23 +}; + +static const razer_zone blackwidow_v4_lbl_zone = +{ + "Underglow Left", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_zone blackwidow_v4_lbr_zone = +{ + "Underglow Right", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_device blackwidow_v4_device = +{ + "Razer Blackwidow V4", + RAZER_BLACKWIDOW_V4_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 8, + 23, + { + &blackwidow_v4_zone, + &blackwidow_v4_lbl_zone, + &blackwidow_v4_lbr_zone, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v4_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V4 Pro 1532:028D | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 23 Columns | +| | +| Zone "Underglow Left" | +| Linear | +| 1 Row, 9 Columns | +| | +| Zone "Underglow right" | +| Linear | +| 1 Row, 9 Columns | +| | +| Zone "Void" - In testing these LEDs were not connected | +| Linear | +| 1 Row, 5 Columns | +| | +| Zone "Underglow Wrist Rest" | +| Linear | +| 1 Rows, 20 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v4_pro_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 23 +}; + +static const razer_zone blackwidow_v4_pro_lbl_zone = +{ + "Underglow Left", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_zone blackwidow_v4_pro_lbr_zone = +{ + "Underglow Right", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_zone blackwidow_v4_pro_void_zone = +{ + "Void", + ZONE_TYPE_LINEAR, + 1, + 5 +}; + +static const razer_zone blackwidow_v4_pro_lbwr_zone = +{ + "Underglow Wrist Rest", + ZONE_TYPE_LINEAR, + 1, + 20 +}; + +static const razer_device blackwidow_v4_pro_device = +{ + "Razer Blackwidow V4 Pro", + RAZER_BLACKWIDOW_V4_PRO_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 8, + 23, + { + &blackwidow_v4_pro_zone, + &blackwidow_v4_pro_lbl_zone, + &blackwidow_v4_pro_lbr_zone, + &blackwidow_v4_pro_void_zone, + &blackwidow_v4_pro_lbwr_zone, + NULL + }, + &razer_blackwidow_v4_pro_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V4 Pro 75% (Wired) 1532:02B3 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 18 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v4_pro_75_wired_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 18 +}; + +static const razer_device blackwidow_v4_pro_75_wired_device = +{ + "Razer Blackwidow V4 Pro 75% (Wired)", + RAZER_BLACKWIDOW_V4_PRO_75_WIRED_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 18, + { + &blackwidow_v4_pro_75_wired_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v4_pro_75_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V4 Pro 75% (Wireless) 1532:02B4 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 18 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v4_pro_75_wireless_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 18 +}; + +static const razer_device blackwidow_v4_pro_75_wireless_device = +{ + "Razer Blackwidow V4 Pro 75% (Wireless)", + RAZER_BLACKWIDOW_V4_PRO_75_WIRELESS_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 18, + { + &blackwidow_v4_pro_75_wireless_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v4_pro_75_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V4 75% (Wired) 1532:02A5 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 18 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v4_75_wired_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 18 +}; + +static const razer_device blackwidow_v4_75_wired_device = +{ + "Razer Blackwidow V4 75% (Wired)", + RAZER_BLACKWIDOW_V4_75_WIRED_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 18, + { + &blackwidow_v4_75_wired_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v4_pro_75_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V4 X 1532:0293 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v4_x_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_v4_x_device = +{ + "Razer Blackwidow V4 X", + RAZER_BLACKWIDOW_V4_X_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 22, + { + &blackwidow_v4_x_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v4_x_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V4 TKL (Wired) 1532:02D7 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 18 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v4_tkl_wired_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 18 +}; + +static const razer_device blackwidow_v4_tkl_wired_device = +{ + "Razer Blackwidow V4 TKL (Wired)", + RAZER_BLACKWIDOW_V4_TKL_WIRED_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 18, + { + &blackwidow_v4_tkl_wired_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v4_tkl_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V4 TKL (Wireless) 1532:02D5 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 18 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v4_tkl_wireless_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 18 +}; + +static const razer_device blackwidow_v4_tkl_wireless_device = +{ + "Razer Blackwidow V4 TKL (Wireless)", + RAZER_BLACKWIDOW_V4_TKL_WIRELESS_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x9F, + 6, + 18, + { + &blackwidow_v4_tkl_wireless_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v4_tkl_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V4 Low Profile TKL (Wired) 1532:02D4 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 18 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v4_lowprofile_tkl_wired_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 18 +}; + +static const razer_device blackwidow_v4_lowprofile_tkl_wired_device = +{ + "Razer Blackwidow V4 Low Profile TKL (Wired)", + RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRED_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 18, + { + &blackwidow_v4_tkl_wired_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v4_tkl_layout //same layout as v4 (just low profile) +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow V4 Low Profile TKL (Wireless) 1532:02D2 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 18 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_v4_lowprofile_tkl_wireless_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 18 +}; + +static const razer_device blackwidow_v4_lowprofile_tkl_wireless_device = +{ + "Razer Blackwidow V4 Low Profile TKL (Wireless)", + RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRELESS_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x9F, + 6, + 18, + { + &blackwidow_v4_lowprofile_tkl_wireless_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_v4_tkl_layout //same layout as v4 (just low profile) +}; + +/*-------------------------------------------------------------*\ +| Razer Blackwidow X Chroma 1532:0216 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_x_chroma_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_x_chroma_device = +{ + "Razer BlackWidow X Chroma", + RAZER_BLACKWIDOW_X_CHROMA_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 22, + { + &blackwidow_x_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_x_chroma_layout +}; + +/*-------------------------------------------------------------*\ +| Razer BlackWidow X Chroma Tournament Edition 1532:021A | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blackwidow_x_chroma_te_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device blackwidow_x_chroma_te_device = +{ + "Razer BlackWidow X Chroma Tournament Edition", + RAZER_BLACKWIDOW_X_CHROMA_TE_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 22, + { + &blackwidow_x_chroma_te_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blackwidow_x_chroma_te_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Cynosa Chroma 1532:022A | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone cynosa_chroma_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device cynosa_chroma_device = +{ + "Razer Cynosa Chroma", + RAZER_CYNOSA_CHROMA_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 22, + { + &cynosa_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_cynosa_chroma_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Cynosa v2 1532:025E | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone cynosa_v2_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device cynosa_v2_device = +{ + "Razer Cynosa v2", + RAZER_CYNOSA_V2_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 22, + { + &cynosa_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_cynosa_chroma_v2_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Cynosa Lite 1532:023F | +| | +| Zone "Keyboard" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone cynosa_lite_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device cynosa_lite_device = +{ + "Razer Cynosa Lite", + RAZER_CYNOSA_LITE_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &cynosa_lite_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Ornata Chroma | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone ornata_chroma_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device ornata_chroma_device = +{ + "Razer Ornata Chroma", + RAZER_ORNATA_CHROMA_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 22, + { + &ornata_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_full_size_shifted_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Ornata Chroma V2 1532:025D | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone ornata_chroma_v2_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device ornata_chroma_v2_device = +{ + "Razer Ornata Chroma V2", + RAZER_ORNATA_CHROMA_V2_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 22, + { + &ornata_chroma_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_ornata_chroma_v2_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Ornata V3 | +| | +| Zone "Keyboard" | +| Linear | +| 10 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone ornata_v3_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_LINEAR, + 1, + 10 +}; + +static const razer_device ornata_v3_device = +{ + "Razer Ornata V3", + RAZER_ORNATA_V3_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 10, + { + &ornata_v3_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Ornata V3 Rev2 1532:02A1 | +| | +| Zone "Keyboard" | +| Linear | +| 10 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone ornata_v3_rev2_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_LINEAR, + 1, + 10 +}; + +static const razer_device ornata_v3_rev2_device = +{ + "Razer Ornata V3 rev2", + RAZER_ORNATA_V3_REV2_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 10, + { + &ornata_v3_rev2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Ornata V3 TKL 1532:02A3 | +| | +| Zone "Keyboard" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone ornata_v3_tkl_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_SINGLE, + 1, + 8 +}; + +static const razer_device ornata_v3_tkl_device = +{ + "Razer Ornata V3 TKL", + RAZER_ORNATA_V3_TKL_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 8, + { + &ornata_v3_tkl_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Ornata V3 X | +| | +| Zone "Keyboard" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone ornata_v3_x_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device ornata_v3_x_device = +{ + "Razer Ornata V3 X", + RAZER_ORNATA_V3_X_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 1, + { + &ornata_v3_x_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Ornata V3 X Rev2 1532:02A2 | +| | +| Zone "Keyboard" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone ornata_v3_x_v2_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device ornata_v3_x_v2_device = +{ + "Razer Ornata V3 X Rev2", + RAZER_ORNATA_V3_X_REV2_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 1, + { + &ornata_v3_x_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer DeathStalker Chroma | +| | +| Zone "Keyboard" | +| Linear | +| 12 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone deathstalker_chroma_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_LINEAR, + 1, + 12 +}; + +static const razer_device deathstalker_chroma_device = +{ + "Razer DeathStalker Chroma", + RAZER_DEATHSTALKER_CHROMA_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_LINEAR, + 0x3F, + 1, + 12, + { + &deathstalker_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Deathstalker V2 1532:0295 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone deathstalker_v2_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device deathstalker_v2_device = +{ + "Razer Deathstalker V2", + RAZER_DEATHSTALKER_V2_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 22, + { + &deathstalker_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_deathstalker_v2_layout +}; + +/*-------------------------------------------------------------*\ +| Razer DeathStalker V2 Pro TKL (Wired) 1532:0298 | +| (Wireless) 1532:0296 | +| (Bluetooth) 1532:0297 | +| | +| Zone "Keyboard" | +| Matrix | +| 84 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone deathstalker_v2_pro_tkl_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 17 +}; + +static const razer_device deathstalker_v2_pro_tkl_wired_device = +{ + "Razer DeathStalker V2 Pro TKL (Wired)", + RAZER_DEATHSTALKER_V2_PRO_TKL_WIRED_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 6, + 17, + { + &deathstalker_v2_pro_tkl_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_deathstalker_v2_pro_tkl_layout +}; + +static const razer_device deathstalker_v2_pro_tkl_wireless_device = +{ + "Razer DeathStalker V2 Pro TKL (Wireless)", + RAZER_DEATHSTALKER_V2_PRO_TKL_WIRELESS_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x9F, + 6, + 17, + { + &deathstalker_v2_pro_tkl_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_deathstalker_v2_pro_tkl_layout +}; + +/*-------------------------------------------------------------*\ +| Razer DeathStalker V2 Pro (Wireless) 1532:0290 | +| | +| Zone "Keyboard" | +| Matrix | +| 104 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone deathstalker_v2_pro_wireless_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device deathstalker_v2_pro_wireless_device = +{ + "Razer DeathStalker V2 Pro (Wireless)", + RAZER_DEATHSTALKER_V2_PRO_WIRELESS_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x9F, + 6, + 22, + { + &deathstalker_v2_pro_wireless_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_deathstalker_v2_pro_layout +}; + +/*-------------------------------------------------------------*\ +| Razer DeathStalker V2 Pro (Wired) 1532:0292 | +| | +| Zone "Keyboard" | +| Matrix | +| 104 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone deathstalker_v2_pro_wired_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device deathstalker_v2_pro_wired_device = +{ + "Razer DeathStalker V2 Pro (Wired)", + RAZER_DEATHSTALKER_V2_PRO_WIRED_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x9F, + 6, + 22, + { + &deathstalker_v2_pro_wired_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_deathstalker_v2_pro_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Huntsman 1532:0227 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone huntsman_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device huntsman_device = +{ + "Razer Huntsman", + RAZER_HUNTSMAN_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 22, + { + &huntsman_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_full_size_shifted_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Huntsman Elite | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +| | +| Zone "Underglow" | +| Matrix | +| 3 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone huntsman_elite_keyboard_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_zone huntsman_elite_underglow_zone = +{ + "Underglow", + ZONE_TYPE_MATRIX, + 3, + 22 +}; + +static const razer_device huntsman_elite_device = +{ + "Razer Huntsman Elite", + RAZER_HUNTSMAN_ELITE_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 9, + 22, + { + &huntsman_elite_keyboard_zone, + &huntsman_elite_underglow_zone, + NULL, + NULL, + NULL, + NULL + }, + &razer_huntsman_common_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Huntsman V2 Analog | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +| | +| Zone "Underglow" | +| Matrix | +| 3 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone huntsman_v2_analog_keyboard_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_zone huntsman_v2_analog_underglow_zone = +{ + "Underglow", + ZONE_TYPE_MATRIX, + 3, + 22 +}; + +static const razer_device huntsman_v2_analog_device = +{ + "Razer Huntsman V2 Analog", + RAZER_HUNTSMAN_V2_ANALOG_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 9, + 22, + { + &huntsman_v2_analog_keyboard_zone, + &huntsman_v2_analog_underglow_zone, + NULL, + NULL, + NULL, + NULL + }, + &razer_huntsman_common_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Huntsman Mini 1532:0257 | +| | +| Zone "Keyboard" | +| Matrix | +| 5 Rows, 15 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone huntsman_mini_keyboard_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 5, + 15 +}; + +static const razer_device huntsman_mini_device = +{ + "Razer Huntsman Mini", + RAZER_HUNTSMAN_MINI_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 5, + 15, + { + &huntsman_mini_keyboard_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_huntsman_mini_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Huntsman Mini Analog 1532:0282 | +| | +| Zone "Keyboard" | +| Matrix | +| 5 Rows, 15 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone huntsman_mini_analog_keyboard_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 5, + 15 +}; + +static const razer_device huntsman_mini_analog_device = +{ + "Razer Huntsman Mini Analog", + RAZER_HUNTSMAN_MINI_ANALOG_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 5, + 15, + { + &huntsman_mini_analog_keyboard_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_huntsman_mini_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Huntsman TE 1532:0243 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 18 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone huntsman_te_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 18 +}; + +static const razer_device huntsman_te_device = +{ + "Razer Huntsman Tournament Edition", + RAZER_HUNTSMAN_TE_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 18, + { + &huntsman_te_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_huntsman_te_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Huntsman V2 TKL 1532:026B | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 17 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone huntsman_v2_tkl_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 17 +}; + +static const razer_device huntsman_v2_tkl_device = +{ + "Razer Huntsman V2 TKL", + RAZER_HUNTSMAN_V2_TKL_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 17, + { + &huntsman_v2_tkl_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_huntsman_v2_tkl_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Huntsman V2 1532:026C | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone huntsman_v2_keyboard_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device huntsman_v2_device = +{ + "Razer Huntsman V2", + RAZER_HUNTSMAN_V2_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 22, + { + &huntsman_v2_keyboard_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_huntsman_v2_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Huntsman V3 Pro 1532:02A6 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 22 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone huntsman_v3_pro_keyboard_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 22 +}; + +static const razer_device huntsman_v3_pro_device = +{ + "Razer Huntsman V3 Pro", + RAZER_HUNTSMAN_V3_PRO_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 22, + { + &huntsman_v3_pro_keyboard_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_huntsman_v3_pro_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Huntsman V3 Pro TKL White 1532:02A7 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 19 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone huntsman_v3_pro_tkl_keyboard_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 19 +}; + +static const razer_device huntsman_v3_pro_tkl_device = +{ + "Razer Huntsman V3 Pro TKL White", + RAZER_HUNTSMAN_V3_PRO_TKL_WHITE_PID, + DEVICE_TYPE_KEYBOARD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 6, + 19, + { + &huntsman_v3_pro_tkl_keyboard_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_huntsman_v3_pro_tkl_layout +}; + +/*-------------------------------------------------------------------------*\ +| LAPTOPS | +\*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------*\ +| Razer Blade (2016) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_2016_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_2016_device = +{ + "Razer Blade (2016)", + RAZER_BLADE_2016_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_2016_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade (Late 2016) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_late_2016_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_late_2016_device = +{ + "Razer Blade (Late 2016)", + RAZER_BLADE_LATE_2016_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_late_2016_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2018 Advanced) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2018_advanced_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_15_2018_advanced_device = +{ + "Razer Blade 15 (2018 Advanced)", + RAZER_BLADE_2018_ADVANCED_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_15_2018_advanced_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2018 Base) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2018_base_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_15_2018_base_device = +{ + "Razer Blade 15 (2018 Base)", + RAZER_BLADE_2018_BASE_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_15_2018_base_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2018 Mercury) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2018_mercury_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_15_2018_mercury_device = +{ + "Razer Blade 15 (2018 Mercury)", + RAZER_BLADE_2018_MERCURY_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_15_2018_mercury_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2019 Advanced) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2019_advanced_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_15_2019_advanced_device = +{ + "Razer Blade 15 (2019 Advanced)", + RAZER_BLADE_2019_ADVANCED_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_15_2019_advanced_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2019 Base) | +| | +| Zone "Keyboard" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2019_base_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device blade_15_2019_base_device = +{ + "Razer Blade 15 (2019 Base)", + RAZER_BLADE_2019_BASE_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 1, + 1, + { + &blade_15_2019_base_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2019 Mercury) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2019_mercury_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_15_2019_mercury_device = +{ + "Razer Blade 15 (2019 Mercury)", + RAZER_BLADE_2019_MERCURY_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_15_2019_mercury_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2019 Studio) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2019_studio_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_15_2019_studio_device = +{ + "Razer Blade 15 (2019 Studio)", + RAZER_BLADE_2019_STUDIO_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_15_2019_studio_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2020 Advanced) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2020_advanced_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_15_2020_advanced_device = +{ + "Razer Blade 15 (2020 Advanced)", + RAZER_BLADE_2020_ADVANCED_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_15_2020_advanced_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2020 Base) | +| | +| Zone "Keyboard" | +| Linear | +| 1 Row, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2020_base_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_device blade_15_2020_base_device = +{ + "Razer Blade 15 (2020 Base)", + RAZER_BLADE_2020_BASE_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 1, + 16, + { + &blade_15_2020_base_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade (Late 2020) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_late_2020_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_late_2020_device = +{ + "Razer Blade (Late 2020)", + RAZER_BLADE_LATE_2020_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_late_2020_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2021 Advanced) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2021_advanced_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_15_2021_advanced_device = +{ + "Razer Blade 15 (2021 Advanced)", + RAZER_BLADE_2021_ADVANCED_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_15_2021_advanced_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2021 Base) | +| | +| Zone "Keyboard" | +| Linear | +| 1 Row, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2021_base_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_device blade_15_2021_base_device = +{ + "Razer Blade 15 (2021 Base)", + RAZER_BLADE_2021_BASE_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 1, + 16, + { + &blade_15_2021_base_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2021 Base) 1532:027A | +| | +| Zone "Keyboard" | +| Linear | +| 1 Row, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2021_base_v2_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_device blade_15_2021_base_v2_device = +{ + "Razer Blade 15 (2021 Base)", + RAZER_BLADE_2021_BASE_V2_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x1F, + 1, + 16, + { + &blade_15_2021_base_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (Late 2021 Advanced) 1532:2067 | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_late_2021_advanced_zone = +{ + "Keyboard", + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_15_late_2021_advanced_device = +{ + "Razer Blade 15 (Late 2021 Advanced)", + RAZER_BLADE_LATE_2021_ADVANCED_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_15_late_2021_advanced_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blade_15_2021_advanced_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 14 (2021) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_14_2021_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_14_2021_device = +{ + "Razer Blade 14 (2021)", + RAZER_BLADE_14_2021_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_14_2021_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_laptop_common_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 14 (2022) 1532:028C | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_14_2022_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_14_2022_device = +{ + "Razer Blade 14 (2022)", + RAZER_BLADE_14_2022_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x1F, + 6, + 16, + { + &blade_14_2022_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_laptop_with_spacebar_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 14 (2023) 1532:029D | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_14_2023_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_14_2023_device = +{ + "Razer Blade 14 (2023)", + RAZER_BLADE_14_2023_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x1F, + 6, + 16, + { + &blade_14_2023_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_laptop_with_spacebar_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blade 15 (2022) 1532:028A | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_15_2022_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_15_2022_device = +{ + "Razer Blade 15 (2022)", + RAZER_BLADE_15_2022_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x1F, + 6, + 16, + { + &blade_15_2022_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blade_15_2022_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Book 13 (2020) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone book_13_2020_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device book_13_2020_device = +{ + "Razer Book 13 (2020)", + RAZER_BOOK_13_2020_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &book_13_2020_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_laptop_common_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Pro (2016) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 25 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_pro_2016_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 25 +}; + +static const razer_device blade_pro_2016_device = +{ + "Razer Blade Pro (2016)", + RAZER_BLADE_PRO_2016_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 25, + { + &blade_pro_2016_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Pro (2017) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 25 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_pro_2017_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 25 +}; + +static const razer_device blade_pro_2017_device = +{ + "Razer Blade Pro (2017)", + RAZER_BLADE_PRO_2017_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 25, + { + &blade_pro_2017_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blade_pro_2017_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Pro (2017 FullHD) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 25 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_pro_2017_fullhd_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 25 +}; + +static const razer_device blade_pro_2017_fullhd_device = +{ + "Razer Blade Pro (2017 FullHD)", + RAZER_BLADE_PRO_2017_FULLHD_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 25, + { + &blade_pro_2017_fullhd_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Pro (2019) | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_pro_2019_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_pro_2019_device = +{ + "Razer Blade Pro (2019)", + RAZER_BLADE_PRO_2019_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_pro_2019_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Pro (Late 2019) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_pro_late_2019_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_pro_late_2019_device = +{ + "Razer Blade Pro (Late 2019)", + RAZER_BLADE_PRO_LATE_2019_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_pro_late_2019_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Pro 17 (2020) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_pro_17_2020_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_pro_17_2020_device = +{ + "Razer Blade Pro 17 (2020)", + RAZER_BLADE_PRO_17_2020_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_pro_17_2020_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Pro 17 (2021) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_pro_17_2021_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_pro_17_2021_device = +{ + "Razer Blade Pro 17 (2021)", + RAZER_BLADE_PRO_17_2021_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_pro_17_2021_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blade_17_pro_2021_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Stealth (2016) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_stealth_2016_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_stealth_2016_device = +{ + "Razer Blade Stealth (2016)", + RAZER_BLADE_STEALTH_2016_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_stealth_2016_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_blade_stealth_2016_layout +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Stealth (Late 2016) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_stealth_late_2016_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_stealth_late_2016_device = +{ + "Razer Blade Stealth (Late 2016)", + RAZER_BLADE_STEALTH_LATE_2016_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_stealth_late_2016_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Stealth (2017) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_stealth_2017_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_stealth_2017_device = +{ + "Razer Blade Stealth (2017)", + RAZER_BLADE_STEALTH_2017_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_stealth_2017_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Stealth (Late 2017) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_stealth_late_2017_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_stealth_late_2017_device = +{ + "Razer Blade Stealth (Late 2017)", + RAZER_BLADE_STEALTH_LATE_2017_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_stealth_late_2017_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Stealth (2019) | +| | +| Zone "Keyboard" | +| Matrix | +| 6 Rows, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_stealth_2019_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_MATRIX, + 6, + 16 +}; + +static const razer_device blade_stealth_2019_device = +{ + "Razer Blade Stealth (2019)", + RAZER_BLADE_STEALTH_2019_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 6, + 16, + { + &blade_stealth_2019_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Stealth (Late 2019) | +| | +| Zone "Keyboard" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone blade_stealth_late_2019_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device blade_stealth_late_2019_device = +{ + "Razer Blade Stealth (Late 2019)", + RAZER_BLADE_STEALTH_LATE_2019_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 1, + 1, + { + &blade_stealth_late_2019_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Stealth (2020) | +| | +| Zone "Keyboard" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone blade_stealth_2020_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device blade_stealth_2020_device = +{ + "Razer Blade Stealth (2020)", + RAZER_BLADE_STEALTH_2020_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 1, + 1, + { + &blade_stealth_2020_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Blade Stealth (Late 2020) | +| | +| Zone "Keyboard" | +| Linear | +| 1 Row, 16 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone blade_stealth_late_2020_zone = +{ + ZONE_EN_KEYBOARD, + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_device blade_stealth_late_2020_device = +{ + "Razer Blade Stealth (Late 2020)", + RAZER_BLADE_STEALTH_LATE_2020_PID, + DEVICE_TYPE_LAPTOP, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 1, + 16, + { + &blade_stealth_late_2020_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------------------*\ +| MICE | +\*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------*\ +| Razer Abyssus Elite DVa Edition 1532:006A | +| | +| Zone "Logo" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone abyssus_elite_dva_edition_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device abyssus_elite_dva_edition_device = +{ + "Razer Abyssus Elite DVa Edition", + RAZER_ABYSSUS_ELITE_DVA_EDITION_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &abyssus_elite_dva_edition_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Abyssus Essential 1532:006B | +| | +| Zone "Logo" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone abyssus_essential_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device abyssus_essential_device = +{ + "Razer Abyssus Essential", + RAZER_ABYSSUS_ESSENTIAL_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &abyssus_essential_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk 1532:0064 | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device basilisk_device = +{ + "Razer Basilisk", + RAZER_BASILISK_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 2, + { + &basilisk_logo_zone, + &basilisk_scroll_wheel_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk Essential | +| | +| Zone "Logo" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_essential_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device basilisk_essential_device = +{ + "Razer Basilisk Essential", + RAZER_BASILISK_ESSENTIAL_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &basilisk_essential_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk Ultimate (Wired) 1532:0086 | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Left LED Strip" | +| Linear | +| 8 LED | +| | +| Zone "Right LED Strip" | +| Linear | +| 4 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_ultimate_wired_right_zone = +{ + "Right LED Strip", + ZONE_TYPE_LINEAR, + 1, + 4 +}; + +static const razer_zone basilisk_ultimate_wired_left_zone = +{ + "Left LED Strip", + ZONE_TYPE_LINEAR, + 1, + 8 +}; + +static const razer_zone basilisk_ultimate_wired_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_ultimate_wired_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device basilisk_ultimate_wired_device = +{ + "Razer Basilisk Ultimate", + RAZER_BASILISK_ULTIMATE_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 14, + { + &basilisk_ultimate_wired_scroll_wheel_zone, + &basilisk_ultimate_wired_logo_zone, + &basilisk_ultimate_wired_left_zone, + &basilisk_ultimate_wired_right_zone, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk Ultimate (Wireless) 1532:0088 | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Left LED Strip" | +| Linear | +| 8 LED | +| | +| Zone "Right LED Strip" | +| Linear | +| 4 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_ultimate_wireless_right_zone = +{ + "Right LED Strip", + ZONE_TYPE_LINEAR, + 1, + 4 +}; + +static const razer_zone basilisk_ultimate_wireless_left_zone = +{ + "Left LED Strip", + ZONE_TYPE_LINEAR, + 1, + 8 +}; + +static const razer_zone basilisk_ultimate_wireless_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_ultimate_wireless_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device basilisk_ultimate_wireless_device = +{ + "Razer Basilisk Ultimate (Wireless)", + RAZER_BASILISK_ULTIMATE_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 14, + { + &basilisk_ultimate_wireless_scroll_wheel_zone, + &basilisk_ultimate_wireless_logo_zone, + &basilisk_ultimate_wireless_left_zone, + &basilisk_ultimate_wireless_right_zone, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V2 1532:0085 | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_v2_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v2_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device basilisk_v2_device = +{ + "Razer Basilisk V2", + RAZER_BASILISK_V2_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 2, + { + &basilisk_v2_scroll_wheel_zone, + &basilisk_v2_logo_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 1532:0099 | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 9 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_v3_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_ledstrip_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_device basilisk_v3_device = +{ + "Razer Basilisk V3", + RAZER_BASILISK_V3_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 11, + { + &basilisk_v3_logo_zone, + &basilisk_v3_scroll_wheel_zone, + &basilisk_v3_ledstrip_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 35K 1532:00CB | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 9 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_v3_35k_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_35k_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_35k_ledstrip_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_device basilisk_v3_35k_device = +{ + "Razer Basilisk V3 35K", + RAZER_BASILISK_V3_35K_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 11, + { + &basilisk_v3_35k_logo_zone, + &basilisk_v3_35k_scroll_wheel_zone, + &basilisk_v3_35k_ledstrip_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 PRO Wired 1532:00AA | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 11 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_v3_pro_wired_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_pro_wired_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_pro_wired_ledstrip_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 11 +}; + +static const razer_device basilisk_v3_pro_wired_device = +{ + "Razer Basilisk V3 Pro (Wired)", + RAZER_BASILISK_V3_PRO_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 13, + { + &basilisk_v3_pro_wired_scroll_wheel_zone, + &basilisk_v3_pro_wired_logo_zone, + &basilisk_v3_pro_wired_ledstrip_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 PRO Wireless 1532:00AB | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 11 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_v3_pro_wireless_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_pro_wireless_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_pro_wireless_ledstrip_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 11 +}; + +static const razer_device basilisk_v3_pro_wireless_device = +{ + "Razer Basilisk V3 Pro (Wireless)", + RAZER_BASILISK_V3_PRO_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 13, + { + &basilisk_v3_pro_wireless_scroll_wheel_zone, + &basilisk_v3_pro_wireless_logo_zone, + &basilisk_v3_pro_wireless_ledstrip_zone, + NULL, + NULL, + NULL + }, + NULL +}; +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 PRO 35K Wired 1532:00CC | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 11 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_v3_pro_35k_wired_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_pro_35k_wired_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_pro_35k_wired_ledstrip_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 11 +}; + +static const razer_device basilisk_v3_pro_35k_wired_device = +{ + "Razer Basilisk V3 Pro 35K (Wired)", + RAZER_BASILISK_V3_PRO_35K_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 13, + { + &basilisk_v3_pro_35k_wired_scroll_wheel_zone, + &basilisk_v3_pro_35k_wired_logo_zone, + &basilisk_v3_pro_35k_wired_ledstrip_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 PRO 35K Wireless 1532:00CD | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 11 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_v3_pro_35k_wireless_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_pro_35k_wireless_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_pro_35k_wireless_ledstrip_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 11 +}; + +static const razer_device basilisk_v3_pro_35k_wireless_device = +{ + "Razer Basilisk V3 Pro 35K (Wireless)", + RAZER_BASILISK_V3_PRO_35K_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 13, + { + &basilisk_v3_pro_35k_wireless_scroll_wheel_zone, + &basilisk_v3_pro_35k_wireless_logo_zone, + &basilisk_v3_pro_35k_wireless_ledstrip_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 PRO 35K Phantom Green Wired 1532:00D6 | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 11 LEDs | +\*-------------------------------------------------------------*/ + +static const razer_device basilisk_v3_pro_35k_pg_wired_device = +{ + "Razer Basilisk V3 Pro 35K Phantom Green Edition (Wired)", + RAZER_BASILISK_V3_PRO_35K_PG_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 13, + { + &basilisk_v3_pro_35k_wired_scroll_wheel_zone, + &basilisk_v3_pro_35k_wired_ledstrip_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 PRO 35K Phantom Green Wireless 1532:00D7 | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 11 LEDs | +\*-------------------------------------------------------------*/ + +static const razer_device basilisk_v3_pro_35k_pg_wireless_device = +{ + "Razer Basilisk V3 Pro 35K Phantom Green Edition (Wireless)", + RAZER_BASILISK_V3_PRO_35K_PG_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 13, + { + &basilisk_v3_pro_35k_wireless_scroll_wheel_zone, + &basilisk_v3_pro_35k_wireless_ledstrip_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 PRO 35K Phantom Green Bluetooth 068E:00D8 | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 11 LEDs | +\*-------------------------------------------------------------*/ + +static const razer_device basilisk_v3_pro_35k_pg_bluetooth_device = +{ + "Razer Basilisk V3 Pro 35K Phantom Green Edition (Bluetooth)", + RAZER_BASILISK_V3_PRO_35K_PG_BLUETOOTH_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 13, + { + &basilisk_v3_pro_35k_wireless_scroll_wheel_zone, + &basilisk_v3_pro_35k_wireless_ledstrip_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 PRO Bluetooth 1532:00AC | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 11 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_v3_pro_bluetooth_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_pro_bluetooth_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone basilisk_v3_pro_bluetooth_ledstrip_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 11 +}; + +static const razer_device basilisk_v3_pro_bluetooth_device = +{ + "Razer Basilisk V3 Pro (Bluetooth)", + RAZER_BASILISK_V3_PRO_BLUETOOTH_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 13, + { + &basilisk_v3_pro_bluetooth_scroll_wheel_zone, + &basilisk_v3_pro_bluetooth_logo_zone, + &basilisk_v3_pro_bluetooth_ledstrip_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Basilisk V3 X HyperSpeed 1532:00B9 | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone basilisk_v3_x_hyperspeed_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device basilisk_v3_x_hyperspeed_device = +{ + "Razer Basilisk V3 X HyperSpeed", + RAZER_BASILISK_V3_X_HYPERSPEED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 1, + { + &basilisk_v3_x_hyperspeed_scroll_wheel_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Cobra 1532:00A3 | +| | +| Zone "Logo" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone cobra_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device cobra_device = +{ + "Razer Cobra", + RAZER_COBRA_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 1, + { + &cobra_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Cobra Pro Wired 1532:00AF | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Underglow" | +| Linear | +| 11 LED | +\*-------------------------------------------------------------*/ +static const razer_zone cobra_pro_wired_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone cobra_pro_wired_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone cobra_pro_wired_underglow_zone = +{ + "Underglow", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_device cobra_pro_wired_device = +{ + "Razer Cobra Pro (Wired)", + RAZER_COBRA_PRO_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 11, + { + &cobra_pro_wired_logo_zone, + &cobra_pro_wired_scroll_wheel_zone, + &cobra_pro_wired_underglow_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Cobra Pro Wireless 1532:00B0 | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Underglow" | +| Linear | +| 11 LED | +\*-------------------------------------------------------------*/ +static const razer_zone cobra_pro_wireless_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone cobra_pro_wireless_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone cobra_pro_wireless_underglow_zone = +{ + "Underglow", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_device cobra_pro_wireless_device = +{ + "Razer Cobra Pro (Wireless)", + RAZER_COBRA_PRO_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 11, + { + &cobra_pro_wireless_logo_zone, + &cobra_pro_wireless_scroll_wheel_zone, + &cobra_pro_wireless_underglow_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer DeathAdder Chroma | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone deathadder_chroma_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone deathadder_chroma_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device deathadder_chroma_device = +{ + "Razer DeathAdder Chroma", + RAZER_DEATHADDER_CHROMA_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_CUSTOM, + 0x1F, + 1, + 2, + { + &deathadder_chroma_logo_zone, + &deathadder_chroma_scroll_wheel_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Deathadder Elite | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone deathadder_elite_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone deathadder_elite_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device deathadder_elite_device = +{ + "Razer DeathAdder Elite", + RAZER_DEATHADDER_ELITE_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 2, + { + &deathadder_elite_logo_zone, + &deathadder_elite_scroll_wheel_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Deathadder Essential 1532:006E | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone deathadder_essential_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone deathadder_essential_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device deathadder_essential_device = +{ + "Razer DeathAdder Essential", + RAZER_DEATHADDER_ESSENTIAL_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 2, + { + &deathadder_essential_logo_zone, + &deathadder_essential_scroll_wheel_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Deathadder Essential V2 1532:0098 | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone deathadder_essential_v2_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone deathadder_essential_v2_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device deathadder_essential_v2_device = +{ + "Razer DeathAdder Essential V2", + RAZER_DEATHADDER_ESSENTIAL_V2_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 2, + { + &deathadder_essential_v2_logo_zone, + &deathadder_essential_v2_scroll_wheel_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Deathadder Essential White Edition 1532:0071 | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone deathadder_essential_white_edition_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone deathadder_essential_white_edition_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device deathadder_essential_white_edition_device = +{ + "Razer DeathAdder Essential (White Edition)", + RAZER_DEATHADDER_ESSENTIAL_WHITE_EDITION_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 2, + { + &deathadder_essential_white_edition_logo_zone, + &deathadder_essential_white_edition_scroll_wheel_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer DeathAdder V2 1532:0084 | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone deathadder_v2_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone deathadder_v2_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device deathadder_v2_device = +{ + "Razer DeathAdder V2", + RAZER_DEATHADDER_V2_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 2, + { + &deathadder_v2_scroll_wheel_zone, + &deathadder_v2_logo_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer DeathAdder V2 Mini 1532:008C | +| | +| Zone "Logo" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone deathadder_v2_mini_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device deathadder_v2_mini_device = +{ + "Razer DeathAdder V2 Mini", + RAZER_DEATHADDER_V2_MINI_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 1, + { + &deathadder_v2_mini_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer DeathAdder V2 Pro (Wired) 1532:007C | +| | +| Zone "Logo" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone deathadder_v2_pro_wired_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device deathadder_v2_pro_wired_device = +{ + "Razer DeathAdder V2 (Wired)", + RAZER_DEATHADDER_V2_PRO_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &deathadder_v2_pro_wired_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer DeathAdder V2 Pro (Wireless) 1532:007D | +| | +| Zone "Logo" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone deathadder_v2_pro_wireless_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device deathadder_v2_pro_wireless_device = +{ + "Razer DeathAdder V2 (Wireless)", + RAZER_DEATHADDER_V2_PRO_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &deathadder_v2_pro_wireless_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Diamondback Chroma | +| | +| Zone "LED Strip" | +| Linear | +| 19 LEDs | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone diamondback_chroma_led_strip_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 19 +}; + +static const razer_zone diamondback_chroma_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone diamondback_chroma_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device diamondback_chroma_device = +{ + "Razer Diamondback Chroma", + RAZER_DIAMONDBACK_CHROMA_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_LINEAR, + 0x3F, + 1, + 21, + { + &diamondback_chroma_led_strip_zone, + &diamondback_chroma_logo_zone, + &diamondback_chroma_scroll_wheel_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Lancehead 2017 (Wired) | +| | +| Zone "Right" | +| Linear | +| 7 LEDs | +| | +| Zone "Left" | +| Linear | +| 7 LEDs | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone lancehead_2017_wired_right_zone = +{ + "Right LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone lancehead_2017_wired_left_zone = +{ + "Left LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone lancehead_2017_wired_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone lancehead_2017_wired_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device lancehead_2017_wired_device = +{ + "Razer Lancehead 2017 (Wired)", + RAZER_LANCEHEAD_2017_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 16, + { + &lancehead_2017_wired_right_zone, + &lancehead_2017_wired_left_zone, + &lancehead_2017_wired_logo_zone, + &lancehead_2017_wired_scroll_wheel_zone, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Lancehead 2017 (Wireless) | +| | +| Zone "Right" | +| Linear | +| 7 LEDs | +| | +| Zone "Left" | +| Linear | +| 7 LEDs | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone lancehead_2017_wireless_right_zone = +{ + "Right LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone lancehead_2017_wireless_left_zone = +{ + "Left LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone lancehead_2017_wireless_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone lancehead_2017_wireless_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device lancehead_2017_wireless_device = +{ + "Razer Lancehead 2017 (Wireless)", + RAZER_LANCEHEAD_2017_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 16, + { + &lancehead_2017_wireless_right_zone, + &lancehead_2017_wireless_left_zone, + &lancehead_2017_wireless_logo_zone, + &lancehead_2017_wireless_scroll_wheel_zone, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Lancehead 2019 (Wired) | +| | +| Zone "Right" | +| Linear | +| 7 LEDs | +| | +| Zone "Left" | +| Linear | +| 7 LEDs | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone lancehead_2019_wired_right_zone = +{ + "Right LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone lancehead_2019_wired_left_zone = +{ + "Left LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone lancehead_2019_wired_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone lancehead_2019_wired_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device lancehead_2019_wired_device = +{ + "Razer Lancehead 2019 (Wired)", + RAZER_LANCEHEAD_2019_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 16, + { + &lancehead_2019_wired_right_zone, + &lancehead_2019_wired_left_zone, + &lancehead_2019_wired_logo_zone, + &lancehead_2019_wired_scroll_wheel_zone, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Lancehead 2019 (Wireless) | +| | +| Zone "Right" | +| Linear | +| 7 LEDs | +| | +| Zone "Left" | +| Linear | +| 7 LEDs | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone lancehead_2019_wireless_right_zone = +{ + "Right LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone lancehead_2019_wireless_left_zone = +{ + "Left LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone lancehead_2019_wireless_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone lancehead_2019_wireless_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device lancehead_2019_wireless_device = +{ + "Razer Lancehead 2019 (Wireless)", + RAZER_LANCEHEAD_2019_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 16, + { + &lancehead_2019_wireless_right_zone, + &lancehead_2019_wireless_left_zone, + &lancehead_2019_wireless_logo_zone, + &lancehead_2019_wireless_scroll_wheel_zone, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Lancehead Tournament Edition 1532:0060 | +| | +| Zone "Right" | +| Linear | +| 7 LEDs | +| | +| Zone "Left" | +| Linear | +| 7 LEDs | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone lancehead_te_right_zone = +{ + "Right LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone lancehead_te_left_zone = +{ + "Left LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone lancehead_te_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone lancehead_te_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device lancehead_te_device = +{ + "Razer Lancehead Tournament Edition", + RAZER_LANCEHEAD_TE_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 16, + { + &lancehead_te_scroll_wheel_zone, + &lancehead_te_logo_zone, + &lancehead_te_right_zone, + &lancehead_te_left_zone, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Leviathan V2 1532:0532 | +| | +| Zone "Speaker Underglow" | +| Linear | +| 18 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone leviathan_v2_speaker_zone = +{ + "Speaker Underglow", + ZONE_TYPE_LINEAR, + 2, + 9 +}; + +static const razer_device leviathan_v2_device = +{ + "Razer Leviathan V2", + RAZER_LEVIATHAN_V2_PID, + DEVICE_TYPE_SPEAKER, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 2, + 9, + { + &leviathan_v2_speaker_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Leviathan V2X 1532:054A | +| | +| Zone "Speaker Underglow" | +| Linear | +| 14 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone leviathan_v2x_speaker_zone = +{ + "Speaker Underglow", + ZONE_TYPE_LINEAR, + 1, + 14 +}; + +static const razer_device leviathan_v2x_device = +{ + "Razer Leviathan V2 X", + RAZER_LEVIATHAN_V2X_PID, + DEVICE_TYPE_SPEAKER, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 14, + { + &leviathan_v2x_speaker_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mamba 2012 (Wired) | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone mamba_2012_wired_zone = +{ + "Scroll Wheel", + ZONE_TYPE_LINEAR, + 1, + 1 +}; + +static const razer_device mamba_2012_wired_device = +{ + "Razer Mamba 2012 (Wired)", + RAZER_MAMBA_2012_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_CUSTOM, + 0x3F, + 1, + 15, + { + &mamba_2012_wired_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mamba 2012 (Wireless) | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone mamba_2012_wireless_zone = +{ + "Scroll Wheel", + ZONE_TYPE_LINEAR, + 1, + 1 +}; + +static const razer_device mamba_2012_wireless_device = +{ + "Razer Mamba 2012 (Wireless)", + RAZER_MAMBA_2012_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_CUSTOM, + 0x3F, + 1, + 15, + { + &mamba_2012_wireless_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mamba 2015 (Wired) | +| | +| Zone "Chroma Zone" | +| Single | +| 15 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone mamba_2015_wired_zone = +{ + "Chroma Zone", + ZONE_TYPE_LINEAR, + 1, + 15 +}; + +static const razer_device mamba_2015_wired_device = +{ + "Razer Mamba 2015 (Wired)", + RAZER_MAMBA_2015_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_LINEAR, + 0x3F, + 1, + 15, + { + &mamba_2015_wired_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mamba 2015 (Wireless) | +| | +| Zone "Chroma Zone" | +| Single | +| 15 LED | +\*-------------------------------------------------------------*/ +static const razer_zone mamba_2015_wireless_zone = +{ + "Chroma Zone", + ZONE_TYPE_LINEAR, + 1, + 15 +}; + +static const razer_device mamba_2015_wireless_device = +{ + "Razer Mamba (Wireless)", + RAZER_MAMBA_2015_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_LINEAR, + 0x3F, + 1, + 15, + { + &mamba_2015_wireless_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mamba 2018 (Wired) | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone mamba_2018_wired_logo_zone = +{ + "Logo Zone", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone mamba_2018_wired_scroll_wheel_zone = +{ + "Scroll Wheel Zone", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device mamba_2018_wired_device = +{ + "Razer Mamba 2018 (Wired)", + RAZER_MAMBA_2018_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 2, + { + &mamba_2018_wired_scroll_wheel_zone, + &mamba_2018_wired_logo_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mamba Wireless (2018) Wireless 1532:0072 | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ + +static const razer_zone mamba_2018_wireless_logo_zone = +{ + "Logo Zone", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone mamba_2018_wireless_scroll_wheel_zone = +{ + "Scroll Wheel Zone", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device mamba_2018_wireless_device = +{ + "Razer Mamba 2018 (Wireless)", + RAZER_MAMBA_2018_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 2, + { + &mamba_2018_wireless_scroll_wheel_zone, + &mamba_2018_wireless_logo_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mamba Elite | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Left" | +| Linear | +| 9 LEDs | +| | +| Zone "Right" | +| Linear | +| 9 LEDs | +| | +\*-------------------------------------------------------------*/ +static const razer_zone mamba_elite_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone mamba_elite_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone mamba_elite_left_zone = +{ + "Left LED Strip", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_zone mamba_elite_right_zone = +{ + "Right LED Strip", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_device mamba_elite_device = +{ + "Razer Mamba Elite", + RAZER_MAMBA_ELITE_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 20, + { + &mamba_elite_scroll_wheel_zone, + &mamba_elite_logo_zone, + &mamba_elite_left_zone, + &mamba_elite_right_zone, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mamba Tournament Edition | +| | +| Zone "Left" | +| Linear | +| 7 LEDs | +| | +| Zone "Right" | +| Linear | +| 7 LEDs | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone mamba_te_left_zone = +{ + "Left LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone mamba_te_right_zone = +{ + "Right LED Strip", + ZONE_TYPE_LINEAR, + 1, + 7 +}; + +static const razer_zone mamba_te_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone mamba_te_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device mamba_te_device = +{ + "Razer Mamba Tournament Edition", + RAZER_MAMBA_TE_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_LINEAR, + 0x3F, + 1, + 16, + { + &mamba_te_left_zone, + &mamba_te_right_zone, + &mamba_te_logo_zone, + &mamba_te_scroll_wheel_zone, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mamba Hyperflux (Wired) | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ + +static const razer_zone mamba_hyperflux_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone mamba_hyperflux_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device mamba_hyperflux_device = +{ + "Razer Mamba Hyperflux (Wired)", + RAZER_MAMBA_HYPERFLUX_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 2, + { + &mamba_hyperflux_scroll_wheel_zone, + &mamba_hyperflux_logo_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Naga Chroma 1532:0053 | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Numpad" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone naga_chroma_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_chroma_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_chroma_numpad_zone = +{ + "Numpad", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device naga_chroma_device = +{ + "Razer Naga Chroma", + RAZER_NAGA_CHROMA_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_LINEAR, + 0x3F, + 1, + 3, + { + &naga_chroma_scroll_wheel_zone, + &naga_chroma_logo_zone, + &naga_chroma_numpad_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Naga Classic 1532:0093 | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Numpad" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone naga_classic_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_classic_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_classic_numpad_zone = +{ + "Numpad", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device naga_classic_device = +{ + "Razer Naga Classic", + RAZER_NAGA_CLASSIC_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 3, + { + &naga_classic_logo_zone, + &naga_classic_scroll_wheel_zone, + &naga_classic_numpad_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Naga Hex V2 1532:0050 | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Numpad" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone naga_hex_v2_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_hex_v2_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_hex_v2_numpad_zone = +{ + "Numpad", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device naga_hex_v2_device = +{ + "Razer Naga Hex V2", + RAZER_NAGA_HEX_V2_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 1, + 3, + { + &naga_hex_v2_logo_zone, + &naga_hex_v2_scroll_wheel_zone, + &naga_hex_v2_numpad_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Naga Left Handed 1532:008D | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Numpad" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone naga_left_handed_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_left_handed_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_left_handed_numpad_zone = +{ + "Numpad", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device naga_left_handed_device = +{ + "Razer Naga Left Handed", + RAZER_NAGA_LEFT_HANDED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 3, + { + &naga_left_handed_logo_zone, + &naga_left_handed_scroll_wheel_zone, + &naga_left_handed_numpad_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Naga Trinity 1532:0067 | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Numpad" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone naga_trinity_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_trinity_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_trinity_numpad_zone = +{ + "Numpad", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device naga_trinity_device = +{ + "Razer Naga Trinity", + RAZER_NAGA_TRINITY_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 3, + { + &naga_trinity_scroll_wheel_zone, + &naga_trinity_logo_zone, + &naga_trinity_numpad_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Naga Pro 1532:008F (wired) 1532:0090 (wireless) | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Numpad" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone naga_pro_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_pro_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_pro_numpad_zone = +{ + "Numpad", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device naga_pro_wired_device = +{ + "Razer Naga Pro (Wired)", + RAZER_NAGA_PRO_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 3, + { + &naga_pro_scroll_wheel_zone, + &naga_pro_logo_zone, + &naga_pro_numpad_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +static const razer_device naga_pro_wireless_device = +{ + "Razer Naga Pro (Wireless)", + RAZER_NAGA_PRO_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 3, + { + &naga_pro_scroll_wheel_zone, + &naga_pro_logo_zone, + &naga_pro_numpad_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Naga Pro V2 1532:00A7 (wired) 1532:00A8 (wireless) | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Numpad" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone naga_pro_v2_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_pro_v2_numpad_zone = +{ + "Numpad", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device naga_pro_v2_wired_device = +{ + "Razer Naga Pro V2 (Wired)", + RAZER_NAGA_PRO_V2_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 2, + { + &naga_pro_v2_logo_zone, + &naga_pro_v2_numpad_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +static const razer_device naga_pro_v2_wireless_device = +{ + "Razer Naga Pro V2 (Wireless)", + RAZER_NAGA_PRO_V2_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 2, + { + &naga_pro_v2_logo_zone, + &naga_pro_v2_numpad_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Viper 8kHz 1532:0091 | +| | +| Zone "Logo" | +| Matrix | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone viper_8khz_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device viper_8khz_device = +{ + "Razer Viper 8kHz", + RAZER_VIPER_8KHZ_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 1, + { + &viper_8khz_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Viper Mini 1532:008A | +| | +| Zone "Logo" | +| Matrix | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone viper_mini_logo_zone = +{ + "Logo", //Matrix of one as per https://github.com/openrazer/openrazer/blob/master/daemon/openrazer_daemon/hardware/mouse.py#L27 + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device viper_mini_device = +{ + "Razer Viper Mini", + RAZER_VIPER_MINI_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &viper_mini_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Viper Ultimate Wired 1532:007A | +| | +| Zone "Logo" | +| Matrix | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone viper_ultimate_wired_logo_zone = +{ + "Logo", //Matrix of one as per https://github.com/openrazer/openrazer/blob/master/daemon/openrazer_daemon/hardware/mouse.py#L1690 + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device viper_ultimate_wired_device = +{ + "Razer Viper Ultimate (Wired)", + RAZER_VIPER_ULTIMATE_WIRED_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &viper_ultimate_wired_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Viper Ultimate Wireless 1532:007B | +| | +| Zone "Logo" | +| Matrix | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone viper_ultimate_wireless_logo_zone = +{ + "Logo", //Matrix of one as per https://github.com/openrazer/openrazer/blob/master/daemon/openrazer_daemon/hardware/mouse.py#L1690 + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device viper_ultimate_wireless_device = +{ + "Razer Viper Ultimate (Wireless)", + RAZER_VIPER_ULTIMATE_WIRELESS_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &viper_ultimate_wireless_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Viper 1532:0078 | +| | +| Zone "Logo" | +| Matrix | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone viper_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device viper_device = +{ + "Razer Viper", + RAZER_VIPER_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &viper_logo_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Naga Epic Chroma | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Numpad" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone naga_epic_chroma_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone naga_epic_chroma_numpad_zone = +{ + "Numpad", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device naga_epic_chroma_device = +{ + "Razer Naga Epic Chroma", + RAZER_NAGA_EPIC_CHROMA_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_CUSTOM, + 0x1F, + 1, + 2, + { + &naga_epic_chroma_scroll_wheel_zone, + &naga_epic_chroma_numpad_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------------------*\ +| KEYPADS | +\*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------*\ +| Razer Orbweaver Chroma | +| | +| Zone "Keypad" | +| Matrix | +| 4 Rows, 5 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone orbweaver_chroma_zone = +{ + "Keypad", + ZONE_TYPE_MATRIX, + 4, + 5 +}; + +static const razer_device orbweaver_chroma_device = +{ + "Razer Orbweaver Chroma", + RAZER_ORBWEAVER_CHROMA_PID, + DEVICE_TYPE_KEYPAD, + RAZER_MATRIX_TYPE_CUSTOM, + 0x3F, + 4, + 5, + { + &orbweaver_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Tartarus Chroma | +| | +| Zone "Keypad" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone tartarus_chroma_zone = +{ + "Keypad", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device tartarus_chroma_device = +{ + "Razer Tartarus Chroma", + RAZER_TARTARUS_CHROMA_PID, + DEVICE_TYPE_KEYPAD, + RAZER_MATRIX_TYPE_CUSTOM, + 0x1F, + 1, + 1, + { + &tartarus_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Tartarus Pro 1532:0244 | +| | +| Zone "Keypad" | +| Matrix | +| 4 Rows, 5 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone tartarus_pro_zone = +{ + "Keypad", + ZONE_TYPE_MATRIX, + 4, + 5 +}; + +static const razer_zone tartarus_pro_K20 = +{ + "Keypad LED 20", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device tartarus_pro_device = +{ + "Razer Tartarus Pro", + RAZER_TARTARUS_PRO_PID, + DEVICE_TYPE_KEYPAD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 21, + { + &tartarus_pro_zone, + &tartarus_pro_K20, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Tartarus V2 1532:022B | +| | +| Zone "Keypad" | +| Matrix | +| 4 Rows, 6 Columns | +\*-------------------------------------------------------------*/ +static const razer_zone tartarus_v2_zone = +{ + "Keypad", + ZONE_TYPE_MATRIX, + 4, + 6 +}; + +static const razer_device tartarus_v2_device = +{ + "Razer Tartarus V2", + RAZER_TARTARUS_V2_PID, + DEVICE_TYPE_KEYPAD, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 4, + 6, + { + &tartarus_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + &razer_tartarus_v2_layout +}; + +/*-------------------------------------------------------------------------*\ +| MOUSEMATS | +\*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------*\ +| Razer Firefly | +| | +| Zone "LED Strip" | +| Linear | +| 15 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone firefly_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 15 +}; + +static const razer_device firefly_device = +{ + "Razer Firefly", + RAZER_FIREFLY_PID, + DEVICE_TYPE_MOUSEMAT, + RAZER_MATRIX_TYPE_LINEAR, + 0x3F, + 1, + 15, + { + &firefly_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Firefly Hyperflux | +| | +| Zone "Scroll Wheel" | +| Single | +| 1 LED | +| | +| Zone "Logo" | +| Single | +| 1 LED | +| | +| Zone "Mousemat" | +| Linear | +| 12 LEDs | +\*-------------------------------------------------------------*/ + +static const razer_zone firefly_hyperflux_scroll_wheel_zone = +{ + "Scroll Wheel", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone firefly_hyperflux_logo_zone = +{ + "Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone firefly_hyperflux_mousemat_zone = +{ + "Mousemat", + ZONE_TYPE_LINEAR, + 1, + 12 +}; + +static const razer_device firefly_hyperflux_device = +{ + "Razer Firefly Hyperflux", + RAZER_FIREFLY_HYPERFLUX_PID, + DEVICE_TYPE_MOUSEMAT, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 14, + { + &firefly_hyperflux_scroll_wheel_zone, + &firefly_hyperflux_logo_zone, + &firefly_hyperflux_mousemat_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Firefly V2 | +| | +| Zone "LED Strip" | +| Linear | +| 19 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone firefly_v2_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 19 +}; + +static const razer_device firefly_v2_device = +{ + "Razer Firefly V2", + RAZER_FIREFLY_V2_PID, + DEVICE_TYPE_MOUSEMAT, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 19, + { + &firefly_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Firefly Pro V2 | +| | +| Zone "LED Strip" | +| Linear | +| 17 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone firefly_v2_pro_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 17 +}; + +static const razer_device firefly_v2_pro_device = +{ + "Razer Firefly V2 Pro", + RAZER_FIREFLY_V2_PRO_PID, + DEVICE_TYPE_MOUSEMAT, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 17, + { + &firefly_v2_pro_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Goliathus | +| | +| Zone "LED Strip" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone goliathus_zone = +{ + "LED Strip", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device goliathus_device = +{ + "Razer Goliathus", + RAZER_GOLIATHUS_CHROMA_PID, + DEVICE_TYPE_MOUSEMAT, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &goliathus_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Goliathus Chroma 3XL 1532:0C06 | +| | +| Zone "LED Strip" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone goliathus_chroma_3xl_zone = +{ + "LED Strip", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device goliathus_chroma_3xl_device = +{ + "Razer Goliathus Chroma 3XL", + RAZER_GOLIATHUS_CHROMA_3XL_PID, + DEVICE_TYPE_MOUSEMAT, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 1, + { + &goliathus_chroma_3xl_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Goliathus Extended | +| | +| Zone "LED Strip" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone goliathus_extended_zone = +{ + "LED Strip", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device goliathus_extended_device = +{ + "Razer Goliathus Extended", + RAZER_GOLIATHUS_CHROMA_EXTENDED_PID, + DEVICE_TYPE_MOUSEMAT, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &goliathus_extended_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Strider Chroma | +| | +| Zone "LED Strip" | +| Matrix | +| 19 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone strider_chroma_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 19 +}; + +static const razer_device strider_chroma_device = +{ + "Razer Strider Chroma", + RAZER_STRIDER_CHROMA_PID, + DEVICE_TYPE_MOUSEMAT, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 19, + { + &strider_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------------------*\ +| HEADSETS | +\*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------*\ +| Razer Kraken 7.1 Chroma | +| | +| Zone "Headset" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_chroma_zone = +{ + "Headset", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device kraken_chroma_device = +{ + "Razer Kraken 7.1 Chroma", + RAZER_KRAKEN_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_NONE, + 0, + 1, + 1, + { + &kraken_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken 7.1 V2 | +| | +| Zone "Headset" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_v2_zone = +{ + "Headset", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device kraken_v2_device = +{ + "Razer Kraken 7.1 V2", + RAZER_KRAKEN_V2_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_NONE, + 0, + 1, + 1, + { + &kraken_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken Ultimate 1532:0527 | +| | +| Zone "Headset" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_ultimate_zone = +{ + "Headset", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device kraken_ultimate_device = +{ + "Razer Kraken Ultimate", + RAZER_KRAKEN_ULTIMATE_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_NONE, + 0, + 1, + 1, + { + &kraken_ultimate_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken Kitty Edition 1532:0F19 | +| | +| Zone "Headset" | +| Matrix | +| 4 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_kitty_zone = +{ + "Headset", + ZONE_TYPE_LINEAR, + 1, + 4 +}; + +static const razer_device kraken_kitty_device = +{ + "Razer Kraken Kitty Edition", + RAZER_KRAKEN_KITTY_EDITION_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 4, + { + &kraken_kitty_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken Kitty Edition V2 1532:0560 | +| | +| Zone "Headset" | +| Matrix | +| 4 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_kitty_black_v2_zone = +{ + "Cat ears", + ZONE_TYPE_LINEAR, + 1, + 2 +}; + +static const razer_zone kraken_kitty_black_v2_headset_left_zone = +{ + "Headset Left", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone kraken_kitty_black_v2_headset_right_zone = +{ + "Headset Right", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device kraken_kitty_black_v2_device = +{ + "Razer Kraken Kitty Black Edition V2", + RAZER_KRAKEN_KITTY_BLACK_EDITION_V2_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 4, + { + &kraken_kitty_black_v2_headset_left_zone, + &kraken_kitty_black_v2_headset_right_zone, + &kraken_kitty_black_v2_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken V3 HyperSense 1532:0533 | +| | +| Zone "Headset" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_v3_hs_zone = +{ + "Headset", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device kraken_v3_hs_device = +{ + "Razer Kraken V3 HyperSense", + RAZER_KRAKEN_V3_HYPERSENSE_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_NONE, + 0, + 1, + 1, + { + &kraken_v3_hs_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken V3 X 1532:0537 | +| | +| Zone "Headset" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_v3_x_zone = +{ + "Headset", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device kraken_v3_x_device = +{ + "Razer Kraken V3 X", + RAZER_KRAKEN_V3_X_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_NONE, + 0, + 1, + 1, + { + &kraken_v3_x_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken V3 1532:0549 | +| | +| Zone "Headset" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_v3_zone = +{ + "Headset", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device kraken_v3_device = +{ + "Razer Kraken V3", + RAZER_KRAKEN_V3_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_NONE, + 0, + 1, + 1, + { + &kraken_v3_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken Kitty V2 Pro 1532:0554 | +| | +| Zone "Left Cat Ear" | +| Single | +| 1 LED | +| Zone "Right Cat Ear" | +| Single | +| 1 LED | +| Zone "Left Logo" | +| Single | +| 1 LED | +| Zone "Right Logo" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_kitty_v2_pro_left_ear_zone = +{ + "Left Cat Ear", + ZONE_TYPE_SINGLE, + 1, + 1 +}; +static const razer_zone kraken_kitty_v2_pro_right_ear_zone = +{ + "Right Cat Ear", + ZONE_TYPE_SINGLE, + 1, + 1 +}; +static const razer_zone kraken_kitty_v2_pro_left_logo_zone = +{ + "Left Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; +static const razer_zone kraken_kitty_v2_pro_right_logo_zone = +{ + "Right Logo", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device kraken_kitty_v2_pro_device = +{ + "Razer Kraken Kitty V2 Pro", + RAZER_KRAKEN_KITTY_V2_PRO_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_NONE, + 0, + 1, + 4, + { + &kraken_kitty_v2_pro_left_ear_zone, + &kraken_kitty_v2_pro_right_ear_zone, + &kraken_kitty_v2_pro_left_logo_zone, + &kraken_kitty_v2_pro_right_logo_zone, + NULL, + NULL + }, + NULL +}; + + +/*-------------------------------------------------------------*\ +| Razer Kraken V4 Wired 1532:056B | +| | +| Zone "Headset" | +| Matrix | +| 9 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_v4_wired_zone = +{ + "Headset", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_device kraken_v4_wired_device = +{ + "Razer Kraken V4 (Wired)", + RAZER_KRAKEN_V4_WIRED_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_EXTENDED, + 0x60, + 1, + 9, + { + &kraken_v4_wired_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken V4 Wireless 1532:056C | +| | +| Zone "Headset" | +| Matrix | +| 9 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_v4_wireless_zone = +{ + "Headset", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_device kraken_v4_wireless_device = +{ + "Razer Kraken V4 (Wireless)", + RAZER_KRAKEN_V4_WIRELESS_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_EXTENDED, + 0x60, + 1, + 9, + { + &kraken_v4_wireless_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken Kitty V3 Pro Wired 1532:0587 | +| | +| Zone "Headset" | +| Linear | +| 9 LED | +| | +| Zone "Ears" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_kitty_v3_pro_wired_headset_zone = +{ + "Headset", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_zone kraken_kitty_v3_pro_wired_ears_zone = +{ + "Ears", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device kraken_kitty_v3_pro_wired_device = +{ + "Razer Kraken Kitty V3 Pro (Wired)", + RAZER_KRAKEN_KITTY_V3_PRO_WIRED_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_EXTENDED, + 0x60, + 1, + 10, + { + &kraken_kitty_v3_pro_wired_headset_zone, + &kraken_kitty_v3_pro_wired_ears_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Kraken Kitty V3 Pro Wireless 1532:0588 | +| | +| Zone "Headset" | +| Linear | +| 9 LED | +| | +| Zone "Ears" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone kraken_kitty_v3_pro_wireless_headset_zone = +{ + "Headset", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_zone kraken_kitty_v3_pro_wireless_ears_zone = +{ + "Ears", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device kraken_kitty_v3_pro_wireless_device = +{ + "Razer Kraken Kitty V3 Pro (Wireless)", + RAZER_KRAKEN_KITTY_V3_PRO_WIRELESS_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_EXTENDED, + 0x60, + 1, + 10, + { + &kraken_kitty_v3_pro_wireless_headset_zone, + &kraken_kitty_v3_pro_wireless_ears_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Tiamat 7.1 V2 | +| | +| Zone "Controller" | +| Linear | +| 15 LEDs | +| | +| Zone "Headset Left" | +| Single | +| 1 LED | +| | +| Zone "Headset Right" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone tiamat_71_v2_controller_zone = +{ + "Controller", + ZONE_TYPE_LINEAR, + 1, + 15 +}; + +static const razer_zone tiamat_71_v2_headset_left_zone = +{ + "Headset Left", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone tiamat_71_v2_headset_right_zone = +{ + "Headset Right", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device tiamat_71_v2_device = +{ + "Razer Tiamat 7.1 V2", + RAZER_TIAMAT_71_V2_PID, + DEVICE_TYPE_HEADSET, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 17, + { + &tiamat_71_v2_controller_zone, + &tiamat_71_v2_headset_left_zone, + &tiamat_71_v2_headset_right_zone, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------------------*\ +| OTHER | +\*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------*\ +| Razer Core | +| | +| Zone "Side Window Lights" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 8 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone core_side_zone = +{ + "Side Window Lights", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone core_led_strip_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 8 +}; + +static const razer_device core_device = +{ + "Razer Core", + RAZER_CORE_PID, + DEVICE_TYPE_GPU, + RAZER_MATRIX_TYPE_STANDARD, + 0x3F, + 1, + 9, + { + &core_side_zone, + &core_led_strip_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Core X | +| | +| Zone "Side Window Lights" | +| Single | +| 1 LED | +| | +| Zone "LED Strip" | +| Linear | +| 15 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone core_x_side_zone = +{ + "Side Window Lights", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_zone core_x_led_strip_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 15 +}; + +static const razer_device core_x_device = +{ + "Razer Core X", + RAZER_CORE_X_PID, + DEVICE_TYPE_GPU, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 16, + { + &core_x_side_zone, + &core_x_led_strip_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Chroma Mug Holder | +| | +| Zone "LED Strip" | +| Linear | +| 15 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone mug_holder_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 15 +}; + +static const razer_device mug_holder_device = +{ + "Razer Chroma Mug Holder", + RAZER_CHROMA_MUG_PID, + DEVICE_TYPE_ACCESSORY, + RAZER_MATRIX_TYPE_LINEAR, + 0x3F, + 1, + 15, + { + &mug_holder_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Chroma Addressable RGB Controller | +| | +| Zone "LED Strip" | +| Linear | +| 80 LEDs | +| | +| Zone "LED Strip" | +| Linear | +| 80 LEDs | +| | +| Zone "LED Strip" | +| Linear | +| 80 LEDs | +| | +| Zone "LED Strip" | +| Linear | +| 80 LEDs | +| | +| Zone "LED Strip" | +| Linear | +| 80 LEDs | +| | +| Zone "LED Strip" | +| Linear | +| 80 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone chromaargb_zone_1 = +{ + "Channel 1", + ZONE_TYPE_LINEAR, + 1, + 80 +}; + +static const razer_zone chromaargb_zone_2 = +{ + "Channel 2", + ZONE_TYPE_LINEAR, + 1, + 80 +}; + +static const razer_zone chromaargb_zone_3 = +{ + "Channel 3", + ZONE_TYPE_LINEAR, + 1, + 80 +}; + +static const razer_zone chromaargb_zone_4 = +{ + "Channel 4", + ZONE_TYPE_LINEAR, + 1, + 80 +}; + +static const razer_zone chromaargb_zone_5 = +{ + "Channel 5", + ZONE_TYPE_LINEAR, + 1, + 80 +}; + +static const razer_zone chromaargb_zone_6 = +{ + "Channel 6", + ZONE_TYPE_LINEAR, + 1, + 80 +}; + +static const razer_device chromaargb_device = +{ + "Razer Chroma Addressable RGB Controller", + RAZER_CHROMA_ADDRESSABLE_RGB_CONTROLLER_PID, + DEVICE_TYPE_LEDSTRIP, + RAZER_MATRIX_TYPE_EXTENDED_ARGB, + 0x3F, + 6, + 80, + { + &chromaargb_zone_1, + &chromaargb_zone_2, + &chromaargb_zone_3, + &chromaargb_zone_4, + &chromaargb_zone_5, + &chromaargb_zone_6 + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Chroma HDK | +| | +| Zone "LED Strip" | +| Linear | +| 16 LEDs | +| | +| Zone "LED Strip" | +| Linear | +| 16 LEDs | +| | +| Zone "LED Strip" | +| Linear | +| 16 LEDs | +| | +| Zone "LED Strip" | +| Linear | +| 16 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone chromahdk_zone_1 = +{ + "Channel 1", + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_zone chromahdk_zone_2 = +{ + "Channel 2", + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_zone chromahdk_zone_3 = +{ + "Channel 3", + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_zone chromahdk_zone_4 = +{ + "Channel 4", + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_device chromahdk_device = +{ + "Razer Chroma HDK", + RAZER_CHROMA_HDK_PID, + DEVICE_TYPE_LEDSTRIP, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 4, + 16, + { + &chromahdk_zone_1, + &chromahdk_zone_2, + &chromahdk_zone_3, + &chromahdk_zone_4, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Chroma PC Case Lighting Kit | +| | +| Zone "LED Strip" | +| Linear | +| 16 LEDs | +| | +| Zone "LED Strip" | +| Linear | +| 16 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone chroma_pc_case_lighting_kit_zone_1 = +{ + "Channel 1", + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_zone chroma_pc_case_lighting_kit_zone_2 = +{ + "Channel 2", + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_device chroma_pc_case_lighting_kit_device = +{ + "Razer Chroma PC Case Lighting Kit", + RAZER_CHROMA_PC_CASE_LIGHTING_KIT_PID, + DEVICE_TYPE_LEDSTRIP, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 2, + 16, + { + &chroma_pc_case_lighting_kit_zone_1, + &chroma_pc_case_lighting_kit_zone_2, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Base Station Chroma | +| | +| Zone "LED Strip" | +| Linear | +| 15 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone base_station_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 15 +}; + +static const razer_device base_station_device = +{ + "Razer Base Station Chroma", + RAZER_BASE_STATION_CHROMA_PID, + DEVICE_TYPE_HEADSET_STAND, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 15, + { + &base_station_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mouse Bungee V3 Chroma | +| | +| Zone "LED Strip" | +| Linear | +| 8 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone mouse_bungee_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 8 +}; + +static const razer_device mouse_bungee_device = +{ + "Razer Mouse Bungee V3 Chroma", + RAZER_MOUSE_BUNGEE_V3_CHROMA_PID, + DEVICE_TYPE_MOUSE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 8, + { + &mouse_bungee_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Base Station V2 Chroma | +| | +| Zone "LED Strip" | +| Linear | +| 8 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone base_station_v2_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 8 +}; + +static const razer_device base_station_v2_device = +{ + "Razer Base Station V2 Chroma", + RAZER_BASE_STATION_V2_CHROMA_PID, + DEVICE_TYPE_HEADSET_STAND, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 8, + { + &base_station_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Laptop Stand Chroma 1532:0F0D | +| | +| Zone "LED Strip" | +| Linear | +| 15 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone laptop_stand_chroma_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 15 +}; + +static const razer_device laptop_stand_chroma_device = +{ + "Razer Laptop Stand Chroma", + RAZER_LAPTOP_STAND_CHROMA_PID, + DEVICE_TYPE_LEDSTRIP, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 15, + { + &laptop_stand_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Laptop Stand Chroma V2 1532:0F2B | +| | +| Zone "LED Strip" | +| Linear | +| 15 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone laptop_stand_chroma_v2_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 15 +}; + +static const razer_device laptop_stand_chroma_v2_device = +{ + "Razer Laptop Stand Chroma V2", + RAZER_LAPTOP_STAND_CHROMA_V2_PID, + DEVICE_TYPE_LEDSTRIP, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 15, + { + &laptop_stand_chroma_v2_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mouse Dock Chroma 1532:007E | +| | +| Zone "Base" | +| Single | +| 1 LED | +\*-------------------------------------------------------------*/ +static const razer_zone mouse_dock_chroma_base_zone = +{ + "Base", + ZONE_TYPE_SINGLE, + 1, + 1 +}; + +static const razer_device mouse_dock_chroma_device = +{ + "Razer Mouse Dock Chroma", + RAZER_MOUSE_DOCK_CHROMA_PID, + DEVICE_TYPE_LIGHT, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 1, + { + &mouse_dock_chroma_base_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Mouse Dock Pro 1532:00A4 | +| | +| Zone "Base" | +| Linear | +| 9 LEDs | +\*-------------------------------------------------------------*/ +static const razer_zone mouse_dock_pro_base_zone = +{ + "Base", + ZONE_TYPE_LINEAR, + 1, + 9 +}; + +static const razer_device mouse_dock_pro_device = +{ + "Razer Mouse Dock Pro", + RAZER_MOUSE_DOCK_PRO_PID, + DEVICE_TYPE_LIGHT, + RAZER_MATRIX_TYPE_EXTENDED, + 0xFF, + 1, + 9, + { + &mouse_dock_pro_base_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Nommo Pro | +| | +| Zone "Left Speaker" | +| Linear | +| 8 LEDs | +| | +| Zone "Right Speaker" | +| Linear | +| 8 LEDs | +| | +\*-------------------------------------------------------------*/ +static const razer_zone nommo_pro_left_zone = +{ + "Left Speaker", + ZONE_TYPE_LINEAR, + 1, + 8 +}; + +static const razer_zone nommo_pro_right_zone = +{ + "Right Speaker", + ZONE_TYPE_LINEAR, + 1, + 8 +}; + +static const razer_device nommo_pro_device = +{ + "Razer Nommo Pro", + RAZER_NOMMO_PRO_PID, + DEVICE_TYPE_SPEAKER, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 2, + 8, + { + &nommo_pro_left_zone, + &nommo_pro_right_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Nommo Chroma | +| | +| Zone "Right Speaker" | +| Linear | +| 8 LEDs | +| | +| Zone "Left Speaker" | +| Linear | +| 8 LEDs | +| | +\*-------------------------------------------------------------*/ +static const razer_zone nommo_chroma_right_zone = +{ + "Right Speaker", + ZONE_TYPE_LINEAR, + 1, + 24 +}; + +static const razer_zone nommo_chroma_left_zone = +{ + "Left Speaker", + ZONE_TYPE_LINEAR, + 1, + 24 +}; + +static const razer_device nommo_chroma_device = +{ + "Razer Nommo Chroma", + RAZER_NOMMO_CHROMA_PID, + DEVICE_TYPE_SPEAKER, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 2, + 24, + { + &nommo_chroma_right_zone, + &nommo_chroma_left_zone, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Charging Pad Chroma | +| | +| Zone "LED Strip" | +| Linear | +| 10 LEDs | +| | +\*-------------------------------------------------------------*/ +static const razer_zone charging_pad_chroma_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 10 +}; + +static const razer_device charging_pad_chroma_device = +{ + "Razer Charging Pad Chroma", + RAZER_CHARGING_PAD_CHROMA_PID, + DEVICE_TYPE_ACCESSORY, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 1, + 10, + { + &charging_pad_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| O11 Dynamic - Razer Edition 1532:0F13 | +| | +| Zone "Case LEDs" | +| Matrix | +| 64 LEDs | +| | +\*-------------------------------------------------------------*/ +static const razer_zone o11_dynamic_case_zone = +{ + "Case LEDs", + ZONE_TYPE_LINEAR, + 4, + 16 +}; + +static const razer_device o11_dynamic_device = +{ + "Lian Li O11 Dynamic - Razer Edition", + RAZER_O11_DYNAMIC_PID, + DEVICE_TYPE_LEDSTRIP, + RAZER_MATRIX_TYPE_EXTENDED, + 0x1F, + 4, + 16, + { + &o11_dynamic_case_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Seiren Emote 1532:0F1B | +| | +| Zone "8-Bit LED Matrix" | +| Matrix | +| 64 LEDs | +| | +\*-------------------------------------------------------------*/ +static const razer_zone seiren_emote_zone = +{ + "8-Bit LED Matrix", + ZONE_TYPE_MATRIX, + 8, + 8 +}; + +static const razer_device seiren_emote_device = +{ + "Razer Seiren Emote", + RAZER_SEIREN_EMOTE_PID, + DEVICE_TYPE_MICROPHONE, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 4, + 16, + { + &seiren_emote_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Thunderbolt 4 Dock Chroma | +| | +| Zone "LED Strip" | +| Linear | +| 12 LEDs | +| | +\*-------------------------------------------------------------*/ +static const razer_zone thunderbolt_4_dock_chroma_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 12 +}; + +static const razer_device thunderbolt_4_dock_chroma_device = +{ + "Razer Thunderbolt 4 Dock Chroma", + RAZER_THUNDERBOLT_4_DOCK_CHROMA_PID, + DEVICE_TYPE_ACCESSORY, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 12, + { + &thunderbolt_4_dock_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Thunderbolt 5 Dock Chroma | +| | +| Zone "LED Strip" | +| Linear | +| 12 LEDs | +| | +\*-------------------------------------------------------------*/ +static const razer_zone thunderbolt_5_dock_chroma_zone = +{ + "LED Strip", + ZONE_TYPE_LINEAR, + 1, + 12 +}; + +static const razer_device thunderbolt_5_dock_chroma_device = +{ + "Razer Thunderbolt 5 Dock Chroma", + RAZER_THUNDERBOLT_5_DOCK_CHROMA_PID, + DEVICE_TYPE_ACCESSORY, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 12, + { + &thunderbolt_5_dock_chroma_zone, + NULL, + NULL, + NULL, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------*\ +| Razer Hanbo Chroma | +| | +| Zone "Pump" | +| Linear | +| 16 LEDs | +| | +| Zone "Fan 1" | +| Linear | +| 18 LEDs | +| | +| Zone "Fan 2" | +| Linear | +| 18 LEDs | +| | +| Zone "Fan 3" | +| Linear | +| 18 LEDs | +| | +\*-------------------------------------------------------------*/ +static const razer_zone hanbo_chroma_pump_zone = +{ + "Pump", + ZONE_TYPE_LINEAR, + 1, + 16 +}; + +static const razer_zone hanbo_chroma_fan_one_zone = +{ + "Fan 1", + ZONE_TYPE_LINEAR, + 1, + 18 +}; + +static const razer_zone hanbo_chroma_fan_two_zone = +{ + "Fan 2", + ZONE_TYPE_LINEAR, + 1, + 18 +}; + +static const razer_zone hanbo_chroma_fan_three_zone = +{ + "Fan 3", + ZONE_TYPE_LINEAR, + 1, + 18 +}; + +static const razer_device hanbo_chroma_device = +{ + "Razer Hanbo Chroma", + RAZER_HANBO_CHROMA_PID, + DEVICE_TYPE_COOLER, + RAZER_MATRIX_TYPE_EXTENDED, + 0x3F, + 1, + 70, + { + &hanbo_chroma_pump_zone, + &hanbo_chroma_fan_one_zone, + &hanbo_chroma_fan_two_zone, + &hanbo_chroma_fan_three_zone, + NULL, + NULL + }, + NULL +}; + +/*-------------------------------------------------------------------------*\ +| DEVICE MASTER LIST | +\*-------------------------------------------------------------------------*/ +const razer_device* razer_device_list[] = +{ +/*-----------------------------------------------------------------*\ +| KEYBOARDS | +\*-----------------------------------------------------------------*/ + &blackwidow_2019_device, + &blackwidow_chroma_device, + &blackwidow_chroma_overwatch_device, + &blackwidow_chroma_te_device, + &blackwidow_chroma_v2_device, + &blackwidow_elite_device, + &blackwidow_v3_device, + &blackwidow_v3_pro_wired_device, + &blackwidow_v3_pro_wireless_device, + &blackwidow_v3_pro_bluetooth_device, + &blackwidow_v3_tkl_device, + &blackwidow_v3_mini_wired_device, + &blackwidow_v3_mini_wireless_device, + &blackwidow_v4_device, + &blackwidow_v4_pro_device, + &blackwidow_v4_pro_75_wired_device, + &blackwidow_v4_pro_75_wireless_device, + &blackwidow_v4_75_wired_device, + &blackwidow_v4_x_device, + &blackwidow_v4_tkl_wired_device, + &blackwidow_v4_tkl_wireless_device, + &blackwidow_v4_lowprofile_tkl_wired_device, + &blackwidow_v4_lowprofile_tkl_wireless_device, + &blackwidow_x_chroma_device, + &blackwidow_x_chroma_te_device, + &cynosa_chroma_device, + &cynosa_v2_device, + &cynosa_lite_device, + &deathstalker_chroma_device, + &deathstalker_v2_device, + &deathstalker_v2_pro_tkl_wired_device, + &deathstalker_v2_pro_tkl_wireless_device, + &deathstalker_v2_pro_wired_device, + &deathstalker_v2_pro_wireless_device, + &huntsman_device, + &huntsman_elite_device, + &huntsman_mini_device, + &huntsman_mini_analog_device, + &huntsman_te_device, + &huntsman_v2_device, + &huntsman_v2_analog_device, + &huntsman_v2_tkl_device, + &huntsman_v3_pro_device, + &huntsman_v3_pro_tkl_device, + &ornata_chroma_device, + &ornata_chroma_v2_device, + &ornata_v3_device, + &ornata_v3_rev2_device, + &ornata_v3_tkl_device, + &ornata_v3_x_device, + &ornata_v3_x_v2_device, +/*-----------------------------------------------------------------*\ +| LAPTOPS | +\*-----------------------------------------------------------------*/ + &blade_2016_device, + &blade_late_2016_device, + &blade_15_2018_advanced_device, + &blade_15_2018_base_device, + &blade_15_2018_mercury_device, + &blade_15_2019_advanced_device, + &blade_15_2019_base_device, + &blade_15_2019_mercury_device, + &blade_15_2019_studio_device, + &blade_15_2020_advanced_device, + &blade_15_2020_base_device, + &blade_late_2020_device, + &blade_15_2021_advanced_device, + &blade_15_2021_base_device, + &blade_15_2021_base_v2_device, + &blade_15_late_2021_advanced_device, + &blade_14_2021_device, + &blade_14_2022_device, + &blade_14_2023_device, + &blade_15_2022_device, + &book_13_2020_device, + &blade_pro_2016_device, + &blade_pro_2017_device, + &blade_pro_2017_fullhd_device, + &blade_pro_2019_device, + &blade_pro_late_2019_device, + &blade_pro_17_2020_device, + &blade_pro_17_2021_device, + &blade_stealth_2016_device, + &blade_stealth_late_2016_device, + &blade_stealth_2017_device, + &blade_stealth_late_2017_device, + &blade_stealth_2019_device, + &blade_stealth_late_2019_device, + &blade_stealth_2020_device, + &blade_stealth_late_2020_device, +/*-----------------------------------------------------------------*\ +| MICE | +\*-----------------------------------------------------------------*/ + &abyssus_elite_dva_edition_device, + &abyssus_essential_device, + &basilisk_device, + &basilisk_essential_device, + &basilisk_ultimate_wired_device, + &basilisk_ultimate_wireless_device, + &basilisk_v2_device, + &basilisk_v3_device, + &basilisk_v3_35k_device, + &basilisk_v3_pro_wired_device, + &basilisk_v3_pro_wireless_device, + &basilisk_v3_pro_35k_wired_device, + &basilisk_v3_pro_35k_wireless_device, + &basilisk_v3_pro_35k_pg_wired_device, + &basilisk_v3_pro_35k_pg_wireless_device, + &basilisk_v3_pro_35k_pg_bluetooth_device, + &basilisk_v3_pro_bluetooth_device, + &basilisk_v3_x_hyperspeed_device, + &cobra_device, + &cobra_pro_wired_device, + &cobra_pro_wireless_device, + &deathadder_chroma_device, + &deathadder_elite_device, + &deathadder_essential_device, + &deathadder_essential_v2_device, + &deathadder_essential_white_edition_device, + &deathadder_v2_device, + &deathadder_v2_mini_device, + &deathadder_v2_pro_wired_device, + &deathadder_v2_pro_wireless_device, + &diamondback_chroma_device, + &lancehead_2017_wired_device, + &lancehead_2017_wireless_device, + &lancehead_2019_wired_device, + &lancehead_2019_wireless_device, + &lancehead_te_device, + &mamba_2012_wired_device, + &mamba_2012_wireless_device, + &mamba_2015_wired_device, + &mamba_2015_wireless_device, + &mamba_2018_wired_device, + &mamba_2018_wireless_device, + &mamba_te_device, + &mamba_elite_device, + &mamba_hyperflux_device, + &naga_chroma_device, + &naga_classic_device, + &naga_epic_chroma_device, + &naga_hex_v2_device, + &naga_left_handed_device, + &naga_trinity_device, + &naga_pro_wired_device, + &naga_pro_wireless_device, + &naga_pro_v2_wired_device, + &naga_pro_v2_wireless_device, + &viper_8khz_device, + &viper_mini_device, + &viper_ultimate_wired_device, + &viper_ultimate_wireless_device, + &viper_device, +/*-----------------------------------------------------------------*\ +| KEYPADS | +\*-----------------------------------------------------------------*/ + &orbweaver_chroma_device, + &tartarus_chroma_device, + &tartarus_pro_device, + &tartarus_v2_device, +/*-----------------------------------------------------------------*\ +| MOUSEMATS | +\*-----------------------------------------------------------------*/ + &firefly_device, + &firefly_hyperflux_device, + &firefly_v2_device, + &firefly_v2_pro_device, + &goliathus_chroma_3xl_device, + &goliathus_device, + &goliathus_extended_device, + &strider_chroma_device, +/*-----------------------------------------------------------------*\ +| HEADSETS | +\*-----------------------------------------------------------------*/ + &kraken_chroma_device, + &kraken_v2_device, + &kraken_ultimate_device, + &kraken_kitty_device, + &kraken_kitty_black_v2_device, + &kraken_v3_hs_device, + &kraken_v3_x_device, + &kraken_v3_device, + &kraken_kitty_v2_pro_device, + &kraken_v4_wired_device, + &kraken_v4_wireless_device, + &kraken_kitty_v3_pro_wired_device, + &kraken_kitty_v3_pro_wireless_device, + &tiamat_71_v2_device, +/*-----------------------------------------------------------------*\ +| OTHER | +\*-----------------------------------------------------------------*/ + &base_station_device, + &base_station_v2_device, + &mouse_bungee_device, + &charging_pad_chroma_device, + &chromaargb_device, + &chromahdk_device, + &chroma_pc_case_lighting_kit_device, + &core_device, + &core_x_device, + &laptop_stand_chroma_device, + &laptop_stand_chroma_v2_device, + &leviathan_v2_device, + &leviathan_v2x_device, + &mug_holder_device, + &mouse_dock_chroma_device, + &mouse_dock_pro_device, + &nommo_chroma_device, + &nommo_pro_device, + &o11_dynamic_device, + &seiren_emote_device, + &thunderbolt_4_dock_chroma_device, + &thunderbolt_5_dock_chroma_device, + &hanbo_chroma_device +}; + +const unsigned int RAZER_NUM_DEVICES = (sizeof(razer_device_list) / sizeof(razer_device_list[ 0 ])); +const razer_device** device_list = razer_device_list; diff --git a/Controllers/RazerController/RazerDevices.h b/Controllers/RazerController/RazerDevices.h new file mode 100644 index 0000000..c849a11 --- /dev/null +++ b/Controllers/RazerController/RazerDevices.h @@ -0,0 +1,308 @@ +/*---------------------------------------------------------*\ +| RazerDevices.h | +| | +| Device list for Razer devices | +| | +| Adam Honse (CalcProgrammer1) 04 Sep 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "RGBControllerKeyNames.h" +#include "RazerController.h" +#include "KeyboardLayoutManager.h" + +/*-----------------------------------------------------*\ +| Razer vendor ID | +\*-----------------------------------------------------*/ +#define RAZER_VID 0x1532 +#define RAZER_BLUETOOTH_VID 0x068E + +/*-----------------------------------------------------*\ +| Razer maximum zones | +| If a new device has more than RAZER_MAX_ZONES, | +| increment RAZER_MAX_ZONES and update all device | +| tables accordingly. | +\*-----------------------------------------------------*/ +#define RAZER_MAX_ZONES 6 + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +| List taken from OpenRazer | +| Non-RGB keyboards were omitted from this list | +\*-----------------------------------------------------*/ +#define RAZER_BLACKWIDOW_2019_PID 0x0241 +#define RAZER_BLACKWIDOW_CHROMA_PID 0x0203 +#define RAZER_BLACKWIDOW_CHROMA_TE_PID 0x0209 +#define RAZER_BLACKWIDOW_CHROMA_V2_PID 0x0221 +#define RAZER_BLACKWIDOW_ELITE_PID 0x0228 +#define RAZER_BLACKWIDOW_ESSENTIAL_PID 0x0237 +#define RAZER_BLACKWIDOW_LITE_PID 0x0235 +#define RAZER_BLACKWIDOW_OVERWATCH_PID 0x0211 +#define RAZER_BLACKWIDOW_V3_PID 0x024E +#define RAZER_BLACKWIDOW_V3_MINI_WIRED_PID 0x0258 +#define RAZER_BLACKWIDOW_V3_MINI_WIRELESS_PID 0x0271 +#define RAZER_BLACKWIDOW_V3_PRO_WIRED_PID 0x025A +#define RAZER_BLACKWIDOW_V3_PRO_BLUETOOTH_PID 0x025B +#define RAZER_BLACKWIDOW_V3_PRO_WIRELESS_PID 0x025C +#define RAZER_BLACKWIDOW_V3_TKL_PID 0x0A24 +#define RAZER_BLACKWIDOW_V4_PID 0x0287 +#define RAZER_BLACKWIDOW_V4_PRO_PID 0x028D +#define RAZER_BLACKWIDOW_V4_X_PID 0x0293 +#define RAZER_BLACKWIDOW_V4_PRO_75_WIRED_PID 0x02B3 +#define RAZER_BLACKWIDOW_V4_PRO_75_WIRELESS_PID 0x02B4 +#define RAZER_BLACKWIDOW_V4_75_WIRED_PID 0x02A5 +#define RAZER_BLACKWIDOW_V4_TKL_WIRED_PID 0x02D7 +#define RAZER_BLACKWIDOW_V4_TKL_WIRELESS_PID 0x02D5 +#define RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRED_PID 0x02D4 +#define RAZER_BLACKWIDOW_V4_LOWPROFILE_TKL_WIRELESS_PID 0x02D2 +#define RAZER_BLACKWIDOW_X_CHROMA_PID 0x0216 +#define RAZER_BLACKWIDOW_X_CHROMA_TE_PID 0x021A +#define RAZER_BLADE_2016_PID 0x020F +#define RAZER_BLADE_LATE_2016_PID 0x0224 +#define RAZER_BLADE_2018_ADVANCED_PID 0x0233 +#define RAZER_BLADE_2018_BASE_PID 0x023B +#define RAZER_BLADE_2018_MERCURY_PID 0x0240 +#define RAZER_BLADE_2019_ADVANCED_PID 0x023A +#define RAZER_BLADE_2019_BASE_PID 0x0246 +#define RAZER_BLADE_2019_MERCURY_PID 0x0245 +#define RAZER_BLADE_2019_STUDIO_PID 0x024D +#define RAZER_BLADE_2020_ADVANCED_PID 0x0253 +#define RAZER_BLADE_2020_BASE_PID 0x0255 +#define RAZER_BLADE_LATE_2020_PID 0x0268 +#define RAZER_BLADE_2021_ADVANCED_PID 0x026D +#define RAZER_BLADE_2021_BASE_PID 0x026F +#define RAZER_BLADE_2021_BASE_V2_PID 0x027A +#define RAZER_BLADE_LATE_2021_ADVANCED_PID 0x0276 + +#define RAZER_BLADE_14_2021_PID 0x0270 +#define RAZER_BLADE_14_2022_PID 0x028C +#define RAZER_BLADE_14_2023_PID 0x029D +#define RAZER_BLADE_15_2022_PID 0x028A + +#define RAZER_BLADE_PRO_2016_PID 0x0210 +#define RAZER_BLADE_PRO_2017_PID 0x0225 +#define RAZER_BLADE_PRO_2017_FULLHD_PID 0x022F +#define RAZER_BLADE_PRO_2019_PID 0x0234 +#define RAZER_BLADE_PRO_LATE_2019_PID 0x024C +#define RAZER_BLADE_PRO_17_2020_PID 0x0256 +#define RAZER_BLADE_PRO_17_2021_PID 0x0279 + +#define RAZER_BLADE_STEALTH_2016_PID 0x0205 +#define RAZER_BLADE_STEALTH_LATE_2016_PID 0x0220 +#define RAZER_BLADE_STEALTH_2017_PID 0x022D +#define RAZER_BLADE_STEALTH_LATE_2017_PID 0x0232 +#define RAZER_BLADE_STEALTH_2019_PID 0x0239 +#define RAZER_BLADE_STEALTH_LATE_2019_PID 0x024A +#define RAZER_BLADE_STEALTH_2020_PID 0x0252 +#define RAZER_BLADE_STEALTH_LATE_2020_PID 0x0259 + +#define RAZER_BOOK_13_2020_PID 0x026A + +#define RAZER_CYNOSA_CHROMA_PID 0x022A +#define RAZER_CYNOSA_LITE_PID 0x023F +#define RAZER_CYNOSA_V2_PID 0x025E +#define RAZER_DEATHSTALKER_CHROMA_PID 0x0204 +#define RAZER_DEATHSTALKER_V2_PID 0x0295 +#define RAZER_DEATHSTALKER_V2_PRO_TKL_WIRELESS_PID 0x0296 +#define RAZER_DEATHSTALKER_V2_PRO_TKL_WIRED_PID 0x0298 +#define RAZER_DEATHSTALKER_V2_PRO_TKL_BT_PID 0x0297 +#define RAZER_DEATHSTALKER_V2_PRO_WIRELESS_PID 0x0290 +#define RAZER_DEATHSTALKER_V2_PRO_WIRED_PID 0x0292 +#define RAZER_HUNTSMAN_ELITE_PID 0x0226 +#define RAZER_HUNTSMAN_PID 0x0227 +#define RAZER_HUNTSMAN_MINI_PID 0x0257 +#define RAZER_HUNTSMAN_MINI_ANALOG_PID 0x0282 +#define RAZER_HUNTSMAN_TE_PID 0x0243 +#define RAZER_HUNTSMAN_V2_ANALOG_PID 0x0266 +#define RAZER_HUNTSMAN_V2_TKL_PID 0x026B +#define RAZER_HUNTSMAN_V2_PID 0x026C +#define RAZER_HUNTSMAN_V3_PRO_PID 0x02A6 +#define RAZER_HUNTSMAN_V3_PRO_TKL_WHITE_PID 0x02A7 +#define RAZER_ORBWEAVER_CHROMA_PID 0x0207 +#define RAZER_ORNATA_CHROMA_PID 0x021E +#define RAZER_ORNATA_CHROMA_V2_PID 0x025D +#define RAZER_ORNATA_V3_PID 0x028F +#define RAZER_ORNATA_V3_REV2_PID 0x02A1 +#define RAZER_ORNATA_V3_TKL_PID 0x02A3 +#define RAZER_ORNATA_V3_X_PID 0x0294 +#define RAZER_ORNATA_V3_X_REV2_PID 0x02A2 +#define RAZER_TARTARUS_CHROMA_PID 0x0208 +#define RAZER_TARTARUS_PRO_PID 0x0244 +#define RAZER_TARTARUS_V2_PID 0x022B + +/*-----------------------------------------------------*\ +| Mouse product IDs | +| List taken from OpenRazer | +\*-----------------------------------------------------*/ +#define RAZER_ABYSSUS_1800_PID 0x0020 +#define RAZER_ABYSSUS_2000_PID 0x005E +#define RAZER_ABYSSUS_ELITE_DVA_EDITION_PID 0x006A +#define RAZER_ABYSSUS_ESSENTIAL_PID 0x006B +#define RAZER_ABYSSUS_PID 0x0042 +#define RAZER_ABYSSUS_V2_PID 0x005B +#define RAZER_ATHERIS_RECEIVER_PID 0x0062 +#define RAZER_BASILISK_PID 0x0064 +#define RAZER_BASILISK_ESSENTIAL_PID 0x0065 +#define RAZER_BASILISK_ULTIMATE_WIRED_PID 0x0086 +#define RAZER_BASILISK_ULTIMATE_WIRELESS_PID 0x0088 +#define RAZER_BASILISK_X_HYPERSPEED_PID 0x0083 +#define RAZER_BASILISK_V2_PID 0x0085 +#define RAZER_BASILISK_V3_PID 0x0099 +#define RAZER_BASILISK_V3_35K_PID 0x00CB +#define RAZER_BASILISK_V3_PRO_WIRED_PID 0x00AA +#define RAZER_BASILISK_V3_PRO_WIRELESS_PID 0x00AB +#define RAZER_BASILISK_V3_PRO_35K_WIRED_PID 0x00CC +#define RAZER_BASILISK_V3_PRO_35K_WIRELESS_PID 0x00CD +#define RAZER_BASILISK_V3_PRO_35K_PG_WIRED_PID 0x00D6 +#define RAZER_BASILISK_V3_PRO_35K_PG_WIRELESS_PID 0x00D7 +#define RAZER_BASILISK_V3_PRO_35K_PG_BLUETOOTH_PID 0x00D8 +#define RAZER_BASILISK_V3_PRO_BLUETOOTH_PID 0x00AC +#define RAZER_BASILISK_V3_X_HYPERSPEED_PID 0x00B9 +#define RAZER_COBRA_PID 0x00A3 +#define RAZER_COBRA_PRO_WIRED_PID 0x00AF +#define RAZER_COBRA_PRO_WIRELESS_PID 0x00B0 +#define RAZER_DEATHADDER_1800_PID 0x0038 +#define RAZER_DEATHADDER_2000_PID 0x004F +#define RAZER_DEATHADDER_2013_PID 0x0037 +#define RAZER_DEATHADDER_3_5G_PID 0x0016 +#define RAZER_DEATHADDER_3500_PID 0x0054 +#define RAZER_DEATHADDER_CHROMA_PID 0x0043 +#define RAZER_DEATHADDER_ELITE_PID 0x005C +#define RAZER_DEATHADDER_ESSENTIAL_PID 0x006E +#define RAZER_DEATHADDER_ESSENTIAL_V2_PID 0x0098 +#define RAZER_DEATHADDER_ESSENTIAL_WHITE_EDITION_PID 0x0071 +#define RAZER_DEATHADDER_V2_MINI_PID 0x008C +#define RAZER_DEATHADDER_V2_PID 0x0084 +#define RAZER_DEATHADDER_V2_PRO_WIRED_PID 0x007C +#define RAZER_DEATHADDER_V2_PRO_WIRELESS_PID 0x007D +#define RAZER_DIAMONDBACK_CHROMA_PID 0x004C +#define RAZER_IMPERATOR_PID 0x002F +#define RAZER_LANCEHEAD_TE_WIRED_PID 0x0060 +#define RAZER_LANCEHEAD_2017_WIRED_PID 0x0059 +#define RAZER_LANCEHEAD_2017_WIRELESS_PID 0x005A +#define RAZER_LANCEHEAD_2019_WIRED_PID 0x0070 +#define RAZER_LANCEHEAD_2019_WIRELESS_PID 0x006F +#define RAZER_MAMBA_2012_WIRED_PID 0x0024 +#define RAZER_MAMBA_2012_WIRELESS_PID 0x0025 +#define RAZER_MAMBA_2015_WIRED_PID 0x0044 +#define RAZER_MAMBA_2015_WIRELESS_PID 0x0045 +#define RAZER_MAMBA_2018_WIRED_PID 0x0073 +#define RAZER_MAMBA_2018_WIRELESS_PID 0x0072 +#define RAZER_MAMBA_ELITE_PID 0x006C +#define RAZER_MAMBA_HYPERFLUX_PID 0x0069 +#define RAZER_MAMBA_TE_PID 0x0046 +#define RAZER_NAGA_2012_PID 0x002E +#define RAZER_NAGA_2014_PID 0x0040 +#define RAZER_NAGA_CHROMA_PID 0x0053 +#define RAZER_NAGA_CLASSIC_PID 0x0093 +#define RAZER_NAGA_EPIC_CHROMA_DOCK_PID 0x003F +#define RAZER_NAGA_EPIC_CHROMA_PID 0x003E +#define RAZER_NAGA_HEX_PID 0x0041 +#define RAZER_NAGA_HEX_RED_PID 0x0036 +#define RAZER_NAGA_HEX_V2_PID 0x0050 +#define RAZER_NAGA_LEFT_HANDED_PID 0x008D +#define RAZER_NAGA_TRINITY_PID 0x0067 +#define RAZER_NAGA_PRO_WIRED_PID 0x008F +#define RAZER_NAGA_PRO_WIRELESS_PID 0x0090 +#define RAZER_NAGA_PRO_V2_WIRED_PID 0x00A7 +#define RAZER_NAGA_PRO_V2_WIRELESS_PID 0x00A8 +#define RAZER_OROCHI_2011_PID 0x0013 +#define RAZER_OROCHI_2013_PID 0x0039 +#define RAZER_OROCHI_CHROMA_PID 0x0048 +#define RAZER_OUROBOROS_PID 0x0032 +#define RAZER_TAIPAN_PID 0x0034 +#define RAZER_VIPER_8KHZ_PID 0x0091 +#define RAZER_VIPER_MINI_PID 0x008A +#define RAZER_VIPER_PID 0x0078 +#define RAZER_VIPER_ULTIMATE_WIRED_PID 0x007A +#define RAZER_VIPER_ULTIMATE_WIRELESS_PID 0x007B + +/*-----------------------------------------------------*\ +| Headset product IDs | +\*-----------------------------------------------------*/ +#define RAZER_KRAKEN_CLASSIC_ALT_PID 0x0506 +#define RAZER_KRAKEN_CLASSIC_PID 0x0501 +#define RAZER_KRAKEN_KITTY_EDITION_PID 0x0F19 +#define RAZER_KRAKEN_KITTY_BLACK_EDITION_PID 0x0F21 +#define RAZER_KRAKEN_PID 0x0504 +#define RAZER_KRAKEN_ULTIMATE_PID 0x0527 +#define RAZER_KRAKEN_V2_PID 0x0510 +#define RAZER_KRAKEN_KITTY_BLACK_EDITION_V2_PID 0x0560 +#define RAZER_KRAKEN_V3_HYPERSENSE_PID 0x0533 +#define RAZER_KRAKEN_V3_X_PID 0x0537 +#define RAZER_KRAKEN_V3_PID 0x0549 +#define RAZER_KRAKEN_KITTY_V2_PRO_PID 0x0554 +#define RAZER_KRAKEN_V4_WIRED_PID 0x056B +#define RAZER_KRAKEN_V4_WIRELESS_PID 0x056C +#define RAZER_KRAKEN_KITTY_V3_PRO_WIRED_PID 0x0587 +#define RAZER_KRAKEN_KITTY_V3_PRO_WIRELESS_PID 0x0588 +#define RAZER_TIAMAT_71_V2_PID 0x0F03 + +/*-----------------------------------------------------*\ +| Accessory product IDs | +| List taken from OpenRazer | +\*-----------------------------------------------------*/ +#define RAZER_BASE_STATION_CHROMA_PID 0x0F08 +#define RAZER_BASE_STATION_V2_CHROMA_PID 0x0F20 +#define RAZER_CHARGING_PAD_CHROMA_PID 0x0F26 +#define RAZER_CHROMA_ADDRESSABLE_RGB_CONTROLLER_PID 0x0F1F +#define RAZER_CHROMA_HDK_PID 0x0F09 +#define RAZER_CHROMA_MUG_PID 0x0F07 +#define RAZER_CHROMA_PC_CASE_LIGHTING_KIT_PID 0x0F0E +#define RAZER_CORE_PID 0x0215 +#define RAZER_CORE_X_PID 0x0F1A +#define RAZER_FIREFLY_HYPERFLUX_PID 0x0068 +#define RAZER_FIREFLY_PID 0x0C00 +#define RAZER_FIREFLY_V2_PID 0x0C04 +#define RAZER_FIREFLY_V2_PRO_PID 0x0C08 +#define RAZER_GOLIATHUS_CHROMA_EXTENDED_PID 0x0C02 +#define RAZER_GOLIATHUS_CHROMA_PID 0x0C01 +#define RAZER_GOLIATHUS_CHROMA_3XL_PID 0x0C06 +#define RAZER_LAPTOP_STAND_CHROMA_PID 0x0F0D +#define RAZER_LAPTOP_STAND_CHROMA_V2_PID 0x0F2B +#define RAZER_LEVIATHAN_V2_PID 0x0532 +#define RAZER_LEVIATHAN_V2X_PID 0x054A +#define RAZER_MOUSE_BUNGEE_V3_CHROMA_PID 0x0F1D +#define RAZER_MOUSE_DOCK_CHROMA_PID 0x007E +#define RAZER_MOUSE_DOCK_PRO_PID 0x00A4 +#define RAZER_NOMMO_CHROMA_PID 0x0517 +#define RAZER_NOMMO_PRO_PID 0x0518 +#define RAZER_O11_DYNAMIC_PID 0x0F13 +#define RAZER_SEIREN_EMOTE_PID 0x0F1B +#define RAZER_STRIDER_CHROMA_PID 0x0C05 +#define RAZER_THUNDERBOLT_4_DOCK_CHROMA_PID 0x0F21 +#define RAZER_THUNDERBOLT_5_DOCK_CHROMA_PID 0x0F52 +#define RAZER_HANBO_CHROMA_PID 0x0F35 + +typedef struct +{ + std::string name; + unsigned int type; + unsigned int rows; + unsigned int cols; +} razer_zone; + +typedef struct +{ + std::string name; + unsigned short pid; + device_type type; + unsigned char matrix_type; + unsigned char transaction_id; + unsigned int rows; + unsigned int cols; + const razer_zone* zones[RAZER_MAX_ZONES]; + keyboard_keymap_overlay_values* layout; +} razer_device; + +/*-----------------------------------------------------*\ +| These constant values are defined in RazerDevices.cpp | +\*-----------------------------------------------------*/ +extern const unsigned int RAZER_NUM_DEVICES; +extern const razer_device** device_list; diff --git a/Controllers/RazerController/RazerHanboController/RGBController_RazerHanbo.cpp b/Controllers/RazerController/RazerHanboController/RGBController_RazerHanbo.cpp new file mode 100644 index 0000000..3d6f3ed --- /dev/null +++ b/Controllers/RazerController/RazerHanboController/RGBController_RazerHanbo.cpp @@ -0,0 +1,189 @@ +/*---------------------------------------------------------*\ +| RGBController_RazerHanbo.cpp | +| | +| RGBController for Razer Hanbo devices | +| | +| Joseph East (dripsnek) 12 Apr 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RazerHanbo.h" +#include "RazerDevices.h" + +/**------------------------------------------------------------------*\ + @name Razer Hanbo Chroma + @category Cooler + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRazerHanboControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RazerHanbo::RGBController_RazerHanbo(RazerHanboController* controller_ptr) +{ + controller = controller_ptr; + name = controller->GetName(); + vendor = "Razer"; + type = controller->GetDeviceType(); + description = "Razer Hanbo Device"; + location = controller->GetDeviceLocation(); + version = controller->GetFirmwareString(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = RAZER_HANBO_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = MIN_BRIGHTNESS; + Direct.brightness_max = MAX_BRIGHTNESS; + Direct.brightness = MAX_BRIGHTNESS/2; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = RAZER_HANBO_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = RAZER_HANBO_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = 0; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + local_mode = RAZER_HANBO_MODE_DIRECT; + + SetupZones(); +} + +RGBController_RazerHanbo::~RGBController_RazerHanbo() +{ + delete controller; +} + +void RGBController_RazerHanbo::SetupZones() +{ + unsigned int device_index = controller->GetDeviceIndex(); + + /*---------------------------------------------------------*\ + | Fill in zone information based on device table | + \*---------------------------------------------------------*/ + for(unsigned int zone_id = 0; zone_id < RAZER_MAX_ZONES; zone_id++) + { + if(device_list[device_index]->zones[zone_id] != NULL) + { + zone new_zone; + + new_zone.name = device_list[device_index]->zones[zone_id]->name; + new_zone.type = device_list[device_index]->zones[zone_id]->type; + new_zone.leds_count = device_list[device_index]->zones[zone_id]->rows * device_list[device_index]->zones[zone_id]->cols; + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + if(new_zone.type == ZONE_TYPE_MATRIX) + { + matrix_map_type * new_map = new matrix_map_type; + + new_zone.matrix_map = new_map; + new_map->height = device_list[device_index]->zones[zone_id]->rows; + new_map->width = device_list[device_index]->zones[zone_id]->cols; + new_map->map = new unsigned int[new_map->height * new_map->width]; + + for(unsigned int y = 0; y < new_map->height; y++) + { + for(unsigned int x = 0; x < new_map->width; x++) + { + new_map->map[(y * new_map->width) + x] = (y * new_map->width) + x; + } + } + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + } + } + + for(unsigned int zone_id = 0; zone_id < zones.size(); zone_id++) + { + for(unsigned int row_id = 0; row_id < device_list[device_index]->zones[zone_id]->rows; row_id++) + { + for(unsigned int col_id = 0; col_id < device_list[device_index]->zones[zone_id]->cols; col_id++) + { + led* new_led = new led(); + + new_led->name = device_list[device_index]->zones[zone_id]->name; + + if(zones[zone_id].leds_count > 1) + { + new_led->name.append(" LED "); + new_led->name.append(std::to_string(col_id + 1)); + } + + leds.push_back(*new_led); + } + } + } + + SetupColors(); +} + +void RGBController_RazerHanbo::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RazerHanbo::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(PUMP); + UpdateZoneLEDs(FAN1); + UpdateZoneLEDs(FAN2); + UpdateZoneLEDs(FAN3); +} + +/*---------------------------------------------------------*\ +| The Hanbo command set is arranged in terms of zones. | +| Transactions are straight forward when grouped this way. | +\*---------------------------------------------------------*/ + +void RGBController_RazerHanbo::UpdateZoneLEDs(int zoneid) +{ + controller->SetZoneLeds(zoneid, this->zones[zoneid]); +} + +void RGBController_RazerHanbo::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RazerHanbo::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case RAZER_HANBO_MODE_DIRECT: + if(local_mode != RAZER_HANBO_MODE_DIRECT) + controller->SetDirectMode(); + break; + + case RAZER_HANBO_MODE_OFF: + controller->SetModeOff(); + break; + + case RAZER_HANBO_MODE_SPECTRUM_CYCLE: + controller->SetModeSpectrumCycle(); + break; + } + + local_mode = modes[active_mode].value; +} diff --git a/Controllers/RazerController/RazerHanboController/RGBController_RazerHanbo.h b/Controllers/RazerController/RazerHanboController/RGBController_RazerHanbo.h new file mode 100644 index 0000000..69e3505 --- /dev/null +++ b/Controllers/RazerController/RazerHanboController/RGBController_RazerHanbo.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------*\ +| RGBController_Razer.h | +| | +| RGBController for Razer Hanbo devices | +| | +| Joseph East (dripsnek) 12 Apr 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RazerHanboController.h" + +enum +{ + RAZER_HANBO_MODE_DIRECT, + RAZER_HANBO_MODE_OFF, + RAZER_HANBO_MODE_SPECTRUM_CYCLE, +}; + +class RGBController_RazerHanbo : public RGBController +{ +public: + RGBController_RazerHanbo(RazerHanboController* controller_ptr); + ~RGBController_RazerHanbo(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RazerHanboController* controller; + int local_mode; +}; diff --git a/Controllers/RazerController/RazerHanboController/RazerHanboController.cpp b/Controllers/RazerController/RazerHanboController/RazerHanboController.cpp new file mode 100644 index 0000000..b74c589 --- /dev/null +++ b/Controllers/RazerController/RazerHanboController/RazerHanboController.cpp @@ -0,0 +1,262 @@ +/*---------------------------------------------------------*\ +| RazerHanboController.cpp | +| | +| Driver for Razer Hanbo devices | +| | +| Joseph East (dripsnek) 12 Apr 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RazerHanboController.h" +#include "RazerDevices.h" + +using namespace std::chrono_literals; + +RazerHanboController::RazerHanboController(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + dev_pid = pid; + location = path; + name = dev_name; + device_index = 0; + + for(unsigned int i = 0; i < RAZER_NUM_DEVICES; i++) + { + if(device_list[i]->pid == dev_pid) + { + device_index = i; + } + } + + GetFirmware(); + SetDirectMode(); +} + +RazerHanboController::~RazerHanboController() +{ + hid_close(dev); +} + +unsigned int RazerHanboController::GetDeviceIndex() +{ + return(device_index); +} + +device_type RazerHanboController::GetDeviceType() +{ + return(device_list[device_index]->type); +} + +std::string RazerHanboController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RazerHanboController::GetFirmwareString() +{ + return firmware_version; +} + +std::string RazerHanboController::GetSerialString() +{ + return serial_string; +} + +std::string RazerHanboController::GetName() +{ + return(name); +} + +void RazerHanboController::SetDirectMode() +{ + razer_hanbo_report request_report = razer_hanbo_create_report(0x82); + razer_hanbo_report response_report = razer_hanbo_create_report(0x00); + + /*---------------------------------------*\ + | Take the one request and transform as | + | appropriate for the sequence. | + \*---------------------------------------*/ + request_report.arguments[0] = 0x01; + UsbSend(&request_report); + UsbReceive(&response_report); + + request_report.header[1] = 0x80; + request_report.arguments[0] = 0x01; + request_report.arguments[1] = 0x00; + request_report.arguments[2] = 0x01; + UsbSend(&request_report); + UsbReceive(&response_report); + + request_report.header[1] = 0x80; + request_report.arguments[0] = 0x01; + request_report.arguments[1] = 0x01; + request_report.arguments[2] = 0x01; + UsbSend(&request_report); + UsbReceive(&response_report); + + memset(&request_report, 0, sizeof(razer_hanbo_report)); + request_report.header[1] = 0x82; + request_report.arguments[0] = 0x01; + UsbSend(&request_report); + UsbReceive(&response_report); +} + +void RazerHanboController::SetModeOff() +{ + SetDirectMode(); +} + +void RazerHanboController::SetModeSpectrumCycle() +{ + razer_hanbo_report request_report = razer_hanbo_create_report(0x82); + razer_hanbo_report response_report = razer_hanbo_create_report(0x00); + + request_report.arguments[0] = 0x01; + UsbSend(&request_report); + UsbReceive(&response_report); + + request_report.header[1] = 0x80; + request_report.arguments[0] = 0x01; + UsbSend(&request_report); + UsbReceive(&response_report); + + request_report.header[1] = 0x80; + request_report.arguments[0] = 0x01; + request_report.arguments[1] = 0x01; + UsbSend(&request_report); + UsbReceive(&response_report); +} + +void RazerHanboController::SetZoneLeds(int zone_idx, const zone& input_zone) +{ + razer_hanbo_report request_report = razer_hanbo_create_report(0x32); + std::string payload; + + unsigned int j = 0; + + if(zone_idx > PUMP) + request_report.header[1] = 0x40; + + payload = "0107000000000" + std::to_string(zone_idx); + + for(unsigned int i = 0; i < payload.length(); i += 2) + { + std::string byteString = payload.substr(i, 2); + char byte = (char)strtol(byteString.c_str(), NULL, 16); + request_report.arguments[j] = byte; + j++; + } + + /*--------------------------------------------*\ + | The color command format is G/R/B | + \*--------------------------------------------*/ + + for(unsigned int i = 0; i < input_zone.leds_count; i++) + { + request_report.arguments[j] = RGBGetGValue(input_zone.colors[i]); + j++; + request_report.arguments[j] = RGBGetRValue(input_zone.colors[i]); + j++; + request_report.arguments[j] = RGBGetBValue(input_zone.colors[i]); + j++; + } + + /*--------------------------------------------*\ + | Writing RGB values does not generate ack | + | reports from the cooler. Add a gap between | + | transactions to not overwhelm it. | + \*--------------------------------------------*/ + UsbSend(&request_report); + std::this_thread::sleep_for(2ms); +} + +/*--------------------------------------------*\ +| The Hanbo allows for individual brightness | +| of the pump cap and fans. OpenRGB only has | +| a single brightness slider. Whilst this | +| function can support individual settings, | +| most of the time they will be invoked with | +| identical values | +\*--------------------------------------------*/ +void RazerHanboController::SetBrightness(int zone, unsigned int brightness) +{ + razer_hanbo_report request_report = razer_hanbo_create_report(0x70); + razer_hanbo_report response_report = razer_hanbo_create_report(0x00); + + request_report.arguments[0] = 0x01; + request_report.arguments[1] = 0x00; + + if(zone > PUMP) + request_report.arguments[1] = 0x01; + + request_report.arguments[2] = brightness & 0xFF; + UsbSend(&request_report); + UsbReceive(&response_report); +} + +/*---------------------------------------------------------------------------------*\ +| Basic report and response creation function | +\*---------------------------------------------------------------------------------*/ + +razer_hanbo_report RazerHanboController::razer_hanbo_create_report(unsigned char header) +{ + /*---------------------------------------------------------*\ + | One type supports both requests and responses. | + | Requests start at header[1] to provide a dummy byte. | + | Responses provide 0 to this function for consistency | + \*---------------------------------------------------------*/ + razer_hanbo_report new_report; + + memset(&new_report, 0, sizeof(razer_hanbo_report)); + new_report.header[1] = header; + return new_report; +} + +/*---------------------------------------------------------------------------------*\ +| Get functions (request information from device) | +\*---------------------------------------------------------------------------------*/ + +void RazerHanboController::GetFirmware() +{ + razer_hanbo_report request_report = razer_hanbo_create_report(0x01); + razer_hanbo_report response_report = razer_hanbo_create_report(0x00); + + request_report.arguments[0] = 0x01; + UsbSend(&request_report); + /*---------------------------------------*\ + | The Hanbo sends firmware reports twice | + \*---------------------------------------*/ + UsbReceive(&response_report); + UsbReceive(&response_report); + + if(response_report.header[0] == 0x02) + { + std::string ret_serial(response_report.arguments, response_report.arguments+15); + std::vector firmware_ret(response_report.arguments+27, response_report.arguments+29); + char major = firmware_ret[0]; + char minor = firmware_ret[1] >> 4 & 0x0F; + char patch = firmware_ret[1] & 0x0F; + char ver[12]; + + snprintf(ver, sizeof(ver), "%hhu.%hhu.%hhu", major, minor, patch); + serial_string = ret_serial; + firmware_version = std::string(ver); + } +} + +/*---------------------------------------------------------------------------------*\ +| USB transfer functions | +\*---------------------------------------------------------------------------------*/ + +int RazerHanboController::UsbReceive(razer_hanbo_report* report) +{ + return hid_read_timeout(dev, (unsigned char*)report, sizeof(*report),2); +} + +int RazerHanboController::UsbSend(razer_hanbo_report* report) +{ + return hid_write(dev, (unsigned char*)report, sizeof(*report)); +} diff --git a/Controllers/RazerController/RazerHanboController/RazerHanboController.h b/Controllers/RazerController/RazerHanboController/RazerHanboController.h new file mode 100644 index 0000000..b952cf4 --- /dev/null +++ b/Controllers/RazerController/RazerHanboController/RazerHanboController.h @@ -0,0 +1,103 @@ +/*---------------------------------------------------------*\ +| RazerHanboController.h | +| | +| Driver for Razer Hanbo devices | +| | +| Joseph East (dripsnek) 12 Apr 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*---------------------------------------------------------*\ +| Struct packing macro for GCC and MSVC | +\*---------------------------------------------------------*/ +#ifdef __GNUC__ +#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) +#endif + +#ifdef _MSC_VER +#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop)) +#endif + +/*---------------------------------------------------------*\ +| The Hanbo does not advertise HID report IDs. | +| In this case under Windows, payloads sent to hidapi must | +| be prefixed with a byte of value 0 even if this exceeds | +| the device report length. This dummy byte never ends up | +| on the wire. Hidapi via libusb under Linux is compatible | +| with this behavior. This is generally not compatible with | +| hidraw when using it directly. | +| | +| Request payloads start at header[1] to make this byte. | +| Responses are unaffected and start at header[0]. | +\*---------------------------------------------------------*/ +PACK(struct razer_hanbo_report +{ + unsigned char header[2]; + unsigned char arguments[63]; +}); + +enum +{ + MIN_BRIGHTNESS = 0x00, + MAX_BRIGHTNESS = 0x64, +}; + +enum +{ + PUMP, + FAN1, + FAN2, + FAN3 +}; + +class RazerHanboController +{ +public: + RazerHanboController(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name); + ~RazerHanboController(); + + unsigned int GetDeviceIndex(); + device_type GetDeviceType(); + std::string GetDeviceLocation(); + std::string GetFirmwareString(); + std::string GetSerialString(); + std::string GetName(); + void SetDirectMode(); + void SetModeOff(); + void SetModeSpectrumCycle(); + void SetZoneLeds(int zone_idc, const zone& input_zone); + void SetBrightness(int zone, unsigned int brightness); + +private: + hid_device* dev; + unsigned short dev_pid; + + /*---------------------------------------------------------*\ + | Device information strings | + \*---------------------------------------------------------*/ + std::string firmware_version; + std::string serial_string; + std::string location; + std::string name; + + /*---------------------------------------------------------*\ + | Index of device in Razer device list | + \*---------------------------------------------------------*/ + unsigned int device_index; + + /*---------------------------------------------------------*\ + | Private functions | + \*---------------------------------------------------------*/ + razer_hanbo_report razer_hanbo_create_report(unsigned char header); + void GetFirmware(); + int UsbReceive(razer_hanbo_report* report); + int UsbSend(razer_hanbo_report* report); +}; diff --git a/Controllers/RazerController/RazerKrakenController/RGBController_RazerKraken.cpp b/Controllers/RazerController/RazerKrakenController/RGBController_RazerKraken.cpp new file mode 100644 index 0000000..159d7c5 --- /dev/null +++ b/Controllers/RazerController/RazerKrakenController/RGBController_RazerKraken.cpp @@ -0,0 +1,259 @@ +/*---------------------------------------------------------*\ +| RGBController_RazerKraken.cpp | +| | +| RGBController for Razer Kraken | +| | +| Adam Honse (CalcProgrammer1) 28 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RazerKraken.h" +#include "RazerDevices.h" + +/**------------------------------------------------------------------*\ + @name Razer Kraken + @category Headset + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRazerKrakenControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RazerKraken::RGBController_RazerKraken(RazerKrakenController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Razer"; + type = controller->GetDeviceType(); + description = "Razer Kraken Device"; + location = controller->GetDeviceLocation(); + version = controller->GetFirmwareString(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = RAZER_KRAKEN_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = RAZER_KRAKEN_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = RAZER_KRAKEN_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = RAZER_KRAKEN_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + /*---------------------------------------------------------*\ + | Razer Kraken 7.1 Chroma only does single color breathing | + \*---------------------------------------------------------*/ + if(device_list[controller->GetDeviceIndex()]->pid == RAZER_KRAKEN_PID) + { + Breathing.colors_max = 1; + } + else + { + Breathing.colors_max = 3; + } + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = RAZER_KRAKEN_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = 0; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(SpectrumCycle); + + SetupZones(); +} + +RGBController_RazerKraken::~RGBController_RazerKraken() +{ + delete controller; +} + +void RGBController_RazerKraken::SetupZones() +{ + unsigned int device_index = controller->GetDeviceIndex(); + + /*---------------------------------------------------------*\ + | Fill in zone information based on device table | + \*---------------------------------------------------------*/ + for(unsigned int zone_id = 0; zone_id < RAZER_MAX_ZONES; zone_id++) + { + if(device_list[device_index]->zones[zone_id] != NULL) + { + zone new_zone; + + new_zone.name = device_list[device_index]->zones[zone_id]->name; + new_zone.type = device_list[device_index]->zones[zone_id]->type; + + new_zone.leds_count = device_list[device_index]->zones[zone_id]->rows * device_list[device_index]->zones[zone_id]->cols; + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + if(new_zone.type == ZONE_TYPE_MATRIX) + { + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + + new_map->height = device_list[device_index]->zones[zone_id]->rows; + new_map->width = device_list[device_index]->zones[zone_id]->cols; + + new_map->map = new unsigned int[new_map->height * new_map->width]; + + for(unsigned int y = 0; y < new_map->height; y++) + { + for(unsigned int x = 0; x < new_map->width; x++) + { + new_map->map[(y * new_map->width) + x] = (y * new_map->width) + x; + } + } + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + } + } + + for(unsigned int zone_id = 0; zone_id < zones.size(); zone_id++) + { + for (unsigned int row_id = 0; row_id < device_list[device_index]->zones[zone_id]->rows; row_id++) + { + for (unsigned int col_id = 0; col_id < device_list[device_index]->zones[zone_id]->cols; col_id++) + { + led* new_led = new led(); + + new_led->name = device_list[device_index]->zones[zone_id]->name; + + if(zones[zone_id].leds_count > 1) + { + new_led->name.append(" LED "); + new_led->name.append(std::to_string(col_id + 1)); + } + + leds.push_back(*new_led); + } + } + } + + SetupColors(); +} + +void RGBController_RazerKraken::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RazerKraken::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetModeCustom(red, grn, blu); +} + +void RGBController_RazerKraken::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RazerKraken::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RazerKraken::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case RAZER_KRAKEN_MODE_OFF: + controller->SetModeOff(); + break; + + case RAZER_KRAKEN_MODE_STATIC: + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + if(modes[active_mode].colors.size() == 1) + { + unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu = RGBGetBValue(modes[active_mode].colors[0]); + + controller->SetModeStatic(red, grn, blu); + } + } + break; + + case RAZER_KRAKEN_MODE_BREATHING: + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + if(modes[active_mode].colors.size() == 1) + { + unsigned char red = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu = RGBGetBValue(modes[active_mode].colors[0]); + + controller->SetModeBreathingOneColor(red, grn, blu); + } + else if(modes[active_mode].colors.size() == 2) + { + unsigned char red1 = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn1 = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu1 = RGBGetBValue(modes[active_mode].colors[0]); + unsigned char red2 = RGBGetRValue(modes[active_mode].colors[1]); + unsigned char grn2 = RGBGetGValue(modes[active_mode].colors[1]); + unsigned char blu2 = RGBGetBValue(modes[active_mode].colors[1]); + + controller->SetModeBreathingTwoColors(red1, grn1, blu1, red2, grn2, blu2); + } + else if(modes[active_mode].colors.size() == 3) + { + unsigned char red1 = RGBGetRValue(modes[active_mode].colors[0]); + unsigned char grn1 = RGBGetGValue(modes[active_mode].colors[0]); + unsigned char blu1 = RGBGetBValue(modes[active_mode].colors[0]); + unsigned char red2 = RGBGetRValue(modes[active_mode].colors[1]); + unsigned char grn2 = RGBGetGValue(modes[active_mode].colors[1]); + unsigned char blu2 = RGBGetBValue(modes[active_mode].colors[1]); + unsigned char red3 = RGBGetRValue(modes[active_mode].colors[2]); + unsigned char grn3 = RGBGetGValue(modes[active_mode].colors[2]); + unsigned char blu3 = RGBGetBValue(modes[active_mode].colors[2]); + + controller->SetModeBreathingThreeColors(red1, grn1, blu1, red2, grn2, blu2, red3, grn3, blu3); + } + } + break; + + case RAZER_KRAKEN_MODE_SPECTRUM_CYCLE: + controller->SetModeSpectrumCycle(); + break; + } +} diff --git a/Controllers/RazerController/RazerKrakenController/RGBController_RazerKraken.h b/Controllers/RazerController/RazerKrakenController/RGBController_RazerKraken.h new file mode 100644 index 0000000..b6d046c --- /dev/null +++ b/Controllers/RazerController/RazerKrakenController/RGBController_RazerKraken.h @@ -0,0 +1,44 @@ +/*---------------------------------------------------------*\ +| RGBController_RazerKraken.h | +| | +| RGBController for Razer Kraken | +| | +| Adam Honse (CalcProgrammer1) 28 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RazerKrakenController.h" + +enum +{ + RAZER_KRAKEN_MODE_DIRECT, + RAZER_KRAKEN_MODE_OFF, + RAZER_KRAKEN_MODE_STATIC, + RAZER_KRAKEN_MODE_BREATHING, + RAZER_KRAKEN_MODE_SPECTRUM_CYCLE, +}; + +class RGBController_RazerKraken : public RGBController +{ +public: + RGBController_RazerKraken(RazerKrakenController* controller_ptr); + ~RGBController_RazerKraken(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RazerKrakenController* controller; +}; diff --git a/Controllers/RazerController/RazerKrakenController/RazerKrakenController.cpp b/Controllers/RazerController/RazerKrakenController/RazerKrakenController.cpp new file mode 100644 index 0000000..99cf541 --- /dev/null +++ b/Controllers/RazerController/RazerKrakenController/RazerKrakenController.cpp @@ -0,0 +1,401 @@ +/*---------------------------------------------------------*\ +| RazerKrakenController.cpp | +| | +| Driver for Razer Kraken | +| | +| Adam Honse (CalcProgrammer1) 28 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RazerKrakenController.h" +#include "RazerDevices.h" + +using namespace std::chrono_literals; + +RazerKrakenController::RazerKrakenController(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + dev_pid = pid; + location = path; + name = dev_name; + device_index = 0; + + /*-----------------------------------------------------------------*\ + | Loop through all known devices to look for a name match | + \*-----------------------------------------------------------------*/ + for (unsigned int i = 0; i < RAZER_NUM_DEVICES; i++) + { + if (device_list[i]->pid == dev_pid) + { + /*---------------------------------------------------------*\ + | Set device ID | + \*---------------------------------------------------------*/ + device_index = i; + } + } + + /*-----------------------------------------------------------------*\ + | Determine addresses for device | + \*-----------------------------------------------------------------*/ + switch(dev_pid) + { + case RAZER_KRAKEN_V2_PID: + case RAZER_KRAKEN_KITTY_BLACK_EDITION_V2_PID: + case RAZER_KRAKEN_ULTIMATE_PID: + led_mode_address = 0x172D; + custom_address = 0x1189; + breathing_address[0] = 0x1741; + breathing_address[1] = 0x1745; + breathing_address[2] = 0x174D; + break; + case RAZER_KRAKEN_CLASSIC_PID: + case RAZER_KRAKEN_CLASSIC_ALT_PID: + case RAZER_KRAKEN_PID: + led_mode_address = 0x1008; + custom_address = 0x1189; + breathing_address[0] = 0x15DE; + breathing_address[1] = 0x15DE; + breathing_address[2] = 0x15DE; + break; + } +} + +RazerKrakenController::~RazerKrakenController() +{ + hid_close(dev); +} + +std::string RazerKrakenController::GetName() +{ + return(name); +} + +unsigned int RazerKrakenController::GetDeviceIndex() +{ + return(device_index); +} + +device_type RazerKrakenController::GetDeviceType() +{ + return(device_list[device_index]->type); +} + +std::string RazerKrakenController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RazerKrakenController::GetFirmwareString() +{ + return(razer_get_firmware()); +} + +std::string RazerKrakenController::GetSerialString() +{ + return(razer_get_serial()); +} + +void RazerKrakenController::SetModeBreathingOneColor(unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_set_mode_breathing_one_color(red, grn, blu); +} + +void RazerKrakenController::SetModeBreathingTwoColors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2) +{ + razer_set_mode_breathing_two_colors(r1, g1, b1, r2, g2, b2); +} + +void RazerKrakenController::SetModeBreathingThreeColors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2, unsigned char r3, unsigned char g3, unsigned char b3) +{ + razer_set_mode_breathing_three_colors(r1, g1, b1, r2, g2, b2, r3, g3, b3); +} + +void RazerKrakenController::SetModeCustom(unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_set_mode_custom(red, grn, blu); +} + +void RazerKrakenController::SetModeOff() +{ + razer_set_mode_none(); +} + +void RazerKrakenController::SetModeSpectrumCycle() +{ + razer_set_mode_spectrum_cycle(); +} + +void RazerKrakenController::SetModeStatic(unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_set_mode_static(red, grn, blu); +} + +/*---------------------------------------------------------------------------------*\ +| Basic report and response creation functions | +\*---------------------------------------------------------------------------------*/ + +razer_kraken_request_report RazerKrakenController::razer_kraken_create_report(unsigned char report_id, unsigned char destination, unsigned char length, unsigned short address) +{ + razer_kraken_request_report new_report; + + /*---------------------------------------------------------*\ + | Zero out the new report | + \*---------------------------------------------------------*/ + memset(&new_report, 0, sizeof(razer_kraken_request_report)); + + /*---------------------------------------------------------*\ + | Fill in the new report with the given parameters | + \*---------------------------------------------------------*/ + new_report.report_id = report_id; + new_report.destination = destination; + new_report.length = length; + new_report.addr_h = (address >> 8); + new_report.addr_l = (address & 0xFF); + + return new_report; +} + +razer_kraken_effect_byte RazerKrakenController::razer_kraken_create_effect_byte() +{ + razer_kraken_effect_byte effect_byte; + + memset(&effect_byte, 0, sizeof(razer_kraken_effect_byte)); + + return effect_byte; +} + +/*---------------------------------------------------------------------------------*\ +| Get functions (request information from device) | +\*---------------------------------------------------------------------------------*/ + +std::string RazerKrakenController::razer_get_firmware() +{ + std::string firmware_string = ""; + struct razer_kraken_request_report report = razer_kraken_create_report(0x04, 0x20, 0x02, 0x0030); + struct razer_kraken_response_report response_report; + + std::this_thread::sleep_for(1ms); + razer_usb_send(&report); + std::this_thread::sleep_for(1ms); + razer_usb_receive(&response_report); + + if(response_report.report_id == 0x05) + { + firmware_string = "v" + std::to_string(response_report.arguments[1]) + "." + std::to_string(response_report.arguments[2]); + } + + return firmware_string; +} + +std::string RazerKrakenController::razer_get_serial() +{ + char serial_string[64] = ""; + struct razer_kraken_request_report report = razer_kraken_create_report(0x04, 0x20, 0x16, 0x7f00); + struct razer_kraken_response_report response_report; + + std::this_thread::sleep_for(1ms); + razer_usb_send(&report); + std::this_thread::sleep_for(1ms); + razer_usb_receive(&response_report); + + if(response_report.report_id == 0x05) + { + strncpy(&serial_string[0], (const char*)&response_report.arguments[0], 22); + serial_string[22] = '\0'; + } + + for(size_t i = 0; i < 22; i++) + { + if(serial_string[i] < 30 || serial_string[i] > 126) + { + serial_string[i] = ' '; + } + } + + std::string ret_string = serial_string; + return ret_string; +} + +/*---------------------------------------------------------------------------------*\ +| Set functions (send information to device) | +\*---------------------------------------------------------------------------------*/ + +void RazerKrakenController::razer_set_mode_breathing_one_color(unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_kraken_request_report rgb_report = razer_kraken_create_report(0x04, 0x40, 0x03, breathing_address[0]); + razer_kraken_request_report effect_report = razer_kraken_create_report(0x04, 0x40, 0x01, led_mode_address); + razer_kraken_effect_byte effect_byte = razer_kraken_create_effect_byte(); + + rgb_report.arguments[0] = red; + rgb_report.arguments[1] = grn; + rgb_report.arguments[2] = blu; + + effect_byte.bits.on_off_static = 1; + effect_byte.bits.single_colour_breathing = 1; + effect_byte.bits.sync = 1; + effect_report.arguments[0] = effect_byte.value; + + razer_usb_send(&rgb_report); + razer_usb_send(&effect_report); +} + +void RazerKrakenController::razer_set_mode_breathing_two_colors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2) +{ + razer_kraken_request_report rgb_report_1 = razer_kraken_create_report(0x04, 0x40, 0x03, breathing_address[1]); + razer_kraken_request_report rgb_report_2 = razer_kraken_create_report(0x04, 0x40, 0x03, breathing_address[1] + 4); + razer_kraken_request_report effect_report = razer_kraken_create_report(0x04, 0x40, 0x01, led_mode_address); + razer_kraken_effect_byte effect_byte = razer_kraken_create_effect_byte(); + + rgb_report_1.arguments[0] = r1; + rgb_report_1.arguments[1] = g1; + rgb_report_1.arguments[2] = b1; + + rgb_report_2.arguments[0] = r2; + rgb_report_2.arguments[1] = g2; + rgb_report_2.arguments[2] = b2; + + effect_byte.bits.on_off_static = 1; + effect_byte.bits.two_colour_breathing = 1; + effect_byte.bits.sync = 1; + effect_report.arguments[0] = effect_byte.value; + + razer_usb_send(&rgb_report_1); + razer_usb_send(&rgb_report_2); + razer_usb_send(&effect_report); +} + +void RazerKrakenController::razer_set_mode_breathing_three_colors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2, unsigned char r3, unsigned char g3, unsigned char b3) +{ + razer_kraken_request_report rgb_report_1 = razer_kraken_create_report(0x04, 0x40, 0x03, breathing_address[1]); + razer_kraken_request_report rgb_report_2 = razer_kraken_create_report(0x04, 0x40, 0x03, breathing_address[1] + 4); + razer_kraken_request_report rgb_report_3 = razer_kraken_create_report(0x04, 0x40, 0x03, breathing_address[1] + 8); + razer_kraken_request_report effect_report = razer_kraken_create_report(0x04, 0x40, 0x01, led_mode_address); + razer_kraken_effect_byte effect_byte = razer_kraken_create_effect_byte(); + + rgb_report_1.arguments[0] = r1; + rgb_report_1.arguments[1] = g1; + rgb_report_1.arguments[2] = b1; + + rgb_report_2.arguments[0] = r2; + rgb_report_2.arguments[1] = g2; + rgb_report_2.arguments[2] = b2; + + rgb_report_3.arguments[0] = r3; + rgb_report_3.arguments[1] = g3; + rgb_report_3.arguments[2] = b3; + + effect_byte.bits.on_off_static = 1; + effect_byte.bits.three_colour_breathing = 1; + effect_byte.bits.sync = 1; + effect_report.arguments[0] = effect_byte.value; + + razer_usb_send(&rgb_report_1); + razer_usb_send(&rgb_report_2); + razer_usb_send(&rgb_report_3); + razer_usb_send(&effect_report); +} + +void RazerKrakenController::razer_set_mode_custom(unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_kraken_request_report rgb_report = razer_kraken_create_report(0x04, 0x40, 3, custom_address); + razer_kraken_request_report effect_report = razer_kraken_create_report(0x04, 0x40, 0x01, led_mode_address); + razer_kraken_effect_byte effect_byte = razer_kraken_create_effect_byte(); + + effect_byte.value = 0; + + effect_byte.bits.on_off_static = 1; + effect_byte.bits.spectrum_cycling = 0; + + rgb_report.arguments[0] = red; + rgb_report.arguments[1] = grn; + rgb_report.arguments[2] = blu; + effect_report.arguments[0] = effect_byte.value; + + switch(dev_pid) + { + case RAZER_KRAKEN_PID: + case RAZER_KRAKEN_V2_PID: + case RAZER_KRAKEN_KITTY_BLACK_EDITION_V2_PID: + case RAZER_KRAKEN_ULTIMATE_PID: + razer_usb_send(&rgb_report); + break; + } + + razer_usb_send(&effect_report); +} + +void RazerKrakenController::razer_set_mode_none() +{ + razer_kraken_request_report report = razer_kraken_create_report(0x04, 0x40, 0x01, led_mode_address); + razer_kraken_effect_byte effect_byte = razer_kraken_create_effect_byte(); + + effect_byte.value = 0; + + effect_byte.bits.on_off_static = 0; + effect_byte.bits.spectrum_cycling = 0; + + report.arguments[0] = effect_byte.value; + + razer_usb_send(&report); +} + +void RazerKrakenController::razer_set_mode_spectrum_cycle() +{ + razer_kraken_request_report report = razer_kraken_create_report(0x04, 0x40, 0x01, led_mode_address); + razer_kraken_effect_byte effect_byte = razer_kraken_create_effect_byte(); + + effect_byte.value = 0; + + effect_byte.bits.on_off_static = 1; + effect_byte.bits.spectrum_cycling = 1; + + report.arguments[0] = effect_byte.value; + + razer_usb_send(&report); +} + +void RazerKrakenController::razer_set_mode_static(unsigned char red, unsigned char grn, unsigned char blu) +{ + razer_kraken_request_report rgb_report = razer_kraken_create_report(0x04, 0x40, 3, breathing_address[0]); + razer_kraken_request_report effect_report = razer_kraken_create_report(0x04, 0x40, 0x01, led_mode_address); + razer_kraken_effect_byte effect_byte = razer_kraken_create_effect_byte(); + + effect_byte.value = 0; + + effect_byte.bits.on_off_static = 1; + effect_byte.bits.spectrum_cycling = 0; + + rgb_report.arguments[0] = red; + rgb_report.arguments[1] = grn; + rgb_report.arguments[2] = blu; + effect_report.arguments[0] = effect_byte.value; + + switch(dev_pid) + { + case RAZER_KRAKEN_PID: + case RAZER_KRAKEN_V2_PID: + case RAZER_KRAKEN_KITTY_BLACK_EDITION_V2_PID: + case RAZER_KRAKEN_ULTIMATE_PID: + razer_usb_send(&rgb_report); + break; + } + + razer_usb_send(&effect_report); +} + +/*---------------------------------------------------------------------------------*\ +| USB transfer functions | +\*---------------------------------------------------------------------------------*/ + +int RazerKrakenController::razer_usb_receive(razer_kraken_response_report* report) +{ + return hid_read(dev, (unsigned char*)report, sizeof(*report)); +} + +int RazerKrakenController::razer_usb_send(razer_kraken_request_report* report) +{ + return hid_write(dev, (unsigned char*)report, sizeof(*report)); +} diff --git a/Controllers/RazerController/RazerKrakenController/RazerKrakenController.h b/Controllers/RazerController/RazerKrakenController/RazerKrakenController.h new file mode 100644 index 0000000..060bda3 --- /dev/null +++ b/Controllers/RazerController/RazerKrakenController/RazerKrakenController.h @@ -0,0 +1,128 @@ +/*---------------------------------------------------------*\ +| RazerKrakenController.h | +| | +| Driver for Razer Kraken | +| | +| Adam Honse (CalcProgrammer1) 28 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*---------------------------------------------------------*\ +| Struct packing macro for GCC and MSVC | +\*---------------------------------------------------------*/ +#ifdef __GNUC__ +#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) +#endif + +#ifdef _MSC_VER +#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop)) +#endif + +union razer_kraken_effect_byte +{ + unsigned char value; + + struct razer_kraken_effect_byte_bits + { + unsigned char on_off_static :1; + unsigned char single_colour_breathing :1; + unsigned char spectrum_cycling :1; + unsigned char sync :1; + unsigned char two_colour_breathing :1; + unsigned char three_colour_breathing :1; + } bits; +}; + +/*---------------------------------------------------------*\ +| Razer Kraken Report Types (taken from OpenRazer) | +\*---------------------------------------------------------*/ +PACK(struct razer_kraken_request_report +{ + unsigned char report_id; + unsigned char destination; + unsigned char length; + unsigned char addr_h; + unsigned char addr_l; + unsigned char arguments[32]; +}); + +PACK(struct razer_kraken_response_report +{ + unsigned char report_id; + unsigned char arguments[36]; +}); + +class RazerKrakenController +{ +public: + RazerKrakenController(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name); + ~RazerKrakenController(); + + unsigned int GetDeviceIndex(); + device_type GetDeviceType(); + std::string GetDeviceLocation(); + std::string GetFirmwareString(); + std::string GetName(); + std::string GetSerialString(); + + void SetModeBreathingOneColor(unsigned char red, unsigned char grn, unsigned char blu); + void SetModeBreathingTwoColors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2); + void SetModeBreathingThreeColors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2, unsigned char r3, unsigned char g3, unsigned char b3); + void SetModeCustom(unsigned char red, unsigned char grn, unsigned char blu); + void SetModeOff(); + void SetModeSpectrumCycle(); + void SetModeStatic(unsigned char red, unsigned char grn, unsigned char blu); + +private: + hid_device* dev; + unsigned short dev_pid; + + /*---------------------------------------------------------*\ + | Device information strings | + \*---------------------------------------------------------*/ + std::string firmware_version; + std::string location; + std::string name; + + /*---------------------------------------------------------*\ + | Kraken LED/Mode Addresses | + \*---------------------------------------------------------*/ + unsigned short breathing_address[3]; + unsigned short custom_address; + unsigned short led_mode_address; + + /*---------------------------------------------------------*\ + | Index of device in Razer device list | + \*---------------------------------------------------------*/ + unsigned int device_index; + + /*---------------------------------------------------------*\ + | Private functions based on OpenRazer | + \*---------------------------------------------------------*/ + razer_kraken_request_report razer_kraken_create_report(unsigned char report_id, unsigned char destination, unsigned char length, unsigned short address); + razer_kraken_effect_byte razer_kraken_create_effect_byte(); + + std::string razer_get_firmware(); + std::string razer_get_serial(); + + void razer_set_mode_breathing_one_color(unsigned char red, unsigned char grn, unsigned char blu); + void razer_set_mode_breathing_two_colors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2); + void razer_set_mode_breathing_three_colors(unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2, unsigned char r3, unsigned char g3, unsigned char b3); + void razer_set_mode_custom(unsigned char red, unsigned char grn, unsigned char blu); + void razer_set_mode_none(); + void razer_set_mode_spectrum_cycle(); + void razer_set_mode_static(unsigned char red, unsigned char grn, unsigned char blu); + + int razer_usb_receive(razer_kraken_response_report* report); + int razer_usb_send(razer_kraken_request_report* report); + + +}; diff --git a/Controllers/RazerController/RazerKrakenV3Controller/RGBController_RazerKrakenV3.cpp b/Controllers/RazerController/RazerKrakenV3Controller/RGBController_RazerKrakenV3.cpp new file mode 100644 index 0000000..68795e4 --- /dev/null +++ b/Controllers/RazerController/RazerKrakenV3Controller/RGBController_RazerKrakenV3.cpp @@ -0,0 +1,189 @@ +/*---------------------------------------------------------*\ +| RGBController_RazerKrakenV3.cpp | +| | +| RGBController for Razer devices with 13-byte reports | +| | +| Greg Sandstrom (superstrom) 1 Nov 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RazerDevices.h" +#include "RGBController_RazerKrakenV3.h" + +RGBController_RazerKrakenV3::RGBController_RazerKrakenV3(RazerKrakenV3Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Razer"; + type = controller->GetDeviceType(); + description = "Razer Device"; + location = controller->GetDeviceLocation(); + version = controller->GetFirmwareString(); + serial = controller->GetSerialString(); + uint8_t max_brightness = controller->GetMaxBrightness(); + + // By default, the device starts as Wave/Spectrum Cycle. + // Set Wave as first mode, so switching to Direct calls DeviceUpdateMode() + + mode Wave; + Wave.name = "Wave"; + Wave.value = RAZER_KRAKEN_V3_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_BRIGHTNESS; + Wave.direction = MODE_DIRECTION_RIGHT; + Wave.color_mode = MODE_COLORS_NONE; + Wave.brightness_min = 0; + Wave.brightness_max = max_brightness; + Wave.brightness = max_brightness; + modes.push_back(Wave); + + + mode Direct; + Direct.name = "Direct"; + Direct.value = RAZER_KRAKEN_V3_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = max_brightness; + Direct.brightness = max_brightness; + modes.push_back(Direct); + + // V3 X does not support this mode. + if(device_list[controller->GetDeviceIndex()]->pid == RAZER_KRAKEN_V3_HYPERSENSE_PID) + { + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = RAZER_KRAKEN_V3_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.colors.resize(1); + modes.push_back(Breathing); + } + + SetupZones(); +} + +RGBController_RazerKrakenV3::~RGBController_RazerKrakenV3() +{ + delete controller; +} + +void RGBController_RazerKrakenV3::SetupZones() +{ + unsigned int device_index = controller->GetDeviceIndex(); + + /*---------------------------------------------------------*\ + | Fill in zone information based on device table | + \*---------------------------------------------------------*/ + for(unsigned int zone_id = 0; zone_id < RAZER_MAX_ZONES; zone_id++) + { + if(device_list[device_index]->zones[zone_id] != NULL) + { + zone new_zone; + + new_zone.name = device_list[device_index]->zones[zone_id]->name; + new_zone.type = device_list[device_index]->zones[zone_id]->type; + + new_zone.leds_count = device_list[device_index]->zones[zone_id]->rows * device_list[device_index]->zones[zone_id]->cols; + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + if(new_zone.type == ZONE_TYPE_MATRIX) + { + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + + new_map->height = device_list[device_index]->zones[zone_id]->rows; + new_map->width = device_list[device_index]->zones[zone_id]->cols; + + new_map->map = new unsigned int[new_map->height * new_map->width]; + + for(unsigned int y = 0; y < new_map->height; y++) + { + for(unsigned int x = 0; x < new_map->width; x++) + { + new_map->map[(y * new_map->width) + x] = (y * new_map->width) + x; + } + } + } + else + { + new_zone.matrix_map = NULL; + } + + zones.push_back(new_zone); + } + } + + for(unsigned int zone_id = 0; zone_id < zones.size(); zone_id++) + { + for (unsigned int row_id = 0; row_id < device_list[device_index]->zones[zone_id]->rows; row_id++) + { + for (unsigned int col_id = 0; col_id < device_list[device_index]->zones[zone_id]->cols; col_id++) + { + led* new_led = new led(); + + new_led->name = device_list[device_index]->zones[zone_id]->name; + + if(zones[zone_id].leds_count > 1) + { + new_led->name.append(" LED "); + new_led->name.append(std::to_string(col_id + 1)); + } + + leds.push_back(*new_led); + } + } + } + + SetupColors(); +} + +void RGBController_RazerKrakenV3::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_RazerKrakenV3::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == RAZER_KRAKEN_V3_MODE_DIRECT) + { + controller->SetDirect(&colors[0]); + } +} + +void RGBController_RazerKrakenV3::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RazerKrakenV3::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RazerKrakenV3::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case RAZER_KRAKEN_V3_MODE_DIRECT: + controller->SetModeDirect(); + controller->SetBrightness(modes[active_mode].brightness); + break; + + case RAZER_KRAKEN_V3_MODE_WAVE: + controller->SetModeWave(); + controller->SetBrightness(modes[active_mode].brightness); + break; + + case RAZER_KRAKEN_V3_MODE_BREATHING: + controller->SetModeBreathing(modes[active_mode].colors); + controller->SetBrightness(modes[active_mode].brightness); + break; + } +} diff --git a/Controllers/RazerController/RazerKrakenV3Controller/RGBController_RazerKrakenV3.h b/Controllers/RazerController/RazerKrakenV3Controller/RGBController_RazerKrakenV3.h new file mode 100644 index 0000000..c721220 --- /dev/null +++ b/Controllers/RazerController/RazerKrakenV3Controller/RGBController_RazerKrakenV3.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_RazerKrakenV3.h | +| | +| RGBController for Razer devices with 13-byte reports | +| | +| Greg Sandstrom (superstrom) 1 Nov 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RazerKrakenV3Controller.h" + +enum +{ + RAZER_KRAKEN_V3_MODE_DIRECT, + RAZER_KRAKEN_V3_MODE_WAVE, + RAZER_KRAKEN_V3_MODE_BREATHING, +}; + +class RGBController_RazerKrakenV3 : public RGBController +{ +public: + RGBController_RazerKrakenV3(RazerKrakenV3Controller* controller_ptr); + ~RGBController_RazerKrakenV3(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RazerKrakenV3Controller* controller; +}; diff --git a/Controllers/RazerController/RazerKrakenV3Controller/RazerKrakenV3Controller.cpp b/Controllers/RazerController/RazerKrakenV3Controller/RazerKrakenV3Controller.cpp new file mode 100644 index 0000000..b7cddad --- /dev/null +++ b/Controllers/RazerController/RazerKrakenV3Controller/RazerKrakenV3Controller.cpp @@ -0,0 +1,280 @@ +/*---------------------------------------------------------*\ +| RazerKrakenV3Controller.cpp | +| | +| Driver for Razer devices with 13-byte reports | +| | +| Greg Sandstrom (superstrom) 1 Nov 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RazerKrakenController.h" +#include "RazerKrakenV3Controller.h" +#include "RazerDevices.h" + +using namespace std::chrono_literals; + +RazerKrakenV3Controller::RazerKrakenV3Controller(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + dev_pid = pid; + location = path; + name = dev_name; + + /*-----------------------------------------------------------------*\ + | Loop through all known devices to look for a name match | + \*-----------------------------------------------------------------*/ + for(unsigned int i = 0; i < RAZER_NUM_DEVICES; i++) + { + if(device_list[i]->pid == dev_pid) + { + /*---------------------------------------------------------*\ + | Set device ID | + \*---------------------------------------------------------*/ + device_index = i; + } + } + + /*-----------------------------------------------------------------*\ + | Determine matrix type for device | + \*-----------------------------------------------------------------*/ + matrix_type = device_list[device_index]->matrix_type; +} + +RazerKrakenV3Controller::~RazerKrakenV3Controller() +{ + hid_close(dev); +} + +std::string RazerKrakenV3Controller::GetName() +{ + return(name); +} + +unsigned int RazerKrakenV3Controller::GetDeviceIndex() +{ + return(device_index); +} + +device_type RazerKrakenV3Controller::GetDeviceType() +{ + return(device_list[device_index]->type); +} + +std::string RazerKrakenV3Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RazerKrakenV3Controller::GetFirmwareString() +{ + return(razer_get_firmware()); +} + +std::string RazerKrakenV3Controller::GetSerialString() +{ + return(razer_get_serial()); +} + +unsigned char RazerKrakenV3Controller::GetMaxBrightness() +{ + /*-----------------------------------------------------*\ + | Max brightness for most devices is 0xFF (255) | + | Add PIDs only for devices that use 0x64 (100) | + | or any another arbitrary value | + \*-----------------------------------------------------*/ + unsigned char max_brightness = 255; + + return(max_brightness); +} + +void RazerKrakenV3Controller::SetModeDirect() +{ + razer_kraken_v3_request_report report = razer_kraken_create_v3_report(); + + // 0 1 2 3 4 5 6 7 8 + // 4001000f0800000000 + // 40010000080000000000000000 + report.report_id = RAZER_KRAKEN_V3_REPORT_ID; + report.command_id = RAZER_KRAKEN_V3_CMD_LIGHTING_SET_MODE; + report.arguments[1] = 0x0F; + report.arguments[2] = RAZER_KRAKEN_V3_MODE_ID_DIRECT; + + hid_write(dev, (unsigned char*)&report, sizeof(report)); +} + +void RazerKrakenV3Controller::SetDirect(RGBColor* colors) +{ + razer_kraken_v3_request_report report = razer_kraken_create_v3_report(); + + // how to get the number of leds? + unsigned int led_count = device_list[device_index]->cols; + + // 0 1 2 3 4 5 6 7 8 + // 400300ffffff000000 + // 400300ffffff00000000000000 + report.report_id = RAZER_KRAKEN_V3_REPORT_ID; + report.command_id = RAZER_KRAKEN_V3_CMD_LIGHTING_SET_COLOR; + + for(unsigned int led_idx = 0; led_idx < led_count; led_idx++) + { + report.arguments[1 + (led_idx * 3)] = RGBGetRValue(colors[led_idx]); + report.arguments[2 + (led_idx * 3)] = RGBGetGValue(colors[led_idx]); + report.arguments[3 + (led_idx * 3)] = RGBGetBValue(colors[led_idx]); + } + + hid_write(dev, (unsigned char*)&report, sizeof(report)); +} + +void RazerKrakenV3Controller::SetBrightness(unsigned char brightness) +{ + razer_kraken_v3_request_report report = razer_kraken_create_v3_report(); + + // 0 1 2 3 4 5 6 7 8 + // 40020000ff00000000 + report.report_id = RAZER_KRAKEN_V3_REPORT_ID; + report.command_id = RAZER_KRAKEN_V3_CMD_LIGHTING_SET_BRIGHTNESS; + + report.arguments[2] = brightness; + + hid_write(dev, (unsigned char*)&report, sizeof(report)); +} + +void RazerKrakenV3Controller::SetModeWave() +{ + razer_kraken_v3_request_report report = razer_kraken_create_v3_report(); + + // 0 1 2 3 4 5 6 7 8 + // 4001000f0300000000 + // 40010100030000000000000000 + report.report_id = RAZER_KRAKEN_V3_REPORT_ID; + report.command_id = RAZER_KRAKEN_V3_CMD_LIGHTING_SET_MODE; + report.arguments[1] = 0x0F; + report.arguments[2] = RAZER_KRAKEN_V3_MODE_ID_WAVE; + + hid_write(dev, (unsigned char*)&report, sizeof(report)); +} + +void RazerKrakenV3Controller::SetModeBreathing(std::vector colors) +{ + razer_kraken_v3_request_report report = razer_kraken_create_v3_report(); + + unsigned char led_count = (unsigned char)colors.size(); + + // 0 1 2 3 4 5 6 7 8 9 0 1 2 + // 400101000101ff000000000000 + // 40010100010100ffff00000000 + // 40010100020200ff00ff000000 + report.report_id = RAZER_KRAKEN_V3_REPORT_ID; + report.command_id = RAZER_KRAKEN_V3_CMD_LIGHTING_SET_MODE; + report.arguments[0] = 0x01; + report.arguments[2] = led_count; // normally this is where the MODE_ID goes.... + report.arguments[3] = led_count; + for(unsigned int led_idx = 0; led_idx < led_count; led_idx++) + { + report.arguments[4 + (led_idx * 3)] = RGBGetRValue(colors[led_idx]); + report.arguments[5 + (led_idx * 3)] = RGBGetGValue(colors[led_idx]); + report.arguments[6 + (led_idx * 3)] = RGBGetBValue(colors[led_idx]); + } + + hid_write(dev, (unsigned char*)&report, sizeof(report)); +} + +razer_kraken_v3_request_report RazerKrakenV3Controller::razer_kraken_create_v3_report() +{ + razer_kraken_v3_request_report new_report; + + /*---------------------------------------------------------*\ + | Zero out the new report | + \*---------------------------------------------------------*/ + memset(&new_report, 0, sizeof(razer_kraken_v3_request_report)); + + new_report.report_id = RAZER_KRAKEN_V3_REPORT_ID; + + return new_report; +} + +razer_kraken_request_report RazerKrakenV3Controller::razer_kraken_create_report(unsigned char report_id, unsigned char destination, unsigned char length, unsigned short address) +{ + razer_kraken_request_report new_report; + + /*---------------------------------------------------------*\ + | Zero out the new report | + \*---------------------------------------------------------*/ + memset(&new_report, 0, sizeof(razer_kraken_request_report)); + + /*---------------------------------------------------------*\ + | Fill in the new report with the given parameters | + \*---------------------------------------------------------*/ + new_report.report_id = report_id; + new_report.destination = destination; + new_report.length = length; + new_report.addr_h = (address >> 8); + new_report.addr_l = (address & 0xFF); + + return new_report; +} + +std::string RazerKrakenV3Controller::razer_get_firmware() +{ + std::string firmware_string = ""; + struct razer_kraken_request_report report = razer_kraken_create_report(0x04, 0x20, 0x02, 0x0030); + struct razer_kraken_response_report response_report; + + std::this_thread::sleep_for(1ms); + razer_usb_send(&report); + std::this_thread::sleep_for(1ms); + razer_usb_receive(&response_report); + + if(response_report.report_id == 0x05) + { + firmware_string = "v" + std::to_string(response_report.arguments[1]) + "." + std::to_string(response_report.arguments[2]); + } + + return firmware_string; +} + +std::string RazerKrakenV3Controller::razer_get_serial() +{ + char serial_string[64] = ""; + struct razer_kraken_request_report report = razer_kraken_create_report(0x04, 0x20, 0x16, 0x7f00); + struct razer_kraken_response_report response_report; + + std::this_thread::sleep_for(1ms); + razer_usb_send(&report); + std::this_thread::sleep_for(1ms); + razer_usb_receive(&response_report); + + if(response_report.report_id == 0x05) + { + strncpy(&serial_string[0], (const char*)&response_report.arguments[0], 22); + serial_string[22] = '\0'; + } + + for(size_t i = 0; i < 22; i++) + { + if(serial_string[i] < 30 || serial_string[i] > 126) + { + serial_string[i] = ' '; + } + } + + std::string ret_string = serial_string; + return ret_string; +} + +/*---------------------------------------------------------------------------------*\ +| USB transfer functions | +\*---------------------------------------------------------------------------------*/ + +int RazerKrakenV3Controller::razer_usb_receive(razer_kraken_response_report* report) +{ + return hid_read(dev, (unsigned char*)report, sizeof(*report)); +} + +int RazerKrakenV3Controller::razer_usb_send(razer_kraken_request_report* report) +{ + return hid_write(dev, (unsigned char*)report, sizeof(*report)); +} diff --git a/Controllers/RazerController/RazerKrakenV3Controller/RazerKrakenV3Controller.h b/Controllers/RazerController/RazerKrakenV3Controller/RazerKrakenV3Controller.h new file mode 100644 index 0000000..2a571b1 --- /dev/null +++ b/Controllers/RazerController/RazerKrakenV3Controller/RazerKrakenV3Controller.h @@ -0,0 +1,106 @@ +/*---------------------------------------------------------*\ +| RazerKrakenV3Controller.h | +| | +| Driver for Razer devices with 13-byte reports | +| | +| Greg Sandstrom (superstrom) 1 Nov 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "DeviceGuardManager.h" +#include "RazerKrakenController.h" + +#define RAZER_KRAKEN_V3_REPORT_ID 0x40 + +enum +{ + RAZER_KRAKEN_V3_CMD_LIGHTING_SET_MODE = 0x01, + RAZER_KRAKEN_V3_CMD_LIGHTING_SET_BRIGHTNESS = 0x02, + RAZER_KRAKEN_V3_CMD_LIGHTING_SET_COLOR = 0x03, +}; + +enum +{ + RAZER_KRAKEN_V3_MODE_ID_DIRECT = 0x08, + RAZER_KRAKEN_V3_MODE_ID_WAVE = 0x03, +}; + +PACK(struct razer_kraken_v3_request_report +{ + unsigned char report_id; // usb_buf[0] + unsigned char command_id; // usb_buf[1] + unsigned char arguments[13]; // usb_buf[2...] +}); + +class RazerKrakenV3Controller +{ +public: + RazerKrakenV3Controller(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name); + ~RazerKrakenV3Controller(); + + unsigned int GetDeviceIndex(); + device_type GetDeviceType(); + std::string GetDeviceLocation(); + std::string GetFirmwareString(); + std::string GetName(); + std::string GetSerialString(); + unsigned char GetMaxBrightness(); + + void SetDirect(RGBColor* colors); + void SetBrightness(unsigned char brightness); + + void SetModeDirect(); + void SetModeWave(); + void SetModeBreathing(std::vector colors); + +private: + hid_device* dev; + unsigned short dev_pid; + + /*---------------------------------------------------------*\ + | Device-specific protocol settings | + \*---------------------------------------------------------*/ + unsigned char dev_transaction_id; + unsigned char dev_led_id; + + /*---------------------------------------------------------*\ + | Device information strings | + \*---------------------------------------------------------*/ + std::string firmware_version; + std::string location; + std::string name; + + /*---------------------------------------------------------*\ + | Index of device in Razer device list | + \*---------------------------------------------------------*/ + unsigned int device_index; + + /*---------------------------------------------------------*\ + | HID report index for request and response | + \*---------------------------------------------------------*/ + unsigned char report_index; + unsigned char response_index; + + /*---------------------------------------------------------*\ + | Matrix type | + \*---------------------------------------------------------*/ + unsigned char matrix_type; + + /*---------------------------------------------------------*\ + | Private functions based on OpenRazer | + \*---------------------------------------------------------*/ + std::string razer_get_firmware(); + std::string razer_get_serial(); + + razer_kraken_v3_request_report razer_kraken_create_v3_report(); + + razer_kraken_request_report razer_kraken_create_report(unsigned char report_id, unsigned char destination, unsigned char length, unsigned short address); + int razer_usb_receive(razer_kraken_response_report* report); + int razer_usb_send(razer_kraken_request_report* report); +}; diff --git a/Controllers/RazerController/RazerKrakenV4Controller/RGBController_RazerKrakenV4.cpp b/Controllers/RazerController/RazerKrakenV4Controller/RGBController_RazerKrakenV4.cpp new file mode 100644 index 0000000..98e42a5 --- /dev/null +++ b/Controllers/RazerController/RazerKrakenV4Controller/RGBController_RazerKrakenV4.cpp @@ -0,0 +1,130 @@ +/*---------------------------------------------------------*\ +| RGBController_RazerKrakenV4.cpp | +| | +| RGBController for Razer 64-byte devices | +| | +| Adam Honse (CalcProgrammer1) 21 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RazerKrakenV4.h" +#include "RazerDevices.h" + +RGBController_RazerKrakenV4::RGBController_RazerKrakenV4(RazerKrakenV4Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Razer"; + type = controller->GetDeviceType(); + description = "Razer Device"; + location = controller->GetDeviceLocation(); + version = controller->GetFirmwareString(); + serial = controller->GetSerialString(); + uint8_t max_brightness = controller->GetMaxBrightness(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = RAZER_KRAKEN_V4_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Wave; + Wave.name = "Wave"; + Wave.value = RAZER_KRAKEN_V4_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_BRIGHTNESS; + Wave.direction = MODE_DIRECTION_RIGHT; + Wave.color_mode = MODE_COLORS_NONE; + Wave.brightness_min = 0; + Wave.brightness_max = max_brightness; + Wave.brightness = max_brightness; + modes.push_back(Wave); + + SetupZones(); +} + +RGBController_RazerKrakenV4::~RGBController_RazerKrakenV4() +{ + delete controller; +} + +void RGBController_RazerKrakenV4::SetupZones() +{ + unsigned int device_index = controller->GetDeviceIndex(); + + /*-----------------------------------------------------*\ + | Fill in zone information based on device table | + | Kraken V4 devices are assumed to only have one row | + \*-----------------------------------------------------*/ + for(unsigned int zone_id = 0; zone_id < RAZER_MAX_ZONES; zone_id++) + { + if(device_list[device_index]->zones[zone_id] != NULL) + { + zone new_zone; + + new_zone.name = device_list[device_index]->zones[zone_id]->name; + new_zone.type = device_list[device_index]->zones[zone_id]->type; + + new_zone.leds_count = device_list[device_index]->zones[zone_id]->cols; + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + new_zone.matrix_map = NULL; + + zones.push_back(new_zone); + + for(unsigned int col_id = 0; col_id < device_list[device_index]->zones[zone_id]->cols; col_id++) + { + led* new_led = new led(); + + new_led->name = device_list[device_index]->zones[zone_id]->name; + + if(zones[zone_id].leds_count > 1) + { + new_led->name.append(" LED "); + new_led->name.append(std::to_string(col_id + 1)); + } + + leds.push_back(*new_led); + } + } + } + + SetupColors(); +} + +void RGBController_RazerKrakenV4::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_RazerKrakenV4::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == RAZER_KRAKEN_V4_MODE_DIRECT) + { + controller->SetDirect(&colors[0]); + } +} + +void RGBController_RazerKrakenV4::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RazerKrakenV4::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RazerKrakenV4::DeviceUpdateMode() +{ + switch(modes[active_mode].value) + { + case RAZER_KRAKEN_V4_MODE_WAVE: + controller->SetModeWave(); + controller->SetBrightness(modes[active_mode].brightness); + break; + } +} diff --git a/Controllers/RazerController/RazerKrakenV4Controller/RGBController_RazerKrakenV4.h b/Controllers/RazerController/RazerKrakenV4Controller/RGBController_RazerKrakenV4.h new file mode 100644 index 0000000..aeca6d0 --- /dev/null +++ b/Controllers/RazerController/RazerKrakenV4Controller/RGBController_RazerKrakenV4.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_RazerKrakenV4.h | +| | +| RGBController for Razer 64-byte devices | +| | +| Adam Honse (CalcProgrammer1) 21 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RazerKrakenV4Controller.h" + +enum +{ + RAZER_KRAKEN_V4_MODE_DIRECT, + RAZER_KRAKEN_V4_MODE_WAVE, +}; + +class RGBController_RazerKrakenV4 : public RGBController +{ +public: + RGBController_RazerKrakenV4(RazerKrakenV4Controller* controller_ptr); + ~RGBController_RazerKrakenV4(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RazerKrakenV4Controller* controller; +}; diff --git a/Controllers/RazerController/RazerKrakenV4Controller/RazerKrakenV4Controller.cpp b/Controllers/RazerController/RazerKrakenV4Controller/RazerKrakenV4Controller.cpp new file mode 100644 index 0000000..29e60d4 --- /dev/null +++ b/Controllers/RazerController/RazerKrakenV4Controller/RazerKrakenV4Controller.cpp @@ -0,0 +1,284 @@ +/*---------------------------------------------------------*\ +| RazerKrakenV4Controller.cpp | +| | +| Driver for Razer devices with 64-byte report | +| | +| Adam Honse (CalcProgrammer1) 21 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RazerKrakenV4Controller.h" +#include "RazerDevices.h" + +using namespace std::chrono_literals; + +RazerKrakenV4Controller::RazerKrakenV4Controller(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + dev_pid = pid; + location = path; + name = dev_name; + + /*-----------------------------------------------------*\ + | Loop through all known devices to look for a name | + | match | + \*-----------------------------------------------------*/ + for(unsigned int i = 0; i < RAZER_NUM_DEVICES; i++) + { + if(device_list[i]->pid == dev_pid) + { + /*---------------------------------------------*\ + | Set device ID | + \*---------------------------------------------*/ + device_index = i; + } + } + + /*-----------------------------------------------------*\ + | Determine matrix type for device | + \*-----------------------------------------------------*/ + matrix_type = device_list[device_index]->matrix_type; + + /*-----------------------------------------------------*\ + | All Kraken V4 devices use 0x02 for report and | + | response index | + \*-----------------------------------------------------*/ + report_index = 0x02; + response_index = 0x02; + + /*-----------------------------------------------------*\ + | Determine transaction ID for device | + \*-----------------------------------------------------*/ + dev_transaction_id = device_list[device_index]->transaction_id; + + /*-----------------------------------------------------*\ + | Determine wireless flag for device | + \*-----------------------------------------------------*/ + switch(dev_pid) + { + case RAZER_KRAKEN_V4_WIRELESS_PID: + case RAZER_KRAKEN_KITTY_V3_PRO_WIRELESS_PID: + dev_wireless_flag = 0x80; + break; + + default: + dev_wireless_flag = 0x00; + break; + } +} + +RazerKrakenV4Controller::~RazerKrakenV4Controller() +{ + hid_close(dev); +} + +std::string RazerKrakenV4Controller::GetName() +{ + return(name); +} + +unsigned int RazerKrakenV4Controller::GetDeviceIndex() +{ + return(device_index); +} + +device_type RazerKrakenV4Controller::GetDeviceType() +{ + return(device_list[device_index]->type); +} + +std::string RazerKrakenV4Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RazerKrakenV4Controller::GetFirmwareString() +{ + return(razer_get_firmware()); +} + +std::string RazerKrakenV4Controller::GetSerialString() +{ + return(razer_get_serial()); +} + +unsigned char RazerKrakenV4Controller::GetMaxBrightness() +{ + /*-----------------------------------------------------*\ + | Max brightness for most devices is 0xFF (255) | + | Add PIDs only for devices that use 0x64 (100) | + | or any another arbitrary value | + \*-----------------------------------------------------*/ + unsigned char max_brightness = 255; + + return(max_brightness); +} + +void RazerKrakenV4Controller::SetDirect(RGBColor* colors) +{ + struct razer_kraken_v4_report report = razer_kraken_v4_create_report(0x0F, 0x03, (5 + (3 * device_list[device_index]->cols))); + + report.arguments[2] = 0; + report.arguments[3] = 0; + report.arguments[4] = device_list[device_index]->cols - 1; + + for(unsigned int led_idx = 0; led_idx < device_list[device_index]->cols; led_idx++) + { + report.arguments[5 + (led_idx * 3)] = RGBGetRValue(colors[led_idx]); + report.arguments[6 + (led_idx * 3)] = RGBGetGValue(colors[led_idx]); + report.arguments[7 + (led_idx * 3)] = RGBGetBValue(colors[led_idx]); + } + + razer_usb_send(&report); +} + +void RazerKrakenV4Controller::SetBrightness(unsigned char brightness) +{ + struct razer_kraken_v4_report report = razer_kraken_v4_create_report(0x00, 0x00, 0x05); + + report.arguments[0] = RAZER_KRAKEN_V4_CMD_LIGHTING_SET_BRIGHTNESS; + report.arguments[2] = 0x01; + report.arguments[3] = brightness; + + razer_usb_send(&report); +} + +void RazerKrakenV4Controller::SetModeWave() +{ + struct razer_kraken_v4_report report = razer_kraken_v4_create_report(0x00, 0x00, 0x05); + + report.arguments[0] = RAZER_KRAKEN_V4_CMD_LIGHTING_SET_MODE; + report.arguments[2] = 0x01; + report.arguments[3] = 0x04; + + razer_usb_send(&report); +} + +unsigned char RazerKrakenV4Controller::razer_kraken_v4_calculate_crc(razer_kraken_v4_report* report) +{ + /*-----------------------------------------------------*\ + | The second to last byte of report is a simple | + | checksum. Just xor all bytes up with overflow and | + | you are done | + \*-----------------------------------------------------*/ + unsigned char crc = 0; + unsigned char* report_ptr = (unsigned char*)report; + + for(unsigned int i = 0; i < 61; i++) + { + crc ^= report_ptr[i]; + } + + return crc; +} + +razer_kraken_v4_report RazerKrakenV4Controller::razer_kraken_v4_create_report(unsigned char command_class, unsigned char command_id, unsigned char data_size) +{ + razer_kraken_v4_report new_report; + + /*-----------------------------------------------------*\ + | Zero out the new report | + \*-----------------------------------------------------*/ + memset(&new_report, 0, sizeof(razer_kraken_v4_report)); + + /*-----------------------------------------------------*\ + | Fill in the new report with the given parameters | + \*-----------------------------------------------------*/ + new_report.report_id = report_index; + new_report.status = 0x00; + new_report.transaction_id = dev_transaction_id; + new_report.remaining_packets = 0x00; + new_report.protocol_type = 0x00; + new_report.data_size = data_size; + new_report.command_class = command_class; + new_report.command_id = command_id; + new_report.wireless_flag = dev_wireless_flag; + + return new_report; +} + +razer_kraken_v4_report RazerKrakenV4Controller::razer_kraken_v4_create_response() +{ + razer_kraken_v4_report new_report; + + /*-----------------------------------------------------*\ + | Zero out the new report | + \*-----------------------------------------------------*/ + memset(&new_report, 0, sizeof(razer_kraken_v4_report)); + + /*-----------------------------------------------------*\ + | Fill in the new report with the given parameters | + \*-----------------------------------------------------*/ + new_report.report_id = response_index; + new_report.status = 0x00; + new_report.transaction_id = dev_transaction_id; + new_report.remaining_packets = 0x00; + new_report.protocol_type = 0x00; + new_report.command_class = 0x00; + new_report.command_id = 0x00; + new_report.data_size = 0x00; + new_report.wireless_flag = dev_wireless_flag; + + return new_report; +} + +std::string RazerKrakenV4Controller::razer_get_firmware() +{ + std::string firmware_string = ""; + struct razer_kraken_v4_report report = razer_kraken_v4_create_report(0x00, 0x00, 0x04); + struct razer_kraken_v4_report response_report = razer_kraken_v4_create_response(); + + report.arguments[0] = RAZER_KRAKEN_V4_CMD_GET_FIRMWARE_INFO; + + std::this_thread::sleep_for(2ms); + razer_usb_send(&report); + std::this_thread::sleep_for(5ms); + razer_usb_receive(&response_report); + + firmware_string = "v" + std::to_string(response_report.arguments[3]) + "." + std::to_string(response_report.arguments[4]) + "." + std::to_string(response_report.arguments[5]) + "." + std::to_string(response_report.arguments[6]); + + return firmware_string; +} + +std::string RazerKrakenV4Controller::razer_get_serial() +{ + char serial_string[16]; + struct razer_kraken_v4_report report = razer_kraken_v4_create_report(0x00, 0x00, 0x04); + struct razer_kraken_v4_report response_report = razer_kraken_v4_create_response(); + + report.arguments[0] = RAZER_KRAKEN_V4_CMD_GET_SERIAL; + + std::this_thread::sleep_for(2ms); + razer_usb_send(&report); + std::this_thread::sleep_for(5ms); + razer_usb_receive(&response_report); + + memcpy(&serial_string[0], &response_report.arguments[3], 16); + serial_string[15] = '\0'; + + for(size_t i = 0; i < 15; i++) + { + if(serial_string[i] < 30 || serial_string[i] > 126) + { + serial_string[i] = ' '; + } + } + + std::string ret_string = serial_string; + return ret_string; +} + +int RazerKrakenV4Controller::razer_usb_receive(razer_kraken_v4_report* report) +{ + report->crc = razer_kraken_v4_calculate_crc(report); + + return hid_read(dev, (unsigned char *)report, sizeof(*report)); +} + +int RazerKrakenV4Controller::razer_usb_send(razer_kraken_v4_report* report) +{ + return hid_write(dev, (unsigned char *)report, sizeof(*report)); +} diff --git a/Controllers/RazerController/RazerKrakenV4Controller/RazerKrakenV4Controller.h b/Controllers/RazerController/RazerKrakenV4Controller/RazerKrakenV4Controller.h new file mode 100644 index 0000000..ad22734 --- /dev/null +++ b/Controllers/RazerController/RazerKrakenV4Controller/RazerKrakenV4Controller.h @@ -0,0 +1,118 @@ +/*---------------------------------------------------------*\ +| RazerKrakenV4Controller.h | +| | +| Driver for Razer devices with 64-byte report | +| | +| Adam Honse (CalcProgrammer1) 21 Oct 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "DeviceGuardManager.h" + +/*---------------------------------------------------------*\ +| Struct packing macro for GCC and MSVC | +\*---------------------------------------------------------*/ +#ifdef __GNUC__ +#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) +#endif + +#ifdef _MSC_VER +#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop)) +#endif + +PACK(struct razer_kraken_v4_report +{ + unsigned char report_id; + unsigned char status; + unsigned char transaction_id; + unsigned short remaining_packets; + unsigned char protocol_type; + unsigned char data_size; + unsigned char command_class; + unsigned char command_id; + unsigned char wireless_flag; + unsigned char arguments[52]; + unsigned char crc; + unsigned char reserved; +}); + +enum +{ + RAZER_KRAKEN_V4_CMD_GET_SERIAL = 0x00, + RAZER_KRAKEN_V4_CMD_GET_FIRMWARE_INFO = 0x02, + RAZER_KRAKEN_V4_CMD_LIGHTING_SET_MODE = 0xC0, + RAZER_KRAKEN_V4_CMD_LIGHTING_SET_BRIGHTNESS = 0xC1, +}; + +class RazerKrakenV4Controller +{ +public: + RazerKrakenV4Controller(hid_device* dev_handle, const char* path, unsigned short pid, std::string dev_name); + ~RazerKrakenV4Controller(); + + unsigned int GetDeviceIndex(); + device_type GetDeviceType(); + std::string GetDeviceLocation(); + std::string GetFirmwareString(); + std::string GetName(); + std::string GetSerialString(); + unsigned char GetMaxBrightness(); + + void SetDirect(RGBColor* colors); + void SetBrightness(unsigned char brightness); + + void SetModeWave(); + +private: + hid_device* dev; + unsigned short dev_pid; + + /*---------------------------------------------------------*\ + | Device-specific protocol settings | + \*---------------------------------------------------------*/ + unsigned char dev_transaction_id; + unsigned char dev_led_id; + unsigned char dev_wireless_flag; + + /*---------------------------------------------------------*\ + | Device information strings | + \*---------------------------------------------------------*/ + std::string firmware_version; + std::string location; + std::string name; + + /*---------------------------------------------------------*\ + | Index of device in Razer device list | + \*---------------------------------------------------------*/ + unsigned int device_index; + + /*---------------------------------------------------------*\ + | HID report index for request and response | + \*---------------------------------------------------------*/ + unsigned char report_index; + unsigned char response_index; + + /*---------------------------------------------------------*\ + | Matrix type | + \*---------------------------------------------------------*/ + unsigned char matrix_type; + + /*---------------------------------------------------------*\ + | Private functions based on OpenRazer | + \*---------------------------------------------------------*/ + unsigned char razer_kraken_v4_calculate_crc(razer_kraken_v4_report* report); + razer_kraken_v4_report razer_kraken_v4_create_report(unsigned char command_class, unsigned char command_id, unsigned char data_size); + razer_kraken_v4_report razer_kraken_v4_create_response(); + + std::string razer_get_firmware(); + std::string razer_get_serial(); + + int razer_usb_receive(razer_kraken_v4_report* report); + int razer_usb_send(razer_kraken_v4_report* report); +}; diff --git a/Controllers/RealtekARGBController/RGBController_RealtekARGB.cpp b/Controllers/RealtekARGBController/RGBController_RealtekARGB.cpp new file mode 100644 index 0000000..95983b2 --- /dev/null +++ b/Controllers/RealtekARGBController/RGBController_RealtekARGB.cpp @@ -0,0 +1,523 @@ +/*---------------------------------------------------------*\ +| RGBController_RealtekARGB.cpp | +| | +| RGBController for Realtek USB ARGB ICs | +| | +| Jerry Fan (JerryFan0612) 13 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RealtekARGB.h" + +/**------------------------------------------------------------------*\ + @name Realtek ARGB Device + @category LEDStrip + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors RealtekARGBControllerDetect + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RealtekARGB::RGBController_RealtekARGB(RealtekARGBController* controller_ptr) +{ + controller = controller_ptr; + name = controller_ptr->get_dev_name(); + vendor = controller_ptr->get_manu_name(); + location = controller_ptr->get_dev_loc(); + serial = controller_ptr->get_sn(); + version = controller_ptr->get_fw_ver(); + description = vendor + "ARGB Device"; + type = DEVICE_TYPE_LEDSTRIP; + std::fill(std::begin(ready_to_reboot), std::end(ready_to_reboot), false); + + SetupModes(); + SetupZones(); +} + +void RGBController_RealtekARGB::SetupModes() +{ + int brightness = controller->get_argb_brightness(0) >> 8; + + mode Direct; + Direct.name = "Direct"; + Direct.value = REALTEK_ARGB_EFF_NULL; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 255; + Direct.brightness = brightness; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = REALTEK_ARGB_EFF_ALWAYS_ON; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + Static.brightness_min = 0; + Static.brightness_max = 255; + Static.brightness = brightness; + modes.push_back(Static); + + mode Blink; + Blink.name = "Blink"; + Blink.value = REALTEK_ARGB_EFF_BLINK; + Blink.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Blink.speed_min = REALTEK_ARGB_SPEED_MIN; + Blink.speed_max = REALTEK_ARGB_SPEED_MAX; + Blink.speed = REALTEK_ARGB_SPEED_NORMAL; + Blink.colors_min = 1; + Blink.colors_max = 2; + Blink.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blink.colors.resize(2); + Blink.brightness_min = 0; + Blink.brightness_max = 255; + Blink.brightness = brightness; + modes.push_back(Blink); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = REALTEK_ARGB_EFF_BREATH; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.speed_min = REALTEK_ARGB_SPEED_MIN; + Breathing.speed_max = REALTEK_ARGB_SPEED_MAX; + Breathing.speed = REALTEK_ARGB_SPEED_NORMAL; + Breathing.colors_min = 1; + Breathing.colors_max = 2; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(2); + Breathing.brightness_min = 0; + Breathing.brightness_max = 255; + Breathing.brightness = brightness; + modes.push_back(Breathing); + + mode Spectrum; + Spectrum.name = "Spectrum"; + Spectrum.value = REALTEK_ARGB_EFF_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Spectrum.speed_min = REALTEK_ARGB_SPEED_MIN; + Spectrum.speed_max = REALTEK_ARGB_SPEED_MAX; + Spectrum.speed = REALTEK_ARGB_SPEED_NORMAL; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.brightness_min = 0; + Spectrum.brightness_max = 255; + Spectrum.brightness = brightness; + modes.push_back(Spectrum); + + mode Scroll; + Scroll.name = "Scroll"; + Scroll.value = REALTEK_ARGB_EFF_SCROLL; + Scroll.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Scroll.speed_min = REALTEK_ARGB_SPEED_MIN; + Scroll.speed_max = REALTEK_ARGB_SPEED_MAX; + Scroll.speed = REALTEK_ARGB_SPEED_NORMAL; + Scroll.colors_min = 1; + Scroll.colors_max = 2; + Scroll.color_mode = MODE_COLORS_MODE_SPECIFIC; + Scroll.colors.resize(2); + Scroll.brightness_min = 0; + Scroll.brightness_max = 255; + Scroll.brightness = brightness; + modes.push_back(Scroll); + + mode RainbowScroll; + RainbowScroll.name = "Rainbow Scroll"; + RainbowScroll.value = REALTEK_ARGB_EFF_RAINBOW_SCROLL; + RainbowScroll.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + RainbowScroll.speed_min = REALTEK_ARGB_SPEED_MIN; + RainbowScroll.speed_max = REALTEK_ARGB_SPEED_MAX; + RainbowScroll.speed = REALTEK_ARGB_SPEED_NORMAL; + RainbowScroll.color_mode = MODE_COLORS_NONE; + RainbowScroll.brightness_min = 0; + RainbowScroll.brightness_max = 255; + RainbowScroll.brightness = brightness; + modes.push_back(RainbowScroll); + + mode RunningWater; + RunningWater.name = "Running Water"; + RunningWater.value = REALTEK_ARGB_EFF_RUNNING_WATER; + RunningWater.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + RunningWater.speed_min = REALTEK_ARGB_SPEED_MIN; + RunningWater.speed_max = REALTEK_ARGB_SPEED_MAX; + RunningWater.speed = REALTEK_ARGB_SPEED_NORMAL; + RunningWater.colors_min = 1; + RunningWater.colors_max = 2; + RunningWater.color_mode = MODE_COLORS_MODE_SPECIFIC; + RunningWater.colors.resize(2); + RunningWater.brightness_min = 0; + RunningWater.brightness_max = 255; + RunningWater.brightness = brightness; + modes.push_back(RunningWater); + + mode Sliding; + Sliding.name = "Sliding"; + Sliding.value = REALTEK_ARGB_EFF_SLIDING; + Sliding.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Sliding.speed_min = REALTEK_ARGB_SPEED_MIN; + Sliding.speed_max = REALTEK_ARGB_SPEED_MAX; + Sliding.speed = REALTEK_ARGB_SPEED_NORMAL; + Sliding.colors_min = 1; + Sliding.colors_max = 2; + Sliding.color_mode = MODE_COLORS_MODE_SPECIFIC; + Sliding.colors.resize(2); + Sliding.brightness_min = 0; + Sliding.brightness_max = 255; + Sliding.brightness = brightness; + modes.push_back(Sliding); + + mode WideSliding; + WideSliding.name = "Wide Sliding"; + WideSliding.value = REALTEK_ARGB_EFF_WIDE_SLIDING; + WideSliding.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + WideSliding.speed_min = REALTEK_ARGB_SPEED_MIN; + WideSliding.speed_max = REALTEK_ARGB_SPEED_MAX; + WideSliding.speed = REALTEK_ARGB_SPEED_NORMAL; + WideSliding.colors_min = 1; + WideSliding.colors_max = 2; + WideSliding.color_mode = MODE_COLORS_MODE_SPECIFIC; + WideSliding.colors.resize(2); + WideSliding.brightness_min = 0; + WideSliding.brightness_max = 255; + WideSliding.brightness = brightness; + modes.push_back(WideSliding); + + mode RainbowSliding; + RainbowSliding.name = "Rainbow Sliding"; + RainbowSliding.value = REALTEK_ARGB_EFF_RAINBOW_SLIDING; + RainbowSliding.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RainbowSliding.speed_min = REALTEK_ARGB_SPEED_MIN; + RainbowSliding.speed_max = REALTEK_ARGB_SPEED_MAX; + RainbowSliding.speed = REALTEK_ARGB_SPEED_NORMAL; + RainbowSliding.color_mode = MODE_COLORS_NONE; + RainbowSliding.brightness_min = 0; + RainbowSliding.brightness_max = 255; + RainbowSliding.brightness = brightness; + modes.push_back(RainbowSliding); + + mode RainbowFadeSliding; + RainbowFadeSliding.name = "Rainbow Fade Sliding"; + RainbowFadeSliding.value = REALTEK_ARGB_EFF_RAINBOW_FADE_SLIDING; + RainbowFadeSliding.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + RainbowFadeSliding.speed_min = REALTEK_ARGB_SPEED_MIN; + RainbowFadeSliding.speed_max = REALTEK_ARGB_SPEED_MAX; + RainbowFadeSliding.speed = REALTEK_ARGB_SPEED_NORMAL; + RainbowFadeSliding.color_mode = MODE_COLORS_NONE; + RainbowFadeSliding.brightness_min = 0; + RainbowFadeSliding.brightness_max = 255; + RainbowFadeSliding.brightness = brightness; + modes.push_back(RainbowFadeSliding); + + mode NewtonCradle; + NewtonCradle.name = "Newton Cradle"; + NewtonCradle.value = REALTEK_ARGB_EFF_NEWTON_CRADLE; + NewtonCradle.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + NewtonCradle.speed_min = REALTEK_ARGB_SPEED_MIN; + NewtonCradle.speed_max = REALTEK_ARGB_SPEED_MAX; + NewtonCradle.speed = REALTEK_ARGB_SPEED_NORMAL; + NewtonCradle.colors_min = 1; + NewtonCradle.colors_max = 2; + NewtonCradle.color_mode = MODE_COLORS_MODE_SPECIFIC; + NewtonCradle.colors.resize(2); + NewtonCradle.brightness_min = 0; + NewtonCradle.brightness_max = 255; + NewtonCradle.brightness = brightness; + modes.push_back(NewtonCradle); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = REALTEK_ARGB_EFF_METEOR; + Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Meteor.speed_min = REALTEK_ARGB_SPEED_MIN; + Meteor.speed_max = REALTEK_ARGB_SPEED_MAX; + Meteor.speed = REALTEK_ARGB_SPEED_NORMAL; + Meteor.colors_min = 1; + Meteor.colors_max = 2; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors.resize(2); + Meteor.brightness_min = 0; + Meteor.brightness_max = 255; + Meteor.brightness = brightness; + modes.push_back(Meteor); + + mode ZigZag; + ZigZag.name = "ZigZag"; + ZigZag.value = REALTEK_ARGB_EFF_ZIGZAG; + ZigZag.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR; + ZigZag.speed_min = REALTEK_ARGB_SPEED_MIN; + ZigZag.speed_max = REALTEK_ARGB_SPEED_MAX; + ZigZag.speed = REALTEK_ARGB_SPEED_NORMAL; + ZigZag.colors_min = 1; + ZigZag.colors_max = 2; + ZigZag.color_mode = MODE_COLORS_MODE_SPECIFIC; + ZigZag.colors.resize(2); + ZigZag.brightness_min = 0; + ZigZag.brightness_max = 255; + ZigZag.brightness = brightness; + modes.push_back(ZigZag); + + mode StarryNight; + StarryNight.name = "Starry Night"; + StarryNight.value = REALTEK_ARGB_EFF_STARRY_NIGHT; + StarryNight.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_RANDOM_COLOR; + StarryNight.speed_min = REALTEK_ARGB_SPEED_MIN; + StarryNight.speed_max = REALTEK_ARGB_SPEED_MAX; + StarryNight.speed = REALTEK_ARGB_SPEED_NORMAL; + StarryNight.colors_min = 1; + StarryNight.colors_max = 1; + StarryNight.color_mode = MODE_COLORS_MODE_SPECIFIC; + StarryNight.colors.resize(1); + StarryNight.brightness_min = 0; + StarryNight.brightness_max = 255; + StarryNight.brightness = brightness; + modes.push_back(StarryNight); + + mode Stack; + Stack.name = "Stack"; + Stack.value = REALTEK_ARGB_EFF_STACK; + Stack.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Stack.colors_min = 1; + Stack.colors_max = 2; + Stack.color_mode = MODE_COLORS_MODE_SPECIFIC; + Stack.colors.resize(2); + Stack.brightness_min = 0; + Stack.brightness_max = 255; + Stack.brightness = brightness; + modes.push_back(Stack); +} + +RGBController_RealtekARGB::~RGBController_RealtekARGB() +{ + delete controller; +} + +void RGBController_RealtekARGB::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + int idx = 0; + int argb_num = 0; + int fix_grps = 0; + int num_fixgrp = 0; + int argb_num_fixgrp = 0; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + valid_grp.clear(); + fix_grps = controller->get_fix_grps(); + + for(int grp_num = 0; grp_num < REALTEK_ARGB_NUM_ARGB_GRP; grp_num++) + { + if(controller->get_zone_enable(grp_num)) + { + valid_grp.push_back(grp_num); + if(fix_grps & (0x1 << grp_num)) + { + num_fixgrp++; + argb_num_fixgrp += controller->get_argb_num(grp_num); + } + } + } + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + zones.resize(valid_grp.size()); + for(int grp_num : valid_grp) + { + argb_num = controller->get_argb_num(grp_num); + zones[idx].name = "strip " + std::to_string(idx + 1); + zones[idx].type = ZONE_TYPE_LINEAR; + if(fix_grps & (0x1 << grp_num)) + { + zones[idx].leds_count = argb_num; + zones[idx].leds_min = argb_num; + zones[idx].leds_max = argb_num; + } + else + { + if(first_run) + { + zones[idx].leds_count = 0; + } + else + { + zones[idx].leds_count = argb_num; + } + + zones[idx].leds_min = 0; + zones[idx].leds_max = (REALTEK_ARGB_MAX - argb_num_fixgrp) / ((int)valid_grp.size() - num_fixgrp); + } + zones[idx].matrix_map = NULL; + for(unsigned int led_idx = 0; led_idx < zones[idx].leds_count; led_idx++) + { + led myled; + myled.name = zones[idx].name + " led_"; + myled.name.append(std::to_string(led_idx + 1)); + leds.push_back(myled); + } + idx++; + } + SetupColors(); +} + +void RGBController_RealtekARGB::ResizeZone(int zone, int new_size) +{ + int fix_grps = controller->get_fix_grps(); + int orig_num = controller->get_argb_num(valid_grp[zone]); + bool need_reboot = true; + + if((size_t) zone >= zones.size()) + { + return; + } + if((new_size == orig_num) && zones[zone].leds_count) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + int total_argb_num = 0; + std::vector rtk_colors(orig_num, 0); + for(int grp_num = 0; grp_num < REALTEK_ARGB_NUM_ARGB_GRP; grp_num++) + { + if(grp_num == valid_grp[zone]) + { + total_argb_num += new_size; + } + else + { + total_argb_num += controller->get_argb_num(grp_num); + } + } + if(total_argb_num > REALTEK_ARGB_MAX) + { + return; + } + + controller->set_argb_direct(valid_grp[zone], rtk_colors, 0xFF); + controller->set_argb_num(valid_grp[zone], new_size); + ready_to_reboot[valid_grp[zone]] = true; + + for(int grp_num = 0; grp_num < REALTEK_ARGB_NUM_ARGB_GRP; grp_num++) + { + if(!ready_to_reboot[grp_num] && !(fix_grps & (0x1 << grp_num))) + { + need_reboot = false; + break; + } + } + if(need_reboot) + { + controller->device_reboot(); + controller->device_rescan_trigger(); + } + SetupZones(); + } +} + +void RGBController_RealtekARGB::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].leds_count > 0) + { + UpdateZoneLEDs((int)zone_idx); + } + } +} + +void RGBController_RealtekARGB::UpdateZoneLEDs(int zone) +{ + unsigned short brightness = 0xFF; + mode& curr_mode = modes[active_mode]; + std::vector color_buf; + + if(curr_mode.color_mode == MODE_COLORS_PER_LED && + curr_mode.value == REALTEK_ARGB_EFF_NULL) //direct mode + { + if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS) + { + brightness = curr_mode.brightness; + } + + color_buf.resize(zones[zone].leds_count); + for(unsigned int i = 0; i < zones[zone].leds_count; i++) + color_buf[i] = zones[zone].colors[i]; + controller->set_argb_direct(valid_grp[zone], color_buf, brightness); + } + else + { + UpdateSingleLED(zone); + } +} + +void RGBController_RealtekARGB::UpdateSingleLED(int zone) +{ + mode& curr_mode = modes[active_mode]; + std::vector rtk_colors = curr_mode.colors; + struct RealtekARGBControllerSetEffParam param; + + param.speed = REALTEK_ARGB_SPEED_NORMAL; + param.brightness = 0xFF; + param.dir = 0; + param.random_color = 0; + + if(curr_mode.flags & MODE_FLAG_HAS_SPEED) + { + param.speed = curr_mode.speed; + } + if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS) + { + param.brightness = curr_mode.brightness; + } + if(curr_mode.flags & MODE_FLAG_HAS_DIRECTION_LR) + { + if(curr_mode.direction == MODE_DIRECTION_RIGHT) + { + param.dir = 1; + } + } + if(curr_mode.flags & MODE_FLAG_HAS_RANDOM_COLOR) + { + if(curr_mode.color_mode == MODE_COLORS_RANDOM) + { + param.random_color = 1; + } + } + + if(curr_mode.color_mode == MODE_COLORS_PER_LED) + { + rtk_colors = colors; + } + else if(curr_mode.color_mode == MODE_COLORS_NONE) + { + rtk_colors.clear(); + } + + controller->set_argb_effect(valid_grp[zone], curr_mode.value, rtk_colors, ¶m); +} + +void RGBController_RealtekARGB::DeviceUpdateMode() +{ + if(modes[active_mode].value != REALTEK_ARGB_EFF_NULL) + { + DeviceUpdateLEDs(); + } +} diff --git a/Controllers/RealtekARGBController/RGBController_RealtekARGB.h b/Controllers/RealtekARGBController/RGBController_RealtekARGB.h new file mode 100644 index 0000000..f5b7332 --- /dev/null +++ b/Controllers/RealtekARGBController/RGBController_RealtekARGB.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_RealtekARGB.h | +| | +| RGBController for Realtek USB ARGB ICs | +| | +| Jerry Fan (JerryFan0612) 13 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RealtekARGBController.h" + +class RGBController_RealtekARGB : public RGBController +{ +public: + RGBController_RealtekARGB(RealtekARGBController* controller_ptr); + ~RGBController_RealtekARGB(); + + void SetupModes(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int zone); + void DeviceUpdateMode(); + +private: + RealtekARGBController* controller; + std::vector valid_grp; + bool ready_to_reboot[REALTEK_ARGB_NUM_ARGB_GRP] = {false}; +}; diff --git a/Controllers/RealtekARGBController/RealtekARGBController.cpp b/Controllers/RealtekARGBController/RealtekARGBController.cpp new file mode 100644 index 0000000..5c1100d --- /dev/null +++ b/Controllers/RealtekARGBController/RealtekARGBController.cpp @@ -0,0 +1,715 @@ +/*---------------------------------------------------------*\ +| RealtekARGBController.cpp | +| | +| Controller for Realtek USB ARGB ICs | +| | +| Jerry Fan (JerryFan0612) 13 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RealtekARGBController.h" +#include "MathUtils.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +static const unsigned char hid_set_packet[] = +{ + 0x56, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xE3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char hid_get_packet[] = +{ + 0x56, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x10, 0xE2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char hid_end_packet[] = +{ + 0x58, 0x53, 0x42, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +RealtekARGBController::RealtekARGBController(hid_device* dev, hid_device_info* info) +{ + hdev = dev; + hidinfo = info; + for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++) + { + argbctl_data[i] = (unsigned char*)calloc(REALTEK_ARGB_CTL_DATA_SIZE, 1); + memset(argbctl_data[i], 0, REALTEK_ARGB_CTL_DATA_SIZE); + prev_bright[i] = 0xFFFF; + } + device_init(); + + keepalive_thread_run = false; + keepalive_thread = NULL; +} + +RealtekARGBController::~RealtekARGBController() +{ + /*-----------------------------------------------------*\ + | Close keepalive thread | + \*-----------------------------------------------------*/ + if(keepalive_thread != NULL) + { + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + } + + if(hdev) + { + for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++) + { + int ret = set_appctl(i, REALTEK_ARGB_LED_CTL_FW); + if(!ret) + { + appctl[i] = REALTEK_ARGB_LED_CTL_FW; + } + } + hid_close(hdev); + hdev = NULL; + } + memset(argbctl_hdr, 0, REALTEK_ARGB_CTL_HDR_SIZE); + for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++) + free(argbctl_data[i]); +} + +void RealtekARGBController::KeepaliveThreadFunction() +{ + /*-----------------------------------------------------------------*\ + | One shot thread to rescan device | + \*-----------------------------------------------------------------*/ + while(keepalive_thread_run.load()) + { + std::this_thread::sleep_for(2s); + device_rescan(); + keepalive_thread_run = false; + } +} + +int RealtekARGBController::usb_hid_ioctl(unsigned char* usb_buf, unsigned char* data, int data_len, + unsigned int offset, unsigned char is_in) +{ + int id; + int ret = 0; + int buf_len = usb_hid_get_report(data_len, &id); + + if(hdev == NULL) + { + free(usb_buf); + return -1; + } + + if(!is_in && data_len) + { + usb_buf[0x04] = data[0]; + } + memcpy(&usb_buf[0x08], &data_len, sizeof(data_len)); + memcpy(&usb_buf[0x1B], &data_len, sizeof(data_len)); + ret = hid_send_feature_report(hdev, usb_buf, sizeof(hid_get_packet)); + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, data, data_len); + usb_buf[0x00] = id; + if(is_in) + { + ret = hid_get_feature_report(hdev, usb_buf, buf_len); + memcpy(data, usb_buf + offset, data_len); + } + else + { + ret = hid_send_feature_report(hdev, usb_buf, buf_len); + } + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_end_packet, sizeof(hid_end_packet)); + ret = hid_get_feature_report(hdev, usb_buf, sizeof(hid_end_packet)); + free(usb_buf); + return (ret > 0) ? 0 : ret; +} + +int RealtekARGBController::usb_hid_get_report(int data_len, int* id) +{ + int retlen = 0; + + if(data_len <= REALTEK_ARGB_HID_DATALEN_CH2) + { + *id = REALTEK_ARGB_HID_ID_DATA_CH2; + retlen = REALTEK_ARGB_HID_DATALEN_CH2; + } + else if(data_len <= REALTEK_ARGB_HID_DATALEN_CH3) + { + *id = REALTEK_ARGB_HID_ID_DATA_CH3; + retlen = REALTEK_ARGB_HID_DATALEN_CH3; + } + else if(data_len <= REALTEK_ARGB_HID_DATALEN_CH4) + { + *id = REALTEK_ARGB_HID_ID_DATA_CH4; + retlen = REALTEK_ARGB_HID_DATALEN_CH4; + } + else if(data_len <= REALTEK_ARGB_HID_DATALEN_CH5) + { + *id = REALTEK_ARGB_HID_ID_DATA_CH5; + retlen = REALTEK_ARGB_HID_DATALEN_CH5; + } + else + { + *id = REALTEK_ARGB_HID_ID_DATA_CH1; + retlen = REALTEK_ARGB_HID_DATALEN_CH1; + } + return retlen; +} + +void RealtekARGBController::device_init() +{ + set_write_unlock(); + get_argbctl_hdr(); + get_argbctl_data(); + for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++) + { + int ret = set_appctl(i, REALTEK_ARGB_LED_CTL_FW); + if(!ret) + { + appctl[i] = REALTEK_ARGB_LED_CTL_FW; + } + } +} + +int RealtekARGBController::set_write_unlock() +{ + int ret = 0; + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + int data_len = 96; + unsigned int addr = 0xAC004000; + unsigned char* data = (unsigned char*)calloc(data_len, 1); + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0x92; + memcpy(&usb_buf[0x17], &addr, sizeof(addr)); + ret = usb_hid_ioctl(usb_buf, data, data_len, 0, true); + free(data); + return ret; +} + +unsigned char RealtekARGBController::get_support_openrgb() +{ + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + unsigned char is_support = 0; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0xCC; + usb_buf[0x14] = 0x04; + usb_buf[0x15] = REALTEK_ARGB_SYNC_METHOD_OPENRGB; + if(usb_hid_ioctl(usb_buf, &is_support, sizeof(is_support), 0, true)) + { + is_support = 0; + } + return is_support; +} + +std::string RealtekARGBController::get_manu_name() +{ + return StringUtils::wchar_to_char(hidinfo->manufacturer_string); +} + +std::string RealtekARGBController::get_product_name() +{ + return StringUtils::wchar_to_char(hidinfo->product_string); +} + +std::string RealtekARGBController::get_sn() +{ + return StringUtils::wchar_to_char(hidinfo->serial_number); +} + +std::string RealtekARGBController::get_dev_loc() +{ + return hidinfo->path; +} + +std::string RealtekARGBController::get_fw_ver() +{ + std::string ver = ""; + struct RealtekARGBControllerFWVersion fw_ver; + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0xA5; + if(!usb_hid_ioctl(usb_buf, (unsigned char*)&fw_ver, sizeof(fw_ver), 0, true)) + { + ver += std::to_string(fw_ver.fw_major_ver) + "." + + std::to_string(fw_ver.fw_minor_ver) + "." + + std::to_string(fw_ver.fw_extra_ver) + "." + + std::to_string(fw_ver.fw_build_date); + } + return ver; +} + +std::string RealtekARGBController::get_ic_uuid() +{ + unsigned int uuid = 0; + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0xC3; + usb_hid_ioctl(usb_buf, (unsigned char*)&uuid, sizeof(uuid), 0, true); + + return StringUtils::u32int_to_hexString(uuid); +} + +std::string RealtekARGBController::get_dev_name() +{ + bool got_custled = false; + std::string devname = get_product_name(); + + if(custled[0]) + { + got_custled = true; + } + else + { + if(!get_custled(custled, sizeof(custled))) + { + got_custled = true; + } + } + + if(got_custled) + { + if(custled[6] == REALTEK_ARGB_CUST_DEVNAME_MANU_UUID) + { + devname = get_manu_name() + get_ic_uuid(); + } + } + return devname; +} + +int RealtekARGBController::get_fix_grps() +{ + bool got_custled = false; + static int fix_grps = 0; + + if(custled[0]) + { + got_custled = true; + } + else + { + if(!get_custled(custled, sizeof(custled))) + { + got_custled = true; + } + } + + if(got_custled) + { + fix_grps = custled[4]; + } + return fix_grps; +} + +bool RealtekARGBController::get_zone_enable(int grp_num) +{ + bool got_custled = false; + bool en = false; + + if(custled[0]) + { + got_custled = true; + } + else + { + if(!get_custled(custled, sizeof(custled))) + { + got_custled = true; + } + } + + if(got_custled) + { + en = (custled[5] & (0x1 << grp_num)) ? true : false; + } + return en; +} + +int RealtekARGBController::get_argb_num(int grp_num) +{ + int num_rgb = 0; + memcpy(&num_rgb, &argbctl_hdr[32 + grp_num * 2], 2); + + return num_rgb; +} + +int RealtekARGBController::get_argb_brightness(int grp_num) +{ + int bright = 0; + memcpy(&bright, &argbctl_hdr[42 + grp_num * 2], 2); + + return bright; +} + +int RealtekARGBController::set_argb_brightness(int grp_num, unsigned short bright) +{ + memcpy(&argbctl_hdr[42 + grp_num * sizeof(bright)], &bright, sizeof(bright)); + set_argbctl_hdr(); + return 0; +} + +int RealtekARGBController::set_appctl(unsigned char grp_num, unsigned char ctl_sts) +{ + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x4C; + usb_buf[0x14] = 0x01; + usb_buf[0x15] = ctl_sts; + usb_buf[0x16] = grp_num; + return usb_hid_ioctl(usb_buf, &ctl_sts, sizeof(ctl_sts), 0, false); +} + +int RealtekARGBController::get_custled(unsigned char* cust, unsigned int cust_len) +{ + int ret = 0; + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + unsigned int addr = 0xAC004000; + unsigned int offset = 0x800; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len + offset, 1);// will release in usb_hid_ioctl function + unsigned char* data = (unsigned char*)calloc(buf_len, 1); + + memset(data, 0x00, buf_len); + memset(usb_buf, 0x00, buf_len + offset); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0x92; + memcpy(&usb_buf[0x17], &addr, sizeof(addr)); + ret = usb_hid_ioctl(usb_buf, data, buf_len, offset, true); + if(!ret) + { + memcpy(cust, &data[70], cust_len); + } + free(data); + return ret; +} + +int RealtekARGBController::get_argbctl_hdr() +{ + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0xCC; + usb_buf[0x14] = 0x02; + return usb_hid_ioctl(usb_buf, argbctl_hdr, REALTEK_ARGB_CTL_HDR_SIZE, 0, true); +} + +int RealtekARGBController::set_argbctl_hdr() +{ + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x4C; + usb_buf[0x14] = 0x02; + return usb_hid_ioctl(usb_buf, argbctl_hdr, REALTEK_ARGB_CTL_HDR_SIZE, 0, false); +} + +int RealtekARGBController::get_argbctl_data() +{ + int ret = 0; + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + int offset = REALTEK_ARGB_CTL_HDR_SIZE; + unsigned char* usb_buf; + + for(int i = 0; i < REALTEK_ARGB_NUM_ARGB_GRP; i++) + { + usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0xCC; + usb_buf[0x14] = 0x02; + memcpy(&usb_buf[0x17], &offset, sizeof(offset)); + ret = usb_hid_ioctl(usb_buf, argbctl_data[i], REALTEK_ARGB_CTL_DATA_SIZE, 0, true); + offset += REALTEK_ARGB_CTL_DATA_SIZE; + } + return ret; +} + +int RealtekARGBController::set_argbctl_data(unsigned char grp_num) +{ + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + int offset = REALTEK_ARGB_CTL_HDR_SIZE + REALTEK_ARGB_CTL_DATA_SIZE * grp_num; + unsigned char* usb_buf; + + usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x4C; + usb_buf[0x14] = 0x02; + memcpy(&usb_buf[0x17], &offset, sizeof(offset)); + return usb_hid_ioctl(usb_buf, argbctl_data[grp_num], REALTEK_ARGB_CTL_DATA_SIZE, 0, false); +} + +int RealtekARGBController::set_eff_id(unsigned char grp_num, unsigned short effid) +{ + memcpy(argbctl_data[grp_num], &effid, sizeof(effid)); + return 0; +} + +int RealtekARGBController::set_p_color(unsigned char grp_num, RGBColor color) +{ + memcpy(argbctl_data[grp_num] + 8, &color, sizeof(color)); + return 0; +} + +int RealtekARGBController::set_s_color(unsigned char grp_num, RGBColor color) +{ + memcpy(argbctl_data[grp_num] + 12, &color, sizeof(color)); + return 0; +} + +int RealtekARGBController::set_cycle(unsigned char grp_num, unsigned short cycle) +{ + memcpy(argbctl_data[grp_num] + 2, &cycle, sizeof(cycle)); + return 0; +} + +int RealtekARGBController::set_ramp(unsigned char grp_num, unsigned short ramp) +{ + memcpy(argbctl_data[grp_num] + 4, &ramp, sizeof(ramp)); + return 0; +} + +int RealtekARGBController::set_stable(unsigned char grp_num, unsigned short stable) +{ + memcpy(argbctl_data[grp_num] + 6, &stable, sizeof(stable)); + return 0; +} + +int RealtekARGBController::set_subcmd(unsigned char grp_num, int subcmd) +{ + memcpy(argbctl_data[grp_num] + 16, &subcmd, sizeof(subcmd)); + return 0; +} + +int RealtekARGBController::set_direct(unsigned char* color, int color_num, unsigned char grp_num) +{ + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + int data_len = color_num * REALTEK_ARGB_COLOR_DEPTH; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x4C; + usb_buf[0x14] = 0x03; + usb_buf[0x15] = REALTEK_ARGB_SYNC_METHOD_OPENRGB; + usb_buf[0x16] = grp_num; + memcpy(&usb_buf[0x17], &color_num, sizeof(color_num)); + return usb_hid_ioctl(usb_buf, color, data_len, 0, false); +} + +int RealtekARGBController::get_flash_argbctl_hdr(unsigned char* data) +{ + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + unsigned int addr = 0xAC000000; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0x92; + memcpy(&usb_buf[0x17], &addr, sizeof(addr)); + return usb_hid_ioctl(usb_buf, data, buf_len, 0, true); +} + +int RealtekARGBController::set_flash_argbctl_hdr(unsigned int offset, unsigned int data_len) +{ + int ret = 0; + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + unsigned int addr = 0xAC000000; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function + unsigned char* data = (unsigned char*)calloc(buf_len, 1); + memset(data, 0, buf_len); + + ret = get_flash_argbctl_hdr(data); + if(!ret) + { + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x10; + memcpy(&usb_buf[0x17], &addr, sizeof(addr)); + ret = usb_hid_ioctl(usb_buf, (unsigned char*)&addr, sizeof(addr), 0, false); + + usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x11; + memcpy(data + offset, argbctl_hdr + offset, data_len); + ret = usb_hid_ioctl(usb_buf, data, buf_len, 0, false); + } + free(data); + return ret; +} + +int RealtekARGBController::set_argb_direct(int grp_num, std::vector color_buf, unsigned short brightness) +{ + int ret = -1; + size_t color_num = color_buf.size(); + size_t buf_len = color_num * REALTEK_ARGB_COLOR_DEPTH; + unsigned char* buf; + std::lock_guard lock(my_mutex); + + if(color_num == 0) + { + goto exit; + } + + if(appctl[grp_num] != REALTEK_ARGB_LED_CTL_APP) + { + ret = set_appctl(grp_num, REALTEK_ARGB_LED_CTL_APP); + if(ret) + { + goto exit; + } + else + { + appctl[grp_num] = REALTEK_ARGB_LED_CTL_APP; + } + } + + if(prev_bright[grp_num] != brightness) + { + prev_bright[grp_num] = brightness; + ret = set_argb_brightness(grp_num, brightness << 8); + if(ret) + { + goto exit; + } + } + + buf = (uint8_t*)malloc(buf_len); + memset(buf, 0, buf_len); + for(size_t i = 0; i < color_num; i++) + { + buf[i * REALTEK_ARGB_COLOR_DEPTH + 0] = RGBGetRValue(color_buf[i]); + buf[i * REALTEK_ARGB_COLOR_DEPTH + 1] = RGBGetGValue(color_buf[i]); + buf[i * REALTEK_ARGB_COLOR_DEPTH + 2] = RGBGetBValue(color_buf[i]); + } + ret = set_direct(buf, (int)color_num, grp_num); + free(buf); +exit: + return ret; +} + +int RealtekARGBController::set_argb_effect(int grp_num, uint8_t mode, std::vector color_buf, struct RealtekARGBControllerSetEffParam* param) +{ + int ret = -1; + int cycle = MathUtils::IntInterpolate(REALTEK_ARGB_CYCLE_MAX, REALTEK_ARGB_CYCLE_MIN, 0, REALTEK_ARGB_SPEED_MAX, param->speed); + std::lock_guard lock(my_mutex); + + if(mode == REALTEK_ARGB_EFF_ALWAYS_ON || mode == REALTEK_ARGB_EFF_STACK) + { + cycle = 0; + } + + if(color_buf.size() >= 1) + { + set_p_color(grp_num, color_buf[0]); + } + if(color_buf.size() >= 2) + { + set_s_color(grp_num, color_buf[1]); + } + + set_eff_id(grp_num, mode); + set_cycle(grp_num, cycle); + set_ramp(grp_num, 0); + set_stable(grp_num, 0); + set_subcmd(grp_num, (param->dir || param->random_color) ? 0x1 : 0x0); + set_argb_brightness(grp_num, param->brightness << 8); + set_argbctl_data(grp_num); + + ret = set_appctl(grp_num, REALTEK_ARGB_LED_CTL_FW); + if(!ret) + { + appctl[grp_num] = REALTEK_ARGB_LED_CTL_FW; + } + + return ret; +} + +int RealtekARGBController::set_argb_num(int grp_num, unsigned short new_num) +{ + memcpy(&argbctl_hdr[32 + grp_num * sizeof(new_num)], &new_num, sizeof(new_num)); + set_flash_argbctl_hdr(32 + grp_num * sizeof(new_num), sizeof(new_num)); + return 0; +} + +int RealtekARGBController::device_reboot() +{ + std::lock_guard lock(my_mutex); + int ret = 0; + int buf_len = REALTEK_ARGB_HID_DATALEN_CH1; + int reboot_type = 1; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_send/usb_hid_get function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x30; + usb_buf[0x14] = reboot_type; + ret = usb_hid_ioctl(usb_buf, (unsigned char*)&reboot_type, sizeof(reboot_type), 0, false); + + hid_close(hdev); + hdev = NULL; + return ret; +} + +void RealtekARGBController::device_rescan_trigger() +{ + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RealtekARGBController::KeepaliveThreadFunction, this); +} + +void RealtekARGBController::device_rescan() +{ + hid_device_info* info_full = hid_enumerate(REALTEK_ARGB_VID, REALTEK_ARGB_PID); + hid_device_info* info_temp = info_full; + + while(info_temp) + { + if(info_temp->vendor_id == REALTEK_ARGB_VID && + info_temp->product_id == REALTEK_ARGB_PID && + info_temp->usage == REALTEK_ARGB_HID2SCSI_USAGE && + info_temp->usage_page == REALTEK_ARGB_HID2SCSI_PG) + { + hid_device* dev_temp = hid_open_path(info_temp->path); + if(dev_temp) + { + hdev = dev_temp; + if(get_support_openrgb()) + { + device_init(); + break; + } + else + { + hid_close(hdev); + hdev = NULL; + } + } + } + info_temp = info_temp->next; + } + hid_free_enumeration(info_full); +} diff --git a/Controllers/RealtekARGBController/RealtekARGBController.h b/Controllers/RealtekARGBController/RealtekARGBController.h new file mode 100644 index 0000000..63c6b4c --- /dev/null +++ b/Controllers/RealtekARGBController/RealtekARGBController.h @@ -0,0 +1,182 @@ +/*---------------------------------------------------------*\ +| RealtekARGBController.h | +| | +| Controller for Realtek USB ARGB ICs | +| | +| Jerry Fan (JerryFan0612) 13 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "RGBController.h" + +#define REALTEK_ARGB_VID 0x0BDA +#define REALTEK_ARGB_PID 0x9209 +#define REALTEK_ARGB_HID2SCSI_PG 0xFF00 +#define REALTEK_ARGB_HID2SCSI_USAGE 0x0001 + +#define REALTEK_ARGB_NUM_ARGB_GRP 5 +#define REALTEK_ARGB_BRINTF_TYPE_HID 1 +#define REALTEK_ARGB_SYNC_METHOD_OPENRGB 6 +#define REALTEK_ARGB_COLOR_DEPTH 3 +#define REALTEK_ARGB_MAX 400 + +#define REALTEK_ARGB_CTL_HDR_SIZE 80 +#define REALTEK_ARGB_CTL_DATA_SIZE 96 + +#define REALTEK_ARGB_HID_DATALEN_CH1 4096 +#define REALTEK_ARGB_HID_DATALEN_CH2 64 +#define REALTEK_ARGB_HID_DATALEN_CH3 512 +#define REALTEK_ARGB_HID_DATALEN_CH4 1200 +#define REALTEK_ARGB_HID_DATALEN_CH5 2048 + +#define REALTEK_ARGB_HID_ID_DATA_CH1 0x57 +#define REALTEK_ARGB_HID_ID_DATA_CH2 0x59 +#define REALTEK_ARGB_HID_ID_DATA_CH3 0x5A +#define REALTEK_ARGB_HID_ID_DATA_CH4 0x5B +#define REALTEK_ARGB_HID_ID_DATA_CH5 0x5C + +enum REALTEK_ARGB_LED_CTL +{ + REALTEK_ARGB_LED_CTL_FW, + REALTEK_ARGB_LED_CTL_APP, + REALTEK_ARGB_LED_CTL_LAMP, +}; + +enum REALTEK_ARGB_EFFECT_ID +{ + REALTEK_ARGB_EFF_NULL, + REALTEK_ARGB_EFF_ALWAYS_ON, + REALTEK_ARGB_EFF_BLINK, + REALTEK_ARGB_EFF_BREATH, + REALTEK_ARGB_EFF_SPECTRUM, + REALTEK_ARGB_EFF_SCROLL, + REALTEK_ARGB_EFF_RAINBOW_SCROLL, + REALTEK_ARGB_EFF_RUNNING_WATER, + REALTEK_ARGB_EFF_SLIDING, + REALTEK_ARGB_EFF_NEWTON_CRADLE, + REALTEK_ARGB_EFF_METEOR, + REALTEK_ARGB_EFF_RAINBOW_SLIDING, + REALTEK_ARGB_EFF_RAINBOW_FADE_SLIDING, + REALTEK_ARGB_EFF_WIDE_SLIDING, + REALTEK_ARGB_EFF_DOT_MATRIX, // reserved: Not supported by OpenRGB + REALTEK_ARGB_EFF_DOT_MATRIX_BREATH, // reserved: Not supported by OpenRGB + REALTEK_ARGB_EFF_ZIGZAG, + REALTEK_ARGB_EFF_STARRY_NIGHT, + REALTEK_ARGB_EFF_STACK, +}; + +enum REALTEK_ARGB_SPEED +{ + REALTEK_ARGB_SPEED_MIN = 1, + REALTEK_ARGB_SPEED_NORMAL = 50, + REALTEK_ARGB_SPEED_MAX = 100, +}; + +enum REALTEK_ARGB_CYCLE_MS +{ + REALTEK_ARGB_CYCLE_MIN = 200, + REALTEK_ARGB_CYCLE_NORMAL = 2000, + REALTEK_ARGB_CYCLE_MAX = 10000, +}; + +enum REALTEK_ARGB_CUST_DEVNAME +{ + REALTEK_ARGB_CUST_DEVNAME_NULL = 0x0, + REALTEK_ARGB_CUST_DEVNAME_MANU_UUID = 0x1, +}; + +struct RealtekARGBControllerSetEffParam +{ + unsigned int speed; + unsigned short brightness; + unsigned char dir; + unsigned char random_color; +}; + +struct RealtekARGBControllerFWVersion +{ + unsigned int fw_major_ver; + unsigned int fw_minor_ver; + unsigned int fw_extra_ver; + unsigned int fw_build_ver; + unsigned int fw_build_date; +}; + +class RealtekARGBController +{ +public: + RealtekARGBController(hid_device* dev, hid_device_info* info); + ~RealtekARGBController(); + + unsigned char get_support_openrgb(); + std::string get_manu_name(); + std::string get_product_name(); + std::string get_sn(); + std::string get_dev_loc(); + std::string get_fw_ver(); + std::string get_ic_uuid(); + std::string get_dev_name(); + int get_fix_grps(); + bool get_zone_enable(int grp_num); + int get_argb_num(int grp_num); + int get_argb_brightness(int grp_num); + int set_argb_brightness(int grp_num, unsigned short bright); + + int set_argb_direct(int grp_num, std::vector color_buf, unsigned short brightness); + int set_argb_effect(int grp_num, uint8_t mode, std::vector color_buf, struct RealtekARGBControllerSetEffParam* param); + int set_argb_num(int grp_num, unsigned short new_num); + int device_reboot(); + void device_rescan_trigger(); +private: + hid_device* hdev; + std::mutex my_mutex; + unsigned char argbctl_hdr[REALTEK_ARGB_CTL_HDR_SIZE] = {0}; + unsigned char* argbctl_data[REALTEK_ARGB_NUM_ARGB_GRP] = {0}; + hid_device_info* hidinfo = NULL; + int appctl[REALTEK_ARGB_NUM_ARGB_GRP] = {REALTEK_ARGB_LED_CTL_FW}; + unsigned short prev_bright[REALTEK_ARGB_NUM_ARGB_GRP] = {0}; + std::atomic keepalive_thread_run; + std::thread* keepalive_thread; + unsigned char custled[16] = {0}; + + void KeepaliveThreadFunction(); + + int usb_hid_ioctl(unsigned char* usb_buf, unsigned char* data, int data_len, + unsigned int offset, unsigned char is_in); + int usb_hid_get_report(int data_len, int* id); + + void device_init(); + int set_write_unlock(); + int set_appctl(unsigned char grp_num, unsigned char ctl_sts); + int set_argbctl_data(unsigned char* data, int data_len, int offset); + int get_custled(unsigned char* cust, unsigned int cust_len); + int get_argbctl_hdr(); + int set_argbctl_hdr(); + int get_argbctl_data(); + int set_argbctl_data(unsigned char grp_num); + int set_eff_id(unsigned char grp_num, unsigned short effid); + int set_p_color(unsigned char grp_num, RGBColor color); + int set_s_color(unsigned char grp_num, RGBColor color); + int set_cycle(unsigned char grp_num, unsigned short cycle); + int set_ramp(unsigned char grp_num, unsigned short ramp); + int set_stable(unsigned char grp_num, unsigned short stable); + int set_subcmd(unsigned char grp_num, int subcmd); + int set_direct(unsigned char* color, int color_num, unsigned char grp_num); + + int get_flash_argbctl_hdr(unsigned char* data); + int set_flash_argbctl_hdr(unsigned int offset, unsigned int data_len); + + void device_rescan(); +}; diff --git a/Controllers/RealtekARGBController/RealtekARGBControllerDetect.cpp b/Controllers/RealtekARGBController/RealtekARGBControllerDetect.cpp new file mode 100644 index 0000000..5ee8bd1 --- /dev/null +++ b/Controllers/RealtekARGBController/RealtekARGBControllerDetect.cpp @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| RealtekARGBControllerDetect.cpp | +| | +| Detector for Realtek USB ARGB ICs | +| | +| Jerry Fan (JerryFan0612) 13 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBController.h" +#include "RGBController_RealtekARGB.h" + +/******************************************************************************************\ +* * +* DetectRealtekARGBControllers * +* * +* Tests the USB address to see if an Realtek ARGB controller exists there * +* * +\******************************************************************************************/ +void DetectRealtekARGBControllers(hid_device_info* info, const std::string& /*name*/) +{ + RealtekARGBController* controller = NULL; + RGBController_RealtekARGB* rgb_controller = NULL; + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + controller = new RealtekARGBController(dev, info); + if(controller->get_support_openrgb()) + { + rgb_controller = new RGBController_RealtekARGB(controller); + if(rgb_controller->type != DEVICE_TYPE_UNKNOWN) + { + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + delete rgb_controller; + } + } + else + { + delete controller; + } + } + return; +} + +REGISTER_HID_DETECTOR_PU("RTL9209", DetectRealtekARGBControllers, REALTEK_ARGB_VID, REALTEK_ARGB_PID, REALTEK_ARGB_HID2SCSI_PG, REALTEK_ARGB_HID2SCSI_USAGE); diff --git a/Controllers/RealtekBridgeController/RGBController_RealtekBridge.cpp b/Controllers/RealtekBridgeController/RGBController_RealtekBridge.cpp new file mode 100644 index 0000000..3f3dd5e --- /dev/null +++ b/Controllers/RealtekBridgeController/RGBController_RealtekBridge.cpp @@ -0,0 +1,303 @@ +/*---------------------------------------------------------*\ +| RGBController_RealtekBridge.cpp | +| | +| Controller for Realtek USB to SSD Bridge ICs | +| | +| Jerry Fan (JerryFan0612) 13 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RealtekBridge.h" + +/**------------------------------------------------------------------*\ + @name Realtek Bridge Device + @category Storage + @type USB + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors RealtekBridgeControllerDetect + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RealtekBridge::RGBController_RealtekBridge(RealtekBridgeController* controller_ptr) +{ + controller = controller_ptr; + name = controller_ptr->get_product_name(); + vendor = controller_ptr->get_manu_name(); + location = controller_ptr->get_dev_loc(); + serial = controller_ptr->get_sn(); + version = controller_ptr->get_fw_ver(); + description = vendor + "Storage Device"; + type = DEVICE_TYPE_STORAGE; + + SetupModes(); + SetupZones(); +} + +RGBController_RealtekBridge::~RGBController_RealtekBridge() +{ + delete controller; +} + +void RGBController_RealtekBridge::SetupModes() +{ + int brightness = controller->get_argb_brightness() >> 8; + + mode Direct; + Direct.name = "Direct"; + Direct.value = REALTEK_BRIDGE_LED_EFF_NONE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 255; + Direct.brightness = brightness; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = REALTEK_BRIDGE_LED_EFF_ALWAYS; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + Static.brightness_min = 0; + Static.brightness_max = 255; + Static.brightness = brightness; + modes.push_back(Static); + + mode Blink; + Blink.name = "Blink"; + Blink.value = REALTEK_BRIDGE_LED_EFF_BLINK; + Blink.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Blink.speed_min = REALTEK_BRIDGE_SPEED_MIN; + Blink.speed_max = REALTEK_BRIDGE_SPEED_MAX; + Blink.speed = REALTEK_BRIDGE_SPEED_NORMAL; + Blink.colors_min = 1; + Blink.colors_max = 1; + Blink.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blink.colors.resize(1); + Blink.brightness_min = 0; + Blink.brightness_max = 255; + Blink.brightness = brightness; + modes.push_back(Blink); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = REALTEK_BRIDGE_LED_EFF_BREATHE; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.speed_min = REALTEK_BRIDGE_SPEED_MIN; + Breathing.speed_max = REALTEK_BRIDGE_SPEED_MAX; + Breathing.speed = REALTEK_BRIDGE_SPEED_NORMAL; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors.resize(1); + Breathing.brightness_min = 0; + Breathing.brightness_max = 255; + Breathing.brightness = brightness; + modes.push_back(Breathing); + + mode Spectrum; + Spectrum.name = "Spectrum"; + Spectrum.value = REALTEK_BRIDGE_LED_EFF_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Spectrum.speed_min = REALTEK_BRIDGE_SPEED_MIN; + Spectrum.speed_max = REALTEK_BRIDGE_SPEED_MAX; + Spectrum.speed = REALTEK_BRIDGE_SPEED_NORMAL; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.brightness_min = 0; + Spectrum.brightness_max = 255; + Spectrum.brightness = brightness; + modes.push_back(Spectrum); + + mode Scroll; + Scroll.name = "Scroll"; + Scroll.value = REALTEK_BRIDGE_LED_EFF_SCROLL; + Scroll.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Scroll.speed_min = REALTEK_BRIDGE_SPEED_MIN; + Scroll.speed_max = REALTEK_BRIDGE_SPEED_MAX; + Scroll.speed = REALTEK_BRIDGE_SPEED_NORMAL; + Scroll.colors_min = 1; + Scroll.colors_max = 1; + Scroll.color_mode = MODE_COLORS_MODE_SPECIFIC; + Scroll.colors.resize(1); + Scroll.brightness_min = 0; + Scroll.brightness_max = 255; + Scroll.brightness = brightness; + modes.push_back(Scroll); + + mode RainbowScroll; + RainbowScroll.name = "Rainbow Scroll"; + RainbowScroll.value = REALTEK_BRIDGE_LED_EFF_RAINBOW_SCROLL; + RainbowScroll.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + RainbowScroll.speed_min = REALTEK_BRIDGE_SPEED_MIN; + RainbowScroll.speed_max = REALTEK_BRIDGE_SPEED_MAX; + RainbowScroll.speed = REALTEK_BRIDGE_SPEED_NORMAL; + RainbowScroll.color_mode = MODE_COLORS_NONE; + RainbowScroll.brightness_min = 0; + RainbowScroll.brightness_max = 255; + RainbowScroll.brightness = brightness; + modes.push_back(RainbowScroll); + + mode RunningWater; + RunningWater.name = "Running Water"; + RunningWater.value = REALTEK_BRIDGE_LED_EFF_RUNNING_WATER; + RunningWater.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + RunningWater.speed_min = REALTEK_BRIDGE_SPEED_MIN; + RunningWater.speed_max = REALTEK_BRIDGE_SPEED_MAX; + RunningWater.speed = REALTEK_BRIDGE_SPEED_NORMAL; + RunningWater.colors_min = 1; + RunningWater.colors_max = 1; + RunningWater.color_mode = MODE_COLORS_MODE_SPECIFIC; + RunningWater.colors.resize(1); + RunningWater.brightness_min = 0; + RunningWater.brightness_max = 255; + RunningWater.brightness = brightness; + modes.push_back(RunningWater); + + mode Sliding; + Sliding.name = "Sliding"; + Sliding.value = REALTEK_BRIDGE_LED_EFF_SLIDING; + Sliding.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Sliding.speed_min = REALTEK_BRIDGE_SPEED_MIN; + Sliding.speed_max = REALTEK_BRIDGE_SPEED_MAX; + Sliding.speed = REALTEK_BRIDGE_SPEED_NORMAL; + Sliding.color_mode = MODE_COLORS_NONE; + Sliding.brightness_min = 0; + Sliding.brightness_max = 255; + Sliding.brightness = brightness; + modes.push_back(Sliding); + + mode NewtonCradle; + NewtonCradle.name = "Newton Cradle"; + NewtonCradle.value = REALTEK_BRIDGE_LED_EFF_NEWTON_CRADLE; + NewtonCradle.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + NewtonCradle.speed_min = REALTEK_BRIDGE_SPEED_MIN; + NewtonCradle.speed_max = REALTEK_BRIDGE_SPEED_MAX; + NewtonCradle.speed = REALTEK_BRIDGE_SPEED_NORMAL; + NewtonCradle.color_mode = MODE_COLORS_NONE; + NewtonCradle.brightness_min = 0; + NewtonCradle.brightness_max = 255; + NewtonCradle.brightness = brightness; + modes.push_back(NewtonCradle); + + mode Meteor; + Meteor.name = "Meteor"; + Meteor.value = REALTEK_BRIDGE_LED_EFF_METEOR; + Meteor.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Meteor.speed_min = REALTEK_BRIDGE_SPEED_MIN; + Meteor.speed_max = REALTEK_BRIDGE_SPEED_MAX; + Meteor.speed = REALTEK_BRIDGE_SPEED_NORMAL; + Meteor.colors_min = 1; + Meteor.colors_max = 1; + Meteor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Meteor.colors.resize(1); + Meteor.brightness_min = 0; + Meteor.brightness_max = 255; + Meteor.brightness = brightness; + modes.push_back(Meteor); +} + +void RGBController_RealtekBridge::SetupZones() +{ + zone argb_zone; + + argb_zone.name = "strip"; + argb_zone.type = ZONE_TYPE_LINEAR; + argb_zone.leds_min = controller->get_argb_num(); + argb_zone.leds_max = controller->get_argb_num(); + argb_zone.leds_count = controller->get_argb_num(); + argb_zone.matrix_map = NULL; + + zones.push_back(argb_zone); + + for(unsigned int led_idx = 0; led_idx < argb_zone.leds_count; led_idx++) + { + led StripLED; + StripLED.name = "led "; + StripLED.name.append(std::to_string(led_idx + 1)); + leds.push_back(StripLED); + } + + SetupColors(); +} + +void RGBController_RealtekBridge::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RealtekBridge::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_RealtekBridge::UpdateZoneLEDs(int /*zone*/) +{ + unsigned short brightness = 0xFF; + mode& curr_mode = modes[active_mode]; + + if(curr_mode.color_mode == MODE_COLORS_PER_LED && + curr_mode.value == REALTEK_BRIDGE_LED_EFF_NONE) //direct mode + { + if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS) + { + brightness = curr_mode.brightness; + } + controller->set_argb_direct(colors, brightness); + } + else + { + UpdateSingleLED(0); + } +} + +void RGBController_RealtekBridge::UpdateSingleLED(int /*led*/) +{ + unsigned char speed = REALTEK_BRIDGE_SPEED_NORMAL; + unsigned char dir = 0; + unsigned short brightness = 0xFF; + mode& curr_mode = modes[active_mode]; + std::vector rtk_colors = curr_mode.colors; + + if(curr_mode.flags & MODE_FLAG_HAS_SPEED) + { + speed = curr_mode.speed; + } + if(curr_mode.flags & MODE_FLAG_HAS_BRIGHTNESS) + { + brightness = curr_mode.brightness; + } + if(curr_mode.flags & MODE_FLAG_HAS_DIRECTION_LR) + { + if(curr_mode.direction == MODE_DIRECTION_RIGHT) + { + dir = 1; + } + } + + if(curr_mode.color_mode == MODE_COLORS_PER_LED) + { + rtk_colors = colors; + } + else if(curr_mode.color_mode == MODE_COLORS_NONE) + { + rtk_colors.clear(); + } + + controller->set_argb_effect(curr_mode.value, rtk_colors, speed, brightness); +} + +void RGBController_RealtekBridge::DeviceUpdateMode() +{ + if(modes[active_mode].value != REALTEK_BRIDGE_LED_EFF_NONE) + { + DeviceUpdateLEDs(); + } +} diff --git a/Controllers/RealtekBridgeController/RGBController_RealtekBridge.h b/Controllers/RealtekBridgeController/RGBController_RealtekBridge.h new file mode 100644 index 0000000..f4e2674 --- /dev/null +++ b/Controllers/RealtekBridgeController/RGBController_RealtekBridge.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_RealtekBridge.h | +| | +| Controller for Realtek USB to SSD Bridge ICs | +| | +| Jerry Fan (JerryFan0612) 13 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RealtekBridgeController.h" + +class RGBController_RealtekBridge : public RGBController +{ +public: + RGBController_RealtekBridge(RealtekBridgeController* controller_ptr); + ~RGBController_RealtekBridge(); + + void SetupModes(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + +private: + RealtekBridgeController* controller; +}; diff --git a/Controllers/RealtekBridgeController/RealtekBridgeController.cpp b/Controllers/RealtekBridgeController/RealtekBridgeController.cpp new file mode 100644 index 0000000..1ff19b9 --- /dev/null +++ b/Controllers/RealtekBridgeController/RealtekBridgeController.cpp @@ -0,0 +1,802 @@ +/*---------------------------------------------------------*\ +| RealtekBridgeController.cpp | +| | +| Controller for Realtek USB to SSD Bridge ICs | +| | +| Jerry Fan (JerryFan0612) 13 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RealtekBridgeController.h" +#include "hsv.h" +#include +#include + +static const unsigned char hid_set_packet[] = +{ + 0x56, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xE3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char hid_get_packet[] = +{ + 0x56, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x10, 0xE2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char hid_end_packet[] = +{ + 0x58, 0x53, 0x42, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +RealtekBridgeController::RealtekBridgeController(hid_device* dev, hid_device_info* info) +{ + hdev = dev; + hidinfo = info; + brctl_data = (unsigned char*)calloc(REALTEK_BRIDGE_CTL_DATA_SIZE, 1); + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + + set_write_unlock(); + get_brctl_hdr(); + get_brctl_data(); + set_appctl(true); +} + +RealtekBridgeController::~RealtekBridgeController() +{ + if(hdev) + { + set_appctl(false); + hid_close(hdev); + hdev = NULL; + } + memset(brctl_hdr, 0, REALTEK_BRIDGE_CTL_HDR_SIZE); + free(brctl_data); +} + +int RealtekBridgeController::usb_hid_ioctl(unsigned char* usb_buf, unsigned char* data, int data_len, + unsigned int offset, unsigned char is_in) +{ + int id; + int ret = 0; + int buf_len = usb_hid_get_report(data_len, &id); + + if(!is_in && data_len) + { + usb_buf[0x04] = data[0]; + } + memcpy(&usb_buf[0x08], &data_len, sizeof(data_len)); + memcpy(&usb_buf[0x1B], &data_len, sizeof(data_len)); + ret = hid_send_feature_report(hdev, usb_buf, sizeof(hid_get_packet)); + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, data, data_len); + usb_buf[0x00] = id; + if(is_in) + { + ret = hid_get_feature_report(hdev, usb_buf, buf_len); + memcpy(data, usb_buf + offset, data_len); + } + else + { + ret = hid_send_feature_report(hdev, usb_buf, buf_len); + } + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_end_packet, sizeof(hid_end_packet)); + ret = hid_get_feature_report(hdev, usb_buf, sizeof(hid_end_packet)); + free(usb_buf); + return (ret > 0) ? 0 : ret; +} + +int RealtekBridgeController::usb_hid_get_report(int data_len, int* id) +{ + int retlen = 0; + + if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH2) + { + *id = REALTEK_BRIDGE_HID_ID_DATA_CH2; + retlen = REALTEK_BRIDGE_HID_DATALEN_CH2; + } + else if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH3) + { + *id = REALTEK_BRIDGE_HID_ID_DATA_CH3; + retlen = REALTEK_BRIDGE_HID_DATALEN_CH3; + } + else if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH4) + { + *id = REALTEK_BRIDGE_HID_ID_DATA_CH4; + retlen = REALTEK_BRIDGE_HID_DATALEN_CH4; + } + else if(data_len <= REALTEK_BRIDGE_HID_DATALEN_CH5) + { + *id = REALTEK_BRIDGE_HID_ID_DATA_CH5; + retlen = REALTEK_BRIDGE_HID_DATALEN_CH5; + } + else + { + *id = REALTEK_BRIDGE_HID_ID_DATA_CH1; + retlen = REALTEK_BRIDGE_HID_DATALEN_CH1; + } + return retlen; +} + +int RealtekBridgeController::set_write_unlock() +{ + int ret = 0; + int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1; + int data_len = 96; + unsigned int addr = 0xAC004000; + unsigned char* data = (unsigned char*)calloc(data_len, 1); + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0x92; + memcpy(&usb_buf[0x17], &addr, sizeof(addr)); + ret = usb_hid_ioctl(usb_buf, data, data_len, 0, true); + free(data); + return ret; +} + +int RealtekBridgeController::get_brctl_hdr() +{ + int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0xCC; + usb_buf[0x14] = 0x02; + return usb_hid_ioctl(usb_buf, brctl_hdr, REALTEK_BRIDGE_CTL_HDR_SIZE, 0, true); +} + +int RealtekBridgeController::set_brctl_hdr() +{ + int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x4C; + usb_buf[0x14] = 0x02; + return usb_hid_ioctl(usb_buf, brctl_hdr, REALTEK_BRIDGE_CTL_HDR_SIZE, 0, false); +} + +int RealtekBridgeController::get_brctl_data() +{ + int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1; + int offset = REALTEK_BRIDGE_CTL_HDR_SIZE; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0xCC; + usb_buf[0x14] = 0x02; + memcpy(&usb_buf[0x17], &offset, sizeof(offset)); + return usb_hid_ioctl(usb_buf, brctl_data, REALTEK_BRIDGE_CTL_DATA_SIZE, 0, true); +} + +int RealtekBridgeController::set_brctl_data() +{ + int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1; + int offset = REALTEK_BRIDGE_CTL_HDR_SIZE; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x4C; + usb_buf[0x14] = 0x02; + memcpy(&usb_buf[0x17], &offset, sizeof(offset)); + return usb_hid_ioctl(usb_buf, brctl_data, REALTEK_BRIDGE_CTL_DATA_SIZE, 0, false); +} + +unsigned char RealtekBridgeController::get_support_openrgb() +{ + int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1; + unsigned char is_support = 0; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0xCC; + usb_buf[0x14] = 0x04; + usb_buf[0x15] = REALTEK_BRIDGE_SYNC_METHOD_OPENRGB; + if(usb_hid_ioctl(usb_buf, &is_support, sizeof(is_support), 0, true)) + { + is_support = 0; + } + return is_support; +} + +std::string RealtekBridgeController::get_manu_name() +{ + return StringUtils::wchar_to_char(hidinfo->manufacturer_string); +} + +std::string RealtekBridgeController::get_product_name() +{ + return StringUtils::wchar_to_char(hidinfo->product_string); +} + +std::string RealtekBridgeController::get_sn() +{ + return StringUtils::wchar_to_char(hidinfo->serial_number); +} + +std::string RealtekBridgeController::get_dev_loc() +{ + return hidinfo->path; +} + +std::string RealtekBridgeController::get_fw_ver() +{ + struct RealtekBridgeControllerFWVersion fw_ver; + std::string ver = ""; + int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_get_packet, sizeof(hid_get_packet)); + usb_buf[0x13] = 0xA5; + if(!usb_hid_ioctl(usb_buf, (unsigned char*)&fw_ver, sizeof(fw_ver), 0, true)) + { + ver += std::to_string(fw_ver.fw_major_ver) + "." + + std::to_string(fw_ver.fw_minor_ver) + "." + + std::to_string(fw_ver.fw_extra_ver) + "." + + std::to_string(fw_ver.fw_build_date); + } + return ver; +} + +int RealtekBridgeController::get_argb_num() +{ + int num_rgb = 0; + memcpy(&num_rgb, &brctl_hdr[24], sizeof(num_rgb)); + + return num_rgb; +} + +int RealtekBridgeController::get_argb_brightness() +{ + int bright = 0; + memcpy(&bright, &brctl_hdr[28], 2); + + return bright; +} + +int RealtekBridgeController::set_argb_brightness(unsigned short bright) +{ + memcpy(&brctl_hdr[28], &bright, sizeof(bright)); + set_brctl_hdr(); + return 0; +} + +int RealtekBridgeController::set_appctl(unsigned char ctl_sts) +{ + int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x4C; + usb_buf[0x14] = 0x01; + usb_buf[0x15] = ctl_sts; + return usb_hid_ioctl(usb_buf, &ctl_sts, sizeof(ctl_sts), 0, false); +} + +int RealtekBridgeController::set_direct(unsigned char* color, int color_num) +{ + int buf_len = REALTEK_BRIDGE_HID_DATALEN_CH1; + int data_len = color_num * REALTEK_BRIDGE_COLOR_DEPTH; + unsigned char* usb_buf = (unsigned char*)calloc(buf_len, 1);// will release in usb_hid_ioctl function + + memset(usb_buf, 0x00, buf_len); + memcpy(usb_buf, hid_set_packet, sizeof(hid_set_packet)); + usb_buf[0x13] = 0x4C; + usb_buf[0x14] = 0x03; + usb_buf[0x15] = REALTEK_BRIDGE_SYNC_METHOD_OPENRGB; + memcpy(&usb_buf[0x17], &color_num, sizeof(color_num)); + return usb_hid_ioctl(usb_buf, color, data_len, 0, false); +} + +int RealtekBridgeController::eff_set_always_on(RGBColor rgb) +{ + int i; + int j; + int row = 1; + int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW; + int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32; + + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + for(i = 0; i < get_argb_num(); i++) + { + brctl_data[i * single_size + 0] = 1; + brctl_data[i * single_size + 1] = row; + + for(j = 0; j < row; j++) + { + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb); + } + } + return set_brctl_data(); +} + +int RealtekBridgeController::eff_set_blink(RGBColor rgb, int cycle) +{ + int i; + int j; + int row = 2; + int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW; + int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32; + + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + for(i = 0; i < get_argb_num(); i++) + { + brctl_data[i * single_size + 0] = 2; + brctl_data[i * single_size + 1] = row; + memcpy(&brctl_data[i * single_size + 2], &cycle, 2); + brctl_data[i * single_size + 8] = 1; + + for(j = 0; j < row; j++) + { + if((j & 0x1) == 0) + { + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb); + } + } + } + return set_brctl_data(); +} + +int RealtekBridgeController::eff_set_breathe(RGBColor rgb, int cycle) +{ + int i; + int j; + int row = 2; + int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW; + int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32; + + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + for(i = 0; i < get_argb_num(); i++) + { + brctl_data[i * single_size + 0] = 4; + brctl_data[i * single_size + 1] = row; + memcpy(&brctl_data[i * single_size + 2], &cycle, 2); + brctl_data[i * single_size + 8] = 2; + + for(j = 0; j < row; j++) + { + if((j & 0x1) == 0) + { + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb); + } + } + } + return set_brctl_data(); +} + +int RealtekBridgeController::eff_set_spectrum(int cycle) +{ + int i; + int j; + int row = 6; + int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW; + int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32; + static const RGBColor rainbow_table[6] = {0x0000FF, 0x0050FF, 0x0080FF, 0x00FF00, 0xFF0000, 0xFF0080}; + + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + for(i = 0; i < get_argb_num(); i++) + { + brctl_data[i * single_size + 0] = 4; + brctl_data[i * single_size + 1] = row; + memcpy(&brctl_data[i * single_size + 2], &cycle, 2); + brctl_data[i * single_size + 8] = 3; + + for(j = 0; j < row; j++) + { + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rainbow_table[j]); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rainbow_table[j]); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rainbow_table[j]); + } + } + return set_brctl_data(); +} + +int RealtekBridgeController::eff_set_scroll(RGBColor rgb, int cycle) +{ + int i; + int j; + int start_idx = -1; + int row = get_argb_num() * 2 - 2; + int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW; + int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32; + + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + for(i = 0; i < get_argb_num(); i++) + { + brctl_data[i * single_size + 0] = 2; + brctl_data[i * single_size + 1] = row; + memcpy(&brctl_data[i * single_size + 2], &cycle, 2); + brctl_data[i * single_size + 8] = 4; + + if(start_idx == -1) + { + start_idx = i; + } + j = i - start_idx; + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb); + if(i != start_idx) + { + j = row - (i - start_idx); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb); + } + } + return set_brctl_data(); +} + +int RealtekBridgeController::eff_set_rainbow_scroll(int cycle) +{ + int i; + int j; + int start_idx = -1; + int row = get_argb_num() * 2 - 2; + int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW; + int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32; + RGBColor slide_table[REALTEK_BRIDGE_MAX_ARGB_NUM] = {0}; + hsv_t hsv_color; + + hsv_color.saturation = 255; + hsv_color.value = 255; + for(i = 0; i < get_argb_num(); i++) + { + hsv_color.hue = (i % get_argb_num()) * (360 / get_argb_num()); + slide_table[i] = hsv2rgb(&hsv_color); + } + + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + for(i = 0; i < get_argb_num(); i++) + { + brctl_data[i * single_size + 0] = 2; + brctl_data[i * single_size + 1] = row; + memcpy(&brctl_data[i * single_size + 2], &cycle, 2); + brctl_data[i * single_size + 8] = 0x85; + + if(start_idx == -1) + { + start_idx = i; + } + j = i - start_idx; + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(slide_table[i]); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(slide_table[i]); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(slide_table[i]); + if(i != start_idx) + { + j = row - (i - start_idx); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(slide_table[i]); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(slide_table[i]); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(slide_table[i]); + } + } + return set_brctl_data(); +} + +int RealtekBridgeController::eff_set_running_water(RGBColor rgb, int cycle) +{ + int i; + int j; + int start_idx = -1; + int row = get_argb_num() * 2; + int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW; + int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32; + + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + for(i = 0; i < get_argb_num(); i++) + { + brctl_data[i * single_size + 0] = 2; + brctl_data[i * single_size + 1] = row; + memcpy(&brctl_data[i * single_size + 2], &cycle, 2); + brctl_data[i * single_size + 8] = 5; + + if(start_idx == -1) + { + start_idx = i; + } + + for(j = i - start_idx; j < row / 2; j++) + { + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb); + } + } + return set_brctl_data(); +} + +int RealtekBridgeController::eff_set_sliding(int cycle) +{ + int i; + int j; + int start_idx = -1; + int row = get_argb_num(); + int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW; + int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32; + RGBColor slide_table[REALTEK_BRIDGE_MAX_ARGB_NUM] = {0}; + RGBColor rgb = 0; + hsv_t hsv_color; + + hsv_color.saturation = 255; + hsv_color.value = 255; + for(i = 0; i < get_argb_num(); i++) + { + hsv_color.hue = (i % get_argb_num()) * (360 / get_argb_num()); + slide_table[i] = hsv2rgb(&hsv_color); + } + + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + for(i = 0; i < get_argb_num(); i++) + { + brctl_data[i * single_size + 0] = 4; + brctl_data[i * single_size + 1] = row; + memcpy(&brctl_data[i * single_size + 2], &cycle, 2); + brctl_data[i * single_size + 8] = 0x83; + + if(start_idx == -1) + { + start_idx = i; + } + + for(j = 0; j < row; j++) + { + rgb = slide_table[(i - start_idx + j) % row]; + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb); + } + } + return set_brctl_data(); +} + +int RealtekBridgeController::eff_set_newton_cradle(int cycle) +{ + int i; + int j; + int start_idx = -1; + int row; + int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW; + int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32; + RGBColor rgb = 0; + static const int const_ball_num = 3; + static const RGBColor ball_color[] = {0x0080FF, 0x0000FF, 0x00FF00, 0xFF0000}; + static const int mid = get_argb_num() >> 1; + + if(get_argb_num() <= const_ball_num) + { + return 0; + } + + row = (get_argb_num() - const_ball_num) * 2; + + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + for(i = 0; i < get_argb_num(); i++) + { + brctl_data[i * single_size + 0] = 2; + brctl_data[i * single_size + 1] = row; + memcpy(&brctl_data[i * single_size + 2], &cycle, 2); + brctl_data[i * single_size + 8] = 0x84; + + if(start_idx == -1) + { + start_idx = i; + } + + for(j = 0; j < row; j++) + { + rgb = 0; + if(i - start_idx > mid - 2 && + i - start_idx < mid + 2) + { + if(j < mid - 1 || j > row - mid) + { + if(i - start_idx == mid - 1) + { + rgb = ball_color[1]; + } + else if(i - start_idx == mid) + { + rgb = ball_color[2]; + } + else + { + rgb = ball_color[3]; + } + } + else + { + if(i - start_idx == mid - 1) + { + rgb = ball_color[0]; + } + else if(i - start_idx == mid) + { + rgb = ball_color[1]; + } + else + { + rgb = ball_color[2]; + } + } + } + else if(i - start_idx < mid) + { + if(i - start_idx == j || i - start_idx + j == row - 1) + { + rgb = ball_color[0]; + } + } + else if(i - start_idx > mid + 1) + { + if(i - start_idx - j == const_ball_num || + i - start_idx + j == row - 1 + const_ball_num) + { + rgb = ball_color[3]; + } + } + + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb); + } + } + return set_brctl_data(); +} + +int RealtekBridgeController::eff_set_meteor(RGBColor rgb, int cycle) +{ + int i; + int j; + int row = 2; + int maxrow = REALTEK_BRIDGE_MAX_APPCTL_ROW; + int single_size = maxrow * REALTEK_BRIDGE_COLOR_DEPTH + 32; + int stable_ms = cycle / row; + int latency = stable_ms / get_argb_num(); + + memset(brctl_data, 0, REALTEK_BRIDGE_CTL_DATA_SIZE); + for(i = 0; i < get_argb_num(); i++) + { + brctl_data[i * single_size + 0] = 4; + brctl_data[i * single_size + 1] = row; + brctl_data[i * single_size + 6] = stable_ms / 100; + brctl_data[i * single_size + 7] = stable_ms / 100; + brctl_data[i * single_size + 8] = 6; + memcpy(&brctl_data[i * single_size + 9], &latency, 2); + + j = 0; + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 32] = RGBGetRValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 33] = RGBGetGValue(rgb); + brctl_data[i * single_size + j * REALTEK_BRIDGE_COLOR_DEPTH + 34] = RGBGetBValue(rgb); + } + return set_brctl_data(); +} + +int RealtekBridgeController::set_argb_direct(std::vector color_buf, unsigned short brightness) +{ + int ret = -1; + size_t color_num = color_buf.size(); + size_t buf_len = color_num * REALTEK_BRIDGE_COLOR_DEPTH; + static unsigned short prev_bright = 0xFFFF; + unsigned char* buf; + + if(color_num <= 0) + { + goto exit; + } + + ret = set_appctl(true); + if(ret) + { + goto exit; + } + + if(prev_bright != brightness) + { + prev_bright = brightness; + ret = set_argb_brightness(brightness << 8); + if(ret) + { + goto exit; + } + } + + buf = (unsigned char*)malloc(buf_len); + memset(buf, 0, buf_len); + for(int i = 0; i < (int)color_num; i++) + { + buf[i * REALTEK_BRIDGE_COLOR_DEPTH + 0] = RGBGetRValue(color_buf[i]); + buf[i * REALTEK_BRIDGE_COLOR_DEPTH + 1] = RGBGetGValue(color_buf[i]); + buf[i * REALTEK_BRIDGE_COLOR_DEPTH + 2] = RGBGetBValue(color_buf[i]); + } + ret = set_direct(buf, (int)color_num); + free(buf); + if(ret) + { + goto exit; + } +exit: + return ret; +} + +int RealtekBridgeController::set_argb_effect(unsigned char mode, std::vector color_buf, int speed, unsigned short brightness) +{ + int ret = -1; + int cycle = MathUtils::IntInterpolate(REALTEK_BRIDGE_CYCLE_MAX, REALTEK_BRIDGE_CYCLE_MIN, 0, REALTEK_BRIDGE_SPEED_MAX, speed); + RGBColor rgb = 0; + static unsigned short prev_bright = 0xFFFF; + + if(color_buf.size() >= 1) + { + rgb = color_buf[0]; + } + + if(prev_bright != brightness) + { + prev_bright = brightness; + ret = set_argb_brightness(brightness << 8); + if(ret) + { + goto exit; + } + } + + switch(mode) + { + case REALTEK_BRIDGE_LED_EFF_ALWAYS: + ret = eff_set_always_on(rgb); + break; + case REALTEK_BRIDGE_LED_EFF_BLINK: + ret = eff_set_blink(rgb, cycle); + break; + case REALTEK_BRIDGE_LED_EFF_BREATHE: + ret = eff_set_breathe(rgb, cycle); + break; + case REALTEK_BRIDGE_LED_EFF_SPECTRUM: + ret = eff_set_spectrum(cycle); + break; + case REALTEK_BRIDGE_LED_EFF_SCROLL: + ret = eff_set_scroll(rgb, cycle); + break; + case REALTEK_BRIDGE_LED_EFF_RAINBOW_SCROLL: + ret = eff_set_rainbow_scroll(cycle); + break; + case REALTEK_BRIDGE_LED_EFF_RUNNING_WATER: + ret = eff_set_running_water(rgb, cycle); + break; + case REALTEK_BRIDGE_LED_EFF_SLIDING: + ret = eff_set_sliding(cycle); + break; + case REALTEK_BRIDGE_LED_EFF_NEWTON_CRADLE: + ret = eff_set_newton_cradle(cycle); + break; + case REALTEK_BRIDGE_LED_EFF_METEOR: + ret = eff_set_meteor(rgb, cycle); + break; + default: + break; + } + if(!ret) + { + ret = set_appctl(false); + } +exit: + return ret; +} diff --git a/Controllers/RealtekBridgeController/RealtekBridgeController.h b/Controllers/RealtekBridgeController/RealtekBridgeController.h new file mode 100644 index 0000000..7b4f13b --- /dev/null +++ b/Controllers/RealtekBridgeController/RealtekBridgeController.h @@ -0,0 +1,126 @@ +/*---------------------------------------------------------*\ +| RealtekBridgeController.h | +| | +| Controller for Realtek USB to SSD Bridge ICs | +| | +| Jerry Fan (JerryFan0612) 13 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "RGBController.h" + +#define REALTEK_BRIDGE_SYNC_METHOD_OPENRGB 6 +#define REALTEK_BRIDGE_COLOR_DEPTH 3 +#define REALTEK_BRIDGE_CTL_HDR_SIZE 64 +#define REALTEK_BRIDGE_CTL_DATA_SIZE 3220 +#define REALTEK_BRIDGE_MAX_ARGB_NUM 20 +#define REALTEK_BRIDGE_MAX_APPCTL_ROW 43 + +#define REALTEK_BRIDGE_HID_DATALEN_CH1 4096 +#define REALTEK_BRIDGE_HID_DATALEN_CH2 64 +#define REALTEK_BRIDGE_HID_DATALEN_CH3 512 +#define REALTEK_BRIDGE_HID_DATALEN_CH4 1200 +#define REALTEK_BRIDGE_HID_DATALEN_CH5 2048 + +#define REALTEK_BRIDGE_HID_ID_DATA_CH1 0x57 +#define REALTEK_BRIDGE_HID_ID_DATA_CH2 0x59 +#define REALTEK_BRIDGE_HID_ID_DATA_CH3 0x5A +#define REALTEK_BRIDGE_HID_ID_DATA_CH4 0x5B +#define REALTEK_BRIDGE_HID_ID_DATA_CH5 0x5C + +enum REALTEK_BRIDGE_LED_EFF +{ + REALTEK_BRIDGE_LED_EFF_NONE, + REALTEK_BRIDGE_LED_EFF_ALWAYS, + REALTEK_BRIDGE_LED_EFF_BLINK, + REALTEK_BRIDGE_LED_EFF_BREATHE, + REALTEK_BRIDGE_LED_EFF_SPECTRUM, + REALTEK_BRIDGE_LED_EFF_SCROLL, + REALTEK_BRIDGE_LED_EFF_RAINBOW_SCROLL, + REALTEK_BRIDGE_LED_EFF_RUNNING_WATER, + REALTEK_BRIDGE_LED_EFF_SLIDING, + REALTEK_BRIDGE_LED_EFF_NEWTON_CRADLE, + REALTEK_BRIDGE_LED_EFF_METEOR, + REALTEK_BRIDGE_NUMBER_OF_LED_EFF_MODE, +}; + +enum REALTEK_BRIDGE_SPEED +{ + REALTEK_BRIDGE_SPEED_MIN = 1, + REALTEK_BRIDGE_SPEED_NORMAL = 50, + REALTEK_BRIDGE_SPEED_MAX = 100, +}; + +enum REALTEK_BRIDGE_CYCLE_MS +{ + REALTEK_BRIDGE_CYCLE_MIN = 200, + REALTEK_BRIDGE_CYCLE_NORMAL = 2000, + REALTEK_BRIDGE_CYCLE_MAX = 10000, +}; + +struct RealtekBridgeControllerFWVersion +{ + unsigned int fw_major_ver; + unsigned int fw_minor_ver; + unsigned int fw_extra_ver; + unsigned int fw_build_ver; + unsigned int fw_build_date; +}; + +class RealtekBridgeController +{ +public: + RealtekBridgeController(hid_device* dev, hid_device_info* info); + ~RealtekBridgeController(); + + unsigned char get_support_openrgb(); + std::string get_manu_name(); + std::string get_product_name(); + std::string get_sn(); + std::string get_dev_loc(); + std::string get_fw_ver(); + int get_argb_num(); + int get_argb_brightness(); + int set_argb_brightness(unsigned short bright); + + int set_argb_direct(std::vector color_buf, unsigned short brightness); + int set_argb_effect(unsigned char mode, std::vector color_buf, int speed, unsigned short brightness); + +private: + hid_device* hdev; + hid_device_info* hidinfo = NULL; + unsigned char brctl_hdr[REALTEK_BRIDGE_CTL_HDR_SIZE] = {0}; + unsigned char* brctl_data = NULL; + + int usb_hid_ioctl(unsigned char* usb_buf, unsigned char* data, int data_len, + unsigned int offset, unsigned char is_in); + int usb_hid_get_report(int data_len, int* id); + int set_write_unlock(); + int get_brctl_hdr(); + int set_brctl_hdr(); + int get_brctl_data(); + int set_brctl_data(); + int set_appctl(unsigned char ctl_sts); + int set_direct(unsigned char* color, int color_num); + int eff_set_always_on(RGBColor rgb); + int eff_set_blink(RGBColor rgb, int cycle); + int eff_set_breathe(RGBColor rgb, int cycle); + int eff_set_spectrum(int cycle); + int eff_set_scroll(RGBColor rgb, int cycle); + int eff_set_rainbow_scroll(int cycle); + int eff_set_running_water(RGBColor rgb, int cycle); + int eff_set_sliding(int cycle); + int eff_set_newton_cradle(int cycle); + int eff_set_meteor(RGBColor rgb, int cycle); +}; diff --git a/Controllers/RealtekBridgeController/RealtekBridgeControllerDetect.cpp b/Controllers/RealtekBridgeController/RealtekBridgeControllerDetect.cpp new file mode 100644 index 0000000..dd91cd9 --- /dev/null +++ b/Controllers/RealtekBridgeController/RealtekBridgeControllerDetect.cpp @@ -0,0 +1,61 @@ +/*---------------------------------------------------------*\ +| RealtekBridgeControllerDetect.cpp | +| | +| Controller for Realtek USB to SSD Bridge ICs | +| | +| Jerry Fan (JerryFan0612) 13 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBController_RealtekBridge.h" + +#define REALTEK_BRIDGE_VID 0x0BDA +#define REALTEK_BRIDGE_PID0 0x9220 +#define REALTEK_BRIDGE_PID1 0x9201 +#define REALTEK_BRIDGE_PID2 0x9210 + +#define REALTEK_HID2SCSI_PG 0xFF00 +#define REALTEK_HID2SCSI_USAGE 0x0001 + +/******************************************************************************************\ +* * +* DetectRealtekBridgeControllers * +* * +* Tests the USB address to see if an Realtek Bridge controller exists there * +* * +\******************************************************************************************/ +void DetectRealtekBridgeControllers(hid_device_info* info, const std::string& /*name*/) +{ + RealtekBridgeController* controller = NULL; + RGBController_RealtekBridge* rgb_controller = NULL; + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + controller = new RealtekBridgeController(dev, info); + if(controller->get_support_openrgb()) + { + rgb_controller = new RGBController_RealtekBridge(controller); + if(rgb_controller->type != DEVICE_TYPE_UNKNOWN) + { + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + delete rgb_controller; + } + } + else + { + delete controller; + } + } + return; +} + +REGISTER_HID_DETECTOR_PU("RTL9220", DetectRealtekBridgeControllers, REALTEK_BRIDGE_VID, REALTEK_BRIDGE_PID0, REALTEK_HID2SCSI_PG, REALTEK_HID2SCSI_USAGE); +REGISTER_HID_DETECTOR_PU("RTL9201", DetectRealtekBridgeControllers, REALTEK_BRIDGE_VID, REALTEK_BRIDGE_PID1, REALTEK_HID2SCSI_PG, REALTEK_HID2SCSI_USAGE); +REGISTER_HID_DETECTOR_PU("RTL9210", DetectRealtekBridgeControllers, REALTEK_BRIDGE_VID, REALTEK_BRIDGE_PID2, REALTEK_HID2SCSI_PG, REALTEK_HID2SCSI_USAGE); diff --git a/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RGBController_RedSquareKeyrox.cpp b/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RGBController_RedSquareKeyrox.cpp new file mode 100644 index 0000000..d158e9e --- /dev/null +++ b/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RGBController_RedSquareKeyrox.cpp @@ -0,0 +1,376 @@ +/*---------------------------------------------------------*\ +| RGBController_RedSquareKeyrox.cpp | +| | +| RGBController for Red Square Keyrox | +| | +| cafeed28 03 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_RedSquareKeyrox.h" + +#define NA 0xFFFFFFFF + +/*-----------------------------------*\ +| TODO: Other Keyrox boards support | +| (but I have only TKL) | +\*-----------------------------------*/ +typedef struct +{ + const unsigned int width; /* matrix width */ + const unsigned int height; /* matrix height */ + std::vector> matrix_map; /* matrix map */ + std::vector led_names; /* led names */ + std::vector led_sequence_positions; /* position in buffers */ +} keyrox; + +/*------------*\ +| Keyrox TKL | +\*------------*/ +static keyrox keyrox_tkl = +{ + 18, + 6, + { + { 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, NA, 9, 10, 11, 12, 13, 14, 15 }, + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, NA, 30, 31, 32 }, + { 33, NA, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49 }, + { 50, NA, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, NA, 62, NA, NA, NA }, + { 63, NA, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, NA, 74, NA, NA, 75, NA }, + { 76, 77, 78, NA, NA, NA, 79, NA, NA, NA, 80, 81, NA, 82, 83, 84, 85, 86 } + }, + { + // 0 + KEY_EN_ESCAPE, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + // 10 + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAUSE_BREAK, + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + // 20 + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + // 30 + KEY_EN_INSERT, + KEY_EN_HOME, + KEY_EN_PAGE_UP, + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + // 40 + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + // 50 + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + // 60 + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + // 70 + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + // 80 + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_MENU, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + }, + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 70, 51, 52, 53, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 50, + 75, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 88, 89, + 94, 95, 96, 98, 100, 101, 102, 103, 104, 105, 106 + } +}; + +typedef struct +{ + std::string name; + int value; + int flags; +} keyrox_effect; + +RGBController_RedSquareKeyrox::RGBController_RedSquareKeyrox(RedSquareKeyroxController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Red Square"; + type = DEVICE_TYPE_KEYBOARD; + description = "Red Square Keyrox Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + keyrox_effect keyrox_effects[13] = + { + { + "Custom", + CUSTOM_MODE_VALUE, + MODE_FLAG_HAS_PER_LED_COLOR + }, + { + "Wave", + WAVE_MODE_VALUE, + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD + }, + { + "Const", + CONST_MODE_VALUE, + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS + }, + { + "Breathe", + BREATHE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED + }, + { + "Heartrate", + HEARTRATE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED + }, + { + "Point", + POINT_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED + }, + { + "Winnower", + WINNOWER_MODE_VALUE, + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD + }, + { + "Stars", + STARS_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED + }, + { + "Spectrum", + SPECTRUM_MODE_VALUE, + MODE_FLAG_HAS_SPEED + }, + { + "Plumflower", + PLUMFLOWER_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED + }, + { + "Shoot", + SHOOT_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED + }, + { + "Ambilight Rotate", + AMBILIGHT_ROTATE_MODE_VALUE, + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD + }, + { + "Ripple", + RIPPLE_MODE_VALUE, + MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED + }, + }; + + for(const keyrox_effect& effect : keyrox_effects) + { + mode m; + m.name = effect.name; + m.value = effect.value; + m.flags = effect.flags | MODE_FLAG_HAS_BRIGHTNESS; + + if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + m.color_mode = MODE_COLORS_MODE_SPECIFIC; + m.colors_min = 1; + m.colors_max = 1; + + m.colors.resize(1); + m.colors.at(0) = ToRGBColor(255, 255, 255); + } + else if(m.flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + m.color_mode = MODE_COLORS_PER_LED; + } + else + { + m.color_mode = MODE_COLORS_NONE; + m.colors_min = 0; + m.colors_max = 0; + m.colors.resize(0); + } + + if(m.flags & MODE_FLAG_HAS_SPEED) + { + m.speed_min = KEYROX_SPEED_MIN; + m.speed_max = KEYROX_SPEED_MAX; + m.speed = m.speed_max; + } + + if(m.flags & MODE_FLAG_HAS_BRIGHTNESS) + { + m.brightness_min = KEYROX_BRIGHTNESS_MIN; + /*------------------------------------------*\ + | In Custom mode, Keyrox stores brightness | + | in A of RGBA and range is 0x00-0xFF | + \*------------------------------------------*/ + m.brightness_max = (m.flags & MODE_FLAG_HAS_PER_LED_COLOR) ? 0xFF : KEYROX_BRIGHTNESS_MAX; + m.brightness = m.brightness_max; + } + + modes.push_back(m); + } + + SetupZones(); +} + +RGBController_RedSquareKeyrox::~RGBController_RedSquareKeyrox() +{ + delete controller; +} + +void RGBController_RedSquareKeyrox::SetupZones() +{ + keyrox* keyboard; + switch(controller->GetVariant()) + { + case KEYROX_VARIANT_TKL: + keyboard = &keyrox_tkl; + break; + } + + controller->SetLedSequencePositions(keyboard->led_sequence_positions); + + /*-----------------*\ + | Create the zone | + \*-----------------*/ + unsigned int zone_size = 0; + + zone z; + z.name = ZONE_EN_KEYBOARD; + z.type = ZONE_TYPE_MATRIX; + + z.matrix_map = new matrix_map_type; + z.matrix_map->height = keyboard->height; + z.matrix_map->width = keyboard->width; + + z.matrix_map->map = new unsigned int[keyboard->height * keyboard->width]; + + for(unsigned int h = 0; h < keyboard->height; h++) + { + for(unsigned int w = 0; w < keyboard->width; w++) + { + unsigned int key = keyboard->matrix_map[h][w]; + z.matrix_map->map[h * keyboard->width + w] = key; + + if(key != NA) + { + led l; + l.name = keyboard->led_names[key]; + leds.push_back(l); + zone_size++; + } + } + } + + z.leds_min = zone_size; + z.leds_max = zone_size; + z.leds_count = zone_size; + + zones.push_back(z); + + SetupColors(); +} + +void RGBController_RedSquareKeyrox::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RedSquareKeyrox::DeviceUpdateLEDs() +{ + controller->SetLEDsData(modes, active_mode, colors); +} + +void RGBController_RedSquareKeyrox::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RedSquareKeyrox::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RedSquareKeyrox::DeviceUpdateMode() +{ + controller->SetMode(modes, active_mode); + controller->SetModeData(modes, active_mode); +} diff --git a/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RGBController_RedSquareKeyrox.h b/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RGBController_RedSquareKeyrox.h new file mode 100644 index 0000000..fd3ab9e --- /dev/null +++ b/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RGBController_RedSquareKeyrox.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_RedSquareKeyrox.h | +| | +| RGBController for Red Square Keyrox | +| | +| cafeed28 03 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RedSquareKeyroxController.h" + +class RGBController_RedSquareKeyrox : public RGBController +{ +public: + RGBController_RedSquareKeyrox(RedSquareKeyroxController* controller_ptr); + ~RGBController_RedSquareKeyrox(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RedSquareKeyroxController* controller; +}; diff --git a/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RedSquareKeyroxController.cpp b/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RedSquareKeyroxController.cpp new file mode 100644 index 0000000..ce5a33e --- /dev/null +++ b/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RedSquareKeyroxController.cpp @@ -0,0 +1,216 @@ +/*---------------------------------------------------------*\ +| RedSquareKeyroxController.cpp | +| | +| Driver for Red Square Keyrox | +| | +| cafeed28 03 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "RedSquareKeyroxController.h" + +using namespace std::chrono_literals; + +RedSquareKeyroxController::RedSquareKeyroxController(hid_device *dev_handle, const hid_device_info &info, int variant, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + this->variant = variant; +} + +RedSquareKeyroxController::~RedSquareKeyroxController() +{ + hid_close(dev); +} + +int RedSquareKeyroxController::GetVariant() +{ + return variant; +} + +std::string RedSquareKeyroxController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RedSquareKeyroxController::GetNameString() +{ + return(name); +} + +std::string RedSquareKeyroxController::GetSerialString() +{ + wchar_t serial_wchar[128]; + hid_get_serial_number_string(dev, serial_wchar, 128); + std::wstring serial_wstring(serial_wchar); + + std::string serial_string; + std::transform(serial_wstring.begin(), serial_wstring.end(), std::back_inserter(serial_string), [] (wchar_t i) + { + return (char)i; + }); + + return serial_string; +} + +int RedSquareKeyroxController::GetDirectionLRUD(int direction) +{ + switch(direction) + { + case MODE_DIRECTION_LEFT: + return 0x10; + case MODE_DIRECTION_RIGHT: + return 0x00; + case MODE_DIRECTION_UP: + return 0x20; + case MODE_DIRECTION_DOWN: + return 0x30; + default: + return 0x00; + } +} + +int RedSquareKeyroxController::GetDirectionUD(int direction) +{ + switch(direction) + { + case MODE_DIRECTION_UP: + return 0xA0; + case MODE_DIRECTION_DOWN: + return 0xB0; + default: + return 0xA0; + } +} + +void RedSquareKeyroxController::SetLedSequencePositions(std::vector positions) +{ + led_sequence_positions = positions; +} + +void RedSquareKeyroxController::SetMode(std::vector modes, int active_mode) +{ + /*---------------------------------------------*\ + | Mode set command | + \*---------------------------------------------*/ + mode m = modes[active_mode]; + + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[4] = 0x01; + usb_buf[6] = 0x04; + usb_buf[8] = m.value; + + Send(usb_buf); +} + +void RedSquareKeyroxController::SetModeData(std::vector modes, int active_mode) +{ + /*---------------------------------------------*\ + | Mode specific data set command | + \*---------------------------------------------*/ + mode m = modes[active_mode]; + + if(m.value == CUSTOM_MODE_VALUE) + { + return; + } + + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[4] = 0x09; + usb_buf[6] = 0x05; + usb_buf[7] = m.value; + usb_buf[8] = m.brightness; + usb_buf[9] = 0xFF; + + if(m.flags & MODE_FLAG_HAS_SPEED) + { + usb_buf[10] = m.speed; + usb_buf[11] = 0xFF; + + if(m.value == SPECTRUM_MODE_VALUE) + { + usb_buf[10] += 0x80; + } + } + + if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + if(m.flags & MODE_FLAG_HAS_RANDOM_COLOR && m.color_mode == MODE_COLORS_RANDOM) + { + usb_buf[10] += 0x80; + } + else + { + usb_buf[11] = RGBGetRValue(m.colors[0]); + usb_buf[12] = RGBGetGValue(m.colors[0]); + usb_buf[13] = RGBGetBValue(m.colors[0]); + } + } + + if((m.flags & MODE_FLAG_HAS_DIRECTION_LR) && (m.flags & MODE_FLAG_HAS_DIRECTION_UD)) + { + usb_buf[10] += GetDirectionLRUD(m.direction); + } + else if((m.flags & MODE_FLAG_HAS_DIRECTION_UD) && !(m.flags & MODE_FLAG_HAS_DIRECTION_LR)) + { + usb_buf[10] += GetDirectionUD(m.direction); + } + + Send(usb_buf); +} + +void RedSquareKeyroxController::SetLEDsData(std::vector modes, int active_mode, std::vector colors) +{ + /*---------------------------------------------*\ + | LEDs data set command | + \*---------------------------------------------*/ + mode m = modes[active_mode]; + + if(m.value != CUSTOM_MODE_VALUE) + { + return; + } + + unsigned char usb_buf[PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, PACKET_DATA_LENGTH); + + usb_buf[4] = 0xB0; + usb_buf[5] = 0x01; + usb_buf[6] = 0x07; + + for(unsigned int i = 0; i < colors.size(); i++) + { + int offset = 7 + led_sequence_positions[i] * 4; + usb_buf[offset + 1] = RGBGetRValue(colors[i]); + usb_buf[offset + 2] = RGBGetGValue(colors[i]); + usb_buf[offset + 3] = RGBGetBValue(colors[i]); + usb_buf[offset + 4] = m.brightness; + } + + Send(usb_buf); +} + +void RedSquareKeyroxController::Send(unsigned char data[PACKET_DATA_LENGTH]) +{ + unsigned char usb_buf[PACKET_DATA_LENGTH + 1]; + + usb_buf[0] = 0x00; // Report ID + + for(int x = 0; x < PACKET_DATA_LENGTH; x++) + { + usb_buf[x + 1] = data[x]; + } + + hid_send_feature_report(dev, usb_buf, PACKET_DATA_LENGTH + 1); + + std::this_thread::sleep_for(10ms); +} diff --git a/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RedSquareKeyroxController.h b/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RedSquareKeyroxController.h new file mode 100644 index 0000000..96b1020 --- /dev/null +++ b/Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RedSquareKeyroxController.h @@ -0,0 +1,90 @@ +/*---------------------------------------------------------*\ +| RedSquareKeyroxController.h | +| | +| Driver for Red Square Keyrox | +| | +| cafeed28 03 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define PACKET_DATA_LENGTH 520 + +/*-----------------------------*\ +| Red Square Keyrox variants | +\*-----------------------------*/ +enum +{ + KEYROX_VARIANT_TKL, +}; + +/*---------------------------------------*\ +| Modes | +\*---------------------------------------*/ +enum +{ + WAVE_MODE_VALUE = 0x00, + CONST_MODE_VALUE = 0x01, + BREATHE_MODE_VALUE = 0x02, + HEARTRATE_MODE_VALUE = 0x03, + POINT_MODE_VALUE = 0x04, + WINNOWER_MODE_VALUE = 0x05, + STARS_MODE_VALUE = 0x06, + SPECTRUM_MODE_VALUE = 0x07, + PLUMFLOWER_MODE_VALUE = 0x08, + SHOOT_MODE_VALUE = 0x09, + AMBILIGHT_ROTATE_MODE_VALUE = 0x0A, + RIPPLE_MODE_VALUE = 0x0B, + CUSTOM_MODE_VALUE = 0x0C, +}; + +/*-----------------------------*\ +| Other settings | +\*-----------------------------*/ +enum +{ + KEYROX_BRIGHTNESS_MIN = 0x00, + KEYROX_BRIGHTNESS_MAX = 0x7F, + KEYROX_SPEED_MIN = 0x00, + KEYROX_SPEED_MAX = 0x04, +}; + + +class RedSquareKeyroxController +{ +public: + RedSquareKeyroxController(hid_device *dev_handle, const hid_device_info &info, int variant, std::string dev_name); + ~RedSquareKeyroxController(); + + int GetVariant(); + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + int GetDirectionLRUD(int direction); // Direction for Left-Right-Up-Down modes + int GetDirectionUD(int direction); // Direction for Up-Down modes + + void SetLedSequencePositions(std::vector positions); + void SetMode(std::vector modes, int active_mode); + void SetModeData(std::vector modes, int active_mode); + void SetLEDsData(std::vector modes, int active_mode, std::vector colors); + + void Send(unsigned char data[PACKET_DATA_LENGTH]); + +protected: + hid_device* dev; + +private: + int variant; + std::string location; + std::string name; + std::string serial_number; + std::vector led_sequence_positions; +}; diff --git a/Controllers/RedSquareKeyroxController/RedSquareKeyroxControllerDetect.cpp b/Controllers/RedSquareKeyroxController/RedSquareKeyroxControllerDetect.cpp new file mode 100644 index 0000000..e416d30 --- /dev/null +++ b/Controllers/RedSquareKeyroxController/RedSquareKeyroxControllerDetect.cpp @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| RedSquareKeyroxControllerDetect.cpp | +| | +| Detector for Red Square Keyrox | +| | +| cafeed28 03 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "RedSquareKeyroxController.h" +#include "RedSquareKeyroxTKLClassicController.h" +#include "RGBController_RedSquareKeyrox.h" +#include "RGBController_RedSquareKeyroxTKLClassic.h" + +/*-----------------------------------------------------*\ +| Red Square vendor ID | +\*-----------------------------------------------------*/ +#define RED_SQUARE_VID 0x1A2C +#define RED_SQUARE_KEYROX_TKL_CLASSIC_VID 0x0416 + +/*-----------------------------------------------------*\ +| Red Square product ID | +\*-----------------------------------------------------*/ +#define RED_SQUARE_KEYROX_TKL_PID 0x1511 +#define RED_SQUARE_KEYROX_TKL_V2_PID 0x2511 +#define RED_SQUARE_KEYROX_TKL_CLASSIC_PID 0xC345 + +void DetectRedSquareKeyroxTKL(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RedSquareKeyroxController* controller = new RedSquareKeyroxController(dev, *info, KEYROX_VARIANT_TKL, name); + RGBController_RedSquareKeyrox* rgb_controller = new RGBController_RedSquareKeyrox(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} +void DetectRedSquareKeyroxTKLClassic(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RedSquareKeyroxTKLClassicController* controller = new RedSquareKeyroxTKLClassicController(dev, *info, name); + RGBController_RedSquareKeyroxTKLClassic* rgb_controller = new RGBController_RedSquareKeyroxTKLClassic(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Red Square Keyrox TKL", DetectRedSquareKeyroxTKL, RED_SQUARE_VID, RED_SQUARE_KEYROX_TKL_PID, 3, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Red Square Keyrox TKL V2", DetectRedSquareKeyroxTKL, RED_SQUARE_VID, RED_SQUARE_KEYROX_TKL_V2_PID, 3, 0xFF00, 2); +REGISTER_HID_DETECTOR_I( "Red Square Keyrox TKL Classic", DetectRedSquareKeyroxTKLClassic, RED_SQUARE_KEYROX_TKL_CLASSIC_VID, RED_SQUARE_KEYROX_TKL_CLASSIC_PID, 2); diff --git a/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RGBController_RedSquareKeyroxTKLClassic.cpp b/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RGBController_RedSquareKeyroxTKLClassic.cpp new file mode 100644 index 0000000..04b0eea --- /dev/null +++ b/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RGBController_RedSquareKeyroxTKLClassic.cpp @@ -0,0 +1,272 @@ +/*---------------------------------------------------------*\ +| RGBController_RedSquareKeyroxTKLClassic.cpp | +| | +| RGBController for Red Square Keyrox TKL Classic | +| | +| vlack 03 May 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RedSquareKeyroxTKLClassic.h" + +/**------------------------------------------------------------------*\ + @name Keyrox + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRedSquareKeyroxTKLClassic + @comment Also named Dark Project KD87a +\*-------------------------------------------------------------------*/ + +typedef struct +{ + std::string name; + int value; + int flags; +} keyrox_effect; + +/*--------------------*\ +| Keyrox TKL Classic | +\*--------------------*/ +layout_values keyrox_tkl_offset_values = +{ + { + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 7, 13, 16, 19, 22, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC INS HOME PGUP */ + 83, 86, 89, 92, 95, 98, 101, 104, 107, 110, 113, 116, 119, 135, 138, 141, 144, + /* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN */ + 159, 162, 165, 168, 171, 174, 177, 180, 183, 186, 199, 202, 205, 211, 214, 217, 220, + /* CPLK A S D F G H J K L ; " # ENTR */ + 235, 241, 244, 247, 250, 263, 266, 269, 272, 275, 278, 281, 284, 287, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU */ + 311, 314, 327, 330, 333, 336, 339, 342, 345, 348, 351, 354, 363, 369, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWR ARWD ARWR */ + 397, 400, 403, 415, 427, 430, 433, 436, 442, 455, 458 + }, + { + /* Add more regional layout fixes here */ + } +}; + +RGBController_RedSquareKeyroxTKLClassic::RGBController_RedSquareKeyroxTKLClassic(RedSquareKeyroxTKLClassicController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Red Square"; + type = DEVICE_TYPE_KEYBOARD; + description = "Red Square Keyrox TKL Classic Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + int BASE_EFFECT_FLAGS = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + + const int EFFECTS_COUNT = 14; + keyrox_effect keyrox_effects[EFFECTS_COUNT] = + { + { + "Static", + CLASSIC_CONST_MODE_VALUE, + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR + }, + { + "Direct", + CLASSIC_CUSTOM_MODE_VALUE, + MODE_FLAG_HAS_PER_LED_COLOR + }, + { + "Wave", + CLASSIC_WAVE_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_HAS_DIRECTION_HV + }, + { + "Breathing", + CLASSIC_FADE_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED + }, + { + "Radar", + CLASSIC_RADAR_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR // round animation + }, + { + "Star (Interactive)", + CLASSIC_STAR_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED + }, + { + "Line (Interactive)", + CLASSIC_LINE_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_HV + }, + { + "Ripple (Interactive)", + CLASSIC_RIPPLE_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED + }, + { + "Stars", + CLASSIC_STARS_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED + }, + { + "Cross (Interactive)", + CLASSIC_CROSS_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED + }, + { + "Horizontal bars (Interactive)", + CLASSIC_WTF_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_DIRECTION_UD + }, + { + "Ripple random", + CLASSIC_RIPPLE_RANDOM_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED + }, + { + "Running line", + CLASSIC_RUNNING_LINE_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR // round direction + }, + { + "Fireworks (Interactive)", + CLASSIC_FIREWORK_MODE_VALUE, + BASE_EFFECT_FLAGS | MODE_FLAG_HAS_SPEED + }, + }; + + for(int i = 0; i < EFFECTS_COUNT; i++) + { + mode m; + m.name = keyrox_effects[i].name; + m.value = keyrox_effects[i].value; + m.flags = keyrox_effects[i].flags | MODE_FLAG_HAS_BRIGHTNESS; + + if(m.flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR && m.value != CLASSIC_CONST_MODE_VALUE) + { + // background and foreground + m.color_mode = MODE_COLORS_MODE_SPECIFIC; + m.colors_min = 2; + m.colors_max = 2; + + m.colors.resize(2); + m.colors.at(0) = ToRGBColor(255, 255, 255); + m.colors.at(1) = ToRGBColor(0, 0, 0); + } + else if(m.flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + m.color_mode = MODE_COLORS_PER_LED; + } + else + { + // foreground only + m.color_mode = MODE_COLORS_MODE_SPECIFIC; + m.colors_min = 1; + m.colors_max = 1; + + m.colors.resize(1); + m.colors.at(0) = ToRGBColor(255, 255, 255); + } + + if(m.flags & MODE_FLAG_HAS_SPEED) + { + m.speed_min = CLASSIC_KEYROX_SPEED_MIN; + m.speed_max = CLASSIC_KEYROX_SPEED_MAX; + m.speed = (CLASSIC_KEYROX_SPEED_MAX - CLASSIC_KEYROX_SPEED_MIN) / 2; + } + + if(m.flags & MODE_FLAG_HAS_BRIGHTNESS) + { + m.brightness_min = CLASSIC_KEYROX_BRIGHTNESS_MIN; + m.brightness_max = CLASSIC_KEYROX_BRIGHTNESS_MAX; + m.brightness = m.brightness_max; + } + + modes.push_back(m); + } + + SetupZones(); +} + +RGBController_RedSquareKeyroxTKLClassic::~RGBController_RedSquareKeyroxTKLClassic() +{ + delete controller; +} + +void RGBController_RedSquareKeyroxTKLClassic::SetupZones() +{ + /*---------------------------------------------------------*\ + | Create the keyboard zone usiung Keyboard Layout Manager | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = ZONE_EN_KEYBOARD; + new_zone.type = ZONE_TYPE_MATRIX; + + KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ANSI_QWERTY, KEYBOARD_SIZE_TKL, keyrox_tkl_offset_values); + + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + new_zone.matrix_map->height = KEYROX_TKL_CLASSIC_HEIGHT; + new_zone.matrix_map->width = KEYROX_TKL_CLASSIC_WIDTH; + + new_zone.matrix_map->map = new unsigned int[new_map->height * new_map->width]; + new_zone.leds_count = new_kb.GetKeyCount(); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + /*---------------------------------------------------------*\ + | Matrix map still uses declared zone rows and columns | + | as the packet structure depends on the matrix map | + \*---------------------------------------------------------*/ + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT, new_map->height, new_map->width); + + /*---------------------------------------------------------*\ + | Create LEDs for the Matrix zone | + | Place keys in the layout to populate the matrix | + \*---------------------------------------------------------*/ + for(size_t led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt((unsigned int)led_idx); + new_led.value = new_kb.GetKeyValueAt((unsigned int)led_idx); + leds.push_back(new_led); + } + + zones.push_back(new_zone); + + SetupColors(); +} + +void RGBController_RedSquareKeyroxTKLClassic::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RedSquareKeyroxTKLClassic::DeviceUpdateLEDs() +{ + controller->SetLEDsData(colors, leds); +} + +void RGBController_RedSquareKeyroxTKLClassic::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RedSquareKeyroxTKLClassic::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RedSquareKeyroxTKLClassic::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode]); +} diff --git a/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RGBController_RedSquareKeyroxTKLClassic.h b/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RGBController_RedSquareKeyroxTKLClassic.h new file mode 100644 index 0000000..6b7f1c1 --- /dev/null +++ b/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RGBController_RedSquareKeyroxTKLClassic.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_RedSquareKeyroxTKLClassic.h | +| | +| RGBController for Red Square Keyrox TKL Classic | +| | +| vlack 03 May 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RGBControllerKeyNames.h" +#include "KeyboardLayoutManager.h" +#include "RedSquareKeyroxTKLClassicController.h" + +#define KEYROX_TKL_CLASSIC_WIDTH 17 +#define KEYROX_TKL_CLASSIC_HEIGHT 6 + +class RGBController_RedSquareKeyroxTKLClassic : public RGBController +{ +public: + RGBController_RedSquareKeyroxTKLClassic(RedSquareKeyroxTKLClassicController* controller_ptr); + ~RGBController_RedSquareKeyroxTKLClassic(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RedSquareKeyroxTKLClassicController* controller; +}; diff --git a/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RedSquareKeyroxTKLClassicController.cpp b/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RedSquareKeyroxTKLClassicController.cpp new file mode 100644 index 0000000..597b87d --- /dev/null +++ b/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RedSquareKeyroxTKLClassicController.cpp @@ -0,0 +1,169 @@ +/*---------------------------------------------------------*\ +| RedSquareKeyroxTKLClassicController.cpp | +| | +| Driver for Red Square Keyrox TKL Classic | +| | +| vlack 03 May 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "RedSquareKeyroxTKLClassicController.h" + +using namespace std::chrono_literals; + +RedSquareKeyroxTKLClassicController::RedSquareKeyroxTKLClassicController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +RedSquareKeyroxTKLClassicController::~RedSquareKeyroxTKLClassicController() +{ + hid_close(dev); +} + +std::string RedSquareKeyroxTKLClassicController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RedSquareKeyroxTKLClassicController::GetNameString() +{ + return(name); +} + +std::string RedSquareKeyroxTKLClassicController::GetSerialString() +{ + wchar_t serial_wchar[128]; + hid_get_serial_number_string(dev, serial_wchar, 128); + std::wstring serial_wstring(serial_wchar); + + std::string serial_string; + std::transform(serial_wstring.begin(), serial_wstring.end(), std::back_inserter(serial_string), [] (wchar_t i) + { + return (char)i; + }); + + return serial_string; +} + +int RedSquareKeyroxTKLClassicController::GetDirection(int direction) +{ + switch(direction) + { + case MODE_DIRECTION_LEFT: + return 0x00; + case MODE_DIRECTION_RIGHT: + return 0x01; + case MODE_DIRECTION_UP: + return 0x02; + case MODE_DIRECTION_DOWN: + return 0x03; + case MODE_DIRECTION_HORIZONTAL: // actually direction out + return 0x04; + case MODE_DIRECTION_VERTICAL: // actually direction in + return 0x05; + default: + return 0x00; + } +} + +int RedSquareKeyroxTKLClassicController::GetDirectionRound(int direction) +{ + switch(direction) + { + case MODE_DIRECTION_LEFT: // actually anticlockwise + return 0x07; + case MODE_DIRECTION_RIGHT: // actually clockwise + return 0x06; + default: + return 0x00; + } +} + +void RedSquareKeyroxTKLClassicController::SetMode(mode m) +{ + /*---------------------------------------------*\ + | Mode set command | + \*---------------------------------------------*/ + + unsigned char usb_buf[CLASSIC_PACKET_DATA_LENGTH]; + memset(usb_buf, 0x00, CLASSIC_PACKET_DATA_LENGTH); + + usb_buf[0] = 0x01; + usb_buf[1] = 0x07; + usb_buf[6] = m.value; + usb_buf[7] = m.brightness; // brightness + usb_buf[8] = m.speed; // speed + + if(m.colors_max == 1 || m.colors_max == 2) + { + usb_buf[9] = RGBGetRValue(m.colors[0]); // front R + usb_buf[10] = RGBGetGValue(m.colors[0]); // front G + usb_buf[11] = RGBGetBValue(m.colors[0]); // front B + } + + if(m.colors_max == 2 && m.color_mode != MODE_COLORS_RANDOM) + { + usb_buf[12] = RGBGetRValue(m.colors[1]); // back R + usb_buf[13] = RGBGetGValue(m.colors[1]); // back G + usb_buf[14] = RGBGetBValue(m.colors[1]); // back B + } + + if(m.value == CLASSIC_RADAR_MODE_VALUE || m.value == CLASSIC_RUNNING_LINE_MODE_VALUE) + { + usb_buf[15] = GetDirectionRound(m.direction); + } + else + { + usb_buf[15] = GetDirection(m.direction); + } + + usb_buf[16] = m.color_mode == MODE_COLORS_RANDOM; + + hid_write(dev, usb_buf, CLASSIC_PACKET_DATA_LENGTH); + // sleep is necessary + std::this_thread::sleep_for(10ms); +} + +void RedSquareKeyroxTKLClassicController::SetLEDsData(std::vector colors, std::vector leds) +{ + /*---------------------------------------------*\ + | LEDs data set command | + \*---------------------------------------------*/ + + unsigned char usb_buf[CLASSIC_PACKET_DATA_LENGTH * 8]; + memset(usb_buf, 0x00, CLASSIC_PACKET_DATA_LENGTH * 8); + + for(unsigned int i = 0; i < colors.size(); i++) + { + int offset = leds[i].value; + usb_buf[offset - 1] = RGBGetRValue(colors[i]); + usb_buf[offset] = RGBGetGValue(colors[i]); + usb_buf[offset + 1] = RGBGetBValue(colors[i]); + } + + for(unsigned int i = 0; i < 8; i++) + { + unsigned char packet[CLASSIC_PACKET_DATA_LENGTH]; + memset(packet, 0x00, CLASSIC_PACKET_DATA_LENGTH); + + packet[0] = 0x01; + packet[1] = 0x0F; + packet[4] = i; // package number + packet[5] = i == 7 ? 0x12 : 0x36; // package size + + for (int x = 6; x < CLASSIC_PACKET_DATA_LENGTH; x++) + { + packet[x] = usb_buf[CLASSIC_PACKET_DATA_LENGTH * i + x]; + } + hid_write(dev, packet, CLASSIC_PACKET_DATA_LENGTH); + } + + // sleep is necessary + std::this_thread::sleep_for(10ms); +} diff --git a/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RedSquareKeyroxTKLClassicController.h b/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RedSquareKeyroxTKLClassicController.h new file mode 100644 index 0000000..29f2337 --- /dev/null +++ b/Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RedSquareKeyroxTKLClassicController.h @@ -0,0 +1,78 @@ +/*---------------------------------------------------------*\ +| RedSquareKeyroxTKLClassicController.h | +| | +| Driver for Red Square Keyrox TKL Classic | +| | +| vlack 03 May 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define CLASSIC_PACKET_DATA_LENGTH 64 + +/*---------------------------------------*\ +| Modes | +\*---------------------------------------*/ +enum +{ + CLASSIC_CONST_MODE_VALUE = 0x00, // static + CLASSIC_BREATHE_MODE_VALUE = 0x01, // breath + CLASSIC_WAVE_MODE_VALUE = 0x02, // wave + CLASSIC_FADE_MODE_VALUE = 0x03, // neon + CLASSIC_RADAR_MODE_VALUE = 0x04, // radar + CLASSIC_STAR_MODE_VALUE = 0x06, // интерактив + CLASSIC_LINE_MODE_VALUE = 0x07, // сияние + CLASSIC_RIPPLE_MODE_VALUE = 0x08, // рябь интерактив + CLASSIC_STARS_MODE_VALUE = 0x09, // мерцание + CLASSIC_CUSTOM_MODE_VALUE = 0x0A, + CLASSIC_CROSS_MODE_VALUE = 0x0B, // скрещивание + CLASSIC_WTF_MODE_VALUE = 0x0C, // быстрый отклик + CLASSIC_RIPPLE_RANDOM_MODE_VALUE = 0x0E, // рябь + CLASSIC_RUNNING_LINE_MODE_VALUE = 0x0F, // бегущая строка + CLASSIC_FIREWORK_MODE_VALUE = 0x10, // firework +}; + +/*-----------------------------*\ +| Other settings | +\*-----------------------------*/ +enum +{ + CLASSIC_KEYROX_BRIGHTNESS_MIN = 0x00, + CLASSIC_KEYROX_BRIGHTNESS_MAX = 0x04, + CLASSIC_KEYROX_SPEED_MIN = 0x00, + CLASSIC_KEYROX_SPEED_MAX = 0x04, +}; + + +class RedSquareKeyroxTKLClassicController +{ +public: + RedSquareKeyroxTKLClassicController(hid_device *dev_handle, const hid_device_info &info, std::string dev_name); + ~RedSquareKeyroxTKLClassicController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + int GetDirection(int direction); + int GetDirectionRound(int direction); + + void SetMode(mode m); + void SetLEDsData(std::vector colors, std::vector leds); + +protected: + hid_device* dev; + +private: + std::string location; + std::string name; + std::string serial_number; + std::vector led_sequence_positions; +}; diff --git a/Controllers/RedragonController/RGBController_RedragonMouse.cpp b/Controllers/RedragonController/RGBController_RedragonMouse.cpp new file mode 100644 index 0000000..8b8125a --- /dev/null +++ b/Controllers/RedragonController/RGBController_RedragonMouse.cpp @@ -0,0 +1,141 @@ +/*---------------------------------------------------------*\ +| RGBController_RedragonMouse.cpp | +| | +| RGBController for Redragon mouse | +| | +| Adam Honse (CalcProgrammer1) 25 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RedragonMouse.h" + +/**------------------------------------------------------------------*\ + @name Redragon Mice + @category Mouse + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectRedragonMice + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RedragonMouse::RGBController_RedragonMouse(RedragonMouseController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Redragon"; + type = DEVICE_TYPE_MOUSE; + description = "Redragon Mouse Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.value = REDRAGON_MOUSE_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Wave; + Wave.name = "Wave"; + Wave.value = REDRAGON_MOUSE_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Wave.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Wave); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = REDRAGON_MOUSE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = REDRAGON_MOUSE_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = REDRAGON_MOUSE_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flashing); + + SetupZones(); +} + +RGBController_RedragonMouse::~RGBController_RedragonMouse() +{ + delete controller; +} + +void RGBController_RedragonMouse::SetupZones() +{ + zone mouse_zone; + mouse_zone.name = "Mouse"; + mouse_zone.type = ZONE_TYPE_SINGLE; + mouse_zone.leds_min = REDRAGON_MOUSE_LED_COUNT; + mouse_zone.leds_max = REDRAGON_MOUSE_LED_COUNT; + mouse_zone.leds_count = REDRAGON_MOUSE_LED_COUNT; + mouse_zone.matrix_map = NULL; + zones.push_back(mouse_zone); + + led mouse_led; + mouse_led.name = "Mouse"; + leds.push_back(mouse_led); + + SetupColors(); +} + +void RGBController_RedragonMouse::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RedragonMouse::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SendMouseColor(red, grn, blu); + controller->SendMouseApply(); +} + +void RGBController_RedragonMouse::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RedragonMouse::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RedragonMouse::DeviceUpdateMode() +{ + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + if((modes[active_mode].value == REDRAGON_MOUSE_MODE_BREATHING) && random) + { + controller->SendMouseMode(REDRAGON_MOUSE_MODE_RANDOM_BREATHING, 0, red, grn, blu); + } + else + { + controller->SendMouseMode(modes[active_mode].value, 0, red, grn, blu); + } + + controller->SendMouseApply(); +} diff --git a/Controllers/RedragonController/RGBController_RedragonMouse.h b/Controllers/RedragonController/RGBController_RedragonMouse.h new file mode 100644 index 0000000..9b93717 --- /dev/null +++ b/Controllers/RedragonController/RGBController_RedragonMouse.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_RedragonMouse.h | +| | +| RGBController for Redragon mouse | +| | +| Adam Honse (CalcProgrammer1) 25 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RedragonMouseController.h" + +class RGBController_RedragonMouse : public RGBController +{ +public: + RGBController_RedragonMouse(RedragonMouseController* controller_ptr); + ~RGBController_RedragonMouse(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RedragonMouseController* controller; +}; diff --git a/Controllers/RedragonController/RedragonControllerDetect.cpp b/Controllers/RedragonController/RedragonControllerDetect.cpp new file mode 100644 index 0000000..29fd8f6 --- /dev/null +++ b/Controllers/RedragonController/RedragonControllerDetect.cpp @@ -0,0 +1,66 @@ +/*---------------------------------------------------------*\ +| RedragonControllerDetect.cpp | +| | +| Detector for Redragon devices | +| | +| Adam Honse (CalcProgrammer1) 15 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "RedragonMouseController.h" +#include "RGBController_RedragonMouse.h" + +/*-----------------------------------------------------*\ +| Mouse product IDs | +\*-----------------------------------------------------*/ +#define REDRAGON_MOUSE_VID 0x04D9 +#define REDRAGON_MOUSE_USAGE_PAGE 0xFFA0 +#define REDRAGON_M711_PID 0xFC30 +#define REDRAGON_M715_PID 0xFC39 +#define REDRAGON_M716_PID 0xFC3A +#define REDRAGON_M908_PID 0xFC4D +#define REDRAGON_M602_PID 0xFC38 +#define REDRAGON_M808_PID 0xFC5F +#define REDRAGON_M801_PID 0xFC58 +#define REDRAGON_M810_PID 0xFA7E +#define REDRAGON_M987_PID 0xFC69 +#define REDRAGON_M921_PID 0xFC40 + +/******************************************************************************************\ +* * +* DetectRedragonMice * +* * +* Tests the USB address to see if a Redragon Mouse controller exists there. * +* * +\******************************************************************************************/ + +void DetectRedragonMice(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RedragonMouseController* controller = new RedragonMouseController(dev, info->path, name); + RGBController_RedragonMouse* rgb_controller = new RGBController_RedragonMouse(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +/*---------------------------------------------------------------------------------------------------------------------------------------------*\ +| Mice | +\*---------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("Redragon M711 Cobra", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M711_PID, 2, REDRAGON_MOUSE_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Redragon M715 Dagger", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M715_PID, 2, REDRAGON_MOUSE_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Redragon M716 Inquisitor", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M716_PID, 2, REDRAGON_MOUSE_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Redragon M908 Impact", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M908_PID, 2, REDRAGON_MOUSE_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Redragon M602 Griffin", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M602_PID, 2, REDRAGON_MOUSE_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Redragon M808 Storm", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M808_PID, 2, REDRAGON_MOUSE_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Redragon M801 Sniper", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M801_PID, 2, REDRAGON_MOUSE_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Redragon M810 Taipan", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M810_PID, 2, REDRAGON_MOUSE_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Redragon M987 Reaping", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M987_PID, 2, REDRAGON_MOUSE_USAGE_PAGE); +REGISTER_HID_DETECTOR_IP("Redragon M921 Azzinoth", DetectRedragonMice, REDRAGON_MOUSE_VID, REDRAGON_M921_PID, 2, REDRAGON_MOUSE_USAGE_PAGE); diff --git a/Controllers/RedragonController/RedragonMouseController.cpp b/Controllers/RedragonController/RedragonMouseController.cpp new file mode 100644 index 0000000..18a50be --- /dev/null +++ b/Controllers/RedragonController/RedragonMouseController.cpp @@ -0,0 +1,167 @@ +/*---------------------------------------------------------*\ +| RedragonMouseController.cpp | +| | +| Driver for Redragon mouse | +| | +| Adam Honse (CalcProgrammer1) 15 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RedragonMouseController.h" +#include "StringUtils.h" + +RedragonMouseController::RedragonMouseController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + unsigned char active_profile = 0x00; + + SendWritePacket(0x002C, 1, &active_profile); + SendMouseApply(); +} + +RedragonMouseController::~RedragonMouseController() +{ + hid_close(dev); +} + +std::string RedragonMouseController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RedragonMouseController::GetNameString() +{ + return(name); +} + +std::string RedragonMouseController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void RedragonMouseController::SendMouseColor + ( + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char color_buf[3]; + + color_buf[0] = red; + color_buf[1] = green; + color_buf[2] = blue; + + SendWritePacket(0x0449, 3, color_buf); +} + +void RedragonMouseController::SendMouseMode + ( + unsigned char mode, + unsigned char speed + ) +{ + unsigned char mode_buf[3]; + + mode_buf[0] = 0x01; //On + mode_buf[1] = speed; + mode_buf[2] = mode; + + SendWritePacket(0x044C, 3, mode_buf); +} + +void RedragonMouseController::SendMouseMode + ( + unsigned char mode, + unsigned char speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char color_mode_buf[6]; + + color_mode_buf[0] = red; + color_mode_buf[1] = green; + color_mode_buf[2] = blue; + color_mode_buf[3] = 0x01; //On + color_mode_buf[4] = speed; + color_mode_buf[5] = mode; + + SendWritePacket(0x0449, 6, color_mode_buf); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void RedragonMouseController::SendMouseApply() +{ + unsigned char usb_buf[REDRAGON_MOUSE_REPORT_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, REDRAGON_MOUSE_REPORT_SIZE); + + /*-----------------------------------------------------*\ + | Set up Apply packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = REDRAGON_MOUSE_REPORT_ID; + usb_buf[0x01] = 0xF1; + usb_buf[0x02] = 0x02; + usb_buf[0x03] = 0x04; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, REDRAGON_MOUSE_REPORT_SIZE); +} + +void RedragonMouseController::SendWritePacket + ( + unsigned short address, + unsigned char data_size, + unsigned char * data + ) +{ + unsigned char usb_buf[REDRAGON_MOUSE_REPORT_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, REDRAGON_MOUSE_REPORT_SIZE); + + /*-----------------------------------------------------*\ + | Set up Lighting Control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = REDRAGON_MOUSE_REPORT_ID; + usb_buf[0x01] = 0xF3; + usb_buf[0x02] = address & 0xFF; + usb_buf[0x03] = address >> 8; + usb_buf[0x04] = data_size; + + /*-----------------------------------------------------*\ + | Copy in data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x08], data, data_size); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, REDRAGON_MOUSE_REPORT_SIZE); +} diff --git a/Controllers/RedragonController/RedragonMouseController.h b/Controllers/RedragonController/RedragonMouseController.h new file mode 100644 index 0000000..220e631 --- /dev/null +++ b/Controllers/RedragonController/RedragonMouseController.h @@ -0,0 +1,76 @@ +/*---------------------------------------------------------*\ +| RedragonMouseController.h | +| | +| Driver for Redragon mouse | +| | +| Adam Honse (CalcProgrammer1) 15 Mar 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +#define REDRAGON_MOUSE_REPORT_ID 0x02 +#define REDRAGON_MOUSE_REPORT_SIZE 16 +#define REDRAGON_MOUSE_LED_COUNT 1 + +enum +{ + REDRAGON_MOUSE_MODE_WAVE = 0x00, + REDRAGON_MOUSE_MODE_RANDOM_BREATHING = 0x01, + REDRAGON_MOUSE_MODE_STATIC = 0x02, + REDRAGON_MOUSE_MODE_BREATHING = 0x04, + REDRAGON_MOUSE_MODE_RAINBOW = 0x08, + REDRAGON_MOUSE_MODE_FLASHING = 0x10 +}; + +class RedragonMouseController +{ +public: + RedragonMouseController(hid_device* dev_handle, const char* path, std::string dev_name); + ~RedragonMouseController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendMouseApply(); + + void SendMouseColor + ( + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SendMouseMode + ( + unsigned char mode, + unsigned char speed + ); + + void SendMouseMode + ( + unsigned char mode, + unsigned char speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendWritePacket + ( + unsigned short address, + unsigned char data_size, + unsigned char * data + ); +}; diff --git a/Controllers/RobobloqLightStripController/RGBController_RobobloqLightStrip.cpp b/Controllers/RobobloqLightStripController/RGBController_RobobloqLightStrip.cpp new file mode 100644 index 0000000..7d9e564 --- /dev/null +++ b/Controllers/RobobloqLightStripController/RGBController_RobobloqLightStrip.cpp @@ -0,0 +1,224 @@ +/*---------------------------------------------------------*\ +| RGBController_RobobloqLightStrip.cpp | +| | +| Detector for Robobloq Monitor Light Strip | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RobobloqLightStrip.h" +#include "RobobloqLightStripController.h" +#include + +/**--------------------------------------------------------------------*\ + @name Robobloq Monitor Light Strip + @category LEDStrip + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRobobloqLightStripController + @comment +\*---------------------------------------------------------------------*/ + +RGBController_RobobloqLightStrip::RGBController_RobobloqLightStrip(RobobloqLightStripController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Robobloq"; + description = "Robobloq Monitor Light Strip (" + std::to_string(controller->GetPhysicalSizeInInches()) + "\")"; + type = DEVICE_TYPE_LEDSTRIP; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Static; + Static.name = "Static"; + Static.value = ROBOBLOQ_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness_min = 1; + Static.brightness_max = 255; + Static.brightness = 255; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ROBOBLOQ_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 1; + Direct.brightness_max = 255; + Direct.brightness = 255; + modes.push_back(Direct); + + /*-----------------------------------------------------*\ + | Add dynamic modes | + \*-----------------------------------------------------*/ + struct DynamicMode + { + const char* name; + int value; + }; + + DynamicMode dynamic_modes[] = + { + { "Rainbow Wave", ROBOBLOQ_MODE_DYNAMIC_RAINBOW }, + { "Breathing", ROBOBLOQ_MODE_DYNAMIC_BREATHING }, + { "Twist", ROBOBLOQ_MODE_DYNAMIC_TWIST }, + { "Beat", ROBOBLOQ_MODE_DYNAMIC_BEAT }, + { "Twirl", ROBOBLOQ_MODE_DYNAMIC_TWIRL }, + { "Lemon", ROBOBLOQ_MODE_DYNAMIC_LEMON }, + { "Electric", ROBOBLOQ_MODE_DYNAMIC_ELECTRIC }, + }; + + for(unsigned int i = 0; i < (sizeof(dynamic_modes) / sizeof(DynamicMode)); i++) + { + mode new_mode; + new_mode.name = dynamic_modes[i].name; + new_mode.value = dynamic_modes[i].value; + new_mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + new_mode.color_mode = MODE_COLORS_NONE; + new_mode.speed_min = 0; + new_mode.speed_max = 100; + new_mode.speed = 50; + new_mode.brightness_min = 1; + new_mode.brightness_max = 255; + new_mode.brightness = 255; + modes.push_back(new_mode); + } + + mode Off; + Off.name = "Off"; + Off.value = ROBOBLOQ_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +}; + +RGBController_RobobloqLightStrip::~RGBController_RobobloqLightStrip() +{ + delete controller; +} + +void RGBController_RobobloqLightStrip::SetupZones() +{ + int led_count = controller->GetLEDCount(); + int leds_per_side = controller->GetLEDsPerSide(); + + struct Side + { + const char* name; + int count; + }; + + std::vector sides = { { "Light Strip", led_count } }; + if(leds_per_side > 0) + { + sides = { + { "Right", leds_per_side }, + { "Top", led_count - (leds_per_side * 2) }, + { "Left", leds_per_side }, + }; + } + + zones.clear(); + zones.resize(sides.size()); + leds.clear(); + leds.resize(led_count); + + for(unsigned int i = 0; i < sides.size(); i++) + { + zone& zone = zones[i]; + zone.name = sides[i].name; + zone.type = ZONE_TYPE_LINEAR; + zone.leds_count = sides[i].count; + zone.leds_min = zone.leds_count; + zone.leds_max = zone.leds_count; + zone.matrix_map = NULL; + } + + for(int i = 0; i < led_count; i++) + { + led& new_led = leds[i]; + new_led.name = "LED " + std::to_string(i + 1); + new_led.value = i; + } + + SetupColors(); +} + +void RGBController_RobobloqLightStrip::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*-----------------------------------------------------*\ + | This device does not support resizing zones | + \*-----------------------------------------------------*/ +} + +void RGBController_RobobloqLightStrip::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == ROBOBLOQ_MODE_DIRECT) + { + controller->SetCustom(colors); + } +} + +void RGBController_RobobloqLightStrip::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RobobloqLightStrip::UpdateSingleLED(int led) +{ + controller->SetLEDColor(led, modes[active_mode].colors[0]); +} + +void RGBController_RobobloqLightStrip::DeviceUpdateMode() +{ + bool mode_changed = false; + + /*-----------------------------------------------------*\ + | Cache mode to avoid repeated SetDynamicEffect calls | + | when adjusting speed/brightness | + \*-----------------------------------------------------*/ + if(modes[active_mode].value != cur_mode) + { + cur_mode = modes[active_mode].value; + mode_changed = true; + } + + if(cur_mode == ROBOBLOQ_MODE_OFF) + { + controller->TurnOff(); + } + else if(cur_mode == ROBOBLOQ_MODE_DIRECT) + { + controller->SetBrightness(modes[active_mode].brightness); + } + else if(cur_mode == ROBOBLOQ_MODE_STATIC) + { + controller->SetColor(modes[active_mode].colors[0]); + controller->SetBrightness(modes[active_mode].brightness); + } + else if(ROBOBLOQ_IS_DYNAMIC_EFFECT(cur_mode)) + { + if(mode_changed) + { + controller->SetDynamicEffect(cur_mode); + } + + controller->SetBrightness(modes[active_mode].brightness); + controller->SetDynamicSpeed(modes[active_mode].speed); + } + else + { + LOG_ERROR("[Robobloq] Requested mode (%02x) is not supported", cur_mode); + } +} diff --git a/Controllers/RobobloqLightStripController/RGBController_RobobloqLightStrip.h b/Controllers/RobobloqLightStripController/RGBController_RobobloqLightStrip.h new file mode 100644 index 0000000..6dfcc8c --- /dev/null +++ b/Controllers/RobobloqLightStripController/RGBController_RobobloqLightStrip.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_RobobloqLightStrip.h | +| | +| Detector for Robobloq Monitor Light Strip | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RobobloqLightStripController.h" + +class RGBController_RobobloqLightStrip : public RGBController +{ +public: + RGBController_RobobloqLightStrip(RobobloqLightStripController* controller_ptr); + ~RGBController_RobobloqLightStrip(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + /*-----------------------------------------------------*\ + | Last mode set via this controller | + \*-----------------------------------------------------*/ + int cur_mode = -1; + RobobloqLightStripController* controller; +}; diff --git a/Controllers/RobobloqLightStripController/RobobloqLightStripController.cpp b/Controllers/RobobloqLightStripController/RobobloqLightStripController.cpp new file mode 100644 index 0000000..f896c06 --- /dev/null +++ b/Controllers/RobobloqLightStripController/RobobloqLightStripController.cpp @@ -0,0 +1,421 @@ +/*---------------------------------------------------------*\ +| RobobloqLightStripController.cpp | +| | +| Detector for Robobloq Monitor Light Strip | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "LogManager.h" +#include "RobobloqLightStripController.h" +#include "RobobloqRangeMerger.h" +#include "RGBController.h" +#include + +using namespace std::chrono_literals; + +RobobloqLightStripController::RobobloqLightStripController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + packet_index = 0x02; + led_count = 0; + + RequestDeviceInfo(); + Initialize(); +} + +RobobloqLightStripController::~RobobloqLightStripController() +{ + hid_close(dev); +} + +std::string RobobloqLightStripController::GetDeviceLocation() +{ + return location; +} + +std::string RobobloqLightStripController::GetDeviceName() +{ + return name; +} + +std::string RobobloqLightStripController::GetSerialString() +{ + return uuid; +} + +std::string RobobloqLightStripController::GetFirmwareVersion() +{ + return firmware_version; +} + +int RobobloqLightStripController::GetLEDCount() +{ + return led_count; +} + +int RobobloqLightStripController::GetLEDsPerSide() +{ + switch(physical_size) + { + case 34: + return 15; + default: + return 0; /* unknown */ + } +} + + +int RobobloqLightStripController::GetPhysicalSizeInInches() +{ + return physical_size; +} + +void RobobloqLightStripController::Initialize() +{ + RequestDeviceInfo(); + + /*-----------------------------------------------------*\ + | This tells the device (permanently) not to try to use | + | its keyboard to open a URL to the driver download | + | page. Yes, it really does that. | + \*-----------------------------------------------------*/ + SendPacket({ROBOBLOQ_CMD_SET_OPEN_URL, 0x00}); + + SetBrightness(0xF9); + SetDynamicSpeed(0x32); +} + +/*---------------------------------------------------------*\ +| Set the entire light strip to the specified color. | +\*---------------------------------------------------------*/ +void RobobloqLightStripController::SetColor(RGBColor c) +{ + std::vector payload = { + 0x86, + 0x01, + (unsigned char)RGBGetRValue(c), + (unsigned char)RGBGetGValue(c), + (unsigned char)RGBGetBValue(c), + led_count, + /*-------------------------------------------------*\ + | Add dummy range (matches original application) | + \*-------------------------------------------------*/ + (unsigned char)(led_count + 1), + 0x00, + 0x00, + 0x00, + 0xFE, + }; + SendPacket(payload, false); +} + +/*---------------------------------------------------------*\ +| Set a single LED to the specified color. LED is 0-indexed | +\*---------------------------------------------------------*/ +void RobobloqLightStripController::SetLEDColor(int led, RGBColor c) +{ + std::vector payload = { + 0x86, + (unsigned char)(led + 1), + (unsigned char)RGBGetRValue(c), + (unsigned char)RGBGetGValue(c), + (unsigned char)RGBGetBValue(c), + (unsigned char)(led + 1), + }; + SendPacket(payload, false); +} + +void RobobloqLightStripController::SetColorRanges(const std::vector& ranges) +{ + /*-----------------------------------------------------*\ + | 6 bytes overhead (header + command + checksum) plus | + | ranges must fit in 64 bytes | + \*-----------------------------------------------------*/ + if((ranges.size() + 6) > 64) + { + LOG_ERROR("[Robobloq] SetColorRanges: Too many ranges (%d) for packet size", (int)ranges.size()); + return; + } + std::vector payload = { 0x86 }; + payload.insert(payload.end(), ranges.begin(), ranges.end()); + SendPacket(payload, false); +} + +void RobobloqLightStripController::SetBrightness(unsigned char brightness) +{ + /*-----------------------------------------------------*\ + | 0 value is interpreted as max (255) | + \*-----------------------------------------------------*/ + if(brightness == 0) + { + brightness = 1; + } + + SendPacket({ROBOBLOQ_CMD_SET_BRIGHTNESS, brightness}); +} + +void RobobloqLightStripController::SetDynamicEffect(unsigned char effect) +{ + SendPacket({ROBOBLOQ_CMD_SET_EFFECT, ROBOBLOQ_EFFECT_DYNAMIC, effect}); +} + +void RobobloqLightStripController::SetDynamicSpeed(unsigned char speed) +{ + /*-----------------------------------------------------*\ + | Device expects 0x00 = fast, 0x64 = slow | + \*-----------------------------------------------------*/ + speed = ROBOBLOQ_DYNAMIC_SPEED_MAX - speed; + + SendPacket({ROBOBLOQ_CMD_SET_DYNAMIC_SPEED, speed}); +} + +void RobobloqLightStripController::TurnOff() +{ + /*-----------------------------------------------------*\ + | The device doesn't really turn off: we send a command | + | to disable any device-side animations, then set all | + | LEDs to black. | + \*-----------------------------------------------------*/ + SendPacket({ROBOBLOQ_CMD_TURN_OFF}); + SetColor(COLOR_BLACK); +} + +/*---------------------------------------------------------*\ +| Update all LED colors at once. | +| | +| The official app reduces 71 LED values to 34 distinct | +| ranges before sending them to the device, so we do the | +| same. SendSyncScreen is capable of sending any number of | +| ranges (e.g 71 - 1 per pixel) but this has not been | +| properly tested. | +\*---------------------------------------------------------*/ +void RobobloqLightStripController::SetCustom(const std::vector& colors) +{ + int num_leds = (int)colors.size(); + if(num_leds != this->led_count) + { + LOG_ERROR("[Robobloq] SetCustom: Number of colors (%d) does not match LED count (%d), rejecting", num_leds, this->led_count); + return; + } + + std::vector color_bytes = MergeRobobloqRanges(colors, ROBOBLOQ_TUPLE_COUNT); + + SendSyncScreen(color_bytes); +} + +/*---------------------------------------------------------*\ +| Internal method to send color ranges to the device. | +\*---------------------------------------------------------*/ +void RobobloqLightStripController::SendSyncScreen(const std::vector& color_bytes) +{ + if(color_bytes.size() % 5 != 0) + { + LOG_ERROR("[Robobloq] SendSyncScreen: color_bytes size (%d) is not a multiple of 5, rejecting", (int)color_bytes.size()); + return; + } + + SendMultiPacket(ROBOBLOQ_CMD_SET_SYNC_SCREEN, color_bytes); +} + +/*---------------------------------------------------------*\ +| Sends a multi-packet command to the device. | +| | +| This allows payloads >64 bytes, e.g. SendSyncScreen | +\*---------------------------------------------------------*/ +void RobobloqLightStripController::SendMultiPacket(unsigned char command, const std::vector& payload) +{ + /*-----------------------------------------------------*\ + | Allow for 6-byte header and trailing checksum | + \*-----------------------------------------------------*/ + unsigned short len = (unsigned short)(payload.size() + 7); + + std::vector data = {0x53, 0x43, (unsigned char)(len >> 8), (unsigned char)(len & 0xFF), packet_index, command}; + data.insert(data.end(), payload.begin(), payload.end()); + + /*-----------------------------------------------------*\ + | Calculate checksum | + \*-----------------------------------------------------*/ + unsigned int sum = 0; + for(std::size_t i = 0; i < data.size(); i++) + { + sum += data[i]; + } + data.push_back(sum & 0xFF); + + /*-----------------------------------------------------*\ + | Pad to a multiple of 64 bytes | + \*-----------------------------------------------------*/ + size_t remainder = data.size() % 64; + if(remainder > 0) + { + data.resize(data.size() + 64 - remainder, 0x00); + } + + /*-----------------------------------------------------*\ + | Send in chunks of 64 bytes | + \*-----------------------------------------------------*/ + for(size_t i = 0; i < data.size(); i += 64) + { + WriteReport(&data[i]); + + /*-------------------------------------------------*\ + | Microsleep to avoid overloading the device | + \*-------------------------------------------------*/ + std::this_thread::sleep_for(1ms); + } + + IncPacketIndex(); +} + +void RobobloqLightStripController::IncPacketIndex() +{ + packet_index = (packet_index + 1) & 0xFF; +} + +void RobobloqLightStripController::WriteReport(const unsigned char* data /* 64 bytes*/) +{ + /*-----------------------------------------------------*\ + | Add report ID | + \*-----------------------------------------------------*/ + std::vector report(65); + report[0] = 0x00; + memcpy(&report[1], data, 64); + + hid_write(dev, report.data(), report.size()); +} + +void RobobloqLightStripController::SendPacket(const std::vector& command, bool flush) +{ + unsigned char length = (unsigned char)(command.size() + 5); + + if(length > 64) + { + LOG_ERROR("[Robobloq] SendPacket: command size (%d) is too large, rejecting", (int)command.size()); + return; + } + + std::vector packet = {0x52, 0x42, length, packet_index}; + packet.insert(packet.end(), command.begin(), command.end()); + + unsigned int csum = 0; + for(std::size_t i = 0; i < packet.size(); i++) + { + csum += packet[i]; + } + packet.push_back(csum & 0xFF); + + packet.resize(64, 0x00); + + WriteReport(packet.data()); + IncPacketIndex(); + + if(flush) + { + /*-------------------------------------------------*\ + | Flush away any awaiting IN packets | + \*-------------------------------------------------*/ + unsigned char buf[64]; + + int res = 1; + while(res > 0) + { + res = hid_read_timeout(dev, buf, 64, 0); + } + } +} + +std::vector RobobloqLightStripController::SendPacketWithReply(const std::vector& command) +{ + unsigned char expected_id = packet_index; + LOG_DEBUG("[Robobloq] WithReply: sending command %02x, expecting reply for packet ID %02x", command[0], expected_id); + SendPacket(command, false); + + int tries = 3; + while(tries--) + { + unsigned char buf[64]; + int res = 1; + while(res > 0) + { + std::this_thread::sleep_for(10ms); + res = hid_read_timeout(dev, buf, 64, 1000); + LOG_DEBUG("[Robobloq] WithReply: read call returned %d packet ID %02x", res, buf[3]); + + if(buf[3] == expected_id) + { + return std::vector(buf, buf + res); + } + } + + /*-------------------------------------------------*\ + | Sometimes device gets stuck and we need to send a | + | new command before we can receive the previous | + | command's reply, so send a no-op | + \*-------------------------------------------------*/ + LOG_DEBUG("[Robobloq] No matching reply, sending no-op to try to unstick device"); + SendPacket({ROBOBLOQ_CMD_SET_OPEN_URL, 0x00}, false); + } + + /*-----------------------------------------------------*\ + | We weren't able to get a reply for this message | + \*-----------------------------------------------------*/ + return {}; +} + +/*** + * Asks device for information and update our attributes. When sent 0x82, the device + * returns information in the following form: + * + * header ID uuid v1.8.2 + * /------------\ /======\ /-----------------------\ /------\ + * 52 42 19 02 82 00 05 01 | 22 01 00 47 cd ab c5 74 | 25 bc b7 dc e5 01 08 02 + * | ? ? | ? + * | led count (71) + * display size (34") + * v1.8.2 is device fw version + */ +bool RobobloqLightStripController::RequestDeviceInfo() +{ + std::vector data = SendPacketWithReply({ROBOBLOQ_CMD_READ_DEVICE_INFO}); + if(data.size() < 24) + { + LOG_DEBUG("[Robobloq] Device Info Data too small! LED count -> 0. Non-Direct modes will work"); + return false; + } + + this->physical_size = data[8]; + this->led_count = data[11]; + + char id_buf[7]; + snprintf(id_buf, sizeof(id_buf), "%02x%02x%02x", data[5], data[6], data[7]); + this->id = std::string(id_buf); + + char uuid_buf[17]; + for(int i = 0; i < 8; i++) + { + snprintf(uuid_buf + (i * 2), sizeof(uuid_buf) - (i * 2), "%02x", data[12 + i]); + } + this->uuid = std::string(uuid_buf); + + char fw_buf[16]; + snprintf(fw_buf, sizeof(fw_buf), "%d.%d.%d", data[21], data[22], data[23]); + this->firmware_version = std::string(fw_buf); + + LOG_DEBUG("[Robobloq] Got device uuid: %s, fw: %s, size: %d\", leds: %d", + this->uuid.c_str(), this->firmware_version.c_str(), + this->physical_size, this->led_count); + + return true; +} diff --git a/Controllers/RobobloqLightStripController/RobobloqLightStripController.h b/Controllers/RobobloqLightStripController/RobobloqLightStripController.h new file mode 100644 index 0000000..e9d3efd --- /dev/null +++ b/Controllers/RobobloqLightStripController/RobobloqLightStripController.h @@ -0,0 +1,128 @@ +/*---------------------------------------------------------*\ +| RobobloqLightStripController.h | +| | +| Detector for Robobloq Monitor Light Strips | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +/*-----------------------------------------*\ +| Lighting modes | +\*-----------------------------------------*/ +enum +{ + /*-------------------------------------*\ + | Dynamic | + \*-------------------------------------*/ + ROBOBLOQ_MODE_DYNAMIC_RAINBOW = 0x00, + ROBOBLOQ_MODE_DYNAMIC_BREATHING = 0x01, + ROBOBLOQ_MODE_DYNAMIC_TWIST = 0x02, + ROBOBLOQ_MODE_DYNAMIC_BEAT = 0x03, + ROBOBLOQ_MODE_DYNAMIC_TWIRL = 0x04, + ROBOBLOQ_MODE_DYNAMIC_LEMON = 0x05, + ROBOBLOQ_MODE_DYNAMIC_ELECTRIC = 0x06, + /*-------------------------------------*\ + | Rhythm (synced to music) | + \*-------------------------------------*/ + ROBOBLOQ_MODE_RHYTHM_CIRCLES = 0x07, + ROBOBLOQ_MODE_RHYTHM_TWIRL = 0x08, + ROBOBLOQ_MODE_RHYTHM_SPARKLE = 0x09, + ROBOBLOQ_MODE_RHYTHM_BUBBLES = 0x10, + ROBOBLOQ_MODE_RHYTHM_SPOTLIGHT = 0x11, + ROBOBLOQ_MODE_RHYTHM_RAINBOW = 0x12, + ROBOBLOQ_MODE_RHYTHM_BLAST = 0x13, + /*-------------------------------------*\ + | Virtual modes | + \*-------------------------------------*/ + ROBOBLOQ_MODE_OFF = 0xFFFD, + ROBOBLOQ_MODE_STATIC = 0xFFFE, + ROBOBLOQ_MODE_DIRECT = 0xFFFF, +}; + +#define ROBOBLOQ_IS_DYNAMIC_EFFECT(x) ((x) >= ROBOBLOQ_MODE_DYNAMIC_RAINBOW && (x) <= ROBOBLOQ_MODE_DYNAMIC_ELECTRIC) +#define ROBOBLOQ_IS_RHYTHM_EFFECT(x) ((x) >= ROBOBLOQ_MODE_RHYTHM_CIRCLES && (x) <= ROBOBLOQ_MODE_RHYTHM_BLAST) + +/*-----------------------------------------*\ +| Commands | +\*-----------------------------------------*/ +enum +{ + ROBOBLOQ_CMD_SET_SYNC_SCREEN = 0x80, + ROBOBLOQ_CMD_READ_DEVICE_INFO = 0x82, + ROBOBLOQ_CMD_SET_EFFECT = 0x85, + ROBOBLOQ_CMD_SET_COLOR = 0x86, + ROBOBLOQ_CMD_SET_BRIGHTNESS = 0x87, + ROBOBLOQ_CMD_SET_DYNAMIC_SPEED = 0x8A, + ROBOBLOQ_CMD_SET_OPEN_URL = 0x93, + ROBOBLOQ_CMD_TURN_OFF = 0x97, +}; + +/*-----------------------------------------*\ +| Effect categories | +\*-----------------------------------------*/ +enum +{ + ROBOBLOQ_EFFECT_DYNAMIC = 0x02, + ROBOBLOQ_EFFECT_RHYTHM = 0x03, +}; + +#define ROBOBLOQ_DYNAMIC_SPEED_MAX 0x64 + +/*---------------------------------------------------------*\ +| Number of (start, end, R, G, B) tuples that will be sent | +| in a SetCustom call | +\*---------------------------------------------------------*/ +#define ROBOBLOQ_TUPLE_COUNT 34 + +class RobobloqLightStripController +{ +public: + RobobloqLightStripController(hid_device* dev_handle, const char* path, std::string dev_name); + ~RobobloqLightStripController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + std::string GetFirmwareVersion(); + int GetLEDCount(); + int GetLEDsPerSide(); + int GetPhysicalSizeInInches(); + + void Initialize(); + void SetColor(RGBColor c); + void SetLEDColor(int led, RGBColor c); + void SetColorRanges(const std::vector& ranges); + void SetBrightness(unsigned char brightness); + void SetDynamicEffect(unsigned char effect); + void SetCustom(const std::vector& colors); + void SetDynamicSpeed(unsigned char speed); + void TurnOff(); + + +private: + hid_device* dev; + std::string name; + std::string location; + std::string id; + std::string uuid; + std::string firmware_version; + unsigned char led_count; + unsigned char physical_size; + unsigned char packet_index; + + void SendPacket(const std::vector& command, bool flush = true); + std::vector SendPacketWithReply(const std::vector& command); + void SendMultiPacket(unsigned char command, const std::vector& payload); + bool RequestDeviceInfo(); + void SendSyncScreen(const std::vector& color_bytes); + void IncPacketIndex(); + void WriteReport(const unsigned char* data); +}; diff --git a/Controllers/RobobloqLightStripController/RobobloqLightStripControllerDetect.cpp b/Controllers/RobobloqLightStripController/RobobloqLightStripControllerDetect.cpp new file mode 100644 index 0000000..d8bf933 --- /dev/null +++ b/Controllers/RobobloqLightStripController/RobobloqLightStripControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RobobloqLightStripControllerDetect.cpp | +| | +| Detector for Robobloq RGB Light Strips | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RobobloqLightStripController.h" +#include "RGBController_RobobloqLightStrip.h" + +#define ROBOBLOQ_USB_VID 0x1A86 + +/*----------------------------------------------------------*\ +| | +| DetectRobobloqLightStripController | +| | +| Detect Robobloq RGB Light Strips | +| | +\*----------------------------------------------------------*/ + +void DetectRobobloqLightStripController + ( + hid_device_info* info, + const std::string& name + ) +{ + hid_device* dev = hid_open_path(info->path); + if(dev != nullptr) + { + RobobloqLightStripController* controller = new RobobloqLightStripController(dev, info->path, name); + RGBController_RobobloqLightStrip* rgb_controller = new RGBController_RobobloqLightStrip(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("Robobloq Monitor Light Strip", DetectRobobloqLightStripController, ROBOBLOQ_USB_VID, 0xFE07, 0xFF00, 0x01); diff --git a/Controllers/RobobloqLightStripController/RobobloqRangeMerger.cpp b/Controllers/RobobloqLightStripController/RobobloqRangeMerger.cpp new file mode 100644 index 0000000..9e91511 --- /dev/null +++ b/Controllers/RobobloqLightStripController/RobobloqRangeMerger.cpp @@ -0,0 +1,155 @@ +/*---------------------------------------------------------*\ +| RobobloqRangeMerger.cpp | +| | +| Helper for merging LED ranges for Robobloq | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RobobloqRangeMerger.h" +#include +#include +#include + + +/*** + * The official application does not send a full set of LED values to the device (i.e. 71 in the + * 34" case) but rather compresses the 71 RGB values down to exactly 34 ranges. It seems to use + * pre-configured ranges, but we can do better by calculating the ranges to use that create the + * least error, preserving single pixel detail. + * + * We use a greedy merge algorithm: initially define a 1-length range for each pixel. Try to merge + * any 2 adjacent ranges and pick the merge that creates the least difference. Repeat until we + * have 34. + */ +std::vector MergeRobobloqRanges(const std::vector& colors, int tuple_count) +{ + if(tuple_count == 0) + { + LOG_ERROR("[Robobloq] MergeRobobloqRanges called with tuple_count == 0"); + return {}; + } + + struct LEDRange + { + int start; /* Start LED (1-indexed) */ + int end; /* End LED */ + int n; /* Number of LEDs in range */ + double sum_r; /* Sum of R values */ + double sum_g; + double sum_b; + double term; /* = sum_r^2 + sum_g^2 + sum_b^2 / n */ + }; + + int num_leds = (int)colors.size(); + std::vector ranges; + ranges.reserve(num_leds); + + /*-----------------------------------------------------*\ + | 1. Initialize ranges (one per pixel) | + \*-----------------------------------------------------*/ + for(int i = 0; i < num_leds; i++) + { + LEDRange r; + r.start = i + 1; /* 1-based index */ + r.end = i + 1; + r.n = 1; + r.sum_r = RGBGetRValue(colors[i]); + r.sum_g = RGBGetGValue(colors[i]); + r.sum_b = RGBGetBValue(colors[i]); + r.term = (r.sum_r * r.sum_r + r.sum_g * r.sum_g + r.sum_b * r.sum_b); + ranges.push_back(r); + } + + /*-----------------------------------------------------*\ + | 2. Merge until we have tuple_count tuples | + \*-----------------------------------------------------*/ + while((int)ranges.size() > tuple_count) + { + double best_delta = std::numeric_limits::max(); + int best_idx = -1; + double best_merged_term = 0; + + /*-----------------------------------------------------*\ + | Find best adjacent pair to merge | + \*-----------------------------------------------------*/ + + /*** + * Minimise Sum of Squared Errors (SSE) = sum(pixel - average)^2 + * = sum(pixel^2) - sum(n*average^2) + * + * As sum(pixel^2) is constant, we need to maximise sum(n*average^2). + * + * Since average = sum / n: + * n * average^2 = n * (sum / n)^2 = n * (sum^2 / n^2) = sum^2 / n + * + * We need to maximise sum(sum_k^2/n_k) for all ranges k. We cache the + * sum^2/n value as 'term'. + **/ + for(size_t i = 0; i < ranges.size() - 1; i++) + { + const LEDRange& r1 = ranges[i]; + const LEDRange& r2 = ranges[i+1]; + + double sum_r = r1.sum_r + r2.sum_r; + double sum_g = r1.sum_g + r2.sum_g; + double sum_b = r1.sum_b + r2.sum_b; + int n = r1.n + r2.n; + + double term_merged = (sum_r * sum_r + sum_g * sum_g + sum_b * sum_b) / n; + double delta = r1.term + r2.term - term_merged; + + if(delta < best_delta) + { + best_delta = delta; + best_idx = (int)i; + best_merged_term = term_merged; + } + } + + if(best_idx != -1) + { + /*---------------------------------------------*\ + | Merge best_idx and best_idx+1 | + \*---------------------------------------------*/ + LEDRange& r_left = ranges[best_idx]; + const LEDRange& r_right = ranges[best_idx+1]; + + r_left.end = r_right.end; + r_left.n += r_right.n; + r_left.sum_r += r_right.sum_r; + r_left.sum_g += r_right.sum_g; + r_left.sum_b += r_right.sum_b; + r_left.term = best_merged_term; + + ranges.erase(ranges.begin() + best_idx + 1); + } + else + { + /*---------------------------------------------*\ + | No merge possible | + \*---------------------------------------------*/ + break; + } + } + + std::vector color_bytes; + color_bytes.reserve(tuple_count * 5); + + for(size_t i = 0; i < ranges.size(); i++) + { + const LEDRange& r = ranges[i]; + unsigned char avg_r = (unsigned char)std::round(r.sum_r / r.n); + unsigned char avg_g = (unsigned char)std::round(r.sum_g / r.n); + unsigned char avg_b = (unsigned char)std::round(r.sum_b / r.n); + + color_bytes.push_back((unsigned char)r.start); + color_bytes.push_back(avg_r); + color_bytes.push_back(avg_g); + color_bytes.push_back(avg_b); + color_bytes.push_back((unsigned char)r.end); + } + + return color_bytes; +} diff --git a/Controllers/RobobloqLightStripController/RobobloqRangeMerger.h b/Controllers/RobobloqLightStripController/RobobloqRangeMerger.h new file mode 100644 index 0000000..25b874e --- /dev/null +++ b/Controllers/RobobloqLightStripController/RobobloqRangeMerger.h @@ -0,0 +1,15 @@ +/*---------------------------------------------------------*\ +| RobobloqRangeMerger.h | +| | +| Helper for merging LED ranges for Robobloq | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +std::vector MergeRobobloqRanges(const std::vector& colors, int tuple_count); diff --git a/Controllers/RoccatController/RoccatBurstController/RGBController_RoccatBurst.cpp b/Controllers/RoccatController/RoccatBurstController/RGBController_RoccatBurst.cpp new file mode 100644 index 0000000..af16f0d --- /dev/null +++ b/Controllers/RoccatController/RoccatBurstController/RGBController_RoccatBurst.cpp @@ -0,0 +1,187 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatBurst.cpp | +| | +| RGBController for Roccat Burst | +| | +| Morgan Guimard (morg) 01 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RoccatBurst.h" + +/**------------------------------------------------------------------*\ + @name Roccat Burst Mouse + @category Mouse + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRoccatBurstCoreControllers,DetectRoccatBurstProControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RoccatBurst::RGBController_RoccatBurst(RoccatBurstController* controller_ptr, unsigned int leds_count): + leds_count(leds_count) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Roccat"; + type = DEVICE_TYPE_MOUSE; + description = "Roccat Burst Mouse Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ROCCAT_BURST_DIRECT_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = ROCCAT_BURST_STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness = ROCCAT_BURST_BRIGHTNESS_MAX; + Static.brightness_min = ROCCAT_BURST_BRIGHTNESS_MIN; + Static.brightness_max = ROCCAT_BURST_BRIGHTNESS_MAX; + Static.colors.resize(leds_count); + modes.push_back(Static); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = ROCCAT_BURST_WAVE_MODE_VALUE; + Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness = ROCCAT_BURST_BRIGHTNESS_MAX; + Rainbow.brightness_min = ROCCAT_BURST_BRIGHTNESS_MIN; + Rainbow.brightness_max = ROCCAT_BURST_BRIGHTNESS_MAX; + Rainbow.speed = ROCCAT_BURST_SPEED_MIN; + Rainbow.speed_min = ROCCAT_BURST_SPEED_MIN; + Rainbow.speed_max = ROCCAT_BURST_SPEED_MAX; + modes.push_back(Rainbow); + + mode HeartBeat; + HeartBeat.name = "HeartBeat"; + HeartBeat.value = ROCCAT_BURST_HEARTBEAT_MODE_VALUE; + HeartBeat.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + HeartBeat.color_mode = MODE_COLORS_MODE_SPECIFIC; + HeartBeat.brightness = ROCCAT_BURST_BRIGHTNESS_MAX; + HeartBeat.brightness_min = ROCCAT_BURST_BRIGHTNESS_MIN; + HeartBeat.brightness_max = ROCCAT_BURST_BRIGHTNESS_MAX; + HeartBeat.speed = ROCCAT_BURST_SPEED_MIN; + HeartBeat.speed_min = ROCCAT_BURST_SPEED_MIN; + HeartBeat.speed_max = ROCCAT_BURST_SPEED_MAX; + HeartBeat.colors.resize(leds_count); + modes.push_back(HeartBeat); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ROCCAT_BURST_BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.brightness = ROCCAT_BURST_BRIGHTNESS_MAX; + Breathing.brightness_min = ROCCAT_BURST_BRIGHTNESS_MIN; + Breathing.brightness_max = ROCCAT_BURST_BRIGHTNESS_MAX; + Breathing.speed = ROCCAT_BURST_SPEED_MIN; + Breathing.speed_min = ROCCAT_BURST_SPEED_MIN; + Breathing.speed_max = ROCCAT_BURST_SPEED_MAX; + Breathing.colors.resize(leds_count); + modes.push_back(Breathing); + + mode Blinking; + Blinking.name = "Blinking"; + Blinking.value = ROCCAT_BURST_BLINKING_MODE_VALUE; + Blinking.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Blinking.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blinking.brightness = ROCCAT_BURST_BRIGHTNESS_MAX; + Blinking.brightness_min = ROCCAT_BURST_BRIGHTNESS_MIN; + Blinking.brightness_max = ROCCAT_BURST_BRIGHTNESS_MAX; + Blinking.speed = ROCCAT_BURST_SPEED_MIN; + Blinking.speed_min = ROCCAT_BURST_SPEED_MIN; + Blinking.speed_max = ROCCAT_BURST_SPEED_MAX; + Blinking.colors.resize(leds_count); + modes.push_back(Blinking); + + SetupZones(); +} + +RGBController_RoccatBurst::~RGBController_RoccatBurst() +{ + delete controller; +} + +void RGBController_RoccatBurst::SetupZones() +{ + zone new_zone; + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = leds_count; + new_zone.leds_max = leds_count; + new_zone.leds_count = leds_count; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + std::string led_names[2] = + { + "Scroll Wheel", + "Logo" + }; + + for(unsigned int i = 0; i < leds_count; i++) + { + led new_led; + new_led.name = led_names[i]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_RoccatBurst::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatBurst::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_RoccatBurst::UpdateZoneLEDs(int /*zone_idx*/) +{ + const mode& active = modes[active_mode]; + + if(active.value == ROCCAT_BURST_DIRECT_MODE_VALUE) + { + controller->SendDirect(colors); + } + else + { + controller->SetMode(active.colors, active.value, active.speed, active.brightness, active.color_mode, active.flags); + } + +} + +void RGBController_RoccatBurst::UpdateSingleLED(int /*led_idx*/) +{ + UpdateZoneLEDs(0); +} + +void RGBController_RoccatBurst::DeviceUpdateMode() +{ + if(modes[active_mode].value == ROCCAT_BURST_DIRECT_MODE_VALUE) + { + controller->SetupDirectMode(); + } + else + { + DeviceUpdateLEDs(); + } +} diff --git a/Controllers/RoccatController/RoccatBurstController/RGBController_RoccatBurst.h b/Controllers/RoccatController/RoccatBurstController/RGBController_RoccatBurst.h new file mode 100644 index 0000000..fac33aa --- /dev/null +++ b/Controllers/RoccatController/RoccatBurstController/RGBController_RoccatBurst.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatBurst.h | +| | +| RGBController for Roccat Burst | +| | +| Morgan Guimard (morg) 01 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatBurstController.h" + +class RGBController_RoccatBurst : public RGBController +{ +public: + RGBController_RoccatBurst(RoccatBurstController* controller_ptr, unsigned int leds_count); + ~RGBController_RoccatBurst(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatBurstController* controller; + unsigned int leds_count; +}; diff --git a/Controllers/RoccatController/RoccatBurstController/RoccatBurstController.cpp b/Controllers/RoccatController/RoccatBurstController/RoccatBurstController.cpp new file mode 100644 index 0000000..ffc67b9 --- /dev/null +++ b/Controllers/RoccatController/RoccatBurstController/RoccatBurstController.cpp @@ -0,0 +1,166 @@ +/*---------------------------------------------------------*\ +| RoccatBurstController.cpp | +| | +| Driver for Roccat Burst | +| | +| Morgan Guimard (morg) 24 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RoccatBurstController.h" +#include "StringUtils.h" + +RoccatBurstController::RoccatBurstController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + + SetupDirectMode(); +} + +RoccatBurstController::~RoccatBurstController() +{ + hid_close(dev); +} + +std::string RoccatBurstController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RoccatBurstController::GetNameString() +{ + return(name); +} + +std::string RoccatBurstController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void RoccatBurstController::SetupDirectMode() +{ + SwitchControl(true); +} + +void RoccatBurstController::SwitchControl(bool direct) +{ + unsigned char usb_buf[ROCCAT_BURST_CONTROL_MODE_PACKET_LENGTH]; + + usb_buf[0x00] = 0x0E; + usb_buf[0x01] = 0x06; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = direct ? 0x01 : 0x00; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = 0xFF; + + hid_send_feature_report(dev, usb_buf, ROCCAT_BURST_CONTROL_MODE_PACKET_LENGTH); +} + +void RoccatBurstController::SendDirect(std::vector colors) +{ + unsigned char usb_buf[ROCCAT_BURST_DIRECT_MODE_PACKET_LENGTH]; + + memset(usb_buf, 0x00, ROCCAT_BURST_DIRECT_MODE_PACKET_LENGTH); + + usb_buf[0x00] = ROCCAT_BURST_DIRECT_MODE_REPORT_ID; + usb_buf[0x01] = ROCCAT_BURST_DIRECT_MODE_BYTE; + + for(unsigned int i = 0; i < colors.size(); i++) + { + usb_buf[0x02 + 3 * i] = RGBGetRValue(colors[i]); + usb_buf[0x03 + 3 * i] = RGBGetGValue(colors[i]); + usb_buf[0x04 + 3 * i] = RGBGetBValue(colors[i]); + } + + hid_send_feature_report(dev, usb_buf, ROCCAT_BURST_DIRECT_MODE_PACKET_LENGTH); +} + +void RoccatBurstController::SetMode(std::vector colors, unsigned char mode_value, unsigned char speed, unsigned char brightness, unsigned int color_mode, unsigned int mode_flags) +{ + /*---------------------------------------------------------*\ + | 1. Read from flash | + \*---------------------------------------------------------*/ + unsigned char usb_buf[ROCCAT_BURST_FLASH_PACKET_LENGTH]; + memset(usb_buf, 0x00, ROCCAT_BURST_FLASH_PACKET_LENGTH); + + usb_buf[0x00] = 0x06; + + hid_get_feature_report(dev, usb_buf, ROCCAT_BURST_FLASH_PACKET_LENGTH); + + /*---------------------------------------------------------*\ + | 2. Update needed bytes | + \*---------------------------------------------------------*/ + usb_buf[0x01] = 0x3F; + usb_buf[0x03] = 0x06; + usb_buf[0x04] = 0x06; + usb_buf[0x05] = 0x1F; + + usb_buf[30] = mode_value; + usb_buf[31] = mode_flags & MODE_FLAG_HAS_SPEED ? speed : 0xFF; + usb_buf[32] = brightness; + + usb_buf[34] = 0xFF; + + if(color_mode & MODE_COLORS_MODE_SPECIFIC) + { + usb_buf[36] = 0x14; + usb_buf[37] = 0xFF; + for(unsigned int i = 0; i < colors.size(); i++) + { + usb_buf[38 + 10 * i] = RGBGetRValue(colors[i]); + usb_buf[39 + 10 * i] = RGBGetGValue(colors[i]); + usb_buf[40 + 10 * i] = RGBGetBValue(colors[i]); + } + } + else if (color_mode & MODE_COLORS_NONE) + { + for(unsigned int i = 0; i < colors.size(); i++) + { + usb_buf[38 + 10 * i] = 0xF4; + usb_buf[39 + 10 * i] = 0x00; + usb_buf[40 + 10 * i] = 0x00; + } + } + + unsigned int crc = CalculateCRC(&usb_buf[0]); + + usb_buf[61] = (unsigned char) crc; + usb_buf[62] = crc >> 8; + + /*---------------------------------------------------------*\ + | 3. Send to flash | + \*---------------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, ROCCAT_BURST_FLASH_PACKET_LENGTH); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + /*---------------------------------------------------------*\ + | 4. Switch to built-in mode | + \*---------------------------------------------------------*/ + SwitchControl(false); +} + +unsigned int RoccatBurstController::CalculateCRC(unsigned char* bytes) +{ + unsigned int crc = 0; + + for(unsigned int i = 0; i < ROCCAT_BURST_FLASH_PACKET_LENGTH - 2; i++) + { + crc += bytes[i]; + } + + return crc; +} diff --git a/Controllers/RoccatController/RoccatBurstController/RoccatBurstController.h b/Controllers/RoccatController/RoccatBurstController/RoccatBurstController.h new file mode 100644 index 0000000..5c02212 --- /dev/null +++ b/Controllers/RoccatController/RoccatBurstController/RoccatBurstController.h @@ -0,0 +1,70 @@ +/*---------------------------------------------------------*\ +| RoccatBurstController.h | +| | +| Driver for Roccat Burst | +| | +| Morgan Guimard (morg) 01 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include + +#define ROCCAT_BURST_CONTROL_MODE_PACKET_LENGTH 6 +#define ROCCAT_BURST_DIRECT_MODE_PACKET_LENGTH 11 +#define ROCCAT_BURST_FLASH_PACKET_LENGTH 63 +#define ROCCAT_BURST_FLASH_REPORT_ID 0x06 +#define ROCCAT_BURST_DIRECT_MODE_REPORT_ID 0x0D +#define ROCCAT_BURST_DIRECT_MODE_BYTE 0x0B +#define ROCCAT_BURST_CORE_NUMBER_OF_LEDS 1 +#define ROCCAT_BURST_PRO_NUMBER_OF_LEDS 2 + +enum +{ + ROCCAT_BURST_DIRECT_MODE_VALUE = 0x00, + ROCCAT_BURST_STATIC_MODE_VALUE = 0x01, + ROCCAT_BURST_WAVE_MODE_VALUE = 0x0A, + ROCCAT_BURST_HEARTBEAT_MODE_VALUE = 0x04, + ROCCAT_BURST_BREATHING_MODE_VALUE = 0x03, + ROCCAT_BURST_BLINKING_MODE_VALUE = 0x02 +}; + +enum +{ + ROCCAT_BURST_SPEED_MIN = 0x01, + ROCCAT_BURST_SPEED_MAX = 0x0B, + ROCCAT_BURST_BRIGHTNESS_MIN = 0x00, + ROCCAT_BURST_BRIGHTNESS_MAX = 0xFF +}; + +class RoccatBurstController +{ +public: + RoccatBurstController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~RoccatBurstController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetupDirectMode(); + void SendDirect(std::vector colors); + void SetMode(std::vector colors, + unsigned char mode_value, + unsigned char speed, + unsigned char brightness, + unsigned int color_mode, + unsigned int mode_flags + ); +private: + hid_device* dev; + std::string location; + std::string name; + + unsigned int CalculateCRC(unsigned char* bytes); + void SwitchControl(bool direct); +}; diff --git a/Controllers/RoccatController/RoccatBurstProAirController/RGBController_RoccatBurstProAir.cpp b/Controllers/RoccatController/RoccatBurstProAirController/RGBController_RoccatBurstProAir.cpp new file mode 100644 index 0000000..1f8bc7b --- /dev/null +++ b/Controllers/RoccatController/RoccatBurstProAirController/RGBController_RoccatBurstProAir.cpp @@ -0,0 +1,148 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatBurstProAir.cpp | +| | +| RGBController for Roccat Burst Pro Air | +| | +| Morgan Guimard (morg) 16 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RoccatBurstProAir.h" + +/**------------------------------------------------------------------*\ + @name Roccat Burst Pro Air + @category Mouse + @type USB + @save :warning: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRoccatBurstProAirCoreControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RoccatBurstProAir::RGBController_RoccatBurstProAir(RoccatBurstProAirController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Roccat"; + type = DEVICE_TYPE_MOUSE; + description = "Roccat Burst Pro Air Mouse Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ROCCAT_BURST_PRO_AIR_DIRECT_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MAX; + Direct.brightness_min = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MIN; + Direct.brightness_max = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MAX; + modes.push_back(Direct); + + mode Blink; + Blink.name = "Blink"; + Blink.value = ROCCAT_BURST_PRO_AIR_BLINK_MODE_VALUE; + Blink.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Blink.color_mode = MODE_COLORS_PER_LED; + Blink.brightness = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MAX; + Blink.brightness_min = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MIN; + Blink.brightness_max = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MAX; + Blink.speed = ROCCAT_BURST_PRO_AIR_SPEED_MIN; + Blink.speed_min = ROCCAT_BURST_PRO_AIR_SPEED_MIN; + Blink.speed_max = ROCCAT_BURST_PRO_AIR_SPEED_MAX; + modes.push_back(Blink); + + mode Breath; + Breath.name = "Breathing"; + Breath.value = ROCCAT_BURST_PRO_AIR_BREATH_MODE_VALUE; + Breath.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breath.color_mode = MODE_COLORS_PER_LED; + Breath.brightness = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MAX; + Breath.brightness_min = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MIN; + Breath.brightness_max = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MAX; + Breath.speed = ROCCAT_BURST_PRO_AIR_SPEED_MIN; + Breath.speed_min = ROCCAT_BURST_PRO_AIR_SPEED_MIN; + Breath.speed_max = ROCCAT_BURST_PRO_AIR_SPEED_MAX; + modes.push_back(Breath); + + mode Wave; + Wave.name = "Wave"; + Wave.value = ROCCAT_BURST_PRO_AIR_WAVE_MODE_VALUE; + Wave.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Wave.color_mode = MODE_COLORS_PER_LED; + Wave.brightness = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MAX; + Wave.brightness_min = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MIN; + Wave.brightness_max = ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MAX; + Wave.speed = ROCCAT_BURST_PRO_AIR_SPEED_MIN; + Wave.speed_min = ROCCAT_BURST_PRO_AIR_SPEED_MIN; + Wave.speed_max = ROCCAT_BURST_PRO_AIR_SPEED_MAX; + modes.push_back(Wave); + + SetupZones(); +} + +RGBController_RoccatBurstProAir::~RGBController_RoccatBurstProAir() +{ + delete controller; +} + +void RGBController_RoccatBurstProAir::SetupZones() +{ + zone new_zone; + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = ROCCAT_BURST_PRO_AIR_PRO_NUMBER_OF_LEDS; + new_zone.leds_max = ROCCAT_BURST_PRO_AIR_PRO_NUMBER_OF_LEDS; + new_zone.leds_count = ROCCAT_BURST_PRO_AIR_PRO_NUMBER_OF_LEDS; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + std::string led_names[ROCCAT_BURST_PRO_AIR_PRO_NUMBER_OF_LEDS] = + { + "Scroll Wheel", + "Logo", + "Left button", + "Right button" + }; + + for(unsigned int i = 0; i < ROCCAT_BURST_PRO_AIR_PRO_NUMBER_OF_LEDS; i++) + { + led new_led; + new_led.name = led_names[i]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_RoccatBurstProAir::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatBurstProAir::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_RoccatBurstProAir::UpdateZoneLEDs(int /*zone_idx*/) +{ + controller->SetColors(colors); +} + +void RGBController_RoccatBurstProAir::UpdateSingleLED(int /*led_idx*/) +{ + UpdateZoneLEDs(0); +} + +void RGBController_RoccatBurstProAir::DeviceUpdateMode() +{ + const mode& active = modes[active_mode]; + controller->SetModeValues(active.value, active.speed, active.brightness); +} diff --git a/Controllers/RoccatController/RoccatBurstProAirController/RGBController_RoccatBurstProAir.h b/Controllers/RoccatController/RoccatBurstProAirController/RGBController_RoccatBurstProAir.h new file mode 100644 index 0000000..35ef709 --- /dev/null +++ b/Controllers/RoccatController/RoccatBurstProAirController/RGBController_RoccatBurstProAir.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatBurstProAir.h | +| | +| RGBController for Roccat Burst Pro Air | +| | +| Morgan Guimard (morg) 16 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatBurstProAirController.h" + +class RGBController_RoccatBurstProAir : public RGBController +{ +public: + RGBController_RoccatBurstProAir(RoccatBurstProAirController* controller_ptr); + ~RGBController_RoccatBurstProAir(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + +private: + RoccatBurstProAirController* controller; +}; diff --git a/Controllers/RoccatController/RoccatBurstProAirController/RoccatBurstProAirController.cpp b/Controllers/RoccatController/RoccatBurstProAirController/RoccatBurstProAirController.cpp new file mode 100644 index 0000000..29c573f --- /dev/null +++ b/Controllers/RoccatController/RoccatBurstProAirController/RoccatBurstProAirController.cpp @@ -0,0 +1,94 @@ +/*---------------------------------------------------------*\ +| RoccatBurstProAirController.cpp | +| | +| Driver for Roccat Burst Pro Air | +| | +| Morgan Guimard (morg) 16 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RoccatBurstProAirController.h" +#include "StringUtils.h" + +RoccatBurstProAirController::RoccatBurstProAirController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +RoccatBurstProAirController::~RoccatBurstProAirController() +{ + hid_close(dev); +} + +std::string RoccatBurstProAirController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RoccatBurstProAirController::GetNameString() +{ + return(name); +} + +std::string RoccatBurstProAirController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void RoccatBurstProAirController::SetColors(std::vector colors) +{ + unsigned char usb_buf[ROCCAT_BURST_PRO_AIR_REPORT_SIZE]; + memset(usb_buf, 0x00, ROCCAT_BURST_PRO_AIR_REPORT_SIZE); + + usb_buf[0] = ROCCAT_BURST_PRO_AIR_REPORT_ID; + + usb_buf[1] = 0x01; + usb_buf[2] = 0x4C; + usb_buf[3] = 0x06; + usb_buf[4] = 0x14; + + for(unsigned char i = 0; i < colors.size(); i++) + { + usb_buf[5 + 5 * i] = i + 1; + usb_buf[6 + 5 * i] = 0xFF; + usb_buf[7 + 5 * i] = RGBGetRValue(colors[i]); + usb_buf[8 + 5 * i] = RGBGetGValue(colors[i]); + usb_buf[9 + 5 * i] = RGBGetBValue(colors[i]); + } + + hid_send_feature_report(dev, usb_buf, ROCCAT_BURST_PRO_AIR_REPORT_SIZE); +} + +void RoccatBurstProAirController::SetModeValues(unsigned char mode_value, unsigned char speed, unsigned char brightness) +{ + unsigned char usb_buf[ROCCAT_BURST_PRO_AIR_REPORT_SIZE]; + memset(usb_buf, 0x00, ROCCAT_BURST_PRO_AIR_REPORT_SIZE); + + usb_buf[0] = ROCCAT_BURST_PRO_AIR_REPORT_ID; + + usb_buf[1] = 0x01; + usb_buf[2] = 0x4C; + usb_buf[3] = 0x06; + usb_buf[4] = 0x06; + + usb_buf[5] = mode_value; + usb_buf[6] = speed; + usb_buf[7] = brightness; + + usb_buf[8] = 0x0F; + + hid_send_feature_report(dev, usb_buf, ROCCAT_BURST_PRO_AIR_REPORT_SIZE); +} diff --git a/Controllers/RoccatController/RoccatBurstProAirController/RoccatBurstProAirController.h b/Controllers/RoccatController/RoccatBurstProAirController/RoccatBurstProAirController.h new file mode 100644 index 0000000..67a20b6 --- /dev/null +++ b/Controllers/RoccatController/RoccatBurstProAirController/RoccatBurstProAirController.h @@ -0,0 +1,54 @@ +/*---------------------------------------------------------*\ +| RoccatBurstProAirController.h | +| | +| Driver for Roccat Burst Pro Air | +| | +| Morgan Guimard (morg) 16 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +#define ROCCAT_BURST_PRO_AIR_REPORT_ID 0x06 +#define ROCCAT_BURST_PRO_AIR_REPORT_SIZE 30 +#define ROCCAT_BURST_PRO_AIR_PRO_NUMBER_OF_LEDS 4 + +enum +{ + ROCCAT_BURST_PRO_AIR_DIRECT_MODE_VALUE = 0x01, + ROCCAT_BURST_PRO_AIR_BLINK_MODE_VALUE = 0x02, + ROCCAT_BURST_PRO_AIR_BREATH_MODE_VALUE = 0x03, + ROCCAT_BURST_PRO_AIR_WAVE_MODE_VALUE = 0x04 +}; + +enum +{ + ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MIN = 0x00, + ROCCAT_BURST_PRO_AIR_BRIGHTNESS_MAX = 0xFF, + ROCCAT_BURST_PRO_AIR_SPEED_MIN = 0x00, + ROCCAT_BURST_PRO_AIR_SPEED_MAX = 0x0B +}; + +class RoccatBurstProAirController +{ +public: + RoccatBurstProAirController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~RoccatBurstProAirController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetColors(std::vector colors); + void SetModeValues(unsigned char mode_value, unsigned char speed, unsigned char brightness); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/RoccatController/RoccatControllerDetect.cpp b/Controllers/RoccatController/RoccatControllerDetect.cpp new file mode 100644 index 0000000..5ba4251 --- /dev/null +++ b/Controllers/RoccatController/RoccatControllerDetect.cpp @@ -0,0 +1,373 @@ +/*---------------------------------------------------------*\ +| RoccatControllerDetect.cpp | +| | +| Detector for Roccat devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RoccatBurstController.h" +#include "RoccatBurstProAirController.h" +#include "RoccatKoneAimoController.h" +#include "RoccatKoneProController.h" +#include "RoccatKoneProAirController.h" +#include "RoccatKoneXPController.h" +#include "RoccatSenseAimoController.h" +#include "RoccatVulcanKeyboardController.h" +#include "RoccatKovaController.h" +#include "RoccatEloController.h" +#include "RGBController_RoccatBurst.h" +#include "RGBController_RoccatBurstProAir.h" +#include "RGBController_RoccatHordeAimo.h" +#include "RGBController_RoccatKoneAimo.h" +#include "RGBController_RoccatKonePro.h" +#include "RGBController_RoccatKoneProAir.h" +#include "RGBController_RoccatKoneXP.h" +#include "RGBController_RoccatSenseAimo.h" +#include "RGBController_RoccatVulcanKeyboard.h" +#include "RGBController_RoccatKova.h" +#include "RGBController_RoccatElo.h" +#include +#include + +#define ROCCAT_VID 0x1E7D +#define TURTLE_BEACH_VID 0x10F5 + +/*--------------------------------------------------------------------------------*\ +| KEYBOARDS | +| RoccatVulcanKeyboardController PIDs defined in RoccatVulcanKeyboardController.h | +\*--------------------------------------------------------------------------------*/ +#define ROCCAT_HORDE_AIMO_PID 0x303E + +/*-----------------------------------------------------------------*\ +| MICE | +\*-----------------------------------------------------------------*/ +#define ROCCAT_BURST_CORE_PID 0x2DE6 +#define ROCCAT_BURST_PRO_PID 0x2DE1 +#define ROCCAT_BURST_PRO_AIR_PID 0x2CA6 +#define ROCCAT_KONE_AIMO_PID 0x2E27 +#define ROCCAT_KONE_AIMO_16K_PID 0x2E2C +#define ROCCAT_KONE_PRO_PID 0x2C88 +#define ROCCAT_KONE_PRO_AIR_PID 0x2C8E +#define ROCCAT_KONE_PRO_AIR_WIRED_PID 0x2C92 +#define ROCCAT_KONE_XP_PID 0x2C8B +#define ROCCAT_KOVA_PID 0x2CEE + +/*-----------------------------------------------------------------*\ +| MOUSEMATS | +\*-----------------------------------------------------------------*/ +#define ROCCAT_SENSE_AIMO_MID_PID 0x343A +#define ROCCAT_SENSE_AIMO_XXL_PID 0x343B + +/*-----------------------------------------------------------------*\ +| HEADSETS | +\*-----------------------------------------------------------------*/ +#define ROCCAT_ELO_PID 0x3A34 + +void DetectRoccatMouseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatKoneAimoController * controller = new RoccatKoneAimoController(dev, info->path, name); + RGBController_RoccatKoneAimo * rgb_controller = new RGBController_RoccatKoneAimo(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +/*---------------------------------------------------------------------------------*\ +| Tracks the paths used in DetectRoccatVulcanKeyboardControllers so multiple Roccat | +| devices can be detected without all controlling the same device. | +\*---------------------------------------------------------------------------------*/ +static std::unordered_set used_paths; + +/*--------------------------------------------------------------------------------*\ +| Removes all entries in used_paths so device discovery does not skip any of them. | +\*--------------------------------------------------------------------------------*/ +void ResetRoccatVulcanKeyboardControllersPaths() +{ + used_paths.clear(); +} + +void DetectRoccatVulcanKeyboardControllers(hid_device_info* info, const std::string& name) +{ + /*-------------------------------------------------------------------------------------------------*\ + | Create a local copy of the HID enumerations for the Roccat Vulcan Keyboard VID/PID and iterate | + | through it. This prevents detection from failing if interface 1 comes before interface 0 in the | + | main info list. | + \*-------------------------------------------------------------------------------------------------*/ + hid_device* dev_ctrl = nullptr; + hid_device* dev_led = nullptr; + hid_device_info* info_full = hid_enumerate(info->vendor_id, info->product_id); + hid_device_info* info_temp = info_full; + + /*--------------------------------------------------------------------------------------------*\ + | Keep track of paths so they can be added to used_paths only if both interfaces can be found. | + \*--------------------------------------------------------------------------------------------*/ + std::string dev_ctrl_path; + std::string dev_led_path; + int dev_led_page; + int dev_ctrl_page; + int dev_led_iface = 3; + int dev_ctrl_iface = 1; + + switch(info->product_id) + { + case ROCCAT_VULCAN_PRO_PID: + case ROCCAT_VULCAN_TKL_PRO_PID: + case ROCCAT_PYRO_PID: + case ROCCAT_MAGMA_PID: + case ROCCAT_MAGMA_MINI_PID: + case ROCCAT_VULCAN_II_PID: + case ROCCAT_VULCAN_II_MAX_PID: + case TURTLE_BEACH_VULCAN_II_PID: + dev_led_page = 0xFF00; + dev_ctrl_page = 0xFF01; + dev_led_iface = 3; + dev_ctrl_iface = 1; + break; + case TURTLE_BEACH_VULCAN_II_TKL_PRO_PID: + dev_led_page = 0xFF00; + dev_ctrl_page = 0x0001; + dev_led_iface = 4; + dev_ctrl_iface = 1; + break; + default: + dev_led_page = 0x0001; + dev_ctrl_page = 0x000B; + dev_led_iface = 3; + dev_ctrl_iface = 1; + break; + } + + while(info_temp) + { + /*----------------------------------------------------------------------------------------*\ + | Check for paths used on an already registered Roccat Vulcan Keyboard controller to avoid | + | registering multiple controllers that refer to the same physical hardware. | + \*----------------------------------------------------------------------------------------*/ + if(info_temp->vendor_id == info->vendor_id + && info_temp->product_id == info->product_id + && used_paths.find(info_temp->path) == used_paths.end() ) + { + if(info_temp->interface_number == dev_ctrl_iface && info_temp->usage_page == dev_ctrl_page) + { + dev_ctrl = hid_open_path(info_temp->path); + dev_ctrl_path = info_temp->path; + } + else if(info_temp->interface_number == dev_led_iface && info_temp->usage_page == dev_led_page) + { + dev_led = hid_open_path(info_temp->path); + dev_led_path = info_temp->path; + } + } + if(dev_ctrl && dev_led) + { + break; + } + info_temp = info_temp->next; + } + + hid_free_enumeration(info_full); + + if(dev_ctrl && dev_led) + { + RoccatVulcanKeyboardController * controller = new RoccatVulcanKeyboardController(dev_ctrl, dev_led, info->path, info->product_id, name); + RGBController_RoccatVulcanKeyboard * rgb_controller = new RGBController_RoccatVulcanKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + + used_paths.insert(dev_ctrl_path); + used_paths.insert(dev_led_path); + } + else + { + // Not all of them could be opened, do some cleanup + hid_close(dev_ctrl); + hid_close(dev_led); + } +} + +void DetectRoccatHordeAimoKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatHordeAimoController * controller = new RoccatHordeAimoController(dev, *info, name); + RGBController_RoccatHordeAimo * rgb_controller = new RGBController_RoccatHordeAimo(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectRoccatBurstCoreControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatBurstController * controller = new RoccatBurstController(dev, *info, name); + RGBController_RoccatBurst * rgb_controller = new RGBController_RoccatBurst(controller, ROCCAT_BURST_CORE_NUMBER_OF_LEDS); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectRoccatBurstProControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatBurstController * controller = new RoccatBurstController(dev, *info, name); + RGBController_RoccatBurst * rgb_controller = new RGBController_RoccatBurst(controller, ROCCAT_BURST_PRO_NUMBER_OF_LEDS); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectRoccatBurstProAirControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatBurstProAirController * controller = new RoccatBurstProAirController(dev, *info, name); + RGBController_RoccatBurstProAir * rgb_controller = new RGBController_RoccatBurstProAir(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectRoccatKoneProControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatKoneProController * controller = new RoccatKoneProController(dev, *info, name); + RGBController_RoccatKonePro * rgb_controller = new RGBController_RoccatKonePro(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectRoccatKoneProAirControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatKoneProAirController * controller = new RoccatKoneProAirController(dev, *info, name); + RGBController_RoccatKoneProAir * rgb_controller = new RGBController_RoccatKoneProAir(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectRoccatKoneXPControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatKoneXPController * controller = new RoccatKoneXPController(dev, info->path, name); + RGBController_RoccatKoneXP * rgb_controller = new RGBController_RoccatKoneXP(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectRoccatKovaControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatKovaController * controller = new RoccatKovaController(dev, info->path, name); + RGBController_RoccatKova * rgb_controller = new RGBController_RoccatKova(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectRoccatEloControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatEloController * controller = new RoccatEloController(dev, *info, name); + RGBController_RoccatElo * rgb_controller = new RGBController_RoccatElo(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectRoccatSenseAimoControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + RoccatSenseAimoController * controller = new RoccatSenseAimoController(dev, info->path, name); + RGBController_RoccatSenseAimo * rgb_controller = new RGBController_RoccatSenseAimo(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_PRE_DETECTION_HOOK(ResetRoccatVulcanKeyboardControllersPaths); + +/*-----------------------------------------------------------------*\ +| KEYBOARDS | +\*-----------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Roccat Horde Aimo", DetectRoccatHordeAimoKeyboardControllers, ROCCAT_VID, ROCCAT_HORDE_AIMO_PID, 1, 0x0B, 0 ); + +REGISTER_HID_DETECTOR_IP ("Roccat Magma", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_MAGMA_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP ("Roccat Magma Mini", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_MAGMA_MINI_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP ("Roccat Pyro", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_PYRO_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP ("Roccat Vulcan 100 Aimo", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_100_AIMO_PID, 1, 11); +REGISTER_HID_DETECTOR_IP ("Roccat Vulcan 120-Series Aimo", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_120_AIMO_PID, 1, 11); +REGISTER_HID_DETECTOR_IP ("Roccat Vulcan TKL", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_TKL_PID, 1, 11); +REGISTER_HID_DETECTOR_IP ("Roccat Vulcan Pro", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_PRO_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP ("Roccat Vulcan TKL Pro", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_TKL_PRO_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP ("Roccat Vulcan II", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_II_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP ("Roccat Vulcan II Max", DetectRoccatVulcanKeyboardControllers, ROCCAT_VID, ROCCAT_VULCAN_II_MAX_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP ("Turtle Beach Vulcan II", DetectRoccatVulcanKeyboardControllers, TURTLE_BEACH_VID, TURTLE_BEACH_VULCAN_II_PID, 1, 0xFF01); +REGISTER_HID_DETECTOR_IP ("Turtle Beach Vulcan II TKL", DetectRoccatVulcanKeyboardControllers, TURTLE_BEACH_VID, TURTLE_BEACH_VULCAN_II_TKL_PID, 1, 11); +REGISTER_HID_DETECTOR_IP ("Turtle Beach Vulcan II TKL Pro", DetectRoccatVulcanKeyboardControllers, TURTLE_BEACH_VID, TURTLE_BEACH_VULCAN_II_TKL_PRO_PID, 1, 0x0001); + +/*-----------------------------------------------------------------*\ +| MICE | +\*-----------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Roccat Burst Core", DetectRoccatBurstCoreControllers, ROCCAT_VID, ROCCAT_BURST_CORE_PID, 3, 0xFF01, 1 ); +REGISTER_HID_DETECTOR_IPU("Roccat Burst Pro", DetectRoccatBurstProControllers, ROCCAT_VID, ROCCAT_BURST_PRO_PID, 3, 0xFF01, 1 ); +REGISTER_HID_DETECTOR_IPU("Roccat Burst Pro Air", DetectRoccatBurstProAirControllers, ROCCAT_VID, ROCCAT_BURST_PRO_AIR_PID, 0, 0x01, 2 ); + +REGISTER_HID_DETECTOR_IPU("Roccat Kone Aimo", DetectRoccatMouseControllers, ROCCAT_VID, ROCCAT_KONE_AIMO_PID, 0, 0x0B, 0 ); +REGISTER_HID_DETECTOR_IPU("Roccat Kone Aimo 16K", DetectRoccatMouseControllers, ROCCAT_VID, ROCCAT_KONE_AIMO_16K_PID, 0, 0x0B, 0 ); + +REGISTER_HID_DETECTOR_IPU("Roccat Kone Pro", DetectRoccatKoneProControllers, ROCCAT_VID, ROCCAT_KONE_PRO_PID, 3, 0xFF01, 1 ); +REGISTER_HID_DETECTOR_IPU("Roccat Kone Pro Air", DetectRoccatKoneProAirControllers, ROCCAT_VID, ROCCAT_KONE_PRO_AIR_PID, 2, 0xFF00, 1 ); +REGISTER_HID_DETECTOR_IPU("Roccat Kone Pro Air (Wired)", DetectRoccatKoneProAirControllers, ROCCAT_VID, ROCCAT_KONE_PRO_AIR_WIRED_PID, 1, 0xFF13, 1 ); + +REGISTER_HID_DETECTOR_IPU("Roccat Kone XP", DetectRoccatKoneXPControllers, ROCCAT_VID, ROCCAT_KONE_XP_PID, 3, 0xFF01, 1 ); + +REGISTER_HID_DETECTOR_IPU("Roccat Kova", DetectRoccatKovaControllers, ROCCAT_VID, ROCCAT_KOVA_PID, 0, 0x0B, 0 ); + +/*-----------------------------------------------------------------*\ +| MOUSEMATS | +\*-----------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Roccat Sense Aimo Mid", DetectRoccatSenseAimoControllers, ROCCAT_VID, ROCCAT_SENSE_AIMO_MID_PID, 0, 0xFF01, 1 ); +REGISTER_HID_DETECTOR_IPU("Roccat Sense Aimo XXL", DetectRoccatSenseAimoControllers, ROCCAT_VID, ROCCAT_SENSE_AIMO_XXL_PID, 0, 0xFF01, 1 ); + +/*-----------------------------------------------------------------*\ +| HEADSETS | +\*-----------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Roccat Elo 7.1", DetectRoccatEloControllers, ROCCAT_VID, ROCCAT_ELO_PID, 3, 0x0C, 1 ); diff --git a/Controllers/RoccatController/RoccatEloController/RGBController_RoccatElo.cpp b/Controllers/RoccatController/RoccatEloController/RGBController_RoccatElo.cpp new file mode 100644 index 0000000..959683e --- /dev/null +++ b/Controllers/RoccatController/RoccatEloController/RGBController_RoccatElo.cpp @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatElo.cpp | +| | +| RGBController for Roccat Elo | +| | +| Flora Aubry 02 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_RoccatElo.h" + +/**------------------------------------------------------------------*\ + @name Roccat Elo 7.1 + @category Headset + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectRoccatEloControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RoccatElo::RGBController_RoccatElo(RoccatEloController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Roccat"; + type = DEVICE_TYPE_HEADSET; + description = "Roccat Elo 7.1 Headset Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_RoccatElo::~RGBController_RoccatElo() +{ + delete controller; +} + +void RGBController_RoccatElo::SetupZones() +{ + zone new_zone; + + new_zone.name = "Headset"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = ROCCAT_ELO_LEDS_COUNT; + new_zone.leds_max = ROCCAT_ELO_LEDS_COUNT; + new_zone.leds_count = ROCCAT_ELO_LEDS_COUNT; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(new_zone.leds_count); + + SetupColors(); +} + +void RGBController_RoccatElo::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatElo::DeviceUpdateLEDs() +{ + controller->SendDirect(colors[0]); +} + +void RGBController_RoccatElo::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatElo::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatElo::DeviceUpdateMode() +{ + +} diff --git a/Controllers/RoccatController/RoccatEloController/RGBController_RoccatElo.h b/Controllers/RoccatController/RoccatEloController/RGBController_RoccatElo.h new file mode 100644 index 0000000..039e35a --- /dev/null +++ b/Controllers/RoccatController/RoccatEloController/RGBController_RoccatElo.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatElo.h | +| | +| RGBController for Roccat Elo | +| | +| Flora Aubry 02 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatEloController.h" + +class RGBController_RoccatElo : public RGBController +{ +public: + RGBController_RoccatElo(RoccatEloController* controller_ptr); + ~RGBController_RoccatElo(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatEloController* controller; +}; diff --git a/Controllers/RoccatController/RoccatEloController/RoccatEloController.cpp b/Controllers/RoccatController/RoccatEloController/RoccatEloController.cpp new file mode 100644 index 0000000..1765f4f --- /dev/null +++ b/Controllers/RoccatController/RoccatEloController/RoccatEloController.cpp @@ -0,0 +1,98 @@ +/*---------------------------------------------------------*\ +| RoccatEloController.cpp | +| | +| Driver for Roccat Elo | +| | +| Flora Aubry 02 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RoccatEloController.h" +#include "StringUtils.h" + +RoccatEloController::RoccatEloController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + + SendInit(); +} + +RoccatEloController::~RoccatEloController() +{ + hid_close(dev); +} + +std::string RoccatEloController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RoccatEloController::GetNameString() +{ + return(name); +} + +std::string RoccatEloController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void RoccatEloController::SendInit() +{ + unsigned char usb_buf[ROCCAT_ELO_REPORT_SIZE]; + + memset(usb_buf, 0x00, ROCCAT_ELO_REPORT_SIZE); + + usb_buf[0x00] = ROCCAT_ELO_REPORT_ID; + usb_buf[0x01] = 0x01; + + hid_write(dev, usb_buf, ROCCAT_ELO_REPORT_SIZE); + + usb_buf[0x01] = 0x02; + + hid_write(dev, usb_buf, ROCCAT_ELO_REPORT_SIZE); + + usb_buf[0x01] = 0x03; + usb_buf[0x03] = 0x01; + + hid_write(dev, usb_buf, ROCCAT_ELO_REPORT_SIZE); + + SendDirect(0); + + memset(usb_buf, 0x00, ROCCAT_ELO_REPORT_SIZE); + + usb_buf[0x00] = ROCCAT_ELO_REPORT_ID; + usb_buf[0x01] = 0x01; + + hid_write(dev, usb_buf, ROCCAT_ELO_REPORT_SIZE); +} + +void RoccatEloController::SendDirect(RGBColor color) +{ + unsigned char usb_buf[ROCCAT_ELO_REPORT_SIZE]; + + memset(usb_buf, 0x00, ROCCAT_ELO_REPORT_SIZE); + + usb_buf[0x00] = ROCCAT_ELO_REPORT_ID; + usb_buf[0x01] = 0x04; + usb_buf[0x04] = RGBGetRValue(color); + usb_buf[0x05] = RGBGetGValue(color); + usb_buf[0x06] = RGBGetBValue(color); + + hid_write(dev, usb_buf, ROCCAT_ELO_REPORT_SIZE); +} + diff --git a/Controllers/RoccatController/RoccatEloController/RoccatEloController.h b/Controllers/RoccatController/RoccatEloController/RoccatEloController.h new file mode 100644 index 0000000..7c52c5b --- /dev/null +++ b/Controllers/RoccatController/RoccatEloController/RoccatEloController.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| RoccatEloController.h | +| | +| Driver for Roccat Elo | +| | +| Flora Aubry 02 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define ROCCAT_ELO_REPORT_SIZE 16 +#define ROCCAT_ELO_LEDS_COUNT 1 +#define ROCCAT_ELO_REPORT_ID 0xFF + +class RoccatEloController +{ +public: + RoccatEloController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~RoccatEloController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendDirect(RGBColor color); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendInit(); +}; diff --git a/Controllers/RoccatController/RoccatHordeAimoController/RGBController_RoccatHordeAimo.cpp b/Controllers/RoccatController/RoccatHordeAimoController/RGBController_RoccatHordeAimo.cpp new file mode 100644 index 0000000..b281761 --- /dev/null +++ b/Controllers/RoccatController/RoccatHordeAimoController/RGBController_RoccatHordeAimo.cpp @@ -0,0 +1,97 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatHordeAimo.cpp | +| | +| RGBController for Roccat Horde Aimo | +| | +| Morgan Guimard (morg) 24 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RoccatHordeAimo.h" + +/**------------------------------------------------------------------*\ + @name Roccat Horde Aimo + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectRoccatHordeAimoKeyboardControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RoccatHordeAimo::RGBController_RoccatHordeAimo(RoccatHordeAimoController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Roccat"; + type = DEVICE_TYPE_KEYBOARD; + description = "Roccat Horde Aimo Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_RoccatHordeAimo::~RGBController_RoccatHordeAimo() +{ + delete controller; +} + +void RGBController_RoccatHordeAimo::SetupZones() +{ + zone new_zone; + new_zone.name = "Keyboard"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = NUMBER_OF_LEDS; + new_zone.leds_max = NUMBER_OF_LEDS; + new_zone.leds_count = NUMBER_OF_LEDS; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + for(unsigned int i = 0; i < NUMBER_OF_LEDS; i++) + { + led new_led; + new_led.name = "LED " + std::to_string(i + 1); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_RoccatHordeAimo::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatHordeAimo::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); +} + +void RGBController_RoccatHordeAimo::UpdateZoneLEDs(int /*zone_idx*/) +{ + controller->SetColors(colors); +} + +void RGBController_RoccatHordeAimo::UpdateSingleLED(int /*led_idx*/) +{ + UpdateZoneLEDs(0); +} + +void RGBController_RoccatHordeAimo::DeviceUpdateMode() +{ + +} diff --git a/Controllers/RoccatController/RoccatHordeAimoController/RGBController_RoccatHordeAimo.h b/Controllers/RoccatController/RoccatHordeAimoController/RGBController_RoccatHordeAimo.h new file mode 100644 index 0000000..251bcc1 --- /dev/null +++ b/Controllers/RoccatController/RoccatHordeAimoController/RGBController_RoccatHordeAimo.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatHordeAimo.h | +| | +| RGBController for Roccat Horde Aimo | +| | +| Morgan Guimard (morg) 24 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatHordeAimoController.h" + +class RGBController_RoccatHordeAimo : public RGBController +{ +public: + RGBController_RoccatHordeAimo(RoccatHordeAimoController* controller_ptr); + ~RGBController_RoccatHordeAimo(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatHordeAimoController* controller; +}; diff --git a/Controllers/RoccatController/RoccatHordeAimoController/RoccatHordeAimoController.cpp b/Controllers/RoccatController/RoccatHordeAimoController/RoccatHordeAimoController.cpp new file mode 100644 index 0000000..79058d8 --- /dev/null +++ b/Controllers/RoccatController/RoccatHordeAimoController/RoccatHordeAimoController.cpp @@ -0,0 +1,96 @@ +/*---------------------------------------------------------*\ +| RoccatHordeAimoController.cpp | +| | +| Driver for Roccat Horde Aimo | +| | +| Morgan Guimard (morg) 24 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RoccatHordeAimoController.h" +#include "StringUtils.h" + +RoccatHordeAimoController::RoccatHordeAimoController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + + InitialPacket(); +} + +RoccatHordeAimoController::~RoccatHordeAimoController() +{ + hid_close(dev); +} + +void RoccatHordeAimoController::InitialPacket() +{ + unsigned char usb_buf[8]; + + memset(usb_buf, 0x00,8); + + usb_buf[0x00] = 0x13; + usb_buf[0x01] = 0x08; + usb_buf[0x02] = 0x01; + + hid_send_feature_report(dev, usb_buf, 8); +} + +std::string RoccatHordeAimoController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RoccatHordeAimoController::GetNameString() +{ + return(name); +} + +std::string RoccatHordeAimoController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void RoccatHordeAimoController::SetColors(std::vector colors) +{ + unsigned char usb_buf[WRITE_PACKET_LENGTH]; + + usb_buf[0x00] = REPORT_ID; + usb_buf[0x01] = WRITE_PACKET_LENGTH; + usb_buf[0x02] = 0xFF; + usb_buf[0x03] = 0xFF; + + for(unsigned int i = 0; i < 6; i++) + { + usb_buf[0x04 + (i * 3)] = RGBGetRValue(colors[i]); + usb_buf[0x05 + (i * 3)] = RGBGetGValue(colors[i]); + usb_buf[0x06 + (i * 3)] = RGBGetBValue(colors[i]); + } + + int crc = 0; + + for(unsigned int i = 0; i < WRITE_PACKET_LENGTH - 2; i++) + { + crc += usb_buf[i]; + } + + usb_buf[22] = crc; + usb_buf[23] = crc >> 8; + + hid_send_feature_report(dev, usb_buf, WRITE_PACKET_LENGTH); + + unsigned char usb_read_buf[READ_PACKET_LENGTH]; + hid_get_feature_report(dev, usb_read_buf, READ_PACKET_LENGTH); +} diff --git a/Controllers/RoccatController/RoccatHordeAimoController/RoccatHordeAimoController.h b/Controllers/RoccatController/RoccatHordeAimoController/RoccatHordeAimoController.h new file mode 100644 index 0000000..4e1e17c --- /dev/null +++ b/Controllers/RoccatController/RoccatHordeAimoController/RoccatHordeAimoController.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| RoccatHordeAimoController.h | +| | +| Driver for Roccat Horde Aimo | +| | +| Morgan Guimard (morg) 24 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +#define WRITE_PACKET_LENGTH 24 +#define READ_PACKET_LENGTH 3 +#define REPORT_ID 0x18 +#define NUMBER_OF_LEDS 6 + +class RoccatHordeAimoController +{ +public: + RoccatHordeAimoController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~RoccatHordeAimoController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetColors(std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; + + void InitialPacket(); +}; diff --git a/Controllers/RoccatController/RoccatKoneAimoController/RGBController_RoccatKoneAimo.cpp b/Controllers/RoccatController/RoccatKoneAimoController/RGBController_RoccatKoneAimo.cpp new file mode 100644 index 0000000..b46b044 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneAimoController/RGBController_RoccatKoneAimo.cpp @@ -0,0 +1,217 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKoneAimo.cpp | +| | +| RGBController for Roccat Kone Aimo | +| | +| Thibaud M (enlight3d) 17 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RoccatKoneAimo.h" + +/**------------------------------------------------------------------*\ + @name Roccat Kone Aimo + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRoccatMouseControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RoccatKoneAimo::RGBController_RoccatKoneAimo(RoccatKoneAimoController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Roccat"; + type = DEVICE_TYPE_MOUSE; + description = "Roccat Kone Aimo Mouse Device"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.speed_min = 0; + Direct.speed_max = 0; + Direct.speed = 0; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + active_mode = 0; + + SetupZones(); +} + +RGBController_RoccatKoneAimo::~RGBController_RoccatKoneAimo() +{ + delete controller; +} + +void RGBController_RoccatKoneAimo::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones and leds per zone | + \*---------------------------------------------------------*/ + zone WHEEL_zone; + WHEEL_zone.name = "Scroll Wheel"; + WHEEL_zone.type = ZONE_TYPE_SINGLE; + WHEEL_zone.leds_min = 1; + WHEEL_zone.leds_max = 1; + WHEEL_zone.leds_count = 1; + WHEEL_zone.matrix_map = NULL; + zones.push_back(WHEEL_zone); + zones_channel.push_back(SCROLL_WHEEL); + + led WHEEL_led; + WHEEL_led.name = "Wheel LED"; + WHEEL_led.value = (unsigned int)zones.size(); + leds.push_back(WHEEL_led); + leds_channel.push_back(SCROLL_WHEEL); + + zone STRIP_LEFT_zone; + STRIP_LEFT_zone.name = "Strip left"; + STRIP_LEFT_zone.type = ZONE_TYPE_LINEAR; + STRIP_LEFT_zone.leds_min = 4; + STRIP_LEFT_zone.leds_max = 4; + STRIP_LEFT_zone.leds_count = 4; + STRIP_LEFT_zone.matrix_map = NULL; + zones.push_back(STRIP_LEFT_zone); + zones_channel.push_back(STRIP_LEFT); + + for(std::size_t led_idx = 0; led_idx < STRIP_LEFT_zone.leds_max; led_idx++) + { + led STRIP_LEFT_led; + STRIP_LEFT_led.name = "Strip left LED " + std::to_string(led_idx + 1); + STRIP_LEFT_led.value = (unsigned int)zones.size(); + leds.push_back(STRIP_LEFT_led); + leds_channel.push_back(STRIP_LEFT); + } + + zone STRIP_RIGHT_zone; + STRIP_RIGHT_zone.name = "Strip right"; + STRIP_RIGHT_zone.type = ZONE_TYPE_LINEAR; + STRIP_RIGHT_zone.leds_min = 4; + STRIP_RIGHT_zone.leds_max = 4; + STRIP_RIGHT_zone.leds_count = 4; + STRIP_RIGHT_zone.matrix_map = NULL; + zones.push_back(STRIP_RIGHT_zone); + zones_channel.push_back(STRIP_RIGHT); + + for(std::size_t led_idx = 0; led_idx < STRIP_RIGHT_zone.leds_max; led_idx++) + { + led STRIP_RIGHT_led; + STRIP_RIGHT_led.name = "Strip right LED " + std::to_string(led_idx + 1); + STRIP_RIGHT_led.value = (unsigned int)zones.size(); + leds.push_back(STRIP_RIGHT_led); + leds_channel.push_back(STRIP_RIGHT); + } + + zone LOWER_LEFT_zone; + LOWER_LEFT_zone.name = "Lower left"; + LOWER_LEFT_zone.type = ZONE_TYPE_SINGLE; + LOWER_LEFT_zone.leds_min = 1; + LOWER_LEFT_zone.leds_max = 1; + LOWER_LEFT_zone.leds_count = 1; + LOWER_LEFT_zone.matrix_map = NULL; + zones.push_back(LOWER_LEFT_zone); + zones_channel.push_back(LOWER_LEFT); + + led LOWER_LEFT_led; + LOWER_LEFT_led.name = "Lower left LED"; + LOWER_LEFT_led.value = (unsigned int)zones.size(); + leds.push_back(LOWER_LEFT_led); + leds_channel.push_back(LOWER_LEFT); + + zone LOWER_RIGHT_zone; + LOWER_RIGHT_zone.name = "Lower right"; + LOWER_RIGHT_zone.type = ZONE_TYPE_SINGLE; + LOWER_RIGHT_zone.leds_min = 1; + LOWER_RIGHT_zone.leds_max = 1; + LOWER_RIGHT_zone.leds_count = 1; + LOWER_RIGHT_zone.matrix_map = NULL; + zones.push_back(LOWER_RIGHT_zone); + zones_channel.push_back(LOWER_RIGHT); + + led LOWER_RIGHT_led; + LOWER_RIGHT_led.name = "Lower right LED"; + LOWER_RIGHT_led.value = (unsigned int)zones.size(); + leds.push_back(LOWER_RIGHT_led); + leds_channel.push_back(LOWER_RIGHT); + + SetupColors(); + + /*---------------------------------------------------------*\ + | Initialize colors for each LED | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned char red = 0x00; + unsigned char grn = 0x00; + unsigned char blu = 0x00; + + colors[led_idx] = ToRGBColor(red, grn, blu); + } +} + +void RGBController_RoccatKoneAimo::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatKoneAimo::DeviceUpdateLEDs() +{ + /*---------------------------------------------------------*\ + | Set colors for all channel/leds | + \*---------------------------------------------------------*/ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetChannelColors(zones_channel[zone_idx], zones[zone_idx].colors, zones[zone_idx].leds_count); + } + /*---------------------------------------------------------*\ + | Apply new colors to the mouse | + \*---------------------------------------------------------*/ + controller->SendUpdate(); +} + +void RGBController_RoccatKoneAimo::UpdateZoneLEDs(int zone_idx) +{ + /*---------------------------------------------------------*\ + | Set colors for one channel of leds | + \*---------------------------------------------------------*/ + controller->SetChannelColors(zones_channel[zone_idx], zones[zone_idx].colors, zones[zone_idx].leds_count); + /*---------------------------------------------------------*\ + | Apply new colors to the mouse | + \*---------------------------------------------------------*/ + controller->SendUpdate(); +} + +void RGBController_RoccatKoneAimo::UpdateSingleLED(int led_idx) +{ + /*---------------------------------------------------------*\ + | Get channel corresponding to led | + \*---------------------------------------------------------*/ + ROCCAT_KONE_AIMO_CHANNEL channel = leds_channel[led_idx]; + /*---------------------------------------------------------*\ + | Update channel corresponding to led | + \*---------------------------------------------------------*/ + controller->SetChannelColors(channel, zones[leds[led_idx].value].colors, zones[leds[led_idx].value].leds_count); + /*---------------------------------------------------------*\ + | Apply new colors to the mouse | + \*---------------------------------------------------------*/ + controller->SendUpdate(); +} + +void RGBController_RoccatKoneAimo::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | This device does not support changing mode | + \*---------------------------------------------------------*/ +} diff --git a/Controllers/RoccatController/RoccatKoneAimoController/RGBController_RoccatKoneAimo.h b/Controllers/RoccatController/RoccatKoneAimoController/RGBController_RoccatKoneAimo.h new file mode 100644 index 0000000..33e0ec4 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneAimoController/RGBController_RoccatKoneAimo.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKoneAimo.h | +| | +| RGBController for Roccat Kone Aimo | +| | +| Thibaud M (enlight3d) 17 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatKoneAimoController.h" + +class RGBController_RoccatKoneAimo : public RGBController +{ +public: + RGBController_RoccatKoneAimo(RoccatKoneAimoController* controller_ptr); + ~RGBController_RoccatKoneAimo(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatKoneAimoController* controller; + std::vector zones_channel; + std::vector leds_channel; +}; diff --git a/Controllers/RoccatController/RoccatKoneAimoController/RoccatKoneAimoController.cpp b/Controllers/RoccatController/RoccatKoneAimoController/RoccatKoneAimoController.cpp new file mode 100644 index 0000000..c7bb8ee --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneAimoController/RoccatKoneAimoController.cpp @@ -0,0 +1,112 @@ +/*---------------------------------------------------------*\ +| RoccatKoneAimoController.cpp | +| | +| Driver for Roccat Kone Aimo | +| | +| Thibaud M (enlight3d) 17 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RoccatKoneAimoController.h" +#include "StringUtils.h" + +RoccatKoneAimoController::RoccatKoneAimoController(hid_device* dev_handle, char *_path, std::string dev_name) +{ + dev = dev_handle; + location = _path; + name = dev_name; + + /*-----------------------------------------------------*\ + | Init usb buffer to 0 and add first two bytes | + \*-----------------------------------------------------*/ + memset(usb_colors_buf, 0x00, USB_COLOR_BUFF_LEN); + usb_colors_buf[0x00] = 0x0D; + usb_colors_buf[0x01] = 0x2E; + + SendInit(); +} + +RoccatKoneAimoController::~RoccatKoneAimoController() +{ + hid_close(dev); +} + +std::string RoccatKoneAimoController::GetName() +{ + return(name); +} + +std::string RoccatKoneAimoController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string RoccatKoneAimoController::GetLocation() +{ + return("HID: " + location); +} + +void RoccatKoneAimoController::SendInit() +{ + unsigned char usb_buf[6] = {00}; + + /*-----------------------------------------------------*\ + | Read first a packet from mouse (swarm does it) | + \*-----------------------------------------------------*/ + hid_get_feature_report(dev, usb_buf, 3); + + /*-----------------------------------------------------*\ + | Set up Init packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x0E; + usb_buf[0x01] = 0x06; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = 0xFF; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, 6); +} + +void RoccatKoneAimoController::SetChannelColors(ROCCAT_KONE_AIMO_CHANNEL channel, RGBColor * colors, unsigned int num_colors) +{ + /*---------------------------------------------------------*\ + | Receiving update request for only one channel | + | and updating usb buffer to match colors | + \*---------------------------------------------------------*/ + for(unsigned char i = 0; i < num_colors; i++) + { + std::size_t color = channel + i; + int usb_idx = (int)(0x02 + (color * 4)); + + usb_colors_buf[usb_idx + R_OFFSET] = RGBGetRValue(colors[i]); + usb_colors_buf[usb_idx + G_OFFSET] = RGBGetGValue(colors[i]); + usb_colors_buf[usb_idx + B_OFFSET] = RGBGetBValue(colors[i]); + } +} + +void RoccatKoneAimoController::SendUpdate() +{ + /*-----------------------------------------------------*\ + | Send packet (whole buffer needs to be sent everytime) | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, usb_colors_buf, 46); + /*-----------------------------------------------------*\ + | Read a packet from mouse (swarm does it) | + \*-----------------------------------------------------*/ + hid_get_feature_report(dev, usb_colors_buf, 3); +} diff --git a/Controllers/RoccatController/RoccatKoneAimoController/RoccatKoneAimoController.h b/Controllers/RoccatController/RoccatKoneAimoController/RoccatKoneAimoController.h new file mode 100644 index 0000000..ec40dda --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneAimoController/RoccatKoneAimoController.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| RoccatKoneAimoController.h | +| | +| Driver for Roccat Kone Aimo | +| | +| Thibaud M (enlight3d) 17 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include "RGBController.h" + +#define HID_MAX_STR 255 +#define NUM_LEDS 11 + +#define R_OFFSET 0 +#define G_OFFSET 1 +#define B_OFFSET 2 + +#define USB_COLOR_BUFF_LEN 46 + +enum ROCCAT_KONE_AIMO_CHANNEL +{ + SCROLL_WHEEL = 0, + STRIP_LEFT = 1, + STRIP_RIGHT = 5, + LOWER_LEFT = 9, + LOWER_RIGHT = 10 +}; + +class RoccatKoneAimoController +{ +public: + RoccatKoneAimoController(hid_device* dev_handle, char *_path, std::string dev_name); + ~RoccatKoneAimoController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + + void SetChannelColors(ROCCAT_KONE_AIMO_CHANNEL channel, RGBColor * colors, unsigned int num_colors); + void SendUpdate(); + +private: + std::string location; + std::string name; + hid_device* dev; + unsigned char usb_colors_buf[USB_COLOR_BUFF_LEN]; // USB buffer to be sent everytime we update mouse's LEDs + + void SendInit(); +}; diff --git a/Controllers/RoccatController/RoccatKoneProAirController/RGBController_RoccatKoneProAir.cpp b/Controllers/RoccatController/RoccatKoneProAirController/RGBController_RoccatKoneProAir.cpp new file mode 100644 index 0000000..f24c15b --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneProAirController/RGBController_RoccatKoneProAir.cpp @@ -0,0 +1,193 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKoneProAir.cpp | +| | +| RGBController for Roccat Kone Pro Air | +| | +| Plunti 10 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RoccatKoneProAir.h" + +/**------------------------------------------------------------------*\ + @name Roccat Kone Pro Air Mouse + @category Mouse + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRoccatKoneProAirControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RoccatKoneProAir::RGBController_RoccatKoneProAir(RoccatKoneProAirController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Roccat"; + type = DEVICE_TYPE_MOUSE; + description = "Roccat Kone Pro Air Mouse Device"; + serial = controller->GetSerialString(); + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ROCCAT_KONE_PRO_AIR_DIRECT_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = ROCCAT_KONE_PRO_AIR_OFF_MODE_VALUE; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = ROCCAT_KONE_PRO_AIR_STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; + Static.brightness_min = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MIN; + Static.brightness_max = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = ROCCAT_KONE_PRO_AIR_RAINBOW_WAVE_MODE_VALUE; + RainbowWave.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.brightness = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; + RainbowWave.brightness_min = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MIN; + RainbowWave.brightness_max = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; + RainbowWave.speed = ROCCAT_KONE_PRO_AIR_SPEED_MID; + RainbowWave.speed_min = ROCCAT_KONE_PRO_AIR_SPEED_MIN; + RainbowWave.speed_max = ROCCAT_KONE_PRO_AIR_SPEED_MAX; + modes.push_back(RainbowWave); + + mode Heartbeat; + Heartbeat.name = "Heartbeat"; + Heartbeat.value = ROCCAT_KONE_PRO_AIR_HEARTBEAT_MODE_VALUE; + Heartbeat.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Heartbeat.color_mode = MODE_COLORS_PER_LED; + Heartbeat.brightness = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; + Heartbeat.brightness_min = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MIN; + Heartbeat.brightness_max = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; + Heartbeat.speed = ROCCAT_KONE_PRO_AIR_SPEED_MID; + Heartbeat.speed_min = ROCCAT_KONE_PRO_AIR_SPEED_MIN; + Heartbeat.speed_max = ROCCAT_KONE_PRO_AIR_SPEED_MAX; + modes.push_back(Heartbeat); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ROCCAT_KONE_PRO_AIR_BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; + Breathing.brightness_min = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MIN; + Breathing.brightness_max = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; + Breathing.speed = ROCCAT_KONE_PRO_AIR_SPEED_MID; + Breathing.speed_min = ROCCAT_KONE_PRO_AIR_SPEED_MIN; + Breathing.speed_max = ROCCAT_KONE_PRO_AIR_SPEED_MAX; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = ROCCAT_KONE_PRO_AIR_FLASHING_MODE_VALUE; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Flashing.color_mode = MODE_COLORS_PER_LED; + Flashing.brightness = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; + Flashing.brightness_min = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MIN; + Flashing.brightness_max = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; + Flashing.speed = ROCCAT_KONE_PRO_AIR_SPEED_MID; + Flashing.speed_min = ROCCAT_KONE_PRO_AIR_SPEED_MIN; + Flashing.speed_max = ROCCAT_KONE_PRO_AIR_SPEED_MAX; + modes.push_back(Flashing); + + mode Battery; + Battery.name = "Battery"; + Battery.value = ROCCAT_KONE_PRO_AIR_BATTERY_MODE_VALUE; + Battery.flags = MODE_FLAG_AUTOMATIC_SAVE; + Battery.color_mode = MODE_COLORS_NONE; + modes.push_back(Battery); + + SetupZones(); +} + +RGBController_RoccatKoneProAir::~RGBController_RoccatKoneProAir() +{ + delete controller; +} + +void RGBController_RoccatKoneProAir::SetupZones() +{ + zone new_zone; + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = ROCCAT_KONE_PRO_AIR_LED_COUNT; + new_zone.leds_max = ROCCAT_KONE_PRO_AIR_LED_COUNT; + new_zone.leds_count = ROCCAT_KONE_PRO_AIR_LED_COUNT; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + std::string led_names[2] = + { + "Left Button", + "Right Button" + }; + + for(unsigned int i = 0; i < ROCCAT_KONE_PRO_AIR_LED_COUNT; i++) + { + led new_led; + new_led.name = led_names[i]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_RoccatKoneProAir::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatKoneProAir::DeviceUpdateLEDs() +{ + const mode& active = modes[active_mode]; + + if(active.value == ROCCAT_KONE_PRO_AIR_DIRECT_MODE_VALUE) + { + controller->SendDirect(colors); + } + else + { + controller->SetMode(colors, active.value, active.speed, active.brightness, active.flags); + } +} + +void RGBController_RoccatKoneProAir::UpdateZoneLEDs(int /*zone_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatKoneProAir::UpdateSingleLED(int /*led_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatKoneProAir::DeviceUpdateMode() +{ + const mode& active = modes[active_mode]; + + if(!(active.flags & MODE_FLAG_HAS_PER_LED_COLOR)) + { + DeviceUpdateLEDs(); + } +} diff --git a/Controllers/RoccatController/RoccatKoneProAirController/RGBController_RoccatKoneProAir.h b/Controllers/RoccatController/RoccatKoneProAirController/RGBController_RoccatKoneProAir.h new file mode 100644 index 0000000..cfa80d4 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneProAirController/RGBController_RoccatKoneProAir.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKoneProAir.h | +| | +| RGBController for Roccat Kone Pro Air | +| | +| Plunti 10 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatKoneProAirController.h" + +class RGBController_RoccatKoneProAir : public RGBController +{ +public: + RGBController_RoccatKoneProAir(RoccatKoneProAirController* controller_ptr); + ~RGBController_RoccatKoneProAir(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatKoneProAirController* controller; +}; diff --git a/Controllers/RoccatController/RoccatKoneProAirController/RoccatKoneProAirController.cpp b/Controllers/RoccatController/RoccatKoneProAirController/RoccatKoneProAirController.cpp new file mode 100644 index 0000000..554492f --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneProAirController/RoccatKoneProAirController.cpp @@ -0,0 +1,119 @@ +/*---------------------------------------------------------*\ +| RoccatKoneProAirController.cpp | +| | +| Driver for Roccat Kone Pro Air | +| | +| Plunti 10 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RoccatKoneProAirController.h" +#include "StringUtils.h" + +RoccatKoneProAirController::RoccatKoneProAirController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +RoccatKoneProAirController::~RoccatKoneProAirController() +{ + hid_close(dev); +} + +std::string RoccatKoneProAirController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RoccatKoneProAirController::GetNameString() +{ + return(name); +} + +std::string RoccatKoneProAirController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void RoccatKoneProAirController::SendDirect(std::vector colors) +{ + SendRGB(true, colors, ROCCAT_KONE_PRO_AIR_DIRECT_MODE_VALUE, ROCCAT_KONE_PRO_AIR_SPEED_MAX, ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX); +} + +void RoccatKoneProAirController::SetMode(std::vector colors, unsigned char mode_value, unsigned char speed, unsigned char brightness, unsigned int mode_flags) +{ + /*---------------------------------------------------------*\ + | 1. Read settings | + \*---------------------------------------------------------*/ + unsigned char active_settings[ROCCAT_KONE_PRO_AIR_SETTINGS_READ_PACKET_LENGTH]; + + unsigned char settings_request[] = {0x00, 0x90, 0x00, 0x04, 0x00, 0x18, 0x25, 0x51, 0x32}; + hid_write(dev, settings_request, sizeof(settings_request)); + + do + { + hid_read(dev, active_settings, ROCCAT_KONE_PRO_AIR_SETTINGS_READ_PACKET_LENGTH); + } while( (active_settings[0] != 0x90) || (active_settings[2] != 0x26) ); + + /*---------------------------------------------------------*\ + | 2. Send settings and select profile | + \*---------------------------------------------------------*/ + unsigned char usb_buf[ROCCAT_KONE_PRO_AIR_SETTINGS_WRITE_PACKET_LENGTH]; + memset(usb_buf, 0x00, ROCCAT_KONE_PRO_AIR_SETTINGS_WRITE_PACKET_LENGTH); + + usb_buf[1] = 0x10; + usb_buf[2] = 0x50; + usb_buf[3] = 0x14; + memcpy(usb_buf + 5, active_settings + 10, 19); + usb_buf[24] = active_settings[41]; + + hid_write(dev, usb_buf, ROCCAT_KONE_PRO_AIR_SETTINGS_WRITE_PACKET_LENGTH); + + /*---------------------------------------------------------*\ + | 3. Send RGB | + \*---------------------------------------------------------*/ + SendRGB(false, + colors, + mode_value, + (mode_flags & MODE_FLAG_HAS_SPEED ) ? speed : (unsigned char)ROCCAT_KONE_PRO_AIR_SPEED_MAX, + (mode_flags & MODE_FLAG_HAS_BRIGHTNESS) ? brightness : (unsigned char)ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX + ); +} + +void RoccatKoneProAirController::SendRGB(bool direct, std::vector colors, unsigned char mode_value, unsigned char speed, unsigned char brightness) +{ + unsigned char usb_buf[ROCCAT_KONE_PRO_AIR_RGB_PACKET_LENGTH]; + memset(usb_buf, 0x00, ROCCAT_KONE_PRO_AIR_RGB_PACKET_LENGTH); + + usb_buf[1] = 0x10; + usb_buf[2] = direct ? 0x10 : 0x50; + usb_buf[3] = 0x0B; + usb_buf[4] = direct ? 0x00 : 0x01; + usb_buf[5] = mode_value; + usb_buf[6] = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; // Explicit brightness for first LED, not used by OpenRGB + usb_buf[7] = ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX; // Explicit brightness for second LED, not used by OpenRGB + usb_buf[8] = brightness; + usb_buf[9] = speed; + + for(unsigned int i = 0; i < colors.size(); i++) + { + usb_buf[10 + 3 * i] = RGBGetRValue(colors[i]); + usb_buf[11 + 3 * i] = RGBGetGValue(colors[i]); + usb_buf[12 + 3 * i] = RGBGetBValue(colors[i]); + } + + hid_write(dev, usb_buf, ROCCAT_KONE_PRO_AIR_RGB_PACKET_LENGTH); +} diff --git a/Controllers/RoccatController/RoccatKoneProAirController/RoccatKoneProAirController.h b/Controllers/RoccatController/RoccatKoneProAirController/RoccatKoneProAirController.h new file mode 100644 index 0000000..62885a5 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneProAirController/RoccatKoneProAirController.h @@ -0,0 +1,62 @@ +/*---------------------------------------------------------*\ +| RoccatKoneProAirController.h | +| | +| Driver for Roccat Kone Pro Air | +| | +| Plunti 10 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +#define ROCCAT_KONE_PRO_AIR_RGB_PACKET_LENGTH 16 +#define ROCCAT_KONE_PRO_AIR_SETTINGS_WRITE_PACKET_LENGTH 25 +#define ROCCAT_KONE_PRO_AIR_SETTINGS_READ_PACKET_LENGTH 42 +#define ROCCAT_KONE_PRO_AIR_LED_COUNT 2 + +enum +{ + ROCCAT_KONE_PRO_AIR_DIRECT_MODE_VALUE = 0x09, + ROCCAT_KONE_PRO_AIR_OFF_MODE_VALUE = 0x00, + ROCCAT_KONE_PRO_AIR_STATIC_MODE_VALUE = 0x01, + ROCCAT_KONE_PRO_AIR_RAINBOW_WAVE_MODE_VALUE = 0x06, + ROCCAT_KONE_PRO_AIR_HEARTBEAT_MODE_VALUE = 0x05, + ROCCAT_KONE_PRO_AIR_BREATHING_MODE_VALUE = 0x02, + ROCCAT_KONE_PRO_AIR_FLASHING_MODE_VALUE = 0x08, + ROCCAT_KONE_PRO_AIR_BATTERY_MODE_VALUE = 0x0A +}; + +enum +{ + ROCCAT_KONE_PRO_AIR_SPEED_MIN = 0x01, + ROCCAT_KONE_PRO_AIR_SPEED_MAX = 0x0B, + ROCCAT_KONE_PRO_AIR_SPEED_MID = (ROCCAT_KONE_PRO_AIR_SPEED_MAX - ROCCAT_KONE_PRO_AIR_SPEED_MIN) / 2, + ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MIN = 0x00, + ROCCAT_KONE_PRO_AIR_BRIGHTNESS_MAX = 0x64 +}; + +class RoccatKoneProAirController +{ +public: + RoccatKoneProAirController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~RoccatKoneProAirController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SendDirect(std::vector colors); + void SetMode(std::vector colors, unsigned char mode_value, unsigned char speed, unsigned char brightness, unsigned int mode_flags); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendRGB(bool direct, std::vector colors, unsigned char mode_value, unsigned char speed, unsigned char brightness); +}; diff --git a/Controllers/RoccatController/RoccatKoneProController/RGBController_RoccatKonePro.cpp b/Controllers/RoccatController/RoccatKoneProController/RGBController_RoccatKonePro.cpp new file mode 100644 index 0000000..5625dc9 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneProController/RGBController_RoccatKonePro.cpp @@ -0,0 +1,188 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKonePro.cpp | +| | +| RGBController for Roccat Kone Pro | +| | +| Garrett Denham (GardenOfWyers) 12 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RoccatKonePro.h" + +/**------------------------------------------------------------------*\ + @name Roccat Kone Pro Mouse + @category Mouse + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRoccatKoneProControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RoccatKonePro::RGBController_RoccatKonePro(RoccatKoneProController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Roccat"; + type = DEVICE_TYPE_MOUSE; + description = "Roccat Kone Pro Mouse Device"; + serial = controller->GetSerialString(); + location = controller->GetDeviceLocation(); + + // Also known as "Intelligent Lighting System" mode in Roccat Swarm + mode Direct; + Direct.name = "Direct"; + Direct.value = ROCCAT_KONE_PRO_DIRECT_MODE_VALUE; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + // Also known as "Fully Lit" mode in Roccat Swarm + mode Static; + Static.name = "Static"; + Static.value = ROCCAT_KONE_PRO_STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness = ROCCAT_KONE_PRO_BRIGHTNESS_MAX; + Static.brightness_min = ROCCAT_KONE_PRO_BRIGHTNESS_MIN; + Static.brightness_max = ROCCAT_KONE_PRO_BRIGHTNESS_MAX; + Static.colors.resize(ROCCAT_KONE_PRO_LED_COUNT); + modes.push_back(Static); + + // Also known as "Wave" mode in Roccat Swarm + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = ROCCAT_KONE_PRO_WAVE_MODE_VALUE; + Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness = ROCCAT_KONE_PRO_BRIGHTNESS_MAX; + Rainbow.brightness_min = ROCCAT_KONE_PRO_BRIGHTNESS_MIN; + Rainbow.brightness_max = ROCCAT_KONE_PRO_BRIGHTNESS_MAX; + Rainbow.speed = ROCCAT_KONE_PRO_SPEED_MID; + Rainbow.speed_min = ROCCAT_KONE_PRO_SPEED_MIN; + Rainbow.speed_max = ROCCAT_KONE_PRO_SPEED_MAX; + modes.push_back(Rainbow); + + mode Heartbeat; + Heartbeat.name = "Heartbeat"; + Heartbeat.value = ROCCAT_KONE_PRO_HEARTBEAT_MODE_VALUE; + Heartbeat.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Heartbeat.color_mode = MODE_COLORS_MODE_SPECIFIC; + Heartbeat.brightness = ROCCAT_KONE_PRO_BRIGHTNESS_MAX; + Heartbeat.brightness_min = ROCCAT_KONE_PRO_BRIGHTNESS_MIN; + Heartbeat.brightness_max = ROCCAT_KONE_PRO_BRIGHTNESS_MAX; + Heartbeat.speed = ROCCAT_KONE_PRO_SPEED_MID; + Heartbeat.speed_min = ROCCAT_KONE_PRO_SPEED_MIN; + Heartbeat.speed_max = ROCCAT_KONE_PRO_SPEED_MAX; + Heartbeat.colors.resize(ROCCAT_KONE_PRO_LED_COUNT); + modes.push_back(Heartbeat); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ROCCAT_KONE_PRO_BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.brightness = ROCCAT_KONE_PRO_BRIGHTNESS_MAX; + Breathing.brightness_min = ROCCAT_KONE_PRO_BRIGHTNESS_MIN; + Breathing.brightness_max = ROCCAT_KONE_PRO_BRIGHTNESS_MAX; + Breathing.speed = ROCCAT_KONE_PRO_SPEED_MID; + Breathing.speed_min = ROCCAT_KONE_PRO_SPEED_MIN; + Breathing.speed_max = ROCCAT_KONE_PRO_SPEED_MAX; + Breathing.colors.resize(ROCCAT_KONE_PRO_LED_COUNT); + modes.push_back(Breathing); + + mode Blinking; + Blinking.name = "Blinking"; + Blinking.value = ROCCAT_KONE_PRO_BLINKING_MODE_VALUE; + Blinking.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Blinking.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blinking.brightness = ROCCAT_KONE_PRO_BRIGHTNESS_MAX; + Blinking.brightness_min = ROCCAT_KONE_PRO_BRIGHTNESS_MIN; + Blinking.brightness_max = ROCCAT_KONE_PRO_BRIGHTNESS_MAX; + Blinking.speed = ROCCAT_KONE_PRO_SPEED_MID; + Blinking.speed_min = ROCCAT_KONE_PRO_SPEED_MIN; + Blinking.speed_max = ROCCAT_KONE_PRO_SPEED_MAX; + Blinking.colors.resize(ROCCAT_KONE_PRO_LED_COUNT); + modes.push_back(Blinking); + + SetupZones(); +} + +RGBController_RoccatKonePro::~RGBController_RoccatKonePro() +{ + delete controller; +} + +void RGBController_RoccatKonePro::SetupZones() +{ + zone new_zone; + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = ROCCAT_KONE_PRO_LED_COUNT; + new_zone.leds_max = ROCCAT_KONE_PRO_LED_COUNT; + new_zone.leds_count = ROCCAT_KONE_PRO_LED_COUNT; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + std::string led_names[2] = + { + "Left Button", + "Right Button" + }; + + for(unsigned int i = 0; i < ROCCAT_KONE_PRO_LED_COUNT; i++) + { + led new_led; + new_led.name = led_names[i]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_RoccatKonePro::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatKonePro::DeviceUpdateLEDs() +{ + const mode& active = modes[active_mode]; + + if(active.value == ROCCAT_KONE_PRO_DIRECT_MODE_VALUE) + { + controller->SendDirect(colors); + } + else + { + controller->SetMode(active.colors, active.value, active.speed, active.brightness, active.color_mode, active.flags); + } +} + +void RGBController_RoccatKonePro::UpdateZoneLEDs(int /*zone_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatKonePro::UpdateSingleLED(int /*led_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatKonePro::DeviceUpdateMode() +{ + if(modes[active_mode].value == ROCCAT_KONE_PRO_DIRECT_MODE_VALUE) + { + controller->SetupDirectMode(); + } + else + { + DeviceUpdateLEDs(); + } +} diff --git a/Controllers/RoccatController/RoccatKoneProController/RGBController_RoccatKonePro.h b/Controllers/RoccatController/RoccatKoneProController/RGBController_RoccatKonePro.h new file mode 100644 index 0000000..ee9c6f0 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneProController/RGBController_RoccatKonePro.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKonePro.h | +| | +| RGBController for Roccat Kone Pro | +| | +| Garrett Denham (GardenOfWyers) 12 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatKoneProController.h" + +class RGBController_RoccatKonePro : public RGBController +{ +public: + RGBController_RoccatKonePro(RoccatKoneProController* controller_ptr); + ~RGBController_RoccatKonePro(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatKoneProController* controller; +}; diff --git a/Controllers/RoccatController/RoccatKoneProController/RoccatKoneProController.cpp b/Controllers/RoccatController/RoccatKoneProController/RoccatKoneProController.cpp new file mode 100644 index 0000000..62799f0 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneProController/RoccatKoneProController.cpp @@ -0,0 +1,169 @@ +/*---------------------------------------------------------*\ +| RoccatKoneProController.cpp | +| | +| Driver for Roccat Kone Pro | +| | +| Garrett Denham (GardenOfWyers) 12 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RoccatKoneProController.h" +#include "StringUtils.h" + +RoccatKoneProController::RoccatKoneProController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; + + SetupDirectMode(); +} + +RoccatKoneProController::~RoccatKoneProController() +{ + hid_close(dev); +} + +std::string RoccatKoneProController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string RoccatKoneProController::GetNameString() +{ + return(name); +} + +std::string RoccatKoneProController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void RoccatKoneProController::SetupDirectMode() +{ + SwitchControl(true); +} + +void RoccatKoneProController::SwitchControl(bool direct) +{ + unsigned char usb_buf[ROCCAT_KONE_PRO_CONTROL_MODE_PACKET_LENGTH]; + + usb_buf[0x00] = 0x0E; + usb_buf[0x01] = 0x06; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = direct ? 0x01 : 0x00; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = 0xFF; + + hid_send_feature_report(dev, usb_buf, ROCCAT_KONE_PRO_CONTROL_MODE_PACKET_LENGTH); +} + +void RoccatKoneProController::SendDirect(std::vector colors) +{ + unsigned char usb_buf[ROCCAT_KONE_PRO_DIRECT_MODE_PACKET_LENGTH]; + + memset(usb_buf, 0x00, ROCCAT_KONE_PRO_DIRECT_MODE_PACKET_LENGTH); + + usb_buf[0x00] = ROCCAT_KONE_PRO_DIRECT_MODE_REPORT_ID; + usb_buf[0x01] = ROCCAT_KONE_PRO_DIRECT_MODE_BYTE; + + for(unsigned int i = 0; i < colors.size(); i++) + { + usb_buf[0x02 + 3 * i] = RGBGetRValue(colors[i]); + usb_buf[0x03 + 3 * i] = RGBGetGValue(colors[i]); + usb_buf[0x04 + 3 * i] = RGBGetBValue(colors[i]); + } + + hid_send_feature_report(dev, usb_buf, ROCCAT_KONE_PRO_DIRECT_MODE_PACKET_LENGTH); +} + +void RoccatKoneProController::SetMode(std::vector colors, unsigned char mode_value, unsigned char speed, unsigned char brightness, unsigned int color_mode, unsigned int mode_flags) +{ + /*---------------------------------------------------------*\ + | 1. Read from flash | + \*---------------------------------------------------------*/ + unsigned char usb_buf[ROCCAT_KONE_PRO_FLASH_PACKET_LENGTH]; + memset(usb_buf, 0x00, ROCCAT_KONE_PRO_FLASH_PACKET_LENGTH); + + hid_get_feature_report(dev, usb_buf, ROCCAT_KONE_PRO_FLASH_PACKET_LENGTH); + + /*---------------------------------------------------------*\ + | 2. Update needed bytes | + \*---------------------------------------------------------*/ + usb_buf[0x00] = 0x06; + usb_buf[0x01] = 0x45; + + usb_buf[0x03] = 0x06; + usb_buf[0x04] = 0x06; + usb_buf[0x05] = 0x1F; + + usb_buf[0x1E] = mode_value; + usb_buf[0x1F] = mode_flags & MODE_FLAG_HAS_SPEED ? speed : 0xFF; + usb_buf[0x20] = brightness; + + if(color_mode & MODE_COLORS_MODE_SPECIFIC) + { + for(unsigned int i = 0; i < colors.size(); i++) + { + usb_buf[0x24 + 5 * i] = 0x14; + usb_buf[0x25 + 5 * i] = 0xFF; + usb_buf[0x26 + 5 * i] = RGBGetRValue(colors[i]); + usb_buf[0x27 + 5 * i] = RGBGetGValue(colors[i]); + usb_buf[0x28 + 5 * i] = RGBGetBValue(colors[i]); + } + } + else if(color_mode & MODE_COLORS_NONE) + { + for(unsigned int i = 0; i < colors.size(); i++) + { + usb_buf[0x24 + 5 * i] = 0x14; + usb_buf[0x25 + 5 * i] = 0x00; + usb_buf[0x26 + 5 * i] = 0x00; + usb_buf[0x27 + 5 * i] = 0x00; + usb_buf[0x28 + 5 * i] = 0x00; + } + } + + usb_buf[0x2E] = 0x14; + usb_buf[0x2F] = 0xFF; + + unsigned int crc = CalculateCRC(&usb_buf[0x00]); + + usb_buf[0x43] = (unsigned char) crc; + usb_buf[0x44] = crc >> 8; + + /*---------------------------------------------------------*\ + | 3. Send to flash | + \*---------------------------------------------------------*/ + hid_send_feature_report(dev, usb_buf, ROCCAT_KONE_PRO_FLASH_PACKET_LENGTH); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + /*---------------------------------------------------------*\ + | 4. Switch to built-in mode | + \*---------------------------------------------------------*/ + SwitchControl(false); +} + +unsigned int RoccatKoneProController::CalculateCRC(unsigned char* bytes) +{ + unsigned int crc = 0; + + for(unsigned int i = 0; i < ROCCAT_KONE_PRO_FLASH_PACKET_LENGTH - 2; i++) + { + crc += bytes[i]; + } + + return crc; +} diff --git a/Controllers/RoccatController/RoccatKoneProController/RoccatKoneProController.h b/Controllers/RoccatController/RoccatKoneProController/RoccatKoneProController.h new file mode 100644 index 0000000..4417420 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneProController/RoccatKoneProController.h @@ -0,0 +1,70 @@ +/*---------------------------------------------------------*\ +| RoccatKoneProController.h | +| | +| Driver for Roccat Kone Pro | +| | +| Garrett Denham (GardenOfWyers) 12 Jan 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +#define ROCCAT_KONE_PRO_CONTROL_MODE_PACKET_LENGTH 6 +#define ROCCAT_KONE_PRO_DIRECT_MODE_PACKET_LENGTH 11 +#define ROCCAT_KONE_PRO_FLASH_PACKET_LENGTH 69 +#define ROCCAT_KONE_PRO_FLASH_REPORT_ID 0x06 +#define ROCCAT_KONE_PRO_DIRECT_MODE_REPORT_ID 0x0D +#define ROCCAT_KONE_PRO_DIRECT_MODE_BYTE 0x0B +#define ROCCAT_KONE_PRO_LED_COUNT 2 + +enum +{ + ROCCAT_KONE_PRO_DIRECT_MODE_VALUE = 0x00, + ROCCAT_KONE_PRO_STATIC_MODE_VALUE = 0x01, + ROCCAT_KONE_PRO_WAVE_MODE_VALUE = 0x0A, + ROCCAT_KONE_PRO_HEARTBEAT_MODE_VALUE = 0x04, + ROCCAT_KONE_PRO_BREATHING_MODE_VALUE = 0x03, + ROCCAT_KONE_PRO_BLINKING_MODE_VALUE = 0x02 +}; + +enum +{ + ROCCAT_KONE_PRO_SPEED_MIN = 0x01, + ROCCAT_KONE_PRO_SPEED_MAX = 0x0B, + ROCCAT_KONE_PRO_SPEED_MID = (ROCCAT_KONE_PRO_SPEED_MAX - ROCCAT_KONE_PRO_SPEED_MIN) / 2, + ROCCAT_KONE_PRO_BRIGHTNESS_MIN = 0x00, + ROCCAT_KONE_PRO_BRIGHTNESS_MAX = 0xFF +}; + +class RoccatKoneProController +{ +public: + RoccatKoneProController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~RoccatKoneProController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetupDirectMode(); + void SendDirect(std::vector colors); + void SetMode(std::vector colors, + unsigned char mode_value, + unsigned char speed, + unsigned char brightness, + unsigned int color_mode, + unsigned int mode_flags + ); +private: + hid_device* dev; + std::string location; + std::string name; + + unsigned int CalculateCRC(unsigned char* bytes); + void SwitchControl(bool direct); +}; diff --git a/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.cpp b/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.cpp new file mode 100644 index 0000000..14ab944 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.cpp @@ -0,0 +1,285 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKoneXP.cpp | +| | +| RGBController for Roccat Kone XP | +| | +| Mola19 12 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RoccatKoneXP.h" + +/**------------------------------------------------------------------*\ + @name Roccat Kone XP Mouse + @category Mouse + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRoccatKoneXPControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RoccatKoneXP::RGBController_RoccatKoneXP(RoccatKoneXPController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Roccat"; + type = DEVICE_TYPE_MOUSE; + description = "Roccat Kone XP Mouse Device"; + version = controller->GetVersion(); + location = controller->GetLocation(); + serial = controller->GetSerial(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ROCCAT_KONE_XP_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = ROCCAT_KONE_XP_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = ROCCAT_KONE_XP_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Static.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Static.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = ROCCAT_KONE_XP_MODE_WAVE; + Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Rainbow.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Rainbow.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + Rainbow.speed = ROCCAT_KONE_XP_SPEED_DEFAULT; + Rainbow.speed_min = ROCCAT_KONE_XP_SPEED_MIN; + Rainbow.speed_max = ROCCAT_KONE_XP_SPEED_MAX; + modes.push_back(Rainbow); + + mode Blinking; + Blinking.name = "Blinking"; + Blinking.value = ROCCAT_KONE_XP_MODE_BLINKING; + Blinking.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Blinking.color_mode = MODE_COLORS_PER_LED; + Blinking.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Blinking.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Blinking.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + Blinking.speed = ROCCAT_KONE_XP_SPEED_DEFAULT; + Blinking.speed_min = ROCCAT_KONE_XP_SPEED_MIN; + Blinking.speed_max = ROCCAT_KONE_XP_SPEED_MAX; + modes.push_back(Blinking); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ROCCAT_KONE_XP_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Breathing.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Breathing.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + Breathing.speed = ROCCAT_KONE_XP_SPEED_DEFAULT; + Breathing.speed_min = ROCCAT_KONE_XP_SPEED_MIN; + Breathing.speed_max = ROCCAT_KONE_XP_SPEED_MAX; + modes.push_back(Breathing); + + mode Heartbeat; + Heartbeat.name = "Heartbeat"; + Heartbeat.value = ROCCAT_KONE_XP_MODE_HEARTBEAT; + Heartbeat.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Heartbeat.color_mode = MODE_COLORS_PER_LED; + Heartbeat.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Heartbeat.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Heartbeat.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + Heartbeat.speed = ROCCAT_KONE_XP_SPEED_DEFAULT; + Heartbeat.speed_min = ROCCAT_KONE_XP_SPEED_MIN; + Heartbeat.speed_max = ROCCAT_KONE_XP_SPEED_MAX; + modes.push_back(Heartbeat); + + mode Photon; + Photon.name = "Photon FX"; + Photon.value = ROCCAT_KONE_XP_MODE_PHOTON_FX; + Photon.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Photon.color_mode = MODE_COLORS_NONE; + Photon.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Photon.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Photon.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + modes.push_back(Photon); + + /*---------------------------------------------------------------------*\ + | This is the default mode for software modes, while swarm isn't active | + \*---------------------------------------------------------------------*/ + mode Default; + Default.name = "Default"; + Default.value = ROCCAT_KONE_XP_MODE_DEFAULT; + Default.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Default.color_mode = MODE_COLORS_NONE; + Default.brightness = ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT; + Default.brightness_min = ROCCAT_KONE_XP_BRIGHTNESS_MIN; + Default.brightness_max = ROCCAT_KONE_XP_BRIGHTNESS_MAX; + modes.push_back(Default); + + SetupZones(); + + uint8_t active_profile = controller->GetActiveProfile(); + controller->SetReadProfile(active_profile); + controller->WaitUntilReady(); + + roccat_kone_xp_mode_struct active = controller->GetMode(); + + for(uint32_t i = 0; i < modes.size(); i++) + { + if(modes[i].value == active.mode) + { + active_mode = i; + break; + } + + /*----------------------------------------------*\ + | If no mode was found, select 0th mode (direct) | + \*----------------------------------------------*/ + if(i == modes.size() - 1) + { + active_mode = 0; + } + } + + modes[active_mode].speed = active.speed; + modes[active_mode].brightness = active.brightness; + + for(uint8_t i = 0; i < 20; i++) + { + colors[i] = active.colors[i].color; + } +} + +RGBController_RoccatKoneXP::~RGBController_RoccatKoneXP() +{ + delete controller; +} + +void RGBController_RoccatKoneXP::SetupZones() +{ + zone left; + left.name = "Left"; + left.type = ZONE_TYPE_LINEAR; + left.leds_min = 9; + left.leds_max = 9; + left.leds_count = 9; + left.matrix_map = NULL; + zones.push_back(left); + + for (uint8_t i = 1; i <= 9; i++) { + led left_led; + left_led.name = "Left LED " + std::to_string(i); + leds.push_back(left_led); + } + + zone right; + right.name = "Right"; + right.type = ZONE_TYPE_LINEAR; + right.leds_min = 9; + right.leds_max = 9; + right.leds_count = 9; + right.matrix_map = NULL; + zones.push_back(right); + + for (uint8_t i = 1; i <= 9; i++) { + led left_led; + left_led.name = "Right LED " + std::to_string(i); + leds.push_back(left_led); + } + + zone wheel; + wheel.name = "Scrollwheel"; + wheel.type = ZONE_TYPE_SINGLE; + wheel.leds_min = 1; + wheel.leds_max = 1; + wheel.leds_count = 1; + wheel.matrix_map = NULL; + zones.push_back(wheel); + + led wheel_led; + wheel_led.name = "Scrollwheel LED"; + leds.push_back(wheel_led); + + zone dpi; + dpi.name = "DPI button"; + dpi.type = ZONE_TYPE_SINGLE; + dpi.leds_min = 1; + dpi.leds_max = 1; + dpi.leds_count = 1; + dpi.matrix_map = NULL; + zones.push_back(dpi); + + led dpi_led; + dpi_led.name = "DPI button LED"; + leds.push_back(dpi_led); + + SetupColors(); +} + +void RGBController_RoccatKoneXP::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatKoneXP::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == ROCCAT_KONE_XP_MODE_DIRECT) + { + controller->SendDirect(colors); + } + else + { + DeviceUpdateMode(); + } +} + +void RGBController_RoccatKoneXP::UpdateZoneLEDs(int /*zone_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatKoneXP::UpdateSingleLED(int /*led_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatKoneXP::DeviceUpdateMode() +{ + mode selected = modes[active_mode]; + + roccat_kone_xp_mode_struct active = controller->GetMode(); + + active.mode = selected.value; + active.speed = selected.speed; + active.brightness = (modes[active_mode].value == ROCCAT_KONE_XP_MODE_DIRECT) ? 0xFF : selected.brightness; + + for(uint8_t i = 0; i < 20; i++) + { + active.colors[i].color = colors[i]; + } + + controller->SetMode(active); + controller->WaitUntilReady(); + + controller->EnableDirect(selected.value == ROCCAT_KONE_XP_MODE_DIRECT); + controller->WaitUntilReady(); +} diff --git a/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.h b/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.h new file mode 100644 index 0000000..464de28 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKoneXP.h | +| | +| RGBController for Roccat Kone XP | +| | +| Mola19 12 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatKoneXPController.h" + +class RGBController_RoccatKoneXP : public RGBController +{ +public: + RGBController_RoccatKoneXP(RoccatKoneXPController* controller_ptr); + ~RGBController_RoccatKoneXP(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatKoneXPController* controller; +}; diff --git a/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.cpp b/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.cpp new file mode 100644 index 0000000..216d96d --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.cpp @@ -0,0 +1,308 @@ +/*---------------------------------------------------------*\ +| RoccatKoneXPController.cpp | +| | +| Driver for Roccat Kone XP | +| | +| Mola19 12 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "LogManager.h" +#include "RoccatKoneXPController.h" +#include "StringUtils.h" + +RoccatKoneXPController::RoccatKoneXPController(hid_device* dev_handle, char *path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +RoccatKoneXPController::~RoccatKoneXPController() +{ + hid_close(dev); +} + +std::string RoccatKoneXPController::GetLocation() +{ + return("HID: " + location); +} + +std::string RoccatKoneXPController::GetName() +{ + return(name); +} + +std::string RoccatKoneXPController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); + +} + +std::string RoccatKoneXPController::GetVersion() +{ + uint8_t buf[9] = { 0x09 }; + int return_length = hid_get_feature_report(dev, buf, 9); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not fetch version. HIDAPI Error: %ls", hid_error(dev)); + return std::string("Unknown"); + } + + char version[5]; + snprintf(version, 5, "%d.%02d", buf[2] / 100, buf[2] % 100); + + return std::string(version); +} + +uint8_t RoccatKoneXPController::GetActiveProfile() +{ + uint8_t buf[4] = { 0x05 }; + int return_length = hid_get_feature_report(dev, buf, 4); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not fetch active profile. HIDAPI Error: %ls", hid_error(dev)); + return 0; + } + + return buf[2]; +} + +#include +roccat_kone_xp_mode_struct RoccatKoneXPController::GetMode() +{ + uint8_t buf[0xAE] = { 0x06 }; + int return_length = hid_get_feature_report(dev, buf, 0xAE); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not fetch mode. HIDAPI Error: %ls", hid_error(dev)); + roccat_kone_xp_mode_struct default_mode; + return default_mode; + } + + roccat_kone_xp_mode_struct active_mode; + + active_mode.profile = buf[2]; + active_mode.byte_3 = buf[3]; + active_mode.byte_4 = buf[4]; + active_mode.dpi_flag = buf[5]; + active_mode.byte_6 = buf[6]; + + for(uint8_t i = 0; i < 10; i++) + { + active_mode.dpi[i] = (buf[8 + i * 2] << 8) + buf[7 + i * 2]; + } + + active_mode.angle_snapping = (bool) buf[27]; + active_mode.byte_28 = buf[28]; + active_mode.polling_rate = buf[29]; + active_mode.mode = buf[30]; + active_mode.speed = buf[31]; + active_mode.brightness = buf[32]; + active_mode.time_until_idle = buf[33]; + active_mode.idle_mode = buf[34]; + active_mode.byte_35 = buf[35]; + + for(uint8_t i = 0; i < 20; i++) + { + active_mode.colors[i].brightness = buf[37 + i * 6]; + active_mode.colors[i].color = ToRGBColor( + buf[38 + i * 6], + buf[39 + i * 6], + buf[40 + i * 6] + ); + } + + active_mode.byte_156 = buf[156]; + active_mode.byte_157 = buf[157]; + active_mode.profile_color_brightness = buf[158]; + active_mode.profile_color_red = buf[159]; + active_mode.profile_color_green = buf[160]; + active_mode.profile_color_blue = buf[161]; + active_mode.byte_162 = buf[162]; + active_mode.theme = buf[163]; + active_mode.auto_dpi_flag = buf[164]; + + active_mode.end_bytes[0] = buf[165]; + active_mode.end_bytes[1] = buf[166]; + active_mode.end_bytes[2] = buf[167]; + active_mode.end_bytes[3] = buf[168]; + active_mode.end_bytes[4] = buf[169]; + active_mode.end_bytes[5] = buf[170]; + active_mode.end_bytes[6] = buf[171]; + + return active_mode; +} + +void RoccatKoneXPController::EnableDirect(bool on_off_switch) +{ + unsigned char usb_buf[6]; + + usb_buf[0x00] = 0x0E; + usb_buf[0x01] = 0x06; + usb_buf[0x02] = 0x01; + usb_buf[0x03] = on_off_switch; + usb_buf[0x04] = 0x00; + usb_buf[0x05] = 0xFF; + + int return_length = hid_send_feature_report(dev, usb_buf, 6); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not send mode. HIDAPI Error: %ls", hid_error(dev)); + } +} + +void RoccatKoneXPController::SetReadProfile(uint8_t profile) +{ + unsigned char usb_buf[4]; + + usb_buf[0x00] = 0x04; + usb_buf[0x01] = profile; + usb_buf[0x02] = 0x80; + usb_buf[0x03] = 0xFF; + + int return_length = hid_send_feature_report(dev, usb_buf, 4); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not set profile to read. HIDAPI Error: %ls", hid_error(dev)); + } +} + +void RoccatKoneXPController::SetMode(roccat_kone_xp_mode_struct mode) +{ + uint8_t buf[0xAE]; + memset(buf, 0x00, 0xAE); + + buf[0x00] = 0x06; + buf[0x01] = 0xAE; + + buf[0x02] = mode.profile; + buf[0x03] = mode.byte_3; + buf[0x04] = mode.byte_4; + buf[0x05] = mode.dpi_flag; + buf[0x06] = mode.byte_6; + + for(uint8_t i = 0; i < 10; i++) + { + buf[0x07 + i * 2] = mode.dpi[i] & 0xFF; + buf[0x08 + i * 2] = mode.dpi[i] >> 8; + } + + buf[0x1B] = mode.angle_snapping; + buf[0x1C] = mode.byte_28; + buf[0x1D] = mode.polling_rate; + buf[0x1E] = mode.mode; + buf[0x1F] = mode.speed; + buf[0x20] = mode.brightness; + buf[0x21] = mode.time_until_idle; + buf[0x22] = mode.idle_mode; + buf[0x23] = mode.byte_35; + + for(uint8_t i = 0; i < 20; i++) + { + buf[0x24 + i * 6] = mode.colors[i].byte_0; + buf[0x25 + i * 6] = 0xFF; + buf[0x26 + i * 6] = RGBGetRValue(mode.colors[i].color); + buf[0x27 + i * 6] = RGBGetGValue(mode.colors[i].color); + buf[0x28 + i * 6] = RGBGetBValue(mode.colors[i].color); + buf[0x29 + i * 6] = mode.colors[i].byte_5; + } + + buf[0x9C] = mode.byte_156; + buf[0x9D] = mode.byte_157; + buf[0x9E] = mode.profile_color_brightness; + buf[0x9F] = mode.profile_color_red; + buf[0xA0] = mode.profile_color_green; + buf[0xA1] = mode.profile_color_blue; + buf[0xA2] = mode.byte_162; + buf[0xA3] = mode.theme & ~0x80; // this stores the swarm intern selected theme (biggest bit is a flag for custom theme) + buf[0xA4] = mode.auto_dpi_flag; + + for(uint8_t i = 0; i < 7; i++) + { + buf[0xA5 + i] = mode.end_bytes[i]; + } + + unsigned short total = 0; + for(int i = 0; i < 0xAE - 2; i++) total += buf[i]; + + buf[0xAE - 2] = total & 0xFF; + buf[0xAE - 1] = total >> 8; + + + int return_length = hid_send_feature_report(dev, buf, 0xAE); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not send mode. HIDAPI Error: %ls", hid_error(dev)); + } +} + +void RoccatKoneXPController::SendDirect(std::vector colors) +{ + uint8_t buf[0x7A]; + memset(buf, 0x00, 0x7A); + + buf[0x00] = 0x0D; + buf[0x01] = 0x7A; + + for(uint8_t i = 0; i < colors.size() && i < 20; i++) + { + /*-----------------------------------------------------------*\ + | This device uses rgbrgb which means that e.g. the red value | + | is calculated by multiplying both given red values. | + | Maybe it is some sort of brightness? | + | For OpenRGBs purpose these aren't usefull, | + | so the second part is always 0xff. | + \*-----------------------------------------------------------*/ + buf[0x02 + i * 6] = RGBGetRValue(colors[i]); + buf[0x03 + i * 6] = RGBGetGValue(colors[i]); + buf[0x04 + i * 6] = RGBGetBValue(colors[i]); + buf[0x05 + i * 6] = 0xFF; + buf[0x06 + i * 6] = 0xFF; + buf[0x07 + i * 6] = 0xFF; + } + + int return_length = hid_send_feature_report(dev, buf, 0x7A); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Kone XP]: Could not send direct. HIDAPI Error: %ls", hid_error(dev)); + } +} + +void RoccatKoneXPController::WaitUntilReady() +{ + uint8_t buf[4]; + memset(buf, 0x00, 4); + + buf[0] = 0x04; + + for(unsigned char i = 0; buf[1] != 1 && i < 100; i++) + { + if(i != 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + + hid_get_feature_report(dev, buf, 4); + } +} diff --git a/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.h b/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.h new file mode 100644 index 0000000..fc4f0d3 --- /dev/null +++ b/Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.h @@ -0,0 +1,103 @@ +/*---------------------------------------------------------*\ +| RoccatKoneXPController.h | +| | +| Driver for Roccat Kone XP | +| | +| Mola19 12 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +enum +{ + ROCCAT_KONE_XP_MODE_DIRECT = 0x0B, + ROCCAT_KONE_XP_MODE_OFF = 0x00, + ROCCAT_KONE_XP_MODE_STATIC = 0x01, + ROCCAT_KONE_XP_MODE_BLINKING = 0x02, + ROCCAT_KONE_XP_MODE_BREATHING = 0x03, + ROCCAT_KONE_XP_MODE_HEARTBEAT = 0x04, + ROCCAT_KONE_XP_MODE_PHOTON_FX = 0x05, + ROCCAT_KONE_XP_MODE_DEFAULT = 0x09, + ROCCAT_KONE_XP_MODE_WAVE = 0x0A +}; + +enum +{ + ROCCAT_KONE_XP_SPEED_MIN = 0x0B, + ROCCAT_KONE_XP_SPEED_MAX = 0x01, + ROCCAT_KONE_XP_SPEED_DEFAULT = 0x06, + ROCCAT_KONE_XP_BRIGHTNESS_MIN = 0x00, + ROCCAT_KONE_XP_BRIGHTNESS_MAX = 0xFF, + ROCCAT_KONE_XP_BRIGHTNESS_DEFAULT = 0xFF +}; + +struct roccat_kone_xp_color_struct +{ + uint8_t byte_0 = 0x14; + uint8_t brightness = 0xFF; + RGBColor color = 0; + uint8_t byte_5 = 0x64; +}; + +struct roccat_kone_xp_mode_struct +{ + uint8_t profile = 0; + uint8_t byte_3 = 0x06; + uint8_t byte_4 = 0x06; + uint8_t dpi_flag = 0x1F; + uint8_t byte_6 = 0x01; + uint16_t dpi[10] = { 0x08, 0x10, 0x18, 0x20, 0x40, 0x08, 0x10, 0x18, 0x20, 0x40 }; + bool angle_snapping = false; + uint8_t byte_28 = 0x00; + uint8_t polling_rate = 0x03; + uint8_t mode = ROCCAT_KONE_XP_MODE_STATIC; + uint8_t speed = 0x00; + uint8_t brightness = 0xFF; + uint8_t time_until_idle = 0x0F; + uint8_t idle_mode = 0x00; + uint8_t byte_35 = 0x00; + roccat_kone_xp_color_struct colors[20]; + uint8_t byte_156 = 0x01; + uint8_t byte_157 = 0x64; + uint8_t profile_color_brightness = 0xFF; + uint8_t profile_color_red = 0xFF; + uint8_t profile_color_green = 0x00; + uint8_t profile_color_blue = 0x00; + uint8_t byte_162 = 0x00; + uint8_t theme = 0x80; + uint8_t auto_dpi_flag = 0x00; + uint8_t end_bytes[7] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +}; + +class RoccatKoneXPController +{ +public: + RoccatKoneXPController(hid_device* dev_handle, char *path, std::string dev_name); + ~RoccatKoneXPController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + std::string GetVersion(); + + uint8_t GetActiveProfile(); + roccat_kone_xp_mode_struct GetMode(); + + void EnableDirect(bool on_off_switch); + void SetReadProfile(uint8_t profile); + void SendDirect(std::vector colors); + void SetMode(roccat_kone_xp_mode_struct mode); + + void WaitUntilReady(); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/RoccatController/RoccatKovaController/RGBController_RoccatKova.cpp b/Controllers/RoccatController/RoccatKovaController/RGBController_RoccatKova.cpp new file mode 100644 index 0000000..e391522 --- /dev/null +++ b/Controllers/RoccatController/RoccatKovaController/RGBController_RoccatKova.cpp @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKova.cpp | +| | +| RGBController for Roccat Kova | +| | +| Gustash 01 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RoccatKova.h" + +/**------------------------------------------------------------------*\ + @name Roccat Kova + @category Mouse + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors RoccatControllerDetect + @comment Color Flow mode is only supported starting at the first + preset color in the mouse's memory, and color offsets for each LED + are not supported. You'd need to use Swarm if you intend to use that + specific feature. +\*-------------------------------------------------------------------*/ + +RGBController_RoccatKova::RGBController_RoccatKova(RoccatKovaController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Roccat"; + type = DEVICE_TYPE_MOUSE; + description = "Roccat Kova Mouse Device"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + version = controller->GetVersion(); + + mode Static; + Static.name = "Static"; + Static.value = ROCCAT_KOVA_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode ColorFlow; + ColorFlow.name = "Color Flow"; + ColorFlow.value = ROCCAT_KOVA_MODE_COLOR_FLOW; + ColorFlow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + ColorFlow.color_mode = MODE_COLORS_RANDOM; + ColorFlow.speed_min = ROCCAT_KOVA_SPEED_MIN; + ColorFlow.speed_max = ROCCAT_KOVA_SPEED_MAX; + modes.push_back(ColorFlow); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = ROCCAT_KOVA_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Flashing.color_mode = MODE_COLORS_PER_LED; + Flashing.speed_min = ROCCAT_KOVA_SPEED_MIN; + Flashing.speed_max = ROCCAT_KOVA_SPEED_MAX; + modes.push_back(Flashing); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ROCCAT_KOVA_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = ROCCAT_KOVA_SPEED_MIN; + Breathing.speed_max = ROCCAT_KOVA_SPEED_MAX; + modes.push_back(Breathing); + + mode Off; + Off.name = "Off"; + Off.value = 0x00; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_RoccatKova::~RGBController_RoccatKova() +{ + delete controller; +} + +void RGBController_RoccatKova::SetupZones() +{ + zone Mouse; + Mouse.name = "Mouse"; + Mouse.type = ZONE_TYPE_LINEAR; + Mouse.leds_count = ROCCAT_KOVA_LED_COUNT; + Mouse.leds_min = ROCCAT_KOVA_LED_COUNT; + Mouse.leds_max = ROCCAT_KOVA_LED_COUNT; + Mouse.matrix_map = NULL; + zones.push_back(Mouse); + + led WheelLED; + WheelLED.name = "Wheel LED"; + WheelLED.value = ROCCAT_KOVA_WHEEL_IDX; + leds.push_back(WheelLED); + + led StripeLED; + StripeLED.name = "Stripe LED"; + StripeLED.value = ROCCAT_KOVA_PIPE_IDX; + leds.push_back(StripeLED); + + SetupColors(); +} + +void RGBController_RoccatKova::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatKova::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_RoccatKova::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_RoccatKova::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_RoccatKova::DeviceUpdateMode() +{ + mode &active = modes[active_mode]; + int mode = active.value; + bool is_color_flow = active.color_mode == MODE_COLORS_RANDOM; + if(active.value == ROCCAT_KOVA_MODE_COLOR_FLOW) + { + mode = ROCCAT_KOVA_MODE_STATIC; + is_color_flow = true; + } + + controller->SetColor(colors[0], colors[1], mode, active.speed, is_color_flow); +} diff --git a/Controllers/RoccatController/RoccatKovaController/RGBController_RoccatKova.h b/Controllers/RoccatController/RoccatKovaController/RGBController_RoccatKova.h new file mode 100644 index 0000000..0f74a0d --- /dev/null +++ b/Controllers/RoccatController/RoccatKovaController/RGBController_RoccatKova.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatKova.h | +| | +| RGBController for Roccat Kova | +| | +| Gustash 01 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatKovaController.h" + +class RGBController_RoccatKova : public RGBController +{ +public: + RGBController_RoccatKova(RoccatKovaController *controller_ptr); + ~RGBController_RoccatKova(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatKovaController *controller; +}; diff --git a/Controllers/RoccatController/RoccatKovaController/RoccatKovaController.cpp b/Controllers/RoccatController/RoccatKovaController/RoccatKovaController.cpp new file mode 100644 index 0000000..b622471 --- /dev/null +++ b/Controllers/RoccatController/RoccatKovaController/RoccatKovaController.cpp @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| RoccatKovaController.cpp | +| | +| Driver for Roccat Kova | +| | +| Gustash 01 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "RoccatKovaController.h" +#include "StringUtils.h" + +RoccatKovaController::RoccatKovaController(hid_device* dev_handle, char *path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + SendInitialPacket(); + FetchFirmwareVersion(); +} + +RoccatKovaController::~RoccatKovaController() +{ + hid_close(dev); +} + +std::string RoccatKovaController::GetLocation() +{ + return("HID: " + location); +} + +std::string RoccatKovaController::GetName() +{ + return(name); +} + +std::string RoccatKovaController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string RoccatKovaController::GetVersion() +{ + return(version); +} + +void RoccatKovaController::SetColor(RGBColor color_wheel, + RGBColor color_stripe, + uint8_t mode, + uint8_t speed, + bool color_flow) +{ + bool is_off = mode == ROCCAT_KOVA_MODE_OFF; + uint8_t report_buf[ROCCAT_KOVA_PROFILE_WRITE_PACKET_SIZE] {00}; + FetchProfileData(report_buf); + + report_buf[0x0] = ROCCAT_KOVA_PROFILE_REPORT_ID; + report_buf[0x1] = ROCCAT_KOVA_PROFILE_WRITE_PACKET_SIZE; + + report_buf[ROCCAT_KOVA_FLAGS_IDX] |= ROCCAT_KOVA_USE_CUSTOM_COLORS_MASK; + if(is_off) + { + report_buf[ROCCAT_KOVA_FLAGS_IDX] &= ~ROCCAT_KOVA_LIGHTS_ON_MASK; + } + else + { + report_buf[ROCCAT_KOVA_FLAGS_IDX] |= ROCCAT_KOVA_LIGHTS_ON_MASK; + } + + /*-------------------------------------------------*\ + | Set colors for each LED and reset the selected | + | preset color to ensure consistency | + \*-------------------------------------------------*/ + report_buf[ROCCAT_KOVA_WHEEL_IDX] = 0x0; + report_buf[ROCCAT_KOVA_WHEEL_IDX + ROCCAT_KOVA_R_OFFSET] = RGBGetRValue(color_wheel); + report_buf[ROCCAT_KOVA_WHEEL_IDX + ROCCAT_KOVA_G_OFFSET] = RGBGetGValue(color_wheel); + report_buf[ROCCAT_KOVA_WHEEL_IDX + ROCCAT_KOVA_B_OFFSET] = RGBGetBValue(color_wheel); + report_buf[ROCCAT_KOVA_PIPE_IDX] = 0x0; + report_buf[ROCCAT_KOVA_PIPE_IDX + ROCCAT_KOVA_R_OFFSET] = RGBGetRValue(color_stripe); + report_buf[ROCCAT_KOVA_PIPE_IDX + ROCCAT_KOVA_G_OFFSET] = RGBGetGValue(color_stripe); + report_buf[ROCCAT_KOVA_PIPE_IDX + ROCCAT_KOVA_B_OFFSET] = RGBGetBValue(color_stripe); + + report_buf[ROCCAT_KOVA_COLOR_FLOW_IDX] = color_flow; + if(!is_off) + { + report_buf[ROCCAT_KOVA_MODE_IDX] = mode; + } + report_buf[ROCCAT_KOVA_EFFECT_SPEED_IDX] = speed; + + uint16_t checksum = GenerateChecksum(report_buf, sizeof(report_buf) - 2); + + report_buf[ROCCAT_KOVA_CHECKSUM_IDX] = checksum & 0xFF; + report_buf[ROCCAT_KOVA_CHECKSUM_IDX + 1] = checksum >> 8; + + hid_send_feature_report(dev, report_buf, ROCCAT_KOVA_PROFILE_WRITE_PACKET_SIZE); +} + +void RoccatKovaController::SendInitialPacket() +{ + uint8_t buf[ROCCAT_KOVA_INIT_WRITE_PACKET_SIZE] {00}; + buf[0x00] = ROCCAT_KOVA_INIT_REPORT_ID; + buf[0x01] = 0x00; + buf[0x02] = 0x80; + hid_send_feature_report(dev, buf, ROCCAT_KOVA_INIT_WRITE_PACKET_SIZE); +} + +void RoccatKovaController::FetchFirmwareVersion() +{ + uint8_t buf[ROCCAT_KOVA_VERSION_READ_PACKET_SIZE] {00}; + buf[0x0] = ROCCAT_KOVA_VERSION_REPORT_ID; + + hid_get_feature_report(dev, buf, ROCCAT_KOVA_VERSION_READ_PACKET_SIZE); + + uint8_t fw_version = buf[ROCCAT_KOVA_FIRMWARE_VERSION_IDX]; + char version_str[5] {00}; + snprintf(version_str, 5, "%.2f", fw_version / 100.); + version = version_str; +} + +void RoccatKovaController::FetchProfileData(uint8_t *buf) +{ + buf[0x00] = ROCCAT_KOVA_PROFILE_REPORT_ID; + hid_get_feature_report(dev, buf, ROCCAT_KOVA_PROFILE_WRITE_PACKET_SIZE); +} + +uint16_t RoccatKovaController::GenerateChecksum(uint8_t *buf, size_t length) +{ + uint16_t checksum = 0x0; + for (uint8_t idx = 0; idx < length; idx++) + { + checksum += buf[idx]; + } + return checksum; +} diff --git a/Controllers/RoccatController/RoccatKovaController/RoccatKovaController.h b/Controllers/RoccatController/RoccatKovaController/RoccatKovaController.h new file mode 100644 index 0000000..d6a1a4c --- /dev/null +++ b/Controllers/RoccatController/RoccatKovaController/RoccatKovaController.h @@ -0,0 +1,93 @@ +/*---------------------------------------------------------*\ +| RoccatKovaController.h | +| | +| Driver for Roccat Kova | +| | +| Gustash 01 Dec 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +#define ROCCAT_KOVA_HID_MAX_STR 255 +#define ROCCAT_KOVA_LED_COUNT 2 +#define ROCCAT_KOVA_SPEED_MIN 1 +#define ROCCAT_KOVA_SPEED_MAX 3 +#define ROCCAT_KOVA_INIT_REPORT_ID 4 +#define ROCCAT_KOVA_INIT_WRITE_PACKET_SIZE 3 +#define ROCCAT_KOVA_PROFILE_REPORT_ID 6 +#define ROCCAT_KOVA_PROFILE_WRITE_PACKET_SIZE 28 +#define ROCCAT_KOVA_VERSION_REPORT_ID 9 +#define ROCCAT_KOVA_VERSION_READ_PACKET_SIZE 8 +/*#define NUM_OF_DPI_SWITCHES 5*/ + +enum +{ + ROCCAT_KOVA_FIRMWARE_VERSION_IDX = 2, + /*ROCCAT_KOVA_SELECTED_PROFILE_IDX = 2,*/ + /*ROCCAT_KOVA_UNKNOWN_3_IDX = 3,*/ + /*ROCCAT_KOVA_UNKNOWN_4_IDX = 4,*/ + /*ROCCAT_KOVA_ORIENTATION_IDX = 5,*/ + /*ROCCAT_KOVA_DPI_SWITCHER_IDX = 6,*/ + /*ROCCAT_KOVA_DPI_SPEED_IDX = 7,*/ + /*ROCCAT_KOVA_SELECTED_DPI_IDX = 12,*/ + /*ROCCAT_KOVA_POLLING_RATE_IDX = 13,*/ + ROCCAT_KOVA_FLAGS_IDX = 14, + ROCCAT_KOVA_COLOR_FLOW_IDX = 15, + ROCCAT_KOVA_MODE_IDX = 16, + ROCCAT_KOVA_EFFECT_SPEED_IDX = 17, + ROCCAT_KOVA_PIPE_IDX = 18, + ROCCAT_KOVA_WHEEL_IDX = 22, + ROCCAT_KOVA_CHECKSUM_IDX = 26, +}; + +#define ROCCAT_KOVA_R_OFFSET 1 +#define ROCCAT_KOVA_G_OFFSET 2 +#define ROCCAT_KOVA_B_OFFSET 3 + +#define ROCCAT_KOVA_USE_CUSTOM_COLORS_MASK 0b00110000 +#define ROCCAT_KOVA_LIGHTS_ON_MASK 0b00000011 + +enum +{ + ROCCAT_KOVA_MODE_OFF = 0x00, + ROCCAT_KOVA_MODE_STATIC = 0x01, + ROCCAT_KOVA_MODE_FLASHING = 0x02, + ROCCAT_KOVA_MODE_BREATHING = 0x03, + ROCCAT_KOVA_MODE_COLOR_FLOW = 0xFF, +}; + +class RoccatKovaController +{ +public: + RoccatKovaController(hid_device* dev_handle, char *path, std::string dev_name); + ~RoccatKovaController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + std::string GetVersion(); + + void SetColor(RGBColor color_wheel, + RGBColor color_stripe, + uint8_t mode, + uint8_t speed, + bool color_flow); + +private: + hid_device* dev; + std::string location; + std::string name; + std::string version; + + void SendInitialPacket(); + void FetchProfileData(uint8_t *buf); + void FetchFirmwareVersion(); + + uint16_t GenerateChecksum(uint8_t *buf, size_t length); +}; diff --git a/Controllers/RoccatController/RoccatSenseAimoController/RGBController_RoccatSenseAimo.cpp b/Controllers/RoccatController/RoccatSenseAimoController/RGBController_RoccatSenseAimo.cpp new file mode 100644 index 0000000..6c1a2bf --- /dev/null +++ b/Controllers/RoccatController/RoccatSenseAimoController/RGBController_RoccatSenseAimo.cpp @@ -0,0 +1,196 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatSenseAimo.cpp | +| | +| RGBController for Roccat Sense Aimo | +| | +| Mola19 09 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_RoccatSenseAimo.h" + +/**------------------------------------------------------------------*\ + @name Roccat Sense Aimo Mousepad + @category Mousemat + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRoccatSenseAimoControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_RoccatSenseAimo::RGBController_RoccatSenseAimo(RoccatSenseAimoController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Roccat"; + type = DEVICE_TYPE_MOUSEMAT; + description = "Roccat Sense Aimo Mousepad Device"; + version = controller->GetVersion(); + location = controller->GetLocation(); + serial = controller->GetSerial(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ROCCAT_SENSE_AIMO_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = ROCCAT_SENSE_AIMO_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness = ROCCAT_SENSE_AIMO_BRIGHTNESS_DEFAULT; + Static.brightness_min = ROCCAT_SENSE_AIMO_BRIGHTNESS_MIN; + Static.brightness_max = ROCCAT_SENSE_AIMO_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = ROCCAT_SENSE_AIMO_MODE_WAVE; + Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness = ROCCAT_SENSE_AIMO_BRIGHTNESS_DEFAULT; + Rainbow.brightness_min = ROCCAT_SENSE_AIMO_BRIGHTNESS_MIN; + Rainbow.brightness_max = ROCCAT_SENSE_AIMO_BRIGHTNESS_MAX; + Rainbow.speed = ROCCAT_SENSE_AIMO_SPEED_DEFAULT; + Rainbow.speed_min = ROCCAT_SENSE_AIMO_SPEED_MIN; + Rainbow.speed_max = ROCCAT_SENSE_AIMO_SPEED_MAX; + modes.push_back(Rainbow); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ROCCAT_SENSE_AIMO_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.brightness = ROCCAT_SENSE_AIMO_BRIGHTNESS_DEFAULT; + Breathing.brightness_min = ROCCAT_SENSE_AIMO_BRIGHTNESS_MIN; + Breathing.brightness_max = ROCCAT_SENSE_AIMO_BRIGHTNESS_MAX; + Breathing.speed = ROCCAT_SENSE_AIMO_SPEED_DEFAULT; + Breathing.speed_min = ROCCAT_SENSE_AIMO_SPEED_MIN; + Breathing.speed_max = ROCCAT_SENSE_AIMO_SPEED_MAX; + modes.push_back(Breathing); + + mode Heartbeat; + Heartbeat.name = "Heartbeat"; + Heartbeat.value = ROCCAT_SENSE_AIMO_MODE_HEARTBEAT; + Heartbeat.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED; + Heartbeat.color_mode = MODE_COLORS_PER_LED; + Heartbeat.brightness = ROCCAT_SENSE_AIMO_BRIGHTNESS_DEFAULT; + Heartbeat.brightness_min = ROCCAT_SENSE_AIMO_BRIGHTNESS_MIN; + Heartbeat.brightness_max = ROCCAT_SENSE_AIMO_BRIGHTNESS_MAX; + Heartbeat.speed = ROCCAT_SENSE_AIMO_SPEED_DEFAULT; + Heartbeat.speed_min = ROCCAT_SENSE_AIMO_SPEED_MIN; + Heartbeat.speed_max = ROCCAT_SENSE_AIMO_SPEED_MAX; + modes.push_back(Heartbeat); + + /*---------------------------------------------------------------------*\ + | This is the default mode for software modes, while swarm isn't active | + \*---------------------------------------------------------------------*/ + mode Default; + Default.name = "Default"; + Default.value = ROCCAT_SENSE_AIMO_MODE_DEFAULT; + Default.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS; + Default.color_mode = MODE_COLORS_NONE; + Default.brightness = ROCCAT_SENSE_AIMO_BRIGHTNESS_DEFAULT; + Default.brightness_min = ROCCAT_SENSE_AIMO_BRIGHTNESS_MIN; + Default.brightness_max = ROCCAT_SENSE_AIMO_BRIGHTNESS_MAX; + modes.push_back(Default); + + SetupZones(); + + mode_struct active = controller->GetMode(); + + for(uint32_t i = 0; i < modes.size(); i++) + { + if(modes[i].value == active.mode) + { + active_mode = i; + break; + } + + /*----------------------------------------------*\ + | If no mode was found, select 0th mode (direct) | + \*----------------------------------------------*/ + if(i == modes.size() - 1) + { + active_mode = 0; + } + } + + modes[active_mode].speed = active.speed; + modes[active_mode].brightness = active.brightness; + + colors[0] = active.left; + colors[1] = active.right; +} + +RGBController_RoccatSenseAimo::~RGBController_RoccatSenseAimo() +{ + delete controller; +} + +void RGBController_RoccatSenseAimo::SetupZones() +{ + zone pad; + pad.name = "Mousepad"; + pad.type = ZONE_TYPE_LINEAR; + pad.leds_min = 2; + pad.leds_max = 2; + pad.leds_count = 2; + pad.matrix_map = NULL; + zones.push_back(pad); + + led left_led; + left_led.name = "Mousepad left led"; + leds.push_back(left_led); + + led right_led; + right_led.name = "Mousepad right led"; + leds.push_back(right_led); + + SetupColors(); +} + +void RGBController_RoccatSenseAimo::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatSenseAimo::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == ROCCAT_SENSE_AIMO_MODE_DIRECT) + { + controller->SendDirect(colors); + } + else + { + DeviceUpdateMode(); + } +} + +void RGBController_RoccatSenseAimo::UpdateZoneLEDs(int /*zone_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatSenseAimo::UpdateSingleLED(int /*led_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatSenseAimo::DeviceUpdateMode() +{ + mode selected = modes[active_mode]; + + mode_struct active = controller->GetMode(); + controller->SetMode(active.profile, selected.value, selected.speed, selected.brightness, colors); +} diff --git a/Controllers/RoccatController/RoccatSenseAimoController/RGBController_RoccatSenseAimo.h b/Controllers/RoccatController/RoccatSenseAimoController/RGBController_RoccatSenseAimo.h new file mode 100644 index 0000000..b67f089 --- /dev/null +++ b/Controllers/RoccatController/RoccatSenseAimoController/RGBController_RoccatSenseAimo.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatSenseAimo.h | +| | +| RGBController for Roccat Sense Aimo | +| | +| Mola19 09 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatSenseAimoController.h" + +class RGBController_RoccatSenseAimo : public RGBController +{ +public: + RGBController_RoccatSenseAimo(RoccatSenseAimoController* controller_ptr); + ~RGBController_RoccatSenseAimo(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatSenseAimoController* controller; +}; diff --git a/Controllers/RoccatController/RoccatSenseAimoController/RoccatSenseAimoController.cpp b/Controllers/RoccatController/RoccatSenseAimoController/RoccatSenseAimoController.cpp new file mode 100644 index 0000000..0e2efef --- /dev/null +++ b/Controllers/RoccatController/RoccatSenseAimoController/RoccatSenseAimoController.cpp @@ -0,0 +1,152 @@ +/*---------------------------------------------------------*\ +| RoccatSenseAimoController.cpp | +| | +| Driver for Roccat Sense Aimo | +| | +| Mola19 09 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "RoccatSenseAimoController.h" +#include "StringUtils.h" + +RoccatSenseAimoController::RoccatSenseAimoController(hid_device* dev_handle, char *path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +RoccatSenseAimoController::~RoccatSenseAimoController() +{ + hid_close(dev); +} + +std::string RoccatSenseAimoController::GetLocation() +{ + return("HID: " + location); +} + +std::string RoccatSenseAimoController::GetName() +{ + return(name); +} + +std::string RoccatSenseAimoController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string RoccatSenseAimoController::GetVersion() +{ + uint8_t buf[8] = { 0x01 }; + int return_length = hid_get_feature_report(dev, buf, 5); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Sense Aimo]: Could not fetch mode. HIDAPI Error: %ls", hid_error(dev)); + return std::string("Unknown"); + } + + char version[6]; + snprintf(version, 6, "%2X.%02X", buf[1], buf[2]); + + return std::string(version); +} + +mode_struct RoccatSenseAimoController::GetMode() +{ + uint8_t buf[19] = { 0x02 }; + int return_length = hid_get_feature_report(dev, buf, 19); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Sense Aimo]: Could not fetch mode. HIDAPI Error: %ls", hid_error(dev)); + mode_struct default_mode; + + default_mode.profile = 0; + default_mode.mode = ROCCAT_SENSE_AIMO_MODE_STATIC; + default_mode.speed = 0; + default_mode.brightness = ROCCAT_SENSE_AIMO_BRIGHTNESS_DEFAULT; + default_mode.left = ToRGBColor(0, 0, 0); + default_mode.right = ToRGBColor(0, 0, 0); + + return default_mode; + } + + mode_struct active_mode; + + active_mode.profile = buf[1]; + active_mode.mode = buf[2]; + active_mode.speed = buf[3]; + active_mode.brightness = buf[4]; + active_mode.left = ToRGBColor(buf[6], buf[7], buf[8]); + active_mode.right = ToRGBColor(buf[14], buf[15], buf[16]); + + return active_mode; +} + +void RoccatSenseAimoController::SetMode(uint8_t profile, uint8_t mode, uint8_t speed, uint8_t brightness, std::vector colors) +{ + uint8_t buf[19]; + memset(buf, 0x00, 19); + + buf[0x00] = 0x02; + buf[0x01] = profile; + + for(uint8_t i = 0; i < 2; i++) + { + buf[0x02 + i * 8] = mode; // this device has per led modes + buf[0x03 + i * 8] = speed; + buf[0x04 + i * 8] = brightness; + buf[0x05 + i * 8] = 0x00; + buf[0x06 + i * 8] = RGBGetRValue(colors[i]); + buf[0x07 + i * 8] = RGBGetGValue(colors[i]); + buf[0x08 + i * 8] = RGBGetBValue(colors[i]); + buf[0x09 + i * 8] = 0xFF; // this device uses RGBA, but OpenRGB doesn't allow it, so it is always max + } + + buf[0x12] = 0x00; // this stores the swarm theme and first bit is a flag if custom is active in swarm. No usage outside Swarm + + int return_length = hid_send_feature_report(dev, buf, 19); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Sense Aimo]: Could not send mode. HIDAPI Error: %ls", hid_error(dev)); + } +} + +void RoccatSenseAimoController::SendDirect(std::vector colors) +{ + uint8_t buf[9]; + memset(buf, 0x00, 9); + + buf[0x00] = 0x03; + + for(uint8_t i = 0; i < 2; i++) + { + buf[0x01 + i * 4] = RGBGetRValue(colors[i]); + buf[0x02 + i * 4] = RGBGetGValue(colors[i]); + buf[0x03 + i * 4] = RGBGetBValue(colors[i]); + buf[0x04 + i * 4] = 0xFF; // this device uses RGBA, but OpenRGB doesn't allow it, so it is always max + } + + int return_length = hid_send_feature_report(dev, buf, 9); + + if(return_length == -1) + { + LOG_DEBUG("[Roccat Sense Aimo]: Could not send direct. HIDAPI Error: %ls", hid_error(dev)); + } +} diff --git a/Controllers/RoccatController/RoccatSenseAimoController/RoccatSenseAimoController.h b/Controllers/RoccatController/RoccatSenseAimoController/RoccatSenseAimoController.h new file mode 100644 index 0000000..8943ec0 --- /dev/null +++ b/Controllers/RoccatController/RoccatSenseAimoController/RoccatSenseAimoController.h @@ -0,0 +1,67 @@ +/*---------------------------------------------------------*\ +| RoccatSenseAimoController.h | +| | +| Driver for Roccat Sense Aimo | +| | +| Mola19 09 Aug 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +enum +{ + ROCCAT_SENSE_AIMO_MODE_DIRECT = 0x0B, + ROCCAT_SENSE_AIMO_MODE_STATIC = 0x01, + ROCCAT_SENSE_AIMO_MODE_BREATHING = 0x03, + ROCCAT_SENSE_AIMO_MODE_HEARTBEAT = 0x04, + ROCCAT_SENSE_AIMO_MODE_DEFAULT = 0x09, + ROCCAT_SENSE_AIMO_MODE_WAVE = 0x0A +}; + +enum +{ + ROCCAT_SENSE_AIMO_SPEED_MIN = 0xFF, + ROCCAT_SENSE_AIMO_SPEED_MAX = 0x00, + ROCCAT_SENSE_AIMO_SPEED_DEFAULT = 0x07, + ROCCAT_SENSE_AIMO_BRIGHTNESS_MIN = 0x00, + ROCCAT_SENSE_AIMO_BRIGHTNESS_MAX = 0xFF, + ROCCAT_SENSE_AIMO_BRIGHTNESS_DEFAULT = 0xFF +}; + +struct mode_struct +{ + uint8_t profile; + uint8_t mode; + uint8_t speed; + uint8_t brightness; + RGBColor left; + RGBColor right; +}; + +class RoccatSenseAimoController +{ +public: + RoccatSenseAimoController(hid_device* dev_handle, char *path, std::string dev_name); + ~RoccatSenseAimoController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + std::string GetVersion(); + + mode_struct GetMode(); + + void SendDirect(std::vector colors); + void SetMode(uint8_t profile, uint8_t mode, uint8_t speed, uint8_t brightness, std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/RoccatController/RoccatVulcanKeyboardController/RGBController_RoccatVulcanKeyboard.cpp b/Controllers/RoccatController/RoccatVulcanKeyboardController/RGBController_RoccatVulcanKeyboard.cpp new file mode 100644 index 0000000..2e2511b --- /dev/null +++ b/Controllers/RoccatController/RoccatVulcanKeyboardController/RGBController_RoccatVulcanKeyboard.cpp @@ -0,0 +1,319 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatVulcanKeyboard.cpp | +| | +| RGBController for Roccat Vulcan keyboard | +| | +| Mola19 17 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBControllerKeyNames.h" +#include "RGBController_RoccatVulcanKeyboard.h" + +#define NA 0xFFFFFFFF + +/**------------------------------------------------------------------*\ + @name Roccat Vulcan Keyboard + @category Keyboard + @type USB + @save :robot: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectRoccatVulcanKeyboardControllers + @comment The mode "Default" differs from device to device and + and sometimes also based on which profile you are on. + Often it is very close to the rainbow mode. +\*-------------------------------------------------------------------*/ + +RGBController_RoccatVulcanKeyboard::RGBController_RoccatVulcanKeyboard(RoccatVulcanKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + pid = controller->device_pid; + + controller->InitDeviceInfo(); + + name = controller->GetName(); + vendor = "Roccat"; + type = DEVICE_TYPE_KEYBOARD; + description = "Roccat Vulcan Keyboard Device"; + version = controller->GetDeviceInfo().version; + location = controller->GetLocation(); + serial = controller->GetSerial(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = ROCCAT_VULCAN_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + + if(pid != ROCCAT_VULCAN_120_AIMO_PID && pid != ROCCAT_VULCAN_100_AIMO_PID) + { + Direct.flags |= MODE_FLAG_HAS_BRIGHTNESS; + Direct.brightness_min = ROCCAT_VULCAN_BRIGHTNESS_MIN; + Direct.brightness_max = ROCCAT_VULCAN_BRIGHTNESS_MAX; + Direct.brightness = ROCCAT_VULCAN_BRIGHTNESS_DEFAULT; + } + + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = ROCCAT_VULCAN_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.brightness_min = ROCCAT_VULCAN_BRIGHTNESS_MIN; + Static.brightness_max = ROCCAT_VULCAN_BRIGHTNESS_MAX; + Static.brightness = ROCCAT_VULCAN_BRIGHTNESS_DEFAULT; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = ROCCAT_VULCAN_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Wave.speed_min = ROCCAT_VULCAN_SPEED_MIN; + Wave.speed_max = ROCCAT_VULCAN_SPEED_MAX; + Wave.speed = ROCCAT_VULCAN_SPEED_DEFAULT; + Wave.brightness_min = ROCCAT_VULCAN_BRIGHTNESS_MIN; + Wave.brightness_max = ROCCAT_VULCAN_BRIGHTNESS_MAX; + Wave.brightness = ROCCAT_VULCAN_BRIGHTNESS_DEFAULT; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + + mode Default; + Default.name = "Default"; + Default.value = ROCCAT_VULCAN_MODE_DEFAULT; + Default.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Default.brightness_min = ROCCAT_VULCAN_BRIGHTNESS_MIN; + Default.brightness_max = ROCCAT_VULCAN_BRIGHTNESS_MAX; + Default.brightness = ROCCAT_VULCAN_BRIGHTNESS_DEFAULT; + Default.color_mode = MODE_COLORS_NONE; + modes.push_back(Default); + + SetupZones(); +} + +RGBController_RoccatVulcanKeyboard::~RGBController_RoccatVulcanKeyboard() +{ + delete controller; +} + +void RGBController_RoccatVulcanKeyboard::SetupZones() +{ + std::map * keyboard_ptr; + + switch(pid) + { + case ROCCAT_VULCAN_100_AIMO_PID: + case ROCCAT_VULCAN_120_AIMO_PID: + keyboard_ptr = &RoccatVulcan120AimoLayouts; + break; + case ROCCAT_VULCAN_TKL_PID: + case ROCCAT_VULCAN_TKL_PRO_PID: + case TURTLE_BEACH_VULCAN_II_TKL_PID: + keyboard_ptr = &RoccatVulcanTKLLayouts; + break; + case TURTLE_BEACH_VULCAN_II_TKL_PRO_PID: + keyboard_ptr = &TurtleBeachVulcanIITKLProLayouts; + break; + case ROCCAT_VULCAN_PRO_PID: + case ROCCAT_PYRO_PID: + keyboard_ptr = &RoccatPyroLayouts; + break; + case ROCCAT_VULCAN_II_PID: + case TURTLE_BEACH_VULCAN_II_PID: + keyboard_ptr = &RoccatVulcanIILayouts; + break; + case ROCCAT_VULCAN_II_MAX_PID: + keyboard_ptr = &RoccatVulcanIIMaxLayouts; + break; + case ROCCAT_MAGMA_PID: + case ROCCAT_MAGMA_MINI_PID: + keyboard_ptr = &RoccatMagmaLayouts; + break; + default: + keyboard_ptr = &RoccatVulcan120AimoLayouts; + } + + std::map & keyboard = *keyboard_ptr; + + unsigned char layout; + + switch(controller->GetDeviceInfo().layout_type) + { + case ROCCAT_VULCAN_LAYOUT_DE: + case ROCCAT_VULCAN_LAYOUT_UK: + case ROCCAT_VULCAN_LAYOUT_FR: + layout = ROCCAT_VULCAN_LAYOUT_UK; + break; + case ROCCAT_VULCAN_LAYOUT_US: + default: + layout = ROCCAT_VULCAN_LAYOUT_US; + } + + + /*---------------------------------------------------------*\ + | Determine zone sizes | + | Vulcan II MAX has 108 keyboard keys + 24 secondary LEDs | + | (physically under parent keys) + 16 palm rest LEDs. | + | Secondary LEDs are split into labeled linear zones. | + \*---------------------------------------------------------*/ + int keyboard_size = keyboard[layout].size; + + if(pid == ROCCAT_VULCAN_II_MAX_PID) + { + keyboard_size = 108; + } + + zone keyboard_zone; + keyboard_zone.name = "Keyboard"; + keyboard_zone.type = ZONE_TYPE_MATRIX; + keyboard_zone.leds_min = keyboard_size; + keyboard_zone.leds_max = keyboard_size; + keyboard_zone.leds_count = keyboard_size; + keyboard_zone.matrix_map = new matrix_map_type; + keyboard_zone.matrix_map->height = keyboard[layout].rows; + keyboard_zone.matrix_map->width = keyboard[layout].cols; + keyboard_zone.matrix_map->map = keyboard[layout].matrix_map; + zones.push_back(keyboard_zone); + + if(pid == ROCCAT_VULCAN_II_MAX_PID) + { + zone fkey_ind_zone; + fkey_ind_zone.name = "F-Key Indicators"; + fkey_ind_zone.type = ZONE_TYPE_LINEAR; + fkey_ind_zone.leds_min = 15; + fkey_ind_zone.leds_max = 15; + fkey_ind_zone.leds_count = 15; + fkey_ind_zone.matrix_map = NULL; + zones.push_back(fkey_ind_zone); + + zone nav1_zone; + nav1_zone.name = "Nav Cluster Indicators"; + nav1_zone.type = ZONE_TYPE_LINEAR; + nav1_zone.leds_min = 3; + nav1_zone.leds_max = 3; + nav1_zone.leds_count = 3; + nav1_zone.matrix_map = NULL; + zones.push_back(nav1_zone); + + zone numlock_zone; + numlock_zone.name = "Num Lock Indicator"; + numlock_zone.type = ZONE_TYPE_LINEAR; + numlock_zone.leds_min = 1; + numlock_zone.leds_max = 1; + numlock_zone.leds_count = 1; + numlock_zone.matrix_map = NULL; + zones.push_back(numlock_zone); + + zone nav2_zone; + nav2_zone.name = "Nav Cluster Indicators 2"; + nav2_zone.type = ZONE_TYPE_LINEAR; + nav2_zone.leds_min = 3; + nav2_zone.leds_max = 3; + nav2_zone.leds_count = 3; + nav2_zone.matrix_map = NULL; + zones.push_back(nav2_zone); + + zone caps_zone; + caps_zone.name = "Caps Lock Indicator"; + caps_zone.type = ZONE_TYPE_LINEAR; + caps_zone.leds_min = 1; + caps_zone.leds_max = 1; + caps_zone.leds_count = 1; + caps_zone.matrix_map = NULL; + zones.push_back(caps_zone); + + zone win_zone; + win_zone.name = "Win Key Indicator"; + win_zone.type = ZONE_TYPE_LINEAR; + win_zone.leds_min = 1; + win_zone.leds_max = 1; + win_zone.leds_count = 1; + win_zone.matrix_map = NULL; + zones.push_back(win_zone); + + zone palmrest_zone; + palmrest_zone.name = "Palm Rest"; + palmrest_zone.type = ZONE_TYPE_LINEAR; + palmrest_zone.leds_min = 16; + palmrest_zone.leds_max = 16; + palmrest_zone.leds_count = 16; + palmrest_zone.matrix_map = NULL; + zones.push_back(palmrest_zone); + } + + for(int led_id = 0; led_id < keyboard[layout].size; led_id++) + { + led new_led; + new_led.name = keyboard[layout].led_names[led_id].name; + new_led.value = keyboard[layout].led_names[led_id].id; + leds.push_back(new_led); + } + + SetupColors(); + + /*---------------------------------------------------------*\ + | sends the init packet for the default mode (direct) | + \*---------------------------------------------------------*/ + DeviceUpdateMode(); + DeviceUpdateLEDs(); +} + +void RGBController_RoccatVulcanKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_RoccatVulcanKeyboard::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == ROCCAT_VULCAN_MODE_DIRECT) + { + std::vector led_color_list = {}; + + for(unsigned int i = 0; i < colors.size(); i++) + { + led_color_list.push_back({ leds[i].value, colors[i] }); + } + + controller->SendColors(led_color_list); + } + else + { + DeviceUpdateMode(); + } +} + +void RGBController_RoccatVulcanKeyboard::UpdateZoneLEDs(int /*zone_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatVulcanKeyboard::UpdateSingleLED(int /*led_idx*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_RoccatVulcanKeyboard::DeviceUpdateMode() +{ + std::vector led_color_list = {}; + + if(modes[active_mode].value == ROCCAT_VULCAN_MODE_STATIC) + { + for(unsigned int i = 0; i < colors.size(); i++) + { + led_color_list.push_back({ leds[i].value, colors[i] }); + } + } + + controller->SendMode(modes[active_mode].value, modes[active_mode].speed, modes[active_mode].brightness, led_color_list); + controller->WaitUntilReady(); + + controller->EnableDirect(modes[active_mode].value == ROCCAT_VULCAN_MODE_DIRECT); + controller->WaitUntilReady(); +} diff --git a/Controllers/RoccatController/RoccatVulcanKeyboardController/RGBController_RoccatVulcanKeyboard.h b/Controllers/RoccatController/RoccatVulcanKeyboardController/RGBController_RoccatVulcanKeyboard.h new file mode 100644 index 0000000..09db449 --- /dev/null +++ b/Controllers/RoccatController/RoccatVulcanKeyboardController/RGBController_RoccatVulcanKeyboard.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_RoccatVulcanKeyboard.h | +| | +| RGBController for Roccat Vulcan keyboard | +| | +| Mola19 17 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "RoccatVulcanKeyboardController.h" + +class RGBController_RoccatVulcanKeyboard : public RGBController +{ +public: + RGBController_RoccatVulcanKeyboard(RoccatVulcanKeyboardController* controller_ptr); + ~RGBController_RoccatVulcanKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + RoccatVulcanKeyboardController* controller; + uint16_t pid; +}; diff --git a/Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardController.cpp b/Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardController.cpp new file mode 100644 index 0000000..88192fe --- /dev/null +++ b/Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardController.cpp @@ -0,0 +1,540 @@ +/*---------------------------------------------------------*\ +| RoccatVulcanKeyboardController.cpp | +| | +| Driver for Roccat Vulcan keyboard | +| | +| Mola19 17 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "LogManager.h" +#include "RoccatVulcanKeyboardController.h" +#include "StringUtils.h" + +RoccatVulcanKeyboardController::RoccatVulcanKeyboardController(hid_device* dev_ctrl_handle, hid_device* dev_led_handle, char *path, uint16_t pid, std::string dev_name) +{ + dev_ctrl = dev_ctrl_handle; + dev_led = dev_led_handle; + location = path; + name = dev_name; + device_pid = pid; +} + +RoccatVulcanKeyboardController::~RoccatVulcanKeyboardController() +{ + hid_close(dev_ctrl); + hid_close(dev_led); +} + +std::string RoccatVulcanKeyboardController::GetLocation() +{ + return("HID: " + location); +} + +std::string RoccatVulcanKeyboardController::GetName() +{ + return(name); +} + +std::string RoccatVulcanKeyboardController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev_ctrl, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +device_info RoccatVulcanKeyboardController::InitDeviceInfo() +{ + uint8_t packet_length; + uint8_t report_id; + + switch(device_pid) + { + case ROCCAT_PYRO_PID: + case ROCCAT_MAGMA_PID: + case ROCCAT_MAGMA_MINI_PID: + case ROCCAT_VULCAN_PRO_PID: + case ROCCAT_VULCAN_TKL_PRO_PID: + case ROCCAT_VULCAN_II_PID: + case ROCCAT_VULCAN_II_MAX_PID: + case TURTLE_BEACH_VULCAN_II_PID: + case TURTLE_BEACH_VULCAN_II_TKL_PRO_PID: + packet_length = 9; + report_id = 0x09; + break; + default: + packet_length = 8; + report_id = 0x0F; + } + + uint8_t* buf = new uint8_t[packet_length]; + memset(buf, 0x00, packet_length); + + buf[0] = report_id; + hid_get_feature_report(dev_ctrl, buf, packet_length); + + /*-------------------------------------------------------------*\ + | buf[2] is version e.g. 103 means v1.03 | + \*-------------------------------------------------------------*/ + dev_info.version_major = buf[2] / 100; + dev_info.version_minor = buf[2] % 100; + + char version[5]; + snprintf(version, 5, "%d.%02d", dev_info.version_major, dev_info.version_minor); + dev_info.version = version; + + if(device_pid == ROCCAT_MAGMA_PID || device_pid == ROCCAT_MAGMA_MINI_PID) + { + /*---------------------------------------------------------*\ + | This device doesn't need a layout, | + | because it doesn't have per-led lighting. | + | Taking us layout as placeholder instead | + \*---------------------------------------------------------*/ + dev_info.layout_type = ROCCAT_VULCAN_LAYOUT_US; + } + else + { + dev_info.layout_type = buf[6]; + } + + LOG_DEBUG("[Roccat Vulcan Keyboard]: Detected layout '0x%02X'", buf[6]); + + delete[] buf; + return dev_info; +} + +device_info RoccatVulcanKeyboardController::GetDeviceInfo() +{ + return dev_info; +} + +bool RoccatVulcanKeyboardController::IsBigEndianDirectMode() +{ + return (dev_info.version_major >= 1 && dev_info.version_minor >= 16); +} + +void RoccatVulcanKeyboardController::EnableDirect(bool on_off_switch) +{ + uint8_t* buf; + switch(device_pid) + { + case ROCCAT_PYRO_PID: + case ROCCAT_MAGMA_PID: + case ROCCAT_MAGMA_MINI_PID: + case ROCCAT_VULCAN_PRO_PID: + case ROCCAT_VULCAN_TKL_PRO_PID: + case ROCCAT_VULCAN_II_PID: + case ROCCAT_VULCAN_II_MAX_PID: + case TURTLE_BEACH_VULCAN_II_PID: + case TURTLE_BEACH_VULCAN_II_TKL_PRO_PID: + buf = new uint8_t[5] { 0x0E, 0x05, on_off_switch, 0x00, 0x00 }; + hid_send_feature_report(dev_ctrl, buf, 5); + break; + default: + buf = new uint8_t[3] { 0x15, 0x00, on_off_switch }; + hid_send_feature_report(dev_ctrl, buf, 3); + } + delete[] buf; +} + +void RoccatVulcanKeyboardController::SendColors(std::vector colors) +{ + unsigned short packet_length; + unsigned char column_length; + unsigned char protocol_version; + + switch(device_pid) + { + case ROCCAT_MAGMA_PID: + case ROCCAT_MAGMA_MINI_PID: + packet_length = 64; + column_length = 5; + protocol_version = 2; + break; + case ROCCAT_PYRO_PID: + packet_length = 378; + column_length = 1; + protocol_version = 2; + break; + case ROCCAT_VULCAN_PRO_PID: + case ROCCAT_VULCAN_TKL_PRO_PID: + packet_length = 384; + column_length = 12; + protocol_version = 2; + break; + case ROCCAT_VULCAN_II_PID: + case TURTLE_BEACH_VULCAN_II_PID: + packet_length = 396; + column_length = 1; + protocol_version = 2; + break; + case ROCCAT_VULCAN_II_MAX_PID: + packet_length = 567; + column_length = 1; + protocol_version = 2; + break; + case TURTLE_BEACH_VULCAN_II_TKL_PRO_PID: + packet_length = 320; + column_length = 1; + protocol_version = 2; + break; + default: + packet_length = 436; + column_length = 12; + protocol_version = 1; + } + + unsigned char packet_num = (unsigned char)(ceil((float)packet_length / 64)); + std::vector> bufs(packet_num); + + for(int p = 0; p < packet_num; p++) + { + bufs[p].resize(65); + memset(&bufs[p][0], 0x00, sizeof(bufs[p][0]) * bufs[p].size()); + } + + if(protocol_version > 1) + { + for(unsigned int i = 0; i < packet_num; i++) + { + bufs[i][1] = 0xA1; + bufs[i][2] = i + 1; + } + } + else + { + bufs[0][1] = 0xA1; + bufs[0][2] = 0x01; + } + + unsigned char header_length_first = (packet_length > 255) ? 4 : 3; + + if(header_length_first == 3) + { + bufs[0][3] = (uint8_t)packet_length; + } + else + { + if(IsBigEndianDirectMode()) + { + bufs[0][3] = (packet_length >> 8) & 0xFF; + bufs[0][4] = packet_length & 0xFF; + } + else + { + bufs[0][3] = packet_length & 0xFF; + bufs[0][4] = (packet_length >> 8) & 0xFF; + } + } + + unsigned int data_length_packet = 64 - header_length_first; + + unsigned int hw_leds = 0; + if(device_pid == TURTLE_BEACH_VULCAN_II_TKL_PRO_PID) + { + for(unsigned int i = 0; i < colors.size(); i++) + { + if(colors[i].value > hw_leds) + { + hw_leds = colors[i].value; + } + } + hw_leds += 1; + } + + for(unsigned int i = 0; i < colors.size(); i++) + { + if(device_pid == TURTLE_BEACH_VULCAN_II_TKL_PRO_PID) + { + const unsigned int CHANNEL_OFFSET = hw_leds; + + unsigned int led_index = colors[i].value; + if(led_index >= hw_leds) + { + continue; + } + + unsigned int logical_pos_r = led_index; + unsigned int p_idx_r = logical_pos_r / data_length_packet; + unsigned int b_idx_r = logical_pos_r % data_length_packet; + + if(p_idx_r < bufs.size()) + { + bufs[p_idx_r][b_idx_r + header_length_first + 1] = RGBGetRValue(colors[i].color); + } + + unsigned int logical_pos_g = led_index + CHANNEL_OFFSET; + unsigned int p_idx_g = logical_pos_g / data_length_packet; + unsigned int b_idx_g = logical_pos_g % data_length_packet; + + if(p_idx_g < bufs.size()) + { + bufs[p_idx_g][b_idx_g + header_length_first + 1] = RGBGetGValue(colors[i].color); + } + + unsigned int logical_pos_b = led_index + 2 * CHANNEL_OFFSET; + unsigned int p_idx_b = logical_pos_b / data_length_packet; + unsigned int b_idx_b = logical_pos_b % data_length_packet; + + if(p_idx_b < bufs.size()) + { + bufs[p_idx_b][b_idx_b + header_length_first + 1] = RGBGetBValue(colors[i].color); + } + } + + else + { + int column = (int)(floor(colors[i].value / column_length)); + int row = colors[i].value % column_length; + + if(protocol_version == 1) + { + int offset = column * 3 * column_length + row + header_length_first; + bufs[offset / 64][offset % 64 + 1] = RGBGetRValue(colors[i].color); + offset += column_length; + bufs[offset / 64][offset % 64 + 1] = RGBGetGValue(colors[i].color); + offset += column_length; + bufs[offset / 64][offset % 64 + 1] = RGBGetBValue(colors[i].color); + } + else + { + int offset = column * 3 * column_length + row; + + bufs[offset / data_length_packet][offset % data_length_packet + header_length_first + 1] = RGBGetRValue(colors[i].color); + + offset += column_length; + bufs[offset / data_length_packet][offset % data_length_packet + header_length_first + 1] = RGBGetGValue(colors[i].color); + + offset += column_length; + bufs[offset / data_length_packet][offset % data_length_packet + header_length_first + 1] = RGBGetBValue(colors[i].color); + } + } + } + + for(int p = 0; p < packet_num; p++) + { + hid_write(dev_led, &bufs[p][0], 65); + } + + ClearResponses(); + AwaitResponse(20); +} + +void RoccatVulcanKeyboardController::SendMode(unsigned int mode, unsigned int speed, unsigned int brightness, std::vector colors) +{ + if(speed == 0) speed = ROCCAT_VULCAN_SPEED_DEFAULT; + if(brightness == 0) brightness = ROCCAT_VULCAN_BRIGHTNESS_DEFAULT; + + unsigned short packet_length; + unsigned char protocol_version; + unsigned char column_length; + + switch(device_pid) + { + case ROCCAT_PYRO_PID: + protocol_version = 2; + packet_length = 365; + column_length = 1; + break; + case ROCCAT_MAGMA_PID: + case ROCCAT_MAGMA_MINI_PID: + protocol_version = 2; + packet_length = 26; + column_length = 5; + break; + case ROCCAT_VULCAN_PRO_PID: + case ROCCAT_VULCAN_TKL_PRO_PID: + protocol_version = 2; + packet_length = 371; + column_length = 12; + break; + case ROCCAT_VULCAN_II_PID: + case TURTLE_BEACH_VULCAN_II_PID: + protocol_version = 2; + packet_length = 377; + column_length = 1; + break; + case ROCCAT_VULCAN_II_MAX_PID: + protocol_version = 2; + packet_length = 542; + column_length = 1; + break; + case TURTLE_BEACH_VULCAN_II_TKL_PRO_PID: + packet_length = 284; + column_length = 1; + protocol_version = 2; + break; + default: + protocol_version = 1; + packet_length = 443; + column_length = 12; + } + + + uint8_t* buf = new uint8_t[packet_length]; + memset(buf, 0x00, packet_length); + + unsigned char header_length = (packet_length > 255) ? 2 : 1; + + buf[0] = (protocol_version == 1) ? 0x0D : 0x11; + + if(header_length == 1) + { + buf[1] = (uint8_t)packet_length; + } + else + { + buf[1] = packet_length % 256; + buf[2] = packet_length / 256; + } + + unsigned char offset = header_length + 1; + + buf[0 + offset] = 0x00; + buf[1 + offset] = mode; + buf[2 + offset] = speed; + + if(protocol_version == 1) + { + buf[3 + offset] = 0x00; + buf[4 + offset] = brightness; + buf[5 + offset] = 0x00; + } + else + { + buf[3 + offset] = brightness; + buf[4 + offset] = 0x00; + buf[5 + offset] = 0x00; + } + + if(device_pid == TURTLE_BEACH_VULCAN_II_TKL_PRO_PID) + { + buf[0 + offset] = 0x03; + buf[4 + offset] = 0x0B; + buf[5 + offset] = 0x01; + } + + unsigned int hw_leds = 0; + if(device_pid == TURTLE_BEACH_VULCAN_II_TKL_PRO_PID) + { + for(unsigned int i = 0; i < colors.size(); i++) + { + if(colors[i].value > hw_leds) + { + hw_leds = colors[i].value; + } + } + hw_leds += 1; + } + + for(unsigned int i = 0; i < colors.size(); i++) + { + if(device_pid == TURTLE_BEACH_VULCAN_II_TKL_PRO_PID) + { + const int HEADER_OFFSET = 9; + const unsigned int CHANNEL_OFFSET = hw_leds; + + unsigned int led_index = colors[i].value; + if(led_index >= hw_leds) continue; + + int pos_r = HEADER_OFFSET + led_index; + int pos_g = HEADER_OFFSET + led_index + CHANNEL_OFFSET; + int pos_b = HEADER_OFFSET + led_index + (2 * CHANNEL_OFFSET); + + if (pos_b < packet_length - 2) + { + buf[pos_r] = RGBGetRValue(colors[i].color); + buf[pos_g] = RGBGetGValue(colors[i].color); + buf[pos_b] = RGBGetBValue(colors[i].color); + } + } + else + { + int column = (int)(floor(colors[i].value / column_length)); + int row = colors[i].value % column_length; + int pos = column * 3 * column_length + row + 9; + + if(pos + (2 * column_length) < packet_length - 2) + { + buf[pos] = RGBGetRValue(colors[i].color); + pos += column_length; + buf[pos] = RGBGetGValue(colors[i].color); + pos += column_length; + buf[pos] = RGBGetBValue(colors[i].color); + } + } + } + + unsigned short total = 0; + for(int i = 0; i < packet_length - 2; i++) total += buf[i]; + + buf[packet_length - 2] = total & 0xFF; + buf[packet_length - 1] = total >> 8; + + hid_send_feature_report(dev_ctrl, buf, packet_length); + + delete[] buf; +} + +void RoccatVulcanKeyboardController::WaitUntilReady() +{ + unsigned short packet_length; + + switch(device_pid) + { + case ROCCAT_PYRO_PID: + case ROCCAT_MAGMA_PID: + case ROCCAT_MAGMA_MINI_PID: + case ROCCAT_VULCAN_PRO_PID: + case ROCCAT_VULCAN_TKL_PRO_PID: + packet_length = 4; + break; + default: + packet_length = 3; + } + + uint8_t* buf = new uint8_t[packet_length]; + + buf[0] = 0x04; + + for(unsigned char i = 0; buf[1] != 1 && i < 100; i++) + { + if(i != 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(25)); + } + + hid_get_feature_report(dev_ctrl, buf, packet_length); + } + + delete[] buf; +} + +void RoccatVulcanKeyboardController::AwaitResponse(int ms) +{ + unsigned char usb_buf_out[65]; + hid_read_timeout(dev_led, usb_buf_out, 65, ms); +} + +void RoccatVulcanKeyboardController::ClearResponses() +{ + int result = 1; + unsigned char usb_buf_flush[65]; + while(result > 0) + { + result = hid_read_timeout(dev_led, usb_buf_flush, 65, 0); + } +} diff --git a/Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardController.h b/Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardController.h new file mode 100644 index 0000000..40e8e17 --- /dev/null +++ b/Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardController.h @@ -0,0 +1,105 @@ +/*---------------------------------------------------------*\ +| RoccatVulcanKeyboardController.h | +| | +| Driver for Roccat Vulcan keyboard | +| | +| Mola19 17 Dec 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "RoccatVulcanKeyboardLayouts.h" + +/*--------------------------------------------------------------------------------*\ +| KEYBOARDS | +| This section was used to be enum. | +\*--------------------------------------------------------------------------------*/ +#define ROCCAT_VULCAN_100_AIMO_PID 0x307A +#define ROCCAT_VULCAN_120_AIMO_PID 0x3098 +#define ROCCAT_VULCAN_TKL_PID 0x2FEE +#define ROCCAT_VULCAN_PRO_PID 0x30F7 +#define ROCCAT_VULCAN_TKL_PRO_PID 0x311A +#define ROCCAT_VULCAN_II_PID 0x2F4E +#define ROCCAT_VULCAN_II_MAX_PID 0x2EE2 +#define ROCCAT_PYRO_PID 0x314C +#define ROCCAT_MAGMA_PID 0x3124 +#define ROCCAT_MAGMA_MINI_PID 0x69A0 +#define TURTLE_BEACH_VULCAN_II_PID 0x501B +#define TURTLE_BEACH_VULCAN_II_TKL_PID 0x5023 +#define TURTLE_BEACH_VULCAN_II_TKL_PRO_PID 0x5001 + +enum +{ + ROCCAT_VULCAN_MODE_DIRECT = 0x0B, + ROCCAT_VULCAN_MODE_STATIC = 0x01, + ROCCAT_VULCAN_MODE_WAVE = 0x0A, + /*-------------------------------------------------------------------*\ + | This mode is not a real mode, it's just the default mode when | + | a mode is software generated, but Swarm is inactive, hence it has | + | no id. Unfortunately 0 is refused by some keyboards, so 2 seems | + | like a good choice as it is not used anywhere else | + \*-------------------------------------------------------------------*/ + ROCCAT_VULCAN_MODE_DEFAULT = 0x02, +}; + +enum +{ + ROCCAT_VULCAN_SPEED_MIN = 0x01, + ROCCAT_VULCAN_SPEED_MAX = 0x0B, + ROCCAT_VULCAN_SPEED_DEFAULT = 0x06, + ROCCAT_VULCAN_BRIGHTNESS_MIN = 0x01, + ROCCAT_VULCAN_BRIGHTNESS_MAX = 0x45, + ROCCAT_VULCAN_BRIGHTNESS_DEFAULT = 0x45, +}; + +struct device_info +{ + std::string version; + int version_major; + int version_minor; + int layout_type; +}; + +struct led_color +{ + unsigned int value; + RGBColor color; +}; + +class RoccatVulcanKeyboardController +{ +public: + RoccatVulcanKeyboardController(hid_device* dev_ctrl_handle, hid_device* dev_led_handle, char *path, uint16_t pid, std::string dev_name); + ~RoccatVulcanKeyboardController(); + + std::string GetSerial(); + std::string GetLocation(); + std::string GetName(); + device_info InitDeviceInfo(); + device_info GetDeviceInfo(); + + void EnableDirect(bool on_off_switch); + void SendColors(std::vector colors); + void SendMode(unsigned int mode, unsigned int speed, unsigned int brightness, std::vector colors); + void WaitUntilReady(); + void AwaitResponse(int ms); + void ClearResponses(); + + + uint16_t device_pid; + +private: + hid_device* dev_ctrl; + hid_device* dev_led; + device_info dev_info; + std::string location; + std::string name; + + bool IsBigEndianDirectMode(); +}; diff --git a/Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardLayouts.h b/Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardLayouts.h new file mode 100644 index 0000000..3bb296b --- /dev/null +++ b/Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardLayouts.h @@ -0,0 +1,1387 @@ +/*---------------------------------------------------------*\ +| RoccatVulcanKeyboardLayouts.h | +| | +| Layouts for Roccat Vulcan keyboard | +| | +| Mola19 29 Sep 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBControllerKeyNames.h" +#include "RGBController.h" + +enum +{ + ROCCAT_VULCAN_LAYOUT_US = 0, + ROCCAT_VULCAN_LAYOUT_DE = 1, + ROCCAT_VULCAN_LAYOUT_UK = 2, + ROCCAT_VULCAN_LAYOUT_FR = 3, +}; + +#define NA 0xFFFFFFFF + +struct led_value +{ + const char* name; + unsigned char id; +}; + +struct layout_info +{ + unsigned int* matrix_map; + int size; + int rows; + int cols; + std::vector led_names; +}; + +static unsigned int ROCCAT_VULCAN_120_AIMO_LAYOUT_KEYS_104[6][24] = +{ + { 0, NA, 8, 14, 19, 24, NA, 34, 39, 44, 49, 55, 61, 66, 70, NA, 74, 78, 83, NA, NA, NA, NA, NA }, + { 1, 6, 9, 15, 20, 25, 29, 35, 40, 45, 50, 56, 62, 67, NA, NA, 75, 79, 84, NA, 87, 92, 96, 101 }, + { 2, NA, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, 71, NA, 76, 80, 85, NA, 88, 93, 97, 102 }, + { 3, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, NA, 72, NA, NA, NA, NA, NA, 89, 94, 98, NA }, + { 4, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, NA, 69, NA, NA, NA, 81, NA, NA, 90, 95, 99, 103 }, + { 5, 7, 13, NA, NA, NA, 33, NA, NA, NA, 54, 60, 65, NA, 73, NA, 77, 82, 86, NA, 91, NA, 100, NA } +}; + +static unsigned int ROCCAT_VULCAN_120_AIMO_LAYOUT_KEYS_105[6][24] = +{ + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, 75, 79, 84, NA, NA, NA, NA, NA }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, 76, 80, 85, NA, 88, 93, 97, 102 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, NA, NA, 77, 81, 86, NA, 89, 94, 98, 103 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 70, 73, NA, NA, NA, NA, NA, 90, 95, 99, NA }, + { 4, 7, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, NA, 71, NA, NA, NA, 82, NA, NA, 91, 96, 100, 104 }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, NA, 74, NA, 78, 83, 87, NA, 92, NA, 101, NA } +}; + +static unsigned int ROCCAT_VULCAN_II_LAYOUT_KEYS_105[6][24] = +{ + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, 75, 79, 84, NA, 105, 106, 107, NA }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, 76, 80, 85, NA, 88, 93, 97, 102 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, NA, NA, 77, 81, 86, NA, 89, 94, 98, 103 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 70, 73, NA, NA, NA, NA, NA, 90, 95, 99, NA }, + { 4, 7, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, NA, 71, NA, NA, NA, 82, NA, NA, 91, 96, 100, 104 }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, NA, 74, NA, 78, 83, 87, NA, 92, NA, 101, NA } +}; + +static unsigned int ROCCAT_VULCAN_TKL_LAYOUT_KEYS_104[6][19] = +{ + { 0, NA, 8, 14, 19, 24, NA, 34, 39, 44, 49, 55, 61, 66, 70, NA, 74, NA, NA }, + { 1, 6, 9, 15, 20, 25, 29, 35, 40, 45, 50, 56, 62, 67, NA, NA, 75, 78, 82 }, + { 2, NA, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, 71, NA, 76, 79, 83 }, + { 3, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, NA, 72, NA, NA, NA, NA }, + { 4, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, NA, 69, NA, NA, NA, 80, NA }, + { 5, 7, 13, NA, NA, NA, 33, NA, NA, NA, 54, 60, 65, NA, 73, NA, 77, 81, 84 } +}; + +static unsigned int ROCCAT_VULCAN_TKL_LAYOUT_KEYS_105[6][19] = +{ + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, 75, NA, NA }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, 76, 79, 83 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, NA, NA, 77, 80, 84 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 70, 73, NA, NA, NA, NA }, + { 4, 7, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, NA, 71, NA, NA, NA, 81, NA }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, NA, 74, NA, 78, 82, 85 } +}; + +static unsigned int TURTLE_BEACH_VULCAN_II_TKL_PRO_LAYOUT_KEYS_105[6][19] = +{ + { 0, NA, 1, 2, 3, 4, NA, 5, 6, 7, 8, 9, 10, 11, 12, NA, NA, NA, NA }, + { 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, NA, NA, 79, 80, 81 }, + { 27, NA, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, NA, NA, 82, 83, 84 }, + { 40, NA, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, NA, NA, NA, NA }, + { 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, NA, 66, NA, NA, NA, 75, NA }, + { 67, 68, 69, NA, NA, NA, 70, NA, NA, NA, NA, 71, 72, 73, 74, NA, 76, 77, 78 } +}; + +static unsigned int ROCCAT_MAGMA_LAYOUT_KEYS[1][5] = +{ + { 0, 1, 2, 3, 4 }, +}; + +static std::map RoccatVulcan120AimoLayouts = +{ + { + ROCCAT_VULCAN_LAYOUT_UK, + { + *ROCCAT_VULCAN_120_AIMO_LAYOUT_KEYS_105, + 105, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x06 }, + { KEY_EN_ISO_BACK_SLASH, 0x09 }, + { KEY_EN_LEFT_WINDOWS, 0x0A }, + + { KEY_EN_F1, 0x0B }, + { KEY_EN_2, 0x0C }, + { KEY_EN_Q, 0x07 }, + { KEY_EN_A, 0x08 }, + { KEY_EN_Z, 0x0F }, + { KEY_EN_LEFT_ALT, 0x10 }, + + { KEY_EN_F2, 0x11 }, + { KEY_EN_3, 0x12 }, + { KEY_EN_W, 0x0D }, + { KEY_EN_S, 0x0E }, + { KEY_EN_X, 0x15 }, + + { KEY_EN_F3, 0x17 }, + { KEY_EN_4, 0x18 }, + { KEY_EN_E, 0x13 }, + { KEY_EN_D, 0x14 }, + { KEY_EN_C, 0x1B }, + + { KEY_EN_F4, 0x1C }, + { KEY_EN_5, 0x1D }, + { KEY_EN_R, 0x19 }, + { KEY_EN_F, 0x1A }, + { KEY_EN_V, 0x20 }, + + { KEY_EN_6, 0x21 }, + { KEY_EN_T, 0x1E }, + { KEY_EN_G, 0x1F }, + { KEY_EN_B, 0x24 }, + { KEY_EN_SPACE, 0x25 }, + + { KEY_EN_F5, 0x30 }, + { KEY_EN_7, 0x31 }, + { KEY_EN_Y, 0x22 }, + { KEY_EN_H, 0x23 }, + { KEY_EN_N, 0x34 }, + + { KEY_EN_F6, 0x35 }, + { KEY_EN_8, 0x36 }, + { KEY_EN_U, 0x32 }, + { KEY_EN_J, 0x33 }, + { KEY_EN_M, 0x39 }, + + { KEY_EN_F7, 0x3B }, + { KEY_EN_9, 0x3C }, + { KEY_EN_I, 0x37 }, + { KEY_EN_K, 0x38 }, + { KEY_EN_COMMA, 0x3F }, + + { KEY_EN_F8, 0x41 }, + { KEY_EN_0, 0x42 }, + { KEY_EN_O, 0x3D }, + { KEY_EN_L, 0x3E }, + { KEY_EN_PERIOD, 0x45 }, + { KEY_EN_RIGHT_ALT, 0x46 }, + + { KEY_EN_F9, 0x4E }, + { KEY_EN_MINUS, 0x48 }, + { KEY_EN_P, 0x43 }, + { KEY_EN_SEMICOLON, 0x44 }, + { KEY_EN_FORWARD_SLASH, 0x4B }, + { KEY_EN_RIGHT_FUNCTION, 0x4C }, + + { KEY_EN_F10, 0x54 }, + { KEY_EN_EQUALS, 0x4F }, + { KEY_EN_LEFT_BRACKET, 0x49 }, + { KEY_EN_QUOTE, 0x4A }, + { KEY_EN_MENU, 0x53 }, + + { KEY_EN_F11, 0x55 }, + { KEY_EN_BACKSPACE, 0x57 }, + { KEY_EN_RIGHT_BRACKET, 0x50 }, + { KEY_EN_POUND, 0x60 }, + { KEY_EN_RIGHT_SHIFT, 0x52 }, + + { KEY_EN_F12, 0x56 }, + { KEY_EN_ISO_ENTER, 0x58 }, + { KEY_EN_RIGHT_CONTROL, 0x59 }, + + { KEY_EN_PRINT_SCREEN, 0x63 }, + { KEY_EN_INSERT, 0x64 }, + { KEY_EN_DELETE, 0x65 }, + { KEY_EN_LEFT_ARROW, 0x66 }, + + { KEY_EN_SCROLL_LOCK, 0x67 }, + { KEY_EN_HOME, 0x68 }, + { KEY_EN_END, 0x69 }, + { KEY_EN_UP_ARROW, 0x6A }, + { KEY_EN_DOWN_ARROW, 0x6B }, + + { KEY_EN_PAUSE_BREAK, 0x6C }, + { KEY_EN_PAGE_UP, 0x6D }, + { KEY_EN_PAGE_DOWN, 0x6E }, + { KEY_EN_RIGHT_ARROW, 0x6F }, + + { KEY_EN_NUMPAD_LOCK, 0x71 }, + { KEY_EN_NUMPAD_7, 0x72 }, + { KEY_EN_NUMPAD_4, 0x73 }, + { KEY_EN_NUMPAD_1, 0x74 }, + { KEY_EN_NUMPAD_0, 0x75 }, + + { KEY_EN_NUMPAD_DIVIDE, 0x77 }, + { KEY_EN_NUMPAD_8, 0x78 }, + { KEY_EN_NUMPAD_5, 0x79 }, + { KEY_EN_NUMPAD_2, 0x7A }, + + { KEY_EN_NUMPAD_TIMES, 0x7C }, + { KEY_EN_NUMPAD_9, 0x7D }, + { KEY_EN_NUMPAD_6, 0x7E }, + { KEY_EN_NUMPAD_3, 0x7F }, + + { KEY_EN_NUMPAD_PERIOD, 0x80 }, + { KEY_EN_NUMPAD_MINUS, 0x81 }, + { KEY_EN_NUMPAD_PLUS, 0x82 }, + { KEY_EN_NUMPAD_ENTER, 0x83 } + } + } + }, + { + ROCCAT_VULCAN_LAYOUT_US, + { + *ROCCAT_VULCAN_120_AIMO_LAYOUT_KEYS_104, + 104, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_BACK_TICK, 0x01 }, + { KEY_EN_TAB, 0x02 }, + { KEY_EN_CAPS_LOCK, 0x03 }, + { KEY_EN_LEFT_SHIFT, 0x04 }, + { KEY_EN_LEFT_CONTROL, 0x05 }, + + { KEY_EN_1, 0x06 }, + { KEY_EN_LEFT_WINDOWS, 0x0A }, + + { KEY_EN_F1, 0x0B }, + { KEY_EN_2, 0x0C }, + { KEY_EN_Q, 0x07 }, + { KEY_EN_A, 0x08 }, + { KEY_EN_Z, 0x0F }, + { KEY_EN_LEFT_ALT, 0x10 }, + + { KEY_EN_F2, 0x11 }, + { KEY_EN_3, 0x12 }, + { KEY_EN_W, 0x0D }, + { KEY_EN_S, 0x0E }, + { KEY_EN_X, 0x15 }, + + { KEY_EN_F3, 0x17 }, + { KEY_EN_4, 0x18 }, + { KEY_EN_E, 0x13 }, + { KEY_EN_D, 0x14 }, + { KEY_EN_C, 0x1B }, + + { KEY_EN_F4, 0x1C }, + { KEY_EN_5, 0x1D }, + { KEY_EN_R, 0x19 }, + { KEY_EN_F, 0x1A }, + { KEY_EN_V, 0x20 }, + + { KEY_EN_6, 0x21 }, + { KEY_EN_T, 0x1E }, + { KEY_EN_G, 0x1F }, + { KEY_EN_B, 0x24 }, + { KEY_EN_SPACE, 0x25 }, + + { KEY_EN_F5, 0x30 }, + { KEY_EN_7, 0x31 }, + { KEY_EN_Y, 0x22 }, + { KEY_EN_H, 0x23 }, + { KEY_EN_N, 0x34 }, + + { KEY_EN_F6, 0x35 }, + { KEY_EN_8, 0x36 }, + { KEY_EN_U, 0x32 }, + { KEY_EN_J, 0x33 }, + { KEY_EN_M, 0x39 }, + + { KEY_EN_F7, 0x3B }, + { KEY_EN_9, 0x3C }, + { KEY_EN_I, 0x37 }, + { KEY_EN_K, 0x38 }, + { KEY_EN_COMMA, 0x3F }, + + { KEY_EN_F8, 0x41 }, + { KEY_EN_0, 0x42 }, + { KEY_EN_O, 0x3D }, + { KEY_EN_L, 0x3E }, + { KEY_EN_PERIOD, 0x45 }, + { KEY_EN_RIGHT_ALT, 0x46 }, + + { KEY_EN_F9, 0x4E }, + { KEY_EN_MINUS, 0x48 }, + { KEY_EN_P, 0x43 }, + { KEY_EN_SEMICOLON, 0x44 }, + { KEY_EN_FORWARD_SLASH, 0x4B }, + { KEY_EN_RIGHT_FUNCTION, 0x4C }, + + { KEY_EN_F10, 0x54 }, + { KEY_EN_EQUALS, 0x4F }, + { KEY_EN_LEFT_BRACKET, 0x49 }, + { KEY_EN_QUOTE, 0x4A }, + { KEY_EN_MENU, 0x53 }, + + { KEY_EN_F11, 0x55 }, + { KEY_EN_BACKSPACE, 0x57 }, + { KEY_EN_RIGHT_BRACKET, 0x50 }, + { KEY_EN_RIGHT_SHIFT, 0x52 }, + + { KEY_EN_F12, 0x56 }, + { KEY_EN_ANSI_BACK_SLASH, 0x51 }, + { KEY_EN_ANSI_ENTER, 0x58 }, + { KEY_EN_RIGHT_CONTROL, 0x59 }, + + { KEY_EN_PRINT_SCREEN, 0x63 }, + { KEY_EN_INSERT, 0x64 }, + { KEY_EN_DELETE, 0x65 }, + { KEY_EN_LEFT_ARROW, 0x66 }, + + { KEY_EN_SCROLL_LOCK, 0x67 }, + { KEY_EN_HOME, 0x68 }, + { KEY_EN_END, 0x69 }, + { KEY_EN_UP_ARROW, 0x6A }, + { KEY_EN_DOWN_ARROW, 0x6B }, + + { KEY_EN_PAUSE_BREAK, 0x6C }, + { KEY_EN_PAGE_UP, 0x6D }, + { KEY_EN_PAGE_DOWN, 0x6E }, + { KEY_EN_RIGHT_ARROW, 0x6F }, + + { KEY_EN_NUMPAD_LOCK, 0x71 }, + { KEY_EN_NUMPAD_7, 0x72 }, + { KEY_EN_NUMPAD_4, 0x73 }, + { KEY_EN_NUMPAD_1, 0x74 }, + { KEY_EN_NUMPAD_0, 0x75 }, + + { KEY_EN_NUMPAD_DIVIDE, 0x77 }, + { KEY_EN_NUMPAD_8, 0x78 }, + { KEY_EN_NUMPAD_5, 0x79 }, + { KEY_EN_NUMPAD_2, 0x7A }, + + { KEY_EN_NUMPAD_TIMES, 0x7C }, + { KEY_EN_NUMPAD_9, 0x7D }, + { KEY_EN_NUMPAD_6, 0x7E }, + { KEY_EN_NUMPAD_3, 0x7F }, + + { KEY_EN_NUMPAD_PERIOD, 0x80 }, + { KEY_EN_NUMPAD_MINUS, 0x81 }, + { KEY_EN_NUMPAD_PLUS, 0x82 }, + { KEY_EN_NUMPAD_ENTER, 0x83 } + } + } + }, +}; + +static std::map RoccatPyroLayouts = +{ + { + ROCCAT_VULCAN_LAYOUT_UK, + { + *ROCCAT_VULCAN_120_AIMO_LAYOUT_KEYS_105, + 105, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x02 }, + { KEY_EN_BACK_TICK, 0x03 }, + { KEY_EN_TAB, 0x04 }, + { KEY_EN_CAPS_LOCK, 0x05 }, + { KEY_EN_LEFT_SHIFT, 0x00 }, + { KEY_EN_LEFT_CONTROL, 0x01 }, + + { KEY_EN_1, 0x08 }, + { KEY_EN_ISO_BACK_SLASH, 0x06 }, + { KEY_EN_LEFT_WINDOWS, 0x07 }, + + { KEY_EN_F1, 0x0D }, + { KEY_EN_2, 0x0E }, + { KEY_EN_Q, 0x09 }, + { KEY_EN_A, 0x0A }, + { KEY_EN_Z, 0x0B }, + { KEY_EN_LEFT_ALT, 0x0C }, + + { KEY_EN_F2, 0x14 }, + { KEY_EN_3, 0x15 }, + { KEY_EN_W, 0x0F }, + { KEY_EN_S, 0x10 }, + { KEY_EN_X, 0x11 }, + + { KEY_EN_F3, 0x19 }, + { KEY_EN_4, 0x1A }, + { KEY_EN_E, 0x16 }, + { KEY_EN_D, 0x17 }, + { KEY_EN_C, 0x18 }, + + { KEY_EN_F4, 0x1E }, + { KEY_EN_5, 0x1F }, + { KEY_EN_R, 0x1B }, + { KEY_EN_F, 0x1C }, + { KEY_EN_V, 0x1D }, + + { KEY_EN_6, 0x24 }, + { KEY_EN_T, 0x20 }, + { KEY_EN_G, 0x21 }, + { KEY_EN_B, 0x22 }, + { KEY_EN_SPACE, 0x23 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_7, 0x29 }, + { KEY_EN_Y, 0x25 }, + { KEY_EN_H, 0x26 }, + { KEY_EN_N, 0x27 }, + + { KEY_EN_F6, 0x2F }, + { KEY_EN_8, 0x30 }, + { KEY_EN_U, 0x2A }, + { KEY_EN_J, 0x2B }, + { KEY_EN_M, 0x2C }, + + { KEY_EN_F7, 0x35 }, + { KEY_EN_9, 0x36 }, + { KEY_EN_I, 0x31 }, + { KEY_EN_K, 0x32 }, + { KEY_EN_COMMA, 0x33 }, + + { KEY_EN_F8, 0x3B }, + { KEY_EN_0, 0x3C }, + { KEY_EN_O, 0x37 }, + { KEY_EN_L, 0x38 }, + { KEY_EN_PERIOD, 0x39 }, + { KEY_EN_RIGHT_ALT, 0x3A }, + + { KEY_EN_F9, 0x41 }, + { KEY_EN_MINUS, 0x42 }, + { KEY_EN_P, 0x3D }, + { KEY_EN_SEMICOLON, 0x3E }, + { KEY_EN_FORWARD_SLASH, 0x3F }, + { KEY_EN_RIGHT_FUNCTION, 0x40 }, + + { KEY_EN_F10, 0x47 }, + { KEY_EN_EQUALS, 0x48 }, + { KEY_EN_LEFT_BRACKET, 0x43 }, + { KEY_EN_QUOTE, 0x44 }, + { KEY_EN_MENU, 0x46 }, + + { KEY_EN_F11, 0x4D }, + { KEY_EN_BACKSPACE, 0x50 }, + { KEY_EN_RIGHT_BRACKET, 0x49 }, + { KEY_EN_POUND, 0x4A }, + { KEY_EN_RIGHT_SHIFT, 0x4B }, + + { KEY_EN_F12, 0x4F }, + { KEY_EN_ISO_ENTER, 0x52 }, + { KEY_EN_RIGHT_CONTROL, 0x4C }, + + { KEY_EN_PRINT_SCREEN, 0x53 }, + { KEY_EN_INSERT, 0x54 }, + { KEY_EN_DELETE, 0x55 }, + { KEY_EN_LEFT_ARROW, 0x56 }, + + { KEY_EN_SCROLL_LOCK, 0x57 }, + { KEY_EN_HOME, 0x58 }, + { KEY_EN_END, 0x59 }, + { KEY_EN_UP_ARROW, 0x5A }, + { KEY_EN_DOWN_ARROW, 0x5B }, + + { KEY_EN_PAUSE_BREAK, 0x5C }, + { KEY_EN_PAGE_UP, 0x5D }, + { KEY_EN_PAGE_DOWN, 0x5E }, + { KEY_EN_RIGHT_ARROW, 0x5F }, + + { KEY_EN_NUMPAD_LOCK, 0x61 }, + { KEY_EN_NUMPAD_7, 0x62 }, + { KEY_EN_NUMPAD_4, 0x63 }, + { KEY_EN_NUMPAD_1, 0x64 }, + { KEY_EN_NUMPAD_0, 0x65 }, + + { KEY_EN_NUMPAD_DIVIDE, 0x67 }, + { KEY_EN_NUMPAD_8, 0x68 }, + { KEY_EN_NUMPAD_5, 0x69 }, + { KEY_EN_NUMPAD_2, 0x6A }, + + { KEY_EN_NUMPAD_TIMES, 0x6C }, + { KEY_EN_NUMPAD_9, 0x6D }, + { KEY_EN_NUMPAD_6, 0x6E }, + { KEY_EN_NUMPAD_3, 0x6F }, + { KEY_EN_NUMPAD_PERIOD, 0x70 }, + + { KEY_EN_NUMPAD_MINUS, 0x72 }, + { KEY_EN_NUMPAD_PLUS, 0x73 }, + { KEY_EN_NUMPAD_ENTER, 0x75 } + } + } + }, + { + ROCCAT_VULCAN_LAYOUT_US, + { + *ROCCAT_VULCAN_120_AIMO_LAYOUT_KEYS_104, + 104, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x02 }, + { KEY_EN_BACK_TICK, 0x03 }, + { KEY_EN_TAB, 0x04 }, + { KEY_EN_CAPS_LOCK, 0x05 }, + { KEY_EN_LEFT_SHIFT, 0x00 }, + { KEY_EN_LEFT_CONTROL, 0x01 }, + + { KEY_EN_1, 0x08 }, + { KEY_EN_LEFT_WINDOWS, 0x07 }, + + { KEY_EN_F1, 0x0D }, + { KEY_EN_2, 0x0E }, + { KEY_EN_Q, 0x09 }, + { KEY_EN_A, 0x0A }, + { KEY_EN_Z, 0x0B }, + { KEY_EN_LEFT_ALT, 0x0C }, + + { KEY_EN_F2, 0x14 }, + { KEY_EN_3, 0x15 }, + { KEY_EN_W, 0x0F }, + { KEY_EN_S, 0x10 }, + { KEY_EN_X, 0x11 }, + + { KEY_EN_F3, 0x19 }, + { KEY_EN_4, 0x1A }, + { KEY_EN_E, 0x16 }, + { KEY_EN_D, 0x17 }, + { KEY_EN_C, 0x18 }, + + { KEY_EN_F4, 0x1E }, + { KEY_EN_5, 0x1F }, + { KEY_EN_R, 0x1B }, + { KEY_EN_F, 0x1C }, + { KEY_EN_V, 0x1D }, + + { KEY_EN_6, 0x24 }, + { KEY_EN_T, 0x20 }, + { KEY_EN_G, 0x21 }, + { KEY_EN_B, 0x22 }, + { KEY_EN_SPACE, 0x23 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_7, 0x29 }, + { KEY_EN_Y, 0x25 }, + { KEY_EN_H, 0x26 }, + { KEY_EN_N, 0x27 }, + + { KEY_EN_F6, 0x2F }, + { KEY_EN_8, 0x30 }, + { KEY_EN_U, 0x2A }, + { KEY_EN_J, 0x2B }, + { KEY_EN_M, 0x2C }, + + { KEY_EN_F7, 0x35 }, + { KEY_EN_9, 0x36 }, + { KEY_EN_I, 0x31 }, + { KEY_EN_K, 0x32 }, + { KEY_EN_COMMA, 0x33 }, + + { KEY_EN_F8, 0x3B }, + { KEY_EN_0, 0x3C }, + { KEY_EN_O, 0x37 }, + { KEY_EN_L, 0x38 }, + { KEY_EN_PERIOD, 0x39 }, + { KEY_EN_RIGHT_ALT, 0x3A }, + + { KEY_EN_F9, 0x41 }, + { KEY_EN_MINUS, 0x42 }, + { KEY_EN_P, 0x3D }, + { KEY_EN_SEMICOLON, 0x3E }, + { KEY_EN_FORWARD_SLASH, 0x3F }, + { KEY_EN_RIGHT_FUNCTION, 0x40 }, + + { KEY_EN_F10, 0x47 }, + { KEY_EN_EQUALS, 0x48 }, + { KEY_EN_LEFT_BRACKET, 0x43 }, + { KEY_EN_QUOTE, 0x44 }, + { KEY_EN_MENU, 0x46 }, + + { KEY_EN_F11, 0x4D }, + { KEY_EN_BACKSPACE, 0x50 }, + { KEY_EN_RIGHT_BRACKET, 0x49 }, + { KEY_EN_RIGHT_SHIFT, 0x4B }, + + { KEY_EN_F12, 0x4F }, + { KEY_EN_ANSI_BACK_SLASH, 0x51 }, + { KEY_EN_ANSI_ENTER, 0x52 }, + { KEY_EN_RIGHT_CONTROL, 0x4C }, + + { KEY_EN_PRINT_SCREEN, 0x53 }, + { KEY_EN_INSERT, 0x54 }, + { KEY_EN_DELETE, 0x55 }, + { KEY_EN_LEFT_ARROW, 0x56 }, + + { KEY_EN_SCROLL_LOCK, 0x57 }, + { KEY_EN_HOME, 0x58 }, + { KEY_EN_END, 0x59 }, + { KEY_EN_UP_ARROW, 0x5A }, + { KEY_EN_DOWN_ARROW, 0x5B }, + + { KEY_EN_PAUSE_BREAK, 0x5C }, + { KEY_EN_PAGE_UP, 0x5D }, + { KEY_EN_PAGE_DOWN, 0x5E }, + { KEY_EN_RIGHT_ARROW, 0x5F }, + + { KEY_EN_NUMPAD_LOCK, 0x61 }, + { KEY_EN_NUMPAD_7, 0x62 }, + { KEY_EN_NUMPAD_4, 0x63 }, + { KEY_EN_NUMPAD_1, 0x64 }, + { KEY_EN_NUMPAD_0, 0x65 }, + + { KEY_EN_NUMPAD_DIVIDE, 0x67 }, + { KEY_EN_NUMPAD_8, 0x68 }, + { KEY_EN_NUMPAD_5, 0x69 }, + { KEY_EN_NUMPAD_2, 0x6A }, + + { KEY_EN_NUMPAD_TIMES, 0x6C }, + { KEY_EN_NUMPAD_9, 0x6D }, + { KEY_EN_NUMPAD_6, 0x6E }, + { KEY_EN_NUMPAD_3, 0x6F }, + { KEY_EN_NUMPAD_PERIOD, 0x70 }, + + { KEY_EN_NUMPAD_MINUS, 0x72 }, + { KEY_EN_NUMPAD_PLUS, 0x73 }, + { KEY_EN_NUMPAD_ENTER, 0x75 } + } + } + }, +}; + +static std::map RoccatVulcanIILayouts = +{ + { + ROCCAT_VULCAN_LAYOUT_UK, + { + *ROCCAT_VULCAN_II_LAYOUT_KEYS_105, + 108, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x02 }, + { KEY_EN_BACK_TICK, 0x03 }, + { KEY_EN_TAB, 0x04 }, + { KEY_EN_CAPS_LOCK, 0x05 }, + { KEY_EN_LEFT_SHIFT, 0x00 }, + { KEY_EN_LEFT_CONTROL, 0x01 }, + + { KEY_EN_1, 0x08 }, + { KEY_EN_ISO_BACK_SLASH, 0x06 }, + { KEY_EN_LEFT_WINDOWS, 0x07 }, + + { KEY_EN_F1, 0x0D }, + { KEY_EN_2, 0x0E }, + { KEY_EN_Q, 0x09 }, + { KEY_EN_A, 0x0A }, + { KEY_EN_Z, 0x0B }, + { KEY_EN_LEFT_ALT, 0x0C }, + + { KEY_EN_F2, 0x14 }, + { KEY_EN_3, 0x15 }, + { KEY_EN_W, 0x0F }, + { KEY_EN_S, 0x10 }, + { KEY_EN_X, 0x11 }, + + { KEY_EN_F3, 0x19 }, + { KEY_EN_4, 0x1A }, + { KEY_EN_E, 0x16 }, + { KEY_EN_D, 0x17 }, + { KEY_EN_C, 0x18 }, + + { KEY_EN_F4, 0x1E }, + { KEY_EN_5, 0x1F }, + { KEY_EN_R, 0x1B }, + { KEY_EN_F, 0x1C }, + { KEY_EN_V, 0x1D }, + + { KEY_EN_6, 0x24 }, + { KEY_EN_T, 0x20 }, + { KEY_EN_G, 0x21 }, + { KEY_EN_B, 0x22 }, + { KEY_EN_SPACE, 0x23 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_7, 0x29 }, + { KEY_EN_Y, 0x25 }, + { KEY_EN_H, 0x26 }, + { KEY_EN_N, 0x27 }, + + { KEY_EN_F6, 0x2F }, + { KEY_EN_8, 0x30 }, + { KEY_EN_U, 0x2A }, + { KEY_EN_J, 0x2B }, + { KEY_EN_M, 0x2C }, + + { KEY_EN_F7, 0x35 }, + { KEY_EN_9, 0x36 }, + { KEY_EN_I, 0x31 }, + { KEY_EN_K, 0x32 }, + { KEY_EN_COMMA, 0x33 }, + + { KEY_EN_F8, 0x3B }, + { KEY_EN_0, 0x3C }, + { KEY_EN_O, 0x37 }, + { KEY_EN_L, 0x38 }, + { KEY_EN_PERIOD, 0x39 }, + { KEY_EN_RIGHT_ALT, 0x3A }, + + { KEY_EN_F9, 0x41 }, + { KEY_EN_MINUS, 0x42 }, + { KEY_EN_P, 0x3D }, + { KEY_EN_SEMICOLON, 0x3E }, + { KEY_EN_FORWARD_SLASH, 0x3F }, + { KEY_EN_RIGHT_FUNCTION, 0x40 }, + + { KEY_EN_F10, 0x47 }, + { KEY_EN_EQUALS, 0x48 }, + { KEY_EN_LEFT_BRACKET, 0x43 }, + { KEY_EN_QUOTE, 0x44 }, + { KEY_EN_MENU, 0x46 }, + + { KEY_EN_F11, 0x4D }, + { KEY_EN_BACKSPACE, 0x50 }, + { KEY_EN_RIGHT_BRACKET, 0x49 }, + { KEY_EN_POUND, 0x4A }, + { KEY_EN_RIGHT_SHIFT, 0x4B }, + + { KEY_EN_F12, 0x4F }, + { KEY_EN_ISO_ENTER, 0x52 }, + { KEY_EN_RIGHT_CONTROL, 0x4C }, + + { KEY_EN_PRINT_SCREEN, 0x53 }, + { KEY_EN_INSERT, 0x54 }, + { KEY_EN_DELETE, 0x55 }, + { KEY_EN_LEFT_ARROW, 0x56 }, + + { KEY_EN_SCROLL_LOCK, 0x57 }, + { KEY_EN_HOME, 0x58 }, + { KEY_EN_END, 0x59 }, + { KEY_EN_UP_ARROW, 0x5A }, + { KEY_EN_DOWN_ARROW, 0x5B }, + + { KEY_EN_PAUSE_BREAK, 0x5C }, + { KEY_EN_PAGE_UP, 0x5D }, + { KEY_EN_PAGE_DOWN, 0x5E }, + { KEY_EN_RIGHT_ARROW, 0x5F }, + + { KEY_EN_NUMPAD_LOCK, 0x61 }, + { KEY_EN_NUMPAD_7, 0x62 }, + { KEY_EN_NUMPAD_4, 0x63 }, + { KEY_EN_NUMPAD_1, 0x64 }, + { KEY_EN_NUMPAD_0, 0x65 }, + + { KEY_EN_NUMPAD_DIVIDE, 0x67 }, + { KEY_EN_NUMPAD_8, 0x68 }, + { KEY_EN_NUMPAD_5, 0x69 }, + { KEY_EN_NUMPAD_2, 0x6A }, + + { KEY_EN_NUMPAD_TIMES, 0x6C }, + { KEY_EN_NUMPAD_9, 0x6D }, + { KEY_EN_NUMPAD_6, 0x6E }, + { KEY_EN_NUMPAD_3, 0x6F }, + { KEY_EN_NUMPAD_PERIOD, 0x70 }, + + { KEY_EN_NUMPAD_MINUS, 0x72 }, + { KEY_EN_NUMPAD_PLUS, 0x73 }, + { KEY_EN_NUMPAD_ENTER, 0x75 }, + { KEY_EN_MEDIA_PREVIOUS, 0x76 }, + { KEY_EN_MEDIA_PLAY_PAUSE, 0x78 }, + + { KEY_EN_MEDIA_NEXT, 0x79 } + } + } + }, +}; + +static unsigned int ROCCAT_VULCAN_II_MAX_LAYOUT_KEYS_ANSI[6][24] = +{ + { 0, NA, 9, 15, 20, 25, NA, 35, 40, 45, 50, 56, 62, 67, 72, NA, 75, 79, 84, NA, 105, 106, 107, NA }, + { 1, 6, 10, 16, 21, 26, 30, 36, 41, 46, 51, 57, 63, 68, NA, NA, 76, 80, 85, NA, 88, 93, 97, 102 }, + { 2, NA, 11, 17, 22, 27, 31, 37, 42, 47, 52, 58, 64, 69, 70, NA, 77, 81, 86, NA, 89, 94, 98, 103 }, + { 3, NA, 12, 18, 23, 28, 32, 38, 43, 48, 53, 59, 65, 73, NA, NA, NA, NA, NA, NA, 90, 95, 99, NA }, + { 4, NA, 13, 19, 24, 29, 33, 39, 44, 49, 54, 60, 71, NA, NA, NA, NA, 82, NA, NA, 91, 96, 100, 104 }, + { 5, 8, 14, NA, NA, NA, 34, NA, NA, NA, 55, 61, 66, 74, NA, NA, 78, 83, 87, NA, 92, NA, 101, NA } +}; + +static std::map RoccatVulcanIIMaxLayouts = +{ + { + ROCCAT_VULCAN_LAYOUT_US, + { + *ROCCAT_VULCAN_II_MAX_LAYOUT_KEYS_ANSI, + 148, + 6, + 24, + { + { KEY_EN_ESCAPE, 0x02 }, + { KEY_EN_BACK_TICK, 0x03 }, + { KEY_EN_TAB, 0x04 }, + { KEY_EN_CAPS_LOCK, 0x05 }, + { KEY_EN_LEFT_SHIFT, 0x00 }, + { KEY_EN_LEFT_CONTROL, 0x01 }, + + { KEY_EN_1, 0x08 }, + { KEY_EN_UNUSED, 0x06 }, + { KEY_EN_LEFT_WINDOWS, 0x07 }, + + { KEY_EN_F1, 0x0D }, + { KEY_EN_2, 0x0E }, + { KEY_EN_Q, 0x09 }, + { KEY_EN_A, 0x0A }, + { KEY_EN_Z, 0x0B }, + { KEY_EN_LEFT_ALT, 0x0C }, + + { KEY_EN_F2, 0x14 }, + { KEY_EN_3, 0x15 }, + { KEY_EN_W, 0x0F }, + { KEY_EN_S, 0x10 }, + { KEY_EN_X, 0x11 }, + + { KEY_EN_F3, 0x19 }, + { KEY_EN_4, 0x1A }, + { KEY_EN_E, 0x16 }, + { KEY_EN_D, 0x17 }, + { KEY_EN_C, 0x18 }, + + { KEY_EN_F4, 0x1F }, + { KEY_EN_5, 0x1E }, + { KEY_EN_R, 0x1B }, + { KEY_EN_F, 0x1C }, + { KEY_EN_V, 0x21 }, + + { KEY_EN_6, 0x24 }, + { KEY_EN_T, 0x20 }, + { KEY_EN_G, 0x1D }, + { KEY_EN_B, 0x22 }, + { KEY_EN_SPACE, 0x28 }, + + { KEY_EN_F5, 0x23 }, + { KEY_EN_7, 0x29 }, + { KEY_EN_Y, 0x25 }, + { KEY_EN_H, 0x26 }, + { KEY_EN_N, 0x27 }, + + { KEY_EN_F6, 0x2F }, + { KEY_EN_8, 0x30 }, + { KEY_EN_U, 0x3D }, + { KEY_EN_J, 0x2B }, + { KEY_EN_M, 0x2C }, + + { KEY_EN_F7, 0x35 }, + { KEY_EN_9, 0x36 }, + { KEY_EN_I, 0x31 }, + { KEY_EN_K, 0x32 }, + { KEY_EN_COMMA, 0x33 }, + + { KEY_EN_F8, 0x3B }, + { KEY_EN_0, 0x3C }, + { KEY_EN_O, 0x37 }, + { KEY_EN_L, 0x38 }, + { KEY_EN_PERIOD, 0x39 }, + { KEY_EN_RIGHT_ALT, 0x3A }, + + { KEY_EN_F9, 0x41 }, + { KEY_EN_MINUS, 0x42 }, + { KEY_EN_P, 0x2A }, + { KEY_EN_SEMICOLON, 0x3E }, + { KEY_EN_FORWARD_SLASH, 0x3F }, + { KEY_EN_RIGHT_FUNCTION, 0x40 }, + + { KEY_EN_F10, 0x47 }, + { KEY_EN_EQUALS, 0x48 }, + { KEY_EN_LEFT_BRACKET, 0x43 }, + { KEY_EN_QUOTE, 0x44 }, + { KEY_EN_MENU, 0x46 }, + + { KEY_EN_F11, 0x4D }, + { KEY_EN_BACKSPACE, 0x50 }, + { KEY_EN_RIGHT_BRACKET, 0x49 }, + { KEY_EN_ANSI_BACK_SLASH, 0x51 }, + { KEY_EN_RIGHT_SHIFT, 0x4B }, + + { KEY_EN_F12, 0x4F }, + { KEY_EN_ANSI_ENTER, 0x52 }, + { KEY_EN_RIGHT_CONTROL, 0x4C }, + + { KEY_EN_PRINT_SCREEN, 0x53 }, + { KEY_EN_INSERT, 0x54 }, + { KEY_EN_DELETE, 0x55 }, + { KEY_EN_LEFT_ARROW, 0x56 }, + + { KEY_EN_SCROLL_LOCK, 0x57 }, + { KEY_EN_HOME, 0x58 }, + { KEY_EN_END, 0x59 }, + { KEY_EN_UP_ARROW, 0x5A }, + { KEY_EN_DOWN_ARROW, 0x5B }, + + { KEY_EN_PAUSE_BREAK, 0x5C }, + { KEY_EN_PAGE_UP, 0x5D }, + { KEY_EN_PAGE_DOWN, 0x5E }, + { KEY_EN_RIGHT_ARROW, 0x5F }, + + { KEY_EN_NUMPAD_LOCK, 0x60 }, + { KEY_EN_NUMPAD_7, 0x61 }, + { KEY_EN_NUMPAD_4, 0x62 }, + { KEY_EN_NUMPAD_1, 0x6C }, + { KEY_EN_NUMPAD_0, 0x64 }, + + { KEY_EN_NUMPAD_DIVIDE, 0x65 }, + { KEY_EN_NUMPAD_8, 0x66 }, + { KEY_EN_NUMPAD_5, 0x67 }, + { KEY_EN_NUMPAD_2, 0x68 }, + + { KEY_EN_NUMPAD_TIMES, 0x69 }, + { KEY_EN_NUMPAD_9, 0x6A }, + { KEY_EN_NUMPAD_6, 0x6B }, + { KEY_EN_NUMPAD_3, 0x63 }, + { KEY_EN_NUMPAD_PERIOD, 0x6D }, + + { KEY_EN_NUMPAD_MINUS, 0x6E }, + { KEY_EN_NUMPAD_PLUS, 0x6F }, + { KEY_EN_NUMPAD_ENTER, 0x71 }, + + { KEY_EN_MEDIA_PREVIOUS, 0x72 }, + { KEY_EN_MEDIA_PLAY_PAUSE, 0x73 }, + { KEY_EN_MEDIA_NEXT, 0x74 }, + + /*-------------------------------------------------------------*\ + | F-Key Indicators (secondary LEDs) | + \*-------------------------------------------------------------*/ + { "Key: F1 LED 2", 0x89 }, + { "Key: F2 LED 2", 0x7B }, + { "Key: F3 LED 2", 0x81 }, + { "Key: F4 LED 2", 0x87 }, + { "Key: F5 LED 2", 0x8D }, + { "Key: F6 LED 2", 0x76 }, + { "Key: F7 LED 2", 0x7C }, + { "Key: F8 LED 2", 0x82 }, + { "Key: F9 LED 2", 0x88 }, + { "Key: F10 LED 2", 0x8E }, + { "Key: F11 LED 2", 0x7A }, + { "Key: F12 LED 2", 0x80 }, + { "Key: Print Screen LED 2", 0x86 }, + { "Key: Scroll Lock LED 2", 0x8C }, + { "Key: Pause/Break LED 2", 0x92 }, + + /*-------------------------------------------------------------*\ + | Nav Cluster Indicators (secondary LEDs) | + \*-------------------------------------------------------------*/ + { "Key: Insert LED 2", 0x75 }, + { "Key: Home LED 2", 0x7E }, + { "Key: Page Up LED 2", 0x84 }, + + /*-------------------------------------------------------------*\ + | Num Lock Indicator (secondary LED) | + \*-------------------------------------------------------------*/ + { "Key: Num Lock LED 2", 0x8B }, + + /*-------------------------------------------------------------*\ + | Nav Cluster Indicators 2 (secondary LEDs) | + \*-------------------------------------------------------------*/ + { "Key: Delete LED 2", 0x78 }, + { "Key: End LED 2", 0x90 }, + { "Key: Page Down LED 2", 0x8A }, + + /*-------------------------------------------------------------*\ + | Caps Lock Indicator (secondary LED) | + \*-------------------------------------------------------------*/ + { "Key: Caps Lock LED 2", 0x7D }, + + /*-------------------------------------------------------------*\ + | Win Key Indicator (secondary LED) | + \*-------------------------------------------------------------*/ + { "Key: Left Windows LED 2", 0x83 }, + + /*-------------------------------------------------------------*\ + | Palm Rest LEDs (order approximate - needs verification) | + \*-------------------------------------------------------------*/ + { "Palm Rest LED 1", 0x96 }, + { "Palm Rest LED 2", 0xAD }, + { "Palm Rest LED 3", 0xA7 }, + { "Palm Rest LED 4", 0xA1 }, + { "Palm Rest LED 5", 0x9B }, + { "Palm Rest LED 6", 0x95 }, + { "Palm Rest LED 7", 0xAC }, + { "Palm Rest LED 8", 0xA6 }, + { "Palm Rest LED 9", 0xA0 }, + { "Palm Rest LED 10", 0x9A }, + { "Palm Rest LED 11", 0x94 }, + { "Palm Rest LED 12", 0xAB }, + { "Palm Rest LED 13", 0xA5 }, + { "Palm Rest LED 14", 0x9F }, + { "Palm Rest LED 15", 0x99 }, + { "Palm Rest LED 16", 0x93 } + } + } + }, +}; + +static std::map RoccatVulcanTKLLayouts = +{ + { + ROCCAT_VULCAN_LAYOUT_UK, + { + *ROCCAT_VULCAN_TKL_LAYOUT_KEYS_105, + 86, + 6, + 19, + { + { KEY_EN_ESCAPE, 0x02 }, + { KEY_EN_BACK_TICK, 0x03 }, + { KEY_EN_TAB, 0x04 }, + { KEY_EN_CAPS_LOCK, 0x05 }, + { KEY_EN_LEFT_SHIFT, 0x00 }, + { KEY_EN_LEFT_CONTROL, 0x01 }, + + { KEY_EN_1, 0x08 }, + { KEY_EN_ISO_BACK_SLASH, 0x06 }, + { KEY_EN_LEFT_WINDOWS, 0x07 }, + + { KEY_EN_F1, 0x0D }, + { KEY_EN_2, 0x0E }, + { KEY_EN_Q, 0x09 }, + { KEY_EN_A, 0x0A }, + { KEY_EN_Z, 0x0B }, + { KEY_EN_LEFT_ALT, 0x0C }, + + { KEY_EN_F2, 0x14 }, + { KEY_EN_3, 0x15 }, + { KEY_EN_W, 0x0F }, + { KEY_EN_S, 0x10 }, + { KEY_EN_X, 0x11 }, + + { KEY_EN_F3, 0x19 }, + { KEY_EN_4, 0x1A }, + { KEY_EN_E, 0x16 }, + { KEY_EN_D, 0x17 }, + { KEY_EN_C, 0x18 }, + + { KEY_EN_F4, 0x1E }, + { KEY_EN_5, 0x1F }, + { KEY_EN_R, 0x1B }, + { KEY_EN_F, 0x1C }, + { KEY_EN_V, 0x1D }, + + { KEY_EN_6, 0x24 }, + { KEY_EN_T, 0x20 }, + { KEY_EN_G, 0x21 }, + { KEY_EN_B, 0x22 }, + { KEY_EN_SPACE, 0x23 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_7, 0x29 }, + { KEY_EN_Y, 0x25 }, + { KEY_EN_H, 0x26 }, + { KEY_EN_N, 0x27 }, + + { KEY_EN_F6, 0x2F }, + { KEY_EN_8, 0x30 }, + { KEY_EN_U, 0x2A }, + { KEY_EN_J, 0x2B }, + { KEY_EN_M, 0x2C }, + + { KEY_EN_F7, 0x35 }, + { KEY_EN_9, 0x36 }, + { KEY_EN_I, 0x31 }, + { KEY_EN_K, 0x32 }, + { KEY_EN_COMMA, 0x33 }, + + { KEY_EN_F8, 0x3B }, + { KEY_EN_0, 0x3C }, + { KEY_EN_O, 0x37 }, + { KEY_EN_L, 0x38 }, + { KEY_EN_PERIOD, 0x39 }, + { KEY_EN_RIGHT_ALT, 0x3A }, + + { KEY_EN_F9, 0x41 }, + { KEY_EN_MINUS, 0x42 }, + { KEY_EN_P, 0x3D }, + { KEY_EN_SEMICOLON, 0x3E }, + { KEY_EN_FORWARD_SLASH, 0x3F }, + { KEY_EN_RIGHT_FUNCTION, 0x40 }, + + { KEY_EN_F10, 0x47 }, + { KEY_EN_EQUALS, 0x48 }, + { KEY_EN_LEFT_BRACKET, 0x43 }, + { KEY_EN_QUOTE, 0x44 }, + { KEY_EN_MENU, 0x46 }, + + { KEY_EN_F11, 0x4D }, + { KEY_EN_BACKSPACE, 0x50 }, + { KEY_EN_RIGHT_BRACKET, 0x49 }, + { KEY_EN_POUND, 0x4A }, + { KEY_EN_RIGHT_SHIFT, 0x4B }, + + { KEY_EN_F12, 0x4F }, + { KEY_EN_ISO_ENTER, 0x52 }, + { KEY_EN_RIGHT_CONTROL, 0x4C }, + + { KEY_EN_MEDIA_MUTE, 0x5C }, + { KEY_EN_INSERT, 0x54 }, + { KEY_EN_DELETE, 0x55 }, + { KEY_EN_LEFT_ARROW, 0x56 }, + + { KEY_EN_HOME, 0x58 }, + { KEY_EN_END, 0x59 }, + { KEY_EN_UP_ARROW, 0x5A }, + { KEY_EN_DOWN_ARROW, 0x5B }, + + { KEY_EN_PAGE_UP, 0x5D }, + { KEY_EN_PAGE_DOWN, 0x5E }, + { KEY_EN_RIGHT_ARROW, 0x5F }, + } + } + }, + { + ROCCAT_VULCAN_LAYOUT_US, + { + *ROCCAT_VULCAN_TKL_LAYOUT_KEYS_104, + 85, + 6, + 19, + { + { KEY_EN_ESCAPE, 0x02 }, + { KEY_EN_BACK_TICK, 0x03 }, + { KEY_EN_TAB, 0x04 }, + { KEY_EN_CAPS_LOCK, 0x05 }, + { KEY_EN_LEFT_SHIFT, 0x00 }, + { KEY_EN_LEFT_CONTROL, 0x01 }, + + { KEY_EN_1, 0x08 }, + { KEY_EN_LEFT_WINDOWS, 0x07 }, + + { KEY_EN_F1, 0x0D }, + { KEY_EN_2, 0x0E }, + { KEY_EN_Q, 0x09 }, + { KEY_EN_A, 0x0A }, + { KEY_EN_Z, 0x0B }, + { KEY_EN_LEFT_ALT, 0x0C }, + + { KEY_EN_F2, 0x14 }, + { KEY_EN_3, 0x15 }, + { KEY_EN_W, 0x0F }, + { KEY_EN_S, 0x10 }, + { KEY_EN_X, 0x11 }, + + { KEY_EN_F3, 0x19 }, + { KEY_EN_4, 0x1A }, + { KEY_EN_E, 0x16 }, + { KEY_EN_D, 0x17 }, + { KEY_EN_C, 0x18 }, + + { KEY_EN_F4, 0x1E }, + { KEY_EN_5, 0x1F }, + { KEY_EN_R, 0x1B }, + { KEY_EN_F, 0x1C }, + { KEY_EN_V, 0x1D }, + + { KEY_EN_6, 0x24 }, + { KEY_EN_T, 0x20 }, + { KEY_EN_G, 0x21 }, + { KEY_EN_B, 0x22 }, + { KEY_EN_SPACE, 0x23 }, + + { KEY_EN_F5, 0x28 }, + { KEY_EN_7, 0x29 }, + { KEY_EN_Y, 0x25 }, + { KEY_EN_H, 0x26 }, + { KEY_EN_N, 0x27 }, + + { KEY_EN_F6, 0x2F }, + { KEY_EN_8, 0x30 }, + { KEY_EN_U, 0x2A }, + { KEY_EN_J, 0x2B }, + { KEY_EN_M, 0x2C }, + + { KEY_EN_F7, 0x35 }, + { KEY_EN_9, 0x36 }, + { KEY_EN_I, 0x31 }, + { KEY_EN_K, 0x32 }, + { KEY_EN_COMMA, 0x33 }, + + { KEY_EN_F8, 0x3B }, + { KEY_EN_0, 0x3C }, + { KEY_EN_O, 0x37 }, + { KEY_EN_L, 0x38 }, + { KEY_EN_PERIOD, 0x39 }, + { KEY_EN_RIGHT_ALT, 0x3A }, + + { KEY_EN_F9, 0x41 }, + { KEY_EN_MINUS, 0x42 }, + { KEY_EN_P, 0x3D }, + { KEY_EN_SEMICOLON, 0x3E }, + { KEY_EN_FORWARD_SLASH, 0x3F }, + { KEY_EN_RIGHT_FUNCTION, 0x40 }, + + { KEY_EN_F10, 0x47 }, + { KEY_EN_EQUALS, 0x48 }, + { KEY_EN_LEFT_BRACKET, 0x43 }, + { KEY_EN_QUOTE, 0x44 }, + { KEY_EN_MENU, 0x46 }, + + { KEY_EN_F11, 0x4D }, + { KEY_EN_BACKSPACE, 0x50 }, + { KEY_EN_RIGHT_BRACKET, 0x49 }, + { KEY_EN_RIGHT_SHIFT, 0x4B }, + + { KEY_EN_F12, 0x4F }, + { KEY_EN_ANSI_BACK_SLASH, 0x51 }, // this one is guessed, not tested with ansi layout + { KEY_EN_ANSI_ENTER, 0x52 }, + { KEY_EN_RIGHT_CONTROL, 0x4C }, + + { KEY_EN_MEDIA_MUTE, 0x5C }, + { KEY_EN_INSERT, 0x54 }, + { KEY_EN_DELETE, 0x55 }, + { KEY_EN_LEFT_ARROW, 0x56 }, + + { KEY_EN_HOME, 0x58 }, + { KEY_EN_END, 0x59 }, + { KEY_EN_UP_ARROW, 0x5A }, + { KEY_EN_DOWN_ARROW, 0x5B }, + + { KEY_EN_PAGE_UP, 0x5D }, + { KEY_EN_PAGE_DOWN, 0x5E }, + { KEY_EN_RIGHT_ARROW, 0x5F }, + } + } + }, +}; + +static std::map TurtleBeachVulcanIITKLProLayouts = +{ + { + ROCCAT_VULCAN_LAYOUT_UK, + { + *TURTLE_BEACH_VULCAN_II_TKL_PRO_LAYOUT_KEYS_105, + 85, + 6, + 19, + { + { KEY_EN_ESCAPE, 0x00 }, + { KEY_EN_F1, 0x01 }, + { KEY_EN_F2, 0x02 }, + { KEY_EN_F3, 0x03 }, + { KEY_EN_F4, 0x04 }, + { KEY_EN_F5, 0x05 }, + { KEY_EN_F6, 0x06 }, + { KEY_EN_F7, 0x07 }, + { KEY_EN_F8, 0x08 }, + { KEY_EN_F9, 0x09 }, + { KEY_EN_F10, 0x0A }, + { KEY_EN_F11, 0x0B }, + { KEY_EN_F12, 0x0C }, + + { KEY_EN_BACK_TICK, 0x0D }, + { KEY_EN_1, 0x0E }, + { KEY_EN_2, 0x0F }, + { KEY_EN_3, 0x10 }, + { KEY_EN_4, 0x11 }, + { KEY_EN_5, 0x12 }, + { KEY_EN_6, 0x13 }, + { KEY_EN_7, 0x14 }, + { KEY_EN_8, 0x15 }, + { KEY_EN_9, 0x16 }, + { KEY_EN_0, 0x17 }, + { KEY_EN_MINUS, 0x18 }, + { KEY_EN_EQUALS, 0x19 }, + { KEY_EN_BACKSPACE, 0x1B }, + + { KEY_EN_TAB, 0x1C }, + { KEY_EN_Q, 0x1D }, + { KEY_EN_W, 0x1E }, + { KEY_EN_E, 0x1F }, + { KEY_EN_R, 0x20 }, + { KEY_EN_T, 0x21 }, + { KEY_EN_Y, 0x22 }, + { KEY_EN_U, 0x23 }, + { KEY_EN_I, 0x24 }, + { KEY_EN_O, 0x25 }, + { KEY_EN_P, 0x26 }, + { KEY_EN_LEFT_BRACKET, 0x27 }, + { KEY_EN_RIGHT_BRACKET, 0x28 }, + + { KEY_EN_CAPS_LOCK, 0x2A }, + { KEY_EN_A, 0x2B }, + { KEY_EN_S, 0x2C }, + { KEY_EN_D, 0x2D }, + { KEY_EN_F, 0x2E }, + { KEY_EN_G, 0x2F }, + { KEY_EN_H, 0x30 }, + { KEY_EN_J, 0x31 }, + { KEY_EN_K, 0x32 }, + { KEY_EN_L, 0x33 }, + { KEY_EN_SEMICOLON, 0x34 }, + { KEY_EN_QUOTE, 0x35 }, + { KEY_EN_POUND, 0x36 }, + { KEY_EN_ISO_ENTER, 0x37 }, + + { KEY_EN_LEFT_SHIFT, 0x38 }, + { KEY_EN_ISO_BACK_SLASH, 0x39 }, + { KEY_EN_Y, 0x3A }, + { KEY_EN_X, 0x3B }, + { KEY_EN_C, 0x3C }, + { KEY_EN_V, 0x3D }, + { KEY_EN_B, 0x3E }, + { KEY_EN_N, 0x3F }, + { KEY_EN_M, 0x40 }, + { KEY_EN_COMMA, 0x41 }, + { KEY_EN_PERIOD, 0x42 }, + { KEY_EN_FORWARD_SLASH, 0x43 }, + { KEY_EN_RIGHT_SHIFT, 0x45 }, + + { KEY_EN_LEFT_CONTROL, 0x46 }, + { KEY_EN_LEFT_WINDOWS, 0x47 }, + { KEY_EN_LEFT_ALT, 0x48 }, + { KEY_EN_SPACE, 0x4A }, + { KEY_EN_RIGHT_ALT, 0x4D }, + { KEY_EN_RIGHT_FUNCTION, 0x4E }, + { KEY_EN_MENU, 0x4F }, + { KEY_EN_RIGHT_CONTROL, 0x50 }, + + { KEY_EN_UP_ARROW, 0x51 }, + { KEY_EN_LEFT_ARROW, 0x52 }, + { KEY_EN_DOWN_ARROW, 0x53 }, + { KEY_EN_RIGHT_ARROW, 0x54 }, + + { KEY_EN_INSERT, 0x5A }, + { KEY_EN_HOME, 0x59 }, + { KEY_EN_PAGE_UP, 0x58 }, + { KEY_EN_DELETE, 0x55 }, + { KEY_EN_END, 0x56 }, + { KEY_EN_PAGE_DOWN, 0x57 } + } + } + }, +}; + +static std::map RoccatMagmaLayouts = +{ + { + ROCCAT_VULCAN_LAYOUT_US, + { + *ROCCAT_MAGMA_LAYOUT_KEYS, + 5, + 1, + 5, + { + { "Keyboard LED 1", 0x00 }, + { "Keyboard LED 2", 0x01 }, + { "Keyboard LED 3", 0x02 }, + { "Keyboard LED 4", 0x03 }, + { "Keyboard LED 5", 0x04 }, + } + } + }, +}; diff --git a/Controllers/SRGBmodsController/SRGBmodsControllerDetect.cpp b/Controllers/SRGBmodsController/SRGBmodsControllerDetect.cpp new file mode 100644 index 0000000..3b2dc53 --- /dev/null +++ b/Controllers/SRGBmodsController/SRGBmodsControllerDetect.cpp @@ -0,0 +1,65 @@ +/*---------------------------------------------------------*\ +| SRGBModsControllerDetect.cpp | +| | +| Detector for SRGBmods devices | +| | +| Adam Honse (CalcProgrammer1) 21 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "SRGBmodsLEDControllerV1.h" +#include "SRGBmodsPicoController.h" +#include "RGBController_SRGBmodsLEDControllerV1.h" +#include "RGBController_SRGBmodsPico.h" + +#define SRGBMODS_VID 0x16D0 + +#define SRGBMODS_PICO_PID 0x1123 +#define SRGBMODS_LED_CONTROLLER_V1_PID 0x1205 + +/******************************************************************************************\ +* * +* DetectSRGBmodsControllers * +* * +* Detect devices supported by the SRGBmods driver * +* * +\******************************************************************************************/ + +void DetectSRGBmodsControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + wchar_t product[128]; + hid_get_product_string(dev, product, 128); + + std::wstring product_str(product); + + /*-------------------------------------------------------------------------*\ + | Test the product string in case this USB ID is reused for other Pi Pico | + | projects | + \*-------------------------------------------------------------------------*/ + if(product_str == L"SRGBmods Pico LED Controller" || product_str == L"Pico LED Controller") + { + SRGBmodsPicoController* controller = new SRGBmodsPicoController(dev, info->path, name); + RGBController_SRGBmodsPico* rgb_controller = new RGBController_SRGBmodsPico(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else if(product_str == L"LED Controller v1") + { + SRGBmodsLEDControllerV1* controller = new SRGBmodsLEDControllerV1(dev, info->path, name); + RGBController_SRGBmodsLEDControllerV1* rgb_controller = new RGBController_SRGBmodsLEDControllerV1(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} /* DetectSRGBmodsControllers() */ + +REGISTER_HID_DETECTOR("SRGBmods Pico LED Controller", DetectSRGBmodsControllers, SRGBMODS_VID, SRGBMODS_PICO_PID ); +REGISTER_HID_DETECTOR("SRGBMods LED Controller v1", DetectSRGBmodsControllers, SRGBMODS_VID, SRGBMODS_LED_CONTROLLER_V1_PID); diff --git a/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/RGBController_SRGBmodsLEDControllerV1.cpp b/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/RGBController_SRGBmodsLEDControllerV1.cpp new file mode 100644 index 0000000..181da76 --- /dev/null +++ b/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/RGBController_SRGBmodsLEDControllerV1.cpp @@ -0,0 +1,223 @@ +/*---------------------------------------------------------*\ +| RGBController_SRGBmodsLEDControllerV1.cpp | +| | +| RGBController for SRGBmods LED Controller V1 | +| | +| Adam Honse (CalcProgrammer1) 30 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SRGBmodsLEDControllerV1.h" + +/**------------------------------------------------------------------*\ + @name SRGBmods LED Controller V1 + @category LEDStrip + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectSRGBmodsControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SRGBmodsLEDControllerV1::RGBController_SRGBmodsLEDControllerV1(SRGBmodsLEDControllerV1* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "SRGBmods.net"; + description = "SRGBmods LED Controller V1 Device"; + type = DEVICE_TYPE_LEDSTRIP; + location = controller->GetLocationString(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = SRGBMODS_LED_CONTROLLER_V1_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = SRGBMODS_LED_CONTROLLER_V1_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.color_mode = MODE_COLORS_RANDOM; + Rainbow.brightness_min = 0x00; + Rainbow.brightness_max = 0xFF; + Rainbow.brightness = 0xFF; + Rainbow.speed_min = 0x0A; + Rainbow.speed_max = 0xFF; + Rainbow.speed = 0x7F; + modes.push_back(Rainbow); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = SRGBMODS_LED_CONTROLLER_V1_MODE_BREATHING_MODE_SPECIFIC; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.brightness_min = 0x00; + Breathing.brightness_max = 0xFF; + Breathing.brightness = 0xFF; + Breathing.speed_min = 0x0A; + Breathing.speed_max = 0xFF; + Breathing.speed = 0x7F; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Static; + Static.name = "Static"; + Static.value = SRGBMODS_LED_CONTROLLER_V1_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness_min = 0x00; + Static.brightness_max = 0xFF; + Static.brightness = 0xFF; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + SetupZones(); +} + +RGBController_SRGBmodsLEDControllerV1::~RGBController_SRGBmodsLEDControllerV1() +{ + delete controller; +} + +void RGBController_SRGBmodsLEDControllerV1::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(SRGBMODS_LED_CONTROLLER_V1_NUM_CHANNELS); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for(unsigned int channel_idx = 0; channel_idx < SRGBMODS_LED_CONTROLLER_V1_NUM_CHANNELS; channel_idx++) + { + char ch_idx_string[2]; + snprintf(ch_idx_string, 2, "%d", channel_idx + 1); + + zones[channel_idx].name = "Channel "; + zones[channel_idx].name.append(ch_idx_string); + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + /*-------------------------------------------------*\ + | The maximum number of LEDs per channel is 800 | + | according to https://srgbmods.net/lcv1/ | + \*-------------------------------------------------*/ + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = 800; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + + zones[channel_idx].matrix_map = NULL; + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + char led_idx_string[4]; + snprintf(led_idx_string, 4, "%d", led_ch_idx + 1); + + led new_led; + new_led.name = "LED "; + new_led.name.append(led_idx_string); + + leds.push_back(new_led); + leds_channel.push_back(channel_idx); + } + } + + SetupColors(); +} + +void RGBController_SRGBmodsLEDControllerV1::ResizeZone(int zone, int new_size) +{ + if((size_t)zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_SRGBmodsLEDControllerV1::DeviceUpdateLEDs() +{ + if(modes[active_mode].value == SRGBMODS_LED_CONTROLLER_V1_MODE_DIRECT) + { + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].leds_count > 0) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } + } + } + else + { + DeviceUpdateMode(); + } +} + +void RGBController_SRGBmodsLEDControllerV1::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SRGBmodsLEDControllerV1::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SRGBmodsLEDControllerV1::DeviceUpdateMode() +{ + if(modes[active_mode].value == SRGBMODS_LED_CONTROLLER_V1_MODE_DIRECT) + { + controller->SetDirect(); + + DeviceUpdateLEDs(); + } + else + { + unsigned int value = modes[active_mode].value; + RGBColor color = 0; + + if(modes[active_mode].value == SRGBMODS_LED_CONTROLLER_V1_MODE_BREATHING_MODE_SPECIFIC && modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + value = SRGBMODS_LED_CONTROLLER_V1_MODE_BREATHING_RANDOM; + } + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + color = modes[active_mode].colors[0]; + } + + controller->SetConfiguration(value, modes[active_mode].speed, modes[active_mode].brightness, color); + } +} diff --git a/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/RGBController_SRGBmodsLEDControllerV1.h b/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/RGBController_SRGBmodsLEDControllerV1.h new file mode 100644 index 0000000..5df7027 --- /dev/null +++ b/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/RGBController_SRGBmodsLEDControllerV1.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| RGBController_SRGBmodsLEDControllerV1.h | +| | +| RGBController for SRGBmods LED Controller V1 | +| | +| Adam Honse (CalcProgrammer1) 30 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SRGBmodsLEDControllerV1.h" + +#define SRGBMODS_LED_CONTROLLER_V1_NUM_CHANNELS 1 + +class RGBController_SRGBmodsLEDControllerV1 : public RGBController +{ +public: + RGBController_SRGBmodsLEDControllerV1(SRGBmodsLEDControllerV1* controller_ptr); + ~RGBController_SRGBmodsLEDControllerV1(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SRGBmodsLEDControllerV1* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/SRGBmodsLEDControllerV1.cpp b/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/SRGBmodsLEDControllerV1.cpp new file mode 100644 index 0000000..ccbc316 --- /dev/null +++ b/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/SRGBmodsLEDControllerV1.cpp @@ -0,0 +1,177 @@ +/*---------------------------------------------------------*\ +| SRGBmodsLEDControllerV1.cpp | +| | +| Driver for SRGBmods LED Controller V1 | +| | +| Adam Honse (CalcProgrammer1) 30 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SRGBmodsLEDControllerV1.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +SRGBmodsLEDControllerV1::SRGBmodsLEDControllerV1(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +SRGBmodsLEDControllerV1::~SRGBmodsLEDControllerV1() +{ + hid_close(dev); +} + +std::string SRGBmodsLEDControllerV1::GetLocationString() +{ + return("HID: " + location); +} + +std::string SRGBmodsLEDControllerV1::GetNameString() +{ + return(name); +} + +std::string SRGBmodsLEDControllerV1::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void SRGBmodsLEDControllerV1::SetChannelLEDs(unsigned char /*channel*/, RGBColor* colors, unsigned int num_colors) +{ + /*-----------------------------------------------------*\ + | Determine number of packets to send | + \*-----------------------------------------------------*/ + unsigned int num_packets = (num_colors / 20) + ((num_colors % 20) > 0); + unsigned int color_idx = 0; + + /*-----------------------------------------------------*\ + | Send direct mode packets until all colors sent | + \*-----------------------------------------------------*/ + for(unsigned int packet_idx = 0; packet_idx < num_packets; packet_idx++) + { + unsigned int colors_in_packet = 20; + + if(num_colors - color_idx < colors_in_packet) + { + colors_in_packet = num_colors - color_idx; + } + + SendPacket(packet_idx + 1, num_packets, false, &colors[color_idx], colors_in_packet); + + color_idx += colors_in_packet; + } +} + +void SRGBmodsLEDControllerV1::SetConfiguration(unsigned char mode, unsigned char speed, unsigned char brightness, RGBColor color) +{ + SendConfiguration(0, 1, mode, speed, brightness, color, 0, 0); +} + +void SRGBmodsLEDControllerV1::SetDirect() +{ + /*-----------------------------------------------------*\ + | Disable hardware lighting and color compression | + \*-----------------------------------------------------*/ + SendConfiguration(0, 0, 0, 0, 0, 0, 0, 0); +} + +void SRGBmodsLEDControllerV1::SendPacket + ( + unsigned char this_packet_id, + unsigned char last_packet_id, + bool reset, + RGBColor* colors, + unsigned int num_colors + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Direct Lighting packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; /* hidapi Report ID*/ + usb_buf[0x01] = this_packet_id; /* This Packet ID */ + usb_buf[0x02] = last_packet_id; /* Last Packet ID */ + usb_buf[0x03] = reset; /* Reset Flag */ + usb_buf[0x04] = 0xAA; /* Color update */ + + for(unsigned int color_idx = 0; color_idx < num_colors; color_idx++) + { + usb_buf[0x05 + (color_idx * 3)] = RGBGetRValue(colors[color_idx]); + usb_buf[0x06 + (color_idx * 3)] = RGBGetGValue(colors[color_idx]); + usb_buf[0x07 + (color_idx * 3)] = RGBGetBValue(colors[color_idx]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} + +void SRGBmodsLEDControllerV1::SendConfiguration + ( + bool reset, + unsigned char hw_effect_enable, + unsigned char hw_effect_mode, + unsigned char hw_effect_speed, + unsigned char hw_effect_brightness, + RGBColor hw_effect_color, + unsigned char status_led_enable, + unsigned char color_compression_enable + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Hardware Configuration packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; /* hidapi Report ID*/ + usb_buf[0x01] = 0x00; /* This Packet ID */ + usb_buf[0x02] = 0x00; /* Last Packet ID */ + usb_buf[0x03] = reset; /* Reset Flag */ + usb_buf[0x04] = 0xBB; /* Config update */ + + usb_buf[0x05] = hw_effect_enable; /* HWL_enable */ + usb_buf[0x08] = hw_effect_mode; /* HWL_effectMode */ + usb_buf[0x09] = hw_effect_speed; /* HWL_effectSpeed */ + usb_buf[0x0A] = hw_effect_brightness; + usb_buf[0x0B] = RGBGetRValue(hw_effect_color); + usb_buf[0x0C] = RGBGetGValue(hw_effect_color); + usb_buf[0x0D] = RGBGetBValue(hw_effect_color); + usb_buf[0x0E] = status_led_enable; + usb_buf[0x0F] = color_compression_enable; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + + /*-----------------------------------------------------*\ + | Delay 200ms | + \*-----------------------------------------------------*/ + std::this_thread::sleep_for(200ms); +} diff --git a/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/SRGBmodsLEDControllerV1.h b/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/SRGBmodsLEDControllerV1.h new file mode 100644 index 0000000..4ec1993 --- /dev/null +++ b/Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/SRGBmodsLEDControllerV1.h @@ -0,0 +1,67 @@ +/*---------------------------------------------------------*\ +| SRGBmodsLEDControllerV1.h | +| | +| Driver for SRGBmods LED Controller V1 | +| | +| Adam Honse (CalcProgrammer1) 30 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +enum +{ + SRGBMODS_LED_CONTROLLER_V1_MODE_RAINBOW = 0x01, /* Rainbow wave mode */ + SRGBMODS_LED_CONTROLLER_V1_MODE_BREATHING_RANDOM = 0x02, /* Breathing random mode */ + SRGBMODS_LED_CONTROLLER_V1_MODE_STATIC = 0x03, /* Static mode */ + SRGBMODS_LED_CONTROLLER_V1_MODE_BREATHING_MODE_SPECIFIC = 0x04, /* Breathing mode specific mode */ + SRGBMODS_LED_CONTROLLER_V1_MODE_DIRECT = 0xFF, /* Direct (SW) mode */ +}; + +class SRGBmodsLEDControllerV1 +{ +public: + SRGBmodsLEDControllerV1(hid_device* dev_handle, const char* path, std::string dev_name); + ~SRGBmodsLEDControllerV1(); + + std::string GetLocationString(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors); + void SetConfiguration(unsigned char mode, unsigned char speed, unsigned char brightness, RGBColor color); + void SetDirect(); + +private: + hid_device* dev; + std::string location; + std::string name; + + void SendPacket + ( + unsigned char this_packet_id, + unsigned char last_packet_id, + bool reset, + RGBColor* colors, + unsigned int num_colors + ); + + void SendConfiguration + ( + bool reset, + unsigned char hw_effect_enable, + unsigned char hw_effect_mode, + unsigned char hw_effect_speed, + unsigned char hw_effect_brightness, + RGBColor hw_effect_color, + unsigned char status_led_enable, + unsigned char color_compression_enable + ); +}; diff --git a/Controllers/SRGBmodsController/SRGBmodsPicoController/RGBController_SRGBmodsPico.cpp b/Controllers/SRGBmodsController/SRGBmodsPicoController/RGBController_SRGBmodsPico.cpp new file mode 100644 index 0000000..fbd855c --- /dev/null +++ b/Controllers/SRGBmodsController/SRGBmodsPicoController/RGBController_SRGBmodsPico.cpp @@ -0,0 +1,155 @@ +/*---------------------------------------------------------*\ +| RGBController_SRGBmodsPico.cpp | +| | +| RGBController for SRGBmods Raspberry Pi Pico LED | +| Controller | +| | +| Adam Honse (CalcProgrammer1) 21 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SRGBmodsPico.h" + +/**------------------------------------------------------------------*\ + @name SRGBmods Raspberry Pi Pico LED Controller + @category LEDStrip + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectSRGBmodsControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SRGBmodsPico::RGBController_SRGBmodsPico(SRGBmodsPicoController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "SRGBmods.net"; + description = "SRGBmods Pico LED Controller Device"; + type = DEVICE_TYPE_LEDSTRIP; + location = controller->GetLocationString(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_SRGBmodsPico::~RGBController_SRGBmodsPico() +{ + delete controller; +} + +void RGBController_SRGBmodsPico::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(SRGBMODS_PICO_NUM_CHANNELS); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for(unsigned int channel_idx = 0; channel_idx < SRGBMODS_PICO_NUM_CHANNELS; channel_idx++) + { + char ch_idx_string[2]; + snprintf(ch_idx_string, 2, "%d", channel_idx + 1); + + zones[channel_idx].name = "Channel "; + zones[channel_idx].name.append(ch_idx_string); + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + /*-------------------------------------------------*\ + | The maximum number of LEDs per channel is 512 | + | according to https://srgbmods.net/picoled/ | + \*-------------------------------------------------*/ + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = 512; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + + zones[channel_idx].matrix_map = NULL; + + for(unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + char led_idx_string[4]; + snprintf(led_idx_string, 4, "%d", led_ch_idx + 1); + + led new_led; + new_led.name = "LED "; + new_led.name.append(led_idx_string); + + leds.push_back(new_led); + leds_channel.push_back(channel_idx); + } + } + + SetupColors(); +} + +void RGBController_SRGBmodsPico::ResizeZone(int zone, int new_size) +{ + if((size_t)zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_SRGBmodsPico::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].leds_count > 0) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } + } +} + +void RGBController_SRGBmodsPico::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_SRGBmodsPico::UpdateSingleLED(int led) +{ + unsigned int channel = leds_channel[led]; + + controller->SetChannelLEDs(channel, zones[channel].colors, zones[channel].leds_count); +} + +void RGBController_SRGBmodsPico::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/SRGBmodsController/SRGBmodsPicoController/RGBController_SRGBmodsPico.h b/Controllers/SRGBmodsController/SRGBmodsPicoController/RGBController_SRGBmodsPico.h new file mode 100644 index 0000000..badffae --- /dev/null +++ b/Controllers/SRGBmodsController/SRGBmodsPicoController/RGBController_SRGBmodsPico.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_SRGBmodsPico.h | +| | +| RGBController for SRGBmods Raspberry Pi Pico LED | +| Controller | +| | +| Adam Honse (CalcProgrammer1) 21 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SRGBmodsPicoController.h" + +#define SRGBMODS_PICO_NUM_CHANNELS 2 + +class RGBController_SRGBmodsPico : public RGBController +{ +public: + RGBController_SRGBmodsPico(SRGBmodsPicoController* controller_ptr); + ~RGBController_SRGBmodsPico(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SRGBmodsPicoController* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/SRGBmodsController/SRGBmodsPicoController/SRGBmodsPicoController.cpp b/Controllers/SRGBmodsController/SRGBmodsPicoController/SRGBmodsPicoController.cpp new file mode 100644 index 0000000..aa6e82a --- /dev/null +++ b/Controllers/SRGBmodsController/SRGBmodsPicoController/SRGBmodsPicoController.cpp @@ -0,0 +1,140 @@ +/*---------------------------------------------------------*\ +| SRGBmodsPicoController.cpp | +| | +| Driver for SRGBmods Raspberry Pi Pico LED Controller | +| | +| Adam Honse (CalcProgrammer1) 21 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SRGBmodsPicoController.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; + +SRGBmodsPicoController::SRGBmodsPicoController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + /*-----------------------------------------------------*\ + | The SRGBmods Pico controller requires a packet within | + | 10 seconds of sending the lighting change in order | + | to not revert back into hardware mode. Start a thread| + | to continuously send a keepalive packet every 5s | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&SRGBmodsPicoController::KeepaliveThread, this); +} + +SRGBmodsPicoController::~SRGBmodsPicoController() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + hid_close(dev); +} + +void SRGBmodsPicoController::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_commit_time) > std::chrono::seconds(1)) + { + SendPacket(1, 0, 0, false, NULL, 0); + } + std::this_thread::sleep_for(5s); + } +} + +std::string SRGBmodsPicoController::GetLocationString() +{ + return("HID: " + location); +} + +std::string SRGBmodsPicoController::GetNameString() +{ + return(name); +} + +std::string SRGBmodsPicoController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void SRGBmodsPicoController::SetChannelLEDs(unsigned char channel, RGBColor* colors, unsigned int num_colors) +{ + unsigned int num_packets = (num_colors / 20) + ((num_colors % 20) > 0); + unsigned int color_idx = 0; + + for(unsigned int packet_idx = 0; packet_idx < num_packets; packet_idx++) + { + unsigned int colors_in_packet = 20; + + if(num_colors - color_idx < colors_in_packet) + { + colors_in_packet = num_colors - color_idx; + } + + SendPacket(channel, packet_idx + 1, num_packets, false, &colors[color_idx], colors_in_packet); + + color_idx += colors_in_packet; + } +} + +void SRGBmodsPicoController::SendPacket + ( + unsigned char channel, + unsigned char this_packet_id, + unsigned char last_packet_id, + bool reset, + RGBColor* colors, + unsigned int num_colors + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Update last commit time | + \*-----------------------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); + + /*-----------------------------------------------------*\ + | Set up Firmware Version Request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; /* hidapi Report ID*/ + usb_buf[0x01] = this_packet_id; /* This Packet ID */ + usb_buf[0x02] = reset; /* Reset Flag */ + usb_buf[0x03] = last_packet_id; /* Last Packet ID */ + usb_buf[0x04] = channel + 1; /* Channel (1 or 2)*/ + + for(unsigned int color_idx = 0; color_idx < num_colors; color_idx++) + { + usb_buf[0x05 + (color_idx * 3)] = RGBGetRValue(colors[color_idx]); + usb_buf[0x06 + (color_idx * 3)] = RGBGetGValue(colors[color_idx]); + usb_buf[0x07 + (color_idx * 3)] = RGBGetBValue(colors[color_idx]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); +} diff --git a/Controllers/SRGBmodsController/SRGBmodsPicoController/SRGBmodsPicoController.h b/Controllers/SRGBmodsController/SRGBmodsPicoController/SRGBmodsPicoController.h new file mode 100644 index 0000000..d87e6a2 --- /dev/null +++ b/Controllers/SRGBmodsController/SRGBmodsPicoController/SRGBmodsPicoController.h @@ -0,0 +1,49 @@ +/*---------------------------------------------------------*\ +| SRGBmodsPicoController.h | +| | +| Driver for SRGBmods Raspberry Pi Pico LED Controller | +| | +| Adam Honse (CalcProgrammer1) 21 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +class SRGBmodsPicoController +{ +public: + SRGBmodsPicoController(hid_device* dev_handle, const char* path, std::string dev_name); + ~SRGBmodsPicoController(); + + std::string GetLocationString(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors); + + void KeepaliveThread(); +private: + hid_device* dev; + std::string location; + std::string name; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_commit_time; + + void SendPacket + ( + unsigned char channel, + unsigned char this_packet_id, + unsigned char last_packet_id, + bool reset, + RGBColor* colors, + unsigned int num_colors + ); +}; diff --git a/Controllers/SapphireGPUController/SapphireGPUControllerDetect.cpp b/Controllers/SapphireGPUController/SapphireGPUControllerDetect.cpp new file mode 100644 index 0000000..75fd3df --- /dev/null +++ b/Controllers/SapphireGPUController/SapphireGPUControllerDetect.cpp @@ -0,0 +1,109 @@ +/*---------------------------------------------------------*\ +| SapphireGPUControllerDetect.cpp | +| | +| Detector for Sapphire Nitro Glow | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "SapphireNitroGlowV1Controller.h" +#include "SapphireNitroGlowV3Controller.h" +#include "RGBController_SapphireNitroGlowV1.h" +#include "RGBController_SapphireNitroGlowV3.h" +#include "i2c_amd_gpu.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/*-----------------------------------------------------*\ +| I2C Addresses for Sapphire Nitro Glow RGB | +\*-----------------------------------------------------*/ +enum +{ + SAPPHIRE_NITRO_GLOW_V1_ADDR = 0x55, + SAPPHIRE_NITRO_GLOW_V3_ADDR = 0x28, +}; + +/******************************************************************************************\ +* * +* TestForSapphireGPUController * +* * +* Tests the given address to see if an Sapphire controller exists there. First * +* does a byte read to test for a response * +* * +\******************************************************************************************/ + +bool TestForSapphireGPUController(i2c_smbus_interface* bus, unsigned char address) +{ + if(bus->pci_vendor == AMD_GPU_VEN && !is_amd_gpu_i2c_bus(bus)) + { + return false; + } + + //Read a byte to test for presence + return bus->i2c_smbus_read_byte(address) >= 0; +} /* TestForSapphireGPUController() */ + +/******************************************************************************************\ +* * +* DetectSapphireGPUControllers * +* * +* Detect Sapphire GPU controllers on the enumerated I2C buses. * +* * +\******************************************************************************************/ + +void DetectSapphireV1Controllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(TestForSapphireGPUController(bus, i2c_addr)) + { + SapphireNitroGlowV1Controller* new_sapphire_gpu = new SapphireNitroGlowV1Controller(bus, i2c_addr, name); + RGBController_SapphireNitroGlowV1* new_controller = new RGBController_SapphireNitroGlowV1(new_sapphire_gpu); + + ResourceManager::get()->RegisterRGBController(new_controller); + } +} /* DetectSapphireV1Controllers() */ + +void DetectSapphireV3Controllers(i2c_smbus_interface* bus, uint8_t i2c_addr, const std::string& name) +{ + if(TestForSapphireGPUController(bus, i2c_addr)) + { + SapphireNitroGlowV3Controller* new_sapphire_gpu = new SapphireNitroGlowV3Controller(bus, i2c_addr, name); + RGBController_SapphireNitroGlowV3* new_controller = new RGBController_SapphireNitroGlowV3(new_sapphire_gpu); + + ResourceManager::get()->RegisterRGBController(new_controller); + } +} /* DetectSapphireV3Controllers() */ + +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 470/480 Nitro+", DetectSapphireV1Controllers, AMD_GPU_VEN, AMD_POLARIS_DEV, SAPPHIRE_LEGACY_SUB_VEN, SAPPHIRE_LEGACY_POLARIS_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V1_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 570/580/590 Nitro+", DetectSapphireV1Controllers, AMD_GPU_VEN, AMD_POLARIS_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_POLARIS_NITRO_PLUS_SUB_DEV1, SAPPHIRE_NITRO_GLOW_V1_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 570/580/590 Nitro+", DetectSapphireV1Controllers, AMD_GPU_VEN, AMD_POLARIS_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_POLARIS_NITRO_PLUS_SUB_DEV2, SAPPHIRE_NITRO_GLOW_V1_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 580 Nitro+ (2048SP)", DetectSapphireV1Controllers, AMD_GPU_VEN, AMD_POLARIS20XL_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_POLARIS_NITRO_PLUS_SUB_DEV1, SAPPHIRE_NITRO_GLOW_V1_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX Vega 56/64 Nitro+", DetectSapphireV1Controllers, AMD_GPU_VEN, AMD_VEGA10_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_VEGA10_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V1_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 5500 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI14_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI14_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 5700 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI10_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI10_NITRO_PLUS_SUB_DEV1, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 5700 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI10_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI10_NITRO_PLUS_SUB_DEV2, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 5700 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI10_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI10_NITRO_PLUS_SUB_DEV3, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6600 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI23_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI23_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6650 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI23_DEV1, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI23_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6700 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI22_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI22_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6750 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI22_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI22_NITRO_PLUS_SUB_DEV1, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6800 Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI21_NITRO_PLUS_SUB_DEV3, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6800 XT Nitro+ SE", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI21_NITRO_PLUS_SUB_DEV1, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6800 XT/6900 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI21_NITRO_PLUS_SUB_DEV2, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6900 XT Nitro+ SE", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI21_6900XT_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6900 XT Toxic", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI21_DEV2, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI21_TOXIC_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6900 XT Toxic", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI21_6900XT_TOXIC_AC_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6900 XT Toxic Limited Edition", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI21_DEV1, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI21_TOXIC_SUB_DEV1, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6950 XT Toxic", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI21_DEV3, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI21_6950XT_TOXIC_AC_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6950 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI21_DEV3, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI21_6950XT_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 6950 XT Nitro+ Pure", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI21_DEV3, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI21_6950XT_NITRO_PLUS_PURE_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 7700 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI32_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI32_7700XT_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 7800 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI32_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI32_7800XT_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 7900 GRE Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI31_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI31_GRE_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 7900 XTX Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI31_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI31_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 9060 XT Pure", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI44_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI44_PURE_XT_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 9060 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI48_DEV1, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI48_NITRO_PLUS_SUB_DEV1, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 9070 Pure", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI48_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI48_PURE_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 9070 XT Nitro+", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI48_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI48_NITRO_PLUS_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); +REGISTER_I2C_PCI_DETECTOR("Sapphire Radeon RX 9070 XT Pure", DetectSapphireV3Controllers, AMD_GPU_VEN, AMD_NAVI48_DEV, SAPPHIRE_SUB_VEN, SAPPHIRE_NAVI48_PURE_XT_SUB_DEV, SAPPHIRE_NITRO_GLOW_V3_ADDR); diff --git a/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/RGBController_SapphireNitroGlowV1.cpp b/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/RGBController_SapphireNitroGlowV1.cpp new file mode 100644 index 0000000..fe375ef --- /dev/null +++ b/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/RGBController_SapphireNitroGlowV1.cpp @@ -0,0 +1,202 @@ +/*---------------------------------------------------------*\ +| RGBController_SapphireNitroGlowV1.cpp | +| | +| RGBController for Sapphire Nitro Glow V1 | +| | +| Adam Honse (CalcProgrammer1) 15 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SapphireNitroGlowV1.h" + +/**------------------------------------------------------------------*\ + @name Sapphire Nitro Glow v1 + @category GPU + @type I2C + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectSapphireV1Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SapphireNitroGlowV1::RGBController_SapphireNitroGlowV1(SapphireNitroGlowV1Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Sapphire"; + description = "Sapphire Nitro Glow V1 Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Sapphire; + Sapphire.name = "Sapphire Blue"; + Sapphire.value = SAPPHIRE_NITRO_GLOW_V1_MODE_SAPPHIRE_BLUE; + Sapphire.flags = MODE_FLAG_HAS_BRIGHTNESS; + Sapphire.color_mode = MODE_COLORS_NONE; + Sapphire.brightness_min = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MIN; + Sapphire.brightness_max = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX; + Sapphire.brightness = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX; + modes.push_back(Sapphire); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = SAPPHIRE_NITRO_GLOW_V1_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.brightness_min = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MIN; + Rainbow.brightness_max = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX; + Rainbow.brightness = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX; + modes.push_back(Rainbow); + + mode Temperature; + Temperature.name = "PCB Temperature"; + Temperature.value = SAPPHIRE_NITRO_GLOW_V1_MODE_BOARD_TEMPERATURE; + Temperature.flags = MODE_FLAG_HAS_BRIGHTNESS; + Temperature.color_mode = MODE_COLORS_NONE; + Temperature.brightness_min = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MIN; + Temperature.brightness_max = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX; + Temperature.brightness = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX; + modes.push_back(Temperature); + + mode FanSpeed; + FanSpeed.name = "Fan Speed"; + FanSpeed.value = SAPPHIRE_NITRO_GLOW_V1_MODE_FAN_SPEED; + FanSpeed.flags = MODE_FLAG_HAS_BRIGHTNESS; + FanSpeed.color_mode = MODE_COLORS_NONE; + FanSpeed.brightness_min = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MIN; + FanSpeed.brightness_max = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX; + FanSpeed.brightness = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX; + modes.push_back(FanSpeed); + + mode Static; + Static.name = "Static"; + Static.value = SAPPHIRE_NITRO_GLOW_V1_MODE_CUSTOM; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MIN; + Static.brightness_max = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX; + Static.brightness = SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.value = SAPPHIRE_NITRO_GLOW_V1_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + + ReadConfiguration(); +} + +RGBController_SapphireNitroGlowV1::~RGBController_SapphireNitroGlowV1() +{ + delete controller; +} + +void RGBController_SapphireNitroGlowV1::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has one LED, so create a single zone and | + | LED for it | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = "GPU Zone"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = "GPU LED"; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zones.push_back(*new_zone); + + SetupColors(); +} + +void RGBController_SapphireNitroGlowV1::ReadConfiguration() +{ + colors[0] = ToRGBColor( + controller->GetRed(), + controller->GetGreen(), + controller->GetBlue() + ); + + switch(controller->GetMode()) + { + case SAPPHIRE_NITRO_GLOW_V1_MODE_SAPPHIRE_BLUE: + active_mode = 0; + break; + + case SAPPHIRE_NITRO_GLOW_V1_MODE_RAINBOW: + active_mode = 1; + break; + + case SAPPHIRE_NITRO_GLOW_V1_MODE_BOARD_TEMPERATURE: + active_mode = 2; + break; + + case SAPPHIRE_NITRO_GLOW_V1_MODE_FAN_SPEED: + active_mode = 3; + break; + + case SAPPHIRE_NITRO_GLOW_V1_MODE_CUSTOM: + active_mode = 4; + break; + + case SAPPHIRE_NITRO_GLOW_V1_MODE_OFF: + active_mode = 5; + break; + + default: + active_mode = 0; + break; + } + + modes[(unsigned int)active_mode].brightness = controller->GetBrightness(); +} + +void RGBController_SapphireNitroGlowV1::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SapphireNitroGlowV1::DeviceUpdateLEDs() +{ + RGBColor color = colors[0]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetColor(red, grn, blu); +} + +void RGBController_SapphireNitroGlowV1::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SapphireNitroGlowV1::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SapphireNitroGlowV1::DeviceUpdateMode() +{ + controller->SetMode((unsigned char)modes[(unsigned int)active_mode].value); + controller->SetBrightness((unsigned char)modes[(unsigned int)active_mode].brightness); +} diff --git a/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/RGBController_SapphireNitroGlowV1.h b/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/RGBController_SapphireNitroGlowV1.h new file mode 100644 index 0000000..56604e1 --- /dev/null +++ b/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/RGBController_SapphireNitroGlowV1.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_SapphireNitroGlowV1.h | +| | +| RGBController for Sapphire Nitro Glow V1 | +| | +| Adam Honse (CalcProgrammer1) 15 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SapphireNitroGlowV1Controller.h" + +class RGBController_SapphireNitroGlowV1 : public RGBController +{ +public: + RGBController_SapphireNitroGlowV1(SapphireNitroGlowV1Controller* controller_ptr); + ~RGBController_SapphireNitroGlowV1(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SapphireNitroGlowV1Controller* controller; + + void ReadConfiguration(); +}; diff --git a/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/SapphireNitroGlowV1Controller.cpp b/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/SapphireNitroGlowV1Controller.cpp new file mode 100644 index 0000000..8814142 --- /dev/null +++ b/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/SapphireNitroGlowV1Controller.cpp @@ -0,0 +1,81 @@ +/*---------------------------------------------------------*\ +| SapphireNitroGlowV1Controller.cpp | +| | +| Driver for Sapphire Nitro Glow V1 | +| | +| Adam Honse (CalcProgrammer1) 15 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "SapphireNitroGlowV1Controller.h" + +SapphireNitroGlowV1Controller::SapphireNitroGlowV1Controller(i2c_smbus_interface* bus, sapphire_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +SapphireNitroGlowV1Controller::~SapphireNitroGlowV1Controller() +{ + +} + +std::string SapphireNitroGlowV1Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string SapphireNitroGlowV1Controller::GetDeviceName() +{ + return(name); +} + +unsigned char SapphireNitroGlowV1Controller::GetRed() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V1_REG_RED)); +} + +unsigned char SapphireNitroGlowV1Controller::GetGreen() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V1_REG_GREEN)); +} + +unsigned char SapphireNitroGlowV1Controller::GetBlue() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V1_REG_BLUE)); +} + +void SapphireNitroGlowV1Controller::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V1_REG_RED, red); + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V1_REG_GREEN, green); + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V1_REG_BLUE, blue); +} + +unsigned char SapphireNitroGlowV1Controller::GetMode() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V1_REG_MODE)); +} + +void SapphireNitroGlowV1Controller::SetMode(unsigned char mode) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V1_REG_MODE, mode); +} + +unsigned char SapphireNitroGlowV1Controller::GetBrightness() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V1_REG_BRIGHTNESS)); +} + +void SapphireNitroGlowV1Controller::SetBrightness(unsigned char brightness) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V1_REG_BRIGHTNESS, brightness); +} diff --git a/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/SapphireNitroGlowV1Controller.h b/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/SapphireNitroGlowV1Controller.h new file mode 100644 index 0000000..12e6bee --- /dev/null +++ b/Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/SapphireNitroGlowV1Controller.h @@ -0,0 +1,66 @@ +/*---------------------------------------------------------*\ +| SapphireNitroGlowV1Controller.h | +| | +| Driver for Sapphire Nitro Glow V1 | +| | +| Adam Honse (CalcProgrammer1) 15 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +#define SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MIN 2; +#define SAPPHITE_NITRO_GLOW_V1_BRIGHTNESS_MAX 0; + +typedef unsigned char sapphire_dev_id; + +enum +{ + SAPPHIRE_NITRO_GLOW_V1_REG_MODE = 0x00, + SAPPHIRE_NITRO_GLOW_V1_REG_BRIGHTNESS = 0x01, + SAPPHIRE_NITRO_GLOW_V1_REG_RED = 0x03, + SAPPHIRE_NITRO_GLOW_V1_REG_GREEN = 0x04, + SAPPHIRE_NITRO_GLOW_V1_REG_BLUE = 0x05, +}; + +enum +{ + SAPPHIRE_NITRO_GLOW_V1_MODE_SAPPHIRE_BLUE = 0x00, + SAPPHIRE_NITRO_GLOW_V1_MODE_RAINBOW = 0x01, + SAPPHIRE_NITRO_GLOW_V1_MODE_BOARD_TEMPERATURE = 0x02, + SAPPHIRE_NITRO_GLOW_V1_MODE_FAN_SPEED = 0x03, + SAPPHIRE_NITRO_GLOW_V1_MODE_CUSTOM = 0x04, + SAPPHIRE_NITRO_GLOW_V1_MODE_OFF = 0x05, +}; + +class SapphireNitroGlowV1Controller +{ +public: + SapphireNitroGlowV1Controller(i2c_smbus_interface* bus, sapphire_dev_id dev, std::string dev_name); + ~SapphireNitroGlowV1Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + unsigned char GetRed(); + unsigned char GetGreen(); + unsigned char GetBlue(); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + + unsigned char GetMode(); + void SetMode(unsigned char mode); + + unsigned char GetBrightness(); + void SetBrightness(unsigned char brightness); + +private: + i2c_smbus_interface* bus; + sapphire_dev_id dev; + std::string name; +}; diff --git a/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/RGBController_SapphireNitroGlowV3.cpp b/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/RGBController_SapphireNitroGlowV3.cpp new file mode 100644 index 0000000..f9c168e --- /dev/null +++ b/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/RGBController_SapphireNitroGlowV3.cpp @@ -0,0 +1,253 @@ +/*---------------------------------------------------------*\ +| RGBController_SapphireNitroGlowV3.cpp | +| | +| RGBController for Sapphire Nitro Glow V3 | +| | +| K900 03 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SapphireNitroGlowV3.h" + +/**------------------------------------------------------------------*\ + @name Sapphire Nitro Glow v3 + @category GPU + @type I2C + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectSapphireV3Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SapphireNitroGlowV3::RGBController_SapphireNitroGlowV3(SapphireNitroGlowV3Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Sapphire"; + description = "Sapphire Nitro Glow V3 Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Static; + Static.name = "Static"; + Static.value = SAPPHIRE_NITRO_GLOW_V3_MODE_CUSTOM; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = SAPPHIRE_NITRO_GLOW_V3_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED; + Rainbow.speed_min = 10; + Rainbow.speed_max = 250; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Runway; + Runway.name = "Runway"; + Runway.value = SAPPHIRE_NITRO_GLOW_V3_MODE_RUNWAY; + Runway.flags = MODE_FLAG_HAS_SPEED; + Runway.speed_min = 5; + Runway.speed_max = 50; + Runway.color_mode = MODE_COLORS_NONE; + modes.push_back(Runway); + + mode ColorCycle; + ColorCycle.name = "Spectrum Cycle"; + ColorCycle.value = SAPPHIRE_NITRO_GLOW_V3_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED; + ColorCycle.speed_min = 30; + ColorCycle.speed_max = 1; + ColorCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(ColorCycle); + + mode Serial; + Serial.name = "Serial"; + Serial.value = SAPPHIRE_NITRO_GLOW_V3_MODE_SERIAL; + Serial.flags = MODE_FLAG_HAS_SPEED; + Serial.speed_min = 255; + Serial.speed_max = 5; + Serial.color_mode = MODE_COLORS_NONE; + modes.push_back(Serial); + + mode External; + External.name = "External Control"; + External.value = SAPPHIRE_NITRO_GLOW_V3_MODE_EXTERNAL_CONTROL; + External.flags = 0; + External.color_mode = MODE_COLORS_NONE; + modes.push_back(External); + + mode Off; + Off.name = "Off"; + Off.value = SAPPHIRE_NITRO_GLOW_V3_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); + + ReadConfiguration(); +} + +RGBController_SapphireNitroGlowV3::~RGBController_SapphireNitroGlowV3() +{ + delete controller; +} + +void RGBController_SapphireNitroGlowV3::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has one LED, so create a single zone and | + | LED for it | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = "GPU Zone"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = "GPU LED"; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zones.push_back(*new_zone); + + SetupColors(); +} + +void RGBController_SapphireNitroGlowV3::ReadConfiguration() +{ + modes[1].speed = controller->GetRainbowAnimationSpeed(); + modes[2].speed = controller->GetRunwayAnimationSpeed(); + modes[3].speed = controller->GetColorCycleAnimationSpeed(); + modes[4].speed = controller->GetSerialAnimationSpeed(); + + colors[0] = ToRGBColor( + controller->GetRed(), + controller->GetBlue(), + controller->GetGreen() + ); + + if(controller->GetExternalControl()) + { + active_mode = 5; + return; + } + + switch(controller->GetMode()) + { + case SAPPHIRE_NITRO_GLOW_V3_MODE_CUSTOM: + active_mode = 0; + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_RAINBOW: + active_mode = 1; + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_RUNWAY: + active_mode = 2; + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_COLOR_CYCLE: + active_mode = 3; + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_SERIAL: + active_mode = 4; + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_OFF: + active_mode = 6; + colors[0] = ToRGBColor(0, 0, 0); + break; + + default: + active_mode = 0; + break; + } +} + +void RGBController_SapphireNitroGlowV3::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SapphireNitroGlowV3::DeviceUpdateLEDs() +{ + RGBColor color = colors[0]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + controller->SetColor(red, grn, blu); +} + +void RGBController_SapphireNitroGlowV3::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SapphireNitroGlowV3::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SapphireNitroGlowV3::DeviceUpdateMode() +{ + auto mode = modes[active_mode]; + + switch(mode.value) + { + case SAPPHIRE_NITRO_GLOW_V3_MODE_CUSTOM: + controller->SetExternalControl(false); + controller->SetMode(mode.value); + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_RAINBOW: + controller->SetExternalControl(false); + controller->SetRainbowAnimationSpeed(mode.speed); + controller->SetMode(mode.value); + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_RUNWAY: + controller->SetExternalControl(false); + controller->SetRunwayAnimationSpeed(mode.speed); + controller->SetMode(mode.value); + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_COLOR_CYCLE: + controller->SetExternalControl(false); + controller->SetColorCycleAnimationSpeed(mode.speed); + controller->SetMode(mode.value); + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_SERIAL: + controller->SetExternalControl(false); + controller->SetSerialAnimationSpeed(mode.speed); + controller->SetMode(mode.value); + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_EXTERNAL_CONTROL: + controller->SetExternalControl(true); + break; + + case SAPPHIRE_NITRO_GLOW_V3_MODE_OFF: + controller->SetExternalControl(false); + controller->SetColor(0, 0, 0); + controller->SetMode(mode.value); + break; + } +} diff --git a/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/RGBController_SapphireNitroGlowV3.h b/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/RGBController_SapphireNitroGlowV3.h new file mode 100644 index 0000000..9554afc --- /dev/null +++ b/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/RGBController_SapphireNitroGlowV3.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_SapphireNitroGlowV3.h | +| | +| RGBController for Sapphire Nitro Glow V3 | +| | +| K900 03 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SapphireNitroGlowV3Controller.h" + +class RGBController_SapphireNitroGlowV3 : public RGBController +{ +public: + RGBController_SapphireNitroGlowV3(SapphireNitroGlowV3Controller* controller_ptr); + ~RGBController_SapphireNitroGlowV3(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SapphireNitroGlowV3Controller* controller; + + void ReadConfiguration(); +}; diff --git a/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/SapphireNitroGlowV3Controller.cpp b/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/SapphireNitroGlowV3Controller.cpp new file mode 100644 index 0000000..bddd2b2 --- /dev/null +++ b/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/SapphireNitroGlowV3Controller.cpp @@ -0,0 +1,141 @@ +/*---------------------------------------------------------*\ +| SapphireNitroGlowV3Controller.cpp | +| | +| Driver for Sapphire Nitro Glow V3 | +| | +| K900 03 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "SapphireNitroGlowV3Controller.h" + +SapphireNitroGlowV3Controller::SapphireNitroGlowV3Controller(i2c_smbus_interface* bus, sapphire_dev_id dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +SapphireNitroGlowV3Controller::~SapphireNitroGlowV3Controller() +{ + +} + +std::string SapphireNitroGlowV3Controller::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string SapphireNitroGlowV3Controller::GetDeviceName() +{ + return(name); +} + +unsigned char SapphireNitroGlowV3Controller::GetRed() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_RED)); +} + +unsigned char SapphireNitroGlowV3Controller::GetGreen() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_GREEN)); +} + +unsigned char SapphireNitroGlowV3Controller::GetBlue() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_BLUE)); +} + +void SapphireNitroGlowV3Controller::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_RED, red); + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_GREEN, green); + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_BLUE, blue); +} + +unsigned char SapphireNitroGlowV3Controller::GetMode() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_MODE)); +} + +void SapphireNitroGlowV3Controller::SetMode(unsigned char mode) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_MODE, mode); +} + +bool SapphireNitroGlowV3Controller::GetExternalControl() +{ + return((bool)bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_EXTERNAL_CONTROL)); +} + +void SapphireNitroGlowV3Controller::SetExternalControl(bool enabled) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_EXTERNAL_CONTROL, (unsigned char)enabled); +} + +unsigned char SapphireNitroGlowV3Controller::GetBrightness() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_BRIGHTNESS)); +} + +void SapphireNitroGlowV3Controller::SetBrightness(unsigned char brightness) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_BRIGHTNESS, brightness); +} + +unsigned char SapphireNitroGlowV3Controller::GetRainbowAnimationSpeed() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_RAINBOW_ANIMATION_SPEED)); +} + +void SapphireNitroGlowV3Controller::SetRainbowAnimationSpeed(unsigned char speed) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_RAINBOW_ANIMATION_SPEED, speed); +} + +unsigned char SapphireNitroGlowV3Controller::GetRunwayAnimationSpeed() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_RUNWAY_ANIMATION_SPEED)); +} + +void SapphireNitroGlowV3Controller::SetRunwayAnimationSpeed(unsigned char speed) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_RUNWAY_ANIMATION_SPEED, speed); +} + +unsigned char SapphireNitroGlowV3Controller::GetRunwayAnimationRepeatCount() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_RUNWAY_ANIMATION_REPEAT_COUNT)); +} + +void SapphireNitroGlowV3Controller::SetRunwayAnimationRepeatCount(unsigned char count) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_RUNWAY_ANIMATION_REPEAT_COUNT, count); +} + +unsigned char SapphireNitroGlowV3Controller::GetColorCycleAnimationSpeed() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_COLOR_CYCLE_ANIMATION_SPEED)); +} + +void SapphireNitroGlowV3Controller::SetColorCycleAnimationSpeed(unsigned char speed) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_COLOR_CYCLE_ANIMATION_SPEED, speed); +} + +unsigned char SapphireNitroGlowV3Controller::GetSerialAnimationSpeed() +{ + return(bus->i2c_smbus_read_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_SERIAL_ANIMATION_SPEED)); +} + +void SapphireNitroGlowV3Controller::SetSerialAnimationSpeed(unsigned char speed) +{ + bus->i2c_smbus_write_byte_data(dev, SAPPHIRE_NITRO_GLOW_V3_REG_SERIAL_ANIMATION_SPEED, speed); +} diff --git a/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/SapphireNitroGlowV3Controller.h b/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/SapphireNitroGlowV3Controller.h new file mode 100644 index 0000000..4ff16db --- /dev/null +++ b/Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/SapphireNitroGlowV3Controller.h @@ -0,0 +1,91 @@ +/*---------------------------------------------------------*\ +| SapphireNitroGlowV3Controller.h | +| | +| Driver for Sapphire Nitro Glow V3 | +| | +| K900 03 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef unsigned char sapphire_dev_id; + +enum +{ + SAPPHIRE_NITRO_GLOW_V3_REG_MODE = 0x10, + SAPPHIRE_NITRO_GLOW_V3_REG_EXTERNAL_CONTROL = 0x0F, + SAPPHIRE_NITRO_GLOW_V3_REG_BRIGHTNESS = 0x3E, + SAPPHIRE_NITRO_GLOW_V3_REG_RUNWAY_ANIMATION_SPEED = 0x11, + SAPPHIRE_NITRO_GLOW_V3_REG_RUNWAY_ANIMATION_REPEAT_COUNT = 0x12, + SAPPHIRE_NITRO_GLOW_V3_REG_COLOR_CYCLE_ANIMATION_SPEED = 0x13, + SAPPHIRE_NITRO_GLOW_V3_REG_RAINBOW_ANIMATION_SPEED = 0x15, + SAPPHIRE_NITRO_GLOW_V3_REG_SERIAL_ANIMATION_SPEED = 0x16, + SAPPHIRE_NITRO_GLOW_V3_REG_MUSIC_VOLUME = 0x29, + SAPPHIRE_NITRO_GLOW_V3_REG_RED = 0x1A, + SAPPHIRE_NITRO_GLOW_V3_REG_GREEN = 0x1B, + SAPPHIRE_NITRO_GLOW_V3_REG_BLUE = 0x1C, +}; + +enum +{ + SAPPHIRE_NITRO_GLOW_V3_MODE_RAINBOW = 0x00, + SAPPHIRE_NITRO_GLOW_V3_MODE_RUNWAY = 0x01, + SAPPHIRE_NITRO_GLOW_V3_MODE_COLOR_CYCLE = 0x02, + SAPPHIRE_NITRO_GLOW_V3_MODE_SERIAL = 0x03, + SAPPHIRE_NITRO_GLOW_V3_MODE_SAPPHIRE_BLUE = 0x04, + SAPPHIRE_NITRO_GLOW_V3_MODE_AUDIO_VISUALIZATION = 0x05, + SAPPHIRE_NITRO_GLOW_V3_MODE_CUSTOM = 0x06, + SAPPHIRE_NITRO_GLOW_V3_MODE_OFF = 0x07, + SAPPHIRE_NITRO_GLOW_V3_MODE_EXTERNAL_CONTROL = 0xFF, +}; + +class SapphireNitroGlowV3Controller +{ +public: + SapphireNitroGlowV3Controller(i2c_smbus_interface* bus, sapphire_dev_id dev, std::string dev_name); + ~SapphireNitroGlowV3Controller(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + unsigned char GetRed(); + unsigned char GetGreen(); + unsigned char GetBlue(); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + + unsigned char GetMode(); + void SetMode(unsigned char mode); + + bool GetExternalControl(); + void SetExternalControl(bool enabled); + + unsigned char GetBrightness(); + void SetBrightness(unsigned char brightness); + + unsigned char GetRainbowAnimationSpeed(); + void SetRainbowAnimationSpeed(unsigned char speed); + + unsigned char GetRunwayAnimationSpeed(); + void SetRunwayAnimationSpeed(unsigned char speed); + + unsigned char GetRunwayAnimationRepeatCount(); + void SetRunwayAnimationRepeatCount(unsigned char count); + + unsigned char GetColorCycleAnimationSpeed(); + void SetColorCycleAnimationSpeed(unsigned char speed); + + unsigned char GetSerialAnimationSpeed(); + void SetSerialAnimationSpeed(unsigned char speed); + +private: + i2c_smbus_interface* bus; + sapphire_dev_id dev; + std::string name; +}; diff --git a/Controllers/SayoDeviceController/RGBController_SayoDevice.cpp b/Controllers/SayoDeviceController/RGBController_SayoDevice.cpp new file mode 100644 index 0000000..296e472 --- /dev/null +++ b/Controllers/SayoDeviceController/RGBController_SayoDevice.cpp @@ -0,0 +1,169 @@ +/*---------------------------------------------------------*\ +| RGBController_SayoDevice.cpp | +| | +| Controller for Sayo Devices | +| | +| Richard Harris 24 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SayoDevice.h" +#include "SayoDeviceController.h" +#include "LogManager.h" + +/**--------------------------------------------------------------------*\ + @name SayoDevice E1 + @category Keyboard + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectSayoDeviceController + @comment +\*---------------------------------------------------------------------*/ + +RGBController_SayoDevice::RGBController_SayoDevice(SayoDeviceController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "SayoDevice"; + description = "SayoDevice E1 Knob"; + type = DEVICE_TYPE_KEYBOARD; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = SAYO_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + /*-----------------------------------------------------*\ + | Breathing - pulses a single color | + \*-----------------------------------------------------*/ + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = SAYO_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_RANDOM_COLOR; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = 0; + Breathing.speed_max = 3; + Breathing.speed = 1; + modes.push_back(Breathing); + + /*-----------------------------------------------------*\ + | Wave - fades through multiple colors | + \*-----------------------------------------------------*/ + mode Wave; + Wave.name = "Wave"; + Wave.value = SAYO_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_RANDOM_COLOR; + Wave.color_mode = MODE_COLORS_NONE; + Wave.speed_min = 0; + Wave.speed_max = 3; + Wave.speed = 1; + modes.push_back(Wave); + + /*-----------------------------------------------------*\ + | Switch - alternates multiple colors | + \*-----------------------------------------------------*/ + mode Switch; + Switch.name = "Switch"; + Switch.value = SAYO_MODE_SWITCH; + Switch.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_RANDOM_COLOR; + Switch.color_mode = MODE_COLORS_NONE; + Switch.speed_min = 0; + Switch.speed_max = 3; + Switch.speed = 1; + modes.push_back(Switch); + + /*-----------------------------------------------------*\ + | Blink - blinks on and off | + \*-----------------------------------------------------*/ + mode Blink; + Blink.name = "Blink"; + Blink.value = SAYO_MODE_BLINK; + Blink.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE | MODE_FLAG_HAS_RANDOM_COLOR; + Blink.color_mode = MODE_COLORS_PER_LED; + Blink.speed_min = 0; + Blink.speed_max = 3; + Blink.speed = 1; + modes.push_back(Blink); + + SetupZones(); +}; + +RGBController_SayoDevice::~RGBController_SayoDevice() +{ + delete controller; +} + +void RGBController_SayoDevice::SetupZones() +{ + const int led_count = 1; + + zone zone; + zone.name = "Underglow"; + zone.type = ZONE_TYPE_SINGLE; + zone.leds_count = led_count; + zone.leds_min = led_count; + zone.leds_max = led_count; + zone.matrix_map = NULL; + + zones.clear(); + zones.push_back(zone); + + leds.clear(); + leds.resize(led_count); + for(int i = 0; i < led_count; i++) + { + led& new_led = leds[i]; + new_led.name = "LED " + std::to_string(i + 1); + new_led.value = i; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_SayoDevice::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*-----------------------------------------------------*\ + | This device does not support resizing zones | + \*-----------------------------------------------------*/ +} + +void RGBController_SayoDevice::DeviceUpdateLEDs() +{ + unsigned int hw_mode = (modes[active_mode].value == SAYO_MODE_DIRECT) ? SAYO_MODE_STATIC : modes[active_mode].value; + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + unsigned int speed = 3 - modes[active_mode].speed; + + controller->SetMode(hw_mode, speed, colors[0], random); +} + +void RGBController_SayoDevice::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SayoDevice::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SayoDevice::DeviceUpdateMode() +{ + /*-----------------------------------------------------*\ + | Mode and color are always set together. | + \*-----------------------------------------------------*/ + DeviceUpdateLEDs(); +} + +void RGBController_SayoDevice::DeviceSaveMode() +{ + controller->Save(); +} diff --git a/Controllers/SayoDeviceController/RGBController_SayoDevice.h b/Controllers/SayoDeviceController/RGBController_SayoDevice.h new file mode 100644 index 0000000..ae0651b --- /dev/null +++ b/Controllers/SayoDeviceController/RGBController_SayoDevice.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_SayoDevice.h | +| | +| Controller for Sayo Devices | +| | +| Richard Harris 24 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SayoDeviceController.h" + +class RGBController_SayoDevice : public RGBController +{ +public: + RGBController_SayoDevice(SayoDeviceController* controller_ptr); + ~RGBController_SayoDevice(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + SayoDeviceController* controller; +}; diff --git a/Controllers/SayoDeviceController/SayoDeviceController.cpp b/Controllers/SayoDeviceController/SayoDeviceController.cpp new file mode 100644 index 0000000..30661a1 --- /dev/null +++ b/Controllers/SayoDeviceController/SayoDeviceController.cpp @@ -0,0 +1,128 @@ +/*---------------------------------------------------------*\ +| SayoDeviceController.cpp | +| | +| Controller for Sayo Devices (USB HID) | +| | +| Richard Harris 24 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "SayoDeviceController.h" +#include "RGBController.h" +#include "Colors.h" + +SayoDeviceController::SayoDeviceController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +SayoDeviceController::~SayoDeviceController() +{ + hid_close(dev); +} + +std::string SayoDeviceController::GetDeviceLocation() +{ + return location; +} + +std::string SayoDeviceController::GetDeviceName() +{ + return name; +} + +void SayoDeviceController::SetMode(unsigned int mode, unsigned int speed, RGBColor color, bool random) +{ + /*-----------------------------------------------------*\ + | Loop color table creates a rainbow effect which looks | + | better than truly random colors, so use that. | + \*-----------------------------------------------------*/ + unsigned int color_mode = random ? SAYO_COLOR_LOOP_TABLE : SAYO_COLOR_STATIC; + unsigned char mode_byte = SAYO_MODE_PACK(speed, color_mode, mode); + + /*-----------------------------------------------------*\ + | 0x1C (0x00) is potentially length (28) as the payload | + | is 27 bytes in size. Could include a zero as many | + | fields seem to be 2-byte. | + | Other fields have not been mapped out at this time. | + \*-----------------------------------------------------*/ + std::vector payload = + { + 0x1C, 0x00, SAYO_CMD_LIGHTING_SET, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x28, 0x00, 0x26, 0x00, 0x4C, 0x00, + 0x26, 0x00, 0x00, 0x00, mode_byte, 0x00, 0x80, 0x80, + (unsigned char)RGBGetRValue(color), + (unsigned char)RGBGetGValue(color), + (unsigned char)RGBGetBValue(color), + }; + SendPacket(payload, true); +} + +/*---------------------------------------------------------*\ +| Persist LED settings to flash memory. | +\*---------------------------------------------------------*/ +void SayoDeviceController::Save() +{ + /*-----------------------------------------------------*\ + | 0x06 0x00 feels like a length too - 6 bytes | + \*-----------------------------------------------------*/ + std::vector payload = { 0x06, 0x00, 0x0d, 0x00, 0x96, 0x72 }; + SendPacket(payload, true); +} + +void SayoDeviceController::SendPacket(const std::vector& command, bool flush) +{ + unsigned char length = (unsigned char)(command.size() + 4); + + if(length > 64) + { + LOG_ERROR("[SayoDevice] SendPacket: command size (%d) is too large, rejecting", (int)command.size()); + return; + } + + std::vector packet = {0x21, 0x12}; + + /*-----------------------------------------------------*\ + | Checksum of header + command, as little endian 2-byte | + | pairs. Checksum is in same format. | + \*-----------------------------------------------------*/ + unsigned short checksum = (unsigned short)(packet[0] | (packet[1] << 8)); + for(std::size_t i = 0; i < command.size(); i += 2) + { + unsigned short word = command[i]; + + if(i + 1 < command.size()) + { + word |= (unsigned short)(command[i + 1] << 8); + } + + checksum = (checksum + word) & 0xFFFF; + } + + packet.push_back(checksum & 0xFF); + packet.push_back(checksum >> 8); + packet.insert(packet.end(), command.begin(), command.end()); + packet.resize(64, 0u); + + hid_write(dev, packet.data(), packet.size()); + + if(flush) + { + /*-------------------------------------------------*\ + | Flush away any awaiting IN packets | + \*-------------------------------------------------*/ + unsigned char buf[64]; + + int res = 1; + while(res > 0) + { + res = hid_read_timeout(dev, buf, 64, 0); + } + } +} diff --git a/Controllers/SayoDeviceController/SayoDeviceController.h b/Controllers/SayoDeviceController/SayoDeviceController.h new file mode 100644 index 0000000..2129ace --- /dev/null +++ b/Controllers/SayoDeviceController/SayoDeviceController.h @@ -0,0 +1,103 @@ +/*---------------------------------------------------------*\ +| SayoDeviceController.h | +| | +| Controller for Sayo Devices | +| | +| Richard Harris 24 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +/*-----------------------------------------*\ +| Lighting modes | +\*-----------------------------------------*/ +enum +{ + SAYO_MODE_STATIC = 0x00, + SAYO_MODE_INDICATOR = 0x01, + SAYO_MODE_BREATHING = 0x02, + SAYO_MODE_BREATHING_ONCE = 0x03, + SAYO_MODE_WAVE = 0x04, + SAYO_MODE_SWITCH = 0x06, + SAYO_MODE_SWITCH_ONCE = 0x07, + SAYO_MODE_BLINK = 0x08, + SAYO_MODE_BLINK_ONCE = 0x09, + SAYO_MODE_FADE_OUT = 0x0E, + SAYO_MODE_FADE_IN = 0x0F, + /*-------------------------------------*\ + | Virtual modes | + \*-------------------------------------*/ + SAYO_MODE_DIRECT = 0xFF, +}; + +/*-----------------------------------------*\ +| Animation speeds | +\*-----------------------------------------*/ +enum +{ + SAYO_SPEED_1X = 3, + SAYO_SPEED_2X = 2, + SAYO_SPEED_4X = 1, + SAYO_SPEED_8X = 0, +}; + +/*-----------------------------------------*\ +| Animation color mode | +| TABLE modes either loop through or pick | +| randomly from a palette of colors. | +| RANDOM is truly random. | +\*-----------------------------------------*/ +enum +{ + SAYO_COLOR_STATIC = 0, + SAYO_COLOR_LOOP_TABLE = 1, + SAYO_COLOR_RANDOM_TABLE = 2, + SAYO_COLOR_RANDOM = 3, +}; + +#define SAYO_MODE_PACK(speed, color_mode, mode) \ + ((unsigned char)((((speed) & 0x3) << 6) | \ + (((color_mode) & 0x3) << 4) | \ + ((mode) & 0xF))) + +/*-----------------------------------------*\ +| Commands | +\*-----------------------------------------*/ +enum +{ + SAYO_CMD_API_LIST = 0x00, + SAYO_CMD_KEY_GET = 0x02, // maybe? + SAYO_CMD_SETTINGS = 0x03, + SAYO_CMD_KEY_SET = 0x10, + SAYO_CMD_LIGHTING_SET = 0x11, + SAYO_CMD_REBOOT = 0x0E, + +}; + +class SayoDeviceController +{ +public: + SayoDeviceController(hid_device* dev_handle, const char* path, std::string dev_name); + ~SayoDeviceController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void SetMode(unsigned int mode, unsigned int speed, RGBColor color, bool random); + void Save(); + +private: + hid_device* dev; + std::string name; + std::string location; + + void SendPacket(const std::vector& command, bool flush = true); +}; diff --git a/Controllers/SayoDeviceController/SayoDeviceControllerDetect.cpp b/Controllers/SayoDeviceController/SayoDeviceControllerDetect.cpp new file mode 100644 index 0000000..412c988 --- /dev/null +++ b/Controllers/SayoDeviceController/SayoDeviceControllerDetect.cpp @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| SayoDeviceControllerDetect.cpp | +| | +| Detector for Sayo Devices | +| | +| Richard Harris 24 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "SayoDeviceController.h" +#include "RGBController_SayoDevice.h" + +#define SAYO_USB_VID 0x8089 +#define SAYO_USB_PID_E1 0x0007 + +/*----------------------------------------------------------*\ +| | +| DetectSayoDevice Controller | +| | +| Detect Sayo Devices | +| | +\*----------------------------------------------------------*/ + +void DetectSayoDeviceController + ( + hid_device_info* info, + const std::string& name + ) +{ + hid_device* dev = hid_open_path(info->path); + if(dev != nullptr) + { + SayoDeviceController* controller = new SayoDeviceController(dev, info->path, name); + RGBController_SayoDevice *rgb_controller = new RGBController_SayoDevice(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("SayoDevice E1", DetectSayoDeviceController, SAYO_USB_VID, SAYO_USB_PID_E1, 0xFF11, 0x0002); diff --git a/Controllers/SeagateController/RGBController_Seagate.cpp b/Controllers/SeagateController/RGBController_Seagate.cpp new file mode 100644 index 0000000..bc00a92 --- /dev/null +++ b/Controllers/SeagateController/RGBController_Seagate.cpp @@ -0,0 +1,171 @@ +/*---------------------------------------------------------*\ +| RGBController_Seagate.cpp | +| | +| RGBController for Seagate | +| | +| Adam Honse (CalcProgrammer1) 08 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Seagate.h" + +/**------------------------------------------------------------------*\ + @name Seagate + @category Storage + @type SCSI + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectSeagateControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Seagate::RGBController_Seagate(SeagateController* controller_ptr) +{ + controller = controller_ptr; + + name = "Seagate Device"; + vendor = "Seagate"; + type = DEVICE_TYPE_STORAGE; + description = "Seagate Device"; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = SEAGATE_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Blink; + Blink.name = "Flashing"; + Blink.value = SEAGATE_MODE_BLINK; + Blink.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Blink.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Blink); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = SEAGATE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Spectrum; + Spectrum.name = "Spectrum Cycle"; + Spectrum.value = SEAGATE_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Spectrum.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Spectrum); + + SetupZones(); +} + +RGBController_Seagate::~RGBController_Seagate() +{ + delete controller; +} + +void RGBController_Seagate::SetupZones() +{ + zone led_zone; + led_zone.name = "LED Strip"; + led_zone.type = ZONE_TYPE_LINEAR; + led_zone.leds_min = 6; + led_zone.leds_max = 6; + led_zone.leds_count = 6; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led new_led; + new_led.name = "LED Strip LED"; + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_Seagate::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Seagate::DeviceUpdateLEDs() +{ + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + UpdateSingleLED(led_idx); + } +} + +void RGBController_Seagate::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Seagate::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + switch(modes[active_mode].value) + { + case SEAGATE_MODE_STATIC: + controller->SetLEDStatic(led, red, grn, blu, false); + break; + + case SEAGATE_MODE_BLINK: + controller->SetLEDBlink(led, red, grn, blu, false); + break; + + case SEAGATE_MODE_BREATHING: + controller->SetLEDBreathing(led, red, grn, blu, false); + break; + + case SEAGATE_MODE_SPECTRUM: + controller->SetLEDsSpectrum(led, false); + break; + } +} + +void RGBController_Seagate::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_Seagate::DeviceSaveMode() +{ + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned char red = RGBGetRValue(colors[led_idx]); + unsigned char grn = RGBGetGValue(colors[led_idx]); + unsigned char blu = RGBGetBValue(colors[led_idx]); + + switch(modes[active_mode].value) + { + case SEAGATE_MODE_STATIC: + controller->SetLEDStatic(led_idx, red, grn, blu, true); + break; + + case SEAGATE_MODE_BLINK: + controller->SetLEDBlink(led_idx, red, grn, blu, true); + break; + + case SEAGATE_MODE_BREATHING: + controller->SetLEDBreathing(led_idx, red, grn, blu, true); + break; + + case SEAGATE_MODE_SPECTRUM: + controller->SetLEDsSpectrum(led_idx, true); + break; + } + } +} diff --git a/Controllers/SeagateController/RGBController_Seagate.h b/Controllers/SeagateController/RGBController_Seagate.h new file mode 100644 index 0000000..7c5221a --- /dev/null +++ b/Controllers/SeagateController/RGBController_Seagate.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_Seagate.h | +| | +| RGBController for Seagate | +| | +| Adam Honse (CalcProgrammer1) 08 Nov 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SeagateController.h" + +class RGBController_Seagate : public RGBController +{ +public: + RGBController_Seagate(SeagateController* controller_ptr); + ~RGBController_Seagate(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void DeviceSaveMode(); + +private: + SeagateController* controller; +}; diff --git a/Controllers/SeagateController/SeagateController.cpp b/Controllers/SeagateController/SeagateController.cpp new file mode 100644 index 0000000..93294df --- /dev/null +++ b/Controllers/SeagateController/SeagateController.cpp @@ -0,0 +1,224 @@ +/*---------------------------------------------------------*\ +| SeagateController.cpp | +| | +| Driver for Seagate | +| | +| Adam Honse (CalcProgrammer1) 15 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "SeagateController.h" + +SeagateController::SeagateController(scsi_device* dev_handle, char* path) +{ + this->dev = dev_handle; + this->path = path; +} + +SeagateController::~SeagateController() +{ + scsi_close(dev); +} + +std::string SeagateController::GetLocation() +{ + std::string str(path.begin(), path.end()); + return("SCSI: " + str); +} + +void SeagateController::SetLEDBlink + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold RGB control data | + \*-----------------------------------------------------------------------------*/ + unsigned char data[0x10] = {0}; + data[0] = 0x10; /* size of data packet */ + data[1] = 0x00; + data[2] = 0x01; + data[3] = 0x09; + data[4] = 0x01; + data[5] = 0x06; + data[6] = led_id; + data[7] = SEAGATE_MODE_BLINK; + if(save) + { + data[8] = 0x03; /* 0x00 for no save, 0x03 for */ + /* save */ + } + else + { + data[8] = 0x00; + } + data[9] = 0x10; + data[10] = 0x10; + data[11] = r; + data[12] = g; + data[13] = b; + data[14] = 0xFF; + data[15] = 0xFF; + + /*-----------------------------------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------------------------------*/ + SendPacket(data, 0x10); +} + +void SeagateController::SetLEDBreathing + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold RGB control data | + \*-----------------------------------------------------------------------------*/ + unsigned char data[0x14] = {0}; + data[0] = 0x14; /* size of data packet */ + data[1] = 0x00; + data[2] = 0x01; + data[3] = 0x09; + data[4] = 0x01; + data[5] = 0x06; + data[6] = led_id; + data[7] = SEAGATE_MODE_BREATHING; + if(save) + { + data[8] = 0x03; /* 0x00 for no save, 0x03 for */ + /* save */ + } + else + { + data[8] = 0x00; + } + data[9] = 0x0F; + data[10] = 0x0F; + data[11] = 0x0F; + data[12] = 0x0F; + data[13] = r; + data[14] = g; + data[15] = b; + data[16] = 0xFF; + data[17] = 0xFF; + data[18] = 0xFF; + data[19] = 0x00; + + /*-----------------------------------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------------------------------*/ + SendPacket(data, 0x14); +} + +void SeagateController::SetLEDsSpectrum + ( + unsigned char led_id, + bool /*save*/ + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold RGB control data | + \*-----------------------------------------------------------------------------*/ + unsigned char data[0x0A] = {0}; + data[0] = 0x0A; /* size of data packet */ + data[1] = 0x00; + data[2] = 0x01; + data[3] = 0x09; + data[4] = 0x01; + data[5] = 0x06; + data[6] = led_id; + data[7] = SEAGATE_MODE_SPECTRUM; + data[8] = 0x02; + data[9] = 0xB4; + + /*-----------------------------------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------------------------------*/ + SendPacket(data, 0x0A); +} + +void SeagateController::SetLEDStatic + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold RGB control data | + \*-----------------------------------------------------------------------------*/ + unsigned char data[0x0E] = {0}; + data[0] = 0x0E; /* size of data packet */ + data[1] = 0x00; + data[2] = 0x01; + data[3] = 0x09; + data[4] = 0x01; + data[5] = 0x06; + data[6] = led_id; + data[7] = SEAGATE_MODE_STATIC; + if(save) + { + data[8] = 0x03; /* 0x00 for no save, 0x03 for */ + /* save */ + } + else + { + data[8] = 0x00; + } + data[9] = r; + data[10] = g; + data[11] = b; + data[12] = 0xFF; + data[13] = 0xFF; + + /*-----------------------------------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------------------------------*/ + SendPacket(data, 0x0E); +} + +void SeagateController::SendPacket + ( + unsigned char * packet, + unsigned char packet_sz + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold CDB | + \*-----------------------------------------------------------------------------*/ + unsigned char cdb[12] = {0}; + cdb[0] = 0xD2; + cdb[1] = 0x53; /* S */ + cdb[2] = 0x65; /* e */ + cdb[3] = 0x74; /* t */ + cdb[4] = 0x4C; /* L */ + cdb[5] = 0x65; /* e */ + cdb[6] = 0x64; /* d */ + cdb[7] = 0x00; + cdb[8] = 0x00; + cdb[9] = 0x30; + cdb[10] = packet_sz; + cdb[11] = 0x00; + + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold sense data | + \*-----------------------------------------------------------------------------*/ + unsigned char sense[32] = {0}; + + /*-----------------------------------------------------------------------------*\ + | Write SCSI packet | + \*-----------------------------------------------------------------------------*/ + scsi_write(dev, packet, packet_sz, cdb, 12, sense, 32); +} diff --git a/Controllers/SeagateController/SeagateController.h b/Controllers/SeagateController/SeagateController.h new file mode 100644 index 0000000..83046b5 --- /dev/null +++ b/Controllers/SeagateController/SeagateController.h @@ -0,0 +1,75 @@ +/*---------------------------------------------------------*\ +| SeagateController.h | +| | +| Driver for Seagate | +| | +| Adam Honse (CalcProgrammer1) 15 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "scsiapi.h" + +enum +{ + SEAGATE_MODE_STATIC = 0x01, /* Static mode */ + SEAGATE_MODE_BLINK = 0x02, /* Blink mode */ + SEAGATE_MODE_BREATHING = 0x03, /* Breathing mode */ + SEAGATE_MODE_SPECTRUM = 0x05, /* Spectrum mode */ +}; + +class SeagateController +{ +public: + SeagateController(scsi_device* dev_handle, char* path); + ~SeagateController(); + + std::string GetLocation(); + + void SetLEDBlink + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ); + + void SetLEDBreathing + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ); + + void SetLEDsSpectrum + ( + unsigned char led_id, + bool save + ); + + void SetLEDStatic + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ); + +private: + scsi_device* dev; + std::string path; + + void SendPacket + ( + unsigned char * packet, + unsigned char packet_sz + ); +}; diff --git a/Controllers/SeagateController/SeagateControllerDetect.cpp b/Controllers/SeagateController/SeagateControllerDetect.cpp new file mode 100644 index 0000000..2338a6b --- /dev/null +++ b/Controllers/SeagateController/SeagateControllerDetect.cpp @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| SeagateControllerDetect.cpp | +| | +| Detector for Seagate | +| | +| Adam Honse (CalcProgrammer1) 15 Jun 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "SeagateController.h" +#include "RGBController_Seagate.h" +#include "scsiapi.h" + +/******************************************************************************************\ +* * +* DetectSeagateControllers * +* * +* Detects Seagate FireCuda HDD devices * +* * +\******************************************************************************************/ + +void DetectSeagateControllers() +{ + scsi_device_info * info = scsi_enumerate(NULL, NULL); + + while(info) + { + if(strncmp(info->vendor, "Seagate", 7) == 0 && strncmp(info->product, "FireCuda HDD", 12) == 0) + { + scsi_device * dev = scsi_open_path(info->path); + + if(dev) + { + SeagateController* controller = new SeagateController(dev, info->path); + RGBController_Seagate* rgb_controller = new RGBController_Seagate(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + info = info->next; + } + + scsi_free_enumeration(info); + +} /* DetectSeagateControllers() */ + +REGISTER_DETECTOR("Seagate Firecuda HDD", DetectSeagateControllers); diff --git a/Controllers/SinowealthController/GenesisXenon200Controller/GenesisXenon200Controller.cpp b/Controllers/SinowealthController/GenesisXenon200Controller/GenesisXenon200Controller.cpp new file mode 100644 index 0000000..a2a56e0 --- /dev/null +++ b/Controllers/SinowealthController/GenesisXenon200Controller/GenesisXenon200Controller.cpp @@ -0,0 +1,56 @@ +/*---------------------------------------------------------*\ +| GenesisXenon200Controller.cpp | +| | +| Driver for Genesis Xenon 200 mouse | +| | +| chrabonszcz Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "GenesisXenon200Controller.h" + +GenesisXenon200Controller::GenesisXenon200Controller(hid_device* dev_handle, hid_device* cmd_dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + cmd_dev = cmd_dev_handle; + location = path; + name = dev_name; +} + +GenesisXenon200Controller::~GenesisXenon200Controller() +{ + +} + +std::string GenesisXenon200Controller::GetLocationString() +{ + return("HID: " + location); +} + +std::string GenesisXenon200Controller::GetNameString() +{ + return(name); +} + +void GenesisXenon200Controller::SaveMode(unsigned char mode, unsigned char value, RGBColor color) +{ + unsigned char usb_buf[154]; + + usb_buf[0] = 0x04; + + hid_get_feature_report(dev, usb_buf, 154); + + usb_buf[0x5D] = mode; + usb_buf[0x60] = value; + usb_buf[0x61] = RGBGetRValue(color); + usb_buf[0x62] = RGBGetGValue(color); + usb_buf[0x63] = RGBGetBValue(color); + + hid_send_feature_report(dev, usb_buf, 154); + + usb_buf[0] = 0x08; + hid_get_feature_report(cmd_dev, usb_buf, 9); + hid_send_feature_report(cmd_dev, usb_buf, 9); +} diff --git a/Controllers/SinowealthController/GenesisXenon200Controller/GenesisXenon200Controller.h b/Controllers/SinowealthController/GenesisXenon200Controller/GenesisXenon200Controller.h new file mode 100644 index 0000000..0ef6ec5 --- /dev/null +++ b/Controllers/SinowealthController/GenesisXenon200Controller/GenesisXenon200Controller.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| GenesisXenon200Controller.h | +| | +| Driver for Genesis Xenon 200 mouse | +| | +| chrabonszcz Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" + +class GenesisXenon200Controller +{ +public: + GenesisXenon200Controller(hid_device* dev_handle, hid_device* cmd_dev_handle, const char* path, std::string dev_name); + ~GenesisXenon200Controller(); + + std::string GetLocationString(); + std::string GetNameString(); + + void SaveMode(unsigned char mode, unsigned char value, RGBColor color); + +private: + hid_device* dev; + hid_device* cmd_dev; + std::string location; + std::string name; +}; diff --git a/Controllers/SinowealthController/GenesisXenon200Controller/RGBController_GenesisXenon200.cpp b/Controllers/SinowealthController/GenesisXenon200Controller/RGBController_GenesisXenon200.cpp new file mode 100644 index 0000000..861e1fa --- /dev/null +++ b/Controllers/SinowealthController/GenesisXenon200Controller/RGBController_GenesisXenon200.cpp @@ -0,0 +1,145 @@ +/*---------------------------------------------------------*\ +| RGBController_GenesisXenon200.cpp | +| | +| RGBController for Genesis Xenon 200 mouse | +| | +| chrabonszcz Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +/**------------------------------------------------------------------*\ + @name Genesis Xenon 200 + @type USB + @save :white_check_mark: + @direct :x: + @effects :white_check_mark: + @detectors DetectSinowealthMouse + @comment +\*-------------------------------------------------------------------*/ + +#include "RGBController_GenesisXenon200.h" + +RGBController_GenesisXenon200::RGBController_GenesisXenon200(GenesisXenon200Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Genesis"; + description = "Genesis Xenon 200 Mouse Device"; + type = DEVICE_TYPE_MOUSE; + location = controller->GetLocationString(); + + mode Static; + Static.name = "Static"; + Static.value = GENESIS_XENON_200_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness = 1; + Static.brightness_min = 0; + Static.brightness_max = 2; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = GENESIS_XENON_200_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed = 1; + Breathing.speed_min = 0; + Breathing.speed_max = 2; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = GENESIS_XENON_200_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.speed = 1; + SpectrumCycle.speed_min = 0; + SpectrumCycle.speed_max = 2; + modes.push_back(SpectrumCycle); + + mode Off; + Off.name = "Off"; + Off.value = GENESIS_XENON_200_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupColors(); +} + +RGBController_GenesisXenon200::~RGBController_GenesisXenon200() +{ + delete controller; +} + +void RGBController_GenesisXenon200::DeviceUpdateMode() +{ + RGBColor color = 0; + unsigned char value = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + color = modes[active_mode].colors[0]; + } + + switch(modes[active_mode].value) + { + case GENESIS_XENON_200_MODE_STATIC: + + value = GENESIS_XENON_200_STATIC_BRIGHTESS_VALUES[modes[active_mode].brightness]; + break; + + case GENESIS_XENON_200_MODE_BREATHING: + + value = GENESIS_XENON_200_BREATHING_SPEED_VALUES[modes[active_mode].speed]; + break; + + case GENESIS_XENON_200_MODE_SPECTRUM_CYCLE: + + value = GENESIS_XENON_200_SPECTRUM_CYCLE_SPEED_VALUES[modes[active_mode].speed]; + break; + + } + + controller->SaveMode(modes[active_mode].value, value, color); +} + +void RGBController_GenesisXenon200::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_GenesisXenon200::DeviceSaveMode() +{ + +} + +void RGBController_GenesisXenon200::SetupZones() +{ + +} + +void RGBController_GenesisXenon200::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_GenesisXenon200::UpdateZoneLEDs(int /*zone*/) +{ + +} + +void RGBController_GenesisXenon200::UpdateSingleLED(int /*led*/) +{ + +} diff --git a/Controllers/SinowealthController/GenesisXenon200Controller/RGBController_GenesisXenon200.h b/Controllers/SinowealthController/GenesisXenon200Controller/RGBController_GenesisXenon200.h new file mode 100644 index 0000000..5beeb2e --- /dev/null +++ b/Controllers/SinowealthController/GenesisXenon200Controller/RGBController_GenesisXenon200.h @@ -0,0 +1,45 @@ +/*---------------------------------------------------------*\ +| RGBController_GenesisXenon200.h | +| | +| RGBController for Genesis Xenon 200 mouse | +| | +| chrabonszcz Jul 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "GenesisXenon200Controller.h" +#include "RGBController.h" + +#define GENESIS_XENON_200_MODE_STATIC 0x18 +#define GENESIS_XENON_200_MODE_BREATHING 0x12 +#define GENESIS_XENON_200_MODE_SPECTRUM_CYCLE 0x14 +#define GENESIS_XENON_200_MODE_OFF 0x11 + +const unsigned char GENESIS_XENON_200_STATIC_BRIGHTESS_VALUES[] {0x11, 0x51, 0xA1}; +const unsigned char GENESIS_XENON_200_BREATHING_SPEED_VALUES[] {0x51, 0x31, 0x11}; +const unsigned char GENESIS_XENON_200_SPECTRUM_CYCLE_SPEED_VALUES[] {0xC1, 0x81, 0x41}; + +class RGBController_GenesisXenon200 : public RGBController +{ +public: + RGBController_GenesisXenon200(GenesisXenon200Controller* controller_ptr); + ~RGBController_GenesisXenon200(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + GenesisXenon200Controller* controller; +}; diff --git a/Controllers/SinowealthController/Sinowealth1007Controller/RGBController_Sinowealth1007.cpp b/Controllers/SinowealthController/Sinowealth1007Controller/RGBController_Sinowealth1007.cpp new file mode 100644 index 0000000..90f5311 --- /dev/null +++ b/Controllers/SinowealthController/Sinowealth1007Controller/RGBController_Sinowealth1007.cpp @@ -0,0 +1,253 @@ +/*---------------------------------------------------------*\ +| RGBController_Sinowealth1007.cpp | +| | +| RGBController for Sinowealth mice with PID 1007 | +| | +| Moon_darker (Vaker) 02 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Sinowealth1007.h" + +static const char *led_names[] = +{ + "Top Left", + "Middle Left", + "Bottom Left", + "Bottom Middle", + "Bottom Right", + "Middle Right", + "Top Right" +}; + +/**------------------------------------------------------------------*\ + @name Sinowealth 1007 Mouse + @category Mouse + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectSinowealthMouse + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Sinowealth1007::RGBController_Sinowealth1007(SinowealthController1007* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "ZET"; + type = DEVICE_TYPE_MOUSE; + description = "ZET Fury Pro Mouse Device"; + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Custom; + Custom.name = "Custom"; + Custom.value = ZET_FURY_PRO_MODE_CUSTOM; + Custom.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_PER_LED_COLOR; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.speed = ZET_FURY_PRO_SPEED_DEF; + modes.push_back(Custom); + + mode Off; + Off.name = "Off"; + Off.value = ZET_FURY_PRO_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + Off.speed = ZET_FURY_PRO_SPEED_DEF; + Off.brightness = ZET_FURY_PRO_BRIGHTNESS_DEF; + modes.push_back(Off); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = ZET_FURY_PRO_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed_min = ZET_FURY_PRO_SPEED_MIN; + Rainbow.speed_max = ZET_FURY_PRO_SPEED_MAX; + Rainbow.speed = ZET_FURY_PRO_SPEED_DEF; + Rainbow.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Rainbow); + + mode Static; + Static.name = "Static"; + Static.value = ZET_FURY_PRO_MODE_STATIC; + Static.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.brightness_min = ZET_FURY_PRO_BRIGHTNESS_MIN; + Static.brightness_max = ZET_FURY_PRO_BRIGHTNESS_MAX; + Static.brightness = ZET_FURY_PRO_BRIGHTNESS_DEF; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ZET_FURY_PRO_MODE_BREATHING; + Breathing.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.speed_min = ZET_FURY_PRO_SPEED_MIN; + Breathing.speed_max = ZET_FURY_PRO_SPEED_MAX; + Breathing.speed = ZET_FURY_PRO_SPEED_DEF; + Breathing.colors_min = 7; + Breathing.colors_max = 7; + Breathing.colors.resize(Breathing.colors_max); + modes.push_back(Breathing); + + mode Pendulum; + Pendulum.name = "Pendulum"; + Pendulum.value = ZET_FURY_PRO_MODE_PENDULUM; + Pendulum.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + Pendulum.color_mode = MODE_COLORS_NONE; + Pendulum.speed_min = ZET_FURY_PRO_SPEED_MIN; + Pendulum.speed_max = ZET_FURY_PRO_SPEED_MAX; + Pendulum.speed = ZET_FURY_PRO_SPEED_DEF; + modes.push_back(Pendulum); + + mode Spectrum; + Spectrum.name = "Spectrum Cycle"; + Spectrum.value = ZET_FURY_PRO_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.speed_min = ZET_FURY_PRO_SPEED_MIN; + Spectrum.speed_max = ZET_FURY_PRO_SPEED_MAX; + Spectrum.speed = ZET_FURY_PRO_SPEED_DEF; + modes.push_back(Spectrum); + + mode TwoColors; + TwoColors.name = "Two Colors"; // Should this be called "Flashing Two Colors" or smth like that maybe? Rapidly changes between 2 colors + TwoColors.value = ZET_FURY_PRO_MODE_TWO_COLORS; + TwoColors.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + TwoColors.color_mode = MODE_COLORS_MODE_SPECIFIC; + TwoColors.colors_min = 2; + TwoColors.colors_max = 2; + TwoColors.colors.resize(TwoColors.colors_max); + modes.push_back(TwoColors); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = ZET_FURY_PRO_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED; + Reactive.color_mode = MODE_COLORS_RANDOM; + Reactive.speed_min = ZET_FURY_PRO_SPEED_MIN; + Reactive.speed_max = ZET_FURY_PRO_SPEED_MAX; + Reactive.speed = ZET_FURY_PRO_SPEED_DEF; + Reactive.colors_min = 7; + Reactive.colors_max = 7; + Reactive.colors.resize(Reactive.colors_max); + modes.push_back(Reactive); + + mode Flicker; + Flicker.name = "Flicker"; // One color fluctuates around max brightness for some time, then changes to another + Flicker.value = ZET_FURY_PRO_MODE_FLICKER; + Flicker.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Flicker.color_mode = MODE_COLORS_NONE; + Flicker.speed_min = ZET_FURY_PRO_SPEED_MIN; + Flicker.speed_max = ZET_FURY_PRO_SPEED_MAX; + Flicker.speed = ZET_FURY_PRO_SPEED_DEF; + Flicker.direction = MODE_DIRECTION_RIGHT; + modes.push_back(Flicker); + + mode Rain; + Rain.name = "Rain"; // More like bad LSD trip + Rain.value = ZET_FURY_PRO_MODE_RAIN; + Rain.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + Rain.color_mode = MODE_COLORS_NONE; + Rain.speed_min = ZET_FURY_PRO_SPEED_MIN; + Rain.speed_max = ZET_FURY_PRO_SPEED_MAX; + Rain.speed = ZET_FURY_PRO_SPEED_DEF; + modes.push_back(Rain); + + mode Snake; + Snake.name = "Snake"; + Snake.value = ZET_FURY_PRO_MODE_SNAKE; + Snake.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + Snake.color_mode = MODE_COLORS_NONE; + Snake.speed_min = ZET_FURY_PRO_SPEED_MIN; + Snake.speed_max = ZET_FURY_PRO_SPEED_MAX; + Snake.speed = ZET_FURY_PRO_SPEED_DEF; + modes.push_back(Snake); + + SetupZones(); +} + +RGBController_Sinowealth1007::~RGBController_Sinowealth1007() +{ + delete controller; +} + +void RGBController_Sinowealth1007::SetupZones() +{ + /*---------------------------------------------------------*\ + | Create a single zone | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = controller->GetLEDCount(); + new_zone.leds_max = controller->GetLEDCount(); + new_zone.leds_count = controller->GetLEDCount(); + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for (unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_Sinowealth1007::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Sinowealth1007::DeviceUpdateLEDs() +{ + controller->SetLEDColors(colors); +} + +void RGBController_Sinowealth1007::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Sinowealth1007::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Sinowealth1007::DeviceUpdateMode() +{ + unsigned char random = (modes[active_mode].flags & MODE_FLAG_HAS_RANDOM_COLOR) ? (unsigned char)ZET_FURY_PRO_SUBMODE_SET_COLOR : 0x00; + random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM) ? (unsigned char)ZET_FURY_PRO_SUBMODE_RANDOM : random; + + if (modes[active_mode].value == ZET_FURY_PRO_MODE_BREATHING) + { + random = ZET_FURY_PRO_SUBMODE_SET_COLOR; // An unfortunate exception that has no random option but requires this + } + + if (!(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR)) + { + modes[active_mode].direction = MODE_DIRECTION_RIGHT; // Left and right are backwards, and we don't want to always append 0x80 + } + + controller->SetMode(modes[active_mode].value, + (modes[active_mode].speed ? modes[active_mode].speed : modes[active_mode].brightness), + modes[active_mode].direction ? ZET_FURY_PRO_DIR_RIGHT : ZET_FURY_PRO_DIR_LEFT, + modes[active_mode].colors, + random, + (modes[active_mode].color_mode == MODE_COLORS_PER_LED)); +} diff --git a/Controllers/SinowealthController/Sinowealth1007Controller/RGBController_Sinowealth1007.h b/Controllers/SinowealthController/Sinowealth1007Controller/RGBController_Sinowealth1007.h new file mode 100644 index 0000000..13a47c2 --- /dev/null +++ b/Controllers/SinowealthController/Sinowealth1007Controller/RGBController_Sinowealth1007.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_Sinowealth1007.h | +| | +| RGBController for Sinowealth mice with PID 1007 | +| | +| Moon_darker (Vaker) 25 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SinowealthController1007.h" + +class RGBController_Sinowealth1007 : public RGBController +{ +public: + RGBController_Sinowealth1007(SinowealthController1007* controller_ptr); + ~RGBController_Sinowealth1007(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SinowealthController1007* controller; +}; diff --git a/Controllers/SinowealthController/Sinowealth1007Controller/SinowealthController1007.cpp b/Controllers/SinowealthController/Sinowealth1007Controller/SinowealthController1007.cpp new file mode 100644 index 0000000..06ba62e --- /dev/null +++ b/Controllers/SinowealthController/Sinowealth1007Controller/SinowealthController1007.cpp @@ -0,0 +1,128 @@ +/*---------------------------------------------------------*\ +| SinowealthController1007.cpp | +| | +| Driver for Sinowealth mice with PID 1007 | +| | +| Moon_darker (Vaker) 02 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "SinowealthController1007.h" +#include "StringUtils.h" + +SinowealthController1007::SinowealthController1007(hid_device* dev, char *_path, std::string dev_name) +{ + this->dev = dev; + this->location = _path; + this->name = dev_name; + + this->led_count = 7; + this->current_mode = ZET_FURY_PRO_MODE_CUSTOM + ZET_FURY_PRO_SPEED_DEF; + this->current_direction = ZET_FURY_PRO_DIR_RIGHT; + + memset(device_colors, 0x00, sizeof(device_colors)); +} + +SinowealthController1007::~SinowealthController1007() +{ + hid_close(dev); +} + +std::string SinowealthController1007::GetLocation() +{ + return("HID: " + location); +} + +std::string SinowealthController1007::GetName() +{ + return(name); +} + +unsigned int SinowealthController1007::GetLEDCount() +{ + return(led_count); +} + +std::string SinowealthController1007::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void SinowealthController1007::SetLEDColors(const std::vector& colors) +{ + memset(device_colors, 0x00, sizeof(device_colors)); + + unsigned int color_counter = 0; + for (RGBColor color: colors) + { + unsigned int pkt_pointer = (color_counter * 3); // 3 bytes per color + + device_colors[pkt_pointer] = RGBGetRValue(color); + device_colors[pkt_pointer + 1] = RGBGetGValue(color); + device_colors[pkt_pointer + 2] = RGBGetBValue(color); + + if (++color_counter == 7) break; + } + + SendPacket(); +} + +void SinowealthController1007::SetMode( + unsigned char mode, + unsigned char spd_or_lum, + unsigned char direction, + const std::vector& colors, + unsigned char random, + bool has_per_led_colors) +{ + current_mode = mode + (spd_or_lum ? spd_or_lum : ZET_FURY_PRO_SPEED_DEF); + current_direction = random ? random : direction; + + if (!has_per_led_colors) + { + memset(device_colors, 0x00, sizeof(device_colors)); + SetLEDColors(colors); + } +} + +void SinowealthController1007::SendPacket() +{ + if (GetProfile() < 0) return; + + unsigned char usb_buf[ZET_FURY_PRO_STATE_BUFFER_LENGTH]; + memcpy(usb_buf, device_configuration, sizeof(usb_buf)); + memcpy(usb_buf + 23, device_colors, sizeof(device_colors)); // colors are bytes 23-43 in RGB format counting from 0 + + usb_buf[21] = current_mode; + usb_buf[22] = current_direction; + + hid_send_feature_report(dev, usb_buf, sizeof(usb_buf)); +} + +int SinowealthController1007::GetProfile() +{ + int bytesReceived; + + memset(device_configuration, 0x00, ZET_FURY_PRO_STATE_BUFFER_LENGTH); + device_configuration[0] = 0x04; + + bytesReceived = hid_get_feature_report(dev, device_configuration, ZET_FURY_PRO_STATE_BUFFER_LENGTH); + if (bytesReceived < 0) + { + LOG_ERROR("[ZET Fury Pro] Error reading device configuration!"); + } + + return bytesReceived; +} diff --git a/Controllers/SinowealthController/Sinowealth1007Controller/SinowealthController1007.h b/Controllers/SinowealthController/Sinowealth1007Controller/SinowealthController1007.h new file mode 100644 index 0000000..90213cb --- /dev/null +++ b/Controllers/SinowealthController/Sinowealth1007Controller/SinowealthController1007.h @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| SinowealthController1007.h | +| | +| Driver for Sinowealth mice with PID 1007 | +| | +| Moon_darker (Vaker) 25 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define ZET_FURY_PRO_STATE_BUFFER_LENGTH 59 +#define ZET_FURY_PRO_COLOR_BUFFER_LENGTH 21 + +#define ZET_FURY_PRO_BRIGHTNESS_MIN 1 +#define ZET_FURY_PRO_BRIGHTNESS_MAX 9 +#define ZET_FURY_PRO_BRIGHTNESS_DEF 9 + +#define ZET_FURY_PRO_SPEED_MIN 1 +#define ZET_FURY_PRO_SPEED_MAX 3 +#define ZET_FURY_PRO_SPEED_DEF 2 + +enum +{ + ZET_FURY_PRO_MODE_OFF = 0x00, + ZET_FURY_PRO_MODE_RAINBOW = 0x10, + ZET_FURY_PRO_MODE_STATIC = 0x20, + ZET_FURY_PRO_MODE_BREATHING = 0x30, + ZET_FURY_PRO_MODE_PENDULUM = 0x40, + ZET_FURY_PRO_MODE_SPECTRUM = 0x50, + ZET_FURY_PRO_MODE_CUSTOM = 0x60, + ZET_FURY_PRO_MODE_TWO_COLORS = 0x70, + ZET_FURY_PRO_MODE_REACTIVE = 0x80, + ZET_FURY_PRO_MODE_FLICKER = 0x90, + ZET_FURY_PRO_MODE_RAIN = 0xA0, + ZET_FURY_PRO_MODE_SNAKE = 0xB0, +}; + +enum +{ + ZET_FURY_PRO_SUBMODE_SET_COLOR = 0x07, + ZET_FURY_PRO_SUBMODE_RANDOM = 0x80, +}; + +enum +{ + ZET_FURY_PRO_DIR_LEFT = 0x80, + ZET_FURY_PRO_DIR_RIGHT = 0x00, +}; + +class SinowealthController1007 +{ +public: + SinowealthController1007(hid_device* dev, char *_path, std::string dev_name); + ~SinowealthController1007(); + + unsigned int GetLEDCount(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + + void SetLEDColors(const std::vector& colors); + void SetMode(unsigned char mode, unsigned char spd_or_lum, unsigned char direction, const std::vector& colors, unsigned char random, bool has_per_led_colors); + int GetProfile(); + void SendPacket(); +private: + hid_device* dev; + + unsigned int led_count; + + unsigned char current_mode; + unsigned char current_direction; + unsigned char device_configuration[ZET_FURY_PRO_STATE_BUFFER_LENGTH]; + unsigned char device_colors[ZET_FURY_PRO_COLOR_BUFFER_LENGTH]; + + std::string location; + std::string name; +}; diff --git a/Controllers/SinowealthController/SinowealthController/RGBController_Sinowealth.cpp b/Controllers/SinowealthController/SinowealthController/RGBController_Sinowealth.cpp new file mode 100644 index 0000000..fa2514b --- /dev/null +++ b/Controllers/SinowealthController/SinowealthController/RGBController_Sinowealth.cpp @@ -0,0 +1,249 @@ +/*---------------------------------------------------------*\ +| RGBController_Sinowealth.cpp | +| | +| RGBController for Sinowealth mice, including Glorious | +| | +| Niels Westphal (crashniels) 20 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Sinowealth.h" + +/**------------------------------------------------------------------*\ + @name Sinowealth Mice + @category Mouse + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectSinowealthMouse + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Sinowealth::RGBController_Sinowealth(SinowealthController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + type = DEVICE_TYPE_MOUSE; + description = "Sinowealth Device"; + location = controller->GetLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Static; + Static.name = "Custom"; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.brightness_min = GLORIOUS_BRIGHTNESS_LOW; + Static.brightness = GLORIOUS_BRIGHTNESS_NORMAL; + Static.brightness_max = GLORIOUS_BRIGHTNESS_HIGH; + Static.color_mode = MODE_COLORS_PER_LED; + Static.value = GLORIOUS_MODE_STATIC; + modes.push_back(Static); + + mode Off; + Off.name = "Off"; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + Off.value = GLORIOUS_MODE_OFF; + modes.push_back(Off); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_UD | MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.speed_min = GLORIOUS_SPEED_SLOW; + Rainbow.speed = GLORIOUS_SPEED_NORMAL; + Rainbow.speed_max = GLORIOUS_SPEED_FAST; + Rainbow.direction = MODE_DIRECTION_UP; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.value = GLORIOUS_MODE_RAINBOW; + modes.push_back(Rainbow); + + mode SpectrumBreathing; + SpectrumBreathing.name = "Seemless Breathing"; + SpectrumBreathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumBreathing.speed_min = GLORIOUS_SPEED_SLOW; + SpectrumBreathing.speed = GLORIOUS_SPEED_NORMAL; + SpectrumBreathing.speed_max = GLORIOUS_SPEED_FAST; + SpectrumBreathing.colors_min = 7; + SpectrumBreathing.colors_max = 7; + SpectrumBreathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + SpectrumBreathing.value = GLORIOUS_MODE_SPECTRUM_BREATING; + SpectrumBreathing.colors.resize(7); + modes.push_back(SpectrumBreathing); + + mode Chase; + Chase.name = "Tail"; + Chase.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Chase.speed_min = GLORIOUS_SPEED_SLOW; + Chase.speed = GLORIOUS_SPEED_NORMAL; + Chase.speed_max = GLORIOUS_SPEED_FAST; + Chase.brightness_min = GLORIOUS_BRIGHTNESS_LOW; + Chase.brightness = GLORIOUS_BRIGHTNESS_NORMAL; + Chase.brightness_max = GLORIOUS_BRIGHTNESS_HIGH; + Chase.color_mode = MODE_COLORS_NONE; + Chase.value = GLORIOUS_MODE_TAIL; + modes.push_back(Chase); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.speed_min = GLORIOUS_SPEED_SLOW; + SpectrumCycle.speed = GLORIOUS_SPEED_NORMAL; + SpectrumCycle.speed_max = GLORIOUS_SPEED_FAST; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.value = GLORIOUS_MODE_SPECTRUM_CYCLE; + modes.push_back(SpectrumCycle); + + mode Flashing; + Flashing.name = "Rave"; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Flashing.speed_min = GLORIOUS_SPEED_SLOW; + Flashing.speed = GLORIOUS_SPEED_NORMAL; + Flashing.speed_max = GLORIOUS_SPEED_FAST; + Flashing.brightness_min = GLORIOUS_BRIGHTNESS_LOW; + Flashing.brightness = GLORIOUS_BRIGHTNESS_NORMAL; + Flashing.brightness_max = GLORIOUS_BRIGHTNESS_HIGH; + Flashing.colors_min = 2; + Flashing.colors_max = 2; + Flashing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Flashing.value = GLORIOUS_MODE_RAVE; + Flashing.colors.resize(2); + modes.push_back(Flashing); + + mode Epilepsy; + Epilepsy.name = "Epilepsy"; + Epilepsy.flags = MODE_FLAG_AUTOMATIC_SAVE; + Epilepsy.color_mode = MODE_COLORS_NONE; + Epilepsy.value = GLORIOUS_MODE_EPILEPSY; + modes.push_back(Epilepsy); + + mode Wave; + Wave.name = "Wave"; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Wave.speed_min = GLORIOUS_SPEED_SLOW; + Wave.speed = GLORIOUS_SPEED_NORMAL; + Wave.speed_max = GLORIOUS_SPEED_FAST; + Wave.brightness_min = GLORIOUS_BRIGHTNESS_LOW; + Wave.brightness = GLORIOUS_BRIGHTNESS_NORMAL; + Wave.brightness_max = GLORIOUS_BRIGHTNESS_HIGH; + Wave.color_mode = MODE_COLORS_NONE; + Wave.value = GLORIOUS_MODE_WAVE; + modes.push_back(Wave); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = GLORIOUS_SPEED_SLOW; + Breathing.speed = GLORIOUS_SPEED_NORMAL; + Breathing.speed_max = GLORIOUS_SPEED_FAST; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.value = GLORIOUS_MODE_BREATHING; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_Sinowealth::~RGBController_Sinowealth() +{ + delete controller; +} + +void RGBController_Sinowealth::SetupZones() +{ + /*---------------------------------------------------------*\ + | Create a single zone | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = controller->GetLEDCount(); + new_zone.leds_max = controller->GetLEDCount(); + new_zone.leds_count = controller->GetLEDCount(); + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led* new_led = new led(); + new_led->name = "Mouse LED"; + leds.push_back(*new_led); + } + + SetupColors(); +} + +void RGBController_Sinowealth::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Sinowealth::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_Sinowealth::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Sinowealth::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Sinowealth::DeviceUpdateMode() +{ + unsigned int direction = 0; + unsigned int speed = GLORIOUS_SPEED_FAST; + unsigned int brightness = GLORIOUS_BRIGHTNESS_HIGH; + + if (modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + speed = modes[active_mode].speed; + } + + if (modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + brightness = modes[active_mode].brightness; + } + + if ((modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) || + (modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_UD) || + (modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_HV)) + { + if (modes[active_mode].direction == MODE_DIRECTION_UP) + { + direction = GLORIOUS_DIRECTION_UP; + } + else + { + direction = GLORIOUS_DIRECTION_DOWN; + } + } + + if (modes[active_mode].color_mode == MODE_COLORS_NONE) + { + controller->SetMode(modes[active_mode].value, speed, brightness, direction, 0); + } + else if (modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + controller->SetMode(modes[active_mode].value, speed, brightness, direction, &colors[0]); + } + else + { + controller->SetMode(modes[active_mode].value, speed, brightness, direction, &modes[active_mode].colors[0]); + } +} + diff --git a/Controllers/SinowealthController/SinowealthController/RGBController_Sinowealth.h b/Controllers/SinowealthController/SinowealthController/RGBController_Sinowealth.h new file mode 100644 index 0000000..3517375 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthController/RGBController_Sinowealth.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_Sinowealth.h | +| | +| RGBController for Sinowealth mice, including Glorious | +| | +| Niels Westphal (crashniels) 20 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SinowealthController.h" + +class RGBController_Sinowealth : public RGBController +{ +public: + RGBController_Sinowealth(SinowealthController* controller_ptr); + ~RGBController_Sinowealth(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SinowealthController* controller; +}; diff --git a/Controllers/SinowealthController/SinowealthController/SinowealthController.cpp b/Controllers/SinowealthController/SinowealthController/SinowealthController.cpp new file mode 100644 index 0000000..9e766bc --- /dev/null +++ b/Controllers/SinowealthController/SinowealthController/SinowealthController.cpp @@ -0,0 +1,208 @@ +/*---------------------------------------------------------*\ +| SinowealthController.cpp | +| | +| Driver for Sinowealth mice, including Glorious | +| | +| Niels Westphal (crashniels) 20 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "SinowealthController.h" +#include "StringUtils.h" + +SinowealthController::SinowealthController(hid_device* dev_data_handle, hid_device* dev_cmd_handle, char *_path, std::string dev_name) +{ + dev_data = dev_data_handle; + dev_cmd = dev_cmd_handle; + location = _path; + name = dev_name; + + led_count = 1; +} + +SinowealthController::~SinowealthController() +{ + hid_close(dev_data); + + /*---------------------------------------------------------------------*\ + | If the dev_cmd handle was passed in as the same device as dev_data | + | then attempting to close it a second time will segfault | + \*---------------------------------------------------------------------*/ + if(dev_cmd) + { + hid_close(dev_cmd); + } +} + +std::string SinowealthController::GetLocation() +{ + return("HID: " + location); +} + +std::string SinowealthController::GetName() +{ + return(name); +} + +unsigned int SinowealthController::GetLEDCount() +{ + return(led_count); +} + +std::string SinowealthController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev_cmd, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string SinowealthController::GetFirmwareVersion() +{ + unsigned char usb_buf[SINOWEALTH_COMMAND_REPORT_SIZE + 1]; // Additional byte for null-terminator + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0] = 5; + usb_buf[1] = 1; + + int ret = hid_send_feature_report(dev_cmd, usb_buf, SINOWEALTH_COMMAND_REPORT_SIZE); + if(ret < 0) return(""); + + usb_buf[1] = 0; + ret = hid_get_feature_report(dev_cmd, usb_buf, SINOWEALTH_COMMAND_REPORT_SIZE); + if(ret < 0) return(""); + + return std::string(reinterpret_cast(usb_buf) + 2); // Skip report and command byte +} + +void SinowealthController::SetMode + ( + unsigned char mode, + unsigned char speed, + unsigned char brightness, + unsigned char direction, + RGBColor* color_buf + ) +{ + if (GetProfile() < SINOWEALTH_CONFIG_SIZE_MIN) return; + + unsigned char usb_buf[SINOWEALTH_CONFIG_REPORT_SIZE]; + memcpy(usb_buf, device_configuration, SINOWEALTH_CONFIG_SIZE); // Yes, we only copy 167 bytes back, for now - if anything weird starts happening use SINOWEALTH_CONFIG_REPORT_SIZE + + usb_buf[0x03] = 0x7B; //write to device + usb_buf[0x06] = 0x00; + + usb_buf[0x35] = mode; + + switch (mode) + { + case GLORIOUS_MODE_RAINBOW: + usb_buf[0x36] = ((brightness & 0xF) << 4) | (speed & 0xF); + usb_buf[0x37] = direction; + break; + case GLORIOUS_MODE_STATIC: + usb_buf[0x38] = ((brightness & 0xF) << 4); + usb_buf[0x39] = RGBGetRValue(color_buf[0]); + usb_buf[0x3A] = RGBGetBValue(color_buf[0]); + usb_buf[0x3B] = RGBGetGValue(color_buf[0]); + break; + case GLORIOUS_MODE_SPECTRUM_BREATING: + //colours not yet researched + usb_buf[0x3C] = ((brightness & 0xF) << 4) | (speed & 0xF); + usb_buf[0x3D] = 0x07; //maybe some kind of bank change?+ + //usb_buf[0x3D] = 0x06; + usb_buf[0x3E] = RGBGetRValue(color_buf[0]); //mode 3 red 1 + usb_buf[0x3F] = RGBGetBValue(color_buf[0]); //mode 3 blue 1 + usb_buf[0x40] = RGBGetGValue(color_buf[0]); //mode 3 green 1 + usb_buf[0x41] = RGBGetRValue(color_buf[1]); //mode 3 red 2 + usb_buf[0x42] = RGBGetBValue(color_buf[1]); //mode 3 blue 2 + usb_buf[0x43] = RGBGetGValue(color_buf[1]); //mode 3 green 2 + usb_buf[0x44] = RGBGetRValue(color_buf[2]); //mode 3 red 3 + usb_buf[0x45] = RGBGetBValue(color_buf[2]); //mode 3 blue 3 + usb_buf[0x46] = RGBGetGValue(color_buf[2]); //mode 3 green 3 + usb_buf[0x47] = RGBGetRValue(color_buf[3]); //mode 3 red 4 + usb_buf[0x48] = RGBGetBValue(color_buf[3]); //mode 3 blue 4 + usb_buf[0x49] = RGBGetGValue(color_buf[3]); //mode 3 green 4 + usb_buf[0x4A] = RGBGetRValue(color_buf[4]); //mode 3 red 5 + usb_buf[0x4B] = RGBGetBValue(color_buf[4]); //mode 3 blue 5 + usb_buf[0x4C] = RGBGetGValue(color_buf[4]); //mode 3 green 5 + usb_buf[0x4D] = RGBGetRValue(color_buf[5]); //mode 3 red 6 + usb_buf[0x4E] = RGBGetBValue(color_buf[5]); //mode 3 blue 6 + usb_buf[0x4F] = RGBGetGValue(color_buf[5]); //mode 3 green 6 + usb_buf[0x50] = RGBGetRValue(color_buf[6]); //mode 3 red 7 + usb_buf[0x51] = RGBGetBValue(color_buf[6]); //mode 3 blue 7 + usb_buf[0x52] = RGBGetGValue(color_buf[6]); //mode 3 green 7 + break; + case GLORIOUS_MODE_TAIL: + usb_buf[0x53] = ((brightness & 0xF) << 4) | (speed & 0xF); + break; + case GLORIOUS_MODE_SPECTRUM_CYCLE: + usb_buf[0x54] = ((brightness & 0xF) << 4) | (speed & 0xF); + break; + case GLORIOUS_MODE_RAVE: + usb_buf[0x74] = ((brightness & 0xF) << 4) | (speed & 0xF); + usb_buf[0x75] = RGBGetRValue(color_buf[0]); //mode 7 red 1 + usb_buf[0x76] = RGBGetBValue(color_buf[0]); //mode 7 blue 1 + usb_buf[0x77] = RGBGetGValue(color_buf[0]); //mode 7 green 1 + usb_buf[0x78] = RGBGetRValue(color_buf[1]); //mode 7 red 2 + usb_buf[0x79] = RGBGetBValue(color_buf[1]); //mode 7 blue 2 + usb_buf[0x7A] = RGBGetGValue(color_buf[1]); //mode 7 green 2 + break; + case GLORIOUS_MODE_WAVE: + usb_buf[0x7C] = ((brightness & 0xF) << 4) | (speed & 0xF); + break; + case GLORIOUS_MODE_BREATHING: + usb_buf[0x7D] = ((brightness & 0xF) << 4) | (speed & 0xF); + usb_buf[0x7E] = RGBGetRValue(color_buf[0]); //mode 0a red + usb_buf[0x7F] = RGBGetBValue(color_buf[0]); //mode 0a blue + usb_buf[0x80] = RGBGetGValue(color_buf[0]); //mode 0a green + case GLORIOUS_MODE_OFF: + usb_buf[0x81] = 0x00; //mode 0 either 0x00 or 0x03 + break; + default: + break; + } + + hid_send_feature_report(dev_data, usb_buf, SINOWEALTH_CONFIG_REPORT_SIZE); +} + +int SinowealthController::GetProfile() +{ + int actual; + unsigned char usb_buf[SINOWEALTH_COMMAND_REPORT_SIZE]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0] = 0x05; + usb_buf[1] = 0x11; + + actual = hid_send_feature_report(dev_cmd, usb_buf, sizeof(usb_buf)); + + if (actual != SINOWEALTH_COMMAND_REPORT_SIZE) + { + LOG_ERROR("[Sinowealth Mouse] Error sending read request!"); + return -1; + } + else + { + memset(device_configuration, 0x00, SINOWEALTH_CONFIG_REPORT_SIZE); + device_configuration[0] = 0x04; + + actual = hid_get_feature_report(dev_data, device_configuration, SINOWEALTH_CONFIG_REPORT_SIZE); + + if (actual < 0) + { + LOG_ERROR("[Sinowealth Mouse] Error reading device configuration!"); + } + } + + return actual; +} diff --git a/Controllers/SinowealthController/SinowealthController/SinowealthController.h b/Controllers/SinowealthController/SinowealthController/SinowealthController.h new file mode 100644 index 0000000..878e926 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthController/SinowealthController.h @@ -0,0 +1,79 @@ +/*---------------------------------------------------------*\ +| SinowealthController.h | +| | +| Driver for Sinowealth mice, including Glorious | +| | +| Niels Westphal (crashniels) 20 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define SINOWEALTH_CONFIG_SIZE 167 +#define SINOWEALTH_CONFIG_SIZE_MIN 131 +#define SINOWEALTH_CONFIG_REPORT_SIZE 520 +#define SINOWEALTH_COMMAND_REPORT_SIZE 6 + +enum +{ + GLORIOUS_MODE_OFF = 0x00, //does nothing + GLORIOUS_MODE_RAINBOW = 0x01, + GLORIOUS_MODE_STATIC = 0x02, + GLORIOUS_MODE_SPECTRUM_BREATING = 0x03, + GLORIOUS_MODE_TAIL = 0x04, + GLORIOUS_MODE_SPECTRUM_CYCLE = 0x05, + GLORIOUS_MODE_RAVE = 0x07, + GLORIOUS_MODE_EPILEPSY = 0x08, //not in the official software + GLORIOUS_MODE_WAVE = 0x09, + GLORIOUS_MODE_BREATHING = 0x0A, +}; + +enum +{ + GLORIOUS_SPEED_SLOW = 0x01, + GLORIOUS_SPEED_NORMAL = 0x02, + GLORIOUS_SPEED_FAST = 0x03, +}; + +enum +{ + GLORIOUS_BRIGHTNESS_LOW = 0x01, + GLORIOUS_BRIGHTNESS_NORMAL = 0x02, + GLORIOUS_BRIGHTNESS_HIGH = 0x04, +}; + +enum +{ + GLORIOUS_DIRECTION_DOWN = 0x00, + GLORIOUS_DIRECTION_UP = 0x01, +}; + +class SinowealthController +{ +public: + SinowealthController(hid_device* dev_data_handle, hid_device* dev_cmd_handle, char *_path, std::string dev_name); //RGB, Command, path + ~SinowealthController(); + + unsigned int GetLEDCount(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + std::string GetFirmwareVersion(); + + void SetMode(unsigned char mode, unsigned char speed, unsigned char brightness, unsigned char direction, RGBColor* color_buf); + int GetProfile(); + +private: + hid_device* dev_cmd; + hid_device* dev_data; + unsigned int led_count; + unsigned char device_configuration[SINOWEALTH_CONFIG_REPORT_SIZE]; + std::string location; + std::string name; +}; diff --git a/Controllers/SinowealthController/SinowealthControllerDetect.cpp b/Controllers/SinowealthController/SinowealthControllerDetect.cpp new file mode 100644 index 0000000..1fe41e2 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthControllerDetect.cpp @@ -0,0 +1,485 @@ +/*---------------------------------------------------------*\ +| SinowealthControllerDetect.cpp | +| | +| Detector for Sinowealth, Genesis and Everest brand Mice | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "RGBController_SinowealthKeyboard10c.h" +#include "SinowealthController.h" +#include "SinowealthController1007.h" +#include "SinowealthKeyboard10cController.h" +#include "SinowealthKeyboard10cDevices.h" +#include "SinowealthKeyboardController.h" // Disabled +#include "SinowealthKeyboard16Controller.h" // Disabled +#include "SinowealthKeyboard90Controller.h" +#include "SinowealthGMOWController.h" +#include "GenesisXenon200Controller.cpp" +#include "RGBController.h" +#include "RGBController_Sinowealth.h" +#include "RGBController_Sinowealth1007.h" +#include "RGBController_SinowealthKeyboard.h" // Disabled +#include "RGBController_SinowealthKeyboard16.h" // Disabled +#include "RGBController_SinowealthKeyboard90.h" +#include "RGBController_SinowealthGMOW.h" +#include "RGBController_GenesisXenon200.h" +#include +#include "LogManager.h" + +#define SINOWEALTH_VID 0x258A + +#define Glorious_Model_O_PID 0x0036 +#define Glorious_Model_OW_PID1 0x2022 // wireless +#define Glorious_Model_OW_PID2 0x2011 // when connected via cable +#define Glorious_Model_D_PID 0x0033 +#define Glorious_Model_DW_PID1 0x2023 // Wireless +#define Glorious_Model_DW_PID2 0x2012 // When connected via cable +#define Everest_GT100_PID 0x0029 +#define ZET_FURY_PRO_PID 0x1007 +#define Fl_Esports_F11_PID 0x0049 +#define RGB_KEYBOARD_0016PID 0x0016 +#define GENESIS_THOR_300_PID 0x0090 +#define GENESIS_XENON_200_PID 0x1007 +#define RGB_KEYBOARD_010CPID 0x010C + +/******************************************************************************************\ +* * +* DetectSinowealthControllers * +* * +* Tests the USB address to see if a Sinowealth controller exists there. * +* * +\******************************************************************************************/ + +#define MAX_EXPECTED_REPORT_SIZE 2048 + +struct expected_report +{ + unsigned int id; + unsigned int size; // Up to MAX_EXPECTED_REPORT_SIZE! + unsigned char* cmd_buf = nullptr; + unsigned int cmd_size; + hid_device* cmd_device = nullptr; + hid_device* device = nullptr; + unsigned char* response = nullptr; + + expected_report(unsigned int id, unsigned size) : id(id), size(size) {} + expected_report(unsigned int id, unsigned size, unsigned char* cmd_buf, unsigned int cmd_size) : id(id), size(size), cmd_buf(cmd_buf), cmd_size(cmd_size) {} +}; + +typedef std::vector expected_reports; + +static int GetDeviceCount(hid_device_info* info, unsigned int &device_count_total, unsigned int device_count_expected) +{ + hid_device_info* info_temp = info; + + while(info_temp) + { + if(info_temp->vendor_id == info->vendor_id // constant SINOWEALTH_VID + && info_temp->product_id == info->product_id // NON-constant + && info_temp->usage_page == info->usage_page) // constant 0xFF00 + { + device_count_total++; + } + info_temp = info_temp->next; + } + + /*----------------------------------------------------------------------*\ + | If we have an expected number and what's left is a multiple of it | + \*----------------------------------------------------------------------*/ + if(device_count_expected == 0 || device_count_total % device_count_expected == 0) + { + return true; + } + + return false; +} + +static bool DetectUsages(hid_device_info* info, std::string name, unsigned int device_count_expected, expected_reports& reports) +{ + hid_device_info* info_temp = info; + hid_device* device = nullptr; + + bool restart_flag = false; + unsigned int device_count = 0; + unsigned int device_count_total = 0; + unsigned char tmp_buf[MAX_EXPECTED_REPORT_SIZE]; + + /*-----------------------------------------------------------------------------------------------*\ + | Yeah, it might seem suboptimal to go over this list twice, but read this first: | + | Sinowealth controllers report many collections on the same interface, usage page and usage id | + | We can't know if detector was called for the 1st time (first collection), or 2nd, 3rd, etc... | + | Relying on pure luck in this question is... not the best approach IMO, so here's how it works: | + | 1. Count remaining devices with our expected VID + PID + Usage Page | + | 2. We know in advance how many collections currently expected device reports, so we compare | + | remaining amount with expected amount | + | 3. If remaining amount is a multiple of expected amount - we're on the first collection of one | + | of connected devices, and proceed with finding expected reports | + \*-----------------------------------------------------------------------------------------------*/ + if(!GetDeviceCount(info, device_count_total, device_count_expected)) + { + LOG_DEBUG("[%s] Detection stage skipped - devices left %d (expected %d) ", name.c_str(), device_count_total, device_count_expected); + reports.clear(); + return false; + } + + /*---------------------------------------------------------------*\ + | Check all devices provided in hid_device_info | + \*---------------------------------------------------------------*/ + while(info_temp) + { + /*----------------------------------------------------------------*\ + | If it's still our device | + \*----------------------------------------------------------------*/ + if(info_temp->vendor_id == info->vendor_id // constant SINOWEALTH_VID + && info_temp->product_id == info->product_id // NON-constant + && info_temp->usage_page == info->usage_page) // constant 0xFF00 + { + /*----------------------------------------------------------*\ + | Open current device to check if it has expected report IDs | + \*----------------------------------------------------------*/ + bool report_found = false; + device = hid_open_path(info_temp->path); + + if(!device) + { + LOG_ERROR("[%s] Couldn't open path \"HID: %s\", do we have enough permissions?", name.c_str(), info_temp->path); + reports.clear(); + return false; + } + + for(expected_report& report: reports) + { + /*-----------------------------------------------------------*\ + | We shouldn't do any checks if device is already found | + \*-----------------------------------------------------------*/ + if(report.device != nullptr) + { + continue; + } + + memset(tmp_buf, 0x00, sizeof(tmp_buf)); + tmp_buf[0] = report.id; + + /*--------------------------------------------------------------------------------------*\ + | If we need to send a command before requesting data, send it and flag the report | + | (DON'T TRY TO CREATE MORE THAN 1 EXPECTED REPORT SENDING COMMANDS) | + \*--------------------------------------------------------------------------------------*/ + if(report.cmd_buf != nullptr && report.cmd_device == nullptr) + { + if(hid_send_feature_report(device, report.cmd_buf, report.cmd_size) > -1) + { + restart_flag = true; // Because Windows + report.cmd_device = device; + LOG_TRACE("[%s] Successfully sent command for ReportId 0x%02X to device at location \"HID: %s\", handle: %08X", name.c_str(), report.id, info_temp->path, device); + } + } + + /*------------------------------------------------------*\ + | Now we try to request data for expected feature report | + \*------------------------------------------------------*/ + if(report.cmd_buf == nullptr || report.cmd_device != nullptr) + { + /*---------------------------------------------------------------------------*\ + | If device actually responds to expected report ID, set a flag | + \*---------------------------------------------------------------------------*/ + if(hid_get_feature_report(device, tmp_buf, report.size) > -1) + { + device_count++; + report_found = true; + report.device = device; + + report.response = new unsigned char[report.size]; + std::memcpy(report.response, tmp_buf, report.size); + + LOG_TRACE("[%s] Successfully requested feature ReportId 0x%02X from device at location \"HID: %s\", handle: %08X", name.c_str(), report.id, info_temp->path, device); + } + } + } + + /*-----------------------------------------------------------*\ + | If it doesn't - make sure to close it! | + | Don't close if restart flag is set because we found cmd_dev | + \*-----------------------------------------------------------*/ + if(!report_found && !restart_flag) hid_close(device); + } + + info_temp = restart_flag ? info : info_temp->next; + restart_flag = false; + + /*-------------------------------------------------------------------------*\ + | If we found everything we expected, stop going through devices list | + | We don't want to go too far in case there are multiple Sinowealth devices | + | with the same VID & PID | + | (I don't care how unlikely it is, we must be prepared for everything) | + \*-------------------------------------------------------------------------*/ + if(device_count == reports.size()) info_temp = nullptr; + } + + /*-----------------------------------------------------------*\ + | If we found less devices than expected - sad, lets clean up | + \*-----------------------------------------------------------*/ + if(device_count < reports.size()) + { + for(expected_report& report: reports) + { + if(report.response != nullptr) + { + delete[] report.response; + report.response = nullptr; + } + if(report.device != nullptr) + { + hid_close(report.device); + } + } + + reports.clear(); + return false; + } + + return true; +} + +static void DetectGenesisXenon200(hid_device_info* info, const std::string name) +{ + expected_reports reports{expected_report(0x04, 154), expected_report(0x08, 9)}; + if(!DetectUsages(info, name, 5, reports)) + { + return; + } + + hid_device* dev = reports.at(0).device; + hid_device* cmd_dev = reports.at(1).device; + + GenesisXenon200Controller* controller = new GenesisXenon200Controller(dev, cmd_dev, info->path, name); + RGBController* rgb_controller = new RGBController_GenesisXenon200(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + +} + +static void DetectZetFuryPro(hid_device_info* info, const std::string& name) +{ +#ifdef USE_HID_USAGE + expected_reports reports{expected_report(0x04, 59)}; + if(!DetectUsages(info, name, 5, reports)) + { + return; + } + hid_device* dev = reports.at(0).device; +#else + hid_device* dev = hid_open_path(info->path); +#endif + + if(dev) + { + SinowealthController1007* controller = new SinowealthController1007(dev, info->path, name); + RGBController_Sinowealth1007* rgb_controller = new RGBController_Sinowealth1007(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +static void DetectSinowealthMouse(hid_device_info* info, const std::string& name) +{ +#ifdef USE_HID_USAGE + unsigned char command[6] = {0x05, 0x11, 0x00, 0x00, 0x00, 0x00}; + expected_reports reports{expected_report(0x04, 520, command, sizeof(command))}; + + if(!DetectUsages(info, name, 3, reports)) + { + return; + } + + hid_device *dev = reports.at(0).device; + hid_device *dev_cmd = reports.at(0).cmd_device; +#else + hid_device* dev = hid_open_path(info->path); + hid_device* dev_cmd = dev; +#endif + + if(dev && dev_cmd) + { + SinowealthController* controller = new SinowealthController(dev, dev_cmd, info->path, name); + RGBController_Sinowealth* rgb_controller = new RGBController_Sinowealth(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +static void DetectGMOW_Cable(hid_device_info* info, const std::string& name) +{ + LOG_DEBUG("[%s] Detected connection via USB cable", name.c_str()); + hid_device *dev = hid_open_path(info->path); + + if(dev) + { + SinowealthGMOWController* controller = new SinowealthGMOWController(dev, info->path, GMOW_CABLE_CONNECTED, name); + RGBController_GMOW* rgb_controller = new RGBController_GMOW(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +static void DetectGMOW_Dongle(hid_device_info* info, const std::string& name) +{ + /*-------------------------------------------------------------------------*\ + | When the GMOW is connected only via the wireless dongle, only one | + | device shows up (PID=2022), and RGB packets go to that device. | + | Same for when it is only plugged in via a cable but not a dongle (except | + | the device is PID=2011). However, when both are plugged in, packets | + | should only go to the cable connected device | + \*-------------------------------------------------------------------------*/ + LOG_DEBUG("[%s] Detected connection via wireless dongle", name.c_str()); + hid_device_info* start = hid_enumerate(SINOWEALTH_VID,0); + hid_device_info* curr = start; + + while(curr) + { + if(curr->product_id == Glorious_Model_OW_PID2 || curr->product_id == Glorious_Model_DW_PID2) + { + return; + } + curr = curr->next; + } + hid_free_enumeration(start); + + hid_device *dev = hid_open_path(info->path); + + if(dev) + { + SinowealthGMOWController* controller = new SinowealthGMOWController(dev, info->path, GMOW_DONGLE_CONNECTED, name); + RGBController_GMOW* rgb_controller = new RGBController_GMOW(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +// static void DetectSinowealthKeyboard16(hid_device_info* info, const std::string& name) +// { +// #ifdef USE_HID_USAGE +// unsigned char command[6] = {0x05, 0x83, 0x00, 0x00, 0x00, 0x00}; +// expected_reports reports{expected_report(0x06, 1032, command, sizeof(command))}; +// if(!DetectUsages(info, name, 3, reports)) +// { +// return; +// } +// hid_device *dev = reports.at(0).device; +// hid_device *dev_cmd = reports.at(0).cmd_device; +// #else +// hid_device* dev = hid_open_path(info->path); +// hid_device* dev_cmd = dev; +// #endif +// if(dev && dev_cmd) +// { +// SinowealthKeyboard16Controller* controller = new SinowealthKeyboard16Controller(dev_cmd, dev, info->path, name); +// RGBController_SinowealthKeyboard16* rgb_controller = new RGBController_SinowealthKeyboard16(controller); +// +// ResourceManager::get()->RegisterRGBController(rgb_controller); +// } +// } + +// static void DetectSinowealthKeyboard(hid_device_info* info, const std::string& name) +// { +// #ifdef USE_HID_USAGE +// unsigned char command[6] = {0x05, 0x83, 0xB6, 0x00, 0x00, 0x00}; +// expected_reports reports{expected_report(0x06, 1032, command, sizeof(command))}; +// if(!DetectUsages(info, name, 3, reports)) +// { +// return; +// } +// +// hid_device *dev = reports.at(0).device; +// hid_device *dev_cmd = reports.at(0).cmd_device; +// +// if(dev && dev_cmd) +// { +// SinowealthKeyboardController* controller = new SinowealthKeyboardController(dev_cmd, dev, info->path, name); +// RGBController_SinowealthKeyboard* rgb_controller = new RGBController_SinowealthKeyboard(controller); +// +// ResourceManager::get()->RegisterRGBController(rgb_controller); +// } +// #else +// // It is unknown why this code used the MOUSE controller here; could it be the reason why it was disabled? +// hid_device* dev = hid_open_path(info->path); +// +// if(dev) +// { +// SinowealthController* controller = new SinowealthController(dev, dev, info->path, name); +// RGBController_Sinowealth* rgb_controller = new RGBController_Sinowealth(controller); +// +// ResourceManager::get()->RegisterRGBController(rgb_controller); +// } +// #endif +// } + +static void DetectSinowealthGenesisKeyboard(hid_device_info* info, const std::string& name) +{ + unsigned int pid = info->product_id; + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SinowealthKeyboard90Controller* controller = new SinowealthKeyboard90Controller(dev, info->path, pid, name); + RGBController_SinowealthKeyboard90* rgb_controller = new RGBController_SinowealthKeyboard90(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +static void DetectSinowealthKeyboard10c(hid_device_info* info, const std::string& name) +{ + unsigned char command[7] = {0x06, 0x82, 0x01, 0x00, 0x01, 0x00, 0x06}; + expected_reports reports{expected_report(0x06, 520, command, 520)}; + + if(!DetectUsages(info, name, 3, reports)) + { + return; + } + + hid_device *dev = reports.at(0).device; + unsigned char model_id = reports.at(0).response[13]; + + if(dev && sinowealth_10c_keyboards.find(model_id) != sinowealth_10c_keyboards.end()) + { + SinowealthKeyboard10cController* controller = new SinowealthKeyboard10cController(dev, info->path, sinowealth_10c_keyboards.at(model_id).device_name); + RGBController_SinowealthKeyboard10c* rgb_controller = new RGBController_SinowealthKeyboard10c(controller, model_id); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +#ifdef USE_HID_USAGE +REGISTER_HID_DETECTOR_P("Glorious Model O / O-", DetectSinowealthMouse, SINOWEALTH_VID, Glorious_Model_O_PID, 0xFF00 ); +REGISTER_HID_DETECTOR_P("Glorious Model D / D-", DetectSinowealthMouse, SINOWEALTH_VID, Glorious_Model_D_PID, 0xFF00 ); +REGISTER_HID_DETECTOR_P("Everest GT-100 RGB", DetectSinowealthMouse, SINOWEALTH_VID, Everest_GT100_PID, 0xFF00 ); +REGISTER_HID_DETECTOR_IPU("ZET Fury Pro", DetectZetFuryPro, SINOWEALTH_VID, ZET_FURY_PRO_PID, 1, 0xFF00, 1 ); +REGISTER_HID_DETECTOR_PU("Glorious Model O / O- Wireless", DetectGMOW_Dongle, SINOWEALTH_VID, Glorious_Model_OW_PID1, 0xFFFF, 1 ); +REGISTER_HID_DETECTOR_PU("Glorious Model O / O- Wireless", DetectGMOW_Cable, SINOWEALTH_VID, Glorious_Model_OW_PID2, 0xFFFF, 0x0000 ); +REGISTER_HID_DETECTOR_PU("Glorious Model D / D- Wireless", DetectGMOW_Dongle, SINOWEALTH_VID, Glorious_Model_DW_PID1, 0xFFFF, 0x0000 ); +REGISTER_HID_DETECTOR_PU("Glorious Model D / D- Wireless", DetectGMOW_Cable, SINOWEALTH_VID, Glorious_Model_DW_PID2, 0xFFFF, 0x0000 ); +REGISTER_HID_DETECTOR_PU("Genesis Xenon 200", DetectGenesisXenon200, SINOWEALTH_VID, GENESIS_XENON_200_PID, 0xFF00, 1 ); +REGISTER_HID_DETECTOR_IPU("Genesis Thor 300", DetectSinowealthGenesisKeyboard, SINOWEALTH_VID, GENESIS_THOR_300_PID, 1, 0xFF00, 1 ); +REGISTER_HID_DETECTOR_IPU("Sinowealth Keyboard", DetectSinowealthKeyboard10c, SINOWEALTH_VID, RGB_KEYBOARD_010CPID, 1, 0xFF00, 1 ); + +// Sinowealth keyboards are disabled due to VID/PID pairs being reused from Redragon keyboards, which ended up in bricking the latter +//REGISTER_HID_DETECTOR_P("FL ESPORTS F11", DetectSinowealthKeyboard, SINOWEALTH_VID, Fl_Esports_F11_PID, 0xFF00 ); +//REGISTER_HID_DETECTOR_P("Sinowealth Keyboard", DetectSinowealthKeyboard16, SINOWEALTH_VID, RGB_KEYBOARD_0016PID, 0xFF00 ); +#else +REGISTER_HID_DETECTOR_I("Glorious Model O / O-", DetectSinowealthMouse, SINOWEALTH_VID, Glorious_Model_O_PID, 1); +REGISTER_HID_DETECTOR_I("Glorious Model D / D-", DetectSinowealthMouse, SINOWEALTH_VID, Glorious_Model_D_PID, 1); +REGISTER_HID_DETECTOR_I("Everest GT-100 RGB", DetectSinowealthMouse, SINOWEALTH_VID, Everest_GT100_PID, 1); +REGISTER_HID_DETECTOR_I("ZET Fury Pro", DetectZetFuryPro, SINOWEALTH_VID, ZET_FURY_PRO_PID, 1); +REGISTER_HID_DETECTOR_I("Glorious Model O / O- Wireless", DetectGMOW_Dongle, SINOWEALTH_VID, Glorious_Model_OW_PID1, 1); +REGISTER_HID_DETECTOR_I("Glorious Model O / O- Wireless", DetectGMOW_Cable, SINOWEALTH_VID, Glorious_Model_OW_PID2, 2); +REGISTER_HID_DETECTOR_I("Glorious Model D / D- Wireless", DetectGMOW_Dongle, SINOWEALTH_VID, Glorious_Model_DW_PID1, 2); +REGISTER_HID_DETECTOR_I("Glorious Model D / D- Wireless", DetectGMOW_Cable, SINOWEALTH_VID, Glorious_Model_DW_PID2, 2); +REGISTER_HID_DETECTOR_I("Genesis Xenon 200", DetectGenesisXenon200, SINOWEALTH_VID, GENESIS_XENON_200_PID, 1); +REGISTER_HID_DETECTOR_I("Genesis Thor 300", DetectSinowealthGenesisKeyboard, SINOWEALTH_VID, GENESIS_THOR_300_PID, 1); + +//REGISTER_HID_DETECTOR_I("FL ESPORTS F11", DetectSinowealthKeyboard, SINOWEALTH_VID, Fl_Esports_F11_PID, 1); +//REGISTER_HID_DETECTOR_I("Sinowealth Keyboard", DetectSinowealthKeyboard16, SINOWEALTH_VID, RGB_KEYBOARD_0016PID, 1); +#endif diff --git a/Controllers/SinowealthController/SinowealthGMOWController/RGBController_SinowealthGMOW.cpp b/Controllers/SinowealthController/SinowealthGMOWController/RGBController_SinowealthGMOW.cpp new file mode 100644 index 0000000..42feeb3 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthGMOWController/RGBController_SinowealthGMOW.cpp @@ -0,0 +1,193 @@ +/*---------------------------------------------------------*\ +| RGBController_SinowealthGMOW.cpp | +| | +| RGBController for Glorious Model O Wireless | +| | +| Matt Silva (thesilvanator) May 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +/**------------------------------------------------------------------*\ + @name Sinowealth Glorious Model O Wireless + @type USB + @save :white_check_mark: + @direct :x: + @effects :white_check_mark: + @detectors DetectSinowealthMouse + @comment +\*-------------------------------------------------------------------*/ + +#include "RGBController_SinowealthGMOW.h" + +RGBController_GMOW::RGBController_GMOW(SinowealthGMOWController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + type = DEVICE_TYPE_MOUSE; + description = "Sinowealth Device"; + location = controller->GetLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Off; + Off.name = "Off"; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + Off.value = GMOW_MODE_OFF; + modes.push_back(Off); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RainbowWave.speed_min = GMOW_SPEED1_MIN; + RainbowWave.speed = GMOW_SPEED1_MID; + RainbowWave.speed_max = GMOW_SPEED1_MAX; + RainbowWave.direction = MODE_DIRECTION_UP; + RainbowWave.color_mode = MODE_COLORS_NONE; + RainbowWave.brightness_min = GMOW_BRIGHTNESS_MIN; + RainbowWave.brightness = GMOW_BRIGHTNESS_MID; + RainbowWave.brightness_max = GMOW_BRIGHTNESS_MAX; + RainbowWave.value = GMOW_MODE_RAINBOW_WAVE; + modes.push_back(RainbowWave); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SpectrumCycle.speed_min = GMOW_SPEED1_MIN; + SpectrumCycle.speed = GMOW_SPEED1_MID; + SpectrumCycle.speed_max = GMOW_SPEED1_MAX; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.brightness_min = GMOW_BRIGHTNESS_MIN; + SpectrumCycle.brightness = GMOW_BRIGHTNESS_MID; + SpectrumCycle.brightness_max = GMOW_BRIGHTNESS_MAX; + SpectrumCycle.value = GMOW_MODE_SPECTRUM_CYCLE; + modes.push_back(SpectrumCycle); + + mode CustomBreathing; + CustomBreathing.name = "Custom Breathing"; + CustomBreathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + CustomBreathing.speed_min = GMOW_SPEED1_MIN; + CustomBreathing.speed = GMOW_SPEED1_MID; + CustomBreathing.speed_max = GMOW_SPEED1_MAX; + CustomBreathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + CustomBreathing.colors_min = 2; + CustomBreathing.colors_max = 7; + CustomBreathing.brightness_min = GMOW_BRIGHTNESS_MIN; + CustomBreathing.brightness = GMOW_BRIGHTNESS_MID; + CustomBreathing.brightness_max = GMOW_BRIGHTNESS_MAX; + CustomBreathing.value = GMOW_MODE_CUSTOM_BREATHING; + CustomBreathing.colors.resize(7); + modes.push_back(CustomBreathing); + + mode Static; + Static.name = "Static"; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness_min = GMOW_BRIGHTNESS_MIN; + Static.brightness = GMOW_BRIGHTNESS_MID; + Static.brightness_max = GMOW_BRIGHTNESS_MAX; + Static.value = GMOW_MODE_STATIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.speed_min = GMOW_SPEED1_MIN; + Breathing.speed = GMOW_SPEED1_MID; + Breathing.speed_max = GMOW_SPEED1_MAX; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.brightness_min = GMOW_BRIGHTNESS_MIN; + Breathing.brightness = GMOW_BRIGHTNESS_MID; + Breathing.brightness_max = GMOW_BRIGHTNESS_MAX; + Breathing.value = GMOW_MODE_BREATHING; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Tail; + Tail.name = "Tail"; + Tail.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Tail.speed_min = GMOW_SPEED1_MIN; + Tail.speed = GMOW_SPEED1_MID; + Tail.speed_max = GMOW_SPEED1_MAX; + Tail.color_mode = MODE_COLORS_NONE; + Tail.brightness_min = GMOW_BRIGHTNESS_MIN; + Tail.brightness = GMOW_BRIGHTNESS_MID; + Tail.brightness_max = GMOW_BRIGHTNESS_MAX; + Tail.value = GMOW_MODE_TAIL; + modes.push_back(Tail); + + mode Rave; + Rave.name = "Rave"; + Rave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Rave.speed_min = GMOW_SPEED2_MIN; + Rave.speed = GMOW_SPEED2_MID; + Rave.speed_max = GMOW_SPEED2_MAX; + Rave.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rave.colors_min = 1; + Rave.colors_max = 2; + Rave.brightness_min = GMOW_BRIGHTNESS_MIN; + Rave.brightness = GMOW_BRIGHTNESS_MID; + Rave.brightness_max = GMOW_BRIGHTNESS_MAX; + Rave.value = GMOW_MODE_RAVE; + Rave.colors.resize(2); + modes.push_back(Rave); + + mode Wave; + Wave.name = "Wave"; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Wave.speed_min = GMOW_SPEED2_MIN; + Wave.speed = GMOW_SPEED2_MID; + Wave.speed_max = GMOW_SPEED2_MAX; + Wave.color_mode = MODE_COLORS_NONE; + Wave.brightness_min = GMOW_BRIGHTNESS_MIN; + Wave.brightness = GMOW_BRIGHTNESS_MID; + Wave.brightness_max = GMOW_BRIGHTNESS_MAX; + Wave.value = GMOW_MODE_WAVE; + modes.push_back(Wave); + + SetupZones(); +} + +RGBController_GMOW::~RGBController_GMOW() +{ + delete controller; +} + +void RGBController_GMOW::SetupZones() +{ + +} + +void RGBController_GMOW::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_GMOW::DeviceUpdateLEDs() +{ + +} + +void RGBController_GMOW::UpdateZoneLEDs(int /*zone*/) +{ + +} + +void RGBController_GMOW::UpdateSingleLED(int /*led*/) +{ + +} + +void RGBController_GMOW::DeviceUpdateMode() +{ + mode curr = modes[active_mode]; + controller->SetMode(active_mode, curr.speed,curr.brightness, curr.brightness, curr.colors.data(), (unsigned char)curr.colors.size()); +} diff --git a/Controllers/SinowealthController/SinowealthGMOWController/RGBController_SinowealthGMOW.h b/Controllers/SinowealthController/SinowealthGMOWController/RGBController_SinowealthGMOW.h new file mode 100644 index 0000000..dd09b0c --- /dev/null +++ b/Controllers/SinowealthController/SinowealthGMOWController/RGBController_SinowealthGMOW.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_SinowealthGMOW.h | +| | +| RGBController for Glorious Model O Wireless | +| | +| Matt Silva (thesilvanator) May 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SinowealthGMOWController.h" + +class RGBController_GMOW : public RGBController +{ +public: + RGBController_GMOW(SinowealthGMOWController* sinowealth_ptr); + ~RGBController_GMOW(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SinowealthGMOWController* controller; +}; diff --git a/Controllers/SinowealthController/SinowealthGMOWController/SinowealthGMOWController.cpp b/Controllers/SinowealthController/SinowealthGMOWController/SinowealthGMOWController.cpp new file mode 100644 index 0000000..2826e6f --- /dev/null +++ b/Controllers/SinowealthController/SinowealthGMOWController/SinowealthGMOWController.cpp @@ -0,0 +1,241 @@ +/*---------------------------------------------------------*\ +| SinowealthGMOWController.cpp | +| | +| Driver for Glorious Model O Wireless | +| | +| Matt Silva (thesilvanator) May 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "LogManager.h" +#include "SinowealthGMOWController.h" +#include "StringUtils.h" + +SinowealthGMOWController::SinowealthGMOWController(hid_device* dev_handle, char *_path, int _type, std::string dev_name) +{ + dev = dev_handle; + location = _path; + name = dev_name; + type = _type; + + memset(mode_packet,0x00, GMOW_PACKET_SIZE); + mode_packet[0x03] = 0x02; + mode_packet[0x05] = 0x02; + mode_packet[0x07] = 0x01; + mode_packet[0x08] = 0xFF; + + memset(wired_packet,0x00, GMOW_PACKET_SIZE); + wired_packet[0x03] = 0x02; + wired_packet[0x04] = 0x02; + wired_packet[0x05] = 0x02; + wired_packet[0x06] = 0x02; + wired_packet[0x07] = 0x01; + + memcpy(less_packet, wired_packet, GMOW_PACKET_SIZE); + less_packet[0x07] = 0x00; +} + +SinowealthGMOWController::~SinowealthGMOWController() +{ + hid_close(dev); +} + +std::string SinowealthGMOWController::GetLocation() +{ + return("HID: " + location); +} + +std::string SinowealthGMOWController::GetName() +{ + return(name); +} + +std::string SinowealthGMOWController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string SinowealthGMOWController::GetFirmwareVersion() +{ + using namespace std::chrono_literals; + + unsigned char buf_send[GMOW_PACKET_SIZE] = {0}; + unsigned char buf_receive[GMOW_PACKET_SIZE] = {0}; + + if(type == GMOW_CABLE_CONNECTED) + { + buf_send[0x03] = 0x02; + } + + buf_send[0x04] = 0x03; + buf_send[0x06] = 0x81; + + hid_send_feature_report(dev,buf_send, GMOW_PACKET_SIZE); + + std::this_thread::sleep_for(50ms); + + hid_get_feature_report(dev,buf_receive,GMOW_PACKET_SIZE); + + char str[128] = {0}; + snprintf(str,128,"%d.%d.%d.%d", buf_receive[7], + buf_receive[8], + buf_receive[9], + buf_receive[10]); + + return std::string(str); +} + +void SinowealthGMOWController::SetMode(unsigned char mode, + unsigned char speed, + unsigned char wired_brightness, + unsigned char less_brightness, + RGBColor* color_buf, + unsigned char color_count) +{ + using namespace std::chrono_literals; + + unsigned char mode_buff[GMOW_PACKET_SIZE]; + memcpy(mode_buff, mode_packet, GMOW_PACKET_SIZE); + mode_buff[0x09] = mode; + + unsigned char wired_buff[GMOW_PACKET_SIZE]; + memcpy(wired_buff, wired_packet, GMOW_PACKET_SIZE); + wired_buff[0x08] = wired_brightness; + + unsigned char less_buff[GMOW_PACKET_SIZE]; + memcpy(less_buff, less_packet, GMOW_PACKET_SIZE); + less_buff[0x08] = less_brightness; + + + switch (mode) + { + case GMOW_MODE_RAINBOW_WAVE: + mode_buff[0x04] = 0x05; + mode_buff[0x0B] = speed; + break; + case GMOW_MODE_SPECTRUM_CYCLE: + mode_buff[0x04] = 0x05; + mode_buff[0x0B] = speed; + mode_buff[0x0C] = 0xFF; + break; + case GMOW_MODE_CUSTOM_BREATHING: + { + mode_buff[0x04] = color_count * 3 + 5; + mode_buff[0x0B] = speed; + + for(unsigned int i = 0; i < color_count; i++) + { + unsigned char r = (char)RGBGetRValue(color_buf[i]); + unsigned char g = (char)RGBGetGValue(color_buf[i]); + unsigned char b = (char)RGBGetBValue(color_buf[i]); + + if(r == 0x00 && g == 0x00 && b == 0x00) + { + r = 0x01; + } + + mode_buff[0x0C + 3*i + 0] = r; + mode_buff[0x0C + 3*i + 1] = g; + mode_buff[0x0C + 3*i + 2] = b; + } + + } + break; + case GMOW_MODE_STATIC: + { + mode_buff[0x04] = 0x05; + mode_buff[0x0B] = 0x09; // rate is replaced with 9 + + unsigned char r = (char)RGBGetRValue(color_buf[0]); + unsigned char g = (char)RGBGetGValue(color_buf[0]); + unsigned char b = (char)RGBGetBValue(color_buf[0]); + + if(r == 0 && g == 0 && b == 0) + { + r = 1; + } + + mode_buff[0x0C] = r; + mode_buff[0x0D] = g; + mode_buff[0x0E] = b; + } + break; + case GMOW_MODE_BREATHING: + { + mode_buff[0x04] = 0x08; + mode_buff[0x0B] = speed; + + unsigned char r = (char)RGBGetRValue(color_buf[0]); + unsigned char g = (char)RGBGetGValue(color_buf[0]); + unsigned char b = (char)RGBGetBValue(color_buf[0]); + + if(r == 0 && g == 0 && b == 0) + { + r = 1; + } + + mode_buff[0x0C] = r; + mode_buff[0x0D] = g; + mode_buff[0x0E] = b; + } + break; + case GMOW_MODE_TAIL: + mode_buff[0x04] = 0x05; + mode_buff[0x0B] = speed; + break; + case GMOW_MODE_RAVE: + { + mode_buff[0x04] = color_count * 3 + 5; + mode_buff[0x0B] = speed; + + for(unsigned int i = 0; i < color_count; i++) { + unsigned char r = (char)RGBGetRValue(color_buf[i]); + unsigned char g = (char)RGBGetGValue(color_buf[i]); + unsigned char b = (char)RGBGetBValue(color_buf[i]); + + if(r == 0 && g == 0 && b == 0) + { + r = 1; + } + + mode_buff[0x0C + 3*i + 0] = r; + mode_buff[0x0C + 3*i + 1] = g; + mode_buff[0x0C + 3*i + 2] = b; + } + } + break; + case GMOW_MODE_WAVE: + mode_buff[0x04] = 0x05; + mode_buff[0x0B] = speed; + break; + case GMOW_MODE_OFF: + mode_buff[0x04] = 0x05; + mode_buff[0x0B] = 0x09; + break; + default: + break; + } + + std::this_thread::sleep_for(50ms); + hid_send_feature_report(dev, mode_buff, GMOW_PACKET_SIZE); + + std::this_thread::sleep_for(50ms); + hid_send_feature_report(dev, wired_buff, GMOW_PACKET_SIZE); + + std::this_thread::sleep_for(50ms); + hid_send_feature_report(dev, less_buff, GMOW_PACKET_SIZE); + + +} diff --git a/Controllers/SinowealthController/SinowealthGMOWController/SinowealthGMOWController.h b/Controllers/SinowealthController/SinowealthGMOWController/SinowealthGMOWController.h new file mode 100644 index 0000000..6d159a7 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthGMOWController/SinowealthGMOWController.h @@ -0,0 +1,87 @@ +/*---------------------------------------------------------*\ +| SinowealthGMOWController.h | +| | +| Driver for Glorious Model O Wireless | +| | +| Matt Silva (thesilvanator) May 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define GMOW_PACKET_SIZE 64 + 1 + +enum +{ + GMOW_MODE_OFF = 0x00, + GMOW_MODE_RAINBOW_WAVE = 0x01, + GMOW_MODE_SPECTRUM_CYCLE = 0x02, + GMOW_MODE_CUSTOM_BREATHING = 0x03, + GMOW_MODE_STATIC = 0x04, + GMOW_MODE_BREATHING = 0x05, + GMOW_MODE_TAIL = 0x06, + GMOW_MODE_RAVE = 0x07, + GMOW_MODE_WAVE = 0x08, +}; + +enum +{ + GMOW_SPEED1_MIN = 0x14, + GMOW_SPEED1_MID = 0x0B, + GMOW_SPEED1_MAX = 0x01, + + GMOW_SPEED2_MIN = 0xC8, + GMOW_SPEED2_MID = 0x6E, + GMOW_SPEED2_MAX = 0x0A, +}; + + +enum +{ + GMOW_BRIGHTNESS_MIN = 0x00, + GMOW_BRIGHTNESS_MID = 0x7F, + GMOW_BRIGHTNESS_MAX = 0xFF, +}; + +enum +{ + GMOW_CABLE_CONNECTED, + GMOW_DONGLE_CONNECTED +}; + + +class SinowealthGMOWController +{ +public: + SinowealthGMOWController(hid_device* dev_handle, char *_path, int type, std::string dev_name); + ~SinowealthGMOWController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + + void SetMode(unsigned char mode, + unsigned char speed, + unsigned char wired_brightness, + unsigned char less_brightness, + RGBColor* color_buf, + unsigned char color_count); + + std::string GetFirmwareVersion(); + +private: + hid_device* dev; + unsigned char mode_packet[GMOW_PACKET_SIZE]; + unsigned char wired_packet[GMOW_PACKET_SIZE]; + unsigned char less_packet[GMOW_PACKET_SIZE]; + + std::string location; + std::string name; + int type; +}; diff --git a/Controllers/SinowealthController/SinowealthKeyboard10cController/RGBController_SinowealthKeyboard10c.cpp b/Controllers/SinowealthController/SinowealthKeyboard10cController/RGBController_SinowealthKeyboard10c.cpp new file mode 100644 index 0000000..ba1a4bd --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard10cController/RGBController_SinowealthKeyboard10c.cpp @@ -0,0 +1,178 @@ +/*---------------------------------------------------------*\ +| RGBController_SinowealthKeyboard10c.cpp | +| | +| RGBController for Sinowealth Keyboards with PID 010C | +| | +| Rodrigo Tavares 27 Nov 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "KeyboardLayoutManager.h" +#include "RGBControllerKeyNames.h" +#include "SinowealthKeyboard10cController.h" +#include "SinowealthKeyboard10cDevices.h" +#include "RGBController_SinowealthKeyboard10c.h" + +using namespace kbd10c; +using namespace std::chrono_literals; + +/**------------------------------------------------------------------*\ + @name Sinowealth Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectSinowealthKeyboard10c + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SinowealthKeyboard10c::RGBController_SinowealthKeyboard10c( + SinowealthKeyboard10cController* controller_ptr, unsigned char model_id) + : model_id(model_id) +{ + controller = controller_ptr; + + name = controller->GetName(); + type = DEVICE_TYPE_KEYBOARD; + vendor = "Sinowealth"; + description = "Sinowealth Keyboard Device"; + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Off; + Off.name = "Off"; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + Off.value = MODE_OFF; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.value = MODE_DIRECT; + modes.push_back(Direct); + + active_mode = MODE_DIRECT; + + SetupZones(); + + /*---------------------------------------------------------*\ + | The Sinowealth 010C Keyboard requires a steady stream | + | of packets in order to not revert out of direct mode. | + | Start a thread to continuously refresh the device | + \*---------------------------------------------------------*/ + keepalive_thread_run = true; + keepalive_thread = new std::thread(&RGBController_SinowealthKeyboard10c::KeepaliveThreadFunction, this); +} + +RGBController_SinowealthKeyboard10c::~RGBController_SinowealthKeyboard10c() +{ + keepalive_thread_run = false; + keepalive_thread->join(); + delete keepalive_thread; + delete controller; +} + +void RGBController_SinowealthKeyboard10c::SetupZones() +{ + /*---------------------------------------------------------*\ + | Create the keyboard zone usiung Keyboard Layout Manager | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = ZONE_EN_KEYBOARD; + new_zone.type = ZONE_TYPE_MATRIX; + + sinowealth_device device = sinowealth_10c_keyboards.at(model_id); + + KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ANSI_QWERTY, device.keyboard_layout.base_size, + device.keyboard_layout.key_values); + new_kb.ChangeKeys(device.keyboard_layout.edit_keys); + + matrix_map_type* new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + new_zone.matrix_map->height = new_kb.GetRowCount(); + new_zone.matrix_map->width = new_kb.GetColumnCount(); + + new_zone.matrix_map->map = new unsigned int[new_map->height * new_map->width]; + new_zone.leds_count = new_kb.GetRowCount() * new_kb.GetColumnCount(); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + /*---------------------------------------------------------*\ + | These keyboards use sparse LED indexes — for example, a | + | 99-key board might use LED indexes 0–112, leaving some | + | numbers unused. Empty positions are marked 0xFFFFFFFF. | + | | + | We map each key to its actual LED index, filling the | + | `leds` vector by those indexes and leaving gaps where no | + | LED exists. | + \*---------------------------------------------------------*/ + + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_VALUE, new_map->height, new_map->width); + + leds.resize(new_zone.leds_count); + + for(unsigned int i = 0, j = 0; i < new_zone.leds_count; i++) + { + if(new_map->map[i] == 0xFFFFFFFF) + { + continue; + } + + led new_led; + + new_led.name = new_kb.GetKeyNameAt(j); + new_led.value = new_kb.GetKeyValueAt(j); + + leds[new_map->map[i]] = new_led; + + j++; + } + + zones.push_back(new_zone); + + SetupColors(); +} + +void RGBController_SinowealthKeyboard10c::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SinowealthKeyboard10c::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + controller->SetLEDsDirect(colors); +} + +void RGBController_SinowealthKeyboard10c::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SinowealthKeyboard10c::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SinowealthKeyboard10c::DeviceUpdateMode() +{ +} + +void RGBController_SinowealthKeyboard10c::KeepaliveThreadFunction() +{ + while(keepalive_thread_run.load()) + { + if(active_mode == MODE_DIRECT && (std::chrono::steady_clock::now() - last_update_time) > 1s) + { + UpdateLEDs(); + } + std::this_thread::sleep_for(500ms); + } +} diff --git a/Controllers/SinowealthController/SinowealthKeyboard10cController/RGBController_SinowealthKeyboard10c.h b/Controllers/SinowealthController/SinowealthKeyboard10cController/RGBController_SinowealthKeyboard10c.h new file mode 100644 index 0000000..968f310 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard10cController/RGBController_SinowealthKeyboard10c.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| RGBController_SinowealthKeyboard10c.h | +| | +| RGBController for Sinowealth Keyboards with PID 010C | +| | +| Rodrigo Tavares 27 Nov 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SinowealthKeyboard10cController.h" + +class RGBController_SinowealthKeyboard10c : public RGBController +{ +public: + RGBController_SinowealthKeyboard10c(SinowealthKeyboard10cController* controller_ptr, unsigned char model_id); + ~RGBController_SinowealthKeyboard10c(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + void KeepaliveThreadFunction(); + + std::chrono::time_point last_update_time; + unsigned char model_id; + SinowealthKeyboard10cController* controller; + std::atomic keepalive_thread_run; + std::thread* keepalive_thread; +}; diff --git a/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cController.cpp b/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cController.cpp new file mode 100644 index 0000000..8cbbd7b --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cController.cpp @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| SinowealthKeyboard10cController.cpp | +| | +| Driver for Sinowealth Keyboards with PID 010C | +| | +| Rodrigo Tavares 27 Nov 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SinowealthKeyboard10cController.h" +#include "RGBController.h" +#include "StringUtils.h" + +using namespace kbd10c; + +SinowealthKeyboard10cController::SinowealthKeyboard10cController(hid_device* dev_handle, char* path, + std::string dev_name) +{ + dev = dev_handle; + name = dev_name; + + current_mode = MODE_DIRECT; + + location = path; +} + +SinowealthKeyboard10cController::~SinowealthKeyboard10cController() +{ + hid_close(dev); +} + +std::string SinowealthKeyboard10cController::GetLocation() +{ + return ("HID: " + location); +} + +std::string SinowealthKeyboard10cController::GetName() +{ + return (name); +} + +unsigned char SinowealthKeyboard10cController::GetCurrentMode() +{ + return current_mode; +} + +std::string SinowealthKeyboard10cController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return (""); + } + + return (StringUtils::wstring_to_string(serial_string)); +} + +void SinowealthKeyboard10cController::SetLEDsDirect(std::vector colors) +{ + const int buffer_size = 520; + unsigned char buf[buffer_size]; + memset(buf, 0x00, buffer_size); + + buf[0x00] = 0x06; + buf[0x01] = 0x08; + buf[0x04] = 0x01; + buf[0x06] = 0x7A; + buf[0x07] = 0x01; + + for(size_t i = 0; i < colors.size(); ++i) + { + buf[0x08 + i * 3] = RGBGetRValue(colors[i]); + buf[0x08 + i * 3 + 1] = RGBGetGValue(colors[i]); + buf[0x08 + i * 3 + 2] = RGBGetBValue(colors[i]); + } + + hid_send_feature_report(dev, buf, buffer_size); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); +} diff --git a/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cController.h b/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cController.h new file mode 100644 index 0000000..d334e82 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cController.h @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| SinowealthKeyboard10cController.h | +| | +| Driver for Sinowealth Keyboards with PID 010C | +| | +| Rodrigo Tavares 27 Nov 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController.h" +#include +#include + +#pragma once + +namespace kbd10c +{ + enum + { + MODE_OFF = 0x0, + MODE_DIRECT = 0x1, + }; +} + + +class SinowealthKeyboard10cController +{ +public: + SinowealthKeyboard10cController(hid_device* dev_handle, char *_path, std::string dev_name); + ~SinowealthKeyboard10cController(); + + unsigned int GetLEDCount(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + unsigned char GetCurrentMode(); + void SetLEDsDirect(std::vector colors); +private: + hid_device* dev; + device_type type; + unsigned char current_mode; + std::string location; + std::string name; +}; diff --git a/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cDevices.cpp b/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cDevices.cpp new file mode 100644 index 0000000..6395785 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cDevices.cpp @@ -0,0 +1,275 @@ +/*---------------------------------------------------------*\ +| SinowealthKeyboard10cDevices.cpp | +| | +| Device list for Sinowealth Keyboards with PID 010C | +| | +| Rodrigo Tavares 27 Nov 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "SinowealthKeyboard10cDevices.h" +#include "KeyboardLayoutManager.h" +#include "RGBControllerKeyNames.h" + +/*-------------------------------------------------------------------------*\ +| KEYMAPS | +\*-------------------------------------------------------------------------*/ +const keyboard_keymap_overlay_values aula_f99_layout +{ + KEYBOARD_SIZE_FULL, + { + { + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 NULL NULL NULL DEL HOME END PGUP PGDN */ + 0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 0, 0, 0, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC NULL NULL NULL NMLK NMDV NMTM NMMI */ + 1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, 0, 0, 0, 91, 97, 103, 109, + /* TAB Q W E R T Y U I O P [ ] \ NULL NULL NULL NM7 NM8 NM9 NMPL */ + 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 74, 80, 0, 0, 0, 92, 98, 104, 110, + /* CPLK A S D F G H J K L ; " # ENTR NM4 NM5 NM6 */ + 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 0, 81, 93, 99, 105, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 4, 0, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 82, 88, 94, 100, 106, 112, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR NM0 NMPD */ + 5, 11, 17, 35, 53, 59, 0, 65, 83, 89, 95, 101, 107, + }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*--------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*--------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 14, 78, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Replace Print w/ Delete + { 0, 0, 15, 90, KEY_EN_HOME, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Replace ScrLk w/ Home + { 0, 0, 16, 96, KEY_EN_END, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Replace Pause w/ End + { 0, 0, 17, 102, KEY_EN_PAGE_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert PgUp + { 0, 0, 18, 108, KEY_EN_PAGE_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert PgDn + + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Insert + { 0, 1, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Home + { 0, 1, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove PgUp + + { 0, 2, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Padding after Tab + { 0, 2, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Delete + { 0, 2, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove End + { 0, 2, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove PgDn + + { 0, 3, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Padding after CapsLk + { 0, 3, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove extra key + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove padding + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove padding + { 0, 4, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove extra key + { 0, 4, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove padding + { 0, 5, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Menu + { 0, 5, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove padding + } +}; + + +const keyboard_keymap_overlay_values aula_f75_layout +{ + KEYBOARD_SIZE_SEVENTY_FIVE, + { + { + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 */ + 0, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC DEL */ + 1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, + /* TAB Q W E R T Y U I O P [ ] \ PGUP */ + 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 74, 80, + /* CPLK A S D F G H J K L ; " # ENTR PGDN */ + 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 0, 81, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU END */ + 4, 0, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR */ + 5, 11, 17, 35, 0, 53, 0, 59, + }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*--------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*--------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_ESCAPE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert ESC + { 0, 1, 14, 85, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Delete + { 0, 2, 14, 86, KEY_EN_PAGE_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert PgUp + { 0, 3, 14, 87, KEY_EN_PAGE_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert PgDn + + { 0, 4, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove RShift gap + { 0, 4, 13, 82, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert UpArrow + { 0, 4, 14, 88, KEY_EN_END, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert End + + { 0, 5, 10, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove RAlt + { 0, 5, 11, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove RMenu + + { 0, 5, 12, 77, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert LeftArrow + { 0, 5, 13, 83, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert DownArrow + { 0, 5, 14, 89, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert RightArrow + + } +}; + + +const keyboard_keymap_overlay_values aula_f87_layout +{ + KEYBOARD_SIZE_TKL, + { + { + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRNT SCRL PAUSE */ + 0, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC INSRT HOME PGUP */ + 1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, 85, 91, 97, + /* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN */ + 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 74, 80, 86, 92, 98, + /* CPLK A S D F G H J K L ; " # ENTR */ + 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 0, 81, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU */ + 4, 0, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 82, 94, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR */ + 5, 11, 17, 35, 53, 59, 65, 83, 89, 95, 101 + }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*--------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*--------------------------------------------------------------------------------------------------------------------*/ + { 0, 4, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove RShift gap + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Add gap after RShift + } +}; + + +const keyboard_keymap_overlay_values leobog_hi75c_pro_layout +{ + KEYBOARD_SIZE_SEVENTY_FIVE, + { + { + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 */ + 0, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC DEL */ + 1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, + /* TAB Q W E R T Y U I O P [ ] \ END */ + 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 74, 80, + /* CPLK A S D F G H J K L ; " # ENTR PGUP */ + 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 0, 81, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU PGDN */ + 4, 0, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 70, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR */ + 5, 11, 17, 35, 53, 59, 0, 65, + }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*--------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*--------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_ESCAPE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert ESC + { 0, 1, 14, 85, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert Delete + { 0, 2, 14, 86, KEY_EN_END, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert End + { 0, 3, 14, 87, KEY_EN_PAGE_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert PgUp + { 0, 4, 14, 88, KEY_EN_PAGE_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert PgDn + + { 0, 4, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove RShift gap + { 0, 4, 13, 82, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert UpArrow + + { 0, 5, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove RMenu + { 0, 5, 13, 77, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert LeftArrow + { 0, 5, 14, 83, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert DownArrow + { 0, 5, 15, 89, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert RightArrow + } +}; + + +const keyboard_keymap_overlay_values redragon_k686_eisa_pro +{ + KEYBOARD_SIZE_FULL, + { + { + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 NULL NULL NULL DEL HOME END PGUP PGDN */ + 0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 0, 0, 0, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC NULL NULL NULL NMLK NMDV NMTM NMMI */ + 1, 7, 13, 19, 25, 31, 37, 43, 49, 55, 61, 67, 73, 79, 0, 0, 0, 91, 97, 103, 109, + /* TAB Q W E R T Y U I O P [ ] \ NULL NULL NULL NM7 NM8 NM9 NMPL */ + 2, 8, 14, 20, 26, 32, 38, 44, 50, 56, 62, 68, 74, 80, 0, 0, 0, 92, 98, 104, 110, + /* CPLK A S D F G H J K L ; " # ENTR NM4 NM5 NM6 */ + 3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 0, 81, 93, 99, 105, + /* LSFT ISO\ Z X C V B N M , . / RSFT ARWU NM1 NM2 NM3 NMER */ + 4, 0, 10, 16, 22, 28, 34, 40, 46, 52, 58, 64, 82, 88, 94, 100, 106, 112, + /* LCTL LWIN LALT SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR NM0 NMPD */ + 5, 11, 17, 35, 53, 59, 0, 65, 83, 89, 95, 101, 107, + }, + { + /* Add more regional layout fixes here */ + } + }, + { + /*--------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Key, Alternate Name, OpCode, | + \*--------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 14, 78, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Replace Print w/ Delete + { 0, 0, 15, 90, KEY_EN_INSERT, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Replace ScrLk w/ Insert + { 0, 0, 16, 96, KEY_EN_END, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Replace Pause w/ End + { 0, 0, 17, 102, KEY_EN_PAGE_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert PgUp + { 0, 0, 18, 108, KEY_EN_PAGE_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Insert PgDn + { 0, 2, 13, 75, KEY_EN_POUND, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert ] } Key + { 0, 4, 1, 76, KEY_EN_ISO_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Insert \ | Key + + { 0, 1, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, // Remove Insert + { 0, 1, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Home + { 0, 1, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove PgUp + + { 0, 2, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Padding after Tab + { 0, 2, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Delete + { 0, 2, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove End + { 0, 2, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove PgDn + + { 0, 3, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, // Padding after CapsLk + { 0, 3, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove extra key + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove padding + { 0, 3, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove padding + { 0, 4, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove extra key + { 0, 4, 15, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove padding + { 0, 5, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove Menu + { 0, 5, 16, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, // Remove padding + } +}; + + +/*-------------------------------------------------------------------------*\ +| DEVICE MODEL MAPPING | +\*-------------------------------------------------------------------------*/ +const sinowealth_device_map sinowealth_10c_keyboards{ + { + 0xCD, { "AULA F75", aula_f75_layout }, + }, + { + 0xA4, { "AULA F99", aula_f99_layout }, + }, + { + 0x0B, { "AULA F87 Pro", aula_f87_layout }, + }, + { + 0xA3, { "LEOBOG Hi75C Pro", leobog_hi75c_pro_layout }, + }, + { + 0x20, { "Redragon K686 Eisa Pro", redragon_k686_eisa_pro }, + }, + { + 0x05, { "Redragon K686 Eisa Pro", redragon_k686_eisa_pro }, + }, +}; diff --git a/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cDevices.h b/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cDevices.h new file mode 100644 index 0000000..7b26c76 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cDevices.h @@ -0,0 +1,29 @@ +/*---------------------------------------------------------*\ +| SinowealthKeyboard10cDevices.cpp | +| | +| Device list for Sinowealth Keyboards with PID 010C | +| | +| Rodrigo Tavares 27 Nov 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +#include "KeyboardLayoutManager.h" + +typedef std::pair led_pair; + +typedef struct +{ + std::string device_name; + keyboard_keymap_overlay_values keyboard_layout; +} sinowealth_device; + +typedef std::map sinowealth_device_map; + +extern const sinowealth_device_map sinowealth_10c_keyboards; diff --git a/Controllers/SinowealthController/SinowealthKeyboard16Controller/RGBController_SinowealthKeyboard16.cpp b/Controllers/SinowealthController/SinowealthKeyboard16Controller/RGBController_SinowealthKeyboard16.cpp new file mode 100644 index 0000000..877b7df --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard16Controller/RGBController_SinowealthKeyboard16.cpp @@ -0,0 +1,463 @@ +/*------------------------------------------*\ +| RGBController_SinowealthKeyboard16.cpp | +| | +| Definitions and types for Sinowealth | +| Keyboard with PID:0016, | +| Hopefully generic for this PID, | +| this was made spefically for ZUOYA X51 | +| | +| Zagorodnikov Aleksey (glooom) 26.07.2021 | +| based on initial implementation from | +| Dmitri Kalinichenko (Dima-Kal) 23/06/2021 | +\*-----------------------------------------=*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_SinowealthKeyboard16.h" +#include + +#define NA 0xFFFFFFFF + +using namespace kbd16; + +static unsigned int matrix_map[6][22] = + { { 0, NA, 2, 3, 4, 5, NA, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, NA, NA, NA, NA }, + { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, NA, 36, 37, 38, 39, 40, 41, 42, 43 }, + { 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, NA, 59, 60, 61, 62, 63, 64, 65 }, + { 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, NA, 79, NA, NA, NA, NA, 84, 85, 86, 87 }, + { 88, NA, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, NA, NA, 102, NA, 104, NA, 106, 107, 108, 109 }, + { 110, 111, 112, NA, NA, 115, NA, NA, 118, 119, 120, NA, 122, NA, NA, 125, 126, 127, 128, 129, 130, 131 } + }; + +static const char *led_names_tkl[] = +{ + KEY_EN_ESCAPE, + KEY_EN_UNUSED, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_UNUSED, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAUSE_BREAK, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_UNUSED, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_HOME, + KEY_EN_PAGE_UP, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, + + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_UNUSED, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_PLUS, + + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_UNUSED, + KEY_EN_ANSI_ENTER, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_PLUS, + + KEY_EN_LEFT_SHIFT, + KEY_EN_UNUSED, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UNUSED, + KEY_EN_UP_ARROW, + KEY_EN_UNUSED, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_ENTER, + + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_SPACE, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_UNUSED, + KEY_EN_RIGHT_CONTROL, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD, + KEY_EN_NUMPAD_ENTER +}; + +/**------------------------------------------------------------------*\ + @name Sinowealth Keyboard 16 + @category Keyboard + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectSinowealthKeyboard + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SinowealthKeyboard16::RGBController_SinowealthKeyboard16(SinowealthKeyboard16Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + type = DEVICE_TYPE_KEYBOARD; + description = "Sinowealth Keyboard Device"; + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + std::vector modes_cfg = controller->GetDeviceModes(); + std::vector color_presets = controller->GetDeviceColors(); + + int mode_id = 0; + + for(const ModeCfg &cfg : modes_cfg) + { + mode Mode = getModeItem(mode_id); + + Mode.brightness = cfg.brightness; + Mode.speed = cfg.speed; + Mode.direction = cfg.direction_left; + + if(cfg.color == COLOR_PRESET_RANDOM) + { + Mode.color_mode = MODE_COLORS_RANDOM; + } + else + { + Mode.color_mode = MODE_COLORS_MODE_SPECIFIC; + } + + ModeColorCfg mode_color = color_presets[mode_id]; + + for(unsigned int i = Mode.colors_min; i < Mode.colors_max; i++) + { + RGBColor color = (mode_color.preset[i].blue << 16) | (mode_color.preset[i].green << 8) | (mode_color.preset[i].red); + Mode.colors.push_back(color); + } + + modes.push_back(Mode); + mode_id++; + } + + /*-----------------------------------------------------------------*\ + | This keyboard supports 5 configurable custom profiles. | + | The first one is named "Custom" and the others are "Custom 2" to | + | "Custom 5". Leaving the first one just "Custom" to adhere to | + | common mode names scheme. | + \*-----------------------------------------------------------------*/ + for(unsigned int i = 0; i < 5; i++) + { + mode PerLed; + + PerLed.name = "Custom"; + + if(i > 0) + { + PerLed.name += " " + std::to_string(i + 1); + } + + PerLed.flags = MODE_FLAG_HAS_PER_LED_COLOR; + PerLed.color_mode = MODE_COLORS_PER_LED; + PerLed.value = MODE_PER_KEY1+i; + + modes.push_back(PerLed); + } + + SetupZones(); + + int device_mode = controller->GetCurrentMode(); + + if(device_mode >= MODE_PER_KEY1) + { + device_mode = mode_id+(device_mode & 0x0F); + colors = controller->GetPerLedColors(); + } + + active_mode = device_mode; +} + +RGBController_SinowealthKeyboard16::~RGBController_SinowealthKeyboard16() +{ + delete controller; +} + +mode RGBController_SinowealthKeyboard16::getModeItem(unsigned int mode_id) +{ + mode Mode; + + Mode.value = mode_id; + Mode.brightness_min = BRIGHTNESS_OFF; + Mode.brightness_max = BRIGHTNESS_FULL; + Mode.speed_min = SPEED_SLOW; + Mode.speed_max = SPEED_FASTEST; + Mode.colors_min = 0; + Mode.colors_max = COLOR_PRESETS_IN_MODE; + + switch(mode_id) + { + case 0: + Mode.name = "Off"; + Mode.flags = 0; + Mode.color_mode = MODE_COLORS_NONE; + break; + case 1: + Mode.name = "Spectrum Cycle"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Mode.color_mode = MODE_COLORS_NONE; + break; + case 2: + Mode.name = "Breathing"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 3: + Mode.name = "Static"; + Mode.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 4: + Mode.name = "Ripples Shining"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 5: + Mode.name = "Reactive"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 6: + Mode.name = "Flash Away"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 7: + Mode.name = "Sine Wave"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 8: + Mode.name = "Raindrops"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 9: + Mode.name = "Rainbow Wave"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 10: + Mode.name = "Rainbow Wheel"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_LR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 11: + Mode.name = "Adorn"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 12: + Mode.name = "Stars Twinkle"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 13: + Mode.name = "Shadow Disappear"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + case 14: + Mode.name = "Retro Snake"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + default: + Mode.name = "Unknown Mode"; + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_PER_LED_COLOR; + Mode.color_mode = MODE_COLORS_RANDOM; + break; + } + + return Mode; +} + +void RGBController_SinowealthKeyboard16::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + zone new_zone; + + new_zone.name = ZONE_EN_KEYBOARD; + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_count = controller->GetLEDCount(); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 22; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + new_led.name = led_names_tkl[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_SinowealthKeyboard16::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SinowealthKeyboard16::DeviceUpdateLEDs() +{ + controller->SetLEDsDirect(colors); +} + +void RGBController_SinowealthKeyboard16::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SinowealthKeyboard16::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SinowealthKeyboard16::DeviceUpdateMode() +{ + mode ActiveMode = modes[active_mode]; + + int color_mode; + + if(ActiveMode.color_mode == MODE_COLORS_MODE_SPECIFIC) + { + color_mode = COLOR_PRESET_0; + } + else + { + color_mode = COLOR_PRESET_RANDOM; + } + + controller->ClearMode(); + + if(ActiveMode.colors.size()) + { + controller->SetColorsForMode(ActiveMode.value, &ActiveMode.colors[0]); + } + + controller->SetMode(ActiveMode.value, ActiveMode.brightness, ActiveMode.speed, ActiveMode.direction, color_mode); + + if(ActiveMode.value >= MODE_PER_KEY1) + { + colors = controller->GetPerLedColors(); + } + else + { + if(color_mode == COLOR_PRESET_RANDOM) + { + std::generate(colors.begin(), colors.end(), std::rand); + } + else + { + std::fill(colors.begin(), colors.end(), ActiveMode.colors[0]); + } + } + + SignalUpdate(); +} diff --git a/Controllers/SinowealthController/SinowealthKeyboard16Controller/RGBController_SinowealthKeyboard16.h b/Controllers/SinowealthController/SinowealthKeyboard16Controller/RGBController_SinowealthKeyboard16.h new file mode 100644 index 0000000..ef95ff4 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard16Controller/RGBController_SinowealthKeyboard16.h @@ -0,0 +1,38 @@ +/*------------------------------------------*\ +| RGBController_SinowealthKeyboard16.h | +| | +| Definitions and types for Sinowealth | +| Keyboard with PID:0016, | +| Hopefully generic for this PID, | +| this was made spefically for ZUOYA X51 | +| | +| Zagorodnikov Aleksey (glooom) 26.07.2021 | +| based on initial implementation from | +| Dmitri Kalinichenko (Dima-Kal) 23/06/2021 | +\*-----------------------------------------=*/ + +#pragma once + +#include "RGBController.h" +#include "SinowealthKeyboard16Controller.h" + +class RGBController_SinowealthKeyboard16 : public RGBController +{ +public: + RGBController_SinowealthKeyboard16(SinowealthKeyboard16Controller* controller_ptr); + ~RGBController_SinowealthKeyboard16(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SinowealthKeyboard16Controller* controller; + + mode getModeItem(unsigned int mode_id); +}; diff --git a/Controllers/SinowealthController/SinowealthKeyboard16Controller/SinowealthKeyboard16Controller.cpp b/Controllers/SinowealthController/SinowealthKeyboard16Controller/SinowealthKeyboard16Controller.cpp new file mode 100644 index 0000000..a42520d --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard16Controller/SinowealthKeyboard16Controller.cpp @@ -0,0 +1,340 @@ +/*------------------------------------------*\ +| SinowealthKeyboard16Controller.cpp | +| | +| Definitions and types for Sinowealth | +| Keyboard with PID:0016, | +| Hopefully generic for this PID, | +| this was made spefically for ZUOYA X51 | +| | +| Zagorodnikov Aleksey (glooom) 26.07.2021 | +| based on initial implementation from | +| Dmitri Kalinichenko (Dima-Kal) 23/06/2021 | +\*-----------------------------------------=*/ + +#include +#include +#include "LogManager.h" +#include "SinowealthKeyboard16Controller.h" +#include "StringUtils.h" + +using namespace std::chrono_literals; +using namespace kbd16; + +static unsigned char request_init[] = {0x05, 0x01, 0xAA, 0xBB, 0x2F, 0x3E}; +static unsigned char request_modes[] = {0x05, 0x83, 0x00, 0x00, 0x00, 0x00}; +static unsigned char request_colors[] = {0x05, 0x88, 0xA8, 0x00, 0x40, 0x00}; +static unsigned char request_per_led_cm12[] = {0x05, 0x89, 0xAC, 0x00, 0x40, 0x00}; +static unsigned char request_per_led_cm34[] = {0x05, 0x89, 0xB0, 0x00, 0x40, 0x00}; +static unsigned char request_per_led_cm5[] = {0x05, 0x89, 0xB4, 0x00, 0x40, 0x00}; + + +SinowealthKeyboard16Controller::SinowealthKeyboard16Controller(hid_device* cmd_handle, hid_device* data_handle, char* path, std::string dev_name) +{ + dev_cmd = cmd_handle; + dev_data = data_handle; + name = dev_name; + + led_count = 132; + + current_mode = MODE_OFF; + location = path; + + memset(mode_config_buf, 0x00, sizeof(mode_config_buf)); + + initCommunication(); + UpdateConfigurationFromDevice(); +} + +SinowealthKeyboard16Controller::~SinowealthKeyboard16Controller() +{ + hid_close(dev_cmd); + hid_close(dev_data); +} + +std::string SinowealthKeyboard16Controller::GetLocation() +{ + return("HID: " + location); +} + +std::string SinowealthKeyboard16Controller::GetName() +{ + return(name); +} + +unsigned char SinowealthKeyboard16Controller::GetCurrentMode() +{ + return current_mode; +} + +unsigned int SinowealthKeyboard16Controller::GetLEDCount() +{ + return led_count; +} + +std::string SinowealthKeyboard16Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev_cmd, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void SinowealthKeyboard16Controller::SetLEDsDirect(std::vector colors) +{ + const int colors_offset = led_count; + int i = colors_start_idx; + + /*-------------------------------------------------------------------------*\ + | CM2 and CM4 presets are located in second half of corresponding arrays | + \*-------------------------------------------------------------------------*/ + if(current_custom_preset == 1 || current_custom_preset == 3) + { + i += (led_count * 3); + } + + for(const RGBColor &color : colors) + { + per_button_color_buf[i] = RGBGetBValue(color); + per_button_color_buf[i + colors_offset] = RGBGetGValue(color); + per_button_color_buf[i + colors_offset + colors_offset] = RGBGetRValue(color); + + i++; + } + + sendConfig(per_button_color_buf); +} + +void SinowealthKeyboard16Controller::SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, bool direction_left, unsigned char color_mode) +{ + if(mode >= MODE_PER_KEY1) + { + current_custom_preset = (mode & 0x0F); + mode_config_buf[current_mode_idx] = MODE_PER_KEY; + mode_config_buf[per_key_mode_idx] = 1; + mode_config_buf[current_mode_idx+1] = (0x20 | current_custom_preset); + + GetButtonColorsConfig(per_button_color_buf); + } + else + { + mode_config_buf[current_mode_idx] = mode; + device_modes[mode].brightness = brightness; + device_modes[mode].speed = speed; + device_modes[mode].color = color_mode; + device_modes[mode].direction_left = direction_left; + mode_config_buf[per_key_mode_idx] = 0; + } + + if(sendConfig(mode_config_buf)) + { + current_mode = mode; + } +} + +void SinowealthKeyboard16Controller::SetColorsForMode(unsigned char mode, RGBColor *profiles) +{ + if(mode >= MODE_PER_KEY1) + { + return; + } + + for(int i = 0; i < COLOR_PRESETS_IN_MODE; i++) + { + modes_colors[mode].preset[i].red = RGBGetRValue(profiles[i]); + modes_colors[mode].preset[i].green = RGBGetGValue(profiles[i]); + modes_colors[mode].preset[i].blue = RGBGetBValue(profiles[i]); + } + + sendConfig(colors_config_buf); +} + +void SinowealthKeyboard16Controller::ClearMode() +{ + mode_config_buf[per_key_mode_idx] = 0; + mode_config_buf[current_mode_idx] = 0; + mode_config_buf[current_mode_idx+1] = 0; + + sendConfig(mode_config_buf); + + /*-----------------------------------------------------------------*\ + | Before apply new settings, keyboard needs turn off LEDs and wait | + | ~200ms, otherwise sometime glitches happens. | + \*-----------------------------------------------------------------*/ + std::this_thread::sleep_for(200ms); +} + +std::vector SinowealthKeyboard16Controller::GetDeviceModes() +{ + std::vector modes; + + for(int i = 0; i < profiles_count; i++) + { + modes.push_back(device_modes[i]); + } + + return modes; +} + +std::vector SinowealthKeyboard16Controller::GetDeviceColors() +{ + std::vector presets; + + for(int i = 0; i < profiles_count; i++) + { + presets.push_back(modes_colors[i]); + } + + return presets; +} + +std::vector SinowealthKeyboard16Controller::GetPerLedColors() +{ + const int colors_offset = led_count; + int start_idx = colors_start_idx; + + /*-------------------------------------------------------------------------*\ + | CM2 and CM4 presets are located in second half of corresponding arrays | + \*-------------------------------------------------------------------------*/ + if(current_custom_preset == 1 || current_custom_preset == 3) + { + start_idx += (led_count * 3); + } + + std::vector res; + res.resize(led_count); + + for(unsigned int i = 0; i < led_count; i++) + { + RGBColor color = (per_button_color_buf[start_idx+i] << 16) + | (per_button_color_buf[start_idx+i+colors_offset] << 8) + | (per_button_color_buf[start_idx+i+colors_offset+colors_offset]); + + res[i] = color; + } + + return res; +} + +void SinowealthKeyboard16Controller::UpdateConfigurationFromDevice() +{ + GetModesConfig(mode_config_buf); + GetColorsConfig(colors_config_buf); + + current_mode = mode_config_buf[current_mode_idx]; + device_modes = (struct ModeCfg*) &mode_config_buf[profiles_start_idx]; + modes_colors = (struct ModeColorCfg*) &colors_config_buf[colors_start_idx]; + + /*-------------------------------------------------------------------------*\ + | When OEM software switch keyboard to custom mode it set 0x0F into 0x15 | + | byte and 01 into 0x14 byte. However if user manually switch to this mode | + | through Fn+1 the keyboard only sets 0x14 byte by itself, but not touched | + | 0x15 byte. Not sure what part is more important, so managing both bytes. | + \*-------------------------------------------------------------------------*/ + if(current_mode == MODE_PER_KEY || mode_config_buf[per_key_mode_idx] == 1) + { + current_custom_preset = (mode_config_buf[current_mode_idx+1] & 0x0F); + current_mode = ((MODE_PER_KEY&0xF) << 4) | current_custom_preset; + + GetButtonColorsConfig(per_button_color_buf); + } +} + +void SinowealthKeyboard16Controller::GetModesConfig(unsigned char *buf) +{ + if(!getConfig(request_modes, buf)) + { + LOG_ERROR("[%s] Could not read modes config table", name.c_str()); + } +} + +void SinowealthKeyboard16Controller::GetColorsConfig(unsigned char *buf) +{ + if(!getConfig(request_colors, buf)) + { + LOG_ERROR("[%s] Could not read colors config table", name.c_str()); + } +} + +void SinowealthKeyboard16Controller::GetButtonColorsConfig(unsigned char *buf) +{ + unsigned char *req; + + switch(current_custom_preset) + { + case 2: // CM3 + case 3: // CM4 + req = request_per_led_cm34; + break; + case 4: // CM5 + req = request_per_led_cm5; + break; + case 0: // CM1 + case 1: // CM2 + default: + req = request_per_led_cm12; + break; + } + + if(!getConfig(req, buf)) + { + LOG_ERROR("[%s] Could not read per LED config table", name.c_str()); + return; + } +} + +void SinowealthKeyboard16Controller::initCommunication() +{ + /*---------------------------------------------------------*\ + | This needs to be sent first time after powerup keyboard. | + \*---------------------------------------------------------*/ + hid_send_feature_report(dev_cmd, request_init, 6); +} + +bool SinowealthKeyboard16Controller::getConfig(unsigned char *request, unsigned char *buf) +{ + hid_send_feature_report(dev_cmd, request, 6); + + unsigned char response[PAYLOAD_LEN]; + + /*---------------------------------------------------------*\ + | Zero out buffer | + \*---------------------------------------------------------*/ + memset(response, 0x00, PAYLOAD_LEN); + + response[0] = 0x06; + + /*---------------------------------------------------------*\ + | Get the response and put it in the response buffer | + \*---------------------------------------------------------*/ + if(hid_get_feature_report(dev_data, response, PAYLOAD_LEN) != -1) + { + response[1] &= ~0x80; // Clear response flag + response[2] = request[2]; + response[3] = request[3]; + response[4] = request[4]; + response[5] = request[5]; + memcpy(buf,response,PAYLOAD_LEN); + read_config_error = false; + return true; + } + + read_config_error = true; + return false; +} + +bool SinowealthKeyboard16Controller::sendConfig(unsigned char *buf) +{ + if(read_config_error) + { + LOG_ERROR("[%s] Can't send new config if reading old config fail", name.c_str()); + return false; + } + + int result = hid_send_feature_report(dev_data, buf, PAYLOAD_LEN); + return (result != -1); +} diff --git a/Controllers/SinowealthController/SinowealthKeyboard16Controller/SinowealthKeyboard16Controller.h b/Controllers/SinowealthController/SinowealthKeyboard16Controller/SinowealthKeyboard16Controller.h new file mode 100644 index 0000000..d241b27 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard16Controller/SinowealthKeyboard16Controller.h @@ -0,0 +1,163 @@ +/*------------------------------------------*\ +| SinowealthKeyboard16Controller.h | +| | +| Definitions and types for Sinowealth | +| Keyboard with PID:0016, | +| Hopefully generic for this PID, | +| this was made spefically for ZUOYA X51 | +| | +| Zagorodnikov Aleksey (glooom) 26.07.2021 | +| based on initial implementation from | +| Dmitri Kalinichenko (Dima-Kal) 23/06/2021 | +\*-----------------------------------------=*/ + +#include "RGBController.h" +#include +#include + +#pragma once + +#define PAYLOAD_LEN 1032 +#define COLOR_PRESETS_IN_MODE 7 + +namespace kbd16 +{ + enum + { + MODE_OFF = 0, + /* + MODE_COLOR_LOOP = 1, + MODE_RESPIRE = 2, + MODE_STATIC = 3, + MODE_RIPPLES_SHINING = 4, + MODE_REACTION = 5, + MODE_FLASH_AWAY = 6, + MODE_SINE_WAVE = 7, + MODE_RAINDROPS = 8, + MODE_NEON_STREAM = 9, + MODE_RAINBOW_WHEEL = 10, + MODE_ADORN = 11, + MODE_STARS_TWINKLE = 12, + MODE_SHADOW_DISAPPEAR = 13, + MODE_RETRO_SNAKE = 14, + */ + MODE_PER_KEY = 0x0F, // General mode for custom presets + MODE_PER_KEY1 = 0xF0, + MODE_PER_KEY2 = 0xF1, + MODE_PER_KEY3 = 0xF2, + MODE_PER_KEY4 = 0xF3, + MODE_PER_KEY5 = 0xF4, + }; + + enum + { + SPEED_SLOW = 0x00, + SPEED_NORMAL = 0x01, + SPEED_FAST = 0x02, + SPEED_FASTER = 0x03, + SPEED_FASTEST = 0x04, + }; + + enum + { + BRIGHTNESS_OFF = 0x00, + BRIGHTNESS_QUARTER = 0x01, + BRIGHTNESS_HALF = 0x02, + BRIGHTNESS_THREE_QUARTERS = 0x03, + BRIGHTNESS_FULL = 0x04, + }; + + enum + { + COLOR_PRESET_0 = 0, + COLOR_PRESET_1 = 1, + COLOR_PRESET_2 = 2, + COLOR_PRESET_3 = 3, + COLOR_PRESET_4 = 4, + COLOR_PRESET_5 = 5, + COLOR_PRESET_6 = 6, + COLOR_PRESET_RANDOM = 7, + }; +} + +struct ModeCfg +{ + unsigned char color:3; + unsigned char :4; + unsigned char direction_left:1; + unsigned char brightness:4; + unsigned char speed:4; +}; + +struct ColorCfg +{ + unsigned char blue:8; + unsigned char green:8; + unsigned char red:8; +}; + +struct ModeColorCfg +{ + ColorCfg preset[COLOR_PRESETS_IN_MODE]; +}; + +class SinowealthKeyboard16Controller +{ +public: + SinowealthKeyboard16Controller(hid_device* cmd_handle, hid_device* data_handle, char *_path, std::string dev_name); + ~SinowealthKeyboard16Controller(); + + unsigned int GetLEDCount(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + unsigned char GetCurrentMode(); + std::vector GetDeviceModes(); + std::vector GetDeviceColors(); + std::vector GetPerLedColors(); + + void SetLEDColor(RGBColor* color_buf); + void SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, RGBColor* color_buf); + void ClearMode(); + void SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, bool direction_left, unsigned char color_mode); + void SetColorsForMode(unsigned char mode, RGBColor profiles[COLOR_PRESETS_IN_MODE]); + void GetProfile(); + void ReadFirmwareInfo(); + void SetLEDsDirect(std::vector colors); + + const int per_key_mode_idx = 20; + const int current_mode_idx = 21; + const int profiles_start_idx = 32; + const int profiles_count = 15; + const int colors_start_idx = 8; + +private: + hid_device* dev_cmd; + hid_device* dev_data; + device_type type; + std::string name; + + unsigned int led_count; + + unsigned char current_mode; + struct ModeCfg* device_modes; + struct ModeColorCfg* modes_colors; + + std::string location; + + unsigned char mode_config_buf[PAYLOAD_LEN]; + unsigned char colors_config_buf[PAYLOAD_LEN]; + unsigned char per_button_color_buf[PAYLOAD_LEN]; + bool read_config_error = false; + unsigned char current_custom_preset = 0; + + void GetModesConfig(unsigned char *buf); + void GetColorsConfig(unsigned char *buf); + void GetButtonColorsConfig(unsigned char *buf); + + void initCommunication(); + bool getConfig(unsigned char reqest[], unsigned char *buf); + bool sendConfig(unsigned char *buf); + + void UpdateConfigurationFromDevice(); +}; diff --git a/Controllers/SinowealthController/SinowealthKeyboard90Controller/RGBController_SinowealthKeyboard90.cpp b/Controllers/SinowealthController/SinowealthKeyboard90Controller/RGBController_SinowealthKeyboard90.cpp new file mode 100644 index 0000000..8c50e1c --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard90Controller/RGBController_SinowealthKeyboard90.cpp @@ -0,0 +1,249 @@ +/*------------------------------------------*\ +| RGBController_SinowealthKeyboard90.cpp | +| | +| Definitions and types for Sinowealth | +| Keyboard with PID:0090, | +| made spefically for Genesis Thor 300 | +| | +| Jan Baier 30/06/2022 | +\*-----------------------------------------=*/ + +#include "RGBController_SinowealthKeyboard90.h" +#include "LogManager.h" + +using namespace thor300; + +/**------------------------------------------------------------------*\ + @name Genesis Thor 300 + @category Keyboard + @type USB + @save :robot: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectSinowealthGenesisKeyboard + @comment Direct mode is not supported by the keyboard +\*-------------------------------------------------------------------*/ + +RGBController_SinowealthKeyboard90::RGBController_SinowealthKeyboard90(SinowealthKeyboard90Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Sinowealth"; + type = DEVICE_TYPE_KEYBOARD; + description = "Generic Sinowealth Keyboard"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + AddMode("Breathing", MODE_BREATHING, true ); + AddMode("CCW Rotation", MODE_CCW_ROTATION, false ); + AddMode("CW Rotation", MODE_CW_ROTATION, false ); + AddMode("Flowers Blossom", MODE_FLOWERS_BLOSSOM, false ); + AddMode("Neon", MODE_NEON, true ); + AddMode("Prismo", MODE_PRISMO, false ); + AddMode("Rainbow Wave", MODE_RAINBOW, false ); + AddMode("Raindrops", MODE_RAINDROPS, true ); + AddMode("Reactive", MODE_RESPONSE, false ); + AddMode("Single Key Reactive", MODE_RESPONSE_SINGLE, true ); + AddMode("Snake", MODE_SNAKE, true ); + AddMode("Stars Twinkling", MODE_TWINKLING, true ); + AddMode("Static", MODE_STATIC, true ); + AddMode("Tornado", MODE_TORNADO, true ); + AddMode("Wave 1", MODE_WAVE_1, true ); + AddMode("Wave 2", MODE_WAVE_2, false ); + AddMode("Wave 3", MODE_WAVE_3, true ); + AddMode("Wave 4", MODE_WAVE_4, true ); + AddMode("Wave 5", MODE_WAVE_5, false ); + + mode Custom; + Custom.name = "Custom"; + Custom.value = MODE_CUSTOM; + Custom.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Custom.brightness_min = BRIGHTNESS_OFF; + Custom.brightness_max = BRIGHTNESS_FULL; + Custom.brightness = BRIGHTNESS_FULL; + Custom.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Custom); + + mode Off; + Off.name = "Off"; + Off.value = MODE_STATIC; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + active_mode = (int)modes.size() - 1; + + SetupZones(); +} + +RGBController_SinowealthKeyboard90::~RGBController_SinowealthKeyboard90() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_SinowealthKeyboard90::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = "Keyboard"; + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_count = 104; + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 23; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + + zones.push_back(new_zone); + + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx].name; + new_led.value = led_names[led_idx].idx; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_SinowealthKeyboard90::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SinowealthKeyboard90::DeviceUpdateLEDs() +{ + controller->SendMode(modes[active_mode].value, modes[active_mode].brightness); + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned char key = leds[led_idx].value; + unsigned char red = RGBGetRValue(colors[led_idx]); + unsigned char green = RGBGetGValue(colors[led_idx]); + unsigned char blue = RGBGetBValue(colors[led_idx]); + + controller->SendSingleLED(key, red, green, blue); + } + + controller->SendCommit(); +} + +void RGBController_SinowealthKeyboard90::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SinowealthKeyboard90::UpdateSingleLED(int /*key*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SinowealthKeyboard90::DeviceUpdateMode() +{ + if (modes[active_mode].value == MODE_CUSTOM) + { + return; + } + + unsigned char mode_color = COLOR_RAINBOW; + if (modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + mode_color = MapRGBToColorEnum(modes[active_mode].colors.at(0)); + } + controller->SendMode + ( + modes[active_mode].value, + modes[active_mode].brightness, + modes[active_mode].speed, + mode_color + ); +} + +unsigned char RGBController_SinowealthKeyboard90::MapRGBToColorEnum(RGBColor color) +{ + unsigned char red = RGBGetRValue(color); + unsigned char green = RGBGetGValue(color); + unsigned char blue = RGBGetBValue(color); + + if (red & green & blue) + { + return COLOR_WHITE; + } + if (red & green) + { + return COLOR_YELLOW; + } + if (red & blue) + { + return COLOR_VIOLET; + } + if (green & blue) + { + return COLOR_CYAN; + } + if (red) + { + return COLOR_RED; + } + if (green) + { + return COLOR_GREEN; + } + if (blue) + { + return COLOR_BLUE; + } + return COLOR_RAINBOW; +} + +void RGBController_SinowealthKeyboard90::AddMode + ( + std::string name, + unsigned char value, + bool color_support + ) +{ + mode Mode; + Mode.name = name; + Mode.value = value; + if (color_support) + { + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | + MODE_FLAG_AUTOMATIC_SAVE; + Mode.colors_min = 1; + Mode.colors_max = 1; + Mode.color_mode = MODE_COLORS_RANDOM; + Mode.colors.resize(1); + } + else + { + Mode.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Mode.color_mode = MODE_COLORS_NONE; + } + Mode.brightness_min = BRIGHTNESS_OFF; + Mode.brightness_max = BRIGHTNESS_FULL; + Mode.brightness = BRIGHTNESS_FULL; + Mode.speed_min = SPEED_SLOWEST; + Mode.speed_max = SPEED_FASTEST; + Mode.speed = SPEED_NORMAL; + modes.push_back(Mode); +} diff --git a/Controllers/SinowealthController/SinowealthKeyboard90Controller/RGBController_SinowealthKeyboard90.h b/Controllers/SinowealthController/SinowealthKeyboard90Controller/RGBController_SinowealthKeyboard90.h new file mode 100644 index 0000000..118ffd8 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard90Controller/RGBController_SinowealthKeyboard90.h @@ -0,0 +1,36 @@ +/*------------------------------------------*\ +| RGBController_SinowealthKeyboard90.h | +| | +| Definitions and types for Sinowealth | +| Keyboard with PID:0090, | +| made spefically for Genesis Thor 300 | +| | +| Jan Baier 30/06/2022 | +\*-----------------------------------------=*/ + +#pragma once +#include "RGBController.h" +#include "SinowealthKeyboard90Controller.h" + +class RGBController_SinowealthKeyboard90 : public RGBController +{ +public: + RGBController_SinowealthKeyboard90(SinowealthKeyboard90Controller* controller_ptr); + ~RGBController_SinowealthKeyboard90(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SinowealthKeyboard90Controller* controller; + + void AddMode(std::string name, unsigned char value, bool color_support); + unsigned char MapRGBToColorEnum(RGBColor color); +}; diff --git a/Controllers/SinowealthController/SinowealthKeyboard90Controller/SinowealthKeyboard90Controller.cpp b/Controllers/SinowealthController/SinowealthKeyboard90Controller/SinowealthKeyboard90Controller.cpp new file mode 100644 index 0000000..0231394 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard90Controller/SinowealthKeyboard90Controller.cpp @@ -0,0 +1,119 @@ +/*------------------------------------------*\ +| SinowealthKeyboard90Controller.cpp | +| | +| Definitions and types for Sinowealth | +| Keyboard with PID:0090, | +| made spefically for Genesis Thor 300 | +| | +| Jan Baier 30/06/2022 | +\*-----------------------------------------=*/ + +#include +#include "LogManager.h" +#include "SinowealthKeyboard90Controller.h" +#include "StringUtils.h" + +using namespace thor300; + +SinowealthKeyboard90Controller::SinowealthKeyboard90Controller(hid_device* dev_handle, const char* path, const unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + usb_pid = pid; +} + +SinowealthKeyboard90Controller::~SinowealthKeyboard90Controller() +{ + hid_close(dev); +} + +std::string SinowealthKeyboard90Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string SinowealthKeyboard90Controller::GetNameString() +{ + return(name); +} + +std::string SinowealthKeyboard90Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned short SinowealthKeyboard90Controller::GetUSBPID() +{ + return(usb_pid); +} + +void SinowealthKeyboard90Controller::SendFeatureReport + ( + unsigned char cmd, + unsigned char arg1, + unsigned char arg2, + unsigned char arg3, + unsigned char arg4, + unsigned char arg5 + ) +{ + unsigned char usb_buf[8]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up control packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x0A; + usb_buf[0x01] = cmd; + usb_buf[0x02] = arg1; + usb_buf[0x03] = arg2; + usb_buf[0x04] = arg3; + usb_buf[0x05] = arg4; + usb_buf[0x06] = arg5; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, (unsigned char *)usb_buf, sizeof(usb_buf)); +} + +void SinowealthKeyboard90Controller::SendMode + ( + unsigned char mode, + unsigned char brightness, + unsigned char speed, + unsigned char color + ) +{ + SendFeatureReport(0x03, 0x01); + SendFeatureReport(0x0A, mode, brightness, speed, color); +} + +void SinowealthKeyboard90Controller::SendSingleLED + ( + unsigned char key, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SendFeatureReport(0x0C, 0x01, key, red, green, blue); +} + +void SinowealthKeyboard90Controller::SendCommit() +{ + SendSingleLED(0x89); +} diff --git a/Controllers/SinowealthController/SinowealthKeyboard90Controller/SinowealthKeyboard90Controller.h b/Controllers/SinowealthController/SinowealthKeyboard90Controller/SinowealthKeyboard90Controller.h new file mode 100644 index 0000000..7b03be6 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboard90Controller/SinowealthKeyboard90Controller.h @@ -0,0 +1,246 @@ +/*------------------------------------------*\ +| SinowealthKeyboard90Controller.h | +| | +| Definitions and types for Sinowealth | +| Keyboard with PID:0090, | +| made spefically for Genesis Thor 300 | +| | +| Jan Baier 30/06/2022 | +\*-----------------------------------------=*/ + +#include "RGBController.h" +#include "RGBControllerKeyNames.h" +#include +#include + +#pragma once + +#define NA 0xFFFFFF + +namespace thor300 +{ + static const unsigned int matrix_map[6][23] = + { { 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, NA, 13, 14, 15, NA, NA, NA, NA, NA }, + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, NA, 30, 31, 32, NA, 33, 34, 35, 36 }, + { 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, NA, NA, 50, 51, 52, NA, 53, 54, 55, NA }, + { 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, NA, NA, NA, NA, NA, 70, 71, 72, 73 }, + { 74, NA, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, NA, 85, NA, NA, 86, NA, NA, 87, 88, 89, NA }, + { 90, 91, 92, NA, NA, NA, 93, NA, NA, NA, 94, 95, 96, 97, NA, 98, 99, 100, NA, 101, NA, 102, 103 } + }; + + typedef struct + { + const char * name; + const unsigned char idx; + } led_type; + + static const led_type led_names[] = + { + /* Key Label Index */ + { KEY_EN_ESCAPE, 0x01 }, + { KEY_EN_F1, 0x03 }, + { KEY_EN_F2, 0x04 }, + { KEY_EN_F3, 0x05 }, + { KEY_EN_F4, 0x06 }, + { KEY_EN_F5, 0x07 }, + { KEY_EN_F6, 0x08 }, + { KEY_EN_F7, 0x09 }, + { KEY_EN_F8, 0x0A }, + { KEY_EN_F9, 0x0B }, + { KEY_EN_F10, 0x0C }, + { KEY_EN_F11, 0x0D }, + { KEY_EN_F12, 0x0F }, + { KEY_EN_PRINT_SCREEN, 0x10 }, + { KEY_EN_SCROLL_LOCK, 0x11 }, + { KEY_EN_PAUSE_BREAK, 0x12 }, + { KEY_EN_BACK_TICK, 0x18 }, + { KEY_EN_1, 0x19 }, + { KEY_EN_2, 0x1A }, + { KEY_EN_3, 0x1B }, + { KEY_EN_4, 0x1C }, + { KEY_EN_5, 0x1D }, + { KEY_EN_6, 0x1E }, + { KEY_EN_7, 0x1F }, + { KEY_EN_8, 0x20 }, + { KEY_EN_9, 0x21 }, + { KEY_EN_0, 0x22 }, + { KEY_EN_MINUS, 0x23 }, + { KEY_EN_EQUALS, 0x24 }, + { KEY_EN_BACKSPACE, 0x26 }, + { KEY_EN_INSERT, 0x27 }, + { KEY_EN_HOME, 0x28 }, + { KEY_EN_PAGE_UP, 0x29 }, + { KEY_EN_NUMPAD_LOCK, 0x2A }, + { KEY_EN_NUMPAD_DIVIDE, 0x2B }, + { KEY_EN_NUMPAD_TIMES, 0x2C }, + { KEY_EN_NUMPAD_MINUS, 0x2D }, + { KEY_EN_TAB, 0x2F }, + { KEY_EN_Q, 0x30 }, + { KEY_EN_W, 0x31 }, + { KEY_EN_E, 0x32 }, + { KEY_EN_R, 0x33 }, + { KEY_EN_T, 0x34 }, + { KEY_EN_Y, 0x35 }, + { KEY_EN_U, 0x36 }, + { KEY_EN_I, 0x37 }, + { KEY_EN_O, 0x38 }, + { KEY_EN_P, 0x39 }, + { KEY_EN_LEFT_BRACKET, 0x3A }, + { KEY_EN_RIGHT_BRACKET, 0x3B }, + { KEY_EN_DELETE, 0x3E }, + { KEY_EN_END, 0x3F }, + { KEY_EN_PAGE_DOWN, 0x40 }, + { KEY_EN_NUMPAD_7, 0x41 }, + { KEY_EN_NUMPAD_8, 0x42 }, + { KEY_EN_NUMPAD_9, 0x43 }, + { KEY_EN_CAPS_LOCK, 0x46 }, + { KEY_EN_A, 0x47 }, + { KEY_EN_S, 0x48 }, + { KEY_EN_D, 0x49 }, + { KEY_EN_F, 0x4A }, + { KEY_EN_G, 0x4B }, + { KEY_EN_H, 0x4C }, + { KEY_EN_J, 0x4D }, + { KEY_EN_K, 0x4E }, + { KEY_EN_L, 0x4F }, + { KEY_EN_SEMICOLON, 0x50 }, + { KEY_EN_QUOTE, 0x51 }, + { KEY_EN_ANSI_BACK_SLASH, 0x52 }, + { KEY_EN_ANSI_ENTER, 0x54 }, + { KEY_EN_NUMPAD_4, 0x58 }, + { KEY_EN_NUMPAD_5, 0x59 }, + { KEY_EN_NUMPAD_6, 0x5A }, + { KEY_EN_NUMPAD_PLUS, 0x44 }, + { KEY_EN_LEFT_SHIFT, 0x5D }, + { KEY_EN_Z, 0x5F }, + { KEY_EN_X, 0x60 }, + { KEY_EN_C, 0x61 }, + { KEY_EN_V, 0x62 }, + { KEY_EN_B, 0x63 }, + { KEY_EN_N, 0x64 }, + { KEY_EN_M, 0x65 }, + { KEY_EN_COMMA, 0x66 }, + { KEY_EN_PERIOD, 0x67 }, + { KEY_EN_FORWARD_SLASH, 0x68 }, + { KEY_EN_RIGHT_SHIFT, 0x6B }, + { KEY_EN_UP_ARROW, 0x6D }, + { KEY_EN_NUMPAD_1, 0x6F }, + { KEY_EN_NUMPAD_2, 0x70 }, + { KEY_EN_NUMPAD_3, 0x71 }, + { KEY_EN_LEFT_CONTROL, 0x74 }, + { KEY_EN_LEFT_WINDOWS, 0x75 }, + { KEY_EN_LEFT_ALT, 0x76 }, + { KEY_EN_SPACE, 0x79 }, + { KEY_EN_RIGHT_ALT, 0x7C }, + { KEY_EN_RIGHT_FUNCTION, 0x7D }, + { KEY_EN_MENU, 0x7E }, + { KEY_EN_RIGHT_CONTROL, 0x80 }, + { KEY_EN_LEFT_ARROW, 0x83 }, + { KEY_EN_DOWN_ARROW, 0x84 }, + { KEY_EN_RIGHT_ARROW, 0x85 }, + { KEY_EN_NUMPAD_0, 0x86 }, + { KEY_EN_NUMPAD_PERIOD, 0x88 }, + { KEY_EN_NUMPAD_ENTER, 0x72 } + }; + + enum + { + SPEED_SLOWEST = 0x00, + SPEED_SLOW = 0x01, + SPEED_NORMAL = 0x02, + SPEED_FAST = 0x03, + SPEED_FASTEST = 0x04, + }; + + enum + { + BRIGHTNESS_OFF = 0x00, + BRIGHTNESS_QUARTER = 0x01, + BRIGHTNESS_HALF = 0x02, + BRIGHTNESS_THREE_QUARTERS = 0x03, + BRIGHTNESS_FULL = 0x04, + }; + + enum + { + COLOR_RED = 0x00, + COLOR_GREEN = 0x01, + COLOR_BLUE = 0x02, + COLOR_YELLOW = 0x03, + COLOR_VIOLET = 0x04, + COLOR_CYAN = 0x05, + COLOR_WHITE = 0x06, + COLOR_RAINBOW = 0x07, + }; + + enum + { + MODE_PRISMO = 0x00, + MODE_BREATHING = 0x01, + MODE_WAVE_1 = 0x02, + MODE_FLOWERS_BLOSSOM = 0x03, + MODE_RAINBOW = 0x04, + MODE_WAVE_2 = 0x05, + MODE_CW_ROTATION = 0x06, + MODE_WAVE_3 = 0x07, + MODE_RESPONSE = 0x08, + MODE_CCW_ROTATION = 0x09, + MODE_SNAKE = 0x0A, + MODE_WAVE_4 = 0x0B, + MODE_TORNADO = 0x0C, + MODE_NEON = 0x0D, + MODE_TWINKLING = 0x0E, + MODE_RESPONSE_SINGLE = 0x0F, + MODE_STATIC = 0x10, + MODE_RAINDROPS = 0x11, + MODE_WAVE_5 = 0x12, + MODE_CUSTOM = 0x13, + }; +} + +class SinowealthKeyboard90Controller +{ +public: + SinowealthKeyboard90Controller(hid_device* dev_handle, const char* path, const unsigned short pid, std::string dev_name); + ~SinowealthKeyboard90Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + unsigned short GetUSBPID(); + + void SendMode + ( + unsigned char mode = thor300::MODE_CUSTOM, + unsigned char brightness = thor300::BRIGHTNESS_HALF, + unsigned char speed = thor300::SPEED_NORMAL, + unsigned char color = thor300::COLOR_RAINBOW + ); + + void SendSingleLED + ( + unsigned char key, + unsigned char red = 0x00, + unsigned char green = 0x00, + unsigned char blue = 0x00 + ); + + void SendCommit(); + +private: + hid_device* dev; + std::string name; + std::string location; + unsigned short usb_pid; + + void SendFeatureReport + ( + unsigned char cmd, + unsigned char arg1 = 0x00, + unsigned char arg2 = 0x00, + unsigned char arg3 = 0x00, + unsigned char arg4 = 0x00, + unsigned char arg5 = 0x00 + ); +}; diff --git a/Controllers/SinowealthController/SinowealthKeyboardController/RGBController_SinowealthKeyboard.cpp b/Controllers/SinowealthController/SinowealthKeyboardController/RGBController_SinowealthKeyboard.cpp new file mode 100644 index 0000000..7814750 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboardController/RGBController_SinowealthKeyboard.cpp @@ -0,0 +1,479 @@ +/*------------------------------------------*\ +| RGBController_SinowealthKeyboard.cpp | +| | +| Definitions and types for Sinowealth | +| Keyboard, Hopefully generic, this was | +| made spefically for FL eSports F11 KB | +| | +| Dmitri Kalinichenko (Dima-Kal) 23/06/2021 | +\*-----------------------------------------=*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_SinowealthKeyboard.h" + +#define NA 0xFFFFFFFF + +static unsigned int tkl_matrix_map[6][17] = + { { 8, NA, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}, + { 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45}, + { 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66}, + { 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 84, NA, NA, NA, NA}, + { 93, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 106, NA, NA, NA, 107, NA}, + { 113, 114, 115, NA, NA, NA, 118, NA, NA, NA, NA, 121, 122, 123, 127, 128, 129}}; + + +static const char *led_names_tkl[] = +{ + KEY_EN_ESCAPE, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + "Key: Pause", + + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_HOME, + KEY_EN_PAGE_UP, + + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_RIGHT_CONTROL, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, +}; + +/**------------------------------------------------------------------*\ + @name Sinowealth Keyboard + @category Keyboard + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectSinowealthKeyboard + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SinowealthKeyboard::RGBController_SinowealthKeyboard(SinowealthKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + type = DEVICE_TYPE_KEYBOARD; + description = "Sinowealth Keyboard Device"; + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.value = MODE_STATIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(1); + modes.push_back(Static); + + mode Custom; + Custom.name = "Custom"; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.value = MODE_PER_KEY; + modes.push_back(Custom); + + mode Off; + Off.name = "Off"; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + Off.value = MODE_OFF; + modes.push_back(Off); + + mode Respire; + Respire.name = "Respire"; + Respire.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Respire.speed_min = SPEED_SLOW; + Respire.speed = SPEED_NORMAL; + Respire.speed_max = SPEED_FASTEST; + Respire.color_mode = MODE_COLORS_RANDOM; + Respire.value = MODE_RESPIRE; + Respire.colors_min = 1; + Respire.colors_max = 1; + Respire.colors.resize(1); + modes.push_back(Respire); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rainbow.speed_min = SPEED_SLOW; + Rainbow.speed = SPEED_NORMAL; + Rainbow.speed_max = SPEED_FASTEST; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.value = MODE_RAINBOW; + modes.push_back(Rainbow); + + mode FlashAway; + FlashAway.name = "Flash Away"; + FlashAway.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + FlashAway.speed_min = SPEED_SLOW; + FlashAway.speed = SPEED_NORMAL; + FlashAway.speed_max = SPEED_FASTEST; + FlashAway.color_mode = MODE_COLORS_RANDOM; + FlashAway.value = MODE_FLASH_AWAY; + FlashAway.colors_min = 1; + FlashAway.colors_max = 1; + FlashAway.colors.resize(1); + modes.push_back(FlashAway); + + mode Raindrops; + Raindrops.name = "Raindrops"; + Raindrops.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Raindrops.speed_min = SPEED_SLOW; + Raindrops.speed = SPEED_NORMAL; + Raindrops.speed_max = SPEED_FASTEST; + Raindrops.color_mode = MODE_COLORS_RANDOM; + Raindrops.value = MODE_RAINDROPS; + Raindrops.colors_min = 1; + Raindrops.colors_max = 1; + Raindrops.colors.resize(1); + modes.push_back(Raindrops); + + mode RainbowWheel; + RainbowWheel.name = "Rainbow Wheel"; + RainbowWheel.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + RainbowWheel.speed_min = SPEED_SLOW; + RainbowWheel.speed = SPEED_NORMAL; + RainbowWheel.speed_max = SPEED_FASTEST; + RainbowWheel.color_mode = MODE_COLORS_RANDOM; + RainbowWheel.value = MODE_RAINBOW_WHEEL; + RainbowWheel.colors_min = 1; + RainbowWheel.colors_max = 1; + RainbowWheel.colors.resize(1); + modes.push_back(RainbowWheel); + + mode RipplesShining; + RipplesShining.name = "Ripples Shining"; + RipplesShining.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + RipplesShining.speed_min = SPEED_SLOW; + RipplesShining.speed = SPEED_NORMAL; + RipplesShining.speed_max = SPEED_FASTEST; + RipplesShining.color_mode = MODE_COLORS_RANDOM; + RipplesShining.value = MODE_RIPPLES_SHINING; + RipplesShining.colors_min = 1; + RipplesShining.colors_max = 1; + RipplesShining.colors.resize(1); + modes.push_back(RipplesShining); + + mode StarsTwinkle; + StarsTwinkle.name = "Stars Twinkle"; + StarsTwinkle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + StarsTwinkle.speed_min = SPEED_SLOW; + StarsTwinkle.speed = SPEED_NORMAL; + StarsTwinkle.speed_max = SPEED_FASTEST; + StarsTwinkle.color_mode = MODE_COLORS_RANDOM; + StarsTwinkle.value = MODE_STARS_TWINKLE; + StarsTwinkle.colors_min = 1; + StarsTwinkle.colors_max = 1; + StarsTwinkle.colors.resize(1); + modes.push_back(StarsTwinkle); + + mode ShadowDisappear; + ShadowDisappear.name = "Shadow Disappear"; + ShadowDisappear.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + ShadowDisappear.speed_min = SPEED_SLOW; + ShadowDisappear.speed = SPEED_NORMAL; + ShadowDisappear.speed_max = SPEED_FASTEST; + ShadowDisappear.color_mode = MODE_COLORS_RANDOM; + ShadowDisappear.value = MODE_SHADOW_DISAPPEAR; + ShadowDisappear.colors_min = 1; + ShadowDisappear.colors_max = 1; + ShadowDisappear.colors.resize(1); + modes.push_back(ShadowDisappear); + + mode RetroSnake; + RetroSnake.name = "Retro Snake"; + RetroSnake.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + RetroSnake.speed_min = SPEED_SLOW; + RetroSnake.speed = SPEED_NORMAL; + RetroSnake.speed_max = SPEED_FASTEST; + RetroSnake.color_mode = MODE_COLORS_RANDOM; + RetroSnake.value = MODE_RETRO_SNAKE; + RetroSnake.colors_min = 1; + RetroSnake.colors_max = 1; + RetroSnake.colors.resize(1); + modes.push_back(RetroSnake); + + mode NeonStream; + NeonStream.name = "Neon Stream"; + NeonStream.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + NeonStream.speed_min = SPEED_SLOW; + NeonStream.speed = SPEED_NORMAL; + NeonStream.speed_max = SPEED_FASTEST; + NeonStream.color_mode = MODE_COLORS_RANDOM; + NeonStream.value = MODE_NEON_STREAM; + NeonStream.colors_min = 1; + NeonStream.colors_max = 1; + NeonStream.colors.resize(1); + modes.push_back(NeonStream); + + mode Reaction; + Reaction.name = "Reaction"; + Reaction.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Reaction.speed_min = SPEED_SLOW; + Reaction.speed = SPEED_NORMAL; + Reaction.speed_max = SPEED_FASTEST; + Reaction.color_mode = MODE_COLORS_RANDOM; + Reaction.value = MODE_REACTION; + Reaction.colors_min = 1; + Reaction.colors_max = 1; + Reaction.colors.resize(1); + modes.push_back(Reaction); + + mode SineWave; + SineWave.name = "Sine Wave"; + SineWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + SineWave.speed_min = SPEED_SLOW; + SineWave.speed = SPEED_NORMAL; + SineWave.speed_max = SPEED_FASTEST; + SineWave.color_mode = MODE_COLORS_RANDOM; + SineWave.value = MODE_SINE_WAVE; + SineWave.colors_min = 1; + SineWave.colors_max = 1; + SineWave.colors.resize(1); + modes.push_back(SineWave); + + mode RetinueScanning; + RetinueScanning.name = "Retinue Scanning"; + RetinueScanning.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + RetinueScanning.speed_min = SPEED_SLOW; + RetinueScanning.speed = SPEED_NORMAL; + RetinueScanning.speed_max = SPEED_FASTEST; + RetinueScanning.color_mode = MODE_COLORS_RANDOM; + RetinueScanning.value = MODE_RETINUE_SCANNING; + RetinueScanning.colors_min = 1; + RetinueScanning.colors_max = 1; + RetinueScanning.colors.resize(1); + modes.push_back(RetinueScanning); + + mode RotatingWindmill; + RotatingWindmill.name = "Rotating Windmill"; + RotatingWindmill.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + RotatingWindmill.speed_min = SPEED_SLOW; + RotatingWindmill.speed = SPEED_NORMAL; + RotatingWindmill.speed_max = SPEED_FASTEST; + RotatingWindmill.color_mode = MODE_COLORS_RANDOM; + RotatingWindmill.value = MODE_ROTATING_WINDMILL; + RotatingWindmill.colors_min = 1; + RotatingWindmill.colors_max = 1; + RotatingWindmill.colors.resize(1); + modes.push_back(RotatingWindmill); + + mode ColorfulWaterfall; + ColorfulWaterfall.name = "Colorful Waterfall"; + ColorfulWaterfall.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + ColorfulWaterfall.speed_min = SPEED_SLOW; + ColorfulWaterfall.speed = SPEED_NORMAL; + ColorfulWaterfall.speed_max = SPEED_FASTEST; + ColorfulWaterfall.color_mode = MODE_COLORS_NONE; + ColorfulWaterfall.value = MODE_COLORFUL_WATERFALL; + modes.push_back(ColorfulWaterfall); + + mode Blossoming; + Blossoming.name = "Blossoming"; + Blossoming.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Blossoming.speed_min = SPEED_SLOW; + Blossoming.speed = SPEED_NORMAL; + Blossoming.speed_max = SPEED_FASTEST; + Blossoming.color_mode = MODE_COLORS_NONE; + Blossoming.value = MODE_BLOSSOMING; + modes.push_back(Blossoming); + + mode RotatingStorm; + RotatingStorm.name = "Rotating Storm"; + RotatingStorm.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + RotatingStorm.speed_min = SPEED_SLOW; + RotatingStorm.speed = SPEED_NORMAL; + RotatingStorm.speed_max = SPEED_FASTEST; + RotatingStorm.color_mode = MODE_COLORS_RANDOM; + RotatingStorm.value = MODE_ROTATING_STORM; + RotatingStorm.colors_min = 1; + RotatingStorm.colors_max = 1; + RotatingStorm.colors.resize(1); + modes.push_back(RotatingStorm); + + mode Collision; + Collision.name = "Collision"; + Collision.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Collision.speed_min = SPEED_SLOW; + Collision.speed = SPEED_NORMAL; + Collision.speed_max = SPEED_FASTEST; + Collision.color_mode = MODE_COLORS_RANDOM; + Collision.value = MODE_COLLISION; + Collision.colors_min = 1; + Collision.colors_max = 1; + Collision.colors.resize(1); + modes.push_back(Collision); + + mode Perfect; + Perfect.name = "Perfect"; + Perfect.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Perfect.speed_min = SPEED_SLOW; + Perfect.speed = SPEED_NORMAL; + Perfect.speed_max = SPEED_FASTEST; + Perfect.color_mode = MODE_COLORS_RANDOM; + Perfect.value = MODE_PERFECT; + Perfect.colors_min = 1; + Perfect.colors_max = 1; + Perfect.colors.resize(1); + modes.push_back(Perfect); + + SetupZones(); +} + +RGBController_SinowealthKeyboard::~RGBController_SinowealthKeyboard() +{ + delete controller; +} + +void RGBController_SinowealthKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + zone new_zone; + + new_zone.name = ZONE_EN_KEYBOARD; + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = 86; + new_zone.leds_max = 86; + new_zone.leds_count = 86; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 17; + new_zone.matrix_map->map = (unsigned int *)&tkl_matrix_map; + + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < 86; led_idx++) + { + led new_led; + new_led.name = led_names_tkl[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_SinowealthKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SinowealthKeyboard::DeviceUpdateLEDs() +{ + controller->SetLEDsDirect(colors); +} + +void RGBController_SinowealthKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SinowealthKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SinowealthKeyboard::DeviceUpdateMode() +{ + unsigned int brightness = BRIGHTNESS_FULL; + RGBColor* selected_color = (modes[active_mode].color_mode == MODE_COLORS_NONE) ? 0 : &modes[active_mode].colors[0]; + + if(modes[active_mode].value == MODE_STATIC) + { + controller->SetStaticColor(selected_color); + } + else + { + controller->SetMode(modes[active_mode].value, brightness, modes[active_mode].speed, modes[active_mode].color_mode); + } +} diff --git a/Controllers/SinowealthController/SinowealthKeyboardController/RGBController_SinowealthKeyboard.h b/Controllers/SinowealthController/SinowealthKeyboardController/RGBController_SinowealthKeyboard.h new file mode 100644 index 0000000..c50b371 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboardController/RGBController_SinowealthKeyboard.h @@ -0,0 +1,33 @@ +/*------------------------------------------*\ +| RGBController_SinowealthKeyboard.h | +| | +| Definitions and types for Sinowealth | +| Keyboard, Hopefully generic, this was | +| made spefically for FL eSports F11 KB | +| | +| Dmitri Kalinichenko (Dima-Kal) 23/06/2021 | +\*-----------------------------------------=*/ + +#pragma once + +#include "RGBController.h" +#include "SinowealthKeyboardController.h" + +class RGBController_SinowealthKeyboard : public RGBController +{ +public: + RGBController_SinowealthKeyboard(SinowealthKeyboardController* controller_ptr); + ~RGBController_SinowealthKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SinowealthKeyboardController* controller; +}; diff --git a/Controllers/SinowealthController/SinowealthKeyboardController/SinowealthKeyboardController.cpp b/Controllers/SinowealthController/SinowealthKeyboardController/SinowealthKeyboardController.cpp new file mode 100644 index 0000000..d34aa6c --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboardController/SinowealthKeyboardController.cpp @@ -0,0 +1,283 @@ +/*------------------------------------------*\ +| SinowealthKeyboardController.cpp | +| | +| Definitions and types for Sinowealth | +| Keyboard, Hopefully generic, this was | +| made spefically for FL eSports F11 KB | +| | +| Dmitri Kalinichenko (Dima-Kal) 23/06/2021 | +\*-----------------------------------------=*/ + +#include +#include "SinowealthKeyboardController.h" +#include "StringUtils.h" + +static unsigned char send_per_key_part_of_command_packet[] = { 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static unsigned char mode_brightness_speed_packet[] = { 0x06, 0x03, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x5A, 0xA5, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x20, 0x00, 0x44, 0x07, 0x30, + 0x07, 0x23, 0x00, 0x23, 0x00, 0x23, 0x07, 0x33, 0x07, 0x23, 0x07, 0x23, 0x07, 0x23, 0x07, 0x23, + 0x07, 0x23, 0x07, 0x23, 0x07, 0x23, 0x07, 0x23, 0x07, 0x23, 0x07, 0x23, 0x07, 0x23, 0x07, 0x23, + 0x07, 0x23, 0x00, 0x10, 0x00, 0x10, 0x07, 0x44, 0x07, 0x44, 0x07, 0x44, 0x07, 0x44, 0x07, 0x44, + 0x07, 0x44, 0x07, 0x44, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5, 0x03, 0x03 }; + +static unsigned char tkl_keys_per_key_index[] = { 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x1d, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x54, 0x5D, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x6A, 0x6B, + 0x71, 0x72, 0x73, 0x76, 0x79, 0x7A, 0x7B, 0x7F, 0x80, 0x81 }; + +static unsigned int keys_tkl_keys_indices_static_command[] = { 0x0022, 0x0024, 0x0026, 0x0027, 0x0029, 0x002B, 0x002D, 0x002E, 0x002F, + 0x0030, 0x0031, 0x0032, 0x0037, 0x0039, 0x003B, 0x003C, 0x003E, + 0x0040, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x004C, 0x004E, + 0x0050, 0x0051, 0x0053, 0x0055, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, + 0x0061, 0x0063, 0x0065, 0x0066, 0x0068, 0x006A, 0x006C, 0x006D, 0x006E, 0x006F, + 0x0070, 0x0071, 0x0076, 0x0078, 0x007A, 0x007B, 0x007D, 0x007F, + 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x008B, 0x008D, 0x008F, + 0x0090, 0x0092, 0x0094, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, + 0x00A0, 0x00A2, 0x00A4, 0x00A5, 0x00A7, 0x00A9, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B5, 0x00B7, 0x00B9, 0x00BA, 0x00BC, 0x00BE, + 0x00C0, 0x00E1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00CA, 0x00CC, 0x00CE, 0x00CF, + 0x00D1, 0x00D3, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DF, + 0x00E1, 0x00E3, 0x00E4, 0x00E6, 0x00E8, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F4, 0x00F6, 0x00F8, 0x00F9, 0x00FB, 0x00FD, 0x00FF, + 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0109, 0x010B, 0x010D, 0x010E, + 0x0110, 0x0112, 0x0114, 0x0115, 0x0116, 0x0117, 0x0118, 0x0119, 0x011E, + 0x0120, 0x0122, 0x0123, 0x0125, 0x0127, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, + 0x0133, 0x0135, 0x0137, 0x0138, 0x013A, 0x013C, 0x013E, 0x013F, + 0x0140, 0x0141, 0x0142, 0x0143, 0x0148, 0x014A, 0x014C, 0x014D, 0x014F, + 0x0151, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157, 0x0158, 0x015D, 0x015F, + 0x0161, 0x0162, 0x0164, 0x0166, 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, + 0x0172, 0x0174, 0x0176, 0x0177, 0x0179, 0x017B, 0x017D, 0x017E, 0x017F, + 0x0180, 0x0181, 0x0182, 0x0187, 0x0189, 0x018B, 0x018C, 0x018E, + 0x0190, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 0x019C, 0x019E, + 0x01A0, 0x01A1, 0x01A3, 0x01A5, 0x01A7, 0x01A8, 0x01A9, 0x01AA, 0x01AB, 0x01AC, + 0x01B1, 0x01B3, 0x01B5, 0x01B6, 0x01B8, 0x01BA, 0x01BC, 0x01BD, 0x01BE, 0x01BF, + 0x01C0, 0x01C1, 0x01C6, 0x01C8, 0x01CA, 0x01Cb, 0x01CD, 0x01CF, + 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01DB, 0x01DD, 0x01DF, + 0x01E0, 0x01E2, 0x01E4, 0x01E6, 0x01E7, 0x01E8, 0x01E9, 0x01EA}; + + +SinowealthKeyboardController::SinowealthKeyboardController(hid_device* dev_cmd_handle, hid_device* dev_data_handle, char* path, std::string dev_name) +{ + dev_cmd = dev_cmd_handle; + dev_data = dev_data_handle; + name = dev_name; + + led_count = sizeof(tkl_keys_per_key_index) / sizeof(*tkl_keys_per_key_index); + + current_mode = MODE_STATIC; + current_speed = SPEED_NORMAL; + + location = path; +} + +SinowealthKeyboardController::~SinowealthKeyboardController() +{ + hid_close(dev_cmd); + hid_close(dev_data); +} + +std::string SinowealthKeyboardController::GetLocation() +{ + return("HID: " + location); +} + +std::string SinowealthKeyboardController::GetName() +{ + return(name); +} + +unsigned char SinowealthKeyboardController::GetCurrentMode() +{ + return current_mode; +} + +unsigned int SinowealthKeyboardController::GetLEDCount() +{ + return(sizeof(tkl_keys_per_key_index) / sizeof(*tkl_keys_per_key_index)); +} + +std::string SinowealthKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev_cmd, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void SinowealthKeyboardController::SetLEDsDirect(std::vector colors) +{ + const int buffer_size = 1032; + + unsigned char buf[buffer_size]; + unsigned int num_keys = sizeof(tkl_keys_per_key_index) / sizeof(*tkl_keys_per_key_index); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x06; + buf[0x01] = 0x09; + buf[0x02] = 0xBC; + buf[0x03] = 0x00; + buf[0x04] = 0x40; + + for(unsigned int i = 0 ; i < (sizeof(send_per_key_part_of_command_packet) / sizeof(char)); i++) + { + buf[0x027C + i] = send_per_key_part_of_command_packet[i]; + } + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for(unsigned int i = 0; i < num_keys; i++) + { + buf[tkl_keys_per_key_index[i]] = RGBGetBValue(colors[i]); + buf[tkl_keys_per_key_index[i] + 0x7E] = RGBGetGValue(colors[i]); + buf[tkl_keys_per_key_index[i] + 0x7E + 0x7E] = RGBGetRValue(colors[i]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev_data, buf, sizeof(buf)); +} + +void SinowealthKeyboardController::SetStaticColor(RGBColor* color_buf) +{ + const int buffer_size = 1032; + + unsigned char usb_buf[buffer_size]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + int offset = 0; + + usb_buf[offset] = 0x06; + offset += 1; + + usb_buf[offset] = 0x08; + offset += 1; + + usb_buf[offset] = 0xB8; + offset += 2; + + usb_buf[offset] = 0x40; + + usb_buf[0x1D] = RGBGetRValue(color_buf[0]); + usb_buf[0x1E] = RGBGetGValue(color_buf[0]); + usb_buf[0x1F] = RGBGetBValue(color_buf[0]); + + unsigned int size_of_keys_array = sizeof (keys_tkl_keys_indices_static_command)/ sizeof(int); + + for(unsigned int i = 0x00; i < size_of_keys_array; i++) + { + unsigned int key_code = keys_tkl_keys_indices_static_command[i]; + usb_buf[key_code] = 0xFF; + } + + hid_send_feature_report(dev_data, usb_buf, sizeof(usb_buf)); +} + +void SinowealthKeyboardController::SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char color_mode) +{ + const int buffer_size = 1032; + + int mode_byte_index = 0x15; + const int speed_and_brightness_byte_index_start = 0x29; // Speed + brightnes level value, Seriously? + + unsigned int color_mode_value = color_mode == MODE_COLORS_RANDOM ? 0x07 : 0x00; // 0x07 - Value to set random color mode + + unsigned char usb_buf[buffer_size]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + unsigned int mode_brightness_speed_packet_length = sizeof(mode_brightness_speed_packet)/sizeof(char); + + for(unsigned int i = 0x00; i < mode_brightness_speed_packet_length; i++) + { + usb_buf[i] = mode_brightness_speed_packet[i]; + } + + usb_buf[mode_byte_index] = mode; + + int speed_and_brightness_byte_index = speed_and_brightness_byte_index_start + ((mode - 2) * 2); + + switch(mode) + { + case MODE_OFF: + break; + case MODE_RAINBOW: + break; + case MODE_FLASH_AWAY: + break; + case MODE_RAINDROPS: + break; + case MODE_RAINBOW_WHEEL: + break; + case MODE_RIPPLES_SHINING: + break; + case MODE_STARS_TWINKLE: + break; + case MODE_SHADOW_DISAPPEAR: + break; + case MODE_RETRO_SNAKE: + break; + case MODE_NEON_STREAM: + break; + case MODE_REACTION: + break; + case MODE_SINE_WAVE: + break; + case MODE_RETINUE_SCANNING: + break; + case MODE_ROTATING_WINDMILL: + break; + case MODE_COLORFUL_WATERFALL: + break; + case MODE_BLOSSOMING: + break; + case MODE_ROTATING_STORM: + break; + case MODE_COLLISION: + break; + case MODE_PERFECT: + break; + case MODE_PER_KEY: + usb_buf[mode_byte_index - 1] = 0x01; + usb_buf[0x27] = 0x24; + break; + } + + int color_mode_byte_index = speed_and_brightness_byte_index - 1; + + usb_buf[speed_and_brightness_byte_index] = speed + brightness; + usb_buf[color_mode_byte_index] = color_mode_value; + + int result = hid_send_feature_report(dev_data, usb_buf, sizeof(usb_buf)); + + if(result != -1) + { + current_mode = mode; + } +} diff --git a/Controllers/SinowealthController/SinowealthKeyboardController/SinowealthKeyboardController.h b/Controllers/SinowealthController/SinowealthKeyboardController/SinowealthKeyboardController.h new file mode 100644 index 0000000..286e573 --- /dev/null +++ b/Controllers/SinowealthController/SinowealthKeyboardController/SinowealthKeyboardController.h @@ -0,0 +1,88 @@ +/*------------------------------------------*\ +| SinowealthKeyboardController.h | +| | +| Definitions and types for Sinowealth | +| Keyboard, Hopefully generic, this was | +| made spefically for FL eSports F11 KB | +| | +| Dmitri Kalinichenko (Dima-Kal) 23/06/2021 | +\*-----------------------------------------=*/ + +#include "RGBController.h" +#include +#include + +#pragma once + +enum +{ + MODE_OFF = 0x0, + MODE_STATIC = 0x1, + MODE_RESPIRE = 0x2, + MODE_RAINBOW = 0x3, + MODE_FLASH_AWAY = 0x4, + MODE_RAINDROPS = 0x5, + MODE_RAINBOW_WHEEL = 0x6, + MODE_RIPPLES_SHINING = 0x7, + MODE_STARS_TWINKLE = 0x8, + MODE_SHADOW_DISAPPEAR = 0x9, + MODE_RETRO_SNAKE = 0xA, + MODE_NEON_STREAM = 0xB, + MODE_REACTION = 0xC, + MODE_SINE_WAVE = 0xD, + MODE_RETINUE_SCANNING = 0xE, + MODE_ROTATING_WINDMILL = 0xF, + MODE_COLORFUL_WATERFALL = 0x10, + MODE_BLOSSOMING = 0x11, + MODE_ROTATING_STORM = 0x12, + MODE_COLLISION = 0x13, + MODE_PERFECT = 0x14, + MODE_PER_KEY = 0x15 +}; + +enum +{ + SPEED_SLOW = 0x12, + SPEED_NORMAL = 0x22, + SPEED_FASTER = 0x32, + SPEED_FASTEST = 0x42, +}; + +enum +{ + BRIGHTNESS_OFF = 0x0, + BRIGHTNESS_QUARTER = 0x1, + BRIGHTNESS_HALF = 0x2, + BRIGHTNESS_THREE_QUARTERS = 0x3, + BRIGHTNESS_FULL = 0x4 +}; + + +class SinowealthKeyboardController +{ +public: + SinowealthKeyboardController(hid_device* dev_cmd_handle, hid_device* dev_data_handle, char *_path, std::string dev_name); //RGB, Command, path + ~SinowealthKeyboardController(); + + unsigned int GetLEDCount(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + unsigned char GetCurrentMode(); + + void SetLEDColor(RGBColor* color_buf); + void SetStaticColor(RGBColor* color_buf); + void SetMode(unsigned char mode, unsigned char brightness, unsigned char speed, unsigned char color_mode); + void GetProfile(); + void ReadFirmwareInfo(); + void SetLEDsDirect(std::vector colors); +private: + hid_device* dev_cmd; + hid_device* dev_data; + device_type type; + unsigned int led_count; + unsigned char current_mode; + unsigned char current_speed; + std::string location; + std::string name; +}; diff --git a/Controllers/SkyloongController/RGBController_SkyloongGK104Pro.cpp b/Controllers/SkyloongController/RGBController_SkyloongGK104Pro.cpp new file mode 100644 index 0000000..6905c76 --- /dev/null +++ b/Controllers/SkyloongController/RGBController_SkyloongGK104Pro.cpp @@ -0,0 +1,161 @@ +/*---------------------------------------------------------*\ +| RGBController_SkyloongGK104Pro.cpp | +| | +| RGBController for Skyloong GK104 Pro | +| | +| Givo (givowo) 30 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_SkyloongGK104Pro.h" +#include "KeyboardLayoutManager.h" + +using namespace std::chrono_literals; + +/*---------------------------------------------------------------------*\ +| Skyloong GK104 Pro Keyboard KLM Layout | +\*---------------------------------------------------------------------*/ +layout_values keyboard_offset_values = +{ + { + /* ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 PRSC SCLK PSBK */ + 0, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + /* BKTK 1 2 3 4 5 6 7 8 9 0 - = BSPC INS HOME PGUP NLCK NP/ NP* NP- */ + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 41, 42, 43, + /* TAB Q W E R T Y U I O P [ ] \ DEL END PGDN NP7 NP8 NP9 NP+ */ + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, + /* CPLK A S D F G H J K L ; " # ENTR NP4 NP5 NP6 */ + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 84, 85, 86, + /* LSFT / Z X C V B N M , . / RSFT ARWU NP1 NP2 NP3 NPEN */ + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 102, 104, 106, 107, 108, 109, + /* LCTL LWIN LALT SPC SPC SPC RALT RFNC RMNU RCTL ARWL ARWD ARWR NP0 NP. */ + 110, 111, 112, 116, 120, 121, 122, 124, 125, 126, 127, 128, 130 + }, + { + /* Add more regional layout fixes here */ + } +}; + +/**------------------------------------------------------------------*\ + @name Skyloong GK104 Pro + @category Keyboard + @type USB + @save :o: + @direct :white_check_mark: + @effects :o: + @detectors SkyloongControllerDetect + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SkyloongGK104Pro::RGBController_SkyloongGK104Pro(SkyloongGK104ProController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Skyloong"; + description = "Skyloong GK104 Pro Keyboard"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_KEYBOARD; + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = BRIGHTNESS_MIN; + Direct.brightness_max = BRIGHTNESS_MAX; + Direct.brightness = BRIGHTNESS_MAX; + + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_SkyloongGK104Pro::~RGBController_SkyloongGK104Pro() +{ + delete controller; +} + +void RGBController_SkyloongGK104Pro::SetupZones() +{ + /*---------------------------------------------------------*\ + | Create the keyboard zone usiung Keyboard Layout Manager | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = ZONE_EN_KEYBOARD; + new_zone.type = ZONE_TYPE_MATRIX; + + KeyboardLayoutManager new_kb(KEYBOARD_LAYOUT_ANSI_QWERTY, KEYBOARD_SIZE_FULL, keyboard_offset_values); + + new_kb.ChangeKeys( + { + { 0, 4, 12, 0, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT }, + { 0, 4, 14, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 5, 4, 114, "Key: Left Space", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY }, + { 0, 5, 8, 118, "Key: Right Space", KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY } + } + ); + + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + new_zone.matrix_map->height = new_kb.GetRowCount(); + new_zone.matrix_map->width = new_kb.GetColumnCount(); + + new_zone.matrix_map->map = new unsigned int[new_map->height * new_map->width]; + new_zone.leds_count = new_kb.GetKeyCount(); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + /*---------------------------------------------------------*\ + | Matrix map still uses declared zone rows and columns | + | as the packet structure depends on the matrix map | + \*---------------------------------------------------------*/ + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT, new_map->height, new_map->width); + + /*---------------------------------------------------------*\ + | Create LEDs for the Matrix zone | + | Place keys in the layout to populate the matrix | + \*---------------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt(led_idx); + new_led.value = new_kb.GetKeyValueAt(led_idx); + + leds.push_back(new_led); + } + + zones.push_back(new_zone); + + SetupColors(); +} + +void RGBController_SkyloongGK104Pro::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SkyloongGK104Pro::DeviceUpdateLEDs() +{ + controller->SendColorPacket(colors, &leds, modes[active_mode].brightness); +} + +void RGBController_SkyloongGK104Pro::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SkyloongGK104Pro::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SkyloongGK104Pro::DeviceUpdateMode() +{ +} diff --git a/Controllers/SkyloongController/RGBController_SkyloongGK104Pro.h b/Controllers/SkyloongController/RGBController_SkyloongGK104Pro.h new file mode 100644 index 0000000..bc32880 --- /dev/null +++ b/Controllers/SkyloongController/RGBController_SkyloongGK104Pro.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_SkyloongGK104Pro.h | +| | +| RGBController for Skyloong GK104 Pro | +| | +| Givo (givowo) 30 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SkyloongGK104ProController.h" + +#define BRIGHTNESS_MIN 0 +#define BRIGHTNESS_MAX 127 + +class RGBController_SkyloongGK104Pro : public RGBController +{ +public: + RGBController_SkyloongGK104Pro(SkyloongGK104ProController* controller_ptr); + ~RGBController_SkyloongGK104Pro(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SkyloongGK104ProController* controller; +}; diff --git a/Controllers/SkyloongController/SkyloongControllerDetect.cpp b/Controllers/SkyloongController/SkyloongControllerDetect.cpp new file mode 100644 index 0000000..08ecc84 --- /dev/null +++ b/Controllers/SkyloongController/SkyloongControllerDetect.cpp @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| SkyloongControllerDetect.cpp | +| | +| Detector for Skyloong Keyboards | +| | +| Givo (givowo) 30 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "SkyloongGK104ProController.h" +#include "RGBController_SkyloongGK104Pro.h" + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define SKYLOONG_KEYBOARD_VID 0x1EA7 +#define SKYLOONG_GK104_PRO_PID 0x0907 +#define SKYLOONG_GK104_PRO_I 1 + +/******************************************************************************************\ +* * +* DetectSkyloongGK104Pro * +* * +* Tests the USB address to see if a Skyloong GK104 Pro controller exists there. * +* * +\******************************************************************************************/ +void DetectSkyloongGK104Pro(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + if(dev) + { + SkyloongGK104ProController* controller = new SkyloongGK104ProController(dev, info->path, name); + RGBController_SkyloongGK104Pro* rgb_controller = new RGBController_SkyloongGK104Pro(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +/*---------------------------------------------------------------------------------------------------------------------------------------------*\ +| Keyboards | +\*---------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_I("Skyloong GK104 Pro", DetectSkyloongGK104Pro, SKYLOONG_KEYBOARD_VID, SKYLOONG_GK104_PRO_PID, SKYLOONG_GK104_PRO_I); diff --git a/Controllers/SkyloongController/SkyloongGK104ProController.cpp b/Controllers/SkyloongController/SkyloongGK104ProController.cpp new file mode 100644 index 0000000..91697ae --- /dev/null +++ b/Controllers/SkyloongController/SkyloongGK104ProController.cpp @@ -0,0 +1,140 @@ +/*---------------------------------------------------------*\ +| SkyloongGK104ProController.cpp | +| | +| Driver for Skyloong GK104 Pro | +| | +| Givo (givowo) 30 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SkyloongGK104ProController.h" + +using namespace std::chrono_literals; + +enum command +{ + ping = 0x0C, + mode = 0xB, + le_define = 0x1A +}; + +SkyloongGK104ProController::SkyloongGK104ProController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + SendCommand(command::ping, SUBCOMMAND_NONE); + SendCommand(command::mode, MODE_ONLINE); + SendCommand(command::ping, SUBCOMMAND_NONE); +} + +SkyloongGK104ProController::~SkyloongGK104ProController() +{ + SendCommand(command::mode, MODE_OFFLINE); + hid_close(dev); +} + +std::string SkyloongGK104ProController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string SkyloongGK104ProController::GetDeviceName() +{ + return(name); +} + +void SkyloongGK104ProController::SendCommand(char command, char sub_command) +{ + unsigned char buf[PACKET_SIZE]; + memset(buf, 0x00, PACKET_SIZE); + + buf[0x01] = command; + buf[0x02] = sub_command; + + uint16_t crc = Crc16CcittFalse(buf, PACKET_SIZE); + + buf[0x07] = crc & 0xFF; + buf[0x08] = crc >> 8; + + hid_write(dev, buf, PACKET_SIZE); +} + +void SkyloongGK104ProController::SendColorPacket(std::vector colors, std::vector *leds, int brightness) +{ + unsigned char le_data[TOTAL_LED_BYTES]; + memset(le_data, 0x00, TOTAL_LED_BYTES); + + for(unsigned int i = 0; i < leds->size(); i++) + { + int index = leds->at(i).value * 4; + le_data[index++] = RGBGetRValue(colors[i]); + le_data[index++] = RGBGetGValue(colors[i]); + le_data[index++] = RGBGetBValue(colors[i]); + le_data[index++] = brightness; + } + + for(int n = 0; n < TOTAL_LED_BYTES; n += LED_BYTES_IN_CHUNK) { + if(n + LED_BYTES_IN_CHUNK <= TOTAL_LED_BYTES) + { + SetLEDefine(n, &le_data[n], LED_BYTES_IN_CHUNK); + } + else + { + SetLEDefine(n, &le_data[n], TOTAL_LED_BYTES - n); + } + } + + SendCommand(command::le_define, LE_DEFINE_SAVE); +} + +void SkyloongGK104ProController::SetLEDefine(int address, unsigned char *le_data, int le_data_length) +{ + unsigned char buf[PACKET_SIZE]; + memset(buf, 0x00, PACKET_SIZE); + + buf[0x01] = command::le_define; + buf[0x02] = LE_DEFINE_SET; + + int header = (address + ((le_data_length << 24) & 0xFF000000)) | 0; + buf[0x03] = header & 0xFF; + buf[0x04] = (header >> 8) & 0xFF; + buf[0x05] = (header >> 16) & 0xFF; + buf[0x06] = (header >> 24) & 0xFF; + + std::copy(le_data, le_data + le_data_length, buf + 9); + + uint16_t crc = Crc16CcittFalse(buf, PACKET_SIZE); + buf[0x07] = crc & 0xFF; + buf[0x08] = crc >> 8; + + hid_write(dev, buf, PACKET_SIZE); +} + +uint16_t SkyloongGK104ProController::Crc16CcittFalse(const uint8_t *buffer, uint16_t size) +{ + uint16_t crc = 0xFFFF; + + while(size--) + { + crc ^= (*buffer++ << 8); + + for(uint8_t i = 0; i < 8; ++i) + { + if(crc & 0x8000) + { + crc = (crc << 1) ^ 0x1021; + } + else + { + crc = crc << 1; + } + } + } + + return crc; +} diff --git a/Controllers/SkyloongController/SkyloongGK104ProController.h b/Controllers/SkyloongController/SkyloongGK104ProController.h new file mode 100644 index 0000000..461db2c --- /dev/null +++ b/Controllers/SkyloongController/SkyloongGK104ProController.h @@ -0,0 +1,54 @@ +/*---------------------------------------------------------*\ +| SkyloongGK104ProController.h | +| | +| Driver for Skyloong GK104 Pro | +| | +| Givo (givowo) 30 Jun 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define PACKET_SIZE 65 + +#define TOTAL_LED_BYTES 528 +#define LED_BYTES_IN_CHUNK 56 + +#define SUBCOMMAND_NONE 0x00 + +#define MODE_OFFLINE 0x04 +#define MODE_ONLINE 0x05 + +#define LE_DEFINE_SET 0x01 +#define LE_DEFINE_SAVE 0x02 + +class SkyloongGK104ProController +{ +public: + SkyloongGK104ProController(hid_device* dev_handle, const char* path, std::string dev_name); + ~SkyloongGK104ProController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void Ping(); + void SetMode(int mode); + void SendCommand(char command, char sub_command); + void SendColorPacket(std::vector colors, std::vector *leds, int brightness); + +private: + hid_device* dev; + std::string location; + std::string name; + + uint16_t Crc16CcittFalse(const uint8_t *buffer, uint16_t size); + void SetLEDefine(int address, unsigned char *le_data, int le_data_length); + void SaveLEDefine(); +}; diff --git a/Controllers/SonyGamepadController/SonyDS4Controller/RGBController_SonyDS4.cpp b/Controllers/SonyGamepadController/SonyDS4Controller/RGBController_SonyDS4.cpp new file mode 100644 index 0000000..74fc89a --- /dev/null +++ b/Controllers/SonyGamepadController/SonyDS4Controller/RGBController_SonyDS4.cpp @@ -0,0 +1,105 @@ +/*---------------------------------------------------------*\ +| RGBController_SonyDS4.cpp | +| | +| RGBController for Sony Dualshock 4 | +| | +| Pol Rius (alpemwarrior) 24 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController.h" +#include "RGBController_SonyDS4.h" + +/**------------------------------------------------------------------*\ + @name Sony Dual Shock 4 controller + @category Gamepad + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectSonyDS4Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SonyDS4::RGBController_SonyDS4(SonyDS4Controller* controller_ptr) +{ + controller = controller_ptr; + + name = "Sony DualShock 4"; + vendor = "Sony"; + type = DEVICE_TYPE_GAMEPAD; + description = "Sony DualShock 4 Device"; + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.value = 0; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_SonyDS4::~RGBController_SonyDS4() +{ + delete controller; +} + +void RGBController_SonyDS4::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has one LED, so create a single zone and | + | LED for it | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = "Controller Zone"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = "Controller LED"; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zones.push_back(*new_zone); + SetupColors(); +} + +void RGBController_SonyDS4::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SonyDS4::DeviceUpdateLEDs() +{ + unsigned char red = char(RGBGetRValue(colors[0])); + unsigned char green = char(RGBGetGValue(colors[0])); + unsigned char blue = char(RGBGetBValue(colors[0])); + controller->SetColors(red, green, blue); +} + +void RGBController_SonyDS4::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SonyDS4::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SonyDS4::DeviceUpdateMode() +{ +} diff --git a/Controllers/SonyGamepadController/SonyDS4Controller/RGBController_SonyDS4.h b/Controllers/SonyGamepadController/SonyDS4Controller/RGBController_SonyDS4.h new file mode 100644 index 0000000..c5f6816 --- /dev/null +++ b/Controllers/SonyGamepadController/SonyDS4Controller/RGBController_SonyDS4.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_SonyDS4.h | +| | +| RGBController for Sony Dualshock 4 | +| | +| Pol Rius (alpemwarrior) 24 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SonyDS4Controller.h" + +class RGBController_SonyDS4 : public RGBController +{ +public: + RGBController_SonyDS4(SonyDS4Controller* controller_ptr); + ~RGBController_SonyDS4(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SonyDS4Controller* controller; +}; diff --git a/Controllers/SonyGamepadController/SonyDS4Controller/SonyDS4Controller.cpp b/Controllers/SonyGamepadController/SonyDS4Controller/SonyDS4Controller.cpp new file mode 100644 index 0000000..c0a9e8e --- /dev/null +++ b/Controllers/SonyGamepadController/SonyDS4Controller/SonyDS4Controller.cpp @@ -0,0 +1,126 @@ +/*---------------------------------------------------------*\ +| SonyDS4Controller.cpp | +| | +| Driver for Sony Dualshock 4 | +| | +| Pol Rius (alpemwarrior) 24 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "SonyDS4Controller.h" +#include "StringUtils.h" + +SonyDS4Controller::SonyDS4Controller(hid_device * device_handle, const char * device_path) +{ + this->dev = device_handle; + unsigned char readBuffer[64]; + unsigned char reportBuffer[64]; + reportBuffer[0] = 0x02; + + hid_get_feature_report(dev, reportBuffer, 64); + for (int i = 0; i < 5; i++) + { + hid_read(dev, readBuffer, 64); + if (readBuffer[0] == 17) + { + is_bluetooth = true; + break; + } + } + + location = device_path; +} + +SonyDS4Controller::~SonyDS4Controller() +{ + hid_close(dev); +} + +std::string SonyDS4Controller::GetLocation() +{ + return("HID: " + location); +} + +std::string SonyDS4Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void SonyDS4Controller::SetColors(unsigned char red, unsigned char green, unsigned char blue) +{ + if(is_bluetooth) + { + sendReportBT(red, green, blue); + } + else + { + sendReportUSB(red, green, blue); + } +} + +void SonyDS4Controller::sendReportBT(unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char buffer[79] = + { + 0xa2, 0x11, 0xC0, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, red, green, blue, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + uint32_t crc = CRCPP::CRC::Calculate(buffer, 75, CRCPP::CRC::CRC_32()); + unsigned char outbuffer[78]; + + /*-------------------------------------------------*\ + | The report has to be signed with the byte 0xa2. | + | However, hidapi already adds 0xa2 to the report, | + | so we need to remove it from the buffer. | + \*-------------------------------------------------*/ + for(unsigned int i = 1; i < 79; i++) + { + outbuffer[i - 1] = buffer[i]; + } + + /*-------------------------------------------------*\ + | Add the crc32 to the end of the buffer | + \*-------------------------------------------------*/ + outbuffer[74] = (0x000000FF & crc); + outbuffer[75] = (0x0000FF00 & crc) >> 8; + outbuffer[76] = (0x00FF0000 & crc) >> 16; + outbuffer[77] = (0xFF000000 & crc) >> 24; + + hid_write(dev, outbuffer, 78); +} + +void SonyDS4Controller::sendReportUSB(unsigned char red, unsigned char green, unsigned char blue) +{ + uint8_t buffer[11] = + { + 0x05, + 0x07, + 0x00, + 0x00, + 0x00, + 0x00, + red, + green, + blue, + 0x00, + 0x00 + }; + + hid_write(dev, buffer, 11); +} diff --git a/Controllers/SonyGamepadController/SonyDS4Controller/SonyDS4Controller.h b/Controllers/SonyGamepadController/SonyDS4Controller/SonyDS4Controller.h new file mode 100644 index 0000000..c9c6469 --- /dev/null +++ b/Controllers/SonyGamepadController/SonyDS4Controller/SonyDS4Controller.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| SonyDS4Controller.h | +| | +| Driver for Sony Dualshock 4 | +| | +| Pol Rius (alpemwarrior) 24 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +class SonyDS4Controller +{ +public: + SonyDS4Controller(hid_device * device_handle, const char * device_path); + ~SonyDS4Controller(); + + std::string GetLocation(); + std::string GetSerialString(); + + void SetColors(unsigned char red, unsigned char green, unsigned char blue); + +private: + hid_device* dev; + bool is_bluetooth = false; + std::string location; + + void sendReportUSB(unsigned char red, unsigned char green, unsigned char blue); + void sendReportBT(unsigned char red, unsigned char green, unsigned char blue); +}; diff --git a/Controllers/SonyGamepadController/SonyDualSenseController/RGBController_SonyDualSense.cpp b/Controllers/SonyGamepadController/SonyDualSenseController/RGBController_SonyDualSense.cpp new file mode 100644 index 0000000..4b70713 --- /dev/null +++ b/Controllers/SonyGamepadController/SonyDualSenseController/RGBController_SonyDualSense.cpp @@ -0,0 +1,141 @@ +/*---------------------------------------------------------*\ +| RGBController_SonyDualSense.cpp | +| | +| RGBController for Sony DualSense | +| | +| Flora Aubry 01 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_SonyDualSense.h" + +/**------------------------------------------------------------------*\ + @name Sony Dual Sense controller + @category Gamepad + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectSonyDualSenseControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SonyDualSense::RGBController_SonyDualSense(SonyDualSenseController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + + if(controller->IsBluetooth()) + { + name.append(" (BT)"); + } + + vendor = "Sony"; + type = DEVICE_TYPE_GAMEPAD; + description = "Sony DualSense Device"; + location = controller->GetLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.value = SONY_DUALSENSE_DIRECT_MODE_VALUE; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = SONY_DUALSENSE_BRIGHTNESS_MIN; + Direct.brightness_max = SONY_DUALSENSE_BRIGHTNESS_MAX; + Direct.brightness = SONY_DUALSENSE_DEFAULT_BRIGHTNESS; + modes.push_back(Direct); + + mode Micoff; + Micoff.value = SONY_DUALSENSE_MIC_OFF_MODE_VALUE; + Micoff.name = "Mic Off (Direct)"; + Micoff.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Micoff.color_mode = MODE_COLORS_PER_LED; + Micoff.brightness_min = SONY_DUALSENSE_BRIGHTNESS_MIN; + Micoff.brightness_max = SONY_DUALSENSE_BRIGHTNESS_MAX; + Micoff.brightness = SONY_DUALSENSE_DEFAULT_BRIGHTNESS; + modes.push_back(Micoff); + + mode Micpulse; + Micpulse.value = SONY_DUALSENSE_MIC_PULSE_MODE_VALUE; + Micpulse.name = "Mic Pulse (Direct)"; + Micpulse.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Micpulse.color_mode = MODE_COLORS_PER_LED; + Micpulse.brightness_min = SONY_DUALSENSE_BRIGHTNESS_MIN; + Micpulse.brightness_max = SONY_DUALSENSE_BRIGHTNESS_MAX; + Micpulse.brightness = SONY_DUALSENSE_DEFAULT_BRIGHTNESS; + modes.push_back(Micpulse); + + SetupZones(); +} + +RGBController_SonyDualSense::~RGBController_SonyDualSense() +{ + delete controller; +} + +void RGBController_SonyDualSense::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has one LED, so create a single zone and | + | LED for it | + \*---------------------------------------------------------*/ + zone lightbar; + lightbar.name = "Lightbar"; + lightbar.type = ZONE_TYPE_SINGLE; + lightbar.leds_min = SONY_DUALSENSE_LIGHTBAR_LED_COUNT; + lightbar.leds_max = SONY_DUALSENSE_LIGHTBAR_LED_COUNT; + lightbar.leds_count = SONY_DUALSENSE_LIGHTBAR_LED_COUNT; + lightbar.matrix_map = NULL; + zones.push_back(lightbar); + + zone playerleds; + playerleds.name = "Player LEDs"; + playerleds.type = ZONE_TYPE_LINEAR; + playerleds.leds_min = SONY_DUALSENSE_PLAYER_LED_COUNT; + playerleds.leds_max = SONY_DUALSENSE_PLAYER_LED_COUNT; + playerleds.leds_count = SONY_DUALSENSE_PLAYER_LED_COUNT; + playerleds.matrix_map = NULL; + zones.push_back(playerleds); + + leds.resize(SONY_DUALSENSE_LIGHTBAR_LED_COUNT + SONY_DUALSENSE_PLAYER_LED_COUNT); + + leds[0].name = "LED 1"; + + for(unsigned int i = 0 ; i < SONY_DUALSENSE_PLAYER_LED_COUNT; i++) + { + leds[i + 1].name = "Player " + std::to_string(i + 1); + } + + SetupColors(); +} + +void RGBController_SonyDualSense::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SonyDualSense::DeviceUpdateLEDs() +{ + controller->SetColors(colors, modes[active_mode].brightness, modes[active_mode].value); +} + +void RGBController_SonyDualSense::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SonyDualSense::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SonyDualSense::DeviceUpdateMode() +{ +} diff --git a/Controllers/SonyGamepadController/SonyDualSenseController/RGBController_SonyDualSense.h b/Controllers/SonyGamepadController/SonyDualSenseController/RGBController_SonyDualSense.h new file mode 100644 index 0000000..a02c2c7 --- /dev/null +++ b/Controllers/SonyGamepadController/SonyDualSenseController/RGBController_SonyDualSense.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_SonyDualSense.h | +| | +| RGBController for Sony DualSense | +| | +| Flora Aubry 01 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SonyDualSenseController.h" + +class RGBController_SonyDualSense : public RGBController +{ +public: + RGBController_SonyDualSense(SonyDualSenseController* controller_ptr); + ~RGBController_SonyDualSense(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SonyDualSenseController* controller; +}; diff --git a/Controllers/SonyGamepadController/SonyDualSenseController/SonyDualSenseController.cpp b/Controllers/SonyGamepadController/SonyDualSenseController/SonyDualSenseController.cpp new file mode 100644 index 0000000..16bbebf --- /dev/null +++ b/Controllers/SonyGamepadController/SonyDualSenseController/SonyDualSenseController.cpp @@ -0,0 +1,134 @@ +/*---------------------------------------------------------*\ +| SonyDualSenseController.cpp | +| | +| Driver for Sony DualSense | +| | +| Flora Aubry 01 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "SonyDualSenseController.h" +#include "StringUtils.h" + +SonyDualSenseController::SonyDualSenseController(hid_device * device_handle, const char * device_path, bool is_bluetooth, std::string dev_name) +{ + dev = device_handle; + location = device_path; + name = dev_name; + this->is_bluetooth = is_bluetooth; +} + +SonyDualSenseController::~SonyDualSenseController() +{ + hid_close(dev); +} + +std::string SonyDualSenseController::GetLocation() +{ + return("HID: " + location); +} + +std::string SonyDualSenseController::GetName() +{ + return(name); +} + +std::string SonyDualSenseController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void SonyDualSenseController::SetColors(std::vector colors, unsigned char brightness, unsigned char mode_value) +{ + if(is_bluetooth) + { + unsigned char buffer[SONY_DUALSENSE_BT_PACKET_SIZE + 1]; + memset(buffer, 0x00, SONY_DUALSENSE_BT_PACKET_SIZE + 1); + + buffer[0] = 0xA2; + buffer[1] = 0x31; + buffer[2] = 0x02; + buffer[3] = 0x0F; + buffer[4] = 0x55; + + buffer[11] = mode_value; + buffer[41] = 0xFF; // Must be > 0x00 to control birghtness + buffer[44] = 0x02; // bypass default blue color when connected to bluetooth + buffer[45] = 0x02 - brightness; + buffer[46] = 0x20 + + ((colors[1] > 0) ) + + ((colors[2] > 0) << 1) + + ((colors[3] > 0) << 2) + + ((colors[4] > 0) << 3) + + ((colors[5] > 0) << 4); + + buffer[47] = RGBGetRValue(colors[0]); + buffer[48] = RGBGetGValue(colors[0]); + buffer[49] = RGBGetBValue(colors[0]); + + uint32_t crc = CRCPP::CRC::Calculate(buffer, SONY_DUALSENSE_BT_PACKET_SIZE - 3, CRCPP::CRC::CRC_32()); + unsigned char outbuffer[SONY_DUALSENSE_BT_PACKET_SIZE]; + + /*-------------------------------------------------*\ + | The report has to be signed with the byte 0xa2. | + | However, hidapi already adds 0xa2 to the report, | + | so we need to remove it from the buffer. | + \*-------------------------------------------------*/ + for(unsigned int i = 1; i < SONY_DUALSENSE_BT_PACKET_SIZE + 1; i++) + { + outbuffer[i - 1] = buffer[i]; + } + + /*-------------------------------------------------*\ + | Add the crc32 to the end of the buffer | + \*-------------------------------------------------*/ + outbuffer[74] = (0x000000FF & crc); + outbuffer[75] = (0x0000FF00 & crc) >> 8; + outbuffer[76] = (0x00FF0000 & crc) >> 16; + outbuffer[77] = (0xFF000000 & crc) >> 24; + + hid_write(dev, outbuffer, SONY_DUALSENSE_BT_PACKET_SIZE); + } + else + { + unsigned char usb_buf[SONY_DUALSENSE_USB_PACKET_SIZE]; + memset(usb_buf, 0x00, SONY_DUALSENSE_USB_PACKET_SIZE); + + usb_buf[0] = 0x02; + usb_buf[1] = 0x0F; + usb_buf[2] = 0x55; + usb_buf[9] = mode_value; + usb_buf[39] = 0xFF; // Must be > 0x00 to control birghtness + usb_buf[43] = 0x02 - brightness; + usb_buf[44] = 0x20 + + ((colors[1] > 0) ) + + ((colors[2] > 0) << 1) + + ((colors[3] > 0) << 2) + + ((colors[4] > 0) << 3) + + ((colors[5] > 0) << 4); + + usb_buf[45] = RGBGetRValue(colors[0]); + usb_buf[46] = RGBGetGValue(colors[0]); + usb_buf[47] = RGBGetBValue(colors[0]); + + hid_write(dev, usb_buf, SONY_DUALSENSE_USB_PACKET_SIZE); + } +} + +bool SonyDualSenseController::IsBluetooth() +{ + return(is_bluetooth); +} diff --git a/Controllers/SonyGamepadController/SonyDualSenseController/SonyDualSenseController.h b/Controllers/SonyGamepadController/SonyDualSenseController/SonyDualSenseController.h new file mode 100644 index 0000000..384786b --- /dev/null +++ b/Controllers/SonyGamepadController/SonyDualSenseController/SonyDualSenseController.h @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| SonyDualSenseController.h | +| | +| Driver for Sony DualSense | +| | +| Flora Aubry 01 Jul 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define SONY_DUALSENSE_LIGHTBAR_LED_COUNT 1 +#define SONY_DUALSENSE_PLAYER_LED_COUNT 5 +#define SONY_DUALSENSE_BT_PACKET_SIZE 78 +#define SONY_DUALSENSE_USB_PACKET_SIZE 48 + +enum +{ + SONY_DUALSENSE_DIRECT_MODE_VALUE = 0x01, + SONY_DUALSENSE_MIC_OFF_MODE_VALUE = 0x00, + SONY_DUALSENSE_MIC_PULSE_MODE_VALUE = 0x02, + SONY_DUALSENSE_BRIGHTNESS_MIN = 0x00, + SONY_DUALSENSE_BRIGHTNESS_MAX = 0x02, + SONY_DUALSENSE_DEFAULT_BRIGHTNESS = 0x01 +}; + +class SonyDualSenseController +{ +public: + SonyDualSenseController(hid_device * device_handle, const char * device_path, bool is_bluetooth, std::string dev_name); + ~SonyDualSenseController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerialString(); + + void SetColors(std::vector colors, unsigned char brightness, unsigned char mode_value); + bool IsBluetooth(); + +private: + hid_device* dev; + std::string location; + std::string name; + bool is_bluetooth; +}; diff --git a/Controllers/SonyGamepadController/SonyGamepadControllerDetect.cpp b/Controllers/SonyGamepadController/SonyGamepadControllerDetect.cpp new file mode 100644 index 0000000..84c9a6c --- /dev/null +++ b/Controllers/SonyGamepadController/SonyGamepadControllerDetect.cpp @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| SonyGamepadControllerDetect.cpp | +| | +| Detector for Sony Gamepads | +| | +| Pol Rius (alpemwarrior) 24 Sep 2020 | +| Flora Aubry 01 Jul 2022 | +| Yoan Berthelot 06 Mar 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_SonyDS4.h" +#include "RGBController_SonyDualSense.h" +#include "Detector.h" + +#define SONY_VID 0x054C + +#define SONY_DS4_V1_PID 0x05C4 +#define SONY_DS4_V2_PID 0x09CC +#define SONY_DS4_RECEIVER_PID 0x0BA0 +#define SONY_DUALSENSE_PID 0x0CE6 +#define SONY_DUALSENSE_EDGE_PID 0x0DF2 + +void DetectSonyDS4Controllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SonyDS4Controller* controller = new SonyDS4Controller(dev, info->path); + RGBController_SonyDS4* rgb_controller = new RGBController_SonyDS4(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSonyDualSenseControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + bool is_bluetooth = info->interface_number == -1; + SonyDualSenseController* controller = new SonyDualSenseController(dev, info->path, is_bluetooth, name); + RGBController_SonyDualSense* rgb_controller = new RGBController_SonyDualSense(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("Sony DualShock 4", DetectSonyDS4Controllers, SONY_VID, SONY_DS4_V1_PID); +REGISTER_HID_DETECTOR("Sony DualShock 4", DetectSonyDS4Controllers, SONY_VID, SONY_DS4_V2_PID); +REGISTER_HID_DETECTOR("Sony DualShock 4", DetectSonyDS4Controllers, SONY_VID, SONY_DS4_RECEIVER_PID); +REGISTER_HID_DETECTOR("Sony DualSense", DetectSonyDualSenseControllers, SONY_VID, SONY_DUALSENSE_PID); +REGISTER_HID_DETECTOR("Sony DualSense Edge", DetectSonyDualSenseControllers, SONY_VID, SONY_DUALSENSE_EDGE_PID); diff --git a/Controllers/SteelSeriesController/SteelSeriesAerox3Controller/SteelSeriesAerox3Controller.cpp b/Controllers/SteelSeriesController/SteelSeriesAerox3Controller/SteelSeriesAerox3Controller.cpp new file mode 100644 index 0000000..c441563 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesAerox3Controller/SteelSeriesAerox3Controller.cpp @@ -0,0 +1,128 @@ +/*---------------------------------------------------------*\ +| SteelSeriesAerox3Controller.cpp | +| | +| Driver for SteelSeries Aerox 3 | +| | +| Chris M (Dr_No) 09 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "SteelSeriesAerox3Controller.h" +#include "LogManager.h" + +SteelSeriesAerox3Controller::SteelSeriesAerox3Controller(hid_device* dev_handle, steelseries_type proto_type, const char* path, std::string dev_name) : SteelSeriesMouseController(dev_handle, proto_type, path, dev_name) +{ + SendInit(); +} + +SteelSeriesAerox3Controller::~SteelSeriesAerox3Controller() +{ + hid_close(dev); +} + +void SteelSeriesAerox3Controller::SendInit() +{ + /*-----------------------------------------------------------------*\ + | This sets sensitivity and allows software mode?? max 5 uint8 | + | buffer[2] = Count eg. 0 thru 5 | + | buffer[4] to [8] = dpi / 50 range = 0x04 - 0xC7 eg. 400 = 0x08 | + \*-----------------------------------------------------------------*/ + uint8_t buffer[STEELSERIES_AEORX3_PACKET_SIZE] = { 0x00, 0x2D }; + + hid_send_feature_report(dev, buffer, STEELSERIES_AEORX3_PACKET_SIZE); +} + +std::string SteelSeriesAerox3Controller::GetFirmwareVersion() +{ + uint8_t result = 0; + const uint8_t CMD = 0x90; + const uint8_t sz = 16; + char version[sz + 1]; + + uint8_t buffer[STEELSERIES_AEORX3_PACKET_SIZE] = { 0x00, CMD, 0x00 }; + + hid_send_feature_report(dev, buffer, STEELSERIES_AEORX3_PACKET_SIZE); + do + { + result = hid_read_timeout(dev, buffer, STEELSERIES_AEORX3_PACKET_SIZE, STEELSERIES_AEORX3_TIMEOUT); + LOG_DEBUG("[%s] Reading version buffer: Bytes Read %d Buffer %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", STEELSERIES_AEORX3_NAME, result, + buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9], buffer[10]); + } while(result > 0 && buffer[0] != CMD); + + if(buffer[0] == CMD) + { + /*-----------------------------------------------------------------*\ + | Read the version from the second character | + \*-----------------------------------------------------------------*/ + memcpy(version, &buffer[1], sz); + version[sz] = 0; + std::string tmp = std::string(version); + LOG_DEBUG("[%s] Version: %s as string %s", STEELSERIES_AEORX3_NAME, version, tmp.c_str()); + + return tmp; + } + else + { + LOG_DEBUG("[%s] Unable to get version: giving up!", STEELSERIES_AEORX3_NAME); + return ""; + } +} + +steelseries_mouse SteelSeriesAerox3Controller::GetMouse() +{ + return aerox_3; +} + +void SteelSeriesAerox3Controller::SetLightEffectAll(uint8_t /*effect*/) +{ + /*-----------------------------------------------------------------*\ + | Not used by this device | + \*-----------------------------------------------------------------*/ +} + +void SteelSeriesAerox3Controller::SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness + ) +{ + uint8_t buffer[STEELSERIES_AEORX3_PACKET_SIZE] = { 0x00, 0x21 }; + + buffer[0x02] = 1 << zone_id; + uint8_t offset = 3 + zone_id * 3; + + buffer[offset] = red; + buffer[offset + 1] = green; + buffer[offset + 2] = blue; + + hid_write(dev, buffer, STEELSERIES_AEORX3_PACKET_SIZE); + + if(brightness != current_brightness) + { + SetBrightness(brightness); + current_brightness = brightness; + } +} + +void SteelSeriesAerox3Controller::SetBrightness(uint8_t brightness) +{ + uint8_t buffer[STEELSERIES_AEORX3_PACKET_SIZE] = { 0x00, 0x23, brightness }; + + hid_write(dev, buffer, STEELSERIES_AEORX3_PACKET_SIZE); +} + +void SteelSeriesAerox3Controller::Save() +{ + /*---------------------------------------------------------------------------------*\ + | Save packet was not confirmed as working but packet is verified as correct. | + | https://github.com/flozz/rivalcfg/blob/master/rivalcfg/devices/aerox3.py#L141 | + \*---------------------------------------------------------------------------------*/ + uint8_t buffer2[STEELSERIES_AEORX3_PACKET_SIZE] = { 0x00, 0x11, 0x00 }; + + hid_write(dev, buffer2, STEELSERIES_AEORX3_PACKET_SIZE); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesAerox3Controller/SteelSeriesAerox3Controller.h b/Controllers/SteelSeriesController/SteelSeriesAerox3Controller/SteelSeriesAerox3Controller.h new file mode 100644 index 0000000..67b2b18 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesAerox3Controller/SteelSeriesAerox3Controller.h @@ -0,0 +1,58 @@ +/*---------------------------------------------------------*\ +| SteelSeriesAerox3Controller.h | +| | +| Driver for SteelSeries Aerox 3 | +| | +| Chris M (Dr_No) 09 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "SteelSeriesGeneric.h" +#include "SteelSeriesMouseController.h" + +#define STEELSERIES_AEORX3_NAME "SteelSeries Aerox 3" +#define STEELSERIES_AEORX3_PACKET_SIZE 65 +#define STEELSERIES_AEORX3_TIMEOUT 250 + +static const steelseries_mouse aerox_3 = +{ + { 0x04 }, + { + {"Front", 0 }, + {"Middle", 1 }, + {"Rear", 2 }, + } +}; + +class SteelSeriesAerox3Controller: public SteelSeriesMouseController +{ +public: + SteelSeriesAerox3Controller(hid_device* dev_handle, steelseries_type proto_type, const char* path, std::string dev_name); + ~SteelSeriesAerox3Controller(); + + std::string GetFirmwareVersion() override; + steelseries_mouse GetMouse() override; + + void Save() override; + void SetLightEffectAll(uint8_t effect) override; + void SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness + ) override; +private: + void SendInit(); + void SetBrightness(uint8_t brightness); + uint8_t current_brightness; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesAerox5Controller/SteelSeriesAerox5Controller.cpp b/Controllers/SteelSeriesController/SteelSeriesAerox5Controller/SteelSeriesAerox5Controller.cpp new file mode 100644 index 0000000..23eb325 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesAerox5Controller/SteelSeriesAerox5Controller.cpp @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| SteelSeriesAerox5Controller.cpp | +| | +| Driver for the Steelseries Aerox 5 | +| | +| Bobby Quantum (BobbyQuantum) 19 May 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "SteelSeriesAerox5Controller.h" +#include "LogManager.h" + +SteelSeriesAerox5Controller::SteelSeriesAerox5Controller(hid_device* dev_handle, steelseries_type proto_type, const char* path, std::string dev_name) : SteelSeriesMouseController(dev_handle, proto_type, path, dev_name) +{ + SendInit(); +} + +SteelSeriesAerox5Controller::~SteelSeriesAerox5Controller() +{ + hid_close(dev); +} + +void SteelSeriesAerox5Controller::SendInit() +{ + /*-----------------------------------------------------------------*\ + | This sets sensitivity and allows software mode?? max 5 uint8 | + | buffer[2] = Count eg. 0 thru 5 | + | buffer[4] to [8] = dpi / 50 range = 0x04 - 0xC7 eg. 400 = 0x08 | + \*-----------------------------------------------------------------*/ + uint8_t buffer[STEELSERIES_AEROX5_PACKET_SIZE] = { 0x00, 0x2D }; + + hid_send_feature_report(dev, buffer, STEELSERIES_AEROX5_PACKET_SIZE); +} + +std::string SteelSeriesAerox5Controller::GetFirmwareVersion() +{ + uint8_t result = 0; + const uint8_t CMD = 0x90; + const uint8_t sz = 16; + char version[sz + 1]; + + uint8_t buffer[STEELSERIES_AEROX5_PACKET_SIZE] = { 0x00, CMD, 0x00 }; + + hid_send_feature_report(dev, buffer, STEELSERIES_AEROX5_PACKET_SIZE); + do + { + result = hid_read_timeout(dev, buffer, STEELSERIES_AEROX5_PACKET_SIZE, STEELSERIES_AEROX5_TIMEOUT); + LOG_DEBUG("[%s] Reading version buffer: Bytes Read %d Buffer %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", STEELSERIES_AEROX5_NAME, result, + buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9], buffer[10]); + } while(result > 0 && buffer[0] != CMD); + + if(buffer[0] == CMD) + { + /*-----------------------------------------------------------------*\ + | Read the version from the second character | + \*-----------------------------------------------------------------*/ + memcpy(version, &buffer[1], sz); + version[sz] = 0; + std::string tmp = std::string(version); + LOG_DEBUG("[%s] Version: %s as string %s", STEELSERIES_AEROX5_NAME, version, tmp.c_str()); + + return tmp; + } + else + { + LOG_DEBUG("[%s] Unable to get version: giving up!", STEELSERIES_AEROX5_NAME); + return ""; + } +} + +steelseries_mouse SteelSeriesAerox5Controller::GetMouse() +{ + return aerox_5; +} + +void SteelSeriesAerox5Controller::SetLightEffectAll(uint8_t /*effect*/) +{ + /*-----------------------------------------------------------------*\ + | Not used by this device | + \*-----------------------------------------------------------------*/ +} + +void SteelSeriesAerox5Controller::SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness + ) +{ + uint8_t buffer[STEELSERIES_AEROX5_PACKET_SIZE] = { 0x00, 0x21, 0x01 }; + + uint8_t offset = 0x03; + + switch (zone_id) + { + case 0: + offset = 0x03; + break; + case 1: + buffer[2] = 0x02; + offset = 0x06; + break; + case 2: + buffer[2] = 0x04; + offset = 0x09; + break; + case 3: + buffer[1] = 0x26; + offset = 0x04; + break; + default: + return; + } + + buffer[offset] = red; + buffer[offset + 1] = green; + buffer[offset + 2] = blue; + + hid_write(dev, buffer, STEELSERIES_AEROX5_PACKET_SIZE); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + + if (brightness != current_brightness) + { + SetBrightness(brightness); + current_brightness = brightness; + } +} + +void SteelSeriesAerox5Controller::SetBrightness(uint8_t brightness) +{ + uint8_t buffer[3] = { 0x00, 0x23, brightness }; + + hid_write(dev, buffer, STEELSERIES_AEROX5_PACKET_SIZE); + + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void SteelSeriesAerox5Controller::Save() +{ + uint8_t buffer2[3] = { 0x00, 0x11, 0x00 }; + + hid_write(dev, buffer2, STEELSERIES_AEROX5_PACKET_SIZE); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesAerox5Controller/SteelSeriesAerox5Controller.h b/Controllers/SteelSeriesController/SteelSeriesAerox5Controller/SteelSeriesAerox5Controller.h new file mode 100644 index 0000000..b08c16f --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesAerox5Controller/SteelSeriesAerox5Controller.h @@ -0,0 +1,61 @@ +/*---------------------------------------------------------*\ +| SteelSeriesAerox5Controller.h | +| | +| Driver for the Steelseries Aerox 5 | +| | +| Bobby Quantum (BobbyQuantum) 19 May 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "SteelSeriesGeneric.h" +#include "SteelSeriesMouseController.h" + +#define STEELSERIES_AEROX5_NAME "SteelSeries Aerox 5" +#define STEELSERIES_AEROX5_PACKET_SIZE 65 +#define STEELSERIES_AEROX5_TIMEOUT 250 + +static const steelseries_mouse aerox_5 = +{ + { 0x04 }, + { + {"Front", 0 }, + {"Middle", 1 }, + {"Rear", 2 }, + {"Reactive", 3 }, + } +}; + + +class SteelSeriesAerox5Controller : public SteelSeriesMouseController +{ +public: + SteelSeriesAerox5Controller(hid_device *dev_handle, steelseries_type proto_type, const char *path, std::string dev_name); + ~SteelSeriesAerox5Controller(); + + std::string GetFirmwareVersion() override; + steelseries_mouse GetMouse() override; + + void Save() override; + void SetLightEffectAll(uint8_t effect) override; + void SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness + ) override; + +private: + void SendInit(); + void SetBrightness(uint8_t brightness); + uint8_t current_brightness; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesAeroxWirelessController/SteelSeriesAeroxWirelessController.cpp b/Controllers/SteelSeriesController/SteelSeriesAeroxWirelessController/SteelSeriesAeroxWirelessController.cpp new file mode 100644 index 0000000..741832e --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesAeroxWirelessController/SteelSeriesAeroxWirelessController.cpp @@ -0,0 +1,225 @@ +/*---------------------------------------------------------*\ +| SteelSeriesAeroxWirelessController.cpp | +| | +| Driver for SteelSeries Aerox 3, 5 and 9 Wireless | +| | +| Ensar S (esensar) 09 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "SteelSeriesAeroxWirelessController.h" +#include "LogManager.h" +#include "SteelSeriesGeneric.h" + +SteelSeriesAeroxWirelessController::SteelSeriesAeroxWirelessController(hid_device* dev_handle, steelseries_type proto_type, const char* path, std::string dev_name) : SteelSeriesMouseController(dev_handle, proto_type, path, dev_name) +{ + switch(proto_type) + { + case AEROX_3_WIRELESS: + name = STEELSERIES_AEROX3_WIRELESS_NAME; + break; + case AEROX_3_WIRELESS_WIRED: + name = STEELSERIES_AEROX3_WIRELESS_WIRED_NAME; + break; + case AEROX_5_WIRELESS: + name = STEELSERIES_AEROX5_WIRELESS_NAME; + break; + case AEROX_5_WIRELESS_WIRED: + name = STEELSERIES_AEROX5_WIRELESS_WIRED_NAME; + break; + case AEROX_5_DESTINY_WIRELESS: + name = STEELSERIES_AEROX5_DESTINY_WIRELESS_NAME; + break; + case AEROX_5_DESTINY_WIRELESS_WIRED: + name = STEELSERIES_AEROX5_DESTINY_WIRELESS_WIRED_NAME; + break; + case AEROX_5_DIABLO_WIRELESS: + name = STEELSERIES_AEROX5_DIABLO_WIRELESS_NAME; + break; + case AEROX_5_DIABLO_WIRELESS_WIRED: + name = STEELSERIES_AEROX5_DIABLO_WIRELESS_WIRED_NAME; + break; + case AEROX_9_WIRELESS: + name = STEELSERIES_AEROX9_WIRELESS_NAME; + break; + case AEROX_9_WIRELESS_WIRED: + name = STEELSERIES_AEROX9_WIRELESS_WIRED_NAME; + break; + default: + name = STEELSERIES_AEROX3_WIRELESS_NAME; + break; + } + SendInit(); +} + +SteelSeriesAeroxWirelessController::~SteelSeriesAeroxWirelessController() +{ + hid_close(dev); +} + +void SteelSeriesAeroxWirelessController::SendInit() +{ + /*-----------------------------------------------------------------*\ + | This sets sensitivity and allows software mode?? max 5 uint8 | + | buffer[2] = Count eg. 0 thru 5 | + | buffer[4] to [8] = dpi / 50 range = 0x04 - 0xC7 eg. 400 = 0x08 | + \*-----------------------------------------------------------------*/ + uint8_t buffer[STEELSERIES_AEROX_WIRELESS_PACKET_SIZE] = { 0x00, 0x2D }; + + hid_send_feature_report(dev, buffer, STEELSERIES_AEROX_WIRELESS_PACKET_SIZE); +} + +bool SteelSeriesAeroxWirelessController::IsWireless() +{ + switch(proto) + { + case AEROX_3_WIRELESS: + case AEROX_5_WIRELESS: + case AEROX_5_DESTINY_WIRELESS: + case AEROX_5_DIABLO_WIRELESS: + case AEROX_9_WIRELESS: + return true; + break; + case AEROX_3_WIRELESS_WIRED: + case AEROX_5_WIRELESS_WIRED: + case AEROX_5_DESTINY_WIRELESS_WIRED: + case AEROX_5_DIABLO_WIRELESS_WIRED: + case AEROX_9_WIRELESS_WIRED: + default: + return false; + break; + } +} + +std::string SteelSeriesAeroxWirelessController::GetFirmwareVersion() +{ + uint8_t result = 0; + const uint8_t CMD = 0x90; + const uint8_t sz = 16; + char version[sz + 1]; + + uint8_t buffer[STEELSERIES_AEROX_WIRELESS_PACKET_SIZE] = { 0x00, CMD, 0x00 }; + + hid_send_feature_report(dev, buffer, STEELSERIES_AEROX_WIRELESS_PACKET_SIZE); + do + { + result = hid_read_timeout(dev, buffer, STEELSERIES_AEROX_WIRELESS_PACKET_SIZE, STEELSERIES_AEROX_WIRELESS_TIMEOUT); + LOG_DEBUG("[%s] Reading version buffer: Bytes Read %d Buffer %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", name, result, + buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9], buffer[10]); + } while(result > 0 && buffer[0] != CMD); + + if(buffer[0] == CMD) + { + /*-----------------------------------------------------------------*\ + | Read the version from the second character | + \*-----------------------------------------------------------------*/ + memcpy(version, &buffer[1], sz); + version[sz] = 0; + std::string tmp = std::string(version); + LOG_DEBUG("[%s] Version: %s as string %s", name, version, tmp.c_str()); + + return tmp; + } + else + { + LOG_DEBUG("[%s] Unable to get version: giving up!", name); + return ""; + } +} + +steelseries_mouse SteelSeriesAeroxWirelessController::GetMouse() +{ + switch(proto) + { + case AEROX_9_WIRELESS: + case AEROX_9_WIRELESS_WIRED: + return aerox_9; + break; + default: + return aerox_3_wireless; + break; + } +} + +void SteelSeriesAeroxWirelessController::SetLightEffectAll(uint8_t effect) +{ + if(effect == 0x05) + { + uint8_t buffer[STEELSERIES_AEROX_WIRELESS_PACKET_SIZE] = { 0x00, 0x22, 0xFF }; + + WriteBuffer(buffer); + } +} + +void SteelSeriesAeroxWirelessController::SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness + ) +{ + uint8_t buffer[STEELSERIES_AEROX_WIRELESS_PACKET_SIZE] = { 0x00, 0x21, 0x01 }; + uint8_t offset = 0x04; + + if (zone_id == 3 && (proto == AEROX_9_WIRELESS_WIRED || proto == AEROX_9_WIRELESS)) + { + buffer[0x03] = 0x00; + buffer[0x01] = 0x26; + } + else + { + buffer[0x03] = zone_id; + } + buffer[offset] = red; + buffer[offset + 1] = green; + buffer[offset + 2] = blue; + + WriteBuffer(buffer); + + // Supports only 10 steps of brightness + brightness = (uint8_t)(brightness / 10); + + if(brightness != current_brightness) + { + SetBrightness(brightness); + current_brightness = brightness; + } +} + +void SteelSeriesAeroxWirelessController::SetBrightness(uint8_t brightness) +{ + uint8_t buffer[STEELSERIES_AEROX_WIRELESS_PACKET_SIZE] = { 0x00, 0x23, brightness }; + + WriteBuffer(buffer); +} + +void SteelSeriesAeroxWirelessController::Save() +{ + /*---------------------------------------------------------------------------------*\ + | Save packet was not confirmed as working but packet is verified as correct. | + | https://github.com/flozz/rivalcfg/blob/master/rivalcfg/devices/aerox3.py#L141 | + \*---------------------------------------------------------------------------------*/ + uint8_t buffer2[STEELSERIES_AEROX_WIRELESS_PACKET_SIZE] = { 0x00, 0x11, 0x00 }; + + WriteBuffer(buffer2); +} + +void SteelSeriesAeroxWirelessController::WriteBuffer(uint8_t* buffer) +{ + if(IsWireless()) + { + buffer[1] |= STEELSERIES_AEROX_WIRELESS_FLAG; + } + + hid_write(dev, buffer, STEELSERIES_AEROX_WIRELESS_PACKET_SIZE); + + if(IsWireless()) + { + // Readback required in wireless mode + hid_read_timeout(dev, buffer, STEELSERIES_AEROX_WIRELESS_PACKET_SIZE, STEELSERIES_AEROX_WIRELESS_TIMEOUT); + } +} diff --git a/Controllers/SteelSeriesController/SteelSeriesAeroxWirelessController/SteelSeriesAeroxWirelessController.h b/Controllers/SteelSeriesController/SteelSeriesAeroxWirelessController/SteelSeriesAeroxWirelessController.h new file mode 100644 index 0000000..16afc83 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesAeroxWirelessController/SteelSeriesAeroxWirelessController.h @@ -0,0 +1,82 @@ +/*---------------------------------------------------------*\ +| SteelSeriesAeroxWirelessController.h | +| | +| Driver for SteelSeries Aerox 3, 5 and 9 Wireless | +| | +| Ensar S (esensar) 09 Sep 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "SteelSeriesGeneric.h" +#include "SteelSeriesMouseController.h" + +#define STEELSERIES_AEROX3_WIRELESS_NAME "SteelSeries Aerox 3 Wireless (2.4 GHz wireless mode)" +#define STEELSERIES_AEROX3_WIRELESS_WIRED_NAME "SteelSeries Aerox 3 Wireless (wired mode)" +#define STEELSERIES_AEROX5_WIRELESS_NAME "SteelSeries Aerox 5 Wireless (2.4 GHz wireless mode)" +#define STEELSERIES_AEROX5_WIRELESS_WIRED_NAME "SteelSeries Aerox 5 Wireless (wired mode)" +#define STEELSERIES_AEROX5_DESTINY_WIRELESS_NAME "SteelSeries Aerox 5 Wireless Destiny 2 Edition (2.4 GHz wireless mode)" +#define STEELSERIES_AEROX5_DESTINY_WIRELESS_WIRED_NAME "SteelSeries Aerox 5 Wireless Destiny 2 Edition (wired mode)" +#define STEELSERIES_AEROX5_DIABLO_WIRELESS_NAME "SteelSeries Aerox 5 Wireless Diablo IV Edition (2.4 GHz wireless mode)" +#define STEELSERIES_AEROX5_DIABLO_WIRELESS_WIRED_NAME "SteelSeries Aerox 5 Wireless Diablo IV Edition (wired mode)" +#define STEELSERIES_AEROX9_WIRELESS_NAME "SteelSeries Aerox 9 Wireless (2.4 GHz wireless mode)" +#define STEELSERIES_AEROX9_WIRELESS_WIRED_NAME "SteelSeries Aerox 9 Wireless (wired mode)" +#define STEELSERIES_AEROX_WIRELESS_PACKET_SIZE 64 +#define STEELSERIES_AEROX_WIRELESS_TIMEOUT 250 +#define STEELSERIES_AEROX_WIRELESS_FLAG 0b01000000 + +static const steelseries_mouse aerox_3_wireless = +{ + { 0x04, 0x05 }, + { + {"Front", 0 }, + {"Middle", 1 }, + {"Rear", 2 }, + } +}; + +static const steelseries_mouse aerox_9 = +{ + { 0x04, 0x05 }, + { + {"Front", 0 }, + {"Middle", 1 }, + {"Rear", 2 }, + {"Reactive", 3 }, + } +}; + +class SteelSeriesAeroxWirelessController: public SteelSeriesMouseController +{ +public: + SteelSeriesAeroxWirelessController(hid_device* dev_handle, steelseries_type proto_type, const char* path, std::string dev_name); + ~SteelSeriesAeroxWirelessController(); + + std::string GetFirmwareVersion() override; + steelseries_mouse GetMouse() override; + + void Save() override; + void SetLightEffectAll(uint8_t effect) override; + void SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness + ) override; +private: + bool IsWireless(); + void SendInit(); + void WriteBuffer(uint8_t* buffer); + void SetBrightness(uint8_t brightness); + uint8_t current_brightness; + const char* name; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesApex3Controller/RGBController_SteelSeriesApex3.cpp b/Controllers/SteelSeriesController/SteelSeriesApex3Controller/RGBController_SteelSeriesApex3.cpp new file mode 100644 index 0000000..84511af --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApex3Controller/RGBController_SteelSeriesApex3.cpp @@ -0,0 +1,131 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesApex3.cpp | +| | +| RGBController for SteelSeries Apex 3 | +| | +| Chris M (Dr_No) 23 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SteelSeriesApex3.h" + +/**------------------------------------------------------------------*\ + @name Steel Series Apex Tri Zone Keyboards + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectSteelSeriesApexTZone + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SteelSeriesApex3::RGBController_SteelSeriesApex3(SteelSeriesApex3Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "SteelSeries"; + type = DEVICE_TYPE_KEYBOARD; + description = "SteelSeries Apex 3 Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode direct; + direct.name = "Direct"; + direct.value = static_cast(APEX3_MODES::DIRECT); + direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + if(controller->SupportsSave()) + { + direct.flags |= MODE_FLAG_MANUAL_SAVE; + } + direct.color_mode = MODE_COLORS_PER_LED; + direct.brightness_min = STEELSERIES_APEX3_BRIGHTNESS_MIN; + direct.brightness_max = controller->GetMaxBrightness(); + direct.brightness = direct.brightness_max; + modes.push_back(direct); + + if(controller->SupportsRainbowWave()) + { + mode rainbow; + rainbow.name = "Rainbow Wave"; + rainbow.value = static_cast(APEX3_MODES::RAINBOW_WAVE); + rainbow.flags = MODE_FLAG_HAS_BRIGHTNESS; + rainbow.color_mode = MODE_COLORS_NONE; + rainbow.brightness_min = STEELSERIES_APEX3_BRIGHTNESS_MIN; + rainbow.brightness_max = controller->GetMaxBrightness(); + rainbow.brightness = rainbow.brightness_max; + modes.push_back(rainbow); + } + + SetupZones(); +} + +RGBController_SteelSeriesApex3::~RGBController_SteelSeriesApex3() +{ + delete controller; +} + +void RGBController_SteelSeriesApex3::DeviceSaveMode() +{ + controller->Save(); +} + +void RGBController_SteelSeriesApex3::SetupZones() +{ + uint8_t led_count = controller->GetLedCount(); + + zone curr_zone; + curr_zone.name = "Keyboard"; + curr_zone.type = ZONE_TYPE_LINEAR; + curr_zone.leds_min = led_count; + curr_zone.leds_max = led_count; + curr_zone.leds_count = led_count; + curr_zone.matrix_map = NULL; + zones.push_back(curr_zone); + + for(size_t i = 0; i < curr_zone.leds_count; i++) + { + led zone_led; + zone_led.name = "LED " + std::to_string(i); + leds.push_back(zone_led); + } + + SetupColors(); +} + +void RGBController_SteelSeriesApex3::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SteelSeriesApex3::DeviceUpdateLEDs() +{ + controller->SetColor(colors, modes[active_mode].value, modes[active_mode].brightness); +} + +void RGBController_SteelSeriesApex3::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SteelSeriesApex3::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SteelSeriesApex3::DeviceUpdateMode() +{ + if(modes[active_mode].color_mode == MODE_FLAG_HAS_PER_LED_COLOR) + { + DeviceUpdateLEDs(); + } + else + { + controller->SetColor(modes[active_mode].colors, modes[active_mode].value, modes[active_mode].brightness); + } +} diff --git a/Controllers/SteelSeriesController/SteelSeriesApex3Controller/RGBController_SteelSeriesApex3.h b/Controllers/SteelSeriesController/SteelSeriesApex3Controller/RGBController_SteelSeriesApex3.h new file mode 100644 index 0000000..8fd14d5 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApex3Controller/RGBController_SteelSeriesApex3.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesApex3.h | +| | +| RGBController for SteelSeries Apex 3 | +| | +| Chris M (Dr_No) 23 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SteelSeriesApex3Controller.h" + +enum class APEX3_MODES +{ + DIRECT = 0, + RAINBOW_WAVE = 1 +}; + +class RGBController_SteelSeriesApex3 : public RGBController +{ +public: + RGBController_SteelSeriesApex3(SteelSeriesApex3Controller* controller_ptr); + ~RGBController_SteelSeriesApex3(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + SteelSeriesApex3Controller* controller; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesApex3Controller/SteelSeriesApex3Controller.cpp b/Controllers/SteelSeriesController/SteelSeriesApex3Controller/SteelSeriesApex3Controller.cpp new file mode 100644 index 0000000..5901f01 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApex3Controller/SteelSeriesApex3Controller.cpp @@ -0,0 +1,49 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApex3Controller.cpp | +| | +| Driver for SteelSeries Apex 3 | +| | +| Chris M (Dr_No) 23 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SteelSeriesApex3Controller.h" +#include "StringUtils.h" + +SteelSeriesApex3Controller::SteelSeriesApex3Controller(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +SteelSeriesApex3Controller::~SteelSeriesApex3Controller() +{ + hid_close(dev); +} + +std::string SteelSeriesApex3Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string SteelSeriesApex3Controller::GetNameString() +{ + return(name); +} + +std::string SteelSeriesApex3Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesApex3Controller/SteelSeriesApex3Controller.h b/Controllers/SteelSeriesController/SteelSeriesApex3Controller/SteelSeriesApex3Controller.h new file mode 100644 index 0000000..57b4307 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApex3Controller/SteelSeriesApex3Controller.h @@ -0,0 +1,45 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApex3Controller.h | +| | +| Driver for SteelSeries Apex 3 | +| | +| Chris M (Dr_No) 23 Feb 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "SteelSeriesGeneric.h" + +#define STEELSERIES_APEX3_BRIGHTNESS_MIN 0x00 +#define STEELSERIES_APEX3_HID_TIMEOUT 100 + +class SteelSeriesApex3Controller +{ +public: + SteelSeriesApex3Controller(hid_device* dev_handle, const char* path, std::string dev_name); + virtual ~SteelSeriesApex3Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + steelseries_type GetKeyboardType(); + + virtual void SetColor(std::vector colors, uint8_t mode, uint8_t brightness) = 0; + virtual void Save() = 0; + virtual uint8_t GetLedCount() = 0; + virtual uint8_t GetMaxBrightness() = 0; + virtual bool SupportsRainbowWave() = 0; + virtual bool SupportsSave() = 0; + + hid_device* dev; + +private: + std::string location; + std::string name; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesApex8ZoneController/SteelSeriesApex8ZoneController.cpp b/Controllers/SteelSeriesController/SteelSeriesApex8ZoneController/SteelSeriesApex8ZoneController.cpp new file mode 100644 index 0000000..8799110 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApex8ZoneController/SteelSeriesApex8ZoneController.cpp @@ -0,0 +1,99 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApex8ZoneController.cpp | +| | +| Driver for SteelSeries Apex 8 Zone | +| | +| Chris M (Dr_No) 23 Feb 2022 | +| Paul K. Gerke 27 Oct 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SteelSeriesApex8ZoneController.h" +#include "LogManager.h" + +SteelSeriesApex8ZoneController::SteelSeriesApex8ZoneController(hid_device* dev_handle, const char* path, std::string dev_name) : SteelSeriesApex3Controller(dev_handle, path, dev_name) +{ + +} + +SteelSeriesApex8ZoneController::~SteelSeriesApex8ZoneController() +{ + +} + +uint8_t SteelSeriesApex8ZoneController::GetLedCount() +{ + return STEELSERIES_8Z_LED_COUNT; +} + +uint8_t SteelSeriesApex8ZoneController::GetMaxBrightness() +{ + return STEELSERIES_8Z_BRIGHTNESS_MAX; +} + +bool SteelSeriesApex8ZoneController::SupportsRainbowWave() +{ + return true; +} + +bool SteelSeriesApex8ZoneController::SupportsSave() +{ + return false; +} + +void SteelSeriesApex8ZoneController::Save() +{ + /*---------------------------------------------------------*\ + | This device does not yet support saving | + \*---------------------------------------------------------*/ +} + +void SteelSeriesApex8ZoneController::SetBrightness(uint8_t brightness) +{ + uint8_t buffer[STEELSERIES_8Z_WRITE_PACKET_SIZE] = { 0x00, 0x23, brightness }; + + hid_write(dev, buffer, STEELSERIES_8Z_WRITE_PACKET_SIZE); + + current_brightness = brightness; +} + +uint8_t SteelSeriesApex8ZoneController::GetBrightness() +{ + uint8_t buffer[STEELSERIES_8Z_WRITE_PACKET_SIZE] = { 0x00, 0xA3 }; + + hid_write(dev, buffer, STEELSERIES_8Z_WRITE_PACKET_SIZE); + + int result = hid_read_timeout(dev, buffer, STEELSERIES_8Z_WRITE_PACKET_SIZE, STEELSERIES_APEX3_HID_TIMEOUT); + if (result > 1 && buffer[0x00] == 0xA3) + { + return(buffer[0x01]); + } + + return(STEELSERIES_8Z_BRIGHTNESS_MAX); +} + +void SteelSeriesApex8ZoneController::SetColor(std::vector colors, uint8_t mode, uint8_t brightness) +{ + uint8_t buffer[STEELSERIES_8Z_WRITE_PACKET_SIZE] = { 0x00, 0x21, 0xFF }; + + buffer[1] += mode; + + for(unsigned int i = 0; i < colors.size(); i++) + { + uint8_t index = i * 3; + + buffer[index + 3] = RGBGetRValue(colors[i]);; + buffer[index + 4] = RGBGetGValue(colors[i]);; + buffer[index + 5] = RGBGetBValue(colors[i]);; + } + + hid_write(dev, buffer, STEELSERIES_8Z_WRITE_PACKET_SIZE); + + if(current_brightness != brightness) + { + SetBrightness(brightness); + } +} diff --git a/Controllers/SteelSeriesController/SteelSeriesApex8ZoneController/SteelSeriesApex8ZoneController.h b/Controllers/SteelSeriesController/SteelSeriesApex8ZoneController/SteelSeriesApex8ZoneController.h new file mode 100644 index 0000000..b6f6911 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApex8ZoneController/SteelSeriesApex8ZoneController.h @@ -0,0 +1,107 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApex8ZoneController.h | +| | +| Driver for SteelSeries Apex 8 Zone | +| | +| Chris M (Dr_No) 23 Feb 2022 | +| Paul K. Gerke 27 Oct 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "SteelSeriesGeneric.h" +#include "SteelSeriesApex3Controller.h" + +#define STEELSERIES_8Z_LED_COUNT 8 +#define STEELSERIES_8Z_WRITE_PACKET_SIZE 65 +#define STEELSERIES_8Z_BRIGHTNESS_MAX 0x10 + +class SteelSeriesApex8ZoneController : public SteelSeriesApex3Controller +{ +public: + SteelSeriesApex8ZoneController(hid_device *dev_handle, const char *path, std::string dev_name); + ~SteelSeriesApex8ZoneController(); + + void SetColor(std::vector colors, uint8_t mode, uint8_t brightness); + void Save(); + uint8_t GetLedCount(); + uint8_t GetMaxBrightness(); + bool SupportsRainbowWave(); + bool SupportsSave(); + +private: + uint8_t current_brightness; + + void SetBrightness(uint8_t brightness); + uint8_t GetBrightness(); +}; + +/*-----------------------------------------------------------------------------------------------*\ +# General keyboard behavior overview + +- The keyboard does not appear to have persistent memory: Cycling the USB + connection resets the keyboard to a `Rainbow Wave` mode. SteelSeries GG + has to be running to set color patterns, indicating `Direct` mode. +- The brightness settings of the keyboard can be read back by the + SteelSeries GG Software from the keyboard. Changing the brightness with + Mod+F11/F12 moves the slider in the SteelSeries GG software. Also, when + changing the brightness and opening the configuration dialog, the slider + is updated to the value set via Mod+F11/F12. + +# Message protocol + +The HID-Messages contain a data-packet that is sent with HID-Requests to the +keyboard to change the keyboard's mode. The first byte seems to be a command-ID. +The full message-length is 64 bytes. + +Values not explicitly mentioned below are zero-bytes. + +## 0x21: Set LED color + + 0x21 BM R1 G1 B1 R2 G2 B2 ... [up to rgb values for zone 8] + + - Bit Mask
A bit mask of LEDs to set where 0xFF sets all LEDs. Setting 2N + as 0 will ***not*** set LED N. + + - The next sequence of bytes are RGB triplets for each of the 8 LEDs. + Value range from 0 up to 255 for each color value. + +## 0x22: Set the keyboard to `Rainbow Wave` mode. Message + + 0x22 0xFF + + - As for 0x21, 0xFF seems to be constant. + +## 0x23: Set Brightness + + Sets the overall brightness of all color channels. Seems to act as a factor + for the individual RGB values, but can be individually adjusted by the user + through the Mod+F11/F12 key combinations. + + 0x23 [brightness] + + - Brightness values are in the range from 0x00 (dark) to 0x10 (full + brightness) + +# Other observed commands/messages of unknown purpose were: + +## 0x6C 0x00 0x01 0x01 + This triggers some sort of URB_INTERRUPT from USB endpoint .2 + reporting back some data however unsure what the data is. It seems + to be 0x6C followed by only zeroes. + +## 0xA3 + Query brightness? + +## 0x90 + Best guess "get firmware version" or similar... or some other settings. Again + a complex answer is echoed from endpoint .2 starting with 0x90 and a + few bytes (~8-10) of data. + +\*-----------------------------------------------------------------------------------------------*/ diff --git a/Controllers/SteelSeriesController/SteelSeriesApex9Controller/SteelSeriesApex9Controller.cpp b/Controllers/SteelSeriesController/SteelSeriesApex9Controller/SteelSeriesApex9Controller.cpp new file mode 100644 index 0000000..594ed6c --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApex9Controller/SteelSeriesApex9Controller.cpp @@ -0,0 +1,160 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApex9Controller.cpp | +| | +| Driver for SteelSeries Apex 9 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include +#include "SteelSeriesApex9Controller.h" + +using namespace std::chrono_literals; + +static unsigned int keys[] = {0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, //20 + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, + 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, //40 + 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, //60 + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, + 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x64, 0xE0, //80 + 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xF0, 0x31, 0x87, + 0x88, 0x89, 0x8A, 0x8B, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, //100 + 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, + 0x63 }; + +SteelSeriesApex9Controller::SteelSeriesApex9Controller(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name) : SteelSeriesApexBaseController (dev_handle, path, dev_name) +{ + proto_type = type; +} + +SteelSeriesApex9Controller::~SteelSeriesApex9Controller() +{ + hid_close(dev); +} + +void SteelSeriesApex9Controller::SetMode(unsigned char mode /*mode*/, std::vector /*colors*/ ) +{ + unsigned char mode_colors[9]; + + active_mode = mode; + + memset(mode_colors, 0x00, sizeof(mode_colors)); +} + +void SteelSeriesApex9Controller::SetLEDsDirect(std::vector colors) +{ + unsigned char buf[APEX_9_PACKET_LENGTH]; + int num_keys = 0; + + num_keys = sizeof(keys) / sizeof(*keys); + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0; + buf[0x01] = APEX_9_PACKET_ID_DIRECT; + buf[0x02] = num_keys; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for(int i = 0; i < num_keys; i++) + { + buf[(i*4)+3] = keys[i]; + buf[(i*4)+4] = RGBGetRValue(colors[i]); + buf[(i*4)+5] = RGBGetGValue(colors[i]); + buf[(i*4)+6] = RGBGetBValue(colors[i]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, APEX_9_PACKET_LENGTH); + +} + +std::string SteelSeriesApex9Controller::GetSerial() +{ + std::string return_string = ""; + + switch(proto_type) + { + case APEX_9_TKL: + return_string = "64847"; + break; + case APEX_9_MINI: + return_string = "64837"; + break; + default: + return_string = "Apex 9 GetSerial() error"; + } + + return(return_string); +} + +std::string SteelSeriesApex9Controller::GetVersion() +{ + std::string return_string = "Unsupported protocol"; + + unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE]; + unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE]; + int result; + + memset(obuf, 0x00, sizeof(obuf)); + obuf[0x00] = 0; + obuf[0x01] = 0x90; + hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE); + result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2); + + if(result > 0) + { + std::string fwver(ibuf, ibuf+STEELSERIES_PACKET_IN_SIZE); + fwver = fwver.substr(2, fwver.size()); + fwver = fwver.c_str(); + + /*---------------------------------------------*\ + | Find 2 periods in string, if found we can | + | form a X.Y.Z revision. | + \*---------------------------------------------*/ + std::size_t majorp = fwver.find('.'); + if(majorp != std::string::npos) + { + std::size_t minorp = fwver.find('.', majorp+1); + if(minorp != std::string::npos) + { + std::string major = fwver.substr(0, majorp); + std::string minor = fwver.substr(majorp+1, (minorp-majorp-1)); + std::string build = fwver.substr(minorp+1); + return_string = "KBD: " + major + "." + minor + "." + build; + } + } + } + + return(return_string); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void SteelSeriesApex9Controller::SelectProfile(unsigned char profile) +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer, set up packet and send | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + buf[0x00] = 0; + buf[0x01] = 0x89; + buf[0x02] = profile; + hid_send_feature_report(dev, buf, 65); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesApex9Controller/SteelSeriesApex9Controller.h b/Controllers/SteelSeriesController/SteelSeriesApex9Controller/SteelSeriesApex9Controller.h new file mode 100644 index 0000000..6bc6336 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApex9Controller/SteelSeriesApex9Controller.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApex9Controller.h | +| | +| Driver for SteelSeries Apex 9 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "SteelSeriesGeneric.h" +#include "SteelSeriesApexBaseController.h" + +enum +{ + APEX_9_PACKET_ID_DIRECT = 0x40, /* Direct mode */ + APEX_9_PACKET_LENGTH = 513, +}; + +class SteelSeriesApex9Controller : public SteelSeriesApexBaseController +{ +public: + SteelSeriesApex9Controller(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name); + ~SteelSeriesApex9Controller(); + + void SetMode(unsigned char mode, std::vector colors); + void SetLEDsDirect(std::vector colors); + + std::string GetSerial() override; + std::string GetVersion() override; + +private: + void SelectProfile(unsigned char profile); +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesApexBaseController.cpp b/Controllers/SteelSeriesController/SteelSeriesApexBaseController.cpp new file mode 100644 index 0000000..2d83bdf --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexBaseController.cpp @@ -0,0 +1,152 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApexBaseController.cpp | +| | +| Driver base for SteelSeries Apex | +| | +| Florian Heilmann (FHeilmann) 19 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "SteelSeriesApexBaseController.h" +#include + +SteelSeriesApexBaseController::SteelSeriesApexBaseController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +SteelSeriesApexBaseController::~SteelSeriesApexBaseController() +{ + +} + +std::string SteelSeriesApexBaseController::GetLocation() +{ + return("HID: " + location); +}; + +std::string SteelSeriesApexBaseController::GetName() +{ + return(name); +} + +/*---------------------------------------------------------*\ +| Gen 1 Apex Pro stores the unit serial number in firmware. | +| The first 5 digits determine the region of the keyboard. | +| This is not the case for Gen 3, call to this function | +| will be ignored. | +\*---------------------------------------------------------*/ +std::string SteelSeriesApexBaseController::GetSerial() +{ + std::string return_string = ""; + if(proto_type == APEX && kbd_quirk == APEX_GEN1) + { + unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE]; + unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE]; + int result; + + memset(obuf, 0x00, sizeof(obuf)); + obuf[0x00] = 0; + obuf[0x01] = 0xFF; + hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE); + + result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2); + + /*-------------------------------------------------*\ + | Only the first 19 bytes are of value | + \*-------------------------------------------------*/ + if(result > 0) + { + std::string serialnum(ibuf, ibuf+19); + return_string = serialnum; + } + } + + return(return_string); +} + +std::string ExtractVersion(std::string version_string) +{ + /*---------------------------------------------*\ + | Find 2 periods in string, if found we can | + | form a X.Y.Z revision. | + \*---------------------------------------------*/ + std::size_t majorp = version_string.find('.'); + if(majorp != std::string::npos) + { + std::size_t minorp = version_string.find('.', majorp+1); + if(minorp != std::string::npos) + { + std::string major = version_string.substr(0, majorp); + std::string minor = version_string.substr(majorp+1, (minorp-majorp-1)); + std::string build = version_string.substr(minorp+1); + return major + "." + minor + "." + build; + } + } + return ""; +} + +std::string SteelSeriesApexBaseController::GetVersion() +{ + std::string return_string = "Unsupported protocol"; + + if(proto_type == APEX) + { + /*-------------------------------------------------*\ + | Gen 1 & 2 Apex Pro report KBD and LED firmware | + | Gen 3 only reports the KBD firmware, ignoring | + | requests to read the LED version | + \*-------------------------------------------------*/ + unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE]; + unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE]; + int result; + + memset(obuf, 0x00, sizeof(obuf)); + obuf[0x00] = 0; + obuf[0x01] = 0x90; + hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE); + result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2); + + if(result > 0) + { + std::string fwver(ibuf, ibuf+STEELSERIES_PACKET_IN_SIZE); + fwver.erase(std::remove(fwver.begin(), fwver.end(), '\0'), fwver.end()); + + /*---------------------------------------------*\ + | Apex Pro Gen 3 needs the first char dropped | + \*---------------------------------------------*/ + if(kbd_quirk == APEX_GEN3) + { + fwver.erase(0,1); + } + + return_string = "KBD: " + ExtractVersion(fwver); + } + + /*-------------------------------------------------*\ + | Clear and reuse buffer | + \*-------------------------------------------------*/ + if(kbd_quirk != APEX_GEN3) + { + memset(ibuf, 0x00, sizeof(ibuf)); + obuf[0x02] = 0x01; + hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE); + result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 10); + + if(result > 0) + { + std::string fwver(ibuf, ibuf+STEELSERIES_PACKET_IN_SIZE); + fwver.erase(std::remove(fwver.begin(), fwver.end(), '\0'), fwver.end()); + fwver = fwver.c_str(); + + return_string = return_string + " / LED: " + ExtractVersion(fwver); + } + } + } + + return(return_string); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesApexBaseController.h b/Controllers/SteelSeriesController/SteelSeriesApexBaseController.h new file mode 100644 index 0000000..c51bd7c --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexBaseController.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApexBaseController.h | +| | +| Driver base for SteelSeries Apex | +| | +| Florian Heilmann (FHeilmann) 19 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "SteelSeriesGeneric.h" + +#define STEELSERIES_PACKET_IN_SIZE 64 +#define STEELSERIES_PACKET_OUT_SIZE STEELSERIES_PACKET_IN_SIZE + 1 + +/*-------------------------------------------------*\ +| Gen 1: 2019-22 models (all FW) & 2023 FW < 1.19.7 | +| Gen 2: 2023 models with FW >= 1.19.7 | +| Gen 3: 2025+ and may feature Gen 3 in the name | +\*-------------------------------------------------*/ + +typedef enum +{ + APEX_GEN1 = 0x00, + APEX_GEN2 = 0x01, + APEX_GEN3 = 0x02, + +} protocol_quirk; + +class SteelSeriesApexBaseController +{ +public: + SteelSeriesApexBaseController(hid_device* dev_handle, const char* path, std::string dev_name); + virtual ~SteelSeriesApexBaseController(); + + std::string GetLocation(); + std::string GetName(); + virtual std::string GetSerial(); + virtual std::string GetVersion(); + + virtual void SetMode(unsigned char mode, std::vector colors) = 0; + + virtual void SetLEDsDirect(std::vector colors) = 0; + + steelseries_type proto_type; + +protected: + hid_device* dev; + unsigned char active_mode; + std::string location; + std::string name; + protocol_quirk kbd_quirk; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesApexController/RGBController_SteelSeriesApex.cpp b/Controllers/SteelSeriesController/SteelSeriesApexController/RGBController_SteelSeriesApex.cpp new file mode 100644 index 0000000..a2128db --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexController/RGBController_SteelSeriesApex.cpp @@ -0,0 +1,160 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesApex.cpp | +| | +| RGBController for SteelSeries Apex 7 | +| | +| Eric Samuelson (edbgon) 05 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_SteelSeriesApex.h" +#include "SteelSeriesApexRegions.h" + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX +}; + +static const unsigned int zone_sizes[] = +{ + sizeof(led_names)/sizeof(char*), +}; + +/**------------------------------------------------------------------*\ + @name Steel Series APEX + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectSteelSeriesApex,DetectSteelSeriesApexM + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SteelSeriesApex::RGBController_SteelSeriesApex(SteelSeriesApexBaseController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "SteelSeries"; + type = DEVICE_TYPE_KEYBOARD; + description = "SteelSeries Apex RGB Device"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + version = controller->GetVersion(); + + proto_type = controller->proto_type; + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0x00; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_SteelSeriesApex::~RGBController_SteelSeriesApex() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + free(zones[zone_index].matrix_map->map); + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_SteelSeriesApex::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + + /*---------------------------------------------------------*\ + | The first 5 chars are the SKU which we need to determine | + | the region. | + \*---------------------------------------------------------*/ + + std::string sku = serial.substr(0, 5); + + unsigned int total_led_count = 0; + + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + + if(zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->map = (unsigned int *) malloc(matrix_mapsize*sizeof(unsigned int)); + + if((proto_type == APEX) || (proto_type == APEX_M) || (proto_type == APEX_9_TKL) || (proto_type == APEX_9_MINI)) + { + SetSkuRegion(*new_zone.matrix_map, sku); + } + } + else + { + new_zone.matrix_map = NULL; + } + + if((proto_type == APEX) || (proto_type == APEX_M) || (proto_type == APEX_9_TKL) || (proto_type == APEX_9_MINI)) + { + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + total_led_count += zone_sizes[zone_idx]; + } + zones.push_back(new_zone); + }; + + SetSkuLedNames(leds, sku, total_led_count); + SetupColors(); +} + +void RGBController_SteelSeriesApex::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SteelSeriesApex::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + controller->SetLEDsDirect(colors); +} + +void RGBController_SteelSeriesApex::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SteelSeriesApex::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SteelSeriesApex::DeviceUpdateMode() +{ + std::vector temp_colors; + controller->SetMode(modes[active_mode].value, temp_colors); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesApexController/RGBController_SteelSeriesApex.h b/Controllers/SteelSeriesController/SteelSeriesApexController/RGBController_SteelSeriesApex.h new file mode 100644 index 0000000..27227e9 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexController/RGBController_SteelSeriesApex.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesApex.h | +| | +| RGBController for SteelSeries Apex 7 | +| | +| Eric Samuelson (edbgon) 05 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "SteelSeriesApexBaseController.h" +#include "SteelSeriesGeneric.h" + +class RGBController_SteelSeriesApex : public RGBController +{ +public: + RGBController_SteelSeriesApex(SteelSeriesApexBaseController* controller_ptr); + ~RGBController_SteelSeriesApex(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SteelSeriesApexBaseController* controller; + steelseries_type proto_type; + + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesApexController/SteelSeriesApexController.cpp b/Controllers/SteelSeriesController/SteelSeriesApexController/SteelSeriesApexController.cpp new file mode 100644 index 0000000..499bc33 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexController/SteelSeriesApexController.cpp @@ -0,0 +1,318 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApexController.cpp | +| | +| Driver for SteelSeries Apex Keyboards | +| | +| New driver based on SignalRGB Plugins | +| https://gitlab.com/signalrgb/signal-plugins/ | +| | +| Eric Samuelson (edbgon) 05 Jul 2020 | +| Filipe S. (filipesn) 5 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "SteelSeriesApexController.h" +#include "LogManager.h" + +using namespace std::chrono_literals; + +#define FIRMWARE_REQ_LEN 645 + +static unsigned int keys[] = {0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, //20 + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, + 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, //40 + 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, //60 + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, + 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x64, 0xE0, //80 + 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xF0, 0x31, 0x87, + 0x88, 0x89, 0x8A, 0x8B, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, //100 + 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, + 0x63, 0xFB }; + +SteelSeriesApexController::SteelSeriesApexController(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name) : SteelSeriesApexBaseController(dev_handle, path, dev_name) +{ + proto_type = type; + kbd_quirk = APEX_GEN1; + + SendInitialization(); +} + +SteelSeriesApexController::~SteelSeriesApexController() +{ + /*-----------------------------------------------------*\ + | Gen 3 models must be explicitly cleared for on-board | + | config selection to apply without power cycling after | + | OpenRGB shuts down. | + \*-----------------------------------------------------*/ + if(kbd_quirk == APEX_GEN3) + { + unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE]; + memset(obuf, 0x00, sizeof(obuf)); + obuf[0x00] = 0; + obuf[0x01] = APEX_GEN3_PACKET_CLEAR_LIGHTING; + hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE); + } + hid_close(dev); +} + +void SteelSeriesApexController::SetMode + ( + unsigned char mode, + std::vector /*colors*/ + ) +{ + unsigned char mode_colors[9]; + + active_mode = mode; + + memset(mode_colors, 0x00, sizeof(mode_colors)); +} + +void SteelSeriesApexController::SetLEDsDirect(std::vector colors) +{ + unsigned char buf[643]; + int num_keys = 0; + + unsigned char packet_id = APEX_PACKET_ID_DIRECT; + + num_keys = sizeof(keys) / sizeof(*keys); + + if(kbd_quirk >= APEX_GEN2) + { + struct hid_device_info* info = hid_get_device_info(dev); + + /*-------------------------------------------------*\ + | Apparently Gen 3 wireless models reuse this | + | protocol, make sure to place their PID here and | + | further below when developing. | + \*-------------------------------------------------*/ + if(info && (info->product_id == 0x1630 || info->product_id == 0x1632 + || info->product_id == 0x162C || info->product_id == 0x162D + || info->product_id == 0x1644 || info->product_id == 0x1646)) + { + packet_id = APEX_2023_PACKET_ID_DIRECT_WIRELESS; + } + else + { + packet_id = APEX_2023_PACKET_ID_DIRECT; + } + } + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0; + buf[0x01] = packet_id; + buf[0x02] = kbd_quirk ? (unsigned char)colors.size() : num_keys; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for(int i = 0; i < num_keys; i++) + { + buf[(i*4)+3] = keys[i]; + buf[(i*4)+4] = RGBGetRValue(colors[i]); + buf[(i*4)+5] = RGBGetGValue(colors[i]); + buf[(i*4)+6] = RGBGetBValue(colors[i]); + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 643); +} + +/*---------------------------------------------------------*\ +| Private packet sending functions. | +\*---------------------------------------------------------*/ +void SteelSeriesApexController::SelectProfile + ( + unsigned char profile + ) +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer, set up packet and send | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + buf[0x00] = 0; + buf[0x01] = 0x89; + buf[0x02] = profile; + hid_send_feature_report(dev, buf, 65); +} + +void SteelSeriesApexController::SendInitialization() +{ + unsigned char buf[FIRMWARE_REQ_LEN]; + unsigned char read_buf[65]; + int res = 0; + char version_str[65] = "Unknown"; + + struct hid_device_info* info = hid_get_device_info(dev); + unsigned short pid = (info) ? info->product_id : 0; + + /*-----------------------------------------------------*\ + | Firmware check for TKL 2023 | + \*-----------------------------------------------------*/ + if(pid == 0x1628) + { + /*-------------------------------------------------*\ + | Zero out buffer | + \*-------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + buf[0x00] = 0x00; + buf[0x01] = 0x90; + + /*-------------------------------------------------*\ + | Send packet | + \*-------------------------------------------------*/ + hid_write(dev, buf, 65); + + /*-------------------------------------------------*\ + | Read Response | + \*-------------------------------------------------*/ + memset(read_buf, 0x00, sizeof(read_buf)); + res = hid_read_timeout(dev, read_buf, sizeof(read_buf), 200); + + /*-------------------------------------------------*\ + | Firmware Check | + \*-------------------------------------------------*/ + if(res > 2 && read_buf[0] == 0x90) + { + int major = 0, minor = 0, patch = 0; + char* fw_ptr = (char*)&read_buf[2]; + + snprintf(version_str, sizeof(version_str), "%s", fw_ptr); + + int count = sscanf(version_str, "%d.%d.%d", &major, &minor, &patch); + + if(count == 3) + { + /*-----------------------------------------*\ + | Currently set to 1.19.7 or newer. | + \*-----------------------------------------*/ + if(major > 1) + { + kbd_quirk = APEX_GEN2; + } + else if(major == 1) + { + if(minor > 19) + { + kbd_quirk = APEX_GEN2; + } + else if(minor == 19 && patch >= 7) + { + kbd_quirk = APEX_GEN2; + } + } + } + } + } + /*-----------------------------------------------------*\ + | Apparently Gen 3 models reuse this protocol, make | + | sure to place their PID here and further above for | + | wireless when developing. | + \*-----------------------------------------------------*/ + else if(pid == 0x1630 || pid == 0x1632 + || pid == 0x162C || pid == 0x162D + || pid == 0x1642 || pid == 0x1644 || pid == 0x1646) + { + kbd_quirk = APEX_GEN3; + } + + /*-----------------------------------------------------*\ + | Send Initialization packet on new protocol. | + \*-----------------------------------------------------*/ + if(kbd_quirk >= APEX_GEN2) + { + memset(buf, 0x00, sizeof(buf)); + buf[0x00] = 0x00; + buf[0x01] = APEX_2023_PACKET_ID_INIT; + hid_send_feature_report(dev, buf, APEX_2023_PACKET_LENGTH); + + LOG_DEBUG("[%s] Using Apex 2023 protocol. FW: %s", name.c_str(), version_str); + } + else + { + LOG_DEBUG("[%s] Using Apex Legacy protocol. FW: %s", name.c_str(), version_str); + } +} + +std::string SteelSeriesApexController::GetSerial() +{ + /*-------------------------------------------------*\ + | Gen 3 doesn't expose the serial number in | + | firmware. A region code is instead set by the | + | user and subsequently read back. This region code | + | is used by all 5 on-board configs. | + | For consistency with other Apex keyboards, this | + | region code in combination with the PID is mapped | + | to an approximate product number as the region | + | patch logic from that point is identical. | + | The product number used may not be an exact match | + | for the keyboard but should reflect the form | + | factor, region and RGB layout | + \*-------------------------------------------------*/ + if(kbd_quirk >= APEX_GEN2) + { + unsigned char obuf[STEELSERIES_PACKET_OUT_SIZE]; + unsigned char ibuf[STEELSERIES_PACKET_IN_SIZE]; + int result; + struct hid_device_info* info = hid_get_device_info(dev); + unsigned short pid = (info) ? info->product_id : 0; + + memset(obuf, 0x00, sizeof(obuf)); + + if(pid == 0x1642) + { + obuf[0x00] = 0; + obuf[0x01] = 0xF5; + hid_write(dev, obuf, STEELSERIES_PACKET_OUT_SIZE); + result = hid_read_timeout(dev, ibuf, STEELSERIES_PACKET_IN_SIZE, 2); + + if(result > 3 && ibuf[0] == 0xF5) + { + switch(ibuf[2]) + { + case 0x1: + return "64740"; + break; + case 0x3: + return "64741"; + break; + case 0x4: + return "64743"; + break; + case 0x6: + return "64744"; + break; + case 0xA: + return "64742"; + break; + case 0xD: + return "64745"; + break; + default: + break; + } + } + } + + return "64865"; + } + + return SteelSeriesApexBaseController::GetSerial(); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesApexController/SteelSeriesApexController.h b/Controllers/SteelSeriesController/SteelSeriesApexController/SteelSeriesApexController.h new file mode 100644 index 0000000..4cd83a8 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexController/SteelSeriesApexController.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApexController.cpp | +| | +| Driver for SteelSeries Apex Keyboards | +| | +| New driver based on SignalRGB Plugins | +| https://gitlab.com/signalrgb/signal-plugins/ | +| | +| Eric Samuelson (edbgon) 05 Jul 2020 | +| Filipe S. (filipesn) 5 Jan 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "SteelSeriesGeneric.h" +#include "SteelSeriesApexBaseController.h" + +enum +{ + APEX_PACKET_ID_DIRECT = 0x3a, /* Direct mode */ + APEX_2023_PACKET_ID_DIRECT = 0x40, /* New Wired Direct mode */ + APEX_2023_PACKET_ID_DIRECT_WIRELESS = 0x61, /* New Wireless Direct mode */ + APEX_2023_PACKET_ID_INIT = 0x4B, /* New Initialization */ + APEX_2023_PACKET_LENGTH = 643, + APEX_GEN3_PACKET_CLEAR_LIGHTING = 0x41, +}; + +class SteelSeriesApexController : public SteelSeriesApexBaseController +{ +public: + SteelSeriesApexController(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name); + ~SteelSeriesApexController(); + + void SetMode + ( + unsigned char mode, + std::vector colors + ); + + void SetLEDsDirect(std::vector colors); + + std::string GetSerial() override; + +private: + + void SelectProfile + ( + unsigned char profile + ); + + void SendInitialization(); + +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesApexMController/SteelSeriesApexMController.cpp b/Controllers/SteelSeriesController/SteelSeriesApexMController/SteelSeriesApexMController.cpp new file mode 100644 index 0000000..8ebefa9 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexMController/SteelSeriesApexMController.cpp @@ -0,0 +1,128 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApexMController.cpp | +| | +| Driver for SteelSeries Apex M750 | +| | +| Florian Heilmann (FHeilmann) 12 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SteelSeriesApexMController.h" + +#define SS_APEX_M_PACKET_SIZE 513 +#define NA 0xFF + +static unsigned int keys_m[] = +{ +/* LCTRL LWIN LALT XXX SPACE XXX XXX XXX XXX RALT RWIN FNC RCTRL XXX XXX LEFT DOWN RIGHT XXX #0 XXX #. */ + 79, 82, 81, NA, 40, NA, NA, NA, NA, 85, 86, 87, 83, NA, NA, 75, 76, 74, NA, 104, NA, 105, + +/* LSHFT Z X C V B N M , . / XXX RSHFT XXX XXX XXX UP XXX #1 #2 #3 #ENTR */ + 80, 25, 23, 2, 21, 1, 13, 12, 49, 50, 51, NA, 84, NA, NA, NA, 77, NA, 95, 96, 97, 94, + +/* CAPLK A S D F G H J K L ; ' XXX ENTER XXX XXX XXX XXX #4 #5 #6 XXX */ + 52, 0, 18, 3, 5, 6, 7, 9, 10, 11, 46, 47, NA, 36, NA, NA, NA, NA, 98, 99, 100, NA, + +/* TAB Q W E R T Y U I O P [ ] XXX \ DELTE END PGDN #7 #8 #9 #+ */ + 39, 16, 22, 4, 17, 19, 24, 20, 8, 14, 15, 43, 44, NA, 88, 71, 72, 73, 101, 102, 103, 93, + +/* ` 1 2 3 4 5 6 7 8 9 0 - = XXX BKSPC INSRT HOME PGUP NUMLK #/ #* #- */ + 48, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 41, 42, NA, 38, 68, 69, 70, 89, 90, 91, 92, + +/* ESC F1 F2 F3 F4 XXX F5 F6 F7 F8 XXX F9 F10 F11 F12 PRTSC SCRLK PAUSE XXX XXX XXX XXX */ + 37, 53, 54, 55, 56, NA, 57, 58, 59, 60, NA, 61, 62, 63, 64, 65, 66, 67, NA, NA, NA, NA +}; + +SteelSeriesApexMController::SteelSeriesApexMController(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name) : SteelSeriesApexBaseController(dev_handle, path, dev_name) +{ + proto_type = type; + EnableLEDControl(); +} + +SteelSeriesApexMController::~SteelSeriesApexMController() +{ + hid_close(dev); +} + +void SteelSeriesApexMController::EnableLEDControl() +{ + unsigned char buf[SS_APEX_M_PACKET_SIZE] = { 0x00 }; + + buf[0x00] = 0x00; + buf[0x01] = 0x00; + buf[0x02] = 0x00; + buf[0x03] = 0x00; + buf[0x04] = 0x01; + buf[0x05] = 0x00; + buf[0x06] = 0x85; + hid_send_feature_report(dev, buf, SS_APEX_M_PACKET_SIZE); + + buf[0x00] = 0x00; + buf[0x01] = 0x00; + buf[0x02] = 0x00; + buf[0x03] = 0x00; + buf[0x04] = 0x03; + buf[0x05] = 0x01; + buf[0x06] = 0x00; + buf[0x07] = 0xff; + hid_send_feature_report(dev, buf, SS_APEX_M_PACKET_SIZE); + + buf[0x00] = 0x00; + buf[0x01] = 0x00; + buf[0x02] = 0x00; + buf[0x03] = 0x00; + buf[0x04] = 0x01; + buf[0x05] = 0x00; + buf[0x06] = 0x85; + hid_send_feature_report(dev, buf, SS_APEX_M_PACKET_SIZE); +} + +void SteelSeriesApexMController::SetMode(unsigned char /*mode*/, std::vector /*colors*/) +{ +} + +void SteelSeriesApexMController::SetLEDsDirect(std::vector colors) +{ + unsigned char buf[SS_APEX_M_PACKET_SIZE] = { 0x00 }; + int num_keys = sizeof(keys_m) / sizeof(*keys_m); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + buf[0x01] = 0x00; + buf[0x02] = 0x00; + buf[0x03] = 0x01; + buf[0x04] = 0x8e; + buf[0x05] = 0x01; + buf[0x06] = 0x03; + buf[0x07] = 0x06; + buf[0x08] = 0x16; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for (int i = 0; i < num_keys; i++) + { + if (keys_m[i] == NA) + { + buf[i * 3 + 9] = 0xFF; + buf[i * 3 + 10] = 0x32; + buf[i * 3 + 11] = 0x00; + } + else + { + buf[(i * 3) + 9] = RGBGetRValue(colors[keys_m[i]]); + buf[(i * 3) + 10] = RGBGetGValue(colors[keys_m[i]]); + buf[(i * 3) + 11] = RGBGetBValue(colors[keys_m[i]]); + } + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, SS_APEX_M_PACKET_SIZE); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesApexMController/SteelSeriesApexMController.h b/Controllers/SteelSeriesController/SteelSeriesApexMController/SteelSeriesApexMController.h new file mode 100644 index 0000000..3e4e28c --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexMController/SteelSeriesApexMController.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApexMController.cpp | +| | +| Driver for SteelSeries Apex M750 | +| | +| Florian Heilmann (FHeilmann) 12 Oct 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "SteelSeriesGeneric.h" +#include "SteelSeriesApexBaseController.h" + +class SteelSeriesApexMController : public SteelSeriesApexBaseController +{ +public: + SteelSeriesApexMController(hid_device* dev_handle, steelseries_type type, const char* path, std::string dev_name); + ~SteelSeriesApexMController(); + + void SetMode + ( + unsigned char mode, + std::vector colors + ); + + void SetLEDsDirect(std::vector colors); + +private: + void EnableLEDControl(); + void SelectProfile + ( + unsigned char profile + ); +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesApexRegions.h b/Controllers/SteelSeriesController/SteelSeriesApexRegions.h new file mode 100644 index 0000000..7e0f733 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexRegions.h @@ -0,0 +1,650 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApexRegions.h | +| | +| Region settings for SteelSeries Apex 5/7/Pro and TKL | +| | +| Joseph East (dripsnek) 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController_SteelSeriesApex.h" +#include "RGBControllerKeyNames.h" + +#define NA 0xFFFFFFFF + +/*----------------------------------------------------------------------*\ +| Steelseries keyboards share a library of 111 standard keys plus extra | +| ambients across all physical form factors. No keyboard has every key | +| fitted but the same format packet can be sent regardless of model and | +| the firmware will pick the keys applicable to it, ignoring the rest. | +| The complication comes in commuicating this to the OpenRGB GUI as | +| different key layouts change the LED positions, additionally some | +| labels / scancodes are overloaded based on the language which clashes | +| with the OpenRGB defaults. In order to account for this a base SKU | +| (ANSI) is assumed which is transformed into a regional SKU when device | +| detection returns a known product number acquired from the keyboard | +\*----------------------------------------------------------------------*/ + +#define MATRIX_HEIGHT 6 +#define MATRIX_WIDTH 22 + +/*-------------------------------------------------------*\ +| Default keymap where values are indicies into led_names | +| or NA for no key. | +\*-------------------------------------------------------*/ +#define MATRIX_MAP_ANSI\ + { { 37, NA, 53, 54, 55, 56, NA, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, NA, NA, NA, NA },\ + { 48, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 41, 42, 38, NA, 68, 69, 70, 94, 95, 96, 97 },\ + { 39, NA, 16, 22, 4 , 17, 19, 24, 20, 8 , 14, 15, 43, 44, 88, 71, 72, 73, 106, 107, 108, 98 },\ + { 52, NA, 0 , 18, 3 , 5 , 6 , 7 , 9 , 10, 11, 46, 47, 36, NA, NA, NA, NA, 103, 104, 105, NA },\ + { 80, NA, 25, 23, 2 , 21, 1 , 13, 12, 49, 50, 51, 84, NA, NA, NA, 77, NA, 100, 101, 102, 99 },\ + { 79, 82, 81, NA, NA, NA, NA, 40, NA, NA, NA, 85, 86, 87, 83, 75, 76, 74, 109, NA, 110, NA } }; + +static const int matrix_mapsize = MATRIX_HEIGHT * MATRIX_WIDTH; + +/*-------------------------------------------------------*\ +| These map to the values defined in the keys array at | +| SteelSeriesApexController | +\*-------------------------------------------------------*/ +static const char* led_names[] = +{ + KEY_EN_A, + KEY_EN_B, + KEY_EN_C, + KEY_EN_D, + KEY_EN_E, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_I, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_M, + KEY_EN_N, + KEY_EN_O, + KEY_EN_P, + KEY_EN_Q, + KEY_EN_R, + KEY_EN_S, + KEY_EN_T, + KEY_EN_U, + KEY_EN_V, + KEY_EN_W, + KEY_EN_X, + KEY_EN_Y, + KEY_EN_Z, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_ANSI_ENTER, + KEY_EN_ESCAPE, + KEY_EN_BACKSPACE, + KEY_EN_TAB, + KEY_EN_SPACE, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_POUND, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_BACK_TICK, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_CAPS_LOCK, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAUSE_BREAK, + KEY_EN_INSERT, + KEY_EN_HOME, + KEY_EN_PAGE_UP, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + KEY_EN_RIGHT_ARROW, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_UP_ARROW, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_SHIFT, + KEY_EN_LEFT_ALT, + KEY_EN_LEFT_WINDOWS, + KEY_EN_RIGHT_CONTROL, + KEY_EN_RIGHT_SHIFT, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_ANSI_BACK_SLASH, + KEY_JP_RO, + KEY_JP_KANA, + KEY_JP_YEN, + KEY_JP_HENKAN, + KEY_JP_MUHENKAN, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, + KEY_EN_NUMPAD_PLUS, + KEY_EN_NUMPAD_ENTER, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD, + KEY_EN_MEDIA_PLAY_PAUSE +}; + +struct matrix_region_patch +{ + int row; + int column; + unsigned int value; +}; + +struct sku_patch +{ + std::vector base_patch; + std::vector region_patch; + std::map key_patch; +}; + +/*-----------------------------------------------*\ +| There are two types of patches, a SKU may | +| use one, both or none. The first type are | +| region patches which modify the location of | +| a logical key (in terms of RGB) based on the | +| scancode emitted by the physical key on the | +| keyboard. This is the most important as it | +| impacts what keys are lit in terms of RGB. | +| | +| The second type are keyname lookups | +| where the face of the key may be different | +| between SKUs but the scancode is the same, this | +| is for convenience of the LED view in the GUI. | +| | +| Each SKU has up to 3 operations performed on | +| it. A generic TKL region patch (where req'd) | +| the SKU region patch then a keyname lookup in | +| that order. | +\*-----------------------------------------------*/ + +/*-----------------------------------------------*\ +| Region patches are structs consisting of | +| {row, column, value} where row and column are | +| 0 indexed into the logical key layout defined | +| by MATRIX_MAP_ANSI. This array closely matches | +| the physical layout of the keyboard. Value is | +| an index into the led_names array and refers | +| to the replacement character which should exist | +| at that location, or NA for no character. When | +| the SetSkuRegion function is called, the | +| contents of MATRIX_MAP_ANSI populate a local | +| array, then based on the SKU argument the | +| specific patches are applied to the local copy | +| before being passed to the controller. | +\*-----------------------------------------------*/ + +static const std::vector apex_jp_region_patch = +{ + {1, 13, 91}, + {1, 14, 38}, + {2, 14, 36}, + {3, 13, 45}, + {4, 12, 89}, + {4, 13, 84}, + {5, 3, 93}, + {5, 10, 92}, + {5, 11, 90}, + {5, 12, 85}, +}; + +static const std::vector apex_iso_region_patch = +{ + {2, 14, 36}, + {3, 13, 45}, + {4, 1, 78}, +}; + +/*-----------------------------------------------*\ +| The TKL region patch is common for all TKL SKUs | +\*-----------------------------------------------*/ + +static const std::vector apex_tkl_us_region_patch = +{ + {0, 15, NA}, + {0, 16, NA}, + {0, 17, 111}, + {1, 18, NA}, + {1, 19, NA}, + {1, 20, NA}, + {1, 21, NA}, + {2, 18, NA}, + {2, 19, NA}, + {2, 20, NA}, + {2, 21, NA}, + {3, 18, NA}, + {3, 19, NA}, + {3, 20, NA}, + {4, 18, NA}, + {4, 19, NA}, + {4, 20, NA}, + {4, 21, NA}, + {5, 18, NA}, + {5, 20, NA}, +}; + +/*-------------------------------------------------*\ +| The Mini region patch is common for all Mini SKUs | +\*-------------------------------------------------*/ + +static const std::vector apex_mini_us_region_patch = +{ + {0, 0, NA}, + {0, 2, NA}, + {0, 3, NA}, + {0, 4, NA}, + {0, 5, NA}, + {0, 7, NA}, + {0, 8, NA}, + {0, 9, NA}, + {0, 10, NA}, + {0, 11, NA}, + {0, 12, NA}, + {0, 13, NA}, + {0, 14, NA}, + {0, 15, NA}, + {0, 16, NA}, + {0, 17, NA}, + {1, 0, 37}, + {1, 15, NA}, + {1, 16, NA}, + {1, 17, NA}, + {1, 18, NA}, + {1, 19, NA}, + {1, 20, NA}, + {1, 21, NA}, + {2, 15, NA}, + {2, 16, NA}, + {2, 17, NA}, + {2, 18, NA}, + {2, 19, NA}, + {2, 20, NA}, + {2, 21, NA}, + {3, 18, NA}, + {3, 19, NA}, + {3, 20, NA}, + {4, 16, NA}, + {4, 18, NA}, + {4, 19, NA}, + {4, 20, NA}, + {4, 21, NA}, + {5, 15, NA}, + {5, 16, NA}, + {5, 17, NA}, + {5, 18, NA}, + {5, 20, NA}, +}; + +/*-----------------------------------------------*\ +| Keyname lookups change the character displayed | +| on the LED view GUI by overriding the value | +| associated with the index in led_names. | +\*-----------------------------------------------*/ + +static const std::map apex_jp_keyname_lookup = +{ + {42, KEY_JP_CHEVRON}, + {43, KEY_JP_AT}, + {44, KEY_EN_LEFT_BRACKET}, + {45, KEY_EN_RIGHT_BRACKET}, + {47, KEY_JP_COLON}, + {48, KEY_JP_EJ}, + {84, KEY_EN_RIGHT_SHIFT}, + {36, KEY_EN_ISO_ENTER}, +}; + +static const std::map apex_uk_keyname_lookup = +{ + {36, KEY_EN_ISO_ENTER}, +}; + +static const std::map apex_nor_keyname_lookup = +{ + {36, KEY_EN_ISO_ENTER}, + {41, KEY_NORD_PLUS_QUESTION}, + {42, KEY_NORD_ACUTE_GRAVE}, + {43, KEY_NORD_AAL}, + {44, KEY_NORD_DOTS_CARET}, + {45, KEY_NORD_QUOTE}, + {46, KEY_NORD_A_OE}, + {47, KEY_NORD_O_AE}, + {48, KEY_NORD_HALF}, + {51, KEY_NORD_HYPHEN}, + {78, KEY_NORD_ANGLE_BRACKET}, +}; + +static const std::map patch_lookup = +{ + /*----------------------------------------------------------*\ + | All TKL keyboards must use apex_tkl_us_region_patch as the | + | base patch, then apply the regional patch on top (if any). | + \*----------------------------------------------------------*/ + + /*---------------------*\ + | APEX Pro Gen 3 | + \*---------------------*/ + { "64660", { {}, {}, {} }}, + + { "64740", { apex_tkl_us_region_patch, {}, {} }}, + { "64742", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_nor_keyname_lookup }}, + { "64743", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64745", { apex_tkl_us_region_patch, apex_jp_region_patch, apex_jp_keyname_lookup }}, + { "64871", { apex_tkl_us_region_patch, {}, {} }}, + + { "64913", { apex_mini_us_region_patch, {}, {} }}, + + /*---------------------*\ + | APEX Pro Gen 2 / 2023 | + \*---------------------*/ + { "64856", { apex_tkl_us_region_patch, {}, {} }}, + { "64865", { apex_tkl_us_region_patch, {}, {} }}, + + { "64820", { apex_mini_us_region_patch, {}, {} }}, + { "64842", { apex_mini_us_region_patch, {}, {} }}, + + /*--------*\ + | APEX Pro | + \*--------*/ + { "64739", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64738", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_nor_keyname_lookup }}, + { "64737", { apex_tkl_us_region_patch, apex_jp_region_patch, apex_jp_keyname_lookup }}, + { "64856", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64734", { apex_tkl_us_region_patch, {}, {} }}, + + { "64631", { {}, apex_iso_region_patch, apex_nor_keyname_lookup }}, + { "64634", { {}, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64629", { {}, apex_jp_region_patch, apex_jp_keyname_lookup }}, + + /*--------*\ + | APEX 9 | + \*--------*/ + { "64847", { apex_tkl_us_region_patch, {}, {} }}, + { "64848", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64849", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_nor_keyname_lookup }}, + // { "64850", { apex_tkl_us_region_patch, apex_iso_region_patch, {} }}, + // { "64851", { apex_tkl_us_region_patch, apex_iso_region_patch, {} }}, + + { "64837", { apex_mini_us_region_patch, {}, {} }}, + + /*--------*\ + | APEX 7 | + \*--------*/ + { "64646", { apex_tkl_us_region_patch, {}, {} }}, + { "64758", { apex_tkl_us_region_patch, {}, {} }}, + { "64747", { apex_tkl_us_region_patch, {}, {} }}, + { "64652", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64760", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64749", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64651", { apex_tkl_us_region_patch, apex_iso_region_patch, apex_nor_keyname_lookup }}, + { "64649", { apex_tkl_us_region_patch, apex_jp_region_patch, apex_jp_keyname_lookup }}, + { "64756", { apex_tkl_us_region_patch, apex_jp_region_patch, apex_jp_keyname_lookup }}, + + { "64635", { {}, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64778", { {}, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64788", { {}, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64641", { {}, apex_iso_region_patch, apex_nor_keyname_lookup }}, + { "64775", { {}, apex_iso_region_patch, apex_nor_keyname_lookup }}, + { "64787", { {}, apex_iso_region_patch, apex_nor_keyname_lookup }}, + { "64639", { {}, apex_jp_region_patch, apex_jp_keyname_lookup }}, + { "64772", { {}, apex_jp_region_patch, apex_jp_keyname_lookup }}, + + /*--------*\ + | APEX 5 | + \*--------*/ + { "64534", { {}, apex_iso_region_patch, apex_uk_keyname_lookup }}, + { "64537", { {}, apex_jp_region_patch, apex_jp_keyname_lookup }}, + { "64533", { {}, apex_iso_region_patch, apex_nor_keyname_lookup }}, +}; + +static void SetSkuRegion (matrix_map_type& input, std::string& sku) +{ + std::map::const_iterator it = patch_lookup.find(sku); + unsigned int local_matrix [MATRIX_HEIGHT][MATRIX_WIDTH] = MATRIX_MAP_ANSI; + input.height = MATRIX_HEIGHT; + input.width = MATRIX_WIDTH; + + if(it != patch_lookup.end()) + { + for(std::size_t i = 0; i < it->second.base_patch.size(); i++) + { + local_matrix[it->second.base_patch[i].row][it->second.base_patch[i].column] = it->second.base_patch[i].value; + } + for(std::size_t i = 0; i < it->second.region_patch.size(); i++) + { + local_matrix[it->second.region_patch[i].row][it->second.region_patch[i].column] = it->second.region_patch[i].value; + } + } + memcpy(input.map, (unsigned int *)local_matrix, sizeof(unsigned int)*MATRIX_HEIGHT*MATRIX_WIDTH); +} + +static void SetSkuLedNames (std::vector& input, std::string& sku, unsigned int led_count) +{ + std::map::const_iterator it = patch_lookup.find(sku); + + for(unsigned int led_idx = 0; led_idx < led_count; led_idx++) + { + led new_led; + + if(it != patch_lookup.end()) + { + std::map::const_iterator jt = it->second.key_patch.find(led_idx); + + if(jt == it->second.key_patch.end()) + { + new_led.name = led_names[led_idx]; + } + else if(jt != it->second.key_patch.end()) + { + new_led.name = jt->second; + } + } + else + { + new_led.name = led_names[led_idx]; + } + input.push_back(new_led); + } +} + +/*-----------------------------------------------------------*\ +| SKU codes for all known Apex Pro / 7 / 5 & TKL variant | +| keyboards as at Janauary 2022. Generated by cross-checking | +| store listings aginst Steelseries website. | +| Updated 2026 for Apex 9 and Pro Gen 2 & Gen 3 | +| The product Pro Mini seem to belong to Gen 2 | +| | +| -- APEX PRO Gen 3 -- | +| | +| "64660", // US | +| | +| >> APEX PRO TKL Gen 3 | +| | +| "64740", // US TKL Black | +| "64741", // UK TKL Black | +| "64742", // Nordic TKL Black | +| "64743", // German TKL Black | +| "64744", // French TKL Black | +| "64745", // Japanese TKL Black | +| | +| >> APEX PRO TKL Wireless Gen 3 | +| | +| "64871", // US TKL Black Wireless | +| "64876", // Japanese TKL Black Wireless | +| | +| >> APEX PRO Mini Gen 3 | +| | +| "64913", // US Mini | +| | +| -- APEX PRO Gen2 / 2023 -- | +| | +| >> APEX PRO TKL Gen 2 / 2023 | +| | +| "64856", // US TKL | +| "64861", // Japanese TKL | +| | +| >> APEX PRO TKL Wireless Gen 2 / 2023 | +| | +| "64865", // US TKL Wireless | +| | +| >> APEX PRO Mini Gen 2 / 2023 | +| | +| "64820", // US Mini | +| | +| >> APEX PRO Mini Wireless Gen 2 / 2023 | +| | +| "64842", // US Mini Wireless | +| | +| -- APEX PRO -- | +| | +| "64626", // US | +| "64627", // German | +| "64628", // French | +| "64629", // Japanese | +| "64630", // Korean | +| "64631", // Nordic | +| "64632", // Taiwanese | +| "64633", // Thai | +| "64634" // UK | +| | +| >> APEX PRO TKL | +| | +| "64734", // US TKL | +| "64735", // German TKL | +| "64736", // French TKL | +| "64737", // Japanese TKL | +| "64738", // Nordic TKL | +| "64739", // UK TKL | +| | +| -- APEX 9 -- | +| | +| >> APEX 9 TKL | +| | +| "64847", // US TKL | +| "64848", // UK TKL | +| "64849", // Nordic TKL | +| "64850", // German TKL | +| "64851", // French TKL | +| | +| >> APEX 9 Mini | +| | +| "64837", // US Mini | +| "64838", // UK Mini | +| "64839", // German Mini | +| "64840", // French Mini | +| "64841", // Nordic Mini | +| | +| -- APEX 7 -- | +| | +| >> RED switches | +| | +| "64635", // UK Red | +| "64636", // US Red | +| "64637", // German Red | +| "64638", // French Red | +| "64639", // Japanese Red | +| "64640", // Korean Red | +| "64641", // Nordic Red | +| "64642", // Russian Red | +| "64643", // Thai Red | +| "64644", // Turkish Red | +| "64645", // Taiwanese Red | +| | +| >> RED TKL | +| | +| "64646" // US Red TKL | +| "64647", // German Red TKL | +| "64648", // French Red TKL | +| "64649", // Japanese Red TKL | +| "64650", // Korean Red TKL | +| "64651", // Nordic Red TKL | +| "64652", // UK Red TKL | +| | +| >> BLUE switches | +| | +| "64770" // German Blue | +| "64771" // French Blue | +| "64772" // Japanese Blue | +| "64773" // Korean Blue | +| "64774" // US Blue | +| "64775" // Nordic Blue | +| "64776" // Thai Blue | +| "64777" // Taiwanese Blue | +| "64778" // UK Blue | +| | +| >> BLUE TKL | +| | +| "64756" // Japanese Blue TKL | +| "64757" // Korean Blue TKL | +| "64758" // US Blue TKL | +| "64760" // UK Blue TKL | +| | +| >> BROWN switches | +| | +| "64784" // German Brown | +| "64785" // French Brown | +| "64786" // US Brown | +| "64787" // Nordic Brown | +| "64788" // UK Brown | +| | +| >> BROWN TKL | +| | +| "64746" // French Brown TKL | +| "64747" // US Brown TKL | +| "64749" // UK Brown TKL | +| | +| -- APEX 5 -- | +| | +| "64533", // Nordic | +| "64534", // UK | +| "64535", // German | +| "64536", // French | +| "64537", // Japanese | +| "64538", // Turkish | +| "64539", // US | +| | +\*-----------------------------------------------------------*/ diff --git a/Controllers/SteelSeriesController/SteelSeriesApexTZoneController/SteelSeriesApexTZoneController.cpp b/Controllers/SteelSeriesController/SteelSeriesApexTZoneController/SteelSeriesApexTZoneController.cpp new file mode 100644 index 0000000..9564655 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexTZoneController/SteelSeriesApexTZoneController.cpp @@ -0,0 +1,80 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApexTZoneController.cpp | +| | +| Driver for SteelSeries Apex T Zone | +| | +| Edbgon 06 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SteelSeriesApexTZoneController.h" + +SteelSeriesApexTZoneController::SteelSeriesApexTZoneController(hid_device* dev_handle, const char* path, std::string dev_name) : SteelSeriesApex3Controller(dev_handle, path, dev_name) +{ + +} + +SteelSeriesApexTZoneController::~SteelSeriesApexTZoneController() +{ + +} + +uint8_t SteelSeriesApexTZoneController::GetLedCount() +{ + return STEELSERIES_TZ_LED_COUNT; +} + +uint8_t SteelSeriesApexTZoneController::GetMaxBrightness() +{ + return STEELSERIES_TZ_BRIGHTNESS_MAX; +} + +bool SteelSeriesApexTZoneController::SupportsRainbowWave() +{ + return false; +} + +bool SteelSeriesApexTZoneController::SupportsSave() +{ + return true; +} + +void SteelSeriesApexTZoneController::Save() +{ + unsigned char buf[STEELSERIES_TZ_WRITE_PACKET_SIZE] = { 0x00, 0x06, 0x00, 0x08 }; + + hid_write(dev, buf, STEELSERIES_TZ_WRITE_PACKET_SIZE); + + buf[0x01] = 0x09; + buf[0x03] = 0x00; + hid_write(dev, buf, STEELSERIES_TZ_WRITE_PACKET_SIZE); +} + +void SteelSeriesApexTZoneController::SetColor(std::vector colors, uint8_t /*mode*/, uint8_t brightness) +{ + unsigned char buf[STEELSERIES_TZ_WRITE_PACKET_SIZE] = { 0x00 }; + + /*-----------------------------------------------------*\ + | Zero out buffer, set up packet and send | + \*-----------------------------------------------------*/ + memset(buf, 0x00, STEELSERIES_TZ_WRITE_PACKET_SIZE); + + buf[0x01] = 0x0A; + buf[0x03] = brightness; + hid_write(dev, buf, STEELSERIES_TZ_WRITE_PACKET_SIZE); + + buf[0x01] = 0x0B; + for(size_t i = 0; i < colors.size(); i++) + { + uint8_t index = (uint8_t)(i * 3); + + buf[index + 3] = RGBGetRValue(colors[i]);; + buf[index + 4] = RGBGetGValue(colors[i]);; + buf[index + 5] = RGBGetBValue(colors[i]);; + } + + hid_write(dev, buf, STEELSERIES_TZ_WRITE_PACKET_SIZE); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesApexTZoneController/SteelSeriesApexTZoneController.h b/Controllers/SteelSeriesController/SteelSeriesApexTZoneController/SteelSeriesApexTZoneController.h new file mode 100644 index 0000000..431ed37 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesApexTZoneController/SteelSeriesApexTZoneController.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| SteelSeriesApexTZoneController.h | +| | +| Driver for SteelSeries Apex T Zone | +| | +| Edbgon 06 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "SteelSeriesGeneric.h" +#include "SteelSeriesApex3Controller.h" + +#define STEELSERIES_TZ_LED_COUNT 10 +#define STEELSERIES_TZ_WRITE_PACKET_SIZE 33 +#define STEELSERIES_TZ_BRIGHTNESS_MAX 0x64 + +class SteelSeriesApexTZoneController : public SteelSeriesApex3Controller +{ +public: + SteelSeriesApexTZoneController(hid_device *dev_handle, const char *path, std::string dev_name); + ~SteelSeriesApexTZoneController(); + + void SetColor(std::vector colors, uint8_t mode, uint8_t brightness); + void Save(); + uint8_t GetLedCount(); + uint8_t GetMaxBrightness(); + bool SupportsRainbowWave(); + bool SupportsSave(); + +private: + +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/RGBController_SteelSeriesArctis5.cpp b/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/RGBController_SteelSeriesArctis5.cpp new file mode 100644 index 0000000..3e6acf2 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/RGBController_SteelSeriesArctis5.cpp @@ -0,0 +1,106 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesArctis5.cpp | +| | +| RGBController for SteelSeries Arctis 5 | +| | +| Morgan Guimard 04 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SteelSeriesArctis5.h" + +/**------------------------------------------------------------------*\ + @name Steelseries Arctis 5 + @category Headset + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectSteelSeriesArctis5 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SteelSeriesArctis5::RGBController_SteelSeriesArctis5(SteelSeriesArctis5Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "SteelSeries"; + type = DEVICE_TYPE_HEADSET; + description = "SteelSeries Arctis 5 Headset Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0x00; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_SteelSeriesArctis5::~RGBController_SteelSeriesArctis5() +{ + delete controller; +} + +void RGBController_SteelSeriesArctis5::SetupZones() +{ + const std::string zone_names[2] = + { + "Left" , "Right" + }; + + for(const std::string& zone_name : zone_names) + { + zone zone; + zone.name = zone_name; + zone.type = ZONE_TYPE_SINGLE; + zone.leds_min = 1; + zone.leds_max = 1; + zone.leds_count = 1; + zone.matrix_map = NULL; + zones.push_back(zone); + + led mouse_led; + mouse_led.name = zone_name; + leds.push_back(mouse_led); + } + + SetupColors(); +} + +void RGBController_SteelSeriesArctis5::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SteelSeriesArctis5::DeviceUpdateLEDs() +{ + for(unsigned int i = 0; i < zones.size(); i++) + { + UpdateZoneLEDs(i); + } +} + +void RGBController_SteelSeriesArctis5::UpdateZoneLEDs(int zone) +{ + controller->SetColor(zone, colors[zone]); +} + +void RGBController_SteelSeriesArctis5::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_SteelSeriesArctis5::DeviceUpdateMode() +{ + +} diff --git a/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/RGBController_SteelSeriesArctis5.h b/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/RGBController_SteelSeriesArctis5.h new file mode 100644 index 0000000..2f241e8 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/RGBController_SteelSeriesArctis5.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesArctis5.h | +| | +| RGBController for SteelSeries Arctis 5 | +| | +| Morgan Guimard 04 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SteelSeriesArctis5Controller.h" + +class RGBController_SteelSeriesArctis5 : public RGBController +{ +public: + RGBController_SteelSeriesArctis5(SteelSeriesArctis5Controller* controller_ptr); + ~RGBController_SteelSeriesArctis5(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SteelSeriesArctis5Controller* controller; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/SteelSeriesArctis5Controller.cpp b/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/SteelSeriesArctis5Controller.cpp new file mode 100644 index 0000000..718098c --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/SteelSeriesArctis5Controller.cpp @@ -0,0 +1,140 @@ +/*---------------------------------------------------------*\ +| SteelSeriesArctis5Controller.cpp | +| | +| Driver for SteelSeries Arctis 5 | +| | +| Morgan Guimard 04 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SteelSeriesArctis5Controller.h" +#include "StringUtils.h" + +SteelSeriesArctis5Controller::SteelSeriesArctis5Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +SteelSeriesArctis5Controller::~SteelSeriesArctis5Controller() +{ + hid_close(dev); +} + +std::string SteelSeriesArctis5Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string SteelSeriesArctis5Controller::GetNameString() +{ + return(name); +} + +std::string SteelSeriesArctis5Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void SteelSeriesArctis5Controller::SetColor(unsigned char zone_id, RGBColor color) +{ + unsigned char usb_buf[ARCTIS_5_REPORT_SIZE]; + + /*----------------------------------------------*\ + | Two packets are sent before a color change | + \*----------------------------------------------*/ + memset(usb_buf, 0x00, ARCTIS_5_REPORT_SIZE); + + usb_buf[0x00] = ARCTIS_5_REPORT_ID; + usb_buf[0x01] = 0x81; + usb_buf[0x02] = 0x43; + usb_buf[0x03] = 0x01; + usb_buf[0x04] = 0x22; + + hid_write(dev, usb_buf, ARCTIS_5_REPORT_SIZE); + + usb_buf[0x04] = 0x23; + + hid_write(dev, usb_buf, ARCTIS_5_REPORT_SIZE); + + /*----------------------------------------------*\ + | First packet: send color | + \*----------------------------------------------*/ + memset(usb_buf, 0x00, ARCTIS_5_REPORT_SIZE); + + usb_buf[0x00] = ARCTIS_5_REPORT_ID; + usb_buf[0x01] = 0x8A; + usb_buf[0x02] = 0x42; + + usb_buf[0x04] = 0x20; + usb_buf[0x05] = 0x41; + + usb_buf[0x07] = RGBGetRValue(color); + usb_buf[0x08] = RGBGetGValue(color); + usb_buf[0x09] = RGBGetBValue(color); + + usb_buf[0x0A] = 0xFF; + usb_buf[0x0B] = 0x32; + usb_buf[0x0C] = 0xC8; + usb_buf[0x0D] = 0xC8; + + hid_write(dev, usb_buf, ARCTIS_5_REPORT_SIZE); + + /*-----------------------------------------*\ + | Second packet: apply to zone | + \*-----------------------------------------*/ + memset(usb_buf, 0x00, ARCTIS_5_REPORT_SIZE); + + usb_buf[0x00] = ARCTIS_5_REPORT_ID; + usb_buf[0x01] = 0x8A; + usb_buf[0x02] = 0x42; + + usb_buf[0x04] = 0x20; + usb_buf[0x05] = 0x41; + usb_buf[0x06] = 0x08; + usb_buf[0x07] = zone_id; + usb_buf[0x08] = 0x01; + + hid_write(dev, usb_buf, ARCTIS_5_REPORT_SIZE); + + /*-----------------------------------------*\ + | Thrid packet: apply to zone | + \*-----------------------------------------*/ + memset(usb_buf, 0x00, ARCTIS_5_REPORT_SIZE); + + usb_buf[0x00] = ARCTIS_5_REPORT_ID; + usb_buf[0x01] = 0x8A; + usb_buf[0x02] = 0x42; + + usb_buf[0x04] = 0x20; + usb_buf[0x05] = 0x60; + usb_buf[0x06] = zone_id; + + hid_write(dev, usb_buf, ARCTIS_5_REPORT_SIZE); + + /*-----------------------------------------*\ + | Last packet: apply | + \*-----------------------------------------*/ + memset(usb_buf, 0x00, ARCTIS_5_REPORT_SIZE); + + usb_buf[0x00] = ARCTIS_5_REPORT_ID; + usb_buf[0x01] = 0x8A; + usb_buf[0x02] = 0x42; + + usb_buf[0x04] = 0x20; + usb_buf[0x05] = 0x05; + + hid_write(dev, usb_buf, ARCTIS_5_REPORT_SIZE); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/SteelSeriesArctis5Controller.h b/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/SteelSeriesArctis5Controller.h new file mode 100644 index 0000000..0eb6cc0 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesArctis5Controller/SteelSeriesArctis5Controller.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| SteelSeriesArctis5Controller.h | +| | +| Driver for SteelSeries Arctis 5 | +| | +| Morgan Guimard 04 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define ARCTIS_5_REPORT_SIZE 37 +#define ARCTIS_5_REPORT_ID 0x06 + +class SteelSeriesArctis5Controller +{ +public: + SteelSeriesArctis5Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~SteelSeriesArctis5Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetColor(unsigned char zone_id, RGBColor color); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesControllerDetect.cpp b/Controllers/SteelSeriesController/SteelSeriesControllerDetect.cpp new file mode 100644 index 0000000..2a40f94 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesControllerDetect.cpp @@ -0,0 +1,525 @@ +/*---------------------------------------------------------*\ +| SteelSeriesControllerDetect.cpp | +| | +| Detector for SteelSeries devices | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "SteelSeriesGeneric.h" +#include "SteelSeriesAeroxWirelessController.h" +#include "SteelSeriesAerox5Controller.h" +#include "SteelSeriesArctis5Controller.h" +#include "SteelSeriesApex8ZoneController.h" +#include "SteelSeriesApex9Controller.h" +#include "SteelSeriesApexController.h" +#include "SteelSeriesApexMController.h" +#include "SteelSeriesApexTZoneController.h" +#include "SteelSeriesOldApexController.h" +#include "SteelSeriesQCKMatController.h" +#include "SteelSeriesRivalController.h" +#include "SteelSeriesRival3Controller.h" +#include "SteelSeriesSenseiController.h" +#include "SteelSeriesSiberiaController.h" +#include "RGBController_SteelSeriesArctis5.h" +#include "RGBController_SteelSeriesApex.h" +#include "RGBController_SteelSeriesApex3.h" +#include "RGBController_SteelSeriesOldApex.h" +#include "RGBController_SteelSeriesQCKMat.h" +#include "RGBController_SteelSeriesRival.h" +#include "RGBController_SteelSeriesRival3.h" +#include "RGBController_SteelSeriesSensei.h" +#include "RGBController_SteelSeriesSiberia.h" + +/*-----------------------------------------------------*\ +| Vendor ID | +\*-----------------------------------------------------*/ +#define STEELSERIES_VID 0x1038 + +/*-----------------------------------------------------*\ +| Mouse product IDs | +\*-----------------------------------------------------*/ +#define STEELSERIES_AEROX_3_PID 0x1836 +#define STEELSERIES_AEROX_3_WIRELESS_PID 0x1838 +#define STEELSERIES_AEROX_3_WIRELESS_WIRED_PID 0x183A +#define STEELSERIES_AEROX_3_CS2_WIRELESS_PID 0x1878 +#define STEELSERIES_AEROX_3_CS2_WIRELESS_WIRED_PID 0x187A +#define STEELSERIES_AEROX_5_WIRELESS_PID 0x1852 +#define STEELSERIES_AEROX_5_WIRELESS_WIRED_PID 0x1854 +#define STEELSERIES_AEROX_5_DESTINY_WIRELESS_PID 0x185C +#define STEELSERIES_AEROX_5_DESTINY_WIRELESS_WIRED_PID 0x185E +#define STEELSERIES_AEROX_5_DIABLO_WIRELESS_PID 0x1860 +#define STEELSERIES_AEROX_5_DIABLO_WIRELESS_WIRED_PID 0x1862 +#define STEELSERIES_AEROX_9_WIRELESS_PID 0x1858 +#define STEELSERIES_AEROX_9_WIRELESS_WIRED_PID 0x185A +#define STEELSERIES_AEROX_5_PID 0x1850 +#define STEELSERIES_AEROX_9_PID 0x185A +#define STEELSERIES_RIVAL_100_PID 0x1702 +#define STEELSERIES_RIVAL_100_DOTA_PID 0x170C +#define STEELSERIES_RIVAL_105_PID 0x1814 +#define STEELSERIES_RIVAL_106_PID 0x1816 +#define STEELSERIES_RIVAL_110_PID 0x1729 +#define STEELSERIES_RIVAL_300_PID 0x1710 +#define ACER_PREDATOR_RIVAL_300_PID 0x1714 +#define STEELSERIES_RIVAL_300_CSGO_PID 0x1394 +#define STEELSERIES_RIVAL_300_CSGO_STM32_PID 0x1716 +#define STEELSERIES_RIVAL_300_CSGO_HYPERBEAST_PID 0x171A +#define STEELSERIES_RIVAL_300_DOTA_PID 0x1392 +#define STEELSERIES_RIVAL_300_HP_PID 0x1718 +#define STEELSERIES_RIVAL_300_BLACKOPS_PID 0x1710 +#define STEELSERIES_RIVAL_310_PID 0x1720 +#define STEELSERIES_RIVAL_310_CSGO_HOWL_PID 0x171E +#define STEELSERIES_RIVAL_310_PUBG_PID 0x1736 +#define STEELSERIES_RIVAL_600_PID 0x1724 +#define STEELSERIES_RIVAL_600_DOTA_2_PID 0x172E +#define STEELSERIES_RIVAL_650_PID 0x172B +#define STEELSERIES_RIVAL_650_WIRELESS_PID 0x1726 +#define STEELSERIES_RIVAL_700_PID 0x1700 +#define STEELSERIES_RIVAL_710_PID 0x1730 +#define STEELSERIES_RIVAL_3_OLD_PID 0x1824 +#define STEELSERIES_RIVAL_3_PID 0x184C +#define STEELSERIES_SENSEI_TEN_PID 0x1832 +#define STEELSERIES_SENSEI_TEN_CSGO_NEON_RIDER_PID 0x1834 +#define STEELSERIES_SENSEI_310_PID 0x1722 + +/*-----------------------------------------------------*\ +| Headset product IDs | +\*-----------------------------------------------------*/ +#define STEELSERIES_SIBERIA_350_PID 0x1229 +#define STEELSERIES_ARCTIS_5_PID 0x1250 +#define STEELSERIES_ARCTIS_5_V2_PID 0x12AA + +/*--------------------------------------------------------------------*\ +| Mousemat product IDs | +\*--------------------------------------------------------------------*/ +#define STEELSERIES_QCK_PRISM_CLOTH_MED_PID 0x150A +#define STEELSERIES_QCK_PRISM_CLOTH_XL_PID 0x150D +#define STEELSERIES_QCK_PRISM_CLOTH_XL_DESTINY_PID 0x151E +#define STEELSERIES_QCK_PRISM_CLOTH_XL_CSGO_NEON_RIDER_PID 0x1514 +#define STEELSERIES_QCK_PRISM_CLOTH_XL_CSGO_NEO_NOIR_PID 0x151C +#define STEELSERIES_QCK_PRISM_CLOTH_3XL_PID 0x1516 +#define STEELSERIES_QCK_PRISM_CLOTH_4XL_PID 0x1518 +#define STEELSERIES_QCK_PRISM_CLOTH_5XL_PID 0x151A +#define STEELSERIES_QCK_PRISM_CLOTH_XL_DESTINY_2_LIGHTFALL_ED_PID 0x1520 + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define STEELSERIES_APEX_3_PID 0x161A +#define STEELSERIES_APEX_3_TKL_PID 0x1622 +#define STEELSERIES_APEX_5_PID 0x161C +#define STEELSERIES_APEX_7_PID 0x1612 +#define STEELSERIES_APEX_7_TKL_PID 0x1618 +#define STEELSERIES_APEX_9_TKL_PID 0x1634 +#define STEELSERIES_APEX_9_MINI_PID 0x1620 +#define STEELSERIES_APEX_PRO_PID 0x1610 +#define STEELSERIES_APEX_PRO_TKL_PID 0x1614 +#define STEELSERIES_APEX_PRO_TKL_2023_PID 0x1628 +#define STEELSERIES_APEX_PRO_TKL_2023_WL_PID_1 0x1630 +#define STEELSERIES_APEX_PRO_TKL_2023_WL_PID_2 0x1632 +#define STEELSERIES_APEX_PRO_TKL_GEN3_PID 0x1642 +#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1 0x1644 +#define STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2 0x1646 +#define STEELSERIES_APEX_M750_PID 0x0616 +#define STEELSERIES_APEX_OG_PID 0x1202 +#define STEELSERIES_APEX_350_PID 0x1206 +#define STEELSERIES_APEX_PRO3_PID 0x1640 + +void DetectSteelSeriesAerox3(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesAerox3Controller* controller = new SteelSeriesAerox3Controller(dev, AEROX_3, info->path, name); + RGBController_SteelSeriesRival3* rgb_controller = new RGBController_SteelSeriesRival3(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesAeroxWireless(hid_device_info* info, const std::string& name, steelseries_type proto_type) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesAeroxWirelessController* controller = new SteelSeriesAeroxWirelessController(dev, proto_type, info->path, name); + RGBController_SteelSeriesRival3* rgb_controller = new RGBController_SteelSeriesRival3(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesAerox3Wireless(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesAeroxWireless(info, name, AEROX_3_WIRELESS); +} + +void DetectSteelSeriesAerox3WirelessWired(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesAeroxWireless(info, name, AEROX_3_WIRELESS_WIRED); +} + +void DetectSteelSeriesAerox5(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesAerox5Controller* controller = new SteelSeriesAerox5Controller(dev, AEROX_3, info->path, name); + RGBController_SteelSeriesRival3* rgb_controller = new RGBController_SteelSeriesRival3(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesAerox5Wireless(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesAeroxWireless(info, name, AEROX_5_WIRELESS); +} + +void DetectSteelSeriesAerox5WirelessWired(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesAeroxWireless(info, name, AEROX_5_WIRELESS_WIRED); +} + +void DetectSteelSeriesAerox5DestinyWireless(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesAeroxWireless(info, name, AEROX_5_DESTINY_WIRELESS); +} + +void DetectSteelSeriesAerox5DestinyWirelessWired(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesAeroxWireless(info, name, AEROX_5_DESTINY_WIRELESS_WIRED); +} + +void DetectSteelSeriesAerox5DiabloWireless(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesAeroxWireless(info, name, AEROX_5_DIABLO_WIRELESS); +} + +void DetectSteelSeriesAerox5DiabloWirelessWired(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesAeroxWireless(info, name, AEROX_5_DIABLO_WIRELESS_WIRED); +} + +void DetectSteelSeriesAerox9Wireless(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesAeroxWireless(info, name, AEROX_9_WIRELESS); +} + +void DetectSteelSeriesAerox9WirelessWired(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesAeroxWireless(info, name, AEROX_9_WIRELESS_WIRED); +} + +void DetectSteelSeriesApex3Full(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesApexTZoneController* controller = new SteelSeriesApexTZoneController(dev, info->path, name); + RGBController_SteelSeriesApex3* rgb_controller = new RGBController_SteelSeriesApex3(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesApex3TKL(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesApex8ZoneController* controller = new SteelSeriesApex8ZoneController(dev, info->path, name); + RGBController_SteelSeriesApex3* rgb_controller = new RGBController_SteelSeriesApex3(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesApex(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesApexController* controller = new SteelSeriesApexController(dev, APEX, info->path, name); + RGBController_SteelSeriesApex* rgb_controller = new RGBController_SteelSeriesApex(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesApex9(hid_device_info* info, const std::string& name, steelseries_type proto_type) +{ + hid_device* dev = hid_open_path(info->path); + if(dev) + { + SteelSeriesApex9Controller* controller = new SteelSeriesApex9Controller(dev, proto_type, info->path, name); + RGBController_SteelSeriesApex* rgb_controller = new RGBController_SteelSeriesApex(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesApex9TKL(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesApex9(info, name, APEX_9_TKL); +} + +void DetectSteelSeriesApex9Mini(hid_device_info* info, const std::string& name) +{ + DetectSteelSeriesApex9(info, name, APEX_9_MINI); +} + +void DetectSteelSeriesApexM(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesApexMController* controller = new SteelSeriesApexMController(dev, APEX_M, info->path, name); + RGBController_SteelSeriesApex* rgb_controller = new RGBController_SteelSeriesApex(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesApexOld(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesOldApexController* controller = new SteelSeriesOldApexController(dev, APEX_OLD, info->path, name); + RGBController_SteelSeriesOldApex* rgb_controller = new RGBController_SteelSeriesOldApex(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesHeadset(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesSiberiaController* controller = new SteelSeriesSiberiaController(dev, info->path, name); + RGBController_SteelSeriesSiberia* rgb_controller = new RGBController_SteelSeriesSiberia(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesMousemat(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesQCKMatController* controller = new SteelSeriesQCKMatController(dev, info->path, name); + RGBController_SteelSeriesQCKMat* rgb_controller = new RGBController_SteelSeriesQCKMat(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesRival100(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesRivalController* controller = new SteelSeriesRivalController(dev, RIVAL_100, info->path, name); + RGBController_SteelSeriesRival* rgb_controller = new RGBController_SteelSeriesRival(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesRival300(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesRivalController* controller = new SteelSeriesRivalController(dev, RIVAL_300, info->path, name); + RGBController_SteelSeriesRival* rgb_controller = new RGBController_SteelSeriesRival(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesRival600(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesRivalController* controller = new SteelSeriesRivalController(dev, RIVAL_600, info->path, name); + RGBController_SteelSeriesRival* rgb_controller = new RGBController_SteelSeriesRival(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + + +void DetectSteelSeriesRival650(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesRivalController* controller = new SteelSeriesRivalController(dev, RIVAL_650, info->path, name); + RGBController_SteelSeriesRival* rgb_controller = new RGBController_SteelSeriesRival(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesRival700(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesRivalController* controller = new SteelSeriesRivalController(dev, RIVAL_700, info->path, name); + RGBController_SteelSeriesRival* rgb_controller = new RGBController_SteelSeriesRival(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + + +void DetectSteelSeriesRival3(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesRival3Controller* controller = new SteelSeriesRival3Controller(dev, RIVAL_3, info->path, name); + RGBController_SteelSeriesRival3* rgb_controller = new RGBController_SteelSeriesRival3(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesSensei(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesSenseiController* controller = new SteelSeriesSenseiController(dev, SENSEI, info->path, name); + RGBController_SteelSeriesSensei* rgb_controller = new RGBController_SteelSeriesSensei(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectSteelSeriesArctis5(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + SteelSeriesArctis5Controller* controller = new SteelSeriesArctis5Controller(dev, *info, name); + RGBController_SteelSeriesArctis5* rgb_controller = new RGBController_SteelSeriesArctis5(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +/*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Mice | +\*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 Wireless", DetectSteelSeriesAerox3Wireless, STEELSERIES_VID, STEELSERIES_AEROX_3_WIRELESS_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 Wireless Wired", DetectSteelSeriesAerox3WirelessWired, STEELSERIES_VID, STEELSERIES_AEROX_3_WIRELESS_WIRED_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 CS2 Dragon Lore Edition Wireless", DetectSteelSeriesAerox3Wireless, STEELSERIES_VID, STEELSERIES_AEROX_3_CS2_WIRELESS_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 CS2 Dragon Lore Edition Wireless Wired", DetectSteelSeriesAerox3WirelessWired, STEELSERIES_VID, STEELSERIES_AEROX_3_CS2_WIRELESS_WIRED_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 3 Wired", DetectSteelSeriesAerox3, STEELSERIES_VID, STEELSERIES_AEROX_3_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 5 Wireless", DetectSteelSeriesAerox5Wireless, STEELSERIES_VID, STEELSERIES_AEROX_5_WIRELESS_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 5 Wireless Wired", DetectSteelSeriesAerox5WirelessWired, STEELSERIES_VID, STEELSERIES_AEROX_5_WIRELESS_WIRED_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 5 Destiny 2 Edition Wireless", DetectSteelSeriesAerox5DestinyWireless, STEELSERIES_VID, STEELSERIES_AEROX_5_DESTINY_WIRELESS_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 5 Destiny 2 Edition Wireless Wired", DetectSteelSeriesAerox5DestinyWirelessWired, STEELSERIES_VID, STEELSERIES_AEROX_5_DESTINY_WIRELESS_WIRED_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 5 Diablo IV Edition Wireless", DetectSteelSeriesAerox5DiabloWireless, STEELSERIES_VID, STEELSERIES_AEROX_5_DIABLO_WIRELESS_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 5 Diablo IV Edition Wireless Wired", DetectSteelSeriesAerox5DiabloWirelessWired, STEELSERIES_VID, STEELSERIES_AEROX_5_DIABLO_WIRELESS_WIRED_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 5 Wired", DetectSteelSeriesAerox5, STEELSERIES_VID, STEELSERIES_AEROX_5_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 9 Wireless", DetectSteelSeriesAerox9Wireless, STEELSERIES_VID, STEELSERIES_AEROX_9_WIRELESS_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Aerox 9 Wireless Wired", DetectSteelSeriesAerox9WirelessWired, STEELSERIES_VID, STEELSERIES_AEROX_9_WIRELESS_WIRED_PID, 3, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 100", DetectSteelSeriesRival100, STEELSERIES_VID, STEELSERIES_RIVAL_100_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 100 DotA 2 Edition", DetectSteelSeriesRival100, STEELSERIES_VID, STEELSERIES_RIVAL_100_DOTA_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 105", DetectSteelSeriesRival100, STEELSERIES_VID, STEELSERIES_RIVAL_105_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 106", DetectSteelSeriesRival100, STEELSERIES_VID, STEELSERIES_RIVAL_106_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 110", DetectSteelSeriesRival100, STEELSERIES_VID, STEELSERIES_RIVAL_110_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 300", DetectSteelSeriesRival300, STEELSERIES_VID, STEELSERIES_RIVAL_300_PID, 0); +REGISTER_HID_DETECTOR_I("Acer Predator Gaming Mouse (Rival 300)", DetectSteelSeriesRival300, STEELSERIES_VID, ACER_PREDATOR_RIVAL_300_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 300 CS:GO Fade Edition", DetectSteelSeriesRival300, STEELSERIES_VID, STEELSERIES_RIVAL_300_CSGO_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 300 CS:GO Fade Edition (stm32)", DetectSteelSeriesRival300, STEELSERIES_VID, STEELSERIES_RIVAL_300_CSGO_STM32_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 300 CS:GO Hyperbeast Edition", DetectSteelSeriesRival300, STEELSERIES_VID, STEELSERIES_RIVAL_300_CSGO_HYPERBEAST_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 300 Dota 2 Edition", DetectSteelSeriesRival300, STEELSERIES_VID, STEELSERIES_RIVAL_300_DOTA_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 300 HP Omen Edition", DetectSteelSeriesRival300, STEELSERIES_VID, STEELSERIES_RIVAL_300_HP_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 300 Black Ops Edition", DetectSteelSeriesRival300, STEELSERIES_VID, STEELSERIES_RIVAL_300_BLACKOPS_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 310", DetectSteelSeriesSensei, STEELSERIES_VID, STEELSERIES_RIVAL_310_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 310 CS:GO Howl Edition", DetectSteelSeriesSensei, STEELSERIES_VID, STEELSERIES_RIVAL_310_CSGO_HOWL_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 310 PUBG Edition", DetectSteelSeriesSensei, STEELSERIES_VID, STEELSERIES_RIVAL_310_PUBG_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 600", DetectSteelSeriesRival600, STEELSERIES_VID, STEELSERIES_RIVAL_600_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 600 Dota 2 Edition", DetectSteelSeriesRival600, STEELSERIES_VID, STEELSERIES_RIVAL_600_DOTA_2_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 650", DetectSteelSeriesRival650, STEELSERIES_VID, STEELSERIES_RIVAL_650_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 650 Wireless", DetectSteelSeriesRival650, STEELSERIES_VID, STEELSERIES_RIVAL_650_WIRELESS_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 700", DetectSteelSeriesRival700, STEELSERIES_VID, STEELSERIES_RIVAL_700_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 710", DetectSteelSeriesRival700, STEELSERIES_VID, STEELSERIES_RIVAL_710_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 3 (Old Firmware)", DetectSteelSeriesRival3, STEELSERIES_VID, STEELSERIES_RIVAL_3_OLD_PID, 3); +REGISTER_HID_DETECTOR_I("SteelSeries Rival 3", DetectSteelSeriesRival3, STEELSERIES_VID, STEELSERIES_RIVAL_3_PID, 3); +REGISTER_HID_DETECTOR_I("SteelSeries Sensei TEN", DetectSteelSeriesSensei, STEELSERIES_VID, STEELSERIES_SENSEI_TEN_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Sensei TEN CS:GO Neon Rider Edition", DetectSteelSeriesSensei, STEELSERIES_VID, STEELSERIES_SENSEI_TEN_CSGO_NEON_RIDER_PID, 0); +REGISTER_HID_DETECTOR_I("SteelSeries Sensei 310", DetectSteelSeriesSensei, STEELSERIES_VID, STEELSERIES_SENSEI_310_PID, 0); + +/*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Headsets | +\*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_I("SteelSeries Siberia 350", DetectSteelSeriesHeadset, STEELSERIES_VID, STEELSERIES_SIBERIA_350_PID, 3 ); +REGISTER_HID_DETECTOR_I("SteelSeries Arctis 5", DetectSteelSeriesArctis5, STEELSERIES_VID, STEELSERIES_ARCTIS_5_PID, 5 ); +REGISTER_HID_DETECTOR_I("SteelSeries Arctis 5", DetectSteelSeriesArctis5, STEELSERIES_VID, STEELSERIES_ARCTIS_5_V2_PID, 5 ); + +/*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Mousemats | +\*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_I("SteelSeries QCK Prism Cloth Medium", DetectSteelSeriesMousemat, STEELSERIES_VID, STEELSERIES_QCK_PRISM_CLOTH_MED_PID, 0 ); +REGISTER_HID_DETECTOR_I("SteelSeries QCK Prism Cloth XL", DetectSteelSeriesMousemat, STEELSERIES_VID, STEELSERIES_QCK_PRISM_CLOTH_XL_PID, 0 ); +REGISTER_HID_DETECTOR_I("SteelSeries QCK Prism Cloth XL Destiny Ed.", DetectSteelSeriesMousemat, STEELSERIES_VID, STEELSERIES_QCK_PRISM_CLOTH_XL_DESTINY_PID, 0 ); +REGISTER_HID_DETECTOR_I("SteelSeries QCK Prism Cloth XL Destiny 2 Lightfall Ed.", DetectSteelSeriesMousemat, STEELSERIES_VID, STEELSERIES_QCK_PRISM_CLOTH_XL_DESTINY_2_LIGHTFALL_ED_PID, 0 ); +REGISTER_HID_DETECTOR_I("SteelSeries QCK Prism Cloth XL CS:GO Neon Rider Ed.", DetectSteelSeriesMousemat, STEELSERIES_VID, STEELSERIES_QCK_PRISM_CLOTH_XL_CSGO_NEON_RIDER_PID, 0 ); +REGISTER_HID_DETECTOR_I("SteelSeries QCK Prism Cloth XL CS:GO Neo Noir Ed.", DetectSteelSeriesMousemat, STEELSERIES_VID, STEELSERIES_QCK_PRISM_CLOTH_XL_CSGO_NEO_NOIR_PID, 0 ); +REGISTER_HID_DETECTOR_I("SteelSeries QCK Prism Cloth 3XL", DetectSteelSeriesMousemat, STEELSERIES_VID, STEELSERIES_QCK_PRISM_CLOTH_3XL_PID, 0 ); +REGISTER_HID_DETECTOR_I("SteelSeries QCK Prism Cloth 4XL", DetectSteelSeriesMousemat, STEELSERIES_VID, STEELSERIES_QCK_PRISM_CLOTH_4XL_PID, 0 ); +REGISTER_HID_DETECTOR_I("SteelSeries QCK Prism Cloth 5XL", DetectSteelSeriesMousemat, STEELSERIES_VID, STEELSERIES_QCK_PRISM_CLOTH_5XL_PID, 0 ); + +/*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Keyboards | +\*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_I ("SteelSeries Apex 3", DetectSteelSeriesApex3Full, STEELSERIES_VID, STEELSERIES_APEX_3_PID, 3 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Apex 3 TKL", DetectSteelSeriesApex3TKL, STEELSERIES_VID, STEELSERIES_APEX_3_TKL_PID, 1, 0xFFC0, 1 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex 5", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_5_PID, 1 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex 7", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_7_PID, 1 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex 7 TKL", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_7_TKL_PID, 1 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex 9 TKL", DetectSteelSeriesApex9TKL, STEELSERIES_VID, STEELSERIES_APEX_9_TKL_PID, 1 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex 9 Mini", DetectSteelSeriesApex9Mini, STEELSERIES_VID, STEELSERIES_APEX_9_MINI_PID, 1 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_PID, 1 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_PID, 1 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL 2023 Wired", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_PID, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL 2023 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_WL_PID_1, 3, 0xFFC0, 1); +REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL 2023 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_2023_WL_PID_2, 3, 0xFFC0, 1); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro TKL Gen 3 Wired", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_PID, 1 ); +REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_1, 3, 0xFFC0, 1); +REGISTER_HID_DETECTOR_IPU("SteelSeries Apex Pro TKL Gen 3 Wireless", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO_TKL_GEN3_WL_PID_2, 3, 0xFFC0, 1); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex M750", DetectSteelSeriesApexM, STEELSERIES_VID, STEELSERIES_APEX_M750_PID, 2 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex (OG)/Apex Fnatic", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_OG_PID, 0 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex 350", DetectSteelSeriesApexOld, STEELSERIES_VID, STEELSERIES_APEX_350_PID, 0 ); +REGISTER_HID_DETECTOR_I ("SteelSeries Apex Pro 3", DetectSteelSeriesApex, STEELSERIES_VID, STEELSERIES_APEX_PRO3_PID, 1 ); diff --git a/Controllers/SteelSeriesController/SteelSeriesGeneric.h b/Controllers/SteelSeriesController/SteelSeriesGeneric.h new file mode 100644 index 0000000..374c037 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesGeneric.h @@ -0,0 +1,48 @@ +/*---------------------------------------------------------*\ +| SteelSeriesGeneric.h | +| | +| Generic file for SteelSeries devices | +| | +| B Horn (bahorn) 17 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +/* Allows us to handle variation in the protocol. + * Defined in a single enum so we can keep the device_list struct the same + * for every possible device. */ +typedef enum +{ + RIVAL_100 = 0x00, + RIVAL_300 = 0x01, + RIVAL_650 = 0x02, + SIBERIA_350 = 0x03, + APEX = 0x04, + APEX_M = 0x05, + APEX_OLD = 0x06, + SENSEI = 0x07, + RIVAL_600 = 0x08, + RIVAL_3 = 0x09, + APEX_TZONE = 0x0A, + RIVAL_700 = 0x0B, + AEROX_3 = 0x0C, + APEX_8ZONE = 0x0D, + AEROX_3_WIRELESS = 0x0E, + AEROX_3_WIRELESS_WIRED = 0x0F, + AEROX_5_WIRELESS = 0x10, + AEROX_5_WIRELESS_WIRED = 0x11, + AEROX_5_DESTINY_WIRELESS = 0x12, + AEROX_5_DESTINY_WIRELESS_WIRED = 0x13, + AEROX_5_DIABLO_WIRELESS = 0x14, + AEROX_5_DIABLO_WIRELESS_WIRED = 0x15, + AEROX_9_WIRELESS = 0x16, + AEROX_9_WIRELESS_WIRED = 0x17, + APEX_9_TKL = 0x18, + APEX_9_MINI = 0x19, +} steelseries_type; diff --git a/Controllers/SteelSeriesController/SteelSeriesMouseController/SteelSeriesMouseController.cpp b/Controllers/SteelSeriesController/SteelSeriesMouseController/SteelSeriesMouseController.cpp new file mode 100644 index 0000000..1042210 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesMouseController/SteelSeriesMouseController.cpp @@ -0,0 +1,63 @@ +/*---------------------------------------------------------*\ +| SteelSeriesMouseController.cpp | +| | +| Driver for SteelSeries Mouse | +| | +| Chris M (Dr_No) 09 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SteelSeriesMouseController.h" +#include "StringUtils.h" + +SteelSeriesMouseController::SteelSeriesMouseController(hid_device* dev_handle, steelseries_type proto_type, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + proto = proto_type; +} + +SteelSeriesMouseController::~SteelSeriesMouseController() +{ + +} + +std::string SteelSeriesMouseController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string SteelSeriesMouseController::GetNameString() +{ + return(name); +} + +std::string SteelSeriesMouseController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +steelseries_type SteelSeriesMouseController::GetMouseType() +{ + return proto; +} + +void SteelSeriesMouseController::Save() +{ + const uint8_t SAVE_BUFFER_SIZE = 10; + uint8_t usb_buf[SAVE_BUFFER_SIZE] = { 0x00, 0x09 }; + + hid_write(dev, usb_buf, SAVE_BUFFER_SIZE); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesMouseController/SteelSeriesMouseController.h b/Controllers/SteelSeriesController/SteelSeriesMouseController/SteelSeriesMouseController.h new file mode 100644 index 0000000..4224f32 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesMouseController/SteelSeriesMouseController.h @@ -0,0 +1,84 @@ +/*---------------------------------------------------------*\ +| SteelSeriesMouseController.h | +| | +| Driver for SteelSeries Mouse | +| | +| Chris M (Dr_No) 09 Jun 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "SteelSeriesGeneric.h" + +#define STEELSERIES_MOUSE_BRIGHTNESS_MAX 0x64 + +/*-----------------------------------------------------------*\ +| Theses are the specific values that get sent to set a mode | +\*-----------------------------------------------------------*/ +enum +{ + STEELSERIES_MOUSE_EFFECT_SPECTRUM_CYCLE = 0x00, + STEELSERIES_MOUSE_EFFECT_BREATHING_MAX = 0x01, + STEELSERIES_MOUSE_EFFECT_BREATHING_MID = 0x02, + STEELSERIES_MOUSE_EFFECT_BREATHING_MIN = 0x03, + STEELSERIES_MOUSE_EFFECT_DIRECT = 0x04, + STEELSERIES_MOUSE_EFFECT_RAINBOW_BREATHING = 0x05, + STEELSERIES_MOUSE_EFFECT_DISCO = 0x06 +}; + +typedef struct +{ + const char* name; + const int value; +} led_info; + +typedef struct +{ + std::vector modes; + std::vector leds; +} steelseries_mouse; + +class SteelSeriesMouseController +{ +public: + SteelSeriesMouseController(hid_device* dev_handle, steelseries_type proto_type, const char* path, std::string dev_name); + virtual ~SteelSeriesMouseController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + steelseries_type GetMouseType(); + + /*-----------------------------------------------------------------*\ + | Save has a common function but can be overridden | + \*-----------------------------------------------------------------*/ + virtual void Save(); + + virtual steelseries_mouse GetMouse() = 0; + virtual std::string GetFirmwareVersion() = 0; + virtual void SetLightEffectAll(uint8_t effect) = 0; + virtual void SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness + ) = 0; + +protected: + hid_device* dev; + std::string location; + std::string name; + steelseries_type proto; + +private: + +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesOldApexController/RGBController_SteelSeriesOldApex.cpp b/Controllers/SteelSeriesController/SteelSeriesOldApexController/RGBController_SteelSeriesOldApex.cpp new file mode 100644 index 0000000..4d82c08 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesOldApexController/RGBController_SteelSeriesOldApex.cpp @@ -0,0 +1,200 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesOldApex.cpp | +| | +| RGBController for older SteelSeries Apex keyboards | +| (Apex/Apex Fnatic/Apex 350) | +| | +| Based on findings in ApexCtl by Audrius/tuxmark5, et. | +| al, https://github.com/tuxmark5/ApexCtl | +| | +| David Lee (RAMChYLD) 15 Nov 2020 | +| Based on work by B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SteelSeriesOldApex.h" + +/**------------------------------------------------------------------*\ + @name Steel Series Apex (Old) + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectSteelSeriesApexOld + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SteelSeriesOldApex::RGBController_SteelSeriesOldApex(SteelSeriesOldApexController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "SteelSeries"; + type = DEVICE_TYPE_KEYBOARD; + description = "SteelSeries Old Apex Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode direct; + direct.name = "Direct"; + direct.value = STEELSERIES_OLDAPEX_DIRECT; + direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(direct); + + SetupZones(); +} + +RGBController_SteelSeriesOldApex::~RGBController_SteelSeriesOldApex() +{ + delete controller; +} + +void RGBController_SteelSeriesOldApex::SetupZones() +{ + /* We have 5 zones to work with. Here goes... */ + zone qwerty_zone; + qwerty_zone.name = "QWERTY"; + qwerty_zone.type = ZONE_TYPE_LINEAR; + qwerty_zone.leds_min = 1; + qwerty_zone.leds_max = 1; + qwerty_zone.leds_count = 1; + qwerty_zone.matrix_map = NULL; + zones.push_back(qwerty_zone); + + led qwerty_led; + qwerty_led.name = "QWERTY"; + leds.push_back(qwerty_led); + + zone tenkey_zone; + tenkey_zone.name = "TenKey"; + tenkey_zone.type = ZONE_TYPE_LINEAR; + tenkey_zone.leds_min = 1; + tenkey_zone.leds_max = 1; + tenkey_zone.leds_count = 1; + tenkey_zone.matrix_map = NULL; + zones.push_back(tenkey_zone); + + led tenkey_led; + tenkey_led.name = "TenKey"; + leds.push_back(tenkey_led); + + zone function_zone; + function_zone.name = "FunctionKeys"; + function_zone.type = ZONE_TYPE_LINEAR; + function_zone.leds_min = 1; + function_zone.leds_max = 1; + function_zone.leds_count = 1; + function_zone.matrix_map = NULL; + zones.push_back(function_zone); + + led function_led; + function_led.name = "FunctionKeys"; + leds.push_back(function_led); + + zone mx_zone; + mx_zone.name = "MXKeys"; + mx_zone.type = ZONE_TYPE_LINEAR; + mx_zone.leds_min = 1; + mx_zone.leds_max = 1; + mx_zone.leds_count = 1; + mx_zone.matrix_map = NULL; + zones.push_back(mx_zone); + + led mx_led; + mx_led.name = "MXKeys"; + leds.push_back(mx_led); + + zone logo_zone; + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_LINEAR; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + led logo_led; + logo_led.name = "Logo"; + leds.push_back(logo_led); + + SetupColors(); +} + +void RGBController_SteelSeriesOldApex::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SteelSeriesOldApex::DeviceUpdateLEDs() +{ + // Due to the inefficient packet design of the OG Apex + // All colors must be blasted with each update + color32 qwerty; + qwerty.red = RGBGetRValue(colors[0]); + qwerty.green = RGBGetGValue(colors[0]); + qwerty.blue = RGBGetBValue(colors[0]); + qwerty.alpha = modes[active_mode].value; + + color32 tenkey; + tenkey.red = RGBGetRValue(colors[1]); + tenkey.green = RGBGetGValue(colors[1]); + tenkey.blue = RGBGetBValue(colors[1]); + tenkey.alpha = modes[active_mode].value; + + color32 functionkey; + functionkey.red = RGBGetRValue(colors[2]); + functionkey.green = RGBGetGValue(colors[2]); + functionkey.blue = RGBGetBValue(colors[2]); + functionkey.alpha = modes[active_mode].value; + + color32 mxkey; + mxkey.red = RGBGetRValue(colors[3]); + mxkey.green = RGBGetGValue(colors[3]); + mxkey.blue = RGBGetBValue(colors[3]); + mxkey.alpha = modes[active_mode].value; + + color32 logo; + logo.red = RGBGetRValue(colors[4]); + logo.green = RGBGetGValue(colors[4]); + logo.blue = RGBGetBValue(colors[4]); + logo.alpha = modes[active_mode].value; + + controller->SetColorDetailed(qwerty, tenkey, functionkey, mxkey, logo); +} + +void RGBController_SteelSeriesOldApex::UpdateZoneLEDs(int /*zone*/) +{ + // updating for one zone is pointless, + // all zones have to be blasted anyway + // so just do a full update + DeviceUpdateLEDs(); +} + + +void RGBController_SteelSeriesOldApex::UpdateSingleLED(int /*led*/) +{ + // Each zone is one LED, however + // updating for one zone is pointless, + // all zones have to be blasted anyway + // so just do a full update + DeviceUpdateLEDs(); +} + +void RGBController_SteelSeriesOldApex::DeviceUpdateMode() +{ + // We are using SetLightingEffect to control the brightness of + // LEDs. Per-zone brightness is actually possible but we are not + // doing that for now. Brightness affects whole keyboard. + + // Because at the moment all this code does is change brightness, + // We just let the new value set in and do a device LED update + // and blast the brightness value along with the RGB values + + DeviceUpdateLEDs(); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesOldApexController/RGBController_SteelSeriesOldApex.h b/Controllers/SteelSeriesController/SteelSeriesOldApexController/RGBController_SteelSeriesOldApex.h new file mode 100644 index 0000000..bd845f0 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesOldApexController/RGBController_SteelSeriesOldApex.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesOldApex.h | +| | +| RGBController for older SteelSeries Apex keyboards | +| (Apex/Apex Fnatic/Apex 350) | +| | +| Based on findings in ApexCtl by Audrius/tuxmark5, et. | +| al, https://github.com/tuxmark5/ApexCtl | +| | +| David Lee (RAMChYLD) 15 Nov 2020 | +| Based on work by B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SteelSeriesOldApexController.h" +#include "color32.h" + +class RGBController_SteelSeriesOldApex : public RGBController +{ +public: + RGBController_SteelSeriesOldApex(SteelSeriesOldApexController* controller_ptr); + ~RGBController_SteelSeriesOldApex(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SteelSeriesOldApexController* controller; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesOldApexController/SteelSeriesOldApexController.cpp b/Controllers/SteelSeriesController/SteelSeriesOldApexController/SteelSeriesOldApexController.cpp new file mode 100644 index 0000000..521e136 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesOldApexController/SteelSeriesOldApexController.cpp @@ -0,0 +1,124 @@ +/*---------------------------------------------------------*\ +| SteelSeriesOldApexController.cpp | +| | +| Driver for older SteelSeries Apex keyboards | +| (Apex/Apex Fnatic/Apex 350) | +| | +| Based on findings in ApexCtl by Audrius/tuxmark5, et. | +| al, https://github.com/tuxmark5/ApexCtl | +| | +| David Lee (RAMChYLD) 15 Nov 2020 | +| Based on work by B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "SteelSeriesOldApexController.h" +#include "StringUtils.h" + +static void send_usb_msg(hid_device* dev, char * data_pkt, unsigned int size) +{ + char* usb_pkt = new char[size + 1]; + + usb_pkt[0] = 0x00; + for(unsigned int i = 1; i < size + 1; i++) + { + usb_pkt[i] = data_pkt[i-1]; + } + + hid_write(dev, (unsigned char *)usb_pkt, size + 1); + + delete[] usb_pkt; +} + +SteelSeriesOldApexController::SteelSeriesOldApexController + ( + hid_device* dev_handle, + steelseries_type proto_type, + const char* path, + std::string dev_name + ) +{ + dev = dev_handle; + location = path; + name = dev_name; + proto = proto_type; +} + +SteelSeriesOldApexController::~SteelSeriesOldApexController() +{ + hid_close(dev); +} + +std::string SteelSeriesOldApexController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string SteelSeriesOldApexController::GetDeviceName() +{ + return(name); +} + +std::string SteelSeriesOldApexController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +steelseries_type SteelSeriesOldApexController::GetKeyboardType() +{ + return proto; +} + +void SteelSeriesOldApexController::SetColorDetailed(color32 qwerty, color32 tenkey, color32 functionkey, color32 mxkey, color32 logo) +{ + char usb_buf[32]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x07; + usb_buf[0x01] = 0x00; // All zones + + // QWERTY Zone + usb_buf[0x02] = qwerty.red; + usb_buf[0x03] = qwerty.green; + usb_buf[0x04] = qwerty.blue; + usb_buf[0x05] = qwerty.alpha; + + // Tenkey Zone + usb_buf[0x06] = tenkey.red; + usb_buf[0x07] = tenkey.green; + usb_buf[0x08] = tenkey.blue; + usb_buf[0x09] = tenkey.alpha; + + // FunctionKey Zone + usb_buf[0x0A] = functionkey.red; + usb_buf[0x0B] = functionkey.green; + usb_buf[0x0C] = functionkey.blue; + usb_buf[0x0D] = functionkey.alpha; + + // MXKey Zone + usb_buf[0x0E] = mxkey.red; + usb_buf[0x0F] = mxkey.green; + usb_buf[0x10] = mxkey.blue; + usb_buf[0x11] = mxkey.alpha; + + //Logo Zone + usb_buf[0x12] = logo.red; + usb_buf[0x13] = logo.green; + usb_buf[0x14] = logo.blue; + usb_buf[0x15] = logo.alpha; + + send_usb_msg(dev, usb_buf, 32); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesOldApexController/SteelSeriesOldApexController.h b/Controllers/SteelSeriesController/SteelSeriesOldApexController/SteelSeriesOldApexController.h new file mode 100644 index 0000000..68fdb55 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesOldApexController/SteelSeriesOldApexController.h @@ -0,0 +1,71 @@ +/*---------------------------------------------------------*\ +| SteelSeriesOldApexController.h | +| | +| Driver for older SteelSeries Apex keyboards | +| (Apex/Apex Fnatic/Apex 350) | +| | +| Based on findings in ApexCtl by Audrius/tuxmark5, et. | +| al, https://github.com/tuxmark5/ApexCtl | +| | +| David Lee (RAMChYLD) 15 Nov 2020 | +| Based on work by B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "color32.h" +#include "SteelSeriesGeneric.h" + +/* Mode, we then use these to set actual effect based on speed. */ +enum +{ + STEELSERIES_OLDAPEX_DIRECT = 0x08, +}; + +/* Effects */ +enum +{ + STEELSERIES_OLDAPEX_EFFECT_DIRECT = 0x08, +}; + +class SteelSeriesOldApexController +{ +public: + SteelSeriesOldApexController + ( + hid_device* dev_handle, + steelseries_type proto_type, + const char* path, + std::string dev_name + ); + + ~SteelSeriesOldApexController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + steelseries_type GetKeyboardType(); + + void SetColorDetailed + ( + color32 qwerty, + color32 tenkey, + color32 functionkey, + color32 mxkey, + color32 logo + ); + + void DoUpdateLEDs(); + +private: + hid_device* dev; + std::string location; + std::string name; + steelseries_type proto; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesQCKMatController/RGBController_SteelSeriesQCKMat.cpp b/Controllers/SteelSeriesController/SteelSeriesQCKMatController/RGBController_SteelSeriesQCKMat.cpp new file mode 100644 index 0000000..f1441a6 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesQCKMatController/RGBController_SteelSeriesQCKMat.cpp @@ -0,0 +1,106 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesQCKMat.cpp | +| | +| RGBController for SteelSeries Mouse | +| | +| Edbgon 22 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SteelSeriesQCKMat.h" + +/**------------------------------------------------------------------*\ + @name Steel Series QCK Mat + @category Mousemat + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectSteelSeriesMousemat + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SteelSeriesQCKMat::RGBController_SteelSeriesQCKMat(SteelSeriesQCKMatController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "SteelSeries"; + type = DEVICE_TYPE_MOUSEMAT; + description = "SteelSeries QCK Mat Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_SteelSeriesQCKMat::~RGBController_SteelSeriesQCKMat() +{ + delete controller; +} + +void RGBController_SteelSeriesQCKMat::SetupZones() +{ + /*---------------------------------------------------------*\ + | QCK has two zones | + \*---------------------------------------------------------*/ + zone mousemat_zone; + mousemat_zone.name = "Mousemat"; + mousemat_zone.type = ZONE_TYPE_SINGLE; + mousemat_zone.leds_min = 2; + mousemat_zone.leds_max = 2; + mousemat_zone.leds_count = 2; + mousemat_zone.matrix_map = NULL; + zones.push_back(mousemat_zone); + + led bot_led; + bot_led.name = "Mat Bottom LED"; + leds.push_back(bot_led); + + led top_led; + top_led.name = "Mat Top LED"; + leds.push_back(top_led); + + SetupColors(); +} + +void RGBController_SteelSeriesQCKMat::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SteelSeriesQCKMat::DeviceUpdateLEDs() +{ + controller->SetColors(colors); +} + +void RGBController_SteelSeriesQCKMat::UpdateZoneLEDs(int /*zone*/) +{ + /*---------------------------------------------------------*\ + | Packet expects both LEDs | + \*---------------------------------------------------------*/ + DeviceUpdateLEDs(); +} + +void RGBController_SteelSeriesQCKMat::UpdateSingleLED(int /*led*/) +{ + /*---------------------------------------------------------*\ + | Packet expects both LEDs | + \*---------------------------------------------------------*/ + DeviceUpdateLEDs(); +} + +void RGBController_SteelSeriesQCKMat::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesQCKMatController/RGBController_SteelSeriesQCKMat.h b/Controllers/SteelSeriesController/SteelSeriesQCKMatController/RGBController_SteelSeriesQCKMat.h new file mode 100644 index 0000000..8dc12f0 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesQCKMatController/RGBController_SteelSeriesQCKMat.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesQCKMat.h | +| | +| RGBController for SteelSeries Mouse | +| | +| Edbgon 22 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SteelSeriesQCKMatController.h" + +class RGBController_SteelSeriesQCKMat : public RGBController +{ +public: + RGBController_SteelSeriesQCKMat(SteelSeriesQCKMatController* controller_ptr); + ~RGBController_SteelSeriesQCKMat(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SteelSeriesQCKMatController* controller; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesQCKMatController/SteelSeriesQCKMatController.cpp b/Controllers/SteelSeriesController/SteelSeriesQCKMatController/SteelSeriesQCKMatController.cpp new file mode 100644 index 0000000..d036adb --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesQCKMatController/SteelSeriesQCKMatController.cpp @@ -0,0 +1,100 @@ +/*---------------------------------------------------------*\ +| SteelSeriesQCKControllerMat.cpp | +| | +| Driver for SteelSeries Mouse | +| | +| Edbgon 22 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SteelSeriesQCKMatController.h" +#include "StringUtils.h" + +SteelSeriesQCKMatController::SteelSeriesQCKMatController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +SteelSeriesQCKMatController::~SteelSeriesQCKMatController() +{ + hid_close(dev); +} + +std::string SteelSeriesQCKMatController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string SteelSeriesQCKMatController::GetDeviceName() +{ + return(name); +} + +std::string SteelSeriesQCKMatController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void SteelSeriesQCKMatController::SetColors(std::vector colors) +{ + unsigned char buf[525]; + unsigned char cbuf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + memset(cbuf, 0x00, sizeof(cbuf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x00; + buf[0x01] = 0x0E; + buf[0x03] = 0x02; + + buf[0x08] = 0xFF; + buf[0x09] = 0x32; + buf[0x0A] = 0xC8; + buf[0x0E] = 0x01; + + buf[0x14] = 0xFF; + buf[0x15] = 0x32; + buf[0x16] = 0xC8; + buf[0x19] = 0x01; + + buf[0x1A] = 0x01; + buf[0x1C] = 0x01; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + buf[0x05] = RGBGetRValue(colors[0]); + buf[0x06] = RGBGetGValue(colors[0]); + buf[0x07] = RGBGetBValue(colors[0]); + + buf[0x11] = RGBGetRValue(colors[1]); + buf[0x12] = RGBGetGValue(colors[1]); + buf[0x13] = RGBGetBValue(colors[1]); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 525); + + cbuf[0x01] = 0x0D; + hid_write(dev, cbuf, 65); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesQCKMatController/SteelSeriesQCKMatController.h b/Controllers/SteelSeriesController/SteelSeriesQCKMatController/SteelSeriesQCKMatController.h new file mode 100644 index 0000000..cd4a9a4 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesQCKMatController/SteelSeriesQCKMatController.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| SteelSeriesQCKControllerMat.h | +| | +| Driver for SteelSeries Mouse | +| | +| Edbgon 22 May 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +class SteelSeriesQCKMatController +{ +public: + SteelSeriesQCKMatController(hid_device* dev_handle, const char* path, std::string dev_name); + ~SteelSeriesQCKMatController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + void SetColors(std::vector colors); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesRival3Controller/RGBController_SteelSeriesRival3.cpp b/Controllers/SteelSeriesController/SteelSeriesRival3Controller/RGBController_SteelSeriesRival3.cpp new file mode 100644 index 0000000..b90a383 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesRival3Controller/RGBController_SteelSeriesRival3.cpp @@ -0,0 +1,174 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesRival3.cpp | +| | +| RGBController for SteelSeries Rival 3 | +| | +| B Horn (bahorn) 29 Aug 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SteelSeriesRival3.h" + +/**------------------------------------------------------------------*\ + @name Steel Series Rival 3 + @category Mouse + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectSteelSeriesRival3,DetectSteelSeriesAerox3,DetectSteelSeriesAerox5,DetectSteelSeriesAerox9 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SteelSeriesRival3::RGBController_SteelSeriesRival3(SteelSeriesMouseController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "SteelSeries"; + type = DEVICE_TYPE_MOUSE; + description = "SteelSeries Mouse Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = STEELSERIES_MOUSE_EFFECT_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0x00; + Direct.brightness_max = STEELSERIES_MOUSE_BRIGHTNESS_MAX; + Direct.brightness = STEELSERIES_MOUSE_BRIGHTNESS_MAX; + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = STEELSERIES_MOUSE_EFFECT_BREATHING_MIN; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = 0; + Breathing.speed_max = 2; + Breathing.speed = 1; + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = STEELSERIES_MOUSE_EFFECT_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_MANUAL_SAVE; + + mode RainbowBreathing; + RainbowBreathing.name = "Rainbow Breathing"; + RainbowBreathing.value = STEELSERIES_MOUSE_EFFECT_RAINBOW_BREATHING; + RainbowBreathing.flags = MODE_FLAG_MANUAL_SAVE; + + /*------------------------------------------------------------------------*\ + | This is a pretty cool mode where it flashes random colors. | + | | + | However, the flashes are in the frequency range where it probably needs | + | a proper warning for it to be compiled in by default. | + | | + | It is disabled in the vendor software, and is only known about as it is | + | documented in rivalcfg. | + | | + | If this does get re-enabled, worth noting it has an issue where this | + | mode is black if you come directly from one of the pulsating modes. | + \*------------------------------------------------------------------------*/ + /* + mode Disco; + Disco.name = "Disco"; + Disco.value = STEELSERIES_MOUSE_EFFECT_DISCO; + Disco.flags = MODE_FLAG_MANUAL_SAVE; + modes.push_back(Disco); + */ + + steelseries_mouse mouse = controller->GetMouse(); + + for(const uint8_t i: mouse.modes) + { + switch(i) + { + case STEELSERIES_MOUSE_EFFECT_SPECTRUM_CYCLE: + modes.push_back(SpectrumCycle); + break; + case STEELSERIES_MOUSE_EFFECT_BREATHING_MIN: + modes.push_back(Breathing); + break; + case STEELSERIES_MOUSE_EFFECT_DIRECT: + modes.push_back(Direct); + break; + case STEELSERIES_MOUSE_EFFECT_RAINBOW_BREATHING: + modes.push_back(RainbowBreathing); + break; + } + } + + SetupZones(); +} + +void RGBController_SteelSeriesRival3::DeviceSaveMode() +{ + controller->Save(); +} + +RGBController_SteelSeriesRival3::~RGBController_SteelSeriesRival3() +{ + delete controller; +} + +void RGBController_SteelSeriesRival3::SetupZones() +{ + steelseries_mouse mouse = controller->GetMouse(); + + for(const led_info info: mouse.leds) + { + zone zone; + zone.name = info.name; + zone.type = ZONE_TYPE_SINGLE; + zone.leds_min = 1; + zone.leds_max = 1; + zone.leds_count = 1; + zone.matrix_map = NULL; + zones.push_back(zone); + + led mouse_led; + mouse_led.name = info.name; + mouse_led.value = info.value; + leds.push_back(mouse_led); + } + SetupColors(); +} + +void RGBController_SteelSeriesRival3::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SteelSeriesRival3::DeviceUpdateLEDs() +{ + for(unsigned int i = 0; i < zones.size(); i++) + { + UpdateZoneLEDs(i); + } + DeviceUpdateMode(); +} + +void RGBController_SteelSeriesRival3::UpdateZoneLEDs(int zone) +{ + UpdateSingleLED(zone); +} + +void RGBController_SteelSeriesRival3::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + controller->SetColor(leds[led].value, red, grn, blu, modes[active_mode].brightness); +} + +void RGBController_SteelSeriesRival3::DeviceUpdateMode() +{ + controller->SetLightEffectAll(modes[active_mode].value - modes[active_mode].speed); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesRival3Controller/RGBController_SteelSeriesRival3.h b/Controllers/SteelSeriesController/SteelSeriesRival3Controller/RGBController_SteelSeriesRival3.h new file mode 100644 index 0000000..f7d54a6 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesRival3Controller/RGBController_SteelSeriesRival3.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesRival3.h | +| | +| RGBController for SteelSeries Rival 3 | +| | +| B Horn (bahorn) 29 Aug 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SteelSeriesAerox3Controller.h" +#include "SteelSeriesRival3Controller.h" + +class RGBController_SteelSeriesRival3 : public RGBController +{ +public: + RGBController_SteelSeriesRival3(SteelSeriesMouseController* controller_ptr); + ~RGBController_SteelSeriesRival3(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + SteelSeriesMouseController* controller; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesRival3Controller/SteelSeriesRival3Controller.cpp b/Controllers/SteelSeriesController/SteelSeriesRival3Controller/SteelSeriesRival3Controller.cpp new file mode 100644 index 0000000..898b7dd --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesRival3Controller/SteelSeriesRival3Controller.cpp @@ -0,0 +1,77 @@ +/*---------------------------------------------------------*\ +| SteelSeriesRival3Controller.cpp | +| | +| Driver for SteelSeries Rival 3 | +| | +| B Horn (bahorn) 29 Aug 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "SteelSeriesRival3Controller.h" + +SteelSeriesRival3Controller::SteelSeriesRival3Controller(hid_device* dev_handle, steelseries_type proto_type, const char* path, std::string dev_name) : SteelSeriesMouseController(dev_handle, proto_type, path, dev_name) +{ + +} + +SteelSeriesRival3Controller::~SteelSeriesRival3Controller() +{ + hid_close(dev); +} + +std::string SteelSeriesRival3Controller::GetFirmwareVersion() +{ + const uint8_t FW_BUFFER_SIZE = 3; + uint8_t usb_buf[FW_BUFFER_SIZE] = { 0x00, 0x10, 0x00 }; + uint16_t version; + std::string return_string; + + hid_write(dev, usb_buf, FW_BUFFER_SIZE); + hid_read(dev, (unsigned char *)&version, 2); + + return_string = std::to_string(version); + return return_string; +} + +steelseries_mouse SteelSeriesRival3Controller::GetMouse() +{ + return rival_3; +} + +void SteelSeriesRival3Controller::SetLightEffectAll(uint8_t effect) +{ + const uint8_t EFFECT_BUFFER_SIZE = 4; + uint8_t usb_buf[EFFECT_BUFFER_SIZE] = { 0x00, 0x06, 0x00, effect }; + + hid_write(dev, usb_buf, EFFECT_BUFFER_SIZE); +} + +void SteelSeriesRival3Controller::SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness + ) +{ + const uint8_t COLOR_BUFFER_SIZE = 8; + uint8_t usb_buf[COLOR_BUFFER_SIZE]; + + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x05; + usb_buf[0x02] = 0x00; + usb_buf[0x03] = zone_id; + + usb_buf[0x04] = red; + usb_buf[0x05] = green; + usb_buf[0x06] = blue; + usb_buf[0x07] = brightness; + + hid_write(dev, usb_buf, COLOR_BUFFER_SIZE); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesRival3Controller/SteelSeriesRival3Controller.h b/Controllers/SteelSeriesController/SteelSeriesRival3Controller/SteelSeriesRival3Controller.h new file mode 100644 index 0000000..e109b6a --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesRival3Controller/SteelSeriesRival3Controller.h @@ -0,0 +1,49 @@ +/*---------------------------------------------------------*\ +| SteelSeriesRival3Controller.h | +| | +| Driver for SteelSeries Rival 3 | +| | +| B Horn (bahorn) 29 Aug 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "SteelSeriesGeneric.h" +#include "SteelSeriesMouseController.h" + +static const steelseries_mouse rival_3 = +{ + { 0x04, 0x03, 0x00, 0x05 }, + { + {"Front", 0x01}, + {"Middle", 0x02}, + {"Rear", 0x03}, + {"Logo", 0x04} + } +}; + +class SteelSeriesRival3Controller: public SteelSeriesMouseController +{ +public: + SteelSeriesRival3Controller(hid_device* dev_handle, steelseries_type proto_type, const char* path, std::string dev_name); + ~SteelSeriesRival3Controller(); + + std::string GetFirmwareVersion(); + steelseries_mouse GetMouse(); + + void SetLightEffectAll(uint8_t effect); + + void SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue, + unsigned char brightness + ); +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesRivalController/RGBController_SteelSeriesRival.cpp b/Controllers/SteelSeriesController/SteelSeriesRivalController/RGBController_SteelSeriesRival.cpp new file mode 100644 index 0000000..fbb3ebb --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesRivalController/RGBController_SteelSeriesRival.cpp @@ -0,0 +1,259 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesRival.cpp | +| | +| RGBController for SteelSeries Rival | +| | +| B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SteelSeriesRival.h" + +typedef struct +{ + const char* name; + const int value; +} steelseries_rival_led_info; + +static const steelseries_rival_led_info rival_650_leds[]= +{ + {"Left 1", 0x12}, + {"Left 2", 0x14}, + {"Left 3", 0x16}, + {"Right 1", 0x13}, + {"Right 2", 0x15}, + {"Right 3", 0x17}, +}; + +static const steelseries_rival_led_info rival_600_leds[]= +{ + {"Left top", 0x02}, + {"Left mid", 0x04}, + {"Left bottom", 0x06}, + {"Right top", 0x03}, + {"Right mid", 0x05}, + {"Right bottom", 0x07}, +}; + +/**------------------------------------------------------------------*\ + @name Steel Series Rival + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectSteelSeriesRival100,DetectSteelSeriesRival300,DetectSteelSeriesRival600,DetectSteelSeriesRival650,DetectSteelSeriesRival700 + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SteelSeriesRival::RGBController_SteelSeriesRival(SteelSeriesRivalController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "SteelSeries"; + type = DEVICE_TYPE_MOUSE; + description = "SteelSeries Rival Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = STEELSERIES_RIVAL_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Pulsate; + Pulsate.name = "Pulsate"; + Pulsate.value = STEELSERIES_RIVAL_PULSATE; + Pulsate.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Pulsate.color_mode = MODE_COLORS_PER_LED; + Pulsate.speed_min = STEELSERIES_RIVAL_EFFECT_PULSATE_MIN; + Pulsate.speed_max = STEELSERIES_RIVAL_EFFECT_PULSATE_MAX; + Pulsate.speed = STEELSERIES_RIVAL_EFFECT_PULSATE_MID; + modes.push_back(Pulsate); + + SetupZones(); +} + +RGBController_SteelSeriesRival::~RGBController_SteelSeriesRival() +{ + delete controller; +} + +void RGBController_SteelSeriesRival::SetupZones() +{ + /* Rival 100 Series only has one Zone */ + zone logo_zone; + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + led logo_led; + logo_led.name = "Logo"; + logo_led.value = 0; + leds.push_back(logo_led); + + /* Rival 300 and 700 extend this by adding Scroll Wheel LED + Zone */ + if(controller->GetMouseType() == RIVAL_300 || + controller->GetMouseType() == RIVAL_700) + { + zone wheel_zone; + wheel_zone.name = "Scroll Wheel"; + wheel_zone.type = ZONE_TYPE_SINGLE; + wheel_zone.leds_min = 1; + wheel_zone.leds_max = 1; + wheel_zone.leds_count = 1; + wheel_zone.matrix_map = NULL; + zones.push_back(wheel_zone); + + led wheel_led; + wheel_led.name = "Scroll Wheel"; + wheel_led.value = 1; + leds.push_back(wheel_led); + } + /* Rival 650 extends this by Scroll Wheel LED + Zone and additional lights LEDs + Zone */ + else if(controller->GetMouseType() == RIVAL_650) + { + leds[0].value = 0x11; + + zone wheel_zone; + wheel_zone.name = "Scroll Wheel"; + wheel_zone.type = ZONE_TYPE_SINGLE; + wheel_zone.leds_min = 1; + wheel_zone.leds_max = 1; + wheel_zone.leds_count = 1; + wheel_zone.matrix_map = NULL; + zones.push_back(wheel_zone); + + led wheel_led; + wheel_led.name = "Scroll Wheel"; + wheel_led.value = 0x10; + leds.push_back(wheel_led); + + zone mouse_zone; + mouse_zone.name = "Mouse"; + mouse_zone.type = ZONE_TYPE_LINEAR; + mouse_zone.leds_min = 6; + mouse_zone.leds_max = 6; + mouse_zone.leds_count = 6; + mouse_zone.matrix_map = NULL; + zones.push_back(mouse_zone); + + for(const steelseries_rival_led_info led_info: rival_650_leds) + { + led mouse_led; + mouse_led.name = led_info.name; + mouse_led.value = led_info.value; + leds.push_back(mouse_led); + } + } + /* Rival 600 is simular to Rival 650 */ + else if(controller->GetMouseType() == RIVAL_600) + { + leds[0].value = 0x01; + + zone wheel_zone; + wheel_zone.name = "Scroll Wheel"; + wheel_zone.type = ZONE_TYPE_SINGLE; + wheel_zone.leds_min = 1; + wheel_zone.leds_max = 1; + wheel_zone.leds_count = 1; + wheel_zone.matrix_map = NULL; + zones.push_back(wheel_zone); + + led wheel_led; + wheel_led.name = "Scroll Wheel"; + wheel_led.value = 0x00; + leds.push_back(wheel_led); + + zone mouse_zone; + mouse_zone.name = "Mouse"; + mouse_zone.type = ZONE_TYPE_LINEAR; + mouse_zone.leds_min = 6; + mouse_zone.leds_max = 6; + mouse_zone.leds_count = 6; + mouse_zone.matrix_map = NULL; + zones.push_back(mouse_zone); + + for(const steelseries_rival_led_info led_info: rival_600_leds) + { + led mouse_led; + mouse_led.name = led_info.name; + mouse_led.value = led_info.value; + leds.push_back(mouse_led); + } + } + + SetupColors(); +} + +void RGBController_SteelSeriesRival::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SteelSeriesRival::DeviceUpdateLEDs() +{ + for(unsigned int i = 0; i < leds.size(); i++) + { + unsigned char red = RGBGetRValue(colors[i]); + unsigned char grn = RGBGetGValue(colors[i]); + unsigned char blu = RGBGetBValue(colors[i]); + controller->SetColor(leds[i].value, red, grn, blu); + } +} + +void RGBController_SteelSeriesRival::UpdateZoneLEDs(int zone) +{ + for(unsigned int i = 0; i < zones[zone].leds_count; i++) + { + unsigned char red = RGBGetRValue(zones[zone].colors[i]); + unsigned char grn = RGBGetGValue(zones[zone].colors[i]); + unsigned char blu = RGBGetBValue(zones[zone].colors[i]); + controller->SetColor(zones[zone].leds[i].value, red, grn, blu); + } +} + +void RGBController_SteelSeriesRival::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + controller->SetColor(leds[led].value, red, grn, blu); +} + +void RGBController_SteelSeriesRival::DeviceUpdateMode() +{ + /* Strictly, the device actually does support different modes for the + * different zones, but we don't support that. */ + //steelseries_type mouse_type = rival->GetMouseType(); + switch (modes[active_mode].value) + { + case STEELSERIES_RIVAL_DIRECT: + controller->SetLightEffectAll(STEELSERIES_RIVAL_EFFECT_DIRECT); + break; + + case STEELSERIES_RIVAL_PULSATE: + controller->SetLightEffectAll(modes[active_mode].speed); + break; + } + + DeviceUpdateLEDs(); +} + +void RGBController_SteelSeriesRival::DeviceSaveMode() +{ + DeviceUpdateMode(); + controller->SaveMode(); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesRivalController/RGBController_SteelSeriesRival.h b/Controllers/SteelSeriesController/SteelSeriesRivalController/RGBController_SteelSeriesRival.h new file mode 100644 index 0000000..b1c20e6 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesRivalController/RGBController_SteelSeriesRival.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesRival.h | +| | +| RGBController for SteelSeries Rival | +| | +| B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SteelSeriesRivalController.h" + +class RGBController_SteelSeriesRival : public RGBController +{ +public: + RGBController_SteelSeriesRival(SteelSeriesRivalController* controller_ptr); + ~RGBController_SteelSeriesRival(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + SteelSeriesRivalController* controller; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesRivalController/SteelSeriesRivalController.cpp b/Controllers/SteelSeriesController/SteelSeriesRivalController/SteelSeriesRivalController.cpp new file mode 100644 index 0000000..c05c0ce --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesRivalController/SteelSeriesRivalController.cpp @@ -0,0 +1,353 @@ +/*---------------------------------------------------------*\ +| SteelSeriesRivalController.cpp | +| | +| Driver for SteelSeries Rival | +| | +| B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "SteelSeriesRivalController.h" +#include "StringUtils.h" + +static void send_usb_msg(hid_device* dev, unsigned char * data_pkt, unsigned int size) +{ + unsigned char* usb_pkt = new unsigned char[size + 1]; + + usb_pkt[0] = 0x00; + for(unsigned int i = 1; i < size + 1; i++) + { + usb_pkt[i] = data_pkt[i-1]; + } + + hid_write(dev, usb_pkt, size + 1); + + delete[] usb_pkt; +} + +SteelSeriesRivalController::SteelSeriesRivalController + ( + hid_device* dev_handle, + steelseries_type proto_type, + const char* path, + std::string dev_name + ) +{ + dev = dev_handle; + location = path; + name = dev_name; + proto = proto_type; +} + +SteelSeriesRivalController::~SteelSeriesRivalController() +{ + hid_close(dev); +} + +std::string SteelSeriesRivalController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string SteelSeriesRivalController::GetDeviceName() +{ + return(name); +} + +std::string SteelSeriesRivalController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string SteelSeriesRivalController::GetFirmwareVersion() +{ + if (proto != RIVAL_300 && proto != RIVAL_700) return ""; + + unsigned char usb_buf[2] = { 0x10, 0x00 }; + uint16_t version; + std::string return_string; + + send_usb_msg(dev, usb_buf, 2); + hid_read(dev, (unsigned char *)&version, 2); + + return_string = std::to_string(version); + return return_string; +} + +steelseries_type SteelSeriesRivalController::GetMouseType() +{ + return proto; +} + +/* Saves to the internal configuration */ +void SteelSeriesRivalController::SaveMode() +{ + unsigned char usb_buf[9]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0x09; + send_usb_msg(dev, usb_buf, 9); +} + +void SteelSeriesRivalController::SetLightEffect + ( + unsigned char zone_id, + unsigned char effect + ) +{ + unsigned char usb_buf[9]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + switch(proto) + { + case RIVAL_100: + usb_buf[0x00] = 0x07; + usb_buf[0x01] = 0x00; + break; + + case RIVAL_300: + usb_buf[0x00] = 0x07; + usb_buf[0x01] = zone_id + 1; + break; + + case RIVAL_700: + return; + + default: + break; + } + usb_buf[0x02] = effect; + send_usb_msg(dev, usb_buf, 9); +} + +void SteelSeriesRivalController::SetLightEffectAll + ( + unsigned char effect + ) +{ + switch(proto) + { + case RIVAL_100: + SetLightEffect(0, effect); + break; + + case RIVAL_300: + SetLightEffect(0, effect); + SetLightEffect(1, effect); + break; + + case RIVAL_650: + for(int i=0x10; i<0x18; i++) + { + SetLightEffect(i, effect); + } + break; + + default: + break; + } +} + +void SteelSeriesRivalController::SetRival650Color + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[60]; + + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x03; + usb_buf[0x04] = 0x30; + usb_buf[0x06] = 0x10; + usb_buf[0x07] = 0x27; + usb_buf[0x16] = 0x01; + usb_buf[0x1E] = 0x04; + usb_buf[0x1F] = red; + usb_buf[0x20] = green; + usb_buf[0x21] = blue; + usb_buf[0x22] = 0xFF; + usb_buf[0x27] = 0xFF; + usb_buf[0x29] = 0x54; + usb_buf[0x2C] = 0xFF; + usb_buf[0x2D] = 0x54; + usb_buf[0x2E] = red; + usb_buf[0x2F] = green; + usb_buf[0x30] = blue; + usb_buf[0x31] = 0x56; + + send_usb_msg(dev, usb_buf, 60); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0x03; + usb_buf[0x02] = 0x30; + usb_buf[0x04] = 0x2C; + + send_usb_msg(dev, usb_buf, 60); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0x05; + usb_buf[0x02] = zone_id;//mousekey 0x10-0x17 + usb_buf[0x03] = 0xFF; + usb_buf[0x08] = 0x5C; + + send_usb_msg(dev, usb_buf, 60); + + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0x00] = 0x1C; + usb_buf[0x02] = 0x55; + usb_buf[0x04] = 0x46; + + send_usb_msg(dev, usb_buf, 60); +} + +void SteelSeriesRivalController::SetRival600Color + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_pkt[0x07]; + + memset(usb_pkt, 0x00, sizeof(usb_pkt)); + + usb_pkt[0x00] = 0x1c; + usb_pkt[0x01] = 0x27; + usb_pkt[0x02] = 0x00; + usb_pkt[0x03] = 1 << zone_id; + usb_pkt[0x04] = red; + usb_pkt[0x05] = green; + usb_pkt[0x06] = blue; + + hid_write(dev, usb_pkt, 0x07); +} + +void SteelSeriesRivalController::SetRival700Color + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + const uint16_t REPORT_SIZE = 578; + + unsigned char usb_buf[REPORT_SIZE]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0x00] = 0x05; + usb_buf[0x02] = zone_id; + + usb_buf[0x03] = red; + usb_buf[0x04] = green; + usb_buf[0x05] = blue; + + usb_buf[0x0b] = zone_id; + usb_buf[0x0c] = 0x01; + + unsigned char *usb_pkt = new unsigned char[REPORT_SIZE + 1]; + + usb_pkt[0] = 0x00; + for (unsigned int i = 1; i < REPORT_SIZE + 1; i++) + { + usb_pkt[i] = usb_buf[i - 1]; + } + + hid_send_feature_report(dev, usb_pkt, REPORT_SIZE + 1); + + delete[] usb_pkt; +} + +void SteelSeriesRivalController::SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[9]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + switch(proto) + { + case RIVAL_100: + usb_buf[0x00] = 0x05; + usb_buf[0x01] = 0x00; + break; + + case RIVAL_300: + usb_buf[0x00] = 0x08; + usb_buf[0x01] = zone_id + 1; + break; + + case RIVAL_650: + SetRival650Color(zone_id, red, green, blue); + return; + + case RIVAL_600: + SetRival600Color(zone_id, red, green, blue); + return; + + case RIVAL_700: + SetRival700Color(zone_id, red, green, blue); + return; + + default: + break; + } + + usb_buf[0x02] = red; + usb_buf[0x03] = green; + usb_buf[0x04] = blue; + + send_usb_msg(dev, usb_buf, 9); +} + +void SteelSeriesRivalController::SetColorAll + ( + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + switch(proto) + { + case RIVAL_100: + SetColor(0, red, green, blue); + break; + + case RIVAL_300: + SetColor(0, red, green, blue); + SetColor(1, red, green, blue); + break; + + case RIVAL_650: + for(int i = 0x10; i < 0x18; i++) + { + SetColor(i, red, green, blue); + } + break; + + default: + break; + } +} diff --git a/Controllers/SteelSeriesController/SteelSeriesRivalController/SteelSeriesRivalController.h b/Controllers/SteelSeriesController/SteelSeriesRivalController/SteelSeriesRivalController.h new file mode 100644 index 0000000..4158e9e --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesRivalController/SteelSeriesRivalController.h @@ -0,0 +1,110 @@ +/*---------------------------------------------------------*\ +| SteelSeriesRivalController.h | +| | +| Driver for SteelSeries Rival | +| | +| B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "SteelSeriesGeneric.h" + +/* Mode, we then use these to set actual effect based on speed. */ +enum +{ + STEELSERIES_RIVAL_DIRECT = 0x00, + STEELSERIES_RIVAL_PULSATE = 0x01 +}; + +/* Effects */ +enum +{ + STEELSERIES_RIVAL_EFFECT_DIRECT = 0x01, + STEELSERIES_RIVAL_EFFECT_PULSATE_MIN = 0x02, + STEELSERIES_RIVAL_EFFECT_PULSATE_MID = 0x03, + STEELSERIES_RIVAL_EFFECT_PULSATE_MAX = 0x04 +}; + +class SteelSeriesRivalController +{ +public: + SteelSeriesRivalController + ( + hid_device* dev_handle, + steelseries_type proto_type, + const char* path, + std::string dev_name + ); + + ~SteelSeriesRivalController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + std::string GetFirmwareVersion(); + + steelseries_type GetMouseType(); + + void SaveMode(); + + void SetLightEffect + ( + unsigned char zone_id, + unsigned char effect + ); + + void SetLightEffectAll + ( + unsigned char effect + ); + + void SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue + ); + void SetColorAll + ( + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev; + std::string location; + std::string name; + steelseries_type proto; + + void SetRival650Color + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SetRival600Color + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SetRival700Color + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue + ); +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesSenseiController/RGBController_SteelSeriesSensei.cpp b/Controllers/SteelSeriesController/SteelSeriesSenseiController/RGBController_SteelSeriesSensei.cpp new file mode 100644 index 0000000..13db491 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesSenseiController/RGBController_SteelSeriesSensei.cpp @@ -0,0 +1,152 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesSensei.cpp | +| | +| RGBController for SteelSeries Sensei | +| | +| Based on SteelSeries Rival controller | +| B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SteelSeriesSensei.h" + +/**------------------------------------------------------------------*\ + @name Steel Series Sensei + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectSteelSeriesSensei + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SteelSeriesSensei::RGBController_SteelSeriesSensei(SteelSeriesSenseiController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "SteelSeries"; + type = DEVICE_TYPE_MOUSE; + description = "SteelSeries Sensei Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = STEELSERIES_SENSEI_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = STEELSERIES_SENSEI_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = STEELSERIES_SENSEI_EFFECT_BREATHING_MIN; + Breathing.speed_max = STEELSERIES_SENSEI_EFFECT_BREATHING_MAX; + Breathing.speed = STEELSERIES_SENSEI_EFFECT_BREATHING_MID; + modes.push_back(Breathing); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = STEELSERIES_SENSEI_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed_min = STEELSERIES_SENSEI_EFFECT_RAINBOW_MIN; + Rainbow.speed_max = STEELSERIES_SENSEI_EFFECT_RAINBOW_MAX; + Rainbow.speed = STEELSERIES_SENSEI_EFFECT_RAINBOW_MID; + modes.push_back(Rainbow); + + SetupZones(); +} + +RGBController_SteelSeriesSensei::~RGBController_SteelSeriesSensei() +{ + delete controller; +} + +void RGBController_SteelSeriesSensei::SetupZones() +{ + zone logo_zone; + logo_zone.name = "Logo"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + zones.push_back(logo_zone); + + led logo_led; + logo_led.name = "Logo"; + leds.push_back(logo_led); + + zone wheel_zone; + wheel_zone.name = "Scroll Wheel"; + wheel_zone.type = ZONE_TYPE_SINGLE; + wheel_zone.leds_min = 1; + wheel_zone.leds_max = 1; + wheel_zone.leds_count = 1; + wheel_zone.matrix_map = NULL; + zones.push_back(wheel_zone); + + led wheel_led; + wheel_led.name = "Scroll Wheel"; + leds.push_back(wheel_led); + + SetupColors(); +} + +void RGBController_SteelSeriesSensei::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SteelSeriesSensei::DeviceUpdateLEDs() +{ + UpdateZoneLEDs(0); + UpdateZoneLEDs(1); +} + +void RGBController_SteelSeriesSensei::UpdateZoneLEDs(int zone) +{ + RGBColor color = colors[zone]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + switch(modes[active_mode].value) + { + case STEELSERIES_SENSEI_MODE_DIRECT: + controller->SetColor(zone, red, grn, blu); + break; + + case STEELSERIES_SENSEI_MODE_BREATHING: + case STEELSERIES_SENSEI_MODE_RAINBOW: + controller->SetLightEffect(zone, modes[active_mode].value, modes[active_mode].speed, red, grn, blu); + break; + } +} + +void RGBController_SteelSeriesSensei::UpdateSingleLED(int led) +{ + /*---------------------------------------------------------*\ + | Each zone only has a single LED, so we can use the LED ID | + | to reference the existing zone code. | + \*---------------------------------------------------------*/ + UpdateZoneLEDs(led); +} + +void RGBController_SteelSeriesSensei::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | Strictly, the device actually does support different modes| + | for the different zones, but we don't support that. | + \*---------------------------------------------------------*/ + DeviceUpdateLEDs(); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesSenseiController/RGBController_SteelSeriesSensei.h b/Controllers/SteelSeriesController/SteelSeriesSenseiController/RGBController_SteelSeriesSensei.h new file mode 100644 index 0000000..b4243dc --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesSenseiController/RGBController_SteelSeriesSensei.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesSensei.h | +| | +| RGBController for SteelSeries Sensei | +| | +| Based on SteelSeries Rival controller | +| B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SteelSeriesSenseiController.h" + +class RGBController_SteelSeriesSensei : public RGBController +{ +public: + RGBController_SteelSeriesSensei(SteelSeriesSenseiController* controller_ptr); + ~RGBController_SteelSeriesSensei(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SteelSeriesSenseiController* controller; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesSenseiController/SteelSeriesSenseiController.cpp b/Controllers/SteelSeriesController/SteelSeriesSenseiController/SteelSeriesSenseiController.cpp new file mode 100644 index 0000000..d15480e --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesSenseiController/SteelSeriesSenseiController.cpp @@ -0,0 +1,297 @@ +/*---------------------------------------------------------*\ +| SteelSeriesSenseiController.cpp | +| | +| Driver for SteelSeries Sensei | +| | +| Based on SteelSeries Rival controller | +| B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "SteelSeriesSenseiController.h" +#include "StringUtils.h" + +static void send_usb_msg(hid_device* dev, unsigned char * data_pkt, unsigned int size) +{ + unsigned char* usb_pkt = new unsigned char[size + 1]; + + usb_pkt[0] = 0x00; + for(unsigned int i = 1; i < size + 1; i++) + { + usb_pkt[i] = data_pkt[i-1]; + } + + hid_write(dev, usb_pkt, size + 1); + + delete[] usb_pkt; +} + +SteelSeriesSenseiController::SteelSeriesSenseiController + ( + hid_device* dev_handle, + steelseries_type proto_type, + const char* path, + std::string dev_name + ) +{ + dev = dev_handle; + location = path; + name = dev_name; + proto = proto_type; +} + +SteelSeriesSenseiController::~SteelSeriesSenseiController() +{ + hid_close(dev); +} + +std::string SteelSeriesSenseiController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string SteelSeriesSenseiController::GetDeviceName() +{ + return(name); +} + +std::string SteelSeriesSenseiController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +steelseries_type SteelSeriesSenseiController::GetMouseType() +{ + return proto; +} + +/* Saves to the internal configuration */ +void SteelSeriesSenseiController::Save() +{ + /*-----------------------------------------------------*\ + | Saves to the internal configuration | + \*-----------------------------------------------------*/ + unsigned char usb_buf[9]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Save packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x59; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + send_usb_msg(dev, usb_buf, 9); +} + + +void SteelSeriesSenseiController::SetLightEffect + ( + unsigned char zone_id, + unsigned char effect, + unsigned char speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Light Effect packet | + \*-----------------------------------------------------*/ + unsigned char dur1 = 0x27; + unsigned char dur2 = 0x10; //10 sec cycle + + switch(effect) + { + case STEELSERIES_SENSEI_MODE_BREATHING: + switch(speed) + { + case STEELSERIES_SENSEI_EFFECT_BREATHING_MIN: + dur1 = 0x27; + dur2 = 0x10; //10 sec cycle + break; + + case STEELSERIES_SENSEI_EFFECT_BREATHING_MID: + dur1 = 0x13; + dur2 = 0x88; //5 sec cycle + break; + + case STEELSERIES_SENSEI_EFFECT_BREATHING_MAX: + dur1 = 0x09; + dur2 = 0xc4; //2.5 sec cycle + break; + } + usb_buf[0x00] = 0x5B; //command byte + usb_buf[0x02] = zone_id; + usb_buf[0x04] = dur1; //duration in ms 1st byte + usb_buf[0x03] = dur2; //duration in ms 2nd byte + usb_buf[0x1B] = 0x03; //Number of colors + + /*---------------------------------------------*\ + | Original software duplicates these RGB bytes, | + | but seems unnecessary | + \*---------------------------------------------*/ + usb_buf[0x1C] = red; + usb_buf[0x1D] = green; + usb_buf[0x1E] = blue; + + usb_buf[0x1F] = red; + usb_buf[0x20] = green; + usb_buf[0x21] = blue; + usb_buf[0x26] = 0x7F; //percent of duration out of 0xFF + usb_buf[0x27] = red; + usb_buf[0x28] = green; + usb_buf[0x29] = blue; + usb_buf[0x2A] = 0x7F; //percent of duration out of 0xFF + break; + + case STEELSERIES_SENSEI_MODE_RAINBOW: + switch(speed) + { + case STEELSERIES_SENSEI_EFFECT_RAINBOW_MIN: + dur1 = 0x4E; + dur2 = 0x20; //20 sec cycle + break; + + case STEELSERIES_SENSEI_EFFECT_RAINBOW_MID: + dur1 = 0x27; + dur2 = 0x10; //10 sec cycle + break; + + case STEELSERIES_SENSEI_EFFECT_RAINBOW_MAX: + dur1 = 0x13; + dur2 = 0x88; //5 sec cycle + break; + } + usb_buf[0x00] = 0x5B; //command byte + usb_buf[0x02] = zone_id; + usb_buf[0x04] = dur1; //duration in ms 1st byte + usb_buf[0x03] = dur2; //duration in ms 2nd byte + usb_buf[0x1B] = 0x07; //Number of colors + + /*---------------------------------------------*\ + | Original software duplicates these RGB bytes, | + | but seems unnecessary | + \*---------------------------------------------*/ + usb_buf[0x1C] = red; + usb_buf[0x1D] = green; + usb_buf[0x1E] = blue; + + usb_buf[0x1C] = 0xFF; + usb_buf[0x1F] = 0xFF; + usb_buf[0x22] = 0x14; + usb_buf[0x23] = 0xFF; + usb_buf[0x24] = 0xFF; + usb_buf[0x26] = 0x2B; //percent of duration out of 0xFF + usb_buf[0x28] = 0xFF; + usb_buf[0x2A] = 0x2B; + usb_buf[0x2C] = 0xFF; + usb_buf[0x2D] = 0xFF; //percent of duration out of 0xFF + usb_buf[0x2E] = 0x28; + usb_buf[0x31] = 0xFF; + usb_buf[0x32] = 0x2B; + usb_buf[0x33] = 0xFF; + usb_buf[0x35] = 0xFF; + usb_buf[0x36] = 0x2B; + usb_buf[0x37] = 0xFF; + usb_buf[0x3A] = 0x14; + break; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + send_usb_msg(dev, usb_buf, sizeof(usb_buf)); +} + + +void SteelSeriesSenseiController::SetLightEffectAll + ( + unsigned char effect, + unsigned char speed, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SetLightEffect(0, effect, speed, red, green, blue); + SetLightEffect(1, effect, speed, red, green, blue); +} + + +void SteelSeriesSenseiController::SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Set Color packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x5B; //command byte + usb_buf[0x02] = zone_id; + + /*-----------------------------------------------------*\ + | Original software duplicates these RGB bytes, | + | but seems unnecessary | + \*-----------------------------------------------------*/ + usb_buf[0x1C] = red; + usb_buf[0x1D] = green; + usb_buf[0x1E] = blue; + + usb_buf[0x1F] = red; + usb_buf[0x20] = green; + usb_buf[0x21] = blue; + usb_buf[0x13] = 0x01; //Static color flag + usb_buf[0x1B] = 0x01; //Number of colors + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + send_usb_msg(dev, usb_buf, sizeof(usb_buf)); +} + +void SteelSeriesSenseiController::SetColorAll + ( + unsigned char red, + unsigned char green, + unsigned char blue + ) +{ + SetColor(0, red, green, blue); + SetColor(1, red, green, blue); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesSenseiController/SteelSeriesSenseiController.h b/Controllers/SteelSeriesController/SteelSeriesSenseiController/SteelSeriesSenseiController.h new file mode 100644 index 0000000..c5e2bc2 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesSenseiController/SteelSeriesSenseiController.h @@ -0,0 +1,103 @@ +/*---------------------------------------------------------*\ +| SteelSeriesSenseiController.h | +| | +| Driver for SteelSeries Sensei | +| | +| Based on SteelSeries Rival controller | +| B Horn (bahorn) 13 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "SteelSeriesGeneric.h" + +/*-------------------------------------------------------------*\ +| Mode, we then use these to set actual effect based on speed. | +\*-------------------------------------------------------------*/ +enum +{ + STEELSERIES_SENSEI_MODE_DIRECT = 0x00, + STEELSERIES_SENSEI_MODE_BREATHING = 0x01, + STEELSERIES_SENSEI_MODE_RAINBOW = 0x02 +}; + +/*-------------------------------------------------------------*\ +| Effects | +\*-------------------------------------------------------------*/ +enum +{ + STEELSERIES_SENSEI_EFFECT_DIRECT = 0x01, + STEELSERIES_SENSEI_EFFECT_BREATHING_MIN = 0x02, + STEELSERIES_SENSEI_EFFECT_BREATHING_MID = 0x03, + STEELSERIES_SENSEI_EFFECT_BREATHING_MAX = 0x04, + STEELSERIES_SENSEI_EFFECT_RAINBOW_MIN = 0x05, + STEELSERIES_SENSEI_EFFECT_RAINBOW_MID = 0x06, + STEELSERIES_SENSEI_EFFECT_RAINBOW_MAX = 0x07 +}; + +class SteelSeriesSenseiController +{ +public: + SteelSeriesSenseiController + ( + hid_device* dev_handle, + steelseries_type proto_type, + const char* path, + std::string dev_name + ); + + ~SteelSeriesSenseiController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + steelseries_type GetMouseType(); + + void Save(); + + void SetLightEffect + ( + unsigned char zone_id, + unsigned char effect, + unsigned char speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SetLightEffectAll + ( + unsigned char effect, + unsigned char speed, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SetColor + ( + unsigned char zone_id, + unsigned char red, + unsigned char green, + unsigned char blue + ); + + void SetColorAll + ( + unsigned char red, + unsigned char green, + unsigned char blue + ); + +private: + hid_device* dev; + std::string location; + std::string name; + steelseries_type proto; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesSiberiaController/RGBController_SteelSeriesSiberia.cpp b/Controllers/SteelSeriesController/SteelSeriesSiberiaController/RGBController_SteelSeriesSiberia.cpp new file mode 100644 index 0000000..614d1c6 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesSiberiaController/RGBController_SteelSeriesSiberia.cpp @@ -0,0 +1,103 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesSiberia.cpp | +| | +| RGBController for SteelSeries Siberia | +| | +| E Karlsson (pilophae) 18 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SteelSeriesSiberia.h" + +/**------------------------------------------------------------------*\ + @name Steel Series Siberia + @category Headset + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectSteelSeriesHeadset + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SteelSeriesSiberia::RGBController_SteelSeriesSiberia(SteelSeriesSiberiaController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "SteelSeries"; + type = DEVICE_TYPE_HEADSET; + description = "SteelSeries Siberia Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + SetupZones(); +} + +RGBController_SteelSeriesSiberia::~RGBController_SteelSeriesSiberia() +{ + delete controller; +} + +void RGBController_SteelSeriesSiberia::SetupZones() +{ + /* Siberia 350 only has one Zone */ + zone earpiece_zone; + earpiece_zone.name = "Headset"; + earpiece_zone.type = ZONE_TYPE_SINGLE; + earpiece_zone.leds_min = 1; + earpiece_zone.leds_max = 1; + earpiece_zone.leds_count = 1; + earpiece_zone.matrix_map = NULL; + zones.push_back(earpiece_zone); + + led earpiece_led; + earpiece_led.name = "Headset LED"; + leds.push_back(earpiece_led); + + SetupColors(); +} + +void RGBController_SteelSeriesSiberia::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_SteelSeriesSiberia::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + controller->SetColor(red, grn, blu); +} + +void RGBController_SteelSeriesSiberia::UpdateZoneLEDs(int zone) +{ + RGBColor color = colors[zone]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + controller->SetColor(red, grn, blu); +} + +void RGBController_SteelSeriesSiberia::UpdateSingleLED(int led) +{ + /* Each zone only has a single LED, so we can use the LED ID to reference + * the existing zone code. */ + UpdateZoneLEDs(led); +} + +void RGBController_SteelSeriesSiberia::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesSiberiaController/RGBController_SteelSeriesSiberia.h b/Controllers/SteelSeriesController/SteelSeriesSiberiaController/RGBController_SteelSeriesSiberia.h new file mode 100644 index 0000000..555b93d --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesSiberiaController/RGBController_SteelSeriesSiberia.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_SteelSeriesSiberia.h | +| | +| RGBController for SteelSeries Siberia | +| | +| E Karlsson (pilophae) 18 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SteelSeriesSiberiaController.h" + +class RGBController_SteelSeriesSiberia : public RGBController +{ +public: + RGBController_SteelSeriesSiberia(SteelSeriesSiberiaController* controller_ptr); + ~RGBController_SteelSeriesSiberia(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + SteelSeriesSiberiaController* controller; +}; diff --git a/Controllers/SteelSeriesController/SteelSeriesSiberiaController/SteelSeriesSiberiaController.cpp b/Controllers/SteelSeriesController/SteelSeriesSiberiaController/SteelSeriesSiberiaController.cpp new file mode 100644 index 0000000..ea5892c --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesSiberiaController/SteelSeriesSiberiaController.cpp @@ -0,0 +1,105 @@ +/*---------------------------------------------------------*\ +| SteelSeriesSiberiaController.cpp | +| | +| Driver for SteelSeries Siberia | +| | +| E Karlsson (pilophae) 18 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SteelSeriesSiberiaController.h" +#include "StringUtils.h" + +static void send_usb_msg(hid_device* dev, unsigned char * data_pkt, unsigned int size) +{ + unsigned char usb_pkt[16]; + memset(usb_pkt, 0x00, sizeof(usb_pkt)); + + // Report number + usb_pkt[0] = 0x01; + // Magic + usb_pkt[1] = 0x00; + // Command + usb_pkt[2] = data_pkt[0]; + // Payload length + usb_pkt[3] = size - 1; + + for(unsigned int i = 0; i < (size - 1); i++) + { + usb_pkt[4 + i] = data_pkt[1 + i]; + } + + hid_write(dev, usb_pkt, 16); +} + +SteelSeriesSiberiaController::SteelSeriesSiberiaController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; +} + +SteelSeriesSiberiaController::~SteelSeriesSiberiaController() +{ + hid_close(dev); +} + +std::string SteelSeriesSiberiaController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string SteelSeriesSiberiaController::GetDeviceName() +{ + return(name); +} + +std::string SteelSeriesSiberiaController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void SteelSeriesSiberiaController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char usb_buf[4]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + // Command 1 + usb_buf[0] = 0x95; + usb_buf[1] = 0x80; + usb_buf[2] = 0xbf; + send_usb_msg(dev, usb_buf, 3); + + // Command 2 + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0] = 0x80; + usb_buf[1] = 0x52; + usb_buf[2] = 0x20; + send_usb_msg(dev, usb_buf, 3); + + // Command 3 (Set color) + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0] = 0x83; + usb_buf[1] = red; + usb_buf[2] = green; + usb_buf[3] = blue; + send_usb_msg(dev, usb_buf, 4); + + // Command 4 + memset(usb_buf, 0x00, sizeof(usb_buf)); + usb_buf[0] = 0x93; + usb_buf[1] = 0x03; + usb_buf[2] = 0x80; + send_usb_msg(dev, usb_buf, 3); +} diff --git a/Controllers/SteelSeriesController/SteelSeriesSiberiaController/SteelSeriesSiberiaController.h b/Controllers/SteelSeriesController/SteelSeriesSiberiaController/SteelSeriesSiberiaController.h new file mode 100644 index 0000000..dd21cf7 --- /dev/null +++ b/Controllers/SteelSeriesController/SteelSeriesSiberiaController/SteelSeriesSiberiaController.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| SteelSeriesSiberiaController.h | +| | +| Driver for SteelSeries Siberia | +| | +| E Karlsson (pilophae) 18 Jun 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +class SteelSeriesSiberiaController +{ +public: + SteelSeriesSiberiaController(hid_device* dev_handle, const char* path, std::string dev_name); + ~SteelSeriesSiberiaController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + std::string GetSerialString(); + + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + +private: + hid_device* dev; + std::string location; + std::string name; +}; diff --git a/Controllers/SteelSeriesController/color32.h b/Controllers/SteelSeriesController/color32.h new file mode 100644 index 0000000..e3ec32c --- /dev/null +++ b/Controllers/SteelSeriesController/color32.h @@ -0,0 +1,20 @@ +/*---------------------------------------------------------*\ +| color32.h | +| | +| Class to hold 32-bit color data | +| | +| David Lee (RAMChYLD) 15 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +typedef struct +{ + unsigned char red; + unsigned char green; + unsigned char blue; + unsigned char alpha; +} color32; diff --git a/Controllers/StreamDeckController/ElgatoStreamDeckController.cpp b/Controllers/StreamDeckController/ElgatoStreamDeckController.cpp new file mode 100644 index 0000000..e281bb3 --- /dev/null +++ b/Controllers/StreamDeckController/ElgatoStreamDeckController.cpp @@ -0,0 +1,85 @@ +/*---------------------------------------------------------*\ +| ElgatoStreamDeckController.cpp | +| | +| Driver for Elgato Stream Deck MK.2 | +| | +| Ferréol DUBOIS COLI (Fefe_du_973) 23 Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "ElgatoStreamDeckController.h" +#include "StringUtils.h" + +ElgatoStreamDeckController::ElgatoStreamDeckController(hid_device* dev_handle, const char* path) : + dev(dev_handle), location(path) +{ +} + +ElgatoStreamDeckController::~ElgatoStreamDeckController() +{ + hid_close(dev); +} + +std::string ElgatoStreamDeckController::GetLocation() +{ + return location; +} + +std::string ElgatoStreamDeckController::GetSerialString() +{ + wchar_t serial[256]; + if(hid_get_serial_number_string(dev, serial, 256) >= 0) + { + std::wstring ws(serial); + return StringUtils::wstring_to_string(ws); + } + return ""; +} + +void ElgatoStreamDeckController::SetBrightness(unsigned char brightness) +{ + unsigned char buffer[32] = {0x03, 0x08, brightness}; + hid_send_feature_report(dev, buffer, sizeof(buffer)); +} + +void ElgatoStreamDeckController::SendFullFrame(const std::vector>& buttonImages) +{ + for(int btnIdx = 0; btnIdx < 15; btnIdx++) + { + if(btnIdx < (int)buttonImages.size()) + { + SendButtonImage(btnIdx, buttonImages[btnIdx]); + } + } +} + +void ElgatoStreamDeckController::SendButtonImage(int buttonIndex, const std::vector& jpegData) +{ + const size_t headerSize = 8; + const size_t packetSize = 1024; + unsigned char buffer[packetSize] = {0}; + + buffer[0] = 0x02; + buffer[1] = 0x07; + buffer[2] = buttonIndex; + buffer[3] = 0x01; + buffer[4] = jpegData.size() & 0xFF; + buffer[5] = (jpegData.size() >> 8) & 0xFF; + buffer[6] = 0x00; + buffer[7] = 0x00; + + size_t bytesToCopy = std::min(jpegData.size(), packetSize - headerSize); + memcpy(buffer + headerSize, jpegData.data(), bytesToCopy); + + hid_write(dev, buffer, packetSize); +} + +void ElgatoStreamDeckController::Reset() +{ + unsigned char resetBuffer[32] = {0x03, 0x02}; + hid_send_feature_report(dev, resetBuffer, sizeof(resetBuffer)); +} diff --git a/Controllers/StreamDeckController/ElgatoStreamDeckController.h b/Controllers/StreamDeckController/ElgatoStreamDeckController.h new file mode 100644 index 0000000..f6d8995 --- /dev/null +++ b/Controllers/StreamDeckController/ElgatoStreamDeckController.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| ElgatoStreamDeckController.h | +| | +| Driver for Elgato Stream Deck MK.2 | +| | +| Ferréol DUBOIS COLI (Fefe_du_973) 23 Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +class ElgatoStreamDeckController +{ +public: + ElgatoStreamDeckController(hid_device* dev_handle, const char* path); + ~ElgatoStreamDeckController(); + + std::string GetLocation(); + std::string GetSerialString(); + void SetBrightness(unsigned char brightness); + void SendFullFrame(const std::vector>& buttonImages); + void SendButtonImage(int buttonIndex, const std::vector& jpegData); + +private: + hid_device* dev; + std::string location; + + void Reset(); +}; diff --git a/Controllers/StreamDeckController/ElgatoStreamDeckControllerDetect.cpp b/Controllers/StreamDeckController/ElgatoStreamDeckControllerDetect.cpp new file mode 100644 index 0000000..ee8e48c --- /dev/null +++ b/Controllers/StreamDeckController/ElgatoStreamDeckControllerDetect.cpp @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| ElgatoStreamDeckControllerDetect.cpp | +| | +| Detector for Elgato Stream Deck MK.2 | +| | +| Ferréol DUBOIS COLI (Fefe_du_973) 23 Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ElgatoStreamDeckController.h" +#include "RGBController_ElgatoStreamDeck.h" + +#define ELGATO_VID 0x0FD9 +#define STREAMDECK_MK2_PID 0x0080 + +void DetectElgatoStreamDeckControllers(hid_device_info* info, const std::string&) +{ + if(info->interface_number == 0) + { + hid_device* dev = hid_open_path(info->path); + if(dev) + { + ElgatoStreamDeckController* controller = new ElgatoStreamDeckController(dev, info->path); + RGBController_ElgatoStreamDeck* rgb_controller = new RGBController_ElgatoStreamDeck(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} + +REGISTER_HID_DETECTOR("Elgato Stream Deck MK.2", DetectElgatoStreamDeckControllers, ELGATO_VID, STREAMDECK_MK2_PID); diff --git a/Controllers/StreamDeckController/RGBController_ElgatoStreamDeck.cpp b/Controllers/StreamDeckController/RGBController_ElgatoStreamDeck.cpp new file mode 100644 index 0000000..9168236 --- /dev/null +++ b/Controllers/StreamDeckController/RGBController_ElgatoStreamDeck.cpp @@ -0,0 +1,128 @@ +/*---------------------------------------------------------*\ +| RGBController_ElgatoStreamDeck.cpp | +| | +| RGBController for Elgato Stream Deck MK.2 | +| | +| Ferréol DUBOIS COLI (Fefe_du_973) 23 Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "RGBController_ElgatoStreamDeck.h" +#include "stb_image_write.h" + +/**------------------------------------------------------------------*\ + @name Elgato Stream Deck MK.2 15 Buttons + @category Accessory + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectElgatoStreamDeckControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_ElgatoStreamDeck::RGBController_ElgatoStreamDeck(ElgatoStreamDeckController *controller_ptr) : controller(controller_ptr) +{ + name = "Elgato Stream Deck MK.2"; + vendor = "Elgato"; + type = DEVICE_TYPE_ACCESSORY; + description = "Stream Deck MK.2 Controller"; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_ElgatoStreamDeck::~RGBController_ElgatoStreamDeck() +{ + delete controller; +} + +void RGBController_ElgatoStreamDeck::SetupZones() +{ + zone deck_zone; + deck_zone.name = "Button Matrix"; + deck_zone.type = ZONE_TYPE_MATRIX; + deck_zone.leds_min = 15; + deck_zone.leds_max = 15; + deck_zone.leds_count = 15; + deck_zone.matrix_map = new matrix_map_type; + deck_zone.matrix_map->height = 3; + deck_zone.matrix_map->width = 5; + deck_zone.matrix_map->map = new unsigned int[15]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + + zones.push_back(deck_zone); + + for(unsigned int i = 0; i < 15; i++) + { + led new_led; + new_led.name = "Button " + std::to_string(i + 1); + leds.push_back(new_led); + } + + SetupColors(); +} + +std::vector RGBController_ElgatoStreamDeck::CreateButtonImage(const RGBColor &color) +{ + const int width = 72; + const int height = 72; + std::vector pixels(width * height * 3); + + unsigned char r = RGBGetRValue(color); + unsigned char g = RGBGetGValue(color); + unsigned char b = RGBGetBValue(color); + + for(int i = 0; i < width * height; i++) + { + pixels[i * 3 + 0] = r; + pixels[i * 3 + 1] = g; + pixels[i * 3 + 2] = b; + } + + std::vector jpegData; + stbi_write_jpg_to_func([](void *context, void *data, int size) + { + std::vector* vec = static_cast*>(context); + vec->insert(vec->end(), static_cast(data), static_cast(data) + size); }, &jpegData, width, height, 3, pixels.data(), 95); // Quality 95 + + return jpegData; +} + +void RGBController_ElgatoStreamDeck::DeviceUpdateLEDs() +{ + std::vector> buttonImages; + for(unsigned int i = 0; i < leds.size(); i++) + { + buttonImages.push_back(CreateButtonImage(colors[i])); + } + controller->SendFullFrame(buttonImages); +} + +void RGBController_ElgatoStreamDeck::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ElgatoStreamDeck::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ElgatoStreamDeck::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_ElgatoStreamDeck::DeviceUpdateMode() +{ + +} diff --git a/Controllers/StreamDeckController/RGBController_ElgatoStreamDeck.h b/Controllers/StreamDeckController/RGBController_ElgatoStreamDeck.h new file mode 100644 index 0000000..c9bc2b5 --- /dev/null +++ b/Controllers/StreamDeckController/RGBController_ElgatoStreamDeck.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_ElgatoStreamDeck.h | +| | +| RGBController for Elgato Stream Deck MK.2 | +| | +| Ferréol DUBOIS COLI (Fefe_du_973) 23 Jan 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "ElgatoStreamDeckController.h" +#include "RGBController.h" + +class RGBController_ElgatoStreamDeck : public RGBController +{ +public: + explicit RGBController_ElgatoStreamDeck(ElgatoStreamDeckController* controller_ptr); + ~RGBController_ElgatoStreamDeck(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ElgatoStreamDeckController* controller; + + std::vector CreateButtonImage(const RGBColor& color); +}; diff --git a/Controllers/TForceXtreemController/RGBController_TForceXtreem.cpp b/Controllers/TForceXtreemController/RGBController_TForceXtreem.cpp new file mode 100644 index 0000000..e3e7a26 --- /dev/null +++ b/Controllers/TForceXtreemController/RGBController_TForceXtreem.cpp @@ -0,0 +1,502 @@ +/*---------------------------------------------------------*\ +| RGBController_TForceXtreem.cpp | +| | +| RGBController for TeamGroup T-Force Xtreem RAM | +| | +| Milan Cermak (krysmanta) 28 Dec 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_TForceXtreem.h" +#include "LogManager.h" +#include "ResourceManager.h" + +/**------------------------------------------------------------------*\ + @name T-Force Xtreem + @category RAM + @type SMBus + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectTForceXtreemControllers + @comment + Verified models: + TeamGroup T-Force Xtreem ARGB DDR4 +\*-------------------------------------------------------------------*/ + +RGBController_TForceXtreem::RGBController_TForceXtreem(TForceXtreemController * controller_ptr) +{ + controller = controller_ptr; + + type = DEVICE_TYPE_DRAM; + name = "T-Force Xtreem RGB"; + vendor = "TeamGroup"; + + location = controller->GetDeviceLocation(); + description = "TeamGroup T-Force Xtreem DRAM"; + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = XTREEM_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = XTREEM_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = XTREEM_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = XTREEM_SPEED_SLOWEST; + Breathing.speed_max = XTREEM_SPEED_FASTEST; + Breathing.speed = XTREEM_SPEED_NORMAL; + modes.push_back(Breathing); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = XTREEM_MODE_FLASHING; + Flashing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED; + Flashing.color_mode = MODE_COLORS_PER_LED; + Flashing.speed_min = XTREEM_SPEED_SLOWEST; + Flashing.speed_max = XTREEM_SPEED_FASTEST; + Flashing.speed = XTREEM_SPEED_NORMAL; + modes.push_back(Flashing); + + mode SpectrumCycle; + SpectrumCycle.name = "Spectrum Cycle"; + SpectrumCycle.value = XTREEM_MODE_SPECTRUM_CYCLE; + SpectrumCycle.flags = MODE_FLAG_HAS_SPEED; + SpectrumCycle.color_mode = MODE_COLORS_NONE; + SpectrumCycle.speed_min = XTREEM_SPEED_SLOWEST; + SpectrumCycle.speed_max = XTREEM_SPEED_FASTEST; + SpectrumCycle.speed = XTREEM_SPEED_NORMAL; + modes.push_back(SpectrumCycle); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = XTREEM_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Rainbow.color_mode = MODE_COLORS_NONE; + Rainbow.speed_min = XTREEM_SPEED_SLOWEST; + Rainbow.speed_max = XTREEM_SPEED_FASTEST; + Rainbow.speed = XTREEM_SPEED_NORMAL; + Rainbow.direction = MODE_DIRECTION_LEFT; + modes.push_back(Rainbow); + + mode ChaseFade; + ChaseFade.name = "Chase Fade"; + ChaseFade.value = XTREEM_MODE_CHASE_FADE; + ChaseFade.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + ChaseFade.color_mode = MODE_COLORS_PER_LED; + ChaseFade.speed_min = XTREEM_SPEED_SLOWEST; + ChaseFade.speed_max = XTREEM_SPEED_FASTEST; + ChaseFade.speed = XTREEM_SPEED_NORMAL; + ChaseFade.direction = MODE_DIRECTION_LEFT; + modes.push_back(ChaseFade); + + mode Chase; + Chase.name = "Chase"; + Chase.value = XTREEM_MODE_CHASE; + Chase.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Chase.color_mode = MODE_COLORS_PER_LED; + Chase.speed_min = XTREEM_SPEED_SLOWEST; + Chase.speed_max = XTREEM_SPEED_FASTEST; + Chase.speed = XTREEM_SPEED_NORMAL; + ChaseFade.direction = MODE_DIRECTION_LEFT; + modes.push_back(Chase); + + mode RandomFlicker; + RandomFlicker.name = "Random Flicker"; + RandomFlicker.value = XTREEM_MODE_RANDOM_FLICKER; + RandomFlicker.flags = MODE_FLAG_HAS_SPEED; + RandomFlicker.color_mode = MODE_COLORS_NONE; + RandomFlicker.speed_min = XTREEM_SPEED_SLOWEST; + RandomFlicker.speed_max = XTREEM_SPEED_FASTEST; + RandomFlicker.speed = XTREEM_SPEED_NORMAL; + modes.push_back(RandomFlicker); + + mode Stack; + Stack.name = "Stack"; + Stack.value = XTREEM_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Stack.color_mode = MODE_COLORS_NONE; + Stack.speed_min = XTREEM_SPEED_SLOWEST; + Stack.speed_max = XTREEM_SPEED_FASTEST; + Stack.speed = XTREEM_SPEED_NORMAL; + modes.push_back(Stack); + + mode Pong; + Pong.name = "Pong"; + Pong.value = XTREEM_MODE_PONG; + Pong.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Pong.color_mode = MODE_COLORS_NONE; + Pong.speed_min = XTREEM_SPEED_SLOWEST; + Pong.speed_max = XTREEM_SPEED_FASTEST; + Pong.speed = XTREEM_SPEED_NORMAL; + modes.push_back(Pong); + + mode Fillup; + Fillup.name = "Fill up"; + Fillup.value = XTREEM_MODE_FILLUP; + Fillup.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + Fillup.color_mode = MODE_COLORS_NONE; + Fillup.speed_min = XTREEM_SPEED_SLOWEST; + Fillup.speed_max = XTREEM_SPEED_FASTEST; + Fillup.speed = XTREEM_SPEED_NORMAL; + modes.push_back(Fillup); + + mode Neon; + Neon.name = "Neon Sign"; + Neon.value = XTREEM_MODE_NEON; + Neon.flags = MODE_FLAG_HAS_SPEED; + Neon.color_mode = MODE_COLORS_NONE; + Neon.speed_min = XTREEM_SPEED_SLOWEST; + Neon.speed_max = XTREEM_SPEED_FASTEST; + Neon.speed = XTREEM_SPEED_NORMAL; + modes.push_back(Neon); + + mode ColorWave; + ColorWave.name = "Wave"; + ColorWave.value = XTREEM_MODE_COLOR_WAVE; + ColorWave.flags = MODE_FLAG_HAS_SPEED; + ColorWave.color_mode = MODE_COLORS_NONE; + ColorWave.speed_min = XTREEM_SPEED_SLOWEST; + ColorWave.speed_max = XTREEM_SPEED_FASTEST; + ColorWave.speed = XTREEM_SPEED_NORMAL; + modes.push_back(ColorWave); + + mode DoubleWave; + DoubleWave.name = "Double Wave"; + DoubleWave.value = XTREEM_MODE_COLOR_DOUBLE_WAVE; + DoubleWave.flags = MODE_FLAG_HAS_SPEED; + DoubleWave.color_mode = MODE_COLORS_NONE; + DoubleWave.speed_min = XTREEM_SPEED_SLOWEST; + DoubleWave.speed_max = XTREEM_SPEED_FASTEST; + DoubleWave.speed = XTREEM_SPEED_NORMAL; + modes.push_back(DoubleWave); + + mode Mixer; + Mixer.name = "Mixer"; + Mixer.value = XTREEM_MODE_MIXER; + Mixer.flags = MODE_FLAG_HAS_SPEED; + Mixer.color_mode = MODE_COLORS_NONE; + Mixer.speed_min = XTREEM_SPEED_SLOWEST; + Mixer.speed_max = XTREEM_SPEED_FASTEST; + Mixer.speed = XTREEM_SPEED_NORMAL; + modes.push_back(Mixer); + + mode Spectrum2; + Spectrum2.name = "Spectrum Cycle 2"; + Spectrum2.value = XTREEM_MODE_SPECTRUM_CYCLE_2; + Spectrum2.flags = MODE_FLAG_HAS_SPEED; + Spectrum2.color_mode = MODE_COLORS_NONE; + Spectrum2.speed_min = XTREEM_SPEED_SLOWEST; + Spectrum2.speed_max = XTREEM_SPEED_FASTEST; + Spectrum2.speed = XTREEM_SPEED_NORMAL; + modes.push_back(Spectrum2); + + mode FireBreathing; + FireBreathing.name = "Fire Breathing"; + FireBreathing.value = XTREEM_MODE_FIRE_BREATHING; + FireBreathing.flags = MODE_FLAG_HAS_SPEED; + FireBreathing.color_mode = MODE_COLORS_NONE; + FireBreathing.speed_min = XTREEM_SPEED_SLOWEST; + FireBreathing.speed_max = XTREEM_SPEED_FASTEST; + FireBreathing.speed = XTREEM_SPEED_NORMAL; + modes.push_back(FireBreathing); + + mode Spectrum3; + Spectrum3.name = "Spectrum Cycle 3"; + Spectrum3.value = XTREEM_MODE_SPECTRUM_CYCLE_3; + Spectrum3.flags = MODE_FLAG_HAS_SPEED; + Spectrum3.color_mode = MODE_COLORS_NONE; + Spectrum3.speed_min = XTREEM_SPEED_SLOWEST; + Spectrum3.speed_max = XTREEM_SPEED_FASTEST; + Spectrum3.speed = XTREEM_SPEED_NORMAL; + modes.push_back(Spectrum3); + + mode Slither; + Slither.name = "Slither"; + Slither.value = XTREEM_MODE_SLITHER; + Slither.flags = MODE_FLAG_HAS_SPEED; + Slither.color_mode = MODE_COLORS_NONE; + Slither.speed_min = XTREEM_SPEED_SLOWEST; + Slither.speed_max = XTREEM_SPEED_FASTEST; + Slither.speed = XTREEM_SPEED_NORMAL; + modes.push_back(Slither); + + mode TForceXtreem; + TForceXtreem.name = "T-Force Xtreem"; + TForceXtreem.value = XTREEM_MODE_TFORCE_XTREEM; + TForceXtreem.flags = MODE_FLAG_HAS_SPEED; + TForceXtreem.color_mode = MODE_COLORS_NONE; + TForceXtreem.speed_min = XTREEM_SPEED_SLOWEST; + TForceXtreem.speed_max = XTREEM_SPEED_FASTEST; + TForceXtreem.speed = XTREEM_SPEED_NORMAL; + modes.push_back(TForceXtreem); + + SetupZones(); + + /*-------------------------------------------------*\ + | Initialize active mode | + \*-------------------------------------------------*/ + active_mode = GetDeviceMode(); +} + +RGBController_TForceXtreem::~RGBController_TForceXtreem() +{ + delete controller; +} + +int RGBController_TForceXtreem::GetDeviceMode() +{ + /*-----------------------------------------------------------------*\ + | Determine starting mode by reading the mode and direct registers | + \*-----------------------------------------------------------------*/ + int dev_mode = controller->ENERegisterRead(XTREEM_REG_MODE); + int color_mode = MODE_COLORS_PER_LED; + int speed = controller->ENERegisterRead(XTREEM_REG_SPEED); + int direction = controller->ENERegisterRead(XTREEM_REG_DIRECTION); + + LOG_TRACE("[%s] Retrieved ENE mode from module: %02d", name.c_str(), dev_mode); + + if(controller->ENERegisterRead(XTREEM_REG_DIRECT)) + { + dev_mode = 0xFFFF; + } + + switch(dev_mode) + { + case XTREEM_MODE_OFF: + case XTREEM_MODE_RAINBOW: + case XTREEM_MODE_SPECTRUM_CYCLE: + case XTREEM_MODE_RANDOM_FLICKER: + color_mode = MODE_COLORS_NONE; + break; + + case XTREEM_MODE_SPECTRUM_CYCLE_CHASE: + dev_mode = XTREEM_MODE_CHASE; + color_mode = MODE_COLORS_RANDOM; + break; + + case XTREEM_MODE_SPECTRUM_CYCLE_BREATHING: + dev_mode = XTREEM_MODE_BREATHING; + color_mode = MODE_COLORS_RANDOM; + break; + + case XTREEM_MODE_SPECTRUM_CYCLE_CHASE_FADE: + dev_mode = XTREEM_MODE_CHASE_FADE; + color_mode = MODE_COLORS_RANDOM; + break; + } + + for(int mode = 0; mode < (int)modes.size(); mode++) + { + if(modes[mode].value == dev_mode) + { + active_mode = mode; + modes[mode].color_mode = color_mode; + + if(modes[mode].flags & MODE_FLAG_HAS_SPEED) + { + modes[mode].speed = speed; + } + + if(modes[mode].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + if(direction == XTREEM_DIRECTION_FORWARD) + { + modes[mode].direction = MODE_DIRECTION_RIGHT; + } + else + { + modes[mode].direction = MODE_DIRECTION_LEFT; + } + } + + break; + } + } + + /*---------------------------------------------------------*\ + | Initialize colors for each LED | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned int led = leds[led_idx].value; + unsigned char red; + unsigned char grn; + unsigned char blu; + + if(active_mode == 0) + { + red = controller->GetLEDRed(led); + grn = controller->GetLEDGreen(led); + blu = controller->GetLEDBlue(led); + } + else + { + red = controller->GetLEDRedEffect(led); + grn = controller->GetLEDGreenEffect(led); + blu = controller->GetLEDBlueEffect(led); + } + + colors[led_idx] = ToRGBColor(red, grn, blu); + } + + return(active_mode); +} + +void RGBController_TForceXtreem::DeviceUpdateLEDs() +{ + if(GetMode() == 0) + { + controller->SetAllColorsDirect(&colors[0]); + } + else + { + controller->SetAllColorsEffect(&colors[0]); + } + +} + +void RGBController_TForceXtreem::UpdateZoneLEDs(int zone) +{ + for(std::size_t led_idx = 0; led_idx < zones[zone].leds_count; led_idx++) + { + int led = zones[zone].leds[led_idx].value; + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + if(GetMode() == 0) + { + controller->SetLEDColorDirect(led, red, grn, blu); + } + else + { + controller->SetLEDColorEffect(led, red, grn, blu); + } + } +} + +void RGBController_TForceXtreem::UpdateSingleLED(int led) +{ + RGBColor color = colors[led]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + + if(GetMode() == 0) + { + controller->SetLEDColorDirect(led, red, grn, blu); + } + else + { + controller->SetLEDColorEffect(led, red, grn, blu); + } +} + +void RGBController_TForceXtreem::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zone | + \*---------------------------------------------------------*/ + zone new_zone; + new_zone.name = "DRAM"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = XTREEM_LED_COUNT; + new_zone.leds_max = XTREEM_LED_COUNT; + new_zone.leds_count = XTREEM_LED_COUNT; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + /*---------------------------------------------------------*\ + | Set up LEDs | + \*---------------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led new_led; + new_led.name = "DRAM LED "; + new_led.name.append(std::to_string(led_idx)); + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_TForceXtreem::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_TForceXtreem::DeviceUpdateMode() +{ + if(modes[active_mode].value == 0xFFFF) + { + controller->SetDirect(true); + } + else + { + int new_mode = modes[active_mode].value; + int new_speed = 0; + int new_direction = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_RANDOM) + { + switch(new_mode) + { + case XTREEM_MODE_CHASE: + new_mode = XTREEM_MODE_SPECTRUM_CYCLE_CHASE; + break; + case XTREEM_MODE_BREATHING: + new_mode = XTREEM_MODE_SPECTRUM_CYCLE_BREATHING; + break; + case XTREEM_MODE_CHASE_FADE: + new_mode = XTREEM_MODE_SPECTRUM_CYCLE_CHASE_FADE; + break; + } + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + new_speed = modes[active_mode].speed; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + switch(modes[active_mode].direction) + { + case MODE_DIRECTION_LEFT: + new_direction = XTREEM_DIRECTION_REVERSE; + break; + + case MODE_DIRECTION_RIGHT: + new_direction = XTREEM_DIRECTION_FORWARD; + break; + } + } + + controller->SetMode(new_mode, new_speed, new_direction); + controller->SetDirect(false); + } +} diff --git a/Controllers/TForceXtreemController/RGBController_TForceXtreem.h b/Controllers/TForceXtreemController/RGBController_TForceXtreem.h new file mode 100644 index 0000000..b8c359d --- /dev/null +++ b/Controllers/TForceXtreemController/RGBController_TForceXtreem.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| RGBController_TForceXtreem.h | +| | +| RGBController for TeamGroup T-Force Xtreem RAM | +| | +| Milan Cermak (krysmanta) 28 Dec 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "TForceXtreemController.h" + +class RGBController_TForceXtreem : public RGBController +{ +public: + RGBController_TForceXtreem(TForceXtreemController* controller_ptr); + ~RGBController_TForceXtreem(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + TForceXtreemController* controller; + + int GetDeviceMode(); +}; diff --git a/Controllers/TForceXtreemController/TForceXtreemController.cpp b/Controllers/TForceXtreemController/TForceXtreemController.cpp new file mode 100644 index 0000000..113f1eb --- /dev/null +++ b/Controllers/TForceXtreemController/TForceXtreemController.cpp @@ -0,0 +1,183 @@ +/*---------------------------------------------------------*\ +| TForceXtreemController.cpp | +| | +| Driver for T-Force XTreem DRAM | +| | +| Milan Cermak (krysmanta) 28 Dec 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "TForceXtreemController.h" +#include "LogManager.h" + +TForceXtreemController::TForceXtreemController(i2c_smbus_interface *bus, ene_dev_id dev) +{ + this->bus = bus; + this->dev = dev; +} + +TForceXtreemController::~TForceXtreemController() +{ +} + +std::string TForceXtreemController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + + return(return_string); +} + +unsigned int TForceXtreemController::GetLEDCount() +{ + return(XTREEM_LED_COUNT); +} + +/*---------------------------------------------------*\ +| LEDs are in a single strip that is folded in half. | +| That makes the LED order: 0-14-1-13-2-...-7-9-8 | +\*---------------------------------------------------*/ +#define XTREEM_LED_OFFSET(x) ((((x) & 0x01) > 0) ? XTREEM_LED_COUNT - 1 - ((x) >> 1) : ((x) >> 1)) + +unsigned char TForceXtreemController::GetLEDRed(unsigned int led) +{ + return(ENERegisterRead(XTREEM_REG_COLORS_DIRECT + ( 3 * XTREEM_LED_OFFSET(led) ))); +} + +unsigned char TForceXtreemController::GetLEDGreen(unsigned int led) +{ + return(ENERegisterRead(XTREEM_REG_COLORS_DIRECT + ( 3 * XTREEM_LED_OFFSET(led) ) + 2)); +} + +unsigned char TForceXtreemController::GetLEDBlue(unsigned int led) +{ + return(ENERegisterRead(XTREEM_REG_COLORS_DIRECT + ( 3 * XTREEM_LED_OFFSET(led) ) + 1)); +} + +unsigned char TForceXtreemController::GetLEDRedEffect(unsigned int led) +{ + return(ENERegisterRead(XTREEM_REG_COLORS_EFFECT + ( 3 * XTREEM_LED_OFFSET(led) ))); +} + +unsigned char TForceXtreemController::GetLEDGreenEffect(unsigned int led) +{ + return(ENERegisterRead(XTREEM_REG_COLORS_EFFECT + ( 3 * XTREEM_LED_OFFSET(led) ) + 2)); +} + +unsigned char TForceXtreemController::GetLEDBlueEffect(unsigned int led) +{ + return(ENERegisterRead(XTREEM_REG_COLORS_EFFECT + ( 3 * XTREEM_LED_OFFSET(led) ) + 1)); +} + +void TForceXtreemController::SetAllColorsDirect(RGBColor* colors) +{ + unsigned char* color_buf = new unsigned char[XTREEM_LED_COUNT * 3]; + unsigned int bytes_sent = 0; + + for(unsigned int i = 0; i < XTREEM_LED_COUNT; i++) + { + unsigned int offset = 3 * XTREEM_LED_OFFSET(i); + color_buf[offset + 0] = RGBGetRValue(colors[i]); + color_buf[offset + 1] = RGBGetBValue(colors[i]); + color_buf[offset + 2] = RGBGetGValue(colors[i]); + } + + while(bytes_sent < (XTREEM_LED_COUNT * 3)) + { + ENERegisterWriteBlock(XTREEM_REG_COLORS_DIRECT + bytes_sent, &color_buf[bytes_sent], 3); + + bytes_sent += 3; + } + + delete[] color_buf; +} + +void TForceXtreemController::SetAllColorsEffect(RGBColor* colors) +{ + unsigned char* color_buf = new unsigned char[XTREEM_LED_COUNT * 3]; + unsigned int bytes_sent = 0; + + for(unsigned int i = 0; i < XTREEM_LED_COUNT; i++) + { + unsigned int offset = 3 * XTREEM_LED_OFFSET(i); + color_buf[offset + 0] = RGBGetRValue(colors[i]); + color_buf[offset + 1] = RGBGetBValue(colors[i]); + color_buf[offset + 2] = RGBGetGValue(colors[i]); + } + + while(bytes_sent < (XTREEM_LED_COUNT * 3)) + { + ENERegisterWriteBlock(XTREEM_REG_COLORS_EFFECT + bytes_sent, &color_buf[bytes_sent], 3); + + bytes_sent += 3; + } + + ENERegisterWrite(XTREEM_REG_APPLY, XTREEM_APPLY_VAL); + + delete[] color_buf; +} + + +void TForceXtreemController::SetDirect(unsigned char direct) +{ + ENERegisterWrite(XTREEM_REG_DIRECT, direct); + ENERegisterWrite(XTREEM_REG_APPLY, XTREEM_APPLY_VAL); +} + +void TForceXtreemController::SetLEDColorDirect(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char colors[3] = { red, blue, green }; + + ENERegisterWriteBlock(XTREEM_REG_COLORS_DIRECT + ( 3 * XTREEM_LED_OFFSET(led) ), colors, 3); +} + +void TForceXtreemController::SetLEDColorEffect(unsigned int led, unsigned char red, unsigned char green, unsigned char blue) +{ + unsigned char colors[3] = { red, blue, green }; + + ENERegisterWriteBlock(XTREEM_REG_COLORS_EFFECT + (3 * XTREEM_LED_OFFSET(led)), colors, 3); + + ENERegisterWrite(XTREEM_REG_APPLY, XTREEM_APPLY_VAL); +} + +void TForceXtreemController::SetMode(unsigned char mode, unsigned char speed, unsigned char direction) +{ + ENERegisterWrite(XTREEM_REG_MODE, mode); + ENERegisterWrite(XTREEM_REG_SPEED, speed); + ENERegisterWrite(XTREEM_REG_DIRECTION, direction); + ENERegisterWrite(XTREEM_REG_APPLY, XTREEM_APPLY_VAL); +} + +unsigned char TForceXtreemController::ENERegisterRead(ene_register reg) +{ + //Write ENE register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Read ENE value + return(bus->i2c_smbus_read_byte_data(dev, 0x81)); +} + +void TForceXtreemController::ENERegisterWrite(ene_register reg, unsigned char val) +{ + //Write ENE register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write ENE value + bus->i2c_smbus_write_byte_data(dev, 0x01, val); +} + +void TForceXtreemController::ENERegisterWriteBlock(ene_register reg, unsigned char * data, unsigned char sz) +{ + //Write ENE register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write ENE block data + bus->i2c_smbus_write_block_data(dev, 0x03, sz, data); +} diff --git a/Controllers/TForceXtreemController/TForceXtreemController.h b/Controllers/TForceXtreemController/TForceXtreemController.h new file mode 100644 index 0000000..66f9e58 --- /dev/null +++ b/Controllers/TForceXtreemController/TForceXtreemController.h @@ -0,0 +1,111 @@ +/*---------------------------------------------------------*\ +| TForceXtreemController.h | +| | +| Driver for T-Force Xtreem DRAM | +| | +| Milan Cermak (krysmanta) 28 Dec 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "i2c_smbus.h" + +#define XTREEM_APPLY_VAL 0x01 /* Value for Apply Changes Register */ +#define XTREEM_LED_COUNT 15 + +typedef unsigned short ene_register; +typedef unsigned char ene_dev_id; + +enum +{ + XTREEM_REG_DIRECT = 0xE020, /* "Direct Access" Selection Register */ + XTREEM_REG_MODE = 0xE021, /* Mode Selection Register */ + XTREEM_REG_SPEED = 0xE022, /* Speed Control Register */ + XTREEM_REG_DIRECTION = 0xE023, /* Direction Control Register */ + XTREEM_REG_APPLY = 0xE02F, /* Apply Changes Register */ + XTREEM_REG_SLOT_INDEX = 0xE0F8, /* Slot Index Register (RAM only) */ + XTREEM_REG_I2C_ADDRESS = 0xE0F9, /* I2C Address Register (RAM only) */ + XTREEM_REG_COLORS_DIRECT = 0xE100, /* Colors for Direct Mode 45 bytes */ + XTREEM_REG_COLORS_EFFECT = 0xE300, /* Colors for Internal Effects 45 bytes */ +}; + +enum +{ + XTREEM_MODE_OFF = 0, /* OFF mode */ + XTREEM_MODE_STATIC = 1, /* Static color mode */ + XTREEM_MODE_BREATHING = 2, /* Breathing effect mode */ + XTREEM_MODE_FLASHING = 3, /* Flashing effect mode */ + XTREEM_MODE_SPECTRUM_CYCLE = 4, /* Spectrum Cycle mode */ + XTREEM_MODE_RAINBOW = 5, /* Rainbow effect mode */ + XTREEM_MODE_SPECTRUM_CYCLE_BREATHING = 6, /* Rainbow Breathing effect mode */ + XTREEM_MODE_CHASE_FADE = 7, /* Chase with Fade effect mode */ + XTREEM_MODE_SPECTRUM_CYCLE_CHASE_FADE = 8, /* Chase with Fade, Rainbow effect mode */ + XTREEM_MODE_CHASE = 9, /* Chase effect mode */ + XTREEM_MODE_SPECTRUM_CYCLE_CHASE = 10, /* Chase with Rainbow effect mode */ + XTREEM_MODE_SPECTRUM_CYCLE_WAVE = 11, /* Wave effect mode */ + XTREEM_MODE_CHASE_RAINBOW_PULSE = 12, /* Chase with Rainbow Pulse effect mode*/ + XTREEM_MODE_RANDOM_FLICKER = 13, /* Random flicker effect mode */ + XTREEM_MODE_STACK = 14, /* Stacking effect mode */ + XTREEM_MODE_PONG = 15, /* Pong effect mode */ + XTREEM_MODE_FILLUP = 16, /* Fill up effect mode */ + XTREEM_MODE_NEON = 17, /* Neon effect mode */ + XTREEM_MODE_COLOR_WAVE = 18, /* Color Wave effect mode */ + XTREEM_MODE_COLOR_DOUBLE_WAVE = 19, /* Color double wave effect mode */ + XTREEM_MODE_MIXER = 20, /* Mixer effect mode */ + XTREEM_MODE_SPECTRUM_CYCLE_2 = 21, /* Spectrum cycle 2 effect mode */ + XTREEM_MODE_FIRE_BREATHING = 22, /* Color shift breathing effect mode */ + XTREEM_MODE_SPECTRUM_CYCLE_3 = 23, /* Spectrum cycle 3 effect mode */ + XTREEM_MODE_SLITHER = 24, /* Slither effect mode */ + XTREEM_MODE_TFORCE_XTREEM = 25, /* Default T-Force Xtreem mode */ + XTREEM_NUMBER_MODES /* Number of Aura modes */ +}; + +enum +{ + XTREEM_SPEED_SLOWEST = 0x04, /* Slowest effect speed */ + XTREEM_SPEED_SLOW = 0x03, /* Slow effect speed */ + XTREEM_SPEED_NORMAL = 0x02, /* Normal effect speed */ + XTREEM_SPEED_FAST = 0x01, /* Fast effect speed */ + XTREEM_SPEED_FASTEST = 0x00, /* Fastest effect speed */ +}; + +enum +{ + XTREEM_DIRECTION_FORWARD = 0x0, /* Forward effect direction */ + XTREEM_DIRECTION_REVERSE = 0x1, /* Reverse effect direction */ +}; + +class TForceXtreemController +{ +public: + TForceXtreemController(i2c_smbus_interface *bus, ene_dev_id dev); + ~TForceXtreemController(); + + std::string GetDeviceLocation(); + unsigned int GetLEDCount(); + unsigned char GetLEDRed(unsigned int led); + unsigned char GetLEDGreen(unsigned int led); + unsigned char GetLEDBlue(unsigned int led); + unsigned char GetLEDRedEffect(unsigned int led); + unsigned char GetLEDGreenEffect(unsigned int led); + unsigned char GetLEDBlueEffect(unsigned int led); + void SetAllColorsDirect(RGBColor* colors); + void SetAllColorsEffect(RGBColor* colors); + void SetDirect(unsigned char direct); + void SetLEDColorDirect(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void SetLEDColorEffect(unsigned int led, unsigned char red, unsigned char green, unsigned char blue); + void SetMode(unsigned char mode, unsigned char speed, unsigned char direction); + + unsigned char ENERegisterRead(ene_register reg); + void ENERegisterWrite(ene_register reg, unsigned char val); + void ENERegisterWriteBlock(ene_register reg, unsigned char * data, unsigned char sz); + +private: + i2c_smbus_interface * bus; + ene_dev_id dev; +}; diff --git a/Controllers/TForceXtreemController/TForceXtreemControllerDetect.cpp b/Controllers/TForceXtreemController/TForceXtreemControllerDetect.cpp new file mode 100644 index 0000000..25972e9 --- /dev/null +++ b/Controllers/TForceXtreemController/TForceXtreemControllerDetect.cpp @@ -0,0 +1,184 @@ +/*---------------------------------------------------------*\ +| TForceXtreemControllerDetect.cpp | +| | +| Detector for T-Force Xtreem RAM | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "TForceXtreemController.h" +#include "LogManager.h" +#include "RGBController_TForceXtreem.h" +#include "i2c_smbus.h" + +#define DETECTOR_NAME "TForce Xtreem Controller" + +using namespace std::chrono_literals; + +/*----------------------------------------------------------------------*\ +| Windows defines "interface" for some reason. Work around this | +\*----------------------------------------------------------------------*/ +#ifdef interface +#undef interface +#endif + +/*----------------------------------------------------------------------*\ +| This list contains the available SMBus addresses for mapping ENE RAM | +\*----------------------------------------------------------------------*/ +#define XTREEM_RAM_ADDRESS_COUNT 13 + +static const unsigned char xtreem_ram_addresses[] = +{ + 0x70, + 0x71, + 0x72, + 0x73, + 0x74, + 0x75, + 0x76, + 0x78, + 0x39, + 0x3A, + 0x3B, + 0x3C, + 0x3D +}; + +/******************************************************************************************\ +* * +* XtreemRegisterWrite * +* * +* A standalone version of the TForceXtreemController::ENERegisterWrite function for * +* access to ENE devices without instancing the TForceXtreemController class. * +* * +\******************************************************************************************/ + +static void XtreemRegisterWrite(i2c_smbus_interface* bus, ene_dev_id dev, ene_register reg, unsigned char val) +{ + //Write ENE register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write ENE value + bus->i2c_smbus_write_byte_data(dev, 0x01, val); +} + +/******************************************************************************************\ +* * +* TestForENESMBusController * +* * +* Tests the given address to see if an ENE controller exists there. First does a * +* quick write to test for a response, and if so does a simple read at 0x90 to test * +* for incrementing values 10...1F which was observed at this location * +* * +\******************************************************************************************/ + +bool TestForTForceXtreemController(i2c_smbus_interface* bus, unsigned char address) +{ + bool pass = false; + + LOG_DEBUG("[%s] looking for devices at 0x%02X...", DETECTOR_NAME, address); + + int res = bus->i2c_smbus_read_byte(address); + + if(res < 0) + { + res = bus->i2c_smbus_read_byte_data(address, 0x00); + } + + if(res >= 0) + { + pass = true; + + LOG_DEBUG("[%s] Detected an I2C device at address %02X, testing register range", DETECTOR_NAME, address); + + for(int i = 0x90; i < 0xA1; i++) + { + res = bus->i2c_smbus_read_byte_data(address, i); + + if(res != (i - 0x80)) + { + LOG_VERBOSE("[%s] Detection failed testing register %02X. Expected %02X, got %02X.", DETECTOR_NAME, i, (i - 0x80), res); + + pass = false; + break; + } + } + } + + return(pass); + +} /* TestForTForceXtreemController() */ + +/******************************************************************************************\ +* * +* DetectTForceXtreemDRAMControllers * +* * +* Detects T-Force Xtreem controllers on DRAM devices * +* * +* bus - pointer to i2c_smbus_interface where device is connected * +* slots - SPD accessors to occupied slots * +* * +\******************************************************************************************/ + +void DetectTForceXtreemControllers(i2c_smbus_interface* bus, std::vector &slots, const std::string &/*name*/) +{ + + LOG_DEBUG("[%s] Remapping ENE SMBus RAM modules on 0x77", DETECTOR_NAME); + + for(SPDWrapper *slot : slots) + { + int address_list_idx = slot->index() - 1; + int res; + + /*-------------------------------------------------*\ + | Full test to avoid conflicts with other ENE DRAMs | + \*-------------------------------------------------*/ + if(!TestForTForceXtreemController(bus, 0x77)) + { + LOG_DEBUG("[%s] No device detected at 0x77, aborting remap", DETECTOR_NAME); + + break; + } + + do + { + address_list_idx++; + + if(address_list_idx < XTREEM_RAM_ADDRESS_COUNT) + { + LOG_DEBUG("[%s] Testing address %02X to see if there is a device there", DETECTOR_NAME, xtreem_ram_addresses[address_list_idx]); + + res = bus->i2c_smbus_write_quick(xtreem_ram_addresses[address_list_idx], I2C_SMBUS_WRITE); + } + else + { + break; + } + } while(res >= 0); + + if(address_list_idx < XTREEM_RAM_ADDRESS_COUNT) + { + LOG_DEBUG("[%s] Remapping slot %d to address %02X", DETECTOR_NAME, slot, xtreem_ram_addresses[address_list_idx]); + + XtreemRegisterWrite(bus, 0x77, XTREEM_REG_SLOT_INDEX, slot->index()); + XtreemRegisterWrite(bus, 0x77, XTREEM_REG_I2C_ADDRESS, (xtreem_ram_addresses[address_list_idx] << 1)); + } + } + + // Add ENE controllers at their remapped addresses + for(unsigned int address_list_idx = 0; address_list_idx < XTREEM_RAM_ADDRESS_COUNT; address_list_idx++) + { + if(TestForTForceXtreemController(bus, xtreem_ram_addresses[address_list_idx])) + { + TForceXtreemController* controller = new TForceXtreemController(bus, xtreem_ram_addresses[address_list_idx]); + RGBController_TForceXtreem* rgb_controller = new RGBController_TForceXtreem(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } +} /* DetectTForceXtreemControllers() */ + +REGISTER_I2C_DIMM_DETECTOR("T-Force Xtreem DDR4 DRAM", DetectTForceXtreemControllers, JEDEC_TEAMGROUP, SPD_DDR4_SDRAM); diff --git a/Controllers/TecknetController/RGBController_Tecknet.cpp b/Controllers/TecknetController/RGBController_Tecknet.cpp new file mode 100644 index 0000000..8ef07ff --- /dev/null +++ b/Controllers/TecknetController/RGBController_Tecknet.cpp @@ -0,0 +1,126 @@ +/*---------------------------------------------------------*\ +| RGBController_Tecknet.cpp | +| | +| RGBController for Tecknet devices | +| | +| Chris M (Dr_No) 29 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Tecknet.h" + +/**------------------------------------------------------------------*\ + @name Tecknet Mouse + @category Mouse + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectTecknetControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Tecknet::RGBController_Tecknet(TecknetController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Tecknet"; + type = DEVICE_TYPE_MOUSE; + description = controller->GetDeviceName(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = TECKNET_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.speed_min = TECKNET_SPEED_OFF; + Direct.speed_max = TECKNET_SPEED_OFF; + Direct.speed = TECKNET_SPEED_OFF; + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.value = TECKNET_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + Off.speed_min = TECKNET_SPEED_OFF; + Off.speed_max = TECKNET_SPEED_OFF; + Off.speed = TECKNET_SPEED_OFF; + modes.push_back(Off); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = TECKNET_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.speed_min = TECKNET_SPEED_SLOW; + Breathing.speed_max = TECKNET_SPEED_FAST; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed = TECKNET_SPEED_NORMAL; + modes.push_back(Breathing); + + SetupZones(); +} + +RGBController_Tecknet::~RGBController_Tecknet() +{ + delete controller; +} + +void RGBController_Tecknet::SetupZones() +{ + zone Tecknet_zone; + Tecknet_zone.name = "Logo"; + Tecknet_zone.type = ZONE_TYPE_SINGLE; + Tecknet_zone.leds_min = 1; + Tecknet_zone.leds_max = 1; + Tecknet_zone.leds_count = 1; + Tecknet_zone.matrix_map = NULL; + zones.push_back(Tecknet_zone); + + led Tecknet_led; + Tecknet_led.name = "Logo"; + leds.push_back(Tecknet_led); + + SetupColors(); +} + +void RGBController_Tecknet::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | Not implemented for this device | + \*---------------------------------------------------------*/ +} + +void RGBController_Tecknet::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + controller->SetColor(red, grn, blu); +} + +void RGBController_Tecknet::UpdateZoneLEDs(int zone) +{ + RGBColor color = colors[zone]; + unsigned char red = RGBGetRValue(color); + unsigned char grn = RGBGetGValue(color); + unsigned char blu = RGBGetBValue(color); + controller->SetColor(red, grn, blu); +} + +void RGBController_Tecknet::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_Tecknet::DeviceUpdateMode() +{ + //If active_mode is "Off" then set brightness to off otherwise high + unsigned char brightness = (active_mode == TECKNET_MODE_OFF) ? TECKNET_BRIGHTNESS_OFF : TECKNET_BRIGHTNESS_HIGH; + + controller->SetMode(modes[active_mode].value, modes[active_mode].speed, brightness); +} diff --git a/Controllers/TecknetController/RGBController_Tecknet.h b/Controllers/TecknetController/RGBController_Tecknet.h new file mode 100644 index 0000000..2a2074d --- /dev/null +++ b/Controllers/TecknetController/RGBController_Tecknet.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_Tecknet.h | +| | +| RGBController for Tecknet devices | +| | +| Chris M (Dr_No) 29 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "TecknetController.h" + +class RGBController_Tecknet : public RGBController +{ +public: + RGBController_Tecknet(TecknetController* controller_ptr); + ~RGBController_Tecknet(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + TecknetController* controller; +}; diff --git a/Controllers/TecknetController/TecknetController.cpp b/Controllers/TecknetController/TecknetController.cpp new file mode 100644 index 0000000..fce3c43 --- /dev/null +++ b/Controllers/TecknetController/TecknetController.cpp @@ -0,0 +1,114 @@ +/*---------------------------------------------------------*\ +| TecknetController.cpp | +| | +| Driver for Tecknet devices | +| | +| Chris M (Dr_No) 29 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "StringUtils.h" +#include "TecknetController.h" + +static unsigned char tecknet_colour_mode_data[][16] = +{ + { 0x02, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00 }, // Direct + { 0x02, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00 }, // Breathing +}; + +static unsigned char tecknet_speed_mode_data[][9] = +{ + { 0x00, 0x00, 0x00, 0x00 }, // Direct + { 0x00, 0x06, 0x03, 0x01 }, // Breathing +}; + +TecknetController::TecknetController(hid_device* dev_handle, char *_path) +{ + dev = dev_handle; + location = _path; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + + current_mode = TECKNET_MODE_DIRECT; + current_speed = TECKNET_SPEED_NORMAL; + current_brightness = TECKNET_BRIGHTNESS_HIGH; +} + +TecknetController::~TecknetController() +{ + hid_close(dev); +} + +std::string TecknetController::GetDeviceName() +{ + return device_name; +} + +std::string TecknetController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string TecknetController::GetLocation() +{ + return("HID: " + location); +} + +void TecknetController::SetMode(unsigned char mode, unsigned char speed, unsigned char brightness) +{ + current_mode = mode; + current_speed = speed; + current_brightness = brightness; + + SendUpdate(); +} + +void TecknetController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + //The Tecknet mouse expects inverted colours in sent packets + current_red = 255 - red; + current_green = 255 - green; + current_blue = 255 - blue; + + SendUpdate(); +} + +void TecknetController::SendUpdate() +{ + unsigned char buffer[TECKNET_PACKET_LENGTH] = { 0x00 }; + int buffer_size = (sizeof(buffer) / sizeof(buffer[0])); + + for(std::size_t i = 0; i < TECKNET_COLOUR_MODE_DATA_SIZE; i++) + { + buffer[i] = tecknet_colour_mode_data[current_mode][i]; + } + + //Set the relevant colour info + buffer[TECKNET_RED_BYTE] = current_red; + buffer[TECKNET_GREEN_BYTE] = current_green; + buffer[TECKNET_BLUE_BYTE] = current_blue; + buffer[TECKNET_BRIGHTNESS_BYTE] = current_brightness; + buffer[TECKNET_SPEED_BYTE] = tecknet_speed_mode_data[current_mode][current_speed]; + + hid_send_feature_report(dev, buffer, buffer_size); +} + diff --git a/Controllers/TecknetController/TecknetController.h b/Controllers/TecknetController/TecknetController.h new file mode 100644 index 0000000..388de87 --- /dev/null +++ b/Controllers/TecknetController/TecknetController.h @@ -0,0 +1,81 @@ +/*---------------------------------------------------------*\ +| TecknetController.h | +| | +| Driver for Tecknet devices | +| | +| Chris M (Dr_No) 29 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include + +#define HID_MAX_STR 255 +#define TECKNET_COLOUR_MODE_DATA_SIZE (sizeof(tecknet_colour_mode_data[0]) / sizeof(tecknet_colour_mode_data[0][0])) +#define TECKNET_DEVICE_NAME_SIZE (sizeof(device_name) / sizeof(device_name[ 0 ])) +#define TECKNET_PACKET_LENGTH 0x10 //16 bytes + +enum +{ + TECKNET_RED_BYTE = 2, + TECKNET_GREEN_BYTE = 3, + TECKNET_BLUE_BYTE = 4, + TECKNET_BRIGHTNESS_BYTE = 5, + TECKNET_SPEED_BYTE = 6 +}; + +enum +{ + TECKNET_MODE_OFF = 0xFF, //LEDs Off + TECKNET_MODE_DIRECT = 0x00, //Direct Mode + TECKNET_MODE_BREATHING = 0x01, //Breathing Mode +}; + +enum +{ + TECKNET_BRIGHTNESS_OFF = 0x00, + TECKNET_BRIGHTNESS_LOW = 0x01, + TECKNET_BRIGHTNESS_MED = 0x02, + TECKNET_BRIGHTNESS_HIGH = 0x03 +}; + +enum +{ + TECKNET_SPEED_OFF = 0x00, // Breathe Off + TECKNET_SPEED_SLOW = 0x01, // Breathe Slow speed + TECKNET_SPEED_NORMAL = 0x02, // Breathe Normal speed + TECKNET_SPEED_FAST = 0x03, // Breathe Fast speed +}; + +class TecknetController +{ +public: + TecknetController(hid_device *dev_handle, char *_path); + ~TecknetController(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + void SetMode(unsigned char mode, unsigned char speed, unsigned char brightness); + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + +private: + std::string device_name; + std::string location; + hid_device* dev; + + unsigned char current_mode; + unsigned char current_speed; + unsigned char current_brightness; + + unsigned char current_red; + unsigned char current_green; + unsigned char current_blue; + + void SendUpdate(); +}; diff --git a/Controllers/TecknetController/TecknetControllerDetect.cpp b/Controllers/TecknetController/TecknetControllerDetect.cpp new file mode 100644 index 0000000..f87d542 --- /dev/null +++ b/Controllers/TecknetController/TecknetControllerDetect.cpp @@ -0,0 +1,47 @@ +/*---------------------------------------------------------*\ +| TecknetControllerDetect.cpp | +| | +| Detector for Tecknet devices | +| | +| Chris M (Dr_No) 29 Jul 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "TecknetController.h" +#include "RGBController_Tecknet.h" + +#define TECKNET_VID 0x04D9 + +#define TECKNET_M0008_PID 0xFC05 +#define TECKNET_M0008_U 0x01 //Usage 01 +#define TECKNET_M0008_UPG 0xFFA0 //Vendor Defined Usage Page + +/******************************************************************************************\ +* * +* DetectTecknetControllers * +* * +* Tests the USB address to see if any Tecknet Controllers. * +* * +\******************************************************************************************/ + +void DetectTecknetControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + if(dev) + { + TecknetController* controller = new TecknetController(dev, info->path); + RGBController_Tecknet* rgb_controller = new RGBController_Tecknet(controller); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectTecknetControllers) */ + +#ifdef USE_HID_USAGE +REGISTER_HID_DETECTOR_PU("Tecknet M008", DetectTecknetControllers, TECKNET_VID, TECKNET_M0008_PID, TECKNET_M0008_UPG, TECKNET_M0008_U); +#else +REGISTER_HID_DETECTOR_I("Tecknet M008", DetectTecknetControllers, TECKNET_VID, TECKNET_M0008_PID, 0); +#endif diff --git a/Controllers/ThermaltakePoseidonZRGBController/RGBController_ThermaltakePoseidonZRGB.cpp b/Controllers/ThermaltakePoseidonZRGBController/RGBController_ThermaltakePoseidonZRGB.cpp new file mode 100644 index 0000000..7bac8f8 --- /dev/null +++ b/Controllers/ThermaltakePoseidonZRGBController/RGBController_ThermaltakePoseidonZRGB.cpp @@ -0,0 +1,294 @@ +/*---------------------------------------------------------*\ +| RGBController_ThermaltakePoseidonZRGB.cpp | +| | +| RGBController for Thermaltake Poseidon Z RGB | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_ThermaltakePoseidonZRGB.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[6][23] = + { { 0, NA, 8, 15, 22, 29, NA, 37, 44, 51, 58, NA, 65, 73, 81, 88, 94, 100, 102, NA, NA, NA, NA }, + { 1, 9, 16, 23, 30, 38, 45, 52, 59, 66, 74, NA, 82, 89, 103, NA, 7, 21, 36, 50, 64, 80, 93 }, + { 2, NA, 10, 17, 24, 31, NA, 39, 46, 53, 60, 67, 75, 83, 90, 95, 14, 28, 43, 57, 72, 87, 86 }, + { 3, NA, 11, 18, 25, 32, NA, 40, 47, 54, 61, 68, 76, 84, 96, NA, NA, NA, NA, 35, 99, 63, NA }, + { 4, NA, 26, 33, 41, 48, NA, 55, NA, 62, 69, 77, 85, 91, 101, NA, NA, 27, NA, 42, 49, 71, 98 }, + { 5, 12, 19, NA, NA, NA, NA, 34, NA, NA, NA, NA, 70, 78, 92, 97, 6, 13, 20, 56, NA, 79, NA } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 104 +}; + +static const char* led_names[] = +{ + KEY_EN_ESCAPE, + KEY_EN_BACK_TICK, + KEY_EN_TAB, + KEY_EN_CAPS_LOCK, + KEY_EN_LEFT_SHIFT, + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_INSERT, + KEY_EN_F1, + KEY_EN_1, + KEY_EN_Q, + KEY_EN_A, + KEY_EN_LEFT_WINDOWS, + KEY_EN_DOWN_ARROW, + KEY_EN_DELETE, + KEY_EN_F2, + KEY_EN_2, + KEY_EN_W, + KEY_EN_S, + KEY_EN_LEFT_ALT, + KEY_EN_RIGHT_ARROW, + KEY_EN_HOME, + KEY_EN_F3, + KEY_EN_3, + KEY_EN_E, + KEY_EN_D, + KEY_EN_Z, + KEY_EN_UP_ARROW, + KEY_EN_END, + KEY_EN_F4, + KEY_EN_4, + KEY_EN_R, + KEY_EN_F, + KEY_EN_X, + KEY_EN_SPACE, + KEY_EN_NUMPAD_4, + KEY_EN_PAGE_UP, + KEY_EN_F5, + KEY_EN_5, + KEY_EN_T, + KEY_EN_G, + KEY_EN_C, + KEY_EN_NUMPAD_1, + KEY_EN_PAGE_DOWN, + KEY_EN_F6, + KEY_EN_6, + KEY_EN_Y, + KEY_EN_H, + KEY_EN_V, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_LOCK, + KEY_EN_F7, + KEY_EN_7, + KEY_EN_U, + KEY_EN_J, + KEY_EN_B, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_7, + KEY_EN_F8, + KEY_EN_8, + KEY_EN_I, + KEY_EN_K, + KEY_EN_N, + KEY_EN_NUMPAD_6, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_F9, + KEY_EN_9, + KEY_EN_O, + KEY_EN_L, + KEY_EN_M, + KEY_EN_RIGHT_ALT, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_8, + KEY_EN_F10, + KEY_EN_0, + KEY_EN_P, + KEY_EN_SEMICOLON, + KEY_EN_COMMA, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_NUMPAD_PERIOD, + KEY_EN_NUMPAD_TIMES, + KEY_EN_F11, + KEY_EN_MINUS, + KEY_EN_LEFT_BRACKET, + KEY_EN_QUOTE, + KEY_EN_PERIOD, + KEY_EN_NUMPAD_PLUS, + KEY_EN_NUMPAD_9, + KEY_EN_F12, + KEY_EN_EQUALS, + KEY_EN_RIGHT_BRACKET, + KEY_EN_FORWARD_SLASH, + KEY_EN_MENU, + KEY_EN_NUMPAD_MINUS, + KEY_EN_PRINT_SCREEN, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_ANSI_ENTER, + KEY_EN_RIGHT_CONTROL, + KEY_EN_NUMPAD_ENTER, + KEY_EN_NUMPAD_5, + KEY_EN_SCROLL_LOCK, + KEY_EN_RIGHT_SHIFT, + KEY_EN_PAUSE_BREAK, + KEY_EN_BACKSPACE +}; + +/**------------------------------------------------------------------*\ + @name Thermaltake PoseidonZ + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectPoseidonZRGBControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_PoseidonZRGB::RGBController_PoseidonZRGB(PoseidonZRGBController* controller_ptr) +{ + controller = controller_ptr; + + name = "Thermaltake Poseidon Z RGB"; + vendor = "Thermaltake"; + type = DEVICE_TYPE_KEYBOARD; + description = "Thermaltake Poseidon Z RGB Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = POSEIDONZ_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = POSEIDONZ_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Wave; + Wave.name = "Wave"; + Wave.value = POSEIDONZ_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_AUTOMATIC_SAVE; + Wave.speed_min = POSEIDONZ_SPEED_SLOW; + Wave.speed_max = POSEIDONZ_SPEED_FAST; + Wave.color_mode = MODE_COLORS_NONE; + Wave.speed = POSEIDONZ_SPEED_FAST; + Wave.direction = MODE_DIRECTION_LEFT; + modes.push_back(Wave); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = POSEIDONZ_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_AUTOMATIC_SAVE; + Ripple.color_mode = MODE_COLORS_NONE; + modes.push_back(Ripple); + + mode Reactive; + Reactive.name = "Reactive"; + Reactive.value = POSEIDONZ_MODE_REACTIVE; + Reactive.flags = MODE_FLAG_AUTOMATIC_SAVE; + Reactive.color_mode = MODE_COLORS_NONE; + modes.push_back(Reactive); + + SetupZones(); +} + +RGBController_PoseidonZRGB::~RGBController_PoseidonZRGB() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_PoseidonZRGB::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 23; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_PoseidonZRGB::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_PoseidonZRGB::DeviceUpdateLEDs() +{ + if(active_mode == 0) + { + controller->SetLEDsDirect(colors); + } + else + { + controller->SetLEDs(colors); + } +} + +void RGBController_PoseidonZRGB::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PoseidonZRGB::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_PoseidonZRGB::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].value, modes[active_mode].direction, modes[active_mode].speed); +} diff --git a/Controllers/ThermaltakePoseidonZRGBController/RGBController_ThermaltakePoseidonZRGB.h b/Controllers/ThermaltakePoseidonZRGBController/RGBController_ThermaltakePoseidonZRGB.h new file mode 100644 index 0000000..ebe1c09 --- /dev/null +++ b/Controllers/ThermaltakePoseidonZRGBController/RGBController_ThermaltakePoseidonZRGB.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_ThermaltakePoseidonZRGB.h | +| | +| RGBController for Thermaltake Poseidon Z RGB | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ThermaltakePoseidonZRGBController.h" + +class RGBController_PoseidonZRGB : public RGBController +{ +public: + RGBController_PoseidonZRGB(PoseidonZRGBController* controller_ptr); + ~RGBController_PoseidonZRGB(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + PoseidonZRGBController* controller; +}; diff --git a/Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBController.cpp b/Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBController.cpp new file mode 100644 index 0000000..ea8dafb --- /dev/null +++ b/Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBController.cpp @@ -0,0 +1,253 @@ +/*---------------------------------------------------------*\ +| ThermaltakePoseidonZRGBController.cpp | +| | +| Driver for Thermaltake Poseidon Z RGB | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "ThermaltakePoseidonZRGBController.h" + +using namespace std::chrono_literals; + +static unsigned int keys[] = {0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x40, + 0x41, 0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, + 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x6C, 0x6D, 0x6F, 0x70, 0x72, 0x73, 0x75, 0x76, 0x77, 0x78, 0x7C, 0x80, 0x81 }; + +PoseidonZRGBController::PoseidonZRGBController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; +} + +PoseidonZRGBController::~PoseidonZRGBController() +{ + hid_close(dev); +} + +std::string PoseidonZRGBController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string PoseidonZRGBController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void PoseidonZRGBController::SetMode(unsigned char mode, unsigned char direction, unsigned char speed) +{ + active_mode = mode; + active_direction = direction; + active_speed = speed; + + SendControl + ( + POSEIDONZ_PROFILE_P1, + POSEIDONZ_PROFILE_P1, + active_direction, + active_mode, + POSEIDONZ_BRIGHTNESS_MAX, + active_speed + ); + + std::this_thread::sleep_for(200ms); +} + +void PoseidonZRGBController::SetLEDsDirect(std::vector colors) +{ + unsigned char red_grn_buf[264]; + unsigned char blu_buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffers | + \*-----------------------------------------------------*/ + memset(red_grn_buf, 0x00, sizeof(red_grn_buf)); + memset(blu_buf, 0x00, sizeof(blu_buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packets | + \*-----------------------------------------------------*/ + red_grn_buf[0] = 0x07; + red_grn_buf[1] = POSEIDONZ_PACKET_ID_SET_DIRECT; + red_grn_buf[2] = POSEIDONZ_PROFILE_P1; + red_grn_buf[3] = POSEIDONZ_DIRECT_RED_GREEN; + red_grn_buf[4] = 0x00; + red_grn_buf[5] = 0x00; + red_grn_buf[6] = 0x00; + red_grn_buf[7] = 0x00; + + blu_buf[0] = 0x07; + blu_buf[1] = POSEIDONZ_PACKET_ID_SET_DIRECT; + blu_buf[2] = POSEIDONZ_PROFILE_P1; + blu_buf[3] = POSEIDONZ_DIRECT_BLUE; + blu_buf[4] = 0x00; + blu_buf[5] = 0x00; + blu_buf[6] = 0x00; + blu_buf[7] = 0x00; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for(std::size_t i = 0; i < 104; i++) + { + red_grn_buf[keys[i] ] = RGBGetRValue(colors[i]); + red_grn_buf[keys[i] + 128] = RGBGetGValue(colors[i]); + blu_buf[ keys[i] ] = RGBGetBValue(colors[i]); + } + + /*-----------------------------------------------------*\ + | Send packets | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, red_grn_buf, 264); + + std::this_thread::sleep_for(5ms); + + hid_send_feature_report(dev, blu_buf, 264); +} + +void PoseidonZRGBController::SetLEDs(std::vector colors) +{ + unsigned char red_color_data[104]; + unsigned char grn_color_data[104]; + unsigned char blu_color_data[104]; + + for(std::size_t i = 0; i < 104; i++) + { + red_color_data[i] = RGBGetRValue(colors[i]); + grn_color_data[i] = RGBGetGValue(colors[i]); + blu_color_data[i] = RGBGetBValue(colors[i]); + } + + SendColor + ( + POSEIDONZ_PROFILE_P1, + POSEIDONZ_COLOR_RED, + red_color_data + ); + + std::this_thread::sleep_for(10ms); + + SendColor + ( + POSEIDONZ_PROFILE_P1, + POSEIDONZ_COLOR_GREEN, + grn_color_data + ); + + std::this_thread::sleep_for(10ms); + + SendColor + ( + POSEIDONZ_PROFILE_P1, + POSEIDONZ_COLOR_BLUE, + blu_color_data + ); + + std::this_thread::sleep_for(10ms); + + SendControl + ( + POSEIDONZ_PROFILE_P1, + POSEIDONZ_PROFILE_P1, + active_direction, + active_mode, + POSEIDONZ_BRIGHTNESS_MAX, + active_speed + ); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void PoseidonZRGBController::SendColor + ( + unsigned char profile_to_edit, + unsigned char color_channel, + unsigned char* color_data + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Color packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = POSEIDONZ_PACKET_ID_SET_COLOR; + buf[0x02] = profile_to_edit; + buf[0x03] = color_channel; + + /*-----------------------------------------------------*\ + | Fill in color data | + \*-----------------------------------------------------*/ + for(int i = 0; i < 104; i++) + { + buf[keys[i]] = color_data[i]; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} + +void PoseidonZRGBController::SendControl + ( + unsigned char profile_to_activate, + unsigned char profile_to_edit, + unsigned char direction, + unsigned char mode, + unsigned char brightness, + unsigned char speed + ) +{ + unsigned char buf[264]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + /*-----------------------------------------------------*\ + | Set up Effect packet | + \*-----------------------------------------------------*/ + buf[0x00] = 0x07; + buf[0x01] = POSEIDONZ_PACKET_ID_SET_EFFECT; + buf[0x02] = profile_to_activate; + buf[0x08] = profile_to_edit; + buf[0x0A] = direction; + buf[0x0C] = mode; + buf[0x0D] = brightness; + buf[0x10] = 0x08; + buf[0x12] = speed; + buf[0x13] = 0x50; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, buf, 264); +} diff --git a/Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBController.h b/Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBController.h new file mode 100644 index 0000000..211b563 --- /dev/null +++ b/Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBController.h @@ -0,0 +1,110 @@ +/*---------------------------------------------------------*\ +| ThermaltakePoseidonZRGBController.h | +| | +| Driver for Thermaltake Poseidon Z RGB | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define POSEIDONZ_START 0x07 +#define POSEIDONZ_PROFILE 0x01 +#define POSEIDONZ_LED_CMD 0x0E +#define POSEIDONZ_RED_GRN_CH 0x01 +#define POSEIDONZ_BLU_CH 0x02 + +enum +{ + POSEIDONZ_PACKET_ID_SET_EFFECT = 0x02, /* Set profile effect packet */ + POSEIDONZ_PACKET_ID_SET_COLOR = 0x09, /* Set profile color packet */ + POSEIDONZ_PACKET_ID_SET_DIRECT = 0x0E, /* Set direct color packet */ +}; + +enum +{ + POSEIDONZ_MODE_STATIC = 0x00, + POSEIDONZ_MODE_REACTIVE = 0x01, + POSEIDONZ_MODE_ARROW_FLOW = 0x02, + POSEIDONZ_MODE_WAVE = 0x03, + POSEIDONZ_MODE_RIPPLE = 0x04 +}; + +enum +{ + POSEIDONZ_PROFILE_P1 = 0x01, + POSEIDONZ_PROFILE_P2 = 0x02, + POSEIDONZ_PROFILE_P3 = 0x03, + POSEIDONZ_PROFILE_P4 = 0x04, + POSEIDONZ_PROFILE_P5 = 0x05 +}; + +enum +{ + POSEIDONZ_BRIGHTNESS_MIN = 0x00, + POSEIDONZ_BRIGHTNESS_MAX = 0x04 +}; + +enum +{ + POSEIDONZ_COLOR_RED = 0x01, + POSEIDONZ_COLOR_GREEN = 0x02, + POSEIDONZ_COLOR_BLUE = 0x03 +}; + +enum +{ + POSEIDONZ_DIRECT_RED_GREEN = 0x01, + POSEIDONZ_DIRECT_BLUE = 0x02 +}; + +enum +{ + POSEIDONZ_SPEED_SLOW = 0x10, + POSEIDONZ_SPEED_FAST = 0x05 +}; + +class PoseidonZRGBController +{ +public: + PoseidonZRGBController(hid_device* dev_handle, const char* path); + ~PoseidonZRGBController(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + + void SetMode(unsigned char mode, unsigned char direction, unsigned char speed); + void SetLEDsDirect(std::vector colors); + void SetLEDs(std::vector colors); + +private: + hid_device* dev; + unsigned char active_mode; + unsigned char active_direction; + unsigned char active_speed; + std::string location; + + void SendControl + ( + unsigned char profile_to_activate, + unsigned char profile_to_edit, + unsigned char direction, + unsigned char mode, + unsigned char brightness, + unsigned char speed + ); + + void SendColor + ( + unsigned char profile_to_edit, + unsigned char color_channel, + unsigned char* color_data + ); +}; diff --git a/Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBControllerDetect.cpp b/Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBControllerDetect.cpp new file mode 100644 index 0000000..c504827 --- /dev/null +++ b/Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBControllerDetect.cpp @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| ThermaltakePoseidonZRGBControllerDetect.cpp | +| | +| Detector for Thermaltake Poseidon Z RGB | +| | +| Adam Honse (CalcProgrammer1) 25 Dec 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "ThermaltakePoseidonZRGBController.h" +#include "RGBController_ThermaltakePoseidonZRGB.h" + +#define TT_POSEIDON_Z_RGB_VID 0x264A +#define TT_POSEIDON_Z_RGB_PID 0x3006 + +/******************************************************************************************\ +* * +* DetectPoseidonZRGBControllers * +* * +* Tests the USB address to see if a Thermaltake Poseidon Z RGB Keyboard controller * +* exists there. * +* * +\******************************************************************************************/ + +void DetectPoseidonZRGBControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + if( dev ) + { + PoseidonZRGBController* controller = new PoseidonZRGBController(dev, info->path); + RGBController_PoseidonZRGB* rgb_controller = new RGBController_PoseidonZRGB(controller); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectPoseidonZRGBControllers() */ + +REGISTER_HID_DETECTOR_IP("Thermaltake Poseidon Z RGB", DetectPoseidonZRGBControllers, TT_POSEIDON_Z_RGB_VID, TT_POSEIDON_Z_RGB_PID, 1, 0xFF01); diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/RGBController_ThermaltakeRiing.cpp b/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/RGBController_ThermaltakeRiing.cpp new file mode 100644 index 0000000..348a756 --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/RGBController_ThermaltakeRiing.cpp @@ -0,0 +1,234 @@ +/*---------------------------------------------------------*\ +| RGBController_ThermaltakeRiing.cpp | +| | +| RGBController for Thermaltake Riing | +| | +| Adam Honse (CalcProgrammer1) 09 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ThermaltakeRiing.h" + +/**------------------------------------------------------------------*\ + @name Thermaltake Riing + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectThermaltakeRiingControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_ThermaltakeRiing::RGBController_ThermaltakeRiing(ThermaltakeRiingController* controller_ptr) +{ + controller = controller_ptr; + + name = "Thermaltake Riing"; + vendor = "Thermaltake"; + type = DEVICE_TYPE_COOLER; + description = "Thermaltake Riing Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + version = controller->GetFirmwareVersion(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = THERMALTAKE_MODE_PER_LED; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.speed_min = 0; + Direct.speed_max = 0; + Direct.speed = 0; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = THERMALTAKE_MODE_FULL; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.speed_min = 0; + Static.speed_max = 0; + Static.speed = 0; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Flow; + Flow.name = "Flow"; + Flow.value = THERMALTAKE_MODE_FLOW; + Flow.flags = MODE_FLAG_HAS_SPEED; + Flow.speed_min = THERMALTAKE_SPEED_SLOW; + Flow.speed_max = THERMALTAKE_SPEED_EXTREME; + Flow.speed = THERMALTAKE_SPEED_NORMAL; + Flow.color_mode = MODE_COLORS_NONE; + modes.push_back(Flow); + + mode Spectrum; + Spectrum.name = "Spectrum"; + Spectrum.value = THERMALTAKE_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_SPEED; + Spectrum.speed_min = THERMALTAKE_SPEED_SLOW; + Spectrum.speed_max = THERMALTAKE_SPEED_EXTREME; + Spectrum.speed = THERMALTAKE_SPEED_NORMAL; + Spectrum.color_mode = MODE_COLORS_NONE; + modes.push_back(Spectrum); + + mode Ripple; + Ripple.name = "Ripple"; + Ripple.value = THERMALTAKE_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Ripple.speed_min = THERMALTAKE_SPEED_SLOW; + Ripple.speed_max = THERMALTAKE_SPEED_EXTREME; + Ripple.speed = THERMALTAKE_SPEED_NORMAL; + Ripple.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Ripple); + + mode Blink; + Blink.name = "Blink"; + Blink.value = THERMALTAKE_MODE_BLINK; + Blink.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Blink.speed_min = THERMALTAKE_SPEED_SLOW; + Blink.speed_max = THERMALTAKE_SPEED_EXTREME; + Blink.speed = THERMALTAKE_SPEED_NORMAL; + Blink.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Blink); + + mode Pulse; + Pulse.name = "Pulse"; + Pulse.value = THERMALTAKE_MODE_PULSE; + Pulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Pulse.speed_min = THERMALTAKE_SPEED_SLOW; + Pulse.speed_max = THERMALTAKE_SPEED_EXTREME; + Pulse.speed = THERMALTAKE_SPEED_NORMAL; + Pulse.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Pulse); + + mode Wave; + Wave.name = "Wave"; + Wave.value = THERMALTAKE_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Wave.speed_min = THERMALTAKE_SPEED_SLOW; + Wave.speed_max = THERMALTAKE_SPEED_EXTREME; + Wave.speed = THERMALTAKE_SPEED_NORMAL; + Wave.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Wave); + + SetupZones(); +} + +RGBController_ThermaltakeRiing::~RGBController_ThermaltakeRiing() +{ + delete controller; +} + +void RGBController_ThermaltakeRiing::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(THERMALTAKE_NUM_CHANNELS); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for (unsigned int channel_idx = 0; channel_idx < THERMALTAKE_NUM_CHANNELS; channel_idx++) + { + char ch_idx_string[2]; + snprintf(ch_idx_string, 2, "%d", channel_idx + 1); + + zones[channel_idx].name = "Riing Channel "; + zones[channel_idx].name.append(ch_idx_string); + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + /*-------------------------------------------------*\ + | The maximum number of colors that would fit in the| + | Riing protocol is 20 | + \*-------------------------------------------------*/ + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = 20; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + + zones[channel_idx].matrix_map = NULL; + + for (unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + char led_idx_string[3]; + snprintf(led_idx_string, 3, "%d", led_ch_idx + 1); + + led new_led; + new_led.name = "Riing Channel "; + new_led.name.append(ch_idx_string); + new_led.name.append(", LED "); + new_led.name.append(led_idx_string); + + leds.push_back(new_led); + leds_channel.push_back(channel_idx); + } + } + + SetupColors(); +} + +void RGBController_ThermaltakeRiing::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_ThermaltakeRiing::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} + +void RGBController_ThermaltakeRiing::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_ThermaltakeRiing::UpdateSingleLED(int led) +{ + unsigned int channel = leds_channel[led]; + + controller->SetChannelLEDs(channel, zones[channel].colors, zones[channel].leds_count); +} + +void RGBController_ThermaltakeRiing::DeviceUpdateMode() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetMode(modes[active_mode].value, modes[active_mode].speed); + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/RGBController_ThermaltakeRiing.h b/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/RGBController_ThermaltakeRiing.h new file mode 100644 index 0000000..f769b6d --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/RGBController_ThermaltakeRiing.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_ThermaltakeRiing.h | +| | +| RGBController for Thermaltake Riing | +| | +| Adam Honse (CalcProgrammer1) 09 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ThermaltakeRiingController.h" + +class RGBController_ThermaltakeRiing : public RGBController +{ +public: + RGBController_ThermaltakeRiing(ThermaltakeRiingController* controller_ptr); + ~RGBController_ThermaltakeRiing(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ThermaltakeRiingController* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/ThermaltakeRiingController.cpp b/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/ThermaltakeRiingController.cpp new file mode 100644 index 0000000..18e7354 --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/ThermaltakeRiingController.cpp @@ -0,0 +1,159 @@ +/*---------------------------------------------------------*\ +| ThermaltakeRiingController.cpp | +| | +| Driver for Thermaltake Riing | +| | +| Adam Honse (CalcProgrammer1) 07 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "ThermaltakeRiingController.h" + +ThermaltakeRiingController::ThermaltakeRiingController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + SendInit(); +} + +ThermaltakeRiingController::~ThermaltakeRiingController() +{ + hid_close(dev); +} + +std::string ThermaltakeRiingController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string ThermaltakeRiingController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string ThermaltakeRiingController::GetFirmwareVersion() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Get Firmware Version packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x33; + usb_buf[0x02] = 0x50; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read_timeout(dev, usb_buf, 65, 100); + + std::string ret_str = std::to_string(usb_buf[2]) + "." + std::to_string(usb_buf[3]) + "." + std::to_string(usb_buf[4]); + + return(ret_str); +} + +void ThermaltakeRiingController::SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors) +{ + unsigned char* color_data = new unsigned char[3 * num_colors]; + + for(unsigned int color = 0; color < num_colors; color++) + { + unsigned int color_idx = color * 3; + color_data[color_idx + 0] = RGBGetGValue(colors[color]); + color_data[color_idx + 1] = RGBGetRValue(colors[color]); + color_data[color_idx + 2] = RGBGetBValue(colors[color]); + } + + SendRGB(channel + 1, current_mode, current_speed, num_colors, color_data); + + delete[] color_data; +} + +void ThermaltakeRiingController::SetMode(unsigned char mode, unsigned char speed) +{ + current_mode = mode; + current_speed = speed; +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void ThermaltakeRiingController::SendInit() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Init packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0xFE; + usb_buf[0x02] = 0x33; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read_timeout(dev, usb_buf, 65, 100); +} + +void ThermaltakeRiingController::SendRGB + ( + unsigned char port, + unsigned char mode, + unsigned char speed, + unsigned char num_colors, + unsigned char* color_data + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up RGB packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0x32; + usb_buf[0x02] = 0x52; + usb_buf[0x03] = port; + usb_buf[0x04] = mode + ( speed & 0x03 ); + + /*-----------------------------------------------------*\ + | Copy in GRB color data | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x05], color_data, (num_colors * 3)); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read_timeout(dev, usb_buf, 65, 100); +} diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/ThermaltakeRiingController.h b/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/ThermaltakeRiingController.h new file mode 100644 index 0000000..9069a10 --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingController/ThermaltakeRiingController.h @@ -0,0 +1,82 @@ +/*---------------------------------------------------------*\ +| ThermaltakeRiingController.h | +| | +| Driver for Thermaltake Riing | +| | +| Adam Honse (CalcProgrammer1) 07 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +enum +{ + THERMALTAKE_PORT_1 = 0x01, + THERMALTAKE_PORT_2 = 0x02, + THERMALTAKE_PORT_3 = 0x03, + THERMALTAKE_PORT_4 = 0x04, + THERMALTAKE_PORT_5 = 0x05 +}; + +enum +{ + THERMALTAKE_MODE_FLOW = 0x00, + THERMALTAKE_MODE_SPECTRUM = 0x04, + THERMALTAKE_MODE_RIPPLE = 0x08, + THERMALTAKE_MODE_BLINK = 0x0C, + THERMALTAKE_MODE_PULSE = 0x10, + THERMALTAKE_MODE_WAVE = 0x14, + THERMALTAKE_MODE_PER_LED = 0x18, + THERMALTAKE_MODE_FULL = 0x19 +}; + +enum +{ + THERMALTAKE_SPEED_SLOW = 0x03, + THERMALTAKE_SPEED_NORMAL = 0x02, + THERMALTAKE_SPEED_FAST = 0x01, + THERMALTAKE_SPEED_EXTREME = 0x00 +}; + +#define THERMALTAKE_NUM_CHANNELS 5 + +class ThermaltakeRiingController +{ +public: + ThermaltakeRiingController(hid_device* dev_handle, const char* path); + ~ThermaltakeRiingController(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + std::string GetFirmwareVersion(); + + void SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors); + void SetMode(unsigned char mode, unsigned char speed); + +private: + hid_device* dev; + + unsigned char current_mode; + unsigned char current_speed; + std::string location; + + void SendInit(); + + void SendRGB + ( + unsigned char port, + unsigned char mode, + unsigned char speed, + unsigned char num_colors, + unsigned char* color_data + ); + + void SendFan(); + void SendSave(); +}; diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingControllerDetect.cpp b/Controllers/ThermaltakeRiingController/ThermaltakeRiingControllerDetect.cpp new file mode 100644 index 0000000..d8617db --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingControllerDetect.cpp @@ -0,0 +1,124 @@ +/*---------------------------------------------------------*\ +| ThermaltakeRiingControllerDetect.cpp | +| | +| Detector for Thermaltake Riing devices | +| | +| Adam Honse (CalcProgrammer1) 07 Feb 2020 | +| Chris M (Dr_No) 15 Feb 2021 | +| Sam B (4rcheria) 24 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "ThermaltakeRiingController.h" +#include "ThermaltakeRiingQuadController.h" +#include "ThermaltakeRiingTrioController.h" +#include "RGBController_ThermaltakeRiing.h" +#include "RGBController_ThermaltakeRiingQuad.h" +#include "RGBController_ThermaltakeRiingTrio.h" + + +#define THERMALTAKE_RIING_VID 0x264A +#define THERMALTAKE_RIING_PID_BEGIN 0x1FA5 +#define THERMALTAKE_RIING_PID_END 0x1FB5 + +/******************************************************************************************\ +* * +* DetectThermaltakeRiingControllers * +* * +* Tests the USB address to see if an AMD Wraith Prism controller exists there. * +* * +\******************************************************************************************/ + +void DetectThermaltakeRiingControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + if(dev) + { + ThermaltakeRiingController* controller = new ThermaltakeRiingController(dev, info->path); + RGBController_ThermaltakeRiing* rgb_controller = new RGBController_ThermaltakeRiing(controller); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectThermaltakeRiingControllers() */ + +void DetectThermaltakeRiingQuadControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + if(dev) + { + ThermaltakeRiingQuadController* controller = new ThermaltakeRiingQuadController(dev, info->path); + RGBController_ThermaltakeRiingQuad* rgb_controller = new RGBController_ThermaltakeRiingQuad(controller); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectThermaltakeRiingTrioControllers(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + if(dev) + { + ThermaltakeRiingTrioController* controller = new ThermaltakeRiingTrioController(dev, info->path); + RGBController_ThermaltakeRiingTrio* rgb_controller = new RGBController_ThermaltakeRiingTrio(controller); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FA5)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FA5); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FA6)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FA6); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FA7)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FA7); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FA8)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FA8); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FA9)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FA9); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FAA)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FAA); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FAB)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FAB); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FAC)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FAC); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FAD)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FAD); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FAE)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FAE); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FAF)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FAF); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FB0)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FB0); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FB1)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FB1); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FB2)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FB2); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FB3)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FB3); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FB4)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FB4); +REGISTER_HID_DETECTOR("Thermaltake Riing (PID 0x1FB5)", DetectThermaltakeRiingControllers, THERMALTAKE_RIING_VID, 0x1FB5); + +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2260)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2260); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2261)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2261); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2262)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2262); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2263)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2263); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2264)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2264); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2265)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2265); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2266)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2266); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2267)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2267); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2268)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2268); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2269)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2269); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x226A)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x226A); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x226B)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x226B); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x226C)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x226C); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x226D)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x226D); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x226E)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x226E); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x226F)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x226F); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x2270)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x2270); +REGISTER_HID_DETECTOR("Thermaltake Riing Quad (PID 0x232B)", DetectThermaltakeRiingQuadControllers, THERMALTAKE_RIING_VID, 0x232B); + +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x2135)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x2135); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x2136)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x2136); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x2137)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x2137); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x2138)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x2138); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x2139)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x2139); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x213A)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x213A); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x213B)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x213B); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x213C)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x213C); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x213D)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x213D); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x213E)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x213E); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x213F)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x213F); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x2141)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x2141); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x2142)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x2142); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x2143)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x2143); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x2144)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x2144); +REGISTER_HID_DETECTOR("Thermaltake Riing Trio (PID 0x2145)", DetectThermaltakeRiingTrioControllers, THERMALTAKE_RIING_VID, 0x2145); diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/RGBController_ThermaltakeRiingQuad.cpp b/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/RGBController_ThermaltakeRiingQuad.cpp new file mode 100644 index 0000000..5f7104a --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/RGBController_ThermaltakeRiingQuad.cpp @@ -0,0 +1,160 @@ +/*---------------------------------------------------------*\ +| RGBController_ThermaltakeRiingQuad.cpp | +| | +| RGBController for Thermaltake Riing Quad | +| | +| Chris M (Dr_No) 15 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ThermaltakeRiingQuad.h" + +/**------------------------------------------------------------------*\ + @name Thermaltake Riing Quad + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectThermaltakeRiingQuadControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_ThermaltakeRiingQuad::RGBController_ThermaltakeRiingQuad(ThermaltakeRiingQuadController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Thermaltake"; + type = DEVICE_TYPE_COOLER; + description = "Thermaltake Riing Quad Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = THERMALTAKE_QUAD_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.speed_min = 0; + Direct.speed_max = 0; + Direct.speed = 0; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_ThermaltakeRiingQuad::~RGBController_ThermaltakeRiingQuad() +{ + delete controller; +} + +void RGBController_ThermaltakeRiingQuad::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(THERMALTAKE_QUAD_NUM_CHANNELS); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for (unsigned int channel_idx = 0; channel_idx < THERMALTAKE_QUAD_NUM_CHANNELS; channel_idx++) + { + char ch_idx_string[2]; + snprintf(ch_idx_string, 2, "%d", channel_idx + 1); + + zones[channel_idx].name = "Riing Channel "; + zones[channel_idx].name.append(ch_idx_string); + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + /*-------------------------------------------------*\ + | The maximum number of colors that would fit in the| + | Riing Quad protocol is 54 | + \*-------------------------------------------------*/ + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = 60; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + + zones[channel_idx].matrix_map = NULL; + + for (unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + char led_idx_string[3]; + snprintf(led_idx_string, 3, "%d", led_ch_idx + 1); + + led new_led; + new_led.name = "Riing Channel "; + new_led.name.append(ch_idx_string); + new_led.name.append(", LED "); + new_led.name.append(led_idx_string); + + leds.push_back(new_led); + leds_channel.push_back(channel_idx); + } + } + + SetupColors(); +} + +void RGBController_ThermaltakeRiingQuad::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_ThermaltakeRiingQuad::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} + +void RGBController_ThermaltakeRiingQuad::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_ThermaltakeRiingQuad::UpdateSingleLED(int led) +{ + unsigned int channel = leds_channel[led]; + + controller->SetChannelLEDs(channel, zones[channel].colors, zones[channel].leds_count); +} + +void RGBController_ThermaltakeRiingQuad::DeviceUpdateMode() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetMode(modes[active_mode].value, modes[active_mode].speed); + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/RGBController_ThermaltakeRiingQuad.h b/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/RGBController_ThermaltakeRiingQuad.h new file mode 100644 index 0000000..03dff06 --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/RGBController_ThermaltakeRiingQuad.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_ThermaltakeRiingQuad.h | +| | +| RGBController for Thermaltake Riing Quad | +| | +| Chris M (Dr_No) 15 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ThermaltakeRiingQuadController.h" + +class RGBController_ThermaltakeRiingQuad : public RGBController +{ +public: + RGBController_ThermaltakeRiingQuad(ThermaltakeRiingQuadController* controller_ptr); + ~RGBController_ThermaltakeRiingQuad(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ThermaltakeRiingQuadController* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/ThermaltakeRiingQuadController.cpp b/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/ThermaltakeRiingQuadController.cpp new file mode 100644 index 0000000..0109443 --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/ThermaltakeRiingQuadController.cpp @@ -0,0 +1,164 @@ +/*---------------------------------------------------------*\ +| ThermaltakeRiingQuadController.cpp | +| | +| Driver for Thermaltake Riing Quad | +| | +| Chris M (Dr_No) 15 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "ThermaltakeRiingQuadController.h" + +ThermaltakeRiingQuadController::ThermaltakeRiingQuadController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + + SendInit(); + + /*-----------------------------------------------------*\ + | The Riing Quad only seems to run in direct mode and | + | requires a packet within seconds to remain in the | + | set mode (similar to Corsair Node Pro). Start a thread| + | to send a packet every TT_QUAD_KEEPALIVE seconds | + \*-----------------------------------------------------*/ + memset(tt_quad_buffer, 0x00, sizeof(tt_quad_buffer)); + unsigned char temp_buffer[3] = { 0x00, 0x32, 0x52 }; + + for(std::size_t zone_index = 0; zone_index < THERMALTAKE_QUAD_NUM_CHANNELS; zone_index++) + { + /*-------------------------------------------------*\ + | Add the constant bytes for the mode info buffer | + \*-------------------------------------------------*/ + memcpy(&tt_quad_buffer[zone_index][0], temp_buffer, 3); + } + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&ThermaltakeRiingQuadController::KeepaliveThread, this); +} + +ThermaltakeRiingQuadController::~ThermaltakeRiingQuadController() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + hid_close(dev); +} + +void ThermaltakeRiingQuadController::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_commit_time) > std::chrono::seconds(THERMALTAKE_QUAD_KEEPALIVE)) + { + SendBuffer(); + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + } +} + +std::string ThermaltakeRiingQuadController::GetDeviceName() +{ + return device_name; +} + +std::string ThermaltakeRiingQuadController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string ThermaltakeRiingQuadController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void ThermaltakeRiingQuadController::SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors) +{ + unsigned char* color_data = new unsigned char[3 * num_colors]; + + for(unsigned int color = 0; color < num_colors; color++) + { + unsigned int color_idx = color * 3; + color_data[color_idx + 0] = RGBGetGValue(colors[color]); + color_data[color_idx + 1] = RGBGetRValue(colors[color]); + color_data[color_idx + 2] = RGBGetBValue(colors[color]); + } + + tt_quad_buffer[channel][THERMALTAKE_QUAD_ZONE_BYTE] = channel + 1; + tt_quad_buffer[channel][THERMALTAKE_QUAD_MODE_BYTE] = current_mode + ( current_speed & 0x03 ); + memcpy(&tt_quad_buffer[channel][THERMALTAKE_QUAD_DATA_BYTE], color_data, (num_colors * 3)); + + hid_write(dev, tt_quad_buffer[channel], THERMALTAKE_QUAD_PACKET_SIZE); + + delete[] color_data; +} + +void ThermaltakeRiingQuadController::SetMode(unsigned char mode, unsigned char speed) +{ + current_mode = mode; + current_speed = speed; +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void ThermaltakeRiingQuadController::SendInit() +{ + unsigned char usb_buf[THERMALTAKE_QUAD_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Init packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0xFE; + usb_buf[0x02] = 0x33; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, THERMALTAKE_QUAD_PACKET_SIZE); + hid_read_timeout(dev, usb_buf, THERMALTAKE_QUAD_PACKET_SIZE, THERMALTAKE_QUAD_INTERRUPT_TIMEOUT); +} + +void ThermaltakeRiingQuadController::SendBuffer() +{ + for(std::size_t channel_index = 0; channel_index < THERMALTAKE_QUAD_NUM_CHANNELS; channel_index++) + { + hid_write(dev, tt_quad_buffer[channel_index], THERMALTAKE_QUAD_PACKET_SIZE); + } + + /*-------------------------------------*\ + | Update the last commit time | + \*-------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); +} diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/ThermaltakeRiingQuadController.h b/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/ThermaltakeRiingQuadController.h new file mode 100644 index 0000000..a797b65 --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/ThermaltakeRiingQuadController.h @@ -0,0 +1,81 @@ +/*---------------------------------------------------------*\ +| ThermaltakeRiingQuadController.h | +| | +| Driver for Thermaltake Riing Quad | +| | +| Chris M (Dr_No) 15 Feb 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define THERMALTAKE_QUAD_PACKET_SIZE 193 +#define THERMALTAKE_QUAD_INTERRUPT_TIMEOUT 250 +#define THERMALTAKE_QUAD_KEEPALIVE 3 +#define HID_MAX_STR 255 + +enum +{ + THERMALTAKE_QUAD_COMMAND_BYTE = 1, + THERMALTAKE_QUAD_FUNCTION_BYTE = 2, + THERMALTAKE_QUAD_ZONE_BYTE = 3, + THERMALTAKE_QUAD_MODE_BYTE = 4, + THERMALTAKE_QUAD_DATA_BYTE = 5, +}; + +enum +{ + THERMALTAKE_QUAD_MODE_DIRECT = 0x24 +}; + +enum +{ + THERMALTAKE_QUAD_SPEED_EXTREME = 0x00, + THERMALTAKE_QUAD_SPEED_FAST = 0x01, + THERMALTAKE_QUAD_SPEED_NORMAL = 0x02, + THERMALTAKE_QUAD_SPEED_SLOW = 0x03, +}; + +#define THERMALTAKE_QUAD_NUM_CHANNELS 5 + +class ThermaltakeRiingQuadController +{ +public: + ThermaltakeRiingQuadController(hid_device* dev_handle, const char* path); + ~ThermaltakeRiingQuadController(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + std::string GetSerial(); + + void SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors); + void SetMode(unsigned char mode, unsigned char speed); + +private: + hid_device* dev; + + unsigned char current_mode; + unsigned char current_speed; + std::string device_name; + std::string location; + + uint8_t tt_quad_buffer[THERMALTAKE_QUAD_NUM_CHANNELS][THERMALTAKE_QUAD_PACKET_SIZE]; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_commit_time; + + void SendBuffer(); + void KeepaliveThread(); + + void SendInit(); + + void SendFan(); + void SendSave(); +}; diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/RGBController_ThermaltakeRiingTrio.cpp b/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/RGBController_ThermaltakeRiingTrio.cpp new file mode 100644 index 0000000..5a8399b --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/RGBController_ThermaltakeRiingTrio.cpp @@ -0,0 +1,160 @@ +/*---------------------------------------------------------*\ +| RGBController_ThermaltakeRiingTrio.cpp | +| | +| RGBController for Thermaltake Riing Trio | +| | +| Sam B (4rcheria) 24 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ThermaltakeRiingTrio.h" + +/**------------------------------------------------------------------*\ + @name Thermaltake Riing Trio + @category Cooler + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectThermaltakeRiingTrioControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_ThermaltakeRiingTrio::RGBController_ThermaltakeRiingTrio(ThermaltakeRiingTrioController* controller_ptr) +{ + controller = controller_ptr; + + name = "Thermaltake Trio"; + vendor = "Thermaltake"; + type = DEVICE_TYPE_COOLER; + description = "Thermaltake Riing Trio Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerial(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = THERMALTAKE_TRIO_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.speed_min = 0; + Direct.speed_max = 0; + Direct.speed = 0; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_ThermaltakeRiingTrio::~RGBController_ThermaltakeRiingTrio() +{ + delete controller; +} + +void RGBController_ThermaltakeRiingTrio::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(THERMALTAKE_TRIO_NUM_CHANNELS); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for (unsigned int channel_idx = 0; channel_idx < THERMALTAKE_TRIO_NUM_CHANNELS; channel_idx++) + { + char ch_idx_string[2]; + snprintf(ch_idx_string, 2, "%d", channel_idx + 1); + + zones[channel_idx].name = "Riing Channel "; + zones[channel_idx].name.append(ch_idx_string); + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + /*-------------------------------------------------*\ + | The maximum number of colors that would fit in the| + | Riing Trio protocol is 54 | + \*-------------------------------------------------*/ + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = 54; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + + zones[channel_idx].matrix_map = NULL; + + for (unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + char led_idx_string[3]; + snprintf(led_idx_string, 3, "%d", led_ch_idx + 1); + + led new_led; + new_led.name = "Riing Channel "; + new_led.name.append(ch_idx_string); + new_led.name.append(", LED "); + new_led.name.append(led_idx_string); + + leds.push_back(new_led); + leds_channel.push_back(channel_idx); + } + } + + SetupColors(); +} + +void RGBController_ThermaltakeRiingTrio::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_ThermaltakeRiingTrio::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} + +void RGBController_ThermaltakeRiingTrio::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_ThermaltakeRiingTrio::UpdateSingleLED(int led) +{ + unsigned int channel = leds_channel[led]; + + controller->SetChannelLEDs(channel, zones[channel].colors, zones[channel].leds_count); +} + +void RGBController_ThermaltakeRiingTrio::DeviceUpdateMode() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + controller->SetMode(modes[active_mode].value, modes[active_mode].speed); + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } +} diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/RGBController_ThermaltakeRiingTrio.h b/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/RGBController_ThermaltakeRiingTrio.h new file mode 100644 index 0000000..94a8fd5 --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/RGBController_ThermaltakeRiingTrio.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_ThermaltakeRiingTrio.h | +| | +| RGBController for Thermaltake Riing Trio | +| | +| Sam B (4rcheria) 24 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ThermaltakeRiingTrioController.h" + +class RGBController_ThermaltakeRiingTrio : public RGBController +{ +public: + RGBController_ThermaltakeRiingTrio(ThermaltakeRiingTrioController* controller_ptr); + ~RGBController_ThermaltakeRiingTrio(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ThermaltakeRiingTrioController* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/ThermaltakeRiingTrioController.cpp b/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/ThermaltakeRiingTrioController.cpp new file mode 100644 index 0000000..6406251 --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/ThermaltakeRiingTrioController.cpp @@ -0,0 +1,146 @@ +/*---------------------------------------------------------*\ +| ThermaltakeRiingTrioController.cpp | +| | +| Driver for Thermaltake Riing Trio | +| | +| Sam B (4rcheria) 24 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "ThermaltakeRiingTrioController.h" + +ThermaltakeRiingTrioController::ThermaltakeRiingTrioController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + + SendInit(); + + memset(tt_trio_buffer, 0x00, sizeof(tt_trio_buffer)); + unsigned char temp_buffer[3] = { 0x00, 0x32, 0x52 }; + + for(std::size_t zone_index = 0; zone_index < THERMALTAKE_TRIO_NUM_CHANNELS; zone_index++) + { + /*-------------------------------------------------*\ + | Add the constant bytes for the mode info buffer | + \*-------------------------------------------------*/ + memcpy(&tt_trio_buffer[zone_index][0], temp_buffer, 3); + } +} + +ThermaltakeRiingTrioController::~ThermaltakeRiingTrioController() +{ + hid_close(dev); +} + +std::string ThermaltakeRiingTrioController::GetDeviceName() +{ + return device_name; +} + +std::string ThermaltakeRiingTrioController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string ThermaltakeRiingTrioController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void ThermaltakeRiingTrioController::SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors) +{ + if(num_colors == 0) return; + + unsigned char* color_data = new unsigned char[3 * num_colors]; + + for(unsigned int color = 0; color < num_colors; color++) + { + unsigned int color_idx = color * 3; + color_data[color_idx + 0] = RGBGetGValue(colors[color]); + color_data[color_idx + 1] = RGBGetRValue(colors[color]); + color_data[color_idx + 2] = RGBGetBValue(colors[color]); + } + + tt_trio_buffer[channel][THERMALTAKE_TRIO_ZONE_BYTE] = channel + 1; + tt_trio_buffer[channel][THERMALTAKE_TRIO_MODE_BYTE] = 0x24; + tt_trio_buffer[channel][5] = 0x03; + tt_trio_buffer[channel][7] = 0x00; + + /*-------------------------------------------------*\ + | create and send chunks min = 2 max = 4 | + \*-------------------------------------------------*/ + for(unsigned int i = 0; (num_colors > (THERMALTAKE_TRIO_CHUNK_LENGTH * i)) || i < 2; i++) + { + unsigned int colors_transmitted = THERMALTAKE_TRIO_CHUNK_LENGTH * i; + unsigned int colors_to_transmit = (num_colors > colors_transmitted) ? num_colors - colors_transmitted : 0; + + memset(&tt_trio_buffer[channel][THERMALTAKE_TRIO_CHUNK_ID], 0x00, sizeof(tt_trio_buffer[channel]) - THERMALTAKE_TRIO_CHUNK_ID); + tt_trio_buffer[channel][THERMALTAKE_TRIO_CHUNK_ID] = i + 1; + if(colors_to_transmit < THERMALTAKE_TRIO_CHUNK_LENGTH) + { + memcpy(&tt_trio_buffer[channel][THERMALTAKE_TRIO_DATA_BYTE], &color_data[colors_transmitted * 3], (colors_to_transmit * 3)); + } + else + { + memcpy(&tt_trio_buffer[channel][THERMALTAKE_TRIO_DATA_BYTE], &color_data[colors_transmitted * 3], (THERMALTAKE_TRIO_CHUNK_LENGTH * 3)); + } + + hid_write(dev, tt_trio_buffer[channel], THERMALTAKE_TRIO_PACKET_SIZE); + } + + delete[] color_data; +} + +void ThermaltakeRiingTrioController::SetMode(unsigned char mode, unsigned char speed) +{ + current_mode = mode; + current_speed = speed; +} + +void ThermaltakeRiingTrioController::SendInit() +{ + unsigned char usb_buf[THERMALTAKE_TRIO_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Init packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = 0xFE; + usb_buf[0x02] = 0x33; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, THERMALTAKE_TRIO_PACKET_SIZE); + hid_read_timeout(dev, usb_buf, THERMALTAKE_TRIO_PACKET_SIZE, THERMALTAKE_TRIO_INTERRUPT_TIMEOUT); +} + diff --git a/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/ThermaltakeRiingTrioController.h b/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/ThermaltakeRiingTrioController.h new file mode 100644 index 0000000..3072b90 --- /dev/null +++ b/Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/ThermaltakeRiingTrioController.h @@ -0,0 +1,77 @@ +/*---------------------------------------------------------*\ +| ThermaltakeRiingTrioController.h | +| | +| Driver for Thermaltake Riing Trio | +| | +| Sam B (4rcheria) 24 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +#define THERMALTAKE_TRIO_PACKET_SIZE 65 +#define THERMALTAKE_TRIO_INTERRUPT_TIMEOUT 250 +#define THERMALTAKE_TRIO_KEEPALIVE 3 +#define HID_MAX_STR 255 + +enum +{ + THERMALTAKE_TRIO_COMMAND_BYTE = 1, + THERMALTAKE_TRIO_FUNCTION_BYTE = 2, + THERMALTAKE_TRIO_ZONE_BYTE = 3, + THERMALTAKE_TRIO_MODE_BYTE = 4, + THERMALTAKE_TRIO_CHUNK_ID = 6, + THERMALTAKE_TRIO_DATA_BYTE = 8, + THERMALTAKE_TRIO_CHUNK_LENGTH = 19, +}; + +enum +{ + THERMALTAKE_TRIO_MODE_DIRECT = 0x24 +}; + +enum +{ + THERMALTAKE_TRIO_SPEED_EXTREME = 0x00, + THERMALTAKE_TRIO_SPEED_FAST = 0x01, + THERMALTAKE_TRIO_SPEED_NORMAL = 0x02, + THERMALTAKE_TRIO_SPEED_SLOW = 0x03, +}; + +#define THERMALTAKE_TRIO_NUM_CHANNELS 5 + +class ThermaltakeRiingTrioController +{ +public: + ThermaltakeRiingTrioController(hid_device* dev_handle, const char* path); + ~ThermaltakeRiingTrioController(); + + std::string GetDeviceName(); + std::string GetDeviceLocation(); + std::string GetSerial(); + + void SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors); + void SetMode(unsigned char mode, unsigned char speed); + +private: + hid_device* dev; + + unsigned char current_mode; + unsigned char current_speed; + std::string device_name; + std::string location; + + uint8_t tt_trio_buffer[THERMALTAKE_TRIO_NUM_CHANNELS][THERMALTAKE_TRIO_PACKET_SIZE]; + + void SendInit(); + + void SendFan(); + void SendSave(); +}; diff --git a/Controllers/ThingMController/BlinkController.cpp b/Controllers/ThingMController/BlinkController.cpp new file mode 100644 index 0000000..f08afba --- /dev/null +++ b/Controllers/ThingMController/BlinkController.cpp @@ -0,0 +1,85 @@ +/*---------------------------------------------------------*\ +| BlinkController.cpp | +| | +| Driver for ThingM Blink | +| | +| Eric S (edbgon) 01 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "BlinkController.h" +#include "StringUtils.h" + +BlinkController::BlinkController(hid_device* dev_handle, char *_path) +{ + dev = dev_handle; + location = _path; + + /*---------------------------------------------------------*\ + | Get device name from HID manufacturer and product strings | + \*---------------------------------------------------------*/ + wchar_t name_string[HID_MAX_STR]; + + hid_get_manufacturer_string(dev, name_string, HID_MAX_STR); + device_name = StringUtils::wstring_to_string(name_string); + + hid_get_product_string(dev, name_string, HID_MAX_STR); + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); +} + +BlinkController::~BlinkController() +{ + if(dev) + { + hid_close(dev); + } +} + +std::string BlinkController::GetDeviceName() +{ + return device_name; +} + +std::string BlinkController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +std::string BlinkController::GetLocation() +{ + return("HID: " + location); +} + +void BlinkController::SendUpdate(unsigned char led, unsigned char red, unsigned char green, unsigned char blue, unsigned int speed) +{ + + unsigned char buffer[BLINK_PACKET_SIZE] = { 0x00 }; + memset(buffer, 0x00, BLINK_PACKET_SIZE); + + buffer[0x00] = 0x01; + buffer[0x01] = 0x63; + buffer[0x02] = red; + buffer[0x03] = green; + buffer[0x04] = blue; + + if(speed > 0) + { + buffer[0x05] = (speed & 0xff00) >> 8; + buffer[0x06] = speed & 0x00ff; + } + + buffer[0x07] = led; + + hid_send_feature_report(dev, buffer, BLINK_PACKET_SIZE); +} diff --git a/Controllers/ThingMController/BlinkController.h b/Controllers/ThingMController/BlinkController.h new file mode 100644 index 0000000..c85b438 --- /dev/null +++ b/Controllers/ThingMController/BlinkController.h @@ -0,0 +1,54 @@ +/*---------------------------------------------------------*\ +| BlinkController.h | +| | +| Driver for ThingM Blink | +| | +| Eric S (edbgon) 01 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +#define BLINK_PACKET_SIZE 9 //Includes extra first byte for non HID Report packets + +#define BLINK_MODE_OFF 0 +#define BLINK_MODE_DIRECT 1 +#define BLINK_MODE_FADE 2 + +#define HID_MAX_STR 255 + +class BlinkController +{ +public: + BlinkController(hid_device* dev_handle, char *_path); + ~BlinkController(); + + std::string GetDeviceName(); + std::string GetSerial(); + std::string GetLocation(); + + unsigned char GetLedRed(); + unsigned char GetLedGreen(); + unsigned char GetLedBlue(); + unsigned char GetLedSpeed(); + unsigned char GetBrightness(); + void SendUpdate(unsigned char led, unsigned char red, unsigned char green, unsigned char blue, unsigned int speed); + +private: + std::string device_name; + std::string serial; + std::string location; + hid_device* dev; + + unsigned char current_red; + unsigned char current_green; + unsigned char current_blue; + + void SendUpdate(); +}; diff --git a/Controllers/ThingMController/RGBController_BlinkController.cpp b/Controllers/ThingMController/RGBController_BlinkController.cpp new file mode 100644 index 0000000..279d9aa --- /dev/null +++ b/Controllers/ThingMController/RGBController_BlinkController.cpp @@ -0,0 +1,131 @@ +/*---------------------------------------------------------*\ +| RGBController_BlinkController.cpp | +| | +| RGBController for ThingM Blink | +| | +| Eric S (edbgon) 01 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_BlinkController.h" + +/**------------------------------------------------------------------*\ + @name ThingM Blink + @category LEDStrip + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectThingMBlink + @comment +\*-------------------------------------------------------------------*/ + +RGBController_BlinkController::RGBController_BlinkController(BlinkController* controller_ptr) +{ + controller = controller_ptr; + + name = "Blink"; + vendor = "ThingM"; + type = DEVICE_TYPE_LEDSTRIP; + description = controller->GetDeviceName(); + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode Off; + Off.name = "Off"; + Off.flags = 0; + Off.value = BLINK_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = BLINK_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.colors_min = 1; + Direct.colors_max = 1; + Direct.colors.resize(1); + modes.push_back(Direct); + + mode Fade; + Fade.name = "Fade"; + Fade.value = BLINK_MODE_FADE; + Fade.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED; + Fade.color_mode = MODE_COLORS_PER_LED; + Fade.speed_min = 0xFFFF; + Fade.speed = 0x0000; + Fade.speed_max = 0x0000; + Fade.colors_min = 1; + Fade.colors_max = 1; + Fade.colors.resize(1); + modes.push_back(Fade); + + SetupZones(); + active_mode = 1; +} + +RGBController_BlinkController::~RGBController_BlinkController() +{ + delete controller; +} + +void RGBController_BlinkController::SetupZones() +{ + zone Blink_zone; + Blink_zone.name = "blink(1) mk2"; + Blink_zone.type = ZONE_TYPE_SINGLE; + Blink_zone.leds_min = 2; + Blink_zone.leds_max = 2; + Blink_zone.leds_count = 2; + Blink_zone.matrix_map = NULL; + zones.push_back(Blink_zone); + + led Blink_led; + Blink_led.name = "LED A"; + Blink_led.value = 1; + leds.push_back(Blink_led); + + Blink_led.name = "LED B"; + Blink_led.value = 2; + leds.push_back(Blink_led); + + SetupColors(); + +} + +void RGBController_BlinkController::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_BlinkController::DeviceUpdateLEDs() +{ + for(std::size_t led = 0; led < colors.size(); led++) + { + UpdateSingleLED((int)led); + } +} + +void RGBController_BlinkController::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_BlinkController::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + controller->SendUpdate(leds[led].value, red, grn, blu, modes[active_mode].speed); +} + +void RGBController_BlinkController::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/ThingMController/RGBController_BlinkController.h b/Controllers/ThingMController/RGBController_BlinkController.h new file mode 100644 index 0000000..36f47b9 --- /dev/null +++ b/Controllers/ThingMController/RGBController_BlinkController.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_BlinkController.h | +| | +| RGBController for ThingM Blink | +| | +| Eric S (edbgon) 01 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "BlinkController.h" + +class RGBController_BlinkController : public RGBController +{ +public: + RGBController_BlinkController(BlinkController* controller_ptr); + ~RGBController_BlinkController(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + BlinkController* controller; +}; diff --git a/Controllers/ThingMController/ThingMControllerDetect.cpp b/Controllers/ThingMController/ThingMControllerDetect.cpp new file mode 100644 index 0000000..8a0e12c --- /dev/null +++ b/Controllers/ThingMController/ThingMControllerDetect.cpp @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| ThingMControllerDetect.cpp | +| | +| Detector for ThingM Blink | +| | +| Eric S (edbgon) 01 Oct 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "BlinkController.h" +#include "RGBController_BlinkController.h" + +#define THINGM_VID 0x27B8 + +#define THINGM_BLINK_PID 0x01ED + +/******************************************************************************************\ +* * +* DetectThingMControllers * +* * +* Tests the USB address to see if any CoolerMaster controllers exists there. * +* * +\******************************************************************************************/ + +void DetectThingMBlink(hid_device_info* info, const std::string&) +{ + hid_device* dev = hid_open_path(info->path); + if(dev) + { + BlinkController* controller = new BlinkController(dev, info->path); + RGBController_BlinkController* rgb_controller = new RGBController_BlinkController(controller); + // Constructor sets the name + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_PU("ThingM blink(1) mk2", DetectThingMBlink, THINGM_VID, THINGM_BLINK_PID, 0xFF00, 0x01); diff --git a/Controllers/ThrustmasterSolController/RGBController_ThrustmasterSol.cpp b/Controllers/ThrustmasterSolController/RGBController_ThrustmasterSol.cpp new file mode 100644 index 0000000..1257bea --- /dev/null +++ b/Controllers/ThrustmasterSolController/RGBController_ThrustmasterSol.cpp @@ -0,0 +1,353 @@ +/*---------------------------------------------------------*\ +| RGBController_ThrustmasterSol.cpp | +| | +| RGBController for Thrustmaster Sol series joysticks | +| | +| Ken Sanislo 02 Apr 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ThrustmasterSol.h" + +/**------------------------------------------------------------------*\ + @name Thrustmaster Sol + @category Gamepad + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :x: + @detectors DetectThrustmasterSolControllers + @comment Thrustmaster Sol series joystick RGB LED control. Supports + Sol-R, Sol F16, and Sol F18 variants. Only Sol-R has been tested. + Uses vendor-specific USB interface 1 (not HID). +\*-------------------------------------------------------------------*/ + +#define NA 0xFFFFFFFF + +static unsigned int logo_matrix_map[2][2] = +{ + { 0, 3 }, + { 1, 2 }, +}; + +static unsigned int ring_matrix_map[3][5] = +{ + { NA, 7, 0, 1, NA }, + { 6, NA, NA, NA, 2 }, + { NA, 5, 4, 3, NA }, +}; + +static unsigned int right_buttons_matrix_map[2][2] = +{ + { 0, 1 }, + { 2, 3 }, +}; + +static unsigned int left_buttons_matrix_map[2][2] = +{ + { 0, 1 }, + { 2, 3 }, +}; + +RGBController_ThrustmasterSol::RGBController_ThrustmasterSol(ThrustmasterSolController* controller_ptr) +{ + controller = controller_ptr; + + vendor = "Thrustmaster"; + type = DEVICE_TYPE_GAMEPAD; + description = "Thrustmaster Sol Series Joystick"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); + + /*---------------------------------------------------------*\ + | Read current EEPROM colors from device so the UI starts | + | with the actual hardware state rather than all-black | + \*---------------------------------------------------------*/ + std::vector hw_zones; + std::vector hw_colors; + controller->ReadColors(hw_zones, hw_colors); + + for(unsigned int i = 0; i < hw_zones.size(); i++) + { + for(unsigned int j = 0; j < leds.size(); j++) + { + if(leds[j].value == hw_zones[i]) + { + colors[j] = hw_colors[i]; + break; + } + } + } +} + +RGBController_ThrustmasterSol::~RGBController_ThrustmasterSol() +{ + delete controller; +} + +void RGBController_ThrustmasterSol::SetupZones() +{ + /*---------------------------------------------------------*\ + | Thumbstick zone (grip, 1 LED) | + \*---------------------------------------------------------*/ + zone thumbstick_zone; + thumbstick_zone.name = "Thumbstick"; + thumbstick_zone.type = ZONE_TYPE_SINGLE; + thumbstick_zone.leds_min = 1; + thumbstick_zone.leds_max = 1; + thumbstick_zone.leds_count = 1; + thumbstick_zone.matrix_map = NULL; + zones.push_back(thumbstick_zone); + + led thumbstick_led; + thumbstick_led.name = "Thumbstick"; + thumbstick_led.value = THRUSTMASTER_SOL_GRIP_FLAG | 0x00; + leds.push_back(thumbstick_led); + + /*---------------------------------------------------------*\ + | TM Logo zone (3 LEDs) | + \*---------------------------------------------------------*/ + zone logo_zone; + logo_zone.name = "TM Logo"; + logo_zone.type = ZONE_TYPE_MATRIX; + logo_zone.leds_min = 4; + logo_zone.leds_max = 4; + logo_zone.leds_count = 4; + logo_zone.matrix_map = new matrix_map_type; + logo_zone.matrix_map->height = 2; + logo_zone.matrix_map->width = 2; + logo_zone.matrix_map->map = (unsigned int *)&logo_matrix_map; + zones.push_back(logo_zone); + + led logo_top_left; + logo_top_left.name = "TM Logo Top Left"; + logo_top_left.value = 0x01; + leds.push_back(logo_top_left); + + led logo_bottom_left; + logo_bottom_left.name = "TM Logo Bottom Left"; + logo_bottom_left.value = 0x02; + leds.push_back(logo_bottom_left); + + led logo_bottom_right; + logo_bottom_right.name = "TM Logo Bottom Right"; + logo_bottom_right.value = 0x03; + leds.push_back(logo_bottom_right); + + led logo_top_right; + logo_top_right.name = "TM Logo Top Right"; + logo_top_right.value = 0x00; + leds.push_back(logo_top_right); + + /*---------------------------------------------------------*\ + | Left Buttons zone (4 LEDs: buttons 5-8) | + \*---------------------------------------------------------*/ + zone left_buttons_zone; + left_buttons_zone.name = "Left Buttons"; + left_buttons_zone.type = ZONE_TYPE_MATRIX; + left_buttons_zone.leds_min = 4; + left_buttons_zone.leds_max = 4; + left_buttons_zone.leds_count = 4; + left_buttons_zone.matrix_map = new matrix_map_type; + left_buttons_zone.matrix_map->height = 2; + left_buttons_zone.matrix_map->width = 2; + left_buttons_zone.matrix_map->map = (unsigned int *)&left_buttons_matrix_map; + zones.push_back(left_buttons_zone); + + led btn5; + btn5.name = "Button 5"; + btn5.value = 0x11; + leds.push_back(btn5); + + led btn6; + btn6.name = "Button 6"; + btn6.value = 0x10; + leds.push_back(btn6); + + led btn7; + btn7.name = "Button 7"; + btn7.value = 0x12; + leds.push_back(btn7); + + led btn8; + btn8.name = "Button 8"; + btn8.value = 0x13; + leds.push_back(btn8); + + /*---------------------------------------------------------*\ + | Base Ring zone (8 LEDs, clockwise from top) | + \*---------------------------------------------------------*/ + zone ring_zone; + ring_zone.name = "Base Ring"; + ring_zone.type = ZONE_TYPE_MATRIX; + ring_zone.leds_min = 8; + ring_zone.leds_max = 8; + ring_zone.leds_count = 8; + ring_zone.matrix_map = new matrix_map_type; + ring_zone.matrix_map->height = 3; + ring_zone.matrix_map->width = 5; + ring_zone.matrix_map->map = (unsigned int *)&ring_matrix_map; + zones.push_back(ring_zone); + + led ring_upper; + ring_upper.name = "Upper"; + ring_upper.value = 0x04; + leds.push_back(ring_upper); + + led ring_upper_right; + ring_upper_right.name = "Upper Right"; + ring_upper_right.value = 0x05; + leds.push_back(ring_upper_right); + + led ring_right; + ring_right.name = "Right"; + ring_right.value = 0x06; + leds.push_back(ring_right); + + led ring_bottom_right; + ring_bottom_right.name = "Bottom Right"; + ring_bottom_right.value = 0x0B; + leds.push_back(ring_bottom_right); + + led ring_bottom; + ring_bottom.name = "Bottom"; + ring_bottom.value = 0x0C; + leds.push_back(ring_bottom); + + led ring_bottom_left; + ring_bottom_left.name = "Bottom Left"; + ring_bottom_left.value = 0x0D; + leds.push_back(ring_bottom_left); + + led ring_left; + ring_left.name = "Left"; + ring_left.value = 0x0E; + leds.push_back(ring_left); + + led ring_upper_left; + ring_upper_left.name = "Upper Left"; + ring_upper_left.value = 0x0F; + leds.push_back(ring_upper_left); + + /*---------------------------------------------------------*\ + | Right Buttons zone (4 LEDs: buttons 16-19) | + \*---------------------------------------------------------*/ + zone right_buttons_zone; + right_buttons_zone.name = "Right Buttons"; + right_buttons_zone.type = ZONE_TYPE_MATRIX; + right_buttons_zone.leds_min = 4; + right_buttons_zone.leds_max = 4; + right_buttons_zone.leds_count = 4; + right_buttons_zone.matrix_map = new matrix_map_type; + right_buttons_zone.matrix_map->height = 2; + right_buttons_zone.matrix_map->width = 2; + right_buttons_zone.matrix_map->map = (unsigned int *)&right_buttons_matrix_map; + zones.push_back(right_buttons_zone); + + led btn17; + btn17.name = "Button 17"; + btn17.value = 0x07; + leds.push_back(btn17); + + led btn16; + btn16.name = "Button 16"; + btn16.value = 0x08; + leds.push_back(btn16); + + led btn19; + btn19.name = "Button 19"; + btn19.value = 0x0A; + leds.push_back(btn19); + + led btn18; + btn18.name = "Button 18"; + btn18.value = 0x09; + leds.push_back(btn18); + + SetupColors(); +} + +void RGBController_ThrustmasterSol::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ThrustmasterSol::DeviceUpdateLEDs() +{ + unsigned int led_zones[THRUSTMASTER_SOL_R_ZONE_COUNT]; + RGBColor led_colors[THRUSTMASTER_SOL_R_ZONE_COUNT]; + + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + led_zones[led_idx] = leds[led_idx].value; + led_colors[led_idx] = colors[led_idx]; + } + + controller->SetLEDColors(led_zones, led_colors, static_cast(leds.size())); +} + +void RGBController_ThrustmasterSol::UpdateZoneLEDs(int zone) +{ + unsigned int start_idx = 0; + unsigned int zone_size = 0; + + for(unsigned int z_idx = 0; z_idx < zones.size(); z_idx++) + { + if(z_idx == (unsigned int)zone) + { + zone_size = zones[z_idx].leds_count; + break; + } + + start_idx += zones[z_idx].leds_count; + } + + unsigned int led_zones[THRUSTMASTER_SOL_R_ZONE_COUNT]; + RGBColor led_colors[THRUSTMASTER_SOL_R_ZONE_COUNT]; + + for(unsigned int led_idx = 0; led_idx < zone_size; led_idx++) + { + unsigned int current_idx = start_idx + led_idx; + led_zones[led_idx] = leds[current_idx].value; + led_colors[led_idx] = colors[current_idx]; + } + + controller->SetLEDColors(led_zones, led_colors, zone_size); +} + +void RGBController_ThrustmasterSol::UpdateSingleLED(int led) +{ + controller->SetLEDColor(leds[led].value, colors[led]); +} + +void RGBController_ThrustmasterSol::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_ThrustmasterSol::DeviceSaveMode() +{ + unsigned int led_zones[THRUSTMASTER_SOL_R_ZONE_COUNT]; + RGBColor led_colors[THRUSTMASTER_SOL_R_ZONE_COUNT]; + + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + led_zones[led_idx] = leds[led_idx].value; + led_colors[led_idx] = colors[led_idx]; + } + + controller->SaveColors(led_zones, led_colors, static_cast(leds.size())); +} diff --git a/Controllers/ThrustmasterSolController/RGBController_ThrustmasterSol.h b/Controllers/ThrustmasterSolController/RGBController_ThrustmasterSol.h new file mode 100644 index 0000000..2fa9f5c --- /dev/null +++ b/Controllers/ThrustmasterSolController/RGBController_ThrustmasterSol.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_ThrustmasterSol.h | +| | +| RGBController for Thrustmaster Sol series joysticks | +| | +| Ken Sanislo 02 Apr 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ThrustmasterSolController.h" + +class RGBController_ThrustmasterSol : public RGBController +{ +public: + RGBController_ThrustmasterSol(ThrustmasterSolController* controller_ptr); + ~RGBController_ThrustmasterSol(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + ThrustmasterSolController* controller; +}; diff --git a/Controllers/ThrustmasterSolController/ThrustmasterSolController.cpp b/Controllers/ThrustmasterSolController/ThrustmasterSolController.cpp new file mode 100644 index 0000000..dc9c50f --- /dev/null +++ b/Controllers/ThrustmasterSolController/ThrustmasterSolController.cpp @@ -0,0 +1,315 @@ +/*---------------------------------------------------------*\ +| ThrustmasterSolController.cpp | +| | +| Driver for Thrustmaster Sol series joysticks | +| | +| Ken Sanislo 02 Apr 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "ThrustmasterSolController.h" +#include "StringUtils.h" + +ThrustmasterSolController::ThrustmasterSolController(libusb_device_handle* dev_handle, + const char* path, + unsigned short pid) +{ + dev = dev_handle; + this->pid = pid; + + location = "USB: "; + location += path; + + /*---------------------------------------------------------*\ + | Get serial number string descriptor | + \*---------------------------------------------------------*/ + libusb_device_descriptor desc; + libusb_get_device_descriptor(libusb_get_device(dev_handle), &desc); + + if(desc.iSerialNumber != 0) + { + unsigned char serial_str[256]; + int ret = libusb_get_string_descriptor_ascii(dev_handle, + desc.iSerialNumber, + serial_str, + sizeof(serial_str)); + if(ret > 0) + { + serial = std::string(reinterpret_cast(serial_str), ret); + } + } +} + +ThrustmasterSolController::~ThrustmasterSolController() +{ + if(dev != nullptr) + { + libusb_release_interface(dev, THRUSTMASTER_SOL_INTERFACE); + libusb_close(dev); + } +} + +std::string ThrustmasterSolController::GetDeviceLocation() +{ + return(location); +} + +std::string ThrustmasterSolController::GetSerialString() +{ + return(serial); +} + +unsigned short ThrustmasterSolController::GetPID() +{ + return(pid); +} + +void ThrustmasterSolController::SendPacket(unsigned char* packet, unsigned int size) +{ + if(dev == nullptr) + { + return; + } + + int actual_length = 0; + libusb_interrupt_transfer(dev, + THRUSTMASTER_SOL_ENDPOINT_OUT, + packet, + size, + &actual_length, + 1000); +} + +void ThrustmasterSolController::BuildAndSendPackets(unsigned int* zones, + RGBColor* colors, + unsigned int count, + bool persistent) +{ + unsigned char persist_flag = persistent ? THRUSTMASTER_SOL_PERSISTENT + : THRUSTMASTER_SOL_VOLATILE; + + /*---------------------------------------------------------*\ + | Separate grip zones from base zones. Zone 0x00 exists on | + | both the base (logo LED) and the grip (thumbstick LED). | + | The GRIP_FLAG bit selects the grip report type. | + \*---------------------------------------------------------*/ + unsigned char base_zones[THRUSTMASTER_SOL_R_ZONE_COUNT]; + RGBColor base_colors[THRUSTMASTER_SOL_R_ZONE_COUNT]; + unsigned int base_count = 0; + + for(unsigned int i = 0; i < count; i++) + { + if(zones[i] & THRUSTMASTER_SOL_GRIP_FLAG) + { + /*-------------------------------------------------*\ + | Send grip packet for thumbstick immediately | + \*-------------------------------------------------*/ + unsigned char zone_id = zones[i] & 0xFF; + + unsigned char packet[THRUSTMASTER_SOL_PACKET_SIZE]; + memset(packet, 0x00, THRUSTMASTER_SOL_PACKET_SIZE); + + packet[0] = THRUSTMASTER_SOL_REPORT_GRIP_LO; + packet[1] = THRUSTMASTER_SOL_REPORT_GRIP_HI; + packet[2] = persist_flag | 0x01; + packet[3] = THRUSTMASTER_SOL_MARKER; + packet[4] = zone_id; + packet[5] = RGBGetRValue(colors[i]); + packet[6] = RGBGetGValue(colors[i]); + packet[7] = RGBGetBValue(colors[i]); + + SendPacket(packet, THRUSTMASTER_SOL_PACKET_SIZE); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + else + { + base_zones[base_count] = zones[i] & 0xFF; + base_colors[base_count] = colors[i]; + base_count++; + } + } + + /*---------------------------------------------------------*\ + | Send base zone packets, batching up to 15 entries each | + \*---------------------------------------------------------*/ + for(unsigned int offset = 0; offset < base_count; offset += THRUSTMASTER_SOL_MAX_ENTRIES) + { + unsigned int entries = base_count - offset; + + if(entries > THRUSTMASTER_SOL_MAX_ENTRIES) + { + entries = THRUSTMASTER_SOL_MAX_ENTRIES; + } + + unsigned char packet[THRUSTMASTER_SOL_PACKET_SIZE]; + memset(packet, 0x00, THRUSTMASTER_SOL_PACKET_SIZE); + + packet[0] = THRUSTMASTER_SOL_REPORT_BASE_LO; + packet[1] = THRUSTMASTER_SOL_REPORT_BASE_HI; + packet[2] = persist_flag | (unsigned char)entries; + packet[3] = THRUSTMASTER_SOL_MARKER; + + for(unsigned int j = 0; j < entries; j++) + { + unsigned int idx = offset + j; + packet[4 + j * 4] = base_zones[idx]; + packet[4 + j * 4 + 1] = RGBGetRValue(base_colors[idx]); + packet[4 + j * 4 + 2] = RGBGetGValue(base_colors[idx]); + packet[4 + j * 4 + 3] = RGBGetBValue(base_colors[idx]); + } + + SendPacket(packet, THRUSTMASTER_SOL_PACKET_SIZE); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } +} + +void ThrustmasterSolController::SetLEDColor(unsigned int zone, RGBColor color) +{ + BuildAndSendPackets(&zone, &color, 1, false); +} + +void ThrustmasterSolController::SetLEDColors(unsigned int* zones, + RGBColor* colors, + unsigned int count) +{ + BuildAndSendPackets(zones, colors, count, false); +} + +void ThrustmasterSolController::SaveColors(unsigned int* zones, + RGBColor* colors, + unsigned int count) +{ + BuildAndSendPackets(zones, colors, count, true); +} + +void ThrustmasterSolController::ReadColors(std::vector& zones, + std::vector& colors) +{ + if(dev == nullptr) + { + return; + } + + zones.clear(); + colors.clear(); + + /*---------------------------------------------------------*\ + | Read base zones (report 0x0002). May require multiple | + | queries if more than 14 zones are returned per response. | + | Zone IDs are returned without the grip flag, so they | + | match the base LED values (including zone 0x00 = logo). | + \*---------------------------------------------------------*/ + unsigned char start = 0; + + for(int page = 0; page < 4; page++) + { + unsigned char query[THRUSTMASTER_SOL_PACKET_SIZE]; + memset(query, 0x00, THRUSTMASTER_SOL_PACKET_SIZE); + + query[0] = THRUSTMASTER_SOL_READ_BASE_LO; + query[1] = THRUSTMASTER_SOL_READ_BASE_HI; + query[2] = start; + + SendPacket(query, THRUSTMASTER_SOL_PACKET_SIZE); + + unsigned char resp[THRUSTMASTER_SOL_PACKET_SIZE]; + int actual = 0; + int ret = libusb_interrupt_transfer(dev, + THRUSTMASTER_SOL_ENDPOINT_IN, + resp, + THRUSTMASTER_SOL_PACKET_SIZE, + &actual, + 1000); + + if(ret != LIBUSB_SUCCESS || actual < 4) + { + break; + } + + unsigned int n = resp[2] & 0x0F; + + if(n == 0) + { + break; + } + + unsigned char last = start; + + for(unsigned int i = 0; i < n; i++) + { + unsigned int off = 4 + i * 4; + + if(off + 3 >= (unsigned int)actual) + { + break; + } + + unsigned char zid = resp[off]; + unsigned char r = resp[off + 1]; + unsigned char g = resp[off + 2]; + unsigned char b = resp[off + 3]; + + zones.push_back(zid); + colors.push_back(ToRGBColor(r, g, b)); + last = zid; + } + + start = last + 1; + + if(n < 14 || start > 0x20) + { + break; + } + } + + /*---------------------------------------------------------*\ + | Read grip zones (report 0x8002). Grip zone IDs are | + | returned with the GRIP_FLAG set so they match the | + | thumbstick LED value. They are added as new entries, | + | not overwriting base entries (zone 0x00 exists on both). | + \*---------------------------------------------------------*/ + unsigned char grip_query[THRUSTMASTER_SOL_PACKET_SIZE]; + memset(grip_query, 0x00, THRUSTMASTER_SOL_PACKET_SIZE); + + grip_query[0] = THRUSTMASTER_SOL_READ_GRIP_LO; + grip_query[1] = THRUSTMASTER_SOL_READ_GRIP_HI; + + SendPacket(grip_query, THRUSTMASTER_SOL_PACKET_SIZE); + + unsigned char grip_resp[THRUSTMASTER_SOL_PACKET_SIZE]; + int grip_actual = 0; + int grip_ret = libusb_interrupt_transfer(dev, + THRUSTMASTER_SOL_ENDPOINT_IN, + grip_resp, + THRUSTMASTER_SOL_PACKET_SIZE, + &grip_actual, + 1000); + + if(grip_ret == LIBUSB_SUCCESS && grip_actual >= 8) + { + unsigned int grip_n = grip_resp[2] & 0x0F; + + for(unsigned int i = 0; i < grip_n; i++) + { + unsigned int off = 4 + i * 4; + + if(off + 3 >= (unsigned int)grip_actual) + { + break; + } + + unsigned char zid = grip_resp[off]; + unsigned char r = grip_resp[off + 1]; + unsigned char g = grip_resp[off + 2]; + unsigned char b = grip_resp[off + 3]; + + zones.push_back(THRUSTMASTER_SOL_GRIP_FLAG | zid); + colors.push_back(ToRGBColor(r, g, b)); + } + } +} diff --git a/Controllers/ThrustmasterSolController/ThrustmasterSolController.h b/Controllers/ThrustmasterSolController/ThrustmasterSolController.h new file mode 100644 index 0000000..738a97e --- /dev/null +++ b/Controllers/ThrustmasterSolController/ThrustmasterSolController.h @@ -0,0 +1,116 @@ +/*---------------------------------------------------------*\ +| ThrustmasterSolController.h | +| | +| Driver for Thrustmaster Sol series joysticks | +| | +| Ken Sanislo 02 Apr 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#ifdef _WIN32 +#include "dependencies/libusb-1.0.27/include/libusb.h" +#else +#include +#endif + +/*-----------------------------------------------------*\ +| Thrustmaster vendor ID | +\*-----------------------------------------------------*/ +#define THRUSTMASTER_VID 0x044F + +/*-----------------------------------------------------*\ +| Thrustmaster Sol series product IDs | +\*-----------------------------------------------------*/ +#define THRUSTMASTER_SOL_R_RIGHT_PID 0x0422 +#define THRUSTMASTER_SOL_R_LEFT_PID 0x042A +#define THRUSTMASTER_SOL_F16_RIGHT_PID 0x0420 +#define THRUSTMASTER_SOL_F18_RIGHT_PID 0x0421 +#define THRUSTMASTER_SOL_F16_LEFT_PID 0x0428 +#define THRUSTMASTER_SOL_F18_LEFT_PID 0x0429 + +/*-----------------------------------------------------*\ +| Thrustmaster Sol USB constants | +\*-----------------------------------------------------*/ +#define THRUSTMASTER_SOL_INTERFACE 1 +#define THRUSTMASTER_SOL_ENDPOINT_OUT 0x02 +#define THRUSTMASTER_SOL_ENDPOINT_IN 0x82 +#define THRUSTMASTER_SOL_PACKET_SIZE 64 +#define THRUSTMASTER_SOL_MAX_ENTRIES 15 + +/*-----------------------------------------------------*\ +| Thrustmaster Sol report type identifiers | +\*-----------------------------------------------------*/ +#define THRUSTMASTER_SOL_REPORT_BASE_LO 0x01 +#define THRUSTMASTER_SOL_REPORT_BASE_HI 0x08 +#define THRUSTMASTER_SOL_REPORT_GRIP_LO 0x01 +#define THRUSTMASTER_SOL_REPORT_GRIP_HI 0x88 + +/*-----------------------------------------------------*\ +| Thrustmaster Sol persistence flags | +\*-----------------------------------------------------*/ +#define THRUSTMASTER_SOL_VOLATILE 0x00 +#define THRUSTMASTER_SOL_PERSISTENT 0x80 + +/*-----------------------------------------------------*\ +| Thrustmaster Sol read query report types | +\*-----------------------------------------------------*/ +#define THRUSTMASTER_SOL_READ_BASE_LO 0x02 +#define THRUSTMASTER_SOL_READ_BASE_HI 0x00 +#define THRUSTMASTER_SOL_READ_GRIP_LO 0x02 +#define THRUSTMASTER_SOL_READ_GRIP_HI 0x80 + +/*-----------------------------------------------------*\ +| Thrustmaster Sol zone constants | +| | +| Zone 0x00 is shared: on the base it is a logo LED, | +| on the grip it is the thumbstick LED. The grip flag | +| (bit 8) selects the grip report type for zone 0x00. | +\*-----------------------------------------------------*/ +#define THRUSTMASTER_SOL_ZONE_THUMBSTICK 0x00 +#define THRUSTMASTER_SOL_GRIP_FLAG 0x100 +#define THRUSTMASTER_SOL_MARKER 0xFF + +/*-----------------------------------------------------*\ +| Thrustmaster Sol zone count for Sol-R | +| 19 base zones + thumbstick + logo zone 0x00 = 21 | +\*-----------------------------------------------------*/ +#define THRUSTMASTER_SOL_R_ZONE_COUNT 21 + +class ThrustmasterSolController +{ +public: + ThrustmasterSolController(libusb_device_handle* dev_handle, + const char* path, + unsigned short pid); + ~ThrustmasterSolController(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + unsigned short GetPID(); + + void SetLEDColor(unsigned int zone, RGBColor color); + void SetLEDColors(unsigned int* zones, RGBColor* colors, + unsigned int count); + void SaveColors(unsigned int* zones, RGBColor* colors, + unsigned int count); + void ReadColors(std::vector& zones, + std::vector& colors); + +private: + libusb_device_handle* dev; + std::string location; + std::string serial; + unsigned short pid; + + void SendPacket(unsigned char* packet, unsigned int size); + void BuildAndSendPackets(unsigned int* zones, RGBColor* colors, + unsigned int count, bool persistent); +}; diff --git a/Controllers/ThrustmasterSolController/ThrustmasterSolControllerDetect.cpp b/Controllers/ThrustmasterSolController/ThrustmasterSolControllerDetect.cpp new file mode 100644 index 0000000..632f013 --- /dev/null +++ b/Controllers/ThrustmasterSolController/ThrustmasterSolControllerDetect.cpp @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| ThrustmasterSolControllerDetect.cpp | +| | +| Detector for Thrustmaster Sol series joysticks | +| | +| Ken Sanislo 02 Apr 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ThrustmasterSolController.h" +#include "RGBController_ThrustmasterSol.h" + +typedef struct +{ + unsigned short usb_vid; + unsigned short usb_pid; + const char* name; +} thrustmaster_sol_device; + +#define THRUSTMASTER_SOL_NUM_DEVICES (sizeof(device_list) / sizeof(device_list[0])) + +static const thrustmaster_sol_device device_list[] = +{ + /*-----------------------------------------------------------------------------------------------------*\ + | Sol-R variants (tested) | + \*-----------------------------------------------------------------------------------------------------*/ + { THRUSTMASTER_VID, THRUSTMASTER_SOL_R_RIGHT_PID, "Thrustmaster Sol-R Right" }, + { THRUSTMASTER_VID, THRUSTMASTER_SOL_R_LEFT_PID, "Thrustmaster Sol-R Left" }, + /*-----------------------------------------------------------------------------------------------------*\ + | Sol F16/F18 variants (untested, same base protocol) | + \*-----------------------------------------------------------------------------------------------------*/ + { THRUSTMASTER_VID, THRUSTMASTER_SOL_F16_RIGHT_PID, "Thrustmaster Sol F16 Right" }, + { THRUSTMASTER_VID, THRUSTMASTER_SOL_F18_RIGHT_PID, "Thrustmaster Sol F18 Right" }, + { THRUSTMASTER_VID, THRUSTMASTER_SOL_F16_LEFT_PID, "Thrustmaster Sol F16 Left" }, + { THRUSTMASTER_VID, THRUSTMASTER_SOL_F18_LEFT_PID, "Thrustmaster Sol F18 Left" }, +}; + +/******************************************************************************************\ +* * +* DetectThrustmasterSolControllers * +* * +* Detect Thrustmaster Sol series joysticks via libusb. * +* LED control is on USB interface 1 (vendor-specific class 255, not HID). * +* Interface 0 (joystick HID) is not touched. * +* * +\******************************************************************************************/ + +void DetectThrustmasterSolControllers() +{ + libusb_init(NULL); + + #ifdef _WIN32 + libusb_set_option(NULL, LIBUSB_OPTION_USE_USBDK); + #endif + + libusb_device** devs; + ssize_t num_devs = libusb_get_device_list(NULL, &devs); + + if(num_devs <= 0) + { + return; + } + + for(ssize_t i = 0; i < num_devs; i++) + { + libusb_device_descriptor desc; + + if(libusb_get_device_descriptor(devs[i], &desc) != 0) + { + continue; + } + + for(std::size_t d = 0; d < THRUSTMASTER_SOL_NUM_DEVICES; d++) + { + if(desc.idVendor == device_list[d].usb_vid && + desc.idProduct == device_list[d].usb_pid) + { + libusb_device_handle* handle = NULL; + + if(libusb_open(devs[i], &handle) != LIBUSB_SUCCESS) + { + continue; + } + + libusb_set_auto_detach_kernel_driver(handle, 1); + + if(libusb_claim_interface(handle, THRUSTMASTER_SOL_INTERFACE) != LIBUSB_SUCCESS) + { + libusb_close(handle); + continue; + } + + uint8_t bus = libusb_get_bus_number(devs[i]); + uint8_t address = libusb_get_device_address(devs[i]); + char path[32]; + snprintf(path, sizeof(path), "%d-%d", bus, address); + + ThrustmasterSolController* controller = new ThrustmasterSolController(handle, path, desc.idProduct); + RGBController_ThrustmasterSol* rgb_controller = new RGBController_ThrustmasterSol(controller); + + rgb_controller->name = device_list[d].name; + + ResourceManager::get()->RegisterRGBController(rgb_controller); + break; + } + } + } + + libusb_free_device_list(devs, 1); +} + +REGISTER_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0420 ) | +| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0421 ) | +| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0422 ) | +| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0428 ) | +| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x0429 ) | +| DUMMY_DEVICE_DETECTOR("Thrustmaster Sol", DetectThrustmasterSolControllers, 0x044F, 0x042A ) | +\*---------------------------------------------------------------------------------------------------------*/ diff --git a/Controllers/TrustController/TrustControllerDetect.cpp b/Controllers/TrustController/TrustControllerDetect.cpp new file mode 100644 index 0000000..436e0ac --- /dev/null +++ b/Controllers/TrustController/TrustControllerDetect.cpp @@ -0,0 +1,64 @@ +/*---------------------------------------------------------*\ +| TrustControllerDetect.cpp | +| | +| Detector for Trust devices | +| | +| Morgan Guimard (morg) 24 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "TrustGXT114Controller.h" +#include "TrustGXT180Controller.h" +#include "RGBController_TrustGXT114.h" +#include "RGBController_TrustGXT180.h" + +/*---------------------------------------------------------*\ +| Trust vendor ID | +\*---------------------------------------------------------*/ +#define TRUST_VID 0x145F + +/*---------------------------------------------------------*\ +| Product IDs | +\*---------------------------------------------------------*/ +#define TRUST_GXT_114_PID 0x026D +#define TRUST_GXT_180_PID 0x0248 + +void DetectTrustGXT114Controllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + TrustGXT114Controller* controller = new TrustGXT114Controller(dev, *info, name); + + if(controller->Test()) + { + RGBController_TrustGXT114* rgb_controller = new RGBController_TrustGXT114(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + delete controller; + } + } +} + +void DetectTrustGXT180Controllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + TrustGXT180Controller* controller = new TrustGXT180Controller(dev, *info, name); + RGBController_TrustGXT180* rgb_controller = new RGBController_TrustGXT180(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("Trust GXT 114", DetectTrustGXT114Controllers, TRUST_VID, TRUST_GXT_114_PID, 1, 0xFF00, 1); +REGISTER_HID_DETECTOR_IPU("Trust GXT 180", DetectTrustGXT180Controllers, TRUST_VID, TRUST_GXT_180_PID, 1, 0xFFA0, 1); diff --git a/Controllers/TrustController/TrustGXT114Controller/RGBController_TrustGXT114.cpp b/Controllers/TrustController/TrustGXT114Controller/RGBController_TrustGXT114.cpp new file mode 100644 index 0000000..668366b --- /dev/null +++ b/Controllers/TrustController/TrustGXT114Controller/RGBController_TrustGXT114.cpp @@ -0,0 +1,133 @@ +/*---------------------------------------------------------*\ +| RGBController_TrustGXT114.cpp | +| | +| RGBController for Trust GXT 114 | +| | +| Morgan Guimard (morg) 24 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_TrustGXT114.h" + +/**------------------------------------------------------------------*\ + @name Trust GXT 114 + @category Mouse + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectTrustGXT114Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_TrustGXT114::RGBController_TrustGXT114(TrustGXT114Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Trust"; + type = DEVICE_TYPE_MOUSE; + description = "Trust GXT 114 Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.value = STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.brightness_min = TRUST_GXT_114_BRIGHTNESS_MIN; + Static.brightness_max = TRUST_GXT_114_BRIGHTNESS_MAX; + Static.brightness = TRUST_GXT_114_BRIGHTNESS_MAX; + Static.colors.resize(1); + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.speed_min = TRUST_GXT_114_SPEED_MIN; + Breathing.speed_max = TRUST_GXT_114_SPEED_MAX; + Breathing.speed = TRUST_GXT_114_SPEED_MIN; + Breathing.colors.resize(1); + modes.push_back(Breathing); + + mode Blink; + Blink.name = "Blink"; + Blink.value = BLINK_MODE_VALUE; + Blink.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_AUTOMATIC_SAVE; + Blink.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blink.colors_min = 1; + Blink.colors_max = 1; + Blink.speed_min = TRUST_GXT_114_SPEED_MIN; + Blink.speed_max = TRUST_GXT_114_SPEED_MAX; + Blink.speed = TRUST_GXT_114_SPEED_MIN; + Blink.colors.resize(1); + modes.push_back(Blink); + + SetupZones(); +} + +RGBController_TrustGXT114::~RGBController_TrustGXT114() +{ + delete controller; +} + +void RGBController_TrustGXT114::SetupZones() +{ + zone new_zone; + + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = TRUST_GXT_114_NUMBER_OF_LEDS; + new_zone.leds_max = TRUST_GXT_114_NUMBER_OF_LEDS; + new_zone.leds_count = TRUST_GXT_114_NUMBER_OF_LEDS; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(new_zone.leds_count); + + for(unsigned int i = 0; i < TRUST_GXT_114_NUMBER_OF_LEDS; i++) + { + leds[i].name = "LED " + std::to_string(i); + } + + SetupColors(); +} + +void RGBController_TrustGXT114::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_TrustGXT114::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_TrustGXT114::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_TrustGXT114::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_TrustGXT114::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode].colors[0], modes[active_mode].brightness, modes[active_mode].speed, modes[active_mode].value); +} diff --git a/Controllers/TrustController/TrustGXT114Controller/RGBController_TrustGXT114.h b/Controllers/TrustController/TrustGXT114Controller/RGBController_TrustGXT114.h new file mode 100644 index 0000000..380a032 --- /dev/null +++ b/Controllers/TrustController/TrustGXT114Controller/RGBController_TrustGXT114.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_TrustGXT114.h | +| | +| RGBController for Trust GXT 114 | +| | +| Morgan Guimard (morg) 24 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "TrustGXT114Controller.h" + +class RGBController_TrustGXT114 : public RGBController +{ +public: + RGBController_TrustGXT114(TrustGXT114Controller* controller_ptr); + ~RGBController_TrustGXT114(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + TrustGXT114Controller* controller; +}; diff --git a/Controllers/TrustController/TrustGXT114Controller/TrustGXT114Controller.cpp b/Controllers/TrustController/TrustGXT114Controller/TrustGXT114Controller.cpp new file mode 100644 index 0000000..18a924d --- /dev/null +++ b/Controllers/TrustController/TrustGXT114Controller/TrustGXT114Controller.cpp @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| TrustGXT114Controller.cpp | +| | +| Driver for Trust GXT 114 | +| | +| Morgan Guimard (morg) 24 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "TrustGXT114Controller.h" + +TrustGXT114Controller::TrustGXT114Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +TrustGXT114Controller::~TrustGXT114Controller() +{ + hid_close(dev); +} + +std::string TrustGXT114Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string TrustGXT114Controller::GetNameString() +{ + return(name); +} + +std::string TrustGXT114Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +bool TrustGXT114Controller::Test() +{ + /*-----------------------------------------*\ + | Send a get feature report, filtering out | + | hid devices that do not anwser. | + \*-----------------------------------------*/ + uint8_t usb_buf[TRUST_GXT_114_REPORT_SIZE] = { TRUST_GXT_114_REPORT_ID }; + return hid_get_feature_report(dev, usb_buf, TRUST_GXT_114_REPORT_SIZE) > 0; +} + +void TrustGXT114Controller::SetMode(RGBColor color, unsigned char brightness, unsigned char speed, unsigned char mode_value) +{ + unsigned char speed_bright = mode_value == STATIC_MODE_VALUE ? brightness : speed; + + /*-----------------------------------------*\ + | Create and zero out the buffer | + \*-----------------------------------------*/ + unsigned char usb_buf[TRUST_GXT_114_REPORT_SIZE]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------*\ + | Fill dynamic data | + \*-----------------------------------------*/ + usb_buf[0] = TRUST_GXT_114_REPORT_ID; + + usb_buf[93] = mode_value; + usb_buf[94] = 0x00; // freq (extra param, let's default it) + usb_buf[95] = 0x00; // times (extra param, let's default it) + usb_buf[96] = speed_bright; // speed or brightness depending on the mode + + usb_buf[103] = RGBGetRValue(color); // r + usb_buf[104] = RGBGetGValue(color); // g + usb_buf[105] = RGBGetBValue(color); // b + + /*-----------------------------------------*\ + | Fill the constant data bytes | + \*-----------------------------------------*/ + for(unsigned int i = 79 ; i <= 89; i++) + { + usb_buf[i] = 0x80; + } + + std::vector values_FF = + { + 101, 108, 109, 113, 114, 115, 117, 118, 119, 124, 127, 128, + 131, 134, 135, 138, 139, 141, 142, 143, 144 + }; + + for(unsigned i = 0; i < values_FF.size(); i ++) + { + usb_buf[values_FF[i]] = 0xff; + } + + usb_buf[8] = 0x4c; // constant data + + usb_buf[71] = 0x11; // constant data + usb_buf[72] = 0xb0; // constant data + usb_buf[74] = 0x89; // constant data + usb_buf[75] = 0x0e; // constant data + usb_buf[76] = 0x9d; // constant data + usb_buf[77] = 0xa7; // constant data + usb_buf[78] = 0xb7; // constant data + + usb_buf[148] = 0x58; // constant data + usb_buf[149] = 0x30; // constant data + usb_buf[150] = 0x31; // constant data + usb_buf[151] = 0x30; // constant data + usb_buf[152] = 0x31; // constant data + usb_buf[153] = 0x30; // constant data + + /*-----------------------------------------*\ + | Send the feature report | + \*-----------------------------------------*/ + hid_send_feature_report(dev, usb_buf, TRUST_GXT_114_REPORT_SIZE); +} diff --git a/Controllers/TrustController/TrustGXT114Controller/TrustGXT114Controller.h b/Controllers/TrustController/TrustGXT114Controller/TrustGXT114Controller.h new file mode 100644 index 0000000..793f379 --- /dev/null +++ b/Controllers/TrustController/TrustGXT114Controller/TrustGXT114Controller.h @@ -0,0 +1,60 @@ +/*---------------------------------------------------------*\ +| TrustGXT114Controller.h | +| | +| Driver for Trust GXT 114 | +| | +| Morgan Guimard (morg) 24 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define TRUST_GXT_114_REPORT_SIZE 154 +#define TRUST_GXT_114_NUMBER_OF_LEDS 1 +#define TRUST_GXT_114_REPORT_ID 0x04 + +enum +{ + STATIC_MODE_VALUE = 0x28, + BREATHING_MODE_VALUE = 0x22, + BLINK_MODE_VALUE = 0x42 +}; + +enum +{ + TRUST_GXT_114_BRIGHTNESS_MIN = 0x12, + TRUST_GXT_114_BRIGHTNESS_MAX = 0xA2 +}; + +enum +{ + TRUST_GXT_114_SPEED_MIN = 0x12, + TRUST_GXT_114_SPEED_MAX = 0x62 +}; + +class TrustGXT114Controller +{ +public: + TrustGXT114Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~TrustGXT114Controller(); + + std::string GetSerialString(); + std::string GetDeviceLocation(); + std::string GetNameString(); + + bool Test(); + void SetMode(RGBColor color, unsigned char brightness, unsigned char speed, unsigned char mode_value); + +protected: + hid_device* dev; + +private: + std::string name; + std::string location; +}; diff --git a/Controllers/TrustController/TrustGXT180Controller/RGBController_TrustGXT180.cpp b/Controllers/TrustController/TrustGXT180Controller/RGBController_TrustGXT180.cpp new file mode 100644 index 0000000..1f030b6 --- /dev/null +++ b/Controllers/TrustController/TrustGXT180Controller/RGBController_TrustGXT180.cpp @@ -0,0 +1,137 @@ +/*---------------------------------------------------------*\ +| RGBController_TrustGXT180.cpp | +| | +| RGBController for Trust GXT 180 | +| | +| Morgan Guimard (morg) 24 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "RGBController_TrustGXT180.h" + +/**------------------------------------------------------------------*\ + @name Trust GXT 180 + @category Mouse + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectTrustGXT180Controllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_TrustGXT180::RGBController_TrustGXT180(TrustGXT180Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Trust"; + type = DEVICE_TYPE_MOUSE; + description = "Trust GXT 180 Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Static; + Static.name = "Static"; + Static.value = TRUST_GXT_180_STATIC_MODE_VALUE; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_PER_LED; + Static.brightness_min = TRUST_GXT_180_BRIGHTNESS_MIN; + Static.brightness_max = TRUST_GXT_180_BRIGHTNESS_MAX; + Static.brightness = TRUST_GXT_180_BRIGHTNESS_MAX; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = TRUST_GXT_180_BREATHING_MODE_VALUE; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = TRUST_GXT_180_SPEED_MIN; + Breathing.speed_max = TRUST_GXT_180_SPEED_MAX; + Breathing.speed = TRUST_GXT_180_SPEED_MIN; + Breathing.brightness_min = TRUST_GXT_180_BRIGHTNESS_MIN; + Breathing.brightness_max = TRUST_GXT_180_BRIGHTNESS_MAX; + Breathing.brightness = TRUST_GXT_180_BRIGHTNESS_MAX; + modes.push_back(Breathing); + + mode ColorShift; + ColorShift.name = "ColorShift"; + ColorShift.value = TRUST_GXT_180_COLORSHIFT_MODE_VALUE; + ColorShift.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + ColorShift.color_mode = MODE_COLORS_RANDOM; + ColorShift.speed_min = TRUST_GXT_180_SPEED_MIN; + ColorShift.speed_max = TRUST_GXT_180_SPEED_MAX; + ColorShift.speed = TRUST_GXT_180_SPEED_MIN; + ColorShift.brightness_min = TRUST_GXT_180_BRIGHTNESS_MIN; + ColorShift.brightness_max = TRUST_GXT_180_BRIGHTNESS_MAX; + ColorShift.brightness = TRUST_GXT_180_BRIGHTNESS_MAX; + modes.push_back(ColorShift); + + mode Off; + Off.name = "Off"; + Off.value = TRUST_GXT_180_OFF_MODE_VALUE; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_TrustGXT180::~RGBController_TrustGXT180() +{ + delete controller; +} + +void RGBController_TrustGXT180::SetupZones() +{ + zone new_zone; + + new_zone.name = "Mouse"; + new_zone.type = ZONE_TYPE_LINEAR; + new_zone.leds_min = TRUST_GXT_180_NUMBER_OF_LEDS; + new_zone.leds_max = TRUST_GXT_180_NUMBER_OF_LEDS; + new_zone.leds_count = TRUST_GXT_180_NUMBER_OF_LEDS; + new_zone.matrix_map = nullptr; + + zones.emplace_back(new_zone); + + leds.resize(new_zone.leds_count); + + for(unsigned int i = 0; i < TRUST_GXT_180_NUMBER_OF_LEDS; i++) + { + leds[i].name = "LED " + std::to_string(i + 1); + } + + SetupColors(); +} + +void RGBController_TrustGXT180::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_TrustGXT180::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_TrustGXT180::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_TrustGXT180::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_TrustGXT180::DeviceUpdateMode() +{ + controller->SetMode(colors[0], modes[active_mode].brightness, modes[active_mode].speed, modes[active_mode].value); +} diff --git a/Controllers/TrustController/TrustGXT180Controller/RGBController_TrustGXT180.h b/Controllers/TrustController/TrustGXT180Controller/RGBController_TrustGXT180.h new file mode 100644 index 0000000..3c2dbde --- /dev/null +++ b/Controllers/TrustController/TrustGXT180Controller/RGBController_TrustGXT180.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_TrustGXT180.h | +| | +| RGBController for Trust GXT 180 | +| | +| Morgan Guimard (morg) 24 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "TrustGXT180Controller.h" + +class RGBController_TrustGXT180 : public RGBController +{ +public: + RGBController_TrustGXT180(TrustGXT180Controller* controller_ptr); + ~RGBController_TrustGXT180(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + TrustGXT180Controller* controller; +}; diff --git a/Controllers/TrustController/TrustGXT180Controller/TrustGXT180Controller.cpp b/Controllers/TrustController/TrustGXT180Controller/TrustGXT180Controller.cpp new file mode 100644 index 0000000..3d77341 --- /dev/null +++ b/Controllers/TrustController/TrustGXT180Controller/TrustGXT180Controller.cpp @@ -0,0 +1,81 @@ +/*---------------------------------------------------------*\ +| TrustGXT180Controller.cpp | +| | +| Driver for Trust GXT 180 | +| | +| Morgan Guimard (morg) 24 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "TrustGXT180Controller.h" + +TrustGXT180Controller::TrustGXT180Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +{ + dev = dev_handle; + location = info.path; + name = dev_name; +} + +TrustGXT180Controller::~TrustGXT180Controller() +{ + hid_close(dev); +} + +std::string TrustGXT180Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string TrustGXT180Controller::GetNameString() +{ + return(name); +} + +std::string TrustGXT180Controller::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void TrustGXT180Controller::SetMode(RGBColor color, unsigned char brightness, unsigned char speed, unsigned char mode_value) +{ + /*-----------------------------------------*\ + | Create and zero out the buffer | + \*-----------------------------------------*/ + unsigned char usb_buf[TRUST_GXT_180_REPORT_SIZE]; + memset(usb_buf, 0x00, sizeof(usb_buf)); + + usb_buf[0] = TRUST_GXT_180_REPORT_ID; + usb_buf[1] = 0x06; + usb_buf[2] = 0xBB; + usb_buf[3] = 0xAA; + usb_buf[4] = 0x2A; + usb_buf[6] = 0x0A; + + if(mode_value != TRUST_GXT_180_OFF_MODE_VALUE) + { + usb_buf[8] = RGBGetRValue(color); + usb_buf[9] = RGBGetGValue(color); + usb_buf[10] = RGBGetBValue(color); + } + + usb_buf[11] = mode_value; + usb_buf[13] = brightness; + usb_buf[14] = speed; + + /*-----------------------------------------*\ + | Send the feature report | + \*-----------------------------------------*/ + hid_send_feature_report(dev, usb_buf, TRUST_GXT_180_REPORT_SIZE); +} diff --git a/Controllers/TrustController/TrustGXT180Controller/TrustGXT180Controller.h b/Controllers/TrustController/TrustGXT180Controller/TrustGXT180Controller.h new file mode 100644 index 0000000..5e2663a --- /dev/null +++ b/Controllers/TrustController/TrustGXT180Controller/TrustGXT180Controller.h @@ -0,0 +1,60 @@ +/*---------------------------------------------------------*\ +| TrustGXT180Controller.h | +| | +| Driver for Trust GXT 180 | +| | +| Morgan Guimard (morg) 24 Mar 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define TRUST_GXT_180_REPORT_SIZE 64 +#define TRUST_GXT_180_NUMBER_OF_LEDS 1 +#define TRUST_GXT_180_REPORT_ID 0x03 + +enum +{ + TRUST_GXT_180_STATIC_MODE_VALUE = 0x01, + TRUST_GXT_180_BREATHING_MODE_VALUE = 0x02, + TRUST_GXT_180_COLORSHIFT_MODE_VALUE = 0x03, + TRUST_GXT_180_OFF_MODE_VALUE = 0x04 +}; + +enum +{ + TRUST_GXT_180_BRIGHTNESS_MIN = 0x00, + TRUST_GXT_180_BRIGHTNESS_MAX = 0x05 +}; + +enum +{ + TRUST_GXT_180_SPEED_MIN = 0x0A, + TRUST_GXT_180_SPEED_MAX = 0x00 +}; + +class TrustGXT180Controller +{ +public: + TrustGXT180Controller(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + ~TrustGXT180Controller(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetMode(RGBColor color, unsigned char brightness, unsigned char speed, unsigned char mode_value); + +protected: + hid_device* dev; + +private: + std::string location; + std::string name; +}; diff --git a/Controllers/ValkyrieKeyboardController/RGBController_ValkyrieKeyboard.cpp b/Controllers/ValkyrieKeyboardController/RGBController_ValkyrieKeyboard.cpp new file mode 100644 index 0000000..838843a --- /dev/null +++ b/Controllers/ValkyrieKeyboardController/RGBController_ValkyrieKeyboard.cpp @@ -0,0 +1,418 @@ +/*---------------------------------------------------------*\ +| RGBController_ValkyrieKeyboard.cpp | +| | +| RGBController for Valkyrie keyboard | +| | +| Nollie (Nuonuo) 06 Dec 2023 | +| Bartholomew Ho (imnotmental) 01 Feb 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_ValkyrieKeyboard.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[6][22] = + { { 0, NA, 1, 2, 3, 4, NA, 5, 6, 7, 8, NA, 9, 10, 11, 12, NA, 13, NA, NA, NA, NA }, + { 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, NA, NA, NA, 28, 29, 30, 31, 32 }, + { 33, NA, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, NA, NA, 47, 48, 49, 50, 51 }, + { 52, NA, NA, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, NA, 63, 64, NA, 65, 66, 67, 68, NA }, + { 69, NA, NA, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, NA, NA, 81, NA, 82, 83, 84, 85 }, + { 86, 87, 88, NA, NA, NA, NA, 89, NA, NA, NA, NA, 90, 91, 92, 93, 94, 95, NA, 96, 97, NA } }; + +static unsigned int normal_matrix_map[6][22] = + {{ 0, NA, 1, 2, 3, 4, NA, 5, 6, 7, 8, NA, 9, 10, 11, 12, NA, 13, 14, 15, 16, 17 }, + { 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, NA, NA, NA, 32, 33, 34, 35, 36 }, + { 37, NA, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, NA, NA, 51, 52, 53, 54, 55 }, + { 56, NA, NA, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, NA, 67, 68, NA, 69, 70, 71, 72, NA }, + { 73, NA, NA, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, NA, NA, 85, NA, 86, 87, 88, 89 }, + { 90, 91, 92, NA, NA, NA, NA, 93, NA, NA, NA, NA, 94, 95, 96, 97, 98, 99, NA, 100, 101, NA } }; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 98 +}; + +static const unsigned int normal_zone_sizes[] = +{ + 102 +}; + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_DELETE, + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_PLUS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_BACK_SLASH, + KEY_EN_PAGE_UP, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_PLUS, + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_ENTER, + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD +}; + +static const char *normal_led_names[] = + { + KEY_EN_ESCAPE, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_DELETE, + KEY_EN_PRINT_SCREEN, + KEY_EN_PAUSE_BREAK, + KEY_EN_HOME, + KEY_EN_END, + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_PLUS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_BACK_SLASH, + KEY_EN_PAGE_UP, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_PLUS, + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_ANSI_ENTER, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + KEY_EN_LEFT_SHIFT, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_ENTER, + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_SPACE, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD +}; +/**------------------------------------------------------------------*\ + @name Valkyrie + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectValkyrieKeyboardControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_ValkyrieKeyboard::RGBController_ValkyrieKeyboard(ValkyrieKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Valkyrie"; + type = DEVICE_TYPE_KEYBOARD; + description = "Valkyrie Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_ValkyrieKeyboard::~RGBController_ValkyrieKeyboard() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + break; + } + + delete controller; +} + +void RGBController_ValkyrieKeyboard::SetupZones() +{ + ValkyrieKeyboardMappingLayoutType layout; + switch(controller->GetInterfaceNum()) + { + case 3: + layout = PRO_LAYOUT; + break; + default: + layout = NORMAL_LAYOUT; + break; + } + + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for(unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + unsigned int zone_size = 0; + unsigned int matrix_width = 0; + unsigned int* matrix_map_ptr = NULL; + + switch(layout) + { + case PRO_LAYOUT: + zone_size = zone_sizes[zone_idx]; + matrix_width = 22; + matrix_map_ptr = (unsigned int *)&matrix_map; + break; + + default: + zone_size = normal_zone_sizes[zone_idx]; + matrix_width = 22; + matrix_map_ptr = (unsigned int *)&normal_matrix_map; + break; + } + + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_size; + new_zone.leds_max = zone_size; + new_zone.leds_count = zone_size; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = matrix_width; + new_zone.matrix_map->map = matrix_map_ptr; + zones.push_back(new_zone); + + total_led_count += zone_size; + } + + for(unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + + switch(layout) + { + case PRO_LAYOUT: + new_led.name = led_names[led_idx]; + break; + default: + new_led.name = normal_led_names[led_idx]; + break; + } + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_ValkyrieKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ValkyrieKeyboard::DeviceUpdateLEDs() +{ + unsigned char colordata[1024]; + + for(std::size_t color_idx = 0; color_idx < colors.size(); color_idx++) + { + colordata[(color_idx*3)+0] = RGBGetRValue(colors[color_idx]); + colordata[(color_idx*3)+1] = RGBGetGValue(colors[color_idx]); + colordata[(color_idx*3)+2] = RGBGetBValue(colors[color_idx]); + } + + controller->SendColors(colordata, sizeof(colordata)); +} + +void RGBController_ValkyrieKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ValkyrieKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ValkyrieKeyboard::DeviceUpdateMode() +{ + +} diff --git a/Controllers/ValkyrieKeyboardController/RGBController_ValkyrieKeyboard.h b/Controllers/ValkyrieKeyboardController/RGBController_ValkyrieKeyboard.h new file mode 100644 index 0000000..e76ceb7 --- /dev/null +++ b/Controllers/ValkyrieKeyboardController/RGBController_ValkyrieKeyboard.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| RGBController_ValkyrieKeyboard.h | +| | +| RGBController for Valkyrie keyboard | +| | +| Nollie (Nuonuo) 06 Dec 2023 | +| Bartholomew Ho (imnotmental) 01 Feb 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ValkyrieKeyboardController.h" + +enum ValkyrieKeyboardMappingLayoutType +{ + NORMAL_LAYOUT, + PRO_LAYOUT, +}; + +class RGBController_ValkyrieKeyboard : public RGBController +{ +public: + RGBController_ValkyrieKeyboard(ValkyrieKeyboardController* controller_ptr); + ~RGBController_ValkyrieKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + +private: + ValkyrieKeyboardController* controller; +}; diff --git a/Controllers/ValkyrieKeyboardController/ValkyrieKeyboardController.cpp b/Controllers/ValkyrieKeyboardController/ValkyrieKeyboardController.cpp new file mode 100644 index 0000000..c62332f --- /dev/null +++ b/Controllers/ValkyrieKeyboardController/ValkyrieKeyboardController.cpp @@ -0,0 +1,166 @@ +/*---------------------------------------------------------*\ +| ValkyrieKeyboardController.cpp | +| | +| Driver for Valkyrie keyboard | +| | +| Nollie (Nuonuo) 06 Dec 2023 | +| Bartholomew Ho (imnotmental) 01 Feb 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "ValkyrieKeyboardController.h" + +ValkyrieKeyboardController::ValkyrieKeyboardController(hid_device* dev_handle, const char* path, const unsigned short pid, const int interface, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + usb_pid = pid; + interface_num = interface; +} + +ValkyrieKeyboardController::~ValkyrieKeyboardController() +{ + hid_close(dev); +} + +std::string ValkyrieKeyboardController::GetDeviceLocation() +{ + return("HID: " + location); +} + +std::string ValkyrieKeyboardController::GetNameString() +{ + return(name); +} + +std::string ValkyrieKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned short ValkyrieKeyboardController::GetUSBPID() +{ + return(usb_pid); +} + +int ValkyrieKeyboardController::GetInterfaceNum() +{ + return(interface_num); +} + +void ValkyrieKeyboardController::SendColors(unsigned char* color_data, unsigned int /*color_data_size*/) +{ + unsigned char usb_buf_pro[392]; + unsigned char usb_buf_normal[408]; + int led_num = 0; + + switch(interface_num) + { + case 3: + led_num = 98; + for(int i = 0; i < led_num; i++) + { + usb_buf_pro[i * 4] = key_code_99[i]; + usb_buf_pro[i * 4 + 1] = color_data[i * 3]; + usb_buf_pro[i * 4 + 2] = color_data[i * 3 + 1]; + usb_buf_pro[i * 4 + 3] = color_data[i * 3 + 2]; + } + break; + default: + led_num = 102; + for(int i = 0; i < led_num; i++) + { + usb_buf_normal[i * 4] = key_code_103[i]; + usb_buf_normal[i * 4 + 1] = color_data[i * 3]; + usb_buf_normal[i * 4 + 2] = color_data[i * 3 + 1]; + usb_buf_normal[i * 4 + 3] = color_data[i * 3 + 2]; + } + } + + SendInitializeColorPacket(); + + for(int i = 0; i <= 6; i++) + { + unsigned int usb_data_num = 16; + if(i == 6) + { + usb_data_num = led_num - usb_data_num * 6; + } + char send_usb_buf[65]; + memset(send_usb_buf, 0x00, sizeof(send_usb_buf)); + + switch(interface_num) + { + case 3: + for(unsigned int index = 0; index < usb_data_num; index++) + { + send_usb_buf[index * 4 + 1] = usb_buf_pro[index * 4 + i * 64 ]; + send_usb_buf[index * 4 + 2] = usb_buf_pro[index * 4 + i * 64 + 1]; + send_usb_buf[index * 4 + 3] = usb_buf_pro[index * 4 + i * 64 + 2]; + send_usb_buf[index * 4 + 4] = usb_buf_pro[index * 4 + i * 64 + 3]; + } + break; + default: + for(unsigned int index = 0; index < usb_data_num; index++) + { + send_usb_buf[index * 4 + 1] = usb_buf_normal[index * 4 + i * 64 ]; + send_usb_buf[index * 4 + 2] = usb_buf_normal[index * 4 + i * 64 + 1]; + send_usb_buf[index * 4 + 3] = usb_buf_normal[index * 4 + i * 64 + 2]; + send_usb_buf[index * 4 + 4] = usb_buf_normal[index * 4 + i * 64 + 3]; + } + break; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_send_feature_report(dev, (unsigned char *)send_usb_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + SendTerminateColorPacket(); + std::this_thread::sleep_for(std::chrono::milliseconds(33)); +} + +void ValkyrieKeyboardController::SendInitializeColorPacket() +{ + uint8_t usb_write_buf[65]; + uint8_t usb_read_buf[65]; + memset(usb_write_buf, 0x00, sizeof(usb_write_buf)); + memset(usb_read_buf, 0x00, sizeof(usb_read_buf)); + usb_write_buf[1] = 0x04; + usb_write_buf[2] = 0x20; + usb_write_buf[9] = 0x08; + hid_send_feature_report(dev, (unsigned char *)usb_write_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + hid_get_feature_report (dev, (unsigned char *)usb_read_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); +} + +void ValkyrieKeyboardController::SendTerminateColorPacket() +{ + uint8_t usb_write_buf[65]; + uint8_t usb_read_buf[65]; + memset(usb_write_buf, 0x00, sizeof(usb_write_buf)); + memset(usb_read_buf, 0x00, sizeof(usb_read_buf)); + hid_send_feature_report(dev, (unsigned char *)usb_write_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + usb_write_buf[1] = 0x04; + usb_write_buf[2] = 0x02; + hid_send_feature_report(dev, (unsigned char *)usb_write_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + hid_get_feature_report (dev, (unsigned char *)usb_read_buf, 65); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); +} diff --git a/Controllers/ValkyrieKeyboardController/ValkyrieKeyboardController.h b/Controllers/ValkyrieKeyboardController/ValkyrieKeyboardController.h new file mode 100644 index 0000000..9d3c3e7 --- /dev/null +++ b/Controllers/ValkyrieKeyboardController/ValkyrieKeyboardController.h @@ -0,0 +1,78 @@ +/*---------------------------------------------------------*\ +| ValkyrieKeyboardController.h | +| | +| Driver for Valkyrie keyboard | +| | +| Nollie (Nuonuo) 06 Dec 2023 | +| Bartholomew Ho (imnotmental) 01 Feb 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +/*-----------------------------------------------------*\ +| Valkyrie vendor ID | +\*-----------------------------------------------------*/ +#define VALKYRIE_VID 0x05AC + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define VALKYRIE_99_PRO_PID 0x024F +#define VALKYRIE_99_NORMAL_PID 0x024F + +class ValkyrieKeyboardController +{ +public: + ValkyrieKeyboardController(hid_device* dev_handle, const char* path, const unsigned short pid, const int interface, std::string dev_name); + ~ValkyrieKeyboardController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + unsigned short GetUSBPID(); + int GetInterfaceNum(); + + void SendColors + ( + unsigned char* color_data, + unsigned int color_data_size + ); + +private: + hid_device* dev; + std::string location; + std::string name; + unsigned short usb_pid; + int interface_num; + + int key_code_99[98] = + { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x77, + 0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x67, + 0x74,0x20,0x21,0x22,0x7A,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D, + 0x2E,0x2F,0x30,0x31,0x43,0x76,0x32,0x33,0x34,0x7B,0x37,0x38,0x39,0x3A, + 0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x55,0x79,0x44,0x45,0x46,0x49, + 0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,0x65,0x56,0x57, + 0x58,0x6A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x62,0x63,0x64,0x66,0x68,0x69 + }; + int key_code_103[102] = + { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x77,0x70,0x73, + 0x75,0x78,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x67, + 0x74,0x20,0x21,0x22,0x7A,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x43,0x76,0x32,0x33,0x34,0x7B,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E, + 0x3F,0x40,0x41,0x42,0x55,0x79,0x44,0x45,0x46,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x65,0x56,0x57,0x58,0x6A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60, + 0x62,0x63,0x64,0x66,0x68,0x69 + }; + + void SendInitializeColorPacket(); + void SendTerminateColorPacket(); +}; diff --git a/Controllers/ValkyrieKeyboardController/ValkyrieKeyboardControllerDetect.cpp b/Controllers/ValkyrieKeyboardController/ValkyrieKeyboardControllerDetect.cpp new file mode 100644 index 0000000..1ecfa97 --- /dev/null +++ b/Controllers/ValkyrieKeyboardController/ValkyrieKeyboardControllerDetect.cpp @@ -0,0 +1,31 @@ +/*---------------------------------------------------------*\ +| ValkyrieKeyboardControllerDetect.cpp | +| | +| Detector for Valkyrie keyboard | +| | +| Nollie (Nuonuo) 06 Dec 2023 | +| Bartholomew Ho (imnotmental) 01 Feb 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "RGBController_ValkyrieKeyboard.h" + +void DetectValkyrieKeyboardControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + ValkyrieKeyboardController* controller = new ValkyrieKeyboardController(dev, info->path, info->product_id, info->interface_number, name); + RGBController_ValkyrieKeyboard* rgb_controller = new RGBController_ValkyrieKeyboard(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +/* DetectValkyrieKeyboardControllers() */ +REGISTER_HID_DETECTOR_IPU("Valkyrie VK99 Pro", DetectValkyrieKeyboardControllers, VALKYRIE_VID, VALKYRIE_99_PRO_PID, 3, 0xFF13, 0x0001); +REGISTER_HID_DETECTOR_IPU("Valkyrie VK99", DetectValkyrieKeyboardControllers, VALKYRIE_VID, VALKYRIE_99_NORMAL_PID, 2, 0xFF13, 0x0001); diff --git a/Controllers/ViewSonicController/ViewSonicControllerDetect.cpp b/Controllers/ViewSonicController/ViewSonicControllerDetect.cpp new file mode 100644 index 0000000..3ede6db --- /dev/null +++ b/Controllers/ViewSonicController/ViewSonicControllerDetect.cpp @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| ViewSonicControllerDetect.cpp | +| | +| Detector for ViewSonic XG270QG and XG270QC | +| | +| Lanzaa 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "VS_XG270QG_Controller.h" +#include "RGBController_XG270QG.h" +#include "VS_XG270QC_Controller.h" +#include "RGBController_XG270QC.h" + +#define WINBOND_VID 0x0416 +#define VIEWSONIC_VID 0x0543 +#define VS_XG270QG_PID 0x5020 +#define VS_XG271QG_PID 0xA004 +#define VS_XG270QC_PID 0xA002 + +void DetectViewSonicQG(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + VS_XG270QG_Controller* controller = new VS_XG270QG_Controller(dev, info->path, name); + RGBController_XG270QG* rgb_controller = new RGBController_XG270QG(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectViewSonicQC(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + VS_XG270QC_Controller* controller = new VS_XG270QC_Controller(dev, info->path, name); + RGBController_XG270QC* rgb_controller = new RGBController_XG270QC(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IPU("ViewSonic Monitor XG270QG", DetectViewSonicQG, WINBOND_VID, VS_XG270QG_PID, 0, 0xFF00, 1); +REGISTER_HID_DETECTOR_IP( "ViewSonic Monitor XG271QG", DetectViewSonicQG, VIEWSONIC_VID, VS_XG271QG_PID, 0, 0x0001); +REGISTER_HID_DETECTOR_IPU("ViewSonic Monitor XG270QC", DetectViewSonicQC, VIEWSONIC_VID, VS_XG270QC_PID, 0, 0x0001, 0); diff --git a/Controllers/ViewSonicController/XG270QC/RGBController_XG270QC.cpp b/Controllers/ViewSonicController/XG270QC/RGBController_XG270QC.cpp new file mode 100644 index 0000000..5c2570e --- /dev/null +++ b/Controllers/ViewSonicController/XG270QC/RGBController_XG270QC.cpp @@ -0,0 +1,169 @@ +/*---------------------------------------------------------*\ +| RGBController_XG270QC.cpp | +| | +| RGBController for ViewSonic XG270QC | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_XG270QC.h" + +/**------------------------------------------------------------------*\ + @name Viewsonic Monitor + @category Accessory + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectViewSonic + @comment +\*-------------------------------------------------------------------*/ + +RGBController_XG270QC::RGBController_XG270QC(VS_XG270QC_Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "ViewSonic"; + type = DEVICE_TYPE_MONITOR; + description = "ViewSonic Monitor Device"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + + mode Off; + Off.name = "Off"; + Off.value = VS_XG270QC_Controller::VS_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Static; + Static.name = "Static"; + Static.value = VS_XG270QC_Controller::VS_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Static.colors_min = 2; + Static.colors_max = 2; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = VS_XG270QC_Controller::VS_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.colors_min = 2; + Breathing.colors_max = 2; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = VS_XG270QC_Controller::VS_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode WarpSpeed; + WarpSpeed.name = "Warp Speed"; + WarpSpeed.value = VS_XG270QC_Controller::VS_MODE_WARP_SPEED; + WarpSpeed.flags = MODE_FLAG_AUTOMATIC_SAVE; + WarpSpeed.color_mode = MODE_COLORS_NONE; + modes.push_back(WarpSpeed); + + mode Stack; + Stack.name = "Stack"; + Stack.value = VS_XG270QC_Controller::VS_MODE_STACK; + Stack.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Stack.colors_min = 2; + Stack.colors_max = 2; + Stack.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Stack); + + //The modes Music and MusicPulse are not supported + + RGBController_XG270QC::SetupZones(); +} + +void RGBController_XG270QC::SetupZones() +{ + zone base; + base.name = "Base"; + base.type = ZONE_TYPE_SINGLE; + base.leds_min = 1; + base.leds_max = 1; + base.leds_count = 1; + base.matrix_map = NULL; + zones.push_back(base); + + zone rear; + rear.name = "Rear"; + rear.type = ZONE_TYPE_SINGLE; + rear.leds_min = 1; + rear.leds_max = 1; + rear.leds_count = 1; + rear.matrix_map = NULL; + zones.push_back(rear); + + led d; + d.name = "Base"; + d.value = 0x00; + leds.push_back(d); + + led back; + back.name = "Rear"; + back.value = 0x01; + leds.push_back(back); + + SetupColors(); +} + +void RGBController_XG270QC::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_XG270QC::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_XG270QC::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_XG270QC::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_XG270QC::DeviceUpdateMode() +{ + uint8_t r1 = 0; + uint8_t g1 = 0; + uint8_t b1 = 0; + uint8_t r2 = 0; + uint8_t g2 = 0; + uint8_t b2 = 0; + + if(modes[active_mode].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + r1 = r2 = RGBGetRValue(modes[active_mode].colors[0]); + g1 = g2 = RGBGetGValue(modes[active_mode].colors[0]); + b1 = b2 = RGBGetBValue(modes[active_mode].colors[0]); + } + else if (modes[active_mode].flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + r1 = RGBGetRValue(colors[0]); + g1 = RGBGetGValue(colors[0]); + b1 = RGBGetBValue(colors[0]); + + r2 = RGBGetRValue(colors[1]); + g2 = RGBGetGValue(colors[1]); + b2 = RGBGetBValue(colors[1]); + } + + controller->SetMode(modes[active_mode].value, r1, g1, b1, modes[active_mode].value, r2, g2, b2); +} diff --git a/Controllers/ViewSonicController/XG270QC/RGBController_XG270QC.h b/Controllers/ViewSonicController/XG270QC/RGBController_XG270QC.h new file mode 100644 index 0000000..cc11752 --- /dev/null +++ b/Controllers/ViewSonicController/XG270QC/RGBController_XG270QC.h @@ -0,0 +1,31 @@ +/*---------------------------------------------------------*\ +| RGBController_XG270QC.h | +| | +| RGBController for ViewSonic XG270QC | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "VS_XG270QC_Controller.h" +#include "RGBController.h" + +class RGBController_XG270QC : public RGBController +{ +public: + RGBController_XG270QC(VS_XG270QC_Controller* controller_ptr); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + VS_XG270QC_Controller* controller; +}; diff --git a/Controllers/ViewSonicController/XG270QC/VS_XG270QC_Controller.cpp b/Controllers/ViewSonicController/XG270QC/VS_XG270QC_Controller.cpp new file mode 100644 index 0000000..c5401b5 --- /dev/null +++ b/Controllers/ViewSonicController/XG270QC/VS_XG270QC_Controller.cpp @@ -0,0 +1,110 @@ +/*---------------------------------------------------------*\ +| VS_XG270QC_Controller.cpp | +| | +| Driver for ViewSonic XG270QC | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "VS_XG270QC_Controller.h" + +VS_XG270QC_Controller::VS_XG270QC_Controller(hid_device* device, const char* path, std::string dev_name) +{ + dev = device; + location = path; + name = dev_name; +} + +VS_XG270QC_Controller::~VS_XG270QC_Controller() +{ + hid_close(dev); +} + +std::string VS_XG270QC_Controller::GetLocation() +{ + return(location); +} + +std::string VS_XG270QC_Controller::GetName() +{ + return(name); +} + +std::string VS_XG270QC_Controller::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void VS_XG270QC_Controller::SetMode(uint8_t mode1, uint8_t r1, uint8_t g1, uint8_t b1, uint8_t mode2, uint8_t r2, uint8_t g2, uint8_t b2) +{ + // Music modes use different values for zone 2 + // Music: zone1=0x12 (VS_MODE_MUSIC), zone2=0x13 (VS_MODE_MUSIC_Z2) + // Music Pulse: zone1=0x12 (VS_MODE_MUSIC_PULSE), zone2=0x14 (VS_MODE_MUSIC_PULSE_Z2) + + uint8_t actual_mode2 = mode2; + + // Map zone 1 music modes to their zone 2 equivalents + if(mode1 == VS_MODE_MUSIC && mode2 == VS_MODE_MUSIC) + { + actual_mode2 = VS_MODE_MUSIC_Z2; // 0x13 + } + else if(mode1 == VS_MODE_MUSIC_PULSE && mode2 == VS_MODE_MUSIC_PULSE) + { + actual_mode2 = VS_MODE_MUSIC_PULSE_Z2; // 0x14 + } + + SendModeComplete(mode1, r1, g1, b1, actual_mode2, r2, g2, b2); +} + +void VS_XG270QC_Controller::SendModeComplete + ( + uint8_t mode1, uint8_t r1, uint8_t g1, uint8_t b1, + uint8_t mode2, uint8_t r2, uint8_t g2, uint8_t b2 + ) +{ + uint8_t data[167] = {0}; + + // Header byte + data[0x00] = 0x02; + + // Zone 1 (Downward facing LEDs) + data[0x01] = mode1; + data[0x02] = r1; + data[0x03] = g1; + data[0x04] = b1; + data[0x05] = 0x00; + data[0x06] = 0x0A; + data[0x07] = 0x00; + + // Zone 2 (Back facing LEDs) + data[0x08] = mode2; + data[0x09] = r2; + data[0x0A] = g2; + data[0x0B] = b2; + data[0x0C] = 0x00; + data[0x0D] = 0x0A; + data[0x0E] = 0x00; + + // End marker + data[0x0F] = 0x01; + + // Rest of the array is already zeroed by initialization + + SendCommand(data, 167); +} + +void VS_XG270QC_Controller::SendCommand(uint8_t *data, size_t length) +{ + hid_send_feature_report(dev, data, length); +} diff --git a/Controllers/ViewSonicController/XG270QC/VS_XG270QC_Controller.h b/Controllers/ViewSonicController/XG270QC/VS_XG270QC_Controller.h new file mode 100644 index 0000000..f5b0509 --- /dev/null +++ b/Controllers/ViewSonicController/XG270QC/VS_XG270QC_Controller.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------*\ +| VS_XG270QC_Controller.h | +| | +| Driver for ViewSonic XG270QC | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBController.h" + +class VS_XG270QC_Controller +{ +public: + enum ModeValues + { + VS_MODE_OFF = 0x00, + VS_MODE_STATIC = 0x01, + VS_MODE_BREATHING = 0x02, + VS_MODE_WARP_SPEED = 0x06, + VS_MODE_RAINBOW = 0x07, + VS_MODE_STACK = 0x09, + VS_MODE_MUSIC = 0x12, + VS_MODE_MUSIC_Z2 = 0x13, // Zone 2 value for Music mode + VS_MODE_MUSIC_PULSE = 0x12, // Same as Music for zone 1 + VS_MODE_MUSIC_PULSE_Z2 = 0x14, // Zone 2 value for Music Pulse mode + }; + + VS_XG270QC_Controller(hid_device* device, const char* path, std::string dev_name); + ~VS_XG270QC_Controller(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + + void SetMode(uint8_t mode1, uint8_t r1, uint8_t g1, uint8_t b1, + uint8_t mode2, uint8_t r2, uint8_t g2, uint8_t b2); + +private: + hid_device* dev; + std::string location; + std::string name; + std::string serial; + + void SendModeComplete + ( + uint8_t mode1, uint8_t r1, uint8_t g1, uint8_t b1, + uint8_t mode2, uint8_t r2, uint8_t g2, uint8_t b2 + ); + void SendCommand(uint8_t *config, size_t length); +}; diff --git a/Controllers/ViewSonicController/XG270QG/RGBController_XG270QG.cpp b/Controllers/ViewSonicController/XG270QG/RGBController_XG270QG.cpp new file mode 100644 index 0000000..62499fb --- /dev/null +++ b/Controllers/ViewSonicController/XG270QG/RGBController_XG270QG.cpp @@ -0,0 +1,189 @@ +/*---------------------------------------------------------*\ +| RGBController_XG270QG.cpp | +| | +| RGBController for ViewSonic XG270QG | +| | +| Lanzaa 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_XG270QG.h" + +/**------------------------------------------------------------------*\ + @name Viewsonic Monitor + @category Accessory + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectViewSonic + @comment +\*-------------------------------------------------------------------*/ + +RGBController_XG270QG::RGBController_XG270QG(VS_XG270QG_Controller* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "ViewSonic"; + type = DEVICE_TYPE_MONITOR; + description = "ViewSonic Monitor Device"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + + mode Off; + Off.name = "Off"; + Off.value = VS_XG270QG_Controller::VS_MODE_OFF; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + mode Custom; + Custom.name = "Custom"; + Custom.value = VS_XG270QG_Controller::VS_MODE_STATIC; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Custom.colors_min = 1; + Custom.colors_max = 1; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.colors.resize(1); + modes.push_back(Custom); + + mode Rainbow; + Rainbow.name = "Rainbow Wave"; + Rainbow.value = VS_XG270QG_Controller::VS_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_AUTOMATIC_SAVE; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Breath; + Breath.name = "Breathing"; + Breath.value = VS_XG270QG_Controller::VS_MODE_BREATHING; + Breath.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Breath.colors_min = 1; + Breath.colors_max = 1; + Breath.color_mode = MODE_COLORS_PER_LED; + Breath.colors.resize(1); + modes.push_back(Breath); + + mode Waterfall; + Waterfall.name = "Waterfall"; + Waterfall.value = VS_XG270QG_Controller::VS_MODE_WATERFALL; + Waterfall.flags = MODE_FLAG_AUTOMATIC_SAVE; + Waterfall.color_mode = MODE_COLORS_NONE; + modes.push_back(Waterfall); + + mode Elite; + Elite.name = "Elite"; + Elite.value = VS_XG270QG_Controller::VS_MODE_ELITE; + Elite.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + Elite.colors_min = 1; + Elite.colors_max = 1; + Elite.color_mode = MODE_COLORS_PER_LED; + Elite.colors.resize(1); + modes.push_back(Elite); + + //mode Jazz; + //Jazz.name = "Jazz Wave (Audio Reactive)"; + //Jazz.value = VS_MODE_JAZZ; + ////Jazz.color_mode = MODE_COLORS_NONE; // might have color + //Jazz.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_AUTOMATIC_SAVE; + //Jazz.colors_min = 1; + //Jazz.colors_max = 1; + //Jazz.color_mode = MODE_COLORS_MODE_SPECIFIC; + //Jazz.colors.resize(1); + //modes.push_back(Jazz); + + //mode EliteGlobal; + //EliteGlobal.name = "Elite Global (Audio Reactive)"; + //EliteGlobal.value = VS_MODE_ELITEGLOBAL; + //EliteGlobal.flags = MODE_FLAG_AUTOMATIC_SAVE; + //EliteGlobal.color_mode = MODE_COLORS_NONE; + //modes.push_back(EliteGlobal); + + RGBController_XG270QG::SetupZones(); +} + +void RGBController_XG270QG::SetupZones() +{ + zone base; + base.name = "Base"; + base.type = ZONE_TYPE_SINGLE; + base.leds_min = 1; + base.leds_max = 1; + base.leds_count = 1; + base.matrix_map = NULL; + zones.push_back(base); + + zone rear; + rear.name = "Rear"; + rear.type = ZONE_TYPE_SINGLE; + rear.leds_min = 1; + rear.leds_max = 1; + rear.leds_count = 1; + rear.matrix_map = NULL; + zones.push_back(rear); + + led d; + d.name = "Base"; + d.value = 0x00; + leds.push_back(d); + + led back; + back.name = "Rear"; + back.value = 0x01; + leds.push_back(back); + + SetupColors(); +} + +void RGBController_XG270QG::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_XG270QG::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_XG270QG::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_XG270QG::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_XG270QG::DeviceUpdateMode() +{ + uint8_t r1 = 0; + uint8_t g1 = 0; + uint8_t b1 = 0; + uint8_t r2 = 0; + uint8_t g2 = 0; + uint8_t b2 = 0; + + if(modes[active_mode].flags & MODE_FLAG_HAS_MODE_SPECIFIC_COLOR) + { + r1 = r2 = RGBGetRValue(modes[active_mode].colors[0]); + g1 = g2 = RGBGetGValue(modes[active_mode].colors[0]); + b1 = b2 = RGBGetBValue(modes[active_mode].colors[0]); + } + else if (modes[active_mode].flags & MODE_FLAG_HAS_PER_LED_COLOR) + { + r1 = RGBGetRValue(colors[0]); + g1 = RGBGetGValue(colors[0]); + b1 = RGBGetBValue(colors[0]); + + r2 = RGBGetRValue(colors[1]); + g2 = RGBGetGValue(colors[1]); + b2 = RGBGetBValue(colors[1]); + } + controller->SetMode(modes[active_mode].value, r1, g1, b1, modes[active_mode].value, r2, g2, b2); +} diff --git a/Controllers/ViewSonicController/XG270QG/RGBController_XG270QG.h b/Controllers/ViewSonicController/XG270QG/RGBController_XG270QG.h new file mode 100644 index 0000000..2c83435 --- /dev/null +++ b/Controllers/ViewSonicController/XG270QG/RGBController_XG270QG.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_XG270QG.h | +| | +| RGBController for ViewSonic XG270QG | +| | +| Lanzaa 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "VS_XG270QG_Controller.h" +#include "RGBController.h" + +class RGBController_XG270QG : public RGBController +{ +public: + RGBController_XG270QG(VS_XG270QG_Controller* controller_ptr); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + VS_XG270QG_Controller* controller; +}; diff --git a/Controllers/ViewSonicController/XG270QG/VS_XG270QG_Controller.cpp b/Controllers/ViewSonicController/XG270QG/VS_XG270QG_Controller.cpp new file mode 100644 index 0000000..94ea92f --- /dev/null +++ b/Controllers/ViewSonicController/XG270QG/VS_XG270QG_Controller.cpp @@ -0,0 +1,100 @@ +/*---------------------------------------------------------*\ +| VS_XG270QG_Controller.cpp | +| | +| Driver for ViewSonic XG270QG | +| | +| Lanzaa 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "StringUtils.h" +#include "VS_XG270QG_Controller.h" + +VS_XG270QG_Controller::VS_XG270QG_Controller(hid_device* device, const char* path, std::string dev_name) +{ + dev = device; + location = path; + name = dev_name; +} + +VS_XG270QG_Controller::~VS_XG270QG_Controller() +{ + hid_close(dev); +} + +std::string VS_XG270QG_Controller::GetLocation() +{ + return(location); +} + +std::string VS_XG270QG_Controller::GetName() +{ + return(name); +} + +std::string VS_XG270QG_Controller::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void VS_XG270QG_Controller::SetMode(uint8_t mode1, uint8_t r1, uint8_t g1, uint8_t b1, uint8_t mode2, uint8_t r2, uint8_t g2, uint8_t b2) +{ + SendModeComplete(mode1, r1, g1, b1, mode2, r2, g2, b2); +} + +void VS_XG270QG_Controller::SendModeComplete + ( + uint8_t mode1, uint8_t r1, uint8_t g1, uint8_t b1, + uint8_t mode2, uint8_t r2, uint8_t g2, uint8_t b2 + ) +{ + uint8_t data[] = + { + 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, + 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + data[0x01] = mode1; // Downward facing LEDs + data[0x02] = r1; + data[0x03] = g1; + data[0x04] = b1; + data[0x05] = 0x00; + data[0x06] = 0x0A; + data[0x07] = 0x00; + + data[0x08] = mode2; // Back facing LEDs + data[0x09] = r2; + data[0x0A] = g2; + data[0x0B] = b2; + data[0x0C] = 0x00; + data[0x0D] = 0x0A; // Might be speed related + + // original data packets are 0x40=64 long + SendCommand(data, 0x20); +} + +void VS_XG270QG_Controller::SendCommand(uint8_t *data, size_t length) +{ + hid_send_feature_report(dev, data, length); +} + diff --git a/Controllers/ViewSonicController/XG270QG/VS_XG270QG_Controller.h b/Controllers/ViewSonicController/XG270QG/VS_XG270QG_Controller.h new file mode 100644 index 0000000..95e206c --- /dev/null +++ b/Controllers/ViewSonicController/XG270QG/VS_XG270QG_Controller.h @@ -0,0 +1,58 @@ +/*---------------------------------------------------------*\ +| VS_XG270QG_Controller.h | +| | +| Driver for ViewSonic XG270QG | +| | +| Lanzaa 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBController.h" + +class VS_XG270QG_Controller +{ +public: + enum ModeValues + { + VS_MODE_OFF = 0x00, + VS_MODE_STATIC = 0x01, + VS_MODE_BREATHING = 0x02, + VS_MODE_RAINBOW = 0x07, + VS_MODE_ELITE = 0x0A, + VS_MODE_JAZZ = 0x0C, + VS_MODE_WATERFALL = 0x12, + VS_MODE_ELITEGLOBAL = 0x13, + }; + + VS_XG270QG_Controller(hid_device* device, const char* path, std::string dev_name); + ~VS_XG270QG_Controller(); + + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + + void SetMode(uint8_t mode1, uint8_t r1, uint8_t g1, uint8_t b1, + uint8_t mode2, uint8_t r2, uint8_t g2, uint8_t b2); + +private: + hid_device* dev; + std::string location; + std::string name; + std::string serial; + + std::string ReadVersion(); + void SendModeComplete + ( + uint8_t mode1, uint8_t r1, uint8_t g1, uint8_t b1, + uint8_t mode2, uint8_t r2, uint8_t g2, uint8_t b2 + ); + void SendCommand(uint8_t *config, size_t length); +}; diff --git a/Controllers/WinbondGamingKeyboardController/RGBController_WinbondGamingKeyboard.cpp b/Controllers/WinbondGamingKeyboardController/RGBController_WinbondGamingKeyboard.cpp new file mode 100644 index 0000000..dcb3048 --- /dev/null +++ b/Controllers/WinbondGamingKeyboardController/RGBController_WinbondGamingKeyboard.cpp @@ -0,0 +1,851 @@ +/*---------------------------------------------------------*\ +| RGBController_WinbondGamingKeyboard.cpp | +| | +| RGBController for Winbond Gaming Keyboard | +| | +| Daniel Gibson 03 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_WinbondGamingKeyboard.h" +#include "RGBControllerKeyNames.h" +#include "KeyboardLayoutManager.h" +#include "LogManager.h" + +/**------------------------------------------------------------------*\ + @name Winbond Gaming Keyboard + @category Keyboard + @type USB + @save :robot: + @direct :x: + @effects :white_check_mark: + @detectors DetectWinbondGamingKeyboard + @comment Also known as Pulsar PCMK, and KT108 (by various manufacturers) +\*-------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------------------------------*\ +| - MSG_NUM is between 0 and (incl) 7, indicating which HID message contains the key's LED | +| - IDX is between 0 and (incl.) 17 (0 to 5 for message 7), | +| indicating the index of that LED within the message. | +| - 1 << 16 is set so KV(0,0) doesn't have the same value as 0 (the default "not assigned" value) | +\*-----------------------------------------------------------------------------------------------*/ +#define KV(MSG_NUM, IDX) ((MSG_NUM) << 8 | (IDX) | (1 << 16)) + +static std::vector additional_mm_leds = +{ + { + 0, // zone + 0, // row + 17, // col + KV(1,0), // value + KEY_EN_MEDIA_VOLUME_UP, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT // opcode + }, + { + 0, // zone + 0, // row + 18, // col + KV(1,1), // value + KEY_EN_MEDIA_VOLUME_DOWN, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT // opcode + }, + { + 0, // zone + 0, // row + 19, // col + KV(1,2), // value + KEY_EN_MEDIA_MUTE, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT // opcode + }, + { + 0, // zone + 0, // row + 20, // col + KV(1,3), // value + "Key: Cylinder?!", // name; TODO: no idea what the symbol meant, was a cylinder.. + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT // opcode + } +}; + +static layout_values winbond_gaming_keyboard_full_layouts = +{ + {}, // "std::vector default_values" is set in InitLayouts() + { // std::map > regional_overlay; + { KEYBOARD_LAYOUT_ISO_QWERTY, + { + /*---------------------------------------------------------*\ + | just setting the values for some keys that should already | + | be defined by KeyboardLayoutManager's keyboard_zone_main | + \*---------------------------------------------------------*/ + { + 0, // zone + 3, // row + 12, // col + KV(4,7), // value + KEY_EN_POUND, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_SWAP_ONLY // opcode + }, + { + 0, // zone + 4, // row + 1, // col + KV(4,17), // value + KEY_EN_ISO_BACK_SLASH, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_SWAP_ONLY // opcode + }, + } + }, + { KEYBOARD_LAYOUT_JIS, + { + /*------------------------------------------------*\ + | that extra key before backspace on JIS keyboards | + \*------------------------------------------------*/ + { + 0, // zone + 1, // row + 13, // col + KV(1,17), // value + KEY_JP_YEN, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT // opcode + } + } + } + /*----------------------------------------------------------------------------------*\ + | TODO: KV(5,10) could be that extra key left of right shift on ABNT keyboards (/ ?) | + \*----------------------------------------------------------------------------------*/ + } +}; + +static layout_values winbond_gaming_keyboard_tkl_layouts = +{ + {}, // "std::vector default_values" is set in InitLayouts() + { // std::map > regional_overlay; + { KEYBOARD_LAYOUT_ISO_QWERTY, + { + /*---------------------------------------------------------*\ + | just setting the values for some keys that should already | + | be defined by KeyboardLayoutManager's keyboard_zone_main | + \*---------------------------------------------------------*/ + { + 0, // zone + 3, // row + 12, // col + KV(4,7), // value + KEY_EN_POUND, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_SWAP_ONLY // opcode + }, + { + 0, // zone + 4, // row + 1, // col + KV(4,17), // value + KEY_EN_ISO_BACK_SLASH, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_SWAP_ONLY // opcode + }, + } + }, + { KEYBOARD_LAYOUT_JIS, + { + /*------------------------------------------------*\ + | that extra key before backspace on JIS keyboards | + \*------------------------------------------------*/ + { + 0, // zone + 1, // row + 13, // col + KV(1,17), // value + KEY_JP_YEN, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT // opcode + } + } + } + /*----------------------------------------------------------------------------------*\ + | TODO: KV(5,10) could be that extra key left of right shift on ABNT keyboards (/ ?) | + \*----------------------------------------------------------------------------------*/ + } +}; + +static layout_values winbond_gaming_keyboard_60_layouts = +{ + {}, // "std::vector default_values" is set in InitLayouts() + { // std::map > regional_overlay; + { KEYBOARD_LAYOUT_ISO_QWERTY, + { + /*---------------------------------------------------------*\ + | just setting the values for some keys that should already | + | be defined by KeyboardLayoutManager's keyboard_zone_main | + \*---------------------------------------------------------*/ + { + 0, // zone + 3, // row + 12, // col + KV(4,7), // value + KEY_EN_POUND, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_SWAP_ONLY // opcode + }, + { + 0, // zone + 4, // row + 1, // col + KV(4,17), // value + KEY_EN_ISO_BACK_SLASH, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_SWAP_ONLY // opcode + }, + } + }, + { KEYBOARD_LAYOUT_JIS, + { + /*------------------------------------------------*\ + | that extra key before backspace on JIS keyboards | + \*------------------------------------------------*/ + { + 0, // zone + 1, // row + 13, // col + KV(1,17), // value + KEY_JP_YEN, // name + KEY_EN_UNUSED, // translated name + KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT // opcode + } + } + } + /*----------------------------------------------------------------------------------*\ + | TODO: KV(5,10) could be that extra key left of right shift on ABNT keyboards (/ ?) | + \*----------------------------------------------------------------------------------*/ + } +}; + +static void InitLayouts(layout_values& keyboard_layouts, KEYBOARD_SIZE kb_size, std::string vendor) +{ + /*-------------------------------------------------------------------*\ + | using kvs ("keyvals" or sth like that) as an alias for | + | keyboard_layouts.default_values, to make the code below | + | shorter/more readable | + \*-------------------------------------------------------------------*/ + std::vector& kvs = keyboard_layouts.default_values; + + /*------------------------------------------------------------------------------------*\ + | Message X: what indices the keys have within the USB HID messages to set their color | + | "??" means "that index exists, but doesn't seem to be used on my TKL ISO keyboard" | + | | + | A Note regarding kb_size: I wrote this with my Pulsar PCMK TKL keyboard, there the | + | LED positions ( KV(MSGNUM, IDX) ) were like on a fullsize keyboard, | + | i.e. there USB messages had space for NumPad keys (or their LEDs). | + | No idea if that's also true for e.g. 60% keyboards, and it's also possible that | + | some positions of NumPad LEDs are incorrect, I only guessed them. | + \*------------------------------------------------------------------------------------*/ + + /* Message 0: ---------------------------------------------------------------*\ + | 0 = Esc, ??, 2 = F1, F2, F3, F4, ??, 7 = F5, F6, F7, F8, F9, F10, F11, F12, | + | 15 = Print, ScrollLock, 17 = Pause | + \*---------------------------------------------------------------------------*/ + + if(kb_size & KEYBOARD_ZONE_FN_ROW) + { + kvs.push_back( KV(0,0) ); // Esc + // F1 to F4 + for(int i=2; i <= 5; ++i) + { + kvs.push_back( KV(0,i) ); + } + + // F5 to F12 + for(int i=7; i <= 14; ++i) + { + kvs.push_back( KV(0,i) ); + } + } + + if(kb_size & KEYBOARD_ZONE_EXTRA) + { + // PrintScreen, ScrollLock, Pause + for(int i=15; i <= 17; ++i) + { + kvs.push_back( KV(0,i) ); + } + } + + /* Message 1: ------------------------------------------------------------------------*\ + | 0-3 = (could be multimedia keys above numblock?), 4 = BKTK, 1, 2, 3, 4, 5, | + | 10 = 6, 7, 8, 9, 0, '-', 16 = '=', 17 = (that extra key before backspace on JIS?) | + \*------------------------------------------------------------------------------------*/ + + // KV(1,0) to KV(1,3) are probably 4 extra multimedia keys above the numblock + + // BKTK ("Quake console key"), 1, 2, ... 0, -, = + if(kb_size & KEYBOARD_ZONE_MAIN) + { + for(int i=4; i <= 16; ++i) + { + kvs.push_back( KV(1,i) ); + } + + // KV(1,17) might be that extra Yen key before backspace on JIS keyboards + } + + /* Message 2: ------------------------------------------------------------*\ + | 0 = Backspace, Ins, Pos1, 3 = PgUp, 4-7 = (NumLock, Num/, Num*, Num- ?), | + | 8 = Tab, Q, W, E, R, T, ..., 16 = I, 17 = O | + \*------------------------------------------------------------------------*/ + + // Backspace + if(kb_size & KEYBOARD_ZONE_MAIN) + { + kvs.push_back( KV(2,0) ); + } + + // Ins, Home, PgUp + if(kb_size & KEYBOARD_ZONE_EXTRA) + { + for(int i=1; i <= 3; ++i) + { + kvs.push_back( KV(2,i) ); + } + } + + // NumLock, NP/, NP*, NP- + if(kb_size & KEYBOARD_ZONE_NUMPAD) + { + for(int i=4; i <= 7; ++i) + { + kvs.push_back( KV(2,i) ); + } + } + + // Tab, Q, ..., I, O + if(kb_size & KEYBOARD_ZONE_MAIN) + { + for(int i=8; i <= 17; ++i) + { + kvs.push_back( KV(2,i) ); + } + } + + /* Message 3: -----------------------------------------------------------*\ + | 0 = P, [ (Ü), ] (+), 3 = (US-\ ?), ??, 5 = Del, End, 7 = PgDown, | + | 8-1 = (Num7, Num8, Num9, Num+ ?), 12 = CapsLock, ??, 14 = A, S, D, F | + \*-----------------------------------------------------------------------*/ + + // P, Ü/[, +/] + if(kb_size & KEYBOARD_ZONE_MAIN) + { + for(int i=0; i <= 2; ++i) + { + kvs.push_back( KV(3,i) ); + } + // Backslash + if(vendor != "Hator") + { + kvs.push_back( KV(3,3) ); + } + else + { + kvs.push_back( KV(3,4) ); + } + } + + // Del, End, PgDown + if(kb_size & KEYBOARD_ZONE_EXTRA) + { + for(int i=5; i <= 7; ++i) + { + kvs.push_back( KV(3,i) ); + } + } + + // NP7-NP9, NP+ + if(kb_size & KEYBOARD_ZONE_NUMPAD) + { + for(int i=8; i <= 11; ++i) + { + kvs.push_back( KV(3,i) ); + } + } + + + if(kb_size & KEYBOARD_ZONE_MAIN) + { + // CapsLock + kvs.push_back( KV(3,12) ); + // A, S, D, F + for(int i=14; i <= 17; ++i) + { + kvs.push_back( KV(3,i) ); + } + } + + /* Message 4: -------------------------------------------------------------*\ + | 0 = G, H, J, K, L, ; (Ö), " (Ä), 7 = #, 8 = Enter, 9 - 15 = ?? | + | (maybe 12-14 are Num4-6, maybe 15 = Num+ ?), 16 = Shift, 17 = ISO-\ (<) | + \*-------------------------------------------------------------------------*/ + + if(kb_size & KEYBOARD_ZONE_MAIN) + { + // G, H, ..., L, Ö/;, Ä/" + for(int i=0; i <= 6; ++i) + { + kvs.push_back( KV(4,i) ); + } + + // KV(4,7) is the ISO # key that doesn't exist on ANSI (set in overlay) + // even though # is no ANSI key, that default_values array expects it to be there.. + kvs.push_back( KV(4,7) ); + + // Enter - assuming that on ANSI it uses the same LED index + kvs.push_back( KV(4,8) ); + } + + + if(kb_size & KEYBOARD_ZONE_NUMPAD) + { + // NP4-6 - not sure if those are really at 12-14, would fit though + for(int i=12; i <= 14; ++i) + { + kvs.push_back( KV(4,i) ); + } + } + + if(kb_size & KEYBOARD_ZONE_MAIN) + { + // Left Shift + kvs.push_back( KV(4,16) ); + + // KV(4,17) is the ISO |\ (or <) key that doesn't exist on ANSI (set in overlay) + // apparently it must be listed anyway.. + kvs.push_back( KV(4,17) ); + } + + /* Message 5: --------------------------------------------------------*\ + | 0 = Z/Y, X, C, V, B, N, M, ',', '.' , 9 = / or -, ??, ??, | + | 12 = Shift, ??, 14 = Up, 15-17 ?? (maybe 16 and 17 are Num1, Num2, | + | and Num3, NumEnter are in next msg?) | + \*--------------------------------------------------------------------*/ + + if(kb_size & KEYBOARD_ZONE_MAIN) + { + // Z (Y), X, .., M, ',', '.', / (- on DE) + for(int i=0; i <= 9; ++i) + { + kvs.push_back( KV(5,i) ); + } + + // KV(5,10) could be that extra key left of right shift on ABNT keyboards (/ ?) + + kvs.push_back( KV(5,12) ); // Right Shift + } + + if(kb_size & KEYBOARD_ZONE_EXTRA) + { + kvs.push_back( KV(5,14) ); // Up + } + + if(kb_size & KEYBOARD_ZONE_NUMPAD) + { + kvs.push_back( KV(5,16) ); // Num1 + kvs.push_back( KV(5,17) ); // Num2 + } + + /* Message 6: -------------------------------------------------------------*\ + | 0 = (Num3 ?), (Num Enter ?), 2 = Ctrl, Win, 4 = Alt, .. ?? .., 8 = Space, | + | .. ?? .., 12 = AltGr, Fn, Menu, 15 = Ctrl, ??, 17 = Left | + \*-------------------------------------------------------------------------*/ + + // Num3, Num Enter + if(kb_size & KEYBOARD_ZONE_NUMPAD) + { + for(int i=0; i <= 1; ++i) + { + kvs.push_back( KV(6,i) ); + } + } + + if(kb_size & KEYBOARD_ZONE_MAIN) + { + // LeftCtrl, Win, LeftAlt + for(int i=2; i <= 4; ++i) + { + kvs.push_back( KV(6,i) ); + } + // Space + kvs.push_back( KV(6,8) ); + // RightAlt, Fn, RMenu, RightCtrl + for(int i=12; i <= 15; ++i) + { + kvs.push_back( KV(6,i) ); + } + } + + if(kb_size & KEYBOARD_ZONE_EXTRA) + kvs.push_back( KV(6,17) ); // Left + + /* Message 7: -------------------------------------------------------*\ + | 0 = Down, 1 = Right, 2-5 = ?? (maybe 2 or 3 = Num0, 4 = NumDecimal, | + | 5 = NumEnter, more likely: 3 = Num0, 5 = NumDec) | + \*-------------------------------------------------------------------*/ + + if(kb_size & KEYBOARD_ZONE_EXTRA) + { + kvs.push_back( KV(7,0) ); // Down + kvs.push_back( KV(7,1) ); // Right + } + + if(kb_size & KEYBOARD_ZONE_NUMPAD) + { + kvs.push_back( KV(7,3) ); // Num0 - TODO: or 7,2 or 7,4 ? + kvs.push_back( KV(7,5) ); // NumDec - TODO: or 7,4 ? + } + + /*-------------------------------------------------------------------*\ + | for fullsize keyboards, add multimedia keys to all layouts/overlays | + \*-------------------------------------------------------------------*/ + if(kb_size == KEYBOARD_SIZE_FULL) + { + for(std::pair>& overlay: keyboard_layouts.regional_overlay) + { + for(const keyboard_led& mm_key_led : additional_mm_leds) + { + overlay.second.push_back(mm_key_led); + } + } + + /*---------------------------------------------------------------------------------*\ + | there is no overlay for the ANSI layout defined yet, add one just for the MM keys | + \*---------------------------------------------------------------------------------*/ + keyboard_layouts.regional_overlay.insert({ KEYBOARD_LAYOUT_ANSI_QWERTY, additional_mm_leds }); + } +} + + + +RGBController_WinbondGamingKeyboard::RGBController_WinbondGamingKeyboard(WinbondGamingKeyboardController* ctrl) + : controller(ctrl) +{ + type = DEVICE_TYPE_KEYBOARD; + name = ctrl->GetName(); + vendor = ctrl->GetVendor(); + description = ctrl->GetDescription(); + location = ctrl->GetDeviceLocation(); + serial = ctrl->GetSerialString(); + version = ctrl->GetVersion(); + + { + mode m; + m.name = "Custom"; + m.value = WINBOND_GK_MODE_CUSTOM; + m.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + m.color_mode = MODE_COLORS_PER_LED; + m.brightness_min = 0; + m.brightness_max = 4; + m.brightness = 1; + m.speed_min = 0; + m.speed_max = 4; + m.speed = 2; + modes.push_back(m); + } + + { + mode m; + m.name = "Static"; + m.value = WINBOND_GK_MODE_STATIC; + m.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + m.color_mode = MODE_COLORS_MODE_SPECIFIC; + m.colors_min = m.colors_max = 1; // one color for all keys + m.colors.push_back(ToRGBColor(255,255,255)); // default to white + m.brightness_min = 0; + m.brightness_max = 4; + m.brightness = 1; + m.speed_min = 0; + m.speed_max = 4; + m.speed = 2; + modes.push_back(m); + } + + { + mode m; + m.name = "Neon"; + m.value = WINBOND_GK_MODE_NEON; + m.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + m.colors.push_back(ToRGBColor(255,0,0)); + m.colors.push_back(ToRGBColor(0,0,0)); + m.colors_min = 0; + m.colors_max = 2; + m.color_mode = MODE_COLORS_RANDOM; + m.brightness_min = 0; + m.brightness_max = 4; + m.brightness = 1; + m.speed_min = 0; + m.speed_max = 4; + m.speed = 2; + modes.push_back(m); + } + + { + mode m; + m.name = "Breathing"; + m.value = WINBOND_GK_MODE_BREATHE; + m.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + m.colors.push_back(ToRGBColor(255,0,0)); + m.colors.push_back(ToRGBColor(0,0,0)); + m.colors_min = 0; + m.colors_max = 2; + m.color_mode = MODE_COLORS_RANDOM; + m.brightness_min = 0; + m.brightness_max = 4; + m.brightness = 1; + m.speed_min = 0; + m.speed_max = 4; + m.speed = 2; + modes.push_back(m); + } + + { + mode m; + m.name = "Wave"; + m.value = WINBOND_GK_MODE_WAVE; + m.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE + | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_DIRECTION_UD + | MODE_FLAG_HAS_DIRECTION_HV; // NOTE: this is really outer to inner or inner to outer + m.direction = MODE_DIRECTION_RIGHT; + m.colors.push_back(ToRGBColor(255,0,0)); + m.colors.push_back(ToRGBColor(0,0,0)); + m.colors_min = 0; + m.colors_max = 2; + m.color_mode = MODE_COLORS_RANDOM; + m.brightness_min = 0; + m.brightness_max = 4; + m.brightness = 1; + m.speed_min = 0; + m.speed_max = 4; + m.speed = 2; + modes.push_back(m); + } + + { + mode m; + m.name = "Snake"; + m.value = WINBOND_GK_MODE_SNAKE; + m.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE + | MODE_FLAG_HAS_DIRECTION_UD; // NOTE: more like CW/CCW + m.direction = MODE_DIRECTION_DOWN; + m.colors.push_back(ToRGBColor(255,0,0)); + m.colors.push_back(ToRGBColor(0,0,0)); + m.colors_min = 0; + m.colors_max = 2; + m.color_mode = MODE_COLORS_RANDOM; + m.brightness_min = 0; + m.brightness_max = 4; + m.brightness = 1; + m.speed_min = 0; + m.speed_max = 4; + m.speed = 2; + modes.push_back(m); + } + + /*------------------------------------------------------------------------*\ + |the following modes are ones that only show effects when a key is pressed | + | and otherwise only a background color | + \*------------------------------------------------------------------------*/ + { + mode m; + m.name = "Aurora (on keypress)"; + m.value = WINBOND_GK_MODE_AURORA; + m.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE + | MODE_FLAG_HAS_DIRECTION_HV; // NOTE: more like to inner/to outer + m.direction = MODE_DIRECTION_HORIZONTAL; + m.colors.push_back(ToRGBColor(255,0,0)); + m.colors.push_back(ToRGBColor(0,0,0)); // TODO: set background to white so keyboard isn't dark? + m.colors_min = 0; + m.colors_max = 2; + m.color_mode = MODE_COLORS_RANDOM; + m.brightness_min = 0; + m.brightness_max = 4; + m.brightness = 1; + m.speed_min = 0; + m.speed_max = 4; + m.speed = 2; + modes.push_back(m); + } + + { + mode m; + m.name = "Ripple (on keypress)"; + m.value = WINBOND_GK_MODE_RIPPLE; + m.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + m.colors.push_back(ToRGBColor(255,0,0)); + m.colors.push_back(ToRGBColor(0,0,0)); // TODO: set background to white so keyboard isn't dark? + m.colors_min = 0; + m.colors_max = 2; + m.color_mode = MODE_COLORS_RANDOM; + m.brightness_min = 0; + m.brightness_max = 4; + m.brightness = 1; + m.speed_min = 0; + m.speed_max = 4; + m.speed = 2; + modes.push_back(m); + } + + { + mode m; + m.name = "Reactive (on keypress)"; + m.value = WINBOND_GK_MODE_REACTIVE; + m.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + m.colors.push_back(ToRGBColor(255,0,0)); + m.colors.push_back(ToRGBColor(0,0,0)); // TODO: set background to white so keyboard isn't dark? + m.colors_min = 0; + m.colors_max = 2; + m.color_mode = MODE_COLORS_RANDOM; + m.brightness_min = 0; + m.brightness_max = 4; + m.brightness = 1; + m.speed_min = 0; + m.speed_max = 4; + m.speed = 2; + modes.push_back(m); + } + + /*--------------------------------------------------------------------------------*\ + | NOTE: logo light has static, neon (WINBOND_GK_MODE_LOGO_NEON), breathe, wave | + | all logo modes have brightness, all but static have speed and 2 colors + random | + \*--------------------------------------------------------------------------------*/ + + SetupZones(); +} + + +RGBController_WinbondGamingKeyboard::~RGBController_WinbondGamingKeyboard() +{ + delete controller; +} + +void RGBController_WinbondGamingKeyboard::SetupZones() +{ + zone new_zone; + new_zone.name = ZONE_EN_KEYBOARD; + new_zone.type = ZONE_TYPE_MATRIX; + + layout_values* layouts = &winbond_gaming_keyboard_full_layouts; + KEYBOARD_SIZE kb_size = controller->GetSize(); + std::string vendor = controller->GetVendor(); + + if(kb_size == KEYBOARD_SIZE_TKL) + { + layouts = &winbond_gaming_keyboard_tkl_layouts; + } + else if(kb_size == KEYBOARD_SIZE_SIXTY) + { + layouts = &winbond_gaming_keyboard_60_layouts; + } + else // size is full or something not supported directly - default to full + { + kb_size = KEYBOARD_SIZE_FULL; + } + + if(layouts->default_values.empty()) + { + InitLayouts(*layouts, kb_size, vendor); + } + + KeyboardLayoutManager new_kb(controller->GetLayout(), kb_size, *layouts); + + matrix_map_type * new_map = new matrix_map_type; + new_zone.matrix_map = new_map; + new_zone.matrix_map->height = new_kb.GetRowCount(); + new_zone.matrix_map->width = new_kb.GetColumnCount(); + + new_zone.matrix_map->map = new unsigned int[new_map->height * new_map->width]; + new_zone.leds_count = new_kb.GetKeyCount(); + new_zone.leds_min = new_zone.leds_count; + new_zone.leds_max = new_zone.leds_count; + + /*---------------------------------------------------------*\ + | Matrix map still uses declared zone rows and columns | + | as the packet structure depends on the matrix map | + \*---------------------------------------------------------*/ + new_kb.GetKeyMap(new_map->map, KEYBOARD_MAP_FILL_TYPE_COUNT); + + /*---------------------------------------------------------*\ + | Create LEDs for the Matrix zone | + | Place keys in the layout to populate the matrix | + \*---------------------------------------------------------*/ + for(size_t led_idx = 0; led_idx < new_zone.leds_count; led_idx++) + { + led new_led; + + new_led.name = new_kb.GetKeyNameAt((unsigned int)led_idx); + new_led.value = new_kb.GetKeyValueAt((unsigned int)led_idx); + + leds.push_back(new_led); + } + + zones.push_back(new_zone); + + /*---------------------------------------------------------------------------*\ + | special case: logo LEDs | + | it's 3 LEDs, but the colors can't be set separately, so represent it as one | + \*---------------------------------------------------------------------------*/ + + if(controller->HasLogoLight()) + { + zone logo_zone; + logo_zone.name = "Logo Light"; + logo_zone.type = ZONE_TYPE_SINGLE; + logo_zone.leds_min = 1; + logo_zone.leds_max = 1; + logo_zone.leds_count = 1; + logo_zone.matrix_map = NULL; + + led zone_led; + zone_led.name = "Logo LEDs"; + zone_led.value = KV(255, 0); // using message num 255 as special case for logo LED + leds.push_back(zone_led); + + zones.push_back(logo_zone); + } + + SetupColors(); +} + +#undef KV + +void RGBController_WinbondGamingKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_WinbondGamingKeyboard::DeviceUpdateLEDs() +{ + controller->SetLEDsData(colors, leds, modes[active_mode].brightness); +} + +void RGBController_WinbondGamingKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_WinbondGamingKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_WinbondGamingKeyboard::DeviceUpdateMode() +{ + controller->SetMode(modes[active_mode]); +} diff --git a/Controllers/WinbondGamingKeyboardController/RGBController_WinbondGamingKeyboard.h b/Controllers/WinbondGamingKeyboardController/RGBController_WinbondGamingKeyboard.h new file mode 100644 index 0000000..19187c6 --- /dev/null +++ b/Controllers/WinbondGamingKeyboardController/RGBController_WinbondGamingKeyboard.h @@ -0,0 +1,52 @@ +/*---------------------------------------------------------*\ +| RGBController_WinbondGamingKeyboard.h | +| | +| RGBController for Winbond Gaming Keyboard | +| | +| Daniel Gibson 03 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "WinbondGamingKeyboardController.h" + +enum +{ + WINBOND_GK_MODE_STATIC = 0, + WINBOND_GK_MODE_BREATHE = 1, + WINBOND_GK_MODE_WAVE = 2, + WINBOND_GK_MODE_NEON = 3, + WINBOND_GK_MODE_LOGO_NEON = 4, // logo-only! + WINBOND_GK_MODE_SNAKE = 5, + /*-------------------------------------------------------------------------*\ + | the following modes are ones that only show effects when a key is pressed | + | and otherwise only a background color | + \*-------------------------------------------------------------------------*/ + WINBOND_GK_MODE_REACTIVE = 6, + WINBOND_GK_MODE_AURORA = 7, + WINBOND_GK_MODE_RIPPLE = 8, + WINBOND_GK_MODE_CUSTOM = 10 +}; + +class RGBController_WinbondGamingKeyboard : public RGBController +{ +public: + RGBController_WinbondGamingKeyboard(WinbondGamingKeyboardController* ctrl); + ~RGBController_WinbondGamingKeyboard(); + + void SetupZones() override; + void ResizeZone(int zone, int new_size) override; + + void DeviceUpdateLEDs() override; + void UpdateZoneLEDs(int zone) override; + void UpdateSingleLED(int led) override; + + void DeviceUpdateMode() override; + +private: + WinbondGamingKeyboardController* controller; +}; diff --git a/Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardController.cpp b/Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardController.cpp new file mode 100644 index 0000000..761096c --- /dev/null +++ b/Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardController.cpp @@ -0,0 +1,427 @@ +/*---------------------------------------------------------*\ +| WinbondGamingKeyboardController.cpp | +| | +| Driver for Winbond Gaming Keyboard | +| | +| Daniel Gibson 03 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "LogManager.h" +#include "StringUtils.h" +#include "WinbondGamingKeyboardController.h" +#include "RGBController_WinbondGamingKeyboard.h" + +#define WINBOND_HID_DATA_LEN 64 + +WinbondGamingKeyboardController::WinbondGamingKeyboardController(hid_device *dev_handle, const hid_device_info &info, const std::string& name) + : dev(dev_handle) +{ + location = "HID: "; + location += info.path; + + SetNameVendorDescription(info, name); + SetVersionLayout(); +} + + +void WinbondGamingKeyboardController::SetNameVendorDescription(const hid_device_info &info, const std::string& devname) +{ + bool using_product_string = false; + if(info.product_string != nullptr && info.product_string[0] != 0) + { + using_product_string = true; + /*----------------------------------------------------------------------------------------------------*\ + | info->product_string can have at most 126 wchars + terminating 0 | + | (according to | + | https://stackoverflow.com/questions/7193645/how-long-is-the-string-of-manufacturer-of-a-usb-device) | + | in UTF-8 that's at most 126*4 chars + terminating 0 | + \*----------------------------------------------------------------------------------------------------*/ + char product_name[126*4 + 1]; + snprintf(product_name, sizeof(product_name), "%ls", info.product_string); + name = product_name; + } + else + { + name = devname; + } + + /*----------------------------------------------------------------------------*\ + | the Pulsar PCMK TKL keyboard (barebone) uses "PCMK TKL" as product string, | + | at least with the latest firmware. I assume that other Pulsar keyboards also | + | contain "PCMK", so use that to set the vendor to Pulsar (unfortunately, | + | Pulsar doesn't seem to set iManufacturer in the USB device descriptor) | + \*----------------------------------------------------------------------------*/ + if(name.find("PCMK") != std::string::npos) + { + vendor = "Pulsar"; + if(name.find("TKL") != std::string::npos) + { + kb_size = KEYBOARD_SIZE_TKL; + hasLogoLight = true; + } + else if(name.find("60") != std::string::npos) + { + /*---------------------------------------------------*\ + | I *guess* that the PCMK 60% has 60 in its name here | + | (if it even uses the same USB PID:VID ...) | + \*---------------------------------------------------*/ + kb_size = KEYBOARD_SIZE_SIXTY; + } + + } + else if((name.find("Rockfall") != std::string::npos) || (name.find("Skyfall") != std::string::npos)) + { + vendor = "Hator"; + layout = KEYBOARD_LAYOUT_ANSI_QWERTY; + if(name.find("TKL") != std::string::npos) + { + kb_size = KEYBOARD_SIZE_TKL; + } + } + else + { + vendor = "Winbond"; + } + + if(using_product_string) + { + description = vendor + " " + name; + } + else + { + description = name; + } +} + +void WinbondGamingKeyboardController::ParseVersionString(const char *str) +{ + /*------------------------------------------------------------------------------*\ + | str should be something like "2NUC,01,KB,FL,K221UKCVRGB,V1.05.03" (for ISO/UK) | + | we're interested in the chars before "CVRGB" for the layout, | + | and the last "V" and following for the version string | + \*------------------------------------------------------------------------------*/ + const char* ver = strrchr(str, 'V'); + if(ver != nullptr) + { + version = ver; + } + else + { + version = "???"; + } + + LOG_DEBUG("[%s] Version response was: '%s'", name.c_str(), str); + + const char* cvrgb = strstr(str, "CVRGB"); + if(cvrgb != nullptr && cvrgb > str + 2) + { + const char* lang = cvrgb - 2; + + if(lang[0] == 'U' && lang[1] == 'K') + { + LOG_DEBUG("[%s] Detected ISO layout from that version string", name.c_str()); + layout = KEYBOARD_LAYOUT_ISO_QWERTY; + } + else if(lang[0] == 'J' && lang[1] == 'P') + { + LOG_DEBUG("[%s] Detected JIS layout from that version string", name.c_str()); + layout = KEYBOARD_LAYOUT_JIS; + } + else + { + LOG_DEBUG("[%s] Detected ANSI layout from that version string", name.c_str()); + layout = KEYBOARD_LAYOUT_ANSI_QWERTY; + } + } + else + { + LOG_DEBUG("[%s] Couldn't detect any layout from that string (didn't contain \"CVRGB\"), defaulting to ISO", name.c_str()); + } + + /*------------------------------------------------------------------------------------------*\ + | NOTE: I don't know exactly how the string would look like on ANSI or JIS keyboards, | + | but the firmware updaters for Pulsar PCMK TKL are called | + | K221CVRGB_V10507.exe for ANSI | + | K221JPCVRGB_V10503.exe for JIS | + | K221UKCVRGB_V10503.exe for ISO | + | so I assume that ANSI indeed has no extra string before CVRGB and JIS has "JP" there | + | | + | For future reference, PCMK 60% JIS firmware name: GD147CKB_M252KBFL_K225JPCVRGB_V10408.exe | + | no idea if that even uses the same chip, and how the LEDs are handled, though | + | | + | Furthermore, no idea how to detect the layout of other Winbond Gaming Keyboards, | + | like KT108 or KT87 (if that one really uses the same chip). | + | Maybe they use a similar version reply, maybe not... | + \*------------------------------------------------------------------------------------------*/ +} + +void WinbondGamingKeyboardController::SetVersionLayout() +{ + { + /*-------------------------------------------------------------*\ + | this requests a string with information about the version etc | + \*-------------------------------------------------------------*/ + unsigned char msg[WINBOND_HID_DATA_LEN] = { 0x01, 0x0D, 0 }; + hid_write(dev, msg, WINBOND_HID_DATA_LEN); + } + + /*--------------------------------------------------------*\ + | the reply looks like | + | 0x1, 0x0D, 0, 0, 0, "2NUC,01,KB,FL,K221UKCVRGB,V1.05.03" | + \*--------------------------------------------------------*/ + for(int i=0; i<10; ++i) // 10 retries + { + /*-----------------------------------------------------------------------------*\ + | +1 to make sure there's always a terminating \0 byte at the end of the string | + \*-----------------------------------------------------------------------------*/ + unsigned char reply[WINBOND_HID_DATA_LEN + 1] = {}; + int len = hid_read_timeout(dev, reply, WINBOND_HID_DATA_LEN, 150); + if(len < 0) + { + continue; + } + if(reply[0] != 1 || reply[1] != 0x0D || reply[4] != 0) // not the message we want + { + continue; + } + + const char* str = (const char*)reply + 2; // skip 0x01 0x0D bytes + /*-----------------------------------------------------------------*\ + | skip any leading whitespace, \0 bytes and other unprintable chars | + \*-----------------------------------------------------------------*/ + for(int j=0; j < WINBOND_HID_DATA_LEN - 2; ++j, ++str) + { + if(*str > ' ' ) + break; + } + if(*str != '\0') + { + ParseVersionString(str); + return; + } + } + + /*--------------*\ + | fallback | + \*--------------*/ + version = "???"; +} + +std::string WinbondGamingKeyboardController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +static void setModeImpl(hid_device* dev, bool is_logo, unsigned char effect_mode, unsigned char colors[2][3], + bool full_color, unsigned char direction, unsigned char speed, unsigned char brightness) +{ + unsigned char buf[WINBOND_HID_DATA_LEN] = + { + 1, // byte 0: Report ID (always 1) + 7, // byte 1: the "command", in this case 7 (set key LED mode) or 8 (set logo LED mode) + 0, 0, 0, 0x0E, // bytes 2-5: not sure about the meaning, they were like this.. + effect_mode, // byte 6 (effect mode, like WINBOND_GK_MODE_STATIC) + brightness, // byte 7 + speed // byte 8 + // the remaining bytes are set below or remain 0 + }; + + if(is_logo) + { + buf[1] = 8; + buf[5] = 0x0D; + } + + /*------------------------------*\ + | bytes 9-14 are fg and bg color | + \*------------------------------*/ + memcpy(buf+9, colors, 2*3); + + buf[15] = direction; + buf[16] = full_color; + // the rest of the buffer remains 0 + + hid_write(dev, buf, WINBOND_HID_DATA_LEN); +} + +void WinbondGamingKeyboardController::SetLEDsData(const std::vector& colors, const std::vector& leds, int brightness) +{ + /*---------------------------------------------------------------------------*\ + | There are 8 HID messages to set the LEDs. | + | Each message starts with the bytes shown below, followed by 18 RGB triplets | + | (except for the last message that has 12 RGB triplets). These triplets only | + | use values up to 0xC1 (193), instead of the usual 0xFF (255). | + | Byte 0 is the Report ID (1), 1 is the command (9), 2 and 3 are always 0 (?),| + | 4 is the message index, 5 is the length of the following RGB data in bytes | + \*---------------------------------------------------------------------------*/ + unsigned char msgs[8][WINBOND_HID_DATA_LEN] = + { + { 1, 9, 0, 0, 0, 0x36, 0 }, + { 1, 9, 0, 0, 1, 0x36, 0 }, + { 1, 9, 0, 0, 2, 0x36, 0 }, + { 1, 9, 0, 0, 3, 0x36, 0 }, + { 1, 9, 0, 0, 4, 0x36, 0 }, + { 1, 9, 0, 0, 5, 0x36, 0 }, + { 1, 9, 0, 0, 6, 0x36, 0 }, + { 1, 9, 0, 0, 7, 0x12, 0 }, + }; + + RGBColor logo_color = ToRGBColor(128, 128, 128); + + for(size_t i = 0, n = colors.size(); i < n; ++i) + { + unsigned val = leds[i].value; + if(val == 0) // no value set + { + continue; + } + + /*--------------------------------------------------------------------------------------------------*\ + | the following two lines are the inverse of the KV() macro in RGBController_WinbondGamingKeyboard.h | + \*--------------------------------------------------------------------------------------------------*/ + int msg_num = (val >> 8) & 255; + int r_offset = (val & 255); + r_offset = r_offset*3 + 6; // 6 is position of first color byte in message + /*----------------------------*\ + | special case: logo LED color | + \*----------------------------*/ + if(msg_num == 255) + { + logo_color = colors[i]; + // logo light is set separately, just remember its color + continue; + } + + msg_num &= 7; // 0..7 + + /*----------------------------*\ + | transform 0..0xFF to 0..0xC1 | + \*----------------------------*/ + int r = (RGBGetRValue(colors[i]) * 0xC1) / 0xFF; + int g = (RGBGetGValue(colors[i]) * 0xC1) / 0xFF; + int b = (RGBGetBValue(colors[i]) * 0xC1) / 0xFF; + msgs[msg_num][r_offset] = r & 255; + msgs[msg_num][r_offset+1] = g & 255; + msgs[msg_num][r_offset+2] = b & 255; + } + + for(int i=0; i<8; ++i) + { + hid_write(dev, msgs[i], WINBOND_HID_DATA_LEN); + } + + if(hasLogoLight) + { + unsigned char colors[2][3] = {}; + colors[0][0] = RGBGetRValue(logo_color); + colors[0][1] = RGBGetGValue(logo_color); + colors[0][2] = RGBGetBValue(logo_color); + setModeImpl(dev, true, WINBOND_GK_MODE_STATIC, colors, false, 0, 3, brightness); + } +} + +static unsigned char getDirection(unsigned int dir, int effect) +{ + /*---------------------------------------------------------------*\ + | Winbond effect directions: 0: right, 1: left, 2: up, 3: down, | + | 4: to outside, 5: to inside, 6: clockwise, 7: counter-clockwise | + \*---------------------------------------------------------------*/ + + switch(effect) + { + case WINBOND_GK_MODE_WAVE: + /*-----------------------------------*\ + | LR, UD; HV for to inside/to outside | + \*-----------------------------------*/ + switch(dir) + { + case MODE_DIRECTION_LEFT: + return 1; + case MODE_DIRECTION_RIGHT: + return 0; + case MODE_DIRECTION_UP: + return 2; + case MODE_DIRECTION_DOWN: + return 3; + case MODE_DIRECTION_HORIZONTAL: + return 4; + case MODE_DIRECTION_VERTICAL: + return 5; + } + + break; + case WINBOND_GK_MODE_SNAKE: // DOWN/UP for CW/CCW + return dir == MODE_DIRECTION_DOWN ? 6 : 7; + case WINBOND_GK_MODE_AURORA: // HORIZONTAL/VERTICAL for to outside/to inside + return dir == MODE_DIRECTION_HORIZONTAL ? 4 : 5; + } + return 0; // effects without a direction +} + +void WinbondGamingKeyboardController::SetMode(const mode& m) +{ + unsigned char colors[2][3] = { {0, 0, 255}, {0, 0, 0} }; + + for(size_t i=0; i < std::min((size_t)2, m.colors.size()); ++i) + { + RGBColor c = m.colors[i]; + colors[i][0] = RGBGetRValue(c); + colors[i][1] = RGBGetGValue(c); + colors[i][2] = RGBGetBValue(c); + } + + unsigned char direction = getDirection(m.direction, m.value); + bool full_color = (m.color_mode == MODE_COLORS_RANDOM || m.value == WINBOND_GK_MODE_CUSTOM); + + setModeImpl(dev, false, m.value, colors, full_color, direction, m.speed, m.brightness); + + if(hasLogoLight) + { + /*---------------------------------------------------------------------------------*\ + | logo light supports static, neon (WINBOND_GK_MODE_LOGO_NEON), breathe, wave | + | all logo modes have brightness, all but static have speed and 2 colors + random | + | select a mode supported by the logo that matches the key mode | + | | + | TODO: the keyboard allows selecting completely different modes for keys and the | + | logo light, but OpenRGB currently doesn't support different modes per zone. | + | if OpenRGB ever supports that, change the code accordingly | + \*---------------------------------------------------------------------------------*/ + int mode = m.value; + int speed = m.speed; + if(mode == WINBOND_GK_MODE_NEON) + { + mode = WINBOND_GK_MODE_LOGO_NEON; + } + else if(mode == WINBOND_GK_MODE_SNAKE) + { + mode = WINBOND_GK_MODE_WAVE; + direction = (direction == 6) ? 1 : 0; + } + else if(mode > WINBOND_GK_MODE_SNAKE) + { + /*------------------------------------------------------*\ + | these remaining modes are reactive, no real equivalent | + | => use static | + \*------------------------------------------------------*/ + mode = WINBOND_GK_MODE_STATIC; + direction = 0; + speed = 0; + } + // else: the other modes < WINBOND_GK_MODE_SNAKE are supported as is + + setModeImpl(dev, true, mode, colors, full_color, direction, speed, m.brightness); + } +} diff --git a/Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardController.h b/Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardController.h new file mode 100644 index 0000000..5a323c1 --- /dev/null +++ b/Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardController.h @@ -0,0 +1,87 @@ +/*---------------------------------------------------------*\ +| WinbondGamingKeyboardController.h | +| | +| Driver for Winbond Gaming Keyboard | +| | +| Daniel Gibson 03 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "KeyboardLayoutManager.h" + +class WinbondGamingKeyboardController +{ + void SetNameVendorDescription(const hid_device_info &info, const std::string& name); + void SetVersionLayout(); + void ParseVersionString(const char* str); + +public: + WinbondGamingKeyboardController(hid_device *dev_handle, const hid_device_info &info, const std::string& name); + + std::string GetSerialString(); + + const char* GetDeviceLocation() const + { + return location.c_str(); + } + + const char* GetName() const + { + return name.c_str(); + } + + const char* GetVendor() const + { + return vendor.c_str(); + } + + const char* GetDescription() const + { + return description.c_str(); + } + + const char* GetVersion() const + { + return version.c_str(); + } + + KEYBOARD_LAYOUT GetLayout() const + { + return layout; + } + + bool HasLogoLight() const + { + return hasLogoLight; + } + + KEYBOARD_SIZE GetSize() const + { + return kb_size; + } + + void SetLEDsData(const std::vector& colors, const std::vector& leds, int brightness); + + void SetMode(const mode& m); + +protected: + hid_device* dev = nullptr; + +private: + std::string location; + std::string name; + std::string description; + std::string vendor; + std::string version; + KEYBOARD_LAYOUT layout = KEYBOARD_LAYOUT_ISO_QWERTY; // default to ISO so most keys can be configured + KEYBOARD_SIZE kb_size = KEYBOARD_SIZE_FULL; + bool hasLogoLight = false; +}; diff --git a/Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardControllerDetect.cpp b/Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardControllerDetect.cpp new file mode 100644 index 0000000..027dc2a --- /dev/null +++ b/Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardControllerDetect.cpp @@ -0,0 +1,66 @@ +/*---------------------------------------------------------*\ +| WinbondGamingKeyboardControllerDetect.cpp | +| | +| Detector for Winbond Gaming Keyboard | +| | +| Daniel Gibson 03 Dec 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "RGBController_WinbondGamingKeyboard.h" +#include "LogManager.h" + +/*-----------------------------------------------------*\ +| Winbond vendor ID | +\*-----------------------------------------------------*/ +#define WINBOND_VID 0x0416 + +/*-----------------------------------------------------*\ +| Winbond product ID | +\*-----------------------------------------------------*/ +#define WINBOND_GAMING_KEYBOARD_PID 0xB23C + +void DetectWinbondGamingKeyboard(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + /*--------------------------------------------------------------------------------------------------*\ + | NOTE: according to https://4pda.to/forum/index.php?showtopic=1061923, | + | the "KT108" keyboard, which has the same VID:PID, uses the product_string "KT108 keyboard" | + | that could be used for KT108-specific settings? OTOH, according to | + | https://usb-ids.gowdy.us/read/UD/0416/b23c there are also variants of KT108 with | + | product string "Gaming Keyboard". | + | Apart from the KT108, there's also a KT87, but no idea about its product_string, | + | or even its VID/PID (I *assume* it's also 0416:B23C) | + | KT87 and KT108 seem to be sold under the brands WIANXP, Nautilus and Capturer | + | | + | Apart from those noname keyboards that one might only find on aliexpress and similar shops, | + | the Pulsar PCMK TKL keyboard (barebone) uses this VID+PID, and that is the one this is | + | tested with - the ISO variant, specifically. | + | ANSI and JIS variants also exist, I'll try to support them as best as I can. | + | | + | Pulsar also offers a 60% barebone in ISO, ANSI and JIS, but no idea about its VID or PID, | + | or product_string, much less about its protocol (even if it uses the same firmware, | + | I don't know which key corresponds to which bytes in the HID message that sets the per-key colors) | + \*--------------------------------------------------------------------------------------------------*/ + + if(dev) + { + LOG_INFO("Detected WinbondGamingKeyboard at %s, product_string is %ls name is %s", info->path, info->product_string, name.c_str()); + + WinbondGamingKeyboardController* controller = new WinbondGamingKeyboardController(dev, *info, name); + RGBController* rgb_controller = new RGBController_WinbondGamingKeyboard(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + LOG_WARNING("Couldn't open hid dev %s: %ls", info->path, hid_error(NULL)); + } +} + +REGISTER_HID_DETECTOR_PU("Winbond Gaming Keyboard", DetectWinbondGamingKeyboard, WINBOND_VID, WINBOND_GAMING_KEYBOARD_PID, 0xFF1B, 0x91); diff --git a/Controllers/WootingKeyboardController/RGBController_WootingKeyboard.cpp b/Controllers/WootingKeyboardController/RGBController_WootingKeyboard.cpp new file mode 100644 index 0000000..69de0a0 --- /dev/null +++ b/Controllers/WootingKeyboardController/RGBController_WootingKeyboard.cpp @@ -0,0 +1,778 @@ +/*---------------------------------------------------------*\ +| RGBController_WootingKeyboard.cpp | +| | +| RGBController for Wooting keyboard | +| | +| Diogo Trindade (diogotr7) 25 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_WootingKeyboard.h" +#include "LogManager.h" + +//TODO: These matrix maps have indices to the 6x21 full layout. +// This is incorrect. The values in the matrix map should be indices to the `leds` vector. +// the Value property in each LED is what should map to the index in the full 6x21 layout. + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map_full[6][21] = { + { 0, NA, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }, + { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41 }, + { 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62 }, + { 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, NA, NA, NA, 80, 81, 82, NA }, + { 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, NA, 97, NA, 99, NA, 101, 102, 103, 104 }, + { 105, 106, 107, NA, NA, NA, 111, NA, NA, NA, 115, 116, 117, 118, 119, 120, 121, NA, 123, 124, NA } +}; + +static const char *led_names_full[6][21] { + //Row 0 + { + KEY_EN_ESCAPE, + KEY_EN_UNUSED, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_PAUSE_BREAK, + KEY_EN_SCROLL_LOCK, + "Key: Custom1", + "Key: Custom2", + "Key: Custom3", + "Key: Mode", + }, + //row 1 + { + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_PLUS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_HOME, + KEY_EN_PAGE_UP, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, + }, + //row 2 + { + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_PLUS, + }, + //row 3 + { + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_POUND, + KEY_EN_ANSI_ENTER, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + KEY_EN_UNUSED, + }, + //row 4 + { + KEY_EN_LEFT_SHIFT, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_UNUSED, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UNUSED, + KEY_EN_UP_ARROW, + KEY_EN_UNUSED, + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_ENTER, + }, + //row 5 + { + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_SPACE, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + KEY_EN_UNUSED, + KEY_EN_NUMPAD_0, + KEY_EN_NUMPAD_PERIOD, + KEY_EN_UNUSED, + } +}; + +static unsigned int matrix_map_tkl[6][17] = { + { 0, NA, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, + { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 }, + { 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58 }, + { 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, NA, NA, NA }, + { 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, NA, 97, NA, 99, NA }, + { 105, 106, 107, NA, NA, NA, 111, NA, NA, NA, 115, 116, 117, 118, 119, 120, 121 } +}; + +static const char *led_names_tkl[6][17] { + //Row 0 + { + KEY_EN_ESCAPE, + KEY_EN_UNUSED, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_PAUSE_BREAK, + "Key: Custom1", + }, + //row 1 + { + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_PLUS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_HOME, + KEY_EN_PAGE_UP, + }, + //row 2 + { + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + }, + //row 3 + { + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_POUND, + KEY_EN_ANSI_ENTER, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + }, + //row 4 + { + KEY_EN_LEFT_SHIFT, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_UNUSED, + KEY_EN_RIGHT_SHIFT, + KEY_EN_UNUSED, + KEY_EN_UP_ARROW, + KEY_EN_UNUSED, + }, + //row 5 + { + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_SPACE, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + } +}; + +static unsigned int matrix_map_80HE[6][17] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, NA, 10, 11, 12, 13, 14, 15, 16 }, + { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 }, + { 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, NA, 56, 57, 58 }, + { 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, NA, 77, NA, NA }, + { 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, NA, 97, 98, 99, NA }, + { 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, NA, 119, 120, 121 } +}; + +static const char *led_names_80HE[6][17] { + //Row 0 + { + KEY_EN_ESCAPE, + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_UNUSED, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + "Key: Mode", + KEY_EN_PRINT_SCREEN, + KEY_EN_PAUSE_BREAK, + }, + //row 1 + { + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_PLUS, + "Key: JIS Specific TODO", + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_HOME, + }, + //row 2 + { + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_UNUSED, + KEY_EN_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_END, + }, + //row 3 + { + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_POUND, + KEY_EN_UNUSED, + KEY_EN_ANSI_ENTER, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + }, + //row 4 + { + KEY_EN_LEFT_SHIFT, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_UNUSED, + "Key: JIS Specific TODO", + KEY_EN_RIGHT_SHIFT, + KEY_EN_UP_ARROW, + KEY_EN_UNUSED, + }, + //row 5 + { + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + "Key: JIS Specific TODO", + "Spacebar LED 1", + "Spacebar LED 2", + KEY_EN_SPACE, + "Spacebar LED 3", + "Spacebar LED 4", + "Key: JIS Specific TODO", + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_WINDOWS, + KEY_EN_RIGHT_FUNCTION, + KEY_EN_UNUSED, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + } +}; + +static unsigned int matrix_map_60[5][14] = { + { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 }, + { 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55 }, + { 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76 }, + { 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, NA, 97 }, + { 105, 106, 107, NA, 109, 110, 111, 112, 113, NA, 115, 116, 117, 118 } +}; + +static const char *led_names_60HE[5][14] { + //row 0 + { + KEY_EN_ESCAPE, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_PLUS, + KEY_EN_BACKSPACE, + }, + //row 1 + { + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_BACK_SLASH, + }, + //row 2 + { + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + KEY_EN_POUND, + KEY_EN_ANSI_ENTER, + }, + //row 3 + { + KEY_EN_LEFT_SHIFT, + KEY_EN_ISO_BACK_SLASH, + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + KEY_EN_UNUSED, + KEY_EN_RIGHT_SHIFT, + }, + //row 4 + { + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + KEY_EN_UNUSED, + "Spacebar LED 1", + "Spacebar LED 2", + KEY_EN_SPACE, + "Spacebar LED 3", + "Spacebar LED 4", + KEY_EN_UNUSED, + KEY_EN_RIGHT_ALT, + KEY_EN_MENU, + KEY_EN_RIGHT_CONTROL, + KEY_EN_RIGHT_FUNCTION, + } +}; + +static unsigned int matrix_map_3pad[5][7] = { + { 0, NA, 2, NA, 4, NA, 6 }, + { 21, NA, NA, NA, NA, NA, 27 }, + { 42, 43, NA, 45, NA, 47, 48 }, + { 63, NA, NA, NA, NA, NA, 69 }, + { NA, 85, 86, NA, 88, 89, NA } +}; + +static const char *led_names_3pad[5][7] { + //row 0 + { + //top, right to left + "Lightbar 14", + KEY_EN_UNUSED, + "Lightbar 13", + KEY_EN_UNUSED, + "Lightbar 12", + KEY_EN_UNUSED, + "Lightbar 11", + }, + //row 1 + { + "Lighbar 1", //left side, top led + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + "Lightbar 10",//right side, top led + }, + //row 2 + { + "Lightbar 2",//left side, middle led + "Keypad 1", + KEY_EN_UNUSED, + "Keypad 2", + KEY_EN_UNUSED, + "Keypad 3", + "Lightbar 9",//right side, middle led + }, + //row 3 + { + "Lightbar 3",//left side, bottom led + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + KEY_EN_UNUSED, + "Lightbar 8",//right side, bottom led + }, + //row 4 + { + //bottom leds, left to right + KEY_EN_UNUSED, + "Lightbar 4", + "Lightbar 5", + KEY_EN_UNUSED, + "Lightbar 6", + "Lightbar 7", + KEY_EN_UNUSED, + } +}; + +/**------------------------------------------------------------------*\ + @name Wooting Keyboards + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectWootingV1KeyboardControllers,DetectWootingV2KeyboardControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_WootingKeyboard::RGBController_WootingKeyboard(WootingKeyboardController* controller_ptr) +{ + controller = controller_ptr; + + LOG_DEBUG("%sAdding meta data", WOOTING_CONTROLLER_NAME); + name = controller->GetName(); + vendor = controller->GetVendor(); + type = DEVICE_TYPE_KEYBOARD; + description = controller->GetDescription(); + location = controller->GetLocation(); + serial = controller->GetSerial(); + + LOG_DEBUG("%sAdding modes", WOOTING_CONTROLLER_NAME); + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_WootingKeyboard::~RGBController_WootingKeyboard() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for (unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if (zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_WootingKeyboard::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + WOOTING_DEVICE_TYPE wooting_type = controller->GetWootingType(); + const char** led_names = nullptr; + unsigned int* matrix_map = nullptr; + size_t matrix_rows = 0; + size_t matrix_columns = 0; + + if(wooting_type == WOOTING_KB_TKL) + { + matrix_map = (unsigned int *)matrix_map_tkl; + led_names = (const char **)led_names_tkl; + matrix_rows = 6; + matrix_columns = 17; + } + else if(wooting_type == WOOTING_KB_80PER) + { + matrix_map = (unsigned int *)matrix_map_80HE; + led_names = (const char **)led_names_80HE; + matrix_rows = 6; + matrix_columns = 17; + } + else if(wooting_type == WOOTING_KB_60PER) + { + matrix_map = (unsigned int *)matrix_map_60; + led_names = (const char **)led_names_60HE; + matrix_rows = 5; + matrix_columns = 14; + } + else if(wooting_type == WOOTING_KB_3PAD) + { + matrix_map = (unsigned int *)matrix_map_3pad; + led_names = (const char **)led_names_3pad; + matrix_rows = 5; + matrix_columns = 7; + } + else // Fullsize and default + { + matrix_map = (unsigned int *)matrix_map_full; + led_names = (const char **)led_names_full; + matrix_rows = 6; + matrix_columns = 21; + } + + unsigned int* new_matrix = new unsigned int[matrix_rows * matrix_columns]; + unsigned int total_led_count = 0; + + for(size_t row = 0; row < matrix_rows; row++) + { + for(size_t col = 0; col < matrix_columns; col++) + { + size_t idx = row * matrix_columns + col; + unsigned int hardware_index = matrix_map[idx]; + + if(hardware_index == NA) + { + new_matrix[idx] = NA; + continue; + } + + led new_led; + new_led.name = led_names[idx]; + new_led.value = hardware_index; + + leds.push_back(new_led); + + // 3. The zone map now points to the LED's position in the vector + new_matrix[idx] = total_led_count; + total_led_count++; + } + } + + + + zone new_zone; + + new_zone.name = name.append(" zone"); + new_zone.type = ZONE_TYPE_MATRIX; + new_zone.leds_min = total_led_count; + new_zone.leds_max = total_led_count; + new_zone.leds_count = total_led_count; + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = (unsigned int)matrix_rows; + new_zone.matrix_map->width = (unsigned int)matrix_columns; + new_zone.matrix_map->map = new_matrix; + + zones.push_back(new_zone); + + SetupColors(); +} + +void RGBController_WootingKeyboard::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_WootingKeyboard::DeviceUpdateLEDs() +{ + RGBColor framebuffer[WOOTING_RGB_ROWS * WOOTING_RGB_COLUMNS] = {0}; + + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned int framebuffer_index = leds[led_idx].value; + framebuffer[framebuffer_index] = colors[led_idx]; + } + + controller->SendDirect(&framebuffer[0], WOOTING_RGB_ROWS * WOOTING_RGB_COLUMNS); +} + +void RGBController_WootingKeyboard::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_WootingKeyboard::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_WootingKeyboard::DeviceUpdateMode() +{ +} diff --git a/Controllers/WootingKeyboardController/RGBController_WootingKeyboard.h b/Controllers/WootingKeyboardController/RGBController_WootingKeyboard.h new file mode 100644 index 0000000..94e8079 --- /dev/null +++ b/Controllers/WootingKeyboardController/RGBController_WootingKeyboard.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_WootingKeyboard.h | +| | +| RGBController for Wooting keyboard | +| | +| Diogo Trindade (diogotr7) 25 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "WootingKeyboardController.h" + +class RGBController_WootingKeyboard : public RGBController +{ +public: + RGBController_WootingKeyboard(WootingKeyboardController* controller_ptr); + ~RGBController_WootingKeyboard(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + WootingKeyboardController* controller; +}; diff --git a/Controllers/WootingKeyboardController/WootingKeyboardController.cpp b/Controllers/WootingKeyboardController/WootingKeyboardController.cpp new file mode 100644 index 0000000..3a40b61 --- /dev/null +++ b/Controllers/WootingKeyboardController/WootingKeyboardController.cpp @@ -0,0 +1,90 @@ +/*---------------------------------------------------------*\ +| WootingKeyboardController.cpp | +| | +| Driver for Wooting keyboard | +| | +| Chris M (Dr_No) 09 Jul 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "WootingKeyboardController.h" + +WootingKeyboardController::WootingKeyboardController() +{ + +} + +WootingKeyboardController::~WootingKeyboardController() +{ + +} + +std::string WootingKeyboardController::GetName() +{ + return name; +} + +std::string WootingKeyboardController::GetVendor() +{ + return vendor; +} + +std::string WootingKeyboardController::GetLocation() +{ + return("HID: " + location); +} + +std::string WootingKeyboardController::GetDescription() +{ + return description; +} + +std::string WootingKeyboardController::GetSerial() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +WOOTING_DEVICE_TYPE WootingKeyboardController::GetWootingType() +{ + return wooting_type; +} + +void WootingKeyboardController::SendInitialize() +{ + wooting_usb_send_feature(WOOTING_COLOR_INIT_COMMAND, 0,0,0,0); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); +} + +bool WootingKeyboardController::wooting_usb_send_feature(uint8_t commandId, uint8_t parameter0, uint8_t parameter1, uint8_t parameter2, uint8_t parameter3) +{ + uint8_t feature_buffer[WOOTING_COMMAND_SIZE] = { 0, 0xD0, 0xDA }; + + /*---------------------------------------------------------*\ + | Set up the Send Feature packet | + \*---------------------------------------------------------*/ + feature_buffer[3] = commandId; + feature_buffer[4] = parameter3; + feature_buffer[5] = parameter2; + feature_buffer[6] = parameter1; + feature_buffer[7] = parameter0; + + /*---------------------------------------------------------*\ + | Send packet | + \*---------------------------------------------------------*/ + uint8_t report_size = hid_send_feature_report(dev, feature_buffer, WOOTING_COMMAND_SIZE); + LOG_DEBUG("%sSend feature returned - %04i expected %04i", WOOTING_CONTROLLER_NAME, report_size, WOOTING_COMMAND_SIZE); + return (report_size == WOOTING_COMMAND_SIZE); +} diff --git a/Controllers/WootingKeyboardController/WootingKeyboardController.h b/Controllers/WootingKeyboardController/WootingKeyboardController.h new file mode 100644 index 0000000..1847908 --- /dev/null +++ b/Controllers/WootingKeyboardController/WootingKeyboardController.h @@ -0,0 +1,83 @@ +/*---------------------------------------------------------*\ +| WootingKeyboardController.h | +| | +| Driver for Wooting keyboard | +| | +| Chris M (Dr_No) 09 Jul 2021 | +| Diogo Trindade (diogotr7) 25 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "LogManager.h" + +#define WOOTING_COMMAND_SIZE 8 +#define WOOTING_RAW_COLORS_REPORT 11 +#define WOOTING_SINGLE_COLOR_COMMAND 30 +#define WOOTING_SINGLE_RESET_COMMAND 31 +#define WOOTING_RESET_ALL_COMMAND 32 +#define WOOTING_COLOR_INIT_COMMAND 33 + + +#define WOOTING_RGB_ROWS 6 +#define WOOTING_RGB_COLUMNS 21 + +#define WOOTING_ONE_RGB_COLUMNS 17 +#define WOOTING_TWO_RGB_COLUMNS 21 +#define WOOTING_60_RGB_COLUMNS 14 +#define WOOTING_3PAD_RGB_COLUMNS 7 + +/*---------------------------------------------------------*\ +| Placeholder for compilation. Redefined by each subclass | +\*---------------------------------------------------------*/ +#define WOOTING_CONTROLLER_NAME "[Wooting] " + +enum WOOTING_DEVICE_TYPE +{ + WOOTING_KB_TKL = 0, + WOOTING_KB_FULL = 1, + WOOTING_KB_60PER = 2, + WOOTING_KB_3PAD = 3, + WOOTING_KB_80PER = 4, +}; + +enum RGB_PARTS +{ + PART0, + PART1, + PART2, + PART3, + PART4 +}; + +class WootingKeyboardController +{ +public: + WootingKeyboardController(); + virtual ~WootingKeyboardController(); + + hid_device* dev; + std::string name; + std::string vendor; + std::string description; + std::string location; + WOOTING_DEVICE_TYPE wooting_type; + + std::string GetName(); + std::string GetVendor(); + std::string GetDescription(); + std::string GetLocation(); + std::string GetSerial(); + WOOTING_DEVICE_TYPE GetWootingType(); + bool wooting_usb_send_feature(uint8_t command, uint8_t param0, + uint8_t param1, uint8_t param2, uint8_t param3); + + virtual void SendDirect(RGBColor* colors, uint8_t color_count) = 0; + virtual void SendInitialize(); +}; diff --git a/Controllers/WootingKeyboardController/WootingKeyboardControllerDetect.cpp b/Controllers/WootingKeyboardController/WootingKeyboardControllerDetect.cpp new file mode 100644 index 0000000..001632f --- /dev/null +++ b/Controllers/WootingKeyboardController/WootingKeyboardControllerDetect.cpp @@ -0,0 +1,186 @@ +/*---------------------------------------------------------*\ +| WootingKeyboardControllerDetect.cpp | +| | +| Detector for Wooting keyboard | +| | +| Diogo Trindade (diogotr7) 25 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "WootingV1KeyboardController.h" +#include "WootingV2KeyboardController.h" +#include "WootingV3KeyboardController.h" +#include "RGBController_WootingKeyboard.h" +#include "LogManager.h" + +#define WOOTING_CONFIG_USAGE_PAGE_V2 0x1337 +#define WOOTING_CONFIG_USAGE_PAGE_V3 0xFF55 + +/*-----------------------------------------------------*\ +| Wooting vendor ID | +\*-----------------------------------------------------*/ +#define WOOTING_OLD_VID 0x03EB +#define WOOTING_NEW_VID 0x31E3 + +/*-----------------------------------------------------*\ +| Keyboard product IDs | +\*-----------------------------------------------------*/ +#define WOOTING_ONE_LEGACY_PID 0xFF01 +#define WOOTING_TWO_LEGACY_PID 0xFF02 +#define WOOTING_ONE_PID 0x1100 +#define WOOTING_TWO_PID 0x1200 +#define WOOTING_TWO_LE_PID 0x1210 +#define WOOTING_TWO_HE_PID 0x1220 +#define WOOTING_TWO_HE_ARM_PID 0x1230 +#define WOOTING_60HE_PID 0x1300 +#define WOOTING_60HE_ARM_PID 0x1310 +#define WOOTING_60HE_PLUS_PID 0x1320 +#define WOOTING_60HE_V2_PID 0x1340 +#define WOOTING_80HE_PID 0x1400 +#define WOOTING_UWU_RGB_PID 0x1510 + +/*-----------------------------------------------------*\ +| Product ID helpers | +| XINP: Xbox input emulation enabled | +| DINP: Classic DirectInput emulation enabled | +| NONE: Controller emulation disabled | +\*-----------------------------------------------------*/ +#define XINP_PID(pid) (pid | 0x0000) +#define DINP_PID(pid) (pid | 0x0001) +#define NONE_PID(pid) (pid | 0x0002) + +void DetectWootingControllers(hid_device_info *info, const std::string &name) +{ + static const char *controller_name = "Wooting"; + LOG_DEBUG("[%s] Interface %i\tPage %04X\tUsage %i\tPath %s", controller_name, info->interface_number, info->usage_page, info->usage, info->path); + + hid_device *dev = hid_open_path(info->path); + + if(!dev) + return; + + WOOTING_DEVICE_TYPE wooting_type; + uint16_t pid = info->product_id; + + if(pid == WOOTING_ONE_LEGACY_PID) + { + wooting_type = WOOTING_KB_TKL; + } + else if(pid == WOOTING_TWO_LEGACY_PID) + { + wooting_type = WOOTING_KB_FULL; + } + else + { + //on modern devices, mask out the last nibble to get base PID + switch(pid & 0xFFF0) + { + case WOOTING_ONE_LEGACY_PID: + case WOOTING_ONE_PID: + wooting_type = WOOTING_KB_TKL; + break; + case WOOTING_TWO_LEGACY_PID: + case WOOTING_TWO_PID: + case WOOTING_TWO_LE_PID: + case WOOTING_TWO_HE_PID: + case WOOTING_TWO_HE_ARM_PID: + wooting_type = WOOTING_KB_FULL; + break; + case WOOTING_60HE_PID: + case WOOTING_60HE_ARM_PID: + case WOOTING_60HE_PLUS_PID: + case WOOTING_60HE_V2_PID: + wooting_type = WOOTING_KB_60PER; + break; + case WOOTING_80HE_PID: + wooting_type = WOOTING_KB_80PER; + break; + case WOOTING_UWU_RGB_PID: + wooting_type = WOOTING_KB_3PAD; + break; + default: + //default to largest keyboard if unknown + wooting_type = WOOTING_KB_FULL; + break; + } + } + + LOG_INFO("[%s] Detected Wooting device type %i for device at path %s", controller_name, wooting_type, info->path); + + //V1 firmware used the ATMEL VID, and uses the V1 controller + if(info->vendor_id == WOOTING_OLD_VID && info->usage_page == WOOTING_CONFIG_USAGE_PAGE_V2) + { + LOG_DEBUG("[%s] Old VID detected - creating V1 Controller", controller_name); + WootingV1KeyboardController *controller = new WootingV1KeyboardController(dev, info->path, wooting_type, name); + + LOG_DEBUG("[%s] Controller created - creating RGBController", controller_name); + RGBController_WootingKeyboard *rgb_controller = new RGBController_WootingKeyboard(controller); + + LOG_DEBUG("[%s] Initialization complete - Registering controller\t%s", controller_name, name.c_str()); + ResourceManager::get()->RegisterRGBController(rgb_controller); + return; + } + + //V2-V2.11 firmware uses the V2 protocol indicated by the V2 usage page + if(info->usage_page == WOOTING_CONFIG_USAGE_PAGE_V2) + { + LOG_DEBUG("[%s] V2 usage page detected - creating V2 Controller", controller_name); + WootingV2KeyboardController *controller = new WootingV2KeyboardController(dev, info->path, wooting_type, name); + + LOG_DEBUG("[%s] Controller created - creating RGBController", controller_name); + RGBController_WootingKeyboard *rgb_controller = new RGBController_WootingKeyboard(controller); + + LOG_DEBUG("[%s] Initialization complete - Registering controller\t%s", controller_name, name.c_str()); + ResourceManager::get()->RegisterRGBController(rgb_controller); + return; + } + + //V2.12+ firmware uses the new report structure indicated by the V3 usage page + if(info->usage_page == WOOTING_CONFIG_USAGE_PAGE_V3) + { + LOG_DEBUG("[%s] V3 usage page detected - creating V3 Controller", controller_name); + WootingV3KeyboardController *controller = new WootingV3KeyboardController(dev, info->path, wooting_type, name); + + LOG_DEBUG("[%s] Controller created - creating RGBController", controller_name); + RGBController_WootingKeyboard *rgb_controller = new RGBController_WootingKeyboard(controller); + + LOG_DEBUG("[%s] Initialization complete - Registering controller\t%s", controller_name, name.c_str()); + ResourceManager::get()->RegisterRGBController(rgb_controller); + return; + } + + hid_close(dev); + + LOG_TRACE("[%s] No compatible Wooting controller found for device at path %s", controller_name, info->path); +} +/*-----------------------------------------------------*\ +| Wooting keyboards use different PIDs based on which | +| gamepad emulation mode is selected. We can use their | +| base PID, and set the last nibble to get the modes. | +\*-----------------------------------------------------*/ +#define REGISTER_WOOTING_DETECTOR(name, vid, pid) \ + static HIDDeviceDetector detector_wooting_##vid##_##pid##_base(name, DetectWootingControllers, vid, NONE_PID(pid), HID_INTERFACE_ANY, HID_USAGE_PAGE_ANY, HID_USAGE_ANY); \ + static HIDDeviceDetector detector_wooting_##vid##_##pid##_xinp(name, DetectWootingControllers, vid, XINP_PID(pid), HID_INTERFACE_ANY, HID_USAGE_PAGE_ANY, HID_USAGE_ANY); \ + static HIDDeviceDetector detector_wooting_##vid##_##pid##_dinp(name, DetectWootingControllers, vid, DINP_PID(pid), HID_INTERFACE_ANY, HID_USAGE_PAGE_ANY, HID_USAGE_ANY) + + +// Legacy devices with V1 firmware +REGISTER_HID_DETECTOR_P("Wooting One (Legacy)", DetectWootingControllers, WOOTING_OLD_VID, WOOTING_ONE_LEGACY_PID, WOOTING_CONFIG_USAGE_PAGE_V2); +REGISTER_HID_DETECTOR_P("Wooting Two (Legacy)", DetectWootingControllers, WOOTING_OLD_VID, WOOTING_TWO_LEGACY_PID, WOOTING_CONFIG_USAGE_PAGE_V2); + +// All other devices +REGISTER_WOOTING_DETECTOR("Wooting One", WOOTING_NEW_VID, WOOTING_ONE_PID ); +REGISTER_WOOTING_DETECTOR("Wooting Two", WOOTING_NEW_VID, WOOTING_TWO_PID ); +REGISTER_WOOTING_DETECTOR("Wooting Two Lekker Edition", WOOTING_NEW_VID, WOOTING_TWO_LE_PID ); +REGISTER_WOOTING_DETECTOR("Wooting Two HE", WOOTING_NEW_VID, WOOTING_TWO_HE_PID ); +REGISTER_WOOTING_DETECTOR("Wooting Two HE (ARM)", WOOTING_NEW_VID, WOOTING_TWO_HE_ARM_PID); +REGISTER_WOOTING_DETECTOR("Wooting 60HE", WOOTING_NEW_VID, WOOTING_60HE_PID ); +REGISTER_WOOTING_DETECTOR("Wooting 60HE (ARM)", WOOTING_NEW_VID, WOOTING_60HE_ARM_PID ); +REGISTER_WOOTING_DETECTOR("Wooting 60HE+", WOOTING_NEW_VID, WOOTING_60HE_PLUS_PID ); +REGISTER_WOOTING_DETECTOR("Wooting 60HEv2", WOOTING_NEW_VID, WOOTING_60HE_V2_PID ); +REGISTER_WOOTING_DETECTOR("Wooting 80HE", WOOTING_NEW_VID, WOOTING_80HE_PID ); +REGISTER_WOOTING_DETECTOR("Wooting UwU RGB", WOOTING_NEW_VID, WOOTING_UWU_RGB_PID ); diff --git a/Controllers/WootingKeyboardController/WootingV1KeyboardController/WootingV1KeyboardController.cpp b/Controllers/WootingKeyboardController/WootingV1KeyboardController/WootingV1KeyboardController.cpp new file mode 100644 index 0000000..de6e7be --- /dev/null +++ b/Controllers/WootingKeyboardController/WootingV1KeyboardController/WootingV1KeyboardController.cpp @@ -0,0 +1,213 @@ +/*---------------------------------------------------------*\ +| WootingV1KeyboardController.cpp | +| | +| Driver for Wooting keyboards with v1 firmware | +| | +| Diogo Trindade (diogotr7) 25 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "WootingV1KeyboardController.h" + +#undef WOOTING_CONTROLLER_NAME +#define WOOTING_CONTROLLER_NAME "[WootingONE] " + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF +#define RGB_RAW_BUFFER_SIZE 96 + +static const unsigned int rgb_led_index[WOOTING_RGB_ROWS][WOOTING_RGB_COLUMNS] = +{ + { 0, NA, 11, 12, 23, 24, 36, 47, 85, 84, 49, 48, 59, 61, 73, 81, 80, 113, 114, 115, 116 }, + { 2, 1, 14, 13, 26, 25, 35, 38, 37, 87, 86, 95, 51, 63, 75, 72, 74, 96, 97, 98, 99 }, + { 3, 4, 15, 16, 27, 28, 39, 42, 40, 88, 89, 52, 53, 71, 76, 83, 77, 102, 103, 104, 100 }, + { 5, 6, 17, 18, 29, 30, 41, 46, 44, 90, 93, 54, 57, 65, NA, NA, NA, 105, 106, 107, NA }, + { 9, 8, 19, 20, 31, 34, 32, 45, 43, 91, 92, 55, NA, 66, NA, 78, NA, 108, 109, 110, 101 }, + { 10, 22, 21, NA, NA, NA, 33, NA, NA, NA, 94, 58, 67, 68, 70, 79, 82, NA, 111, 112, NA } +}; + +static uint16_t getCrc16ccitt(const uint8_t* buffer, uint16_t size) +{ + uint16_t crc = 0; + + while(size--) + { + crc ^= (*buffer++ << 8); + + for(uint8_t i = 0; i < 8; ++i) + { + if(crc & 0x8000) + { + crc = (crc << 1) ^ 0x1021; + } + else + { + crc = crc << 1; + } + } + } + + return crc; +} + +WootingV1KeyboardController::WootingV1KeyboardController(hid_device* dev_handle, const char *path, WOOTING_DEVICE_TYPE wooting_type, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + this->wooting_type = wooting_type; + key_code_limit = WOOTING_TWO_KEY_CODE_LIMIT; + + /*---------------------------------------------------------*\ + | Get device HID manufacturer and product strings | + \*---------------------------------------------------------*/ + const int szTemp = 256; + wchar_t tmpName[szTemp]; + + hid_get_manufacturer_string(dev, tmpName, szTemp); + vendor = std::string(StringUtils::wstring_to_string(tmpName)); + + hid_get_product_string(dev, tmpName, szTemp); + description = std::string(StringUtils::wstring_to_string(tmpName)); + + SendInitialize(); +} + +WootingV1KeyboardController::~WootingV1KeyboardController() +{ + +} + +void WootingV1KeyboardController::SendDirect(RGBColor* colors, uint8_t colour_count) +{ + const uint8_t pwm_mem_map[48] = + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D + }; + + unsigned char buffer0[RGB_RAW_BUFFER_SIZE] = {0}; + unsigned char buffer1[RGB_RAW_BUFFER_SIZE] = {0}; + unsigned char buffer2[RGB_RAW_BUFFER_SIZE] = {0}; + unsigned char buffer3[RGB_RAW_BUFFER_SIZE] = {0}; + unsigned char buffer4[RGB_RAW_BUFFER_SIZE] = {0}; + + for(uint8_t index = 0; index < colour_count; index++) + { + unsigned char row = index / WOOTING_RGB_COLUMNS; + unsigned char col = index % WOOTING_RGB_COLUMNS; + unsigned char led_index = rgb_led_index[row][col]; + + if(led_index > key_code_limit) + { + continue; + } + + unsigned char *buffer_pointer = buffer0; + + if(led_index >= 96) + { + buffer_pointer = buffer4; + } + else if(led_index >= 72) + { + buffer_pointer = buffer3; + } + else if(led_index >= 48) + { + buffer_pointer = buffer2; + } + else if(led_index >= 24) + { + buffer_pointer = buffer1; + } + else + { + buffer_pointer = buffer0; + } + + unsigned char buffer_index = pwm_mem_map[led_index % 24]; + buffer_pointer[buffer_index + 0x00] = RGBGetRValue(colors[index]); + buffer_pointer[buffer_index + 0x10] = RGBGetGValue(colors[index]); + buffer_pointer[buffer_index + 0x20] = RGBGetBValue(colors[index]); + } + + wooting_usb_send_buffer(RGB_PARTS::PART0, buffer0); + wooting_usb_send_buffer(RGB_PARTS::PART1, buffer1); + wooting_usb_send_buffer(RGB_PARTS::PART2, buffer2); + wooting_usb_send_buffer(RGB_PARTS::PART3, buffer3); + if(key_code_limit > WOOTING_ONE_KEY_CODE_LIMIT) + { + wooting_usb_send_buffer(RGB_PARTS::PART4, buffer4); + } +} + +bool WootingV1KeyboardController::wooting_usb_send_buffer(RGB_PARTS part_number, uint8_t* rgb_buffer) +{ + unsigned char report_buffer[WOOTING_REPORT_SIZE] = {0}; + + /*---------------------------------------------------------*\ + | Set up the Send Buffer packet | + \*---------------------------------------------------------*/ + report_buffer[0] = 0; // HID report index (unused) + report_buffer[1] = 0xD0; // Magic word + report_buffer[2] = 0xDA; // Magic word + report_buffer[3] = WOOTING_RAW_COLORS_REPORT; // Report ID + + switch(part_number) + { + case PART0: + report_buffer[4] = 0; // Slave nr + report_buffer[5] = 0; // Reg start address + break; + + case PART1: + report_buffer[4] = 0; // Slave nr + report_buffer[5] = RGB_RAW_BUFFER_SIZE; // Reg start address + break; + + case PART2: + report_buffer[4] = 1; // Slave nr + report_buffer[5] = 0; // Reg start address + break; + + case PART3: + report_buffer[4] = 1; // Slave nr + report_buffer[5] = RGB_RAW_BUFFER_SIZE; // Reg start address + break; + + case PART4: + report_buffer[4] = 2; // Slave nr + report_buffer[5] = 0; // Reg start address + break; + + default: + return false; + break; + } + + /*---------------------------------------------------------*\ + | Copy in the buffer data | + \*---------------------------------------------------------*/ + memcpy(&report_buffer[6], rgb_buffer, RGB_RAW_BUFFER_SIZE); + + /*---------------------------------------------------------*\ + | Calculate the CRC and append it to the packet | + \*---------------------------------------------------------*/ + unsigned short crc = getCrc16ccitt((unsigned char*)&report_buffer, WOOTING_REPORT_SIZE - 2); + report_buffer[127] = (unsigned char)crc; + report_buffer[128] = crc >> 8; + + /*---------------------------------------------------------*\ + | Send packet | + \*---------------------------------------------------------*/ + hid_write(dev, report_buffer, WOOTING_REPORT_SIZE); + + return true; +} diff --git a/Controllers/WootingKeyboardController/WootingV1KeyboardController/WootingV1KeyboardController.h b/Controllers/WootingKeyboardController/WootingV1KeyboardController/WootingV1KeyboardController.h new file mode 100644 index 0000000..5229629 --- /dev/null +++ b/Controllers/WootingKeyboardController/WootingV1KeyboardController/WootingV1KeyboardController.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| WootingV1KeyboardController.h | +| | +| Driver for Wooting keyboards with v1 firmware | +| | +| Diogo Trindade (diogotr7) 25 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "WootingKeyboardController.h" + +#define WOOTING_REPORT_SIZE 129 +#define WOOTING_ONE_KEY_CODE_LIMIT 95 +#define WOOTING_TWO_KEY_CODE_LIMIT 116 + +class WootingV1KeyboardController : public WootingKeyboardController +{ +public: + WootingV1KeyboardController(hid_device* dev_handle, const char *path, WOOTING_DEVICE_TYPE wooting_type, std::string dev_name); + ~WootingV1KeyboardController(); + + uint8_t key_code_limit; + + void SendDirect(RGBColor* colors, uint8_t colour_count); + +private: + bool wooting_usb_send_buffer(RGB_PARTS part_number, uint8_t* report_buffer); +}; diff --git a/Controllers/WootingKeyboardController/WootingV2KeyboardController/WootingV2KeyboardController.cpp b/Controllers/WootingKeyboardController/WootingV2KeyboardController/WootingV2KeyboardController.cpp new file mode 100644 index 0000000..c3f4470 --- /dev/null +++ b/Controllers/WootingKeyboardController/WootingV2KeyboardController/WootingV2KeyboardController.cpp @@ -0,0 +1,72 @@ +/*---------------------------------------------------------*\ +| WootingV2KeyboardController.cpp | +| | +| Driver for Wooting keyboards with v2 firmware | +| | +| Chris M (Dr_No) 09 Jul 2021 | +| Diogo Trindade (diogotr7) 25 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "WootingV2KeyboardController.h" + +#define WOOTING_TWO_REPORT_SIZE 257 + +#undef WOOTING_CONTROLLER_NAME +#define WOOTING_CONTROLLER_NAME "[WootingTWO] " + +//Indicates an unused entry in matrix +#define NA 0x7D + +//WootingTwo uses a 16bit color space +typedef uint16_t R5G6B5_color; +#define RGB888ToRGBcolor16(r, g, b) ((R5G6B5_color)((red & 0xF8) << 8 | (green & 0xFC) << 3 | (b & 0xF8) >> 3)) +#define RGB32ToRGBcolor16(color32) ((R5G6B5_color)((color32 & 0xF8) << 8 | (color32 & 0xFC00) >> 5 | (color32 & 0xF80000) >> 19)) + +WootingV2KeyboardController::WootingV2KeyboardController(hid_device* dev_handle, const char *path, WOOTING_DEVICE_TYPE wooting_type, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + this->wooting_type = wooting_type; + + /*---------------------------------------------------------*\ + | Get device HID manufacturer and product strings | + \*---------------------------------------------------------*/ + const int szTemp = 256; + wchar_t tmpName[szTemp]; + + hid_get_manufacturer_string(dev, tmpName, szTemp); + vendor = std::string(StringUtils::wstring_to_string(tmpName)); + + hid_get_product_string(dev, tmpName, szTemp); + description = std::string(StringUtils::wstring_to_string(tmpName)); + + SendInitialize(); +} + +WootingV2KeyboardController::~WootingV2KeyboardController() +{ + +} + +void WootingV2KeyboardController::SendDirect(RGBColor* colors, uint8_t color_count) +{ + uint8_t rgb_buffer[WOOTING_TWO_REPORT_SIZE] = { 0, 0xD0, 0xDA, WOOTING_RAW_COLORS_REPORT}; + + for(std::size_t index = 0; index < color_count; index++) + { + size_t buffer_index = 4 + (index * 2); + R5G6B5_color color16 = RGB32ToRGBcolor16(colors[index]); + + rgb_buffer[buffer_index] = color16 & 0xFF; + rgb_buffer[buffer_index+1] = color16 >> 8; + } + + uint16_t report_size = hid_write(dev, rgb_buffer, WOOTING_TWO_REPORT_SIZE); + LOG_DEBUG("%sSend buffer returned - %04i expected %04i", WOOTING_CONTROLLER_NAME, report_size, WOOTING_TWO_REPORT_SIZE); +} diff --git a/Controllers/WootingKeyboardController/WootingV2KeyboardController/WootingV2KeyboardController.h b/Controllers/WootingKeyboardController/WootingV2KeyboardController/WootingV2KeyboardController.h new file mode 100644 index 0000000..a23a827 --- /dev/null +++ b/Controllers/WootingKeyboardController/WootingV2KeyboardController/WootingV2KeyboardController.h @@ -0,0 +1,24 @@ +/*---------------------------------------------------------*\ +| WootingV2KeyboardController.h | +| | +| Driver for Wooting keyboards with v2 firmware | +| | +| Chris M (Dr_No) 09 Jul 2021 | +| Diogo Trindade (diogotr7) 25 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "WootingKeyboardController.h" + +class WootingV2KeyboardController : public WootingKeyboardController +{ +public: + WootingV2KeyboardController(hid_device* dev_handle, const char *path, WOOTING_DEVICE_TYPE wooting_type, std::string dev_name); + ~WootingV2KeyboardController(); + + void SendDirect(RGBColor* colors, uint8_t colour_count); +}; diff --git a/Controllers/WootingKeyboardController/WootingV3KeyboardController/WootingV3KeyboardController.cpp b/Controllers/WootingKeyboardController/WootingV3KeyboardController/WootingV3KeyboardController.cpp new file mode 100644 index 0000000..027d2eb --- /dev/null +++ b/Controllers/WootingKeyboardController/WootingV3KeyboardController/WootingV3KeyboardController.cpp @@ -0,0 +1,76 @@ +/*---------------------------------------------------------*\ +| WootingV3KeyboardController.cpp | +| | +| Driver for Wooting keyboards with v3 firmware | +| | +| Diogo Trindade (diogotr7) 25 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "WootingV3KeyboardController.h" + +#define WOOTING_V3_REPORT_SIZE 2046 + +#undef WOOTING_CONTROLLER_NAME +#define WOOTING_CONTROLLER_NAME "[WootingTWO] " + +//Indicates an unused entry in matrix +#define NA 0x7D + +//WootingTwo uses a 16bit color space +typedef uint16_t R5G6B5_color; +#define RGB888ToRGBcolor16(r, g, b) ((R5G6B5_color)((red & 0xF8) << 8 | (green & 0xFC) << 3 | (b & 0xF8) >> 3)) +#define RGB32ToRGBcolor16(color32) ((R5G6B5_color)((color32 & 0xF8) << 8 | (color32 & 0xFC00) >> 5 | (color32 & 0xF80000) >> 19)) + +WootingV3KeyboardController::WootingV3KeyboardController(hid_device* dev_handle, const char *path, WOOTING_DEVICE_TYPE wooting_type, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + this->wooting_type = wooting_type; + + /*---------------------------------------------------------*\ + | Get device HID manufacturer and product strings | + \*---------------------------------------------------------*/ + const int szTemp = 256; + wchar_t tmpName[szTemp]; + + hid_get_manufacturer_string(dev, tmpName, szTemp); + vendor = std::string(StringUtils::wstring_to_string(tmpName)); + + hid_get_product_string(dev, tmpName, szTemp); + description = std::string(StringUtils::wstring_to_string(tmpName)); + + SendInitialize(); +} + +WootingV3KeyboardController::~WootingV3KeyboardController() +{ + +} + +void WootingV3KeyboardController::SendDirect(RGBColor* colors, uint8_t color_count) +{ + uint8_t rgb_buffer[WOOTING_V3_REPORT_SIZE] = {0}; + rgb_buffer[0] = 4; + rgb_buffer[1] = 0xD1; + rgb_buffer[2] = 0xDA; + rgb_buffer[3] = WOOTING_RAW_COLORS_REPORT; + + for(std::size_t index = 0; index < color_count; index++) + { + size_t buffer_index = 4 + (index * 2); + R5G6B5_color color16 = RGB32ToRGBcolor16(colors[index]); + + rgb_buffer[buffer_index] = color16 & 0xFF; + rgb_buffer[buffer_index+1] = color16 >> 8; + } + + uint16_t report_size = hid_write(dev, rgb_buffer, WOOTING_V3_REPORT_SIZE); + LOG_DEBUG("%sSend buffer returned - %04i expected %04i", WOOTING_CONTROLLER_NAME, report_size, WOOTING_V3_REPORT_SIZE); +} + diff --git a/Controllers/WootingKeyboardController/WootingV3KeyboardController/WootingV3KeyboardController.h b/Controllers/WootingKeyboardController/WootingV3KeyboardController/WootingV3KeyboardController.h new file mode 100644 index 0000000..2e24a8d --- /dev/null +++ b/Controllers/WootingKeyboardController/WootingV3KeyboardController/WootingV3KeyboardController.h @@ -0,0 +1,23 @@ +/*---------------------------------------------------------*\ +| WootingV3KeyboardController.h | +| | +| Driver for Wooting keyboards with v3 firmware | +| | +| Diogo Trindade (diogotr7) 25 Dec 2025 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "WootingKeyboardController.h" + +class WootingV3KeyboardController : public WootingKeyboardController +{ +public: + WootingV3KeyboardController(hid_device* dev_handle, const char *path, WOOTING_DEVICE_TYPE wooting_type, std::string dev_name); + ~WootingV3KeyboardController(); + + void SendDirect(RGBColor* colors, uint8_t colour_count); +}; diff --git a/Controllers/WushiController/RGBController_WushiL50USB.cpp b/Controllers/WushiController/RGBController_WushiL50USB.cpp new file mode 100644 index 0000000..ac8da27 --- /dev/null +++ b/Controllers/WushiController/RGBController_WushiL50USB.cpp @@ -0,0 +1,196 @@ +/*---------------------------------------------------------*\ +| RGBController_WushiL50USB.cpp | +| | +| RGBController for Wushi L50 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_WushiL50USB.h" + +RGBController_WushiL50USB::RGBController_WushiL50USB(WushiL50USBController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->getName(); + type = DEVICE_TYPE_ACCESSORY; + description = "Wushi L50 device"; + vendor = "Wushi"; + location = controller->getLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = WUSHI_L50_EFFECT_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 1; + Direct.brightness_max = 2; + Direct.brightness = 2; + modes.push_back(Direct); + + mode Breath; + Breath.name = "Breathing"; + Breath.value = WUSHI_L50_EFFECT_BREATH; + Breath.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_SPEED; + Breath.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breath.speed_min = 1; + Breath.speed_max = 4; + Breath.speed = 3; + Breath.colors_min = 1; + Breath.colors_max = 1; + Breath.colors.resize(1); + modes.push_back(Breath); + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = WUSHI_L50_EFFECT_WAVE; + Wave.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_SPEED; + Wave.color_mode = MODE_COLORS_RANDOM; + Wave.speed_min = 1; + Wave.speed_max = 4; + Wave.speed = 3; + modes.push_back(Wave); + + mode Smooth; + Smooth.name = "Spectrum Cycle"; + Smooth.value = WUSHI_L50_EFFECT_SMOOTH; + Smooth.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED; + Smooth.color_mode = MODE_COLORS_RANDOM; + Smooth.speed_min = 1; + Smooth.speed_max = 4; + Smooth.speed = 3; + modes.push_back(Smooth); + + mode Race; + Race.name = "Race Cycle"; + Race.value = WUSHI_L50_EFFECT_RACE; + Race.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_SPEED; + Race.color_mode = MODE_COLORS_RANDOM; + Race.speed_min = 1; + Race.speed_max = 4; + Race.speed = 3; + modes.push_back(Race); + + mode Stack; + Stack.name = "Stacking"; + Stack.value = WUSHI_L50_EFFECT_STACK; + Stack.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Stack.color_mode = MODE_COLORS_RANDOM; + Stack.brightness_min = 1; + Stack.brightness_max = 2; + Stack.brightness = 2; + Stack.speed_min = 1; + Stack.speed_max = 4; + Stack.speed = 3; + modes.push_back(Stack); + + SetupZones(); +} + +RGBController_WushiL50USB::~RGBController_WushiL50USB() +{ + delete controller; +} + +void RGBController_WushiL50USB::SetupZones() +{ + zone new_zone; + new_zone.name = "Dock"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_count = WUSHI_L50_NUM_LEDS; + new_zone.leds_max = new_zone.leds_count; + new_zone.leds_min = new_zone.leds_count; + new_zone.matrix_map = NULL; + + zones.push_back(new_zone); + + for(unsigned int led_idx = 0; led_idx < WUSHI_L50_NUM_LEDS; led_idx++ ) + { + led new_led; + new_led.name = "Dock Zone "; + new_led.name.append(std::to_string(led_idx + 1)); + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_WushiL50USB::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_WushiL50USB::DeviceUpdateLEDs() +{ + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + state.SetColors(colors); + } + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + state.zone0_rgb[0] = RGBGetRValue(modes[active_mode].colors[0]); + state.zone0_rgb[1] = RGBGetGValue(modes[active_mode].colors[0]); + state.zone0_rgb[2] = RGBGetBValue(modes[active_mode].colors[0]); + } + + controller->setMode(&state); +} + +void RGBController_WushiL50USB::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_WushiL50USB::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_WushiL50USB::DeviceUpdateMode() +{ + state.Reset(); + + state.effect = modes[active_mode].value; + + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + state.SetColors(colors); + } + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + state.zone0_rgb[0] = RGBGetRValue(modes[active_mode].colors[0]); + state.zone0_rgb[1] = RGBGetGValue(modes[active_mode].colors[0]); + state.zone0_rgb[2] = RGBGetBValue(modes[active_mode].colors[0]); + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_LR) + { + state.wave_ltr = modes[active_mode].direction ? 0 : 1; + state.wave_rtl = modes[active_mode].direction ? 1 : 0; + } + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + state.speed = modes[active_mode].speed; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_BRIGHTNESS) + { + state.brightness = modes[active_mode].brightness; + } + + controller->setMode(&state); +} + +void RGBController_WushiL50USB::DeviceSaveMode() +{ + /*---------------------------------------------------------*\ + | This device does not support saving or multiple modes | + \*---------------------------------------------------------*/ +} diff --git a/Controllers/WushiController/RGBController_WushiL50USB.h b/Controllers/WushiController/RGBController_WushiL50USB.h new file mode 100644 index 0000000..f225948 --- /dev/null +++ b/Controllers/WushiController/RGBController_WushiL50USB.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------*\ +| RGBController_WushiL50USB.h | +| | +| RGBController for Wushi L50 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "WushiL50USBController.h" +#include "RGBController.h" + +class RGBController_WushiL50USB : public RGBController +{ +public: + RGBController_WushiL50USB(WushiL50USBController* controller_ptr); + ~RGBController_WushiL50USB(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + +private: + WushiL50USBController * controller; + WushiL50State state; +}; diff --git a/Controllers/WushiController/WushiL50USBController.cpp b/Controllers/WushiController/WushiL50USBController.cpp new file mode 100644 index 0000000..81efb2c --- /dev/null +++ b/Controllers/WushiController/WushiL50USBController.cpp @@ -0,0 +1,88 @@ +/*---------------------------------------------------------*\ +| WushiL50USBController.cpp | +| | +| Driver for Wushi L50 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "WushiL50USBController.h" + +WushiL50USBController::WushiL50USBController(hidapi_wrapper hid_wrapper, hid_device* dev_handle, const char* path, std::string dev_name) +{ + wrapper = hid_wrapper; + dev = dev_handle; + location = path; + name = dev_name; +} + +WushiL50USBController::~WushiL50USBController() +{ + wrapper.hid_close(dev); +} + +std::string WushiL50USBController::getName() +{ + return name; +} + +std::string WushiL50USBController::getLocation() +{ + return location; +} + +std::string WushiL50USBController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = wrapper.hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void WushiL50USBController::setMode(WushiL50State * in_mode) +{ + unsigned char usb_buf[WUSHI_L50_HID_PACKET_SIZE]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up custom lighting packet | + \*-----------------------------------------------------*/ +#ifdef _WIN32 + #define OFFSET 1 + usb_buf[0x00] = 0xCC; +#else + #define OFFSET 0 +#endif + usb_buf[0x00 + OFFSET] = 0x16; + usb_buf[0x01 + OFFSET] = in_mode->effect; + usb_buf[0x02 + OFFSET] = in_mode->speed; + usb_buf[0x03 + OFFSET] = in_mode->brightness; + + /*-----------------------------------------------------*\ + | Copy in color data | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x04 + OFFSET], in_mode->zone0_rgb, 3); + memcpy(&usb_buf[0x07 + OFFSET], in_mode->zone1_rgb, 3); + memcpy(&usb_buf[0x0A + OFFSET], in_mode->zone2_rgb, 3); + memcpy(&usb_buf[0x0D + OFFSET], in_mode->zone3_rgb, 3); + + usb_buf[0x11 + OFFSET] = in_mode->wave_ltr; + usb_buf[0x12 + OFFSET] = in_mode->wave_rtl; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + wrapper.hid_send_feature_report(dev, usb_buf, WUSHI_L50_HID_PACKET_SIZE); +} diff --git a/Controllers/WushiController/WushiL50USBController.h b/Controllers/WushiController/WushiL50USBController.h new file mode 100644 index 0000000..1642ab6 --- /dev/null +++ b/Controllers/WushiController/WushiL50USBController.h @@ -0,0 +1,112 @@ +/*---------------------------------------------------------*\ +| WushiL50USBController.h | +| | +| Driver for Wushi L50 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" +#include "hidapi_wrapper.h" + +#ifndef HID_MAX_STR +#define HID_MAX_STR 255 +#endif + +#define WUSHI_L50_HID_PACKET_SIZE 65 +#define WUSHI_L50_NUM_LEDS 4 + +enum WUSHI_L50_EFFECT +{ + WUSHI_L50_EFFECT_STATIC = 1, /* Static mode */ + WUSHI_L50_EFFECT_BREATH = 3, /* Breathing mode */ + WUSHI_L50_EFFECT_WAVE = 4, /* Wave mode */ + WUSHI_L50_EFFECT_SMOOTH = 6, /* Smooth mode */ + WUSHI_L50_EFFECT_RACE = 8, /* Race mode */ + WUSHI_L50_EFFECT_STACK = 10, /* Stack mode */ +}; + +enum WUSHI_L50_BRIGHTNESS +{ + WUSHI_L50_BRIGHTNESS_LOW = 1, /* Low brightness */ + WUSHI_L50_BRIGHTNESS_HIGH = 2, /* High brightness */ +}; + +enum WUSHI_L50_SPEED +{ + WUSHI_L50_SPEED_SLOWEST = 1, /* Slowest speed */ + WUSHI_L50_SPEED_SLOW = 2, /* Slow speed */ + WUSHI_L50_SPEED_FAST = 3, /* Fast speed */ + WUSHI_L50_SPEED_FASTEST = 4, /* Fastest speed */ +}; + +enum WUSHI_L50_Direction +{ + WUSHI_L50_Direction_LEFT = 1, /* Left direction */ + WUSHI_L50_Direction_RIGHT = 2, /* Right direction */ +}; + +class WushiL50State +{ +public: + uint8_t effect = WUSHI_L50_EFFECT_STATIC; + uint8_t speed = WUSHI_L50_SPEED_SLOWEST; + uint8_t brightness = WUSHI_L50_BRIGHTNESS_LOW; + uint8_t zone0_rgb[3] = {0xFF, 0xFF, 0xFF}; + uint8_t zone1_rgb[3] = {0xFF, 0xFF, 0xFF}; + uint8_t zone2_rgb[3] = {0xFF, 0xFF, 0xFF}; + uint8_t zone3_rgb[3] = {0xFF, 0xFF, 0xFF}; + uint8_t wave_ltr = 0; + uint8_t wave_rtl = 0; + + void Reset() + { + effect = WUSHI_L50_EFFECT_STATIC; + speed = WUSHI_L50_SPEED_SLOWEST; + brightness = WUSHI_L50_BRIGHTNESS_LOW; + wave_ltr = 0; + wave_rtl = 0; + } + + void SetColors(std::vector group_colors) + { + zone0_rgb[0] = RGBGetRValue(group_colors[0]); + zone0_rgb[1] = RGBGetGValue(group_colors[0]); + zone0_rgb[2] = RGBGetBValue(group_colors[0]); + zone1_rgb[0] = RGBGetRValue(group_colors[1]); + zone1_rgb[1] = RGBGetGValue(group_colors[1]); + zone1_rgb[2] = RGBGetBValue(group_colors[1]); + zone2_rgb[0] = RGBGetRValue(group_colors[2]); + zone2_rgb[1] = RGBGetGValue(group_colors[2]); + zone2_rgb[2] = RGBGetBValue(group_colors[2]); + zone3_rgb[0] = RGBGetRValue(group_colors[3]); + zone3_rgb[1] = RGBGetGValue(group_colors[3]); + zone3_rgb[2] = RGBGetBValue(group_colors[3]); + + wave_rtl = 0; + } +}; + +class WushiL50USBController +{ +public: + WushiL50USBController(hidapi_wrapper hid_wrapper, hid_device* dev_handle, const char* path, std::string dev_name); + ~WushiL50USBController(); + + std::string getName(); + std::string getLocation(); + std::string GetSerialString(); + + void setMode(WushiL50State * in_mode); + +private: + hidapi_wrapper wrapper; + hid_device * dev; + std::string location; + std::string name; +}; diff --git a/Controllers/WushiController/WushiL50USBDetect.cpp b/Controllers/WushiController/WushiL50USBDetect.cpp new file mode 100644 index 0000000..896198e --- /dev/null +++ b/Controllers/WushiController/WushiL50USBDetect.cpp @@ -0,0 +1,38 @@ +/*---------------------------------------------------------*\ +| WushiL50USBControllerDetect.cpp | +| | +| Detector for Wushi L50 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "WushiL50USBController.h" +#include "RGBController_WushiL50USB.h" + +/*-----------------------------------------------------*\ +| Wushi vendor ID | +\*-----------------------------------------------------*/ +#define WUSHI_VID 0x306F + +/*-----------------------------------------------------*\ +| Wushi device ID | +\*-----------------------------------------------------*/ +#define WUSHI_PID 0x1234 + +void DetectWushiL50USBControllers(hidapi_wrapper wrapper, hid_device_info* info, const std::string& name) +{ + hid_device* dev = wrapper.hid_open_path(info->path); + + if(dev) + { + WushiL50USBController* controller = new WushiL50USBController(wrapper, dev, info->path, name); + RGBController_WushiL50USB* rgb_controller = new RGBController_WushiL50USB(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_WRAPPED_DETECTOR("JSAUX RGB Docking Station", DetectWushiL50USBControllers, WUSHI_VID, WUSHI_PID); diff --git a/Controllers/XPGSummonerKeyboardController/RGBController_XPGSummoner.cpp b/Controllers/XPGSummonerKeyboardController/RGBController_XPGSummoner.cpp new file mode 100644 index 0000000..8b5b54c --- /dev/null +++ b/Controllers/XPGSummonerKeyboardController/RGBController_XPGSummoner.cpp @@ -0,0 +1,333 @@ +/*---------------------------------------------------------*\ +| RGBController_XPGSummoner.cpp | +| | +| RGBController for XPG Summoner keyboard | +| | +| Erick Granados (eriosgamer) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_XPGSummoner.h" + +#define NA 0xFFFFFFFF +#define LED_REAL_COUNT (6 * 21) +#define LED_COUNT (LED_REAL_COUNT - 22) + +/*---------------------------------------------------------*\ +| ordered_matrix: Physical LED layout | +\*---------------------------------------------------------*/ +static unsigned int ordered_matrix[6][21] = +{ + {0, NA, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, NA, NA, NA, NA}, + {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36}, + {37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57}, + {58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, NA, 70, NA, NA, NA, 71, 72, 73, NA}, + {74, NA, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, NA, NA, 86, NA, 87, 88, 89, 90}, + {91, 92, 93, NA, NA, NA, 94, NA, NA, NA, 95, 96, 97, 98, 99, 100, 101, 102, NA, 103, NA} +}; + +/*---------------------------------------------------------*\ +| matrix_map: Logical LED mapping | +\*---------------------------------------------------------*/ +static unsigned int matrix_map[6][21] = +{ + {11, NA, 22, 30, 25, 27, 7, 51, 57, 62, 86, 87, 83, 85, 79, 72, 0, NA, NA, NA, NA}, + {14, 15, 23, 31, 39, 38, 46, 47, 55, 63, 71, 70, 54, 81, 102, 118, 110, 92, 100, 108, 109}, + {9, 8, 16, 24, 32, 33, 41, 40, 48, 56, 64, 65, 49, 82, 94, 119, 111, 88, 96, 104, 112}, + {17, 10, 18, 26, 34, 35, 43, 42, 50, 58, 66, 67, NA, 84, NA, NA, NA, 89, 97, 105, NA}, + {121, NA, 12, 20, 28, 36, 37, 45, 44, 52, 60, 69, 122, NA, NA, 115, NA, 90, 98, 106, 114}, + {6, 124, 75, NA, NA, NA, 91, NA, NA, NA, 77, 125, 61, 4, 117, 93, 101, 99, NA, 107, NA} +}; + +/*---------------------------------------------------------*\ +| zone_names: Zone names | +\*---------------------------------------------------------*/ +const char *zone_names[] = +{ + ZONE_EN_KEYBOARD +}; + +zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX +}; + +const unsigned int zone_sizes[] = +{ + LED_COUNT +}; + +/*---------------------------------------------------------*\ +| led_names: LED names | +\*---------------------------------------------------------*/ +static const char *led_names[] = +{ + KEY_EN_ESCAPE, // Esc + KEY_EN_F1, // F1 + KEY_EN_F2, // F2 + KEY_EN_F3, // F3 + KEY_EN_F4, // F4 + KEY_EN_F5, // F5 + KEY_EN_F6, // F6 + KEY_EN_F7, // F7 + KEY_EN_F8, // F8 + KEY_EN_F9, // F9 + KEY_EN_F10, // F10 + KEY_EN_F11, // F11 + KEY_EN_F12, // F12 + KEY_EN_PRINT_SCREEN, // PrtSc + KEY_EN_SCROLL_LOCK, // Scroll + KEY_EN_PAUSE_BREAK, // Pause + KEY_EN_BACK_TICK, // ` + KEY_EN_1, // 1 + KEY_EN_2, // 2 + KEY_EN_3, // 3 + KEY_EN_4, // 4 + KEY_EN_5, // 5 + KEY_EN_6, // 6 + KEY_EN_7, // 7 + KEY_EN_8, // 8 + KEY_EN_9, // 9 + KEY_EN_0, // 0 + KEY_EN_MINUS, // - + KEY_EN_EQUALS, // = + KEY_EN_BACKSPACE, // Backspace + KEY_EN_INSERT, // Insert + KEY_EN_HOME, // Home + KEY_EN_PAGE_UP, // PgUp + KEY_EN_NUMPAD_LOCK, // NumLock + KEY_EN_NUMPAD_DIVIDE, // / + KEY_EN_NUMPAD_TIMES, // * + KEY_EN_NUMPAD_MINUS, // - + KEY_EN_TAB, // Tab + KEY_EN_Q, // Q + KEY_EN_W, // W + KEY_EN_E, // E + KEY_EN_R, // R + KEY_EN_T, // T + KEY_EN_Y, // Y + KEY_EN_U, // U + KEY_EN_I, // I + KEY_EN_O, // O + KEY_EN_P, // P + KEY_EN_LEFT_BRACKET, // [ + KEY_EN_RIGHT_BRACKET, // ] + KEY_EN_ANSI_BACK_SLASH, // Backslash + KEY_EN_DELETE, // Del + KEY_EN_END, // End + KEY_EN_PAGE_DOWN, // PgDn + KEY_EN_NUMPAD_7, // 7 + KEY_EN_NUMPAD_8, // 8 + KEY_EN_NUMPAD_9, // 9 + KEY_EN_NUMPAD_PLUS, // + + KEY_EN_CAPS_LOCK, // Caps + KEY_EN_A, // A + KEY_EN_S, // S + KEY_EN_D, // D + KEY_EN_F, // F + KEY_EN_G, // G + KEY_EN_H, // H + KEY_EN_J, // J + KEY_EN_K, // K + KEY_EN_L, // L + KEY_EN_SEMICOLON, // ; + KEY_EN_QUOTE, // ' + KEY_EN_ISO_ENTER, // Enter + KEY_EN_NUMPAD_4, // 4 + KEY_EN_NUMPAD_5, // 5 + KEY_EN_NUMPAD_6, // 6 + KEY_EN_LEFT_SHIFT, // Shift + KEY_EN_Z, // Z + KEY_EN_X, // X + KEY_EN_C, // C + KEY_EN_V, // V + KEY_EN_B, // B + KEY_EN_N, // N + KEY_EN_M, // M + KEY_EN_COMMA, // , + KEY_EN_PERIOD, // . + KEY_EN_FORWARD_SLASH, // / + KEY_EN_RIGHT_SHIFT, // Shift + KEY_EN_UP_ARROW, // ↑ + KEY_EN_NUMPAD_1, // 1 + KEY_EN_NUMPAD_2, // 2 + KEY_EN_NUMPAD_3, // 3 + KEY_EN_NUMPAD_ENTER, // Enter + KEY_EN_LEFT_CONTROL, // Ctrl + KEY_EN_LEFT_WINDOWS, // Win + KEY_EN_LEFT_ALT, // Alt + KEY_EN_SPACE, // Space + KEY_EN_RIGHT_ALT, // AltGr + KEY_EN_RIGHT_FUNCTION, // Fn + KEY_EN_MENU, // Menu + KEY_EN_RIGHT_CONTROL, // Ctrl + KEY_EN_LEFT_ARROW, // ← + KEY_EN_DOWN_ARROW, // ↓ + KEY_EN_RIGHT_ARROW, // → + KEY_EN_NUMPAD_0, // 0 + KEY_EN_NUMPAD_PERIOD // . +}; + +/**------------------------------------------------------------------*\ + @name XPG Summoner Keyboard + @category Keyboard + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectXPGSummonerControllers + @comment +\*-------------------------------------------------------------------*/ + +/*---------------------------------------------------------*\ +| RGBController_XPGSummoner constructor | +\*---------------------------------------------------------*/ +RGBController_XPGSummoner::RGBController_XPGSummoner(XPGSummonerController *controller_ptr) +{ + controller = controller_ptr; + name = controller->GetNameString(); + vendor = "XPG"; + description = "XPG Summoner Keyboard Device"; + location = controller->GetLocationString(); + serial = controller->GetSerialString(); + type = DEVICE_TYPE_KEYBOARD; + + mode Direct; + Direct.name = "Direct"; + Direct.value = XPG_SUMMONER_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +/*---------------------------------------------------------*\ +| Destructor | +\*---------------------------------------------------------*/ +RGBController_XPGSummoner::~RGBController_XPGSummoner() +{ + for(unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if(zones[zone_index].matrix_map != NULL) + { + delete zones[zone_index].matrix_map; + } + } + delete controller; +} + +/*---------------------------------------------------------*\ +| SetupZones: Initializes zones and LEDs | +\*---------------------------------------------------------*/ +void RGBController_XPGSummoner::SetupZones() +{ + leds.clear(); + colors.clear(); + zones.clear(); + leds.reserve(LED_COUNT); + colors.reserve(LED_COUNT); + zones.reserve(1); + + zone new_zone; + new_zone.name = zone_names[0]; + new_zone.type = zone_types[0]; + new_zone.leds_min = zone_sizes[0]; + new_zone.leds_max = zone_sizes[0]; + new_zone.leds_count = zone_sizes[0]; + + if(new_zone.type == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = 6; + new_zone.matrix_map->width = 21; + new_zone.matrix_map->map = (unsigned int *)&ordered_matrix; + } + else + { + new_zone.matrix_map = NULL; + } + zones.push_back(new_zone); + + size_t linear_idx = 0; + for(int row = 0; row < 6; ++row) + { + for(int col = 0; col < 21; ++col) + { + unsigned int led_id = matrix_map[row][col]; + if(led_id == NA) + continue; + led new_led; + new_led.name = led_names[linear_idx]; + new_led.value = led_id; + leds.push_back(new_led); + ++linear_idx; + } + } + colors.assign(LED_COUNT, 0x000000); + + SetupColors(); +} + +/*---------------------------------------------------------*\ +| ResizeZone: Not supported for this device | +\*---------------------------------------------------------*/ +void RGBController_XPGSummoner::ResizeZone(int /*zone*/, int /*new_size*/) +{ + // This device does not support resizing zones +} + +/*---------------------------------------------------------*\ +| DeviceUpdateLEDs: Updates LED colors | +\*---------------------------------------------------------*/ +void RGBController_XPGSummoner::DeviceUpdateLEDs() +{ + const unsigned char brightness = 0x64; + const unsigned int frame_buf_length = 126 * 4; + unsigned char frame_buf[frame_buf_length] = {0}; + + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + if(leds[led_idx].value == NA) + { + continue; + } + if(modes[active_mode].color_mode == MODE_COLORS_PER_LED) + { + std::size_t real_idx = leds[led_idx].value; + frame_buf[(real_idx * 4) + 0] = brightness; + frame_buf[(real_idx * 4) + 1] = RGBGetRValue(colors[led_idx]); + frame_buf[(real_idx * 4) + 2] = RGBGetGValue(colors[led_idx]); + frame_buf[(real_idx * 4) + 3] = RGBGetBValue(colors[led_idx]); + } + } + controller->SendColors(frame_buf, sizeof(frame_buf)); +} + +/*---------------------------------------------------------*\ +| UpdateZoneLEDs: Updates all LEDs in a zone | +\*---------------------------------------------------------*/ +void RGBController_XPGSummoner::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +/*---------------------------------------------------------*\ +| UpdateSingleLED: Updates a single LED | +\*---------------------------------------------------------*/ +void RGBController_XPGSummoner::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +/*---------------------------------------------------------*\ +| DeviceUpdateMode: Updates device mode | +\*---------------------------------------------------------*/ +void RGBController_XPGSummoner::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + diff --git a/Controllers/XPGSummonerKeyboardController/RGBController_XPGSummoner.h b/Controllers/XPGSummonerKeyboardController/RGBController_XPGSummoner.h new file mode 100644 index 0000000..8266f4f --- /dev/null +++ b/Controllers/XPGSummonerKeyboardController/RGBController_XPGSummoner.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| RGBController_XPGSummoner.h | +| | +| RGBController for XPG Summoner keyboard | +| | +| Erick Granados (eriosgamer) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "XPGSummonerController.h" + +class RGBController_XPGSummoner : public RGBController +{ +public: + RGBController_XPGSummoner(XPGSummonerController* controller_ptr); + ~RGBController_XPGSummoner(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + void DeviceUpdateMode(); + +private: + XPGSummonerController* controller; +}; diff --git a/Controllers/XPGSummonerKeyboardController/XPGSummonerController.cpp b/Controllers/XPGSummonerKeyboardController/XPGSummonerController.cpp new file mode 100644 index 0000000..b4c19fe --- /dev/null +++ b/Controllers/XPGSummonerKeyboardController/XPGSummonerController.cpp @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| XPGSummonerController.cpp | +| | +| Driver for XPG Summoner keyboard | +| | +| Erick Granados (eriosgamer) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "XPGSummonerController.h" +#include "StringUtils.h" + +XPGSummonerController::XPGSummonerController(hid_device *dev_handle, const char *path, const unsigned short pid, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + usb_pid = pid; + + SendInitialize(); +} + +XPGSummonerController::~XPGSummonerController() +{ + hid_close(dev); +} + +void XPGSummonerController::SendInitialize() +{ + unsigned char init_buf[265] = + { + 0x07, 0xEA, 0x00, 0x00, + }; + memset(init_buf + 4, 0x00, 261); + hid_write(dev, init_buf, 265); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +std::string XPGSummonerController::GetLocationString() +{ + return("HID: " + location); +} + +std::string XPGSummonerController::GetNameString() +{ + return(name); +} + +std::string XPGSummonerController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +unsigned short XPGSummonerController::GetUSBPID() +{ + return(usb_pid); +} + +void XPGSummonerController::SendColors(unsigned char *color_data, unsigned int color_data_size) +{ + const int leds_count = 126; + const int bytes_per_led = 4; + const int total_bytes = leds_count * bytes_per_led; + const int block_size = 256; + int zones = 2; + + for(int zone = 0; zone < zones; zone++) + { + int offset = zone * block_size; + int remaining = total_bytes - offset; + color_data_size = (remaining > block_size) ? block_size : remaining; + + SendColorDataPacket(zone, &color_data[offset], color_data_size); + } +} + +unsigned int XPGSummonerController::SendColorDataPacket( + unsigned char packet_id, + unsigned char *color_data, + unsigned int color_size) +{ + unsigned char packet[265] = {0}; + packet[0] = 0x07; + packet[1] = 0xA3; + packet[2] = 0x08; + packet[3] = 0x00; + packet[4] = packet_id; + packet[5] = 0x00; + + unsigned int copy_size = (color_size > 256) ? 256 : color_size; + memcpy(&packet[6], color_data, copy_size); + + hid_write(dev, packet, 265); + + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + return copy_size; +} + +void XPGSummonerController::SendTerminateColorPacket() +{ + /*-------------------------------*\ + | Set up Terminate Color packet | + | This packet is used to stop | + | any active color effects on the | + | keyboard. | + \*-------------------------------*/ + unsigned char terminate_buf[265] = + { + 0x07, 0xEA, 0x00, 0x00, + }; + memset(terminate_buf + 4, 0x00, 261); + hid_write(dev, terminate_buf, 265); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} diff --git a/Controllers/XPGSummonerKeyboardController/XPGSummonerController.h b/Controllers/XPGSummonerKeyboardController/XPGSummonerController.h new file mode 100644 index 0000000..fa97231 --- /dev/null +++ b/Controllers/XPGSummonerKeyboardController/XPGSummonerController.h @@ -0,0 +1,66 @@ +/*---------------------------------------------------------*\ +| XPGSummonerController.h | +| | +| Driver for XPG Summoner keyboard | +| | +| Erick Granados (eriosgamer) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "hidapi.h" +#include "RGBController.h" + +/*-----------------------------------------------------*\ +| XPG vendor ID | +\*-----------------------------------------------------*/ +#define XPG_VID 0x125F + +/*-----------------------------------------------------*\ +| Keyboard product ID | +\*-----------------------------------------------------*/ +#define XPG_SUMMONER_PID 0x9418 + +enum +{ + XPG_SUMMONER_MODE_DIRECT = 0x01 +}; + +class XPGSummonerController +{ +public: + XPGSummonerController(hid_device *dev_handle, const char *path, const unsigned short pid, std::string dev_name); + ~XPGSummonerController(); + + std::string GetLocationString(); + std::string GetNameString(); + std::string GetSerialString(); + unsigned short GetUSBPID(); + + void SendColors + ( + unsigned char *color_data, + unsigned int color_data_size + ); + + unsigned int SendColorDataPacket + ( + unsigned char packet_id, + unsigned char *color_data, + unsigned int color_size + ); + + void SendTerminateColorPacket(); + + void SendInitialize(); + +private: + hid_device *dev; + std::string location; + std::string name; + unsigned short usb_pid; +}; diff --git a/Controllers/XPGSummonerKeyboardController/XPGSummonerControllerDetect.cpp b/Controllers/XPGSummonerKeyboardController/XPGSummonerControllerDetect.cpp new file mode 100644 index 0000000..1efa485 --- /dev/null +++ b/Controllers/XPGSummonerKeyboardController/XPGSummonerControllerDetect.cpp @@ -0,0 +1,37 @@ +/*---------------------------------------------------------*\ +| XPGSummonerControllerDetect.cpp | +| | +| Detector for XPG Summoner keyboard | +| | +| Erick Granados (eriosgamer) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "XPGSummonerController.h" +#include "RGBController_XPGSummoner.h" +#include + +/******************************************************************************************\ +* * +* DetectXPGSummonerControllers * +* * +* Tests the USB address to see if a XPG Summoner Keyboard controller exists there. * +* * +\******************************************************************************************/ + +void DetectXPGSummonerControllers(hid_device_info *info, const std::string &name) +{ + hid_device *dev = hid_open_path(info->path); + + if(dev) + { + XPGSummonerController *controller = new XPGSummonerController(dev, info->path, info->product_id, name); + RGBController_XPGSummoner *rgb_controller = new RGBController_XPGSummoner(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectXPGSummonerControllers() */ + +REGISTER_HID_DETECTOR_IPU("XPG Summoner Gaming Keyboard", DetectXPGSummonerControllers, XPG_VID, XPG_SUMMONER_PID, 2, 0xFF01, 0x0001); diff --git a/Controllers/YeelightController/RGBController_Yeelight.cpp b/Controllers/YeelightController/RGBController_Yeelight.cpp new file mode 100644 index 0000000..e4ce6b0 --- /dev/null +++ b/Controllers/YeelightController/RGBController_Yeelight.cpp @@ -0,0 +1,119 @@ +/*---------------------------------------------------------*\ +| RGBController_Yeelight.cpp | +| | +| RGBController for Yeelight | +| | +| Adam Honse (CalcProgrammer1) 18 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Yeelight.h" + +/**------------------------------------------------------------------*\ + @name Yeelight + @category Light + @type Network + @save :x: + @direct :rotating_light: + @effects :white_check_mark: + @detectors DetectYeelightControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Yeelight::RGBController_Yeelight(YeelightController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = controller->GetManufacturer(); + type = DEVICE_TYPE_LIGHT; + version = controller->GetVersion(); + description = "Yeelight Device"; + serial = controller->GetUniqueID(); + location = controller->GetLocation(); + + /*---------------------------------------------------------*\ + | If using music mode, use mode name "Direct" as the music | + | mode interface can handle high speed updates from effects | + | engine software. If not using music mode, name the mode | + | "Static" to prevent effect engine use, as the standard | + | interface is limited to a very low update rate | + \*---------------------------------------------------------*/ + if(controller->GetMusicMode()) + { + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + } + else + { + mode Static; + Static.name = "Static"; + Static.value = 0; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + } + + SetupZones(); +} + +RGBController_Yeelight::~RGBController_Yeelight() +{ + delete controller; +} + +void RGBController_Yeelight::SetupZones() +{ + zone led_zone; + led_zone.name = "RGB Light"; + led_zone.type = ZONE_TYPE_SINGLE; + led_zone.leds_min = 1; + led_zone.leds_max = 1; + led_zone.leds_count = 1; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + led new_led; + new_led.name = "RGB Light"; + + leds.push_back(new_led); + + SetupColors(); +} + +void RGBController_Yeelight::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Yeelight::DeviceUpdateLEDs() +{ + unsigned char red = RGBGetRValue(colors[0]); + unsigned char grn = RGBGetGValue(colors[0]); + unsigned char blu = RGBGetBValue(colors[0]); + + controller->SetColor(red, grn, blu); +} + +void RGBController_Yeelight::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Yeelight::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Yeelight::DeviceUpdateMode() +{ + +} diff --git a/Controllers/YeelightController/RGBController_Yeelight.h b/Controllers/YeelightController/RGBController_Yeelight.h new file mode 100644 index 0000000..b11e3cd --- /dev/null +++ b/Controllers/YeelightController/RGBController_Yeelight.h @@ -0,0 +1,34 @@ +/*---------------------------------------------------------*\ +| RGBController_Yeelight.h | +| | +| RGBController for Yeelight | +| | +| Adam Honse (CalcProgrammer1) 18 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "YeelightController.h" + +class RGBController_Yeelight : public RGBController +{ +public: + RGBController_Yeelight(YeelightController* controller_ptr); + ~RGBController_Yeelight(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + YeelightController* controller; +}; diff --git a/Controllers/YeelightController/YeelightController.cpp b/Controllers/YeelightController/YeelightController.cpp new file mode 100644 index 0000000..b2038ff --- /dev/null +++ b/Controllers/YeelightController/YeelightController.cpp @@ -0,0 +1,251 @@ +/*---------------------------------------------------------*\ +| YeelightController.cpp | +| | +| Driver for Yeelight | +| | +| Adam Honse (CalcProgrammer1) 18 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "YeelightController.h" +#include + +using json = nlohmann::json; + +YeelightController::YeelightController(std::string ip, std::string host_ip, bool music_mode_val) +{ + /*-----------------------------------------------------------------*\ + | Fill in location string with device's IP address | + \*-----------------------------------------------------------------*/ + location = "IP: " + ip; + music_mode = music_mode_val; + this->host_ip = host_ip; + + /*-----------------------------------------------------------------*\ + | Open a TCP client sending to the device's IP, port 38899 | + \*-----------------------------------------------------------------*/ + port.tcp_client(ip.c_str(), "55443"); + + SetPower(); + + if(music_mode) + { + bool port_opened = false; + char port_string[8]; + + /*-----------------------------------------------------------------*\ + | Start searching for open port for music mode at 55444 | + \*-----------------------------------------------------------------*/ + music_mode_port = 55444; + + while(!port_opened) + { + /*-----------------------------------------------------------------*\ + | Convert port to string | + \*-----------------------------------------------------------------*/ + snprintf(port_string, 8, "%d", music_mode_port); + + /*-----------------------------------------------------------------*\ + | Open a TCP server for music mode if enabled | + \*-----------------------------------------------------------------*/ + port_opened = music_mode_server.tcp_server(port_string); + + if(!port_opened) + { + music_mode_port++; + + /*-------------------------------------------------------------*\ + | If we've tested the maximum port value, 65535, and it failed, | + | give up and don't use music mode | + \*-------------------------------------------------------------*/ + if(music_mode_port >= 65536) + { + music_mode = false; + break; + } + + continue; + } + + /*-----------------------------------------------------------------*\ + | Command bulb to connect to our TCP server | + \*-----------------------------------------------------------------*/ + SetMusicMode(); + + /*-----------------------------------------------------------------*\ + | Get the client socket for the music mode connection | + \*-----------------------------------------------------------------*/ + music_mode_sock = music_mode_server.tcp_server_listen(); + } + } +} + +YeelightController::~YeelightController() +{ +} + +std::string YeelightController::GetLocation() +{ + return(location); +} + +std::string YeelightController::GetName() +{ + return("Yeelight"); +} + +std::string YeelightController::GetVersion() +{ + return(""); +} + +std::string YeelightController::GetManufacturer() +{ + return("Yeelight"); +} + +std::string YeelightController::GetUniqueID() +{ + return(""); +} + +bool YeelightController::GetMusicMode() +{ + return(music_mode); +} + +void YeelightController::SetMusicMode() +{ + json command; + + char hostname[256]; + char* ip_addr; + struct hostent* host_entry; + + /*-----------------------------------------------------------------*\ + | The Yeelight bulb requires this PC's local IP address for music | + | mode. Get the first IP address of this computer's hostname, or | + | use the one defined | + \*-----------------------------------------------------------------*/ + if(host_ip.empty()) + { + gethostname(hostname, 256); + host_entry = gethostbyname(hostname); + ip_addr = inet_ntoa(*((struct in_addr*) host_entry->h_addr_list[0])); + } + else + { + ip_addr = &host_ip[0]; + } + + /*-----------------------------------------------------------------*\ + | Fill in the set_rgb command with RGB information. | + | The bulb will not respond to 0, 0, 0, so if all channels are zero,| + | set the state to off. Otherwise, set it to on. | + \*-----------------------------------------------------------------*/ + command["id"] = 1; + command["method"] = "set_music"; + command["params"][0] = 1; + command["params"][1] = ip_addr; + command["params"][2] = music_mode_port; + + /*-----------------------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------------------*/ + std::string command_str = command.dump().append("\r\n"); + + port.tcp_client_connect(); + port.tcp_client_write((char *)command_str.c_str(), (int)command_str.length() + 1); + port.tcp_close(); +} + +void YeelightController::SetPower() +{ + json command; + + /*-----------------------------------------------------------------*\ + | Fill in the set_rgb command with RGB information. | + | The bulb will not respond to 0, 0, 0, so if all channels are zero,| + | set the state to off. Otherwise, set it to on. | + \*-----------------------------------------------------------------*/ + command["id"] = 1; + command["method"] = "set_power"; + command["params"][0] = "on"; + command["params"][1] = "sudden"; + command["params"][2] = 0; + command["params"][3] = 2; + + /*-----------------------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------------------*/ + std::string command_str = command.dump().append("\r\n"); + + port.tcp_client_connect(); + port.tcp_client_write((char *)command_str.c_str(), (int)command_str.length() + 1); + port.tcp_close(); +} + +void YeelightController::SetColor(unsigned char red, unsigned char green, unsigned char blue) +{ + json command; + + /*-----------------------------------------------------------------*\ + | Yeelight doesn't seem to support proper RGB, it just uses RGB to | + | calculate hue and saturation. It doesn't affect brightness. To | + | work around this, determine the highest value and scale to 100 to | + | use as brightness | + \*-----------------------------------------------------------------*/ + float bright = red; + + if(green > bright) + { + bright = green; + } + + if(blue > bright) + { + bright = blue; + } + + bright = (100.0f * (bright / 255.0f)); + + /*-----------------------------------------------------------------*\ + | Calculate the RGB field as 0x00RRGGBB | + \*-----------------------------------------------------------------*/ + unsigned int rgb = (red << 16) | (green << 8) | (blue << 0); + + /*-----------------------------------------------------------------*\ + | Because of Yeelight's weird quirks with true RGB, we have to use | + | the Color Flow option but configure only one frame. Because the | + | set_cf option provides both RGB and brightness in one command, it | + | allows better RGB control than the set_rgb function. | + \*-----------------------------------------------------------------*/ + std::string cf = "50,1," + std::to_string(rgb) +"," + std::to_string((int)bright); + + /*-----------------------------------------------------------------*\ + | Fill in the set_cf command with the color flow string. | + \*-----------------------------------------------------------------*/ + command["id"] = 1; + command["method"] = "start_cf"; + command["params"][0] = 1; + command["params"][1] = 1; + command["params"][2] = cf; + + /*-----------------------------------------------------------------*\ + | Convert the JSON object to a string and write it | + \*-----------------------------------------------------------------*/ + std::string command_str = command.dump().append("\r\n"); + + if(music_mode) + { + send(*music_mode_sock, (char *)command_str.c_str(), (int)command_str.length(), 0); + } + else + { + port.tcp_client_connect(); + port.tcp_client_write((char *)command_str.c_str(), (int)command_str.length() + 1); + port.tcp_close(); + } +} diff --git a/Controllers/YeelightController/YeelightController.h b/Controllers/YeelightController/YeelightController.h new file mode 100644 index 0000000..3659957 --- /dev/null +++ b/Controllers/YeelightController/YeelightController.h @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| YeelightController.h | +| | +| Driver for Yeelight | +| | +| Adam Honse (CalcProgrammer1) 18 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "net_port.h" + +class YeelightController +{ +public: + YeelightController(std::string ip, std::string host_ip, bool music_mode_val); + ~YeelightController(); + + std::string GetLocation(); + std::string GetName(); + std::string GetVersion(); + std::string GetManufacturer(); + std::string GetUniqueID(); + + bool GetMusicMode(); + + void SetMusicMode(); + void SetPower(); + void SetColor(unsigned char red, unsigned char green, unsigned char blue); + +private: + std::string location; + std::string host_ip; + net_port port; + bool music_mode; + unsigned int music_mode_port; + net_port music_mode_server; + SOCKET * music_mode_sock; +}; diff --git a/Controllers/YeelightController/YeelightControllerDetect.cpp b/Controllers/YeelightController/YeelightControllerDetect.cpp new file mode 100644 index 0000000..aa2dfaa --- /dev/null +++ b/Controllers/YeelightController/YeelightControllerDetect.cpp @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| YeelightControllerDetect.cpp | +| | +| Detector for Yeelight | +| | +| Adam Honse (CalcProgrammer1) 18 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "YeelightController.h" +#include "RGBController_Yeelight.h" +#include "SettingsManager.h" + +/******************************************************************************************\ +* * +* DetectYeelightControllers * +* * +* Detect Yeelight devices * +* * +\******************************************************************************************/ + +void DetectYeelightControllers() +{ + json yeelight_settings; + + /*-------------------------------------------------*\ + | Get Yeelight settings from settings manager | + \*-------------------------------------------------*/ + yeelight_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("YeelightDevices"); + + /*-------------------------------------------------*\ + | If the Yeelight settings contains devices, process| + \*-------------------------------------------------*/ + if(yeelight_settings.contains("devices")) + { + for(unsigned int device_idx = 0; device_idx < yeelight_settings["devices"].size(); device_idx++) + { + std::string yeelight_host_ip; + + if(yeelight_settings["devices"][device_idx].contains("host_ip")) + { + yeelight_host_ip = yeelight_settings["devices"][device_idx]["host_ip"]; + } + + if(yeelight_settings["devices"][device_idx].contains("ip")) + { + std::string yeelight_ip = yeelight_settings["devices"][device_idx]["ip"]; + bool music_mode = false; + + if(yeelight_settings["devices"][device_idx].contains("music_mode")) + { + music_mode = yeelight_settings["devices"][device_idx]["music_mode"]; + } + + YeelightController* controller = new YeelightController(yeelight_ip, yeelight_host_ip, music_mode); + RGBController_Yeelight* rgb_controller = new RGBController_Yeelight(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } + +} /* DetectYeelightControllers() */ + +REGISTER_DETECTOR("Yeelight", DetectYeelightControllers); diff --git a/Controllers/ZETKeyboardController/RGBController_ZETBladeOptical.cpp b/Controllers/ZETKeyboardController/RGBController_ZETBladeOptical.cpp new file mode 100644 index 0000000..9ba568b --- /dev/null +++ b/Controllers/ZETKeyboardController/RGBController_ZETBladeOptical.cpp @@ -0,0 +1,544 @@ +/*---------------------------------------------------------*\ +| RGBController_ZETBladeOptical.cpp | +| | +| RGBController for ZET Blade | +| | +| Based on HyperX Alloy Elite2 implementation by | +| KundaPanda | +| | +| Moon_darker (Vaker) 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" +#include "RGBController_ZETBladeOptical.h" + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +static unsigned int matrix_map[ZET_BLADE_OPTICAL_ROWS][ZET_BLADE_OPTICAL_COLUMNS] = +{ + { 0, NA, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, NA, 13, 14, 15, NA, NA, NA, NA }, // Skipped: 1, 17, 18, 19, 20 + { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, NA, 30, 31, 32, 33, 34, 35, 36 }, + { 37, NA, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 }, + { 58, NA, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, NA, NA, NA, NA, 71, 72, 73, NA }, // Skipped: 75, 77, 78, 79, 83 + { 74, NA, NA, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, NA, NA, 86, NA, 87, 88, 89, 90 }, // Skipped: 85, 96, 98, 100 + { 91, 92, 93, NA, NA, NA, NA, 94, NA, NA, NA, 95, 96, 97, 98, 99, 100, 101, 102, NA, 103, NA } // Skipped: 108, 109, 111, 112, 113, 116, 123, 125 +}; + +static const char* zone_names[] = +{ + ZONE_EN_KEYBOARD, +}; + +static zone_type zone_types[] = +{ + ZONE_TYPE_MATRIX, +}; + +static const unsigned int zone_sizes[] = +{ + 104, +}; + +static const char *led_names[] = +{ + KEY_EN_ESCAPE, + // Skip index 1 + KEY_EN_F1, + KEY_EN_F2, + KEY_EN_F3, + KEY_EN_F4, + KEY_EN_F5, + KEY_EN_F6, + KEY_EN_F7, + KEY_EN_F8, + KEY_EN_F9, + KEY_EN_F10, + KEY_EN_F11, + KEY_EN_F12, + KEY_EN_PRINT_SCREEN, + KEY_EN_SCROLL_LOCK, + KEY_EN_PAUSE_BREAK, + // Skip index 17 + // Skip index 18 + // Skip index 19 + // Skip index 20 + KEY_EN_BACK_TICK, + KEY_EN_1, + KEY_EN_2, + KEY_EN_3, + KEY_EN_4, + KEY_EN_5, + KEY_EN_6, + KEY_EN_7, + KEY_EN_8, + KEY_EN_9, + KEY_EN_0, + KEY_EN_MINUS, + KEY_EN_EQUALS, + KEY_EN_BACKSPACE, + KEY_EN_INSERT, + KEY_EN_HOME, + KEY_EN_PAGE_UP, + KEY_EN_NUMPAD_LOCK, + KEY_EN_NUMPAD_DIVIDE, + KEY_EN_NUMPAD_TIMES, + KEY_EN_NUMPAD_MINUS, + KEY_EN_TAB, + KEY_EN_Q, + KEY_EN_W, + KEY_EN_E, + KEY_EN_R, + KEY_EN_T, + KEY_EN_Y, + KEY_EN_U, + KEY_EN_I, + KEY_EN_O, + KEY_EN_P, + KEY_EN_LEFT_BRACKET, + KEY_EN_RIGHT_BRACKET, + KEY_EN_ANSI_BACK_SLASH, + KEY_EN_DELETE, + KEY_EN_END, + KEY_EN_PAGE_DOWN, + KEY_EN_NUMPAD_7, + KEY_EN_NUMPAD_8, + KEY_EN_NUMPAD_9, + KEY_EN_NUMPAD_PLUS, + KEY_EN_CAPS_LOCK, + KEY_EN_A, + KEY_EN_S, + KEY_EN_D, + KEY_EN_F, + KEY_EN_G, + KEY_EN_H, + KEY_EN_J, + KEY_EN_K, + KEY_EN_L, + KEY_EN_SEMICOLON, + KEY_EN_QUOTE, + // Skip index 75 + KEY_EN_ANSI_ENTER, + // Skip index 77 + // Skip index 78 + // Skip index 79 + KEY_EN_NUMPAD_4, + KEY_EN_NUMPAD_5, + KEY_EN_NUMPAD_6, + // Skip index 83 + KEY_EN_LEFT_SHIFT, + // Skip index 85 + KEY_EN_Z, + KEY_EN_X, + KEY_EN_C, + KEY_EN_V, + KEY_EN_B, + KEY_EN_N, + KEY_EN_M, + KEY_EN_COMMA, + KEY_EN_PERIOD, + KEY_EN_FORWARD_SLASH, + // Skip index 96 + KEY_EN_RIGHT_SHIFT, + // Skip index 98 + KEY_EN_UP_ARROW, + // Skip index 100 + KEY_EN_NUMPAD_1, + KEY_EN_NUMPAD_2, + KEY_EN_NUMPAD_3, + KEY_EN_NUMPAD_ENTER, + KEY_EN_LEFT_CONTROL, + KEY_EN_LEFT_WINDOWS, + KEY_EN_LEFT_ALT, + // Skip index 108 + // Skip index 109 + KEY_EN_SPACE, + // Skip index 111 + // Skip index 112 + // Skip index 113 + KEY_EN_RIGHT_ALT, + KEY_EN_RIGHT_FUNCTION, + // Skip index 116 + KEY_EN_MENU, + KEY_EN_RIGHT_CONTROL, + KEY_EN_LEFT_ARROW, + KEY_EN_DOWN_ARROW, + KEY_EN_RIGHT_ARROW, + KEY_EN_NUMPAD_0, + // Skip index 123 + KEY_EN_NUMPAD_PERIOD, +}; + +/**------------------------------------------------------------------*\ + @name Zet Blade Optical + @category Keyboard + @type USB + @save :x: + @direct :x: + @effects :white_check_mark: + @detectors DetectZETBladeOptical + @comment +\*-------------------------------------------------------------------*/ + +RGBController_ZETBladeOptical::RGBController_ZETBladeOptical(ZETBladeOpticalController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "ZET"; + type = DEVICE_TYPE_KEYBOARD; + description = "ZET Blade Optical Keyboard Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Custom; + Custom.name = "Custom"; + Custom.value = ZET_BLADE_OPTICAL_MODE_CUSTOM; + Custom.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Custom.color_mode = MODE_COLORS_PER_LED; + Custom.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Custom.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Custom.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + modes.push_back(Custom); + + mode Static; + Static.name = "Static"; + Static.value = ZET_BLADE_OPTICAL_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.resize(Static.colors_max); + Static.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Static.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Static.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + Static.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(Static); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ZET_BLADE_OPTICAL_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Breathing.color_mode = MODE_COLORS_RANDOM; + Breathing.colors_min = 1; + Breathing.colors_max = 1; + Breathing.colors.resize(Breathing.colors_max); + Breathing.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Breathing.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Breathing.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + Breathing.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + Breathing.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + Breathing.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(Breathing); + + mode OnPress; + OnPress.name = "Reactive"; + OnPress.value = ZET_BLADE_OPTICAL_MODE_ON_PRESS; + OnPress.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + OnPress.color_mode = MODE_COLORS_RANDOM; + OnPress.colors_min = 1; + OnPress.colors_max = 1; + OnPress.colors.resize(OnPress.colors_max); + OnPress.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + OnPress.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + OnPress.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + OnPress.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + OnPress.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + OnPress.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(OnPress); + + mode Raindrop; + Raindrop.name = "Reactive Raindrop"; + Raindrop.value = ZET_BLADE_OPTICAL_MODE_RAINDROP; + Raindrop.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Raindrop.color_mode = MODE_COLORS_RANDOM; + Raindrop.colors_min = 1; + Raindrop.colors_max = 1; + Raindrop.colors.resize(Raindrop.colors_max); + Raindrop.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Raindrop.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Raindrop.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + Raindrop.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + Raindrop.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + Raindrop.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(Raindrop); + + mode Ripple; + Ripple.name = "Reactive Ripple"; + Ripple.value = ZET_BLADE_OPTICAL_MODE_RIPPLE; + Ripple.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Ripple.color_mode = MODE_COLORS_RANDOM; + Ripple.colors_min = 1; + Ripple.colors_max = 1; + Ripple.colors.resize(Ripple.colors_max); + Ripple.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Ripple.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Ripple.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + Ripple.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + Ripple.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + Ripple.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(Ripple); + + mode Laser; + Laser.name = "Reactive Laser"; + Laser.value = ZET_BLADE_OPTICAL_MODE_LASER; + Laser.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Laser.color_mode = MODE_COLORS_RANDOM; + Laser.colors_min = 1; + Laser.colors_max = 1; + Laser.colors.resize(Laser.colors_max); + Laser.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Laser.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Laser.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + Laser.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + Laser.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + Laser.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(Laser); + + mode Waves; + Waves.name = "Waves"; + Waves.value = ZET_BLADE_OPTICAL_MODE_WAVES; + Waves.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Waves.color_mode = MODE_COLORS_RANDOM; + Waves.colors_min = 1; + Waves.colors_max = 1; + Waves.colors.resize(Waves.colors_max); + Waves.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Waves.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Waves.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + Waves.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + Waves.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + Waves.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(Waves); + + mode Rain; + Rain.name = "Rain"; + Rain.value = ZET_BLADE_OPTICAL_MODE_RAIN; + Rain.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Rain.color_mode = MODE_COLORS_NONE; + Rain.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Rain.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Rain.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + Rain.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + Rain.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + Rain.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(Rain); + + mode Spectrum; + Spectrum.name = "Spectrum Cycle"; + Spectrum.value = ZET_BLADE_OPTICAL_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Spectrum.color_mode = MODE_COLORS_NONE; + Spectrum.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Spectrum.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Spectrum.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + Spectrum.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + Spectrum.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + Spectrum.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(Spectrum); + + mode SurfingRight; // Now, you might be thinking... it'd be better to use .direction here, right? WRONG! There is no Surfing Left /._. + SurfingRight.name = "Rainbow Wave"; + SurfingRight.value = ZET_BLADE_OPTICAL_MODE_SURFING_RIGHT; + SurfingRight.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SurfingRight.color_mode = MODE_COLORS_NONE; + SurfingRight.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + SurfingRight.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + SurfingRight.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + SurfingRight.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + SurfingRight.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + SurfingRight.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(SurfingRight); + + mode SurfingCenter; + SurfingCenter.name = "Rainbow Wave Center"; + SurfingCenter.value = ZET_BLADE_OPTICAL_MODE_SURFING_CENTER; + SurfingCenter.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SurfingCenter.color_mode = MODE_COLORS_NONE; + SurfingCenter.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + SurfingCenter.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + SurfingCenter.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + SurfingCenter.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + SurfingCenter.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + SurfingCenter.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(SurfingCenter); + + mode SurfingCross; + SurfingCross.name = "Rainbow Wave Cross"; + SurfingCross.value = ZET_BLADE_OPTICAL_MODE_SURFING_CROSS; + SurfingCross.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + SurfingCross.color_mode = MODE_COLORS_NONE; + SurfingCross.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + SurfingCross.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + SurfingCross.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + SurfingCross.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + SurfingCross.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + SurfingCross.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(SurfingCross); + + mode RotateMarquee; + RotateMarquee.name = "Vortex"; + RotateMarquee.value = ZET_BLADE_OPTICAL_MODE_ROTATE_MARQUEE; + RotateMarquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + RotateMarquee.color_mode = MODE_COLORS_NONE; + RotateMarquee.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + RotateMarquee.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + RotateMarquee.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + RotateMarquee.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + RotateMarquee.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + RotateMarquee.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(RotateMarquee); + + mode Traffic; + Traffic.name = "Traffic"; + Traffic.value = ZET_BLADE_OPTICAL_MODE_TRAFFIC; + Traffic.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Traffic.color_mode = MODE_COLORS_NONE; + Traffic.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Traffic.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Traffic.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + Traffic.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + Traffic.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + Traffic.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(Traffic); + + mode Gradient; + Gradient.name = "Gradient"; + Gradient.value = ZET_BLADE_OPTICAL_MODE_GRADIENT; + Gradient.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_AUTOMATIC_SAVE; + Gradient.color_mode = MODE_COLORS_NONE; + Gradient.brightness_min = ZET_BLADE_OPTICAL_BRIGHTNESS_MIN; + Gradient.brightness_max = ZET_BLADE_OPTICAL_BRIGHTNESS_MAX; + Gradient.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + Gradient.speed_min = ZET_BLADE_OPTICAL_SPEED_MIN; + Gradient.speed_max = ZET_BLADE_OPTICAL_SPEED_MAX; + Gradient.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + modes.push_back(Gradient); + + mode Off; + Off.name = "Off"; + Off.value = ZET_BLADE_OPTICAL_MODE_OFF; + Off.flags = MODE_FLAG_AUTOMATIC_SAVE; + Off.color_mode = MODE_COLORS_NONE; + Off.speed = ZET_BLADE_OPTICAL_SPEED_DEF; + Off.brightness = ZET_BLADE_OPTICAL_BRIGHTNESS_DEF; + modes.push_back(Off); + + SetupZones(); +} + +RGBController_ZETBladeOptical::~RGBController_ZETBladeOptical() +{ + /*---------------------------------------------------------*\ + | Delete the matrix map | + \*---------------------------------------------------------*/ + for (unsigned int zone_index = 0; zone_index < zones.size(); zone_index++) + { + if (zones[zone_index].matrix_map != nullptr) + { + delete zones[zone_index].matrix_map; + } + } + + delete controller; +} + +void RGBController_ZETBladeOptical::SetupZones() +{ + /*---------------------------------------------------------*\ + | Set up zones | + \*---------------------------------------------------------*/ + unsigned int total_led_count = 0; + for (unsigned int zone_idx = 0; zone_idx < 1; zone_idx++) + { + zone new_zone; + new_zone.name = zone_names[zone_idx]; + new_zone.type = zone_types[zone_idx]; + new_zone.leds_min = zone_sizes[zone_idx]; + new_zone.leds_max = zone_sizes[zone_idx]; + new_zone.leds_count = zone_sizes[zone_idx]; + + if (zone_types[zone_idx] == ZONE_TYPE_MATRIX) + { + new_zone.matrix_map = new matrix_map_type; + new_zone.matrix_map->height = ZET_BLADE_OPTICAL_ROWS; + new_zone.matrix_map->width = ZET_BLADE_OPTICAL_COLUMNS; + new_zone.matrix_map->map = (unsigned int *)&matrix_map; + } + else + { + new_zone.matrix_map = nullptr; + } + + zones.push_back(new_zone); + + total_led_count += zone_sizes[zone_idx]; + } + + for (unsigned int led_idx = 0; led_idx < total_led_count; led_idx++) + { + led new_led; + new_led.name = led_names[led_idx]; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_ZETBladeOptical::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ZETBladeOptical::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + + if(active_mode == 0) + { + controller->SetLEDDirect(colors, modes[active_mode].brightness); + } +} + +void RGBController_ZETBladeOptical::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ZETBladeOptical::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_ZETBladeOptical::DeviceUpdateMode() +{ + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM || modes[active_mode].color_mode == MODE_COLORS_NONE); + unsigned char mode_colors[3]; + + mode_colors[0] = 0; + mode_colors[1] = 0; + mode_colors[2] = 0; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + mode_colors[0] = RGBGetRValue(modes[active_mode].colors[0]); + mode_colors[1] = RGBGetGValue(modes[active_mode].colors[0]); + mode_colors[2] = RGBGetBValue(modes[active_mode].colors[0]); + } + + controller->SetEffect(modes[active_mode].value, + modes[active_mode].speed, + modes[active_mode].brightness, + random, + mode_colors[0], + mode_colors[1], + mode_colors[2]); + + std::this_thread::sleep_for(std::chrono::milliseconds(15)); +} + diff --git a/Controllers/ZETKeyboardController/RGBController_ZETBladeOptical.h b/Controllers/ZETKeyboardController/RGBController_ZETBladeOptical.h new file mode 100644 index 0000000..6f5af95 --- /dev/null +++ b/Controllers/ZETKeyboardController/RGBController_ZETBladeOptical.h @@ -0,0 +1,44 @@ +/*---------------------------------------------------------*\ +| RGBController_ZETBladeOptical.h | +| | +| RGBController for ZET Blade | +| | +| Based on HyperX Alloy Elite2 implementation by | +| KundaPanda | +| | +| Moon_darker (Vaker) 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "ZETBladeOpticalController.h" + +#define ZET_BLADE_OPTICAL_ROWS 6 +#define ZET_BLADE_OPTICAL_COLUMNS 22 + +class RGBController_ZETBladeOptical : public RGBController +{ +public: + RGBController_ZETBladeOptical(ZETBladeOpticalController* controller_ptr); + ~RGBController_ZETBladeOptical(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ZETBladeOpticalController* controller; + std::chrono::time_point last_update_time; +}; diff --git a/Controllers/ZETKeyboardController/ZETBladeOpticalController.cpp b/Controllers/ZETKeyboardController/ZETBladeOpticalController.cpp new file mode 100644 index 0000000..880a286 --- /dev/null +++ b/Controllers/ZETKeyboardController/ZETBladeOpticalController.cpp @@ -0,0 +1,267 @@ +/*---------------------------------------------------------*\ +| ZETBladeOpticalController.cpp | +| | +| Driver for ZET Blade | +| | +| Based on HyperX Alloy Elite2 implementation by | +| KundaPanda | +| | +| Moon_darker (Vaker) 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "StringUtils.h" +#include "ZETBladeOpticalController.h" + +using namespace std::chrono_literals; + +//0xFFFFFFFF indicates an unused entry in matrix +#define NA 0xFFFFFFFF + +/*-----------------------------------------*\ +| Skip these indices in the color output | +\*-----------------------------------------*/ +static const unsigned int SKIP_INDICES[] = { 1, 17, 18, 19, 20, 75, 77, 78, 79, 83, 85, 96, 98, 100, 108, 109, 111, 112, 113, 116, 123, 125 }; + + +ZETBladeOpticalController::ZETBladeOpticalController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + effect_mode = ZET_BLADE_OPTICAL_MODE_STATIC; +} + +ZETBladeOpticalController::~ZETBladeOpticalController() +{ + hid_close(dev); +} + +std::string ZETBladeOpticalController::GetDeviceLocation() +{ + return("HID " + location); +} + +std::string ZETBladeOpticalController::GetNameString() +{ + return(name); +} + +std::string ZETBladeOpticalController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void ZETBladeOpticalController::PrepareHeader(unsigned char* packet, unsigned char brightness) +{ + PrepareHeader(packet, 0x1C, 0x02, brightness, 0xFF); // Custom 2, 2, -, separator +} + +void ZETBladeOpticalController::PrepareHeader(unsigned char* packet, unsigned char mode, unsigned char speed, unsigned char brightness, unsigned char color) +{ + /*-----------------------------------------------------*\ + | Prepare packet header | + \*-----------------------------------------------------*/ + packet[0x00] = 0x04; // Report ID + packet[0x01] = 0xAE; // RGB Control Packet (KB = 0xA0) + packet[0x02] = 0x01; // unk + packet[0x05] = mode; // Mode + packet[0x06] = speed; // Speed, 0-4 + packet[0x07] = brightness; // Brightness, 0-4 + packet[0x08] = color; // Separator FF or Color, 0-7 (0-6 in static color mode) (Rainbow,) R, G, B, Y, M, C, W +} + +void ZETBladeOpticalController::SetLEDDirect(const std::vector& colors, unsigned char brightness) +{ + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer and prepare packet header | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + PrepareHeader(buf, brightness); + + /*-----------------------------------------------------*\ + | Variables to keep track of color sending and skipping | + \*-----------------------------------------------------*/ + size_t buf_idx = ZET_BLADE_OPTICAL_HEADER_LEN; + size_t color_idx = 0; + size_t packets_sent = 0; + size_t skipped = 0; + const unsigned int* skip_idx = &SKIP_INDICES[0]; + bool last_color = false; + bool ending_flag = false; + + /*-----------------------------------------------------*\ + | Continue filling and sending packets while color data | + | remains | + \*-----------------------------------------------------*/ + while(color_idx < colors.size()) + { + /*-------------------------------------------------*\ + | If at a skipped index, increment skipped count | + | and index | + \*-------------------------------------------------*/ + if(*skip_idx == color_idx + skipped) + { + skip_idx++; + + if(skip_idx >= SKIP_INDICES + sizeof(SKIP_INDICES) / sizeof(unsigned int)) + { + skip_idx = SKIP_INDICES; + } + + skipped++; + continue; + } + + /*-------------------------------------------------*\ + | Packets have colors in groups of 4 bytes, with | + | the first byte being key id and then R, G, B. | + \*-------------------------------------------------*/ + buf[buf_idx] = (unsigned char)(color_idx + skipped + ZET_BLADE_OPTICAL_KEY_OFFSET); + buf[buf_idx + 1] = RGBGetRValue(colors[color_idx]); + buf[buf_idx + 2] = RGBGetGValue(colors[color_idx]); + buf[buf_idx + 3] = RGBGetBValue(colors[color_idx]); + + /*-------------------------------------------------*\ + | Increment packet buffer index by 4 bytes | + \*-------------------------------------------------*/ + buf_idx += ZET_BLADE_OPTICAL_COLOR_LEN; + color_idx++; + last_color = (color_idx == colors.size()); + + /*-------------------------------------------------*\ + | If the packet buffer is full, send it and reset | + | buffer indexing | + | OR | + | If all colors have been filled into the buffer, | + | send the packet | + \*-------------------------------------------------*/ + if((buf_idx + ZET_BLADE_OPTICAL_HEADER_LEN >= sizeof(buf)) || last_color) + { + /*---------------------------------------------*\ + | If we still have place for an | + | ending sequence - squeeze it in! | + \*---------------------------------------------*/ + if(last_color && (buf_idx + ZET_BLADE_OPTICAL_COLOR_LEN < sizeof(buf))) + { + buf[buf_idx] = 0xFF; + ending_flag = true; + } + + /*---------------------------------------------*\ + | Send packet | + \*---------------------------------------------*/ + hid_write(dev, buf, sizeof(buf)); + + /*---------------------------------------------*\ + | Wait for the poor slowpoke to process packet | + \*---------------------------------------------*/ + std::this_thread::sleep_for(ZET_BLADE_OPTICAL_DELAY); + + /*---------------------------------------------*\ + | Zero out buffer, reset index, prepare header | + \*---------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + buf_idx = ZET_BLADE_OPTICAL_HEADER_LEN; + PrepareHeader(buf, brightness); + + /*---------------------------------------------*\ + | Increment packet counter | + \*---------------------------------------------*/ + packets_sent++; + } + } + + /*---------------------------------------------*\ + | If there's anything left to send - send it | + \*---------------------------------------------*/ + if(!ending_flag) + { + buf[buf_idx] = 0xFF; + hid_write(dev, buf, sizeof(buf)); + std::this_thread::sleep_for(ZET_BLADE_OPTICAL_DELAY); + } +} + +unsigned char ZETBladeOpticalController::RGBToPalette(unsigned char red, + unsigned char grn, + unsigned char blu + ) +{ + /*------------------------*\ + | 0 0 1 (1) -> (1) Red | + | 0 1 0 (2) -> (2) Green | + | 0 1 1 (3) -> (4) Yellow | + | 1 0 0 (4) -> (3) Blue | + | 1 0 1 (5) -> (5) Magenta | + | 1 1 0 (6) -> (6) Cyan | + | 1 1 1 (7) -> (7) White | + \*------------------------*/ + unsigned char color_mask = ((blu > 127) << 2 & 4) | ((grn > 127) << 1 & 2) | ((red > 127) & 1); + + switch(color_mask) // (Rainbow/Off,) R, G, B, Y, M, C, W + { + case 3: + return 4; + case 4: + return 3; + default: + return color_mask; + } +} + +void ZETBladeOpticalController::SetEffect(unsigned char mode, + unsigned char speed, + unsigned char brightness, + bool random, + unsigned char red, + unsigned char grn, + unsigned char blu + ) +{ + /*-------------------------------------------------------------*\ + | Prep some status variables and return if we're in custom mode | + \*-------------------------------------------------------------*/ + bool static_mode = (mode == ZET_BLADE_OPTICAL_MODE_STATIC); + effect_mode = mode; + custom_mode = (effect_mode == ZET_BLADE_OPTICAL_MODE_CUSTOM); + + if(custom_mode) + { + return; + } + + unsigned char color = RGBToPalette(red, grn, blu); + unsigned char buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer and prepare packet | + \*-----------------------------------------------------*/ + memset(buf, 0x00, sizeof(buf)); + + brightness = (static_mode && color == 0) ? 0 : brightness; + color = (static_mode && color > 0) ? (color - 1) : color; + color = random ? 0 : color; + PrepareHeader(buf, mode, speed, brightness, color); + + /*---------------------------------------------*\ + | Send packet... and wait | + \*---------------------------------------------*/ + hid_write(dev, buf, sizeof(buf)); + std::this_thread::sleep_for(ZET_BLADE_OPTICAL_DELAY); +} + diff --git a/Controllers/ZETKeyboardController/ZETBladeOpticalController.h b/Controllers/ZETKeyboardController/ZETBladeOpticalController.h new file mode 100644 index 0000000..8dba935 --- /dev/null +++ b/Controllers/ZETKeyboardController/ZETBladeOpticalController.h @@ -0,0 +1,74 @@ +/*---------------------------------------------------------*\ +| ZETBladeOpticalController.h | +| | +| Driver for ZET Blade | +| | +| Based on HyperX Alloy Elite2 implementation by | +| KundaPanda | +| | +| Moon_darker (Vaker) 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "RGBController.h" + +#define ZET_BLADE_OPTICAL_DELAY 12ms +#define ZET_BLADE_OPTICAL_HEADER_LEN 9 +#define ZET_BLADE_OPTICAL_COLOR_LEN 4 +#define ZET_BLADE_OPTICAL_KEY_OFFSET 0x80 + +#define ZET_BLADE_OPTICAL_SPEED_MIN 0 +#define ZET_BLADE_OPTICAL_SPEED_MAX 4 +#define ZET_BLADE_OPTICAL_SPEED_DEF 2 +#define ZET_BLADE_OPTICAL_BRIGHTNESS_MIN 0 +#define ZET_BLADE_OPTICAL_BRIGHTNESS_MAX 4 +#define ZET_BLADE_OPTICAL_BRIGHTNESS_DEF 4 + +#define ZET_BLADE_OPTICAL_MODE_OFF 0x01 +#define ZET_BLADE_OPTICAL_MODE_CUSTOM 0x1C +#define ZET_BLADE_OPTICAL_MODE_STATIC 0x02 +#define ZET_BLADE_OPTICAL_MODE_BREATHING 0x03 +#define ZET_BLADE_OPTICAL_MODE_ON_PRESS 0x04 +#define ZET_BLADE_OPTICAL_MODE_RAINDROP 0x05 +#define ZET_BLADE_OPTICAL_MODE_RIPPLE 0x06 +#define ZET_BLADE_OPTICAL_MODE_LASER 0x07 +#define ZET_BLADE_OPTICAL_MODE_WAVES 0x08 +#define ZET_BLADE_OPTICAL_MODE_RAIN 0x09 +#define ZET_BLADE_OPTICAL_MODE_SPECTRUM 0x0A +#define ZET_BLADE_OPTICAL_MODE_SURFING_RIGHT 0x0B +#define ZET_BLADE_OPTICAL_MODE_SURFING_CENTER 0x0D +#define ZET_BLADE_OPTICAL_MODE_SURFING_CROSS 0x0F +#define ZET_BLADE_OPTICAL_MODE_ROTATE_MARQUEE 0x0C +#define ZET_BLADE_OPTICAL_MODE_TRAFFIC 0x0E +#define ZET_BLADE_OPTICAL_MODE_GRADIENT 0x15 + +class ZETBladeOpticalController +{ +public: + ZETBladeOpticalController(hid_device* dev_handle, const char* path, std::string dev_name); + ~ZETBladeOpticalController(); + + std::string GetDeviceLocation(); + std::string GetNameString(); + std::string GetSerialString(); + + void SetLEDDirect(const std::vector& colors, unsigned char brightness); + + void SetEffect(unsigned char mode, unsigned char speed, unsigned char brightness, bool random, unsigned char red1, unsigned char grn1, unsigned char blu1); +private: + hid_device* dev; + std::string location; + std::string name; + unsigned int effect_mode; + bool custom_mode; + + void PrepareHeader(unsigned char *packet, unsigned char brightness); + void PrepareHeader(unsigned char *packet, unsigned char mode, unsigned char speed, unsigned char brightness, unsigned char color); + unsigned char RGBToPalette(unsigned char red, unsigned char grn, unsigned char blu); +}; diff --git a/Controllers/ZETKeyboardController/ZETKeyboardControllerDetect.cpp b/Controllers/ZETKeyboardController/ZETKeyboardControllerDetect.cpp new file mode 100644 index 0000000..7417bca --- /dev/null +++ b/Controllers/ZETKeyboardController/ZETKeyboardControllerDetect.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| ZETKeyboardControllerDetect.cpp | +| | +| Detector for ZET Blade | +| | +| Based on HyperX Alloy Elite2 implementation by | +| KundaPanda | +| | +| Moon_darker (Vaker) 23 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "ZETBladeOpticalController.h" +#include "RGBController_ZETBladeOptical.h" + +/*-----------------------------------------------------*\ +| ZET keyboard VID/PID pairs | +\*-----------------------------------------------------*/ +#define ZET_BLADE_OPTICAL_VID 0x2EA8 +#define ZET_BLADE_OPTICAL_PID 0x2125 + +void DetectZETBladeOptical(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if (dev) + { + ZETBladeOpticalController* controller = new ZETBladeOpticalController(dev, info->path, name); + RGBController_ZETBladeOptical* rgb_controller = new RGBController_ZETBladeOptical(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_HID_DETECTOR_IP("ZET Blade Optical", DetectZETBladeOptical, ZET_BLADE_OPTICAL_VID, ZET_BLADE_OPTICAL_PID, 1, 0xFF00); diff --git a/Controllers/ZalmanZSyncController/RGBController_ZalmanZSync.cpp b/Controllers/ZalmanZSyncController/RGBController_ZalmanZSync.cpp new file mode 100644 index 0000000..d4de8e9 --- /dev/null +++ b/Controllers/ZalmanZSyncController/RGBController_ZalmanZSync.cpp @@ -0,0 +1,335 @@ +/*---------------------------------------------------------*\ +| RGBController_ZalmanZSync.cpp | +| | +| RGBController for Zalman Z Sync | +| | +| Adam Honse (CalcProgrammer1) 30 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ZalmanZSync.h" + +/**------------------------------------------------------------------*\ + @name Zalmna Z Sync + @category LEDStrip + @type USB + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectZalmanZSyncControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_ZalmanZSync::RGBController_ZalmanZSync(ZalmanZSyncController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetNameString(); + vendor = "Zalman"; + description = "Zalman Z Sync Device"; + type = DEVICE_TYPE_LEDSTRIP; + version = controller->GetFirmwareString(); + location = controller->GetLocationString(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFFFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode RainbowWave; + RainbowWave.name = "Rainbow Wave"; + RainbowWave.value = ZALMAN_Z_SYNC_MODE_RAINBOW_WAVE; + RainbowWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RainbowWave.speed_min = ZALMAN_Z_SYNC_SPEED_SLOW; + RainbowWave.speed_max = ZALMAN_Z_SYNC_SPEED_FAST; + RainbowWave.speed = ZALMAN_Z_SYNC_SPEED_MEDIUM; + RainbowWave.direction = MODE_DIRECTION_RIGHT; + RainbowWave.color_mode = MODE_COLORS_NONE; + modes.push_back(RainbowWave); + + mode ColorShift; + ColorShift.name = "Color Shift"; + ColorShift.value = ZALMAN_Z_SYNC_MODE_COLOR_SHIFT; + ColorShift.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + ColorShift.colors_min = 2; + ColorShift.colors_max = 2; + ColorShift.speed_min = ZALMAN_Z_SYNC_SPEED_SLOW; + ColorShift.speed_max = ZALMAN_Z_SYNC_SPEED_FAST; + ColorShift.speed = ZALMAN_Z_SYNC_SPEED_MEDIUM; + ColorShift.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorShift.colors.resize(2); + modes.push_back(ColorShift); + + mode ColorPulse; + ColorPulse.name = "Color Pulse"; + ColorPulse.value = ZALMAN_Z_SYNC_MODE_COLOR_PULSE; + ColorPulse.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + ColorPulse.colors_min = 2; + ColorPulse.colors_max = 2; + ColorPulse.speed_min = ZALMAN_Z_SYNC_SPEED_SLOW; + ColorPulse.speed_max = ZALMAN_Z_SYNC_SPEED_FAST; + ColorPulse.speed = ZALMAN_Z_SYNC_SPEED_MEDIUM; + ColorPulse.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorPulse.colors.resize(2); + modes.push_back(ColorPulse); + + mode ColorWave; + ColorWave.name = "Color Wave"; + ColorWave.value = ZALMAN_Z_SYNC_MODE_COLOR_WAVE; + ColorWave.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + ColorWave.colors_min = 2; + ColorWave.colors_max = 2; + ColorWave.speed_min = ZALMAN_Z_SYNC_SPEED_SLOW; + ColorWave.speed_max = ZALMAN_Z_SYNC_SPEED_FAST; + ColorWave.speed = ZALMAN_Z_SYNC_SPEED_MEDIUM; + ColorWave.direction = MODE_DIRECTION_RIGHT; + ColorWave.color_mode = MODE_COLORS_MODE_SPECIFIC; + ColorWave.colors.resize(2); + modes.push_back(ColorWave); + + mode Static; + Static.name = "Static"; + Static.value = ZALMAN_Z_SYNC_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Static.colors_min = 1; + Static.colors_max = 1; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors.resize(1); + modes.push_back(Static); + + mode Temperature; + Temperature.name = "Temperature"; + Temperature.value = ZALMAN_Z_SYNC_MODE_TEMPERATURE; + Temperature.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + Temperature.colors_min = 3; + Temperature.colors_max = 3; + Temperature.color_mode = MODE_COLORS_MODE_SPECIFIC; + Temperature.colors.resize(3); + modes.push_back(Temperature); + + mode Visor; + Visor.name = "Visor"; + Visor.value = ZALMAN_Z_SYNC_MODE_VISOR; + Visor.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Visor.colors_min = 2; + Visor.colors_max = 2; + Visor.speed_min = ZALMAN_Z_SYNC_SPEED_SLOW; + Visor.speed_max = ZALMAN_Z_SYNC_SPEED_FAST; + Visor.speed = ZALMAN_Z_SYNC_SPEED_MEDIUM; + Visor.color_mode = MODE_COLORS_MODE_SPECIFIC; + Visor.colors.resize(2); + modes.push_back(Visor); + + mode Marquee; + Marquee.name = "Marquee"; + Marquee.value = ZALMAN_Z_SYNC_MODE_MARQUEE; + Marquee.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Marquee.colors_min = 1; + Marquee.colors_max = 1; + Marquee.speed_min = ZALMAN_Z_SYNC_SPEED_SLOW; + Marquee.speed_max = ZALMAN_Z_SYNC_SPEED_FAST; + Marquee.speed = ZALMAN_Z_SYNC_SPEED_MEDIUM; + Marquee.direction = MODE_DIRECTION_RIGHT; + Marquee.color_mode = MODE_COLORS_MODE_SPECIFIC; + Marquee.colors.resize(1); + modes.push_back(Marquee); + + mode Blink; + Blink.name = "Blink"; + Blink.value = ZALMAN_Z_SYNC_MODE_BLINK; + Blink.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Blink.colors_min = 2; + Blink.colors_max = 2; + Blink.speed_min = ZALMAN_Z_SYNC_SPEED_SLOW; + Blink.speed_max = ZALMAN_Z_SYNC_SPEED_FAST; + Blink.speed = ZALMAN_Z_SYNC_SPEED_MEDIUM; + Blink.color_mode = MODE_COLORS_MODE_SPECIFIC; + Blink.colors.resize(2); + modes.push_back(Blink); + + mode Sequential; + Sequential.name = "Sequential"; + Sequential.value = ZALMAN_Z_SYNC_MODE_SEQUENTIAL; + Sequential.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_RANDOM_COLOR; + Sequential.colors_min = 1; + Sequential.colors_max = 1; + Sequential.speed_min = ZALMAN_Z_SYNC_SPEED_SLOW; + Sequential.speed_max = ZALMAN_Z_SYNC_SPEED_FAST; + Sequential.speed = ZALMAN_Z_SYNC_SPEED_MEDIUM; + Sequential.direction = MODE_DIRECTION_RIGHT; + Sequential.color_mode = MODE_COLORS_MODE_SPECIFIC; + Sequential.colors.resize(1); + modes.push_back(Sequential); + + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = ZALMAN_Z_SYNC_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED; + Rainbow.speed_min = ZALMAN_Z_SYNC_SPEED_SLOW; + Rainbow.speed_max = ZALMAN_Z_SYNC_SPEED_FAST; + Rainbow.speed = ZALMAN_Z_SYNC_SPEED_MEDIUM; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + SetupZones(); +} + +RGBController_ZalmanZSync::~RGBController_ZalmanZSync() +{ + delete controller; +} + +void RGBController_ZalmanZSync::SetupZones() +{ + /*-------------------------------------------------*\ + | Only set LED count on the first run | + \*-------------------------------------------------*/ + bool first_run = false; + + if(zones.size() == 0) + { + first_run = true; + } + + /*-------------------------------------------------*\ + | Clear any existing color/LED configuration | + \*-------------------------------------------------*/ + leds.clear(); + colors.clear(); + zones.resize(ZALMAN_Z_SYNC_NUM_CHANNELS); + + /*-------------------------------------------------*\ + | Set zones and leds | + \*-------------------------------------------------*/ + for (unsigned int channel_idx = 0; channel_idx < ZALMAN_Z_SYNC_NUM_CHANNELS; channel_idx++) + { + char ch_idx_string[2]; + snprintf(ch_idx_string, 2, "%d", channel_idx + 1); + + zones[channel_idx].name = "Channel "; + zones[channel_idx].name.append(ch_idx_string); + zones[channel_idx].type = ZONE_TYPE_LINEAR; + + /*-------------------------------------------------*\ + | I did some experimenting and determined that the | + | maximum number of LEDs the Corsair Commander Pro | + | can support is 200. | + \*-------------------------------------------------*/ + zones[channel_idx].leds_min = 0; + zones[channel_idx].leds_max = 40; + + if(first_run) + { + zones[channel_idx].leds_count = 0; + } + + zones[channel_idx].matrix_map = NULL; + + for (unsigned int led_ch_idx = 0; led_ch_idx < zones[channel_idx].leds_count; led_ch_idx++) + { + char led_idx_string[4]; + snprintf(led_idx_string, 4, "%d", led_ch_idx + 1); + + led new_led; + new_led.name = "LED "; + new_led.name.append(led_idx_string); + + leds.push_back(new_led); + leds_channel.push_back(channel_idx); + } + } + + SetupColors(); +} + +void RGBController_ZalmanZSync::ResizeZone(int zone, int new_size) +{ + if((size_t) zone >= zones.size()) + { + return; + } + + if(((unsigned int)new_size >= zones[zone].leds_min) && ((unsigned int)new_size <= zones[zone].leds_max)) + { + zones[zone].leds_count = new_size; + + SetupZones(); + } +} + +void RGBController_ZalmanZSync::DeviceUpdateLEDs() +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + if(zones[zone_idx].leds_count > 0) + { + controller->SetChannelLEDs((unsigned char)zone_idx, zones[zone_idx].colors, zones[zone_idx].leds_count); + } + } +} + +void RGBController_ZalmanZSync::UpdateZoneLEDs(int zone) +{ + controller->SetChannelLEDs(zone, zones[zone].colors, zones[zone].leds_count); +} + +void RGBController_ZalmanZSync::UpdateSingleLED(int led) +{ + unsigned int channel = leds_channel[led]; + + controller->SetChannelLEDs(channel, zones[channel].colors, zones[channel].leds_count); +} + +void RGBController_ZalmanZSync::DeviceUpdateMode() +{ + if(modes[active_mode].value == 0xFFFF) + { + DeviceUpdateLEDs(); + } + else + { + for(int channel = 0; channel < ZALMAN_Z_SYNC_NUM_CHANNELS; channel++) + { + unsigned int direction = 0; + bool random = (modes[active_mode].color_mode == MODE_COLORS_RANDOM); + + if(modes[active_mode].direction == MODE_DIRECTION_RIGHT) + { + direction = 1; + } + + unsigned char mode_colors[9]; + + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + for(std::size_t i = 0; i < modes[active_mode].colors.size(); i++) + { + mode_colors[(3 * i) + 0] = RGBGetRValue(modes[active_mode].colors[i]); + mode_colors[(3 * i) + 1] = RGBGetGValue(modes[active_mode].colors[i]); + mode_colors[(3 * i) + 2] = RGBGetBValue(modes[active_mode].colors[i]); + } + } + + controller->SetChannelEffect(channel, + zones[channel].leds_count, + modes[active_mode].value, + modes[active_mode].speed, + direction, + random, + mode_colors[0], + mode_colors[1], + mode_colors[2], + mode_colors[3], + mode_colors[4], + mode_colors[5], + mode_colors[6], + mode_colors[7], + mode_colors[8]); + } + } +} diff --git a/Controllers/ZalmanZSyncController/RGBController_ZalmanZSync.h b/Controllers/ZalmanZSyncController/RGBController_ZalmanZSync.h new file mode 100644 index 0000000..d943440 --- /dev/null +++ b/Controllers/ZalmanZSyncController/RGBController_ZalmanZSync.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_ZalmanZSync.h | +| | +| RGBController for Zalman Z Sync | +| | +| Adam Honse (CalcProgrammer1) 30 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ZalmanZSyncController.h" + +class RGBController_ZalmanZSync : public RGBController +{ +public: + RGBController_ZalmanZSync(ZalmanZSyncController* controller_ptr); + ~RGBController_ZalmanZSync(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ZalmanZSyncController* controller; + std::vector leds_channel; + std::vector zones_channel; +}; diff --git a/Controllers/ZalmanZSyncController/ZalmanZSyncController.cpp b/Controllers/ZalmanZSyncController/ZalmanZSyncController.cpp new file mode 100644 index 0000000..71f429b --- /dev/null +++ b/Controllers/ZalmanZSyncController/ZalmanZSyncController.cpp @@ -0,0 +1,486 @@ +/*---------------------------------------------------------*\ +| ZalmanZSyncController.cpp | +| | +| Driver for Zalman Z Sync | +| | +| Based on CorsairLightingNodeConroller, the protocol is | +| the same as the Corsair Lighting Node except with 8 | +| channels | +| | +| Adam Honse (CalcProgrammer1) 30 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "StringUtils.h" +#include "ZalmanZSyncController.h" + +using namespace std::chrono_literals; + +ZalmanZSyncController::ZalmanZSyncController(hid_device* dev_handle, const char* path, std::string dev_name) +{ + dev = dev_handle; + location = path; + name = dev_name; + + SendFirmwareRequest(); + + /*-----------------------------------------------------*\ + | The Corsair Lighting Node Pro requires a packet within| + | 20 seconds of sending the lighting change in order | + | to not revert back into rainbow mode. Start a thread | + | to continuously send a keepalive packet every 5s | + \*-----------------------------------------------------*/ + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&ZalmanZSyncController::KeepaliveThread, this); +} + +ZalmanZSyncController::~ZalmanZSyncController() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + hid_close(dev); +} + +void ZalmanZSyncController::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_commit_time) > std::chrono::seconds(1)) + { + SendCommit(); + } + std::this_thread::sleep_for(1s); + } +} + +std::string ZalmanZSyncController::GetFirmwareString() +{ + return(firmware_version); +} + +std::string ZalmanZSyncController::GetLocationString() +{ + return("HID: " + location); +} + +std::string ZalmanZSyncController::GetNameString() +{ + return(name); +} + +std::string ZalmanZSyncController::GetSerialString() +{ + wchar_t serial_string[128]; + int ret = hid_get_serial_number_string(dev, serial_string, 128); + + if(ret != 0) + { + return(""); + } + + return(StringUtils::wstring_to_string(serial_string)); +} + +void ZalmanZSyncController::SetChannelEffect(unsigned char channel, + unsigned char num_leds, + unsigned char mode, + unsigned char speed, + unsigned char direction, + bool random, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2, + unsigned char red3, + unsigned char grn3, + unsigned char blu3 + ) +{ + /*-----------------------------------------------------*\ + | Send Reset packet | + \*-----------------------------------------------------*/ + SendReset(channel); + + /*-----------------------------------------------------*\ + | Send Begin packet | + \*-----------------------------------------------------*/ + SendBegin(channel); + + /*-----------------------------------------------------*\ + | Set Port State packet | + \*-----------------------------------------------------*/ + SendPortState(channel, ZALMAN_Z_SYNC_PORT_STATE_HARDWARE); + + /*-----------------------------------------------------*\ + | Set Effect Configuration packet | + \*-----------------------------------------------------*/ + SendEffectConfig + ( + channel, + 0, + num_leds, + mode, + speed, + direction, + random, + red1, + grn1, + blu1, + red2, + grn2, + blu2, + red3, + grn3, + blu3, + 0, + 0, + 0 + ); + + /*-----------------------------------------------------*\ + | Send Commit packet | + \*-----------------------------------------------------*/ + SendCommit(); +} + +void ZalmanZSyncController::SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors) +{ + unsigned char red_color_data[50]; + unsigned char grn_color_data[50]; + unsigned char blu_color_data[50]; + unsigned char pkt_offset = 0; + unsigned char pkt_size = 0; + unsigned int colors_remaining = num_colors; + + /*-----------------------------------------------------*\ + | Send Port State packet | + \*-----------------------------------------------------*/ + SendPortState(channel, ZALMAN_Z_SYNC_PORT_STATE_SOFTWARE); + + /*-----------------------------------------------------*\ + | Loop through colors and send 50 at a time | + \*-----------------------------------------------------*/ + while(colors_remaining > 0) + { + if(colors_remaining < 50) + { + pkt_size = colors_remaining; + } + else + { + pkt_size = 50; + } + + for(int color_idx = 0; color_idx < pkt_size; color_idx++) + { + red_color_data[color_idx] = RGBGetRValue(colors[pkt_offset + color_idx]); + grn_color_data[color_idx] = RGBGetGValue(colors[pkt_offset + color_idx]); + blu_color_data[color_idx] = RGBGetBValue(colors[pkt_offset + color_idx]); + } + + SendDirect(channel, pkt_offset, pkt_size, ZALMAN_Z_SYNC_DIRECT_CHANNEL_RED, red_color_data); + SendDirect(channel, pkt_offset, pkt_size, ZALMAN_Z_SYNC_DIRECT_CHANNEL_GREEN, grn_color_data); + SendDirect(channel, pkt_offset, pkt_size, ZALMAN_Z_SYNC_DIRECT_CHANNEL_BLUE, blu_color_data); + + colors_remaining -= pkt_size; + pkt_offset += pkt_size; + } + + /*-----------------------------------------------------*\ + | Send Commit packet | + \*-----------------------------------------------------*/ + SendCommit(); +} + +/*-------------------------------------------------------------------------------------------------*\ +| Private packet sending functions. | +\*-------------------------------------------------------------------------------------------------*/ + +void ZalmanZSyncController::SendFirmwareRequest() +{ + int actual; + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Firmware Version Request packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = ZALMAN_Z_SYNC_PACKET_ID_FIRMWARE; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + actual = hid_read(dev, usb_buf, 17); + + if(actual > 0) + { + if(usb_buf[0x03] < 112) + { + firmware_version = "0.7.1"; + } + else + { + firmware_version = std::to_string(usb_buf[0x02]) + "." + std::to_string(usb_buf[0x03] >> 4) + "." + std::to_string(usb_buf[0x03] & 0x0F); + } + } +} + +void ZalmanZSyncController::SendDirect + ( + unsigned char channel, + unsigned char start, + unsigned char count, + unsigned char color_channel, + unsigned char* color_data + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Direct packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = ZALMAN_Z_SYNC_PACKET_ID_DIRECT; + usb_buf[0x02] = channel; + usb_buf[0x03] = start; + usb_buf[0x04] = count; + usb_buf[0x05] = color_channel; + + /*-----------------------------------------------------*\ + | Copy in color data bytes | + \*-----------------------------------------------------*/ + memcpy(&usb_buf[0x06], color_data, count); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 17); +} + +void ZalmanZSyncController::SendCommit() +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Update last commit time | + \*-----------------------------------------------------*/ + last_commit_time = std::chrono::steady_clock::now(); + + /*-----------------------------------------------------*\ + | Set up Commit packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = ZALMAN_Z_SYNC_PACKET_ID_COMMIT; + usb_buf[0x02] = 0xFF; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 17); +} + +void ZalmanZSyncController::SendBegin + ( + unsigned char channel + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Begin packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = ZALMAN_Z_SYNC_PACKET_ID_BEGIN; + usb_buf[0x02] = channel; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 17); +} + +void ZalmanZSyncController::SendEffectConfig + ( + unsigned char channel, + unsigned char count, + unsigned char led_type, + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char change_style, + unsigned char color_0_red, + unsigned char color_0_green, + unsigned char color_0_blue, + unsigned char color_1_red, + unsigned char color_1_green, + unsigned char color_1_blue, + unsigned char color_2_red, + unsigned char color_2_green, + unsigned char color_2_blue, + unsigned short temperature_0, + unsigned short temperature_1, + unsigned short temperature_2 + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Effect Config packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = ZALMAN_Z_SYNC_PACKET_ID_EFFECT_CONFIG; + usb_buf[0x02] = channel; + usb_buf[0x03] = count; + usb_buf[0x04] = led_type; + + /*-----------------------------------------------------*\ + | Set up mode parameters | + \*-----------------------------------------------------*/ + usb_buf[0x05] = mode; + usb_buf[0x06] = speed; + usb_buf[0x07] = direction; + usb_buf[0x08] = change_style; + usb_buf[0x09] = 0; + + /*-----------------------------------------------------*\ + | Set up mode colors | + \*-----------------------------------------------------*/ + usb_buf[0x0A] = color_0_red; + usb_buf[0x0B] = color_0_green; + usb_buf[0x0C] = color_0_blue; + usb_buf[0x0D] = color_1_red; + usb_buf[0x0E] = color_1_green; + usb_buf[0x0F] = color_1_blue; + usb_buf[0x10] = color_2_red; + usb_buf[0x11] = color_2_green; + usb_buf[0x12] = color_2_blue; + + /*-----------------------------------------------------*\ + | Set up temperatures | + \*-----------------------------------------------------*/ + usb_buf[0x13] = (temperature_0 >> 8); + usb_buf[0x14] = (temperature_0 & 0xFF); + usb_buf[0x15] = (temperature_1 >> 8); + usb_buf[0x16] = (temperature_1 & 0xFF); + usb_buf[0x17] = (temperature_2 >> 8); + usb_buf[0x18] = (temperature_2 & 0xFF); + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 17); +} + +void ZalmanZSyncController::SendTemperature() +{ + +} + +void ZalmanZSyncController::SendReset + ( + unsigned char channel + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Reset packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = ZALMAN_Z_SYNC_PACKET_ID_RESET; + usb_buf[0x02] = channel; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 17); +} + +void ZalmanZSyncController::SendPortState + ( + unsigned char channel, + unsigned char state + ) +{ + unsigned char usb_buf[65]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Port State packet | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x00; + usb_buf[0x01] = ZALMAN_Z_SYNC_PACKET_ID_PORT_STATE; + usb_buf[0x02] = channel; + usb_buf[0x03] = state; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + hid_write(dev, usb_buf, 65); + hid_read(dev, usb_buf, 17); +} + +void ZalmanZSyncController::SendBrightness() +{ + +} + +void ZalmanZSyncController::SendLEDCount() +{ + +} + +void ZalmanZSyncController::SendProtocol() +{ + +} diff --git a/Controllers/ZalmanZSyncController/ZalmanZSyncController.h b/Controllers/ZalmanZSyncController/ZalmanZSyncController.h new file mode 100644 index 0000000..8d1ef63 --- /dev/null +++ b/Controllers/ZalmanZSyncController/ZalmanZSyncController.h @@ -0,0 +1,196 @@ +/*---------------------------------------------------------*\ +| ZalmanZSyncController.h | +| | +| Driver for Zalman Z Sync | +| | +| Based on CorsairLightingNodeConroller, the protocol is | +| the same as the Corsair Lighting Node except with 8 | +| channels | +| | +| Adam Honse (CalcProgrammer1) 30 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" + +enum +{ + ZALMAN_Z_SYNC_PACKET_ID_FIRMWARE = 0x02, /* Get firmware version */ + ZALMAN_Z_SYNC_PACKET_ID_DIRECT = 0x32, /* Direct mode LED update packet */ + ZALMAN_Z_SYNC_PACKET_ID_COMMIT = 0x33, /* Commit changes packet */ + ZALMAN_Z_SYNC_PACKET_ID_BEGIN = 0x34, /* Begin effect packet */ + ZALMAN_Z_SYNC_PACKET_ID_EFFECT_CONFIG = 0x35, /* Effect mode configuration packet */ + ZALMAN_Z_SYNC_PACKET_ID_TEMPERATURE = 0x36, /* Update temperature value packet */ + ZALMAN_Z_SYNC_PACKET_ID_RESET = 0x37, /* Reset channel packet */ + ZALMAN_Z_SYNC_PACKET_ID_PORT_STATE = 0x38, /* Set port state packet */ + ZALMAN_Z_SYNC_PACKET_ID_BRIGHTNESS = 0x39, /* Set brightness packet */ + ZALMAN_Z_SYNC_PACKET_ID_LED_COUNT = 0x3A, /* Set LED count packet */ + ZALMAN_Z_SYNC_PACKET_ID_PROTOCOL = 0x3B, /* Set protocol packet */ +}; + +enum +{ + ZALMAN_Z_SYNC_DIRECT_CHANNEL_RED = 0x00, /* Red channel for direct update */ + ZALMAN_Z_SYNC_DIRECT_CHANNEL_GREEN = 0x01, /* Green channel for direct update */ + ZALMAN_Z_SYNC_DIRECT_CHANNEL_BLUE = 0x02, /* Blue channel for direct update */ +}; + +enum +{ + ZALMAN_Z_SYNC_PORT_STATE_HARDWARE = 0x01, /* Effect hardware control of channel */ + ZALMAN_Z_SYNC_PORT_STATE_SOFTWARE = 0x02, /* Direct software control of channel */ +}; + +enum +{ + ZALMAN_Z_SYNC_LED_TYPE_LED_STRIP = 0x0A, /* Corsair LED Strip Type */ + ZALMAN_Z_SYNC_LED_TYPE_HD_FAN = 0x0C, /* Corsair HD-series Fan Type */ + ZALMAN_Z_SYNC_LED_TYPE_SP_FAN = 0x01, /* Corsair SP-series Fan Type */ + ZALMAN_Z_SYNC_LED_TYPE_ML_FAN = 0x02, /* Corsair ML-series Fan Type */ +}; + +enum +{ + ZALMAN_Z_SYNC_CHANNEL_1 = 0x00, /* Channel 1 */ + ZALMAN_Z_SYNC_CHANNEL_2 = 0x01, /* Channel 2 */ + ZALMAN_Z_SYNC_CHANNEL_3 = 0x02, /* Channel 3 */ + ZALMAN_Z_SYNC_CHANNEL_4 = 0x03, /* Channel 4 */ + ZALMAN_Z_SYNC_CHANNEL_5 = 0x04, /* Channel 5 */ + ZALMAN_Z_SYNC_CHANNEL_6 = 0x05, /* Channel 6 */ + ZALMAN_Z_SYNC_CHANNEL_7 = 0x06, /* Channel 7 */ + ZALMAN_Z_SYNC_CHANNEL_8 = 0x07, /* Channel 8 */ + ZALMAN_Z_SYNC_NUM_CHANNELS = 0x08, /* Number of channels */ +}; + +enum +{ + ZALMAN_Z_SYNC_SPEED_FAST = 0x00, /* Fast speed */ + ZALMAN_Z_SYNC_SPEED_MEDIUM = 0x01, /* Medium speed */ + ZALMAN_Z_SYNC_SPEED_SLOW = 0x02, /* Slow speed */ +}; + +enum +{ + ZALMAN_Z_SYNC_MODE_RAINBOW_WAVE = 0x00, /* Rainbow Wave mode */ + ZALMAN_Z_SYNC_MODE_COLOR_SHIFT = 0x01, /* Color Shift mode */ + ZALMAN_Z_SYNC_MODE_COLOR_PULSE = 0x02, /* Color Pulse mode */ + ZALMAN_Z_SYNC_MODE_COLOR_WAVE = 0x03, /* Color Wave mode */ + ZALMAN_Z_SYNC_MODE_STATIC = 0x04, /* Static mode */ + ZALMAN_Z_SYNC_MODE_TEMPERATURE = 0x05, /* Temperature mode */ + ZALMAN_Z_SYNC_MODE_VISOR = 0x06, /* Visor mode */ + ZALMAN_Z_SYNC_MODE_MARQUEE = 0x07, /* Marquee mode */ + ZALMAN_Z_SYNC_MODE_BLINK = 0x08, /* Blink mode */ + ZALMAN_Z_SYNC_MODE_SEQUENTIAL = 0x09, /* Sequential mode */ + ZALMAN_Z_SYNC_MODE_RAINBOW = 0x0A, /* Rainbow mode */ +}; + +class ZalmanZSyncController +{ +public: + ZalmanZSyncController(hid_device* dev_handle, const char* path, std::string dev_name); + ~ZalmanZSyncController(); + + std::string GetFirmwareString(); + std::string GetLocationString(); + std::string GetNameString(); + std::string GetSerialString(); + + unsigned int GetStripsOnChannel(unsigned int channel); + + void SetChannelEffect(unsigned char channel, + unsigned char num_leds, + unsigned char mode, + unsigned char speed, + unsigned char direction, + bool random, + unsigned char red1, + unsigned char grn1, + unsigned char blu1, + unsigned char red2, + unsigned char grn2, + unsigned char blu2, + unsigned char red3, + unsigned char grn3, + unsigned char blu3 + ); + + void SetChannelLEDs(unsigned char channel, RGBColor * colors, unsigned int num_colors); + + void KeepaliveThread(); + +private: + hid_device* dev; + std::string firmware_version; + std::string location; + std::string name; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_commit_time; + + void SendFirmwareRequest(); + + void SendDirect + ( + unsigned char channel, + unsigned char start, + unsigned char count, + unsigned char color_channel, + unsigned char* color_data + ); + + void SendCommit(); + + void SendBegin + ( + unsigned char channel + ); + + void SendEffectConfig + ( + unsigned char channel, + unsigned char count, + unsigned char led_type, + unsigned char mode, + unsigned char speed, + unsigned char direction, + unsigned char change_style, + unsigned char color_0_red, + unsigned char color_0_green, + unsigned char color_0_blue, + unsigned char color_1_red, + unsigned char color_1_green, + unsigned char color_1_blue, + unsigned char color_2_red, + unsigned char color_2_green, + unsigned char color_2_blue, + unsigned short temperature_0, + unsigned short temperature_1, + unsigned short temperature_2 + ); + + void SendTemperature(); + + void SendReset + ( + unsigned char channel + ); + + void SendPortState + ( + unsigned char channel, + unsigned char state + ); + + void SendBrightness(); + + void SendLEDCount(); + + void SendProtocol(); +}; diff --git a/Controllers/ZalmanZSyncController/ZalmanZSyncControllerDetect.cpp b/Controllers/ZalmanZSyncController/ZalmanZSyncControllerDetect.cpp new file mode 100644 index 0000000..1396a74 --- /dev/null +++ b/Controllers/ZalmanZSyncController/ZalmanZSyncControllerDetect.cpp @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| ZalmanZSyncControllerDetect.cpp | +| | +| Detector for Zalman Z Sync | +| | +| Adam Honse (CalcProgrammer1) 30 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "Detector.h" +#include "ZalmanZSyncController.h" +#include "RGBController_ZalmanZSync.h" + +#define ZALMAN_VID 0x1C57 +#define ZALMAN_Z_SYNC_PID 0x7ED0 + +/******************************************************************************************\ +* * +* DetectZalmanZSyncControllers * +* * +* Detect devices supported by the Zalman Z Sync driver * +* * +\******************************************************************************************/ + +void DetectZalmanZSyncControllers(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + ZalmanZSyncController* controller = new ZalmanZSyncController(dev, info->path, name); + RGBController_ZalmanZSync* rgb_controller = new RGBController_ZalmanZSync(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} /* DetectZalmanZSyncControllers() */ + +REGISTER_HID_DETECTOR("Zalman Z Sync", DetectZalmanZSyncControllers, ZALMAN_VID, ZALMAN_Z_SYNC_PID); diff --git a/Controllers/ZotacBlackwellGPUController/RGBController_ZotacBlackwellGPU.cpp b/Controllers/ZotacBlackwellGPUController/RGBController_ZotacBlackwellGPU.cpp new file mode 100644 index 0000000..29648a4 --- /dev/null +++ b/Controllers/ZotacBlackwellGPUController/RGBController_ZotacBlackwellGPU.cpp @@ -0,0 +1,383 @@ +/*---------------------------------------------------------*\ +| RGBController_ZotacBlackwellGPU.cpp | +| | +| RGBController for ZOTAC Blackwell (RTX 50 series) GPU | +| | +| Eder Sánchez 27 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ZotacBlackwellGPU.h" +#include "LogManager.h" +#include "pci_ids.h" + +/**------------------------------------------------------------------*\ + @name ZOTAC RTX 50 series GPU + @category GPU + @type I2C + @save :robot: + @direct :x: + @effects :tools: + @detectors DetectZotacBlackwellGPUControllersPCI + @comment + Supports ZOTAC Blackwell (RTX 50 series) GPUs. The zone layout + varies per card and is resolved from a static table keyed on + PCI device and sub-device IDs. + + The controller uses individual SMBus byte writes (registers + 0x20-0x2F) with a 3ms delay between each transaction. + + To add new cards, add PCI ID entries in `pci_ids/pci_ids.h`, + a zone config entry in `device_zone_configs` below, and a + `REGISTER_I2C_PCI_DETECTOR` line in + `Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUControllerDetect.cpp`. +\*-------------------------------------------------------------------*/ + +const RGBController_ZotacBlackwellGPU::DeviceZoneConfig RGBController_ZotacBlackwellGPU::device_zone_configs[] = +{ + { NVIDIA_RTX5080_DEV, ZOTAC_RTX5080_AMP_EXTREME_SUB_DEV, { "Logo", "Side Bar", "Infinity Mirror" }, 3 }, + { NVIDIA_RTX5090_DEV, ZOTAC_RTX5090_SOLID_OC_SUB_DEV, { "ZOTAC Gaming", "Logo" }, 2 }, + { 0, 0, { nullptr }, 0 } +}; + +const RGBController_ZotacBlackwellGPU::DeviceZoneConfig* RGBController_ZotacBlackwellGPU::FindZoneConfig(uint16_t device, uint16_t subdevice) +{ + for(const DeviceZoneConfig* cfg = device_zone_configs; cfg->zone_count != 0; cfg++) + { + if(cfg->device == device && cfg->subdevice == subdevice) + { + return cfg; + } + } + return nullptr; +} + +RGBController_ZotacBlackwellGPU::RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr, + uint16_t device, uint16_t subdevice) +{ + controller = controller_ptr; + + const DeviceZoneConfig* cfg = FindZoneConfig(device, subdevice); + if(cfg == nullptr) + { + LOG_ERROR("[%s] Unrecognized PCI device/subdevice: %04X/%04X. Falling back to three generic zones.", + controller->GetName().c_str(), device, subdevice); + zone_names.push_back("Zone 0"); + zone_names.push_back("Zone 1"); + zone_names.push_back("Zone 2"); + } + else + { + for(uint8_t z = 0; z < cfg->zone_count; z++) + { + zone_names.push_back(cfg->zones[z]); + } + } + + name = controller->GetName(); + vendor = "ZOTAC"; + description = "ZOTAC RTX 50 series RGB GPU Device (" + controller->GetVersion() + ")"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + version = controller->GetVersion(); + + /*---------------------------------------------------------*\ + | Static mode | + \*---------------------------------------------------------*/ + mode STATIC; + STATIC.name = "Static"; + STATIC.value = ZOTAC_BLACKWELL_GPU_MODE_STATIC; + STATIC.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR; + STATIC.brightness_min = 0; + STATIC.brightness_max = 100; + STATIC.brightness = 100; + STATIC.color_mode = MODE_COLORS_PER_LED; + modes.push_back(STATIC); + + /*---------------------------------------------------------*\ + | Breathe mode | + \*---------------------------------------------------------*/ + mode BREATHE; + BREATHE.name = "Breathe"; + BREATHE.value = ZOTAC_BLACKWELL_GPU_MODE_BREATHE; + BREATHE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + BREATHE.brightness_min = 0; + BREATHE.brightness_max = 100; + BREATHE.brightness = 100; + BREATHE.speed_min = 0; + BREATHE.speed_max = 100; + BREATHE.speed = 20; + BREATHE.color_mode = MODE_COLORS_PER_LED; + modes.push_back(BREATHE); + + /*---------------------------------------------------------*\ + | Fade mode | + \*---------------------------------------------------------*/ + mode FADE; + FADE.name = "Fade"; + FADE.value = ZOTAC_BLACKWELL_GPU_MODE_FADE; + FADE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + FADE.speed_min = 0; + FADE.speed_max = 100; + FADE.speed = 20; + FADE.color_mode = MODE_COLORS_NONE; + modes.push_back(FADE); + + /*---------------------------------------------------------*\ + | Wink mode | + \*---------------------------------------------------------*/ + mode WINK; + WINK.name = "Wink"; + WINK.value = ZOTAC_BLACKWELL_GPU_MODE_WINK; + WINK.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + WINK.brightness_min = 0; + WINK.brightness_max = 100; + WINK.brightness = 100; + WINK.speed_min = 0; + WINK.speed_max = 100; + WINK.speed = 20; + WINK.color_mode = MODE_COLORS_PER_LED; + modes.push_back(WINK); + + /*---------------------------------------------------------*\ + | Glide mode | + \*---------------------------------------------------------*/ + mode GLIDE; + GLIDE.name = "Glide"; + GLIDE.value = ZOTAC_BLACKWELL_GPU_MODE_GLIDE; + GLIDE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_PER_LED_COLOR; + GLIDE.brightness_min = 0; + GLIDE.brightness_max = 100; + GLIDE.brightness = 100; + GLIDE.speed_min = 0; + GLIDE.speed_max = 100; + GLIDE.speed = 20; + GLIDE.color_mode = MODE_COLORS_PER_LED; + modes.push_back(GLIDE); + + /*---------------------------------------------------------*\ + | Prism mode (called "Rainbow" in older firmware) | + \*---------------------------------------------------------*/ + mode PRISM; + PRISM.name = "Prism"; + PRISM.value = ZOTAC_BLACKWELL_GPU_MODE_PRISM; + PRISM.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + PRISM.speed_min = 0; + PRISM.speed_max = 100; + PRISM.speed = 20; + PRISM.color_mode = MODE_COLORS_NONE; + modes.push_back(PRISM); + + /*---------------------------------------------------------*\ + | Bokeh mode | + \*---------------------------------------------------------*/ + mode BOKEH; + BOKEH.name = "Bokeh"; + BOKEH.value = ZOTAC_BLACKWELL_GPU_MODE_BOKEH; + BOKEH.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + BOKEH.brightness_min = 0; + BOKEH.brightness_max = 100; + BOKEH.brightness = 100; + BOKEH.speed_min = 0; + BOKEH.speed_max = 100; + BOKEH.speed = 20; + BOKEH.color_mode = MODE_COLORS_PER_LED; + modes.push_back(BOKEH); + + /*---------------------------------------------------------*\ + | Beacon mode | + \*---------------------------------------------------------*/ + mode BEACON; + BEACON.name = "Beacon"; + BEACON.value = ZOTAC_BLACKWELL_GPU_MODE_BEACON; + BEACON.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + BEACON.brightness_min = 0; + BEACON.brightness_max = 100; + BEACON.brightness = 100; + BEACON.speed_min = 0; + BEACON.speed_max = 100; + BEACON.speed = 20; + BEACON.color_mode = MODE_COLORS_PER_LED; + modes.push_back(BEACON); + + /*---------------------------------------------------------*\ + | Tandem mode | + \*---------------------------------------------------------*/ + mode TANDEM; + TANDEM.name = "Tandem"; + TANDEM.value = ZOTAC_BLACKWELL_GPU_MODE_TANDEM; + TANDEM.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + TANDEM.brightness_min = 0; + TANDEM.brightness_max = 100; + TANDEM.brightness = 100; + TANDEM.speed_min = 0; + TANDEM.speed_max = 100; + TANDEM.speed = 20; + TANDEM.color_mode = MODE_COLORS_PER_LED; + modes.push_back(TANDEM); + + /*---------------------------------------------------------*\ + | Tidal mode | + \*---------------------------------------------------------*/ + mode TIDAL; + TIDAL.name = "Tidal"; + TIDAL.value = ZOTAC_BLACKWELL_GPU_MODE_TIDAL; + TIDAL.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR | MODE_FLAG_HAS_PER_LED_COLOR; + TIDAL.brightness_min = 0; + TIDAL.brightness_max = 100; + TIDAL.brightness = 100; + TIDAL.speed_min = 0; + TIDAL.speed_max = 100; + TIDAL.speed = 20; + TIDAL.color_mode = MODE_COLORS_PER_LED; + modes.push_back(TIDAL); + + /*---------------------------------------------------------*\ + | Astra mode | + \*---------------------------------------------------------*/ + mode ASTRA; + ASTRA.name = "Astra"; + ASTRA.value = ZOTAC_BLACKWELL_GPU_MODE_ASTRA; + ASTRA.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + ASTRA.brightness_min = 0; + ASTRA.brightness_max = 100; + ASTRA.brightness = 100; + ASTRA.speed_min = 0; + ASTRA.speed_max = 100; + ASTRA.speed = 20; + ASTRA.color_mode = MODE_COLORS_PER_LED; + modes.push_back(ASTRA); + + /*---------------------------------------------------------*\ + | Cosmic mode | + \*---------------------------------------------------------*/ + mode COSMIC; + COSMIC.name = "Cosmic"; + COSMIC.value = ZOTAC_BLACKWELL_GPU_MODE_COSMIC; + COSMIC.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + COSMIC.brightness_min = 0; + COSMIC.brightness_max = 100; + COSMIC.brightness = 100; + COSMIC.speed_min = 0; + COSMIC.speed_max = 100; + COSMIC.speed = 20; + COSMIC.color_mode = MODE_COLORS_PER_LED; + modes.push_back(COSMIC); + + /*---------------------------------------------------------*\ + | Volta mode | + \*---------------------------------------------------------*/ + mode VOLTA; + VOLTA.name = "Volta"; + VOLTA.value = ZOTAC_BLACKWELL_GPU_MODE_VOLTA; + VOLTA.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + VOLTA.brightness_min = 0; + VOLTA.brightness_max = 100; + VOLTA.brightness = 100; + VOLTA.speed_min = 0; + VOLTA.speed_max = 100; + VOLTA.speed = 20; + VOLTA.color_mode = MODE_COLORS_PER_LED; + modes.push_back(VOLTA); + + SetupZones(); +} + +RGBController_ZotacBlackwellGPU::~RGBController_ZotacBlackwellGPU() +{ + delete controller; +} + +void RGBController_ZotacBlackwellGPU::SetupZones() +{ + /*---------------------------------------------------------*\ + | One single-LED zone per name from the device_zone_configs | + | table. The zone's index is its position here, which is | + | written verbatim to the zone register (0x21) on update. | + \*---------------------------------------------------------*/ + for(const std::string& zone_name : zone_names) + { + zone new_zone; + new_zone.name = zone_name; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + led new_led; + new_led.name = zone_name + " LED"; + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_ZotacBlackwellGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ZotacBlackwellGPU::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_ZotacBlackwellGPU::UpdateZoneLEDs(int zone) +{ + DeviceUpdateZone(zone); +} + +void RGBController_ZotacBlackwellGPU::UpdateSingleLED(int led) +{ + DeviceUpdateZone(led); +} + +void RGBController_ZotacBlackwellGPU::DeviceUpdateZone(int zone) +{ + unsigned int mode_val = modes[active_mode].value; + unsigned int brightness = modes[active_mode].brightness; + unsigned int speed = modes[active_mode].speed; + unsigned int direction = modes[active_mode].direction == MODE_DIRECTION_RIGHT + ? ZOTAC_BLACKWELL_GPU_DIR_RIGHT + : ZOTAC_BLACKWELL_GPU_DIR_LEFT; + + RGBColor color1; + RGBColor color2 = ToRGBColor(0, 0, 0); + + switch(modes[active_mode].color_mode) + { + case MODE_COLORS_PER_LED: + color1 = colors[zone]; + break; + + case MODE_COLORS_MODE_SPECIFIC: + color1 = (modes[active_mode].colors.size() >= 1) + ? modes[active_mode].colors[0] + : ToRGBColor(0, 0, 0); + color2 = (modes[active_mode].colors.size() >= 2) + ? modes[active_mode].colors[1] + : ToRGBColor(0, 0, 0); + break; + + default: + color1 = ToRGBColor(0, 0, 0); + break; + } + + controller->SetMode(zone, mode_val, color1, color2, brightness, speed, direction); + controller->Commit(); +} + +void RGBController_ZotacBlackwellGPU::DeviceUpdateMode() +{ + for(unsigned int zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + DeviceUpdateZone(zone_idx); + } +} diff --git a/Controllers/ZotacBlackwellGPUController/RGBController_ZotacBlackwellGPU.h b/Controllers/ZotacBlackwellGPUController/RGBController_ZotacBlackwellGPU.h new file mode 100644 index 0000000..40f8224 --- /dev/null +++ b/Controllers/ZotacBlackwellGPUController/RGBController_ZotacBlackwellGPU.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| RGBController_ZotacBlackwellGPU.h | +| | +| RGBController for ZOTAC Blackwell (RTX 50 series) GPU | +| | +| Eder Sánchez 27 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "ZotacBlackwellGPUController.h" + +class RGBController_ZotacBlackwellGPU : public RGBController +{ +public: + RGBController_ZotacBlackwellGPU(ZotacBlackwellGPUController* controller_ptr, + uint16_t device, uint16_t subdevice); + ~RGBController_ZotacBlackwellGPU(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceUpdateZone(int zone); + +private: + ZotacBlackwellGPUController* controller; + std::vector zone_names; + + struct DeviceZoneConfig + { + uint16_t device; + uint16_t subdevice; + const char* zones[4]; + uint8_t zone_count; + }; + + static const DeviceZoneConfig device_zone_configs[]; + + static const DeviceZoneConfig* FindZoneConfig(uint16_t device, uint16_t subdevice); +}; diff --git a/Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUController.cpp b/Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUController.cpp new file mode 100644 index 0000000..e02e962 --- /dev/null +++ b/Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUController.cpp @@ -0,0 +1,125 @@ +/*---------------------------------------------------------*\ +| ZotacBlackwellGPUController.cpp | +| | +| Driver for ZOTAC Blackwell (RTX 50 series) GPU | +| | +| Eder Sánchez 27 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "ZotacBlackwellGPUController.h" +#include "LogManager.h" + +ZotacBlackwellGPUController::ZotacBlackwellGPUController(i2c_smbus_interface* bus, u8 dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; + + ReadVersion(); +} + +ZotacBlackwellGPUController::~ZotacBlackwellGPUController() +{ +} + +std::string ZotacBlackwellGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string ZotacBlackwellGPUController::GetName() +{ + return(name); +} + +std::string ZotacBlackwellGPUController::GetVersion() +{ + return(version); +} + +void ZotacBlackwellGPUController::ReadVersion() +{ + /*---------------------------------------------------------*\ + | Read version via raw I2C block read. The first bytes | + | returned contain the ASCII version string (e.g. | + | "N762A-2008e"). If the raw read fails or returns empty, | + | fall back to reading register 0x2F as an identifier. | + \*---------------------------------------------------------*/ + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + + if(bus->i2c_read_block(dev, &rdata_len, rdata_pkt) >= 0 && rdata_pkt[0] != 0x00) + { + version = std::string((char*)rdata_pkt); + } + else + { + version = "Unknown"; + } + + LOG_INFO("[%s] Firmware version: %s", name.c_str(), version.c_str()); +} + +void ZotacBlackwellGPUController::SetMode +( + unsigned int zone, + unsigned int mode_val, + RGBColor color1, + RGBColor color2, + unsigned int brightness, + unsigned int speed, + unsigned int direction +) +{ + /*---------------------------------------------------------*\ + | Write all 16 registers 0x20 - 0x2F via individual | + | i2c_smbus_write_byte_data calls with 3ms delay between | + | each transaction. Partial writes are not supported. | + \*---------------------------------------------------------*/ + + u8 regs[16]; + regs[0x00] = 0x00; /* 0x20 - Fixed = 0x00 */ + regs[0x01] = (u8)zone; /* 0x21 - Zone index */ + regs[0x02] = (u8)mode_val; /* 0x22 - Mode */ + regs[0x03] = (u8)RGBGetRValue(color1); /* 0x23 - Red 1 */ + regs[0x04] = (u8)RGBGetGValue(color1); /* 0x24 - Green 1 */ + regs[0x05] = (u8)RGBGetBValue(color1); /* 0x25 - Blue 1 */ + regs[0x06] = (u8)RGBGetRValue(color2); /* 0x26 - Red 2 */ + regs[0x07] = (u8)RGBGetGValue(color2); /* 0x27 - Green 2 */ + regs[0x08] = (u8)RGBGetBValue(color2); /* 0x28 - Blue 2 */ + regs[0x09] = (u8)brightness; /* 0x29 - Brightness (0-100) */ + regs[0x0A] = (u8)speed; /* 0x2A - Speed (0-100) */ + regs[0x0B] = (u8)direction; /* 0x2B - Direction */ + regs[0x0C] = 0x00; /* 0x2C - Reserved */ + regs[0x0D] = 0x00; /* 0x2D - Reserved */ + regs[0x0E] = 0x00; /* 0x2E - Reserved */ + regs[0x0F] = 0x00; /* 0x2F - Reserved */ + + for(int i = 0; i < 16; i++) + { + bus->i2c_smbus_write_byte_data(dev, (u8)(ZOTAC_BLACKWELL_GPU_REG_FIXED + i), regs[i]); + std::this_thread::sleep_for(std::chrono::microseconds(ZOTAC_BLACKWELL_GPU_DELAY_US)); + } +} + +void ZotacBlackwellGPUController::Commit() +{ + /*---------------------------------------------------------*\ + | Write commit register to apply staged changes. | + | A longer delay is needed after commit to allow the | + | firmware to process the change — without this, some | + | effects don't visually update until a parameter changes. | + \*---------------------------------------------------------*/ + bus->i2c_smbus_write_byte_data(dev, ZOTAC_BLACKWELL_GPU_REG_COMMIT, 0x01); + std::this_thread::sleep_for(std::chrono::microseconds(ZOTAC_BLACKWELL_GPU_COMMIT_DELAY_US)); +} diff --git a/Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUController.h b/Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUController.h new file mode 100644 index 0000000..3b4f45f --- /dev/null +++ b/Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUController.h @@ -0,0 +1,101 @@ +/*---------------------------------------------------------*\ +| ZotacBlackwellGPUController.h | +| | +| Driver for ZOTAC Blackwell (RTX 50 series) GPU | +| | +| Eder Sánchez 27 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +/*---------------------------------------------------------*\ +| ZOTAC Blackwell I2C address | +\*---------------------------------------------------------*/ +#define ZOTAC_BLACKWELL_GPU_ADDR 0x4B + +/*---------------------------------------------------------*\ +| Register map (0x20 - 0x2F) | +\*---------------------------------------------------------*/ +#define ZOTAC_BLACKWELL_GPU_REG_FIXED 0x20 +#define ZOTAC_BLACKWELL_GPU_REG_ZONE 0x21 +#define ZOTAC_BLACKWELL_GPU_REG_MODE 0x22 +#define ZOTAC_BLACKWELL_GPU_REG_RED1 0x23 +#define ZOTAC_BLACKWELL_GPU_REG_GREEN1 0x24 +#define ZOTAC_BLACKWELL_GPU_REG_BLUE1 0x25 +#define ZOTAC_BLACKWELL_GPU_REG_RED2 0x26 +#define ZOTAC_BLACKWELL_GPU_REG_GREEN2 0x27 +#define ZOTAC_BLACKWELL_GPU_REG_BLUE2 0x28 +#define ZOTAC_BLACKWELL_GPU_REG_BRIGHTNESS 0x29 +#define ZOTAC_BLACKWELL_GPU_REG_SPEED 0x2A +#define ZOTAC_BLACKWELL_GPU_REG_DIRECTION 0x2B +#define ZOTAC_BLACKWELL_GPU_REG_RESERVED_2C 0x2C +#define ZOTAC_BLACKWELL_GPU_REG_RESERVED_2D 0x2D +#define ZOTAC_BLACKWELL_GPU_REG_RESERVED_2E 0x2E +#define ZOTAC_BLACKWELL_GPU_REG_RESERVED_2F 0x2F + +/*---------------------------------------------------------*\ +| Control registers | +\*---------------------------------------------------------*/ +#define ZOTAC_BLACKWELL_GPU_REG_RELOAD 0x11 +#define ZOTAC_BLACKWELL_GPU_REG_COMMIT 0x17 + +/*---------------------------------------------------------*\ +| Mode values (from Firestorm V5.0.0.012E reverse eng.) | +\*---------------------------------------------------------*/ +#define ZOTAC_BLACKWELL_GPU_MODE_STATIC 0x01 +#define ZOTAC_BLACKWELL_GPU_MODE_BREATHE 0x02 +#define ZOTAC_BLACKWELL_GPU_MODE_FADE 0x03 +#define ZOTAC_BLACKWELL_GPU_MODE_WINK 0x04 +#define ZOTAC_BLACKWELL_GPU_MODE_GLIDE 0x08 +#define ZOTAC_BLACKWELL_GPU_MODE_PRISM 0x09 +#define ZOTAC_BLACKWELL_GPU_MODE_BOKEH 0x0A +#define ZOTAC_BLACKWELL_GPU_MODE_BEACON 0x0B +#define ZOTAC_BLACKWELL_GPU_MODE_TANDEM 0x18 +#define ZOTAC_BLACKWELL_GPU_MODE_TIDAL 0x19 +#define ZOTAC_BLACKWELL_GPU_MODE_ASTRA 0x20 +#define ZOTAC_BLACKWELL_GPU_MODE_COSMIC 0x21 +#define ZOTAC_BLACKWELL_GPU_MODE_VOLTA 0x22 + +/*---------------------------------------------------------*\ +| Direction values | +\*---------------------------------------------------------*/ +#define ZOTAC_BLACKWELL_GPU_DIR_LEFT 0x00 +#define ZOTAC_BLACKWELL_GPU_DIR_RIGHT 0x01 + +/*---------------------------------------------------------*\ +| I2C transaction delays (microseconds) | +\*---------------------------------------------------------*/ +#define ZOTAC_BLACKWELL_GPU_DELAY_US 3000 +#define ZOTAC_BLACKWELL_GPU_COMMIT_DELAY_US 10000 + +class ZotacBlackwellGPUController +{ +public: + ZotacBlackwellGPUController(i2c_smbus_interface* bus, u8 dev, std::string dev_name); + ~ZotacBlackwellGPUController(); + + std::string GetDeviceLocation(); + std::string GetName(); + std::string GetVersion(); + + void SetMode(unsigned int zone, unsigned int mode_val, + RGBColor color1, RGBColor color2, + unsigned int brightness, unsigned int speed, + unsigned int direction); + void Commit(); + +private: + i2c_smbus_interface* bus; + u8 dev; + std::string name; + std::string version; + + void ReadVersion(); +}; diff --git a/Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUControllerDetect.cpp b/Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUControllerDetect.cpp new file mode 100644 index 0000000..dc6d29f --- /dev/null +++ b/Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUControllerDetect.cpp @@ -0,0 +1,46 @@ +/*---------------------------------------------------------*\ +| ZotacBlackwellGPUControllerDetect.cpp | +| | +| Detector for ZOTAC Blackwell (RTX 50 series) GPU | +| | +| Eder Sánchez 27 Mar 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ZotacBlackwellGPUController.h" +#include "RGBController_ZotacBlackwellGPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* DetectZotacBlackwellGPUControllersPCI * +* * +* Detect ZOTAC Blackwell (RTX 50 series) RGB controllers on the enumerated * +* I2C busses at address 0x4B. Zone configuration is resolved inside the * +* RGBController by looking up the PCI device/sub-device IDs in a static table. * +* * +* bus - pointer to i2c_smbus_interface where RGB device is connected * +* dev - I2C address of RGB device * +* * +\******************************************************************************************/ +void DetectZotacBlackwellGPUControllersPCI(i2c_smbus_interface* bus, u8 i2c_addr, const std::string& name) +{ + s32 result = bus->i2c_smbus_read_byte_data(i2c_addr, 0x10); + + if(result >= 0) + { + ZotacBlackwellGPUController* controller = new ZotacBlackwellGPUController(bus, i2c_addr, name); + RGBController_ZotacBlackwellGPU* rgb_controller = new RGBController_ZotacBlackwellGPU(controller, + bus->pci_device, + bus->pci_subsystem_device); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 5080 AMP Extreme INFINITY", DetectZotacBlackwellGPUControllersPCI, NVIDIA_VEN, NVIDIA_RTX5080_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX5080_AMP_EXTREME_SUB_DEV, 0x4B); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 5090 SOLID OC", DetectZotacBlackwellGPUControllersPCI, NVIDIA_VEN, NVIDIA_RTX5090_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX5090_SOLID_OC_SUB_DEV, 0x4B); diff --git a/Controllers/ZotacTuringGPUController/RGBController_ZotacTuringGPU.cpp b/Controllers/ZotacTuringGPUController/RGBController_ZotacTuringGPU.cpp new file mode 100644 index 0000000..c71ec98 --- /dev/null +++ b/Controllers/ZotacTuringGPUController/RGBController_ZotacTuringGPU.cpp @@ -0,0 +1,156 @@ +/*---------------------------------------------------------*\ +| RGBController_ZotacTuringGPU.cpp | +| | +| RGBController for Zotac Turing GPU | +| | +| David Henry 07 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_ZotacTuringGPU.h" + +/**------------------------------------------------------------------*\ + @name ZOTAC Turing GPU + @category GPU + @type I2C + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectZotacTuringGPUControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_ZotacTuringGPU::RGBController_ZotacTuringGPU(ZotacTuringGPUController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "ZOTAC"; + description = "ZOTAC Turing-based RGB GPU Device"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + mode Direct; + Direct.name = "Direct"; + Direct.value = ZOTAC_GPU_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Flashing; + Flashing.name = "Flashing"; + Flashing.value = ZOTAC_GPU_MODE_STROBE; + Flashing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Flashing.speed_min = ZOTAC_GPU_SPEED_SLOWEST; + Flashing.speed_max = ZOTAC_GPU_SPEED_FASTEST; + Flashing.speed = ZOTAC_GPU_SPEED_NORMAL; + Flashing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Flashing); + + mode Wave; + Wave.name = "Rainbow Wave"; + Wave.value = ZOTAC_GPU_MODE_WAVE; + Wave.flags = MODE_FLAG_HAS_SPEED; + Wave.speed_min = ZOTAC_GPU_SPEED_SLOWEST; + Wave.speed_max = ZOTAC_GPU_SPEED_FASTEST; + Wave.speed = ZOTAC_GPU_SPEED_NORMAL; + Wave.color_mode = MODE_COLORS_NONE; + modes.push_back(Wave); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = ZOTAC_GPU_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_PER_LED_COLOR; + Breathing.speed_min = ZOTAC_GPU_SPEED_SLOWEST; + Breathing.speed_max = ZOTAC_GPU_SPEED_FASTEST; + Breathing.speed = ZOTAC_GPU_SPEED_NORMAL; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode ColorCycle; + ColorCycle.name = "Spectrum Cycle"; + ColorCycle.value = ZOTAC_GPU_MODE_COLOR_CYCLE; + ColorCycle.flags = MODE_FLAG_HAS_SPEED; + ColorCycle.speed_min = ZOTAC_GPU_SPEED_SLOWEST; + ColorCycle.speed_max = ZOTAC_GPU_SPEED_FASTEST; + ColorCycle.speed = ZOTAC_GPU_SPEED_NORMAL; + ColorCycle.color_mode = MODE_COLORS_NONE; + modes.push_back(ColorCycle); + + SetupZones(); +} + +RGBController_ZotacTuringGPU::~RGBController_ZotacTuringGPU() +{ + delete controller; +} + +void RGBController_ZotacTuringGPU::SetupZones() +{ + /*---------------------------------------------------------*\ + | This device only has one LED, so create a single zone and | + | LED for it | + \*---------------------------------------------------------*/ + zone* new_zone = new zone(); + led* new_led = new led(); + + new_zone->name = "GPU Zone"; + new_zone->type = ZONE_TYPE_SINGLE; + new_zone->leds_min = 1; + new_zone->leds_max = 1; + new_zone->leds_count = 1; + new_zone->matrix_map = NULL; + + new_led->name = "GPU LED"; + + /*---------------------------------------------------------*\ + | Push the zone and LED on to device vectors | + \*---------------------------------------------------------*/ + leds.push_back(*new_led); + zones.push_back(*new_zone); + + SetupColors(); + SetupInitialValues(); +} + +void RGBController_ZotacTuringGPU::SetupInitialValues() +{ + /*---------------------------------------------------------*\ + | Retrieve current values by reading the device | + \*---------------------------------------------------------*/ + unsigned int speed; + + controller->GetMode(colors[0], active_mode, speed); + modes[active_mode].speed = speed; + + SignalUpdate(); +} + +void RGBController_ZotacTuringGPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ZotacTuringGPU::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_ZotacTuringGPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_ZotacTuringGPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_ZotacTuringGPU::DeviceUpdateMode() +{ + controller->SetMode(colors[0], modes[active_mode].value, modes[active_mode].speed); +} diff --git a/Controllers/ZotacTuringGPUController/RGBController_ZotacTuringGPU.h b/Controllers/ZotacTuringGPUController/RGBController_ZotacTuringGPU.h new file mode 100644 index 0000000..533d8a5 --- /dev/null +++ b/Controllers/ZotacTuringGPUController/RGBController_ZotacTuringGPU.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_ZotacTuringGPU.h | +| | +| RGBController for Zotac Turing GPU | +| | +| David Henry 07 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ZotacTuringGPUController.h" + +class RGBController_ZotacTuringGPU : public RGBController +{ +public: + RGBController_ZotacTuringGPU(ZotacTuringGPUController* controller_ptr); + ~RGBController_ZotacTuringGPU(); + + void SetupInitialValues(); + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + ZotacTuringGPUController* controller; +}; diff --git a/Controllers/ZotacTuringGPUController/ZotacTuringGPUController.cpp b/Controllers/ZotacTuringGPUController/ZotacTuringGPUController.cpp new file mode 100644 index 0000000..01fe6bb --- /dev/null +++ b/Controllers/ZotacTuringGPUController/ZotacTuringGPUController.cpp @@ -0,0 +1,74 @@ +/*---------------------------------------------------------*\ +| ZotacTuringGPUController.cpp | +| | +| Driver for Zotac Turing GPU | +| | +| David Henry 07 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "ZotacTuringGPUController.h" + +ZotacTuringGPUController::ZotacTuringGPUController(i2c_smbus_interface* bus, u8 dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; +} + +ZotacTuringGPUController::~ZotacTuringGPUController() +{ +} + +std::string ZotacTuringGPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return("I2C: " + return_string); +} + +std::string ZotacTuringGPUController::GetDeviceName() +{ + return(name); +} + +void ZotacTuringGPUController::GetMode(RGBColor& color, int& mode, unsigned int& speed) +{ + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + + if(bus->i2c_read_block(dev, &rdata_len, rdata_pkt) >= 0) + { + mode = rdata_pkt[0]; + color = ToRGBColor(rdata_pkt[1], rdata_pkt[2], rdata_pkt[3]); + speed = rdata_pkt[5]; + } +} + +void ZotacTuringGPUController::SetMode(RGBColor color, int mode, unsigned int speed) +{ + u8 data_pkt[] = + { + ZOTAC_TURING_GPU_REG_COLOR_AND_MODE, + 0x00, // Is it some zone index? + (u8)mode, + (u8)RGBGetRValue(color), + (u8)RGBGetGValue(color), + (u8)RGBGetBValue(color), + 0x00, + (u8)speed + }; + + bus->i2c_write_block(dev, sizeof(data_pkt), data_pkt); + + /*---------------------------------------------------------*\ + | Read back color and mode. Not doing this seems to hang | + | the RGB controller device when switching mode... | + \*---------------------------------------------------------*/ + GetMode(color, mode, speed); +} diff --git a/Controllers/ZotacTuringGPUController/ZotacTuringGPUController.h b/Controllers/ZotacTuringGPUController/ZotacTuringGPUController.h new file mode 100644 index 0000000..305aa19 --- /dev/null +++ b/Controllers/ZotacTuringGPUController/ZotacTuringGPUController.h @@ -0,0 +1,56 @@ +/*---------------------------------------------------------*\ +| ZotacTuringGPUController.h | +| | +| Driver for Zotac Turing GPU | +| | +| David Henry 07 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +enum +{ + ZOTAC_TURING_GPU_REG_COLOR_AND_MODE = 0xA0, +}; + +enum +{ + ZOTAC_GPU_MODE_STATIC = 0x00, + ZOTAC_GPU_MODE_STROBE = 0x01, + ZOTAC_GPU_MODE_WAVE = 0x02, + ZOTAC_GPU_MODE_BREATHING = 0x03, + ZOTAC_GPU_MODE_COLOR_CYCLE = 0x04, +}; + +enum +{ + ZOTAC_GPU_SPEED_SLOWEST = 0x09, + ZOTAC_GPU_SPEED_NORMAL = 0x04, + ZOTAC_GPU_SPEED_FASTEST = 0x00 +}; + +class ZotacTuringGPUController +{ +public: + ZotacTuringGPUController(i2c_smbus_interface* bus, u8 dev, std::string dev_name); + ~ZotacTuringGPUController(); + + std::string GetDeviceLocation(); + std::string GetDeviceName(); + + void GetMode(RGBColor& color, int& mode, unsigned int& speed); + void SetMode(RGBColor color, int mode, unsigned int speed); + +private: + i2c_smbus_interface* bus; + u8 dev; + std::string name; + +}; diff --git a/Controllers/ZotacTuringGPUController/ZotacTuringGPUControllerDetect.cpp b/Controllers/ZotacTuringGPUController/ZotacTuringGPUControllerDetect.cpp new file mode 100644 index 0000000..7eaba3e --- /dev/null +++ b/Controllers/ZotacTuringGPUController/ZotacTuringGPUControllerDetect.cpp @@ -0,0 +1,64 @@ +/*---------------------------------------------------------*\ +| ZotacTuringGPUControllerDetect.cpp | +| | +| Detector for Zotac Turing GPU | +| | +| David Henry 07 Jan 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ZotacTuringGPUController.h" +#include "RGBController_ZotacTuringGPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* TestForZotacTuringGPUController * +* * +* Tests the given address to see if an RGB controller exists there. * +* * +\******************************************************************************************/ + +bool TestForZotacTuringGPUController(i2c_smbus_interface* bus, u8 i2c_addr) +{ + /*---------------------------------------------------------*\ + | This command seems to enable the RGB controller (0xF1, | + | 0x00 disables it). | + | Not really sure it's mandatory, but we can still use it | + | for testing the device: if the command succeeds, assume | + | it's a valid device. | + \*---------------------------------------------------------*/ + u8 data_pkt[] = { ZOTAC_TURING_GPU_REG_COLOR_AND_MODE, 0xF1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return (bus->i2c_write_block(i2c_addr, sizeof(data_pkt), data_pkt) >= 0); +} + +/******************************************************************************************\ +* * +* DetectZotacTuringGPUControllers * +* * +* Detect ZOTAC Turing RGB controllers on the enumerated I2C busses at address 0x49. * +* * +* bus - pointer to i2c_smbus_interface where RGB device is connected * +* dev - I2C address of RGB device * +* * +\******************************************************************************************/ + +void DetectZotacTuringGPUControllers(i2c_smbus_interface* bus, u8 i2c_addr, const std::string& name) +{ + if(TestForZotacTuringGPUController(bus, i2c_addr)) + { + ZotacTuringGPUController* controller = new ZotacTuringGPUController(bus, i2c_addr, name); + RGBController_ZotacTuringGPU* rgb_controller = new RGBController_ZotacTuringGPU(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 2070 SUPER Twin Fan", DetectZotacTuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2070S_OC_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX2070S_GAMING_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 2080 SUPER Twin Fan", DetectZotacTuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080S_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX2080S_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 2080 AMP", DetectZotacTuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080_A_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX2080_AMP_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 2080 Ti AMP", DetectZotacTuringGPUControllers, NVIDIA_VEN, NVIDIA_RTX2080TI_A_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX2080_AMP_TI_SUB_DEV, 0x49); diff --git a/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp b/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp new file mode 100644 index 0000000..9958522 --- /dev/null +++ b/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp @@ -0,0 +1,514 @@ +/*---------------------------------------------------------*\ +| RGBController_ZotacV2GPU.cpp | +| | +| RGBController for Zotac V2 GPU | +| | +| Krzysztof Haładyn (krzys_h) 16 Mar 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController_ZotacV2GPU.h" +#include "LogManager.h" + +std::map ZOTAC_V2_GPU_CONFIG = +{ + { "N653E-1013", { 2, false } }, // ZOTAC GAMING GeForce RTX 3070 Ti Trinity OC + { "N653A-1013", { 1, false } }, // ZOTAC GAMING GeForce RTX 3070 Ti AMP Holo + { "N612E-1011", { 2, false } }, // ZOTAC GAMING GeForce RTX 3080 Trinity OC LHR 12GB & 3090 Trinity & 3070 Ti + { "N612A-1012", { 2, false } }, // ZOTAC GAMING GeForce RTX 3080 Ti AMP Holo + { "N617E-1011", { 3, false } }, // ZOTAC GAMING GeForce RTX 3070 AMP Holo LHR + { "N618E-1013", { 4, true } }, // ZOTAC GAMING GeForce RTX 3090 AMP Core Holo + { "N618A-1015", { 4, true } }, // ZOTAC GAMING GeForce RTX 3090 AMP Extreme Holo + { "N696E-1040", { 1, false } }, // ZOTAC GAMING GeForce RTX 4070 Ti Trinity OC + { "N675E-1019", { 1, true } }, // ZOTAC GAMING GeForce RTX 4090 Trinity OC + { "N675E-1062", { 1, true } }, // ZOTAC GAMING GeForce RTX 4090 Trinity OC Alternate Controller Version + { "N675A-1019", { 5, true } }, // ZOTAC GAMING GeForce RTX 4080 16GB AMP Extreme AIRO + { "N675A-1062", { 5, true } }, // ZOTAC GAMING GeForce RTX 4090 AMP Extreme AIRO +}; + +std::vector> ZOTAC_V2_GPU_DUET_PRESETS = +{ + { ToRGBColor(0x32, 0xCF, 0xA7), ToRGBColor(0x93, 0x34, 0xC2) }, + { ToRGBColor(0x00, 0xC9, 0x14), ToRGBColor(0x00, 0x20, 0xF5) }, + { ToRGBColor(0xD1, 0xFC, 0x00), ToRGBColor(0xF1, 0x0C, 0x00) }, + { ToRGBColor(0xFF, 0x68, 0x7C), ToRGBColor(0xD4, 0x00, 0x4D) }, +}; + +/**------------------------------------------------------------------*\ + @name ZOTAC 30/40 series GPU + @category GPU + @type I2C + @save :robot: + @direct :x: + @effects :tools: + @detectors DetectZotacV2GPUControllers + @comment + OpenRGB does not support per-zone effect modes, so only + the synchronized mode is supported for now. Sound based + effects are not supported. Idle/active config is not + supported. + + To add new cards, in addition to entries in `pci_ids/pci_ids.h` + and `Controllers/ZotacV2GPUController/ZotacV2GPUControllerDetect.cpp` + an entry associating the controller version with the LED + configuration must be added to the [ZotacV2GPUConfig map](https://gitlab.com/CalcProgrammer1/OpenRGB/-/blob/master/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp?ref_type=heads#L14) in + `Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp`. + + Controller version is identified by polling the controller on address + 0x49 (so far for all known cards) at register 0xA0 with the following + packet `0xF1 0x00 0x00 0x00 0x00 0x00 0x00` + + The first ten bytes when read back from that address are the version, + this must be converted into Unicode. + + The polling and reading is done by the ZOTAC Firestorm app on startup + and can be monitored using NvAPIspy on Windows. + + For RTX 3080 Trinity OC LHR 12GB the relevant NvAPISpy log entries are: + + > ```plaintext + > NvAPI_I2CWrite: Dev: 0x49 RegSize: 0x00 Reg: Size: 0x08 Data: 0xA0 0xF1 0x00 0x00 0x00 0x00 0x00 0x00 + > NvAPI_I2CRead: Dev: 0x49 RegSize: 0x01 Reg: 0xA0 Size: 0x20 Data: 0x4E 0x36 0x31 0x32 0x45 0x2D 0x31 0x30 0x31 0x31 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + > ``` + + The read data `0x4E 0x36 0x31 0x32 0x45 0x2D 0x31 0x30 0x31 0x31` converts + to Unicode `N612E-1011` + + Cards with correct entries in `pci_ids/pci_ids.h` and `Controllers/ZotacV2GPUController/ZotacV2GPUControllerDetect.cpp` + but not in `Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp` + should produce a log error with the controller version read from the card. + + The LED configuration is the number of independently configurable + zones followed by a bool representing support for external led stip. + These can be determined by looking at the Spectra tab in Firestorm. +\*-------------------------------------------------------------------*/ + +RGBController_ZotacV2GPU::RGBController_ZotacV2GPU(ZotacV2GPUController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "ZOTAC"; + description = "ZOTAC 30/40 series RGB GPU Device (" + controller->GetVersion() + ")"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + if(ZOTAC_V2_GPU_CONFIG.count(controller->GetVersion()) > 0) + { + config = ZOTAC_V2_GPU_CONFIG.at(controller->GetVersion()); + } + else + { + LOG_ERROR("[%s] Unrecognized controller version %s", name.c_str(), controller->GetVersion().c_str()); + config = { 0, false }; + } + + + version += std::to_string(config.numberOfZones) + " zones, " + + (config.supportsExternalLEDStrip ? "with" : "without") + " external LED strip support"; + + mode STATIC; + STATIC.name = "Static"; + STATIC.value = ZOTAC_V2_GPU_MODE_STATIC; + STATIC.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + STATIC.brightness_min = 0; + STATIC.brightness_max = 100; + STATIC.brightness = 100; + STATIC.color_mode = MODE_COLORS_MODE_SPECIFIC; + STATIC.colors_min = 1; + STATIC.colors_max = 1; + STATIC.colors.resize(1); + STATIC.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(STATIC); + + mode BREATH; + BREATH.name = "Breath"; + BREATH.value = ZOTAC_V2_GPU_MODE_BREATH; + BREATH.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + BREATH.brightness_min = 0; + BREATH.brightness_max = 100; + BREATH.brightness = 100; + BREATH.speed_min = 0; + BREATH.speed_max = 100; + BREATH.speed = 20; + BREATH.color_mode = MODE_COLORS_MODE_SPECIFIC; + BREATH.colors_min = 1; + BREATH.colors_max = 1; + BREATH.colors.resize(1); + BREATH.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(BREATH); + + mode FADE; + FADE.name = "Fade"; + FADE.value = ZOTAC_V2_GPU_MODE_FADE; + FADE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + FADE.speed_min = 0; + FADE.speed_max = 100; + FADE.speed = 20; + FADE.color_mode = MODE_COLORS_NONE; + modes.push_back(FADE); + + mode WINK; + WINK.name = "Wink"; + WINK.value = ZOTAC_V2_GPU_MODE_WINK; + WINK.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + WINK.brightness_min = 0; + WINK.brightness_max = 100; + WINK.brightness = 100; + WINK.speed_min = 0; + WINK.speed_max = 100; + WINK.speed = 20; + WINK.color_mode = MODE_COLORS_MODE_SPECIFIC; + WINK.colors_min = 1; + WINK.colors_max = 1; + WINK.colors.resize(1); + WINK.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(WINK); + + if(config.numberOfZones > 1) + { + // This mode is only supported on GPUs with more than one zone, + // because it spans multiple zones. + + // It's also supported in synchronized mode only (which is the only + // thing this RGBController supports for now anyway) + + mode FLASH; + FLASH.name = "Flash"; + FLASH.value = ZOTAC_V2_GPU_MODE_FLASH; + FLASH.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + FLASH.speed_min = 0; + FLASH.speed_max = 100; + FLASH.speed = 20; + FLASH.color_mode = MODE_COLORS_NONE; + modes.push_back(FLASH); + } + + // (Sound activated - not supported) + //mode SHINE; + //SHINE.name = "Shine"; + //SHINE.value = ZOTAC_V2_GPU_MODE_SHINE; + //SHINE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + //SHINE.brightness_min = 0; + //SHINE.brightness_max = 100; + //SHINE.brightness = 100; + //SHINE.color_mode = MODE_COLORS_MODE_SPECIFIC; + //SHINE.colors_min = 1; + //SHINE.colors_max = 1; + //SHINE.colors.resize(1); + //SHINE.colors[0] = ToRGBColor(0, 0, 255); + //modes.push_back(SHINE); + + mode RANDOM; + RANDOM.name = "Random"; + RANDOM.value = ZOTAC_V2_GPU_MODE_RANDOM; + RANDOM.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + RANDOM.brightness_min = 0; + RANDOM.brightness_max = 100; + RANDOM.brightness = 100; + RANDOM.speed_min = 0; + RANDOM.speed_max = 100; + RANDOM.speed = 20; + RANDOM.color_mode = MODE_COLORS_MODE_SPECIFIC; + RANDOM.colors_min = 1; + RANDOM.colors_max = 1; + RANDOM.colors.resize(1); + RANDOM.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(RANDOM); + + mode SLIDE; + SLIDE.name = "Slide"; + SLIDE.value = ZOTAC_V2_GPU_MODE_SLIDE; + SLIDE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + SLIDE.brightness_min = 0; + SLIDE.brightness_max = 100; + SLIDE.brightness = 100; + SLIDE.speed_min = 0; + SLIDE.speed_max = 100; + SLIDE.speed = 20; + SLIDE.color_mode = MODE_COLORS_MODE_SPECIFIC; + SLIDE.colors_min = 1; + SLIDE.colors_max = 1; + SLIDE.colors.resize(1); + SLIDE.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(SLIDE); + + mode RAINBOW; + RAINBOW.name = "Rainbow"; + RAINBOW.value = ZOTAC_V2_GPU_MODE_RAINBOW; + RAINBOW.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RAINBOW.speed_min = 0; + RAINBOW.speed_max = 100; + RAINBOW.speed = 20; + RAINBOW.color_mode = MODE_COLORS_NONE; + modes.push_back(RAINBOW); + mode RAINBOW_CIRCUIT = RAINBOW; + RAINBOW_CIRCUIT.name = "Rainbow (circuit)"; + modes.push_back(RAINBOW_CIRCUIT); + + mode MARQUEE; + MARQUEE.name = "Marquee"; + MARQUEE.value = ZOTAC_V2_GPU_MODE_MARQUEE; + MARQUEE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + MARQUEE.brightness_min = 0; + MARQUEE.brightness_max = 100; + MARQUEE.brightness = 100; + MARQUEE.speed_min = 0; + MARQUEE.speed_max = 100; + MARQUEE.speed = 20; + MARQUEE.color_mode = MODE_COLORS_MODE_SPECIFIC; + MARQUEE.colors_min = 1; + MARQUEE.colors_max = 1; + MARQUEE.colors.resize(1); + MARQUEE.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(MARQUEE); + mode MARQUEE_CIRCUIT = MARQUEE; + MARQUEE_CIRCUIT.name = "Marquee (circuit)"; + modes.push_back(MARQUEE_CIRCUIT); + + mode DRIP; + DRIP.name = "Drip"; + DRIP.value = ZOTAC_V2_GPU_MODE_DRIP; + DRIP.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + DRIP.brightness_min = 0; + DRIP.brightness_max = 100; + DRIP.brightness = 100; + DRIP.speed_min = 0; + DRIP.speed_max = 100; + DRIP.speed = 20; + DRIP.color_mode = MODE_COLORS_MODE_SPECIFIC; + DRIP.colors_min = 1; + DRIP.colors_max = 1; + DRIP.colors.resize(1); + DRIP.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(DRIP); + mode DRIP_CIRCUIT = DRIP; + DRIP_CIRCUIT.name = "Drip (circuit)"; + modes.push_back(DRIP_CIRCUIT); + + // (Sound activated - not supported) + //mode DANCE; + //DANCE.name = "Dance (sound activated)"; + //DANCE.value = ZOTAC_V2_GPU_MODE_DANCE; + //DANCE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + //DANCE.brightness_min = 0; + //DANCE.brightness_max = 100; + //DANCE.brightness = 100; + //DANCE.color_mode = MODE_COLORS_MODE_SPECIFIC; + //DANCE.colors_min = 1; + //DANCE.colors_max = 1; + //DANCE.colors.resize(1); + //DANCE.colors[0] = ToRGBColor(0, 0, 255); + //modes.push_back(DANCE); + + mode DUET; + DUET.name = "Duet"; + DUET.value = ZOTAC_V2_GPU_MODE_DUET; + DUET.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + DUET.speed_min = 0; + DUET.speed_max = 100; + DUET.speed = 20; + DUET.color_mode = MODE_COLORS_MODE_SPECIFIC; + DUET.colors_min = 2; + DUET.colors_max = 2; + DUET.colors.resize(2); + DUET.colors[0] = ZOTAC_V2_GPU_DUET_PRESETS[1].first; + DUET.colors[1] = ZOTAC_V2_GPU_DUET_PRESETS[1].second; + modes.push_back(DUET); + mode DUET_CIRCUIT = DUET; + DUET_CIRCUIT.name = "Duet (circuit)"; + modes.push_back(DUET_CIRCUIT); + + + mode PATH; + PATH.name = "Path"; + PATH.value = ZOTAC_V2_GPU_MODE_PATH; + PATH.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + PATH.brightness_min = 0; + PATH.brightness_max = 100; + PATH.brightness = 100; + PATH.speed_min = 0; + PATH.speed_max = 100; + PATH.speed = 20; + PATH.color_mode = MODE_COLORS_NONE; + modes.push_back(PATH); + + SetupZones(); +} + +RGBController_ZotacV2GPU::~RGBController_ZotacV2GPU() +{ + delete controller; +} + +void RGBController_ZotacV2GPU::SetupZones() +{ + led new_led; + new_led.name = "GPU LED"; + leds.push_back(new_led); + + zone new_zone; + new_zone.name = "GPU Zone"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + SetupColors(); + SetupInitialValues(); +} + +void RGBController_ZotacV2GPU::SetupInitialValues() +{ + /*---------------------------------------------------------*\ + | Retrieve current values by reading the device | + \*---------------------------------------------------------*/ + + bool on; + int syncMode; + ZotacV2GPUZone zoneConfig; + + // We don't support anything other than synchronized mode, so read the last + // config used in synchronized mode for idle settings. + int zoneNum = FindSynchronizedZoneNum(ZOTAC_V2_GPU_SYNC_SYNCHRONIZED); + if(!controller->GetMode(zoneNum, ZOTAC_V2_GPU_CONFIG_IDLE, syncMode, zoneConfig, on)) + { + return; + } + + for(unsigned int i = 0; i < modes.size(); ++i) + { + if(zoneConfig.mode != modes[i].value) + { + continue; + } + + if(zoneConfig.mode == ZOTAC_V2_GPU_MODE_RAINBOW || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_MARQUEE || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_DRIP || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_DUET) + { + if((zoneConfig.circuit == ZOTAC_V2_GPU_CIRCUIT_ON) != (modes[i].name.find("(circuit)") != std::string::npos)) + { + continue; + } + } + + active_mode = i; + } + + colors[0] = zoneConfig.color1; + if(modes[active_mode].colors.size() >= 1) + { + modes[active_mode].colors[0] = zoneConfig.color1; + } + if(modes[active_mode].colors.size() >= 2) + { + modes[active_mode].colors[1] = zoneConfig.color2; + } + modes[active_mode].speed = zoneConfig.speed; + modes[active_mode].brightness = zoneConfig.brightness; + modes[active_mode].direction = zoneConfig.direction; + + SignalUpdate(); +} + +void RGBController_ZotacV2GPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ZotacV2GPU::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_ZotacV2GPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_ZotacV2GPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_ZotacV2GPU::DeviceUpdateMode() +{ + ZotacV2GPUZone zoneConfig; + zoneConfig.mode = modes[active_mode].value; + + zoneConfig.color1 = modes[active_mode].colors.size() >= 1 ? modes[active_mode].colors[0] : ToRGBColor(0, 0, 0); + zoneConfig.color2 = modes[active_mode].colors.size() >= 2 ? modes[active_mode].colors[1] : ToRGBColor(0, 0, 0); + + // This is probably not strictly neccessary + zoneConfig.colorPreset = 0; + if(zoneConfig.mode == ZOTAC_V2_GPU_MODE_DUET) + { + zoneConfig.colorPreset = (unsigned int)ZOTAC_V2_GPU_DUET_PRESETS.size(); // custom + for(size_t i = 0; i < ZOTAC_V2_GPU_DUET_PRESETS.size(); ++i) + { + if(zoneConfig.color1 == ZOTAC_V2_GPU_DUET_PRESETS[i].first && + zoneConfig.color2 == ZOTAC_V2_GPU_DUET_PRESETS[i].second) + { + zoneConfig.colorPreset = (unsigned int)i; + } + } + } + + zoneConfig.speed = modes[active_mode].speed; + zoneConfig.brightness = modes[active_mode].brightness; + zoneConfig.direction = modes[active_mode].direction == MODE_DIRECTION_RIGHT ? ZOTAC_V2_GPU_DIR_RIGHT : ZOTAC_V2_GPU_DIR_LEFT; + + if(zoneConfig.mode == ZOTAC_V2_GPU_MODE_RAINBOW || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_MARQUEE || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_DRIP || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_DUET) + { + zoneConfig.circuit = modes[active_mode].name.find("(circuit)") != std::string::npos ? ZOTAC_V2_GPU_CIRCUIT_ON : ZOTAC_V2_GPU_CIRCUIT_OFF; + } + else + { + zoneConfig.circuit = 0; + } + + int zoneNum = FindSynchronizedZoneNum(ZOTAC_V2_GPU_SYNC_SYNCHRONIZED); + controller->TurnOnOff(true); + controller->SetMode(zoneNum, ZOTAC_V2_GPU_CONFIG_IDLE, ZOTAC_V2_GPU_SYNC_SYNCHRONIZED, zoneConfig); + controller->SetMode(zoneNum, ZOTAC_V2_GPU_CONFIG_ACTIVE, ZOTAC_V2_GPU_SYNC_SYNCHRONIZED, zoneConfig); +} + +int RGBController_ZotacV2GPU::FindSynchronizedZoneNum(int syncMode) +{ + // Figure out the index of the zone used for ZOTAC_V2_GPU_SYNC_SYNCHRONIZED + // or ZOTAC_V2_GPU_SYNC_SYNCHRONIZED_WITH_EXTERNAL settings based on the GPU + // zone config + + int lastRealZone = config.numberOfZones - 1; + if(config.supportsExternalLEDStrip) + { + lastRealZone += 1; + } + + if(syncMode == ZOTAC_V2_GPU_SYNC_SYNCHRONIZED) + { + return lastRealZone + 1; + } + else if(syncMode == ZOTAC_V2_GPU_SYNC_SYNCHRONIZED_WITH_EXTERNAL) + { + assert(config.supportsExternalLEDStrip); + return lastRealZone + 2; + } + else + { + assert(false); + return 0; + } +} diff --git a/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.h b/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.h new file mode 100644 index 0000000..6540df3 --- /dev/null +++ b/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.h @@ -0,0 +1,40 @@ +/*---------------------------------------------------------*\ +| RGBController_ZotacV2GPU.h | +| | +| RGBController for Zotac V2 GPU | +| | +| Krzysztof Haładyn (krzys_h) 16 Mar 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ZotacV2GPUController.h" + +class RGBController_ZotacV2GPU : public RGBController +{ +public: + RGBController_ZotacV2GPU(ZotacV2GPUController* controller_ptr); + ~RGBController_ZotacV2GPU(); + + void SetupInitialValues(); + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + ZotacV2GPUConfig config; + +private: + ZotacV2GPUController* controller; + + int FindSynchronizedZoneNum(int syncMode); +}; diff --git a/Controllers/ZotacV2GPUController/ZotacV2GPUController.cpp b/Controllers/ZotacV2GPUController/ZotacV2GPUController.cpp new file mode 100644 index 0000000..9f30edd --- /dev/null +++ b/Controllers/ZotacV2GPUController/ZotacV2GPUController.cpp @@ -0,0 +1,203 @@ +/*---------------------------------------------------------*\ +| ZotacV2GPUController.cpp | +| | +| Driver for Zotac V2 GPU | +| | +| Krzysztof Haładyn (krzys_h) 16 Mar 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "ZotacV2GPUController.h" +#include "LogManager.h" + +ZotacV2GPUController::ZotacV2GPUController(i2c_smbus_interface* bus, u8 dev, std::string dev_name) +{ + this->bus = bus; + this->dev = dev; + this->name = dev_name; + + if(dev) + { + ReadVersion(); + } +} + +ZotacV2GPUController::~ZotacV2GPUController() +{ +} + +std::string ZotacV2GPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return ("I2C: " + return_string); +} + +std::string ZotacV2GPUController::GetName() +{ + return(name); +} + +std::string ZotacV2GPUController::GetVersion() +{ + return(version); +} + +bool ZotacV2GPUController::ReadVersion() +{ + u8 data_pkt[] = { ZOTAC_V2_GPU_REG_RGB, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + if(bus->i2c_write_block(dev, sizeof(data_pkt), data_pkt) < 0) + { + return false; + } + + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + if(bus->i2c_read_block(dev, &rdata_len, rdata_pkt) < 0) + { + return false; + } + + version = std::string((char*)rdata_pkt); + + return true; +} + +bool ZotacV2GPUController::TurnOnOff(bool on) +{ + return SendCommand(on, false, 0, 0, 0, ZotacV2GPUZone()); +} + +bool ZotacV2GPUController::ResetToDefaults() +{ + return SendCommand(true, true, 0, 0, 0, ZotacV2GPUZone()); +} + +bool ZotacV2GPUController::SetMode(int zone, int idleActive, int syncMode, ZotacV2GPUZone zoneConfig) +{ + // NOTE: This only works if the device is in the ON state. Otherwise, the SetMode command will behave + // like TurnOnOff(true), and the change will be ignored. + + // NOTE: syncMode is per idleActive, NOT per (zone, idleActive) pair like zoneConfig is - as in, + // you can have ACTIVE in INDIVIDUAL mode and IDLE in SYNCHRONIZED mode, but you can't have + // different syncModes in different zones. The last written value is always applied, so make + // sure you don't change it accidentally between writes. + // TODO: Verify what I said above - it doesn't match the GUI, but it seems to match the NvAPISpy traces + // From the GUI it seems like syncMode should be global, period. + + return SendCommand(true, false, zone, idleActive, syncMode, zoneConfig); +} + +bool ZotacV2GPUController::GetMode(int zone, int idleActive, int& syncMode, ZotacV2GPUZone& zoneConfig, bool& on) +{ + u8 data_pkt[] = + { + ZOTAC_V2_GPU_REG_RGB, + 0xF0, + 0x00, + 0x00, + 0x00, + (u8)idleActive, + (u8)zone, + 0x00, + }; + + if(bus->i2c_write_block(dev, sizeof(data_pkt), data_pkt) < 0) + { + return false; + } + + + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + if(bus->i2c_read_block(dev, &rdata_len, rdata_pkt) < 0) + { + return false; + } + + bool readReset; + int readZone; + int readIdleActive; + if(!ParseCommand(on, readReset, readZone, readIdleActive, syncMode, zoneConfig)) + { + return false; + } + + if(readReset != 0) + { + LOG_WARNING("Reset byte was not 0?!"); + } + + if(readZone != zone || readIdleActive != idleActive) + { + LOG_WARNING("Got unexpected data - expected to recieve data for (%d, %d) but got for (%d, %d)", zone, idleActive, readZone, readIdleActive); + return false; + } + + return true; +} + +bool ZotacV2GPUController::SendCommand(bool on, bool reset, int zone, int idleActive, int syncMode, ZotacV2GPUZone zoneConfig) +{ + u8 data_pkt[] = + { + ZOTAC_V2_GPU_REG_RGB, + on ? (u8)0x01 : (u8)0x00, + reset ? (u8)0x01 : (u8)0x00, + 0x00, + 0x00, + (u8)idleActive, + (u8)zone, + (u8)zoneConfig.mode, + (u8)RGBGetRValue(zoneConfig.color1), + (u8)RGBGetGValue(zoneConfig.color1), + (u8)RGBGetBValue(zoneConfig.color1), + (u8)zoneConfig.speed, + (u8)zoneConfig.brightness, + (u8)zoneConfig.direction, + 0x00, + (u8)syncMode, + (u8)zoneConfig.circuit, + (u8)RGBGetRValue(zoneConfig.color2), + (u8)RGBGetGValue(zoneConfig.color2), + (u8)RGBGetBValue(zoneConfig.color2), + (u8)zoneConfig.colorPreset, + }; + + if(bus->i2c_write_block(dev, sizeof(data_pkt), data_pkt) < 0) + { + return false; + } + return true; +} + +bool ZotacV2GPUController::ParseCommand(bool& on, bool& reset, int& zone, int& idleActive, int& syncMode, ZotacV2GPUZone& zoneConfig) +{ + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + + if(bus->i2c_read_block(dev, &rdata_len, rdata_pkt) < 0) + { + return false; + } + + on = rdata_pkt[0] != 0x00; + reset = rdata_pkt[1] != 0x00; + idleActive = rdata_pkt[4]; + zone = rdata_pkt[5]; + zoneConfig.mode = rdata_pkt[6]; + zoneConfig.color1 = ToRGBColor(rdata_pkt[7], rdata_pkt[8], rdata_pkt[9]); + zoneConfig.speed = rdata_pkt[10]; + zoneConfig.brightness = rdata_pkt[11]; + zoneConfig.direction = rdata_pkt[12]; + syncMode = rdata_pkt[14]; + zoneConfig.circuit = rdata_pkt[15]; + zoneConfig.color2 = ToRGBColor(rdata_pkt[16], rdata_pkt[17], rdata_pkt[18]); + zoneConfig.colorPreset = rdata_pkt[19]; + return true; +} diff --git a/Controllers/ZotacV2GPUController/ZotacV2GPUController.h b/Controllers/ZotacV2GPUController/ZotacV2GPUController.h new file mode 100644 index 0000000..b1d7c06 --- /dev/null +++ b/Controllers/ZotacV2GPUController/ZotacV2GPUController.h @@ -0,0 +1,109 @@ +/*---------------------------------------------------------*\ +| ZotacV2GPUController.h | +| | +| Driver for Zotac V2 GPU | +| | +| Krzysztof Haładyn (krzys_h) 16 Mar 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +enum +{ + ZOTAC_V2_GPU_REG_RGB = 0xA0, +}; + +enum +{ + ZOTAC_V2_GPU_CONFIG_IDLE = 0x00, // Config for when there is no load + ZOTAC_V2_GPU_CONFIG_ACTIVE = 0x01, // Config for when GPU is under load +}; + +enum +{ + ZOTAC_V2_GPU_SYNC_INDIVIDUAL = 0x00, // Everything separated + ZOTAC_V2_GPU_SYNC_SYNCHRONIZED = 0x01, // All internal zones synchronized, external is separated + ZOTAC_V2_GPU_SYNC_SYNCHRONIZED_WITH_EXTERNAL = 0x02, // Everything synchronized +}; + +enum +{ + ZOTAC_V2_GPU_MODE_STATIC = 0x00, // Basic static color + ZOTAC_V2_GPU_MODE_BREATH = 0x01, // Single color fades on and off + ZOTAC_V2_GPU_MODE_FADE = 0x02, // All colors fade through the spectrum + ZOTAC_V2_GPU_MODE_WINK = 0x03, // Single color flashes on and off + ZOTAC_V2_GPU_MODE_FLASH = 0x04, // Each zone flashes a different color (only supported in SYNCHRONIZED or SYNCHRONIZED_WITH_EXTERNAL mode) + ZOTAC_V2_GPU_MODE_SHINE = 0x05, // (Sound activated) Single color, on and off + ZOTAC_V2_GPU_MODE_RANDOM = 0x06, // Single color, random patern + ZOTAC_V2_GPU_MODE_SLIDE = 0x07, // Single color, moves one side to the other + ZOTAC_V2_GPU_MODE_RAINBOW = 0x08, // All colors move one side to the other + ZOTAC_V2_GPU_MODE_MARQUEE = 0x09, // Very similar to SLIDE effect + ZOTAC_V2_GPU_MODE_DRIP = 0x0A, // Similar to SLIDE as well, less color moves + ZOTAC_V2_GPU_MODE_DANCE = 0x0B, // (Sound activated) Single color, equalizer effect + ZOTAC_V2_GPU_MODE_DUET = 0x17, // Dual colors + ZOTAC_V2_GPU_MODE_PATH = 0x18, // Very similar to RAINBOW effect +}; + +enum +{ + ZOTAC_V2_GPU_DIR_LEFT = 0x00, + ZOTAC_V2_GPU_DIR_RIGHT = 0x01, +}; + +enum +{ + ZOTAC_V2_GPU_CIRCUIT_ON = 0x00, + ZOTAC_V2_GPU_CIRCUIT_OFF = 0x01, +}; + +struct ZotacV2GPUConfig +{ + int numberOfZones = 0; + bool supportsExternalLEDStrip = false; +}; + +struct ZotacV2GPUZone +{ + int mode = 0; + RGBColor color1 = ToRGBColor(0, 0, 0); + RGBColor color2 = ToRGBColor(0, 0, 0); + unsigned int colorPreset = 0; + unsigned int speed = 0; + unsigned int brightness = 0; + unsigned int direction = 0; + unsigned int circuit = 0; +}; + + +class ZotacV2GPUController +{ +public: + ZotacV2GPUController(i2c_smbus_interface* bus, u8 dev, std::string dev_name); + ~ZotacV2GPUController(); + + std::string GetDeviceLocation(); + std::string GetName(); + std::string GetVersion(); + + bool TurnOnOff(bool on); + bool ResetToDefaults(); + bool GetMode(int zone, int idleActive, int& syncMode, ZotacV2GPUZone& zoneConfig, bool& on); + bool SetMode(int zone, int idleActive, int syncMode, ZotacV2GPUZone zoneConfig); + +private: + i2c_smbus_interface* bus; + u8 dev; + std::string name; + std::string version; + + bool ReadVersion(); + bool SendCommand(bool on, bool reset, int zone, int idleActive, int syncMode, ZotacV2GPUZone zoneConfig); + bool ParseCommand(bool& on, bool& reset, int& zone, int& idleActive, int& syncMode, ZotacV2GPUZone& zoneConfig); +}; diff --git a/Controllers/ZotacV2GPUController/ZotacV2GPUControllerDetect.cpp b/Controllers/ZotacV2GPUController/ZotacV2GPUControllerDetect.cpp new file mode 100644 index 0000000..f4e51bb --- /dev/null +++ b/Controllers/ZotacV2GPUController/ZotacV2GPUControllerDetect.cpp @@ -0,0 +1,67 @@ +/*---------------------------------------------------------*\ +| ZotacV2GPUControllerDetect.cpp | +| | +| Detector for Zotac V2 GPU | +| | +| Krzysztof Haładyn (krzys_h) 16 Mar 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "ZotacV2GPUController.h" +#include "RGBController_ZotacV2GPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" +#include "LogManager.h" + +/******************************************************************************************\ +* * +* DetectZotacV2GPUControllers * +* * +* Detect ZOTAC 30/40 series RGB controllers on the enumerated I2C busses * +* at address 0x49. * +* * +* bus - pointer to i2c_smbus_interface where RGB device is connected * +* dev - I2C address of RGB device * +* * +\******************************************************************************************/ + +void DetectZotacV2GPUControllers(i2c_smbus_interface* bus, u8 i2c_addr, const std::string& name) +{ + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + + if(bus->i2c_read_block(i2c_addr, &rdata_len, rdata_pkt) >= 0) + { + ZotacV2GPUController* controller = new ZotacV2GPUController(bus, i2c_addr, name); + RGBController_ZotacV2GPU* rgb_controller = new RGBController_ZotacV2GPU(controller); + + if(rgb_controller->config.numberOfZones > 0) + { + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + LOG_ERROR("[%s] RGB controller not registered.", name.c_str()); + } + } +} + +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3070 AMP Holo LHR", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3070_LHR_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3070_AMP_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3070 Ti", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_GA102_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3070TI_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3070 Ti Trinity OC/AMP Holo", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3070TI_TRINITY_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3080 Trinity OC", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3080_TRINITY_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3080 Trinity LHR", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3080_TRINITY_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3080 AMP Holo", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3080_AMP_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3080 AMP Holo LHR", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_LHR_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3080_AMP_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3080 12GB Trinity OC LHR", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080_12G_LHR_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3080_12G_LHR_TRINITY_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3080 Ti AMP Holo", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3080TI_AMP_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3090 AMP Extreme Holo", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3090_AMP_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3090 Trinity", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3090_TRINITY_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 4070 Ti Trinity OC", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4070TI_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX4070TI_TRINITY_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 4080 AMP Extreme AIRO", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX4080_AMP_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 4080 AMP Extreme AIRO", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4080_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX4080_AMP_ALT_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 4090 Trinity OC", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX4090_TRINITY_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 4090 AMP Extreme AIRO", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX4090_AMP_SUB_DEV, 0x49); diff --git a/Detector.h b/Detector.h new file mode 100644 index 0000000..140f3bf --- /dev/null +++ b/Detector.h @@ -0,0 +1,41 @@ +/*---------------------------------------------------------*\ +| Detector.h | +| | +| Macros for registering detectors | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "DeviceDetector.h" + +#define REGISTER_DETECTOR(name, func) static DeviceDetector device_detector_obj_##func(name, func) +#define REGISTER_I2C_DETECTOR(name, func) static I2CDeviceDetector device_detector_obj_##func(name, func) +#define REGISTER_I2C_DIMM_DETECTOR(name, func, jedec_id, dimm_type) static I2CDIMMDeviceDetector device_detector_obj_##func##jedec_id(name, func, jedec_id, dimm_type) +#define REGISTER_I2C_PCI_DETECTOR(name, func, ven, dev, subven, subdev, addr) static I2CPCIDeviceDetector device_detector_obj_##ven##dev##subven##subdev##addr##func(name, func, ven, dev, subven, subdev, addr) +#define REGISTER_I2C_BUS_DETECTOR(func) static I2CBusDetector device_detector_obj_##func(func) +#define REGISTER_HID_DETECTOR(name, func, vid, pid) static HIDDeviceDetector device_detector_obj_##vid##pid(name, func, vid, pid, HID_INTERFACE_ANY, HID_USAGE_PAGE_ANY, HID_USAGE_ANY) +#define REGISTER_HID_DETECTOR_I(name, func, vid, pid, interface) static HIDDeviceDetector device_detector_obj_##vid##pid##_##interface(name, func, vid, pid, interface, HID_USAGE_PAGE_ANY, HID_USAGE_ANY) +#define REGISTER_HID_DETECTOR_IP(name, func, vid, pid, interface, page) static HIDDeviceDetector device_detector_obj_##vid##pid##_##interface##_##page(name, func, vid, pid, interface, page, HID_USAGE_ANY) +#define REGISTER_HID_DETECTOR_IPU(name, func, vid, pid, interface, page, usage) static HIDDeviceDetector device_detector_obj_##vid##pid##_##interface##_##page##_##usage(name, func, vid, pid, interface, page, usage) +#define REGISTER_HID_DETECTOR_P(name, func, vid, pid, page) static HIDDeviceDetector device_detector_obj_##vid##pid##__##page(name, func, vid, pid, HID_INTERFACE_ANY, page, HID_USAGE_ANY) +#define REGISTER_HID_DETECTOR_PU(name, func, vid, pid, page, usage) static HIDDeviceDetector device_detector_obj_##vid##pid##__##page##_##usage(name, func, vid, pid, HID_INTERFACE_ANY, page, usage) +#define REGISTER_HID_WRAPPED_DETECTOR(name, func, vid, pid) static HIDWrappedDeviceDetector device_detector_obj_##vid##pid(name, func, vid, pid, HID_INTERFACE_ANY, HID_USAGE_PAGE_ANY, HID_USAGE_ANY) +#define REGISTER_HID_WRAPPED_DETECTOR_I(name, func, vid, pid, interface) static HIDWrappedDeviceDetector device_detector_obj_##vid##pid##_##interface(name, func, vid, pid, interface, HID_USAGE_PAGE_ANY, HID_USAGE_ANY) +#define REGISTER_HID_WRAPPED_DETECTOR_IPU(name, func, vid, pid, interface, page, usage) static HIDWrappedDeviceDetector device_detector_obj_##vid##pid##_##interface##_##page##_##usage(name, func, vid, pid, interface, page, usage) +#define REGISTER_HID_WRAPPED_DETECTOR_PU(name, func, vid, pid, page, usage) static HIDWrappedDeviceDetector device_detector_obj_##vid##pid##__##page##_##usage(name, func, vid, pid, HID_INTERFACE_ANY, page, usage) +#define REGISTER_DYNAMIC_DETECTOR(name, func) static DynamicDetector device_detector_obj_##func(name, func) +#define REGISTER_PRE_DETECTION_HOOK(func) static PreDetectionHook device_detector_obj_##func(func) + +#define REGISTER_DYNAMIC_I2C_DETECTOR(name, func) I2CDeviceDetector device_detector_obj_##func(name, func) +#define REGISTER_DYNAMIC_I2C_DIMM_DETECTOR(name, func, jedec_id, dimm_type) I2CDIMMDeviceDetector device_detector_obj_##func(name, func, jedec_id, dimm_type) +#define REGISTER_DYNAMIC_I2C_PCI_DETECTOR(name, func, ven, dev, subven, subdev, addr) I2CPCIDeviceDetector device_detector_obj_##ven##dev##subven##subdev##addr##func(name, func, ven, dev, subven, subdev, addr) +#define REGISTER_DYNAMIC_I2C_BUS_DETECTOR(func) I2CBusDetector device_detector_obj_##func(func) +#define REGISTER_DYNAMIC_HID_DETECTOR(name, func, vid, pid) HIDDeviceDetector device_detector_obj_##vid##pid(name, func, vid, pid, HID_INTERFACE_ANY, HID_USAGE_PAGE_ANY, HID_USAGE_ANY) +#define REGISTER_DYNAMIC_HID_DETECTOR_I(name, func, vid, pid, interface) HIDDeviceDetector device_detector_obj_##vid##pid##_##interface(name, func, vid, pid, interface, HID_USAGE_PAGE_ANY, HID_USAGE_ANY) +#define REGISTER_DYNAMIC_HID_DETECTOR_IP(name, func, vid, pid, interface, page) HIDDeviceDetector device_detector_obj_##vid##pid##_##interface##_##page(name, func, vid, pid, interface, page, HID_USAGE_ANY) +#define REGISTER_DYNAMIC_HID_DETECTOR_IPU(name, func, vid, pid, interface, page, usage) HIDDeviceDetector device_detector_obj_##vid##pid##_##interface##_##page##_##usage(name, func, vid, pid, interface, page, usage) +#define REGISTER_DYNAMIC_HID_DETECTOR_P(name, func, vid, pid, page) HIDDeviceDetector device_detector_obj_##vid##pid##__##page(name, func, vid, pid, HID_INTERFACE_ANY, page, HID_USAGE_ANY) +#define REGISTER_DYNAMIC_HID_DETECTOR_PU(name, func, vid, pid, page, usage) HIDDeviceDetector device_detector_obj_##vid##pid##__##page##_##usage(name, func, vid, pid, HID_INTERFACE_ANY, page, usage) diff --git a/DeviceDetector.h b/DeviceDetector.h new file mode 100644 index 0000000..7e1d327 --- /dev/null +++ b/DeviceDetector.h @@ -0,0 +1,97 @@ +/*---------------------------------------------------------*\ +| DeviceDetector.h | +| | +| Device detector functionality | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +#include "ResourceManager.h" + +class DeviceDetector +{ +public: + DeviceDetector(std::string name, DeviceDetectorFunction detector) + { + ResourceManager::get()->RegisterDeviceDetector(name, detector); + } +}; + +class I2CDeviceDetector +{ +public: + I2CDeviceDetector(std::string name, I2CDeviceDetectorFunction detector) + { + ResourceManager::get()->RegisterI2CDeviceDetector(name, detector); + } +}; + +class I2CDIMMDeviceDetector +{ +public: + I2CDIMMDeviceDetector(std::string name, I2CDIMMDeviceDetectorFunction detector, uint16_t jedec_id, uint8_t dimm_type) + { + ResourceManager::get()->RegisterI2CDIMMDeviceDetector(name, detector, jedec_id, dimm_type); + } +}; + +class I2CPCIDeviceDetector +{ +public: + I2CPCIDeviceDetector(std::string name, I2CPCIDeviceDetectorFunction detector, uint16_t ven_id, uint16_t dev_id, uint16_t subven_id, uint16_t subdev_id, uint8_t i2c_addr) + { + ResourceManager::get()->RegisterI2CPCIDeviceDetector(name, detector, ven_id, dev_id, subven_id, subdev_id, i2c_addr); + } +}; + +class I2CBusDetector +{ +public: + I2CBusDetector(I2CBusDetectorFunction detector) + { + ResourceManager::get()->RegisterI2CBusDetector(detector); + } +}; + +class HIDDeviceDetector +{ +public: + HIDDeviceDetector(std::string name, HIDDeviceDetectorFunction detector, uint16_t vid, uint16_t pid, int interface, int usage_page, int usage) + { + ResourceManager::get()->RegisterHIDDeviceDetector(name, detector, vid, pid, interface, usage_page, usage); + } +}; + +class HIDWrappedDeviceDetector +{ +public: + HIDWrappedDeviceDetector(std::string name, HIDWrappedDeviceDetectorFunction detector, uint16_t vid, uint16_t pid, int interface, int usage_page, int usage) + { + ResourceManager::get()->RegisterHIDWrappedDeviceDetector(name, detector, vid, pid, interface, usage_page, usage); + } +}; + +class DynamicDetector +{ +public: + DynamicDetector(std::string name, DynamicDetectorFunction detector) + { + ResourceManager::get()->RegisterDynamicDetector(name, detector); + } +}; + +class PreDetectionHook +{ +public: + PreDetectionHook(PreDetectionHookFunction hook) + { + ResourceManager::get()->RegisterPreDetectionHook(hook); + } +}; diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ab92824 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,128 @@ +# syntax=docker/dockerfile:1.7 + +FROM debian:bookworm-slim@sha256:88200866dfff7ea7f5cbcb6ec7c8a701889efe6fe859fe64d6990e4b07ea4171 AS openrgb-builder +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + git \ + libhidapi-dev \ + libmbedtls-dev \ + libusb-1.0-0-dev \ + pkgconf \ + qt5-qmake \ + qtbase5-dev \ + qtbase5-dev-tools \ + qtchooser \ + qttools5-dev-tools \ + && rm -rf /var/lib/apt/lists/* +WORKDIR /src/openrgb +COPY *.cpp *.h OpenRGB.pro ./ +COPY AutoStart/ AutoStart/ +COPY Controllers/ Controllers/ +COPY dependencies/ dependencies/ +COPY dmiinfo/ dmiinfo/ +COPY hidapi_wrapper/ hidapi_wrapper/ +COPY i2c_smbus/ i2c_smbus/ +COPY i2c_tools/ i2c_tools/ +COPY interop/ interop/ +COPY KeyboardLayoutManager/ KeyboardLayoutManager/ +COPY mac/ mac/ +COPY net_port/ net_port/ +COPY pci_ids/ pci_ids/ +COPY qt/ qt/ +COPY RGBController/ RGBController/ +COPY scripts/ scripts/ +COPY scsiapi/ scsiapi/ +COPY serial_port/ serial_port/ +COPY SPDAccessor/ SPDAccessor/ +COPY startup/ startup/ +COPY super_io/ super_io/ +COPY SuspendResume/ SuspendResume/ +COPY wmi/ wmi/ +RUN mkdir -p build \ + && cd build \ + && qmake ../OpenRGB.pro CONFIG+=release \ + && make -j"$(nproc)" \ + && test -x ./openrgb \ + && install -D -m 0755 ./openrgb /out/usr/local/bin/openrgb \ + && if [ -f ./60-openrgb.rules ]; then install -D -m 0644 ./60-openrgb.rules /out/usr/lib/udev/rules.d/60-openrgb.rules; fi + +FROM node:22-bookworm-slim@sha256:83f487e0a63425e5b4d146fb5e5be574bcbe1b7b843d3ebafdd95eaf7767a7e5 AS frontend-builder +WORKDIR /src/frontend +COPY lumaops/frontend/package.json lumaops/frontend/package-lock.json ./ +RUN npm ci --ignore-scripts +COPY lumaops/frontend/ ./ +RUN npm run build + +FROM python:3.12-slim-bookworm@sha256:0f5b26b9518d002b6173fd61daad821fa340635ebfec5bba471013f9ca114579 AS backend-builder +ENV VIRTUAL_ENV=/opt/venv +RUN python -m venv "$VIRTUAL_ENV" +ENV PATH="$VIRTUAL_ENV/bin:$PATH" +WORKDIR /src/backend +COPY lumaops/backend/ ./ +RUN pip install --no-cache-dir --upgrade "pip==26.1.2" \ + && pip install --no-cache-dir . + +FROM python:3.12-slim-bookworm@sha256:0f5b26b9518d002b6173fd61daad821fa340635ebfec5bba471013f9ca114579 AS runtime +ARG DEBIAN_FRONTEND=noninteractive +ARG LUMAOPS_VERSION=0.1.0 +LABEL org.opencontainers.image.title="LumaOps" \ + org.opencontainers.image.version="$LUMAOPS_VERSION" \ + org.opencontainers.image.description="Local-first OpenRGB control plane" \ + org.opencontainers.image.licenses="GPL-2.0-or-later" +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + gosu \ + libdbus-1-3 \ + libgl1 \ + libhidapi-hidraw0 \ + libhidapi-libusb0 \ + libmbedcrypto7 \ + libmbedtls14 \ + libmbedx509-1 \ + libqt5core5a \ + libqt5dbus5 \ + libqt5gui5 \ + libqt5network5 \ + libqt5widgets5 \ + libusb-1.0-0 \ + tini \ + && rm -rf /var/lib/apt/lists/* \ + && groupadd --gid 1000 lumaops \ + && useradd --uid 1000 --gid 1000 --home-dir /config/lumaops --shell /usr/sbin/nologin lumaops + +COPY --from=openrgb-builder /out/ / +COPY --from=backend-builder /opt/venv /opt/venv +COPY --from=frontend-builder /src/frontend/dist /opt/lumaops/static +COPY docker/supervisor.py docker/healthcheck.py /opt/lumaops/ +COPY docker/entrypoint.sh /usr/local/bin/lumaops-entrypoint +RUN chmod 0755 /usr/local/bin/lumaops-entrypoint /opt/lumaops/supervisor.py /opt/lumaops/healthcheck.py + +ENV PATH="/opt/venv/bin:$PATH" \ + PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + LUMAOPS_ENV=production \ + APP_HOST=0.0.0.0 \ + APP_PORT=8080 \ + LOG_LEVEL=INFO \ + TZ=Europe/Brussels \ + OPENRGB_HOST=127.0.0.1 \ + OPENRGB_PORT=6742 \ + OPENRGB_CONFIG_DIR=/config/openrgb \ + DATABASE_URL=sqlite:////data/lumaops.db \ + CONFIG_DIR=/config/lumaops \ + DATA_DIR=/data \ + LOGS_DIR=/logs \ + STATIC_DIR=/opt/lumaops/static \ + ENABLE_NETWORK_DISCOVERY=true \ + ENABLE_HOME_ASSISTANT=false \ + ENABLE_WLED=false \ + PUID=99 \ + PGID=100 + +WORKDIR /opt/lumaops +VOLUME ["/config", "/data", "/logs"] +EXPOSE 8080 +ENTRYPOINT ["/usr/local/bin/lumaops-entrypoint"] +HEALTHCHECK --interval=30s --timeout=8s --start-period=90s --retries=3 \ + CMD ["/opt/venv/bin/python", "/opt/lumaops/healthcheck.py"] diff --git a/Documentation/Common-Modes.md b/Documentation/Common-Modes.md new file mode 100644 index 0000000..4305bdc --- /dev/null +++ b/Documentation/Common-Modes.md @@ -0,0 +1,19 @@ +# Common Modes in OpenRGB + +OpenRGB uses "modes" to describe RGB effects built into a device’s firmware. These effects can be changed by selecting a mode in the OpenRGB interface. The patterns are generated by the device’s RGB controller, not by OpenRGB, and run independently of the PC. In the interest of being able to better describe these modes, we are trying to standardize the mode names across all supported devices. + +There are several effects that are pretty common across many brands and vendors of RGB hardware. Manufacturers often use different names for the same mode. For example, a fade-in/fade-out effect might be called "Fading", "Breathing", or "Breath". A color-cycling effect might appear as "Cycle", "Spectrum Cycle", "Spectrum", or "Rainbow". A lot of OpenRGB's existing code simply copies the same names the official software gave to the modes, but these differences in name prevent us from being able to apply what is essentially the same effect across multiple devices when the names don't match. It also limits our ability to provide clear descriptions of modes through tooltips. To remedy this, we have compiled a list of common modes and providing names that we can use across all devices for any mode that implements a certain style of effect. If all devices have their fade on/fade off mode called "Breathing", we can just set "Breathing" across all devices and end up with something that looks at least sort of uniform, if not synchronized in time. + +# Common OpenRGB Modes + +| Standardized Name | Description | Example GIF | +| ----------------- | ----------- | ----------- | +| Direct | Direct mode is a mode that allows setting individual LEDs to static colors that does not fade or flicker upon color changes and does not save the updated colors to device memory. Used for effect engine software to rapidly update the LEDs for PC software driven effects. Some devices require a continuous packet stream to remain in direct mode, otherwise they revert to built-in effects. | | +| Custom | Custom mode is a mode that allows setting individual LEDs to static colors, but does not meet the criteria for being a Direct mode. This means it either flickers, fades, or saves to device memory. Generally unsuitable for effect engine software. | | +| Static | Static mode is a mode that sets the entire device or device zones, but not individual LEDs, to a static color. This mode may fade or flicker and may save to device memory. | | +| Breathing | Light gradually fades from fully off to fully on over some period of time and then gradually fades back to fully off. | | +| Flashing | Light abruptly changes from fully off to fully on instantly, then instantly turns back off after a period of time. | | +| Spectrum Cycle | Light gradually cycles through the entire color spectrum. All lights on the device are the same color. | | +| Rainbow Wave | Light gradually cycles through the entire color spectrum. Lights are staggered as to produce a rainbow pattern that moves. | | +| Reactive | Generally only available on input devices, this is a mode that lights one or more LEDs when an input (key, mouse button, etc) is pressed. | | +| Off | All lights are disabled. | | diff --git a/Documentation/Compiling.md b/Documentation/Compiling.md new file mode 100644 index 0000000..37030b0 --- /dev/null +++ b/Documentation/Compiling.md @@ -0,0 +1,71 @@ +# Compiling + +This document details the process to compile OpenRGB from source on supported operating systems. + +## Windows + + * You will need the **Microsoft Visual 2019 C++ runtime** installed. You can get it [here](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist) + * To build the application yourself on Windows: + 1. [Install Git](https://git-scm.com/download) + 2. Clone the [OpenRGB-Qt-Packages](https://gitlab.com/OpenRGBDevelopers/OpenRGB-Qt-Packages) git repo and run `install.bat` (or optionally `install-chocolatey.bat` if you use the Chocolatey package manager). + 3. In the OpenRGB source directory, run the `scripts\build-windows.bat` file with arguments ` `. + * Qt versions provided by `OpenRGB-Qt-Packages` include `5.15.0` (using MSVC `2019`) and `6.8.3` (using MSVC `2022`). + * For example, for a Qt5 64-bit build, `.\scripts\build-windows.bat 5.15.0 2019 64` + 4. You can also use Qt Creator to build and debug the project, you will need to install it from the Qt Online Installer or by downloading and extracting a binary release and manually configuring it. + +## Linux + + 1. Install build dependencies + - Debian/Ubuntu: `sudo apt install git build-essential qtcreator qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libusb-1.0-0-dev libhidapi-dev pkgconf libmbedtls-dev qttools5-dev-tools` + - Fedora: `sudo dnf install automake gcc-c++ git hidapi-devel libusbx-devel mbedtls-devel pkgconf qt5-qtbase-devel qt5-linguist` + 2. `git clone https://gitlab.com/CalcProgrammer1/OpenRGB` + 3. `cd OpenRGB` + 4. `mkdir build` + 5. `cd build` + 4. `qmake ../OpenRGB.pro` + 5. `make -j$(nproc)` + 6. You can then run the application from the compile directory with `./openrgb` or install with `make install` + 7. You will also need to [install the latest udev rules](UdevRules.md). + +#### Packaging + +You can also build OpenRGB generic AppImage packages and distribution-specific packages for Debian-based and Fedora-based distros. Install the build dependencies from the section above for your distribution before proceeding. + + * AppImage: + + * Debian/Ubuntu: + * Make sure OpenRGB is cloned in ~/OpenRGB before proceeding. Output .deb is in ~/. + 1. `sudo apt install debhelper` + 2. `cd ~/OpenRGB` + 3. `scripts/build-package-files.sh debian/changelog` + 4. `dpkg-buildpackage -us -B` + + * Fedora: + * Make sure OpenRGB is cloned in ~/OpenRGB before proceeding. Output .rpm is in ~/rpmbuild/RPMS/. + 1. `sudo dnf install rpmdevtools dnf-plugins-core` + 2. `cd ~/` + 3. `rpmdev-setuptree` + 4. `tar -cf rpmbuild/SOURCES/OpenRGB.tar.gz OpenRGB/` + 4. `cd OpenRGB` + 5. `./scripts/build-package-files.sh fedora/OpenRGB.spec` + 6. `cd ~/` + 7. `cp OpenRGB/fedora/OpenRGB.spec rpmbuild/SPECS/` + 8. `sudo dnf builddep rpmbuild/SPECS/OpenRGB.spec -y` + 9. `cd rpmbuild/SOURCES` + 10. `tar -xf OpenRGB.tar.gz` + 11. `cd ~/` + 12. `rpmbuild -ba rpmbuild/SPECS/OpenRGB.spec` + +## MacOS + + 1. Install build dependencies with Homebrew + - Install Homebrew by following the instructions at https://brew.sh/ + - `brew install git qt5 hidapi libusb mbedtls@2` + - `brew link qt5` + 2. [Create a local certificate](https://support.apple.com/guide/keychain-access/create-self-signed-certificates-kyca8916/mac) called OpenRGB with code signing capability + 3. git clone https://gitlab.com/CalcProgrammer1/OpenRGB + 4. cd OpenRGB + 5. qmake OpenRGB.pro + 6. make -j8 + 7. macdeployqt OpenRGB.app -codesign=OpenRGB + 8. Copy the OpenRGB.app application package to Applications diff --git a/Documentation/Images/OpenRGB.png b/Documentation/Images/OpenRGB.png new file mode 100644 index 0000000..8c6ec36 Binary files /dev/null and b/Documentation/Images/OpenRGB.png differ diff --git a/Documentation/Images/OpenRGB_Screenshot.png b/Documentation/Images/OpenRGB_Screenshot.png new file mode 100644 index 0000000..3e921c7 Binary files /dev/null and b/Documentation/Images/OpenRGB_Screenshot.png differ diff --git a/Documentation/KernelParameters.md b/Documentation/KernelParameters.md new file mode 100644 index 0000000..ea50e05 --- /dev/null +++ b/Documentation/KernelParameters.md @@ -0,0 +1,17 @@ +# Kernel Parameters + + * To resolve an ACPI conflict add the `acpi_enforce_resources=lax` kernel parameter. + * If you want to check if the kernel was loaded with this option you can execute this command from the terminal once you've rebooted: `cat /proc/cmdline`. + +### Arch Linux + + * Please see [the Arch wiki](https://wiki.archlinux.org/title/kernel_parameters) for details on how to update your bootloader. + +### Debian/Ubuntu + + * Please see [the Ubuntu Documentation](https://wiki.ubuntu.com/Kernel/KernelBootParameters) for Kernel Parameters for more information on updating your boot parameters. + +### Fedora + + * On Fedora, install `grubby` and then following command: `grubby --update-kernel=ALL --args="acpi_enforce_resources=lax"`. + * For more information please refer to the Fedora docs for [grubby](https://docs.fedoraproject.org/en-US/fedora/latest/system-administrators-guide/kernel-module-driver-configuration/Working_with_the_GRUB_2_Boot_Loader/#sec-Making_Persistent_Changes_to_a_GRUB_2_Menu_Using_the_grubby_Tool). diff --git a/Documentation/OpenRGBSDK.md b/Documentation/OpenRGBSDK.md new file mode 100644 index 0000000..82765cf --- /dev/null +++ b/Documentation/OpenRGBSDK.md @@ -0,0 +1,405 @@ +# OpenRGB SDK Documentation + +OpenRGB provides a network-based Software Development Kit (SDK) interface for third-party software applications to integrate with OpenRGB to control lighting on OpenRGB-supported devices. This protocol is a binary, packet-based protocol designed for efficient, lightweight transfer of lighting data over a TCP/IP connection. It may be used locally or over a physical network between computers. The protocol is versioned. Client and server must negotiate a minimum supported protocol version upon connection. The selected protocol version determines what capabilities are available and can change packet format for certain packets as new information is added to the protocol. + +The protocol mimics the [RGBController API](The-RGBController-API) closely. It can be thought of as "RGBController over IP" in that the protocol is designed so that a network RGBController object can be created on the client that is a direct copy of the real RGBController object on the server. Calls to the network client RGBController object send packets to the server which trigger calls to the real object, updating the necessary object data before the call. + +# Protocol Versions + +| Protocol Version | OpenRGB Release | Description | +| ---------------- | --------------- | -------------------------------------------------------------------------------------------------------------- | +| 0 | 0.3 | Initial (unversioned) protocol | +| 1 | 0.5 | Add versioning, add vendor string | +| 2 | 0.6 | Add profile controls | +| 3 | 0.7 | Add brightness field to modes, add SaveMode() | +| 4 | 0.9 | Add segments field to zones, plugin interface | +| 5 | 1.0 | Add zone flags, controller flags, effects-only zones, alternative LED names, add ClearSegments and AddSegments | + +\* Denotes unreleased version, reflects status of current pipeline + +# Protocol Basics + +The default port for the OpenRGB SDK server is 6742. This is "ORGB" on a telephone keypad. + +Each packet starts with a header that indicates the packet is an OpenRGB SDK packet and provides the device and packet IDs. The header format is described in the following table. + +### NetPacketHeader structure + +| Size | Format | Name | Description | +| ---- | ------------ | ----------- | ------------------- | +| 4 | char[4] | pkt_magic | Magic value, "ORGB" | +| 4 | unsigned int | pkt_dev_idx | Device Index | +| 4 | unsigned int | pkt_id | Packet ID | +| 4 | unsigned int | pkt_size | Packet Size | + +`pkt_magic`: Always set this to the literal value "ORGB". + +`pkt_dev_idx`: The device index that the command is targeting. + +`pkt_id`: The command ID, see IDs table below + +`pkt_size`: The size, in bytes, of the packet data + +### Packet IDs + +The following IDs represent different SDK commands. Each ID packet has a certain format of data associated with it, which will be explained under each ID's section of this document. Gaps have been left in the ID values to allow for future expansion. The same ID values are often used for both request and response packets. + +| Value | Name | Description | Protocol Version | +| ----- | ------------------------------------------------------------------------------------------- | ------------------------------------------------ | ---------------- | +| 0 | [NET_PACKET_ID_REQUEST_CONTROLLER_COUNT](#net_packet_id_request_controller_count) | Request RGBController device count from server | 0 | +| 1 | [NET_PACKET_ID_REQUEST_CONTROLLER_DATA](#net_packet_id_request_controller_data) | Request RGBController data block | 0 | +| 40 | [NET_PACKET_ID_REQUEST_PROTOCOL_VERSION](#net_packet_id_request_protocol_version) | Request OpenRGB SDK protocol version from server | 1* | +| 50 | [NET_PACKET_ID_SET_CLIENT_NAME](#net_packet_id_set_client_name) | Send client name string to server | 0 | +| 100 | [NET_PACKET_ID_DEVICE_LIST_UPDATED](#net_packet_id_device_list_updated) | Indicate to clients that device list has updated | 1 | +| 140 | [NET_PACKET_ID_REQUEST_RESCAN_DEVICES](#net_packet_id_request_rescan_devices) | Request server to rescan devices | 5 | +| 150 | [NET_PACKET_ID_REQUEST_PROFILE_LIST](#net_packet_id_request_profile_list) | Request profile list | 2 | +| 151 | [NET_PACKET_ID_REQUEST_SAVE_PROFILE](#net_packet_id_request_save_profile) | Save current configuration in a new profile | 2 | +| 152 | [NET_PACKET_ID_REQUEST_LOAD_PROFILE](#net_packet_id_request_load_profile) | Load a given profile | 2 | +| 153 | [NET_PACKET_ID_REQUEST_DELETE_PROFILE](#net_packet_id_request_delete_profile) | Delete a given profile | 2 | +| 200 | [NET_PACKET_ID_REQUEST_PLUGIN_LIST](#net_packet_id_request_plugin_list) | Request plugin list | 4 | +| 201 | [NET_PACKET_ID_PLUGIN_SPECIFIC](#net_packet_id_plugin_specific) | Plugin specific | 4 | +| 1000 | [NET_PACKET_ID_RGBCONTROLLER_RESIZEZONE](#net_packet_id_rgbcontroller_resizezone) | RGBController::ResizeZone() | 0 | +| 1001 | [NET_PACKET_ID_RGBCONTROLLER_CLEARSEGMENTS](#net_packet_id_rgbcontroller_clearsegments) | RGBController::ClearSegments() | 5 | +| 1002 | [NET_PACKET_ID_RGBCONTROLLER_ADDSEGMENT](#net_packet_id_rgbcontroller_addsegment) | RGBController::AddSegment() | 5 | +| 1050 | [NET_PACKET_ID_RGBCONTROLLER_UPDATELEDS](#net_packet_id_rgbcontroller_updateleds) | RGBController::UpdateLEDs() | 0 | +| 1051 | [NET_PACKET_ID_RGBCONTROLLER_UPDATEZONELEDS](#net_packet_id_rgbcontroller_updatezoneleds) | RGBController::UpdateZoneLEDs() | 0 | +| 1052 | [NET_PACKET_ID_RGBCONTROLLER_UPDATESINGLELED](#net_packet_id_rgbcontroller_updatesingleled) | RGBController::UpdateSingleLED() | 0 | +| 1100 | [NET_PACKET_ID_RGBCONTROLLER_SETCUSTOMMODE](#net_packet_id_rgbcontroller_setcustommode) | RGBController::SetCustomMode() | 0 | +| 1101 | [NET_PACKET_ID_RGBCONTROLLER_UPDATEMODE](#net_packet_id_rgbcontroller_updatemode) | RGBController::UpdateMode() | 0 | +| 1102 | [NET_PACKET_ID_RGBCONTROLLER_SAVEMODE](#net_packet_id_rgbcontroller_savemode) | RGBController::SaveMode() | 3 | + +\* The NET_PACKET_ID_REQUEST_PROTOCOL_VERSION packet was not present in protocol version 0, but clients supporting protocol versions 1+ should always send this packet. If no response is received, it should be assumed that the server is using protocol 0. + +# Packet-Specific Documentation + +## NET_PACKET_ID_REQUEST_CONTROLLER_COUNT + +### Request [Size: 0] + +The client uses this ID to request the number of controllers on the server. The request contains no data. + +### Response [Size: 4] + +The server responds to this request with the number of controllers in the device list. The response contains a single `unsigned int`, size 4, holding this value. + +## NET_PACKET_ID_REQUEST_CONTROLLER_DATA + +### Request [Protocol 0 Size: 0] [Protocol 1+ Size: 4] + +The client uses this ID to request the controller data for a given controller. For protocol 0, this request contains no data. For protocol 1 or higher, this request contains a single `unsigned int`, size 4, holding the highest protocol version supported by both the client and the server. The `pkt_dev_idx` of this request's header indicates which controller you are requesting data for. Upon connecting, the client should request controller data from 0 to [controller count], where [controller count] is the value from NET_PACKET_ID_REQUEST_CONTROLLER_COUNT. + +NOTE: Before sending this request, the client should request the protocol version from the server and determine the value to send, if any. If the server is using protocol version 0, even if the SDK implementation supports higher, send this packet with no data. + +### Response [Size: Variable] + +The server responds to this request with a large data block. The format of the block is shown below. Portions of this block are omitted if the requested protocol level is below the listed value. The receiver is expected to parse this data block using the same protocol version sent in the request (or protocol 0 if the request is sent with no data). + +| Size | Format | Name | Protocol Version | Description | +| ------------------- | ------------------------------------- | ------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------ | +| 4 | unsigned int | data_size | 0 | Size of all data in packet | +| 4 | int | type | 0 | RGBController type field value | +| 2 | unsigned short | name_len | 0 | Length of RGBController name field string, including null termination | +| name_len | char[name_len] | name | 0 | RGBController name field string value, including null termination | +| 2 | unsigned short | vendor_len | 1 | Length of RGBController vendor field string, including null termination | +| vendor_len | char[vendor_len] | vendor | 1 | RGBController vendor field string value, including null termination | +| 2 | unsigned short | description_len | 0 | Length of RGBController description field string, including null termination | +| description_len | char[description_len] | description | 0 | RGBController description field string value, including null termination | +| 2 | unsigned short | version_len | 0 | Length of RGBController version field string, including null termination | +| version_len | char[version_len] | version | 0 | RGBController version field string value, including null termination | +| 2 | unsigned short | serial_len | 0 | Length of RGBController serial field string, including null termination | +| serial_len | char[serial_len] | serial | 0 | RGBController serial field string value, including null termination | +| 2 | unsigned short | location_len | 0 | Length of RGBController location field string, including null termination | +| location_len | char[location_len] | location | 0 | RGBController location field string value, including null termination | +| 2 | unsigned short | num_modes | 0 | Number of modes in RGBController | +| 4 | int | active_mode | 0 | RGBController active_mode field value | +| Variable | Mode Data[num_modes] | modes | 0 | See [Mode Data](#mode-data) block format table. Repeat num_modes times | +| 2 | unsigned short | num_zones | 0 | Number of zones in RGBController | +| Variable | Zone Data[num_zones] | zones | 0 | See [Zone Data](#zone-data) block format table. Repeat num_zones times | +| 2 | unsigned short | num_leds | 0 | Number of LEDs in RGBController | +| Variable | LED Data[num_leds] | leds | 0 | See [LED Data](#led-data) block format table. Repeat num_leds times | +| 2 | unsigned short | num_colors | 0 | Number of colors in RGBController | +| 4 * num_colors | RGBColor[num_colors] | colors | 0 | RGBController colors field values | +| 2 | unsigned short | num_led_alt_names | 5 | Number of LED alternate name strings | +| Variable | LED Alternate Name[num_led_alt_names] | led_alt_names | 5 | See [LED Alternate Name Data](#led-alternate-names-data) block format table. Repeat num_led_alt_names times | +| 4 | unsigned int | flags | 5 | RGBController flags field value | + +## Mode Data + +The Mode Data block represents one entry in the `RGBController::modes` vector. Portions of this block are omitted if the requested protocol level is below the listed value. + +| Size | Format | Name | Protocol Version | Description | +| ------------------- | ------------------------- | ------------------- | ---------------- | ------------------------------------------------------ | +| 2 | unsigned short | mode_name_len | 0 | Length of mode name string, including null termination | +| mode_name_len | char[mode_name_len] | mode_name | 0 | Mode name string value, including null termination | +| 4 | int | mode_value | 0 | Mode value field value | +| 4 | unsigned int | mode_flags | 0 | Mode flags field value | +| 4 | unsigned int | mode_speed_min | 0 | Mode speed_min field value | +| 4 | unsigned int | mode_speed_max | 0 | Mode speed_max field value | +| 4 | unsigned int | mode_brightness_min | 3 | Mode brightness_min field value | +| 4 | unsigned int | mode_brightness_max | 3 | Mode brightness_max field value | +| 4 | unsigned int | mode_colors_min | 0 | Mode colors_min field value | +| 4 | unsigned int | mode_colors_max | 0 | Mode colors_max field value | +| 4 | unsigned int | mode_speed | 0 | Mode speed value | +| 4 | unsigned int | mode_brightness | 3 | Mode brightness value | +| 4 | unsigned int | mode_direction | 0 | Mode direction value | +| 4 | unsigned int | mode_color_mode | 0 | Mode color_mode value | +| 2 | unsigned short | mode_num_colors | 0 | Mode number of colors | +| 4 * mode_num_colors | RGBColor[mode_num_colors] | mode_colors | 0 | Mode color values | + +## Zone Data + +The Zone Data block represents one entry in the `RGBController::zones` vector. + +| Size | Format | Name | Protocol Version | Description | +| ---------------------- | --------------------------------- | ------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------ | +| 2 | unsigned short | zone_name_len | 0 | Length of zone name string, including null termination | +| zone_name_len | char[zone_name_len] | zone_name | 0 | Zone name string value, including null termination | +| 4 | int | zone_type | 0 | Zone type value | +| 4 | unsigned int | zone_leds_min | 0 | Zone leds_min value | +| 4 | unsigned int | zone_leds_max | 0 | Zone leds_max value | +| 4 | unsigned int | zone_leds_count | 0 | Zone leds_count value | +| 2 | unsigned short | zone_matrix_len | 0 | Zone matrix length if matrix_map exists: (matrix_map width * height * 4) + 8 OTHERWISE 0 if matrix_map NULL | +| 4* | unsigned int | zone_matrix_height | 0 | Zone matrix_map height (*only if matrix_map exists) | +| 4* | unsigned int | zone_matrix_width | 0 | Zone matrix_map width (*only if matrix_map exists) | +| (zone_matrix_len - 8)* | unsigned int[zone_matrix_len - 8] | zone_matrix_data | 0 | Zone matrix_map data (*only if matrix_map exists) | +| 2 | unsigned short | num_segments | 4 | Number of segments in zone | +| Variable | Segment Data[num_segments] | segments | 4 | See [Segment Data](#segment-data) block format table. Repeat num_segments times | +| 4 | unsigned int | zone_flags | 5 | Zone flags value | + +## Segment Data + +The Segment Data block represents one entry in the `RGBController::zones::segments` vector. This data block was introduced in protocol version 4. + +| Size | Format | Name | Protocol Version | Description | +| ---------------- | ---------------------- | ------------------ | ---------------- | --------------------------------------------------------- | +| 2 | unsigned short | segment_name_len | 4 | Length of segment name string, including null termination | +| segment_name_len | char[segment_name_len] | segment_name | 4 | Segment name string value, including null termination | +| 4 | int | segment_type | 4 | Segment type value | +| 4 | unsigned int | segment_start_idx | 4 | Segment start_idx value | +| 4 | unsigned int | segment_leds_count | 4 | Segment leds_count value | + +## LED Data + +The LED Data block represents one entry in the `RGBController::leds` vector. + +| Size | Format | Name | Protocol Version | Description | +| ------------------- | ------------------------- | ------------------- | ---------------- | ------------------------------------------------------ | +| 2 | unsigned short | led_name_len | 0 | Length of LED name string, including null termination | +| led_name_len | char[led_name_len] | led_name | 0 | LED name string value, including null termination | +| 4 | unsigned int | led_value | 0 | LED value field value | + +## LED Alternate Name Data + +The LED Alternate Name Data block represents one entry in the `RGBController::led_alt_names` vector. This data block was introduced in protocol version 5. + +| Size | Format | Name | Protocol Version | Description | +| ---------------- | ---------------------- | ---------------- | ---------------- | --------------------------------------------------------------- | +| 2 | unsigned short | led_alt_name_len | 5 | Length of LED alternate name string, including null termination | +| led_alt_name_len | char[led_alt_name_len] | led_alt_name | 5 | LED alternate name string value, including null termination | + +## NET_PACKET_ID_REQUEST_PROTOCOL_VERSION + +### Request [Size: 4] + +The client uses this ID to request the server's highest supported protocol version as well as to indicate to the server the client's highest supported protocol version. The request contains a single `unsigned int`, size 4, containing the client's highest supported protocol version. + +### Response [Size: 4] + +The server responds to this request with a single `unsigned int`, size 4, containing the server's highest supported protocol version. If the server is using protocol version 0, it will not send a response. If no response is received, assume the server's highest supported protocol version is version 0. + +## NET_PACKET_ID_SET_CLIENT_NAME + +### Client Only [Size: Variable] + +The client uses this ID to send the client's null-terminated name string to the server. The size of the packet is the size of the string including the null terminator. In C, this is strlen() + 1. There is no response from the server for this packet. + +## NET_PACKET_ID_DEVICE_LIST_UPDATED + +### Server Only [Size: 0] + +The server uses this ID to notify a client that the server's device list has been updated. Upon receiving this packet, clients should synchronize their local device lists with the server by requesting size and controller data again. This packet contains no data. + +## NET_PACKET_ID_REQUEST_RESCAN_DEVICES + +### Client Only [Size: 0] + +The client uses this ID to request the server rescan its devices. + +## NET_PACKET_ID_REQUEST_PROFILE_LIST + +### Request [Size: 0] + +The client uses this ID to request the server's profile list. The request contains no data. + +### Response [Size: Variable] + +The server responds to this request with a data block. The format of the block is shown below. + +| Size | Format | Name | Protocol Version | Description | +| -------- | -------------------------- | ------------ | ---------------- | -------------------------------------------------------------------------------- | +| 4 | unsigned int | data_size | 2 | Size of all data in packet | +| 2 | unsigned short | num_profiles | 2 | Number of profiles on server | +| Variable | Profile Data[num_profiles] | profiles | 2 | See [Profile Data](#profile-data) block format table. Repeat num_profiles times | + +## Profile Data + +The profile data block represents the information of one profile. This data block was introduced in protocol version 2. + +| Size | Format | Name | Protocol Version | Description | +| ---------------- | ---------------------- | ---------------- | ---------------- | --------------------------------------------------------- | +| 2 | unsigned short | profile_name_len | 2 | Length of profile name string, including null termination | +| profile_name_len | char[profile_name_len] | profile_name | 2 | Profile name string value, including null termination | + +## NET_PACKET_ID_REQUEST_SAVE_PROFILE + +### Client Only [Size: Variable] + +The client uses this ID to command the server to save the current configuration to a profile. It passes the name of the profile to save as a null-terminated string. The size of the packet is the size of the string including the null terminator. In C, this is strlen() + 1. There is no response from the server for this packet. + +## NET_PACKET_ID_REQUEST_LOAD_PROFILE + +### Client Only [Size: Variable] + +The client uses this ID to command the server to load the given profile. It passes the name of the profile to load as a null-terminated string. The size of the packet is the size of the string including the null terminator. In C, this is strlen() + 1. There is no response from the server for this packet. + +Calling this function will not actually update the controllers. Instead, the controller states will be updated from the profile on the server side. After sending this request, the client should re-request all controller states from the server so that the client controller states match the server states loaded from the profile. After requesting all of the controller data, the client shall call UpdateMode() on all controllers to apply the updated state. + +## NET_PACKET_ID_REQUEST_DELETE_PROFILE + +### Client Only [Size: Variable] + +The client uses this ID to command the server to delete the given profile. It passes the name of the profile to delete as a null-terminated string. The size of the packet is the size of the string including the null terminator. In C, this is strlen() + 1. There is no response from the server for this packet. + +## NET_PACKET_ID_REQUEST_PLUGIN_LIST + +### Request [Size: 0] + +The client uses this ID to request the server's plugin list. The request contains no data. + +### Response [Size: Variable] + +The server responds to this request with a data block. The format of the block is shown below. + +| Size | Format | Name | Protocol Version | Description | +| -------- | ------------------------ | ----------- | ---------------- | ----------------------------------------------------------------------------- | +| 4 | unsigned int | data_size | 4 | Size of all data in packet | +| 2 | unsigned short | num_plugins | 4 | Number of plugins on server | +| Variable | Plugin Data[num_plugins] | plugins | 4 | See [Plugin Data](#plugin-data) block format table. Repeat num_plugins times | + +## Plugin Data + +The plugin data block represents the information of one plugin. This data block was introduced in protocol version 4. + +| Size | Format | Name | Protocol Version | Description | +| ---------------------- | ---------------------------- | ----------------------- | ---------------- | --------------------------------------------------------------- | +| 2 | unsigned short | plugin_name_len | 4 | Length of plugin name string, including null termination | +| plugin_name_len | char[plugin_name_len] | plugin_name | 4 | Plugin name string value, including null termination | +| 2 | unsigned short | plugin_description_len | 4 | Length of plugin description string, including null termination | +| plugin_description_len | char[plugin_description_len] | plugin_description | 4 | Plugin description string value, including null termination | +| 2 | unsigned short | plugin_version_len | 4 | Length of plugin version string, including null termination | +| plugin_version_len | char[plugin_version_len] | plugin_version | 4 | Plugin version string value, including null termination | +| 4 | unsigned int | plugin_index | 4 | Plugin index value | +| 4 | unsigned int | plugin_protocol_version | 4 | Plugin protocol version value | + +## NET_PACKET_ID_PLUGIN_SPECIFIC + +### Request [Size: Variable] + +This packet is used to send data to a plugin. The `pkt_dev_idx` field in the header specifies which plugin to send to and corresponds to the `plugin_index` field in the plugin list. The first 4 bytes of the data is the plugin packet type, the rest of the packet is plugin-specific. + +List of plugins that currently support this: + +- [Effects plugin](https://gitlab.com/OpenRGBDevelopers/OpenRGBEffectsPlugin/-/blob/master/SDK.md) + +### Response [Size: Variable] + +The response is optionally generated by the plugin. The data in the packet is plugin-specific. + +## NET_PACKET_ID_RGBCONTROLLER_RESIZEZONE + +### Client Only [Size: 8] + +The client uses this ID to call the ResizeZone() function of an RGBController device. The packet data contains a data block. The format of the block is shown below. The `pkt_dev_idx` of this request's header indicates which controller you are calling ResizeZone() on. + +| Size | Format | Name | Description | +| ---- | ------ | -------- | -------------------- | +| 4 | int | zone_idx | Zone index to resize | +| 4 | int | new_size | New size of the zone | + +## NET_PACKET_ID_RGBCONTROLLER_CLEARSEGMENTS + +### Client Only [Size: 4] + +The client uses this ID to call the ClearSegments() function of an RGBController device. The packet contains the index of the zone to clear segments on, type int (size 4). The `pkt_dev_idx` of this request's header indicates which controller you are calling ClearSegments() on. + +## NET_PACKET_ID_RGBCONTROLLER_ADDSEGMENT + +### Client Only [Size: Variable] + +The client uses this ID to call the AddSegment() function of an RGBController device. The packet contains a data block. The format of the block is shown below. The `pkt_dev_idx` of this request's header indicates which controller you are calling AddSegment() on. + +| Size | Format | Name | Description | +| ---------------- | ---------------------- | ---------------- | --------------------------------------------------------- | +| 4 | unsigned int | data_size | Size of all data in packet | +| 4 | unsigned int | zone_idx | Zone index to add segment to | +| Variable | Segment Data | segment | See [Segment Data](#segment-data) block format table. | + +## NET_PACKET_ID_RGBCONTROLLER_UPDATELEDS + +### Client Only [Size: Variable] + +The client uses this ID to call the UpdateLEDs() function of an RGBController device. The packet data contains a data block. The format of the block is shown below. The `pkt_dev_idx` of this request's header indicates which controller you are calling UpdateLEDs() on. + +| Size | Format | Name | Description | +| -------------- | -------------------- | ---------- | ----------------------------------- | +| 4 | unsigned int | data_size | Size of all data in packet | +| 2 | unsigned short | num_colors | Number of color values in packet | +| 4 * num_colors | RGBColor[num_colors] | led_color | Color values for each LED in device | + +## NET_PACKET_ID_RGBCONTROLLER_UPDATEZONELEDS + +### Client Only [Size: Variable] + +The client uses this ID to call the UpdateZoneLEDs() function of an RGBController device. The packet data contains a data block. The format of the data block is shown below. The `pkt_dev_idx` of this request's header indicates which controller you are calling UpdateZoneLEDs() on. + +| Size | Format | Name | Description | +| -------------- | -------------------- | ---------- | --------------------------------- | +| 4 | unsigned int | data_size | Size of all data in packet | +| 4 | unsigned int | zone_idx | Zone index to update | +| 2 | unsigned short | num_colors | Number of color values in packet | +| 4 * num_colors | RGBColor[num_colors] | led_color | Color values for each LED in zone | + +## NET_PACKET_ID_RGBCONTROLLER_UPDATESINGLELED + +### Client Only [Size: 8] + +The client uses this ID to call the UpdateSingleLED() function of an RGBController device. The packet data contains a data block. The format of the data block is shown below. The `pkt_dev_idx` of this request's header indicates which controller you are calling UpdateSingleLED() on. + +| Size | Format | Name | Description | +| ---- | -------- | --------- | ----------- | +| 4 | int | led_idx | LED index | +| 4 | RGBColor | led_color | LED color | + +## NET_PACKET_ID_RGBCONTROLLER_SETCUSTOMMODE + +### Client Only [Size: 0] + +The client uses this ID to call the SetCustomMode() function of an RGBController device. The packet contains no data. The `pkt_dev_idx` of this request's header indicates which controller you are calling SetCustomMode() on. + +## NET_PACKET_ID_RGBCONTROLLER_UPDATEMODE + +### Client Only [Size: Variable] + +The client uses this ID to call the UpdateMode() function of an RGBController device. The packet contains a data block. The format of the data block is shown below. The `pkt_dev_idx` of this request's header indicates which controller you are calling UpdateMode() on. + +| Size | Format | Name | Protocol Version | Description | +| ------------------- | ------------------------- | ------------------- | ---------------- | ------------------------------------------------------ | +| 4 | unsigned int | data_size | 0 | Size of all data in packet | +| 4 | int | mode_idx | 0 | Mode index to update | +| Variable | Mode Data | mode | 0 | See [Mode Data](#mode-data) block format table. | + +## NET_PACKET_ID_RGBCONTROLLER_SAVEMODE + +### Client Only [Size: Variable] + +The client uses this ID to call the SaveMode() function of an RGBController device. The packet contains a data block. The format of the data block is the same as for [NET_PACKET_ID_RGBCONTROLLER_UPDATEMODE](#net_packet_id_rgbcontroller_updatemode). The `pkt_dev_idx` of this request's header indicates which controller you are calling SaveMode() on. diff --git a/Documentation/RGBControllerAPI.md b/Documentation/RGBControllerAPI.md new file mode 100644 index 0000000..57e67ff --- /dev/null +++ b/Documentation/RGBControllerAPI.md @@ -0,0 +1,325 @@ +# RGBController API + +Device support in OpenRGB can be broken down into three major components. + +* Controller +* Detector +* RGBController + +## **Controller** + +A device's Controller class is a free-form class that provides whatever functionality is necessary to communicate with a device. This class should implement functions to send control packets to a device and receive information packets from a device. It should provide the capability to set device colors and modes. The Controller header file should provide defined constants for mode, speed, and other control values specific to the device's protocol. If possible, this class should provide the capability to retrieve firmware version and serial number information from the device. This class can also provide additional device protocol functionality even if it goes unused in OpenRGB currently. For instance, you may provide functions for controlling mouse DPI, polling rate, fan speed, or any other device-specific capability you want. If OpenRGB ever implements these extra functions in the future, having them implemented already in the Controller will make that easier. + +The Controller class files are kept in the Controllers/ folder. + +## **Detector** + +A device's Detector function scans the system's interfaces to see if a particular device (Controller/RGBController) exists. Several types of detectors exist and are listed below. Each detector type is passed different arguments based on the interface it is detecting. The REGISTER_DETECTOR macros are used to register a detector function with the ResourceManager which is responsible for calling detector functions at detection time. Detector functions are then responsible for creating instances of Controllers and RGBControllers and registering them with the ResourceManager by calling the `ResourceManager::RegisterRGBController` interface. + +HID Detectors + +HID (Human Interface Device) is the most common interface for USB devices with RGB capabilities, especially for peripherals such as keyboards and mice. While it is usually used over USB, HID can also be used over Bluetooth. The `hidapi` library is used for interfacing with HID devices. The following detector formats can be registered: + +```C++ +REGISTER_HID_DETECTOR("HID Detector Name", DetectHIDDevicesFunction, HID_VID, HID_PID); +REGISTER_HID_DETECTOR_I("HID Detector Name", DetectHIDDevicesFunction, HID_VID, HID_PID, HID_INTERFACE); +REGISTER_HID_DETECTOR_IP("HID Detector Name", DetectHIDDevicesFunction, HID_VID, HID_PID, HID_INTERFACE, HID_PAGE); +REGISTER_HID_DETECTOR_IPU("HID Detector Name", DetectHIDDevicesFunction, HID_VID, HID_PID, HID_INTERFACE, HID_PAGE, HID_USAGE); +REGISTER_HID_DETECTOR_P("HID Detector Name", DetectHIDDevicesFunction, HID_VID, HID_PID, HID_PAGE); +REGISTER_HID_DETECTOR_PU("HID Detector Name", DetectHIDDevicesFunction, HID_VID, HID_PID, HID_PAGE, HID_USAGE); +``` + +The I/IP/IPU/P/PU variants add filtering for specific HID interfaces, pages, and usages as many HID devices expose multiple interfaces and not all are used for RGB control. + +I2C/SMBus Detectors + +I2C (Inter-Integrated Circuit), or SMBus (System Management Bus, a compatible subset of I2C), is the second most common interface used by RGB devices and is used for on-board RGB on certrain motherboards, most graphics cards, and all RAM modules. Each I2C device has a 7-bit address. As I2C does not offer a standardized means of identifying a device on the bus, we have several different options for detecting I2C devices that can narrow down the search to a specific I2C bus. + +```C++ +REGISTER_I2C_DETECTOR("I2C Detector Name", DetectI2CDevicesFunction); +REGISTER_I2C_DIMM_DETECTOR("I2C Detector Name", DetectI2CDevicesFunction, JEDEC_ID, DIMM_TYPE); +REGISTER_I2C_PCI_DETECTOR("I2C Detector Name", DetectI2CDevicesFunction, PCI_VEN, PCI_DEV, PCI_SUBVEN, PCI_SUBDEV, I2C_ADDR); +``` + +The standard version of the I2C detector calls the detector function with a vector of all available I2C buses. The detector can then perform any chip specific detection necessary to determine if the device exists on any of the given buses. Only use this version of the detector if the DIMM or PCI variants are not suitable for your device. There are additional macros that can be used to narrow down I2C bus detection such as `IF_MOBO_SMBUS` for motherboard buses and `IF_DRAM_SMBUS` for DRAM buses. + +The DIMM version of the detector can be used to filter for specific DRAM modules using SPD information. Only the I2C bus for the DRAM will be provided and the detector will only be called if the JEDEC ID and DIMM type match. + +The PCI version of the detector can be used to filter for I2C devices on specific PCI cards, usually graphics cards. The detector will only be called for I2C buses with matching PCI IDs. The detector can also provide a specific address, though it is possible for the detector function to ignore this address if more complex address determination is needed. + +Generic Detectors + +The generic detector type is used for any device that doesn't fit into one of the previous detection types. This detector is frequently used for manually configured devices such as network and serial port devices. It is also used for USB devices that cannot be accessed via `hidapi` or serial and instead requiring direct USB access via `libusb`. + +```C++ +REGISTER_DETECTOR("Generic Detector Name", DetectDevicesFunction); +``` + +The Detector files are kept in the Controllers/ folder. + +## **RGBController** + +OpenRGB uses an internal API called RGBController to standardize the interface to RGB devices from multiple vendors and categories. This API uses vectors to describe each device. This API is implemented as an RGBController class that is inherited by each implementation, for example the RGBController_CorsairPeripheral is defined like so: + +```C++ +#include "RGBController.h" + +class RGBController_CorsairPeripheral : public RGBController +{ +``` + +The RGBController files for a controller implementation are kept in the Controllers/ folder alongside the Controller and Detector files. + +The RGBController class specification contains the following: + +* Device Name +* Device Vendor +* Device Description +* Device Version +* Device Serial +* Device Location +* Vector of LEDs +* Vector of Zones +* Vector of Modes +* Vector of Colors (32-bit 0x00BBGGRR format) +* Device Type (enum) +* Active mode index +* Vector of LED Alternate Names +* Controller Flags + +### Device Types + +| Value | Description | +| ----- | ------------- | +| 0 | Motherboard | +| 1 | DRAM | +| 2 | GPU | +| 3 | Cooler | +| 4 | LED Strip | +| 5 | Keyboard | +| 6 | Mouse | +| 7 | Mousemat | +| 8 | Headset | +| 9 | Headset Stand | +| 10 | Gamepad | +| 11 | Light | +| 12 | Speaker | +| 13 | Virtual | +| 14 | Storage | +| 15 | Case | +| 16 | Microphone | +| 17 | Accessory | +| 18 | Keypad | +| 19 | Laptop | +| 20 | Monitor | +| 21 | Unknown | + +Additional device types may be added in the future. They are added after the last known device type. Anything out of range should be considered Unknown. + +### Controller Flags + +| Controller Flags Bit | Name | Description | +| -------------------- | ------- | --------------------------------------------------- | +| 0 | Local | Controller is provided by this OpenRGB instance | +| 1 | Remote | Controller is provided by a remote OpenRGB instance | +| 2 | Virtual | Controller is virtual (not a physical device) | + +### LED Alternate Names + +The LED Altrernate Names vector can override the base name of an LED. The intended use case for this field is providing regional key names for non-English keyboard layouts. The base key names should always be provided in English QWERYY layout for positional mapping to work on certain SDK applications, so the alternate names field can override the base name to provide the correct key name for the localized layout without disrupting SDK application mapping. If not overriding any LED names, this vector can be left empty. If only overriding certain LED names, those not being overridden can be empty strings. If used, the length of this vector must equal the length of the LEDs vector. + +## LEDs + +The LED structure contains information about an LED. + +* LED Name +* LED Value + +The Value has no defined functionality in the RGBController API and is provided for implementation-specific use. You can use this field to associate implementation-specific data with an LED. + +## Zones + +The Zone structure contains information about a zone. A zone is a logical grouping of LEDs defined by the RGBController implementation. LEDs in a zone must be contiguous in the RGBController's LEDs/Colors vectors. + +* Zone Name +* Zone Type +* LED pointer +* Color pointer +* Start Index +* LED Count +* Minimum number of LEDs +* Maximum number of LEDs +* Matrix map pointer +* Vector of segments +* Zone Flags + +The LED pointer and Color pointer point to the first LED/Color in the RGBController's LEDs/Colors vector associated with this zone. The Start Index is the index to the same LED/Color in the vectors. + +The LED count is the number of LEDs in the zone. For zones with a fixed number of LEDs, the count, min, and max values should all be equal. For zones with a user-adjustable number of LEDs, the count should be between the min and max values, inclusively. User-adjustable zones are most commonly used to represent addressable RGB (ARGB) controllers as the number of LEDs depends on what strips/devices are attached to the ARGB headers. The ResizeZone function in the RGBController API is used to resize the number of LEDs in the zone. The initial value should be zero for ARGB zones if the device does not provide a means to automatically determint the number of connected LEDs. + +### Zone Types + +The zone type enum defines the zone type. This describes the physical layout of the zone and can be used by software to generate appropriate effects for the zone. + +| Zone Type Value | Description | +| --------------- | ----------- | +| 0 | Single | +| 1 | Linear (1D) | +| 2 | Matrix (2D) | + +### Matrix Map + +Each zone has a matrix map pointer which allows an optional matrix map to be associated with the zone. The matrix map is used to provide positioning information about LEDs in a 2D grid. If a matrix map is not provided for a zone, the zone's matrix map pointer must be set to NULL. + +A matrix map has the following: + +* Height +* Width +* Map data pointer + +The height and width determine the size of the map data. The map data pointer should point to a data block of (Height * Width) unsigned 32-bit integers. This data can be accessed as if it were a Map[Y][X] 2D array. The values of the map are LED index values in the zone (so offset by Start Index from the RGBController's LEDs vector). If a spot in the matrix is unused and does not map to an LED, it should be set to 0xFFFFFFFF. + +### Segments + +Each Zone contains a vector of Segments. Segments can be used to divide a physical zone (such as an ARGB header) into multiple logical sub-zones, or segments. This is mainly used for ARGB zones with multiple components daisy-chained together. For example, segments can be used to group multiple rings on an ARGB fan or multiple daisy-chained fans connected to one header. If the device is capable of automatically detecting multiple components connected to a single output, the RGBController may create segments automatically during zone creation. Otherwise, leaving this vector empty will indicate that the zone contains no segments, though resizable zones allow the user to define their own segments. + +A segment contains the following: + +* Segment Name +* Segment Type (See Zone Type values) +* Start Index +* LED Count + +The Start Index is the index within the Zone where the Segment starts. The LED Count is the number of LEDs in the Segment. Care should be taken to ensure that the total number of LEDs across all segments equals the number of LEDs in the Zone and the start indices do not overlap. + +### Zone Flags + +The Zone Flags field is a bitfield with informational flags related to the Zone. + +| Zone Flags Bit | Name | Description | +| -------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| 0 | Resize Effects Only | This zone is resizable, but the size is only used for effects modes. The zone is treated as a single LED in the Colors vector for per-LED modes | + +## Modes + +Modes represent internal effects and have a name field that describes the effect. The mode's index in the vector is its ID. The Active Mode variable in the RGBController class specifies which mode is currently selected. A mode contains the following: + +* Mode Name +* Mode Value +* Mode Flags +* Minimum Speed +* Maximum Speed +* Minimum number of colors +* Maximum number of colors +* Speed Value +* Direction +* Color Mode +* Colors Vector + +The mode value is field is provided to hold an implementation-defined mode value. This is usually the mode's value in the hardware protocol. + +The mode flags field is a bitfield that contains information about what features a mode has. + +| Mode Flags Bit | Description | +| -------------- | ------------------------------------------------ | +| 0 | Mode has speed parameter | +| 1 | Mode has left/right direction parameter | +| 2 | Mode has up/down direction parameter | +| 3 | Mode has horizontal/vertical direction parameter | +| 4 | Mode has brightness parameter | +| 5 | Mode has per-LED color settings | +| 6 | Mode has mode specific color settings | +| 7 | Mode has random color option | + +The mode minimum and maximum speed fields should be set to the implementation-specific minimum and maximum speed values for the given mode if the mode supports speed control. The mode speed value field will be set between the minimum and maximum value, inclusively. The minimum speed may be a greater numerical value than the maximum speed if your device's speed adjustment is inverted (usually because the device takes a delay period rather than a speed value). + +The mode minimum and maximum number of colors fields should be used if the mode supports mode-specific color settings. These determine the size range of the mode's Colors vector. If the mode has a fixed number of colors, the minimum and maximum should be equal. Mode-specific colors are used when a mode has one or more configurable colors but these colors do not apply directly to individual LEDs. Example would be a breathing mode that cycles between one or more colors each breath pulse. A mode may have multiple color options available, for instance a breathing mode that can either use one or more defined colors or just cycle through random colors. The available color modes for a given mode are set with the flags. The selected color mode is set using the color mode field, which can be one of the following values. + +| Color Mode Value | Description | +| ---------------- | ------------------------------------------------------------------------------------------------- | +| 0 | None - this mode does not have configurable colors | +| 1 | Per-LED - this mode uses the RGBController's colors vector to set each LED to its specified color | +| 2 | Mode Specific - this mode has one or more configurable colors, but not individual LED control | +| 3 | Random - this mode can be switched to a random or cycling color palette | + +## Functions + +### `std::string GetName()` + +Returns the `name` string of the device. + +### `std::string GetVendor()` + +Returns the `vendor` string of the device. + +### `std::string GetDescription()` + +Returns the `description` string of the device. + +### `std::string GetVersion()` + +Returns the `version` string of the device. + +### `std::string GetSerial()` + +Returns the `serial` string of the device. + +### `std::string GetLocation()` + +Returns the `location` string of the device. + +### `std::string GetModeName(int mode)` + +Returns the `name` string of the given mode in the `modes` vector. + +### `std::string GetZoneName(int zone)` + +Returns the `name` string of the given zone in the `zones` vector. + +### `std::string GetLEDName(int led)` + +Returns the `name` string of the given LED in the `leds` vector. + +### `RGBColor GetLED(unsigned int led)` + +Returns the color value of the given LED in the `colors` vector. + +### `void SetLED(unsigned int led, RGBColor color)` + +Sets the color value of the given LED in the `colors` vector. + +### `void SetAllLEDs(RGBColor color)` + +Sets the color value of all LEDs in the `colors` vector. + +### `void SetAllZoneLEDs(int zone, RGBColor color)` + +Sets the color value of all LEDs in the given zone in the `colors` vector. + +### `int GetMode()` + +Returns the active mode index of the device. The returned int should line up with the `modes` vector. + +### `void SetMode(int mode)` + +Sets the active mode index of the device. The mode should be the index in the `modes` vector of the mode you wish to set. + +### `void SetCustomMode()` + +When called, the device should be put into its software-controlled mode. This differs between devices, but generally devices have a direct control or static effect mode. Ideally, this mode should not save to the device's internal Flash. This function sets up a device for software effect control. + +### `void UpdateLEDs()` + +Update all LEDs based on the `colors` vector. + +### `void UpdateZoneLEDs(int zone)` + +Update all LEDs in the given zone based on the `colors` vector. + +### `void UpdateSingleLED(int led)` + +Update a single LED based on the `colors` vector. + +### `void UpdateMode()` + +Update the mode based on the active mode index and the `modes` vector. diff --git a/Documentation/SMBusAccess.md b/Documentation/SMBusAccess.md new file mode 100644 index 0000000..07e76c4 --- /dev/null +++ b/Documentation/SMBusAccess.md @@ -0,0 +1,49 @@ +# SMBus Access + +This document details the process to set up SMBus/I2C access on supported operating systems. + +SMBus, or [System Management Bus](https://en.wikipedia.org/wiki/System_Management_Bus), is a low-level interface present on most PC motherboards. Some RGB control devices are attached via SMBus. These include all DDR4 and DDR5 RAM modules with integrated RGB lighting as well as the onboard lighting on several motherboards, mostly from the X370/Z270 and X470/Z370 generations. + +If you are not using RGB RAM and you are not using a motherboard from the X370/Z270 or X470/Z370 generation you can skip these steps and ignore the SMBus warning if it appears. + +SMBus is generally not meant to be accessed by user applications, but RGB software creates an exception to this rule. This means that some steps may be necessary to allow OpenRGB permission to access the SMBus interface. These steps are listed below. + +## Windows + + * On Windows, OpenRGB uses the [PawnIO](https://pawnio.eu/) driver to access the SMBus interface. You must install PawnIO by downloading and running its installer prior to using OpenRGB. + * **You must run the application as Administrator in order for PawnIO to be able to access SMBus. OpenRGB may be installed as a background service that runs with Administrator permissions.** + * Early versions of OpenRGB used [WinRing0](https://github.com/GermanAizek/WinRing0) and even earlier versions used [InpOut32](https://www.highrez.co.uk/downloads/inpout32/). These drivers are no longer used and should be removed to avoid warnings by anti-cheat and anti-virus software. You can uninstall Inpout32 by following the instructions [here](https://gitlab.com/CalcProgrammer1/OpenRGB/-/issues/669#note_461054255). + +## Linux + + 1. Install the `i2c-tools` package. + 2. Load the i2c-dev module: `sudo modprobe i2c-dev` + 3. Load the i2c driver for your chipset: + * Intel + * `sudo modprobe i2c-i801` + * AMD + * `sudo modprobe i2c-piix4` + * Nuvoton + * This interface is used alongside `i2c-i801` on some older ASUS Intel motherboards for the on-board lighting. + * `sudo modprobe i2c-nct6793` + * Note: The i2c-nct6793 driver must be installed separately, see [i2c-nct6793-dkms](https://gitlab.com/CalcProgrammer1/i2c-nct6793-dkms) + + * If you want the i2c modules to load automatically at boot, run the following: + 1. `sudo touch /etc/modules-load.d/i2c.conf` + 2. `sudo sh -c 'echo "i2c-dev" >> /etc/modules-load.d/i2c.conf'` + 3. Run the following based on which i2c drivers you loaded in the previous section: + * `sudo sh -c 'echo "i2c-i801" >> /etc/modules-load.d/i2c.conf'` + * `sudo sh -c 'echo "i2c-piix4" >> /etc/modules-load.d/i2c.conf'` + + * You will have to enable user access to the i2c devices if you don't run OpenRGB as root. + 1. List all SMBus controllers: `sudo i2cdetect -l` + 2. Note the number(s) for piix4 or i801 controllers. + 3. Give user access to those controllers. If you have not installed OpenRGB from a distribution package then most likely you need to install the udev rules manually. + + * Some Gigabyte/Aorus motherboards have an ACPI conflict with the SMBus controller. You can bypass this conflict by adding the `acpi_enforce_resources=lax` kernel parameter to your kernel command line. See the [Kernel Parameters](Documentation/KernelParameters.md) page for more information. + + * The [spd5118 kernel driver](https://docs.kernel.org/hwmon/spd5118.html) can claim certain I2C addresses for Kingston Fury DDR5 memory and thus prevent other kernel modules from accessing them. This is the case if the `i2cdetect` command prints the character string `UU` on the I2C bus responsible for the DRAM. A solution to this problem is to unload the `spd5118` kernel driver using `rmmod spd5118`. + +## MacOS + + * For Intel devices using a controller in the i801 family you have to download and install the [macUSPCIO driver](https://github.com/ShadyNawara/macUSPCIO/releases) diff --git a/Documentation/USBAccess.md b/Documentation/USBAccess.md new file mode 100644 index 0000000..6c9a7f9 --- /dev/null +++ b/Documentation/USBAccess.md @@ -0,0 +1,23 @@ +# USB Access + +This document details the process to set up USB access on supported operating systems. + +USB, or [Universal Serial Bus](https://en.wikipedia.org/wiki/USB) is the most common interface used to connect RGB devices to a PC. It can be used both externally, where a device has a cable which plugs into a USB port or motherboard header, or internally, where a device such as an RGB controller chip built into a motherboard is wired directly to the processor or chipset's USB interface. + +USB access permissions vary based on the type of device and the operating system. Some steps may be necessary to allow OpenRGB permission to access these devices. + +## Windows + + * Windows should not need any special setup to access USB devices. + * If a device does not get detected, try running OpenRGB as Administrator. + * Early versions of OpenRGB used the WinUSB driver, installed using Zadig. This is no longer required, and you need to uninstall the WinUSB driver if you previously installed it. You can uninstall the WinUSB driver by following [this guide](https://gitlab.com/CalcProgrammer1/OpenRGB/-/wikis/Frequently-Asked-Questions#i-installed-the-winusb-driver-for-a-device-and-i-wish-to-uninstall-it). + +## Linux + + * USB devices require [udev rules](/Documentation/UdevRules.md) to access as a normal user. + * Alternatively you can run OpenRGB as root to detect all USB devices. (Not recommended) + * USB based Gigabyte AORUS motherboards may also have an ACPI conflict. Please [add a kernel parameter](#kernel-parameters) to resolve this conflict. + +## MacOS + + * USB devices may require the Input Monitoring permission. You can add OpenRGB in System Preferences > Security & Privacy > Privacy. diff --git a/Documentation/UdevRules.md b/Documentation/UdevRules.md new file mode 100644 index 0000000..f8bc711 --- /dev/null +++ b/Documentation/UdevRules.md @@ -0,0 +1,16 @@ +# Udev Rules + +On Linux, OpenRGB provides a udev rules file to configure access permissions to supported devices. + +If you install OpenRGB through a distribution-specific package, whether provided by your distribution's official repositories, from packages downloaded from OpenRGB's website or GitLab CI, or from building packages yourself, the udev rules should be installed as part of that package. You should not need to manually install them. + +If you are using OpenRGB compiled from source (not as part of a package), using OpenRGB as an AppImage, or using OpenRGB from Flatpak, you will need to install the udev rules manually. + +## Installation + + * If you have installed OpenRGB from a package then latest udev rules are installed locally at `/usr/lib/udev/rules.d/60-openrgb.rules` + * Udev rules are built from the source at compile time. When building locally they are installed with the `make install` step to `/usr/lib/udev/rules.d/60-openrgb.rules` + * If you need to install the udev rules file manually you can also download the [latest compiled udev rules](https://gitlab.com/CalcProgrammer1/OpenRGB/-/jobs/artifacts/master/raw/60-openrgb.rules?job=Linux+amd64+AppImage&inline=false) from Gitlab. + - Copy this 60-openrgb.rules file to `/usr/lib/udev/rules.d/` or to `/etc/udev/rules.d/` if you're on an immutable system. + - Then reload rules with `sudo udevadm control --reload-rules && sudo udevadm trigger` + * There is also a [udev rules installation script available at openrgb.org](https://openrgb.org/udev.html). diff --git a/KeyboardLayoutManager/KeyboardLayoutManager.cpp b/KeyboardLayoutManager/KeyboardLayoutManager.cpp new file mode 100644 index 0000000..efca2c1 --- /dev/null +++ b/KeyboardLayoutManager/KeyboardLayoutManager.cpp @@ -0,0 +1,1097 @@ +/*---------------------------------------------------------*\ +| KeyboardLayoutManager.cpp | +| | +| Helper library to produce keyboard layouts | +| | +| Chris M (Dr_No) 04 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "KeyboardLayoutManager.h" + +const char* KLM_CLASS_NAME = "KLM"; +const char* KEYBOARD_NAME_DEFAULT = "DEFAULT "; +const char* KEYBOARD_NAME_ISO = "ISO "; +const char* KEYBOARD_NAME_ANSI = "ANSI "; +const char* KEYBOARD_NAME_JIS = "JIS"; +const char* KEYBOARD_NAME_AZERTY = "AZERTY"; +const char* KEYBOARD_NAME_QWERTY = "QWERTY"; +const char* KEYBOARD_NAME_QWERTZ = "QWERTZ"; +const char* KEYBOARD_NAME_ABNT2 = "ABNT2"; + +const char* KEYBOARD_NAME_FULL = "Full 104 key "; +const char* KEYBOARD_NAME_TKL = "Tenkeyless "; +const char* KEYBOARD_NAME_SIXTY = "Sixty percent "; +const char* KEYBOARD_NAME_SEVENTY_FIVE = "Seventy Five percent "; + +const char* LOG_MSG_EMPTY = "empty "; +const char* LOG_MSG_UNUSED_KEY = "'unused' key"; +const char* LOG_MSG_SHIFTING_RIGHT = ", shifting keys right"; +const char* LOG_MSG_CREATED_NEW = "[%s] Created new %s%s with %d rows and %d columns containing %d keys"; +const char* LOG_MSG_INSERT_BEFORE = "[%s] Inserting %s before %s @ %02d, %02d%s"; +const char* LOG_MSG_MISSING_OPCODE = "[%s] Error: Opcode %d not found for %s @ %02d, %02d"; + +/*-------------------------------------------------------------------------*\ +| Keyboard Base Maps | +| | +| The following maps define the following standardized sections of the | +| keyboard layout: | +| | +| *-----------------------------------* *-----------* | +| | Function Key Row (ESC, F1-F12) | | Extras | | +| *-----------------------------------* | | | +| | | | +| *-----------------------------------* | | *-----------* | +| | Main Key Block | | | | Num Pad | | +| | | | | | | | +| | | | | | | | +| *-----------------------------------* *-----------* *-----------* | +| | +| The base keymap for a given keyboard size is assembled by combining the | +| blocks used in that particular layout. | +| | +\*-------------------------------------------------------------------------*/ + +static const std::vector keyboard_zone_main = +{ + /*-----------------------------------------------------------------------------------------------------------------------------*\ + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-----------------------------------------------------------------------------------------------------------------------------*/ + { 0, 1, 0, 0, KEY_EN_BACK_TICK, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 1, 0, KEY_EN_1, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 2, 0, KEY_EN_2, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 3, 0, KEY_EN_3, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 4, 0, KEY_EN_4, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 5, 0, KEY_EN_5, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 6, 0, KEY_EN_6, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 7, 0, KEY_EN_7, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 8, 0, KEY_EN_8, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 9, 0, KEY_EN_9, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 10, 0, KEY_EN_0, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 11, 0, KEY_EN_MINUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 12, 0, KEY_EN_EQUALS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 13, 0, KEY_EN_BACKSPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 0, 0, KEY_EN_TAB, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 1, 0, KEY_EN_Q, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 2, 0, KEY_EN_W, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 3, 0, KEY_EN_E, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 4, 0, KEY_EN_R, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 5, 0, KEY_EN_T, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 6, 0, KEY_EN_Y, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 7, 0, KEY_EN_U, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 8, 0, KEY_EN_I, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 9, 0, KEY_EN_O, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 10, 0, KEY_EN_P, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 11, 0, KEY_EN_LEFT_BRACKET, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 12, 0, KEY_EN_RIGHT_BRACKET, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 13, 0, KEY_EN_ANSI_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 0, 0, KEY_EN_CAPS_LOCK, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 1, 0, KEY_EN_A, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 2, 0, KEY_EN_S, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 3, 0, KEY_EN_D, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 4, 0, KEY_EN_F, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 5, 0, KEY_EN_G, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 6, 0, KEY_EN_H, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 7, 0, KEY_EN_J, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 8, 0, KEY_EN_K, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 9, 0, KEY_EN_L, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 10, 0, KEY_EN_SEMICOLON, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 11, 0, KEY_EN_QUOTE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 12, 0, KEY_EN_POUND, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT }, + { 0, 3, 13, 0, KEY_EN_ANSI_ENTER, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 0, 0, KEY_EN_LEFT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 1, 0, KEY_EN_ISO_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 2, 0, KEY_EN_Z, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 3, 0, KEY_EN_X, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 4, 0, KEY_EN_C, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 5, 0, KEY_EN_V, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 6, 0, KEY_EN_B, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 7, 0, KEY_EN_N, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 8, 0, KEY_EN_M, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 9, 0, KEY_EN_COMMA, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 10, 0, KEY_EN_PERIOD, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 11, 0, KEY_EN_FORWARD_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 13, 0, KEY_EN_RIGHT_SHIFT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 0, 0, KEY_EN_LEFT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 1, 0, KEY_EN_LEFT_WINDOWS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 2, 0, KEY_EN_LEFT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 6, 0, KEY_EN_SPACE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 10, 0, KEY_EN_RIGHT_ALT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 11, 0, KEY_EN_RIGHT_FUNCTION, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 12, 0, KEY_EN_MENU, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 13, 0, KEY_EN_RIGHT_CONTROL, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, +}; + +static const std::vector keyboard_zone_fn_row = +{ + /*-----------------------------------------------------------------------------------------------------------------------------*\ + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-----------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 0, 0, KEY_EN_ESCAPE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 2, 0, KEY_EN_F1, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 3, 0, KEY_EN_F2, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 4, 0, KEY_EN_F3, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 5, 0, KEY_EN_F4, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 6, 0, KEY_EN_F5, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 7, 0, KEY_EN_F6, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 8, 0, KEY_EN_F7, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 9, 0, KEY_EN_F8, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 10, 0, KEY_EN_F9, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 11, 0, KEY_EN_F10, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 12, 0, KEY_EN_F11, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 13, 0, KEY_EN_F12, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, +}; + +static const std::vector keyboard_zone_extras = +{ + /*-----------------------------------------------------------------------------------------------------------------------------*\ + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-----------------------------------------------------------------------------------------------------------------------------*/ + { 0, 0, 14, 0, KEY_EN_PRINT_SCREEN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 15, 0, KEY_EN_SCROLL_LOCK, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 0, 16, 0, KEY_EN_PAUSE_BREAK, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 14, 0, KEY_EN_INSERT, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 15, 0, KEY_EN_HOME, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 16, 0, KEY_EN_PAGE_UP, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 14, 0, KEY_EN_DELETE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 15, 0, KEY_EN_END, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 16, 0, KEY_EN_PAGE_DOWN, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 15, 0, KEY_EN_UP_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 14, 0, KEY_EN_LEFT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 15, 0, KEY_EN_DOWN_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 16, 0, KEY_EN_RIGHT_ARROW, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, +}; + +static const std::vector keyboard_zone_numpad = +{ + /*-----------------------------------------------------------------------------------------------------------------------------*\ + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-----------------------------------------------------------------------------------------------------------------------------*/ + { 0, 1, 17, 0, KEY_EN_NUMPAD_LOCK, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 18, 0, KEY_EN_NUMPAD_DIVIDE, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 19, 0, KEY_EN_NUMPAD_TIMES, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 1, 20, 0, KEY_EN_NUMPAD_MINUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 17, 0, KEY_EN_NUMPAD_7, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 18, 0, KEY_EN_NUMPAD_8, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 19, 0, KEY_EN_NUMPAD_9, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 20, 0, KEY_EN_NUMPAD_PLUS, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 17, 0, KEY_EN_NUMPAD_4, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 18, 0, KEY_EN_NUMPAD_5, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 3, 19, 0, KEY_EN_NUMPAD_6, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 17, 0, KEY_EN_NUMPAD_1, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 18, 0, KEY_EN_NUMPAD_2, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 19, 0, KEY_EN_NUMPAD_3, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 4, 20, 0, KEY_EN_NUMPAD_ENTER, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 18, 0, KEY_EN_NUMPAD_0, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 5, 19, 0, KEY_EN_NUMPAD_PERIOD, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, +}; + +keyboard_keymap_overlay iso_azerty +{ + KEYBOARD_SIZE_FULL, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 3, 12, 0, KEY_EN_UNUSED, KEY_FR_ASTERIX, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 1, 0, KEY_EN_UNUSED, KEY_NORD_ANGLE_BRACKET, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_FR_SUPER_2, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 1, 0, KEY_EN_UNUSED, KEY_FR_AMPERSAND, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 2, 0, KEY_EN_UNUSED, KEY_FR_ACUTE_E, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 3, 0, KEY_EN_UNUSED, KEY_FR_DOUBLEQUOTE, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 4, 0, KEY_EN_UNUSED, KEY_EN_QUOTE, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 5, 0, KEY_EN_UNUSED, KEY_FR_LEFT_PARENTHESIS, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 6, 0, KEY_EN_UNUSED, KEY_EN_MINUS, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 7, 0, KEY_EN_UNUSED, KEY_FR_GRAVE_E, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 8, 0, KEY_EN_UNUSED, KEY_FR_UNDERSCORE, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 9, 0, KEY_EN_UNUSED, KEY_FR_CEDILLA_C, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 10, 0, KEY_EN_UNUSED, KEY_FR_GRAVE_A, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 11, 0, KEY_EN_UNUSED, KEY_FR_RIGHT_PARENTHESIS, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 1, 0, KEY_EN_UNUSED, KEY_EN_A, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 2, 0, KEY_EN_UNUSED, KEY_EN_Z, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 11, 0, KEY_EN_UNUSED, KEY_JP_CHEVRON, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 12, 0, KEY_EN_UNUSED, KEY_FR_DOLLAR, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 3, 1, 0, KEY_EN_UNUSED, KEY_EN_Q, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 3, 10, 0, KEY_EN_UNUSED, KEY_EN_M, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 3, 11, 0, KEY_EN_UNUSED, KEY_FR_GRAVE_U, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 2, 0, KEY_EN_UNUSED, KEY_EN_W, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 8, 0, KEY_EN_UNUSED, KEY_EN_COMMA, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 9, 0, KEY_EN_UNUSED, KEY_EN_SEMICOLON, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 10, 0, KEY_EN_UNUSED, KEY_JP_COLON, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 11, 0, KEY_EN_UNUSED, KEY_FR_EXCLAIMATION, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + } +}; + +keyboard_keymap_overlay ansi_qwerty +{ + KEYBOARD_SIZE_FULL, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 3, 12, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 4, 1, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + } +}; + +keyboard_keymap_overlay iso_qwerty +{ + KEYBOARD_SIZE_FULL, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 2, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + } +}; + +keyboard_keymap_overlay iso_qwertz +{ + KEYBOARD_SIZE_FULL, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 3, 12, 0, KEY_EN_UNUSED, KEY_EN_POUND, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 1, 0, KEY_EN_UNUSED, KEY_NORD_ANGLE_BRACKET, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_JP_CHEVRON, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 11, 0, KEY_EN_UNUSED, KEY_DE_ESZETT, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 12, 0, KEY_EN_UNUSED, KEY_EN_BACK_TICK, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 6, 0, KEY_EN_UNUSED, KEY_EN_Z, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 11, 0, KEY_EN_UNUSED, KEY_DE_DIAERESIS_U, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 12, 0, KEY_EN_UNUSED, KEY_EN_PLUS, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 3, 10, 0, KEY_EN_UNUSED, KEY_DE_DIAERESIS_O, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 3, 11, 0, KEY_EN_UNUSED, KEY_DE_DIAERESIS_A, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 2, 0, KEY_EN_UNUSED, KEY_EN_Y, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 11, 0, KEY_EN_UNUSED, KEY_EN_MINUS, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + } +}; + +keyboard_keymap_overlay jis +{ + KEYBOARD_SIZE_FULL, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 3, 12, 0, KEY_EN_RIGHT_BRACKET, KEY_EN_UNUSED, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 12, 0, KEY_EN_BACK_SLASH, KEY_EN_UNUSED, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 1, 12, 0, KEY_JP_CHEVRON, KEY_EN_UNUSED, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 11, 0, KEY_JP_AT, KEY_EN_UNUSED, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 12, 0, KEY_EN_LEFT_BRACKET, KEY_EN_UNUSED, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 3, 11, 0, KEY_JP_COLON, KEY_EN_UNUSED, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + } +}; + +keyboard_keymap_overlay abnt2 +{ + KEYBOARD_SIZE_FULL, + { + /*-------------------------------------------------------------------------------------------------------------------------------------*\ + | Edit Keys | + | Zone, Row, Column, Value, Name, Alternate Name, OpCode | + \*-------------------------------------------------------------------------------------------------------------------------------------*/ + { 0, 1, 0, 0, KEY_EN_UNUSED, KEY_EN_QUOTE, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 11, 0, KEY_EN_UNUSED, KEY_NORD_ACUTE_GRAVE, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 2, 12, 0, KEY_EN_UNUSED, KEY_EN_LEFT_BRACKET, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 3, 10, 0, KEY_EN_UNUSED, KEY_FR_CEDILLA_C, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 3, 11, 0, KEY_EN_UNUSED, KEY_BR_TILDE, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 3, 12, 0, KEY_EN_UNUSED, KEY_EN_RIGHT_BRACKET, KEYBOARD_OPCODE_ADD_ALT_NAME, }, + { 0, 4, 11, 0, KEY_EN_SEMICOLON, KEY_EN_UNUSED, KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT, }, + { 0, 2, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_SWAP_ONLY, }, + { 0, 4, 13, 0, KEY_EN_UNUSED, KEY_EN_UNUSED, KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT, }, + } +}; + +KeyboardLayoutManager::KeyboardLayoutManager(KEYBOARD_LAYOUT layout, KEYBOARD_SIZE size) : KeyboardLayoutManager(layout, size, {}) +{ +} + +KeyboardLayoutManager::KeyboardLayoutManager(KEYBOARD_LAYOUT layout, KEYBOARD_SIZE size, layout_values values) +{ + /*---------------------------------------------------------------------*\ + | Store given size bitfield | + \*---------------------------------------------------------------------*/ + physical_size = size; + + /*---------------------------------------------------------------------*\ + | If the given size is EMPTY, we are done. No keys need added | + \*---------------------------------------------------------------------*/ + if(physical_size == KEYBOARD_SIZE::KEYBOARD_SIZE_EMPTY) + { + LOG_INFO(LOG_MSG_CREATED_NEW, KLM_CLASS_NAME, name.c_str(), LOG_MSG_EMPTY, rows, cols, physical_size); + return; + } + + /*---------------------------------------------------------------------*\ + | Add sections to the keymap based on KEYBOARD_SIZE bitfield | + \*---------------------------------------------------------------------*/ + if(physical_size & KEYBOARD_ZONE_MAIN) + { + InsertKeys(keyboard_zone_main); + } + + if(physical_size & KEYBOARD_ZONE_FN_ROW) + { + InsertKeys(keyboard_zone_fn_row); + } + + if(physical_size & KEYBOARD_ZONE_EXTRA) + { + InsertKeys(keyboard_zone_extras); + } + + if(physical_size & KEYBOARD_ZONE_NUMPAD) + { + InsertKeys(keyboard_zone_numpad); + } + + /*---------------------------------------------------------------------*\ + | Add any values passed into the constructor before switching layouts | + | and declare a value set for any changes afterwards | + \*---------------------------------------------------------------------*/ + for(size_t key_idx = 0; key_idx < (unsigned int)values.default_values.size() && key_idx < keymap.size(); key_idx++) + { + keymap[key_idx].value = values.default_values[key_idx]; + } + + /*---------------------------------------------------------------------*\ + | Modify the base default QWERTY layout to the desired regional layout | + \*---------------------------------------------------------------------*/ + std::string tmp_name; + + switch(layout) + { + case KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_DEFAULT: + default: + tmp_name = KEYBOARD_NAME_DEFAULT; + break; + + case KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ANSI_QWERTY: + ChangeKeys(ansi_qwerty); + tmp_name = KEYBOARD_NAME_ANSI; + tmp_name.append(KEYBOARD_NAME_QWERTY); + break; + + case KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ISO_QWERTY: + ChangeKeys(iso_qwerty); + tmp_name = KEYBOARD_NAME_ISO; + tmp_name.append(KEYBOARD_NAME_QWERTY); + break; + + /*-------------------------------------------------*\ + | Non-English, non-QWERTY layouts are disabled | + | until proper translation feature is implemented | + \*-------------------------------------------------*/ + case KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ISO_AZERTY: + ChangeKeys(iso_azerty); + tmp_name = KEYBOARD_NAME_AZERTY; + break; + + case KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ISO_QWERTZ: + ChangeKeys(iso_qwertz); + tmp_name = KEYBOARD_NAME_QWERTZ; + break; + + case KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_JIS: + ChangeKeys(jis); + tmp_name = KEYBOARD_NAME_JIS; + break; + + case KEYBOARD_LAYOUT::KEYBOARD_LAYOUT_ABNT2: + ChangeKeys(abnt2); + tmp_name = KEYBOARD_NAME_ABNT2; + break; + } + + /*---------------------------------------------------------------------*\ + | If the regional layouts were passed in count() returns true before | + | attempting to swap keys. | + \*---------------------------------------------------------------------*/ + bool found_overlay = (bool)values.regional_overlay.count(layout); + + LOG_DEBUG("[%s] Regional overlay %d was %sfound.", KLM_CLASS_NAME, layout, (found_overlay) ? KEY_EN_UNUSED : "not "); + if(found_overlay) + { + LOG_DEBUG("[%s] Processing regional overlay for %s", KLM_CLASS_NAME, tmp_name.c_str()); + SwapKeys(values.regional_overlay.find(layout)->second); + } + + /*---------------------------------------------------------------------*\ + | Size specific fixes | + \*---------------------------------------------------------------------*/ + switch(size) + { + case KEYBOARD_SIZE::KEYBOARD_SIZE_SIXTY: + /*-------------------------------------------------------------*\ + | Remove the empty Function row and swap in the Escape key | + \*-------------------------------------------------------------*/ + name = KEYBOARD_NAME_SIXTY; + RemoveRow(0); + SwapKey(keyboard_zone_fn_row[0]); + break; + + case KEYBOARD_SIZE::KEYBOARD_SIZE_SEVENTY_FIVE: + name = KEYBOARD_NAME_SEVENTY_FIVE; + break; + + case KEYBOARD_SIZE::KEYBOARD_SIZE_TKL: + name = KEYBOARD_NAME_TKL; + break; + + case KEYBOARD_SIZE::KEYBOARD_SIZE_FULL: + name = KEYBOARD_NAME_FULL; + break; + + default: + /*-------------------------------------------------------------*\ + | If the keyboard size is not a standard size output | + | the combined number as a string | + \*-------------------------------------------------------------*/ + name = "Size ("; + name.append(std::to_string(size) + ") "); + } + + /*---------------------------------------------------------------------*\ + | Ensure rows and cols are accurate by updating dimensions | + \*---------------------------------------------------------------------*/ + UpdateDimensions(); + + LOG_INFO(LOG_MSG_CREATED_NEW, KLM_CLASS_NAME, name.c_str(), tmp_name.c_str(), rows, cols, keymap.size()); +} + +KeyboardLayoutManager::~KeyboardLayoutManager() +{ + +} + +void KeyboardLayoutManager::ChangeKeys(key_set edit_keys) +{ + OpCodeSwitch(edit_keys); +} + +void KeyboardLayoutManager::ChangeKeys(keyboard_keymap_overlay new_layout) +{ + OpCodeSwitch(new_layout.edit_keys); +} + +void KeyboardLayoutManager::ChangeKeys(keyboard_keymap_overlay_values new_layout) +{ + OpCodeSwitch(new_layout.edit_keys); +} + +void KeyboardLayoutManager::OpCodeSwitch(key_set change_keys) +{ + LOG_DEBUG("[%s] %d keys to edit", KLM_CLASS_NAME, change_keys.size()); + + for(size_t chg_key_idx = 0; chg_key_idx < (unsigned int)change_keys.size(); chg_key_idx++) + { + switch(change_keys[chg_key_idx].opcode) + { + case KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT: + InsertKey(change_keys[chg_key_idx]); + break; + + case KEYBOARD_OPCODE_SWAP_ONLY: + SwapKey(change_keys[chg_key_idx]); + break; + + case KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT: + RemoveKey(change_keys[chg_key_idx]); + break; + + case KEYBOARD_OPCODE_INS_SHFT_ADJACENT: + //TODO: Insert, then find next unused and remove shift left + //SwapKey(change_keys[chg_key_idx]); + break; + + case KEYBOARD_OPCODE_INSERT_ROW: + if(InsertRow(change_keys[chg_key_idx].row)) + { + SwapKey(change_keys[chg_key_idx]); + } + break; + + case KEYBOARD_OPCODE_REMOVE_ROW: + RemoveRow(change_keys[chg_key_idx].row); + break; + + case KEYBOARD_OPCODE_ADD_ALT_NAME: + AddAltName(change_keys[chg_key_idx]); + break; + + default: + LOG_DEBUG(LOG_MSG_MISSING_OPCODE, KLM_CLASS_NAME, change_keys[chg_key_idx].opcode, + change_keys[chg_key_idx].name, change_keys[chg_key_idx].row, change_keys[chg_key_idx].col); + } + } + + UpdateDimensions(); +} + +void KeyboardLayoutManager::InsertKey(keyboard_led ins_key) +{ + /*---------------------------------------------------------------------*\ + | Get the insertion point | + \*---------------------------------------------------------------------*/ + unsigned int ins_row = ins_key.row; + unsigned int ins_col = ins_key.col; + const char* ins_name = ins_key.name; + + unsigned int key_idx = 0; + + for(/*key_idx*/; key_idx < (unsigned int)keymap.size(); key_idx++) + { + /*---------------------------------------------------------------------*\ + | Search through all existing keys and determine where in the list to | + | insert the new key. Order is row first, then column. | + \*---------------------------------------------------------------------*/ + if((ins_row < keymap[key_idx].row) || ((ins_row == keymap[key_idx].row) && (ins_col <= keymap[key_idx].col))) + { + break; + } + } + + /*---------------------------------------------------------------------*\ + | Determine whether to update row shift or not | + \*---------------------------------------------------------------------*/ + bool update_row = true; + + /*---------------------------------------------------------------------*\ + | If the search reached the end, put the new key at the end of the list | + \*---------------------------------------------------------------------*/ + if(key_idx == (unsigned int)keymap.size()) + { + LOG_DEBUG(LOG_MSG_INSERT_BEFORE, KLM_CLASS_NAME, ins_name, "the end", ins_row, ins_col, KEY_EN_UNUSED); + keymap.push_back(ins_key); + update_row = false; + } + + /*---------------------------------------------------------------------*\ + | If inserting an empty key in the middle of the list, the key entry is | + | not actually added. Instead, increment the col field of all keys on | + | the same row after the inserted key. | + \*---------------------------------------------------------------------*/ + else if(strlen(ins_name) == 0) + { + LOG_DEBUG(LOG_MSG_INSERT_BEFORE, KLM_CLASS_NAME, LOG_MSG_UNUSED_KEY, keymap[key_idx].name, keymap[key_idx].row, keymap[key_idx].col, LOG_MSG_SHIFTING_RIGHT); + } + else + { + LOG_DEBUG(LOG_MSG_INSERT_BEFORE, KLM_CLASS_NAME, ins_name, keymap[key_idx].name, ins_row, ins_col, KEY_EN_UNUSED); + keymap.insert(keymap.begin() + key_idx, ins_key); + key_idx++; + } + + /*---------------------------------------------------------------------*\ + | If update_row is true, key at key_idx is not the end of the vector. | + | For the remaining keys, if the row is equal to the inserted key row, | + | shift 1 column right | + \*---------------------------------------------------------------------*/ + if(update_row) + { + for(/*key_idx*/; key_idx < (unsigned int)keymap.size(); key_idx++) + { + if((keymap[key_idx].row == ins_row) && (keymap[key_idx].col >= ins_col)) + { + keymap[key_idx].col++; + } + + if(keymap[key_idx].row > ins_row) + { + break; + } + } + } +} + +void KeyboardLayoutManager::InsertKeys(std::vector ins_keys) +{ + LOG_DEBUG("[%s] %d keys to insert", KLM_CLASS_NAME, ins_keys.size()); + + /*---------------------------------------------------------------------*\ + | Insert new keys one by one | + \*---------------------------------------------------------------------*/ + for(unsigned int ins_key_idx = 0; ins_key_idx < (unsigned int)ins_keys.size(); ins_key_idx++) + { + InsertKey(ins_keys[ins_key_idx]); + } + + /*---------------------------------------------------------------------*\ + | Ensure rows and cols are accurate by updating dimensions after insert | + \*---------------------------------------------------------------------*/ + UpdateDimensions(); +} + +void KeyboardLayoutManager::SwapKey(keyboard_led swp_key) +{ + /*---------------------------------------------------------------------*\ + | Get the swap point | + \*---------------------------------------------------------------------*/ + unsigned int swp_row = swp_key.row; + unsigned int swp_col = swp_key.col; + const char* swp_name = swp_key.name; + unsigned int swp_value = swp_key.value; + + /*---------------------------------------------------------------------*\ + | If the keymap is empty, insert the key | + \*---------------------------------------------------------------------*/ + if(keymap.size() == 0) + { + keymap.push_back(swp_key); + return; + } + + /*---------------------------------------------------------------------*\ + | Otherwise, loop through and either swap an existing entry or insert | + | a new entry if the given location does not already have a key present | + \*---------------------------------------------------------------------*/ + for(unsigned int key_idx = 0; key_idx < (unsigned int)keymap.size(); key_idx++) + { + /*---------------------------------------------------------------------*\ + | If the row and column are identical, we've found the swap location | + \*---------------------------------------------------------------------*/ + if((swp_row == keymap[key_idx].row) && (swp_col == keymap[key_idx].col)) + { + std::string tmp_name = (strlen(swp_name) == 0) ? LOG_MSG_UNUSED_KEY : swp_name; + LOG_DEBUG("[%s] Swapping in %s and %s out @ %02d, %02d", KLM_CLASS_NAME, tmp_name.c_str(), keymap[key_idx].name, swp_row, swp_col); + + /*---------------------------------------------------------------------*\ + | If the key to be swapped in is an unused key, we want to remove the | + | entry from the keymap rather than perform a swap | + \*---------------------------------------------------------------------*/ + if(strlen(swp_name) == 0) + { + keymap.erase(keymap.begin() + key_idx); + } + /*---------------------------------------------------------------------*\ + | Otherwise, update the entry at this position with the new name and | + | value | + \*---------------------------------------------------------------------*/ + else + { + keymap[key_idx].name = swp_name; + keymap[key_idx].value = swp_value; + } + break; + } + + /*---------------------------------------------------------------------*\ + | If the key row is greater than the swap key row OR the key row is | + | equal to the swap key row and the key column is greater than the swap | + | key column, we've gone past the swap location without a match. In | + | this situation, we need to insert the swap key into the empty location| + | without performing a shift right. | + \*---------------------------------------------------------------------*/ + if((keymap[key_idx].row > swp_row) + ||((keymap[key_idx].row == swp_row) && (keymap[key_idx].col > swp_col))) + { + /*---------------------------------------------------------------------*\ + | Only insert the new key if the new key is not unused | + \*---------------------------------------------------------------------*/ + if(strlen(swp_name) != 0) + { + LOG_DEBUG(LOG_MSG_INSERT_BEFORE, KLM_CLASS_NAME, swp_name, keymap[key_idx].name, swp_row, swp_col, KEY_EN_UNUSED); + if(key_idx == 0) + { + keymap.insert(keymap.begin(), swp_key); + } + else + { + keymap.insert(keymap.begin() + (key_idx - 1), swp_key); + } + } + break; + } + } +} + +void KeyboardLayoutManager::SwapKeys(std::vector swp_keys) +{ + LOG_DEBUG("[%s] %d keys to swap", KLM_CLASS_NAME, swp_keys.size()); + + /*---------------------------------------------------------------------*\ + | Swap keys one by one | + \*---------------------------------------------------------------------*/ + for(unsigned int swp_key_idx = 0; swp_key_idx < (unsigned int)swp_keys.size(); swp_key_idx++) + { + SwapKey(swp_keys[swp_key_idx]); + } +} + +void KeyboardLayoutManager::RemoveKey(keyboard_led rmv_key) +{ + /*---------------------------------------------------------------------*\ + | Get the remove point | + \*---------------------------------------------------------------------*/ + unsigned int rmv_row = rmv_key.row; + unsigned int rmv_col = rmv_key.col; + + /*---------------------------------------------------------------------*\ + | Loop through and find the entry to remove | + \*---------------------------------------------------------------------*/ + for(unsigned int key_idx = 0; key_idx < (unsigned int)keymap.size(); key_idx++) + { + /*---------------------------------------------------------------------*\ + | If the row and column are identical, we've found the swap location | + \*---------------------------------------------------------------------*/ + if((rmv_row == keymap[key_idx].row) && (rmv_col == keymap[key_idx].col)) + { + LOG_DEBUG("[%s] Removing %s @ %02d, %02d and shifting keys left", KLM_CLASS_NAME, keymap[key_idx].name, rmv_row, rmv_col); + keymap.erase(keymap.begin() + key_idx); + + for(/*key_idx*/; key_idx < (unsigned int)keymap.size(); key_idx++) + { + if(rmv_row == keymap[key_idx].row) + { + keymap[key_idx].col--; + } + else + { + break; + } + } + + break; + } + + if((rmv_row == keymap[key_idx].row) && (rmv_col < keymap[key_idx].col)) + { + LOG_DEBUG("[%s] Removing unused key @ %02d, %02d and shifting keys left", KLM_CLASS_NAME, rmv_row, rmv_col); + + for(/*key_idx*/; key_idx < (unsigned int)keymap.size(); key_idx++) + { + if(rmv_row == keymap[key_idx].row) + { + keymap[key_idx].col--; + } + else + { + break; + } + } + + break; + } + } +} + +bool KeyboardLayoutManager::InsertRow(uint8_t ins_row) +{ + /*---------------------------------------------------------------------*\ + | Check row is valid to Insert | + \*---------------------------------------------------------------------*/ + if(ins_row >= rows) + { + LOG_DEBUG("[%s] Inserting row %d failed as rows currently = %d", KLM_CLASS_NAME, ins_row, rows); + return false; + } + + /*---------------------------------------------------------------------*\ + | Loop through to find the first key in the row to insert | + \*---------------------------------------------------------------------*/ + unsigned int key_idx = 0; + + for(/*key_idx*/; key_idx < (unsigned int)keymap.size(); key_idx++) + { + if(ins_row <= keymap[key_idx].row) + { + break; + } + } + + LOG_DEBUG("[%s] Attempting to insert row %d before %s at index %d", + KLM_CLASS_NAME, ins_row, keymap[key_idx].name, key_idx); + /*---------------------------------------------------------------------*\ + | Loop through the remaining rows and adjust row number | + \*---------------------------------------------------------------------*/ + if(ins_row <= keymap[key_idx].row) + { + for(/*key_idx*/; key_idx < (unsigned int)keymap.size(); key_idx++) + { + keymap[key_idx].row++; + } + + LOG_DEBUG("[%s] Insert row %d successful", KLM_CLASS_NAME, ins_row); + } + return true; +} + +void KeyboardLayoutManager::RemoveRow(uint8_t rmv_row) +{ + /*---------------------------------------------------------------------*\ + | Check row is valid to remove | + \*---------------------------------------------------------------------*/ + if(rmv_row >= rows) + { + LOG_DEBUG("[%s] Removing row %d failed as rows currently = %d", KLM_CLASS_NAME, rmv_row, rows); + return; + } + + /*---------------------------------------------------------------------*\ + | Loop through and remove any keys in the row | + \*---------------------------------------------------------------------*/ + unsigned int key_idx = 0; + + while(key_idx < (unsigned int)keymap.size() && rmv_row >= keymap[key_idx].row) + { + if(rmv_row == keymap[key_idx].row) + { + LOG_DEBUG("[%s] Removing %s @ %02d, %02d from row %d", KLM_CLASS_NAME, keymap[key_idx].name, keymap[key_idx].row, keymap[key_idx].col, rmv_row); + keymap.erase(keymap.begin() + key_idx); + } + else + { + key_idx++; + } + } + + /*---------------------------------------------------------------------*\ + | Loop through the remaining rows and adjust row number | + \*---------------------------------------------------------------------*/ + if(rmv_row < keymap[key_idx].row) + { + for(/*key_idx*/; key_idx < (unsigned int)keymap.size(); key_idx++) + { + keymap[key_idx].row--; + } + + LOG_DEBUG("[%s] Remove row %d successful", KLM_CLASS_NAME, rmv_row); + } +} + +void KeyboardLayoutManager::AddAltName(keyboard_led key) +{ + /*---------------------------------------------------------------------*\ + | Get the edit point | + \*---------------------------------------------------------------------*/ + unsigned int edit_row = key.row; + unsigned int edit_col = key.col; + const char* edit_alt_name = key.alt_name; + + /*---------------------------------------------------------------------*\ + | Otherwise, loop through and find the edit location | + \*---------------------------------------------------------------------*/ + for(unsigned int key_idx = 0; key_idx < keymap.size(); key_idx++) + { + /*---------------------------------------------------------------------*\ + | If the row and column are identical, we've found the edit location | + \*---------------------------------------------------------------------*/ + if((edit_row == keymap[key_idx].row) && (edit_col == keymap[key_idx].col)) + { + /*---------------------------------------------------------------------*\ + | Update the entry at this position with the new translated name | + \*---------------------------------------------------------------------*/ + LOG_DEBUG("[%s] Adding alternate name %s to %s @ %02d, %02d", KLM_CLASS_NAME, edit_alt_name, keymap[key_idx].name, keymap[key_idx].row, keymap[key_idx].col); + keymap[key_idx].alt_name = edit_alt_name; + break; + } + } +} + +std::string KeyboardLayoutManager::GetName() +{ + return name; +} + +KEYBOARD_LAYOUT KeyboardLayoutManager::GetLayout() +{ + return layout; +} + +KEYBOARD_SIZE KeyboardLayoutManager::GetPhysicalSize() +{ + return physical_size; +} + +unsigned int KeyboardLayoutManager::GetKeyCount() +{ + return (unsigned int)keymap.size(); +} + +std::string KeyboardLayoutManager::GetKeyNameAt(unsigned int key_idx) +{ + if(key_idx < (unsigned int)keymap.size()) + { + return keymap[key_idx].name; + } + + return KEY_EN_UNUSED; +} + +std::string KeyboardLayoutManager::GetKeyNameAt(unsigned int row, unsigned int col) +{ + for(std::vector::iterator key = keymap.begin(); key != keymap.end(); ++key) + { + if(key->row == row && key->col == col) + { + return key->name; + } + } + + return KEY_EN_UNUSED; +} + +unsigned int KeyboardLayoutManager::GetKeyValueAt(unsigned int key_idx) +{ + if(key_idx < keymap.size()) + { + return keymap[key_idx].value; + } + + return -1; +} + +unsigned int KeyboardLayoutManager::GetKeyValueAt(unsigned int row, unsigned int col) +{ + for(std::vector::iterator key = keymap.begin(); key != keymap.end(); ++key) + { + if(key->row == row && key->col == col) + { + return key->value; + } + } + + return -1; +} + +std::string KeyboardLayoutManager::GetKeyAltNameAt(unsigned int key_idx) +{ + if(key_idx < keymap.size()) + { + return keymap[key_idx].alt_name; + } + + return KEY_EN_UNUSED; +} + +std::string KeyboardLayoutManager::GetKeyAltNameAt(unsigned int row, unsigned int col) +{ + for(std::vector::iterator key = keymap.begin(); key != keymap.end(); ++key) + { + if(key->row == row && key->col == col) + { + return key->alt_name; + } + } + + return KEY_EN_UNUSED; +} + +unsigned int KeyboardLayoutManager::GetRowCount() +{ + return rows; +} + +unsigned int KeyboardLayoutManager::GetColumnCount() +{ + return cols; +} + +void KeyboardLayoutManager::GetKeyMap(unsigned int* map_ptr) +{ + GetKeyMap(map_ptr, KEYBOARD_MAP_FILL_TYPE_INDEX, rows, cols); +} + +void KeyboardLayoutManager::GetKeyMap(unsigned int* map_ptr, KEYBOARD_MAP_FILL_TYPE fill_type) +{ + GetKeyMap(map_ptr, fill_type, rows, cols); +} + +void KeyboardLayoutManager::GetKeyMap(unsigned int* map_ptr, KEYBOARD_MAP_FILL_TYPE fill_type, uint8_t height = 0, uint8_t width = 0) +{ + unsigned int no_key = -1; + + /*-------------------------------------------------------------------------*\ + | If explicit dimensions are passed (non-zero), use them as-is. | + | Only fall back to internal dimensions when zero is passed. | + | This ensures we don't write beyond the caller's allocated buffer. | + \*-------------------------------------------------------------------------*/ + if(width == 0) + { + width = cols; + } + if(height == 0) + { + height = rows; + } + + for(unsigned int r = 0; r < height; r++) + { + unsigned int offset = r * width; + + for(unsigned int c = 0; c < width; c++) + { + map_ptr[offset + c] = no_key; + } + } + + for(unsigned int i = 0; i < (unsigned int)keymap.size(); i++) + { + /*---------------------------------------------------------------------*\ + | Skip keys that fall outside the requested map dimensions | + \*---------------------------------------------------------------------*/ + if(keymap[i].row >= height || keymap[i].col >= width) + { + continue; + } + + unsigned int offset = (keymap[i].row * width) + keymap[i].col; + switch(fill_type) + { + case KEYBOARD_MAP_FILL_TYPE_COUNT: + map_ptr[offset] = i; + break; + + case KEYBOARD_MAP_FILL_TYPE_VALUE: + map_ptr[offset] = keymap[i].value; + break; + + case KEYBOARD_MAP_FILL_TYPE_INDEX: + default: + map_ptr[offset] = offset; + break; + } + } +} + +void KeyboardLayoutManager::UpdateDimensions() +{ + /*---------------------------------------------------------------------*\ + | Compute max_row and max_col. | + \*---------------------------------------------------------------------*/ + uint8_t max_row = 0; + uint8_t max_col = 0; + + /*---------------------------------------------------------------------*\ + | Search through the keymap and find the maximum row and column values | + \*---------------------------------------------------------------------*/ + for(unsigned int key_idx = 0; key_idx < (unsigned int)keymap.size(); key_idx++) + { + if(keymap[key_idx].row > max_row) + { + max_row = keymap[key_idx].row; + } + if(keymap[key_idx].col > max_col) + { + max_col = keymap[key_idx].col; + } + } + + /*---------------------------------------------------------------------*\ + | The size is one greater than the highest row/column value | + \*---------------------------------------------------------------------*/ + rows = max_row + 1; + cols = max_col + 1; +} diff --git a/KeyboardLayoutManager/KeyboardLayoutManager.h b/KeyboardLayoutManager/KeyboardLayoutManager.h new file mode 100644 index 0000000..93c9309 --- /dev/null +++ b/KeyboardLayoutManager/KeyboardLayoutManager.h @@ -0,0 +1,158 @@ +/*---------------------------------------------------------*\ +| KeyboardLayoutManager.h | +| | +| Helper library to produce keyboard layouts | +| | +| Chris M (Dr_No) 04 Feb 2023 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBControllerKeyNames.h" + +extern const char* KLM_CLASS_NAME; +extern const char* KEYBOARD_NAME_FULL; +extern const char* KEYBOARD_NAME_TKL; +extern const char* KEYBOARD_NAME_SIXTY; +extern const char* KEYBOARD_NAME_SEVENTY_FIVE; +extern const char* LOG_MSG_UNUSED_KEY; + +enum KEYBOARD_ZONE_BITS +{ + KEYBOARD_ZONE_MAIN = ( 1 << 0 ), + KEYBOARD_ZONE_FN_ROW = ( 1 << 1 ), + KEYBOARD_ZONE_EXTRA = ( 1 << 2 ), + KEYBOARD_ZONE_NUMPAD = ( 1 << 3 ), +}; + +enum KEYBOARD_SIZE +{ + KEYBOARD_SIZE_EMPTY = 0, + KEYBOARD_SIZE_FULL = ( KEYBOARD_ZONE_MAIN | KEYBOARD_ZONE_FN_ROW | + KEYBOARD_ZONE_EXTRA | KEYBOARD_ZONE_NUMPAD ), + KEYBOARD_SIZE_TKL = ( KEYBOARD_ZONE_MAIN | KEYBOARD_ZONE_FN_ROW | KEYBOARD_ZONE_EXTRA ), + KEYBOARD_SIZE_SEVENTY_FIVE = ( KEYBOARD_ZONE_MAIN | KEYBOARD_ZONE_FN_ROW ), + KEYBOARD_SIZE_SIXTY = ( KEYBOARD_ZONE_MAIN ), +}; + +enum KEYBOARD_LAYOUT +{ + KEYBOARD_LAYOUT_DEFAULT = 0, + KEYBOARD_LAYOUT_ANSI_QWERTY, + KEYBOARD_LAYOUT_ISO_QWERTY, + KEYBOARD_LAYOUT_ISO_QWERTZ, + KEYBOARD_LAYOUT_ISO_AZERTY, + KEYBOARD_LAYOUT_JIS, + KEYBOARD_LAYOUT_ABNT2, +}; + +enum KEYBOARD_MAP_FILL_TYPE +{ + KEYBOARD_MAP_FILL_TYPE_COUNT, + KEYBOARD_MAP_FILL_TYPE_INDEX, + KEYBOARD_MAP_FILL_TYPE_VALUE, +}; + +enum KEYBOARD_OPCODE +{ + KEYBOARD_OPCODE_INSERT_SHIFT_RIGHT = 0, + KEYBOARD_OPCODE_SWAP_ONLY = 1, + KEYBOARD_OPCODE_REMOVE_SHIFT_LEFT = 2, + KEYBOARD_OPCODE_INS_SHFT_ADJACENT = 3, + KEYBOARD_OPCODE_INSERT_ROW = 4, + KEYBOARD_OPCODE_REMOVE_ROW = 5, + KEYBOARD_OPCODE_ADD_ALT_NAME = 6, +}; + +typedef struct +{ + std::uint8_t zone; + std::uint8_t row; + std::uint8_t col; + unsigned int value; + const char* name; + const char* alt_name; + KEYBOARD_OPCODE opcode; +} keyboard_led; + +typedef + std::vector key_set; + +typedef struct +{ + std::vector default_values; + std::map regional_overlay; +} layout_values; + +typedef struct +{ + KEYBOARD_SIZE base_size; + key_set edit_keys; +} keyboard_keymap_overlay; + +typedef struct +{ + KEYBOARD_SIZE base_size; + layout_values key_values; + key_set edit_keys; +} keyboard_keymap_overlay_values; + +class KeyboardLayoutManager +{ +public: + KeyboardLayoutManager(KEYBOARD_LAYOUT, KEYBOARD_SIZE); + KeyboardLayoutManager(KEYBOARD_LAYOUT, KEYBOARD_SIZE, layout_values values); + ~KeyboardLayoutManager(); + + void ChangeKeys(key_set edit_keys); + void ChangeKeys(keyboard_keymap_overlay new_layout); + void ChangeKeys(keyboard_keymap_overlay_values new_layout); + void UpdateDimensions(); + + std::string GetName(); + KEYBOARD_LAYOUT GetLayout(); + KEYBOARD_SIZE GetPhysicalSize(); + + unsigned int GetKeyCount(); + std::string GetKeyNameAt(unsigned int key_idx); + std::string GetKeyNameAt(unsigned int row, unsigned int col); + std::string GetKeyAltNameAt(unsigned int key_idx); + std::string GetKeyAltNameAt(unsigned int row, unsigned int col); + + unsigned int GetKeyValueAt(unsigned int key_idx); + unsigned int GetKeyValueAt(unsigned int row, unsigned int col); + + unsigned int GetRowCount(); + unsigned int GetColumnCount(); + + void GetKeyMap(unsigned int* map_ptr); + void GetKeyMap(unsigned int* map_ptr, KEYBOARD_MAP_FILL_TYPE fill_type); + void GetKeyMap(unsigned int* map_ptr, KEYBOARD_MAP_FILL_TYPE fill_type, + std::uint8_t height, std::uint8_t width); + +private: + void OpCodeSwitch(key_set change_keys); + void InsertKey(keyboard_led key); + void InsertKeys(std::vector keys); + bool InsertRow(std::uint8_t row); + void SwapKey(keyboard_led keys); + void SwapKeys(std::vector keys); + void RemoveKey(keyboard_led keys); + void RemoveRow(std::uint8_t row); + void AddAltName(keyboard_led key); + + KEYBOARD_LAYOUT layout; + KEYBOARD_SIZE physical_size; + std::string name = KLM_CLASS_NAME; + std::uint8_t rows = 0; + std::uint8_t cols = 0; + std::vector keymap; +}; + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0342160 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + OpenAuraSDK + Copyright (C) 2019 Adam Honse + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/LogManager.cpp b/LogManager.cpp new file mode 100644 index 0000000..72571a1 --- /dev/null +++ b/LogManager.cpp @@ -0,0 +1,510 @@ +/*---------------------------------------------------------*\ +| LogManager.cpp | +| | +| Manages log file and output to the console | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogManager.h" + +#include +#include +#include +#include +#include + +#include "filesystem.h" + +const char* LogManager::log_codes[] = {"FATAL:", "ERROR:", "Warning:", "Info:", "Verbose:", "Debug:", "Trace:", "Dialog:"}; + +const char* TimestampPattern = "%04d%02d%02d_%02d%02d%02d"; + +/*---------------------------------------------------------*\ +| Relies on the structure of the template above | +\*---------------------------------------------------------*/ +const char* TimestampRegex = "[0-9]{8}_[0-9]{6}"; + +LogManager::LogManager() +{ + base_clock = std::chrono::steady_clock::now(); + log_console_enabled = false; + log_file_enabled = true; +} + +LogManager* LogManager::get() +{ + static LogManager* _instance = nullptr; + static std::mutex instance_mutex; + std::lock_guard grd(instance_mutex); + + /*-----------------------------------------------------*\ + | Create a new instance if one does not exist | + \*-----------------------------------------------------*/ + if(!_instance) + { + _instance = new LogManager(); + } + + return _instance; +} + +unsigned int LogManager::getLoglevel() +{ + if(log_console_enabled) + { + return(LL_TRACE); + } + else + { + return(loglevel); + } +} + +void LogManager::configure(json config, const filesystem::path& defaultDir) +{ + std::lock_guard grd(entry_mutex); + + /*-----------------------------------------------------*\ + | If the log is not open, create a new log file | + \*-----------------------------------------------------*/ + if(!log_stream.is_open()) + { + /*-------------------------------------------------*\ + | If a limit is declared in the config for the | + | maximum number of log files, respect the limit | + | Log rotation will remove the files matching the | + | current "logfile", starting with the oldest ones | + | (according to the timestamp in their filename) | + | i.e. with the lexicographically smallest filename | + | 0 or less equals no limit (default) | + \*-------------------------------------------------*/ + int loglimit = 0; + if(config.contains("file_count_limit") && config["file_count_limit"].is_number_integer()) + { + loglimit = config["file_count_limit"]; + } + + if(config.contains("log_file")) + { + log_file_enabled = config["log_file"]; + } + + /*-------------------------------------------------*\ + | Default template for the logfile name | + | The # symbol is replaced with a timestamp | + \*-------------------------------------------------*/ + std::string logtempl = "OpenRGB_#.log"; + + if(log_file_enabled) + { + /*---------------------------------------------*\ + | If the logfile is defined in the | + | configuration, use the configured name | + \*---------------------------------------------*/ + if(config.contains("logfile")) + { + const json& logfile_obj = config["logfile"]; + if(logfile_obj.is_string()) + { + std::string tmpname = config["logfile"]; + if(!tmpname.empty()) + { + logtempl = tmpname; + } + } + } + /*---------------------------------------------*\ + | If the # symbol is found in the log file | + | name, replace it with a timestamp | + \*---------------------------------------------*/ + time_t t = time(0); + struct tm* tmp = localtime(&t); + char time_string[64]; + snprintf(time_string, 64, TimestampPattern, 1900 + tmp->tm_year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + + std::string logname = logtempl; + size_t oct = logname.find("#"); + if(oct != logname.npos) + { + logname.replace(oct, 1, time_string); + } + + /*---------------------------------------------*\ + | If the path is relative, use logs dir | + \*---------------------------------------------*/ + filesystem::path p = filesystem::u8path(logname); + if(p.is_relative()) + { + p = defaultDir / "logs" / logname; + } + filesystem::create_directories(p.parent_path()); + + /*---------------------------------------------*\ + | "Log rotation": remove old log files | + | exceeding the current configured limit | + \*---------------------------------------------*/ + rotate_logs(p.parent_path(), filesystem::u8path(logtempl).filename(), loglimit); + + /*---------------------------------------------*\ + | Open the logfile | + \*---------------------------------------------*/ + log_stream.open(p); + + /*---------------------------------------------*\ + | Print Git Commit info, version, etc. | + \*---------------------------------------------*/ + log_stream << " OpenRGB v" << VERSION_STRING << std::endl; + log_stream << " Commit: " << GIT_COMMIT_ID << " from " << GIT_COMMIT_DATE << std::endl; + log_stream << " Launched: " << time_string << std::endl; + log_stream << "====================================================================================================" << std::endl; + log_stream << std::endl; + } + } + + /*-----------------------------------------------------*\ + | Check loglevel configuration | + \*-----------------------------------------------------*/ + if(config.contains("loglevel")) + { + const json& loglevel_obj = config["loglevel"]; + + /*-------------------------------------------------*\ + | Set the log level if configured | + \*-------------------------------------------------*/ + if(loglevel_obj.is_number_integer()) + { + loglevel = loglevel_obj; + } + } + + /*-----------------------------------------------------*\ + | Check log console configuration | + \*-----------------------------------------------------*/ + if(config.contains("log_console")) + { + log_console_enabled = config["log_console"]; + } + + /*-----------------------------------------------------*\ + | Flush the log | + \*-----------------------------------------------------*/ + _flush(); +} + +void LogManager::_flush() +{ + /*-----------------------------------------------------*\ + | If the log is open, write out buffered messages | + \*-----------------------------------------------------*/ + if(log_stream.is_open()) + { + for(size_t msg = 0; msg < temp_messages.size(); ++msg) + { + if(temp_messages[msg]->level <= loglevel || temp_messages[msg]->level == LL_DIALOG) + { + /*-----------------------------------------*\ + | Put the timestamp here | + \*-----------------------------------------*/ + std::chrono::milliseconds counter = std::chrono::duration_cast(temp_messages[msg]->counted_second); + log_stream << std::left << std::setw(6) << counter.count() << "|"; + log_stream << std::left << std::setw(9) << log_codes[temp_messages[msg]->level]; + log_stream << temp_messages[msg]->buffer; + + if(print_source) + { + log_stream << " [" << temp_messages[msg]->filename << ":" << temp_messages[msg]->line << "]"; + } + + log_stream << std::endl; + } + } + + /*-------------------------------------------------*\ + | Clear temp message buffers after writing them out | + \*-------------------------------------------------*/ + temp_messages.clear(); + + /*-------------------------------------------------*\ + | Flush the stream | + \*-------------------------------------------------*/ + log_stream.flush(); + } +} + +void LogManager::flush() +{ + std::lock_guard grd(entry_mutex); + _flush(); +} + +void LogManager::_append(const char* filename, int line, unsigned int level, const char* fmt, va_list va) +{ + /*-----------------------------------------------------*\ + | If a critical message occurs, enable source | + | printing and set loglevel and verbosity to highest | + \*-----------------------------------------------------*/ + if(level == LL_FATAL) + { + print_source = true; + loglevel = LL_DEBUG; + verbosity = LL_DEBUG; + } + + /*-----------------------------------------------------*\ + | Create a new message | + \*-----------------------------------------------------*/ + PLogMessage mes = std::make_shared(); + + /*-----------------------------------------------------*\ + | Resize the buffer, then fill in the message text | + \*-----------------------------------------------------*/ + va_list va2; + va_copy(va2, va); + int len = vsnprintf(nullptr, 0, fmt, va); + mes->buffer.resize(len); + vsnprintf(&(mes->buffer[0]), len + 1, fmt, va2); + va_end(va2); + + /*-----------------------------------------------------*\ + | Fill in message information | + \*-----------------------------------------------------*/ + mes->level = level; + mes->filename = filename; + mes->line = line; + mes->counted_second = std::chrono::steady_clock::now() - base_clock; + + /*-----------------------------------------------------*\ + | If this is a dialog message, call the dialog show | + | callback | + \*-----------------------------------------------------*/ + if(level == LL_DIALOG) + { + for(size_t idx = 0; idx < dialog_show_callbacks.size(); idx++) + { + dialog_show_callbacks[idx](dialog_show_callback_args[idx], mes); + } + } + + /*-----------------------------------------------------*\ + | If the message is within the current verbosity, print | + | it on the screen | + | TODO: Put the timestamp here | + \*-----------------------------------------------------*/ + if(level <= verbosity || level == LL_DIALOG) + { + std::cout << mes->buffer; + if(print_source) + { + std::cout << " [" << mes->filename << ":" << mes->line << "]"; + } + std::cout << std::endl; + } + + /*-----------------------------------------------------*\ + | Add the message to the logfile queue | + \*-----------------------------------------------------*/ + temp_messages.push_back(mes); + + if(log_console_enabled) + { + all_messages.push_back(mes); + } + + /*-----------------------------------------------------*\ + | Flush the queues | + \*-----------------------------------------------------*/ + _flush(); +} + +std::vector LogManager::messages() +{ + return all_messages; +} + +void LogManager::clearMessages() +{ + all_messages.clear(); +} + +void LogManager::append(const char* filename, int line, unsigned int level, const char* fmt, ...) +{ + va_list va; + va_start(va, fmt); + + std::lock_guard grd(entry_mutex); + _append(filename, line, level, fmt, va); + + va_end(va); +} + +void LogManager::setLoglevel(unsigned int level) +{ + /*-----------------------------------------------------*\ + | Check that the new log level is valid, otherwise set | + | it within the valid range | + \*-----------------------------------------------------*/ + if(level > LL_TRACE) + { + level = LL_TRACE; + } + + LOG_DEBUG("[LogManager] Loglevel set to %d", level); + + /*-----------------------------------------------------*\ + | Set the new log level | + \*-----------------------------------------------------*/ + loglevel = level; +} + +void LogManager::setVerbosity(unsigned int level) +{ + /*-----------------------------------------------------*\ + | Check that the new verbosity is valid, otherwise set | + | it within the valid range | + \*-----------------------------------------------------*/ + if(level > LL_TRACE) + { + level = LL_TRACE; + } + + LOG_DEBUG("[LogManager] Verbosity set to %d", level); + + /*-----------------------------------------------------*\ + | Set the new verbosity | + \*-----------------------------------------------------*/ + verbosity = level; +} + +void LogManager::setPrintSource(bool v) +{ + LOG_DEBUG("[LogManager] Source code location printouts were %s", v ? "enabled" : "disabled"); + print_source = v; +} + +void LogManager::RegisterDialogShowCallback(LogDialogShowCallback callback, void* receiver) +{ + LOG_DEBUG("[LogManager] dialog show callback registered"); + dialog_show_callbacks.push_back(callback); + dialog_show_callback_args.push_back(receiver); +} + +void LogManager::UnregisterDialogShowCallback(LogDialogShowCallback callback, void* receiver) +{ + for(size_t idx = 0; idx < dialog_show_callbacks.size(); idx++) + { + if(dialog_show_callbacks[idx] == callback && dialog_show_callback_args[idx] == receiver) + { + dialog_show_callbacks.erase(dialog_show_callbacks.begin() + idx); + dialog_show_callback_args.erase(dialog_show_callback_args.begin() + idx); + } + } +} + +void LogManager::rotate_logs(const filesystem::path& folder, const filesystem::path& templ, int max_count) +{ + if(max_count < 1) + { + return; + } + + std::string templ2 = templ.filename().generic_u8string(); + + /*-----------------------------------------------------*\ + | Process the templ2 into a usable regex | + | The # symbol is replaced with a timestamp regex | + | Any regex-unfriendly symbols are escaped with a | + | backslash | + \*-----------------------------------------------------*/ + std::string regex_templ = "^"; + for(size_t i = 0; i < templ2.size(); ++i) + { + switch(templ2[i]) + { + /*-------------------------------------------------*\ + | Symbols that have special meanings in regex'es | + | need backslash escaping | + \*-------------------------------------------------*/ + case '.': + case '^': + case '$': + case '(': + case ')': + case '{': + case '}': + case '+': + case '[': + case ']': + case '*': + case '-': + /*-------------------------------------------------*\ + | Should have been filtered out by the filesystem | + | processing, but... who knows | + \*-------------------------------------------------*/ + case '\\': + regex_templ.push_back('\\'); + regex_templ.push_back(templ2[i]); + break; + + /*-------------------------------------------------*\ + | The # symbol is reserved for the timestamp and | + | thus is replaced with the timestamp regex | + | template | + \*-------------------------------------------------*/ + case '#': + regex_templ.append(TimestampRegex); + break; + + default: + regex_templ.push_back(templ2[i]); + break; + } + } + regex_templ.push_back('$'); + + std::regex r(regex_templ); + + std::vector valid_paths; + std::filesystem::directory_iterator it(folder); + for(; it != filesystem::end(it); ++it) + { + if(it->is_regular_file()) + { + std::string fname = it->path().filename().u8string(); + if(std::regex_match(fname, r)) + { + valid_paths.push_back(it->path()); + } + } + } + std::sort(valid_paths.begin(), valid_paths.end()); + + /*-----------------------------------------------------*\ + | NOTE: the "1" extra file to remove creates space for | + | the one we're about to create for max_count <= 0 and | + | to prevent any possible errors in the above logic | + \*-----------------------------------------------------*/ + size_t remove_count = valid_paths.size() - max_count + 1; + if(remove_count > valid_paths.size()) + { + remove_count = valid_paths.size(); + } + + for(size_t i = 0; i < remove_count; ++i) + { + /*-------------------------------------------------*\ + | Uses error code to force the `remove` call to be | + | `noexcept` | + \*-------------------------------------------------*/ + std::error_code ec; + if(filesystem::remove(valid_paths[i], ec)) + { + LOG_VERBOSE("[LogManager] Removed log file [%s] during rotation", valid_paths[i].u8string().c_str()); + } + else + { + LOG_WARNING("[LogManager] Failed to remove log file [%s] during rotation: %s", valid_paths[i].u8string().c_str(), ec.message().c_str()); + } + } +} diff --git a/LogManager.h b/LogManager.h new file mode 100644 index 0000000..9cf37b8 --- /dev/null +++ b/LogManager.h @@ -0,0 +1,126 @@ +/*---------------------------------------------------------*\ +| LogManager.h | +| | +| Manages log file and output to the console | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#ifndef LOGMANAGER_H +#define LOGMANAGER_H + +#include +#include +#include +#include +#include +#include +#include "filesystem.h" + +/*-------------------------------------------------*\ +| Common LOG strings | +| This may need to be in it's own .h file | +\*-------------------------------------------------*/ +#define SMBUS_CHECK_DEVICE_MESSAGE_EN "[%s] Bus %02d is a motherboard and the subvendor matches the one for %s, looking for a device at 0x%02X" +#define SMBUS_CHECK_DEVICE_FAILURE_EN "[%s] Bus %02d is not a motherboard or the subvendor does not match the one for %s, skipping detection" + +#define GPU_DETECT_MESSAGE "[%s] Found a device match at Bus %02d for Device 0x%04X and SubDevice 0x%04X: %s" + +using json = nlohmann::json; + +enum +{ + LL_FATAL, // Critical unrecoverable errors that cause a generalized crash of a module or of the entire app + LL_ERROR, // Local errors that abort an operation + LL_WARNING, // Local errors that may cause an operation to have an undefined behavior or may have dangerous/unforeseen consequences + LL_INFO, // Initialization messages, significant actions and follow-up information + LL_VERBOSE, // Tracing of commands and performed actions, usually for debug purposes, comments on the higher priority messages + LL_DEBUG, // Deep tracing, "printf-style debugging" alternative, for debug purposes. Such messages should be put all over the code instead of comments + LL_TRACE, + LL_DIALOG // Log messages to be shown in a GUI dialog box +}; + +struct LogMessage +{ + std::string buffer; + unsigned int level; + const char* filename; + int line; + std::chrono::duration counted_second; + // int timestamp or float time_offset? TBD +}; +typedef std::shared_ptr PLogMessage; +typedef void(*LogDialogShowCallback)(void*, PLogMessage); + +class LogManager +{ +private: + LogManager(); + LogManager(const LogManager&) = delete; + LogManager(LogManager&&) = delete; + ~LogManager(); + std::recursive_mutex entry_mutex; + std::mutex section_mutex; + std::ofstream log_stream; + + std::vector dialog_show_callbacks; + std::vector dialog_show_callback_args; + + // A temporary log message storage to hold them until the stream opens + std::vector temp_messages; + + // A log message storage that will be displayed in the app + std::vector all_messages; + + // A flag that marks if the message source file name and line number should be printed on screen + bool print_source = false; + + // Logfile max level + unsigned int loglevel = LL_INFO; + + // Verbosity (stdout) max level + unsigned int verbosity = LL_WARNING; + + //Clock from LogManager creation + std::chrono::time_point base_clock; + + // A non-guarded append() + void _append(const char* filename, int line, unsigned int level, const char* fmt, va_list va); + + // A non-guarded flush() + void _flush(); + + void rotate_logs(const filesystem::path& folder, const filesystem::path& templ, int max_count); + +public: + static LogManager* get(); + void configure(json config, const filesystem::path & defaultDir); + void flush(); + void append(const char* filename, int line, unsigned int level, const char* fmt, ...); + void setLoglevel(unsigned int); + void setVerbosity(unsigned int); + void setPrintSource(bool); + void RegisterDialogShowCallback(LogDialogShowCallback callback, void* receiver); + void UnregisterDialogShowCallback(LogDialogShowCallback callback, void* receiver); + unsigned int getLoglevel(); + unsigned int getVerbosity() {return verbosity;} + void clearMessages(); + std::vector messages(); + + bool log_console_enabled; + bool log_file_enabled; + static const char* log_codes[]; +}; + +#define LogAppend(level, ...) LogManager::get()->append(__FILE__, __LINE__, level, __VA_ARGS__) +#define LOG_FATAL(...) LogAppend(LL_FATAL, __VA_ARGS__) +#define LOG_ERROR(...) LogAppend(LL_ERROR, __VA_ARGS__) +#define LOG_WARNING(...) LogAppend(LL_WARNING, __VA_ARGS__) +#define LOG_INFO(...) LogAppend(LL_INFO, __VA_ARGS__) +#define LOG_VERBOSE(...) LogAppend(LL_VERBOSE, __VA_ARGS__) +#define LOG_DEBUG(...) LogAppend(LL_DEBUG, __VA_ARGS__) +#define LOG_TRACE(...) LogAppend(LL_TRACE, __VA_ARGS__) +#define LOG_DIALOG(...) LogAppend(LL_DIALOG, __VA_ARGS__) + +#endif // LOGMANAGER_H diff --git a/MathUtils.cpp b/MathUtils.cpp new file mode 100644 index 0000000..d9203f9 --- /dev/null +++ b/MathUtils.cpp @@ -0,0 +1,24 @@ +/*---------------------------------------------------------*\ +| MathUtils.cpp | +| | +| Math utility functions | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "MathUtils.h" + +int MathUtils::IntInterpolate(int y0, int y1, int x0, int x1, int x) +{ + if(x1 == x0) + return y0; + + if(y0 == y1) + return y0; + + double t = (double)(x - x0) / (double)(x1 - x0); + double y = y0 * (1.0 - t) + y1 * t; + return (int)round(y); +} \ No newline at end of file diff --git a/MathUtils.h b/MathUtils.h new file mode 100644 index 0000000..21e63ec --- /dev/null +++ b/MathUtils.h @@ -0,0 +1,18 @@ +/*---------------------------------------------------------*\ +| MathUtils.h | +| | +| Math utility functions | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +class MathUtils +{ +public: + static int IntInterpolate(int y0, int y1, int x0, int x1, int x); +}; \ No newline at end of file diff --git a/NetworkClient.cpp b/NetworkClient.cpp new file mode 100644 index 0000000..d6a3896 --- /dev/null +++ b/NetworkClient.cpp @@ -0,0 +1,1087 @@ +/*---------------------------------------------------------*\ +| NetworkClient.cpp | +| | +| OpenRGB SDK network client | +| | +| Adam Honse (CalcProgrammer1) 09 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "NetworkClient.h" +#include "RGBController_Network.h" + +#ifdef _WIN32 +#include +#define MSG_NOSIGNAL 0 +#endif + +#ifdef __APPLE__ +#include +#endif + +#ifdef __linux__ +#include +#include +#include +#endif + +#ifdef __linux__ +const int yes = 1; +#else +const char yes = 1; +#endif + +using namespace std::chrono_literals; + +NetworkClient::NetworkClient(std::vector& control) : controllers(control) +{ + port_ip = "127.0.0.1"; + port_num = OPENRGB_SDK_PORT; + client_string_sent = false; + client_sock = -1; + protocol_initialized = false; + server_connected = false; + server_controller_count = 0; + server_controller_count_requested = false; + server_controller_count_received = false; + server_protocol_version = 0; + server_reinitialize = false; + change_in_progress = false; + + ListenThread = NULL; + ConnectionThread = NULL; +} + +NetworkClient::~NetworkClient() +{ + StopClient(); +} + +void NetworkClient::ClearCallbacks() +{ + ClientInfoChangeCallbacks.clear(); + ClientInfoChangeCallbackArgs.clear(); +} + +void NetworkClient::ClientInfoChanged() +{ + ClientInfoChangeMutex.lock(); + ControllerListMutex.lock(); + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + for(unsigned int callback_idx = 0; callback_idx < ClientInfoChangeCallbacks.size(); callback_idx++) + { + ClientInfoChangeCallbacks[callback_idx](ClientInfoChangeCallbackArgs[callback_idx]); + } + + ControllerListMutex.unlock(); + ClientInfoChangeMutex.unlock(); +} + +std::string NetworkClient::GetIP() +{ + return port_ip; +} + +unsigned short NetworkClient::GetPort() +{ + return port_num; +} + +unsigned int NetworkClient::GetProtocolVersion() +{ + unsigned int protocol_version = 0; + + if(server_protocol_version > OPENRGB_SDK_PROTOCOL_VERSION) + { + protocol_version = OPENRGB_SDK_PROTOCOL_VERSION; + } + else + { + protocol_version = server_protocol_version; + } + + return(protocol_version); +} + +bool NetworkClient::GetConnected() +{ + return(server_connected); +} + +bool NetworkClient::GetOnline() +{ + return(server_connected && client_string_sent && protocol_initialized && server_initialized); +} + +void NetworkClient::RegisterClientInfoChangeCallback(NetClientCallback new_callback, void * new_callback_arg) +{ + ClientInfoChangeCallbacks.push_back(new_callback); + ClientInfoChangeCallbackArgs.push_back(new_callback_arg); +} + +void NetworkClient::SetIP(std::string new_ip) +{ + if(server_connected == false) + { + port_ip = new_ip; + } +} + +void NetworkClient::SetName(std::string new_name) +{ + client_name = new_name; + + if(server_connected == true) + { + SendData_ClientString(); + } +} + +void NetworkClient::SetPort(unsigned short new_port) +{ + if(server_connected == false) + { + port_num = new_port; + } +} + +void NetworkClient::StartClient() +{ + /*---------------------------------------------------------*\ + | Start a TCP server and launch threads | + \*---------------------------------------------------------*/ + char port_str[6]; + snprintf(port_str, 6, "%d", port_num); + + port.tcp_client(port_ip.c_str(), port_str); + + client_active = true; + + /*---------------------------------------------------------*\ + | Start the connection thread | + \*---------------------------------------------------------*/ + ConnectionThread = new std::thread(&NetworkClient::ConnectionThreadFunction, this); + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + ClientInfoChanged(); +} + +void NetworkClient::StopClient() +{ + /*---------------------------------------------------------*\ + | Disconnect the server and set it as inactive | + \*---------------------------------------------------------*/ + server_connected = false; + client_active = false; + + /*---------------------------------------------------------*\ + | Shut down and close the client socket | + \*---------------------------------------------------------*/ + if(server_connected) + { + shutdown(client_sock, SD_RECEIVE); + closesocket(client_sock); + } + + client_active = false; + server_connected = false; + + /*---------------------------------------------------------*\ + | Close the listen thread | + \*---------------------------------------------------------*/ + if(ListenThread) + { + ListenThread->join(); + delete ListenThread; + ListenThread = nullptr; + } + + /*---------------------------------------------------------*\ + | Close the connection thread | + \*---------------------------------------------------------*/ + if(ConnectionThread) + { + connection_cv.notify_all(); + ConnectionThread->join(); + delete ConnectionThread; + ConnectionThread = nullptr; + } + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + ClientInfoChanged(); +} + +void NetworkClient::ConnectionThreadFunction() +{ + std::unique_lock lock(connection_mutex); + + /*---------------------------------------------------------*\ + | This thread manages the connection to the server | + \*---------------------------------------------------------*/ + while(client_active == true) + { + if(server_connected == false) + { + /*---------------------------------------------------------*\ + | Connect to server and reconnect if the connection is lost | + \*---------------------------------------------------------*/ + server_initialized = false; + + /*---------------------------------------------------------*\ + | Try to connect to server | + \*---------------------------------------------------------*/ + if(port.tcp_client_connect() == true) + { + client_sock = port.sock; + printf( "Connected to server\n" ); + + /*---------------------------------------------------------*\ + | Server is now connected | + \*---------------------------------------------------------*/ + server_connected = true; + + /*---------------------------------------------------------*\ + | Start the listener thread | + \*---------------------------------------------------------*/ + ListenThread = new std::thread(&NetworkClient::ListenThreadFunction, this); + + /*---------------------------------------------------------*\ + | Server is not initialized | + \*---------------------------------------------------------*/ + server_initialized = false; + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + ClientInfoChanged(); + } + else + { + printf( "Connection attempt failed\n" ); + } + } + + /*-------------------------------------------------------------*\ + | Double-check client_active as it could have changed | + \*-------------------------------------------------------------*/ + if(client_active && ( protocol_initialized == false || client_string_sent == false || server_initialized == false ) && server_connected == true) + { + /*---------------------------------------------------------*\ + | Initialize protocol version if it hasn't already been | + | initialized | + \*---------------------------------------------------------*/ + if(!protocol_initialized) + { + /*-----------------------------------------------------*\ + | Request protocol version | + \*-----------------------------------------------------*/ + SendRequest_ProtocolVersion(); + + /*-----------------------------------------------------*\ + | Wait up to 1s for protocol version reply | + \*-----------------------------------------------------*/ + unsigned int timeout_counter = 0; + + while(!server_protocol_version_received) + { + connection_cv.wait_for(lock, 5ms); + if(!client_active) + { + break; + } + + timeout_counter++; + + /*-------------------------------------------------*\ + | If no protocol version received within 1s, assume | + | the server doesn't support protocol versioning | + | and use protocol version 0 | + \*-------------------------------------------------*/ + if(timeout_counter > 200) + { + server_protocol_version = 0; + server_protocol_version_received = true; + } + } + + protocol_initialized = true; + } + + /*---------------------------------------------------------*\ + | Send client string if it hasn't already been sent | + \*---------------------------------------------------------*/ + if(!client_string_sent) + { + /*-----------------------------------------------------*\ + | Once server is connected, send client string | + \*-----------------------------------------------------*/ + SendData_ClientString(); + + client_string_sent = true; + } + + /*---------------------------------------------------------*\ + | Initialize the server device list if it hasn't already | + | been initialized | + \*---------------------------------------------------------*/ + if(!server_initialized) + { + /*-----------------------------------------------------*\ + | Request the server controller count | + \*-----------------------------------------------------*/ + if(!server_controller_count_requested) + { + SendRequest_ControllerCount(); + + server_controller_count_requested = true; + } + else + { + /*-------------------------------------------------*\ + | Wait for the server controller count to be | + | received | + \*-------------------------------------------------*/ + if(server_controller_count_received) + { + /*---------------------------------------------*\ + | Once count is received, request controllers | + | When data is received, increment count of | + | requested controllers until all controllers | + | have been received | + \*---------------------------------------------*/ + if(requested_controllers < server_controller_count) + { + if(!controller_data_requested) + { + printf("Client: Requesting controller %d\r\n", requested_controllers); + + controller_data_received = false; + SendRequest_ControllerData(requested_controllers); + + controller_data_requested = true; + } + + if(controller_data_received) + { + requested_controllers++; + controller_data_requested = false; + } + } + else + { + ControllerListMutex.lock(); + + /*-----------------------------------------*\ + | All controllers received, add them to | + | master list | + \*-----------------------------------------*/ + printf("Client: All controllers received, adding them to master list\r\n"); + for(std::size_t controller_idx = 0; controller_idx < server_controllers.size(); controller_idx++) + { + controllers.push_back(server_controllers[controller_idx]); + } + + ControllerListMutex.unlock(); + + /*-----------------------------------------*\ + | Client info has changed, call the | + | callbacks | + \*-----------------------------------------*/ + ClientInfoChanged(); + + server_initialized = true; + } + } + } + } + + /*---------------------------------------------------------*\ + | Wait 1 ms or until the thread is requested to stop | + \*---------------------------------------------------------*/ + connection_cv.wait_for(lock, 1ms); + } + else + { + /*---------------------------------------------------------*\ + | Wait 1 sec or until the thread is requested to stop | + \*---------------------------------------------------------*/ + connection_cv.wait_for(lock, 1s); + } + } +} + +int NetworkClient::recv_select(SOCKET s, char *buf, int len, int flags) +{ + fd_set set; + struct timeval timeout; + + while(1) + { + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + FD_ZERO(&set); + FD_SET(s, &set); + + int rv = select((int)s + 1, &set, NULL, NULL, &timeout); + + if(rv == SOCKET_ERROR || server_connected == false) + { + return 0; + } + else if(rv == 0) + { + continue; + } + else + { + /*-------------------------------------------------*\ + | Set QUICKACK socket option on Linux to improve | + | performance | + \*-------------------------------------------------*/ +#ifdef __linux__ + setsockopt(s, IPPROTO_TCP, TCP_QUICKACK, &yes, sizeof(yes)); +#endif + return(recv(s, buf, len, flags)); + } + + } +} + +void NetworkClient::ListenThreadFunction() +{ + printf("Network client listener started\n"); + + /*---------------------------------------------------------*\ + | This thread handles messages received from the server | + \*---------------------------------------------------------*/ + while(server_connected == true) + { + NetPacketHeader header; + int bytes_read = 0; + char * data = NULL; + + for(unsigned int i = 0; i < 4; i++) + { + /*---------------------------------------------------------*\ + | Read byte of magic | + \*---------------------------------------------------------*/ + bytes_read = recv_select(client_sock, &header.pkt_magic[i], 1, 0); + + if(bytes_read <= 0) + { + goto listen_done; + } + + /*---------------------------------------------------------*\ + | Test characters of magic "ORGB" | + \*---------------------------------------------------------*/ + if(header.pkt_magic[i] != openrgb_sdk_magic[i]) + { + continue; + } + } + + /*---------------------------------------------------------*\ + | If we get to this point, the magic is correct. Read the | + | rest of the header | + \*---------------------------------------------------------*/ + bytes_read = 0; + do + { + int tmp_bytes_read = 0; + + tmp_bytes_read = recv_select(client_sock, (char *)&header.pkt_dev_idx + bytes_read, sizeof(header) - sizeof(header.pkt_magic) - bytes_read, 0); + + bytes_read += tmp_bytes_read; + + if(tmp_bytes_read <= 0) + { + goto listen_done; + } + + } while(bytes_read != sizeof(header) - sizeof(header.pkt_magic)); + + /*---------------------------------------------------------*\ + | Header received, now receive the data | + \*---------------------------------------------------------*/ + if(header.pkt_size > 0) + { + bytes_read = 0; + + data = new char[header.pkt_size]; + + do + { + int tmp_bytes_read = 0; + + tmp_bytes_read = recv_select(client_sock, &data[(unsigned int)bytes_read], header.pkt_size - bytes_read, 0); + + if(tmp_bytes_read <= 0) + { + goto listen_done; + } + bytes_read += tmp_bytes_read; + + } while ((unsigned int)bytes_read < header.pkt_size); + } + + /*---------------------------------------------------------*\ + | Entire request received, select functionality based on | + | request ID | + \*---------------------------------------------------------*/ + switch(header.pkt_id) + { + case NET_PACKET_ID_REQUEST_CONTROLLER_COUNT: + ProcessReply_ControllerCount(header.pkt_size, data); + break; + + case NET_PACKET_ID_REQUEST_CONTROLLER_DATA: + ProcessReply_ControllerData(header.pkt_size, data, header.pkt_dev_idx); + break; + + case NET_PACKET_ID_REQUEST_PROTOCOL_VERSION: + ProcessReply_ProtocolVersion(header.pkt_size, data); + break; + + case NET_PACKET_ID_DEVICE_LIST_UPDATED: + ProcessRequest_DeviceListChanged(); + break; + } + + delete[] data; + } + +listen_done: + printf( "Client socket has been closed"); + client_string_sent = false; + controller_data_requested = false; + controller_data_received = false; + protocol_initialized = false; + requested_controllers = 0; + server_controller_count = 0; + server_controller_count_requested = false; + server_controller_count_received = false; + server_initialized = false; + server_connected = false; + + ControllerListMutex.lock(); + + for(size_t server_controller_idx = 0; server_controller_idx < server_controllers.size(); server_controller_idx++) + { + for(size_t controller_idx = 0; controller_idx < controllers.size(); controller_idx++) + { + if(controllers[controller_idx] == server_controllers[server_controller_idx]) + { + controllers.erase(controllers.begin() + controller_idx); + break; + } + } + } + + std::vector server_controllers_copy = server_controllers; + + server_controllers.clear(); + + for(size_t server_controller_idx = 0; server_controller_idx < server_controllers_copy.size(); server_controller_idx++) + { + delete server_controllers_copy[server_controller_idx]; + } + + ControllerListMutex.unlock(); + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + ClientInfoChanged(); +} + +void NetworkClient::WaitOnControllerData() +{ + for(int i = 0; i < 1000; i++) + { + if(controller_data_received) + { + break; + } + std::this_thread::sleep_for(1ms); + } + + return; +} + +void NetworkClient::ProcessReply_ControllerCount(unsigned int data_size, char * data) +{ + if(data_size == sizeof(unsigned int)) + { + memcpy(&server_controller_count, data, sizeof(unsigned int)); + + server_controller_count_received = true; + requested_controllers = 0; + controller_data_requested = false; + + printf("Client: Received controller count from server: %d\r\n", server_controller_count); + } +} + +void NetworkClient::ProcessReply_ControllerData(unsigned int data_size, char * data, unsigned int dev_idx) +{ + /*---------------------------------------------------------*\ + | Verify the controller description size (first 4 bytes of | + | data) matches the packet size in the header | + \*---------------------------------------------------------*/ + if(data_size == *((unsigned int*)data)) + { + RGBController_Network * new_controller = new RGBController_Network(this, dev_idx); + + new_controller->ReadDeviceDescription((unsigned char *)data, GetProtocolVersion()); + + /*-----------------------------------------------------*\ + | Mark this controller as remote owned | + \*-----------------------------------------------------*/ + new_controller->flags &= ~CONTROLLER_FLAG_LOCAL; + new_controller->flags |= CONTROLLER_FLAG_REMOTE; + + ControllerListMutex.lock(); + + if(dev_idx >= server_controllers.size()) + { + server_controllers.push_back(new_controller); + } + else + { + server_controllers[dev_idx]->active_mode = new_controller->active_mode; + server_controllers[dev_idx]->leds.clear(); + server_controllers[dev_idx]->leds = new_controller->leds; + server_controllers[dev_idx]->colors.clear(); + server_controllers[dev_idx]->colors = new_controller->colors; + for(unsigned int i = 0; i < server_controllers[dev_idx]->zones.size(); i++) + { + server_controllers[dev_idx]->zones[i].leds_count = new_controller->zones[i].leds_count; + server_controllers[dev_idx]->zones[i].segments.clear(); + server_controllers[dev_idx]->zones[i].segments = new_controller->zones[i].segments; + } + server_controllers[dev_idx]->SetupColors(); + + delete new_controller; + } + + ControllerListMutex.unlock(); + + controller_data_received = true; + } +} + +void NetworkClient::ProcessReply_ProtocolVersion(unsigned int data_size, char * data) +{ + if(data_size == sizeof(unsigned int)) + { + memcpy(&server_protocol_version, data, sizeof(unsigned int)); + server_protocol_version_received = true; + } +} + +void NetworkClient::ProcessRequest_DeviceListChanged() +{ + change_in_progress = true; + + /*---------------------------------------------------------*\ + | Delete all controllers from the server's controller list | + \*---------------------------------------------------------*/ + ControllerListMutex.lock(); + + for(size_t server_controller_idx = 0; server_controller_idx < server_controllers.size(); server_controller_idx++) + { + for(size_t controller_idx = 0; controller_idx < controllers.size(); controller_idx++) + { + if(controllers[controller_idx] == server_controllers[server_controller_idx]) + { + controllers.erase(controllers.begin() + controller_idx); + break; + } + } + } + + std::vector server_controllers_copy = server_controllers; + + server_controllers.clear(); + + for(size_t server_controller_idx = 0; server_controller_idx < server_controllers_copy.size(); server_controller_idx++) + { + delete server_controllers_copy[server_controller_idx]; + } + + ControllerListMutex.unlock(); + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + ClientInfoChanged(); + + /*---------------------------------------------------------*\ + | Mark server as uninitialized and reset server | + | initialization state so that it restarts the list | + | requesting process | + \*---------------------------------------------------------*/ + controller_data_requested = false; + controller_data_received = false; + requested_controllers = 0; + server_controller_count = 0; + server_controller_count_requested = false; + server_controller_count_received = false; + server_initialized = false; + + change_in_progress = false; +} + +void NetworkClient::SendData_ClientString() +{ + NetPacketHeader reply_hdr; + + InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_SET_CLIENT_NAME, (unsigned int)strlen(client_name.c_str()) + 1); + + send_in_progress.lock(); + send(client_sock, (char *)&reply_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)client_name.c_str(), reply_hdr.pkt_size, MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_ControllerCount() +{ + NetPacketHeader request_hdr; + + InitNetPacketHeader(&request_hdr, 0, NET_PACKET_ID_REQUEST_CONTROLLER_COUNT, 0); + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_ControllerData(unsigned int dev_idx) +{ + NetPacketHeader request_hdr; + unsigned int protocol_version; + + controller_data_received = false; + + memcpy(request_hdr.pkt_magic, openrgb_sdk_magic, sizeof(openrgb_sdk_magic)); + + request_hdr.pkt_dev_idx = dev_idx; + request_hdr.pkt_id = NET_PACKET_ID_REQUEST_CONTROLLER_DATA; + + if(server_protocol_version == 0) + { + request_hdr.pkt_size = 0; + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send_in_progress.unlock(); + } + else + { + request_hdr.pkt_size = sizeof(unsigned int); + + /*-------------------------------------------------------------*\ + | Limit the protocol version to the highest supported by both | + | the client and the server. | + \*-------------------------------------------------------------*/ + if(server_protocol_version > OPENRGB_SDK_PROTOCOL_VERSION) + { + protocol_version = OPENRGB_SDK_PROTOCOL_VERSION; + } + else + { + protocol_version = server_protocol_version; + } + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)&protocol_version, sizeof(unsigned int), MSG_NOSIGNAL); + send_in_progress.unlock(); + } +} + +void NetworkClient::SendRequest_ProtocolVersion() +{ + NetPacketHeader request_hdr; + unsigned int request_data; + + InitNetPacketHeader(&request_hdr, 0, NET_PACKET_ID_REQUEST_PROTOCOL_VERSION, sizeof(unsigned int)); + + request_data = OPENRGB_SDK_PROTOCOL_VERSION; + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)&request_data, sizeof(unsigned int), MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_RescanDevices() +{ + if(GetProtocolVersion() >= 5) + { + NetPacketHeader request_hdr; + + InitNetPacketHeader(&request_hdr, 0, NET_PACKET_ID_REQUEST_RESCAN_DEVICES, 0); + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send_in_progress.unlock(); + } +} + +void NetworkClient::SendRequest_RGBController_ClearSegments(unsigned int dev_idx, int zone) +{ + if(change_in_progress) + { + return; + } + + NetPacketHeader request_hdr; + int request_data[1]; + + InitNetPacketHeader(&request_hdr, dev_idx, NET_PACKET_ID_RGBCONTROLLER_CLEARSEGMENTS, sizeof(request_data)); + + request_data[0] = zone; + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)&request_data, sizeof(request_data), MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_RGBController_AddSegment(unsigned int dev_idx, unsigned char * data, unsigned int size) +{ + if(change_in_progress) + { + return; + } + + NetPacketHeader request_hdr; + + InitNetPacketHeader(&request_hdr, dev_idx, NET_PACKET_ID_RGBCONTROLLER_ADDSEGMENT, size); + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)data, size, 0); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_RGBController_ResizeZone(unsigned int dev_idx, int zone, int new_size) +{ + if(change_in_progress) + { + return; + } + + NetPacketHeader request_hdr; + int request_data[2]; + + InitNetPacketHeader(&request_hdr, dev_idx, NET_PACKET_ID_RGBCONTROLLER_RESIZEZONE, sizeof(request_data)); + + request_data[0] = zone; + request_data[1] = new_size; + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)&request_data, sizeof(request_data), MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_RGBController_UpdateLEDs(unsigned int dev_idx, unsigned char * data, unsigned int size) +{ + if(change_in_progress) + { + return; + } + + NetPacketHeader request_hdr; + + InitNetPacketHeader(&request_hdr, dev_idx, NET_PACKET_ID_RGBCONTROLLER_UPDATELEDS, size); + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)data, size, 0); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_RGBController_UpdateZoneLEDs(unsigned int dev_idx, unsigned char * data, unsigned int size) +{ + if(change_in_progress) + { + return; + } + + NetPacketHeader request_hdr; + + InitNetPacketHeader(&request_hdr, dev_idx, NET_PACKET_ID_RGBCONTROLLER_UPDATEZONELEDS, size); + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)data, size, MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_RGBController_UpdateSingleLED(unsigned int dev_idx, unsigned char * data, unsigned int size) +{ + if(change_in_progress) + { + return; + } + + NetPacketHeader request_hdr; + + InitNetPacketHeader(&request_hdr, dev_idx, NET_PACKET_ID_RGBCONTROLLER_UPDATESINGLELED, size); + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)data, size, MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_RGBController_SetCustomMode(unsigned int dev_idx) +{ + if(change_in_progress) + { + return; + } + + NetPacketHeader request_hdr; + + InitNetPacketHeader(&request_hdr, dev_idx, NET_PACKET_ID_RGBCONTROLLER_SETCUSTOMMODE, 0); + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_RGBController_UpdateMode(unsigned int dev_idx, unsigned char * data, unsigned int size) +{ + if(change_in_progress) + { + return; + } + + NetPacketHeader request_hdr; + + InitNetPacketHeader(&request_hdr, dev_idx, NET_PACKET_ID_RGBCONTROLLER_UPDATEMODE, size); + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)data, size, MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_RGBController_SaveMode(unsigned int dev_idx, unsigned char * data, unsigned int size) +{ + if(change_in_progress) + { + return; + } + + NetPacketHeader request_hdr; + + InitNetPacketHeader(&request_hdr, dev_idx, NET_PACKET_ID_RGBCONTROLLER_SAVEMODE, size); + + send_in_progress.lock(); + send(client_sock, (char *)&request_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)data, size, MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_LoadProfile(std::string profile_name) +{ + NetPacketHeader reply_hdr; + + InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_LOAD_PROFILE, (unsigned int)strlen(profile_name.c_str()) + 1); + + send_in_progress.lock(); + send(client_sock, (char *)&reply_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)profile_name.c_str(), reply_hdr.pkt_size, MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_SaveProfile(std::string profile_name) +{ + NetPacketHeader reply_hdr; + + InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_SAVE_PROFILE, (unsigned int)strlen(profile_name.c_str()) + 1); + + send_in_progress.lock(); + send(client_sock, (char *)&reply_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)profile_name.c_str(), reply_hdr.pkt_size, MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_DeleteProfile(std::string profile_name) +{ + NetPacketHeader reply_hdr; + + InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_DELETE_PROFILE, (unsigned int)strlen(profile_name.c_str()) + 1); + + send_in_progress.lock(); + send(client_sock, (char *)&reply_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send(client_sock, (char *)profile_name.c_str(), reply_hdr.pkt_size, MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +void NetworkClient::SendRequest_GetProfileList() +{ + NetPacketHeader reply_hdr; + + InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_PROFILE_LIST, 0); + + send_in_progress.lock(); + send(client_sock, (char *)&reply_hdr, sizeof(NetPacketHeader), MSG_NOSIGNAL); + send_in_progress.unlock(); +} + +std::vector * NetworkClient::ProcessReply_ProfileList(unsigned int data_size, char * data) +{ + std::vector * profile_list; + + if(data_size > 0) + { + profile_list = new std::vector(data_size); + + /*---------------------------------------------------------*\ + | Skip 4 first bytes (data length, unused) | + \*---------------------------------------------------------*/ + unsigned short data_ptr = sizeof(unsigned short); + unsigned short num_profile; + + memcpy(&num_profile, data, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + for(int i = 0; i < num_profile; i++) + { + unsigned short name_len; + + memcpy(&name_len, data, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + std::string profile_name(data, name_len); + profile_list->push_back(profile_name); + + data_ptr += name_len; + } + + server_controller_count_received = true; + } + else + { + profile_list = new std::vector(0); + } + + return profile_list; +} diff --git a/NetworkClient.h b/NetworkClient.h new file mode 100644 index 0000000..9714250 --- /dev/null +++ b/NetworkClient.h @@ -0,0 +1,129 @@ +/*---------------------------------------------------------*\ +| NetworkClient.h | +| | +| OpenRGB SDK network client | +| | +| Adam Honse (CalcProgrammer1) 09 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "NetworkProtocol.h" +#include "net_port.h" + +typedef void (*NetClientCallback)(void *); + +class NetworkClient +{ +public: + NetworkClient(std::vector& control); + ~NetworkClient(); + + void ClientInfoChanged(); + + bool GetConnected(); + std::string GetIP(); + unsigned short GetPort(); + unsigned int GetProtocolVersion(); + bool GetOnline(); + + void ClearCallbacks(); + void RegisterClientInfoChangeCallback(NetClientCallback new_callback, void * new_callback_arg); + + void SetIP(std::string new_ip); + void SetName(std::string new_name); + void SetPort(unsigned short new_port); + + void StartClient(); + void StopClient(); + + void ConnectionThreadFunction(); + void ListenThreadFunction(); + + void WaitOnControllerData(); + + void ProcessReply_ControllerCount(unsigned int data_size, char * data); + void ProcessReply_ControllerData(unsigned int data_size, char * data, unsigned int dev_idx); + void ProcessReply_ProtocolVersion(unsigned int data_size, char * data); + + void ProcessRequest_DeviceListChanged(); + + void SendData_ClientString(); + + void SendRequest_ControllerCount(); + void SendRequest_ControllerData(unsigned int dev_idx); + void SendRequest_ProtocolVersion(); + + void SendRequest_RescanDevices(); + + void SendRequest_RGBController_ClearSegments(unsigned int dev_idx, int zone); + void SendRequest_RGBController_AddSegment(unsigned int dev_idx, unsigned char * data, unsigned int size); + void SendRequest_RGBController_ResizeZone(unsigned int dev_idx, int zone, int new_size); + + void SendRequest_RGBController_UpdateLEDs(unsigned int dev_idx, unsigned char * data, unsigned int size); + void SendRequest_RGBController_UpdateZoneLEDs(unsigned int dev_idx, unsigned char * data, unsigned int size); + void SendRequest_RGBController_UpdateSingleLED(unsigned int dev_idx, unsigned char * data, unsigned int size); + + void SendRequest_RGBController_SetCustomMode(unsigned int dev_idx); + + void SendRequest_RGBController_UpdateMode(unsigned int dev_idx, unsigned char * data, unsigned int size); + void SendRequest_RGBController_SaveMode(unsigned int dev_idx, unsigned char * data, unsigned int size); + + + std::vector * ProcessReply_ProfileList(unsigned int data_size, char * data); + + void SendRequest_GetProfileList(); + void SendRequest_LoadProfile(std::string profile_name); + void SendRequest_SaveProfile(std::string profile_name); + void SendRequest_DeleteProfile(std::string profile_name); + + std::vector server_controllers; + + std::mutex ControllerListMutex; + +protected: + std::vector& controllers; + + +private: + SOCKET client_sock; + std::string client_name; + net_port port; + std::string port_ip; + unsigned short port_num; + std::atomic client_active; + bool client_string_sent; + bool controller_data_received; + bool controller_data_requested; + bool protocol_initialized; + bool server_connected; + bool server_initialized; + bool server_reinitialize; + unsigned int server_controller_count; + bool server_controller_count_requested; + bool server_controller_count_received; + unsigned int server_protocol_version; + bool server_protocol_version_received; + bool change_in_progress; + unsigned int requested_controllers; + std::mutex send_in_progress; + + std::mutex connection_mutex; + std::condition_variable connection_cv; + + std::thread * ConnectionThread; + std::thread * ListenThread; + + std::mutex ClientInfoChangeMutex; + std::vector ClientInfoChangeCallbacks; + std::vector ClientInfoChangeCallbackArgs; + + int recv_select(SOCKET s, char *buf, int len, int flags); +}; diff --git a/NetworkProtocol.cpp b/NetworkProtocol.cpp new file mode 100644 index 0000000..91eca08 --- /dev/null +++ b/NetworkProtocol.cpp @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| NetworkProtocol.cpp | +| | +| OpenRGB SDK network protocol | +| | +| Adam Honse (CalcProgrammer1) 09 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "NetworkProtocol.h" + +/*-----------------------------------------------------*\ +| OpenRGB SDK Magic Value "ORGB" | +\*-----------------------------------------------------*/ +const char openrgb_sdk_magic[OPENRGB_SDK_MAGIC_SIZE] = { 'O', 'R', 'G', 'B' }; + +void InitNetPacketHeader + ( + NetPacketHeader * pkt_hdr, + unsigned int pkt_dev_idx, + unsigned int pkt_id, + unsigned int pkt_size + ) +{ + memcpy(pkt_hdr->pkt_magic, openrgb_sdk_magic, sizeof(openrgb_sdk_magic)); + + pkt_hdr->pkt_dev_idx = pkt_dev_idx; + pkt_hdr->pkt_id = pkt_id; + pkt_hdr->pkt_size = pkt_size; +} diff --git a/NetworkProtocol.h b/NetworkProtocol.h new file mode 100644 index 0000000..697bc1b --- /dev/null +++ b/NetworkProtocol.h @@ -0,0 +1,98 @@ +/*---------------------------------------------------------*\ +| NetworkProtocol.h | +| | +| OpenRGB SDK network protocol | +| | +| Adam Honse (CalcProgrammer1) 09 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +/*---------------------------------------------------------------------*\ +| OpenRGB SDK protocol version | +| | +| 0: Initial (unversioned) protocol | +| 1: Add versioning, vendor string (Release 0.5) | +| 2: Add profile controls (Release 0.6) | +| 3: Add brightness field to modes (Release 0.7) | +| 4: Add segments field to zones, network plugins (Release 0.9) | +| 5: Zone flags, controller flags, resizable effects-only zones | + (Release 1.0) | +\*---------------------------------------------------------------------*/ +#define OPENRGB_SDK_PROTOCOL_VERSION 5 + +/*-----------------------------------------------------*\ +| Default Interface to bind to. | +\*-----------------------------------------------------*/ +#define OPENRGB_SDK_HOST "0.0.0.0" + +/*-----------------------------------------------------*\ +| Default OpenRGB SDK port is 6742 | +| This is "ORGB" on a phone keypad | +\*-----------------------------------------------------*/ +#define OPENRGB_SDK_PORT 6742 + +/*-----------------------------------------------------*\ +| OpenRGB SDK Magic Value "ORGB" | +\*-----------------------------------------------------*/ +#define OPENRGB_SDK_MAGIC_SIZE 4 +extern const char openrgb_sdk_magic[OPENRGB_SDK_MAGIC_SIZE]; + +typedef struct NetPacketHeader +{ + char pkt_magic[4]; /* Magic value "ORGB" identifies beginning of packet */ + unsigned int pkt_dev_idx; /* Device index */ + unsigned int pkt_id; /* Packet ID */ + unsigned int pkt_size; /* Packet size */ +} NetPacketHeader; + +enum +{ + /*----------------------------------------------------------------------------------------------------------*\ + | Network requests | + \*----------------------------------------------------------------------------------------------------------*/ + NET_PACKET_ID_REQUEST_CONTROLLER_COUNT = 0, /* Request RGBController device count from server */ + NET_PACKET_ID_REQUEST_CONTROLLER_DATA = 1, /* Request RGBController data block */ + + NET_PACKET_ID_REQUEST_PROTOCOL_VERSION = 40, /* Request OpenRGB SDK protocol version from server */ + + NET_PACKET_ID_SET_CLIENT_NAME = 50, /* Send client name string to server */ + + NET_PACKET_ID_DEVICE_LIST_UPDATED = 100, /* Indicate to clients that device list has updated */ + + NET_PACKET_ID_REQUEST_RESCAN_DEVICES = 140, /* Request rescan of devices */ + + NET_PACKET_ID_REQUEST_PROFILE_LIST = 150, /* Request profile list */ + NET_PACKET_ID_REQUEST_SAVE_PROFILE = 151, /* Save current configuration in a new profile */ + NET_PACKET_ID_REQUEST_LOAD_PROFILE = 152, /* Load a given profile */ + NET_PACKET_ID_REQUEST_DELETE_PROFILE = 153, /* Delete a given profile */ + + NET_PACKET_ID_REQUEST_PLUGIN_LIST = 200, /* Request list of plugins */ + NET_PACKET_ID_PLUGIN_SPECIFIC = 201, /* Interact with a plugin */ + + /*----------------------------------------------------------------------------------------------------------*\ + | RGBController class functions | + \*----------------------------------------------------------------------------------------------------------*/ + NET_PACKET_ID_RGBCONTROLLER_RESIZEZONE = 1000, /* RGBController::ResizeZone() */ + NET_PACKET_ID_RGBCONTROLLER_CLEARSEGMENTS = 1001, /* RGBController::ClearSegments() */ + NET_PACKET_ID_RGBCONTROLLER_ADDSEGMENT = 1002, /* RGBController::AddSegment() */ + + NET_PACKET_ID_RGBCONTROLLER_UPDATELEDS = 1050, /* RGBController::UpdateLEDs() */ + NET_PACKET_ID_RGBCONTROLLER_UPDATEZONELEDS = 1051, /* RGBController::UpdateZoneLEDs() */ + NET_PACKET_ID_RGBCONTROLLER_UPDATESINGLELED = 1052, /* RGBController::UpdateSingleLED() */ + + NET_PACKET_ID_RGBCONTROLLER_SETCUSTOMMODE = 1100, /* RGBController::SetCustomMode() */ + NET_PACKET_ID_RGBCONTROLLER_UPDATEMODE = 1101, /* RGBController::UpdateMode() */ + NET_PACKET_ID_RGBCONTROLLER_SAVEMODE = 1102, /* RGBController::SaveMode() */ +}; + +void InitNetPacketHeader + ( + NetPacketHeader * pkt_hdr, + unsigned int pkt_dev_idx, + unsigned int pkt_id, + unsigned int pkt_size + ); diff --git a/NetworkServer.cpp b/NetworkServer.cpp new file mode 100644 index 0000000..81937ef --- /dev/null +++ b/NetworkServer.cpp @@ -0,0 +1,1294 @@ +/*---------------------------------------------------------*\ +| NetworkServer.cpp | +| | +| OpenRGB SDK network server | +| | +| Adam Honse (CalcProgrammer1) 09 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "NetworkServer.h" +#include "LogManager.h" + +#ifndef WIN32 +#include +#include +#include +#include +#else +#include +#endif +#include +#include +#include +#include + +#ifdef WIN32 +#include +#else +#include +#endif + +#ifdef __linux__ +const int yes = 1; +#else +const char yes = 1; +#endif + +using namespace std::chrono_literals; + +NetworkClientInfo::NetworkClientInfo() +{ + client_string = "Client"; + client_ip = OPENRGB_SDK_HOST; + client_sock = INVALID_SOCKET; + client_listen_thread = nullptr; + client_protocol_version = 0; +} + +NetworkClientInfo::~NetworkClientInfo() +{ + if(client_sock != INVALID_SOCKET) + { + LOG_INFO("[NetworkServer] Closing server connection: %s", client_ip.c_str()); + delete client_listen_thread; + shutdown(client_sock, SD_RECEIVE); + closesocket(client_sock); + } +} + +NetworkServer::NetworkServer(std::vector& control) : controllers(control) +{ + host = OPENRGB_SDK_HOST; + port_num = OPENRGB_SDK_PORT; + server_online = false; + server_listening = false; + legacy_workaround_enabled = false; + + for(int i = 0; i < MAXSOCK; i++) + { + ConnectionThread[i] = nullptr; + } + + profile_manager = nullptr; +} + +NetworkServer::~NetworkServer() +{ + StopServer(); +} + +void NetworkServer::ClientInfoChanged() +{ + ClientInfoChangeMutex.lock(); + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + for(unsigned int callback_idx = 0; callback_idx < ClientInfoChangeCallbacks.size(); callback_idx++) + { + ClientInfoChangeCallbacks[callback_idx](ClientInfoChangeCallbackArgs[callback_idx]); + } + + ClientInfoChangeMutex.unlock(); +} + +void NetworkServer::DeviceListChanged() +{ + /*---------------------------------------------------------*\ + | Indicate to the clients that the controller list has | + | changed | + \*---------------------------------------------------------*/ + for(unsigned int client_idx = 0; client_idx < ServerClients.size(); client_idx++) + { + SendRequest_DeviceListChanged(ServerClients[client_idx]->client_sock); + } +} + +void NetworkServer::ServerListeningChanged() +{ + ServerListeningChangeMutex.lock(); + + /*---------------------------------------------------------*\ + | Server state has changed, call the callbacks | + \*---------------------------------------------------------*/ + for(unsigned int callback_idx = 0; callback_idx < ServerListeningChangeCallbacks.size(); callback_idx++) + { + ServerListeningChangeCallbacks[callback_idx](ServerListeningChangeCallbackArgs[callback_idx]); + } + + ServerListeningChangeMutex.unlock(); +} + +std::string NetworkServer::GetHost() +{ + return host; +} + +unsigned short NetworkServer::GetPort() +{ + return port_num; +} + +bool NetworkServer::GetOnline() +{ + return server_online; +} + +bool NetworkServer::GetListening() +{ + return server_listening; +} + +unsigned int NetworkServer::GetNumClients() +{ + return (unsigned int)ServerClients.size(); +} + +const char * NetworkServer::GetClientString(unsigned int client_num) +{ + const char * result; + + ServerClientsMutex.lock(); + + if(client_num < ServerClients.size()) + { + result = ServerClients[client_num]->client_string.c_str(); + } + else + { + result = ""; + } + + ServerClientsMutex.unlock(); + + return result; +} + +const char * NetworkServer::GetClientIP(unsigned int client_num) +{ + const char * result; + + ServerClientsMutex.lock(); + + if(client_num < ServerClients.size()) + { + result = ServerClients[client_num]->client_ip.c_str(); + } + else + { + result = ""; + } + + ServerClientsMutex.unlock(); + + return result; +} + +unsigned int NetworkServer::GetClientProtocolVersion(unsigned int client_num) +{ + unsigned int result; + + ServerClientsMutex.lock(); + + if(client_num < ServerClients.size()) + { + result = ServerClients[client_num]->client_protocol_version; + } + else + { + result = 0; + } + + ServerClientsMutex.unlock(); + + return result; +} + +void NetworkServer::RegisterClientInfoChangeCallback(NetServerCallback new_callback, void * new_callback_arg) +{ + ClientInfoChangeCallbacks.push_back(new_callback); + ClientInfoChangeCallbackArgs.push_back(new_callback_arg); +} + +void NetworkServer::RegisterServerListeningChangeCallback(NetServerCallback new_callback, void * new_callback_arg) +{ + ServerListeningChangeCallbacks.push_back(new_callback); + ServerListeningChangeCallbackArgs.push_back(new_callback_arg); +} + +void NetworkServer::SetHost(std::string new_host) +{ + if(server_online == false) + { + host = new_host; + } +} + +void NetworkServer::SetLegacyWorkaroundEnable(bool enable) +{ + legacy_workaround_enabled = enable; +} + +void NetworkServer::SetPort(unsigned short new_port) +{ + if(server_online == false) + { + port_num = new_port; + } +} + +void NetworkServer::StartServer() +{ + int err; + struct addrinfo hints, *res, *result; + + /*---------------------------------------------------------*\ + | Start a TCP server and launch threads | + \*---------------------------------------------------------*/ + char port_str[6]; + snprintf(port_str, 6, "%d", port_num); + + socket_count = 0; + + /*---------------------------------------------------------*\ + | Windows requires WSAStartup before using sockets | + \*---------------------------------------------------------*/ +#ifdef WIN32 + if(WSAStartup(MAKEWORD(2, 2), &wsa) != NO_ERROR) + { + WSACleanup(); + return; + } +#endif + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + err = getaddrinfo(host.c_str(), port_str, &hints, &result); + + if(err) + { + LOG_ERROR("[NetworkServer] Unable to get address."); + WSACleanup(); + return; + } + + /*---------------------------------------------------------*\ + | Create a server socket for each address returned. | + \*---------------------------------------------------------*/ + for(res = result; res && socket_count < MAXSOCK; res = res->ai_next) + { + server_sock[socket_count] = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + if(server_sock[socket_count] == INVALID_SOCKET) + { + LOG_ERROR("[NetworkServer] Network socket could not be created."); + WSACleanup(); + return; + } + + /*---------------------------------------------------------*\ + | Set socket options - reuse addr | + \*---------------------------------------------------------*/ + setsockopt(server_sock[socket_count], SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + + /*---------------------------------------------------------*\ + | Bind the server socket | + \*---------------------------------------------------------*/ + if(bind(server_sock[socket_count], res->ai_addr, res->ai_addrlen) == SOCKET_ERROR) + { + if(errno == EADDRINUSE) + { + LOG_ERROR("[NetworkServer] Could not bind network socket. Is port %hu already being used?", GetPort()); + } + else if(errno == EACCES) + { + LOG_ERROR("[NetworkServer] Could not bind network socket. Access to socket was denied."); + } + else if(errno == EBADF) + { + LOG_ERROR("[NetworkServer] Could not bind network socket. sockfd is not a valid file descriptor."); + } + else if(errno == EINVAL) + { + LOG_ERROR("[NetworkServer] Could not bind network socket. The socket is already bound to an address, or addrlen is wrong, or addr is not a valid address for this socket's domain."); + } + else if(errno == ENOTSOCK) + { + LOG_ERROR("[NetworkServer] Could not bind network socket. The file descriptor sockfd does not refer to a socket."); + } + else + { + /*---------------------------------------------------------*\ + | errno could be a Linux specific error, see: | + | https://man7.org/linux/man-pages/man2/bind.2.html | + \*---------------------------------------------------------*/ + LOG_ERROR("[NetworkServer] Could not bind network socket. Error code: %d.", errno); + } + + WSACleanup(); + return; + } + + /*---------------------------------------------------------*\ + | Set socket options - no delay | + \*---------------------------------------------------------*/ + setsockopt(server_sock[socket_count], IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)); + + socket_count += 1; + } + + freeaddrinfo(result); + server_online = true; + + /*---------------------------------------------------------*\ + | Start the connection thread | + \*---------------------------------------------------------*/ + for(int curr_socket = 0; curr_socket < socket_count; curr_socket++) + { + ConnectionThread[curr_socket] = new std::thread(&NetworkServer::ConnectionThreadFunction, this, curr_socket); + ConnectionThread[curr_socket]->detach(); + } +} + +void NetworkServer::StopServer() +{ + int curr_socket; + server_online = false; + + ServerClientsMutex.lock(); + + for(unsigned int client_idx = 0; client_idx < ServerClients.size(); client_idx++) + { + delete ServerClients[client_idx]; + } + + ServerClients.clear(); + + for(curr_socket = 0; curr_socket < socket_count; curr_socket++) + { + shutdown(server_sock[curr_socket], SD_RECEIVE); + closesocket(server_sock[curr_socket]); + } + + ServerClientsMutex.unlock(); + + for(curr_socket = 0; curr_socket < socket_count; curr_socket++) + { + if(ConnectionThread[curr_socket]) + { + delete ConnectionThread[curr_socket]; + ConnectionThread[curr_socket] = nullptr; + } + } + + socket_count = 0; + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + ClientInfoChanged(); +} + +void NetworkServer::ConnectionThreadFunction(int socket_idx) +{ + /*---------------------------------------------------------*\ + | This thread handles client connections | + \*---------------------------------------------------------*/ + LOG_INFO("[NetworkServer] Network connection thread started on port %hu", GetPort()); + + while(server_online == true) + { + /*---------------------------------------------------------*\ + | Create new socket for client connection | + \*---------------------------------------------------------*/ + NetworkClientInfo * client_info = new NetworkClientInfo(); + + /*---------------------------------------------------------*\ + | Listen for incoming client connection on the server | + | socket. This call blocks until a connection is | + | established | + \*---------------------------------------------------------*/ + if(listen(server_sock[socket_idx], 10) < 0) + { + LOG_INFO("[NetworkServer] Connection thread closed"); + server_online = false; + + return; + } + + server_listening = true; + ServerListeningChanged(); + + /*---------------------------------------------------------*\ + | Accept the client connection | + \*---------------------------------------------------------*/ + client_info->client_sock = accept_select((int)server_sock[socket_idx]); + + if(client_info->client_sock < 0) + { + LOG_INFO("[NetworkServer] Connection thread closed"); + server_online = false; + + server_listening = false; + ServerListeningChanged(); + + return; + } + + /*---------------------------------------------------------*\ + | Get the new client socket and store it in the clients | + | vector | + \*---------------------------------------------------------*/ + u_long arg = 0; + ioctlsocket(client_info->client_sock, FIONBIO, &arg); + setsockopt(client_info->client_sock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)); + + /*---------------------------------------------------------*\ + | Discover the remote hosts IP | + \*---------------------------------------------------------*/ + struct sockaddr_storage tmp_addr; + char ipstr[INET6_ADDRSTRLEN]; + socklen_t len; + len = sizeof(tmp_addr); + getpeername(client_info->client_sock, (struct sockaddr*)&tmp_addr, &len); + + if(tmp_addr.ss_family == AF_INET) + { + struct sockaddr_in *s_4 = (struct sockaddr_in *)&tmp_addr; + inet_ntop(AF_INET, &s_4->sin_addr, ipstr, sizeof(ipstr)); + client_info->client_ip = ipstr; + } + else + { + struct sockaddr_in6 *s_6 = (struct sockaddr_in6 *)&tmp_addr; + inet_ntop(AF_INET6, &s_6->sin6_addr, ipstr, sizeof(ipstr)); + client_info->client_ip = ipstr; + } + + /*---------------------------------------------------------*\ + | We need to lock before the thread could possibly finish | + \*---------------------------------------------------------*/ + ServerClientsMutex.lock(); + + /*---------------------------------------------------------*\ + | Start a listener thread for the new client socket | + \*---------------------------------------------------------*/ + client_info->client_listen_thread = new std::thread(&NetworkServer::ListenThreadFunction, this, client_info); + client_info->client_listen_thread->detach(); + + ServerClients.push_back(client_info); + ServerClientsMutex.unlock(); + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + ClientInfoChanged(); + } + + LOG_INFO("[NetworkServer] Connection thread closed"); + server_online = false; + server_listening = false; + ServerListeningChanged(); +} + +int NetworkServer::accept_select(int sockfd) +{ + fd_set set; + struct timeval timeout; + + while(1) + { + timeout.tv_sec = TCP_TIMEOUT_SECONDS; + timeout.tv_usec = 0; + + FD_ZERO(&set); + FD_SET(sockfd, &set); + + int rv = select(sockfd + 1, &set, NULL, NULL, &timeout); + + if(rv == SOCKET_ERROR || server_online == false) + { + return -1; + } + else if(rv == 0) + { + continue; + } + else + { + return(accept((int)sockfd, NULL, NULL)); + } + } +} + +int NetworkServer::recv_select(SOCKET s, char *buf, int len, int flags) +{ + fd_set set; + struct timeval timeout; + + while(1) + { + timeout.tv_sec = TCP_TIMEOUT_SECONDS; + timeout.tv_usec = 0; + + FD_ZERO(&set); + FD_SET(s, &set); + + int rv = select((int)s + 1, &set, NULL, NULL, &timeout); + + if(rv == SOCKET_ERROR || server_online == false) + { + return 0; + } + else if(rv == 0) + { + continue; + } + else + { + /*-------------------------------------------------*\ + | Set QUICKACK socket option on Linux to improve | + | performance | + \*-------------------------------------------------*/ +#ifdef __linux__ + setsockopt(s, IPPROTO_TCP, TCP_QUICKACK, &yes, sizeof(yes)); +#endif + return(recv(s, buf, len, flags)); + } + } +} + +void NetworkServer::ListenThreadFunction(NetworkClientInfo * client_info) +{ + SOCKET client_sock = client_info->client_sock; + + LOG_INFO("[NetworkServer] Network server started"); + + /*---------------------------------------------------------*\ + | This thread handles messages received from clients | + \*---------------------------------------------------------*/ + while(server_online == true) + { + NetPacketHeader header; + int bytes_read = 0; + char * data = NULL; + + for(unsigned int i = 0; i < 4; i++) + { + /*---------------------------------------------------------*\ + | Read byte of magic | + \*---------------------------------------------------------*/ + bytes_read = recv_select(client_sock, &header.pkt_magic[i], 1, 0); + + if(bytes_read <= 0) + { + LOG_ERROR("[NetworkServer] recv_select failed receiving magic, closing listener"); + goto listen_done; + } + + /*---------------------------------------------------------*\ + | Test characters of magic "ORGB" | + \*---------------------------------------------------------*/ + if(header.pkt_magic[i] != openrgb_sdk_magic[i]) + { + LOG_ERROR("[NetworkServer] Invalid magic received"); + continue; + } + } + + /*---------------------------------------------------------*\ + | If we get to this point, the magic is correct. Read the | + | rest of the header | + \*---------------------------------------------------------*/ + bytes_read = 0; + do + { + int tmp_bytes_read = 0; + + tmp_bytes_read = recv_select(client_sock, (char *)&header.pkt_dev_idx + bytes_read, sizeof(header) - sizeof(header.pkt_magic) - bytes_read, 0); + + bytes_read += tmp_bytes_read; + + if(tmp_bytes_read <= 0) + { + LOG_ERROR("[NetworkServer] recv_select failed receiving header, closing listener"); + goto listen_done; + } + + } while(bytes_read != sizeof(header) - sizeof(header.pkt_magic)); + + /*---------------------------------------------------------*\ + | Header received, now receive the data | + \*---------------------------------------------------------*/ + bytes_read = 0; + if(header.pkt_size > 0) + { + data = new char[header.pkt_size]; + + do + { + int tmp_bytes_read = 0; + + tmp_bytes_read = recv_select(client_sock, &data[(unsigned int)bytes_read], header.pkt_size - bytes_read, 0); + + if(tmp_bytes_read <= 0) + { + LOG_ERROR("[NetworkServer] recv_select failed receiving data, closing listener"); + goto listen_done; + } + bytes_read += tmp_bytes_read; + + } while ((unsigned int)bytes_read < header.pkt_size); + } + + /*---------------------------------------------------------*\ + | Entire request received, select functionality based on | + | request ID | + \*---------------------------------------------------------*/ + switch(header.pkt_id) + { + case NET_PACKET_ID_REQUEST_CONTROLLER_COUNT: + SendReply_ControllerCount(client_sock); + break; + + case NET_PACKET_ID_REQUEST_CONTROLLER_DATA: + { + unsigned int protocol_version = 0; + + if(header.pkt_size == sizeof(unsigned int)) + { + memcpy(&protocol_version, data, sizeof(unsigned int)); + } + + SendReply_ControllerData(client_sock, header.pkt_dev_idx, protocol_version); + } + break; + + case NET_PACKET_ID_REQUEST_PROTOCOL_VERSION: + SendReply_ProtocolVersion(client_sock); + ProcessRequest_ClientProtocolVersion(client_sock, header.pkt_size, data); + break; + + case NET_PACKET_ID_SET_CLIENT_NAME: + if(data == NULL) + { + break; + } + + ProcessRequest_ClientString(client_sock, header.pkt_size, data); + break; + + case NET_PACKET_ID_REQUEST_RESCAN_DEVICES: + ProcessRequest_RescanDevices(); + break; + + case NET_PACKET_ID_RGBCONTROLLER_RESIZEZONE: + if(data == NULL) + { + break; + } + + if((header.pkt_dev_idx < controllers.size()) && (header.pkt_size == (2 * sizeof(int)))) + { + int zone; + int new_size; + + memcpy(&zone, data, sizeof(int)); + memcpy(&new_size, data + sizeof(int), sizeof(int)); + + controllers[header.pkt_dev_idx]->ResizeZone(zone, new_size); + profile_manager->SaveProfile("sizes", true); + } + break; + + case NET_PACKET_ID_RGBCONTROLLER_UPDATELEDS: + if(data == NULL) + { + break; + } + + /*---------------------------------------------------------*\ + | Verify the color description size (first 4 bytes of data) | + | matches the packet size in the header | + | | + | If protocol version is 4 or below and the legacy SDK | + | compatibility workaround is enabled, ignore this check. | + | This allows backwards compatibility with old versions of | + | SDK applications that didn't properly implement the size | + | field. | + \*---------------------------------------------------------*/ + if((header.pkt_size == *((unsigned int*)data)) + || ((client_info->client_protocol_version <= 4) + && (legacy_workaround_enabled))) + { + if(header.pkt_dev_idx < controllers.size()) + { + controllers[header.pkt_dev_idx]->SetColorDescription((unsigned char *)data); + controllers[header.pkt_dev_idx]->UpdateLEDs(); + } + } + else + { + LOG_ERROR("[NetworkServer] UpdateLEDs packet has invalid size. Packet size: %d, Data size: %d", header.pkt_size, *((unsigned int*)data)); + goto listen_done; + } + break; + + case NET_PACKET_ID_RGBCONTROLLER_UPDATEZONELEDS: + if(data == NULL) + { + break; + } + + /*---------------------------------------------------------*\ + | Verify the color description size (first 4 bytes of data) | + | matches the packet size in the header | + | | + | If protocol version is 4 or below and the legacy SDK | + | compatibility workaround is enabled, ignore this check. | + | This allows backwards compatibility with old versions of | + | SDK applications that didn't properly implement the size | + | field. | + \*---------------------------------------------------------*/ + if((header.pkt_size == *((unsigned int*)data)) + || ((client_info->client_protocol_version <= 4) + && (legacy_workaround_enabled))) + { + if(header.pkt_dev_idx < controllers.size()) + { + int zone; + + memcpy(&zone, &data[sizeof(unsigned int)], sizeof(int)); + + controllers[header.pkt_dev_idx]->SetZoneColorDescription((unsigned char *)data); + controllers[header.pkt_dev_idx]->UpdateZoneLEDs(zone); + } + } + else + { + LOG_ERROR("[NetworkServer] UpdateZoneLEDs packet has invalid size. Packet size: %d, Data size: %d", header.pkt_size, *((unsigned int*)data)); + goto listen_done; + } + break; + + case NET_PACKET_ID_RGBCONTROLLER_UPDATESINGLELED: + if(data == NULL) + { + break; + } + + /*---------------------------------------------------------*\ + | Verify the single LED color description size (8 bytes) | + | matches the packet size in the header | + \*---------------------------------------------------------*/ + if(header.pkt_size == (sizeof(int) + sizeof(RGBColor))) + { + if(header.pkt_dev_idx < controllers.size()) + { + int led; + + memcpy(&led, data, sizeof(int)); + + controllers[header.pkt_dev_idx]->SetSingleLEDColorDescription((unsigned char *)data); + controllers[header.pkt_dev_idx]->UpdateSingleLED(led); + } + } + else + { + LOG_ERROR("[NetworkServer] UpdateSingleLED packet has invalid size. Packet size: %d, Data size: %d", header.pkt_size, (sizeof(int) + sizeof(RGBColor))); + goto listen_done; + } + break; + + case NET_PACKET_ID_RGBCONTROLLER_SETCUSTOMMODE: + if(header.pkt_dev_idx < controllers.size()) + { + controllers[header.pkt_dev_idx]->SetCustomMode(); + } + break; + + case NET_PACKET_ID_RGBCONTROLLER_UPDATEMODE: + if(data == NULL) + { + break; + } + + /*---------------------------------------------------------*\ + | Verify the mode description size (first 4 bytes of data) | + | matches the packet size in the header | + | | + | If protocol version is 4 or below and the legacy SDK | + | compatibility workaround is enabled, ignore this check. | + | This allows backwards compatibility with old versions of | + | SDK applications that didn't properly implement the size | + | field. | + \*---------------------------------------------------------*/ + if((header.pkt_size == *((unsigned int*)data)) + || ((client_info->client_protocol_version <= 4) + && (legacy_workaround_enabled))) + { + if(header.pkt_dev_idx < controllers.size()) + { + controllers[header.pkt_dev_idx]->SetModeDescription((unsigned char *)data, client_info->client_protocol_version); + controllers[header.pkt_dev_idx]->UpdateMode(); + } + } + else + { + LOG_ERROR("[NetworkServer] UpdateMode packet has invalid size. Packet size: %d, Data size: %d", header.pkt_size, *((unsigned int*)data)); + goto listen_done; + } + break; + + case NET_PACKET_ID_RGBCONTROLLER_SAVEMODE: + if(data == NULL) + { + break; + } + + /*---------------------------------------------------------*\ + | Verify the mode description size (first 4 bytes of data) | + | matches the packet size in the header | + | | + | If protocol version is 4 or below and the legacy SDK | + | compatibility workaround is enabled, ignore this check. | + | This allows backwards compatibility with old versions of | + | SDK applications that didn't properly implement the size | + | field. | + \*---------------------------------------------------------*/ + if((header.pkt_size == *((unsigned int*)data)) + || ((client_info->client_protocol_version <= 4) + && (legacy_workaround_enabled))) + { + if(header.pkt_dev_idx < controllers.size()) + { + controllers[header.pkt_dev_idx]->SetModeDescription((unsigned char *)data, client_info->client_protocol_version); + controllers[header.pkt_dev_idx]->SaveMode(); + } + } + break; + + case NET_PACKET_ID_REQUEST_PROFILE_LIST: + SendReply_ProfileList(client_sock); + break; + + case NET_PACKET_ID_REQUEST_SAVE_PROFILE: + if(data == NULL) + { + break; + } + + if(profile_manager) + { + std::string profile_name; + profile_name.assign(data, header.pkt_size); + + profile_manager->SaveProfile(profile_name); + } + + break; + + case NET_PACKET_ID_REQUEST_LOAD_PROFILE: + if(data == NULL) + { + break; + } + + if(profile_manager) + { + std::string profile_name; + profile_name.assign(data, header.pkt_size); + + profile_manager->LoadProfile(profile_name); + } + + for(RGBController* controller : controllers) + { + controller->UpdateLEDs(); + } + + break; + + case NET_PACKET_ID_REQUEST_DELETE_PROFILE: + if(data == NULL) + { + break; + } + + if(profile_manager) + { + std::string profile_name; + profile_name.assign(data, header.pkt_size); + + profile_manager->DeleteProfile(profile_name); + } + + break; + + case NET_PACKET_ID_REQUEST_PLUGIN_LIST: + SendReply_PluginList(client_sock); + break; + + case NET_PACKET_ID_PLUGIN_SPECIFIC: + { + unsigned int plugin_pkt_type = *((unsigned int*)(data)); + unsigned int plugin_pkt_size = header.pkt_size - (sizeof(unsigned int)); + unsigned char* plugin_data = (unsigned char*)(data + sizeof(unsigned int)); + + if(header.pkt_dev_idx < plugins.size()) + { + NetworkPlugin plugin = plugins[header.pkt_dev_idx]; + unsigned char* output = plugin.callback(plugin.callback_arg, plugin_pkt_type, plugin_data, &plugin_pkt_size); + if(output != nullptr) + { + SendReply_PluginSpecific(client_sock, plugin_pkt_type, output, plugin_pkt_size); + } + } + break; + } + break; + + case NET_PACKET_ID_RGBCONTROLLER_CLEARSEGMENTS: + if(data == NULL) + { + break; + } + + if((header.pkt_dev_idx < controllers.size()) && (header.pkt_size == sizeof(int))) + { + int zone; + + memcpy(&zone, data, sizeof(int)); + + controllers[header.pkt_dev_idx]->ClearSegments(zone); + profile_manager->SaveProfile("sizes", true); + } + break; + + case NET_PACKET_ID_RGBCONTROLLER_ADDSEGMENT: + { + /*---------------------------------------------------------*\ + | Verify the segment description size (first 4 bytes of | + | data) matches the packet size in the header | + \*---------------------------------------------------------*/ + if(header.pkt_size == *((unsigned int*)data)) + { + if(header.pkt_dev_idx < controllers.size()) + { + controllers[header.pkt_dev_idx]->SetSegmentDescription((unsigned char *)data); + profile_manager->SaveProfile("sizes", true); + } + } + } + break; + } + + delete[] data; + } + +listen_done: + + ServerClientsMutex.lock(); + + for(unsigned int this_idx = 0; this_idx < ServerClients.size(); this_idx++) + { + if(ServerClients[this_idx] == client_info) + { + delete client_info; + ServerClients.erase(ServerClients.begin() + this_idx); + break; + } + } + + client_info = nullptr; + + ServerClientsMutex.unlock(); + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + ClientInfoChanged(); +} + +void NetworkServer::ProcessRequest_ClientProtocolVersion(SOCKET client_sock, unsigned int data_size, char * data) +{ + unsigned int protocol_version = 0; + + if(data_size == sizeof(unsigned int) && (data != NULL)) + { + memcpy(&protocol_version, data, sizeof(unsigned int)); + } + + if(protocol_version > OPENRGB_SDK_PROTOCOL_VERSION) + { + protocol_version = OPENRGB_SDK_PROTOCOL_VERSION; + } + + ServerClientsMutex.lock(); + for(unsigned int this_idx = 0; this_idx < ServerClients.size(); this_idx++) + { + if(ServerClients[this_idx]->client_sock == client_sock) + { + ServerClients[this_idx]->client_protocol_version = protocol_version; + break; + } + } + ServerClientsMutex.unlock(); + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + ClientInfoChanged(); +} + +void NetworkServer::ProcessRequest_ClientString(SOCKET client_sock, unsigned int data_size, char * data) +{ + ServerClientsMutex.lock(); + for(unsigned int this_idx = 0; this_idx < ServerClients.size(); this_idx++) + { + if(ServerClients[this_idx]->client_sock == client_sock) + { + ServerClients[this_idx]->client_string.assign(data, data_size); + break; + } + } + ServerClientsMutex.unlock(); + + /*---------------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*---------------------------------------------------------*/ + ClientInfoChanged(); +} + +void NetworkServer::ProcessRequest_RescanDevices() +{ + ResourceManager::get()->RescanDevices(); +} + +void NetworkServer::SendReply_ControllerCount(SOCKET client_sock) +{ + NetPacketHeader reply_hdr; + unsigned int reply_data; + + InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_CONTROLLER_COUNT, sizeof(unsigned int)); + + reply_data = (unsigned int)controllers.size(); + + send_in_progress.lock(); + send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); + send(client_sock, (const char *)&reply_data, sizeof(unsigned int), 0); + send_in_progress.unlock(); +} + +void NetworkServer::SendReply_ControllerData(SOCKET client_sock, unsigned int dev_idx, unsigned int protocol_version) +{ + if(dev_idx < controllers.size()) + { + NetPacketHeader reply_hdr; + unsigned char *reply_data = controllers[dev_idx]->GetDeviceDescription(protocol_version); + unsigned int reply_size; + + memcpy(&reply_size, reply_data, sizeof(reply_size)); + + InitNetPacketHeader(&reply_hdr, dev_idx, NET_PACKET_ID_REQUEST_CONTROLLER_DATA, reply_size); + + send_in_progress.lock(); + send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); + send(client_sock, (const char *)reply_data, reply_size, 0); + send_in_progress.unlock(); + + delete[] reply_data; + } +} + +void NetworkServer::SendReply_ProtocolVersion(SOCKET client_sock) +{ + NetPacketHeader reply_hdr; + unsigned int reply_data; + + InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_PROTOCOL_VERSION, sizeof(unsigned int)); + + reply_data = OPENRGB_SDK_PROTOCOL_VERSION; + + send_in_progress.lock(); + send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); + send(client_sock, (const char *)&reply_data, sizeof(unsigned int), 0); + send_in_progress.unlock(); +} + +void NetworkServer::SendRequest_DeviceListChanged(SOCKET client_sock) +{ + NetPacketHeader pkt_hdr; + + InitNetPacketHeader(&pkt_hdr, 0, NET_PACKET_ID_DEVICE_LIST_UPDATED, 0); + + send_in_progress.lock(); + send(client_sock, (char *)&pkt_hdr, sizeof(NetPacketHeader), 0); + send_in_progress.unlock(); +} + +void NetworkServer::SendReply_ProfileList(SOCKET client_sock) +{ + if(!profile_manager) + { + return; + } + + NetPacketHeader reply_hdr; + unsigned char *reply_data = profile_manager->GetProfileListDescription(); + unsigned int reply_size; + + memcpy(&reply_size, reply_data, sizeof(reply_size)); + + InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_PROFILE_LIST, reply_size); + + send_in_progress.lock(); + send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); + send(client_sock, (const char *)reply_data, reply_size, 0); + send_in_progress.unlock(); +} + +void NetworkServer::SendReply_PluginList(SOCKET client_sock) +{ + unsigned int data_size = 0; + unsigned int data_ptr = 0; + + /*---------------------------------------------------------*\ + | Calculate data size | + \*---------------------------------------------------------*/ + unsigned short num_plugins = (unsigned short)plugins.size(); + + data_size += sizeof(data_size); + data_size += sizeof(num_plugins); + + for(unsigned int i = 0; i < num_plugins; i++) + { + data_size += sizeof(unsigned short) * 3; + data_size += (unsigned int)strlen(plugins[i].name.c_str()) + 1; + data_size += (unsigned int)strlen(plugins[i].description.c_str()) + 1; + data_size += (unsigned int)strlen(plugins[i].version.c_str()) + 1; + data_size += sizeof(unsigned int) * 2; + } + + /*---------------------------------------------------------*\ + | Create data buffer | + \*---------------------------------------------------------*/ + unsigned char *data_buf = new unsigned char[data_size]; + + /*---------------------------------------------------------*\ + | Copy in data size | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &data_size, sizeof(data_size)); + data_ptr += sizeof(data_size); + + /*---------------------------------------------------------*\ + | Copy in num_plugins | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &num_plugins, sizeof(num_plugins)); + data_ptr += sizeof(num_plugins); + + for(unsigned int i = 0; i < num_plugins; i++) + { + /*---------------------------------------------------------*\ + | Copy in plugin name (size+data) | + \*---------------------------------------------------------*/ + unsigned short str_len = (unsigned short)strlen(plugins[i].name.c_str()) + 1; + + memcpy(&data_buf[data_ptr], &str_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], plugins[i].name.c_str()); + data_ptr += str_len; + + /*---------------------------------------------------------*\ + | Copy in plugin description (size+data) | + \*---------------------------------------------------------*/ + str_len = (unsigned short)strlen(plugins[i].description.c_str()) + 1; + + memcpy(&data_buf[data_ptr], &str_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], plugins[i].description.c_str()); + data_ptr += str_len; + + /*---------------------------------------------------------*\ + | Copy in plugin version (size+data) | + \*---------------------------------------------------------*/ + str_len = (unsigned short)strlen(plugins[i].version.c_str()) + 1; + + memcpy(&data_buf[data_ptr], &str_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], plugins[i].version.c_str()); + data_ptr += str_len; + + /*---------------------------------------------------------*\ + | Copy in plugin index (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &i, sizeof(unsigned int)); + data_ptr += sizeof(unsigned int); + + /*---------------------------------------------------------*\ + | Copy in plugin sdk version (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &plugins[i].protocol_version, sizeof(unsigned int)); + data_ptr += sizeof(unsigned int); + } + + NetPacketHeader reply_hdr; + unsigned int reply_size; + + memcpy(&reply_size, data_buf, sizeof(reply_size)); + + InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_REQUEST_PLUGIN_LIST, reply_size); + + send_in_progress.lock(); + send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); + send(client_sock, (const char *)data_buf, reply_size, 0); + send_in_progress.unlock(); + + delete [] data_buf; +} + +void NetworkServer::SendReply_PluginSpecific(SOCKET client_sock, unsigned int pkt_type, unsigned char* data, unsigned int data_size) +{ + NetPacketHeader reply_hdr; + + InitNetPacketHeader(&reply_hdr, 0, NET_PACKET_ID_PLUGIN_SPECIFIC, data_size + sizeof(pkt_type)); + + send_in_progress.lock(); + send(client_sock, (const char *)&reply_hdr, sizeof(NetPacketHeader), 0); + send(client_sock, (const char *)&pkt_type, sizeof(pkt_type), 0); + send(client_sock, (const char *)data, data_size, 0); + send_in_progress.unlock(); + + delete [] data; +} + +void NetworkServer::SetProfileManager(ProfileManagerInterface* profile_manager_pointer) +{ + profile_manager = profile_manager_pointer; +} + +void NetworkServer::RegisterPlugin(NetworkPlugin plugin) +{ + plugins.push_back(plugin); +} + +void NetworkServer::UnregisterPlugin(std::string plugin_name) +{ + for(std::vector::iterator it = plugins.begin(); it != plugins.end(); it++) + { + if(it->name == plugin_name) + { + plugins.erase(it); + break; + } + } +} diff --git a/NetworkServer.h b/NetworkServer.h new file mode 100644 index 0000000..62e8c1c --- /dev/null +++ b/NetworkServer.h @@ -0,0 +1,139 @@ +/*---------------------------------------------------------*\ +| NetworkServer.h | +| | +| OpenRGB SDK network server | +| | +| Adam Honse (CalcProgrammer1) 09 May 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "NetworkProtocol.h" +#include "net_port.h" +#include "ProfileManager.h" +#include "ResourceManager.h" + +#define MAXSOCK 32 +#define TCP_TIMEOUT_SECONDS 5 + +typedef void (*NetServerCallback)(void *); +typedef unsigned char* (*NetPluginCallback)(void *, unsigned int, unsigned char*, unsigned int*); + +struct NetworkPlugin +{ + std::string name; + std::string description; + std::string version; + NetPluginCallback callback; + void* callback_arg; + unsigned int protocol_version; +}; + +class NetworkClientInfo +{ +public: + NetworkClientInfo(); + ~NetworkClientInfo(); + + SOCKET client_sock; + std::thread * client_listen_thread; + std::string client_string; + unsigned int client_protocol_version; + std::string client_ip; +}; + +class NetworkServer +{ +public: + NetworkServer(std::vector& control); + ~NetworkServer(); + + std::string GetHost(); + unsigned short GetPort(); + bool GetOnline(); + bool GetListening(); + unsigned int GetNumClients(); + const char * GetClientString(unsigned int client_num); + const char * GetClientIP(unsigned int client_num); + unsigned int GetClientProtocolVersion(unsigned int client_num); + + void ClientInfoChanged(); + void DeviceListChanged(); + void RegisterClientInfoChangeCallback(NetServerCallback, void * new_callback_arg); + + void ServerListeningChanged(); + void RegisterServerListeningChangeCallback(NetServerCallback, void * new_callback_arg); + + void SetHost(std::string host); + void SetLegacyWorkaroundEnable(bool enable); + void SetPort(unsigned short new_port); + + void StartServer(); + void StopServer(); + + void ConnectionThreadFunction(int socket_idx); + void ListenThreadFunction(NetworkClientInfo * client_sock); + + void ProcessRequest_ClientProtocolVersion(SOCKET client_sock, unsigned int data_size, char * data); + void ProcessRequest_ClientString(SOCKET client_sock, unsigned int data_size, char * data); + void ProcessRequest_RescanDevices(); + + void SendReply_ControllerCount(SOCKET client_sock); + void SendReply_ControllerData(SOCKET client_sock, unsigned int dev_idx, unsigned int protocol_version); + void SendReply_ProtocolVersion(SOCKET client_sock); + + void SendRequest_DeviceListChanged(SOCKET client_sock); + void SendReply_ProfileList(SOCKET client_sock); + void SendReply_PluginList(SOCKET client_sock); + void SendReply_PluginSpecific(SOCKET client_sock, unsigned int pkt_type, unsigned char* data, unsigned int data_size); + + void SetProfileManager(ProfileManagerInterface* profile_manager_pointer); + + void RegisterPlugin(NetworkPlugin plugin); + void UnregisterPlugin(std::string plugin_name); + +protected: + std::string host; + unsigned short port_num; + std::atomic server_online; + std::atomic server_listening; + + std::vector& controllers; + + std::mutex ServerClientsMutex; + std::vector ServerClients; + std::thread * ConnectionThread[MAXSOCK]; + + std::mutex ClientInfoChangeMutex; + std::vector ClientInfoChangeCallbacks; + std::vector ClientInfoChangeCallbackArgs; + + std::mutex ServerListeningChangeMutex; + std::vector ServerListeningChangeCallbacks; + std::vector ServerListeningChangeCallbackArgs; + + ProfileManagerInterface* profile_manager; + + std::vector plugins; + + std::mutex send_in_progress; + +private: +#ifdef WIN32 + WSADATA wsa; +#endif + + bool legacy_workaround_enabled; + int socket_count; + SOCKET server_sock[MAXSOCK]; + + int accept_select(int sockfd); + int recv_select(SOCKET s, char *buf, int len, int flags); +}; diff --git a/OpenRGB.pro b/OpenRGB.pro new file mode 100644 index 0000000..a49a5a5 --- /dev/null +++ b/OpenRGB.pro @@ -0,0 +1,814 @@ +#-----------------------------------------------------------------------------------------------# +# OpenRGB 0.x QMake Project # +# # +# Adam Honse (CalcProgrammer1) 5/25/2020 # +#-----------------------------------------------------------------------------------------------# + +#-----------------------------------------------------------------------------------------------# +# Qt Configuration # +#-----------------------------------------------------------------------------------------------# +QT += \ + core \ + gui \ + +#-----------------------------------------------------------------------------------------------# +# Set compiler to use C++17 to make std::filesystem available # +#-----------------------------------------------------------------------------------------------# +CONFIG += c++17 \ + lrelease \ + embed_translations \ + silent \ + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +#-----------------------------------------------------------------------------------------------# +# Application Configuration # +#-----------------------------------------------------------------------------------------------# +MAJOR = 0 +MINOR = 9 +SUFFIX = 1.0rc3 + +SHORTHASH = $$system("git rev-parse --short=7 HEAD") +LASTTAG = "release_"$$MAJOR"."$$MINOR +COMMAND = "git rev-list --count "$$LASTTAG"..HEAD" +COMMITS = $$system($$COMMAND) + +VERSION_NUM = $$MAJOR"."$$MINOR"."$$COMMITS +VERSION_STR = $$MAJOR"."$$MINOR + +VERSION_DEB = $$VERSION_NUM +VERSION_WIX = $$VERSION_NUM +VERSION_AUR = $$VERSION_NUM +VERSION_RPM = $$VERSION_NUM + +equals(SUFFIX, "git") { +VERSION_STR = $$VERSION_STR"+ ("$$SUFFIX$$COMMITS")" +VERSION_DEB = $$VERSION_DEB"~git"$$SHORTHASH +VERSION_AUR = $$VERSION_AUR".g"$$SHORTHASH +VERSION_RPM = $$VERSION_RPM"^git"$$SHORTHASH +} else { + !isEmpty(SUFFIX) { +VERSION_STR = $$VERSION_STR"+ ("$$SUFFIX")" +VERSION_DEB = $$VERSION_DEB"~"$$SUFFIX +VERSION_AUR = $$VERSION_AUR"."$$SUFFIX +VERSION_RPM = $$VERSION_RPM"^"$$SUFFIX + } +} + +TARGET = OpenRGB +TEMPLATE = app + +message("VERSION_NUM: "$$VERSION_NUM) +message("VERSION_STR: "$$VERSION_STR) +message("VERSION_SFX: "$$SUFFIX) +message("VERSION_DEB: "$$VERSION_DEB) +message("VERSION_WIX: "$$VERSION_WIX) +message("VERSION_AUR: "$$VERSION_AUR) +message("VERSION_RPM: "$$VERSION_RPM) +message("QT_VERSION: "$$QT_VERSION) +#-----------------------------------------------------------------------------------------------# +# Automatically generated build information # +#-----------------------------------------------------------------------------------------------# +win32:BUILDDATE = $$system(date /t) +linux:BUILDDATE = $$system(date -R -d "@${SOURCE_DATE_EPOCH:-$(date +%s)}") +freebsd:BUILDDATE = $$system(date -j -R -r "${SOURCE_DATE_EPOCH:-$(date +%s)}") +macx:BUILDDATE = $$system(date -j -R -r "${SOURCE_DATE_EPOCH:-$(date +%s)}") +GIT_COMMIT_ID = $$system(git log -n 1 --pretty=format:"%H") +GIT_COMMIT_DATE = $$system(git log -n 1 --pretty=format:"%ci") + +unix { + GIT_BRANCH = $$system(sh scripts/git-get-branch.sh) +} +else { + GIT_BRANCH = $$system(powershell -ExecutionPolicy Bypass -File scripts/git-get-branch.ps1) +} + +message("GIT_BRANCH: "$$GIT_BRANCH) +DEFINES += \ + VERSION_STRING=\\"\"\"$$VERSION_STR\\"\"\" \ + BUILDDATE_STRING=\\"\"\"$$BUILDDATE\\"\"\" \ + GIT_COMMIT_ID=\\"\"\"$$GIT_COMMIT_ID\\"\"\" \ + GIT_COMMIT_DATE=\\"\"\"$$GIT_COMMIT_DATE\\"\"\" \ + GIT_BRANCH=\\"\"\"$$GIT_BRANCH\\"\"\" + +#-----------------------------------------------------------------------------------------------# +# OpenRGB dynamically added sources # +#-----------------------------------------------------------------------------------------------# +FORMS += $$files("qt/*.ui", true) + +for(iter, FORMS) { + GUI_INCLUDES += $$dirname(iter) +} +GUI_INCLUDES = $$unique(GUI_INCLUDES) + +GUI_H = $$files("qt/*.h", true) +GUI_CPP = $$files("qt/*.cpp", true) + +CONTROLLER_H = $$files("Controllers/*.h", true) +CONTROLLER_CPP = $$files("Controllers/*.cpp", true) + +for(iter, $$list($$CONTROLLER_H)) { + CONTROLLER_INCLUDES += $$dirname(iter) +} +CONTROLLER_INCLUDES = $$unique(CONTROLLER_INCLUDES) + +#-----------------------------------------------------------------------------------------------# +# Remove OS-specific files from the overall controller headers and sources lists # +# The suffixes _Windows, _Linux, _FreeBSD, and _MacOS are usable to denote that a file only # +# applies to one or more OSes. The suffixes may be combined such as _Windows_Linux.cpp. # +#-----------------------------------------------------------------------------------------------# +CONTROLLER_H_WINDOWS = $$files("Controllers/*_Windows*.h", true) +CONTROLLER_CPP_WINDOWS = $$files("Controllers/*_Windows*.cpp", true) +CONTROLLER_H_LINUX = $$files("Controllers/*_Linux*.h", true) +CONTROLLER_CPP_LINUX = $$files("Controllers/*_Linux*.cpp", true) +CONTROLLER_H_FREEBSD = $$files("Controllers/*_FreeBSD*.h", true) +CONTROLLER_CPP_FREEBSD = $$files("Controllers/*_FreeBSD*.cpp", true) +CONTROLLER_H_MACOS = $$files("Controllers/*_MacOS*.h", true) +CONTROLLER_CPP_MACOS = $$files("Controllers/*_MacOS*.cpp", true) + +CONTROLLER_H -= $$CONTROLLER_H_WINDOWS +CONTROLLER_H -= $$CONTROLLER_H_LINUX +CONTROLLER_H -= $$CONTROLLER_H_FREEBSD +CONTROLLER_H -= $$CONTROLLER_H_MACOS + +CONTROLLER_CPP -= $$CONTROLLER_CPP_WINDOWS +CONTROLLER_CPP -= $$CONTROLLER_CPP_LINUX +CONTROLLER_CPP -= $$CONTROLLER_CPP_FREEBSD +CONTROLLER_CPP -= $$CONTROLLER_CPP_MACOS + +#-----------------------------------------------------------------------------------------------# +# OpenRGB Common # +#-----------------------------------------------------------------------------------------------# +INCLUDEPATH += \ + $$CONTROLLER_INCLUDES \ + $$GUI_INCLUDES \ + dependencies/ColorWheel \ + dependencies/CRCpp/ \ + dependencies/hueplusplus-1.2.0/include \ + dependencies/hueplusplus-1.2.0/include/hueplusplus \ + dependencies/httplib \ + dependencies/json/ \ + dependencies/mdns \ + dmiinfo/ \ + hidapi_wrapper/ \ + i2c_smbus/ \ + i2c_tools/ \ + interop/ \ + net_port/ \ + pci_ids/ \ + scsiapi/ \ + serial_port/ \ + super_io/ \ + AutoStart/ \ + KeyboardLayoutManager/ \ + RGBController/ \ + qt/ \ + SPDAccessor/ \ + SuspendResume/ \ + dependencies/stb/ + +HEADERS += \ + $$GUI_H \ + $$CONTROLLER_H \ + Colors.h \ + dependencies/ColorWheel/ColorWheel.h \ + dependencies/json/nlohmann/json.hpp \ + LogManager.h \ + NetworkClient.h \ + NetworkProtocol.h \ + NetworkServer.h \ + OpenRGBPluginInterface.h \ + PluginManager.h \ + ProfileManager.h \ + ResourceManager.h \ + ResourceManagerInterface.h \ + SettingsManager.h \ + Detector.h \ + DeviceDetector.h \ + dmiinfo/dmiinfo.h \ + filesystem.h \ + hidapi_wrapper/hidapi_wrapper.h \ + i2c_smbus/i2c_smbus.h \ + i2c_tools/i2c_tools.h \ + interop/DeviceGuard.h \ + interop/DeviceGuardLock.h \ + interop/DeviceGuardManager.h \ + net_port/net_port.h \ + pci_ids/pci_ids.h \ + scsiapi/scsiapi.h \ + serial_port/find_usb_serial_port.h \ + serial_port/serial_port.h \ + super_io/super_io.h \ + MathUtils.h \ + StringUtils.h \ + SuspendResume/SuspendResume.h \ + AutoStart/AutoStart.h \ + KeyboardLayoutManager/KeyboardLayoutManager.h \ + RGBController/RGBController.h \ + RGBController/RGBController_Dummy.h \ + RGBController/RGBControllerKeyNames.h \ + RGBController/RGBController_Network.h \ + startup/startup.h \ + +SOURCES += \ + $$GUI_CPP \ + $$CONTROLLER_CPP \ + dependencies/ColorWheel/ColorWheel.cpp \ + dependencies/hueplusplus-1.2.0/src/Action.cpp \ + dependencies/hueplusplus-1.2.0/src/APICache.cpp \ + dependencies/hueplusplus-1.2.0/src/BaseDevice.cpp \ + dependencies/hueplusplus-1.2.0/src/BaseHttpHandler.cpp \ + dependencies/hueplusplus-1.2.0/src/Bridge.cpp \ + dependencies/hueplusplus-1.2.0/src/BridgeConfig.cpp \ + dependencies/hueplusplus-1.2.0/src/CLIPSensors.cpp \ + dependencies/hueplusplus-1.2.0/src/ColorUnits.cpp \ + dependencies/hueplusplus-1.2.0/src/EntertainmentMode.cpp \ + dependencies/hueplusplus-1.2.0/src/ExtendedColorHueStrategy.cpp \ + dependencies/hueplusplus-1.2.0/src/ExtendedColorTemperatureStrategy.cpp \ + dependencies/hueplusplus-1.2.0/src/Group.cpp \ + dependencies/hueplusplus-1.2.0/src/HueCommandAPI.cpp \ + dependencies/hueplusplus-1.2.0/src/HueDeviceTypes.cpp \ + dependencies/hueplusplus-1.2.0/src/HueException.cpp \ + dependencies/hueplusplus-1.2.0/src/Light.cpp \ + dependencies/hueplusplus-1.2.0/src/ModelPictures.cpp \ + dependencies/hueplusplus-1.2.0/src/NewDeviceList.cpp \ + dependencies/hueplusplus-1.2.0/src/Scene.cpp \ + dependencies/hueplusplus-1.2.0/src/Schedule.cpp \ + dependencies/hueplusplus-1.2.0/src/Sensor.cpp \ + dependencies/hueplusplus-1.2.0/src/SimpleBrightnessStrategy.cpp \ + dependencies/hueplusplus-1.2.0/src/SimpleColorHueStrategy.cpp \ + dependencies/hueplusplus-1.2.0/src/SimpleColorTemperatureStrategy.cpp \ + dependencies/hueplusplus-1.2.0/src/StateTransaction.cpp \ + dependencies/hueplusplus-1.2.0/src/TimePattern.cpp \ + dependencies/hueplusplus-1.2.0/src/UPnP.cpp \ + dependencies/hueplusplus-1.2.0/src/Utils.cpp \ + dependencies/hueplusplus-1.2.0/src/ZLLSensors.cpp \ + startup/startup.cpp \ + cli.cpp \ + dmiinfo/dmiinfo.cpp \ + LogManager.cpp \ + NetworkClient.cpp \ + NetworkProtocol.cpp \ + NetworkServer.cpp \ + PluginManager.cpp \ + ProfileManager.cpp \ + ResourceManager.cpp \ + SPDAccessor/DDR4DirectAccessor.cpp \ + SPDAccessor/DDR5DirectAccessor.cpp \ + SPDAccessor/SPDAccessor.cpp \ + SPDAccessor/SPDDetector.cpp \ + SPDAccessor/SPDWrapper.cpp \ + SettingsManager.cpp \ + i2c_smbus/i2c_smbus.cpp \ + i2c_tools/i2c_tools.cpp \ + interop/DeviceGuard.cpp \ + interop/DeviceGuardLock.cpp \ + interop/DeviceGuardManager.cpp \ + net_port/net_port.cpp \ + serial_port/serial_port.cpp \ + MathUtils.cpp \ + StringUtils.cpp \ + AutoStart/AutoStart.cpp \ + KeyboardLayoutManager/KeyboardLayoutManager.cpp \ + RGBController/RGBController.cpp \ + RGBController/RGBController_Dummy.cpp \ + RGBController/RGBControllerKeyNames.cpp \ + RGBController/RGBController_Network.cpp \ + +RESOURCES += \ + qt/resources.qrc \ + +#-----------------------------------------------------------------------------------------------# +# General configuration to decide if in-tree dependencies are used or not +#-----------------------------------------------------------------------------------------------# + +!system_libe131:SOURCES += dependencies/libe131/src/e131.c +!system_libe131:INCLUDEPATH += dependencies/libe131/src/ + +#-----------------------------------------------------------------------------------------------# +# General configuration out-of-tree dependencies if in-tree are not used for systems +# who use pkg-config i.e. Unix-like. Also includes macOS as Homebrew uses pkg-config too. +#-----------------------------------------------------------------------------------------------# + +unix { + system_libe131 { + CONFIG += link_pkgconfig + PKGCONFIG += libe131 + } +} + +#-----------------------------------------------------------------------------------------------# +# Translations # +# NB: Translation files should not be added dynamically due to the process # +# to add new translations relies on entries here in OpenRGB.pro # +#-----------------------------------------------------------------------------------------------# +TRANSLATIONS += \ + qt/i18n/OpenRGB_be_BY.ts \ + qt/i18n/OpenRGB_de_DE.ts \ + qt/i18n/OpenRGB_el_GR.ts \ + qt/i18n/OpenRGB_en_US.ts \ + qt/i18n/OpenRGB_en_AU.ts \ + qt/i18n/OpenRGB_en_GB.ts \ + qt/i18n/OpenRGB_es_ES.ts \ + qt/i18n/OpenRGB_fr_FR.ts \ + qt/i18n/OpenRGB_hr_HR.ts \ + qt/i18n/OpenRGB_it_IT.ts \ + qt/i18n/OpenRGB_ja_JP.ts \ + qt/i18n/OpenRGB_ko_KR.ts \ + qt/i18n/OpenRGB_ms_MY.ts \ + qt/i18n/OpenRGB_nb_NO.ts \ + qt/i18n/OpenRGB_pl_PL.ts \ + qt/i18n/OpenRGB_pt_BR.ts \ + qt/i18n/OpenRGB_ru_RU.ts \ + qt/i18n/OpenRGB_uk_UA.ts \ + qt/i18n/OpenRGB_zh_CN.ts \ + qt/i18n/OpenRGB_zh_TW.ts \ + +#-----------------------------------------------------------------------------------------------# +# Windows-specific Configuration # +#-----------------------------------------------------------------------------------------------# +win32:QMAKE_CXXFLAGS += /utf-8 +win32:INCLUDEPATH += \ + dependencies/display-library/include \ + dependencies/hidapi-win/include \ + dependencies/libusb-1.0.27/include \ + dependencies/mbedtls-3.2.1/include \ + dependencies/NVFC \ + dependencies/PawnIO \ + i2c_smbus/Windows \ + wmi/ \ + +win32:SOURCES += $$CONTROLLER_CPP_WINDOWS + +win32:SOURCES += \ + dependencies/hueplusplus-1.2.0/src/WinHttpHandler.cpp \ + dependencies/NVFC/nvapi.cpp \ + i2c_smbus/Windows/i2c_smbus_amdadl.cpp \ + i2c_smbus/Windows/i2c_smbus_nvapi.cpp \ + scsiapi/scsiapi_windows.c \ + serial_port/find_usb_serial_port_win.cpp \ + SuspendResume/SuspendResume_Windows.cpp \ + wmi/wmi.cpp \ + AutoStart/AutoStart-Windows.cpp \ + startup/main_Windows.cpp \ + +win32:HEADERS += $$CONTROLLER_H_WINDOWS + +win32:HEADERS += \ + dependencies/display-library/include/adl_defines.h \ + dependencies/display-library/include/adl_sdk.h \ + dependencies/display-library/include/adl_structures.h \ + dependencies/NVFC/nvapi.h \ + dependencies/PawnIO/PawnIOLib.h \ + i2c_smbus/Windows/i2c_smbus_amdadl.h \ + i2c_smbus/Windows/i2c_smbus_nvapi.h \ + i2c_smbus/Windows/i2c_smbus_pawnio.h \ + wmi/wmi.h \ + AutoStart/AutoStart-Windows.h \ + SuspendResume/SuspendResume_Windows.h \ + +win32:contains(QMAKE_TARGET.arch, x86_64) { + win32:SOURCES += \ + i2c_smbus/Windows/i2c_smbus_pawnio.cpp \ + super_io/super_io_pawnio.cpp \ + + LIBS += \ + -lws2_32 \ + -liphlpapi \ + -L"$$PWD/dependencies/libusb-1.0.27/VS2019/MS64/dll" -llibusb-1.0 \ + -L"$$PWD/dependencies/hidapi-win/x64/" -lhidapi \ + -L"$$PWD/dependencies/mbedtls-3.2.1/lib/x64/" -lmbedcrypto -lmbedtls -lmbedx509 \ + -L"$$PWD/dependencies/PawnIO/" -lPawnIOLib \ +} + +win32:contains(QMAKE_TARGET.arch, x86) { + win32:SOURCES += \ + super_io/super_io.cpp \ + + LIBS += \ + -lws2_32 \ + -liphlpapi \ + -L"$$PWD/dependencies/libusb-1.0.27/VS2019/MS32/dll" -llibusb-1.0 \ + -L"$$PWD/dependencies/hidapi-win/x86/" -lhidapi \ + -L"$$PWD/dependencies/mbedtls-3.2.1/lib/x86/" -lmbedcrypto -lmbedtls -lmbedx509 \ +} + +win32:DEFINES -= \ + UNICODE + +win32:DEFINES += \ + USE_HID_USAGE \ + _MBCS \ + WIN32 \ + _CRT_SECURE_NO_WARNINGS \ + _WINSOCK_DEPRECATED_NO_WARNINGS \ + WIN32_LEAN_AND_MEAN \ + +win32:RC_ICONS += \ + qt/OpenRGB.ico + +win32:DISTFILES += \ + dependencies/PawnIO/modules/SmbusPIIX4.bin \ + dependencies/PawnIO/modules/SmbusI801.bin \ + dependencies/PawnIO/modules/LpcIO.bin + +#-----------------------------------------------------------------------------------------------# +# Windows GitLab CI Configuration # +#-----------------------------------------------------------------------------------------------# +win32:CONFIG(debug, debug|release) { + win32:DESTDIR = debug +} + +win32:CONFIG(release, debug|release) { + win32:DESTDIR = release +} + +win32:OBJECTS_DIR = _intermediate_$$DESTDIR/.obj +win32:MOC_DIR = _intermediate_$$DESTDIR/.moc +win32:RCC_DIR = _intermediate_$$DESTDIR/.qrc +win32:UI_DIR = _intermediate_$$DESTDIR/.ui + +#-----------------------------------------------------------------------------------------------# +# Copy dependencies to output directory # +#-----------------------------------------------------------------------------------------------# + +win32:contains(QMAKE_TARGET.arch, x86_64) { + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/libusb-1.0.27/VS2019/MS64/dll/libusb-1.0.dll)\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/hidapi-win/x64/hidapi.dll )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/PawnIOLib.dll )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/SmbusPIIX4.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/SmbusI801.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/SmbusIntelSkylakeIMC.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/SmbusNCT6793.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/PawnIO/modules/LpcIO.bin )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + first.depends = $(first) copydata + export(first.depends) + export(copydata.commands) + QMAKE_EXTRA_TARGETS += first copydata +} + +win32:contains(QMAKE_TARGET.arch, x86) { + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/libusb-1.0.27/VS2019/MS32/dll/libusb-1.0.dll)\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + copydata.commands += $(COPY_FILE) \"$$shell_path($$PWD/dependencies/hidapi-win/x86/hidapi.dll )\" \"$$shell_path($$DESTDIR)\" $$escape_expand(\n\t) + + first.depends = $(first) copydata + export(first.depends) + export(copydata.commands) + QMAKE_EXTRA_TARGETS += first copydata +} + +#-----------------------------------------------------------------------------------------------# +# Linux-specific Configuration # +#-----------------------------------------------------------------------------------------------# +contains(QMAKE_PLATFORM, linux) { + CONFIG += link_pkgconfig + + PKGCONFIG += \ + libusb-1.0 + + TARGET = $$lower($$TARGET) + + HEADERS += $$CONTROLLER_H_LINUX + + HEADERS += \ + dependencies/NVFC/nvapi.h \ + i2c_smbus/Linux/i2c_smbus_linux.h \ + AutoStart/AutoStart-Linux.h \ + SPDAccessor/EE1004Accessor_Linux.h \ + SPDAccessor/SPD5118Accessor_Linux.h \ + SuspendResume/SuspendResume_Linux_FreeBSD.h \ + super_io/super_io.h \ + + INCLUDEPATH += \ + dependencies/NVFC \ + i2c_smbus/Linux \ + /usr/include/mbedtls/ \ + + LIBS += \ + -L/usr/lib/mbedtls/ \ + -lmbedx509 \ + -lmbedtls \ + -lmbedcrypto \ + -ldl \ + + COMPILER_VERSION = $$system($$QMAKE_CXX " -dumpversion") + if (!versionAtLeast(COMPILER_VERSION, "9")) { + LIBS += -lstdc++fs + } + + QT += dbus + + QMAKE_CXXFLAGS += -Wno-implicit-fallthrough -Wno-psabi + + #-------------------------------------------------------------------------------------------# + # Determine which hidapi to use based on availability # + # Prefer hidraw backend, then libusb # + #-------------------------------------------------------------------------------------------# + packagesExist(hidapi-hidraw) { + PKGCONFIG += hidapi-hidraw + + #---------------------------------------------------------------------------------------# + # hidapi-hidraw >= 0.10.1 supports USAGE/USAGE_PAGE # + # Define USE_HID_USAGE if hidapi-hidraw supports it # + #---------------------------------------------------------------------------------------# + HIDAPI_HIDRAW_VERSION = $$system($$PKG_CONFIG --modversion hidapi-hidraw) + if(versionAtLeast(HIDAPI_HIDRAW_VERSION, "0.10.1")) { + DEFINES += USE_HID_USAGE + } + } else { + packagesExist(hidapi-libusb) { + PKGCONFIG += hidapi-libusb + } else { + PKGCONFIG += hidapi + } + } + + SOURCES += $$CONTROLLER_CPP_LINUX + + SOURCES += \ + dependencies/hueplusplus-1.2.0/src/LinHttpHandler.cpp \ + dependencies/NVFC/nvapi.cpp \ + i2c_smbus/Linux/i2c_smbus_linux.cpp \ + scsiapi/scsiapi_linux.c \ + serial_port/find_usb_serial_port_linux.cpp \ + AutoStart/AutoStart-Linux.cpp \ + SPDAccessor/EE1004Accessor_Linux.cpp \ + SPDAccessor/SPD5118Accessor_Linux.cpp \ + SuspendResume/SuspendResume_Linux_FreeBSD.cpp \ + startup/main_FreeBSD_Linux_MacOS.cpp \ + super_io/super_io.cpp \ + + #-------------------------------------------------------------------------------------------# + # Set up install paths # + # These install paths are used for AppImage and .deb packaging # + #-------------------------------------------------------------------------------------------# + isEmpty(PREFIX) { + PREFIX = /usr + } + + !defined(OPENRGB_SYSTEM_PLUGIN_DIRECTORY, var):OPENRGB_SYSTEM_PLUGIN_DIRECTORY = \ + "$$PREFIX/lib/openrgb/plugins" \ + + DEFINES += \ + OPENRGB_SYSTEM_PLUGIN_DIRECTORY=\\"\"\"$$OPENRGB_SYSTEM_PLUGIN_DIRECTORY\\"\"\" \ + + #-------------------------------------------------------------------------------------------# + # Custom target for dynamically created udev_rules # + # Ordinarily you would add the 'udev_rules' target to both QMAKE_EXTRA_TARGETS to add a # + # rule in the Makefile and PRE_TARGETDEPS to ensure it is a dependency of the TARGET # + # # + # ie. QMAKE_EXTRA_TARGETS += udev_rules # + # PRE_TARGETDEPS += udev_rules # + #-------------------------------------------------------------------------------------------# + CONFIG(release, debug|release) { + udev_rules.CONFIG = no_check_exist + udev_rules.target = 60-openrgb.rules + udev_rules.path = $$PREFIX/lib/udev/rules.d/ + + exists($$udev_rules.target) { + message($$udev_rules.target " - UDEV rules file exists. Removing from build") + udev_rules.files = $$udev_rules.target + } else { + message($$udev_rules.target " - UDEV rules file missing. Adding script to build") + #-----------------------------------------------------------------------------------# + # This is a compiler config flag to save the preproccessed .ii & .s # + # files so as to automatically process the UDEV rules and the Supported Devices # + #-----------------------------------------------------------------------------------# + QMAKE_CXXFLAGS+=-save-temps + QMAKE_CXXFLAGS-=-pipe + udev_rules.extra = $$PWD/scripts/build-udev-rules.sh $$PWD $$GIT_COMMIT_ID + udev_rules.files = $$OUT_PWD/60-openrgb.rules + } + } + + #-------------------------------------------------------------------------------------------# + # Add static files to installation # + #-------------------------------------------------------------------------------------------# + target.path=$$PREFIX/bin/ + desktop.path=$$PREFIX/share/applications/ + desktop.files+=qt/org.openrgb.OpenRGB.desktop + icon.path=$$PREFIX/share/icons/hicolor/128x128/apps/ + icon.files+=qt/org.openrgb.OpenRGB.png + metainfo.path=$$PREFIX/share/metainfo/ + metainfo.files+=qt/org.openrgb.OpenRGB.metainfo.xml + systemd_service.path=$$PREFIX/lib/systemd/system/ + systemd_service.files+=qt/openrgb.service + tmpfiles.path=$$PREFIX/lib/tmpfiles.d/ + tmpfiles.files+=qt/openrgb.conf + INSTALLS += target desktop icon metainfo udev_rules systemd_service tmpfiles +} + +#-----------------------------------------------------------------------------------------------# +# FreeBSD-specific Configuration # +#-----------------------------------------------------------------------------------------------# +contains(QMAKE_PLATFORM, freebsd) { + CONFIG += link_pkgconfig + + PKGCONFIG += \ + libusb-1.0 + + TARGET = $$lower($$TARGET) + + HEADERS += $$CONTROLLER_H_FREEBSD + + HEADERS += \ + AutoStart/AutoStart-FreeBSD.h \ + SuspendResume/SuspendResume_Linux_FreeBSD.h \ + super_io/super_io.h \ + + HEADERS -= \ + Controllers/SeagateController/RGBController_Seagate.h \ + Controllers/SeagateController/SeagateController.h \ + Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_ROGArion.h \ + $$CONTROLLER_H_WINDOWS \ + + LIBS += \ + -lmbedx509 \ + -lmbedtls \ + -lmbedcrypto \ + + COMPILER_VERSION = $$system($$QMAKE_CXX " -dumpversion") + if (!versionAtLeast(COMPILER_VERSION, "9")) { + LIBS += -lstdc++fs + } + + QT += dbus + + #-------------------------------------------------------------------------------------------# + # Determine which hidapi to use based on availability # + # Prefer hidraw backend, then libusb # + #-------------------------------------------------------------------------------------------# + packagesExist(hidapi-hidraw) { + PKGCONFIG += hidapi-hidraw + + #---------------------------------------------------------------------------------------# + # hidapi-hidraw >= 0.10.1 supports USAGE/USAGE_PAGE # + # Define USE_HID_USAGE if hidapi-hidraw supports it # + #---------------------------------------------------------------------------------------# + packagesExist(hidapi-hidraw>=0.10.1) { + DEFINES += USE_HID_USAGE + } + } else { + packagesExist(hidapi-libusb) { + PKGCONFIG += hidapi-libusb + } else { + PKGCONFIG += hidapi + } + } + + SOURCES += $$CONTROLLER_CPP_FREEBSD + + SOURCES += \ + dependencies/hueplusplus-1.2.0/src/LinHttpHandler.cpp \ + serial_port/find_usb_serial_port_linux.cpp \ + AutoStart/AutoStart-FreeBSD.cpp \ + SuspendResume/SuspendResume_Linux_FreeBSD.cpp \ + startup/main_FreeBSD_Linux_MacOS.cpp \ + super_io/super_io.cpp \ + + SOURCES -= \ + Controllers/SeagateController/RGBController_Seagate.cpp \ + Controllers/SeagateController/SeagateController.cpp \ + Controllers/SeagateController/SeagateControllerDetect.cpp \ + Controllers/ENESMBusController/ROGArionDetect.cpp \ + Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_ROGArion.cpp \ + + #-------------------------------------------------------------------------------------------# + # Set up install paths # + # These install paths are used for AppImage and .deb packaging # + #-------------------------------------------------------------------------------------------# + isEmpty(PREFIX) { + PREFIX = /usr + } + + target.path=$$PREFIX/bin/ + desktop.path=$$PREFIX/share/applications/ + desktop.files+=qt/org.openrgb.OpenRGB.desktop + icon.path=$$PREFIX/share/icons/hicolor/128x128/apps/ + icon.files+=qt/org.openrgb.OpenRGB.png + metainfo.path=$$PREFIX/share/metainfo/ + metainfo.files+=qt/org.openrgb.OpenRGB.metainfo.xml + rules.path=$$PREFIX/lib/udev/rules.d/ + rules.files+=60-openrgb.rules + INSTALLS += target desktop icon metainfo rules +} + +unix:!macx:CONFIG(asan) { + message("ASan Mode") + QMAKE_CFLAGS=-fsanitize=address + QMAKE_CXXFLAGS=-fsanitize=address + QMAKE_LFLAGS=-fsanitize=address +} + +#-----------------------------------------------------------------------------------------------# +# MacOS-specific Configuration # +#-----------------------------------------------------------------------------------------------# +QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.15 + +#-----------------------------------------------------------------------------------------------# +# Common MacOS definitions # +#-----------------------------------------------------------------------------------------------# +macx { + CONFIG += link_pkgconfig + CONFIG += sdk_no_version_check + + PKGCONFIG += \ + libusb-1.0 \ + hidapi + + DEFINES += \ + USE_HID_USAGE \ + + QMAKE_CXXFLAGS += \ + -Wno-narrowing \ + + HEADERS += \ + AutoStart/AutoStart-MacOS.h \ + qt/macutils.h \ + SuspendResume/SuspendResume_MacOS.h \ + + HEADERS += $$CONTROLLER_H_MACOS + + SOURCES += \ + dependencies/hueplusplus-1.2.0/src/LinHttpHandler.cpp \ + serial_port/find_usb_serial_port_macos.cpp \ + AutoStart/AutoStart-MacOS.cpp \ + qt/macutils.mm \ + SuspendResume/SuspendResume_MacOS.cpp \ + startup/main_FreeBSD_Linux_MacOS.cpp \ + + SOURCES += $$CONTROLLER_CPP_MACOS + + # Use mbedtls 3 + MBEDTLS_PREFIX = $$system(brew --prefix mbedtls@3) + + INCLUDEPATH += \ + $$MBEDTLS_PREFIX/include \ + + LIBS += \ + -lmbedx509 \ + -lmbedcrypto \ + -lmbedtls \ + -L$$MBEDTLS_PREFIX/lib + + ICON = qt/OpenRGB.icns + + info_plist.input = mac/Info.plist.in + info_plist.output = $$OUT_PWD/Info.plist + QMAKE_SUBSTITUTES += info_plist + QMAKE_INFO_PLIST = $$OUT_PWD/Info.plist +} + +#-----------------------------------------------------------------------------------------------# +# Apple Silicon (arm64) Homebrew installs at /opt/homebrew # +#-----------------------------------------------------------------------------------------------# +macx:contains(QMAKE_HOST.arch, arm64) { + INCLUDEPATH += \ + /opt/homebrew/include \ + + SOURCES += \ + scsiapi/scsiapi_macos.c \ + super_io/super_io.cpp \ + + HEADERS += \ + super_io/super_io.h \ + + LIBS += \ + -L/opt/homebrew/lib \ +} + +#-----------------------------------------------------------------------------------------------# +# Intel (x86_64) Homebrew installs at /usr/local/lib # +#-----------------------------------------------------------------------------------------------# +macx:contains(QMAKE_HOST.arch, x86_64) { + INCLUDEPATH += \ + dependencies/macUSPCIO \ + i2c_smbus/MacOS \ + /usr/local/include \ + /usr/local/homebrew/include \ + + SOURCES += \ + i2c_smbus/MacOS/i2c_smbus_i801.cpp \ + i2c_smbus/MacOS/i2c_smbus_nct6775.cpp \ + i2c_smbus/MacOS/i2c_smbus_piix4.cpp \ + scsiapi/scsiapi_macos.c \ + super_io/super_io.cpp \ + + HEADERS += \ + dependencies/macUSPCIO/macUSPCIOAccess.h \ + i2c_smbus/MacOS/i2c_smbus_i801.h \ + i2c_smbus/MacOS/i2c_smbus_nct6775.h \ + i2c_smbus/MacOS/i2c_smbus_piix4.h \ + super_io/super_io.h \ + + LIBS += \ + -L/usr/local/lib \ + -L/usr/local/homebrew/lib \ + + DEFINES += \ + _MACOSX_X86_X64 \ +} + +DISTFILES += \ + debian/openrgb-udev.postinst \ + debian/openrgb.postinst diff --git a/OpenRGBPluginInterface.h b/OpenRGBPluginInterface.h new file mode 100644 index 0000000..ba35f24 --- /dev/null +++ b/OpenRGBPluginInterface.h @@ -0,0 +1,86 @@ +/*---------------------------------------------------------*\ +| OpenRGBPluginInterface.h | +| | +| OpenRGB SDK network protocol | +| | +| herosilas12 (CoffeeIsLife) 11 Dec 2020 | +| Adam Honse (CalcProgrammer1) 05 Jan 2021 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "ResourceManagerInterface.h" + +#define OpenRGBPluginInterface_IID "com.OpenRGBPluginInterface" + +/*-----------------------------------------------------------------------------------------------------*\ +| OpenRGB Plugin API Versions | +| 0: OpenRGB 0.6 Unversioned, early plugin API. | +| 1: OpenRGB 0.61 First versioned API, introduced with plugin settings changes | +| 2: OpenRGB 0.7 First released versioned API, callback unregister functions in ResourceManager | +| 3: OpenRGB 0.9 Use filesystem::path for paths, Added segments | +| 4: OpenRGB 1.0 Resizable effects-only zones, zone flags | +\*-----------------------------------------------------------------------------------------------------*/ +#define OPENRGB_PLUGIN_API_VERSION 4 + +/*-----------------------------------------------------------------------------------------------------*\ +| Plugin Tab Location Values | +\*-----------------------------------------------------------------------------------------------------*/ +enum +{ + OPENRGB_PLUGIN_LOCATION_TOP = 0, /* Top-level tab (no icon) */ + OPENRGB_PLUGIN_LOCATION_DEVICES = 1, /* Devices tab */ + OPENRGB_PLUGIN_LOCATION_INFORMATION = 2, /* Information tab */ + OPENRGB_PLUGIN_LOCATION_SETTINGS = 3, /* Settings tab */ +}; + +struct OpenRGBPluginInfo +{ + /*-------------------------------------------------------------------------------------------------*\ + | Plugin Details | + \*-------------------------------------------------------------------------------------------------*/ + std::string Name; /* Plugin name string */ + std::string Description; /* Plugin description string */ + std::string Version; /* Plugin version string */ + std::string Commit; /* Plugin commit (git or otherwise) string */ + std::string URL; /* Plugin project URL string */ + QImage Icon; /* Icon image (displayed 64x64) */ + + /*-------------------------------------------------------------------------------------------------*\ + | Plugin Tab Configuration | + \*-------------------------------------------------------------------------------------------------*/ + unsigned int Location; /* Plugin tab location from Plugin Tab Location enum */ + /* This field is mandatory, an invalid value will */ + /* prevent plugin tab from being displayed */ + std::string Label; /* Plugin tab label string */ + std::string TabIconString; /* Plugin tab icon string, leave empty to use custom */ + QImage TabIcon; /* Custom tab icon image (displayed 16x16) */ +}; + +class OpenRGBPluginInterface +{ +public: + virtual ~OpenRGBPluginInterface() {} + + /*-------------------------------------------------------------------------------------------------*\ + | Plugin Information | + \*-------------------------------------------------------------------------------------------------*/ + virtual OpenRGBPluginInfo GetPluginInfo() = 0; + virtual unsigned int GetPluginAPIVersion() = 0; + + /*-------------------------------------------------------------------------------------------------*\ + | Plugin Functionality | + \*-------------------------------------------------------------------------------------------------*/ + virtual void Load(ResourceManagerInterface* resource_manager_ptr) = 0; + virtual QWidget* GetWidget() = 0; + virtual QMenu* GetTrayMenu() = 0; + virtual void Unload() = 0; +}; + +Q_DECLARE_INTERFACE(OpenRGBPluginInterface, OpenRGBPluginInterface_IID) diff --git a/PUBLIC-SOURCE-MANIFEST.sha256 b/PUBLIC-SOURCE-MANIFEST.sha256 new file mode 100644 index 0000000..e8503fc --- /dev/null +++ b/PUBLIC-SOURCE-MANIFEST.sha256 @@ -0,0 +1,2362 @@ +004f81f60cd94f40a4190a251207046c49947a18665acd9d286d4ed37262000c .dockerignore +d60541998d51c1858da15f04afb54b28db56c21b86217c3cb7fc3d7ddb15c406 .editorconfig +b31877c3a1005f270a5814a32e1d3ea3e6ae88eb7857ba578dd0931d6d8517c5 .env.example +c72d7473cb0746b3eb542ec0fbbf6ef3cf1fc9ca764d703e6209a8949a2ab890 .gitea/workflows/managed-validation.yml +37071ae4aaa1cbeacdf6560459e0c2302b620d998500c42012c87c16574018a8 .github/FUNDING.yml +2c1e738d39017495aabd2daf47b0cab54c5442362c51f1c08cd460297a9308c0 .github/workflows/issue_opened.yml +47ff2411d0e07423534fbf69fee1c5b1a1a26ed98aa7adc9ae0c9f5427bcba17 .github/workflows/openrgb_upstream_compat_guard.yml +a3deb9677d421609dff0fa3b2724004dfb6c714f1c11ba53d5d55e65b327e179 .github/workflows/pr_opened.yml +3cef8a1349a801071645ffc59f0bfc9c95ad57b72f483338f581e396ba355c71 .gitignore +07856c77e64cbd3e1d73cf376121405a220dcb68f2a03333d5898f8fa6e25ee4 .gitlab-ci.yml +504246559ed2105e3e058c36737a8a9fa8c08023ab2c88cb52bcda2f7430547e .gitlab/CODEOWNERS +24792f68f1792a9b4125f5be218361103dd9e29f618c274cce595125b0b639eb .gitlab/issue_templates/Bug Report.md +ba1dba1730422cfc2a0a7443a4e42ec1f06fcc1fad2d221aeb6ac15cc5bff48b .gitlab/issue_templates/Feature Request.md +b8634a356eb9753bb3a7b82aff7488fc5646db745dda2b201d75a439efbbde2d .gitlab/issue_templates/New Device.md +5c0635991178a736eec0249bdc55151c0c0f3bd58e1edae3ca9d517e5d373597 .gitlab/merge_request_templates/New Device.md +36a40621f3238ea7ce4fd7ade4d6e0735a8c4eb597583765b8721524215444f4 .gitleaks.toml +b9ed98e05e8ea1d12bf32b9077c36673da1404c9ee2ece48870b2e15f439a037 AutoStart/AutoStart-FreeBSD.cpp +b43179febf58c33200160a7fe94a9be471857e20937775fa0990374df54745b9 AutoStart/AutoStart-FreeBSD.h +0e2cde5cff46d376ba7c1ddbe3f7612e9416a1bd154a3eb5e2b39cbd7591cda2 AutoStart/AutoStart-Linux.cpp +72fd5051f087cded4bdd6ab59bf0b7477597c5bf5afb3927e37676ffb87a51e8 AutoStart/AutoStart-Linux.h +5df32a94abefd0486300296c68ae7490a9a556bacb8031bcc192e8d221c770b3 AutoStart/AutoStart-MacOS.cpp +38d8addb171fb27394128be3d56197c400b8a488c2010d82a3fefb6f641cd2d1 AutoStart/AutoStart-MacOS.h +bb0f92c296e143824c013ae08b30850f2ad857d9384be0b0ef4f48c51ab04e5f AutoStart/AutoStart-Windows.cpp +05ac3372f76173f005f85aa523364fa371b1accb3af74ae320961591bc3e9acf AutoStart/AutoStart-Windows.h +6e597fc1673e6005a5b8dcdda73cac52f3a10710c1ab76331b376d76c3c4aec9 AutoStart/AutoStart.cpp +b75ae194dffae84436e73433994c60daa59d68c6f58c7eccb7b6ff6535fff422 AutoStart/AutoStart.h +6c81cce5ddd38b61b88085253682c18fd615a826d34c49e0086e1cc51124af34 CONTRIBUTING.md +9863c04e6076c69ba45f851f6bbf3f4cfccef5ad516fec60c3ad01a15b5edf4b Colors.h +a0e2314af1f9a89d322c84c54dae704d1ab69005d2cfc6de308a2caf59adf1cb Controllers/A4TechController/A4Tech_Detector.cpp +1f124e0ea213041c5e94a836c865d20dfedc83b8df55daf8c15a34a98cc01b1b Controllers/A4TechController/BloodyB820RController/BloodyB820RController.cpp +323fa91e74ec33b214d814020d372123903334c2706e6028beaf22faf8bbaa4b Controllers/A4TechController/BloodyB820RController/BloodyB820RController.h +f86f1d2f93f7aaf20b450611476ab14a46c15d1ad404132c954f47e553fa21d0 Controllers/A4TechController/BloodyB820RController/RGBController_BloodyB820R.cpp +89a6225c93940c245552921f00aa0892ea04de2a45c82f12b9f9387d7c47bee2 Controllers/A4TechController/BloodyB820RController/RGBController_BloodyB820R.h +7e664f79429b30a712a165e9fec36bdab119260c83ab706e8fd7f231a6bdf5ec Controllers/A4TechController/BloodyMouseController/BloodyMouseController.cpp +1d06daf647925aa09d718a00ecc3dedccf12fece762bcdeba20afc4bd7489f56 Controllers/A4TechController/BloodyMouseController/BloodyMouseController.h +c34fd06a558cb3d20071f8d43cff302958a09f6c904139b482940c2b0aba0598 Controllers/A4TechController/BloodyMouseController/RGBController_BloodyMouse.cpp +4fb82c294a8ce13173a4edffa3efc400778c7cc59cd4f45133872f088a70465b Controllers/A4TechController/BloodyMouseController/RGBController_BloodyMouse.h +cf5c55ba26e56cbd193706e987ddd35f083ed4e63fab953bfe11e405c713f7cd Controllers/AMBXController/AMBXController.cpp +76871495873a3c169fcab67ba44939ceb09de81802a0f0c30c9a0b415fb19c36 Controllers/AMBXController/AMBXController.h +44a4860650d716019eed88f400db9907316fb25a1921f0a6d219e433029201da Controllers/AMBXController/AMBXControllerDetect.cpp +d3f0c99e660a512465d009d76f660ceb6fa29af7dbc699de6f0c87d457af5934 Controllers/AMBXController/RGBController_AMBX.cpp +730c310b7a9e8632fabbae24a6f7c86dcc212651eaa0e1fd3d3d141430d7dc3c Controllers/AMBXController/RGBController_AMBX.h +8d8cce015eace618aa36ed5c982cb47819a7ab02190e21da9ec891bdadcdefae Controllers/AMDWraithPrismController/AMDWraithPrismController.cpp +723573c94a07455658e7d798ed2dc5843d284571025ffad4fc4e22f1977cdbbe Controllers/AMDWraithPrismController/AMDWraithPrismController.h +439c50cec58c7b1c2dfb90cc9bf3c295abac70b28944c928b57d11fc98080c50 Controllers/AMDWraithPrismController/AMDWraithPrismControllerDetect.cpp +904efa4dd3c30049b13cabe78792d2f4e87ccb279c1a85ebf5703d8d2fc20c6f Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.cpp +6789a6f8cab7b2f74d6791f46463c039e46a1353b398dfe7c64295b527b971d9 Controllers/AMDWraithPrismController/RGBController_AMDWraithPrism.h +cd43fa37ff2bfa4131f25ecc21e94de9f5d11261d78d3e2027015e47352d2d06 Controllers/AOCKeyboardController/AOCKeyboardController.cpp +f4580439f12951c269666d2859fcd00a755d11f397c95a406f06381aaa46f6bd Controllers/AOCKeyboardController/AOCKeyboardController.h +e47a4094612c0b265d5aa71f641c57f1466268674e2f9dd3fa1e6ba8c3ed0eee Controllers/AOCKeyboardController/AOCKeyboardControllerDetect.cpp +da7b39516352dbedb160de61413a502aa76c69d70e532a3c7cd10cd862a1b26e Controllers/AOCKeyboardController/RGBController_AOCKeyboard.cpp +c861da3db57160625ccb8e4f8deec32d8f86763e723333f908b31aaeb3d002d1 Controllers/AOCKeyboardController/RGBController_AOCKeyboard.h +db4b84803dcdbe994b8743ce7ede92547802f0899f85b3466b6f0613f94d979a Controllers/AOCMouseController/AOCMouseController.cpp +978ce7804c3d8c0337a0c89bf48ab2215d457d80fa3ae526c17211bc347b4455 Controllers/AOCMouseController/AOCMouseController.h +ad41f2a6e10eca4a36f64900f7c776c1c081020ca4337afc3a0013cb203509c7 Controllers/AOCMouseController/AOCMouseControllerDetect.cpp +39af1943a8f4860526b3a8c2399fcf38ba065b859ac8a1dd3539788190b6922a Controllers/AOCMouseController/RGBController_AOCMouse.cpp +0d19b4bc1c0dc47008ebd4c85459535f8b21e014112a8ec9896c7d8fbeec4864 Controllers/AOCMouseController/RGBController_AOCMouse.h +45e9a6e62708bc6262ee5894495f005ff7e026bd4c95e0544e399ed6242c6b53 Controllers/AOCMousematController/AOCMousematController.cpp +d5e1eefa508e273b193d2d76a55c8a74f480ad8e2f2465dd8700fbd75c0cd7e6 Controllers/AOCMousematController/AOCMousematController.h +ac8540e850e90d82461d9a4bd8471a028659e2ce515824d3b8eb0cfa3621307c Controllers/AOCMousematController/AOCMousematControllerDetect.cpp +c27e4bab837115c4da645f77e84618c7d1d7d453f6591e15a3490f9f05d5fa5a Controllers/AOCMousematController/RGBController_AOCMousemat.cpp +50a130ef795d5e931c2d5dbd8a620267490def670f762d54bc4f1800d6832602 Controllers/AOCMousematController/RGBController_AOCMousemat.h +0f122ce33babd76f7935da0cd840822f33db817169a25f5bb7a2d44f399b7e69 Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBController.cpp +081e47759463454fc851cddeead6d1c3c9ef31a28887d6c00e164d6fc46f9063 Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBController.h +10e2bc7b51feaeb2c0a48c7d647ceaf6fbc4f4244d65920eae165fe6b40da7ed Controllers/ASRockPolychromeUSBController/ASRockPolychromeUSBControllerDetect.cpp +ef0b393a324e089e7cc335e197caad02ce5f84d4cae6c97ca5774ff4b7d99788 Controllers/ASRockPolychromeUSBController/RGBController_ASRockPolychromeUSB.cpp +a93fd7d437d744ced49eefc12ea26f0f0e7d7a5492257d5d3d3eece8300ffd65 Controllers/ASRockPolychromeUSBController/RGBController_ASRockPolychromeUSB.h +462a91f23dd4e2d3cccda9e948129e295e48123b876b75865d73a3fdfb3749f1 Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/ASRockASRRGBSMBusController.cpp +17cfee61ef192bb5357b2c4b0a12664fa7589d557936ba8c0f0f69baa8eb6134 Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/ASRockASRRGBSMBusController.h +2abe1411b602fcaf91504c86193a05cf4cde1c908b18ab98cc212d2c2a2f6d36 Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/RGBController_ASRockASRRGBSMBus.cpp +84488b6637f91175a2783641e46017910c1f34da39e9c641bb610d244fced2f5 Controllers/ASRockSMBusController/ASRockASRRGBSMBusController/RGBController_ASRockASRRGBSMBus.h +75c31ff7aec8746f1f3308d909507e72383db87b8da47627b37e00a4392559fa Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/ASRockPolychromeV1SMBusController.cpp +be215c151f8054a814481ee0f3f13dcb094070352f814b179fe0cc2a9a4cf7e6 Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/ASRockPolychromeV1SMBusController.h +41aee450be38189e89c867b797fe135fe2a9870ab3ab869ffd05f52c1b6573aa Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/RGBController_ASRockPolychromeV1SMBus.cpp +9862aababb8df8ee055936d7ac5cfc13954ef56f6b4565cb413d3b0be3f08be7 Controllers/ASRockSMBusController/ASRockPolychromeV1SMBusController/RGBController_ASRockPolychromeV1SMBus.h +c206a631975c9d9be815081e3869e1b3c184ff1db40188a33d1d41f7e360f3d6 Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/ASRockPolychromeV2SMBusController.cpp +849b2f7850f71caac284de159d40e9d5d38b4820d9d596bdf15349c53d78a64a Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/ASRockPolychromeV2SMBusController.h +369e3cdb2f5673552c9f0243b8e4797cac553873e3e59596af389a75e9b41209 Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/RGBController_ASRockPolychromeV2SMBus.cpp +ba99ee0e4b2ced493eca867ca536a31d29ad9884ee8dc63e497c595ba0b2af90 Controllers/ASRockSMBusController/ASRockPolychromeV2SMBusController/RGBController_ASRockPolychromeV2SMBus.h +e0bd2abafdd4ffa6e846920b60189a539aecf4221178f60a82e67bfeaf401f73 Controllers/ASRockSMBusController/ASRockSMBusControllerDetect.cpp +ba197e80312d1414195c65876798ba39a9640e479ab09f487b8170a2174f5549 Controllers/AlienwareController/AlienwareController.cpp +e087a4d091b60558046e738cd3cd9c7f8a48530df1785e0594a153929e6aa8b9 Controllers/AlienwareController/AlienwareController.h +dbc09058a2ef2b317f80099d075c95b5f4f14877e3d22a2d36d832e17af5ef97 Controllers/AlienwareController/AlienwareControllerDetect.cpp +847816055c9813aaaf48de5d482e7f7192089800ab01eea2ebe4b07e2319c842 Controllers/AlienwareController/RGBController_Alienware.cpp +ec428b721f529f5322694a7d01a06509a43f05d9e80af5640b80c0acc8b097c8 Controllers/AlienwareController/RGBController_Alienware.h +f2ea9ec201a552475ddd3dbafacfa7ce6e242fcd2f01366ed7cabb8ca44593fe Controllers/AlienwareKeyboardController/AlienwareAW410KController/AlienwareAW410KController.cpp +948cd4c85cd927e5d7fcd14d9f8f5507b5a858613b625f2b7b95e3f9d76c17cd Controllers/AlienwareKeyboardController/AlienwareAW410KController/AlienwareAW410KController.h +ae18b65dda23e21557bc0b0ad8d12e2c3c32418ffb11555d552571a7ab54271d Controllers/AlienwareKeyboardController/AlienwareAW410KController/RGBController_AlienwareAW410K.cpp +9de93b13a8116ae6d5a04b9a4dd0f08e32186dfcc4573cee8eb24395e435b98d Controllers/AlienwareKeyboardController/AlienwareAW410KController/RGBController_AlienwareAW410K.h +905870255dde8f09ec404143433821ee60dc99a5fe0365c58fd38fee369442ce Controllers/AlienwareKeyboardController/AlienwareAW510KController/AlienwareAW510KController.cpp +f460f0b7c85719adc51be4b4f4e3cc4271106a6db813d6cd75530128083272bb Controllers/AlienwareKeyboardController/AlienwareAW510KController/AlienwareAW510KController.h +e6edca2a2c46a28ee575425a0df87bcffafc226b901be0ccabeb2a093dce8536 Controllers/AlienwareKeyboardController/AlienwareAW510KController/RGBController_AlienwareAW510K.cpp +dd6d8a79c9b55befcb641e78dee1dc0ac55032511936252abf0ee19706bba276 Controllers/AlienwareKeyboardController/AlienwareAW510KController/RGBController_AlienwareAW510K.h +8d2b259f62c720ba68e8ac057cdb181ceabe1299344d0710e14cbbfdbde3d164 Controllers/AlienwareKeyboardController/AlienwareKeyboardControllerDetect.cpp +96ed3ecae8e4713267f61461894ee18d8f752e72a6a64ae94283033d1ddc9574 Controllers/AlienwareMonitorController/AlienwareAW3423DWFController.cpp +accc36a2671cb7853643e9f57867047ed433564472b277c6c28cb5d0b4bbc0dc Controllers/AlienwareMonitorController/AlienwareAW3423DWFController.h +88e675e1c1928ccd7a6ebc6e42f6197c46599028b25a6fd95f671d2ab32ff7e7 Controllers/AlienwareMonitorController/AlienwareMonitorController.cpp +7cdfa489198f4578a0c3f93d26c62ec242e5dcf4078a52674df45c6ac8fcfbce Controllers/AlienwareMonitorController/AlienwareMonitorController.h +6798de6e38a12a52075b8f9b6414c84c7bceedbe283b2f489d65bc35d26334a6 Controllers/AlienwareMonitorController/AlienwareMonitorControllerDetect.cpp +9f15a4e4614113cb7b17edc91266a6c55c6da3ef61a82bf9d26bce78513ffb41 Controllers/AlienwareMonitorController/RGBController_AlienwareAW3423DWF.cpp +64df8140261ee3375221eae36134dbc6608bdd3a4315909f04ad4457e03cfee2 Controllers/AlienwareMonitorController/RGBController_AlienwareAW3423DWF.h +51a3ebc7d66b87ce9d06033f6ce4b95765295fd9562b3ceb97a79f143fe8d8dd Controllers/AlienwareMonitorController/RGBController_AlienwareMonitor.cpp +d412c7b710a462f3077e50008cf7fbb0cfc6c3a5a374e5697ffe3ce3e5359d6a Controllers/AlienwareMonitorController/RGBController_AlienwareMonitor.h +4f86b584decc64af1744df021ec753b9b45aece3501e89162f2a94e12ff5e579 Controllers/AnnePro2Controller/AnnePro2Controller.cpp +f6f07423289b37d1db85d6efe6064202924612922c6c18a5469c989a755127f4 Controllers/AnnePro2Controller/AnnePro2Controller.h +91729c77d609082f4e94ef4ef037ae0cc7ab2239acd4489e40f6fc1121a3420c Controllers/AnnePro2Controller/AnnePro2ControllerDetect.cpp +957b979f3fbd4b1ef07ac453eadc03d7ed38a505b0fc4a741b57869d714e45e2 Controllers/AnnePro2Controller/RGBController_AnnePro2.cpp +073e2465373e3dc39aa8fdce65b9aaf91bd42082fa637723d030befccbf37f7b Controllers/AnnePro2Controller/RGBController_AnnePro2.h +98e43eb12bb546a3ed72e95b044e7ee8db21b1a5b07271edce6e383a59fddf71 Controllers/ArcticController/ArcticController.cpp +8bcc932b5bf4254ed4976d0343900f48d47fae2d1e5a5bfb6a772771a12da3a6 Controllers/ArcticController/ArcticController.h +335492ccfeec7b7609acc0f1bb68cd0746c590ec2fbb67af6856b2464538a9f8 Controllers/ArcticController/ArcticControllerDetect.cpp +3d45234b6e7a82eb00b150330a09718a42b15800a8df3a043cb0fb799639a5d0 Controllers/ArcticController/RGBController_Arctic.cpp +8a9d942194e4b060c7d8d16413cd249b97a22e6de2deb57621ce5e145b968cfb Controllers/ArcticController/RGBController_Arctic.h +727ed8f7e23729e4ce745334731d680de69c7ac08e87cd8c228d7ee171f4bad3 Controllers/AresonController/AresonController.cpp +b912e557afbb9c0a883a5d11170d7fa94644bca03a12ed14ccab58895dc462a6 Controllers/AresonController/AresonController.h +85deb6199a23d91af7e16afce1b0d7eca4d4033773b4850931e405a175554733 Controllers/AresonController/AresonControllerDetect.cpp +4d8f059d578e6ac6d34d9ba1beaab10775c4a8e7bc0041621968e54f4e08fc47 Controllers/AresonController/RGBController_Areson.cpp +98fa8a4eca7b3605234a9852b55485fa59b24a8de95f035703d1f883591d93e9 Controllers/AresonController/RGBController_Areson.h +445ae561f1ecead11f579cb1f241f34ebb911dc397027da0309aedcedc8ddf26 Controllers/AsusAuraCoreController/AsusAuraCoreController/AsusAuraCoreController.cpp +29a9fc4a687269098b48a3dd3d5ec762ba198833ebb4ef66c5875595bf8de332 Controllers/AsusAuraCoreController/AsusAuraCoreController/AsusAuraCoreController.h +92c16077896447f2a21227cd3e5b5197b7b80fc4bb08eee9e612467b4d07f2db Controllers/AsusAuraCoreController/AsusAuraCoreController/RGBController_AsusAuraCore.cpp +b55342945b378cb29e23614a310dd167d2a14cc61a97b45b4ca9a53396c1cfac Controllers/AsusAuraCoreController/AsusAuraCoreController/RGBController_AsusAuraCore.h +11a325dddccc4b3cdd679d294f7fc5037980d529c528c4baee2ead3a9da7a262 Controllers/AsusAuraCoreController/AsusAuraCoreControllerDetect.cpp +4564a11a9045d31acf05f6805a877af3c9816e80b1e6fdc9ab78ecf72e7cc991 Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopController.cpp +7ae41448cc082162447fde9b754a8114b7ad5148cb3cf9022d04773f3e3eef1a Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopController.h +661c8edfa1210cab5488e1288954e640baccd20fb8b414b64145786fa4ed6baf Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopDevices.cpp +097f1600bbcfa05cbba756bfbc14d8d3667d38f27786a6f5c79ec8aa6f29f884 Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/AsusAuraCoreLaptopDevices.h +c9a4db3d663fcdac24d42eb7513e4059e09553833b1e95307927fb3a111b99ac Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/RGBController_AsusAuraCoreLaptop.cpp +4e90c3a4bac47dd8be22d521ba9fe82c2edaa11dd3e1d741d2dc8966da835420 Controllers/AsusAuraCoreController/AsusAuraCoreLaptopController/RGBController_AsusAuraCoreLaptop.h +5ac4116d08b5cfae0b2506e853c54e58d3300e61a5e9d186823fb8805e758216 Controllers/AsusAuraGPUController/AsusAuraGPUController.cpp +b0458cfc22658fd691578be5d6fd494ea8cb59991c6b618433a63d602b985703 Controllers/AsusAuraGPUController/AsusAuraGPUController.h +861d83d07ed4ef370a8e474ddfe86afd8df60f98d35f9f6e7d4139f5be7e9b45 Controllers/AsusAuraGPUController/AsusAuraGPUControllerDetect.cpp +6109bf1098ca35446f74893eb97ceac3b4ee09f32f81e64413d77581a6c76ce3 Controllers/AsusAuraGPUController/RGBController_AsusAuraGPU.cpp +73cf8edcf60e12b858f5d46749f709513ab8799bb86b511287aaf0ac30f49b2a Controllers/AsusAuraGPUController/RGBController_AsusAuraGPU.h +2d35f4a12ae7ff4280fbcc36783988fc72d0f280f9ec6a1f2d685db7f7e6eb78 Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/AsusAuraHeadsetStandController.cpp +e5d2077704c624722757338a89e2ee8abe161c58298cf1c474774b82463bbb62 Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/AsusAuraHeadsetStandController.h +3a4ff6716f2828cb6f41b71cbc051bd83c6cfe8cb1ff9d14058d4612f694d707 Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/RGBController_AsusAuraHeadsetStand.cpp +837e9184ab3b04bb81c505be85e2b120b38c0a1575edb8fc6197b33f9aec4524 Controllers/AsusAuraUSBController/AsusAuraHeadsetStandController/RGBController_AsusAuraHeadsetStand.h +de7e47962297875dad9d483fc86e8992d0beee6537eea7f23966a045ef426275 Controllers/AsusAuraUSBController/AsusAuraKeyboardController/AsusAuraKeyboardController.cpp +635d310be3bc1563d4bc1ea7d4a731ead6a0838794cf12328f55d91ed8c2a0f1 Controllers/AsusAuraUSBController/AsusAuraKeyboardController/AsusAuraKeyboardController.h +f6630659cd26990e08288514fbe9a88b33b8e2db70cb0b31acae37b5aca9c414 Controllers/AsusAuraUSBController/AsusAuraKeyboardController/RGBController_AsusAuraKeyboard.cpp +f427f3bbf86fded5aa1e45bd9842d24111f04c205256e67bbce56ac3ca37158a Controllers/AsusAuraUSBController/AsusAuraKeyboardController/RGBController_AsusAuraKeyboard.h +62f7511ce6956e13f916259d8caa76655fdffb90a89fb91f90423fb9a0ae1e61 Controllers/AsusAuraUSBController/AsusAuraMonitorController/AsusAuraMonitorController.cpp +cff6c3182f1ac0b99059e4bfb3c564f19b60435f2783fa428bd0e0c94e4ffc0b Controllers/AsusAuraUSBController/AsusAuraMonitorController/AsusAuraMonitorController.h +8f619c04e8d1b4f67cdcca84315f673988cd28721f24415e7e68106913d591ed Controllers/AsusAuraUSBController/AsusAuraMonitorController/RGBController_AsusAuraMonitor.cpp +1ae1f5df0de0d0f46969c1542264419041e73d548ec97c69d777f190356c2d53 Controllers/AsusAuraUSBController/AsusAuraMonitorController/RGBController_AsusAuraMonitor.h +53d7f1b59b8f2fd8439a0318b8deb3bdfad4199c43be479d4390aab899e03848 Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseController.cpp +036f2505c0312109f311fe6a3c63e364c99561b55a6fbd24318e73aed0a7aa3d Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseController.h +3c600c7aee155f573ad5bc02603536788197352da5db0365553ac1b1a1512654 Controllers/AsusAuraUSBController/AsusAuraMouseController/AsusAuraMouseDevices.h +bfee496b3685e858a2c294da5fa6e4fd578b9d266cb8658994d8b8e5a75da26b Controllers/AsusAuraUSBController/AsusAuraMouseController/RGBController_AsusAuraMouse.cpp +52a6fb0b2af8e2751728060e681ba8c067258823c01091851130b7b3c83c1e46 Controllers/AsusAuraUSBController/AsusAuraMouseController/RGBController_AsusAuraMouse.h +d5b4102d0039179ade0ad9d650f6940ed03a04660d56185a5ebc341e2feb8a73 Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/AsusAuraMouseGen1Controller.cpp +102c782e8474fc63135ab8f7ffcc82187f5ca9051c6d1cf0f91410c1aed1826b Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/AsusAuraMouseGen1Controller.h +866ebdc3b5d6e4255a5eab3ba23f16ae15e4608de5e56ff8dd914b9468d1e1da Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGSpatha.cpp +86d39777170fb77935cddccb2810f49603095f1f5445a55fad61422dafd820a6 Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGSpatha.h +7fd8fe6cafcd3d83c8f3a3d0b1d84403c9ade7864cf434790c628685d6c0fe3b Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGStrixEvolve.cpp +c0505bf88732f9e32848a6b3089fce2c7978ecc8042d2222f41034aef3039bd9 Controllers/AsusAuraUSBController/AsusAuraMouseGen1Controller/RGBController_AsusROGStrixEvolve.h +6ae0a1374dbb55d1d3a60094944b2f7ea2c2a6c2af3ff691bab0e8afa7f141e2 Controllers/AsusAuraUSBController/AsusAuraMousematController/AsusAuraMousematController.cpp +c1e374d8ff2dfc728ec9908b1099a09ed1b1f061a089ab910aec48290f7c684e Controllers/AsusAuraUSBController/AsusAuraMousematController/AsusAuraMousematController.h +aba46966007c86ec3fddeacff5b1004da770659b10d8bebc1c43f4d8adbec849 Controllers/AsusAuraUSBController/AsusAuraMousematController/RGBController_AsusAuraMousemat.cpp +2ddb8baf027b34ebac571739acdea30bc7f9c2db1ee478ddf4dde1dbd8abed81 Controllers/AsusAuraUSBController/AsusAuraMousematController/RGBController_AsusAuraMousemat.h +d436df0b9bd480cadc3db23f04034b61db6164cee74b4dbd89c2985b59dd432c Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/AsusAuraRyuoAIOController.cpp +18e0f37f08b7642655a92a16fe1d70559862cb77232ce2cff540d4327deab916 Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/AsusAuraRyuoAIOController.h +b948251029107dfb837ae5023d6df0301206546ed99bd356d6e66d4610cb9251 Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/RGBController_AsusAuraRyuoAIO.cpp +57739fd750565bc1e7f96c8b167d47747f8d89c2e1edd69d77e64e8aad8b887b Controllers/AsusAuraUSBController/AsusAuraRyuoAIOController/RGBController_AsusAuraRyuoAIO.h +ad90c09f1ecfd3d2919ccaf5532ea90f427617fc444e28ce7a1db31eeeea1a75 Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.cpp +f830e0ac3957811fab60a79155019de048ba9a55a4b1c45577a409e6b04d41ec Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardController.h +c9da69bbfb91408501182fd781e4c09800f206a73d5568c23ee0b9749dce07b4 Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/AsusAuraTUFKeyboardLayouts.h +65f34d492cd28ce13c1eb251be18e0b4d04bbccde7e1ca65f1aba8d658ebf40e Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.cpp +3992210120cd497c78a5ea5a1b19bb29c0a93e9779aabe031f43cac03163c7b2 Controllers/AsusAuraUSBController/AsusAuraTUFKeyboardController/RGBController_AsusAuraTUFKeyboard.h +fdfaaba6645dc62ff7f8e917388b8391ee4beda8836e64bfe9af0f65c1cbc285 Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraAddressableController.cpp +13b026b253c1cd0931ef3af6fef26a6cf0c8e1feebc2a08585d22c9b025a43d8 Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraAddressableController.h +61f47da5ba7416d99ebd5b19c7c6e00f587d6bcec3d23d3ccb34ce87eac48d13 Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraMainboardController.cpp +02c5212c288c35dbfad763b7dcbbb1794ee642b62c829a2500811bad44d16fe7 Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraMainboardController.h +28dcb35c6c59decc1c8c6e031b67898179be720c92d39c57c8b0ee1a58c14d38 Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraUSBController.cpp +20ee7d6930ecced7cb771bf4ee0b595792822d66e0c290ff96b7fd0bdd031fb0 Controllers/AsusAuraUSBController/AsusAuraUSBController/AsusAuraUSBController.h +2f3dc5f7bb35196f6ea45994faed10444546017b756c2946f535e193fdd1905d Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraMainboard.cpp +6453059b7f3267d433e5938cf51743b9028557fb12e64ab85d84581bdf6e8f08 Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraMainboard.h +858babe5845f53ee50bc91f4c7a6fcb06834cfb83f52341fb1950812698abc2f Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraUSB.cpp +bfbcfc595e7d8d252ca2eb38d85029f98c7114a6eb76d6b47c2840a740061141 Controllers/AsusAuraUSBController/AsusAuraUSBController/RGBController_AsusAuraUSB.h +15ebf3e93195a7ed7535e74af1f18e1b3eae7b63070de2493ee230d9adeb6138 Controllers/AsusAuraUSBController/AsusAuraUSBControllerDetect.cpp +25fe536678e114664520cec5d487231853e154c136312022e29a33bfe847de30 Controllers/AsusAuraUSBController/AsusROGAllyController/AsusROGAllyController.cpp +bfd65a6a132a30627d744dffa7121b971e4157963eb25e957ebf50775c965f6a Controllers/AsusAuraUSBController/AsusROGAllyController/AsusROGAllyController.h +4bf4043e52a7f7ba5268f2a54414a66a03ec14ee6ae0d76052a5fb20dd312113 Controllers/AsusAuraUSBController/AsusROGAllyController/RGBController_AsusROGAlly.cpp +2ed4d73eea77dca6e1df72532ce2c6c5535e3fd05966f6770acf3dae15f1570f Controllers/AsusAuraUSBController/AsusROGAllyController/RGBController_AsusROGAlly.h +f26d21a1579ede56b2143bb16f500b4485d21fb872709d36f873baf909d0deb6 Controllers/AsusAuraUSBController/AsusROGStrixLCController/AsusROGStrixLCController.cpp +04de852ee6f461239f74eb7f048facc103364f7f710889d7c9a5e84938313307 Controllers/AsusAuraUSBController/AsusROGStrixLCController/AsusROGStrixLCController.h +58c4b851d42615d47754946ffca2358d554a1f6050166973c2260d2b813fb454 Controllers/AsusAuraUSBController/AsusROGStrixLCController/RGBController_AsusROGStrixLC.cpp +83397bea22a473a0cf274ce8c5560c35cd6f10a895d0e019a07add675e8c16a5 Controllers/AsusAuraUSBController/AsusROGStrixLCController/RGBController_AsusROGStrixLC.h +53b1acc60e4dbe5f635c4ce56216358b731ad17e2263479ff04de75e55d5c77c Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/AsusCerberusKeyboardController.cpp +555f80d11266dde091abb98d8bf196dc1e20e8eeb8c54aeb130c11f3142c53cd Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/AsusCerberusKeyboardController.h +68ad2e218d2b1164afbececd52c413dec9b2e5b7451d95d34d620bc552135a4a Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/RGBController_AsusCerberusKeyboard.cpp +9c94eff39f363239f4d469f6f9cb98a31fd86a28991b54a83daf303336e392b9 Controllers/AsusLegacyUSBController/AsusCerberusKeyboardController/RGBController_AsusCerberusKeyboard.h +52ff35c26e46161a534005e97c0bb161126e82e19ac99da1f77bd83b438e1984 Controllers/AsusLegacyUSBController/AsusLegacyUSBControllerDetect.cpp +40cf7174daef014612a0c2826409e3a7487200bfa2e099c1947f6fadbbaa9abe Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/AsusSagarisKeyboardController.cpp +3bc68f92c133f208788330afdfb7b3919374b022789f5f2937b9908b0fc8b792 Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/AsusSagarisKeyboardController.h +418ba1033d6c14b6aa0ff97126bb8067128b11b3a4298f998871df20b6a4ff20 Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/RGBController_AsusSagarisKeyboard.cpp +7e51d0e09acaf0c93fa82841b652d9d742406053218bfe064eb28a8f880b51ae Controllers/AsusLegacyUSBController/AsusSagarisKeyboardController/RGBController_AsusSagarisKeyboard.h +631879a25d915ee649300d35c5b4ad0925e075cc20c3543fe5a8009fecbd9e2b Controllers/AsusLegacyUSBController/AsusStrixClawController/AsusStrixClawController.cpp +3d56d84119ed52aba5cdcbf9caa835a7a2265ad6e2fd48ff0da4c748435a2dd9 Controllers/AsusLegacyUSBController/AsusStrixClawController/AsusStrixClawController.h +db272d2afb06fe02b16c42d948994c391fa192043ebd0927a0adebc474453625 Controllers/AsusLegacyUSBController/AsusStrixClawController/RGBController_AsusStrixClaw.cpp +ed9cc3e655c227bec6cef192637897cd1c9aa688573b14a9716fdaaa9846b28d Controllers/AsusLegacyUSBController/AsusStrixClawController/RGBController_AsusStrixClaw.h +22f7812e938df4beccac9d4bfa4c09f84dca33806a8b3dc171758912959aed6d Controllers/AsusMonitorController/AsusMonitorController.cpp +164ea879fd9addd59c6d958d33352a23fd3cb89b2e30c662fccf7a3c0f438b13 Controllers/AsusMonitorController/AsusMonitorController.h +39a0e2942d9e5bdcdb329dde66c8ffd6aed644c3a4d8aec2363a9a2f8d25d9c8 Controllers/AsusMonitorController/AsusMonitorControllerDetect.cpp +36ddcdb188aad7a1e48693ed974fcbe424e8167fa163375b02f5d02294a3dab9 Controllers/AsusMonitorController/RGBController_AsusMonitor.cpp +e984fb56c7afaf6d08ff2a71ab4acdd642b39a89d2036c661ad70f4e45b1e036 Controllers/AsusMonitorController/RGBController_AsusMonitor.h +49506dc308d52401eecd528762aa05a4254b1128a5824e807108a29f3dfbe471 Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Linux.cpp +3ea8f0090c999deb48195783167f34d62e24ff383739461f10ae89b1e9ca1b7c Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Linux.h +203d963bba5627d4aafdb36e657f4e52ca567860c8672c3e98d62d36267520bd Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Windows.cpp +64fe64710cfd6a5760228cbcbc0cf80a14dfe373ab8382cd721890507305c821 Controllers/AsusTUFLaptopController/AsusTUFLaptopController_Windows.h +03068fd85b924d3347326455b130f67ea391caf6789b2fc77850b27aed1ba5c6 Controllers/AsusTUFLaptopController/AsusTUFLaptopDetect_Linux.cpp +6fcdfe191b701f10d4edb49535647c29ba6c0499d160e0013c8fecc43f4c4cac Controllers/AsusTUFLaptopController/AsusTUFLaptopDetect_Windows.cpp +02cd0a1db8f71b50c6e9f3c239f1d8c2c53785439c9d5cbd1e05c6b92119467b Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Linux.cpp +e91b3c7d74cde1859832e870dc2aeef2996b7a8e6e910a7592738dafcb7443d2 Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Linux.h +12db3cead0e0194dd28531a8d68e649524fa2da68d01f7e723ef003b64ca09b9 Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Windows.cpp +3d32db1ebc466c402177154fdbc42449ab97527f8e98a9df4089b2ef7f15f6d1 Controllers/AsusTUFLaptopController/RGBController_AsusTUFLaptop_Windows.h +2ec8170c6c59ed194009e6e765c9bc9d30e2b1d251f9104ef9721a353b31ca39 Controllers/BlinkyTapeController/BlinkyTapeController.cpp +41ff7f117ca3d81bbd96cd6969e5810efdb368529b1dd3917ebd2fefbd04dc68 Controllers/BlinkyTapeController/BlinkyTapeController.h +be8bcd018362f263efeb9846b8181ccaf542b70c0a901e6a6264151ab6f8dafc Controllers/BlinkyTapeController/BlinkyTapeControllerDetect.cpp +7af297cf69d73b585cf5fd11c3001aa6b991e0ad9ecb24681ec5b7a2f8d6a0f5 Controllers/BlinkyTapeController/RGBController_BlinkyTape.cpp +34c93718fffdfc0896c70e9912397bb6ee7a83757908ffca83eb271934832504 Controllers/BlinkyTapeController/RGBController_BlinkyTape.h +2f220b4867fe2053aaff2a3d14be21110def8740f91882c8f6b897f8c667083a Controllers/CherryKeyboardController/CherryKeyboardController.cpp +f9d740fc45b6041a4e74c8d236801a56f1bc7da0794eda1d15e50788536b1fbe Controllers/CherryKeyboardController/CherryKeyboardController.h +661661768598331ac34c89d0dd12a3b338d8039f1f24a728fdb45c216200ebcc Controllers/CherryKeyboardController/CherryKeyboardControllerDetect.cpp +e626771b31a65f88b133e0c13bbf463056665d2b34fbf2c9b1944c83163f2096 Controllers/CherryKeyboardController/RGBController_CherryKeyboard.cpp +ea08b49d4041c0942a1a877e2a62156d58375dba75a4ad5fd1bdd957487f5c4b Controllers/CherryKeyboardController/RGBController_CherryKeyboard.h +ca351b3ee9f35a035f96b1bfda972d7f16cac7e0a04c9159fac9dfd47028c45c Controllers/ClevoKeyboardController/ClevoKeyboardController.cpp +661f095d9ed8065fd8e83f3bee153d1aa5b8ef659ade5fa5d9585b051e772364 Controllers/ClevoKeyboardController/ClevoKeyboardController.h +53da4fb455871337533027894481c08e7e060a20816c0d023e0faeecbbdbd49f Controllers/ClevoKeyboardController/ClevoKeyboardControllerDetect.cpp +d66128503adac90023d6d02ae86c6e16d6f1cfd324e1ee6e5e83f7241db91dd9 Controllers/ClevoKeyboardController/ClevoKeyboardDevices.cpp +d5dc9f62d807f9be70bcacf05fc6454ad8d9cd2772264274310177463d601606 Controllers/ClevoKeyboardController/ClevoKeyboardDevices.h +9ff6e5de2138707496a99f7b243845728af15170984044a452455b88a97affda Controllers/ClevoKeyboardController/RGBController_ClevoKeyboard.cpp +a019e66f4506c9e8f48a0bd915a9a7c4f1e6eaf1b2e611aebbab48c10a98fe06 Controllers/ClevoKeyboardController/RGBController_ClevoKeyboard.h +3f23d9bd6bb81d4fa7dd579274b7a04fdb9bb55398b42a4727027148289103f2 Controllers/ClevoLightbarController/ClevoLightbarController.cpp +3ee486152fe7b15b02b938fcb21d5ae42869193ca61bb4045fb72f214388a061 Controllers/ClevoLightbarController/ClevoLightbarController.h +d37fb2359d100aa3adf36075c0f2419ad6f7662751af890b1dfe4e3126e5debc Controllers/ClevoLightbarController/ClevoLightbarControllerDetect.cpp +98b7ca3b9ab83267234cd5bcaaa4f2c679b864d38d77bba8af3bb92e38fd01e7 Controllers/ClevoLightbarController/RGBController_ClevoLightbar.cpp +6ec5103cb95b2b36bf1f80cbbfa46bb14b4eeb042fe61b568e7baba8baf31099 Controllers/ClevoLightbarController/RGBController_ClevoLightbar.h +3866415db907a836db372a92a160bf4853cd6d932fbb006beafb0d2714c0148b Controllers/ColorfulGPUController/ColorfulGPUController.cpp +db675c4ad04e6ce4d6de9a00862db450794e80045101161401f9d7cbb41bb09a Controllers/ColorfulGPUController/ColorfulGPUController.h +963ae28b9fc016b2c1f46e2bb9ceab4bf7a104a55dae6b1c3bc5a323b6af3c78 Controllers/ColorfulGPUController/ColorfulGPUControllerDetect.cpp +fa7874539571ffff9910386849c6d22d5064f714f4ecdb35b9eb3cd3b1873691 Controllers/ColorfulGPUController/RGBController_ColorfulGPU.cpp +97941a5755a5bba5ad4efd8040af088bdc175e8ee2804613b0587f8037f337e5 Controllers/ColorfulGPUController/RGBController_ColorfulGPU.h +2e302198e4556d71e1dd7e27e5265e52287d48b2ff09e5ffbef18d630ec0d218 Controllers/ColorfulTuringGPUController/ColorfulTuringGPUController.cpp +8d02c6eab91b390df2fc17ccbb5a7f24f017bdbc6098a198438ecef50bd6123b Controllers/ColorfulTuringGPUController/ColorfulTuringGPUController.h +585719292290c4d90bef82d6cb952937b46b3aac767747014cf6c84f0ddb1840 Controllers/ColorfulTuringGPUController/ColorfulTuringGPUControllerDetect.cpp +04de8488fc9f4b21b15f8a52cc758766fe443a6b10afbc2383c3578e5a416e90 Controllers/ColorfulTuringGPUController/RGBController_ColorfulTuringGPU.cpp +3247f09a49e8fc1d519311fb0695eeacf5b31d31328c19bd9313ddce724f6682 Controllers/ColorfulTuringGPUController/RGBController_ColorfulTuringGPU.h +902a6d7a22236dd3c684d18b8c5c3d2ee6a6c82b577c819d6c27b322b55c15c6 Controllers/CoolerMasterController/CMARGBController/CMARGBController.cpp +ef162ddc6d02c6889a8ebb5d4d864b65547e51f91cccbb4bbd7d3e3535d2242b Controllers/CoolerMasterController/CMARGBController/CMARGBController.h +0489542796a4962c32e42bc416291e36ed23080bf26afa0712a308e3eb3907c4 Controllers/CoolerMasterController/CMARGBController/RGBController_CMARGBController.cpp +93a292557b7925c0eca9ac40a73507f8b775992139fc9a5547cc96184c9c5c38 Controllers/CoolerMasterController/CMARGBController/RGBController_CMARGBController.h +874e5a1cac4a556f1a8ee97e61c84184c91f4688bac45b8efa15e3fc1321334a Controllers/CoolerMasterController/CMARGBGen2A1Controller/CMARGBGen2A1Controller.cpp +e8657658ecfe95d64c04310daaf6b3a381e87c6ae931362328b121bf8231f6ad Controllers/CoolerMasterController/CMARGBGen2A1Controller/CMARGBGen2A1Controller.h +8b781aabc1e0377059c4cde32f960d2f393e9290552a32b4397b6c38fda54beb Controllers/CoolerMasterController/CMARGBGen2A1Controller/RGBController_CMARGBGen2A1Controller.cpp +11783015ccdae85631979f5b92dec0a9bb5bc692576a3f5d1a03461204d6b762 Controllers/CoolerMasterController/CMARGBGen2A1Controller/RGBController_CMARGBGen2A1Controller.h +c46c0fa84a04363836c19526f617dce4283476f784346be625813b681b121a75 Controllers/CoolerMasterController/CMGD160Controller/CMGD160Controller.cpp +c8ef866dd39297d5ca3f035c118132729d29000fb0d125e9eb1e02b16437ab8d Controllers/CoolerMasterController/CMGD160Controller/CMGD160Controller.h +ce5c4169dda4aa8a4c0786d793432369e2cb00fd4d1dafee90f2d58b740e8cd5 Controllers/CoolerMasterController/CMGD160Controller/RGBController_CMGD160Controller.cpp +24823739d0f553247e4126f7e0af3719bf42cd12ddd41941f08ec62479630eb4 Controllers/CoolerMasterController/CMGD160Controller/RGBController_CMGD160Controller.h +68235fb2ae177e6cd90216799299b16baa71948125ecc155d1c90827d0364012 Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardAbstractController.cpp +dfdba2c100933aaaacdc397828dff524a54cfab8b3ba8c5f8d7f3790aaff8035 Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardAbstractController.h +1a0e67a26187fdf212a345e783ce543ddecf4dc7d4ae213e0de7e1c6ed67c509 Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardDevices.cpp +11d54ecf73bd62c13d84c1eb6649092dca7312d44ec158c37498deab7f692dd9 Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardDevices.h +3b6592e48b4b1ae0bdf997a1d894a16c8b8ed1d6b547c21612ac79be2e3f8112 Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV1Controller.cpp +f27a5d267175c3a1d8e21eb041676449ffbb642a6dbc62b2d36261629e33d902 Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV1Controller.h +8e53c3a110ae91825f8dea2c3202b30a6513c8ec1e3e9ec808933318f4884de4 Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV2Controller.cpp +452abf09346bb27caaf1dc1e8f2a78b327ca2568bf1c823096adebec7dc53ce6 Controllers/CoolerMasterController/CMKeyboardController/CMKeyboardV2Controller.h +c680658a7b10940479bfac269f328c1bb7a0e934c850223409af36dd305862dd Controllers/CoolerMasterController/CMKeyboardController/RGBController_CMKeyboardController.cpp +8528082bf921b2be587b338997c4438fd49f4ecf824d51dfdb43b1ad54921112 Controllers/CoolerMasterController/CMKeyboardController/RGBController_CMKeyboardController.h +ee4da0c063aa44c7e5820e03bd1f47bfa46e02a063c245c06eebb4f4db66330c Controllers/CoolerMasterController/CMMM711Controller/CMMM711Controller.cpp +4fa0921006dbf325a5c715f10350853186593bb120951b7a739b919048fed5f5 Controllers/CoolerMasterController/CMMM711Controller/CMMM711Controller.h +baa9120012d60d883b9c0fd5143090ba9df72e3868e3b545404b2dcb542c5d63 Controllers/CoolerMasterController/CMMM711Controller/RGBController_CMMM711Controller.cpp +f57d1e51385815b76b23cf9fcac6d684f5dbabd5ed063100ba81867c48a34cdd Controllers/CoolerMasterController/CMMM711Controller/RGBController_CMMM711Controller.h +367c094f59e6c436af526c5b3c78cd30d7f6f81a024fddf92a21b6d051cf701a Controllers/CoolerMasterController/CMMM712Controller/CMMM712Controller.cpp +7241ec7a29d9cb4cd82d99967fac7dbc1b6c50294706303252e60a95401d2923 Controllers/CoolerMasterController/CMMM712Controller/CMMM712Controller.h +2891c954903fd06998851283e523356b78f57e55ad0cea0bb5971a2c03fb79e8 Controllers/CoolerMasterController/CMMM712Controller/MM712protocol.txt +de3ada7badaa515b36249cf1b03a3c981703324e8260e483aa620e97184963d3 Controllers/CoolerMasterController/CMMM712Controller/RGBController_CMMM712Controller.cpp +3c36fc5c0f2497d51cfa1ef1a6a6f21223af818d969e025b8c0deb88f1225adf Controllers/CoolerMasterController/CMMM712Controller/RGBController_CMMM712Controller.h +972fcb0862743e0c4b593ecd8da26d5a2339cb34305dd6ae0b7dc27ee9e8f57f Controllers/CoolerMasterController/CMMMController/CMMMController.cpp +97c10d52ea9c521c30f2173e7cbd1fcf14c9745497d777bc4048c45a26bd6b24 Controllers/CoolerMasterController/CMMMController/CMMMController.h +614b8e05cc556097cca47f5f8715f302ae7e261833177323942db32455688e3e Controllers/CoolerMasterController/CMMMController/RGBController_CMMMController.cpp +89828f4ab91063403b27481a92ddce2572a9c2d5926c5aa8e89d040cd371fc62 Controllers/CoolerMasterController/CMMMController/RGBController_CMMMController.h +2e6b70d2026c40ba047d238dd842ee2a15994c256b8d135c136058282407445c Controllers/CoolerMasterController/CMMP750Controller/CMMP750Controller.cpp +ae617334e59062a2f03d701dd67cc6c723b766dc30f3b92442d7bb75a7543ce9 Controllers/CoolerMasterController/CMMP750Controller/CMMP750Controller.h +d0f326fc672b030c3c11d97dda65e0ee7de62b1e15018ee1a3d9e73efc163b81 Controllers/CoolerMasterController/CMMP750Controller/RGBController_CMMP750Controller.cpp +00578da45c3b8a9e8aefc8fe1121726d4602194879dd87208efa4bd4ada52483 Controllers/CoolerMasterController/CMMP750Controller/RGBController_CMMP750Controller.h +b6577c73cd7bfb52a8da33066517773f339e119d615ad1e1a8caeda9c13ec74a Controllers/CoolerMasterController/CMMonitorController/CMMonitorController.cpp +3f682dc443160b50ff0599054294ccd3873afc8537e38194f5441acfafd66731 Controllers/CoolerMasterController/CMMonitorController/CMMonitorController.h +514869121c2584c77d102d307d9484933a78e660f670b0f65948d1b35ced42a1 Controllers/CoolerMasterController/CMMonitorController/RGBController_CMMonitorController.cpp +ff0901c07b2cd30ff49ddd11be4753eb2414451287b1b41db1a8a0c44191b74c Controllers/CoolerMasterController/CMMonitorController/RGBController_CMMonitorController.h +6c1670c465eb73de285d280108f9e2e65e9b5f65307a697f8a83cd375c99e48a Controllers/CoolerMasterController/CMR6000Controller/CMR6000Controller.cpp +cf8f4d7dd976834e7f31596996811ad64ec7517a15fa312155d4f8508563931b Controllers/CoolerMasterController/CMR6000Controller/CMR6000Controller.h +bc446d43a5c4353fab94c2a8ecc729c4061298d6efe213f82b9ebff821ee606d Controllers/CoolerMasterController/CMR6000Controller/RGBController_CMR6000Controller.cpp +92b543016a416bcc0432ee5506be7b4431034d62ba64f6b53b76b27fa56dd7f7 Controllers/CoolerMasterController/CMR6000Controller/RGBController_CMR6000Controller.h +041097cf1bdb180ab05beef783cbadd075c72973cdec175a46f9cfdb67bab351 Controllers/CoolerMasterController/CMRGBController/CMRGBController.cpp +d92cd39bde090aa0126b9cf82d27edc702f1acd0b112c77d43c7d037caa0fa81 Controllers/CoolerMasterController/CMRGBController/CMRGBController.h +89837bec9ab42dbba616f86c71c1ec6fc21b569fe86b5011692a00502b8b5ff3 Controllers/CoolerMasterController/CMRGBController/RGBController_CMRGBController.cpp +ac5e66fd3263da1fde05d85bf6a5becfb2d0d0228a95288c4ac5fc35f80b6752 Controllers/CoolerMasterController/CMRGBController/RGBController_CMRGBController.h +c700ee51ebdf62286ca662464be6539dcb7a692cd26747797dfc107cbc6a206a Controllers/CoolerMasterController/CMSmallARGBController/CMSmallARGBController.cpp +5b370e199f810bce0dd6925d46bcb1dee9286a31f5917e92dabcb2571ca62c1a Controllers/CoolerMasterController/CMSmallARGBController/CMSmallARGBController.h +a7949241f0eac2848b2ded81bfdf7a521906d5632b807d743b69d3732afb453b Controllers/CoolerMasterController/CMSmallARGBController/RGBController_CMSmallARGBController.cpp +233acf05ebc60d047c1732c2622f7d5c13e74b188c1ccafddb6ee7a837c7ee01 Controllers/CoolerMasterController/CMSmallARGBController/RGBController_CMSmallARGBController.h +28c826bf63e95257125e6b37c7ee5e189f4d671d4cb629caf3a9bbb93f0d3185 Controllers/CoolerMasterController/CoolerMasterControllerDetect.cpp +0119a95aad57638e7120f98b770c36424ad2b8f5daae1581c2278ebef943aed5 Controllers/CorsairCommanderCoreController/CorsairCommanderCoreController.cpp +5056f2bf2e0456e179d68b72ece2dc6fe19d3379d4f5e88feee493af29585bdc Controllers/CorsairCommanderCoreController/CorsairCommanderCoreController.h +2f1a52041b32002acf831c2ed8380a3e011c8c2fd4b14d1f983be7bae0c92d15 Controllers/CorsairCommanderCoreController/CorsairCommanderCoreControllerDetect.cpp +73ca899ef6f2a6cc071d478c15652e05aefb8601542d08686b43e415bf439bd9 Controllers/CorsairCommanderCoreController/RGBController_CorsairCommanderCore.cpp +586c6b47f16451d44af647d712eb9c8793301fb2db0088ceda4db0cee1bdd27c Controllers/CorsairCommanderCoreController/RGBController_CorsairCommanderCore.h +221aa08c68dfd51619ba2b7f9f102dfaa16e1af4c778d2c8f9ecf90c853abab4 Controllers/CorsairController/CorsairDeviceGuard.cpp +a534a055255f0a2bdfbd8dc0cc2df0443405bc61c72ad88603931e2b7adff2aa Controllers/CorsairController/CorsairDeviceGuard.h +0fd98d63ebd85ca1bb1cf691c453a3038e826adc70ea709252ae2830a523046c Controllers/CorsairDRAMController/CorsairDRAMController.cpp +6a51f3dce6681916f596d5817ebbabf66ded0d7767d95fcb3fbb7bdfed5346f1 Controllers/CorsairDRAMController/CorsairDRAMController.h +f0768ae5c2c957cf5451619e3213c12272938fe1894112a3da0bdb2c503af835 Controllers/CorsairDRAMController/CorsairDRAMControllerDetect.cpp +1207a475473e3151ddfc08de6b568c51c62780ab15a2bd591a47cd3ca295659a Controllers/CorsairDRAMController/CorsairDRAMDevices.cpp +9b9e4374cd393d450f4a42eba70824443ea580dd27268076204bb8d5f2056ea2 Controllers/CorsairDRAMController/CorsairDRAMDevices.h +f58315e78564f2d3f536eaa237f38c501167aa0de98bc2f4a4b6d77336856fa7 Controllers/CorsairDRAMController/RGBController_CorsairDRAM.cpp +42ba1b99f89ceb8f6545d4ec4d0d47808faf82ce3fb56b676bc3369be586ff2d Controllers/CorsairDRAMController/RGBController_CorsairDRAM.h +4b4a2285d0d6190839477bb60b160b14a776687434d52ba98bc740deb72fcf0b Controllers/CorsairHydro2Controller/CorsairHydro2Controller.cpp +a8fa207db207d79be3af432144f2599108b9c473fc23ff1141accaeb92a2ad18 Controllers/CorsairHydro2Controller/CorsairHydro2Controller.h +d9a360efd550ae0ed4e6d2a32b2451bff1360d181d31b82263000fde7e3e3616 Controllers/CorsairHydro2Controller/CorsairHydro2ControllerDetect.cpp +e401095c651c4a978974ebd8d23787a840a113a750ed39df13d4143f9665440e Controllers/CorsairHydro2Controller/RGBController_CorsairHydro2.cpp +fa11e25b0e0b142241390c6855d2f921f573007922b99f005e8d612b918046a6 Controllers/CorsairHydro2Controller/RGBController_CorsairHydro2.h +844b9d17fb4bd770eff14bac64cfae974e9ce63e543a8828a031383c828efe8a Controllers/CorsairHydroController/CorsairHydroController.cpp +7baf01fec8902bfbb5bf534d42c97e6d7a47f52d0c7da43c6ba1149bde597b37 Controllers/CorsairHydroController/CorsairHydroController.h +7e19635c1db39cebd943fe2f1998614404a73809bc087370951f7854881b1ba2 Controllers/CorsairHydroController/CorsairHydroControllerDetect.cpp +30d13447dfba383bcb0f5af72ee692f3bd5268fde9fc5646afb006dbfffa1ddd Controllers/CorsairHydroController/RGBController_CorsairHydro.cpp +5628b763257673d47873b0e560889e7d86d86caa0405f81a96ea7f8e6c0e5e7b Controllers/CorsairHydroController/RGBController_CorsairHydro.h +049c02eae17b9b741156ca9b5bb510f2192cd0a52cc305e4ac62a58aa40e3cef Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumController.cpp +f49ab2749f185e8562e1c1cfbe71389c7c9a8287d3ca6bfb1e70a140afe8d261 Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumController.h +820bfd91394b8e1753468405bfbf02c408663e5de4e51a5183a4b13587761584 Controllers/CorsairHydroPlatinumController/CorsairHydroPlatinumControllerDetect.cpp +224fbd4d976be3ddb35d9cd7b7f913dc1255f83954fa40bdbfae89182d4414a9 Controllers/CorsairHydroPlatinumController/RGBController_CorsairHydroPlatinum.cpp +6394419a89309e46f71afcb20cb24777c58c9dea559782177c4e0a9f228101b0 Controllers/CorsairHydroPlatinumController/RGBController_CorsairHydroPlatinum.h +ee54a592a381adf891dcb4b2900bc70e77ed648f30055fdb78419da149d4256f Controllers/CorsairICueLinkController/CorsairICueLinkController.cpp +6e4b1d6631f3f43fb35991e96d984705649fbd4896a33bc673a711674df7e82d Controllers/CorsairICueLinkController/CorsairICueLinkController.h +f4bb28c325db12412d176b8880aa7cb9114e77ca4f4fea35614d83cc2dc2cb6d Controllers/CorsairICueLinkController/CorsairICueLinkControllerDetect.cpp +ef509cd8316f26842dced1bc9f890fee18cd4230b3ae61a2149d04f30e2126fd Controllers/CorsairICueLinkController/CorsairICueLinkProtocol.cpp +8b8e2c0575b5e33b458444ca55df71b2873da6d431757ba966fc94ff7340070c Controllers/CorsairICueLinkController/CorsairICueLinkProtocol.h +372e14b37b3f4523296b8d8e1c8197fd33180fae044a5cec1341b6aaaf1dd382 Controllers/CorsairICueLinkController/RGBController_CorsairICueLink.cpp +4b3f55e686d334138eed2624998b426c48578a0bcc791141a7318764e8978256 Controllers/CorsairICueLinkController/RGBController_CorsairICueLink.h +4853f121544113134fbd633bfc376e0fc736ca808928c104c9ae7db12a592813 Controllers/CorsairLightingNodeController/CorsairLightingNodeController.cpp +8a6026e441c2ab143c4e4594280d5272d21b401054f79845cf000a0e76d9f527 Controllers/CorsairLightingNodeController/CorsairLightingNodeController.h +83dd40cb6feb3cd96c5f7e9249f6e361ba63e907d3d26cc5193cc6a116b54184 Controllers/CorsairLightingNodeController/CorsairLightingNodeControllerDetect.cpp +a292b047c630a734e67568dbeffb67907bae24a4045ef16d1c85ae804f3e3220 Controllers/CorsairLightingNodeController/RGBController_CorsairLightingNode.cpp +6d4c8a3dc72db3a3916d1eb4019a0676c81734b07f71e6299c8eec8fe0202329 Controllers/CorsairLightingNodeController/RGBController_CorsairLightingNode.h +a24af59cc9694b5e77b333277bd797faf6b86bf96097eeda655325503f2029aa Controllers/CorsairPeripheralController/CorsairK55RGBPROXTController.cpp +99377cecd8d3b9a5e6773b44be4f185441572b180b685b70a4858ae4db061327 Controllers/CorsairPeripheralController/CorsairK55RGBPROXTController.h +b06fa9779de1072262ee97169f392e559505371103de5ed317c1fd043959342f Controllers/CorsairPeripheralController/CorsairK65MiniController.cpp +1bcb0ba8d8770cdd8e9e83afc5157868edd34d07ace151baaa7eed05d8749012 Controllers/CorsairPeripheralController/CorsairK65MiniController.h +db14940607f9df3162862d0ee8afe7faf9ae4c9a08362021ece59eefeeb45d01 Controllers/CorsairPeripheralController/CorsairPeripheralController.cpp +3abf8d57d7c24566de518ce5312f509293a9826b25f30e62d684b0703b9606e1 Controllers/CorsairPeripheralController/CorsairPeripheralController.h +3053be83bdebe0fdffcf85b72b69c1af4af5dcee311c243911b65b9f1716f5d8 Controllers/CorsairPeripheralController/CorsairPeripheralControllerDetect.cpp +d7d57d4ab4008f42aa34baefa0e3f02f9cb8026515bae61bb4de436535503cfd Controllers/CorsairPeripheralController/RGBController_CorsairK55RGBPROXT.cpp +2f4d42279d1a2a32d47db0894876bf57e1939c9fda3de6041a4d366a194035f9 Controllers/CorsairPeripheralController/RGBController_CorsairK55RGBPROXT.h +6cd2276fa7843ba3ada2de011074533d7d6eba1b67109356245523d437ac4231 Controllers/CorsairPeripheralController/RGBController_CorsairK65Mini.cpp +b52f315df8133e7a0a94410f4ea3f8aa41f19a1ca51efcbf75d5e6c8f4917b0a Controllers/CorsairPeripheralController/RGBController_CorsairK65Mini.h +6675df5dc1886206c4e2694f4093d6f8400d77f1d21bc719e02eb1d0f7ccc5fa Controllers/CorsairPeripheralController/RGBController_CorsairPeripheral.cpp +e7073fbc960f50c913523ac912c5f884cdf2e20b09f67ea43627ab230a2e9dee Controllers/CorsairPeripheralController/RGBController_CorsairPeripheral.h +9f4c7dd45fd92f9c967ac2ddb222397e01a2bc5b7843dc66d943b3ea7a7ab8dc Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Controller.cpp +af72aed29f1b941215e36c472938b5598104aa4e69bf3428b394763cf4041841 Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Controller.h +c51ca7f5ffd2bd4167306138f7ab9746e759e24874929d49e20dac20a97a0e06 Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2ControllerDetect.cpp +4a4dceb02725ee68a9b6bc64f1e0ea399e5e05ce99c520a2086bc4778f7b5591 Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Devices.cpp +6aaf6bb8e691892165cdb93e820250bd40548905b1504fa88c1369e7a3ac7557 Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2Devices.h +ca7f632ef46e830fb1fe35633b011ff75e9270cbe46ac3949f929599f1641494 Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2HardwareController.cpp +78ee5af9d5e74c1f23e2b0f2ebfb0a8c27fbdd54a016ddcaf26320d411430c45 Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2HardwareController.h +1f6465ee4b7ea25da8c15f860d1a5a74b7d6b2d1c29fc931d18383345c7b74b4 Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2SoftwareController.cpp +abf82841acaa69948130ef96576d3b05d202925e2a43bcf72a4bc541403d9583 Controllers/CorsairPeripheralV2Controller/CorsairPeripheralV2SoftwareController.h +a1d33a01125d46eabf4036713bb274ae75130720673124e9b35abf8a1c90501d Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Hardware.cpp +47b835be5acbb5357ef729918e27d460bc342f92977d8ebb68e5622a89ddb30a Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Hardware.h +e38c3f00ebf82deb8e738158669f207f480480d96f7347e285fcd8fbd35a51d0 Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Software.cpp +f4d92dedb4085e913062a2557a5f4f65418525fa69ea3dbfd9014c76514a4d70 Controllers/CorsairPeripheralV2Controller/RGBController_CorsairV2Software.h +b3388d2f4322498af9830193539d41e9a115d51a74862a8d38fb8e67bcd425a5 Controllers/CorsairVengeanceController/CorsairVengeanceController.cpp +f94b0b8d69610e450a4f74ee1944c2b5ec9ed6688e661d5e6c73717f964e5f47 Controllers/CorsairVengeanceController/CorsairVengeanceController.h +c041028118fd6f5e995acf93a0d8858597bebc09a58d171448b6f0ca787a738f Controllers/CorsairVengeanceController/CorsairVengeanceControllerDetect.cpp +5db550d1e29ebcd14b5c614db5e6d18c8c8342d2996a390f50e1791afc14916d Controllers/CorsairVengeanceController/RGBController_CorsairVengeance.cpp +2f8dd9266569ba1d42dab79729d72ea729913d0f8a1b2e3462cba4d7ad114e66 Controllers/CorsairVengeanceController/RGBController_CorsairVengeance.h +aecb50c339f77ee321d8fddfc1805ce17b9a04684a257c0f9e165d4d63dfbae2 Controllers/CougarController/CougarControllerDetect.cpp +ac6f2097eddc1c1dde30c3a363788e2500f598fa5aae9b657a638b784d869ab4 Controllers/CougarController/CougarKeyboardController/CougarKeyboardController.cpp +7d343894fd850d1e0f9eebde61b5d9beefcc1a2bfd2de58d2edc5c4781cae239 Controllers/CougarController/CougarKeyboardController/CougarKeyboardController.h +49463e2e9f537ab2f8ba39b059d8dd1d6a866f98e6888c1af2e930157cbe377f Controllers/CougarController/CougarKeyboardController/RGBController_CougarKeyboard.cpp +89352d89637dd30719253ff67c448afa546cfee2dbecf4fd6d019df0c44b7813 Controllers/CougarController/CougarKeyboardController/RGBController_CougarKeyboard.h +fee304fd672509c37c322817844de3aff96b08af7a4d0a494f77a4df6be7f05a Controllers/CougarController/CougarRevengerSTController/CougarRevengerSTController.cpp +6861d33f761962e26211d6ac0fc1211dec18d03e363f2d64a6df6d0bb78a913b Controllers/CougarController/CougarRevengerSTController/CougarRevengerSTController.h +aded309fc84d424ffe6a3618784dbabfb9c82f7b609286fd9ded0d276891c7e4 Controllers/CougarController/CougarRevengerSTController/RGBController_CougarRevengerST.cpp +dc7af4151745f2476edf1c6c596a03cc71f0ffb74fe56046e435c33a017145e6 Controllers/CougarController/CougarRevengerSTController/RGBController_CougarRevengerST.h +433624b4459c8b2b393a0bfd7aadcb255a618ededad1536bdb6e9fd6cb1c6c1f Controllers/CreativeController/CreativeControllerDetect.cpp +3ea128b71bb96124848d8125a000fc0130889095dd161ec0e3a8ae081ada5e1d Controllers/CreativeController/CreativeSoundBlasterAE5ControllerBase.h +78e9ea6b4b733282f198831e5e90bde15950c1c65d29a2c937c9169fd7d249a8 Controllers/CreativeController/CreativeSoundBlasterAE5ControllerDetect_Windows.cpp +ba677ced61f6beeca1fb61e081a080a4e946b4b8c92d968a47d54f3284d00d1c Controllers/CreativeController/CreativeSoundBlasterAE5Controller_Windows.cpp +4616f707a11d45e94686c716e9467683e5e5fd9ecae64d33d528adf6c875a34e Controllers/CreativeController/CreativeSoundBlasterAE5Controller_Windows.h +b8d71d7e2d07988ce5c1b025c5c07f2e15f398a4f7d7307e6dd9d057980305ba Controllers/CreativeController/CreativeSoundBlasterXG6Controller.cpp +a1238b92f35e64385a974a667b686b51e1ae8127169618458744cb6c4d53d3ac Controllers/CreativeController/CreativeSoundBlasterXG6Controller.h +1f3f839b2289229bba382987f79513776fb779c7fbfafd4034e633f91ac8388a Controllers/CreativeController/RGBController_CreativeSoundBlasterAE5_Windows.cpp +a263162b73edbfeb423fd55cccb79b13b496cdad5ab069afd8fe75526aeb9432 Controllers/CreativeController/RGBController_CreativeSoundBlasterAE5_Windows.h +bf8bb17fb0b65b0a5a575cbfb5034500faf676a8e82f9285a37eb6c894b1d2db Controllers/CreativeController/RGBController_CreativeSoundBlasterXG6.cpp +756355f0f721a7749b53e505d134fdf8536964a3e4fbabb2c9b4d9ee7b18fdeb Controllers/CreativeController/RGBController_CreativeSoundBlasterXG6.h +9078a511b5c579f94c43141f5913a4e30ed495a532f761062fa1fe7472c6c357 Controllers/CrucialController/CrucialController.cpp +3984152f31e02e7a91bf2b6a9375d37799a80450b7284c83040ba36c4108efb2 Controllers/CrucialController/CrucialController.h +173af25d59b6374c7ab4d509a4792df65fe0e6a1fbd7253e173434447e3b976c Controllers/CrucialController/CrucialControllerDetect.cpp +f657cfa4b61206283a22de6a224e78b684b1ed26e7f70d256cafc3790e235689 Controllers/CrucialController/RGBController_Crucial.cpp +460954e9aa65f2aebce3316725a7af8e2a3623dc8f9876d73818a89e34a4ad0a Controllers/CrucialController/RGBController_Crucial.h +f7116ed6549efcf95c55405c2ce893266ac2560bd00693855985f9464868be86 Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiController.cpp +f704df3449bdbde94e824f1cca67c169d14b47352ef74ce58e8d587bdb354c91 Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiController.h +e363e820c0805b073f206abd04f1b8b9b3b5ce9a5a8407d1cdae4f9a2f3cfa27 Controllers/CryorigH7QuadLumiController/CryorigH7QuadLumiControllerDetect.cpp +6ef8c4444033a11ade28b7d0e447245c8559a17375bd1ed375e42bca099a6ac6 Controllers/CryorigH7QuadLumiController/RGBController_CryorigH7QuadLumi.cpp +a63ad5252268681c610d3e9e89e6174d26e44fbcdde7a712f705cb62f13d3e21 Controllers/CryorigH7QuadLumiController/RGBController_CryorigH7QuadLumi.h +912ac3d5cb06c14b7709b7dc7516ecdc3873a6c30aff5a4bcd833e6531596d2c Controllers/DDPController/DDPController.cpp +cbd76541119b9e702faf72990e450de27e37cd556e0daff9f87fb0da1340ef7b Controllers/DDPController/DDPController.h +41af528db816279ea460f7edfc5bc1c0a4b88bb334dbdea7ffec153bbded7e60 Controllers/DDPController/DDPControllerDetect.cpp +f36bc8f863f5d54a6666d4da7000eb663f52d5b301e8b9446df991498243f132 Controllers/DDPController/RGBController_DDP.cpp +df5e963d0fd89101a9ba5d7a12e9e589f956e2c31dbb272a247aa2b58b398f4d Controllers/DDPController/RGBController_DDP.h +e64ed38aa897ac7867178bde1f581747a257e6d4e81f38813cc1db000d3d8df7 Controllers/DMXController/DMXControllerDetect.cpp +8cc5c010e673a3f2178be24324d555dc81e46bc2ff01bc05ca24466d30849f71 Controllers/DMXController/RGBController_DMX.cpp +b721de1a3c09a1cd161258512604f47a01ac285e5555ba7af26f2b914a78ebae Controllers/DMXController/RGBController_DMX.h +00f91d147afba588213381ed628a2996983904298df08d963b40c02e644ab2fa Controllers/DRGBController/DRGBController.cpp +8bc8b7a61d4b27ab0e0fed167a040c6c73c7fe2d849dae5b484cd5076c7c44bd Controllers/DRGBController/DRGBController.h +a898dc9ebe826356554b8356888568009cd2f05425b1aa55909c7c5fadb85746 Controllers/DRGBController/DRGBControllerDetect.cpp +f86581233d593c34e3520fe37c7e0d8cbaffeaa593ffe4fc364ddf410f66eac9 Controllers/DRGBController/RGBController_DRGB.cpp +9bdcfac0b8d46be31e362cc07beb73f2959bc76a9c8f242b928eac11574b9274 Controllers/DRGBController/RGBController_DRGB.h +b35e1e511777688296656c344139207fc78658d18794d0984c3209ca519fa327 Controllers/DarkProject/DarkProjectControllerDetect.cpp +63018d38aa496541d2c74cf55b68cf98c079f3f42402a1c3158f5c631f245d56 Controllers/DarkProject/DarkProjectKeyboardController.cpp +c2a5cfa652a330e5e314274306cf3d2cdcae1b894536a6adbca4c83859f4b98e Controllers/DarkProject/DarkProjectKeyboardController.h +4f7c0ceecef0268c0ebbd4ed60e640a0da67b61b69e8dc71ccb66d1bf9a97907 Controllers/DarkProject/RGBController_DarkProjectKeyboard.cpp +575afe50a3727cdc4a255e415228e79f89b2eb5c7137471382ca857453993fd5 Controllers/DarkProject/RGBController_DarkProjectKeyboard.h +78cb2ce13a679e024679ec562353e7a8113869bdc02f7d4b4633476d55ffa1a7 Controllers/DasKeyboardController/DasKeyboardController.cpp +d79baf72b8d14746c7f2dc43c2cc7fe00989aa3e12f6714d4cfeabd1f03e1804 Controllers/DasKeyboardController/DasKeyboardController.h +19063145be1b9f2c57d402835c3423b6ab66760816d4eff816eb8cf881c941ce Controllers/DasKeyboardController/DasKeyboardControllerDetect.cpp +44a3827f023bee348c07b9e1e10e9bce1d7241add3e4c221a5a80350dae4c47c Controllers/DasKeyboardController/RGBController_DasKeyboard.cpp +404ae2dadbda32d179cd4f16aa520906544cef9b87f099e2aa6be8f966509c85 Controllers/DasKeyboardController/RGBController_DasKeyboard.h +fb73fdfed9a60b5ef758e2a2cc51143a09f383e42261f2e60f3fbf3ee7a2e15a Controllers/DebugController/DebugControllerDetect.cpp +004f93eb92778ab3ceb0a94c75caace7c6c0a5094632d3629abb2878c9901dd3 Controllers/DebugController/RGBController_Debug.cpp +761ffd36070cb27cbec0cd4ea07b97bd70dacd81810ecf1c5385877252efcf3c Controllers/DebugController/RGBController_Debug.h +b630a815023968fec8a0cc855add353e1f29c61a57d75da1762b83d3f9f20cbf Controllers/DreamCheekyController/DreamCheekyController.cpp +86d66d52023595d80d5bc1d88eddf22392c54a7eda50bcbd8e9318d2bfc08fa1 Controllers/DreamCheekyController/DreamCheekyController.h +77e935fac3fbaf514284411b77cda85511e0674b5d00ddfcc8077484cee6662c Controllers/DreamCheekyController/DreamCheekyControllerDetect.cpp +692daf7024163710c928b9addf1a5e793080d5cdaeaab2313ff6cfc9b892ca43 Controllers/DreamCheekyController/RGBController_DreamCheeky.cpp +cde0a873f4bea8d0b4053bbc1954905d0a5762eae668df6c1d5ab030cf2a8633 Controllers/DreamCheekyController/RGBController_DreamCheeky.h +cca5811c1d83309b7c749090e87b6764dadaa3eb8c59a697414a3cec556a41dd Controllers/DuckyKeyboardController/DuckyKeyboardController.cpp +46a234925e637cc6c547360a6d2c743272ce7000ae205067c694b9ed8f1ac672 Controllers/DuckyKeyboardController/DuckyKeyboardController.h +fb86f6bcad3f3525b1b5841b38e88e40ed88c300e6e32026ae6b6ca95eb5b858 Controllers/DuckyKeyboardController/DuckyKeyboardControllerDetect.cpp +5dd4bd7b7408bc5403bab060bb4dea5c03ce977888843f4b6b01c86e1381aac1 Controllers/DuckyKeyboardController/RGBController_DuckyKeyboard.cpp +10e8498f967df5c56b4a7eca0487bad49cb9870ab87de3a3cadb21f253cf3606 Controllers/DuckyKeyboardController/RGBController_DuckyKeyboard.h +e281a7d11d933397bbc6f0c18ccf7376862c0cecd72071273a9b59cc857f464a Controllers/DygmaRaiseController/DygmaRaiseController.cpp +03d4aa98543631f8df10121bf3d7fd4a2b4bc77538bfcd66dcb4c08ccd39ac51 Controllers/DygmaRaiseController/DygmaRaiseController.h +342d6fc89abd957fb8473b5d221569e8ac2aa00653ac41cd0d2955582ad6bb8f Controllers/DygmaRaiseController/DygmaRaiseControllerDetect.cpp +a0470a1607e6fc0691760b8d57d95e3c499ebaccd8028b166436a0304d2f974a Controllers/DygmaRaiseController/RGBController_DygmaRaise.cpp +6de4a7c3437518a1da96196ce3b85a38422556a33022d757abf38ecee50d25b4 Controllers/DygmaRaiseController/RGBController_DygmaRaise.h +77f3d79431eafb3bc7e0a46002d72f71f39c62a0eb0e59aaf2cf45e90d876810 Controllers/E131Controller/E131ControllerDetect.cpp +07517ba9f621e52c579c623196fcfd1ef858a90cdbadc21514fec0e72b1bc924 Controllers/E131Controller/RGBController_E131.cpp +bb2d9be61303059c43ce4392a1a86fd9ab0c29dbdbe0695bbcd021fb1ed7c755 Controllers/E131Controller/RGBController_E131.h +1c7618a6c34819a1f8e31e0a10b298d20879ebac0ee5497ffcf3635ffc0994d9 Controllers/EKController/EKController.cpp +9b178d2fd9de330e3d258e464fbdf18c680ec5d90d25013c94d681abd49cf6f1 Controllers/EKController/EKController.h +62278db8fb1d131987f064f961c7f08fe1c2862f6f5cafa22b8a0aa0a4ba0506 Controllers/EKController/EKControllerDetect.cpp +bce0fbdabf9b5ecc00395e9dac5216685ad07430b63c4f9ba1b681600170c4fe Controllers/EKController/RGBController_EKController.cpp +fb06c36098c3000b7a588bc83bad7b7ae03eefa9d11a017541d510c13d938633 Controllers/EKController/RGBController_EKController.h +0e40c9705eee0838edbc63a21b6d93a5e8d275adda513692425753cb542e1d0e Controllers/ENESMBusController/ENESMBusController.cpp +55d7766964ca3bf9b1e051116efb2836a1dac3c48def4edfe026f4bb6a7bc45c Controllers/ENESMBusController/ENESMBusController.h +b93910e7eb5856fab27945f0f9f378e7c710f5e688ca06ea53ca8b1ec5d16123 Controllers/ENESMBusController/ENESMBusControllerDetect.cpp +b55cd10672d75c951eb8ff865ea59e1e04ee40d0107fa3f06eb30a72c19272f3 Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface.h +9e6614d85d65c908dfdafad4e77bf11cad88ba4195c18a32ad21ba1a77b6cec8 Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_ROGArion.cpp +b6a7e6b31cac960a4a1395147f24f9108e1c98f5f283e73f53f3217e9936d2b9 Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_ROGArion.h +a3785f1b2382e2ebd3ae15ef293ad116267ba6044839d6e0f13013b2a25832ee Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Linux.cpp +ff00506c29da4b6afdaaf3ff3b02717f90ef7cf50b7913fbd57c799d4f5f8786 Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Linux.h +b7c227cc3d4011a59105cd48db6c1999b746e71cab87b945a601e16b0abfdd26 Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Windows.cpp +f63444e53854e2e602350516ad9861eafd8a19320ad97b116e1022bb10ecd133 Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_SpectrixS40G_Windows.h +235e24858ce8c17463c04a6e041791dbb3d6ad0c5166a7bed251f619032f01fe Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_i2c_smbus.cpp +68f3d39fd9a81c500b658e12524c35cf55a2143d9bfe3964c7eab7ad7d71ee92 Controllers/ENESMBusController/ENESMBusInterface/ENESMBusInterface_i2c_smbus.h +ba8dadb750ee725226f892442e327baeaec2064b4491d1dc43069805385d0f75 Controllers/ENESMBusController/RGBController_ENESMBus.cpp +b6a945fe55fc5532e8ba74edc0d151bb0ae5db8d1c13d3fee86fdb930378a4e6 Controllers/ENESMBusController/RGBController_ENESMBus.h +ec136b1a7d942c0158316dce86fc3a379682e6e33322b920e91bec32ad5097ff Controllers/ENESMBusController/ROGArionDetect.cpp +810e11bcf47ca2e6f82fe7e00cd4b8b07768817b86337c29f635b3f1668a9fd3 Controllers/ENESMBusController/XPGSpectrixS40GDetect_Linux.cpp +b95912002f0e8c720030a11f51af5de6c17bf1671164f62b9d61d4bda2746201 Controllers/ENESMBusController/XPGSpectrixS40GDetect_Windows.cpp +7b70efe00df19218d4bc7e04f44edc3f8e8fdf0f25fd1aa2e1c50b783ccec01c Controllers/EVGAAmpereGPUController/EVGAAmpereGPUControllerDetect.cpp +4d7848f8d93c020a51442a17494ebfb7636a8dc62d8d8d06f064e4d39b8ebac4 Controllers/EVGAAmpereGPUController/EVGAGPUv3Controller.cpp +e6c2871fa9b9c26f1d5ead3388fe4a27d8233222e6c47afdfebf7a9c81821c18 Controllers/EVGAAmpereGPUController/EVGAGPUv3Controller.h +30bc15fa58f23e4408336d405080da216d19fe8388a030945aa84a81d1ea5408 Controllers/EVGAAmpereGPUController/RGBController_EVGAGPUv3.cpp +bfabe676af5905a5002903ac3615599dc0d502864343d8923aa7e284be5cba35 Controllers/EVGAAmpereGPUController/RGBController_EVGAGPUv3.h +de95af08e1a0c6fb27148e4463f0f23bc4bb93a727ff93c89261981a825ba6f7 Controllers/EVGAGP102GPUController/EVGAGP102Controller.cpp +1ef2cd5e6ec3d6163f8fcc76a967bed97712dce5b60380feed20c123f94f86e1 Controllers/EVGAGP102GPUController/EVGAGP102Controller.h +9a367e305242d64641b2266c2cc1c6d7267b90829b7208b737d766b6dd36fca6 Controllers/EVGAGP102GPUController/EVGAGP102GPUControllerDetect.cpp +2aecc4908c70c0ef2b1f5b052651e1b19d48043f11dcc2e6237704a60a65f7a1 Controllers/EVGAGP102GPUController/RGBController_EVGAGP102.cpp +ae4762f1ff1f50789c15a22088d71c2f4002b0d111965b16fdf876d43beafb2e Controllers/EVGAGP102GPUController/RGBController_EVGAGP102.h +d207fc88b8c06fbadc613a4f90d41860478037663795a5c0e3d9d36f58a30bc0 Controllers/EVGAPascalGPUController/EVGAGPUv1Controller.cpp +6ef7eca734f15f73234198f7a565d5f203b11ff60bf2b6b73fcb8249e92af090 Controllers/EVGAPascalGPUController/EVGAGPUv1Controller.h +7a7e1b521306bb23ae146104e3773b7891aff7adad6243727897592f35e60d56 Controllers/EVGAPascalGPUController/EVGAPascalGPUControllerDetect.cpp +4ea5df7cca1ee8778b3a1a19a8a4bf1ea5a942e543f86b2aaacd268052f930df Controllers/EVGAPascalGPUController/RGBController_EVGAGPUv1.cpp +3f1643ea07b8db536f7611d75c477a99e1ef0cbf17b63a394499ce36a0b805d8 Controllers/EVGAPascalGPUController/RGBController_EVGAGPUv1.h +9c884f6a96902cc74ec8aff2ee638c218ca07c9f2dcb143b37a2732675ce7789 Controllers/EVGASMBusController/EVGAACX30SMBusController.cpp +8ba8f1456bbddd796d4507aa30821fc3655d8664283282e80fd2cc3fa8663b15 Controllers/EVGASMBusController/EVGAACX30SMBusController.h +63868962825fb213a3f8327f5aee8069255f5ee4b3bd5cc75fcaaed3461f5a30 Controllers/EVGASMBusController/EVGASMBusControllerDetect.cpp +f363068dbbe0fcfd724bcf479f52da8e4e881db106078d7279e7ace7a38d77d2 Controllers/EVGASMBusController/RGBController_EVGAACX30SMBus.cpp +46919f7e7409d6de50f89efe362bdbbc8083f507d7b10fa5a6f1ec237f01c51f Controllers/EVGASMBusController/RGBController_EVGAACX30SMBus.h +b41f86687f5b36355703373f372e126b5d74403eeaff1c10f5aafb1dfe5d8d87 Controllers/EVGATuringGPUController/EVGAGPUv2Controller.cpp +c4013ceaf26117af0d96f4448df267137f4d095d3c55fabeceae8a437a0bd83d Controllers/EVGATuringGPUController/EVGAGPUv2Controller.h +3a8ada665c0d52998b42081126d75349f509c33b5196b482ab8da929172095bd Controllers/EVGATuringGPUController/EVGATuringGPUControllerDetect.cpp +1f84fc17d0beef86fffd370cd9e6c2ddc1763bba127e2f9234a7978364b7d3e5 Controllers/EVGATuringGPUController/RGBController_EVGAGPUv2.cpp +bc4f36f87b0e7249f360452c88dc9a9e1daffa3231778d771a8666ef7921136a Controllers/EVGATuringGPUController/RGBController_EVGAGPUv2.h +443a74fa3fc508ef6d9449091ea20902df52a7c0faec4b916fc53eabf84a2fce Controllers/EVGAUSBController/EVGAKeyboardController/EVGAKeyboardController.cpp +5ba679ee728659bc6f0280179cc7a98ea70833421021f466c435ef1aa204df04 Controllers/EVGAUSBController/EVGAKeyboardController/EVGAKeyboardController.h +14a248fd8aa3ee0000e9cdc5715aafcbeb2ab5c0275495e7d44c8e11e845cbe0 Controllers/EVGAUSBController/EVGAKeyboardController/RGBController_EVGAKeyboard.cpp +b228fe190a97349288d24705ce602158b9f2258748f44d449e35ef3da8571ec9 Controllers/EVGAUSBController/EVGAKeyboardController/RGBController_EVGAKeyboard.h +116dd0b948beac3502ae848aad7209b7c1e32501f9de380d635237d0d8fcae5c Controllers/EVGAUSBController/EVGAMouseController/EVGAMouseController.cpp +1b305e30cbc52509cf9244d0ce0bba86428a14512b93a97db754972e37d2bb45 Controllers/EVGAUSBController/EVGAMouseController/EVGAMouseController.h +46cf89c244b803f2295cd919032039b03b7023a25a3e204474db40a2c53f81ac Controllers/EVGAUSBController/EVGAMouseController/RGBController_EVGAMouse.cpp +a9af871de4e0d44d372fa0b52a4d177821de2e0dbf63f1e4eb94ff53aae6912f Controllers/EVGAUSBController/EVGAMouseController/RGBController_EVGAMouse.h +5fa2a5328ec1e57ef059ff4159affb441ea177768c2663a86d6f9b5d58c8044a Controllers/EVGAUSBController/EVGAUSBControllerDetect.cpp +a962deb90f892a15b190a77ef4f3073c66d12ee96656d247a0062bf611adaf5f Controllers/EVisionKeyboardController/EVisionKeyboardController.cpp +2dcba0b3bfaa1a1bf931eacc8d0485bbfeb70a03e7f4392ad98f99fbf85fae4d Controllers/EVisionKeyboardController/EVisionKeyboardController.h +53da0c836c373fcf3c41f81d410a3d5a7dd0f0c6f2ffc83e014d6bdd8e0fc027 Controllers/EVisionKeyboardController/EVisionKeyboardControllerDetect.cpp +63f6ccbac933a9c4226d7e4be54bcce65f7ac067b6499b04a7691db773daa85a Controllers/EVisionKeyboardController/EVisionV2KeyboardController.cpp +c34b3a0473cb51fe97e4704438cb264e27fbe43fcf01f1b839aa3dfffb333bbd Controllers/EVisionKeyboardController/EVisionV2KeyboardController.h +d282c90c04cf045dd9e17af679b801123712f496846bed5e2ec6ed4b53ef8b58 Controllers/EVisionKeyboardController/RGBController_EVisionKeyboard.cpp +15c13c8cce91a0585f1447cff62adfaa0dbc42e6a800dbc1f5d68dce7b7e9522 Controllers/EVisionKeyboardController/RGBController_EVisionKeyboard.h +6a6fd1d47ca2b2b43f447922885af881a45d86cd98f9b82ad474f45f202ae54c Controllers/EVisionKeyboardController/RGBController_EVisionV2Keyboard.cpp +2ae851a84ad29690d7b8994991a953aed0ab156b093c7ecf4654e8996d95f456 Controllers/EVisionKeyboardController/RGBController_EVisionV2Keyboard.h +7dfee7c65930fa0d009913113690f6a743a0509a4f3060c3eade4e1eb8fe01e1 Controllers/ElgatoKeyLightController/ElgatoKeyLightController.cpp +cc87d9820b30e1b28f08ba451b8372c2e769d818e57cf38a754ee7941bfbc44c Controllers/ElgatoKeyLightController/ElgatoKeyLightController.h +e1e34e59190e7c9d6f30a875c23fab270829d2cde50fe5072d70cbfa3976a3c9 Controllers/ElgatoKeyLightController/ElgatoKeyLightControllerDetect.cpp +ab980454e06d8927fb50a0e8a5b758faccbb67faa1f5a3311ca5310a1ff6d772 Controllers/ElgatoKeyLightController/RGBController_ElgatoKeyLight.cpp +b5d6f854cc23458213f1735080b7d51f44446c6228795fa59c4de7ba7e1c88eb Controllers/ElgatoKeyLightController/RGBController_ElgatoKeyLight.h +5a54ec2cf1e4121099018feee1c0738a99216552277990f646753da0a70db2a8 Controllers/ElgatoLightStripController/ElgatoLightStripController.cpp +1aacea54ae2158c6762544e8933fe9aef657f1aad6cc0686dab7c400c8a66705 Controllers/ElgatoLightStripController/ElgatoLightStripController.h +aa492eadd696958d8a046c59b226d494e614efd45caa4742e0b3ccab138f86cf Controllers/ElgatoLightStripController/ElgatoLightStripControllerDetect.cpp +7fcd2ef843d3e9a27b2a6e2875d7dd9fd16ae126144af8d62600958f8df64b32 Controllers/ElgatoLightStripController/RGBController_ElgatoLightStrip.cpp +c0172d10e75da5268850a9e7e7133c37eb749473b9b1ac7d65b60a83f734c5b3 Controllers/ElgatoLightStripController/RGBController_ElgatoLightStrip.h +42df5a1414d27cf51ec5cd221e97bde50650efc9aea52a59432658665928dd03 Controllers/EpomakerController/EpomakerController.cpp +73f53aca12004b61f92f988f149febaddab7cb9cfaded66712d17b8e31d9909e Controllers/EpomakerController/EpomakerController.h +f8a27a85425926b78cd4747d4b21cdd9ab2c28208dd496b32f16d144832549c3 Controllers/EpomakerController/EpomakerControllerDetect.cpp +5e588d278727ef9829f00acd77e543578d83768337950524ea9aabe3b99ed911 Controllers/EpomakerController/RGBController_EpomakerController.cpp +e1ac6f6d35fbf0d7d1b00ee0c5e9be3c7a1c516ecd22cbb34bfabf75d426463c Controllers/EpomakerController/RGBController_EpomakerController.h +e60e187176d8f83bc259844d8f31c3dbd65a78a873b573cc33def7405cc06d73 Controllers/EspurnaController/EspurnaController.cpp +3cdd3c5490a7b14bfb0d928a0fe2d80583544ee0c7ae298043735ecc37e9b688 Controllers/EspurnaController/EspurnaController.h +699091be7167bf962333ae3c9d7b25a553b4e0343222bc6ec6f1b4a6a01e4fec Controllers/EspurnaController/EspurnaControllerDetect.cpp +bb18986ffc6c69dfdb2998283035c739d35b4dbf7650b0a06d867cf71e0ef31c Controllers/EspurnaController/RGBController_Espurna.cpp +cace4815884b92e9535bf33920dc6253a78d10fb250fcb490028104105f5202b Controllers/EspurnaController/RGBController_Espurna.h +f407dbc4a595b3ce37925f261249b02a75f7e66646da0c6367b27252b71bd3a7 Controllers/FanBusController/FanBusController.cpp +7d0714cc849718c1b0cccc9eac64dce57428279bf8d1f9c19f6e50f5a5970182 Controllers/FanBusController/FanBusController.h +e106eea678154f5040f43069b31e31e0864c6577472a9f08ba9e5122fb155863 Controllers/FanBusController/FanBusControllerDetect.cpp +1713417ca11a96132fcda8a626fa6ec0e647ae12f8cc6a9e0d4a125ee54a3d60 Controllers/FanBusController/FanBusInterface.cpp +9724e89a9c0f999a2f2dd4b088f35cc38b970b10933ffd52421cdbc892ff9ab7 Controllers/FanBusController/FanBusInterface.h +4a2199e0484ad54165056a5c057d2247a8008590fbffcbe231ad3184fad30da3 Controllers/FanBusController/RGBController_FanBus.cpp +76d26f13f98f5ac1760352e73f72b8a9953f7324c37dd3e753a8fb78b6990306 Controllers/FanBusController/RGBController_FanBus.h +cd2b88fa27028e37dfa45306320bfa5c16139e0521e07f5df02008c094ead685 Controllers/FaustusController/RGBController_Faustus_Linux.cpp +183cfd1ffcad5c76b5bc76f6b30a794dfd1b25e0e02cc4fc9d8e52bb2be3525d Controllers/FaustusController/RGBController_Faustus_Linux.h +e74ba663a2682a02de2c5ee312dab7e84991e9c957a799fef42c155cbf87aa7b Controllers/FnaticStreakController/FnaticStreakController.cpp +56844b03351893443647adf56ad119fe07a37d04b582e4f856977ad309057559 Controllers/FnaticStreakController/FnaticStreakController.h +cb076de832c2ff9e415de9b73f51bfe7eb4a6aace92aeab8ee9843ffde560ec4 Controllers/FnaticStreakController/FnaticStreakControllerDetect.cpp +f8208fa1fd59e64080e7b5290691558a8c344ff5fe3c2e7ea47f4c434619d753 Controllers/FnaticStreakController/RGBController_FnaticStreak.cpp +883a26aace7960f1fd4f666bd63af72190f0a7866dd2a6c862a2f90b44b59be4 Controllers/FnaticStreakController/RGBController_FnaticStreak.h +b6e98926a25ea47210b73947a3e46b967a18f99b7e32acb4afaae98a2f5b3cbc Controllers/GaiZongGaiKeyboardController/GaiZhongGaiController.cpp +c115e451d0a055b2602c4a082ef491027ee3a5238286de0a9a880f1e9da0b010 Controllers/GaiZongGaiKeyboardController/GaiZhongGaiController.h +dd06a88ad40e14bbef73391e50e4cfae3c9555e66fb36a114c75b2b9f7113437 Controllers/GaiZongGaiKeyboardController/GaiZhongGaiControllerDetect.cpp +b983711c1c47a29cc1e00b7a6477e5685701c75f1144adb003dbbebdfbf6a0bf Controllers/GaiZongGaiKeyboardController/RGBController_GaiZhongGai.cpp +dbb349d6797b66660fdf117014c3f771d53eeb7dfa7732f5673273feebe31ed3 Controllers/GaiZongGaiKeyboardController/RGBController_GaiZhongGai.h +e58235c4a0e5a14ea13f92cc7a72a4a6165714f3545eb78728fb22aeabfc408e Controllers/GainwardGPUController/GainwardGPUControllerDetect.cpp +818c287f4dc8596e0c2bdc7c61ec84c7506fdbf0aa0ce637fcb0ae35b1740e3a Controllers/GainwardGPUController/GainwardGPUv1Controller/GainwardGPUv1Controller.cpp +fde5c974bc3977d0e04f69d06364b3db6b0dc4535bc8f71b70278321ae5a6873 Controllers/GainwardGPUController/GainwardGPUv1Controller/GainwardGPUv1Controller.h +de219f16d20ee1afc36af963c5da9ffb86af328d80db33a3c130bf8e81ca1a05 Controllers/GainwardGPUController/GainwardGPUv1Controller/RGBController_GainwardGPUv1.cpp +95e90aef5e34d2da60f13249b8e030c26e2249146d5f0e5e4083ffa1706e6105 Controllers/GainwardGPUController/GainwardGPUv1Controller/RGBController_GainwardGPUv1.h +d46183d227a43367ee820eb321e1c028402f94a94761d3751ea084cb453a8b04 Controllers/GainwardGPUController/GainwardGPUv2Controller/GainwardGPUv2Controller.cpp +ca318917b7bd02ee64fd6a072afa5922c9cd0790b944aa177cdf7fe11b532770 Controllers/GainwardGPUController/GainwardGPUv2Controller/GainwardGPUv2Controller.h +4327a56592c1ae58d2435117bf675a44ac91ab33b229c1c9f53aa5151f0abc65 Controllers/GainwardGPUController/GainwardGPUv2Controller/RGBController_GainwardGPUv2.cpp +c73fbce79864ed8ff439d14d74b0dec75065f1a2b3d44556984a66ea6bfd864c Controllers/GainwardGPUController/GainwardGPUv2Controller/RGBController_GainwardGPUv2.h +a2e2ff8178a18bdceebbc50214406ee667b99dd7723d3f4e4a79ea9552cbe223 Controllers/GalaxGPUController/GalaxGPUControllerDetect.cpp +668b1cbd682fef655653a7ba372cbc4e3bde244c5effc13cefec0e339c1e30b5 Controllers/GalaxGPUController/GalaxGPUv1Controller/GalaxGPUv1Controller.cpp +008c8e85b4212c8b053a95afda8029e32d0daeb3b549452407fa5036a778dd69 Controllers/GalaxGPUController/GalaxGPUv1Controller/GalaxGPUv1Controller.h +d44c5eefeb4128f88dd6f55233579bb5cd251d9bc38db549eb8360fb2ad75d42 Controllers/GalaxGPUController/GalaxGPUv1Controller/RGBController_GalaxGPUv1.cpp +34c820e830645c4b33f2904d649910cefeaf0103efb50c85cf5068a0f5817eb7 Controllers/GalaxGPUController/GalaxGPUv1Controller/RGBController_GalaxGPUv1.h +6c5fcfc94274de8bdfd77bf757d2e223aa81a8cdb6b79b6d8a754415d01c1f01 Controllers/GalaxGPUController/GalaxGPUv2Controller/GalaxGPUv2Controller.cpp +576092c9d3934005a706e77641ba8aa26ab57b91412324ad100d202749df1017 Controllers/GalaxGPUController/GalaxGPUv2Controller/GalaxGPUv2Controller.h +25bc73fe1e2f429fc5b7c6a4d8fc2738927d4afb6b36965c9dba5ff9eff77060 Controllers/GalaxGPUController/GalaxGPUv2Controller/RGBController_GalaxGPUv2.cpp +85b5c039ffe371d601b035f9d096fb4fda6b8989c12204da01d91c453378c7c4 Controllers/GalaxGPUController/GalaxGPUv2Controller/RGBController_GalaxGPUv2.h +dcc31b88a5780d5de106004ac43348acb1a2949ca1e1b109c8c5ab2b6c0a34eb Controllers/GigabyteAorusCPUCoolerController/ATC800Controller.cpp +ee1a9b471a39d4175f5a930c806b0f14a00eb814dcf06c865baa0052bf134415 Controllers/GigabyteAorusCPUCoolerController/ATC800Controller.h +1c570fee541d5cd778b459e1f0203310b622ffec824fee72a1fb5f8fc8c0c8b4 Controllers/GigabyteAorusCPUCoolerController/GigabyteAorusCPUCoolerControllerDetect.cpp +9d6a8adb5e2673d31a6904ca87fb9cf662db4a2b9f758b2e81000b09049d27c0 Controllers/GigabyteAorusCPUCoolerController/RGBController_AorusATC800.cpp +9b52fa6980ed054477d05f2eb201b59739679f2c7b03c789871192ac25c16355 Controllers/GigabyteAorusCPUCoolerController/RGBController_AorusATC800.h +3691a5f03e8fe3bfa8a8bbdce5fe0fd790c7c62b5d861bfa4452bc478c386cb5 Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopController.cpp +55cebdad42723b2d21a7ad80c918093d522a3bde3c5138e500f03b1b518e2625 Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopController.h +089a9a5880c1eb3d3b1b2102bce5c88c6921464d407a57943c54160ac2b2daac Controllers/GigabyteAorusLaptopController/GigabyteAorusLaptopControllerDetect.cpp +9ca8887c9023d8660b527eecb6f7f293fa6da828269e8554ac964a5f2c1ae486 Controllers/GigabyteAorusLaptopController/RGBController_GigabyteAorusLaptop.cpp +2acd76f5205d77b9cf127138bfa2fc0578687a3dbad0ef055924fea146ac7bdd Controllers/GigabyteAorusLaptopController/RGBController_GigabyteAorusLaptop.h +d3789fe9e3a21223779774e61d689f6e037d5a4ce1d97487653eef6f74ad3eab Controllers/GigabyteAorusMouseController/GigabyteAorusMouseController.cpp +34ebccda7b2a1b8eb641f9adc07bfb5843124c5bbfe348de3a149e8f2b7d907b Controllers/GigabyteAorusMouseController/GigabyteAorusMouseController.h +794d574a0c99ff99ed1ffd5a5e532aa072840647a3a217b56c9427ffeb7a252e Controllers/GigabyteAorusMouseController/GigabyteAorusMouseControllerDetect.cpp +f2338ed3d941b003b6069928bd8087bdac30b3bbd9a17f3fa40a901a10f2f759 Controllers/GigabyteAorusMouseController/RGBController_GigabyteAorusMouse.cpp +7c4405dd35afd18d4d095bc127b588285459cb14aeabf38a67ea1e97a978f871 Controllers/GigabyteAorusMouseController/RGBController_GigabyteAorusMouse.h +1a229ac34f113d07420e0c8714c9777b12178fcaf65d1bd24e2957561890a30a Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseController.cpp +d4b7f1798aab81079aa0216b60a00ee0a810d82079695ee3ff75033f324d343a Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseController.h +8951ff88a501850b162569cda02f56f81d9f8a9245ae6c5098a27bc7d9db1749 Controllers/GigabyteAorusPCCaseController/GigabyteAorusPCCaseControllerDetect.cpp +2546e3c247905b585e76781b37d5d8e80f9347f85e7cda7f3cd6a3eec93ac192 Controllers/GigabyteAorusPCCaseController/RGBController_GigabyteAorusPCCase.cpp +8e61f73d6b753b6604fac69a943fa938c9a197176620f06d629ed3034daa8dfd Controllers/GigabyteAorusPCCaseController/RGBController_GigabyteAorusPCCase.h +640649a0efac3efc117fcecbcb62df7c9c17c04cc05a51c015069118d3415dbd Controllers/GigabyteCastor3Controller/GigabyteCastor3Controller.cpp +5b2e70ae72c64087186d71bc48ac4400bc8d64c5dbb901d6164dd00b4247377d Controllers/GigabyteCastor3Controller/GigabyteCastor3Controller.h +f06fbfed4664eeb17efe335a368a36921898a63d43c1ab33c061fedff7752895 Controllers/GigabyteCastor3Controller/GigabyteCastor3ControllerDetect.cpp +743b9c13f0afa230569d82772106ec3a048ee36adcc415da5f8d8930b3bb08be Controllers/GigabyteCastor3Controller/RGBController_GigabyteCastor3.cpp +b72be566af1c2ada517d5ddfded34520371096059bf16dab25222444513f771a Controllers/GigabyteCastor3Controller/RGBController_GigabyteCastor3.h +08a4374fdc3b8440f6788e0eb609284407eedfd74a931a966d2da21a497f5cbb Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUController.cpp +740627cc50371dcd3ac52fa1d5bcb816c1e1450b4bc2d5d154df64788b438056 Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUController.h +db6888c9d4add3392cf1864cc48674de6807184478091be0f648a792d5f9bacc Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUControllerDetect.cpp +3130098ae2338f21816e1e94f3aa52067569ed6596bdbce0f757bb0931b5f247 Controllers/GigabyteRGBFusion2BlackwellGPUController/GigabyteRGBFusion2BlackwellGPUDefinitions.h +42e2dbfaa7b1256f4006f68c5bdee4df85be9ce4f4058113784f4de6b63fb15a Controllers/GigabyteRGBFusion2BlackwellGPUController/RGBController_GigabyteRGBFusion2BlackwellGPU.cpp +0e6c4e73ae7bf70a17386bc686d88c71be175655db535dfb725ada7c527e532e Controllers/GigabyteRGBFusion2BlackwellGPUController/RGBController_GigabyteRGBFusion2BlackwellGPU.h +fccda68eeac8b38d1bfb221d288affe4181b6fef61ba1196ad886f00fd8fdb41 Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMController.cpp +3aa66bd42f6aece770f43844823a8bb283567d75e243b4b031385606590ea592 Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMController.h +ed37b0ec430f7c7deb0006c6ce15709a89bf8b046fa430741750f22e55903fd8 Controllers/GigabyteRGBFusion2DRAMController/GigabyteRGBFusion2DRAMControllerDetect.cpp +ea690f6ffa60727fe07c7063d09312c11e67efc420237c36bb3e46ef50945a0f Controllers/GigabyteRGBFusion2DRAMController/RGBController_GigabyteRGBFusion2DRAM.cpp +f067e2f87edc9bcf3fe26fc5d94f2844bf3cbec2c3e698cd1e25928507ae6d52 Controllers/GigabyteRGBFusion2DRAMController/RGBController_GigabyteRGBFusion2DRAM.h +bc547611938ea13ef69e64d945f3d8bbb1541ef6de1c07b639dcc5f179e91dac Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUController.cpp +b81b56cc53fd1d54c36beb67c41b7160113c2e76d21376f24c41cec4f6d4b142 Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUController.h +e6b43e7957229b9b7a77edf9afbf01471fcff0d56360a4d0ae26618206da842c Controllers/GigabyteRGBFusion2GPUController/GigabyteRGBFusion2GPUControllerDetect.cpp +451c9430c34d1f684095f4eec40d047768fee3b560f90a15c911a74bf442466a Controllers/GigabyteRGBFusion2GPUController/RGBController_GigabyteRGBFusion2GPU.cpp +e6ad66873261cfbd76b3e7bc390837154951ab85457662b9d145873bf2b27400 Controllers/GigabyteRGBFusion2GPUController/RGBController_GigabyteRGBFusion2GPU.h +1daa187b6f823d45d6a08f2fb84a57a13758ed72b25ccb8fb09f0072d1bf7868 Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusController.cpp +02894ea4471872f96b0d40de5457221e6f8310ecc53cc347114169f5a06da253 Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusController.h +aaad0660afdac0a4e434fb37b0572cd04b097ffb252534c618150d273f3930db Controllers/GigabyteRGBFusion2SMBusController/GigabyteRGBFusion2SMBusControllerDetect.cpp +ac0453ddb5ed2a60fd33e922cb88db841d221888a9dd660a29e3c3745c77ba82 Controllers/GigabyteRGBFusion2SMBusController/RGBController_GigabyteRGBFusion2SMBus.cpp +b4f83c129680cb2cdd67d158208f0cdbdf851a7a503d8d0c4c4432b091d9fe07 Controllers/GigabyteRGBFusion2SMBusController/RGBController_GigabyteRGBFusion2SMBus.h +30cd2a96855c9b88daf818e7b41f71620753d447c23ba1444416d906686b0123 Controllers/GigabyteRGBFusion2USBController/GigabyteFusion2USB_Devices.cpp +dc1e499b9e086f3c42355913723f619b54e384fca96d8d3247cde7806baa2640 Controllers/GigabyteRGBFusion2USBController/GigabyteFusion2USB_Devices.h +106b27dda5cb6075e9da61034a59ffc42818a13c3d3225f6251578530a9b0853 Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBController.cpp +fbc3fd546538cf948aafe83df3e54ee2ebe9a7fbe731265c1b47dadd6148a28b Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBController.h +4756469eda808d8d055cb83b5b5574456b3c80c591efafe866faf4f935021c40 Controllers/GigabyteRGBFusion2USBController/GigabyteRGBFusion2USBControllerDetect.cpp +6a052c246e41b597419c9713cc6709f66e3518a8ce519b777723017f7f5130a5 Controllers/GigabyteRGBFusion2USBController/RGBController_GigabyteRGBFusion2USB.cpp +68d981e235726bf630f8859a532a4ccfa9e5c5b1c98a4c6c0d72be624a017e70 Controllers/GigabyteRGBFusion2USBController/RGBController_GigabyteRGBFusion2USB.h +f7be0375555866c3db4f64f09997335239a6ac7755e054f522a60f2cee04133a Controllers/GigabyteRGBFusionController/GigabyteRGBFusionController.cpp +9d15895fa8521466d2344c98253e9382e1feed9919d42aaa294f0e5dbea250f2 Controllers/GigabyteRGBFusionController/GigabyteRGBFusionController.h +19654312c646aac299f3dd02fa6789ac87a1d26598c488dda00e6843372e6983 Controllers/GigabyteRGBFusionController/GigabyteRGBFusionControllerDetect.cpp +45d7cfc1e17a9ce0fa13d0d0de6919a4e0281be697f20fcfc6815f00567228d0 Controllers/GigabyteRGBFusionController/RGBController_GigabyteRGBFusion.cpp +77ec084e22a3a1127f166537794aba4a12030fe36484edae0533ceba9fac4910 Controllers/GigabyteRGBFusionController/RGBController_GigabyteRGBFusion.h +eba1572c4648bbeea6b8cf42d373a80aa6cd6248b494e1fe9c8f5d2f2d974a83 Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUController.cpp +5875d23bb15bd6450d05f809213ce42d8cc7b5c8779a02a242dcd70314e86582 Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUController.h +1c6a9ea32145f13cdf5ff860d91ce71b944c51746f2562c07d8afe2d6a60a830 Controllers/GigabyteRGBFusionGPUController/GigabyteRGBFusionGPUControllerDetect.cpp +c24182983da4d3e453bf3a42629faa1b4785137ca159bdcee78fa5886de2f6a4 Controllers/GigabyteRGBFusionGPUController/RGBController_GigabyteRGBFusionGPU.cpp +44faa7dd86b98c3328e98f222bcc3e9caca9067899e01f323fab0e17907291d9 Controllers/GigabyteRGBFusionGPUController/RGBController_GigabyteRGBFusionGPU.h +6780dd92d85b3f45d1bdd565513bfd8f9948b6c36b3e892273bf40b0331bfa06 Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBController.cpp +719c62fff82ccd54793c6c977fb0983d3f5d6b1e0d2b69e4f32246c24daf8590 Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBController.h +60c8af9eeb9001ffb3246dcdae6c5479317adc2a2777fb9444272f52265d46b9 Controllers/GigabyteSuperIORGBController/GigabyteSuperIORGBControllerDetect.cpp +7834e938526bf3cd0ec8a9564411d42f1539fb0573cccfb178be8346ea99a9b8 Controllers/GigabyteSuperIORGBController/RGBController_GigabyteSuperIORGB.cpp +25c9c20c29a2270aa2e9c7316f6ed7afeac2cfabc04ec7638207f064fddf62e1 Controllers/GigabyteSuperIORGBController/RGBController_GigabyteSuperIORGB.h +3a001ae80f506203336e9b8a902f52870b1078445ce2089209f0ce522ea15b39 Controllers/GoveeController/GoveeController.cpp +5a6aebd6c167e2f19f0c1b228df7c61aa0c2f7de1d3840244fd3d5894ca4122b Controllers/GoveeController/GoveeController.h +b6d81b77a131db616e65417b6080a80c61f21d8bf475776cfee907457d76bc34 Controllers/GoveeController/GoveeControllerDetect.cpp +af313d65d7a2f6ae61d4c385e989892094bbad3053766c8585917048c9e66389 Controllers/GoveeController/RGBController_Govee.cpp +d430b3accc2292042c8089acdadcf87b96794f1e9ba47008f84d7601c1b89947 Controllers/GoveeController/RGBController_Govee.h +ab69609c7666e99e8417b1635336003d785766da610f12d6aaedf3cfc095f3d8 Controllers/GoveeController/base64.hpp +37737524d6a2a6bb08de3c0dfb77bc02c0c0a17f8cd5f7975cad0624f23caf77 Controllers/HPOmen30LController/HPOmen30LController.cpp +6305eac07ee234138c58e2f322c0901103e8307cfeae6d5125254fcdf50abfd7 Controllers/HPOmen30LController/HPOmen30LController.h +06ae8722ec66d025b5161982c004efad938f2413cc38b323c0f0a213e074bc1c Controllers/HPOmen30LController/HPOmen30LControllerDetect.cpp +df81335dfb16e4f4c4d8c0257bfff1dfc69543a8b2bf5a2b775a0348bc5a9f5d Controllers/HPOmen30LController/RGBController_HPOmen30L.cpp +ad35ff9b3c8cfb17970b2a3ca3ded0f70ebde21afd516985202131369f78d19e Controllers/HPOmen30LController/RGBController_HPOmen30L.h +f4117c946186e2f72f2c177ca95369cde582f995c7418cbe2d9fa39aa557088c Controllers/HPOmenLaptopController/HPOmenLaptopController_Windows.cpp +89003ab0f16f9a842d4e72f56bb286672491cf3a7a5509cf75f1c4492f7b0686 Controllers/HPOmenLaptopController/HPOmenLaptopController_Windows.h +0684851c5fdb69cbc994fc7388ae0aba1de299affeff786b9f4a5bcde43b15d9 Controllers/HPOmenLaptopController/HPOmenLaptopWMIDetect_Windows.cpp +6bdb7ab88134bedf747706396cde804fa356e49a280949997770e030bba9375f Controllers/HPOmenLaptopController/RGBController_HPOmenLaptopWMI_Windows.cpp +3d5046fb7b4d32c3185036d4c0b9266e66fe0525431f16ed51f597f3cfbcb39c Controllers/HPOmenLaptopController/RGBController_HPOmenLaptopWMI_Windows.h +e8b7b6560b41061dc93b8a7a0d877d88534af76d93c6d768810aea99accbeb6e Controllers/HYTEKeyboardController/HYTEKeyboardController.cpp +4cacc2587fde220ebdbb5b612ba6162133808fd6bfed93b7fb0c3a32a282bc9f Controllers/HYTEKeyboardController/HYTEKeyboardController.h +90ba5c6233bfee12955005b807d3934e431c835d1fc05269b2a2f1f9d45c224a Controllers/HYTEKeyboardController/HYTEKeyboardControllerDetect.cpp +118afa0d2682fd8214313323ac5ca97b7a2f8ca0423cfc680f7827e649834f80 Controllers/HYTEKeyboardController/RGBController_HYTEKeyboard.cpp +d6e4308134a919e94d1d1de836681a975b4a57adff2a7e2e8fca36d544c4faef Controllers/HYTEKeyboardController/RGBController_HYTEKeyboard.h +0652f0f3a800495292229602814f8f9f477c55691bacc889b176f85fcc30c0c3 Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematControllerDetect_FreeBSD_Linux.cpp +c60087be3eb3da14ede82fa58d1d9bc642003b7cf89865a4abeb9c776c548d3d Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematController_FreeBSD_Linux.cpp +56a886b18bac6ec8b9f50155de5edb3230f8aedfb7e5599dbaadd72985fef6a7 Controllers/HYTEMousematController/HYTEMousematController_FreeBSD_Linux/HYTEMousematController_FreeBSD_Linux.h +6a2bf33151678fced0d553e86165d92644b5406c72dd927aa9fc1657784238bf Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematControllerDetect_Windows_MacOS.cpp +2081923680310204d3249e4932d1147561f6b040596b9ec63bbdbfd4b5dde506 Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematController_Windows_MacOS.cpp +c7d31c9a1c7393f05e67d88654053eab6d04d847a5b68bc99ad48e8e2fadcbb8 Controllers/HYTEMousematController/HYTEMousematController_Windows_MacOS/HYTEMousematController_Windows_MacOS.h +87345c872a2f272758f25a01a31cc2eae6217e1642033d8663c33bd692da8347 Controllers/HYTEMousematController/RGBController_HYTEMousemat.cpp +89f6707aa896e3b843130f8af3fd66dc2222a0a92b1fba4eb58bbc15c7321543 Controllers/HYTEMousematController/RGBController_HYTEMousemat.h +5a1c5bc34aa9f642686a39dbf1d5ac540f94c60dd830d340215ba8b55e758041 Controllers/HYTENexusController/HYTENexusController.cpp +343ed3a8daf32413209c771a7657bf0b0465629134cd9f20cb2ec4bbd0101b0b Controllers/HYTENexusController/HYTENexusController.h +5c7ad15d73d5b5d2bf7471a1917f8aa7509ad880c5ee8943cd9b028020756ea5 Controllers/HYTENexusController/HYTENexusControllerDetect.cpp +54199f95827c53b8690533416b07bedf54dc4fd2b0b48036e47505381f89b96a Controllers/HYTENexusController/RGBController_HYTENexus.cpp +9ee5ca4a14ad0d65818ecc619a55a5fa1b0332d5037e6ac314a8d842c4fd10b6 Controllers/HYTENexusController/RGBController_HYTENexus.h +7c597dd8eaa7c73a7182d4b54f4338759493fdb37994cdd03b3313a7917e89a9 Controllers/HoltekController/HoltekA070Controller/HoltekA070Controller.cpp +9196f7289be31c944dc42879f133f85afa5f2a7f6eceaa0a268711ee540dd3e9 Controllers/HoltekController/HoltekA070Controller/HoltekA070Controller.h +f4dd8b3c7f7b48149181d285eb63889b3eb7fdecebf9be65959c37c65fe83490 Controllers/HoltekController/HoltekA070Controller/RGBController_HoltekA070.cpp +fe001714a9a35c215bcb7e755268b2e838e20763e862bc1357d841e7590f15e1 Controllers/HoltekController/HoltekA070Controller/RGBController_HoltekA070.h +8f19a22737c3cb8fa4196ebbec445a584ff075cad27aa68adde48175b8cebef3 Controllers/HoltekController/HoltekA1FAController/HoltekA1FAController.cpp +f2ca8243261fb433210db3ac1ddc37c6cbfcb2ed73676fc9b4ec60827012e410 Controllers/HoltekController/HoltekA1FAController/HoltekA1FAController.h +a12c3354d61b464ad9d2ad1aec60bf06531b2409d5627b20860c5440349fb770 Controllers/HoltekController/HoltekA1FAController/RGBController_HoltekA1FA.cpp +1a7e5cfb0ae8adcf433edccdbf24cce69a7ad9e4f8ff4fee4c91d9abd3704e3e Controllers/HoltekController/HoltekA1FAController/RGBController_HoltekA1FA.h +019756b7247b10dd70f307bc6030569509ccf0cb7aaf9a9751fe17a233374420 Controllers/HoltekController/HoltekControllerDetect.cpp +b002a14edd138338197d11b09bde483eabfaee2c3cdaedb2e5c43397dc0336e4 Controllers/HyperXDRAMController/HyperXDRAMController.cpp +81782b714842e42255319f3943ab4ae0fa9f7a465baf2f0461e1e579af9bce64 Controllers/HyperXDRAMController/HyperXDRAMController.h +66f6d73363713c474d605bfe9f31efd360903b748e7edb2e1449bb967db294df Controllers/HyperXDRAMController/HyperXDRAMControllerDetect.cpp +15cde724182d21bba038b45fbe0180362b719c6cbe935cc8cc798194d70ea306 Controllers/HyperXDRAMController/RGBController_HyperXDRAM.cpp +d5ad4e94458d912e316f6bda71227ca1518e870695f7d7881ecc9d588b2b84d7 Controllers/HyperXDRAMController/RGBController_HyperXDRAM.h +871bf20c8bb7a05641407732425989d1cc32785cbf3deb2ef356ceece175019e Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.cpp +5626494f7db93784812543a86573071b2b32124bf4d6af7aa9489e7cbed22256 Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/HyperXAlloyElite2Controller.h +f5479b6d8ede745a8be293b69a2fea7f3864412999fd8001b2c0852ce1f8cfbc Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/RGBController_HyperXAlloyElite2.cpp +d82c7751354e0d3c65dad3a2096e13f94cca68bd807132abd1d2f1365821e729 Controllers/HyperXKeyboardController/HyperXAlloyElite2Controller/RGBController_HyperXAlloyElite2.h +8b3113a444172b185e47e16e21ef69afd73ccf90da078334343be8e79e32d560 Controllers/HyperXKeyboardController/HyperXAlloyEliteController/HyperXAlloyEliteController.cpp +47da4350a81aaf6f095bd9d871c43e52b257a645db9052a308b07cf5d52e33ab Controllers/HyperXKeyboardController/HyperXAlloyEliteController/HyperXAlloyEliteController.h +58eb6500c8a52d6b9458dcd2fbf105dfe823ba552a841fd64490a774b4e4e2ac Controllers/HyperXKeyboardController/HyperXAlloyEliteController/RGBController_HyperXAlloyElite.cpp +9cfb710ec025ef4122739030e4ea095ee6875a36f7482b278a1bcb931c4617e1 Controllers/HyperXKeyboardController/HyperXAlloyEliteController/RGBController_HyperXAlloyElite.h +9bae93804d7e39577cc5592209abd56cad4486e7d80180b55b773478f06d3f1b Controllers/HyperXKeyboardController/HyperXAlloyFPSController/HyperXAlloyFPSController.cpp +d93718d473e246d4ab6670205898b68cfeee050428c4d345f2c0412aca77620b Controllers/HyperXKeyboardController/HyperXAlloyFPSController/HyperXAlloyFPSController.h +84018e49370d72a5540215f53e8ab8f1dc19013eb57181801477fe9377b3c69a Controllers/HyperXKeyboardController/HyperXAlloyFPSController/RGBController_HyperXAlloyFPS.cpp +fec03e3efbe680da99de0e300d6bf59fd04a4b50edc682d5162bb92b03f28971 Controllers/HyperXKeyboardController/HyperXAlloyFPSController/RGBController_HyperXAlloyFPS.h +04526880b321d57bfd924d50e2a8c4659e163add2f7bfe21433eb419ec4067fc Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.cpp +813087c2cb94817a74436dd3d5cd2e095a5c7b676aa17bc791bd8ac570dab18e Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/HyperXAlloyOrigins60and65Controller.h +c7377aac7aecfffc58ba23aef8a5da4fe5a905e40556f28ab1ce5eb7f754c4d8 Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/RGBController_HyperXAlloyOrigins60and65.cpp +b22dc01dbd6ff339439bece6e75b64005af91ff9675ddd06919b9f99867d2858 Controllers/HyperXKeyboardController/HyperXAlloyOrigins60and65Controller/RGBController_HyperXAlloyOrigins60and65.h +fd637e10f9ae59a7b0adf845f90c4ba34425aec891f4421b3157d6527f3d8380 Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.cpp +bfcfa34453c42248f5b1eaa6212f2cc1ae3bb39ed2140a92c81c245e7929b18a Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/HyperXAlloyOriginsController.h +4099734a7e1f2eacfe024e639ae27bdaed6176e8e9b6dced7620b2d74e17418d Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/RGBController_HyperXAlloyOrigins.cpp +2a227e130aeb2f4efc356448ccbf84f579ad2f83e232bc2c775accf72572b5df Controllers/HyperXKeyboardController/HyperXAlloyOriginsController/RGBController_HyperXAlloyOrigins.h +a13d975e1a52f02318698fc3795b749a507d311ca4e994dee9f49b5765a75879 Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.cpp +890d50967a1c1f42afc5fd448676ce58c2b3a932919e61a04cfb177c70e84b46 Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/HyperXAlloyOriginsCoreController.h +f6e92e1f0d830e0f4a81dd7fbed8ad43a4855daa50a1061283f82a478ba9f5b9 Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/RGBController_HyperXAlloyOriginsCore.cpp +69f1039488405b9abd140bf691937473cb6c5944fea73c2e84bfc82ae94395dd Controllers/HyperXKeyboardController/HyperXAlloyOriginsCoreController/RGBController_HyperXAlloyOriginsCore.h +ec8bdb0e727520837309822d39d1773c2f6918b8eec73fc5ac67320d81dc9473 Controllers/HyperXKeyboardController/HyperXEve1800Controller/HyperXEve1800Controller.cpp +f1faabab4c2e40f1defff143ba6e52df0c84c3b9a1c72b22486a8ab13b144003 Controllers/HyperXKeyboardController/HyperXEve1800Controller/HyperXEve1800Controller.h +5fead5038e441a816c9791493806b3ffc3fce5a33aae277c3164c9227a6c1598 Controllers/HyperXKeyboardController/HyperXEve1800Controller/RGBController_HyperXEve1800.cpp +0d0370e4ac8dc80f2dc02bf08ed61cc30884ec5141e0c2986189c3e2760edd77 Controllers/HyperXKeyboardController/HyperXEve1800Controller/RGBController_HyperXEve1800.h +2604e70f17dd7d9756550b32a47740359af115bae960fe548e28b870deacad36 Controllers/HyperXKeyboardController/HyperXKeyboardControllerDetect.cpp +0259ecde7d82b84e2dbddc1389998ca668f5f4d1b224bc17beec6aceee6643ae Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.cpp +3d19daaa85bb035215ae7d9da2fae7822141b8c4325d1a69f9c14245226e57f0 Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/HyperXOrigins2_65Controller.h +23825ff95d38016fb1f7933909e3f0d94cbd80aecf93d760d823443404f19d85 Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/RGBController_HyperXOrigins2_65.cpp +87f22e733db61a0328b5b872b90561542ed0ffd7972574e17b48ba68f3f34a94 Controllers/HyperXKeyboardController/HyperXOrigins2_65Controller/RGBController_HyperXOrigins2_65.h +761f2f2cbe7f409137506bbae7170b58ce0c538debd3a27709023198911058a8 Controllers/HyperXMicrophoneController/HyperXMicrophoneController.cpp +1dfedc741349c80578ef093071d720bb2fbd214a6eb4057ef0c10eeb89aa0102 Controllers/HyperXMicrophoneController/HyperXMicrophoneController.h +8ae1a3391c6a420f192f87f5910038b8e30232434a8ea345eef86a407956d908 Controllers/HyperXMicrophoneController/HyperXMicrophoneControllerDetect.cpp +6fa59d3342817e422c9b16d3fa0d8a948eff51bf60fb6082bd1b4a2ada5541fc Controllers/HyperXMicrophoneController/RGBController_HyperXMicrophone.cpp +c177768c098829eea976fd7b417cf4386b34776dee171057e9797ec941640693 Controllers/HyperXMicrophoneController/RGBController_HyperXMicrophone.h +be732296f54728b9b2204d3113d9f687cb1b76afc2b52f101466e340b2ad0206 Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.cpp +fcb8338879a02847d88ebf59d4ea03240f029b3ce59df900a4edc6ab59745cca Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2Controller.h +466350f0b2f7f2fb8b8f69760709d467ffe90c90fe721428017c3ae6bc25989c Controllers/HyperXMicrophoneV2Controller/HyperXMicrophoneV2ControllerDetect.cpp +a59e775c02cfdb98fba8be85bd9b1e2a5feac477bc8f33dad17558c0e8e20cbd Controllers/HyperXMicrophoneV2Controller/RGBController_HyperXMicrophoneV2.cpp +0fb462033e898cf94ed9d7fe6f78d61f22f83545f59d8bdf7269c3ebeca619e4 Controllers/HyperXMicrophoneV2Controller/RGBController_HyperXMicrophoneV2.h +09d4b15347022d5399686e011f5902c0ea8e4985018d5c56c7067677a5c68d4d Controllers/HyperXMouseController/HyperXMouseControllerDetect.cpp +1a6b6bd17def030fbd4efa0ec620a9ac6dcbcaebcb6316f3cf458c45059d1ab1 Controllers/HyperXMouseController/HyperXPulsefireDartController/HyperXPulsefireDartController.cpp +43e67ce0ba2164a845d60c12fc1f8865d550126638413e9bf5d5dee32e8cc4e7 Controllers/HyperXMouseController/HyperXPulsefireDartController/HyperXPulsefireDartController.h +a25bc39e0fc4d0665b57cd2315cc3f5f4a8e985643125df1ba757e1f9a99537a Controllers/HyperXMouseController/HyperXPulsefireDartController/RGBController_HyperXPulsefireDart.cpp +a3a22baa94549a8772df1a78919c57cd112afeef40897d6bfd4476c65404dcc4 Controllers/HyperXMouseController/HyperXPulsefireDartController/RGBController_HyperXPulsefireDart.h +43d0c812d321076d294f3913a584f93243777cee1cba969a32db10a6c5ff1c5e Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.cpp +73dea9952a3da69ddcfdd57e7278f695830402e4104340c07954c3d6d47f0de0 Controllers/HyperXMouseController/HyperXPulsefireFPSProController/HyperXPulsefireFPSProController.h +7b372ed55d589769b15773fa8f606cd6c2713f4053fa3d404d801a77cea20f31 Controllers/HyperXMouseController/HyperXPulsefireFPSProController/RGBController_HyperXPulsefireFPSPro.cpp +2458c8ac2bff117f3e5212343a25d760970a779338ec66b45525b05c97d53591 Controllers/HyperXMouseController/HyperXPulsefireFPSProController/RGBController_HyperXPulsefireFPSPro.h +b50bd6e79009acc951ae33a070d887621d90a4e7c6c8b82d28a36b2bb83833e3 Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.cpp +ef80061770ee38d72afddb869fc42b96206040c1c7be781b84f8e9cfa7ed0e35 Controllers/HyperXMouseController/HyperXPulsefireHasteController/HyperXPulsefireHasteController.h +100222e890b2822fe3d98989c002d3a020c00973de71edaf9ae0a6ec2b160ec3 Controllers/HyperXMouseController/HyperXPulsefireHasteController/RGBController_HyperXPulsefireHaste.cpp +716f9070bfa3bdf299d411343012251ebb4aa03b1873dcfc3083a74d3a05f2a6 Controllers/HyperXMouseController/HyperXPulsefireHasteController/RGBController_HyperXPulsefireHaste.h +3cebf23b226b40577fb422c1455861e5baecb5ae8d9700a482120fd879966971 Controllers/HyperXMouseController/HyperXPulsefireRaidController/HyperXPulsefireRaidController.cpp +3bc14a6662a35e95b6c2e24cfff3391a79d807ab9d665ea5ba0d537a6e1e58f6 Controllers/HyperXMouseController/HyperXPulsefireRaidController/HyperXPulsefireRaidController.h +8fb027f25ec8b0f005d646aa9eb124b179b4e0b910b2f7c4101a1b1a8edf6db5 Controllers/HyperXMouseController/HyperXPulsefireRaidController/RGBController_HyperXPulsefireRaid.cpp +ae012d989e563cebb537f0ea5a0471e9d4dae12d125718c2e94a870d982776dd Controllers/HyperXMouseController/HyperXPulsefireRaidController/RGBController_HyperXPulsefireRaid.h +27f8d1e4543af282968e44038cd0ec30c4385361f60f9de227cfacd7c3d9ced2 Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.cpp +63fcbcb12010c2174badb68311ce3b2ce0357f08eea653d1302be26b0cda5a26 Controllers/HyperXMouseController/HyperXPulsefireSurgeController/HyperXPulsefireSurgeController.h +84d4f44b621ae295cc2569ae6581c0925de0382b9c8a56519c529111ca0dc0ea Controllers/HyperXMouseController/HyperXPulsefireSurgeController/RGBController_HyperXPulsefireSurge.cpp +b262faca7fc9df68697af016e56ecf96be07fd602d7d5b96c8ed0c0b2824b517 Controllers/HyperXMouseController/HyperXPulsefireSurgeController/RGBController_HyperXPulsefireSurge.h +780d9a56c707bf6a59f797a977b83a9602203c176603998aba145790e09bd212 Controllers/HyperXMousematController/HyperXMousematController.cpp +5c5a33426ab6d303539c176bc074b37326298c702d730640e0d5793f8548ee18 Controllers/HyperXMousematController/HyperXMousematController.h +f17cc618a614af4e17e8cadc94077e38547ccb474497e9a110b32cfdf9cb6fdb Controllers/HyperXMousematController/HyperXMousematControllerDetect.cpp +9f2f50459563070edf06a806b40fbf7e779ccc75745a08c3f06119129a8d818e Controllers/HyperXMousematController/RGBController_HyperXMousemat.cpp +44d6eec82b5999ebe54e7fa426c429b0d7efb46a0cbf27c090c74cf9e5bdc5ab Controllers/HyperXMousematController/RGBController_HyperXMousemat.h +60dd2b3837672a79fa26c30ea5ebd9ec5a2cda605a5855b0de715f6842f61d07 Controllers/InstantMouseController/InstantMouseController.cpp +50a69fa84c5bbb550a0eb3e5559a2ef88d247aa6ff9d72e32bc2b649e82c0dbe Controllers/InstantMouseController/InstantMouseController.h +a102f1b62c13d2774455278ea79a56ea7a7d6df5cfc910ba5321978b6d04e8fd Controllers/InstantMouseController/InstantMouseControllerDetect.cpp +d0fbb9bb1a14331d44992b563c04f839ba93e9c2ffdbaf3c27fc5341744c698a Controllers/InstantMouseController/InstantMouseDevices.h +ccfdbc7a81dc2276d145a99170c6c5973adef7d9988d7e5ecf3acecc252470ef Controllers/InstantMouseController/RGBController_InstantMouse.cpp +848229a6377b1c186780e8477122856d07f98f932205d84851593297be28140d Controllers/InstantMouseController/RGBController_InstantMouse.h +0ac19fa24dbc311cb83ba8755c19e5f9f491445e766701faff2a8e850ae8fcb2 Controllers/IntelArcA770LEController/IntelArcA770LEController.cpp +36d1f79e24b20781e388aca5824743c0bfb840c1d7532c3e11d35b0561bec4a2 Controllers/IntelArcA770LEController/IntelArcA770LEController.h +2df45a1142b0327715a2f5e88d0362496508085e76e3fdcf9cc8ee254d1bc24a Controllers/IntelArcA770LEController/IntelArcA770LEControllerDetect.cpp +ac15b2a2429a71bc68b20e0de63c04a1aeebd84106807c781c296f54469b12b4 Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.cpp +fed3a115ffc1afb8c7fa350b42700c1906a39795f02ad33a2df068fa6b56fbe6 Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.h +17aa230debdd59b733abf01db65b2cbba50131e1c9749e77b2379e546e54de53 Controllers/IonicoController/IonicoController.cpp +4a8a33a2b2311b5cbd10fc90bc4ba1b95f2cb8002fb5624ec446f5e96e45a246 Controllers/IonicoController/IonicoController.h +f468cd0ea7ed1e1547c0a370df62a8edbbba075eda6cc1d2371ac3a2b5fda526 Controllers/IonicoController/IonicoControllerDetect.cpp +dc54f4befed47323fbd75476abeca467a907cb221bccbf23d46dff9679d867b7 Controllers/IonicoController/RGBController_Ionico.cpp +6000f7dee68841c58eb080ef0bed5721c8810fb9995f36a088f952c381614881 Controllers/IonicoController/RGBController_Ionico.h +81e8f13fefadd451df300d1d228ebcd2e0fbfa4c8f2dd281439713a91a7e4637 Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.cpp +71db9bf16df2a37b391f0afc552dc82586351150a6e94654b4631abefdee2b73 Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBController.h +129ab83374054b5d95bfa66811b64e07c47b5cbb71535bf6f33214979462253f Controllers/JGINYUEInternalUSBController/JGINYUEInternalUSBControllerDetect.cpp +804921f1e10eaed2f2b5c179f3a9ea8fc458eccb59c7a23fd0899be5a2711fc8 Controllers/JGINYUEInternalUSBController/RGBController_JGINYUEInternalUSB.cpp +c8ef1938f01e4ef97caa963b4ae7ae154fa885f628fdb31089c8f4f76be398cd Controllers/JGINYUEInternalUSBController/RGBController_JGINYUEInternalUSB.h +7d46bcda423fe7cc919a3df8de2b0894fe1d163d89fb3155b92fbe8f16a2c7ff Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.cpp +0503f01aee4c90d2c7de04b45b6b869cba2dd5c76957876ef1fa89bfb1bcf40f Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2Controller.h +780ba71b34cc984dc1437503eb505910f35ea1de0b7547f3fcf7f5083802e909 Controllers/JGINYUEInternalUSBV2Controller/JGINYUEInternalUSBV2ControllerDetect.cpp +b41730a0979c29e202d4f9bfb3dbb8f874148f2329a2d3af1a206cbc25475a65 Controllers/JGINYUEInternalUSBV2Controller/RGBController_JGINYUEInternalUSBV2.cpp +a27149eb246de37e325d739df6dd4123c45f7cecec2aa4034e7550d68fc3fbde Controllers/JGINYUEInternalUSBV2Controller/RGBController_JGINYUEInternalUSBV2.h +024c322be75e7d9b26bf355e6940e13157eb74f98fba66e4aa3d33c28aabefda Controllers/KasaSmartController/KasaSmartController.cpp +759767879f2a2b351dcfacd03b0e03ec5091efb185ff1be55d98edbed7e967ee Controllers/KasaSmartController/KasaSmartController.h +e85ad869a010444486049faa091395c7e14e4d0041e54365446eb860a571b34e Controllers/KasaSmartController/KasaSmartControllerDetect.cpp +40f7d99520b4bc5cefa61601b3b803c1865920333350408ef1f041c40972d167 Controllers/KasaSmartController/RGBController_KasaSmart.cpp +d8a06fdf61f54dc3e8f5787a20b7ddd4df0e097bfb3ad7944c419b8476bd4dd5 Controllers/KasaSmartController/RGBController_KasaSmart.h +b6fb21ec53f468c9976a7cb11d2f5f702da23270163fba603cb3e7fbd6020328 Controllers/KeychronKeyboardController/KeychronKeyboardController.cpp +34976fbf87fccc78a91baa2b322610f5d53e9491a5e926755b240e43af46964d Controllers/KeychronKeyboardController/KeychronKeyboardController.h +82a7a8c659b4996b62175a58f6e8f66199769c10325b4066103d3ee526688997 Controllers/KeychronKeyboardController/KeychronKeyboardControllerDetect.cpp +0c7c8c43ad95aa96011f23dc33f4b9ece6859580169993ae10a703668d2d7553 Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.cpp +db68b5023264c975280360128c26c661233c690ab40e89db0188304f1c98c4d0 Controllers/KeychronKeyboardController/RGBController_KeychronKeyboard.h +699f96ecf6f0b3e9821c74bf6fa1538341c47c30e42cd438b44490445e64d75d Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.cpp +fa48a580744881d683b5c56d29378de0ace0d149ff249514b0c6883c0877a3e1 Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.h +ec3c1e689ebf839a5fd006edca7ca6bb3eca5da8180ce8ef7e4654de18965d9b Controllers/KingstonFuryDRAMController/KingstonFuryDRAMControllerDetect.cpp +73fef7908a344856eaf7be07f5aa747fd6dd1e1def8b429bc67b6b57b82daa83 Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.cpp +3bd68800fc68fb695d670226847f015f22647501627b382118dd335b890c38ff Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.h +15c1f80080fce2291b1a30282c1dfada0dc408e79112c0db4f3c95a4011b509d Controllers/LEDStripController/LEDStripController.cpp +9007d67a53feaf58b905ab26182bb07b3d271450f505e4aa1228767d7cba3a3d Controllers/LEDStripController/LEDStripController.h +87b66fb524738a21483d36af5e8dcf4a3fe08d0eacf72c7c2c85d90338617b2e Controllers/LEDStripController/LEDStripControllerDetect.cpp +b03cae2b5f5ec6be0cfb23e20a510660479970bded8eb0be06bb943818c74139 Controllers/LEDStripController/RGBController_LEDStrip.cpp +d8f1bd86a94fd9b4713fda8765553421e0517e908d037a9c09212443751c3574 Controllers/LEDStripController/RGBController_LEDStrip.h +9add386b247340fdc3e176dcd1b60879b4efc75ef2bb0b4004902dd46223523a Controllers/LGMonitorController/LGMonitorController.cpp +737f27df48804d36042d8ec4ace6d0452b3af617333b9877829cc123915bd369 Controllers/LGMonitorController/LGMonitorController.h +ab66234b774c091820817f2a7d76d1820cab73073d1f0d63b69b904a1ab821b5 Controllers/LGMonitorController/LGMonitorControllerDetect.cpp +b0e67033bdfd24b848512ce54c28e8a11dd3818b9cbef0b2c21517b251572349 Controllers/LGMonitorController/RGBController_LGMonitor.cpp +84bccd65b559ff8f0334f4c609592ce238eba801bc89cc63bf80fa8592a685f3 Controllers/LGMonitorController/RGBController_LGMonitor.h +32fcddfba2a900afbca2ea98e787ed89c2d9821fe6dff554bfc6e08b235bbb73 Controllers/LIFXController/LIFXController.cpp +6abf559292f574f1617c948279195ad3b590618a47d976efda1647e82dc19552 Controllers/LIFXController/LIFXController.h +7ad262e04afe5b0dbcbf5e90d228a57a63924c188772e29787d54ebb99820d0e Controllers/LIFXController/LIFXControllerDetect.cpp +4278c40ef2367609f21691120b16f827bf10f66547d6e6ba2cb7076e34ab8601 Controllers/LIFXController/RGBController_LIFX.cpp +79c45410d58313c9a2dc75826234ffc55e94c16eb7477a4a86d06cff14d505ef Controllers/LIFXController/RGBController_LIFX.h +e15242012cc7fdae665c83d074c2b99e9297f250752989c5713441ed657b5dc7 Controllers/LaviewTechnologyController/LaviewTechnologyController/LaviewTechnologyController.cpp +88b6f72a54fe2904bd287238e1f6d331aae1225712a0860b828ed0a59df62e40 Controllers/LaviewTechnologyController/LaviewTechnologyController/LaviewTechnologyController.h +0643e576dd23abf54d0e8b9a67223888e103dba1b39668e1701465e1a30cda00 Controllers/LaviewTechnologyController/LaviewTechnologyController/RGBController_LaviewTechnology.cpp +50f8f387702ba794bf3aa994b311a00724378e2519449a50cd28ef28247a13d4 Controllers/LaviewTechnologyController/LaviewTechnologyController/RGBController_LaviewTechnology.h +d4f4013e7b2bdd790f303c4ba3d876214588ee67a450f011f488e64c1d831d76 Controllers/LaviewTechnologyController/LaviewTechnologyDetector.cpp +d38ee428b2d400b6a3300cb663a028c7c853f2463febe3cedcecf82d6b52e46e Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseController.cpp +709ddf09921ed36a36e97ed2898c1c370dd488f894099b5be8f0a88b4552dd79 Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseController.h +75311df9884646b708250b933b7095fe73aa17684f21bcd122ff0c9a9c1b8240 Controllers/LegoDimensionsToypadBaseController/LegoDimensionsToypadBaseControllerDetect.cpp +531b4df4689a647e2174a9e8224cc1e109b801c6a837959df87d8028c9eecd34 Controllers/LegoDimensionsToypadBaseController/RGBController_LegoDimensionsToypadBase.cpp +7754a57d54372786dfa1f741a0894be233c4f7c98754af45ca1b1695b7f8a18a Controllers/LegoDimensionsToypadBaseController/RGBController_LegoDimensionsToypadBase.h +22f9358bc85ae80211ecfd750cb22828e77de931a445cbeb2a22a5d4ad447687 Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBController.cpp +d7043fcb82b61b713d4ec983b1534b37d3b0841439b37afd97979e99ade15ee1 Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBController.h +e3deef6c1b273a54b0b8db23bb056fb6913f26b6cf03006208ab9bea02e13b9f Controllers/LenovoControllers/Lenovo4ZoneUSBController/Lenovo4ZoneUSBControllerDetect.cpp +f49bb6ea1cc75b70556c42a6bfc0151457437894128647d4e183b1aaf81301e7 Controllers/LenovoControllers/Lenovo4ZoneUSBController/LenovoDevices4Zone.h +6640c02dd0b25dc44bafebbad6e8b55fb8c040f4e561bb153f32e4ec2cc4c501 Controllers/LenovoControllers/Lenovo4ZoneUSBController/RGBController_Lenovo4ZoneUSB.cpp +eea0f709136ea87c906867fb71eb285dff28a02209789d010e0167e10508abe9 Controllers/LenovoControllers/Lenovo4ZoneUSBController/RGBController_Lenovo4ZoneUSB.h +166bea051b5a90ddba475b273de2bdd1fea3ac7fbd469de883b1608eb452a88f Controllers/LenovoControllers/LenovoDevices.h +d6d21dcfdc44b4abddd687ab41e9747e08a0f449ae86aaa516d62fb8d2d476d0 Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510Controller.cpp +d9cbf6855c477f2f8e240793d92d234daaa95f44b1f5ad2fac86ccb0996ecc32 Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510Controller.h +96b84fd269d1c6b3454734f374ae1d87dba7e5f2bd6fd0507c3df3e6c2606150 Controllers/LenovoControllers/LenovoLegionK510Controller/LenovoK510ControllerDetect.cpp +b49a38275dc6a17987d2bb4e462ddbe0a257763fe1344d220f557f5b95d74dea Controllers/LenovoControllers/LenovoLegionK510Controller/RGBController_LenovoK510.cpp +52c12986aca064de75fd027fbaeb681e8e77f40c3f34639db9752c6db62820bb Controllers/LenovoControllers/LenovoLegionK510Controller/RGBController_LenovoK510.h +193c1bee21aea6da5388bb8480ed69471a442a80c2308aa235ae84e7ad9f8316 Controllers/LenovoControllers/LenovoM300Controller/LenovoM300Controller.cpp +cb3faf3be2fd588263a89f24409786e810c082d5faa1a6fb9739680711609d9e Controllers/LenovoControllers/LenovoM300Controller/LenovoM300Controller.h +d2fef059bf6c3399cd0d59711caf2739deb0642e2080800fcc1c64266cae13c1 Controllers/LenovoControllers/LenovoM300Controller/LenovoM300ControllerDetect.cpp +3ed8afb4f727ed959ba719aaf76a73e010ad3c5b6d045178f5b55561007163dd Controllers/LenovoControllers/LenovoM300Controller/RGBController_LenovoM300.cpp +8313dbf5c2da840c3504924b418140f7d6e1cdc747d54dec7a983f7148d72c28 Controllers/LenovoControllers/LenovoM300Controller/RGBController_LenovoM300.h +9875810e65cc443f50c938ae8cb25f4093d6888a276eaeaa7ad07fc3ec5a2342 Controllers/LenovoControllers/LenovoUSBController/LenovoUSBController.cpp +3342675ec1e0745201df92d9babfe30d60c38d912eac3267ae3aace5b4c9eeda Controllers/LenovoControllers/LenovoUSBController/LenovoUSBController.h +36792311329f9ef3bcd867ed93f050b439a1cab5d93dc4e1d227e8f3b2b5763c Controllers/LenovoControllers/LenovoUSBController/RGBController_LenovoUSB.cpp +b8a924ee5b22dbc5cdeeb6185647a3eda45e20903258891f9688b5cdd389fec6 Controllers/LenovoControllers/LenovoUSBController/RGBController_LenovoUSB.h +b996838387c15cfc7cfd08c00847b826c98b6d7ecc77b23dc125355926895c61 Controllers/LenovoControllers/LenovoUSBControllerDetect.cpp +3558c387dd45a017675f504ff561a986916f36a740c8163ff017632316cdb1c6 Controllers/LenovoControllers/LenovoUSBController_Gen7_8/LenovoUSBController_Gen7_8.cpp +cc628722069762cd6d47b91bcf140d58ef108429d0f385ed72c18d8ea448caa0 Controllers/LenovoControllers/LenovoUSBController_Gen7_8/LenovoUSBController_Gen7_8.h +b3e346b58e6efcdf3750449ec271622e3b14ad9cb94af75fb6b420baef27d19d Controllers/LenovoControllers/LenovoUSBController_Gen7_8/RGBController_Lenovo_Gen7_8.cpp +e7bdae89e9d8264fbaee7e8df7fa32ed1d5fa617b0991ee7306bc19655f1072a Controllers/LenovoControllers/LenovoUSBController_Gen7_8/RGBController_Lenovo_Gen7_8.h +f91f581b656872c035e720dd41efbc6a48300bc0332bbd1856adaaee7ad74518 Controllers/LenovoMotherboardController/LenovoMotherboardController.cpp +effcf0036fbef5379ea50a3ed2b6eca6d3ffd82bfc2bcd24bb1e2cbe3d020a62 Controllers/LenovoMotherboardController/LenovoMotherboardController.h +a96d6eef8f1eccb62dfe049ed34c3d1aedc7da3b38b168f5fd438a45abf31208 Controllers/LenovoMotherboardController/LenovoMotherboardControllerDetect.cpp +f615df0f600febdc84a994888fc51794de2c6847939c975517f3002d71f40ca2 Controllers/LenovoMotherboardController/RGBController_LenovoMotherboard.cpp +6bdc4834a48bfe1f63bdfa8e1ecf941dec45b8067b1eeb4c85d5a3edb10079f7 Controllers/LenovoMotherboardController/RGBController_LenovoMotherboard.h +f1d4fe3fc79cb33b86bccf4dc0add02b99ea36ee53700dcf54304bc26fe86bb8 Controllers/LexipMouseController/LexipMouseController.cpp +8c281874786082fb0f0d20da81769805719f0d583a14a3d9c4645560bd2436d9 Controllers/LexipMouseController/LexipMouseController.h +9600f8c1e7983d72d7a2206e05dfc58dce22d5a44b58299a8333daebcce279cc Controllers/LexipMouseController/LexipMouseControllerDetect.cpp +68a6f606cc719453d2f32c924472b3e421b53177da03040f18706948894df9cc Controllers/LexipMouseController/RGBController_LexipMouse.cpp +22214be73c6488ac64128ff8c24f2ce96ecbd32b8a51290abbda6e2b7b2ae991 Controllers/LexipMouseController/RGBController_LexipMouse.h +e2b2bc05e7a9908bf35fb25d98684f778eb6a1c1bf10e2ff322f6cf209ab3c60 Controllers/LianLiController/LianLiControllerDetect.cpp +4b9f5fe1b1f697e7782ad132ae2db5dbd02897fd11daa17bd461bef0fc27abea Controllers/LianLiController/LianLiGAIITrinityController/LianLiGAIITrinityController.cpp +59043f2554b9003be86eced8b6b350927512b50e0e5e15dc76063c99f9ff6553 Controllers/LianLiController/LianLiGAIITrinityController/LianLiGAIITrinityController.h +325e320c04f05f06171db8b698d9bd37a51dcab42423057691e3bc2ff1fb7f5b Controllers/LianLiController/LianLiGAIITrinityController/RGBController_LianLiGAIITrinity.cpp +b7e30f6d9008e878192b1068d3ee2ecd9a89e80559088fe5e9e7a15dd4ea2c06 Controllers/LianLiController/LianLiGAIITrinityController/RGBController_LianLiGAIITrinity.h +48f4b89ffac636597f6b52299b824f8046c0bf102ffa27e7842d9504e20766df Controllers/LianLiController/LianLiStrimerLConnectController/LianLiStrimerLConnectController.cpp +95a91a65b57cf3303c654745fa89b768da236df38fde73f24bd7fd052ebc4ce1 Controllers/LianLiController/LianLiStrimerLConnectController/LianLiStrimerLConnectController.h +dde5e08c8f0dc54d3340e1ccaa269e81597c1c94e74ecbd38b758995174db44e Controllers/LianLiController/LianLiStrimerLConnectController/RGBController_LianLiStrimerLConnect.cpp +9777d72db75884aa4e99aa06abb1e800bf4830e31aecdb17ed85890a973da271 Controllers/LianLiController/LianLiStrimerLConnectController/RGBController_LianLiStrimerLConnect.h +8b8c02d411d2f612b76a261fca35958f1e60ac2f90f3362152e8f6758baf2dd0 Controllers/LianLiController/LianLiUniHubALController/LianLiUniHubALController.cpp +b6214d7e010884b5d5f8721e8fec6685a6ab4522bfa188de32bcc524caf3fd54 Controllers/LianLiController/LianLiUniHubALController/LianLiUniHubALController.h +25fe4eb943f17143eb9c1fed40cf6e889c43577b1b9f03b646c5807897456abc Controllers/LianLiController/LianLiUniHubALController/RGBController_LianLiUniHubAL.cpp +016075cf52abbb384808fc7983f68a4a679ac2d0b03727762c5dc1bc1d7843ee Controllers/LianLiController/LianLiUniHubALController/RGBController_LianLiUniHubAL.h +59a723b55ef758e329ac424758730623e85570ab0c73ccd2c19a798eec1a373d Controllers/LianLiController/LianLiUniHubController/LianLiUniHubController.cpp +551fd79e15403d7dfde1d05a80626c64db4d181d3b2beabf8c408c34ce0753d3 Controllers/LianLiController/LianLiUniHubController/LianLiUniHubController.h +2f3e8939593a1e893409cef49dfa430adb8ba7ca11fe47532236e76b918d2de4 Controllers/LianLiController/LianLiUniHubController/RGBController_LianLiUniHub.cpp +d4eafa0205eed01955e826a6a66a6854514d6790d9a26a35e355448c57ec72db Controllers/LianLiController/LianLiUniHubController/RGBController_LianLiUniHub.h +301ab92cbc5a7993a391c2fcc35ec2a8d8e54657b32ff69667848a923484dcf9 Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.cpp +01a46d2469c416cf02b90a56703e9045f8606e953564f0881a0913d1c34952b6 Controllers/LianLiController/LianLiUniHubSLController/LianLiUniHubSLController.h +4acd330232a35f8b32b6a271bcdc58fb87f43095b8c925629cd4444c4d151c2e Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.cpp +bcf21a3df7d464844bed4ca217a9d5f56eb9c57d2f97c319bddb8710ff5fb95b Controllers/LianLiController/LianLiUniHubSLController/RGBController_LianLiUniHubSL.h +262f63e4ac93f9797db6f1cf8f3af51fbefc8d6419ff6b96efa8e39e75c0e7c3 Controllers/LianLiController/LianLiUniHubSLInfinityController/LianLiUniHubSLInfinityController.cpp +2421d93ebb99a28a195245830b16174617b8a59bee9d96be513d72ea5e64d6c0 Controllers/LianLiController/LianLiUniHubSLInfinityController/LianLiUniHubSLInfinityController.h +09b703ee139f57d38d0a24a4424e34df4e31c6ef8b60c0fc534e13e000ae7479 Controllers/LianLiController/LianLiUniHubSLInfinityController/RGBController_LianLiUniHubSLInfinity.cpp +6f978846c11096b4b26238984a468a2853320d11f6f4908dcc15671b3c2bc55c Controllers/LianLiController/LianLiUniHubSLInfinityController/RGBController_LianLiUniHubSLInfinity.h +8eac5d2eb8c0762cdbf2e187bca6b93a11576de54377461b20365bcd010f222c Controllers/LianLiController/LianLiUniHubSLV2Controller/LianLiUniHubSLV2Controller.cpp +8b0c3f505a9cca88feae14826964b94c40facd88c182d5069fe6e0bf3c811036 Controllers/LianLiController/LianLiUniHubSLV2Controller/LianLiUniHubSLV2Controller.h +a0d76e7e3f79597f238c3b3cada28623460e15c8478a082c5c6dd2ed852b4caa Controllers/LianLiController/LianLiUniHubSLV2Controller/RGBController_LianLiUniHubSLV2.cpp +04a3f056fdbf499f6cb5b6a321acd5d0a259defc98c68fca3ef2f06cef57304b Controllers/LianLiController/LianLiUniHubSLV2Controller/RGBController_LianLiUniHubSLV2.h +8f53239cbbf47d96b7e833a31503a28ca76b965c5f561965941fd8ae6caf38fc Controllers/LianLiController/LianLiUniHub_AL10Controller/LianLiUniHub_AL10Controller.cpp +b253d61ea33d2b30165630a495c264d43c1683176be6442f8f97f0eb779b91b7 Controllers/LianLiController/LianLiUniHub_AL10Controller/LianLiUniHub_AL10Controller.h +f85ae2d9d6fc121e0c5e6f8c3d64002928adecdec157740917ae287808acbf15 Controllers/LianLiController/LianLiUniHub_AL10Controller/RGBController_LianLiUniHub_AL10.cpp +0e107792cf068d3a02d9d8206473461c6ad17d6b2d2bc3d0b9079c1b93e641d2 Controllers/LianLiController/LianLiUniHub_AL10Controller/RGBController_LianLiUniHub_AL10.h +d9498afbd59b847434bb86d22913425b8f9fae1a4867ff4cd474ea1088170996 Controllers/LianLiController/LianLiUniversalScreenController/LianLiUniversalScreenController.cpp +1b444e02d754e8dc81a55383e3e0d88b8dabcc91b4b5954a039ee224e556303c Controllers/LianLiController/LianLiUniversalScreenController/LianLiUniversalScreenController.h +5eb6027c2a53d42329a82d2b362885113324aa161b01186accc95022f362e99c Controllers/LianLiController/LianLiUniversalScreenController/RGBController_LianLiUniversalScreen.cpp +f3f530a4624e84fa8d6d7b1682e68b15f368d09f39e8bfea5eafff8ada83cc6f Controllers/LianLiController/LianLiUniversalScreenController/RGBController_LianLiUniversalScreen.h +49acf56f06a78a6a1a6304f03609b80cb4005cd241d82918d49479128fac5f39 Controllers/LightSaltController/LightSaltController.cpp +1714b96c0fe9aa42a1c41fb2e0a17544c09d3bb937654c4e8f3be02b3428620f Controllers/LightSaltController/LightSaltController.h +6705d1d32ed37ed25a0c86a7c418a187c4d1118d6dfb7d417ed1c8b1d5a449a9 Controllers/LightSaltController/LightSaltControllerDetect.cpp +b072f57cb406eef2d609a2485f42da81793b6adab031517b3c49938bc2b2177d Controllers/LightSaltController/RGBController_LightSalt.cpp +0c54fe66e9c84ed654a5b6c039d37cdf1c8213032dde2d6017106b303d825895 Controllers/LightSaltController/RGBController_LightSalt.h +1b17553d23f29e3ff0788b51f6dea3843de3005d91b93375b27e2a1f3eb4718c Controllers/LightSaltController/RGBController_LightSaltKeyboard.cpp +bd664d835ee51218feccfd799de6ff075efa77aa84fd13ba92a7bc7ac1adb9c7 Controllers/LightSaltController/RGBController_LightSaltKeyboard.h +d420d484fbfb3425977b4e2f44099b6473782a7ce0a6b264d68cb7148b529e10 Controllers/LightSaltController/RGBController_LightSaltKeypad.cpp +f17cc020cf64e865a2c56dd4ca5572cec490539b959cf8c8361907c4b3f7c2fd Controllers/LightSaltController/RGBController_LightSaltKeypad.h +5b200cc970e3b4e5aa241145f1b861bf84ad7a068d669f0c270d39ffffcdf197 Controllers/LinuxLEDController/LinuxLEDControllerDetect_Linux.cpp +c657dfb7186f56ef91adaec84f7f86a8b3e71f00e0def43f8095836acb6c09ef Controllers/LinuxLEDController/LinuxLEDController_Linux.cpp +4d5e31b8c92e50972b3d50310ab74f042283bd224e72d13e77ade14a98fdbe71 Controllers/LinuxLEDController/LinuxLEDController_Linux.h +a56225fa1887ee713bf9294752e674c1226b61477a0bfac1abe1d3122dbe3216 Controllers/LinuxLEDController/RGBController_LinuxLED_Linux.cpp +abf36163803fa2954f572d3376f5a61e9be6fdabd1ecb7350a700f036340c1ed Controllers/LinuxLEDController/RGBController_LinuxLED_Linux.h +f851eb655d1700eb86a8b06c05f47637bd8f15f34cf24ec5da0bc1192fd7b244 Controllers/LogitechController/LogitechControllerDetect.cpp +593ed5b5d2a037f834e277d297acad95ed5f0ee7a604e9ef8099404b06cc61b0 Controllers/LogitechController/LogitechG203LController/LogitechG203LController.cpp +d3fe9c3e6673c849bdd98c3d4a7d83650399d51e32b073933e33cc88538237a3 Controllers/LogitechController/LogitechG203LController/LogitechG203LController.h +108fdbf688f4950851ab54e3dfa542b390e2d1cbfa49f5ba486e3799a3e498ec Controllers/LogitechController/LogitechG203LController/RGBController_LogitechG203L.cpp +e94fbedf7187038d4c878c4ffec09322c1ee08b3d3492f34898df091c586f891 Controllers/LogitechController/LogitechG203LController/RGBController_LogitechG203L.h +5d82eda4072b270e512a915384cb6e313f552363ee1260c522eb2203728373f1 Controllers/LogitechController/LogitechG213Controller/LogitechG213Controller.cpp +e0fc4fa85fb8db77f619d14137ae5d6bd9a362a52764befb8b683f2859a1189f Controllers/LogitechController/LogitechG213Controller/LogitechG213Controller.h +da1d4ff3ed2cad8514d107c235cb3b095e266cce8feedfed072b9fc80b94c5b5 Controllers/LogitechController/LogitechG213Controller/RGBController_LogitechG213.cpp +51d808dffd906337e1a02f69f64b6575eab5c0b6ef10e4262c137b593eff2b94 Controllers/LogitechController/LogitechG213Controller/RGBController_LogitechG213.h +920f262dd2eab08c2641822889b689ad4e3ceb19003cbc7c66a850ce85c11876 Controllers/LogitechController/LogitechG560Controller/LogitechG560Controller.cpp +db349328d265e87ce7ee3af9455547565c3f96297827e11bdd787ad3215d90a8 Controllers/LogitechController/LogitechG560Controller/LogitechG560Controller.h +0d05c0447e5413458c2afb55b17d440b33a0c97021a043952bbd8dd1ad22d5a5 Controllers/LogitechController/LogitechG560Controller/RGBController_LogitechG560.cpp +32784194fb17f4d80b0139cd5bee348b82decd000d742edcc61fe3eb97ef851e Controllers/LogitechController/LogitechG560Controller/RGBController_LogitechG560.h +85f892d103db8533bf0e495f21eb5d52fdb9ac854f2c9b1075e90c312db523da Controllers/LogitechController/LogitechG600Controller/LogitechG600Controller.cpp +ee831eb862264094c9fecf51e0a60838bafd2e74bc788299011a1953d91b9d3e Controllers/LogitechController/LogitechG600Controller/LogitechG600Controller.h +bcfb8b9ec0eee6b1632117102bd1f23a07c216a4b7a7568c93224609e38cd65e Controllers/LogitechController/LogitechG600Controller/RGBController_LogitechG600.cpp +bd4bd1d49b34559f17df54d562ce73c53cde6e4fb7dcefcd3b1eaada154374a9 Controllers/LogitechController/LogitechG600Controller/RGBController_LogitechG600.h +7b1c0c566079132b801a19b82f75bf6d9e3cb3b72b02bbf0e648a174945322b2 Controllers/LogitechController/LogitechG810Controller/LogitechG810Controller.cpp +68e5a0f18e5cae272d32954d8c59b844193e2cde7c44b97d7ba2a1faea77a593 Controllers/LogitechController/LogitechG810Controller/LogitechG810Controller.h +2fb1511bb0c86a0e33d3def37b9d2f6a475d6b996a25da42923d4913ba08c0b8 Controllers/LogitechController/LogitechG810Controller/RGBController_LogitechG810.cpp +faf40e5fd2696823c7ca2d5c365254e33703d79a979d65595974c006b9d82d99 Controllers/LogitechController/LogitechG810Controller/RGBController_LogitechG810.h +be6c0e8ff9f6efedf1d1e1b64decee21bc0a70b1e1a7a70a849ed3dac7b0f448 Controllers/LogitechController/LogitechG815Controller/LogitechG815Controller.cpp +d8370fb27f596d8b666a9a792c706bf5ee754c363dae2901c0305360445df329 Controllers/LogitechController/LogitechG815Controller/LogitechG815Controller.h +92eaf187dd365d0b3a161ebc906802a3ab4bd57ba10a497fbad229519715c994 Controllers/LogitechController/LogitechG815Controller/RGBController_LogitechG815.cpp +05d51b53d791c43e67502d8fb1501ed3e2648959b2dbd2592a71d0f6acf30d37 Controllers/LogitechController/LogitechG815Controller/RGBController_LogitechG815.h +8d28ce0e8a677e60e010aa731baa08614ae82f9a96b5018f6a20134272751d53 Controllers/LogitechController/LogitechG910Controller/LogitechG910Controller.cpp +7578f6412cc3f806069cb1172aeadfb19010eb2ff767aa4888c68c65fdb2d59c Controllers/LogitechController/LogitechG910Controller/LogitechG910Controller.h +d08b0f323c74b729f7af8c041fabe750cc8894203f6bac538eda2e182fc01cbf Controllers/LogitechController/LogitechG910Controller/RGBController_LogitechG910.cpp +86003e7e4ff17892a2eca4cf6484292d449dc00d2455e2e41b0b710a2842102a Controllers/LogitechController/LogitechG910Controller/RGBController_LogitechG910.h +3069caf509b841c337213fa04fdecb86888358c4dfad418cf1626ece818a3cd3 Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.cpp +b7a22ed5f9218dcc97f13f6c8437b10a5200f130234225c432f5454fc57208e8 Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.h +732b332166650121ddfe325d856a5fac7d261a4bd25f78f528ccd4ea8e691268 Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.cpp +97196b3286c3976235509ef1eda572d8386bda8070e72abce14822d1b7b5dec3 Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.h +92fe52c5b928e175fcbfa8470d5171758d5a777594983ada9d353a545d46e23d Controllers/LogitechController/LogitechG933Controller/LogitechG933Controller.cpp +c8b06eb13b953cdef233fe0cea38f5e4b20f4c991b8afa677d6deddd47c873f2 Controllers/LogitechController/LogitechG933Controller/LogitechG933Controller.h +f6e179e04c78fb62ee2d2dff2d935ffcdcec061e7d492d0a1477ba10a16dc4bd Controllers/LogitechController/LogitechG933Controller/RGBController_LogitechG933.cpp +565b85f3b3218e27a9da97f432ff92c1ea8f54dae4cb5ed9de8dd68f74f146e8 Controllers/LogitechController/LogitechG933Controller/RGBController_LogitechG933.h +4aba6eb8d5c0b91d7ef06198671be5cb864666d0297d766372f07dbdd69271d6 Controllers/LogitechController/LogitechGLightsyncController/LogitechGLightsyncController.cpp +694ef31da9485cf7aa8ddfce43aef3d18e4af0cee352d65bc30adc2d66006215 Controllers/LogitechController/LogitechGLightsyncController/LogitechGLightsyncController.h +ff09f693ebeccb64f55f064e758d4c36b66aa3dfcb6bf970ed06e30b004216a0 Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync.cpp +cbaa11dc3a4801238016f171a26bca85152d5130d814bc076a4ea9b66d416b21 Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync.h +e4d39e6e1b2b7d12ea9385f7c05cf4e56f764c87b7977180e3c0cf7c5fada4d7 Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync1zone.cpp +f05b242e510624f3e4328df2417de6fa5fbe8a6a469812af3d0d648306447583 Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGLightsync1zone.h +fff4f115a9d2d666e90f34c7dfca6a48147038163381f9320883fcab27901367 Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGPowerPlay.cpp +76d951c156093936653a9985f74f5a24c0bdbbfa7222e174b39379ad50156b1b Controllers/LogitechController/LogitechGLightsyncController/RGBController_LogitechGPowerPlay.h +3dc3697f8460353b9d58ce8705981cf37fe3f1ef98339e70854cb4e813cf1fc3 Controllers/LogitechController/LogitechGProKeyboardController/LogitechGProKeyboardController.cpp +8675dabb422b4505f39f610715e278ebe917d05fec3eb7443b4b77a348b49e8a Controllers/LogitechController/LogitechGProKeyboardController/LogitechGProKeyboardController.h +410209905641363ff101e132d4205e852b5dc4babd3e7fe5db4c63f8a811c828 Controllers/LogitechController/LogitechGProKeyboardController/RGBController_LogitechGProKeyboard.cpp +8be618d89256708e2eb8a2c1e1ad1a80105c9d2f1b6bdd46316e41c292e7cffc Controllers/LogitechController/LogitechGProKeyboardController/RGBController_LogitechGProKeyboard.h +7b659f26e37909d50576071674af42b96e7518608a4f20ac03f7635fe43011c9 Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller.cpp +91ce5f82e8cc16081ff22f1dcbf873cb24ec8a95e8a4bcafe605ca15b6a6025e Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller.h +af0acfd3805ba12973b19f38caa1bca1f07dc6b1b8e6ea2740b569b6c9b43ae5 Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller_Linux.cpp +75c6b0994f1c369f665657d2a387e7ac6dfc1a88f4ab8965326d539d007af03b Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20Controller_Windows_MacOS.cpp +4d0c1683f8366de9562f3c1492e84cb59f66661ecb01ac001c2e5540aeb4fbe4 Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20IdleSettings.cpp +d3712631b9ff68b99f8fefe40820da14c42a2141b5e06579e4f0e6d71e953159 Controllers/LogitechController/LogitechHIDPP20Controller/LogitechHIDPP20IdleSettings.h +20f4620dd47e6082f3c7105ed924fd2680c9e53d49ecfeb5861e13e63476c18f Controllers/LogitechController/LogitechHIDPP20Controller/RGBController_LogitechHIDPP20.cpp +01a88067881b83269effc2b710a1624cd10909248240b1b9607abae0a081a4ca Controllers/LogitechController/LogitechHIDPP20Controller/RGBController_LogitechHIDPP20.h +bf1bcb383f2080cec57d83591cbe5902601c59712beaacf76e66372962c768ff Controllers/LogitechController/LogitechLightspeedController/LogitechLightspeedController.cpp +aeda7ec692fb61f0519a2b448e2d1e7fc77c053239d80e193061c6df92e30797 Controllers/LogitechController/LogitechLightspeedController/LogitechLightspeedController.h +8d7164ab36f8c86f14bb965513fdc3efff2f4428253fb00ea3def569f329f565 Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.cpp +84fd3efef5be42bcc3578b5bab7b14631091b27f8262f53a489aba48b5c7f5cd Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.h +3e4295c7501226b99d24c15a4e91541f9da00055759ffe5e6755508597df6b8d Controllers/LogitechController/LogitechProtocolCommon.cpp +68ed2cfaaa083746da11536a64e8ba21e9f04398f91dfd9b778e7c72700e7e7d Controllers/LogitechController/LogitechProtocolCommon.h +66ee328925f7767cde69353bae00e7dde90196613734d3c7c7dcb713d5262bb9 Controllers/LogitechController/LogitechX56Controller/LogitechX56Controller.cpp +0dac4f72372e5b0ff5839c5acd15d7a32319350c35213b8ae5ebfd9743fd9d52 Controllers/LogitechController/LogitechX56Controller/LogitechX56Controller.h +82e1d2bbd20a208bd7d35f3a9a8b32973a317f6ae5e2c44393b5e325de9297da Controllers/LogitechController/LogitechX56Controller/RGBController_LogitechX56.cpp +401415c3d9ef2b0437315eebf7d0205de01ae034c3bffe4702b95f21cc71c28d Controllers/LogitechController/LogitechX56Controller/RGBController_LogitechX56.h +ea1815d8fb5df51c6d582afb370cf4d1e2c9929fce70df6df07c9fca895d6361 Controllers/LuxaforController/LuxaforController.cpp +e71dac3df1df414d1acf95faa1c71742cd0a04e8a7b43e33261f58bb317c6e89 Controllers/LuxaforController/LuxaforController.h +9d917dc83c3bd355660f03c8b0940863d849fecfc6b4717a9b070385b0d9e18c Controllers/LuxaforController/LuxaforControllerDetect.cpp +cac4dc481a159efb2d47c8d68a3ce491f92b649bd5db79ffff8601c18856a9c4 Controllers/LuxaforController/RGBController_Luxafor.cpp +4bfac3a275bf18bc29595278a6013abb3ec2f651e79f603757fb190f2b340b6a Controllers/LuxaforController/RGBController_Luxafor.h +4f12d5a3826a7df008c9bc9a5ee197bc0efa7b749708e787c1b14e4ac433c34b Controllers/MNTKeyboardController/MNTKeyboardController.cpp +d3ff148af080e777f85b84e8125275a55434d84985442c0ebb94de36ff599e26 Controllers/MNTKeyboardController/MNTKeyboardController.h +26dddb1483af5fe6773f1a66c4ca9daf5a22a2e1a7ff7602986af6804ff64483 Controllers/MNTKeyboardController/MNTKeyboardControllerDetect.cpp +720596320d76f807e095c9ead86a456881ea13b031ddc408ce1cf85478f97e8d Controllers/MNTKeyboardController/MNTPocketReformKeyboardController.cpp +55f3e1ea97d226b1cf46a34aee43bd0d4a325a75ee889e51d7313bf99d3940ba Controllers/MNTKeyboardController/MNTPocketReformKeyboardController.h +db7a1eb7b961b9245362975364657eb204b2f524f8e0f041f1d9beebcb1b2a8f Controllers/MNTKeyboardController/MNTReformKeyboardController.cpp +58d19a9f8c772f881147c3f05a1b51c822ad24be07bcc5270896cbceb8a4444b Controllers/MNTKeyboardController/MNTReformKeyboardController.h +b6286c2668b2bc7022db14b1c84503538e65035b34fa1f1c7bee8a82d7746528 Controllers/MNTKeyboardController/RGBController_MNTKeyboard.cpp +d67f5f2e342378dff54fdcd850c9eff473988b7463b9c60360183b1e831d021e Controllers/MNTKeyboardController/RGBController_MNTKeyboard.h +6f746bb9a316f68bcf35c9e3031be33ffaf6944cb13f627165a80ce59ea930c2 Controllers/MNTKeyboardController/RGBController_MNTPocketReformKeyboard.cpp +fc334d9b7662f01e5bb48f7b4a55aee27f3e534a85325f5a52c2df8dd27aaa91 Controllers/MNTKeyboardController/RGBController_MNTPocketReformKeyboard.h +6cbe1e776cbb2e27af470d5ee1aa566bad1771292ecfb4563080387f3eb53954 Controllers/MNTKeyboardController/RGBController_MNTReformKeyboard.cpp +3cb701fc5b17fdd4d3d5f3f987f2566d8b928fb9d0d74bee3b0710e7c20203d4 Controllers/MNTKeyboardController/RGBController_MNTReformKeyboard.h +88ceacdda143f5d3515161fe614b77d85858e3cf8b7fb314766e8fbc1c77792d Controllers/MSI3ZoneController/MSI3ZoneController.cpp +9b67a1c9de7679f98e54964fa3df159f585e08efb12837de94cfeab7a4e75689 Controllers/MSI3ZoneController/MSI3ZoneController.h +632539b97120a42bf073b2a4c4cc25129ac1434e37a2bd3b236a39d11fb1021f Controllers/MSI3ZoneController/MSI3ZoneControllerDetect.cpp +40cc73f05ac0ddd597834ac3c6531881af3a27bf258405128ddcc3bd3ab1df33 Controllers/MSI3ZoneController/RGBController_MSI3Zone.cpp +2507b5cb428bd724bd31c1bc0feed98686db89edd857615a6816d5cf2ecb260a Controllers/MSI3ZoneController/RGBController_MSI3Zone.h +c78a57ef5920a93f1cdfc686744f7361dd783bb5687e18de04f6c9a677611474 Controllers/MSIGPUController/MSIGPUController/MSIGPUController.cpp +945246c7647f8f82aaa3465910739743a04ac043798cff27996e2655ec7272ea Controllers/MSIGPUController/MSIGPUController/MSIGPUController.h +b354944366d6c52cb390ef7c54e433e1efaff5efb92930abcd920b17d7ee182d Controllers/MSIGPUController/MSIGPUController/MSIGPUControllerDetect.cpp +5451d6d409398a69e86517b79587f0817a8e9b0ecbb48ea1cb32aa81d7d4c504 Controllers/MSIGPUController/MSIGPUController/RGBController_MSIGPU.cpp +4ea5cc414fca4609f78558ea3a68849371ddc1617ae1e0d897fb8780655e6d4e Controllers/MSIGPUController/MSIGPUController/RGBController_MSIGPU.h +8f2c70d175feb0f4dcc7b0132061b3762d3a364e99ecf387391e4c67da3008df Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2Controller.cpp +2e99b718f1c623c2b34aa6e7fbeb918b3b062a0812d657ab5382c1e114b29d71 Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2Controller.h +1494b50256b3f2044bd1a7d4f33cbeba8db6659bc49ba3a90c2623b442e56515 Controllers/MSIGPUController/MSIGPUv2Controller/MSIGPUv2ControllerDetect.cpp +2397910a89c1b1fe229601932ef42720309c5934b8c7ec84fa22e2d70d328529 Controllers/MSIGPUController/MSIGPUv2Controller/RGBController_MSIGPUv2.cpp +35520a755925d0528a62392eff2f208f7c84ef537b40b9952eb691c4f0733f1a Controllers/MSIGPUController/MSIGPUv2Controller/RGBController_MSIGPUv2.h +4a8d24fc5f557c6b5168c7821a9df2dfab5b4a27d96bb090210be6b3ed63fa26 Controllers/MSIKeyboardController/MSIKeyboardControllerDetect.cpp +eeda0292d08c93f898be9911ac485b8cf8c1ceab55713fd33cfd5ccd5b62638f Controllers/MSIKeyboardController/MSIMysticLightKBController/MSIMysticLightKBController.cpp +976829e7fa15d2569eb7675565bb9c6852a558089824abdd13f61bd316b12c96 Controllers/MSIKeyboardController/MSIMysticLightKBController/MSIMysticLightKBController.h +10dc794f6153beb6a4cc9978535defa3f47bd83f9185e752db3c687e8b28d481 Controllers/MSIKeyboardController/MSIMysticLightKBController/RGBController_MSIMysticLightKB.cpp +5cfa50adc294da5b9c27f5f4b855e08410b458fc2b94a7f186694248290cb941 Controllers/MSIKeyboardController/MSIMysticLightKBController/RGBController_MSIMysticLightKB.h +e6f2f2e9fe0c9188b5633a56ce686eb0e0b94c715f05a11cc3543f2772d9348a Controllers/MSILaptopController/MSILaptopController.cpp +5e148257732a3396232ad8b64931bab383fef7709d909ae973755cbbd8d6c5e2 Controllers/MSILaptopController/MSILaptopController.h +97be4170c870a6496af2da47ee6dc1745732f69c550cba91a6d843cb953aa8ac Controllers/MSILaptopController/MSILaptopControllerDetect.cpp +480a503c108d35bbd6e8a2690037816eb17f7dfb61d4d54322ee3dfbe51a2b59 Controllers/MSILaptopController/RGBController_MSILaptop.cpp +84b649dbe80921c7e8b235ba33cef97dc24e1bea4af4e4ede387bbbfea86e256 Controllers/MSILaptopController/RGBController_MSILaptop.h +974cb1c23bf0ed99ebd2104c3864e1c286cfbd3d98ccab93d89853463a969055 Controllers/MSIMonitorController/MSIMonitorController.cpp +318aabebe620aed1b4b422ccd54bfbd7feb60130fab0999aa4ac0d272f26743e Controllers/MSIMonitorController/MSIMonitorController.h +61821a57653c2ff735d0f78f5bb56f015df839085610457236091d3936e9e7a0 Controllers/MSIMonitorController/MSIMonitorControllerDetect.cpp +2be0898477d7cebcd55304c68e3221b2dbf8db204653f44ecce84da9aab6e70a Controllers/MSIMonitorController/RGBController_MSIMonitor.cpp +1760bbb4c601474661e723fa3f4f82968e8f4092bd4a3de292c23059ae07c70c Controllers/MSIMonitorController/RGBController_MSIMonitor.h +dd770d6e5290c2487abfb821d6580d20df1e06d63b2f119e9ad3fdf973b20d09 Controllers/MSIMysticLightController/MSIMysticLight112Controller/MSIMysticLight112Controller.cpp +80bb6cc2a51bfb83359e11b7dc17100510fee2ebe88506dfe1843184bf62ea68 Controllers/MSIMysticLightController/MSIMysticLight112Controller/MSIMysticLight112Controller.h +39c1c97ebac2624f00c690694e3ae319a482981729b90142e31e9e0832eda60d Controllers/MSIMysticLightController/MSIMysticLight112Controller/RGBController_MSIMysticLight112.cpp +87aca6806320cfca1ce697da04ae408a59ae7cce6fb4e8317ffafa4449ba2b66 Controllers/MSIMysticLightController/MSIMysticLight112Controller/RGBController_MSIMysticLight112.h +9c254e6b6578f92dce72a3d6381e13dc4b85d371e6e13075d83bcee30c31d533 Controllers/MSIMysticLightController/MSIMysticLight162Controller/MSIMysticLight162Controller.cpp +200c6ba577adabc4f852b27aec187ea21e21ba77277b60f149e48542d7933f8a Controllers/MSIMysticLightController/MSIMysticLight162Controller/MSIMysticLight162Controller.h +6cdcf99398d5386d408b115b89b731e8d329dfa4bef5a685fcdd7c17e17abcfb Controllers/MSIMysticLightController/MSIMysticLight162Controller/RGBController_MSIMysticLight162.cpp +f2f2e785cc2b55a880bf7abd59ca82d64b03dc813b662f8fb5441895e636fa7d Controllers/MSIMysticLightController/MSIMysticLight162Controller/RGBController_MSIMysticLight162.h +16d9bdc58fd2205df80af2c67055ebab94a7858461c83f8bce91d102fb57c85e Controllers/MSIMysticLightController/MSIMysticLight185Controller/MSIMysticLight185Controller.cpp +c600ebcb8650780529a7618976b62d8696f163a12e7413f769f39b22954d2a51 Controllers/MSIMysticLightController/MSIMysticLight185Controller/MSIMysticLight185Controller.h +331cda7f01809c576631647b49e943154c516be49f8082f7ce7d55686560522d Controllers/MSIMysticLightController/MSIMysticLight185Controller/RGBController_MSIMysticLight185.cpp +0f16dd5fb50a9f51e7c779b1ee38e5a571af5d158b4b8fae19c3de863668b3c3 Controllers/MSIMysticLightController/MSIMysticLight185Controller/RGBController_MSIMysticLight185.h +3f9c5efeb7e2d8d9dc55a8a6ad99f776ba7f3d8cf2ffabeba543ee8a5e77be7b Controllers/MSIMysticLightController/MSIMysticLight64Controller/MSIMysticLight64Controller.cpp +e8ccbff43c8e7fc1036c57eac9d9d815051123894028c87b652b8333f8c2fbb3 Controllers/MSIMysticLightController/MSIMysticLight64Controller/MSIMysticLight64Controller.h +bcd8b3865f4a5750517c5f8d3a97970376bc6f06ef9599032162c301aee591a2 Controllers/MSIMysticLightController/MSIMysticLight64Controller/RGBController_MSIMysticLight64.cpp +3eb11d973731e3ed23c7d280c1e70b65736e825b014e82dcb3bdd477c7df95d5 Controllers/MSIMysticLightController/MSIMysticLight64Controller/RGBController_MSIMysticLight64.h +b4beb5905dfadd14094f2da8e1f31f4cb792ea25c0859711979845204164c08b Controllers/MSIMysticLightController/MSIMysticLight761Controller/MSIMysticLight761Controller.cpp +ee10380deab4e55afeafdba1e97ab49902f11a9450a0d99c86e7cd26d3459fce Controllers/MSIMysticLightController/MSIMysticLight761Controller/MSIMysticLight761Controller.h +d962d78c1acc34ec5e947441b402f6a7fcbafb6ebabf33946b6552a2ae47d15c Controllers/MSIMysticLightController/MSIMysticLight761Controller/RGBController_MSIMysticLight761.cpp +7cf523689bf0ca1421379207c01cf9bfda2058de2cc8c28c16addcab55147a8b Controllers/MSIMysticLightController/MSIMysticLight761Controller/RGBController_MSIMysticLight761.h +cd3d31f55e83bd4f07e2814c8e8ec33bb148281182502600e05c4b0dc16896ca Controllers/MSIMysticLightController/MSIMysticLightCommon.h +fe2f8a6b9905996ef883e8fb376b2292071d827237c311a92b93f83add875a73 Controllers/MSIMysticLightController/MSIMysticLightControllerDetect.cpp +dd3486d6581757a63a5a505364a0279894795e93616ea06db9cf7d2593f81fe3 Controllers/MSIOptixController/MSIOptixController.cpp +fbc46add1de09898564944ea38c4c5e133020903ece9522f2d7cbb3b6aa82712 Controllers/MSIOptixController/MSIOptixController.h +5b1b9fb803e3f4702d7e858ce3cb5cc73842185eb219416df590a6a5079e1ed4 Controllers/MSIOptixController/MSIOptixControllerDetect.cpp +35ad33d00f99dabb517d0858cf589def9b2a3fea860e1524955ac1fe09234ed9 Controllers/MSIOptixController/RGBController_MSIOptix.cpp +e574bd683c566b968e0cf5b8cd6b22a8e2c5f29e0fd006540e1028b3aae04627 Controllers/MSIOptixController/RGBController_MSIOptix.h +402dd2531e395abeec658599776952b37d304570ef527d828c2a4ec5234fe8fc Controllers/MSIRGBController/MSIRGBController.cpp +f374ed5be05f8ec2ba293869718d9edc5170b198ac9c0bc1ad27a7e750782cdf Controllers/MSIRGBController/MSIRGBController.h +db87eeb25b115370650f62277e4d7eae6970dc0dcfd130699757ed79fb1e69ea Controllers/MSIRGBController/MSIRGBControllerDetect.cpp +82290237d0dd16525c8a9323497b6ebcfab8dd7eedf63d8a912b367808f5563e Controllers/MSIRGBController/RGBController_MSIRGB.cpp +0261f15a36813dc44282e3abcb6bbdc0ee85dad9ca9bbe0b02cb4a0897146a20 Controllers/MSIRGBController/RGBController_MSIRGB.h +53e89e65fb0ba82db72ed6541dd1031c50581b79f6ae7f916f8288e3eb5594fe Controllers/MSIVigorController/MSIVigorControllerDetect.cpp +85d63e165d524f1e1c7ff946bf68fd24f285d7f34843f068a1795182b2a785a9 Controllers/MSIVigorController/MSIVigorGK30Controller.cpp +74fdfb1b9eecd39451e7ece33ae052399c577410b3b2ec30a0422e6009c56180 Controllers/MSIVigorController/MSIVigorGK30Controller.h +0d8da89af0366bc832f0d4cc7976d5cc5bad6e8ceca93deb98f42fcc16495869 Controllers/MSIVigorController/RGBController_MSIVigorGK30.cpp +be187562056ef0e42d0f449f6c384000068b354bf9410d28c8adcfb25df325ee Controllers/MSIVigorController/RGBController_MSIVigorGK30.h +204e839b799acf605fe69a5c4476dd9c2ab9ef7c4cd789682b7cd1f17dccf8b1 Controllers/MadCatzCyborgController/MadCatzCyborgController.cpp +79a30bc641288effe08da265e45d08be5114247aa20c29ad0927140351293907 Controllers/MadCatzCyborgController/MadCatzCyborgController.h +1b145d7a697c37c6a7659ce726f3f8579a4293d65183bdba3e1e36775dc84cb0 Controllers/MadCatzCyborgController/MadCatzCyborgControllerDetect.cpp +bdadd5c053282aef103d9a53910a7833553734514bda8e7847ac911516f35c30 Controllers/MadCatzCyborgController/RGBController_MadCatzCyborg.cpp +7dad65031f450790950e0e9df0f662468de561dded682608e3c78a9464f2bb3f Controllers/MadCatzCyborgController/RGBController_MadCatzCyborg.h +110922d0ed1a97b0df773c00bb4a0f37e42b25711b55eaf2fe87c3fc32dce37a Controllers/ManliGPUController/ManliGPUController.cpp +e174cce31c64e5612ef3d57c614bfea8c0563d8cefb562fd98972dc8752e959f Controllers/ManliGPUController/ManliGPUController.h +2ff8ed4cf945d50fc29a690128eff1ea1b9f1b796a4092f89c35fb42fe8d63e9 Controllers/ManliGPUController/ManliGPUControllerDetect.cpp +e03609cde7dfb35d6f0d78796a2de36aae791887d46181772825d555b69b135e Controllers/ManliGPUController/RGBController_ManliGPU.cpp +fe6d4cf22214f191ee09673d6a8bec17d10b1e60b9c0a0b416fbecea8ad73bfa Controllers/ManliGPUController/RGBController_ManliGPU.h +c8e94ddb7fd1d36e79eb914bf63d6982c3f4591c166da667f0d393ecf4398e48 Controllers/MintakaKeyboardController/MintakaKeyboardController.cpp +039df582b7f4ece326209a65f853a00af91c60334a3c765967e21f694bac91f4 Controllers/MintakaKeyboardController/MintakaKeyboardController.h +81502b17211399a55b54b01a4ba1b8b2db44b35b30396c817f0d777b68e317c4 Controllers/MintakaKeyboardController/MintakaKeyboardControllerDetect.cpp +9d01e443989798f93668b3515c423829992ee5151d2454cd340c0bb93927eb0d Controllers/MintakaKeyboardController/RGBController_MintakaKeyboard.cpp +f0fee65aba1bad97140dce5207b380cd5db887b0cc6afb9137a05ab5b75f2108 Controllers/MintakaKeyboardController/RGBController_MintakaKeyboard.h +b77b8de1abae927dbb3aa4d61889e52d62c6e2dc694be3891acbd2abaf71930a Controllers/MountainKeyboardController/Mountain60KeyboardController.cpp +7a94939299c0a3a02af6ef62737070dffcbc7c0edb109722521be9b45512e3c3 Controllers/MountainKeyboardController/Mountain60KeyboardController.h +2c651021970312725fc91f56c97724e486daa804dc3b96830c03b0d0769d3286 Controllers/MountainKeyboardController/MountainKeyboardController.cpp +921ea984f6660d52539cf8c189fd6abd426fc68933d24e8540f5c37a92a47b1d Controllers/MountainKeyboardController/MountainKeyboardController.h +e3dea057439b8a2f50e98012bff0ed08f9fa262f2eba473ad38f5e153041d94f Controllers/MountainKeyboardController/MountainKeyboardControllerDetect.cpp +58e9784089a96d267e4659198e99e37dffe6e153902e26ab3ff2bb60f9b9c8d2 Controllers/MountainKeyboardController/RGBController_Mountain60Keyboard.cpp +c0bd082d330edae95d51f6648955507446fc5fb03e41923c378ed9246019d1bf Controllers/MountainKeyboardController/RGBController_Mountain60Keyboard.h +e761a2d281e91fc790968a8c1ad54ca913d8517e4c70791c69bdc3db53707d01 Controllers/MountainKeyboardController/RGBController_MountainKeyboard.cpp +b74df5809e3ce6bea983f0739bed7c0281caa4557d24c505651530a22b23f815 Controllers/MountainKeyboardController/RGBController_MountainKeyboard.h +a53c2876eb1ac6db02e9430c7aa1b8c266746382d31441d54d05ab6d2b4b46dd Controllers/N5312AController/N5312AController.cpp +f3b6837db8343237e827e3708244b1ce379e970693f13cd818e3345301d85057 Controllers/N5312AController/N5312AController.h +76ac2b8050dfa4fd587f25d5b0a5b15fc03269266bc178438d6e61070dc58a6f Controllers/N5312AController/N5312AControllerDetect.cpp +6c8bc8b6fc710b833d76cbc779f1e066b6baff2cb747a1d82335e2c57808a7f1 Controllers/N5312AController/RGBController_N5312A.cpp +a031d63d41252a64a1860b9252e8914d76fba1f201588300cfdc7bb7bd308724 Controllers/N5312AController/RGBController_N5312A.h +63264ad1e4fe6960563222551afdbd182371d65f59818e251c6d9f10e13f7ffa Controllers/NVIDIAIlluminationController/NVIDIAIlluminationControllerDetect_Windows_Linux.cpp +70da06f477a5839dacd16589f4576723f9f5f612192c5c8888a9c54cc59efa6c Controllers/NVIDIAIlluminationController/NVIDIAIlluminationV1Controller_Windows_Linux.cpp +a9d805094b465c1d276b6a41df7ca3f9df3e0e13ba2d31c0104f5e121fba3387 Controllers/NVIDIAIlluminationController/NVIDIAIlluminationV1Controller_Windows_Linux.h +8f0737db29c71b4b134723843d5d16ae50870f268fdc394dc3e83629b8ce4f9f Controllers/NVIDIAIlluminationController/RGBController_NVIDIAIllumination_Windows_Linux.cpp +eafde067c75333371e4b6d211319a719a5c9a65016b834f6121b13d4691b95b7 Controllers/NVIDIAIlluminationController/RGBController_NVIDIAIllumination_Windows_Linux.h +4973b89f55c11b07204d1ddde5a26643a82d9112354fc36f5fed9163c87117b8 Controllers/NVIDIAIlluminationController/nvapi_accessor_Windows_Linux.cpp +8ccc20ea3e8853ba44c36800a419941d2436b648305a9973644e80057f9e1b8b Controllers/NVIDIAIlluminationController/nvapi_accessor_Windows_Linux.h +04a80ad952778b9bca612b24c1d64c5601375c6aa9b43d29f7801e695099f909 Controllers/NZXTHue1Controller/NZXTHue1Controller.cpp +04d42ddef1b9f2d6c02863a5b3113941d69e55a4b43e2a0fabd2dbfe87e54cff Controllers/NZXTHue1Controller/NZXTHue1Controller.h +e849c4b25b647476191921ce5547017b8477d4a89e7d4629c3c713b3f5a46735 Controllers/NZXTHue1Controller/NZXTHue1ControllerDetect.cpp +6742ca4342095bea6fcf11510051bd80c49dc0e02df7e2731dafe1c7b24df746 Controllers/NZXTHue1Controller/RGBController_NZXTHue1.cpp +c54dadc216b1878affeee160a4b4d631aa50d7f25ce65bd246dfa07390a83c0f Controllers/NZXTHue1Controller/RGBController_NZXTHue1.h +b3be122bdeb82f414e39031851365f42271742e848b68789987b8154c7677068 Controllers/NZXTHue2Controller/NZXTHue2Controller.cpp +ee1b79ce43b8f94006ea054f618b5385f91a9d1ccac8ce65ed1022d510332f0f Controllers/NZXTHue2Controller/NZXTHue2Controller.h +5b84127270bd18abbec344d317fd7be3316db134f717febcd354ab9d9b222285 Controllers/NZXTHue2Controller/NZXTHue2ControllerDetect.cpp +e3ef3506408b7ccf3bcf9bc419299c75c93cb9c82708ae6556e6f12d52638483 Controllers/NZXTHue2Controller/RGBController_NZXTHue2.cpp +a5824a4cd3fb8442040e874f143eb27418c820e101c4ab6e6bd942a9c2240515 Controllers/NZXTHue2Controller/RGBController_NZXTHue2.h +676f56d299bd47ad946f480f28cec40940c9dc38445d34330dac43ec9db77683 Controllers/NZXTHuePlusController/NZXTHuePlusController.cpp +dc68cbddd5607631b70700b3c866ff5269889dbe028bb77480279fb14e5b35c9 Controllers/NZXTHuePlusController/NZXTHuePlusController.h +30608b2a5d76dbf59aa1cd4fbbac8c4d118c1d7954676b2ec6c7969acc106ee3 Controllers/NZXTHuePlusController/NZXTHuePlusControllerDetect.cpp +a1dffdd088ff9b68963ee97dffefb66cedbe48e4a4930982b771bb4ec97a4369 Controllers/NZXTHuePlusController/RGBController_NZXTHuePlus.cpp +34570a388932941d8130ebd7e7ba6a4f6ae9bcefcc9482adf47a4407bf0ba5eb Controllers/NZXTHuePlusController/RGBController_NZXTHuePlus.h +a98528336370d3082e22dd231905fdab5ea01c59e0c36c1c98b1caf14054ea60 Controllers/NZXTKrakenController/NZXTKrakenController.cpp +538a451e5331530c6b25b46d52ea17adcd7ff146e7d887eb068fa30d90adc2a2 Controllers/NZXTKrakenController/NZXTKrakenController.h +ded5f71ba1f50d797802f7b0ad170fecc93c97971ed9252fdafadbf478298fe0 Controllers/NZXTKrakenController/NZXTKrakenControllerDetect.cpp +50ffeb8c61d4818c76f24fc819fafa9ea7bfd66b439bef808cf31a00f08de2aa Controllers/NZXTKrakenController/RGBController_NZXTKraken.cpp +7cb5b726912ac42146522837deb54c0c6bffc38a1e5113a4bf52efb6a1082661 Controllers/NZXTKrakenController/RGBController_NZXTKraken.h +ddafcb9a7224d8194969b2419afb7ba9a4bd4d3b05649dd87fc1d31cefa3dba5 Controllers/NZXTMouseController/NZXTMouseController.cpp +50cb9d325f1b73b8c3fcc2626f9948144263b6bab9334a9dcd56b8948f22ddba Controllers/NZXTMouseController/NZXTMouseController.h +f5a1c1cd05df3b89491dba16a644eb4d33e187f5b6b589e24b800faeb23463eb Controllers/NZXTMouseController/NZXTMouseControllerDetect.cpp +1e9f132662462d51210ecc6ded0191488fd7fff84ddcba927a5624b0f1921292 Controllers/NZXTMouseController/RGBController_NZXTMouse.cpp +cb04e6eddad5d8ff928fd2c5b558848c83ac3adf2ce2ba07cb143944d19c0bae Controllers/NZXTMouseController/RGBController_NZXTMouse.h +c5e85993e89283f6588d3fc818dd93ff9078c2e182c484521a04c26aaa751dcc Controllers/NanoleafController/NanoleafController.cpp +8101d73fd2e283f92b54eca3a72b6865033dcc0eb9ea8d33dc64077a23dedbf7 Controllers/NanoleafController/NanoleafController.h +597111d27cc556ead8f1a143c283d06d9588d147b662a7d0f8dd9652dad63d18 Controllers/NanoleafController/NanoleafControllerDetect.cpp +c01def0334bbc5c502e12acc544df3f33fbbc08cab889be81d22e3611d8bccfa Controllers/NanoleafController/RGBController_Nanoleaf.cpp +1ae36ba09972bb77236cd4bda1089b4af24dcb0e710ff1e8ebd2d51945375df4 Controllers/NanoleafController/RGBController_Nanoleaf.h +b206eb8dba0267d149addb80023223584a9eade28a12d33743137bbd65f1c166 Controllers/NollieController/NollieController.cpp +3b357b6e748923dac6fba6a42283432c0089ee47a9d36e3bf7cec26be0179842 Controllers/NollieController/NollieController.h +c55dc33c8874c80a9d2f1d285e65799bcd99ed90dbf4cedf6664c3230cbbf350 Controllers/NollieController/NollieControllerDetect.cpp +680eeefc5e2757d5bbc616ece6c612ffc4d7395ee247b5c5f825acb753866cac Controllers/NollieController/RGBController_Nollie.cpp +dd57ac24690c0cc2915716f5469f6fd43dbc91884f6b3939e4becc08a531cb76 Controllers/NollieController/RGBController_Nollie.h +dfb5bdebb10aacbbd9dc97414ab7509ca85eed7d4f363317fbe7f884f5e4c34f Controllers/NvidiaESAController/NvidiaESAController.cpp +de3f7d154bbed3d81b877e8b7cea4c8374e59243ea8b231e005ec942e223977e Controllers/NvidiaESAController/NvidiaESAController.h +ef4832d504ea50579177fa401247be4d7ba804b448c2ae6e6ba126f41b7f771d Controllers/NvidiaESAController/NvidiaESAControllerDetect.cpp +ea059e56d62ed1cd46b1ea5e3bf3f61c2380aa8b39b4390d7e5e8464673b66e3 Controllers/NvidiaESAController/RGBController_NvidiaESA.cpp +5ee2b22662a2f97e3ec8c38d29ebb1f3d3c855e4ca61046156b2608183a0ce31 Controllers/NvidiaESAController/RGBController_NvidiaESA.h +d3b4c0fcff6f9afa637499d46673b5a6ba0355df4e3985c2980d0ff8241af544 Controllers/OKSController/OKSKeyboardController.cpp +90e31ff694f14a726b90b70609269db06f6e5a17b94b65a334622db600fa4d51 Controllers/OKSController/OKSKeyboardController.h +74fcf924704df73770743fb2b32dade5c3b5ee9898af86c142fb3479f58b72fb Controllers/OKSController/OKSKeyboardControllerDetect.cpp +3519b2f62dceb4cdb53a2ae7cb03f87c81e6cd6054963b455affe6f907ee4ddd Controllers/OKSController/RGBController_OKSKeyboard.cpp +6bcb1cab09a7a6f4a0744366fa37077cf609defdd1b129c577652ae2e37b6dc6 Controllers/OKSController/RGBController_OKSKeyboard.h +e8114c57ca4cd93c8f4974498e13833c8adadf4645f02a5949d1ddfe9abd1313 Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUController.cpp +f617db09e4e5fabc646b833aa65d34885df63b6b15290300a4aebd19a8a11c9c Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUController.h +f96a0c58732b56d52f1b716dba22294b2e824c4834b6ee7474e6ef285a6fd5cb Controllers/PNYARGBEpicXGPUController/PNYARGBEpicXGPUControllerDetect.cpp +b2fd76cf39e2df3b0007bc94423150423a32c0ffbc2b2b1ad318e47ad79b12c4 Controllers/PNYARGBEpicXGPUController/RGBController_PNYARGBEpicXGPU.cpp +32a1f01be0412dee22fb8ae19b0b1091fb3b353b0e72b776dc521d5fa7c80c7c Controllers/PNYARGBEpicXGPUController/RGBController_PNYARGBEpicXGPU.h +a3c8b2a2c6c14c4b8d09b5ed043b6a3004da730ab0fc8e08f46c0845b1501182 Controllers/PNYGPUController/PNYGPUController.cpp +d18a5bd0c30cdafe2ffc81edf2a409a9c664ada844e1129f2e40773a448fb122 Controllers/PNYGPUController/PNYGPUController.h +929a4ceca90d4d8f6f28f12932a401306520f568d1f2751881b031b83c9457e1 Controllers/PNYGPUController/PNYGPUControllerDetect.cpp +79aa2413cfaa020ae4474158ed2868b6efc839fbec18bb022a28825c2b508216 Controllers/PNYGPUController/RGBController_PNYGPU.cpp +67325d5eb856cd969ebbaebebffa9b04da4cdafcc2ae1e94c8c3d5836e40db25 Controllers/PNYGPUController/RGBController_PNYGPU.h +0c5bd76a949ed89a9f97d85ffcb277c0101996f1bc7ce8878e540f6491f97bcc Controllers/PNYLovelaceGPUController/PNYLovelaceGPUController.cpp +6e9d4b14c00b5804889498461bacb708562f4fc3640dd6f68ff53a0a2e362157 Controllers/PNYLovelaceGPUController/PNYLovelaceGPUController.h +6c51423989f9608e7a53c182a2d44ca78b46c6f1eb6fab87af52d03f562aa729 Controllers/PNYLovelaceGPUController/PNYLovelaceGPUControllerDetect.cpp +878b2c2a70fe7daf8ef38a95ce2dcbca1020bb7d34e1b9f15f143d18b96c4dd1 Controllers/PNYLovelaceGPUController/RGBController_PNYLovelaceGPU.cpp +5a9cfc97a0f2755e0f876549187972ebac773107f58be6558e2884fd36a00351 Controllers/PNYLovelaceGPUController/RGBController_PNYLovelaceGPU.h +b87f5aac9212ae3de87bd13bed97f8b22a26edae35df9ec6132caaf74a3cc5db Controllers/PalitGPUController/PalitGPUController.cpp +3264da163754609acf75e3c7206069480423dfff212ab7b21ffa743737708b33 Controllers/PalitGPUController/PalitGPUController.h +7cb249e481b2112e20abbe952ff03638b97a91fe6909ec2de31c5a947f4b749b Controllers/PalitGPUController/PalitGPUControllerDetect.cpp +9db5e104f45d8cf30d1bb232958bfd57101e750bd22d5de6ccb6fbfe84ec31e4 Controllers/PalitGPUController/RGBController_PalitGPU.cpp +852f3a391ff33dc184a86d9622489f8de6ddda2ac85743c0266101ec4631fb2e Controllers/PalitGPUController/RGBController_PalitGPU.h +b35a31111cb3bfaf9943f12f0761aa2763ce5c6fcfe4fa05ed59a38676a35fc2 Controllers/PatriotViperController/PatriotViperController.cpp +b2e8d84c9a9a4968fa6b7bf12b5fb24149d73556f80f6263991ec09afa49aa0d Controllers/PatriotViperController/PatriotViperController.h +94c491912ad21c474832f3bd7717f4a7cc175faf226e07afe5d9eb82e23f08a7 Controllers/PatriotViperController/PatriotViperControllerDetect.cpp +986ff649ce1dbeebfa1c8d6b0bad5248cff58a84f5bcf3e3481e7c68279e46a7 Controllers/PatriotViperController/RGBController_PatriotViper.cpp +98d29f50f215a7b996375d40cad6baf118a44cf47cdde970358ad15e025d1ac6 Controllers/PatriotViperController/RGBController_PatriotViper.h +35d34290dca85111f0f70ee8c909a2d8948839286cbb7a9beab16e5e78ac9a58 Controllers/PatriotViperMouseController/PatriotViperMouseController.cpp +f781e532ea9a6692148443703bcd816ae10efa8934302c935d8f39033dcf850b Controllers/PatriotViperMouseController/PatriotViperMouseController.h +b1c9087221d6288bda4272fa975609dc555a10a6a70556da2eabd377f3f07a7e Controllers/PatriotViperMouseController/PatriotViperMouseControllerDetect.cpp +a1d27f4ac4c1cd3ca72ea0be6bc30e7d17dcff865da63e10786dfeb42a14a030 Controllers/PatriotViperMouseController/RGBController_PatriotViperMouse.cpp +ac6c994ed6a83dc6b5e9204a84e5552178fbc8175ded891924798c49e0380752 Controllers/PatriotViperMouseController/RGBController_PatriotViperMouse.h +23c0de4338805c5c005944184cf4d19628903ede2d6c9c7fbae4275fab2d923a Controllers/PatriotViperSteelController/PatriotViperSteelController.cpp +d59a3f5b67e4116a44e36f1877271dc14bd8dc598424fb5a8370648a8e0085ed Controllers/PatriotViperSteelController/PatriotViperSteelController.h +44d5f4c5a1b2446e50aed504c2abd8e87c5479f81be9bdc3f194c4d8c6af0a57 Controllers/PatriotViperSteelController/PatriotViperSteelControllerDetect.cpp +472f77b9e886980abccc6af91c06f2d4316263a1c5b3022402efe140f4a441f0 Controllers/PatriotViperSteelController/RGBController_PatriotViperSteel.cpp +6b35b03483e75f24fa5f170f6d4d4b455153800744dcf1061c821f7de9a5ed62 Controllers/PatriotViperSteelController/RGBController_PatriotViperSteel.h +97e9cd2cecd3ad6a6b93f1feebf50df803651e5de1429e647ba7a9e5042502cb Controllers/PhilipsHueController/PhilipsHueController.cpp +5a35ed8834853f3b28e62a600db6a8e7c02d5ee49f0b61f5e22fe79eb81b72f0 Controllers/PhilipsHueController/PhilipsHueController.h +8ad37286602759773ad757ec14fb99d6fcf48186c031b9b557487ec51317ab06 Controllers/PhilipsHueController/PhilipsHueControllerDetect.cpp +8883412e0473561057e1cbd00c99a8508f92e19f623455c6b609ca1dc552a3e1 Controllers/PhilipsHueController/PhilipsHueEntertainmentController.cpp +c5fd42849d4896f647a91ce3d9459484f3c5c6133396daabf8d3af41f423d668 Controllers/PhilipsHueController/PhilipsHueEntertainmentController.h +834c9db1c11445453b2a695893022523b398f84f34fee1b6eb7a88bc026d4535 Controllers/PhilipsHueController/PhilipsHueSettingsHandler.cpp +d48002e62678deda0673da5b5f13d037fe759aa139fc6f94b98311f053a9ea99 Controllers/PhilipsHueController/PhilipsHueSettingsHandler.h +72cec604543c9f25e9d46a6f0b2f9e9d6584cb7efddac5ce0f4b6478b4b099eb Controllers/PhilipsHueController/RGBController_PhilipsHue.cpp +fe1ab149b23c7713674be2b5e8e32fb6f39814d45f8398f898cfd150aa258fd5 Controllers/PhilipsHueController/RGBController_PhilipsHue.h +fd830e0b91f8ce27d4cff760d7f6ed1ccb937c7daaae09486f40b0b3edf71ff7 Controllers/PhilipsHueController/RGBController_PhilipsHueEntertainment.cpp +495f6c40ed6b6acefaf3ffb4f69187e1ec90d34b0c49db92f933d75fec2046d3 Controllers/PhilipsHueController/RGBController_PhilipsHueEntertainment.h +d891ce77a0852ccdc146df61e6ba7ab437f50b40af940b5718095c5166832ef3 Controllers/PhilipsWizController/PhilipsWizController.cpp +99aa58ce820c81e5d18df471cb87fcb9c43a52fcd59c48bf353e6f5c94a40bc5 Controllers/PhilipsWizController/PhilipsWizController.h +9834e655f871bd7c65f0188158067661c40bcde11aa8870c046043f2ff0dd47a Controllers/PhilipsWizController/PhilipsWizControllerDetect.cpp +bd9223883118fe234b415f2988f98d378f26f0837562de2ffc1db80fbe6c21b5 Controllers/PhilipsWizController/RGBController_PhilipsWiz.cpp +f149b24ac1fbdfbb241f9baf02bcd5a0cd239b98e75c2035f640d8ba299fab69 Controllers/PhilipsWizController/RGBController_PhilipsWiz.h +f8a6ae3dbd1ed32620a43ae08b7fb592115b7fa79448c1c862528c65a77d108e Controllers/PowerColorGPUController/PowerColorGPUControllerDetect.cpp +63f5f20e9c515fcea9ce3db530a7d435e4a2f69e18f42758bc1a0678f23d53ba Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/PowerColorRedDevilV1Controller.cpp +0f502744ba79c1650ae22a83a53461cd77dfffe567acef36bbb816c2814f85cb Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/PowerColorRedDevilV1Controller.h +2530ddeb336627b7e313423eef3828d176e93a39e9d5dd52f8985ca5b630dd2c Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/RGBController_PowerColorRedDevilV1.cpp +a372328f3b41ec5bc33411125ab58359fad8aa52cdfc2594834ad907a7fededf Controllers/PowerColorGPUController/PowerColorRedDevilV1Controller/RGBController_PowerColorRedDevilV1.h +20bfb37eeb5eb5003ef800a2bd2c6fd2298e1f17f9d30ab30cf131c32e8e8c33 Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/PowerColorRedDevilV2Controller.cpp +125a9367ca6f3a41e1b940ab75fc880aa14fc09ccaf7753171ec43cfa18ee675 Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/PowerColorRedDevilV2Controller.h +9ba1642984a69dd416be82137238de0665743f653e232952dce80a0fb0181fe4 Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/RGBController_PowerColorRedDevilV2.cpp +05e94c5f04b3a34f0775e7abb2caa04a7ca98d91b49f8ad262c40d34179cf318 Controllers/PowerColorGPUController/PowerColorRedDevilV2Controller/RGBController_PowerColorRedDevilV2.h +4b21c292792f85e27c726f24047df3b41e6f3e401281aac8d87d624748ab7615 Controllers/QMKController/QMKCommon.h +58290cae98278d5c9ae7868117208dd8f30bf6277ba682c64d1b732d2d2cf24f Controllers/QMKController/QMKKeychronController/QMKKeychronController.cpp +9a64e8f654c2732a76d106a7bcfa3f809d82b9858f52ffb01ed32da2949ccc10 Controllers/QMKController/QMKKeychronController/QMKKeychronController.h +83145862789a93106ac8df07a401f5ac6a121a397b4c8d9d725130209eb8cf10 Controllers/QMKController/QMKKeychronController/QMKKeychronControllerDetect.cpp +bb75716707ee177842e0de2c1a42ff425183f9dea5bda5eb26ddfd045ef290dd Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.cpp +46e09729e528e1f6ce1207931a62d214ea023ebd095c1615c5c8ece976bd5382 Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.h +421ae19cb6bb7c6c813533fceece0bc9f5860698a34324bf36afcc415a7d82a8 Controllers/QMKController/QMKKeycodes.cpp +edc088889d1a923e557f9421223f3568650980e994f9147f40a564c0ac098959 Controllers/QMKController/QMKKeycodes.h +860369a4897925690a78a122bdcec749c0e9dcdd556f0642b786c6a526f9be1c Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBBaseController.cpp +762602966fd47ba851cc921bfcd14528bf54b3ecd0ae71861e246ec6b1993943 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBBaseController.h +74c2fc85be7127a22591fb7e6f4fb33f4ef42ed06f36a810994f9f3c2dd1bea3 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBController.h +b8eb86ff3a75ad44c6efbdec1e5aeb22f02696743caba6a2e308a91941dfdc7a Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBControllerDetect.cpp +b6d54965853dc70e363c0b81dee8087d6044752d690939d9dfc54496b2e13e40 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/QMKOpenRGBRev9Controller.cpp +e07115ed3b0ba67840578509c8b149ae31ca4b70ba2c05cdd52be54f680b76fd Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/QMKOpenRGBRev9Controller.h +8245c69e7fb1b03e76dfe1d6e245da541af4c872fadb56d7dd7583e4d504ec00 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/RGBController_QMKOpenRGBRev9.cpp +7af11f836abad9e0e930f0c35376934e13e820fcc90a07582d1d68ab0d55371a Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRev9Controller/RGBController_QMKOpenRGBRev9.h +22b34149f209ea87ff15f0a0c747485c4be3ba614b2fd95795d19ceefcfc71d4 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/QMKOpenRGBRevBController.cpp +463c0668420bacf6c7ccfcac06d48158df28fdcd59ed43575fe646c6ff493770 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/QMKOpenRGBRevBController.h +c89661786f10f259b837dc5f585220038bb0e23a07b77250e2034fa10a738b35 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/RGBController_QMKOpenRGBRevB.cpp +51523908f53d6dc9df41b5830922987f35f0dab7b4f01be3ab7fd6466f12a287 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevBController/RGBController_QMKOpenRGBRevB.h +6c1a262d3df68f046c86e09e1cca6051062ac908d5f08f5f9251d9b0e77593e1 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/QMKOpenRGBRevDController.cpp +bc2cec54de6cc48b96a401ea6792407f37cdcebb071bb9a3748e9e7c24b308dc Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/QMKOpenRGBRevDController.h +b9a8e9a68d420b3c12ccc422c309323678a6aa3bf928f1ee36e6578b5a677275 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/RGBController_QMKOpenRGBRevD.cpp +dfbad4885b9a5e377a64bfe33b782a1522cd6b898978ed5b43e114c8e1f33f5a Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevDController/RGBController_QMKOpenRGBRevD.h +0b04863fc138bb29f01586adc1e762c6138fdacc9dfecb04c0e72814f3e17ca9 Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevEController/RGBController_QMKOpenRGBRevE.cpp +2cb1d57aa64eda05473982d1f9e04cb8fa94bf3f0b67cd767e4fb2f9fb5a14ed Controllers/QMKController/QMKOpenRGBController/QMKOpenRGBRevEController/RGBController_QMKOpenRGBRevE.h +d99218772db79a71ca23718c3633a4331229a4fdabf8db60a5abc7a7c81962d6 Controllers/QMKController/QMKViaCommands.h +de7fb06729fa08ef35c1dd4971a5cf6e64927c8054d193c430905eea83e954f0 Controllers/QMKController/QMKVialRGBController/QMKVialRGBController.cpp +596cc822fc041d6668e79673a2c815f472acbebed8e31b5f6f13daafc19d5c10 Controllers/QMKController/QMKVialRGBController/QMKVialRGBController.h +70c4200483a17e7af350018df7d2ffc4d7192beb0ee7a564ebf6fdb04a1121d4 Controllers/QMKController/QMKVialRGBController/QMKVialRGBControllerDetect.cpp +7e492759f8e00a125eb422386033e0db02fb8400bb4d059547d5bd71d138aa9b Controllers/QMKController/QMKVialRGBController/RGBController_QMKVialRGB.cpp +b4135e14dbc3220eb5ff289e4aad7ecba9f2e2dde1ba7d8f04ed205180696680 Controllers/QMKController/QMKVialRGBController/RGBController_QMKVialRGB.h +806d54d68fc0c82ebdf9bd3047ccb370e39e53866e6954bd095ab8cdeca6cdee Controllers/RazerController/RazerController/RGBController_Razer.cpp +e70ec75e0c2365a4f665874cb0e2a5962ab5525dcd9af8fb9fef66fc952510da Controllers/RazerController/RazerController/RGBController_Razer.h +c9f69a7f08be93eb85a0cca136675da9e20dde01b91f5194308803867ebab13e Controllers/RazerController/RazerController/RGBController_RazerAddressable.cpp +2fbdd3a0a9cd07b7510cf6de9ce1b37caa811814d4d854b85e90fc4d3dccf617 Controllers/RazerController/RazerController/RGBController_RazerAddressable.h +ceabade9bd66208a8dd41f00c1008259381e57390ff1739816f60ee5f392f95f Controllers/RazerController/RazerController/RazerController.cpp +c8443da7148e6f9f80bcf8942053764ddac84904a4bf64398dcda5b56c1543eb Controllers/RazerController/RazerController/RazerController.h +069895dfe51f0b7eb7e6b30576728a51528ad7958cfa39a16212637ea64798ff Controllers/RazerController/RazerControllerDetect.cpp +a9c0ca38fb33652268565871a19a00b75869eb91215342da97629127a9babe52 Controllers/RazerController/RazerDeviceGuard.cpp +365f74e3471b0c6f06b21afc1b65b0d43bb66dec979e0431ad8b16c4d4c018f6 Controllers/RazerController/RazerDeviceGuard.h +a0038e3fda32fcb37e512efec4728cefd3d1b210bcedc1824cb1705fb64e57a4 Controllers/RazerController/RazerDevices.cpp +8686d2fc83af4397cb31e44cd446944e2395196797541340878b2c2aef1d21b8 Controllers/RazerController/RazerDevices.h +35c37b95b9f3f2243827260fa26cff2c39afff61ad04d6bf23f80fd60cd6d047 Controllers/RazerController/RazerHanboController/RGBController_RazerHanbo.cpp +b76c8442e40c3a9fbfeb8555a86820e4f6e7448de4eb4c652c619ac21b06f958 Controllers/RazerController/RazerHanboController/RGBController_RazerHanbo.h +e2007222ff16d1c0c3f02c2c9fe9144d27c3f2ad086505c558b78ef92856cb8a Controllers/RazerController/RazerHanboController/RazerHanboController.cpp +d5598bdf1abcfd78043a17205605d34e8cfd30008ca3da2adb2481d4063a284f Controllers/RazerController/RazerHanboController/RazerHanboController.h +e1925c903b310995f34cabef8dae5f916fdd2fc61948bdedd5bf4df0905b2962 Controllers/RazerController/RazerKrakenController/RGBController_RazerKraken.cpp +6af94820c5f7014ec354a89c9e3852d1aa2a4c21486abaf08cbdfebece445aa1 Controllers/RazerController/RazerKrakenController/RGBController_RazerKraken.h +983fd7fdb7509c73b1077c1d4727b0735d6b07265cf89a9de2bb1189d0142fee Controllers/RazerController/RazerKrakenController/RazerKrakenController.cpp +53ac2abb8a76bf3cf1ece4625c27b248f54273c1572c44fc1e44e69107abed84 Controllers/RazerController/RazerKrakenController/RazerKrakenController.h +852b63930880595ccecc0f0cabc25d2317f224244c816b04494e9c70ca0359b9 Controllers/RazerController/RazerKrakenV3Controller/RGBController_RazerKrakenV3.cpp +7a17c82c239021052c7f51ed73338819dda7bc677831f1dfe0dbd70e45125bb4 Controllers/RazerController/RazerKrakenV3Controller/RGBController_RazerKrakenV3.h +1982990d71abd23be4fe7a35158fe6602d96f36f68dad0a2d8c3af35f857a53c Controllers/RazerController/RazerKrakenV3Controller/RazerKrakenV3Controller.cpp +50ab0dfdf1d8dd2e2e63eac0d40074894928b70a842e05db5d1019f4422717ed Controllers/RazerController/RazerKrakenV3Controller/RazerKrakenV3Controller.h +44ea7cddff2195b336d55c120a982849244effcbca98562a8b86526ffb3eefe0 Controllers/RazerController/RazerKrakenV4Controller/RGBController_RazerKrakenV4.cpp +9cc853e254f40213aff9ae80523e57f9b4d03fc14e7e184f307e12fabe10c033 Controllers/RazerController/RazerKrakenV4Controller/RGBController_RazerKrakenV4.h +25b5a56e766c47a6c59490dd8a402f5f5dc6bec8c36772f20f2a195bd429ae3a Controllers/RazerController/RazerKrakenV4Controller/RazerKrakenV4Controller.cpp +16e157784e9fc964a539cbb9f613f1ac99aa0681905554fb663b09baa2029c4d Controllers/RazerController/RazerKrakenV4Controller/RazerKrakenV4Controller.h +ad57579f1fd9545aa3aa8c7a12598b1915c4f020815ff0d497833ee867989463 Controllers/RealtekARGBController/RGBController_RealtekARGB.cpp +2f48bac6fcd7d0a8499a73b671742be90a1a1cb9c47ce7cf51b14396e39aa7f6 Controllers/RealtekARGBController/RGBController_RealtekARGB.h +491878c00ea54fd32d8d49edf7cf975628173ff5a94993923f8dcd07d8e6d3e9 Controllers/RealtekARGBController/RealtekARGBController.cpp +272a0685d744a4fc41328a3f7d6aadbc28f481a5805aef3f117ef868ecb8065f Controllers/RealtekARGBController/RealtekARGBController.h +bd323a71490ab068eb3180c2dc91d8f61692d14116aa212889b43696b2373507 Controllers/RealtekARGBController/RealtekARGBControllerDetect.cpp +fb1d1cb9bd2d8a98c5ee3e3e2670055f18076a07c57842712d5957f57f5c16f4 Controllers/RealtekBridgeController/RGBController_RealtekBridge.cpp +60af461a6b76be661d6f9a8234df27803da27699bad8f02afd11b44bca5197d8 Controllers/RealtekBridgeController/RGBController_RealtekBridge.h +7927baf8f1329a54609f0296159487bfa8fb8f60f7f552cdcb1130a7e5d0e442 Controllers/RealtekBridgeController/RealtekBridgeController.cpp +31e81f7268eeb865fec76a22fe60bd7b0105f155d2c06cf446d5ca889760cb94 Controllers/RealtekBridgeController/RealtekBridgeController.h +fd9cdae03d7b52c46529ea986a37d3ad8533c5f0a3bee75e7bba92cda3ebeee0 Controllers/RealtekBridgeController/RealtekBridgeControllerDetect.cpp +0a7bf1c50ec2141f2614a6878cfe0c9af2e9c797c3a874ef403a81ea41e03cc1 Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RGBController_RedSquareKeyrox.cpp +d2470cfdd39ea3871ce5a400583ed8b3bad5e606f5c392016ff9fe66cde6da3f Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RGBController_RedSquareKeyrox.h +2fd65ef9bcdf47c71e818c1eaf8852e5e7bd5fa09b9d691ea3760ed1ac286ed2 Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RedSquareKeyroxController.cpp +5ec2e23292e6a815fccb0c966255454056da403831691c624a95680eed6062a4 Controllers/RedSquareKeyroxController/RedSquareKeyroxController/RedSquareKeyroxController.h +a059d0fc839a3ab09e4b9245965faec4ea9f5c7ef9353dc396a76e4d714f949a Controllers/RedSquareKeyroxController/RedSquareKeyroxControllerDetect.cpp +cf5202c863aa87f5f250090000f618049c969bd5fd7e0e59355d7b0fef6217fd Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RGBController_RedSquareKeyroxTKLClassic.cpp +9cb5963747b1ec3bde42c2bc73c920a774140248aa21afee3fb84d4c78aaf8f3 Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RGBController_RedSquareKeyroxTKLClassic.h +e60591747bb1a8e4470b8e39842d23f1db2c45013ec5449e03d99f8dbb4df641 Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RedSquareKeyroxTKLClassicController.cpp +80c7698cac8ca7ed15e01c28ddd25c6e016435cad111d3aeae1cbc069638a3e0 Controllers/RedSquareKeyroxController/RedSquareKeyroxTKLClassicController/RedSquareKeyroxTKLClassicController.h +6f50774f679ae8078797ed00d78179c38748ec1a91208c9ac944a79b02130b46 Controllers/RedragonController/RGBController_RedragonMouse.cpp +312e776ef4d3d9230ad7cf26b9727b5275908ded2f57d5e2dd80eabfbc1f76e7 Controllers/RedragonController/RGBController_RedragonMouse.h +85b1ece96354358041d5895ea24dd6d15b9a05c05eaff4bbc097ab81f9e34e23 Controllers/RedragonController/RedragonControllerDetect.cpp +fb9224d08576f0964c77e97c96d4e67bc75a8fdd391b6ef3602075fdb84ca133 Controllers/RedragonController/RedragonMouseController.cpp +407ea8a7883b4071e9e4f0a4f1785425652f929345bdb8ef3d9c5d7eac148655 Controllers/RedragonController/RedragonMouseController.h +5fc1f5719c0a72d7cd1dca6203970489e0c7375a986a6d878851f9f82247a6ae Controllers/RobobloqLightStripController/RGBController_RobobloqLightStrip.cpp +572ae982e1940e36caf15371330a85f4267408a4c2fc161b631af7228a18daa9 Controllers/RobobloqLightStripController/RGBController_RobobloqLightStrip.h +e87208f0e5ad1e1ba843cd137286bd3ffe6460feb001c1360013713d39b6e032 Controllers/RobobloqLightStripController/RobobloqLightStripController.cpp +07e0a7be30efbfcf990819ae4c07d05f18ffab4f3490ed43f944c298e27c8d03 Controllers/RobobloqLightStripController/RobobloqLightStripController.h +0cc44cae634cb30495ceca4578e9c53554419fa2a0fcf85f77c3502c1e4a1c51 Controllers/RobobloqLightStripController/RobobloqLightStripControllerDetect.cpp +5200ff8da585e847d2c927219c2873dbb58e0854c537668b7ce23c694cfbb12a Controllers/RobobloqLightStripController/RobobloqRangeMerger.cpp +c874072daebaf532cc7500f660463bc5c6f00bb18f1665103af50b3077852122 Controllers/RobobloqLightStripController/RobobloqRangeMerger.h +19af0a22e4c5f73ea7fc4434b12ac29ae201dee9f831dd38fe444f1fa133e56c Controllers/RoccatController/RoccatBurstController/RGBController_RoccatBurst.cpp +9ef4a1767a14e2d853f70bf4d5ea2f657526f5af084b180b2b27a95a6874a2fc Controllers/RoccatController/RoccatBurstController/RGBController_RoccatBurst.h +d946cd6d94c73e67fffc712098c6f6ef2c298c9ad6023f016438ec998574e72a Controllers/RoccatController/RoccatBurstController/RoccatBurstController.cpp +1f8ef852e6fe0782cdffa326a5af8e1b6ce71358cf41e993fd91a6f8ce8e316b Controllers/RoccatController/RoccatBurstController/RoccatBurstController.h +0d71af8f6f24bf476fd3c83c3e8ceda70513e93195471ee833bbd8cae154a550 Controllers/RoccatController/RoccatBurstProAirController/RGBController_RoccatBurstProAir.cpp +9c90ac8ae64dc8c145436d38b3ee89c93bec530f7f61620ca5917a01e243b253 Controllers/RoccatController/RoccatBurstProAirController/RGBController_RoccatBurstProAir.h +a3dedc1afc86e212c155216d7220e48cc8d11ad48bbb950e3ee477511b38b070 Controllers/RoccatController/RoccatBurstProAirController/RoccatBurstProAirController.cpp +d221812d9d22beca288b893a40750914191aa4b85413feb223cd56e859a1a8bd Controllers/RoccatController/RoccatBurstProAirController/RoccatBurstProAirController.h +7ebc7dd37c24c1690c191855674f411c3470d85068f36dcc987fb9fb5edd9806 Controllers/RoccatController/RoccatControllerDetect.cpp +819d65ed54ce8045aca95118dbd634c59d1a7cc9904f781d3d04e3ba52af3bdb Controllers/RoccatController/RoccatEloController/RGBController_RoccatElo.cpp +2a6dee0eb471ba11f4c2e577c42db0f53136619e22588f7d61da67ba30eede0e Controllers/RoccatController/RoccatEloController/RGBController_RoccatElo.h +9110968b87e617d57af749bc5e787619b26486f253e54a5b2be50b97b661730a Controllers/RoccatController/RoccatEloController/RoccatEloController.cpp +c9d56fa106caa01c13878da3df405f21eb25ef4a1b3f8ad40efa398140df72b8 Controllers/RoccatController/RoccatEloController/RoccatEloController.h +f187a8de68953e73f8b642e6811700a6f84294e72dfd468773ef1e8c374459fa Controllers/RoccatController/RoccatHordeAimoController/RGBController_RoccatHordeAimo.cpp +7d563d0d9c0c1e8e7127c700a5e6e2d3746c886900b7aeaf2ed5da7124a66532 Controllers/RoccatController/RoccatHordeAimoController/RGBController_RoccatHordeAimo.h +d8dabd61b3a8fbe915d1b26e54990c8d43a60da915c19dd6516323ef9e51163d Controllers/RoccatController/RoccatHordeAimoController/RoccatHordeAimoController.cpp +136a234e178a12d6a89d14d6a5e43eeede57c4af75d0a1035e4d7bb342e01891 Controllers/RoccatController/RoccatHordeAimoController/RoccatHordeAimoController.h +4fd7ba2b23be407569ccf2c9a5f9b12785f251c937525eef7113a0c552e94651 Controllers/RoccatController/RoccatKoneAimoController/RGBController_RoccatKoneAimo.cpp +92113f47c9c7f6c52f86997b650766096c2a651a04a67be36047fbb992f6137a Controllers/RoccatController/RoccatKoneAimoController/RGBController_RoccatKoneAimo.h +1af7d852711cac9ea771386a9a4cec26268075bf3e50a9bdbc59416560cbfcfb Controllers/RoccatController/RoccatKoneAimoController/RoccatKoneAimoController.cpp +5f50de1a820075f710d904605be541175eb93f90ad54507f7ed8d4abd3065f15 Controllers/RoccatController/RoccatKoneAimoController/RoccatKoneAimoController.h +95de234b46750988b554c10d4e3e5c2943ebf8e8b01c0113928eef4ea8555f4a Controllers/RoccatController/RoccatKoneProAirController/RGBController_RoccatKoneProAir.cpp +e831c5c914633e4a90625c91875be5e27a2659d632b2995431b0ab3b6be195b6 Controllers/RoccatController/RoccatKoneProAirController/RGBController_RoccatKoneProAir.h +fa99fd78de927a7e397525b393275aad4f51e59a7058f9b6074fd93202ff2093 Controllers/RoccatController/RoccatKoneProAirController/RoccatKoneProAirController.cpp +a4a55bf7015f6e3103f18d7b05490c9ee1a8f471eecf8c5a32decae624797cb3 Controllers/RoccatController/RoccatKoneProAirController/RoccatKoneProAirController.h +b69b959c50981608c46804590ece86094d2b68b3987a62c0124d509e0a1624cb Controllers/RoccatController/RoccatKoneProController/RGBController_RoccatKonePro.cpp +867624e4681031e3fcaf958ac4ee3400d0aa47ad58ebc6a282e64afbaadac1aa Controllers/RoccatController/RoccatKoneProController/RGBController_RoccatKonePro.h +df1408d5048fd57ed6bf1e42625a2320021c4cd82554420dac7e728b21b02dd0 Controllers/RoccatController/RoccatKoneProController/RoccatKoneProController.cpp +71ca613db816a7f559e8102f1dfb2ae324555ac30532450bbbc313e8183e3405 Controllers/RoccatController/RoccatKoneProController/RoccatKoneProController.h +b3e97f6d0b2b97463c8820ee0320809805d49261382b1ab449a973d8a0cbeb41 Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.cpp +776d0039b59acc82c5e11ff378e88f09cad0234a91f3cec1a2c5f168fac82c6c Controllers/RoccatController/RoccatKoneXPController/RGBController_RoccatKoneXP.h +a186066492a095954c887c775c8f0e88d2316af362e076409c87c6a8ec6d352b Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.cpp +1a52f72c6f1b47930e17c1dab8b98ec66450259dba1034d34521765269292856 Controllers/RoccatController/RoccatKoneXPController/RoccatKoneXPController.h +4631fe0a801640027edf77290076a5c1c99b95f13e458851602a0a45b364f572 Controllers/RoccatController/RoccatKovaController/RGBController_RoccatKova.cpp +9d98a4ede2e9661a86ed8c15ab81c338de0f4d767ec83d8072bb9671c7f9db19 Controllers/RoccatController/RoccatKovaController/RGBController_RoccatKova.h +1d4a7886289bb27bd4ebb3f88a4be49591d173489ce6aa879b3c52c3a8e8d9a0 Controllers/RoccatController/RoccatKovaController/RoccatKovaController.cpp +e0872ed7d40179ecab64d54e43dc00267636f8214c1d985e311c2bfaa685f74b Controllers/RoccatController/RoccatKovaController/RoccatKovaController.h +59114cfdf71a2323e79ffb1fbcd7c023a4f91d141b4d2d3ead384d9770ba5aad Controllers/RoccatController/RoccatSenseAimoController/RGBController_RoccatSenseAimo.cpp +d0e756715e861d3d2a4ff8e47e402e9d4391cdc7167adffac15f9d2732412186 Controllers/RoccatController/RoccatSenseAimoController/RGBController_RoccatSenseAimo.h +7c43c8a6f0d6c98a1e9f94c7cf50aa75f8987e72d7b82a056e4294691d3b0953 Controllers/RoccatController/RoccatSenseAimoController/RoccatSenseAimoController.cpp +711a0e3b87c361efc4534ae4c872400d06cfa7601bdfd2a000166225af5ca21d Controllers/RoccatController/RoccatSenseAimoController/RoccatSenseAimoController.h +b04f9662616cef718aa78cf0c83c88a7f19e1314ea81cb904ac70eeeedcc48ca Controllers/RoccatController/RoccatVulcanKeyboardController/RGBController_RoccatVulcanKeyboard.cpp +9aa68294a79ecb3cf9f1f845c68ead02c8e5cb490fc55f5e3a75629615aac04f Controllers/RoccatController/RoccatVulcanKeyboardController/RGBController_RoccatVulcanKeyboard.h +1313c61a94e64ea279c33805f922da620fb9dcba47268c30c113b8180c0abd93 Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardController.cpp +bb165bc490f7aead935d211237ee4e401a9516abe715cb78da25fc5ce67af34e Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardController.h +e65aa8d22f92edc4cbcabaa0fb9be40f5e43c774daaaa84c3aebe13b82199057 Controllers/RoccatController/RoccatVulcanKeyboardController/RoccatVulcanKeyboardLayouts.h +656ffc3022299ea20adad310152f233665cffc8cb9ca36013c7057ebc4b5e0e7 Controllers/SRGBmodsController/SRGBmodsControllerDetect.cpp +5af41a8ca2ddf6865c9171139641dcd8b77da6ac93b9c6030c950fcd537da87a Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/RGBController_SRGBmodsLEDControllerV1.cpp +c3debb9f3f02d3f5a029f9012ba54cd4c518e752bb2cc118f92517e0e8f5501d Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/RGBController_SRGBmodsLEDControllerV1.h +a956fb5d6d74adad354641a03fd637597c9166ea6f084995797170dfec81b642 Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/SRGBmodsLEDControllerV1.cpp +e14d300e23990d46297ec8af1baac3d0f6c3a1b4f3a860568ca817ff5735cdcc Controllers/SRGBmodsController/SRGBmodsLEDControllerV1/SRGBmodsLEDControllerV1.h +e03ae20a9f9d83b5d97431973cbd093c3d965da43f2eba181e06f93da2b369bc Controllers/SRGBmodsController/SRGBmodsPicoController/RGBController_SRGBmodsPico.cpp +9fa2d67cc61f886bde0c095cbc7e6acf0167b140b45ef78c05d4411c1a87b1c5 Controllers/SRGBmodsController/SRGBmodsPicoController/RGBController_SRGBmodsPico.h +9bdcccfbc5d2aee6320fd30468df7e0c5ef355666eb0a318742b084a1d72c56e Controllers/SRGBmodsController/SRGBmodsPicoController/SRGBmodsPicoController.cpp +279561f9fe9a6e9e569693538c53b8ed2b35158f701b317b2773f10b1114cd71 Controllers/SRGBmodsController/SRGBmodsPicoController/SRGBmodsPicoController.h +11f15c7a8a6019e8f4f8c64a14aab47d63db31f6276499288965298412db9ef5 Controllers/SapphireGPUController/SapphireGPUControllerDetect.cpp +e48c9584c2f5bc559dc99b27739ded7bee151d0b8f7cc33641b1981d791f6dd8 Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/RGBController_SapphireNitroGlowV1.cpp +94b8b0dc60ebfa8984f30e1ff5c182eeb081bffae85b3f6989a9ab091df229ce Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/RGBController_SapphireNitroGlowV1.h +a96d14ca849570dadd98cf4946ce6cac3938af5bfa6c457cdeff4242558f8c51 Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/SapphireNitroGlowV1Controller.cpp +d9e38dd56258329ef24659181a44570a971d3caa769d03e68a7ba4584efd1ebb Controllers/SapphireGPUController/SapphireNitroGlowV1Controller/SapphireNitroGlowV1Controller.h +42b17e97d201de84d0b123c7edc2945fe9ed5316fe97715248c9fbe663477df0 Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/RGBController_SapphireNitroGlowV3.cpp +eb4dbc29f0d66ae8699dfc458ed56790c9e4b4ce31617ca353c9d65be4f83157 Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/RGBController_SapphireNitroGlowV3.h +a6d5fe1e87f40cdaec1de848feaadc2a949ec0ecbf939c22c9e831e951bdd1a6 Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/SapphireNitroGlowV3Controller.cpp +342ea60d1d0d2c4094c120d2845979a8038d5b4bd8de9b382e322f7c964cb091 Controllers/SapphireGPUController/SapphireNitroGlowV3Controller/SapphireNitroGlowV3Controller.h +5b541962017a2e1e6866d70031352e4309c3f2dc5863ed04425e807cf1feccfe Controllers/SayoDeviceController/RGBController_SayoDevice.cpp +9f21f851cb475050ee31f163ef6c57f5af1ac1f60375338eaf277a3bf9752d38 Controllers/SayoDeviceController/RGBController_SayoDevice.h +682c8fc2f2f7338bc4c84771a9a6d9e542a8d06251eb7e2b22d0b895c2206764 Controllers/SayoDeviceController/SayoDeviceController.cpp +a2ba37d235dafbe2b7b8d1b45d22c6abfc460e71a653de73db2471a92e3262be Controllers/SayoDeviceController/SayoDeviceController.h +e1bb93b8d220a637b85a921a61ba67318f6f57d16463164df3bd27e549443c44 Controllers/SayoDeviceController/SayoDeviceControllerDetect.cpp +7d4d5905e5c6e78d2414efc02cec5471f6431199d531cd40336a02fd7d6674dc Controllers/SeagateController/RGBController_Seagate.cpp +8aa7977afbedc599593fc2c12b7629988298cf0a68812a48846b85279f7153ba Controllers/SeagateController/RGBController_Seagate.h +2fb5b6fdc6d132a2cc89ca162154e445c7031ccf940cb26ecd064524976f3321 Controllers/SeagateController/SeagateController.cpp +e8ab6f0f6f6298c432982d400a24dcdd09bd7adf79dc873f8899bdc410a31926 Controllers/SeagateController/SeagateController.h +423e2bfbc6aa760cb35fc125ae24f2f62deb30cb7b8fe8047272a5366bc7f6ff Controllers/SeagateController/SeagateControllerDetect.cpp +b6bc5f940da3cc92dbde8f6ff162a364245820d766bf6fbf50058ff12661a13f Controllers/SinowealthController/GenesisXenon200Controller/GenesisXenon200Controller.cpp +2d7fe6b7ebf743df00c1b9af02621f25424e174e188b9506e0c07ca8a384fb2f Controllers/SinowealthController/GenesisXenon200Controller/GenesisXenon200Controller.h +de6927502635a20d54b2b1c9cf900c80c2492a3b468d3e930d0c96271b844c40 Controllers/SinowealthController/GenesisXenon200Controller/RGBController_GenesisXenon200.cpp +6166c344c4bdb771af9c8f89d1e3ae08126b7f485823bdd86fac9cd4c5418607 Controllers/SinowealthController/GenesisXenon200Controller/RGBController_GenesisXenon200.h +abfaef144be727ccaa561d9662fcbb5739c8f96a658f2ef6d9546f0329302cbf Controllers/SinowealthController/Sinowealth1007Controller/RGBController_Sinowealth1007.cpp +cbbf51af8b9fc9e736a6b79eb8197021da856edfad322263fc633cd9826f747e Controllers/SinowealthController/Sinowealth1007Controller/RGBController_Sinowealth1007.h +2109b6dcc9db4035392057aac53d13e51710d2d09049138663cc6ba30197bb36 Controllers/SinowealthController/Sinowealth1007Controller/SinowealthController1007.cpp +d2b0e02022df9b40d034fd2639f9d3394aace19e534f2673fcb1ad240c221037 Controllers/SinowealthController/Sinowealth1007Controller/SinowealthController1007.h +50b6694d2251256ab9847a476f1d7540965026daec63309fb4c1a1cae8eedf4c Controllers/SinowealthController/SinowealthController/RGBController_Sinowealth.cpp +ddc24540dc0657af8112719fd4166693b006c72a070652461090c8b0f531865c Controllers/SinowealthController/SinowealthController/RGBController_Sinowealth.h +9fa2517865adba63c70969fd52ab5335aad83685af752a327f2620d623109f16 Controllers/SinowealthController/SinowealthController/SinowealthController.cpp +365048affa85aa1e153af644b5bcaa964895fec7aadee835e35fa1f6f3a4de70 Controllers/SinowealthController/SinowealthController/SinowealthController.h +9d0ae85a84a46bf32deea23cac6d7f745c24bc872bfa1d69fa9b6e944426970f Controllers/SinowealthController/SinowealthControllerDetect.cpp +546f49653b20e26e226ee4cd96e7fd7f372da0d41215ca59ce5429aff32ba443 Controllers/SinowealthController/SinowealthGMOWController/RGBController_SinowealthGMOW.cpp +96bbd3c73fe7ddf5106c044f3fc52247e319c3529150aefcbad887c1e864ec3d Controllers/SinowealthController/SinowealthGMOWController/RGBController_SinowealthGMOW.h +3b65749f5935ed17d529c2ada5d95b0177a1b2cbcc73d15aa33ad86403b31364 Controllers/SinowealthController/SinowealthGMOWController/SinowealthGMOWController.cpp +62dd38f778a8d50c7e7ea87ff9d3784abe4ee771dd4d029f13109bf7ef77f9ce Controllers/SinowealthController/SinowealthGMOWController/SinowealthGMOWController.h +a24082bb5637a017505f858a7645e4a64cd30bb7b0415778b0c189a74a1dfa83 Controllers/SinowealthController/SinowealthKeyboard10cController/RGBController_SinowealthKeyboard10c.cpp +2183aaa7ada87493832e6e0faa95bd4273b3cf8b0a851aec2530ba60bfa653df Controllers/SinowealthController/SinowealthKeyboard10cController/RGBController_SinowealthKeyboard10c.h +b561c4f3163454fc3eb52ceb053477363e4f903f3e5ce63020b94b9c0d99f22e Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cController.cpp +1c92175e74b16400b441ca9b8d4c141e81cfd6e27019984dddab82628a8dc681 Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cController.h +fe6f31bee856d3f2cbec2aa4632a319b3f05d80b62874e46d977291eba7effe0 Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cDevices.cpp +9c730a61381ec90552b364192e577dba2d7bf70468d6240ffa591af0b6a41023 Controllers/SinowealthController/SinowealthKeyboard10cController/SinowealthKeyboard10cDevices.h +70236f23866ebd81757beaed1bb4be5758207d23ba53969ae0ccce2748a4cfa6 Controllers/SinowealthController/SinowealthKeyboard16Controller/RGBController_SinowealthKeyboard16.cpp +c8f5764eee8a1c6df73a9aeee3d6683e10ff274a7d7c9fc7dc9455873040b274 Controllers/SinowealthController/SinowealthKeyboard16Controller/RGBController_SinowealthKeyboard16.h +906ebf1dc8fa3b5c21ed6318ef341bbdb453199522ae243c17823daf6da15937 Controllers/SinowealthController/SinowealthKeyboard16Controller/SinowealthKeyboard16Controller.cpp +1a9c0828092ff3d13c90adabdddefab0958042d4b51e67a2ee6f207c64da2a57 Controllers/SinowealthController/SinowealthKeyboard16Controller/SinowealthKeyboard16Controller.h +76f0a778d3d53d36680a87c9a9d9ccccbcb9b0f28485fb0490595ead153b6126 Controllers/SinowealthController/SinowealthKeyboard90Controller/RGBController_SinowealthKeyboard90.cpp +76a6330b94560dfd4d300e27eed1e5fbfeb1235168655b73838ca95cb030bfad Controllers/SinowealthController/SinowealthKeyboard90Controller/RGBController_SinowealthKeyboard90.h +cb4501960e9a5a6673364a1920839458dd32afc981599b9f0483bc0454805afc Controllers/SinowealthController/SinowealthKeyboard90Controller/SinowealthKeyboard90Controller.cpp +923f4f1338df9dac166a268721381b03506c9932f111071453fd9a484735f3ab Controllers/SinowealthController/SinowealthKeyboard90Controller/SinowealthKeyboard90Controller.h +45e3f8050bd9ab9b83ad09b598778a9597ce37980ac9d1fc5967e89407c36638 Controllers/SinowealthController/SinowealthKeyboardController/RGBController_SinowealthKeyboard.cpp +757e1dd9c1d236f59dbc6b620cade91c2bf235a44e1b71e230238f06bd476045 Controllers/SinowealthController/SinowealthKeyboardController/RGBController_SinowealthKeyboard.h +6a212ea5e00055d7430977c4051ae3193373168f52ed6c6f5fcfb55e83ee4bef Controllers/SinowealthController/SinowealthKeyboardController/SinowealthKeyboardController.cpp +a0874cc0bd62b4e10b8f2e4b7750a6910216001c5381d32e5184a277c3df81a1 Controllers/SinowealthController/SinowealthKeyboardController/SinowealthKeyboardController.h +b3842038e02b0fa4ef9fb6d4becfc590d9c87ea66b3899b79dc8a1f75993cf02 Controllers/SkyloongController/RGBController_SkyloongGK104Pro.cpp +effd6e10e31ca85db1c6a30c9a9e1c4f4ce6e60028113b8b1fd068a16b74f1fd Controllers/SkyloongController/RGBController_SkyloongGK104Pro.h +f867fd2e98c9cef4ed380eca8a4c91accfc7e941d76777b5ba0791c155887b8b Controllers/SkyloongController/SkyloongControllerDetect.cpp +a21eb7b0c0e23b0e462b3fa6a61eee5d6324271676283ee5cef77b8c965e5ef2 Controllers/SkyloongController/SkyloongGK104ProController.cpp +bba9572d09de76d586d4e2c2623f687539a98fbb7317e6bc91891b0bf57030e7 Controllers/SkyloongController/SkyloongGK104ProController.h +ba0ad3c71be549051435e7ba1528227feb5d0046c1a4c4dc1713f0e7b9be9b9f Controllers/SonyGamepadController/SonyDS4Controller/RGBController_SonyDS4.cpp +25858c98a71fd1697fe4d72ae07914f660f38ef2628aa645b8e2d9be87751581 Controllers/SonyGamepadController/SonyDS4Controller/RGBController_SonyDS4.h +067d6ce54ff0e2e2bd39a60223d98a1c353dee4b48805c8c9614f38e6c720fbb Controllers/SonyGamepadController/SonyDS4Controller/SonyDS4Controller.cpp +48503ef2305face4a2d607bca2c3d2eb645cfff157fcba2e79a88a4f2a9766e4 Controllers/SonyGamepadController/SonyDS4Controller/SonyDS4Controller.h +9277673f8f115002d6050c6245a54df870957d940c350bae3a5a3da8565043bf Controllers/SonyGamepadController/SonyDualSenseController/RGBController_SonyDualSense.cpp +9e60bd346e873f986ec1391d35ed270d0673cd4a998d4bbf9346f6f5e8ee61f4 Controllers/SonyGamepadController/SonyDualSenseController/RGBController_SonyDualSense.h +82904b4623cce8843fa561aff8a5787244ff270c2634f65cdd6a5f294a76906e Controllers/SonyGamepadController/SonyDualSenseController/SonyDualSenseController.cpp +43ea324204a17147c3b23489712ce0923f8af9fcdcb187e3098e03d69f89f0a9 Controllers/SonyGamepadController/SonyDualSenseController/SonyDualSenseController.h +dd933a2e9c84eb348a0e95bd1772ad75f6161f06fca5b47698914f9d6f6defdd Controllers/SonyGamepadController/SonyGamepadControllerDetect.cpp +c2fb0bc6e79e79984ecee8ee6be60071247ea92a6f60796c3ed270064116c512 Controllers/SteelSeriesController/SteelSeriesAerox3Controller/SteelSeriesAerox3Controller.cpp +0a775b339542d44a15ea087614b73cac5ce484c4b94d01e5959c230053206eb1 Controllers/SteelSeriesController/SteelSeriesAerox3Controller/SteelSeriesAerox3Controller.h +5301403587d73815fab3ed46e7743b2979ddab8fe49b736f39ff1d45dc07df67 Controllers/SteelSeriesController/SteelSeriesAerox5Controller/SteelSeriesAerox5Controller.cpp +8f9f030b12c3fff3bcd29b1d46047a6aee5107262d9f8359a576272a52b759d3 Controllers/SteelSeriesController/SteelSeriesAerox5Controller/SteelSeriesAerox5Controller.h +7d4a879d913cf0ee25470496a9937effd2e70db644f4b44330172093b0b3d636 Controllers/SteelSeriesController/SteelSeriesAeroxWirelessController/SteelSeriesAeroxWirelessController.cpp +f957fc74ee72aedd85c97edb1ff1c3c3eeacf4c6ba985555024838bee2a85904 Controllers/SteelSeriesController/SteelSeriesAeroxWirelessController/SteelSeriesAeroxWirelessController.h +e64fa040ddc18974670e65b9f8b21b7eb5737685783c5585964d2e968a080da9 Controllers/SteelSeriesController/SteelSeriesApex3Controller/RGBController_SteelSeriesApex3.cpp +bf36d3d52a6f7aef963bf7c0c891cf500f4ca311dd9235cbecb160e76a427c70 Controllers/SteelSeriesController/SteelSeriesApex3Controller/RGBController_SteelSeriesApex3.h +e2506bb50f1a5cbcddc6b52e12f3b90c3aa5e8d42e529879cef77558876fb238 Controllers/SteelSeriesController/SteelSeriesApex3Controller/SteelSeriesApex3Controller.cpp +a19d4f8cfdcb17957935ca66160f0ef796ba10c667a8af5cc5a380edcf31addc Controllers/SteelSeriesController/SteelSeriesApex3Controller/SteelSeriesApex3Controller.h +f43e8d268d225667639e1741d6454452ac27eb08e40ed76c76f0a527aeba5bbf Controllers/SteelSeriesController/SteelSeriesApex8ZoneController/SteelSeriesApex8ZoneController.cpp +a91f268ec7c2dce159dda15e32a7d57d4e1f057f76f846207410c9cf80d6cb1f Controllers/SteelSeriesController/SteelSeriesApex8ZoneController/SteelSeriesApex8ZoneController.h +a37f0da93b541164016ff87fcc778bd37bf22f764422f2eff00cab04bc3b2a83 Controllers/SteelSeriesController/SteelSeriesApex9Controller/SteelSeriesApex9Controller.cpp +4ce739acede1ca367508096519d592a2c4c0d308bb1fb6a8cc900cf4c4a99ff1 Controllers/SteelSeriesController/SteelSeriesApex9Controller/SteelSeriesApex9Controller.h +def5e701c727ea3a69d5264673273cdbeb9acd02de4d2581c5c915bcee6cde35 Controllers/SteelSeriesController/SteelSeriesApexBaseController.cpp +8f6e774f6ad3c3c500aa2c108edcf2c04227bb6922f086fdf19895936ec510b7 Controllers/SteelSeriesController/SteelSeriesApexBaseController.h +d3adb8e5f13829c97962c5ece1e6f4288c2d0ebfb7e5681913efdaa363333b11 Controllers/SteelSeriesController/SteelSeriesApexController/RGBController_SteelSeriesApex.cpp +33f3d0fca0eb609d58f4507502f97366634e2275c7c44d31f668320b20dd9625 Controllers/SteelSeriesController/SteelSeriesApexController/RGBController_SteelSeriesApex.h +0571bab6a3781ffe912e04d60aaa36291dd5989cd3356a8bd75e78677ea1278c Controllers/SteelSeriesController/SteelSeriesApexController/SteelSeriesApexController.cpp +f02abf5479e8d60e09b464bd061b9a233006acee33049a473788405777a6a09e Controllers/SteelSeriesController/SteelSeriesApexController/SteelSeriesApexController.h +48d0a7dca0ac8792160d78a8f2326cbe6f621bc553be04b5f0ce875eabf19f50 Controllers/SteelSeriesController/SteelSeriesApexMController/SteelSeriesApexMController.cpp +964bf74afdd9a6dc93a01b95c2128d7dc5f740aee836d267f9785f1f4c75b6a1 Controllers/SteelSeriesController/SteelSeriesApexMController/SteelSeriesApexMController.h +925696c3ec7d5921a5d0bb97c183125827380634c2814409d70c028acce96774 Controllers/SteelSeriesController/SteelSeriesApexRegions.h +7577cffeccfd477062ae79d71e70663a48d3dac352de1aa95d6cb0c1b6397cf7 Controllers/SteelSeriesController/SteelSeriesApexTZoneController/SteelSeriesApexTZoneController.cpp +d16d0a2efe44e469e07793737b537cd7aeda4644a1892376f0b96c4471af8ebd Controllers/SteelSeriesController/SteelSeriesApexTZoneController/SteelSeriesApexTZoneController.h +a5b142c49e881432c760abe858c9d7422c3921d5f176a71c1642ae3194d5aa2d Controllers/SteelSeriesController/SteelSeriesArctis5Controller/RGBController_SteelSeriesArctis5.cpp +abfed454c2560c64dd1938104275bdb6f2be7d3ffafdb37b08d442a22b7a2781 Controllers/SteelSeriesController/SteelSeriesArctis5Controller/RGBController_SteelSeriesArctis5.h +81fdde95b722e0db649d31c3e7d8995dc25f089729465f3d1dfa70ebac6f6517 Controllers/SteelSeriesController/SteelSeriesArctis5Controller/SteelSeriesArctis5Controller.cpp +13032a5ddee7a015ce123562a86e4468a22a47cca13a9f32e1c8626f23b87df5 Controllers/SteelSeriesController/SteelSeriesArctis5Controller/SteelSeriesArctis5Controller.h +b45db07231bad641edf0a6a85d297b37caaf668b9b3934a7e3412663d7198789 Controllers/SteelSeriesController/SteelSeriesControllerDetect.cpp +4ad167c15f63e44ced03e02369aaa43e23409a8a15423573fbd1d3b94a5846ac Controllers/SteelSeriesController/SteelSeriesGeneric.h +f75315ed3f690337832c8991e3ca210eb6fcf64bd9754d6f105f601a930c06c2 Controllers/SteelSeriesController/SteelSeriesMouseController/SteelSeriesMouseController.cpp +13962d73b27ae7da5c4d0db70811710063d939aac2a9c069a301745c624c7746 Controllers/SteelSeriesController/SteelSeriesMouseController/SteelSeriesMouseController.h +0781ee54b8288044fdb6149229109442645524c6dfe4b16637bff7363d0a7394 Controllers/SteelSeriesController/SteelSeriesOldApexController/RGBController_SteelSeriesOldApex.cpp +a66a6e0b09e6ce17df1c26dbb062947c86b43522ad05bc07b2833833dcf3fee4 Controllers/SteelSeriesController/SteelSeriesOldApexController/RGBController_SteelSeriesOldApex.h +425c8177580c406756ca3e1271a80c6761790e3cb6081d199b3c3a8235fd4f1b Controllers/SteelSeriesController/SteelSeriesOldApexController/SteelSeriesOldApexController.cpp +bb7efcb65a7957b4f3940e8a99a5be83b68015beb529a5db0469637ee082aed1 Controllers/SteelSeriesController/SteelSeriesOldApexController/SteelSeriesOldApexController.h +ed298b1bef5b865fdc1963ab45d482220a4e29a83e5bbb08dd134f74db2e6645 Controllers/SteelSeriesController/SteelSeriesQCKMatController/RGBController_SteelSeriesQCKMat.cpp +4698450164d74aa75856ad54bfaa040e14c5f60eafec846b563d6301f7ae6cbd Controllers/SteelSeriesController/SteelSeriesQCKMatController/RGBController_SteelSeriesQCKMat.h +1151ca6df3a45e9036c5e4e1679e16846f8d569449c1cb9d19a46f45c0117365 Controllers/SteelSeriesController/SteelSeriesQCKMatController/SteelSeriesQCKMatController.cpp +05602b88159cce3f8a5b25ccee9b0bf8f934af45fabdaf3bad44e4311d0ab004 Controllers/SteelSeriesController/SteelSeriesQCKMatController/SteelSeriesQCKMatController.h +40b2da5ee7124ef08db5306814ede69e031f1b6a6f1ac282f128709fd802498b Controllers/SteelSeriesController/SteelSeriesRival3Controller/RGBController_SteelSeriesRival3.cpp +1672e815d9be0f972b6d8e8bc1cc54f78ce9659b5aa990ab06f8687c10b12a04 Controllers/SteelSeriesController/SteelSeriesRival3Controller/RGBController_SteelSeriesRival3.h +02231fd0fc425eb67b07fb8c176a0bc15ef5e8f55641086af5c006e06ad6f0bb Controllers/SteelSeriesController/SteelSeriesRival3Controller/SteelSeriesRival3Controller.cpp +d942f6e4bbf526660e51e15cd47b1794535c982d15bcd6abc21339d5a90fb617 Controllers/SteelSeriesController/SteelSeriesRival3Controller/SteelSeriesRival3Controller.h +0e8d562e92b02f7c3d65cf168489462adf902aaaf2c8cb516bdedac16984556c Controllers/SteelSeriesController/SteelSeriesRivalController/RGBController_SteelSeriesRival.cpp +53fb057515a5f8e8590f5bd335bf88bde317d749b872a6f7ab2c56a4bfdbcd14 Controllers/SteelSeriesController/SteelSeriesRivalController/RGBController_SteelSeriesRival.h +818ded415284480816bd0e08a916eeba7ef245c8228c10860e60cc02b749366e Controllers/SteelSeriesController/SteelSeriesRivalController/SteelSeriesRivalController.cpp +bc028940e5058300e8c2dbad72a28b51a04745423d712f09c4084a5d58260487 Controllers/SteelSeriesController/SteelSeriesRivalController/SteelSeriesRivalController.h +a484ffe848c7aed36864442853ebe390aa40fbaf2935b9e93a4c77934294f0ab Controllers/SteelSeriesController/SteelSeriesSenseiController/RGBController_SteelSeriesSensei.cpp +ce60ab5b0699c25bf397d01dc1093d8dcd06763b665729d65e1c8cba520d9fc3 Controllers/SteelSeriesController/SteelSeriesSenseiController/RGBController_SteelSeriesSensei.h +c7160c57f122386fd7dff894b37315f00ce4c113db93de345bb667a7d9cb3dff Controllers/SteelSeriesController/SteelSeriesSenseiController/SteelSeriesSenseiController.cpp +5bee3cc9a9bdb47db2e9063fb2e3e4ae50ebdb5db044514497600ca240e8ca03 Controllers/SteelSeriesController/SteelSeriesSenseiController/SteelSeriesSenseiController.h +45e2d651718cc00a8b332980832f3d121c2a61defaf3c9a6388547fcd94e695b Controllers/SteelSeriesController/SteelSeriesSiberiaController/RGBController_SteelSeriesSiberia.cpp +c8ac51cc2a2acab9b3928796a3f6ce0efc05a9f0cf4cf4a19b13870d7c9f324e Controllers/SteelSeriesController/SteelSeriesSiberiaController/RGBController_SteelSeriesSiberia.h +beeb334a82d72f77fa2d27d6f45fcb4b3e82ce86913f3faa055b6445be2c696b Controllers/SteelSeriesController/SteelSeriesSiberiaController/SteelSeriesSiberiaController.cpp +a7caa7e0436b9f9b6736722cb8b1d6fdb98f1d20bc4373396b9dded0c29e78d7 Controllers/SteelSeriesController/SteelSeriesSiberiaController/SteelSeriesSiberiaController.h +c32b139c70e5c752565a2772ea9b93994adde1fdff7bd59949e3858d9ed59416 Controllers/SteelSeriesController/color32.h +09122ea2881b59c05b20703a01112d0ae17df90c4c10855777382a6815c10568 Controllers/StreamDeckController/ElgatoStreamDeckController.cpp +a111b9226d40f2c8c44fd19905d3781da494ed8bcdb94c8396099b94fef19fbc Controllers/StreamDeckController/ElgatoStreamDeckController.h +9ff11f28bb65c862faef15c8a0156ebe2f4456383560604ca6ef5f022097dfa3 Controllers/StreamDeckController/ElgatoStreamDeckControllerDetect.cpp +89e3347e294acb844a6202def0865fc94fc5702ace8b8700651b6b9ce0ebe613 Controllers/StreamDeckController/RGBController_ElgatoStreamDeck.cpp +01756998adbc7d79b6f63fca8840ce6dcdea14c6ac821c5dc3528a36606df648 Controllers/StreamDeckController/RGBController_ElgatoStreamDeck.h +df3d9334b769901066be8e37ade392865e61c12a07ccfb37bde6ef1d0aba4a88 Controllers/TForceXtreemController/RGBController_TForceXtreem.cpp +1141187c7414014796c9dbdca27c853728f3997c58ea68582a371662d1cecb0a Controllers/TForceXtreemController/RGBController_TForceXtreem.h +7f20ed83d36ce9807870257f35b78c91fee6394bf413627aebf32f2277abcda1 Controllers/TForceXtreemController/TForceXtreemController.cpp +d9758159a7bfe23cf61e62e9c82b1c8151b12e8b2e88a51d8fffa437c1b2d5f0 Controllers/TForceXtreemController/TForceXtreemController.h +389b79475e8b46c42db72b1a56eb670a7c5d2387aad74cc56915cbb63079bb5e Controllers/TForceXtreemController/TForceXtreemControllerDetect.cpp +a479241b293acab8ada1e27d493d4b668f7ad64aa80e838b57262b6421e9dcfc Controllers/TecknetController/RGBController_Tecknet.cpp +b3bbcc09fc7fbd894d8e8fd0bc5c6d5610da56c82ae71cf15c04bcb12b3cbc29 Controllers/TecknetController/RGBController_Tecknet.h +699beb3ceb545e2a3cf39daeb1a07d652d523c3a1efcbf9f3e2e6fce8afbae4c Controllers/TecknetController/TecknetController.cpp +f1d0ed75aba34c1df9974961820d7eb324f169dbff28a0f0c216989bd7a5bb69 Controllers/TecknetController/TecknetController.h +4d336894409164f9ccfeed50a79712b20c2c0a619cd4e8cbeed30df0d671616e Controllers/TecknetController/TecknetControllerDetect.cpp +fabcf6a7cd18c4a49fb025c4d3cf072e52da9f6c5a64db021d50946cad1300d3 Controllers/ThermaltakePoseidonZRGBController/RGBController_ThermaltakePoseidonZRGB.cpp +74acea319deb0e334a6e3d6ac8a2446c19d24c913e1481e41be158a73edbc7d4 Controllers/ThermaltakePoseidonZRGBController/RGBController_ThermaltakePoseidonZRGB.h +9e5090b7d5ff23c13f568738074b8a3cfbb6a9e57b73dcd05d898ff846e2b117 Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBController.cpp +7bad0402c84370f91263f972027d975251767ba1fc322848117df6432cf7f371 Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBController.h +114dbec16f63f64be47ad2abc48369efa96456c60713f1f9107c206ac44b1e5a Controllers/ThermaltakePoseidonZRGBController/ThermaltakePoseidonZRGBControllerDetect.cpp +bfdcb0f07719f46e944fd4601882750b5d4ba4f987855023ce4464871d404a99 Controllers/ThermaltakeRiingController/ThermaltakeRiingController/RGBController_ThermaltakeRiing.cpp +2139f95ccf773dff98688228dbaa272cecaecc12592c85be732a385c8fdf7ed6 Controllers/ThermaltakeRiingController/ThermaltakeRiingController/RGBController_ThermaltakeRiing.h +d71cdf41a949baa32b7d22e2dfe3d9896ca0ed44985460037e71a6c4a4dcdc2d Controllers/ThermaltakeRiingController/ThermaltakeRiingController/ThermaltakeRiingController.cpp +131054a3b297121bd7d1cff4214a67a490f96c2e4bf440a813c538ce5ff77033 Controllers/ThermaltakeRiingController/ThermaltakeRiingController/ThermaltakeRiingController.h +a8d6b8752eb4b795149bf87e43120cffc951fa0af205625d2eebc84fb591cd92 Controllers/ThermaltakeRiingController/ThermaltakeRiingControllerDetect.cpp +0340b2dd5e770390b7def1f760f3a8d3e5351ab3e878bc12d6be7893ae3bb575 Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/RGBController_ThermaltakeRiingQuad.cpp +e1bc777a6db4b6acc0712b436dbfef124e0264abb493580f2b8f39ef3225e18a Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/RGBController_ThermaltakeRiingQuad.h +f68207596f752a41b0b2eb2fdedb3a1d64f335c0ecaf3000445dde6c95ca6b08 Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/ThermaltakeRiingQuadController.cpp +04e69c112aeb8b101fd854d7e3edf8eca9fc356568352d1023f89013f79fb2c2 Controllers/ThermaltakeRiingController/ThermaltakeRiingQuadController/ThermaltakeRiingQuadController.h +ca5f130f0e98c4ee459aa4acacc01a9d6e6266579fe03eb1493a2728458a093a Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/RGBController_ThermaltakeRiingTrio.cpp +0cb02d3a0ca73bc7f20f22a850079fcfd1b13cadd033bc239afc129e28e6e8ba Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/RGBController_ThermaltakeRiingTrio.h +e7c496dea1b8994bf4f7e9f194004f6d942cb14dafad2156b6b3fb323db8b586 Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/ThermaltakeRiingTrioController.cpp +688d02b16f17d7ec48e5463f6b8206f38f006c693661cc45105b3a01bdf28f87 Controllers/ThermaltakeRiingController/ThermaltakeRiingTrioController/ThermaltakeRiingTrioController.h +a19263fd5bdb4a829a0a2ce1fc182fbf9265a3dfe293b6b8419abbb4f051b7e1 Controllers/ThingMController/BlinkController.cpp +8ca95d52d984d31492ee99d6dfbf50a897936bb0f99067726b9f36b6b77acf91 Controllers/ThingMController/BlinkController.h +0d70130b076634f59f0e4184ffe453325150da0118d7094b988aeea9ea4a3ba1 Controllers/ThingMController/RGBController_BlinkController.cpp +829fc29d60ddb882822425ae647e740f7fdd3384e864069df331c01a684171ab Controllers/ThingMController/RGBController_BlinkController.h +2d7c6017cda3cc0d8c913111b6f62409e9c5e3158784c9f25e28a730cd0bc800 Controllers/ThingMController/ThingMControllerDetect.cpp +7ce347a2351d6403519a82adf1dbb9f659863d389c176ef1dfba3763a369bf4e Controllers/ThrustmasterSolController/RGBController_ThrustmasterSol.cpp +2207c6cbb49025b1daf2d31b387c827497b42857662dfc30ec6a02d2af6d842e Controllers/ThrustmasterSolController/RGBController_ThrustmasterSol.h +b47aefd5cb22555f2c679ae077dc7105dd095f55a6cae5c69fdf8393e631b43a Controllers/ThrustmasterSolController/ThrustmasterSolController.cpp +63dcf8b048ba96308a95b001f8af5340bdbf8686677e06f1f9e7454123a69979 Controllers/ThrustmasterSolController/ThrustmasterSolController.h +782658e2dd3bfb36552215ad773cc1544840f4ec75c64976f51b822ba61a7ea0 Controllers/ThrustmasterSolController/ThrustmasterSolControllerDetect.cpp +ff7f518be37845f472254d64299e122162d3618c6692bf25d19306473cc93ae9 Controllers/TrustController/TrustControllerDetect.cpp +50fcae68cdc433c975a91d15972c21cac6686c473df589204d59bf1d400a431d Controllers/TrustController/TrustGXT114Controller/RGBController_TrustGXT114.cpp +4f4aa2bdbe8c430060ccf9655613f9d1cadd391a92446192a1ae32539148682b Controllers/TrustController/TrustGXT114Controller/RGBController_TrustGXT114.h +696b384acededb0eb3a24cc9deeef9eafbb8890a49d226235827152c4ac6533e Controllers/TrustController/TrustGXT114Controller/TrustGXT114Controller.cpp +2c1b970ed0113283a6a454792c1668ca5ae7d7f312c1cf5ab3089b8a769de725 Controllers/TrustController/TrustGXT114Controller/TrustGXT114Controller.h +b3e64602b63adc2cd880ae9982a74dbecbcaa3397362012b61e0046aa2713d22 Controllers/TrustController/TrustGXT180Controller/RGBController_TrustGXT180.cpp +44a2a432be7263fe2d8bd76e0c0ed181036629983f1185f063a2651e58869ac8 Controllers/TrustController/TrustGXT180Controller/RGBController_TrustGXT180.h +d2d01c0b570b60dead81956c8690fd0f16e3f737bd5ad5ddffc61f52a9c9c585 Controllers/TrustController/TrustGXT180Controller/TrustGXT180Controller.cpp +6a0bcfb48b8003452b6559e78f726a051e898a4bc302652e5f38497f9f7dca89 Controllers/TrustController/TrustGXT180Controller/TrustGXT180Controller.h +79db2a7873993036e5048ccca5fdd81f32b5eb446811f995970485b6525eb9f4 Controllers/ValkyrieKeyboardController/RGBController_ValkyrieKeyboard.cpp +20a58e1fabf5709dfc5acfee98ef5134ffa545ea8ee3d3b6f9bd0c0df27b7ab7 Controllers/ValkyrieKeyboardController/RGBController_ValkyrieKeyboard.h +e4166f44790fa8f61375ee6d58e1f0081fafc56c6cd4def75cbbbb3e69f17c5d Controllers/ValkyrieKeyboardController/ValkyrieKeyboardController.cpp +eef2936f4c5648a6bbee7b458ce0e472ddc7b631546d7148964e67477f17895d Controllers/ValkyrieKeyboardController/ValkyrieKeyboardController.h +82c2f005d562096d42a894c1dcaef511933530084546b2630fc63ad3a6f4279a Controllers/ValkyrieKeyboardController/ValkyrieKeyboardControllerDetect.cpp +a15fa1361c452debdf9e34a9bc1f004e4fa539d333b34b5abbfc6e83a592232b Controllers/ViewSonicController/ViewSonicControllerDetect.cpp +9c69c5d87f3778f53419b7703526d13bf7edd8f03c3d3c547a4fa752a6c69ab5 Controllers/ViewSonicController/XG270QC/RGBController_XG270QC.cpp +9a3c7e9d76d31cb5485e4b0e8d4047c8bc15451e7f71308b8f78be773942169e Controllers/ViewSonicController/XG270QC/RGBController_XG270QC.h +9815b96e35008eeb25be57219bf9b083edbfab665929570777b9c419689cf78c Controllers/ViewSonicController/XG270QC/VS_XG270QC_Controller.cpp +d8ffc26135c0ec5f83d11be1000945b4d0c1e71fc1ae9e6dde4e6490ef29c526 Controllers/ViewSonicController/XG270QC/VS_XG270QC_Controller.h +744e10300a4f0682e8e6ea001041d614d3624acafa167c3c7ef2c6c95fce9233 Controllers/ViewSonicController/XG270QG/RGBController_XG270QG.cpp +e3f806962d69cdf6a678a4968c99fc9f18981d154afe72768866fff75da1cf74 Controllers/ViewSonicController/XG270QG/RGBController_XG270QG.h +f01b6cbff7ebdcf12cafebb6b68d411d4fabcb9126ce34e9e159749008f464a1 Controllers/ViewSonicController/XG270QG/VS_XG270QG_Controller.cpp +87132e16b6a645f5d4c016676e489faf235eaff4b62f400b8944a1fc643420fc Controllers/ViewSonicController/XG270QG/VS_XG270QG_Controller.h +f5ad9d899ec0486a4201085c528749c7673d57b1457dfaddaae3b1999258344d Controllers/WinbondGamingKeyboardController/RGBController_WinbondGamingKeyboard.cpp +b7daea42b3173d7fdb85086e5bac4504de7509ba20f37c39f884de11db8d0402 Controllers/WinbondGamingKeyboardController/RGBController_WinbondGamingKeyboard.h +7604f608c53f24f14abdb7b8a581c863a4f25be12cd8727c888fa98989cd84b3 Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardController.cpp +5c326a4f24f157fb94ba272b8ba6751c8edb45c52241dd12220837d6453f0610 Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardController.h +8bfe8f2c3d0eda21d50d48fc78dae33209ead9e8bf99394bcd3cf73141b2fa61 Controllers/WinbondGamingKeyboardController/WinbondGamingKeyboardControllerDetect.cpp +e0567cc0cf76ee07345534b969322d23861f0a0f0f9f963000c9bf83ee368660 Controllers/WootingKeyboardController/RGBController_WootingKeyboard.cpp +3509c5974eb403c8f5c6ff9ad52225d9d260654b99718374f2f2ec20b60ad07e Controllers/WootingKeyboardController/RGBController_WootingKeyboard.h +bdfe45b4e50c272eb7308f636828900808f1622e1973a42923cc4d0e4eb189fb Controllers/WootingKeyboardController/WootingKeyboardController.cpp +13ca270101ce862ee883cf7e1d2bad3efe7559920688843a20abc35ab54954a9 Controllers/WootingKeyboardController/WootingKeyboardController.h +3ba9909227e5dd1a161029a562fd9416fc8b774ee9068d842942a0fb3bbc39ad Controllers/WootingKeyboardController/WootingKeyboardControllerDetect.cpp +a2457f2c7800d7bc330b4cda0f0a8b801fe59c334f29839ad431d539fd8665b4 Controllers/WootingKeyboardController/WootingV1KeyboardController/WootingV1KeyboardController.cpp +1fd0a7e7f2ae85c9f78a96d8f92899a5bcc21b0dde57b66e6c0f0914db0272bb Controllers/WootingKeyboardController/WootingV1KeyboardController/WootingV1KeyboardController.h +2739026beb41da50aa4aefcacebc42b6fa840269f550e3579f6f6317178c1fee Controllers/WootingKeyboardController/WootingV2KeyboardController/WootingV2KeyboardController.cpp +0676388f66f447aa67576d1af5aca0d1ac4e836e525da50a08fa814f18083a76 Controllers/WootingKeyboardController/WootingV2KeyboardController/WootingV2KeyboardController.h +d25889bf5c875f924a9edbbd1856c76ff79fb6a102413647deca74377bb6fa5e Controllers/WootingKeyboardController/WootingV3KeyboardController/WootingV3KeyboardController.cpp +cd4a63fe6332d8af9424ca3926bbd54cd0564309ab1ccf53139573308d9514e8 Controllers/WootingKeyboardController/WootingV3KeyboardController/WootingV3KeyboardController.h +561e54bf4ff597a65400610b02c4818e71493698564f812dbda8d6fe2b9f265e Controllers/WushiController/RGBController_WushiL50USB.cpp +e02363181cec3d2d35ff873f2184485b5c1c11781af8880b859b81fd4e6bc9c0 Controllers/WushiController/RGBController_WushiL50USB.h +c22032fad2db96961dcbbfba3967278a490050bd212c7a5bebfb5cacf51a5006 Controllers/WushiController/WushiL50USBController.cpp +c1eb45bc7dd2b395e1922b532f09debba53180ef21461105096c58977405d25c Controllers/WushiController/WushiL50USBController.h +32916971f7db3796d2d823ebcb1bde23175cd75660fa716e45964adf36627b7f Controllers/WushiController/WushiL50USBDetect.cpp +193b165eef42fe82d4b15c4db4463eebea639b9060b65cbe8a58fdaaa61b591a Controllers/XPGSummonerKeyboardController/RGBController_XPGSummoner.cpp +925132a8963c916dad75edbbbb27b6a0fc22127909fbdbd83905b89daa126565 Controllers/XPGSummonerKeyboardController/RGBController_XPGSummoner.h +43424fd25f75679e6d9c1dada03862c65b6c1ec24c9be90a7b4a5f8e3b6f9c2e Controllers/XPGSummonerKeyboardController/XPGSummonerController.cpp +ef4517194e65ff627bc1764a46fac421d166fe213b84a849b190140fc28fe68a Controllers/XPGSummonerKeyboardController/XPGSummonerController.h +ad0200038ae719b68fe18128a33c4f6b68b7a57a36c92ebcc3fec9c04a0cd507 Controllers/XPGSummonerKeyboardController/XPGSummonerControllerDetect.cpp +ef1c430325dde8fe805e9b86ac4e4df6f6b3db1f0e0c019cf958b1e7ba1f30fa Controllers/YeelightController/RGBController_Yeelight.cpp +14e52401397747007cd1a15d32cce43fd0d767db9614518c76e5412ff90e7da6 Controllers/YeelightController/RGBController_Yeelight.h +1beda5e8f36502138b9a093745792fcf2870ea9ff8b79e17f26bf272968e0137 Controllers/YeelightController/YeelightController.cpp +99c6f1a8c53a43d6c4f80158870c843fc616e246ac1ce7d6486590b945c13680 Controllers/YeelightController/YeelightController.h +2f713ed5a5b86cb297bc78c25bdfc87730a2815d0248c0623978db63c3336d84 Controllers/YeelightController/YeelightControllerDetect.cpp +92309d7aa73c5db6349e9d6120fb32cf26f79eaf74360fce5440faeafe88b1e2 Controllers/ZETKeyboardController/RGBController_ZETBladeOptical.cpp +84a4328816743482ea284973865be6539c2cb072498253a6f9b831b9c0f37ec5 Controllers/ZETKeyboardController/RGBController_ZETBladeOptical.h +5bd075d87bc4fc5e29ea0acb1831ba3a19624a9bf31543c85dcb5077b3ed06c8 Controllers/ZETKeyboardController/ZETBladeOpticalController.cpp +d79efbc9e0e8315eaa9fdf58f71533f6161c4c31d21477cfd46d11a80ffb31f1 Controllers/ZETKeyboardController/ZETBladeOpticalController.h +1074c4728ea364377c1219803cffb72a9e11cd6469765b1d6ca997f591686257 Controllers/ZETKeyboardController/ZETKeyboardControllerDetect.cpp +2cea38feda6e218eabd7b2eca08b0922afa1e018cc7e83f25fd943bcdc8158e8 Controllers/ZalmanZSyncController/RGBController_ZalmanZSync.cpp +bd42dcd881eb1929f71803e2f2bc69d0aec308a0d4ef34544382e44c5b87bfe8 Controllers/ZalmanZSyncController/RGBController_ZalmanZSync.h +7677bda360be4d914b3a4990c84bc63484045e84cfd9bf3038b030b6eef7102c Controllers/ZalmanZSyncController/ZalmanZSyncController.cpp +ceb3d7351ea04dccd29c5e9ce737071851927134247a6d261a3167e546cbe6ca Controllers/ZalmanZSyncController/ZalmanZSyncController.h +30c6cde2e4a1861bee78b157ea2c8234129bf5e568ff4d7ae4128c30f3aacf46 Controllers/ZalmanZSyncController/ZalmanZSyncControllerDetect.cpp +2193181deeb323a459eb36105752255d26cbdad23ad39430c834e8825b7c366d Controllers/ZotacBlackwellGPUController/RGBController_ZotacBlackwellGPU.cpp +3546cf5ce8720127bcfdc55b9020d07905f0ec4524b4541dce2d21034bac2b6e Controllers/ZotacBlackwellGPUController/RGBController_ZotacBlackwellGPU.h +f7d3df8fd4cc0762d07dd161c6bde872af64f76d092a0d0c42b3da20cefaa89d Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUController.cpp +46c29e7f993389b9a194350360fa8cebce5e7c909d2cd1b307f3525de545fd16 Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUController.h +9643bb2c82274dfb1f0784a83ee0d372095950efab2ac2612f7ae6c3002f4e1e Controllers/ZotacBlackwellGPUController/ZotacBlackwellGPUControllerDetect.cpp +75b9800729c6094e8505618d1697f8dec67625ea4391af6012994239a958acfa Controllers/ZotacTuringGPUController/RGBController_ZotacTuringGPU.cpp +c8abf7d48c456b3ef155cd5974587716d81439cbbc126e28b4c8e7dd323f8639 Controllers/ZotacTuringGPUController/RGBController_ZotacTuringGPU.h +e3cdef5731d08a4b88fe26135ae4b8abc5c300cc84fb4d931f595f50d096f9bd Controllers/ZotacTuringGPUController/ZotacTuringGPUController.cpp +b29d96a433a99ce9f2f273531d8a97c5730bca15b3f3987a979b18aa7e69c248 Controllers/ZotacTuringGPUController/ZotacTuringGPUController.h +3a98bca04bb0129c038d6e093fd072064f73d449bb7e872eddb7d8108ce1e94c Controllers/ZotacTuringGPUController/ZotacTuringGPUControllerDetect.cpp +ec03bac61c42392dfe14178c2402c48a4c9c37efcfe600721e268ef513439087 Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp +9c6f069e0784a53c6b0435039ec6d2f474cfa64927b813f5fbfcbbb8c27b6d96 Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.h +ef4b3501c829f505c67312fa979facffa726e4d1db62ec3ecea432bf87cd1c79 Controllers/ZotacV2GPUController/ZotacV2GPUController.cpp +23363cfd9d3e7310ff0db0c0e007f3626886f9065f69e61af5f073fe2bca1d00 Controllers/ZotacV2GPUController/ZotacV2GPUController.h +8f9dcf558fc1103236a02b21fe9a41d35782d9c2a58088ced60a9b46e379a314 Controllers/ZotacV2GPUController/ZotacV2GPUControllerDetect.cpp +a46ce58a97b43e4aeaea0691f750485924d19cd4b9b2518fa03159469cca8457 Detector.h +b6b7421d220de7442a9f976d24e6fe9ecfbcca36b3d652c82a5d363aba6c51fc DeviceDetector.h +b60fd1e30d44eef64c1febd2c85eb0f1578bc1723616d36676a0e1e096d17050 Dockerfile +92fbabbccf517c8aa7901852c1be69127dc34decb80e6f3c51b0f19008f4fbab Documentation/Common-Modes.md +5022ecd46625500ae56763a5f18ca40ecf024e8c8e5a211cec84ea9c4b7458e4 Documentation/Compiling.md +5450694183be4a63ca7d766c72b90b1d6e3456db542779c9d08aefe27a3a713b Documentation/Images/OpenRGB.png +69c8d9c44006b6f447f33cf2bb2471201dde38606d1d01feb481e35c5fa011f2 Documentation/Images/OpenRGB_Screenshot.png +8ac2eb2d360cac94089252f4ca06f9a6125efcdfc2a88f951893d2d208b65075 Documentation/KernelParameters.md +87ed75646427daa51eca9e9f94a79a67bd53470677f66c9092b70d136ec8f793 Documentation/OpenRGBSDK.md +5a1146eb2f54e813ceb26fa374b9dc438d4903517657d9c4de849090e54ba6b4 Documentation/RGBControllerAPI.md +efda3f199670cd82c35b42003ae871f0bf4ca63999d270bb21955b149f6dd100 Documentation/SMBusAccess.md +d6f8ad2d23adfa20cd9a9ef44883317b12d26c8f6eb139056cb22446b10950ca Documentation/USBAccess.md +5e5b6bf148a1506f3f77038547e7480f90f5ea17c6cf3c239e4bb13add1b97c5 Documentation/UdevRules.md +58d2908f0565ce3b1c29b910c77af67d33a0a8d73c6af6be440e669d589c8a3f KeyboardLayoutManager/KeyboardLayoutManager.cpp +4fe4be043a8bd9f04e936f37e2d786b8b5a3fd353591265d4b030621a0518176 KeyboardLayoutManager/KeyboardLayoutManager.h +02953cd889069052ae9dd04babd3862508b06ed52531e4f383a2fde03341f594 LICENSE +420ae1d15f9a4ab004977988469b465cfbf00bd989ed0761e93cdc97bb808ad5 LogManager.cpp +b89d3f33aa52ef71ec328691fd94b6b2afe915a86e27660a54470a8caaf9de4c LogManager.h +625a257dcd25c3d2bddc0078fb7f72bfc1dd06ffb0eb0e8a92a54d7128f766d6 MathUtils.cpp +5cce53eb165480f9f0d4279d693ff05b341d66df973c7d7a85b1fcf9b0f3c438 MathUtils.h +b9bcf88759d165fa71aacc71daf192e213f4c344a8879d02b4c7ed6aba20cb0e NetworkClient.cpp +83f80569479918f9675ba79eece4f6056f2a2c4cfb49e18d3cfe97f1e5ca5ad5 NetworkClient.h +5d5261f90b401e152a861b110ab65e8359498f61df0b111a470d990d694dd4a2 NetworkProtocol.cpp +6ba458903758bbb5332653f2cae94ae5d5f5e701b7a9e0f0ec3d59e28a879bd9 NetworkProtocol.h +afca2eb2a19a0bba379a647ff339f97dcd14bcbada61f02a51d7bbc7f738957f NetworkServer.cpp +e5d8e2b740c506277fb34aa5ef9d46ee651027e8321460ce4cfc911ea4905d7b NetworkServer.h +2c598c2ce3a742320b7eca32b6a8ce54797933263949c459670ca559d00f1b2d OpenRGB.pro +e326cc3459fcd9415302161efa90f0025571be09efc3ea9ad8c6d6fe071fa498 OpenRGBPluginInterface.h +4df43fdb6f649e978799ffdaead78829f5b17b7d5986a00254b81e4449e23c0b PluginManager.cpp +e60a8f8ab0e5ec5b5aa6a46b5379eaffcc19f8e2d0a8b3c6443106a721254de8 PluginManager.h +2cb59005561e9eedc8fb39ab64282d24a3d28eeb3d9fd71e0b6df083aee7802a ProfileManager.cpp +fe941b927072893ff6ebca6580b0b41b94c8283b04f781c14894b7adb3aeb3c4 ProfileManager.h +288cbf6d407ea809eb637c0dba5e02e48522e067c50c28db34b34a138d7c26e7 README.md +1d00f940dff4ee20bc32ebb7c21454c9b995b8aa22641026559e2c0fa7d5fc88 RGBController/RGBController.cpp +23dc3944993e972c0c05e229f2703e66b233f8881fa3e98958ab0c578aedcbc6 RGBController/RGBController.h +5320d0a913b7de45941cc5c50f62b0b6c5962b6c95bf35c3cf7358db7327daa4 RGBController/RGBControllerKeyNames.cpp +6aa2038fee057e0717f49159795250c651af2adcecf9ee91df7d12a217a2d148 RGBController/RGBControllerKeyNames.h +864898d85bc598e6bfd0b0614b6652c8869006cc48aba0de9fc8cad2636eb6be RGBController/RGBController_Dummy.cpp +7f6a719541b8f0424f6650178da2aece3c477f6ea2b6a3e78d1d9791dec523bf RGBController/RGBController_Dummy.h +e9fa58ccc3e63a70726b64e9b55738f9f3a12f78a102ee35ce60924302aa16d3 RGBController/RGBController_Network.cpp +3507cddd62bcaecbbae4ea2ca74644578b35044a9d427de6a188497fcac7ceee RGBController/RGBController_Network.h +c5940221743b50fc8485ca59137ab5a569467ce65188d01388949305bddeb0b3 ResourceManager.cpp +e9929cff4e59cca3bda5c5c242ede7fdc426f62501f32e7da5ca73a1d5954187 ResourceManager.h +d7c12b70d042585aa870a9c419857da224cd5686cc8186569e63ee60c19a61fe ResourceManagerInterface.h +8bdf9cea26d37b40f928489f6192590729e46598c1348c586cd0ee374158cf83 SECURITY.md +51e6f0c4d9ffffc8b7d736b951e452ce95efbd63572ee0fb8473f57f8cc91b1b SPDAccessor/DDR4DirectAccessor.cpp +95e62817262d1e39609f30e22c1091371c45962943498f93a6fd1a2bd1548834 SPDAccessor/DDR4DirectAccessor.h +aa6ed12803f5d3b297675e21b09c7636d6f35453484a23707d96c6a57d06f9db SPDAccessor/DDR5DirectAccessor.cpp +60699d650917654d3effce893103f83f4cc8d99ab81b9374c509850467b5e409 SPDAccessor/DDR5DirectAccessor.h +96a86a07fcea9ff88c517c689699116ae7f76f5baf81916954c43155590e84ce SPDAccessor/EE1004Accessor_Linux.cpp +dbe7ac77547ec72958902e3500c2f744bcbe04cf16a13d67bc010c0d1d7d97bb SPDAccessor/EE1004Accessor_Linux.h +c5fc619f0f006017fec1165f53a9885acede04c5c335358a5a1fd9177b1b3829 SPDAccessor/SPD5118Accessor_Linux.cpp +e65d5e349a67562c109e0afa6aeb2b9a25249430d8d6dd53e914b9459fd1b06e SPDAccessor/SPD5118Accessor_Linux.h +72eb87317defa1342a33720335239aadd6c1eb499903bad098fee011d2926939 SPDAccessor/SPDAccessor.cpp +dc36b837ba819e7493e80b58c7a18043697c515651d5e05c3ee15ea14151de21 SPDAccessor/SPDAccessor.h +60159ed4bd3514899893b83b75fb93ad6f00309b5034787ebb4716d6566d73d1 SPDAccessor/SPDCommon.h +a8dd1feeab678c751b2338e69af6974d6654e49edb77e6a928037b5a2b2749a1 SPDAccessor/SPDDetector.cpp +086a9d00727c439d3a41a29df246a2cf6adb89eebb1a0ca1869bc0617086f4ce SPDAccessor/SPDDetector.h +28f4e69d495290530e22318dcd6f79d458237992e60464e2d602252250467839 SPDAccessor/SPDWrapper.cpp +9c8135bbbf33142ad0e5e12e963d3d61713a8f66b2a984076ccfb79a38161091 SPDAccessor/SPDWrapper.h +2d93aff84bd66b8f3c2b838fa773b9c087bf7ff491cfaf81f7fc69eab4b6227c SettingsManager.cpp +1cd688ea95998227b70086dd1053dc6391e2f93efdf2d201a4250dce6defad4f SettingsManager.h +cbb2af0b2d09e9d14f080ae6e0b851480ef88e8c04491dec000cb7c56cf4d1ac StringUtils.cpp +3dd2e57db23b1972958657eb2e08f3d507dcad185a62d955e62670bc55712d4c StringUtils.h +216ca039d86ffa5bce7fcab9ca05e5ac6f4a0b45df2cf12cf2481d88d3b34dac SuspendResume/SuspendResume.h +bb2c0c1805b7415d4b23c351bfe6ed11dc64211ab77ca6bd4c276f776f617073 SuspendResume/SuspendResume_Linux_FreeBSD.cpp +06c1d081557edb0700e3ec366969f0d7cd87a3548604be5ac9b865611c8ed470 SuspendResume/SuspendResume_Linux_FreeBSD.h +40854bfb33942522943ef1507b0f96a56c0f89a3fccc09c64f1c1d313b0d6671 SuspendResume/SuspendResume_MacOS.cpp +87d04a0f3cb990afab0051728202349986b725edb9f44df36796bf445d4f082e SuspendResume/SuspendResume_MacOS.h +3cec00a341575a8eeb18b99c22a9bd12f010fd67c9c03db14b49ad09ffc295cd SuspendResume/SuspendResume_Windows.cpp +68c8cfbd908da15364484acd9d883e5b40d2449649df99aa3f3cdcda82b02cf0 SuspendResume/SuspendResume_Windows.h +ef408b4fe6fe58a8cec5148812fcb8684ff3943b549ac78836fea910bbb103c9 cli.cpp +2f9612017e828865dce573468c276cc2627c6e832c9ee582a87d138ec1afc78c cli.h +e55007c14bd1a4d76db0e6cc085d6f76ed08c578b82681b5df1a2fd7111ca5ae debian/changelog.in +4593c89b4d60161d78fef3f48312d7833649b876a0c1d762cb5b9d73acaa0a20 debian/compat +bd86ae4984354160bdf0ac4359bcb7c891c178b6dba7f1a92cb713cf77d807c6 debian/control +0b2c3e81b5ef4519b4cb5c15b7ba8a21b182ed5c17b456a7081eafcd652389d9 debian/copyright +9e7e318f7cf2d7403d72651cbd811142c883cc0774d29a033c878414d061998c debian/openrgb.postinst +73e80f30d0fba1f9f5f0351fcca42dc08b806219c90251185e85fb14a91cd441 debian/rules +f88568efb192f79693c33238c1e701b91dbf45077a5337dcba48b3adf928d0d2 debian/source/format +000328d34d124df3821945ae23a481a6d50ffbb7e2ee282b46f9f37e814e98f0 dependencies/CRCpp/CRC.h +3b6d27e2ce7d3d0b0ae83c4afe9a55320d9a88b96b071b46cd2f260a49393562 dependencies/ColorWheel/ColorWheel.cpp +25c92bc34a7fbd3a9c62b0896f24ca55947b2b3d721325c07d87cb7accc3c086 dependencies/ColorWheel/ColorWheel.h +eac713effb468289e1ee00a6fffab7d43040cd500279ed095c7f8775bdf772ab dependencies/NVFC/nvapi.cpp +0c59085151b5eccb38a0accf893c5e597abc603d97d69627f6c78cf0b21a39da dependencies/NVFC/nvapi.h +c6e7c2f54643ef9cb51b9a3ec7b0efc9e204803c206f33014e39f281546ea9b4 dependencies/PawnIO/PawnIOLib.dll +79e50e2921761a7306548909e8ae7a59915bda1f9e97f3549bc5645911e24891 dependencies/PawnIO/PawnIOLib.h +27561b484bd4b29c083d7eee61a2f3486b7b494916df62d0ec8a379e5acc2e09 dependencies/PawnIO/PawnIOLib.lib +4247d588b9da9c598a65c6f5b8255a90bedee510ee1c354f9a909a2f959cd4df dependencies/PawnIO/modules/LpcIO.bin +c561fd4a2669ec4e52676fdc6905f54edf70c7308efc93b4126ad644ac00a319 dependencies/PawnIO/modules/SmbusI801.bin +36dc1d4f860e56b0f8eb84afe26a6c55aa03346cd51a455a1aa6c601532c7303 dependencies/PawnIO/modules/SmbusIntelSkylakeIMC.bin +4767b71f8ab870d6414ceb15adfdcddac4e539928481ed590463e481253ecd1b dependencies/PawnIO/modules/SmbusNCT6793.bin +0a5a8166ffde0ca12db6f897f5dce295e885a9629954f544bf315b2cce6f00ba dependencies/PawnIO/modules/SmbusPIIX4.bin +339f418cb98b6a07cc373bffaac231b23e3ba067801abe95d6a7ab46dacca61a dependencies/display-library/include/adl_defines.h +3bae7b5ef266fa2ae36b421d83946435da41ed660cc6373e8ca590497e222a4b dependencies/display-library/include/adl_sdk.h +e9a780301951120271ba7492a0bcbcb17dd3133d5e8b43c560e1c0a48e9de81d dependencies/display-library/include/adl_structures.h +5f36f4b385ed862ad9df9a4e5c4638fce5f6994d193c7d465748d1f70aaf8864 dependencies/hidapi-win/include/hidapi.h +61f444fe6ca1cade0995302291aea561373fee837f299c3da7df8f1cdd1c9261 dependencies/hidapi-win/include/hidapi_winapi.h +ebeb835e2b4530ed68843f19d6a2604c51772e3c26e7f542fde194075f82d9b4 dependencies/hidapi-win/x64/hidapi.dll +915c0c8003fc9b39ca5d6d2c6a70ff8bb4f11752604850e27b0c3fe8387e8df6 dependencies/hidapi-win/x64/hidapi.lib +e9b4e51b7060a9d748c0bbeb67113f6166ced6f342ba9cb852385c4506766a57 dependencies/hidapi-win/x86/hidapi.dll +e9299d124f4366488ffb7594d6b75e94ecb5c48dd58c9283e72f7dec739a18d5 dependencies/hidapi-win/x86/hidapi.lib +48a647dc56ed27152c6fc9d96322cc62ab39df6f6a4b649d663f29b0a5c4fc3f dependencies/httplib/httplib.h +5779c11296038a9bf303cad49e1553cd76269ccdf668bd86b1ee54562f7a99ce dependencies/hueplusplus-1.2.0/.clang-format +e143f895e2ac706be13389fdd634bf5ef01537e082297548d7c0d536973793d2 dependencies/hueplusplus-1.2.0/.github/CONTRIBUTING.md +bea886b3b16d5f2b063a6e1ec1ae8fec614df3d8f4ef81aaace0512e0c8d8e57 dependencies/hueplusplus-1.2.0/.github/FUNDING.yml +96943c8133f6b94a835ea80aebb9af4dc7dd22e5a35230262240ba0205b308ff dependencies/hueplusplus-1.2.0/.github/ISSUE_TEMPLATE/bug_report.md +7cc93c9805964941eea62d083934a9434ce9b93741425221e5709f49aaafa640 dependencies/hueplusplus-1.2.0/.github/ISSUE_TEMPLATE/feature_request.md +1f1d2001e1edcaa508cc88c5a6ea94ca651ae71a78a9c4aec5806416e82d008e dependencies/hueplusplus-1.2.0/.github/workflows/build.yml +cdf6d4776d4df062382906f68b66a69144ba0eb193529b2d019f01715b396cf8 dependencies/hueplusplus-1.2.0/.gitignore +98b523850b55d363a94754a332a750da878ab798e16cce0cd8ad15aaf80c9898 dependencies/hueplusplus-1.2.0/.gitmodules +aab7bbde720cd5f0e1c31d02897d2dc7883a724d5a97994823a124efff639fee dependencies/hueplusplus-1.2.0/.travis.yml +64902e3f7eadf42852b2cfe8234780da00250c2672ebb4d15f6cbaff49326d51 dependencies/hueplusplus-1.2.0/CMakeLists.txt +19cef97dc55c5a5b8abcbb475458796ce1d2e0a726f59ff918e4eacccab33cc8 dependencies/hueplusplus-1.2.0/Doxyfile +7d3a95e5e06978064ed3f8e2b7c8f845e7fd8a405294727cc708f94cb83b8059 dependencies/hueplusplus-1.2.0/LICENSE +649be7c300bd95ff0fe03d08caf86daae7659ed983408b029b162f8be768e83a dependencies/hueplusplus-1.2.0/README.md +bcd79a9f2eb7e033699a405ac586adc3e6950d7c667611c26459e0531d913bbe dependencies/hueplusplus-1.2.0/cmake/cmake_uninstall.cmake.in +fdfb8db4436f1e8258005d3f835d0f577a87306695e24d266bb2d921a0cb56b6 dependencies/hueplusplus-1.2.0/cmake/hueplusplus-config.cmake.in +a65916167a87ca0e8b1ee8b18f29ee82fba6d86e1572a0bcfb0af12fbf38aab8 dependencies/hueplusplus-1.2.0/codecov.yml +1ba9856914417c46d7f56922b79764453777fda705d49e1abd52b3e6d7e76713 dependencies/hueplusplus-1.2.0/doc/markdown/Build.md +e68a833002d88ac6b6dff938da65ca2ef2141e75a2153ef5c6ab947c8a9aa5d0 dependencies/hueplusplus-1.2.0/doc/markdown/Getting_Started.md +2e50b32210af230cd6609523c3e56cd2781449fa495a8fac09c88cb7519d9274 dependencies/hueplusplus-1.2.0/doc/markdown/Mainpage.md +2590d70f3dafac38970a64c72b033e41d80920aef44be95587a0493d06bfe8ed dependencies/hueplusplus-1.2.0/doc/markdown/Sensors.md +33aeb9820081ea982286cb92c8158a6f27e59e98ce0d9e7c9839b9c6728fb226 dependencies/hueplusplus-1.2.0/doc/markdown/Shared_State.md +779f525666c5ce5e0490b5f3c4ea6d3b6e0d70f6ed42e35d63ed5435c3e27ce8 dependencies/hueplusplus-1.2.0/doc/markdown/Transactions.md +c78befa62a91bdf795bcebc20cc206ecd0d814e40c615e9750fa098794529a2c dependencies/hueplusplus-1.2.0/examples/BridgeSetup.cpp +7514107056996457789c971d77d7fe70365399a4b142eebc7ec039bab1606d26 dependencies/hueplusplus-1.2.0/examples/CMakeLists.txt +20a5915ec5dbf724106e937c328a372bc0a8cf9e3d437dfce7a53e27385d8723 dependencies/hueplusplus-1.2.0/examples/LightsOff.cpp +7db15cc38eded26bf84e7c6a68ad59d29962171296e22120c30db18eb1e2c1fd dependencies/hueplusplus-1.2.0/examples/Snippets.cpp +6028e39f599342165b4b70445e8e67306783d5652d75e9c69434999b2eb35701 dependencies/hueplusplus-1.2.0/examples/UsernameConfig.cpp +1af16ad054379ddea3e416748af5c2504de3d3df5f944ceb01a2bc67109abeb4 dependencies/hueplusplus-1.2.0/include/hueplusplus/APICache.h +251ef12559d256e906ecd08eb644d899f5990697b2d96513ac91cf5cb42c9c6c dependencies/hueplusplus-1.2.0/include/hueplusplus/Action.h +ff04e5dd9e35d71658a0c34ba80bf8010eeb88403daf0f96674e5ccb20cab6ce dependencies/hueplusplus-1.2.0/include/hueplusplus/BaseDevice.h +30e947cfc394ff086d73aa23635302c9b401a519a33285627afbce05424b16a0 dependencies/hueplusplus-1.2.0/include/hueplusplus/BaseHttpHandler.h +97020fc1fe1337ea4e2170694f6e075c48820e9a621a3e7214f4014386d9bcda dependencies/hueplusplus-1.2.0/include/hueplusplus/Bridge.h +ee9377ccc72b14606e4693be195a1bb04895d8ed021f9ea13531f5f97bd82ef5 dependencies/hueplusplus-1.2.0/include/hueplusplus/BridgeConfig.h +20926206692902bdaed8b8ffddf72b1604d727cef3719df5b1db21ca06fac47a dependencies/hueplusplus-1.2.0/include/hueplusplus/BrightnessStrategy.h +cb54bb9ca0aa4efac736deaa0dfda73305dfb43abe0fad90102402e6b581ef54 dependencies/hueplusplus-1.2.0/include/hueplusplus/CLIPSensors.h +cf76b43a4979d799a2f48ef5b0e2bb5172149a021a70078602070ddf38079d8f dependencies/hueplusplus-1.2.0/include/hueplusplus/ColorHueStrategy.h +2b069138cffdcecfc69f7d52e07217b35d16d36c5ec11ed98307dda9e2513e9a dependencies/hueplusplus-1.2.0/include/hueplusplus/ColorTemperatureStrategy.h +ae1c796b0d5a26ecec02693cc791987721c1f28f1d0503a70ff17359405e29f4 dependencies/hueplusplus-1.2.0/include/hueplusplus/ColorUnits.h +00f4843431a2732bbb761b1d9225a3fd66d01e2d60a0e1217933980d92692a31 dependencies/hueplusplus-1.2.0/include/hueplusplus/Condition.h +dbbec0a472908840e1d0865804d5f05a9608baefacf63eb51adb38020161832c dependencies/hueplusplus-1.2.0/include/hueplusplus/EntertainmentMode.h +7a198a822328bf5e27f236b75463b47022fb7f3b23fd80e1fbef47edb7a33d6c dependencies/hueplusplus-1.2.0/include/hueplusplus/ExtendedColorHueStrategy.h +231c48d5424fa35155b5d3a83c742366353ef79bf0f51f25b57ec06c230a2843 dependencies/hueplusplus-1.2.0/include/hueplusplus/ExtendedColorTemperatureStrategy.h +e8a541ec146ebb7ab0b8258f47894e6cd1920a5ff34c7c10509dd7d3a85edbab dependencies/hueplusplus-1.2.0/include/hueplusplus/Group.h +0d3088ad41b384bb70784f712202e02512fd4f0e6865079e8fe778d29adad363 dependencies/hueplusplus-1.2.0/include/hueplusplus/HueCommandAPI.h +f94fbc37495e781daa643ea728ba029508edfaa1e05a95fbee0235312f6d272a dependencies/hueplusplus-1.2.0/include/hueplusplus/HueDeviceTypes.h +30d357a2938a0be8e46c72280a4b90acf771e4b4bc5f879d67fede3e579aa8df dependencies/hueplusplus-1.2.0/include/hueplusplus/HueException.h +b5d051dbd412e50046820640b4aab1d836eaa07106c62782fef58f450a628e82 dependencies/hueplusplus-1.2.0/include/hueplusplus/HueExceptionMacro.h +8120306128512728648ffcfe55088931342e7ab5b7dd5a7cacb33d8b8f501be5 dependencies/hueplusplus-1.2.0/include/hueplusplus/IHttpHandler.h +8a1e6a7eb2594d7e45a02c1d9de615a8f058fd1ef128addc823773c2662d16a3 dependencies/hueplusplus-1.2.0/include/hueplusplus/LibConfig.h +0e94c62bc396ab5e496a64848631add93861630f8a71f204c6ee2d4df8a5c1b1 dependencies/hueplusplus-1.2.0/include/hueplusplus/Light.h +f95db31ae509ae0e0b84dd48b926face66e10433ab77d2c439b6268c1dd017b2 dependencies/hueplusplus-1.2.0/include/hueplusplus/LinHttpHandler.h +894c201ba61657ba12c80906f6742cca8c8c8904240348d23b7334319e218dd8 dependencies/hueplusplus-1.2.0/include/hueplusplus/ModelPictures.h +dd8cbc3272474d6e7fe93214c3b905e35b58b84d251abc121d37eb998266c98f dependencies/hueplusplus-1.2.0/include/hueplusplus/NewDeviceList.h +a15fc4f6d63defd653e2fda7175988a9099043f72b87897c2b0312b6ad96ebeb dependencies/hueplusplus-1.2.0/include/hueplusplus/ResourceList.h +520e5abb157375018a7d71fde78e5d7554be6cd6b7d7faad788c386ceacfe683 dependencies/hueplusplus-1.2.0/include/hueplusplus/Rule.h +bc84757ae6371a3122019f2c07d06bd7c568d386e786ac5b922c9d4c31853392 dependencies/hueplusplus-1.2.0/include/hueplusplus/Scene.h +1058bdd126c2a2936994c1d0550b65c076f7280dbf3454ad2d6a1c18a52de173 dependencies/hueplusplus-1.2.0/include/hueplusplus/Schedule.h +e3197e3363b387e0dda7c3aeecccd19bd8625ed61664d670ca140d8d5c8239d4 dependencies/hueplusplus-1.2.0/include/hueplusplus/Sensor.h +b68cd4c240ad6cc6fd45ecacfde42e786272257ef6d1a74dc50da4cc4bbbf275 dependencies/hueplusplus-1.2.0/include/hueplusplus/SensorList.h +b444bbf1096971045a980fb1010fd25fe97ffa49731b16543fabc68ef5541534 dependencies/hueplusplus-1.2.0/include/hueplusplus/SimpleBrightnessStrategy.h +48d50eb60e43a7e9e0ac30efdba6a09d06f2aba63bb4dba08997830dd2e792fd dependencies/hueplusplus-1.2.0/include/hueplusplus/SimpleColorHueStrategy.h +683151f35cfc62804c6688274fbf42b2f69aac2c5d68b060b9c3619f06487cd8 dependencies/hueplusplus-1.2.0/include/hueplusplus/SimpleColorTemperatureStrategy.h +acf4a837988c591079f2dc907118d282afe6adc45163d4ff9c3ca92e7c838286 dependencies/hueplusplus-1.2.0/include/hueplusplus/StateTransaction.h +bb3f2538b789c5d7e82d35d04df0b906c0c5e5f7c1d5be0caed4a2849ad3687b dependencies/hueplusplus-1.2.0/include/hueplusplus/TimePattern.h +550eda546656834815332c8bf641fa0efb3f97911ea1146b9fe52f4deacaa8be dependencies/hueplusplus-1.2.0/include/hueplusplus/UPnP.h +7ecff99e02ff2421887965a893f9b9095c7df568c2ed6d7aca8fd96fc214475e dependencies/hueplusplus-1.2.0/include/hueplusplus/Utils.h +36a9ee5258fd6f3d46df80d5d6cfedb09fcaff68a7e67e4fed4586de01a4ba29 dependencies/hueplusplus-1.2.0/include/hueplusplus/WinHttpHandler.h +77b72d5285a45bf4f94afc05d976a657dc495d11409bb24fd5ed8c459851596a dependencies/hueplusplus-1.2.0/include/hueplusplus/ZLLSensors.h +6dfd41e78023703921ca8e7b9f74eb0468e95970b62270ac71e7f21a73894f9e dependencies/hueplusplus-1.2.0/lgtm.yml +3d6634fb1b5e4bb5cb8df61133a9087c0913d71fe2d70b7d05a2f375820766bf dependencies/hueplusplus-1.2.0/src/APICache.cpp +4ceaebb7b47e13794b5b719e0835a243bf509a27ef25a3866ce430ba8e474ad7 dependencies/hueplusplus-1.2.0/src/Action.cpp +fe63533c1b0d72d0d76c548c98ffa2f0d438f921947c168ae0189aed9c4c245d dependencies/hueplusplus-1.2.0/src/BaseDevice.cpp +4eed1e4249c5316b712815b5875be599d35ef0f27e4788ab27037d4ced95ae22 dependencies/hueplusplus-1.2.0/src/BaseHttpHandler.cpp +055d74154912fe89d0325578a87466db7e12009e05a3c8802783d791cfa7bf63 dependencies/hueplusplus-1.2.0/src/Bridge.cpp +6fe4181de00e43f1befeb45171c4253bd0b02ae24bd12137b87c55273d09398b dependencies/hueplusplus-1.2.0/src/BridgeConfig.cpp +da839a4f56bdfdd92ea26406a2f0fdae4580e45976bac7b95c1d17b072b28e51 dependencies/hueplusplus-1.2.0/src/CLIPSensors.cpp +16ea54747624cd178ef166f321c8b5a5998835f5426a2c3ea048d8fbe8a2a9bf dependencies/hueplusplus-1.2.0/src/CMakeLists.txt +adab278df9d5f0a5ce7fe33fd6d919473635a1ae17b74f22a5d0ebaad2718f7f dependencies/hueplusplus-1.2.0/src/ColorUnits.cpp +0d724f605e3fa5172e8ce4cce5efc02054ab72e5b70f851e71955277c90d75f2 dependencies/hueplusplus-1.2.0/src/EntertainmentMode.cpp +3c3578f636d1a6bd21e3ef56f5f5009ed5ddfa9ff4ef9fc188138f288349221d dependencies/hueplusplus-1.2.0/src/ExtendedColorHueStrategy.cpp +3b489e672c300ffb8d21b9409de5b519a75a2c59e81b01b52ed4e362733eeeea dependencies/hueplusplus-1.2.0/src/ExtendedColorTemperatureStrategy.cpp +547233d6f8ac2717483f175e448d8f9683a32e26580b6a7023fa621937425411 dependencies/hueplusplus-1.2.0/src/Group.cpp +10e4b04be295e7e26f240607781e12b7c37de7653f5d2884debee1cd8397a438 dependencies/hueplusplus-1.2.0/src/HueCommandAPI.cpp +764112aab00ddec7a20efae798ff9037b3a1842aaf99c2eedc729fa5d52955c1 dependencies/hueplusplus-1.2.0/src/HueDeviceTypes.cpp +b9dbdb86c40fb3269e62a0972fb314f15bc464135ce78224e95ea0888d55d0cb dependencies/hueplusplus-1.2.0/src/HueException.cpp +de102fd65a946e221c05d59ff822fdb8898f325c472e0c850c8a4e5d61b19fb7 dependencies/hueplusplus-1.2.0/src/Light.cpp +7827f43fd56963fe863a01a0d20560bb9ed195ac8866e63b8ed6954546922aee dependencies/hueplusplus-1.2.0/src/LinHttpHandler.cpp +c95b44c4a507b32538718820042bb757f597ba624430887ad6effc783711b897 dependencies/hueplusplus-1.2.0/src/ModelPictures.cpp +76fe35d7a4a45c73a140c3fd479fe00af9db5669461277f7098cf142e9306ac2 dependencies/hueplusplus-1.2.0/src/NewDeviceList.cpp +eaad098d0ea55e239ef60e0f33dc81321df0919037a36d9bc8b26f00f463f2c2 dependencies/hueplusplus-1.2.0/src/Rule.cpp +48a04b2f877b4c60739de6b48ba2e13d45eff60a18f54ee7fd29c3fbcf80104e dependencies/hueplusplus-1.2.0/src/Scene.cpp +0756f45c07267e323e89e2e121d8614639cde1cd68a12eff81f665d254244db6 dependencies/hueplusplus-1.2.0/src/Schedule.cpp +e53c587e836a2998af5e7bbaaa26da99e02562ab3248741e6bd380b001b17b5b dependencies/hueplusplus-1.2.0/src/Sensor.cpp +f963332528689c895f4869fdfda2dc044c1b9cea577a8f64e90bf1d52c093732 dependencies/hueplusplus-1.2.0/src/SimpleBrightnessStrategy.cpp +239886012e36441ca3291046ba3793968fcf56f2d16a3743f32823d6d9a32b50 dependencies/hueplusplus-1.2.0/src/SimpleColorHueStrategy.cpp +edb4146bb5c0f4fec0f021a584273cfd96c8367463aca4e37f593924324221d7 dependencies/hueplusplus-1.2.0/src/SimpleColorTemperatureStrategy.cpp +5877d22d5f63c495a25f2aec04ca9965320294e6cd42fe497cc9183134e21172 dependencies/hueplusplus-1.2.0/src/StateTransaction.cpp +4988d98fb68db25f49ce3aff768e7eed90460b560b479ec7c7649dcb20eaafbf dependencies/hueplusplus-1.2.0/src/TimePattern.cpp +96c80fbdfad5209a2965484eda418be2c5d202c0ce9e7ab169f2aba234d2341f dependencies/hueplusplus-1.2.0/src/UPnP.cpp +ae52e045ef27738e6afe1131ca6e2061cba5237d29b9ca613d5aa0348a6ac44e dependencies/hueplusplus-1.2.0/src/Utils.cpp +8736f35b827e82ec574e82836466e72b25c4c6c1d3c0a29b9bf4b8279f7aabdb dependencies/hueplusplus-1.2.0/src/WinHttpHandler.cpp +93f10a302357c965b98afe746c2126ea4befacca45baabd61652ac3f23278d10 dependencies/hueplusplus-1.2.0/src/ZLLSensors.cpp +a7b700651eec50aac0dd993b53d84482db6a787c17ea05132c96020da358834e dependencies/hueplusplus-1.2.0/test/CMakeLists.txt +a269c48e20bd792136d34f92dacaa2ccf677be63d4323748437e501162c13d02 dependencies/hueplusplus-1.2.0/test/CMakeLists.txt.in +3b77d945809b7c0532a4b8d3595cb80ad8b26368e23cd0829384266487e263dd dependencies/hueplusplus-1.2.0/test/CodeCoverage.cmake +34b54574f2412dd9b6498a2297f8c27ddfd9a3aae5b74262bd99bcdb2dc9083a dependencies/hueplusplus-1.2.0/test/TestTransaction.h +c1e4f164c1b08e19295a94a08cb3e90d49a19f889e576519f3cedd8e2f12dd3b dependencies/hueplusplus-1.2.0/test/mocks/mock_BaseHttpHandler.h +e7e2eadfa44ee08794b0b2e8a1220f4c3746fc164b16239967706163a5cd5d3c dependencies/hueplusplus-1.2.0/test/mocks/mock_HttpHandler.h +1c62fe5fad923ae6adcb283e05d5cd12d40a5b8411f7c0aca588bf8848b71353 dependencies/hueplusplus-1.2.0/test/mocks/mock_Light.h +27c6bdccf7bfc1a8042a1813c1397bbff389d53be77d928f1fee34508ffa0938 dependencies/hueplusplus-1.2.0/test/test_APICache.cpp +443f4f9e6e0f2adbbeb25f9d1ce56820b1de0e5d4693d6c381389fe0ccf6c96b dependencies/hueplusplus-1.2.0/test/test_Action.cpp +9b824ac4850ebef1afe787ba3a628d58294dea32870076ddbec269e4ba200d6d dependencies/hueplusplus-1.2.0/test/test_BaseDevice.cpp +b8cbbc9d121e560b3f83a1e29f9b23ea5c6707ac17621ca34644c2dcd9f8764b dependencies/hueplusplus-1.2.0/test/test_BaseHttpHandler.cpp +d05ec96a6a3e0680a6ae086072ceedfef5e6cd3ab0489567efe3332f249a87f8 dependencies/hueplusplus-1.2.0/test/test_Bridge.cpp +c826f1f2b86d7b48e3c319463f7e747a9b63897a1f6cc38c7de2360c72ad21ee dependencies/hueplusplus-1.2.0/test/test_BridgeConfig.cpp +23727ff212d06a59d6949750a9d6909c8bf024a5ecbe2427269a4dff49e9dbc8 dependencies/hueplusplus-1.2.0/test/test_ColorUnits.cpp +dc297f659f8c5b3fd092c5c8fb5aec9789cb5e46da61bfa0b1cc64c78936dda2 dependencies/hueplusplus-1.2.0/test/test_ExtendedColorHueStrategy.cpp +2ad8367cf1e81c4697ebb2e2b1b8be0b6a0b3e8b402c5b25d4d9abdb8ad5daf9 dependencies/hueplusplus-1.2.0/test/test_ExtendedColorTemperatureStrategy.cpp +ad020fe2446ca789a852af7f8628dc49e232e0a2e51979209ad7203c9480b36b dependencies/hueplusplus-1.2.0/test/test_Group.cpp +5f97ac708295191c4e62a0eef6b5bed4b69e34d1785374333730d43946b624f1 dependencies/hueplusplus-1.2.0/test/test_HueCommandAPI.cpp +df432ecd839a132158b861380f6364a601a08096e0cb72e7a8c4c86f6c933f28 dependencies/hueplusplus-1.2.0/test/test_Light.cpp +0c277f75da715d10cd39adc830924b1d55cdfaa6c7478ffcd3e3d679036352d7 dependencies/hueplusplus-1.2.0/test/test_LightFactory.cpp +c8df8afb0515a3f0c1cc7a8f831e69378059c8660c52e3ad28c35d3243caab45 dependencies/hueplusplus-1.2.0/test/test_Main.cpp +34a95a28f61315d4c5051f3bff900fc275ba4d404b42e4e323366916ea01e263 dependencies/hueplusplus-1.2.0/test/test_NewDeviceList.cpp +9d5e478029101457127e647557a5f160c3d182aa05efcbe0b2c1ae894c056954 dependencies/hueplusplus-1.2.0/test/test_ResourceList.cpp +51f551f889c15898fd8879f9a9772a57a8a770f66f02b1d799093bb2ecd0e627 dependencies/hueplusplus-1.2.0/test/test_Rule.cpp +90c3fb86236313d90cdb16294ef334520ea7cb0205f469dd028e05f303c098ca dependencies/hueplusplus-1.2.0/test/test_Scene.cpp +da9e989c141526bebf84141d0b091a6b5dab6a9153fc27ecb2bd9bc75e9de43e dependencies/hueplusplus-1.2.0/test/test_Schedule.cpp +21442bda6576ca1a46dc3aa286d58c247b24b5cadbfd48d2d922ff488fdcfaba dependencies/hueplusplus-1.2.0/test/test_Sensor.cpp +22f59d6f544a840b3fbfa3e845f8c5fbcc4aa3564df64c3071ae96bc2130d50b dependencies/hueplusplus-1.2.0/test/test_SensorImpls.cpp +ef33d1d0e49bbe51340945dcb86dc62ae79cc2eea460d66708a084aa72e671fe dependencies/hueplusplus-1.2.0/test/test_SensorList.cpp +f6a8af538f74f6578f5fc64778cf6331ca05d91928558a56336975a9f3e2d14b dependencies/hueplusplus-1.2.0/test/test_SimpleBrightnessStrategy.cpp +81be89101bfff211d10a83dd0047ddcdec0293fd8cd8307fe4b0ef25e44d755a dependencies/hueplusplus-1.2.0/test/test_SimpleColorHueStrategy.cpp +9038306d8bb65f157ad199c062b8749b9004c45966e39c19f2eb54e9b2bff5e5 dependencies/hueplusplus-1.2.0/test/test_SimpleColorTemperatureStrategy.cpp +be00c02b0010622cc15109a0959fe48f97319ff4787eb663c638bc1538718095 dependencies/hueplusplus-1.2.0/test/test_StateTransaction.cpp +95174c5137d6fba9da437e2a4a8c300013e576ca8162b4b2842b0adfb3f616f4 dependencies/hueplusplus-1.2.0/test/test_TimePattern.cpp +cfc6139832f5b42902acded14e1134686ebc41d158d80b42c481719b25b3c0b1 dependencies/hueplusplus-1.2.0/test/test_UPnP.cpp +c0c207e895e598083a59d9fa1d4bdc5e17e3b6c93bb98d171c3d794bb0a51726 dependencies/hueplusplus-1.2.0/test/testhelper.h +853b2eb3bf7632c949f4a2dd6fb8a629c5ff25cc0059d29c7d21915dc719487f dependencies/json/nlohmann/json.hpp +f52dda035a687e0835775eb1c2f6aa72be7116f6810546d343afd6c23ec0c042 dependencies/libe131/src/e131.c +453fdbfbe1a994827e5e453a3f32cdd8b08f79b170d6a856021c61c43f073f03 dependencies/libe131/src/e131.h +c33d57eace96bb694546fefa7f51d56591681c115d43e227298f711dd4815712 dependencies/libusb-1.0.27/MinGW32/dll/libusb-1.0.dll +3f050dee9fbf995bfbed2f4969131877fcd1b59bfd3afd2a767ff6844e872430 dependencies/libusb-1.0.27/MinGW64/dll/libusb-1.0.dll +1d467c2428e52d1abc44711f8126295fbdb7986f41186b77991d1f9d6ee8dd39 dependencies/libusb-1.0.27/README.txt +8a4e184a0e588233cac4c28285fd31422e033af23c9231c1ca304953934c4cb3 dependencies/libusb-1.0.27/VS2013/MS32/dll/libusb-1.0.dll +106ba24f62b8007abefbd109c2512719da869932ae31f9eac8cbabc287d53318 dependencies/libusb-1.0.27/VS2013/MS32/dll/libusb-1.0.exp +66f39de107a6b07b03e4bbc2cc63e65f31fbb452d6c24c136d75a7ff76f123c9 dependencies/libusb-1.0.27/VS2013/MS32/dll/libusb-1.0.lib +3dad65bf0f17836c22ecf6ad9f2c367c478b33a6c2b55e1347a662cfb1069269 dependencies/libusb-1.0.27/VS2013/MS32/static/libusb-1.0.lib +4b7a3a22b335af6fee13e657e16ec82ee5110cf44b840fe629b8e07b3c855331 dependencies/libusb-1.0.27/VS2013/MS64/dll/libusb-1.0.dll +98e2decbb8b30041c0a70040056cfc62134aba1bbc4400800bddca3d9fbdc913 dependencies/libusb-1.0.27/VS2013/MS64/dll/libusb-1.0.exp +856ccf151c65165d5aa1d27263a07adf38b80fb532803331dc584a3ae030700f dependencies/libusb-1.0.27/VS2013/MS64/dll/libusb-1.0.lib +ec26b73893d6863c8c058d931ac06a008b704371be46691e2ad4e1787b676c9a dependencies/libusb-1.0.27/VS2013/MS64/static/libusb-1.0.lib +74e719c49ef1dbf1837bec2675e8c1a5c29d5dbfcbd5fe57f585aec8b5ff0223 dependencies/libusb-1.0.27/VS2015/MS32/dll/libusb-1.0.dll +de72a04d0e393600134213090243a06633a49afb6ab697b299e5972b5574ab7f dependencies/libusb-1.0.27/VS2015/MS32/dll/libusb-1.0.exp +be64dee326a6dd5eb3bed55da9e97e392696398caa703e064f55a2cc65ca9fcb dependencies/libusb-1.0.27/VS2015/MS32/dll/libusb-1.0.lib +18ea72039ca7de9971ef47de3916c4647bb023cb20b42eff530b3b6cc60b0ba7 dependencies/libusb-1.0.27/VS2015/MS32/static/libusb-1.0.lib +44c7914f39e26c246fb5adf889be89e493c399bf2cb4d6e6943a66955476a8df dependencies/libusb-1.0.27/VS2015/MS64/dll/libusb-1.0.dll +c16bcfd0857aeda6b11e883c5e4f75c4c0f0e72129191a6e03ace35dbc705df5 dependencies/libusb-1.0.27/VS2015/MS64/dll/libusb-1.0.exp +00007bc8fe7fa28f09c4969eafa795fb33902f23e2e1a1a86173143d07321eb9 dependencies/libusb-1.0.27/VS2015/MS64/dll/libusb-1.0.lib +90938ec91a1404ad99f3eb105ce336a574304f262384b6398d86fe1e184f43da dependencies/libusb-1.0.27/VS2015/MS64/static/libusb-1.0.lib +867296024ca598c9ed10a1d70eedbff04e71d4416792b79d0c20c8ce88b828da dependencies/libusb-1.0.27/VS2017/MS32/dll/libusb-1.0.dll +8ad93d26af254475c125fa3513c747fe25c77626e060a8e9be6c6d103e627cb7 dependencies/libusb-1.0.27/VS2017/MS32/dll/libusb-1.0.exp +2992eb70013e3595751d32de052221a4e7c2ba65fc47d7b1fbe7fac4d8d741d4 dependencies/libusb-1.0.27/VS2017/MS32/dll/libusb-1.0.lib +479bc0409b7fa606edca38344d2aa2819a98c94ff6975a04198bc8251152ba32 dependencies/libusb-1.0.27/VS2017/MS32/static/libusb-1.0.lib +c06cfd8152723a2e6b6a6b51afda12a63b963d1e694a4547cfc7edd471458746 dependencies/libusb-1.0.27/VS2017/MS64/dll/libusb-1.0.dll +d08f1361577cdb9cc7f3544efc41cd4f993e5f12fd4234e0184dc05d23522620 dependencies/libusb-1.0.27/VS2017/MS64/dll/libusb-1.0.exp +e09738d0c4219eadca447f074b5e33813a89b4245aaf7bfb334e06f7c7bd9ee4 dependencies/libusb-1.0.27/VS2017/MS64/dll/libusb-1.0.lib +879b62f09db6426dbc4fbebc7f362ba030a374499ff11968e22c2dabe36584d4 dependencies/libusb-1.0.27/VS2017/MS64/static/libusb-1.0.lib +33158a41a9aa6d464e0a597240f7e81855f58f591f0e8c9b53237448598f3367 dependencies/libusb-1.0.27/VS2019/MS32/dll/libusb-1.0.dll +021f8d7cdff0904b00d3ba5fddc974cb442d89abf1236a0326b49d20e1487b98 dependencies/libusb-1.0.27/VS2019/MS32/dll/libusb-1.0.exp +a84000eec9fab3184818cc85d17def7f1e2ba8ca00c356bc5b7243c95599269a dependencies/libusb-1.0.27/VS2019/MS32/dll/libusb-1.0.lib +d52f341ac8a4ae00e179fc3fd8694fadc2a10d9bbfec5fddbf127e08a26cb69f dependencies/libusb-1.0.27/VS2019/MS32/static/libusb-1.0.lib +a8c91f0ff68fb7802a9f4416728f0eeb4d99af4ceaa4ef7dfe9374e76e375018 dependencies/libusb-1.0.27/VS2019/MS64/dll/libusb-1.0.dll +98454b269aca73a35edc8f41b9abdda7f6e5e5508b4b4283036ea0f50380cc0d dependencies/libusb-1.0.27/VS2019/MS64/dll/libusb-1.0.exp +8e1e40fe3474ca2f7f62fac6ce8fe5253a39dcd9845dc00af95581522a49002d dependencies/libusb-1.0.27/VS2019/MS64/dll/libusb-1.0.lib +b582f27d235b96b02dbda8b8ab189fad3a7c7f64f80352121f469e1082c0ba91 dependencies/libusb-1.0.27/VS2019/MS64/static/libusb-1.0.lib +d4b4b670ce43d99c7d6d41eac3d780b6438da318a31d9638cc1d89402ad9edb4 dependencies/libusb-1.0.27/VS2022/MS32/dll/libusb-1.0.dll +2ca9adec70c26e61b9c71b72db318dd79905c186119be3daa9dddcc58756bca7 dependencies/libusb-1.0.27/VS2022/MS32/dll/libusb-1.0.exp +9569b13bf6b301bb2086dfcc393a9ba3897dcd60fcfbbdfee1c3384dc0d1e030 dependencies/libusb-1.0.27/VS2022/MS32/dll/libusb-1.0.lib +059bea5aca43b763d59bff0b592e94c66b51e019eb94560b3a6dd86d3f7830ed dependencies/libusb-1.0.27/VS2022/MS32/static/libusb-1.0.lib +d4e5db4fad8bef7201dc5a4a71ca997e6dccc9da25e8a988d4be63b13a02208d dependencies/libusb-1.0.27/VS2022/MS64/dll/libusb-1.0.dll +fc1a90473a6edee9d611b7730be94ae82aaa71ff33daf8b042f0e06c2554af38 dependencies/libusb-1.0.27/VS2022/MS64/dll/libusb-1.0.exp +8259d0de0f630c0ed8b45074f9e6e37848254c65be0a9d746bc1b02e97a9f70f dependencies/libusb-1.0.27/VS2022/MS64/dll/libusb-1.0.lib +61f157dc5bb9e2f9a071208ef339b95a219395f733ebbd2d262aa80d7170811b dependencies/libusb-1.0.27/VS2022/MS64/static/libusb-1.0.lib +0fc527f41c5874b733961d213c587bc0b3c0f45cd7e081d0a141a1c6bcc1d069 dependencies/libusb-1.0.27/examples/bin32/dpfp.exe +0bf4cd56dec45056537ea170f26b97d4638a7aad645a84d5684be60c54ffce7e dependencies/libusb-1.0.27/examples/bin32/dpfp_threaded.exe +55ee3358a9c4ddc4fc23de5971aaebe14167b87881ce72907f592bd13bf4e6cc dependencies/libusb-1.0.27/examples/bin32/fxload.exe +2e152fc48e70143d70c464b1d0007d035b6aead7f251470834b80e530aa44cb8 dependencies/libusb-1.0.27/examples/bin32/hotplugtest.exe +89ebf41143fb03d500f4acfcbcca251e77ad66a8a98a57c2de28c0150d0440c0 dependencies/libusb-1.0.27/examples/bin32/init_context.exe +2237c462c0a6bd2fe19423c69c5b96bcb9814eb167d7fd2956ce1a5b2eac68ca dependencies/libusb-1.0.27/examples/bin32/listdevs.exe +e9b2ea58c104eb414332794b49735c172be1b60aae28418ae53145891aae914f dependencies/libusb-1.0.27/examples/bin32/sam3u_benchmark.exe +7761f41faa4f80d39ab23ff130aa2afa3f8329233a9bb0e21293773755316ab2 dependencies/libusb-1.0.27/examples/bin32/set_option.exe +a6e1d9e45cb849859773b698314c795993f09842fdeece75df604979ec24ed86 dependencies/libusb-1.0.27/examples/bin32/stress.exe +5795c9a6a86cccdd0cd1ff211eccf7d23a8acf5e3d06ef169380298e8cdb6900 dependencies/libusb-1.0.27/examples/bin32/stress_mt.exe +c5363d673e474fce5e8fd1babfa3d26e308f58cabc365bc614ab142880951657 dependencies/libusb-1.0.27/examples/bin32/testlibusb.exe +12532af5406cfed9c88e4f1e39008aef7f876fdeeb1469d047030b5da4a91fd1 dependencies/libusb-1.0.27/examples/bin32/xusb.exe +d05b618fdee0f4c146398a88bc5885c0625774858eb261bab0cc28250ac45d34 dependencies/libusb-1.0.27/examples/bin64/dpfp.exe +9f3ae9e61716a358bf4a5e61eaab1d74557147ba64615a8b696c2455740d26ef dependencies/libusb-1.0.27/examples/bin64/dpfp_threaded.exe +2eab0b95595a2634a5ed25d952f6e835220b76d8e5dadd7ae009e35e4e917975 dependencies/libusb-1.0.27/examples/bin64/fxload.exe +501ca0dad9f2637ef75c978552bf0745648616d4ad705a3cbd7ae4013f3e457a dependencies/libusb-1.0.27/examples/bin64/hotplugtest.exe +fc19f7e4b76b883099b2944dbc37cc8cefd73921f8dc6e019b4ee93847d7ce18 dependencies/libusb-1.0.27/examples/bin64/init_context.exe +9f3aa82de865081611522f8192620f8d9d0e9d9070d746fdfcf0f3c56736dc40 dependencies/libusb-1.0.27/examples/bin64/listdevs.exe +5c58cf7ce02d6b219587b5501f041fb5109392eac54762881fef577992319cd5 dependencies/libusb-1.0.27/examples/bin64/sam3u_benchmark.exe +03dc8dfdc8417bd930d2a6d509f3447255f224cfc3204d3b5c8082a9d50e9b58 dependencies/libusb-1.0.27/examples/bin64/set_option.exe +5cab0676f3f5c4a6f49483f73699325267a6c6bebadffb9399c91d10069f5354 dependencies/libusb-1.0.27/examples/bin64/stress.exe +1c77aa7b20aa8c2faf3fae98eee023ac9758d7633ea7c76f33711284c6721c41 dependencies/libusb-1.0.27/examples/bin64/stress_mt.exe +3a85c5a712a149fb170f42f7680839272d7b2ad706f296587e572708707a20f5 dependencies/libusb-1.0.27/examples/bin64/testlibusb.exe +35825fded5652665dc4336e745c4356618124bca978e12b3bd789f60987935d8 dependencies/libusb-1.0.27/examples/bin64/xusb.exe +2df55a303d8984a32e8b242f3d3a905ea7ae4d7ec74419c105b5fc2465ef37e9 dependencies/libusb-1.0.27/examples/source/dpfp.c +4395c0f045e3adf2535e02422b22b9428f02390f3ba7fff526312b32d77f4437 dependencies/libusb-1.0.27/examples/source/ezusb.c +7257be3a60164001ca312cce6d6c001a5bdd5d595f8c01dc3afe540077a98432 dependencies/libusb-1.0.27/examples/source/ezusb.h +747ba601b8bcd4ff9eb99159f8bead9902704402c80befc3bba97b2ba863ae73 dependencies/libusb-1.0.27/examples/source/fxload.c +1762c77128cb30dfe4e265f5e2b5ac2437ea4ea97ddc45e81dcf1d2208ac7527 dependencies/libusb-1.0.27/examples/source/hotplugtest.c +3147a4f823854fbbdc45462c1ba7d2aaf8f21574ab68fab5e0bf93ed891d4003 dependencies/libusb-1.0.27/examples/source/listdevs.c +c52f5a11dc432813f6cacb181e2594cfc501263c5ef159da0d0579be4d4d2ec4 dependencies/libusb-1.0.27/examples/source/sam3u_benchmark.c +cf7c36ce4a75ff6f2eac92f9e8d82a9eac73c71284cabdb61c98ed2ef813fd13 dependencies/libusb-1.0.27/examples/source/testlibusb.c +6e81c181f579080697e9f951eff3587c3771672a76531ad82174944b31a2c042 dependencies/libusb-1.0.27/examples/source/xusb.c +eee5f747f7e0f33f36581f9ff82872a99286269eb97df28ff1c9a135825f159f dependencies/libusb-1.0.27/include/libusb.h +fc9c2461f4dd0fc619db32388245ccc34bacfccad5e445c7ef21d9ed09dfc09b dependencies/libusb-1.0.27/libusb-1.0.def +6091e246700fc0d0396016d60b5abf46f3f92b8a7cb2a3d28fd85fc85b935b36 dependencies/macUSPCIO/macUSPCIOAccess.h +10fdc3c214808872e20fdba6fff08a707956b7d03da94694ee2f9d61b8be9707 dependencies/mbedtls-3.2.1/include/mbedtls/aes.h +af7ecc7401104f9b43e017025853732810c7abcf0a5580c08d7b64c05c4fa0a9 dependencies/mbedtls-3.2.1/include/mbedtls/aria.h +f9fd3d9dba1d7c9a6c538517c1ce87e2c066dd7e6245414a7ec4af79f32e3861 dependencies/mbedtls-3.2.1/include/mbedtls/asn1.h +7a670e08536b0c3a00b6deac5e136282fdc08d1aca14ec5beb009e9764f565e0 dependencies/mbedtls-3.2.1/include/mbedtls/asn1write.h +cb27e291dd9899adb1734b5c8f543685116ae65132cc8466ff83727ede6f0b9f dependencies/mbedtls-3.2.1/include/mbedtls/base64.h +5bf74840bd35dca0d6bf5d71b37e268074d3ba0d24d3a5a528a77a2ec5b6d60b dependencies/mbedtls-3.2.1/include/mbedtls/bignum.h +efb9eac5e01df9659e8442be723af674eaed675433fb753b8c31f8477fded0d2 dependencies/mbedtls-3.2.1/include/mbedtls/build_info.h +737e8b23dbe03d25f13bb84e65c5daebad458458e8aa81a5cae1c2aba0244b28 dependencies/mbedtls-3.2.1/include/mbedtls/camellia.h +ec3bbfd1d689829bd6ad68c4e3c13d35d645bbf0d4bb342982c20f9eb2ccc5a0 dependencies/mbedtls-3.2.1/include/mbedtls/ccm.h +f60bb3020405a305936efa4f191bc2a8c0afbe96deba54234481d89bda6e8ef5 dependencies/mbedtls-3.2.1/include/mbedtls/chacha20.h +577d2919918163bbb11e760b7cc959407083140a8bc3198ef7954343567adfee dependencies/mbedtls-3.2.1/include/mbedtls/chachapoly.h +85190e13c1503180523f2e3738f2e4152ab74ec41802fc57e673a400b086dd87 dependencies/mbedtls-3.2.1/include/mbedtls/check_config.h +577eea0213cdafc56f778f5d3e34eded6c3e267cf3d67a380547855fdcac0435 dependencies/mbedtls-3.2.1/include/mbedtls/cipher.h +57b64c8c075644dd1df1356961867d60ddf00134546683e988bd282b811a1fab dependencies/mbedtls-3.2.1/include/mbedtls/cmac.h +e9fd3a73a8a080a1f1f73261a3c2fbe8c4a3f420c36ecbc17da56eaa45d37295 dependencies/mbedtls-3.2.1/include/mbedtls/compat-2.x.h +7fbd83d8c3f132c88c5a9cc9f55926ae8a9bf3a4a3d84a1c4b1009a936dab06c dependencies/mbedtls-3.2.1/include/mbedtls/config_psa.h +02f2916160d1cf0cdadeda4ddc588d1eb6c87ffd4b84c2c8a7c950411be0b296 dependencies/mbedtls-3.2.1/include/mbedtls/constant_time.h +c9342955e7e4fc648d15c8c2c5849f30f810ff5de43b66a8922b5aacbc98e950 dependencies/mbedtls-3.2.1/include/mbedtls/ctr_drbg.h +97e4a8e2f6d1264da8268d707af259fac1753f7b454602da2d0f30544a5780c2 dependencies/mbedtls-3.2.1/include/mbedtls/debug.h +a87d630f3041eacf890997bcb93b3e6e9296fb1e0a80e7e097a195bbc303f565 dependencies/mbedtls-3.2.1/include/mbedtls/des.h +08e07a8edcf14ddff5845afbf1efc14235138754857e825e27e2e87326ca7181 dependencies/mbedtls-3.2.1/include/mbedtls/dhm.h +23a145606a47004dec4a8070e72d651d1364599b9a34fce772dda4702518e52c dependencies/mbedtls-3.2.1/include/mbedtls/ecdh.h +2a0f7e245bfc26828080928cd7466d87bb82b869a1318359d349559cd8a511e6 dependencies/mbedtls-3.2.1/include/mbedtls/ecdsa.h +2fb799c92936c16806b4158def1c7ac4a0ac311cf45d1a95ddf2e43cb22d54d6 dependencies/mbedtls-3.2.1/include/mbedtls/ecjpake.h +9aa2464b0ccb464a6508b7ce946c1ff53beb83250515bb466a79b49fde6bac7d dependencies/mbedtls-3.2.1/include/mbedtls/ecp.h +64a3dbd7304bdd286de3ce9bd2ea93b69d79b2e1ccffcb8b4b8249d4e5f299fe dependencies/mbedtls-3.2.1/include/mbedtls/entropy.h +ec384e540bb91c780270e3f78447769ab0b74d002a5760a3e2b44eb853153f52 dependencies/mbedtls-3.2.1/include/mbedtls/error.h +092430bba9681f0d8d1c6279099edbb53c50f47e2e2ef3bedc3a53afbb84f050 dependencies/mbedtls-3.2.1/include/mbedtls/gcm.h +828ec4136892bfe3a18e16da9617bdd45d042aeb5aff749eca7c7c82f0a4caef dependencies/mbedtls-3.2.1/include/mbedtls/hkdf.h +b2b36e7ae7ef3031b654564b38375e5754dda042da56c7911d93360f09327055 dependencies/mbedtls-3.2.1/include/mbedtls/hmac_drbg.h +bbc37c56e4b883fffa8eb93e1d66e57b650cadd7e64e52158feb4b397b05695a dependencies/mbedtls-3.2.1/include/mbedtls/mbedtls_config.h +cd63304fec2b2ffb24d1e6837c8c5cd726654149576ba09b93edc9e1b8c4b814 dependencies/mbedtls-3.2.1/include/mbedtls/md.h +b86e4a1c213278d20c74ad3c564b0e1c304485ad60452005db001592113eec26 dependencies/mbedtls-3.2.1/include/mbedtls/md5.h +5d1b34b0a29c2525622cb26a125575e1046c744ca60fd15a77907eb201e659e4 dependencies/mbedtls-3.2.1/include/mbedtls/memory_buffer_alloc.h +5b5f7882c574f5f807d4dcea58a4f1b5b0c28be570e68b7912a1d7c97842ca3e dependencies/mbedtls-3.2.1/include/mbedtls/net_sockets.h +207c7805c80d3a634e9b5e44f6d641558246eeb69220d225177d39cead831acb dependencies/mbedtls-3.2.1/include/mbedtls/nist_kw.h +e0e22d0e81036cec7c3e5aff38403f962f0e98700c9a61edd197d566db51d71b dependencies/mbedtls-3.2.1/include/mbedtls/oid.h +3ee3449b97bbca2e47490743724a0c044bed968e6db81188e91b5a77b9aeacb1 dependencies/mbedtls-3.2.1/include/mbedtls/pem.h +6264644ffe9a7f876dd390a19529e7ad9fff0ff0bb940801ff1b146c34ff71db dependencies/mbedtls-3.2.1/include/mbedtls/pk.h +364154051d139c2a37ebdee87c5193a7d9321a3a6aa87ba83a93bba59145f992 dependencies/mbedtls-3.2.1/include/mbedtls/pkcs12.h +e5f326ee057ac56cbed3f1a6fd913b5612b2ac8233a20966c2df15889dad1cc3 dependencies/mbedtls-3.2.1/include/mbedtls/pkcs5.h +1797a53d1179b5ef0783b0417d27b1b475e25ba694dbcb5ab5e2a1ad17688b98 dependencies/mbedtls-3.2.1/include/mbedtls/platform.h +6714b715c4b6b21d6a0b38ab0934b2a04871c7c80c3ce2f5c2115739b1b7dbd2 dependencies/mbedtls-3.2.1/include/mbedtls/platform_time.h +7176c519203f3bb2440aade51ff64b91ca28ed22b8fd37d189b5f77b9feca43d dependencies/mbedtls-3.2.1/include/mbedtls/platform_util.h +e399a9cf25d9a21dd4dd9353b4c1dddc401806b7968dccb9f2900d39613bbc69 dependencies/mbedtls-3.2.1/include/mbedtls/poly1305.h +268b9e02ec15e0882a833716f21dc49f2bf08f5c162a745c3baee33279741bd4 dependencies/mbedtls-3.2.1/include/mbedtls/private_access.h +ed6eefb73749ce0f005747bcdd5c7a4cf90342df1669581ffec6bd81b2e702c5 dependencies/mbedtls-3.2.1/include/mbedtls/psa_util.h +82c0b55742ffb08e8912d016703fb65b8eca39545a9860170c33eab1b23cdf25 dependencies/mbedtls-3.2.1/include/mbedtls/ripemd160.h +023fed6a14849bb7f40f868adc7ad47fcbf553aefd88a7586235b1c80f878634 dependencies/mbedtls-3.2.1/include/mbedtls/rsa.h +dcc4d110a4f949d0c374e91e73beba409b86ddde9fb19bf101943769dbb1c6c3 dependencies/mbedtls-3.2.1/include/mbedtls/sha1.h +2fb9f41984b9ce42e978b33d2f297d1a085e9acd39547f9713fa71c5bdfbff47 dependencies/mbedtls-3.2.1/include/mbedtls/sha256.h +14bf7cb29e077cfda0190d8911043d276a478562a9b3961267772c952fab957d dependencies/mbedtls-3.2.1/include/mbedtls/sha512.h +0bafd533d0740d44da9a0a20731735153440f75e944579083fcac0aa7ab874c6 dependencies/mbedtls-3.2.1/include/mbedtls/ssl.h +74376debb8dcfdf5a166558cdbbf7ddbe3f938262cdb4d9a7aaf9806ef9ace60 dependencies/mbedtls-3.2.1/include/mbedtls/ssl_cache.h +15df23c59c326887c0703c6a6c7c295a087426dfa0b3ed6559342e897b588503 dependencies/mbedtls-3.2.1/include/mbedtls/ssl_ciphersuites.h +94f8c811c1cfded55235be3b672fb45c2340a9dff3e003843a6c17cd9d680dd3 dependencies/mbedtls-3.2.1/include/mbedtls/ssl_cookie.h +e6c6cd2c212762dff4a34edfadd6af0ae778f6b7c7bf72e78c5eb3a60f46c35f dependencies/mbedtls-3.2.1/include/mbedtls/ssl_ticket.h +cfb8c8a42c4feb58a7ecd4925cda7c1e0ac93a0f4d2a5b89d059e2db22e2bfdc dependencies/mbedtls-3.2.1/include/mbedtls/threading.h +a06bd2e6a04c4c0af617d167146acc9e8077a0527d9be87555428af29d0e2d0b dependencies/mbedtls-3.2.1/include/mbedtls/timing.h +18ca02ce03e76f27482d96f0755cf0d4dca1cfa4165a1f608a2f9a215a50e795 dependencies/mbedtls-3.2.1/include/mbedtls/version.h +fc43c81230ff017bfadb853e0afd8ff08c6ba7e105ca256ac44798fb5a80c81f dependencies/mbedtls-3.2.1/include/mbedtls/x509.h +e441612c635c949b4c05ad50a5fdb0f70b60db09bc88382906d2ef994d1b2a01 dependencies/mbedtls-3.2.1/include/mbedtls/x509_crl.h +2ed0689674257133c217fc7c55e5a613cc267f252beb5935f368d9920eed9c15 dependencies/mbedtls-3.2.1/include/mbedtls/x509_crt.h +c483aa2479c30ba1583ba753aeb6a195a89fbbcd2001307f43aa185bc5b26261 dependencies/mbedtls-3.2.1/include/mbedtls/x509_csr.h +9fa141af163ce44ab3e366392ad3ca67c09e5ced9667d2cbaa51b2080933ba51 dependencies/mbedtls-3.2.1/include/psa/crypto.h +4ad9b97a200f658a3bffda6660aa47a9064b1fcd903189d59bb05c356ca53aaa dependencies/mbedtls-3.2.1/include/psa/crypto_builtin_composites.h +3be979e966516936edb1573867a5053a66b05be5905016ca91bd654c37fa5cd2 dependencies/mbedtls-3.2.1/include/psa/crypto_builtin_primitives.h +dc849dcc3f70657d4cc3bf302efe41e495320cd969ebe083b6653a2ec96910b6 dependencies/mbedtls-3.2.1/include/psa/crypto_compat.h +b8c2a327756ed8f2262018818a2cac78ee1cedfb18649334736df490c944c727 dependencies/mbedtls-3.2.1/include/psa/crypto_config.h +6f780b3abcb8a83e2a9874c46391721e346d822e8cb2aa85b5f91cbf97a6179f dependencies/mbedtls-3.2.1/include/psa/crypto_driver_common.h +0525410816468ee32d1331ee1a30305bded0ffb6a40c50cc26278782526a859e dependencies/mbedtls-3.2.1/include/psa/crypto_driver_contexts_composites.h +ec9fea9a2d99ccacba8f1e7feca9364eee23790a19d7b043e94ef13e0fb2f913 dependencies/mbedtls-3.2.1/include/psa/crypto_driver_contexts_primitives.h +cc74b7523aeeede275d55593b94505b2b29842b3cdc01e752310b4384d3fc37b dependencies/mbedtls-3.2.1/include/psa/crypto_extra.h +a6dbcd2083e033cad99c9b7cb7159d41999468cd78876b734a9a24a40bcb677a dependencies/mbedtls-3.2.1/include/psa/crypto_platform.h +cef17113fb9e1b31f45d2b713ad464b9478a0e99418f71c2952db5162f37189a dependencies/mbedtls-3.2.1/include/psa/crypto_se_driver.h +05765a42487c3618eaedb97554d13afc6772f17ea6708ec99cb217200c9f641c dependencies/mbedtls-3.2.1/include/psa/crypto_sizes.h +18d603856eef01af7f444b334db369686625450728daac8e1cf4b5db7ba0c3dc dependencies/mbedtls-3.2.1/include/psa/crypto_struct.h +e0f7f2b90ca2a36adcf73d07940c24e770e024630a0a2cc8b46e577a210fc18b dependencies/mbedtls-3.2.1/include/psa/crypto_types.h +d9a01ff38b5d20215567fb7c78cb5e35ff6c522c737143d180e94fd68473c865 dependencies/mbedtls-3.2.1/include/psa/crypto_values.h +161c6afdb3e6787976bc1c529d6432a4be20d05b67152ee0a5bd9ea02aa4a5cd dependencies/mbedtls-3.2.1/lib/x64/mbedcrypto.lib +5127f8ed4648ec32b9980f2d2fe3308a8164a2e6bed9bb4e4338943141fb9c38 dependencies/mbedtls-3.2.1/lib/x64/mbedtls.lib +77878fd68a1f67c1f55cc59c6eb2c221b878bd9f72c8a55f9ba4b0effc4b508c dependencies/mbedtls-3.2.1/lib/x64/mbedx509.lib +3d0b1541713c21ffc08a1428dbb9effd436f3447018224542aceaa69b8ee2b62 dependencies/mbedtls-3.2.1/lib/x86/mbedcrypto.lib +6db920d11fcfcd56be47ad33dfc6e05e89386771e047772993724fd52e6ead67 dependencies/mbedtls-3.2.1/lib/x86/mbedtls.lib +0da88282e54d7552166fbf716eed95b161e361aa6bf0ddff84374cbdda44ee3a dependencies/mbedtls-3.2.1/lib/x86/mbedx509.lib +dade8ee341b4e9606a42e8d6067d5d036417bbf11cf255ab5ecd2b005d72289d dependencies/mdns/mdns.h +17f339e7582ff4d4d56e40b0e87d77eda8d8f8e62806278ddf8fcf0b8b3d9a84 dependencies/stb/stb_image_write.h +2ded4401edccbec3f0f239d3a885e0d7e4d09c14cb1d4ffe1bb3f24e106801f0 dmiinfo/dmiinfo.cpp +5a5a55fe853db0ccff5bb0ffc5ea319ea72b0ade1b293d319a7f1d62094ef872 dmiinfo/dmiinfo.h +82d28a432dab144b522d217bf32f88de31a2861e12d430c5b52f98717baa68bd docker-compose.unraid.yml +d50c2aca25ac6cd7810d4b9a3ab14dfec6a6a1b7272d20ac62e3b2598c6cb102 docker-compose.yml +15eb33e89457f1311f7696028c40a0651fec6d962447efddb8ed1cebe47cfd7a docker/compose.diagnostic.yml +10cc4740f26b4ad86791c24c5497588aa01f93d5fa03e269442f0f4e85819e54 docker/compose.host-network.yml +b562469ac260ca2cb171719888ca902dd55f41cf4730ae4951d2c3fca053a0b5 docker/entrypoint.sh +91f35a0cce1e4e1a46d8dbb938fb33d824a06b0afa786c20c065f794cb1b0c61 docker/healthcheck.py +8e9cd6946aa75eb1a3b6c79fe97d8086f367d5ec782063f454ca6524533e3373 docker/supervisor.py +92a2237e9cdc6d9414a36423bcacc6f98d7d38212d8c469f224859aae25ef06c docker/unraid-lumaops.xml +77967ce2fc262dd83a65cad06f465b6cc1a6872d4a4c4d48ef352b4cc08855f2 docs/ARCHITECTURE.md +27e9b6c73eb342bf6f70ec83e9b10fa09e89e6ad6d7ccfaed1a77684700587e3 docs/BACKUP_AND_RESTORE.md +179025f084d86217199eb66b07072515a81296f7ed8d56aa324ce09f74842e8f docs/CONNECTOR_DEVELOPMENT.md +22a6a0f091beec8054761f89f85e8fad2edb95144dad24607ec8ac4166c334d8 docs/CORE_PATCHES.md +afb502c9ab613dfbad561e1ba8d226bd7d5eb0aa0bfbbfe37f913de4831439bf docs/HARDWARE_ACCESS.md +067a7f0b2f9667ad796758fa5468cbaaa8b5c60e963025a66e509c27c3df5bd7 docs/OPENRGB_UPSTREAM_SYNC.md +c4520f0184fe10580a92fb56faead7737598ea6707c136cf16338a9d4bfd1900 docs/PUBLIC_RELEASE.md +ff25efab40c24093125bddd0b1af092ba10d474a8dc71238c01f947da43ceb86 docs/REMOTE_AGENT_DESIGN.md +35c806bd8236f9a339d75ef85eac66eddca4b49dc0c51a56ff327ddf03133890 docs/SECURITY.md +9fc08d75100943c45c9fac7f72ab7c093895e4630c72f8e72d01d1e4d0859ed2 docs/SOURCE_AUDIT.md +c2bf0ace46d6b81b468a5cd43986d749fc7021a3d8275cf6abe0d3380164d8f2 docs/TESTING.md +c8e0a87948cf77ec678af4e520e7a88188f0b4ebff7243f4e5b09f6064cca15b docs/TROUBLESHOOTING.md +e97acd93dc84abd2de2b11fb5790a4478b157585d7534f1b4e2fd639008035d9 docs/UNRAID_DEPLOYMENT.md +c7270475bcbb0dd0d23b3f58ae2733de15c49a38e33b50c390988aa87f897f49 docs/VERIFICATION.md +1093bec3263c48c998e4d57830ba3ceacec77ebe9dfb087e7dfbad22026700a4 fedora/OpenRGB.spec.in +c10d4caf6a869af85d3ce3bb1be68fe3948e81e1f9610937f877462b71838e27 filesystem.h +c38ecb9775785feedb6b41ec018749bb5922f3685ac60b9d79dd3cc10378f669 hidapi_wrapper/hidapi_wrapper.h +f2faead3f530bb53f756929d6940cf072cb6390cf570033d07041111cd4f56ed i2c_smbus/Linux/i2c_smbus_linux.cpp +df5803bb80d6f809ff543fec6c539782ac9459d838413a78038a2d0e479863d6 i2c_smbus/Linux/i2c_smbus_linux.h +85e71f93d70ff65e802e4fbcd5358ed688dfe7397fb4156cd1e43376b56df5cb i2c_smbus/MacOS/i2c_smbus_i801.cpp +de6dd61cb6bb1bc86cdabc07b4b62f65a8d1947ace7d2bba3b7411e9f6ad2564 i2c_smbus/MacOS/i2c_smbus_i801.h +cc8f0fb99b732816f4d1571692162214061aa5ca789e99b95309e112b181e67f i2c_smbus/MacOS/i2c_smbus_nct6775.cpp +cd11b81a65855e297534a7eb4f635aee35591977e44f0b3a6d9cdfbcc277e4ac i2c_smbus/MacOS/i2c_smbus_nct6775.h +ab1de61607a7fe3313970c976f277cd12e6a6806517b1671c41bf0c47c5a3d39 i2c_smbus/MacOS/i2c_smbus_piix4.cpp +955e8cb17492333fc48def30b373b708d6223c0f007ef22e52df52adafd02c21 i2c_smbus/MacOS/i2c_smbus_piix4.h +279164b01f75a374f4efd53bf1e160d0250d17525e936712251922358eaf7943 i2c_smbus/Windows/i2c_smbus_amdadl.cpp +2fd9c7bd53d592208d6d5299033bd878377b5a3d8d5380cdd7060160a2bfd3a7 i2c_smbus/Windows/i2c_smbus_amdadl.h +adf84bab6baaab7393147dcc3824340e29c900ed89aaa912c4613b7d098d99f9 i2c_smbus/Windows/i2c_smbus_nvapi.cpp +edcb7c50d5bffeda9a45726885418aa38238114876150ced5b36c698f204ca33 i2c_smbus/Windows/i2c_smbus_nvapi.h +d07afd590b9c0bf28f01c50ebeeb616eb48cd34fd8106700ef619579fcb6ac29 i2c_smbus/Windows/i2c_smbus_pawnio.cpp +34840e1f1ba3390442c59841fed665518f6683358d42fc39da378ceb007e8e7c i2c_smbus/Windows/i2c_smbus_pawnio.h +b2edaec2d61bb10acf0cc06696971ec1be9746391da216e0d2dc0267780ff8ba i2c_smbus/i2c_amd_gpu.h +4f6783bae65eac2413fb9473af776fb661e22d6a348f0e84ade627c9dba593e3 i2c_smbus/i2c_smbus.cpp +33424b4008a5e59e6a5a0f2b248d50349e27155a2d83d286215bcaac88e8f8d1 i2c_smbus/i2c_smbus.h +a07346da5eec127d4dc704e0d22ef708a9397c11e3fe1c1ee02b7596c0338f55 i2c_tools/i2c_tools.cpp +264c450b4c2d1caa6b60e474b7b468b807be9c8e260d8c118983e8d8876a7747 i2c_tools/i2c_tools.h +70aad822a14d4b4adbf216fc75e489bd5e8fc1d042a525992985f5f06fdf828e interop/DeviceGuard.cpp +82bbc7954d008a5395de75853d7482fea72acc4efb6899fba2ac554eaed4dbeb interop/DeviceGuard.h +51991695fdf970f27c9d48db28536a27c5807f074440c266ac0f4ac52a66af19 interop/DeviceGuardLock.cpp +54fceda99daa86e25c10395b8a1424e7edd64ddbf95feb7d0c466da4db66c381 interop/DeviceGuardLock.h +9dcd328473aadc6a1581566b741203aefa96335458db3b278120e08781fb96e2 interop/DeviceGuardManager.cpp +ee77d5b8c74bd44e96e87cefa2a0757212577607fc3b084d2cb040a148569be5 interop/DeviceGuardManager.h +c408e4e326a5ddc226b60cafecb7e143b05a3e5aa848644bba6d672bb9df3d23 lumaops/backend/README.md +af649aabbd734647d72d47bd8c238691c7d01180015d54e7af94a8725e930e58 lumaops/backend/pyproject.toml +0975713ea3610b0b05a217ed352e60b69659078073569cab187ece790f85c4b0 lumaops/backend/src/lumaops_backend/__init__.py +d6866d2e6e7a50ab34b7609621566de9e8249ce86c45ac46937b647a41d30e7b lumaops/backend/src/lumaops_backend/api/__init__.py +d26fdcce6a596fe83c1bc59c8c5823cd45d3530a29f64e4fea0d9fb66582cace lumaops/backend/src/lumaops_backend/api/router.py +76855b801ae6c41c276b6cbf360ba6783a51621e4ffd786acd943399365ed973 lumaops/backend/src/lumaops_backend/application.py +8d7c46e89f2194d01a92ff527472f85f7fe81ea01a52938f1d02709da759909d lumaops/backend/src/lumaops_backend/auth.py +67adec80846c2a5de915fa59bf890dccc6d3ef05ce9880a763cee5d7b2930cc2 lumaops/backend/src/lumaops_backend/config.py +87cd80c78d8e4b314fd3285a263d3b0be9e8f3bdd7434bc83d0d5db3a5a3eda3 lumaops/backend/src/lumaops_backend/connectors/__init__.py +aab1b4de783fede11099bbe69dbe3cc8701d10a6cd02dcbf5ea64d01ecfb76de lumaops/backend/src/lumaops_backend/connectors/base.py +2a934909fe5552251e8ae226a61f23fa8a7b8b60d640fb031fca28e8bd457eba lumaops/backend/src/lumaops_backend/connectors/mock.py +74bbb06f5fdc7995c06dfc56350c805d5801476775d448af71d04af16edc6b0d lumaops/backend/src/lumaops_backend/connectors/openrgb/__init__.py +f09936dcdd9aefc08fa81dfb46e259ed36e78893d3b0185df5ab87cb959be068 lumaops/backend/src/lumaops_backend/connectors/openrgb/adapter.py +e699c8e763db253252ef362d1ab5ab3d2dd631e971e080154c228ebfdd9eccea lumaops/backend/src/lumaops_backend/connectors/openrgb/protocol.py +827a3e370d01efb21907d99206a7cbaed73a1ac68e39d913874452938f3a1ded lumaops/backend/src/lumaops_backend/connectors/registry.py +412f40e72ef16d3631bb2da21541ba68ca3eb4e7491838ac630d6cfc39723560 lumaops/backend/src/lumaops_backend/database.py +c08a4f86ab139d213331aa3830363c5706532d3ef4d273c8964cd79ab959270f lumaops/backend/src/lumaops_backend/errors.py +cf096bdf87d92403fa53418109bf715d1cd677e1b3122c8b4e09041b4f8f9f49 lumaops/backend/src/lumaops_backend/events.py +c13b0613c9d2003c43bab5b3c8eace73cadcab5d656d13962d54dffd2ec7e275 lumaops/backend/src/lumaops_backend/logging_config.py +c7afb1374aa6e89fcc9cb3c6faa31efdb4d5d23e8e3a200784e6a23ac7bcb15c lumaops/backend/src/lumaops_backend/main.py +3ba8f2f5f04647394503e78481594d0aeadb18620cf40a97fb27efdb94ad1a52 lumaops/backend/src/lumaops_backend/migrations/0001_initial.sql +a78d385960733b76a0e1ed7151b6ad706041a8ebfc3b4f0d7c2853800eb83401 lumaops/backend/src/lumaops_backend/migrations/0002_automation_descriptions.sql +49fc2201f2710138689b2e3c1b483929b508f00746f863930a9053df6140a735 lumaops/backend/src/lumaops_backend/migrations/0003_device_desired_state.sql +789c95f7fecacc213ef32b5e55df1c63a00168898b6320ecef90c2c4889401c9 lumaops/backend/src/lumaops_backend/migrations/0004_device_classification.sql +8cbe81f90648ed4947291673e3c29c0de622b072eaaeb695e1005f9e51eb1374 lumaops/backend/src/lumaops_backend/repository.py +5343fc91e5e66b76df19ce9afc3db760ace7bb5aeba03b6c8575548c8a63d61e lumaops/backend/src/lumaops_backend/schemas.py +596bdcccf22a08219ddcae1eeb2ffeb87d2bb7328c21b52cc4233a25e7f2bc3f lumaops/backend/src/lumaops_backend/secrets.py +75eaa5152e68c420a46ca64ffd455d9093265aa310d571dd3cfcc32ca98887b3 lumaops/backend/src/lumaops_backend/services/__init__.py +9104b7e9577b74910cf9927d2eb181ec35cf71c8ec3d389b82498e59e5403d58 lumaops/backend/src/lumaops_backend/services/automations.py +35ba0f55dced709d0528a9f479745610c52eac16c27b6da1c0903ef0330d4b36 lumaops/backend/src/lumaops_backend/services/backups.py +90833240ec04347486aba5059b22bbb3404d0dc925675c4063b0860a616e2758 lumaops/backend/src/lumaops_backend/services/commands.py +874367d4107a475692ce11f351dba61717f49771dc527795048acc511ad98cbd lumaops/backend/src/lumaops_backend/services/diagnostics.py +d411161228eb873fd4c392e9dd9b18a2545b11ce9200391e370bb775027f8c01 lumaops/backend/src/lumaops_backend/services/health.py +9105776f25a5e44313be862a1bc7e32e3bf5a0b74a425cdfbb78c22b5ce79ef3 lumaops/backend/src/lumaops_backend/services/inventory.py +69dcf4511964e669f897e57357904476b80d2dc18861c15c8e9f24dd84547e20 lumaops/backend/src/lumaops_backend/services/resources.py +11107d2f0e0a7b3d1280e6e7d099cb0bd85bf83be33de44e8449e083c906ee0a lumaops/backend/src/lumaops_backend/services/scenes.py +bcfc8400dd31b02ae851e21f74f3f82e835a3ca1993f642d8585eb444456a972 lumaops/backend/src/lumaops_backend/services/setup.py +0df16cc201ca55f9a582c1874b3bc4cd9efd074e3a85832a8671a41510777d28 lumaops/backend/tests/conftest.py +ca1a03d0b627f31406e780027b18e06d43a79c32dbdc8f006eb763e690267597 lumaops/backend/tests/test_api_flow.py +2193b05f76a299d7f4cc29c135c9bbdae68a1c2524a1e4c251ac2410f1930abc lumaops/backend/tests/test_connector_contract.py +f0b849c0b3a1aac5a2817b2781736f695ba3a5eff960d5dcdda48b9136c94f83 lumaops/backend/tests/test_database_and_security.py +deeff314ed3a278883fb526156d32364b45689b4bbfae471c20598c549416d5b lumaops/backend/tests/test_openrgb_adapter.py +f69395fad831f1a1acb227c822f6b9a461ce45ec7b0cb406cf7e456de1c254a5 lumaops/backend/tests/test_protocol.py +f5d4181712b046db2b5ad4d3ed05f8d3e8f86e2c6021600e895e781652ece55e lumaops/frontend/eslint.config.js +3f3b3adbc90cee6da7364a37ec80d016e857c01c640a7557489bf8eb1b7a5049 lumaops/frontend/index.html +7eeb88331cf346d032fe09e3caae80ac1f8e550019a50b1aa4eae43f6ba828fb lumaops/frontend/package-lock.json +89aec88a99c9f5d23a5b424898e4802bd0e5143e10e3dc5be9ef094e01628ebc lumaops/frontend/package.json +a4be4bd36d00940f7bbc06e6cbc1106d9784aeabbd6371ad10e9299a6c841ab0 lumaops/frontend/public/assets/favicon.svg +2e8ee5b12e6fc406dc191211e273890234935010e573852ad675fae584064573 lumaops/frontend/src/App.tsx +1a9d994b178561d75c045f48baeed977e9c08ca732052a6b6f9088d6e89ed74e lumaops/frontend/src/AuthGate.test.tsx +7adbe1dabaf17848e4fdd8b0b2abc9225541cba348196404ae44bf80e9702bd7 lumaops/frontend/src/api/client.test.ts +672d02b8c27ff167df35d55d3b674dde4dadc6a5a5096b16acabc4c6d125be52 lumaops/frontend/src/api/client.ts +95cf8aba0bbfbfd294d983a5a5f1428b8f59b6391f1c8d6d990c261d1de58507 lumaops/frontend/src/api/hooks.ts +e15fafffb674a81412c528818aa990afa5cc05a9b4c4f6538abe453b70d16db6 lumaops/frontend/src/api/types.ts +48a9797862182fcf6dc7d4368b60095d5b0ac6320e4722ecb2750a021e047893 lumaops/frontend/src/components/AppShell.tsx +1c1f698b357eec1f332185fd012252e9b0bc92da5359455ca65ef1759ffb5c4d lumaops/frontend/src/components/AutomationEditor.tsx +b1e2e7810df7e8c7e191b5a81eb87d2412e773cd689b99d15f7375caf9ff5fa6 lumaops/frontend/src/components/ConfirmDialog.tsx +8599448566d0652be2038b68f363b18f5f9efefa8976df79fe624d66a5908609 lumaops/frontend/src/components/DeviceCard.tsx +9e89e4da28642e38885108ed45803eb5ceafedd619ef60b3fb6c23a399b1c68c lumaops/frontend/src/components/DeviceGroupCard.tsx +8b0ca15a7cbb51a75cfa730780484e05e31839d1db89f077d8155aec549325ac lumaops/frontend/src/components/RgbControlPanel.tsx +956e33f07b8ef9d015d71be11c4c7de4288b796d61e3ee73d1b5eae0b3a5f043 lumaops/frontend/src/components/RgbZoneControls.tsx +991400d2e32b90c7df0d483d708dda7a9d19ac2f28b80b853654a6b9e6de5d22 lumaops/frontend/src/components/SceneEditor.tsx +df26f0799b05ea54b422570f0a1418a96c012b2055739179b60532f7487b9975 lumaops/frontend/src/components/Toast.tsx +77964b6adf93a8c89531483d1430a14a6d35844ad1ce4532c5110675c51c56fc lumaops/frontend/src/components/ui.test.tsx +c49acaf0ceb08dcf98fbd779c01bc87d5c7a839f82f9a499adc610c725dbce7c lumaops/frontend/src/components/ui.tsx +007c79110d84a52b8e4f156d557d888df2e757e9d5ec98f3239266c5d8163cf7 lumaops/frontend/src/lib/i18n.tsx +528ad3f533fb2ef474a621c512b201263edb46c49726f41e21d55097ec4e4167 lumaops/frontend/src/lib/theme.tsx +ffdc878a9249c0a41075fb81c205ef7b13072a2af14f919bf85967f15af6ff17 lumaops/frontend/src/lib/utils.ts +f39e0d88948de5c2ec66585fac76a2d139f560f98f246c3cf5d3f9e2c49b4717 lumaops/frontend/src/main.tsx +cc503371d4ce0130af68c5925672091909922744228fedf88f2397f9928846fd lumaops/frontend/src/pages/AboutPage.tsx +4c258cbaf2457c81d931e45f779dcaf15e2bd7431057207f14af3eaf30bb33a9 lumaops/frontend/src/pages/ActivityPage.tsx +60d9bb31c49defdd15283cd966fee1be83610e72dce9630768d557a1c1551634 lumaops/frontend/src/pages/AutomationsPage.test.tsx +5a5c9239e429a78b14afc69091670bd18e1ca26c791b489353fa1d60c0fc888b lumaops/frontend/src/pages/AutomationsPage.tsx +d852caa5a967cf0e417062f8e991dc4af9f3922255c8cf2c32782dfbb5ed5887 lumaops/frontend/src/pages/BackupsPage.tsx +101a9e1d2d8d3881f00f4b3788e41bbc2c1e13086032a4b069900c688bc55145 lumaops/frontend/src/pages/ConnectorsPage.tsx +6fe7a5f10b770ecc40c25cd5dc7b990c1898a6ad91ecbbcf9fc15ece383e2ed9 lumaops/frontend/src/pages/DeviceDetailPage.test.tsx +52c74e1032825e1b55a01681692733dedda034e497979f97107da94c9a290c27 lumaops/frontend/src/pages/DeviceDetailPage.tsx +5b2da76b1f71f15426dabb9441a1733fa98692758846057961a119b1dcc0fad2 lumaops/frontend/src/pages/DeviceGroupPage.test.tsx +3dc8fb05edd5a4ed91cabd9950f9c28253235fc459e3417de48d2e0f1c4774b5 lumaops/frontend/src/pages/DeviceGroupPage.tsx +5e93d1c979c5c9c92a5c05299576d96a44ffb43c53117993e8e828b418d7ae3a lumaops/frontend/src/pages/DevicesPage.test.tsx +0a267caf2dd2233173a48ea8104eb39c4301b8ebe0d7f8fe97a6f3a0ae6f1588 lumaops/frontend/src/pages/DevicesPage.tsx +276c9a5cc550a10a0bef3768c7a9ef9a9e727dd274ccc3a76224b66f5988e0b3 lumaops/frontend/src/pages/DiagnosticsPage.tsx +19687375a5f7d2c5946c38fa2c06676cd1c1faa383b88e4ebb8f95015056973b lumaops/frontend/src/pages/DiscoveryPage.tsx +f4dd5f1f69e1b37141977b8d95d730702942453bcd743d1162ccffc5a4d1942c lumaops/frontend/src/pages/NetworkPage.tsx +f1eb44bea20c862c709eb95b51ef30b2de4d7db8a257a27a201993aa1c273256 lumaops/frontend/src/pages/OverviewPage.test.tsx +1d4ffcdbc4d541b1f6dc1ea31c5b3a6dd58f48eb7605c34576890a5ae5a363c8 lumaops/frontend/src/pages/OverviewPage.tsx +feba9e6c6ab270c81f7e344f84f36977e71cb4cd3dd88248d801bab85064a23f lumaops/frontend/src/pages/ScenesPage.tsx +879c61f199e870a9d137caa400e26e120cf55e3d11d73dd9d643316f8bb511f4 lumaops/frontend/src/pages/SettingsPage.tsx +7313f45520c05f34413bc1f9e0d73205ce5263096149c9e7f97d4dd81d9df356 lumaops/frontend/src/pages/SetupPage.test.tsx +f77d6c0d10f14629c1b59b942ae4578f39c63122ceb4a2bd25117b3e2e4d1043 lumaops/frontend/src/pages/SetupPage.tsx +43a79a718c2d6e427972023f7222f10cf2083a0db0cc72c740f3cf2c6310cda9 lumaops/frontend/src/pages/SpacesPage.test.tsx +8d43e76a725a500a5d1d42963ac66e1b39b30cd32f4f9c78e2d9b49c1d1aed5f lumaops/frontend/src/pages/SpacesPage.tsx +48af3fb67c4a3aabe14b6ea31e4c09ab8be3487a9683ed81805fb566783aaa95 lumaops/frontend/src/styles.css +4d1f182ee971cf1bd56954a3d31508b6197cce2cc5235eb40cde266fb78513ad lumaops/frontend/src/test/render.tsx +a2871eba8853b32f0318e26305b25e43bb22eba25c76da8acaeaeae7aaca5a98 lumaops/frontend/src/test/setup.ts +f143733555ee727c891ccfa675fe94bd1a49c64aff112ec93e0b5121e9bc15e7 lumaops/frontend/src/vite-env.d.ts +ae4efde14f1ce21380f8e23770b6a87a8b90462fae1ca4ee37db31f1beeda36d lumaops/frontend/tsconfig.app.json +53a5de3ac873acc59864b5d565114c460d45f248e60e27ec84a2a621ad26d027 lumaops/frontend/tsconfig.json +8dba3e1ee9ad54ece2a07722062502bee996616b8957d3754ab1ef27088b5da1 lumaops/frontend/tsconfig.node.json +8a742ec5b998f5f23dfb8ddb70923db1390925d53e1e414355d09d444d850fe1 lumaops/frontend/vite.config.ts +21be4c199a629090df01ac3801c831118586f33c1c1005e822bb6d9c2de9ae4a mac/Info.plist.in +7e1590b1e84c5c13bb45674de5c118c70ac9c9e1e392d8e6230baa34a9623907 net_port/net_port.cpp +c5171b31394b325af4b839f39fb03c5ebe2d34c049a1f343024820cb0d1345ed net_port/net_port.h +e9d1d9178a2994a958f208c190ab2949cc5bca271764025c907b56c8c78080ab pci_ids/pci_ids.h +bed64fd18161bd3faa33640f2503d72308682e6bc5b3ee8c250da2138cc2e8b3 qt/DetectorTableModel.cpp +4888f6b3586f8252174e329139c9eb0ee67372e089240520607784c3e749b805 qt/DetectorTableModel.h +34a242c1a91cd25bfe60670e097e7532d3f4e173779c7df654b6f50bb414624a qt/DeviceView.cpp +085cb74f4f94e664ead29e7666365881d380b8edafac00953b486c10a1765316 qt/DeviceView.h +3b3359b529ba66ecf13b630246e7cf4b1348ad0cc4854be16a2fbd4978a44775 qt/ManualDevicesSettingsPage/BaseManualDeviceEntry.cpp +a4e38f0ba8efa3b1bad8ad9f619de697b74f543d9fef1be939178ace4828bc67 qt/ManualDevicesSettingsPage/BaseManualDeviceEntry.h +956e5023f51a78f08b4ef895745f4fc1dc478618c366e2243750c5f9173beefc qt/ManualDevicesSettingsPage/DDPSettingsEntry/DDPSettingsEntry.cpp +5e253a8b517b979d84a3356a604a207a1d61750137ffaa097091b4b6d97e3564 qt/ManualDevicesSettingsPage/DDPSettingsEntry/DDPSettingsEntry.h +188c146eb24ca8e21befc1b1ce1976c32993337e0fe7630c5dfecfe736779901 qt/ManualDevicesSettingsPage/DDPSettingsEntry/DDPSettingsEntry.ui +efc2940c628d7c539fdeb16f4c2e16265aef0c3f708089ec748d38ede3bfc30a qt/ManualDevicesSettingsPage/DMXSettingsEntry/DMXSettingsEntry.cpp +76b91918fcd096655adc2589b048325e31b333baeab5e51393cf225f172ade9b qt/ManualDevicesSettingsPage/DMXSettingsEntry/DMXSettingsEntry.h +99fdc2e23afd980af556313d99e249a2ceb3d4bac7f16987fc0ebe43aa5a8ec9 qt/ManualDevicesSettingsPage/DMXSettingsEntry/DMXSettingsEntry.ui +7c80f97e9d58101e0192b49f92929c2f3e0c17077077f3b11d2e7a3d529c1ede qt/ManualDevicesSettingsPage/DebugSettingsEntry/DebugSettingsEntry.cpp +94e2ff3bc054d4bcd31b05384c32019bfdbea7fef6dd3e06dd0b5353183aeb67 qt/ManualDevicesSettingsPage/DebugSettingsEntry/DebugSettingsEntry.h +30dc1633abdb3d9db676aa52c42ae5f991f5969fa2263e2e4431f7afb1b52cc5 qt/ManualDevicesSettingsPage/DebugSettingsEntry/DebugSettingsEntry.ui +7a147b6d128ef6cb0e763be598c57fe7d9ecca2ac23cb8dd5d4333a8d0cc1fc1 qt/ManualDevicesSettingsPage/E131SettingsEntry/E131SettingsEntry.cpp +d1bc8cfbabd233bd218eae22a1fa8259081dbb31254d6ebe1f1a13ddb738e7a0 qt/ManualDevicesSettingsPage/E131SettingsEntry/E131SettingsEntry.h +802271fe7a73ed17810056100f94f41ddd75f69232c62015131575723a20cd13 qt/ManualDevicesSettingsPage/E131SettingsEntry/E131SettingsEntry.ui +23cb71c3c9e221d777ca526c15df46361232a4fe14cba64f536d9f803c5e846d qt/ManualDevicesSettingsPage/ElgatoKeyLightSettingsEntry/ElgatoKeyLightSettingsEntry.cpp +1a4bd030c35bc86edace5da1a9287d7d1076e81eba2de1bc859c0c7b2f198bb3 qt/ManualDevicesSettingsPage/ElgatoKeyLightSettingsEntry/ElgatoKeyLightSettingsEntry.h +e4920facd161bfe6d7c28423d8fbed338f341fcd03828d71d6f4c7e177dfb819 qt/ManualDevicesSettingsPage/ElgatoKeyLightSettingsEntry/ElgatoKeyLightSettingsEntry.ui +6e797b23b5f97eee3fa16bc1a7a1dd31f7eba5c6853e210b7bc26259d8949702 qt/ManualDevicesSettingsPage/ElgatoLightStripSettingsEntry/ElgatoLightStripSettingsEntry.cpp +f0079cfbc47994b9755f6e9837ff068c9afab26e68392994d864f05ee85c6e84 qt/ManualDevicesSettingsPage/ElgatoLightStripSettingsEntry/ElgatoLightStripSettingsEntry.h +8aa821dd199b2e24b1410a458fd5a58f4bdf53b6f25de0ddadf273be8252fb6b qt/ManualDevicesSettingsPage/ElgatoLightStripSettingsEntry/ElgatoLightStripSettingsEntry.ui +118df49a6c0dac409d93572487bf241088b5ffd8f68e197e715e4f257107eb03 qt/ManualDevicesSettingsPage/GoveeSettingsEntry/GoveeSettingsEntry.cpp +6dc38e8ad608f16fe021de7326d571c59d4e8cc1fad4380bcd2ac1c74d19059d qt/ManualDevicesSettingsPage/GoveeSettingsEntry/GoveeSettingsEntry.h +051dca536ae0b37bef42b0c4a42f0c1a746cc7285c613430b725119b3539f757 qt/ManualDevicesSettingsPage/GoveeSettingsEntry/GoveeSettingsEntry.ui +1b23357b798520b8e31c132825bc2d19bac7d5b39918265656e2f62ec8544711 qt/ManualDevicesSettingsPage/KasaSmartSettingsEntry/KasaSmartSettingsEntry.cpp +00cdee32e6924e06c4f63d005b209eb7d76431d6ab8b12ee4880f571c2d7697b qt/ManualDevicesSettingsPage/KasaSmartSettingsEntry/KasaSmartSettingsEntry.h +8df79f805d55aa54e2730ad1f35d574f7de9e57706d215970a3d2429eeb4f5ab qt/ManualDevicesSettingsPage/KasaSmartSettingsEntry/KasaSmartSettingsEntry.ui +0acfadd0aa900cad0728eacae6797e39fd4713eaca36eec87a5a18442837312c qt/ManualDevicesSettingsPage/LIFXSettingsEntry/LIFXSettingsEntry.cpp +aa8ce0ac6bb97963eaad97169df171bcf1aa48e2c5655bc19c0b03c0f8fe307c qt/ManualDevicesSettingsPage/LIFXSettingsEntry/LIFXSettingsEntry.h +5bf5cceefa269fa93936d39c495532561529cc21d41af18a746f1240d58fb88a qt/ManualDevicesSettingsPage/LIFXSettingsEntry/LIFXSettingsEntry.ui +a2c4898b72a6bb8e8e9097a5180609a665992f57da9ff05250fc44c3e85b6ef0 qt/ManualDevicesSettingsPage/ManualDevicesSettingsPage.cpp +1b59d0a09d2e230421ecb490da0bdf804f09e7e9e9ca9f5df5db466b9a98cc8d qt/ManualDevicesSettingsPage/ManualDevicesSettingsPage.h +72c89179d9d7059c979de48aa6dff70bbdef4fd0b8ffdf8db8b58e01efa3053f qt/ManualDevicesSettingsPage/ManualDevicesSettingsPage.ui +b2aff3b64ec8594ffe2767c1f7afb683f7b5af656682c1e7fd7ce6c4e9682270 qt/ManualDevicesSettingsPage/ManualDevicesTypeManager.cpp +ec371aac61b8e27fecee63475e3e27fd595857e9bc7c301701f42e0856acd98b qt/ManualDevicesSettingsPage/ManualDevicesTypeManager.h +1d4e0b61482ff8ed89eb8bd405f59c1ad2a4604aa1936ed63f0fab540c57a700 qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafNewDeviceDialog.cpp +30aac9376a634b072a805e9332ed6e82a602e6d97a526bb9cf330f36fd6d8ec5 qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafNewDeviceDialog.h +ff56a842fb80c38087b606ab15f20d217627b1ca64d7dd2bddffc5a6cf32a535 qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafNewDeviceDialog.ui +83e20a26c32b27dfba6b893b47213574bb652181bae0d453d04459661412bd68 qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafScanDialog.cpp +80d02cd7424c3dad9cfe72213d17a6a00034b644df46f11802331dd3008c3eda qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafScanDialog.h +267a136146b9c4f21714ae6814632227faddbe6379bc242166835fa57b5901bb qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafScanDialog.ui +50ab49ba79227771a72ae4ff94d8cd67137f02af71f3d94f34ad135bc482eed9 qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafScanningThread.cpp +b6dc31dae1a5c4127bafb483337a33e0917c3cf96bf1c8eda06e7b74891c61b9 qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafScanningThread.h +34dc99f455807dc73db15813e21d10dd916ff92f5b786a41d19ea34060dc24d0 qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafSettingsEntry.cpp +dac2b5f649df8b05411f515448d78ac190caed718bd7ef806c0dbb8133672850 qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafSettingsEntry.h +2900e873263e14665290d2ff5256baa5545affd6965555f21c42160650d8f878 qt/ManualDevicesSettingsPage/NanoleafSettingsEntry/NanoleafSettingsEntry.ui +0c70973576f4e1ca14187d36e33fabaa53d0c42d74bb469b5ac5f7642590c140 qt/ManualDevicesSettingsPage/PhilipsHueSettingsEntry/PhilipsHueSettingsEntry.cpp +4916999d254328c7c53b551486061bff1cca02dc8b29e1fadc46d00b6a31369e qt/ManualDevicesSettingsPage/PhilipsHueSettingsEntry/PhilipsHueSettingsEntry.h +1eb790376bc03999278b5ce904d7bb2d6c1e3d60cd45aa6146e941b56eb97680 qt/ManualDevicesSettingsPage/PhilipsHueSettingsEntry/PhilipsHueSettingsEntry.ui +dcb878b6c75b0293518bc06556c42a1bdeb86d55679184c4c153d7abd692d2c4 qt/ManualDevicesSettingsPage/PhilipsWizSettingsEntry/PhilipsWizSettingsEntry.cpp +5c87cad2369c72b58421e5e6362cc975f1e3167cc79ac16b8c217953d8691f2f qt/ManualDevicesSettingsPage/PhilipsWizSettingsEntry/PhilipsWizSettingsEntry.h +dd35a90361b991bd4d5a3b8dfc33bdb85c68273716453ca943d2902da3a097f2 qt/ManualDevicesSettingsPage/PhilipsWizSettingsEntry/PhilipsWizSettingsEntry.ui +fb58713ed287b3c6b213a3d61435df505108d63dcc1e671c2d8b615212c9aa4d qt/ManualDevicesSettingsPage/QMKORGBSettingsEntry/QMKORGBSettingsEntry.cpp +ea5bf5b66c35b8c89e5c3eac70c65c0cf045fe3e458a23fbbd967b92d31df170 qt/ManualDevicesSettingsPage/QMKORGBSettingsEntry/QMKORGBSettingsEntry.h +05346d489e35e61ea91ccd6ad7c2fe2d2bf87ef1c0113183cb0db5019ddad2c9 qt/ManualDevicesSettingsPage/QMKORGBSettingsEntry/QMKORGBSettingsEntry.ui +361fd79392a0a86ecf4bb5b4e0962ff3e2ba4b5b776c77554e912d7d6b554995 qt/ManualDevicesSettingsPage/QMKVialRGBSettingsEntry/QMKVialRGBSettingsEntry.cpp +2a0335fb90d2e2381a8f24aa994d5524c0c965f13b7db6b314502c666a9344e5 qt/ManualDevicesSettingsPage/QMKVialRGBSettingsEntry/QMKVialRGBSettingsEntry.h +969e24c84240a4280bae45271f4ab7a97fa0508fd590488f10a51cfaf63b4678 qt/ManualDevicesSettingsPage/QMKVialRGBSettingsEntry/QMKVialRGBSettingsEntry.ui +3350dbe979bd800ae3c402fdbe8c02c63140b844967c1c5e14e57645eedda082 qt/ManualDevicesSettingsPage/SerialSettingsEntry/SerialSettingsEntry.cpp +41bb3502b445910c946205d7b7accaf8362c68401ef086a90a966d7d913d8323 qt/ManualDevicesSettingsPage/SerialSettingsEntry/SerialSettingsEntry.h +26a477099d186290dd4c5137adff8fe565d38aebb904e6a1b3df1d6b86acde28 qt/ManualDevicesSettingsPage/SerialSettingsEntry/SerialSettingsEntry.ui +b9d5ba28a478c834ee5779c2a778c3c3c0638b781c51cd62250ccc3d984c4dc8 qt/ManualDevicesSettingsPage/YeelightSettingsEntry/YeelightSettingsEntry.cpp +2e9d9ab19513f93da64cc3b939be3a786af75b6232ed2dd293b456d65077b263 qt/ManualDevicesSettingsPage/YeelightSettingsEntry/YeelightSettingsEntry.h +7fc4cf531b25934c5cf43d7dc5905f11dd5a87ef21e836cbf22dd43309ab8f62 qt/ManualDevicesSettingsPage/YeelightSettingsEntry/YeelightSettingsEntry.ui +e873528a9933ca13cd5290eb0af3462c688fa8e688812bf3d12d08ce705d7029 qt/OpenRGB.icns +40364769fe16f3044f5f58fdf542d28f2037bc9609a9bf80d60657c44a510daa qt/OpenRGB.ico +906e8fa1fb5009f481a37faf1e671167122ab8b1fe45a7aaa96e86cbd57a71a3 qt/OpenRGBClientInfoPage/OpenRGBClientInfoPage.cpp +06bd8522ee93ef4c71ede9f407edb4e646d3fe20bc4d635cb7d60e2aaf5bdc3b qt/OpenRGBClientInfoPage/OpenRGBClientInfoPage.h +930cd93471f53a358492cd7e729bd95edf941ac4186a2bb061480238dd0009e9 qt/OpenRGBClientInfoPage/OpenRGBClientInfoPage.ui +3847d057d41c251745bb057693a79e49a95bc849fd4662c65efb5101f9548d60 qt/OpenRGBConsolePage/OpenRGBConsolePage.cpp +a013e18ee30678a82562f498fe00d8b14600c192df249aed94eb6b68de3e5ec1 qt/OpenRGBConsolePage/OpenRGBConsolePage.h +32a5acdaecf99c00e7f72ad5b768fc52724986df2a00332fadb9d80e24f0dce9 qt/OpenRGBConsolePage/OpenRGBConsolePage.ui +77edf6eb88a65eece18b1bafd6dc5928920868c408b90ab1949f33c31bc41281 qt/OpenRGBDeviceInfoPage/OpenRGBDeviceInfoPage.cpp +efbfaef06e2aba660defb8d984ece9bbc74396359e11a9b4bd807a7ef696f3cf qt/OpenRGBDeviceInfoPage/OpenRGBDeviceInfoPage.h +27fbe0a39036d5354dd2b10111ef66b7a1c62a7ac58dad60430119b7cb78fb90 qt/OpenRGBDeviceInfoPage/OpenRGBDeviceInfoPage.ui +39f063ce070d7dffaafa169fc2ed77473b04c045668fa962bf7b223bd9f908d1 qt/OpenRGBDevicePage/OpenRGBDevicePage.cpp +eb1d7db7bd70c03bcc06ce9d3db5ed0988c358c190df40ddddad51ca07150585 qt/OpenRGBDevicePage/OpenRGBDevicePage.h +f412d59e2ba70882d6b246e116a52281706e11ddc3099c4853b3bd7aa6376882 qt/OpenRGBDevicePage/OpenRGBDevicePage.ui +15594004cd6b72e5f9a8c4186439bce7e964e667d512a3d94d01953fe5d0e4ad qt/OpenRGBDialog/OpenRGBDialog.cpp +6e3a251174bdd85dd10b43bed480e90bd473167f7f7ebf376303e79f57bf8a90 qt/OpenRGBDialog/OpenRGBDialog.h +f62aad837cd36da97ddb2ddc26de4166b55be18dda48942f9fce5acdb41c8858 qt/OpenRGBDialog/OpenRGBDialog.ui +26d1cbcd276b0ab8f071b92df7e826112d2750a0d955f6e577946727df996243 qt/OpenRGBFont.cpp +2031111427dc369f2cd4f5704f117d37d39f390030e398d8820c45d27bff999e qt/OpenRGBFont.h +647e8014896cf63c51bc9d873c10ea08b9b24652a9cd62574393218c1fa3b289 qt/OpenRGBGreyscale.png +24a01469bcb178ea73849e80325430e0c0081892ac44be3c0f29a8b8b722aa2e qt/OpenRGBHardwareIDsDialog/OpenRGBHardwareIDsDialog.cpp +3b56119f1f48510e8be7e5725c16badd72b7a84043f4c02ba3c45e0263ea2c73 qt/OpenRGBHardwareIDsDialog/OpenRGBHardwareIDsDialog.h +48ac118368680652768eb977e98457739e084f34ff90e578aab129d05be7ef4a qt/OpenRGBHardwareIDsDialog/OpenRGBHardwareIDsDialog.ui +a1593566deb15befc8021ce8e0c78fa2741c1f83b82257b2ffacaad6b6afa983 qt/OpenRGBPluginContainer/OpenRGBPluginContainer.cpp +1b5c97e32cd8a99c39089329ea55f7478ad936b9070d325ac39be80dc6cb081f qt/OpenRGBPluginContainer/OpenRGBPluginContainer.h +2a71e5648805496fc88dd19e1d2e8515c493828f0036a2c59256897007671578 qt/OpenRGBPluginContainer/OpenRGBPluginContainer.ui +f90ca7e573b64514be1b9a273e7d0eea6570e7c700c8864395cc0b48dddf0594 qt/OpenRGBPluginsPage/OpenRGBPluginsEntry.cpp +559f75fa4041ea0e7a08269dc87aeef987ab0ae52a96efaccd27393b7eb39244 qt/OpenRGBPluginsPage/OpenRGBPluginsEntry.h +00266aede7021246d5fab11c13a3ac41417034bf0ff4986c30fc0ce5a631570a qt/OpenRGBPluginsPage/OpenRGBPluginsEntry.ui +04d8419d204de61a0d2b9dd52e1c2a7b676cafa6b0547e224f8c0be6823e4a95 qt/OpenRGBPluginsPage/OpenRGBPluginsList.cpp +084eca43f262a8aa74159af29b22ce0a0f64e323175cce9a0fa6637a3e624ec5 qt/OpenRGBPluginsPage/OpenRGBPluginsList.h +56c14563d6f754a1a56943b812ce27d36a3df90bbc1602dae112160aa3f37362 qt/OpenRGBPluginsPage/OpenRGBPluginsPage.cpp +99046a181a43ba03b65cb40ebe5b796cef43f742ba8e0909a25d827fa507fa4c qt/OpenRGBPluginsPage/OpenRGBPluginsPage.h +13aa65903b0b0b5795e813895c3c2431fa3d98eff4aaf626a6ad1b0e0739a190 qt/OpenRGBPluginsPage/OpenRGBPluginsPage.ui +3d0e0f972bfb4befcd6e18670b73f2ffb3ec5f3fd133ef53f84480010f7a3d1c qt/OpenRGBProfileSaveDialog/OpenRGBProfileSaveDialog.cpp +368ef5e5e741ad560de6c5fadbf3ef79120c8a62368e71a4014d0f5eec900a42 qt/OpenRGBProfileSaveDialog/OpenRGBProfileSaveDialog.h +82e5ebef285dec7741a425676b6d8736f5a8249b0f4a38688af39310e58da428 qt/OpenRGBProfileSaveDialog/OpenRGBProfileSaveDialog.ui +4a3f7d32ccc1e9389063ad011292b4bf201ce2b8ef8d53076827e4defa1f515c qt/OpenRGBServerInfoPage/OpenRGBServerInfoPage.cpp +c8fe11fc926894799928f5692f1dd4c9ef2aa70e420236e8e22ad90fbdac29c1 qt/OpenRGBServerInfoPage/OpenRGBServerInfoPage.h +07dda97f00cd9c3909efccddbb308e9bd8b405e4162f093a9d2ee381440c90f0 qt/OpenRGBServerInfoPage/OpenRGBServerInfoPage.ui +de3f40513f2a6a26e656acbc3d7e10a0450677315cb609930a85d14f397f4f00 qt/OpenRGBSettingsPage/OpenRGBSettingsPage.cpp +449439d1eaa65b7ba318e590581f65e4afed565bfccc938136bbec000bcfb45d qt/OpenRGBSettingsPage/OpenRGBSettingsPage.h +656d2311927cfae53f8d3aa77cd3ed5a107d8854cb748f2f89f91353b4a96ffb qt/OpenRGBSettingsPage/OpenRGBSettingsPage.ui +722490b5f6ff76c579223c9f505bba8a1482739f696da01834bb8aa63c3f6348 qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.cpp +056a549c707c31ccd7b9f7719b873e7789f5a6f5488460dda1a38b73bda63398 qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.h +dbef61bde50d89f7d492fe7514e2ebadfcbc31882971db8cbc392e3f0b2e07a0 qt/OpenRGBSoftwareInfoPage/OpenRGBSoftwareInfoPage.ui +731a99d435e939068a511364cc3f1276f3f1a64361f90a6b114aa2c60b26eebe qt/OpenRGBSupportedDevicesPage/OpenRGBSupportedDevicesPage.cpp +f37594cd146c88abaaaf366b75ec108437d7f0b605a6b1af33b116dd3f2dca9e qt/OpenRGBSupportedDevicesPage/OpenRGBSupportedDevicesPage.h +3329168caecce5f19b82364d8ae9c5d157f4176a00b993c3c099b844b812a60e qt/OpenRGBSupportedDevicesPage/OpenRGBSupportedDevicesPage.ui +c518ec48d1379854f05a908208c02b25bd370661a5cd6f72b7582fbc41d8bc4e qt/OpenRGBSystemInfoPage/OpenRGBSystemInfoPage.cpp +1837e3b2df6eef5e21aeddf8cde2a96caba3c9da18e85630ceee1b8e6de5c126 qt/OpenRGBSystemInfoPage/OpenRGBSystemInfoPage.h +2f80788913dca441c74d8b8447e492a0fa3f16f4a4437f41e0736f88e536824b qt/OpenRGBSystemInfoPage/OpenRGBSystemInfoPage.ui +3e937bc23b517f89fe50b8e243dfdd40429b5226071b9c6250f9ee1f8559545c qt/OpenRGBThemeManager.cpp +e4686905fcddc288cca9be27d5513bb707a1c7f4c4363e7f9b03f4cec52054f5 qt/OpenRGBThemeManager.h +cfb1efcc3edf4cd95022563bb254c9848ca9e84e4ac9a399f24c4e55529f18d0 qt/OpenRGBZoneResizeDialog/OpenRGBZoneResizeDialog.cpp +0f000fa346783184d54e74a674cbba89df0b9cebb90b4cb0c1c8de45bd22093f qt/OpenRGBZoneResizeDialog/OpenRGBZoneResizeDialog.h +0439d2a51dccd78e72a06f951a0944cb90ab44a2acc603bd65a0cbb34fdc83a7 qt/OpenRGBZoneResizeDialog/OpenRGBZoneResizeDialog.ui +2b5e52923b0b8d757d7900a1c07bf9bb65f2740beee3096ad6dd7aa254c2204b qt/OpenRGBZonesBulkResizer/OpenRGBZonesBulkResizer.cpp +08c2ff8e03a159a70192d08bc6ce78de9f5d158283b98c3657333121dde41329 qt/OpenRGBZonesBulkResizer/OpenRGBZonesBulkResizer.h +d5e7c31c277bf909dfe8d4edfe966e99cac1380a86bca5e7983fac5ed828bc22 qt/OpenRGBZonesBulkResizer/OpenRGBZonesBulkResizer.ui +ce4f1930d5744f751408543b5eb1b8e7b8489048730065ef8e6344c0bc0b3ef2 qt/QTooltipedSlider.cpp +c4bc77cbdab60b1ed366172ef343256260e3f7e5ae0f2fe96887b35ae313e619 qt/QTooltipedSlider.h +f86b07b77162fa48faad208fa88fd7cc6a6c80285f51345ec01ec5b3c724984b qt/TabLabel.cpp +0ffc26001615755655796a5549a5fb748e138468db22b27df16f842b53de88e3 qt/TabLabel.h +4f9c511b21a845300111de87daeaa9ece22b13b3f11e63ee0a5fc49833d63a9a qt/TabLabel.ui +cc9b2ef9229874a891fb585b16d668eae5541c6b3512021ec9a767a78c66f8e2 qt/fonts/OpenRGB.ttf +1cb8f974db97f502edc95316735520bff5e9657e8cd2c8036c7cba5b4f46abb4 qt/fonts/README.md +4f07888370b682372007c1b9e0afbefdfc50f08695f16ec58279a0612777060b qt/hsv.cpp +b23e623f9678b13546aeca32ce5591ccdf12435e6199f204dc7744c102698120 qt/hsv.h +cb247de31cad27e20e6715db5db36c2e1508f81f220df91a1b62f26985984a4f qt/i18n/OpenRGB_be_BY.ts +f36388a04f0acc22720c35689d682a302f26f0dca041bcd62ecf5698a565f4d9 qt/i18n/OpenRGB_de_DE.ts +edf4b62cc1ceb22853271b2a8fd9db37b0badacdf31255be2c2af76a43b69238 qt/i18n/OpenRGB_el_GR.ts +eef8fab8f03ffbc24df2f5507330d128ef1eef04ffd9d2f0112f5e905868b158 qt/i18n/OpenRGB_en_AU.ts +e6a0d063e09f56107e66e6220bca8acb5ec0dda857618222f4114cd3a2f52e3e qt/i18n/OpenRGB_en_GB.ts +95e0dd9f2ac5834961d313ba29126bbb36e9c051acea1967f6ab4e530fb1fb7b qt/i18n/OpenRGB_en_US.ts +195e73bd2621afcdee6e49e84dd287b8b93ebf8cb19b7a30f9900b78eb13ef66 qt/i18n/OpenRGB_es_ES.ts +40694cb34df7fe655858e938ed9d4c827c30f05a0b0bf972599431da4f60c4d7 qt/i18n/OpenRGB_fr_FR.ts +26c6f1ef602858992ec114be9fad76b5292da5932dcdbb2e4db9075fbcdbd673 qt/i18n/OpenRGB_hr_HR.ts +d17d92e1b15044aba9fa57cfaf5249c3a37250e8c4669c3e514c6c50faf73e81 qt/i18n/OpenRGB_it_IT.ts +0c7e7ea5143ef1178493d5ea838b87e9a5881f971a0a0a65fa4b3bc4876ade31 qt/i18n/OpenRGB_ja_JP.ts +f2b43b3fa67807d3ce12288409284220d04ad98b345ebe48a44843f263a6921f qt/i18n/OpenRGB_ko_KR.ts +2c297d605784f820e20668e71264466a8663c238b459c94b6d018a759336387c qt/i18n/OpenRGB_ms_MY.ts +1b7d24c009ea43c526773fc7a5d00c4e36f780b282b4c884535e2f58ddb10201 qt/i18n/OpenRGB_nb_NO.ts +22b120eb295dc73c98a005e8e1c480c139617a1b4d7af6ccc0bec5eac4d9e13f qt/i18n/OpenRGB_pl_PL.ts +7d8dcef9173d00198a6932b5a18cc0a7591018e82e0d20453a7cb66124b319f1 qt/i18n/OpenRGB_pt_BR.ts +7d80699699b8b1b5d07f13a12a28d9812a962dc33419df92a06af400cdd05350 qt/i18n/OpenRGB_ru_RU.ts +8984f6f8a419db3e7f081bcb0f612fe5f7ced46fb8788c9df2927d2d96d4319a qt/i18n/OpenRGB_tr_TR.ts +d5fefe3bcf2c9144d2b3ce4530a395810020ffd71cd7f10c283fee5e9caea6a8 qt/i18n/OpenRGB_uk_UA.ts +019914eee44cbb086939739a1c079a50281e1ad1d49fef953b8bbffa860692b3 qt/i18n/OpenRGB_zh_CN.ts +f68ff8c6245ab14238bb268629bc4add23299f58c847587811f4d452b999d2d3 qt/i18n/OpenRGB_zh_TW.ts +fe9e4e6e4dc8872f955f69580653a7befcc959d3eb7d8c2732309a0577d0b60d qt/macutils.h +d7fabb873d74d04a64f7e93e2249f4e6d7cef40aa0bc489cfaa13effb7f53541 qt/macutils.mm +35fc91f52993ab84049a8aba2e1a9dcaab5fa9d80f54ee25e09d9c4b8c92b7b9 qt/openrgb.conf +0068670b2fa413983de19866f0682f3bd31e118df6d0405bcb72fe607674b58e qt/openrgb.service +e93ca0351db028ae6af0097c3709fcb69222536637aa9f0b425e37bd54889b2c qt/org.openrgb.OpenRGB.desktop +b6b8c414bf1fb8afdfce6ba3435824f6b49a849276a2be88816ce0373767eee8 qt/org.openrgb.OpenRGB.metainfo.xml +493f21265a2783ba29b6a2cd94a3e8c040e7bf45e57d5ac18762b0edfbf4ed54 qt/org.openrgb.OpenRGB.png +a011c38b772f01204250800b120e7a63767f24de95bee316f4146ab54898367a qt/swatches.cpp +f0081784a7f1275b3b5c433742682ea75a92379968a589bc606d5422332d638d qt/swatches.h +7211568682ac092054f3c8d45a392c09fc72197835a3aa9bc3c7474ab312492b scripts/AppImage.patch +d367db3868ac46b452ebe8ae758a75363b24024478f7d53574ca11c46e190bc7 scripts/License.rtf +1ab208010b911add52a755403abb5cb59749073375e700d7c835cce212cff4d1 scripts/banner.bmp +3bab3fb8f36306bb30bf773fe83ff588bdc951a1052934b3431a0ef9b0962cfb scripts/dialog_background.bmp +4991639804743e2960eeda9ea91323885d17d2763c8c26ce10a05a2ed4a088cd scripts/export-public-source.sh +398862bef5e421bee7e200fce86244aba614cbea02b73b3420be1b19a582cc6d scripts/git-get-branch.ps1 +8ea8d87553b95b73d5fe1cc8c84d955f5091cf2572914364f549461a424bd25b scripts/git-get-branch.sh +172c4f52c197f427c79446134e0d810011ba2ce5ba89e03db156c3675608c8d1 scripts/openrgb-udev-install.sh +f527c95d094238b3a5cee99390bcd58704c1a17432f39d48ffc89ebafa80a7ba scripts/openrgb-upstream-guard.ps1 +da805501744cfb3726f1c36bc91d1a42ee5c1d9cc32cbb9834336c579216734e scripts/openrgb-upstream-guard.sh +aeedc69310db069e52bd770f2882046e6aa2f93c063218c6eac23e422f49113a scripts/prepare-artifacts.py +7ec8f32a79030ba625c2b7f444f3b22d70a94a983ab66189890b22b0a8cff6ea scripts/redeploy-lumaops.ps1 +dd3e9e8f73b0c504c55a399ba5a5bccc9db6e9a1a596e4cc214a3762484a0058 scripts/redeploy-lumaops.sh +3ba2c0867fe8beb5f939db6b593373e159b405d29017e7de9de8ac5d8ab66403 scripts/unraid-hardware-setup.sh +30941516a9ff1daad643f8bf9691b81c4585147baa4c67a5ff9c6eb36a99bb95 scsiapi/scsiapi.h +b7e077a01db9db5e0030d15951c95634866b907ce1516d517f72735462437b78 scsiapi/scsiapi_linux.c +b8986606495af1fa1db2596a5105b99958a7d63e66cc1c524441dda68cd974bc scsiapi/scsiapi_macos.c +ad40e61ea6ecd57d30dea59ae409264768c38e558e41b540de0dec6acddacb3d scsiapi/scsiapi_windows.c +52c63b3a513988c1f3c11274a690fc611db6265fa22693e7724127b018899855 serial_port/find_usb_serial_port.h +408c50fbcb4292fe19024a48e91966687a9cfecf2d9d8a84a8b6852802795d6f serial_port/find_usb_serial_port_linux.cpp +6bbb175497c7e812cfeb1a31e05a8a3bcc287f0d5b1b7f9a79f9e5328c8d7843 serial_port/find_usb_serial_port_macos.cpp +94be2c882d0346b3e638f8df7484f69d0299190b57eaf75917cc868e50743ad1 serial_port/find_usb_serial_port_win.cpp +15bc1a7bc6a28ea7be864007bf5e06c9567e12c873937f54d5a40e380c679ba7 serial_port/serial_port.cpp +8ee6605203cd554663b7959b3e389aab58e84e179633271b2cd934cb59752af1 serial_port/serial_port.h +9a028379621cea800b2b973092884666bd23fc27e63c82bcb0b4d5f14544684d startup/main_FreeBSD_Linux_MacOS.cpp +304ba1098179e54221681afc9f956e9bb0affca70431b83ba46ff26b138180ec startup/main_Windows.cpp +e55289b1ee19dd00616a59ccf9e62143e2ed96a3b5ef9d6b20709315f2027eeb startup/startup.cpp +86810006738f51a64f19e8c843053c0aa735ff5d70bca28ae723397af51db512 startup/startup.h +79638a011359f09b6a7e82b15499669f853fa8c25be9fd0334cb331b0742a84d super_io/super_io.cpp +889b140ec4f905ad86be54fbaa1f94ce7eb3461e809f8535b1f400f819483fea super_io/super_io.h +16dbdcee87d409600a376caf75f6f30d17c140cfb3d0124e1cc301f2bc3a3a81 super_io/super_io_pawnio.cpp +abd8ba352a78eca610c455e1049b426cc1b5114e1be1a9cab2ab65c9848f23d6 wmi/wmi.cpp +f27edf9f7513dcb916f4e87a9f02f7705a776f2cb829d15ee5b698eae22d2ab5 wmi/wmi.h diff --git a/PluginManager.cpp b/PluginManager.cpp new file mode 100644 index 0000000..769961f --- /dev/null +++ b/PluginManager.cpp @@ -0,0 +1,534 @@ +/*---------------------------------------------------------*\ +| PluginManager.cpp | +| | +| OpenRGB plugin manager | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "LogManager.h" +#include "filesystem.h" +#include "PluginManager.h" +#include "OpenRGBThemeManager.h" +#include "SettingsManager.h" +#include "ResourceManager.h" + +#ifdef _WIN32 +#include +#endif + +PluginManager::PluginManager() +{ + /*---------------------------------------------------------*\ + | Initialize plugin manager class variables | + \*---------------------------------------------------------*/ + AddPluginCallbackVal = nullptr; + AddPluginCallbackArg = nullptr; + RemovePluginCallbackVal = nullptr; + RemovePluginCallbackArg = nullptr; + + /*-------------------------------------------------------------------------*\ + | Create OpenRGB plugins directory | + \*-------------------------------------------------------------------------*/ + filesystem::path plugins_dir = ResourceManager::get()->GetConfigurationDirectory() / plugins_path; + + filesystem::create_directories(plugins_dir); +} + +void PluginManager::RegisterAddPluginCallback(AddPluginCallback new_callback, void * new_callback_arg) +{ + AddPluginCallbackVal = new_callback; + AddPluginCallbackArg = new_callback_arg; +} + +void PluginManager::RegisterRemovePluginCallback(RemovePluginCallback new_callback, void * new_callback_arg) +{ + RemovePluginCallbackVal = new_callback; + RemovePluginCallbackArg = new_callback_arg; +} + +void PluginManager::ScanAndLoadPlugins() +{ + /*---------------------------------------------------------*\ + | Get the user plugins directory | + | | + | The user plugins directory is a directory named "plugins" | + | in the configuration directory | + \*---------------------------------------------------------*/ + filesystem::path plugins_dir = ResourceManager::get()->GetConfigurationDirectory() / plugins_path; + ScanAndLoadPluginsFrom(plugins_dir, false); + +#ifdef OPENRGB_SYSTEM_PLUGIN_DIRECTORY + /*---------------------------------------------------------*\ + | Get the system plugins directory | + | | + | The system plugin directory can be set during build time, | + | e.g. by the package maintainer to load plugins installed | + | via package manager | + \*---------------------------------------------------------*/ + ScanAndLoadPluginsFrom(OPENRGB_SYSTEM_PLUGIN_DIRECTORY, true); +#endif + +#ifdef _WIN32 + /*---------------------------------------------------------*\ + | Get the exe folder plugins directory (Windows) | + | | + | On Windows, system plugins are located in a folder called | + | "plugins" inside the folder where the OpenRGB.exe file is | + | installed. Typically, C:\Program Files\OpenRGB but other | + | install paths are allowed. | + \*---------------------------------------------------------*/ + char path[MAX_PATH]; + GetModuleFileName(NULL, path, MAX_PATH); + + filesystem::path exe_dir(path); + exe_dir = exe_dir.remove_filename() / plugins_path; + + ScanAndLoadPluginsFrom(exe_dir, true); +#endif +} + +void PluginManager::ScanAndLoadPluginsFrom(const filesystem::path & plugins_dir, bool is_system) +{ + if(is_system) + { + LOG_TRACE("[PluginManager] Scanning system plugin directory: %s", plugins_dir.generic_u8string().c_str()); + } + else + { + LOG_TRACE("[PluginManager] Scanning user plugin directory: %s", plugins_dir.generic_u8string().c_str()); + } + + if(!filesystem::is_directory(plugins_dir)) + { + return; + } + + /*---------------------------------------------------------*\ + | Get a list of all files in the plugins directory | + \*---------------------------------------------------------*/ + + for(const filesystem::directory_entry& entry: filesystem::directory_iterator(plugins_dir)) + { + if(filesystem::is_directory(entry.path())) + { + continue; + } + + filesystem::path plugin_path = entry.path(); + LOG_TRACE("[PluginManager] Found plugin file %s", plugin_path.filename().generic_u8string().c_str()); + AddPlugin(plugin_path, is_system); + } +} + +void PluginManager::AddPlugin(const filesystem::path& path, bool is_system) +{ + OpenRGBPluginInterface* plugin = nullptr; + + unsigned int plugin_idx; + + /*---------------------------------------------------------------------*\ + | Open plugin settings | + \*---------------------------------------------------------------------*/ + json plugin_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Plugins"); + + /*---------------------------------------------------------------------*\ + | Check if this plugin is on the remove list | + \*---------------------------------------------------------------------*/ + if(plugin_settings.contains("plugins_remove")) + { + for(unsigned int plugin_remove_idx = 0; plugin_remove_idx < plugin_settings["plugins_remove"].size(); plugin_remove_idx++) + { + LOG_WARNING("[PluginManager] Checking remove %d, %s", plugin_remove_idx, to_string(plugin_settings["plugins_remove"][plugin_remove_idx]).c_str()); + + if(plugin_settings["plugins_remove"][plugin_remove_idx] == path.generic_u8string()) + { + /*---------------------------------------------------------*\ + | Delete the plugin file | + \*---------------------------------------------------------*/ + filesystem::remove(path); + } + + /*-----------------------------------------------------------------*\ + | Erase the plugin from the remove list | + \*-----------------------------------------------------------------*/ + plugin_settings["plugins_remove"].erase(plugin_remove_idx); + + ResourceManager::get()->GetSettingsManager()->SetSettings("Plugins", plugin_settings); + ResourceManager::get()->GetSettingsManager()->SaveSettings(); + } + } + + /*---------------------------------------------------------------------*\ + | Search active plugins to see if this path already exists | + \*---------------------------------------------------------------------*/ + for(plugin_idx = 0; plugin_idx < ActivePlugins.size(); plugin_idx++) + { + if(path == ActivePlugins[plugin_idx].path) + { + break; + } + } + + /*---------------------------------------------------------------------*\ + | If the path does not match an existing entry, create a new entry | + \*---------------------------------------------------------------------*/ + if(plugin_idx == ActivePlugins.size()) + { + /*-----------------------------------------------------------------*\ + | Create a QPluginLoader and load the plugin | + \*-----------------------------------------------------------------*/ + std::string path_string = path.generic_u8string(); + QPluginLoader* loader = new QPluginLoader(QString::fromStdString(path_string)); + QObject* instance = loader->instance(); + + if(!loader->isLoaded()) + { + LOG_WARNING("[PluginManager] Plugin %s cannot be loaded: %s", path.c_str(), loader->errorString().toStdString().c_str()); + } + + /*-----------------------------------------------------------------*\ + | Check that the plugin is valid, then check the API version | + \*-----------------------------------------------------------------*/ + if(instance) + { + plugin = qobject_cast(instance); + + if(plugin) + { + if(plugin->GetPluginAPIVersion() == OPENRGB_PLUGIN_API_VERSION) + { + LOG_TRACE("[PluginManager] Plugin %s has a compatible API version", path.c_str()); + + /*-----------------------------------------------------*\ + | Get the plugin information | + \*-----------------------------------------------------*/ + OpenRGBPluginInfo info = plugin->GetPluginInfo(); + + /*-----------------------------------------------------*\ + | Search the settings to see if it is enabled | + \*-----------------------------------------------------*/ + std::string name = ""; + std::string description = ""; + bool enabled = true; + bool found = false; + unsigned int plugin_ct = 0; + + if(plugin_settings.contains("plugins")) + { + plugin_ct = (unsigned int)plugin_settings["plugins"].size(); + + for(unsigned int plugin_settings_idx = 0; plugin_settings_idx < plugin_settings["plugins"].size(); plugin_settings_idx++) + { + if(plugin_settings["plugins"][plugin_settings_idx].contains("name")) + { + name = plugin_settings["plugins"][plugin_settings_idx]["name"]; + } + + if(plugin_settings["plugins"][plugin_settings_idx].contains("description")) + { + description = plugin_settings["plugins"][plugin_settings_idx]["description"]; + } + + if(plugin_settings["plugins"][plugin_settings_idx].contains("enabled")) + { + enabled = plugin_settings["plugins"][plugin_settings_idx]["enabled"]; + } + + if((info.Name == name) + &&(info.Description == description)) + { + found = true; + break; + } + } + } + + /*-----------------------------------------------------*\ + | If the plugin was not in the list, add it to the list | + | and default it to enabled, then save the settings | + \*-----------------------------------------------------*/ + if(!found) + { + plugin_settings["plugins"][plugin_ct]["name"] = info.Name; + plugin_settings["plugins"][plugin_ct]["description"] = info.Description; + plugin_settings["plugins"][plugin_ct]["enabled"] = enabled; + + ResourceManager::get()->GetSettingsManager()->SetSettings("Plugins", plugin_settings); + ResourceManager::get()->GetSettingsManager()->SaveSettings(); + } + + LOG_VERBOSE("[PluginManager] Loaded plugin %s", info.Name.c_str()); + + /*-----------------------------------------------------*\ + | Add the plugin to the PluginManager active plugins | + \*-----------------------------------------------------*/ + OpenRGBPluginEntry entry; + + entry.info = info; + entry.plugin = plugin; + entry.loader = loader; + entry.path = path_string; + entry.enabled = enabled; + entry.widget = nullptr; + entry.incompatible = false; + entry.api_version = plugin->GetPluginAPIVersion(); + entry.is_system = is_system; + + loader->unload(); + + ActivePlugins.push_back(entry); + + if(entry.enabled) + { + LoadPlugin(&ActivePlugins.back()); + } + } + else + { + /*-----------------------------------------------------*\ + | Fill in a plugin information object with text showing | + | the plugin is incompatible | + \*-----------------------------------------------------*/ + OpenRGBPluginInfo info; + + info.Name = "Incompatible Plugin"; + info.Description = "This plugin is not compatible with this version of OpenRGB."; + + /*-----------------------------------------------------*\ + | Add the plugin to the PluginManager active plugins | + | but mark it as incompatible | + \*-----------------------------------------------------*/ + OpenRGBPluginEntry entry; + + entry.info = info; + entry.plugin = plugin; + entry.loader = loader; + entry.path = path_string; + entry.enabled = false; + entry.widget = nullptr; + entry.incompatible = true; + entry.api_version = plugin->GetPluginAPIVersion(); + entry.is_system = is_system; + + loader->unload(); + + PluginManager::ActivePlugins.push_back(entry); + + bool unloaded = loader->unload(); + + LOG_WARNING("[PluginManager] Plugin %s has an incompatible API version", path.c_str()); + + if(!unloaded) + { + LOG_WARNING("[PluginManager] Plugin %s cannot be unloaded", path.c_str()); + } + } + } + else + { + LOG_WARNING("[PluginManager] Plugin %s cannot be casted to OpenRGBPluginInterface", path.c_str()); + } + } + else + { + LOG_WARNING("[PluginManager] Plugin %s cannot be instantiated.", path.c_str()); + } + } +} + +void PluginManager::RemovePlugin(const filesystem::path& path) +{ + unsigned int plugin_idx; + + LOG_TRACE("[PluginManager] Attempting to remove plugin %s", path.c_str()); + + /*---------------------------------------------------------------------*\ + | Search active plugins to see if this path already exists | + \*---------------------------------------------------------------------*/ + for(plugin_idx = 0; plugin_idx < ActivePlugins.size(); plugin_idx++) + { + if(path == ActivePlugins[plugin_idx].path) + { + break; + } + } + + /*---------------------------------------------------------------------*\ + | If the plugin path does not exist in the active plugins list, return | + \*---------------------------------------------------------------------*/ + if(plugin_idx == ActivePlugins.size()) + { + LOG_TRACE("[PluginManager] Plugin %s not active", path.c_str()); + return; + } + + /*---------------------------------------------------------------------*\ + | If the selected plugin is in the list and loaded, unload it | + \*---------------------------------------------------------------------*/ + if(ActivePlugins[plugin_idx].loader->isLoaded()) + { + LOG_TRACE("[PluginManager] Plugin %s is active, unloading", path.c_str()); + UnloadPlugin(&ActivePlugins[plugin_idx]); + } + + /*---------------------------------------------------------------------*\ + | Remove the plugin from the active plugins list | + \*---------------------------------------------------------------------*/ + ActivePlugins.erase(ActivePlugins.begin() + plugin_idx); +} + +void PluginManager::EnablePlugin(const filesystem::path& path) +{ + unsigned int plugin_idx; + + /*---------------------------------------------------------------------*\ + | Search active plugins to see if this path already exists | + \*---------------------------------------------------------------------*/ + for(plugin_idx = 0; plugin_idx < ActivePlugins.size(); plugin_idx++) + { + if(path == ActivePlugins[plugin_idx].path) + { + break; + } + } + + /*---------------------------------------------------------------------*\ + | If the plugin path does not exist in the active plugins list, return | + \*---------------------------------------------------------------------*/ + if(plugin_idx == ActivePlugins.size()) + { + return; + } + + ActivePlugins[plugin_idx].enabled = true; + LoadPlugin(&ActivePlugins[plugin_idx]); +} + +void PluginManager::LoadPlugin(OpenRGBPluginEntry* plugin_entry) +{ + /*---------------------------------------------------------------------*\ + | If the plugin is in the list but is incompatible, return | + \*---------------------------------------------------------------------*/ + if(plugin_entry->incompatible) + { + return; + } + + /*---------------------------------------------------------------------*\ + | If the selected plugin is in the list but not loaded, load it | + \*---------------------------------------------------------------------*/ + if(!plugin_entry->loader->isLoaded()) + { + plugin_entry->loader->load(); + + QObject* instance = plugin_entry->loader->instance(); + + if(instance) + { + OpenRGBPluginInterface* plugin = qobject_cast(instance); + + if(plugin) + { + if(plugin->GetPluginAPIVersion() == OPENRGB_PLUGIN_API_VERSION) + { + plugin_entry->plugin = plugin; + + plugin->Load(ResourceManager::get()); + + /*-------------------------------------------------*\ + | Call the Add Plugin callback | + \*-------------------------------------------------*/ + if(AddPluginCallbackArg != nullptr) + { + AddPluginCallbackVal(AddPluginCallbackArg, plugin_entry); + } + } + } + } + } +} + +void PluginManager::DisablePlugin(const filesystem::path& path) +{ + unsigned int plugin_idx; + + /*---------------------------------------------------------------------*\ + | Search active plugins to see if this path already exists | + \*---------------------------------------------------------------------*/ + for(plugin_idx = 0; plugin_idx < ActivePlugins.size(); plugin_idx++) + { + if(path == ActivePlugins[plugin_idx].path) + { + break; + } + } + + /*---------------------------------------------------------------------*\ + | If the plugin path does not exist in the active plugins list, return | + \*---------------------------------------------------------------------*/ + if(plugin_idx == ActivePlugins.size()) + { + return; + } + + ActivePlugins[plugin_idx].enabled = false; + UnloadPlugin(&ActivePlugins[plugin_idx]); +} + +void PluginManager::UnloadPlugin(OpenRGBPluginEntry* plugin_entry) +{ + /*---------------------------------------------------------------------*\ + | If the selected plugin is in the list and loaded, unload it | + \*---------------------------------------------------------------------*/ + if(plugin_entry->loader->isLoaded()) + { + /*-------------------------------------------------*\ + | Call plugin's Unload function before GUI removal | + \*-------------------------------------------------*/ + plugin_entry->plugin->Unload(); + + /*-------------------------------------------------*\ + | Call the Remove Plugin callback | + \*-------------------------------------------------*/ + if(RemovePluginCallbackVal != nullptr) + { + RemovePluginCallbackVal(RemovePluginCallbackArg, plugin_entry); + } + + bool unloaded = plugin_entry->loader->unload(); + + if(!unloaded) + { + LOG_WARNING("[PluginManager] Plugin %s cannot be unloaded", plugin_entry->path.c_str()); + } + else + { + LOG_TRACE("[PluginManager] Plugin %s successfully unloaded", plugin_entry->path.c_str()); + } + } + else + { + LOG_TRACE("[PluginManager] Plugin %s was already unloaded", plugin_entry->path.c_str()); + } +} + +void PluginManager::LoadPlugins() +{ + for(OpenRGBPluginEntry& plugin_entry: ActivePlugins) + { + if(plugin_entry.enabled) + { + LoadPlugin(&plugin_entry); + } + } +} + +void PluginManager::UnloadPlugins() +{ + for(OpenRGBPluginEntry& plugin_entry: ActivePlugins) + { + UnloadPlugin(&plugin_entry); + } +} diff --git a/PluginManager.h b/PluginManager.h new file mode 100644 index 0000000..cf5b90d --- /dev/null +++ b/PluginManager.h @@ -0,0 +1,71 @@ +/*---------------------------------------------------------*\ +| PluginManager.h | +| | +| OpenRGB plugin manager | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "OpenRGBPluginInterface.h" + +struct OpenRGBPluginEntry +{ + OpenRGBPluginInfo info; + OpenRGBPluginInterface* plugin; + QPluginLoader* loader; + QWidget* widget; + QMenu* traymenu; + std::string path; + bool enabled; + bool incompatible; + bool is_system; + int api_version; +}; + +typedef void (*AddPluginCallback)(void *, OpenRGBPluginEntry* plugin); +typedef void (*RemovePluginCallback)(void *, OpenRGBPluginEntry* plugin); + +class PluginManager +{ +public: + PluginManager(); + + void RegisterAddPluginCallback(AddPluginCallback new_callback, void * new_callback_arg); + void RegisterRemovePluginCallback(RemovePluginCallback new_callback, void * new_callback_arg); + + void ScanAndLoadPlugins(); + + void AddPlugin(const filesystem::path& path, bool is_system); + void RemovePlugin(const filesystem::path& path); + + void EnablePlugin(const filesystem::path& path); + void DisablePlugin(const filesystem::path& path); + + void LoadPlugins(); + void UnloadPlugins(); + + std::vector ActivePlugins; + +private: + void LoadPlugin(OpenRGBPluginEntry* plugin_entry); + void UnloadPlugin(OpenRGBPluginEntry* plugin_entry); + + void ScanAndLoadPluginsFrom(const filesystem::path & plugins_dir, bool is_system); + + AddPluginCallback AddPluginCallbackVal; + void * AddPluginCallbackArg; + + RemovePluginCallback RemovePluginCallbackVal; + void * RemovePluginCallbackArg; + + const char * plugins_path = "plugins/"; +}; diff --git a/ProfileManager.cpp b/ProfileManager.cpp new file mode 100644 index 0000000..6b0f8dd --- /dev/null +++ b/ProfileManager.cpp @@ -0,0 +1,557 @@ +/*---------------------------------------------------------*\ +| ProfileManager.cpp | +| | +| OpenRGB profile manager | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include "ProfileManager.h" +#include "ResourceManager.h" +#include "RGBController_Dummy.h" +#include "LogManager.h" +#include "NetworkProtocol.h" +#include "filesystem.h" +#include "StringUtils.h" + +#define OPENRGB_PROFILE_HEADER "OPENRGB_PROFILE" +#define OPENRGB_PROFILE_VERSION OPENRGB_SDK_PROTOCOL_VERSION + +ProfileManager::ProfileManager(const filesystem::path& config_dir) +{ + configuration_directory = config_dir; + UpdateProfileList(); +} + +ProfileManager::~ProfileManager() +{ + +} + +bool ProfileManager::SaveProfile(std::string profile_name, bool sizes) +{ + profile_name = StringUtils::remove_null_terminating_chars(profile_name); + + /*---------------------------------------------------------*\ + | Get the list of controllers from the resource manager | + \*---------------------------------------------------------*/ + std::vector controllers = ResourceManager::get()->GetRGBControllers(); + + /*---------------------------------------------------------*\ + | If a name was entered, save the profile file | + \*---------------------------------------------------------*/ + if(profile_name != "") + { + /*---------------------------------------------------------*\ + | Extension .orp - OpenRgb Profile | + \*---------------------------------------------------------*/ + std::string filename = profile_name; + + /*---------------------------------------------------------*\ + | Determine file extension | + \*---------------------------------------------------------*/ + if(sizes) + { + filename += ".ors"; + } + else + { + filename += ".orp"; + } + + /*---------------------------------------------------------*\ + | Open an output file in binary mode | + \*---------------------------------------------------------*/ + filesystem::path profile_path = configuration_directory / filesystem::u8path(filename); + std::ofstream controller_file(profile_path, std::ios::out | std::ios::binary | std::ios::trunc); + + /*---------------------------------------------------------*\ + | Write header | + | 16 bytes - "OPENRGB_PROFILE" | + | 4 bytes - Version, unsigned int | + \*---------------------------------------------------------*/ + unsigned int profile_version = OPENRGB_PROFILE_VERSION; + controller_file.write(OPENRGB_PROFILE_HEADER, 16); + controller_file.write((char *)&profile_version, sizeof(unsigned int)); + + /*---------------------------------------------------------*\ + | Write controller data for each controller | + \*---------------------------------------------------------*/ + for(std::size_t controller_index = 0; controller_index < controllers.size(); controller_index++) + { + /*-----------------------------------------------------*\ + | Ignore remote and virtual controllers when saving | + | sizes | + \*-----------------------------------------------------*/ + if(sizes && (controllers[controller_index]->flags & CONTROLLER_FLAG_REMOTE + || controllers[controller_index]->flags & CONTROLLER_FLAG_VIRTUAL)) + { + break; + } + + unsigned char *controller_data = controllers[controller_index]->GetDeviceDescription(profile_version); + unsigned int controller_size; + + memcpy(&controller_size, controller_data, sizeof(controller_size)); + + controller_file.write((const char *)controller_data, controller_size); + + delete[] controller_data; + } + + /*---------------------------------------------------------*\ + | Close the file when done | + \*---------------------------------------------------------*/ + controller_file.close(); + + /*---------------------------------------------------------*\ + | Update the profile list | + \*---------------------------------------------------------*/ + UpdateProfileList(); + + return(true); + } + else + { + return(false); + } +} + +void ProfileManager::SetConfigurationDirectory(const filesystem::path& directory) +{ + configuration_directory = directory; + UpdateProfileList(); +} + +bool ProfileManager::LoadProfile(std::string profile_name) +{ + profile_name = StringUtils::remove_null_terminating_chars(profile_name); + return(LoadProfileWithOptions(profile_name, false, true)); +} + +bool ProfileManager::LoadSizeFromProfile(std::string profile_name) +{ + profile_name = StringUtils::remove_null_terminating_chars(profile_name); + return(LoadProfileWithOptions(profile_name, true, false)); +} + +std::vector ProfileManager::LoadProfileToList + ( + std::string profile_name, + bool sizes + ) +{ + std::vector temp_controllers; + unsigned int controller_size; + unsigned int controller_offset = 0; + + filesystem::path filename = configuration_directory / filesystem::u8path(profile_name); + + /*---------------------------------------------------------*\ + | Determine file extension | + \*---------------------------------------------------------*/ + if(sizes) + { + filename.concat(".ors"); + } + else + { + if(filename.extension() != ".orp") + { + filename.concat(".orp"); + } + } + + /*---------------------------------------------------------*\ + | Open input file in binary mode | + \*---------------------------------------------------------*/ + std::ifstream controller_file(filename, std::ios::in | std::ios::binary); + + /*---------------------------------------------------------*\ + | Read and verify file header | + \*---------------------------------------------------------*/ + char profile_string[16] = ""; + unsigned int profile_version = 0; + + controller_file.read(profile_string, 16); + controller_file.read((char *)&profile_version, sizeof(unsigned int)); + + /*---------------------------------------------------------*\ + | Profile version started at 1 and protocol version started | + | at 0. Version 1 profiles should use protocol 0, but 2 or | + | greater should be synchronized | + \*---------------------------------------------------------*/ + if(profile_version == 1) + { + profile_version = 0; + } + + controller_offset += 16 + sizeof(unsigned int); + controller_file.seekg(controller_offset); + + if(strcmp(profile_string, OPENRGB_PROFILE_HEADER) == 0) + { + if(profile_version <= OPENRGB_PROFILE_VERSION) + { + /*---------------------------------------------------------*\ + | Read controller data from file until EOF | + \*---------------------------------------------------------*/ + while(!(controller_file.peek() == EOF)) + { + controller_file.read((char *)&controller_size, sizeof(controller_size)); + + unsigned char *controller_data = new unsigned char[controller_size]; + + controller_file.seekg(controller_offset); + + controller_file.read((char *)controller_data, controller_size); + + RGBController_Dummy *temp_controller = new RGBController_Dummy(); + + temp_controller->ReadDeviceDescription(controller_data, profile_version); + + temp_controllers.push_back(temp_controller); + + delete[] controller_data; + + controller_offset += controller_size; + controller_file.seekg(controller_offset); + } + } + } + + return(temp_controllers); +} + +bool ProfileManager::LoadDeviceFromListWithOptions + ( + std::vector& temp_controllers, + std::vector& temp_controller_used, + RGBController* load_controller, + bool load_size, + bool load_settings + ) +{ + for(std::size_t temp_index = 0; temp_index < temp_controllers.size(); temp_index++) + { + RGBController *temp_controller = temp_controllers[temp_index]; + + /*---------------------------------------------------------*\ + | Do not compare location string for HID devices, as the | + | location string may change between runs as devices are | + | connected and disconnected. Also do not compare the I2C | + | bus number, since it is not persistent across reboots | + | on Linux - strip the I2C number and compare only address. | + \*---------------------------------------------------------*/ + bool location_check; + + if(load_controller->GetLocation().find("HID: ") == 0) + { + location_check = true; + } + else if(load_controller->GetLocation().find("I2C: ") == 0) + { + std::size_t loc = load_controller->GetLocation().rfind(", "); + if(loc == std::string::npos) + { + location_check = false; + } + else + { + std::string i2c_address = load_controller->GetLocation().substr(loc + 2); + location_check = temp_controller->GetLocation().find(i2c_address) != std::string::npos; + } + } + else + { + location_check = temp_controller->GetLocation() == load_controller->GetLocation(); + } + + /*---------------------------------------------------------*\ + | Test if saved controller data matches this controller | + \*---------------------------------------------------------*/ + if((temp_controller_used[temp_index] == false ) + &&(temp_controller->type == load_controller->type ) + &&(temp_controller->GetName() == load_controller->GetName() ) + &&(temp_controller->GetDescription() == load_controller->GetDescription()) + &&(temp_controller->GetVersion() == load_controller->GetVersion() ) + &&(temp_controller->GetSerial() == load_controller->GetSerial() ) + &&(location_check == true )) + { + /*---------------------------------------------------------*\ + | Set used flag for this temp device | + \*---------------------------------------------------------*/ + temp_controller_used[temp_index] = true; + + /*---------------------------------------------------------*\ + | Update zone sizes if requested | + \*---------------------------------------------------------*/ + if(load_size) + { + if(temp_controller->zones.size() == load_controller->zones.size()) + { + for(std::size_t zone_idx = 0; zone_idx < temp_controller->zones.size(); zone_idx++) + { + if((temp_controller->zones[zone_idx].name == load_controller->zones[zone_idx].name ) + &&(temp_controller->zones[zone_idx].type == load_controller->zones[zone_idx].type ) + &&(temp_controller->zones[zone_idx].leds_min == load_controller->zones[zone_idx].leds_min ) + &&(temp_controller->zones[zone_idx].leds_max == load_controller->zones[zone_idx].leds_max )) + { + if(temp_controller->zones[zone_idx].leds_count != load_controller->zones[zone_idx].leds_count) + { + load_controller->ResizeZone((int)zone_idx, temp_controller->zones[zone_idx].leds_count); + } + + if(temp_controller->zones[zone_idx].segments.size() != load_controller->zones[zone_idx].segments.size()) + { + load_controller->zones[zone_idx].segments.clear(); + + for(std::size_t segment_idx = 0; segment_idx < temp_controller->zones[zone_idx].segments.size(); segment_idx++) + { + load_controller->zones[zone_idx].segments.push_back(temp_controller->zones[zone_idx].segments[segment_idx]); + } + } + } + } + } + } + + /*---------------------------------------------------------*\ + | Update settings if requested | + \*---------------------------------------------------------*/ + if(load_settings) + { + /*---------------------------------------------------------*\ + | Update all modes | + \*---------------------------------------------------------*/ + if(temp_controller->modes.size() == load_controller->modes.size()) + { + for(std::size_t mode_index = 0; mode_index < temp_controller->modes.size(); mode_index++) + { + if((temp_controller->modes[mode_index].name == load_controller->modes[mode_index].name ) + &&(temp_controller->modes[mode_index].value == load_controller->modes[mode_index].value ) + &&(temp_controller->modes[mode_index].flags == load_controller->modes[mode_index].flags ) + &&(temp_controller->modes[mode_index].speed_min == load_controller->modes[mode_index].speed_min ) + &&(temp_controller->modes[mode_index].speed_max == load_controller->modes[mode_index].speed_max ) + //&&(temp_controller->modes[mode_index].brightness_min == load_controller->modes[mode_index].brightness_min) + //&&(temp_controller->modes[mode_index].brightness_max == load_controller->modes[mode_index].brightness_max) + &&(temp_controller->modes[mode_index].colors_min == load_controller->modes[mode_index].colors_min ) + &&(temp_controller->modes[mode_index].colors_max == load_controller->modes[mode_index].colors_max )) + { + load_controller->modes[mode_index].speed = temp_controller->modes[mode_index].speed; + load_controller->modes[mode_index].brightness = temp_controller->modes[mode_index].brightness; + load_controller->modes[mode_index].direction = temp_controller->modes[mode_index].direction; + load_controller->modes[mode_index].color_mode = temp_controller->modes[mode_index].color_mode; + + load_controller->modes[mode_index].colors.resize(temp_controller->modes[mode_index].colors.size()); + + for(std::size_t mode_color_index = 0; mode_color_index < temp_controller->modes[mode_index].colors.size(); mode_color_index++) + { + load_controller->modes[mode_index].colors[mode_color_index] = temp_controller->modes[mode_index].colors[mode_color_index]; + } + } + + } + + load_controller->active_mode = temp_controller->active_mode; + } + + /*---------------------------------------------------------*\ + | Update all colors | + \*---------------------------------------------------------*/ + if(temp_controller->colors.size() == load_controller->colors.size()) + { + for(std::size_t color_index = 0; color_index < temp_controller->colors.size(); color_index++) + { + load_controller->colors[color_index] = temp_controller->colors[color_index]; + } + } + } + + return(true); + } + } + + return(false); +} + +bool ProfileManager::LoadProfileWithOptions + ( + std::string profile_name, + bool load_size, + bool load_settings + ) +{ + std::vector temp_controllers; + std::vector temp_controller_used; + bool ret_val = false; + + /*---------------------------------------------------------*\ + | Get the list of controllers from the resource manager | + \*---------------------------------------------------------*/ + std::vector controllers = ResourceManager::get()->GetRGBControllers(); + + /*---------------------------------------------------------*\ + | Open input file in binary mode | + \*---------------------------------------------------------*/ + temp_controllers = LoadProfileToList(profile_name); + + /*---------------------------------------------------------*\ + | Set up used flag vector | + \*---------------------------------------------------------*/ + temp_controller_used.resize(temp_controllers.size()); + + for(unsigned int controller_idx = 0; controller_idx < temp_controller_used.size(); controller_idx++) + { + temp_controller_used[controller_idx] = false; + } + + /*---------------------------------------------------------*\ + | Loop through all controllers. For each controller, search| + | all saved controllers until a match is found | + \*---------------------------------------------------------*/ + for(std::size_t controller_index = 0; controller_index < controllers.size(); controller_index++) + { + bool temp_ret_val = LoadDeviceFromListWithOptions(temp_controllers, temp_controller_used, controllers[controller_index], load_size, load_settings); + std::string current_name = controllers[controller_index]->GetName() + " @ " + controllers[controller_index]->GetLocation(); + LOG_INFO("[ProfileManager] Profile loading: %s for %s", ( temp_ret_val ? "Succeeded" : "FAILED!" ), current_name.c_str()); + ret_val |= temp_ret_val; + } + + /*---------------------------------------------------------*\ + | Delete all temporary controllers | + \*---------------------------------------------------------*/ + for(unsigned int controller_idx = 0; controller_idx < temp_controllers.size(); controller_idx++) + { + delete temp_controllers[controller_idx]; + } + + return(ret_val); +} + +void ProfileManager::DeleteProfile(std::string profile_name) +{ + profile_name = StringUtils::remove_null_terminating_chars(profile_name); + + filesystem::path filename = configuration_directory / profile_name; + filename.concat(".orp"); + + filesystem::remove(filename); + + UpdateProfileList(); +} + +void ProfileManager::UpdateProfileList() +{ + profile_list.clear(); + + /*---------------------------------------------------------*\ + | Load profiles by looking for .orp files in current dir | + \*---------------------------------------------------------*/ + for(const auto & entry : filesystem::directory_iterator(configuration_directory)) + { + std::string filename = entry.path().filename().string(); + + if(filename.find(".orp") != std::string::npos) + { + LOG_INFO("[ProfileManager] Found file: %s attempting to validate header", filename.c_str()); + + /*---------------------------------------------------------*\ + | Open input file in binary mode | + \*---------------------------------------------------------*/ + filesystem::path file_path = configuration_directory; + file_path.append(filename); + std::ifstream profile_file(file_path, std::ios::in | std::ios::binary); + + /*---------------------------------------------------------*\ + | Read and verify file header | + \*---------------------------------------------------------*/ + char profile_string[16]; + unsigned int profile_version; + + profile_file.read(profile_string, 16); + profile_file.read((char *)&profile_version, sizeof(unsigned int)); + + if(strcmp(profile_string, OPENRGB_PROFILE_HEADER) == 0) + { + if(profile_version <= OPENRGB_PROFILE_VERSION) + { + /*---------------------------------------------------------*\ + | Add this profile to the list | + \*---------------------------------------------------------*/ + filename.erase(filename.length() - 4); + profile_list.push_back(filename); + + LOG_INFO("[ProfileManager] Valid v%i profile found for %s", profile_version, filename.c_str()); + } + else + { + LOG_WARNING("[ProfileManager] Profile %s isn't valid for current version (v%i, expected v%i at most)", filename.c_str(), profile_version, OPENRGB_PROFILE_VERSION); + } + } + else + { + LOG_WARNING("[ProfileManager] Profile %s isn't valid: header is missing", filename.c_str()); + } + + profile_file.close(); + } + } +} + +unsigned char * ProfileManager::GetProfileListDescription() +{ + unsigned int data_ptr = 0; + unsigned int data_size = 0; + + /*---------------------------------------------------------*\ + | Calculate data size | + \*---------------------------------------------------------*/ + unsigned short num_profiles = (unsigned short)profile_list.size(); + + data_size += sizeof(data_size); + data_size += sizeof(num_profiles); + + for(unsigned int i = 0; i < num_profiles; i++) + { + data_size += sizeof (unsigned short); + data_size += (unsigned int)strlen(profile_list[i].c_str()) + 1; + } + + /*---------------------------------------------------------*\ + | Create data buffer | + \*---------------------------------------------------------*/ + unsigned char *data_buf = new unsigned char[data_size]; + + /*---------------------------------------------------------*\ + | Copy in data size | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &data_size, sizeof(data_size)); + data_ptr += sizeof(data_size); + + /*---------------------------------------------------------*\ + | Copy in num_profiles | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &num_profiles, sizeof(num_profiles)); + data_ptr += sizeof(num_profiles); + + /*---------------------------------------------------------*\ + | Copy in profile names (size+data) | + \*---------------------------------------------------------*/ + for(unsigned int i = 0; i < num_profiles; i++) + { + unsigned short name_len = (unsigned short)strlen(profile_list[i].c_str()) + 1; + + memcpy(&data_buf[data_ptr], &name_len, sizeof(name_len)); + data_ptr += sizeof(name_len); + + strcpy((char *)&data_buf[data_ptr], profile_list[i].c_str()); + data_ptr += name_len; + } + + return(data_buf); +} diff --git a/ProfileManager.h b/ProfileManager.h new file mode 100644 index 0000000..7cb73aa --- /dev/null +++ b/ProfileManager.h @@ -0,0 +1,95 @@ +/*---------------------------------------------------------*\ +| ProfileManager.h | +| | +| OpenRGB profile manager | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "filesystem.h" + +class ProfileManagerInterface +{ +public: + virtual bool SaveProfile + ( + std::string profile_name, + bool sizes = false + ) = 0; + virtual bool LoadProfile(std::string profile_name) = 0; + virtual bool LoadSizeFromProfile(std::string profile_name) = 0; + virtual void DeleteProfile(std::string profile_name) = 0; + virtual unsigned char * GetProfileListDescription() = 0; + + std::vector profile_list; + + virtual bool LoadDeviceFromListWithOptions + ( + std::vector& temp_controllers, + std::vector& temp_controller_used, + RGBController* load_controller, + bool load_size, + bool load_settings + ) = 0; + + virtual std::vector LoadProfileToList + ( + std::string profile_name, + bool sizes = false + ) = 0; + + virtual void SetConfigurationDirectory(const filesystem::path& directory) = 0; +protected: + virtual ~ProfileManagerInterface() {}; +}; + +class ProfileManager: public ProfileManagerInterface +{ +public: + ProfileManager(const filesystem::path& config_dir); + ~ProfileManager(); + + bool SaveProfile + ( + std::string profile_name, + bool sizes = false + ); + bool LoadProfile(std::string profile_name); + bool LoadSizeFromProfile(std::string profile_name); + void DeleteProfile(std::string profile_name); + unsigned char * GetProfileListDescription(); + + std::vector profile_list; + + bool LoadDeviceFromListWithOptions + ( + std::vector& temp_controllers, + std::vector& temp_controller_used, + RGBController* load_controller, + bool load_size, + bool load_settings + ); + + std::vector LoadProfileToList + ( + std::string profile_name, + bool sizes = false + ); + + void SetConfigurationDirectory(const filesystem::path& directory); + +private: + filesystem::path configuration_directory; + + void UpdateProfileList(); + bool LoadProfileWithOptions + ( + std::string profile_name, + bool load_size, + bool load_settings + ); +}; diff --git a/README.md b/README.md new file mode 100644 index 0000000..b43ca3d --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# LumaOps + +LumaOps brengt al je ondersteunde RGB-hardware samen in één lokale webinterface. +Vanuit je browser beheer je apparaten, ruimtes, groepen, scènes, planningen, back-ups +en diagnostiek. Er is geen cloudaccount nodig: de toepassing, database en +hardware-engine draaien samen op je eigen Linux- of Unraid-server. + +> **Release status:** 0.1.0 is een release candidate. Begin met één apparaat en een +> statische kleur op lage helderheid. RGB-protocollen zijn vaak reverse-engineered; +> directe hardwarebesturing houdt altijd een beperkt risico in. + +## Wat je krijgt + +- één responsieve interface voor inventaris, bediening, scènes en automatiseringen; +- lokale opslag in SQLite met controleerbare back-up en restore; +- audit- en diagnostiekfuncties zonder ruwe secrets in exports; +- OpenRGB 1.0rc3 als ingebedde hardware-engine; +- een afgeschermde interne OpenRGB SDK-verbinding op `127.0.0.1:6742`; +- expliciete USB-, HID- en I²C-device mappings in plaats van een privileged container. + +Native WLED- en Home Assistant-connectors zijn nog niet inbegrepen in 0.1.0. + +## Snel starten + +Vereisten: Docker Compose v2, Linux/Unraid x86_64 en toegang tot de benodigde +hardware-device nodes. + +```sh +cp .env.example .env +# Vervang LUMAOPS_ADMIN_TOKEN in .env door een unieke waarde: +openssl rand -base64 32 +docker compose build +docker compose up -d +``` + +Open vervolgens `http://SERVER-IP:1223`, meld je aan met de ingestelde beheertoken +en doorloop de setupwizard. De token is standaard verplicht omdat de webinterface +op het LAN luistert. Gebruik `SECURE_COOKIES=true` zodra je LumaOps achter HTTPS +plaatst; laat dit op `false` voor rechtstreekse HTTP-toegang op een vertrouwd LAN. + +Map alleen de hardware die je werkelijk nodig hebt. Voor netwerkapparaten die +broadcast, multicast of mDNS vereisen, staat een gedocumenteerde host-network +override klaar: + +```sh +docker compose -f docker-compose.yml -f docker/compose.host-network.yml up -d +``` + +Publiceer poort `6742` nooit. Het OpenRGB SDK-protocol heeft zelf geen +authenticatie of transportbeveiliging. + +## Documentatie + +- [Unraid-installatie](docs/UNRAID_DEPLOYMENT.md) +- [Hardwaretoegang](docs/HARDWARE_ACCESS.md) +- [Back-up en herstel](docs/BACKUP_AND_RESTORE.md) +- [Veiligheidsmodel](docs/SECURITY.md) +- [Probleemoplossing](docs/TROUBLESHOOTING.md) +- [Testen en verificatie](docs/TESTING.md) +- [OpenRGB upstream synchroniseren](docs/OPENRGB_UPSTREAM_SYNC.md) +- [Gedocumenteerde OpenRGB-corepatches](docs/CORE_PATCHES.md) +- [Publieke releasegrens](docs/PUBLIC_RELEASE.md) + +De broncode is bewust als OpenRGB-fork opgebouwd. LumaOps staat onder `lumaops/`, +de containerlaag onder `docker/` en projectdocumentatie onder `docs/`. + +## OpenRGB en licentie + +LumaOps bouwt op [OpenRGB](https://openrgb.org), ontwikkeld door de OpenRGB- +community. De geïmporteerde 1.0rc3-bron, oorspronkelijke copyrightvermeldingen en +licentieteksten blijven behouden. LumaOps en de gecombineerde distributie vallen +onder **GPL-2.0-or-later**. Wie images of binaries verspreidt, moet de bijbehorende +broncode en wijzigingen onder die licentie beschikbaar maken. + +Zie [LICENSE](LICENSE) voor de volledige licentietekst en [CONTRIBUTING.md](CONTRIBUTING.md) +voor de upstream bijdragevoorwaarden. diff --git a/RGBController/RGBController.cpp b/RGBController/RGBController.cpp new file mode 100644 index 0000000..9f4518e --- /dev/null +++ b/RGBController/RGBController.cpp @@ -0,0 +1,2217 @@ +/*---------------------------------------------------------*\ +| RGBController.cpp | +| | +| OpenRGB's RGB controller hardware abstration layer, | +| provides a generic representation of an RGB device | +| | +| Adam Honse (CalcProgrammer1) 02 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "RGBController.h" + +using namespace std::chrono_literals; + +mode::mode() +{ + name = ""; + value = 0; + flags = 0; + speed_min = 0; + speed_max = 0; + brightness_min = 0; + brightness_max = 0; + colors_min = 0; + colors_max = 0; + speed = 0; + brightness = 0; + direction = 0; + color_mode = 0; +} + +mode::~mode() +{ + colors.clear(); +} + +zone::zone() +{ + name = ""; + type = 0; + leds = NULL; + colors = NULL; + start_idx = 0; + leds_count = 0; + leds_min = 0; + leds_max = 0; + matrix_map = NULL; + flags = 0; +} + +zone::~zone() +{ + +} + +RGBController::RGBController() +{ + flags = 0; + DeviceThreadRunning = true; + DeviceCallThread = new std::thread(&RGBController::DeviceCallThreadFunction, this); +} + +RGBController::~RGBController() +{ + DeviceThreadRunning = false; + DeviceCallThread->join(); + delete DeviceCallThread; + + leds.clear(); + colors.clear(); + zones.clear(); + modes.clear(); +} + +std::string RGBController::GetName() +{ + return(name); +} + +std::string RGBController::GetVendor() +{ + return(vendor); +} + +std::string RGBController::GetDescription() +{ + return(description); +} + +std::string RGBController::GetVersion() +{ + return(version); +} + +std::string RGBController::GetSerial() +{ + return(serial); +} + +std::string RGBController::GetLocation() +{ + return(location); +} + +std::string RGBController::GetModeName(unsigned int mode) +{ + return(modes[mode].name); +} + +std::string RGBController::GetZoneName(unsigned int zone) +{ + return(zones[zone].name); +} + +std::string RGBController::GetLEDName(unsigned int led) +{ + if(led < led_alt_names.size()) + { + if(led_alt_names[led] != "") + { + return(led_alt_names[led]); + } + } + + return(leds[led].name); +} + +unsigned char * RGBController::GetDeviceDescription(unsigned int protocol_version) +{ + unsigned int data_ptr = 0; + unsigned int data_size = 0; + + /*---------------------------------------------------------*\ + | Calculate data size | + \*---------------------------------------------------------*/ + unsigned short name_len = (unsigned short)strlen(name.c_str()) + 1; + unsigned short vendor_len = (unsigned short)strlen(vendor.c_str()) + 1; + unsigned short description_len = (unsigned short)strlen(description.c_str()) + 1; + unsigned short version_len = (unsigned short)strlen(version.c_str()) + 1; + unsigned short serial_len = (unsigned short)strlen(serial.c_str()) + 1; + unsigned short location_len = (unsigned short)strlen(location.c_str()) + 1; + unsigned short num_modes = (unsigned short)modes.size(); + unsigned short num_zones = (unsigned short)zones.size(); + unsigned short num_leds = (unsigned short)leds.size(); + unsigned short num_colors = (unsigned short)colors.size(); + unsigned short num_led_alt_names= (unsigned short)led_alt_names.size(); + + unsigned short *mode_name_len = new unsigned short[num_modes]; + unsigned short *zone_name_len = new unsigned short[num_zones]; + unsigned short *led_name_len = new unsigned short[num_leds]; + + unsigned short *zone_matrix_len = new unsigned short[num_zones]; + unsigned short *mode_num_colors = new unsigned short[num_modes]; + + data_size += sizeof(data_size); + data_size += sizeof(device_type); + data_size += name_len + sizeof(name_len); + + if(protocol_version >= 1) + { + data_size += vendor_len + sizeof(vendor_len); + } + + data_size += description_len + sizeof(description_len); + data_size += version_len + sizeof(version_len); + data_size += serial_len + sizeof(serial_len); + data_size += location_len + sizeof(location_len); + + data_size += sizeof(num_modes); + data_size += sizeof(active_mode); + + for(int mode_index = 0; mode_index < num_modes; mode_index++) + { + mode_name_len[mode_index] = (unsigned short)strlen(modes[mode_index].name.c_str()) + 1; + mode_num_colors[mode_index] = (unsigned short)modes[mode_index].colors.size(); + + data_size += mode_name_len[mode_index] + sizeof(mode_name_len[mode_index]); + data_size += sizeof(modes[mode_index].value); + data_size += sizeof(modes[mode_index].flags); + data_size += sizeof(modes[mode_index].speed_min); + data_size += sizeof(modes[mode_index].speed_max); + if(protocol_version >= 3) + { + data_size += sizeof(modes[mode_index].brightness_min); + data_size += sizeof(modes[mode_index].brightness_max); + } + data_size += sizeof(modes[mode_index].colors_min); + data_size += sizeof(modes[mode_index].colors_max); + data_size += sizeof(modes[mode_index].speed); + if(protocol_version >= 3) + { + data_size += sizeof(modes[mode_index].brightness); + } + data_size += sizeof(modes[mode_index].direction); + data_size += sizeof(modes[mode_index].color_mode); + data_size += sizeof(mode_num_colors[mode_index]); + data_size += (mode_num_colors[mode_index] * sizeof(RGBColor)); + } + + data_size += sizeof(num_zones); + + for(int zone_index = 0; zone_index < num_zones; zone_index++) + { + zone_name_len[zone_index] = (unsigned short)strlen(zones[zone_index].name.c_str()) + 1; + + data_size += zone_name_len[zone_index] + sizeof(zone_name_len[zone_index]); + data_size += sizeof(zones[zone_index].type); + data_size += sizeof(zones[zone_index].leds_min); + data_size += sizeof(zones[zone_index].leds_max); + data_size += sizeof(zones[zone_index].leds_count); + + if(zones[zone_index].matrix_map == NULL) + { + zone_matrix_len[zone_index] = 0; + } + else + { + zone_matrix_len[zone_index] = (unsigned short)((2 * sizeof(unsigned int)) + (zones[zone_index].matrix_map->height * zones[zone_index].matrix_map->width * sizeof(unsigned int))); + } + + data_size += sizeof(zone_matrix_len[zone_index]); + data_size += zone_matrix_len[zone_index]; + + if(protocol_version >= 4) + { + /*---------------------------------------------------------*\ + | Number of segments in zone | + \*---------------------------------------------------------*/ + data_size += sizeof(unsigned short); + + for(size_t segment_index = 0; segment_index < zones[zone_index].segments.size(); segment_index++) + { + /*---------------------------------------------------------*\ + | Length of segment name string | + \*---------------------------------------------------------*/ + data_size += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Segment name string data | + \*---------------------------------------------------------*/ + data_size += (unsigned int)strlen(zones[zone_index].segments[segment_index].name.c_str()) + 1; + + data_size += sizeof(zones[zone_index].segments[segment_index].type); + data_size += sizeof(zones[zone_index].segments[segment_index].start_idx); + data_size += sizeof(zones[zone_index].segments[segment_index].leds_count); + } + } + + /*---------------------------------------------------------*\ + | Zone flags | + \*---------------------------------------------------------*/ + if(protocol_version >= 5) + { + data_size += sizeof(unsigned int); + } + } + + data_size += sizeof(num_leds); + + for(int led_index = 0; led_index < num_leds; led_index++) + { + led_name_len[led_index] = (unsigned short)strlen(leds[led_index].name.c_str()) + 1; + + data_size += led_name_len[led_index] + sizeof(led_name_len[led_index]); + + data_size += sizeof(leds[led_index].value); + } + + /*---------------------------------------------------------*\ + | LED alternate names | + \*---------------------------------------------------------*/ + if(protocol_version >= 5) + { + /*-----------------------------------------------------*\ + | Number of LED alternate names | + \*-----------------------------------------------------*/ + data_size += sizeof(num_led_alt_names); + + /*-----------------------------------------------------*\ + | LED alternate name strings | + \*-----------------------------------------------------*/ + for(std::size_t led_idx = 0; led_idx < led_alt_names.size(); led_idx++) + { + data_size += sizeof(unsigned short); + data_size += (unsigned int)strlen(led_alt_names[led_idx].c_str()) + 1; + } + } + + /*---------------------------------------------------------*\ + | Controller flags | + \*---------------------------------------------------------*/ + if(protocol_version >= 5) + { + data_size += sizeof(flags); + } + + data_size += sizeof(num_colors); + data_size += num_colors * sizeof(RGBColor); + + /*---------------------------------------------------------*\ + | Create data buffer | + \*---------------------------------------------------------*/ + unsigned char *data_buf = new unsigned char[data_size]; + + /*---------------------------------------------------------*\ + | Copy in data size | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &data_size, sizeof(data_size)); + data_ptr += sizeof(data_size); + + /*---------------------------------------------------------*\ + | Copy in type | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &type, sizeof(device_type)); + data_ptr += sizeof(device_type); + + /*---------------------------------------------------------*\ + | Copy in name (size+data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &name_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], name.c_str()); + data_ptr += name_len; + + /*---------------------------------------------------------*\ + | Copy in vendor (size+data) if protocol 1 or higher | + \*---------------------------------------------------------*/ + if(protocol_version >= 1) + { + memcpy(&data_buf[data_ptr], &vendor_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], vendor.c_str()); + data_ptr += vendor_len; + } + + /*---------------------------------------------------------*\ + | Copy in description (size+data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &description_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], description.c_str()); + data_ptr += description_len; + + /*---------------------------------------------------------*\ + | Copy in version (size+data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &version_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], version.c_str()); + data_ptr += version_len; + + /*---------------------------------------------------------*\ + | Copy in serial (size+data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &serial_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], serial.c_str()); + data_ptr += serial_len; + + /*---------------------------------------------------------*\ + | Copy in location (size+data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &location_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], location.c_str()); + data_ptr += location_len; + + /*---------------------------------------------------------*\ + | Copy in number of modes (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &num_modes, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in active mode (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &active_mode, sizeof(active_mode)); + data_ptr += sizeof(active_mode); + + /*---------------------------------------------------------*\ + | Copy in modes | + \*---------------------------------------------------------*/ + for(int mode_index = 0; mode_index < num_modes; mode_index++) + { + /*---------------------------------------------------------*\ + | Copy in mode name (size+data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &mode_name_len[mode_index], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], modes[mode_index].name.c_str()); + data_ptr += mode_name_len[mode_index]; + + /*---------------------------------------------------------*\ + | Copy in mode value (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode_index].value, sizeof(modes[mode_index].value)); + data_ptr += sizeof(modes[mode_index].value); + + /*---------------------------------------------------------*\ + | Copy in mode flags (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode_index].flags, sizeof(modes[mode_index].flags)); + data_ptr += sizeof(modes[mode_index].flags); + + /*---------------------------------------------------------*\ + | Copy in mode speed_min (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode_index].speed_min, sizeof(modes[mode_index].speed_min)); + data_ptr += sizeof(modes[mode_index].speed_min); + + /*---------------------------------------------------------*\ + | Copy in mode speed_max (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode_index].speed_max, sizeof(modes[mode_index].speed_max)); + data_ptr += sizeof(modes[mode_index].speed_max); + + /*---------------------------------------------------------*\ + | Copy in mode brightness_min and brightness_max (data) if | + | protocol 3 or higher | + \*---------------------------------------------------------*/ + if(protocol_version >= 3) + { + memcpy(&data_buf[data_ptr], &modes[mode_index].brightness_min, sizeof(modes[mode_index].brightness_min)); + data_ptr += sizeof(modes[mode_index].brightness_min); + + memcpy(&data_buf[data_ptr], &modes[mode_index].brightness_max, sizeof(modes[mode_index].brightness_max)); + data_ptr += sizeof(modes[mode_index].brightness_max); + } + + /*---------------------------------------------------------*\ + | Copy in mode colors_min (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode_index].colors_min, sizeof(modes[mode_index].colors_min)); + data_ptr += sizeof(modes[mode_index].colors_min); + + /*---------------------------------------------------------*\ + | Copy in mode colors_max (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode_index].colors_max, sizeof(modes[mode_index].colors_max)); + data_ptr += sizeof(modes[mode_index].colors_max); + + /*---------------------------------------------------------*\ + | Copy in mode speed (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode_index].speed, sizeof(modes[mode_index].speed)); + data_ptr += sizeof(modes[mode_index].speed); + + /*---------------------------------------------------------*\ + | Copy in mode brightness (data) if protocol 3 or higher | + \*---------------------------------------------------------*/ + if(protocol_version >= 3) + { + memcpy(&data_buf[data_ptr], &modes[mode_index].brightness, sizeof(modes[mode_index].brightness)); + data_ptr += sizeof(modes[mode_index].brightness); + } + + /*---------------------------------------------------------*\ + | Copy in mode direction (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode_index].direction, sizeof(modes[mode_index].direction)); + data_ptr += sizeof(modes[mode_index].direction); + + /*---------------------------------------------------------*\ + | Copy in mode color_mode (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode_index].color_mode, sizeof(modes[mode_index].color_mode)); + data_ptr += sizeof(modes[mode_index].color_mode); + + /*---------------------------------------------------------*\ + | Copy in mode number of colors | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &mode_num_colors[mode_index], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in mode mode colors | + \*---------------------------------------------------------*/ + for(int color_index = 0; color_index < mode_num_colors[mode_index]; color_index++) + { + /*---------------------------------------------------------*\ + | Copy in color (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode_index].colors[color_index], sizeof(modes[mode_index].colors[color_index])); + data_ptr += sizeof(modes[mode_index].colors[color_index]); + } + } + + /*---------------------------------------------------------*\ + | Copy in number of zones (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &num_zones, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in zones | + \*---------------------------------------------------------*/ + for(int zone_index = 0; zone_index < num_zones; zone_index++) + { + /*---------------------------------------------------------*\ + | Copy in zone name (size+data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zone_name_len[zone_index], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], zones[zone_index].name.c_str()); + data_ptr += zone_name_len[zone_index]; + + /*---------------------------------------------------------*\ + | Copy in zone type (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone_index].type, sizeof(zones[zone_index].type)); + data_ptr += sizeof(zones[zone_index].type); + + /*---------------------------------------------------------*\ + | Check for resizable effects-only zone. For protocol | + | versions that do not support this feature, we have to | + | overwrite the leds_min/max/count parameters to 1 so that | + | the zone appears a fixed size to older clients. | + \*---------------------------------------------------------*/ + if((zones[zone_index].flags & ZONE_FLAG_RESIZE_EFFECTS_ONLY) && (protocol_version < 5)) + { + /*---------------------------------------------------------*\ + | Create a temporary variable to hold the fixed value of 1 | + \*---------------------------------------------------------*/ + unsigned int tmp_size = 1; + + /*---------------------------------------------------------*\ + | Copy in temporary minimum LED count (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &tmp_size, sizeof(tmp_size)); + data_ptr += sizeof(tmp_size); + + /*---------------------------------------------------------*\ + | Copy in temporary maximum LED count (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &tmp_size, sizeof(tmp_size)); + data_ptr += sizeof(tmp_size); + + /*---------------------------------------------------------*\ + | Copy in temporary LED count (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &tmp_size, sizeof(tmp_size)); + data_ptr += sizeof(tmp_size); + } + else + { + /*---------------------------------------------------------*\ + | Copy in zone minimum LED count (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone_index].leds_min, sizeof(zones[zone_index].leds_min)); + data_ptr += sizeof(zones[zone_index].leds_min); + + /*---------------------------------------------------------*\ + | Copy in zone maximum LED count (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone_index].leds_max, sizeof(zones[zone_index].leds_max)); + data_ptr += sizeof(zones[zone_index].leds_max); + + /*---------------------------------------------------------*\ + | Copy in zone LED count (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone_index].leds_count, sizeof(zones[zone_index].leds_count)); + data_ptr += sizeof(zones[zone_index].leds_count); + } + + /*---------------------------------------------------------*\ + | Copy in size of zone matrix | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zone_matrix_len[zone_index], sizeof(zone_matrix_len[zone_index])); + data_ptr += sizeof(zone_matrix_len[zone_index]); + + /*---------------------------------------------------------*\ + | Copy in matrix data if size is nonzero | + \*---------------------------------------------------------*/ + if(zone_matrix_len[zone_index] > 0) + { + /*---------------------------------------------------------*\ + | Copy in matrix height | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone_index].matrix_map->height, sizeof(zones[zone_index].matrix_map->height)); + data_ptr += sizeof(zones[zone_index].matrix_map->height); + + /*---------------------------------------------------------*\ + | Copy in matrix width | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone_index].matrix_map->width, sizeof(zones[zone_index].matrix_map->width)); + data_ptr += sizeof(zones[zone_index].matrix_map->width); + + /*---------------------------------------------------------*\ + | Copy in matrix map | + \*---------------------------------------------------------*/ + for(unsigned int matrix_idx = 0; matrix_idx < (zones[zone_index].matrix_map->height * zones[zone_index].matrix_map->width); matrix_idx++) + { + memcpy(&data_buf[data_ptr], &zones[zone_index].matrix_map->map[matrix_idx], sizeof(zones[zone_index].matrix_map->map[matrix_idx])); + data_ptr += sizeof(zones[zone_index].matrix_map->map[matrix_idx]); + } + } + + /*---------------------------------------------------------*\ + | Copy in segments | + \*---------------------------------------------------------*/ + if(protocol_version >= 4) + { + unsigned short num_segments = (unsigned short)zones[zone_index].segments.size(); + + /*---------------------------------------------------------*\ + | Number of segments in zone | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &num_segments, sizeof(num_segments)); + data_ptr += sizeof(num_segments); + + for(int segment_index = 0; segment_index < num_segments; segment_index++) + { + /*---------------------------------------------------------*\ + | Length of segment name string | + \*---------------------------------------------------------*/ + unsigned short segment_name_length = (unsigned short)strlen(zones[zone_index].segments[segment_index].name.c_str()) + 1; + + memcpy(&data_buf[data_ptr], &segment_name_length, sizeof(segment_name_length)); + data_ptr += sizeof(segment_name_length); + + /*---------------------------------------------------------*\ + | Segment name string data | + \*---------------------------------------------------------*/ + strcpy((char *)&data_buf[data_ptr], zones[zone_index].segments[segment_index].name.c_str()); + data_ptr += segment_name_length; + + /*---------------------------------------------------------*\ + | Segment type data | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone_index].segments[segment_index].type, sizeof(zones[zone_index].segments[segment_index].type)); + data_ptr += sizeof(zones[zone_index].segments[segment_index].type); + + /*---------------------------------------------------------*\ + | Segment start index data | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone_index].segments[segment_index].start_idx, sizeof(zones[zone_index].segments[segment_index].start_idx)); + data_ptr += sizeof(zones[zone_index].segments[segment_index].start_idx); + + /*---------------------------------------------------------*\ + | Segment LED count data | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone_index].segments[segment_index].leds_count, sizeof(zones[zone_index].segments[segment_index].leds_count)); + data_ptr += sizeof(zones[zone_index].segments[segment_index].leds_count); + } + } + + /*---------------------------------------------------------*\ + | Copy in zone flags | + \*---------------------------------------------------------*/ + if(protocol_version >= 5) + { + /*---------------------------------------------------------*\ + | Zone flags | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone_index].flags, sizeof(zones[zone_index].flags)); + data_ptr += sizeof(zones[zone_index].flags); + } + } + + /*---------------------------------------------------------*\ + | Copy in number of LEDs (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &num_leds, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in LEDs | + \*---------------------------------------------------------*/ + for(int led_index = 0; led_index < num_leds; led_index++) + { + /*---------------------------------------------------------*\ + | Copy in LED name (size+data) | + \*---------------------------------------------------------*/ + unsigned short ledname_len = (unsigned short)strlen(leds[led_index].name.c_str()) + 1; + memcpy(&data_buf[data_ptr], &ledname_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], leds[led_index].name.c_str()); + data_ptr += ledname_len; + + /*---------------------------------------------------------*\ + | Copy in LED value (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &leds[led_index].value, sizeof(leds[led_index].value)); + data_ptr += sizeof(leds[led_index].value); + } + + /*---------------------------------------------------------*\ + | Copy in number of colors (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &num_colors, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in colors | + \*---------------------------------------------------------*/ + for(int color_index = 0; color_index < num_colors; color_index++) + { + /*---------------------------------------------------------*\ + | Copy in color (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &colors[color_index], sizeof(colors[color_index])); + data_ptr += sizeof(colors[color_index]); + } + + /*---------------------------------------------------------*\ + | LED alternate names data | + \*---------------------------------------------------------*/ + if(protocol_version >= 5) + { + /*---------------------------------------------------------*\ + | Number of LED alternate name strings | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &num_led_alt_names, sizeof(num_led_alt_names)); + data_ptr += sizeof(num_led_alt_names); + + for(std::size_t led_idx = 0; led_idx < led_alt_names.size(); led_idx++) + { + /*---------------------------------------------------------*\ + | Copy in LED alternate name (size+data) | + \*---------------------------------------------------------*/ + unsigned short string_length = (unsigned short)strlen(led_alt_names[led_idx].c_str()) + 1; + + memcpy(&data_buf[data_ptr], &string_length, sizeof(string_length)); + data_ptr += sizeof(string_length); + + strcpy((char *)&data_buf[data_ptr], led_alt_names[led_idx].c_str()); + data_ptr += string_length; + } + } + + /*---------------------------------------------------------*\ + | Controller flags data | + \*---------------------------------------------------------*/ + if(protocol_version >= 5) + { + memcpy(&data_buf[data_ptr], &flags, sizeof(flags)); + data_ptr += sizeof(flags); + } + + delete[] mode_name_len; + delete[] zone_name_len; + delete[] led_name_len; + + delete[] zone_matrix_len; + delete[] mode_num_colors; + + return(data_buf); +} + +void RGBController::ReadDeviceDescription(unsigned char* data_buf, unsigned int protocol_version) +{ + unsigned int data_ptr = 0; + + data_ptr += sizeof(unsigned int); + + /*---------------------------------------------------------*\ + | Copy in type | + \*---------------------------------------------------------*/ + memcpy(&type, &data_buf[data_ptr], sizeof(device_type)); + data_ptr += sizeof(device_type); + + /*---------------------------------------------------------*\ + | Copy in name | + \*---------------------------------------------------------*/ + unsigned short name_len; + memcpy(&name_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + name = (char *)&data_buf[data_ptr]; + data_ptr += name_len; + + /*---------------------------------------------------------*\ + | Copy in vendor if protocol version is 1 or higher | + \*---------------------------------------------------------*/ + if(protocol_version >= 1) + { + unsigned short vendor_len; + memcpy(&vendor_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + vendor = (char *)&data_buf[data_ptr]; + data_ptr += vendor_len; + } + + /*---------------------------------------------------------*\ + | Copy in description | + \*---------------------------------------------------------*/ + unsigned short description_len; + memcpy(&description_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + description = (char *)&data_buf[data_ptr]; + data_ptr += description_len; + + /*---------------------------------------------------------*\ + | Copy in version | + \*---------------------------------------------------------*/ + unsigned short version_len; + memcpy(&version_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + version = (char *)&data_buf[data_ptr]; + data_ptr += version_len; + + /*---------------------------------------------------------*\ + | Copy in serial | + \*---------------------------------------------------------*/ + unsigned short serial_len; + memcpy(&serial_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + serial = (char *)&data_buf[data_ptr]; + data_ptr += serial_len; + + /*---------------------------------------------------------*\ + | Copy in location | + \*---------------------------------------------------------*/ + unsigned short location_len; + memcpy(&location_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + location = (char *)&data_buf[data_ptr]; + data_ptr += location_len; + + /*---------------------------------------------------------*\ + | Copy in number of modes (data) | + \*---------------------------------------------------------*/ + unsigned short num_modes; + memcpy(&num_modes, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in active mode (data) | + \*---------------------------------------------------------*/ + memcpy(&active_mode, &data_buf[data_ptr], sizeof(active_mode)); + data_ptr += sizeof(active_mode); + + /*---------------------------------------------------------*\ + | Copy in modes | + \*---------------------------------------------------------*/ + for(int mode_index = 0; mode_index < num_modes; mode_index++) + { + mode new_mode; + + /*---------------------------------------------------------*\ + | Copy in mode name (size+data) | + \*---------------------------------------------------------*/ + unsigned short modename_len; + memcpy(&modename_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + new_mode.name = (char *)&data_buf[data_ptr]; + data_ptr += modename_len; + + /*---------------------------------------------------------*\ + | Copy in mode value (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode.value, &data_buf[data_ptr], sizeof(new_mode.value)); + data_ptr += sizeof(new_mode.value); + + /*---------------------------------------------------------*\ + | Copy in mode flags (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode.flags, &data_buf[data_ptr], sizeof(new_mode.flags)); + data_ptr += sizeof(new_mode.flags); + + /*---------------------------------------------------------*\ + | Copy in mode speed_min (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode.speed_min, &data_buf[data_ptr], sizeof(new_mode.speed_min)); + data_ptr += sizeof(new_mode.speed_min); + + /*---------------------------------------------------------*\ + | Copy in mode speed_max (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode.speed_max, &data_buf[data_ptr], sizeof(new_mode.speed_max)); + data_ptr += sizeof(new_mode.speed_max); + + /*---------------------------------------------------------*\ + | Copy in mode brightness min and max if protocol version | + | is 3 or higher | + \*---------------------------------------------------------*/ + if(protocol_version >= 3) + { + memcpy(&new_mode.brightness_min, &data_buf[data_ptr], sizeof(new_mode.brightness_min)); + data_ptr += sizeof(new_mode.brightness_min); + + memcpy(&new_mode.brightness_max, &data_buf[data_ptr], sizeof(new_mode.brightness_max)); + data_ptr += sizeof(new_mode.brightness_max); + } + + /*---------------------------------------------------------*\ + | Copy in mode colors_min (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode.colors_min, &data_buf[data_ptr], sizeof(new_mode.colors_min)); + data_ptr += sizeof(new_mode.colors_min); + + /*---------------------------------------------------------*\ + | Copy in mode colors_max (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode.colors_max, &data_buf[data_ptr], sizeof(new_mode.colors_max)); + data_ptr += sizeof(new_mode.colors_max); + + /*---------------------------------------------------------*\ + | Copy in mode speed (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode.speed, &data_buf[data_ptr], sizeof(new_mode.speed)); + data_ptr += sizeof(new_mode.speed); + + /*---------------------------------------------------------*\ + | Copy in mode brightness if protocol version is 3 or higher| + \*---------------------------------------------------------*/ + if(protocol_version >= 3) + { + memcpy(&new_mode.brightness, &data_buf[data_ptr], sizeof(new_mode.brightness)); + data_ptr += sizeof(new_mode.brightness); + } + + /*---------------------------------------------------------*\ + | Copy in mode direction (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode.direction, &data_buf[data_ptr], sizeof(new_mode.direction)); + data_ptr += sizeof(new_mode.direction); + + /*---------------------------------------------------------*\ + | Copy in mode color_mode (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode.color_mode, &data_buf[data_ptr], sizeof(new_mode.color_mode)); + data_ptr += sizeof(new_mode.color_mode); + + /*---------------------------------------------------------*\ + | Copy in mode number of colors | + \*---------------------------------------------------------*/ + unsigned short mode_num_colors; + memcpy(&mode_num_colors, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in mode mode colors | + \*---------------------------------------------------------*/ + for(int color_index = 0; color_index < mode_num_colors; color_index++) + { + /*---------------------------------------------------------*\ + | Copy in color (data) | + \*---------------------------------------------------------*/ + RGBColor new_color; + memcpy(&new_color, &data_buf[data_ptr], sizeof(RGBColor)); + data_ptr += sizeof(modes[mode_index].colors[color_index]); + + new_mode.colors.push_back(new_color); + } + + modes.push_back(new_mode); + } + + /*---------------------------------------------------------*\ + | Copy in number of zones (data) | + \*---------------------------------------------------------*/ + unsigned short num_zones; + memcpy(&num_zones, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in zones | + \*---------------------------------------------------------*/ + for(int zone_index = 0; zone_index < num_zones; zone_index++) + { + zone new_zone; + + /*---------------------------------------------------------*\ + | Copy in zone name (size+data) | + \*---------------------------------------------------------*/ + unsigned short zonename_len; + memcpy(&zonename_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + new_zone.name = (char *)&data_buf[data_ptr]; + data_ptr += zonename_len; + + /*---------------------------------------------------------*\ + | Copy in zone type (data) | + \*---------------------------------------------------------*/ + memcpy(&new_zone.type, &data_buf[data_ptr], sizeof(new_zone.type)); + data_ptr += sizeof(new_zone.type); + + /*---------------------------------------------------------*\ + | Copy in zone minimum LED count (data) | + \*---------------------------------------------------------*/ + memcpy(&new_zone.leds_min, &data_buf[data_ptr], sizeof(new_zone.leds_min)); + data_ptr += sizeof(new_zone.leds_min); + + /*---------------------------------------------------------*\ + | Copy in zone maximum LED count (data) | + \*---------------------------------------------------------*/ + memcpy(&new_zone.leds_max, &data_buf[data_ptr], sizeof(new_zone.leds_max)); + data_ptr += sizeof(new_zone.leds_max); + + /*---------------------------------------------------------*\ + | Copy in zone LED count (data) | + \*---------------------------------------------------------*/ + memcpy(&new_zone.leds_count, &data_buf[data_ptr], sizeof(new_zone.leds_count)); + data_ptr += sizeof(new_zone.leds_count); + + /*---------------------------------------------------------*\ + | Copy in size of zone matrix | + \*---------------------------------------------------------*/ + unsigned short zone_matrix_len; + memcpy(&zone_matrix_len, &data_buf[data_ptr], sizeof(zone_matrix_len)); + data_ptr += sizeof(zone_matrix_len); + + /*---------------------------------------------------------*\ + | Copy in matrix data if size is nonzero | + \*---------------------------------------------------------*/ + if(zone_matrix_len > 0) + { + /*---------------------------------------------------------*\ + | Create a map data structure to fill in and attach it to | + | the new zone | + \*---------------------------------------------------------*/ + matrix_map_type * new_map = new matrix_map_type; + + new_zone.matrix_map = new_map; + + /*---------------------------------------------------------*\ + | Copy in matrix height | + \*---------------------------------------------------------*/ + memcpy(&new_map->height, &data_buf[data_ptr], sizeof(new_map->height)); + data_ptr += sizeof(new_map->height); + + /*---------------------------------------------------------*\ + | Copy in matrix width | + \*---------------------------------------------------------*/ + memcpy(&new_map->width, &data_buf[data_ptr], sizeof(new_map->width)); + data_ptr += sizeof(new_map->width); + + /*---------------------------------------------------------*\ + | Copy in matrix map | + \*---------------------------------------------------------*/ + new_map->map = new unsigned int[new_map->height * new_map->width]; + + for(unsigned int matrix_idx = 0; matrix_idx < (new_map->height * new_map->width); matrix_idx++) + { + memcpy(&new_map->map[matrix_idx], &data_buf[data_ptr], sizeof(new_map->map[matrix_idx])); + data_ptr += sizeof(new_map->map[matrix_idx]); + } + } + else + { + new_zone.matrix_map = NULL; + } + + /*---------------------------------------------------------*\ + | Copy in segments | + \*---------------------------------------------------------*/ + if(protocol_version >= 4) + { + unsigned short num_segments = 0; + + /*---------------------------------------------------------*\ + | Number of segments in zone | + \*---------------------------------------------------------*/ + memcpy(&num_segments, &data_buf[data_ptr], sizeof(num_segments)); + data_ptr += sizeof(num_segments); + + for(int segment_index = 0; segment_index < num_segments; segment_index++) + { + segment new_segment; + + /*---------------------------------------------------------*\ + | Copy in segment name (size+data) | + \*---------------------------------------------------------*/ + unsigned short segmentname_len; + memcpy(&segmentname_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + new_segment.name = (char *)&data_buf[data_ptr]; + data_ptr += segmentname_len; + + /*---------------------------------------------------------*\ + | Segment type data | + \*---------------------------------------------------------*/ + memcpy(&new_segment.type, &data_buf[data_ptr], sizeof(new_segment.type)); + data_ptr += sizeof(new_segment.type); + + /*---------------------------------------------------------*\ + | Segment start index data | + \*---------------------------------------------------------*/ + memcpy(&new_segment.start_idx, &data_buf[data_ptr], sizeof(new_segment.start_idx)); + data_ptr += sizeof(new_segment.start_idx); + + /*---------------------------------------------------------*\ + | Segment LED count data | + \*---------------------------------------------------------*/ + memcpy(&new_segment.leds_count, &data_buf[data_ptr], sizeof(new_segment.leds_count)); + data_ptr += sizeof(new_segment.leds_count); + + new_zone.segments.push_back(new_segment); + } + } + + /*---------------------------------------------------------*\ + | Copy in zone flags | + \*---------------------------------------------------------*/ + if(protocol_version >= 5) + { + memcpy(&new_zone.flags, &data_buf[data_ptr], sizeof(new_zone.flags)); + data_ptr += sizeof(new_zone.flags); + } + + zones.push_back(new_zone); + } + + /*---------------------------------------------------------*\ + | Copy in number of LEDs (data) | + \*---------------------------------------------------------*/ + unsigned short num_leds; + memcpy(&num_leds, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in LEDs | + \*---------------------------------------------------------*/ + for(int led_index = 0; led_index < num_leds; led_index++) + { + led new_led; + + /*---------------------------------------------------------*\ + | Copy in LED name (size+data) | + \*---------------------------------------------------------*/ + unsigned short ledname_len; + memcpy(&ledname_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + new_led.name = (char *)&data_buf[data_ptr]; + data_ptr += ledname_len; + + /*---------------------------------------------------------*\ + | Copy in LED value (data) | + \*---------------------------------------------------------*/ + memcpy(&new_led.value, &data_buf[data_ptr], sizeof(new_led.value)); + data_ptr += sizeof(new_led.value); + + leds.push_back(new_led); + } + + /*---------------------------------------------------------*\ + | Copy in number of colors (data) | + \*---------------------------------------------------------*/ + unsigned short num_colors; + memcpy(&num_colors, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in colors | + \*---------------------------------------------------------*/ + for(int color_index = 0; color_index < num_colors; color_index++) + { + RGBColor new_color; + + /*---------------------------------------------------------*\ + | Copy in color (data) | + \*---------------------------------------------------------*/ + memcpy(&new_color, &data_buf[data_ptr], sizeof(RGBColor)); + data_ptr += sizeof(RGBColor); + + colors.push_back(new_color); + } + + /*---------------------------------------------------------*\ + | Copy in LED alternate names data | + \*---------------------------------------------------------*/ + if(protocol_version >= 5) + { + /*---------------------------------------------------------*\ + | Copy in number of LED alternate names | + \*---------------------------------------------------------*/ + unsigned short num_led_alt_names; + + memcpy(&num_led_alt_names, &data_buf[data_ptr], sizeof(num_led_alt_names)); + data_ptr += sizeof(num_led_alt_names); + + for(int led_idx = 0; led_idx < num_led_alt_names; led_idx++) + { + unsigned short string_length = 0; + + /*---------------------------------------------------------*\ + | Copy in LED alternate name string (size+data) | + \*---------------------------------------------------------*/ + memcpy(&string_length, &data_buf[data_ptr], sizeof(string_length)); + data_ptr += sizeof(string_length); + + led_alt_names.push_back((char *)&data_buf[data_ptr]); + data_ptr += string_length; + } + } + + /*---------------------------------------------------------*\ + | Copy in controller flags data | + \*---------------------------------------------------------*/ + if(protocol_version >= 5) + { + memcpy(&flags, &data_buf[data_ptr], sizeof(flags)); + data_ptr += sizeof(flags); + } + + /*---------------------------------------------------------*\ + | Setup colors | + \*---------------------------------------------------------*/ + SetupColors(); +} + +unsigned char * RGBController::GetModeDescription(int mode, unsigned int protocol_version) +{ + unsigned int data_ptr = 0; + unsigned int data_size = 0; + + unsigned short mode_name_len; + unsigned short mode_num_colors; + + /*---------------------------------------------------------*\ + | Calculate data size | + \*---------------------------------------------------------*/ + mode_name_len = (unsigned short)strlen(modes[mode].name.c_str()) + 1; + mode_num_colors = (unsigned short)modes[mode].colors.size(); + + data_size += sizeof(data_size); + data_size += sizeof(mode); + data_size += sizeof(mode_name_len); + data_size += mode_name_len; + data_size += sizeof(modes[mode].value); + data_size += sizeof(modes[mode].flags); + data_size += sizeof(modes[mode].speed_min); + data_size += sizeof(modes[mode].speed_max); + if(protocol_version >= 3) + { + data_size += sizeof(modes[mode].brightness_min); + data_size += sizeof(modes[mode].brightness_max); + } + data_size += sizeof(modes[mode].colors_min); + data_size += sizeof(modes[mode].colors_max); + data_size += sizeof(modes[mode].speed); + if(protocol_version >= 3) + { + data_size += sizeof(modes[mode].brightness); + } + data_size += sizeof(modes[mode].direction); + data_size += sizeof(modes[mode].color_mode); + data_size += sizeof(mode_num_colors); + data_size += (mode_num_colors * sizeof(RGBColor)); + + /*---------------------------------------------------------*\ + | Create data buffer | + \*---------------------------------------------------------*/ + unsigned char *data_buf = new unsigned char[data_size]; + + /*---------------------------------------------------------*\ + | Copy in data size | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &data_size, sizeof(data_size)); + data_ptr += sizeof(data_size); + + /*---------------------------------------------------------*\ + | Copy in mode index | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &mode, sizeof(int)); + data_ptr += sizeof(int); + + /*---------------------------------------------------------*\ + | Copy in mode name (size+data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &mode_name_len, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + strcpy((char *)&data_buf[data_ptr], modes[mode].name.c_str()); + data_ptr += mode_name_len; + + /*---------------------------------------------------------*\ + | Copy in mode value (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode].value, sizeof(modes[mode].value)); + data_ptr += sizeof(modes[mode].value); + + /*---------------------------------------------------------*\ + | Copy in mode flags (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode].flags, sizeof(modes[mode].flags)); + data_ptr += sizeof(modes[mode].flags); + + /*---------------------------------------------------------*\ + | Copy in mode speed_min (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode].speed_min, sizeof(modes[mode].speed_min)); + data_ptr += sizeof(modes[mode].speed_min); + + /*---------------------------------------------------------*\ + | Copy in mode speed_max (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode].speed_max, sizeof(modes[mode].speed_max)); + data_ptr += sizeof(modes[mode].speed_max); + + /*---------------------------------------------------------*\ + | Copy in mode brightness min and max if protocol version | + | is 3 or higher | + \*---------------------------------------------------------*/ + if(protocol_version >= 3) + { + memcpy(&data_buf[data_ptr], &modes[mode].brightness_min, sizeof(modes[mode].brightness_min)); + data_ptr += sizeof(modes[mode].brightness_min); + + memcpy(&data_buf[data_ptr], &modes[mode].brightness_max, sizeof(modes[mode].brightness_max)); + data_ptr += sizeof(modes[mode].brightness_max); + } + + /*---------------------------------------------------------*\ + | Copy in mode colors_min (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode].colors_min, sizeof(modes[mode].colors_min)); + data_ptr += sizeof(modes[mode].colors_min); + + /*---------------------------------------------------------*\ + | Copy in mode colors_max (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode].colors_max, sizeof(modes[mode].colors_max)); + data_ptr += sizeof(modes[mode].colors_max); + + /*---------------------------------------------------------*\ + | Copy in mode speed (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode].speed, sizeof(modes[mode].speed)); + data_ptr += sizeof(modes[mode].speed); + + /*---------------------------------------------------------*\ + | Copy in mode brightness if protocol version is 3 or higher| + \*---------------------------------------------------------*/ + if(protocol_version >= 3) + { + memcpy(&data_buf[data_ptr], &modes[mode].brightness, sizeof(modes[mode].brightness)); + data_ptr += sizeof(modes[mode].brightness); + } + + /*---------------------------------------------------------*\ + | Copy in mode direction (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode].direction, sizeof(modes[mode].direction)); + data_ptr += sizeof(modes[mode].direction); + + /*---------------------------------------------------------*\ + | Copy in mode color_mode (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode].color_mode, sizeof(modes[mode].color_mode)); + data_ptr += sizeof(modes[mode].color_mode); + + /*---------------------------------------------------------*\ + | Copy in mode number of colors | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &mode_num_colors, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in mode mode colors | + \*---------------------------------------------------------*/ + for(int color_index = 0; color_index < mode_num_colors; color_index++) + { + /*---------------------------------------------------------*\ + | Copy in color (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &modes[mode].colors[color_index], sizeof(modes[mode].colors[color_index])); + data_ptr += sizeof(modes[mode].colors[color_index]); + } + + return(data_buf); +} + +void RGBController::SetModeDescription(unsigned char* data_buf, unsigned int protocol_version) +{ + int mode_idx; + unsigned int data_ptr = sizeof(unsigned int); + + /*---------------------------------------------------------*\ + | Copy in mode index | + \*---------------------------------------------------------*/ + memcpy(&mode_idx, &data_buf[data_ptr], sizeof(int)); + data_ptr += sizeof(int); + + /*---------------------------------------------------------*\ + | Check if we aren't reading beyond the list of modes. | + \*---------------------------------------------------------*/ + if(((size_t) mode_idx) > modes.size()) + { + return; + } + + /*---------------------------------------------------------*\ + | Get pointer to target mode | + \*---------------------------------------------------------*/ + mode * new_mode = &modes[mode_idx]; + + /*---------------------------------------------------------*\ + | Set active mode to the new mode | + \*---------------------------------------------------------*/ + active_mode = mode_idx; + + /*---------------------------------------------------------*\ + | Copy in mode name (size+data) | + \*---------------------------------------------------------*/ + unsigned short modename_len; + memcpy(&modename_len, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + new_mode->name = (char *)&data_buf[data_ptr]; + data_ptr += modename_len; + + /*---------------------------------------------------------*\ + | Copy in mode value (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode->value, &data_buf[data_ptr], sizeof(new_mode->value)); + data_ptr += sizeof(new_mode->value); + + /*---------------------------------------------------------*\ + | Copy in mode flags (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode->flags, &data_buf[data_ptr], sizeof(new_mode->flags)); + data_ptr += sizeof(new_mode->flags); + + /*---------------------------------------------------------*\ + | Copy in mode speed_min (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode->speed_min, &data_buf[data_ptr], sizeof(new_mode->speed_min)); + data_ptr += sizeof(new_mode->speed_min); + + /*---------------------------------------------------------*\ + | Copy in mode speed_max (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode->speed_max, &data_buf[data_ptr], sizeof(new_mode->speed_max)); + data_ptr += sizeof(new_mode->speed_max); + + /*---------------------------------------------------------*\ + | Copy in mode brightness_min and brightness_max (data) if | + | protocol 3 or higher | + \*---------------------------------------------------------*/ + if(protocol_version >= 3) + { + memcpy(&new_mode->brightness_min, &data_buf[data_ptr], sizeof(new_mode->brightness_min)); + data_ptr += sizeof(new_mode->brightness_min); + + memcpy(&new_mode->brightness_max, &data_buf[data_ptr], sizeof(new_mode->brightness_max)); + data_ptr += sizeof(new_mode->brightness_max); + } + + /*---------------------------------------------------------*\ + | Copy in mode colors_min (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode->colors_min, &data_buf[data_ptr], sizeof(new_mode->colors_min)); + data_ptr += sizeof(new_mode->colors_min); + + /*---------------------------------------------------------*\ + | Copy in mode colors_max (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode->colors_max, &data_buf[data_ptr], sizeof(new_mode->colors_max)); + data_ptr += sizeof(new_mode->colors_max); + + /*---------------------------------------------------------*\ + | Copy in mode speed (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode->speed, &data_buf[data_ptr], sizeof(new_mode->speed)); + data_ptr += sizeof(new_mode->speed); + + /*---------------------------------------------------------*\ + | Copy in mode brightness (data) if protocol 3 or higher | + \*---------------------------------------------------------*/ + if(protocol_version >= 3) + { + memcpy(&new_mode->brightness, &data_buf[data_ptr], sizeof(new_mode->brightness)); + data_ptr += sizeof(new_mode->brightness); + } + + /*---------------------------------------------------------*\ + | Copy in mode direction (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode->direction, &data_buf[data_ptr], sizeof(new_mode->direction)); + data_ptr += sizeof(new_mode->direction); + + /*---------------------------------------------------------*\ + | Copy in mode color_mode (data) | + \*---------------------------------------------------------*/ + memcpy(&new_mode->color_mode, &data_buf[data_ptr], sizeof(new_mode->color_mode)); + data_ptr += sizeof(new_mode->color_mode); + + /*---------------------------------------------------------*\ + | Copy in mode number of colors | + \*---------------------------------------------------------*/ + unsigned short mode_num_colors; + memcpy(&mode_num_colors, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in mode mode colors | + \*---------------------------------------------------------*/ + new_mode->colors.clear(); + for(int color_index = 0; color_index < mode_num_colors; color_index++) + { + /*---------------------------------------------------------*\ + | Copy in color (data) | + \*---------------------------------------------------------*/ + RGBColor new_color; + memcpy(&new_color, &data_buf[data_ptr], sizeof(RGBColor)); + data_ptr += sizeof(RGBColor); + + new_mode->colors.push_back(new_color); + } +} + +unsigned char * RGBController::GetColorDescription() +{ + unsigned int data_ptr = 0; + unsigned int data_size = 0; + + unsigned short num_colors = (unsigned short)colors.size(); + + /*---------------------------------------------------------*\ + | Calculate data size | + \*---------------------------------------------------------*/ + data_size += sizeof(data_size); + data_size += sizeof(num_colors); + data_size += num_colors * sizeof(RGBColor); + + /*---------------------------------------------------------*\ + | Create data buffer | + \*---------------------------------------------------------*/ + unsigned char *data_buf = new unsigned char[data_size]; + + /*---------------------------------------------------------*\ + | Copy in data size | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &data_size, sizeof(data_size)); + data_ptr += sizeof(data_size); + + /*---------------------------------------------------------*\ + | Copy in number of colors (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &num_colors, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in colors | + \*---------------------------------------------------------*/ + for(int color_index = 0; color_index < num_colors; color_index++) + { + /*---------------------------------------------------------*\ + | Copy in color (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &colors[color_index], sizeof(colors[color_index])); + data_ptr += sizeof(colors[color_index]); + } + + return(data_buf); +} + +void RGBController::SetColorDescription(unsigned char* data_buf) +{ + unsigned int data_ptr = sizeof(unsigned int); + + /*---------------------------------------------------------*\ + | Copy in number of colors (data) | + \*---------------------------------------------------------*/ + unsigned short num_colors; + memcpy(&num_colors, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Check if we aren't reading beyond the list of colors. | + \*---------------------------------------------------------*/ + if(((size_t)num_colors) > colors.size()) + { + return; + } + + /*---------------------------------------------------------*\ + | Copy in colors | + \*---------------------------------------------------------*/ + for(int color_index = 0; color_index < num_colors; color_index++) + { + RGBColor new_color; + + /*---------------------------------------------------------*\ + | Copy in color (data) | + \*---------------------------------------------------------*/ + memcpy(&new_color, &data_buf[data_ptr], sizeof(RGBColor)); + data_ptr += sizeof(RGBColor); + + colors[color_index] = new_color; + } +} + +unsigned char * RGBController::GetZoneColorDescription(int zone) +{ + unsigned int data_ptr = 0; + unsigned int data_size = 0; + + unsigned short num_colors = zones[zone].leds_count; + + /*---------------------------------------------------------*\ + | Calculate data size | + \*---------------------------------------------------------*/ + data_size += sizeof(data_size); + data_size += sizeof(zone); + data_size += sizeof(num_colors); + data_size += num_colors * sizeof(RGBColor); + + /*---------------------------------------------------------*\ + | Create data buffer | + \*---------------------------------------------------------*/ + unsigned char *data_buf = new unsigned char[data_size]; + + /*---------------------------------------------------------*\ + | Copy in data size | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &data_size, sizeof(data_size)); + data_ptr += sizeof(data_size); + + /*---------------------------------------------------------*\ + | Copy in zone index | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zone, sizeof(zone)); + data_ptr += sizeof(zone); + + /*---------------------------------------------------------*\ + | Copy in number of colors (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &num_colors, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in colors | + \*---------------------------------------------------------*/ + for(int color_index = 0; color_index < num_colors; color_index++) + { + /*---------------------------------------------------------*\ + | Copy in color (data) | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zones[zone].colors[color_index], sizeof(zones[zone].colors[color_index])); + data_ptr += sizeof(zones[zone].colors[color_index]); + } + + return(data_buf); +} + +void RGBController::SetZoneColorDescription(unsigned char* data_buf) +{ + unsigned int data_ptr = sizeof(unsigned int); + unsigned int zone_idx; + + /*---------------------------------------------------------*\ + | Copy in zone index | + \*---------------------------------------------------------*/ + memcpy(&zone_idx, &data_buf[data_ptr], sizeof(zone_idx)); + data_ptr += sizeof(zone_idx); + + /*---------------------------------------------------------*\ + | Check if we aren't reading beyond the list of zones. | + \*---------------------------------------------------------*/ + if(((size_t)zone_idx) > zones.size()) + { + return; + } + + /*---------------------------------------------------------*\ + | Copy in number of colors (data) | + \*---------------------------------------------------------*/ + unsigned short num_colors; + memcpy(&num_colors, &data_buf[data_ptr], sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Copy in colors | + \*---------------------------------------------------------*/ + for(int color_index = 0; color_index < num_colors; color_index++) + { + RGBColor new_color; + + /*---------------------------------------------------------*\ + | Copy in color (data) | + \*---------------------------------------------------------*/ + memcpy(&new_color, &data_buf[data_ptr], sizeof(RGBColor)); + data_ptr += sizeof(RGBColor); + + zones[zone_idx].colors[color_index] = new_color; + } +} + +unsigned char * RGBController::GetSingleLEDColorDescription(int led) +{ + /*---------------------------------------------------------*\ + | Fixed size descrption: | + | int: LED index | + | RGBColor: LED color | + \*---------------------------------------------------------*/ + unsigned char *data_buf = new unsigned char[sizeof(int) + sizeof(RGBColor)]; + + /*---------------------------------------------------------*\ + | Copy in LED index | + \*---------------------------------------------------------*/ + memcpy(&data_buf[0], &led, sizeof(int)); + + /*---------------------------------------------------------*\ + | Copy in LED color | + \*---------------------------------------------------------*/ + memcpy(&data_buf[sizeof(led)], &colors[led], sizeof(RGBColor)); + + return(data_buf); +} + +void RGBController::SetSingleLEDColorDescription(unsigned char* data_buf) +{ + /*---------------------------------------------------------*\ + | Fixed size descrption: | + | int: LED index | + | RGBColor: LED color | + \*---------------------------------------------------------*/ + int led_idx; + + /*---------------------------------------------------------*\ + | Copy in LED index | + \*---------------------------------------------------------*/ + memcpy(&led_idx, &data_buf[0], sizeof(led_idx)); + + /*---------------------------------------------------------*\ + | Check if we aren't reading beyond the list of leds. | + \*---------------------------------------------------------*/ + if(((size_t)led_idx) > leds.size()) + { + return; + } + + /*---------------------------------------------------------*\ + | Copy in LED color | + \*---------------------------------------------------------*/ + memcpy(&colors[led_idx], &data_buf[sizeof(led_idx)], sizeof(RGBColor)); +} + +unsigned char * RGBController::GetSegmentDescription(int zone, segment new_segment) +{ + unsigned int data_ptr = 0; + unsigned int data_size = 0; + + /*---------------------------------------------------------*\ + | Length of data size | + \*---------------------------------------------------------*/ + data_size += sizeof(data_size); + + /*---------------------------------------------------------*\ + | Length of zone index | + \*---------------------------------------------------------*/ + data_size += sizeof(zone); + + /*---------------------------------------------------------*\ + | Length of segment name string | + \*---------------------------------------------------------*/ + data_size += sizeof(unsigned short); + + /*---------------------------------------------------------*\ + | Segment name string data | + \*---------------------------------------------------------*/ + data_size += (unsigned int)strlen(new_segment.name.c_str()) + 1; + + data_size += sizeof(new_segment.type); + data_size += sizeof(new_segment.start_idx); + data_size += sizeof(new_segment.leds_count); + + /*---------------------------------------------------------*\ + | Create data buffer | + \*---------------------------------------------------------*/ + unsigned char *data_buf = new unsigned char[data_size]; + + /*---------------------------------------------------------*\ + | Copy in data size | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &data_size, sizeof(data_size)); + data_ptr += sizeof(data_size); + + /*---------------------------------------------------------*\ + | Copy in zone index | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &zone, sizeof(zone)); + data_ptr += sizeof(zone); + + /*---------------------------------------------------------*\ + | Length of segment name string | + \*---------------------------------------------------------*/ + unsigned short segment_name_length = (unsigned short)strlen(new_segment.name.c_str()) + 1; + + memcpy(&data_buf[data_ptr], &segment_name_length, sizeof(segment_name_length)); + data_ptr += sizeof(segment_name_length); + + /*---------------------------------------------------------*\ + | Segment name string data | + \*---------------------------------------------------------*/ + strcpy((char *)&data_buf[data_ptr], new_segment.name.c_str()); + data_ptr += segment_name_length; + + /*---------------------------------------------------------*\ + | Segment type data | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &new_segment.type, sizeof(new_segment.type)); + data_ptr += sizeof(new_segment.type); + + /*---------------------------------------------------------*\ + | Segment start index data | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &new_segment.start_idx, sizeof(new_segment.start_idx)); + data_ptr += sizeof(new_segment.start_idx); + + /*---------------------------------------------------------*\ + | Segment LED count data | + \*---------------------------------------------------------*/ + memcpy(&data_buf[data_ptr], &new_segment.leds_count, sizeof(new_segment.leds_count)); + data_ptr += sizeof(new_segment.leds_count); + + return(data_buf); +} + +void RGBController::SetSegmentDescription(unsigned char* data_buf) +{ + unsigned int data_ptr = sizeof(unsigned int); + + /*---------------------------------------------------------*\ + | Copy in zone index | + \*---------------------------------------------------------*/ + unsigned int zone_idx; + memcpy(&zone_idx, &data_buf[data_ptr], sizeof(zone_idx)); + data_ptr += sizeof(zone_idx); + + /*---------------------------------------------------------*\ + | Length of segment name string | + \*---------------------------------------------------------*/ + unsigned short segment_name_length; + memcpy(&segment_name_length, &data_buf[data_ptr], sizeof(segment_name_length)); + data_ptr += sizeof(segment_name_length); + + /*---------------------------------------------------------*\ + | Segment name string data | + \*---------------------------------------------------------*/ + char * segment_name = new char[segment_name_length]; + memcpy(segment_name, &data_buf[data_ptr], segment_name_length); + data_ptr += segment_name_length; + + /*---------------------------------------------------------*\ + | Segment type data | + \*---------------------------------------------------------*/ + zone_type segment_type; + memcpy(&segment_type, &data_buf[data_ptr], sizeof(segment_type)); + data_ptr += sizeof(segment_type); + + /*---------------------------------------------------------*\ + | Segment start index data | + \*---------------------------------------------------------*/ + unsigned int segment_start_idx; + memcpy(&segment_start_idx, &data_buf[data_ptr], sizeof(segment_start_idx)); + data_ptr += sizeof(segment_start_idx); + + /*---------------------------------------------------------*\ + | Segment LED count data | + \*---------------------------------------------------------*/ + unsigned int segment_leds_count; + memcpy(&segment_leds_count, &data_buf[data_ptr], sizeof(segment_leds_count)); + data_ptr += sizeof(segment_leds_count); + + /*---------------------------------------------------------*\ + | Add new segment | + \*---------------------------------------------------------*/ + segment new_segment; + + new_segment.name = segment_name; + new_segment.type = segment_type; + new_segment.start_idx = segment_start_idx; + new_segment.leds_count = segment_leds_count; + + AddSegment(zone_idx, new_segment); + + delete[] segment_name; +} + +void RGBController::SetupColors() +{ + unsigned int total_led_count; + unsigned int zone_led_count; + + /*---------------------------------------------------------*\ + | Determine total number of LEDs on the device | + \*---------------------------------------------------------*/ + total_led_count = 0; + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + total_led_count += GetLEDsInZone((unsigned int)zone_idx); + } + + /*---------------------------------------------------------*\ + | Set the size of the color buffer to the number of LEDs | + \*---------------------------------------------------------*/ + colors.resize(total_led_count); + + /*---------------------------------------------------------*\ + | Set the color buffer pointers on each zone | + \*---------------------------------------------------------*/ + total_led_count = 0; + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + zones[zone_idx].start_idx = total_led_count; + zone_led_count = GetLEDsInZone((unsigned int)zone_idx); + + if((colors.size() > 0) && (zone_led_count > 0)) + { + zones[zone_idx].colors = &colors[total_led_count]; + } + else + { + zones[zone_idx].colors = NULL; + } + + if((leds.size() > 0) && (zone_led_count > 0)) + { + zones[zone_idx].leds = &leds[total_led_count]; + } + else + { + zones[zone_idx].leds = NULL; + } + + + total_led_count += zone_led_count; + } +} + +unsigned int RGBController::GetLEDsInZone(unsigned int zone) +{ + unsigned int leds_count = zones[zone].leds_count; + + if(zones[zone].flags & ZONE_FLAG_RESIZE_EFFECTS_ONLY) + { + if(leds_count > 1) + { + leds_count = 1; + } + } + + return(leds_count); +} + +RGBColor RGBController::GetLED(unsigned int led) +{ + if(led < colors.size()) + { + return(colors[led]); + } + else + { + return(0x00000000); + } +} + +void RGBController::SetLED(unsigned int led, RGBColor color) +{ + if(led < colors.size()) + { + colors[led] = color; + } +} + +void RGBController::SetAllLEDs(RGBColor color) +{ + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + SetAllZoneLEDs((int)zone_idx, color); + } +} + +void RGBController::SetAllZoneLEDs(int zone, RGBColor color) +{ + for (std::size_t color_idx = 0; color_idx < GetLEDsInZone(zone); color_idx++) + { + zones[zone].colors[color_idx] = color; + } +} + +int RGBController::GetMode() +{ + return(active_mode); +} + +void RGBController::SetMode(int mode) +{ + active_mode = mode; + + UpdateMode(); +} + +void RGBController::RegisterUpdateCallback(RGBControllerCallback new_callback, void * new_callback_arg) +{ + UpdateCallbacks.push_back(new_callback); + UpdateCallbackArgs.push_back(new_callback_arg); +} + +void RGBController::UnregisterUpdateCallback(void * callback_arg) +{ + for(unsigned int callback_idx = 0; callback_idx < UpdateCallbackArgs.size(); callback_idx++ ) + { + if(UpdateCallbackArgs[callback_idx] == callback_arg) + { + UpdateCallbackArgs.erase(UpdateCallbackArgs.begin() + callback_idx); + UpdateCallbacks.erase(UpdateCallbacks.begin() + callback_idx); + + break; + } + } +} + +void RGBController::ClearCallbacks() +{ + UpdateCallbacks.clear(); + UpdateCallbackArgs.clear(); +} + +void RGBController::SignalUpdate() +{ + UpdateMutex.lock(); + + /*-------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*-------------------------------------------------*/ + for(unsigned int callback_idx = 0; callback_idx < UpdateCallbacks.size(); callback_idx++) + { + UpdateCallbacks[callback_idx](UpdateCallbackArgs[callback_idx]); + } + + UpdateMutex.unlock(); +} +void RGBController::UpdateLEDs() +{ + CallFlag_UpdateLEDs = true; + + SignalUpdate(); +} + +void RGBController::UpdateMode() +{ + CallFlag_UpdateMode = true; +} + +void RGBController::SaveMode() +{ + DeviceSaveMode(); +} + +void RGBController::DeviceUpdateLEDs() +{ + +} + +void RGBController::SetCustomMode() +{ + /*-------------------------------------------------*\ + | Search the Controller's mode list for a suitable | + | per-LED custom mode in the following order: | + | 1. Direct | + | 2. Custom | + | 3. Static | + \*-------------------------------------------------*/ + #define NUM_CUSTOM_MODE_NAMES 3 + + const std::string custom_mode_names[] = + { + "Direct", + "Custom", + "Static" + }; + + for(unsigned int custom_mode_idx = 0; custom_mode_idx < NUM_CUSTOM_MODE_NAMES; custom_mode_idx++) + { + for(unsigned int mode_idx = 0; mode_idx < modes.size(); mode_idx++) + { + if((modes[mode_idx].name == custom_mode_names[custom_mode_idx]) + && ((modes[mode_idx].color_mode == MODE_COLORS_PER_LED) + || (modes[mode_idx].color_mode == MODE_COLORS_MODE_SPECIFIC))) + { + active_mode = mode_idx; + return; + } + } + } +} + +void RGBController::DeviceUpdateMode() +{ + +} + +void RGBController::DeviceCallThreadFunction() +{ + CallFlag_UpdateLEDs = false; + CallFlag_UpdateMode = false; + + while(DeviceThreadRunning.load() == true) + { + if(CallFlag_UpdateMode.load() == true) + { + if(flags & CONTROLLER_FLAG_RESET_BEFORE_UPDATE) + { + CallFlag_UpdateMode = false; + DeviceUpdateMode(); + } + else + { + DeviceUpdateMode(); + CallFlag_UpdateMode = false; + } + } + if(CallFlag_UpdateLEDs.load() == true) + { + if(flags & CONTROLLER_FLAG_RESET_BEFORE_UPDATE) + { + CallFlag_UpdateLEDs = false; + DeviceUpdateLEDs(); + } + else + { + DeviceUpdateLEDs(); + CallFlag_UpdateLEDs = false; + } + } + else + { + std::this_thread::sleep_for(1ms); + } + } +} + +void RGBController::DeviceSaveMode() +{ + /*-------------------------------------------------*\ + | If not implemented by controller, does nothing | + \*-------------------------------------------------*/ +} + +void RGBController::ClearSegments(int zone) +{ + zones[zone].segments.clear(); +} + +void RGBController::AddSegment(int zone, segment new_segment) +{ + zones[zone].segments.push_back(new_segment); +} + +std::string device_type_to_str(device_type type) +{ + switch(type) + { + case DEVICE_TYPE_MOTHERBOARD: + return "Motherboard"; + case DEVICE_TYPE_DRAM: + return "DRAM"; + case DEVICE_TYPE_GPU: + return "GPU"; + case DEVICE_TYPE_COOLER: + return "Cooler"; + case DEVICE_TYPE_LEDSTRIP: + return "LED Strip"; + case DEVICE_TYPE_KEYBOARD: + return "Keyboard"; + case DEVICE_TYPE_MOUSE: + return "Mouse"; + case DEVICE_TYPE_MOUSEMAT: + return "Mousemat"; + case DEVICE_TYPE_HEADSET: + return "Headset"; + case DEVICE_TYPE_HEADSET_STAND: + return "Headset Stand"; + case DEVICE_TYPE_GAMEPAD: + return "Gamepad"; + case DEVICE_TYPE_LIGHT: + return "Light"; + case DEVICE_TYPE_SPEAKER: + return "Speaker"; + case DEVICE_TYPE_VIRTUAL: + return "Virtual"; + case DEVICE_TYPE_STORAGE: + return "Storage"; + case DEVICE_TYPE_CASE: + return "Case"; + case DEVICE_TYPE_MICROPHONE: + return "Microphone"; + case DEVICE_TYPE_ACCESSORY: + return "Accessory"; + case DEVICE_TYPE_KEYPAD: + return "Keypad"; + case DEVICE_TYPE_LAPTOP: + return "Laptop"; + case DEVICE_TYPE_MONITOR: + return "Monitor"; + default: + return "Unknown"; + } +} diff --git a/RGBController/RGBController.h b/RGBController/RGBController.h new file mode 100644 index 0000000..6a890ec --- /dev/null +++ b/RGBController/RGBController.h @@ -0,0 +1,429 @@ +/*---------------------------------------------------------*\ +| RGBController.h | +| | +| OpenRGB's RGB controller hardware abstration layer, | +| provides a generic representation of an RGB device | +| | +| Adam Honse (CalcProgrammer1) 02 Jun 2019 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +/*------------------------------------------------------------------*\ +| RGB Color Type and Conversion Macros | +\*------------------------------------------------------------------*/ +typedef unsigned int RGBColor; + +#define RGBGetRValue(rgb) (rgb & 0x000000FF) +#define RGBGetGValue(rgb) ((rgb >> 8) & 0x000000FF) +#define RGBGetBValue(rgb) ((rgb >> 16) & 0x000000FF) + +#define ToRGBColor(r, g, b) ((RGBColor)((b << 16) | (g << 8) | (r))) + +#define RGBToBGRColor(rgb) ((rgb & 0xFF) << 16 | (rgb & 0xFF00) | (rgb & 0xFF0000) >> 16) + +/*------------------------------------------------------------------*\ +| Mode Flags | +\*------------------------------------------------------------------*/ +enum +{ + MODE_FLAG_HAS_SPEED = (1 << 0), /* Mode has speed parameter */ + MODE_FLAG_HAS_DIRECTION_LR = (1 << 1), /* Mode has left/right parameter */ + MODE_FLAG_HAS_DIRECTION_UD = (1 << 2), /* Mode has up/down parameter */ + MODE_FLAG_HAS_DIRECTION_HV = (1 << 3), /* Mode has horiz/vert parameter */ + MODE_FLAG_HAS_BRIGHTNESS = (1 << 4), /* Mode has brightness parameter */ + MODE_FLAG_HAS_PER_LED_COLOR = (1 << 5), /* Mode has per-LED colors */ + MODE_FLAG_HAS_MODE_SPECIFIC_COLOR = (1 << 6), /* Mode has mode specific colors */ + MODE_FLAG_HAS_RANDOM_COLOR = (1 << 7), /* Mode has random color option */ + MODE_FLAG_MANUAL_SAVE = (1 << 8), /* Mode can manually be saved */ + MODE_FLAG_AUTOMATIC_SAVE = (1 << 9), /* Mode automatically saves */ +}; + +/*------------------------------------------------------------------*\ +| Mode Directions | +\*------------------------------------------------------------------*/ +enum +{ + MODE_DIRECTION_LEFT = 0, /* Mode direction left */ + MODE_DIRECTION_RIGHT = 1, /* Mode direction right */ + MODE_DIRECTION_UP = 2, /* Mode direction up */ + MODE_DIRECTION_DOWN = 3, /* Mode direction down */ + MODE_DIRECTION_HORIZONTAL = 4, /* Mode direction horizontal */ + MODE_DIRECTION_VERTICAL = 5, /* Mode direction vertical */ +}; + +/*------------------------------------------------------------------*\ +| Mode Color Types | +\*------------------------------------------------------------------*/ +enum +{ + MODE_COLORS_NONE = 0, /* Mode has no colors */ + MODE_COLORS_PER_LED = 1, /* Mode has per LED colors selected */ + MODE_COLORS_MODE_SPECIFIC = 2, /* Mode specific colors selected */ + MODE_COLORS_RANDOM = 3, /* Mode has random colors selected */ +}; + +/*------------------------------------------------------------------*\ +| Mode Class | +\*------------------------------------------------------------------*/ +class mode +{ +public: + /*--------------------------------------------------------------*\ + | Mode Information | + \*--------------------------------------------------------------*/ + std::string name; /* Mode name */ + int value; /* Device-specific mode value */ + unsigned int flags; /* Mode flags bitfield */ + unsigned int speed_min; /* speed minimum value */ + unsigned int speed_max; /* speed maximum value */ + unsigned int brightness_min; /*brightness min value */ + unsigned int brightness_max; /*brightness max value */ + unsigned int colors_min; /* minimum number of mode colors*/ + unsigned int colors_max; /* maximum numver of mode colors*/ + + /*--------------------------------------------------------------*\ + | Mode Settings | + \*--------------------------------------------------------------*/ + unsigned int speed; /* Mode speed parameter value */ + unsigned int brightness; /* Mode brightness value */ + unsigned int direction; /* Mode direction value */ + unsigned int color_mode; /* Mode color selection */ + std::vector + colors; /* mode-specific colors */ + + /*--------------------------------------------------------------*\ + | Mode Constructor / Destructor | + \*--------------------------------------------------------------*/ + mode(); + ~mode(); +}; + +/*------------------------------------------------------------------*\ +| LED Struct | +\*------------------------------------------------------------------*/ +typedef struct +{ + std::string name; /* LED name */ + unsigned int value; /* Device-specific LED value */ +} led; + +/*------------------------------------------------------------------*\ +| Zone Flags | +\*------------------------------------------------------------------*/ +enum +{ + ZONE_FLAG_RESIZE_EFFECTS_ONLY = (1 << 0), /* Zone is resizable, but only for */ + /* effects - treat as single LED */ +}; + +/*------------------------------------------------------------------*\ +| Zone Types | +\*------------------------------------------------------------------*/ +typedef int zone_type; + +enum +{ + ZONE_TYPE_SINGLE, + ZONE_TYPE_LINEAR, + ZONE_TYPE_MATRIX +}; + +/*------------------------------------------------------------------*\ +| Matrix Map Struct | +\*------------------------------------------------------------------*/ +typedef struct +{ + unsigned int height; + unsigned int width; + unsigned int * map; +} matrix_map_type; + +/*------------------------------------------------------------------*\ +| Segment Struct | +\*------------------------------------------------------------------*/ +typedef struct +{ + std::string name; /* Segment name */ + zone_type type; /* Segment type */ + unsigned int start_idx; /* Start index within zone */ + unsigned int leds_count; /* Number of LEDs in segment*/ +} segment; + +/*------------------------------------------------------------------*\ +| Zone Class | +\*------------------------------------------------------------------*/ +class zone +{ +public: + std::string name; /* Zone name */ + zone_type type; /* Zone type */ + led * leds; /* List of LEDs in zone */ + RGBColor * colors; /* Colors of LEDs in zone */ + unsigned int start_idx; /* Start index of led/color */ + unsigned int leds_count; /* Number of LEDs in zone */ + unsigned int leds_min; /* Minimum number of LEDs */ + unsigned int leds_max; /* Maximum number of LEDs */ + matrix_map_type * matrix_map; /* Matrix map pointer */ + std::vector segments; /* Segments in zone */ + unsigned int flags; /* Zone flags bitfield */ + + /*--------------------------------------------------------------*\ + | Zone Constructor / Destructor | + \*--------------------------------------------------------------*/ + zone(); + ~zone(); +}; + +/*------------------------------------------------------------------*\ +| Device Types | +| The enum order should be maintained as is for the API however | +| DEVICE_TYPE_UNKNOWN needs to remain last. Any new device types | +| need to be inserted at the end of the list but before unknown. | +\*------------------------------------------------------------------*/ +typedef int device_type; + +enum +{ + DEVICE_TYPE_MOTHERBOARD, + DEVICE_TYPE_DRAM, + DEVICE_TYPE_GPU, + DEVICE_TYPE_COOLER, + DEVICE_TYPE_LEDSTRIP, + DEVICE_TYPE_KEYBOARD, + DEVICE_TYPE_MOUSE, + DEVICE_TYPE_MOUSEMAT, + DEVICE_TYPE_HEADSET, + DEVICE_TYPE_HEADSET_STAND, + DEVICE_TYPE_GAMEPAD, + DEVICE_TYPE_LIGHT, + DEVICE_TYPE_SPEAKER, + DEVICE_TYPE_VIRTUAL, + DEVICE_TYPE_STORAGE, + DEVICE_TYPE_CASE, + DEVICE_TYPE_MICROPHONE, + DEVICE_TYPE_ACCESSORY, + DEVICE_TYPE_KEYPAD, + DEVICE_TYPE_LAPTOP, + DEVICE_TYPE_MONITOR, + DEVICE_TYPE_UNKNOWN, +}; + +/*------------------------------------------------------------------*\ +| Controller Flags | +\*------------------------------------------------------------------*/ +enum +{ + CONTROLLER_FLAG_LOCAL = (1 << 0), /* Device is local to this instance */ + CONTROLLER_FLAG_REMOTE = (1 << 1), /* Device is on a remote instance */ + CONTROLLER_FLAG_VIRTUAL = (1 << 2), /* Device is a virtual device */ + + CONTROLLER_FLAG_RESET_BEFORE_UPDATE = (1 << 8), /* Device resets update flag before */ + /* calling update function */ +}; + +/*------------------------------------------------------------------*\ +| RGBController Callback Types | +\*------------------------------------------------------------------*/ +typedef void (*RGBControllerCallback)(void *); + +std::string device_type_to_str(device_type type); + +class RGBControllerInterface +{ +public: + virtual void SetupColors() = 0; + + virtual unsigned int GetLEDsInZone(unsigned int zone) = 0; + virtual std::string GetName() = 0; + virtual std::string GetVendor() = 0; + virtual std::string GetDescription() = 0; + virtual std::string GetVersion() = 0; + virtual std::string GetSerial() = 0; + virtual std::string GetLocation() = 0; + + virtual std::string GetModeName(unsigned int mode) = 0; + virtual std::string GetZoneName(unsigned int zone) = 0; + virtual std::string GetLEDName(unsigned int led) = 0; + + virtual RGBColor GetLED(unsigned int led) = 0; + virtual void SetLED(unsigned int led, RGBColor color) = 0; + virtual void SetAllLEDs(RGBColor color) = 0; + virtual void SetAllZoneLEDs(int zone, RGBColor color) = 0; + + virtual int GetMode() = 0; + virtual void SetMode(int mode) = 0; + + virtual unsigned char * GetDeviceDescription(unsigned int protocol_version) = 0; + virtual void ReadDeviceDescription(unsigned char* data_buf, unsigned int protocol_version) = 0; + + virtual unsigned char * GetModeDescription(int mode, unsigned int protocol_version) = 0; + virtual void SetModeDescription(unsigned char* data_buf, unsigned int protocol_version) = 0; + + virtual unsigned char * GetColorDescription() = 0; + virtual void SetColorDescription(unsigned char* data_buf) = 0; + + virtual unsigned char * GetZoneColorDescription(int zone) = 0; + virtual void SetZoneColorDescription(unsigned char* data_buf) = 0; + + virtual unsigned char * GetSingleLEDColorDescription(int led) = 0; + virtual void SetSingleLEDColorDescription(unsigned char* data_buf) = 0; + + virtual void RegisterUpdateCallback(RGBControllerCallback new_callback, void * new_callback_arg) = 0; + virtual void UnregisterUpdateCallback(void * callback_arg) = 0; + virtual void ClearCallbacks() = 0; + virtual void SignalUpdate() = 0; + + virtual void UpdateLEDs() = 0; + //virtual void UpdateZoneLEDs(int zone) = 0; + //virtual void UpdateSingleLED(int led) = 0; + + virtual void UpdateMode() = 0; + virtual void SaveMode() = 0; + + virtual void DeviceCallThreadFunction() = 0; + + virtual void ClearSegments(int zone) = 0; + virtual void AddSegment(int zone, segment new_segment) = 0; + + /*---------------------------------------------------------*\ + | Functions to be implemented in device implementation | + \*---------------------------------------------------------*/ + virtual void SetupZones() = 0; + + virtual void ResizeZone(int zone, int new_size) = 0; + + virtual void DeviceUpdateLEDs() = 0; + virtual void UpdateZoneLEDs(int zone) = 0; + virtual void UpdateSingleLED(int led) = 0; + + virtual void DeviceUpdateMode() = 0; + virtual void DeviceSaveMode() = 0; + + virtual void SetCustomMode() = 0; +}; + +class RGBController : public RGBControllerInterface +{ +public: + std::string name; /* controller name */ + std::string vendor; /* controller vendor */ + std::string description; /* controller description */ + std::string version; /* controller version */ + std::string serial; /* controller serial number */ + std::string location; /* controller location */ + std::vector leds; /* LEDs */ + std::vector zones; /* Zones */ + std::vector modes; /* Modes */ + std::vector colors; /* Color buffer */ + device_type type; /* device type */ + int active_mode = 0;/* active mode */ + std::vector + led_alt_names; /* alternate LED names */ + unsigned int flags; /* controller flags */ + + /*---------------------------------------------------------*\ + | RGBController base class constructor | + \*---------------------------------------------------------*/ + RGBController(); + virtual ~RGBController(); + + /*---------------------------------------------------------*\ + | Generic functions implemented in RGBController.cpp | + \*---------------------------------------------------------*/ + void SetupColors(); + + unsigned int GetLEDsInZone(unsigned int zone); + std::string GetName(); + std::string GetVendor(); + std::string GetDescription(); + std::string GetVersion(); + std::string GetSerial(); + std::string GetLocation(); + + std::string GetModeName(unsigned int mode); + std::string GetZoneName(unsigned int zone); + std::string GetLEDName(unsigned int led); + + RGBColor GetLED(unsigned int led); + void SetLED(unsigned int led, RGBColor color); + void SetAllLEDs(RGBColor color); + void SetAllZoneLEDs(int zone, RGBColor color); + + int GetMode(); + void SetMode(int mode); + + unsigned char * GetDeviceDescription(unsigned int protocol_version); + void ReadDeviceDescription(unsigned char* data_buf, unsigned int protocol_version); + + unsigned char * GetModeDescription(int mode, unsigned int protocol_version); + void SetModeDescription(unsigned char* data_buf, unsigned int protocol_version); + + unsigned char * GetColorDescription(); + void SetColorDescription(unsigned char* data_buf); + + unsigned char * GetZoneColorDescription(int zone); + void SetZoneColorDescription(unsigned char* data_buf); + + unsigned char * GetSingleLEDColorDescription(int led); + void SetSingleLEDColorDescription(unsigned char* data_buf); + + unsigned char * GetSegmentDescription(int zone, segment new_segment); + void SetSegmentDescription(unsigned char* data_buf); + + void RegisterUpdateCallback(RGBControllerCallback new_callback, void * new_callback_arg); + void UnregisterUpdateCallback(void * callback_arg); + void ClearCallbacks(); + void SignalUpdate(); + + void UpdateLEDs(); + //void UpdateZoneLEDs(int zone); + //void UpdateSingleLED(int led); + + void UpdateMode(); + void SaveMode(); + + void DeviceCallThreadFunction(); + + void ClearSegments(int zone); + void AddSegment(int zone, segment new_segment); + + /*---------------------------------------------------------*\ + | Functions to be implemented in device implementation | + \*---------------------------------------------------------*/ + virtual void SetupZones() = 0; + + virtual void ResizeZone(int zone, int new_size) = 0; + + virtual void DeviceUpdateLEDs() = 0; + virtual void UpdateZoneLEDs(int zone) = 0; + virtual void UpdateSingleLED(int led) = 0; + + virtual void DeviceUpdateMode() = 0; + void DeviceSaveMode(); + + void SetCustomMode(); + +private: + std::thread* DeviceCallThread; + std::atomic CallFlag_UpdateLEDs; + std::atomic CallFlag_UpdateMode; + std::atomic DeviceThreadRunning; + //bool CallFlag_UpdateZoneLEDs = false; + //bool CallFlag_UpdateSingleLED = false; + //bool CallFlag_UpdateMode = false; + + std::mutex UpdateMutex; + std::vector UpdateCallbacks; + std::vector UpdateCallbackArgs; +}; diff --git a/RGBController/RGBControllerKeyNames.cpp b/RGBController/RGBControllerKeyNames.cpp new file mode 100644 index 0000000..4372e7c --- /dev/null +++ b/RGBController/RGBControllerKeyNames.cpp @@ -0,0 +1,209 @@ +/*---------------------------------------------------------*\ +| RGBControllerKeyNames.cpp | +| | +| List of standardized names to represent keyboard keys | +| when naming LEDs on keyboard devices | +| | +| Chris M (Dr_No) 25 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBControllerKeyNames.h" + +const char* KEY_EN_UNUSED = ""; +const char* ZONE_EN_KEYBOARD = "Keyboard"; + +const char* KEY_EN_ESCAPE = "Key: Escape"; +const char* KEY_EN_F1 = "Key: F1"; +const char* KEY_EN_F2 = "Key: F2"; +const char* KEY_EN_F3 = "Key: F3"; +const char* KEY_EN_F4 = "Key: F4"; +const char* KEY_EN_F5 = "Key: F5"; +const char* KEY_EN_F6 = "Key: F6"; +const char* KEY_EN_F7 = "Key: F7"; +const char* KEY_EN_F8 = "Key: F8"; +const char* KEY_EN_F9 = "Key: F9"; +const char* KEY_EN_F10 = "Key: F10"; +const char* KEY_EN_F11 = "Key: F11"; +const char* KEY_EN_F12 = "Key: F12"; +const char* KEY_EN_PRINT_SCREEN = "Key: Print Screen"; +const char* KEY_EN_SCROLL_LOCK = "Key: Scroll Lock"; +const char* KEY_EN_PAUSE_BREAK = "Key: Pause/Break"; +const char* KEY_EN_POWER = "Key: Power"; + +const char* KEY_EN_BACK_TICK = "Key: `"; +const char* KEY_EN_1 = "Key: 1"; +const char* KEY_EN_2 = "Key: 2"; +const char* KEY_EN_3 = "Key: 3"; +const char* KEY_EN_4 = "Key: 4"; +const char* KEY_EN_5 = "Key: 5"; +const char* KEY_EN_6 = "Key: 6"; +const char* KEY_EN_7 = "Key: 7"; +const char* KEY_EN_8 = "Key: 8"; +const char* KEY_EN_9 = "Key: 9"; +const char* KEY_EN_0 = "Key: 0"; +const char* KEY_EN_MINUS = "Key: -"; +const char* KEY_EN_PLUS = "Key: +"; +const char* KEY_EN_EQUALS = "Key: ="; +const char* KEY_EN_BACKSPACE = "Key: Backspace"; +const char* KEY_EN_INSERT = "Key: Insert"; +const char* KEY_EN_HOME = "Key: Home"; +const char* KEY_EN_PAGE_UP = "Key: Page Up"; + +const char* KEY_EN_TAB = "Key: Tab"; +const char* KEY_EN_Q = "Key: Q"; +const char* KEY_EN_W = "Key: W"; +const char* KEY_EN_E = "Key: E"; +const char* KEY_EN_R = "Key: R"; +const char* KEY_EN_T = "Key: T"; +const char* KEY_EN_Y = "Key: Y"; +const char* KEY_EN_U = "Key: U"; +const char* KEY_EN_I = "Key: I"; +const char* KEY_EN_O = "Key: O"; +const char* KEY_EN_P = "Key: P"; +const char* KEY_EN_LEFT_BRACKET = "Key: ["; +const char* KEY_EN_RIGHT_BRACKET = "Key: ]"; +const char* KEY_EN_BACK_SLASH = "Key: \\"; +const char* KEY_EN_ANSI_BACK_SLASH = "Key: \\ (ANSI)"; +const char* KEY_EN_DELETE = "Key: Delete"; +const char* KEY_EN_END = "Key: End"; +const char* KEY_EN_PAGE_DOWN = "Key: Page Down"; + +const char* KEY_EN_CAPS_LOCK = "Key: Caps Lock"; +const char* KEY_EN_A = "Key: A"; +const char* KEY_EN_S = "Key: S"; +const char* KEY_EN_D = "Key: D"; +const char* KEY_EN_F = "Key: F"; +const char* KEY_EN_G = "Key: G"; +const char* KEY_EN_H = "Key: H"; +const char* KEY_EN_J = "Key: J"; +const char* KEY_EN_K = "Key: K"; +const char* KEY_EN_L = "Key: L"; +const char* KEY_EN_SEMICOLON = "Key: ;"; +const char* KEY_EN_QUOTE = "Key: '"; +const char* KEY_EN_POUND = "Key: #"; +const char* KEY_EN_ANSI_ENTER = "Key: Enter"; +const char* KEY_EN_ISO_ENTER = "Key: Enter (ISO)"; + +const char* KEY_EN_LEFT_SHIFT = "Key: Left Shift"; +const char* KEY_EN_ISO_BACK_SLASH = "Key: \\ (ISO)"; +const char* KEY_EN_Z = "Key: Z"; +const char* KEY_EN_X = "Key: X"; +const char* KEY_EN_C = "Key: C"; +const char* KEY_EN_V = "Key: V"; +const char* KEY_EN_B = "Key: B"; +const char* KEY_EN_N = "Key: N"; +const char* KEY_EN_M = "Key: M"; +const char* KEY_EN_COMMA = "Key: ,"; +const char* KEY_EN_PERIOD = "Key: ."; +const char* KEY_EN_FORWARD_SLASH = "Key: /"; +const char* KEY_EN_RIGHT_SHIFT = "Key: Right Shift"; +const char* KEY_EN_UP_ARROW = "Key: Up Arrow"; + +const char* KEY_EN_LEFT_CONTROL = "Key: Left Control"; +const char* KEY_EN_LEFT_WINDOWS = "Key: Left Windows"; +const char* KEY_EN_LEFT_FUNCTION = "Key: Left Fn"; +const char* KEY_EN_LEFT_ALT = "Key: Left Alt"; +const char* KEY_EN_SPACE = "Key: Space"; +const char* KEY_EN_RIGHT_ALT = "Key: Right Alt"; +const char* KEY_EN_RIGHT_FUNCTION = "Key: Right Fn"; +const char* KEY_EN_RIGHT_WINDOWS = "Key: Right Windows"; +const char* KEY_EN_MENU = "Key: Menu"; +const char* KEY_EN_RIGHT_CONTROL = "Key: Right Control"; +const char* KEY_EN_LEFT_ARROW = "Key: Left Arrow"; +const char* KEY_EN_DOWN_ARROW = "Key: Down Arrow"; +const char* KEY_EN_RIGHT_ARROW = "Key: Right Arrow"; + +const char* KEY_EN_NUMPAD_LOCK = "Key: Num Lock"; +const char* KEY_EN_NUMPAD_DIVIDE = "Key: Number Pad /"; +const char* KEY_EN_NUMPAD_TIMES = "Key: Number Pad *"; +const char* KEY_EN_NUMPAD_MINUS = "Key: Number Pad -"; +const char* KEY_EN_NUMPAD_PLUS = "Key: Number Pad +"; +const char* KEY_EN_NUMPAD_PERIOD = "Key: Number Pad ."; +const char* KEY_EN_NUMPAD_ENTER = "Key: Number Pad Enter"; +const char* KEY_EN_NUMPAD_EQUAL = "Key: Number Pad ="; +const char* KEY_EN_NUMPAD_0 = "Key: Number Pad 0"; +const char* KEY_EN_NUMPAD_1 = "Key: Number Pad 1"; +const char* KEY_EN_NUMPAD_2 = "Key: Number Pad 2"; +const char* KEY_EN_NUMPAD_3 = "Key: Number Pad 3"; +const char* KEY_EN_NUMPAD_4 = "Key: Number Pad 4"; +const char* KEY_EN_NUMPAD_5 = "Key: Number Pad 5"; +const char* KEY_EN_NUMPAD_6 = "Key: Number Pad 6"; +const char* KEY_EN_NUMPAD_7 = "Key: Number Pad 7"; +const char* KEY_EN_NUMPAD_8 = "Key: Number Pad 8"; +const char* KEY_EN_NUMPAD_9 = "Key: Number Pad 9"; + +const char* KEY_EN_MEDIA_PLAY_PAUSE = "Key: Media Play/Pause"; +const char* KEY_EN_MEDIA_PREVIOUS = "Key: Media Previous"; +const char* KEY_EN_MEDIA_NEXT = "Key: Media Next"; +const char* KEY_EN_MEDIA_STOP = "Key: Media Stop"; +const char* KEY_EN_MEDIA_MUTE = "Key: Media Mute"; +const char* KEY_EN_MEDIA_VOLUME_DOWN = "Key: Media Volume -"; +const char* KEY_EN_MEDIA_VOLUME_UP = "Key: Media Volume +"; + +const char* KEY_EN_F13 = "Key: F13"; +const char* KEY_EN_F14 = "Key: F14"; +const char* KEY_EN_F15 = "Key: F15"; +const char* KEY_EN_F16 = "Key: F16"; +const char* KEY_EN_F17 = "Key: F17"; +const char* KEY_EN_F18 = "Key: F18"; +const char* KEY_EN_F19 = "Key: F19"; +const char* KEY_EN_F20 = "Key: F20"; +const char* KEY_EN_F21 = "Key: F21"; +const char* KEY_EN_F22 = "Key: F22"; +const char* KEY_EN_F23 = "Key: F23"; +const char* KEY_EN_F24 = "Key: F24"; + +const char* KEY_JP_RO = "Key: _"; +const char* KEY_JP_EJ = "Key: E/J"; +const char* KEY_JP_ZENKAKU = "Key: 半角/全角"; +const char* KEY_JP_KANA = "Key: かな"; +const char* KEY_JP_HENKAN = "Key: 変換"; +const char* KEY_JP_MUHENKAN = "Key: 無変換"; +const char* KEY_JP_YEN = "Key: ¥"; +const char* KEY_JP_AT = "Key: @"; +const char* KEY_JP_CHEVRON = "Key: ^"; +const char* KEY_JP_COLON = "Key: :"; +const char* KEY_JP_KATAKANA = "Key: カタカナ"; +const char* KEY_JP_HIRAGANA = "Key: ひらがな"; + +const char* KEY_KR_HAN = "Key: 한/영"; +const char* KEY_KR_HANJA = "Key: 한자"; + +const char* KEY_NORD_AAL = "Key: Å"; +const char* KEY_NORD_A_OE = "Key: Ä Ø"; +const char* KEY_NORD_O_AE = "Key: Ö Æ"; +const char* KEY_NORD_HALF = "Key: § ½"; +const char* KEY_NORD_HYPHEN = "Key: - _"; +const char* KEY_NORD_PLUS_QUESTION = "Key: + ?"; +const char* KEY_NORD_ACUTE_GRAVE = "Key: ´ `"; +const char* KEY_NORD_DOTS_CARET = "Key: ¨ ^"; +const char* KEY_NORD_QUOTE = "Key: ' *"; +const char* KEY_NORD_ANGLE_BRACKET = "Key: < >"; + +const char* KEY_DE_ESZETT = "Key: ß"; +const char* KEY_DE_DIAERESIS_A = "Key: Ä"; +const char* KEY_DE_DIAERESIS_O = "Key: Ö"; +const char* KEY_DE_DIAERESIS_U = "Key: Ü"; + +const char* KEY_FR_SUPER_2 = "Key: ²"; +const char* KEY_FR_AMPERSAND = "Key: &"; +const char* KEY_FR_ACUTE_E = "Key: é"; +const char* KEY_FR_DOUBLEQUOTE = "Key: \""; +const char* KEY_FR_LEFT_PARENTHESIS = "Key: ("; +const char* KEY_FR_GRAVE_E = "Key: è"; +const char* KEY_FR_UNDERSCORE = "Key: _"; +const char* KEY_FR_CEDILLA_C = "Key: ç"; +const char* KEY_FR_GRAVE_A = "Key: à"; +const char* KEY_FR_RIGHT_PARENTHESIS = "Key: )"; +const char* KEY_FR_DOLLAR = "Key: $"; +const char* KEY_FR_GRAVE_U = "Key: ù"; +const char* KEY_FR_ASTERIX = "Key: *"; +const char* KEY_FR_EXCLAIMATION = "Key: !"; + +const char* KEY_ES_OPEN_QUESTION_MARK = "Key: ¿/¡"; +const char* KEY_ES_TILDE = "Key: ´/¨"; +const char* KEY_ES_ENIE = "Key: Ñ"; +const char* KEY_BR_TILDE = "Key: ~"; diff --git a/RGBController/RGBControllerKeyNames.h b/RGBController/RGBControllerKeyNames.h new file mode 100644 index 0000000..f066a1e --- /dev/null +++ b/RGBController/RGBControllerKeyNames.h @@ -0,0 +1,209 @@ +/*---------------------------------------------------------*\ +| RGBControllerKeyNames.h | +| | +| List of standardized names to represent keyboard keys | +| when naming LEDs on keyboard devices | +| | +| Chris M (Dr_No) 25 Jan 2022 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +extern const char* KEY_EN_UNUSED; +extern const char* ZONE_EN_KEYBOARD; + +extern const char* KEY_EN_ESCAPE; +extern const char* KEY_EN_F1; +extern const char* KEY_EN_F2; +extern const char* KEY_EN_F3; +extern const char* KEY_EN_F4; +extern const char* KEY_EN_F5; +extern const char* KEY_EN_F6; +extern const char* KEY_EN_F7; +extern const char* KEY_EN_F8; +extern const char* KEY_EN_F9; +extern const char* KEY_EN_F10; +extern const char* KEY_EN_F11; +extern const char* KEY_EN_F12; +extern const char* KEY_EN_PRINT_SCREEN; +extern const char* KEY_EN_SCROLL_LOCK; +extern const char* KEY_EN_PAUSE_BREAK; +extern const char* KEY_EN_POWER; + +extern const char* KEY_EN_BACK_TICK; +extern const char* KEY_EN_1; +extern const char* KEY_EN_2; +extern const char* KEY_EN_3; +extern const char* KEY_EN_4; +extern const char* KEY_EN_5; +extern const char* KEY_EN_6; +extern const char* KEY_EN_7; +extern const char* KEY_EN_8; +extern const char* KEY_EN_9; +extern const char* KEY_EN_0; +extern const char* KEY_EN_MINUS; +extern const char* KEY_EN_PLUS; +extern const char* KEY_EN_EQUALS; +extern const char* KEY_EN_BACKSPACE; +extern const char* KEY_EN_INSERT; +extern const char* KEY_EN_HOME; +extern const char* KEY_EN_PAGE_UP; + +extern const char* KEY_EN_TAB; +extern const char* KEY_EN_Q; +extern const char* KEY_EN_W; +extern const char* KEY_EN_E; +extern const char* KEY_EN_R; +extern const char* KEY_EN_T; +extern const char* KEY_EN_Y; +extern const char* KEY_EN_U; +extern const char* KEY_EN_I; +extern const char* KEY_EN_O; +extern const char* KEY_EN_P; +extern const char* KEY_EN_LEFT_BRACKET; +extern const char* KEY_EN_RIGHT_BRACKET; +extern const char* KEY_EN_BACK_SLASH; +extern const char* KEY_EN_ANSI_BACK_SLASH; +extern const char* KEY_EN_DELETE; +extern const char* KEY_EN_END; +extern const char* KEY_EN_PAGE_DOWN; + +extern const char* KEY_EN_CAPS_LOCK; +extern const char* KEY_EN_A; +extern const char* KEY_EN_S; +extern const char* KEY_EN_D; +extern const char* KEY_EN_F; +extern const char* KEY_EN_G; +extern const char* KEY_EN_H; +extern const char* KEY_EN_J; +extern const char* KEY_EN_K; +extern const char* KEY_EN_L; +extern const char* KEY_EN_SEMICOLON; +extern const char* KEY_EN_QUOTE; +extern const char* KEY_EN_POUND; +extern const char* KEY_EN_ANSI_ENTER; +extern const char* KEY_EN_ISO_ENTER; + +extern const char* KEY_EN_LEFT_SHIFT; +extern const char* KEY_EN_ISO_BACK_SLASH; +extern const char* KEY_EN_Z; +extern const char* KEY_EN_X; +extern const char* KEY_EN_C; +extern const char* KEY_EN_V; +extern const char* KEY_EN_B; +extern const char* KEY_EN_N; +extern const char* KEY_EN_M; +extern const char* KEY_EN_COMMA; +extern const char* KEY_EN_PERIOD; +extern const char* KEY_EN_FORWARD_SLASH; +extern const char* KEY_EN_RIGHT_SHIFT; +extern const char* KEY_EN_UP_ARROW; + +extern const char* KEY_EN_LEFT_CONTROL; +extern const char* KEY_EN_LEFT_WINDOWS; +extern const char* KEY_EN_LEFT_FUNCTION; +extern const char* KEY_EN_LEFT_ALT; +extern const char* KEY_EN_SPACE; +extern const char* KEY_EN_RIGHT_ALT; +extern const char* KEY_EN_RIGHT_FUNCTION; +extern const char* KEY_EN_RIGHT_WINDOWS; +extern const char* KEY_EN_MENU; +extern const char* KEY_EN_RIGHT_CONTROL; +extern const char* KEY_EN_LEFT_ARROW; +extern const char* KEY_EN_DOWN_ARROW; +extern const char* KEY_EN_RIGHT_ARROW; + +extern const char* KEY_EN_NUMPAD_LOCK; +extern const char* KEY_EN_NUMPAD_DIVIDE; +extern const char* KEY_EN_NUMPAD_TIMES; +extern const char* KEY_EN_NUMPAD_MINUS; +extern const char* KEY_EN_NUMPAD_PLUS; +extern const char* KEY_EN_NUMPAD_PERIOD; +extern const char* KEY_EN_NUMPAD_ENTER; +extern const char* KEY_EN_NUMPAD_EQUAL; +extern const char* KEY_EN_NUMPAD_0; +extern const char* KEY_EN_NUMPAD_1; +extern const char* KEY_EN_NUMPAD_2; +extern const char* KEY_EN_NUMPAD_3; +extern const char* KEY_EN_NUMPAD_4; +extern const char* KEY_EN_NUMPAD_5; +extern const char* KEY_EN_NUMPAD_6; +extern const char* KEY_EN_NUMPAD_7; +extern const char* KEY_EN_NUMPAD_8; +extern const char* KEY_EN_NUMPAD_9; + +extern const char* KEY_EN_MEDIA_PLAY_PAUSE; +extern const char* KEY_EN_MEDIA_PREVIOUS; +extern const char* KEY_EN_MEDIA_NEXT; +extern const char* KEY_EN_MEDIA_STOP; +extern const char* KEY_EN_MEDIA_MUTE; +extern const char* KEY_EN_MEDIA_VOLUME_DOWN; +extern const char* KEY_EN_MEDIA_VOLUME_UP; + +extern const char* KEY_EN_F13; +extern const char* KEY_EN_F14; +extern const char* KEY_EN_F15; +extern const char* KEY_EN_F16; +extern const char* KEY_EN_F17; +extern const char* KEY_EN_F18; +extern const char* KEY_EN_F19; +extern const char* KEY_EN_F20; +extern const char* KEY_EN_F21; +extern const char* KEY_EN_F22; +extern const char* KEY_EN_F23; +extern const char* KEY_EN_F24; + +extern const char* KEY_JP_RO; +extern const char* KEY_JP_EJ; +extern const char* KEY_JP_ZENKAKU; +extern const char* KEY_JP_KANA; +extern const char* KEY_JP_HENKAN; +extern const char* KEY_JP_MUHENKAN; +extern const char* KEY_JP_YEN; +extern const char* KEY_JP_AT; +extern const char* KEY_JP_CHEVRON; +extern const char* KEY_JP_COLON; +extern const char* KEY_JP_KATAKANA; +extern const char* KEY_JP_HIRAGANA; + +extern const char* KEY_KR_HAN; +extern const char* KEY_KR_HANJA; + +extern const char* KEY_NORD_AAL; +extern const char* KEY_NORD_A_OE; +extern const char* KEY_NORD_O_AE; +extern const char* KEY_NORD_HALF; +extern const char* KEY_NORD_HYPHEN; +extern const char* KEY_NORD_PLUS_QUESTION; +extern const char* KEY_NORD_ACUTE_GRAVE; +extern const char* KEY_NORD_DOTS_CARET; +extern const char* KEY_NORD_QUOTE; +extern const char* KEY_NORD_ANGLE_BRACKET; + +extern const char* KEY_DE_ESZETT; +extern const char* KEY_DE_DIAERESIS_A; +extern const char* KEY_DE_DIAERESIS_O; +extern const char* KEY_DE_DIAERESIS_U; + +extern const char* KEY_FR_SUPER_2; +extern const char* KEY_FR_AMPERSAND; +extern const char* KEY_FR_ACUTE_E; +extern const char* KEY_FR_DOUBLEQUOTE; +extern const char* KEY_FR_LEFT_PARENTHESIS; +extern const char* KEY_FR_GRAVE_E; +extern const char* KEY_FR_UNDERSCORE; +extern const char* KEY_FR_CEDILLA_C; +extern const char* KEY_FR_GRAVE_A; +extern const char* KEY_FR_RIGHT_PARENTHESIS; +extern const char* KEY_FR_DOLLAR; +extern const char* KEY_FR_GRAVE_U; +extern const char* KEY_FR_ASTERIX; +extern const char* KEY_FR_EXCLAIMATION; + +extern const char* KEY_ES_OPEN_QUESTION_MARK; +extern const char* KEY_ES_TILDE; +extern const char* KEY_ES_ENIE; +extern const char* KEY_BR_TILDE; diff --git a/RGBController/RGBController_Dummy.cpp b/RGBController/RGBController_Dummy.cpp new file mode 100644 index 0000000..119742b --- /dev/null +++ b/RGBController/RGBController_Dummy.cpp @@ -0,0 +1,73 @@ +/*---------------------------------------------------------*\ +| RGBController_Dummy.cpp | +| | +| Dummy RGBController that can mimic various devices for | +| development and test purposes | +| | +| Adam Honse (CalcProgrammer1) 25 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_Dummy.h" + +/**------------------------------------------------------------------*\ + @name Dummy + @category Dummy + @type I2C or Serial or WMI or USB + @save :white_check_mark: or :robot: or :o: or :x: + @direct :white_check_mark: or :rotating_light: or :o: or :x: + @effects :white_check_mark: or :rotating_light: or :tools: or :o: or :x: + @detectors DetectDummy,DetectDummy2 + @comment Insert multiline dummy comment here + + | Symbol | Meaning | + | :---: | :--- | + | :white_check_mark: | Fully supported by OpenRGB | + | :rotating_light: | Support is problematic | + | :robot: | Feature is automatic and can not be turned off | + | :tools: | Partially supported by OpenRGB | + | :o: | Not currently supported by OpenRGB | + | :x: | Not applicable for this device | +*/ + +RGBController_Dummy::RGBController_Dummy() +{ + +} + +void RGBController_Dummy::SetupZones() +{ + +} + +void RGBController_Dummy::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_Dummy::DeviceUpdateLEDs() +{ + +} + +void RGBController_Dummy::UpdateZoneLEDs(int /*zone*/) +{ + +} + +void RGBController_Dummy::UpdateSingleLED(int /*led*/) +{ + +} + +void RGBController_Dummy::SetCustomMode() +{ + +} + +void RGBController_Dummy::DeviceUpdateMode() +{ + +} diff --git a/RGBController/RGBController_Dummy.h b/RGBController/RGBController_Dummy.h new file mode 100644 index 0000000..4ab5c1f --- /dev/null +++ b/RGBController/RGBController_Dummy.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| RGBController_Dummy.h | +| | +| Dummy RGBController that can mimic various devices for | +| development and test purposes | +| | +| Adam Honse (CalcProgrammer1) 25 Feb 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" + +class RGBController_Dummy : public RGBController +{ +public: + RGBController_Dummy(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void SetCustomMode(); + void DeviceUpdateMode(); +}; diff --git a/RGBController/RGBController_Network.cpp b/RGBController/RGBController_Network.cpp new file mode 100644 index 0000000..61c6ac8 --- /dev/null +++ b/RGBController/RGBController_Network.cpp @@ -0,0 +1,136 @@ +/*---------------------------------------------------------*\ +| RGBController_Network.cpp | +| | +| RGBController implementation that represents a remote | +| RGBController instance from a connected OpenRGB server | +| | +| Adam Honse (CalcProgrammer1) 11 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include + +#include "RGBController_Network.h" + +RGBController_Network::RGBController_Network(NetworkClient * client_ptr, unsigned int dev_idx_val) +{ + client = client_ptr; + dev_idx = dev_idx_val; +} + +void RGBController_Network::SetupZones() +{ + //Don't send anything, this function should only process on host +} + +void RGBController_Network::ClearSegments(int zone) +{ + client->SendRequest_RGBController_ClearSegments(dev_idx, zone); + + client->SendRequest_ControllerData(dev_idx); + client->WaitOnControllerData(); +} + +void RGBController_Network::AddSegment(int zone, segment new_segment) +{ + unsigned char * data = GetSegmentDescription(zone, new_segment); + unsigned int size; + + memcpy(&size, &data[0], sizeof(unsigned int)); + + client->SendRequest_RGBController_AddSegment(dev_idx, data, size); + + delete[] data; + + client->SendRequest_ControllerData(dev_idx); + client->WaitOnControllerData(); +} + +void RGBController_Network::ResizeZone(int zone, int new_size) +{ + client->SendRequest_RGBController_ResizeZone(dev_idx, zone, new_size); + + client->SendRequest_ControllerData(dev_idx); + client->WaitOnControllerData(); +} + +void RGBController_Network::DeviceUpdateLEDs() +{ + unsigned char * data = GetColorDescription(); + unsigned int size; + + memcpy(&size, &data[0], sizeof(unsigned int)); + + client->SendRequest_RGBController_UpdateLEDs(dev_idx, data, size); + + delete[] data; +} + +void RGBController_Network::UpdateZoneLEDs(int zone) +{ + unsigned char * data = GetZoneColorDescription(zone); + unsigned int size; + + memcpy(&size, &data[0], sizeof(unsigned int)); + + client->SendRequest_RGBController_UpdateZoneLEDs(dev_idx, data, size); + + delete[] data; +} + +void RGBController_Network::UpdateSingleLED(int led) +{ + unsigned char * data = GetSingleLEDColorDescription(led); + + client->SendRequest_RGBController_UpdateSingleLED(dev_idx, data, sizeof(int) + sizeof(RGBColor)); + + delete[] data; +} + +void RGBController_Network::SetCustomMode() +{ + client->SendRequest_RGBController_SetCustomMode(dev_idx); + + client->SendRequest_ControllerData(dev_idx); + client->WaitOnControllerData(); +} + +void RGBController_Network::DeviceUpdateMode() +{ + unsigned char * data = GetModeDescription(active_mode, client->GetProtocolVersion()); + unsigned int size; + + memcpy(&size, &data[0], sizeof(unsigned int)); + + client->SendRequest_RGBController_UpdateMode(dev_idx, data, size); + + delete[] data; +} + +void RGBController_Network::DeviceSaveMode() +{ + unsigned char * data = GetModeDescription(active_mode, client->GetProtocolVersion()); + unsigned int size; + + memcpy(&size, &data[0], sizeof(unsigned int)); + + client->SendRequest_RGBController_SaveMode(dev_idx, data, size); + + delete[] data; +} + +/*-----------------------------------------------------*\ +| This function overrides RGBController::UpdateLEDs()! | +| Normally, UpdateLEDs() sets a flag for the updater | +| thread to update the device asynchronously, which | +| prevents delays updating local devices. This causes | +| instability and flickering with network devices though| +| so for the network implementation, process all updates| +| synchronously. | +\*-----------------------------------------------------*/ +void RGBController_Network::UpdateLEDs() +{ + DeviceUpdateLEDs(); +} diff --git a/RGBController/RGBController_Network.h b/RGBController/RGBController_Network.h new file mode 100644 index 0000000..fd66e36 --- /dev/null +++ b/RGBController/RGBController_Network.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| RGBController_Network.h | +| | +| RGBController implementation that represents a remote | +| RGBController instance from a connected OpenRGB server | +| | +| Adam Honse (CalcProgrammer1) 11 Apr 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "NetworkClient.h" + +class RGBController_Network : public RGBController +{ +public: + RGBController_Network(NetworkClient * client_ptr, unsigned int dev_idx_val); + + void SetupZones(); + + void ClearSegments(int zone); + void AddSegment(int zone, segment new_segment); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void SetCustomMode(); + void DeviceUpdateMode(); + void DeviceSaveMode(); + + void UpdateLEDs(); + +private: + NetworkClient * client; + unsigned int dev_idx; +}; diff --git a/ResourceManager.cpp b/ResourceManager.cpp new file mode 100644 index 0000000..d84ffc3 --- /dev/null +++ b/ResourceManager.cpp @@ -0,0 +1,2243 @@ +/*---------------------------------------------------------*\ +| ResourceManager.cpp | +| | +| OpenRGB Resource Manager controls access to application | +| components including RGBControllers, I2C interfaces, | +| and network SDK components | +| | +| Adam Honse (CalcProgrammer1) 27 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#ifdef _WIN32 +#include +#include +#endif + +#include +#include +#include +#include "cli.h" +#include "pci_ids/pci_ids.h" +#include "ResourceManager.h" +#include "ProfileManager.h" +#include "LogManager.h" +#include "SettingsManager.h" +#include "NetworkClient.h" +#include "NetworkServer.h" +#include "filesystem.h" +#include "StringUtils.h" + +#ifdef __linux__ +#include +#endif + +/*---------------------------------------------------------*\ +| Warning Dialog Strings | +\*---------------------------------------------------------*/ +const char* I2C_ERR_WIN = QT_TRANSLATE_NOOP("ResourceManager", + "

Warning:

" + "

One or more I2C/SMBus interfaces failed to initialize.

" + "

Depending on which interfaces failed to initialize, some RGB DRAM modules, some motherboards' onboard RGB lighting, and RGB graphics cards may not be available in OpenRGB.

" + "

On Windows, this is usually caused by a failure to load the PawnIO driver.

" + "

For OpenRGB to access these devices, you must install PawnIO from https://pawnio.eu and run OpenRGB as administrator or as a system service.

" + "

If you are not using any of the devices listed above, you can safely ignore this message.

"); +const char* I2C_ERR_LINUX = QT_TRANSLATE_NOOP("ResourceManager", + "

Warning:

" + "

One or more I2C/SMBus interfaces failed to initialize.

" + "

Depending on which interfaces failed to initialize, some RGB DRAM modules, some motherboards' onboard RGB lighting, and RGB graphics cards may not be available in OpenRGB.

" + "

On Linux, this is usually because the i2c-dev module is not loaded.

" + "

For OpenRGB to access these devices, you must load the i2c-dev module along with the correct I2C driver module for your motherboard. " + "This is usually i2c-piix4 for AMD systems and i2c-i801 for Intel systems.

" + "

If you are not using any of the devices listed above, you can safely ignore this message.

"); + +const char* UDEV_MISSING = QT_TRANSLATE_NOOP("ResourceManager", + "

Warning:

" + "

The OpenRGB udev rules are not installed.

" + "

Most devices will not be available unless running OpenRGB as root.

" + "

If using AppImage, Flatpak, or self-compiled versions of OpenRGB you must install the udev rules manually

" + "

See https://openrgb.org/udev to install the udev rules manually

"); +const char* UDEV_MUTLI = QT_TRANSLATE_NOOP("ResourceManager", + "

Warning:

" + "

Multiple OpenRGB udev rules are installed.

" + "

The udev rules file 60-openrgb.rules is installed in both /etc/udev/rules.d and /usr/lib/udev/rules.d.

" + "

Multiple udev rules files can conflict, it is recommended to remove one of them.

"); + + +const hidapi_wrapper default_wrapper = +{ + NULL, + (hidapi_wrapper_send_feature_report) hid_send_feature_report, + (hidapi_wrapper_get_feature_report) hid_get_feature_report, + (hidapi_wrapper_get_serial_number_string) hid_get_serial_number_string, + (hidapi_wrapper_open_path) hid_open_path, + (hidapi_wrapper_enumerate) hid_enumerate, + (hidapi_wrapper_free_enumeration) hid_free_enumeration, + (hidapi_wrapper_close) hid_close, + (hidapi_wrapper_error) hid_error +}; + +bool BasicHIDBlock::compare(hid_device_info* info) +{ + return ( (vid == info->vendor_id) + && (pid == info->product_id) +#ifdef USE_HID_USAGE + && ( (usage_page == HID_USAGE_PAGE_ANY) + || (usage_page == info->usage_page) ) + && ( (usage == HID_USAGE_ANY) + || (usage == info->usage) ) + && ( (interface == HID_INTERFACE_ANY) + || (interface == info->interface_number ) ) +#else + && ( (interface == HID_INTERFACE_ANY) + || (interface == info->interface_number ) ) +#endif + ); +} + +bool BasicHIDBlock::compare_no_interface(hid_device_info* info) +{ + return ( (vid == info->vendor_id) + && (pid == info->product_id) +#ifdef USE_HID_USAGE + && ( (usage_page == HID_USAGE_PAGE_ANY) + || (usage_page == info->usage_page) ) + && ( (usage == HID_USAGE_ANY) + || (usage == info->usage) ) +#endif + ); +} + +ResourceManager* ResourceManager::instance; + +using namespace std::chrono_literals; + +ResourceManager *ResourceManager::get() +{ + if(!instance) + { + instance = new ResourceManager(); + } + + return instance; +} + +ResourceManager::ResourceManager() +{ + /*-----------------------------------------------------*\ + | Initialize Detection Variables | + \*-----------------------------------------------------*/ + auto_connection_client = NULL; + auto_connection_active = false; + detection_enabled = true; + detection_percent = 100; + detection_string = ""; + detection_is_required = false; + dynamic_detectors_processed = false; + init_finished = false; + initial_detection = true; + background_thread_running = true; + + /*-----------------------------------------------------*\ + | Start the background detection thread in advance; it | + | will be suspended until necessary | + \*-----------------------------------------------------*/ + DetectDevicesThread = new std::thread(&ResourceManager::BackgroundThreadFunction, this); + + SetupConfigurationDirectory(); + + /*-----------------------------------------------------*\ + | Load settings from file | + \*-----------------------------------------------------*/ + settings_manager = new SettingsManager(); + + settings_manager->LoadSettings(GetConfigurationDirectory() / "OpenRGB.json"); + + /*-----------------------------------------------------*\ + | Configure the log manager | + \*-----------------------------------------------------*/ + LogManager::get()->configure(settings_manager->GetSettings("LogManager"), GetConfigurationDirectory()); + + /*-----------------------------------------------------*\ + | Initialize Server Instance | + | If configured, pass through full controller list | + | including clients. Otherwise, pass only local | + | hardware controllers | + \*-----------------------------------------------------*/ + json server_settings = settings_manager->GetSettings("Server"); + bool all_controllers = false; + bool legacy_workaround = false; + + if(server_settings.contains("all_controllers")) + { + all_controllers = server_settings["all_controllers"]; + } + + if(all_controllers) + { + server = new NetworkServer(rgb_controllers); + } + else + { + server = new NetworkServer(rgb_controllers_hw); + } + + /*-----------------------------------------------------*\ + | Enable legacy SDK workaround in server if configured | + \*-----------------------------------------------------*/ + if(server_settings.contains("legacy_workaround")) + { + legacy_workaround = server_settings["legacy_workaround"]; + } + + if(legacy_workaround) + { + server->SetLegacyWorkaroundEnable(true); + } + + /*-----------------------------------------------------*\ + | Load sizes list from file | + \*-----------------------------------------------------*/ + profile_manager = new ProfileManager(GetConfigurationDirectory()); + server->SetProfileManager(profile_manager); + rgb_controllers_sizes = profile_manager->LoadProfileToList("sizes", true); + + /*-----------------------------------------------------*\ + | If configured, lower process priority to potentially | + | reduce interference with other programs. Positive | + | nice values decrease priority on Linux and MacOS. | + \*-----------------------------------------------------*/ + json general_settings = settings_manager->GetSettings("General"); + bool low_priority = false; + + if(general_settings.contains("low_priority")) + { + low_priority = general_settings["low_priority"]; + } + + if(low_priority) + { +#if defined(__linux__) || defined(__APPLE__) + setpriority(PRIO_PROCESS, 0, 10); +#endif +#ifdef _WIN32 + SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS); +#endif + } +} + +ResourceManager::~ResourceManager() +{ + Cleanup(); + + /*-----------------------------------------------------*\ + | Mark the background detection thread as not running | + | and then wake it up so it knows that it has to stop | + \*-----------------------------------------------------*/ + background_thread_running = false; + BackgroundFunctionStartTrigger.notify_one(); + + /*-----------------------------------------------------*\ + | Stop the background thread | + \*-----------------------------------------------------*/ + if(DetectDevicesThread) + { + DetectDevicesThread->join(); + delete DetectDevicesThread; + DetectDevicesThread = nullptr; + } +} + +void ResourceManager::RegisterI2CBus(i2c_smbus_interface *bus) +{ + LOG_INFO("[ResourceManager] Registering I2C interface: %s Device %04X:%04X Subsystem: %04X:%04X", bus->device_name, bus->pci_vendor, bus->pci_device,bus->pci_subsystem_vendor,bus->pci_subsystem_device); + busses.push_back(bus); +} + +std::vector & ResourceManager::GetI2CBusses() +{ + return busses; +} + +void ResourceManager::RegisterRGBController(RGBController *rgb_controller) +{ + /*-----------------------------------------------------*\ + | Mark this controller as locally owned | + \*-----------------------------------------------------*/ + rgb_controller->flags &= ~CONTROLLER_FLAG_REMOTE; + rgb_controller->flags |= CONTROLLER_FLAG_LOCAL; + + LOG_INFO("[%s] Registering RGB controller", rgb_controller->GetName().c_str()); + rgb_controllers_hw.push_back(rgb_controller); + + /*-----------------------------------------------------*\ + | If the device list size has changed, call the device | + | list changed callbacks | + | | + | TODO: If all detection is reworked to use | + | RegisterRGBController, tracking of previous list size | + | can be removed and profile can be loaded per | + | controller before adding to list | + \*-----------------------------------------------------*/ + if(rgb_controllers_hw.size() != detection_prev_size) + { + /*-------------------------------------------------*\ + | First, load sizes for the new controllers | + \*-------------------------------------------------*/ + for(unsigned int controller_size_idx = detection_prev_size; controller_size_idx < rgb_controllers_hw.size(); controller_size_idx++) + { + profile_manager->LoadDeviceFromListWithOptions(rgb_controllers_sizes, detection_size_entry_used, rgb_controllers_hw[controller_size_idx], true, false); + } + + UpdateDeviceList(); + } + + detection_prev_size = (unsigned int)rgb_controllers_hw.size(); + + UpdateDeviceList(); +} + +void ResourceManager::UnregisterRGBController(RGBController* rgb_controller) +{ + LOG_INFO("[%s] Unregistering RGB controller", rgb_controller->GetName().c_str()); + + /*-----------------------------------------------------*\ + | Clear callbacks from the controller before removal | + \*-----------------------------------------------------*/ + rgb_controller->ClearCallbacks(); + + /*-----------------------------------------------------*\ + | Find the controller to remove and remove it from the | + | hardware list | + \*-----------------------------------------------------*/ + std::vector::iterator hw_it = std::find(rgb_controllers_hw.begin(), rgb_controllers_hw.end(), rgb_controller); + + if (hw_it != rgb_controllers_hw.end()) + { + rgb_controllers_hw.erase(hw_it); + } + + /*-----------------------------------------------------*\ + | Find the controller to remove and remove it from the | + | master list | + \*-----------------------------------------------------*/ + std::vector::iterator rgb_it = std::find(rgb_controllers.begin(), rgb_controllers.end(), rgb_controller); + + if (rgb_it != rgb_controllers.end()) + { + rgb_controllers.erase(rgb_it); + } + + UpdateDeviceList(); +} + +std::vector & ResourceManager::GetRGBControllers() +{ + return rgb_controllers; +} + +void ResourceManager::RegisterI2CBusDetector(I2CBusDetectorFunction detector) +{ + i2c_bus_detectors.push_back(detector); +} + +void ResourceManager::RegisterI2CDeviceDetector(std::string name, I2CDeviceDetectorFunction detector) +{ + i2c_device_detector_strings.push_back(name); + i2c_device_detectors.push_back(detector); +} + +void ResourceManager::RegisterI2CDIMMDeviceDetector(std::string name, I2CDIMMDeviceDetectorFunction detector, uint16_t jedec_id, uint8_t dimm_type) +{ + I2CDIMMDeviceDetectorBlock block; + + block.name = name; + block.function = detector; + block.jedec_id = jedec_id; + block.dimm_type = dimm_type; + + i2c_dimm_device_detectors.push_back(block); +} + +void ResourceManager::RegisterI2CPCIDeviceDetector(std::string name, I2CPCIDeviceDetectorFunction detector, uint16_t ven_id, uint16_t dev_id, uint16_t subven_id, uint16_t subdev_id, uint8_t i2c_addr) +{ + I2CPCIDeviceDetectorBlock block; + + block.name = name; + block.function = detector; + block.ven_id = ven_id; + block.dev_id = dev_id; + block.subven_id = subven_id; + block.subdev_id = subdev_id; + block.i2c_addr = i2c_addr; + + i2c_pci_device_detectors.push_back(block); +} + +void ResourceManager::RegisterDeviceDetector(std::string name, DeviceDetectorFunction detector) +{ + device_detector_strings.push_back(name); + device_detectors.push_back(detector); +} + +void ResourceManager::RegisterHIDDeviceDetector(std::string name, + HIDDeviceDetectorFunction detector, + uint16_t vid, + uint16_t pid, + int interface, + int usage_page, + int usage) +{ + HIDDeviceDetectorBlock block; + + block.name = name; + block.vid = vid; + block.pid = pid; + block.function = detector; + block.interface = interface; + block.usage_page = usage_page; + block.usage = usage; + + hid_device_detectors.push_back(block); +} + +void ResourceManager::RegisterHIDWrappedDeviceDetector(std::string name, + HIDWrappedDeviceDetectorFunction detector, + uint16_t vid, + uint16_t pid, + int interface, + int usage_page, + int usage) +{ + HIDWrappedDeviceDetectorBlock block; + + block.name = name; + block.vid = vid; + block.pid = pid; + block.function = detector; + block.interface = interface; + block.usage_page = usage_page; + block.usage = usage; + + hid_wrapped_device_detectors.push_back(block); +} + +void ResourceManager::RegisterDynamicDetector(std::string name, DynamicDetectorFunction detector) +{ + dynamic_detector_strings.push_back(name); + dynamic_detectors.push_back(detector); +} + +void ResourceManager::RegisterPreDetectionHook(PreDetectionHookFunction hook) +{ + pre_detection_hooks.push_back(hook); +} + +void ResourceManager::RegisterClientInfoChangeCallback(ClientInfoChangeCallback new_callback, void * new_callback_arg) +{ + ClientInfoChangeCallbacks.push_back(new_callback); + ClientInfoChangeCallbackArgs.push_back(new_callback_arg); + + LOG_TRACE("[ResourceManager] Registered client info change callback. Total callbacks registered: %d", ClientInfoChangeCallbacks.size()); +} + +void ResourceManager::UnregisterClientInfoChangeCallback(ClientInfoChangeCallback callback, void * callback_arg) +{ + for(size_t idx = 0; idx < ClientInfoChangeCallbacks.size(); idx++) + { + if(ClientInfoChangeCallbacks[idx] == callback && ClientInfoChangeCallbackArgs[idx] == callback_arg) + { + ClientInfoChangeCallbacks.erase(ClientInfoChangeCallbacks.begin() + idx); + ClientInfoChangeCallbackArgs.erase(ClientInfoChangeCallbackArgs.begin() + idx); + } + } + + LOG_TRACE("[ResourceManager] Unregistered client info change callback. Total callbacks registered: %d", ClientInfoChangeCallbacks.size()); +} + +void ResourceManager::RegisterDeviceListChangeCallback(DeviceListChangeCallback new_callback, void * new_callback_arg) +{ + DeviceListChangeCallbacks.push_back(new_callback); + DeviceListChangeCallbackArgs.push_back(new_callback_arg); + + LOG_TRACE("[ResourceManager] Registered device list change callback. Total callbacks registered: %d", DeviceListChangeCallbacks.size()); +} + +void ResourceManager::UnregisterDeviceListChangeCallback(DeviceListChangeCallback callback, void * callback_arg) +{ + for(size_t idx = 0; idx < DeviceListChangeCallbacks.size(); idx++) + { + if(DeviceListChangeCallbacks[idx] == callback && DeviceListChangeCallbackArgs[idx] == callback_arg) + { + DeviceListChangeCallbacks.erase(DeviceListChangeCallbacks.begin() + idx); + DeviceListChangeCallbackArgs.erase(DeviceListChangeCallbackArgs.begin() + idx); + } + } + + LOG_TRACE("[ResourceManager] Unregistered device list change callback. Total callbacks registered: %d", DeviceListChangeCallbacks.size()); +} + +void ResourceManager::RegisterI2CBusListChangeCallback(I2CBusListChangeCallback new_callback, void * new_callback_arg) +{ + I2CBusListChangeCallbacks.push_back(new_callback); + I2CBusListChangeCallbackArgs.push_back(new_callback_arg); +} + +void ResourceManager::UnregisterI2CBusListChangeCallback(I2CBusListChangeCallback callback, void * callback_arg) +{ + for(size_t idx = 0; idx < I2CBusListChangeCallbacks.size(); idx++) + { + if(I2CBusListChangeCallbacks[idx] == callback && I2CBusListChangeCallbackArgs[idx] == callback_arg) + { + I2CBusListChangeCallbacks.erase(I2CBusListChangeCallbacks.begin() + idx); + I2CBusListChangeCallbackArgs.erase(I2CBusListChangeCallbackArgs.begin() + idx); + } + } +} + +void ResourceManager::RegisterDetectionProgressCallback(DetectionProgressCallback new_callback, void *new_callback_arg) +{ + DetectionProgressCallbacks.push_back(new_callback); + DetectionProgressCallbackArgs.push_back(new_callback_arg); + + LOG_TRACE("[ResourceManager] Registered detection progress callback. Total callbacks registered: %d", DetectionProgressCallbacks.size()); +} + +void ResourceManager::UnregisterDetectionProgressCallback(DetectionProgressCallback callback, void *callback_arg) +{ + for(size_t idx = 0; idx < DetectionProgressCallbacks.size(); idx++) + { + if(DetectionProgressCallbacks[idx] == callback && DetectionProgressCallbackArgs[idx] == callback_arg) + { + DetectionProgressCallbacks.erase(DetectionProgressCallbacks.begin() + idx); + DetectionProgressCallbackArgs.erase(DetectionProgressCallbackArgs.begin() + idx); + } + } + + LOG_TRACE("[ResourceManager] Unregistered detection progress callback. Total callbacks registered: %d", DetectionProgressCallbacks.size()); +} + +void ResourceManager::RegisterDetectionStartCallback(DetectionStartCallback new_callback, void *new_callback_arg) +{ + DetectionStartCallbacks.push_back(new_callback); + DetectionStartCallbackArgs.push_back(new_callback_arg); +} + +void ResourceManager::UnregisterDetectionStartCallback(DetectionStartCallback callback, void *callback_arg) +{ + for(size_t idx = 0; idx < DetectionStartCallbacks.size(); idx++) + { + if(DetectionStartCallbacks[idx] == callback && DetectionStartCallbackArgs[idx] == callback_arg) + { + DetectionStartCallbacks.erase(DetectionStartCallbacks.begin() + idx); + DetectionStartCallbackArgs.erase(DetectionStartCallbackArgs.begin() + idx); + } + } +} + +void ResourceManager::RegisterDetectionEndCallback(DetectionEndCallback new_callback, void *new_callback_arg) +{ + DetectionEndCallbacks.push_back(new_callback); + DetectionEndCallbackArgs.push_back(new_callback_arg); +} + +void ResourceManager::UnregisterDetectionEndCallback(DetectionEndCallback callback, void *callback_arg) +{ + for(size_t idx = 0; idx < DetectionEndCallbacks.size(); idx++) + { + if(DetectionEndCallbacks[idx] == callback && DetectionEndCallbackArgs[idx] == callback_arg) + { + DetectionEndCallbacks.erase(DetectionEndCallbacks.begin() + idx); + DetectionEndCallbackArgs.erase(DetectionEndCallbackArgs.begin() + idx); + } + } +} + +void ResourceManager::UpdateDeviceList() +{ + DeviceListChangeMutex.lock(); + + /*-----------------------------------------------------*\ + | Insert hardware controllers into controller list | + \*-----------------------------------------------------*/ + for(unsigned int hw_controller_idx = 0; hw_controller_idx < rgb_controllers_hw.size(); hw_controller_idx++) + { + /*-------------------------------------------------*\ + | Check if the controller is already in the list | + | at the correct index | + \*-------------------------------------------------*/ + if(hw_controller_idx < rgb_controllers.size()) + { + if(rgb_controllers[hw_controller_idx] == rgb_controllers_hw[hw_controller_idx]) + { + continue; + } + } + + /*-------------------------------------------------*\ + | If not, check if the controller is already in the | + | list at a different index | + \*-------------------------------------------------*/ + for(unsigned int controller_idx = 0; controller_idx < rgb_controllers.size(); controller_idx++) + { + if(rgb_controllers[controller_idx] == rgb_controllers_hw[hw_controller_idx]) + { + rgb_controllers.erase(rgb_controllers.begin() + controller_idx); + rgb_controllers.insert(rgb_controllers.begin() + hw_controller_idx, rgb_controllers_hw[hw_controller_idx]); + break; + } + } + + /*-------------------------------------------------*\ + | If it still hasn't been found, add it to the list | + \*-------------------------------------------------*/ + rgb_controllers.insert(rgb_controllers.begin() + hw_controller_idx, rgb_controllers_hw[hw_controller_idx]); + } + + /*-----------------------------------------------------*\ + | Device list has changed, call the callbacks | + \*-----------------------------------------------------*/ + DeviceListChanged(); + + /*-----------------------------------------------------*\ + | Device list has changed, inform all clients connected | + | to this server | + \*-----------------------------------------------------*/ + server->DeviceListChanged(); + + DeviceListChangeMutex.unlock(); +} + +void ResourceManager::ClientInfoChanged() +{ + /*-----------------------------------------------------*\ + | Client info has changed, call the callbacks | + \*-----------------------------------------------------*/ + LOG_TRACE("[ResourceManager] Calling client info change callbacks."); + + for(std::size_t callback_idx = 0; callback_idx < ClientInfoChangeCallbacks.size(); callback_idx++) + { + ResourceManager::ClientInfoChangeCallbacks[callback_idx](ClientInfoChangeCallbackArgs[callback_idx]); + } +} + +void ResourceManager::DeviceListChanged() +{ + /*-----------------------------------------------------*\ + | Device list has changed, call the callbacks | + \*-----------------------------------------------------*/ + LOG_TRACE("[ResourceManager] Calling device list change callbacks."); + + for(std::size_t callback_idx = 0; callback_idx < DeviceListChangeCallbacks.size(); callback_idx++) + { + ResourceManager::DeviceListChangeCallbacks[callback_idx](DeviceListChangeCallbackArgs[callback_idx]); + } +} + +void ResourceManager::DetectionProgressChanged() +{ + DetectionProgressMutex.lock(); + + /*-----------------------------------------------------*\ + | Detection progress has changed, call the callbacks | + \*-----------------------------------------------------*/ + LOG_TRACE("[ResourceManager] Calling detection progress callbacks."); + + for(std::size_t callback_idx = 0; callback_idx < (unsigned int)DetectionProgressCallbacks.size(); callback_idx++) + { + DetectionProgressCallbacks[callback_idx](DetectionProgressCallbackArgs[callback_idx]); + } + + DetectionProgressMutex.unlock(); +} + +void ResourceManager::I2CBusListChanged() +{ + I2CBusListChangeMutex.lock(); + + /*-----------------------------------------------------*\ + | Detection progress has changed, call the callbacks | + \*-----------------------------------------------------*/ + for(std::size_t callback_idx = 0; callback_idx < (unsigned int)I2CBusListChangeCallbacks.size(); callback_idx++) + { + I2CBusListChangeCallbacks[callback_idx](I2CBusListChangeCallbackArgs[callback_idx]); + } + + I2CBusListChangeMutex.unlock(); +} + +void ResourceManager::SetupConfigurationDirectory() +{ + config_dir.clear(); +#ifdef _WIN32 + const wchar_t* appdata = _wgetenv(L"APPDATA"); + if(appdata != NULL) + { + config_dir = appdata; + } +#else + const char* xdg_config_home = getenv("XDG_CONFIG_HOME"); + const char* home = getenv("HOME"); + /*-----------------------------------------------------*\ + | Check both XDG_CONFIG_HOME and APPDATA environment | + | variables. If neither exist, use current directory | + \*-----------------------------------------------------*/ + if(xdg_config_home != NULL) + { + config_dir = xdg_config_home; + } + else if(home != NULL) + { + config_dir = home; + config_dir /= ".config"; + } +#endif + + + /*-----------------------------------------------------*\ + | If a configuration directory was found, append OpenRGB| + \*-----------------------------------------------------*/ + if(config_dir != "") + { + config_dir.append("OpenRGB"); + + /*-------------------------------------------------*\ + | Create OpenRGB configuration directory if it | + | doesn't exist | + \*-------------------------------------------------*/ + filesystem::create_directories(config_dir); + } + else + { + config_dir = "./"; + } +} + +filesystem::path ResourceManager::GetConfigurationDirectory() +{ + return(config_dir); +} + +void ResourceManager::SetConfigurationDirectory(const filesystem::path &directory) +{ + config_dir = directory; + settings_manager->LoadSettings(directory / "OpenRGB.json"); + profile_manager->SetConfigurationDirectory(directory); + + rgb_controllers_sizes.clear(); + rgb_controllers_sizes = profile_manager->LoadProfileToList("sizes", true); +} + +NetworkServer* ResourceManager::GetServer() +{ + return(server); +} + +static void NetworkClientInfoChangeCallback(void* this_ptr) +{ + ResourceManager* this_obj = (ResourceManager*)this_ptr; + + this_obj->ClientInfoChanged(); + this_obj->DeviceListChanged(); +} + +void ResourceManager::RegisterNetworkClient(NetworkClient* new_client) +{ + new_client->RegisterClientInfoChangeCallback(NetworkClientInfoChangeCallback, this); + + clients.push_back(new_client); +} + +void ResourceManager::UnregisterNetworkClient(NetworkClient* network_client) +{ + /*-----------------------------------------------------*\ + | Stop the disconnecting client | + \*-----------------------------------------------------*/ + network_client->StopClient(); + + /*-----------------------------------------------------*\ + | Clear callbacks from the client before removal | + \*-----------------------------------------------------*/ + network_client->ClearCallbacks(); + + /*-----------------------------------------------------*\ + | Find the client to remove and remove it from the | + | clients list | + \*-----------------------------------------------------*/ + std::vector::iterator client_it = std::find(clients.begin(), clients.end(), network_client); + + if(client_it != clients.end()) + { + clients.erase(client_it); + } + + /*-----------------------------------------------------*\ + | Delete the client | + \*-----------------------------------------------------*/ + delete network_client; + + UpdateDeviceList(); +} + + +/******************************************************************************************\ +* * +* AttemptLocalConnection * +* * +* Attempts an SDK connection to the local server. Returns true if success * +* * +\******************************************************************************************/ + +bool ResourceManager::AttemptLocalConnection() +{ + detection_percent = 0; + detection_string = "Attempting local server connection..."; + DetectionProgressChanged(); + + LOG_DEBUG("[ResourceManager] Attempting local server connection..."); + + bool success = false; + + auto_connection_client = new NetworkClient(ResourceManager::get()->GetRGBControllers()); + + std::string titleString = "OpenRGB "; + titleString.append(VERSION_STRING); + + auto_connection_client->SetName(titleString.c_str()); + auto_connection_client->StartClient(); + + for(int timeout = 0; timeout < 10; timeout++) + { + if(auto_connection_client->GetConnected()) + { + break; + } + std::this_thread::sleep_for(5ms); + } + + if(!auto_connection_client->GetConnected()) + { + LOG_TRACE("[ResourceManager] Client failed to connect"); + auto_connection_client->StopClient(); + LOG_TRACE("[ResourceManager] Client stopped"); + + delete auto_connection_client; + + auto_connection_client = NULL; + } + else + { + ResourceManager::get()->RegisterNetworkClient(auto_connection_client); + LOG_TRACE("[ResourceManager] Registered network client"); + + success = true; + + /*-------------------------------------------------*\ + | Wait up to 5 seconds for the client connection to | + | retrieve all controllers | + \*-------------------------------------------------*/ + for(int timeout = 0; timeout < 1000; timeout++) + { + if(auto_connection_client->GetOnline()) + { + break; + } + std::this_thread::sleep_for(5ms); + } + } + + return success; +} + +std::vector& ResourceManager::GetClients() +{ + return(clients); +} + +ProfileManager* ResourceManager::GetProfileManager() +{ + return(profile_manager); +} + +SettingsManager* ResourceManager::GetSettingsManager() +{ + return(settings_manager); +} + +bool ResourceManager::GetDetectionEnabled() +{ + return(detection_enabled); +} + +unsigned int ResourceManager::GetDetectionPercent() +{ + return (detection_percent.load()); +} + +const char *ResourceManager::GetDetectionString() +{ + return (detection_string); +} + +void ResourceManager::Cleanup() +{ + ResourceManager::get()->WaitForDeviceDetection(); + + std::vector rgb_controllers_hw_copy = rgb_controllers_hw; + + for(std::size_t hw_controller_idx = 0; hw_controller_idx < rgb_controllers_hw.size(); hw_controller_idx++) + { + for(std::size_t controller_idx = 0; controller_idx < rgb_controllers.size(); controller_idx++) + { + if(rgb_controllers[controller_idx] == rgb_controllers_hw[hw_controller_idx]) + { + rgb_controllers.erase(rgb_controllers.begin() + controller_idx); + break; + } + } + } + + /*-----------------------------------------------------*\ + | Clear the hardware controllers list and set the | + | previous hardware controllers list size to zero | + \*-----------------------------------------------------*/ + rgb_controllers_hw.clear(); + detection_prev_size = 0; + + for(RGBController* rgb_controller : rgb_controllers_hw_copy) + { + delete rgb_controller; + } + + std::vector busses_copy = busses; + + busses.clear(); + + for(i2c_smbus_interface* bus : busses_copy) + { + delete bus; + } + + RunInBackgroundThread(std::bind(&ResourceManager::HidExitCoroutine, this)); +} + +void ResourceManager::ProcessPreDetectionHooks() +{ + for(std::size_t hook_idx = 0; hook_idx < pre_detection_hooks.size(); hook_idx++) + { + pre_detection_hooks[hook_idx](); + } +} + +void ResourceManager::ProcessDynamicDetectors() +{ + for(std::size_t detector_idx = 0; detector_idx < dynamic_detectors.size(); detector_idx++) + { + dynamic_detectors[detector_idx](); + } + + dynamic_detectors_processed = true; +} + +/*---------------------------------------------------------*\ +| Handle ALL pre-detection routines | +| The system should be ready to start a detection thread | +| (returns false if detection can not proceed) | +\*---------------------------------------------------------*/ +bool ResourceManager::ProcessPreDetection() +{ + /*-----------------------------------------------------*\ + | Process pre-detection hooks | + \*-----------------------------------------------------*/ + ProcessPreDetectionHooks(); + + /*-----------------------------------------------------*\ + | Process Dynamic Detectors | + \*-----------------------------------------------------*/ + if(!dynamic_detectors_processed) + { + ProcessDynamicDetectors(); + } + + /*-----------------------------------------------------*\ + | Call detection start callbacks | + \*-----------------------------------------------------*/ + LOG_TRACE("[ResourceManager] Calling detection start callbacks."); + + for(std::size_t callback_idx = 0; callback_idx < DetectionStartCallbacks.size(); callback_idx++) + { + DetectionStartCallbacks[callback_idx](DetectionStartCallbackArgs[callback_idx]); + } + + /*-----------------------------------------------------*\ + | Update the detector settings | + \*-----------------------------------------------------*/ + UpdateDetectorSettings(); + if(detection_enabled) + { + /*-------------------------------------------------*\ + | Do nothing is it is already detecting devices | + \*-------------------------------------------------*/ + if(detection_is_required.load()) + { + return false; + } + + /*-------------------------------------------------*\ + | If there's anything left from the last time, | + | we shall remove it first | + \*-------------------------------------------------*/ + detection_percent = 0; + detection_string = ""; + + DetectionProgressChanged(); + + Cleanup(); + + UpdateDeviceList(); + + /*-------------------------------------------------*\ + | Initialize HID interface for detection | + \*-------------------------------------------------*/ + int hid_status = hid_init(); + + LOG_INFO("[ResourceManager] Initializing HID interfaces: %s", ((hid_status == 0) ? "Success" : "Failed")); + + /*-------------------------------------------------*\ + | Mark the detection as ongoing | + | So the detection thread may proceed | + \*-------------------------------------------------*/ + detection_is_required = true; + + return true; + } + return false; +} + +void ResourceManager::DetectDevices() +{ + if(ProcessPreDetection()) + { + // Run the detection coroutine + RunInBackgroundThread(std::bind(&ResourceManager::DetectDevicesCoroutine, this)); + } + + if(!detection_enabled) + { + ProcessPostDetection(); + } +} + +void ResourceManager::RescanDevices() +{ + /*-----------------------------------------------------*\ + | If automatic local connection is active, the primary | + | instance is the local server, so send rescan requests | + | to the automatic local connection client | + \*-----------------------------------------------------*/ + if(auto_connection_active && auto_connection_client != NULL) + { + auto_connection_client->SendRequest_RescanDevices(); + } + + /*-----------------------------------------------------*\ + | If detection is disabled and there is exactly one | + | client, the primary instance is the connected server, | + | so send rescan requests to the first (and only) | + | client | + \*-----------------------------------------------------*/ + else if(!detection_enabled && clients.size() == 1) + { + clients[0]->SendRequest_RescanDevices(); + } + + /*-----------------------------------------------------*\ + | Perform local rescan | + \*-----------------------------------------------------*/ + DetectDevices(); +} + +void ResourceManager::ProcessPostDetection() +{ + /*-----------------------------------------------------*\ + | Signal that detection is complete | + \*-----------------------------------------------------*/ + detection_percent = 100; + DetectionProgressChanged(); + + LOG_INFO("[ResourceManager] Calling Post-detection callbacks"); + /*-----------------------------------------------------*\ + | Call detection end callbacks | + \*-----------------------------------------------------*/ + for(std::size_t callback_idx = 0; callback_idx < DetectionEndCallbacks.size(); callback_idx++) + { + DetectionEndCallbacks[callback_idx](DetectionEndCallbackArgs[callback_idx]); + } + + detection_is_required = false; + + LOG_INFO("------------------------------------------------------"); + LOG_INFO("| Detection completed |"); + LOG_INFO("------------------------------------------------------"); +} + +void ResourceManager::DisableDetection() +{ + detection_enabled = false; +} + +void ResourceManager::DetectDevicesCoroutine() +{ + DetectDeviceMutex.lock(); + + hid_device_info* current_hid_device; + float percent = 0.0f; + float percent_denominator = 0.0f; + json detector_settings; + unsigned int hid_device_count = 0; + hid_device_info* hid_devices = NULL; + bool hid_safe_mode = false; + unsigned int initial_detection_delay_ms = 0; + + LOG_INFO("------------------------------------------------------"); + LOG_INFO("| Start device detection |"); + LOG_INFO("------------------------------------------------------"); + + /*-----------------------------------------------------*\ + | Open device disable list and read in disabled | + | device strings | + \*-----------------------------------------------------*/ + detector_settings = settings_manager->GetSettings("Detectors"); + + /*-----------------------------------------------------*\ + | Check HID safe mode setting | + \*-----------------------------------------------------*/ + if(detector_settings.contains("hid_safe_mode")) + { + hid_safe_mode = detector_settings["hid_safe_mode"]; + } + + /*-----------------------------------------------------*\ + | Check initial detection delay setting | + \*-----------------------------------------------------*/ + if(detector_settings.contains("initial_detection_delay_ms")) + { + initial_detection_delay_ms = detector_settings["initial_detection_delay_ms"]; + } + + /*-----------------------------------------------------*\ + | If configured, delay detection for the configured | + | time only on first detection | + \*-----------------------------------------------------*/ + if(initial_detection) + { + if(initial_detection_delay_ms != 0) + { + LOG_INFO("[ResourceManager] Delaying detection for %d ms", initial_detection_delay_ms); + std::this_thread::sleep_for(initial_detection_delay_ms * 1ms); + } + + initial_detection = false; + } + + /*-----------------------------------------------------*\ + | Reset the size entry used flags vector | + \*-----------------------------------------------------*/ + detection_size_entry_used.resize(rgb_controllers_sizes.size()); + + for(std::size_t size_idx = 0; size_idx < (unsigned int)detection_size_entry_used.size(); size_idx++) + { + detection_size_entry_used[size_idx] = false; + } + + /*-----------------------------------------------------*\ + | Calculate the percentage denominator by adding the | + | number of I2C and miscellaneous detectors and the | + | number of enumerated HID devices | + | | + | Start by iterating through all HID devices in list to | + | get a total count | + \*-----------------------------------------------------*/ + if(!hid_safe_mode) + { + hid_devices = hid_enumerate(0, 0); + } + + current_hid_device = hid_devices; + + while(current_hid_device) + { + hid_device_count++; + + current_hid_device = current_hid_device->next; + } + + percent_denominator = (float)(i2c_device_detectors.size() + i2c_dimm_device_detectors.size() + i2c_pci_device_detectors.size() + device_detectors.size()) + (float)hid_device_count; + + /*-----------------------------------------------------*\ + | Start at 0% detection progress | + \*-----------------------------------------------------*/ + detection_percent = 0; + +#ifdef __linux__ + /*-----------------------------------------------------*\ + | Check if the udev rules exist | + \*-----------------------------------------------------*/ + bool udev_not_exist = false; + bool udev_multiple = false; + + if(access("/etc/udev/rules.d/60-openrgb.rules", F_OK) != 0) + { + if(access("/usr/lib/udev/rules.d/60-openrgb.rules", F_OK) != 0) + { + udev_not_exist = true; + } + } + else + { + if(access("/usr/lib/udev/rules.d/60-openrgb.rules", F_OK) == 0) + { + udev_multiple = true; + } + } +#endif + + /*-----------------------------------------------------*\ + | Detect i2c interfaces | + \*-----------------------------------------------------*/ + LOG_INFO("------------------------------------------------------"); + LOG_INFO("| Detecting I2C interfaces |"); + LOG_INFO("------------------------------------------------------"); + + bool i2c_interface_fail = false; + + for(unsigned int i2c_bus_detector_idx = 0; i2c_bus_detector_idx < (unsigned int)i2c_bus_detectors.size() && detection_is_required.load(); i2c_bus_detector_idx++) + { + if(i2c_bus_detectors[i2c_bus_detector_idx]() == false) + { + i2c_interface_fail = true; + } + + I2CBusListChanged(); + } + + /*-----------------------------------------------------*\ + | Detect i2c devices | + \*-----------------------------------------------------*/ + LOG_INFO("------------------------------------------------------"); + LOG_INFO("| Detecting I2C devices |"); + LOG_INFO("------------------------------------------------------"); + for(unsigned int i2c_detector_idx = 0; i2c_detector_idx < (unsigned int)i2c_device_detectors.size() && detection_is_required.load(); i2c_detector_idx++) + { + std::size_t controller_size = rgb_controllers_hw.size(); + detection_string = i2c_device_detector_strings[i2c_detector_idx].c_str(); + + /*-------------------------------------------------*\ + | Check if this detector is enabled | + \*-------------------------------------------------*/ + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + if(this_device_enabled) + { + DetectionProgressChanged(); + + i2c_device_detectors[i2c_detector_idx](busses); + } + + /*-------------------------------------------------*\ + | If the device list size has changed, call the | + | device list changed callbacks | + \*-------------------------------------------------*/ + if(rgb_controllers_hw.size() == controller_size) + { + LOG_DEBUG("[%s] no devices found", detection_string); + } + + LOG_TRACE("[%s] detection end", detection_string); + + /*-------------------------------------------------*\ + | Update detection percent | + \*-------------------------------------------------*/ + percent = ((float)i2c_detector_idx + 1.0f) / percent_denominator; + + detection_percent = (unsigned int)(percent * 100.0f); + } + + /*-----------------------------------------------------*\ + | Detect i2c DIMM modules | + \*-----------------------------------------------------*/ + LOG_INFO("------------------------------------------------------"); + LOG_INFO("| Detecting I2C DIMM modules |"); + LOG_INFO("------------------------------------------------------"); + + detection_string = "Reading DRAM SPD Information"; + DetectionProgressChanged(); + + for(unsigned int bus = 0; bus < busses.size() && IsAnyDimmDetectorEnabled(detector_settings); bus++) + { + IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + std::vector slots; + SPDMemoryType dimm_type = SPD_RESERVED; + + for(uint8_t spd_addr = 0x50; spd_addr < 0x58; spd_addr++) + { + SPDDetector spd(busses[bus], spd_addr, dimm_type); + if(spd.is_valid()) + { + SPDWrapper accessor(spd); + dimm_type = spd.memory_type(); + LOG_INFO("[ResourceManager] Detected occupied slot %d, bus %d, type %s", spd_addr - 0x50 + 1, bus, spd_memory_type_name[dimm_type]); + LOG_DEBUG("[ResourceManager] Jedec ID: 0x%04x", accessor.jedec_id()); + slots.push_back(accessor); + } + } + + for(unsigned int i2c_detector_idx = 0; i2c_detector_idx < i2c_dimm_device_detectors.size() && detection_is_required.load(); i2c_detector_idx++) + { + if((i2c_dimm_device_detectors[i2c_detector_idx].dimm_type == dimm_type) && is_jedec_in_slots(slots, i2c_dimm_device_detectors[i2c_detector_idx].jedec_id)) + { + detection_string = i2c_dimm_device_detectors[i2c_detector_idx].name.c_str(); + + /*-------------------------------------*\ + | Check if this detector is enabled | + \*-------------------------------------*/ + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + if(this_device_enabled) + { + DetectionProgressChanged(); + + std::vector matching_slots = slots_with_jedec(slots, i2c_dimm_device_detectors[i2c_detector_idx].jedec_id); + i2c_dimm_device_detectors[i2c_detector_idx].function(busses[bus], matching_slots, i2c_dimm_device_detectors[i2c_detector_idx].name); + } + + LOG_TRACE("[%s] detection end", detection_string); + } + + /*-----------------------------------------*\ + | Update detection percent | + \*-----------------------------------------*/ + percent = (i2c_device_detectors.size() + i2c_detector_idx + 1.0f) / percent_denominator; + + detection_percent = (unsigned int)(percent * 100.0f); + } + } + } + + /*-----------------------------------------------------*\ + | Detect i2c PCI devices | + \*-----------------------------------------------------*/ + LOG_INFO("------------------------------------------------------"); + LOG_INFO("| Detecting I2C PCI devices |"); + LOG_INFO("------------------------------------------------------"); + for(unsigned int i2c_detector_idx = 0; i2c_detector_idx < (unsigned int)i2c_pci_device_detectors.size() && detection_is_required.load(); i2c_detector_idx++) + { + detection_string = i2c_pci_device_detectors[i2c_detector_idx].name.c_str(); + + /*-------------------------------------------------*\ + | Check if this detector is enabled | + \*-------------------------------------------------*/ + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + if(this_device_enabled) + { + DetectionProgressChanged(); + + for(unsigned int bus = 0; bus < busses.size(); bus++) + { + if(busses[bus]->pci_vendor == i2c_pci_device_detectors[i2c_detector_idx].ven_id && + busses[bus]->pci_device == i2c_pci_device_detectors[i2c_detector_idx].dev_id && + busses[bus]->pci_subsystem_vendor == i2c_pci_device_detectors[i2c_detector_idx].subven_id && + busses[bus]->pci_subsystem_device == i2c_pci_device_detectors[i2c_detector_idx].subdev_id) + { + i2c_pci_device_detectors[i2c_detector_idx].function(busses[bus], i2c_pci_device_detectors[i2c_detector_idx].i2c_addr, i2c_pci_device_detectors[i2c_detector_idx].name); + } + } + } + + LOG_TRACE("[%s] detection end", detection_string); + + /*-------------------------------------------------*\ + | Update detection percent | + \*-------------------------------------------------*/ + percent = (i2c_device_detectors.size() + i2c_dimm_device_detectors.size() + i2c_detector_idx + 1.0f) / percent_denominator; + + detection_percent = (unsigned int)(percent * 100.0f); + } + + /*-----------------------------------------------------*\ + | Detect HID devices | + | | + | Reset current device pointer to first device | + \*-----------------------------------------------------*/ + LOG_INFO("------------------------------------------------------"); + LOG_INFO("| Detecting HID devices |"); + if (hid_safe_mode) + LOG_INFO("| with safe mode |"); + LOG_INFO("------------------------------------------------------"); + current_hid_device = hid_devices; + + if(hid_safe_mode) + { + /*-------------------------------------------------*\ + | Loop through all available detectors. If all | + | required information matches, run the detector | + \*-------------------------------------------------*/ + for(unsigned int hid_detector_idx = 0; hid_detector_idx < (unsigned int)hid_device_detectors.size() && detection_is_required.load(); hid_detector_idx++) + { + HIDDeviceDetectorBlock & detector = hid_device_detectors[hid_detector_idx]; + hid_devices = hid_enumerate(detector.vid, detector.pid); + + LOG_VERBOSE("[ResourceManager] Trying to run detector for [%s] (for %04x:%04x)", detector.name.c_str(), detector.vid, detector.pid); + + current_hid_device = hid_devices; + + bool detector_has_match = false; + while(current_hid_device) + { + if(detector.compare(current_hid_device)) + { + detector_has_match = true; + detection_string = detector.name.c_str(); + + /*-------------------------------------*\ + | Check if this detector is enabled or | + | needs to be added to the settings list| + \*-------------------------------------*/ + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + + if(this_device_enabled) + { + DetectionProgressChanged(); + + detector.function(current_hid_device, hid_device_detectors[hid_detector_idx].name); + + LOG_TRACE("[%s] detection end", detection_string); + } + } + + current_hid_device = current_hid_device->next; + } + + if(!detector_has_match) + { + current_hid_device = hid_devices; + while(current_hid_device) + { + if(detector.compare_no_interface(current_hid_device)) + { + detection_string = detector.name.c_str(); + + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s (fallback match)", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + + if(this_device_enabled) + { + DetectionProgressChanged(); + + detector.function(current_hid_device, hid_device_detectors[hid_detector_idx].name); + } + break; + } + current_hid_device = current_hid_device->next; + } + } + + hid_free_enumeration(hid_devices); + } + } + else + { + /*-------------------------------------------------*\ + | Iterate through all devices in list and run | + | detectors | + \*-------------------------------------------------*/ + hid_device_count = 0; + + while(current_hid_device) + { + if(LogManager::get()->getLoglevel() >= LL_DEBUG) + { + const char* manu_name = StringUtils::wchar_to_char(current_hid_device->manufacturer_string); + const char* prod_name = StringUtils::wchar_to_char(current_hid_device->product_string); + LOG_DEBUG("[%04X:%04X U=%04X P=0x%04X I=%d] %-25s - %s", current_hid_device->vendor_id, current_hid_device->product_id, current_hid_device->usage, current_hid_device->usage_page, current_hid_device->interface_number, manu_name, prod_name); + } + detection_string = ""; + DetectionProgressChanged(); + bool this_device_matched = false; + std::vector loose_matched_hid_detectors; + std::vector loose_matched_wrapped_detectors; + + /*---------------------------------------------*\ + | Loop through all available detectors. If all | + | required information matches, run the detector| + \*---------------------------------------------*/ + for(unsigned int hid_detector_idx = 0; hid_detector_idx < (unsigned int)hid_device_detectors.size() && detection_is_required.load(); hid_detector_idx++) + { + HIDDeviceDetectorBlock & detector = hid_device_detectors[hid_detector_idx]; + if(detector.compare(current_hid_device)) + { + detection_string = detector.name.c_str(); + this_device_matched = true; + + /*-------------------------------------*\ + | Check if this detector is enabled or | + | needs to be added to the settings list| + \*-------------------------------------*/ + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + + if(this_device_enabled) + { + DetectionProgressChanged(); + + detector.function(current_hid_device, hid_device_detectors[hid_detector_idx].name); + } + } + else if(detector.compare_no_interface(current_hid_device)) + { + loose_matched_hid_detectors.push_back(hid_detector_idx); + } + } + + /*---------------------------------------------*\ + | Loop through all available wrapped HID | + | detectors. If all required information | + | matches, run the detector | + \*---------------------------------------------*/ + for(unsigned int hid_detector_idx = 0; hid_detector_idx < (unsigned int)hid_wrapped_device_detectors.size() && detection_is_required.load(); hid_detector_idx++) + { + HIDWrappedDeviceDetectorBlock & detector = hid_wrapped_device_detectors[hid_detector_idx]; + if(detector.compare(current_hid_device)) + { + detection_string = detector.name.c_str(); + this_device_matched = true; + + /*-------------------------------------*\ + | Check if this detector is enabled or | + | needs to be added to the settings list| + \*-------------------------------------*/ + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + + if(this_device_enabled) + { + DetectionProgressChanged(); + + detector.function(default_wrapper, current_hid_device, hid_wrapped_device_detectors[hid_detector_idx].name); + } + } + else if(detector.compare_no_interface(current_hid_device)) + { + loose_matched_wrapped_detectors.push_back(hid_detector_idx); + } + } + + if(!this_device_matched) + { + if((loose_matched_hid_detectors.size() + loose_matched_wrapped_detectors.size()) == 1) + { + if(loose_matched_hid_detectors.size() == 1) + { + HIDDeviceDetectorBlock & detector = hid_device_detectors[loose_matched_hid_detectors.front()]; + detection_string = detector.name.c_str(); + + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s (fallback match)", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + if(this_device_enabled) + { + DetectionProgressChanged(); + detector.function(current_hid_device, detector.name); + } + } + else + { + HIDWrappedDeviceDetectorBlock & detector = hid_wrapped_device_detectors[loose_matched_wrapped_detectors.front()]; + detection_string = detector.name.c_str(); + + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s (fallback match)", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + if(this_device_enabled) + { + DetectionProgressChanged(); + detector.function(default_wrapper, current_hid_device, detector.name); + } + } + } + else if((loose_matched_hid_detectors.size() + loose_matched_wrapped_detectors.size()) > 1) + { + LOG_DEBUG("[ResourceManager] HID device [%04X:%04X] matched multiple fallback detector signatures; skipping automatic fallback registration to avoid ambiguous matching.", current_hid_device->vendor_id, current_hid_device->product_id); + } + } + + /*---------------------------------------------*\ + | Update detection percent | + \*---------------------------------------------*/ + hid_device_count++; + + percent = (i2c_device_detectors.size() + i2c_dimm_device_detectors.size() + i2c_pci_device_detectors.size() + hid_device_count) / percent_denominator; + + detection_percent = (unsigned int)(percent * 100.0f); + + /*---------------------------------------------*\ + | Move on to the next HID device | + \*---------------------------------------------*/ + current_hid_device = current_hid_device->next; + } + + /*-------------------------------------------------*\ + | Done using the device list, free it | + \*-------------------------------------------------*/ + hid_free_enumeration(hid_devices); + } + + /*-----------------------------------------------------*\ + | Detect HID devices | + | | + | Reset current device pointer to first device | + \*-----------------------------------------------------*/ +#ifdef __linux__ +#ifdef __GLIBC__ + LOG_INFO("------------------------------------------------------"); + LOG_INFO("| Detecting libusb HID devices |"); + LOG_INFO("------------------------------------------------------"); + + void * dyn_handle = NULL; + hidapi_wrapper wrapper; + + /*-----------------------------------------------------*\ + | Load the libhidapi-libusb library | + \*-----------------------------------------------------*/ +#ifdef __GLIBC__ + if((dyn_handle = dlopen("libhidapi-libusb.so", RTLD_NOW | RTLD_NODELETE | RTLD_DEEPBIND))) +#else + if(dyn_handle = dlopen("libhidapi-libusb.so", RTLD_NOW | RTLD_NODELETE )) +#endif + { + /*-------------------------------------------------*\ + | Create a wrapper with the libusb functions | + \*-------------------------------------------------*/ + wrapper = + { + .dyn_handle = dyn_handle, + .hid_send_feature_report = (hidapi_wrapper_send_feature_report) dlsym(dyn_handle,"hid_send_feature_report"), + .hid_get_feature_report = (hidapi_wrapper_get_feature_report) dlsym(dyn_handle,"hid_get_feature_report"), + .hid_get_serial_number_string = (hidapi_wrapper_get_serial_number_string) dlsym(dyn_handle,"hid_get_serial_number_string"), + .hid_open_path = (hidapi_wrapper_open_path) dlsym(dyn_handle,"hid_open_path"), + .hid_enumerate = (hidapi_wrapper_enumerate) dlsym(dyn_handle,"hid_enumerate"), + .hid_free_enumeration = (hidapi_wrapper_free_enumeration) dlsym(dyn_handle,"hid_free_enumeration"), + .hid_close = (hidapi_wrapper_close) dlsym(dyn_handle,"hid_close"), + .hid_error = (hidapi_wrapper_error) dlsym(dyn_handle,"hid_free_enumeration") + }; + + hid_devices = wrapper.hid_enumerate(0, 0); + + current_hid_device = hid_devices; + + /*-------------------------------------------------*\ + | Iterate through all devices in list and run | + | detectors | + \*-------------------------------------------------*/ + hid_device_count = 0; + + while(current_hid_device) + { + if(LogManager::get()->getLoglevel() >= LL_DEBUG) + { + const char* manu_name = StringUtils::wchar_to_char(current_hid_device->manufacturer_string); + const char* prod_name = StringUtils::wchar_to_char(current_hid_device->product_string); + LOG_DEBUG("[%04X:%04X U=%04X P=0x%04X I=%d] %-25s - %s", current_hid_device->vendor_id, current_hid_device->product_id, current_hid_device->usage, current_hid_device->usage_page, current_hid_device->interface_number, manu_name, prod_name); + } + detection_string = ""; + DetectionProgressChanged(); + bool this_device_matched = false; + std::vector loose_matched_wrapped_detectors; + + /*---------------------------------------------*\ + | Loop through all available wrapped HID | + | detectors. If all required information | + | matches, run the detector | + \*---------------------------------------------*/ + for(unsigned int hid_detector_idx = 0; hid_detector_idx < (unsigned int)hid_wrapped_device_detectors.size() && detection_is_required.load(); hid_detector_idx++) + { + HIDWrappedDeviceDetectorBlock & detector = hid_wrapped_device_detectors[hid_detector_idx]; + if(detector.compare(current_hid_device)) + { + this_device_matched = true; + detection_string = detector.name.c_str(); + + /*-------------------------------------*\ + | Check if this detector is enabled or | + | needs to be added to the settings list| + \*-------------------------------------*/ + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + + if(this_device_enabled) + { + DetectionProgressChanged(); + + detector.function(wrapper, current_hid_device, detector.name); + } + } + else if(detector.compare_no_interface(current_hid_device)) + { + loose_matched_wrapped_detectors.push_back(hid_detector_idx); + } + } + + if((!this_device_matched) && (loose_matched_wrapped_detectors.size() == 1)) + { + HIDWrappedDeviceDetectorBlock & detector = hid_wrapped_device_detectors[loose_matched_wrapped_detectors.front()]; + detection_string = detector.name.c_str(); + + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s (fallback match)", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + if(this_device_enabled) + { + DetectionProgressChanged(); + detector.function(wrapper, current_hid_device, detector.name); + } + } + + /*---------------------------------------------*\ + | Update detection percent | + \*---------------------------------------------*/ + hid_device_count++; + + percent = (i2c_device_detectors.size() + i2c_dimm_device_detectors.size() + i2c_pci_device_detectors.size() + hid_device_count) / percent_denominator; + + detection_percent = percent * 100.0f; + + /*---------------------------------------------*\ + | Move on to the next HID device | + \*---------------------------------------------*/ + current_hid_device = current_hid_device->next; + } + + /*-------------------------------------------------*\ + | Done using the device list, free it | + \*-------------------------------------------------*/ + wrapper.hid_free_enumeration(hid_devices); + } +#endif +#endif + + /*-----------------------------------------------------*\ + | Detect other devices | + \*-----------------------------------------------------*/ + LOG_INFO("------------------------------------------------------"); + LOG_INFO("| Detecting other devices |"); + LOG_INFO("------------------------------------------------------"); + + for(unsigned int detector_idx = 0; detector_idx < (unsigned int)device_detectors.size() && detection_is_required.load(); detector_idx++) + { + detection_string = device_detector_strings[detector_idx].c_str(); + + /*-------------------------------------------------*\ + | Check if this detector is enabled | + \*-------------------------------------------------*/ + bool this_device_enabled = true; + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) + { + this_device_enabled = detector_settings["detectors"][detection_string]; + } + + LOG_DEBUG("[%s] is %s", detection_string, ((this_device_enabled == true) ? "enabled" : "disabled")); + + if(this_device_enabled) + { + DetectionProgressChanged(); + + device_detectors[detector_idx](); + } + + LOG_TRACE("[%s] detection end", detection_string); + + /*-------------------------------------------------*\ + | Update detection percent | + \*-------------------------------------------------*/ + percent = (i2c_device_detectors.size() + hid_device_count + detector_idx + 1.0f) / percent_denominator; + + detection_percent = (unsigned int)(percent * 100.0f); + } + + /*-----------------------------------------------------*\ + | Make sure that when the detection is done, progress | + | bar is set to 100% | + \*-----------------------------------------------------*/ + ProcessPostDetection(); + + DetectDeviceMutex.unlock(); + +#ifdef __linux__ + /*-----------------------------------------------------*\ + | If the udev rules file is not installed, show a dialog| + \*-----------------------------------------------------*/ + if(udev_not_exist) + { + LOG_DIALOG("%s", UDEV_MISSING); + + udev_multiple = false; + i2c_interface_fail = false; + } + + /*-----------------------------------------------------*\ + | If multiple udev rules files are installed, show a | + | dialog | + \*-----------------------------------------------------*/ + if(udev_multiple) + { + LOG_DIALOG("%s", UDEV_MUTLI); + + i2c_interface_fail = false; + } + +#endif + + /*-----------------------------------------------------*\ + | If any i2c interfaces failed to detect due to an | + | error condition, show a dialog | + \*-----------------------------------------------------*/ + if(i2c_interface_fail) + { +#ifdef _WIN32 + LOG_DIALOG("%s", I2C_ERR_WIN); +#endif +#ifdef __linux__ + LOG_DIALOG("%s", I2C_ERR_LINUX); +#endif + } +} + +void ResourceManager::StopDeviceDetection() +{ + LOG_INFO("[ResourceManager] Detection abort requested"); + detection_is_required = false; + detection_percent = 100; + detection_string = "Stopping"; +} + +void ResourceManager::Initialize(bool tryConnect, bool detectDevices, bool startServer, bool applyPostOptions) +{ + /*-----------------------------------------------------*\ + | Cache the parameters | + | TODO: Possibly cache them in the CLI file somewhere | + \*-----------------------------------------------------*/ + tryAutoConnect = tryConnect; + detection_enabled = detectDevices; + start_server = startServer; + apply_post_options = applyPostOptions; + + RunInBackgroundThread(std::bind(&ResourceManager::InitCoroutine, this)); +} + +void ResourceManager::InitCoroutine() +{ + /*-----------------------------------------------------*\ + | If enabled, try connecting to local server instead of | + | detecting devices from this instance of OpenRGB | + \*-----------------------------------------------------*/ + if(tryAutoConnect) + { + detection_percent = 0; + detection_string = "Attempting server connection..."; + DetectionProgressChanged(); + + /*-------------------------------------------------*\ + | Attempt connection to local server | + \*-------------------------------------------------*/ + if(AttemptLocalConnection()) + { + LOG_DEBUG("[ResourceManager] Local OpenRGB server connected, running in client mode"); + + /*---------------------------------------------*\ + | Set auto connection active flag and disable | + | detection if the local server was connected | + \*---------------------------------------------*/ + auto_connection_active = true; + DisableDetection(); + } + + tryAutoConnect = false; + } + + /*-----------------------------------------------------*\ + | Initialize Saved Client Connections | + \*-----------------------------------------------------*/ + json client_settings = settings_manager->GetSettings("Client"); + + if(client_settings.contains("clients")) + { + for(unsigned int client_idx = 0; client_idx < client_settings["clients"].size(); client_idx++) + { + NetworkClient * client = new NetworkClient(rgb_controllers); + + std::string titleString = "OpenRGB "; + titleString.append(VERSION_STRING); + + std::string client_ip = client_settings["clients"][client_idx]["ip"]; + unsigned short client_port = client_settings["clients"][client_idx]["port"]; + + client->SetIP(client_ip.c_str()); + client->SetName(titleString.c_str()); + client->SetPort(client_port); + + client->StartClient(); + + for(int timeout = 0; timeout < 100; timeout++) + { + if(client->GetConnected()) + { + break; + } + std::this_thread::sleep_for(10ms); + } + + RegisterNetworkClient(client); + } + } + + /*-----------------------------------------------------*\ + | Start server if requested | + \*-----------------------------------------------------*/ + if(start_server) + { + detection_percent = 0; + detection_string = "Starting server"; + DetectionProgressChanged(); + + GetServer()->StartServer(); + if(!GetServer()->GetOnline()) + { + LOG_DEBUG("[ResourceManager] Server failed to start"); + } + } + + /*-----------------------------------------------------*\ + | Perform actual detection if enabled | + | Done in the same thread (InitThread), as we need to | + | wait for completion anyway | + \*-----------------------------------------------------*/ + if(detection_enabled) + { + LOG_DEBUG("[ResourceManager] Running standalone"); + if(ProcessPreDetection()) + { + /*---------------------------------------------*\ + | We are currently in a coroutine, so run | + | detection directly with no scheduling | + \*---------------------------------------------*/ + DetectDevicesCoroutine(); + } + } + else + { + ProcessPostDetection(); + } + + /*-----------------------------------------------------*\ + | Process command line arguments after detection only | + | if the pre-detection parsing indicated it should be | + | run | + \*-----------------------------------------------------*/ + if(apply_post_options) + { + cli_post_detection(); + } + + init_finished = true; +} + +void ResourceManager::HidExitCoroutine() +{ + /*-----------------------------------------------------*\ + | Cleanup HID interface | + | WARNING: may not be ran from any other thread!!! | + \*-----------------------------------------------------*/ + int hid_status = hid_exit(); + + LOG_DEBUG("[ResourceManager] Closing HID interfaces: %s", ((hid_status == 0) ? "Success" : "Failed")); +} + +void ResourceManager::RunInBackgroundThread(std::function coroutine) +{ + if(std::this_thread::get_id() == DetectDevicesThread->get_id()) + { + /*-------------------------------------------------*\ + | We are already in the background thread - don't | + | schedule the call, run it immediately | + \*-------------------------------------------------*/ + coroutine(); + } + else + { + BackgroundThreadStateMutex.lock(); + if(ScheduledBackgroundFunction != nullptr) + { + LOG_WARNING("[ResourceManager] Detection coroutine: assigned a new coroutine when one was already scheduled - probably two rescan events sent at once"); + } + ScheduledBackgroundFunction = coroutine; + BackgroundThreadStateMutex.unlock(); + BackgroundFunctionStartTrigger.notify_one(); + } +} + +void ResourceManager::BackgroundThreadFunction() +{ + /*-----------------------------------------------------*\ + | The background thread that runs scheduled coroutines | + | when applicable | + | Stays asleep if nothing is scheduled | + | NOTE: this thread owns the HIDAPI library internal | + | objects on MacOS | + | hid_init and hid_exit may not be called outside of | + | this thread. Calling hid_exit outside of this thread | + | WILL cause an immediate CRASH on MacOS. | + | BackgroundThreadStateMutex will be UNLOCKED as long | + | as the thread is suspended. It locks automatically | + | when any coroutine is running. However, it seems to | + | be necessary to be separate from the | + | DeviceDetectionMutex, even though their states are | + | nearly identical. | + \------------------------------------------------------*/ + + std::unique_lock lock(BackgroundThreadStateMutex); + while(background_thread_running) + { + if(ScheduledBackgroundFunction) + { + std::function coroutine = nullptr; + std::swap(ScheduledBackgroundFunction, coroutine); + try + { + coroutine(); + } + catch(std::exception& e) + { + LOG_ERROR("[ResourceManager] Unhandled exception in coroutine; e.what(): %s", e.what()); + } + catch(...) + { + LOG_ERROR("[ResourceManager] Unhandled exception in coroutine"); + } + } + /*-------------------------------------------------*\ + | This line will cause the thread to suspend until | + | the condition variable is triggered | + | NOTE: it may be subject to "spurious wakeups" | + \*-------------------------------------------------*/ + BackgroundFunctionStartTrigger.wait(lock); + } +} + +void ResourceManager::UpdateDetectorSettings() +{ + json detector_settings; + bool save_settings = false; + + /*-----------------------------------------------------*\ + | Open device disable list and read in disabled device | + | strings | + \*-----------------------------------------------------*/ + detector_settings = settings_manager->GetSettings("Detectors"); + + /*-----------------------------------------------------*\ + | Loop through all I2C detectors and see if any need to | + | be saved to the settings | + \*-----------------------------------------------------*/ + for(unsigned int i2c_detector_idx = 0; i2c_detector_idx < (unsigned int)i2c_device_detectors.size(); i2c_detector_idx++) + { + detection_string = i2c_device_detector_strings[i2c_detector_idx].c_str(); + + if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) + { + detector_settings["detectors"][detection_string] = true; + save_settings = true; + } + } + + /*-----------------------------------------------------*\ + | Loop through all I2C DIMM detectors and see if any | + | need to be saved to the settings | + \*-----------------------------------------------------*/ + for(unsigned int i2c_detector_idx = 0; i2c_detector_idx < (unsigned int)i2c_dimm_device_detectors.size(); i2c_detector_idx++) + { + detection_string = i2c_dimm_device_detectors[i2c_detector_idx].name.c_str(); + + if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) + { + detector_settings["detectors"][detection_string] = true; + save_settings = true; + } + } + + /*-----------------------------------------------------*\ + | Loop through all I2C PCI detectors and see if any | + | need to be saved to the settings | + \*-----------------------------------------------------*/ + for(unsigned int i2c_pci_detector_idx = 0; i2c_pci_detector_idx < (unsigned int)i2c_pci_device_detectors.size(); i2c_pci_detector_idx++) + { + detection_string = i2c_pci_device_detectors[i2c_pci_detector_idx].name.c_str(); + + if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) + { + detector_settings["detectors"][detection_string] = true; + save_settings = true; + } + } + + /*-----------------------------------------------------*\ + | Loop through all HID detectors and see if any need to | + | be saved to the settings | + \*-----------------------------------------------------*/ + for(unsigned int hid_detector_idx = 0; hid_detector_idx < (unsigned int)hid_device_detectors.size(); hid_detector_idx++) + { + detection_string = hid_device_detectors[hid_detector_idx].name.c_str(); + + if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) + { + detector_settings["detectors"][detection_string] = true; + save_settings = true; + } + } + + /*-----------------------------------------------------*\ + | Loop through all HID wrapped detectors and see if any | + | need to be saved to the settings | + \*-----------------------------------------------------*/ + for(unsigned int hid_wrapped_detector_idx = 0; hid_wrapped_detector_idx < (unsigned int)hid_wrapped_device_detectors.size(); hid_wrapped_detector_idx++) + { + detection_string = hid_wrapped_device_detectors[hid_wrapped_detector_idx].name.c_str(); + + if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) + { + detector_settings["detectors"][detection_string] = true; + save_settings = true; + } + } + + /*-----------------------------------------------------*\ + | Loop through remaining detectors and see if any need | + | to be saved to the settings | + \*-----------------------------------------------------*/ + for(unsigned int detector_idx = 0; detector_idx < (unsigned int)device_detectors.size(); detector_idx++) + { + detection_string = device_detector_strings[detector_idx].c_str(); + + if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) + { + detector_settings["detectors"][detection_string] = true; + save_settings = true; + } + } + + /*-----------------------------------------------------*\ + | If there were any setting changes that need to be | + | saved, set the settings in the settings manager and | + | save them. | + \*-----------------------------------------------------*/ + if(save_settings) + { + LOG_INFO("[ResourceManager] Saving detector settings"); + + settings_manager->SetSettings("Detectors", detector_settings); + + settings_manager->SaveSettings(); + } +} + +void ResourceManager::WaitForInitialization() +{ + /*-----------------------------------------------------*\ + | A reliable sychronization of this kind is impossible | + | without the use of a `barrier` implementation, which | + | is only introduced in C++20 | + \*-----------------------------------------------------*/ + while(!init_finished) + { + std::this_thread::sleep_for(1ms); + }; +} + +void ResourceManager::WaitForDeviceDetection() +{ + DetectDeviceMutex.lock(); + DetectDeviceMutex.unlock(); +} + +bool ResourceManager::IsAnyDimmDetectorEnabled(json &detector_settings) +{ + for(unsigned int i2c_detector_idx = 0; i2c_detector_idx < i2c_dimm_device_detectors.size() && detection_is_required.load(); i2c_detector_idx++) + { + std::string detector_name_string = i2c_dimm_device_detectors[i2c_detector_idx].name.c_str(); + /*-------------------------------------------------*\ + | Check if this detector is enabled | + \*-------------------------------------------------*/ + if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detector_name_string) && + detector_settings["detectors"][detector_name_string] == true) + { + return true; + } + } + return false; +} diff --git a/ResourceManager.h b/ResourceManager.h new file mode 100644 index 0000000..2786d42 --- /dev/null +++ b/ResourceManager.h @@ -0,0 +1,370 @@ +/*---------------------------------------------------------*\ +| ResourceManager.h | +| | +| OpenRGB Resource Manager controls access to application | +| components including RGBControllers, I2C interfaces, | +| and network SDK components | +| | +| Adam Honse (CalcProgrammer1) 27 Sep 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "SPDWrapper.h" +#include "hidapi_wrapper.h" +#include "i2c_smbus.h" +#include "ResourceManagerInterface.h" +#include "filesystem.h" +#include + +using json = nlohmann::json; + +#define HID_INTERFACE_ANY -1 +#define HID_USAGE_ANY -1 +#define HID_USAGE_PAGE_ANY -1 + +struct hid_device_info; +class NetworkClient; +class NetworkServer; +class ProfileManager; +class RGBController; +class SettingsManager; + +typedef std::function I2CBusDetectorFunction; +typedef std::function DeviceDetectorFunction; +typedef std::function&)> I2CDeviceDetectorFunction; +typedef std::function&, const std::string&)> I2CDIMMDeviceDetectorFunction; +typedef std::function I2CPCIDeviceDetectorFunction; +typedef std::function HIDDeviceDetectorFunction; +typedef std::function HIDWrappedDeviceDetectorFunction; +typedef std::function DynamicDetectorFunction; +typedef std::function PreDetectionHookFunction; + +class BasicHIDBlock +{ +public: + std::string name; + uint16_t vid; + uint16_t pid; + int interface; + int usage_page; + int usage; + + bool compare(hid_device_info* info); + bool compare_no_interface(hid_device_info* info); +}; + +class HIDDeviceDetectorBlock : public BasicHIDBlock +{ +public: + HIDDeviceDetectorFunction function; +}; + +class HIDWrappedDeviceDetectorBlock : public BasicHIDBlock +{ +public: + HIDWrappedDeviceDetectorFunction function; +}; + +typedef struct +{ + std::string name; + I2CPCIDeviceDetectorFunction function; + uint16_t ven_id; + uint16_t dev_id; + uint16_t subven_id; + uint16_t subdev_id; + uint8_t i2c_addr; +} I2CPCIDeviceDetectorBlock; + +typedef struct +{ + std::string name; + I2CDIMMDeviceDetectorFunction function; + uint16_t jedec_id; + uint8_t dimm_type; +} I2CDIMMDeviceDetectorBlock; + +/*---------------------------------------------------------*\ +| Define a macro for QT lupdate to parse | +\*---------------------------------------------------------*/ +#define QT_TRANSLATE_NOOP(scope, x) x + +extern const char* I2C_ERR_WIN; +extern const char* I2C_ERR_LINUX; +extern const char* UDEV_MISSING; +extern const char* UDEV_MULTI; + +class ResourceManager: public ResourceManagerInterface +{ +public: + static ResourceManager *get(); + + ResourceManager(); + ~ResourceManager(); + + void RegisterI2CBus(i2c_smbus_interface *); + std::vector & GetI2CBusses(); + + void RegisterRGBController(RGBController *rgb_controller); + void UnregisterRGBController(RGBController *rgb_controller); + + std::vector & GetRGBControllers(); + + void RegisterI2CBusDetector (I2CBusDetectorFunction detector); + void RegisterDeviceDetector (std::string name, DeviceDetectorFunction detector); + void RegisterI2CDeviceDetector (std::string name, I2CDeviceDetectorFunction detector); + void RegisterI2CDIMMDeviceDetector (std::string name, I2CDIMMDeviceDetectorFunction detector, uint16_t jedec_id, uint8_t dimm_type); + void RegisterI2CPCIDeviceDetector (std::string name, I2CPCIDeviceDetectorFunction detector, uint16_t ven_id, uint16_t dev_id, uint16_t subven_id, uint16_t subdev_id, uint8_t i2c_addr); + void RegisterHIDDeviceDetector (std::string name, + HIDDeviceDetectorFunction detector, + uint16_t vid, + uint16_t pid, + int interface = HID_INTERFACE_ANY, + int usage_page = HID_USAGE_PAGE_ANY, + int usage = HID_USAGE_ANY); + void RegisterHIDWrappedDeviceDetector (std::string name, + HIDWrappedDeviceDetectorFunction detector, + uint16_t vid, + uint16_t pid, + int interface = HID_INTERFACE_ANY, + int usage_page = HID_USAGE_PAGE_ANY, + int usage = HID_USAGE_ANY); + void RegisterDynamicDetector (std::string name, DynamicDetectorFunction detector); + void RegisterPreDetectionHook (PreDetectionHookFunction hook); + + void RegisterClientInfoChangeCallback(ClientInfoChangeCallback new_callback, void * new_callback_arg); + void RegisterDeviceListChangeCallback(DeviceListChangeCallback new_callback, void * new_callback_arg); + void RegisterDetectionProgressCallback(DetectionProgressCallback new_callback, void * new_callback_arg); + void RegisterDetectionStartCallback(DetectionStartCallback new_callback, void * new_callback_arg); + void RegisterDetectionEndCallback(DetectionEndCallback new_callback, void * new_callback_arg); + void RegisterI2CBusListChangeCallback(I2CBusListChangeCallback new_callback, void * new_callback_arg); + + void UnregisterClientInfoChangeCallback(ClientInfoChangeCallback new_callback, void * new_callback_arg); + void UnregisterDeviceListChangeCallback(DeviceListChangeCallback callback, void * callback_arg); + void UnregisterDetectionProgressCallback(DetectionProgressCallback callback, void *callback_arg); + void UnregisterDetectionStartCallback(DetectionStartCallback callback, void *callback_arg); + void UnregisterDetectionEndCallback(DetectionEndCallback callback, void *callback_arg); + void UnregisterI2CBusListChangeCallback(I2CBusListChangeCallback callback, void * callback_arg); + + bool GetDetectionEnabled(); + unsigned int GetDetectionPercent(); + const char* GetDetectionString(); + + filesystem::path GetConfigurationDirectory(); + + void RegisterNetworkClient(NetworkClient* new_client); + void UnregisterNetworkClient(NetworkClient* network_client); + + std::vector& GetClients(); + NetworkServer* GetServer(); + + ProfileManager* GetProfileManager(); + SettingsManager* GetSettingsManager(); + + void SetConfigurationDirectory(const filesystem::path &directory); + + void ProcessPreDetectionHooks(); // Consider making private + void ProcessDynamicDetectors(); // Consider making private + void UpdateDeviceList(); + void ClientInfoChanged(); + void DeviceListChanged(); + void DetectionProgressChanged(); + void I2CBusListChanged(); + + void Initialize(bool tryConnect, bool detectDevices, bool startServer, bool applyPostOptions); + + void Cleanup(); + + void DetectDevices(); + + void DisableDetection(); + + void RescanDevices(); + + void StopDeviceDetection(); + + void WaitForInitialization(); + void WaitForDeviceDetection(); + +private: + void UpdateDetectorSettings(); + void SetupConfigurationDirectory(); + bool AttemptLocalConnection(); + bool ProcessPreDetection(); + void ProcessPostDetection(); + bool IsAnyDimmDetectorEnabled(json &detector_settings); + void RunInBackgroundThread(std::function); + void BackgroundThreadFunction(); + + /*-----------------------------------------------------*\ + | Functions that must be run in the background thread | + | These are not related to STL coroutines, yet this | + | name is the most convenient | + \*-----------------------------------------------------*/ + void InitCoroutine(); + void DetectDevicesCoroutine(); + void HidExitCoroutine(); + + /*-----------------------------------------------------*\ + | Static pointer to shared instance of ResourceManager | + \*-----------------------------------------------------*/ + static ResourceManager* instance; + + /*-----------------------------------------------------*\ + | Auto connection permitting flag | + \*-----------------------------------------------------*/ + bool tryAutoConnect; + + /*-----------------------------------------------------*\ + | Detection enabled flag | + \*-----------------------------------------------------*/ + bool detection_enabled; + + /*-----------------------------------------------------*\ + | Auto connection active flag | + \*-----------------------------------------------------*/ + bool auto_connection_active; + + /*-----------------------------------------------------*\ + | Auto connection client pointer | + \*-----------------------------------------------------*/ + NetworkClient * auto_connection_client; + + /*-----------------------------------------------------*\ + | Auto connection permitting flag | + \*-----------------------------------------------------*/ + bool start_server; + + /*-----------------------------------------------------*\ + | Auto connection permitting flag | + \*-----------------------------------------------------*/ + bool apply_post_options; + + /*-----------------------------------------------------*\ + | Initialization completion flag | + \*-----------------------------------------------------*/ + std::atomic init_finished; + + /*-----------------------------------------------------*\ + | Initial detection flag | + \*-----------------------------------------------------*/ + bool initial_detection; + + /*-----------------------------------------------------*\ + | Profile Manager | + \*-----------------------------------------------------*/ + ProfileManager* profile_manager; + + /*-----------------------------------------------------*\ + | Settings Manager | + \*-----------------------------------------------------*/ + SettingsManager* settings_manager; + + /*-----------------------------------------------------*\ + | I2C/SMBus Interfaces | + \*-----------------------------------------------------*/ + std::vector busses; + + /*-----------------------------------------------------*\ + | RGBControllers | + \*-----------------------------------------------------*/ + std::vector rgb_controllers_sizes; + std::vector rgb_controllers_hw; + std::vector rgb_controllers; + + /*-----------------------------------------------------*\ + | Network Server | + \*-----------------------------------------------------*/ + NetworkServer* server; + + /*-----------------------------------------------------*\ + | Network Clients | + \*-----------------------------------------------------*/ + std::vector clients; + + /*-----------------------------------------------------*\ + | Detectors | + \*-----------------------------------------------------*/ + std::vector device_detectors; + std::vector device_detector_strings; + std::vector i2c_bus_detectors; + std::vector i2c_device_detectors; + std::vector i2c_device_detector_strings; + std::vector i2c_dimm_device_detectors; + std::vector i2c_pci_device_detectors; + std::vector hid_device_detectors; + std::vector hid_wrapped_device_detectors; + std::vector dynamic_detectors; + std::vector dynamic_detector_strings; + std::vector pre_detection_hooks; + + bool dynamic_detectors_processed; + + /*-----------------------------------------------------*\ + | Detection Thread and Detection State | + \*-----------------------------------------------------*/ + std::thread * DetectDevicesThread; + std::mutex DetectDeviceMutex; + std::function ScheduledBackgroundFunction; + std::mutex BackgroundThreadStateMutex; + + /*-----------------------------------------------------*\ + | NOTE: wakes up the background detection thread | + \*-----------------------------------------------------*/ + std::condition_variable BackgroundFunctionStartTrigger; + + std::atomic background_thread_running; + std::atomic detection_is_required; + std::atomic detection_percent; + std::atomic detection_prev_size; + std::vector detection_size_entry_used; + const char* detection_string; + + /*-----------------------------------------------------*\ + | Client Info Changed Callback | + \*-----------------------------------------------------*/ + std::vector ClientInfoChangeCallbacks; + std::vector ClientInfoChangeCallbackArgs; + + /*-----------------------------------------------------*\ + | Device List Changed Callback | + \*-----------------------------------------------------*/ + std::mutex DeviceListChangeMutex; + std::vector DeviceListChangeCallbacks; + std::vector DeviceListChangeCallbackArgs; + + /*-----------------------------------------------------*\ + | Detection Progress, Start, and End Callbacks | + \*-----------------------------------------------------*/ + std::mutex DetectionProgressMutex; + std::vector DetectionProgressCallbacks; + std::vector DetectionProgressCallbackArgs; + + std::vector DetectionStartCallbacks; + std::vector DetectionStartCallbackArgs; + + std::vector DetectionEndCallbacks; + std::vector DetectionEndCallbackArgs; + + /*-----------------------------------------------------*\ + | I2C/SMBus Adapter List Changed Callback | + \*-----------------------------------------------------*/ + std::mutex I2CBusListChangeMutex; + std::vector I2CBusListChangeCallbacks; + std::vector I2CBusListChangeCallbackArgs; + + /*-----------------------------------------------------*\ + | OpenRGB configuration directory path | + \*-----------------------------------------------------*/ + filesystem::path config_dir; +}; diff --git a/ResourceManagerInterface.h b/ResourceManagerInterface.h new file mode 100644 index 0000000..20c2b30 --- /dev/null +++ b/ResourceManagerInterface.h @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| ResourceManagerInterface.h | +| | +| Provides a virtual interface to ResourceManager for | +| exposing ResourceManager to plugins. Changes to this | +| class structure require a new plugin API version. | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" +#include "filesystem.h" + +class NetworkClient; +class NetworkServer; +class ProfileManager; +class RGBController; +class SettingsManager; + +typedef void (*ClientInfoChangeCallback)(void *); +typedef void (*DeviceListChangeCallback)(void *); +typedef void (*DetectionProgressCallback)(void *); +typedef void (*DetectionStartCallback)(void *); +typedef void (*DetectionEndCallback)(void *); +typedef void (*I2CBusListChangeCallback)(void *); + +class ResourceManagerInterface +{ +public: + virtual std::vector & GetI2CBusses() = 0; + + virtual void RegisterRGBController(RGBController *rgb_controller) = 0; + virtual void UnregisterRGBController(RGBController *rgb_controller) = 0; + + virtual void RegisterDeviceListChangeCallback(DeviceListChangeCallback new_callback, void * new_callback_arg) = 0; + virtual void RegisterDetectionProgressCallback(DetectionProgressCallback new_callback, void * new_callback_arg) = 0; + virtual void RegisterDetectionStartCallback(DetectionStartCallback new_callback, void * new_callback_arg) = 0; + virtual void RegisterDetectionEndCallback(DetectionEndCallback new_callback, void * new_callback_arg) = 0; + virtual void RegisterI2CBusListChangeCallback(I2CBusListChangeCallback new_callback, void * new_callback_arg) = 0; + + virtual void UnregisterDeviceListChangeCallback(DeviceListChangeCallback callback, void * callback_arg) = 0; + virtual void UnregisterDetectionProgressCallback(DetectionProgressCallback callback, void *callback_arg) = 0; + virtual void UnregisterDetectionStartCallback(DetectionStartCallback callback, void *callback_arg) = 0; + virtual void UnregisterDetectionEndCallback(DetectionEndCallback callback, void *callback_arg) = 0; + virtual void UnregisterI2CBusListChangeCallback(I2CBusListChangeCallback callback, void * callback_arg) = 0; + + virtual std::vector & GetRGBControllers() = 0; + + virtual unsigned int GetDetectionPercent() = 0; + + virtual filesystem::path GetConfigurationDirectory() = 0; + + virtual std::vector& GetClients() = 0; + virtual NetworkServer* GetServer() = 0; + + virtual ProfileManager* GetProfileManager() = 0; + virtual SettingsManager* GetSettingsManager() = 0; + + virtual void UpdateDeviceList() = 0; + virtual void WaitForDeviceDetection() = 0; + +protected: + virtual ~ResourceManagerInterface() {}; +}; diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..1236bb8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,12 @@ +# Security policy + +Gebruik geen publieke issue voor vermoedelijke kwetsbaarheden, tokens, +configuratiebestanden of diagnostische exports. Meld een kwetsbaarheid privé via +`security@itworx.tech`. + +Ondersteunde release: de meest recente getagde LumaOps-release. Vermeld in een +rapport de versie, impact, minimale reproductiestappen en een voorstel voor +gecoördineerde openbaarmaking. Stuur nooit productiegegevens mee. + +Het technische veiligheidsmodel en de deploymentgrenzen staan in +[docs/SECURITY.md](docs/SECURITY.md). diff --git a/SPDAccessor/DDR4DirectAccessor.cpp b/SPDAccessor/DDR4DirectAccessor.cpp new file mode 100644 index 0000000..de79a71 --- /dev/null +++ b/SPDAccessor/DDR4DirectAccessor.cpp @@ -0,0 +1,94 @@ +/*---------------------------------------------------------*\ +| DDR4DirectAccessor.cpp | +| | +| DDR4 SPD accessor implementation using direct i2c | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "DDR4DirectAccessor.h" + +using namespace std::chrono_literals; + +DDR4DirectAccessor::DDR4DirectAccessor(i2c_smbus_interface *bus, uint8_t spd_addr) + : DDR4Accessor(bus, spd_addr) +{ +} + +DDR4DirectAccessor::~DDR4DirectAccessor() +{ +} + +bool DDR4DirectAccessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr) +{ + /*-----------------------------------------------------*\ + | Select low page | + \*-----------------------------------------------------*/ + bus->i2c_smbus_write_byte(0x36, 0x00); + + std::this_thread::sleep_for(SPD_IO_DELAY); + + /*-----------------------------------------------------*\ + | Read value at address 0 in SPD device | + \*-----------------------------------------------------*/ + s32 value = bus->i2c_smbus_read_byte_data(spd_addr, 0x00); + + /*-----------------------------------------------------*\ + | DDR4 is available if value is 0x23 | + \*-----------------------------------------------------*/ + return(value == 0x23); +} + +SPDAccessor *DDR4DirectAccessor::copy() +{ + return new DDR4DirectAccessor(bus, address); +} + +uint8_t DDR4DirectAccessor::at(uint16_t addr) +{ + /*-----------------------------------------------------*\ + | Ensure address is valid | + \*-----------------------------------------------------*/ + if(addr >= SPD_DDR4_EEPROM_LENGTH) + { + return 0xFF; + } + + /*-----------------------------------------------------*\ + | Switch to the page containing address | + \*-----------------------------------------------------*/ + set_page(addr >> SPD_DDR4_EEPROM_PAGE_SHIFT); + + /*-----------------------------------------------------*\ + | Calculate offset | + \*-----------------------------------------------------*/ + uint8_t offset = (uint8_t)(addr & SPD_DDR4_EEPROM_PAGE_MASK); + + /*-----------------------------------------------------*\ + | Read value at address | + \*-----------------------------------------------------*/ + uint32_t value = bus->i2c_smbus_read_byte_data(address, offset); + + std::this_thread::sleep_for(SPD_IO_DELAY); + + /*-----------------------------------------------------*\ + | Return value | + \*-----------------------------------------------------*/ + return((uint8_t)value); +} + +void DDR4DirectAccessor::set_page(uint8_t page) +{ + /*-----------------------------------------------------*\ + | Switch page if not already active | + \*-----------------------------------------------------*/ + if(current_page != page) + { + bus->i2c_smbus_write_byte_data(0x36 + page, 0x00, 0xFF); + current_page = page; + + std::this_thread::sleep_for(SPD_IO_DELAY); + } +} diff --git a/SPDAccessor/DDR4DirectAccessor.h b/SPDAccessor/DDR4DirectAccessor.h new file mode 100644 index 0000000..8fe025f --- /dev/null +++ b/SPDAccessor/DDR4DirectAccessor.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| DDR4DirectAccessor.h | +| | +| DDR4 SPD accessor implementation using direct i2c | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "SPDAccessor.h" + +class DDR4DirectAccessor : public DDR4Accessor +{ + public: + DDR4DirectAccessor(i2c_smbus_interface *bus, uint8_t address); + virtual ~DDR4DirectAccessor(); + + static bool isAvailable(i2c_smbus_interface *bus, uint8_t address); + + virtual SPDAccessor * copy(); + virtual uint8_t at(uint16_t addr); + + private: + uint8_t current_page = 0xFF; + static const uint16_t SPD_DDR4_EEPROM_LENGTH = 512; + static const uint8_t SPD_DDR4_EEPROM_PAGE_SHIFT = 8; + static const uint8_t SPD_DDR4_EEPROM_PAGE_MASK = 0xFF; + + void set_page(uint8_t page); +}; diff --git a/SPDAccessor/DDR5DirectAccessor.cpp b/SPDAccessor/DDR5DirectAccessor.cpp new file mode 100644 index 0000000..2c3ff9d --- /dev/null +++ b/SPDAccessor/DDR5DirectAccessor.cpp @@ -0,0 +1,122 @@ +/*---------------------------------------------------------*\ +| DDR5DirectAccessor.cpp | +| | +| DDR5 SPD accessor implementation using direct i2c | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "DDR5DirectAccessor.h" +#include "LogManager.h" + +using namespace std::chrono_literals; + +DDR5DirectAccessor::DDR5DirectAccessor(i2c_smbus_interface *bus, uint8_t spd_addr) + : DDR5Accessor(bus, spd_addr) +{ +} + +DDR5DirectAccessor::~DDR5DirectAccessor() +{ +} + +bool DDR5DirectAccessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr) +{ + bool retry = true; + + while(true) + { + std::this_thread::sleep_for(SPD_IO_DELAY); + int ddr5Magic = bus->i2c_smbus_read_byte_data(spd_addr, 0x00); + std::this_thread::sleep_for(SPD_IO_DELAY); + int ddr5Sensor = bus->i2c_smbus_read_byte_data(spd_addr, 0x01); + std::this_thread::sleep_for(SPD_IO_DELAY); + + if(ddr5Magic < 0 || ddr5Sensor < 0) + { + break; + } + + LOG_TRACE("[DDR5DirectAccessor] SPD Hub Magic: 0x%02x 0x%02x", ddr5Magic, ddr5Sensor); + + if(ddr5Magic == 0x51 && (ddr5Sensor & 0xEF) == 0x08) + { + return true; + } + + int page = bus->i2c_smbus_read_byte_data(spd_addr, SPD_DDR5_MREG_VIRTUAL_PAGE); + std::this_thread::sleep_for(SPD_IO_DELAY); + + LOG_TRACE("[DDR5DirectAccessor] SPD Page: 0x%02x", page); + if(page < 0) + { + break; + } + else if(retry && page > 0 && page < (SPD_DDR5_EEPROM_LENGTH >> SPD_DDR5_EEPROM_PAGE_SHIFT)) + { + // This still might be a DDR5 module, just the page is off + bus->i2c_smbus_write_byte_data(spd_addr, SPD_DDR5_MREG_VIRTUAL_PAGE, 0); + std::this_thread::sleep_for(SPD_IO_DELAY); + retry = false; + } + else + { + break; + } + } + return false; +} + +SPDAccessor *DDR5DirectAccessor::copy() +{ + DDR5DirectAccessor *access = new DDR5DirectAccessor(bus, address); + access->current_page = this->current_page; + return access; +} + +uint8_t DDR5DirectAccessor::at(uint16_t addr) +{ + /*-----------------------------------------------------*\ + | Ensure address is valid | + \*-----------------------------------------------------*/ + if(addr >= SPD_DDR5_EEPROM_LENGTH) + { + return 0xFF; + } + + /*-----------------------------------------------------*\ + | Switch to the page containing address | + \*-----------------------------------------------------*/ + set_page(addr >> SPD_DDR5_EEPROM_PAGE_SHIFT); + + /*-----------------------------------------------------*\ + | Calculate offset | + \*-----------------------------------------------------*/ + uint8_t offset = (uint8_t)(addr & SPD_DDR5_EEPROM_PAGE_MASK) | 0x80; + + /*-----------------------------------------------------*\ + | Read value at address | + \*-----------------------------------------------------*/ + uint32_t value = bus->i2c_smbus_read_byte_data(address, offset); + + std::this_thread::sleep_for(SPD_IO_DELAY); + + /*-----------------------------------------------------*\ + | Return value | + \*-----------------------------------------------------*/ + return((uint8_t)value); +} + +void DDR5DirectAccessor::set_page(uint8_t page) +{ + /*-----------------------------------------------------*\ + | Switch page if not already active | + \*-----------------------------------------------------*/ + if(current_page != page) + { + bus->i2c_smbus_write_byte_data(address, SPD_DDR5_MREG_VIRTUAL_PAGE, page); + current_page = page; + std::this_thread::sleep_for(SPD_IO_DELAY); + } +} diff --git a/SPDAccessor/DDR5DirectAccessor.h b/SPDAccessor/DDR5DirectAccessor.h new file mode 100644 index 0000000..8a24b06 --- /dev/null +++ b/SPDAccessor/DDR5DirectAccessor.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| DDR5DirectAccessor.h | +| | +| DDR5 SPD accessor implementation using direct i2c | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "SPDAccessor.h" + +class DDR5DirectAccessor : public DDR5Accessor +{ + public: + DDR5DirectAccessor(i2c_smbus_interface *bus, uint8_t address); + virtual ~DDR5DirectAccessor(); + + static bool isAvailable(i2c_smbus_interface *bus, uint8_t address); + + virtual SPDAccessor * copy(); + virtual uint8_t at(uint16_t addr); + + private: + uint8_t current_page = 0xFF; + static const uint16_t SPD_DDR5_EEPROM_LENGTH = 2048; + static const uint8_t SPD_DDR5_EEPROM_PAGE_SHIFT = 7; + static const uint8_t SPD_DDR5_EEPROM_PAGE_MASK = 0x7F; + static const uint8_t SPD_DDR5_MREG_VIRTUAL_PAGE = 0x0B; + + void set_page(uint8_t page); +}; diff --git a/SPDAccessor/EE1004Accessor_Linux.cpp b/SPDAccessor/EE1004Accessor_Linux.cpp new file mode 100644 index 0000000..df85358 --- /dev/null +++ b/SPDAccessor/EE1004Accessor_Linux.cpp @@ -0,0 +1,72 @@ +/*---------------------------------------------------------*\ +| EE1004Accessor_Linux.cpp | +| | +| SPD accessor implementation using e1004 driver on Linux | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "EE1004Accessor_Linux.h" +#include "filesystem.h" + +const char *EE1004Accessor::SPD_EE1004_PATH = "/sys/bus/i2c/drivers/ee1004/%u-%04x/eeprom"; + +EE1004Accessor::EE1004Accessor(i2c_smbus_interface *bus, uint8_t spd_addr) + : DDR4Accessor(bus, spd_addr), valid(false) +{ +} + +EE1004Accessor::~EE1004Accessor() +{ +} + +bool EE1004Accessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr) +{ + int size = snprintf(nullptr, 0, SPD_EE1004_PATH, bus->bus_id, spd_addr); + char *path = new char[size+1]; + snprintf(path, size+1, SPD_EE1004_PATH, bus->bus_id, spd_addr); + bool result = std::filesystem::exists(path); + delete[] path; + return result; +} + +SPDAccessor *EE1004Accessor::copy() +{ + EE1004Accessor *access = new EE1004Accessor(bus, address); + memcpy(access->dump, this->dump, sizeof(this->dump)); + access->valid = this->valid; + return access; +} + +uint8_t EE1004Accessor::at(uint16_t addr) +{ + if(!valid) + { + readEEPROM(); + } + // Prevent indexing out of bounds + if(addr >= sizeof(dump)) + { + return 0xFF; + } + + return dump[addr]; +} + +void EE1004Accessor::readEEPROM() +{ + int size = snprintf(nullptr, 0, SPD_EE1004_PATH, bus->bus_id, address); + char *filename = new char[size+1]; + snprintf(filename, size+1, SPD_EE1004_PATH, bus->bus_id, address); + + std::ifstream eeprom_file(filename, std::ios::in | std::ios::binary); + if(eeprom_file) + { + eeprom_file.read((char*)dump, sizeof(dump)); + eeprom_file.close(); + } + delete[] filename; +} diff --git a/SPDAccessor/EE1004Accessor_Linux.h b/SPDAccessor/EE1004Accessor_Linux.h new file mode 100644 index 0000000..76cc1b3 --- /dev/null +++ b/SPDAccessor/EE1004Accessor_Linux.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| EE1004Accessor_Linux.h | +| | +| SPD accessor implementation using e1004 driver on Linux | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "SPDAccessor.h" + +class EE1004Accessor : public DDR4Accessor +{ + public: + EE1004Accessor(i2c_smbus_interface *bus, uint8_t address); + virtual ~EE1004Accessor(); + + static bool isAvailable(i2c_smbus_interface *bus, uint8_t address); + + virtual SPDAccessor *copy(); + virtual uint8_t at(uint16_t addr); + + private: + static const char *SPD_EE1004_PATH; + + uint8_t dump[512]; + bool valid; + + void readEEPROM(); +}; diff --git a/SPDAccessor/SPD5118Accessor_Linux.cpp b/SPDAccessor/SPD5118Accessor_Linux.cpp new file mode 100644 index 0000000..40194e6 --- /dev/null +++ b/SPDAccessor/SPD5118Accessor_Linux.cpp @@ -0,0 +1,73 @@ +/*---------------------------------------------------------*\ +| SPD5118Accessor_Linux.cpp | +| | +| DDR5 SPD accessor implementation using spd5118 driver | +| on Linux | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "SPD5118Accessor_Linux.h" +#include "filesystem.h" + +const char *SPD5118Accessor::SPD_SPD5118_PATH = "/sys/bus/i2c/drivers/spd5118/%u-%04x/eeprom"; + +SPD5118Accessor::SPD5118Accessor(i2c_smbus_interface *bus, uint8_t spd_addr) + : DDR5Accessor(bus, spd_addr), valid(false) +{ +} + +SPD5118Accessor::~SPD5118Accessor() +{ +} + +bool SPD5118Accessor::isAvailable(i2c_smbus_interface *bus, uint8_t spd_addr) +{ + int size = snprintf(nullptr, 0, SPD_SPD5118_PATH, bus->bus_id, spd_addr); + char *path = new char[size+1]; + snprintf(path, size+1, SPD_SPD5118_PATH, bus->bus_id, spd_addr); + bool result = std::filesystem::exists(path); + delete[] path; + return result; +} + +SPDAccessor *SPD5118Accessor::copy() +{ + SPD5118Accessor *access = new SPD5118Accessor(bus, address); + memcpy(access->dump, this->dump, sizeof(this->dump)); + access->valid = this->valid; + return access; +} + +uint8_t SPD5118Accessor::at(uint16_t addr) +{ + if(!valid) + { + readEEPROM(); + } + // Prevent indexing out of bounds + if(addr >= sizeof(dump)) + { + return 0xFF; + } + + return dump[addr]; +} + +void SPD5118Accessor::readEEPROM() +{ + int size = snprintf(nullptr, 0, SPD_SPD5118_PATH, bus->bus_id, address); + char *filename = new char[size+1]; + snprintf(filename, size+1, SPD_SPD5118_PATH, bus->bus_id, address); + + std::ifstream eeprom_file(filename, std::ios::in | std::ios::binary); + if(eeprom_file) + { + eeprom_file.read((char*)dump, sizeof(dump)); + eeprom_file.close(); + } + delete[] filename; +} diff --git a/SPDAccessor/SPD5118Accessor_Linux.h b/SPDAccessor/SPD5118Accessor_Linux.h new file mode 100644 index 0000000..d277c29 --- /dev/null +++ b/SPDAccessor/SPD5118Accessor_Linux.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| SPD5118Accessor_Linux.h | +| | +| DDR5 SPD accessor implementation using spd5118 driver | +| on Linux | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "SPDAccessor.h" + +class SPD5118Accessor : public DDR5Accessor +{ + public: + SPD5118Accessor(i2c_smbus_interface *bus, uint8_t address); + virtual ~SPD5118Accessor(); + + static bool isAvailable(i2c_smbus_interface *bus, uint8_t address); + + virtual SPDAccessor *copy(); + virtual uint8_t at(uint16_t addr); + + private: + static const char *SPD_SPD5118_PATH; + + uint8_t dump[2048]; + bool valid; + + void readEEPROM(); +}; diff --git a/SPDAccessor/SPDAccessor.cpp b/SPDAccessor/SPDAccessor.cpp new file mode 100644 index 0000000..c75fd56 --- /dev/null +++ b/SPDAccessor/SPDAccessor.cpp @@ -0,0 +1,218 @@ +/*---------------------------------------------------------*\ +| SPDAccessor.cpp | +| | +| Access to SPD information on various DIMMs | +| | +| Milan Cermak (krysmanta) 09 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "DDR4DirectAccessor.h" +#include "DDR5DirectAccessor.h" +#include "LogManager.h" +#include "SPDAccessor.h" + +#ifdef __linux__ +#include "EE1004Accessor_Linux.h" +#include "SPD5118Accessor_Linux.h" +#endif + +using namespace std::chrono_literals; + +/*---------------------------------------------------------*\ +| Sources for define values: | +| - https://en.wikipedia.org/wiki/Serial_presence_detect | +| - JEDEC DDR5 Serial Presence Detect (SPD): Table of | +| contents | +\*---------------------------------------------------------*/ +#define BASIC_MEMORY_TYPE_ADDR (0x02) + +#define DDR4_JEDEC_ID_ADDR (0x140) +#define DDR4_PART_NR_START (0x149) +#define DDR4_PART_NR_END (0x15C) +#define DDR4_PART_NR_LEN (DDR4_PART_NR_END - DDR4_PART_NR_START + 1) +#define DDR4_MANUF_SPECIFIC_START (0x161) +#define DDR4_MANUF_SPECIFIC_END (0x17D) +#define DDR4_MANUF_SPECIFIC_LEN (DDR4_MANUF_SPECIFIC_END - DDR4_MANUF_SPECIFIC_START + 1) + +#define DDR5_JEDEC_ID_ADDR (0x200) +#define DDR5_PART_NR_START (0x209) +#define DDR5_PART_NR_END (0x226) +#define DDR5_PART_NR_LEN (DDR5_PART_NR_END - DDR5_PART_NR_START + 1) +#define DDR5_MANUF_SPECIFIC_START (0x22B) +#define DDR5_MANUF_SPECIFIC_END (0x27F) +#define DDR5_MANUF_SPECIFIC_LEN (DDR5_MANUF_SPECIFIC_END - DDR5_MANUF_SPECIFIC_START + 1) + +const char *spd_memory_type_name[] = +{ + "Reserved", + "FPM", + "EDO", + "Nibble", + "SDR", + "Multiplex ROM", + "DDR", + "DDR", + "DDR2", + "FB", + "FB Probe", + "DDR3", + "DDR4", + "Reserved", + "DDR4e", + "LPDDR3", + "LPDDR4", + "LPDDR4X", + "DDR5", + "LPDDR5" +}; + +SPDAccessor::SPDAccessor(i2c_smbus_interface *bus, uint8_t spd_addr) +{ + this->bus = bus; + this->address = spd_addr; +} + +SPDAccessor::~SPDAccessor() +{ +} + +SPDAccessor *SPDAccessor::for_memory_type(SPDMemoryType type, i2c_smbus_interface *bus, uint8_t spd_addr) +{ + /*-----------------------------------------------------*\ + | DDR4 can use DDR4DirectAccessor or EE1004Accessor | + \*-----------------------------------------------------*/ + if(type == SPD_DDR4_SDRAM) + { +#ifdef __linux__ + if(EE1004Accessor::isAvailable(bus, spd_addr)) + { + return(new EE1004Accessor(bus, spd_addr)); + } +#endif + return(new DDR4DirectAccessor(bus, spd_addr)); + } + + /*-----------------------------------------------------*\ + | DDR5 can use DDR5DirectAccessor or SPD5118Accessor | + \*-----------------------------------------------------*/ + if(type == SPD_DDR5_SDRAM) + { +#ifdef __linux__ + if(SPD5118Accessor::isAvailable(bus, spd_addr)) + { + return(new SPD5118Accessor(bus, spd_addr)); + } +#endif + return(new DDR5DirectAccessor(bus, spd_addr)); + } + + return(nullptr); +}; + +std::string SPDAccessor::read_part_nr_at(uint16_t address, std::size_t len) +{ + std::string part_number; + + for(uint16_t i = 0; i < (uint16_t)len; i++) + { + uint16_t spd_addr = address + i; + part_number += (char)this->at(spd_addr); + } + + /*-----------------------------------------------------*\ + | Find the true end of string and truncate it to that | + | point. Part number should be padded with 0x20 | + | (space) for DDR4 (Source: Wikipedia). | + | It may be padded with 0x00 (Source: real-life tests | + | on DDR5 memory). | + | Note: To prevent infinite loop, end_of_string_idx | + | MUST be signed. | + \*-----------------------------------------------------*/ + std::size_t end_of_string_idx = part_number.length(); + + for(; end_of_string_idx > 0; end_of_string_idx--) + { + if((part_number[end_of_string_idx - 1] != '\0') + && (part_number[end_of_string_idx - 1] != ' ')) + { + break; + } + } + part_number = part_number.substr(0, end_of_string_idx + 1); + + return part_number; +} + +/*---------------------------------------------------------*\ +| Internal implementation for specific memory type. | +\*---------------------------------------------------------*/ + +DDR4Accessor::DDR4Accessor(i2c_smbus_interface *bus, uint8_t spd_addr) + : SPDAccessor(bus, spd_addr) +{ +} + +DDR4Accessor::~DDR4Accessor() +{ +} + +SPDMemoryType DDR4Accessor::memory_type() +{ + return((SPDMemoryType)(this->at(BASIC_MEMORY_TYPE_ADDR))); +} + +uint16_t DDR4Accessor::jedec_id() +{ + return((this->at(DDR4_JEDEC_ID_ADDR) << 8) + (this->at(DDR4_JEDEC_ID_ADDR+1) & 0x7f) - 1); +} + +std::string DDR4Accessor::part_number() +{ + return this->read_part_nr_at(DDR4_PART_NR_START, DDR4_PART_NR_LEN); +} + +uint8_t DDR4Accessor::manufacturer_data(uint16_t index) +{ + if(index > DDR4_MANUF_SPECIFIC_LEN-1) + { + return 0; + } + return this->at(DDR4_MANUF_SPECIFIC_START + index); +} + + +DDR5Accessor::DDR5Accessor(i2c_smbus_interface *bus, uint8_t spd_addr) + : SPDAccessor(bus, spd_addr) +{ +} + +DDR5Accessor::~DDR5Accessor() +{ +} + +SPDMemoryType DDR5Accessor::memory_type() +{ + return((SPDMemoryType)(this->at(BASIC_MEMORY_TYPE_ADDR))); +} + +uint16_t DDR5Accessor::jedec_id() +{ + return((this->at(DDR5_JEDEC_ID_ADDR) << 8) + (this->at(DDR5_JEDEC_ID_ADDR+1) & 0x7f) - 1); +} + +std::string DDR5Accessor::part_number() +{ + return this->read_part_nr_at(DDR5_PART_NR_START, DDR5_PART_NR_LEN); +} + +uint8_t DDR5Accessor::manufacturer_data(uint16_t index) +{ + if(index > DDR5_MANUF_SPECIFIC_LEN-1) + { + return 0; + } + return this->at(DDR5_MANUF_SPECIFIC_START + index); +} diff --git a/SPDAccessor/SPDAccessor.h b/SPDAccessor/SPDAccessor.h new file mode 100644 index 0000000..3569595 --- /dev/null +++ b/SPDAccessor/SPDAccessor.h @@ -0,0 +1,66 @@ +/*---------------------------------------------------------*\ +| SPDAccessor.h | +| | +| Access to SPD information on various DIMMs | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "SPDCommon.h" + +#include +#include +#include + +class SPDAccessor +{ + public: + SPDAccessor(i2c_smbus_interface *bus, uint8_t address); + virtual ~SPDAccessor(); + + static SPDAccessor *for_memory_type(SPDMemoryType type, i2c_smbus_interface *bus, uint8_t address); + + virtual SPDMemoryType memory_type() = 0; + virtual uint16_t jedec_id() = 0; + virtual std::string part_number() = 0; + virtual uint8_t manufacturer_data(uint16_t index) = 0; + + virtual SPDAccessor *copy() = 0; + + virtual uint8_t at(uint16_t addr) = 0; + + protected: + i2c_smbus_interface *bus; + uint8_t address; + + std::string read_part_nr_at(uint16_t address, std::size_t len); +}; + +/*---------------------------------------------------------*\ +| Internal implementation for specific memory type. | +\*---------------------------------------------------------*/ + +class DDR4Accessor : public SPDAccessor +{ + public: + DDR4Accessor(i2c_smbus_interface *bus, uint8_t address); + virtual ~DDR4Accessor(); + virtual SPDMemoryType memory_type(); + virtual uint16_t jedec_id(); + virtual std::string part_number(); + virtual uint8_t manufacturer_data(uint16_t index); +}; + +class DDR5Accessor : public SPDAccessor +{ + public: + DDR5Accessor(i2c_smbus_interface *bus, uint8_t address); + virtual ~DDR5Accessor(); + virtual SPDMemoryType memory_type(); + virtual uint16_t jedec_id(); + virtual std::string part_number(); + virtual uint8_t manufacturer_data(uint16_t index); +}; diff --git a/SPDAccessor/SPDCommon.h b/SPDAccessor/SPDCommon.h new file mode 100644 index 0000000..6eb506b --- /dev/null +++ b/SPDAccessor/SPDCommon.h @@ -0,0 +1,56 @@ +/*---------------------------------------------------------*\ +| SPDCommon.h | +| | +| Common definitions for SPD | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "i2c_smbus.h" + +typedef enum +{ + JEDEC_KINGSTON = 0x0117, + JEDEC_CORSAIR = 0x021D, + JEDEC_ADATA = 0x044A, + JEDEC_GSKILL = 0x044C, + JEDEC_TEAMGROUP = 0x046E, + JEDEC_KINGSTON_2 = 0x300F, + JEDEC_KINGSTON_3 = 0x3011, + JEDEC_MUSHKIN = 0x8313, + JEDEC_GIGABYTE = 0x8971, + JEDEC_THERMALTAKE = 0x8A41, + JEDEC_PATRIOT = 0x8501 +} JedecIdentifier; + +typedef enum +{ + SPD_RESERVED = 0, + SPD_FPM_DRAM = 1, + SPD_EDO = 2, + SPD_NIBBLE = 3, + SPD_SDR_SDRAM = 4, + SPD_MUX_ROM = 5, + SPD_DDR_SGRAM = 6, + SPD_DDR_SDRAM = 7, + SPD_DDR2_SDRAM = 8, + SPD_FB_DIMM = 9, + SPD_FB_PROBE = 10, + SPD_DDR3_SDRAM = 11, + SPD_DDR4_SDRAM = 12, + SPD_RESERVED2 = 13, + SPD_DDR4E_SDRAM = 14, + SPD_LPDDR3_SDRAM = 15, + SPD_LPDDR4_SDRAM = 16, + SPD_LPDDR4X_SDRAM = 17, + SPD_DDR5_SDRAM = 18, + SPD_LPDDR5_SDRAM = 19 +} SPDMemoryType; + +#define SPD_IO_DELAY 1ms + +extern const char *spd_memory_type_name[]; diff --git a/SPDAccessor/SPDDetector.cpp b/SPDAccessor/SPDDetector.cpp new file mode 100644 index 0000000..0ae1e9b --- /dev/null +++ b/SPDAccessor/SPDDetector.cpp @@ -0,0 +1,140 @@ +/*---------------------------------------------------------*\ +| SPDDetector.cpp | +| | +| Detector for DRAM modules using SPD information | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "DDR4DirectAccessor.h" +#include "DDR5DirectAccessor.h" +#include "LogManager.h" +#include "SPDDetector.h" + +#ifdef __linux__ +#include "EE1004Accessor_Linux.h" +#include "SPD5118Accessor_Linux.h" +#endif + +SPDDetector::SPDDetector(i2c_smbus_interface *bus, uint8_t address, SPDMemoryType mem_type = SPD_RESERVED) + : bus(bus), address(address), mem_type(mem_type), valid(false) +{ + detect_memory_type(); +} + +bool SPDDetector::is_valid() const +{ + return(valid); +} + +SPDMemoryType SPDDetector::memory_type() const +{ + return(mem_type); +} + +void SPDDetector::detect_memory_type() +{ + SPDAccessor *accessor; + + /*---------------------------------------------------------*\ + | On Linux, attempt to use the ee1004 or spd5118 drivers to | + | access SPD on DDR4 and DDR5, respectively | + \*---------------------------------------------------------*/ +#ifdef __linux__ + if(EE1004Accessor::isAvailable(bus, address)) + { + LOG_DEBUG("[SPDDetector] Probing DRAM using EE1004 Accessor on bus %d, address 0x%02x", bus->bus_id, address); + accessor = new EE1004Accessor(bus, address); + } + else if(SPD5118Accessor::isAvailable(bus, address)) + { + LOG_DEBUG("[SPDDetector] Probing DRAM using SPD5118 Accessor on bus %d, address 0x%02x", bus->bus_id, address); + accessor = new SPD5118Accessor(bus, address); + } + else +#endif + /*---------------------------------------------------------*\ + | Otherwise, access the SPD using a direct accessor using | + | i2c for DDR4 and DDR5 | + \*---------------------------------------------------------*/ + if((mem_type == SPD_RESERVED + || mem_type == SPD_DDR4_SDRAM + || mem_type == SPD_DDR4E_SDRAM + || mem_type == SPD_LPDDR4_SDRAM + || mem_type == SPD_LPDDR4X_SDRAM) + && DDR4DirectAccessor::isAvailable(bus, address)) + { + LOG_DEBUG("[SPDDetector] Probing DRAM using DDR4 Direct Accessor on bus %d, address 0x%02x", bus->bus_id, address); + accessor = new DDR4DirectAccessor(bus, address); + } + else if((mem_type == SPD_RESERVED + || mem_type == SPD_DDR5_SDRAM + || mem_type == SPD_LPDDR5_SDRAM) + && DDR5DirectAccessor::isAvailable(bus, address)) + { + LOG_DEBUG("[SPDDetector] Probing DRAM using DDR5 Direct Accessor on bus %d, address 0x%02x", bus->bus_id, address); + accessor = new DDR5DirectAccessor(bus, address); + } + /*---------------------------------------------------------*\ + | Otherwise, probe the SPD directly using i2c, probably an | + | older system than DDR4 | + \*---------------------------------------------------------*/ + else if(mem_type == SPD_RESERVED) + { + LOG_DEBUG("[SPDDetector] Probing DRAM older than DDR4 on bus %d, address 0x%02x", bus->bus_id, address); + + int value = bus->i2c_smbus_read_byte_data(address, 0x02); + + if(value < 0) + { + valid = false; + } + else + { + mem_type = (SPDMemoryType)value; + + /*-------------------------------------------------*\ + | We are only interested in DDR4 and DDR5 systems | + \*-------------------------------------------------*/ + valid = (mem_type == SPD_DDR4_SDRAM + || mem_type == SPD_DDR4E_SDRAM + || mem_type == SPD_LPDDR4_SDRAM + || mem_type == SPD_LPDDR4X_SDRAM + || mem_type == SPD_DDR5_SDRAM + || mem_type == SPD_LPDDR5_SDRAM); + } + + return; + } + /*---------------------------------------------------------*\ + | If memory type could not be determined, detection failed | + \*---------------------------------------------------------*/ + else + { + LOG_DEBUG("[SPDDetector] Memory type could not be determined for bus %d, address 0x%02x", bus->bus_id, address); + valid = false; + return; + } + + /*---------------------------------------------------------*\ + | If an accessor was created, save the memory type | + \*---------------------------------------------------------*/ + valid = true; + mem_type = accessor->memory_type(); + + /*---------------------------------------------------------*\ + | Delete the accessor | + \*---------------------------------------------------------*/ + delete accessor; +} + +uint8_t SPDDetector::spd_address() const +{ + return(this->address); +} + +i2c_smbus_interface *SPDDetector::smbus() const +{ + return(this->bus); +} diff --git a/SPDAccessor/SPDDetector.h b/SPDAccessor/SPDDetector.h new file mode 100644 index 0000000..9ab2222 --- /dev/null +++ b/SPDAccessor/SPDDetector.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| SPDDetector.h | +| | +| Detector for DRAM modules using SPD information | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "SPDAccessor.h" +#include "SPDCommon.h" + +class SPDDetector +{ + public: + SPDDetector(i2c_smbus_interface *bus, uint8_t address, SPDMemoryType mem_type); + + bool is_valid() const; + SPDMemoryType memory_type() const; + uint8_t spd_address() const; + i2c_smbus_interface *smbus() const; + + private: + i2c_smbus_interface *bus; + uint8_t address; + SPDMemoryType mem_type; + bool valid; + + void detect_memory_type(); +}; diff --git a/SPDAccessor/SPDWrapper.cpp b/SPDAccessor/SPDWrapper.cpp new file mode 100644 index 0000000..51b81f1 --- /dev/null +++ b/SPDAccessor/SPDWrapper.cpp @@ -0,0 +1,141 @@ +/*---------------------------------------------------------*\ +| SPDWrapper.cpp | +| | +| Wrapper for DRAM modules using SPD information | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "SPDWrapper.h" + +SPDWrapper::SPDWrapper(const SPDWrapper &wrapper) +{ + if(wrapper.accessor != nullptr) + { + this->accessor = wrapper.accessor->copy(); + } + this->addr = wrapper.addr; + this->mem_type = wrapper.mem_type; + + /*-----------------------------------------------------*\ + | Read the JEDEC ID and cache its value | + | This saves a significant amount of time over reading | + | the JEDEC ID each time it is accessed | + \*-----------------------------------------------------*/ + if(accessor == nullptr) + { + jedec_id_val = 0x0000; + } + else + { + jedec_id_val = accessor->jedec_id(); + } +} + +SPDWrapper::SPDWrapper(const SPDDetector &detector) +{ + this->addr = detector.spd_address(); + this->mem_type = detector.memory_type(); + + /*-----------------------------------------------------*\ + | Allocate a new accessor | + \*-----------------------------------------------------*/ + this->accessor = SPDAccessor::for_memory_type(this->mem_type, detector.smbus(), this->addr); + + /*-----------------------------------------------------*\ + | Read the JEDEC ID and cache its value | + | This saves a significant amount of time over reading | + | the JEDEC ID each time it is accessed | + \*-----------------------------------------------------*/ + if(accessor == nullptr) + { + jedec_id_val = 0x0000; + } + else + { + jedec_id_val = accessor->jedec_id(); + } +} + +SPDWrapper::~SPDWrapper() +{ + delete accessor; +} + +SPDMemoryType SPDWrapper::memory_type() +{ + return mem_type; +} + +uint8_t SPDWrapper::address() +{ + return this->addr; +} + +int SPDWrapper::index() +{ + return this->addr - 0x50; +} + +uint16_t SPDWrapper::jedec_id() +{ + return jedec_id_val; +} + +std::string SPDWrapper::part_number() +{ + if(accessor == nullptr) + { + return std::string(); + } + return accessor->part_number(); +} + +uint8_t SPDWrapper::manufacturer_data(uint16_t index) +{ + if(accessor == nullptr) + { + return 0x00; + } + return accessor->manufacturer_data(index); +} + +/*---------------------------------------------------------*\ +| Helper functions for easier collection handling. | +\*---------------------------------------------------------*/ + +bool is_jedec_in_slots(std::vector &slots, uint16_t jedec_id) +{ + /*-----------------------------------------------------*\ + | Search through all SPD slots to see if any have the | + | desired JEDEC ID | + \*-----------------------------------------------------*/ + for(SPDWrapper &slot : slots) + { + if(slot.jedec_id() == jedec_id) + { + return true; + } + } + return false; +} + +std::vector slots_with_jedec(std::vector &slots, uint16_t jedec_id) +{ + std::vector matching_slots; + + /*-----------------------------------------------------*\ + | Search through all SPD slots and build a list of all | + | slots matching the desired JEDEC ID | + \*-----------------------------------------------------*/ + for(SPDWrapper &slot : slots) + { + if(slot.jedec_id() == jedec_id) + { + matching_slots.push_back(&slot); + } + } + + return matching_slots; +} diff --git a/SPDAccessor/SPDWrapper.h b/SPDAccessor/SPDWrapper.h new file mode 100644 index 0000000..88739ee --- /dev/null +++ b/SPDAccessor/SPDWrapper.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------*\ +| SPDWrapper.h | +| | +| Wrapper for DRAM modules using SPD information | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "SPDAccessor.h" +#include "SPDCommon.h" +#include "SPDDetector.h" + +class SPDWrapper +{ + public: + SPDWrapper(const SPDWrapper &wrapper); + SPDWrapper(const SPDDetector &detector); + ~SPDWrapper(); + + uint8_t address(); + SPDMemoryType memory_type(); + int index(); + uint16_t jedec_id(); + std::string part_number(); + uint8_t manufacturer_data(uint16_t index); + + private: + SPDAccessor *accessor = nullptr; + uint8_t addr; + uint16_t jedec_id_val; + SPDMemoryType mem_type; +}; + +/*-------------------------------------------------------------------------*\ +| Helper functions for easier collection handling. | +\*-------------------------------------------------------------------------*/ + +bool is_jedec_in_slots(std::vector &slots, uint16_t jedec_id); +std::vector slots_with_jedec(std::vector &slots, uint16_t jedec_id); diff --git a/SettingsManager.cpp b/SettingsManager.cpp new file mode 100644 index 0000000..8a395a6 --- /dev/null +++ b/SettingsManager.cpp @@ -0,0 +1,129 @@ +/*---------------------------------------------------------*\ +| SettingsManager.cpp | +| | +| OpenRGB Settings Manager maintains a list of application| +| settings in JSON format. Other components may register | +| settings with this class and store/load values. | +| | +| Adam Honse (CalcProgrammer1) 04 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "SettingsManager.h" +#include "LogManager.h" + +SettingsManager::SettingsManager() +{ + config_found = false; +} + +SettingsManager::~SettingsManager() +{ + +} + +json SettingsManager::GetSettings(std::string settings_key) +{ + /*-----------------------------------------------------*\ + | Check to see if the key exists in the settings store | + | and return the settings associated with the key if it | + | exists. We lock the mutex to protect the value from | + | changing while data is being read and copy before | + | unlocking. | + \*-----------------------------------------------------*/ + json result; + + mutex.lock(); + if(settings_data.contains(settings_key)) + { + result = settings_data[settings_key]; + } + + mutex.unlock(); + + return result; +} + +void SettingsManager::SetSettings(std::string settings_key, json new_settings) +{ + mutex.lock(); + settings_data[settings_key] = new_settings; + mutex.unlock(); +} + +void SettingsManager::LoadSettings(const filesystem::path& filename) +{ + /*-----------------------------------------------------*\ + | Clear any stored settings before loading | + \*-----------------------------------------------------*/ + mutex.lock(); + + settings_data.clear(); + + /*-----------------------------------------------------*\ + | Store settings filename, so we can save to it later | + \*-----------------------------------------------------*/ + settings_filename = filename; + + /*-----------------------------------------------------*\ + | Open input file in binary mode | + \*-----------------------------------------------------*/ + config_found = filesystem::exists(filename); + if(config_found) + { + std::ifstream settings_file(settings_filename, std::ios::in | std::ios::binary); + + /*-------------------------------------------------*\ + | Read settings into JSON store | + \*-------------------------------------------------*/ + if(settings_file) + { + try + { + settings_file >> settings_data; + } + catch(const std::exception& e) + { + /*-----------------------------------------*\ + | If an exception was caught, that means | + | the JSON parsing failed. Clear out any | + | data in the store as it is corrupt. We | + | could attempt a reload for backup | + | location | + \*-----------------------------------------*/ + LOG_ERROR("[SettingsManager] JSON parsing failed: %s", e.what()); + + settings_data.clear(); + } + } + + settings_file.close(); + } + + mutex.unlock(); +} + +void SettingsManager::SaveSettings() +{ + mutex.lock(); + std::ofstream settings_file(settings_filename, std::ios::out | std::ios::binary); + + if(settings_file) + { + try + { + settings_file << settings_data.dump(4); + } + catch(const std::exception& e) + { + LOG_ERROR("[SettingsManager] Cannot write to file: %s", e.what()); + } + + settings_file.close(); + } + mutex.unlock(); +} diff --git a/SettingsManager.h b/SettingsManager.h new file mode 100644 index 0000000..325907f --- /dev/null +++ b/SettingsManager.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------*\ +| SettingsManager.h | +| | +| OpenRGB Settings Manager maintains a list of application| +| settings in JSON format. Other components may register | +| settings with this class and store/load values. | +| | +| Adam Honse (CalcProgrammer1) 04 Nov 2020 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "filesystem.h" + +using json = nlohmann::json; + +class SettingsManagerInterface +{ +public: + virtual json GetSettings(std::string settings_key) = 0; + virtual void SetSettings(std::string settings_key, json new_settings) = 0; + + virtual void LoadSettings(const filesystem::path& filename) = 0; + virtual void SaveSettings() = 0; + +protected: + virtual ~SettingsManagerInterface() {}; +}; + +class SettingsManager: public SettingsManagerInterface +{ +public: + SettingsManager(); + ~SettingsManager(); + + json GetSettings(std::string settings_key) override; + void SetSettings(std::string settings_key, json new_settings) override; + + void LoadSettings(const filesystem::path& filename) override; + void SaveSettings() override; + +private: + json settings_data; + json settings_prototype; + filesystem::path settings_filename; + std::mutex mutex; + bool config_found; +}; diff --git a/StringUtils.cpp b/StringUtils.cpp new file mode 100644 index 0000000..c337cb8 --- /dev/null +++ b/StringUtils.cpp @@ -0,0 +1,130 @@ +/*---------------------------------------------------------*\ +| StringUtils.cpp | +| | +| String utility functions | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +/*---------------------------------------------------------*\ +| codecvt is deprecated, but there's no replacement so we | +| can ignore the warnings | +\*---------------------------------------------------------*/ +#if defined(_MSC_VER) +#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(__clang__) +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +#include +#include +#include +#include +#include "StringUtils.h" + +const char* StringUtils::wchar_to_char(const wchar_t* pwchar) +{ + if(pwchar == nullptr) + { + return ""; + } + + /*-----------------------------------------------------*\ + | Get the number of characters in the string. | + \*-----------------------------------------------------*/ + int currentCharIndex = 0; + char currentChar = (char)pwchar[currentCharIndex]; + + while(currentChar != '\0') + { + currentCharIndex++; + currentChar = (char)pwchar[currentCharIndex]; + } + + const int charCount = currentCharIndex + 1; + + /*-----------------------------------------------------*\ + | Allocate a new block of memory size char (1 byte) | + | instead of wide char (2 bytes) | + \*-----------------------------------------------------*/ + char* filePathC = (char*)malloc(sizeof(char) * charCount); + + for(int i = 0; i < charCount; i++) + { + /*-------------------------------------------------*\ + | Convert to char (1 byte) | + \*-------------------------------------------------*/ + char character = (char)pwchar[i]; + + *filePathC = character; + + filePathC += sizeof(char); + + } + + filePathC += '\0'; + + filePathC -= (sizeof(char) * charCount); + + return(filePathC); +} + +std::string StringUtils::wchar_to_string(const wchar_t* pwchar) +{ + if(pwchar == nullptr) + { + return std::string(); + } + + return wstring_to_string(std::wstring(pwchar)); +} + +std::string StringUtils::wstring_to_string(const std::wstring wstring) +{ + std::wstring_convert, wchar_t> converter; + + return(converter.to_bytes(wstring)); +} + +std::string StringUtils::u16string_to_string(const std::u16string wstring) +{ + std::wstring_convert,char16_t> converter; + + return(converter.to_bytes(wstring)); +} + +const std::string StringUtils::remove_null_terminating_chars(std::string input) +{ + while(!input.empty() && input.back() == 0) + { + input.pop_back(); + } + + return(input); +} + +std::string StringUtils::u32int_to_hexString(unsigned int value) +{ + char hex_str[20] = {0}; + snprintf(hex_str, sizeof(hex_str), "%X", value); + return std::string(hex_str); +} + +std::string StringUtils::normalize_hex_id(const std::string& id) +{ + std::string out; + out.reserve(id.size()); + + for(char c : id) + { + if(c != '-') + { + out += (char)tolower((unsigned char)c); + } + } + + return out; +} diff --git a/StringUtils.h b/StringUtils.h new file mode 100644 index 0000000..60484e8 --- /dev/null +++ b/StringUtils.h @@ -0,0 +1,24 @@ +/*---------------------------------------------------------*\ +| StringUtils.h | +| | +| String utility functions | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include + +class StringUtils +{ +public: + static const char* wchar_to_char(const wchar_t* pwchar); + static std::string wchar_to_string(const wchar_t* pwchar); + static std::string wstring_to_string(const std::wstring wstring); + static std::string u16string_to_string(const std::u16string wstring); + static const std::string remove_null_terminating_chars(std::string input); + static std::string u32int_to_hexString(unsigned int value); + static std::string normalize_hex_id(const std::string& id); +}; diff --git a/SuspendResume/SuspendResume.h b/SuspendResume/SuspendResume.h new file mode 100644 index 0000000..0bedb6f --- /dev/null +++ b/SuspendResume/SuspendResume.h @@ -0,0 +1,31 @@ +/*---------------------------------------------------------*\ +| SuspendResume.h | +| | +| Suspend/resume common implementation | +| | +| Zach Deibert (zachdeibert) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +class SuspendResumeListenerBase +{ +protected: + virtual void OnSuspend() = 0; + virtual void OnResume() = 0; +}; + +#ifdef _WIN32 +#include "SuspendResume_Windows.h" +#endif + +#ifdef __APPLE__ +#include "SuspendResume_MacOS.h" +#endif + +#if defined(__linux__) || defined(__FreeBSD__) +#include "SuspendResume_Linux_FreeBSD.h" +#endif diff --git a/SuspendResume/SuspendResume_Linux_FreeBSD.cpp b/SuspendResume/SuspendResume_Linux_FreeBSD.cpp new file mode 100644 index 0000000..4e2bb5f --- /dev/null +++ b/SuspendResume/SuspendResume_Linux_FreeBSD.cpp @@ -0,0 +1,39 @@ +/*---------------------------------------------------------*\ +| SuspendResume_Linux_FreeBSD.cpp | +| | +| Suspend/resume Linux/FreeBSD implementation | +| | +| Zach Deibert (zachdeibert) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SuspendResume.h" + +SuspendResumeLoginManager::SuspendResumeLoginManager(SuspendResumeListener *srl) : srl(srl), bus(QDBusConnection::systemBus()) +{ + bus.connect("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "PrepareForSleep", this, SLOT(PrepareForSleep(bool))); +} + +SuspendResumeLoginManager::~SuspendResumeLoginManager() +{ + bus.disconnect("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "PrepareForSleep", this, SLOT(PrepareForSleep(bool))); +} + +void SuspendResumeLoginManager::PrepareForSleep(bool mode) +{ + if(mode) + { + srl->OnSuspend(); + } + else + { + srl->OnResume(); + } +} + +SuspendResumeListener::SuspendResumeListener() : login_manager(this) +{ +} diff --git a/SuspendResume/SuspendResume_Linux_FreeBSD.h b/SuspendResume/SuspendResume_Linux_FreeBSD.h new file mode 100644 index 0000000..f47d1a5 --- /dev/null +++ b/SuspendResume/SuspendResume_Linux_FreeBSD.h @@ -0,0 +1,45 @@ +/*---------------------------------------------------------*\ +| SuspendResume_Linux_FreeBSD.h | +| | +| Suspend/resume Linux/FreeBSD implementation | +| | +| Zach Deibert (zachdeibert) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "SuspendResume.h" + +class SuspendResumeListener; + +class SuspendResumeLoginManager : public QObject +{ + Q_OBJECT + +public: + SuspendResumeLoginManager(SuspendResumeListener *srl); + ~SuspendResumeLoginManager(); + +public slots: + void PrepareForSleep(bool mode); + +private: + SuspendResumeListener *srl; + QDBusConnection bus; +}; + +class SuspendResumeListener : public SuspendResumeListenerBase +{ + friend class SuspendResumeLoginManager; + +protected: + SuspendResumeListener(); + +private: + SuspendResumeLoginManager login_manager; +}; diff --git a/SuspendResume/SuspendResume_MacOS.cpp b/SuspendResume/SuspendResume_MacOS.cpp new file mode 100644 index 0000000..c4bee9a --- /dev/null +++ b/SuspendResume/SuspendResume_MacOS.cpp @@ -0,0 +1,45 @@ +/*---------------------------------------------------------*\ +| SuspendResume_MacOS.cpp | +| | +| Suspend/resume MacOS implementation | +| | +| Zach Deibert (zachdeibert) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "SuspendResume.h" +#include "IOKit/pwr_mgt/IOPMLib.h" +#include "IOKit/IOMessage.h" + +SuspendResumeListener::SuspendResumeListener() +{ + root_port = IORegisterForSystemPower(this, &port_ref, &SuspendResumeListener::SystemPowerCallback, ¬ifier); + CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(port_ref), kCFRunLoopCommonModes); +} + +SuspendResumeListener::~SuspendResumeListener() +{ + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(port_ref), kCFRunLoopCommonModes); + IODeregisterForSystemPower(¬ifier); + IOServiceClose(root_port); + IONotificationPortDestroy(port_ref); +} + +void SuspendResumeListener::SystemPowerCallback(void *refcon, io_service_t service, uint32_t message_type, void *message_argument) +{ + (void)service; + SuspendResumeListener *spl = (SuspendResumeListener *)refcon; + switch(message_type) + { + case kIOMessageSystemWillSleep: + spl->OnSuspend(); + IOAllowPowerChange(spl->root_port, (intptr_t)message_argument); + break; + case kIOMessageSystemHasPoweredOn: + spl->OnResume(); + break; + } +} diff --git a/SuspendResume/SuspendResume_MacOS.h b/SuspendResume/SuspendResume_MacOS.h new file mode 100644 index 0000000..5fd2b99 --- /dev/null +++ b/SuspendResume/SuspendResume_MacOS.h @@ -0,0 +1,31 @@ +/*---------------------------------------------------------*\ +| SuspendResume_MacOS.h | +| | +| Suspend/resume MacOS implementation | +| | +| Zach Deibert (zachdeibert) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "SuspendResume.h" +#include "IOKit/pwr_mgt/IOPMLib.h" +#include "IOKit/IOMessage.h" + +class SuspendResumeListener : public SuspendResumeListenerBase +{ +protected: + SuspendResumeListener(); + virtual ~SuspendResumeListener(); + +private: + static void SystemPowerCallback(void *refcon, io_service_t service, uint32_t message_type, void *message_argument); + + io_connect_t root_port; + IONotificationPortRef port_ref; + io_object_t notifier; +}; diff --git a/SuspendResume/SuspendResume_Windows.cpp b/SuspendResume/SuspendResume_Windows.cpp new file mode 100644 index 0000000..09c1f41 --- /dev/null +++ b/SuspendResume/SuspendResume_Windows.cpp @@ -0,0 +1,48 @@ +/*---------------------------------------------------------*\ +| SuspendResume_Windows.cpp | +| | +| Suspend/resume Windows implementation | +| | +| Zach Deibert (zachdeibert) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "SuspendResume.h" +#include "windows.h" + +SuspendResumeListener::SuspendResumeListener() +{ + QCoreApplication::instance()->installNativeEventFilter(this); +} + +SuspendResumeListener::~SuspendResumeListener() +{ + QCoreApplication::instance()->removeNativeEventFilter(this); +} + +bool SuspendResumeListener::nativeEventFilter(const QByteArray &event_type, void *message, NEFResultType *result) +{ + (void)result; + if(event_type == "windows_generic_MSG") + { + switch(((MSG *)message)->message) + { + case WM_POWERBROADCAST: + switch(((MSG *)message)->wParam) + { + case PBT_APMSUSPEND: + OnSuspend(); + break; + case PBT_APMRESUMEAUTOMATIC: + OnResume(); + break; + } + break; + } + } + return false; +} diff --git a/SuspendResume/SuspendResume_Windows.h b/SuspendResume/SuspendResume_Windows.h new file mode 100644 index 0000000..77f5135 --- /dev/null +++ b/SuspendResume/SuspendResume_Windows.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| SuspendResume_Windows.h | +| | +| Suspend/resume Windows implementation | +| | +| Zach Deibert (zachdeibert) 12 Nov 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include "SuspendResume.h" + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#define NEFResultType long +#else +#define NEFResultType qintptr +#endif + +class SuspendResumeListener : public SuspendResumeListenerBase, private QAbstractNativeEventFilter +{ +protected: + SuspendResumeListener(); + virtual ~SuspendResumeListener(); + +private: + bool nativeEventFilter(const QByteArray &event_type, void *message, NEFResultType *result); +}; diff --git a/cli.cpp b/cli.cpp new file mode 100644 index 0000000..84629ff --- /dev/null +++ b/cli.cpp @@ -0,0 +1,1806 @@ +/*---------------------------------------------------------*\ +| cli.cpp | +| | +| OpenRGB command line interface | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "AutoStart.h" +#include "filesystem.h" +#include "ProfileManager.h" +#include "ResourceManager.h" +#include "RGBController.h" +#include "i2c_smbus.h" +#include "NetworkClient.h" +#include "NetworkServer.h" +#include "LogManager.h" +#include "Colors.h" + +/*-------------------------------------------------------------*\ +| Quirk for MSVC; which doesn't support this case-insensitive | +| function | +\*-------------------------------------------------------------*/ +#ifdef _WIN32 +#include + #define strcasecmp _strcmpi +#endif + +using namespace std::chrono_literals; + +static std::string profile_save_filename = ""; +const unsigned int brightness_percentage = 100; +const unsigned int speed_percentage = 100; + +static int preserve_argc = 0; +static char** preserve_argv = nullptr; + +enum +{ + RET_FLAG_PRINT_HELP = 1, + RET_FLAG_START_GUI = 2, + RET_FLAG_I2C_TOOLS = 4, + RET_FLAG_START_MINIMIZED = 8, + RET_FLAG_NO_DETECT = 16, + RET_FLAG_CLI_POST_DETECTION = 32, + RET_FLAG_START_SERVER = 64, + RET_FLAG_NO_AUTO_CONNECT = 128, +}; + +struct DeviceOptions +{ + int device; + int zone = -1; + std::vector> colors; + std::string mode; + unsigned int speed = 100; + unsigned int brightness = 100; + unsigned int size; + bool random_colors = false; + bool hasSize = false; + bool hasOption = false; +}; + +struct ServerOptions +{ + bool start = false; + unsigned short port = OPENRGB_SDK_PORT; +}; + +struct Options +{ + std::vector devices; + + /*---------------------------------------------------------*\ + | If hasDevice is false, devices above is empty and | + | allDeviceOptions shall be applied to all available devices| + | except in the case that a profile was loaded. | + \*---------------------------------------------------------*/ + bool hasDevice = false; + bool profile_loaded = false; + DeviceOptions allDeviceOptions; + ServerOptions servOpts; +}; + +/*---------------------------------------------------------------------------------------------------------*\ +| Support a common subset of human colors; for easier typing: https://www.w3.org/TR/css-color-3/#svg-color | +\*---------------------------------------------------------------------------------------------------------*/ +struct HumanColors { uint32_t rgb; const char* keyword; } static const human_colors[] = +{ + { COLOR_BLACK, "black" }, + { COLOR_NAVY, "navy" }, + { COLOR_DARKBLUE, "darkblue" }, + { COLOR_MEDIUMBLUE, "mediumblue" }, + { COLOR_BLUE, "blue" }, + { COLOR_DARKGREEN, "darkgreen" }, + { COLOR_GREEN, "green" }, + { COLOR_TEAL, "teal" }, + { COLOR_DARKCYAN, "darkcyan" }, + { COLOR_DEEPSKYBLUE, "deepskyblue" }, + { COLOR_DARKTURQUOISE, "darkturquoise" }, + { COLOR_MEDIUMSPRINGGREEN, "mediumspringgreen" }, + { COLOR_LIME, "lime" }, + { COLOR_SPRINGGREEN, "springgreen" }, + { COLOR_AQUA, "aqua" }, + { COLOR_CYAN, "cyan" }, + { COLOR_MIDNIGHTBLUE, "midnightblue" }, + { COLOR_DODGERBLUE, "dodgerblue" }, + { COLOR_LIGHTSEAGREEN, "lightseagreen" }, + { COLOR_FORESTGREEN, "forestgreen" }, + { COLOR_SEAGREEN, "seagreen" }, + { COLOR_DARKSLATEGRAY, "darkslategray" }, + { COLOR_DARKSLATEGREY, "darkslategrey" }, + { COLOR_LIMEGREEN, "limegreen" }, + { COLOR_MEDIUMSEAGREEN, "mediumseagreen" }, + { COLOR_TURQUOISE, "turquoise" }, + { COLOR_ROYALBLUE, "royalblue" }, + { COLOR_STEELBLUE, "steelblue" }, + { COLOR_DARKSLATEBLUE, "darkslateblue" }, + { COLOR_MEDIUMTURQUOISE, "mediumturquoise" }, + { COLOR_INDIGO, "indigo" }, + { COLOR_DARKOLIVEGREEN, "darkolivegreen" }, + { COLOR_CADETBLUE, "cadetblue" }, + { COLOR_CORNFLOWERBLUE, "cornflowerblue" }, + { COLOR_MEDIUMAQUAMARINE, "mediumaquamarine" }, + { COLOR_DIMGRAY, "dimgray" }, + { COLOR_DIMGREY, "dimgrey" }, + { COLOR_SLATEBLUE, "slateblue" }, + { COLOR_OLIVEDRAB, "olivedrab" }, + { COLOR_SLATEGRAY, "slategray" }, + { COLOR_SLATEGREY, "slategrey" }, + { COLOR_LIGHTSLATEGRAY, "lightslategray" }, + { COLOR_LIGHTSLATEGREY, "lightslategrey" }, + { COLOR_MEDIUMSLATEBLUE, "mediumslateblue" }, + { COLOR_LAWNGREEN, "lawngreen" }, + { COLOR_CHARTREUSE, "chartreuse" }, + { COLOR_AQUAMARINE, "aquamarine" }, + { COLOR_MAROON, "maroon" }, + { COLOR_PURPLE, "purple" }, + { COLOR_ELECTRIC_ULTRAMARINE, "electricultramarine" }, + { COLOR_OLIVE, "olive" }, + { COLOR_GRAY, "gray" }, + { COLOR_GREY, "grey" }, + { COLOR_SKYBLUE, "skyblue" }, + { COLOR_LIGHTSKYBLUE, "lightskyblue" }, + { COLOR_BLUEVIOLET, "blueviolet" }, + { COLOR_DARKRED, "darkred" }, + { COLOR_DARKMAGENTA, "darkmagenta" }, + { COLOR_SADDLEBROWN, "saddlebrown" }, + { COLOR_DARKSEAGREEN, "darkseagreen" }, + { COLOR_LIGHTGREEN, "lightgreen" }, + { COLOR_MEDIUMPURPLE, "mediumpurple" }, + { COLOR_DARKVIOLET, "darkviolet" }, + { COLOR_PALEGREEN, "palegreen" }, + { COLOR_DARKORCHID, "darkorchid" }, + { COLOR_YELLOWGREEN, "yellowgreen" }, + { COLOR_SIENNA, "sienna" }, + { COLOR_BROWN, "brown" }, + { COLOR_DARKGRAY, "darkgray" }, + { COLOR_DARKGREY, "darkgrey" }, + { COLOR_LIGHTBLUE, "lightblue" }, + { COLOR_GREENYELLOW, "greenyellow" }, + { COLOR_PALETURQUOISE, "paleturquoise" }, + { COLOR_LIGHTSTEELBLUE, "lightsteelblue" }, + { COLOR_POWDERBLUE, "powderblue" }, + { COLOR_FIREBRICK, "firebrick" }, + { COLOR_DARKGOLDENROD, "darkgoldenrod" }, + { COLOR_MEDIUMORCHID, "mediumorchid" }, + { COLOR_ROSYBROWN, "rosybrown" }, + { COLOR_DARKKHAKI, "darkkhaki" }, + { COLOR_SILVER, "silver" }, + { COLOR_MEDIUMVIOLETRED, "mediumvioletred" }, + { COLOR_INDIANRED, "indianred" }, + { COLOR_PERU, "peru" }, + { COLOR_CHOCOLATE, "chocolate" }, + { COLOR_TAN, "tan" }, + { COLOR_LIGHTGRAY, "lightgray" }, + { COLOR_LIGHTGREY, "lightgrey" }, + { COLOR_THISTLE, "thistle" }, + { COLOR_ORCHID, "orchid" }, + { COLOR_GOLDENROD, "goldenrod" }, + { COLOR_PALEVIOLETRED, "palevioletred" }, + { COLOR_CRIMSON, "crimson" }, + { COLOR_GAINSBORO, "gainsboro" }, + { COLOR_PLUM, "plum" }, + { COLOR_BURLYWOOD, "burlywood" }, + { COLOR_LIGHTCYAN, "lightcyan" }, + { COLOR_LAVENDER, "lavender" }, + { COLOR_DARKSALMON, "darksalmon" }, + { COLOR_VIOLET, "violet" }, + { COLOR_PALEGOLDENROD, "palegoldenrod" }, + { COLOR_LIGHTCORAL, "lightcoral" }, + { COLOR_KHAKI, "khaki" }, + { COLOR_ALICEBLUE, "aliceblue" }, + { COLOR_HONEYDEW, "honeydew" }, + { COLOR_AZURE, "azure" }, + { COLOR_SANDYBROWN, "sandybrown" }, + { COLOR_WHEAT, "wheat" }, + { COLOR_BEIGE, "beige" }, + { COLOR_WHITESMOKE, "whitesmoke" }, + { COLOR_MINTCREAM, "mintcream" }, + { COLOR_GHOSTWHITE, "ghostwhite" }, + { COLOR_SALMON, "salmon" }, + { COLOR_ANTIQUEWHITE, "antiquewhite" }, + { COLOR_LINEN, "linen" }, + { COLOR_LIGHTGOLDENRODYELLOW, "lightgoldenrodyellow" }, + { COLOR_OLDLACE, "oldlace" }, + { COLOR_RED, "red" }, + { COLOR_FUCHSIA, "fuchsia" }, + { COLOR_MAGENTA, "magenta" }, + { COLOR_DEEPPINK, "deeppink" }, + { COLOR_ORANGERED, "orangered" }, + { COLOR_TOMATO, "tomato" }, + { COLOR_HOTPINK, "hotpink" }, + { COLOR_CORAL, "coral" }, + { COLOR_DARKORANGE, "darkorange" }, + { COLOR_LIGHTSALMON, "lightsalmon" }, + { COLOR_ORANGE, "orange" }, + { COLOR_LIGHTPINK, "lightpink" }, + { COLOR_PINK, "pink" }, + { COLOR_GOLD, "gold" }, + { COLOR_PEACHPUFF, "peachpuff" }, + { COLOR_NAVAJOWHITE, "navajowhite" }, + { COLOR_MOCCASIN, "moccasin" }, + { COLOR_BISQUE, "bisque" }, + { COLOR_MISTYROSE, "mistyrose" }, + { COLOR_BLANCHEDALMOND, "blanchedalmond" }, + { COLOR_PAPAYAWHIP, "papayawhip" }, + { COLOR_LAVENDERBLUSH, "lavenderblush" }, + { COLOR_SEASHELL, "seashell" }, + { COLOR_CORNSILK, "cornsilk" }, + { COLOR_LEMONCHIFFON, "lemonchiffon" }, + { COLOR_FLORALWHITE, "floralwhite" }, + { COLOR_SNOW, "snow" }, + { COLOR_YELLOW, "yellow" }, + { COLOR_LIGHTYELLOW, "lightyellow" }, + { COLOR_IVORY, "ivory" }, + { COLOR_WHITE, "white" }, + { 0, NULL } +}; + +bool ParseColors(std::string colors_string, DeviceOptions *options) +{ + while (colors_string.length() > 0) + { + size_t rgb_end = colors_string.find_first_of(','); + std::string color = colors_string.substr(0, rgb_end); + int32_t rgb = 0; + + bool parsed = false; + + if (color.length() <= 0) + break; + + /*-----------------------------------------------------------------*\ + | This will set correct colour mode for modes with a | + | MODE_COLORS_RANDOM else generate a random colour from the | + | human_colors list above | + \*-----------------------------------------------------------------*/ + if (color == "random") + { + options->random_colors = true; + srand((unsigned int)time(NULL)); + int index = rand() % (sizeof(human_colors) / sizeof(human_colors[0])) + 1; //Anything other than black + rgb = human_colors[index].rgb; + parsed = true; + } + else + { + /* swy: (A) try interpreting it as text; as human keywords, otherwise strtoul() will pick up 'darkgreen' as 0xDA */ + for (const struct HumanColors *hc = human_colors; hc->keyword != NULL; hc++) + { + if (strcasecmp(hc->keyword, color.c_str()) != 0) + continue; + + rgb = hc->rgb; parsed = true; + + break; + } + } + + /* swy: (B) no luck, try interpreting it as an hexadecimal number instead */ + if (!parsed) + { + if (color.length() == 6) + { + const char *colorptr = color.c_str(); char *endptr = NULL; + + rgb = strtoul(colorptr, &endptr, 16); + + /* swy: check that strtoul() has advanced the read pointer until the end (NULL terminator); + that means it has read the whole thing */ + if (colorptr != endptr && endptr && *endptr == '\0') + parsed = true; + } + } + + /* swy: we got it, save the 32-bit integer as a tuple of three RGB bytes */ + if (parsed) + { + options->colors.push_back(std::make_tuple( + (rgb >> (8 * 2)) & 0xFF, /* RR.... */ + (rgb >> (8 * 1)) & 0xFF, /* ..GG.. */ + (rgb >> (8 * 0)) & 0xFF /* ....BB */ + )); + } + else + { + std::cout << "Error: Unknown color: '" + color + "', skipping." << std::endl; + } + + // If there are no more colors + if (rgb_end == std::string::npos) + break; + + // Remove the current color and the next color's leading comma + colors_string = colors_string.substr(color.length() + 1); + } + + return options->colors.size() > 0; +} + +unsigned int ParseMode(DeviceOptions& options, std::vector &rgb_controllers) +{ + // no need to check if --mode wasn't passed + if (options.mode.size() == 0) + { + return rgb_controllers[options.device]->active_mode; + } + + /*---------------------------------------------------------*\ + | Search through all of the device modes and see if there is| + | a match. If no match is found, print an error message. | + \*---------------------------------------------------------*/ + for(unsigned int mode_idx = 0; mode_idx < rgb_controllers[options.device]->modes.size(); mode_idx++) + { + if (strcasecmp(rgb_controllers[options.device]->modes[mode_idx].name.c_str(), options.mode.c_str()) == 0) + { + return mode_idx; + } + } + + std::cout << "Error: Mode '" + options.mode + "' not available for device '" + rgb_controllers[options.device]->GetName() + "'" << std::endl; + return false; +} + +DeviceOptions* GetDeviceOptionsForDevID(Options *opts, int device) +{ + if (device == -1) + { + return &opts->allDeviceOptions; + } + + for (unsigned int i = 0; i < opts->devices.size(); i++) + { + if (opts->devices[i].device == device) + { + return &opts->devices[i]; + } + } + + // should never happen + std::cout << "Internal error: Tried setting an option on a device that wasn't specified" << std::endl; + abort(); +} + +std::string QuoteIfNecessary(std::string str) +{ + if (str.find(' ') == std::string::npos) + { + return str; + } + else + { + return "'" + str + "'"; + } +} + +/*---------------------------------------------------------------------------------------------------------*\ +| Option processing functions | +\*---------------------------------------------------------------------------------------------------------*/ + +void OptionHelp() +{ + std::string help_text; + help_text += "OpenRGB "; + help_text += VERSION_STRING; + help_text += ", for controlling RGB lighting.\n"; + help_text += "Usage: OpenRGB (--device [--mode] [--color])...\n"; + help_text += "\n"; + help_text += "Options:\n"; + help_text += "--gui Shows the GUI. GUI also appears when not passing any parameters\n"; + help_text += "--startminimized Starts the GUI minimized to tray. Implies --gui, even if not specified\n"; + help_text += "--client [IP]:[Port] Starts an SDK client on the given IP:Port (assumes port 6742 if not specified)\n"; + help_text += "--server Starts the SDK's server\n"; + help_text += "--server-host Sets the SDK's server host. Default: 0.0.0.0 (all network interfaces)\n"; + help_text += "--server-port Sets the SDK's server port. Default: 6742 (1024-65535)\n"; + help_text += "-l, --list-devices Lists every compatible device with their number\n"; + help_text += "-d, --device [0-9 | \"name\"] Selects device to apply colors and/or effect to, or applies to all devices if omitted\n"; + help_text += " Basic string search is implemented 3 characters or more\n"; + help_text += " Can be specified multiple times with different modes and colors\n"; + help_text += "-z, --zone [0-9] Selects zone to apply colors and/or sizes to, or applies to all zones in device if omitted\n"; + help_text += " Must be specified after specifying a device\n"; + help_text += "-c, --color [random | FFFFF,00AAFF ...] Sets colors on each device directly if no effect is specified, and sets the effect color if an effect is specified\n"; + help_text += " If there are more LEDs than colors given, the last color will be applied to the remaining LEDs\n"; + help_text += "-m, --mode [breathing | static | ...] Sets the mode to be applied, check --list-devices to see which modes are supported on your device\n"; + help_text += "-b, --brightness [0-100] Sets the brightness as a percentage if the mode supports brightness\n"; + help_text += "-s, --speed [0-100] Sets the speed as a percentage if the mode supports speed\n"; + help_text += "-sz, --size [0-N] Sets the new size of the specified device zone.\n"; + help_text += " Must be specified after specifying a zone.\n"; + help_text += " If the specified size is out of range, or the zone does not offer resizing capability, the size will not be changed\n"; + help_text += "-V, --version Display version and software build information\n"; + help_text += "-p, --profile filename[.orp] Load the profile from filename/filename.orp\n"; + help_text += "-sp, --save-profile filename.orp Save the given settings to profile filename.orp\n"; + help_text += "--i2c-tools Shows the I2C/SMBus Tools page in the GUI. Implies --gui, even if not specified.\n"; + help_text += " USE I2C TOOLS AT YOUR OWN RISK! Don't use this option if you don't know what you're doing!\n"; + help_text += " There is a risk of bricking your motherboard, RGB controller, and RAM if you send invalid SMBus/I2C transactions.\n"; + help_text += "--localconfig Use the current working directory instead of the global configuration directory.\n"; + help_text += "--config path Use a custom path instead of the global configuration directory.\n"; + help_text += "--nodetect Do not try to detect hardware at startup.\n"; + help_text += "--noautoconnect Do not try to autoconnect to a local server at startup.\n"; + help_text += "--loglevel [0-6 | error | warning ...] Set the log level (0: fatal to 6: trace).\n"; + help_text += "--print-source Print the source code file and line number for each log entry.\n"; + help_text += "-v, --verbose Print log messages to stdout.\n"; + help_text += "-vv, --very-verbose Print debug messages and log messages to stdout.\n"; + help_text += "--autostart-check Check if OpenRGB starting at login is enabled.\n"; + help_text += "--autostart-disable Disable OpenRGB starting at login.\n"; + help_text += "--autostart-enable arguments Enable OpenRGB to start at login. Requires arguments to give to OpenRGB at login.\n"; + + std::cout << help_text << std::endl; +} + +void OptionVersion() +{ + std::string version_text; + version_text += "OpenRGB "; + version_text += VERSION_STRING; + version_text += ", for controlling RGB lighting.\n"; + version_text += " Version:\t\t "; + version_text += VERSION_STRING; + version_text += "\n Build Date\t\t "; + version_text += BUILDDATE_STRING; + version_text += "\n Git Commit ID\t\t "; + version_text += GIT_COMMIT_ID; + version_text += "\n Git Commit Date\t "; + version_text += GIT_COMMIT_DATE; + version_text += "\n Git Branch\t\t "; + version_text += GIT_BRANCH; + version_text += "\n"; + + std::cout << version_text << std::endl; +} + +void OptionListDevices(std::vector& rgb_controllers) +{ + ResourceManager::get()->WaitForDeviceDetection(); + + for(std::size_t controller_idx = 0; controller_idx < rgb_controllers.size(); controller_idx++) + { + RGBController *controller = rgb_controllers[controller_idx]; + + /*---------------------------------------------------------*\ + | Print device name | + \*---------------------------------------------------------*/ + std::cout << controller_idx << ": " << controller->GetName() << std::endl; + + /*---------------------------------------------------------*\ + | Print device type | + \*---------------------------------------------------------*/ + std::cout << " Type: " << device_type_to_str(controller->type) << std::endl; + + /*---------------------------------------------------------*\ + | Print device description | + \*---------------------------------------------------------*/ + if(!controller->GetDescription().empty()) + { + std::cout << " Description: " << controller->GetDescription() << std::endl; + } + + /*---------------------------------------------------------*\ + | Print device version | + \*---------------------------------------------------------*/ + if(!controller->GetVersion().empty()) + { + std::cout << " Version: " << controller->GetLocation() << std::endl; + } + + /*---------------------------------------------------------*\ + | Print device location | + \*---------------------------------------------------------*/ + if(!controller->GetLocation().empty()) + { + std::cout << " Location: " << controller->GetLocation() << std::endl; + } + + /*---------------------------------------------------------*\ + | Print device serial | + \*---------------------------------------------------------*/ + if(!controller->GetSerial().empty()) + { + std::cout << " Serial: " << controller->GetSerial() << std::endl; + } + + /*---------------------------------------------------------*\ + | Print device modes | + \*---------------------------------------------------------*/ + if(!controller->modes.empty()) + { + std::cout << " Modes:"; + + int current_mode = controller->GetMode(); + for(std::size_t mode_idx = 0; mode_idx < controller->modes.size(); mode_idx++) + { + std::string modeStr = QuoteIfNecessary(controller->modes[mode_idx].name); + + if(current_mode == (int)mode_idx) + { + modeStr = "[" + modeStr + "]"; + } + std::cout << " " << modeStr; + } + std::cout << std::endl; + } + + /*---------------------------------------------------------*\ + | Print device zones | + \*---------------------------------------------------------*/ + if(!controller->zones.empty()) + { + std::cout << " Zones:"; + + for(std::size_t zone_idx = 0; zone_idx < controller->zones.size(); zone_idx++) + { + std::cout << " " << QuoteIfNecessary(controller->zones[zone_idx].name); + } + std::cout << std::endl; + } + + /*---------------------------------------------------------*\ + | Print device LEDs | + \*---------------------------------------------------------*/ + if(!controller->leds.empty()) + { + std::cout << " LEDs:"; + + for(std::size_t led_idx = 0; led_idx < controller->leds.size(); led_idx++) + { + std::cout << " " << QuoteIfNecessary(controller->leds[led_idx].name); + } + std::cout << std::endl; + } + + std::cout << std::endl; + } +} + +bool OptionDevice(std::vector* current_devices, std::string argument, Options* options, std::vector& rgb_controllers) +{ + bool found = false; + ResourceManager::get()->WaitForDeviceDetection(); + + try + { + int current_device = std::stoi(argument); + + LOG_TRACE("[CLI] using device number %d for argument %s", current_device, argument.c_str()); + + if((current_device >= static_cast(rgb_controllers.size())) || (current_device < 0)) + { + throw nullptr; + } + + DeviceOptions newDev; + newDev.device = current_device; + + if(!options->hasDevice) + { + options->hasDevice = true; + } + + current_devices->push_back(newDev); + + found = true; + } + catch(...) + { + if(argument.length() > 1) + { + std::string argument_lower = argument; + std::transform(argument_lower.begin(), argument_lower.end(), argument_lower.begin(), ::tolower); + + LOG_TRACE("[CLI] Searching for %s", argument_lower.c_str()); + + for(unsigned int i = 0; i < rgb_controllers.size(); i++) + { + /*---------------------------------------------------------*\ + | If the argument is not a number then check all the | + | controllers names for a match | + \*---------------------------------------------------------*/ + std::string name = rgb_controllers[i]->GetName(); + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + LOG_TRACE("[CLI] Comparing to %s", name.c_str()); + + if(name.find(argument_lower) != std::string::npos) + { + found = true; + + DeviceOptions newDev; + newDev.device = i; + + if(!options->hasDevice) + { + options->hasDevice = true; + } + + current_devices->push_back(newDev); + } + } + } + else + { + std::cout << "Error: Empty device ID" << std::endl; + return false; + } + } + + if(!found) + { + std::cout << "Error: Cannot find device \"" << argument << "\"" << std::endl; + } + + return found; +} + +bool OptionZone(std::vector* current_devices, std::string argument, Options* /*options*/, std::vector& rgb_controllers) +{ + bool found = false; + ResourceManager::get()->WaitForDeviceDetection(); + + try + { + int current_zone = std::stoi(argument); + + for(size_t i = 0; i < current_devices->size(); i++) + { + int current_device = current_devices->at(i).device; + + if(current_zone >= static_cast(rgb_controllers[current_device]->zones.size()) || (current_zone < 0)) + { + throw nullptr; + } + + current_devices->at(i).zone = current_zone; + found = true; + } + } + catch(...) + { + std::cout << "Error: Invalid zone ID: " + argument << std::endl; + return false; + } + + return found; +} + +bool CheckColor(std::string argument, DeviceOptions* currentDevOpts) +{ + if(ParseColors(argument, currentDevOpts)) + { + currentDevOpts->hasOption = true; + return true; + } + else + { + std::cout << "Error: Invalid color value: " + argument << std::endl; + return false; + } +} + +bool OptionColor(std::vector* current_devices, std::string argument, Options* options) +{ + /*---------------------------------------------------------*\ + | If a device is not selected i.e. size() == 0 | + | then add color to allDeviceOptions | + \*---------------------------------------------------------*/ + bool found = false; + DeviceOptions* currentDevOpts = &options->allDeviceOptions; + + if(current_devices->size() == 0) + { + found = CheckColor(argument, currentDevOpts); + } + else + { + for(size_t i = 0; i < current_devices->size(); i++) + { + currentDevOpts = ¤t_devices->at(i); + + found = CheckColor(argument, currentDevOpts); + } + } + + return found; +} + +bool OptionMode(std::vector* current_devices, std::string argument, Options* options) +{ + if(argument.size() == 0) + { + std::cout << "Error: --mode passed with no argument" << std::endl; + return false; + } + + /*---------------------------------------------------------*\ + | If a device is not selected i.e. size() == 0 | + | then add mode to allDeviceOptions | + \*---------------------------------------------------------*/ + bool found = false; + DeviceOptions* currentDevOpts = &options->allDeviceOptions; + + if(current_devices->size() == 0) + { + currentDevOpts->mode = argument; + currentDevOpts->hasOption = true; + found = true; + } + else + { + for(size_t i = 0; i < current_devices->size(); i++) + { + currentDevOpts = ¤t_devices->at(i); + + currentDevOpts->mode = argument; + currentDevOpts->hasOption = true; + found = true; + } + } + + if(!found) + { + std::cout << "Error: No devices for mode \"" << argument << "\"" << std::endl; + } + return found; +} + +bool OptionSpeed(std::vector* current_devices, std::string argument, Options* options) +{ + if(argument.size() == 0) + { + std::cout << "Error: --speed passed with no argument" << std::endl; + return false; + } + + /*---------------------------------------------------------*\ + | If a device is not selected i.e. size() == 0 | + | then add speed to allDeviceOptions | + \*---------------------------------------------------------*/ + bool found = false; + DeviceOptions* currentDevOpts = &options->allDeviceOptions; + + if(current_devices->size() == 0) + { + currentDevOpts->speed = std::min(std::max(std::stoi(argument), 0),(int)speed_percentage); + currentDevOpts->hasOption = true; + found = true; + } + else + { + for(size_t i = 0; i < current_devices->size(); i++) + { + DeviceOptions* currentDevOpts = ¤t_devices->at(i); + + currentDevOpts->speed = std::min(std::max(std::stoi(argument), 0),(int)speed_percentage); + currentDevOpts->hasOption = true; + found = true; + } + } + + if(!found) + { + std::cout << "Error: No devices for speed \"" << argument << "\"" << std::endl; + } + return found; +} + +bool OptionBrightness(std::vector* current_devices, std::string argument, Options* options) +{ + if(argument.size() == 0) + { + std::cout << "Error: --brightness passed with no argument" << std::endl; + return false; + } + + /*---------------------------------------------------------*\ + | If a device is not selected i.e. size() == 0 | + | then add brightness to allDeviceOptions | + \*---------------------------------------------------------*/ + bool found = false; + DeviceOptions* currentDevOpts = &options->allDeviceOptions; + + if(current_devices->size() == 0) + { + currentDevOpts->brightness = std::min(std::max(std::stoi(argument), 0),(int)brightness_percentage); + currentDevOpts->hasOption = true; + found = true; + } + else + { + for(size_t i = 0; i < current_devices->size(); i++) + { + DeviceOptions* currentDevOpts = ¤t_devices->at(i); + + currentDevOpts->brightness = std::min(std::max(std::stoi(argument), 0),(int)brightness_percentage); + currentDevOpts->hasOption = true; + found = true; + } + } + + if(!found) + { + std::cout << "Error: No devices for brightness \"" << argument << "\"" << std::endl; + } + return found; +} + +bool OptionSize(std::vector* current_devices, std::string argument, Options* /*options*/, std::vector& rgb_controllers) +{ + const unsigned int new_size = std::stoi(argument); + + ResourceManager::get()->WaitForDeviceDetection(); + + for(size_t i = 0; i < current_devices->size(); i++) + { + int current_device = current_devices->at(i).device; + int current_zone = current_devices->at(i).zone; + + /*---------------------------------------------------------*\ + | Fail out if device, zone, or size are out of range | + \*---------------------------------------------------------*/ + if((current_device >= static_cast(rgb_controllers.size())) || (current_device < 0)) + { + std::cout << "Error: Device is out of range" << std::endl; + return false; + } + else if((current_zone >= static_cast(rgb_controllers[current_device]->zones.size())) || (current_zone < 0)) + { + std::cout << "Error: Zone is out of range" << std::endl; + return false; + } + else if((new_size < rgb_controllers[current_device]->zones[current_zone].leds_min) || (new_size > rgb_controllers[current_device]->zones[current_zone].leds_max)) + { + std::cout << "Error: New size is out of range" << std::endl; + } + + /*---------------------------------------------------------*\ + | Resize the zone | + \*---------------------------------------------------------*/ + rgb_controllers[current_device]->ResizeZone(current_zone, new_size); + + /*---------------------------------------------------------*\ + | Save the profile | + \*---------------------------------------------------------*/ + ResourceManager::get()->GetProfileManager()->SaveProfile("sizes", true); + } + + return true; +} + +bool OptionProfile(std::string argument, std::vector& rgb_controllers) +{ + ResourceManager::get()->WaitForDeviceDetection(); + + /*---------------------------------------------------------*\ + | Attempt to load profile | + \*---------------------------------------------------------*/ + if(ResourceManager::get()->GetProfileManager()->LoadProfile(argument)) + { + /*-----------------------------------------------------*\ + | Change device mode if profile loading was successful | + \*-----------------------------------------------------*/ + for(std::size_t controller_idx = 0; controller_idx < rgb_controllers.size(); controller_idx++) + { + RGBController* device = rgb_controllers[controller_idx]; + + device->DeviceUpdateMode(); + LOG_DEBUG("[CLI] Updating mode for %s to %i", device->GetName().c_str(), device->active_mode); + + if(device->modes[device->active_mode].color_mode == MODE_COLORS_PER_LED) + { + device->DeviceUpdateLEDs(); + LOG_DEBUG("[CLI] Mode uses per-LED color, also updating LEDs"); + } + } + + std::cout << "Profile loaded successfully" << std::endl; + return true; + } + else + { + std::cout << "Profile failed to load" << std::endl; + return false; + } +} + +bool OptionSaveProfile(std::string argument) +{ + /*---------------------------------------------------------*\ + | Set save profile filename | + \*---------------------------------------------------------*/ + profile_save_filename = argument; + return(true); +} + +int ProcessOptions(Options* options, std::vector& rgb_controllers) +{ + unsigned int ret_flags = 0; + int arg_index = 1; + std::vector current_devices; + + options->hasDevice = false; + options->profile_loaded = false; + +#ifdef _WIN32 + int fake_argc; + wchar_t** argvw = CommandLineToArgvW(GetCommandLineW(), &fake_argc); +#endif + + while(arg_index < preserve_argc) + { + std::string option = preserve_argv[arg_index]; + std::string argument = ""; + filesystem::path arg_path; + + /*---------------------------------------------------------*\ + | Handle options that take an argument | + \*---------------------------------------------------------*/ + if(arg_index + 1 < preserve_argc) + { + argument = preserve_argv[arg_index + 1]; +#ifdef _WIN32 + arg_path = argvw[arg_index + 1]; +#else + arg_path = argument; +#endif + } + + /*---------------------------------------------------------*\ + | -l / --list-devices (no arguments) | + \*---------------------------------------------------------*/ + if(option == "--list-devices" || option == "-l") + { + OptionListDevices(rgb_controllers); + exit(0); + } + + /*---------------------------------------------------------*\ + | -d / --device | + \*---------------------------------------------------------*/ + else if(option == "--device" || option == "-d") + { + while(!current_devices.empty()) + { + options->devices.push_back(current_devices.back()); + current_devices.pop_back(); + } + + if(!OptionDevice(¤t_devices, argument, options, rgb_controllers)) + { + return RET_FLAG_PRINT_HELP; + } + + arg_index++; + } + + /*---------------------------------------------------------*\ + | -z / --zone | + \*---------------------------------------------------------*/ + else if(option == "--zone" || option == "-z") + { + if(!OptionZone(¤t_devices, argument, options, rgb_controllers)) + { + return RET_FLAG_PRINT_HELP; + } + + arg_index++; + } + + /*---------------------------------------------------------*\ + | -c / --color | + \*---------------------------------------------------------*/ + else if(option == "--color" || option == "-c") + { + if(!OptionColor(¤t_devices, argument, options)) + { + return RET_FLAG_PRINT_HELP; + } + + arg_index++; + } + + /*---------------------------------------------------------*\ + | -m / --mode | + \*---------------------------------------------------------*/ + else if(option == "--mode" || option == "-m") + { + if(!OptionMode(¤t_devices, argument, options)) + { + return RET_FLAG_PRINT_HELP; + } + + arg_index++; + } + + /*---------------------------------------------------------*\ + | -b / --brightness | + \*---------------------------------------------------------*/ + else if(option == "--brightness" || option == "-b") + { + if(!OptionBrightness(¤t_devices, argument, options)) + { + return RET_FLAG_PRINT_HELP; + } + + arg_index++; + } + + /*---------------------------------------------------------*\ + | -s / --speed | + \*---------------------------------------------------------*/ + else if(option == "--speed" || option == "-s") + { + if(!OptionSpeed(¤t_devices, argument, options)) + { + return RET_FLAG_PRINT_HELP; + } + + arg_index++; + } + + /*---------------------------------------------------------*\ + | -sz / --size | + \*---------------------------------------------------------*/ + else if(option == "--size" || option == "-sz") + { + if(!OptionSize(¤t_devices, argument, options, rgb_controllers)) + { + return RET_FLAG_PRINT_HELP; + } + + arg_index++; + } + + /*---------------------------------------------------------*\ + | -p / --profile | + \*---------------------------------------------------------*/ + else if(option == "--profile" || option == "-p") + { + options->profile_loaded = OptionProfile(arg_path.generic_u8string(), rgb_controllers); + + arg_index++; + } + + /*---------------------------------------------------------*\ + | -sp / --save-profile | + \*---------------------------------------------------------*/ + else if(option == "--save-profile" || option == "-sp") + { + OptionSaveProfile(arg_path.generic_u8string()); + + arg_index++; + } + + /*---------------------------------------------------------*\ + | Invalid option | + \*---------------------------------------------------------*/ + else + { + if((option == "--localconfig") + ||(option == "--nodetect") + ||(option == "--noautoconnect") + ||(option == "--server") + ||(option == "--gui") + ||(option == "--i2c-tools" || option == "--yolo") + ||(option == "--startminimized") + ||(option == "--print-source") + ||(option == "--verbose" || option == "-v") + ||(option == "--very-verbose" || option == "-vv") + ||(option == "--help" || option == "-h") + ||(option == "--version" || option == "-V") + ||(option == "--autostart-check") + ||(option == "--autostart-disable")) + { + /*-------------------------------------------------*\ + | Do nothing, these are pre-detection arguments | + | and this parser should ignore them | + \*-------------------------------------------------*/ + } + else if((option == "--server-port") + ||(option == "--server-host") + ||(option == "--loglevel") + ||(option == "--config") + ||(option == "--client") + ||(option == "--autostart-enable")) + { + /*-------------------------------------------------*\ + | Increment index for pre-detection arguments with | + | parameter | + \*-------------------------------------------------*/ + arg_index++; + } + else + { + /*-------------------------------------------------*\ + | If the argument is not a pre-detection argument, | + | throw an error and print help | + \*-------------------------------------------------*/ + std::cout << "Error: Invalid option: " + option << std::endl; + return RET_FLAG_PRINT_HELP; + } + } + + arg_index++; + } + + /*---------------------------------------------------------*\ + | If a device was specified, check to verify that a | + | corresponding option was also specified | + \*---------------------------------------------------------*/ + while(!current_devices.empty()) + { + options->devices.push_back(current_devices.back()); + current_devices.pop_back(); + } + + if(options->hasDevice) + { + for(std::size_t option_idx = 0; option_idx < options->devices.size(); option_idx++) + { + if(!options->devices[option_idx].hasOption) + { + std::cout << "Error: Device " + std::to_string(option_idx) + " specified, but neither mode nor color given" << std::endl; + return RET_FLAG_PRINT_HELP; + } + } + return 0; + } + else + { + return ret_flags; + } +} + +void ApplyOptions(DeviceOptions& options, std::vector& rgb_controllers) +{ + RGBController* device = rgb_controllers[options.device]; + + /*---------------------------------------------------------*\ + | Set mode first, in case it's 'direct' (which affects | + | SetLED below) | + \*---------------------------------------------------------*/ + unsigned int mode = ParseMode(options, rgb_controllers); + + /*---------------------------------------------------------*\ + | If the user has specified random colours and the device | + | supports that colour mode then swich to it before | + | evaluating if a colour needs to be set | + \*---------------------------------------------------------*/ + if(options.random_colors && (device->modes[mode].flags & MODE_FLAG_HAS_RANDOM_COLOR)) + { + device->modes[mode].color_mode = MODE_COLORS_RANDOM; + } + + /*---------------------------------------------------------*\ + | If the user has specified random colours and the device | + | supports that colour mode then swich to it before | + | evaluating if a colour needs to be set | + \*---------------------------------------------------------*/ + if((device->modes[mode].flags & MODE_FLAG_HAS_BRIGHTNESS)) + { + unsigned int new_brightness = device->modes[mode].brightness_max - device->modes[mode].brightness_min; + new_brightness *= options.brightness; + new_brightness /= brightness_percentage; + + device->modes[mode].brightness = device->modes[mode].brightness_min + new_brightness; + } + + if((device->modes[mode].flags & MODE_FLAG_HAS_SPEED)) + { + unsigned int new_speed = device->modes[mode].speed_max - device->modes[mode].speed_min; + new_speed *= options.speed; + new_speed /= speed_percentage; + + device->modes[mode].speed = device->modes[mode].speed_min + new_speed; + } + + /*---------------------------------------------------------*\ + | Determine which color mode this mode uses and update | + | colors accordingly | + \*---------------------------------------------------------*/ + switch(device->modes[mode].color_mode) + { + case MODE_COLORS_NONE: + break; + + case MODE_COLORS_RANDOM: + break; + + case MODE_COLORS_PER_LED: + if(options.colors.size() != 0) + { + std::size_t last_set_color = 0; + + RGBColor* start_from; + unsigned int led_count; + if(options.zone < 0) + { + start_from = &device->colors[0]; + led_count = (unsigned int)device->leds.size(); + } + else + { + start_from = device->zones[options.zone].colors; + led_count = device->zones[options.zone].leds_count; + } + + for(std::size_t led_idx = 0; led_idx < led_count; led_idx++) + { + if(led_idx < options.colors.size()) + { + last_set_color = led_idx; + } + + start_from[led_idx] = ToRGBColor(std::get<0>(options.colors[last_set_color]), + std::get<1>(options.colors[last_set_color]), + std::get<2>(options.colors[last_set_color])); + } + } + break; + + case MODE_COLORS_MODE_SPECIFIC: + if(options.colors.size() >= device->modes[mode].colors_min && options.colors.size() <= device->modes[mode].colors_max) + { + device->modes[mode].colors.resize(options.colors.size()); + + for(std::size_t color_idx = 0; color_idx < options.colors.size(); color_idx++) + { + device->modes[mode].colors[color_idx] = ToRGBColor(std::get<0>(options.colors[color_idx]), + std::get<1>(options.colors[color_idx]), + std::get<2>(options.colors[color_idx])); + } + } + else + { + std::cout << "Wrong number of colors specified for mode " + device->modes[mode].name << std::endl; + std::cout << "Please provide between " + std::to_string(device->modes[mode].colors_min) + " and " + std::to_string(device->modes[mode].colors_min) + " colors" << std::endl; + exit(0); + } + break; + } + + /*---------------------------------------------------------*\ + | Set device mode | + \*---------------------------------------------------------*/ + device->active_mode = mode; + device->DeviceUpdateMode(); + + /*---------------------------------------------------------*\ + | Set device per-LED colors if necessary | + \*---------------------------------------------------------*/ + if(device->modes[mode].color_mode == MODE_COLORS_PER_LED) + { + device->DeviceUpdateLEDs(); + } +} + + +unsigned int cli_pre_detection(int argc, char* argv[]) +{ + /*---------------------------------------------------------*\ + | Process only the arguments that should be performed prior | + | to detecting devices and/or starting clients | + \*---------------------------------------------------------*/ + int arg_index = 1; + unsigned int cfg_args = 0; + unsigned int ret_flags = 0; + std::string server_host = OPENRGB_SDK_HOST; + unsigned short server_port = OPENRGB_SDK_PORT; + bool server_start = false; + bool print_help = false; + + preserve_argc = argc; + preserve_argv = argv; + +#ifdef _WIN32 + int fake_argc; + wchar_t** argvw = CommandLineToArgvW(GetCommandLineW(), &fake_argc); +#endif + + while(arg_index < argc) + { + std::string option = argv[arg_index]; + std::string argument = ""; + + LOG_DEBUG("[CLI] Parsing CLI option: %s", option.c_str()); + + /*---------------------------------------------------------*\ + | Handle options that take an argument | + \*---------------------------------------------------------*/ + if(arg_index + 1 < argc) + { + argument = argv[arg_index + 1]; + } + + /*---------------------------------------------------------*\ + | --localconfig | + \*---------------------------------------------------------*/ + if(option == "--localconfig") + { + ResourceManager::get()->SetConfigurationDirectory("./"); + cfg_args++; + } + + /*---------------------------------------------------------*\ + | --config | + \*---------------------------------------------------------*/ + else if(option == "--config") + { + cfg_args+= 2; + arg_index++; +#ifdef _WIN32 + filesystem::path config_path(argvw[arg_index]); +#else + filesystem::path config_path(argument); +#endif + + if(filesystem::is_directory(config_path)) + { + ResourceManager::get()->SetConfigurationDirectory(config_path); + LOG_INFO("[CLI] Setting config directory to %s",argument.c_str()); // TODO: Use config_path in logs somehow + } + else + { + LOG_ERROR("[CLI] '%s' is not a valid directory",argument.c_str()); // TODO: Use config_path in logs somehow + print_help = true; + break; + } + } + + /*---------------------------------------------------------*\ + | --nodetect | + \*---------------------------------------------------------*/ + else if(option == "--nodetect") + { + ret_flags |= RET_FLAG_NO_DETECT; + cfg_args++; + } + + /*---------------------------------------------------------*\ + | --noautoconnect | + \*---------------------------------------------------------*/ + else if(option == "--noautoconnect") + { + ret_flags |= RET_FLAG_NO_AUTO_CONNECT; + cfg_args++; + } + + /*---------------------------------------------------------*\ + | --client | + \*---------------------------------------------------------*/ + else if(option == "--client") + { + NetworkClient * client = new NetworkClient(ResourceManager::get()->GetRGBControllers()); + + std::size_t pos = argument.find(":"); + std::string ip = argument.substr(0, pos); + unsigned short port_val; + + if(pos == argument.npos) + { + port_val = OPENRGB_SDK_PORT; + } + else + { + std::string port = argument.substr(argument.find(":") + 1); + port_val = std::stoi(port); + } + + std::string titleString = "OpenRGB "; + titleString.append(VERSION_STRING); + + client->SetIP(ip.c_str()); + client->SetName(titleString.c_str()); + client->SetPort(port_val); + + client->StartClient(); + + for(int timeout = 0; timeout < 100; timeout++) + { + if(client->GetConnected()) + { + break; + } + std::this_thread::sleep_for(10ms); + } + + ResourceManager::get()->RegisterNetworkClient(client); + + cfg_args++; + arg_index++; + } + + /*---------------------------------------------------------*\ + | --server (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--server") + { + server_start = true; + } + + /*---------------------------------------------------------*\ + | --server-port | + \*---------------------------------------------------------*/ + else if(option == "--server-port") + { + if (argument != "") + { + try + { + int port = std::stoi(argument); + if (port >= 1024 && port <= 65535) + { + server_port = port; + server_start = true; + } + else + { + std::cout << "Error: Port out of range: " << port << " (1024-65535)" << std::endl; + print_help = true; + break; + } + } + catch(std::invalid_argument& /*e*/) + { + std::cout << "Error: Invalid data in --server-port argument (expected a number in range 1024-65535)" << std::endl; + print_help = true; + break; + } + } + else + { + std::cout << "Error: Missing argument for --server-port" << std::endl; + print_help = true; + break; + } + cfg_args++; + arg_index++; + } + /*---------------------------------------------------------*\ + | --server-host | + \*---------------------------------------------------------*/ + else if(option == "--server-host") + { + if (argument != "") + { + std::string host = argument; + + server_host = host; + server_start = true; + } + else + { + std::cout << "Error: Missing argument for --server-host" << std::endl; + print_help = true; + break; + } + cfg_args++; + arg_index++; + } + + /*---------------------------------------------------------*\ + | --loglevel | + \*---------------------------------------------------------*/ + else if(option == "--loglevel") + { + if (argument != "") + { + try + { + int level = std::stoi(argument); + if (level >= 0 && level <= LL_TRACE) + { + LogManager::get()->setLoglevel(level); + } + else + { + LOG_ERROR("[CLI] Loglevel out of range: %d (0-6)", level); + print_help = true; + break; + } + } + catch(std::invalid_argument& /*e*/) + { + if(!strcasecmp(argument.c_str(), "fatal")) + { + LogManager::get()->setLoglevel(LL_FATAL); + } + else if(!strcasecmp(argument.c_str(), "error")) + { + LogManager::get()->setLoglevel(LL_ERROR); + } + else if(!strcasecmp(argument.c_str(), "warning")) + { + LogManager::get()->setLoglevel(LL_WARNING); + } + else if(!strcasecmp(argument.c_str(), "info")) + { + LogManager::get()->setLoglevel(LL_INFO); + } + else if(!strcasecmp(argument.c_str(), "verbose")) + { + LogManager::get()->setLoglevel(LL_VERBOSE); + } + else if(!strcasecmp(argument.c_str(), "debug")) + { + LogManager::get()->setLoglevel(LL_DEBUG); + } + else if(!strcasecmp(argument.c_str(), "trace")) + { + LogManager::get()->setLoglevel(LL_TRACE); + } + else + { + LOG_ERROR("[CLI] Invalid loglevel"); + print_help = true; + break; + } + } + } + else + { + LOG_ERROR("[CLI] Missing argument for --loglevel"); + print_help = true; + break; + } + cfg_args+= 2; + arg_index++; + } + + /*---------------------------------------------------------*\ + | --autostart-check (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--autostart-check") + { + AutoStart auto_start("OpenRGB"); + + if(auto_start.IsAutoStartEnabled()) + { + std::cout << "Autostart is enabled." << std::endl; + } + else + { + std::cout << "Autostart is disabled." << std::endl; + } + } + + /*---------------------------------------------------------*\ + | --autostart-disable (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--autostart-disable") + { + AutoStart auto_start("OpenRGB"); + + if(auto_start.DisableAutoStart()) + { + std::cout << "Autostart disabled." << std::endl; + } + else + { + std::cout << "Autostart failed to disable." << std::endl; + } + } + + /*---------------------------------------------------------*\ + | --autostart-enable | + \*---------------------------------------------------------*/ + else if(option == "--autostart-enable") + { + if (argument != "") + { + std::string desc = "OpenRGB "; + desc += VERSION_STRING; + desc += ", for controlling RGB lighting."; + + AutoStart auto_start("OpenRGB"); + AutoStartInfo auto_start_interface; + + auto_start_interface.args = argument; + auto_start_interface.category = "Utility;"; + auto_start_interface.desc = desc; + auto_start_interface.icon = "OpenRGB"; + auto_start_interface.path = auto_start.GetExePath(); + + if(auto_start.EnableAutoStart(auto_start_interface)) + { + std::cout << "Autostart enabled." << std::endl; + } + else + { + std::cout << "Autostart failed to enable." << std::endl; + } + } + else + { + std::cout << "Error: Missing argument for --autostart-enable" << std::endl; + print_help = true; + break; + } + + cfg_args++; + arg_index++; + } + + /*---------------------------------------------------------*\ + | --gui (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--gui") + { + ret_flags |= RET_FLAG_START_GUI; + } + + /*---------------------------------------------------------*\ + | --i2c-tools / --yolo (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--i2c-tools" || option == "--yolo") + { + ret_flags |= RET_FLAG_START_GUI | RET_FLAG_I2C_TOOLS; + } + + /*---------------------------------------------------------*\ + | --startminimized (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--startminimized") + { + ret_flags |= RET_FLAG_START_GUI | RET_FLAG_START_MINIMIZED; + } + + /*---------------------------------------------------------*\ + | -h / --help (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--help" || option == "-h") + { + print_help = true; + break; + } + + /*---------------------------------------------------------*\ + | -V / --version (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--version" || option == "-V") + { + OptionVersion(); + exit(0); + } + + /*---------------------------------------------------------*\ + | -v / --verbose (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--verbose" || option == "-v") + { + LogManager::get()->setVerbosity(LL_VERBOSE); + cfg_args++; + } + + /*---------------------------------------------------------*\ + | -vv / --very-verbose (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--very-verbose" || option == "-vv") + { + LogManager::get()->setVerbosity(LL_TRACE); + cfg_args++; + } + + /*---------------------------------------------------------*\ + | --print-source (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--print-source") + { + LogManager::get()->setPrintSource(true); + cfg_args++; + } + + /*---------------------------------------------------------*\ + | Any unrecognized arguments trigger the post-detection CLI | + \*---------------------------------------------------------*/ + else + { + ret_flags |= RET_FLAG_CLI_POST_DETECTION; + } + + arg_index++; + } + + if(print_help) + { + OptionHelp(); + exit(0); + } + + if(server_start) + { + NetworkServer * server = ResourceManager::get()->GetServer(); + server->SetHost(server_host); + server->SetPort(server_port); + ret_flags |= RET_FLAG_START_SERVER; + } + + if((argc - cfg_args) <= 1) + { + ret_flags |= RET_FLAG_START_GUI; + } + + return(ret_flags); +} + +unsigned int cli_post_detection() +{ + /*---------------------------------------------------------*\ + | Wait for device detection | + \*---------------------------------------------------------*/ + ResourceManager::get()->WaitForDeviceDetection(); + + /*---------------------------------------------------------*\ + | Get controller list from resource manager | + \*---------------------------------------------------------*/ + std::vector rgb_controllers = ResourceManager::get()->GetRGBControllers(); + + /*---------------------------------------------------------*\ + | Process the argument options | + \*---------------------------------------------------------*/ + Options options; + unsigned int ret_flags = ProcessOptions(&options, rgb_controllers); + + /*---------------------------------------------------------*\ + | If the return flags are set, exit CLI mode without | + | processing device updates from CLI input. | + \*---------------------------------------------------------*/ + switch(ret_flags) + { + case 0: + break; + + case RET_FLAG_PRINT_HELP: + std::cout << "Run `OpenRGB --help` for syntax" << std::endl; + exit(-1); + break; + + default: + return ret_flags; + break; + } + + /*---------------------------------------------------------*\ + | If the options has one or more specific devices, loop | + | through all of the specific devices and apply settings. | + | Otherwise, apply settings to all devices. | + \*---------------------------------------------------------*/ + if (options.hasDevice) + { + for(unsigned int device_idx = 0; device_idx < options.devices.size(); device_idx++) + { + ApplyOptions(options.devices[device_idx], rgb_controllers); + } + } + else if (!options.profile_loaded) + { + for (unsigned int device_idx = 0; device_idx < rgb_controllers.size(); device_idx++) + { + options.allDeviceOptions.device = device_idx; + ApplyOptions(options.allDeviceOptions, rgb_controllers); + } + } + + /*---------------------------------------------------------*\ + | If there is a save filename set, save the profile | + \*---------------------------------------------------------*/ + if (profile_save_filename != "") + { + if(ResourceManager::get()->GetProfileManager()->SaveProfile(profile_save_filename)) + { + LOG_INFO("[CLI] Profile saved successfully"); + } + else + { + LOG_ERROR("[CLI] Profile saving failed"); + } + } + + std::this_thread::sleep_for(1s); + + return 0; +} diff --git a/cli.h b/cli.h new file mode 100644 index 0000000..f7f0d7f --- /dev/null +++ b/cli.h @@ -0,0 +1,19 @@ +#ifndef CLI_H +#define CLI_H + +unsigned int cli_pre_detection(int argc, char* argv[]); +unsigned int cli_post_detection(); + +enum +{ + RET_FLAG_PRINT_HELP = 1, + RET_FLAG_START_GUI = 2, + RET_FLAG_I2C_TOOLS = 4, + RET_FLAG_START_MINIMIZED = 8, + RET_FLAG_NO_DETECT = 16, + RET_FLAG_CLI_POST_DETECTION = 32, + RET_FLAG_START_SERVER = 64, + RET_FLAG_NO_AUTO_CONNECT = 128, +}; + +#endif diff --git a/debian/changelog.in b/debian/changelog.in new file mode 100644 index 0000000..fa403e6 --- /dev/null +++ b/debian/changelog.in @@ -0,0 +1,5 @@ +openrgb (__VERSION__) UNRELEASED; urgency=medium + + * Builds from git master. See git history for more information. + + -- Adam Honse Sun, 12 Apr 2020 22:57:34 -0500 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..d434014 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +10 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..122f99c --- /dev/null +++ b/debian/control @@ -0,0 +1,29 @@ +Source: openrgb +Maintainer: Adam Honse +Section: misc +Priority: optional +Standards-Version: 3.9.2 +Build-Depends: + debhelper (>= 9), + pkg-config, + qtbase5-dev, + qtbase5-dev-tools, + qttools5-dev-tools, + qt5-qmake, + libusb-1.0-0-dev, + libhidapi-dev, + libmbedtls-dev, +Homepage: https://gitlab.com/CalcProgrammer1/OpenRGB + +Package: openrgb +Architecture: any +Depends: + ${shlibs:Depends}, + ${misc:Depends}, + udev, +Recommends: + openrgb-dkms-drivers, +Conflicts: + openrgb-udev, +Description: Open source RGB lighting control + OpenRGB controls RGB lighting diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..e0b9b80 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,51 @@ + This package was debianized by Adam Honse on + 3rd December 2020. + + The current Debian maintainer is Chris M + + It was downloaded from: https://gitlab.com/CalcProgrammer1/OpenRGB + + Upstream Authors: Adam Honse, Chris M, Dmitry K + +License: GPL-2+ + Copyright: 2019 - Present Adam Honse + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + On Debian systems, the complete text of the GNU General Public + License, version 2, can be found in /usr/share/common-licenses/GPL-2. + +License: GPL-2+ + The Files in ./* are Copyright 2019 - Present Adam Honse + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + On Debian systems, the complete text of the GNU Lesser General Public + License, can be found in /usr/share/common-licenses/LGPL. + + The Debian packaging is (C) 2006, Chris M and + is licensed under the GPL, see above. diff --git a/debian/openrgb.postinst b/debian/openrgb.postinst new file mode 100644 index 0000000..8180210 --- /dev/null +++ b/debian/openrgb.postinst @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# -e is not set should this step fail for whatever reason the installation is still valid +set -u -o pipefail + +# Reload rules +if [ -f /bin/udevadm ]; then + udevadm control --reload-rules || echo "done" + udevadm trigger || : +else + echo + echo "\/-------------------------------------------------------\\" + echo "\| Critical: This system does not have udev installed. \|" + echo "\| \|" + echo "\| Please install udev with: sudo apt -y install udev \|" + echo "\\-------------------------------------------------------\/" + echo +fi diff --git a/debian/rules b/debian/rules new file mode 100644 index 0000000..0110b0f --- /dev/null +++ b/debian/rules @@ -0,0 +1,5 @@ +#!/usr/bin/make -f +export QT_SELECT := qt5 + +%: + dh $@ --parallel diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..cd029ff --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/dependencies/CRCpp/CRC.h b/dependencies/CRCpp/CRC.h new file mode 100644 index 0000000..0b20b36 --- /dev/null +++ b/dependencies/CRCpp/CRC.h @@ -0,0 +1,2088 @@ +/** + @file CRC.h + @author Daniel Bahr + @version 1.2.0.0 + @copyright + @parblock + CRC++ + Copyright (c) 2022, Daniel Bahr + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of CRC++ nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + @endparblock +*/ + +/* + CRC++ can be configured by setting various #defines before #including this header file: + + #define crcpp_uint8 - Specifies the type used to store CRCs that have a width of 8 bits or less. + This type is not used in CRC calculations. Defaults to ::std::uint8_t. + #define crcpp_uint16 - Specifies the type used to store CRCs that have a width between 9 and 16 bits (inclusive). + This type is not used in CRC calculations. Defaults to ::std::uint16_t. + #define crcpp_uint32 - Specifies the type used to store CRCs that have a width between 17 and 32 bits (inclusive). + This type is not used in CRC calculations. Defaults to ::std::uint32_t. + #define crcpp_uint64 - Specifies the type used to store CRCs that have a width between 33 and 64 bits (inclusive). + This type is not used in CRC calculations. Defaults to ::std::uint64_t. + #define crcpp_size - This type is used for loop iteration and function signatures only. Defaults to ::std::size_t. + #define CRCPP_USE_NAMESPACE - Define to place all CRC++ code within the ::CRCPP namespace. + #define CRCPP_BRANCHLESS - Define to enable a branchless CRC implementation. The branchless implementation uses a single integer + multiplication in the bit-by-bit calculation instead of a small conditional. The branchless implementation + may be faster on processor architectures which support single-instruction integer multiplication. + #define CRCPP_USE_CPP11 - Define to enables C++11 features (move semantics, constexpr, static_assert, etc.). + #define CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS - Define to include definitions for little-used CRCs. +*/ + +#ifndef CRCPP_CRC_H_ +#define CRCPP_CRC_H_ + +/* OpenRGB: We define our configuration macros here. */ +#define CRCPP_USE_NAMESPACE + +#include // Includes CHAR_BIT +#ifdef CRCPP_USE_CPP11 +#include // Includes ::std::size_t +#include // Includes ::std::uint8_t, ::std::uint16_t, ::std::uint32_t, ::std::uint64_t +#else +#include // Includes size_t +#include // Includes uint8_t, uint16_t, uint32_t, uint64_t +#endif +#include // Includes ::std::numeric_limits +#include // Includes ::std::move + +#ifndef crcpp_uint8 +# ifdef CRCPP_USE_CPP11 + /// @brief Unsigned 8-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint8 ::std::uint8_t +# else + /// @brief Unsigned 8-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint8 uint8_t +# endif +#endif + +#ifndef crcpp_uint16 +# ifdef CRCPP_USE_CPP11 + /// @brief Unsigned 16-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint16 ::std::uint16_t +# else + /// @brief Unsigned 16-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint16 uint16_t +# endif +#endif + +#ifndef crcpp_uint32 +# ifdef CRCPP_USE_CPP11 + /// @brief Unsigned 32-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint32 ::std::uint32_t +# else + /// @brief Unsigned 32-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint32 uint32_t +# endif +#endif + +#ifndef crcpp_uint64 +# ifdef CRCPP_USE_CPP11 + /// @brief Unsigned 64-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint64 ::std::uint64_t +# else + /// @brief Unsigned 64-bit integer definition, used primarily for parameter definitions. +# define crcpp_uint64 uint64_t +# endif +#endif + +#ifndef crcpp_size +# ifdef CRCPP_USE_CPP11 + /// @brief Unsigned size definition, used for specifying data sizes. +# define crcpp_size ::std::size_t +# else + /// @brief Unsigned size definition, used for specifying data sizes. +# define crcpp_size size_t +# endif +#endif + +#ifdef CRCPP_USE_CPP11 + /// @brief Compile-time expression definition. +# define crcpp_constexpr constexpr +#else + /// @brief Compile-time expression definition. +# define crcpp_constexpr const +#endif + +#ifdef CRCPP_USE_NAMESPACE +namespace CRCPP +{ +#endif + +/** + @brief Static class for computing CRCs. + @note This class supports computation of full and multi-part CRCs, using a bit-by-bit algorithm or a + byte-by-byte lookup table. The CRCs are calculated using as many optimizations as is reasonable. + If compiling with C++11, the constexpr keyword is used liberally so that many calculations are + performed at compile-time instead of at runtime. +*/ +class CRC +{ +public: + // Forward declaration + template + struct Table; + + /** + @brief CRC parameters. + */ + template + struct Parameters + { + CRCType polynomial; ///< CRC polynomial + CRCType initialValue; ///< Initial CRC value + CRCType finalXOR; ///< Value to XOR with the final CRC + bool reflectInput; ///< true to reflect all input bytes + bool reflectOutput; ///< true to reflect the output CRC (reflection occurs before the final XOR) + + Table MakeTable() const; + }; + + /** + @brief CRC lookup table. After construction, the CRC parameters are fixed. + @note A CRC table can be used for multiple CRC calculations. + */ + template + struct Table + { + // Constructors are intentionally NOT marked explicit. + Table(const Parameters & parameters); + +#ifdef CRCPP_USE_CPP11 + Table(Parameters && parameters); +#endif + + const Parameters & GetParameters() const; + + const CRCType * GetTable() const; + + CRCType operator[](unsigned char index) const; + + private: + void InitTable(); + + Parameters parameters; ///< CRC parameters used to construct the table + CRCType table[1 << CHAR_BIT]; ///< CRC lookup table + }; + + // The number of bits in CRCType must be at least as large as CRCWidth. + // CRCType must be an unsigned integer type or a custom type with operator overloads. + template + static CRCType Calculate(const void * data, crcpp_size size, const Parameters & parameters); + + template + static CRCType Calculate(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc); + + template + static CRCType Calculate(const void * data, crcpp_size size, const Table & lookupTable); + + template + static CRCType Calculate(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc); + + template + static CRCType CalculateBits(const void * data, crcpp_size size, const Parameters & parameters); + + template + static CRCType CalculateBits(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc); + + template + static CRCType CalculateBits(const void * data, crcpp_size size, const Table & lookupTable); + + template + static CRCType CalculateBits(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc); + + // Common CRCs up to 64 bits. + // Note: Check values are the computed CRCs when given an ASCII input of "123456789" (without null terminator) +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters< crcpp_uint8, 4> & CRC_4_ITU(); + static const Parameters< crcpp_uint8, 5> & CRC_5_EPC(); + static const Parameters< crcpp_uint8, 5> & CRC_5_ITU(); + static const Parameters< crcpp_uint8, 5> & CRC_5_USB(); + static const Parameters< crcpp_uint8, 6> & CRC_6_CDMA2000A(); + static const Parameters< crcpp_uint8, 6> & CRC_6_CDMA2000B(); + static const Parameters< crcpp_uint8, 6> & CRC_6_ITU(); + static const Parameters< crcpp_uint8, 6> & CRC_6_NR(); + static const Parameters< crcpp_uint8, 7> & CRC_7(); +#endif + static const Parameters< crcpp_uint8, 8> & CRC_8(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters< crcpp_uint8, 8> & CRC_8_EBU(); + static const Parameters< crcpp_uint8, 8> & CRC_8_MAXIM(); + static const Parameters< crcpp_uint8, 8> & CRC_8_WCDMA(); + static const Parameters< crcpp_uint8, 8> & CRC_8_LTE(); + static const Parameters & CRC_10(); + static const Parameters & CRC_10_CDMA2000(); + static const Parameters & CRC_11(); + static const Parameters & CRC_11_NR(); + static const Parameters & CRC_12_CDMA2000(); + static const Parameters & CRC_12_DECT(); + static const Parameters & CRC_12_UMTS(); + static const Parameters & CRC_13_BBC(); + static const Parameters & CRC_15(); + static const Parameters & CRC_15_MPT1327(); +#endif + static const Parameters & CRC_16_ARC(); + static const Parameters & CRC_16_BUYPASS(); + static const Parameters & CRC_16_CCITTFALSE(); + static const Parameters & CRC_16_MCRF4XX(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters & CRC_16_CDMA2000(); + static const Parameters & CRC_16_CMS(); + static const Parameters & CRC_16_DECTR(); + static const Parameters & CRC_16_DECTX(); + static const Parameters & CRC_16_DNP(); +#endif + static const Parameters & CRC_16_GENIBUS(); + static const Parameters & CRC_16_KERMIT(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters & CRC_16_MAXIM(); + static const Parameters & CRC_16_MODBUS(); + static const Parameters & CRC_16_T10DIF(); + static const Parameters & CRC_16_USB(); +#endif + static const Parameters & CRC_16_X25(); + static const Parameters & CRC_16_XMODEM(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters & CRC_17_CAN(); + static const Parameters & CRC_21_CAN(); + static const Parameters & CRC_24(); + static const Parameters & CRC_24_FLEXRAYA(); + static const Parameters & CRC_24_FLEXRAYB(); + static const Parameters & CRC_24_LTEA(); + static const Parameters & CRC_24_LTEB(); + static const Parameters & CRC_24_NRC(); + static const Parameters & CRC_30(); +#endif + static const Parameters & CRC_32(); + static const Parameters & CRC_32_BZIP2(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters & CRC_32_C(); +#endif + static const Parameters & CRC_32_MPEG2(); + static const Parameters & CRC_32_POSIX(); +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + static const Parameters & CRC_32_Q(); + static const Parameters & CRC_40_GSM(); + static const Parameters & CRC_64(); +#endif + +#ifdef CRCPP_USE_CPP11 + CRC() = delete; + CRC(const CRC & other) = delete; + CRC & operator=(const CRC & other) = delete; + CRC(CRC && other) = delete; + CRC & operator=(CRC && other) = delete; +#endif + +private: +#ifndef CRCPP_USE_CPP11 + CRC(); + CRC(const CRC & other); + CRC & operator=(const CRC & other); +#endif + + template + static IntegerType Reflect(IntegerType value, crcpp_uint16 numBits); + + template + static CRCType Finalize(CRCType remainder, CRCType finalXOR, bool reflectOutput); + + template + static CRCType UndoFinalize(CRCType remainder, CRCType finalXOR, bool reflectOutput); + + template + static CRCType CalculateRemainder(const void * data, crcpp_size size, const Parameters & parameters, CRCType remainder); + + template + static CRCType CalculateRemainder(const void * data, crcpp_size size, const Table & lookupTable, CRCType remainder); + + template + static CRCType CalculateRemainderBits(unsigned char byte, crcpp_size numBits, const Parameters & parameters, CRCType remainder); +}; + +/** + @brief Returns a CRC lookup table construct using these CRC parameters. + @note This function primarily exists to allow use of the auto keyword instead of instantiating + a table directly, since template parameters are not inferred in constructors. + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC lookup table +*/ +template +inline CRC::Table CRC::Parameters::MakeTable() const +{ + // This should take advantage of RVO and optimize out the copy. + return CRC::Table(*this); +} + +/** + @brief Constructs a CRC table from a set of CRC parameters + @param[in] params CRC parameters + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC +*/ +template +inline CRC::Table::Table(const Parameters & params) : + parameters(params) +{ + InitTable(); +} + +#ifdef CRCPP_USE_CPP11 +/** + @brief Constructs a CRC table from a set of CRC parameters + @param[in] params CRC parameters + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC +*/ +template +inline CRC::Table::Table(Parameters && params) : + parameters(::std::move(params)) +{ + InitTable(); +} +#endif + +/** + @brief Gets the CRC parameters used to construct the CRC table + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC parameters +*/ +template +inline const CRC::Parameters & CRC::Table::GetParameters() const +{ + return parameters; +} + +/** + @brief Gets the CRC table + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC table +*/ +template +inline const CRCType * CRC::Table::GetTable() const +{ + return table; +} + +/** + @brief Gets an entry in the CRC table + @param[in] index Index into the CRC table + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC table entry +*/ +template +inline CRCType CRC::Table::operator[](unsigned char index) const +{ + return table[index]; +} + +/** + @brief Initializes a CRC table. + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC +*/ +template +inline void CRC::Table::InitTable() +{ + // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth) + static crcpp_constexpr CRCType BIT_MASK((CRCType(1) << (CRCWidth - CRCType(1))) | + ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1))); + + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); + + CRCType crc; + unsigned char byte = 0; + + // Loop over each dividend (each possible number storable in an unsigned char) + do + { + crc = CRC::CalculateRemainder(&byte, sizeof(byte), parameters, CRCType(0)); + + // This mask might not be necessary; all unit tests pass with this line commented out, + // but that might just be a coincidence based on the CRC parameters used for testing. + // In any case, this is harmless to leave in and only adds a single machine instruction per loop iteration. + crc &= BIT_MASK; + + if (!parameters.reflectInput && CRCWidth < CHAR_BIT) + { + // Undo the special operation at the end of the CalculateRemainder() + // function for non-reflected CRCs < CHAR_BIT. + crc = static_cast(crc << SHIFT); + } + + table[byte] = crc; + } + while (++byte); +} + +/** + @brief Computes a CRC. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bytes + @param[in] parameters CRC parameters + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Parameters & parameters) +{ + CRCType remainder = CalculateRemainder(data, size, parameters, parameters.initialValue); + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} +/** + @brief Appends additional data to a previous CRC calculation. + @note This function can be used to compute multi-part CRCs. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bytes + @param[in] parameters CRC parameters + @param[in] crc CRC from a previous calculation + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc) +{ + CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); + + remainder = CalculateRemainder(data, size, parameters, remainder); + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Computes a CRC via a lookup table. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bytes + @param[in] lookupTable CRC lookup table + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Table & lookupTable) +{ + const Parameters & parameters = lookupTable.GetParameters(); + + CRCType remainder = CalculateRemainder(data, size, lookupTable, parameters.initialValue); + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Appends additional data to a previous CRC calculation using a lookup table. + @note This function can be used to compute multi-part CRCs. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bytes + @param[in] lookupTable CRC lookup table + @param[in] crc CRC from a previous calculation + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc) +{ + const Parameters & parameters = lookupTable.GetParameters(); + + CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); + + remainder = CalculateRemainder(data, size, lookupTable, remainder); + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Computes a CRC. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bits + @param[in] parameters CRC parameters + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Parameters & parameters) +{ + CRCType remainder = parameters.initialValue; + + // Calculate the remainder on a whole number of bytes first, then call + // a special-case function for the remaining bits. + crcpp_size wholeNumberOfBytes = size / CHAR_BIT; + if (wholeNumberOfBytes > 0) + { + remainder = CalculateRemainder(data, wholeNumberOfBytes, parameters, remainder); + } + + crcpp_size remainingNumberOfBits = size % CHAR_BIT; + if (remainingNumberOfBits != 0) + { + unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); + remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); + } + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} +/** + @brief Appends additional data to a previous CRC calculation. + @note This function can be used to compute multi-part CRCs. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bits + @param[in] parameters CRC parameters + @param[in] crc CRC from a previous calculation + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Parameters & parameters, CRCType crc) +{ + CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); + + // Calculate the remainder on a whole number of bytes first, then call + // a special-case function for the remaining bits. + crcpp_size wholeNumberOfBytes = size / CHAR_BIT; + if (wholeNumberOfBytes > 0) + { + remainder = CalculateRemainder(data, wholeNumberOfBytes, parameters, parameters.initialValue); + } + + crcpp_size remainingNumberOfBits = size % CHAR_BIT; + if (remainingNumberOfBits != 0) + { + unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); + remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); + } + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Computes a CRC via a lookup table. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bits + @param[in] lookupTable CRC lookup table + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Table & lookupTable) +{ + const Parameters & parameters = lookupTable.GetParameters(); + + CRCType remainder = parameters.initialValue; + + // Calculate the remainder on a whole number of bytes first, then call + // a special-case function for the remaining bits. + crcpp_size wholeNumberOfBytes = size / CHAR_BIT; + if (wholeNumberOfBytes > 0) + { + remainder = CalculateRemainder(data, wholeNumberOfBytes, lookupTable, remainder); + } + + crcpp_size remainingNumberOfBits = size % CHAR_BIT; + if (remainingNumberOfBits != 0) + { + unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); + remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); + } + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Appends additional data to a previous CRC calculation using a lookup table. + @note This function can be used to compute multi-part CRCs. + @param[in] data Data over which CRC will be computed + @param[in] size Size of the data, in bits + @param[in] lookupTable CRC lookup table + @param[in] crc CRC from a previous calculation + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC +*/ +template +inline CRCType CRC::CalculateBits(const void * data, crcpp_size size, const Table & lookupTable, CRCType crc) +{ + const Parameters & parameters = lookupTable.GetParameters(); + + CRCType remainder = UndoFinalize(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); + + // Calculate the remainder on a whole number of bytes first, then call + // a special-case function for the remaining bits. + crcpp_size wholeNumberOfBytes = size / CHAR_BIT; + if (wholeNumberOfBytes > 0) + { + remainder = CalculateRemainder(data, wholeNumberOfBytes, lookupTable, parameters.initialValue); + } + + crcpp_size remainingNumberOfBits = size % CHAR_BIT; + if (remainingNumberOfBits > 0) + { + unsigned char lastByte = *(reinterpret_cast(data) + wholeNumberOfBytes); + remainder = CalculateRemainderBits(lastByte, remainingNumberOfBits, parameters, remainder); + } + + // No need to mask the remainder here; the mask will be applied in the Finalize() function. + + return Finalize(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput); +} + +/** + @brief Reflects (i.e. reverses the bits within) an integer value. + @param[in] value Value to reflect + @param[in] numBits Number of bits in the integer which will be reflected + @tparam IntegerType Integer type of the value being reflected + @return Reflected value +*/ +template +inline IntegerType CRC::Reflect(IntegerType value, crcpp_uint16 numBits) +{ + IntegerType reversedValue(0); + + for (crcpp_uint16 i = 0; i < numBits; ++i) + { + reversedValue = static_cast((reversedValue << 1) | (value & 1)); + value = static_cast(value >> 1); + } + + return reversedValue; +} + +/** + @brief Computes the final reflection and XOR of a CRC remainder. + @param[in] remainder CRC remainder to reflect and XOR + @param[in] finalXOR Final value to XOR with the remainder + @param[in] reflectOutput true to reflect each byte of the remainder before the XOR + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return Final CRC +*/ +template +inline CRCType CRC::Finalize(CRCType remainder, CRCType finalXOR, bool reflectOutput) +{ + // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth) + static crcpp_constexpr CRCType BIT_MASK = (CRCType(1) << (CRCWidth - CRCType(1))) | + ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1)); + + if (reflectOutput) + { + remainder = Reflect(remainder, CRCWidth); + } + + return (remainder ^ finalXOR) & BIT_MASK; +} + +/** + @brief Undoes the process of computing the final reflection and XOR of a CRC remainder. + @note This function allows for computation of multi-part CRCs + @note Calling UndoFinalize() followed by Finalize() (or vice versa) will always return the original remainder value: + + CRCType x = ...; + CRCType y = Finalize(x, finalXOR, reflectOutput); + CRCType z = UndoFinalize(y, finalXOR, reflectOutput); + assert(x == z); + + @param[in] crc Reflected and XORed CRC + @param[in] finalXOR Final value XORed with the remainder + @param[in] reflectOutput true if the remainder is to be reflected + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return Un-finalized CRC remainder +*/ +template +inline CRCType CRC::UndoFinalize(CRCType crc, CRCType finalXOR, bool reflectOutput) +{ + // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth) + static crcpp_constexpr CRCType BIT_MASK = (CRCType(1) << (CRCWidth - CRCType(1))) | + ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1)); + + crc = (crc & BIT_MASK) ^ finalXOR; + + if (reflectOutput) + { + crc = Reflect(crc, CRCWidth); + } + + return crc; +} + +/** + @brief Computes a CRC remainder. + @param[in] data Data over which the remainder will be computed + @param[in] size Size of the data, in bytes + @param[in] parameters CRC parameters + @param[in] remainder Running CRC remainder. Can be an initial value or the result of a previous CRC remainder calculation. + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC remainder +*/ +template +inline CRCType CRC::CalculateRemainder(const void * data, crcpp_size size, const Parameters & parameters, CRCType remainder) +{ +#ifdef CRCPP_USE_CPP11 + // This static_assert is put here because this function will always be compiled in no matter what + // the template parameters are and whether or not a table lookup or bit-by-bit algorithm is used. + static_assert(::std::numeric_limits::digits >= CRCWidth, "CRCType is too small to contain a CRC of width CRCWidth."); +#else + // Catching this compile-time error is very important. Sadly, the compiler error will be very cryptic, but it's + // better than nothing. + enum { static_assert_failed_CRCType_is_too_small_to_contain_a_CRC_of_width_CRCWidth = 1 / (::std::numeric_limits::digits >= CRCWidth ? 1 : 0) }; +#endif + + const unsigned char * current = reinterpret_cast(data); + + // Slightly different implementations based on the parameters. The current implementations try to eliminate as much + // computation from the inner loop (looping over each bit) as possible. + if (parameters.reflectInput) + { + CRCType polynomial = CRC::Reflect(parameters.polynomial, CRCWidth); + while (size--) + { + remainder = static_cast(remainder ^ *current++); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < CHAR_BIT; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & 1) + // remainder = (remainder >> 1) ^ polynomial; + // else + // remainder >>= 1; + remainder = static_cast((remainder >> 1) ^ ((remainder & 1) * polynomial)); +#else + remainder = static_cast((remainder & 1) ? ((remainder >> 1) ^ polynomial) : (remainder >> 1)); +#endif + } + } + } + else if (CRCWidth >= CHAR_BIT) + { + static crcpp_constexpr CRCType CRC_WIDTH_MINUS_ONE(CRCWidth - CRCType(1)); +#ifndef CRCPP_BRANCHLESS + static crcpp_constexpr CRCType CRC_HIGHEST_BIT_MASK(CRCType(1) << CRC_WIDTH_MINUS_ONE); +#endif + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CRCWidth >= CHAR_BIT) ? static_cast(CRCWidth - CHAR_BIT) : 0); + + while (size--) + { + remainder = static_cast(remainder ^ (static_cast(*current++) << SHIFT)); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < CHAR_BIT; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & CRC_HIGHEST_BIT_MASK) + // remainder = (remainder << 1) ^ parameters.polynomial; + // else + // remainder <<= 1; + remainder = static_cast((remainder << 1) ^ (((remainder >> CRC_WIDTH_MINUS_ONE) & 1) * parameters.polynomial)); +#else + remainder = static_cast((remainder & CRC_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ parameters.polynomial) : (remainder << 1)); +#endif + } + } + } + else + { + static crcpp_constexpr CRCType CHAR_BIT_MINUS_ONE(CHAR_BIT - 1); +#ifndef CRCPP_BRANCHLESS + static crcpp_constexpr CRCType CHAR_BIT_HIGHEST_BIT_MASK(CRCType(1) << CHAR_BIT_MINUS_ONE); +#endif + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); + + CRCType polynomial = static_cast(parameters.polynomial << SHIFT); + remainder = static_cast(remainder << SHIFT); + + while (size--) + { + remainder = static_cast(remainder ^ *current++); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < CHAR_BIT; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & CHAR_BIT_HIGHEST_BIT_MASK) + // remainder = (remainder << 1) ^ polynomial; + // else + // remainder <<= 1; + remainder = static_cast((remainder << 1) ^ (((remainder >> CHAR_BIT_MINUS_ONE) & 1) * polynomial)); +#else + remainder = static_cast((remainder & CHAR_BIT_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ polynomial) : (remainder << 1)); +#endif + } + } + + remainder = static_cast(remainder >> SHIFT); + } + + return remainder; +} + +/** + @brief Computes a CRC remainder using lookup table. + @param[in] data Data over which the remainder will be computed + @param[in] size Size of the data, in bytes + @param[in] lookupTable CRC lookup table + @param[in] remainder Running CRC remainder. Can be an initial value or the result of a previous CRC remainder calculation. + @tparam CRCType Integer type for storing the CRC result + @tparam CRCWidth Number of bits in the CRC + @return CRC remainder +*/ +template +inline CRCType CRC::CalculateRemainder(const void * data, crcpp_size size, const Table & lookupTable, CRCType remainder) +{ + const unsigned char * current = reinterpret_cast(data); + + if (lookupTable.GetParameters().reflectInput) + { + while (size--) + { +#if defined(WIN32) || defined(_WIN32) || defined(WINCE) + // Disable warning about data loss when doing (remainder >> CHAR_BIT) when + // remainder is one byte long. The algorithm is still correct in this case, + // though it's possible that one additional machine instruction will be executed. +# pragma warning (push) +# pragma warning (disable : 4333) +#endif + remainder = static_cast((remainder >> CHAR_BIT) ^ lookupTable[static_cast(remainder ^ *current++)]); +#if defined(WIN32) || defined(_WIN32) || defined(WINCE) +# pragma warning (pop) +#endif + } + } + else if (CRCWidth >= CHAR_BIT) + { + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CRCWidth >= CHAR_BIT) ? static_cast(CRCWidth - CHAR_BIT) : 0); + + while (size--) + { + remainder = static_cast((remainder << CHAR_BIT) ^ lookupTable[static_cast((remainder >> SHIFT) ^ *current++)]); + } + } + else + { + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); + + remainder = static_cast(remainder << SHIFT); + + while (size--) + { + // Note: no need to mask here since remainder is guaranteed to fit in a single byte. + remainder = lookupTable[static_cast(remainder ^ *current++)]; + } + + remainder = static_cast(remainder >> SHIFT); + } + + return remainder; +} + +template +inline CRCType CRC::CalculateRemainderBits(unsigned char byte, crcpp_size numBits, const Parameters & parameters, CRCType remainder) +{ + // Slightly different implementations based on the parameters. The current implementations try to eliminate as much + // computation from the inner loop (looping over each bit) as possible. + if (parameters.reflectInput) + { + CRCType polynomial = CRC::Reflect(parameters.polynomial, CRCWidth); + remainder = static_cast(remainder ^ byte); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < numBits; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & 1) + // remainder = (remainder >> 1) ^ polynomial; + // else + // remainder >>= 1; + remainder = static_cast((remainder >> 1) ^ ((remainder & 1) * polynomial)); +#else + remainder = static_cast((remainder & 1) ? ((remainder >> 1) ^ polynomial) : (remainder >> 1)); +#endif + } + } + else if (CRCWidth >= CHAR_BIT) + { + static crcpp_constexpr CRCType CRC_WIDTH_MINUS_ONE(CRCWidth - CRCType(1)); +#ifndef CRCPP_BRANCHLESS + static crcpp_constexpr CRCType CRC_HIGHEST_BIT_MASK(CRCType(1) << CRC_WIDTH_MINUS_ONE); +#endif + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CRCWidth >= CHAR_BIT) ? static_cast(CRCWidth - CHAR_BIT) : 0); + + remainder = static_cast(remainder ^ (static_cast(byte) << SHIFT)); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < numBits; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & CRC_HIGHEST_BIT_MASK) + // remainder = (remainder << 1) ^ parameters.polynomial; + // else + // remainder <<= 1; + remainder = static_cast((remainder << 1) ^ (((remainder >> CRC_WIDTH_MINUS_ONE) & 1) * parameters.polynomial)); +#else + remainder = static_cast((remainder & CRC_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ parameters.polynomial) : (remainder << 1)); +#endif + } + } + else + { + static crcpp_constexpr CRCType CHAR_BIT_MINUS_ONE(CHAR_BIT - 1); +#ifndef CRCPP_BRANCHLESS + static crcpp_constexpr CRCType CHAR_BIT_HIGHEST_BIT_MASK(CRCType(1) << CHAR_BIT_MINUS_ONE); +#endif + // The conditional expression is used to avoid a -Wshift-count-overflow warning. + static crcpp_constexpr CRCType SHIFT((CHAR_BIT >= CRCWidth) ? static_cast(CHAR_BIT - CRCWidth) : 0); + + CRCType polynomial = static_cast(parameters.polynomial << SHIFT); + remainder = static_cast((remainder << SHIFT) ^ byte); + + // An optimizing compiler might choose to unroll this loop. + for (crcpp_size i = 0; i < numBits; ++i) + { +#ifdef CRCPP_BRANCHLESS + // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following: + // if (remainder & CHAR_BIT_HIGHEST_BIT_MASK) + // remainder = (remainder << 1) ^ polynomial; + // else + // remainder <<= 1; + remainder = static_cast((remainder << 1) ^ (((remainder >> CHAR_BIT_MINUS_ONE) & 1) * polynomial)); +#else + remainder = static_cast((remainder & CHAR_BIT_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ polynomial) : (remainder << 1)); +#endif + } + + remainder = static_cast(remainder >> SHIFT); + } + + return remainder; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-4 ITU. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-4 ITU has the following parameters and check value: + - polynomial = 0x3 + - initial value = 0x0 + - final XOR = 0x0 + - reflect input = true + - reflect output = true + - check value = 0x7 + @return CRC-4 ITU parameters +*/ +inline const CRC::Parameters & CRC::CRC_4_ITU() +{ + static const Parameters parameters = { 0x3, 0x0, 0x0, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-5 EPC. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-5 EPC has the following parameters and check value: + - polynomial = 0x09 + - initial value = 0x09 + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0x00 + @return CRC-5 EPC parameters +*/ +inline const CRC::Parameters & CRC::CRC_5_EPC() +{ + static const Parameters parameters = { 0x09, 0x09, 0x00, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-5 ITU. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-5 ITU has the following parameters and check value: + - polynomial = 0x15 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = true + - reflect output = true + - check value = 0x07 + @return CRC-5 ITU parameters +*/ +inline const CRC::Parameters & CRC::CRC_5_ITU() +{ + static const Parameters parameters = { 0x15, 0x00, 0x00, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-5 USB. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-5 USB has the following parameters and check value: + - polynomial = 0x05 + - initial value = 0x1F + - final XOR = 0x1F + - reflect input = true + - reflect output = true + - check value = 0x19 + @return CRC-5 USB parameters +*/ +inline const CRC::Parameters & CRC::CRC_5_USB() +{ + static const Parameters parameters = { 0x05, 0x1F, 0x1F, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-6 CDMA2000-A. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-6 CDMA2000-A has the following parameters and check value: + - polynomial = 0x27 + - initial value = 0x3F + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0x0D + @return CRC-6 CDMA2000-A parameters +*/ +inline const CRC::Parameters & CRC::CRC_6_CDMA2000A() +{ + static const Parameters parameters = { 0x27, 0x3F, 0x00, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-6 CDMA2000-B. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-6 CDMA2000-A has the following parameters and check value: + - polynomial = 0x07 + - initial value = 0x3F + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0x3B + @return CRC-6 CDMA2000-B parameters +*/ +inline const CRC::Parameters & CRC::CRC_6_CDMA2000B() +{ + static const Parameters parameters = { 0x07, 0x3F, 0x00, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-6 ITU. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-6 ITU has the following parameters and check value: + - polynomial = 0x03 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = true + - reflect output = true + - check value = 0x06 + @return CRC-6 ITU parameters +*/ +inline const CRC::Parameters & CRC::CRC_6_ITU() +{ + static const Parameters parameters = { 0x03, 0x00, 0x00, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-6 NR. + @note The parameters are static and are delayed-constructed to reduce memory + footprint. + @note CRC-6 NR has the following parameters and check value: + - polynomial = 0x21 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0x15 + @return CRC-6 NR parameters +*/ +inline const CRC::Parameters & CRC::CRC_6_NR() +{ + static const Parameters parameters = { 0x21, 0x00, 0x00, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-7 JEDEC. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-7 JEDEC has the following parameters and check value: + - polynomial = 0x09 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0x75 + @return CRC-7 JEDEC parameters +*/ +inline const CRC::Parameters & CRC::CRC_7() +{ + static const Parameters parameters = { 0x09, 0x00, 0x00, false, false }; + return parameters; +} +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +/** + @brief Returns a set of parameters for CRC-8 SMBus. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-8 SMBus has the following parameters and check value: + - polynomial = 0x07 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0xF4 + @return CRC-8 SMBus parameters +*/ +inline const CRC::Parameters & CRC::CRC_8() +{ + static const Parameters parameters = { 0x07, 0x00, 0x00, false, false }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-8 EBU (aka CRC-8 AES). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-8 EBU has the following parameters and check value: + - polynomial = 0x1D + - initial value = 0xFF + - final XOR = 0x00 + - reflect input = true + - reflect output = true + - check value = 0x97 + @return CRC-8 EBU parameters +*/ +inline const CRC::Parameters & CRC::CRC_8_EBU() +{ + static const Parameters parameters = { 0x1D, 0xFF, 0x00, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-8 MAXIM (aka CRC-8 DOW-CRC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-8 MAXIM has the following parameters and check value: + - polynomial = 0x31 + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = true + - reflect output = true + - check value = 0xA1 + @return CRC-8 MAXIM parameters +*/ +inline const CRC::Parameters & CRC::CRC_8_MAXIM() +{ + static const Parameters parameters = { 0x31, 0x00, 0x00, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-8 WCDMA. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-8 WCDMA has the following parameters and check value: + - polynomial = 0x9B + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = true + - reflect output = true + - check value = 0x25 + @return CRC-8 WCDMA parameters +*/ +inline const CRC::Parameters & CRC::CRC_8_WCDMA() +{ + static const Parameters parameters = { 0x9B, 0x00, 0x00, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-8 LTE. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-8 LTE has the following parameters and check value: + - polynomial = 0x9B + - initial value = 0x00 + - final XOR = 0x00 + - reflect input = false + - reflect output = false + - check value = 0xEA + @return CRC-8 LTE parameters +*/ +inline const CRC::Parameters & CRC::CRC_8_LTE() +{ + static const Parameters parameters = { 0x9B, 0x00, 0x00, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-10 ITU. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-10 ITU has the following parameters and check value: + - polynomial = 0x233 + - initial value = 0x000 + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0x199 + @return CRC-10 ITU parameters +*/ +inline const CRC::Parameters & CRC::CRC_10() +{ + static const Parameters parameters = { 0x233, 0x000, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-10 CDMA2000. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-10 CDMA2000 has the following parameters and check value: + - polynomial = 0x3D9 + - initial value = 0x3FF + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0x233 + @return CRC-10 CDMA2000 parameters +*/ +inline const CRC::Parameters & CRC::CRC_10_CDMA2000() +{ + static const Parameters parameters = { 0x3D9, 0x3FF, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-11 FlexRay. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-11 FlexRay has the following parameters and check value: + - polynomial = 0x385 + - initial value = 0x01A + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0x5A3 + @return CRC-11 FlexRay parameters +*/ +inline const CRC::Parameters & CRC::CRC_11() +{ + static const Parameters parameters = { 0x385, 0x01A, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-11 NR. + @note The parameters are static and are delayed-constructed to reduce memory + footprint. + @note CRC-11 NR has the following parameters and check value: + - polynomial = 0x621 + - initial value = 0x000 + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0x5CA + @return CRC-11 NR parameters +*/ +inline const CRC::Parameters & CRC::CRC_11_NR() +{ + static const Parameters parameters = { 0x621, 0x000, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-12 CDMA2000. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-12 CDMA2000 has the following parameters and check value: + - polynomial = 0xF13 + - initial value = 0xFFF + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0xD4D + @return CRC-12 CDMA2000 parameters +*/ +inline const CRC::Parameters & CRC::CRC_12_CDMA2000() +{ + static const Parameters parameters = { 0xF13, 0xFFF, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-12 DECT (aka CRC-12 X-CRC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-12 DECT has the following parameters and check value: + - polynomial = 0x80F + - initial value = 0x000 + - final XOR = 0x000 + - reflect input = false + - reflect output = false + - check value = 0xF5B + @return CRC-12 DECT parameters +*/ +inline const CRC::Parameters & CRC::CRC_12_DECT() +{ + static const Parameters parameters = { 0x80F, 0x000, 0x000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-12 UMTS (aka CRC-12 3GPP). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-12 UMTS has the following parameters and check value: + - polynomial = 0x80F + - initial value = 0x000 + - final XOR = 0x000 + - reflect input = false + - reflect output = true + - check value = 0xDAF + @return CRC-12 UMTS parameters +*/ +inline const CRC::Parameters & CRC::CRC_12_UMTS() +{ + static const Parameters parameters = { 0x80F, 0x000, 0x000, false, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-13 BBC. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-13 BBC has the following parameters and check value: + - polynomial = 0x1CF5 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x04FA + @return CRC-13 BBC parameters +*/ +inline const CRC::Parameters & CRC::CRC_13_BBC() +{ + static const Parameters parameters = { 0x1CF5, 0x0000, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-15 CAN. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-15 CAN has the following parameters and check value: + - polynomial = 0x4599 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x059E + @return CRC-15 CAN parameters +*/ +inline const CRC::Parameters & CRC::CRC_15() +{ + static const Parameters parameters = { 0x4599, 0x0000, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-15 MPT1327. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-15 MPT1327 has the following parameters and check value: + - polynomial = 0x6815 + - initial value = 0x0000 + - final XOR = 0x0001 + - reflect input = false + - reflect output = false + - check value = 0x2566 + @return CRC-15 MPT1327 parameters +*/ +inline const CRC::Parameters & CRC::CRC_15_MPT1327() +{ + static const Parameters parameters = { 0x6815, 0x0000, 0x0001, false, false }; + return parameters; +} +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +/** + @brief Returns a set of parameters for CRC-16 ARC (aka CRC-16 IBM, CRC-16 LHA). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 ARC has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = true + - reflect output = true + - check value = 0xBB3D + @return CRC-16 ARC parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_ARC() +{ + static const Parameters parameters = { 0x8005, 0x0000, 0x0000, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 BUYPASS (aka CRC-16 VERIFONE, CRC-16 UMTS). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 BUYPASS has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0xFEE8 + @return CRC-16 BUYPASS parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_BUYPASS() +{ + static const Parameters parameters = { 0x8005, 0x0000, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 CCITT FALSE. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 CCITT FALSE has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0xFFFF + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x29B1 + @return CRC-16 CCITT FALSE parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_CCITTFALSE() +{ + static const Parameters parameters = { 0x1021, 0xFFFF, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 MCRF4XX. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 MCRF4XX has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0xFFFF + - final XOR = 0x0000 + - reflect input = true + - reflect output = true + - check value = 0x6F91 + @return CRC-16 MCRF4XX parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_MCRF4XX() +{ + static const Parameters parameters = { 0x1021, 0xFFFF, 0x0000, true, true}; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-16 CDMA2000. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 CDMA2000 has the following parameters and check value: + - polynomial = 0xC867 + - initial value = 0xFFFF + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x4C06 + @return CRC-16 CDMA2000 parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_CDMA2000() +{ + static const Parameters parameters = { 0xC867, 0xFFFF, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 CMS. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 CMS has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0xFFFF + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0xAEE7 + @return CRC-16 CMS parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_CMS() +{ + static const Parameters parameters = { 0x8005, 0xFFFF, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 DECT-R (aka CRC-16 R-CRC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 DECT-R has the following parameters and check value: + - polynomial = 0x0589 + - initial value = 0x0000 + - final XOR = 0x0001 + - reflect input = false + - reflect output = false + - check value = 0x007E + @return CRC-16 DECT-R parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_DECTR() +{ + static const Parameters parameters = { 0x0589, 0x0000, 0x0001, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 DECT-X (aka CRC-16 X-CRC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 DECT-X has the following parameters and check value: + - polynomial = 0x0589 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x007F + @return CRC-16 DECT-X parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_DECTX() +{ + static const Parameters parameters = { 0x0589, 0x0000, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 DNP. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 DNP has the following parameters and check value: + - polynomial = 0x3D65 + - initial value = 0x0000 + - final XOR = 0xFFFF + - reflect input = true + - reflect output = true + - check value = 0xEA82 + @return CRC-16 DNP parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_DNP() +{ + static const Parameters parameters = { 0x3D65, 0x0000, 0xFFFF, true, true }; + return parameters; +} +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +/** + @brief Returns a set of parameters for CRC-16 GENIBUS (aka CRC-16 EPC, CRC-16 I-CODE, CRC-16 DARC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 GENIBUS has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0xFFFF + - final XOR = 0xFFFF + - reflect input = false + - reflect output = false + - check value = 0xD64E + @return CRC-16 GENIBUS parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_GENIBUS() +{ + static const Parameters parameters = { 0x1021, 0xFFFF, 0xFFFF, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 KERMIT (aka CRC-16 CCITT, CRC-16 CCITT-TRUE). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 KERMIT has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = true + - reflect output = true + - check value = 0x2189 + @return CRC-16 KERMIT parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_KERMIT() +{ + static const Parameters parameters = { 0x1021, 0x0000, 0x0000, true, true }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-16 MAXIM. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 MAXIM has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0x0000 + - final XOR = 0xFFFF + - reflect input = true + - reflect output = true + - check value = 0x44C2 + @return CRC-16 MAXIM parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_MAXIM() +{ + static const Parameters parameters = { 0x8005, 0x0000, 0xFFFF, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 MODBUS. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 MODBUS has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0xFFFF + - final XOR = 0x0000 + - reflect input = true + - reflect output = true + - check value = 0x4B37 + @return CRC-16 MODBUS parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_MODBUS() +{ + static const Parameters parameters = { 0x8005, 0xFFFF, 0x0000, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 T10-DIF. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 T10-DIF has the following parameters and check value: + - polynomial = 0x8BB7 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0xD0DB + @return CRC-16 T10-DIF parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_T10DIF() +{ + static const Parameters parameters = { 0x8BB7, 0x0000, 0x0000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 USB. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 USB has the following parameters and check value: + - polynomial = 0x8005 + - initial value = 0xFFFF + - final XOR = 0xFFFF + - reflect input = true + - reflect output = true + - check value = 0xB4C8 + @return CRC-16 USB parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_USB() +{ + static const Parameters parameters = { 0x8005, 0xFFFF, 0xFFFF, true, true }; + return parameters; +} + +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +/** + @brief Returns a set of parameters for CRC-16 X-25 (aka CRC-16 IBM-SDLC, CRC-16 ISO-HDLC, CRC-16 B). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 X-25 has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0xFFFF + - final XOR = 0xFFFF + - reflect input = true + - reflect output = true + - check value = 0x906E + @return CRC-16 X-25 parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_X25() +{ + static const Parameters parameters = { 0x1021, 0xFFFF, 0xFFFF, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-16 XMODEM (aka CRC-16 ZMODEM, CRC-16 ACORN, CRC-16 LTE). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-16 XMODEM has the following parameters and check value: + - polynomial = 0x1021 + - initial value = 0x0000 + - final XOR = 0x0000 + - reflect input = false + - reflect output = false + - check value = 0x31C3 + @return CRC-16 XMODEM parameters +*/ +inline const CRC::Parameters & CRC::CRC_16_XMODEM() +{ + static const Parameters parameters = { 0x1021, 0x0000, 0x0000, false, false }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-17 CAN. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-17 CAN has the following parameters and check value: + - polynomial = 0x1685B + - initial value = 0x00000 + - final XOR = 0x00000 + - reflect input = false + - reflect output = false + - check value = 0x04F03 + @return CRC-17 CAN parameters +*/ +inline const CRC::Parameters & CRC::CRC_17_CAN() +{ + static const Parameters parameters = { 0x1685B, 0x00000, 0x00000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-21 CAN. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-21 CAN has the following parameters and check value: + - polynomial = 0x102899 + - initial value = 0x000000 + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0x0ED841 + @return CRC-21 CAN parameters +*/ +inline const CRC::Parameters & CRC::CRC_21_CAN() +{ + static const Parameters parameters = { 0x102899, 0x000000, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 OPENPGP. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-24 OPENPGP has the following parameters and check value: + - polynomial = 0x864CFB + - initial value = 0xB704CE + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0x21CF02 + @return CRC-24 OPENPGP parameters +*/ +inline const CRC::Parameters & CRC::CRC_24() +{ + static const Parameters parameters = { 0x864CFB, 0xB704CE, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 FlexRay-A. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-24 FlexRay-A has the following parameters and check value: + - polynomial = 0x5D6DCB + - initial value = 0xFEDCBA + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0x7979BD + @return CRC-24 FlexRay-A parameters +*/ +inline const CRC::Parameters & CRC::CRC_24_FLEXRAYA() +{ + static const Parameters parameters = { 0x5D6DCB, 0xFEDCBA, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 FlexRay-B. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-24 FlexRay-B has the following parameters and check value: + - polynomial = 0x5D6DCB + - initial value = 0xABCDEF + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0x1F23B8 + @return CRC-24 FlexRay-B parameters +*/ +inline const CRC::Parameters & CRC::CRC_24_FLEXRAYB() +{ + static const Parameters parameters = { 0x5D6DCB, 0xABCDEF, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 LTE-A/NR-A. + @note The parameters are static and are delayed-constructed to reduce memory + footprint. + @note CRC-24 LTE-A has the following parameters and check value: + - polynomial = 0x864CFB + - initial value = 0x000000 + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0xCDE703 + @return CRC-24 LTE-A parameters +*/ +inline const CRC::Parameters & CRC::CRC_24_LTEA() +{ + static const Parameters parameters = { 0x864CFB, 0x000000, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 LTE-B/NR-B. + @note The parameters are static and are delayed-constructed to reduce memory + footprint. + @note CRC-24 LTE-B has the following parameters and check value: + - polynomial = 0x800063 + - initial value = 0x000000 + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0x23EF52 + @return CRC-24 LTE-B parameters +*/ +inline const CRC::Parameters & CRC::CRC_24_LTEB() +{ + static const Parameters parameters = { 0x800063, 0x000000, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-24 NR-C. + @note The parameters are static and are delayed-constructed to reduce memory + footprint. + @note CRC-24 NR-C has the following parameters and check value: + - polynomial = 0xB2B117 + - initial value = 0x000000 + - final XOR = 0x000000 + - reflect input = false + - reflect output = false + - check value = 0xF48279 + @return CRC-24 NR-C parameters +*/ +inline const CRC::Parameters & CRC::CRC_24_NRC() +{ + static const Parameters parameters = { 0xB2B117, 0x000000, 0x000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-30 CDMA. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-30 CDMA has the following parameters and check value: + - polynomial = 0x2030B9C7 + - initial value = 0x3FFFFFFF + - final XOR = 0x00000000 + - reflect input = false + - reflect output = false + - check value = 0x3B3CB540 + @return CRC-30 CDMA parameters +*/ +inline const CRC::Parameters & CRC::CRC_30() +{ + static const Parameters parameters = { 0x2030B9C7, 0x3FFFFFFF, 0x00000000, false, false }; + return parameters; +} +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +/** + @brief Returns a set of parameters for CRC-32 (aka CRC-32 ADCCP, CRC-32 PKZip). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 has the following parameters and check value: + - polynomial = 0x04C11DB7 + - initial value = 0xFFFFFFFF + - final XOR = 0xFFFFFFFF + - reflect input = true + - reflect output = true + - check value = 0xCBF43926 + @return CRC-32 parameters +*/ +inline const CRC::Parameters & CRC::CRC_32() +{ + static const Parameters parameters = { 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-32 BZIP2 (aka CRC-32 AAL5, CRC-32 DECT-B, CRC-32 B-CRC). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 BZIP2 has the following parameters and check value: + - polynomial = 0x04C11DB7 + - initial value = 0xFFFFFFFF + - final XOR = 0xFFFFFFFF + - reflect input = false + - reflect output = false + - check value = 0xFC891918 + @return CRC-32 BZIP2 parameters +*/ +inline const CRC::Parameters & CRC::CRC_32_BZIP2() +{ + static const Parameters parameters = { 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, false, false }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-32 C (aka CRC-32 ISCSI, CRC-32 Castagnoli, CRC-32 Interlaken). + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 C has the following parameters and check value: + - polynomial = 0x1EDC6F41 + - initial value = 0xFFFFFFFF + - final XOR = 0xFFFFFFFF + - reflect input = true + - reflect output = true + - check value = 0xE3069283 + @return CRC-32 C parameters +*/ +inline const CRC::Parameters & CRC::CRC_32_C() +{ + static const Parameters parameters = { 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true }; + return parameters; +} +#endif + +/** + @brief Returns a set of parameters for CRC-32 MPEG-2. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 MPEG-2 has the following parameters and check value: + - polynomial = 0x04C11DB7 + - initial value = 0xFFFFFFFF + - final XOR = 0x00000000 + - reflect input = false + - reflect output = false + - check value = 0x0376E6E7 + @return CRC-32 MPEG-2 parameters +*/ +inline const CRC::Parameters & CRC::CRC_32_MPEG2() +{ + static const Parameters parameters = { 0x04C11DB7, 0xFFFFFFFF, 0x00000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-32 POSIX. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 POSIX has the following parameters and check value: + - polynomial = 0x04C11DB7 + - initial value = 0x00000000 + - final XOR = 0xFFFFFFFF + - reflect input = false + - reflect output = false + - check value = 0x765E7680 + @return CRC-32 POSIX parameters +*/ +inline const CRC::Parameters & CRC::CRC_32_POSIX() +{ + static const Parameters parameters = { 0x04C11DB7, 0x00000000, 0xFFFFFFFF, false, false }; + return parameters; +} + +#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS +/** + @brief Returns a set of parameters for CRC-32 Q. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-32 Q has the following parameters and check value: + - polynomial = 0x814141AB + - initial value = 0x00000000 + - final XOR = 0x00000000 + - reflect input = false + - reflect output = false + - check value = 0x3010BF7F + @return CRC-32 Q parameters +*/ +inline const CRC::Parameters & CRC::CRC_32_Q() +{ + static const Parameters parameters = { 0x814141AB, 0x00000000, 0x00000000, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-40 GSM. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-40 GSM has the following parameters and check value: + - polynomial = 0x0004820009 + - initial value = 0x0000000000 + - final XOR = 0xFFFFFFFFFF + - reflect input = false + - reflect output = false + - check value = 0xD4164FC646 + @return CRC-40 GSM parameters +*/ +inline const CRC::Parameters & CRC::CRC_40_GSM() +{ + static const Parameters parameters = { 0x0004820009, 0x0000000000, 0xFFFFFFFFFF, false, false }; + return parameters; +} + +/** + @brief Returns a set of parameters for CRC-64 ECMA. + @note The parameters are static and are delayed-constructed to reduce memory footprint. + @note CRC-64 ECMA has the following parameters and check value: + - polynomial = 0x42F0E1EBA9EA3693 + - initial value = 0x0000000000000000 + - final XOR = 0x0000000000000000 + - reflect input = false + - reflect output = false + - check value = 0x6C40DF5F0B497347 + @return CRC-64 ECMA parameters +*/ +inline const CRC::Parameters & CRC::CRC_64() +{ + static const Parameters parameters = { 0x42F0E1EBA9EA3693, 0x0000000000000000, 0x0000000000000000, false, false }; + return parameters; +} +#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS + +#ifdef CRCPP_USE_NAMESPACE +} +#endif + +#endif // CRCPP_CRC_H_ diff --git a/dependencies/ColorWheel/ColorWheel.cpp b/dependencies/ColorWheel/ColorWheel.cpp new file mode 100644 index 0000000..c865e62 --- /dev/null +++ b/dependencies/ColorWheel/ColorWheel.cpp @@ -0,0 +1,497 @@ +/*-----------------------------------------------------*\ +| ColorWheel.cpp | +| | +| Color wheel selector widget for Qt | +| | +| Original: https://github.com/liuyanghejerry/Qt-Plus | +| | +| Modified by Adam Honse (calcprogrammer1@gmail.com) | +\*-----------------------------------------------------*/ + +#include "ColorWheel.h" +#include +#include +#include +#include +#include +#include + +ColorWheel::ColorWheel(QWidget *parent) : + QWidget(parent), + initSize(128,128), + mouseDown(false), + margin(0), + wheelWidth(10), + current(Qt::red), + inWheel(false), + inSquare(false) +{ + current = current.toHsv(); +} + +QColor ColorWheel::color() +{ + return current; +} + +void ColorWheel::setColor(const QColor &color) +{ + if(color == current) return; + if(color.hue() != current.hue()) + { + hueChanged(color.hue()); + } + + if((color.saturation() != current.saturation()) || (color.value() != current.value())) + { + svChanged(color); + } + + update(); + emit colorChanged(color); +} + + +QColor ColorWheel::posColor(const QPoint &point) +{ + /*-----------------------------------------------------*\ + | Subtract offsets from point value | + \*-----------------------------------------------------*/ + int point_x = point.x() - x_offset; + int point_y = point.y() - y_offset; + + /*-----------------------------------------------------*\ + | If within wheel region, update hue from point | + | position | + \*-----------------------------------------------------*/ + if(inWheel) + { + qreal hue = 0; + int r = qMin(width() - x_offset, height() - y_offset) / 2; + if( point_x > r ) + { + if(point_y < r ) + { + //1 + hue = 90 - (qAtan2( (point_x - r) , (r - point_y) ) / 3.14 / 2 * 360); + } + else + { + //4 + hue = 270 + (qAtan2( (point_x - r) , (point_y - r ) ) / 3.14 / 2 * 360); + } + } + else + { + if(point_y < r ) + { + //2 + hue = 90 + (qAtan2( (r - point_x) , (r - point_y) ) / 3.14 / 2 * 360); + } + else + { + //3 + hue = 270 - (qAtan2( (r - point_x) , (point_y - r )) / 3.14 / 2 * 360); + } + } + + /*-----------------------------------------------------*\ + | Restrict hue to range 0-359 | + \*-----------------------------------------------------*/ + hue = (hue > 359) ? 359 : hue; + hue = hue < 0 ? 0 : hue; + + return QColor::fromHsv(hue, + current.saturation(), + current.value()); + } + + /*-----------------------------------------------------*\ + | If within square region, update saturation and value | + | from point position | + \*-----------------------------------------------------*/ + if(inSquare) + { + // region of the widget + int w = qMin(width() - x_offset, height() - y_offset); + + // radius of outer circle + qreal r = w/2 - margin; + + // radius of inner circle + qreal ir = r - wheelWidth; + + // left corner of square + qreal m = w/2.0 - ir/qSqrt(2); + + QPoint p = point - QPoint(x_offset, y_offset) - QPoint(m, m); + qreal SquareWidth = 2*ir/qSqrt(2); + qreal saturation = qBound(0.0, p.x()/SquareWidth, 1.0); + qreal value = qBound(0.0, p.y()/SquareWidth, 1.0); + + return QColor::fromHsvF( current.hueF(), + saturation, + value); + } + return QColor(); +} + +QSize ColorWheel::sizeHint () const +{ + return QSize(height(),height()); +} + +QSize ColorWheel::minimumSizeHint () const +{ + return initSize; +} + +void ColorWheel::mousePressEvent(QMouseEvent *event) +{ + /*-----------------------------------------------------*\ + | Update last position | + \*-----------------------------------------------------*/ + lastPos = event->pos(); + + /*-----------------------------------------------------*\ + | If mouse is within wheel region, process wheel (hue) | + \*-----------------------------------------------------*/ + if(wheelRegion.contains(lastPos)) + { + inWheel = true; + inSquare = false; + QColor color = posColor(lastPos); + hueChanged(color.hue()); + } + + /*-----------------------------------------------------*\ + | If mouse is within square region, process square | + | (saturation and value) | + \*-----------------------------------------------------*/ + else if(squareRegion.contains(lastPos)) + { + inWheel = false; + inSquare = true; + QColor color = posColor(lastPos); + svChanged(color); + } + + /*-----------------------------------------------------*\ + | Set the mouse down flag if the click started inside a | + | selectable region | + \*-----------------------------------------------------*/ + mouseDown = inWheel || inSquare; +} + +void ColorWheel::mouseMoveEvent(QMouseEvent *event) +{ + /*-----------------------------------------------------*\ + | Update last position | + \*-----------------------------------------------------*/ + lastPos = event->pos(); + + /*-----------------------------------------------------*\ + | Don't process if mouse button is not down | + \*-----------------------------------------------------*/ + if(!mouseDown) + { + return; + } + + /*-----------------------------------------------------*\ + | If dragging started in the wheel, continue processing | + | hue from the cursor angle even outside the wheel | + \*-----------------------------------------------------*/ + if(inWheel) + { + QColor color = posColor(lastPos); + hueChanged(color.hue()); + } + + /*-----------------------------------------------------*\ + | If dragging started in the square, continue processing| + | saturation and value with clamped coordinates | + \*-----------------------------------------------------*/ + else if(inSquare) + { + QColor color = posColor(lastPos); + svChanged(color); + } +} + +void ColorWheel::mouseReleaseEvent(QMouseEvent *) +{ + /*-----------------------------------------------------*\ + | Clear mouse down and in-region flags | + \*-----------------------------------------------------*/ + mouseDown = false; + inWheel = false; + inSquare = false; +} + +void ColorWheel::resizeEvent(QResizeEvent *event) +{ + unsigned int size = 0; + + if(event->size().width() < event->size().height()) + { + size = event->size().width(); + } + else + { + size = event->size().height(); + } + + wheelWidth = 0.1 * size; + + wheel = QPixmap(event->size()); + wheel.fill(Qt::transparent); + drawWheelImage(event->size()); + drawSquareImage(current.hue()); + update(); +} + +void ColorWheel::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + QStyleOption opt; + opt.initFrom(this); + composeWheel(); + painter.drawPixmap(0,0,wheel); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); +} + +void ColorWheel::drawWheelImage(const QSize &newSize) +{ + /*-----------------------------------------------------*\ + | Create image canvas | + \*-----------------------------------------------------*/ + wheelImage = QImage(newSize, QImage::Format_ARGB32_Premultiplied); + + /*-----------------------------------------------------*\ + | Paint the background | + \*-----------------------------------------------------*/ + wheelImage.fill(Qt::transparent); + + /*-----------------------------------------------------*\ + | Create rainbow gradient for wheel | + \*-----------------------------------------------------*/ + QConicalGradient conicalGradient(0, 0, 0); + conicalGradient.setColorAt(0.0, Qt::red); + conicalGradient.setColorAt(60.0 / 360.0, Qt::yellow); + conicalGradient.setColorAt(120.0 / 360.0, Qt::green); + conicalGradient.setColorAt(180.0 / 360.0, Qt::cyan); + conicalGradient.setColorAt(240.0 / 360.0, Qt::blue); + conicalGradient.setColorAt(300.0 / 360.0, Qt::magenta); + conicalGradient.setColorAt(1.0, Qt::red); + + /*-----------------------------------------------------*\ + | Set up painter with antialiasing | + \*-----------------------------------------------------*/ + QPainter painter(&wheelImage); + painter.setRenderHint(QPainter::Antialiasing); + + /*-----------------------------------------------------*\ + | Paint the wheel | + \*-----------------------------------------------------*/ + int size = qMin(newSize.width(), newSize.height()); + x_offset = (newSize.width() - size) / 2; + y_offset = (newSize.height() - size) / 2; + int r = size; + + QPainterPath painterpath; + painterpath.addEllipse(QPoint(0,0),r/2-margin,r/2-margin); + painterpath.addEllipse(QPoint(0,0),r/2-margin-wheelWidth,r/2-margin-wheelWidth); + + painter.translate(x_offset + (size / 2), y_offset + (size / 2)); + + QBrush brush(conicalGradient); + painter.setPen(Qt::NoPen); + painter.setBrush(brush); + + painter.drawPath(painterpath); + + /*-----------------------------------------------------*\ + | Calculate wheel region and subtract out the inner | + | region | + \*-----------------------------------------------------*/ + wheelRegion = QRegion(r/2, r/2, r-2*margin, r-2*margin, QRegion::Ellipse); + wheelRegion.translate(x_offset - (r-2*margin)/2, y_offset - (r-2*margin)/2); + + int tmp = 2*(margin+wheelWidth); + QRegion subRe( r/2, r/2, r-tmp, r-tmp, QRegion::Ellipse ); + subRe.translate( x_offset - (r-tmp)/2, y_offset - (r-tmp)/2); + wheelRegion -= subRe; + + CleanWheel = QPixmap().fromImage(wheelImage); +} + +void ColorWheel::drawSquareImage(const int &hue) +{ +// QPainter painter(&squarePixmap); +// painter.setRenderHint(QPainter::Antialiasing); + + /*-----------------------------------------------------*\ + | Calculate dimensions | + \*-----------------------------------------------------*/ + int w = qMin(width(), height()); + + // radius of outer circle + qreal r = w/2-margin; + + // radius of inner circle + qreal ir = r-wheelWidth; + + // left corner of square + qreal m = w/2.0-ir/qSqrt(2); + + /*-----------------------------------------------------*\ + | Create image canvas | + \*-----------------------------------------------------*/ + QImage square(255,255, QImage::Format_ARGB32_Premultiplied); + + /*-----------------------------------------------------*\ + | Paint the square. X axis is saturation and Y axis is | + | value | + \*-----------------------------------------------------*/ + QColor color; + QRgb qrgb; + + for(int x = 0; x < 255; x++) + { + for(int y = 0; y < 255; y++) + { + color = QColor::fromHsv(hue, x, y); + + qrgb = qRgb(color.red(),color.green(),color.blue()); + + square.setPixel(x, y, qrgb); + } + } + + /*-----------------------------------------------------*\ + | Copy the fixed-size square image on to the scaled | + | canvas | + \*-----------------------------------------------------*/ + qreal SquareWidth = 2*ir/qSqrt(2); + squareImage = square.scaled(SquareWidth, SquareWidth); + + /*-----------------------------------------------------*\ + | Calculate square region | + \*-----------------------------------------------------*/ + squareRegion = QRegion(x_offset + m, y_offset + m, SquareWidth, SquareWidth); + CleanSquare = squareImage; +} + +void ColorWheel::drawIndicator(const int &hue) +{ + QPainter painter(&wheel); + painter.setRenderHint(QPainter::Antialiasing); + if(hue > 20 && hue < 200) + { + painter.setPen(Qt::black); + } + else + { + painter.setPen(Qt::white); + } + painter.setBrush(Qt::NoBrush); + + QPen pen = painter.pen(); + pen.setWidth(3); + painter.setPen(pen); + qreal r = qMin(height(), width()) / 2.0; + painter.translate(x_offset + r, y_offset + r); + painter.rotate( -hue ); + r = qMin(height(), width()) / 2.0 - margin - wheelWidth/2; + painter.drawEllipse(QPointF(r,0.0),5,5); +} + +void ColorWheel::drawPicker(const QColor &color) +{ + QPainter painter(&wheel); + painter.setRenderHint(QPainter::Antialiasing); + QPen pen; + + // region of the widget + int w = qMin(width(), height()); + + // radius of outer circle + qreal r = w/2-margin; + + // radius of inner circle + qreal ir = r-wheelWidth; + + // left corner of square + qreal m = w/2.0-ir/qSqrt(2); + + painter.translate(x_offset + m-5, y_offset + m-5); + + qreal SquareWidth = 2*ir/qSqrt(2); + qreal S = color.saturationF()*SquareWidth; + qreal V = color.valueF()*SquareWidth; + + if(color.saturation() > 30 ||color.value() < 50) + { + pen.setColor(Qt::white); + } + + pen.setWidth(3); + painter.setPen(pen); + painter.drawEllipse(S,V,10,10); +} + +void ColorWheel::composeWheel() +{ + wheel = CleanWheel; + squareImage = CleanSquare; + QPainter composePainter(&wheel); + composePainter.drawImage(0, 0, wheelImage); + composePainter.drawImage(squareRegion.boundingRect().topLeft(), squareImage); + composePainter.end(); + drawIndicator(current.hue()); + drawPicker(current); +} + +void ColorWheel::hueChanged(const int &hue) +{ + if((hue < 0) || (hue > 359)) + { + return; + } + + int s = current.saturation(); + int v = current.value(); + current.setHsv(hue, s, v); + + drawSquareImage(hue); + + if(!isVisible()) + { + return; + } + repaint(); + + emit colorChanged(current); +} + +void ColorWheel::svChanged(const QColor &newcolor) +{ + int hue = current.hue(); + + current.setHsv + ( + hue, + newcolor.saturation(), + newcolor.value() + ); + + if(!isVisible()) + { + return; + } + + repaint(); + + emit colorChanged(current); +} diff --git a/dependencies/ColorWheel/ColorWheel.h b/dependencies/ColorWheel/ColorWheel.h new file mode 100644 index 0000000..5f5a6bf --- /dev/null +++ b/dependencies/ColorWheel/ColorWheel.h @@ -0,0 +1,59 @@ +#ifndef COLORWHEEL_H +#define COLORWHEEL_H + +#include + +class ColorWheel : public QWidget +{ + Q_OBJECT +public: + explicit ColorWheel(QWidget *parent = 0); + + virtual QSize sizeHint () const; + virtual QSize minimumSizeHint () const; + QColor color(); + +signals: + void colorChanged(const QColor color); + +public slots: + void setColor(const QColor &color); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *); + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *); +private: + QSize initSize; + QImage wheelImage; + QImage squareImage; + QPixmap wheel; + bool mouseDown; + QPoint lastPos; + int margin; + int wheelWidth; + QRegion wheelRegion; + QRegion squareRegion; + QColor current; + bool inWheel; + bool inSquare; + int x_offset; + int y_offset; + + QPixmap CleanWheel; + QImage CleanSquare; + + QColor posColor(const QPoint &point); + void drawWheelImage(const QSize &newSize); + void drawIndicator(const int &hue); + void drawPicker(const QColor &color); + void drawSquareImage(const int &hue); + void composeWheel(); +private slots: + void hueChanged(const int &hue); + void svChanged(const QColor &newcolor); +}; + +#endif // COLORWHEEL_H diff --git a/dependencies/NVFC/nvapi.cpp b/dependencies/NVFC/nvapi.cpp new file mode 100644 index 0000000..0c7b157 --- /dev/null +++ b/dependencies/NVFC/nvapi.cpp @@ -0,0 +1,612 @@ +#ifdef _WIN32 + #define _WIN32_LEAN_AND_MEAN + #include +#elif __linux__ + #include +#endif + +#include "nvapi.h" +#include + +typedef void * (*nvapi_QueryInterface_t)(int); + +// Constructors for NvAPI structures that just zero the memory and set the right version +NV_DELTA_ENTRY::NV_DELTA_ENTRY() +{ + memset((void*)this, 0, sizeof *this); +} + +NV_GPU_PSTATES20_V2::NV_GPU_PSTATES20_V2() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_GPU_PSTATES20_V2, 2); +} + +NV_CLOCK_FREQUENCIES_V2::NV_CLOCK_FREQUENCIES_V2() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_CLOCK_FREQUENCIES_V2, 2); +} + +NV_GPU_PERFORMANCE_TABLE_V1::NV_GPU_PERFORMANCE_TABLE_V1() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_GPU_PERFORMANCE_TABLE_V1, 1); +} + +NV_DYNAMIC_PSTATES_V1::NV_DYNAMIC_PSTATES_V1() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_DYNAMIC_PSTATES_V1, 1); +} + +NV_GPU_POWER_POLICIES_INFO_V1::NV_GPU_POWER_POLICIES_INFO_V1() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_GPU_POWER_POLICIES_INFO_V1, 1); +} + +NV_GPU_POWER_POLICIES_STATUS_V1::NV_GPU_POWER_POLICIES_STATUS_V1() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_GPU_POWER_POLICIES_STATUS_V1, 1); +} + +NV_GPU_VOLTAGE_DOMAINS_STATUS_V1::NV_GPU_VOLTAGE_DOMAINS_STATUS_V1() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_GPU_VOLTAGE_DOMAINS_STATUS_V1, 1); +} + +NV_GPU_THERMAL_SETTINGS_V2::NV_GPU_THERMAL_SETTINGS_V2() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_GPU_THERMAL_SETTINGS_V2, 2); +} + +NV_GPU_THERMAL_POLICIES_INFO_V2::NV_GPU_THERMAL_POLICIES_INFO_V2() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_GPU_THERMAL_POLICIES_INFO_V2, 2); +} + +NV_GPU_THERMAL_POLICIES_STATUS_V2::NV_GPU_THERMAL_POLICIES_STATUS_V2() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_GPU_THERMAL_POLICIES_STATUS_V2, 2); +} + +NV_GPU_COOLER_SETTINGS_V2::NV_GPU_COOLER_SETTINGS_V2() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_GPU_COOLER_SETTINGS_V2, 2); +} + +NV_GPU_COOLER_LEVELS_V1::NV_GPU_COOLER_LEVELS_V1() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_GPU_COOLER_LEVELS_V1, 1); +} + +NV_MEMORY_INFO_V2::NV_MEMORY_INFO_V2() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_MEMORY_INFO_V2, 2); +} + +NV_DISPLAY_DRIVER_VERSION_V1::NV_DISPLAY_DRIVER_VERSION_V1() +{ + memset((void*)this, 0, sizeof *this); + version = NV_STRUCT_VERSION(NV_DISPLAY_DRIVER_VERSION_V1, 1); +} + +NV_I2C_INFO_V3::NV_I2C_INFO_V3() +{ + memset((void*)this, 0, sizeof * this); + version = NV_STRUCT_VERSION(NV_I2C_INFO_V3, 3); +} + +// Interface: 0150E828 +static NV_STATUS (*pNvAPI_Initialize)(); + +// Interface: D22BDD7E +static NV_STATUS (*pNvAPI_Unload)(); + +// Interface: 9ABDD40D +static NV_STATUS (*pNvAPI_EnumDisplayHandle)( + NV_S32 this_enum, + NV_DISPLAY_HANDLE *display_handle); + +// Interface: E5AC921F +static NV_STATUS (*pNvAPI_EnumPhysicalGPUs)( + NV_PHYSICAL_GPU_HANDLE *physical_gpu_handles, + NV_S32 *gpu_count); + +// Interface: F951A4D1 +static NV_STATUS (*pNvAPI_GetDisplayDriverVersion)( + NV_DISPLAY_HANDLE display_handle, + NV_DISPLAY_DRIVER_VERSION_V1 *display_driver_version); + +// Interface: 01053FA5 +static NV_STATUS (*pNvAPI_GetInterfaceVersionString)( + NV_SHORT_STRING version); + +// Interface: 34EF9506 +static NV_STATUS (*pNvAPI_GetPhysicalGPUsFromDisplay)( + NV_DISPLAY_HANDLE display_handle, + NV_PHYSICAL_GPU_HANDLE *gpu_handles, + NV_U32 *gpu_count); + +// Interface: 774AA982 +static NV_STATUS (*pNvAPI_GetMemoryInfo)( + NV_DISPLAY_HANDLE display_handle, + NV_MEMORY_INFO_V2 *memory_info); + +// Interface: 0CEEE8E9F +static NV_STATUS (*pNvAPI_GPU_GetFullName)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_SHORT_STRING name); + +// Interface: 6FF81213 +static NV_STATUS (*pNvAPI_GPU_GetPStates20)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_PSTATES20_V2 *pstates); + +// Interface: 0F4DAE6B +static NV_STATUS (*pNvAPI_GPU_SetPStates20)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_PSTATES20_V2 *pstates); + +// Interface: DCB616C3 +static NV_STATUS (*pNvAPI_GPU_GetAllClockFrequencies)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_CLOCK_FREQUENCIES_V2 *frequencies); + +// Interface: 60DED2ED +static NV_STATUS (*pNvAPI_GPU_GetDynamicPStates)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_DYNAMIC_PSTATES_V1 *dynamic_pstates); + +// Interface: 34206D86 +static NV_STATUS (*pNvAPI_GPU_GetPowerPoliciesInfo)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_POWER_POLICIES_INFO_V1 *policies_info); + +// Interface: 70916171 +static NV_STATUS (*pNvAPI_GPU_GetPowerPoliciesStatus)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_POWER_POLICIES_STATUS_V1 *policies_status); + +// Interface: 0C16C7E2C +static NV_STATUS (*pNvAPI_GPU_GetVoltageDomainStatus)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_VOLTAGE_DOMAINS_STATUS_V1 *voltage_domains_status); + +// Interface: 0E3640A56 +static NV_STATUS (*pNvAPI_GPU_GetThermalSettings)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_THERMAL_TARGET sensor_index, + NV_GPU_THERMAL_SETTINGS_V2 *thermal_settings); + +// Interface: 014B83A5F +static NV_STATUS (*pNvAPI_GPU_GetSerialNumber)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_SHORT_STRING serial_number); + +// Interface: 0AD95F5ED +static NV_STATUS (*pNvAPI_GPU_SetPowerPoliciesStatus)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_POWER_POLICIES_STATUS_V1* policies_status); + +// Interface: 00D258BB5 +static NV_STATUS (*pNvAPI_GPU_GetThermalPoliciesInfo)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_THERMAL_POLICIES_INFO_V2* thermal_info); + +// Interface: 0E9C425A1 +static NV_STATUS (*pNvAPI_GPU_GetThermalPoliciesStatus)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_THERMAL_POLICIES_STATUS_V2* thermal_status); + +// Interface: 034C0B13D +static NV_STATUS (*pNvAPI_GPU_SetThermalPoliciesStatus)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_THERMAL_POLICIES_STATUS_V2* thermal_status); + +// Interface: DA141340 +static NV_STATUS (*pNvAPI_GPU_GetCoolerSettings)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_S32 cooler_index, + NV_GPU_COOLER_SETTINGS_V2 *cooler_settings); + +// Interface: 891FA0AE +static NV_STATUS (*pNvAPI_GPU_SetCoolerLevels)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_S32 cooler_index, + NV_GPU_COOLER_LEVELS_V1 *cooler_levels); + +// Interface: 2DDFB66E +static NV_STATUS (*pNvAPI_GPU_GetPCIIdentifiers)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_U32 *device_id, + NV_U32 *sub_system_id, + NV_U32 *revision_id, + NV_U32 *ext_device_id); + +// Interface: 283AC65A +static NV_STATUS (*pNvAPI_I2CWriteEx)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_I2C_INFO_V3* i2c_info, + NV_U32 *unknown); + +// Interface: 4D7B0709 +static NV_STATUS(*pNvAPI_I2CReadEx)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_I2C_INFO_V3* i2c_info, + NV_U32 *unknown); + +// Interface: 3DBF5764 +static NV_STATUS(*pNvAPI_GPU_ClientIllumZonesGetControl)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* pIllumZonesControl); + +// Interface: 197D065E +static NV_STATUS(*pNvAPI_GPU_ClientIllumZonesSetControl)( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* pIllumZonesControl); + +static bool QueryInterfaceOpaque(nvapi_QueryInterface_t query_interface, NV_U32 id, void **result) +{ + void *address = ((void *(*)(NV_U32))query_interface)(id); + if (address) { + *result = address; + return true; + } + return false; +} + +template +static void QueryInterfaceCast(nvapi_QueryInterface_t query_interface, NV_U32 id, const char */*function_name*/, F &function_pointer) +{ + /*const bool result = */QueryInterfaceOpaque(query_interface, id, (void **)&function_pointer); + ////Log::write("%s querying interface '0x%08x' '%s'", result ? "success" : "failure", id, function_name); +} + +#define QueryInterface(query_interface, id, function) \ + QueryInterfaceCast((query_interface), (id), #function, p ## function) + +static void QueryInterfaces(nvapi_QueryInterface_t query_interface) +{ + //Log::write("querying interfaces with '0x%p'", query_interface); + + QueryInterface(query_interface, 0x0150E828, NvAPI_Initialize); + QueryInterface(query_interface, 0xD22BDD7E, NvAPI_Unload); + QueryInterface(query_interface, 0x9ABDD40D, NvAPI_EnumDisplayHandle); + QueryInterface(query_interface, 0xE5AC921F, NvAPI_EnumPhysicalGPUs); + QueryInterface(query_interface, 0xF951A4D1, NvAPI_GetDisplayDriverVersion); + QueryInterface(query_interface, 0x01053FA5, NvAPI_GetInterfaceVersionString); + QueryInterface(query_interface, 0x34EF9506, NvAPI_GetPhysicalGPUsFromDisplay); + QueryInterface(query_interface, 0x774AA982, NvAPI_GetMemoryInfo); + + QueryInterface(query_interface, 0x0CEEE8E9F, NvAPI_GPU_GetFullName); + QueryInterface(query_interface, 0x6FF81213, NvAPI_GPU_GetPStates20); + QueryInterface(query_interface, 0x0F4DAE6B, NvAPI_GPU_SetPStates20); + QueryInterface(query_interface, 0xDCB616C3, NvAPI_GPU_GetAllClockFrequencies); + QueryInterface(query_interface, 0x60DED2ED, NvAPI_GPU_GetDynamicPStates); + QueryInterface(query_interface, 0x34206D86, NvAPI_GPU_GetPowerPoliciesInfo); + QueryInterface(query_interface, 0x70916171, NvAPI_GPU_GetPowerPoliciesStatus); + QueryInterface(query_interface, 0x0C16C7E2C, NvAPI_GPU_GetVoltageDomainStatus); + QueryInterface(query_interface, 0x0E3640A56, NvAPI_GPU_GetThermalSettings); + QueryInterface(query_interface, 0x014B83A5F, NvAPI_GPU_GetSerialNumber); + QueryInterface(query_interface, 0x0AD95F5ED, NvAPI_GPU_SetPowerPoliciesStatus); + QueryInterface(query_interface, 0x00D258BB5, NvAPI_GPU_GetThermalPoliciesInfo); + QueryInterface(query_interface, 0x0E9C425A1, NvAPI_GPU_GetThermalPoliciesStatus); + QueryInterface(query_interface, 0x034C0B13D, NvAPI_GPU_SetThermalPoliciesStatus); + QueryInterface(query_interface, 0xDA141340, NvAPI_GPU_GetCoolerSettings); + QueryInterface(query_interface, 0x891FA0AE, NvAPI_GPU_SetCoolerLevels); + QueryInterface(query_interface, 0x2DDFB66E, NvAPI_GPU_GetPCIIdentifiers); + + QueryInterface(query_interface, 0x283AC65A, NvAPI_I2CWriteEx); + QueryInterface(query_interface, 0x4D7B0709, NvAPI_I2CReadEx); + QueryInterface(query_interface, 0x3DBF5764, NvAPI_GPU_ClientIllumZonesGetControl); + QueryInterface(query_interface, 0x197D065E, NvAPI_GPU_ClientIllumZonesSetControl); +} + +NV_STATUS NvAPI_Initialize() +{ + if (!pNvAPI_Initialize) { +#ifdef _WIN32 + const char *name = sizeof(void*) == 4 ? "nvapi.dll" : "nvapi64.dll"; + HMODULE nvapi = LoadLibraryA(name); + if (!nvapi) { + //Log::write("failed to load '%s'", name); + return -1; + } + //Log::write("loaded '%s' '0x%p'", name, nvapi); + nvapi_QueryInterface_t query_interface = (nvapi_QueryInterface_t) GetProcAddress(nvapi, "nvapi_QueryInterface"); + if (!query_interface) { + //Log::write("failed to find 'nvapi_QueryInterface'"); + return -1; + } +#elif __linux__ + void* nvapi = nullptr; + if (!nvapi) nvapi = dlopen("libnvidia-api.so.1", RTLD_LAZY); + if (!nvapi) nvapi = dlopen("libnvidia-api.so", RTLD_LAZY); + if (!nvapi) { + // NVIDIA Driver is not installed + //Log::write("failed to load libnvidia-api.so, NVIDIA Driver is not installed"); + return -1; + } + nvapi_QueryInterface_t query_interface = (nvapi_QueryInterface_t) dlsym(nvapi, "nvapi_QueryInterface"); + if (!query_interface) { + // NVIDIA Driver is probably not up to date, requires at least driver version 525 + //Log::write("failed to load QueryInterface from libnvidia-api.so, NVIDIA Driver is not up to date"); + return -1; + } +#endif + + QueryInterfaces(query_interface); + } + + return pNvAPI_Initialize + ? (*pNvAPI_Initialize)() + : -1; +} + +NV_STATUS NvAPI_Unload() +{ + return pNvAPI_Unload + ? (*pNvAPI_Unload)() + : -1; +} + +NV_STATUS NvAPI_EnumDisplayHandle( + NV_S32 this_enum, + NV_DISPLAY_HANDLE *display_handle) +{ + return pNvAPI_EnumDisplayHandle + ? (*pNvAPI_EnumDisplayHandle)(this_enum, display_handle) + : -1; +} + +NV_STATUS NvAPI_EnumPhysicalGPUs( + NV_PHYSICAL_GPU_HANDLE *physical_gpu_handles, + NV_S32 *gpu_count) +{ + return pNvAPI_EnumPhysicalGPUs + ? (*pNvAPI_EnumPhysicalGPUs)(physical_gpu_handles, gpu_count) + : -1; +} + +NV_STATUS NvAPI_GetDisplayDriverVersion( + NV_DISPLAY_HANDLE display_handle, + NV_DISPLAY_DRIVER_VERSION_V1 *display_driver_version) +{ + return pNvAPI_GetDisplayDriverVersion + ? (*pNvAPI_GetDisplayDriverVersion)(display_handle, display_driver_version) + : -1; +} + +NV_STATUS NvAPI_GetInterfaceVersionString( + NV_SHORT_STRING version) +{ + return pNvAPI_GetInterfaceVersionString + ? (*pNvAPI_GetInterfaceVersionString)(version) + : -1; +} + +NV_STATUS NvAPI_GetPhysicalGPUsFromDisplay( + NV_DISPLAY_HANDLE display_handle, + NV_PHYSICAL_GPU_HANDLE *gpu_handles, + NV_U32 *gpu_count) +{ + return pNvAPI_GetPhysicalGPUsFromDisplay + ? (*pNvAPI_GetPhysicalGPUsFromDisplay)(display_handle, gpu_handles, gpu_count) + : -1; +} + +NV_STATUS NvAPI_GetMemoryInfo( + NV_DISPLAY_HANDLE display_handle, + NV_MEMORY_INFO_V2 *memory_info) +{ + return pNvAPI_GetMemoryInfo + ? (*pNvAPI_GetMemoryInfo)(display_handle, memory_info) + : -1; +} + +NV_STATUS NvAPI_GPU_GetFullName( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_SHORT_STRING name) +{ + return pNvAPI_GPU_GetFullName + ? (*pNvAPI_GPU_GetFullName)(physical_gpu_handle, name) + : -1; +} + +NV_STATUS NvAPI_GPU_GetPStates20( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_PSTATES20_V2 *pstates) +{ + return pNvAPI_GPU_GetPStates20 + ? (*pNvAPI_GPU_GetPStates20)(physical_gpu_handle, pstates) + : -1; +} + +NV_STATUS NvAPI_GPU_SetPStates20( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_PSTATES20_V2 *pstates) +{ + return pNvAPI_GPU_SetPStates20 + ? (*pNvAPI_GPU_GetPStates20)(physical_gpu_handle, pstates) + : -1; +} + +NV_STATUS NvAPI_GPU_GetAllClockFrequencies( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_CLOCK_FREQUENCIES_V2 *frequencies) +{ + return pNvAPI_GPU_GetAllClockFrequencies + ? (*pNvAPI_GPU_GetAllClockFrequencies)(physical_gpu_handle, frequencies) + : -1; +} + +NV_STATUS NvAPI_GPU_GetDynamicPStates( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_DYNAMIC_PSTATES_V1 *dynamic_pstates) +{ + return pNvAPI_GPU_GetDynamicPStates + ? (*pNvAPI_GPU_GetDynamicPStates)(physical_gpu_handle, dynamic_pstates) + : -1; +} + +NV_STATUS NvAPI_GPU_GetPowerPoliciesInfo( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_POWER_POLICIES_INFO_V1 *policies_info) +{ + return pNvAPI_GPU_GetPowerPoliciesInfo + ? (*pNvAPI_GPU_GetPowerPoliciesInfo)(physical_gpu_handle, policies_info) + : -1; +} + +NV_STATUS NvAPI_GPU_GetPowerPoliciesStatus( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_POWER_POLICIES_STATUS_V1 *policies_status) +{ + return pNvAPI_GPU_GetPowerPoliciesStatus + ? (*NvAPI_GPU_GetPowerPoliciesStatus)(physical_gpu_handle, policies_status) + : -1; +} + +NV_STATUS NvAPI_GPU_GetVoltageDomainStatus( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_VOLTAGE_DOMAINS_STATUS_V1 *voltage_domains_status) +{ + return pNvAPI_GPU_GetVoltageDomainStatus + ? (*pNvAPI_GPU_GetVoltageDomainStatus)(physical_gpu_handle, voltage_domains_status) + : -1; +} + +NV_STATUS NvAPI_GPU_GetThermalSettings( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_THERMAL_TARGET sensor_index, + NV_GPU_THERMAL_SETTINGS_V2 *thermal_settings) +{ + return pNvAPI_GPU_GetThermalSettings + ? (*pNvAPI_GPU_GetThermalSettings)(physical_gpu_handle, sensor_index, thermal_settings) + : -1; +} + +NV_STATUS NvAPI_GPU_GetSerialNumber( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_SHORT_STRING serial_number) +{ + return pNvAPI_GPU_GetSerialNumber + ? (*pNvAPI_GPU_GetSerialNumber)(physical_gpu_handle, serial_number) + : -1; +} + +NV_STATUS NvAPI_GPU_SetPowerPoliciesStatus( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_POWER_POLICIES_STATUS_V1* policies_status) +{ + return pNvAPI_GPU_SetPowerPoliciesStatus + ? (*pNvAPI_GPU_SetPowerPoliciesStatus)(physical_gpu_handle, policies_status) + : -1; +} + +NV_STATUS NvAPI_GPU_GetThermalPoliciesInfo( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_THERMAL_POLICIES_INFO_V2* thermal_info) +{ + return pNvAPI_GPU_GetThermalPoliciesInfo + ? (*pNvAPI_GPU_GetThermalPoliciesInfo)(physical_gpu_handle, thermal_info) + : -1; +} + +NV_STATUS NvAPI_GPU_GetThermalPoliciesStatus( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_THERMAL_POLICIES_STATUS_V2* thermal_status) +{ + return pNvAPI_GPU_GetThermalPoliciesStatus + ? (*pNvAPI_GPU_GetThermalPoliciesStatus)(physical_gpu_handle, thermal_status) + : -1; +} + +NV_STATUS NvAPI_GPU_SetThermalPoliciesStatus( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_THERMAL_POLICIES_STATUS_V2* thermal_status) +{ + return pNvAPI_GPU_SetThermalPoliciesStatus + ? (*pNvAPI_GPU_SetThermalPoliciesStatus)(physical_gpu_handle, thermal_status) + : -1; +} + +NV_STATUS NvAPI_GPU_GetCoolerSettings( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_S32 cooler_index, + NV_GPU_COOLER_SETTINGS_V2 *cooler_settings) +{ + return pNvAPI_GPU_GetCoolerSettings + ? (*pNvAPI_GPU_GetCoolerSettings)(physical_gpu_handle, cooler_index, cooler_settings) + : -1; +} + +NV_STATUS NvAPI_GPU_SetCoolerLevels( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_S32 cooler_index, + NV_GPU_COOLER_LEVELS_V1 *cooler_levels) +{ + return pNvAPI_GPU_SetCoolerLevels + ? (*pNvAPI_GPU_SetCoolerLevels)(physical_gpu_handle, cooler_index, cooler_levels) + : -1; +} + +NV_STATUS NvAPI_GPU_GetPCIIdentifiers( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_U32 *device_id, + NV_U32 *sub_system_id, + NV_U32 *revision_id, + NV_U32 *ext_device_id) +{ + return pNvAPI_GPU_GetPCIIdentifiers + ? (*pNvAPI_GPU_GetPCIIdentifiers)(physical_gpu_handle, device_id, sub_system_id, revision_id, ext_device_id) + : -1; +} + +NV_STATUS NvAPI_I2CWriteEx( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_I2C_INFO_V3* i2c_info, + NV_U32 *unknown) +{ + return pNvAPI_I2CWriteEx + ? (*pNvAPI_I2CWriteEx)(physical_gpu_handle, i2c_info, unknown) + : -1; +} + +NV_STATUS NvAPI_I2CReadEx( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_I2C_INFO_V3* i2c_info, + NV_U32 *unknown) +{ + return pNvAPI_I2CReadEx + ? (*pNvAPI_I2CReadEx)(physical_gpu_handle, i2c_info, unknown) + : -1; +} + +NV_STATUS NvAPI_GPU_ClientIllumZonesGetControl( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* pIllumZonesControl) +{ + return pNvAPI_GPU_ClientIllumZonesGetControl + ? (*pNvAPI_GPU_ClientIllumZonesGetControl)(physical_gpu_handle, pIllumZonesControl) + : -1; +} + +NV_STATUS NvAPI_GPU_ClientIllumZonesSetControl( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* pIllumZonesControl) +{ + return pNvAPI_GPU_ClientIllumZonesSetControl + ? (*pNvAPI_GPU_ClientIllumZonesSetControl)(physical_gpu_handle, pIllumZonesControl) + : -1; +} diff --git a/dependencies/NVFC/nvapi.h b/dependencies/NVFC/nvapi.h new file mode 100644 index 0000000..6b7edf5 --- /dev/null +++ b/dependencies/NVFC/nvapi.h @@ -0,0 +1,1035 @@ +#ifndef NVAPI_H +#define NVAPI_H + +#include + +typedef int32_t NV_S32; +typedef uint32_t NV_U32; +typedef uint8_t NV_U8; +typedef uint16_t NV_U16; + +typedef NV_S32* NV_HANDLE; +typedef NV_HANDLE NV_PHYSICAL_GPU_HANDLE; +typedef NV_HANDLE NV_VIRTUAL_GPU_HANDLE; +typedef NV_HANDLE NV_UNATTACHED_DISPLAY_HANDLE; +typedef NV_HANDLE NV_DISPLAY_HANDLE; + +typedef char NV_SHORT_STRING[64]; + +typedef NV_S32 NV_STATUS; + +#define NV_STRUCT_VERSION(STRUCT, VERSION) \ + (((VERSION) << 16) | sizeof(STRUCT)) + +enum class NV_CLOCK_SYSTEM : NV_S32 { + GPU, + MEMORY, + SHADER +}; + +enum class NV_DYNAMIC_PSTATES_SYSTEM : NV_S32 { + GPU, + FB, + VID, + BUS +}; + +struct NV_DELTA_ENTRY { + NV_DELTA_ENTRY(); + NV_S32 value; + NV_S32 value_min; + NV_S32 value_max; +}; + +struct NV_GPU_PSTATES20_V2 { + NV_GPU_PSTATES20_V2(); + NV_U32 version; + NV_U32 flags; + NV_U32 state_count; + NV_U32 clock_count; + NV_U32 voltage_count; + struct { + NV_U32 state_num; + NV_U32 flags; + struct { + NV_U32 domain; + NV_U32 type; // NOTE(dweiler): 0 = single frequency, 1 = dynamic frequencu + NV_U32 flags; // NOTE(dweiler): flags don't appear to be enforced by NVAPI + NV_DELTA_ENTRY frequency_delta; // NOTE(dweiler): only valid when type == 1 + NV_U32 min_or_single_frequency; // NOTE(dweiler): only valid when type == 0 + NV_U32 max_frequency; // NOTE(dweiler): only valid when type == 1 + NV_U32 voltage_domain; // NOTE(dweiler): only valid when type == 1 + NV_U32 min_voltage; // NOTE(dweiler): only valid when type == 1 + NV_U32 max_voltage; // NOTE(dweiler): only valid when type == 1 + } clocks[8]; + struct { + NV_U32 domain; + NV_U32 flags; // NOTE(dweiler): base voltage can only be changed if bit 0 is set + NV_U32 voltage; + NV_DELTA_ENTRY voltage_delta; + } base_voltages[4]; // NOTE(dweiler): base voltage (resting voltage wheen given a pstate) for all available voltage domains + } states[16]; + struct { + NV_U32 voltage_count; + struct { + NV_U32 domain; + NV_U32 flags; + NV_U32 voltage; + NV_DELTA_ENTRY voltage_delta; + } voltages[4]; + } over_voltage; +}; + +enum class NV_CLOCK_FREQUENCY_TYPE : NV_S32 { + CURRENT, + BASE, + BOOST, + LAST +}; + +struct NV_CLOCK_FREQUENCIES_V2 { + NV_CLOCK_FREQUENCIES_V2(); + NV_U32 version; + NV_U32 clock_type; + struct { + NV_U32 present; + NV_U32 frequency; + } entries[32]; +}; + +struct NV_GPU_PERFORMANCE_TABLE_V1 { + NV_GPU_PERFORMANCE_TABLE_V1(); + NV_U32 version; + NV_U32 plevel_count; + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 domain_entries; + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 pstate_level; + NV_U32 : 32; // NOTE(dweiler): unknown value + struct { + struct { + NV_U32 domain; + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 clock; + NV_U32 default_clock; + NV_U32 min_clock; + NV_U32 max_clock; + NV_U32 : 32; // NOTE(dweiler): unknown value + } domains[32]; + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 setting_flags; + } entries[10]; + NV_U32 unknown[450]; // NOTE(dweiler): the following block of memory is completely unknown +}; + +struct NV_DYNAMIC_PSTATES_V1 { + NV_DYNAMIC_PSTATES_V1(); + NV_U32 version; + NV_U32 flags; + struct { + NV_U32 present; + NV_U32 value; + } pstates[8]; +}; + +struct NV_GPU_POWER_POLICIES_INFO_V1 { + NV_GPU_POWER_POLICIES_INFO_V1(); + NV_U32 version; + NV_U32 flags; + struct { + NV_U32 pstate; + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 min_power; + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 default_power; + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 max_power; + NV_U32 : 32; // NOTE(dweiler): unknown value + } entries[4]; +}; + +struct NV_GPU_POWER_POLICIES_STATUS_V1 { + NV_GPU_POWER_POLICIES_STATUS_V1(); + NV_U32 version; + NV_U32 count; + struct { + NV_U32 pstate; // NOTE(dweiler): assumed? + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_U32 power; + NV_U32 : 32; // NOTE(dweiler): unknown value + } entries[4]; +}; + +struct NV_GPU_VOLTAGE_DOMAINS_STATUS_V1 { + NV_GPU_VOLTAGE_DOMAINS_STATUS_V1(); + NV_U32 version; + NV_U32 flags; + NV_U32 count; + struct { + NV_U32 voltage_domain; + NV_U32 current_voltage; + } entries[16]; +}; + +enum class NV_THERMAL_CONTROLLER : NV_S32 { + NONE, + GPU_INTERNAL, + ADM103, + MAX6649, + MAX1617, + LM99, + LM89, + LM64, + ADT7473, + SBMAX6649, + VBIOSEVT, + OS, + UNKNOWN = -1 +}; + +enum class NV_THERMAL_TARGET : NV_S32 { + NONE = 0, + GPU = 1, + MEMORY = 2, + POWER_SUPPLY = 4, + BOARD = 8, + ALL = 15, + UNKNOWN = -1 +}; + +enum class NV_I2C_SPEED : NV_U32 { + NVAPI_I2C_SPEED_DEFAULT, + NVAPI_I2C_SPEED_3KHZ, + NVAPI_I2C_SPEED_10KHZ, + NVAPI_I2C_SPEED_33KHZ, + NVAPI_I2C_SPEED_100KHZ, + NVAPI_I2C_SPEED_200KHZ, + NVAPI_I2C_SPEED_400KHZ +}; + +struct NV_GPU_THERMAL_SETTINGS_V2 { + NV_GPU_THERMAL_SETTINGS_V2(); + NV_U32 version; + NV_U32 count; + struct { + NV_THERMAL_CONTROLLER controller; + NV_S32 default_min; + NV_S32 default_max; + NV_S32 current_temperature; + NV_THERMAL_TARGET target; + } sensor[3]; +}; + +struct NV_GPU_THERMAL_POLICIES_INFO_V2 { + NV_GPU_THERMAL_POLICIES_INFO_V2(); + NV_U32 version; + NV_U32 flags; + struct { + NV_U32 controller; // NOTE(dweiler): can't be NV_THERMAL_CONTROLLER since this needs to be unsigned + NV_U32 : 32; // NOTE(dweiler): unknown value + NV_S32 min; // NOTE(dweiler): stored as multiples of 256 + NV_S32 default_; // NOTE(dweiler): stored as multiples of 256 + NV_S32 max; // NOTE(dweiler): stored as multiples of 256 + NV_U32 default_flags; // NOTE(dweiler): bit zero of the flags indicates the thermal priority + } entries[4]; +}; + +struct NV_GPU_THERMAL_POLICIES_STATUS_V2 { + NV_GPU_THERMAL_POLICIES_STATUS_V2(); + NV_U32 version; + NV_U32 count; + struct { + NV_U32 controller; + NV_U32 value; // NOTE(dweiler): stored as multiples of 256 + NV_U32 flags; // NOTE(dweiler): bit zero of the flags indicates the thermal priority + } entries[4]; +}; + +struct NV_GPU_COOLER_SETTINGS_V2 { + NV_GPU_COOLER_SETTINGS_V2(); + NV_U32 version; + NV_U32 count; + struct { + NV_S32 type; + NV_S32 controller; + NV_S32 default_min; + NV_S32 default_max; + NV_S32 current_min; + NV_S32 current_max; + NV_S32 current_level; + NV_S32 default_policy; + NV_S32 current_policy; + NV_S32 target; + NV_S32 control_type; + NV_S32 active; + } coolers[20]; +}; + +struct NV_GPU_COOLER_LEVELS_V1 { + NV_GPU_COOLER_LEVELS_V1(); + NV_U32 version; + struct { + NV_S32 level; + NV_S32 policy; // NOTE(dweiler): 0x20 is default policy, 0x01 is user supplied policy + // TODO(dweiler): figure out what other policy values are valid + } levels[20]; +}; + +struct NV_MEMORY_INFO_V2 { + NV_MEMORY_INFO_V2(); + NV_U32 version; + NV_U32 values[5]; +}; + +struct NV_DISPLAY_DRIVER_VERSION_V1 { + NV_DISPLAY_DRIVER_VERSION_V1(); + NV_U32 version; + NV_U32 driver_version; // NOTE(dweiler): major = (driver_version / 100), minor = (driver_version % 100) + NV_U32 : 32; // NOTE(dweiler): unknown vaue + NV_SHORT_STRING build_branch; + NV_SHORT_STRING adapter; +}; + +struct NV_I2C_INFO_V3 { + NV_I2C_INFO_V3(); + NV_U32 version; + NV_U32 display_mask; + NV_U8 is_ddc_port; + NV_U8 i2c_dev_address; + NV_U8* i2c_reg_address; + NV_U32 reg_addr_size; + NV_U8* data; + NV_U32 size; + NV_U32 i2c_speed; + NV_I2C_SPEED i2c_speed_khz; + NV_U8 port_id; + NV_U32 is_port_id_set; +}; + +// NvAPI RGB related stuff (CMiller) + +typedef enum +{ + NV_GPU_CLIENT_ILLUM_ZONE_TYPE_INVALID = 0, + NV_GPU_CLIENT_ILLUM_ZONE_TYPE_RGB, + NV_GPU_CLIENT_ILLUM_ZONE_TYPE_COLOR_FIXED, + NV_GPU_CLIENT_ILLUM_ZONE_TYPE_RGBW, + NV_GPU_CLIENT_ILLUM_ZONE_TYPE_SINGLE_COLOR, +} NV_GPU_CLIENT_ILLUM_ZONE_TYPE; + + +typedef enum +{ + NV_GPU_CLIENT_ILLUM_ZONE_LOCATION_GPU_TOP_0 = 0x00, + NV_GPU_CLIENT_ILLUM_ZONE_LOCATION_GPU_FRONT_0 = 0x08, + NV_GPU_CLIENT_ILLUM_ZONE_LOCATION_GPU_BACK_0 = 0x0C, + NV_GPU_CLIENT_ILLUM_ZONE_LOCATION_SLI_TOP_0 = 0x20, + NV_GPU_CLIENT_ILLUM_ZONE_LOCATION_INVALID = 0xFFFFFFFF, +} NV_GPU_CLIENT_ILLUM_ZONE_LOCATION; + + +typedef enum +{ + NV_GPU_CLIENT_ILLUM_PIECEWISE_LINEAR_CYCLE_HALF_HALT = 0, + NV_GPU_CLIENT_ILLUM_PIECEWISE_LINEAR_CYCLE_FULL_HALT, + NV_GPU_CLIENT_ILLUM_PIECEWISE_LINEAR_CYCLE_FULL_REPEAT, + NV_GPU_CLIENT_ILLUM_PIECEWISE_LINEAR_CYCLE_INVALID = 0xFF, +} NV_GPU_CLIENT_ILLUM_PIECEWISE_LINEAR_CYCLE_TYPE; + + +typedef enum +{ + NV_GPU_CLIENT_ILLUM_CTRL_MODE_MANUAL_RGB = 0, // deprecated + NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_RGB, // deprecated + + NV_GPU_CLIENT_ILLUM_CTRL_MODE_MANUAL = 0, + NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR, + + // Strictly add new control modes above this. + NV_GPU_CLIENT_ILLUM_CTRL_MODE_INVALID = 0xFF, +} NV_GPU_CLIENT_ILLUM_CTRL_MODE; + + +#define NV_GPU_CLIENT_ILLUM_ZONE_NUM_ZONES_MAX 32 + +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_INFO_DATA_RGB +{ + NV_U8 rsvd; +} NV_GPU_CLIENT_ILLUM_ZONE_INFO_DATA_RGB; + +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_INFO_DATA_RGBW +{ + NV_U8 rsvd; +} NV_GPU_CLIENT_ILLUM_ZONE_INFO_DATA_RGBW; + +/*! + * Used in \ref NV_GPU_CLIENT_ILLUM_ZONE_INFO_V1 + * Describes the static information of illum zone type SINGLE_COLOR. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_INFO_DATA_SINGLE_COLOR +{ + NV_U8 rsvd; +} NV_GPU_CLIENT_ILLUM_ZONE_INFO_DATA_SINGLE_COLOR; + +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_INFO_V1 +{ + NV_GPU_CLIENT_ILLUM_ZONE_TYPE type; + + /*! + * Index pointing to an Illumination Device that controls this zone. + */ + NV_U8 illumDeviceIdx; + + /*! + * Provider index for representing logical to physical zone mapping. + */ + NV_U8 provIdx; + + /*! + * Location of the zone on the board. + */ + NV_GPU_CLIENT_ILLUM_ZONE_LOCATION zoneLocation; + + union + { + // + // Need to be careful when add/expanding types in this union. If any type + // exceeds sizeof(rsvd) then rsvd has failed its purpose. + // + NV_GPU_CLIENT_ILLUM_ZONE_INFO_DATA_RGB rgb; + NV_GPU_CLIENT_ILLUM_ZONE_INFO_DATA_RGBW rgbw; + NV_GPU_CLIENT_ILLUM_ZONE_INFO_DATA_SINGLE_COLOR singleColor; + /*! + * Reserved bytes for possible future extension of this struct. + */ + NV_U8 rsvd[64]; + } data; + + NV_U8 rsvd[64]; +} NV_GPU_CLIENT_ILLUM_ZONE_INFO_V1; + +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_INFO_PARAMS_V1 +{ + /*! + * Version of structure. Must always be first member. + */ + NV_U32 version; + + /*! + * Number of illumination zones present. + */ + NV_U32 numIllumZones; + + /*! + * Reserved bytes for possible future extension of this struct. + */ + NV_U8 rsvd[64]; + NV_GPU_CLIENT_ILLUM_ZONE_INFO_V1 zones[NV_GPU_CLIENT_ILLUM_ZONE_NUM_ZONES_MAX]; +} NV_GPU_CLIENT_ILLUM_ZONE_INFO_PARAMS_V1; + +#define NV_GPU_CLIENT_ILLUM_ZONE_INFO_PARAMS_VER_1 MAKE_NVAPI_VERSION(NV_GPU_CLIENT_ILLUM_ZONE_INFO_PARAMS_V1, 1) +#define NV_GPU_CLIENT_ILLUM_ZONE_INFO_PARAMS_VER NV_GPU_CLIENT_ILLUM_ZONE_INFO_PARAMS_VER_1 +typedef NV_GPU_CLIENT_ILLUM_ZONE_INFO_PARAMS_V1 NV_GPU_CLIENT_ILLUM_ZONE_INFO_PARAMS; + + + + +/*! + * Used in \ref NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGB + * Parameters required to represent control mode of type + * \ref NV_GPU_CLIENT_ILLUM_CTRL_MODE_MANUAL_RGB. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGB_PARAMS +{ + /*! + * Red compenent of color applied to the zone. + */ + NV_U8 colorR; + + /*! + * Green compenent of color applied to the zone. + */ + NV_U8 colorG; + + /*! + * Blue compenent of color applied to the zone. + */ + NV_U8 colorB; + + /*! + * Brightness perecentage value of the zone. + */ + NV_U8 brightnessPct; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGB_PARAMS; + +/*! + * Used in \ref NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_RGB + * Data required to represent control mode of type + * \ref NV_GPU_CLIENT_ILLUM_CTRL_MODE_MANUAL_RGB. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGB +{ + /*! + * Parameters required to represent control mode of type + * \ref NV_GPU_CLIENT_ILLUM_CTRL_MODE_MANUAL_RGB. + */ + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGB_PARAMS rgbParams; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGB; + +/*! + * Used in \ref NV_GPU_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_RGB + * Data required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_RGB. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR +{ + /*! + * Type of cycle effect to apply. + */ + NV_GPU_CLIENT_ILLUM_PIECEWISE_LINEAR_CYCLE_TYPE cycleType; + + /*! + * Number of times to repeat function within group period. + */ + NV_U8 grpCount; + + /*! + * Time in ms to transition from color A to color B. + */ + NV_U16 riseTimems; + + /*! + * Time in ms to transition from color B to color A. + */ + NV_U16 fallTimems; + + /*! + * Time in ms to remain at color A before color A to color B transition. + */ + NV_U16 ATimems; + + /*! + * Time in ms to remain at color B before color B to color A transition. + */ + NV_U16 BTimems; + + /*! + * Time in ms to remain idle before next group of repeated function cycles. + */ + NV_U16 grpIdleTimems; + + /*! + * Time in ms to offset the cycle relative to other zones. + */ + NV_U16 phaseOffsetms; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR; + +/*! + * Used in \ref NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_RGB + * Data required to represent control mode of type + * \ref NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_RGB. + */ + +#define NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_COLOR_ENDPOINTS 2 + +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_RGB +{ + /*! + * Parameters required to represent control mode of type + * \ref NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_RGB. + */ + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGB_PARAMS rgbParams[NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_COLOR_ENDPOINTS]; + + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR piecewiseLinearData; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_RGB; + +/*! + * Used in \ref NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_V1 + * Describes the control data for illumination zone of type + * \ref NV_GPU_CLIENT_ILLUM_ZONE_TYPE_RGB. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_RGB +{ + /*! + * Union of illumination zone control data for zone of type NV_GPU_CLIENT_ILLUM_ZONE_TYPE_RGB. + * Interpreted as per ctrlMode. + */ + union + { + // + // Need to be careful when add/expanding types in this union. If any type + // exceeds sizeof(rsvd) then rsvd has failed its purpose. + // + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGB manualRGB; + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_RGB piecewiseLinearRGB; + + /*! + * Reserved bytes for possible future extension of this struct. + */ + NV_U8 rsvd[64]; + } data; + + /*! + * Reserved for future. + */ + NV_U8 rsvd[64]; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_RGB; + +/*! + * Used in \ref NV_GPU_ILLUM_ZONE_CONTROL_DATA_MANUAL_COLOR_FIXED + * Parameters required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_MANUAL_RGB. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_COLOR_FIXED_PARAMS +{ + /*! + * Brightness percentage value of the zone. + */ + NV_U8 brightnessPct; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_COLOR_FIXED_PARAMS; + +/*! + * Used in \ref NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_COLOR_FIXED + * Data required to represent control mode of type + * \ref NV_GPU_CLIENT_ILLUM_CTRL_MODE_MANUAL_RGB. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_COLOR_FIXED +{ + /*! + * Parameters required to represent control mode of type + * \ref NV_GPU_CLIENT_ILLUM_CTRL_MODE_MANUAL_RGB. + */ + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_COLOR_FIXED_PARAMS colorFixedParams; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_COLOR_FIXED; + +/*! + * Used in \ref NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_COLOR_FIXED + * Data required to represent control mode of type + * \ref NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_RGB. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_COLOR_FIXED +{ + /*! + * Parameters required to represent control mode of type + * \ref NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_RGB. + */ + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_COLOR_FIXED_PARAMS colorFixedParams[NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_COLOR_ENDPOINTS]; + + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR piecewiseLinearData; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_COLOR_FIXED; + +/*! + * Used in \ref NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_V1 + * Describes the control data for illum zone of type + * \ref NV_GPU_CLIENT_ILLUM_ZONE_TYPE_COLOR_FIXED. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_COLOR_FIXED +{ + /*! + * Union of illum zone control data for zone of type NV_GPU_CLIENT_ILLUM_ZONE_TYPE_COLOR_FIXED. + * Interpreted as per ctrlMode. + */ + union + { + // + // Need to be careful when add/expanding types in this union. If any type + // exceeds sizeof(rsvd) then rsvd has failed its purpose. + // + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_COLOR_FIXED manualColorFixed; + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_COLOR_FIXED piecewiseLinearColorFixed; + /*! + * Reserved bytes for possible future extension of this struct. + */ + NV_U8 rsvd[64]; + } data; + + /*! + * Reserved for future. + */ + NV_U8 rsvd[64]; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_COLOR_FIXED; + +/*! + * Used in \ref NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGBW + * Parameters required to represent control mode of type + * \ref NV_GPU_CLIENT_ILLUM_CTRL_MODE_MANUAL_RGBW. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGBW_PARAMS +{ + /*! + * Red component of color applied to the zone. + */ + NV_U8 colorR; + + /*! + * Green component of color applied to the zone. + */ + NV_U8 colorG; + + /*! + * Blue component of color applied to the zone. + */ + NV_U8 colorB; + + /*! + * White component of color applied to the zone. + */ + NV_U8 colorW; + + /*! + * Brightness percentage value of the zone. + */ + NV_U8 brightnessPct; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGBW_PARAMS; + +/*! + * Used in \ref NV_GPU_ILLUM_ZONE_CONTROL_DATA_RGBW + * Data required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_MANUAL_RGBW. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGBW +{ + /*! + * Parameters required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_MANUAL_RGBW. + */ + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGBW_PARAMS rgbwParams; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGBW; + + +/*! + * Used in \ref NV_GPU_ILLUM_ZONE_CONTROL_DATA_RGBW + * Data required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_RGBW. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_RGBW +{ + /*! + * Parameters required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_RGBW. + */ + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGBW_PARAMS rgbwParams[NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_COLOR_ENDPOINTS]; + + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR piecewiseLinearData; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_RGBW; + +/*! + * Used in \ref NV_GPU_ILLUM_ZONE_CONTROL_V1 + * Describes the control data for illum zone of type + * \ref NV_GPU_ILLUM_ZONE_TYPE_RGBW. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_RGBW +{ + /*! + * Union of illum zone control data for zone of type NV_GPU_ILLUM_ZONE_TYPE_RGBW. + * Interpreted as per ctrlMode. + */ + union + { + // + // Need to be careful when add/expanding types in this union. If any type + // exceeds sizeof(rsvd) then rsvd has failed its purpose. + // + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_RGBW manualRGBW; + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_RGBW piecewiseLinearRGBW; + /*! + * Reserved bytes for possible future extension of this struct. + */ + NV_U8 rsvd[64]; + } data; + + /*! + * Reserved for future. + */ + NV_U8 rsvd[64]; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_RGBW; + +/*! + * Used in \ref NV_GPU_ILLUM_ZONE_CONTROL_DATA_MANUAL_SINGLE_COLOR + * Parameters required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_MANUAL_SINGLE_COLOR. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_SINGLE_COLOR_PARAMS +{ + /*! + * Brightness percentage value of the zone. + */ + NV_U8 brightnessPct; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_SINGLE_COLOR_PARAMS; + +/*! + * Used in \ref NV_GPU_ILLUM_ZONE_CONTROL_DATA_SINGLE_COLOR + * Data required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_MANUAL_SINGLE_COLOR. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_SINGLE_COLOR +{ + /*! + * Parameters required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_MANUAL_SINGLE_COLOR. + */ + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_SINGLE_COLOR_PARAMS singleColorParams; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_SINGLE_COLOR; + +/*! + * Used in \ref NV_GPU_ILLUM_ZONE_CONTROL_DATA_SINGLE_COLOR + * Data required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_SINGLE_COLOR. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_SINGLE_COLOR +{ + /*! + * Parameters required to represent control mode of type + * \ref NV_GPU_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_SINGLE_COLOR. + */ + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_SINGLE_COLOR_PARAMS singleColorParams[NV_GPU_CLIENT_ILLUM_CTRL_MODE_PIECEWISE_LINEAR_COLOR_ENDPOINTS]; + + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR piecewiseLinearData; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_SINGLE_COLOR; + +/*! + * Used in \ref NV_GPU_ILLUM_ZONE_CONTROL_V1 + * Describes the control data for illum zone of type + * \ref NV_GPU_ILLUM_ZONE_TYPE_SINGLE_COLOR. + */ +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_SINGLE_COLOR +{ + /*! + * Union of illum zone control data for zone of type NV_GPU_ILLUM_ZONE_TYPE_SINGLE_COLOR. + * Interpreted as per ctrlMode. + */ + union + { + // + // Need to be careful when add/expanding types in this union. If any type + // exceeds sizeof(rsvd) then rsvd has failed its purpose. + // + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_MANUAL_SINGLE_COLOR manualSingleColor; + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_PIECEWISE_LINEAR_SINGLE_COLOR piecewiseLinearSingleColor; + /*! + * Reserved bytes for possible future extension of this struct. + */ + NV_U8 rsvd[64]; + } data; + + /*! + * Reserved for future. + */ + NV_U8 rsvd[64]; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_SINGLE_COLOR; + +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_V1 +{ + NV_GPU_CLIENT_ILLUM_ZONE_TYPE type; + NV_GPU_CLIENT_ILLUM_CTRL_MODE ctrlMode; + union + { + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_RGB rgb; + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_COLOR_FIXED colorFixed; + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_RGBW rgbw; + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_DATA_SINGLE_COLOR singleColor; + NV_U8 rsvd[64]; + } data; + NV_U8 rsvd[64]; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_V1; + +typedef struct _NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS_V1 +{ + NV_U32 version; + + /*! + * Bit field specifying the set of values to retrieve or set + * - default (NV_TRUE) + * - currently active (NV_FALSE). + */ + NV_U32 bDefault : 1; + NV_U32 rsvdField : 31; + + /*! + * Number of illumination zones present. + */ + NV_U32 numIllumZonesControl; + + /*! + * Reserved bytes for possible future extension of this struct. + */ + NV_U8 rsvd[64]; + + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_V1 zones[NV_GPU_CLIENT_ILLUM_ZONE_NUM_ZONES_MAX]; +} NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS_V1; + +#define NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS_VER_1 MAKE_NVAPI_VERSION(NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS_V1, 1) +#define NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS_VER NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS_VER_1 +typedef NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS_V1 NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS; + + + +// Interface: 0150E828 +NV_STATUS NvAPI_Initialize(); + +// Interface: D22BDD7E +NV_STATUS NvAPI_Unload(); + +// Interface: 9ABDD40D +NV_STATUS NvAPI_EnumDisplayHandle( + NV_S32 this_enum, + NV_DISPLAY_HANDLE *display_handle); + +// Interface: E5AC921F +NV_STATUS NvAPI_EnumPhysicalGPUs( + NV_PHYSICAL_GPU_HANDLE *physical_gpu_handles, + NV_S32 *gpu_count); + +// Interface: F951A4D1 +NV_STATUS NvAPI_GetDisplayDriverVersion( + NV_DISPLAY_HANDLE display_handle, + NV_DISPLAY_DRIVER_VERSION_V1 *display_driver_version); + +// Interface: 01053FA5 +NV_STATUS NvAPI_GetInterfaceVersionString( + NV_SHORT_STRING version); + +// Interface: 34EF9506 +NV_STATUS NvAPI_GetPhysicalGPUsFromDisplay( + NV_DISPLAY_HANDLE display_handle, + NV_PHYSICAL_GPU_HANDLE *gpu_handles, + NV_U32 *gpu_count); + +// Interface: 774AA982 +NV_STATUS NvAPI_GetMemoryInfo( + NV_DISPLAY_HANDLE display_handle, + NV_MEMORY_INFO_V2 *memory_info); + +// Interface: 0CEEE8E9F +NV_STATUS NvAPI_GPU_GetFullName( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_SHORT_STRING name); + +// Interface: 6FF81213 +NV_STATUS NvAPI_GPU_GetPStates20( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_PSTATES20_V2 *pstates); + +// Interface: 0F4DAE6B +NV_STATUS NvAPI_GPU_SetPStates20( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_PSTATES20_V2 *pstates); + +// Get frequencies of all clocks of the GPU +// +// The actual frequencies (current, base, boost) returned is based on the value +// set in frequencies->clock_type before calling this function. The value values +// are part of the NV_CLOCK_FREQUENCY_TYPE enumeration. +// +// Interface: DCB616C3 +NV_STATUS NvAPI_GPU_GetAllClockFrequencies( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_CLOCK_FREQUENCIES_V2 *frequencies); + +// Interface: 60DED2ED +NV_STATUS NvAPI_GPU_GetDynamicPStates( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_DYNAMIC_PSTATES_V1 *dynamic_pstates); + +// Interface: 34206D86 +NV_STATUS NvAPI_GPU_GetPowerPoliciesInfo( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_POWER_POLICIES_INFO_V1 *policies_info); + +// Interface: 70916171 +NV_STATUS NvAPI_GPU_GetPowerPoliciesStatus( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_POWER_POLICIES_STATUS_V1 *policies_status); + +// Interface: 0C16C7E2C +NV_STATUS NvAPI_GPU_GetVoltageDomainStatus( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_VOLTAGE_DOMAINS_STATUS_V1 *voltage_domains_status); + +// Get the thermal settings of the GPU +// +// The value of [sensor_index] must be one of the values of NV_THERMAL_TARGET, +// either a single sensor, or the special value NV_THERMAL_TARGET::ALL for all +// sensors present on the GPU +// +// Interface: 0E3640A56 +NV_STATUS NvAPI_GPU_GetThermalSettings( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_THERMAL_TARGET sensor_index, + NV_GPU_THERMAL_SETTINGS_V2 *thermal_settings); + +// Get the serial number of the GPU +// +// The NV_SHORT_STRING will be filled out accordingly +// +// Interface: 014B83A5F +NV_STATUS NvAPI_GPU_GetSerialNumber( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_SHORT_STRING serial_number); + +// Interface: 0AD95F5ED +NV_STATUS NvAPI_GPU_SetPowerPoliciesStatus( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_POWER_POLICIES_STATUS_V1* policies_status); + +// Interface: 00D258BB5 +NV_STATUS NvAPI_GPU_GetThermalPoliciesInfo( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_THERMAL_POLICIES_INFO_V2* thermal_info); + +// Interface: 0E9C425A1 +NV_STATUS NvAPI_GPU_GetThermalPoliciesStatus( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_THERMAL_POLICIES_STATUS_V2* thermal_status); + +// Interface: 034C0B13D +NV_STATUS NvAPI_GPU_SetThermalPoliciesStatus( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_THERMAL_POLICIES_STATUS_V2* thermal_status); + +// Interface: DA141340 +NV_STATUS NvAPI_GPU_GetCoolerSettings( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_S32 cooler_index, + NV_GPU_COOLER_SETTINGS_V2 *cooler_settings); + +// Interface: 891FA0AE +NV_STATUS NvAPI_GPU_SetCoolerLevels( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_S32 cooler_index, + NV_GPU_COOLER_LEVELS_V1 *cooler_levels); + +// Interface: 2DDFB66E +NV_STATUS NvAPI_GPU_GetPCIIdentifiers( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_U32 *device_id, + NV_U32 *sub_system_id, + NV_U32 *revision_id, + NV_U32 *ext_device_id); + +// Interface: 283AC65A +NV_STATUS NvAPI_I2CWriteEx( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_I2C_INFO_V3 *i2c_info, + NV_U32 *unknown); + +// Interface: 4D7B0709 +NV_STATUS NvAPI_I2CReadEx( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_I2C_INFO_V3* i2c_info, + NV_U32 *unknown); + +// Interface: 73C01D58 +NV_STATUS NvAPI_GPU_ClientIllumZonesGetControl( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* pIllumZonesControl); + +// Interface: 57024C62 +NV_STATUS NvAPI_GPU_ClientIllumZonesSetControl( + NV_PHYSICAL_GPU_HANDLE physical_gpu_handle, + NV_GPU_CLIENT_ILLUM_ZONE_CONTROL_PARAMS* pIllumZonesControl); + +#endif diff --git a/dependencies/PawnIO/PawnIOLib.dll b/dependencies/PawnIO/PawnIOLib.dll new file mode 100644 index 0000000..1e79e77 Binary files /dev/null and b/dependencies/PawnIO/PawnIOLib.dll differ diff --git a/dependencies/PawnIO/PawnIOLib.h b/dependencies/PawnIO/PawnIOLib.h new file mode 100644 index 0000000..465b7c8 --- /dev/null +++ b/dependencies/PawnIO/PawnIOLib.h @@ -0,0 +1,75 @@ +// PawnIOLib - Library and tooling source to be used with PawnIO. +// Copyright (C) 2023 namazso +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#ifndef PAWNIOLIB_LIBRARY_H +#define PAWNIOLIB_LIBRARY_H + +#ifdef PawnIOLib_EXPORTS +#define PAWNIO_EXPORT __declspec(dllexport) +#else +#define PAWNIO_EXPORT __declspec(dllimport) +#endif + +#define PAWNIOAPI EXTERN_C PAWNIO_EXPORT HRESULT STDAPICALLTYPE + +/// Get PawnIOLib version. +/// +/// @p version A pointer to a ULONG which receives the version. +/// @return A HRESULT. +PAWNIOAPI pawnio_version(PULONG version); + +/// Open a PawnIO executor. +/// +/// @p handle A handle to the executor, or NULL. +/// @return A HRESULT. +PAWNIOAPI pawnio_open(PHANDLE handle); + +/// Load a PawnIO blob. +/// +/// @p handle Handle from @c pawnio_open. +/// @p blob Blob to load. +/// @p size Size of blob. +/// @return A HRESULT. +PAWNIOAPI pawnio_load(HANDLE handle, const UCHAR* blob, SIZE_T size); + +/// Executes a function from the loaded blob. +/// +/// @p handle Handle from @c pawnio_open. +/// @p name Function name to execute. +/// @p in Input buffer. +/// @p in_size Input buffer count. +/// @p out Output buffer. +/// @p out_size Output buffer count. +/// @p return_size Entries written in out_size. +/// @return A HRESULT. +PAWNIOAPI pawnio_execute( + HANDLE handle, + PCSTR name, + const ULONG64* in, + SIZE_T in_size, + PULONG64 out, + SIZE_T out_size, + PSIZE_T return_size +); + +/// Close a PawnIO executor. +/// +/// @p handle Handle from @c pawnio_open. +/// @return A HRESULT. +PAWNIOAPI pawnio_close(HANDLE handle); + +#endif //PAWNIOLIB_LIBRARY_H diff --git a/dependencies/PawnIO/PawnIOLib.lib b/dependencies/PawnIO/PawnIOLib.lib new file mode 100644 index 0000000..daa76a6 Binary files /dev/null and b/dependencies/PawnIO/PawnIOLib.lib differ diff --git a/dependencies/PawnIO/modules/LpcIO.bin b/dependencies/PawnIO/modules/LpcIO.bin new file mode 100644 index 0000000..41ce8cc Binary files /dev/null and b/dependencies/PawnIO/modules/LpcIO.bin differ diff --git a/dependencies/PawnIO/modules/SmbusI801.bin b/dependencies/PawnIO/modules/SmbusI801.bin new file mode 100644 index 0000000..ed59049 Binary files /dev/null and b/dependencies/PawnIO/modules/SmbusI801.bin differ diff --git a/dependencies/PawnIO/modules/SmbusIntelSkylakeIMC.bin b/dependencies/PawnIO/modules/SmbusIntelSkylakeIMC.bin new file mode 100644 index 0000000..b992fe4 Binary files /dev/null and b/dependencies/PawnIO/modules/SmbusIntelSkylakeIMC.bin differ diff --git a/dependencies/PawnIO/modules/SmbusNCT6793.bin b/dependencies/PawnIO/modules/SmbusNCT6793.bin new file mode 100644 index 0000000..2ea0033 Binary files /dev/null and b/dependencies/PawnIO/modules/SmbusNCT6793.bin differ diff --git a/dependencies/PawnIO/modules/SmbusPIIX4.bin b/dependencies/PawnIO/modules/SmbusPIIX4.bin new file mode 100644 index 0000000..6b400db Binary files /dev/null and b/dependencies/PawnIO/modules/SmbusPIIX4.bin differ diff --git a/dependencies/display-library/include/adl_defines.h b/dependencies/display-library/include/adl_defines.h new file mode 100644 index 0000000..b3aa731 --- /dev/null +++ b/dependencies/display-library/include/adl_defines.h @@ -0,0 +1,2596 @@ +// +// Copyright (c) 2016 - 2022 Advanced Micro Devices, Inc. All rights reserved. +// +// MIT LICENSE: +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// \file adl_defines.h +/// \brief Contains all definitions exposed by ADL for \ALL platforms.\n Included in ADL SDK +/// +/// This file contains all definitions used by ADL. +/// The ADL definitions include the following: +/// \li ADL error codes +/// \li Enumerations for the ADLDisplayInfo structure +/// \li Maximum limits +/// + +#ifndef ADL_DEFINES_H_ +#define ADL_DEFINES_H_ + +/// \defgroup DEFINES Constants and Definitions +/// @{ + +/// \defgroup define_misc Miscellaneous Constant Definitions +/// @{ + +/// \name General Definitions +/// @{ + +/// Defines ADL_TRUE +#define ADL_TRUE 1 +/// Defines ADL_FALSE +#define ADL_FALSE 0 + +/// Defines the maximum string length +#define ADL_MAX_CHAR 4096 +/// Defines the maximum string length +#define ADL_MAX_PATH 256 +/// Defines the maximum number of supported adapters +#define ADL_MAX_ADAPTERS 250 +/// Defines the maxumum number of supported displays +#define ADL_MAX_DISPLAYS 150 +/// Defines the maxumum string length for device name +#define ADL_MAX_DEVICENAME 32 +/// Defines for all adapters +#define ADL_ADAPTER_INDEX_ALL -1 +/// Defines APIs with iOption none +#define ADL_MAIN_API_OPTION_NONE 0 +/// @} + +/// \name Definitions for iOption parameter used by +/// ADL_Display_DDCBlockAccess_Get() +/// @{ + +/// Switch to DDC line 2 before sending the command to the display. +#define ADL_DDC_OPTION_SWITCHDDC2 0x00000001 +/// Save command in the registry under a unique key, corresponding to parameter \b iCommandIndex +#define ADL_DDC_OPTION_RESTORECOMMAND 0x00000002 +/// Combine write-read DDC block access command. +#define ADL_DDC_OPTION_COMBOWRITEREAD 0x00000010 +/// Direct DDC access to the immediate device connected to graphics card. +/// MST with this option set: DDC command is sent to first branch. +/// MST with this option not set: DDC command is sent to the end node sink device. +#define ADL_DDC_OPTION_SENDTOIMMEDIATEDEVICE 0x00000020 +/// @} + +/// \name Values for +/// ADLI2C.iAction used with ADL_Display_WriteAndReadI2C() +/// @{ + +#define ADL_DL_I2C_ACTIONREAD 0x00000001 +#define ADL_DL_I2C_ACTIONWRITE 0x00000002 +#define ADL_DL_I2C_ACTIONREAD_REPEATEDSTART 0x00000003 +#define ADL_DL_I2C_ACTIONIS_PRESENT 0x00000004 +/// @} + + +/// @} //Misc + +/// \defgroup define_adl_results Result Codes +/// This group of definitions are the various results returned by all ADL functions \n +/// @{ +/// All OK, but need to wait +#define ADL_OK_WAIT 4 +/// All OK, but need restart +#define ADL_OK_RESTART 3 +/// All OK but need mode change +#define ADL_OK_MODE_CHANGE 2 +/// All OK, but with warning +#define ADL_OK_WARNING 1 +/// ADL function completed successfully +#define ADL_OK 0 +/// Generic Error. Most likely one or more of the Escape calls to the driver failed! +#define ADL_ERR -1 +/// ADL not initialized +#define ADL_ERR_NOT_INIT -2 +/// One of the parameter passed is invalid +#define ADL_ERR_INVALID_PARAM -3 +/// One of the parameter size is invalid +#define ADL_ERR_INVALID_PARAM_SIZE -4 +/// Invalid ADL index passed +#define ADL_ERR_INVALID_ADL_IDX -5 +/// Invalid controller index passed +#define ADL_ERR_INVALID_CONTROLLER_IDX -6 +/// Invalid display index passed +#define ADL_ERR_INVALID_DIPLAY_IDX -7 +/// Function not supported by the driver +#define ADL_ERR_NOT_SUPPORTED -8 +/// Null Pointer error +#define ADL_ERR_NULL_POINTER -9 +/// Call can't be made due to disabled adapter +#define ADL_ERR_DISABLED_ADAPTER -10 +/// Invalid Callback +#define ADL_ERR_INVALID_CALLBACK -11 +/// Display Resource conflict +#define ADL_ERR_RESOURCE_CONFLICT -12 +//Failed to update some of the values. Can be returned by set request that include multiple values if not all values were successfully committed. +#define ADL_ERR_SET_INCOMPLETE -20 +/// There's no Linux XDisplay in Linux Console environment +#define ADL_ERR_NO_XDISPLAY -21 +/// escape call failed becuse of incompatiable driver found in driver store +#define ADL_ERR_CALL_TO_INCOMPATIABLE_DRIVER -22 +/// not running as administrator +#define ADL_ERR_NO_ADMINISTRATOR_PRIVILEGES -23 +/// Feature Sync Start api is not called yet +#define ADL_ERR_FEATURESYNC_NOT_STARTED -24 +/// Adapter is in an invalid power state +#define ADL_ERR_INVALID_POWER_STATE -25 + +/// @} +/// + +/// \defgroup define_display_type Display Type +/// Define Monitor/CRT display type +/// @{ +/// Define Monitor display type +#define ADL_DT_MONITOR 0 +/// Define TV display type +#define ADL_DT_TELEVISION 1 +/// Define LCD display type +#define ADL_DT_LCD_PANEL 2 +/// Define DFP display type +#define ADL_DT_DIGITAL_FLAT_PANEL 3 +/// Define Componment Video display type +#define ADL_DT_COMPONENT_VIDEO 4 +/// Define Projector display type +#define ADL_DT_PROJECTOR 5 +/// @} + +/// \defgroup define_display_connection_type Display Connection Type +/// @{ +/// Define unknown display output type +#define ADL_DOT_UNKNOWN 0 +/// Define composite display output type +#define ADL_DOT_COMPOSITE 1 +/// Define SVideo display output type +#define ADL_DOT_SVIDEO 2 +/// Define analog display output type +#define ADL_DOT_ANALOG 3 +/// Define digital display output type +#define ADL_DOT_DIGITAL 4 +/// @} + +/// \defgroup define_color_type Display Color Type and Source +/// Define Display Color Type and Source +/// @{ +#define ADL_DISPLAY_COLOR_BRIGHTNESS (1 << 0) +#define ADL_DISPLAY_COLOR_CONTRAST (1 << 1) +#define ADL_DISPLAY_COLOR_SATURATION (1 << 2) +#define ADL_DISPLAY_COLOR_HUE (1 << 3) +#define ADL_DISPLAY_COLOR_TEMPERATURE (1 << 4) + +/// Color Temperature Source is EDID +#define ADL_DISPLAY_COLOR_TEMPERATURE_SOURCE_EDID (1 << 5) +/// Color Temperature Source is User +#define ADL_DISPLAY_COLOR_TEMPERATURE_SOURCE_USER (1 << 6) +/// @} + +/// \defgroup define_adjustment_capabilities Display Adjustment Capabilities +/// Display adjustment capabilities values. Returned by ADL_Display_AdjustCaps_Get +/// @{ +#define ADL_DISPLAY_ADJUST_OVERSCAN (1 << 0) +#define ADL_DISPLAY_ADJUST_VERT_POS (1 << 1) +#define ADL_DISPLAY_ADJUST_HOR_POS (1 << 2) +#define ADL_DISPLAY_ADJUST_VERT_SIZE (1 << 3) +#define ADL_DISPLAY_ADJUST_HOR_SIZE (1 << 4) +#define ADL_DISPLAY_ADJUST_SIZEPOS (ADL_DISPLAY_ADJUST_VERT_POS | ADL_DISPLAY_ADJUST_HOR_POS | ADL_DISPLAY_ADJUST_VERT_SIZE | ADL_DISPLAY_ADJUST_HOR_SIZE) +#define ADL_DISPLAY_CUSTOMMODES (1<<5) +#define ADL_DISPLAY_ADJUST_UNDERSCAN (1<<6) +/// @} + +///Down-scale support +#define ADL_DISPLAY_CAPS_DOWNSCALE (1 << 0) + +/// Sharpness support +#define ADL_DISPLAY_CAPS_SHARPNESS (1 << 0) + +/// \defgroup define_desktop_config Desktop Configuration Flags +/// These flags are used by ADL_DesktopConfig_xxx +/// \deprecated This API has been deprecated because it was only used for RandR 1.1 (Red Hat 5.x) distributions which is now not supported. +/// @{ +#define ADL_DESKTOPCONFIG_UNKNOWN 0 /* UNKNOWN desktop config */ +#define ADL_DESKTOPCONFIG_SINGLE (1 << 0) /* Single */ +#define ADL_DESKTOPCONFIG_CLONE (1 << 2) /* Clone */ +#define ADL_DESKTOPCONFIG_BIGDESK_H (1 << 4) /* Big Desktop Horizontal */ +#define ADL_DESKTOPCONFIG_BIGDESK_V (1 << 5) /* Big Desktop Vertical */ +#define ADL_DESKTOPCONFIG_BIGDESK_HR (1 << 6) /* Big Desktop Reverse Horz */ +#define ADL_DESKTOPCONFIG_BIGDESK_VR (1 << 7) /* Big Desktop Reverse Vert */ +#define ADL_DESKTOPCONFIG_RANDR12 (1 << 8) /* RandR 1.2 Multi-display */ +/// @} + +/// needed for ADLDDCInfo structure +#define ADL_MAX_DISPLAY_NAME 256 + +/// \defgroup define_edid_flags Values for ulDDCInfoFlag +/// defines for ulDDCInfoFlag EDID flag +/// @{ +#define ADL_DISPLAYDDCINFOEX_FLAG_PROJECTORDEVICE (1 << 0) +#define ADL_DISPLAYDDCINFOEX_FLAG_EDIDEXTENSION (1 << 1) +#define ADL_DISPLAYDDCINFOEX_FLAG_DIGITALDEVICE (1 << 2) +#define ADL_DISPLAYDDCINFOEX_FLAG_HDMIAUDIODEVICE (1 << 3) +#define ADL_DISPLAYDDCINFOEX_FLAG_SUPPORTS_AI (1 << 4) +#define ADL_DISPLAYDDCINFOEX_FLAG_SUPPORT_xvYCC601 (1 << 5) +#define ADL_DISPLAYDDCINFOEX_FLAG_SUPPORT_xvYCC709 (1 << 6) +/// @} + +/// \defgroup define_displayinfo_connector Display Connector Type +/// defines for ADLDisplayInfo.iDisplayConnector +/// @{ +#define ADL_DISPLAY_CONTYPE_UNKNOWN 0 +#define ADL_DISPLAY_CONTYPE_VGA 1 +#define ADL_DISPLAY_CONTYPE_DVI_D 2 +#define ADL_DISPLAY_CONTYPE_DVI_I 3 +#define ADL_DISPLAY_CONTYPE_ATICVDONGLE_NTSC 4 +#define ADL_DISPLAY_CONTYPE_ATICVDONGLE_JPN 5 +#define ADL_DISPLAY_CONTYPE_ATICVDONGLE_NONI2C_JPN 6 +#define ADL_DISPLAY_CONTYPE_ATICVDONGLE_NONI2C_NTSC 7 +#define ADL_DISPLAY_CONTYPE_PROPRIETARY 8 +#define ADL_DISPLAY_CONTYPE_HDMI_TYPE_A 10 +#define ADL_DISPLAY_CONTYPE_HDMI_TYPE_B 11 +#define ADL_DISPLAY_CONTYPE_SVIDEO 12 +#define ADL_DISPLAY_CONTYPE_COMPOSITE 13 +#define ADL_DISPLAY_CONTYPE_RCA_3COMPONENT 14 +#define ADL_DISPLAY_CONTYPE_DISPLAYPORT 15 +#define ADL_DISPLAY_CONTYPE_EDP 16 +#define ADL_DISPLAY_CONTYPE_WIRELESSDISPLAY 17 +#define ADL_DISPLAY_CONTYPE_USB_TYPE_C 18 +/// @} + +/// TV Capabilities and Standards +/// \defgroup define_tv_caps TV Capabilities and Standards +/// \deprecated Dropping support for TV displays +/// @{ +#define ADL_TV_STANDARDS (1 << 0) +#define ADL_TV_SCART (1 << 1) + +/// TV Standards Definitions +#define ADL_STANDARD_NTSC_M (1 << 0) +#define ADL_STANDARD_NTSC_JPN (1 << 1) +#define ADL_STANDARD_NTSC_N (1 << 2) +#define ADL_STANDARD_PAL_B (1 << 3) +#define ADL_STANDARD_PAL_COMB_N (1 << 4) +#define ADL_STANDARD_PAL_D (1 << 5) +#define ADL_STANDARD_PAL_G (1 << 6) +#define ADL_STANDARD_PAL_H (1 << 7) +#define ADL_STANDARD_PAL_I (1 << 8) +#define ADL_STANDARD_PAL_K (1 << 9) +#define ADL_STANDARD_PAL_K1 (1 << 10) +#define ADL_STANDARD_PAL_L (1 << 11) +#define ADL_STANDARD_PAL_M (1 << 12) +#define ADL_STANDARD_PAL_N (1 << 13) +#define ADL_STANDARD_PAL_SECAM_D (1 << 14) +#define ADL_STANDARD_PAL_SECAM_K (1 << 15) +#define ADL_STANDARD_PAL_SECAM_K1 (1 << 16) +#define ADL_STANDARD_PAL_SECAM_L (1 << 17) +/// @} + + +/// \defgroup define_video_custom_mode Video Custom Mode flags +/// Component Video Custom Mode flags. This is used by the iFlags parameter in ADLCustomMode +/// @{ +#define ADL_CUSTOMIZEDMODEFLAG_MODESUPPORTED (1 << 0) +#define ADL_CUSTOMIZEDMODEFLAG_NOTDELETETABLE (1 << 1) +#define ADL_CUSTOMIZEDMODEFLAG_INSERTBYDRIVER (1 << 2) +#define ADL_CUSTOMIZEDMODEFLAG_INTERLACED (1 << 3) +#define ADL_CUSTOMIZEDMODEFLAG_BASEMODE (1 << 4) +/// @} + +/// \defgroup define_ddcinfoflag Values used for DDCInfoFlag +/// ulDDCInfoFlag field values used by the ADLDDCInfo structure +/// @{ +#define ADL_DISPLAYDDCINFOEX_FLAG_PROJECTORDEVICE (1 << 0) +#define ADL_DISPLAYDDCINFOEX_FLAG_EDIDEXTENSION (1 << 1) +#define ADL_DISPLAYDDCINFOEX_FLAG_DIGITALDEVICE (1 << 2) +#define ADL_DISPLAYDDCINFOEX_FLAG_HDMIAUDIODEVICE (1 << 3) +#define ADL_DISPLAYDDCINFOEX_FLAG_SUPPORTS_AI (1 << 4) +#define ADL_DISPLAYDDCINFOEX_FLAG_SUPPORT_xvYCC601 (1 << 5) +#define ADL_DISPLAYDDCINFOEX_FLAG_SUPPORT_xvYCC709 (1 << 6) +/// @} + +/// \defgroup define_cv_dongle Values used by ADL_CV_DongleSettings_xxx +/// The following is applicable to ADL_DISPLAY_CONTYPE_ATICVDONGLE_JP and ADL_DISPLAY_CONTYPE_ATICVDONGLE_NONI2C_D only +/// \deprecated Dropping support for Component Video displays +/// @{ +#define ADL_DISPLAY_CV_DONGLE_D1 (1 << 0) +#define ADL_DISPLAY_CV_DONGLE_D2 (1 << 1) +#define ADL_DISPLAY_CV_DONGLE_D3 (1 << 2) +#define ADL_DISPLAY_CV_DONGLE_D4 (1 << 3) +#define ADL_DISPLAY_CV_DONGLE_D5 (1 << 4) + +/// The following is applicable to ADL_DISPLAY_CONTYPE_ATICVDONGLE_NA and ADL_DISPLAY_CONTYPE_ATICVDONGLE_NONI2C only + +#define ADL_DISPLAY_CV_DONGLE_480I (1 << 0) +#define ADL_DISPLAY_CV_DONGLE_480P (1 << 1) +#define ADL_DISPLAY_CV_DONGLE_540P (1 << 2) +#define ADL_DISPLAY_CV_DONGLE_720P (1 << 3) +#define ADL_DISPLAY_CV_DONGLE_1080I (1 << 4) +#define ADL_DISPLAY_CV_DONGLE_1080P (1 << 5) +#define ADL_DISPLAY_CV_DONGLE_16_9 (1 << 6) +#define ADL_DISPLAY_CV_DONGLE_720P50 (1 << 7) +#define ADL_DISPLAY_CV_DONGLE_1080I25 (1 << 8) +#define ADL_DISPLAY_CV_DONGLE_576I25 (1 << 9) +#define ADL_DISPLAY_CV_DONGLE_576P50 (1 << 10) +#define ADL_DISPLAY_CV_DONGLE_1080P24 (1 << 11) +#define ADL_DISPLAY_CV_DONGLE_1080P25 (1 << 12) +#define ADL_DISPLAY_CV_DONGLE_1080P30 (1 << 13) +#define ADL_DISPLAY_CV_DONGLE_1080P50 (1 << 14) +/// @} + +/// \defgroup define_formats_ovr Formats Override Settings +/// Display force modes flags +/// @{ +/// +#define ADL_DISPLAY_FORMAT_FORCE_720P 0x00000001 +#define ADL_DISPLAY_FORMAT_FORCE_1080I 0x00000002 +#define ADL_DISPLAY_FORMAT_FORCE_1080P 0x00000004 +#define ADL_DISPLAY_FORMAT_FORCE_720P50 0x00000008 +#define ADL_DISPLAY_FORMAT_FORCE_1080I25 0x00000010 +#define ADL_DISPLAY_FORMAT_FORCE_576I25 0x00000020 +#define ADL_DISPLAY_FORMAT_FORCE_576P50 0x00000040 +#define ADL_DISPLAY_FORMAT_FORCE_1080P24 0x00000080 +#define ADL_DISPLAY_FORMAT_FORCE_1080P25 0x00000100 +#define ADL_DISPLAY_FORMAT_FORCE_1080P30 0x00000200 +#define ADL_DISPLAY_FORMAT_FORCE_1080P50 0x00000400 + +///< Below are \b EXTENDED display mode flags + +#define ADL_DISPLAY_FORMAT_CVDONGLEOVERIDE 0x00000001 +#define ADL_DISPLAY_FORMAT_CVMODEUNDERSCAN 0x00000002 +#define ADL_DISPLAY_FORMAT_FORCECONNECT_SUPPORTED 0x00000004 +#define ADL_DISPLAY_FORMAT_RESTRICT_FORMAT_SELECTION 0x00000008 +#define ADL_DISPLAY_FORMAT_SETASPECRATIO 0x00000010 +#define ADL_DISPLAY_FORMAT_FORCEMODES 0x00000020 +#define ADL_DISPLAY_FORMAT_LCDRTCCOEFF 0x00000040 +/// @} + +/// Defines used by OD5 +#define ADL_PM_PARAM_DONT_CHANGE 0 + +/// The following defines Bus types +/// @{ +#define ADL_BUSTYPE_PCI 0 /* PCI bus */ +#define ADL_BUSTYPE_AGP 1 /* AGP bus */ +#define ADL_BUSTYPE_PCIE 2 /* PCI Express bus */ +#define ADL_BUSTYPE_PCIE_GEN2 3 /* PCI Express 2nd generation bus */ +#define ADL_BUSTYPE_PCIE_GEN3 4 /* PCI Express 3rd generation bus */ +#define ADL_BUSTYPE_PCIE_GEN4 5 /* PCI Express 4th generation bus */ +/// @} + +/// \defgroup define_ws_caps Workstation Capabilities +/// Workstation values +/// @{ + +/// This value indicates that the workstation card supports active stereo though stereo output connector +#define ADL_STEREO_SUPPORTED (1 << 2) +/// This value indicates that the workstation card supports active stereo via "blue-line" +#define ADL_STEREO_BLUE_LINE (1 << 3) +/// This value is used to turn off stereo mode. +#define ADL_STEREO_OFF 0 +/// This value indicates that the workstation card supports active stereo. This is also used to set the stereo mode to active though the stereo output connector +#define ADL_STEREO_ACTIVE (1 << 1) +/// This value indicates that the workstation card supports auto-stereo monitors with horizontal interleave. This is also used to set the stereo mode to use the auto-stereo monitor with horizontal interleave +#define ADL_STEREO_AUTO_HORIZONTAL (1 << 30) +/// This value indicates that the workstation card supports auto-stereo monitors with vertical interleave. This is also used to set the stereo mode to use the auto-stereo monitor with vertical interleave +#define ADL_STEREO_AUTO_VERTICAL (1 << 31) +/// This value indicates that the workstation card supports passive stereo, ie. non stereo sync +#define ADL_STEREO_PASSIVE (1 << 6) +/// This value indicates that the workstation card supports auto-stereo monitors with vertical interleave. This is also used to set the stereo mode to use the auto-stereo monitor with vertical interleave +#define ADL_STEREO_PASSIVE_HORIZ (1 << 7) +/// This value indicates that the workstation card supports auto-stereo monitors with vertical interleave. This is also used to set the stereo mode to use the auto-stereo monitor with vertical interleave +#define ADL_STEREO_PASSIVE_VERT (1 << 8) +/// This value indicates that the workstation card supports auto-stereo monitors with Samsung. +#define ADL_STEREO_AUTO_SAMSUNG (1 << 11) +/// This value indicates that the workstation card supports auto-stereo monitors with Tridility. +#define ADL_STEREO_AUTO_TSL (1 << 12) +/// This value indicates that the workstation card supports DeepBitDepth (10 bpp) +#define ADL_DEEPBITDEPTH_10BPP_SUPPORTED (1 << 5) + +/// This value indicates that the workstation supports 8-Bit Grayscale +#define ADL_8BIT_GREYSCALE_SUPPORTED (1 << 9) +/// This value indicates that the workstation supports CUSTOM TIMING +#define ADL_CUSTOM_TIMING_SUPPORTED (1 << 10) + +/// Load balancing is supported. +#define ADL_WORKSTATION_LOADBALANCING_SUPPORTED 0x00000001 +/// Load balancing is available. +#define ADL_WORKSTATION_LOADBALANCING_AVAILABLE 0x00000002 + +/// Load balancing is disabled. +#define ADL_WORKSTATION_LOADBALANCING_DISABLED 0x00000000 +/// Load balancing is Enabled. +#define ADL_WORKSTATION_LOADBALANCING_ENABLED 0x00000001 + + + +/// @} + +/// \defgroup define_adapterspeed speed setting from the adapter +/// @{ +#define ADL_CONTEXT_SPEED_UNFORCED 0 /* default asic running speed */ +#define ADL_CONTEXT_SPEED_FORCEHIGH 1 /* asic running speed is forced to high */ +#define ADL_CONTEXT_SPEED_FORCELOW 2 /* asic running speed is forced to low */ + +#define ADL_ADAPTER_SPEEDCAPS_SUPPORTED (1 << 0) /* change asic running speed setting is supported */ +/// @} + +/// \defgroup define_glsync Genlock related values +/// GL-Sync port types (unique values) +/// @{ +/// Unknown port of GL-Sync module +#define ADL_GLSYNC_PORT_UNKNOWN 0 +/// BNC port of of GL-Sync module +#define ADL_GLSYNC_PORT_BNC 1 +/// RJ45(1) port of of GL-Sync module +#define ADL_GLSYNC_PORT_RJ45PORT1 2 +/// RJ45(2) port of of GL-Sync module +#define ADL_GLSYNC_PORT_RJ45PORT2 3 + +// GL-Sync Genlock settings mask (bit-vector) + +/// None of the ADLGLSyncGenlockConfig members are valid +#define ADL_GLSYNC_CONFIGMASK_NONE 0 +/// The ADLGLSyncGenlockConfig.lSignalSource member is valid +#define ADL_GLSYNC_CONFIGMASK_SIGNALSOURCE (1 << 0) +/// The ADLGLSyncGenlockConfig.iSyncField member is valid +#define ADL_GLSYNC_CONFIGMASK_SYNCFIELD (1 << 1) +/// The ADLGLSyncGenlockConfig.iSampleRate member is valid +#define ADL_GLSYNC_CONFIGMASK_SAMPLERATE (1 << 2) +/// The ADLGLSyncGenlockConfig.lSyncDelay member is valid +#define ADL_GLSYNC_CONFIGMASK_SYNCDELAY (1 << 3) +/// The ADLGLSyncGenlockConfig.iTriggerEdge member is valid +#define ADL_GLSYNC_CONFIGMASK_TRIGGEREDGE (1 << 4) +/// The ADLGLSyncGenlockConfig.iScanRateCoeff member is valid +#define ADL_GLSYNC_CONFIGMASK_SCANRATECOEFF (1 << 5) +/// The ADLGLSyncGenlockConfig.lFramelockCntlVector member is valid +#define ADL_GLSYNC_CONFIGMASK_FRAMELOCKCNTL (1 << 6) + + +// GL-Sync Framelock control mask (bit-vector) + +/// Framelock is disabled +#define ADL_GLSYNC_FRAMELOCKCNTL_NONE 0 +/// Framelock is enabled +#define ADL_GLSYNC_FRAMELOCKCNTL_ENABLE ( 1 << 0) + +#define ADL_GLSYNC_FRAMELOCKCNTL_DISABLE ( 1 << 1) +#define ADL_GLSYNC_FRAMELOCKCNTL_SWAP_COUNTER_RESET ( 1 << 2) +#define ADL_GLSYNC_FRAMELOCKCNTL_SWAP_COUNTER_ACK ( 1 << 3) +#define ADL_GLSYNC_FRAMELOCKCNTL_VERSION_KMD (1 << 4) + +#define ADL_GLSYNC_FRAMELOCKCNTL_STATE_ENABLE ( 1 << 0) +#define ADL_GLSYNC_FRAMELOCKCNTL_STATE_KMD (1 << 4) + +// GL-Sync Framelock counters mask (bit-vector) +#define ADL_GLSYNC_COUNTER_SWAP ( 1 << 0 ) + +// GL-Sync Signal Sources (unique values) + +/// GL-Sync signal source is undefined +#define ADL_GLSYNC_SIGNALSOURCE_UNDEFINED 0x00000100 +/// GL-Sync signal source is Free Run +#define ADL_GLSYNC_SIGNALSOURCE_FREERUN 0x00000101 +/// GL-Sync signal source is the BNC GL-Sync port +#define ADL_GLSYNC_SIGNALSOURCE_BNCPORT 0x00000102 +/// GL-Sync signal source is the RJ45(1) GL-Sync port +#define ADL_GLSYNC_SIGNALSOURCE_RJ45PORT1 0x00000103 +/// GL-Sync signal source is the RJ45(2) GL-Sync port +#define ADL_GLSYNC_SIGNALSOURCE_RJ45PORT2 0x00000104 + + +// GL-Sync Signal Types (unique values) + +/// GL-Sync signal type is unknown +#define ADL_GLSYNC_SIGNALTYPE_UNDEFINED 0 +/// GL-Sync signal type is 480I +#define ADL_GLSYNC_SIGNALTYPE_480I 1 +/// GL-Sync signal type is 576I +#define ADL_GLSYNC_SIGNALTYPE_576I 2 +/// GL-Sync signal type is 480P +#define ADL_GLSYNC_SIGNALTYPE_480P 3 +/// GL-Sync signal type is 576P +#define ADL_GLSYNC_SIGNALTYPE_576P 4 +/// GL-Sync signal type is 720P +#define ADL_GLSYNC_SIGNALTYPE_720P 5 +/// GL-Sync signal type is 1080P +#define ADL_GLSYNC_SIGNALTYPE_1080P 6 +/// GL-Sync signal type is 1080I +#define ADL_GLSYNC_SIGNALTYPE_1080I 7 +/// GL-Sync signal type is SDI +#define ADL_GLSYNC_SIGNALTYPE_SDI 8 +/// GL-Sync signal type is TTL +#define ADL_GLSYNC_SIGNALTYPE_TTL 9 +/// GL_Sync signal type is Analog +#define ADL_GLSYNC_SIGNALTYPE_ANALOG 10 + +// GL-Sync Sync Field options (unique values) + +///GL-Sync sync field option is undefined +#define ADL_GLSYNC_SYNCFIELD_UNDEFINED 0 +///GL-Sync sync field option is Sync to Field 1 (used for Interlaced signal types) +#define ADL_GLSYNC_SYNCFIELD_BOTH 1 +///GL-Sync sync field option is Sync to Both fields (used for Interlaced signal types) +#define ADL_GLSYNC_SYNCFIELD_1 2 + + +// GL-Sync trigger edge options (unique values) + +/// GL-Sync trigger edge is undefined +#define ADL_GLSYNC_TRIGGEREDGE_UNDEFINED 0 +/// GL-Sync trigger edge is the rising edge +#define ADL_GLSYNC_TRIGGEREDGE_RISING 1 +/// GL-Sync trigger edge is the falling edge +#define ADL_GLSYNC_TRIGGEREDGE_FALLING 2 +/// GL-Sync trigger edge is both the rising and the falling edge +#define ADL_GLSYNC_TRIGGEREDGE_BOTH 3 + + +// GL-Sync scan rate coefficient/multiplier options (unique values) + +/// GL-Sync scan rate coefficient/multiplier is undefined +#define ADL_GLSYNC_SCANRATECOEFF_UNDEFINED 0 +/// GL-Sync scan rate coefficient/multiplier is 5 +#define ADL_GLSYNC_SCANRATECOEFF_x5 1 +/// GL-Sync scan rate coefficient/multiplier is 4 +#define ADL_GLSYNC_SCANRATECOEFF_x4 2 +/// GL-Sync scan rate coefficient/multiplier is 3 +#define ADL_GLSYNC_SCANRATECOEFF_x3 3 +/// GL-Sync scan rate coefficient/multiplier is 5:2 (SMPTE) +#define ADL_GLSYNC_SCANRATECOEFF_x5_DIV_2 4 +/// GL-Sync scan rate coefficient/multiplier is 2 +#define ADL_GLSYNC_SCANRATECOEFF_x2 5 +/// GL-Sync scan rate coefficient/multiplier is 3 : 2 +#define ADL_GLSYNC_SCANRATECOEFF_x3_DIV_2 6 +/// GL-Sync scan rate coefficient/multiplier is 5 : 4 +#define ADL_GLSYNC_SCANRATECOEFF_x5_DIV_4 7 +/// GL-Sync scan rate coefficient/multiplier is 1 (default) +#define ADL_GLSYNC_SCANRATECOEFF_x1 8 +/// GL-Sync scan rate coefficient/multiplier is 4 : 5 +#define ADL_GLSYNC_SCANRATECOEFF_x4_DIV_5 9 +/// GL-Sync scan rate coefficient/multiplier is 2 : 3 +#define ADL_GLSYNC_SCANRATECOEFF_x2_DIV_3 10 +/// GL-Sync scan rate coefficient/multiplier is 1 : 2 +#define ADL_GLSYNC_SCANRATECOEFF_x1_DIV_2 11 +/// GL-Sync scan rate coefficient/multiplier is 2 : 5 (SMPTE) +#define ADL_GLSYNC_SCANRATECOEFF_x2_DIV_5 12 +/// GL-Sync scan rate coefficient/multiplier is 1 : 3 +#define ADL_GLSYNC_SCANRATECOEFF_x1_DIV_3 13 +/// GL-Sync scan rate coefficient/multiplier is 1 : 4 +#define ADL_GLSYNC_SCANRATECOEFF_x1_DIV_4 14 +/// GL-Sync scan rate coefficient/multiplier is 1 : 5 +#define ADL_GLSYNC_SCANRATECOEFF_x1_DIV_5 15 + + +// GL-Sync port (signal presence) states (unique values) + +/// GL-Sync port state is undefined +#define ADL_GLSYNC_PORTSTATE_UNDEFINED 0 +/// GL-Sync port is not connected +#define ADL_GLSYNC_PORTSTATE_NOCABLE 1 +/// GL-Sync port is Idle +#define ADL_GLSYNC_PORTSTATE_IDLE 2 +/// GL-Sync port has an Input signal +#define ADL_GLSYNC_PORTSTATE_INPUT 3 +/// GL-Sync port is Output +#define ADL_GLSYNC_PORTSTATE_OUTPUT 4 + + +// GL-Sync LED types (used index within ADL_Workstation_GLSyncPortState_Get returned ppGlSyncLEDs array) (unique values) + +/// Index into the ADL_Workstation_GLSyncPortState_Get returned ppGlSyncLEDs array for the one LED of the BNC port +#define ADL_GLSYNC_LEDTYPE_BNC 0 +/// Index into the ADL_Workstation_GLSyncPortState_Get returned ppGlSyncLEDs array for the Left LED of the RJ45(1) or RJ45(2) port +#define ADL_GLSYNC_LEDTYPE_RJ45_LEFT 0 +/// Index into the ADL_Workstation_GLSyncPortState_Get returned ppGlSyncLEDs array for the Right LED of the RJ45(1) or RJ45(2) port +#define ADL_GLSYNC_LEDTYPE_RJ45_RIGHT 1 + + +// GL-Sync LED colors (unique values) + +/// GL-Sync LED undefined color +#define ADL_GLSYNC_LEDCOLOR_UNDEFINED 0 +/// GL-Sync LED is unlit +#define ADL_GLSYNC_LEDCOLOR_NOLIGHT 1 +/// GL-Sync LED is yellow +#define ADL_GLSYNC_LEDCOLOR_YELLOW 2 +/// GL-Sync LED is red +#define ADL_GLSYNC_LEDCOLOR_RED 3 +/// GL-Sync LED is green +#define ADL_GLSYNC_LEDCOLOR_GREEN 4 +/// GL-Sync LED is flashing green +#define ADL_GLSYNC_LEDCOLOR_FLASH_GREEN 5 + + +// GL-Sync Port Control (refers one GL-Sync Port) (unique values) + +/// Used to configure the RJ54(1) or RJ42(2) port of GL-Sync is as Idle +#define ADL_GLSYNC_PORTCNTL_NONE 0x00000000 +/// Used to configure the RJ54(1) or RJ42(2) port of GL-Sync is as Output +#define ADL_GLSYNC_PORTCNTL_OUTPUT 0x00000001 + + +// GL-Sync Mode Control (refers one Display/Controller) (bitfields) + +/// Used to configure the display to use internal timing (not genlocked) +#define ADL_GLSYNC_MODECNTL_NONE 0x00000000 +/// Bitfield used to configure the display as genlocked (either as Timing Client or as Timing Server) +#define ADL_GLSYNC_MODECNTL_GENLOCK 0x00000001 +/// Bitfield used to configure the display as Timing Server +#define ADL_GLSYNC_MODECNTL_TIMINGSERVER 0x00000002 + +// GL-Sync Mode Status +/// Display is currently not genlocked +#define ADL_GLSYNC_MODECNTL_STATUS_NONE 0x00000000 +/// Display is currently genlocked +#define ADL_GLSYNC_MODECNTL_STATUS_GENLOCK 0x00000001 +/// Display requires a mode switch +#define ADL_GLSYNC_MODECNTL_STATUS_SETMODE_REQUIRED 0x00000002 +/// Display is capable of being genlocked +#define ADL_GLSYNC_MODECNTL_STATUS_GENLOCK_ALLOWED 0x00000004 + +#define ADL_MAX_GLSYNC_PORTS 8 +#define ADL_MAX_GLSYNC_PORT_LEDS 8 + +/// @} + +/// \defgroup define_crossfirestate CrossfireX state of a particular adapter CrossfireX combination +/// @{ +#define ADL_XFIREX_STATE_NOINTERCONNECT ( 1 << 0 ) /* Dongle / cable is missing */ +#define ADL_XFIREX_STATE_DOWNGRADEPIPES ( 1 << 1 ) /* CrossfireX can be enabled if pipes are downgraded */ +#define ADL_XFIREX_STATE_DOWNGRADEMEM ( 1 << 2 ) /* CrossfireX cannot be enabled unless mem downgraded */ +#define ADL_XFIREX_STATE_REVERSERECOMMENDED ( 1 << 3 ) /* Card reversal recommended, CrossfireX cannot be enabled. */ +#define ADL_XFIREX_STATE_3DACTIVE ( 1 << 4 ) /* 3D client is active - CrossfireX cannot be safely enabled */ +#define ADL_XFIREX_STATE_MASTERONSLAVE ( 1 << 5 ) /* Dongle is OK but master is on slave */ +#define ADL_XFIREX_STATE_NODISPLAYCONNECT ( 1 << 6 ) /* No (valid) display connected to master card. */ +#define ADL_XFIREX_STATE_NOPRIMARYVIEW ( 1 << 7 ) /* CrossfireX is enabled but master is not current primary device */ +#define ADL_XFIREX_STATE_DOWNGRADEVISMEM ( 1 << 8 ) /* CrossfireX cannot be enabled unless visible mem downgraded */ +#define ADL_XFIREX_STATE_LESSTHAN8LANE_MASTER ( 1 << 9 ) /* CrossfireX can be enabled however performance not optimal due to <8 lanes */ +#define ADL_XFIREX_STATE_LESSTHAN8LANE_SLAVE ( 1 << 10 ) /* CrossfireX can be enabled however performance not optimal due to <8 lanes */ +#define ADL_XFIREX_STATE_PEERTOPEERFAILED ( 1 << 11 ) /* CrossfireX cannot be enabled due to failed peer to peer test */ +#define ADL_XFIREX_STATE_MEMISDOWNGRADED ( 1 << 16 ) /* Notification that memory is currently downgraded */ +#define ADL_XFIREX_STATE_PIPESDOWNGRADED ( 1 << 17 ) /* Notification that pipes are currently downgraded */ +#define ADL_XFIREX_STATE_XFIREXACTIVE ( 1 << 18 ) /* CrossfireX is enabled on current device */ +#define ADL_XFIREX_STATE_VISMEMISDOWNGRADED ( 1 << 19 ) /* Notification that visible FB memory is currently downgraded */ +#define ADL_XFIREX_STATE_INVALIDINTERCONNECTION ( 1 << 20 ) /* Cannot support current inter-connection configuration */ +#define ADL_XFIREX_STATE_NONP2PMODE ( 1 << 21 ) /* CrossfireX will only work with clients supporting non P2P mode */ +#define ADL_XFIREX_STATE_DOWNGRADEMEMBANKS ( 1 << 22 ) /* CrossfireX cannot be enabled unless memory banks downgraded */ +#define ADL_XFIREX_STATE_MEMBANKSDOWNGRADED ( 1 << 23 ) /* Notification that memory banks are currently downgraded */ +#define ADL_XFIREX_STATE_DUALDISPLAYSALLOWED ( 1 << 24 ) /* Extended desktop or clone mode is allowed. */ +#define ADL_XFIREX_STATE_P2P_APERTURE_MAPPING ( 1 << 25 ) /* P2P mapping was through peer aperture */ +#define ADL_XFIREX_STATE_P2PFLUSH_REQUIRED ADL_XFIREX_STATE_P2P_APERTURE_MAPPING /* For back compatible */ +#define ADL_XFIREX_STATE_XSP_CONNECTED ( 1 << 26 ) /* There is CrossfireX side port connection between GPUs */ +#define ADL_XFIREX_STATE_ENABLE_CF_REBOOT_REQUIRED ( 1 << 27 ) /* System needs a reboot bofore enable CrossfireX */ +#define ADL_XFIREX_STATE_DISABLE_CF_REBOOT_REQUIRED ( 1 << 28 ) /* System needs a reboot after disable CrossfireX */ +#define ADL_XFIREX_STATE_DRV_HANDLE_DOWNGRADE_KEY ( 1 << 29 ) /* Indicate base driver handles the downgrade key updating */ +#define ADL_XFIREX_STATE_CF_RECONFIG_REQUIRED ( 1 << 30 ) /* CrossfireX need to be reconfigured by CCC because of a LDA chain broken */ +#define ADL_XFIREX_STATE_ERRORGETTINGSTATUS ( 1 << 31 ) /* Could not obtain current status */ +/// @} + +/////////////////////////////////////////////////////////////////////////// +// ADL_DISPLAY_ADJUSTMENT_PIXELFORMAT adjustment values +// (bit-vector) +/////////////////////////////////////////////////////////////////////////// +/// \defgroup define_pixel_formats Pixel Formats values +/// This group defines the various Pixel Formats that a particular digital display can support. \n +/// Since a display can support multiple formats, these values can be bit-or'ed to indicate the various formats \n +/// @{ +#define ADL_DISPLAY_PIXELFORMAT_UNKNOWN 0 +#define ADL_DISPLAY_PIXELFORMAT_RGB (1 << 0) +#define ADL_DISPLAY_PIXELFORMAT_YCRCB444 (1 << 1) //Limited range +#define ADL_DISPLAY_PIXELFORMAT_YCRCB422 (1 << 2) //Limited range +#define ADL_DISPLAY_PIXELFORMAT_RGB_LIMITED_RANGE (1 << 3) +#define ADL_DISPLAY_PIXELFORMAT_RGB_FULL_RANGE ADL_DISPLAY_PIXELFORMAT_RGB //Full range +#define ADL_DISPLAY_PIXELFORMAT_YCRCB420 (1 << 4) +/// @} + +/// \defgroup define_contype Connector Type Values +/// ADLDisplayConfig.ulConnectorType defines +/// @{ +#define ADL_DL_DISPLAYCONFIG_CONTYPE_UNKNOWN 0 +#define ADL_DL_DISPLAYCONFIG_CONTYPE_CV_NONI2C_JP 1 +#define ADL_DL_DISPLAYCONFIG_CONTYPE_CV_JPN 2 +#define ADL_DL_DISPLAYCONFIG_CONTYPE_CV_NA 3 +#define ADL_DL_DISPLAYCONFIG_CONTYPE_CV_NONI2C_NA 4 +#define ADL_DL_DISPLAYCONFIG_CONTYPE_VGA 5 +#define ADL_DL_DISPLAYCONFIG_CONTYPE_DVI_D 6 +#define ADL_DL_DISPLAYCONFIG_CONTYPE_DVI_I 7 +#define ADL_DL_DISPLAYCONFIG_CONTYPE_HDMI_TYPE_A 8 +#define ADL_DL_DISPLAYCONFIG_CONTYPE_HDMI_TYPE_B 9 +#define ADL_DL_DISPLAYCONFIG_CONTYPE_DISPLAYPORT 10 +/// @} + +/////////////////////////////////////////////////////////////////////////// +// ADL_DISPLAY_DISPLAYINFO_ Definitions +// for ADLDisplayInfo.iDisplayInfoMask and ADLDisplayInfo.iDisplayInfoValue +// (bit-vector) +/////////////////////////////////////////////////////////////////////////// +/// \defgroup define_displayinfomask Display Info Mask Values +/// @{ +#define ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED 0x00000001 +#define ADL_DISPLAY_DISPLAYINFO_DISPLAYMAPPED 0x00000002 +#define ADL_DISPLAY_DISPLAYINFO_NONLOCAL 0x00000004 +#define ADL_DISPLAY_DISPLAYINFO_FORCIBLESUPPORTED 0x00000008 +#define ADL_DISPLAY_DISPLAYINFO_GENLOCKSUPPORTED 0x00000010 +#define ADL_DISPLAY_DISPLAYINFO_MULTIVPU_SUPPORTED 0x00000020 +#define ADL_DISPLAY_DISPLAYINFO_LDA_DISPLAY 0x00000040 +#define ADL_DISPLAY_DISPLAYINFO_MODETIMING_OVERRIDESSUPPORTED 0x00000080 + +#define ADL_DISPLAY_DISPLAYINFO_MANNER_SUPPORTED_SINGLE 0x00000100 +#define ADL_DISPLAY_DISPLAYINFO_MANNER_SUPPORTED_CLONE 0x00000200 + +/// Legacy support for XP +#define ADL_DISPLAY_DISPLAYINFO_MANNER_SUPPORTED_2VSTRETCH 0x00000400 +#define ADL_DISPLAY_DISPLAYINFO_MANNER_SUPPORTED_2HSTRETCH 0x00000800 +#define ADL_DISPLAY_DISPLAYINFO_MANNER_SUPPORTED_EXTENDED 0x00001000 + +/// More support manners +#define ADL_DISPLAY_DISPLAYINFO_MANNER_SUPPORTED_NSTRETCH1GPU 0x00010000 +#define ADL_DISPLAY_DISPLAYINFO_MANNER_SUPPORTED_NSTRETCHNGPU 0x00020000 +#define ADL_DISPLAY_DISPLAYINFO_MANNER_SUPPORTED_RESERVED2 0x00040000 +#define ADL_DISPLAY_DISPLAYINFO_MANNER_SUPPORTED_RESERVED3 0x00080000 + +/// Projector display type +#define ADL_DISPLAY_DISPLAYINFO_SHOWTYPE_PROJECTOR 0x00100000 + +/// @} + + +/////////////////////////////////////////////////////////////////////////// +// ADL_ADAPTER_DISPLAY_MANNER_SUPPORTED_ Definitions +// for ADLAdapterDisplayCap of ADL_Adapter_Display_Cap() +// (bit-vector) +/////////////////////////////////////////////////////////////////////////// +/// \defgroup define_adaptermanner Adapter Manner Support Values +/// @{ +#define ADL_ADAPTER_DISPLAYCAP_MANNER_SUPPORTED_NOTACTIVE 0x00000001 +#define ADL_ADAPTER_DISPLAYCAP_MANNER_SUPPORTED_SINGLE 0x00000002 +#define ADL_ADAPTER_DISPLAYCAP_MANNER_SUPPORTED_CLONE 0x00000004 +#define ADL_ADAPTER_DISPLAYCAP_MANNER_SUPPORTED_NSTRETCH1GPU 0x00000008 +#define ADL_ADAPTER_DISPLAYCAP_MANNER_SUPPORTED_NSTRETCHNGPU 0x00000010 + +/// Legacy support for XP +#define ADL_ADAPTER_DISPLAYCAP_MANNER_SUPPORTED_2VSTRETCH 0x00000020 +#define ADL_ADAPTER_DISPLAYCAP_MANNER_SUPPORTED_2HSTRETCH 0x00000040 +#define ADL_ADAPTER_DISPLAYCAP_MANNER_SUPPORTED_EXTENDED 0x00000080 + +#define ADL_ADAPTER_DISPLAYCAP_PREFERDISPLAY_SUPPORTED 0x00000100 +#define ADL_ADAPTER_DISPLAYCAP_BEZEL_SUPPORTED 0x00000200 + + +/////////////////////////////////////////////////////////////////////////// +// ADL_DISPLAY_DISPLAYMAP_MANNER_ Definitions +// for ADLDisplayMap.iDisplayMapMask and ADLDisplayMap.iDisplayMapValue +// (bit-vector) +/////////////////////////////////////////////////////////////////////////// +#define ADL_DISPLAY_DISPLAYMAP_MANNER_RESERVED 0x00000001 +#define ADL_DISPLAY_DISPLAYMAP_MANNER_NOTACTIVE 0x00000002 +#define ADL_DISPLAY_DISPLAYMAP_MANNER_SINGLE 0x00000004 +#define ADL_DISPLAY_DISPLAYMAP_MANNER_CLONE 0x00000008 +#define ADL_DISPLAY_DISPLAYMAP_MANNER_RESERVED1 0x00000010 // Removed NSTRETCH +#define ADL_DISPLAY_DISPLAYMAP_MANNER_HSTRETCH 0x00000020 +#define ADL_DISPLAY_DISPLAYMAP_MANNER_VSTRETCH 0x00000040 +#define ADL_DISPLAY_DISPLAYMAP_MANNER_VLD 0x00000080 + +/// @} + +/////////////////////////////////////////////////////////////////////////// +// ADL_DISPLAY_DISPLAYMAP_OPTION_ Definitions +// for iOption in function ADL_Display_DisplayMapConfig_Get +// (bit-vector) +/////////////////////////////////////////////////////////////////////////// +#define ADL_DISPLAY_DISPLAYMAP_OPTION_GPUINFO 0x00000001 + +/////////////////////////////////////////////////////////////////////////// +// ADL_DISPLAY_DISPLAYTARGET_ Definitions +// for ADLDisplayTarget.iDisplayTargetMask and ADLDisplayTarget.iDisplayTargetValue +// (bit-vector) +/////////////////////////////////////////////////////////////////////////// +#define ADL_DISPLAY_DISPLAYTARGET_PREFERRED 0x00000001 + +/////////////////////////////////////////////////////////////////////////// +// ADL_DISPLAY_POSSIBLEMAPRESULT_VALID Definitions +// for ADLPossibleMapResult.iPossibleMapResultMask and ADLPossibleMapResult.iPossibleMapResultValue +// (bit-vector) +/////////////////////////////////////////////////////////////////////////// +#define ADL_DISPLAY_POSSIBLEMAPRESULT_VALID 0x00000001 +#define ADL_DISPLAY_POSSIBLEMAPRESULT_BEZELSUPPORTED 0x00000002 +#define ADL_DISPLAY_POSSIBLEMAPRESULT_OVERLAPSUPPORTED 0x00000004 + +/////////////////////////////////////////////////////////////////////////// +// ADL_DISPLAY_MODE_ Definitions +// for ADLMode.iModeMask, ADLMode.iModeValue, and ADLMode.iModeFlag +// (bit-vector) +/////////////////////////////////////////////////////////////////////////// +/// \defgroup define_displaymode Display Mode Values +/// @{ +#define ADL_DISPLAY_MODE_COLOURFORMAT_565 0x00000001 +#define ADL_DISPLAY_MODE_COLOURFORMAT_8888 0x00000002 +#define ADL_DISPLAY_MODE_ORIENTATION_SUPPORTED_000 0x00000004 +#define ADL_DISPLAY_MODE_ORIENTATION_SUPPORTED_090 0x00000008 +#define ADL_DISPLAY_MODE_ORIENTATION_SUPPORTED_180 0x00000010 +#define ADL_DISPLAY_MODE_ORIENTATION_SUPPORTED_270 0x00000020 +#define ADL_DISPLAY_MODE_REFRESHRATE_ROUNDED 0x00000040 +#define ADL_DISPLAY_MODE_REFRESHRATE_ONLY 0x00000080 + +#define ADL_DISPLAY_MODE_PROGRESSIVE_FLAG 0 +#define ADL_DISPLAY_MODE_INTERLACED_FLAG 2 +/// @} + +/////////////////////////////////////////////////////////////////////////// +// ADL_OSMODEINFO Definitions +/////////////////////////////////////////////////////////////////////////// +/// \defgroup define_osmode OS Mode Values +/// @{ +#define ADL_OSMODEINFOXPOS_DEFAULT -640 +#define ADL_OSMODEINFOYPOS_DEFAULT 0 +#define ADL_OSMODEINFOXRES_DEFAULT 640 +#define ADL_OSMODEINFOYRES_DEFAULT 480 +#define ADL_OSMODEINFOXRES_DEFAULT800 800 +#define ADL_OSMODEINFOYRES_DEFAULT600 600 +#define ADL_OSMODEINFOREFRESHRATE_DEFAULT 60 +#define ADL_OSMODEINFOCOLOURDEPTH_DEFAULT 8 +#define ADL_OSMODEINFOCOLOURDEPTH_DEFAULT16 16 +#define ADL_OSMODEINFOCOLOURDEPTH_DEFAULT24 24 +#define ADL_OSMODEINFOCOLOURDEPTH_DEFAULT32 32 +#define ADL_OSMODEINFOORIENTATION_DEFAULT 0 +#define ADL_OSMODEINFOORIENTATION_DEFAULT_WIN7 DISPLAYCONFIG_ROTATION_FORCE_UINT32 +#define ADL_OSMODEFLAG_DEFAULT 0 +/// @} + +/////////////////////////////////////////////////////////////////////////// +// ADLThreadingModel Enumeration +/////////////////////////////////////////////////////////////////////////// +/// \defgroup thread_model +/// Used with \ref ADL_Main_ControlX2_Create and \ref ADL2_Main_ControlX2_Create to specify how ADL handles API calls when executed by multiple threads concurrently. +/// \brief Declares ADL threading behavior. +/// @{ +typedef enum ADLThreadingModel +{ + ADL_THREADING_UNLOCKED = 0, /*!< Default behavior. ADL will not enforce serialization of ADL API executions by multiple threads. Multiple threads will be allowed to enter to ADL at the same time. Note that ADL library is not guaranteed to be thread-safe. Client that calls ADL_Main_Control_Create have to provide its own mechanism for ADL calls serialization. */ + ADL_THREADING_LOCKED /*!< ADL will enforce serialization of ADL API when called by multiple threads. Only single thread will be allowed to enter ADL API at the time. This option makes ADL calls thread-safe. You shouldn't use this option if ADL calls will be executed on Linux on x-server rendering thread. It can cause the application to hung. */ +}ADLThreadingModel; + +/// @} +/////////////////////////////////////////////////////////////////////////// +// ADLPurposeCode Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLPurposeCode +{ + ADL_PURPOSECODE_NORMAL = 0, + ADL_PURPOSECODE_HIDE_MODE_SWITCH, + ADL_PURPOSECODE_MODE_SWITCH, + ADL_PURPOSECODE_ATTATCH_DEVICE, + ADL_PURPOSECODE_DETACH_DEVICE, + ADL_PURPOSECODE_SETPRIMARY_DEVICE, + ADL_PURPOSECODE_GDI_ROTATION, + ADL_PURPOSECODE_ATI_ROTATION +}; +/////////////////////////////////////////////////////////////////////////// +// ADLAngle Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLAngle +{ + ADL_ANGLE_LANDSCAPE = 0, + ADL_ANGLE_ROTATERIGHT = 90, + ADL_ANGLE_ROTATE180 = 180, + ADL_ANGLE_ROTATELEFT = 270, +}; + +/////////////////////////////////////////////////////////////////////////// +// ADLOrientationDataType Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLOrientationDataType +{ + ADL_ORIENTATIONTYPE_OSDATATYPE, + ADL_ORIENTATIONTYPE_NONOSDATATYPE +}; + +/////////////////////////////////////////////////////////////////////////// +// ADLPanningMode Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLPanningMode +{ + ADL_PANNINGMODE_NO_PANNING = 0, + ADL_PANNINGMODE_AT_LEAST_ONE_NO_PANNING = 1, + ADL_PANNINGMODE_ALLOW_PANNING = 2, +}; + +/////////////////////////////////////////////////////////////////////////// +// ADLLARGEDESKTOPTYPE Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLLARGEDESKTOPTYPE +{ + ADL_LARGEDESKTOPTYPE_NORMALDESKTOP = 0, + ADL_LARGEDESKTOPTYPE_PSEUDOLARGEDESKTOP = 1, + ADL_LARGEDESKTOPTYPE_VERYLARGEDESKTOP = 2 +}; + +/////////////////////////////////////////////////////////////////////////// +// ADLPlatform Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLPlatForm +{ + GRAPHICS_PLATFORM_DESKTOP = 0, + GRAPHICS_PLATFORM_MOBILE = 1 +}; + +/////////////////////////////////////////////////////////////////////////// +// ADLGraphicCoreGeneration Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLGraphicCoreGeneration +{ + ADL_GRAPHIC_CORE_GENERATION_UNDEFINED = 0, + ADL_GRAPHIC_CORE_GENERATION_PRE_GCN = 1, + ADL_GRAPHIC_CORE_GENERATION_GCN = 2, + ADL_GRAPHIC_CORE_GENERATION_RDNA = 3 +}; + +// Other Definitions for internal use + +// Values for ADL_Display_WriteAndReadI2CRev_Get() + +#define ADL_I2C_MAJOR_API_REV 0x00000001 +#define ADL_I2C_MINOR_DEFAULT_API_REV 0x00000000 +#define ADL_I2C_MINOR_OEM_API_REV 0x00000001 + +// Values for ADL_Display_WriteAndReadI2C() +#define ADL_DL_I2C_LINE_OEM 0x00000001 +#define ADL_DL_I2C_LINE_OD_CONTROL 0x00000002 +#define ADL_DL_I2C_LINE_OEM2 0x00000003 +#define ADL_DL_I2C_LINE_OEM3 0x00000004 +#define ADL_DL_I2C_LINE_OEM4 0x00000005 +#define ADL_DL_I2C_LINE_OEM5 0x00000006 +#define ADL_DL_I2C_LINE_OEM6 0x00000007 +#define ADL_DL_I2C_LINE_GPIO 0x00000008 + +// Max size of I2C data buffer +#define ADL_DL_I2C_MAXDATASIZE 0x00000018 +#define ADL_DL_I2C_MAXWRITEDATASIZE 0x0000000C +#define ADL_DL_I2C_MAXADDRESSLENGTH 0x00000006 +#define ADL_DL_I2C_MAXOFFSETLENGTH 0x00000004 + +// I2C clock speed in KHz +#define ADL_DL_I2C_SPEED_50K 50 +#define ADL_DL_I2C_SPEED_100K 100 +#define ALD_DL_I2C_SPEED_400K 400 +#define ADL_DL_I2C_SPEED_1M 1000 +#define ADL_DL_I2C_SPEED_2M 2300 + +/// Values for ADLDisplayProperty.iPropertyType +#define ADL_DL_DISPLAYPROPERTY_TYPE_UNKNOWN 0 +#define ADL_DL_DISPLAYPROPERTY_TYPE_EXPANSIONMODE 1 +#define ADL_DL_DISPLAYPROPERTY_TYPE_USEUNDERSCANSCALING 2 +/// Enables ITC processing for HDMI panels that are capable of the feature +#define ADL_DL_DISPLAYPROPERTY_TYPE_ITCFLAGENABLE 9 +#define ADL_DL_DISPLAYPROPERTY_TYPE_DOWNSCALE 11 +#define ADL_DL_DISPLAYPROPERTY_TYPE_INTEGER_SCALING 12 + + +/// Values for ADLDisplayContent.iContentType +/// Certain HDMI panels that support ITC have support for a feature such that, the display on the panel +/// can be adjusted to optimize the view of the content being displayed, depending on the type of content. +#define ADL_DL_DISPLAYCONTENT_TYPE_GRAPHICS 1 +#define ADL_DL_DISPLAYCONTENT_TYPE_PHOTO 2 +#define ADL_DL_DISPLAYCONTENT_TYPE_CINEMA 4 +#define ADL_DL_DISPLAYCONTENT_TYPE_GAME 8 + + + +//values for ADLDisplayProperty.iExpansionMode +#define ADL_DL_DISPLAYPROPERTY_EXPANSIONMODE_CENTER 0 +#define ADL_DL_DISPLAYPROPERTY_EXPANSIONMODE_FULLSCREEN 1 +#define ADL_DL_DISPLAYPROPERTY_EXPANSIONMODE_ASPECTRATIO 2 + + +///\defgroup define_dither_states Dithering options +/// @{ +/// Dithering disabled. +#define ADL_DL_DISPLAY_DITHER_DISABLED 0 +/// Use default driver settings for dithering. Note that the default setting could be dithering disabled. +#define ADL_DL_DISPLAY_DITHER_DRIVER_DEFAULT 1 +/// Temporal dithering to 6 bpc. Note that if the input is 12 bits, the two least significant bits will be truncated. +#define ADL_DL_DISPLAY_DITHER_FM6 2 +/// Temporal dithering to 8 bpc. +#define ADL_DL_DISPLAY_DITHER_FM8 3 +/// Temporal dithering to 10 bpc. +#define ADL_DL_DISPLAY_DITHER_FM10 4 +/// Spatial dithering to 6 bpc. Note that if the input is 12 bits, the two least significant bits will be truncated. +#define ADL_DL_DISPLAY_DITHER_DITH6 5 +/// Spatial dithering to 8 bpc. +#define ADL_DL_DISPLAY_DITHER_DITH8 6 +/// Spatial dithering to 10 bpc. +#define ADL_DL_DISPLAY_DITHER_DITH10 7 +/// Spatial dithering to 6 bpc. Random number generators are reset every frame, so the same input value of a certain pixel will always be dithered to the same output value. Note that if the input is 12 bits, the two least significant bits will be truncated. +#define ADL_DL_DISPLAY_DITHER_DITH6_NO_FRAME_RAND 8 +/// Spatial dithering to 8 bpc. Random number generators are reset every frame, so the same input value of a certain pixel will always be dithered to the same output value. +#define ADL_DL_DISPLAY_DITHER_DITH8_NO_FRAME_RAND 9 +/// Spatial dithering to 10 bpc. Random number generators are reset every frame, so the same input value of a certain pixel will always be dithered to the same output value. +#define ADL_DL_DISPLAY_DITHER_DITH10_NO_FRAME_RAND 10 +/// Truncation to 6 bpc. +#define ADL_DL_DISPLAY_DITHER_TRUN6 11 +/// Truncation to 8 bpc. +#define ADL_DL_DISPLAY_DITHER_TRUN8 12 +/// Truncation to 10 bpc. +#define ADL_DL_DISPLAY_DITHER_TRUN10 13 +/// Truncation to 10 bpc followed by spatial dithering to 8 bpc. +#define ADL_DL_DISPLAY_DITHER_TRUN10_DITH8 14 +/// Truncation to 10 bpc followed by spatial dithering to 6 bpc. +#define ADL_DL_DISPLAY_DITHER_TRUN10_DITH6 15 +/// Truncation to 10 bpc followed by temporal dithering to 8 bpc. +#define ADL_DL_DISPLAY_DITHER_TRUN10_FM8 16 +/// Truncation to 10 bpc followed by temporal dithering to 6 bpc. +#define ADL_DL_DISPLAY_DITHER_TRUN10_FM6 17 +/// Truncation to 10 bpc followed by spatial dithering to 8 bpc and temporal dithering to 6 bpc. +#define ADL_DL_DISPLAY_DITHER_TRUN10_DITH8_FM6 18 +/// Spatial dithering to 10 bpc followed by temporal dithering to 8 bpc. +#define ADL_DL_DISPLAY_DITHER_DITH10_FM8 19 +/// Spatial dithering to 10 bpc followed by temporal dithering to 6 bpc. +#define ADL_DL_DISPLAY_DITHER_DITH10_FM6 20 +/// Truncation to 8 bpc followed by spatial dithering to 6 bpc. +#define ADL_DL_DISPLAY_DITHER_TRUN8_DITH6 21 +/// Truncation to 8 bpc followed by temporal dithering to 6 bpc. +#define ADL_DL_DISPLAY_DITHER_TRUN8_FM6 22 +/// Spatial dithering to 8 bpc followed by temporal dithering to 6 bpc. +#define ADL_DL_DISPLAY_DITHER_DITH8_FM6 23 +#define ADL_DL_DISPLAY_DITHER_LAST ADL_DL_DISPLAY_DITHER_DITH8_FM6 +/// @} + + +/// Display Get Cached EDID flag +#define ADL_MAX_EDIDDATA_SIZE 256 // number of UCHAR +#define ADL_MAX_OVERRIDEEDID_SIZE 512 // number of UCHAR +#define ADL_MAX_EDID_EXTENSION_BLOCKS 3 + +#define ADL_DL_CONTROLLER_OVERLAY_ALPHA 0 +#define ADL_DL_CONTROLLER_OVERLAY_ALPHAPERPIX 1 + +#define ADL_DL_DISPLAY_DATA_PACKET__INFO_PACKET_RESET 0x00000000 +#define ADL_DL_DISPLAY_DATA_PACKET__INFO_PACKET_SET 0x00000001 +#define ADL_DL_DISPLAY_DATA_PACKET__INFO_PACKET_SCAN 0x00000002 + +///\defgroup define_display_packet Display Data Packet Types +/// @{ +#define ADL_DL_DISPLAY_DATA_PACKET__TYPE__AVI 0x00000001 +#define ADL_DL_DISPLAY_DATA_PACKET__TYPE__GAMMUT 0x00000002 +#define ADL_DL_DISPLAY_DATA_PACKET__TYPE__VENDORINFO 0x00000004 +#define ADL_DL_DISPLAY_DATA_PACKET__TYPE__HDR 0x00000008 +#define ADL_DL_DISPLAY_DATA_PACKET__TYPE__SPD 0x00000010 +/// @} + +// matrix types +#define ADL_GAMUT_MATRIX_SD 1 // SD matrix i.e. BT601 +#define ADL_GAMUT_MATRIX_HD 2 // HD matrix i.e. BT709 + +///\defgroup define_clockinfo_flags Clock flags +/// Used by ADLAdapterODClockInfo.iFlag +/// @{ +#define ADL_DL_CLOCKINFO_FLAG_FULLSCREEN3DONLY 0x00000001 +#define ADL_DL_CLOCKINFO_FLAG_ALWAYSFULLSCREEN3D 0x00000002 +#define ADL_DL_CLOCKINFO_FLAG_VPURECOVERYREDUCED 0x00000004 +#define ADL_DL_CLOCKINFO_FLAG_THERMALPROTECTION 0x00000008 +/// @} + +// Supported GPUs +// ADL_Display_PowerXpressActiveGPU_Get() +#define ADL_DL_POWERXPRESS_GPU_INTEGRATED 1 +#define ADL_DL_POWERXPRESS_GPU_DISCRETE 2 + +// Possible values for lpOperationResult +// ADL_Display_PowerXpressActiveGPU_Get() +#define ADL_DL_POWERXPRESS_SWITCH_RESULT_STARTED 1 // Switch procedure has been started - Windows platform only +#define ADL_DL_POWERXPRESS_SWITCH_RESULT_DECLINED 2 // Switch procedure cannot be started - All platforms +#define ADL_DL_POWERXPRESS_SWITCH_RESULT_ALREADY 3 // System already has required status - All platforms +#define ADL_DL_POWERXPRESS_SWITCH_RESULT_DEFERRED 5 // Switch was deferred and requires an X restart - Linux platform only + +// PowerXpress support version +// ADL_Display_PowerXpressVersion_Get() +#define ADL_DL_POWERXPRESS_VERSION_MAJOR 2 // Current PowerXpress support version 2.0 +#define ADL_DL_POWERXPRESS_VERSION_MINOR 0 + +#define ADL_DL_POWERXPRESS_VERSION (((ADL_DL_POWERXPRESS_VERSION_MAJOR) << 16) | ADL_DL_POWERXPRESS_VERSION_MINOR) + +//values for ADLThermalControllerInfo.iThermalControllerDomain +#define ADL_DL_THERMAL_DOMAIN_OTHER 0 +#define ADL_DL_THERMAL_DOMAIN_GPU 1 + +//values for ADLThermalControllerInfo.iFlags +#define ADL_DL_THERMAL_FLAG_INTERRUPT 1 +#define ADL_DL_THERMAL_FLAG_FANCONTROL 2 + +///\defgroup define_fanctrl Fan speed cotrol +/// Values for ADLFanSpeedInfo.iFlags +/// @{ +#define ADL_DL_FANCTRL_SUPPORTS_PERCENT_READ 1 +#define ADL_DL_FANCTRL_SUPPORTS_PERCENT_WRITE 2 +#define ADL_DL_FANCTRL_SUPPORTS_RPM_READ 4 +#define ADL_DL_FANCTRL_SUPPORTS_RPM_WRITE 8 +/// @} + +//values for ADLFanSpeedValue.iSpeedType +#define ADL_DL_FANCTRL_SPEED_TYPE_PERCENT 1 +#define ADL_DL_FANCTRL_SPEED_TYPE_RPM 2 + +//values for ADLFanSpeedValue.iFlags +#define ADL_DL_FANCTRL_FLAG_USER_DEFINED_SPEED 1 + +// MVPU interfaces +#define ADL_DL_MAX_MVPU_ADAPTERS 4 +#define MVPU_ADAPTER_0 0x00000001 +#define MVPU_ADAPTER_1 0x00000002 +#define MVPU_ADAPTER_2 0x00000004 +#define MVPU_ADAPTER_3 0x00000008 +#define ADL_DL_MAX_REGISTRY_PATH 256 + +//values for ADLMVPUStatus.iStatus +#define ADL_DL_MVPU_STATUS_OFF 0 +#define ADL_DL_MVPU_STATUS_ON 1 + +// values for ASIC family +///\defgroup define_Asic_type Detailed asic types +/// Defines for Adapter ASIC family type +/// @{ +#define ADL_ASIC_UNDEFINED 0 +#define ADL_ASIC_DISCRETE (1 << 0) +#define ADL_ASIC_INTEGRATED (1 << 1) +#define ADL_ASIC_WORKSTATION (1 << 2) +#define ADL_ASIC_FIREMV (1 << 3) +#define ADL_ASIC_XGP (1 << 4) +#define ADL_ASIC_FUSION (1 << 5) +#define ADL_ASIC_FIRESTREAM (1 << 6) +#define ADL_ASIC_EMBEDDED (1 << 7) +// Backward compatibility +#define ADL_ASIC_FIREGL ADL_ASIC_WORKSTATION +/// @} + +///\defgroup define_detailed_timing_flags Detailed Timimg Flags +/// Defines for ADLDetailedTiming.sTimingFlags field +/// @{ +#define ADL_DL_TIMINGFLAG_DOUBLE_SCAN 0x0001 +//sTimingFlags is set when the mode is INTERLACED, if not PROGRESSIVE +#define ADL_DL_TIMINGFLAG_INTERLACED 0x0002 +//sTimingFlags is set when the Horizontal Sync is POSITIVE, if not NEGATIVE +#define ADL_DL_TIMINGFLAG_H_SYNC_POLARITY 0x0004 +//sTimingFlags is set when the Vertical Sync is POSITIVE, if not NEGATIVE +#define ADL_DL_TIMINGFLAG_V_SYNC_POLARITY 0x0008 +/// @} + +///\defgroup define_modetiming_standard Timing Standards +/// Defines for ADLDisplayModeInfo.iTimingStandard field +/// @{ +#define ADL_DL_MODETIMING_STANDARD_CVT 0x00000001 // CVT Standard +#define ADL_DL_MODETIMING_STANDARD_GTF 0x00000002 // GFT Standard +#define ADL_DL_MODETIMING_STANDARD_DMT 0x00000004 // DMT Standard +#define ADL_DL_MODETIMING_STANDARD_CUSTOM 0x00000008 // User-defined standard +#define ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT 0x00000010 // Remove Mode from overriden list +#define ADL_DL_MODETIMING_STANDARD_CVT_RB 0x00000020 // CVT-RB Standard +/// @} + +// \defgroup define_xserverinfo driver x-server info +/// These flags are used by ADL_XServerInfo_Get() +// @ + +/// Xinerama is active in the x-server, Xinerama extension may report it to be active but it +/// may not be active in x-server +#define ADL_XSERVERINFO_XINERAMAACTIVE (1<<0) + +/// RandR 1.2 is supported by driver, RandR extension may report version 1.2 +/// but driver may not support it +#define ADL_XSERVERINFO_RANDR12SUPPORTED (1<<1) +// @ + + +///\defgroup define_eyefinity_constants Eyefinity Definitions +/// @{ + +#define ADL_CONTROLLERVECTOR_0 1 // ADL_CONTROLLERINDEX_0 = 0, (1 << ADL_CONTROLLERINDEX_0) +#define ADL_CONTROLLERVECTOR_1 2 // ADL_CONTROLLERINDEX_1 = 1, (1 << ADL_CONTROLLERINDEX_1) + +#define ADL_DISPLAY_SLSGRID_ORIENTATION_000 0x00000001 +#define ADL_DISPLAY_SLSGRID_ORIENTATION_090 0x00000002 +#define ADL_DISPLAY_SLSGRID_ORIENTATION_180 0x00000004 +#define ADL_DISPLAY_SLSGRID_ORIENTATION_270 0x00000008 +#define ADL_DISPLAY_SLSGRID_CAP_OPTION_RELATIVETO_LANDSCAPE 0x00000001 +#define ADL_DISPLAY_SLSGRID_CAP_OPTION_RELATIVETO_CURRENTANGLE 0x00000002 +#define ADL_DISPLAY_SLSGRID_PORTAIT_MODE 0x00000004 +#define ADL_DISPLAY_SLSGRID_KEEPTARGETROTATION 0x00000080 + +#define ADL_DISPLAY_SLSGRID_SAMEMODESLS_SUPPORT 0x00000010 +#define ADL_DISPLAY_SLSGRID_MIXMODESLS_SUPPORT 0x00000020 +#define ADL_DISPLAY_SLSGRID_DISPLAYROTATION_SUPPORT 0x00000040 +#define ADL_DISPLAY_SLSGRID_DESKTOPROTATION_SUPPORT 0x00000080 + + +#define ADL_DISPLAY_SLSMAP_SLSLAYOUTMODE_FIT 0x0100 +#define ADL_DISPLAY_SLSMAP_SLSLAYOUTMODE_FILL 0x0200 +#define ADL_DISPLAY_SLSMAP_SLSLAYOUTMODE_EXPAND 0x0400 + +#define ADL_DISPLAY_SLSMAP_IS_SLS 0x1000 +#define ADL_DISPLAY_SLSMAP_IS_SLSBUILDER 0x2000 +#define ADL_DISPLAY_SLSMAP_IS_CLONEVT 0x4000 + +#define ADL_DISPLAY_SLSMAPCONFIG_GET_OPTION_RELATIVETO_LANDSCAPE 0x00000001 +#define ADL_DISPLAY_SLSMAPCONFIG_GET_OPTION_RELATIVETO_CURRENTANGLE 0x00000002 + +#define ADL_DISPLAY_SLSMAPCONFIG_CREATE_OPTION_RELATIVETO_LANDSCAPE 0x00000001 +#define ADL_DISPLAY_SLSMAPCONFIG_CREATE_OPTION_RELATIVETO_CURRENTANGLE 0x00000002 + +#define ADL_DISPLAY_SLSMAPCONFIG_REARRANGE_OPTION_RELATIVETO_LANDSCAPE 0x00000001 +#define ADL_DISPLAY_SLSMAPCONFIG_REARRANGE_OPTION_RELATIVETO_CURRENTANGLE 0x00000002 + +#define ADL_SLS_SAMEMODESLS_SUPPORT 0x0001 +#define ADL_SLS_MIXMODESLS_SUPPORT 0x0002 +#define ADL_SLS_DISPLAYROTATIONSLS_SUPPORT 0x0004 +#define ADL_SLS_DESKTOPROTATIONSLS_SUPPORT 0x0008 + +#define ADL_SLS_TARGETS_INVALID 0x0001 +#define ADL_SLS_MODES_INVALID 0x0002 +#define ADL_SLS_ROTATIONS_INVALID 0x0004 +#define ADL_SLS_POSITIONS_INVALID 0x0008 +#define ADL_SLS_LAYOUTMODE_INVALID 0x0010 + +#define ADL_DISPLAY_SLSDISPLAYOFFSET_VALID 0x0002 + +#define ADL_DISPLAY_SLSGRID_RELATIVETO_LANDSCAPE 0x00000010 +#define ADL_DISPLAY_SLSGRID_RELATIVETO_CURRENTANGLE 0x00000020 + + +/// The bit mask identifies displays is currently in bezel mode. +#define ADL_DISPLAY_SLSMAP_BEZELMODE 0x00000010 +/// The bit mask identifies displays from this map is arranged. +#define ADL_DISPLAY_SLSMAP_DISPLAYARRANGED 0x00000002 +/// The bit mask identifies this map is currently in used for the current adapter. +#define ADL_DISPLAY_SLSMAP_CURRENTCONFIG 0x00000004 + +///For onlay active SLS map info +#define ADL_DISPLAY_SLSMAPINDEXLIST_OPTION_ACTIVE 0x00000001 + +///For Bezel +#define ADL_DISPLAY_BEZELOFFSET_STEPBYSTEPSET 0x00000004 +#define ADL_DISPLAY_BEZELOFFSET_COMMIT 0x00000008 + +typedef enum SLS_ImageCropType { + Fit = 1, + Fill = 2, + Expand = 3 +}SLS_ImageCropType; + + +typedef enum DceSettingsType { + DceSetting_HdmiLq, + DceSetting_DpSettings, + DceSetting_Protection + +} DceSettingsType; + +typedef enum DpLinkRate { + DPLinkRate_Unknown, + DPLinkRate_RBR, + DPLinkRate_2_16Gbps, + DPLinkRate_2_43Gbps, + DPLinkRate_HBR, + DPLinkRate_4_32Gbps, + DPLinkRate_HBR2, + DPLinkRate_HBR3, + DPLinkRate_UHBR10, + DPLinkRate_UHBR13D5, + DPLinkRate_UHBR20 + +} DpLinkRate; + +/// @} + +///\defgroup define_powerxpress_constants PowerXpress Definitions +/// @{ + +/// The bit mask identifies PX caps for ADLPXConfigCaps.iPXConfigCapMask and ADLPXConfigCaps.iPXConfigCapValue +#define ADL_PX_CONFIGCAPS_SPLASHSCREEN_SUPPORT 0x0001 +#define ADL_PX_CONFIGCAPS_CF_SUPPORT 0x0002 +#define ADL_PX_CONFIGCAPS_MUXLESS 0x0004 +#define ADL_PX_CONFIGCAPS_PROFILE_COMPLIANT 0x0008 +#define ADL_PX_CONFIGCAPS_NON_AMD_DRIVEN_DISPLAYS 0x0010 +#define ADL_PX_CONFIGCAPS_FIXED_SUPPORT 0x0020 +#define ADL_PX_CONFIGCAPS_DYNAMIC_SUPPORT 0x0040 +#define ADL_PX_CONFIGCAPS_HIDE_AUTO_SWITCH 0x0080 + +/// The bit mask identifies PX schemes for ADLPXSchemeRange +#define ADL_PX_SCHEMEMASK_FIXED 0x0001 +#define ADL_PX_SCHEMEMASK_DYNAMIC 0x0002 + +/// PX Schemes +typedef enum ADLPXScheme +{ + ADL_PX_SCHEME_INVALID = 0, + ADL_PX_SCHEME_FIXED = ADL_PX_SCHEMEMASK_FIXED, + ADL_PX_SCHEME_DYNAMIC = ADL_PX_SCHEMEMASK_DYNAMIC +}ADLPXScheme; + +/// Just keep the old definitions for compatibility, need to be removed later +typedef enum PXScheme +{ + PX_SCHEME_INVALID = 0, + PX_SCHEME_FIXED = 1, + PX_SCHEME_DYNAMIC = 2 +} PXScheme; + + +/// @} + +///\defgroup define_appprofiles For Application Profiles +/// @{ + +#define ADL_APP_PROFILE_FILENAME_LENGTH 256 +#define ADL_APP_PROFILE_TIMESTAMP_LENGTH 32 +#define ADL_APP_PROFILE_VERSION_LENGTH 32 +#define ADL_APP_PROFILE_PROPERTY_LENGTH 64 + +enum ApplicationListType +{ + ADL_PX40_MRU, + ADL_PX40_MISSED, + ADL_PX40_DISCRETE, + ADL_PX40_INTEGRATED, + ADL_MMD_PROFILED, + ADL_PX40_TOTAL +}; + +typedef enum ADLProfilePropertyType +{ + ADL_PROFILEPROPERTY_TYPE_BINARY = 0, + ADL_PROFILEPROPERTY_TYPE_BOOLEAN, + ADL_PROFILEPROPERTY_TYPE_DWORD, + ADL_PROFILEPROPERTY_TYPE_QWORD, + ADL_PROFILEPROPERTY_TYPE_ENUMERATED, + ADL_PROFILEPROPERTY_TYPE_STRING +}ADLProfilePropertyType; + + +//Virtual display type returning virtual display type and for request for creating a dummy target ID (xInput or remote play) +typedef enum ADL_VIRTUALDISPLAY_TYPE +{ + ADL_VIRTUALDISPLAY_NONE = 0, + ADL_VIRTUALDISPLAY_XINPUT = 1, //Requested for xInput + ADL_VIRTUALDISPLAY_REMOTEPLAY = 2, //Requested for emulated display during remote play + ADL_VIRTUALDISPLAY_GENERIC = 10 //Generic virtual display, af a type different than any of the above special ones +}ADL_VIRTUALDISPLAY_TYPE; + +/// @} + +///\defgroup define_dp12 For Display Port 1.2 +/// @{ + +/// Maximum Relative Address Link +#define ADL_MAX_RAD_LINK_COUNT 15 + +/// @} + +///\defgroup defines_gamutspace Driver Supported Gamut Space +/// @{ + +/// The flags desribes that gamut is related to source or to destination and to overlay or to graphics +#define ADL_GAMUT_REFERENCE_SOURCE (1 << 0) +#define ADL_GAMUT_GAMUT_VIDEO_CONTENT (1 << 1) + +/// The flags are used to describe the source of gamut and how read information from struct ADLGamutData +#define ADL_CUSTOM_WHITE_POINT (1 << 0) +#define ADL_CUSTOM_GAMUT (1 << 1) +#define ADL_GAMUT_REMAP_ONLY (1 << 2) + +/// The define means the predefined gamut values . +///Driver uses to find entry in the table and apply appropriate gamut space. +#define ADL_GAMUT_SPACE_CCIR_709 (1 << 0) +#define ADL_GAMUT_SPACE_CCIR_601 (1 << 1) +#define ADL_GAMUT_SPACE_ADOBE_RGB (1 << 2) +#define ADL_GAMUT_SPACE_CIE_RGB (1 << 3) +#define ADL_GAMUT_SPACE_CUSTOM (1 << 4) +#define ADL_GAMUT_SPACE_CCIR_2020 (1 << 5) +#define ADL_GAMUT_SPACE_APPCTRL (1 << 6) + +/// Predefine white point values are structed similar to gamut . +#define ADL_WHITE_POINT_5000K (1 << 0) +#define ADL_WHITE_POINT_6500K (1 << 1) +#define ADL_WHITE_POINT_7500K (1 << 2) +#define ADL_WHITE_POINT_9300K (1 << 3) +#define ADL_WHITE_POINT_CUSTOM (1 << 4) + +///gamut and white point coordinates are from 0.0 -1.0 and divider is used to find the real value . +/// X float = X int /divider +#define ADL_GAMUT_WHITEPOINT_DIVIDER 10000 + +///gamma a0 coefficient uses the following divider: +#define ADL_REGAMMA_COEFFICIENT_A0_DIVIDER 10000000 +///gamma a1 ,a2,a3 coefficients use the following divider: +#define ADL_REGAMMA_COEFFICIENT_A1A2A3_DIVIDER 1000 + +///describes whether the coefficients are from EDID or custom user values. +#define ADL_EDID_REGAMMA_COEFFICIENTS (1 << 0) +///Used for struct ADLRegamma. Feature if set use gamma ramp, if missing use regamma coefficents +#define ADL_USE_GAMMA_RAMP (1 << 4) +///Used for struct ADLRegamma. If the gamma ramp flag is used then the driver could apply de gamma corretion to the supplied curve and this depends on this flag +#define ADL_APPLY_DEGAMMA (1 << 5) +///specifies that standard SRGB gamma should be applied +#define ADL_EDID_REGAMMA_PREDEFINED_SRGB (1 << 1) +///specifies that PQ gamma curve should be applied +#define ADL_EDID_REGAMMA_PREDEFINED_PQ (1 << 2) +///specifies that PQ gamma curve should be applied, lower max nits +#define ADL_EDID_REGAMMA_PREDEFINED_PQ_2084_INTERIM (1 << 3) +///specifies that 3.6 gamma should be applied +#define ADL_EDID_REGAMMA_PREDEFINED_36 (1 << 6) +///specifies that BT709 gama should be applied +#define ADL_EDID_REGAMMA_PREDEFINED_BT709 (1 << 7) +///specifies that regamma should be disabled, and application controls regamma content (of the whole screen) +#define ADL_EDID_REGAMMA_PREDEFINED_APPCTRL (1 << 8) + +/// @} + +/// \defgroup define_ddcinfo_pixelformats DDCInfo Pixel Formats +/// @{ +/// defines for iPanelPixelFormat in struct ADLDDCInfo2 +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_RGB656 0x00000001L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_RGB666 0x00000002L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_RGB888 0x00000004L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_RGB101010 0x00000008L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_RGB161616 0x00000010L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_RGB_RESERVED1 0x00000020L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_RGB_RESERVED2 0x00000040L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_RGB_RESERVED3 0x00000080L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_XRGB_BIAS101010 0x00000100L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_YCBCR444_8BPCC 0x00000200L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_YCBCR444_10BPCC 0x00000400L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_YCBCR444_12BPCC 0x00000800L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_YCBCR422_8BPCC 0x00001000L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_YCBCR422_10BPCC 0x00002000L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_YCBCR422_12BPCC 0x00004000L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_YCBCR420_8BPCC 0x00008000L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_YCBCR420_10BPCC 0x00010000L +#define ADL_DISPLAY_DDCINFO_PIXEL_FORMAT_YCBCR420_12BPCC 0x00020000L +/// @} + +/// \defgroup define_source_content_TF ADLSourceContentAttributes transfer functions (gamma) +/// @{ +/// defines for iTransferFunction in ADLSourceContentAttributes +#define ADL_TF_sRGB 0x0001 ///< sRGB +#define ADL_TF_BT709 0x0002 ///< BT.709 +#define ADL_TF_PQ2084 0x0004 ///< PQ2084 +#define ADL_TF_PQ2084_INTERIM 0x0008 ///< PQ2084-Interim +#define ADL_TF_LINEAR_0_1 0x0010 ///< Linear 0 - 1 +#define ADL_TF_LINEAR_0_125 0x0020 ///< Linear 0 - 125 +#define ADL_TF_DOLBYVISION 0x0040 ///< DolbyVision +#define ADL_TF_GAMMA_22 0x0080 ///< Plain 2.2 gamma curve +/// @} + +/// \defgroup define_source_content_CS ADLSourceContentAttributes color spaces +/// @{ +/// defines for iColorSpace in ADLSourceContentAttributes +#define ADL_CS_sRGB 0x0001 ///< sRGB +#define ADL_CS_BT601 0x0002 ///< BT.601 +#define ADL_CS_BT709 0x0004 ///< BT.709 +#define ADL_CS_BT2020 0x0008 ///< BT.2020 +#define ADL_CS_ADOBE 0x0010 ///< Adobe RGB +#define ADL_CS_P3 0x0020 ///< DCI-P3 +#define ADL_CS_scRGB_MS_REF 0x0040 ///< scRGB (MS Reference) +#define ADL_CS_DISPLAY_NATIVE 0x0080 ///< Display Native +#define ADL_CS_APP_CONTROL 0x0100 ///< Application Controlled +#define ADL_CS_DOLBYVISION 0x0200 ///< DolbyVision +/// @} + +/// \defgroup define_HDR_support ADLDDCInfo2 HDR support options +/// @{ +/// defines for iSupportedHDR in ADLDDCInfo2 +#define ADL_HDR_CEA861_3 0x0001 ///< HDR10/CEA861.3 HDR supported +#define ADL_HDR_DOLBYVISION 0x0002 ///< \deprecated DolbyVision HDR supported +#define ADL_HDR_FREESYNC_HDR 0x0004 ///< FreeSync HDR supported +/// @} + +/// \defgroup define_FreesyncFlags ADLDDCInfo2 Freesync HDR flags +/// @{ +/// defines for iFreesyncFlags in ADLDDCInfo2 +#define ADL_HDR_FREESYNC_BACKLIGHT_SUPPORT 0x0001 ///< Global backlight control supported +#define ADL_HDR_FREESYNC_LOCAL_DIMMING 0x0002 ///< Local dimming supported +/// @} + +/// \defgroup define_source_content_flags ADLSourceContentAttributes flags +/// @{ +/// defines for iFlags in ADLSourceContentAttributes +#define ADL_SCA_LOCAL_DIMMING_DISABLE 0x0001 ///< Disable local dimming +/// @} + +/// \defgroup define_dbd_state Deep Bit Depth +/// @{ + +/// defines for ADL_Workstation_DeepBitDepth_Get and ADL_Workstation_DeepBitDepth_Set functions +// This value indicates that the deep bit depth state is forced off +#define ADL_DEEPBITDEPTH_FORCEOFF 0 +/// This value indicates that the deep bit depth state is set to auto, the driver will automatically enable the +/// appropriate deep bit depth state depending on what connected display supports. +#define ADL_DEEPBITDEPTH_10BPP_AUTO 1 +/// This value indicates that the deep bit depth state is forced on to 10 bits per pixel, this is regardless if the display +/// supports 10 bpp. +#define ADL_DEEPBITDEPTH_10BPP_FORCEON 2 + +/// defines for ADLAdapterConfigMemory of ADL_Adapter_ConfigMemory_Get +/// If this bit is set, it indicates that the Deep Bit Depth pixel is set on the display +#define ADL_ADAPTER_CONFIGMEMORY_DBD (1 << 0) +/// If this bit is set, it indicates that the display is rotated (90, 180 or 270) +#define ADL_ADAPTER_CONFIGMEMORY_ROTATE (1 << 1) +/// If this bit is set, it indicates that passive stereo is set on the display +#define ADL_ADAPTER_CONFIGMEMORY_STEREO_PASSIVE (1 << 2) +/// If this bit is set, it indicates that the active stereo is set on the display +#define ADL_ADAPTER_CONFIGMEMORY_STEREO_ACTIVE (1 << 3) +/// If this bit is set, it indicates that the tear free vsync is set on the display +#define ADL_ADAPTER_CONFIGMEMORY_ENHANCEDVSYNC (1 << 4) +#define ADL_ADAPTER_CONFIGMEMORY_TEARFREEVSYNC (1 << 4) +/// @} + +/// \defgroup define_adl_validmemoryrequiredfields Memory Type +/// @{ + +/// This group defines memory types in ADLMemoryRequired struct \n +/// Indicates that this is the visible memory +#define ADL_MEMORYREQTYPE_VISIBLE (1 << 0) +/// Indicates that this is the invisible memory. +#define ADL_MEMORYREQTYPE_INVISIBLE (1 << 1) +/// Indicates that this is amount of visible memory per GPU that should be reserved for all other allocations. +#define ADL_MEMORYREQTYPE_GPURESERVEDVISIBLE (1 << 2) +/// @} + +/// \defgroup define_adapter_tear_free_status +/// Used in ADL_Adapter_TEAR_FREE_Set and ADL_Adapter_TFD_Get functions to indicate the tear free +/// desktop status. +/// @{ +/// Tear free desktop is enabled. +#define ADL_ADAPTER_TEAR_FREE_ON 1 +/// Tear free desktop can't be enabled due to a lack of graphic adapter memory. +#define ADL_ADAPTER_TEAR_FREE_NOTENOUGHMEM -1 +/// Tear free desktop can't be enabled due to quad buffer stereo being enabled. +#define ADL_ADAPTER_TEAR_FREE_OFF_ERR_QUADBUFFERSTEREO -2 +/// Tear free desktop can't be enabled due to MGPU-SLS being enabled. +#define ADL_ADAPTER_TEAR_FREE_OFF_ERR_MGPUSLD -3 +/// Tear free desktop is disabled. +#define ADL_ADAPTER_TEAR_FREE_OFF 0 +/// @} + +/// \defgroup define_adapter_crossdisplay_platforminfo +/// Used in ADL_Adapter_CrossDisplayPlatformInfo_Get function to indicate the Crossdisplay platform info. +/// @{ +/// CROSSDISPLAY platform. +#define ADL_CROSSDISPLAY_PLATFORM (1 << 0) +/// CROSSDISPLAY platform for Lasso station. +#define ADL_CROSSDISPLAY_PLATFORM_LASSO (1 << 1) +/// CROSSDISPLAY platform for docking station. +#define ADL_CROSSDISPLAY_PLATFORM_DOCKSTATION (1 << 2) +/// @} + +/// \defgroup define_adapter_crossdisplay_option +/// Used in ADL_Adapter_CrossdisplayInfoX2_Set function to indicate cross display options. +/// @{ +/// Checking if 3D application is runnning. If yes, not to do switch, return ADL_OK_WAIT; otherwise do switch. +#define ADL_CROSSDISPLAY_OPTION_NONE 0 +/// Force switching without checking for running 3D applications +#define ADL_CROSSDISPLAY_OPTION_FORCESWITCH (1 << 0) +/// @} + +/// \defgroup define_adapter_states Adapter Capabilities +/// These defines the capabilities supported by an adapter. It is used by \ref ADL_Adapter_ConfigureState_Get +/// @{ +/// Indicates that the adapter is headless (i.e. no displays can be connected to it) +#define ADL_ADAPTERCONFIGSTATE_HEADLESS ( 1 << 2 ) +/// Indicates that the adapter is configured to define the main rendering capabilities. For example, adapters +/// in Crossfire(TM) configuration, this bit would only be set on the adapter driving the display(s). +#define ADL_ADAPTERCONFIGSTATE_REQUISITE_RENDER ( 1 << 0 ) +/// Indicates that the adapter is configured to be used to unload some of the rendering work for a particular +/// requisite rendering adapter. For eample, for adapters in a Crossfire configuration, this bit would be set +/// on all adapters that are currently not driving the display(s) +#define ADL_ADAPTERCONFIGSTATE_ANCILLARY_RENDER ( 1 << 1 ) +/// Indicates that scatter gather feature enabled on the adapter +#define ADL_ADAPTERCONFIGSTATE_SCATTERGATHER ( 1 << 4 ) +/// @} + +/// \defgroup define_controllermode_ulModifiers +/// These defines the detailed actions supported by set viewport. It is used by \ref ADL_Display_ViewPort_Set +/// @{ +/// Indicate that the viewport set will change the view position +#define ADL_CONTROLLERMODE_CM_MODIFIER_VIEW_POSITION 0x00000001 +/// Indicate that the viewport set will change the view PanLock +#define ADL_CONTROLLERMODE_CM_MODIFIER_VIEW_PANLOCK 0x00000002 +/// Indicate that the viewport set will change the view size +#define ADL_CONTROLLERMODE_CM_MODIFIER_VIEW_SIZE 0x00000008 +/// @} + +/// \defgroup defines for Mirabilis +/// These defines are used for the Mirabilis feature +/// @{ +/// +/// Indicates the maximum number of audio sample rates +#define ADL_MAX_AUDIO_SAMPLE_RATE_COUNT 16 +/// @} + +/////////////////////////////////////////////////////////////////////////// +// ADLMultiChannelSplitStateFlag Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLMultiChannelSplitStateFlag +{ + ADLMultiChannelSplit_Unitialized = 0, + ADLMultiChannelSplit_Disabled = 1, + ADLMultiChannelSplit_Enabled = 2, + ADLMultiChannelSplit_SaveProfile = 3 +}; + +/////////////////////////////////////////////////////////////////////////// +// ADLSampleRate Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLSampleRate +{ + ADLSampleRate_32KHz =0, + ADLSampleRate_44P1KHz, + ADLSampleRate_48KHz, + ADLSampleRate_88P2KHz, + ADLSampleRate_96KHz, + ADLSampleRate_176P4KHz, + ADLSampleRate_192KHz, + ADLSampleRate_384KHz, //DP1.2 + ADLSampleRate_768KHz, //DP1.2 + ADLSampleRate_Undefined +}; + +/// \defgroup define_overdrive6_capabilities +/// These defines the capabilities supported by Overdrive 6. It is used by \ref ADL_Overdrive6_Capabilities_Get +/// @{ +/// Indicate that core (engine) clock can be changed. +#define ADL_OD6_CAPABILITY_SCLK_CUSTOMIZATION 0x00000001 +/// Indicate that memory clock can be changed. +#define ADL_OD6_CAPABILITY_MCLK_CUSTOMIZATION 0x00000002 +/// Indicate that graphics activity reporting is supported. +#define ADL_OD6_CAPABILITY_GPU_ACTIVITY_MONITOR 0x00000004 +/// Indicate that power limit can be customized. +#define ADL_OD6_CAPABILITY_POWER_CONTROL 0x00000008 +/// Indicate that SVI2 Voltage Control is supported. +#define ADL_OD6_CAPABILITY_VOLTAGE_CONTROL 0x00000010 +/// Indicate that OD6+ percentage adjustment is supported. +#define ADL_OD6_CAPABILITY_PERCENT_ADJUSTMENT 0x00000020 +/// Indicate that Thermal Limit Unlock is supported. +#define ADL_OD6_CAPABILITY_THERMAL_LIMIT_UNLOCK 0x00000040 +///Indicate that Fan speed needs to be displayed in RPM +#define ADL_OD6_CAPABILITY_FANSPEED_IN_RPM 0x00000080 +/// @} + +/// \defgroup define_overdrive6_supported_states +/// These defines the power states supported by Overdrive 6. It is used by \ref ADL_Overdrive6_Capabilities_Get +/// @{ +/// Indicate that overdrive is supported in the performance state. This is currently the only state supported. +#define ADL_OD6_SUPPORTEDSTATE_PERFORMANCE 0x00000001 +/// Do not use. Reserved for future use. +#define ADL_OD6_SUPPORTEDSTATE_POWER_SAVING 0x00000002 +/// @} + +/// \defgroup define_overdrive6_getstateinfo +/// These defines the power states to get information about. It is used by \ref ADL_Overdrive6_StateInfo_Get +/// @{ +/// Get default clocks for the performance state. +#define ADL_OD6_GETSTATEINFO_DEFAULT_PERFORMANCE 0x00000001 +/// Do not use. Reserved for future use. +#define ADL_OD6_GETSTATEINFO_DEFAULT_POWER_SAVING 0x00000002 +/// Get clocks for current state. Currently this is the same as \ref ADL_OD6_GETSTATEINFO_CUSTOM_PERFORMANCE +/// since only performance state is supported. +#define ADL_OD6_GETSTATEINFO_CURRENT 0x00000003 +/// Get the modified clocks (if any) for the performance state. If clocks were not modified +/// through Overdrive 6, then this will return the same clocks as \ref ADL_OD6_GETSTATEINFO_DEFAULT_PERFORMANCE. +#define ADL_OD6_GETSTATEINFO_CUSTOM_PERFORMANCE 0x00000004 +/// Do not use. Reserved for future use. +#define ADL_OD6_GETSTATEINFO_CUSTOM_POWER_SAVING 0x00000005 +/// @} + +/// \defgroup define_overdrive6_getstate and define_overdrive6_getmaxclockadjust +/// These defines the power states to get information about. It is used by \ref ADL_Overdrive6_StateEx_Get and \ref ADL_Overdrive6_MaxClockAdjust_Get +/// @{ +/// Get default clocks for the performance state. Only performance state is currently supported. +#define ADL_OD6_STATE_PERFORMANCE 0x00000001 +/// @} + +/// \defgroup define_overdrive6_setstate +/// These define which power state to set customized clocks on. It is used by \ref ADL_Overdrive6_State_Set +/// @{ +/// Set customized clocks for the performance state. +#define ADL_OD6_SETSTATE_PERFORMANCE 0x00000001 +/// Do not use. Reserved for future use. +#define ADL_OD6_SETSTATE_POWER_SAVING 0x00000002 +/// @} + +/// \defgroup define_overdrive6_thermalcontroller_caps +/// These defines the capabilities of the GPU thermal controller. It is used by \ref ADL_Overdrive6_ThermalController_Caps +/// @{ +/// GPU thermal controller is supported. +#define ADL_OD6_TCCAPS_THERMAL_CONTROLLER 0x00000001 +/// GPU fan speed control is supported. +#define ADL_OD6_TCCAPS_FANSPEED_CONTROL 0x00000002 +/// Fan speed percentage can be read. +#define ADL_OD6_TCCAPS_FANSPEED_PERCENT_READ 0x00000100 +/// Fan speed can be set by specifying a percentage value. +#define ADL_OD6_TCCAPS_FANSPEED_PERCENT_WRITE 0x00000200 +/// Fan speed RPM (revolutions-per-minute) can be read. +#define ADL_OD6_TCCAPS_FANSPEED_RPM_READ 0x00000400 +/// Fan speed can be set by specifying an RPM value. +#define ADL_OD6_TCCAPS_FANSPEED_RPM_WRITE 0x00000800 +/// @} + +/// \defgroup define_overdrive6_fanspeed_type +/// These defines the fan speed type being reported. It is used by \ref ADL_Overdrive6_FanSpeed_Get +/// @{ +/// Fan speed reported in percentage. +#define ADL_OD6_FANSPEED_TYPE_PERCENT 0x00000001 +/// Fan speed reported in RPM. +#define ADL_OD6_FANSPEED_TYPE_RPM 0x00000002 +/// Fan speed has been customized by the user, and fan is not running in automatic mode. +#define ADL_OD6_FANSPEED_USER_DEFINED 0x00000100 +/// @} + +/// \defgroup define_overdrive_EventCounter_type +/// These defines the EventCounter type being reported. It is used by \ref ADL2_OverdriveN_CountOfEvents_Get ,can be used on older OD version supported ASICs also. +/// @{ +#define ADL_ODN_EVENTCOUNTER_THERMAL 0 +#define ADL_ODN_EVENTCOUNTER_VPURECOVERY 1 +/// @} + +/////////////////////////////////////////////////////////////////////////// +// ADLODNControlType Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLODNControlType +{ + ODNControlType_Current = 0, + ODNControlType_Default, + ODNControlType_Auto, + ODNControlType_Manual +}; + +enum ADLODNDPMMaskType +{ + ADL_ODN_DPM_CLOCK = 1 << 0, + ADL_ODN_DPM_VDDC = 1 << 1, + ADL_ODN_DPM_MASK = 1 << 2, +}; + +//ODN features Bits for ADLODNCapabilitiesX2 +enum ADLODNFeatureControl +{ + ADL_ODN_SCLK_DPM = 1 << 0, + ADL_ODN_MCLK_DPM = 1 << 1, + ADL_ODN_SCLK_VDD = 1 << 2, + ADL_ODN_MCLK_VDD = 1 << 3, + ADL_ODN_FAN_SPEED_MIN = 1 << 4, + ADL_ODN_FAN_SPEED_TARGET = 1 << 5, + ADL_ODN_ACOUSTIC_LIMIT_SCLK = 1 << 6, + ADL_ODN_TEMPERATURE_FAN_MAX = 1 << 7, + ADL_ODN_TEMPERATURE_SYSTEM = 1 << 8, + ADL_ODN_POWER_LIMIT = 1 << 9, + ADL_ODN_SCLK_AUTO_LIMIT = 1 << 10, + ADL_ODN_MCLK_AUTO_LIMIT = 1 << 11, + ADL_ODN_SCLK_DPM_MASK_ENABLE = 1 << 12, + ADL_ODN_MCLK_DPM_MASK_ENABLE = 1 << 13, + ADL_ODN_MCLK_UNDERCLOCK_ENABLE = 1 << 14, + ADL_ODN_SCLK_DPM_THROTTLE_NOTIFY = 1 << 15, + ADL_ODN_POWER_UTILIZATION = 1 << 16, + ADL_ODN_PERF_TUNING_SLIDER = 1 << 17, + ADL_ODN_REMOVE_WATTMAN_PAGE = 1 << 31 // Internal Only +}; + +//If any new feature is added, PPLIB only needs to add ext feature ID and Item ID(Seeting ID). These IDs should match the drive defined in CWDDEPM.h +enum ADLODNExtFeatureControl +{ + ADL_ODN_EXT_FEATURE_MEMORY_TIMING_TUNE = 1 << 0, + ADL_ODN_EXT_FEATURE_FAN_ZERO_RPM_CONTROL = 1 << 1, + ADL_ODN_EXT_FEATURE_AUTO_UV_ENGINE = 1 << 2, //Auto under voltage + ADL_ODN_EXT_FEATURE_AUTO_OC_ENGINE = 1 << 3, //Auto OC Enine + ADL_ODN_EXT_FEATURE_AUTO_OC_MEMORY = 1 << 4, //Auto OC memory + ADL_ODN_EXT_FEATURE_FAN_CURVE = 1 << 5 //Fan curve + +}; + +//If any new feature is added, PPLIB only needs to add ext feature ID and Item ID(Seeting ID).These IDs should match the drive defined in CWDDEPM.h +enum ADLODNExtSettingId +{ + ADL_ODN_PARAMETER_AC_TIMING = 0, + ADL_ODN_PARAMETER_FAN_ZERO_RPM_CONTROL, + ADL_ODN_PARAMETER_AUTO_UV_ENGINE, + ADL_ODN_PARAMETER_AUTO_OC_ENGINE, + ADL_ODN_PARAMETER_AUTO_OC_MEMORY, + ADL_ODN_PARAMETER_FAN_CURVE_TEMPERATURE_1, + ADL_ODN_PARAMETER_FAN_CURVE_SPEED_1, + ADL_ODN_PARAMETER_FAN_CURVE_TEMPERATURE_2, + ADL_ODN_PARAMETER_FAN_CURVE_SPEED_2, + ADL_ODN_PARAMETER_FAN_CURVE_TEMPERATURE_3, + ADL_ODN_PARAMETER_FAN_CURVE_SPEED_3, + ADL_ODN_PARAMETER_FAN_CURVE_TEMPERATURE_4, + ADL_ODN_PARAMETER_FAN_CURVE_SPEED_4, + ADL_ODN_PARAMETER_FAN_CURVE_TEMPERATURE_5, + ADL_ODN_PARAMETER_FAN_CURVE_SPEED_5, + ADL_ODN_POWERGAUGE, + ODN_COUNT + +} ; + +//OD8 Capability features bits +enum ADLOD8FeatureControl +{ + ADL_OD8_GFXCLK_LIMITS = 1 << 0, + ADL_OD8_GFXCLK_CURVE = 1 << 1, + ADL_OD8_UCLK_MAX = 1 << 2, + ADL_OD8_POWER_LIMIT = 1 << 3, + ADL_OD8_ACOUSTIC_LIMIT_SCLK = 1 << 4, //FanMaximumRpm + ADL_OD8_FAN_SPEED_MIN = 1 << 5, //FanMinimumPwm + ADL_OD8_TEMPERATURE_FAN = 1 << 6, //FanTargetTemperature + ADL_OD8_TEMPERATURE_SYSTEM = 1 << 7, //MaxOpTemp + ADL_OD8_MEMORY_TIMING_TUNE = 1 << 8, + ADL_OD8_FAN_ZERO_RPM_CONTROL = 1 << 9 , + ADL_OD8_AUTO_UV_ENGINE = 1 << 10, //Auto under voltage + ADL_OD8_AUTO_OC_ENGINE = 1 << 11, //Auto overclock engine + ADL_OD8_AUTO_OC_MEMORY = 1 << 12, //Auto overclock memory + ADL_OD8_FAN_CURVE = 1 << 13, //Fan curve + ADL_OD8_WS_AUTO_FAN_ACOUSTIC_LIMIT = 1 << 14, //Workstation Manual Fan controller + ADL_OD8_GFXCLK_QUADRATIC_CURVE = 1 << 15, + ADL_OD8_OPTIMIZED_GPU_POWER_MODE = 1 << 16, + ADL_OD8_ODVOLTAGE_LIMIT = 1 << 17, + ADL_OD8_ADV_OC_LIMITS = 1 << 18, //Advanced OC limits. + ADL_OD8_PER_ZONE_GFX_VOLTAGE_OFFSET = 1 << 19, //Per Zone gfx voltage offset feature + ADL_OD8_AUTO_CURVE_OPTIMIZER = 1 << 20, //Auto per zone tuning. + ADL_OD8_GFX_VOLTAGE_LIMIT = 1 << 21, //Voltage limit slider + ADL_OD8_TDC_LIMIT = 1 << 22, //TDC slider + ADL_OD8_FULL_CONTROL_MODE = 1 << 23, //Full control + ADL_OD8_POWER_SAVING_FEATURE_CONTROL = 1 << 24, //Power saving feature control + ADL_OD8_POWER_GAUGE = 1 << 25 //Power Gauge +}; + + +typedef enum ADLOD8SettingId +{ + OD8_GFXCLK_FMAX = 0, + OD8_GFXCLK_FMIN, + OD8_GFXCLK_FREQ1, + OD8_GFXCLK_VOLTAGE1, + OD8_GFXCLK_FREQ2, + OD8_GFXCLK_VOLTAGE2, + OD8_GFXCLK_FREQ3, + OD8_GFXCLK_VOLTAGE3, + OD8_UCLK_FMAX, + OD8_POWER_PERCENTAGE, + OD8_FAN_MIN_SPEED, + OD8_FAN_ACOUSTIC_LIMIT, + OD8_FAN_TARGET_TEMP, + OD8_OPERATING_TEMP_MAX, + OD8_AC_TIMING, + OD8_FAN_ZERORPM_CONTROL, + OD8_AUTO_UV_ENGINE_CONTROL, + OD8_AUTO_OC_ENGINE_CONTROL, + OD8_AUTO_OC_MEMORY_CONTROL, + OD8_FAN_CURVE_TEMPERATURE_1, + OD8_FAN_CURVE_SPEED_1, + OD8_FAN_CURVE_TEMPERATURE_2, + OD8_FAN_CURVE_SPEED_2, + OD8_FAN_CURVE_TEMPERATURE_3, + OD8_FAN_CURVE_SPEED_3, + OD8_FAN_CURVE_TEMPERATURE_4, + OD8_FAN_CURVE_SPEED_4, + OD8_FAN_CURVE_TEMPERATURE_5, + OD8_FAN_CURVE_SPEED_5, + OD8_WS_FAN_AUTO_FAN_ACOUSTIC_LIMIT, + OD8_GFXCLK_CURVE_COEFFICIENT_A, // As part of the agreement with UI team, the min/max voltage limits for the + OD8_GFXCLK_CURVE_COEFFICIENT_B, // quadratic curve graph will be stored in the min and max limits of + OD8_GFXCLK_CURVE_COEFFICIENT_C, // coefficient a, b and c. A, b and c themselves do not have limits. + OD8_GFXCLK_CURVE_VFT_FMIN, + OD8_UCLK_FMIN, + OD8_FAN_ZERO_RPM_STOP_TEMPERATURE, + OD8_OPTIMZED_POWER_MODE, + OD8_OD_VOLTAGE,// RSX - voltage offset feature + OD8_ADV_OC_LIMITS_SETTING, + OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_1, + OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_2, + OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_3, + OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_4, + OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_5, + OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_6, + OD8_AUTO_CURVE_OPTIMIZER_SETTING, + OD8_GFX_VOLTAGE_LIMIT_SETTING, + OD8_TDC_PERCENTAGE, + OD8_FULL_CONTROL_MODE_SETTING, + OD8_IDLE_POWER_SAVING_FEATURE_CONTROL, + OD8_RUNTIME_POWER_SAVING_FEATURE_CONTROL, + OD8_POWER_GAUGE, + OD8_COUNT +} ADLOD8SettingId; + + +//Define Performance Metrics Log max sensors number +#define ADL_PMLOG_MAX_SENSORS 256 + +/// \deprecated Replaced with ADL_PMLOG_SENSORS +typedef enum ADLSensorType +{ + SENSOR_MAXTYPES = 0, + PMLOG_CLK_GFXCLK = 1, // Current graphic clock value in MHz + PMLOG_CLK_MEMCLK = 2, // Current memory clock value in MHz + PMLOG_CLK_SOCCLK = 3, + PMLOG_CLK_UVDCLK1 = 4, + PMLOG_CLK_UVDCLK2 = 5, + PMLOG_CLK_VCECLK = 6, + PMLOG_CLK_VCNCLK = 7, + PMLOG_TEMPERATURE_EDGE = 8, // Current edge of the die temperature value in C + PMLOG_TEMPERATURE_MEM = 9, + PMLOG_TEMPERATURE_VRVDDC = 10, + PMLOG_TEMPERATURE_VRMVDD = 11, + PMLOG_TEMPERATURE_LIQUID = 12, + PMLOG_TEMPERATURE_PLX = 13, + PMLOG_FAN_RPM = 14, // Current fan RPM value + PMLOG_FAN_PERCENTAGE = 15, // Current ratio of fan RPM and max RPM + PMLOG_SOC_VOLTAGE = 16, + PMLOG_SOC_POWER = 17, + PMLOG_SOC_CURRENT = 18, + PMLOG_INFO_ACTIVITY_GFX = 19, // Current graphic activity level in percentage + PMLOG_INFO_ACTIVITY_MEM = 20, // Current memory activity level in percentage + PMLOG_GFX_VOLTAGE = 21, // Current graphic voltage in mV + PMLOG_MEM_VOLTAGE = 22, + PMLOG_ASIC_POWER = 23, // Current ASIC power draw in Watt + PMLOG_TEMPERATURE_VRSOC = 24, + PMLOG_TEMPERATURE_VRMVDD0 = 25, + PMLOG_TEMPERATURE_VRMVDD1 = 26, + PMLOG_TEMPERATURE_HOTSPOT = 27, // Current center of the die temperature value in C + PMLOG_TEMPERATURE_GFX = 28, + PMLOG_TEMPERATURE_SOC = 29, + PMLOG_GFX_POWER = 30, + PMLOG_GFX_CURRENT = 31, + PMLOG_TEMPERATURE_CPU = 32, + PMLOG_CPU_POWER = 33, + PMLOG_CLK_CPUCLK = 34, + PMLOG_THROTTLER_STATUS = 35, // A bit map of GPU throttle information. If a bit is set, the bit represented type of thorttling occurred in the last metrics sampling period + PMLOG_CLK_VCN1CLK1 = 36, + PMLOG_CLK_VCN1CLK2 = 37, + PMLOG_SMART_POWERSHIFT_CPU = 38, + PMLOG_SMART_POWERSHIFT_DGPU = 39, + PMLOG_BUS_SPEED = 40, // Current PCIE bus speed running + PMLOG_BUS_LANES = 41, // Current PCIE bus lanes using + PMLOG_TEMPERATURE_LIQUID0 = 42, + PMLOG_TEMPERATURE_LIQUID1 = 43, + PMLOG_CLK_FCLK = 44, + PMLOG_THROTTLER_STATUS_CPU = 45, + PMLOG_SSPAIRED_ASICPOWER = 46, // apuPower + PMLOG_SSTOTAL_POWERLIMIT = 47, // Total Power limit + PMLOG_SSAPU_POWERLIMIT = 48, // APU Power limit + PMLOG_SSDGPU_POWERLIMIT = 49, // DGPU Power limit + PMLOG_TEMPERATURE_HOTSPOT_GCD = 50, + PMLOG_TEMPERATURE_HOTSPOT_MCD = 51, + PMLOG_THROTTLER_TEMP_EDGE_PERCENTAGE = 52, + PMLOG_THROTTLER_TEMP_HOTSPOT_PERCENTAGE = 53, + PMLOG_THROTTLER_TEMP_HOTSPOT_GCD_PERCENTAGE = 54, + PMLOG_THROTTLER_TEMP_HOTSPOT_MCD_PERCENTAGE = 55, + PMLOG_THROTTLER_TEMP_MEM_PERCENTAGE = 56, + PMLOG_THROTTLER_TEMP_VR_GFX_PERCENTAGE = 57, + PMLOG_THROTTLER_TEMP_VR_MEM0_PERCENTAGE = 58, + PMLOG_THROTTLER_TEMP_VR_MEM1_PERCENTAGE = 59, + PMLOG_THROTTLER_TEMP_VR_SOC_PERCENTAGE = 60, + PMLOG_THROTTLER_TEMP_LIQUID0_PERCENTAGE = 61, + PMLOG_THROTTLER_TEMP_LIQUID1_PERCENTAGE = 62, + PMLOG_THROTTLER_TEMP_PLX_PERCENTAGE = 63, + PMLOG_THROTTLER_TDC_GFX_PERCENTAGE = 64, + PMLOG_THROTTLER_TDC_SOC_PERCENTAGE = 65, + PMLOG_THROTTLER_TDC_USR_PERCENTAGE = 66, + PMLOG_THROTTLER_PPT0_PERCENTAGE = 67, + PMLOG_THROTTLER_PPT1_PERCENTAGE = 68, + PMLOG_THROTTLER_PPT2_PERCENTAGE = 69, + PMLOG_THROTTLER_PPT3_PERCENTAGE = 70, + PMLOG_THROTTLER_FIT_PERCENTAGE = 71, + PMLOG_THROTTLER_GFX_APCC_PLUS_PERCENTAGE = 72, + PMLOG_BOARD_POWER = 73, + PMLOG_MAX_SENSORS_REAL +} ADLSensorType; + + +//Throttle Status +typedef enum ADL_THROTTLE_NOTIFICATION +{ + ADL_PMLOG_THROTTLE_POWER = 1 << 0, + ADL_PMLOG_THROTTLE_THERMAL = 1 << 1, + ADL_PMLOG_THROTTLE_CURRENT = 1 << 2, +} ADL_THROTTLE_NOTIFICATION; + +typedef enum ADL_PMLOG_SENSORS +{ + ADL_SENSOR_MAXTYPES = 0, + ADL_PMLOG_CLK_GFXCLK = 1, + ADL_PMLOG_CLK_MEMCLK = 2, + ADL_PMLOG_CLK_SOCCLK = 3, + ADL_PMLOG_CLK_UVDCLK1 = 4, + ADL_PMLOG_CLK_UVDCLK2 = 5, + ADL_PMLOG_CLK_VCECLK = 6, + ADL_PMLOG_CLK_VCNCLK = 7, + ADL_PMLOG_TEMPERATURE_EDGE = 8, + ADL_PMLOG_TEMPERATURE_MEM = 9, + ADL_PMLOG_TEMPERATURE_VRVDDC = 10, + ADL_PMLOG_TEMPERATURE_VRMVDD = 11, + ADL_PMLOG_TEMPERATURE_LIQUID = 12, + ADL_PMLOG_TEMPERATURE_PLX = 13, + ADL_PMLOG_FAN_RPM = 14, + ADL_PMLOG_FAN_PERCENTAGE = 15, + ADL_PMLOG_SOC_VOLTAGE = 16, + ADL_PMLOG_SOC_POWER = 17, + ADL_PMLOG_SOC_CURRENT = 18, + ADL_PMLOG_INFO_ACTIVITY_GFX = 19, + ADL_PMLOG_INFO_ACTIVITY_MEM = 20, + ADL_PMLOG_GFX_VOLTAGE = 21, + ADL_PMLOG_MEM_VOLTAGE = 22, + ADL_PMLOG_ASIC_POWER = 23, + ADL_PMLOG_TEMPERATURE_VRSOC = 24, + ADL_PMLOG_TEMPERATURE_VRMVDD0 = 25, + ADL_PMLOG_TEMPERATURE_VRMVDD1 = 26, + ADL_PMLOG_TEMPERATURE_HOTSPOT = 27, + ADL_PMLOG_TEMPERATURE_GFX = 28, + ADL_PMLOG_TEMPERATURE_SOC = 29, + ADL_PMLOG_GFX_POWER = 30, + ADL_PMLOG_GFX_CURRENT = 31, + ADL_PMLOG_TEMPERATURE_CPU = 32, + ADL_PMLOG_CPU_POWER = 33, + ADL_PMLOG_CLK_CPUCLK = 34, + ADL_PMLOG_THROTTLER_STATUS = 35, // GFX + ADL_PMLOG_CLK_VCN1CLK1 = 36, + ADL_PMLOG_CLK_VCN1CLK2 = 37, + ADL_PMLOG_SMART_POWERSHIFT_CPU = 38, + ADL_PMLOG_SMART_POWERSHIFT_DGPU = 39, + ADL_PMLOG_BUS_SPEED = 40, + ADL_PMLOG_BUS_LANES = 41, + ADL_PMLOG_TEMPERATURE_LIQUID0 = 42, + ADL_PMLOG_TEMPERATURE_LIQUID1 = 43, + ADL_PMLOG_CLK_FCLK = 44, + ADL_PMLOG_THROTTLER_STATUS_CPU = 45, + ADL_PMLOG_SSPAIRED_ASICPOWER = 46, // apuPower + ADL_PMLOG_SSTOTAL_POWERLIMIT = 47, // Total Power limit + ADL_PMLOG_SSAPU_POWERLIMIT = 48, // APU Power limit + ADL_PMLOG_SSDGPU_POWERLIMIT = 49, // DGPU Power limit + ADL_PMLOG_TEMPERATURE_HOTSPOT_GCD = 50, + ADL_PMLOG_TEMPERATURE_HOTSPOT_MCD = 51, + ADL_PMLOG_THROTTLER_TEMP_EDGE_PERCENTAGE = 52, + ADL_PMLOG_THROTTLER_TEMP_HOTSPOT_PERCENTAGE = 53, + ADL_PMLOG_THROTTLER_TEMP_HOTSPOT_GCD_PERCENTAGE = 54, + ADL_PMLOG_THROTTLER_TEMP_HOTSPOT_MCD_PERCENTAGE = 55, + ADL_PMLOG_THROTTLER_TEMP_MEM_PERCENTAGE = 56, + ADL_PMLOG_THROTTLER_TEMP_VR_GFX_PERCENTAGE = 57, + ADL_PMLOG_THROTTLER_TEMP_VR_MEM0_PERCENTAGE = 58, + ADL_PMLOG_THROTTLER_TEMP_VR_MEM1_PERCENTAGE = 59, + ADL_PMLOG_THROTTLER_TEMP_VR_SOC_PERCENTAGE = 60, + ADL_PMLOG_THROTTLER_TEMP_LIQUID0_PERCENTAGE = 61, + ADL_PMLOG_THROTTLER_TEMP_LIQUID1_PERCENTAGE = 62, + ADL_PMLOG_THROTTLER_TEMP_PLX_PERCENTAGE = 63, + ADL_PMLOG_THROTTLER_TDC_GFX_PERCENTAGE = 64, + ADL_PMLOG_THROTTLER_TDC_SOC_PERCENTAGE = 65, + ADL_PMLOG_THROTTLER_TDC_USR_PERCENTAGE = 66, + ADL_PMLOG_THROTTLER_PPT0_PERCENTAGE = 67, + ADL_PMLOG_THROTTLER_PPT1_PERCENTAGE = 68, + ADL_PMLOG_THROTTLER_PPT2_PERCENTAGE = 69, + ADL_PMLOG_THROTTLER_PPT3_PERCENTAGE = 70, + ADL_PMLOG_THROTTLER_FIT_PERCENTAGE = 71, + ADL_PMLOG_THROTTLER_GFX_APCC_PLUS_PERCENTAGE = 72, + ADL_PMLOG_BOARD_POWER = 73, + ADL_PMLOG_MAX_SENSORS_REAL +} ADL_PMLOG_SENSORS; + +/// \defgroup define_ecc_mode_states +/// These defines the ECC(Error Correction Code) state. It is used by \ref ADL_Workstation_ECC_Get,ADL_Workstation_ECC_Set +/// @{ +/// Error Correction is OFF. +#define ECC_MODE_OFF 0 +/// Error Correction is ECCV2. +#define ECC_MODE_ON 2 +/// Error Correction is HBM. +#define ECC_MODE_HBM 3 +/// @} + +/// \defgroup define_board_layout_flags +/// These defines are the board layout flags state which indicates what are the valid properties of \ref ADLBoardLayoutInfo . It is used by \ref ADL_Adapter_BoardLayout_Get +/// @{ +/// Indicates the number of slots is valid. +#define ADL_BLAYOUT_VALID_NUMBER_OF_SLOTS 0x1 +/// Indicates the slot sizes are valid. Size of the slot consists of the length and width. +#define ADL_BLAYOUT_VALID_SLOT_SIZES 0x2 +/// Indicates the connector offsets are valid. +#define ADL_BLAYOUT_VALID_CONNECTOR_OFFSETS 0x4 +/// Indicates the connector lengths is valid. +#define ADL_BLAYOUT_VALID_CONNECTOR_LENGTHS 0x8 +/// @} + +/// \defgroup define_max_constants +/// These defines are the maximum value constants. +/// @{ +/// Indicates the Maximum supported slots on board. +#define ADL_ADAPTER_MAX_SLOTS 4 +/// Indicates the Maximum supported connectors on slot. +#define ADL_ADAPTER_MAX_CONNECTORS 10 +/// Indicates the Maximum supported properties of connection +#define ADL_MAX_CONNECTION_TYPES 32 +/// Indicates the Maximum relative address link count. +#define ADL_MAX_RELATIVE_ADDRESS_LINK_COUNT 15 +/// Indicates the Maximum size of EDID data block size +#define ADL_MAX_DISPLAY_EDID_DATA_SIZE 1024 +/// Indicates the Maximum count of Error Records. +#define ADL_MAX_ERROR_RECORDS_COUNT 256 +/// Indicates the maximum number of power states supported +#define ADL_MAX_POWER_POLICY 6 +/// @} + +/// \defgroup define_connection_types +/// These defines are the connection types constants which indicates what are the valid connection type of given connector. It is used by \ref ADL_Adapter_SupportedConnections_Get +/// @{ +/// Indicates the VGA connection type is valid. +#define ADL_CONNECTION_TYPE_VGA 0 +/// Indicates the DVI_I connection type is valid. +#define ADL_CONNECTION_TYPE_DVI 1 +/// Indicates the DVI_SL connection type is valid. +#define ADL_CONNECTION_TYPE_DVI_SL 2 +/// Indicates the HDMI connection type is valid. +#define ADL_CONNECTION_TYPE_HDMI 3 +/// Indicates the DISPLAY PORT connection type is valid. +#define ADL_CONNECTION_TYPE_DISPLAY_PORT 4 +/// Indicates the Active dongle DP->DVI(single link) connection type is valid. +#define ADL_CONNECTION_TYPE_ACTIVE_DONGLE_DP_DVI_SL 5 +/// Indicates the Active dongle DP->DVI(double link) connection type is valid. +#define ADL_CONNECTION_TYPE_ACTIVE_DONGLE_DP_DVI_DL 6 +/// Indicates the Active dongle DP->HDMI connection type is valid. +#define ADL_CONNECTION_TYPE_ACTIVE_DONGLE_DP_HDMI 7 +/// Indicates the Active dongle DP->VGA connection type is valid. +#define ADL_CONNECTION_TYPE_ACTIVE_DONGLE_DP_VGA 8 +/// Indicates the Passive dongle DP->HDMI connection type is valid. +#define ADL_CONNECTION_TYPE_PASSIVE_DONGLE_DP_HDMI 9 +/// Indicates the Active dongle DP->VGA connection type is valid. +#define ADL_CONNECTION_TYPE_PASSIVE_DONGLE_DP_DVI 10 +/// Indicates the MST type is valid. +#define ADL_CONNECTION_TYPE_MST 11 +/// Indicates the active dongle, all types. +#define ADL_CONNECTION_TYPE_ACTIVE_DONGLE 12 +/// Indicates the Virtual Connection Type. +#define ADL_CONNECTION_TYPE_VIRTUAL 13 +/// Macros for generating bitmask from index. +#define ADL_CONNECTION_BITMAST_FROM_INDEX(index) (1 << index) +/// @} + +/// \defgroup define_connection_properties +/// These defines are the connection properties which indicates what are the valid properties of given connection type. It is used by \ref ADL_Adapter_SupportedConnections_Get +/// @{ +/// Indicates the property Bitrate is valid. +#define ADL_CONNECTION_PROPERTY_BITRATE 0x1 +/// Indicates the property number of lanes is valid. +#define ADL_CONNECTION_PROPERTY_NUMBER_OF_LANES 0x2 +/// Indicates the property 3D caps is valid. +#define ADL_CONNECTION_PROPERTY_3DCAPS 0x4 +/// Indicates the property output bandwidth is valid. +#define ADL_CONNECTION_PROPERTY_OUTPUT_BANDWIDTH 0x8 +/// Indicates the property colordepth is valid. +#define ADL_CONNECTION_PROPERTY_COLORDEPTH 0x10 +/// @} + +/// \defgroup define_lanecount_constants +/// These defines are the Lane count constants which will be used in DP & etc. +/// @{ +/// Indicates if lane count is unknown +#define ADL_LANECOUNT_UNKNOWN 0 +/// Indicates if lane count is 1 +#define ADL_LANECOUNT_ONE 1 +/// Indicates if lane count is 2 +#define ADL_LANECOUNT_TWO 2 +/// Indicates if lane count is 4 +#define ADL_LANECOUNT_FOUR 4 +/// Indicates if lane count is 8 +#define ADL_LANECOUNT_EIGHT 8 +/// Indicates default value of lane count +#define ADL_LANECOUNT_DEF ADL_LANECOUNT_FOUR +/// @} + +/// \defgroup define_linkrate_constants +/// These defines are the link rate constants which will be used in DP & etc. +/// @{ +/// Indicates if link rate is unknown +#define ADL_LINK_BITRATE_UNKNOWN 0 +/// Indicates if link rate is 1.62Ghz +#define ADL_LINK_BITRATE_1_62_GHZ 0x06 +/// Indicates if link rate is 2.7Ghz +#define ADL_LINK_BITRATE_2_7_GHZ 0x0A +/// Indicates if link rate is 5.4Ghz +#define ADL_LINK_BITRATE_5_4_GHZ 0x14 + +/// Indicates if link rate is 8.1Ghz +#define ADL_LINK_BITRATE_8_1_GHZ 0x1E +/// Indicates default value of link rate +#define ADL_LINK_BITRATE_DEF ADL_LINK_BITRATE_2_7_GHZ +/// @} + +/// \defgroup define_colordepth_constants +/// These defines are the color depth constants which will be used in DP & etc. +/// @{ +#define ADL_CONNPROP_S3D_ALTERNATE_TO_FRAME_PACK 0x00000001 +/// @} + + +/// \defgroup define_colordepth_constants +/// These defines are the color depth constants which will be used in DP & etc. +/// @{ +/// Indicates if color depth is unknown +#define ADL_COLORDEPTH_UNKNOWN 0 +/// Indicates if color depth is 666 +#define ADL_COLORDEPTH_666 1 +/// Indicates if color depth is 888 +#define ADL_COLORDEPTH_888 2 +/// Indicates if color depth is 101010 +#define ADL_COLORDEPTH_101010 3 +/// Indicates if color depth is 121212 +#define ADL_COLORDEPTH_121212 4 +/// Indicates if color depth is 141414 +#define ADL_COLORDEPTH_141414 5 +/// Indicates if color depth is 161616 +#define ADL_COLORDEPTH_161616 6 +/// Indicates default value of color depth +#define ADL_COLOR_DEPTH_DEF ADL_COLORDEPTH_888 +/// @} + + +/// \defgroup define_emulation_status +/// These defines are the status of emulation +/// @{ +/// Indicates if real device is connected. +#define ADL_EMUL_STATUS_REAL_DEVICE_CONNECTED 0x1 +/// Indicates if emulated device is presented. +#define ADL_EMUL_STATUS_EMULATED_DEVICE_PRESENT 0x2 +/// Indicates if emulated device is used. +#define ADL_EMUL_STATUS_EMULATED_DEVICE_USED 0x4 +/// In case when last active real/emulated device used (when persistence is enabled but no emulation enforced then persistence will use last connected/emulated device). +#define ADL_EMUL_STATUS_LAST_ACTIVE_DEVICE_USED 0x8 +/// @} + +/// \defgroup define_emulation_mode +/// These defines are the modes of emulation +/// @{ +/// Indicates if no emulation is used +#define ADL_EMUL_MODE_OFF 0 +/// Indicates if emulation is used when display connected +#define ADL_EMUL_MODE_ON_CONNECTED 1 +/// Indicates if emulation is used when display dis connected +#define ADL_EMUL_MODE_ON_DISCONNECTED 2 +/// Indicates if emulation is used always +#define ADL_EMUL_MODE_ALWAYS 3 +/// @} + +/// \defgroup define_emulation_query +/// These defines are the modes of emulation +/// @{ +/// Indicates Data from real device +#define ADL_QUERY_REAL_DATA 0 +/// Indicates Emulated data +#define ADL_QUERY_EMULATED_DATA 1 +/// Indicates Data currently in use +#define ADL_QUERY_CURRENT_DATA 2 +/// @} + +/// \defgroup define_persistence_state +/// These defines are the states of persistence +/// @{ +/// Indicates persistence is disabled +#define ADL_EDID_PERSISTANCE_DISABLED 0 +/// Indicates persistence is enabled +#define ADL_EDID_PERSISTANCE_ENABLED 1 +/// @} + +/// \defgroup define_connector_types Connector Type +/// defines for ADLConnectorInfo.iType +/// @{ +/// Indicates unknown Connector type +#define ADL_CONNECTOR_TYPE_UNKNOWN 0 +/// Indicates VGA Connector type +#define ADL_CONNECTOR_TYPE_VGA 1 +/// Indicates DVI-D Connector type +#define ADL_CONNECTOR_TYPE_DVI_D 2 +/// Indicates DVI-I Connector type +#define ADL_CONNECTOR_TYPE_DVI_I 3 +/// Indicates Active Dongle-NA Connector type +#define ADL_CONNECTOR_TYPE_ATICVDONGLE_NA 4 +/// Indicates Active Dongle-JP Connector type +#define ADL_CONNECTOR_TYPE_ATICVDONGLE_JP 5 +/// Indicates Active Dongle-NONI2C Connector type +#define ADL_CONNECTOR_TYPE_ATICVDONGLE_NONI2C 6 +/// Indicates Active Dongle-NONI2C-D Connector type +#define ADL_CONNECTOR_TYPE_ATICVDONGLE_NONI2C_D 7 +/// Indicates HDMI-Type A Connector type +#define ADL_CONNECTOR_TYPE_HDMI_TYPE_A 8 +/// Indicates HDMI-Type B Connector type +#define ADL_CONNECTOR_TYPE_HDMI_TYPE_B 9 +/// Indicates Display port Connector type +#define ADL_CONNECTOR_TYPE_DISPLAYPORT 10 +/// Indicates EDP Connector type +#define ADL_CONNECTOR_TYPE_EDP 11 +/// Indicates MiniDP Connector type +#define ADL_CONNECTOR_TYPE_MINI_DISPLAYPORT 12 +/// Indicates Virtual Connector type +#define ADL_CONNECTOR_TYPE_VIRTUAL 13 +/// Indicates USB type C Connector type +#define ADL_CONNECTOR_TYPE_USB_TYPE_C 14 +/// @} + +/// \defgroup define_freesync_usecase +/// These defines are to specify use cases in which FreeSync should be enabled +/// They are a bit mask. To specify FreeSync for more than one use case, the input value +/// should be set to include multiple bits set +/// @{ +/// Indicates FreeSync is enabled for Static Screen case +#define ADL_FREESYNC_USECASE_STATIC 0x1 +/// Indicates FreeSync is enabled for Video use case +#define ADL_FREESYNC_USECASE_VIDEO 0x2 +/// Indicates FreeSync is enabled for Gaming use case +#define ADL_FREESYNC_USECASE_GAMING 0x4 +/// @} + +/// \defgroup define_freesync_caps +/// These defines are used to retrieve FreeSync display capabilities. +/// GPU support flag also indicates whether the display is +/// connected to a GPU that actually supports FreeSync +/// @{ +#define ADL_FREESYNC_CAP_SUPPORTED (1 << 0) +#define ADL_FREESYNC_CAP_GPUSUPPORTED (1 << 1) +#define ADL_FREESYNC_CAP_DISPLAYSUPPORTED (1 << 2) +#define ADL_FREESYNC_CAP_CURRENTMODESUPPORTED (1 << 3) +#define ADL_FREESYNC_CAP_NOCFXORCFXSUPPORTED (1 << 4) +#define ADL_FREESYNC_CAP_NOGENLOCKORGENLOCKSUPPORTED (1 << 5) +#define ADL_FREESYNC_CAP_BORDERLESSWINDOWSUPPORTED (1 << 6) +/// @} + +/// \defgroup define_freesync_labelIndex +/// These defines are used to retrieve which FreeSync label to use +/// @{ +#define ADL_FREESYNC_LABEL_UNSUPPORTED 0 +#define ADL_FREESYNC_LABEL_FREESYNC 1 +#define ADL_FREESYNC_LABEL_ADAPTIVE_SYNC 2 +#define ADL_FREESYNC_LABEL_VRR 3 +#define ADL_FREESYNC_LABEL_FREESYNC_PREMIUM 4 +#define ADL_FREESYNC_LABEL_FREESYNC_PREMIUM_PRO 5 +/// @} + +/// Freesync Power optimization masks +/// @{ +#define ADL_FREESYNC_POWEROPTIMIZATION_SUPPORTED_MASK (1 << 0) +#define ADL_FREESYNC_POWEROPTIMIZATION_ENABLED_MASK (1 << 1) +#define ADL_FREESYNC_POWEROPTIMIZATION_DEFAULT_VALUE_MASK (1 << 2) +/// @} + +/// \defgroup define_MST_CommandLine_execute +/// @{ +/// Indicates the MST command line for branch message if the bit is set. Otherwise, it is display message +#define ADL_MST_COMMANDLINE_PATH_MSG 0x1 +/// Indicates the MST command line to send message in broadcast way it the bit is set +#define ADL_MST_COMMANDLINE_BROADCAST 0x2 + +/// @} + + +/// \defgroup define_Adapter_CloneTypes_Get +/// @{ +/// Indicates there is crossGPU clone with non-AMD dispalys +#define ADL_CROSSGPUDISPLAYCLONE_AMD_WITH_NONAMD 0x1 +/// Indicates there is crossGPU clone +#define ADL_CROSSGPUDISPLAYCLONE 0x2 + +/// @} + +/// \defgroup define_D3DKMT_HANDLE +/// @{ +/// Handle can be used to create Device Handle when using CreateDevice() +typedef unsigned int ADL_D3DKMT_HANDLE; +/// @} + + +// End Bracket for Constants and Definitions. Add new groups ABOVE this line! + +/// @} + + +typedef enum ADL_RAS_ERROR_INJECTION_MODE +{ + ADL_RAS_ERROR_INJECTION_MODE_SINGLE = 1, + ADL_RAS_ERROR_INJECTION_MODE_MULTIPLE = 2 +}ADL_RAS_ERROR_INJECTION_MODE; + + +typedef enum ADL_RAS_BLOCK_ID +{ + ADL_RAS_BLOCK_ID_UMC = 0, + ADL_RAS_BLOCK_ID_SDMA, + ADL_RAS_BLOCK_ID_GFX_HUB, + ADL_RAS_BLOCK_ID_MMHUB, + ADL_RAS_BLOCK_ID_ATHUB, + ADL_RAS_BLOCK_ID_PCIE_BIF, + ADL_RAS_BLOCK_ID_HDP, + ADL_RAS_BLOCK_ID_XGMI_WAFL, + ADL_RAS_BLOCK_ID_DF, + ADL_RAS_BLOCK_ID_SMN, + ADL_RAS_BLOCK_ID_SEM, + ADL_RAS_BLOCK_ID_MP0, + ADL_RAS_BLOCK_ID_MP1, + ADL_RAS_BLOCK_ID_FUSE +}ADL_RAS_BLOCK_ID; + +typedef enum ADL_MEM_SUB_BLOCK_ID +{ + ADL_RAS__UMC_HBM = 0, + ADL_RAS__UMC_SRAM = 1 +}ADL_MEM_SUB_BLOCK_ID; + +typedef enum _ADL_RAS_ERROR_TYPE +{ + ADL_RAS_ERROR__NONE = 0, + ADL_RAS_ERROR__PARITY = 1, + ADL_RAS_ERROR__SINGLE_CORRECTABLE = 2, + ADL_RAS_ERROR__PARITY_SINGLE_CORRECTABLE = 3, + ADL_RAS_ERROR__MULTI_UNCORRECTABLE = 4, + ADL_RAS_ERROR__PARITY_MULTI_UNCORRECTABLE = 5, + ADL_RAS_ERROR__SINGLE_CORRECTABLE_MULTI_UNCORRECTABLE = 6, + ADL_RAS_ERROR__PARITY_SINGLE_CORRECTABLE_MULTI_UNCORRECTABLE = 7, + ADL_RAS_ERROR__POISON = 8, + ADL_RAS_ERROR__PARITY_POISON = 9, + ADL_RAS_ERROR__SINGLE_CORRECTABLE_POISON = 10, + ADL_RAS_ERROR__PARITY_SINGLE_CORRECTABLE_POISON = 11, + ADL_RAS_ERROR__MULTI_UNCORRECTABLE_POISON = 12, + ADL_RAS_ERROR__PARITY_MULTI_UNCORRECTABLE_POISON = 13, + ADL_RAS_ERROR__SINGLE_CORRECTABLE_MULTI_UNCORRECTABLE_POISON = 14, + ADL_RAS_ERROR__PARITY_SINGLE_CORRECTABLE_MULTI_UNCORRECTABLE_POISON = 15 +}ADL_RAS_ERROR_TYPE; + +typedef enum ADL_RAS_INJECTION_METHOD +{ + ADL_RAS_ERROR__UMC_METH_COHERENT = 0, + ADL_RAS_ERROR__UMC_METH_SINGLE_SHOT = 1, + ADL_RAS_ERROR__UMC_METH_PERSISTENT = 2, + ADL_RAS_ERROR__UMC_METH_PERSISTENT_DISABLE = 3 +}ADL_RAS_INJECTION_METHOD; + +// Driver event types +typedef enum ADL_DRIVER_EVENT_TYPE +{ + ADL_EVENT_ID_AUTO_FEATURE_COMPLETED = 30, + ADL_EVENT_ID_FEATURE_AVAILABILITY = 31, + +} ADL_DRIVER_EVENT_TYPE; + + +//UIFeature Ids +typedef enum ADL_UIFEATURES_GROUP +{ + ADL_UIFEATURES_GROUP_DVR = 0, + ADL_UIFEATURES_GROUP_TURBOSYNC = 1, + ADL_UIFEATURES_GROUP_FRAMEMETRICSMONITOR = 2, + ADL_UIFEATURES_GROUP_FRTC = 3, + ADL_UIFEATURES_GROUP_XVISION = 4, + ADL_UIFEATURES_GROUP_BLOCKCHAIN = 5, + ADL_UIFEATURES_GROUP_GAMEINTELLIGENCE = 6, + ADL_UIFEATURES_GROUP_CHILL = 7, + ADL_UIFEATURES_GROUP_DELAG = 8, + ADL_UIFEATURES_GROUP_BOOST = 9, + ADL_UIFEATURES_GROUP_USU = 10, + ADL_UIFEATURES_GROUP_XGMI = 11, + ADL_UIFEATURES_GROUP_PROVSR = 12, + ADL_UIFEATURES_GROUP_SMA = 13, + ADL_UIFEATURES_GROUP_CAMERA = 14, + ADL_UIFEATURES_GROUP_FRTCPRO = 15 +} ADL_UIFEATURES_GROUP; + + + +/// Maximum brightness supported by Radeon LED interface +#define ADL_RADEON_LED_MAX_BRIGHTNESS 2 + +/// Maximum speed supported by Radeon LED interface +#define ADL_RADEON_LED_MAX_SPEED 4 + +/// Maximum RGB supported by Radeon LED interface +#define ADL_RADEON_LED_MAX_RGB 255 + +/// Maximum MORSE code supported string +#define ADL_RADEON_LED_MAX_MORSE_CODE 260 + +/// Maximum LED ROW ON GRID +#define ADL_RADEON_LED_MAX_LED_ROW_ON_GRID 7 + +/// Maximum LED COLUMN ON GRID +#define ADL_RADEON_LED_MAX_LED_COLUMN_ON_GRID 24 + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief +/// +/// +/// +/// +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef enum ADL_RADEON_USB_LED_BAR_CONTROLS +{ + RadeonLEDBarControl_OFF = 0, + RadeonLEDBarControl_Static, + RadeonLEDBarControl_Rainbow, + RadeonLEDBarControl_Swirl, + RadeonLEDBarControl_Chase, + RadeonLEDBarControl_Bounce, + RadeonLEDBarControl_MorseCode, + RadeonLEDBarControl_ColorCycle, + RadeonLEDBarControl_Breathing, + RadeonLEDBarControl_CustomPattern, + RadeonLEDBarControl_MAX +}ADL_RADEON_USB_LED_BAR_CONTROLS; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief +/// +/// +/// +/// +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef unsigned int RadeonLEDBARSupportedControl; + + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief +/// +/// +/// +/// +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef enum ADL_RADEON_USB_LED_CONTROL_CONFIGS +{ + RadeonLEDPattern_Speed = 0, + RadeonLEDPattern_Brightness, + RadeonLEDPattern_Direction, + RadeonLEDPattern_Color, + RadeonLEDPattern_MAX +}ADL_RADEON_USB_LED_CONTROL_CONFIGS; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief +/// +/// +/// +/// +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef unsigned int RadeonLEDBARSupportedConfig; + +//User blob feature settings +typedef enum ADL_USER_SETTINGS +{ + ADL_USER_SETTINGS_ENHANCEDSYNC = 1 << 0, //notify Enhanced Sync settings change + ADL_USER_SETTINGS_CHILL_PROFILE = 1 << 1, //notify Chill settings change + ADL_USER_SETTINGS_DELAG_PROFILE = 1 << 2, //notify Delag settings change + ADL_USER_SETTINGS_BOOST_PROFILE = 1 << 3, //notify Boost settings change + ADL_USER_SETTINGS_USU_PROFILE = 1 << 4, //notify USU settings change + ADL_USER_SETTINGS_CVDC_PROFILE = 1 << 5, //notify Color Vision Deficiency Corretion settings change + ADL_USER_SETTINGS_SCE_PROFILE = 1 << 6, + ADL_USER_SETTINGS_PROVSR = 1 << 7 + } ADL_USER_SETTINGS; + +#define ADL_REG_DEVICE_FUNCTION_1 0x00000001 +#endif /* ADL_DEFINES_H_ */ + + diff --git a/dependencies/display-library/include/adl_sdk.h b/dependencies/display-library/include/adl_sdk.h new file mode 100644 index 0000000..0923a6a --- /dev/null +++ b/dependencies/display-library/include/adl_sdk.h @@ -0,0 +1,46 @@ +// +// Copyright (c) 2016 - 2022 Advanced Micro Devices, Inc. All rights reserved. +// +// MIT LICENSE: +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// \file adl_sdk.h +/// \brief Contains the definition of the Memory Allocation Callback.\n Included in ADL SDK +/// +/// \n\n +/// This file contains the definition of the Memory Allocation Callback.\n +/// It also includes definitions of the respective structures and constants.\n +/// This is the only header file to be included in a C/C++ project using ADL + +#ifndef ADL_SDK_H_ +#define ADL_SDK_H_ + +#include "adl_structures.h" + +#if defined (LINUX) +#define __stdcall +#endif /* (LINUX) */ + +/// Memory Allocation Call back +typedef void* ( __stdcall *ADL_MAIN_MALLOC_CALLBACK )( int ); + +#define ADL_SDK_MAJOR_VERSION 17 +#define ADL_SDK_MINOR_VERSION 1 + +#endif /* ADL_SDK_H_ */ diff --git a/dependencies/display-library/include/adl_structures.h b/dependencies/display-library/include/adl_structures.h new file mode 100644 index 0000000..601ad74 --- /dev/null +++ b/dependencies/display-library/include/adl_structures.h @@ -0,0 +1,4289 @@ +// +// Copyright (c) 2016 - 2022 Advanced Micro Devices, Inc. All rights reserved. +// +// MIT LICENSE: +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/// \file adl_structures.h +///\brief This file contains the structure declarations that are used by the public ADL interfaces for \ALL platforms.\n Included in ADL SDK +/// +/// All data structures used in AMD Display Library (ADL) public interfaces should be defined in this header file. +/// + +#ifndef ADL_STRUCTURES_H_ +#define ADL_STRUCTURES_H_ + +#include "adl_defines.h" +#include +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the graphics adapter. +/// +/// This structure is used to store various information about the graphics adapter. This +/// information can be returned to the user. Alternatively, it can be used to access various driver calls to set +/// or fetch various settings upon the user's request. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct AdapterInfo +{ +/// \ALL_STRUCT_MEM + +/// Size of the structure. + int iSize; +/// The ADL index handle. One GPU may be associated with one or two index handles + int iAdapterIndex; +/// The unique device ID associated with this adapter. + char strUDID[ADL_MAX_PATH]; +/// The BUS number associated with this adapter. + int iBusNumber; +/// The driver number associated with this adapter. + int iDeviceNumber; +/// The function number. + int iFunctionNumber; +/// The vendor ID associated with this adapter. + int iVendorID; +/// Adapter name. + char strAdapterName[ADL_MAX_PATH]; +/// Display name. For example, "\\\\Display0" for Windows or ":0:0" for Linux. + char strDisplayName[ADL_MAX_PATH]; +/// Present or not; 1 if present and 0 if not present.It the logical adapter is present, the display name such as \\\\.\\Display1 can be found from OS + int iPresent; + +#if defined (_WIN32) || defined (_WIN64) +/// \WIN_STRUCT_MEM + +/// Exist or not; 1 is exist and 0 is not present. + int iExist; +/// Driver registry path. + char strDriverPath[ADL_MAX_PATH]; +/// Driver registry path Ext for. + char strDriverPathExt[ADL_MAX_PATH]; +/// PNP string from Windows. + char strPNPString[ADL_MAX_PATH]; +/// It is generated from EnumDisplayDevices. + int iOSDisplayIndex; + +#endif /* (_WIN32) || (_WIN64) */ + +#if defined (LINUX) +/// \LNX_STRUCT_MEM + +/// Internal X screen number from GPUMapInfo (DEPRICATED use XScreenInfo) + int iXScreenNum; +/// Internal driver index from GPUMapInfo + int iDrvIndex; +/// \deprecated Internal x config file screen identifier name. Use XScreenInfo instead. + char strXScreenConfigName[ADL_MAX_PATH]; + +#endif /* (LINUX) */ +} AdapterInfo, *LPAdapterInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the Linux X screen information. +/// +/// This structure is used to store the current screen number and xorg.conf ID name assoicated with an adapter index. +/// This structure is updated during ADL_Main_Control_Refresh or ADL_ScreenInfo_Update. +/// Note: This structure should be used in place of iXScreenNum and strXScreenConfigName in AdapterInfo as they will be +/// deprecated. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +#if defined (LINUX) +typedef struct XScreenInfo +{ +/// Internal X screen number from GPUMapInfo. + int iXScreenNum; +/// Internal x config file screen identifier name. + char strXScreenConfigName[ADL_MAX_PATH]; +} XScreenInfo, *LPXScreenInfo; +#endif /* (LINUX) */ + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about an controller mode +/// +/// This structure is used to store information of an controller mode +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLAdapterCaps +{ + /// AdapterID for this adapter + int iAdapterID; + /// Number of controllers for this adapter + int iNumControllers; + /// Number of displays for this adapter + int iNumDisplays; + /// Number of overlays for this adapter + int iNumOverlays; + /// Number of GLSyncConnectors + int iNumOfGLSyncConnectors; + /// The bit mask identifies the adapter caps + int iCapsMask; + /// The bit identifies the adapter caps \ref define_adapter_caps + int iCapsValue; +}ADLAdapterCaps; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing additional information about the ASIC memory +/// +/// This structure is used to store additional information about the ASIC memory. This +/// information can be returned to the user. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLMemoryInfo2 +{ + /// Memory size in bytes. + long long iMemorySize; + /// Memory type in string. + char strMemoryType[ADL_MAX_PATH]; + /// Highest default performance level Memory bandwidth in Mbytes/s + long long iMemoryBandwidth; + /// HyperMemory size in bytes. + long long iHyperMemorySize; + + /// Invisible Memory size in bytes. + long long iInvisibleMemorySize; + /// Visible Memory size in bytes. + long long iVisibleMemorySize; +} ADLMemoryInfo2, *LPADLMemoryInfo2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing additional information about the ASIC memory +/// +/// This structure is used to store additional information about the ASIC memory. This +/// information can be returned to the user. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLMemoryInfo3 +{ + /// Memory size in bytes. + long long iMemorySize; + /// Memory type in string. + char strMemoryType[ADL_MAX_PATH]; + /// Highest default performance level Memory bandwidth in Mbytes/s + long long iMemoryBandwidth; + /// HyperMemory size in bytes. + long long iHyperMemorySize; + + /// Invisible Memory size in bytes. + long long iInvisibleMemorySize; + /// Visible Memory size in bytes. + long long iVisibleMemorySize; + /// Vram vendor ID + long long iVramVendorRevId; +} ADLMemoryInfo3, *LPADLMemoryInfo3; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing additional information about the ASIC memory +/// +/// This structure is used to store additional information about the ASIC memory. This +/// information can be returned to the user. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLMemoryInfoX4 +{ + /// Memory size in bytes. + long long iMemorySize; + /// Memory type in string. + char strMemoryType[ADL_MAX_PATH]; + /// Highest default performance level Memory bandwidth in Mbytes/s + long long iMemoryBandwidth; + /// HyperMemory size in bytes. + long long iHyperMemorySize; + + /// Invisible Memory size in bytes. + long long iInvisibleMemorySize; + /// Visible Memory size in bytes. + long long iVisibleMemorySize; + /// Vram vendor ID + long long iVramVendorRevId; + /// Memory Bandiwidth that is calculated and finalized on the driver side, grab and go. + long long iMemoryBandwidthX2; + /// Memory Bit Rate that is calculated and finalized on the driver side, grab and go. + long long iMemoryBitRateX2; + +} ADLMemoryInfoX4, *LPADLMemoryInfoX4; + +/////////////////////////////////////////////////////////////////////////// +// ADLvRamVendor Enumeration +/////////////////////////////////////////////////////////////////////////// +enum ADLvRamVendors +{ + ADLvRamVendor_Unsupported = 0x0, + ADLvRamVendor_SAMSUNG, + ADLvRamVendor_INFINEON, + ADLvRamVendor_ELPIDA, + ADLvRamVendor_ETRON, + ADLvRamVendor_NANYA, + ADLvRamVendor_HYNIX, + ADLvRamVendor_MOSEL, + ADLvRamVendor_WINBOND, + ADLvRamVendor_ESMT, + ADLvRamVendor_MICRON = 0xF, + ADLvRamVendor_Undefined +}; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about components of ASIC GCN architecture +/// +/// Elements of GCN info are compute units, number of Tex (Texture filtering units) , number of ROPs (render back-ends). +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLGcnInfo +{ + int CuCount; //Number of compute units on the ASIC. + int TexCount; //Number of texture mapping units. + int RopCount; //Number of Render backend Units. + int ASICFamilyId; //Such SI, VI. See /inc/asic_reg/atiid.h for family ids + int ASICRevisionId; //Such as Ellesmere, Fiji. For example - VI family revision ids are stored in /inc/asic_reg/vi_id.h +}ADLGcnInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related virtual segment config information. +/// +/// This structure is used to store information related virtual segment config +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLVirtualSegmentSettingsOutput +{ + int virtualSegmentSupported; // 1 - subsequent values are valid + int virtualSegmentDefault; //virtual segment default, 1: enable, 0: disable + int virtualSegmentCurrent; //virtual segment current, 1: enable, 0: disable + int iMinSizeInMB; //minimum value + int iMaxSizeInMB; //maximum value + int icurrentSizeInMB; //last configured otherwise same as factory default + int idefaultSizeInMB; //factory default + int iMask; //fileds for extension in the future + int iValue; //fileds for extension in the future +} ADLVirtualSegmentSettingsOutput; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about the Chipset. +/// +/// This structure is used to store various information about the Chipset. This +/// information can be returned to the user. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLChipSetInfo +{ + int iBusType; ///< Bus type. + int iBusSpeedType; ///Maximum Bus Speed of the current platform + int iMaxPCIELaneWidth; ///< Number of PCIE lanes. + int iCurrentPCIELaneWidth; ///< Current PCIE Lane Width + int iSupportedAGPSpeeds; ///< Bit mask or AGP transfer speed. + int iCurrentAGPSpeed; ///< Current AGP speed +} ADLChipSetInfo, *LPADLChipSetInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the ASIC memory. +/// +/// This structure is used to store various information about the ASIC memory. This +/// information can be returned to the user. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLMemoryInfo +{ +/// Memory size in bytes. + long long iMemorySize; +/// Memory type in string. + char strMemoryType[ADL_MAX_PATH]; +/// Memory bandwidth in Mbytes/s. + long long iMemoryBandwidth; +} ADLMemoryInfo, *LPADLMemoryInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about memory required by type +/// +/// This structure is returned by ADL_Adapter_ConfigMemory_Get, which given a desktop and display configuration +/// will return the Memory used. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLMemoryRequired +{ + long long iMemoryReq; /// Memory in bytes required + int iType; /// Type of Memory \ref define_adl_validmemoryrequiredfields + int iDisplayFeatureValue; /// Display features \ref define_adl_visiblememoryfeatures that are using this type of memory +} ADLMemoryRequired, *LPADLMemoryRequired; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the features associated with a display +/// +/// This structure is a parameter to ADL_Adapter_ConfigMemory_Get, which given a desktop and display configuration +/// will return the Memory used. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLMemoryDisplayFeatures +{ + int iDisplayIndex; /// ADL Display index + int iDisplayFeatureValue; /// features that the display is using \ref define_adl_visiblememoryfeatures +} ADLMemoryDisplayFeatures, *LPADLMemoryDisplayFeatures; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing DDC information. +/// +/// This structure is used to store various DDC information that can be returned to the user. +/// Note that all fields of type int are actually defined as unsigned int types within the driver. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDDCInfo +{ +/// Size of the structure + int ulSize; +/// Indicates whether the attached display supports DDC. If this field is zero on return, no other DDC information fields will be used. + int ulSupportsDDC; +/// Returns the manufacturer ID of the display device. Should be zeroed if this information is not available. + int ulManufacturerID; +/// Returns the product ID of the display device. Should be zeroed if this information is not available. + int ulProductID; +/// Returns the name of the display device. Should be zeroed if this information is not available. + char cDisplayName[ADL_MAX_DISPLAY_NAME]; +/// Returns the maximum Horizontal supported resolution. Should be zeroed if this information is not available. + int ulMaxHResolution; +/// Returns the maximum Vertical supported resolution. Should be zeroed if this information is not available. + int ulMaxVResolution; +/// Returns the maximum supported refresh rate. Should be zeroed if this information is not available. + int ulMaxRefresh; +/// Returns the display device preferred timing mode's horizontal resolution. + int ulPTMCx; +/// Returns the display device preferred timing mode's vertical resolution. + int ulPTMCy; +/// Returns the display device preferred timing mode's refresh rate. + int ulPTMRefreshRate; +/// Return EDID flags. + int ulDDCInfoFlag; +} ADLDDCInfo, *LPADLDDCInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing DDC information. +/// +/// This structure is used to store various DDC information that can be returned to the user. +/// Note that all fields of type int are actually defined as unsigned int types within the driver. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDDCInfo2 +{ +/// Size of the structure + int ulSize; +/// Indicates whether the attached display supports DDC. If this field is zero on return, no other DDC +/// information fields will be used. + int ulSupportsDDC; +/// Returns the manufacturer ID of the display device. Should be zeroed if this information is not available. + int ulManufacturerID; +/// Returns the product ID of the display device. Should be zeroed if this information is not available. + int ulProductID; +/// Returns the name of the display device. Should be zeroed if this information is not available. + char cDisplayName[ADL_MAX_DISPLAY_NAME]; +/// Returns the maximum Horizontal supported resolution. Should be zeroed if this information is not available. + int ulMaxHResolution; +/// Returns the maximum Vertical supported resolution. Should be zeroed if this information is not available. + int ulMaxVResolution; +/// Returns the maximum supported refresh rate. Should be zeroed if this information is not available. + int ulMaxRefresh; +/// Returns the display device preferred timing mode's horizontal resolution. + int ulPTMCx; +/// Returns the display device preferred timing mode's vertical resolution. + int ulPTMCy; +/// Returns the display device preferred timing mode's refresh rate. + int ulPTMRefreshRate; +/// Return EDID flags. + int ulDDCInfoFlag; +/// Returns 1 if the display supported packed pixel, 0 otherwise + int bPackedPixelSupported; +/// Returns the Pixel formats the display supports \ref define_ddcinfo_pixelformats + int iPanelPixelFormat; +/// Return EDID serial ID. + int ulSerialID; +/// Return minimum monitor luminance data + int ulMinLuminanceData; +/// Return average monitor luminance data + int ulAvgLuminanceData; +/// Return maximum monitor luminance data + int ulMaxLuminanceData; + +/// Bit vector of supported transfer functions \ref define_source_content_TF + int iSupportedTransferFunction; + +/// Bit vector of supported color spaces \ref define_source_content_CS + int iSupportedColorSpace; + +/// Display Red Chromaticity X coordinate multiplied by 10000 + int iNativeDisplayChromaticityRedX; +/// Display Red Chromaticity Y coordinate multiplied by 10000 + int iNativeDisplayChromaticityRedY; +/// Display Green Chromaticity X coordinate multiplied by 10000 + int iNativeDisplayChromaticityGreenX; +/// Display Green Chromaticity Y coordinate multiplied by 10000 + int iNativeDisplayChromaticityGreenY; +/// Display Blue Chromaticity X coordinate multiplied by 10000 + int iNativeDisplayChromaticityBlueX; +/// Display Blue Chromaticity Y coordinate multiplied by 10000 + int iNativeDisplayChromaticityBlueY; +/// Display White Point X coordinate multiplied by 10000 + int iNativeDisplayChromaticityWhitePointX; +/// Display White Point Y coordinate multiplied by 10000 + int iNativeDisplayChromaticityWhitePointY; +/// Display diffuse screen reflectance 0-1 (100%) in units of 0.01 + int iDiffuseScreenReflectance; +/// Display specular screen reflectance 0-1 (100%) in units of 0.01 + int iSpecularScreenReflectance; +/// Bit vector of supported color spaces \ref define_HDR_support + int iSupportedHDR; +/// Bit vector for freesync flags + int iFreesyncFlags; + +/// Return minimum monitor luminance without dimming data + int ulMinLuminanceNoDimmingData; + + int ulMaxBacklightMaxLuminanceData; + int ulMinBacklightMaxLuminanceData; + int ulMaxBacklightMinLuminanceData; + int ulMinBacklightMinLuminanceData; + + // Reserved for future use + int iReserved[4]; +} ADLDDCInfo2, *LPADLDDCInfo2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information controller Gamma settings. +/// +/// This structure is used to store the red, green and blue color channel information for the. +/// controller gamma setting. This information is returned by ADL, and it can also be used to +/// set the controller gamma setting. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLGamma +{ +/// Red color channel gamma value. + float fRed; +/// Green color channel gamma value. + float fGreen; +/// Blue color channel gamma value. + float fBlue; +} ADLGamma, *LPADLGamma; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about component video custom modes. +/// +/// This structure is used to store the component video custom mode. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLCustomMode +{ +/// Custom mode flags. They are returned by the ADL driver. + int iFlags; +/// Custom mode width. + int iModeWidth; +/// Custom mode height. + int iModeHeight; +/// Custom mode base width. + int iBaseModeWidth; +/// Custom mode base height. + int iBaseModeHeight; +/// Custom mode refresh rate. + int iRefreshRate; +} ADLCustomMode, *LPADLCustomMode; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing Clock information for OD5 calls. +/// +/// This structure is used to retrieve clock information for OD5 calls. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLGetClocksOUT +{ + long ulHighCoreClock; + long ulHighMemoryClock; + long ulHighVddc; + long ulCoreMin; + long ulCoreMax; + long ulMemoryMin; + long ulMemoryMax; + long ulActivityPercent; + long ulCurrentCoreClock; + long ulCurrentMemoryClock; + long ulReserved; +} ADLGetClocksOUT; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing HDTV information for display calls. +/// +/// This structure is used to retrieve HDTV information information for display calls. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayConfig +{ +/// Size of the structure + long ulSize; +/// HDTV connector type. + long ulConnectorType; +/// HDTV capabilities. + long ulDeviceData; +/// Overridden HDTV capabilities. + long ulOverridedDeviceData; +/// Reserved field + long ulReserved; +} ADLDisplayConfig; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the display device. +/// +/// This structure is used to store display device information +/// such as display index, type, name, connection status, mapped adapter and controller indexes, +/// whether or not multiple VPUs are supported, local display connections or not (through Lasso), etc. +/// This information can be returned to the user. Alternatively, it can be used to access various driver calls to set +/// or fetch various display device related settings upon the user's request. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayID +{ +/// The logical display index belonging to this adapter. + int iDisplayLogicalIndex; + +///\brief The physical display index. +/// For example, display index 2 from adapter 2 can be used by current adapter 1.\n +/// So current adapter may enumerate this adapter as logical display 7 but the physical display +/// index is still 2. + int iDisplayPhysicalIndex; + +/// The persistent logical adapter index for the display. + int iDisplayLogicalAdapterIndex; + +///\brief The persistent physical adapter index for the display. +/// It can be the current adapter or a non-local adapter. \n +/// If this adapter index is different than the current adapter, +/// the Display Non Local flag is set inside DisplayInfoValue. + int iDisplayPhysicalAdapterIndex; +} ADLDisplayID, *LPADLDisplayID; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the display device. +/// +/// This structure is used to store various information about the display device. This +/// information can be returned to the user, or used to access various driver calls to set +/// or fetch various display-device-related settings upon the user's request +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayInfo +{ +/// The DisplayID structure + ADLDisplayID displayID; + +///\deprecated The controller index to which the display is mapped.\n Will not be used in the future\n + int iDisplayControllerIndex; + +/// The display's EDID name. + char strDisplayName[ADL_MAX_PATH]; + +/// The display's manufacturer name. + char strDisplayManufacturerName[ADL_MAX_PATH]; + +/// The Display type. For example: CRT, TV, CV, DFP. + int iDisplayType; + +/// The display output type. For example: HDMI, SVIDEO, COMPONMNET VIDEO. + int iDisplayOutputType; + +/// The connector type for the device. + int iDisplayConnector; + +///\brief The bit mask identifies the number of bits ADLDisplayInfo is currently using. \n +/// It will be the sum all the bit definitions in ADL_DISPLAY_DISPLAYINFO_xxx. + int iDisplayInfoMask; + +/// The bit mask identifies the display status. \ref define_displayinfomask + int iDisplayInfoValue; +} ADLDisplayInfo, *LPADLDisplayInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the display port MST device. +/// +/// This structure is used to store various MST information about the display port device. This +/// information can be returned to the user, or used to access various driver calls to +/// fetch various display-device-related settings upon the user's request +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayDPMSTInfo +{ + /// The ADLDisplayID structure + ADLDisplayID displayID; + + /// total bandwidth available on the DP connector + int iTotalAvailableBandwidthInMpbs; + /// bandwidth allocated to this display + int iAllocatedBandwidthInMbps; + + // info from DAL DpMstSinkInfo + /// string identifier for the display + char strGlobalUniqueIdentifier[ADL_MAX_PATH]; + + /// The link count of relative address, rad[0] upto rad[linkCount] are valid + int radLinkCount; + /// The physical connector ID, used to identify the physical DP port + int iPhysicalConnectorID; + + /// Relative address, address scheme starts from source side + char rad[ADL_MAX_RAD_LINK_COUNT]; +} ADLDisplayDPMSTInfo, *LPADLDisplayDPMSTInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the display mode definition used per controller. +/// +/// This structure is used to store the display mode definition used per controller. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayMode +{ +/// Vertical resolution (in pixels). + int iPelsHeight; +/// Horizontal resolution (in pixels). + int iPelsWidth; +/// Color depth. + int iBitsPerPel; +/// Refresh rate. + int iDisplayFrequency; +} ADLDisplayMode; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing detailed timing parameters. +/// +/// This structure is used to store the detailed timing parameters. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDetailedTiming +{ +/// Size of the structure. + int iSize; +/// Timing flags. \ref define_detailed_timing_flags + short sTimingFlags; +/// Total width (columns). + short sHTotal; +/// Displayed width. + short sHDisplay; +/// Horizontal sync signal offset. + short sHSyncStart; +/// Horizontal sync signal width. + short sHSyncWidth; +/// Total height (rows). + short sVTotal; +/// Displayed height. + short sVDisplay; +/// Vertical sync signal offset. + short sVSyncStart; +/// Vertical sync signal width. + short sVSyncWidth; +/// Pixel clock value. + short sPixelClock; +/// Overscan right. + short sHOverscanRight; +/// Overscan left. + short sHOverscanLeft; +/// Overscan bottom. + short sVOverscanBottom; +/// Overscan top. + short sVOverscanTop; + short sOverscan8B; + short sOverscanGR; +} ADLDetailedTiming; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing display mode information. +/// +/// This structure is used to store the display mode information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayModeInfo +{ +/// Timing standard of the current mode. \ref define_modetiming_standard + int iTimingStandard; +/// Applicable timing standards for the current mode. + int iPossibleStandard; +/// Refresh rate factor. + int iRefreshRate; +/// Num of pixels in a row. + int iPelsWidth; +/// Num of pixels in a column. + int iPelsHeight; +/// Detailed timing parameters. + ADLDetailedTiming sDetailedTiming; +} ADLDisplayModeInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about display property. +/// +/// This structure is used to store the display property for the current adapter. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayProperty +{ +/// Must be set to sizeof the structure + int iSize; +/// Must be set to \ref ADL_DL_DISPLAYPROPERTY_TYPE_EXPANSIONMODE or \ref ADL_DL_DISPLAYPROPERTY_TYPE_USEUNDERSCANSCALING + int iPropertyType; +/// Get or Set \ref ADL_DL_DISPLAYPROPERTY_EXPANSIONMODE_CENTER or \ref ADL_DL_DISPLAYPROPERTY_EXPANSIONMODE_FULLSCREEN or \ref ADL_DL_DISPLAYPROPERTY_EXPANSIONMODE_ASPECTRATIO or \ref ADL_DL_DISPLAYPROPERTY_TYPE_ITCFLAGENABLE + int iExpansionMode; +/// Display Property supported? 1: Supported, 0: Not supported + int iSupport; +/// Display Property current value + int iCurrent; +/// Display Property Default value + int iDefault; +} ADLDisplayProperty; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Clock. +/// +/// This structure is used to store the clock information for the current adapter +/// such as core clock and memory clock info. +///\nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLClockInfo +{ +/// Core clock in 10 KHz. + int iCoreClock; +/// Memory clock in 10 KHz. + int iMemoryClock; +} ADLClockInfo, *LPADLClockInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about I2C. +/// +/// This structure is used to store the I2C information for the current adapter. +/// This structure is used by the ADL_Display_WriteAndReadI2C() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLI2C +{ +/// Size of the structure + int iSize; +/// Numerical value representing hardware I2C. + int iLine; +/// The 7-bit I2C slave device address, shifted one bit to the left. + int iAddress; +/// The offset of the data from the address. + int iOffset; +/// Read from or write to slave device. \ref ADL_DL_I2C_ACTIONREAD or \ref ADL_DL_I2C_ACTIONWRITE or \ref ADL_DL_I2C_ACTIONREAD_REPEATEDSTART + int iAction; +/// I2C clock speed in KHz. + int iSpeed; +/// A numerical value representing the number of bytes to be sent or received on the I2C bus. + int iDataSize; +/// Address of the characters which are to be sent or received on the I2C bus. + char *pcData; +} ADLI2C; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about EDID data. +/// +/// This structure is used to store the information about EDID data for the adapter. +/// This structure is used by the ADL_Display_EdidData_Get() and ADL_Display_EdidData_Set() functions. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayEDIDData +{ +/// Size of the structure + int iSize; +/// Set to 0 + int iFlag; + /// Size of cEDIDData. Set by ADL_Display_EdidData_Get() upon return + int iEDIDSize; +/// 0, 1 or 2. If set to 3 or above an error ADL_ERR_INVALID_PARAM is generated + int iBlockIndex; +/// EDID data + char cEDIDData[ADL_MAX_EDIDDATA_SIZE]; +/// Reserved + int iReserved[4]; +}ADLDisplayEDIDData; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about input of controller overlay adjustment. +/// +/// This structure is used to store the information about input of controller overlay adjustment for the adapter. +/// This structure is used by the ADL_Display_ControllerOverlayAdjustmentCaps_Get, ADL_Display_ControllerOverlayAdjustmentData_Get, and +/// ADL_Display_ControllerOverlayAdjustmentData_Set() functions. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLControllerOverlayInput +{ +/// Should be set to the sizeof the structure + int iSize; +///\ref ADL_DL_CONTROLLER_OVERLAY_ALPHA or \ref ADL_DL_CONTROLLER_OVERLAY_ALPHAPERPIX + int iOverlayAdjust; +/// Data. + int iValue; +/// Should be 0. + int iReserved; +} ADLControllerOverlayInput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about overlay adjustment. +/// +/// This structure is used to store the information about overlay adjustment for the adapter. +/// This structure is used by the ADLControllerOverlayInfo() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLAdjustmentinfo +{ +/// Default value + int iDefault; +/// Minimum value + int iMin; +/// Maximum Value + int iMax; +/// Step value + int iStep; +} ADLAdjustmentinfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about controller overlay information. +/// +/// This structure is used to store information about controller overlay info for the adapter. +/// This structure is used by the ADL_Display_ControllerOverlayAdjustmentCaps_Get() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLControllerOverlayInfo +{ +/// Should be set to the sizeof the structure + int iSize; +/// Data. + ADLAdjustmentinfo sOverlayInfo; +/// Should be 0. + int iReserved[3]; +} ADLControllerOverlayInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing GL-Sync module information. +/// +/// This structure is used to retrieve GL-Sync module information for +/// Workstation Framelock/Genlock. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLGLSyncModuleID +{ +/// Unique GL-Sync module ID. + int iModuleID; +/// GL-Sync GPU port index (to be passed into ADLGLSyncGenlockConfig.lSignalSource and ADLGlSyncPortControl.lSignalSource). + int iGlSyncGPUPort; +/// GL-Sync module firmware version of Boot Sector. + int iFWBootSectorVersion; +/// GL-Sync module firmware version of User Sector. + int iFWUserSectorVersion; +} ADLGLSyncModuleID , *LPADLGLSyncModuleID; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing GL-Sync ports capabilities. +/// +/// This structure is used to retrieve hardware capabilities for the ports of the GL-Sync module +/// for Workstation Framelock/Genlock (such as port type and number of associated LEDs). +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLGLSyncPortCaps +{ +/// Port type. Bitfield of ADL_GLSYNC_PORTTYPE_* \ref define_glsync + int iPortType; +/// Number of LEDs associated for this port. + int iNumOfLEDs; +}ADLGLSyncPortCaps, *LPADLGLSyncPortCaps; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing GL-Sync Genlock settings. +/// +/// This structure is used to get and set genlock settings for the GPU ports of the GL-Sync module +/// for Workstation Framelock/Genlock.\n +/// \see define_glsync +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLGLSyncGenlockConfig +{ +/// Specifies what fields in this structure are valid \ref define_glsync + int iValidMask; +/// Delay (ms) generating a sync signal. + int iSyncDelay; +/// Vector of framelock control bits. Bitfield of ADL_GLSYNC_FRAMELOCKCNTL_* \ref define_glsync + int iFramelockCntlVector; +/// Source of the sync signal. Either GL_Sync GPU Port index or ADL_GLSYNC_SIGNALSOURCE_* \ref define_glsync + int iSignalSource; +/// Use sampled sync signal. A value of 0 specifies no sampling. + int iSampleRate; +/// For interlaced sync signals, the value can be ADL_GLSYNC_SYNCFIELD_1 or *_BOTH \ref define_glsync + int iSyncField; +/// The signal edge that should trigger synchronization. ADL_GLSYNC_TRIGGEREDGE_* \ref define_glsync + int iTriggerEdge; +/// Scan rate multiplier applied to the sync signal. ADL_GLSYNC_SCANRATECOEFF_* \ref define_glsync + int iScanRateCoeff; +}ADLGLSyncGenlockConfig, *LPADLGLSyncGenlockConfig; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing GL-Sync port information. +/// +/// This structure is used to get status of the GL-Sync ports (BNC or RJ45s) +/// for Workstation Framelock/Genlock. +/// \see define_glsync +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLGlSyncPortInfo +{ +/// Type of GL-Sync port (ADL_GLSYNC_PORT_*). + int iPortType; +/// The number of LEDs for this port. It's also filled within ADLGLSyncPortCaps. + int iNumOfLEDs; +/// Port state ADL_GLSYNC_PORTSTATE_* \ref define_glsync + int iPortState; +/// Scanned frequency for this port (vertical refresh rate in milliHz; 60000 means 60 Hz). + int iFrequency; +/// Used for ADL_GLSYNC_PORT_BNC. It is ADL_GLSYNC_SIGNALTYPE_* \ref define_glsync + int iSignalType; +/// Used for ADL_GLSYNC_PORT_RJ45PORT*. It is GL_Sync GPU Port index or ADL_GLSYNC_SIGNALSOURCE_*. \ref define_glsync + int iSignalSource; +} ADLGlSyncPortInfo, *LPADLGlSyncPortInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing GL-Sync port control settings. +/// +/// This structure is used to configure the GL-Sync ports (RJ45s only) +/// for Workstation Framelock/Genlock. +/// \see define_glsync +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLGlSyncPortControl +{ +/// Port to control ADL_GLSYNC_PORT_RJ45PORT1 or ADL_GLSYNC_PORT_RJ45PORT2 \ref define_glsync + int iPortType; +/// Port control data ADL_GLSYNC_PORTCNTL_* \ref define_glsync + int iControlVector; +/// Source of the sync signal. Either GL_Sync GPU Port index or ADL_GLSYNC_SIGNALSOURCE_* \ref define_glsync + int iSignalSource; +} ADLGlSyncPortControl; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing GL-Sync mode of a display. +/// +/// This structure is used to get and set GL-Sync mode settings for a display connected to +/// an adapter attached to a GL-Sync module for Workstation Framelock/Genlock. +/// \see define_glsync +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLGlSyncMode +{ +/// Mode control vector. Bitfield of ADL_GLSYNC_MODECNTL_* \ref define_glsync + int iControlVector; +/// Mode status vector. Bitfield of ADL_GLSYNC_MODECNTL_STATUS_* \ref define_glsync + int iStatusVector; +/// Index of GL-Sync connector used to genlock the display/controller. + int iGLSyncConnectorIndex; +} ADLGlSyncMode, *LPADLGlSyncMode; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing GL-Sync mode of a display. +/// +/// This structure is used to get and set GL-Sync mode settings for a display connected to +/// an adapter attached to a GL-Sync module for Workstation Framelock/Genlock. +/// \see define_glsync +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLGlSyncMode2 +{ +/// Mode control vector. Bitfield of ADL_GLSYNC_MODECNTL_* \ref define_glsync + int iControlVector; +/// Mode status vector. Bitfield of ADL_GLSYNC_MODECNTL_STATUS_* \ref define_glsync + int iStatusVector; +/// Index of GL-Sync connector used to genlock the display/controller. + int iGLSyncConnectorIndex; +/// Index of the display to which this GLSync applies to. + int iDisplayIndex; +} ADLGlSyncMode2, *LPADLGlSyncMode2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the packet info of a display. +/// +/// This structure is used to get and set the packet information of a display. +/// This structure is used by ADLDisplayDataPacket. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLInfoPacket +{ + char hb0; + char hb1; + char hb2; +/// sb0~sb27 + char sb[28]; +}ADLInfoPacket; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the AVI packet info of a display. +/// +/// This structure is used to get and set AVI the packet info of a display. +/// This structure is used by ADLDisplayDataPacket. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLAVIInfoPacket //Valid user defined data/ +{ +/// byte 3, bit 7 + char bPB3_ITC; +/// byte 5, bit [7:4]. + char bPB5; +}ADLAVIInfoPacket; + +// Overdrive clock setting structure definition. + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the Overdrive clock setting. +/// +/// This structure is used to get the Overdrive clock setting. +/// This structure is used by ADLAdapterODClockInfo. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODClockSetting +{ +/// Deafult clock + int iDefaultClock; +/// Current clock + int iCurrentClock; +/// Maximum clcok + int iMaxClock; +/// Minimum clock + int iMinClock; +/// Requested clcock + int iRequestedClock; +/// Step + int iStepClock; +} ADLODClockSetting; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the Overdrive clock information. +/// +/// This structure is used to get the Overdrive clock information. +/// This structure is used by the ADL_Display_ODClockInfo_Get() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLAdapterODClockInfo +{ +/// Size of the structure + int iSize; +/// Flag \ref define_clockinfo_flags + int iFlags; +/// Memory Clock + ADLODClockSetting sMemoryClock; +/// Engine Clock + ADLODClockSetting sEngineClock; +} ADLAdapterODClockInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the Overdrive clock configuration. +/// +/// This structure is used to set the Overdrive clock configuration. +/// This structure is used by the ADL_Display_ODClockConfig_Set() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLAdapterODClockConfig +{ +/// Size of the structure + int iSize; +/// Flag \ref define_clockinfo_flags + int iFlags; +/// Memory Clock + int iMemoryClock; +/// Engine Clock + int iEngineClock; +} ADLAdapterODClockConfig; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about current power management related activity. +/// +/// This structure is used to store information about current power management related activity. +/// This structure (Overdrive 5 interfaces) is used by the ADL_PM_CurrentActivity_Get() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPMActivity +{ +/// Must be set to the size of the structure + int iSize; +/// Current engine clock. + int iEngineClock; +/// Current memory clock. + int iMemoryClock; +/// Current core voltage. + int iVddc; +/// GPU utilization. + int iActivityPercent; +/// Performance level index. + int iCurrentPerformanceLevel; +/// Current PCIE bus speed. + int iCurrentBusSpeed; +/// Number of PCIE bus lanes. + int iCurrentBusLanes; +/// Maximum number of PCIE bus lanes. + int iMaximumBusLanes; +/// Reserved for future purposes. + int iReserved; +} ADLPMActivity; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about thermal controller. +/// +/// This structure is used to store information about thermal controller. +/// This structure is used by ADL_PM_ThermalDevices_Enum. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLThermalControllerInfo +{ +/// Must be set to the size of the structure + int iSize; +/// Possible valies: \ref ADL_DL_THERMAL_DOMAIN_OTHER or \ref ADL_DL_THERMAL_DOMAIN_GPU. + int iThermalDomain; +/// GPU 0, 1, etc. + int iDomainIndex; +/// Possible valies: \ref ADL_DL_THERMAL_FLAG_INTERRUPT or \ref ADL_DL_THERMAL_FLAG_FANCONTROL + int iFlags; +} ADLThermalControllerInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about thermal controller temperature. +/// +/// This structure is used to store information about thermal controller temperature. +/// This structure is used by the ADL_PM_Temperature_Get() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLTemperature +{ +/// Must be set to the size of the structure + int iSize; +/// Temperature in millidegrees Celsius. + int iTemperature; +} ADLTemperature; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about thermal controller fan speed. +/// +/// This structure is used to store information about thermal controller fan speed. +/// This structure is used by the ADL_PM_FanSpeedInfo_Get() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLFanSpeedInfo +{ +/// Must be set to the size of the structure + int iSize; +/// \ref define_fanctrl + int iFlags; +/// Minimum possible fan speed value in percents. + int iMinPercent; +/// Maximum possible fan speed value in percents. + int iMaxPercent; +/// Minimum possible fan speed value in RPM. + int iMinRPM; +/// Maximum possible fan speed value in RPM. + int iMaxRPM; +} ADLFanSpeedInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about fan speed reported by thermal controller. +/// +/// This structure is used to store information about fan speed reported by thermal controller. +/// This structure is used by the ADL_Overdrive5_FanSpeed_Get() and ADL_Overdrive5_FanSpeed_Set() functions. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLFanSpeedValue +{ +/// Must be set to the size of the structure + int iSize; +/// Possible valies: \ref ADL_DL_FANCTRL_SPEED_TYPE_PERCENT or \ref ADL_DL_FANCTRL_SPEED_TYPE_RPM + int iSpeedType; +/// Fan speed value + int iFanSpeed; +/// The only flag for now is: \ref ADL_DL_FANCTRL_FLAG_USER_DEFINED_SPEED + int iFlags; +} ADLFanSpeedValue; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the range of Overdrive parameter. +/// +/// This structure is used to store information about the range of Overdrive parameter. +/// This structure is used by ADLODParameters. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODParameterRange +{ +/// Minimum parameter value. + int iMin; +/// Maximum parameter value. + int iMax; +/// Parameter step value. + int iStep; +} ADLODParameterRange; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive parameters. +/// +/// This structure is used to store information about Overdrive parameters. +/// This structure is used by the ADL_Overdrive5_ODParameters_Get() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODParameters +{ +/// Must be set to the size of the structure + int iSize; +/// Number of standard performance states. + int iNumberOfPerformanceLevels; +/// Indicates whether the GPU is capable to measure its activity. + int iActivityReportingSupported; +/// Indicates whether the GPU supports discrete performance levels or performance range. + int iDiscretePerformanceLevels; +/// Reserved for future use. + int iReserved; +/// Engine clock range. + ADLODParameterRange sEngineClock; +/// Memory clock range. + ADLODParameterRange sMemoryClock; +/// Core voltage range. + ADLODParameterRange sVddc; +} ADLODParameters; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive level. +/// +/// This structure is used to store information about Overdrive level. +/// This structure is used by ADLODPerformanceLevels. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODPerformanceLevel +{ +/// Engine clock. + int iEngineClock; +/// Memory clock. + int iMemoryClock; +/// Core voltage. + int iVddc; +} ADLODPerformanceLevel; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive performance levels. +/// +/// This structure is used to store information about Overdrive performance levels. +/// This structure is used by the ADL_Overdrive5_ODPerformanceLevels_Get() and ADL_Overdrive5_ODPerformanceLevels_Set() functions. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODPerformanceLevels +{ +/// Must be set to sizeof( \ref ADLODPerformanceLevels ) + sizeof( \ref ADLODPerformanceLevel ) * (ADLODParameters.iNumberOfPerformanceLevels - 1) + int iSize; + int iReserved; +/// Array of performance state descriptors. Must have ADLODParameters.iNumberOfPerformanceLevels elements. + ADLODPerformanceLevel aLevels [1]; +} ADLODPerformanceLevels; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the proper CrossfireX chains combinations. +/// +/// This structure is used to store information about the CrossfireX chains combination for a particular adapter. +/// This structure is used by the ADL_Adapter_Crossfire_Caps(), ADL_Adapter_Crossfire_Get(), and ADL_Adapter_Crossfire_Set() functions. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLCrossfireComb +{ +/// Number of adapters in this combination. + int iNumLinkAdapter; +/// A list of ADL indexes of the linked adapters in this combination. + int iAdaptLink[3]; +} ADLCrossfireComb; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing CrossfireX state and error information. +/// +/// This structure is used to store state and error information about a particular adapter CrossfireX combination. +/// This structure is used by the ADL_Adapter_Crossfire_Get() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLCrossfireInfo +{ +/// Current error code of this CrossfireX combination. + int iErrorCode; +/// Current \ref define_crossfirestate + int iState; +/// If CrossfireX is supported by this combination. The value is either \ref ADL_TRUE or \ref ADL_FALSE. + int iSupported; +} ADLCrossfireInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about the BIOS. +/// +/// This structure is used to store various information about the Chipset. This +/// information can be returned to the user. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLBiosInfo +{ + char strPartNumber[ADL_MAX_PATH]; ///< Part number. + char strVersion[ADL_MAX_PATH]; ///< Version number. + char strDate[ADL_MAX_PATH]; ///< BIOS date in yyyy/mm/dd hh:mm format. +} ADLBiosInfo, *LPADLBiosInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about adapter location. +/// +/// This structure is used to store information about adapter location. +/// This structure is used by ADLMVPUStatus. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLAdapterLocation +{ +/// PCI Bus number : 8 bits + int iBus; +/// Device number : 5 bits + int iDevice; +/// Function number : 3 bits + int iFunction; +} ADLAdapterLocation,ADLBdf; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing version information +/// +/// This structure is used to store software version information, description of the display device and a web link to the latest installed Catalyst drivers. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLVersionsInfo +{ + /// Driver Release (Packaging) Version (e.g. 8.71-100128n-094835E-ATI) + char strDriverVer[ADL_MAX_PATH]; + /// Catalyst Version(e.g. "10.1"). + char strCatalystVersion[ADL_MAX_PATH]; + /// Web link to an XML file with information about the latest AMD drivers and locations (e.g. "http://www.amd.com/us/driverxml" ) + char strCatalystWebLink[ADL_MAX_PATH]; +} ADLVersionsInfo, *LPADLVersionsInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing version information +/// +/// This structure is used to store software version information, description of the display device and a web link to the latest installed Catalyst drivers. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLVersionsInfoX2 +{ + /// Driver Release (Packaging) Version (e.g. "16.20.1035-160621a-303814C") + char strDriverVer[ADL_MAX_PATH]; + /// Catalyst Version(e.g. "15.8"). + char strCatalystVersion[ADL_MAX_PATH]; + /// Crimson Version(e.g. "16.6.2"). + char strCrimsonVersion[ADL_MAX_PATH]; + /// Web link to an XML file with information about the latest AMD drivers and locations (e.g. "http://support.amd.com/drivers/xml/driver_09_us.xml" ) + char strCatalystWebLink[ADL_MAX_PATH]; +} ADLVersionsInfoX2, *LPADLVersionsInfoX2; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about MultiVPU capabilities. +/// +/// This structure is used to store information about MultiVPU capabilities. +/// This structure is used by the ADL_Display_MVPUCaps_Get() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLMVPUCaps +{ +/// Must be set to sizeof( ADLMVPUCaps ). + int iSize; +/// Number of adapters. + int iAdapterCount; +/// Bits set for all possible MVPU masters. \ref MVPU_ADAPTER_0 .. \ref MVPU_ADAPTER_3 + int iPossibleMVPUMasters; +/// Bits set for all possible MVPU slaves. \ref MVPU_ADAPTER_0 .. \ref MVPU_ADAPTER_3 + int iPossibleMVPUSlaves; +/// Registry path for each adapter. + char cAdapterPath[ADL_DL_MAX_MVPU_ADAPTERS][ADL_DL_MAX_REGISTRY_PATH]; +} ADLMVPUCaps; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about MultiVPU status. +/// +/// This structure is used to store information about MultiVPU status. +/// Ths structure is used by the ADL_Display_MVPUStatus_Get() function. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLMVPUStatus +{ +/// Must be set to sizeof( ADLMVPUStatus ). + int iSize; +/// Number of active adapters. + int iActiveAdapterCount; +/// MVPU status. + int iStatus; +/// PCI Bus/Device/Function for each active adapter participating in MVPU. + ADLAdapterLocation aAdapterLocation[ADL_DL_MAX_MVPU_ADAPTERS]; +} ADLMVPUStatus; + +// Displays Manager structures + +/////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about the activatable source. +/// +/// This structure is used to store activatable source information +/// This information can be returned to the user. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLActivatableSource +{ + /// The Persistent logical Adapter Index. + int iAdapterIndex; + /// The number of Activatable Sources. + int iNumActivatableSources; + /// The bit mask identifies the number of bits ActivatableSourceValue is using. (Not currnetly used) + int iActivatableSourceMask; + /// The bit mask identifies the status. (Not currnetly used) + int iActivatableSourceValue; +} ADLActivatableSource, *LPADLActivatableSource; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about display mode. +/// +/// This structure is used to store the display mode for the current adapter +/// such as X, Y positions, screen resolutions, orientation, +/// color depth, refresh rate, progressive or interlace mode, etc. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLMode +{ +/// Adapter index. + int iAdapterIndex; +/// Display IDs. + ADLDisplayID displayID; +/// Screen position X coordinate. + int iXPos; +/// Screen position Y coordinate. + int iYPos; +/// Screen resolution Width. + int iXRes; +/// Screen resolution Height. + int iYRes; +/// Screen Color Depth. E.g., 16, 32. + int iColourDepth; +/// Screen refresh rate. Could be fractional E.g. 59.97 + float fRefreshRate; +/// Screen orientation. E.g., 0, 90, 180, 270. + int iOrientation; +/// Vista mode flag indicating Progressive or Interlaced mode. + int iModeFlag; +/// The bit mask identifying the number of bits this Mode is currently using. It is the sum of all the bit definitions defined in \ref define_displaymode + int iModeMask; +/// The bit mask identifying the display status. The detailed definition is in \ref define_displaymode + int iModeValue; +} ADLMode, *LPADLMode; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about display target information. +/// +/// This structure is used to store the display target information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayTarget +{ + /// The Display ID. + ADLDisplayID displayID; + + /// The display map index identify this manner and the desktop surface. + int iDisplayMapIndex; + + /// The bit mask identifies the number of bits DisplayTarget is currently using. It is the sum of all the bit definitions defined in \ref ADL_DISPLAY_DISPLAYTARGET_PREFERRED. + int iDisplayTargetMask; + + /// The bit mask identifies the display status. The detailed definition is in \ref ADL_DISPLAY_DISPLAYTARGET_PREFERRED. + int iDisplayTargetValue; +} ADLDisplayTarget, *LPADLDisplayTarget; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the display SLS bezel Mode information. +/// +/// This structure is used to store the display SLS bezel Mode information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct tagADLBezelTransientMode +{ + /// Adapter Index + int iAdapterIndex; + + /// SLS Map Index + int iSLSMapIndex; + + /// The mode index + int iSLSModeIndex; + + /// The mode + ADLMode displayMode; + + /// The number of bezel offsets belongs to this map + int iNumBezelOffset; + + /// The first bezel offset array index in the native mode array + int iFirstBezelOffsetArrayIndex; + + /// The bit mask identifies the bits this structure is currently using. It will be the total OR of all the bit definitions. + int iSLSBezelTransientModeMask; + + /// The bit mask identifies the display status. The detail definition is defined below. + int iSLSBezelTransientModeValue; +} ADLBezelTransientMode, *LPADLBezelTransientMode; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about the adapter display manner. +/// +/// This structure is used to store adapter display manner information +/// This information can be returned to the user. Alternatively, it can be used to access various driver calls to +/// fetch various display device related display manner settings upon the user's request. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLAdapterDisplayCap +{ + /// The Persistent logical Adapter Index. + int iAdapterIndex; + /// The bit mask identifies the number of bits AdapterDisplayCap is currently using. Sum all the bits defined in ADL_ADAPTER_DISPLAYCAP_XXX + int iAdapterDisplayCapMask; + /// The bit mask identifies the status. Refer to ADL_ADAPTER_DISPLAYCAP_XXX + int iAdapterDisplayCapValue; +} ADLAdapterDisplayCap, *LPADLAdapterDisplayCap; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about display mapping. +/// +/// This structure is used to store the display mapping data such as display manner. +/// For displays with horizontal or vertical stretch manner, +/// this structure also stores the display order, display row, and column data. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayMap +{ +/// The current display map index. It is the OS desktop index. For example, if the OS index 1 is showing clone mode, the display map will be 1. + int iDisplayMapIndex; + +/// The Display Mode for the current map + ADLMode displayMode; + +/// The number of display targets belongs to this map\n + int iNumDisplayTarget; + +/// The first target array index in the Target array\n + int iFirstDisplayTargetArrayIndex; + +/// The bit mask identifies the number of bits DisplayMap is currently using. It is the sum of all the bit definitions defined in ADL_DISPLAY_DISPLAYMAP_MANNER_xxx. + int iDisplayMapMask; + +///The bit mask identifies the display status. The detailed definition is in ADL_DISPLAY_DISPLAYMAP_MANNER_xxx. + int iDisplayMapValue; +} ADLDisplayMap, *LPADLDisplayMap; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about the display device possible map for one GPU +/// +/// This structure is used to store the display device possible map +/// This information can be returned to the user. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPossibleMap +{ + /// The current PossibleMap index. Each PossibleMap is assigned an index + int iIndex; + /// The adapter index identifying the GPU for which to validate these Maps & Targets + int iAdapterIndex; + /// Number of display Maps for this GPU to be validated + int iNumDisplayMap; + /// The display Maps list to validate + ADLDisplayMap* displayMap; + /// the number of display Targets for these display Maps + int iNumDisplayTarget; + /// The display Targets list for these display Maps to be validated. + ADLDisplayTarget* displayTarget; +} ADLPossibleMap, *LPADLPossibleMap; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about display possible mapping. +/// +/// This structure is used to store the display possible mapping's controller index for the current display. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPossibleMapping +{ + int iDisplayIndex; ///< The display index. Each display is assigned an index. + int iDisplayControllerIndex; ///< The controller index to which display is mapped. + int iDisplayMannerSupported; ///< The supported display manner. +} ADLPossibleMapping, *LPADLPossibleMapping; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing information about the validated display device possible map result. +/// +/// This structure is used to store the validated display device possible map result +/// This information can be returned to the user. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPossibleMapResult +{ + /// The current display map index. It is the OS Desktop index. For example, OS Index 1 showing clone mode. The Display Map will be 1. + int iIndex; + // The bit mask identifies the number of bits PossibleMapResult is currently using. It will be the sum all the bit definitions defined in ADL_DISPLAY_POSSIBLEMAPRESULT_VALID. + int iPossibleMapResultMask; + /// The bit mask identifies the possible map result. The detail definition is defined in ADL_DISPLAY_POSSIBLEMAPRESULT_XXX. + int iPossibleMapResultValue; +} ADLPossibleMapResult, *LPADLPossibleMapResult; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the display SLS Grid information. +/// +/// This structure is used to store the display SLS Grid information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSLSGrid +{ +/// The Adapter index. + int iAdapterIndex; + +/// The grid index. + int iSLSGridIndex; + +/// The grid row. + int iSLSGridRow; + +/// The grid column. + int iSLSGridColumn; + +/// The grid bit mask identifies the number of bits DisplayMap is currently using. Sum of all bits defined in ADL_DISPLAY_SLSGRID_ORIENTATION_XXX + int iSLSGridMask; + +/// The grid bit value identifies the display status. Refer to ADL_DISPLAY_SLSGRID_ORIENTATION_XXX + int iSLSGridValue; +} ADLSLSGrid, *LPADLSLSGrid; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the display SLS Map information. +/// +/// This structure is used to store the display SLS Map information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSLSMap +{ + /// The Adapter Index + int iAdapterIndex; + + /// The current display map index. It is the OS Desktop index. For example, OS Index 1 showing clone mode. The Display Map will be 1. + int iSLSMapIndex; + + /// Indicate the current grid + ADLSLSGrid grid; + + /// OS surface index + int iSurfaceMapIndex; + + /// Screen orientation. E.g., 0, 90, 180, 270 + int iOrientation; + + /// The number of display targets belongs to this map + int iNumSLSTarget; + + /// The first target array index in the Target array + int iFirstSLSTargetArrayIndex; + + /// The number of native modes belongs to this map + int iNumNativeMode; + + /// The first native mode array index in the native mode array + int iFirstNativeModeArrayIndex; + + /// The number of bezel modes belongs to this map + int iNumBezelMode; + + /// The first bezel mode array index in the native mode array + int iFirstBezelModeArrayIndex; + + /// The number of bezel offsets belongs to this map + int iNumBezelOffset; + + /// The first bezel offset array index in the + int iFirstBezelOffsetArrayIndex; + + /// The bit mask identifies the number of bits DisplayMap is currently using. Sum all the bit definitions defined in ADL_DISPLAY_SLSMAP_XXX. + int iSLSMapMask; + + /// The bit mask identifies the display map status. Refer to ADL_DISPLAY_SLSMAP_XXX + int iSLSMapValue; +} ADLSLSMap, *LPADLSLSMap; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the display SLS Offset information. +/// +/// This structure is used to store the display SLS Offset information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSLSOffset +{ + /// The Adapter Index + int iAdapterIndex; + + /// The current display map index. It is the OS Desktop index. For example, OS Index 1 showing clone mode. The Display Map will be 1. + int iSLSMapIndex; + + /// The Display ID. + ADLDisplayID displayID; + + /// SLS Bezel Mode Index + int iBezelModeIndex; + + /// SLS Bezel Offset X + int iBezelOffsetX; + + /// SLS Bezel Offset Y + int iBezelOffsetY; + + /// SLS Display Width + int iDisplayWidth; + + /// SLS Display Height + int iDisplayHeight; + + /// The bit mask identifies the number of bits Offset is currently using. + int iBezelOffsetMask; + + /// The bit mask identifies the display status. + int iBezelffsetValue; +} ADLSLSOffset, *LPADLSLSOffset; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the display SLS Mode information. +/// +/// This structure is used to store the display SLS Mode information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSLSMode +{ + /// The Adapter Index + int iAdapterIndex; + + /// The current display map index. It is the OS Desktop index. For example, OS Index 1 showing clone mode. The Display Map will be 1. + int iSLSMapIndex; + + /// The mode index + int iSLSModeIndex; + + /// The mode for this map. + ADLMode displayMode; + + /// The bit mask identifies the number of bits Mode is currently using. + int iSLSNativeModeMask; + + /// The bit mask identifies the display status. + int iSLSNativeModeValue; +} ADLSLSMode, *LPADLSLSMode; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the display Possible SLS Map information. +/// +/// This structure is used to store the display Possible SLS Map information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPossibleSLSMap +{ + /// The current display map index. It is the OS Desktop index. + /// For example, OS Index 1 showing clone mode. The Display Map will be 1. + int iSLSMapIndex; + + /// Number of display map to be validated. + int iNumSLSMap; + + /// The display map list for validation + ADLSLSMap* lpSLSMap; + + /// the number of display map config to be validated. + int iNumSLSTarget; + + /// The display target list for validation. + ADLDisplayTarget* lpDisplayTarget; +} ADLPossibleSLSMap, *LPADLPossibleSLSMap; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the SLS targets. +/// +/// This structure is used to store the SLS targets information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSLSTarget +{ + /// the logic adapter index + int iAdapterIndex; + + /// The SLS map index + int iSLSMapIndex; + + /// The target ID + ADLDisplayTarget displayTarget; + + /// Target postion X in SLS grid + int iSLSGridPositionX; + + /// Target postion Y in SLS grid + int iSLSGridPositionY; + + /// The view size width, height and rotation angle per SLS Target + ADLMode viewSize; + + /// The bit mask identifies the bits in iSLSTargetValue are currently used + int iSLSTargetMask; + + /// The bit mask identifies status info. It is for function extension purpose + int iSLSTargetValue; +} ADLSLSTarget, *LPADLSLSTarget; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the Adapter offset stepping size. +/// +/// This structure is used to store the Adapter offset stepping size information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLBezelOffsetSteppingSize +{ + /// the logic adapter index + int iAdapterIndex; + + /// The SLS map index + int iSLSMapIndex; + + /// Bezel X stepping size offset + int iBezelOffsetSteppingSizeX; + + /// Bezel Y stepping size offset + int iBezelOffsetSteppingSizeY; + + /// Identifies the bits this structure is currently using. It will be the total OR of all the bit definitions. + int iBezelOffsetSteppingSizeMask; + + /// Bit mask identifies the display status. + int iBezelOffsetSteppingSizeValue; +} ADLBezelOffsetSteppingSize, *LPADLBezelOffsetSteppingSize; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the overlap offset info for all the displays for each SLS mode. +/// +/// This structure is used to store the no. of overlapped modes for each SLS Mode once user finishes the configuration from Overlap Widget +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSLSOverlappedMode +{ + /// the SLS mode for which the overlap is configured + ADLMode SLSMode; + /// the number of target displays in SLS. + int iNumSLSTarget; + /// the first target array index in the target array + int iFirstTargetArrayIndex; +}ADLSLSTargetOverlap, *LPADLSLSTargetOverlap; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about driver supported PowerExpress Config Caps +/// +/// This structure is used to store the driver supported PowerExpress Config Caps +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPXConfigCaps +{ + /// The Persistent logical Adapter Index. + int iAdapterIndex; + + /// The bit mask identifies the number of bits PowerExpress Config Caps is currently using. It is the sum of all the bit definitions defined in ADL_PX_CONFIGCAPS_XXXX /ref define_powerxpress_constants. + int iPXConfigCapMask; + + /// The bit mask identifies the PowerExpress Config Caps value. The detailed definition is in ADL_PX_CONFIGCAPS_XXXX /ref define_powerxpress_constants. + int iPXConfigCapValue; +} ADLPXConfigCaps, *LPADLPXConfigCaps; + +///////////////////////////////////////////////////////////////////////////////////////// +///\brief Enum containing PX or HG type +/// +/// This enum is used to get PX or hG type +/// +/// \nosubgrouping +////////////////////////////////////////////////////////////////////////////////////////// +typedef enum ADLPxType +{ + //Not AMD related PX/HG or not PX or HG at all + ADL_PX_NONE = 0, + //A+A PX + ADL_SWITCHABLE_AMDAMD = 1, + // A+A HG + ADL_HG_AMDAMD = 2, + //A+I PX + ADL_SWITCHABLE_AMDOTHER = 3, + //A+I HG + ADL_HG_AMDOTHER = 4, +}ADLPxType; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about an application +/// +/// This structure is used to store basic information of an application +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLApplicationData +{ + /// Path Name + char strPathName[ADL_MAX_PATH]; + /// File Name + char strFileName[ADL_APP_PROFILE_FILENAME_LENGTH]; + /// Creation timestamp + char strTimeStamp[ADL_APP_PROFILE_TIMESTAMP_LENGTH]; + /// Version + char strVersion[ADL_APP_PROFILE_VERSION_LENGTH]; +}ADLApplicationData; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about an application +/// +/// This structure is used to store basic information of an application +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLApplicationDataX2 +{ + /// Path Name + wchar_t strPathName[ADL_MAX_PATH]; + /// File Name + wchar_t strFileName[ADL_APP_PROFILE_FILENAME_LENGTH]; + /// Creation timestamp + wchar_t strTimeStamp[ADL_APP_PROFILE_TIMESTAMP_LENGTH]; + /// Version + wchar_t strVersion[ADL_APP_PROFILE_VERSION_LENGTH]; +}ADLApplicationDataX2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about an application +/// +/// This structure is used to store basic information of an application including process id +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLApplicationDataX3 +{ + /// Path Name + wchar_t strPathName[ADL_MAX_PATH]; + /// File Name + wchar_t strFileName[ADL_APP_PROFILE_FILENAME_LENGTH]; + /// Creation timestamp + wchar_t strTimeStamp[ADL_APP_PROFILE_TIMESTAMP_LENGTH]; + /// Version + wchar_t strVersion[ADL_APP_PROFILE_VERSION_LENGTH]; + //Application Process id + unsigned int iProcessId; +}ADLApplicationDataX3; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information of a property of an application profile +/// +/// This structure is used to store property information of an application profile +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct PropertyRecord +{ + /// Property Name + char strName [ADL_APP_PROFILE_PROPERTY_LENGTH]; + /// Property Type + ADLProfilePropertyType eType; + /// Data Size in bytes + int iDataSize; + /// Property Value, can be any data type + unsigned char uData[1]; +}PropertyRecord; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about an application profile +/// +/// This structure is used to store information of an application profile +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLApplicationProfile +{ + /// Number of properties + int iCount; + /// Buffer to store all property records + PropertyRecord record[1]; +}ADLApplicationProfile; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about an OD5 Power Control feature +/// +/// This structure is used to store information of an Power Control feature +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPowerControlInfo +{ +/// Minimum value. +int iMinValue; +/// Maximum value. +int iMaxValue; +/// The minimum change in between minValue and maxValue. +int iStepValue; + } ADLPowerControlInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about an controller mode +/// +/// This structure is used to store information of an controller mode +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLControllerMode +{ + /// This falg indicates actions that will be applied by set viewport + /// The value can be a combination of ADL_CONTROLLERMODE_CM_MODIFIER_VIEW_POSITION, + /// ADL_CONTROLLERMODE_CM_MODIFIER_VIEW_PANLOCK and ADL_CONTROLLERMODE_CM_MODIFIER_VIEW_SIZE + int iModifiers; + + /// Horizontal view starting position + int iViewPositionCx; + + /// Vertical view starting position + int iViewPositionCy; + + /// Horizontal left panlock position + int iViewPanLockLeft; + + /// Horizontal right panlock position + int iViewPanLockRight; + + /// Vertical top panlock position + int iViewPanLockTop; + + /// Vertical bottom panlock position + int iViewPanLockBottom; + + /// View resolution in pixels (width) + int iViewResolutionCx; + + /// View resolution in pixels (hight) + int iViewResolutionCy; +}ADLControllerMode; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about a display +/// +/// This structure is used to store information about a display +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayIdentifier +{ + /// ADL display index + long ulDisplayIndex; + + /// manufacturer ID of the display + long ulManufacturerId; + + /// product ID of the display + long ulProductId; + + /// serial number of the display + long ulSerialNo; +} ADLDisplayIdentifier; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 clock range +/// +/// This structure is used to store information about Overdrive 6 clock range +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6ParameterRange +{ + /// The starting value of the clock range + int iMin; + /// The ending value of the clock range + int iMax; + /// The minimum increment between clock values + int iStep; +} ADLOD6ParameterRange; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 capabilities +/// +/// This structure is used to store information about Overdrive 6 capabilities +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6Capabilities +{ + /// Contains a bitmap of the OD6 capability flags. Possible values: \ref ADL_OD6_CAPABILITY_SCLK_CUSTOMIZATION, + /// \ref ADL_OD6_CAPABILITY_MCLK_CUSTOMIZATION, \ref ADL_OD6_CAPABILITY_GPU_ACTIVITY_MONITOR + int iCapabilities; + /// Contains a bitmap indicating the power states + /// supported by OD6. Currently only the performance state + /// is supported. Possible Values: \ref ADL_OD6_SUPPORTEDSTATE_PERFORMANCE + int iSupportedStates; + /// Number of levels. OD6 will always use 2 levels, which describe + /// the minimum to maximum clock ranges. + /// The 1st level indicates the minimum clocks, and the 2nd level + /// indicates the maximum clocks. + int iNumberOfPerformanceLevels; + /// Contains the hard limits of the sclk range. Overdrive + /// clocks cannot be set outside this range. + ADLOD6ParameterRange sEngineClockRange; + /// Contains the hard limits of the mclk range. Overdrive + /// clocks cannot be set outside this range. + ADLOD6ParameterRange sMemoryClockRange; + + /// Value for future extension + int iExtValue; + /// Mask for future extension + int iExtMask; +} ADLOD6Capabilities; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 clock values. +/// +/// This structure is used to store information about Overdrive 6 clock values. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6PerformanceLevel +{ + /// Engine (core) clock. + int iEngineClock; + /// Memory clock. + int iMemoryClock; +} ADLOD6PerformanceLevel; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 clocks. +/// +/// This structure is used to store information about Overdrive 6 clocks. This is a +/// variable-sized structure. iNumberOfPerformanceLevels indicate how many elements +/// are contained in the aLevels array. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6StateInfo +{ + /// Number of levels. OD6 uses clock ranges instead of discrete performance levels. + /// iNumberOfPerformanceLevels is always 2. The 1st level indicates the minimum clocks + /// in the range. The 2nd level indicates the maximum clocks in the range. + int iNumberOfPerformanceLevels; + + /// Value for future extension + int iExtValue; + /// Mask for future extension + int iExtMask; + + /// Variable-sized array of levels. + /// The number of elements in the array is specified by iNumberofPerformanceLevels. + ADLOD6PerformanceLevel aLevels [1]; +} ADLOD6StateInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about current Overdrive 6 performance status. +/// +/// This structure is used to store information about current Overdrive 6 performance status. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6CurrentStatus +{ + /// Current engine clock in 10 KHz. + int iEngineClock; + /// Current memory clock in 10 KHz. + int iMemoryClock; + /// Current GPU activity in percent. This + /// indicates how "busy" the GPU is. + int iActivityPercent; + /// Not used. Reserved for future use. + int iCurrentPerformanceLevel; + /// Current PCI-E bus speed + int iCurrentBusSpeed; + /// Current PCI-E bus # of lanes + int iCurrentBusLanes; + /// Maximum possible PCI-E bus # of lanes + int iMaximumBusLanes; + + /// Value for future extension + int iExtValue; + /// Mask for future extension + int iExtMask; +} ADLOD6CurrentStatus; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 thermal contoller capabilities +/// +/// This structure is used to store information about Overdrive 6 thermal controller capabilities +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6ThermalControllerCaps +{ + /// Contains a bitmap of thermal controller capability flags. Possible values: \ref ADL_OD6_TCCAPS_THERMAL_CONTROLLER, \ref ADL_OD6_TCCAPS_FANSPEED_CONTROL, + /// \ref ADL_OD6_TCCAPS_FANSPEED_PERCENT_READ, \ref ADL_OD6_TCCAPS_FANSPEED_PERCENT_WRITE, \ref ADL_OD6_TCCAPS_FANSPEED_RPM_READ, \ref ADL_OD6_TCCAPS_FANSPEED_RPM_WRITE + int iCapabilities; + /// Minimum fan speed expressed as a percentage + int iFanMinPercent; + /// Maximum fan speed expressed as a percentage + int iFanMaxPercent; + /// Minimum fan speed expressed in revolutions-per-minute + int iFanMinRPM; + /// Maximum fan speed expressed in revolutions-per-minute + int iFanMaxRPM; + + /// Value for future extension + int iExtValue; + /// Mask for future extension + int iExtMask; +} ADLOD6ThermalControllerCaps; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 fan speed information +/// +/// This structure is used to store information about Overdrive 6 fan speed information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6FanSpeedInfo +{ + /// Contains a bitmap of the valid fan speed type flags. Possible values: \ref ADL_OD6_FANSPEED_TYPE_PERCENT, \ref ADL_OD6_FANSPEED_TYPE_RPM, \ref ADL_OD6_FANSPEED_USER_DEFINED + int iSpeedType; + /// Contains current fan speed in percent (if valid flag exists in iSpeedType) + int iFanSpeedPercent; + /// Contains current fan speed in RPM (if valid flag exists in iSpeedType) + int iFanSpeedRPM; + + /// Value for future extension + int iExtValue; + /// Mask for future extension + int iExtMask; +} ADLOD6FanSpeedInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 fan speed value +/// +/// This structure is used to store information about Overdrive 6 fan speed value +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6FanSpeedValue +{ + /// Indicates the units of the fan speed. Possible values: \ref ADL_OD6_FANSPEED_TYPE_PERCENT, \ref ADL_OD6_FANSPEED_TYPE_RPM + int iSpeedType; + /// Fan speed value (units as indicated above) + int iFanSpeed; + + /// Value for future extension + int iExtValue; + /// Mask for future extension + int iExtMask; +} ADLOD6FanSpeedValue; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 PowerControl settings. +/// +/// This structure is used to store information about Overdrive 6 PowerControl settings. +/// PowerControl is the feature which allows the performance characteristics of the GPU +/// to be adjusted by changing the PowerTune power limits. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6PowerControlInfo +{ + /// The minimum PowerControl adjustment value + int iMinValue; + /// The maximum PowerControl adjustment value + int iMaxValue; + /// The minimum difference between PowerControl adjustment values + int iStepValue; + + /// Value for future extension + int iExtValue; + /// Mask for future extension + int iExtMask; +} ADLOD6PowerControlInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 PowerControl settings. +/// +/// This structure is used to store information about Overdrive 6 PowerControl settings. +/// PowerControl is the feature which allows the performance characteristics of the GPU +/// to be adjusted by changing the PowerTune power limits. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6VoltageControlInfo +{ + /// The minimum VoltageControl adjustment value + int iMinValue; + /// The maximum VoltageControl adjustment value + int iMaxValue; + /// The minimum difference between VoltageControl adjustment values + int iStepValue; + + /// Value for future extension + int iExtValue; + /// Mask for future extension + int iExtMask; +} ADLOD6VoltageControlInfo; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing ECC statistics namely SEC counts and DED counts +/// Single error count - count of errors that can be corrected +/// Doubt Error Detect - count of errors that cannot be corrected +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLECCData +{ + // Single error count - count of errors that can be corrected + int iSec; + // Double error detect - count of errors that cannot be corrected + int iDed; +} ADLECCData; + +/// \brief Handle to ADL client context. +/// +/// ADL clients obtain context handle from initial call to \ref ADL2_Main_Control_Create. +/// Clients have to pass the handle to each subsequent ADL call and finally destroy +/// the context with call to \ref ADL2_Main_Control_Destroy +/// \nosubgrouping +typedef void *ADL_CONTEXT_HANDLE; + +/// \brief Handle to ADL Frame Monitor Token. +/// +/// Frame Monitor clients obtain handle from initial call to \ref ADL2_Adapter_FrameMetrics_FrameDuration_Enable +/// Clients have to pass the handle to each subsequent ADL call to \ref ADL2_Adapter_FrameMetrics_FrameDuration_Get +/// and finally destroy the token with call to \ref ADL2_Adapter_FrameMetrics_FrameDuration_Disable +/// \nosubgrouping +typedef void *ADL_FRAME_DURATION_HANDLE; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the display mode definition used per controller. +/// +/// This structure is used to store the display mode definition used per controller. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayModeX2 +{ +/// Horizontal resolution (in pixels). + int iWidth; +/// Vertical resolution (in lines). + int iHeight; +/// Interlaced/Progressive. The value will be set for Interlaced as ADL_DL_TIMINGFLAG_INTERLACED. If not set it is progressive. Refer define_detailed_timing_flags. + int iScanType; +/// Refresh rate. + int iRefreshRate; +/// Timing Standard. Refer define_modetiming_standard. + int iTimingStandard; +} ADLDisplayModeX2; + +typedef enum ADLAppProcessState +{ + APP_PROC_INVALID = 0, // Invalid Application + APP_PROC_PREMPTION = 1, // The Application is being set up for Process Creation + APP_PROC_CREATION = 2, // The Application's Main Process is created by the OS + APP_PROC_READ = 3, // The Application's Data is ready to be read + APP_PROC_WAIT = 4, // The Application is waiting for Timeout or Notification to Resume + APP_PROC_RUNNING = 5, // The Application is running + APP_PROC_TERMINATE = 6 // The Application is about to terminate +}ADLAppProcessState; + +typedef enum ADLAppInterceptionListType +{ + ADL_INVALID_FORMAT = 0, + ADL_IMAGEFILEFORMAT = 1, + ADL_ENVVAR = 2 +}ADLAppInterceptionListType; + +typedef struct ADLAppInterceptionInfo +{ + wchar_t AppName[ADL_MAX_PATH]; // the file name of the application or env var + unsigned int ProcessId; + ADLAppInterceptionListType AppFormat; + ADLAppProcessState AppState; +} ADLAppInterceptionInfo; + +typedef enum ADL_AP_DATABASE // same as _SHARED_AP_DATABASE in "inc/shared/shared_escape.h" +{ + ADL_AP_DATABASE__SYSTEM, + ADL_AP_DATABASE__USER, + ADL_AP_DATABASE__OEM +} ADL_AP_DATABASE; + +typedef struct ADLAppInterceptionInfoX2 +{ + wchar_t AppName[ADL_MAX_PATH]; // the file name of the application or env var + unsigned int ProcessId; + unsigned int WaitForResumeNeeded; + wchar_t CommandLine[ADL_MAX_PATH]; // The command line on app start/stop event + ADLAppInterceptionListType AppFormat; + ADLAppProcessState AppState; +} ADLAppInterceptionInfoX2; + +typedef struct ADLAppInterceptionInfoX3 +{ + wchar_t AppName[ADL_MAX_PATH]; // the file name of the application or env var + unsigned int ProcessId; + unsigned int WaitForResumeNeeded; + unsigned int RayTracingStatus; // returns the Ray Tracing status if it is enabled atleast once in session. + wchar_t CommandLine[ADL_MAX_PATH]; // The command line on app start/stop event + ADLAppInterceptionListType AppFormat; + ADLAppProcessState AppState; +} ADLAppInterceptionInfoX3; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information info for a property record in a profile +/// +/// This structure is used to store info for a property record in a profile +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPropertyRecordCreate +{ + /// Name of the property + wchar_t * strPropertyName; + /// Data type of the property + ADLProfilePropertyType eType; + // Value of the property + wchar_t * strPropertyValue; +} ADLPropertyRecordCreate; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information info for an application record +/// +/// This structure is used to store info for an application record +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLApplicationRecord +{ + /// Title of the application + wchar_t * strTitle; + /// File path of the application + wchar_t * strPathName; + /// File name of the application + wchar_t * strFileName; + /// File versin the application + wchar_t * strVersion; + /// Nostes on the application + wchar_t * strNotes; + /// Driver area which the application uses + wchar_t * strArea; + /// Name of profile assigned to the application + wchar_t * strProfileName; + // Source where this application record come from + ADL_AP_DATABASE recordSource; +} ADLApplicationRecord; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 extension capabilities +/// +/// This structure is used to store information about Overdrive 6 extension capabilities +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6CapabilitiesEx +{ + /// Contains a bitmap of the OD6 extension capability flags. Possible values: \ref ADL_OD6_CAPABILITY_SCLK_CUSTOMIZATION, + /// \ref ADL_OD6_CAPABILITY_MCLK_CUSTOMIZATION, \ref ADL_OD6_CAPABILITY_GPU_ACTIVITY_MONITOR, + /// \ref ADL_OD6_CAPABILITY_POWER_CONTROL, \ref ADL_OD6_CAPABILITY_VOLTAGE_CONTROL, \ref ADL_OD6_CAPABILITY_PERCENT_ADJUSTMENT, + //// \ref ADL_OD6_CAPABILITY_THERMAL_LIMIT_UNLOCK + int iCapabilities; + /// The Power states that support clock and power customization. Only performance state is currently supported. + /// Possible Values: \ref ADL_OD6_SUPPORTEDSTATE_PERFORMANCE + int iSupportedStates; + /// Returns the hard limits of the SCLK overdrive adjustment range. Overdrive clocks should not be adjusted outside of this range. The values are specified as +/- percentages. + ADLOD6ParameterRange sEngineClockPercent; + /// Returns the hard limits of the MCLK overdrive adjustment range. Overdrive clocks should not be adjusted outside of this range. The values are specified as +/- percentages. + ADLOD6ParameterRange sMemoryClockPercent; + /// Returns the hard limits of the Power Limit adjustment range. Power limit should not be adjusted outside this range. The values are specified as +/- percentages. + ADLOD6ParameterRange sPowerControlPercent; + /// Reserved for future expansion of the structure. + int iExtValue; + /// Reserved for future expansion of the structure. + int iExtMask; +} ADLOD6CapabilitiesEx; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 extension state information +/// +/// This structure is used to store information about Overdrive 6 extension state information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6StateEx +{ + /// The current engine clock adjustment value, specified as a +/- percent. + int iEngineClockPercent; + /// The current memory clock adjustment value, specified as a +/- percent. + int iMemoryClockPercent; + /// The current power control adjustment value, specified as a +/- percent. + int iPowerControlPercent; + /// Reserved for future expansion of the structure. + int iExtValue; + /// Reserved for future expansion of the structure. + int iExtMask; +} ADLOD6StateEx; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive 6 extension recommended maximum clock adjustment values +/// +/// This structure is used to store information about Overdrive 6 extension recommended maximum clock adjustment values +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD6MaxClockAdjust +{ + /// The recommended maximum engine clock adjustment in percent, for the specified power limit value. + int iEngineClockMax; + /// The recommended maximum memory clock adjustment in percent, for the specified power limit value. + /// Currently the memory is independent of the Power Limit setting, so iMemoryClockMax will always return the maximum + /// possible adjustment value. This field is here for future enhancement in case we add a dependency between Memory Clock + /// adjustment and Power Limit setting. + int iMemoryClockMax; + /// Reserved for future expansion of the structure. + int iExtValue; + /// Reserved for future expansion of the structure. + int iExtMask; +} ADLOD6MaxClockAdjust; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the Connector information +/// +/// this structure is used to get the connector information like length, positions & etc. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLConnectorInfo +{ + ///index of the connector(0-based) + int iConnectorIndex; + ///used for disply identification/ordering + int iConnectorId; + ///index of the slot, 0-based index. + int iSlotIndex; + ///Type of the connector. \ref define_connector_types + int iType; + ///Position of the connector(in millimeters), from the right side of the slot. + int iOffset; + ///Length of the connector(in millimeters). + int iLength; +} ADLConnectorInfo; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the slot information +/// +/// this structure is used to get the slot information like length of the slot, no of connectors on the slot & etc. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLBracketSlotInfo +{ + ///index of the slot, 0-based index. + int iSlotIndex; + ///length of the slot(in millimeters). + int iLength; + ///width of the slot(in millimeters). + int iWidth; +} ADLBracketSlotInfo; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing MST branch information +/// +/// this structure is used to store the MST branch information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLMSTRad +{ + ///depth of the link. + int iLinkNumber; + /// Relative address, address scheme starts from source side + char rad[ADL_MAX_RAD_LINK_COUNT]; +} ADLMSTRad; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing port information +/// +/// this structure is used to get the display or MST branch information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDevicePort +{ + ///index of the connector. + int iConnectorIndex; + ///Relative MST address. If MST RAD contains 0 it means DP or Root of the MST topology. For non DP connectors MST RAD is ignored. + ADLMSTRad aMSTRad; +} ADLDevicePort; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing supported connection types and properties +/// +/// this structure is used to get the supported connection types and supported properties of given connector +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSupportedConnections +{ + ///Bit vector of supported connections. Bitmask is defined in constants section. \ref define_connection_types + int iSupportedConnections; + ///Array of bitvectors. Each bit vector represents supported properties for one connection type. Index of this array is connection type (bit number in mask). + int iSupportedProperties[ADL_MAX_CONNECTION_TYPES]; +} ADLSupportedConnections; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing connection state of the connector +/// +/// this structure is used to get the current Emulation status and mode of the given connector +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLConnectionState +{ + ///The value is bit vector. Each bit represents status. See masks constants for details. \ref define_emulation_status + int iEmulationStatus; + ///It contains information about current emulation mode. See constants for details. \ref define_emulation_mode + int iEmulationMode; + ///If connection is active it will contain display id, otherwise CWDDEDI_INVALID_DISPLAY_INDEX + int iDisplayIndex; +} ADLConnectionState; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing connection properties information +/// +/// this structure is used to retrieve the properties of connection type +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLConnectionProperties +{ + //Bit vector. Represents actual properties. Supported properties for specific connection type. \ref define_connection_properties + int iValidProperties; + //Bitrate(in MHz). Could be used for MST branch, DP or DP active dongle. \ref define_linkrate_constants + int iBitrate; + //Number of lanes in DP connection. \ref define_lanecount_constants + int iNumberOfLanes; + //Color depth(in bits). \ref define_colordepth_constants + int iColorDepth; + //3D capabilities. It could be used for some dongles. For instance: alternate framepack. Value of this property is bit vector. + int iStereo3DCaps; + ///Output Bandwidth. Could be used for MST branch, DP or DP Active dongle. \ref define_linkrate_constants + int iOutputBandwidth; +} ADLConnectionProperties; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing connection information +/// +/// this structure is used to retrieve the data from driver which includes +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLConnectionData +{ + ///Connection type. based on the connection type either iNumberofPorts or IDataSize,EDIDdata is valid, \ref define_connection_types + int iConnectionType; + ///Specifies the connection properties. + ADLConnectionProperties aConnectionProperties; + ///Number of ports + int iNumberofPorts; + ///Number of Active Connections + int iActiveConnections; + ///actual size of EDID data block size. + int iDataSize; + ///EDID Data + char EdidData[ADL_MAX_DISPLAY_EDID_DATA_SIZE]; +} ADLConnectionData; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about an controller mode including Number of Connectors +/// +/// This structure is used to store information of an controller mode +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLAdapterCapsX2 +{ + /// AdapterID for this adapter + int iAdapterID; + /// Number of controllers for this adapter + int iNumControllers; + /// Number of displays for this adapter + int iNumDisplays; + /// Number of overlays for this adapter + int iNumOverlays; + /// Number of GLSyncConnectors + int iNumOfGLSyncConnectors; + /// The bit mask identifies the adapter caps + int iCapsMask; + /// The bit identifies the adapter caps \ref define_adapter_caps + int iCapsValue; + /// Number of Connectors for this adapter + int iNumConnectors; +}ADLAdapterCapsX2; + +typedef enum ADL_ERROR_RECORD_SEVERITY +{ + ADL_GLOBALLY_UNCORRECTED = 1, + ADL_LOCALLY_UNCORRECTED = 2, + ADL_DEFFERRED = 3, + ADL_CORRECTED = 4 +}ADL_ERROR_RECORD_SEVERITY; + +typedef union _ADL_ECC_EDC_FLAG +{ + struct + { + unsigned int isEccAccessing : 1; + unsigned int reserved : 31; + }bits; + unsigned int u32All; +}ADL_ECC_EDC_FLAG; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about EDC Error Record +/// +/// This structure is used to store EDC Error Record +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLErrorRecord +{ + // Severity of error + ADL_ERROR_RECORD_SEVERITY Severity; + + // Is the counter valid? + int countValid; + + // Counter value, if valid + unsigned int count; + + // Is the location information valid? + int locationValid; + + // Physical location of error + unsigned int CU; // CU number on which error occurred, if known + char StructureName[32]; // e.g. LDS, TCC, etc. + + // Time of error record creation (e.g. time of query, or time of poison interrupt) + char tiestamp[32]; + + unsigned int padding[3]; +}ADLErrorRecord; + +typedef enum ADL_EDC_BLOCK_ID +{ + ADL_EDC_BLOCK_ID_SQCIS = 1, + ADL_EDC_BLOCK_ID_SQCDS = 2, + ADL_EDC_BLOCK_ID_SGPR = 3, + ADL_EDC_BLOCK_ID_VGPR = 4, + ADL_EDC_BLOCK_ID_LDS = 5, + ADL_EDC_BLOCK_ID_GDS = 6, + ADL_EDC_BLOCK_ID_TCL1 = 7, + ADL_EDC_BLOCK_ID_TCL2 = 8 +}ADL_EDC_BLOCK_ID; + +typedef enum ADL_ERROR_INJECTION_MODE +{ + ADL_ERROR_INJECTION_MODE_SINGLE = 1, + ADL_ERROR_INJECTION_MODE_MULTIPLE = 2, + ADL_ERROR_INJECTION_MODE_ADDRESS = 3 +}ADL_ERROR_INJECTION_MODE; + +typedef union _ADL_ERROR_PATTERN +{ + struct + { + unsigned long EccInjVector : 16; + unsigned long EccInjEn : 9; + unsigned long EccBeatEn : 4; + unsigned long EccChEn : 4; + unsigned long reserved : 31; + } bits; + unsigned long long u64Value; +} ADL_ERROR_PATTERN; + +typedef struct ADL_ERROR_INJECTION_DATA +{ + unsigned long long errorAddress; + ADL_ERROR_PATTERN errorPattern; +}ADL_ERROR_INJECTION_DATA; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about EDC Error Injection +/// +/// This structure is used to store EDC Error Injection +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLErrorInjection +{ + ADL_EDC_BLOCK_ID blockId; + ADL_ERROR_INJECTION_MODE errorInjectionMode; +}ADLErrorInjection; + +typedef struct ADLErrorInjectionX2 +{ + ADL_EDC_BLOCK_ID blockId; + ADL_ERROR_INJECTION_MODE errorInjectionMode; + ADL_ERROR_INJECTION_DATA errorInjectionData; +}ADLErrorInjectionX2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing per display FreeSync capability information. +/// +/// This structure is used to store the FreeSync capability of both the display and +/// the GPU the display is connected to. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLFreeSyncCap +{ + /// FreeSync capability flags. \ref define_freesync_caps + int iCaps; + /// Reports minimum FreeSync refresh rate supported by the display in micro hertz + int iMinRefreshRateInMicroHz; + /// Reports maximum FreeSync refresh rate supported by the display in micro hertz + int iMaxRefreshRateInMicroHz; + /// Index of FreeSync Label to use: ADL_FREESYNC_LABEL_* + unsigned char ucLabelIndex; + /// Reserved + char cReserved[3]; + int iReserved[4]; +} ADLFreeSyncCap; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing per display Display Connectivty Experience Settings +/// +/// This structure is used to store the Display Connectivity Experience settings of a +/// display +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDceSettings +{ + DceSettingsType type; // Defines which structure is in the union below + union + { + struct + { + bool qualityDetectionEnabled; + } HdmiLq; + struct + { + DpLinkRate linkRate; // Read-only + unsigned int numberOfActiveLanes; // Read-only + unsigned int numberofTotalLanes; // Read-only + int relativePreEmphasis; // Allowable values are -2 to +2 + int relativeVoltageSwing; // Allowable values are -2 to +2 + int persistFlag; + } DpLink; + struct + { + bool linkProtectionEnabled; // Read-only + } Protection; + } Settings; + int iReserved[15]; +} ADLDceSettings; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Graphic Core +/// +/// This structure is used to get Graphic Core Info +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLGraphicCoreInfo +{ + /// indicate the graphic core generation + int iGCGen; + + union + { + /// Total number of CUs. Valid for GCN (iGCGen == GCN) + int iNumCUs; + /// Total number of WGPs. Valid for RDNA (iGCGen == RDNA) + int iNumWGPs; + }; + + union + { + /// Number of processing elements per CU. Valid for GCN (iGCGen == GCN) + int iNumPEsPerCU; + /// Number of processing elements per WGP. Valid for RDNA (iGCGen == RDNA) + int iNumPEsPerWGP; + }; + + /// Total number of SIMDs. Valid for Pre GCN (iGCGen == Pre-GCN) + int iNumSIMDs; + + /// Total number of ROPs. Valid for both GCN and Pre GCN + int iNumROPs; + + /// reserved for future use + int iReserved[11]; +}ADLGraphicCoreInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive N clock range +/// +/// This structure is used to store information about Overdrive N clock range +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODNParameterRange +{ + /// The starting value of the clock range + int iMode; + /// The starting value of the clock range + int iMin; + /// The ending value of the clock range + int iMax; + /// The minimum increment between clock values + int iStep; + /// The default clock values + int iDefault; +} ADLODNParameterRange; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive N capabilities +/// +/// This structure is used to store information about Overdrive N capabilities +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODNCapabilities +{ + /// Number of levels which describe the minimum to maximum clock ranges. + /// The 1st level indicates the minimum clocks, and the 2nd level + /// indicates the maximum clocks. + int iMaximumNumberOfPerformanceLevels; + /// Contains the hard limits of the sclk range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange sEngineClockRange; + /// Contains the hard limits of the mclk range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange sMemoryClockRange; + /// Contains the hard limits of the vddc range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange svddcRange; + /// Contains the hard limits of the power range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange power; + /// Contains the hard limits of the power range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange powerTuneTemperature; + /// Contains the hard limits of the Temperature range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange fanTemperature; + /// Contains the hard limits of the Fan range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange fanSpeed; + /// Contains the hard limits of the Fan range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange minimumPerformanceClock; +} ADLODNCapabilities; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive N capabilities +/// +/// This structure is used to store information about Overdrive N capabilities +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODNCapabilitiesX2 +{ + /// Number of levels which describe the minimum to maximum clock ranges. + /// The 1st level indicates the minimum clocks, and the 2nd level + /// indicates the maximum clocks. + int iMaximumNumberOfPerformanceLevels; + /// bit vector, which tells what are the features are supported. + /// \ref: ADLODNFEATURECONTROL + int iFlags; + /// Contains the hard limits of the sclk range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange sEngineClockRange; + /// Contains the hard limits of the mclk range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange sMemoryClockRange; + /// Contains the hard limits of the vddc range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange svddcRange; + /// Contains the hard limits of the power range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange power; + /// Contains the hard limits of the power range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange powerTuneTemperature; + /// Contains the hard limits of the Temperature range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange fanTemperature; + /// Contains the hard limits of the Fan range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange fanSpeed; + /// Contains the hard limits of the Fan range. Overdrive + /// clocks cannot be set outside this range. + ADLODNParameterRange minimumPerformanceClock; + /// Contains the hard limits of the throttleNotification + ADLODNParameterRange throttleNotificaion; + /// Contains the hard limits of the Auto Systemclock + ADLODNParameterRange autoSystemClock; +} ADLODNCapabilitiesX2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive level. +/// +/// This structure is used to store information about Overdrive level. +/// This structure is used by ADLODPerformanceLevels. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODNPerformanceLevel +{ + /// clock. + int iClock; + /// VDCC. + int iVddc; + /// enabled + int iEnabled; +} ADLODNPerformanceLevel; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive N performance levels. +/// +/// This structure is used to store information about Overdrive performance levels. +/// This structure is used by the ADL_OverdriveN_ODPerformanceLevels_Get() and ADL_OverdriveN_ODPerformanceLevels_Set() functions. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODNPerformanceLevels +{ + int iSize; + //Automatic/manual + int iMode; + /// Must be set to sizeof( \ref ADLODPerformanceLevels ) + sizeof( \ref ADLODPerformanceLevel ) * (ADLODParameters.iNumberOfPerformanceLevels - 1) + int iNumberOfPerformanceLevels; + /// Array of performance state descriptors. Must have ADLODParameters.iNumberOfPerformanceLevels elements. + ADLODNPerformanceLevel aLevels[1]; +} ADLODNPerformanceLevels; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive N Fan Speed. +/// +/// This structure is used to store information about Overdrive Fan control . +/// This structure is used by the ADL_OverdriveN_ODPerformanceLevels_Get() and ADL_OverdriveN_ODPerformanceLevels_Set() functions. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODNFanControl +{ + int iMode; + int iFanControlMode; + int iCurrentFanSpeedMode; + int iCurrentFanSpeed; + int iTargetFanSpeed; + int iTargetTemperature; + int iMinPerformanceClock; + int iMinFanLimit; +} ADLODNFanControl; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive N power limit. +/// +/// This structure is used to store information about Overdrive power limit. +/// This structure is used by the ADL_OverdriveN_ODPerformanceLevels_Get() and ADL_OverdriveN_ODPerformanceLevels_Set() functions. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODNPowerLimitSetting +{ + int iMode; + int iTDPLimit; + int iMaxOperatingTemperature; +} ADLODNPowerLimitSetting; + +typedef struct ADLODNPerformanceStatus +{ + int iCoreClock; + int iMemoryClock; + int iDCEFClock; + int iGFXClock; + int iUVDClock; + int iVCEClock; + int iGPUActivityPercent; + int iCurrentCorePerformanceLevel; + int iCurrentMemoryPerformanceLevel; + int iCurrentDCEFPerformanceLevel; + int iCurrentGFXPerformanceLevel; + int iUVDPerformanceLevel; + int iVCEPerformanceLevel; + int iCurrentBusSpeed; + int iCurrentBusLanes; + int iMaximumBusLanes; + int iVDDC; + int iVDDCI; +} ADLODNPerformanceStatus; + +///\brief Structure containing information about Overdrive level. +/// +/// This structure is used to store information about Overdrive level. +/// This structure is used by ADLODPerformanceLevels. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODNPerformanceLevelX2 +{ + /// clock. + int iClock; + /// VDCC. + int iVddc; + /// enabled + int iEnabled; + /// MASK + int iControl; +} ADLODNPerformanceLevelX2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive N performance levels. +/// +/// This structure is used to store information about Overdrive performance levels. +/// This structure is used by the ADL_OverdriveN_ODPerformanceLevels_Get() and ADL_OverdriveN_ODPerformanceLevels_Set() functions. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLODNPerformanceLevelsX2 +{ + int iSize; + //Automatic/manual + int iMode; + /// Must be set to sizeof( \ref ADLODPerformanceLevels ) + sizeof( \ref ADLODPerformanceLevel ) * (ADLODParameters.iNumberOfPerformanceLevels - 1) + int iNumberOfPerformanceLevels; + /// Array of performance state descriptors. Must have ADLODParameters.iNumberOfPerformanceLevels elements. + ADLODNPerformanceLevelX2 aLevels[1]; +} ADLODNPerformanceLevelsX2; + +typedef enum ADLODNCurrentPowerType +{ + ODN_GPU_TOTAL_POWER = 0, + ODN_GPU_PPT_POWER, + ODN_GPU_SOCKET_POWER, + ODN_GPU_CHIP_POWER +} ADLODNCurrentPowerType; + +// in/out: CWDDEPM_CURRENTPOWERPARAMETERS +typedef struct ADLODNCurrentPowerParameters +{ + int size; + ADLODNCurrentPowerType powerType; + int currentPower; +} ADLODNCurrentPowerParameters; + +//ODN Ext range data structure +typedef struct ADLODNExtSingleInitSetting +{ + int mode; + int minValue; + int maxValue; + int step; + int defaultValue; +} ADLODNExtSingleInitSetting; + +//OD8 Ext range data structure +typedef struct ADLOD8SingleInitSetting +{ + int featureID; + int minValue; + int maxValue; + int defaultValue; +} ADLOD8SingleInitSetting; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive8 initial setting +/// +/// This structure is used to store information about Overdrive8 initial setting +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD8InitSetting +{ + int count; + int overdrive8Capabilities; + ADLOD8SingleInitSetting od8SettingTable[OD8_COUNT]; +} ADLOD8InitSetting; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive8 current setting +/// +/// This structure is used to store information about Overdrive8 current setting +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLOD8CurrentSetting +{ + int count; + int Od8SettingTable[OD8_COUNT]; +} ADLOD8CurrentSetting; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Overdrive8 set setting +/// +/// This structure is used to store information about Overdrive8 set setting +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLOD8SingleSetSetting +{ + int value; + int requested; // 0 - default , 1 - requested + int reset; // 0 - do not reset , 1 - reset setting back to default +} ADLOD8SingleSetSetting; + +typedef struct ADLOD8SetSetting +{ + int count; + ADLOD8SingleSetSetting od8SettingTable[OD8_COUNT]; +} ADLOD8SetSetting; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Performance Metrics data +/// +/// This structure is used to store information about Performance Metrics data output +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSingleSensorData +{ + int supported; + int value; +} ADLSingleSensorData; + +typedef struct ADLPMLogDataOutput +{ + int size; + ADLSingleSensorData sensors[ADL_PMLOG_MAX_SENSORS]; +}ADLPMLogDataOutput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about PPLog settings. +/// +/// This structure is used to store information about PPLog settings. +/// This structure is used by the ADL2_PPLogSettings_Set() and ADL2_PPLogSettings_Get() functions. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPPLogSettings +{ + int BreakOnAssert; + int BreakOnWarn; + int LogEnabled; + int LogFieldMask; + int LogDestinations; + int LogSeverityEnabled; + int LogSourceMask; + int PowerProfilingEnabled; + int PowerProfilingTimeInterval; +}ADLPPLogSettings; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related Frames Per Second for AC and DC. +/// +/// This structure is used to store information related AC and DC Frames Per Second settings +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLFPSSettingsOutput +{ + /// size + int ulSize; + /// FPS Monitor is enabled in the AC state if 1 + int bACFPSEnabled; + /// FPS Monitor is enabled in the DC state if 1 + int bDCFPSEnabled; + /// Current Value of FPS Monitor in AC state + int ulACFPSCurrent; + /// Current Value of FPS Monitor in DC state + int ulDCFPSCurrent; + /// Maximum FPS Threshold allowed in PPLib for AC + int ulACFPSMaximum; + /// Minimum FPS Threshold allowed in PPLib for AC + int ulACFPSMinimum; + /// Maximum FPS Threshold allowed in PPLib for DC + int ulDCFPSMaximum; + /// Minimum FPS Threshold allowed in PPLib for DC + int ulDCFPSMinimum; +} ADLFPSSettingsOutput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related Frames Per Second for AC and DC. +/// +/// This structure is used to store information related AC and DC Frames Per Second settings +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLFPSSettingsInput +{ + /// size + int ulSize; + /// Settings are for Global FPS (used by CCC) + int bGlobalSettings; + /// Current Value of FPS Monitor in AC state + int ulACFPSCurrent; + /// Current Value of FPS Monitor in DC state + int ulDCFPSCurrent; + /// Reserved + int ulReserved[6]; +} ADLFPSSettingsInput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related power management logging. +/// +/// This structure is used to store support information for power management logging. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +enum { ADL_PMLOG_MAX_SUPPORTED_SENSORS = 256 }; + +typedef struct ADLPMLogSupportInfo +{ + /// list of sensors defined by ADL_PMLOG_SENSORS + unsigned short usSensors[ADL_PMLOG_MAX_SUPPORTED_SENSORS]; + /// Reserved + int ulReserved[16]; +} ADLPMLogSupportInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information to start power management logging. +/// +/// This structure is used as input to ADL2_Adapter_PMLog_Start +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPMLogStartInput +{ + /// list of sensors defined by ADL_PMLOG_SENSORS + unsigned short usSensors[ADL_PMLOG_MAX_SUPPORTED_SENSORS]; + /// Sample rate in milliseconds + unsigned long ulSampleRate; + /// Reserved + int ulReserved[15]; +} ADLPMLogStartInput; + +typedef struct ADLPMLogData +{ + /// Structure version + unsigned int ulVersion; + /// Current driver sample rate + unsigned int ulActiveSampleRate; + /// Timestamp of last update + unsigned long long ulLastUpdated; + /// 2D array of senesor and values + unsigned int ulValues[ADL_PMLOG_MAX_SUPPORTED_SENSORS][2]; + /// Reserved + unsigned int ulReserved[256]; +} ADLPMLogData; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information to start power management logging. +/// +/// This structure is returned as output from ADL2_Adapter_PMLog_Start +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPMLogStartOutput +{ + /// Pointer to memory address containing logging data + union + { + void* pLoggingAddress; + unsigned long long ptr_LoggingAddress; + }; + /// Reserved + int ulReserved[14]; +} ADLPMLogStartOutput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information to query limts of power management logging. +/// +/// This structure is returned as output from ADL2_Adapter_PMLog_SensorLimits_Get +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLPMLogSensorLimits +{ + int SensorLimits[ADL_PMLOG_MAX_SENSORS][2]; //index 0: min, 1: max +} ADLPMLogSensorLimits; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related RAS Get Error Counts Information +/// +/// This structure is used to store RAS Error Counts Get Input Information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLRASGetErrorCountsInput +{ + unsigned int Reserved[16]; +} ADLRASGetErrorCountsInput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related RAS Get Error Counts Information +/// +/// This structure is used to store RAS Error Counts Get Output Information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLRASGetErrorCountsOutput +{ + unsigned int CorrectedErrors; // includes both DRAM and SRAM ECC + unsigned int UnCorrectedErrors; // includes both DRAM and SRAM ECC + unsigned int Reserved[14]; +} ADLRASGetErrorCountsOutput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related RAS Get Error Counts Information +/// +/// This structure is used to store RAS Error Counts Get Information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLRASGetErrorCounts +{ + unsigned int InputSize; + ADLRASGetErrorCountsInput Input; + unsigned int OutputSize; + ADLRASGetErrorCountsOutput Output; +} ADLRASGetErrorCounts; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related RAS Error Counts Reset Information +/// +/// This structure is used to store RAS Error Counts Reset Input Information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLRASResetErrorCountsInput +{ + unsigned int Reserved[8]; +} ADLRASResetErrorCountsInput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related RAS Error Counts Reset Information +/// +/// This structure is used to store RAS Error Counts Reset Output Information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLRASResetErrorCountsOutput +{ + unsigned int Reserved[8]; +} ADLRASResetErrorCountsOutput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related RAS Error Counts Reset Information +/// +/// This structure is used to store RAS Error Counts Reset Information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLRASResetErrorCounts +{ + unsigned int InputSize; + ADLRASResetErrorCountsInput Input; + unsigned int OutputSize; + ADLRASResetErrorCountsOutput Output; +} ADLRASResetErrorCounts; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related RAS Error Injection information +/// +/// This structure is used to store RAS Error Injection input information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLRASErrorInjectonInput +{ + unsigned long long Address; + ADL_RAS_INJECTION_METHOD Value; + ADL_RAS_BLOCK_ID BlockId; + ADL_RAS_ERROR_TYPE InjectErrorType; + ADL_MEM_SUB_BLOCK_ID SubBlockIndex; + unsigned int padding[9]; +} ADLRASErrorInjectonInput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related RAS Error Injection information +/// +/// This structure is used to store RAS Error Injection output information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLRASErrorInjectionOutput +{ + unsigned int ErrorInjectionStatus; + unsigned int padding[15]; +} ADLRASErrorInjectionOutput; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related RAS Error Injection information +/// +/// This structure is used to store RAS Error Injection information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLRASErrorInjection +{ + unsigned int InputSize; + ADLRASErrorInjectonInput Input; + unsigned int OutputSize; + ADLRASErrorInjectionOutput Output; +} ADLRASErrorInjection; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about an application +/// +/// This structure is used to store basic information of a recently ran or currently running application +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSGApplicationInfo +{ + /// Application file name + wchar_t strFileName[ADL_MAX_PATH]; + /// Application file path + wchar_t strFilePath[ADL_MAX_PATH]; + /// Application version + wchar_t strVersion[ADL_MAX_PATH]; + /// Timestamp at which application has run + long long int timeStamp; + /// Holds whether the applicaition profile exists or not + unsigned int iProfileExists; + /// The GPU on which application runs + unsigned int iGPUAffinity; + /// The BDF of the GPU on which application runs + ADLBdf GPUBdf; +} ADLSGApplicationInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related Frames Per Second for AC and DC. +/// +/// This structure is used to store information related AC and DC Frames Per Second settings +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +enum { ADLPreFlipPostProcessingInfoInvalidLUTIndex = 0xFFFFFFFF }; + +enum ADLPreFlipPostProcessingLUTAlgorithm +{ + ADLPreFlipPostProcessingLUTAlgorithm_Default = 0, + ADLPreFlipPostProcessingLUTAlgorithm_Full, + ADLPreFlipPostProcessingLUTAlgorithm_Approximation +}; + +typedef struct ADLPreFlipPostProcessingInfo +{ + /// size + int ulSize; + /// Current active state + int bEnabled; + /// Current selected LUT index. 0xFFFFFFF returned if nothing selected. + int ulSelectedLUTIndex; + /// Current selected LUT Algorithm + int ulSelectedLUTAlgorithm; + /// Reserved + int ulReserved[12]; +} ADLPreFlipPostProcessingInfo; + +typedef struct ADL_ERROR_REASON +{ + int boost; //ON, when boost is Enabled + int delag; //ON, when delag is Enabled + int chill; //ON, when chill is Enabled + int proVsr; //ON, when proVsr is Enabled +}ADL_ERROR_REASON; + +typedef struct ADL_ERROR_REASON2 +{ + int boost; //ON, when boost is Enabled + int delag; //ON, when delag is Enabled + int chill; //ON, when chill is Enabled + int proVsr; //ON, when proVsr is Enabled + int upscale; //ON, when RSR is Enabled +}ADL_ERROR_REASON2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about DELAG Settings change reason +/// +/// Elements of DELAG settings changed reason. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_DELAG_NOTFICATION_REASON +{ + int HotkeyChanged; //Set when Hotkey value is changed + int GlobalEnableChanged; //Set when Global enable value is changed + int GlobalLimitFPSChanged; //Set when Global enable value is changed +}ADL_DELAG_NOTFICATION_REASON; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about DELAG Settings +/// +/// Elements of DELAG settings. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_DELAG_SETTINGS +{ + int Hotkey; // Hotkey value + int GlobalEnable; //Global enable value + int GlobalLimitFPS; //Global Limit FPS + int GlobalLimitFPS_MinLimit; //Gloabl Limit FPS slider min limit value + int GlobalLimitFPS_MaxLimit; //Gloabl Limit FPS slider max limit value + int GlobalLimitFPS_Step; //Gloabl Limit FPS step value +}ADL_DELAG_SETTINGS; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about BOOST Settings change reason +/// +/// Elements of BOOST settings changed reason. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_BOOST_NOTFICATION_REASON +{ + int HotkeyChanged; //Set when Hotkey value is changed + int GlobalEnableChanged; //Set when Global enable value is changed + int GlobalMinResChanged; //Set when Global min resolution value is changed +}ADL_BOOST_NOTFICATION_REASON; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about BOOST Settings +/// +/// Elements of BOOST settings. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_BOOST_SETTINGS +{ + int Hotkey; // Hotkey value + int GlobalEnable; //Global enable value + int GlobalMinRes; //Gloabl Min Resolution value + int GlobalMinRes_MinLimit; //Gloabl Min Resolution slider min limit value + int GlobalMinRes_MaxLimit; //Gloabl Min Resolution slider max limit value + int GlobalMinRes_Step; //Gloabl Min Resolution step value +}ADL_BOOST_SETTINGS; + + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about ProVSR Settings change reason +/// +/// Elements of ProVSR settings changed reason. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_PROVSR_NOTFICATION_REASON +{ + int HotkeyChanged; //Set when Hotkey value is changed + int GlobalEnableChanged; //Set when Global enable value is changed +}ADL_PROVSR_NOTFICATION_REASON; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Pro VSR Settings +/// +/// Elements of ProVSR settings. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_PROVSR_SETTINGS +{ + int Hotkey; // Hotkey value + int GlobalEnable; //Global enable value +}ADL_PROVSR_SETTINGS; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about Image Boost(OGL) Settings change reason +/// +/// Elements of Image Boost settings changed reason. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_IMAGE_BOOST_NOTFICATION_REASON +{ + int HotkeyChanged; //Set when Hotkey value is changed + int GlobalEnableChanged; //Set when Global enable value is changed +}ADL_IMAGE_BOOST_NOTFICATION_REASON; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about OGL IMAGE BOOST Settings +/// +/// Elements of OGL IMAGE BOOST settings. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_IMAGE_BOOST_SETTINGS +{ + int Hotkey; // Hotkey value + int GlobalEnable; //Global enable value +}ADL_IMAGE_BOOST_SETTINGS; + +///////////////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about RIS Settings change reason +/// +/// Elements of RIS settings changed reason. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_RIS_NOTFICATION_REASON +{ + unsigned int GlobalEnableChanged; //Set when Global enable value is changed + unsigned int GlobalSharpeningDegreeChanged; //Set when Global sharpening Degree value is changed +}ADL_RIS_NOTFICATION_REASON; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about RIS Settings +/// +/// Elements of RIS settings. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_RIS_SETTINGS +{ + int GlobalEnable; //Global enable value + int GlobalSharpeningDegree; //Global sharpening value + int GlobalSharpeningDegree_MinLimit; //Gloabl sharpening slider min limit value + int GlobalSharpeningDegree_MaxLimit; //Gloabl sharpening slider max limit value + int GlobalSharpeningDegree_Step; //Gloabl sharpening step value +}ADL_RIS_SETTINGS; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about CHILL Settings change reason +/// +/// Elements of Chiil settings changed reason. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_CHILL_NOTFICATION_REASON +{ + int HotkeyChanged; //Set when Hotkey value is changed + int GlobalEnableChanged; //Set when Global enable value is changed + int GlobalMinFPSChanged; //Set when Global min FPS value is changed + int GlobalMaxFPSChanged; //Set when Global max FPS value is changed +}ADL_CHILL_NOTFICATION_REASON; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about CHILL Settings +/// +/// Elements of Chill settings. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_CHILL_SETTINGS +{ + int Hotkey; // Hotkey value + int GlobalEnable; //Global enable value + int GlobalMinFPS; //Global Min FPS value + int GlobalMaxFPS; //Global Max FPS value + int GlobalFPS_MinLimit; //Gloabl FPS slider min limit value + int GlobalFPS_MaxLimit; //Gloabl FPS slider max limit value + int GlobalFPS_Step; //Gloabl FPS Slider step value +}ADL_CHILL_SETTINGS; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about DRIVERUPSCALE Settings change reason +/// +/// Elements of DRIVERUPSCALE settings changed reason. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_DRIVERUPSCALE_NOTFICATION_REASON +{ + int ModeOverrideEnabledChanged; //Set when Global min resolution value is changed + int GlobalEnabledChanged; //Set when Global enable value is changed +}ADL_DRIVERUPSCALE_NOTFICATION_REASON; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about DRIVERUPSCALE Settings +/// +/// Elements of DRIVERUPSCALE settings. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_DRIVERUPSCALE_SETTINGS +{ + int ModeOverrideEnabled; + int GlobalEnabled; +}ADL_DRIVERUPSCALE_SETTINGS; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure Containing R G B values for Radeon USB LED Bar +/// +/// Elements of RGB Values. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_RADEON_LED_COLOR_CONFIG +{ + unsigned short R : 8; // Red Value + unsigned short G : 8; // Green Value + unsigned short B : 8; // Blue Value +}ADL_RADEON_LED_COLOR_CONFIG; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure Containing All Generic LED configuration for user requested LED pattern. The driver will apply the confgiuration as requested +/// +/// Elements of Radeon USB LED configuration. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_RADEON_LED_PATTERN_CONFIG_GENERIC +{ + short brightness : 8; // Brightness of LED + short speed : 8; // Speed of LED pattern + bool directionCounterClockWise; //Direction of LED Pattern + ADL_RADEON_LED_COLOR_CONFIG colorConfig; // RGB value of LED pattern + char morseCodeText[ADL_RADEON_LED_MAX_MORSE_CODE]; // Morse Code user input for Morse Code LED pattern + char morseCodeTextOutPut[ADL_RADEON_LED_MAX_MORSE_CODE]; // Driver set output representation of Morse Code + int morseCodeTextOutPutLen; // Length of Morse Code output +}ADL_RADEON_LED_PATTERN_CONFIG_GENERIC; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure Containing All custom grid pattern LED configuration for user requested LED grid pattern. The driver will apply the confgiuration as requested +/// +/// Elements of Radeon USB LED custom grid configuration. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_RADEON_LED_CUSTOM_LED_CONFIG +{ + short brightness : 8; // Brightness of LED + ADL_RADEON_LED_COLOR_CONFIG colorConfig[ADL_RADEON_LED_MAX_LED_ROW_ON_GRID][ADL_RADEON_LED_MAX_LED_COLUMN_ON_GRID]; // Full grid array representation of Radeon LED to be populated by user +}ADL_RADEON_LED_CUSTOM_GRID_LED_CONFIG; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure Containing All Radeon USB LED requests and controls. +/// +/// Elements of Radeon USB LED Controls. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_RADEON_LED_PATTERN_CONFIG +{ + ADL_RADEON_USB_LED_BAR_CONTROLS control; //Requested LED pattern + + union + { + ADL_RADEON_LED_PATTERN_CONFIG_GENERIC genericPararmeters; //Requested pattern configuration settings + ADL_RADEON_LED_CUSTOM_GRID_LED_CONFIG customGridConfig; //Requested custom grid configuration settings + }; +}ADL_RADEON_LED_PATTERN_CONFIG; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about the graphics adapter with extended caps +/// +/// This structure is used to store various information about the graphics adapter. This +/// information can be returned to the user. Alternatively, it can be used to access various driver calls to set +/// or fetch various settings upon the user's request. +/// This AdapterInfoX2 struct extends the AdapterInfo struct in adl_structures.h +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct AdapterInfoX2 +{ + /// \ALL_STRUCT_MEM + + /// Size of the structure. + int iSize; + /// The ADL index handle. One GPU may be associated with one or two index handles + int iAdapterIndex; + /// The unique device ID associated with this adapter. + char strUDID[ADL_MAX_PATH]; + /// The BUS number associated with this adapter. + int iBusNumber; + /// The driver number associated with this adapter. + int iDeviceNumber; + /// The function number. + int iFunctionNumber; + /// The vendor ID associated with this adapter. + int iVendorID; + /// Adapter name. + char strAdapterName[ADL_MAX_PATH]; + /// Display name. For example, "\\\\Display0" + char strDisplayName[ADL_MAX_PATH]; + /// Present or not; 1 if present and 0 if not present.It the logical adapter is present, the display name such as \\\\.\\Display1 can be found from OS + int iPresent; + /// Exist or not; 1 is exist and 0 is not present. + int iExist; + /// Driver registry path. + char strDriverPath[ADL_MAX_PATH]; + /// Driver registry path Ext for. + char strDriverPathExt[ADL_MAX_PATH]; + /// PNP string from Windows. + char strPNPString[ADL_MAX_PATH]; + /// It is generated from EnumDisplayDevices. + int iOSDisplayIndex; + /// The bit mask identifies the adapter info + int iInfoMask; + /// The bit identifies the adapter info \ref define_adapter_info + int iInfoValue; +} AdapterInfoX2, *LPAdapterInfoX2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about driver gamut space , whether it is related to source or to destination, overlay or graphics +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLGamutReference +{ + /// mask whether it is related to source or to destination, overlay or graphics + int iGamutRef; +}ADLGamutReference; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about driver supported gamut spaces , capability method +/// +/// This structure is used to get driver all supported gamut spaces +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLGamutInfo +{ + ///Any combination of following ADL_GAMUT_SPACE_CCIR_709 - ADL_GAMUT_SPACE_CUSTOM + int SupportedGamutSpace; + + ///Any combination of following ADL_WHITE_POINT_5000K - ADL_WHITE_POINT_CUSTOM + int SupportedWhitePoint; +} ADLGamutInfo; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about driver point coordinates +/// +/// This structure is used to store the driver point coodinates for gamut and white point +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLPoint +{ + /// x coordinate + int iX; + /// y coordinate + int iY; +} ADLPoint; +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about driver supported gamut coordinates +/// +/// This structure is used to store the driver supported supported gamut coordinates +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLGamutCoordinates +{ + /// red channel chromasity coordinate + ADLPoint Red; + /// green channel chromasity coordinate + ADLPoint Green; + /// blue channel chromasity coordinate + ADLPoint Blue; +} ADLGamutCoordinates; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about driver current gamut space , parent struct for ADLGamutCoordinates and ADLWhitePoint +/// This structure is used to get/set driver supported gamut space +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLGamutData +{ + ///used as mask and could be 4 options + ///BIT_0 If flag ADL_GAMUT_REFERENCE_SOURCE is asserted set operation is related to gamut source , + ///if not gamut destination + ///BIT_1 If flag ADL_GAMUT_GAMUT_VIDEO_CONTENT is asserted + ///BIT_2,BIT_3 used as mask and could be 4 options custom (2) + predefined (2) + ///0. Gamut predefined, white point predefined -> 0 | 0 + ///1. Gamut predefined, white point custom -> 0 | ADL_CUSTOM_WHITE_POINT + ///2. White point predefined, gamut custom -> 0 | ADL_CUSTOM_GAMUT + ///3. White point custom, gamut custom -> ADL_CUSTOM_GAMUT | ADL_CUSTOM_WHITE_POINT + int iFeature; + + ///one of ADL_GAMUT_SPACE_CCIR_709 - ADL_GAMUT_SPACE_CIE_RGB + int iPredefinedGamut; + + ///one of ADL_WHITE_POINT_5000K - ADL_WHITE_POINT_9300K + int iPredefinedWhitePoint; + + ///valid when in mask avails ADL_CUSTOM_WHITE_POINT + ADLPoint CustomWhitePoint; + + ///valid when in mask avails ADL_CUSTOM_GAMUT + ADLGamutCoordinates CustomGamut; +} ADLGamutData; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing detailed timing parameters. +/// +/// This structure is used to store the detailed timing parameters. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDetailedTimingX2 +{ + /// Size of the structure. + int iSize; + /// Timing flags. \ref define_detailed_timing_flags + int sTimingFlags; + /// Total width (columns). + int sHTotal; + /// Displayed width. + int sHDisplay; + /// Horizontal sync signal offset. + int sHSyncStart; + /// Horizontal sync signal width. + int sHSyncWidth; + /// Total height (rows). + int sVTotal; + /// Displayed height. + int sVDisplay; + /// Vertical sync signal offset. + int sVSyncStart; + /// Vertical sync signal width. + int sVSyncWidth; + /// Pixel clock value. + int sPixelClock; + /// Overscan right. + short sHOverscanRight; + /// Overscan left. + short sHOverscanLeft; + /// Overscan bottom. + short sVOverscanBottom; + /// Overscan top. + short sVOverscanTop; + short sOverscan8B; + short sOverscanGR; +} ADLDetailedTimingX2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing display mode information. +/// +/// This structure is used to store the display mode information. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLDisplayModeInfoX2 +{ + /// Timing standard of the current mode. \ref define_modetiming_standard + int iTimingStandard; + /// Applicable timing standards for the current mode. + int iPossibleStandard; + /// Refresh rate factor. + int iRefreshRate; + /// Num of pixels in a row. + int iPelsWidth; + /// Num of pixels in a column. + int iPelsHeight; + /// Detailed timing parameters. + ADLDetailedTimingX2 sDetailedTiming; +} ADLDisplayModeInfoX2; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about I2C. +/// +/// This structure is used to store the I2C information for the current adapter. +/// This structure is used by \ref ADL_Display_WriteAndReadI2CLargePayload +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLI2CLargePayload +{ + /// Size of the structure + int iSize; + /// Numerical value representing hardware I2C. + int iLine; + /// The 7-bit I2C slave device address. + int iAddress; + /// The offset of the data from the address. + int iOffset; + /// Read from or write to slave device. \ref ADL_DL_I2C_ACTIONREAD or \ref ADL_DL_I2C_ACTIONWRITE + int iAction; + /// I2C clock speed in KHz. + int iSpeed; + /// I2C option flags. \ref define_ADLI2CLargePayload + int iFlags; + /// A numerical value representing the number of bytes to be sent or received on the I2C bus. + int iDataSize; + /// Address of the characters which are to be sent or received on the I2C bus. + char *pcData; +} ADLI2CLargePayload; + +/// Size in bytes of the Feature Name +#define ADL_FEATURE_NAME_LENGTH 16 + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing the Multimedia Feature Name +/// +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLFeatureName +{ + /// The Feature Name + char FeatureName[ADL_FEATURE_NAME_LENGTH]; +} ADLFeatureName, *LPADLFeatureName; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about MM Feature Capabilities. +/// +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLFeatureCaps +{ + /// The Feature Name + ADLFeatureName Name; + // char strFeatureName[ADL_FEATURE_NAME_LENGTH]; + + /// Group ID. All Features in the same group are shown sequentially in the same UI Page. + int iGroupID; + + /// Visual ID. Places one or more features in a Group Box. If zero, no Group Box is added. + int iVisualID; + + /// Page ID. All Features with the same Page ID value are shown together on the same UI page. + int iPageID; + + /// Feature Property Mask. Indicates which are the valid bits for iFeatureProperties. + int iFeatureMask; + + /// Feature Property Values. See definitions for ADL_FEATURE_PROPERTIES_XXX + int iFeatureProperties; + + /// Apperance of the User-Controlled Boolean. + int iControlType; + + /// Style of the User-Controlled Boolean. + int iControlStyle; + + /// Apperance of the Adjustment Controls. + int iAdjustmentType; + + /// Style of the Adjustment Controls. + int iAdjustmentStyle; + + /// Default user-controlled boolean value. Valid only if ADLFeatureCaps supports user-controlled boolean. + int bDefault; + + /// Minimum integer value. Valid only if ADLFeatureCaps indicates support for integers. + int iMin; + + /// Maximum integer value. Valid only if ADLFeatureCaps indicates support for integers. + int iMax; + + /// Step integer value. Valid only if ADLFeatureCaps indicates support for integers. + int iStep; + + /// Default integer value. Valid only if ADLFeatureCaps indicates support for integers. + int iDefault; + + /// Minimum float value. Valid only if ADLFeatureCaps indicates support for floats. + float fMin; + + /// Maximum float value. Valid only if ADLFeatureCaps indicates support for floats. + float fMax; + + /// Step float value. Valid only if ADLFeatureCaps indicates support for floats. + float fStep; + + /// Default float value. Valid only if ADLFeatureCaps indicates support for floats. + float fDefault; + + /// The Mask for available bits for enumerated values.(If ADLFeatureCaps supports ENUM values) + int EnumMask; +} ADLFeatureCaps, *LPADLFeatureCaps; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about MM Feature Values. +/// +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLFeatureValues +{ + /// The Feature Name + ADLFeatureName Name; + // char strFeatureName[ADL_FEATURE_NAME_LENGTH]; + + /// User controlled Boolean current value. Valid only if ADLFeatureCaps supports Boolean. + int bCurrent; + + /// Current integer value. Valid only if ADLFeatureCaps indicates support for integers. + int iCurrent; + + /// Current float value. Valid only if ADLFeatureCaps indicates support for floats. + float fCurrent; + + /// The States for the available bits for enumerated values. + int EnumStates; +} ADLFeatureValues, *LPADLFeatureValues; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing HDCP Settings info +/// +/// This structure is used to store the HDCP settings of a +/// display +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLHDCPSettings +{ + int iHDCPProtectionVersion; // Version, starting from 1 + int iHDCPCaps; //Caps used to ensure at least one protection scheme is supported, 1 is HDCP1X and 2 is HDCP22 + int iAllowAll; //Allow all is true, disable all is false + int iHDCPVale; + int iHDCPMask; +} ADLHDCPSettings; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing Mantle App info +/// +/// This structure is used to store the Mantle Driver information +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct ADLMantleAppInfo +{ + /// mantle api version + int apiVersion; + /// mantle driver version + long driverVersion; + /// mantle vendroe id + long vendorId; + /// mantle device id + long deviceId; + /// mantle gpu type; + int gpuType; + /// gpu name + char gpuName[256]; + /// mem size + int maxMemRefsPerSubmission; + /// virtual mem size + long long virtualMemPageSize; + /// mem update + long long maxInlineMemoryUpdateSize; + /// bound descriptot + long maxBoundDescriptorSets; + /// thread group size + long maxThreadGroupSize; + /// time stamp frequency + long long timestampFrequency; + /// color target + long multiColorTargetClears; +}ADLMantleAppInfo, *LPADLMantleAppInfo; + +//////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about SDIData +///This structure is used to store information about the state of the SDI whether it is on +///or off and the current size of the segment or aperture size. +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSDIData +{ + /// The SDI state, ADL_SDI_ON or ADL_SDI_OFF, for the current SDI mode + int iSDIState; + /// Size of the memory segment for SDI (in MB). + int iSizeofSDISegment; +} ADLSDIData, *LPADLSDIData; + + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about FRTCPRO Settings +/// +/// Elements of FRTCPRO settings. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_FRTCPRO_Settings +{ + int DefaultState; //The default status for FRTC pro + int CurrentState; //The current enable/disable status for FRTC pro + unsigned int DefaultValue; //The default FPS value for FRTC pro. + unsigned int CurrentValue; //The current FPS value for FRTC pro. + unsigned int maxSupportedFps; //The max value for FRTC pro. + unsigned int minSupportedFps; //The min value for FRTC pro. +}ADL_FRTCPRO_Settings, *LPADLFRTCProSettings; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information about FRTCPRO Settings changed reason +/// +/// Reason of FRTCPRO changed. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_FRTCPRO_CHANGED_REASON +{ + int StateChanged; // FRTCPro state changed + int ValueChanged; // FRTCPro value changed +}ADL_FRTCPRO_CHANGED_REASON; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing the display mode definition used per controller. +/// +/// This structure is used to store the display mode definition used per controller. +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADL_DL_DISPLAY_MODE +{ + int iPelsHeight; // Vertical resolution (in pixels). + int iPelsWidth; // Horizontal resolution (in pixels). + int iBitsPerPel; // Color depth. + int iDisplayFrequency; // Refresh rate. +} ADL_DL_DISPLAY_MODE; + +///////////////////////////////////////////////////////////////////////////////////////////// +///\brief Structure containing information related DCE support +/// +/// This structure is used to store a bit vector of possible DCE support +/// +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef union _ADLDCESupport +{ + struct + { + unsigned int PrePhasis : 1; + unsigned int voltageSwing : 1; + unsigned int reserved : 30; + }bits; + unsigned int u32All; +}ADLDCESupport; + +///////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure for Smart shift 2.0 settings +/// +/// This structure is used to return the smart shift settings +/// \nosubgrouping +//////////////////////////////////////////////////////////////////////////////////////////// +typedef struct ADLSmartShiftSettings +{ + int iMinRange; + int iMaxRange; + int iDefaultMode; //Refer to CWDDEPM_ODN_CONTROL_TYPE + int iDefaultValue; + int iCurrentMode; + int iCurrentValue; + int iFlags; //refer to define_smartshift_bits +}ADLSmartShiftSettings, *LPADLSmartShiftSettings; +#endif /* ADL_STRUCTURES_H_ */ diff --git a/dependencies/hidapi-win/include/hidapi.h b/dependencies/hidapi-win/include/hidapi.h new file mode 100644 index 0000000..2da647f --- /dev/null +++ b/dependencies/hidapi-win/include/hidapi.h @@ -0,0 +1,624 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + libusb/hidapi Team + + Copyright 2023, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + https://github.com/libusb/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +/* #480: this is to be refactored properly for v1.0 */ +#ifdef _WIN32 + #ifndef HID_API_NO_EXPORT_DEFINE + #define HID_API_EXPORT __declspec(dllexport) + #endif +#endif +#ifndef HID_API_EXPORT + #define HID_API_EXPORT /**< API export macro */ +#endif +/* To be removed in v1.0 */ +#define HID_API_CALL /**< API call macro */ + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +/** @brief Static/compile-time major version of the library. + + @ingroup API +*/ +#define HID_API_VERSION_MAJOR 0 +/** @brief Static/compile-time minor version of the library. + + @ingroup API +*/ +#define HID_API_VERSION_MINOR 14 +/** @brief Static/compile-time patch version of the library. + + @ingroup API +*/ +#define HID_API_VERSION_PATCH 0 + +/* Helper macros */ +#define HID_API_AS_STR_IMPL(x) #x +#define HID_API_AS_STR(x) HID_API_AS_STR_IMPL(x) +#define HID_API_TO_VERSION_STR(v1, v2, v3) HID_API_AS_STR(v1.v2.v3) + +/** @brief Coverts a version as Major/Minor/Patch into a number: + <8 bit major><16 bit minor><8 bit patch>. + + This macro was added in version 0.12.0. + + Convenient function to be used for compile-time checks, like: + @code{.c} + #if HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) + @endcode + + @ingroup API +*/ +#define HID_API_MAKE_VERSION(mj, mn, p) (((mj) << 24) | ((mn) << 8) | (p)) + +/** @brief Static/compile-time version of the library. + + This macro was added in version 0.12.0. + + @see @ref HID_API_MAKE_VERSION. + + @ingroup API +*/ +#define HID_API_VERSION HID_API_MAKE_VERSION(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH) + +/** @brief Static/compile-time string version of the library. + + @ingroup API +*/ +#define HID_API_VERSION_STR HID_API_TO_VERSION_STR(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH) + +/** @brief Maximum expected HID Report descriptor size in bytes. + + Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0) + + @ingroup API +*/ +#define HID_API_MAX_REPORT_DESCRIPTOR_SIZE 4096 + +#ifdef __cplusplus +extern "C" { +#endif + /** A structure to hold the version numbers. */ + struct hid_api_version { + int major; /**< major version number */ + int minor; /**< minor version number */ + int patch; /**< patch version number */ + }; + + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** @brief HID underlying bus types. + + @ingroup API + */ + typedef enum { + /** Unknown bus type */ + HID_API_BUS_UNKNOWN = 0x00, + + /** USB bus + Specifications: + https://usb.org/hid */ + HID_API_BUS_USB = 0x01, + + /** Bluetooth or Bluetooth LE bus + Specifications: + https://www.bluetooth.com/specifications/specs/human-interface-device-profile-1-1-1/ + https://www.bluetooth.com/specifications/specs/hid-service-1-0/ + https://www.bluetooth.com/specifications/specs/hid-over-gatt-profile-1-0/ */ + HID_API_BUS_BLUETOOTH = 0x02, + + /** I2C bus + Specifications: + https://docs.microsoft.com/previous-versions/windows/hardware/design/dn642101(v=vs.85) */ + HID_API_BUS_I2C = 0x03, + + /** SPI bus + Specifications: + https://www.microsoft.com/download/details.aspx?id=103325 */ + HID_API_BUS_SPI = 0x04, + } hid_bus_type; + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac/hidraw only) */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac/hidraw only) */ + unsigned short usage; + /** The USB interface which this logical device + represents. + + Valid only if the device is a USB HID device. + Set to -1 in all other cases. + */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + + /** Underlying bus type + Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0) + */ + hid_bus_type bus_type; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(NULL) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device_info, containing information about the HID devices + attached to the system, + or NULL in the case of failure or if no HID devices present in the system. + Call hid_error(NULL) to get the failure reason. + + @note The returned value by this function must to be freed by calling hid_free_enumeration(), + when not needed anymore. + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + Call hid_error(NULL) to get the failure reason. + + @note The returned object must be freed by calling hid_close(), + when not needed anymore. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + Call hid_error(NULL) to get the failure reason. + + @note The returned object must be freed by calling hid_close(), + when not needed anymore. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. + Call hid_error(dev) to get the failure reason. + If no packet was available to be read within + the timeout period, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. + Call hid_error(dev) to get the failure reason. + If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Get a input report from a HID device. + + Since version 0.10.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 10, 0) + + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen); + + /** @brief Get The struct #hid_device_info from a HID device. + + Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0) + + @ingroup API + @param dev A device handle returned from hid_open(). + + @returns + This function returns a pointer to the struct #hid_device_info + for this hid_device, or NULL in the case of failure. + Call hid_error(dev) to get the failure reason. + This struct is valid until the device is closed with hid_close(). + + @note The returned object is owned by the @p dev, and SHOULD NOT be freed by the user. + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_get_device_info(hid_device *dev); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + Call hid_error(dev) to get the failure reason. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a report descriptor from a HID device. + + Since version 0.14.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 14, 0) + + User has to provide a preallocated buffer where descriptor will be copied to. + The recommended size for preallocated buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param buf The buffer to copy descriptor into. + @param buf_size The size of the buffer in bytes. + + @returns + This function returns non-negative number of bytes actually copied, or -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size); + + /** @brief Get a string describing the last error which occurred. + + This function is intended for logging/debugging purposes. + + This function guarantees to never return NULL. + If there was no error in the last function call - + the returned string clearly indicates that. + + Any HIDAPI function that can explicitly indicate an execution failure + (e.g. by an error code, or by returning NULL) - may set the error string, + to be returned by this function. + + Strings returned from hid_error() must not be freed by the user, + i.e. owned by HIDAPI library. + Device-specific error string may remain allocated at most until hid_close() is called. + Global error string may remain allocated at most until hid_exit() is called. + + @ingroup API + @param dev A device handle returned from hid_open(), + or NULL to get the last non-device-specific error + (e.g. for errors in hid_open() or hid_enumerate()). + + @returns + A string describing the last error (if any). + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev); + + /** @brief Get a runtime version of the library. + + This function is thread-safe. + + @ingroup API + + @returns + Pointer to statically allocated struct, that contains version. + */ + HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void); + + + /** @brief Get a runtime version string of the library. + + This function is thread-safe. + + @ingroup API + + @returns + Pointer to statically allocated string, that contains version string. + */ + HID_API_EXPORT const char* HID_API_CALL hid_version_str(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dependencies/hidapi-win/include/hidapi_winapi.h b/dependencies/hidapi-win/include/hidapi_winapi.h new file mode 100644 index 0000000..da57684 --- /dev/null +++ b/dependencies/hidapi-win/include/hidapi_winapi.h @@ -0,0 +1,74 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + libusb/hidapi Team + + Copyright 2022, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + https://github.com/libusb/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + * + * Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) + */ + +#ifndef HIDAPI_WINAPI_H__ +#define HIDAPI_WINAPI_H__ + +#include + +#include + +#include "hidapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** @brief Get the container ID for a HID device. + + Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) + + This function returns the `DEVPKEY_Device_ContainerId` property of + the given device. This can be used to correlate different + interfaces/ports on the same hardware device. + + @ingroup API + @param dev A device handle returned from hid_open(). + @param guid The device's container ID on return. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id); + + /** + * @brief Reconstructs a HID Report Descriptor from a Win32 HIDP_PREPARSED_DATA structure. + * This reconstructed report descriptor is logical identical to the real report descriptor, + * but not byte wise identical. + * + * @param[in] hidp_preparsed_data Pointer to the HIDP_PREPARSED_DATA to read, i.e.: the value of PHIDP_PREPARSED_DATA, + * as returned by HidD_GetPreparsedData WinAPI function. + * @param buf Pointer to the buffer where the report descriptor should be stored. + * @param[in] buf_size Size of the buffer. The recommended size for the buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes. + * + * @return Returns size of reconstructed report descriptor if successful, -1 for error. + */ + int HID_API_EXPORT_CALL hid_winapi_descriptor_reconstruct_pp_data(void *hidp_preparsed_data, unsigned char *buf, size_t buf_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dependencies/hidapi-win/x64/hidapi.dll b/dependencies/hidapi-win/x64/hidapi.dll new file mode 100644 index 0000000..4eded6e Binary files /dev/null and b/dependencies/hidapi-win/x64/hidapi.dll differ diff --git a/dependencies/hidapi-win/x64/hidapi.lib b/dependencies/hidapi-win/x64/hidapi.lib new file mode 100644 index 0000000..7656004 Binary files /dev/null and b/dependencies/hidapi-win/x64/hidapi.lib differ diff --git a/dependencies/hidapi-win/x86/hidapi.dll b/dependencies/hidapi-win/x86/hidapi.dll new file mode 100644 index 0000000..c137c4e Binary files /dev/null and b/dependencies/hidapi-win/x86/hidapi.dll differ diff --git a/dependencies/hidapi-win/x86/hidapi.lib b/dependencies/hidapi-win/x86/hidapi.lib new file mode 100644 index 0000000..85d906c Binary files /dev/null and b/dependencies/hidapi-win/x86/hidapi.lib differ diff --git a/dependencies/httplib/httplib.h b/dependencies/httplib/httplib.h new file mode 100644 index 0000000..801e066 --- /dev/null +++ b/dependencies/httplib/httplib.h @@ -0,0 +1,9806 @@ +// +// httplib.h +// +// Copyright (c) 2024 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef CPPHTTPLIB_HTTPLIB_H +#define CPPHTTPLIB_HTTPLIB_H + +#define CPPHTTPLIB_VERSION "0.16.0" + +/* + * Configuration + */ + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND +#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND +#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND +#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND +#ifdef _WIN32 +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000 +#else +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 +#endif +#endif + +#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH +#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT +#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 +#endif + +#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT +#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024 +#endif + +#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)()) +#endif + +#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_RANGE_MAX_COUNT +#define CPPHTTPLIB_RANGE_MAX_COUNT 1024 +#endif + +#ifndef CPPHTTPLIB_TCP_NODELAY +#define CPPHTTPLIB_TCP_NODELAY false +#endif + +#ifndef CPPHTTPLIB_RECV_BUFSIZ +#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u) +#endif + +#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ +#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u) +#endif + +#ifndef CPPHTTPLIB_THREAD_POOL_COUNT +#define CPPHTTPLIB_THREAD_POOL_COUNT \ + ((std::max)(8u, std::thread::hardware_concurrency() > 0 \ + ? std::thread::hardware_concurrency() - 1 \ + : 0)) +#endif + +#ifndef CPPHTTPLIB_RECV_FLAGS +#define CPPHTTPLIB_RECV_FLAGS 0 +#endif + +#ifndef CPPHTTPLIB_SEND_FLAGS +#define CPPHTTPLIB_SEND_FLAGS 0 +#endif + +#ifndef CPPHTTPLIB_LISTEN_BACKLOG +#define CPPHTTPLIB_LISTEN_BACKLOG 5 +#endif + +/* + * Headers + */ + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif //_CRT_SECURE_NO_WARNINGS + +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif //_CRT_NONSTDC_NO_DEPRECATE + +#if defined(_MSC_VER) +#if _MSC_VER < 1900 +#error Sorry, Visual Studio versions prior to 2015 are not supported +#endif + +#pragma comment(lib, "ws2_32.lib") + +#ifdef _WIN64 +using ssize_t = __int64; +#else +using ssize_t = long; +#endif +#endif // _MSC_VER + +#ifndef S_ISREG +#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG) +#endif // S_ISREG + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR) +#endif // S_ISDIR + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +#include +#include +#include + +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +using socket_t = SOCKET; +#ifdef CPPHTTPLIB_USE_POLL +#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) +#endif + +#else // not _WIN32 + +#include +#if !defined(_AIX) && !defined(__MVS__) +#include +#endif +#ifdef __MVS__ +#include +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#endif +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include +#ifdef CPPHTTPLIB_USE_POLL +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +using socket_t = int; +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif +#endif //_WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#ifdef _WIN32 +#include + +// these are defined in wincrypt.h and it breaks compilation if BoringSSL is +// used +#undef X509_NAME +#undef X509_CERT_PAIR +#undef X509_EXTENSIONS +#undef PKCS7_SIGNER_INFO + +#ifdef _MSC_VER +#pragma comment(lib, "crypt32.lib") +#endif +#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) +#include +#if TARGET_OS_OSX +#include +#include +#endif // TARGET_OS_OSX +#endif // _WIN32 + +#include +#include +#include +#include + +#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK) +#include +#endif + +#include +#include + +#if OPENSSL_VERSION_NUMBER < 0x30000000L +#error Sorry, OpenSSL versions prior to 3.0.0 are not supported +#endif + +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +#include +#include +#endif + +/* + * Declaration + */ +namespace httplib { + +namespace detail { + +/* + * Backport std::make_unique from C++14. + * + * NOTE: This code came up with the following stackoverflow post: + * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique + * + */ + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(Args &&...args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +template +typename std::enable_if::value, std::unique_ptr>::type +make_unique(std::size_t n) { + typedef typename std::remove_extent::type RT; + return std::unique_ptr(new RT[n]); +} + +struct ci { + bool operator()(const std::string &s1, const std::string &s2) const { + return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), + s2.end(), + [](unsigned char c1, unsigned char c2) { + return ::tolower(c1) < ::tolower(c2); + }); + } +}; + +// This is based on +// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". + +struct scope_exit { + explicit scope_exit(std::function &&f) + : exit_function(std::move(f)), execute_on_destruction{true} {} + + scope_exit(scope_exit &&rhs) noexcept + : exit_function(std::move(rhs.exit_function)), + execute_on_destruction{rhs.execute_on_destruction} { + rhs.release(); + } + + ~scope_exit() { + if (execute_on_destruction) { this->exit_function(); } + } + + void release() { this->execute_on_destruction = false; } + +private: + scope_exit(const scope_exit &) = delete; + void operator=(const scope_exit &) = delete; + scope_exit &operator=(scope_exit &&) = delete; + + std::function exit_function; + bool execute_on_destruction; +}; + +} // namespace detail + +enum StatusCode { + // Information responses + Continue_100 = 100, + SwitchingProtocol_101 = 101, + Processing_102 = 102, + EarlyHints_103 = 103, + + // Successful responses + OK_200 = 200, + Created_201 = 201, + Accepted_202 = 202, + NonAuthoritativeInformation_203 = 203, + NoContent_204 = 204, + ResetContent_205 = 205, + PartialContent_206 = 206, + MultiStatus_207 = 207, + AlreadyReported_208 = 208, + IMUsed_226 = 226, + + // Redirection messages + MultipleChoices_300 = 300, + MovedPermanently_301 = 301, + Found_302 = 302, + SeeOther_303 = 303, + NotModified_304 = 304, + UseProxy_305 = 305, + unused_306 = 306, + TemporaryRedirect_307 = 307, + PermanentRedirect_308 = 308, + + // Client error responses + BadRequest_400 = 400, + Unauthorized_401 = 401, + PaymentRequired_402 = 402, + Forbidden_403 = 403, + NotFound_404 = 404, + MethodNotAllowed_405 = 405, + NotAcceptable_406 = 406, + ProxyAuthenticationRequired_407 = 407, + RequestTimeout_408 = 408, + Conflict_409 = 409, + Gone_410 = 410, + LengthRequired_411 = 411, + PreconditionFailed_412 = 412, + PayloadTooLarge_413 = 413, + UriTooLong_414 = 414, + UnsupportedMediaType_415 = 415, + RangeNotSatisfiable_416 = 416, + ExpectationFailed_417 = 417, + ImATeapot_418 = 418, + MisdirectedRequest_421 = 421, + UnprocessableContent_422 = 422, + Locked_423 = 423, + FailedDependency_424 = 424, + TooEarly_425 = 425, + UpgradeRequired_426 = 426, + PreconditionRequired_428 = 428, + TooManyRequests_429 = 429, + RequestHeaderFieldsTooLarge_431 = 431, + UnavailableForLegalReasons_451 = 451, + + // Server error responses + InternalServerError_500 = 500, + NotImplemented_501 = 501, + BadGateway_502 = 502, + ServiceUnavailable_503 = 503, + GatewayTimeout_504 = 504, + HttpVersionNotSupported_505 = 505, + VariantAlsoNegotiates_506 = 506, + InsufficientStorage_507 = 507, + LoopDetected_508 = 508, + NotExtended_510 = 510, + NetworkAuthenticationRequired_511 = 511, +}; + +using Headers = std::multimap; + +using Params = std::multimap; +using Match = std::smatch; + +using Progress = std::function; + +struct Response; +using ResponseHandler = std::function; + +struct MultipartFormData { + std::string name; + std::string content; + std::string filename; + std::string content_type; +}; +using MultipartFormDataItems = std::vector; +using MultipartFormDataMap = std::multimap; + +class DataSink { +public: + DataSink() : os(&sb_), sb_(*this) {} + + DataSink(const DataSink &) = delete; + DataSink &operator=(const DataSink &) = delete; + DataSink(DataSink &&) = delete; + DataSink &operator=(DataSink &&) = delete; + + std::function write; + std::function is_writable; + std::function done; + std::function done_with_trailer; + std::ostream os; + +private: + class data_sink_streambuf final : public std::streambuf { + public: + explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {} + + protected: + std::streamsize xsputn(const char *s, std::streamsize n) override { + sink_.write(s, static_cast(n)); + return n; + } + + private: + DataSink &sink_; + }; + + data_sink_streambuf sb_; +}; + +using ContentProvider = + std::function; + +using ContentProviderWithoutLength = + std::function; + +using ContentProviderResourceReleaser = std::function; + +struct MultipartFormDataProvider { + std::string name; + ContentProviderWithoutLength provider; + std::string filename; + std::string content_type; +}; +using MultipartFormDataProviderItems = std::vector; + +using ContentReceiverWithProgress = + std::function; + +using ContentReceiver = + std::function; + +using MultipartContentHeader = + std::function; + +class ContentReader { +public: + using Reader = std::function; + using MultipartReader = std::function; + + ContentReader(Reader reader, MultipartReader multipart_reader) + : reader_(std::move(reader)), + multipart_reader_(std::move(multipart_reader)) {} + + bool operator()(MultipartContentHeader header, + ContentReceiver receiver) const { + return multipart_reader_(std::move(header), std::move(receiver)); + } + + bool operator()(ContentReceiver receiver) const { + return reader_(std::move(receiver)); + } + + Reader reader_; + MultipartReader multipart_reader_; +}; + +using Range = std::pair; +using Ranges = std::vector; + +struct Request { + std::string method; + std::string path; + Headers headers; + std::string body; + + std::string remote_addr; + int remote_port = -1; + std::string local_addr; + int local_port = -1; + + // for server + std::string version; + std::string target; + Params params; + MultipartFormDataMap files; + Ranges ranges; + Match matches; + std::unordered_map path_params; + + // for client + ResponseHandler response_handler; + ContentReceiverWithProgress content_receiver; + Progress progress; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + const SSL *ssl = nullptr; +#endif + + bool has_header(const std::string &key) const; + std::string get_header_value(const std::string &key, size_t id = 0) const; + uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; + size_t get_header_value_count(const std::string &key) const; + void set_header(const std::string &key, const std::string &val); + + bool has_param(const std::string &key) const; + std::string get_param_value(const std::string &key, size_t id = 0) const; + size_t get_param_value_count(const std::string &key) const; + + bool is_multipart_form_data() const; + + bool has_file(const std::string &key) const; + MultipartFormData get_file_value(const std::string &key) const; + std::vector get_file_values(const std::string &key) const; + + // private members... + size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT; + size_t content_length_ = 0; + ContentProvider content_provider_; + bool is_chunked_content_provider_ = false; + size_t authorization_count_ = 0; +}; + +struct Response { + std::string version; + int status = -1; + std::string reason; + Headers headers; + std::string body; + std::string location; // Redirect location + + bool has_header(const std::string &key) const; + std::string get_header_value(const std::string &key, size_t id = 0) const; + uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; + size_t get_header_value_count(const std::string &key) const; + void set_header(const std::string &key, const std::string &val); + + void set_redirect(const std::string &url, int status = StatusCode::Found_302); + void set_content(const char *s, size_t n, const std::string &content_type); + void set_content(const std::string &s, const std::string &content_type); + void set_content(std::string &&s, const std::string &content_type); + + void set_content_provider( + size_t length, const std::string &content_type, ContentProvider provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + void set_content_provider( + const std::string &content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + void set_chunked_content_provider( + const std::string &content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + Response() = default; + Response(const Response &) = default; + Response &operator=(const Response &) = default; + Response(Response &&) = default; + Response &operator=(Response &&) = default; + ~Response() { + if (content_provider_resource_releaser_) { + content_provider_resource_releaser_(content_provider_success_); + } + } + + // private members... + size_t content_length_ = 0; + ContentProvider content_provider_; + ContentProviderResourceReleaser content_provider_resource_releaser_; + bool is_chunked_content_provider_ = false; + bool content_provider_success_ = false; +}; + +class Stream { +public: + virtual ~Stream() = default; + + virtual bool is_readable() const = 0; + virtual bool is_writable() const = 0; + + virtual ssize_t read(char *ptr, size_t size) = 0; + virtual ssize_t write(const char *ptr, size_t size) = 0; + virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; + virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0; + virtual socket_t socket() const = 0; + + template + ssize_t write_format(const char *fmt, const Args &...args); + ssize_t write(const char *ptr); + ssize_t write(const std::string &s); +}; + +class TaskQueue { +public: + TaskQueue() = default; + virtual ~TaskQueue() = default; + + virtual bool enqueue(std::function fn) = 0; + virtual void shutdown() = 0; + + virtual void on_idle() {} +}; + +class ThreadPool final : public TaskQueue { +public: + explicit ThreadPool(size_t n, size_t mqr = 0) + : shutdown_(false), max_queued_requests_(mqr) { + while (n) { + threads_.emplace_back(worker(*this)); + n--; + } + } + + ThreadPool(const ThreadPool &) = delete; + ~ThreadPool() override = default; + + bool enqueue(std::function fn) override { + { + std::unique_lock lock(mutex_); + if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) { + return false; + } + jobs_.push_back(std::move(fn)); + } + + cond_.notify_one(); + return true; + } + + void shutdown() override { + // Stop all worker threads... + { + std::unique_lock lock(mutex_); + shutdown_ = true; + } + + cond_.notify_all(); + + // Join... + for (auto &t : threads_) { + t.join(); + } + } + +private: + struct worker { + explicit worker(ThreadPool &pool) : pool_(pool) {} + + void operator()() { + for (;;) { + std::function fn; + { + std::unique_lock lock(pool_.mutex_); + + pool_.cond_.wait( + lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); + + if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } + + fn = pool_.jobs_.front(); + pool_.jobs_.pop_front(); + } + + assert(true == static_cast(fn)); + fn(); + } + } + + ThreadPool &pool_; + }; + friend struct worker; + + std::vector threads_; + std::list> jobs_; + + bool shutdown_; + size_t max_queued_requests_ = 0; + + std::condition_variable cond_; + std::mutex mutex_; +}; + +using Logger = std::function; + +using SocketOptions = std::function; + +void default_socket_options(socket_t sock); + +const char *status_message(int status); + +std::string get_bearer_token_auth(const Request &req); + +namespace detail { + +class MatcherBase { +public: + virtual ~MatcherBase() = default; + + // Match request path and populate its matches and + virtual bool match(Request &request) const = 0; +}; + +/** + * Captures parameters in request path and stores them in Request::path_params + * + * Capture name is a substring of a pattern from : to /. + * The rest of the pattern is matched agains the request path directly + * Parameters are captured starting from the next character after + * the end of the last matched static pattern fragment until the next /. + * + * Example pattern: + * "/path/fragments/:capture/more/fragments/:second_capture" + * Static fragments: + * "/path/fragments/", "more/fragments/" + * + * Given the following request path: + * "/path/fragments/:1/more/fragments/:2" + * the resulting capture will be + * {{"capture", "1"}, {"second_capture", "2"}} + */ +class PathParamsMatcher final : public MatcherBase { +public: + PathParamsMatcher(const std::string &pattern); + + bool match(Request &request) const override; + +private: + static constexpr char marker = ':'; + // Treat segment separators as the end of path parameter capture + // Does not need to handle query parameters as they are parsed before path + // matching + static constexpr char separator = '/'; + + // Contains static path fragments to match against, excluding the '/' after + // path params + // Fragments are separated by path params + std::vector static_fragments_; + // Stores the names of the path parameters to be used as keys in the + // Request::path_params map + std::vector param_names_; +}; + +/** + * Performs std::regex_match on request path + * and stores the result in Request::matches + * + * Note that regex match is performed directly on the whole request. + * This means that wildcard patterns may match multiple path segments with /: + * "/begin/(.*)/end" will match both "/begin/middle/end" and "/begin/1/2/end". + */ +class RegexMatcher final : public MatcherBase { +public: + RegexMatcher(const std::string &pattern) : regex_(pattern) {} + + bool match(Request &request) const override; + +private: + std::regex regex_; +}; + +ssize_t write_headers(Stream &strm, const Headers &headers); + +} // namespace detail + +class Server { +public: + using Handler = std::function; + + using ExceptionHandler = + std::function; + + enum class HandlerResponse { + Handled, + Unhandled, + }; + using HandlerWithResponse = + std::function; + + using HandlerWithContentReader = std::function; + + using Expect100ContinueHandler = + std::function; + + Server(); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server &Get(const std::string &pattern, Handler handler); + Server &Post(const std::string &pattern, Handler handler); + Server &Post(const std::string &pattern, HandlerWithContentReader handler); + Server &Put(const std::string &pattern, Handler handler); + Server &Put(const std::string &pattern, HandlerWithContentReader handler); + Server &Patch(const std::string &pattern, Handler handler); + Server &Patch(const std::string &pattern, HandlerWithContentReader handler); + Server &Delete(const std::string &pattern, Handler handler); + Server &Delete(const std::string &pattern, HandlerWithContentReader handler); + Server &Options(const std::string &pattern, Handler handler); + + bool set_base_dir(const std::string &dir, + const std::string &mount_point = std::string()); + bool set_mount_point(const std::string &mount_point, const std::string &dir, + Headers headers = Headers()); + bool remove_mount_point(const std::string &mount_point); + Server &set_file_extension_and_mimetype_mapping(const std::string &ext, + const std::string &mime); + Server &set_default_file_mimetype(const std::string &mime); + Server &set_file_request_handler(Handler handler); + + template + Server &set_error_handler(ErrorHandlerFunc &&handler) { + return set_error_handler_core( + std::forward(handler), + std::is_convertible{}); + } + + Server &set_exception_handler(ExceptionHandler handler); + Server &set_pre_routing_handler(HandlerWithResponse handler); + Server &set_post_routing_handler(Handler handler); + + Server &set_expect_100_continue_handler(Expect100ContinueHandler handler); + Server &set_logger(Logger logger); + + Server &set_address_family(int family); + Server &set_tcp_nodelay(bool on); + Server &set_socket_options(SocketOptions socket_options); + + Server &set_default_headers(Headers headers); + Server & + set_header_writer(std::function const &writer); + + Server &set_keep_alive_max_count(size_t count); + Server &set_keep_alive_timeout(time_t sec); + + Server &set_read_timeout(time_t sec, time_t usec = 0); + template + Server &set_read_timeout(const std::chrono::duration &duration); + + Server &set_write_timeout(time_t sec, time_t usec = 0); + template + Server &set_write_timeout(const std::chrono::duration &duration); + + Server &set_idle_interval(time_t sec, time_t usec = 0); + template + Server &set_idle_interval(const std::chrono::duration &duration); + + Server &set_payload_max_length(size_t length); + + bool bind_to_port(const std::string &host, int port, int socket_flags = 0); + int bind_to_any_port(const std::string &host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const std::string &host, int port, int socket_flags = 0); + + bool is_running() const; + void wait_until_ready() const; + void stop(); + + std::function new_task_queue; + +protected: + bool process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request); + + std::atomic svr_sock_{INVALID_SOCKET}; + size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; + time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; + size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; + +private: + using Handlers = + std::vector, Handler>>; + using HandlersForContentReader = + std::vector, + HandlerWithContentReader>>; + + static std::unique_ptr + make_matcher(const std::string &pattern); + + Server &set_error_handler_core(HandlerWithResponse handler, std::true_type); + Server &set_error_handler_core(Handler handler, std::false_type); + + socket_t create_server_socket(const std::string &host, int port, + int socket_flags, + SocketOptions socket_options) const; + int bind_internal(const std::string &host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request &req, Response &res, Stream &strm); + bool handle_file_request(const Request &req, Response &res, + bool head = false); + bool dispatch_request(Request &req, Response &res, + const Handlers &handlers) const; + bool dispatch_request_for_content_reader( + Request &req, Response &res, ContentReader content_reader, + const HandlersForContentReader &handlers) const; + + bool parse_request_line(const char *s, Request &req) const; + void apply_ranges(const Request &req, Response &res, + std::string &content_type, std::string &boundary) const; + bool write_response(Stream &strm, bool close_connection, Request &req, + Response &res); + bool write_response_with_content(Stream &strm, bool close_connection, + const Request &req, Response &res); + bool write_response_core(Stream &strm, bool close_connection, + const Request &req, Response &res, + bool need_apply_ranges); + bool write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type); + bool read_content(Stream &strm, Request &req, Response &res); + bool + read_content_with_content_receiver(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver); + bool read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) const; + + virtual bool process_and_close_socket(socket_t sock); + + std::atomic is_running_{false}; + std::atomic done_{false}; + + struct MountPointEntry { + std::string mount_point; + std::string base_dir; + Headers headers; + }; + std::vector base_dirs_; + std::map file_extension_and_mimetype_map_; + std::string default_file_mimetype_ = "application/octet-stream"; + Handler file_request_handler_; + + Handlers get_handlers_; + Handlers post_handlers_; + HandlersForContentReader post_handlers_for_content_reader_; + Handlers put_handlers_; + HandlersForContentReader put_handlers_for_content_reader_; + Handlers patch_handlers_; + HandlersForContentReader patch_handlers_for_content_reader_; + Handlers delete_handlers_; + HandlersForContentReader delete_handlers_for_content_reader_; + Handlers options_handlers_; + + HandlerWithResponse error_handler_; + ExceptionHandler exception_handler_; + HandlerWithResponse pre_routing_handler_; + Handler post_routing_handler_; + Expect100ContinueHandler expect_100_continue_handler_; + + Logger logger_; + + int address_family_ = AF_UNSPEC; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = default_socket_options; + + Headers default_headers_; + std::function header_writer_ = + detail::write_headers; +}; + +enum class Error { + Success = 0, + Unknown, + Connection, + BindIPAddress, + Read, + Write, + ExceedRedirectCount, + Canceled, + SSLConnection, + SSLLoadingCerts, + SSLServerVerification, + UnsupportedMultipartBoundaryChars, + Compression, + ConnectionTimeout, + ProxyConnection, + + // For internal use only + SSLPeerCouldBeClosed_, +}; + +std::string to_string(Error error); + +std::ostream &operator<<(std::ostream &os, const Error &obj); + +class Result { +public: + Result() = default; + Result(std::unique_ptr &&res, Error err, + Headers &&request_headers = Headers{}) + : res_(std::move(res)), err_(err), + request_headers_(std::move(request_headers)) {} + // Response + operator bool() const { return res_ != nullptr; } + bool operator==(std::nullptr_t) const { return res_ == nullptr; } + bool operator!=(std::nullptr_t) const { return res_ != nullptr; } + const Response &value() const { return *res_; } + Response &value() { return *res_; } + const Response &operator*() const { return *res_; } + Response &operator*() { return *res_; } + const Response *operator->() const { return res_.get(); } + Response *operator->() { return res_.get(); } + + // Error + Error error() const { return err_; } + + // Request Headers + bool has_request_header(const std::string &key) const; + std::string get_request_header_value(const std::string &key, + size_t id = 0) const; + uint64_t get_request_header_value_u64(const std::string &key, + size_t id = 0) const; + size_t get_request_header_value_count(const std::string &key) const; + +private: + std::unique_ptr res_; + Error err_ = Error::Unknown; + Headers request_headers_; +}; + +class ClientImpl { +public: + explicit ClientImpl(const std::string &host); + + explicit ClientImpl(const std::string &host, int port); + + explicit ClientImpl(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + virtual ~ClientImpl(); + + virtual bool is_valid() const; + + Result Get(const std::string &path); + Result Get(const std::string &path, const Headers &headers); + Result Get(const std::string &path, Progress progress); + Result Get(const std::string &path, const Headers &headers, + Progress progress); + Result Get(const std::string &path, ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const std::string &path, ContentReceiver content_receiver, + Progress progress); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ContentReceiver content_receiver, + Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress = nullptr); + + Result Head(const std::string &path); + Result Head(const std::string &path, const Headers &headers); + + Result Post(const std::string &path); + Result Post(const std::string &path, const Headers &headers); + Result Post(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Post(const std::string &path, const std::string &body, + const std::string &content_type); + Result Post(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Post(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Post(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Params ¶ms); + Result Post(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Post(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress); + Result Post(const std::string &path, const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Put(const std::string &path); + Result Put(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Put(const std::string &path, const std::string &body, + const std::string &content_type); + Result Put(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Put(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Put(const std::string &path, size_t content_length, + ContentProvider content_provider, const std::string &content_type); + Result Put(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Params ¶ms); + Result Put(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Put(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress); + Result Put(const std::string &path, const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Patch(const std::string &path); + Result Patch(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const std::string &body, + const std::string &content_type); + Result Patch(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Patch(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + + Result Delete(const std::string &path); + Result Delete(const std::string &path, const Headers &headers); + Result Delete(const std::string &path, const char *body, + size_t content_length, const std::string &content_type); + Result Delete(const std::string &path, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Delete(const std::string &path, const std::string &body, + const std::string &content_type); + Result Delete(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Delete(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + + Result Options(const std::string &path); + Result Options(const std::string &path, const Headers &headers); + + bool send(Request &req, Response &res, Error &error); + Result send(const Request &req); + + void stop(); + + std::string host() const; + int port() const; + + size_t is_socket_open() const; + socket_t socket() const; + + void set_hostname_addr_map(std::map addr_map); + + void set_default_headers(Headers headers); + + void + set_header_writer(std::function const &writer); + + void set_address_family(int family); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + template + void + set_connection_timeout(const std::chrono::duration &duration); + + void set_read_timeout(time_t sec, time_t usec = 0); + template + void set_read_timeout(const std::chrono::duration &duration); + + void set_write_timeout(time_t sec, time_t usec = 0); + template + void set_write_timeout(const std::chrono::duration &duration); + + void set_basic_auth(const std::string &username, const std::string &password); + void set_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const std::string &username, + const std::string &password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_url_encode(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const std::string &intf); + + void set_proxy(const std::string &host, int port); + void set_proxy_basic_auth(const std::string &username, + const std::string &password); + void set_proxy_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const std::string &username, + const std::string &password); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_ca_cert_path(const std::string &ca_cert_file_path, + const std::string &ca_cert_dir_path = std::string()); + void set_ca_cert_store(X509_STORE *ca_cert_store); + X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void enable_server_certificate_verification(bool enabled); +#endif + + void set_logger(Logger logger); + +protected: + struct Socket { + socket_t sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSL *ssl = nullptr; +#endif + + bool is_open() const { return sock != INVALID_SOCKET; } + }; + + virtual bool create_and_connect_socket(Socket &socket, Error &error); + + // All of: + // shutdown_ssl + // shutdown_socket + // close_socket + // should ONLY be called when socket_mutex_ is locked. + // Also, shutdown_ssl and close_socket should also NOT be called concurrently + // with a DIFFERENT thread sending requests using that socket. + virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully); + void shutdown_socket(Socket &socket) const; + void close_socket(Socket &socket); + + bool process_request(Stream &strm, Request &req, Response &res, + bool close_connection, Error &error); + + bool write_content_with_provider(Stream &strm, const Request &req, + Error &error) const; + + void copy_settings(const ClientImpl &rhs); + + // Socket endpoint information + const std::string host_; + const int port_; + const std::string host_and_port_; + + // Current open socket + Socket socket_; + mutable std::mutex socket_mutex_; + std::recursive_mutex request_mutex_; + + // These are all protected under socket_mutex + size_t socket_requests_in_flight_ = 0; + std::thread::id socket_requests_are_from_thread_ = std::thread::id(); + bool socket_should_be_closed_when_request_is_done_ = false; + + // Hostname-IP map + std::map addr_map_; + + // Default headers + Headers default_headers_; + + // Header writer + std::function header_writer_ = + detail::write_headers; + + // Settings + std::string client_cert_path_; + std::string client_key_path_; + + time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; + time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + + std::string basic_auth_username_; + std::string basic_auth_password_; + std::string bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string digest_auth_username_; + std::string digest_auth_password_; +#endif + + bool keep_alive_ = false; + bool follow_location_ = false; + + bool url_encode_ = true; + + int address_family_ = AF_UNSPEC; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = nullptr; + + bool compress_ = false; + bool decompress_ = true; + + std::string interface_; + + std::string proxy_host_; + int proxy_port_ = -1; + + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; + std::string proxy_bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string proxy_digest_auth_username_; + std::string proxy_digest_auth_password_; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string ca_cert_file_path_; + std::string ca_cert_dir_path_; + + X509_STORE *ca_cert_store_ = nullptr; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool server_certificate_verification_ = true; +#endif + + Logger logger_; + +private: + bool send_(Request &req, Response &res, Error &error); + Result send_(Request &&req); + + socket_t create_client_socket(Error &error) const; + bool read_response_line(Stream &strm, const Request &req, + Response &res) const; + bool write_request(Stream &strm, Request &req, bool close_connection, + Error &error); + bool redirect(Request &req, Response &res, Error &error); + bool handle_request(Stream &strm, Request &req, Response &res, + bool close_connection, Error &error); + std::unique_ptr send_with_content_provider( + Request &req, const char *body, size_t content_length, + ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const std::string &content_type, Error &error); + Result send_with_content_provider( + const std::string &method, const std::string &path, + const Headers &headers, const char *body, size_t content_length, + ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const std::string &content_type, Progress progress); + ContentProviderWithoutLength get_multipart_content_provider( + const std::string &boundary, const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) const; + + std::string adjust_host_string(const std::string &host) const; + + virtual bool process_socket(const Socket &socket, + std::function callback); + virtual bool is_ssl() const; +}; + +class Client { +public: + // Universal interface + explicit Client(const std::string &scheme_host_port); + + explicit Client(const std::string &scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path); + + // HTTP only interface + explicit Client(const std::string &host, int port); + + explicit Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + Client(Client &&) = default; + + ~Client(); + + bool is_valid() const; + + Result Get(const std::string &path); + Result Get(const std::string &path, const Headers &headers); + Result Get(const std::string &path, Progress progress); + Result Get(const std::string &path, const Headers &headers, + Progress progress); + Result Get(const std::string &path, ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const std::string &path, ContentReceiver content_receiver, + Progress progress); + Result Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + Result Get(const std::string &path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ContentReceiver content_receiver, + Progress progress = nullptr); + Result Get(const std::string &path, const Params ¶ms, + const Headers &headers, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress = nullptr); + + Result Head(const std::string &path); + Result Head(const std::string &path, const Headers &headers); + + Result Post(const std::string &path); + Result Post(const std::string &path, const Headers &headers); + Result Post(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Post(const std::string &path, const std::string &body, + const std::string &content_type); + Result Post(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Post(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Post(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Post(const std::string &path, const Params ¶ms); + Result Post(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Post(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress); + Result Post(const std::string &path, const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Put(const std::string &path); + Result Put(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Put(const std::string &path, const std::string &body, + const std::string &content_type); + Result Put(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Put(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Put(const std::string &path, size_t content_length, + ContentProvider content_provider, const std::string &content_type); + Result Put(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Put(const std::string &path, const Params ¶ms); + Result Put(const std::string &path, const Headers &headers, + const Params ¶ms); + Result Put(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress); + Result Put(const std::string &path, const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items); + + Result Patch(const std::string &path); + Result Patch(const std::string &path, const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const std::string &body, + const std::string &content_type); + Result Patch(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Patch(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + Result Patch(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + size_t content_length, ContentProvider content_provider, + const std::string &content_type); + Result Patch(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type); + + Result Delete(const std::string &path); + Result Delete(const std::string &path, const Headers &headers); + Result Delete(const std::string &path, const char *body, + size_t content_length, const std::string &content_type); + Result Delete(const std::string &path, const char *body, + size_t content_length, const std::string &content_type, + Progress progress); + Result Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress); + Result Delete(const std::string &path, const std::string &body, + const std::string &content_type); + Result Delete(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress); + Result Delete(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type); + Result Delete(const std::string &path, const Headers &headers, + const std::string &body, const std::string &content_type, + Progress progress); + + Result Options(const std::string &path); + Result Options(const std::string &path, const Headers &headers); + + bool send(Request &req, Response &res, Error &error); + Result send(const Request &req); + + void stop(); + + std::string host() const; + int port() const; + + size_t is_socket_open() const; + socket_t socket() const; + + void set_hostname_addr_map(std::map addr_map); + + void set_default_headers(Headers headers); + + void + set_header_writer(std::function const &writer); + + void set_address_family(int family); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + template + void + set_connection_timeout(const std::chrono::duration &duration); + + void set_read_timeout(time_t sec, time_t usec = 0); + template + void set_read_timeout(const std::chrono::duration &duration); + + void set_write_timeout(time_t sec, time_t usec = 0); + template + void set_write_timeout(const std::chrono::duration &duration); + + void set_basic_auth(const std::string &username, const std::string &password); + void set_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const std::string &username, + const std::string &password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_url_encode(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const std::string &intf); + + void set_proxy(const std::string &host, int port); + void set_proxy_basic_auth(const std::string &username, + const std::string &password); + void set_proxy_bearer_token_auth(const std::string &token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const std::string &username, + const std::string &password); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void enable_server_certificate_verification(bool enabled); +#endif + + void set_logger(Logger logger); + + // SSL +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_ca_cert_path(const std::string &ca_cert_file_path, + const std::string &ca_cert_dir_path = std::string()); + + void set_ca_cert_store(X509_STORE *ca_cert_store); + void load_ca_cert_store(const char *ca_cert, std::size_t size); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; +#endif + +private: + std::unique_ptr cli_; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool is_ssl_ = false; +#endif +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLServer : public Server { +public: + SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path = nullptr, + const char *client_ca_cert_dir_path = nullptr, + const char *private_key_password = nullptr); + + SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store = nullptr); + + SSLServer( + const std::function &setup_ssl_ctx_callback); + + ~SSLServer() override; + + bool is_valid() const override; + + SSL_CTX *ssl_context() const; + + void update_certs (X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store = nullptr); + +private: + bool process_and_close_socket(socket_t sock) override; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; +}; + +class SSLClient final : public ClientImpl { +public: + explicit SSLClient(const std::string &host); + + explicit SSLClient(const std::string &host, int port); + + explicit SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path, + const std::string &private_key_password = std::string()); + + explicit SSLClient(const std::string &host, int port, X509 *client_cert, + EVP_PKEY *client_key, + const std::string &private_key_password = std::string()); + + ~SSLClient() override; + + bool is_valid() const override; + + void set_ca_cert_store(X509_STORE *ca_cert_store); + void load_ca_cert_store(const char *ca_cert, std::size_t size); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; + +private: + bool create_and_connect_socket(Socket &socket, Error &error) override; + void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override; + void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully); + + bool process_socket(const Socket &socket, + std::function callback) override; + bool is_ssl() const override; + + bool connect_with_proxy(Socket &sock, Response &res, bool &success, + Error &error); + bool initialize_ssl(Socket &socket, Error &error); + + bool load_certs(); + + bool verify_host(X509 *server_cert) const; + bool verify_host_with_subject_alt_name(X509 *server_cert) const; + bool verify_host_with_common_name(X509 *server_cert) const; + bool check_host_name(const char *pattern, size_t pattern_len) const; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; + std::once_flag initialize_cert_; + + std::vector host_components_; + + long verify_result_ = 0; + + friend class ClientImpl; +}; +#endif + +/* + * Implementation of template methods. + */ + +namespace detail { + +template +inline void duration_to_sec_and_usec(const T &duration, U callback) { + auto sec = std::chrono::duration_cast(duration).count(); + auto usec = std::chrono::duration_cast( + duration - std::chrono::seconds(sec)) + .count(); + callback(static_cast(sec), static_cast(usec)); +} + +inline uint64_t get_header_value_u64(const Headers &headers, + const std::string &key, size_t id, + uint64_t def) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return std::strtoull(it->second.data(), nullptr, 10); + } + return def; +} + +} // namespace detail + +inline uint64_t Request::get_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(headers, key, id, 0); +} + +inline uint64_t Response::get_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(headers, key, id, 0); +} + +template +inline ssize_t Stream::write_format(const char *fmt, const Args &...args) { + const auto bufsiz = 2048; + std::array buf{}; + + auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); + if (sn <= 0) { return sn; } + + auto n = static_cast(sn); + + if (n >= buf.size() - 1) { + std::vector glowable_buf(buf.size()); + + while (n >= glowable_buf.size() - 1) { + glowable_buf.resize(glowable_buf.size() * 2); + n = static_cast( + snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); + } + return write(&glowable_buf[0], n); + } else { + return write(buf.data(), n); + } +} + +inline void default_socket_options(socket_t sock) { + int yes = 1; +#ifdef _WIN32 + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&yes), sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + reinterpret_cast(&yes), sizeof(yes)); +#else +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, + reinterpret_cast(&yes), sizeof(yes)); +#else + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&yes), sizeof(yes)); +#endif +#endif +} + +inline const char *status_message(int status) { + switch (status) { + case StatusCode::Continue_100: return "Continue"; + case StatusCode::SwitchingProtocol_101: return "Switching Protocol"; + case StatusCode::Processing_102: return "Processing"; + case StatusCode::EarlyHints_103: return "Early Hints"; + case StatusCode::OK_200: return "OK"; + case StatusCode::Created_201: return "Created"; + case StatusCode::Accepted_202: return "Accepted"; + case StatusCode::NonAuthoritativeInformation_203: + return "Non-Authoritative Information"; + case StatusCode::NoContent_204: return "No Content"; + case StatusCode::ResetContent_205: return "Reset Content"; + case StatusCode::PartialContent_206: return "Partial Content"; + case StatusCode::MultiStatus_207: return "Multi-Status"; + case StatusCode::AlreadyReported_208: return "Already Reported"; + case StatusCode::IMUsed_226: return "IM Used"; + case StatusCode::MultipleChoices_300: return "Multiple Choices"; + case StatusCode::MovedPermanently_301: return "Moved Permanently"; + case StatusCode::Found_302: return "Found"; + case StatusCode::SeeOther_303: return "See Other"; + case StatusCode::NotModified_304: return "Not Modified"; + case StatusCode::UseProxy_305: return "Use Proxy"; + case StatusCode::unused_306: return "unused"; + case StatusCode::TemporaryRedirect_307: return "Temporary Redirect"; + case StatusCode::PermanentRedirect_308: return "Permanent Redirect"; + case StatusCode::BadRequest_400: return "Bad Request"; + case StatusCode::Unauthorized_401: return "Unauthorized"; + case StatusCode::PaymentRequired_402: return "Payment Required"; + case StatusCode::Forbidden_403: return "Forbidden"; + case StatusCode::NotFound_404: return "Not Found"; + case StatusCode::MethodNotAllowed_405: return "Method Not Allowed"; + case StatusCode::NotAcceptable_406: return "Not Acceptable"; + case StatusCode::ProxyAuthenticationRequired_407: + return "Proxy Authentication Required"; + case StatusCode::RequestTimeout_408: return "Request Timeout"; + case StatusCode::Conflict_409: return "Conflict"; + case StatusCode::Gone_410: return "Gone"; + case StatusCode::LengthRequired_411: return "Length Required"; + case StatusCode::PreconditionFailed_412: return "Precondition Failed"; + case StatusCode::PayloadTooLarge_413: return "Payload Too Large"; + case StatusCode::UriTooLong_414: return "URI Too Long"; + case StatusCode::UnsupportedMediaType_415: return "Unsupported Media Type"; + case StatusCode::RangeNotSatisfiable_416: return "Range Not Satisfiable"; + case StatusCode::ExpectationFailed_417: return "Expectation Failed"; + case StatusCode::ImATeapot_418: return "I'm a teapot"; + case StatusCode::MisdirectedRequest_421: return "Misdirected Request"; + case StatusCode::UnprocessableContent_422: return "Unprocessable Content"; + case StatusCode::Locked_423: return "Locked"; + case StatusCode::FailedDependency_424: return "Failed Dependency"; + case StatusCode::TooEarly_425: return "Too Early"; + case StatusCode::UpgradeRequired_426: return "Upgrade Required"; + case StatusCode::PreconditionRequired_428: return "Precondition Required"; + case StatusCode::TooManyRequests_429: return "Too Many Requests"; + case StatusCode::RequestHeaderFieldsTooLarge_431: + return "Request Header Fields Too Large"; + case StatusCode::UnavailableForLegalReasons_451: + return "Unavailable For Legal Reasons"; + case StatusCode::NotImplemented_501: return "Not Implemented"; + case StatusCode::BadGateway_502: return "Bad Gateway"; + case StatusCode::ServiceUnavailable_503: return "Service Unavailable"; + case StatusCode::GatewayTimeout_504: return "Gateway Timeout"; + case StatusCode::HttpVersionNotSupported_505: + return "HTTP Version Not Supported"; + case StatusCode::VariantAlsoNegotiates_506: return "Variant Also Negotiates"; + case StatusCode::InsufficientStorage_507: return "Insufficient Storage"; + case StatusCode::LoopDetected_508: return "Loop Detected"; + case StatusCode::NotExtended_510: return "Not Extended"; + case StatusCode::NetworkAuthenticationRequired_511: + return "Network Authentication Required"; + + default: + case StatusCode::InternalServerError_500: return "Internal Server Error"; + } +} + +inline std::string get_bearer_token_auth(const Request &req) { + if (req.has_header("Authorization")) { + static std::string BearerHeaderPrefix = "Bearer "; + return req.get_header_value("Authorization") + .substr(BearerHeaderPrefix.length()); + } + return ""; +} + +template +inline Server & +Server::set_read_timeout(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); + return *this; +} + +template +inline Server & +Server::set_write_timeout(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); + return *this; +} + +template +inline Server & +Server::set_idle_interval(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); }); + return *this; +} + +inline std::string to_string(const Error error) { + switch (error) { + case Error::Success: return "Success (no error)"; + case Error::Connection: return "Could not establish connection"; + case Error::BindIPAddress: return "Failed to bind IP address"; + case Error::Read: return "Failed to read connection"; + case Error::Write: return "Failed to write connection"; + case Error::ExceedRedirectCount: return "Maximum redirect count exceeded"; + case Error::Canceled: return "Connection handling canceled"; + case Error::SSLConnection: return "SSL connection failed"; + case Error::SSLLoadingCerts: return "SSL certificate loading failed"; + case Error::SSLServerVerification: return "SSL server verification failed"; + case Error::UnsupportedMultipartBoundaryChars: + return "Unsupported HTTP multipart boundary characters"; + case Error::Compression: return "Compression failed"; + case Error::ConnectionTimeout: return "Connection timed out"; + case Error::ProxyConnection: return "Proxy connection failed"; + case Error::Unknown: return "Unknown"; + default: break; + } + + return "Invalid"; +} + +inline std::ostream &operator<<(std::ostream &os, const Error &obj) { + os << to_string(obj); + os << " (" << static_cast::type>(obj) << ')'; + return os; +} + +inline uint64_t Result::get_request_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(request_headers_, key, id, 0); +} + +template +inline void ClientImpl::set_connection_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { + set_connection_timeout(sec, usec); + }); +} + +template +inline void ClientImpl::set_read_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); +} + +template +inline void ClientImpl::set_write_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); +} + +template +inline void Client::set_connection_timeout( + const std::chrono::duration &duration) { + cli_->set_connection_timeout(duration); +} + +template +inline void +Client::set_read_timeout(const std::chrono::duration &duration) { + cli_->set_read_timeout(duration); +} + +template +inline void +Client::set_write_timeout(const std::chrono::duration &duration) { + cli_->set_write_timeout(duration); +} + +/* + * Forward declarations and types that will be part of the .h file if split into + * .h + .cc. + */ + +std::string hosted_at(const std::string &hostname); + +void hosted_at(const std::string &hostname, std::vector &addrs); + +std::string append_query_params(const std::string &path, const Params ¶ms); + +std::pair make_range_header(const Ranges &ranges); + +std::pair +make_basic_authentication_header(const std::string &username, + const std::string &password, + bool is_proxy = false); + +namespace detail { + +std::string encode_query_param(const std::string &value); + +std::string decode_url(const std::string &s, bool convert_plus_to_space); + +void read_file(const std::string &path, std::string &out); + +std::string trim_copy(const std::string &s); + +void divide( + const char *data, std::size_t size, char d, + std::function + fn); + +void divide( + const std::string &str, char d, + std::function + fn); + +void split(const char *b, const char *e, char d, + std::function fn); + +void split(const char *b, const char *e, char d, size_t m, + std::function fn); + +bool process_client_socket(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, + std::function callback); + +socket_t create_client_socket( + const std::string &host, const std::string &ip, int port, + int address_family, bool tcp_nodelay, SocketOptions socket_options, + time_t connection_timeout_sec, time_t connection_timeout_usec, + time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, const std::string &intf, Error &error); + +const char *get_header_value(const Headers &headers, const std::string &key, + size_t id = 0, const char *def = nullptr); + +std::string params_to_query_str(const Params ¶ms); + +void parse_query_text(const char *data, std::size_t size, Params ¶ms); + +void parse_query_text(const std::string &s, Params ¶ms); + +bool parse_multipart_boundary(const std::string &content_type, + std::string &boundary); + +bool parse_range_header(const std::string &s, Ranges &ranges); + +int close_socket(socket_t sock); + +ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags); + +ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags); + +enum class EncodingType { None = 0, Gzip, Brotli }; + +EncodingType encoding_type(const Request &req, const Response &res); + +class BufferStream final : public Stream { +public: + BufferStream() = default; + ~BufferStream() override = default; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + void get_local_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + + const std::string &get_buffer() const; + +private: + std::string buffer; + size_t position = 0; +}; + +class compressor { +public: + virtual ~compressor() = default; + + typedef std::function Callback; + virtual bool compress(const char *data, size_t data_length, bool last, + Callback callback) = 0; +}; + +class decompressor { +public: + virtual ~decompressor() = default; + + virtual bool is_valid() const = 0; + + typedef std::function Callback; + virtual bool decompress(const char *data, size_t data_length, + Callback callback) = 0; +}; + +class nocompressor final : public compressor { +public: + ~nocompressor() override = default; + + bool compress(const char *data, size_t data_length, bool /*last*/, + Callback callback) override; +}; + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +class gzip_compressor final : public compressor { +public: + gzip_compressor(); + ~gzip_compressor() override; + + bool compress(const char *data, size_t data_length, bool last, + Callback callback) override; + +private: + bool is_valid_ = false; + z_stream strm_; +}; + +class gzip_decompressor final : public decompressor { +public: + gzip_decompressor(); + ~gzip_decompressor() override; + + bool is_valid() const override; + + bool decompress(const char *data, size_t data_length, + Callback callback) override; + +private: + bool is_valid_ = false; + z_stream strm_; +}; +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +class brotli_compressor final : public compressor { +public: + brotli_compressor(); + ~brotli_compressor(); + + bool compress(const char *data, size_t data_length, bool last, + Callback callback) override; + +private: + BrotliEncoderState *state_ = nullptr; +}; + +class brotli_decompressor final : public decompressor { +public: + brotli_decompressor(); + ~brotli_decompressor(); + + bool is_valid() const override; + + bool decompress(const char *data, size_t data_length, + Callback callback) override; + +private: + BrotliDecoderResult decoder_r; + BrotliDecoderState *decoder_s = nullptr; +}; +#endif + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. +class stream_line_reader { +public: + stream_line_reader(Stream &strm, char *fixed_buffer, + size_t fixed_buffer_size); + const char *ptr() const; + size_t size() const; + bool end_with_crlf() const; + bool getline(); + +private: + void append(char c); + + Stream &strm_; + char *fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_ = 0; + std::string glowable_buffer_; +}; + +class mmap { +public: + mmap(const char *path); + ~mmap(); + + bool open(const char *path); + void close(); + + bool is_open() const; + size_t size() const; + const char *data() const; + +private: +#if defined(_WIN32) + HANDLE hFile_; + HANDLE hMapping_; +#else + int fd_; +#endif + size_t size_; + void *addr_; +}; + +} // namespace detail + +// ---------------------------------------------------------------------------- + +/* + * Implementation that will be part of the .cc file if split into .h + .cc. + */ + +namespace detail { + +inline bool is_hex(char c, int &v) { + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; +} + +inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, + int &val) { + if (i >= s.size()) { return false; } + + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { return false; } + auto v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; +} + +inline std::string from_i_to_hex(size_t n) { + static const auto charset = "0123456789abcdef"; + std::string ret; + do { + ret = charset[n & 15] + ret; + n >>= 4; + } while (n > 0); + return ret; +} + +inline size_t to_utf8(int code, char *buff) { + if (code < 0x0080) { + buff[0] = static_cast(code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = static_cast(0xF0 | ((code >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((code >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (code & 0x3F)); + return 4; + } + + // NOTREACHED + return 0; +} + +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c +inline std::string base64_encode(const std::string &in) { + static const auto lookup = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + std::string out; + out.reserve(in.size()); + + auto val = 0; + auto valb = -6; + + for (auto c : in) { + val = (val << 8) + static_cast(c); + valb += 8; + while (valb >= 0) { + out.push_back(lookup[(val >> valb) & 0x3F]); + valb -= 6; + } + } + + if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); } + + while (out.size() % 4) { + out.push_back('='); + } + + return out; +} + +inline bool is_file(const std::string &path) { +#ifdef _WIN32 + return _access_s(path.c_str(), 0) == 0; +#else + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); +#endif +} + +inline bool is_dir(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); +} + +inline bool is_valid_path(const std::string &path) { + size_t level = 0; + size_t i = 0; + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + if (path[i] == '\0') { + return false; + } else if (path[i] == '\\') { + return false; + } + i++; + } + + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { return false; } + level--; + } else { + level++; + } + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } + + return true; +} + +inline std::string encode_query_param(const std::string &value) { + std::ostringstream escaped; + escaped.fill('0'); + escaped << std::hex; + + for (auto c : value) { + if (std::isalnum(static_cast(c)) || c == '-' || c == '_' || + c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' || + c == ')') { + escaped << c; + } else { + escaped << std::uppercase; + escaped << '%' << std::setw(2) + << static_cast(static_cast(c)); + escaped << std::nouppercase; + } + } + + return escaped.str(); +} + +inline std::string encode_url(const std::string &s) { + std::string result; + result.reserve(s.size()); + + for (size_t i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': result += "%20"; break; + case '+': result += "%2B"; break; + case '\r': result += "%0D"; break; + case '\n': result += "%0A"; break; + case '\'': result += "%27"; break; + case ',': result += "%2C"; break; + // case ':': result += "%3A"; break; // ok? probably... + case ';': result += "%3B"; break; + default: + auto c = static_cast(s[i]); + if (c >= 0x80) { + result += '%'; + char hex[4]; + auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c); + assert(len == 2); + result.append(hex, static_cast(len)); + } else { + result += s[i]; + } + break; + } + } + + return result; +} + +inline std::string decode_url(const std::string &s, + bool convert_plus_to_space) { + std::string result; + + for (size_t i = 0; i < s.size(); i++) { + if (s[i] == '%' && i + 1 < s.size()) { + if (s[i + 1] == 'u') { + auto val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { result.append(buff, len); } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + auto val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += static_cast(val); + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (convert_plus_to_space && s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; +} + +inline void read_file(const std::string &path, std::string &out) { + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], static_cast(size)); +} + +inline std::string file_extension(const std::string &path) { + std::smatch m; + static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, re)) { return m[1].str(); } + return std::string(); +} + +inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; } + +inline std::pair trim(const char *b, const char *e, size_t left, + size_t right) { + while (b + left < e && is_space_or_tab(b[left])) { + left++; + } + while (right > 0 && is_space_or_tab(b[right - 1])) { + right--; + } + return std::make_pair(left, right); +} + +inline std::string trim_copy(const std::string &s) { + auto r = trim(s.data(), s.data() + s.size(), 0, s.size()); + return s.substr(r.first, r.second - r.first); +} + +inline std::string trim_double_quotes_copy(const std::string &s) { + if (s.length() >= 2 && s.front() == '"' && s.back() == '"') { + return s.substr(1, s.size() - 2); + } + return s; +} + +inline void +divide(const char *data, std::size_t size, char d, + std::function + fn) { + const auto it = std::find(data, data + size, d); + const auto found = static_cast(it != data + size); + const auto lhs_data = data; + const auto lhs_size = static_cast(it - data); + const auto rhs_data = it + found; + const auto rhs_size = size - lhs_size - found; + + fn(lhs_data, lhs_size, rhs_data, rhs_size); +} + +inline void +divide(const std::string &str, char d, + std::function + fn) { + divide(str.data(), str.size(), d, std::move(fn)); +} + +inline void split(const char *b, const char *e, char d, + std::function fn) { + return split(b, e, d, (std::numeric_limits::max)(), std::move(fn)); +} + +inline void split(const char *b, const char *e, char d, size_t m, + std::function fn) { + size_t i = 0; + size_t beg = 0; + size_t count = 1; + + while (e ? (b + i < e) : (b[i] != '\0')) { + if (b[i] == d && count < m) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + beg = i + 1; + count++; + } + i++; + } + + if (i) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + } +} + +inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer, + size_t fixed_buffer_size) + : strm_(strm), fixed_buffer_(fixed_buffer), + fixed_buffer_size_(fixed_buffer_size) {} + +inline const char *stream_line_reader::ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } +} + +inline size_t stream_line_reader::size() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_used_size_; + } else { + return glowable_buffer_.size(); + } +} + +inline bool stream_line_reader::end_with_crlf() const { + auto end = ptr() + size(); + return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; +} + +inline bool stream_line_reader::getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0;; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { break; } + } + + return true; +} + +inline void stream_line_reader::append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } +} + +inline mmap::mmap(const char *path) +#if defined(_WIN32) + : hFile_(NULL), hMapping_(NULL) +#else + : fd_(-1) +#endif + , + size_(0), addr_(nullptr) { + open(path); +} + +inline mmap::~mmap() { close(); } + +inline bool mmap::open(const char *path) { + close(); + +#if defined(_WIN32) + std::wstring wpath; + for (size_t i = 0; i < strlen(path); i++) { + wpath += path[i]; + } + + hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, + OPEN_EXISTING, NULL); + + if (hFile_ == INVALID_HANDLE_VALUE) { return false; } + + LARGE_INTEGER size{}; + if (!::GetFileSizeEx(hFile_, &size)) { return false; } + size_ = static_cast(size.QuadPart); + + hMapping_ = + ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL); + + if (hMapping_ == NULL) { + close(); + return false; + } + + addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0); +#else + fd_ = ::open(path, O_RDONLY); + if (fd_ == -1) { return false; } + + struct stat sb; + if (fstat(fd_, &sb) == -1) { + close(); + return false; + } + size_ = static_cast(sb.st_size); + + addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0); +#endif + + if (addr_ == nullptr) { + close(); + return false; + } + + return true; +} + +inline bool mmap::is_open() const { return addr_ != nullptr; } + +inline size_t mmap::size() const { return size_; } + +inline const char *mmap::data() const { + return static_cast(addr_); +} + +inline void mmap::close() { +#if defined(_WIN32) + if (addr_) { + ::UnmapViewOfFile(addr_); + addr_ = nullptr; + } + + if (hMapping_) { + ::CloseHandle(hMapping_); + hMapping_ = NULL; + } + + if (hFile_ != INVALID_HANDLE_VALUE) { + ::CloseHandle(hFile_); + hFile_ = INVALID_HANDLE_VALUE; + } +#else + if (addr_ != nullptr) { + munmap(addr_, size_); + addr_ = nullptr; + } + + if (fd_ != -1) { + ::close(fd_); + fd_ = -1; + } +#endif + size_ = 0; +} +inline int close_socket(socket_t sock) { +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif +} + +template inline ssize_t handle_EINTR(T fn) { + ssize_t res = 0; + while (true) { + res = fn(); + if (res < 0 && errno == EINTR) { continue; } + break; + } + return res; +} + +inline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) { + return handle_EINTR([&]() { + return recv(sock, +#ifdef _WIN32 + static_cast(ptr), static_cast(size), +#else + ptr, size, +#endif + flags); + }); +} + +inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size, + int flags) { + return handle_EINTR([&]() { + return send(sock, +#ifdef _WIN32 + static_cast(ptr), static_cast(size), +#else + ptr, size, +#endif + flags); + }); +} + +inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return -1; } +#endif + + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { + return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); + }); +#endif +} + +inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return -1; } +#endif + + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { + return select(static_cast(sock + 1), nullptr, &fds, nullptr, &tv); + }); +#endif +} + +inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, + time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN | POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + + if (poll_res == 0) { return Error::ConnectionTimeout; } + + if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) { + auto error = 0; + socklen_t len = sizeof(error); + auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len); + auto successful = res >= 0 && !error; + return successful ? Error::Success : Error::Connection; + } + + return Error::Connection; +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return Error::Connection; } +#endif + + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + auto ret = handle_EINTR([&]() { + return select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv); + }); + + if (ret == 0) { return Error::ConnectionTimeout; } + + if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { + auto error = 0; + socklen_t len = sizeof(error); + auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len); + auto successful = res >= 0 && !error; + return successful ? Error::Success : Error::Connection; + } + return Error::Connection; +#endif +} + +inline bool is_socket_alive(socket_t sock) { + const auto val = detail::select_read(sock, 0, 0); + if (val == 0) { + return true; + } else if (val < 0 && errno == EBADF) { + return false; + } + char buf[1]; + return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0; +} + +class SocketStream final : public Stream { +public: + SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, time_t write_timeout_usec); + ~SocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + void get_local_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + +private: + socket_t sock_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; + + std::vector read_buff_; + size_t read_buff_off_ = 0; + size_t read_buff_content_size_ = 0; + + static const size_t read_buff_size_ = 1024l * 4; +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLSocketStream final : public Stream { +public: + SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec); + ~SSLSocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + void get_local_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + +private: + socket_t sock_; + SSL *ssl_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; +}; +#endif + +inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) { + using namespace std::chrono; + auto start = steady_clock::now(); + while (true) { + auto val = select_read(sock, 0, 10000); + if (val < 0) { + return false; + } else if (val == 0) { + auto current = steady_clock::now(); + auto duration = duration_cast(current - start); + auto timeout = keep_alive_timeout_sec * 1000; + if (duration.count() > timeout) { return false; } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } else { + return true; + } + } +} + +template +inline bool +process_server_socket_core(const std::atomic &svr_sock, socket_t sock, + size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, T callback) { + assert(keep_alive_max_count > 0); + auto ret = false; + auto count = keep_alive_max_count; + while (svr_sock != INVALID_SOCKET && count > 0 && + keep_alive(sock, keep_alive_timeout_sec)) { + auto close_connection = count == 1; + auto connection_closed = false; + ret = callback(close_connection, connection_closed); + if (!ret || connection_closed) { break; } + count--; + } + return ret; +} + +template +inline bool +process_server_socket(const std::atomic &svr_sock, socket_t sock, + size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + return process_server_socket_core( + svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec, + [&](bool close_connection, bool &connection_closed) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +inline bool process_client_socket(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, + std::function callback) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +inline int shutdown_socket(socket_t sock) { +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif +} + +template +socket_t create_socket(const std::string &host, const std::string &ip, int port, + int address_family, int socket_flags, bool tcp_nodelay, + SocketOptions socket_options, + BindOrConnect bind_or_connect) { + // Get address info + const char *node = nullptr; + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (!ip.empty()) { + node = ip.c_str(); + // Ask getaddrinfo to convert IP in c-string to address + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + } else { + if (!host.empty()) { node = host.c_str(); } + hints.ai_family = address_family; + hints.ai_flags = socket_flags; + } + +#ifndef _WIN32 + if (hints.ai_family == AF_UNIX) { + const auto addrlen = host.length(); + if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; } + + auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol); + if (sock != INVALID_SOCKET) { + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + std::copy(host.begin(), host.end(), addr.sun_path); + + hints.ai_addr = reinterpret_cast(&addr); + hints.ai_addrlen = static_cast( + sizeof(addr) - sizeof(addr.sun_path) + addrlen); + + fcntl(sock, F_SETFD, FD_CLOEXEC); + if (socket_options) { socket_options(sock); } + + if (!bind_or_connect(sock, hints)) { + close_socket(sock); + sock = INVALID_SOCKET; + } + } + return sock; + } +#endif + + auto service = std::to_string(port); + + if (getaddrinfo(node, service.c_str(), &hints, &result)) { +#if defined __linux__ && !defined __ANDROID__ + res_init(); +#endif + return INVALID_SOCKET; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket +#ifdef _WIN32 + auto sock = + WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0, + WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED); + /** + * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 + * and above the socket creation fails on older Windows Systems. + * + * Let's try to create a socket the old way in this case. + * + * Reference: + * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa + * + * WSA_FLAG_NO_HANDLE_INHERIT: + * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with + * SP1, and later + * + */ + if (sock == INVALID_SOCKET) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + } +#else + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#endif + if (sock == INVALID_SOCKET) { continue; } + +#ifndef _WIN32 + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { + close_socket(sock); + continue; + } +#endif + + if (tcp_nodelay) { + auto yes = 1; +#ifdef _WIN32 + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast(&yes), sizeof(yes)); +#else + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast(&yes), sizeof(yes)); +#endif + } + + if (socket_options) { socket_options(sock); } + + if (rp->ai_family == AF_INET6) { + auto no = 0; +#ifdef _WIN32 + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast(&no), sizeof(no)); +#else + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast(&no), sizeof(no)); +#endif + } + + // bind or connect + if (bind_or_connect(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return INVALID_SOCKET; +} + +inline void set_nonblocking(socket_t sock, bool nonblocking) { +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, + nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif +} + +inline bool is_connection_error() { +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif +} + +inline bool bind_ip_address(socket_t sock, const std::string &host) { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (getaddrinfo(host.c_str(), "0", &hints, &result)) { return false; } + + auto ret = false; + for (auto rp = result; rp; rp = rp->ai_next) { + const auto &ai = *rp; + if (!::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + ret = true; + break; + } + } + + freeaddrinfo(result); + return ret; +} + +#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__ +#define USE_IF2IP +#endif + +#ifdef USE_IF2IP +inline std::string if2ip(int address_family, const std::string &ifn) { + struct ifaddrs *ifap; + getifaddrs(&ifap); + std::string addr_candidate; + for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifn == ifa->ifa_name && + (AF_UNSPEC == address_family || + ifa->ifa_addr->sa_family == address_family)) { + if (ifa->ifa_addr->sa_family == AF_INET) { + auto sa = reinterpret_cast(ifa->ifa_addr); + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { + freeifaddrs(ifap); + return std::string(buf, INET_ADDRSTRLEN); + } + } else if (ifa->ifa_addr->sa_family == AF_INET6) { + auto sa = reinterpret_cast(ifa->ifa_addr); + if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) { + char buf[INET6_ADDRSTRLEN] = {}; + if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) { + // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL + auto s6_addr_head = sa->sin6_addr.s6_addr[0]; + if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) { + addr_candidate = std::string(buf, INET6_ADDRSTRLEN); + } else { + freeifaddrs(ifap); + return std::string(buf, INET6_ADDRSTRLEN); + } + } + } + } + } + } + freeifaddrs(ifap); + return addr_candidate; +} +#endif + +inline socket_t create_client_socket( + const std::string &host, const std::string &ip, int port, + int address_family, bool tcp_nodelay, SocketOptions socket_options, + time_t connection_timeout_sec, time_t connection_timeout_usec, + time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, const std::string &intf, Error &error) { + auto sock = create_socket( + host, ip, port, address_family, 0, tcp_nodelay, std::move(socket_options), + [&](socket_t sock2, struct addrinfo &ai) -> bool { + if (!intf.empty()) { +#ifdef USE_IF2IP + auto ip_from_if = if2ip(address_family, intf); + if (ip_from_if.empty()) { ip_from_if = intf; } + if (!bind_ip_address(sock2, ip_from_if)) { + error = Error::BindIPAddress; + return false; + } +#endif + } + + set_nonblocking(sock2, true); + + auto ret = + ::connect(sock2, ai.ai_addr, static_cast(ai.ai_addrlen)); + + if (ret < 0) { + if (is_connection_error()) { + error = Error::Connection; + return false; + } + error = wait_until_socket_is_ready(sock2, connection_timeout_sec, + connection_timeout_usec); + if (error != Error::Success) { return false; } + } + + set_nonblocking(sock2, false); + + { +#ifdef _WIN32 + auto timeout = static_cast(read_timeout_sec * 1000 + + read_timeout_usec / 1000); + setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)); +#else + timeval tv; + tv.tv_sec = static_cast(read_timeout_sec); + tv.tv_usec = static_cast(read_timeout_usec); + setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&tv), sizeof(tv)); +#endif + } + { + +#ifdef _WIN32 + auto timeout = static_cast(write_timeout_sec * 1000 + + write_timeout_usec / 1000); + setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)); +#else + timeval tv; + tv.tv_sec = static_cast(write_timeout_sec); + tv.tv_usec = static_cast(write_timeout_usec); + setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, + reinterpret_cast(&tv), sizeof(tv)); +#endif + } + + error = Error::Success; + return true; + }); + + if (sock != INVALID_SOCKET) { + error = Error::Success; + } else { + if (error == Error::Success) { error = Error::Connection; } + } + + return sock; +} + +inline bool get_ip_and_port(const struct sockaddr_storage &addr, + socklen_t addr_len, std::string &ip, int &port) { + if (addr.ss_family == AF_INET) { + port = ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + port = + ntohs(reinterpret_cast(&addr)->sin6_port); + } else { + return false; + } + + std::array ipstr{}; + if (getnameinfo(reinterpret_cast(&addr), addr_len, + ipstr.data(), static_cast(ipstr.size()), nullptr, + 0, NI_NUMERICHOST)) { + return false; + } + + ip = ipstr.data(); + return true; +} + +inline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (!getsockname(sock, reinterpret_cast(&addr), + &addr_len)) { + get_ip_and_port(addr, addr_len, ip, port); + } +} + +inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + if (!getpeername(sock, reinterpret_cast(&addr), + &addr_len)) { +#ifndef _WIN32 + if (addr.ss_family == AF_UNIX) { +#if defined(__linux__) + struct ucred ucred; + socklen_t len = sizeof(ucred); + if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) { + port = ucred.pid; + } +#elif defined(SOL_LOCAL) && defined(SO_PEERPID) // __APPLE__ + pid_t pid; + socklen_t len = sizeof(pid); + if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) { + port = pid; + } +#endif + return; + } +#endif + get_ip_and_port(addr, addr_len, ip, port); + } +} + +inline constexpr unsigned int str2tag_core(const char *s, size_t l, + unsigned int h) { + return (l == 0) + ? h + : str2tag_core( + s + 1, l - 1, + // Unsets the 6 high bits of h, therefore no overflow happens + (((std::numeric_limits::max)() >> 6) & + h * 33) ^ + static_cast(*s)); +} + +inline unsigned int str2tag(const std::string &s) { + return str2tag_core(s.data(), s.size(), 0); +} + +namespace udl { + +inline constexpr unsigned int operator"" _t(const char *s, size_t l) { + return str2tag_core(s, l, 0); +} + +} // namespace udl + +inline std::string +find_content_type(const std::string &path, + const std::map &user_data, + const std::string &default_content_type) { + auto ext = file_extension(path); + + auto it = user_data.find(ext); + if (it != user_data.end()) { return it->second; } + + using udl::operator""_t; + + switch (str2tag(ext)) { + default: return default_content_type; + + case "css"_t: return "text/css"; + case "csv"_t: return "text/csv"; + case "htm"_t: + case "html"_t: return "text/html"; + case "js"_t: + case "mjs"_t: return "text/javascript"; + case "txt"_t: return "text/plain"; + case "vtt"_t: return "text/vtt"; + + case "apng"_t: return "image/apng"; + case "avif"_t: return "image/avif"; + case "bmp"_t: return "image/bmp"; + case "gif"_t: return "image/gif"; + case "png"_t: return "image/png"; + case "svg"_t: return "image/svg+xml"; + case "webp"_t: return "image/webp"; + case "ico"_t: return "image/x-icon"; + case "tif"_t: return "image/tiff"; + case "tiff"_t: return "image/tiff"; + case "jpg"_t: + case "jpeg"_t: return "image/jpeg"; + + case "mp4"_t: return "video/mp4"; + case "mpeg"_t: return "video/mpeg"; + case "webm"_t: return "video/webm"; + + case "mp3"_t: return "audio/mp3"; + case "mpga"_t: return "audio/mpeg"; + case "weba"_t: return "audio/webm"; + case "wav"_t: return "audio/wave"; + + case "otf"_t: return "font/otf"; + case "ttf"_t: return "font/ttf"; + case "woff"_t: return "font/woff"; + case "woff2"_t: return "font/woff2"; + + case "7z"_t: return "application/x-7z-compressed"; + case "atom"_t: return "application/atom+xml"; + case "pdf"_t: return "application/pdf"; + case "json"_t: return "application/json"; + case "rss"_t: return "application/rss+xml"; + case "tar"_t: return "application/x-tar"; + case "xht"_t: + case "xhtml"_t: return "application/xhtml+xml"; + case "xslt"_t: return "application/xslt+xml"; + case "xml"_t: return "application/xml"; + case "gz"_t: return "application/gzip"; + case "zip"_t: return "application/zip"; + case "wasm"_t: return "application/wasm"; + } +} + +inline bool can_compress_content_type(const std::string &content_type) { + using udl::operator""_t; + + auto tag = str2tag(content_type); + + switch (tag) { + case "image/svg+xml"_t: + case "application/javascript"_t: + case "application/json"_t: + case "application/xml"_t: + case "application/protobuf"_t: + case "application/xhtml+xml"_t: return true; + + default: + return !content_type.rfind("text/", 0) && tag != "text/event-stream"_t; + } +} + +inline EncodingType encoding_type(const Request &req, const Response &res) { + auto ret = + detail::can_compress_content_type(res.get_header_value("Content-Type")); + if (!ret) { return EncodingType::None; } + + const auto &s = req.get_header_value("Accept-Encoding"); + (void)(s); + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + // TODO: 'Accept-Encoding' has br, not br;q=0 + ret = s.find("br") != std::string::npos; + if (ret) { return EncodingType::Brotli; } +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 + ret = s.find("gzip") != std::string::npos; + if (ret) { return EncodingType::Gzip; } +#endif + + return EncodingType::None; +} + +inline bool nocompressor::compress(const char *data, size_t data_length, + bool /*last*/, Callback callback) { + if (!data_length) { return true; } + return callback(data, data_length); +} + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +inline gzip_compressor::gzip_compressor() { + std::memset(&strm_, 0, sizeof(strm_)); + strm_.zalloc = Z_NULL; + strm_.zfree = Z_NULL; + strm_.opaque = Z_NULL; + + is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, + Z_DEFAULT_STRATEGY) == Z_OK; +} + +inline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); } + +inline bool gzip_compressor::compress(const char *data, size_t data_length, + bool last, Callback callback) { + assert(is_valid_); + + do { + constexpr size_t max_avail_in = + (std::numeric_limits::max)(); + + strm_.avail_in = static_cast( + (std::min)(data_length, max_avail_in)); + strm_.next_in = const_cast(reinterpret_cast(data)); + + data_length -= strm_.avail_in; + data += strm_.avail_in; + + auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH; + auto ret = Z_OK; + + std::array buff{}; + do { + strm_.avail_out = static_cast(buff.size()); + strm_.next_out = reinterpret_cast(buff.data()); + + ret = deflate(&strm_, flush); + if (ret == Z_STREAM_ERROR) { return false; } + + if (!callback(buff.data(), buff.size() - strm_.avail_out)) { + return false; + } + } while (strm_.avail_out == 0); + + assert((flush == Z_FINISH && ret == Z_STREAM_END) || + (flush == Z_NO_FLUSH && ret == Z_OK)); + assert(strm_.avail_in == 0); + } while (data_length > 0); + + return true; +} + +inline gzip_decompressor::gzip_decompressor() { + std::memset(&strm_, 0, sizeof(strm_)); + strm_.zalloc = Z_NULL; + strm_.zfree = Z_NULL; + strm_.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value + // to ensure that any gzip stream can be decoded. The offset of 32 specifies + // that the stream type should be automatically detected either gzip or + // deflate. + is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK; +} + +inline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); } + +inline bool gzip_decompressor::is_valid() const { return is_valid_; } + +inline bool gzip_decompressor::decompress(const char *data, size_t data_length, + Callback callback) { + assert(is_valid_); + + auto ret = Z_OK; + + do { + constexpr size_t max_avail_in = + (std::numeric_limits::max)(); + + strm_.avail_in = static_cast( + (std::min)(data_length, max_avail_in)); + strm_.next_in = const_cast(reinterpret_cast(data)); + + data_length -= strm_.avail_in; + data += strm_.avail_in; + + std::array buff{}; + while (strm_.avail_in > 0 && ret == Z_OK) { + strm_.avail_out = static_cast(buff.size()); + strm_.next_out = reinterpret_cast(buff.data()); + + ret = inflate(&strm_, Z_NO_FLUSH); + + assert(ret != Z_STREAM_ERROR); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: inflateEnd(&strm_); return false; + } + + if (!callback(buff.data(), buff.size() - strm_.avail_out)) { + return false; + } + } + + if (ret != Z_OK && ret != Z_STREAM_END) { return false; } + + } while (data_length > 0); + + return true; +} +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +inline brotli_compressor::brotli_compressor() { + state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); +} + +inline brotli_compressor::~brotli_compressor() { + BrotliEncoderDestroyInstance(state_); +} + +inline bool brotli_compressor::compress(const char *data, size_t data_length, + bool last, Callback callback) { + std::array buff{}; + + auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS; + auto available_in = data_length; + auto next_in = reinterpret_cast(data); + + for (;;) { + if (last) { + if (BrotliEncoderIsFinished(state_)) { break; } + } else { + if (!available_in) { break; } + } + + auto available_out = buff.size(); + auto next_out = buff.data(); + + if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in, + &available_out, &next_out, nullptr)) { + return false; + } + + auto output_bytes = buff.size() - available_out; + if (output_bytes) { + callback(reinterpret_cast(buff.data()), output_bytes); + } + } + + return true; +} + +inline brotli_decompressor::brotli_decompressor() { + decoder_s = BrotliDecoderCreateInstance(0, 0, 0); + decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT + : BROTLI_DECODER_RESULT_ERROR; +} + +inline brotli_decompressor::~brotli_decompressor() { + if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); } +} + +inline bool brotli_decompressor::is_valid() const { return decoder_s; } + +inline bool brotli_decompressor::decompress(const char *data, + size_t data_length, + Callback callback) { + if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || + decoder_r == BROTLI_DECODER_RESULT_ERROR) { + return 0; + } + + auto next_in = reinterpret_cast(data); + size_t avail_in = data_length; + size_t total_out; + + decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + + std::array buff{}; + while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { + char *next_out = buff.data(); + size_t avail_out = buff.size(); + + decoder_r = BrotliDecoderDecompressStream( + decoder_s, &avail_in, &next_in, &avail_out, + reinterpret_cast(&next_out), &total_out); + + if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; } + + if (!callback(buff.data(), buff.size() - avail_out)) { return false; } + } + + return decoder_r == BROTLI_DECODER_RESULT_SUCCESS || + decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; +} +#endif + +inline bool has_header(const Headers &headers, const std::string &key) { + return headers.find(key) != headers.end(); +} + +inline const char *get_header_value(const Headers &headers, + const std::string &key, size_t id, + const char *def) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { return it->second.c_str(); } + return def; +} + +inline bool compare_case_ignore(const std::string &a, const std::string &b) { + if (a.size() != b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (::tolower(a[i]) != ::tolower(b[i])) { return false; } + } + return true; +} + +template +inline bool parse_header(const char *beg, const char *end, T fn) { + // Skip trailing spaces and tabs. + while (beg < end && is_space_or_tab(end[-1])) { + end--; + } + + auto p = beg; + while (p < end && *p != ':') { + p++; + } + + if (p == end) { return false; } + + auto key_end = p; + + if (*p++ != ':') { return false; } + + while (p < end && is_space_or_tab(*p)) { + p++; + } + + if (p < end) { + auto key_len = key_end - beg; + if (!key_len) { return false; } + + auto key = std::string(beg, key_end); + auto val = compare_case_ignore(key, "Location") + ? std::string(p, end) + : decode_url(std::string(p, end), false); + fn(key, val); + return true; + } + + return false; +} + +inline bool read_headers(Stream &strm, Headers &headers) { + const auto bufsiz = 2048; + char buf[bufsiz]; + stream_line_reader line_reader(strm, buf, bufsiz); + + for (;;) { + if (!line_reader.getline()) { return false; } + + // Check if the line ends with CRLF. + auto line_terminator_len = 2; + if (line_reader.end_with_crlf()) { + // Blank line indicates end of headers. + if (line_reader.size() == 2) { break; } +#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR + } else { + // Blank line indicates end of headers. + if (line_reader.size() == 1) { break; } + line_terminator_len = 1; + } +#else + } else { + continue; // Skip invalid line. + } +#endif + + if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; } + + // Exclude line terminator + auto end = line_reader.ptr() + line_reader.size() - line_terminator_len; + + parse_header(line_reader.ptr(), end, + [&](const std::string &key, const std::string &val) { + headers.emplace(key, val); + }); + } + + return true; +} + +inline bool read_content_with_length(Stream &strm, uint64_t len, + Progress progress, + ContentReceiverWithProgress out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { return false; } + + if (!out(buf, static_cast(n), r, len)) { return false; } + r += static_cast(n); + + if (progress) { + if (!progress(r, len)) { return false; } + } + } + + return true; +} + +inline void skip_content_with_length(Stream &strm, uint64_t len) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { return; } + r += static_cast(n); + } +} + +inline bool read_content_without_length(Stream &strm, + ContentReceiverWithProgress out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + for (;;) { + auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); + if (n <= 0) { return true; } + + if (!out(buf, static_cast(n), r, 0)) { return false; } + r += static_cast(n); + } + + return true; +} + +template +inline bool read_content_chunked(Stream &strm, T &x, + ContentReceiverWithProgress out) { + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader line_reader(strm, buf, bufsiz); + + if (!line_reader.getline()) { return false; } + + unsigned long chunk_len; + while (true) { + char *end_ptr; + + chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16); + + if (end_ptr == line_reader.ptr()) { return false; } + if (chunk_len == ULONG_MAX) { return false; } + + if (chunk_len == 0) { break; } + + if (!read_content_with_length(strm, chunk_len, nullptr, out)) { + return false; + } + + if (!line_reader.getline()) { return false; } + + if (strcmp(line_reader.ptr(), "\r\n") != 0) { return false; } + + if (!line_reader.getline()) { return false; } + } + + assert(chunk_len == 0); + + // Trailer + if (!line_reader.getline()) { return false; } + + while (strcmp(line_reader.ptr(), "\r\n") != 0) { + if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; } + + // Exclude line terminator + constexpr auto line_terminator_len = 2; + auto end = line_reader.ptr() + line_reader.size() - line_terminator_len; + + parse_header(line_reader.ptr(), end, + [&](const std::string &key, const std::string &val) { + x.headers.emplace(key, val); + }); + + if (!line_reader.getline()) { return false; } + } + + return true; +} + +inline bool is_chunked_transfer_encoding(const Headers &headers) { + return compare_case_ignore( + get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked"); +} + +template +bool prepare_content_receiver(T &x, int &status, + ContentReceiverWithProgress receiver, + bool decompress, U callback) { + if (decompress) { + std::string encoding = x.get_header_value("Content-Encoding"); + std::unique_ptr decompressor; + + if (encoding == "gzip" || encoding == "deflate") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + decompressor = detail::make_unique(); +#else + status = StatusCode::UnsupportedMediaType_415; + return false; +#endif + } else if (encoding.find("br") != std::string::npos) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + decompressor = detail::make_unique(); +#else + status = StatusCode::UnsupportedMediaType_415; + return false; +#endif + } + + if (decompressor) { + if (decompressor->is_valid()) { + ContentReceiverWithProgress out = [&](const char *buf, size_t n, + uint64_t off, uint64_t len) { + return decompressor->decompress(buf, n, + [&](const char *buf2, size_t n2) { + return receiver(buf2, n2, off, len); + }); + }; + return callback(std::move(out)); + } else { + status = StatusCode::InternalServerError_500; + return false; + } + } + } + + ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off, + uint64_t len) { + return receiver(buf, n, off, len); + }; + return callback(std::move(out)); +} + +template +bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, + Progress progress, ContentReceiverWithProgress receiver, + bool decompress) { + return prepare_content_receiver( + x, status, std::move(receiver), decompress, + [&](const ContentReceiverWithProgress &out) { + auto ret = true; + auto exceed_payload_max_length = false; + + if (is_chunked_transfer_encoding(x.headers)) { + ret = read_content_chunked(strm, x, out); + } else if (!has_header(x.headers, "Content-Length")) { + ret = read_content_without_length(strm, out); + } else { + auto len = get_header_value_u64(x.headers, "Content-Length", 0, 0); + if (len > payload_max_length) { + exceed_payload_max_length = true; + skip_content_with_length(strm, len); + ret = false; + } else if (len > 0) { + ret = read_content_with_length(strm, len, std::move(progress), out); + } + } + + if (!ret) { + status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413 + : StatusCode::BadRequest_400; + } + return ret; + }); +} // namespace detail + +inline ssize_t write_headers(Stream &strm, const Headers &headers) { + ssize_t write_len = 0; + for (const auto &x : headers) { + auto len = + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { return len; } + write_len += len; + } + auto len = strm.write("\r\n"); + if (len < 0) { return len; } + write_len += len; + return write_len; +} + +inline bool write_data(Stream &strm, const char *d, size_t l) { + size_t offset = 0; + while (offset < l) { + auto length = strm.write(d + offset, l - offset); + if (length < 0) { return false; } + offset += static_cast(length); + } + return true; +} + +template +inline bool write_content(Stream &strm, const ContentProvider &content_provider, + size_t offset, size_t length, T is_shutting_down, + Error &error) { + size_t end_offset = offset + length; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + if (strm.is_writable() && write_data(strm, d, l)) { + offset += l; + } else { + ok = false; + } + } + return ok; + }; + + data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; + + while (offset < end_offset && !is_shutting_down()) { + if (!strm.is_writable()) { + error = Error::Write; + return false; + } else if (!content_provider(offset, end_offset - offset, data_sink)) { + error = Error::Canceled; + return false; + } else if (!ok) { + error = Error::Write; + return false; + } + } + + error = Error::Success; + return true; +} + +template +inline bool write_content(Stream &strm, const ContentProvider &content_provider, + size_t offset, size_t length, + const T &is_shutting_down) { + auto error = Error::Success; + return write_content(strm, content_provider, offset, length, is_shutting_down, + error); +} + +template +inline bool +write_content_without_length(Stream &strm, + const ContentProvider &content_provider, + const T &is_shutting_down) { + size_t offset = 0; + auto data_available = true; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + offset += l; + if (!strm.is_writable() || !write_data(strm, d, l)) { ok = false; } + } + return ok; + }; + + data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; + + data_sink.done = [&](void) { data_available = false; }; + + while (data_available && !is_shutting_down()) { + if (!strm.is_writable()) { + return false; + } else if (!content_provider(offset, 0, data_sink)) { + return false; + } else if (!ok) { + return false; + } + } + return true; +} + +template +inline bool +write_content_chunked(Stream &strm, const ContentProvider &content_provider, + const T &is_shutting_down, U &compressor, Error &error) { + size_t offset = 0; + auto data_available = true; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + data_available = l > 0; + offset += l; + + std::string payload; + if (compressor.compress(d, l, false, + [&](const char *data, size_t data_len) { + payload.append(data, data_len); + return true; + })) { + if (!payload.empty()) { + // Emit chunked response header and footer for each chunk + auto chunk = + from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; + if (!strm.is_writable() || + !write_data(strm, chunk.data(), chunk.size())) { + ok = false; + } + } + } else { + ok = false; + } + } + return ok; + }; + + data_sink.is_writable = [&]() -> bool { return strm.is_writable(); }; + + auto done_with_trailer = [&](const Headers *trailer) { + if (!ok) { return; } + + data_available = false; + + std::string payload; + if (!compressor.compress(nullptr, 0, true, + [&](const char *data, size_t data_len) { + payload.append(data, data_len); + return true; + })) { + ok = false; + return; + } + + if (!payload.empty()) { + // Emit chunked response header and footer for each chunk + auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; + if (!strm.is_writable() || + !write_data(strm, chunk.data(), chunk.size())) { + ok = false; + return; + } + } + + static const std::string done_marker("0\r\n"); + if (!write_data(strm, done_marker.data(), done_marker.size())) { + ok = false; + } + + // Trailer + if (trailer) { + for (const auto &kv : *trailer) { + std::string field_line = kv.first + ": " + kv.second + "\r\n"; + if (!write_data(strm, field_line.data(), field_line.size())) { + ok = false; + } + } + } + + static const std::string crlf("\r\n"); + if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; } + }; + + data_sink.done = [&](void) { done_with_trailer(nullptr); }; + + data_sink.done_with_trailer = [&](const Headers &trailer) { + done_with_trailer(&trailer); + }; + + while (data_available && !is_shutting_down()) { + if (!strm.is_writable()) { + error = Error::Write; + return false; + } else if (!content_provider(offset, 0, data_sink)) { + error = Error::Canceled; + return false; + } else if (!ok) { + error = Error::Write; + return false; + } + } + + error = Error::Success; + return true; +} + +template +inline bool write_content_chunked(Stream &strm, + const ContentProvider &content_provider, + const T &is_shutting_down, U &compressor) { + auto error = Error::Success; + return write_content_chunked(strm, content_provider, is_shutting_down, + compressor, error); +} + +template +inline bool redirect(T &cli, Request &req, Response &res, + const std::string &path, const std::string &location, + Error &error) { + Request new_req = req; + new_req.path = path; + new_req.redirect_count_ -= 1; + + if (res.status == StatusCode::SeeOther_303 && + (req.method != "GET" && req.method != "HEAD")) { + new_req.method = "GET"; + new_req.body.clear(); + new_req.headers.clear(); + } + + Response new_res; + + auto ret = cli.send(new_req, new_res, error); + if (ret) { + req = new_req; + res = new_res; + + if (res.location.empty()) { res.location = location; } + } + return ret; +} + +inline std::string params_to_query_str(const Params ¶ms) { + std::string query; + + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { query += "&"; } + query += it->first; + query += "="; + query += encode_query_param(it->second); + } + return query; +} + +inline void parse_query_text(const char *data, std::size_t size, + Params ¶ms) { + std::set cache; + split(data, data + size, '&', [&](const char *b, const char *e) { + std::string kv(b, e); + if (cache.find(kv) != cache.end()) { return; } + cache.insert(std::move(kv)); + + std::string key; + std::string val; + divide(b, static_cast(e - b), '=', + [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data, + std::size_t rhs_size) { + key.assign(lhs_data, lhs_size); + val.assign(rhs_data, rhs_size); + }); + + if (!key.empty()) { + params.emplace(decode_url(key, true), decode_url(val, true)); + } + }); +} + +inline void parse_query_text(const std::string &s, Params ¶ms) { + parse_query_text(s.data(), s.size(), params); +} + +inline bool parse_multipart_boundary(const std::string &content_type, + std::string &boundary) { + auto boundary_keyword = "boundary="; + auto pos = content_type.find(boundary_keyword); + if (pos == std::string::npos) { return false; } + auto end = content_type.find(';', pos); + auto beg = pos + strlen(boundary_keyword); + boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg)); + return !boundary.empty(); +} + +inline void parse_disposition_params(const std::string &s, Params ¶ms) { + std::set cache; + split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) { + std::string kv(b, e); + if (cache.find(kv) != cache.end()) { return; } + cache.insert(kv); + + std::string key; + std::string val; + split(b, e, '=', [&](const char *b2, const char *e2) { + if (key.empty()) { + key.assign(b2, e2); + } else { + val.assign(b2, e2); + } + }); + + if (!key.empty()) { + params.emplace(trim_double_quotes_copy((key)), + trim_double_quotes_copy((val))); + } + }); +} + +#ifdef CPPHTTPLIB_NO_EXCEPTIONS +inline bool parse_range_header(const std::string &s, Ranges &ranges) { +#else +inline bool parse_range_header(const std::string &s, Ranges &ranges) try { +#endif + auto is_valid = [](const std::string &str) { + return std::all_of(str.cbegin(), str.cend(), + [](unsigned char c) { return std::isdigit(c); }); + }; + + if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) { + const auto pos = static_cast(6); + const auto len = static_cast(s.size() - 6); + auto all_valid_ranges = true; + split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { + if (!all_valid_ranges) { return; } + + const auto it = std::find(b, e, '-'); + if (it == e) { + all_valid_ranges = false; + return; + } + + const auto lhs = std::string(b, it); + const auto rhs = std::string(it + 1, e); + if (!is_valid(lhs) || !is_valid(rhs)) { + all_valid_ranges = false; + return; + } + + const auto first = + static_cast(lhs.empty() ? -1 : std::stoll(lhs)); + const auto last = + static_cast(rhs.empty() ? -1 : std::stoll(rhs)); + if ((first == -1 && last == -1) || + (first != -1 && last != -1 && first > last)) { + all_valid_ranges = false; + return; + } + + ranges.emplace_back(first, last); + }); + return all_valid_ranges && !ranges.empty(); + } + return false; +#ifdef CPPHTTPLIB_NO_EXCEPTIONS +} +#else +} catch (...) { return false; } +#endif + +class MultipartFormDataParser { +public: + MultipartFormDataParser() = default; + + void set_boundary(std::string &&boundary) { + boundary_ = boundary; + dash_boundary_crlf_ = dash_ + boundary_ + crlf_; + crlf_dash_boundary_ = crlf_ + dash_ + boundary_; + } + + bool is_valid() const { return is_valid_; } + + bool parse(const char *buf, size_t n, const ContentReceiver &content_callback, + const MultipartContentHeader &header_callback) { + + buf_append(buf, n); + + while (buf_size() > 0) { + switch (state_) { + case 0: { // Initial boundary + buf_erase(buf_find(dash_boundary_crlf_)); + if (dash_boundary_crlf_.size() > buf_size()) { return true; } + if (!buf_start_with(dash_boundary_crlf_)) { return false; } + buf_erase(dash_boundary_crlf_.size()); + state_ = 1; + break; + } + case 1: { // New entry + clear_file_info(); + state_ = 2; + break; + } + case 2: { // Headers + auto pos = buf_find(crlf_); + if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; } + while (pos < buf_size()) { + // Empty line + if (pos == 0) { + if (!header_callback(file_)) { + is_valid_ = false; + return false; + } + buf_erase(crlf_.size()); + state_ = 3; + break; + } + + const auto header = buf_head(pos); + + if (!parse_header(header.data(), header.data() + header.size(), + [&](const std::string &, const std::string &) {})) { + is_valid_ = false; + return false; + } + + static const std::string header_content_type = "Content-Type:"; + + if (start_with_case_ignore(header, header_content_type)) { + file_.content_type = + trim_copy(header.substr(header_content_type.size())); + } else { + static const std::regex re_content_disposition( + R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~", + std::regex_constants::icase); + + std::smatch m; + if (std::regex_match(header, m, re_content_disposition)) { + Params params; + parse_disposition_params(m[1], params); + + auto it = params.find("name"); + if (it != params.end()) { + file_.name = it->second; + } else { + is_valid_ = false; + return false; + } + + it = params.find("filename"); + if (it != params.end()) { file_.filename = it->second; } + + it = params.find("filename*"); + if (it != params.end()) { + // Only allow UTF-8 enconnding... + static const std::regex re_rfc5987_encoding( + R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase); + + std::smatch m2; + if (std::regex_match(it->second, m2, re_rfc5987_encoding)) { + file_.filename = decode_url(m2[1], false); // override... + } else { + is_valid_ = false; + return false; + } + } + } + } + buf_erase(pos + crlf_.size()); + pos = buf_find(crlf_); + } + if (state_ != 3) { return true; } + break; + } + case 3: { // Body + if (crlf_dash_boundary_.size() > buf_size()) { return true; } + auto pos = buf_find(crlf_dash_boundary_); + if (pos < buf_size()) { + if (!content_callback(buf_data(), pos)) { + is_valid_ = false; + return false; + } + buf_erase(pos + crlf_dash_boundary_.size()); + state_ = 4; + } else { + auto len = buf_size() - crlf_dash_boundary_.size(); + if (len > 0) { + if (!content_callback(buf_data(), len)) { + is_valid_ = false; + return false; + } + buf_erase(len); + } + return true; + } + break; + } + case 4: { // Boundary + if (crlf_.size() > buf_size()) { return true; } + if (buf_start_with(crlf_)) { + buf_erase(crlf_.size()); + state_ = 1; + } else { + if (dash_.size() > buf_size()) { return true; } + if (buf_start_with(dash_)) { + buf_erase(dash_.size()); + is_valid_ = true; + buf_erase(buf_size()); // Remove epilogue + } else { + return true; + } + } + break; + } + } + } + + return true; + } + +private: + void clear_file_info() { + file_.name.clear(); + file_.filename.clear(); + file_.content_type.clear(); + } + + bool start_with_case_ignore(const std::string &a, + const std::string &b) const { + if (a.size() < b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (::tolower(a[i]) != ::tolower(b[i])) { return false; } + } + return true; + } + + const std::string dash_ = "--"; + const std::string crlf_ = "\r\n"; + std::string boundary_; + std::string dash_boundary_crlf_; + std::string crlf_dash_boundary_; + + size_t state_ = 0; + bool is_valid_ = false; + MultipartFormData file_; + + // Buffer + bool start_with(const std::string &a, size_t spos, size_t epos, + const std::string &b) const { + if (epos - spos < b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (a[i + spos] != b[i]) { return false; } + } + return true; + } + + size_t buf_size() const { return buf_epos_ - buf_spos_; } + + const char *buf_data() const { return &buf_[buf_spos_]; } + + std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); } + + bool buf_start_with(const std::string &s) const { + return start_with(buf_, buf_spos_, buf_epos_, s); + } + + size_t buf_find(const std::string &s) const { + auto c = s.front(); + + size_t off = buf_spos_; + while (off < buf_epos_) { + auto pos = off; + while (true) { + if (pos == buf_epos_) { return buf_size(); } + if (buf_[pos] == c) { break; } + pos++; + } + + auto remaining_size = buf_epos_ - pos; + if (s.size() > remaining_size) { return buf_size(); } + + if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; } + + off = pos + 1; + } + + return buf_size(); + } + + void buf_append(const char *data, size_t n) { + auto remaining_size = buf_size(); + if (remaining_size > 0 && buf_spos_ > 0) { + for (size_t i = 0; i < remaining_size; i++) { + buf_[i] = buf_[buf_spos_ + i]; + } + } + buf_spos_ = 0; + buf_epos_ = remaining_size; + + if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); } + + for (size_t i = 0; i < n; i++) { + buf_[buf_epos_ + i] = data[i]; + } + buf_epos_ += n; + } + + void buf_erase(size_t size) { buf_spos_ += size; } + + std::string buf_; + size_t buf_spos_ = 0; + size_t buf_epos_ = 0; +}; + +inline std::string to_lower(const char *beg, const char *end) { + std::string out; + auto it = beg; + while (it != end) { + out += static_cast(::tolower(*it)); + it++; + } + return out; +} + +inline std::string random_string(size_t length) { + static const char data[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + // std::random_device might actually be deterministic on some + // platforms, but due to lack of support in the c++ standard library, + // doing better requires either some ugly hacks or breaking portability. + static std::random_device seed_gen; + + // Request 128 bits of entropy for initialization + static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), + seed_gen()}; + + static std::mt19937 engine(seed_sequence); + + std::string result; + for (size_t i = 0; i < length; i++) { + result += data[engine() % (sizeof(data) - 1)]; + } + return result; +} + +inline std::string make_multipart_data_boundary() { + return "--cpp-httplib-multipart-data-" + detail::random_string(16); +} + +inline bool is_multipart_boundary_chars_valid(const std::string &boundary) { + auto valid = true; + for (size_t i = 0; i < boundary.size(); i++) { + auto c = boundary[i]; + if (!std::isalnum(c) && c != '-' && c != '_') { + valid = false; + break; + } + } + return valid; +} + +template +inline std::string +serialize_multipart_formdata_item_begin(const T &item, + const std::string &boundary) { + std::string body = "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\"" + item.name + "\""; + if (!item.filename.empty()) { + body += "; filename=\"" + item.filename + "\""; + } + body += "\r\n"; + if (!item.content_type.empty()) { + body += "Content-Type: " + item.content_type + "\r\n"; + } + body += "\r\n"; + + return body; +} + +inline std::string serialize_multipart_formdata_item_end() { return "\r\n"; } + +inline std::string +serialize_multipart_formdata_finish(const std::string &boundary) { + return "--" + boundary + "--\r\n"; +} + +inline std::string +serialize_multipart_formdata_get_content_type(const std::string &boundary) { + return "multipart/form-data; boundary=" + boundary; +} + +inline std::string +serialize_multipart_formdata(const MultipartFormDataItems &items, + const std::string &boundary, bool finish = true) { + std::string body; + + for (const auto &item : items) { + body += serialize_multipart_formdata_item_begin(item, boundary); + body += item.content + serialize_multipart_formdata_item_end(); + } + + if (finish) { body += serialize_multipart_formdata_finish(boundary); } + + return body; +} + +inline bool range_error(Request &req, Response &res) { + if (!req.ranges.empty() && 200 <= res.status && res.status < 300) { + ssize_t contant_len = static_cast( + res.content_length_ ? res.content_length_ : res.body.size()); + + ssize_t prev_first_pos = -1; + ssize_t prev_last_pos = -1; + size_t overwrapping_count = 0; + + // NOTE: The following Range check is based on '14.2. Range' in RFC 9110 + // 'HTTP Semantics' to avoid potential denial-of-service attacks. + // https://www.rfc-editor.org/rfc/rfc9110#section-14.2 + + // Too many ranges + if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; } + + for (auto &r : req.ranges) { + auto &first_pos = r.first; + auto &last_pos = r.second; + + if (first_pos == -1 && last_pos == -1) { + first_pos = 0; + last_pos = contant_len; + } + + if (first_pos == -1) { + first_pos = contant_len - last_pos; + last_pos = contant_len - 1; + } + + if (last_pos == -1) { last_pos = contant_len - 1; } + + // Range must be within content length + if (!(0 <= first_pos && first_pos <= last_pos && + last_pos <= contant_len - 1)) { + return true; + } + + // Ranges must be in ascending order + if (first_pos <= prev_first_pos) { return true; } + + // Request must not have more than two overlapping ranges + if (first_pos <= prev_last_pos) { + overwrapping_count++; + if (overwrapping_count > 2) { return true; } + } + + prev_first_pos = (std::max)(prev_first_pos, first_pos); + prev_last_pos = (std::max)(prev_last_pos, last_pos); + } + } + + return false; +} + +inline std::pair +get_range_offset_and_length(Range r, size_t content_length) { + assert(r.first != -1 && r.second != -1); + assert(0 <= r.first && r.first < static_cast(content_length)); + assert(r.first <= r.second && + r.second < static_cast(content_length)); + (void)(content_length); + return std::make_pair(r.first, static_cast(r.second - r.first) + 1); +} + +inline std::string make_content_range_header_field( + const std::pair &offset_and_length, size_t content_length) { + auto st = offset_and_length.first; + auto ed = st + offset_and_length.second - 1; + + std::string field = "bytes "; + field += std::to_string(st); + field += "-"; + field += std::to_string(ed); + field += "/"; + field += std::to_string(content_length); + return field; +} + +template +bool process_multipart_ranges_data(const Request &req, + const std::string &boundary, + const std::string &content_type, + size_t content_length, SToken stoken, + CToken ctoken, Content content) { + for (size_t i = 0; i < req.ranges.size(); i++) { + ctoken("--"); + stoken(boundary); + ctoken("\r\n"); + if (!content_type.empty()) { + ctoken("Content-Type: "); + stoken(content_type); + ctoken("\r\n"); + } + + auto offset_and_length = + get_range_offset_and_length(req.ranges[i], content_length); + + ctoken("Content-Range: "); + stoken(make_content_range_header_field(offset_and_length, content_length)); + ctoken("\r\n"); + ctoken("\r\n"); + + if (!content(offset_and_length.first, offset_and_length.second)) { + return false; + } + ctoken("\r\n"); + } + + ctoken("--"); + stoken(boundary); + ctoken("--"); + + return true; +} + +inline void make_multipart_ranges_data(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type, + size_t content_length, + std::string &data) { + process_multipart_ranges_data( + req, boundary, content_type, content_length, + [&](const std::string &token) { data += token; }, + [&](const std::string &token) { data += token; }, + [&](size_t offset, size_t length) { + assert(offset + length <= content_length); + data += res.body.substr(offset, length); + return true; + }); +} + +inline size_t get_multipart_ranges_data_length(const Request &req, + const std::string &boundary, + const std::string &content_type, + size_t content_length) { + size_t data_length = 0; + + process_multipart_ranges_data( + req, boundary, content_type, content_length, + [&](const std::string &token) { data_length += token.size(); }, + [&](const std::string &token) { data_length += token.size(); }, + [&](size_t /*offset*/, size_t length) { + data_length += length; + return true; + }); + + return data_length; +} + +template +inline bool +write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type, + size_t content_length, const T &is_shutting_down) { + return process_multipart_ranges_data( + req, boundary, content_type, content_length, + [&](const std::string &token) { strm.write(token); }, + [&](const std::string &token) { strm.write(token); }, + [&](size_t offset, size_t length) { + return write_content(strm, res.content_provider_, offset, length, + is_shutting_down); + }); +} + +inline bool expect_content(const Request &req) { + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || + req.method == "PRI" || req.method == "DELETE") { + return true; + } + // TODO: check if Content-Length is set + return false; +} + +inline bool has_crlf(const std::string &s) { + auto p = s.c_str(); + while (*p) { + if (*p == '\r' || *p == '\n') { return true; } + p++; + } + return false; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline std::string message_digest(const std::string &s, const EVP_MD *algo) { + auto context = std::unique_ptr( + EVP_MD_CTX_new(), EVP_MD_CTX_free); + + unsigned int hash_length = 0; + unsigned char hash[EVP_MAX_MD_SIZE]; + + EVP_DigestInit_ex(context.get(), algo, nullptr); + EVP_DigestUpdate(context.get(), s.c_str(), s.size()); + EVP_DigestFinal_ex(context.get(), hash, &hash_length); + + std::stringstream ss; + for (auto i = 0u; i < hash_length; ++i) { + ss << std::hex << std::setw(2) << std::setfill('0') + << static_cast(hash[i]); + } + + return ss.str(); +} + +inline std::string MD5(const std::string &s) { + return message_digest(s, EVP_md5()); +} + +inline std::string SHA_256(const std::string &s) { + return message_digest(s, EVP_sha256()); +} + +inline std::string SHA_512(const std::string &s) { + return message_digest(s, EVP_sha512()); +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#ifdef _WIN32 +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store +inline bool load_system_certs_on_windows(X509_STORE *store) { + auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT"); + if (!hStore) { return false; } + + auto result = false; + PCCERT_CONTEXT pContext = NULL; + while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != + nullptr) { + auto encoded_cert = + static_cast(pContext->pbCertEncoded); + + auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if (x509) { + X509_STORE_add_cert(store, x509); + X509_free(x509); + result = true; + } + } + + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + return result; +} +#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) +#if TARGET_OS_OSX +template +using CFObjectPtr = + std::unique_ptr::type, void (*)(CFTypeRef)>; + +inline void cf_object_ptr_deleter(CFTypeRef obj) { + if (obj) { CFRelease(obj); } +} + +inline bool retrieve_certs_from_keychain(CFObjectPtr &certs) { + CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef}; + CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll, + kCFBooleanTrue}; + + CFObjectPtr query( + CFDictionaryCreate(nullptr, reinterpret_cast(keys), values, + sizeof(keys) / sizeof(keys[0]), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks), + cf_object_ptr_deleter); + + if (!query) { return false; } + + CFTypeRef security_items = nullptr; + if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess || + CFArrayGetTypeID() != CFGetTypeID(security_items)) { + return false; + } + + certs.reset(reinterpret_cast(security_items)); + return true; +} + +inline bool retrieve_root_certs_from_keychain(CFObjectPtr &certs) { + CFArrayRef root_security_items = nullptr; + if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) { + return false; + } + + certs.reset(root_security_items); + return true; +} + +inline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) { + auto result = false; + for (auto i = 0; i < CFArrayGetCount(certs); ++i) { + const auto cert = reinterpret_cast( + CFArrayGetValueAtIndex(certs, i)); + + if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; } + + CFDataRef cert_data = nullptr; + if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) != + errSecSuccess) { + continue; + } + + CFObjectPtr cert_data_ptr(cert_data, cf_object_ptr_deleter); + + auto encoded_cert = static_cast( + CFDataGetBytePtr(cert_data_ptr.get())); + + auto x509 = + d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get())); + + if (x509) { + X509_STORE_add_cert(store, x509); + X509_free(x509); + result = true; + } + } + + return result; +} + +inline bool load_system_certs_on_macos(X509_STORE *store) { + auto result = false; + CFObjectPtr certs(nullptr, cf_object_ptr_deleter); + if (retrieve_certs_from_keychain(certs) && certs) { + result = add_certs_to_x509_store(certs.get(), store); + } + + if (retrieve_root_certs_from_keychain(certs) && certs) { + result = add_certs_to_x509_store(certs.get(), store) || result; + } + + return result; +} +#endif // TARGET_OS_OSX +#endif // _WIN32 +#endif // CPPHTTPLIB_OPENSSL_SUPPORT + +#ifdef _WIN32 +class WSInit { +public: + WSInit() { + WSADATA wsaData; + if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true; + } + + ~WSInit() { + if (is_valid_) WSACleanup(); + } + + bool is_valid_ = false; +}; + +static WSInit wsinit_; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline std::pair make_digest_authentication_header( + const Request &req, const std::map &auth, + size_t cnonce_count, const std::string &cnonce, const std::string &username, + const std::string &password, bool is_proxy = false) { + std::string nc; + { + std::stringstream ss; + ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count; + nc = ss.str(); + } + + std::string qop; + if (auth.find("qop") != auth.end()) { + qop = auth.at("qop"); + if (qop.find("auth-int") != std::string::npos) { + qop = "auth-int"; + } else if (qop.find("auth") != std::string::npos) { + qop = "auth"; + } else { + qop.clear(); + } + } + + std::string algo = "MD5"; + if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); } + + std::string response; + { + auto H = algo == "SHA-256" ? detail::SHA_256 + : algo == "SHA-512" ? detail::SHA_512 + : detail::MD5; + + auto A1 = username + ":" + auth.at("realm") + ":" + password; + + auto A2 = req.method + ":" + req.path; + if (qop == "auth-int") { A2 += ":" + H(req.body); } + + if (qop.empty()) { + response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2)); + } else { + response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + + ":" + qop + ":" + H(A2)); + } + } + + auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : ""; + + auto field = "Digest username=\"" + username + "\", realm=\"" + + auth.at("realm") + "\", nonce=\"" + auth.at("nonce") + + "\", uri=\"" + req.path + "\", algorithm=" + algo + + (qop.empty() ? ", response=\"" + : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" + + cnonce + "\", response=\"") + + response + "\"" + + (opaque.empty() ? "" : ", opaque=\"" + opaque + "\""); + + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); +} +#endif + +inline bool parse_www_authenticate(const Response &res, + std::map &auth, + bool is_proxy) { + auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; + if (res.has_header(auth_key)) { + static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); + auto s = res.get_header_value(auth_key); + auto pos = s.find(' '); + if (pos != std::string::npos) { + auto type = s.substr(0, pos); + if (type == "Basic") { + return false; + } else if (type == "Digest") { + s = s.substr(pos + 1); + auto beg = std::sregex_iterator(s.begin(), s.end(), re); + for (auto i = beg; i != std::sregex_iterator(); ++i) { + const auto &m = *i; + auto key = s.substr(static_cast(m.position(1)), + static_cast(m.length(1))); + auto val = m.length(2) > 0 + ? s.substr(static_cast(m.position(2)), + static_cast(m.length(2))) + : s.substr(static_cast(m.position(3)), + static_cast(m.length(3))); + auth[key] = val; + } + return true; + } + } + } + return false; +} + +class ContentProviderAdapter { +public: + explicit ContentProviderAdapter( + ContentProviderWithoutLength &&content_provider) + : content_provider_(content_provider) {} + + bool operator()(size_t offset, size_t, DataSink &sink) { + return content_provider_(offset, sink); + } + +private: + ContentProviderWithoutLength content_provider_; +}; + +} // namespace detail + +inline std::string hosted_at(const std::string &hostname) { + std::vector addrs; + hosted_at(hostname, addrs); + if (addrs.empty()) { return std::string(); } + return addrs[0]; +} + +inline void hosted_at(const std::string &hostname, + std::vector &addrs) { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (getaddrinfo(hostname.c_str(), nullptr, &hints, &result)) { +#if defined __linux__ && !defined __ANDROID__ + res_init(); +#endif + return; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + const auto &addr = + *reinterpret_cast(rp->ai_addr); + std::string ip; + auto dummy = -1; + if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip, + dummy)) { + addrs.push_back(ip); + } + } + + freeaddrinfo(result); +} + +inline std::string append_query_params(const std::string &path, + const Params ¶ms) { + std::string path_with_query = path; + const static std::regex re("[^?]+\\?.*"); + auto delm = std::regex_match(path, re) ? '&' : '?'; + path_with_query += delm + detail::params_to_query_str(params); + return path_with_query; +} + +// Header utilities +inline std::pair +make_range_header(const Ranges &ranges) { + std::string field = "bytes="; + auto i = 0; + for (const auto &r : ranges) { + if (i != 0) { field += ", "; } + if (r.first != -1) { field += std::to_string(r.first); } + field += '-'; + if (r.second != -1) { field += std::to_string(r.second); } + i++; + } + return std::make_pair("Range", std::move(field)); +} + +inline std::pair +make_basic_authentication_header(const std::string &username, + const std::string &password, bool is_proxy) { + auto field = "Basic " + detail::base64_encode(username + ":" + password); + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, std::move(field)); +} + +inline std::pair +make_bearer_token_authentication_header(const std::string &token, + bool is_proxy = false) { + auto field = "Bearer " + token; + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, std::move(field)); +} + +// Request implementation +inline bool Request::has_header(const std::string &key) const { + return detail::has_header(headers, key); +} + +inline std::string Request::get_header_value(const std::string &key, + size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Request::get_header_value_count(const std::string &key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Request::set_header(const std::string &key, + const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline bool Request::has_param(const std::string &key) const { + return params.find(key) != params.end(); +} + +inline std::string Request::get_param_value(const std::string &key, + size_t id) const { + auto rng = params.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { return it->second; } + return std::string(); +} + +inline size_t Request::get_param_value_count(const std::string &key) const { + auto r = params.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline bool Request::is_multipart_form_data() const { + const auto &content_type = get_header_value("Content-Type"); + return !content_type.rfind("multipart/form-data", 0); +} + +inline bool Request::has_file(const std::string &key) const { + return files.find(key) != files.end(); +} + +inline MultipartFormData Request::get_file_value(const std::string &key) const { + auto it = files.find(key); + if (it != files.end()) { return it->second; } + return MultipartFormData(); +} + +inline std::vector +Request::get_file_values(const std::string &key) const { + std::vector values; + auto rng = files.equal_range(key); + for (auto it = rng.first; it != rng.second; it++) { + values.push_back(it->second); + } + return values; +} + +// Response implementation +inline bool Response::has_header(const std::string &key) const { + return headers.find(key) != headers.end(); +} + +inline std::string Response::get_header_value(const std::string &key, + size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Response::get_header_value_count(const std::string &key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Response::set_header(const std::string &key, + const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline void Response::set_redirect(const std::string &url, int stat) { + if (!detail::has_crlf(url)) { + set_header("Location", url); + if (300 <= stat && stat < 400) { + this->status = stat; + } else { + this->status = StatusCode::Found_302; + } + } +} + +inline void Response::set_content(const char *s, size_t n, + const std::string &content_type) { + body.assign(s, n); + + auto rng = headers.equal_range("Content-Type"); + headers.erase(rng.first, rng.second); + set_header("Content-Type", content_type); +} + +inline void Response::set_content(const std::string &s, + const std::string &content_type) { + set_content(s.data(), s.size(), content_type); +} + +inline void Response::set_content(std::string &&s, + const std::string &content_type) { + body = std::move(s); + + auto rng = headers.equal_range("Content-Type"); + headers.erase(rng.first, rng.second); + set_header("Content-Type", content_type); +} + +inline void Response::set_content_provider( + size_t in_length, const std::string &content_type, ContentProvider provider, + ContentProviderResourceReleaser resource_releaser) { + set_header("Content-Type", content_type); + content_length_ = in_length; + if (in_length > 0) { content_provider_ = std::move(provider); } + content_provider_resource_releaser_ = std::move(resource_releaser); + is_chunked_content_provider_ = false; +} + +inline void Response::set_content_provider( + const std::string &content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser) { + set_header("Content-Type", content_type); + content_length_ = 0; + content_provider_ = detail::ContentProviderAdapter(std::move(provider)); + content_provider_resource_releaser_ = std::move(resource_releaser); + is_chunked_content_provider_ = false; +} + +inline void Response::set_chunked_content_provider( + const std::string &content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser) { + set_header("Content-Type", content_type); + content_length_ = 0; + content_provider_ = detail::ContentProviderAdapter(std::move(provider)); + content_provider_resource_releaser_ = std::move(resource_releaser); + is_chunked_content_provider_ = true; +} + +// Result implementation +inline bool Result::has_request_header(const std::string &key) const { + return request_headers_.find(key) != request_headers_.end(); +} + +inline std::string Result::get_request_header_value(const std::string &key, + size_t id) const { + return detail::get_header_value(request_headers_, key, id, ""); +} + +inline size_t +Result::get_request_header_value_count(const std::string &key) const { + auto r = request_headers_.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +// Stream implementation +inline ssize_t Stream::write(const char *ptr) { + return write(ptr, strlen(ptr)); +} + +inline ssize_t Stream::write(const std::string &s) { + return write(s.data(), s.size()); +} + +namespace detail { + +// Socket stream implementation +inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {} + +inline SocketStream::~SocketStream() = default; + +inline bool SocketStream::is_readable() const { + return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SocketStream::is_writable() const { + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && + is_socket_alive(sock_); +} + +inline ssize_t SocketStream::read(char *ptr, size_t size) { +#ifdef _WIN32 + size = + (std::min)(size, static_cast((std::numeric_limits::max)())); +#else + size = (std::min)(size, + static_cast((std::numeric_limits::max)())); +#endif + + if (read_buff_off_ < read_buff_content_size_) { + auto remaining_size = read_buff_content_size_ - read_buff_off_; + if (size <= remaining_size) { + memcpy(ptr, read_buff_.data() + read_buff_off_, size); + read_buff_off_ += size; + return static_cast(size); + } else { + memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size); + read_buff_off_ += remaining_size; + return static_cast(remaining_size); + } + } + + if (!is_readable()) { return -1; } + + read_buff_off_ = 0; + read_buff_content_size_ = 0; + + if (size < read_buff_size_) { + auto n = read_socket(sock_, read_buff_.data(), read_buff_size_, + CPPHTTPLIB_RECV_FLAGS); + if (n <= 0) { + return n; + } else if (n <= static_cast(size)) { + memcpy(ptr, read_buff_.data(), static_cast(n)); + return n; + } else { + memcpy(ptr, read_buff_.data(), size); + read_buff_off_ = size; + read_buff_content_size_ = static_cast(n); + return static_cast(size); + } + } else { + return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS); + } +} + +inline ssize_t SocketStream::write(const char *ptr, size_t size) { + if (!is_writable()) { return -1; } + +#if defined(_WIN32) && !defined(_WIN64) + size = + (std::min)(size, static_cast((std::numeric_limits::max)())); +#endif + + return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS); +} + +inline void SocketStream::get_remote_ip_and_port(std::string &ip, + int &port) const { + return detail::get_remote_ip_and_port(sock_, ip, port); +} + +inline void SocketStream::get_local_ip_and_port(std::string &ip, + int &port) const { + return detail::get_local_ip_and_port(sock_, ip, port); +} + +inline socket_t SocketStream::socket() const { return sock_; } + +// Buffer stream implementation +inline bool BufferStream::is_readable() const { return true; } + +inline bool BufferStream::is_writable() const { return true; } + +inline ssize_t BufferStream::read(char *ptr, size_t size) { +#if defined(_MSC_VER) && _MSC_VER < 1910 + auto len_read = buffer._Copy_s(ptr, size, size, position); +#else + auto len_read = buffer.copy(ptr, size, position); +#endif + position += static_cast(len_read); + return static_cast(len_read); +} + +inline ssize_t BufferStream::write(const char *ptr, size_t size) { + buffer.append(ptr, size); + return static_cast(size); +} + +inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, + int & /*port*/) const {} + +inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/, + int & /*port*/) const {} + +inline socket_t BufferStream::socket() const { return 0; } + +inline const std::string &BufferStream::get_buffer() const { return buffer; } + +inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) { + // One past the last ending position of a path param substring + std::size_t last_param_end = 0; + +#ifndef CPPHTTPLIB_NO_EXCEPTIONS + // Needed to ensure that parameter names are unique during matcher + // construction + // If exceptions are disabled, only last duplicate path + // parameter will be set + std::unordered_set param_name_set; +#endif + + while (true) { + const auto marker_pos = pattern.find(marker, last_param_end); + if (marker_pos == std::string::npos) { break; } + + static_fragments_.push_back( + pattern.substr(last_param_end, marker_pos - last_param_end)); + + const auto param_name_start = marker_pos + 1; + + auto sep_pos = pattern.find(separator, param_name_start); + if (sep_pos == std::string::npos) { sep_pos = pattern.length(); } + + auto param_name = + pattern.substr(param_name_start, sep_pos - param_name_start); + +#ifndef CPPHTTPLIB_NO_EXCEPTIONS + if (param_name_set.find(param_name) != param_name_set.cend()) { + std::string msg = "Encountered path parameter '" + param_name + + "' multiple times in route pattern '" + pattern + "'."; + throw std::invalid_argument(msg); + } +#endif + + param_names_.push_back(std::move(param_name)); + + last_param_end = sep_pos + 1; + } + + if (last_param_end < pattern.length()) { + static_fragments_.push_back(pattern.substr(last_param_end)); + } +} + +inline bool PathParamsMatcher::match(Request &request) const { + request.matches = std::smatch(); + request.path_params.clear(); + request.path_params.reserve(param_names_.size()); + + // One past the position at which the path matched the pattern last time + std::size_t starting_pos = 0; + for (size_t i = 0; i < static_fragments_.size(); ++i) { + const auto &fragment = static_fragments_[i]; + + if (starting_pos + fragment.length() > request.path.length()) { + return false; + } + + // Avoid unnecessary allocation by using strncmp instead of substr + + // comparison + if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(), + fragment.length()) != 0) { + return false; + } + + starting_pos += fragment.length(); + + // Should only happen when we have a static fragment after a param + // Example: '/users/:id/subscriptions' + // The 'subscriptions' fragment here does not have a corresponding param + if (i >= param_names_.size()) { continue; } + + auto sep_pos = request.path.find(separator, starting_pos); + if (sep_pos == std::string::npos) { sep_pos = request.path.length(); } + + const auto ¶m_name = param_names_[i]; + + request.path_params.emplace( + param_name, request.path.substr(starting_pos, sep_pos - starting_pos)); + + // Mark everythin up to '/' as matched + starting_pos = sep_pos + 1; + } + // Returns false if the path is longer than the pattern + return starting_pos >= request.path.length(); +} + +inline bool RegexMatcher::match(Request &request) const { + request.path_params.clear(); + return std::regex_match(request.path, request.matches, regex_); +} + +} // namespace detail + +// HTTP server implementation +inline Server::Server() + : new_task_queue( + [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif +} + +inline Server::~Server() = default; + +inline std::unique_ptr +Server::make_matcher(const std::string &pattern) { + if (pattern.find("/:") != std::string::npos) { + return detail::make_unique(pattern); + } else { + return detail::make_unique(pattern); + } +} + +inline Server &Server::Get(const std::string &pattern, Handler handler) { + get_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline Server &Server::Post(const std::string &pattern, Handler handler) { + post_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline Server &Server::Post(const std::string &pattern, + HandlerWithContentReader handler) { + post_handlers_for_content_reader_.emplace_back(make_matcher(pattern), + std::move(handler)); + return *this; +} + +inline Server &Server::Put(const std::string &pattern, Handler handler) { + put_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline Server &Server::Put(const std::string &pattern, + HandlerWithContentReader handler) { + put_handlers_for_content_reader_.emplace_back(make_matcher(pattern), + std::move(handler)); + return *this; +} + +inline Server &Server::Patch(const std::string &pattern, Handler handler) { + patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline Server &Server::Patch(const std::string &pattern, + HandlerWithContentReader handler) { + patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern), + std::move(handler)); + return *this; +} + +inline Server &Server::Delete(const std::string &pattern, Handler handler) { + delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline Server &Server::Delete(const std::string &pattern, + HandlerWithContentReader handler) { + delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern), + std::move(handler)); + return *this; +} + +inline Server &Server::Options(const std::string &pattern, Handler handler) { + options_handlers_.emplace_back(make_matcher(pattern), std::move(handler)); + return *this; +} + +inline bool Server::set_base_dir(const std::string &dir, + const std::string &mount_point) { + return set_mount_point(mount_point, dir); +} + +inline bool Server::set_mount_point(const std::string &mount_point, + const std::string &dir, Headers headers) { + if (detail::is_dir(dir)) { + std::string mnt = !mount_point.empty() ? mount_point : "/"; + if (!mnt.empty() && mnt[0] == '/') { + base_dirs_.push_back({mnt, dir, std::move(headers)}); + return true; + } + } + return false; +} + +inline bool Server::remove_mount_point(const std::string &mount_point) { + for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { + if (it->mount_point == mount_point) { + base_dirs_.erase(it); + return true; + } + } + return false; +} + +inline Server & +Server::set_file_extension_and_mimetype_mapping(const std::string &ext, + const std::string &mime) { + file_extension_and_mimetype_map_[ext] = mime; + return *this; +} + +inline Server &Server::set_default_file_mimetype(const std::string &mime) { + default_file_mimetype_ = mime; + return *this; +} + +inline Server &Server::set_file_request_handler(Handler handler) { + file_request_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_error_handler_core(HandlerWithResponse handler, + std::true_type) { + error_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_error_handler_core(Handler handler, + std::false_type) { + error_handler_ = [handler](const Request &req, Response &res) { + handler(req, res); + return HandlerResponse::Handled; + }; + return *this; +} + +inline Server &Server::set_exception_handler(ExceptionHandler handler) { + exception_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) { + pre_routing_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_post_routing_handler(Handler handler) { + post_routing_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_logger(Logger logger) { + logger_ = std::move(logger); + return *this; +} + +inline Server & +Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { + expect_100_continue_handler_ = std::move(handler); + return *this; +} + +inline Server &Server::set_address_family(int family) { + address_family_ = family; + return *this; +} + +inline Server &Server::set_tcp_nodelay(bool on) { + tcp_nodelay_ = on; + return *this; +} + +inline Server &Server::set_socket_options(SocketOptions socket_options) { + socket_options_ = std::move(socket_options); + return *this; +} + +inline Server &Server::set_default_headers(Headers headers) { + default_headers_ = std::move(headers); + return *this; +} + +inline Server &Server::set_header_writer( + std::function const &writer) { + header_writer_ = writer; + return *this; +} + +inline Server &Server::set_keep_alive_max_count(size_t count) { + keep_alive_max_count_ = count; + return *this; +} + +inline Server &Server::set_keep_alive_timeout(time_t sec) { + keep_alive_timeout_sec_ = sec; + return *this; +} + +inline Server &Server::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; + return *this; +} + +inline Server &Server::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; + return *this; +} + +inline Server &Server::set_idle_interval(time_t sec, time_t usec) { + idle_interval_sec_ = sec; + idle_interval_usec_ = usec; + return *this; +} + +inline Server &Server::set_payload_max_length(size_t length) { + payload_max_length_ = length; + return *this; +} + +inline bool Server::bind_to_port(const std::string &host, int port, + int socket_flags) { + return bind_internal(host, port, socket_flags) >= 0; +} +inline int Server::bind_to_any_port(const std::string &host, int socket_flags) { + return bind_internal(host, 0, socket_flags); +} + +inline bool Server::listen_after_bind() { + auto se = detail::scope_exit([&]() { done_ = true; }); + return listen_internal(); +} + +inline bool Server::listen(const std::string &host, int port, + int socket_flags) { + auto se = detail::scope_exit([&]() { done_ = true; }); + return bind_to_port(host, port, socket_flags) && listen_internal(); +} + +inline bool Server::is_running() const { return is_running_; } + +inline void Server::wait_until_ready() const { + while (!is_running() && !done_) { + std::this_thread::sleep_for(std::chrono::milliseconds{1}); + } +} + +inline void Server::stop() { + if (is_running_) { + assert(svr_sock_ != INVALID_SOCKET); + std::atomic sock(svr_sock_.exchange(INVALID_SOCKET)); + detail::shutdown_socket(sock); + detail::close_socket(sock); + } +} + +inline bool Server::parse_request_line(const char *s, Request &req) const { + auto len = strlen(s); + if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; } + len -= 2; + + { + size_t count = 0; + + detail::split(s, s + len, ' ', [&](const char *b, const char *e) { + switch (count) { + case 0: req.method = std::string(b, e); break; + case 1: req.target = std::string(b, e); break; + case 2: req.version = std::string(b, e); break; + default: break; + } + count++; + }); + + if (count != 3) { return false; } + } + + static const std::set methods{ + "GET", "HEAD", "POST", "PUT", "DELETE", + "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"}; + + if (methods.find(req.method) == methods.end()) { return false; } + + if (req.version != "HTTP/1.1" && req.version != "HTTP/1.0") { return false; } + + { + // Skip URL fragment + for (size_t i = 0; i < req.target.size(); i++) { + if (req.target[i] == '#') { + req.target.erase(i); + break; + } + } + + detail::divide(req.target, '?', + [&](const char *lhs_data, std::size_t lhs_size, + const char *rhs_data, std::size_t rhs_size) { + req.path = detail::decode_url( + std::string(lhs_data, lhs_size), false); + detail::parse_query_text(rhs_data, rhs_size, req.params); + }); + } + + return true; +} + +inline bool Server::write_response(Stream &strm, bool close_connection, + Request &req, Response &res) { + // NOTE: `req.ranges` should be empty, otherwise it will be applied + // incorrectly to the error content. + req.ranges.clear(); + return write_response_core(strm, close_connection, req, res, false); +} + +inline bool Server::write_response_with_content(Stream &strm, + bool close_connection, + const Request &req, + Response &res) { + return write_response_core(strm, close_connection, req, res, true); +} + +inline bool Server::write_response_core(Stream &strm, bool close_connection, + const Request &req, Response &res, + bool need_apply_ranges) { + assert(res.status != -1); + + if (400 <= res.status && error_handler_ && + error_handler_(req, res) == HandlerResponse::Handled) { + need_apply_ranges = true; + } + + std::string content_type; + std::string boundary; + if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); } + + // Prepare additional headers + if (close_connection || req.get_header_value("Connection") == "close") { + res.set_header("Connection", "close"); + } else { + std::stringstream ss; + ss << "timeout=" << keep_alive_timeout_sec_ + << ", max=" << keep_alive_max_count_; + res.set_header("Keep-Alive", ss.str()); + } + + if (!res.has_header("Content-Type") && + (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) { + res.set_header("Content-Type", "text/plain"); + } + + if (!res.has_header("Content-Length") && res.body.empty() && + !res.content_length_ && !res.content_provider_) { + res.set_header("Content-Length", "0"); + } + + if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { + res.set_header("Accept-Ranges", "bytes"); + } + + if (post_routing_handler_) { post_routing_handler_(req, res); } + + // Response line and headers + { + detail::BufferStream bstrm; + + if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, + status_message(res.status))) { + return false; + } + + if (!header_writer_(bstrm, res.headers)) { return false; } + + // Flush buffer + auto &data = bstrm.get_buffer(); + detail::write_data(strm, data.data(), data.size()); + } + + // Body + auto ret = true; + if (req.method != "HEAD") { + if (!res.body.empty()) { + if (!detail::write_data(strm, res.body.data(), res.body.size())) { + ret = false; + } + } else if (res.content_provider_) { + if (write_content_with_provider(strm, req, res, boundary, content_type)) { + res.content_provider_success_ = true; + } else { + ret = false; + } + } + } + + // Log + if (logger_) { logger_(req, res); } + + return ret; +} + +inline bool +Server::write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type) { + auto is_shutting_down = [this]() { + return this->svr_sock_ == INVALID_SOCKET; + }; + + if (res.content_length_ > 0) { + if (req.ranges.empty()) { + return detail::write_content(strm, res.content_provider_, 0, + res.content_length_, is_shutting_down); + } else if (req.ranges.size() == 1) { + auto offset_and_length = detail::get_range_offset_and_length( + req.ranges[0], res.content_length_); + + return detail::write_content(strm, res.content_provider_, + offset_and_length.first, + offset_and_length.second, is_shutting_down); + } else { + return detail::write_multipart_ranges_data( + strm, req, res, boundary, content_type, res.content_length_, + is_shutting_down); + } + } else { + if (res.is_chunked_content_provider_) { + auto type = detail::encoding_type(req, res); + + std::unique_ptr compressor; + if (type == detail::EncodingType::Gzip) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + compressor = detail::make_unique(); +#endif + } else if (type == detail::EncodingType::Brotli) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + compressor = detail::make_unique(); +#endif + } else { + compressor = detail::make_unique(); + } + assert(compressor != nullptr); + + return detail::write_content_chunked(strm, res.content_provider_, + is_shutting_down, *compressor); + } else { + return detail::write_content_without_length(strm, res.content_provider_, + is_shutting_down); + } + } +} + +inline bool Server::read_content(Stream &strm, Request &req, Response &res) { + MultipartFormDataMap::iterator cur; + auto file_count = 0; + if (read_content_core( + strm, req, res, + // Regular + [&](const char *buf, size_t n) { + if (req.body.size() + n > req.body.max_size()) { return false; } + req.body.append(buf, n); + return true; + }, + // Multipart + [&](const MultipartFormData &file) { + if (file_count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) { + return false; + } + cur = req.files.emplace(file.name, file); + return true; + }, + [&](const char *buf, size_t n) { + auto &content = cur->second.content; + if (content.size() + n > content.max_size()) { return false; } + content.append(buf, n); + return true; + })) { + const auto &content_type = req.get_header_value("Content-Type"); + if (!content_type.find("application/x-www-form-urlencoded")) { + if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) { + res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414? + return false; + } + detail::parse_query_text(req.body, req.params); + } + return true; + } + return false; +} + +inline bool Server::read_content_with_content_receiver( + Stream &strm, Request &req, Response &res, ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) { + return read_content_core(strm, req, res, std::move(receiver), + std::move(multipart_header), + std::move(multipart_receiver)); +} + +inline bool +Server::read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) const { + detail::MultipartFormDataParser multipart_form_data_parser; + ContentReceiverWithProgress out; + + if (req.is_multipart_form_data()) { + const auto &content_type = req.get_header_value("Content-Type"); + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary)) { + res.status = StatusCode::BadRequest_400; + return false; + } + + multipart_form_data_parser.set_boundary(std::move(boundary)); + out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) { + /* For debug + size_t pos = 0; + while (pos < n) { + auto read_size = (std::min)(1, n - pos); + auto ret = multipart_form_data_parser.parse( + buf + pos, read_size, multipart_receiver, multipart_header); + if (!ret) { return false; } + pos += read_size; + } + return true; + */ + return multipart_form_data_parser.parse(buf, n, multipart_receiver, + multipart_header); + }; + } else { + out = [receiver](const char *buf, size_t n, uint64_t /*off*/, + uint64_t /*len*/) { return receiver(buf, n); }; + } + + if (req.method == "DELETE" && !req.has_header("Content-Length")) { + return true; + } + + if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr, + out, true)) { + return false; + } + + if (req.is_multipart_form_data()) { + if (!multipart_form_data_parser.is_valid()) { + res.status = StatusCode::BadRequest_400; + return false; + } + } + + return true; +} + +inline bool Server::handle_file_request(const Request &req, Response &res, + bool head) { + for (const auto &entry : base_dirs_) { + // Prefix match + if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) { + std::string sub_path = "/" + req.path.substr(entry.mount_point.size()); + if (detail::is_valid_path(sub_path)) { + auto path = entry.base_dir + sub_path; + if (path.back() == '/') { path += "index.html"; } + + if (detail::is_file(path)) { + for (const auto &kv : entry.headers) { + res.set_header(kv.first, kv.second); + } + + auto mm = std::make_shared(path.c_str()); + if (!mm->is_open()) { return false; } + + res.set_content_provider( + mm->size(), + detail::find_content_type(path, file_extension_and_mimetype_map_, + default_file_mimetype_), + [mm](size_t offset, size_t length, DataSink &sink) -> bool { + sink.write(mm->data() + offset, length); + return true; + }); + + if (!head && file_request_handler_) { + file_request_handler_(req, res); + } + + return true; + } + } + } + } + return false; +} + +inline socket_t +Server::create_server_socket(const std::string &host, int port, + int socket_flags, + SocketOptions socket_options) const { + return detail::create_socket( + host, std::string(), port, address_family_, socket_flags, tcp_nodelay_, + std::move(socket_options), + [](socket_t sock, struct addrinfo &ai) -> bool { + if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + return false; + } + if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; } + return true; + }); +} + +inline int Server::bind_internal(const std::string &host, int port, + int socket_flags) { + if (!is_valid()) { return -1; } + + svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_); + if (svr_sock_ == INVALID_SOCKET) { return -1; } + + if (port == 0) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (getsockname(svr_sock_, reinterpret_cast(&addr), + &addr_len) == -1) { + return -1; + } + if (addr.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&addr)->sin6_port); + } else { + return -1; + } + } else { + return port; + } +} + +inline bool Server::listen_internal() { + auto ret = true; + is_running_ = true; + auto se = detail::scope_exit([&]() { is_running_ = false; }); + + { + std::unique_ptr task_queue(new_task_queue()); + + while (svr_sock_ != INVALID_SOCKET) { +#ifndef _WIN32 + if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) { +#endif + auto val = detail::select_read(svr_sock_, idle_interval_sec_, + idle_interval_usec_); + if (val == 0) { // Timeout + task_queue->on_idle(); + continue; + } +#ifndef _WIN32 + } +#endif + socket_t sock = accept(svr_sock_, nullptr, nullptr); + + if (sock == INVALID_SOCKET) { + if (errno == EMFILE) { + // The per-process limit of open file descriptors has been reached. + // Try to accept new connections after a short sleep. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } else if (errno == EINTR || errno == EAGAIN) { + continue; + } + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; + } + + { +#ifdef _WIN32 + auto timeout = static_cast(read_timeout_sec_ * 1000 + + read_timeout_usec_ / 1000); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)); +#else + timeval tv; + tv.tv_sec = static_cast(read_timeout_sec_); + tv.tv_usec = static_cast(read_timeout_usec_); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&tv), sizeof(tv)); +#endif + } + { + +#ifdef _WIN32 + auto timeout = static_cast(write_timeout_sec_ * 1000 + + write_timeout_usec_ / 1000); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)); +#else + timeval tv; + tv.tv_sec = static_cast(write_timeout_sec_); + tv.tv_usec = static_cast(write_timeout_usec_); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, + reinterpret_cast(&tv), sizeof(tv)); +#endif + } + + if (!task_queue->enqueue( + [this, sock]() { process_and_close_socket(sock); })) { + detail::shutdown_socket(sock); + detail::close_socket(sock); + } + } + + task_queue->shutdown(); + } + + return ret; +} + +inline bool Server::routing(Request &req, Response &res, Stream &strm) { + if (pre_routing_handler_ && + pre_routing_handler_(req, res) == HandlerResponse::Handled) { + return true; + } + + // File handler + auto is_head_request = req.method == "HEAD"; + if ((req.method == "GET" || is_head_request) && + handle_file_request(req, res, is_head_request)) { + return true; + } + + if (detail::expect_content(req)) { + // Content reader handler + { + ContentReader reader( + [&](ContentReceiver receiver) { + return read_content_with_content_receiver( + strm, req, res, std::move(receiver), nullptr, nullptr); + }, + [&](MultipartContentHeader header, ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, nullptr, + std::move(header), + std::move(receiver)); + }); + + if (req.method == "POST") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + post_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PUT") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + put_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PATCH") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + patch_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "DELETE") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + delete_handlers_for_content_reader_)) { + return true; + } + } + } + + // Read content into `req.body` + if (!read_content(strm, req, res)) { return false; } + } + + // Regular handler + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); + } else if (req.method == "PATCH") { + return dispatch_request(req, res, patch_handlers_); + } + + res.status = StatusCode::BadRequest_400; + return false; +} + +inline bool Server::dispatch_request(Request &req, Response &res, + const Handlers &handlers) const { + for (const auto &x : handlers) { + const auto &matcher = x.first; + const auto &handler = x.second; + + if (matcher->match(req)) { + handler(req, res); + return true; + } + } + return false; +} + +inline void Server::apply_ranges(const Request &req, Response &res, + std::string &content_type, + std::string &boundary) const { + if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) { + auto it = res.headers.find("Content-Type"); + if (it != res.headers.end()) { + content_type = it->second; + res.headers.erase(it); + } + + boundary = detail::make_multipart_data_boundary(); + + res.set_header("Content-Type", + "multipart/byteranges; boundary=" + boundary); + } + + auto type = detail::encoding_type(req, res); + + if (res.body.empty()) { + if (res.content_length_ > 0) { + size_t length = 0; + if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) { + length = res.content_length_; + } else if (req.ranges.size() == 1) { + auto offset_and_length = detail::get_range_offset_and_length( + req.ranges[0], res.content_length_); + + length = offset_and_length.second; + + auto content_range = detail::make_content_range_header_field( + offset_and_length, res.content_length_); + res.set_header("Content-Range", content_range); + } else { + length = detail::get_multipart_ranges_data_length( + req, boundary, content_type, res.content_length_); + } + res.set_header("Content-Length", std::to_string(length)); + } else { + if (res.content_provider_) { + if (res.is_chunked_content_provider_) { + res.set_header("Transfer-Encoding", "chunked"); + if (type == detail::EncodingType::Gzip) { + res.set_header("Content-Encoding", "gzip"); + } else if (type == detail::EncodingType::Brotli) { + res.set_header("Content-Encoding", "br"); + } + } + } + } + } else { + if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) { + ; + } else if (req.ranges.size() == 1) { + auto offset_and_length = + detail::get_range_offset_and_length(req.ranges[0], res.body.size()); + auto offset = offset_and_length.first; + auto length = offset_and_length.second; + + auto content_range = detail::make_content_range_header_field( + offset_and_length, res.body.size()); + res.set_header("Content-Range", content_range); + + assert(offset + length <= res.body.size()); + res.body = res.body.substr(offset, length); + } else { + std::string data; + detail::make_multipart_ranges_data(req, res, boundary, content_type, + res.body.size(), data); + res.body.swap(data); + } + + if (type != detail::EncodingType::None) { + std::unique_ptr compressor; + std::string content_encoding; + + if (type == detail::EncodingType::Gzip) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + compressor = detail::make_unique(); + content_encoding = "gzip"; +#endif + } else if (type == detail::EncodingType::Brotli) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + compressor = detail::make_unique(); + content_encoding = "br"; +#endif + } + + if (compressor) { + std::string compressed; + if (compressor->compress(res.body.data(), res.body.size(), true, + [&](const char *data, size_t data_len) { + compressed.append(data, data_len); + return true; + })) { + res.body.swap(compressed); + res.set_header("Content-Encoding", content_encoding); + } + } + } + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length); + } +} + +inline bool Server::dispatch_request_for_content_reader( + Request &req, Response &res, ContentReader content_reader, + const HandlersForContentReader &handlers) const { + for (const auto &x : handlers) { + const auto &matcher = x.first; + const auto &handler = x.second; + + if (matcher->match(req)) { + handler(req, res, content_reader); + return true; + } + } + return false; +} + +inline bool +Server::process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request) { + std::array buf{}; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + // Connection has been closed on client + if (!line_reader.getline()) { return false; } + + Request req; + + Response res; + res.version = "HTTP/1.1"; + res.headers = default_headers_; + +#ifdef _WIN32 + // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL). +#else +#ifndef CPPHTTPLIB_USE_POLL + // Socket file descriptor exceeded FD_SETSIZE... + if (strm.socket() >= FD_SETSIZE) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = StatusCode::InternalServerError_500; + return write_response(strm, close_connection, req, res); + } +#endif +#endif + + // Check if the request URI doesn't exceed the limit + if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = StatusCode::UriTooLong_414; + return write_response(strm, close_connection, req, res); + } + + // Request line and headers + if (!parse_request_line(line_reader.ptr(), req) || + !detail::read_headers(strm, req.headers)) { + res.status = StatusCode::BadRequest_400; + return write_response(strm, close_connection, req, res); + } + + if (req.get_header_value("Connection") == "close") { + connection_closed = true; + } + + if (req.version == "HTTP/1.0" && + req.get_header_value("Connection") != "Keep-Alive") { + connection_closed = true; + } + + strm.get_remote_ip_and_port(req.remote_addr, req.remote_port); + req.set_header("REMOTE_ADDR", req.remote_addr); + req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); + + strm.get_local_ip_and_port(req.local_addr, req.local_port); + req.set_header("LOCAL_ADDR", req.local_addr); + req.set_header("LOCAL_PORT", std::to_string(req.local_port)); + + if (req.has_header("Range")) { + const auto &range_header_value = req.get_header_value("Range"); + if (!detail::parse_range_header(range_header_value, req.ranges)) { + res.status = StatusCode::RangeNotSatisfiable_416; + return write_response(strm, close_connection, req, res); + } + } + + if (setup_request) { setup_request(req); } + + if (req.get_header_value("Expect") == "100-continue") { + int status = StatusCode::Continue_100; + if (expect_100_continue_handler_) { + status = expect_100_continue_handler_(req, res); + } + switch (status) { + case StatusCode::Continue_100: + case StatusCode::ExpectationFailed_417: + strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, + status_message(status)); + break; + default: return write_response(strm, close_connection, req, res); + } + } + + // Routing + auto routed = false; +#ifdef CPPHTTPLIB_NO_EXCEPTIONS + routed = routing(req, res, strm); +#else + try { + routed = routing(req, res, strm); + } catch (std::exception &e) { + if (exception_handler_) { + auto ep = std::current_exception(); + exception_handler_(req, res, ep); + routed = true; + } else { + res.status = StatusCode::InternalServerError_500; + std::string val; + auto s = e.what(); + for (size_t i = 0; s[i]; i++) { + switch (s[i]) { + case '\r': val += "\\r"; break; + case '\n': val += "\\n"; break; + default: val += s[i]; break; + } + } + res.set_header("EXCEPTION_WHAT", val); + } + } catch (...) { + if (exception_handler_) { + auto ep = std::current_exception(); + exception_handler_(req, res, ep); + routed = true; + } else { + res.status = StatusCode::InternalServerError_500; + res.set_header("EXCEPTION_WHAT", "UNKNOWN"); + } + } +#endif + if (routed) { + if (res.status == -1) { + res.status = req.ranges.empty() ? StatusCode::OK_200 + : StatusCode::PartialContent_206; + } + + if (detail::range_error(req, res)) { + res.body.clear(); + res.content_length_ = 0; + res.content_provider_ = nullptr; + res.status = StatusCode::RangeNotSatisfiable_416; + return write_response(strm, close_connection, req, res); + } + + return write_response_with_content(strm, close_connection, req, res); + } else { + if (res.status == -1) { res.status = StatusCode::NotFound_404; } + + return write_response(strm, close_connection, req, res); + } +} + +inline bool Server::is_valid() const { return true; } + +inline bool Server::process_and_close_socket(socket_t sock) { + auto ret = detail::process_server_socket( + svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, + [this](Stream &strm, bool close_connection, bool &connection_closed) { + return process_request(strm, close_connection, connection_closed, + nullptr); + }); + + detail::shutdown_socket(sock); + detail::close_socket(sock); + return ret; +} + +// HTTP client implementation +inline ClientImpl::ClientImpl(const std::string &host) + : ClientImpl(host, 80, std::string(), std::string()) {} + +inline ClientImpl::ClientImpl(const std::string &host, int port) + : ClientImpl(host, port, std::string(), std::string()) {} + +inline ClientImpl::ClientImpl(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : host_(host), port_(port), + host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)), + client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} + +inline ClientImpl::~ClientImpl() { + std::lock_guard guard(socket_mutex_); + shutdown_socket(socket_); + close_socket(socket_); +} + +inline bool ClientImpl::is_valid() const { return true; } + +inline void ClientImpl::copy_settings(const ClientImpl &rhs) { + client_cert_path_ = rhs.client_cert_path_; + client_key_path_ = rhs.client_key_path_; + connection_timeout_sec_ = rhs.connection_timeout_sec_; + read_timeout_sec_ = rhs.read_timeout_sec_; + read_timeout_usec_ = rhs.read_timeout_usec_; + write_timeout_sec_ = rhs.write_timeout_sec_; + write_timeout_usec_ = rhs.write_timeout_usec_; + basic_auth_username_ = rhs.basic_auth_username_; + basic_auth_password_ = rhs.basic_auth_password_; + bearer_token_auth_token_ = rhs.bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + digest_auth_username_ = rhs.digest_auth_username_; + digest_auth_password_ = rhs.digest_auth_password_; +#endif + keep_alive_ = rhs.keep_alive_; + follow_location_ = rhs.follow_location_; + url_encode_ = rhs.url_encode_; + address_family_ = rhs.address_family_; + tcp_nodelay_ = rhs.tcp_nodelay_; + socket_options_ = rhs.socket_options_; + compress_ = rhs.compress_; + decompress_ = rhs.decompress_; + interface_ = rhs.interface_; + proxy_host_ = rhs.proxy_host_; + proxy_port_ = rhs.proxy_port_; + proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; + proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; + proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; + proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; +#endif +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + ca_cert_file_path_ = rhs.ca_cert_file_path_; + ca_cert_dir_path_ = rhs.ca_cert_dir_path_; + ca_cert_store_ = rhs.ca_cert_store_; +#endif +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + server_certificate_verification_ = rhs.server_certificate_verification_; +#endif + logger_ = rhs.logger_; +} + +inline socket_t ClientImpl::create_client_socket(Error &error) const { + if (!proxy_host_.empty() && proxy_port_ != -1) { + return detail::create_client_socket( + proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_, + socket_options_, connection_timeout_sec_, connection_timeout_usec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, interface_, error); + } + + // Check is custom IP specified for host_ + std::string ip; + auto it = addr_map_.find(host_); + if (it != addr_map_.end()) { ip = it->second; } + + return detail::create_client_socket( + host_, ip, port_, address_family_, tcp_nodelay_, socket_options_, + connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_, + read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_, + error); +} + +inline bool ClientImpl::create_and_connect_socket(Socket &socket, + Error &error) { + auto sock = create_client_socket(error); + if (sock == INVALID_SOCKET) { return false; } + socket.sock = sock; + return true; +} + +inline void ClientImpl::shutdown_ssl(Socket & /*socket*/, + bool /*shutdown_gracefully*/) { + // If there are any requests in flight from threads other than us, then it's + // a thread-unsafe race because individual ssl* objects are not thread-safe. + assert(socket_requests_in_flight_ == 0 || + socket_requests_are_from_thread_ == std::this_thread::get_id()); +} + +inline void ClientImpl::shutdown_socket(Socket &socket) const { + if (socket.sock == INVALID_SOCKET) { return; } + detail::shutdown_socket(socket.sock); +} + +inline void ClientImpl::close_socket(Socket &socket) { + // If there are requests in flight in another thread, usually closing + // the socket will be fine and they will simply receive an error when + // using the closed socket, but it is still a bug since rarely the OS + // may reassign the socket id to be used for a new socket, and then + // suddenly they will be operating on a live socket that is different + // than the one they intended! + assert(socket_requests_in_flight_ == 0 || + socket_requests_are_from_thread_ == std::this_thread::get_id()); + + // It is also a bug if this happens while SSL is still active +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + assert(socket.ssl == nullptr); +#endif + if (socket.sock == INVALID_SOCKET) { return; } + detail::close_socket(socket.sock); + socket.sock = INVALID_SOCKET; +} + +inline bool ClientImpl::read_response_line(Stream &strm, const Request &req, + Response &res) const { + std::array buf{}; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + if (!line_reader.getline()) { return false; } + +#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR + const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n"); +#else + const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n"); +#endif + + std::cmatch m; + if (!std::regex_match(line_reader.ptr(), m, re)) { + return req.method == "CONNECT"; + } + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + res.reason = std::string(m[3]); + + // Ignore '100 Continue' + while (res.status == StatusCode::Continue_100) { + if (!line_reader.getline()) { return false; } // CRLF + if (!line_reader.getline()) { return false; } // next response line + + if (!std::regex_match(line_reader.ptr(), m, re)) { return false; } + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + res.reason = std::string(m[3]); + } + + return true; +} + +inline bool ClientImpl::send(Request &req, Response &res, Error &error) { + std::lock_guard request_mutex_guard(request_mutex_); + auto ret = send_(req, res, error); + if (error == Error::SSLPeerCouldBeClosed_) { + assert(!ret); + ret = send_(req, res, error); + } + return ret; +} + +inline bool ClientImpl::send_(Request &req, Response &res, Error &error) { + { + std::lock_guard guard(socket_mutex_); + + // Set this to false immediately - if it ever gets set to true by the end of + // the request, we know another thread instructed us to close the socket. + socket_should_be_closed_when_request_is_done_ = false; + + auto is_alive = false; + if (socket_.is_open()) { + is_alive = detail::is_socket_alive(socket_.sock); + if (!is_alive) { + // Attempt to avoid sigpipe by shutting down nongracefully if it seems + // like the other side has already closed the connection Also, there + // cannot be any requests in flight from other threads since we locked + // request_mutex_, so safe to close everything immediately + const bool shutdown_gracefully = false; + shutdown_ssl(socket_, shutdown_gracefully); + shutdown_socket(socket_); + close_socket(socket_); + } + } + + if (!is_alive) { + if (!create_and_connect_socket(socket_, error)) { return false; } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + // TODO: refactoring + if (is_ssl()) { + auto &scli = static_cast(*this); + if (!proxy_host_.empty() && proxy_port_ != -1) { + auto success = false; + if (!scli.connect_with_proxy(socket_, res, success, error)) { + return success; + } + } + + if (!scli.initialize_ssl(socket_, error)) { return false; } + } +#endif + } + + // Mark the current socket as being in use so that it cannot be closed by + // anyone else while this request is ongoing, even though we will be + // releasing the mutex. + if (socket_requests_in_flight_ > 1) { + assert(socket_requests_are_from_thread_ == std::this_thread::get_id()); + } + socket_requests_in_flight_ += 1; + socket_requests_are_from_thread_ = std::this_thread::get_id(); + } + + for (const auto &header : default_headers_) { + if (req.headers.find(header.first) == req.headers.end()) { + req.headers.insert(header); + } + } + + auto ret = false; + auto close_connection = !keep_alive_; + + auto se = detail::scope_exit([&]() { + // Briefly lock mutex in order to mark that a request is no longer ongoing + std::lock_guard guard(socket_mutex_); + socket_requests_in_flight_ -= 1; + if (socket_requests_in_flight_ <= 0) { + assert(socket_requests_in_flight_ == 0); + socket_requests_are_from_thread_ = std::thread::id(); + } + + if (socket_should_be_closed_when_request_is_done_ || close_connection || + !ret) { + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); + } + }); + + ret = process_socket(socket_, [&](Stream &strm) { + return handle_request(strm, req, res, close_connection, error); + }); + + if (!ret) { + if (error == Error::Success) { error = Error::Unknown; } + } + + return ret; +} + +inline Result ClientImpl::send(const Request &req) { + auto req2 = req; + return send_(std::move(req2)); +} + +inline Result ClientImpl::send_(Request &&req) { + auto res = detail::make_unique(); + auto error = Error::Success; + auto ret = send(req, *res, error); + return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)}; +} + +inline bool ClientImpl::handle_request(Stream &strm, Request &req, + Response &res, bool close_connection, + Error &error) { + if (req.path.empty()) { + error = Error::Connection; + return false; + } + + auto req_save = req; + + bool ret; + + if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) { + auto req2 = req; + req2.path = "http://" + host_and_port_ + req.path; + ret = process_request(strm, req2, res, close_connection, error); + req = req2; + req.path = req_save.path; + } else { + ret = process_request(strm, req, res, close_connection, error); + } + + if (!ret) { return false; } + + if (res.get_header_value("Connection") == "close" || + (res.version == "HTTP/1.0" && res.reason != "Connection established")) { + // TODO this requires a not-entirely-obvious chain of calls to be correct + // for this to be safe. + + // This is safe to call because handle_request is only called by send_ + // which locks the request mutex during the process. It would be a bug + // to call it from a different thread since it's a thread-safety issue + // to do these things to the socket if another thread is using the socket. + std::lock_guard guard(socket_mutex_); + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); + } + + if (300 < res.status && res.status < 400 && follow_location_) { + req = req_save; + ret = redirect(req, res, error); + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if ((res.status == StatusCode::Unauthorized_401 || + res.status == StatusCode::ProxyAuthenticationRequired_407) && + req.authorization_count_ < 5) { + auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407; + const auto &username = + is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; + const auto &password = + is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; + + if (!username.empty() && !password.empty()) { + std::map auth; + if (detail::parse_www_authenticate(res, auth, is_proxy)) { + Request new_req = req; + new_req.authorization_count_ += 1; + new_req.headers.erase(is_proxy ? "Proxy-Authorization" + : "Authorization"); + new_req.headers.insert(detail::make_digest_authentication_header( + req, auth, new_req.authorization_count_, detail::random_string(10), + username, password, is_proxy)); + + Response new_res; + + ret = send(new_req, new_res, error); + if (ret) { res = new_res; } + } + } + } +#endif + + return ret; +} + +inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) { + if (req.redirect_count_ == 0) { + error = Error::ExceedRedirectCount; + return false; + } + + auto location = res.get_header_value("location"); + if (location.empty()) { return false; } + + const static std::regex re( + R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)"); + + std::smatch m; + if (!std::regex_match(location, m, re)) { return false; } + + auto scheme = is_ssl() ? "https" : "http"; + + auto next_scheme = m[1].str(); + auto next_host = m[2].str(); + if (next_host.empty()) { next_host = m[3].str(); } + auto port_str = m[4].str(); + auto next_path = m[5].str(); + auto next_query = m[6].str(); + + auto next_port = port_; + if (!port_str.empty()) { + next_port = std::stoi(port_str); + } else if (!next_scheme.empty()) { + next_port = next_scheme == "https" ? 443 : 80; + } + + if (next_scheme.empty()) { next_scheme = scheme; } + if (next_host.empty()) { next_host = host_; } + if (next_path.empty()) { next_path = "/"; } + + auto path = detail::decode_url(next_path, true) + next_query; + + if (next_scheme == scheme && next_host == host_ && next_port == port_) { + return detail::redirect(*this, req, res, path, location, error); + } else { + if (next_scheme == "https") { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(next_host, next_port); + cli.copy_settings(*this); + if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); } + return detail::redirect(cli, req, res, path, location, error); +#else + return false; +#endif + } else { + ClientImpl cli(next_host, next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, path, location, error); + } + } +} + +inline bool ClientImpl::write_content_with_provider(Stream &strm, + const Request &req, + Error &error) const { + auto is_shutting_down = []() { return false; }; + + if (req.is_chunked_content_provider_) { + // TODO: Brotli support + std::unique_ptr compressor; +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { + compressor = detail::make_unique(); + } else +#endif + { + compressor = detail::make_unique(); + } + + return detail::write_content_chunked(strm, req.content_provider_, + is_shutting_down, *compressor, error); + } else { + return detail::write_content(strm, req.content_provider_, 0, + req.content_length_, is_shutting_down, error); + } +} + +inline bool ClientImpl::write_request(Stream &strm, Request &req, + bool close_connection, Error &error) { + // Prepare additional headers + if (close_connection) { + if (!req.has_header("Connection")) { + req.set_header("Connection", "close"); + } + } + + if (!req.has_header("Host")) { + if (is_ssl()) { + if (port_ == 443) { + req.set_header("Host", host_); + } else { + req.set_header("Host", host_and_port_); + } + } else { + if (port_ == 80) { + req.set_header("Host", host_); + } else { + req.set_header("Host", host_and_port_); + } + } + } + + if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); } + +#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT + if (!req.has_header("User-Agent")) { + auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION; + req.set_header("User-Agent", agent); + } +#endif + + if (req.body.empty()) { + if (req.content_provider_) { + if (!req.is_chunked_content_provider_) { + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.content_length_); + req.set_header("Content-Length", length); + } + } + } else { + if (req.method == "POST" || req.method == "PUT" || + req.method == "PATCH") { + req.set_header("Content-Length", "0"); + } + } + } else { + if (!req.has_header("Content-Type")) { + req.set_header("Content-Type", "text/plain"); + } + + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.body.size()); + req.set_header("Content-Length", length); + } + } + + if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) { + if (!req.has_header("Authorization")) { + req.headers.insert(make_basic_authentication_header( + basic_auth_username_, basic_auth_password_, false)); + } + } + + if (!proxy_basic_auth_username_.empty() && + !proxy_basic_auth_password_.empty()) { + if (!req.has_header("Proxy-Authorization")) { + req.headers.insert(make_basic_authentication_header( + proxy_basic_auth_username_, proxy_basic_auth_password_, true)); + } + } + + if (!bearer_token_auth_token_.empty()) { + if (!req.has_header("Authorization")) { + req.headers.insert(make_bearer_token_authentication_header( + bearer_token_auth_token_, false)); + } + } + + if (!proxy_bearer_token_auth_token_.empty()) { + if (!req.has_header("Proxy-Authorization")) { + req.headers.insert(make_bearer_token_authentication_header( + proxy_bearer_token_auth_token_, true)); + } + } + + // Request line and headers + { + detail::BufferStream bstrm; + + const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path; + bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + + header_writer_(bstrm, req.headers); + + // Flush buffer + auto &data = bstrm.get_buffer(); + if (!detail::write_data(strm, data.data(), data.size())) { + error = Error::Write; + return false; + } + } + + // Body + if (req.body.empty()) { + return write_content_with_provider(strm, req, error); + } + + if (!detail::write_data(strm, req.body.data(), req.body.size())) { + error = Error::Write; + return false; + } + + return true; +} + +inline std::unique_ptr ClientImpl::send_with_content_provider( + Request &req, const char *body, size_t content_length, + ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const std::string &content_type, Error &error) { + if (!content_type.empty()) { req.set_header("Content-Type", content_type); } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { req.set_header("Content-Encoding", "gzip"); } +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_ && !content_provider_without_length) { + // TODO: Brotli support + detail::gzip_compressor compressor; + + if (content_provider) { + auto ok = true; + size_t offset = 0; + DataSink data_sink; + + data_sink.write = [&](const char *data, size_t data_len) -> bool { + if (ok) { + auto last = offset + data_len == content_length; + + auto ret = compressor.compress( + data, data_len, last, + [&](const char *compressed_data, size_t compressed_data_len) { + req.body.append(compressed_data, compressed_data_len); + return true; + }); + + if (ret) { + offset += data_len; + } else { + ok = false; + } + } + return ok; + }; + + while (ok && offset < content_length) { + if (!content_provider(offset, content_length - offset, data_sink)) { + error = Error::Canceled; + return nullptr; + } + } + } else { + if (!compressor.compress(body, content_length, true, + [&](const char *data, size_t data_len) { + req.body.append(data, data_len); + return true; + })) { + error = Error::Compression; + return nullptr; + } + } + } else +#endif + { + if (content_provider) { + req.content_length_ = content_length; + req.content_provider_ = std::move(content_provider); + req.is_chunked_content_provider_ = false; + } else if (content_provider_without_length) { + req.content_length_ = 0; + req.content_provider_ = detail::ContentProviderAdapter( + std::move(content_provider_without_length)); + req.is_chunked_content_provider_ = true; + req.set_header("Transfer-Encoding", "chunked"); + } else { + req.body.assign(body, content_length); + } + } + + auto res = detail::make_unique(); + return send(req, *res, error) ? std::move(res) : nullptr; +} + +inline Result ClientImpl::send_with_content_provider( + const std::string &method, const std::string &path, const Headers &headers, + const char *body, size_t content_length, ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const std::string &content_type, Progress progress) { + Request req; + req.method = method; + req.headers = headers; + req.path = path; + req.progress = progress; + + auto error = Error::Success; + + auto res = send_with_content_provider( + req, body, content_length, std::move(content_provider), + std::move(content_provider_without_length), content_type, error); + + return Result{std::move(res), error, std::move(req.headers)}; +} + +inline std::string +ClientImpl::adjust_host_string(const std::string &host) const { + if (host.find(':') != std::string::npos) { return "[" + host + "]"; } + return host; +} + +inline bool ClientImpl::process_request(Stream &strm, Request &req, + Response &res, bool close_connection, + Error &error) { + // Send request + if (!write_request(strm, req, close_connection, error)) { return false; } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if (is_ssl()) { + auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1; + if (!is_proxy_enabled) { + char buf[1]; + if (SSL_peek(socket_.ssl, buf, 1) == 0 && + SSL_get_error(socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN) { + error = Error::SSLPeerCouldBeClosed_; + return false; + } + } + } +#endif + + // Receive response and headers + if (!read_response_line(strm, req, res) || + !detail::read_headers(strm, res.headers)) { + error = Error::Read; + return false; + } + + // Body + if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" && + req.method != "CONNECT") { + auto redirect = 300 < res.status && res.status < 400 && follow_location_; + + if (req.response_handler && !redirect) { + if (!req.response_handler(res)) { + error = Error::Canceled; + return false; + } + } + + auto out = + req.content_receiver + ? static_cast( + [&](const char *buf, size_t n, uint64_t off, uint64_t len) { + if (redirect) { return true; } + auto ret = req.content_receiver(buf, n, off, len); + if (!ret) { error = Error::Canceled; } + return ret; + }) + : static_cast( + [&](const char *buf, size_t n, uint64_t /*off*/, + uint64_t /*len*/) { + if (res.body.size() + n > res.body.max_size()) { + return false; + } + res.body.append(buf, n); + return true; + }); + + auto progress = [&](uint64_t current, uint64_t total) { + if (!req.progress || redirect) { return true; } + auto ret = req.progress(current, total); + if (!ret) { error = Error::Canceled; } + return ret; + }; + + int dummy_status; + if (!detail::read_content(strm, res, (std::numeric_limits::max)(), + dummy_status, std::move(progress), std::move(out), + decompress_)) { + if (error != Error::Canceled) { error = Error::Read; } + return false; + } + } + + // Log + if (logger_) { logger_(req, res); } + + return true; +} + +inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider( + const std::string &boundary, const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) const { + size_t cur_item = 0; + size_t cur_start = 0; + // cur_item and cur_start are copied to within the std::function and maintain + // state between successive calls + return [&, cur_item, cur_start](size_t offset, + DataSink &sink) mutable -> bool { + if (!offset && !items.empty()) { + sink.os << detail::serialize_multipart_formdata(items, boundary, false); + return true; + } else if (cur_item < provider_items.size()) { + if (!cur_start) { + const auto &begin = detail::serialize_multipart_formdata_item_begin( + provider_items[cur_item], boundary); + offset += begin.size(); + cur_start = offset; + sink.os << begin; + } + + DataSink cur_sink; + auto has_data = true; + cur_sink.write = sink.write; + cur_sink.done = [&]() { has_data = false; }; + + if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) { + return false; + } + + if (!has_data) { + sink.os << detail::serialize_multipart_formdata_item_end(); + cur_item++; + cur_start = 0; + } + return true; + } else { + sink.os << detail::serialize_multipart_formdata_finish(boundary); + sink.done(); + return true; + } + }; +} + +inline bool +ClientImpl::process_socket(const Socket &socket, + std::function callback) { + return detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, std::move(callback)); +} + +inline bool ClientImpl::is_ssl() const { return false; } + +inline Result ClientImpl::Get(const std::string &path) { + return Get(path, Headers(), Progress()); +} + +inline Result ClientImpl::Get(const std::string &path, Progress progress) { + return Get(path, Headers(), std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers) { + return Get(path, headers, Progress()); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, + Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = std::move(progress); + + return send_(std::move(req)); +} + +inline Result ClientImpl::Get(const std::string &path, + ContentReceiver content_receiver) { + return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const std::string &path, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, Headers(), nullptr, std::move(content_receiver), + std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver) { + return Get(path, headers, nullptr, std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, headers, nullptr, std::move(content_receiver), + std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, Headers(), std::move(response_handler), + std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, headers, std::move(response_handler), + std::move(content_receiver), nullptr); +} + +inline Result ClientImpl::Get(const std::string &path, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, Headers(), std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.response_handler = std::move(response_handler); + req.content_receiver = + [content_receiver](const char *data, size_t data_length, + uint64_t /*offset*/, uint64_t /*total_length*/) { + return content_receiver(data, data_length); + }; + req.progress = std::move(progress); + + return send_(std::move(req)); +} + +inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, + const Headers &headers, Progress progress) { + if (params.empty()) { return Get(path, headers); } + + std::string path_with_query = append_query_params(path, params); + return Get(path_with_query, headers, std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, params, headers, nullptr, std::move(content_receiver), + std::move(progress)); +} + +inline Result ClientImpl::Get(const std::string &path, const Params ¶ms, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + if (params.empty()) { + return Get(path, headers, std::move(response_handler), + std::move(content_receiver), std::move(progress)); + } + + std::string path_with_query = append_query_params(path, params); + return Get(path_with_query, headers, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} + +inline Result ClientImpl::Head(const std::string &path) { + return Head(path, Headers()); +} + +inline Result ClientImpl::Head(const std::string &path, + const Headers &headers) { + Request req; + req.method = "HEAD"; + req.headers = headers; + req.path = path; + + return send_(std::move(req)); +} + +inline Result ClientImpl::Post(const std::string &path) { + return Post(path, std::string(), std::string()); +} + +inline Result ClientImpl::Post(const std::string &path, + const Headers &headers) { + return Post(path, headers, nullptr, 0, std::string()); +} + +inline Result ClientImpl::Post(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return Post(path, Headers(), body, content_length, content_type, nullptr); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return send_with_content_provider("POST", path, headers, body, content_length, + nullptr, nullptr, content_type, nullptr); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("POST", path, headers, body, content_length, + nullptr, nullptr, content_type, progress); +} + +inline Result ClientImpl::Post(const std::string &path, const std::string &body, + const std::string &content_type) { + return Post(path, Headers(), body, content_type); +} + +inline Result ClientImpl::Post(const std::string &path, const std::string &body, + const std::string &content_type, + Progress progress) { + return Post(path, Headers(), body, content_type, progress); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return send_with_content_provider("POST", path, headers, body.data(), + body.size(), nullptr, nullptr, content_type, + nullptr); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("POST", path, headers, body.data(), + body.size(), nullptr, nullptr, content_type, + progress); +} + +inline Result ClientImpl::Post(const std::string &path, const Params ¶ms) { + return Post(path, Headers(), params); +} + +inline Result ClientImpl::Post(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return Post(path, Headers(), content_length, std::move(content_provider), + content_type); +} + +inline Result ClientImpl::Post(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return Post(path, Headers(), std::move(content_provider), content_type); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return send_with_content_provider("POST", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type, nullptr); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type, + nullptr); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Post(path, headers, query, "application/x-www-form-urlencoded"); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress) { + auto query = detail::params_to_query_str(params); + return Post(path, headers, query, "application/x-www-form-urlencoded", + progress); +} + +inline Result ClientImpl::Post(const std::string &path, + const MultipartFormDataItems &items) { + return Post(path, Headers(), items); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Post(path, headers, body, content_type); +} + +inline Result ClientImpl::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + if (!detail::is_multipart_boundary_chars_valid(boundary)) { + return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; + } + + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Post(path, headers, body, content_type); +} + +inline Result +ClientImpl::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + return send_with_content_provider( + "POST", path, headers, nullptr, 0, nullptr, + get_multipart_content_provider(boundary, items, provider_items), + content_type, nullptr); +} + +inline Result ClientImpl::Put(const std::string &path) { + return Put(path, std::string(), std::string()); +} + +inline Result ClientImpl::Put(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return Put(path, Headers(), body, content_length, content_type); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return send_with_content_provider("PUT", path, headers, body, content_length, + nullptr, nullptr, content_type, nullptr); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("PUT", path, headers, body, content_length, + nullptr, nullptr, content_type, progress); +} + +inline Result ClientImpl::Put(const std::string &path, const std::string &body, + const std::string &content_type) { + return Put(path, Headers(), body, content_type); +} + +inline Result ClientImpl::Put(const std::string &path, const std::string &body, + const std::string &content_type, + Progress progress) { + return Put(path, Headers(), body, content_type, progress); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return send_with_content_provider("PUT", path, headers, body.data(), + body.size(), nullptr, nullptr, content_type, + nullptr); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("PUT", path, headers, body.data(), + body.size(), nullptr, nullptr, content_type, + progress); +} + +inline Result ClientImpl::Put(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return Put(path, Headers(), content_length, std::move(content_provider), + content_type); +} + +inline Result ClientImpl::Put(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return Put(path, Headers(), std::move(content_provider), content_type); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return send_with_content_provider("PUT", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type, nullptr); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type, + nullptr); +} + +inline Result ClientImpl::Put(const std::string &path, const Params ¶ms) { + return Put(path, Headers(), params); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Put(path, headers, query, "application/x-www-form-urlencoded"); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress) { + auto query = detail::params_to_query_str(params); + return Put(path, headers, query, "application/x-www-form-urlencoded", + progress); +} + +inline Result ClientImpl::Put(const std::string &path, + const MultipartFormDataItems &items) { + return Put(path, Headers(), items); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Put(path, headers, body, content_type); +} + +inline Result ClientImpl::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + if (!detail::is_multipart_boundary_chars_valid(boundary)) { + return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; + } + + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + const auto &body = detail::serialize_multipart_formdata(items, boundary); + return Put(path, headers, body, content_type); +} + +inline Result +ClientImpl::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + const auto &boundary = detail::make_multipart_data_boundary(); + const auto &content_type = + detail::serialize_multipart_formdata_get_content_type(boundary); + return send_with_content_provider( + "PUT", path, headers, nullptr, 0, nullptr, + get_multipart_content_provider(boundary, items, provider_items), + content_type, nullptr); +} +inline Result ClientImpl::Patch(const std::string &path) { + return Patch(path, std::string(), std::string()); +} + +inline Result ClientImpl::Patch(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return Patch(path, Headers(), body, content_length, content_type); +} + +inline Result ClientImpl::Patch(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type, + Progress progress) { + return Patch(path, Headers(), body, content_length, content_type, progress); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return Patch(path, headers, body, content_length, content_type, nullptr); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("PATCH", path, headers, body, + content_length, nullptr, nullptr, + content_type, progress); +} + +inline Result ClientImpl::Patch(const std::string &path, + const std::string &body, + const std::string &content_type) { + return Patch(path, Headers(), body, content_type); +} + +inline Result ClientImpl::Patch(const std::string &path, + const std::string &body, + const std::string &content_type, Progress progress) { + return Patch(path, Headers(), body, content_type, progress); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return Patch(path, headers, body, content_type, nullptr); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, + Progress progress) { + return send_with_content_provider("PATCH", path, headers, body.data(), + body.size(), nullptr, nullptr, content_type, + progress); +} + +inline Result ClientImpl::Patch(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return Patch(path, Headers(), content_length, std::move(content_provider), + content_type); +} + +inline Result ClientImpl::Patch(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return Patch(path, Headers(), std::move(content_provider), content_type); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return send_with_content_provider("PATCH", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type, nullptr); +} + +inline Result ClientImpl::Patch(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type, + nullptr); +} + +inline Result ClientImpl::Delete(const std::string &path) { + return Delete(path, Headers(), std::string(), std::string()); +} + +inline Result ClientImpl::Delete(const std::string &path, + const Headers &headers) { + return Delete(path, headers, std::string(), std::string()); +} + +inline Result ClientImpl::Delete(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return Delete(path, Headers(), body, content_length, content_type); +} + +inline Result ClientImpl::Delete(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type, + Progress progress) { + return Delete(path, Headers(), body, content_length, content_type, progress); +} + +inline Result ClientImpl::Delete(const std::string &path, + const Headers &headers, const char *body, + size_t content_length, + const std::string &content_type) { + return Delete(path, headers, body, content_length, content_type, nullptr); +} + +inline Result ClientImpl::Delete(const std::string &path, + const Headers &headers, const char *body, + size_t content_length, + const std::string &content_type, + Progress progress) { + Request req; + req.method = "DELETE"; + req.headers = headers; + req.path = path; + req.progress = progress; + + if (!content_type.empty()) { req.set_header("Content-Type", content_type); } + req.body.assign(body, content_length); + + return send_(std::move(req)); +} + +inline Result ClientImpl::Delete(const std::string &path, + const std::string &body, + const std::string &content_type) { + return Delete(path, Headers(), body.data(), body.size(), content_type); +} + +inline Result ClientImpl::Delete(const std::string &path, + const std::string &body, + const std::string &content_type, + Progress progress) { + return Delete(path, Headers(), body.data(), body.size(), content_type, + progress); +} + +inline Result ClientImpl::Delete(const std::string &path, + const Headers &headers, + const std::string &body, + const std::string &content_type) { + return Delete(path, headers, body.data(), body.size(), content_type); +} + +inline Result ClientImpl::Delete(const std::string &path, + const Headers &headers, + const std::string &body, + const std::string &content_type, + Progress progress) { + return Delete(path, headers, body.data(), body.size(), content_type, + progress); +} + +inline Result ClientImpl::Options(const std::string &path) { + return Options(path, Headers()); +} + +inline Result ClientImpl::Options(const std::string &path, + const Headers &headers) { + Request req; + req.method = "OPTIONS"; + req.headers = headers; + req.path = path; + + return send_(std::move(req)); +} + +inline void ClientImpl::stop() { + std::lock_guard guard(socket_mutex_); + + // If there is anything ongoing right now, the ONLY thread-safe thing we can + // do is to shutdown_socket, so that threads using this socket suddenly + // discover they can't read/write any more and error out. Everything else + // (closing the socket, shutting ssl down) is unsafe because these actions are + // not thread-safe. + if (socket_requests_in_flight_ > 0) { + shutdown_socket(socket_); + + // Aside from that, we set a flag for the socket to be closed when we're + // done. + socket_should_be_closed_when_request_is_done_ = true; + return; + } + + // Otherwise, still holding the mutex, we can shut everything down ourselves + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); +} + +inline std::string ClientImpl::host() const { return host_; } + +inline int ClientImpl::port() const { return port_; } + +inline size_t ClientImpl::is_socket_open() const { + std::lock_guard guard(socket_mutex_); + return socket_.is_open(); +} + +inline socket_t ClientImpl::socket() const { return socket_.sock; } + +inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { + connection_timeout_sec_ = sec; + connection_timeout_usec_ = usec; +} + +inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; +} + +inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; +} + +inline void ClientImpl::set_basic_auth(const std::string &username, + const std::string &password) { + basic_auth_username_ = username; + basic_auth_password_ = password; +} + +inline void ClientImpl::set_bearer_token_auth(const std::string &token) { + bearer_token_auth_token_ = token; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void ClientImpl::set_digest_auth(const std::string &username, + const std::string &password) { + digest_auth_username_ = username; + digest_auth_password_ = password; +} +#endif + +inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; } + +inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; } + +inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; } + +inline void +ClientImpl::set_hostname_addr_map(std::map addr_map) { + addr_map_ = std::move(addr_map); +} + +inline void ClientImpl::set_default_headers(Headers headers) { + default_headers_ = std::move(headers); +} + +inline void ClientImpl::set_header_writer( + std::function const &writer) { + header_writer_ = writer; +} + +inline void ClientImpl::set_address_family(int family) { + address_family_ = family; +} + +inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } + +inline void ClientImpl::set_socket_options(SocketOptions socket_options) { + socket_options_ = std::move(socket_options); +} + +inline void ClientImpl::set_compress(bool on) { compress_ = on; } + +inline void ClientImpl::set_decompress(bool on) { decompress_ = on; } + +inline void ClientImpl::set_interface(const std::string &intf) { + interface_ = intf; +} + +inline void ClientImpl::set_proxy(const std::string &host, int port) { + proxy_host_ = host; + proxy_port_ = port; +} + +inline void ClientImpl::set_proxy_basic_auth(const std::string &username, + const std::string &password) { + proxy_basic_auth_username_ = username; + proxy_basic_auth_password_ = password; +} + +inline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) { + proxy_bearer_token_auth_token_ = token; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void ClientImpl::set_proxy_digest_auth(const std::string &username, + const std::string &password) { + proxy_digest_auth_username_ = username; + proxy_digest_auth_password_ = password; +} + +inline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path, + const std::string &ca_cert_dir_path) { + ca_cert_file_path_ = ca_cert_file_path; + ca_cert_dir_path_ = ca_cert_dir_path; +} + +inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (ca_cert_store && ca_cert_store != ca_cert_store_) { + ca_cert_store_ = ca_cert_store; + } +} + +inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert, + std::size_t size) const { + auto mem = BIO_new_mem_buf(ca_cert, static_cast(size)); + if (!mem) { return nullptr; } + + auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr); + if (!inf) { + BIO_free_all(mem); + return nullptr; + } + + auto cts = X509_STORE_new(); + if (cts) { + for (auto i = 0; i < static_cast(sk_X509_INFO_num(inf)); i++) { + auto itmp = sk_X509_INFO_value(inf, i); + if (!itmp) { continue; } + + if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); } + if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); } + } + } + + sk_X509_INFO_pop_free(inf, X509_INFO_free); + BIO_free_all(mem); + return cts; +} + +inline void ClientImpl::enable_server_certificate_verification(bool enabled) { + server_certificate_verification_ = enabled; +} +#endif + +inline void ClientImpl::set_logger(Logger logger) { + logger_ = std::move(logger); +} + +/* + * SSL Implementation + */ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +namespace detail { + +template +inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, + U SSL_connect_or_accept, V setup) { + SSL *ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + ssl = SSL_new(ctx); + } + + if (ssl) { + set_nonblocking(sock, true); + auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); + BIO_set_nbio(bio, 1); + SSL_set_bio(ssl, bio, bio); + + if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) { + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + set_nonblocking(sock, false); + return nullptr; + } + BIO_set_nbio(bio, 0); + set_nonblocking(sock, false); + } + + return ssl; +} + +inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, + bool shutdown_gracefully) { + // sometimes we may want to skip this to try to avoid SIGPIPE if we know + // the remote has closed the network connection + // Note that it is not always possible to avoid SIGPIPE, this is merely a + // best-efforts. + if (shutdown_gracefully) { SSL_shutdown(ssl); } + + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); +} + +template +bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl, + U ssl_connect_or_accept, + time_t timeout_sec, + time_t timeout_usec) { + auto res = 0; + while ((res = ssl_connect_or_accept(ssl)) != 1) { + auto err = SSL_get_error(ssl, res); + switch (err) { + case SSL_ERROR_WANT_READ: + if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; } + break; + case SSL_ERROR_WANT_WRITE: + if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; } + break; + default: break; + } + return false; + } + return true; +} + +template +inline bool process_server_socket_ssl( + const std::atomic &svr_sock, SSL *ssl, socket_t sock, + size_t keep_alive_max_count, time_t keep_alive_timeout_sec, + time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + return process_server_socket_core( + svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec, + [&](bool close_connection, bool &connection_closed) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +template +inline bool +process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +class SSLInit { +public: + SSLInit() { + OPENSSL_init_ssl( + OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); + } +}; + +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) { + SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY); +} + +inline SSLSocketStream::~SSLSocketStream() = default; + +inline bool SSLSocketStream::is_readable() const { + return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SSLSocketStream::is_writable() const { + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 && + is_socket_alive(sock_); +} + +inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { + if (SSL_pending(ssl_) > 0) { + return SSL_read(ssl_, ptr, static_cast(size)); + } else if (is_readable()) { + auto ret = SSL_read(ssl_, ptr, static_cast(size)); + if (ret < 0) { + auto err = SSL_get_error(ssl_, ret); + auto n = 1000; +#ifdef _WIN32 + while (--n >= 0 && (err == SSL_ERROR_WANT_READ || + (err == SSL_ERROR_SYSCALL && + WSAGetLastError() == WSAETIMEDOUT))) { +#else + while (--n >= 0 && err == SSL_ERROR_WANT_READ) { +#endif + if (SSL_pending(ssl_) > 0) { + return SSL_read(ssl_, ptr, static_cast(size)); + } else if (is_readable()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + ret = SSL_read(ssl_, ptr, static_cast(size)); + if (ret >= 0) { return ret; } + err = SSL_get_error(ssl_, ret); + } else { + return -1; + } + } + } + return ret; + } + return -1; +} + +inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) { + if (is_writable()) { + auto handle_size = static_cast( + std::min(size, (std::numeric_limits::max)())); + + auto ret = SSL_write(ssl_, ptr, static_cast(handle_size)); + if (ret < 0) { + auto err = SSL_get_error(ssl_, ret); + auto n = 1000; +#ifdef _WIN32 + while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE || + (err == SSL_ERROR_SYSCALL && + WSAGetLastError() == WSAETIMEDOUT))) { +#else + while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) { +#endif + if (is_writable()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + ret = SSL_write(ssl_, ptr, static_cast(handle_size)); + if (ret >= 0) { return ret; } + err = SSL_get_error(ssl_, ret); + } else { + return -1; + } + } + } + return ret; + } + return -1; +} + +inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, + int &port) const { + detail::get_remote_ip_and_port(sock_, ip, port); +} + +inline void SSLSocketStream::get_local_ip_and_port(std::string &ip, + int &port) const { + detail::get_local_ip_and_port(sock_, ip, port); +} + +inline socket_t SSLSocketStream::socket() const { return sock_; } + +static SSLInit sslinit_; + +} // namespace detail + +// SSL HTTP server implementation +inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path, + const char *client_ca_cert_dir_path, + const char *private_key_password) { + ctx_ = SSL_CTX_new(TLS_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION); + + if (private_key_password != nullptr && (private_key_password[0] != '\0')) { + SSL_CTX_set_default_passwd_cb_userdata( + ctx_, + reinterpret_cast(const_cast(private_key_password))); + } + + if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != + 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_file_path || client_ca_cert_dir_path) { + SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, + client_ca_cert_dir_path); + + SSL_CTX_set_verify( + ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + } + } +} + +inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store) { + ctx_ = SSL_CTX_new(TLS_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION); + + if (SSL_CTX_use_certificate(ctx_, cert) != 1 || + SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_store) { + SSL_CTX_set_cert_store(ctx_, client_ca_cert_store); + + SSL_CTX_set_verify( + ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + } + } +} + +inline SSLServer::SSLServer( + const std::function &setup_ssl_ctx_callback) { + ctx_ = SSL_CTX_new(TLS_method()); + if (ctx_) { + if (!setup_ssl_ctx_callback(*ctx_)) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLServer::~SSLServer() { + if (ctx_) { SSL_CTX_free(ctx_); } +} + +inline bool SSLServer::is_valid() const { return ctx_; } + +inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; } + +inline void SSLServer::update_certs (X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store) { + + std::lock_guard guard(ctx_mutex_); + + SSL_CTX_use_certificate (ctx_, cert); + SSL_CTX_use_PrivateKey (ctx_, private_key); + + if (client_ca_cert_store != nullptr) { + SSL_CTX_set_cert_store (ctx_, client_ca_cert_store); + } +} + +inline bool SSLServer::process_and_close_socket(socket_t sock) { + auto ssl = detail::ssl_new( + sock, ctx_, ctx_mutex_, + [&](SSL *ssl2) { + return detail::ssl_connect_or_accept_nonblocking( + sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_); + }, + [](SSL * /*ssl2*/) { return true; }); + + auto ret = false; + if (ssl) { + ret = detail::process_server_socket_ssl( + svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, + [this, ssl](Stream &strm, bool close_connection, + bool &connection_closed) { + return process_request(strm, close_connection, connection_closed, + [&](Request &req) { req.ssl = ssl; }); + }); + + // Shutdown gracefully if the result seemed successful, non-gracefully if + // the connection appeared to be closed. + const bool shutdown_gracefully = ret; + detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully); + } + + detail::shutdown_socket(sock); + detail::close_socket(sock); + return ret; +} + +// SSL HTTP client implementation +inline SSLClient::SSLClient(const std::string &host) + : SSLClient(host, 443, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port) + : SSLClient(host, port, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path, + const std::string &private_key_password) + : ClientImpl(host, port, client_cert_path, client_key_path) { + ctx_ = SSL_CTX_new(TLS_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char *b, const char *e) { + host_components_.emplace_back(b, e); + }); + + if (!client_cert_path.empty() && !client_key_path.empty()) { + if (!private_key_password.empty()) { + SSL_CTX_set_default_passwd_cb_userdata( + ctx_, reinterpret_cast( + const_cast(private_key_password.c_str()))); + } + + if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), + SSL_FILETYPE_PEM) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), + SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::SSLClient(const std::string &host, int port, + X509 *client_cert, EVP_PKEY *client_key, + const std::string &private_key_password) + : ClientImpl(host, port) { + ctx_ = SSL_CTX_new(TLS_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char *b, const char *e) { + host_components_.emplace_back(b, e); + }); + + if (client_cert != nullptr && client_key != nullptr) { + if (!private_key_password.empty()) { + SSL_CTX_set_default_passwd_cb_userdata( + ctx_, reinterpret_cast( + const_cast(private_key_password.c_str()))); + } + + if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || + SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::~SSLClient() { + if (ctx_) { SSL_CTX_free(ctx_); } + // Make sure to shut down SSL since shutdown_ssl will resolve to the + // base function rather than the derived function once we get to the + // base class destructor, and won't free the SSL (causing a leak). + shutdown_ssl_impl(socket_, true); +} + +inline bool SSLClient::is_valid() const { return ctx_; } + +inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (ca_cert_store) { + if (ctx_) { + if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) { + // Free memory allocated for old cert and use new store `ca_cert_store` + SSL_CTX_set_cert_store(ctx_, ca_cert_store); + } + } else { + X509_STORE_free(ca_cert_store); + } + } +} + +inline void SSLClient::load_ca_cert_store(const char *ca_cert, + std::size_t size) { + set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size)); +} + +inline long SSLClient::get_openssl_verify_result() const { + return verify_result_; +} + +inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; } + +inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) { + return is_valid() && ClientImpl::create_and_connect_socket(socket, error); +} + +// Assumes that socket_mutex_ is locked and that there are no requests in flight +inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, + bool &success, Error &error) { + success = true; + Response proxy_res; + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { + Request req2; + req2.method = "CONNECT"; + req2.path = host_and_port_; + return process_request(strm, req2, proxy_res, false, error); + })) { + // Thread-safe to close everything because we are assuming there are no + // requests in flight + shutdown_ssl(socket, true); + shutdown_socket(socket); + close_socket(socket); + success = false; + return false; + } + + if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) { + if (!proxy_digest_auth_username_.empty() && + !proxy_digest_auth_password_.empty()) { + std::map auth; + if (detail::parse_www_authenticate(proxy_res, auth, true)) { + proxy_res = Response(); + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { + Request req3; + req3.method = "CONNECT"; + req3.path = host_and_port_; + req3.headers.insert(detail::make_digest_authentication_header( + req3, auth, 1, detail::random_string(10), + proxy_digest_auth_username_, proxy_digest_auth_password_, + true)); + return process_request(strm, req3, proxy_res, false, error); + })) { + // Thread-safe to close everything because we are assuming there are + // no requests in flight + shutdown_ssl(socket, true); + shutdown_socket(socket); + close_socket(socket); + success = false; + return false; + } + } + } + } + + // If status code is not 200, proxy request is failed. + // Set error to ProxyConnection and return proxy response + // as the response of the request + if (proxy_res.status != StatusCode::OK_200) { + error = Error::ProxyConnection; + res = std::move(proxy_res); + // Thread-safe to close everything because we are assuming there are + // no requests in flight + shutdown_ssl(socket, true); + shutdown_socket(socket); + close_socket(socket); + return false; + } + + return true; +} + +inline bool SSLClient::load_certs() { + auto ret = true; + + std::call_once(initialize_cert_, [&]() { + std::lock_guard guard(ctx_mutex_); + if (!ca_cert_file_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), + nullptr)) { + ret = false; + } + } else if (!ca_cert_dir_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, nullptr, + ca_cert_dir_path_.c_str())) { + ret = false; + } + } else { + auto loaded = false; +#ifdef _WIN32 + loaded = + detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_)); +#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__) +#if TARGET_OS_OSX + loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_)); +#endif // TARGET_OS_OSX +#endif // _WIN32 + if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); } + } + }); + + return ret; +} + +inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) { + auto ssl = detail::ssl_new( + socket.sock, ctx_, ctx_mutex_, + [&](SSL *ssl2) { + if (server_certificate_verification_) { + if (!load_certs()) { + error = Error::SSLLoadingCerts; + return false; + } + SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr); + } + + if (!detail::ssl_connect_or_accept_nonblocking( + socket.sock, ssl2, SSL_connect, connection_timeout_sec_, + connection_timeout_usec_)) { + error = Error::SSLConnection; + return false; + } + + if (server_certificate_verification_) { + verify_result_ = SSL_get_verify_result(ssl2); + + if (verify_result_ != X509_V_OK) { + error = Error::SSLServerVerification; + return false; + } + + auto server_cert = SSL_get1_peer_certificate(ssl2); + + if (server_cert == nullptr) { + error = Error::SSLServerVerification; + return false; + } + + if (!verify_host(server_cert)) { + X509_free(server_cert); + error = Error::SSLServerVerification; + return false; + } + X509_free(server_cert); + } + + return true; + }, + [&](SSL *ssl2) { + // NOTE: Direct call instead of using the OpenSSL macro to suppress + // -Wold-style-cast warning + // SSL_set_tlsext_host_name(ssl2, host_.c_str()); + SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, + static_cast(const_cast(host_.c_str()))); + return true; + }); + + if (ssl) { + socket.ssl = ssl; + return true; + } + + shutdown_socket(socket); + close_socket(socket); + return false; +} + +inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) { + shutdown_ssl_impl(socket, shutdown_gracefully); +} + +inline void SSLClient::shutdown_ssl_impl(Socket &socket, + bool shutdown_gracefully) { + if (socket.sock == INVALID_SOCKET) { + assert(socket.ssl == nullptr); + return; + } + if (socket.ssl) { + detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully); + socket.ssl = nullptr; + } + assert(socket.ssl == nullptr); +} + +inline bool +SSLClient::process_socket(const Socket &socket, + std::function callback) { + assert(socket.ssl); + return detail::process_client_socket_ssl( + socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, std::move(callback)); +} + +inline bool SSLClient::is_ssl() const { return true; } + +inline bool SSLClient::verify_host(X509 *server_cert) const { + /* Quote from RFC2818 section 3.1 "Server Identity" + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. + + Matching is performed using the matching rules specified by + [RFC2459]. If more than one identity of a given type is present in + the certificate (e.g., more than one dNSName name, a match in any one + of the set is considered acceptable.) Names may contain the wildcard + character * which is considered to match any single domain name + component or component fragment. E.g., *.a.com matches foo.a.com but + not bar.foo.a.com. f*.com matches foo.com but not bar.com. + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. + + */ + return verify_host_with_subject_alt_name(server_cert) || + verify_host_with_common_name(server_cert); +} + +inline bool +SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { + auto ret = false; + + auto type = GEN_DNS; + + struct in6_addr addr6 {}; + struct in_addr addr {}; + size_t addr_len = 0; + +#ifndef __MINGW32__ + if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { + type = GEN_IPADD; + addr_len = sizeof(struct in6_addr); + } else if (inet_pton(AF_INET, host_.c_str(), &addr)) { + type = GEN_IPADD; + addr_len = sizeof(struct in_addr); + } +#endif + + auto alt_names = static_cast( + X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); + + if (alt_names) { + auto dsn_matched = false; + auto ip_matched = false; + + auto count = sk_GENERAL_NAME_num(alt_names); + + for (decltype(count) i = 0; i < count && !dsn_matched; i++) { + auto val = sk_GENERAL_NAME_value(alt_names, i); + if (val->type == type) { + auto name = + reinterpret_cast(ASN1_STRING_get0_data(val->d.ia5)); + auto name_len = static_cast(ASN1_STRING_length(val->d.ia5)); + + switch (type) { + case GEN_DNS: dsn_matched = check_host_name(name, name_len); break; + + case GEN_IPADD: + if (!memcmp(&addr6, name, addr_len) || + !memcmp(&addr, name, addr_len)) { + ip_matched = true; + } + break; + } + } + } + + if (dsn_matched || ip_matched) { ret = true; } + } + + GENERAL_NAMES_free(const_cast( + reinterpret_cast(alt_names))); + return ret; +} + +inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { + const auto subject_name = X509_get_subject_name(server_cert); + + if (subject_name != nullptr) { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, + name, sizeof(name)); + + if (name_len != -1) { + return check_host_name(name, static_cast(name_len)); + } + } + + return false; +} + +inline bool SSLClient::check_host_name(const char *pattern, + size_t pattern_len) const { + if (host_.size() == pattern_len && host_ == pattern) { return true; } + + // Wildcard match + // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484 + std::vector pattern_components; + detail::split(&pattern[0], &pattern[pattern_len], '.', + [&](const char *b, const char *e) { + pattern_components.emplace_back(b, e); + }); + + if (host_components_.size() != pattern_components.size()) { return false; } + + auto itr = pattern_components.begin(); + for (const auto &h : host_components_) { + auto &p = *itr; + if (p != h && p != "*") { + auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && + !p.compare(0, p.size() - 1, h)); + if (!partial_match) { return false; } + } + ++itr; + } + + return true; +} +#endif + +// Universal client implementation +inline Client::Client(const std::string &scheme_host_port) + : Client(scheme_host_port, std::string(), std::string()) {} + +inline Client::Client(const std::string &scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path) { + const static std::regex re( + R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)"); + + std::smatch m; + if (std::regex_match(scheme_host_port, m, re)) { + auto scheme = m[1].str(); + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if (!scheme.empty() && (scheme != "http" && scheme != "https")) { +#else + if (!scheme.empty() && scheme != "http") { +#endif +#ifndef CPPHTTPLIB_NO_EXCEPTIONS + std::string msg = "'" + scheme + "' scheme is not supported."; + throw std::invalid_argument(msg); +#endif + return; + } + + auto is_ssl = scheme == "https"; + + auto host = m[2].str(); + if (host.empty()) { host = m[3].str(); } + + auto port_str = m[4].str(); + auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80); + + if (is_ssl) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + cli_ = detail::make_unique(host, port, client_cert_path, + client_key_path); + is_ssl_ = is_ssl; +#endif + } else { + cli_ = detail::make_unique(host, port, client_cert_path, + client_key_path); + } + } else { + // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress) + // if port param below changes. + cli_ = detail::make_unique(scheme_host_port, 80, + client_cert_path, client_key_path); + } +} + +inline Client::Client(const std::string &host, int port) + : cli_(detail::make_unique(host, port)) {} + +inline Client::Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : cli_(detail::make_unique(host, port, client_cert_path, + client_key_path)) {} + +inline Client::~Client() = default; + +inline bool Client::is_valid() const { + return cli_ != nullptr && cli_->is_valid(); +} + +inline Result Client::Get(const std::string &path) { return cli_->Get(path); } +inline Result Client::Get(const std::string &path, const Headers &headers) { + return cli_->Get(path, headers); +} +inline Result Client::Get(const std::string &path, Progress progress) { + return cli_->Get(path, std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Headers &headers, + Progress progress) { + return cli_->Get(path, headers, std::move(progress)); +} +inline Result Client::Get(const std::string &path, + ContentReceiver content_receiver) { + return cli_->Get(path, std::move(content_receiver)); +} +inline Result Client::Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, std::move(content_receiver)); +} +inline Result Client::Get(const std::string &path, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Headers &headers, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, headers, std::move(content_receiver), + std::move(progress)); +} +inline Result Client::Get(const std::string &path, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver)); +} +inline Result Client::Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, std::move(response_handler), + std::move(content_receiver)); +} +inline Result Client::Get(const std::string &path, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, headers, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Params ¶ms, + const Headers &headers, Progress progress) { + return cli_->Get(path, params, headers, std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Params ¶ms, + const Headers &headers, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, params, headers, std::move(content_receiver), + std::move(progress)); +} +inline Result Client::Get(const std::string &path, const Params ¶ms, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, params, headers, std::move(response_handler), + std::move(content_receiver), std::move(progress)); +} + +inline Result Client::Head(const std::string &path) { return cli_->Head(path); } +inline Result Client::Head(const std::string &path, const Headers &headers) { + return cli_->Head(path, headers); +} + +inline Result Client::Post(const std::string &path) { return cli_->Post(path); } +inline Result Client::Post(const std::string &path, const Headers &headers) { + return cli_->Post(path, headers); +} +inline Result Client::Post(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return cli_->Post(path, body, content_length, content_type); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return cli_->Post(path, headers, body, content_length, content_type); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Post(path, headers, body, content_length, content_type, + progress); +} +inline Result Client::Post(const std::string &path, const std::string &body, + const std::string &content_type) { + return cli_->Post(path, body, content_type); +} +inline Result Client::Post(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Post(path, body, content_type, progress); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return cli_->Post(path, headers, body, content_type); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Post(path, headers, body, content_type, progress); +} +inline Result Client::Post(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Post(path, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Post(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Post(path, std::move(content_provider), content_type); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Post(path, headers, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Post(path, headers, std::move(content_provider), content_type); +} +inline Result Client::Post(const std::string &path, const Params ¶ms) { + return cli_->Post(path, params); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const Params ¶ms) { + return cli_->Post(path, headers, params); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress) { + return cli_->Post(path, headers, params, progress); +} +inline Result Client::Post(const std::string &path, + const MultipartFormDataItems &items) { + return cli_->Post(path, items); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items) { + return cli_->Post(path, headers, items); +} +inline Result Client::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + return cli_->Post(path, headers, items, boundary); +} +inline Result +Client::Post(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + return cli_->Post(path, headers, items, provider_items); +} +inline Result Client::Put(const std::string &path) { return cli_->Put(path); } +inline Result Client::Put(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return cli_->Put(path, body, content_length, content_type); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return cli_->Put(path, headers, body, content_length, content_type); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Put(path, headers, body, content_length, content_type, progress); +} +inline Result Client::Put(const std::string &path, const std::string &body, + const std::string &content_type) { + return cli_->Put(path, body, content_type); +} +inline Result Client::Put(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Put(path, body, content_type, progress); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return cli_->Put(path, headers, body, content_type); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Put(path, headers, body, content_type, progress); +} +inline Result Client::Put(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Put(path, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Put(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Put(path, std::move(content_provider), content_type); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Put(path, headers, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Put(path, headers, std::move(content_provider), content_type); +} +inline Result Client::Put(const std::string &path, const Params ¶ms) { + return cli_->Put(path, params); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const Params ¶ms) { + return cli_->Put(path, headers, params); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const Params ¶ms, Progress progress) { + return cli_->Put(path, headers, params, progress); +} +inline Result Client::Put(const std::string &path, + const MultipartFormDataItems &items) { + return cli_->Put(path, items); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items) { + return cli_->Put(path, headers, items); +} +inline Result Client::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + return cli_->Put(path, headers, items, boundary); +} +inline Result +Client::Put(const std::string &path, const Headers &headers, + const MultipartFormDataItems &items, + const MultipartFormDataProviderItems &provider_items) { + return cli_->Put(path, headers, items, provider_items); +} +inline Result Client::Patch(const std::string &path) { + return cli_->Patch(path); +} +inline Result Client::Patch(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return cli_->Patch(path, body, content_length, content_type); +} +inline Result Client::Patch(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Patch(path, body, content_length, content_type, progress); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return cli_->Patch(path, headers, body, content_length, content_type); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Patch(path, headers, body, content_length, content_type, progress); +} +inline Result Client::Patch(const std::string &path, const std::string &body, + const std::string &content_type) { + return cli_->Patch(path, body, content_type); +} +inline Result Client::Patch(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Patch(path, body, content_type, progress); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return cli_->Patch(path, headers, body, content_type); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Patch(path, headers, body, content_type, progress); +} +inline Result Client::Patch(const std::string &path, size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Patch(path, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Patch(const std::string &path, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Patch(path, std::move(content_provider), content_type); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const std::string &content_type) { + return cli_->Patch(path, headers, content_length, std::move(content_provider), + content_type); +} +inline Result Client::Patch(const std::string &path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const std::string &content_type) { + return cli_->Patch(path, headers, std::move(content_provider), content_type); +} +inline Result Client::Delete(const std::string &path) { + return cli_->Delete(path); +} +inline Result Client::Delete(const std::string &path, const Headers &headers) { + return cli_->Delete(path, headers); +} +inline Result Client::Delete(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type) { + return cli_->Delete(path, body, content_length, content_type); +} +inline Result Client::Delete(const std::string &path, const char *body, + size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Delete(path, body, content_length, content_type, progress); +} +inline Result Client::Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type) { + return cli_->Delete(path, headers, body, content_length, content_type); +} +inline Result Client::Delete(const std::string &path, const Headers &headers, + const char *body, size_t content_length, + const std::string &content_type, Progress progress) { + return cli_->Delete(path, headers, body, content_length, content_type, progress); +} +inline Result Client::Delete(const std::string &path, const std::string &body, + const std::string &content_type) { + return cli_->Delete(path, body, content_type); +} +inline Result Client::Delete(const std::string &path, const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Delete(path, body, content_type, progress); +} +inline Result Client::Delete(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type) { + return cli_->Delete(path, headers, body, content_type); +} +inline Result Client::Delete(const std::string &path, const Headers &headers, + const std::string &body, + const std::string &content_type, Progress progress) { + return cli_->Delete(path, headers, body, content_type, progress); +} +inline Result Client::Options(const std::string &path) { + return cli_->Options(path); +} +inline Result Client::Options(const std::string &path, const Headers &headers) { + return cli_->Options(path, headers); +} + +inline bool Client::send(Request &req, Response &res, Error &error) { + return cli_->send(req, res, error); +} + +inline Result Client::send(const Request &req) { return cli_->send(req); } + +inline void Client::stop() { cli_->stop(); } + +inline std::string Client::host() const { return cli_->host(); } + +inline int Client::port() const { return cli_->port(); } + +inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); } + +inline socket_t Client::socket() const { return cli_->socket(); } + +inline void +Client::set_hostname_addr_map(std::map addr_map) { + cli_->set_hostname_addr_map(std::move(addr_map)); +} + +inline void Client::set_default_headers(Headers headers) { + cli_->set_default_headers(std::move(headers)); +} + +inline void Client::set_header_writer( + std::function const &writer) { + cli_->set_header_writer(writer); +} + +inline void Client::set_address_family(int family) { + cli_->set_address_family(family); +} + +inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } + +inline void Client::set_socket_options(SocketOptions socket_options) { + cli_->set_socket_options(std::move(socket_options)); +} + +inline void Client::set_connection_timeout(time_t sec, time_t usec) { + cli_->set_connection_timeout(sec, usec); +} + +inline void Client::set_read_timeout(time_t sec, time_t usec) { + cli_->set_read_timeout(sec, usec); +} + +inline void Client::set_write_timeout(time_t sec, time_t usec) { + cli_->set_write_timeout(sec, usec); +} + +inline void Client::set_basic_auth(const std::string &username, + const std::string &password) { + cli_->set_basic_auth(username, password); +} +inline void Client::set_bearer_token_auth(const std::string &token) { + cli_->set_bearer_token_auth(token); +} +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_digest_auth(const std::string &username, + const std::string &password) { + cli_->set_digest_auth(username, password); +} +#endif + +inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); } +inline void Client::set_follow_location(bool on) { + cli_->set_follow_location(on); +} + +inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); } + +inline void Client::set_compress(bool on) { cli_->set_compress(on); } + +inline void Client::set_decompress(bool on) { cli_->set_decompress(on); } + +inline void Client::set_interface(const std::string &intf) { + cli_->set_interface(intf); +} + +inline void Client::set_proxy(const std::string &host, int port) { + cli_->set_proxy(host, port); +} +inline void Client::set_proxy_basic_auth(const std::string &username, + const std::string &password) { + cli_->set_proxy_basic_auth(username, password); +} +inline void Client::set_proxy_bearer_token_auth(const std::string &token) { + cli_->set_proxy_bearer_token_auth(token); +} +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_proxy_digest_auth(const std::string &username, + const std::string &password) { + cli_->set_proxy_digest_auth(username, password); +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::enable_server_certificate_verification(bool enabled) { + cli_->enable_server_certificate_verification(enabled); +} +#endif + +inline void Client::set_logger(Logger logger) { + cli_->set_logger(std::move(logger)); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_ca_cert_path(const std::string &ca_cert_file_path, + const std::string &ca_cert_dir_path) { + cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path); +} + +inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_store(ca_cert_store); + } else { + cli_->set_ca_cert_store(ca_cert_store); + } +} + +inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) { + set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size)); +} + +inline long Client::get_openssl_verify_result() const { + if (is_ssl_) { + return static_cast(*cli_).get_openssl_verify_result(); + } + return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? +} + +inline SSL_CTX *Client::ssl_context() const { + if (is_ssl_) { return static_cast(*cli_).ssl_context(); } + return nullptr; +} +#endif + +// ---------------------------------------------------------------------------- + +} // namespace httplib + +#if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL) +#undef poll +#endif + +#endif // CPPHTTPLIB_HTTPLIB_H diff --git a/dependencies/hueplusplus-1.2.0/.clang-format b/dependencies/hueplusplus-1.2.0/.clang-format new file mode 100644 index 0000000..be3aa8d --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/.clang-format @@ -0,0 +1,61 @@ +--- +# Based on Webkit style +BasedOnStyle: Webkit +IndentWidth: 4 +ColumnLimit: 120 +--- +Language: Cpp +Standard: Cpp11 +# Pointers aligned to the left +DerivePointerAlignment: false +PointerAlignment: Left +AccessModifierOffset: -4 +AllowShortFunctionsOnASingleLine: Inline +AlwaysBreakTemplateDeclarations: true +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakConstructorInitializers: BeforeColon +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +Cpp11BracedListStyle: true +FixNamespaceComments: true +IncludeBlocks: Regroup +IncludeCategories: + # C++ standard headers (no .h) + - Regex: '<[[:alnum:]_-]+>' + Priority: 1 + # Hueplusplus library + - Regex: '' + Priority: 2 + # Extenal libraries (with .h) + - Regex: '<[[:alnum:]_./-]+>' + Priority: 3 + # Headers from same folder + - Regex: '"[[:alnum:]_.-]+"' + Priority: 4 + # Headers from other folders + - Regex: '"[[:alnum:]_/.-]+"' + Priority: 5 +IndentCaseLabels: false +NamespaceIndentation: None +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterTemplateKeyword: true +SpacesInAngles: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +UseTab: Never \ No newline at end of file diff --git a/dependencies/hueplusplus-1.2.0/.github/CONTRIBUTING.md b/dependencies/hueplusplus-1.2.0/.github/CONTRIBUTING.md new file mode 100644 index 0000000..011a768 --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/.github/CONTRIBUTING.md @@ -0,0 +1,58 @@ +# Contribution Guide +Help is always welcome. If you want to contribute to hueplusplus, please read these guidelines + +## Request feature / Report bug +To request a feature or report a bug, create an issue using the templates. + +## Making changes +If you want to add a new feature or fix a bug, first check out the development branch and open and closed issues. +Maybe the feature already exists and you would just do duplicate work. + +Also use the development branch as the base for your feature branch, because all pull requests are first rebased into development. + +## Pull requests +When creating a pull request, be sure to choose development as the target. +You might need to rebase on development again and merge in new changes. + +### Keeping up with changes +While you are working on your pull request or your forked branch it might occur that +someone has force pushed the development branch, from which you started your feature branch. +In that case you will need to rebase onto the force pushed branch. For that you need to follow these steps: + +1. Switch to the development branch +``` +git checkout development +``` +2. Add this repository as your remote upstream +``` +git remote add upstream git@github.com:enwi/hueplusplus.git +``` +3. Fetch all changes +``` +git fetch upstream +``` +4. Reset your development branch and replace it with our (force pushed) version +``` +git reset --hard upstream/development +``` +> If you have for some reason made changes to your development branch do a rebase pull to preserve them +> ``` +> git pull --rebase upstream/development +> ``` +5. Switch back to your feature branch +``` +git checkout name-of-your-feature-branch +``` +6. Rebase your changes on to the new development branch +``` +git rebase development +``` +7. Force push your changes (because you are diverged now) +``` +git push --force +``` + + +## Code style +The code is formatted using clang-format. If you do not want to use it yourself, try to keep your style consistent with the other code +so not too many reformats are necessary. diff --git a/dependencies/hueplusplus-1.2.0/.github/FUNDING.yml b/dependencies/hueplusplus-1.2.0/.github/FUNDING.yml new file mode 100644 index 0000000..7403a23 --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [enwi] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/dependencies/hueplusplus-1.2.0/.github/ISSUE_TEMPLATE/bug_report.md b/dependencies/hueplusplus-1.2.0/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..e31ba3e --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'Status: Available, Type: Bug' +assignees: '' + +--- + +**Describe the bug**: +A clear and concise description of what the bug is. + +**To Reproduce**: +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior**: +A clear and concise description of what you expected to happen. + +**Console log/Error message**: +If applicable, add a console log or error message to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. MacOS, Windows, Linux, ESP32 SDK, Arduino] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/dependencies/hueplusplus-1.2.0/.github/ISSUE_TEMPLATE/feature_request.md b/dependencies/hueplusplus-1.2.0/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..ff51c8c --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: 'Status: Available, Type: Enhancement' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/dependencies/hueplusplus-1.2.0/.github/workflows/build.yml b/dependencies/hueplusplus-1.2.0/.github/workflows/build.yml new file mode 100644 index 0000000..2ec35c0 --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/.github/workflows/build.yml @@ -0,0 +1,72 @@ +name: CI + +on: + push: + branches: + - master + - development + pull_request: + +jobs: + build: + runs-on: ubuntu-24.04 + + env: + LINUX_DIST: bionic + DEPS_DIR: ${{ github.workspace }}/deps + COMPILER_NAME: gcc + CXX: g++ + CC: gcc + RUN_TESTS: true + COVERAGE: false + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + # PATH: ${{ github.workspace }}/deps/cmake/bin:${{ env.PATH }} + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -y gcc g++ lcov doxygen graphviz python3-yaml + + # - name: Install CodeCov and LCOV + # run: | + # sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-7 90 + # wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.13.orig.tar.gz + # tar xf lcov_1.13.orig.tar.gz + # make -C lcov-1.13 "PREFIX=${HOME}/.local" install + # echo "${HOME}/.local/bin" >> $GITHUB_PATH + + - name: Show tool versions + run: | + echo $PATH + echo $CXX + $CXX --version + $CXX -v + cmake --version + lcov --version + gcov --version + + - name: Build project + run: | + mkdir -p build + cd build + cmake .. -Dhueplusplus_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -Dhueplusplus_EXAMPLES=ON + make hueplusplus_examples hueplusplus_snippets + make coveragetest + cd .. + doxygen Doxyfile + touch doc/html/.nojekyll + + - name: Upload coverage to Codecov + run: | + bash <(curl -s https://codecov.io/bash) + + - name: Deploy documentation to GitHub Pages + if: github.ref == 'refs/heads/master' + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./doc/html diff --git a/dependencies/hueplusplus-1.2.0/.gitignore b/dependencies/hueplusplus-1.2.0/.gitignore new file mode 100644 index 0000000..bf27848 --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/.gitignore @@ -0,0 +1,65 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# build directory +/build* +/out +/bin + +# Generated documentation +/doc/html + +# General +.DS_Store +.AppleDouble +.LSOverride +.vs + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/dependencies/hueplusplus-1.2.0/.gitmodules b/dependencies/hueplusplus-1.2.0/.gitmodules new file mode 100644 index 0000000..808ab1f --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/.gitmodules @@ -0,0 +1,6 @@ +[submodule "lib/mbedtls"] + path = lib/mbedtls + url = https://github.com/ARMmbed/mbedtls.git +[submodule "lib/json"] + path = lib/json + url = https://github.com/nlohmann/json.git diff --git a/dependencies/hueplusplus-1.2.0/.travis.yml b/dependencies/hueplusplus-1.2.0/.travis.yml new file mode 100644 index 0000000..3f6a97c --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/.travis.yml @@ -0,0 +1,76 @@ +language: generic + +env: + global: + # Ubuntu version + - LINUX_DIST=bionic + - DEPS_DIR=${TRAVIS_BUILD_DIR}/deps + # compiler settings + - COMPILER_NAME=gcc + - CXX=g++ + - CC=gcc + # Misc + - RUN_TESTS=true + - COVERAGE=false + - PATH=${DEPS_DIR}/cmake/bin:${PATH} + +matrix: + include: + - os: linux + dist: bionic + sudo: true + compiler: gcc + addons: + apt: + packages: + # Misc + - python-yaml + - doxygen + - graphviz +before_install: + # Combine global build options with OS/compiler-dependent options + - export CMAKE_OPTIONS=${CMAKE_OPTIONS}" "${ENV_CMAKE_OPTIONS} + - export CXX_FLAGS=${CXX_FLAGS}" "${ENV_CXX_FLAGS} + # c++14 + - sudo apt-get update -qq + +install: + # CodeCov + - sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-7 90 + # we have to build lcov on our own, because it is not possible to install lcov-1.13 with apt + - wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.13.orig.tar.gz && tar xf lcov_1.13.orig.tar.gz && make -C lcov-1.13 "PREFIX=${HOME}/.local" install && export PATH="${PATH}:${HOME}/.local/bin"; + # show info + - echo ${PATH} + - echo ${CXX} + - ${CXX} --version + - ${CXX} -v + - cmake --version + - lcov --version + +script: + ############################################################################ + # Build main, tests and examples + ############################################################################ + - mkdir -p build + - cd build + - cmake .. -Dhueplusplus_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -Dhueplusplus_EXAMPLES=ON + - make hueplusplus_examples hueplusplus_snippets + - make coveragetest + - cd .. + - doxygen Doxyfile + # .nojekyll file prevents hiding of files starting with _ + - touch doc/html/.nojekyll + + +after_success: + # upload result to codecov + - bash <(curl -s https://codecov.io/bash) + +deploy: + provider: pages + skip_cleanup: true + local_dir: doc/html + github_token: $GH_REPO_TOKEN + on: + branch: master + diff --git a/dependencies/hueplusplus-1.2.0/CMakeLists.txt b/dependencies/hueplusplus-1.2.0/CMakeLists.txt new file mode 100644 index 0000000..6f1faec --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/CMakeLists.txt @@ -0,0 +1,128 @@ +cmake_minimum_required(VERSION 3.10.2...3.28) + +# Add cmake dir to module path, so Find*.cmake can be found +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) + +project(hueplusplus VERSION 1.2.0 LANGUAGES CXX) + +# check whether hueplusplus is compiled directly or included as a subdirectory +if(NOT DEFINED hueplusplus_master_project) + if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + set(hueplusplus_master_project ON) + else() + set(hueplusplus_master_project OFF) + endif() +endif() + +# options to set +option(hueplusplus_TESTS "Build tests" OFF) +option(hueplusplus_EXAMPLES "Build examples" OFF) +option(hueplusplus_NO_EXTERNAL_LIBRARIES "Do not try to use external libraries" OFF) + +# Try to find installed packages +if(NOT hueplusplus_NO_EXTERNAL_LIBRARIES) + # Suppress warnings if libraries are not found, they will be built from submodules + find_package(MbedTLS QUIET) + find_package(nlohmann_json QUIET) +endif() + +set(NEED_SUBMODULES NOT (${MbedTLS_FOUND} AND ${nlohmann_json_FOUND})) + +option(CLANG_TIDY_FIX "Perform fixes for Clang-Tidy" OFF) +find_program(CLANG_TIDY_EXE NAMES "clang-tidy" DOC "Path to clang-tidy executable") +if(CLANG_TIDY_EXE) + if(CLANG_TIDY_FIX) + set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-fix") + else() + set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}") + endif() +endif() + +# update submodules +find_package(Git QUIET) +if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + option(GIT_SUBMODULE "Check submodules during build" ON) + if(GIT_SUBMODULE AND NEED_SUBMODULES) + message(STATUS "Submodule update") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif() + endif() +endif() + +# Set default build type if none was specified +set(default_build_type "Release") +if(hueplusplus_master_project AND (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)) + message(STATUS "Setting build type to '${default_build_type}' as none was specified") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) + # Set possible values for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() + + +# get the correct installation directory for add_library() to work +if(WIN32 AND NOT CYGWIN) + set(DEF_INSTALL_CMAKE_DIR cmake) +else() + set(DEF_INSTALL_CMAKE_DIR lib/cmake/hueplusplus) +endif() +set(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") + +# target for uninstall +if(NOT TARGET uninstall) + configure_file( + "${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) +endif() + +# if we are on a apple machine this is needed +if (1 AND APPLE) + set(CMAKE_MACOSX_RPATH 1) +endif() + +if(NOT MbedTLS_FOUND) + # Build mbedtls if not installed + message(STATUS "MbedTLS was not found, the submodule is used.") + set(USE_STATIC_MBEDTLS_LIBRARY ON) + set(USE_SHARED_MBEDTLS_LIBRARY OFF) + add_subdirectory("lib/mbedtls" EXCLUDE_FROM_ALL) + + # Compile the mbedtls library as a static with position independent code, + # because we need it for both a shared and static library + set_property(TARGET mbedtls PROPERTY POSITION_INDEPENDENT_CODE ON) + set_property(TARGET mbedcrypto PROPERTY POSITION_INDEPENDENT_CODE ON) + set_property(TARGET mbedx509 PROPERTY POSITION_INDEPENDENT_CODE ON) + + if(CMAKE_VERSION VERSION_LESS 3.18) + # Aliases for compatibility with find_package, newer cmake versions add these already + add_library(MbedTLS::mbedtls ALIAS mbedtls) + add_library(MbedTLS::mbedcrypto ALIAS mbedcrypto) + add_library(MbedTLS::mbedx509 ALIAS mbedx509) + endif() +endif() + +if(NOT nlohmann_json_FOUND) + # Use embedded json + message(STATUS "nlohmann_json was not found, the submodule is used.") + # disable tests for json + set(JSON_BuildTests OFF CACHE INTERNAL "") + add_subdirectory("lib/json" EXCLUDE_FROM_ALL) +endif() + +add_subdirectory(src) + +# if the user decided to use tests add the subdirectory +if(hueplusplus_TESTS) + add_subdirectory("test") +endif() + +if(hueplusplus_EXAMPLES) + add_subdirectory("examples") +endif() diff --git a/dependencies/hueplusplus-1.2.0/Doxyfile b/dependencies/hueplusplus-1.2.0/Doxyfile new file mode 100644 index 0000000..8e84537 --- /dev/null +++ b/dependencies/hueplusplus-1.2.0/Doxyfile @@ -0,0 +1,2612 @@ +# Doxyfile 1.8.20 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = hueplusplus + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = 1.2.0 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = include + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# (including Cygwin) and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = include/hueplusplus \ + src \ + doc/markdown \ + examples + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = *::detail + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = *.cpp + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = Mainpage.md + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the "-p" option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /